mirror of
https://github.com/MPSU/APS.git
synced 2025-09-15 17:20:10 +00:00
426 lines
45 KiB
Markdown
426 lines
45 KiB
Markdown
# Руководство по поиску и исправлению ошибок в проекте
|
||
|
||
## Цель
|
||
|
||
При выполнении лабораторных работ вы непременно будете сталкиваться с множеством ошибок. И это нормально: **"Не ошибается тот, кто ничего не делает" — © Джейсон Стейтем**.
|
||
Важно воспитать в себе положительное восприятие обнаружения ошибок (ведь это приводит к улучшению вашего творения). Если относиться к обнаружению ошибок отрицательно, то вы подсознательно будете пытаться найти ошибки спустя рукава, но, если вы "в домике", и ошибок не видите — это не значит, что их нет.
|
||
|
||
При должном отношении, поиск ошибок может превратиться в увлекательное детективное расследование, где у вас есть "место преступления" (обнаруженное несоответствие в поведении, обычно это не сама ошибка, а ее следствие, круги на воде) и какой-то "набор улик" (фрагменты лога, исходный код). И вы по чуть-чуть будете разматывать "нераспутываемую паутину лжи", получая все новые улики, ведущие к истинной ошибке.
|
||
|
||
Этот документ посвящен практикуму по поискам подобных ошибок в **SystemVerilog**-коде.
|
||
|
||
> Обратите внимание на то, как ставится ударение в словосочетании "временна́я диаграмма" (не "вре́менная"). В обиходе это словосочетание заменяется словом "времянка".
|
||
|
||
- [Руководство по поиску и исправлению ошибок в проекте](#руководство-по-поиску-и-исправлению-ошибок-в-проекте)
|
||
- [Цель](#цель)
|
||
- [Алгоритм поиска ошибок](#алгоритм-поиска-ошибок)
|
||
- [Работа с логом при появлении ошибок](#работа-с-логом-при-появлении-ошибок)
|
||
- [Поиск ошибки на временной диаграмме](#поиск-ошибки-на-временной-диаграмме)
|
||
- [Открытие файла исходного кода проблемного сигнала](#открытие-файла-исходного-кода-проблемного-сигнала)
|
||
- [Добавление сигналов объектов на временную диаграмму](#добавление-сигналов-объектов-на-временную-диаграмму)
|
||
- [Сброс симуляции и ее повтор, установка времени моделирования](#сброс-симуляции-и-ее-повтор-установка-времени-моделирования)
|
||
- [Исправление сигналов с Z-состоянием](#исправление-сигналов-с-z-состоянием)
|
||
- [Поиск ошибки в сигналах, формирующих проблемный сигнал](#поиск-ошибки-в-сигналах-формирующих-проблемный-сигнал)
|
||
- [Исправление логики проблемного сигнала](#исправление-логики-проблемного-сигнала)
|
||
- [Проблема необъявленных сигналов](#проблема-необъявленных-сигналов)
|
||
- [Самостоятельная работа](#самостоятельная-работа)
|
||
|
||
## Алгоритм поиска ошибок
|
||
|
||
1. Обычно всё начинается с сообщения в логе тестов (никто не проверяет глазами временную диаграмму сложных проектов, состоящую из тысяч сигналов, меняющихся миллионы раз за микросекунду), но на наших простых лабах, этот шаг иногда может быть и пропущен.
|
||
Сообщение в логе обычно содержит следующую ключевую информацию: имя сигнала, на котором установилось неверное значение, и время когда это произошло. Чем лучше написаны тесты, тем больше ключевой информации будет отражено в сообщении, поэтому написание тестов является своего рода искусством.
|
||
2. Получив имя сигнала и время, мы отправляемся на временную диаграмму и проверяем нашу ошибку. Как это сделать? Необходимо определить по коду, какие сигналы и каким образом управляют нашим сигналом. Вариантов может быть несколько:
|
||
1. Управляющие сигналы имеют корректное значение, но логика, по которой они управляют сигналом неверна, из-за этого на нем возникает неверное значение.
|
||
Это идеальный случай, при возникновении которого мы сразу же находим причину проблемы и исправляем ее.
|
||
2. Логика управления верна, а какая-то часть управляющих сигналов имеет неверное значение (пусть для примера, неверное значение будет на управляющем сигнале `X`). Это означает, что обнаруженное несоответствие сигналов является уже следствием какой-то ошибки, и мы должны вернуться к шагу 2, проверяя источники сигналов для сигнала `X`. Так происходит до тех пор, пока мы не попадаем в тип 1.
|
||
3. Логика управления и значения управляющих сигналов верны. Это самый сложный тип ошибок, который заключается либо в ошибке в спецификации разрабатываемого устройства, либо в САПРе или компонентах, влияющих на его работу. В рамках данного курса вас не должны заботить данные ошибки, и при их возникновении вам стоит обратиться к преподавателю (предварительно убедившись, что ошибка совершенно точно не подходит под первые два варианта).
|
||
4. Любая возможная комбинация всех предыдущих типов.
|
||
3. Обнаружив первопричину ошибки, мы исправляем ее (возможно дополняя набор тестов, или внеся правки в спецификацию), и повторно запускаем все тесты, чтобы убедиться в двух вещах:
|
||
1. ошибка действительно исправлена
|
||
2. исправление ошибки не породило новых ошибок
|
||
|
||
Давайте отработаем эти шаги на примере отладки ошибок в проекте по [вычислению приблизительной длины вектора](../Other//vector_abs/).
|
||
|
||
## Работа с логом при появлении ошибок
|
||
|
||
После запуска симуляции мы видим в логе множество ошибок:
|
||
|
||

|
||
|
||
_Рисунок 1. Пример сообщения об ошибках в тесте._
|
||
|
||
В любой ситуации с множеством ошибок, сначала надо разбираться с самой первой из них, поскольку она может быть причиной появления всех остальных. Поэтому листаем лог до момента первой ошибки:
|
||
|
||

|
||
|
||
_Рисунок 2. Пример конкретной ошибки в тесте._
|
||
|
||
В логе сказано, что в момент времени `5ns`, на дизайн подавались координаты вектора, равные `0` и `0`, модель посчитала, что длина вектора равна нулю, в то время как дизайн вернул значение `x`.
|
||
|
||
## Поиск ошибки на временной диаграмме
|
||
|
||
Давайте найдем это место на временной диаграмме. Обычно, сразу после запуска симуляции на временной диаграмме отображено место, где симуляция остановилась (возможно с очень неподходящим масштабом). Для начала подгоним масштаб таким образом, чтобы вся временная диаграмма умещалась в окне. Это делается либо нажатием правой кнопкой мыши по в области отображения сигналов, с выбором "Full View" во всплывающем меню, либо нажатием соответствующей кнопки на панели временной диаграммы (см. _рис. 4_), либо нажатием комбинации клавиш `Ctrl+0`. Затем найдем приблизительное место рядом с тем временем, что нас интересует, установим там курсор, и приблизим масштаб (покрутив колесиком мыши при зажатой клавише `Ctrl`), периодически уточняя местоположения курсора, пока не найдем интересующее нас место.
|
||
|
||

|
||
|
||
_Рисунок 3. Пример временной диаграммы сразу поле остановки моделирования._
|
||
|
||

|
||
|
||
_Рисунок 4. Пример установки масштаба временной диаграммы таким образом, чтобы та помещалась в текущем окне._
|
||
|
||

|
||
|
||
_Рисунок 5. Пример временной диаграммы после подгонки масштаба._
|
||
|
||

|
||
|
||
_Рисунок 6. Установка курсора в начало моделирования, чтобы, при увеличении масштаба, временная диаграмма сходилась к началу._
|
||
|
||

|
||
|
||
_Рисунок 7. Временная диаграмма, отмасштабированная к времени ошибки с рис. 2._
|
||
|
||
Мы видим ровно ту информацию, которую нам предоставил тестбенч. Теперь надо разобраться в причинах возникновения X-состояния. Такое может произойти в двух ситуациях: какой-то из сигналов, формирующих этот находится в `X` или `Z` состоянии, либо же два каких-то сигнала одновременно пытаются выставить разные значения (подобный вариант встречается куда реже и в цикле ваших лабораторных вряд ли встретится).
|
||
|
||
## Открытие файла исходного кода проблемного сигнала
|
||
|
||
В любом случае, первым делом необходимо определить, источник формирования значения сигнала `res`. Для этого, откроем файл с исходным кодом, где определен данный сигнал. Для этого, нажмем правой кнопкой мыши по имени сигнала на временной диаграмме, и выберем `Go To Source Code`:
|
||
|
||

|
||
|
||
_Рисунок 8. Переход к месту объявления "проблемного" сигнала._
|
||
|
||
Открывается следующий код (с курсором на строчке `wire [31:0] res;`):
|
||
|
||
```SystemVerilog
|
||
module tb();
|
||
|
||
reg [31:0] a;
|
||
reg [31:0] b;
|
||
wire [31:0] res;
|
||
|
||
vector_abs dut(
|
||
.x(a),
|
||
.y(b),
|
||
.abs(res)
|
||
);
|
||
//...
|
||
```
|
||
|
||
Выделив `res` мы видим, что у нас подсветился `res` в строке `abs(res)`. Это означает, что мы завели наш провод внутрь объекта `dut` модуля `vector_abs`, и у нас проблема второго типа (X-состояние передалось от выхода `abs` модуля `vector_abs` проводу `res` модуля `tb`).
|
||
|
||
В этом можно убедиться, если вытащить сигналы модуля `vector_abs` на временную диаграмму. Чтобы это сделать, надо переключиться на окно `Scope`, где размещена иерархия объектов нашего тестбенча
|
||
|
||
## Добавление сигналов объектов на временную диаграмму
|
||
|
||
> Обратите внимание, что в иерархии окна `Scope` находятся не имена модулей, а имена сущностей модуля. В приведенном выше листинге кода мы создали сущность модуля `vector_abs` с именем `dut`, поэтому в иерархии `Scope` мы видим внутри модуля верхнего уровня объект `dut` (не `vector_abs`), так будет и со всеми вложенными объектами.
|
||
|
||
Выделим объект `dut`. В окне `Objects` справа отобразятся все внутренние сигналы (входы/выходы, внутренние провода и регистры) объекта `dut`:
|
||
|
||

|
||
|
||
_Рисунок 9. Отображение внутренних сигналов проверяемого модуля._
|
||
|
||
Вообще говоря, мы уже видим, что выход `abs` (к которому подключен наш провод `res`) находится в X-состоянии, но для отработки навыков, разберемся с добавлением на временную диаграмму. Можно поступить двумя способами:
|
||
|
||
1. Добавить все сигналы (то, что видно в окне `Objects` на временную диаграмму) из окна `Scope` для этого, либо перетаскиваем нужный нам объект, зажав левую кнопку мыши на временную диаграмму, либо жмем правой кнопкой мыши по нужному объекту, и выбираем `Add to Wave Window`
|
||
2. Добавить отдельные сигналы из окна `Objects`. Для этого выделяем их (возможно множественное выделение через модификаторы `shift` или `ctrl`), и как и в прошлом случае, либо перетаскиваем сигналы левой кнопкой мыши, либо добавляем их через правую кнопку мыши.
|
||
|
||

|
||
|
||
_Рисунок 10. Добавление сигналов модуля на временную диаграмму._
|
||
|
||

|
||
|
||
_Рисунок 11. Результат добавления сигналов модуля на временную диаграмму._
|
||
|
||
По мере роста сложности проекта, число сигналов на временной диаграмме будет постоянно расти, в связи с чем встает вопрос группировки сигналов.
|
||
|
||
Для того чтобы объединить сигналы в группу, необходимо их выделить. Это можно сделать двумя способами:
|
||
|
||
1. "прокликав" интересующие сигналы при зажатой клавише `Ctrl`;
|
||
2. если речь идет о диапазоне сигналов, можно выбрать сигнал с одного края, после чего, при зажатой клавише `Shift`, выбрать сигнал с другого края этого диапазона.
|
||
|
||
После выбора, необходимо нажать правой кнопкой мыши по выделенным сигналам, и в низу выпадающего списка выбрать `New Group`.
|
||
|
||

|
||
|
||
_Рисунок 12. Пример создания группы сигналов (контекстное меню было обрезано для удобства отображения)._
|
||
|
||
После создания группы, ей нужно будет дать имя. В случае, если все сигналы принадлежат одному модулю, удобно называть группу сигналов именем этого модуля.
|
||
|
||

|
||
|
||
_Рисунок 13. Пример созданной группы сигналов._
|
||
|
||
Данну группу можно сворачивать и разворачивать, нажимая на соответствующую стрелку слева от имени группы.
|
||
|
||
> Обратите внимание, что часть сигналов отображают какое-то значение (сигнал `abs` отображает X-состояние), а часть не отображают ничего. Так произошло, потому что провод `abs` **непрерывно связан** с проводом `res`, с точки зрения симулятора это одна сущность, и записывая во время моделирования значения для сигнала `res`, симулятор неявно записывал значения для сигнала `abs`, чего не скажешь про остальные сигналы, которых не было во время моделирования на временной диаграмме.
|
||
|
||
## Сброс симуляции и ее повтор, установка времени моделирования
|
||
|
||
Для того, чтобы получить отсутствующие значения, необходимо повторить моделирование. Для этого, необходимо сбросить время моделирования в 0 и запустить его снова.
|
||
|
||
Для этого, необходимо на панели симуляции нажать кнопку `Restart` (`|◀`), а затем кнопку `Run all` (`▶`) или `Run for` (`▶t`). Положение кнопок в окне Vivado иллюстрирует _рис. 14_.
|
||
|
||

|
||
|
||
_Рисунок 14. Расположение кнопок, управляющих моделированием в окне Vivado._
|
||
|
||
Панель управления симуляции с кнопками:
|
||
|
||
1. `Restart`, горячие клавиши: `Ctrl+Shift+F5`;
|
||
2. `Run all`, горячая клавиша: `F3`;
|
||
3. `Run for`, горячие клавиши: `Shift+F2`;
|
||
4. `Relaunch Simulation`.
|
||
|
||
`Run for` выполняет моделирование указанного количества времени, после чего моделирование приостанавливается. Моделирование может быть остановлено так же и вручную, либо вызовом соответствующей инструкции из кода теста.
|
||
|
||
`Run all` отличается от `Run for` тем, что в качестве количества моделируемого времени указывается "бесконечность", и моделирование будет остановлено только вручную, либо вызовом соответствующей инструкции.
|
||
|
||
> Обратите внимание, что для добавления недостающих значений добавленных сигналов лучше всего выполнять описанную выше инструкцию. Аналогичного результата можно добиться и нажатием на кнопку `Relaunch Simulation`, однако эта команда запускает повторную компиляцию и запуск симуляции, что для крупных проектов выльется в потерю времени на излишнюю компиляцию.
|
||
|
||
Кроме того, чтобы курсор и лог снова не ушли далеко от места первой ошибки, можно сразу указать, необходимое нам время моделирования перед выполнением команды `Run for`: `5ns`.
|
||
|
||

|
||
|
||
_Рисунок 15. Пример моделирования 5ns._
|
||
|
||
На _рис. 16_ представлен результат моделирования с новыми сигналами.
|
||
|
||

|
||
|
||
_Рисунок 16. Результат повторного моделирования после добавления на временную диаграмму новых сигналов._
|
||
|
||
Видим два сигнала в Z-состоянии и один сигнал в X-состоянии. Обычно, сигналы с Z-состоянием проще всего исправить, т.к. зачастую это забытое или некорректное подключение провода. Кроме того, сигнал, зависящий от сигнала с Z-состоянием, может оказаться в X-состоянии, так что это может быть решением нашей проблемы, поэтому займемся проводами `min` и `min_half`. Сперва займемся сигналом `min` и перейдем к шагу 2 нашего алгоритма (нажимаем правой кнопкой мыши и выбираем `Go To Source Code`):
|
||
|
||
```SystemVerilog
|
||
module vector_abs(
|
||
input [31:0] x,
|
||
input [31:0] y,
|
||
output[31:0] abs
|
||
);
|
||
|
||
|
||
wire [31:0] min;
|
||
wire [31:0] min_half;
|
||
|
||
max_min max_min_unit(
|
||
.a(x),
|
||
.b(y),
|
||
.max(max),
|
||
.min(min)
|
||
);
|
||
```
|
||
|
||
## Исправление сигналов с Z-состоянием
|
||
|
||
Мы видим, что сигнал `min` подключен к выходу `min` объекта `max_min_unit` модуля `max_min`. Добавим сигналы этого модуля на временную диаграмму. Для этого, необходимо раскрыть список объектов, содержащихся в объекте `dut` иерархии объектов `Scope` и выбрать там объект `max_min_unit`.
|
||
|
||

|
||
|
||
_Рисунок 17. Добавление сигналов вложенных модулей на временную диаграмму._
|
||
|
||
Добавляем внутренние сигналы на временную диаграмму, группируем их под именем `max_min`, и повторяем моделирование.
|
||
|
||

|
||
|
||
_Рисунок 18. Результат добавления и группировки сигналов подмодуля `max_min`._
|
||
|
||
Произошло что-то странное: все внутренние сигналы объекта `max_min_unit` "зеленые" (не имеющие X или Z состояния), однако подключенный к выходу этого модуля сигнал `min` находится в Z-состоянии. Как такое могло произойти?
|
||
|
||
Если присмотреться к сигналу `min`, находящемуся в Z-состоянии, можно заметить, что младшая цифра находится не в Z-состоянии, а в состоянии `0`, такое же значение стоит и на сигнале `min` объекта `max_min_unit`. Это интересно.
|
||
|
||
Если присмотреться к этим двум сигналам еще пристальней, то можно увидеть, что у сигнала `min` объекта `dut` разрядность 32 бита, в то время как разрядность сигнала `min` объекта `max_min_unit` составляет 4 бита.
|
||
|
||
Это и является проблемой: мы подключили 4 бита сигнала 4-разрядного сигнала `min` к младшим 4 битам 32-разрядного сигнала `min`, а остальные разряды остались не подключенными.
|
||
|
||
По всей видимости, при написании модуля `max_min`, была указана неверная разрядность сигнала `min`, вместо `31` было написано `3`. Исправим это и повторим моделирование.
|
||
|
||
> Обратите внимание, что поскольку мы изменили исходный код, в этот раз необходимо нажать на кнопку `Relaunch Simulation`, поскольку нужна повторная компиляция проекта.
|
||
|
||

|
||
|
||
_Рисунок 19. Результат моделирования после исправления разрядности сигнала `min`._
|
||
|
||
В логе сообщается о 102 найденных ошибках. Ровно на одну ошибку меньше, чем было ранее. Это не означает, что в проекте осталось 102 ошибки, только то, что, исправив данную ошибку — мы действительно что-то исправили, и один из тестовых сценариев, который ранее завершался ошибкой, теперь завершился без нее.
|
||
|
||
Помните, что если в проекте много ошибок, то часть ошибок может выправлять поведение других ошибок (хоть и не всегда, но иногда минус на минус может выдать плюс контексте ошибок проекта), поэтому надо осторожно полагаться на число найденных ошибок, если их больше нуля.
|
||
|
||
Посмотрим на нашу временную диаграмму снова, и выберем дальнейшие действия:
|
||
|
||

|
||
|
||
_Рисунок 20. Временная диаграмма после исправления разрядности сигнала `min`._
|
||
|
||
Мы видим, что на временной диаграмме не осталось сигналов в X или Z-состоянии, а значит мы собрали все "низковисящие" улики нашего с вами расследования. Вернемся к месту преступления и попробуем поискать новые улики:
|
||
|
||

|
||
|
||
_Рисунок 21. Первая ошибка в новом логе моделирования._
|
||
|
||
## Поиск ошибки в сигналах, формирующих проблемный сигнал
|
||
|
||
Мы видим, что первой ошибкой в логе стала не та ошибка, что была прежде. Раньше первый неверный результат мы видели в момент времени `5ns`, когда на дизайн подавались значения `0` и `0`, теперь же первой ошибкой стал момент времени `10ns`, когда на дизайн подаются значения `1` и `1`. Наше устройство считает, что результат должен равняться `3`, в то время как модель считает, что результат должен равняться `1`. Проверим, нет ли ошибки в модели и посчитаем результат самостоятельно:
|
||
|
||
Для определения приблизительной длины вектора в евклидовом пространстве(вычисления квадратного корня из суммы квадратов / длины гипотенузы прямоугольного треугольника) можно воспользоваться формулой:
|
||
|
||
`sqrt(a^2 + b^2) ≈ max + min/2`, где `max` и `min` — большее и меньшее из пары чисел соответственно [**Ричард Лайонс: Цифровая обработка сигналов, Глава 13.2, стр. 475**].
|
||
|
||
Подставим наши числа в формулу (поскольку оба числа равны, не важно какое из них будет максимумом, а какое минимумом):
|
||
|
||
```text
|
||
1 + 1/2 = 1.5
|
||
```
|
||
|
||
Ни модель, ни дизайн не правы?
|
||
|
||
На самом деле, наше устройство поддерживает только целочисленную арифметику, поэтому результат будет:
|
||
|
||
```text
|
||
1 + 1/2 = 1 + 0 = 1
|
||
```
|
||
|
||
Модель правильно отразила особенность нашего устройства и дала корректный результат.
|
||
|
||
Значит надо смотреть как формируется результат в нашем устройстве, посмотрим на выход `abs` в модуле `vector_abs`:
|
||
|
||
```SystemVerilog
|
||
assign abs = max + min_half;
|
||
```
|
||
|
||
Выход `abs` зависит от двух внутренних сигналов: max и `min_half`. В соответствии с нашим алгоритмом, либо проблема в логике, связывающей эти два сигнала (операции сложения), либо в значении какого-то из этих сигналов, либо комбинации этих вариантов.
|
||
|
||
Изучив модуль, мы понимаем, что в логике этого присваивания проблем нет, т.к. оно повторяет логику формулы `max + min/2`, складывая максимум с половиной минимума. Значит проблема в значении какого-то из этих сигналов (или обоих из них). Посчитаем значения этих сигналов самостоятельно (для сложного проекта эти значения бы посчитала модель):
|
||
`1` и `0`.
|
||
|
||
Смотрим, какие значения установлены на сигналах `max` и `min_half` в момент времени `10ns`.
|
||
|
||

|
||
|
||
_Рисунок 22. Значения сигналов `max` и `min_half` в момент времени `10 ns`
|
||
|
||
> Обратите внимание: вы можете менять и цвета сигналов временной диаграммы через контекстное меню выделенных сигналов.
|
||
|
||
Мы видим, что в момент времени `10 ns` значения `max` и `min_half` изменились ак `1 -> 4` и `2 -> 8` соответственно. Нас интересуют значения `1` и `2`, т.к. в момент времени `10ns` на выходе дизайна в этот момент был установившийся результат для предыдущих значений (еще не успел посчитаться результат для новых значений).
|
||
|
||
Значение `max=1` совпадает с ожидаемым, в то время как `min_half=2` явно нет.
|
||
|
||
Мы нашли причину неправильного вычисления результата: и правда, `1+2=3`, теперь необходимо найти ошибку в вычислении сигнала `min_half`.
|
||
|
||
Как и с сигналом `abs`, необходимо определить сигналы, влияющие на значение сигнала `min_half`. Данный сигнал подключен к выходу `quotient` модуля `half_divider`, поэтому мы будем смотреть исходный код данного модуля:
|
||
|
||
```SystemVerilog
|
||
module half_divider(
|
||
input [31:0] numerator,
|
||
output[31:0] quotient
|
||
);
|
||
|
||
assign quotient = numerator << 1'b1;
|
||
|
||
endmodule
|
||
```
|
||
|
||
Что делает данный модуль? Он принимает на вход значение и делит его на два. На вход данного модуля будет приходить значение минимума из нашей формулы.
|
||
|
||
Выход данного модуля зависит от входа `numerator` и логики сдвига влево на 1. Это значит, что проблема либо в логике, либо в значении, подаваемом на вход. Выведем сигнал `numerator` на временную диаграмму и посмотрим на его значение в момент времени `10ns.
|
||
|
||

|
||
|
||
_Рисунок 23. Значение сигнала `numerator` в момент времени `10 ns`._
|
||
|
||
Мы помним, что в момент, когда дизайн начал выдавать неправильный результат, на его входы подавались числа `1` и `1`, это значит, что на вход `numerator` пришло корректное значение: минимум из этих двух чисел и правда равен `1`. Проверим логику данного модуля.
|
||
|
||
## Исправление логики проблемного сигнала
|
||
|
||
Операция деления в цифровой схемотехнике является очень "дорогой" в плане ресурсов логических блоков и критического пути, поэтому этой операции часто стараются избегать. В нашем случае, нам не нужно обычное деление — нам нужно деление только напополам. В двоичной арифметике, для того чтобы разделить число на два, достаточно отбросить его младшую цифру. Вы часто пользуетесь подобной операцией в повседневной жизни при выполнении операции деления на 10: отбрасываете младшую цифру в десятичной арифметике.
|
||
|
||
Именно поэтому, когда мы в первый раз пытались посчитать результат "на бумаге", у нас было расхождение с моделью: когда мы делим 1 на 2, мы получаем 0.5, однако деление путем отбрасывания цифры округляет результат вниз (1/2=0, 15/10=1).
|
||
|
||
Как "отбросить" цифру средствами цифровой логики? Для этого используется операция сдвига вправо.
|
||
|
||
Операция сдвига вправо в **SystemVerilog** записывается оператором `>>`. Справа от оператора указывается число "отбрасываемых цифр", в нашем случае одна. Но постойте, в логике присваивания стоит оператор `<<`. Это ошибка, исправим ее!
|
||
|
||
Повторяем моделирование.
|
||
|
||

|
||
|
||
_Рисунок 24. Результат моделирования после исправления оператора сдвига._
|
||
|
||
Снова на одну ошибку меньше. Не унываем, вряд ли в проекте число ошибок больше, чем число непустых строк самого проекта. Возвращаемся к начальной ошибке:
|
||
|
||

|
||
|
||
_Рисунок 25. Первая ошибка в повторном моделировании._
|
||
|
||
Мы продвинулись во времени безошибочного моделирования до `15 ns`, начинаем наше расследование с начала:
|
||
|
||
На вход дизайна подаются значения `3` и `4`, дизайн считает, что результатом вычисления `max + min/2` будет `2`, модель считает, что `5`. Посчитаем сами:
|
||
|
||
```text
|
||
max=4
|
||
min=3
|
||
max + min/2 = 4 + 3/2 = 4 + 1 = 5
|
||
```
|
||
|
||
И снова модель выдала правильный результат. Разберемся в значениях сигналов, формирующих сигнал `abs`.
|
||
|
||
## Проблема необъявленных сигналов
|
||
|
||
К этому моменту на вашей временной диаграмме скорей всего стало уже очень много сигналов. Уберем лишние, оставив только внутренние сигналы модуля `vector_abs` (для этого выделяем не нужные сигналы, и удаляем их с помощью клавиши `Delete`).
|
||
|
||

|
||
|
||
_Рисунок 26. Поведение внутренних сигналов модуля `vector_abs` на временной диаграмме._
|
||
|
||
В глаза сразу же бросается, что сигнал `max` внешне отличается от всех остальных — он ведет себя как однобитный сигнал. Если все остальные сигналы 32-разрядные, то и сигнал `max` должен быть таким же. Перейдем к объявлению этого сигнала, чтобы это исправить (нажав правой кнопкой мыши, и выбрав `Go To Source Code`):
|
||
|
||
```SystemVerilog
|
||
module vector_abs(
|
||
input [31:0] x,
|
||
input [31:0] y,
|
||
output[31:0] abs
|
||
);
|
||
|
||
|
||
wire [31:0] min;
|
||
wire [31:0] min_half;
|
||
|
||
max_min max_min_unit(
|
||
.a(x),
|
||
.b(y),
|
||
.max(max),
|
||
.min(min)
|
||
);
|
||
//...
|
||
```
|
||
|
||
Это странно, курсор был установлен на строку `.max(max)`, хотя раньше в этом случае курсор устанавливался на строку, где объявлялся выбранный сигнал. Но вот в чем дело, если мы просмотрим файл внимательно, то не обнаружим объявления сигнала вовсе. Как так вышло, что мы использовали необъявленный сигнал, а САПР не выдал нам ошибку? Дело в том, что стандарт [IEEE 1364-2005](https://ieeexplore.ieee.org/document/1620780) для языка **SystemVerilog** допускает подобное использование необъявленного сигнала. В этом случае, синтезатор неявно создаст одноименный одноразрядный сигнал, что и произошло.
|
||
|
||
Для исправления этой ошибки, объявим сигнал `max` с корректной разрядностью и повторим моделирование.
|
||
|
||

|
||
|
||
_Рисунок 27. Результат моделирования после объявления пропущенного сигнала._
|
||
|
||
## Самостоятельная работа
|
||
|
||
Число ошибок сократилось до 40! Мы явно на верном пути. Повторяем предыдущие шаги, вернувшись к первой ошибке:
|
||
|
||

|
||
|
||
_Рисунок 28. Первая ошибка в повторном моделировании._
|
||
|
||
В этот раз первая ошибка осталась прежней, только теперь дизайн считает, что результат должен равняться шести (в прошлый раз дизайн выдавал `2`). Мы уже убедились, что в этом случае модель дает правильный результат, поэтому сразу перейдем к формирующим результат сигналам:
|
||
|
||

|
||
|
||
_Рисунок 29. Поведение внутренних сигналов модуля `vector_abs` на временной диаграмме._
|
||
|
||
Видим, что значение сигнала `min_half`, формирующего значение выхода `abs` неверно (минимумом из `3` и `4` является `3`, `3/2 = 1`).
|
||
|
||
Не отходя далеко от кассы, мы замечаем, что значение `min`, формирующее сигнал `min_half` неверно: его значение `4`, а должно быть `3`.
|
||
|
||
Используя [файлы исходного кода проекта](../Other/vector_abs/), попробуйте разобраться в последней обнаруженной нами ошибке.
|