diff --git a/.pic/Basic Verilog structures/assignments/fig_06.png b/.pic/Basic Verilog structures/assignments/fig_06.png index af67b95..93ff309 100644 Binary files a/.pic/Basic Verilog structures/assignments/fig_06.png and b/.pic/Basic Verilog structures/assignments/fig_06.png differ diff --git a/Basic Verilog structures/Assignments.md b/Basic Verilog structures/Assignments.md index 9c6630e..dd85aef 100644 --- a/Basic Verilog structures/Assignments.md +++ b/Basic Verilog structures/Assignments.md @@ -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)