Рефактор присваиваний

This commit is contained in:
Andrei Solodovnikov
2024-02-06 16:11:07 +03:00
parent a7d5552121
commit b32a67fa20
15 changed files with 98 additions and 84 deletions

1
.github/SUMMARY.md vendored
View File

@@ -45,7 +45,6 @@
- [Защелки](Basic%20Verilog%20structures/Latches.md)
- [О различиях между блокирующими и неблокирующими присваиваниями](Basic%20Verilog%20structures/Assignments.md)
- [Контроллеры](Basic%20Verilog%20structures/Controllers.md)
- [Тестовое окружение](Basic%20Verilog%20structures/Testbench.md)
---

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 361 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 218 KiB

After

Width:  |  Height:  |  Size: 218 KiB

View File

@@ -7,24 +7,43 @@
Давайте разберемся что это за присваивания и почему необходимо руководствоваться этими правилами.
Начать придется издалека. Несмотря на то, что SystemVerilog является **языком описания аппаратуры**, он так же является и языком для верификации описанной аппаратуры (слово `Verilog` является объединением двух слов: `verification` и `logic`). Для целей верификации в языке выделено целое подмножество конструкций, которые не могут быть использованы для описания аппаратуры — так называемое "_несинтезируемое подмножество языка SystemVerilog_". Разумеется, часть языка, которая может быть использована для описания аппаратуры ("_синтезируемое подмножество языка SystemVerilog_") тоже может использоваться в верификации (хотя бы для того, чтобы было что проверять, ведь проверяемая аппаратура описывается этими средствами).
Начать придется издалека. Несмотря на то, что SystemVerilog является **языком описания аппаратуры**, он так же является и языком для верификации описанной аппаратуры (слово `Verilog` является объединением двух слов: `verification` и `logic`). Для целей верификации в языке выделено целое подмножество конструкций, которые не могут быть использованы для описания аппаратуры — так называемое "_несинтезируемое подмножество языка SystemVerilog_". Разумеется, часть языка, которая может быть использована для описания аппаратуры ("_синтезируемое подмножество языка SystemVerilog_") тоже может использоваться в верификации.
Давайте для начала разберемся в том, как будут использоваться операторы присваивания при программном моделировании (так называемой симуляции) — одним из инструментов верификации. Разобравшись в поведении операторов во время симуляции будет куда проще объяснить результат использования операторов при синтезе цифровой схемы.
Для начала введем пару сокращений для удобства дальнейшего повествования:
Введем пару сокращений для удобства дальнейшего повествования:
- под `LHS` (left hand side) мы будем подразумевать "выражение, **которому присваивают**";
- под `RHS` (right hand side) мы будем подразумевать "выражение **которое присваивают**".
- под `LHS` (left hand side) мы будем подразумевать "выражение, **которому** присваивают";
- под `RHS` (right hand side) мы будем подразумевать "выражение **которое** присваивают".
В выражении `a = b+c`, `a` является `LHS`, `b+c` является `RHS`.
Существует два вида присваиваний: **непрерывное** и **процедурное**.
два вида присваиваний: **непрерывное** и **процедурное**.
```SystemVerilog
module assignment_example(
input logic a, b
output logic c, d
);
// непрерывное присваивание
assign c = a + b;
// процедурное присваивание
always_comb begin
d = a + b;
end
endmodule
```
_Листинг 1. Пример непрерывного и процедурного присваивания._
С непрерывным присваиванием вы знакомитесь в самом начале — это оператор `assign`. Непрерывное присваивание постоянно следит за `RHS` этого оператора, и каждый раз, когда любая часть этого выражения меняет своё значение, производит пересчёт значения `RHS`, а затем сразу же передает это значение `LHS`. Если мы произведем `assign a = b+c`, то каждый раз, когда будет меняться значение `b` или `c`, будет пересчитываться результат их суммы, который сразу же будет присвоен выражению `a`.
**Непрерывное присваивание может быть использовано только вне программных блоков.**
Под "программными блоками" подразумеваются блоки `always` (всех типов) и `initial`. Есть и другие программные блоки, но в рамках данного курса лабораторных работ вы с ними не столкнетесь. Вообще говоря, синтаксис языка SystemVerilog допускает использование оператора `assign` внутри программного блока, однако в рамках данного курса не существует ни одной ситуации, когда это может потребоваться и со 100% вероятностью будет ошибочно.
Под "программными блоками" подразумеваются блоки `always` (всех типов) и `initial`. Есть и другие программные блоки, но в рамках данного курса лабораторных работ вы с ними не столкнетесь. Вообще говоря, синтаксис языка SystemVerilog допускает использование оператора `assign` внутри программного блока (так называемое "**процедурное непрерывное присваивание**")[[2, стр. 232]](https://ieeexplore.ieee.org/document/8299595), однако в рамках данного курса не существует ни одной ситуации, когда это может потребоваться и со 100% вероятностью будет ошибочно.
В отличие от непрерывного присваивания, **процедурное присваивание может быть использовано только в программных блоках**.
@@ -70,11 +89,15 @@ _Рисунок 2. Пример цепочки неблокирующих при
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".
Так называемое "**потом**" наступает когда завершается вычисление `RHS` всех неблокирующих присваиваний и завершение присвоений всех блокирующих присваиваний (однако "потом" все равно происходит в тот же момент времени, обратите внимание на значение времени на _рис. 2_). В стандарте SystemVerilog этот момент называется `NBA-region` (сокр. от "Non-Blocking Assignment region") [[2, стр. 61]](https://ieeexplore.ieee.org/document/8299595). Выполнение отложенных присваиваний происходит в том же порядке, в котором они шли в программном блоке. Подробнее о том как работает событийная симуляция (event based simulation) в SystemVerilog вы можете прочесть в стандарте [IEEE 1800-2017](https://ieeexplore.ieee.org/document/8299595) (раздел 4). Стандарт доступен бесплатно всем желающим по программе "IEEE GET Program".
Таким образом, если `LHS` блокирующего присваивания используется в качестве операнда `RHS` любого другого последующего присваивания, результат предыдущего выражения будет передан дальше **в тот же момент времени**, что очень похоже на "_последовательное вычисление_".
Таким образом, если `LHS` **блокирующего** присваивания используется в качестве операнда `RHS` любого другого последующего присваивания, это выражение будет иметь уже обновленное значение, что очень похоже на "_последовательное вычисление_".
С другой стороны `LHS` неблокирующего присваивания не может использоваться в качестве операнда `RHS` последующих присваиваний, что создает иллюзию "_параллельного вычисления_".
С другой стороны значение, присвоенное `LHS` значение с помощью **неблокирующего** присваивания не может использоваться в качестве операнда `RHS` последующих присваиваний, что создает иллюзию "_параллельного вычисления_" (см. _рис. 3_).
![../.pic/Basic%20Verilog%20structures/assignments/fig_03.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_03.drawio.svg)
_Рисунок 3. Иллюстрация блокирующих и неблокирующих присваиваний._
Теперь, понимая как работают присваивания с точки зрения моделирования, посмотрим на то, во что могут синтезироваться подобные операторы.
@@ -104,37 +127,37 @@ assign out = c;
endmodule
```
_Листинг 1. Пример описания модуля, использующего цепочку блокирующих присваиваний._
_Листинг 2. Пример описания модуля, использующего цепочку блокирующих присваиваний._
Если вы уже знакомы с содержимым документа о том, [как описывать регистры](Registers.md), подумайте: какой будет результат синтеза у этой схемы?
---
Давайте "прочитаем" эту схему. Мы видим модуль, с входом `in`, выходом `out` и тактирующим синхроимпульсом `clk`. Так же мы видим три сигнала `a`,`b`,`c`, которые описываются в блоке `always_ff`, предназначенном для описания регистров. Значение `in` по цепочке этих регистров передается до регистра `c`, выход которого подключен к выходу `out`.
Давайте "прочитаем" эту схему. Мы видим модуль, с входом `in`, выходом `out` и тактирующим синхроимпульсом `clk`. Также мы видим три сигнала `a`,`b`,`c`, которые описываются в блоке `always_ff`, предназначенном для описания регистров. Значение `in` по цепочке этих регистров передается до регистра `c`, выход которого подключен к выходу `out`.
Похоже, что здесь был описан [**сдвиговый регистр**](https://ru.wikipedia.org/wiki/Регистр_(цифровая_техника)#Сдвигающие_(последовательные)_регистры), представленный на _рис. 3_.
Похоже, что здесь был описан [**сдвиговый регистр**](https://ru.wikipedia.org/wiki/Регистр_(цифровая_техника)#Сдвигающие_(последовательные)_регистры), представленный на _рис. 4_.
![../.pic/Basic%20Verilog%20structures/assignments/fig_03.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_03.drawio.svg)
![../.pic/Basic%20Verilog%20structures/assignments/fig_04.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_04.drawio.svg)
_Рисунок 3. Трехразрядный сдвиговый регистр._
_Рисунок 4. Трехразрядный сдвиговый регистр._
Давайте откроем цифровую схему, сгенерированную Vivado и убедимся в наших выводах.
![../.pic/Basic%20Verilog%20structures/assignments/fig_04.png](../.pic/Basic%20Verilog%20structures/assignments/fig_04.png)
![../.pic/Basic%20Verilog%20structures/assignments/fig_05.png](../.pic/Basic%20Verilog%20structures/assignments/fig_05.png)
_Рисунок 4. Схема, сгенерированная Vivado по описанию из Листинга 1._
_Рисунок 5. Схема, сгенерированная Vivado по описанию из Листинга 2._
Произошло что-то странное. Вместо трех регистров Vivado создал только один и судя по названию — это последний регистр `c`. Почему это произошло?
Изучим внимательней поведение цепочки блокирующих присваиваний, представленное на _рис. 1_.
Каждое последующее присваивание ожидало, пока не выполнится предыдущее, таким образом, `RHS` первого присваивания (`5`) сразу же (без каких-либо задержек) распространился по всем регистрам. Моделируя _Листинг 1_ мы получим **поведение**, когда на вход каждого регистра будет подаваться сигнал `in`.
Каждое последующее присваивание ожидало, пока не выполнится предыдущее, таким образом, `RHS` первого присваивания (`5`) сразу же распространился по всем регистрам. Моделируя _Листинг 2_ мы получим **поведение**, когда на вход каждого регистра будет подаваться сигнал `in`.
Таким образом на самом деле, мы должны были изобразить нашу схему как на _рис. 5_.
Таким образом на самом деле, мы должны были изобразить нашу схему как на _рис. 6_.
![../.pic/Basic%20Verilog%20structures/assignments/fig_05.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_05.drawio.svg)
![../.pic/Basic%20Verilog%20structures/assignments/fig_06.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_06.drawio.svg)
_Рисунок 5. Схема, описанная Листингом 1._
_Рисунок 6. Схема, описанная Листингом 2._
Но почему тогда на схеме Vivado не осталось регистров `a` и `b`? Посмотрим на них внимательней. Их выходы ни на что не влияют, они висят неподключенные. А значит эти регистры не имеют никакого смысла и если их убрать, ничего не изменится.
@@ -149,11 +172,11 @@ Vivado обнаружил, что регистры `a` и `b` ни на что
Если вы используете Vivado 2023.1 и новее, вы можете обнаруживать подобные предупреждения более удобным способом — посредством линтера, который можно вызвать из вкладки `RTL ANALYSIS` окна `Flow Navigator`.
![../.pic/Basic%20Verilog%20structures/assignments/fig_06.png](../.pic/Basic%20Verilog%20structures/assignments/fig_06.png)
![../.pic/Basic%20Verilog%20structures/assignments/fig_07.png](../.pic/Basic%20Verilog%20structures/assignments/fig_07.png)
_Рисунок 6. Пример вызова линтера._
_Рисунок 7. Пример вызова линтера._
Давайте заменим в _Листинге 1_ блокирующие присваивания на неблокирующие. Напоминаем, что оператор неблокирующего присваивания записывается как `<=`.
Давайте заменим в _Листинге 2_ блокирующие присваивания на неблокирующие. Напоминаем, что оператор неблокирующего присваивания записывается как `<=`.
```SystemVerilog
module example_2(
@@ -175,21 +198,21 @@ assign out = c;
endmodule
```
_Листинг 2. Пример описания модуля, использующего цепочку неблокирующих присваиваний._
_Листинг 3. Пример описания модуля, использующего цепочку неблокирующих присваиваний._
Посмотрим, какую схему сгенерирует Vivado в этот раз.
![../.pic/Basic%20Verilog%20structures/assignments/fig_07.png](../.pic/Basic%20Verilog%20structures/assignments/fig_07.png)
![../.pic/Basic%20Verilog%20structures/assignments/fig_08.png](../.pic/Basic%20Verilog%20structures/assignments/fig_08.png)
_Рисунок 7. Схема, сгенерированная Vivado по описанию из Листинга 2._
_Рисунок 8. Схема, сгенерированная Vivado по описанию из Листинга 3._
Вряд ли полученный результат стал для вас неожиданным сюжетным поворотом, но давайте разберемся, почему в этот раз сгенерировалась схема, аналогичная представленной на _рис. 3_.
Вряд ли полученный результат стал для вас неожиданным сюжетным поворотом, но давайте разберемся, почему в этот раз сгенерировалась схема, аналогичная представленной на _рис. 4_.
Для этого обратимся к примеру, представленному на _рис. 2_. В данном примере **неблокирующем присваивании** сперва вычислялись значения всех `RHS` (запоминались значения выходов всех регистров) и только потом происходило присваивание новых значений. Подобное **поведение** аналогично поведению сдвиговых регистров.
Для этого обратимся к примеру, представленному на _рис. 2_. В данном примере **неблокирующего присваивания** сперва вычислялись значения всех `RHS` (запоминались значения выходов всех регистров) и только потом происходило присваивание новых значений. Подобное **поведение** аналогично поведению сдвиговых регистров.
Слово "_поведение_" было выделено дважды неспроста. Описание схем, которое мы сделали называется "**поведенческим описанием схемы**".
Можно ли реализовать сдвиговый регистр, используя блокирующие присваивания? Конечно. Например, можно поменять порядок присваиваний как в _Листинге 3_.
Можно ли реализовать сдвиговый регистр, используя блокирующие присваивания? Конечно. Например, можно поменять порядок присваиваний как в _Листинге 4_.
```SystemVerilog
module example_3(
@@ -211,9 +234,9 @@ assign out = c;
endmodule
```
_Листинг 3. Цепочка блокирующих присваиваний в порядке обратном приведенному в Листинге 1._
_Листинг 4. Цепочка блокирующих присваиваний в порядке обратном приведенному в Листинге 2._
В этом случае, линтер не сообщит ни о каких ошибках, а Vivado сгенерирует схему, аналогичную _рис. 7_
В этом случае, линтер не сообщит ни о каких ошибках, а Vivado сгенерирует схему, аналогичную _рис. 8_
Так произошло, поскольку мы разорвали зависимость значений `RHS` последующих присваиваний от значений `LHS` предыдущих (поведение, которое демонстрировала цепочка неблокирующих присваиваний с самого начала). Однако данное решение является скорее хаком, чем примером хорошего проектирования. По сути, мы просто подстроили код, описанный с помощью блокирующих присваиваний таким образом, чтоб он вел себя как код, использующий неблокирующие присваивания.
@@ -247,29 +270,29 @@ assign out = c;
endmodule
```
_Листинг 4. Сдвиговый регистр, описанный через блокирующие присваивания в отдельных блоках always._
_Листинг 5. Сдвиговый регистр, описанный через блокирующие присваивания в отдельных блоках 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_, результат на временной диаграмме совпадет с поведением сдвигового регистра.
Сгенерированная в Vivado схема будет аналогична _рис. 8_. Но давайте попробуем промоделировать работу этой схемы, подавая случайные воздействия на вход `in`.
![../.pic/Basic%20Verilog%20structures/assignments/fig_09.png](../.pic/Basic%20Verilog%20structures/assignments/fig_09.png)
_Рисунок 9. Моделирование поведения сдвигового регистра._
_Рисунок 9. Симуляция модуля, описанного Листингом 5._
Однако, как уже объяснялось ранее, вы не можете рассчитывать на такой результат. Сегодня симулятор смоделировал поведение одним образом — завтра он смоделирует этот же код, в котором не изменилась ни одна строка, по-другому, и будет по прежнему работать в соответствии со стандартом.
Выглядит как-то не по "сдвигово-регистерски". В чем же дело?
Как уже упоминалось ранее, программные блоки (коими являются блоки `always`) исполняются во время моделирования независимо друг от друга в недетерминированном стандартом порядке. На практике это означает то, что сперва может исполниться второй блок, потом третий, а потом первый — либо в любом другом порядке. Разработчик не может (и не должен) рассчитывать на порядок блоков `always` при описании схемы.
Конкретно в данной ситуации, симулятор воспроизвел блоки ровно в том порядке, в котором они были описаны. Сперва `a` получил значение `in`, потом `b` получил обновленное значение `a`, затем `c` получил обновленное значение `b`.
Поскольку поведение недетерминировано, нельзя однозначно сказать, какую схему должен был воспроизвести синтезатор. В данной ситуации, он воспроизвел схему которую мы хотели получить, но моделирование того же самого модуля демонстрирует поведение вовсе не этой схемы.
Если заменить порядок `always` подобно тому, как мы изменили порядок в _Листинге 4_, результат на временной диаграмме совпадет с поведением сдвигового регистра.
![../.pic/Basic%20Verilog%20structures/assignments/fig_10.png](../.pic/Basic%20Verilog%20structures/assignments/fig_10.png)
_Рисунок 10. Моделирование поведения сдвигового регистра._
Однако, как уже объяснялось ранее, вы не можете рассчитывать на такой результат. Сегодня симулятор смоделировал поведение одним образом — завтра он смоделирует этот же код (в котором не изменилась ни одна строка) по-другому, и будет по прежнему работать в соответствии со стандартом.
Для того, чтобы получить детерминированный результат, вам необходимо снова воспользоваться неблокирующим присваиванием, поскольку и в этом случае порядок исполнения блоков `always` не влияет на результат присваиваний — сначала вычисляются значения `RHS` всех неблокирующих присваиваний всех программных блоков, и только потом происходит присваивание этих значений `LHS`.
@@ -292,17 +315,17 @@ end
endmodule
```
_Листинг 5. Пример цепочки блокирующих присваиваний с комбинационной логикой._
_Листинг 6. Пример цепочки блокирующих присваиваний с комбинационной логикой._
Остановитесь на минуту и подумайте, схему с каким поведением описывает _Листинг 5_?
Остановитесь на минуту и подумайте, схему с каким поведением описывает _Листинг 6_?
---
Как вы могли догадаться, данный листинг описывает схему с одним регистром `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)
![../.pic/Basic%20Verilog%20structures/assignments/fig_11.png](../.pic/Basic%20Verilog%20structures/assignments/fig_11.png)
_Рисунок 10. Схема, сгенерированная Vivado по описанию из Листинга 5._
_Рисунок 11. Схема, сгенерированная Vivado по описанию из Листинга 6._
Попробуйте догадаться о том, что произойдет, если снова заменить блокирующие присваивания на неблокирующие?
@@ -310,9 +333,9 @@ _Рисунок 10. Схема, сгенерированная Vivado по оп
Результат изменится следующим образом.
![../.pic/Basic%20Verilog%20structures/assignments/fig_11.png](../.pic/Basic%20Verilog%20structures/assignments/fig_11.png)
![../.pic/Basic%20Verilog%20structures/assignments/fig_12.png](../.pic/Basic%20Verilog%20structures/assignments/fig_12.png)
_Рисунок 11. Схема, сгенерированная Vivado по описанию из Листинга 5 после замены блокирующих присваиваний на неблокирующие._
_Рисунок 12. Схема, сгенерированная Vivado по описанию из Листинга 6 после замены блокирующих присваиваний на неблокирующие._
Из прочтенного может сложиться впечатление, будто бы автор хочет показать, что блокирующее присваивание — это плохо, а неблокирующее — хорошо, однако это не так. Это просто два похожих инструмента, работающих разными способами, о которых должен знать профессионал, использующий эти инструменты.
@@ -320,7 +343,7 @@ _Рисунок 11. Схема, сгенерированная Vivado по оп
Рассмотрим предыдущий пример еще раз. Нельзя сказать, что одна схема лучше другой — это просто две разные схемы и то, какая из них вам нужна зависит только от вашей задачи.
Однако нельзя не заметить, что при использовании блокирующего присваивания, мы "теряли" регистры.
Однако нельзя не заметить, что при использовании блокирующего присваивания, мы "теряли" регистры. Более того, моделирование **неблокирующих** присваиваний ближе всего по поведению приближено к моделированию регистровой логики [[1, стр. 14]](http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf).
Пока что мы рассматривали только синхронные схемы (схемы, работающие по тактовому синхроимпульсу).
@@ -342,19 +365,19 @@ end
endmodule
```
_Листинг 6. Пример цепочки блокирующих присваиваний в комбинационной схеме._
_Листинг 7. Пример цепочки блокирующих присваиваний в комбинационной схеме._
> Обратите внимание на то, что `always_ff` поменялся на `always_comb`.
Как вы думаете, какая схема будет сгенерирована по описанию, представленному _Листинга 6_, и что произойдет с этой схемой, если заменить в нем все блокирующие присваивания на неблокирующие?
Как вы думаете, какая схема будет сгенерирована по описанию, представленному _Листинга 7_, и что произойдет с этой схемой, если заменить в нем все блокирующие присваивания на неблокирующие?
---
Вас может это удивить, но в обоих случаях будет сгенерирована схема, представленная на _рис. 12_.
Вас может это удивить, но в обоих случаях будет сгенерирована схема, представленная на _рис. 13_.
![../.pic/Basic%20Verilog%20structures/assignments/fig_12.png](../.pic/Basic%20Verilog%20structures/assignments/fig_12.png)
![../.pic/Basic%20Verilog%20structures/assignments/fig_13.png](../.pic/Basic%20Verilog%20structures/assignments/fig_13.png)
_Рисунок 12. Схема, сгенерированная Vivado по описанию из Листинга 6._
_Рисунок 13. Схема, сгенерированная Vivado по описанию из Листинга 7._
> Девочка остолбенела. У неё возникло отчётливое чувство какой-то ужасной несправедливости по отношению к ней. Гарри Поттер был грязным, отвратительным обманщиком и лжецом. Но во время игры все его ответы были верными. [Элиезер Юдковский / Гарри Поттер и методы рационального мышления]
@@ -366,15 +389,15 @@ _Рисунок 12. Схема, сгенерированная Vivado по оп
Теперь, когда мы стали использовать блок `always_comb`, правила игры изменились. Нет, принцип работы блокирующих и неблокирующих присваиваний остался тем же самым. Изменилось только то, сколько раз будет вызван данный блок.
Начнем со схемы, построенной по блокирующему присваиванию. В общем-то, тут у вас не должно было возникнуть вопросов, логика ровно та же, что была и при построении схемы по _Листингу 5_ (_рис. 10)_, только без выходного регистра. Что логично, ведь мы убрали тактирующий сигнал.
Начнем со схемы, построенной по описанию, использующему блокирующее присваивание. В общем-то, тут у вас не должно было возникнуть вопросов, логика ровно та же, что была и при построении схемы по _Листингу 6_ (_рис. 11)_, только без выходного регистра. Что логично, ведь мы убрали из описания тактирующий сигнал.
Вопрос в том, почему это вдруг схема, построенная после замены блокирующих присваиваний на неблокирующие ведет себя точно так же?
Рассмотрим _рис. 13_.
Рассмотрим _рис. 14_.
![../.pic/Basic%20Verilog%20structures/assignments/fig_13.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_13.drawio.svg)
![../.pic/Basic%20Verilog%20structures/assignments/fig_14.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_14.drawio.svg)
_Рисунок 13. Моделирование цепочки присваиваний в комбинационном блоке `always`._
_Рисунок 14. Моделирование цепочки присваиваний в комбинационном блоке `always`._
Комбинационный блок `always` начинает исполняться **каждый раз**, когда операнд любого `RHS` этого блока меняет своё значение.
@@ -394,35 +417,23 @@ _Рисунок 13. Моделирование цепочки присваива
> Получается что для комбинационной логики нет разницы между блокирующим и неблокирующим присваиванием, после переходных процессов результат будет одинаковым?
И да и нет. С точки зрения синтеза схемы так и есть. Однако есть нюанс в случае моделирования схемы.
Особенность планировщика событий симуляции такова, что все `RHS` неблокирующих присваиваний будут вычислены после выполнения всех блокирующих присваиваний.
Чем это может быть удобно?
Давайте сделаем небольшое лирическое отступление и разберем часто используемую в мире разработки цифровых схем аббревиатуру `RTL`. Она расшифровывается как `Register Transfer Level` — уровень межрегистровых передач. Цифровую схему можно рассматривать на различных уровнях абстракции. Можно рассматривать с физического уровня: как набор проводников, транзисторов, а так же электронов и "дырок", бегающих по ним. Можно рассматривать схему на уровне логических вентилей (gate-level). Одним из таких уровней абстракции является уровень межрегистровых передач, когда мы делим схему на две части: регистры, и комбинационную логику их связующую.
По каждому такту синхроимпульса, в регистры будет записываться новое значение, пришедшее от комбинационной логики, что приведет к изменению на выходе регистра и начнет цепочку изменений в комбинационной логике, соединяющей регистры. К следующему такту на концах комбинационной логики, являющихся входами регистров должны оказаться установившееся значения, которые будут записаны в регистры и все повторится снова.
Было бы удобно разделить эти две части: обновление значений в регистрах и обновление значений в комбинационной логике таким образом, чтобы сперва обновлялась комбинационная логика, а потом уже обновлялось значение в регистрах.
Именно этого можно добиться, если комбинационная логика будет описана посредством блокирующих присваиваний, которые будут выполнены до неблокирующих присваиваний, используемых при описании регистров.
И да и нет. С точки зрения синтеза схемы так и есть. Однако есть нюанс в случае моделирования схемы. Поведение комбинационной логики лучше моделирует блокирующее присваивание[[1, стр. 14]](http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf).
Подведем итоги прочитанному:
- Блокирующее присваивание блокирует выполнение остальных операций до завершения текущего присваивания. Оно подобно обычному присваиванию в парадигме программирования.
- Неблокирующее присваивание сперва вычисляет `RHS`, давая исполниться остальным операциям до самого присваивания. Причем в случае, если после неблокирующего присваивания выполняется блокирующее, от `LHS` которого зависело `RHS` неблокирующего присваивания — значение `RHS` будет обновлено (в упрощенном виде с некоторыми оговорками это предложение можно читать как: _"неблокирующие присваивания исполняются после выполнения всех блокирующих"_).
- Неблокирующее присваивание сперва вычисляет `RHS`, давая исполниться остальным операциям до самого присваивания.
В связи с особенностями поведения блокирующего и неблокирующего присваивания, выведены следующие две максимы:
- при описании последовательностной логики (регистров) используйте **неблокирующее** присваивание;
- при описании комбинационной логики используйте **блокирующее** присваивание.
Кроме того, существуют следующие рекомендации и требования[1, стр. 5]:
Кроме того, существуют следующие рекомендации и требования[[1, стр. 5]](http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf):
-ри описании как последовательностной логики, так и комбинационной в одном блоке `always` используйте неблокирующее присваивание._
- _Не смешивайте в одном блоке блокирующие и неблокирующие присваивания_ — стандарт допускает подобное описание, но оно затрудняет его чтение.
- _Не смешивайте блокирующие и неблокирующие присваивания для одного и того же сигнала_ — стандарт это запрещает.
- _Не смешивайте в одном блоке блокирующие и неблокирующие присваивания_ — стандарт допускает подобное описание, но оно затрудняет его чтение. Представьте, что читая описание схемы, вам бы постоянно приходилось держать в голове какие присваивания уже произошли, а какие только произойдут, чтобы понять как эта схема работает.
- _Не смешивайте блокирующие и неблокирующие присваивания для одного и того же сигнала_ — стандарт это запрещает (для блоков `always_ff`, `always_comb`, `always_latch`).
Использованная литература: