diff --git a/.pic/Basic Verilog structures/assignments/fig_01.drawio.svg b/.pic/Basic Verilog structures/assignments/fig_01.drawio.svg new file mode 100644 index 0000000..4b5d2c9 --- /dev/null +++ b/.pic/Basic Verilog structures/assignments/fig_01.drawio.svg @@ -0,0 +1,4 @@ + + + +
logic [31:0] a,b,c;
initial begin
  a = 5;
  b = a;
  c = b;
end
a5b5c5
0ns
0ns
\ No newline at end of file diff --git a/.pic/Basic Verilog structures/assignments/fig_02.drawio.svg b/.pic/Basic Verilog structures/assignments/fig_02.drawio.svg new file mode 100644 index 0000000..c09a55a --- /dev/null +++ b/.pic/Basic Verilog structures/assignments/fig_02.drawio.svg @@ -0,0 +1,4 @@ + + + +55a35b23c72235abc
logic [31:0] a,b,c;
initial begin
  // ...
  a <= 5;
  b <= a;
  c <= b;
end
5ns
5ns
\ No newline at end of file diff --git a/.pic/Basic Verilog structures/assignments/fig_03.drawio.svg b/.pic/Basic Verilog structures/assignments/fig_03.drawio.svg new file mode 100644 index 0000000..db8f9c5 --- /dev/null +++ b/.pic/Basic Verilog structures/assignments/fig_03.drawio.svg @@ -0,0 +1,4 @@ + + + +
a
b
c
in
out
clk
\ No newline at end of file diff --git a/.pic/Basic Verilog structures/assignments/fig_04.png b/.pic/Basic Verilog structures/assignments/fig_04.png new file mode 100644 index 0000000..57e5876 Binary files /dev/null and b/.pic/Basic Verilog structures/assignments/fig_04.png differ diff --git a/.pic/Basic Verilog structures/assignments/fig_05.drawio.svg b/.pic/Basic Verilog structures/assignments/fig_05.drawio.svg new file mode 100644 index 0000000..db9b9c5 --- /dev/null +++ b/.pic/Basic Verilog structures/assignments/fig_05.drawio.svg @@ -0,0 +1,4 @@ + + + +
a
b
c
in
out
clk
\ No newline at end of file diff --git a/.pic/Basic Verilog structures/assignments/fig_06.png b/.pic/Basic Verilog structures/assignments/fig_06.png new file mode 100644 index 0000000..af67b95 Binary files /dev/null and b/.pic/Basic Verilog structures/assignments/fig_06.png differ diff --git a/.pic/Basic Verilog structures/assignments/fig_07.png b/.pic/Basic Verilog structures/assignments/fig_07.png new file mode 100644 index 0000000..3a95c05 Binary files /dev/null and b/.pic/Basic Verilog structures/assignments/fig_07.png differ diff --git a/.pic/Basic Verilog structures/assignments/fig_08.png b/.pic/Basic Verilog structures/assignments/fig_08.png new file mode 100644 index 0000000..c66be01 Binary files /dev/null and b/.pic/Basic Verilog structures/assignments/fig_08.png differ diff --git a/.pic/Basic Verilog structures/assignments/fig_09.png b/.pic/Basic Verilog structures/assignments/fig_09.png new file mode 100644 index 0000000..cbe0d91 Binary files /dev/null and b/.pic/Basic Verilog structures/assignments/fig_09.png differ diff --git a/.pic/Basic Verilog structures/assignments/fig_10.png b/.pic/Basic Verilog structures/assignments/fig_10.png new file mode 100644 index 0000000..4a35308 Binary files /dev/null and b/.pic/Basic Verilog structures/assignments/fig_10.png differ diff --git a/.pic/Basic Verilog structures/assignments/fig_11.png b/.pic/Basic Verilog structures/assignments/fig_11.png new file mode 100644 index 0000000..a5c4b8e Binary files /dev/null and b/.pic/Basic Verilog structures/assignments/fig_11.png differ diff --git a/.pic/Basic Verilog structures/assignments/fig_12.png b/.pic/Basic Verilog structures/assignments/fig_12.png new file mode 100644 index 0000000..c93ed4d Binary files /dev/null and b/.pic/Basic Verilog structures/assignments/fig_12.png differ diff --git a/.pic/Basic Verilog structures/assignments/fig_13.drawio.svg b/.pic/Basic Verilog structures/assignments/fig_13.drawio.svg new file mode 100644 index 0000000..aab00c1 --- /dev/null +++ b/.pic/Basic Verilog structures/assignments/fig_13.drawio.svg @@ -0,0 +1,4 @@ + + + +
logic [31:0] a,b,c;
always_comb begin
  temp <= a | b;
  d    <= c & temp;
end
abctempd01lӏxх|
5ns
5ns
Первый проход блока always
Второй проход блока always
\ No newline at end of file diff --git a/Basic Verilog structures/Assignments.md b/Basic Verilog structures/Assignments.md new file mode 100644 index 0000000..9c6630e --- /dev/null +++ b/Basic Verilog structures/Assignments.md @@ -0,0 +1,426 @@ +# Различие блокирующего и неблокирующего присваивания + +Вскоре после начала курса студенты сталкиваются с понятиями "блокирующего" и "неблокирующего" присваивания. Часто объяснения преподавателей по этой теме сопровождаются словами "последовательный" и "параллельный", а также предлагается _просто запомнить_[1, стр. 2](http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf): + +- При описании последовательностной логики (регистров) используйте **неблокирующее** присваивание. +- При описании комбинационной логики используйте **блокирующее** присваивание. + +Давайте разберемся что это за присваивания и почему необходимо руководствоваться этими правилами. + +Начать придется издалека. Несмотря на то, что SystemVerilog является **языком описания аппаратуры**, он так же является и языком для верификации описанной аппаратуры (слово `Verilog` является объединением двух слов: `verification` и `logic`). Для целей верификации в языке выделено целое подмножество конструкций, которые не могут быть использованы для описания аппаратуры — так называемое "_несинтезируемое подмножество языка SystemVerilog_". Разумеется, часть языка, которая может быть использована для описания аппаратуры ("_синтезируемое подмножество языка SystemVerilog_") тоже может использоваться в верификации (хотя бы для того, чтобы было что проверять, ведь проверяемая аппаратура описывается этими средствами). + +Давайте для начала разберемся в том, как будут использоваться операторы присваивания при программном моделировании (так называемой симуляции) — одним из инструментов верификации. Разобравшись в поведении операторов во время симуляции будет куда проще объяснить результат использования операторов при синтезе цифровой схемы. + +Для начала введем пару сокращений для удобства дальнейшего повествования: + +- под `LHS (left hand side)` мы будем подразумевать "выражение, **которому присваивают**"; +- под `RHS` (right hand side) мы будем подразумевать "выражение **которое присваивают**". + +В выражении `a = b+c`, `a` является `LHS`, `b+c` является `RHS`. + +Существует два вида присваиваний: **непрерывное** и **процедурное**. + +С непрерывным присваиванием вы знакомитесь в самом начале — это оператор `assign`. Непрерывное присваивание постоянно следит за `RHS` этого оператора, и каждый раз, когда любая часть этого выражения меняет своё значение, производит пересчёт значения `RHS`, а затем сразу же передает это значение `LHS`. Если мы произведем `assign a = b+c`, то каждый раз, когда будет меняться значение `b` или `c`, будет пересчитываться результат их суммы, который сразу же будет присвоен выражению `a`. + +**Непрерывное присваивание может быть использовано только вне программных блоков**. Под "программными блоками" подразумеваются блоки `always` (всех типов) и `initial`. Есть и другие программные блоки, но в рамках данного курса лабораторных работ вы с ними не столкнетесь. Вообще говоря, синтаксис языка SystemVerilog допускает использование оператора `assign` внутри программного блока, однако в рамках данного курса не существует ни одной ситуации, когда это может потребоваться и со 100% вероятностью будет ошибочно. + +В отличие от непрерывного присваивания, **процедурное присваивание может быть использовано только в программных блоках**. + +С точки зрения моделирования (не описания аппаратуры), программный блок — это программа (в привычном вам понимании парадигмы программирования), исполняющаяся в отдельном процессе. Программные блоки исполняются независимо друг от друга по определенным событиям. + +Блоки `initial` (их может быть много) исполняются в момент начала моделирования. Блоки `always` исполняются по событиям указанным в **списке чувствительности**: + +- `always @(posedge clk)` будет исполняться каждый раз когда произойдет положительный фронт `clk`; +- `always @(a,b,c)` будет исполняться каждый раз, когда изменится значение любого из сигналов `a`,`b`,`c`; +- `always @(*)` будет исполняться каждый раз, когда изменится состояние любой составляющей любого RHS в этом блоке (когда изменится хоть что-то, от чего зависит любое выражение слева от оператора присваивания в этом блоке). + +Похожие правила применимы и к остальным блокам `always`: `always_comb`, `always_ff`, `always_latch` (с некоторыми оговорками, не имеющими значения в рамках данного повествования). + +Под независимостью исполнения подразумевается то, что порядок исполнения блоков не зависит от очередности, в которой они были описаны. Более того, исполнение одного блока может быть приостановлено, чтобы исполнить другой блок. + +А вот выражения внутри отдельного программного блока (с точке зрения моделирования) исполняются последовательно. И вот тут на сцену выходят два типа программного присваивания: блокирующее и неблокирующее. + +Блокирующее присваивание (оператор `=`) **блокирует** исполнение дальнейших выражений до завершения вычисления `RHS` и присвоения вычисленного результата `LHS` (иными словами, это привычное вам присваивание из мира программирования, там все работает точно так же). + +Неблокирующее присваивание (оператор `<=`) производит вычисление `RHS`, запоминает его, и **откладывает** присваивание вычисленного значения, позволяя выполняться остальным выражениям до завершения присваивания `LHS`. + +Пример, представленный на _рис. 1_. + +![../.pic/Basic%20Verilog%20structures/assignments/fig_01.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_01.drawio.svg) + +_Рисунок 1. Пример цепочки блокирующих присваиваний._ + +1. Сперва вычисляется `RHS` первого присваивания программного блока — константа `5`. +2. Затем, вычисленное значение записывается в LHS первого присваивания — сигнал `a` становится равным `5`. +3. Далее вычисляется `RHS` следующего присваивания — `a`, которое к этому моменту уже равно `5`. +4. Поскольку вычисленное `RHS` равняется `5` то `LHS` второго присваивания (`b`) тоже становится равным `5`. +5. Аналогичным образом `c` тоже становится равным `5`. + +Обратите внимание, что все это произошло в нулевой момент времени. На временной диаграмме Vivado просто отобразится, что все сигналы одновременно стали равны `5`, однако с точки зрения симулятора это было не так. Другие симуляторы (например `QuestaSim`) позволяют настроить временную диаграмму таким образом, чтобы отображались все переходы между присваиваниями. + +Посмотрим, как работает аналогичная цепочка неблокирующих присваиваний. Чтобы иллюстрация была более наглядной, предположим, что перед присваиваниями был исполнен какой-то код, который привел `a` в состояние `3`, `b` в `2`, `c` в `7`. + +![../.pic/Basic%20Verilog%20structures/assignments/fig_02.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_02.drawio.svg) + +_Рисунок 2. Пример цепочки неблокирующих присваиваний._ + +1. Сперва вычисляется значение `RHS` первого присваивания (`5`). Присваивание этого значения **откладывается** на потом. +2. Затем вычисляется значение `RHS` второго присваивания. Поскольку `a` еще не присвоили значение `5`, результатом `RHS` становится текущее значение `a` — 3. Присваивание этого значения сигналу `b` **откладывается** на потом. +3. Аналогичным образом вычисляется `RHS` третьего присваивания (`2`). Присваивание этого значения **откладывается** на потом. + +Так называемое "**потом**" наступает когда завершается вычисление `RHS` всех неблокирующих присваиваний и завершение присвоений всех блокирующих присваиваний (однако "потом" все равно происходит в тот же момент времени, обратите внимание на значение времени на _рис. 2_). В стандарте SystemVerilog этот момент называется как `NBA-region` (сокр. от "Non-Blocking Assignment region") [2, стр. 61]. Выполнение отложенных присваиваний происходит в том же порядке, в котором они шли в программном блоке. Подробнее о том как работает событийная симуляция (event based simulation) в SystemVerilog вы можете прочесть в стандарте [IEEE 1800-2017](https://ieeexplore.ieee.org/document/8299595) (раздел 4). Стандарт доступен бесплатно всем желающим по программе "IEEE GET Program". + +Таким образом, если `LHS` блокирующего присваивания используется в качестве операнда `RHS` любого другого последующего присваивания, результат предыдущего выражение будет передан дальше **в тот же момент времени**, что очень похоже на "_последовательное вычисление_". + +С другой стороны `LHS` неблокирующего присваивания не может использоваться в качестве операнда `RHS` последующих присваиваний, что создает иллюзию "_параллельного вычисления_". + +Теперь, понимая как работают присваивания с точки зрения моделирования, посмотрим на то, во что могут синтезироваться подобные операторы. + +Начнем с непрерывного присваивания. Оно превращается в провод, передающий данные от `RHS` к `LHS`. При вы должны контролировать что к чему вы присваиваете (не путайте местами `RHS` и `LHS`). + +То во что синтезируются блокирующие и неблокирующие присваивания зависят от описываемой логики, поэтому давайте разберем несколько примеров. + +Рассмотрим исходный примера c цепочкой блокирующих присваиваний, только теперь перепишем его в синтезируемом виде, сохранив изначальную идею. + +```SystemVerilog +module example_1( + input logic clk, + input logic [31:0] in, + output logic [31:0] out +); + +logic [31:0] a,b,c; + +always_ff @(posedge clk) begin + a = in; + b = a; + c = b; +end + +assign out = c; + +endmodule +``` + +_Листинг 1. Пример описания модуля, использующего цепочку блокирующих присваиваний._ + +Если вы уже знакомы с содержимым документа о том, [как описывать регистры](Registers.md), подумайте, какой будет результат синтеза у этой схемы? + +--- + +Давайте "прочитаем" эту схему. Мы видим модуль, с входом `in`, выходом `out` и тактирующим синхроимпульсом `clk`. Так же мы видим три сигнала `a`,`b`,`c`, которые описываются в блоке `always_ff`, предназначенном для описания регистров. Значение `in` по цепочке этих регистров передается до регистра `c`, выход которого подключен к выходу `out`. + +Похоже, что здесь был описан [**сдвиговый регистр**](https://ru.wikipedia.org/wiki/Регистр_(цифровая_техника)#Сдвигающие_(последовательные)_регистры), представленный на _рис. 3_. + +![../.pic/Basic%20Verilog%20structures/assignments/fig_03.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_03.drawio.svg) + +_Рисунок 3. Трехразрядный сдвиговый регистр._ + +Давайте откроем цифровую схему, сгенерированную Vivado и убедимся в наших выводах. + +![../.pic/Basic%20Verilog%20structures/assignments/fig_04.png](../.pic/Basic%20Verilog%20structures/assignments/fig_04.png) + +_Рисунок 4. Схема, сгенерированная Vivado по описанию из Листинга 1._ + +Произошло что-то странное. Вместо трех регистров Vivado создал только один и судя по названию — это последний регистр `c`. Почему это произошло? + +Изучим внимательней поведение цепочки блокирующих присваиваний, представленное на _рис. 1_. + +Каждое последующее присваивание ожидало, пока не выполнится предыдущее, таким образом, `RHS` первого присваивания (`5`) сразу же (без каких-либо задержек) распространился по всем регистрам. Моделируя _Листинг 1_ мы получим **поведение**, когда на вход каждого регистра будет подаваться сигнал `in`. + +Таким образом на самом деле, мы должны были изображать нашу схему как на _рис. 5_. + +![../.pic/Basic%20Verilog%20structures/assignments/fig_05.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_05.drawio.svg) + +_Рисунок 5. Схема, описанная Листингом 1._ + +Но почему тогда на схеме Vivado не осталось регистров `a` и `b`? Посмотрим на них внимательней. Их выходы ни на что не влияют, они висят неподключенные. А значит эти регистры не имеют никакого смысла и если их убрать, ничего не изменится. + +При генерации схемы, Vivado вывел в `Tcl Console` следующие предупреждения: + +```text +WARNING: [Synth 8-6014] Unused sequential element a_reg was removed. [block_assignment.sv:10] +WARNING: [Synth 8-6014] Unused sequential element b_reg was removed. [block_assignment.sv:11] +``` + +Если вы используете Vivado 2023.1 и новее, вы можете обнаруживать подобные ошибки более удобным способом — посредством линтера, который можно вызвать из вкладки `RTL ANALYSIS` окна `Flow Navigator`. + +![../.pic/Basic%20Verilog%20structures/assignments/fig_06.png](../.pic/Basic%20Verilog%20structures/assignments/fig_06.png) + +_Рисунок 6. Пример вызова линтера._ + +Давайте заменим в _Листинге 1_ блокирующие присваивания на неблокирующие. Напоминаем, что оператор неблокирующего присваивания записывается как `<=`. + +```SystemVerilog +module example_2( + input logic clk, + input logic [31:0] in, + output logic [31:0] out +); + +logic [31:0] a,b,c; + +always_ff @(posedge clk) begin + a <= in; + b <= a; + c <= b; +end + +assign out = c; + +endmodule +``` + +_Листинг 2. Пример описания модуля, использующего цепочку неблокирующих присваиваний._ + +Посмотрим, какую схему сгенерирует Vivado в этот раз. + +![../.pic/Basic%20Verilog%20structures/assignments/fig_07.png](../.pic/Basic%20Verilog%20structures/assignments/fig_07.png) + +_Рисунок 7. Схема, сгенерированная Vivado по описанию из Листинга 2._ + +Вряд ли полученный результат стал для вас неожиданным сюжетным поворотом, но давайте разберемся, почему в этот раз сгенерировалась схема, аналогичная представленной на _рис. 3_. + +Для этого обратимся к примеру, представленному на _рис. 2_. В данном примере **неблокирующем присваивании** сперва вычислялись значения всех `RHS` (запоминались значения выходов всех регистров) и только потом происходило присваивание новых значений. Подобное **поведение** аналогично поведению сдвиговых регистров. + +Слово "_поведение_" было выделено дважды неспроста. Описание схем, которое мы сделали называется "**поведенческим описанием схемы**". + +Можно ли реализовать сдвиговый регистр, используя блокирующие присваивания? Конечно. Например, можно поменять порядок присваиваний как в _Листинге 3_. + +```SystemVerilog +module example_3( + input logic clk, + input logic [31:0] in, + output logic [31:0] out +); + +logic [31:0] a,b,c; + +always_ff @(posedge clk) begin + c = b; + b = a; + a = in; +end + +assign out = c; + +endmodule +``` + +_Листинг 3. Цепочка блокирующих присваиваний в порядке обратном приведенному в Листинге 1._ + +В этом случае, линтер не сообщит ни о каких ошибках, а Vivado сгенерирует схему, аналогичную _рис. 7_ + +Так произошло, поскольку мы разорвали зависимость значений `RHS` последующих присваиваний от значений `LHS` предыдущих (поведение, которое демонстрировала цепочка неблокирующих присваиваний с самого начала). Однако данное решение является скорее хаком, чем примером хорошего проектирования. По сути, мы просто подстроили код, описанный с помощью блокирующих присваиваний таким образом, чтоб он вел себя как код, использующий неблокирующие присваивания. + +Важно отметить, что при использовании неблокирующих присваиваний, их порядок вообще не имеет значения. + +Давайте разнесем логику работы каждого регистра по отдельным блокам `always`. + +```SystemVerilog +module example_4( + input logic clk, + input logic [31:0] in, + output logic [31:0] out +); + +logic [31:0] a,b,c; + +always_ff @(posedge clk) begin + a = in; +end + +always_ff @(posedge clk) begin + b = a; +end + +always_ff @(posedge clk) begin + c = b; +end + +assign out = c; + +endmodule +``` + +_Листинг 4. Сдвиговый регистр, описанный через блокирующие присваивания в отдельных блоках always._ + +Сгенерированная в Vivado схема будет аналогична _рис. 7_. Но давайте попробуем промоделировать работу этой схемы, подавая случайные воздействия на вход `in`. + +![../.pic/Basic%20Verilog%20structures/assignments/fig_08.png](../.pic/Basic%20Verilog%20structures/assignments/fig_08.png) + +_Рисунок 8. Симуляция модуля, описанного Листингом 4._ + +Выглядит как-то не по "сдвигово-регистрерски". В чем же дело? + +Как уже упоминалось ранее, программные блоки (коими являются блоки `always`) исполняются во время моделирования независимо друг от друга в недетерминированном стандартом порядке. На практике это означает то, что сперва может исполниться второй блок, потом третий, а потом первый — либо в любом другом порядке. Так сделано для того, чтобы разработчик не мог рассчитывать на порядок блоков `always` при описании. + +Конкретно в данной ситуации, симулятор воспроизвел блоки ровно в том порядке, в котором они были описаны. Сперва `a` получил значение `in`, потом `b` получил обновленное значение `a`, затем `c` получил обновленное значение `b`. + +Поскольку поведение недетерминировано, нельзя однозначно сказать, какую схему должен был воспроизвести синтезатор. В данной ситуации, он воспроизвел схему которую мы хотели получить, что не помогло, тем не менее с моделированием. + +Если заменить порядок `always` подобно тому, как мы изменили порядок в _Листинге 3_, результат на временной диаграмме совпадет с поведением сдвигового регистра. + +![../.pic/Basic%20Verilog%20structures/assignments/fig_09.png](../.pic/Basic%20Verilog%20structures/assignments/fig_09.png) + +_Рисунок 9. Моделирование поведения сдвигового регистра._ + +Однако, как уже объяснялось ранее, вы не можете рассчитывать на такой результат. Сегодня симулятор смоделировал поведение одним образом — завтра он смоделирует этот же код, в котором не изменилась ни одна строка, и будет по прежнему работать в соответствии со стандартом. + +Для того, чтобы получить детерминированный результат, вам необходимо снова воспользоваться неблокирующим присваиванием, поскольку и в этом случае порядок исполнения блоков `always` не влияет на результат присваиваний — сначала вычисляются значения `RHS` всех неблокирующих присваиваний всех программных блоков, и только потом происходит присваивание этих значений `LHS`. + +Рассмотрим еще один пример того, как различие в присваиваниях приведет к описанию двух различных схем: + +```SystemVerilog +module example_5( + input logic clk, + input logic a, b, c, + output logic d +); + +logic temp; + +always_ff @(posedge clk) begin + temp = a | b; + d = c & temp; +end + +endmodule +``` + +_Листинг 5. Пример цепочки блокирующих присваиваний с комбинационной логикой._ + +Остановитесь на минуту и подумайте, схему с каким поведением описывает _Листинг 5_? + +--- + +Как вы могли догадаться, данный листинг описывает схему с одним регистром `d`, на вход которого подается результат комбинационной логики `c & (a | b)`, поскольку сперва в `temp` попадает результат `a | b` и только после этого вычисляется значение `c & temp`. + +![../.pic/Basic%20Verilog%20structures/assignments/fig_10.png](../.pic/Basic%20Verilog%20structures/assignments/fig_10.png) + +_Рисунок 10. Схема, сгенерированная Vivado по описанию из Листинга 5._ + +Попробуйте догадаться о том, что произойдет, если снова заменить блокирующие присваивания на неблокирующие? + +--- + +Результат изменится следующим образом. + +![../.pic/Basic%20Verilog%20structures/assignments/fig_11.png](../.pic/Basic%20Verilog%20structures/assignments/fig_11.png) + +_Рисунок 11. Схема, сгенерированная Vivado по описанию из Листинга 5 после замены блокирующих присваиваний на неблокирующие._ + +Из прочтенного может сложиться впечатление, будто бы автор хочет показать, что блокирующее присваивание — это плохо, а неблокирующее — хорошо, однако это не так. Это просто два похожих инструмента, работающих разными способами, о которых должен знать профессионал, использующий эти инструменты. + +Одно и тоже описание, использующее разные типы присваиваний приводит к синтезу разных схем. + +Рассмотрим предыдущий пример еще раз. Нельзя сказать, что одна схема лучше другой — это просто две разные схемы и то, какая из них вам нужна зависит только от вашей задачи. + +Однако нельзя не заметить, что при использовании блокирующего присваивания, мы "теряли" регистры. + +Пока что мы рассматривали только синхронные схемы (схемы, работающие по тактовому синхроимпульсу). + +Рассмотрим зависимость от типа присваивания в комбинационных схемах. Для этого возьмем предыдущий пример, у уберем тактирующий синхроимпульс. + +```SystemVerilog +module example_6( + input logic a, b, c, + output logic d +); + +logic temp; + +always_comb begin + temp = a | b; + d = c & temp; +end + +endmodule +``` + +_Листинг 6. Пример цепочки блокирующих присваиваний в комбинационной схеме._ + +> Обратите внимание на то, что изменился и блок always. + +Как вы думаете, какая схема будет сгенерирована по описанию, представленному _Листинга 6_, и что произойдет с этой схемой, если заменить в нем все блокирующие присваивания на неблокирующие? + +--- + +Вас может это удивить, но в обоих случаях будет сгенерирована схема, представленная на _рис. 12_. + +![../.pic/Basic%20Verilog%20structures/assignments/fig_12.png](../.pic/Basic%20Verilog%20structures/assignments/fig_12.png) + +_Рисунок 12. Схема, сгенерированная Vivado по описанию из Листинга 6._ + +> Девочка остолбенела. У неё возникло отчётливое чувство какой-то ужасной несправедливости по отношению к ней. Гарри Поттер был грязным, отвратительным обманщиком и лжецом. Но во время игры все его ответы были верными. [Элиезер Юдковский / Гарри Поттер и методы рационального мышления] + +На протяжении всего документа вам рассказывали, что использование блокирующих присваиваний приведет к изменению поведения и синтезу другой схемы, а теперь сами же приводят пример, где схема остается точно такой же! + +Давайте разберемся по порядку, что же произошло. + +Все дело в изменении блока `always`. Когда мы использовали `always_ff @(posedge clk)`, этот программный блок исполнялся только один раз за такт. + +Теперь, когда мы стали использовать блок `always_comb`, правила игры изменились. Нет, принцип работы блокирующих и неблокирующих присваиваний остался тем же самым. Изменилось только то, сколько раз будет вызван данный блок. + +Начнем со схемы, построенной по блокирующему присваиванию. В общем-то, тут у вас не должно было возникнуть вопросов, логика ровно та же, что была и при построении схемы по _Листингу 5_ (_рис. 10)_, только без выходного регистра. Что логично, ведь мы убрали тактирующий сигнал. + +Вопрос в том, почему это вдруг схема, построенная после замены блокирующих присваиваний на неблокирующие ведет себя точно так же? + +Рассмотрим _рис. 13_. + +![../.pic/Basic%20Verilog%20structures/assignments/fig_13.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_13.drawio.svg) + +_Рисунок 13. Моделирование цепочки присваиваний в комбинационном блоке `always`._ + +Комбинационный блок `always` начинает исполняться **каждый раз**, когда операнд любого `RHS` этого блока меняет своё значение. + +Изначально, блок `always` начал исполняться, когда операнды `a`, `b` и `c` приняли значение `1`, `0` и `1` соответственно. В этот момент (поскольку присваивание неблокирующее), вычисляются значения `RHS` для присваивания сигналам `temp` и `d`. Новое значение `temp` будет `1`, но пока что этого не произошло, и `temp` все ещё в состоянии `x`, поэтому новым значением `d` все ещё будет `x`. + +После происходит присваивание вычисленных значений сигналам `temp` и `d`. + +Однако `temp` является операндом `RHS` в выражении `d = c & temp`, поэтому блок `always`. Запускается еще один раз в этот же момент времени (в примере это `5ns`). Поскольку значения `a` и `b` не менялись, значение `RHS` первого выражения останется прежним. А вот значение `temp` уже иное, поэтому `RHS` второго выражения станет `1`. + +После повторного присваивания сигналы `temp` и `d` примут установившееся значение. Поскольку ни один из операндов `RHS`—выражений больше не изменил своего значения, блок `always` больше не вызывается. + +Подобное поведение можно сравнить с переходным процессом в комбинационной схеме. + +Обратите внимание, поведение схем описанных при разных типах присваивания слегка различаются. При блокирующем присваивании все сигналы приняли установившиеся значения за один проход блока `always`, при неблокирующем потребовалось несколько проходов. Однако с точки зрения пользователя, читающего временную диаграмму, в обоих ситуациях сигналы изменили свое значение мгновенно. + +Поэтому не смотря на различия в типах присваиваний схемы получились одинаковыми. + +> Получается что для комбинационной логики нет разницы между блокирующим и неблокирующим присваиванием, после переходных процессов результат будет одинаковым? + +И да и нет. С точки зрения синтеза схемы так и есть. Однако есть нюанс в случае моделирования схемы. + +Особенность планировщика событий симуляции такова, что все `RHS` неблокирующих присваиваний будут вычислены после выполнения всех блокирующих присваиваний. + +Чем это может быть удобно? + +Давайте сделаем небольшое лирическое отступление и разберем часто используемую в мире разработки цифровых схем аббревиатуру `RTL`. Она расшифровывается как `Register Transfer Level` — уровень межрегистровых передач. Цифровую схему можно рассматривать на различных уровнях абстракции. Можно рассматривать с физического уровня: как набор проводников, транзисторов, а так же электронов и "дырок", бегающих по ним. Можно рассматривать схему на уровне логических вентилей (gate-level). Одним из таких уровней абстракции является уровень межрегистровых передач, когда мы делим схему на две части: регистры, и комбинационную логика их связывающая. + +По каждому такту синхроимпульса, в регистры будет записываться новое значение, что приведет к изменению на выходе регистра и начнет цепочку изменений в комбинационной логике, соединяющей регистры. К следующему такту на концах комбинационной логике, являющихся входами регистров должны оказаться установившееся значения, которые будут записаны и все повторится снова. + +Было бы удобно разделить эти две части: обновление значений в регистрах и обновление значений в комбинационной логике таким образом, чтобы сперва обновлялась комбинационная логика, а потом уже обновлялось значение в регистрах. + +Именно этого можно добиться, если комбинационная логика будет описана посредством блокирующих присваиваний, которые будут выполнены до неблокирующих присваиваний, используемых при описании регистров. + +Подведем итоги прочитанному: + +- Блокирующее присваивание блокирует выполнение остальных операций до завершения текущего присваивания. Оно подобно обычному присваиванию в парадигме программирования. +- Неблокирующее присваивание сперва вычисляет `RHS`, давая исполниться остальным операциям до самого присваивания. Причем `RHS` в случае, если после неблокирующего присваивания выполняется блокирующее от `LHS` которого зависело `RHS` неблокирующего присваивания — значение `RHS` будет обновлено. + +В связи с особенностями поведения блокирующего и неблокирующего присваивания, выведены следующие две максимы: + +- При описании последовательностной логики (регистров) используйте **неблокирующее** присваивание. +- При описании комбинационной логики используйте **блокирующее** присваивание. + +Кроме того, существуют следующие рекомендации и требования[1, стр. 5]: + +- _При описании как последовательностной логики, так и комбинационной в одном блоке `always` используйте неблокирующее присваивание._ +- _Не смешивайте в одном блоке блокирующие и неблокирующие присваивания_ — стандарт допускает подобное описание, но оно затрудняет его чтение. +- _Не смешивайте блокирующие и неблокирующие присваивания для одного и того же сигнала_ — стандарт это запрещает. + +Использованная литература: + +1. [Clifford E. Cummings / Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kil](http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf) +2. [1800-2017 - IEEE Standard for SystemVerilog--Unified Hardware Design, Specification, and Verification Language](https://ieeexplore.ieee.org/document/8299595)