Параметры и обобщённые модули: parameter и generate
Учимся писать модули, которые настраиваются под нужный размер, и размножать аппаратуру по шаблону.
parameter — настраиваемая константа модуля, задаваемая при инстанцировании; позволяет описать один модуль, работающий с любой разрядностью.
Писать отдельный модуль под 8-битный счётчик, потом под 16-битный, потом под 32-битный — расточительно. Verilog даёт два инструмента обобщения: parameter (настраиваемые константы) и generate (повторение аппаратуры по шаблону). С ними один модуль покрывает целое семейство схем — это и переиспользование, и защита от ошибок копипасты.
Параметры: настраиваемая разрядность
parameter объявляет константу со значением по умолчанию, которое можно переопределить при инстанцировании. Классический пример — счётчик любой разрядности:
module counter #(
parameter WIDTH = 8 // разрядность по умолчанию — 8 бит
)(
input wire clk,
input wire rst,
output reg [WIDTH-1:0] count
);
always @(posedge clk) begin
if (rst) count <= 0;
else count <= count + 1;
end
endmoduleТеперь один и тот же модуль инстанцируется под разные размеры:
counter #(.WIDTH(8)) c8 (.clk(clk), .rst(rst), .count(cnt8)); // 8-битный
counter #(.WIDTH(16)) c16 (.clk(clk), .rst(rst), .count(cnt16)); // 16-битный
counter #(.WIDTH(32)) c32 (.clk(clk), .rst(rst), .count(cnt32)); // 32-битныйОдин исходник — три разных счётчика в железе. Изменили логику — она исправилась во всех инстансах сразу.
generate: размножение аппаратуры
Блок generate с переменной genvar повторяет фрагмент схемы N раз во время синтеза. Это не цикл во времени — это шаблон, по которому строится N копий железа. Пример: 8-битный побитовый XOR двух шин как восемь однобитных вентилей:
genvar i;
generate
for (i = 0; i < 8; i = i + 1) begin : xor_gen
assign y[i] = a[i] ^ b[i]; // создаёт 8 независимых вентилей XOR
end
endgenerateПосле синтеза появятся 8 физических вентилей, работающих параллельно. generate особенно полезен для регулярных структур: массивов сумматоров, цепочек регистров, матриц обработки.
Как работает под капотом
Параметр меняет только разрядность, а логика остаётся той же. Промоделируем счётчик разной разрядности на Python и увидим, как параметр WIDTH задаёт предел переполнения:
def max_count(width):
return 2 ** width - 1 # максимальное значение WIDTH-битного счётчика
for width in [4, 8, 16, 32]:
print(f"WIDTH={width:2} -> счёт 0..{max_count(width)} ({2**width} значений)")Вывод:
WIDTH= 4 -> счёт 0..15 (16 значений) WIDTH= 8 -> счёт 0..255 (256 значений) WIDTH=16 -> счёт 0..65535 (65536 значений) WIDTH=32 -> счёт 0..4294967295 (4294967296 значений)
Один параметр WIDTH определяет и число триггеров, и предел счёта. Это и есть обобщение: меняем константу — получаем нужный размер железа без переписывания логики.
Частые ошибки
- Считать
generate forциклом выполнения. Это шаблон размножения железа на этапе синтеза, а не цикл во времени; внутри не может быть «итераций по такту». - Жёстко зашивать разрядность. Магические числа вроде
[7:0]по всему коду затрудняют переиспользование; выносите их вparameter. - Забыть метку
begin : имяв generate-цикле. Многие инструменты требуют именованный блок внутри generate.
Итог
parameterделает модуль настраиваемым (например, по разрядности) и переиспользуемым.- Переопределение при инстанцировании:
module #(.WIDTH(16)) inst (...). generateсgenvarразмножает аппаратуру по шаблону на этапе синтеза.- generate-цикл строит N копий железа, а не выполняет итерации во времени.