WIP: APS cumulative update (#98)

* WIP: APS cumulative update

* Update How FPGA works.md

* Перенос раздела "Последовательностная логика" в отдельный док

* Исправление картинки

* Исправление оформления индексов

* Переработка раздела Vivado Basics

* Добавление картинки в руководство по созданию проекта

* Исправление ссылок в анализе rtl

* Обновление изображения в sequential logic

* Исправление ссылок в bug hunting

* Исправление ссылок

* Рефактор руководства по прошивке ПЛИС

* Mass update

* Update fig_10

* Restore fig_02
This commit is contained in:
Andrei Solodovnikov
2024-09-02 10:20:08 +03:00
committed by GitHub
parent 78bb01ef95
commit a28002e681
195 changed files with 3640 additions and 2664 deletions

View File

@@ -7,21 +7,21 @@
Давайте разберемся что это за присваивания и почему необходимо руководствоваться этими правилами.
Начать придется издалека. Несмотря на то, что SystemVerilog является **языком описания аппаратуры**, он так же является и языком для верификации описанной аппаратуры (слово `Verilog` является объединением двух слов: `verification` и `logic`). Для целей верификации в языке выделено целое подмножество конструкций, которые не могут быть использованы для описания аппаратуры — так называемое "_несинтезируемое подмножество языка SystemVerilog_". Разумеется, часть языка, которая может быть использована для описания аппаратуры ("_синтезируемое подмножество языка SystemVerilog_") тоже может использоваться в верификации.
Начать придется издалека. Несмотря на то, что SystemVerilog является **языком описания аппаратуры**, он так же является и языком для верификации описанной аппаратуры (слово `Verilog` является объединением двух слов: `verification` и `logic` [2, стр. 24]). Для целей верификации в языке выделено целое подмножество конструкций, которые не могут быть использованы для описания аппаратуры — так называемое "_несинтезируемое подмножество языка SystemVerilog_". Разумеется, часть языка, которая может быть использована для описания аппаратуры ("_синтезируемое подмножество языка SystemVerilog_") тоже может использоваться в верификации.
Давайте для начала разберемся в том, как будут использоваться операторы присваивания при программном моделировании (так называемой симуляции) — одним из инструментов верификации. Разобравшись в поведении операторов во время симуляции будет куда проще объяснить результат использования операторов при синтезе цифровой схемы.
Давайте для начала разберемся в том, как будут использоваться операторы присваивания при программном моделировании (так называемой симуляции) — одним из инструментов верификации. Разобравшись в поведении операторов во время симуляции, будет куда проще объяснить результат использования операторов при синтезе цифровой схемы.
Введем пару сокращений для удобства дальнейшего повествования:
- под `LHS` (left hand side) мы будем подразумевать "выражение, **которому** присваивают";
- под `RHS` (right hand side) мы будем подразумевать "выражение **которое** присваивают".
- под `RHS` (right hand side) мы будем подразумевать "выражение, **которое** присваивают".
В выражении `a = b+c`, `a` является `LHS`, `b+c` является `RHS`.
два вида присваиваний: **непрерывное** и **процедурное**.
Существует два вида присваиваний: **непрерывное** и **процедурное**.
```SystemVerilog
module assignment_example(
```Verilog
module example_1(
input logic a, b
output logic c, d
);
@@ -49,7 +49,7 @@ _Листинг 1. Пример непрерывного и процедурно
С точки зрения моделирования (не описания аппаратуры), программный блок — это программа (в привычном вам понимании парадигмы программирования), исполняющаяся в отдельном процессе. Программные блоки исполняются независимо друг от друга по определенным событиям.
Блоки `initial` (их может быть много) исполняются в момент начала моделирования. Блоки `always` исполняются по событиям указанным в **списке чувствительности**:
Блоки `initial` (их может быть много) исполняются в момент начала моделирования. Блоки `always` исполняются по событиям, указанным в **списке чувствительности**:
- `always @(posedge clk)` будет исполняться каждый раз когда произойдет положительный фронт `clk`;
- `always @(a,b,c)` будет исполняться каждый раз, когда изменится значение любого из сигналов `a`,`b`,`c`;
@@ -89,11 +89,11 @@ _Рисунок 2. Пример цепочки неблокирующих при
2. Затем вычисляется значение `RHS` второго присваивания. Поскольку `a` еще не присвоили значение `5`, результатом `RHS` становится текущее значение `a` — 3. Присваивание этого значения сигналу `b` **откладывается** на потом.
3. Аналогичным образом вычисляется `RHS` третьего присваивания (`2`). Присваивание этого значения также **откладывается** на потом.
Так называемое "**потом**" наступает когда завершается вычисление `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".
Так называемое "**потом**" наступает, когда завершается вычисление `RHS` всех неблокирующих присваиваний и завершение присвоений всех блокирующих присваиваний (однако "потом" все равно происходит в тот же момент времени, обратите внимание на значение времени на _рис. 2_). В стандарте SystemVerilog этот момент называется `NBA-region` (сокр. от "Non-Blocking Assignment region") [[2, стр. 61]](https://ieeexplore.ieee.org/document/10458102). Выполнение отложенных присваиваний происходит в том же порядке, в котором они шли в программном блоке. Подробнее о том как, работает событийная симуляция (event based simulation) в SystemVerilog, вы можете прочесть в стандарте [IEEE 1800-2023](https://ieeexplore.ieee.org/document/10458102) (раздел 4). Стандарт доступен бесплатно всем желающим по программе "IEEE GET Program".
Таким образом, если `LHS` **блокирующего** присваивания используется в качестве операнда `RHS` любого другого последующего присваивания, это выражение будет иметь уже обновленное значение, что очень похоже на "_последовательное вычисление_".
С другой стороны значение, присвоенное `LHS` значение с помощью **неблокирующего** присваивания не может использоваться в качестве операнда `RHS` последующих присваиваний, что создает иллюзию "_параллельного вычисления_" (см. _рис. 3_).
С другой стороны значение, присвоенное `LHS` значение с помощью **неблокирующего** присваивания, не может использоваться в качестве операнда `RHS` последующих присваиваний, что создает иллюзию "_параллельного вычисления_" (см. _рис. 3_).
![../.pic/Basic%20Verilog%20structures/assignments/fig_03.drawio.svg](../.pic/Basic%20Verilog%20structures/assignments/fig_03.drawio.svg)
@@ -101,20 +101,20 @@ _Рисунок 3. Иллюстрация блокирующих и неблок
Теперь, понимая как работают присваивания с точки зрения моделирования, посмотрим на то, во что могут синтезироваться подобные операторы.
Начнем с непрерывного присваивания. Оно превращается в провод, передающий данные от `RHS` к `LHS`. При этом вы должны контролировать что к чему вы присваиваете (не путайте местами `RHS` и `LHS`).
Начнем с непрерывного присваивания. Оно превращается в провод, передающий данные от `RHS` к `LHS`. При этом вы должны следить за тем, **что** и **чему** вы присваиваете (не путайте местами `RHS` и `LHS`).
То во что синтезируются блокирующие и неблокирующие присваивания зависит от описываемой логики, поэтому давайте разберем несколько примеров.
То, во что синтезируются блокирующие и неблокирующие присваивания зависит от описываемой логики, поэтому давайте разберём несколько примеров.
Начнем с исходного примера c цепочкой блокирующих присваиваний, только теперь перепишем его в синтезируемом виде, сохранив изначальную идею.
```SystemVerilog
module example_1(
```Verilog
module example_2(
input logic clk,
input logic [31:0] in,
output logic [31:0] out
);
logic [31:0] a,b,c;
logic [31:0] a, b, c;
always_ff @(posedge clk) begin
a = in;
@@ -133,7 +133,7 @@ _Листинг 2. Пример описания модуля, использу
---
Давайте "прочитаем" эту схему. Мы видим модуль, с входом `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/Регистр_(цифровая_техника)#Сдвигающие_(последовательные)_регистры), представленный на _рис. 4_.
@@ -151,7 +151,7 @@ _Рисунок 5. Схема, сгенерированная Vivado по опи
Изучим внимательней поведение цепочки блокирующих присваиваний, представленное на _рис. 1_.
Каждое последующее присваивание ожидало, пока не выполнится предыдущее, таким образом, `RHS` первого присваивания (`5`) сразу же распространился по всем регистрам. Моделируя _Листинг 2_ мы получим **поведение**, когда на вход каждого регистра будет подаваться сигнал `in`.
Каждое последующее присваивание ожидало, пока не выполнится предыдущее, таким образом, `RHS` первого присваивания (`5`) сразу же распространился по всем регистрам. Моделируя _Листинг 2_, мы получим **поведение**, когда на вход каждого регистра будет подаваться сигнал `in`.
Таким образом на самом деле, мы должны были изобразить нашу схему как на _рис. 6_.
@@ -159,7 +159,7 @@ _Рисунок 5. Схема, сгенерированная Vivado по опи
_Рисунок 6. Схема, описанная Листингом 2._
Но почему тогда на схеме Vivado не осталось регистров `a` и `b`? Посмотрим на них внимательней. Их выходы ни на что не влияют, они висят неподключенные. А значит эти регистры не имеют никакого смысла и если их убрать, ничего не изменится.
Но почему тогда на схеме Vivado не осталось регистров `a` и `b`? Посмотрим на них внимательней. Их выходы ни на что не влияют, они ни к чему не подключены. А значит эти регистры не имеют никакого смысла и, если их убрать, ничего не изменится.
При генерации схемы, Vivado вывел в `Tcl Console` следующие предупреждения:
@@ -178,8 +178,8 @@ _Рисунок 7. Пример вызова линтера._
Давайте заменим в _Листинге 2_ блокирующие присваивания на неблокирующие. Напоминаем, что оператор неблокирующего присваивания записывается как `<=`.
```SystemVerilog
module example_2(
```Verilog
module example_3(
input logic clk,
input logic [31:0] in,
output logic [31:0] out
@@ -214,8 +214,8 @@ _Рисунок 8. Схема, сгенерированная Vivado по опи
Можно ли реализовать сдвиговый регистр, используя блокирующие присваивания? Конечно. Например, можно поменять порядок присваиваний как в _Листинге 4_.
```SystemVerilog
module example_3(
```Verilog
module example_4(
input logic clk,
input logic [31:0] in,
output logic [31:0] out
@@ -244,8 +244,8 @@ _Листинг 4. Цепочка блокирующих присваивани
Давайте разнесем логику работы каждого регистра по отдельным блокам `always`.
```SystemVerilog
module example_4(
```Verilog
module example_5(
input logic clk,
input logic [31:0] in,
output logic [31:0] out
@@ -292,14 +292,14 @@ _Рисунок 9. Симуляция модуля, описанного Лис
_Рисунок 10. Моделирование поведения сдвигового регистра._
Однако, как уже объяснялось ранее, вы не можете рассчитывать на такой результат. Сегодня симулятор смоделировал поведение одним образом — завтра он смоделирует этот же код (в котором не изменилась ни одна строка) по-другому, и будет по прежнему работать в соответствии со стандартом.
Однако, как уже объяснялось ранее, вы не можете рассчитывать на такой результат. Сегодня симулятор смоделировал поведение одним образом — завтра он смоделирует этот же код (в котором не изменилась ни одна строка) по-другому, и будет по-прежнему работать в соответствии со стандартом.
Для того, чтобы получить детерминированный результат, вам необходимо снова воспользоваться неблокирующим присваиванием, поскольку и в этом случае порядок исполнения блоков `always` не влияет на результат присваиваний — сначала вычисляются значения `RHS` всех неблокирующих присваиваний всех программных блоков, и только потом происходит присваивание этих значений `LHS`.
Рассмотрим еще один пример того, как различие в присваиваниях приведет к описанию двух различных схем:
```SystemVerilog
module example_5(
```Verilog
module example_6(
input logic clk,
input logic a, b, c,
output logic d
@@ -349,8 +349,8 @@ _Рисунок 12. Схема, сгенерированная Vivado по оп
Рассмотрим зависимость от типа присваивания в комбинационных схемах. Для этого возьмем предыдущий пример, и уберем тактирующий синхроимпульс.
```SystemVerilog
module example_6(
```Verilog
module example_7(
input logic a, b, c,
output logic d
);
@@ -413,12 +413,14 @@ _Рисунок 14. Моделирование цепочки присваива
Обратите внимание, поведение схем описанных при разных типах присваивания слегка различаются. При блокирующем присваивании все сигналы приняли установившиеся значения за один проход блока `always`, при неблокирующем потребовалось несколько проходов. Однако с точки зрения пользователя, читающего временную диаграмму, в обоих ситуациях сигналы изменили свое значение мгновенно.
Поэтому не смотря на различия в типах присваиваний схемы получились одинаковыми.
Поэтому несмотря на различия в типах присваиваний схемы получились одинаковыми.
> Получается что для комбинационной логики нет разницы между блокирующим и неблокирующим присваиванием, после переходных процессов результат будет одинаковым?
И да и нет. С точки зрения синтеза схемы так и есть. Однако есть нюанс в случае моделирования схемы. Поведение комбинационной логики лучше моделирует блокирующее присваивание[[1, стр. 14]](http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf).
## Итоги главы
Подведем итоги прочитанному:
- Блокирующее присваивание блокирует выполнение остальных операций до завершения текущего присваивания. Оно подобно обычному присваиванию в парадигме программирования.
@@ -435,7 +437,7 @@ _Рисунок 14. Моделирование цепочки присваива
- _Не смешивайте в одном блоке блокирующие и неблокирующие присваивания_ — стандарт допускает подобное описание, но оно затрудняет его чтение. Представьте, что читая описание схемы, вам бы постоянно приходилось держать в голове какие присваивания уже произошли, а какие только произойдут, чтобы понять как эта схема работает.
- _Не смешивайте блокирующие и неблокирующие присваивания для одного и того же сигнала_ — стандарт это запрещает (для блоков `always_ff`, `always_comb`, `always_latch`).
Использованная литература:
## Список источников
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)