
Представленные до сих пор языковые конструкции позволяют описывать аппаратуру на относительно детальном уровне. Моделирование схемы с помощью логических вентилей и непрерывных назначений достаточно точно отражает логическую структуру моделируемой схемы. Однако, эти конструкции не обеспечивают той силы абстракции, которая необходима для описания сложных высокоуровневых аспектов системы. Процедурные конструкции, описанные в этом пункте, хорошо подходят для решения таких проблем, как описание микропроцессора или реализация сложных проверок синхронизации.
Этот пункт начинается с краткого обзора поведенческой модели, чтобы обеспечить контекст для многих типов поведенческих утверждений в Verilog HDL.
9.1 Обзор поведенческой модели
Поведенческие модели Verilog содержат процедурные конструкции, которые управляют моделированием и манипулируют переменными типов данных, описанных ранее. Эти утверждения содержатся в процедурах. Каждая процедура имеет связанный с ней поток действий.
Деятельность начинается с управляющих конструкций initial и always. Каждая конструкция initial и always запускает отдельный поток активности. Все потоки активности являются параллельными, чтобы смоделировать присущую аппаратуре параллельность. Формальное описание этих конструкций приведено в разделе 9.9.
module behave;
reg [1:0] a, b;
initial begin
a = 'b1;
b = 'b0;
end
always begin
#50 a = ~a;
end
always begin
#100 b = ~b;
end
endmodule
Во время моделирования этой модели все потоки, определяемые конструкциями initial и always, начинаются вместе в нулевой момент времени моделирования. Начальные конструкции выполняются один раз, а конструкции always выполняются многократно.
В этой модели переменные reg a и b инициализируются в 1 и 0, соответственно, в нулевой момент времени моделирования. После этого начальная конструкция завершается и больше не выполняется во время этого цикла моделирования. Эта начальная конструкция содержит блок операторов begin-end (также называемый последовательным блоком). В этом блоке begin-end сначала инициализируется a, а затем b.
Конструкции always также начинаются в нулевой момент времени, но значения переменных не изменяются до тех пор, пока не истечет время, заданное регуляторами задержки (введенными через #). Таким образом, reg a инвертируется через 50 единиц времени, а reg b — через 100 единиц времени. Поскольку конструкции всегда повторяются, эта модель будет генерировать две меандра. Reg a переключается с периодом 100 единиц времени, а reg b переключается с периодом 200 единиц времени. Эти две всегда повторяющиеся конструкции выполняются одновременно на протяжении всего времени моделирования.
9.2 Процедурные назначения
Как описано в пункте 6, процедурные назначения используются для обновления типов данных reg, integer, time, real, realtime и memory. Существует существенная разница между процедурными и непрерывными назначениями:
- Непрерывные назначения управляют сетками и оцениваются и обновляются всякий раз, когда входной операнд меняет значение.
- Процедурные назначения обновляют значение переменных под управлением окружающих их конструкций процедурного потока.
Правая сторона процедурного назначения может быть любым выражением, которое оценивается в значение. Левая часть должна быть переменной, которая получает назначение от правой части. Левая часть процедурного назначения может принимать одну из следующих форм:
- Тип данных reg, integer, real, realtime или time. Присвоение ссылки на имя одного из этих типов данных.
- Битовая выборка типа данных reg, integer или time. Назначение одного бита, при котором остальные биты остаются нетронутыми.
- Частичная выборка типа данных reg, integer или time. Частичная выборка одного или нескольких смежных битов, при которой остальные биты остаются нетронутыми.
- Слово памяти: одно слово памяти.
- Конкатенация или вложенная конкатенация любого из вышеперечисленных. Конкатенация или вложенная конкатенация любой из предыдущих четырех форм.
Такая спецификация эффективно разбивает результат выражения правой стороны и присваивает части разбиения, в порядке убывания, различным частям конкатенации или вложенной конкатенации.
Как описано в разделе 5.4, если правая часть оценивается в меньшее количество битов, чем левая часть, значение правой части подставляется в размер левой части. Если правая часть беззнаковая, она заполняется в соответствии с правилами, указанными в пункте 5.4.1. Если правая часть знаковая, она расширяется по знаку.
Verilog HDL содержит два типа процедурных операторов назначения:
- Блокирование процедурных операторов назначения(присваивания)
- Неблокирующие процедурные операторы назначения(присваивания)
Блокирующие и неблокирующие операторы процедурного назначения задают различные процедурные потоки в последовательных блоках.
9.2.1 Блокирование процедурных назначений(присваиваний)
Блокирующий процедурный оператор назначения должен быть выполнен до выполнения операторов, следующих за ним последовательном блоке(см. 9.8.1). Блокирующий процедурный оператор назначения не должен препятствовать выполнению операторов, следующих за ним параллельном блоке (см. 9.8.2).
Синтаксис блокирующего процедурного назначения приведен в Синтаксисе 9-1.
blocking_assignment ::=
variable_lvalue = [ delay_or_event_control ] expression
delay_control ::=
# delay_value
| # ( mintypmax_expression )
delay_or_event_control ::=
delay_control
| event_control
| repeat ( expression ) event_control
event_control ::=
@ hierarchical_event_identifier
| @ ( event_expression )
| @*
| @ (*)
event_expression ::=
expression
| posedge expression
| negedge expression
| event_expression or event_expression
| event_expression , event_expression
variable_lvalue ::=
hierarchical_variable_identifier [ { [ expression ] } [ range_expression ] ]
| { variable_lvalue { , variable_lvalue } }
В этом синтаксисе variable_lvalue — это тип данных, допустимый для процедурного оператора назначения. Символ = — оператор назначения, а delay_or_event_control — это необязательное управление временем внутри назначения. Управление может быть либо управлением задержки (например, #6), либо управлением события (например, @(posedge clk)).
Выражение — это значение правой части, которое должно быть присвоено левой части. Если variable_lvalue требует оценки, она должна быть оценена в момент времени, указанный внутриприсвоительным контролем времени.
Оператор назначения =, используемый в блокирующих процедурных назначениях, также используется процедурных непрерывных назначениях, непрерывных назначениях.
Например:
rega = 0;
rega[3] = 1; // битовая выборка(bit-select)
rega[3:5] = 7; // частичная выборка(part-select)
mema[address] = 8'hff; // присвоение элементу памяти
{carry, acc} = rega + regb; // конкатенация
9.2.2 Неблокирующее процедурное назначение(присвоение)
Неблокирующее процедурное назначение позволяет планировать присвоение без блокирования процедурного потока. Оператор неблокирующего процедурного назначения может быть использован в тех случаях, когда несколько присваиваний переменных на одном временном шаге могут быть выполнены без учета порядка или зависимости друг от друга.
Синтаксис неблокирующего процедурного назначения приведен в Синтаксисе 9-2.
nonblocking_assignment ::=
variable_lvalue <= [ delay_or_event_control ] expression
delay_control ::=
# delay_value
| # ( mintypmax_expression )
delay_or_event_control ::=
delay_control
| event_control
| repeat ( expression ) event_control
event_control ::=
@ hierarchical_event_identifier
| @ ( event_expression )
| @*
| @ (*)
event_expression ::=
expression
| posedge expression
| negedge expression
| event_expression or event_expression
| event_expression , event_expression
variable_lvalue ::=
hierarchical_variable_identifier [ { [ expression ] } [ range_expression ] ]
| { variable_lvalue { , variable_lvalue } }
В этом синтаксисе variable_lvalue — это тип данных, который действителен для процедурного оператора назначения, <= — это неблокирующий оператор назначения, а delay_or_event_control — это необязательный контроль времени внутри назначения. Если variable_lvalue требует оценки, она должна быть оценена одновременно с выражением в правой части. Порядок оценки значения variable_lvalue и выражения правой части не определен, если управление временем не указано.
Неблокирующий оператор назначения — это тот же оператор, что и реляционный оператор less- than-or-equal-to. Интерпретация должна определяться контекстом, в котором появляется <=. Когда <= используется в выражении, он должен интерпретироваться как реляционный оператор; а когда он используется в неблокирующем процедурном назначении, он должен интерпретироваться как оператор присвоения.
module evaluates2 (out);
output out;
reg a, b, c;
initial begin
a = 0;
b = 1;
c = 0;
end
always c = #5 ~c;
always @(posedge c) begin
a <= b; // evaluates, schedules,
b <= a; // and executes in two steps
end
endmodule
Шаг 1: В позе c симулятор оценивает правые части неблокирующих назначений и планирует присвоение новых значений в конце событий обновления неблокирующих назначений (см. 11.4). Неблокирующие назначение происходит каждом смене значения переменой c
a = 0
b = 1Шаг 2: Когда симулятор активирует события обновления неблокирующего назначения, симулятор обновляет левую часть каждого неблокирующего назначения.
Значения назначений:
a = 1
Перевод Официального Стандарта Verilog HDL
b = 0
В конце временного шага означает, что неблокирующие назначения являются последними присвоениями, выполненными на временном шаге. За одним исключением. События неблокирующих назначений могут создавать события блокирующих назначений. Эти блокирующие события назначения должны обрабатываться после запланированных неблокирующих событий.
В отличие от события или управления задержкой блокирующих присвоений, неблокирующее присвоение не блокирует процедурный поток. Неблокирующее присвоение оценивает, планирует присвоение, но не блокирует выполнение последующих операторов в блоке begin-end.
//non_block1.v
module non_block1;
reg a, b, c, d, e, f;
//blocking assignments
initial begin
a = #10 1; // a будет присвоено 1 через 10
b = #2 0; // b будет присвоено 0 через 12
c = #4 1; // c будет присвоено 1 через 16
end
//non-blocking assignments
initial begin
d <= #10 1; // d будет присвоено 1 через 10
e <= #2 0; // e будет присвоено 0 через 2
f <= #4 1; // f будет присвоено 1 через 4
end
endmodule
Запланированные изменения в момент времени 2: e = 0
Запланированные изменения в момент времени 4: f = 1
Запланированные изменения в момент времени 10: d = 1
Перевод Официального Стандарта Verilog HDL
Как показано в предыдущем примере, симулятор оценивает и планирует назначения на конец текущего временного шага. Также может выполнять операции замены с неблокирующими процедурными назначениями.
//non_block1.v
module non_block1;
reg a, b;
initial begin
a = 0;
b = 1;
a <= b; // evaluates, schedules, and
b <= a; // executes in two steps
end
initial begin
$monitor ($time, ,"a = %b b = %b", a, b);
#100 $finish;
end
endmodule
Шаг 1: Симулятор оценивает правую часть неблокирующих назначений и присваивает задания на конец текущего временного шага.
Шаг 2: В конце текущего временного шага симулятор обновляет левую часть каждого неблокирующего оператора назначения.
Значения назначений: a = 1 || b = 0
Перевод Официального Стандарта Verilog HDL
Порядок выполнения отдельных неблокирующих назначений для данной переменной должен сохраняться. Другими словами, если существует четкий порядок выполнения набора неблокирующих назначений, то порядок результирующих обновлений присвоения неблокирующих назначений должен быть таким же, как и порядок выполнения (см. 11.4.1).
module multiple;
reg a;
initial a = 1;
// Присвоенное значение reg является детерминированным
initial begin
a <= #4 0; // планирует a = 0 в момент времени 4
a <= #4 1; // планирует a = 1 в момент времени 4
end // Через 4, a = 1
endmodule
Если симулятор выполняет два процедурных блока одновременно и если эти процедурные блоки содержат неблокирующие операторы назначения одной и той же переменной, конечное значение этой переменной является неопределенным. Например, значение reg a является неопределенным в следующем примере:
module multiple2;
reg a;
initial a = 1;
initial a <= #4 0; // планируется 0 черезе 4
initial a <= #4 1; // планируется 1 черезе 4
// черезе 4, a = ??
// Присвоенное значение регистра является неопределенным
endmodule
Тот факт, что два неблокирующих присвоения одной и той же переменной находятся в разных блоках, сам по себе не является достаточным для того, чтобы сделать порядок присвоений переменной неопределенным. Например, значение reg a в конце временного цикла 16 является детерминированным в следующем примере:
module multiple3;
reg a;
initial #8 a <= #8 1; // выполняется в момент времени 8;
// планирует обновление на 1 в момент времени 16
initial #12 a <= #4 0; // выполняется в момент времени 12;
// планирует обновление 0 в момент времени 16
// Поскольку определено, что обновление a до значения 1
// запланировано до обновления a до значения 0,
// тогда определено, что a будет иметь значение 0
// в конце временного интервала 16.
endmodule
Следующий пример показывает, как значение i[0] назначается на r1 и как назначение планируется после каждой временной задержки:
Пример 7
module multiple4;
reg r1;
reg [2:0] i;
initial begin
// выполняет присвоение r1 без отмены предыдущих присвоений
for (i = 0; i <= 5; i = i+1)
r1 <= # (i*10) i[0];
end
endmodule

9.3 Процедурные непрерывные назначения
Процедурные непрерывные назначения (с использованием ключевых слов assign и force) — это процедурные назначения, которые позволяют непрерывно передавать выражения переменным или сетям. Синтаксис этих операторов приведен в Синтаксисе 9-3.
Левая часть назначения в операторе assign должна быть ссылкой на переменную или конкатенацией переменных. Это не должно быть слово памяти (ссылка на массив) или битовая или частичная выборка переменной.
Отличие от этого, левая часть назначения операторе force может быть ссылкой на переменную или сеть. Он может быть конкатенацией любого из вышеперечисленного. Битовая выборка и частичная выборка векторных переменных не допускается.
net_assignment ::=
net_lvalue = expression
procedural_continuous_assignments ::=
assign variable_assignment
| deassign variable_lvalue
| force variable_assignment
| force net_assignment
| release variable_lvalue
| release net_lvalue
variable_assignment ::=
variable_lvalue = expression
net_lvalue ::=
hierarchical_net_identifier [ { [ constant_expression ] } [ constant_range_expression ] ]
| { net_lvalue { , net_lvalue } }
variable_lvalue ::=
hierarchical_variable_identifier [ { [ expression ] } [ range_expression ] ]
| { variable_lvalue { , variable_lvalue } }
9.3.1 Процедурные назначения assign и deassign
Процедурный оператор непрерывного назначения assign должен переопределять все процедурные назначения переменной. Процедурный оператор deassign завершает процедурное непрерывное присвоение переменной. Значение переменной остается неизменным до тех пор, пока переменной не будет присвоено новое значение с помощью процедурного назначения или процедурного непрерывного назначения. Процедурные операторы assign и deassign позволяют, например, моделировать асинхронный сброс/очистку на D-типе flip-flop с фронтальным триггером, где тактовый генератор блокируется, когда активен сброс или предустановка.
Если ключевое слово assign применяется к переменной, для которой уже существует процедурное непрерывное назначение, то это новое процедурное непрерывное назначение должно деассигновать переменную перед выполнением нового процедурного непрерывного назначения.
Например:
module dff (q, d, clear, preset, clock);
output q;
input d, clear, preset, clock;
reg q;
always @(clear or preset)
if (!clear)
assign q = 0;
else if (!preset)
assign q = 1;
else
deassign q;
always @(posedge clock)
q = d;
endmodule
Если и clear, и preset низкие, то выход q будет постоянно удерживаться на соответствующем постоянном значении, и положительный фронт тактового генератора не повлияет на q. Если и clear, и preset высокие, то q дезассигнуется.
9.3.2 Процедурные назначения force и release
Другая форма процедурного непрерывного назначения обеспечивается процедурными операторами force и release. Эти операторы имеют схожий эффект с парой assign-deassign, но force может быть применена как к сеткам, так и к переменным. Левая сторона назначения может быть переменной, сеткой, константным битовой или частичной выборкой векторной сетки или конкатенацией. Это не может быть слово памяти (ссылка на массив), битовая выборка или частичная выборка векторной переменной.
Оператор force для переменной отменяет другое процедурное назначение или процедурное непрерывное назначение переменной до тех пор, пока не будет выполнен процедурный оператор release для переменной. При освобождении, если переменная не имеет активного процедурного непрерывного назначения assign, переменная не должна немедленно менять значение. Переменная сохраняет свое текущее значение до следующего процедурного присвоения или процедурного непрерывного присвоения переменной. Освобождение переменной, которая в настоящее время имеет активное процедурное непрерывное назначение assign, немедленно восстанавливает назначение.
Процедурный оператор force для сети должен переопределять все источники сети — выходы вентилей, выходы модулей и непрерывные назначения — до тех пор, пока не будет выполнен процедурный оператор release для сети. При освобождении сети немедленно присваивается значение, определенное источником сети.
module test;
reg a, b, c, d;
wire e;
and and1 (e, a, b, c);
initial begin
$monitor("%d d=%b,e=%b", $stime, d, e);
assign d = a & b & c;
a = 1;
b = 0;
c = 1;
#10;
force d = (a | b | c);
force e = (a | b | c);
#10;
release d;
release e;
#10 $finish;
end
endmodule
Результаты:
Перевод Официального Стандарта Verilog HDL
0 d=0,e=0
10 d=1,e=1
20 d=0,e=0
В этом примере экземпляр вентиля and «and1» «исправлен», чтобы действовать как вентиль or. Это сделано с помощью процедурного оператора force, который присваивает его выход на значение входов ORed.
force a = b + f(c) ;
Если b изменится или c изменится, a будет принудительно приведен к новому значению выражения b+f(c).