Основы модулей Verilog: структура и синтаксис
module — блок Verilog-кода, реализующий определённую функциональность. Модули могут вкладываться друг в друга, а высокоуровневый модуль общается с низкоуровневыми через их входные и выходные порты.
Синтаксис
Модуль должен быть заключён между ключевыми словами module и endmodule. Имя модуля указывается сразу после module, также может быть объявлен опциональный список портов. Порты, объявленные в списке портов, не могут быть переобъявлены в теле модуля.
module <имя> ([список_портов]);
// Содержимое модуля
endmodule
// Модуль может иметь пустой список портов
module имя;
// Содержимое модуля
endmodule
Все объявления переменных, операторы потока данных, функции, задачи и инстанцирования нижних модулей должны быть внутри module ... endmodule. В одном файле может быть несколько модулей с разными именами в любом порядке.
Пример
Модуль dff представляет D-триггер с тремя входами d, clk, rstn и одним выходом q. Содержимое описывает поведение D-триггера для разных комбинаций входов. Вход d всегда присваивается выходу q по положительному фронту clk, если rstn высок (активный низкий сброс).
// Модуль "dff" с 3 входами и 1 выходом
module dff (
input d,
input clk,
input rstn,
output reg q
);
// Содержимое модуля
always @ (posedge clk) begin
if (!rstn) q <= 0;
else q <= d;
end
endmodule
Аппаратная схема
При синтезе этот модуль превратится в следующую цифровую схему.
Важно: Нельзя писать код вне модуля!
Назначение модуля
Модуль представляет проектную единицу, реализующую определённые поведенческие характеристики, и превращается в цифровую схему при синтезе. Любая комбинация входов даёт соответствующий выход. Это позволяет переиспользовать один модуль для построения более крупных модулей, реализующих сложное железо.
Например, показанный выше DFF можно соединить в цепочку для формирования сдвигового регистра.
module shift_reg (
input d, input clk, input rstn, output q
);
wire [2:0] q_net;
dff u0 (.d(d), .clk(clk), .rstn(rstn), .q(q_net[0]));
dff u1 (.d(q_net[0]), .clk(clk), .rstn(rstn), .q(q_net[1]));
dff u2 (.d(q_net[1]), .clk(clk), .rstn(rstn), .q(q_net[2]));
dff u3 (.d(q_net[2]), .clk(clk), .rstn(rstn), .q(q));
endmodule
Аппаратная схема
Инстанции dff соединены проводами, как описано в RTL-модуле Verilog.
Обратный процесс также возможен: разбиение простого GPU на подблоки, каждый из которых представлен модулем с конкретной функциональностью. GPU ниже разбит на пять подблоков с разными задачами. Блок шины вводит данные, другой извлекает инструкции, остальные обрабатывают данные от предыдущих блоков.
Каждый подблок — module с набором входов/выходов для связи с другими. Подблоки можно делить дальше на более мелкие.
Что такое топ-уровневые модули?
Топ-уровневый модуль — тот, который содержит все остальные. Он не инстанцируется внутри другого модуля.
Например, дизайн-модули обычно инстанцируются в топ-уровневом тестбенче для симуляции с входными стимулами. Тестбенч не инстанцируется нигде, он инкапсулирует всё — значит, он топ-уровневый модуль.
Топ-уровень дизайна (Design Top Level)
Код ниже имеет топ-уровневый модуль design, так как он содержит все подмодули. Подмодули могут иметь вложенные подмодули (mod3 внутри mod1, mod4 внутри mod2). Все включены в топ-уровень при инстанцировании mod1 и mod2. Это делает design полным и топ-уровневым для дизайна.
//---------------------------------
// Design code
//---------------------------------
module mod3 ( [port_list] ); reg c; // Design code endmodule
module mod4 ( [port_list] ); wire a; // Design code endmodule
module mod1 ( [port_list] );
wire y;
mod3 mod_inst1 ( ... ); // Первый инстанс модуля "mod3" с именем "mod_inst1"
mod3 mod_inst2 ( ... ); // Второй инстанс тоже "mod3" с именем "mod_inst2"
endmodule
module mod2 ( [port_list] );
mod4 mod_inst1 ( ... );
mod4 mod_inst2 ( ... );
endmodule
// Топ-уровневый модуль
module design ( [port_list]);
wire _net;
mod1 mod_inst1 ( ... ); // содержит все модули и подмодули
mod2 mod_inst2 ( ... );
endmodule
Топ-уровень тестбенча (Testbench Top Level)
Тестбенч содержит стимулы для проверки функциональности дизайна и используется для функциональной верификации симуляторами. Дизайн инстанцируется и называется d0 внутри тестбенча. С точки зрения симулятора тестбенч — топ-уровневый модуль.
//-----------------------------------------------------------
// Testbench code
// С точки зрения симуляции это топ-уровневый модуль,
// потому что 'design' инстанцирован внутри
//-----------------------------------------------------------
module testbench;
design d0 ( [port_list_connections] );
// Остальной код тестбенча
endmodule
Иерархические имена
Иерархическая структура образуется, когда модули инстанцируются друг в друге, и топ-уровневый модуль называется корнем (root). Поскольку каждый инстанс нижнего модуля в данном модуле должен иметь уникальное имя, нет двусмысленности в доступе к сигналам. Иерархическое имя строится списком идентификаторов, разделённых точками . для каждого уровня иерархии. Любой сигнал доступен в любом модуле через иерархический путь к нему.
// Пример из топ-уровневых модулей выше
design.mod_inst1 // доступ к инстансу модуля mod_inst1
design.mod_inst1.y // доступ к сигналу "y" внутри mod_inst1
design.mod_inst2.mod_inst2.a // доступ к сигналу "a" внутри модуля mod4
testbench.d0._net; // топ-уровневый сигнал _net внутри design, доступный из testbench
Verilog
- Освоение Verilog: полное руководство по проектированию цифровых схем
- Освоение конкатенации в Verilog: практическое руководство
- Основы Verilog: краткий обзор
- Понимание назначений в Verilog: типы, синтаксис и лучшие практики
- Блокирующие и неблокирующие назначения в Verilog: ключевые различия
- Мастерство функций в Verilog для эффективного RTL дизайна
- Понимание задач (Tasks) в Verilog: отличия от функций, синтаксис и применение
- Иерархические ссылки в Verilog: область видимости и избежание коллизий имен
- Генератор тактового сигнала в Verilog: проектирование и реализация
- Мастерство математических функций Verilog для эффективного аппаратного дизайна