12. Generate блоки Verilog HDL

Синтаксис конструкции generate(блока генерации) Verilog HDL
Содержание

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-5-Синтаксис для конструкций generate

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 во время оценки схемы генерации цикла.

Например:

Пример 1-Примеры законных и незаконных порождающих циклов
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
Пример 2 — Параметризованный gray-code–to–binary-code с использованием цикла для генерации непрерывных назначений
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 объявление сети находится внутри цикла генерации для создания проводов, необходимых для соединения примитивов вентилей для каждой итерации цикла.

Пример 3 — Генерируемый пульсационный сумматор с объявлением двумерной сети вне цикла генерации
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
Пример 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; 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).

Пример 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. Она никак не относится к конструкциям генерации циклов.

Пример 1
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.

При правильном использовании параметров, результирующая рекурсия может быть завершена, что приводит к созданию правильной иерархии модели. Из-за правил определения модулей верхнего уровня модуль, содержащий экземпляр самого себя, не будет модулем верхнего уровня.

Пример 2 — реализация модуля параметризованного умножения
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
Пример 3 — Генерация с помощью case для обработки шириной менее 3
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
Пример 4 — модуль памяти dimm
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