Вы находитесь на странице: 1из 8

6 Оператор generate

Оператор generate упрощает процесс создания параметризированных моделей. Позволяет


управлять декларациями переменных, функций и задач, созданием копий модулей и
примитивов, операторов непрерывного присвоения, блоков initial и always. Каждая
генерируемая копия имеет уникальное имя и может быть доступна через иерархическое имя.
Индексы в операторе generate задаются через переменную genvar с синтаксисом:

genvar list_of_genvar_identifiers ;

где list_of_genvar_identifiers – список переменных genvar.


Переменная декларируется в модуле, в котором она используется, в операторе generate и за его
пределами. В последнем случае она может быть использована несколькими блоками generate.
Переменная genvar имеет целочисленный тип и может принимать значения больше или равные 0.
Инициализируемое значение переменной genvar не должно равняться правой границе диапазаона ее
значений. Если два оператора generate используют в качестве индекса одну и ту же переменную, то они
не могут быть вложенными.
Параметр genvar существует только во время подготовки проекта к моделированию, во время
моделирования они недоступны.
Для поддержки межсоединений между структурными элементами и/или процедурными блоками, в
операторе generate допускает выполнять декларирование переменных обоих классов (net и register)
типов данных и событий event.
Пример использования оператора generate с конструкцией for, выполняющий генерацию нескольких
операторов присвоения assign, представлен листингом 1. Модуль xorGen содержит параметры,
определяющие число разрядов в устройстве width и величину задержки delay. Для управления
оператором generate определена переменная i, которая меняется в диапазоне [1: width]. При width =4
(значение по умолчанию) генерируются следующие четыре копии операторов непрерывного
присвоения.
assign #delay xout[1l] = xin1[1] ^ xin2[1];
assign #delay xout[2] = xin1[2] ^ xin2[2];
assign #delay xout[3] = xin1l[3] ^ xin2[3];
assign #delay xout[4] = xin1[4] ^ xin2[4];

Поскольку оператор generate выполняется во время инициализации моделирования, эти четыре


оператора становятся частью модуля xorGen и заменяют конструкцию generate … endgenerate.
Листинг 1. Генерирование копий операторов assign
module xorGen
#(parameter width = 4, delay =10)
(output [1:width] xout,
input [1:width] xin1, xin2);

generate
genvar i;
for (i = 1; i <= width; i=i + 1) begin: xi
assign #delay xout[i] = xin1[i] ^ xin2[i];
end
2
endgenerate
endmodule

В качестве операторов управления в конструкции generate, допускается использование for, if-then-else и


case.
В декларативную часть оператора generate могут быть включены задачи и функции. При этом они
должны иметь уникальное имя и могут быть доступны, через иерархическое имя.

1.1 Оператор generate с циклом for.


Оператор generate в комбинации с циклом for может создавать несколько копий заданных конструкций
Синтаксис:
generate {
for ( genvar_assignment ; constant_expression ; genvar_assignment )
begin: generate_block_identifier
{ generate_item }
end
} endgenerate
где
➢ genvar_assignment – начальное значение переменной genvar,
➢ constant_expression – условие завершения цикла,
➢ genvar_assignment – шаг цикла,
➢ generate_item – генерируемые операторы.
Вложенный блок begin-end имеет обязательную уникальную метку generate_block_identifier и доступен
через иерархическое имя. Фигурные скобки означают возможность присутствия нескольких копий
ограничиваемых ими конструкций.
При создании копий логических элементов каждая копия получает индивидуальное имя, которое
содержит в качестве суффикса строковое представление значения индекса genvar. Например, фрагмент
примера модели устройства из листинге 1, с реализацией с помощью стандартного примитива xor
может выглядеть следующим образом.
generate
genvar i;
for (i = 1; i <= width; i=i+1) begin: xi
xor #delay a (xout[i], xin1[i], xin2[i]);
end
endgenerate

Аналогичным образом могут быть созданы копии оператора always индексами от 1 до 4.


generate
genvar i;
for (i = 1; i <= width; i=i+1) begin: xi
always @(*)
xout[i] = xin1[i] ^ xin2[i];
end
endgenerate
3

Листинг 2,а) и 2,б) представляет еще один пример применения оператора generate с циклом for для
создания модели преобразователя кода Грея в двоичный код gray2bini. Разрядность входных и
выходных линий задаются параметром size по умолчанию равным 8.
Листинг 2. Преобразователь кода Грея в двоичный последовательный код
а) // с применением оператора непрерывного присвоения
module gray2bin1
#(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];
end endgenerate
endmodule

б) // с применением оператора always


module gray2bin2
#(parameter SIZE = 8)
(output [SIZE-1:0] bin, input [SIZE-1:0] gray);

reg [SIZE-1:0] bin;


genvar i;
generate for (i=0; i<SIZE; i=i+1) begin:bit
always @(gray[SIZE-1:i]) // fixed part select
bin[i] = ^gray[SIZE-1:i];
end endgenerate
endmodule

1.2. Условный оператор generate


Условный оператор generate использует конструкцию if-else-if с условиями, вычисляемыми на этапе
инициализации моделирования, что позволяет выборочно создавать копий операторов. Синтаксис:
generate
if ( constant_expression )
generate_item_or_null
[else generate_item_or_null ]
endgenerate
где constant_expression – условие генерирования операторов.
В модели устройства умножения multiplier, представленной листингом 3, параметры a_width и b_width
задают разрядность операндов в битах. Если один из параметров имеет значение меньшее 8,
реализуется устройство умножения CLA (carry look-ahead adder - сумматор с предварительным
просмотром в схеме ускоренного переноса). Если оба параметра a_width и b_width больше или равны 8,
то используется устройство умножения Wallace tree (Дерево Уоллеса). Генерируемые устройства имеют
метку u1.
Листинг 3 Параметризируемое устройство умножения
module multiplier
#(parameter a_width = 8, b_width = 8)
(input [a_width-1:0] a, [b_width-1:0] b,
output [a_width+b_width-1:0] product);
4

generate
if((a_width < 8) || (b_width < 8))
CLA_multiplier #(a_width,b_width) u1(a, b, product);
else
WALLACE_multiplier #(a_width,b_width) u1(a, b, product);
endgenerate
endmodule

1.3 Оператор generate с оператором case


Для условного выбора в операторе generate может быть также использована конструкция case.
Синтаксис:
generate
case ( constant_expression )
genvar_case_item { genvar_case_item } endcase
genvar_case_item ::= constant_expression { , constant_expression } :
generate_item_or_null | default [ : ] generate_item_or_null
endgenerate

Пример с листинга 4 представляет способ генерирования различных модулей сумматоров, в


зависимости от параметра WIDTH.
Листинг 4. Оператор generate с конструкцией case
generate
case (WIDTH)
1: adder_1bit x1(co, sum, a, b, ci); // 1-битовый сумматор
2: adder_2bit x1(co, sum, a, b, ci); // 2-битовый сумматор
default: adder_cla #(WIDTH) x1(co, sum, a, b, ci);
// сумматор CLA
endcase
endgenerate

1.4 Примеры использования оператора generate

Пример сумматора со сквозным переносом


Пример представляет реализацию вентильной модели сумматора со сквозным переносом (листинг 5).
Оператором generate выполняется генерирование копий имен для вентилей:
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.
5

Сгенерированные копии вентилей соединяются с помощью многомерного массива t типа wire,


определенного вне оператора generate (листинг 5, а)).
В примере с листинга 5, б) для связи вентилей используются три линии t1, t2, t3, декларация которых
выполнена внутри оператора generate. Для каждой итерации цикла создается собственные копии этих
переменных. 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

Листинг 5. Две модели сумматора со сквозным переносом


а) module addergen1
#(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;
generate
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
endgenerate

assign co = c[SIZE];
endmodule

б)module addergen2
#(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;
generate
for(i=0; i<SIZE; i=i+1) begin:bit
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
endgenerate

assign co = c[SIZE];
endmodule

Пример сумматора
Рассматривая моделирования n-битового сумматора (где n>1), который также имеет условные выходы,
обозначающие отрицательный результат, перенос или переполнение дополнительного кода (листинг 6). В этом
случае не генерируются копии сумматора. Операторы if-then-else и case могут быть использованы для
6
генерирования этих различных конфигурации. Листинг 8 представляет модуль, использующий оператор case для
создания различных конфигураций схемы сумматора adder в зависимости от того какие биты будут
генерироваться. Три различных ситуации обрабатываются отдельно. Для большинства состояний, carry in
соединяется с carry out предыдущего состояния. Самый младший бит (бит 0), перенос в блок является переносом
в устройство (cIn). Для самого старшего разряда (определяемого параметром width) должны быть реализованы:
перенос в старший разряд (cOut), overFlow и neg.
Листинг 6. Модель полного сумматора
module adderWithConditionCodes
#(parameter width = 1)
(output reg [width-1:0] sum,
output reg cOut, neg, overFlow,
input [width-1:0] a, b,
input cIn);

reg [width -1:0] c;


generate
genvar i;
for (i = 0; 1<= width-1; i=i+l) begin: stage
case(i)
0: begin
always @(*) begin
sum[i] = a[i] ^ b[i] ^ cIn;
c[i] = a[i]&b[i] | b[i]&cIn | a[i] & cIn;
end
end
width-1: begin
always @(*) begin
sum[i] = a[i] ^ b[i] ^ c[i-1];
cOut = a[i]&b[i] | b[i]&c[i-1] | a[i] & c[i-1];
neg = sum[i];
overFlow = cOut^ c[i-1];
end
end
default: begin
always @(*) begin
sum[i] = a[i] ^ b[i] ^ c[i-l];
c[i] = a[i]&b[i] | b[i]&c[i-1] | a[i] &c[i-l];
end
end
endcase
end
endgenerate
endmodule
1.4.3. Использование вложенных операторов generate
Модель, представленная листингом 7, иллюстрирует использование вложенных циклических операторов
циклических и условных операторов generate..
Листинг 7. Многоуровненый оператор generate
parameter SIZE = 2;
genvar i, j, k, m;

generate
for (i=0; i<SIZE+1; i=i+1) begin:B1 // область действия B1[i]
M1 N1(); // копии B1[i].N1[i] компонента M1
for (j=0; j<SIZE; j=j+1) begin:B2 // область действия B1[i].B2[j]
M2 N2(); // копии B1[i].B2[j].N2 компонента M2
for (k=0; k<SIZE; k=k+1) begin:B3
7
// область действия B1[i].B2[j].B3[k]
// копии B1[i].B2[j].B3[k].N3
M3 N3();
end
end
if (i>0)
for (m=0; m<SIZE; m=m+1) begin:B4
// область действия B1[i].B4[m]
M4 N4(); // копии B1[i].B4[m].N4
end
end
endgenerate

Некоторые имена, генерируемых в многоуровневом операторе generate имен копий компонентов:


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[0].N4 B1[1].B4[1].N4

Контрольные задания
7.1. Разработать структурную модель 32-битного двунаправленного трансивера, использующую в
качестве компонента функциональную модель 1-битного трансивера, который имеет два двунаправленных
входа данных – a и b. Асинхронный, активный по низкому уровню порт разрешения выходов oe_n. Порт
направления dir. Когда oe_n = 0 и dir = 0, данные передаются из b в a; если oe_n = 0 и dir = 1, то данные
передаются с a на b. Если oe_n = 1, то a и b имеют значение высокого импеданса.
7.2. Нарисовать схему, соответствующую следующему оператору generate:
generate
genvar stage
for (stage = 1; stage <= 4; stage = stage +1) begin: Ui
d_ff delay_ff (.clk(sys_clk),
.d(delayed_data[stage-1]), .q(delayed_data[stage]));
end
endgenerate

7.3. Создать условный оператор generate, который соединяет переменную external_clk с сигналом
internal_clk, если параметр positive_clk = 1. В противном случае на линию internal_clk подается инверсное
значение external_clk.
7.4. Разработать структурную модель устройства, состоящую из n мультиплексоров 2-в-1, соединенных
параллельно. Входами устройства являются сигнал управления Sel, два n-битовых вектора A и B. Выход
описан n-битовым сектором C. Величина n определена с помощью директивы `define.
8

7.5. Разработать структурную модель двоичного счетчика со сквозным переносом, реализованую на


D-триггерах. Количество компонентво задается через параметр n, который по-умолчанию равен 4.

Вам также может понравиться