12.4 Конструкции generate
Конструкции generate используются для условного или многократного объявления экземпляров блоков генерации в модуле. Блок generate — это набор из одного или нескольких элементов модуля. Блок generate не может содержать объявления портов, parameter, блоки specify или specparam. Все остальные элементы модуля, включая другие конструкции generate, разрешены в блоке generate. Конструкции generate предоставляют возможность значениям параметров влиять на структуру модели. Они также позволяют более лаконично описывать модули с повторяющейся структурой и делают возможным рекурсивное объявление экземпляра модуля.
Существует 2 вида конструкций generate: циклы и условия. Конструкции генерации циклов позволяют объявлять экземпляры в один блок генерации в модель несколько раз. Условные конструкции generate, к которым относятся конструкции if-generate и case-generate, определяет не более одного блока generate из набора альтернативных блоков generate. Термин схема generate относится к методу определения того, какие или сколько блоков generate объявляются. Он включает в себя условные выражения, альтернативы case и управляющие операторы цикла, которые появляются в конструкции generate.
Генерируемые схемы оцениваются во время разработки модели. Разработка происходит после разбора HDL и перед моделированием. Она включает в себя расширение объявления экземпляра модулей, вычисление значений параметров, разрешение иерархических имен (см. 12.5), установление связности сетей и в целом подготовку модели к моделированию. Хотя схемы генерации используют синтаксис, похожий на поведенческие(always, initial, …) объявления, важно понимать, что они не выполняются во время моделирования. Они оцениваются во время разработки, и результат определяется до начала моделирования. Поэтому все выражения в схемах генерации должны быть константными выражениями, детерминированными(известными, константными) во время разработки. Более подробно о разработке см. в разделе 12.8.
В результате разработки конструкции generate создается ноль или более экземпляров блока generate. Экземпляр блока generate в чем-то схож с экземпляром модуля. Он создает новый уровень иерархии. Он вызывает к существованию объекты, поведенческие конструкции и экземпляры модулей в блоке. Эти конструкции действуют так же, как если бы они находились в модуле, созданном с помощью экземпляра модуля, за исключением того, что на объявления объектов из объемлющей области видимости можно ссылаться напрямую (см. 12.7). На имена в экземпляре именованных блоках генерации можно ссылаться иерархически, как описано в 12.5.
Ключевые слова generate и endgenerate могут быть использованы в модуле для определения области генерации. Область генерации — это текстовый участок в описании модуля, где могут появляться конструкции generate. Использование областей генерации(generate) является необязательным. В модуле нет семантических различий, если используется область генерации. Синтаксический анализатор может решить распознать область генерации, чтобы выдавать различные сообщения об ошибках при неправильном использовании ключевых слов конструкции generate. Регионы generate не вложены, и они могут встречаться только непосредственно в модуле. Если используется ключевое слово generate, ему должно соответствовать ключевое слово endgenerate.
Синтаксис для конструкций генерации приведен в Синтаксис 12-5.
module_or_generate_item ::=
{ attribute_instance } module_or_generate_item_declaration
| { attribute_instance } local_parameter_declaration ;
| { attribute_instance } parameter_override
| { attribute_instance } continuous_assign
| { attribute_instance } gate_instantiation
| { attribute_instance } udp_instantiation
| { attribute_instance } module_instantiation
| { attribute_instance } initial_construct
| { attribute_instance } always_construct
| { attribute_instance } loop_generate_construct
| { attribute_instance } conditional_generate_construct
generate_region ::=
generate { module_or_generate_item } endgenerate
genvar_declaration ::=
genvar list_of_genvar_identifiers ;
list_of_genvar_identifiers ::=
genvar_identifier { , genvar_identifier }
loop_generate_construct ::=
for ( genvar_initialization ; genvar_expression ; genvar_iteration )
generate_block
genvar_initialization ::=
genvar_identifier = constant_expression
genvar_expression ::=
genvar_primary
| unary_operator { attribute_instance } genvar_primary
| genvar_expression binary_operator { attribute_instance } genvar_expression
| genvar_expression ? { attribute_instance } genvar_expression : genvar_expression
genvar_iteration ::=
genvar_identifier = genvar_expression
genvar_primary ::=
constant_primary
| genvar_identifier
conditional_generate_construct ::=
if_generate_construct
| case_generate_construct
if_generate_construct ::=
if ( constant_expression ) generate_block_or_null
[ else generate_block_or_null ]
case_generate_construct ::=
case ( constant_expression )
case_generate_item { case_generate_item } endcase
case_generate_item ::=
constant_expression { , constant_expression } : generate_block_or_null
| default [ : ] generate_block_or_null
generate_block ::=
module_or_generate_item
| begin [ : generate_block_identifier ] { module_or_generate_item } end
generate_block_or_null ::=
generate_block | ;
12.4.1 Конструкции циклов generate
Конструкция циклов генерации позволяет многократно объявлять экземпляры в блоке generate, используя синтаксис, схожий с оператором цикла for. Переменная индекса цикла должна быть объявлена в объявлении genvar до ее использования в схеме генерации цикла.
Genvar используется как целое число во время разработки для оценки цикла generate и создания экземпляров в блоке generate, но он не существует во время моделирования. На genvar нельзя ссылаться нигде, кроме как в схеме генерации цикла.
Назначения инициализации и итерации в схеме генерации циклов должны присваиваться одному и тому же параметру genvar. Назначение инициализации не должно ссылаться на переменную индекса цикла в правой части.
В блоке generate конструкции цикла генерации имеется неявное объявление localparam. Это целочисленный параметр, который имеет то же имя и тип, что и индексная переменная цикла, а его значением в каждом экземпляре блока генерации является значение индексной переменной на момент разработки экземпляра. Этот параметр может быть использован в любом месте блока generate, где может быть использован обычный параметр с целочисленным значением. На него можно ссылаться с иерархическим именем.
Поскольку эта неявная localparam имеет то же имя, что и genvar, любая ссылка на это имя внутри блока генерации цикла будет ссылкой на localparam, а не на genvar. Как следствие, невозможно иметь две вложенные конструкции loop generate, использующие один и тот же genvar.
generate блоки в конструкциях циклов генерации могут быть именованными или неименованными, и они могут состоять только из одного элемента, который не обязательно должен быть окружен ключевыми словами begin/end. Даже если ключевые слова begin/end отсутствуют, это все равно блок generate, который, как и все блоки generate, при объявлении включает отдельную область видимости и новый уровень иерархии.
Если блок generate имеет имя, то это объявление массива экземпляров блока generate. Значения индексов в этом массиве — это значения, принимаемые genvar во время разработки. Это может быть разреженный массив, поскольку значения genvar не обязательно должны образовывать непрерывный диапазон целых чисел. Массив считается объявленным, даже если схема генерации цикла не привела ни к одному экземпляру блока generate. Если блок generate не имеет имени, то на объявления в нем нельзя ссылаться, используя иерархические имена, кроме как из иерархии, созданной самим блоком generate.
Будет ошибкой, если имя массива экземпляров блоков генерации конфликтует с любым другим объявлением, включая любой другой массив экземпляров блоков генерации. Будет ошибкой, если схема генерации цикла не завершается. Будет ошибкой, если значение genvar повторяется во время оценки схемы генерации цикла. Будет ошибкой, если любой бит genvar будет установлен в x или z во время оценки схемы генерации цикла.
Например:
module mod_a;
genvar i;
// Ключевые слова "generate", "endgenerate" необязательны
for (i=0; i<5; i=i+1) begin:a
for (i=0; i<5; i=i+1) begin:b
... // ошибка -- использование "i" d в качестве индекса цикла for
... // два вложенных порождающих цикла
end
end
endmodule
------
module mod_b;
genvar i;
reg a;
for (i=1; i<0; i=i+1) begin: a
... // ошибка -- "a" конфликтует с именем reg "a"
end
endmodule
------
module mod_c;
genvar i;
for (i=1; i<5; i=i+1) begin: a
...
end
for (i=10; i<15; i=i+1) begin: a
... // ошибка -- "a" конфилктует с предыдущем
... // циклом даже если индексы уникальны
end
endmodule
module gray2bin1 (bin, gray);
parameter SIZE = 8; // этот модуль параметризированный
output [SIZE-1:0] bin;
input [SIZE-1:0] gray;
genvar i;
generate
for (i=0; i<SIZE; i=i+1) begin:bit
assign bin[i] = ^gray[SIZE-1:i];
// i ссылается на неявно объявленный localparam чьё
// значение в каждом экземпляре блока generate является
// значение genvar на момент его разработки.
end
endgenerate
endmodule
Модули в примерах 3 и 4 представляют собой параметризованные модули пульсирующих сумматоров, использующие цикл for генерации примитивов вентилей Verilog. В примере 3 используется объявление двумерной сети вне цикла генерации для создания соединений между примитивами вентилей, а в примере 4 объявление сети находится внутри цикла генерации для создания проводов, необходимых для соединения примитивов вентилей для каждой итерации цикла.
module addergen1 (co, sum, a, b, ci);
parameter SIZE = 4;
output [SIZE-1:0] sum;
output co;
input [SIZE-1:0] a, b;
input ci;
wire [SIZE :0] c;
wire [SIZE-1:0] t [1:3];
genvar i;
assign c[0] = ci;
// Иерархические имена экземпляров вентилей:
// xor вентили: bit[0].g1 bit[1].g1 bit[2].g1 bit[3].g1
// bit[0].g2 bit[1].g2 bit[2].g2 bit[3].g2
// and вентили: bit[0].g3 bit[1].g3 bit[2].g3 bit[3].g3
// bit[0].g4 bit[1].g4 bit[2].g4 bit[3].g4
// or вентили: bit[0].g5 bit[1].g5 bit[2].g5 bit[3].g5
// Сгенерированные экземпляры связаны с
// многомерные сети t[1][3:0] t[2][3:0] t[3][3:0]
// (Всего 12 сетей)
for(i=0; i<SIZE; i=i+1) begin:bit
xor g1 ( t[1][i], a[i], b[i]);
xor g2 ( sum[i], t[1][i], c[i]);
and g3 ( t[2][i], a[i], b[i]);
and g4 ( t[3][i], t[1][i], c[i]);
or g5 ( c[i+1], t[2][i], t[3][i]);
end
assign co = c[SIZE];
endmodule
module addergen1 (co, sum, a, b, ci);
parameter SIZE = 4;
output [SIZE-1:0] sum;
output co;
input [SIZE-1:0] a, b;
input ci;
wire [SIZE :0] c;
genvar i;
assign c[0] = ci;
// Иерархические имена экземпляров вентилей:
// xor вентили: bit[0].g1 bit[1].g1 bit[2].g1 bit[3].g1
// bit[0].g2 bit[1].g2 bit[2].g2 bit[3].g2
// and вентили: bit[0].g3 bit[1].g3 bit[2].g3 bit[3].g3
// bit[0].g4 bit[1].g4 bit[2].g4 bit[3].g4
// or вентили: bit[0].g5 bit[1].g5 bit[2].g5 bit[3].g5
// Экземпляры вентилей соединенные сетями с именами:
// bit[0].t1 bit[1].t1 bit[2].t1 bit[3].t1
// bit[0].t2 bit[1].t2 bit[2].t2 bit[3].t2
// bit[0].t3 bit[1].t3 bit[2].t3 bit[3].t3
for(i=0; i<SIZE; i=i+1) begin:bit
wire t1, t2, t3;
xor g1 ( t1, a[i], b[i]);
xor g2 ( sum[i], t1, c[i]);
and g3 ( t2, a[i], b[i]);
and g4 ( t3, t1, c[i]);
or g5 ( c[i+1], t2, t3);
end
assign co = c[SIZE];
endmodule
Иерархические имена экземпляров блока generate в многоуровневом цикле генерации показаны в примере 5. Для каждого экземпляра блока, созданного циклом генерации, идентификатор блока generate для цикла индексируется путем добавления «[значение genvar]» к концу идентификатора блока generate. Эти имена можно использовать в иерархических именах путей (см. 12.5).
parameter SIZE = 2;
genvar i, j, k, m;
generate
for (i=0; i<SIZE; i=i+1) begin:B1 // область B1[i]
M1 N1(); // экземпляры B1[i].N1
for (j=0; j<SIZE; j=j+1) begin:B2 // область B1[i].B2[j]
M2 N2(); // instantiates B1[i].B2[j].N2
for (k=0; k<SIZE; k=k+1) begin:B3 // область B1[i].B2[j].B3[k]
M3 N3(); // экземпляры B1[i].B2[j].B3[k].N3
end
end
if (i>0) begin:B4 // область B1[i].B4
for (m=0; m<SIZE; m=m+1) begin:B5 // область B1[i].B4.B5[m]
M4 N4(); // экземпляры B1[i].B4.B5[m].N4
end
end
end
endgenerate
// Некоторые примеры иерархических имен для экземпляров модуля:
// B1[0].N1 B1[1].N1
// B1[0].B2[0].N2 B1[0].B2[1].N2
// B1[0].B2[0].B3[0].N3 B1[0].B2[0].B3[1].N3
// B1[0].B2[1].B3[0].N3
// B1[1].B4.B5[0].N4 B1[1].B4.B5[1].N4
12.4.2 Условные конструкции generate
Условные конструкции generate if-generate и case-generate выбирают не более одного блока generate из набора альтернативных блоков generate на основе константных выражений, оцениваемых в процессе разработки. Выбранный блок generate, если таковой имеется, объявляется экземпляром в модуле.
Блоки генерации в условных конструкциях генерации могут быть именованными или неименованными, и они могут состоять только из одного элемента, который не обязательно должен быть окружен ключевыми словами begin/end. Даже если ключевые слова begin/end отсутствуют, это все равно блок generate, который, как и все блоки generate, включает в себя отдельную область видимости и новый уровень иерархии, когда он объявляется.
Поскольку объявляется экземпляр только один из альтернативных блоков generate, допускается наличие более одного блока с одинаковым именем в одной условной конструкции generate. Не допускается, чтобы любой из именованных блоков generate имел то же имя, что и блоки generate в любой другой условной или циклической конструкции generate в той же области видимости, даже если блоки с тем же именем не выбраны для объявления экзнмпляров. Не допускается, чтобы любой из именованных блоков generate имел то же имя, что и любое другое объявление в той же области видимости, даже если этот блок не выбран для объявления экзнмпляров.
Если выбранный для определения блок генерации имеет имя, то это имя объявляет экземпляр блока генерации и является именем для создаваемой им области видимости. Применяются обычные правила иерархического именования. Если блок генерации, выбранный для объявления экземпляров, не имеет имени, он все равно создает область видимости. Однако, в объявлении в ней нельзя ссылаться, используя иерархические имена, кроме как из иерархии, созданной самим блоком генерации.
Если блок генерации в условной конструкции генерации состоит только из одного элемента, который сам является условной конструкцией генерации, и если этот элемент не окружен ключевыми словами begin/end, то этот блок генерации не рассматривается как отдельная область видимости. Конструкция generate внутри этого блока считается непосредственно вложенной. Блоки генерации непосредственно вложенной конструкции рассматриваются так, как если бы они принадлежали внешней конструкции. Поэтому они могут иметь то же имя, что и блоки generate внешней конструкции, и не могут иметь то же имя, что и любое объявление в области видимости, охватывающей внешнюю конструкцию (включая другие блоки generate в других конструкциях generate в этой области видимости). Это позволяет выражать сложные условные схемы генерации без создания ненужных уровней иерархии блоков генерации.
Чаще всего это используется для создания схемы генерации if-else-if с любым количеством пунктов else-if, все из которых могут иметь блоки генерации с одинаковым именем, поскольку для объявления экземпляров будет выбран только один. Допустимо объединять конструкции if-generate и case-generate в одной сложной схеме генерации. Прямое вложение применяется только к условным конструкциям generate, вложенным в условные конструкции generate. Она никак не относится к конструкциям генерации циклов.
module test;
parameter p = 0, q = 0;
wire a, b, c;
//---------------------------------------------------------
// Код для генерации экземпляра u1.g1 или отсутствия экземпляра.
// Экземпляр u1.g1 одного из следующих вентилей:
// (and, or, xor, xnor) генерируются если
// {p,q} == {1,0}, {1,2}, {2,0}, {2,1}, {2,2}, {2, default}
//---------------------------------------------------------
if (p == 1)
if (q == 0)
begin : u1 // Если p==1 и q==0, то объявляет экземпляр
and g1(a, b, c); // AND с иерархическим именем test.u1.g1
end
else if (q == 2)
begin : u1 // Если p==1 и q==2, то объявляет экземпляр
or g1(a, b, c); // OR с иерархическим именем test.u1.g1
end
// "else" добавлено в конец объявления "if (q == 2)".
else ; // Если p==1 и q!=0 или 2, то не объявляет экземпляр
else if (p == 2)
case (q)
0, 1, 2:
begin : u1 // Если p==2 т q==0,1, или 2, то объявляет экземпляр
xor g1(a, b, c);// XOR с иерархическим именем test.u1.g1
end
default:
begin : u1 // Если p==2 и q!=0,1, или 2, то объявляет экземпляр
xnor g1(a, b, c);// XNOR с иерархическим именем test.u1.g1
end
endcase
endmodule
Эта конструкция generate выберет не более одного блока generate с именем u1. Иерархическое имя инстанцирования вентиля в этом блоке будет test.u1.g1. При вложении конструкций if-generate, else всегда принадлежит ближайшей конструкции if.
Как и в приведенном выше примере, блок generate else с нулем может быть вставлен для того, чтобы последующий else принадлежал внешней конструкции if. Ключевые слова begin/end также могут быть использованы для разграничения. Однако это нарушит критерии прямой вложенности, и будет создан дополнительный уровень иерархии блоков generate.
Перевод Официального Стандарта Verilog HDL
Условные конструкции generate позволяют модулю содержать экземпляра самого себя. Тоже самое можно сказать о конструкциях loop generate, но это легче сделать помощью условных generate.
При правильном использовании параметров, результирующая рекурсия может быть завершена, что приводит к созданию правильной иерархии модели. Из-за правил определения модулей верхнего уровня модуль, содержащий экземпляр самого себя, не будет модулем верхнего уровня.
module multiplier(a,b,product);
parameter a_width = 8, b_width = 8;
localparam product_width = a_width+b_width;
// не может быть изменен непосредственно с помощью объявления defparam
// или объявления экземпляра модуля #
input [a_width-1:0] a;
input [b_width-1:0] b;
output [product_width-1:0] product;
generate
if((a_width < 8) || (b_width < 8)) begin: mult
CLA_multiplier #(a_width,b_width) u1(a, b, product);
// объявление экземпляра множителя CLA
end
else begin: mult
WALLACE_multiplier #(a_width,b_width) u1(a, b, product);
// объявление экземпляра множителя дерева Уоллеса
end
endgenerate
// Иерархическое имя экземпляра mult.u1
endmodule
generate
case (WIDTH)
1: begin: adder // реализация 1-битного сумматора
adder_1bit x1(co, sum, a, b, ci);
end
2: begin: adder // реализация 2-битного сумматора
adder_2bit x1(co, sum, a, b, ci);
end
default:
begin: adder // другие - неопреленные ширины сумматоры с переносом
adder_cla #(WIDTH) x1(co, sum, a, b, ci);
end
endcase
// Иерархическое имя экземпляра - adder.x1
endgenerate
module dimm(addr, ba, rasx, casx, csx, wex, cke, clk, dqm, data, dev_id);
parameter [31:0] MEM_WIDTH = 16, MEM_SIZE = 8; // в мегабайтах
input [10:0] addr;
input ba, rasx, casx, csx, wex, cke, clk;
input [ 7:0] dqm;
inout [63:0] data;
input [ 4:0] dev_id;
genvar i;
case ({MEM_SIZE, MEM_WIDTH})
{32'd8, 32'd16}: // 8Meg x 16 бит в ширину
begin: memory
for (i=0; i<4; i=i+1) begin:word
sms_08b216t0 p(.clk(clk), .csb(csx), .cke(cke),.ba(ba),
.addr(addr), .rasb(rasx), .casb(casx),
.web(wex), .udqm(dqm[2*i+1]), .ldqm(dqm[2*i]),
.dqi(data[15+16*i:16*i]), .dev_id(dev_id));
// Иерархические имена экземпляров - memory.word[3].p,
// memory.word[2].p, memory.word[1].p, memory.word[0].p,
// и задача memory.read_mem
end
task read_mem;
input [31:0] address;
output [63:0] data;
begin // вызов read_mem в sms модуле
word[3].p.read_mem(address, data[63:48]);
word[2].p.read_mem(address, data[47:32]);
word[1].p.read_mem(address, data[31:16]);
word[0].p.read_mem(address, data[15: 0]);
end
endtask
end
{32'd16, 32'd8}: // 16Meg x 8 бит в ширину
begin: memory
for (i=0; i<8; i=i+1) begin:byte
sms_16b208t0 p(.clk(clk), .csb(csx), .cke(cke),.ba(ba),
.addr(addr), .rasb(rasx), .casb(casx),
.web(wex), .dqm(dqm[i]),
.dqi(data[7+8*i:8*i]), .dev_id(dev_id));
// Иерархические имена экземпляров - memory.byte[7].p,
// memory.byte[6].p, ... , memory.byte[1].p, memory.byte[0].p,
// и задача memory.read_mem
end
task read_mem;
input [31:0] address;
output [63:0] data;
begin // call read_mem in sms module
byte[7].p.read_mem(address, data[63:56]);
byte[6].p.read_mem(address, data[55:48]);
byte[5].p.read_mem(address, data[47:40]);
byte[4].p.read_mem(address, data[39:32]);
byte[3].p.read_mem(address, data[31:24]);
byte[2].p.read_mem(address, data[23:16]);
byte[1].p.read_mem(address, data[15: 8]);
byte[0].p.read_mem(address, data[ 7: 0]);
end
endtask
end
// Другие случаи памяти ...
endcase
endmodule
12.4.3 Внешние имена для безымянных генерируемых блоков
Хотя безымянный блок генерации не имеет имени, которое можно использовать в иерархическом имени, он должен иметь имя, по которому внешние интерфейсы смогут ссылаться на него. Для этого каждому неименованному генерирующему блоку присваивается имя, как описано в следующем параграфе.
Каждой generate конструкции в данной области видимости присваивается номер. Номер будет равен 1 для конструкции, которая текстово появляется первой в этой области видимости, и будет увеличиваться на 1 для каждой последующей конструкции generate в этой области видимости. Всем безымянным блокам генерации присваивается имя «genblk», где — номер, присвоенный конструкции generate. Если такое имя будет конфликтовать с явно объявленным именем, то перед номером добавляются ведущие нули до тех пор, пока имя не перестанет конфликтовать.
Каждой генераторной конструкции присваивается свой номер, как описано в предыдущем параграфе, даже если она не содержит никаких безымянных generate блоков.
Перевод Официального Стандарта Verilog HDL
module top;
parameter genblk2 = 0;
genvar i;
// Следующий блок генерации неявно назван genblk1
if (genblk2) reg a; // top.genblk1.a
else reg b; // top.genblk1.b
// Следующий блок генерации имеет неявное имя genblk02
// поскольку genblk2 уже является объявленным идентификатором
if (genblk2) reg a; // top.genblk02.a
else reg b; // top.genblk02.b
// Следующий блок generate был бы назван genblk3
// но имеет явное название g1
for (i = 0; i < 1; i = i + 1) begin : g1 // Имя блока
// Следующий блок генерации неявно назван genblk1
// как первая вложенная область видимости внутри g1
if (1) reg a; // top.g1[0].genblk1.a
end
// Следующий блок генерации неявно назван genblk4, поскольку
// он принадлежит четвертой конструкции generate в области видимости "top".
// Предыдущий блок генерации был бы
// назван genblk3, если он не был явно назван g1
for (i = 0; i < 1; i = i + 1)
// Следующий блок генерации неявно назван genblk1
// как первый вложенный блок generate в genblk4
if (1) reg a; // top.genblk4[0].genblk1.a
// Следующий блок генерации имеет неявное имя genblk5
if (1) reg a; // top.genblk5.a
endmodule