Исправление опечаток в Assignments

This commit is contained in:
Andrei Solodovnikov
2024-02-03 00:28:34 +03:00
parent 34274ccb40
commit 80502c5bc8
2 changed files with 38 additions and 34 deletions

View File

@@ -1,9 +1,9 @@
# Различие блокирующего и неблокирующего присваивания
Вскоре после начала курса студенты сталкиваются с понятиями "блокирующего" и "неблокирующего" присваивания. Часто объяснения преподавателей по этой теме сопровождаются словами "последовательный" и "параллельный", а также предлагается _просто запомнить_[1, стр. 2](http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf):
Вскоре после начала курса студенты сталкиваются с понятиями "блокирующего" и "неблокирующего" присваивания. Часто объяснения преподавателей по этой теме сопровождаются словами "последовательный" и "параллельный", а также предлагается _просто запомнить_ [[1, стр. 2]](http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf):
- При описании последовательностной логики (регистров) используйте **неблокирующее** присваивание.
- При описании комбинационной логики используйте **блокирующее** присваивание.
- при описании последовательностной логики (регистров) используйте **неблокирующее** присваивание;
- при описании комбинационной логики используйте **блокирующее** присваивание.
Давайте разберемся что это за присваивания и почему необходимо руководствоваться этими правилами.
@@ -13,7 +13,7 @@
Для начала введем пару сокращений для удобства дальнейшего повествования:
- под `LHS (left hand side)` мы будем подразумевать "выражение, **которому присваивают**";
- под `LHS` (left hand side) мы будем подразумевать "выражение, **которому присваивают**";
- под `RHS` (right hand side) мы будем подразумевать "выражение **которое присваивают**".
В выражении `a = b+c`, `a` является `LHS`, `b+c` является `RHS`.
@@ -22,7 +22,9 @@
С непрерывным присваиванием вы знакомитесь в самом начале — это оператор `assign`. Непрерывное присваивание постоянно следит за `RHS` этого оператора, и каждый раз, когда любая часть этого выражения меняет своё значение, производит пересчёт значения `RHS`, а затем сразу же передает это значение `LHS`. Если мы произведем `assign a = b+c`, то каждый раз, когда будет меняться значение `b` или `c`, будет пересчитываться результат их суммы, который сразу же будет присвоен выражению `a`.
**Непрерывное присваивание может быть использовано только вне программных блоков**. Под "программными блоками" подразумеваются блоки `always` (всех типов) и `initial`. Есть и другие программные блоки, но в рамках данного курса лабораторных работ вы с ними не столкнетесь. Вообще говоря, синтаксис языка SystemVerilog допускает использование оператора `assign` внутри программного блока, однако в рамках данного курса не существует ни одной ситуации, когда это может потребоваться и со 100% вероятностью будет ошибочно.
**Непрерывное присваивание может быть использовано только вне программных блоков.**
Под "программными блоками" подразумеваются блоки `always` (всех типов) и `initial`. Есть и другие программные блоки, но в рамках данного курса лабораторных работ вы с ними не столкнетесь. Вообще говоря, синтаксис языка SystemVerilog допускает использование оператора `assign` внутри программного блока, однако в рамках данного курса не существует ни одной ситуации, когда это может потребоваться и со 100% вероятностью будет ошибочно.
В отличие от непрерывного присваивания, **процедурное присваивание может быть использовано только в программных блоках**.
@@ -32,19 +34,19 @@
- `always @(posedge clk)` будет исполняться каждый раз когда произойдет положительный фронт `clk`;
- `always @(a,b,c)` будет исполняться каждый раз, когда изменится значение любого из сигналов `a`,`b`,`c`;
- `always @(*)` будет исполняться каждый раз, когда изменится состояние любой составляющей любого RHS в этом блоке (когда изменится хоть что-то, от чего зависит любое выражение слева от оператора присваивания в этом блоке).
- `always @(*)` будет исполняться каждый раз, когда изменится состояние любой составляющей любого `RHS` в этом блоке (когда изменится хоть что-то, от чего зависит любое выражение слева от оператора присваивания в этом блоке).
Похожие правила применимы и к остальным блокам `always`: `always_comb`, `always_ff`, `always_latch` (с некоторыми оговорками, не имеющими значения в рамках данного повествования).
Под независимостью исполнения подразумевается то, что порядок исполнения блоков не зависит от очередности, в которой они были описаны. Более того, исполнение одного блока может быть приостановлено, чтобы исполнить другой блок.
А вот выражения внутри отдельного программного блока (с точке зрения моделирования) исполняются последовательно. И вот тут на сцену выходят два типа программного присваивания: блокирующее и неблокирующее.
А вот выражения внутри отдельного программного блока (с точке зрения моделирования) исполняются последовательно. И вот тут на сцену выходят два типа процедурного присваивания: блокирующее и неблокирующее.
Блокирующее присваивание (оператор `=`) **блокирует** исполнение дальнейших выражений до завершения вычисления `RHS` и присвоения вычисленного результата `LHS` (иными словами, это привычное вам присваивание из мира программирования, там все работает точно так же).
Неблокирующее присваивание (оператор `<=`) производит вычисление `RHS`, запоминает его, и **откладывает** присваивание вычисленного значения, позволяя выполняться остальным выражениям до завершения присваивания `LHS`.
Пример, представленный на _рис. 1_.
Рассмотрим пример, представленный на _рис. 1_.
![../.pic/Basic%20Verilog%20structures/assignments/fig_01.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_01.drawio.svg)
@@ -66,21 +68,21 @@ _Рисунок 2. Пример цепочки неблокирующих при
1. Сперва вычисляется значение `RHS` первого присваивания (`5`). Присваивание этого значения **откладывается** на потом.
2. Затем вычисляется значение `RHS` второго присваивания. Поскольку `a` еще не присвоили значение `5`, результатом `RHS` становится текущее значение `a` — 3. Присваивание этого значения сигналу `b` **откладывается** на потом.
3. Аналогичным образом вычисляется `RHS` третьего присваивания (`2`). Присваивание этого значения **откладывается** на потом.
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]. Выполнение отложенных присваиваний происходит в том же порядке, в котором они шли в программном блоке. Подробнее о том как работает событийная симуляция (event based simulation) в SystemVerilog вы можете прочесть в стандарте [IEEE 1800-2017](https://ieeexplore.ieee.org/document/8299595) (раздел 4). Стандарт доступен бесплатно всем желающим по программе "IEEE GET Program".
Таким образом, если `LHS` блокирующего присваивания используется в качестве операнда `RHS` любого другого последующего присваивания, результат предыдущего выражение будет передан дальше **в тот же момент времени**, что очень похоже на "_последовательное вычисление_".
Таким образом, если `LHS` блокирующего присваивания используется в качестве операнда `RHS` любого другого последующего присваивания, результат предыдущего выражения будет передан дальше **в тот же момент времени**, что очень похоже на "_последовательное вычисление_".
С другой стороны `LHS` неблокирующего присваивания не может использоваться в качестве операнда `RHS` последующих присваиваний, что создает иллюзию "_параллельного вычисления_".
Теперь, понимая как работают присваивания с точки зрения моделирования, посмотрим на то, во что могут синтезироваться подобные операторы.
Начнем с непрерывного присваивания. Оно превращается в провод, передающий данные от `RHS` к `LHS`. При вы должны контролировать что к чему вы присваиваете (не путайте местами `RHS` и `LHS`).
Начнем с непрерывного присваивания. Оно превращается в провод, передающий данные от `RHS` к `LHS`. При этом вы должны контролировать что к чему вы присваиваете (не путайте местами `RHS` и `LHS`).
То во что синтезируются блокирующие и неблокирующие присваивания зависят от описываемой логики, поэтому давайте разберем несколько примеров.
То во что синтезируются блокирующие и неблокирующие присваивания зависит от описываемой логики, поэтому давайте разберем несколько примеров.
Рассмотрим исходный примера c цепочкой блокирующих присваиваний, только теперь перепишем его в синтезируемом виде, сохранив изначальную идею.
Начнем с исходного примера c цепочкой блокирующих присваиваний, только теперь перепишем его в синтезируемом виде, сохранив изначальную идею.
```SystemVerilog
module example_1(
@@ -104,7 +106,7 @@ endmodule
_Листинг 1. Пример описания модуля, использующего цепочку блокирующих присваиваний._
Если вы уже знакомы с содержимым документа о том, [как описывать регистры](Registers.md), подумайте, какой будет результат синтеза у этой схемы?
Если вы уже знакомы с содержимым документа о том, [как описывать регистры](Registers.md), подумайте: какой будет результат синтеза у этой схемы?
---
@@ -128,7 +130,7 @@ _Рисунок 4. Схема, сгенерированная Vivado по опи
Каждое последующее присваивание ожидало, пока не выполнится предыдущее, таким образом, `RHS` первого присваивания (`5`) сразу же (без каких-либо задержек) распространился по всем регистрам. Моделируя _Листинг 1_ мы получим **поведение**, когда на вход каждого регистра будет подаваться сигнал `in`.
Таким образом на самом деле, мы должны были изображать нашу схему как на _рис. 5_.
Таким образом на самом деле, мы должны были изобразить нашу схему как на _рис. 5_.
![../.pic/Basic%20Verilog%20structures/assignments/fig_05.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_05.drawio.svg)
@@ -139,11 +141,13 @@ _Рисунок 5. Схема, описанная Листингом 1._
При генерации схемы, 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]
WARNING: [Synth 8-6014] Unused sequential element a_reg was removed. [example_1.sv:10]
WARNING: [Synth 8-6014] Unused sequential element b_reg was removed. [example_1.sv:11]
```
Если вы используете Vivado 2023.1 и новее, вы можете обнаруживать подобные ошибки более удобным способом — посредством линтера, который можно вызвать из вкладки `RTL ANALYSIS` окна `Flow Navigator`.
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)
@@ -213,7 +217,7 @@ _Листинг 3. Цепочка блокирующих присваивани
Так произошло, поскольку мы разорвали зависимость значений `RHS` последующих присваиваний от значений `LHS` предыдущих (поведение, которое демонстрировала цепочка неблокирующих присваиваний с самого начала). Однако данное решение является скорее хаком, чем примером хорошего проектирования. По сути, мы просто подстроили код, описанный с помощью блокирующих присваиваний таким образом, чтоб он вел себя как код, использующий неблокирующие присваивания.
Важно отметить, что при использовании неблокирующих присваиваний, их порядок вообще не имеет значения.
Важно отметить, что при использовании неблокирующих присваиваний, их порядок вообще не имеет значения с точки зрения синтеза.
Давайте разнесем логику работы каждого регистра по отдельным блокам `always`.
@@ -251,13 +255,13 @@ _Листинг 4. Сдвиговый регистр, описанный чер
_Рисунок 8. Симуляция модуля, описанного Листингом 4._
Выглядит как-то не по "сдвигово-регистрерски". В чем же дело?
Выглядит как-то не по "сдвигово-регистерски". В чем же дело?
Как уже упоминалось ранее, программные блоки (коими являются блоки `always`) исполняются во время моделирования независимо друг от друга в недетерминированном стандартом порядке. На практике это означает то, что сперва может исполниться второй блок, потом третий, а потом первый — либо в любом другом порядке. Так сделано для того, чтобы разработчик не мог рассчитывать на порядок блоков `always` при описании.
Как уже упоминалось ранее, программные блоки (коими являются блоки `always`) исполняются во время моделирования независимо друг от друга в недетерминированном стандартом порядке. На практике это означает то, что сперва может исполниться второй блок, потом третий, а потом первый — либо в любом другом порядке. Разработчик не может рассчитывать на порядок блоков `always` при описании схемы.
Конкретно в данной ситуации, симулятор воспроизвел блоки ровно в том порядке, в котором они были описаны. Сперва `a` получил значение `in`, потом `b` получил обновленное значение `a`, затем `c` получил обновленное значение `b`.
Поскольку поведение недетерминировано, нельзя однозначно сказать, какую схему должен был воспроизвести синтезатор. В данной ситуации, он воспроизвел схему которую мы хотели получить, что не помогло, тем не менее с моделированием.
Поскольку поведение недетерминировано, нельзя однозначно сказать, какую схему должен был воспроизвести синтезатор. В данной ситуации, он воспроизвел схему которую мы хотели получить, что, тем не менее, не помогло с моделированием.
Если заменить порядок `always` подобно тому, как мы изменили порядок в _Листинге 3_, результат на временной диаграмме совпадет с поведением сдвигового регистра.
@@ -265,7 +269,7 @@ _Рисунок 8. Симуляция модуля, описанного Лис
_Рисунок 9. Моделирование поведения сдвигового регистра._
Однако, как уже объяснялось ранее, вы не можете рассчитывать на такой результат. Сегодня симулятор смоделировал поведение одним образом — завтра он смоделирует этот же код, в котором не изменилась ни одна строка, и будет по прежнему работать в соответствии со стандартом.
Однако, как уже объяснялось ранее, вы не можете рассчитывать на такой результат. Сегодня симулятор смоделировал поведение одним образом — завтра он смоделирует этот же код, в котором не изменилась ни одна строка, по-другому, и будет по прежнему работать в соответствии со стандартом.
Для того, чтобы получить детерминированный результат, вам необходимо снова воспользоваться неблокирующим присваиванием, поскольку и в этом случае порядок исполнения блоков `always` не влияет на результат присваиваний — сначала вычисляются значения `RHS` всех неблокирующих присваиваний всех программных блоков, и только потом происходит присваивание этих значений `LHS`.
@@ -312,7 +316,7 @@ _Рисунок 11. Схема, сгенерированная Vivado по оп
Из прочтенного может сложиться впечатление, будто бы автор хочет показать, что блокирующее присваивание — это плохо, а неблокирующее — хорошо, однако это не так. Это просто два похожих инструмента, работающих разными способами, о которых должен знать профессионал, использующий эти инструменты.
Одно и тоже описание, использующее разные типы присваиваний приводит к синтезу разных схем.
Одно и тоже описание, использующее разные типы присваиваний может привести к синтезу разных схем.
Рассмотрим предыдущий пример еще раз. Нельзя сказать, что одна схема лучше другой — это просто две разные схемы и то, какая из них вам нужна зависит только от вашей задачи.
@@ -320,7 +324,7 @@ _Рисунок 11. Схема, сгенерированная Vivado по оп
Пока что мы рассматривали только синхронные схемы (схемы, работающие по тактовому синхроимпульсу).
Рассмотрим зависимость от типа присваивания в комбинационных схемах. Для этого возьмем предыдущий пример, у уберем тактирующий синхроимпульс.
Рассмотрим зависимость от типа присваивания в комбинационных схемах. Для этого возьмем предыдущий пример, и уберем тактирующий синхроимпульс.
```SystemVerilog
module example_6(
@@ -340,7 +344,7 @@ endmodule
_Листинг 6. Пример цепочки блокирующих присваиваний в комбинационной схеме._
> Обратите внимание на то, что изменился и блок always.
> Обратите внимание на то, что `always_ff` поменялся на `always_comb`.
Как вы думаете, какая схема будет сгенерирована по описанию, представленному _Листинга 6_, и что произойдет с этой схемой, если заменить в нем все блокирующие присваивания на неблокирующие?
@@ -378,7 +382,7 @@ _Рисунок 13. Моделирование цепочки присваива
После происходит присваивание вычисленных значений сигналам `temp` и `d`.
Однако `temp` является операндом `RHS` в выражении `d = c & temp`, поэтому блок `always`. Запускается еще один раз в этот же момент времени (в примере это `5ns`). Поскольку значения `a` и `b` не менялись, значение `RHS` первого выражения останется прежним. А вот значение `temp` уже иное, поэтому `RHS` второго выражения станет `1`.
Однако `temp` является операндом `RHS` в выражении `d = c & temp`, поэтому блок `always` запускается еще один раз в этот же момент времени (в примере это `5ns`). Поскольку значения `a` и `b` не менялись, значение `RHS` первого выражения останется прежним. А вот значение `temp` уже иное, поэтому `RHS` второго выражения станет `1`.
После повторного присваивания сигналы `temp` и `d` примут установившееся значение. Поскольку ни один из операндов `RHS`—выражений больше не изменил своего значения, блок `always` больше не вызывается.
@@ -396,9 +400,9 @@ _Рисунок 13. Моделирование цепочки присваива
Чем это может быть удобно?
Давайте сделаем небольшое лирическое отступление и разберем часто используемую в мире разработки цифровых схем аббревиатуру `RTL`. Она расшифровывается как `Register Transfer Level` — уровень межрегистровых передач. Цифровую схему можно рассматривать на различных уровнях абстракции. Можно рассматривать с физического уровня: как набор проводников, транзисторов, а так же электронов и "дырок", бегающих по ним. Можно рассматривать схему на уровне логических вентилей (gate-level). Одним из таких уровней абстракции является уровень межрегистровых передач, когда мы делим схему на две части: регистры, и комбинационную логика их связывающая.
Давайте сделаем небольшое лирическое отступление и разберем часто используемую в мире разработки цифровых схем аббревиатуру `RTL`. Она расшифровывается как `Register Transfer Level` — уровень межрегистровых передач. Цифровую схему можно рассматривать на различных уровнях абстракции. Можно рассматривать с физического уровня: как набор проводников, транзисторов, а так же электронов и "дырок", бегающих по ним. Можно рассматривать схему на уровне логических вентилей (gate-level). Одним из таких уровней абстракции является уровень межрегистровых передач, когда мы делим схему на две части: регистры, и комбинационную логику их связующую.
По каждому такту синхроимпульса, в регистры будет записываться новое значение, что приведет к изменению на выходе регистра и начнет цепочку изменений в комбинационной логике, соединяющей регистры. К следующему такту на концах комбинационной логике, являющихся входами регистров должны оказаться установившееся значения, которые будут записаны и все повторится снова.
По каждому такту синхроимпульса, в регистры будет записываться новое значение, пришедшее от комбинационной логики, что приведет к изменению на выходе регистра и начнет цепочку изменений в комбинационной логике, соединяющей регистры. К следующему такту на концах комбинационной логики, являющихся входами регистров должны оказаться установившееся значения, которые будут записаны в регистры и все повторится снова.
Было бы удобно разделить эти две части: обновление значений в регистрах и обновление значений в комбинационной логике таким образом, чтобы сперва обновлялась комбинационная логика, а потом уже обновлялось значение в регистрах.
@@ -407,12 +411,12 @@ _Рисунок 13. Моделирование цепочки присваива
Подведем итоги прочитанному:
- Блокирующее присваивание блокирует выполнение остальных операций до завершения текущего присваивания. Оно подобно обычному присваиванию в парадигме программирования.
- Неблокирующее присваивание сперва вычисляет `RHS`, давая исполниться остальным операциям до самого присваивания. Причем `RHS` в случае, если после неблокирующего присваивания выполняется блокирующее от `LHS` которого зависело `RHS` неблокирующего присваивания — значение `RHS` будет обновлено.
- Неблокирующее присваивание сперва вычисляет `RHS`, давая исполниться остальным операциям до самого присваивания. Причем в случае, если после неблокирующего присваивания выполняется блокирующее, от `LHS` которого зависело `RHS` неблокирующего присваивания — значение `RHS` будет обновлено (в упрощенном виде с некоторыми оговорками это предложение можно читать как: _"неблокирующие присваивания исполняются после выполнения всех блокирующих"_).
В связи с особенностями поведения блокирующего и неблокирующего присваивания, выведены следующие две максимы:
- При описании последовательностной логики (регистров) используйте **неблокирующее** присваивание.
- При описании комбинационной логики используйте **блокирующее** присваивание.
- при описании последовательностной логики (регистров) используйте **неблокирующее** присваивание;
- при описании комбинационной логики используйте **блокирующее** присваивание.
Кроме того, существуют следующие рекомендации и требования[1, стр. 5]:
@@ -422,5 +426,5 @@ _Рисунок 13. Моделирование цепочки присваива
Использованная литература:
1. [Clifford E. Cummings / Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kil](http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf)
1. [Clifford E. Cummings / Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill](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)