mirror of
https://github.com/MPSU/APS.git
synced 2025-11-20 23:00:40 +00:00
Compare commits
28 Commits
3d535f765b
...
feedback_w
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
306162bdb4 | ||
|
|
e69490613a | ||
|
|
40911947d6 | ||
|
|
200fb19e50 | ||
|
|
e82ec9a836 | ||
|
|
46233e3a6a | ||
|
|
530aeb2b13 | ||
|
|
449bccc4ca | ||
|
|
8e9bea28de | ||
|
|
8bb0dc396e | ||
|
|
de5a19a41b | ||
|
|
c75a3bc2f3 | ||
|
|
db58f70145 | ||
|
|
c930d2d069 | ||
|
|
ab2b963008 | ||
|
|
fc8c9f8063 | ||
|
|
070b21bb17 | ||
|
|
ad437aa561 | ||
|
|
b8f85cdac9 | ||
|
|
1690104348 | ||
|
|
93d0a1eb67 | ||
|
|
2e90212df9 | ||
|
|
9e3042a2bb | ||
|
|
dd9a7a2025 | ||
|
|
f793a5be7f | ||
|
|
1b474abd8e | ||
|
|
12e0b9fa89 | ||
|
|
4348086be1 |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 37 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 45 KiB |
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
Начать придется издалека. Несмотря на то, что SystemVerilog является **языком описания аппаратуры**, он так же является и языком для верификации описанной аппаратуры (слово `Verilog` является объединением двух слов: `verification` и `logic` [[2](http://archive.computerhistory.org/resources/access/text/2013/11/102746653-05-01-acc.pdf), стр. 24]). Для целей верификации в языке выделено целое подмножество конструкций, которые не могут быть использованы для описания аппаратуры — так называемое "_несинтезируемое подмножество языка SystemVerilog_". Разумеется, часть языка, которая может быть использована для описания аппаратуры ("_синтезируемое подмножество языка SystemVerilog_") тоже может использоваться в верификации.
|
Начать придется издалека. Несмотря на то, что SystemVerilog является **языком описания аппаратуры**, он так же является и языком для верификации описанной аппаратуры (слово `Verilog` является объединением двух слов: `verification` и `logic` [[2](http://archive.computerhistory.org/resources/access/text/2013/11/102746653-05-01-acc.pdf), стр. 24]). Для целей верификации в языке выделено целое подмножество конструкций, которые не могут быть использованы для описания аппаратуры — так называемое "_несинтезируемое подмножество языка SystemVerilog_". Разумеется, часть языка, которая может быть использована для описания аппаратуры ("_синтезируемое подмножество языка SystemVerilog_") тоже может использоваться в верификации.
|
||||||
|
|
||||||
Давайте для начала разберемся в том, как будут использоваться операторы присваивания при программном моделировании (так называемой симуляции) — одним из инструментов верификации. Разобравшись в поведении операторов во время симуляции, будет куда проще объяснить результат использования операторов при синтезе цифровой схемы.
|
Давайте для начала разберемся в том, как будут использоваться операторы присваивания при программном моделировании (так называемой симуляции) — одном из инструментов верификации. Разобравшись в поведении операторов во время симуляции, будет куда проще объяснить результат использования операторов при синтезе цифровой схемы.
|
||||||
|
|
||||||
Введем пару сокращений для удобства дальнейшего повествования:
|
Введем пару сокращений для удобства дальнейшего повествования:
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ _Листинг 1. Пример непрерывного и процедурно
|
|||||||
|
|
||||||
Под "программными блоками" подразумеваются блоки `always` (всех типов) и `initial`. Есть и другие программные блоки, но в рамках данного курса лабораторных работ вы с ними не столкнетесь. Вообще говоря, синтаксис языка SystemVerilog допускает использование оператора `assign` внутри программного блока (так называемое "**процедурное непрерывное присваивание**") [[2, стр. 256]](https://ieeexplore.ieee.org/document/10458102), однако в рамках данного курса не существует ни одной ситуации, когда это может потребоваться и со 100% вероятностью будет ошибочно.
|
Под "программными блоками" подразумеваются блоки `always` (всех типов) и `initial`. Есть и другие программные блоки, но в рамках данного курса лабораторных работ вы с ними не столкнетесь. Вообще говоря, синтаксис языка SystemVerilog допускает использование оператора `assign` внутри программного блока (так называемое "**процедурное непрерывное присваивание**") [[2, стр. 256]](https://ieeexplore.ieee.org/document/10458102), однако в рамках данного курса не существует ни одной ситуации, когда это может потребоваться и со 100% вероятностью будет ошибочно.
|
||||||
|
|
||||||
В отличие от непрерывного присваивания, **процедурное присваивание может быть использовано только в программных блоках**.
|
В отличие от непрерывного присваивания, **процедурное присваивание может быть использовано только в программных блоках** (процедурное присваивание в общем-то и является присваиванием, произошедшим в программном блоке).
|
||||||
|
|
||||||
С точки зрения моделирования (не описания аппаратуры), программный блок — это программа (в привычном вам понимании парадигмы программирования), исполняющаяся в отдельном процессе. Программные блоки исполняются независимо друг от друга по определенным событиям.
|
С точки зрения моделирования (не описания аппаратуры), программный блок — это программа (в привычном вам понимании парадигмы программирования), исполняющаяся в отдельном процессе. Программные блоки исполняются независимо друг от друга по определенным событиям.
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ _Рисунок 1. Пример цепочки блокирующих присв
|
|||||||
1. Сперва вычисляется `RHS` первого присваивания программного блока — константа `5`.
|
1. Сперва вычисляется `RHS` первого присваивания программного блока — константа `5`.
|
||||||
2. Затем, вычисленное значение записывается в LHS первого присваивания — сигнал `a` становится равным `5`.
|
2. Затем, вычисленное значение записывается в LHS первого присваивания — сигнал `a` становится равным `5`.
|
||||||
3. Далее вычисляется `RHS` следующего присваивания — `a`, которое к этому моменту уже равно `5`.
|
3. Далее вычисляется `RHS` следующего присваивания — `a`, которое к этому моменту уже равно `5`.
|
||||||
4. Поскольку вычисленное `RHS` равняется `5` то `LHS` второго присваивания (`b`) тоже становится равным `5`.
|
4. Поскольку вычисленное `RHS` равняется `5`, `LHS` второго присваивания (`b`) тоже становится равным `5`.
|
||||||
5. Аналогичным образом `c` тоже становится равным `5`.
|
5. Аналогичным образом `c` тоже становится равным `5`.
|
||||||
|
|
||||||
Обратите внимание, что все это произошло в нулевой момент времени. На временной диаграмме Vivado просто отобразится, что все сигналы одновременно стали равны `5`, однако с точки зрения симулятора это было не так. Другие симуляторы (например `QuestaSim`) позволяют настроить временную диаграмму таким образом, чтобы отображались все переходы между присваиваниями.
|
Обратите внимание, что все это произошло в нулевой момент времени. На временной диаграмме Vivado просто отобразится, что все сигналы одновременно стали равны `5`, однако с точки зрения симулятора это было не так. Другие симуляторы (например `QuestaSim`) позволяют настроить временную диаграмму таким образом, чтобы отображались все переходы между присваиваниями.
|
||||||
@@ -234,7 +234,7 @@ assign out = c;
|
|||||||
endmodule
|
endmodule
|
||||||
```
|
```
|
||||||
|
|
||||||
_Листинг 4. Цепочка блокирующих присваиваний в порядке обратном приведенному в Листинге 2._
|
_Листинг 4. Цепочка блокирующих присваиваний в порядке, обратном приведенному в Листинге 2._
|
||||||
|
|
||||||
В этом случае, линтер не сообщит ни о каких ошибках, а Vivado сгенерирует схему, аналогичную _рис. 8_
|
В этом случае, линтер не сообщит ни о каких ошибках, а Vivado сгенерирует схему, аналогичную _рис. 8_
|
||||||
|
|
||||||
@@ -369,7 +369,7 @@ _Листинг 7. Пример цепочки блокирующих присв
|
|||||||
|
|
||||||
> Обратите внимание на то, что `always_ff` поменялся на `always_comb`.
|
> Обратите внимание на то, что `always_ff` поменялся на `always_comb`.
|
||||||
|
|
||||||
Как вы думаете, какая схема будет сгенерирована по описанию, представленному _Листинга 7_, и что произойдет с этой схемой, если заменить в нем все блокирующие присваивания на неблокирующие?
|
Как вы думаете, какая схема будет сгенерирована по описанию, представленному в _листинге 7_, и что произойдет с этой схемой, если заменить в нем все блокирующие присваивания на неблокирующие?
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ endmodule
|
|||||||
|-------------|-------------------------------------------------------------------------|
|
|-------------|-------------------------------------------------------------------------|
|
||||||
|sum[0]; | Обращение к младшему биту вектора sum, объявленного выше |
|
|sum[0]; | Обращение к младшему биту вектора sum, объявленного выше |
|
||||||
|sum[7:5]; | Обращение к старшим трём битам 8-битного вектора sum, объявленного выше |
|
|sum[7:5]; | Обращение к старшим трём битам 8-битного вектора sum, объявленного выше |
|
||||||
|sum[5+:3]; | Обращение к трём битам, начиная со пятого (т.е. это аналог предыдущего выражения, удобно использовать, когда известен начальный бит и их количество, а конечный нужно считать через них) |
|
|sum[5+:3]; | Обращение к трём битам, начиная с пятого (т.е. это аналог предыдущего выражения, удобно использовать, когда известен начальный бит и их количество, а конечный нужно считать через них) |
|
||||||
|sum[7-:3]; | Обращение к трём битам, заканчивая седьмым (т.е. это аналог предыдущего выражения, удобно использовать, когда известен конечный бит и их количество, а начальный нужно считать через них) |
|
|sum[7-:3]; | Обращение к трём битам, заканчивая седьмым (т.е. это аналог предыдущего выражения, удобно использовать, когда известен конечный бит и их количество, а начальный нужно считать через них) |
|
||||||
|
|
||||||
_Таблица 1. Способы обращения как к отдельным битам вектора, так и к диапазонам его бит._
|
_Таблица 1. Способы обращения как к отдельным битам вектора, так и к диапазонам его бит._
|
||||||
|
|||||||
@@ -61,13 +61,13 @@ assign Y = S==1 ? D1 : D0;
|
|||||||
|
|
||||||
## Блок always
|
## Блок always
|
||||||
|
|
||||||
Блок `always` — это специальный блок, который позволяет описывать комбинационные и последовательностные схемы (см. документ "[Последовательностная логика](../Introduction/Sequential%20logic.md)"), используя более сложные конструкции, такие как `if-else`, `case`. На самом деле, в языке SystemVerilog помимо общего блока `always`, которым можно описать любой вид логики, существует множество специализированных блоков, предназначенных для описания отдельно комбинационной, синхронной и последовательностной асинхронной логики соответственно:
|
Блок `always` — это специальный блок, который позволяет описывать комбинационные и последовательностные схемы (см. документ "[Последовательностная логика](../Introduction/Sequential%20logic.md)"), используя более сложные конструкции, такие как `if-else`, `case`. На самом деле, в языке SystemVerilog помимо общего блока `always`, которым можно описать любой вид логики, существует множество специализированных блоков, предназначенных для описания отдельно комбинационной, последовательностной синхронной и асинхронной логики соответственно:
|
||||||
|
|
||||||
- always_comb
|
- always_comb
|
||||||
- always_ff
|
- always_ff
|
||||||
- always_latch
|
- always_latch
|
||||||
|
|
||||||
Мультиплексор можно описать в любом из этих блоков, разница будет лишь в том, к чему именно будет подключен выход мультиплексора: к проводу, регистру, или защелке.
|
Мультиплексор можно описать в любом из этих блоков, разница будет лишь в том, к чему именно будет подключен выход мультиплексора: к проводу, регистру или защелке.
|
||||||
|
|
||||||
В зависимости от вида `always`-блока используется один из двух видов присваиваний: **блокирующее присваивание** (`=`) и **неблокирующего присваивания** (`<=`). Подробно о различиях между присваиваниями рассказано в [этом документе](Assignments.md). До его прочтения запомните:
|
В зависимости от вида `always`-блока используется один из двух видов присваиваний: **блокирующее присваивание** (`=`) и **неблокирующего присваивания** (`<=`). Подробно о различиях между присваиваниями рассказано в [этом документе](Assignments.md). До его прочтения запомните:
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ assign Y = S==1 ? D1 : D0;
|
|||||||
|
|
||||||
Далее описывается присваивание сигнала, который должен идти на выход при управляющем сигнале равном единице (значение до оператора `:` в тернарном операторе).
|
Далее описывается присваивание сигнала, который должен идти на выход при управляющем сигнале равном единице (значение до оператора `:` в тернарном операторе).
|
||||||
|
|
||||||
После, в блоке `else` описывается присваивание сигнала, который должен идти на выход при управляющем сигнале равном нулю (значение после оператора `:` в тернарном операторе).
|
После в блоке `else` описывается присваивание сигнала, который должен идти на выход при управляющем сигнале, равном нулю (значение после оператора `:` в тернарном операторе).
|
||||||
|
|
||||||
```Verilog
|
```Verilog
|
||||||
logic Y;
|
logic Y;
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ end
|
|||||||
```
|
```
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> Обратите внимание на оператор `<=`. В данном случае, это не знак "меньше либо равно", а оператор **неблокирующего присваивания**. Существует оператор **блокирующего присваивания** (`=`), который может поменять способ построения схемы для такого же выражения справа от оператора. Хоть это и плохая практика в обучении, но пока вам надо просто запомнить: **при описании записи в регистр всегда используйте оператор неблокирующего присваивания `<=`**. Подробнее о рассказано в документе "[О различиях между блокирующими и неблокирующими присваиваниями](./Assignments.md)".
|
> Обратите внимание на оператор `<=`. В данном случае, это не знак "меньше либо равно", а оператор **неблокирующего присваивания**. Существует оператор **блокирующего присваивания** (`=`), который может поменять способ построения схемы для такого же выражения справа от оператора. Хоть это и плохая практика в обучении, но пока вам надо просто запомнить: **при описании записи в регистр всегда используйте оператор неблокирующего присваивания `<=`**. Подробнее рассказано в документе "[О различиях между блокирующими и неблокирующими присваиваниями](./Assignments.md)".
|
||||||
|
|
||||||
Помимо прочего, нам необходимо связать выход схемы с выходом регистра. Это можно сделать уже известным вам оператором **непрерывного присваивания** `assign`.
|
Помимо прочего, нам необходимо связать выход схемы с выходом регистра. Это можно сделать уже известным вам оператором **непрерывного присваивания** `assign`.
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ endmodule
|
|||||||
Обратите внимание на очередность условий. В первую очередь, мы проверяем условие **сброса**, и только после этого условие **разрешения на запись**.
|
Обратите внимание на очередность условий. В первую очередь, мы проверяем условие **сброса**, и только после этого условие **разрешения на запись**.
|
||||||
Если сперва проверить разрешение на запись, а затем в блоке `else` описать логику сброса, то регистр не будет сбрасываться в случае, если `enable` будет равен `1` (запись в регистр будет приоритетней его сброса). Если сброс описать не в блоке `else`, а в отдельном блоке `if`, то может возникнуть неопределенное поведение: нельзя однозначно сказать, что запишется в регистр, если одновременно придут сигналы `reset` и `enable`. Поэтому при наличии сигнала сброса, остальная логика по записи в регистр должна размещаться в блоке `else`.
|
Если сперва проверить разрешение на запись, а затем в блоке `else` описать логику сброса, то регистр не будет сбрасываться в случае, если `enable` будет равен `1` (запись в регистр будет приоритетней его сброса). Если сброс описать не в блоке `else`, а в отдельном блоке `if`, то может возникнуть неопределенное поведение: нельзя однозначно сказать, что запишется в регистр, если одновременно придут сигналы `reset` и `enable`. Поэтому при наличии сигнала сброса, остальная логика по записи в регистр должна размещаться в блоке `else`.
|
||||||
|
|
||||||
Кроме того, САПР-ы смотрят на паттерн описания элемента схемы, и когда распознают его, реализуют элемент так как задумывал разработчик. Поэтому при описании регистра всегда сперва описывается сигнал сброса (если он используется) и только затем в блоке `else` описывается вся остальная часть логики записи.
|
Кроме того, САПР смотрят на паттерн описания элемента схемы и, когда распознают его, реализуют элемент так, как задумывал разработчик. Поэтому при описании регистра всегда сперва описывается сигнал сброса (если он используется) и только затем в блоке `else` описывается вся остальная часть логики записи.
|
||||||
|
|
||||||
Итоговая схема регистра со сбросом и сигналом разрешения записи:
|
Итоговая схема регистра со сбросом и сигналом разрешения записи:
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ endmodule
|
|||||||
|
|
||||||
Поэтому так важно разобраться в базовом способе описания регистра.
|
Поэтому так важно разобраться в базовом способе описания регистра.
|
||||||
|
|
||||||
Более того, с точки зрения синтезатора данное описание проще для синтеза, т.к. ему не разделять из одного `always` блока комбинационную и синхронные части.
|
Более того, с точки зрения синтезатора данное описание проще для синтеза, т.к. ему не надо разделять из одного `always` блока комбинационную и синхронные части.
|
||||||
|
|
||||||
Вообще говоря, регистр в общем смысле этого слова представляет собой многоразрядную конструкцию (в рассмотренном ранее примере, 1-битный регистр мог представлять из себя простой D-триггер).
|
Вообще говоря, регистр в общем смысле этого слова представляет собой многоразрядную конструкцию (в рассмотренном ранее примере, 1-битный регистр мог представлять из себя простой D-триггер).
|
||||||
Создание многоразрядного регистра мало отличается от создания многоразрядного провода, а описание логики записи в многоразрядный регистр ничем не отличается от логики записи в одноразрядный регистр:
|
Создание многоразрядного регистра мало отличается от создания многоразрядного провода, а описание логики записи в многоразрядный регистр ничем не отличается от логики записи в одноразрядный регистр:
|
||||||
|
|||||||
48
ERRATA.md
48
ERRATA.md
@@ -2,6 +2,44 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
**28.10.2025**: В ЛР№3 (стр. 90) указано неверное количество блоков, необходимое для реализации 1 KiB памяти.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary> Исправленная версия абзаца </summary>
|
||||||
|
|
||||||
|
Таким образом, преимущество распределенной памяти относительно регистровой заключается в лучшей утилизации ресурсов: одним трёхвходовым LUT можно описать до 8 бит распределенной памяти, в то время как одним D-триггером можно описать только один бит регистровой памяти. Предположим, что в ПЛИС размещены логические блоки, структура которых изображена на _рис. 2_ и нам необходимо реализовать 1 KiB памяти. Мы можем реализовать распределенную память, используя <ins>512</ins> логических блоков (в каждом блоке два трёхвходовых LUT), либо регистровую память, используя <ins>8192</ins> логических блока.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
**27.10.2025**: Исправлено отображение инверсии выхода Q̅ в _рисунках I.2-13_ и _I.3-6_.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary> Исправленная версия рисунка </summary>
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
**27.10.2025**: Исправлена опечатка в описании функционального поведения ведомой защёлки в составе D-триггера на стр. 41:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- пока сигнал `clk = 0`
|
||||||
|
+ пока сигнал `clk = 1`
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary> Исправленная версия предложения </summary>
|
||||||
|
|
||||||
|
Несмотря на то, что ведомая защёлка "прозрачна" всё то время, пока сигнал `clk = 1`, данные в ней остаются стабильными, поскольку выход ведущей защёлки больше не может измениться.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
**22.05.2025**: Исправлено несоответствие в названиях модулей в ЛР10-12.
|
**22.05.2025**: Исправлено несоответствие в названиях модулей в ЛР10-12.
|
||||||
|
|
||||||
- `irq_controller` следует читать как `interrupt_controller`;
|
- `irq_controller` следует читать как `interrupt_controller`;
|
||||||
@@ -18,7 +56,7 @@ _Рисунок II.12-3. Структурная схема блока приор
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
**13.05.2025**: Исправлен рисунок II.8-3 — исправлена опечатка в названии нижнего сигнала (`mem_wd_i` → `mem_wd_o`).
|
**13.05.2025**: Исправлен рисунок II.8-3 — исправлена опечатка в названии нижнего сигнала (`mem_wd_i` → `mem_wd_o`).
|
||||||
|
|
||||||
@@ -47,7 +85,7 @@ _Рисунок II.12-3. Структурная схема блока приор
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
**11.07.2025**: Обнаружена ошибка вёрстки в примере использования битовых сдвигов на стр. 79. Операции по установке, очистке и чтению N-го бита выглядят следующим образом:
|
**11.07.2025**: Обнаружена ошибка вёрстки в примере использования битовых сдвигов на стр. 79. Операции по установке, очистке и чтению N-го бита выглядят следующим образом:
|
||||||
|
|
||||||
@@ -57,7 +95,7 @@ X = X & ~(1 << N); // Очистка N-го бита
|
|||||||
Y = (X & (1 << N)) != 0; // Чтение N-го бита
|
Y = (X & (1 << N)) != 0; // Чтение N-го бита
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
**11.07.2025**: Исправлена опечатка в предпоследнем абзаце стр. 227 (в конце первого предложения должен был быть написан **LMA**):
|
**11.07.2025**: Исправлена опечатка в предпоследнем абзаце стр. 227 (в конце первого предложения должен был быть написан **LMA**):
|
||||||
|
|
||||||
@@ -73,13 +111,13 @@ Y = (X & (1 << N)) != 0; // Чтение N-го бита
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
**16.06.2025**: Исправлена ошибка в _листинге II.14-2_.
|
**16.06.2025**: Исправлена ошибка в _листинге II.14-2_.
|
||||||
|
|
||||||
Предпоследнюю инструкцию (`lw a0, 40(a0)`) следует читать как `lw a0, 24(a0)`.
|
Предпоследнюю инструкцию (`lw a0, 40(a0)`) следует читать как `lw a0, 24(a0)`.
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
**29.03.2025**: Исправлен рисунок II.4-4 — убрана логика безусловного перехода, т.к. она должна была появиться только в следующем параграфе.
|
**29.03.2025**: Исправлен рисунок II.4-4 — убрана логика безусловного перехода, т.к. она должна была появиться только в следующем параграфе.
|
||||||
|
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ _Рисунок 13. Программируемая ячейка памяти П
|
|||||||
|
|
||||||
## Таблицы подстановки (Look-Up Tables, LUTs)
|
## Таблицы подстановки (Look-Up Tables, LUTs)
|
||||||
|
|
||||||
Представьте мультиплексор с четырьмя входными сигналами и двухбитным управляющим сигналом (обратите внимание, что в теперь это сигнал использует обычное двоичное кодирование). Но теперь, вместо того чтобы выставлять входные сигналы во внешний мир, давайте подключим их к программируемой памяти. Это означает, что мы можем "запрограммировать" каждый из входов на какое-то константное значение. Поместим то, что у нас получилось, в отдельный блок и вот, мы получили двухвходовую **Таблицу подстановки** (**Look-Up Tables**, далее **LUT**).
|
Представьте мультиплексор с четырьмя входными сигналами и двухбитным управляющим сигналом (обратите внимание, что теперь это сигнал использует обычное двоичное кодирование). Но теперь, вместо того чтобы выставлять входные сигналы во внешний мир, давайте подключим их к программируемой памяти. Это означает, что мы можем "запрограммировать" каждый из входов на какое-то константное значение. Поместим то, что у нас получилось, в отдельный блок и вот, мы получили двухвходовую **Таблицу подстановки** (**Look-Up Tables**, далее **LUT**).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,13 @@ _Рисунок 1. Пример комбинационной (а), и после
|
|||||||
|
|
||||||
Последовательностная логика делится на **синхронную** и **асинхронную**.
|
Последовательностная логика делится на **синхронную** и **асинхронную**.
|
||||||
|
|
||||||
**Синхронной логикой** называется такая логика, которая обновляет своё состояние (содержимое ячеек памяти) одновременно (**синхронно**) с фронтом тактового сигнала. В свою очередь **асинхронная последовательностная логика** — это логика, которая может обновлять своё состояние **асинхронно** (т.е. без привязки к фронту тактового синхроимпульса). Бывает также и синхронная логика с асинхронными сигналами предустановки/сброса.
|
**Синхронной логикой** называется такая логика, которая обновляет своё состояние (содержимое ячеек памяти) одновременно (**синхронно**) с фронтом тактового сигнала*. В свою очередь **асинхронная последовательностная логика** — это логика, которая может обновлять своё состояние **асинхронно** (т.е. без привязки к фронту тактового синхроимпульса). Бывает также и синхронная логика с асинхронными сигналами предустановки/сброса.
|
||||||
|
|
||||||
Комбинационная логика по своей природе является асинхронной, поэтому в зависимости от контекста под "асинхронной логикой" может подразумеваться как комбинационная логика, так и последовательностная логика, которая может обновлять значение не по фронту тактового синхроимпульса.
|
Комбинационная логика по своей природе является асинхронной, поэтому в зависимости от контекста под "асинхронной логикой" может подразумеваться как комбинационная логика, так и последовательностная логика, которая может обновлять значение не по фронту тактового синхроимпульса.
|
||||||
|
|
||||||
|
> [!Info]
|
||||||
|
> В некоторых источниках синхронной логикой могут называть и ту, что работает по уровню (а не фронту) единого источника тактового синхроимпульса [[1, стр. 164](https://reader.lanbook.com/book/241166?lms=d92e0036d4c90623ffd0a8ecc34dee72)].
|
||||||
|
|
||||||
## Бистабильные ячейки
|
## Бистабильные ячейки
|
||||||
|
|
||||||
**Бистабильная ячейка** — это элемент статической памяти, способный принимать одно из двух устойчивых состояний, соответствующих цифровым значениям "0" или "1".
|
**Бистабильная ячейка** — это элемент статической памяти, способный принимать одно из двух устойчивых состояний, соответствующих цифровым значениям "0" или "1".
|
||||||
@@ -86,13 +89,13 @@ D-триггер — это элемент статической памяти,
|
|||||||
|
|
||||||
_Рисунок 5. Схема и таблица истинности D-триггера._
|
_Рисунок 5. Схема и таблица истинности D-триггера._
|
||||||
|
|
||||||
Принцип работы D-триггера, схема которого представлена на _рис. 5_ заключается в том, что управляющий сигнал `E` одной защёлки является инверсией управляющего сигнала `E` другой защёлки. Это значит, что пока одна защёлка "прозрачна" и принимает данные со входа — другая "непрозрачна" и данные не принимает. В момент, когда тактовый синхроимпульс меняет своё значение с `0` на `1`, ведущая защёлка становится "непрозрачной" для новых данных с входа `D`, и "запертые" в ней данные попадают в только что открывшуюся ведомую защёлку. Несмотря на то, что ведомая защёлка "прозрачна" всё то время, пока сигнал `clk = 0`, данные в ней остаются стабильными, поскольку выход ведущей защёлки больше не может измениться.
|
Принцип работы D-триггера, схема которого представлена на _рис. 5_ заключается в том, что управляющий сигнал `E` одной защёлки является инверсией управляющего сигнала `E` другой защёлки. Это значит, что пока одна защёлка "прозрачна" и принимает данные со входа — другая "непрозрачна" и данные не принимает. В момент, когда тактовый синхроимпульс меняет своё значение с `0` на `1`, ведущая защёлка становится "непрозрачной" для новых данных с входа `D`, и "запертые" в ней данные попадают в только что открывшуюся ведомую защёлку. Несмотря на то, что ведомая защёлка "прозрачна" всё то время, пока сигнал `clk = 1`, данные в ней остаются стабильными, поскольку выход ведущей защёлки больше не может измениться.
|
||||||
|
|
||||||
Описанные схемы бистабильных ячеек представляют собой скорее математическое описание элементов памяти — так проще объяснить принцип их работы. Если ваша технология позволяет реализовать элементы И, ИЛИ и НЕ — значит вы точно можете реализовать подобные элементы. При этом, используя особенности конкретной технологии, данные схемы можно реализовывать более эффективно. D-защёлку, к примеру, можно реализовать схемой, представленной на _рис. 6_.
|
Описанные схемы бистабильных ячеек представляют собой скорее математическое описание элементов памяти — так проще объяснить принцип их работы. Если ваша технология позволяет реализовать элементы И, ИЛИ и НЕ — значит вы точно можете реализовать подобные элементы. При этом, используя особенности конкретной технологии, данные схемы можно реализовывать более эффективно. D-защёлку, к примеру, можно реализовать схемой, представленной на _рис. 6_.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
_Рисунок 6. Конфигурируемая ячейка памяти ПЛИС Xilinx XC2064 [[1, стр. 2-63](https://archive.org/details/programmablegate00xili/page/n93/mode/2up)]._
|
_Рисунок 6. Конфигурируемая ячейка памяти ПЛИС Xilinx XC2064 [[2, стр. 2-63](https://archive.org/details/programmablegate00xili/page/n93/mode/2up)]._
|
||||||
|
|
||||||
## Метастабильность
|
## Метастабильность
|
||||||
|
|
||||||
@@ -173,12 +176,13 @@ _Рисунок 10. Схема и временная диаграмма прос
|
|||||||
11. **D-триггер** — это бистабильная ячейка, которая подобно D-защёлке имеет входы `clk` и `D`, но который сохраняет данные только в момент одного из фронтов тактового синхроимпульса (положительного или отрицательного фронта). Как и любые бистабильные ячейки, D-триггер подвержен явлению **метастабильности**. Метастабильность в D-триггере может возникнуть, если данные на входе `D` меняются во временном окне, расположенном в окрестностях фронта тактового синхроимпульса, определяемом следующими двумя параметрами:
|
11. **D-триггер** — это бистабильная ячейка, которая подобно D-защёлке имеет входы `clk` и `D`, но который сохраняет данные только в момент одного из фронтов тактового синхроимпульса (положительного или отрицательного фронта). Как и любые бистабильные ячейки, D-триггер подвержен явлению **метастабильности**. Метастабильность в D-триггере может возникнуть, если данные на входе `D` меняются во временном окне, расположенном в окрестностях фронта тактового синхроимпульса, определяемом следующими двумя параметрами:
|
||||||
1. T<sub>setup</sub> (**setup time**) — **время предустановки**. Это интервал, в течение которого сигнал на входе должен оставаться неизменным перед наступлением фронта тактового сигнала.
|
1. T<sub>setup</sub> (**setup time**) — **время предустановки**. Это интервал, в течение которого сигнал на входе должен оставаться неизменным перед наступлением фронта тактового сигнала.
|
||||||
2. T<sub>hold</sub> (**hold time**) — **время удержания**. Это интервал, в течение которого сигнал на входе должен оставаться стабильным после наступления фронта тактового сигнала.
|
2. T<sub>hold</sub> (**hold time**) — **время удержания**. Это интервал, в течение которого сигнал на входе должен оставаться стабильным после наступления фронта тактового сигнала.
|
||||||
12. **Метастабильное состояние** — это состояние бистабильной ячейки, при котором та не находится ни в одном из стабильных цифровых состояниях: `0`/`1`, находясь при этом примерно посередине между ними. Через неопределённый (но предсказуемое с точки зрения вероятностей) промежуток времени, бистабильная ячейка может выйти из этого состояния, приняв любое из значений `0`/`1`.
|
12. **Метастабильное состояние** — это состояние бистабильной ячейки, при котором та не находится ни в одном из стабильных цифровых состояниях `0`/`1`, находясь при этом примерно посередине между ними. Через неопределённый промежуток времени (длину которого можно оценить с точки зрения вероятностей) бистабильная ячейка может выйти из этого состояния, приняв любое из значений `0`/`1`.
|
||||||
13. В большинстве случаев метастабильность является нежелательным явлением в цифровой схеме. Причиной такого явления может стать работа схемы на частоте, не подходящей для имеющегося у данной схемы критического пути. Для того, чтобы узнать, сможет ли схема работать на заданной частоте, проводится **статический временной анализ** (**static timing analysis**, **STA**).
|
13. В большинстве случаев метастабильность является нежелательным явлением в цифровой схеме. Причиной такого явления может стать работа схемы на частоте, не подходящей для имеющегося у данной схемы критического пути. Для того, чтобы узнать, сможет ли схема работать на заданной частоте, проводится **статический временной анализ** (**static timing analysis**, **STA**).
|
||||||
14. Метастабильность может возникнуть и в случае, если сигнал данных по своей природе является асинхронным тактовому сигналу бистабильной ячейки: он может передаваться по событиям из внешнего мира, или с выхода бистабильных ячеек, работающих от других тактовых синхроимпульсов (подобная ситуация называется **пересечением тактовых доменов**, **clock domain crossing**, **CDC**).
|
14. Метастабильность может возникнуть и в случае, если сигнал данных по своей природе является асинхронным тактовому сигналу бистабильной ячейки: он может передаваться по событиям из внешнего мира, или с выхода бистабильных ячеек, работающих от других тактовых синхроимпульсов (подобная ситуация называется **пересечением тактовых доменов**, **clock domain crossing**, **CDC**).
|
||||||
|
|
||||||
## Список источников
|
## Список источников
|
||||||
|
|
||||||
1. Xilinx / [The Programmable Gate Array Data Book](https://archive.org/details/programmablegate00xili);
|
1. [Д.М. Харрис, С.Л. Харрис / Цифровая схемотехника и архитектура компьютера: RISC-V / пер. с англ. В. С. Яценков, А. Ю. Романов; под. ред. А. Ю. Романова / М.: ДМК Пресс, 2021](https://e.lanbook.com/book/241166);
|
||||||
2. J. Wakerly, Digital Design: Principles and Practices (5th Edition). Pearson, 2017;
|
2. Xilinx / [The Programmable Gate Array Data Book](https://archive.org/details/programmablegate00xili);
|
||||||
3. [Метастабильность триггера и межтактовая синхронизация](https://habr.com/ru/articles/254869/).
|
3. J. Wakerly, Digital Design: Principles and Practices (5th Edition). Pearson, 2017;
|
||||||
|
4. [Метастабильность триггера и межтактовая синхронизация](https://habr.com/ru/articles/254869/).
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ module fulladder4(
|
|||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
либо же можно создать массив 1-битных сумматоров.
|
Либо же можно создать массив 1-битных сумматоров.
|
||||||
|
|
||||||
Создание массива модулей схоже с созданием одного модуля за исключением того, что после имени экземпляра модуля указывается диапазон, определяющий количество модулей в массиве. При этом подключение сигналов к массиву модулей осуществляется следующим образом:
|
Создание массива модулей схоже с созданием одного модуля за исключением того, что после имени экземпляра модуля указывается диапазон, определяющий количество модулей в массиве. При этом подключение сигналов к массиву модулей осуществляется следующим образом:
|
||||||
|
|
||||||
@@ -292,6 +292,15 @@ endmodule
|
|||||||
|
|
||||||
_Листинг 3. Пример создания массива модулей._
|
_Листинг 3. Пример создания массива модулей._
|
||||||
|
|
||||||
|
Реализация массива сумматоров будет осложнена тем, что вам потребуется каким-то образом организовать передачу выходного бита переноса предыдущего разряда до входного бита переноса следующего разряда. Для этого рекомендуется создать два 32-битных вектора:
|
||||||
|
|
||||||
|
- вектор входных битов переноса;
|
||||||
|
- вектор выходных битов переноса.
|
||||||
|
|
||||||
|
Далее, с помощью оператора непрерывного присваивания соединить разряды вектора выходных битов переноса с соответствующими разрядами вектора входных битов переноса. Кроме того, вам потребуется связать входной и выходной биты переноса модуля с младшим и старшим разрядом соответствующих векторов.
|
||||||
|
|
||||||
|
После того, как векторы бит переноса будут готовы, создание массива модулей уже не будет представлять сложности.
|
||||||
|
|
||||||
### Порядок выполнения задания
|
### Порядок выполнения задания
|
||||||
|
|
||||||
1. Создайте проект, согласно [руководству по созданию проекта в Vivado](../../Vivado%20Basics/01.%20New%20project.md)
|
1. Создайте проект, согласно [руководству по созданию проекта в Vivado](../../Vivado%20Basics/01.%20New%20project.md)
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ _Таблица 2. Список операций сравнения._
|
|||||||
|
|
||||||
**Выражения в этих двух таблицах приведены для примера. Не все из них можно просто переписать — часть этих выражений надо дополнить. Чтобы вы не копировали выражения, в них вставлены неподдерживаемые символы.**
|
**Выражения в этих двух таблицах приведены для примера. Не все из них можно просто переписать — часть этих выражений надо дополнить. Чтобы вы не копировали выражения, в них вставлены неподдерживаемые символы.**
|
||||||
|
|
||||||
Несмотря на разделение на вычислительные операции, и операции сравнения, в _Таблице 1_ (вычислительных операция) оказалось две операции `SLTS` и `SLTU`, которые выполняют сравнения. В итоге у нас есть две похожие пары инструкций:
|
Несмотря на разделение на вычислительные операции, и операции сравнения, в _Таблице 1_ (вычислительных операций) оказалось две операции: `SLTS` и `SLTU`, которые выполняют сравнения. В итоге у нас есть две похожие пары инструкций:
|
||||||
|
|
||||||
- `LTS`
|
- `LTS`
|
||||||
- `LTU`
|
- `LTU`
|
||||||
|
|||||||
@@ -41,9 +41,9 @@
|
|||||||
|
|
||||||
Для ОЗУ требуется больше сигналов. Кроме входного `addr` и выходного `read_data` добавляются: входные данные для записи `write_data`, сигнал синхронизации `clk`, который определяет момент записи данных и сигнал разрешения на запись `write_enable`, который контролирует нужно ли записывать данные или только считывать. Для того, чтобы записать информацию в такую память необходимо:
|
Для ОЗУ требуется больше сигналов. Кроме входного `addr` и выходного `read_data` добавляются: входные данные для записи `write_data`, сигнал синхронизации `clk`, который определяет момент записи данных и сигнал разрешения на запись `write_enable`, который контролирует нужно ли записывать данные или только считывать. Для того, чтобы записать информацию в такую память необходимо:
|
||||||
|
|
||||||
- выставить адрес `addr` в который планируется запись данных,
|
- выставить адрес `addr`, в который планируется запись данных,
|
||||||
- выставить сами данные для записи на вход `write_data`,
|
- выставить сами данные для записи на вход `write_data`,
|
||||||
- установить сигнал `write_enable` в состояние разрешения записи (как правило это 1) и
|
- установить сигнал `write_enable` в состояние разрешения записи (как правило, это 1) и
|
||||||
- дождаться нужного (положительного, либо отрицательного) фронта `clk` — в этот момент данные будут записаны по указанному адресу.
|
- дождаться нужного (положительного, либо отрицательного) фронта `clk` — в этот момент данные будут записаны по указанному адресу.
|
||||||
|
|
||||||
Также возможна реализация, в которой вход `write_data` и выход `read_data` объединены в единый вход/выход `data`. В этом случае операции чтения и записи разделены во времени и используют для этого один единый порт ввода-вывода (`inout`, двунаправленный порт) `data`.
|
Также возможна реализация, в которой вход `write_data` и выход `read_data` объединены в единый вход/выход `data`. В этом случае операции чтения и записи разделены во времени и используют для этого один единый порт ввода-вывода (`inout`, двунаправленный порт) `data`.
|
||||||
@@ -54,7 +54,7 @@ _Рисунок 1. Примеры блоков ПЗУ и ОЗУ._
|
|||||||
|
|
||||||
Кроме того, различают память с **синхронным** и **асинхронным** чтением. В первом случае, перед выходным сигналом шины данных ставится дополнительный регистр, в который по тактовому синхроимпульсу записываются запрашиваемые данные. Такой способ может значительно сократить **критический путь** цифровой схемы, но требует дополнительный такт на доступ в память. В свою очередь, асинхронное чтение позволяет получить данные, не дожидаясь очередного синхроимпульса, но такой способ увеличивает критический путь.
|
Кроме того, различают память с **синхронным** и **асинхронным** чтением. В первом случае, перед выходным сигналом шины данных ставится дополнительный регистр, в который по тактовому синхроимпульсу записываются запрашиваемые данные. Такой способ может значительно сократить **критический путь** цифровой схемы, но требует дополнительный такт на доступ в память. В свою очередь, асинхронное чтение позволяет получить данные, не дожидаясь очередного синхроимпульса, но такой способ увеличивает критический путь.
|
||||||
|
|
||||||
Еще одной характеристикой памяти является количество доступных портов чтения или записи (не путайте с портами модуля, которые являются любыми его входными/выходными сигналами). Количество портов определяет к скольким ячейкам памяти можно обратиться одновременно. Проще говоря, сколько входов адреса существует. Все примеры памяти рассмотренные выше являются **однопортовыми**, то есть у них один порт. Например, если у памяти 2 входа адреса `addr1` и `addr2` — это **двухпортовая память**. При этом не важно, можно ли по этим адресам только читать/писать или выполнять обе операции.
|
Еще одной характеристикой памяти является количество доступных портов чтения или записи (не путайте с портами модуля, которые являются любыми его входными/выходными сигналами). Количество портов определяет, к скольким ячейкам памяти можно обратиться одновременно. Проще говоря, сколько входов адреса существует. Все примеры памяти рассмотренные выше являются **однопортовыми**, то есть у них один порт. Например, если у памяти 2 входа адреса `addr1` и `addr2` — это **двухпортовая память**. При этом не важно, можно ли по этим адресам только читать/писать или выполнять обе операции.
|
||||||
|
|
||||||
Регистровый файл, который будет реализован в рамках данной работы, является **трехпортовым**, и имеет 2 порта на чтение и 1 порт на запись.
|
Регистровый файл, который будет реализован в рамках данной работы, является **трехпортовым**, и имеет 2 порта на чтение и 1 порт на запись.
|
||||||
|
|
||||||
@@ -64,13 +64,13 @@ _Рисунок 1. Примеры блоков ПЗУ и ОЗУ._
|
|||||||
|
|
||||||
_Рисунок 2. Структурная схема логического блока в ПЛИС[[1]](https://en.wikipedia.org/wiki/Field-programmable_gate_array)._
|
_Рисунок 2. Структурная схема логического блока в ПЛИС[[1]](https://en.wikipedia.org/wiki/Field-programmable_gate_array)._
|
||||||
|
|
||||||
В логическом блоке есть **таблицы подстановки** (Look Up Table, LUT), которые представляют собой не что иное как память, которая конфигурируется под нужды хранения, а не реализацию логики. Таким образом, трехвходовой LUT может выступать в роли 8-битной памяти.
|
В логическом блоке есть **таблицы подстановки** (Look Up Table, LUT), которые представляют собой не что иное как память, которую можно сконфигурировать под нужды хранения, а не реализацию логики. Таким образом, трехвходовой LUT может выступать в роли 8-битной памяти.
|
||||||
|
|
||||||
Однако LUT будет сложно приспособить под многопортовую память: посмотрим на схему еще раз: три входа LUT формируют адрес одной из восьми ячеек. Это означает, что среди этих восьми ячеек нельзя обратиться к двум из них одновременно.
|
Однако LUT будет сложно приспособить под многопортовую память: посмотрим на схему еще раз: три входа LUT формируют адрес одной из восьми ячеек. Это означает, что среди этих восьми ячеек нельзя обратиться к двум из них одновременно.
|
||||||
|
|
||||||
Для реализации многопортовой памяти небольшого размера лучше воспользоваться расположенным в логическом блоке D-триггером (**DFF** на _рис. 2_). Несмотря на то, что D-триггер позволяет воспроизвести только 1 разряд элемента памяти, он не ограничивает реализацию по портам.
|
Для реализации многопортовой памяти небольшого размера лучше воспользоваться расположенным в логическом блоке D-триггером (**DFF** на _рис. 2_). Несмотря на то, что D-триггер позволяет воспроизвести только 1 разряд элемента памяти, он не ограничивает реализацию по портам.
|
||||||
|
|
||||||
Таким образом, преимущество распределенной памяти относительно регистровой заключается в лучшей утилизации ресурсов: одним трёхвходовым LUT можно описать до 8 бит распределенной памяти, в то время как одним D-триггером можно описать только один бит регистровой памяти. Предположим, что в ПЛИС размещены логические блоки, структура которых изображена на _рис. 2_ и нам необходимо реализовать 1 KiB памяти. Мы можем реализовать распределенную память, используя 64 логических блока (в каждом блоке два трёхвходовых LUT), либо регистровую память, используя 1024 логических блока.
|
Таким образом, преимущество распределенной памяти относительно регистровой заключается в лучшей утилизации ресурсов: одним трёхвходовым LUT можно описать до 8 бит распределенной памяти, в то время как одним D-триггером можно описать только один бит регистровой памяти. Предположим, что в ПЛИС размещены логические блоки, структура которых изображена на _рис. 2_ и нам необходимо реализовать 1 KiB памяти. Мы можем реализовать распределенную память, используя 512 логических блоков (в каждом блоке два трёхвходовых LUT), либо регистровую память, используя 8192 логических блока.
|
||||||
|
|
||||||
Недостатком является ограниченность в реализации многопортовой памяти.
|
Недостатком является ограниченность в реализации многопортовой памяти.
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ _Листинг 1. Пример создания массива ячеек._
|
|||||||
|
|
||||||
В первой строке _листинга 1_ создаётся память с шестнадцатью (от 0-го до 15-го адреса) 20-битными ячейками памяти. В таком случае говорят, что ширина памяти 20 бит, а глубина 16. Для адресации такой памяти потребуется адрес с разрядностью ceil(log2(16)) = 4 бита (`ceil` — операция округления вверх).
|
В первой строке _листинга 1_ создаётся память с шестнадцатью (от 0-го до 15-го адреса) 20-битными ячейками памяти. В таком случае говорят, что ширина памяти 20 бит, а глубина 16. Для адресации такой памяти потребуется адрес с разрядностью ceil(log2(16)) = 4 бита (`ceil` — операция округления вверх).
|
||||||
|
|
||||||
Для обращения к конкретной ячейке памяти используются квадратные скобки с указанием нужного адреса: `memory[addr]`. Грубо говоря, то, что указывается в квадратных скобках будет подключено ко входу адреса памяти `memory`.
|
Для обращения к конкретной ячейке памяти используются квадратные скобки с указанием нужного адреса: `memory[addr]` (см. _листинг 2_). Грубо говоря, то, что указывается в квадратных скобках, будет подключено ко входу адреса памяти `memory`.
|
||||||
|
|
||||||
Как уже говорилось, чтение из памяти может быть сделано двумя способами: синхронно и асинхронно.
|
Как уже говорилось, чтение из памяти может быть сделано двумя способами: синхронно и асинхронно.
|
||||||
|
|
||||||
@@ -297,7 +297,7 @@ endmodule
|
|||||||
|
|
||||||
_Листинг 4. SystemVerilog-описание памяти инструкций._
|
_Листинг 4. SystemVerilog-описание памяти инструкций._
|
||||||
|
|
||||||
### 3. Регистровый файл
|
### 2. Регистровый файл
|
||||||
|
|
||||||
Необходимо описать на языке SystemVerilog модуль регистрового файла для процессора с архитектурой RISC-V, представляющего собой трехпортовое ОЗУ с двумя портами на чтение и одним портом на запись и состоящей из 32-х 32-битных регистров, объединенных в массив с именем `rf_mem`.
|
Необходимо описать на языке SystemVerilog модуль регистрового файла для процессора с архитектурой RISC-V, представляющего собой трехпортовое ОЗУ с двумя портами на чтение и одним портом на запись и состоящей из 32-х 32-битных регистров, объединенных в массив с именем `rf_mem`.
|
||||||
|
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ module lab_05_tb_decoder();
|
|||||||
assert(std::randomize(mutation));
|
assert(std::randomize(mutation));
|
||||||
// we want to broke (invert constraint of) func3 if
|
// we want to broke (invert constraint of) func3 if
|
||||||
// 9's bit is 1 while six lower bits are 0
|
// 9's bit is 1 while six lower bits are 0
|
||||||
if(mutation[9] & !(|mutation[5:0])) begin
|
if(mutation[9] & !(|mutation[3:0])) begin
|
||||||
assert(std::randomize(func7 ) with {!(func7 inside {7'h0, 7'h20});});
|
assert(std::randomize(func7 ) with {!(func7 inside {7'h0, 7'h20});});
|
||||||
end
|
end
|
||||||
else begin
|
else begin
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
Данное описание было дано не совсем корректным образом, чтобы в третьей лабораторной работе было более чёткое понимание задания. В чём заключается некорректность? Процессор должен быть способен не только **обращаться** к отдельным байтам в памяти, но и **обновлять** в памяти любой отдельный байт, а также **считывать** отдельные байты.
|
Данное описание было дано не совсем корректным образом, чтобы в третьей лабораторной работе было более чёткое понимание задания. В чём заключается некорректность? Процессор должен быть способен не только **обращаться** к отдельным байтам в памяти, но и **обновлять** в памяти любой отдельный байт, а также **считывать** отдельные байты.
|
||||||
|
|
||||||
Вопрос считывания отдельного байта будет решаться специальным модулем **загрузки и сохранения**. Памяти данных при этом будет достаточно возвращать всё слово, содержащее запрашиваемый байт как это уже было сделано памяти инструкций.
|
Вопрос считывания отдельного байта будет решаться специальным модулем **загрузки и сохранения**. Памяти данных при этом будет достаточно возвращать всё слово, содержащее запрашиваемый байт как это уже было сделано в памяти инструкций.
|
||||||
|
|
||||||
Нас интересует возможность памяти обновлять любой из байт в слове. Подобный функционал часто используется при реализации памяти и в системных интерфейсах, например AXI4 или APB. Для этого используется специальный сигнал, который называется `byte enable`. Разрядность этого сигнала равна числу байт в шине данных (в нашем случае разрядность `byte enable` составляет 4). Вы можете представить этот сигнал, как 4 провода, каждый из которых является сигналом разрешения записи для отдельной памяти с шириной данных в 1 байт.
|
Нас интересует возможность памяти обновлять любой из байт в слове. Подобный функционал часто используется при реализации памяти и в системных интерфейсах, например AXI4 или APB. Для этого используется специальный сигнал, который называется `byte enable`. Разрядность этого сигнала равна числу байт в шине данных (в нашем случае разрядность `byte enable` составляет 4). Вы можете представить этот сигнал, как 4 провода, каждый из которых является сигналом разрешения записи для отдельной памяти с шириной данных в 1 байт.
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ _Рисунок 1. Окно исходных кодов проекта._
|
|||||||
|
|
||||||
`Simulation Sources` хранит в себе иерархию верификационного окружения, **включая модули из папки** `Design Sources` — т.е. все модули (как синтезируемые, так и не синтезируемые), которые будут использованы при моделировании.
|
`Simulation Sources` хранит в себе иерархию верификационного окружения, **включая модули из папки** `Design Sources` — т.е. все модули (как синтезируемые, так и не синтезируемые), которые будут использованы при моделировании.
|
||||||
|
|
||||||
> Обратите внимание на то, вкладка `Hierarchy` не содержит файлов. Здесь отображается иерархия модулей проекта. Один модуль может быть использован несколько раз — и в этом случае он будет столько же раз отображён в иерархии, хотя файл, хранящий описание этого модуля останется один (см. _рис. 6_).
|
> Обратите внимание на то, что вкладка `Hierarchy` не содержит файлов. Здесь отображается иерархия модулей проекта. Один модуль может быть использован несколько раз — и в этом случае он будет столько же раз отображён в иерархии, хотя файл, хранящий описание этого модуля останется один (см. _рис. 6_).
|
||||||
|
|
||||||
#### Добавление файла в проект
|
#### Добавление файла в проект
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ _Рисунок 1. Окно исходных кодов проекта._
|
|||||||
|
|
||||||
- файлы ограничений для синтеза схемы под конкретную ПЛИС (`Constraints`);
|
- файлы ограничений для синтеза схемы под конкретную ПЛИС (`Constraints`);
|
||||||
- файлы проектируемой схемы (`Design Sources`);
|
- файлы проектируемой схемы (`Design Sources`);
|
||||||
- файлы верификационного окружения для верификации схемы (`Simulation Sources`).
|
- файлы верификационного окружения проектируемой схемы (`Simulation Sources`).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ _Рисунок 6. Уведомление об обновлении иерарх
|
|||||||
|
|
||||||
После того, как Vivado закончит обновлять иерархию (и, если при создании файла вы отказались указывать порты модуля, нажав на кнопку `Cancel`), рядом с папкой `Design Sources` появится стрелка, позволяющая развернуть эту папку, внутри которой обнаружится подпапка `Non-module Files` с созданным нами файлом. Новый файл пометили таким образом, поскольку он не содержит модуля. Как только в нем окажется описание какого-нибудь модуля, эта подпапка пропадёт.
|
После того, как Vivado закончит обновлять иерархию (и, если при создании файла вы отказались указывать порты модуля, нажав на кнопку `Cancel`), рядом с папкой `Design Sources` появится стрелка, позволяющая развернуть эту папку, внутри которой обнаружится подпапка `Non-module Files` с созданным нами файлом. Новый файл пометили таким образом, поскольку он не содержит модуля. Как только в нем окажется описание какого-нибудь модуля, эта подпапка пропадёт.
|
||||||
|
|
||||||
Откроем редактор двойным кликом по файлу `max_min.sv` и опишем в нём код, приведённый в листинге 1. В коде _листингов 1-3_ могут содержаться логические ошибки — они запланированы и будут найдены и исправлены в главе "Руководство по поиску и исправлению ошибок".
|
Откроем редактор двойным кликом по файлу `max_min.sv` и опишем в нём код, приведённый в листинге 1. В коде _листингов 1-3_ могут содержаться логические ошибки — они запланированы и будут найдены и исправлены в документе "[Руководство по поиску функциональных ошибок](./05.%20Bug%20hunting.md)".
|
||||||
|
|
||||||
```Verilog
|
```Verilog
|
||||||
module max_min(
|
module max_min(
|
||||||
@@ -166,7 +166,7 @@ endmodule
|
|||||||
|
|
||||||
_Листинг 3. Описание модуля vector\_abs._
|
_Листинг 3. Описание модуля vector\_abs._
|
||||||
|
|
||||||
В `Simulation Sources` добавьте файл tb_vector_abs, описываемый _листингом 4_.
|
В `Simulation Sources` добавьте файл `tb_vector_abs`, описываемый _листингом 4_.
|
||||||
|
|
||||||
```Verilog
|
```Verilog
|
||||||
module tb_vector_abs();
|
module tb_vector_abs();
|
||||||
@@ -251,72 +251,7 @@ _Рисунок 8. Иерархия проекта, представленная
|
|||||||
|
|
||||||
_Рисунок 9. Выбор модуля верхнего уровня (показана середина выпадающего списка)._
|
_Рисунок 9. Выбор модуля верхнего уровня (показана середина выпадающего списка)._
|
||||||
|
|
||||||
Обратите внимание на то, как строится иерархия проекта. Модули, являющиеся объектами других модулей "вложены" в эти модули. Причем в иерархии проекта сперва указывается имя объекта модуля, затем через двоеточие имя самого модуля. В скобках указывается имя файла, где модуль описан. Модуль, который не содержится в других модулях не имеет имени объекта модуля (т.к. нет сущности, которая бы этот объект создавала). Если модуль будет содержать несколько объектов одного и того же модуля, в иерархии будут отображены все эти объекты — именно поэтому нужно понимать, чем иерархия модулей отличается от дерева файлов. Несмотря на то, что модуль описан всего в одном файле, в иерархии проекта может встречаться несколько экземпляров одного и того же модуля.
|
Обратите внимание на то, как строится иерархия проекта. Модули, являющиеся объектами других модулей "вложены" в эти модули. Причем в иерархии проекта сперва указывается имя объекта модуля, затем через двоеточие имя самого модуля. В скобках указывается имя файла, где модуль описан. Модуль, который не содержится в других модулях, не имеет имени объекта модуля (т.к. нет сущности, которая бы этот объект создавала). Если модуль будет содержать несколько объектов одного и того же модуля, в иерархии будут отображены все эти объекты — именно поэтому нужно понимать, чем иерархия модулей отличается от дерева файлов. Несмотря на то, что модуль описан всего в одном файле, в иерархии проекта может встречаться несколько экземпляров одного и того же модуля.
|
||||||
|
|
||||||
Добавьте в `Simulation Sources` файл `tb_vector_abs`, содержимое которого представлено в _листинге 4_.
|
|
||||||
|
|
||||||
```Verilog
|
|
||||||
module tb_vector_abs();
|
|
||||||
|
|
||||||
logic [31:0] a;
|
|
||||||
logic [31:0] b;
|
|
||||||
logic [31:0] res;
|
|
||||||
|
|
||||||
vector_abs dut(
|
|
||||||
.x(a),
|
|
||||||
.y(b),
|
|
||||||
.abs(res)
|
|
||||||
);
|
|
||||||
integer err_count = 0;
|
|
||||||
|
|
||||||
task check_result(input logic [31:0]a, b, res);
|
|
||||||
begin : check_result
|
|
||||||
reg [31:0] ref_res;
|
|
||||||
ref_res = a < b? a/2 + b : a + b/2;
|
|
||||||
if (res !== ref_res) begin
|
|
||||||
$display("Incorrect res at time %0t:", $time);
|
|
||||||
$display("a = %0d, b = %0d", a, b);
|
|
||||||
$display("design res = %0d", res);
|
|
||||||
$display("reference res = %0d", ref_res);
|
|
||||||
$display("------------------");
|
|
||||||
err_count = err_count + 1'b1;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
endtask
|
|
||||||
|
|
||||||
initial begin : test
|
|
||||||
integer i;
|
|
||||||
$timeformat(-9,0,"ns");
|
|
||||||
a = 0; b = 0;
|
|
||||||
#5;
|
|
||||||
check_result(a,b,res);
|
|
||||||
|
|
||||||
|
|
||||||
a = 1; b = 1;
|
|
||||||
#5;
|
|
||||||
check_result(a,b,res);
|
|
||||||
|
|
||||||
a = 3; b = 4;
|
|
||||||
#5;
|
|
||||||
check_result(a,b,res);
|
|
||||||
|
|
||||||
|
|
||||||
for(i = 0; i < 100; i=i+1) begin
|
|
||||||
a = $random()&32'hff; b = $random()&32'hff;
|
|
||||||
#5;
|
|
||||||
check_result(a,b,res);
|
|
||||||
end
|
|
||||||
|
|
||||||
$display("Test has been finished with %d errors", err_count);
|
|
||||||
if(err_count == 0) begin
|
|
||||||
$display("SUCCESS!");
|
|
||||||
end
|
|
||||||
$finish();
|
|
||||||
end
|
|
||||||
endmodule
|
|
||||||
```
|
|
||||||
|
|
||||||
_Листинг 4. Описание модуля tb\_vector\_abs._
|
|
||||||
|
|
||||||
#### Ошибки иерархии
|
#### Ошибки иерархии
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
_Рисунок 1. Запуск симуляции через вкладку `SIMULATION` окна `Flow Navigator`._
|
_Рисунок 1. Запуск симуляции через вкладку `SIMULATION` окна `Flow Navigator`._
|
||||||
|
|
||||||
1. В иерархии проекта нажать по папке `sim_1` правой кнопкой мыши, далее выбрать `Run Simulation` → `Run Behavioral Simulation`.
|
2. В иерархии проекта нажать по папке `sim_1` правой кнопкой мыши, далее выбрать `Run Simulation` → `Run Behavioral Simulation`.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -55,9 +55,9 @@ _Рисунок 3. Окно симуляции._
|
|||||||
4. перезапустить симуляцию (по умолчанию горячей клавиши нет, но может быть добавлена в настройках);
|
4. перезапустить симуляцию (по умолчанию горячей клавиши нет, но может быть добавлена в настройках);
|
||||||
5. закрыть симуляцию.
|
5. закрыть симуляцию.
|
||||||
|
|
||||||
Отличие сброса симуляции от её перезапуска отличается в следующем. При сбросе симуляции очищаются промоделированные значения добавленных на временную диаграмму сигналов (сами сигналы остаются на месте), при этом время симуляции перемещается на нулевую отметку (т.е симуляция начнется заново). Подобное действие может быть необходимо в случае отладки, или же если посреди моделирования вы добавили на временную диаграмму новые сигналы, и хотите увидеть их поведение с самого начала симуляции. При сбросе симуляции не выполняется компиляция исходников (даже если их содержимое было изменено).
|
Отличие сброса симуляции от её перезапуска заключается в следующем. При сбросе симуляции очищаются промоделированные значения добавленных на временную диаграмму сигналов (сами сигналы остаются на месте), при этом время симуляции перемещается на нулевую отметку (т.е симуляция начнется заново). Подобное действие может быть необходимо в случае отладки, или же если посреди моделирования вы добавили на временную диаграмму новые сигналы, и хотите увидеть их поведение с самого начала симуляции. При сбросе симуляции не выполняется компиляция исходников (даже если их содержимое было изменено).
|
||||||
|
|
||||||
Перезапуск симуляции похож на закрытие симуляции и повторное её открытие. При этом, если в исходниках происходили изменения — файлы будут перекомпилированы. Обратите внимание, что Vivado в первую очередь обнаруживает только изменения, сделанные из собственного редактора. В случае, если файлы были изменены извне (в особенности это касается `mem`-файлов, которые начинают использоваться начиная с четвертой лабораторной работы) — Vivado может не обнаружить новых изменений. В случае, если симуляция ранее уже запускалась и с тех пор Vivado не обнаружил изменений в файлах — повторная компиляция, не производится и симуляция запускается средствами уже скомпилированных объектов. В случае, если изменения были сделаны извне, но Vivado их не обнаружил, можно очистить предыдущую сборку нажав правой кнопкой мыши по кнопки `Simulation` в окне `Flow Navigator` и выбрав `Reset Behavioral Simulation` (см. _рис. 4_).
|
Перезапуск симуляции похож на закрытие симуляции и повторное её открытие. При этом, если в исходниках происходили изменения — файлы будут перекомпилированы. Обратите внимание, что Vivado в первую очередь обнаруживает только изменения, сделанные из собственного редактора. В случае, если файлы были изменены извне (в особенности это касается `mem`-файлов, которые начинают использоваться с четвертой лабораторной работы) — Vivado может не обнаружить новых изменений. В случае, если симуляция ранее уже запускалась и с тех пор Vivado не обнаружил изменений в файлах — повторная компиляция не производится, и симуляция запускается средствами уже скомпилированных объектов. В случае, если изменения были сделаны извне, но Vivado их не обнаружил, можно очистить предыдущую сборку, нажав правой кнопкой мыши по `Simulation` в окне `Flow Navigator` и выбрав `Reset Behavioral Simulation` (см. _рис. 4_).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -69,4 +69,4 @@ _Рисунок 4. Сброс файлов симуляции._
|
|||||||
|
|
||||||
> Если вы изменили модуль верхнего уровня в `Simulation Sources`, вам необходимо закрыть текущую симуляцию. Без этого новая не сможет запуститься и будет выдавать ошибку "boost filesystem remove: Процесс не может получить доступ к файлу". Подробнее об этой ошибке рассказано можно прочесть в "[Списке типичных ошибок в Vivado](../Other/FAQ.md)".
|
> Если вы изменили модуль верхнего уровня в `Simulation Sources`, вам необходимо закрыть текущую симуляцию. Без этого новая не сможет запуститься и будет выдавать ошибку "boost filesystem remove: Процесс не может получить доступ к файлу". Подробнее об этой ошибке рассказано можно прочесть в "[Списке типичных ошибок в Vivado](../Other/FAQ.md)".
|
||||||
|
|
||||||
Подробнее о поиске ошибок и работе с временной диаграммой рассказано в главе "Руководство по поиску ошибок".
|
Подробнее о поиске ошибок и работе с временной диаграммой рассказано в главе "[Руководство по поиску функциональных ошибок](05.%20Bug%20hunting.md)".
|
||||||
|
|||||||
@@ -31,13 +31,13 @@
|
|||||||
|
|
||||||
1. Обычно всё начинается с сообщения в логе тестов (никто не проверяет глазами временную диаграмму сложных проектов, состоящую из тысяч сигналов, меняющихся миллионы раз за микросекунду), но на наших лабораторных работах с относительно простыми модулями, этот шаг иногда может быть и пропущен.
|
1. Обычно всё начинается с сообщения в логе тестов (никто не проверяет глазами временную диаграмму сложных проектов, состоящую из тысяч сигналов, меняющихся миллионы раз за микросекунду), но на наших лабораторных работах с относительно простыми модулями, этот шаг иногда может быть и пропущен.
|
||||||
Сообщение в логе обычно содержит следующую ключевую информацию: имя сигнала, на котором установилось неверное значение, и время, когда это произошло. Чем лучше написано верификационное окружение, тем больше ключевой информации будет отражено в сообщении, поэтому его написание является своего рода искусством.
|
Сообщение в логе обычно содержит следующую ключевую информацию: имя сигнала, на котором установилось неверное значение, и время, когда это произошло. Чем лучше написано верификационное окружение, тем больше ключевой информации будет отражено в сообщении, поэтому его написание является своего рода искусством.
|
||||||
1. Получив имя сигнала и время, мы отправляемся на временную диаграмму и проверяем нашу ошибку. Как это сделать? Необходимо определить по коду, какие сигналы и каким образом управляют нашим сигналом. Вариантов может быть несколько:
|
2. Получив имя сигнала и время, мы отправляемся на временную диаграмму и проверяем нашу ошибку. Как это сделать? Необходимо определить по коду, какие сигналы и каким образом управляют нашим сигналом. Вариантов может быть несколько:
|
||||||
1. Управляющие сигналы имеют корректное значение, но логика, по которой они управляют сигналом неверна, из-за этого на нем возникает неверное значение.
|
1. Управляющие сигналы имеют корректное значение, но логика, по которой они управляют сигналом неверна, из-за этого на нем возникает неверное значение.
|
||||||
Это идеальный случай, при возникновении которого мы сразу же находим причину проблемы и исправляем ее.
|
Это идеальный случай, при возникновении которого мы сразу же находим причину проблемы и исправляем ее.
|
||||||
2. Логика управления верна, а какая-то часть управляющих сигналов имеет неверное значение (пусть для примера, неверное значение будет на управляющем сигнале `X`). Это означает, что обнаруженное несоответствие сигналов является уже следствием какой-то ошибки, и мы должны вернуться к шагу 2, проверяя источники для сигнала со значением `X`. Так происходит до тех пор, пока мы не попадаем в тип 1.
|
2. Логика управления верна, а какая-то часть управляющих сигналов имеет неверное значение (пусть для примера, неверное значение будет на управляющем сигнале `X`). Это означает, что обнаруженное несоответствие сигналов является уже следствием какой-то ошибки, и мы должны вернуться к шагу 2, проверяя источники для сигнала со значением `X`. Так происходит до тех пор, пока мы не попадаем в тип 1.
|
||||||
3. Логика управления и значения управляющих сигналов верны. Это самый сложный тип ошибок, который заключается либо в ошибке в спецификации разрабатываемого устройства, либо в САПРе или компонентах, влияющих на его работу. В рамках данного курса вас не должны заботить данные ошибки, и при их возникновении вам стоит обратиться к преподавателю (предварительно убедившись, что ошибка совершенно точно не подходит под первые два варианта).
|
3. Логика управления и значения управляющих сигналов верны. Это самый сложный тип ошибок, который заключается либо в ошибке в спецификации разрабатываемого устройства, либо в САПРе или компонентах, влияющих на его работу. В рамках данного курса вас не должны заботить данные ошибки, и при их возникновении вам стоит обратиться к преподавателю (предварительно убедившись, что ошибка совершенно точно не подходит под первые два варианта).
|
||||||
4. Любая возможная комбинация всех предыдущих типов.
|
4. Любая возможная комбинация всех предыдущих типов.
|
||||||
2. Обнаружив первопричину ошибки, мы исправляем ее (возможно дополняя набор тестов, или внеся правки в спецификацию), и повторно запускаем все тесты, чтобы убедиться в двух вещах:
|
3. Обнаружив первопричину ошибки, мы исправляем ее (возможно дополняя набор тестов, или внеся правки в спецификацию), и повторно запускаем все тесты, чтобы убедиться в двух вещах:
|
||||||
1. ошибка действительно исправлена
|
1. ошибка действительно исправлена
|
||||||
2. исправление ошибки не породило новых ошибок
|
2. исправление ошибки не породило новых ошибок
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ _Рисунок 2. Пример конкретной ошибки в тесте.
|
|||||||
|
|
||||||
## Поиск ошибки на временной диаграмме
|
## Поиск ошибки на временной диаграмме
|
||||||
|
|
||||||
Давайте найдем это место на временной диаграмме. Обычно, сразу после запуска симуляции на временной диаграмме отображено место, где симуляция остановилась (возможно с очень неподходящим масштабом). Для начала подгоним масштаб таким образом, чтобы вся временная диаграмма умещалась в окне. Это делается либо нажатием правой кнопкой мыши по в области отображения сигналов, с выбором "Full View" во всплывающем меню, либо нажатием соответствующей кнопки на панели временной диаграммы (см. _рис. 4_), либо нажатием комбинации клавиш `Ctrl+0`. Затем найдем приблизительное место рядом с тем временем, что нас интересует, установим там курсор, и приблизим масштаб (покрутив колесиком мыши при зажатой клавише `Ctrl`), периодически уточняя местоположения курсора, пока не найдем интересующее нас место.
|
Давайте найдем это место на временной диаграмме. Обычно, сразу после запуска симуляции на временной диаграмме отображено место, где симуляция остановилась (возможно с очень неподходящим масштабом). Для начала подгоним масштаб таким образом, чтобы вся временная диаграмма умещалась в окне. Это делается либо нажатием правой кнопкой мыши по области отображения сигналов, с выбором "Full View" во всплывающем меню, либо нажатием соответствующей кнопки на панели временной диаграммы (см. _рис. 4_), либо нажатием комбинации клавиш `Ctrl+0`. Затем найдем приблизительное место рядом с тем временем, что нас интересует, установим там курсор, и приблизим масштаб (покрутив колесиком мыши при зажатой клавише `Ctrl`), периодически уточняя местоположения курсора, пока не найдем интересующее нас место.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -259,7 +259,7 @@ _Рисунок 19. Результат моделирования после и
|
|||||||
|
|
||||||
В логе сообщается о 102 найденных ошибках. Ровно на одну ошибку меньше, чем было ранее. Это не означает, что в проекте осталось 102 ошибки, только то, что, исправив данную ошибку — мы действительно что-то исправили, и один из тестовых сценариев, который ранее завершался ошибкой, теперь завершился без неё.
|
В логе сообщается о 102 найденных ошибках. Ровно на одну ошибку меньше, чем было ранее. Это не означает, что в проекте осталось 102 ошибки, только то, что, исправив данную ошибку — мы действительно что-то исправили, и один из тестовых сценариев, который ранее завершался ошибкой, теперь завершился без неё.
|
||||||
|
|
||||||
Помните, что если в проекте много ошибок, то часть ошибок может выправлять поведение других ошибок (хоть и не всегда, но иногда минус на минус может выдать плюс контексте ошибок проекта), поэтому надо с осторожностью полагаться на число найденных ошибок, если это число больше нуля.
|
Помните, что если в проекте много ошибок, то часть ошибок может выправлять поведение других ошибок (хоть и не всегда, но иногда минус на минус может выдать плюс в контексте ошибок проекта), поэтому надо с осторожностью полагаться на число найденных ошибок, если это число больше нуля.
|
||||||
|
|
||||||
Посмотрим на нашу временную диаграмму снова, и выберем дальнейшие действия:
|
Посмотрим на нашу временную диаграмму снова, и выберем дальнейшие действия:
|
||||||
|
|
||||||
@@ -303,7 +303,7 @@ _Рисунок 21. Первая ошибка в новом логе модел
|
|||||||
assign abs = max + min_half;
|
assign abs = max + min_half;
|
||||||
```
|
```
|
||||||
|
|
||||||
Выход `abs` зависит от двух внутренних сигналов: max и `min_half`. В соответствии с нашим алгоритмом, либо проблема в логике, связывающей эти два сигнала (операции сложения), либо в значении какого-то из этих сигналов, либо комбинации этих вариантов.
|
Выход `abs` зависит от двух внутренних сигналов: `max` и `min_half`. В соответствии с нашим алгоритмом, либо проблема в логике, связывающей эти два сигнала (операции сложения), либо в значении какого-то из этих сигналов, либо комбинации этих вариантов.
|
||||||
|
|
||||||
Изучив модуль, мы понимаем, что в логике этого присваивания проблем нет, т.к. оно повторяет логику формулы `max + min/2`, складывая максимум с половиной минимума. Значит проблема в значении какого-то из этих сигналов (или обоих из них). Посчитаем значения этих сигналов самостоятельно (для сложного проекта эти значения посчитала бы модель):
|
Изучив модуль, мы понимаем, что в логике этого присваивания проблем нет, т.к. оно повторяет логику формулы `max + min/2`, складывая максимум с половиной минимума. Значит проблема в значении какого-то из этих сигналов (или обоих из них). Посчитаем значения этих сигналов самостоятельно (для сложного проекта эти значения посчитала бы модель):
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user