# Руководство по поиску функциональных ошибок

## Цель

При выполнении лабораторных работ вы непременно будете сталкиваться с множеством ошибок. И это нормально: **"Не ошибается тот, кто ничего не делает" — © Джейсон Стейтем**.

Важно воспитать в себе положительное восприятие обнаружения ошибок (ведь это приводит к улучшению вашего творения). Если относиться к обнаружению ошибок отрицательно, то вы подсознательно будете пытаться найти ошибки спустя рукава, но, если вы "в домике", и ошибок не видите — это не значит, что их нет.

При должном отношении, поиск ошибок может превратиться в увлекательное детективное расследование, где у вас есть "место преступления" (обнаруженное несоответствие в поведении, обычно это не сама ошибка, а ее следствие, круги на воде) и какой-то "набор улик" (фрагменты лога, исходный код). И вы, по чуть-чуть, будете разматывать "нераспутываемую паутину лжи", получая всё новые улики, ведущие к истинной ошибке.

Этот документ представляет собой практикум по поиску подобных ошибок в **SystemVerilog**-коде.

> [!IMPORTANT]
> Обратите внимание на то, как ставится ударение в словосочетании "временна́я диаграмма" (не "вре́менная"). В обиходе это словосочетание заменяется словом "времянка".

- [Руководство по поиску функциональных ошибок](#Руководство-по-поиску-функциональных-ошибок)
  - [Цель](#Цель)
  - [Алгоритм поиска ошибок](#Алгоритм-поиска-ошибок)
  - [Работа с логом при появлении ошибок](#Работа-с-логом-при-появлении-ошибок)
  - [Поиск ошибки на временной диаграмме](#Поиск-ошибки-на-временной-диаграмме)
  - [Открытие файла исходного кода проблемного сигнала](#Открытие-файла-исходного-кода-проблемного-сигнала)
  - [Добавление сигналов объектов на временную диаграмму](#Добавление-сигналов-объектов-на-временную-диаграмму)
  - [Сброс симуляции и ее повтор, установка времени моделирования](#Сброс-симуляции-и-ее-повтор-установка-времени-моделирования)
  - [Исправление сигналов с Z-состоянием](#Исправление-сигналов-с-z-состоянием)
  - [Поиск ошибки в сигналах, формирующих проблемный сигнал](#Поиск-ошибки-в-сигналах-формирующих-проблемный-сигнал)
  - [Исправление логики проблемного сигнала](#Исправление-логики-проблемного-сигнала)
  - [Проблема необъявленных сигналов](#Проблема-необъявленных-сигналов)
  - [Самостоятельная работа](#Самостоятельная-работа)

## Алгоритм поиска ошибок

1. Обычно всё начинается с сообщения в логе тестов (никто не проверяет глазами временную диаграмму сложных проектов, состоящую из тысяч сигналов, меняющихся миллионы раз за микросекунду), но на наших лабораторных работах с относительно простыми модулями, этот шаг иногда может быть и пропущен.  
Сообщение в логе обычно содержит следующую ключевую информацию: имя сигнала, на котором установилось неверное значение, и время, когда это произошло. Чем лучше написано верификационное окружение, тем больше ключевой информации будет отражено в сообщении, поэтому его написание является своего рода искусством.
2. Получив имя сигнала и время, мы отправляемся на временную диаграмму и проверяем нашу ошибку. Как это сделать? Необходимо определить по коду, какие сигналы и каким образом управляют нашим сигналом. Вариантов может быть несколько:
   1. Управляющие сигналы имеют корректное значение, но логика, по которой они управляют сигналом неверна, из-за этого на нем возникает неверное значение.
   Это идеальный случай, при возникновении которого мы сразу же находим причину проблемы и исправляем ее.
   2. Логика управления верна, а какая-то часть управляющих сигналов имеет неверное значение (пусть для примера, неверное значение будет на управляющем сигнале `X`). Это означает, что обнаруженное несоответствие сигналов является уже следствием какой-то ошибки, и мы должны вернуться к шагу 2, проверяя источники для сигнала со значением `X`. Так происходит до тех пор, пока мы не попадаем в тип 1.
   3. Логика управления и значения управляющих сигналов верны. Это самый сложный тип ошибок, который заключается либо в ошибке в спецификации разрабатываемого устройства, либо в САПРе или компонентах, влияющих на его работу. В рамках данного курса вас не должны заботить данные ошибки, и при их возникновении вам стоит обратиться к преподавателю (предварительно убедившись, что ошибка совершенно точно не подходит под первые два варианта).
   4. Любая возможная комбинация всех предыдущих типов.
3. Обнаружив первопричину ошибки, мы исправляем ее (возможно дополняя набор тестов, или внеся правки в спецификацию), и повторно запускаем все тесты, чтобы убедиться в двух вещах:
   1. ошибка действительно исправлена
   2. исправление ошибки не породило новых ошибок

Давайте отработаем эти шаги на примере отладки ошибок в [проекте](./vector_abs/) по вычислению приблизительной длины вектора, создание которого было описано в документе "[Менеджер проекта](./03.%20Project%20manager.md)".

## Работа с логом при появлении ошибок

После запуска симуляции мы видим в логе множество ошибок:

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_01.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_01.png)

_Рисунок 1. Пример сообщения об ошибках в тесте._

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

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_02.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_02.png)

_Рисунок 2. Пример конкретной ошибки в тесте._

В логе сказано, что в момент времени `5ns`, на вход схемы подавались координаты вектора, равные `0` и `0`, модель посчитала, что длина вектора равна нулю, в то время как схема вернула значение `x`.

## Поиск ошибки на временной диаграмме

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

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_03.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_03.png)

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

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_04.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_04.png)

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

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_05.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_05.png)

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

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_06.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_06.png)

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

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_07.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_07.png)

_Рисунок 7. Временная диаграмма, отмасштабированная к времени ошибки с рис. 2._

Мы видим ровно ту информацию, которую нам предоставил тестбенч. Теперь надо разобраться в причинах возникновения X-состояния. Такое может произойти по множеству причин, вот три из них:

1. какой-то из сигналов, формирующих этот находится в `X` или `Z` состоянии;
2. два каких-то сигнала одновременно пытаются выставить разные значения на целевой сигнал;
3. этот сигнал является выходом модуля, но был описан с ключевым словом `input`.

## Открытие файла исходного кода проблемного сигнала

В любом случае, первым делом необходимо определить, источник формирования значения сигнала `res`. Откроем файл с исходным кодом, где определен данный сигнал. Для этого, нажмем правой кнопкой мыши по имени сигнала на временной диаграмме, и выберем `Go To Source Code`:

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_08.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_08.png)

_Рисунок 8. Переход к месту объявления "проблемного" сигнала._

Откроется код, представленный в _листинге 1_ (с курсором на строчке `logic [31:0] res;`):

```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)
);
//...
```

_Листинг 1. Начало кода симулируемого тестбенча._

Выделив `res` мы видим, что у нас подсветился `res` в строке `abs(res)`. Это означает, что мы завели наш провод внутрь объекта `dut` модуля `vector_abs`, и у нас проблема второго типа (в логике работы провода `res` нет ошибок, он принял некорректное значение, поскольку ему таковое передали).

В этом можно убедиться, если вытащить сигналы модуля `vector_abs` на временную диаграмму. Чтобы это сделать, надо переключиться на окно `Scope`, где размещена иерархия моделируемых объектов.

## Добавление сигналов объектов на временную диаграмму

> [!IMPORTANT]
> Обратите внимание, что в иерархии окна `Scope` находятся не имена модулей, а имена сущностей модуля. В приведенном выше листинге кода мы создали сущность модуля `vector_abs` с именем `dut`, поэтому в иерархии `Scope` мы видим внутри модуля верхнего уровня объект `dut` (не `vector_abs`), так будет и со всеми вложенными объектами.

Выделим объект `dut`. В окне `Objects` справа отобразятся все внутренние сигналы (входы/выходы, внутренние провода и регистры) объекта `dut`:

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_09.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_09.png)

_Рисунок 9. Отображение внутренних сигналов проверяемого модуля._

Вообще говоря, мы уже видим, что выход `abs` (к которому подключен наш провод `res`) находится в X-состоянии, но для отработки навыков, разберемся с добавлением новых сигналов на временную диаграмму. Можно поступить двумя способами:

1. Добавить все сигналы (то, что видно в окне `Objects` на временную диаграмму) из окна `Scope` для этого, либо перетаскиваем нужный нам объект, зажав левую кнопку мыши на временную диаграмму, либо жмем правой кнопкой мыши по нужному объекту, и выбираем `Add to Wave Window`
2. Добавить отдельные сигналы из окна `Objects`. Для этого выделяем их (возможно множественное выделение через модификаторы `shift` или `ctrl`), и как и в прошлом случае, либо перетаскиваем сигналы левой кнопкой мыши, либо добавляем их через правую кнопку мыши.

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_10.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_10.png)

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

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_11.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_11.png)

_Рисунок 11. Результат добавления сигналов модуля на временную диаграмму._

По мере роста сложности проекта, число сигналов на временной диаграмме будет постоянно расти, в связи с чем встает вопрос группировки сигналов.

Для того чтобы объединить сигналы в группу, необходимо их выделить. Это можно сделать двумя способами:

1. кликнув левой кнопкой мыши по каждому из интересующих сигналов при зажатой клавише `Ctrl`;
2. если речь идет о диапазоне сигналов, можно выбрать сигнал с одного края, после чего, при зажатой клавише `Shift`, выбрать сигнал с другого края этого диапазона.

После выбора, необходимо нажать правой кнопкой мыши по выделенным сигналам, и в низу выпадающего списка выбрать `New Group`.

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_12.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_12.png)

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

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

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_13.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_13.png)

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

Данную группу можно сворачивать и разворачивать, нажимая на соответствующую стрелку слева от имени группы.

> [!IMPORTANT]
> Обратите внимание, что часть сигналов отображают какое-то значение (сигнал `abs` отображает X-состояние), а часть не отображают ничего. Так произошло, потому что провод `abs` **непрерывно связан** с проводом `res`. С точки зрения симулятора это одна сущность, и записывая во время моделирования значения для сигнала `res`, он неявно записывал значения и для сигнала `abs`. Этого нельзя сказать про остальные сигналы, которых не было во время моделирования на временной диаграмме.

## Сброс симуляции и ее повтор, установка времени моделирования

Для того, чтобы получить отсутствующие значения, необходимо повторить моделирование. Для этого, необходимо сбросить время моделирования в 0 и запустить его снова.

Для этого, необходимо на панели симуляции нажать кнопку `Restart` (`|◀`), а затем кнопку `Run all` (`▶`) или `Run for` (`▶t`). Положение кнопок в окне Vivado иллюстрирует _рис. 14_.

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_14.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_14.png)

_Рисунок 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` тем, что в качестве количества моделируемого времени указывается "бесконечность", и моделирование будет остановлено только вручную, либо вызовом соответствующей инструкции.

> [!IMPORTANT]
> Обратите внимание, что для добавления недостающих значений добавленных сигналов лучше всего выполнять описанную выше инструкцию. Аналогичного результата можно добиться и нажатием на кнопку `Relaunch Simulation`, однако эта команда работает дольше и, если вы не меняли исходный код модулей, не нужна.

Кроме того, чтобы курсор и лог снова не ушли далеко от места первой ошибки, можно сразу указать, необходимое нам время моделирования перед выполнением команды `Run for`: `5ns`.

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_15.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_15.png)

_Рисунок 15. Пример моделирования 5ns._

На _рис. 16_ представлен результат моделирования с новыми сигналами.

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_16.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_16.png)

_Рисунок 16. Результат повторного моделирования после добавления на временную диаграмму новых сигналов._

Видим два сигнала в Z-состоянии и один сигнал в X-состоянии. Обычно, сигналы с Z-состоянием проще всего исправить, т.к. зачастую это забытое или некорректное подключение провода. Кроме того, сигнал, зависящий от сигнала с Z-состоянием, может оказаться в X-состоянии, так что это может быть решением нашей проблемы, поэтому проверим провода `min` и `min_half`. Сперва займемся сигналом `min` и перейдем к шагу 2 нашего алгоритма (нажимаем правой кнопкой мыши и выбираем `Go To Source Code`):

```Verilog
module vector_abs(
  input  logic [31:0] x,
  input  logic [31:0] y,
  output logic [31:0] abs
);


  logic [31:0] min;
  logic [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`.

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_17.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_17.png)

_Рисунок 17. Добавление сигналов вложенных модулей на временную диаграмму._

Добавляем внутренние сигналы на временную диаграмму, группируем их под именем `max_min`, и повторяем моделирование.

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_18.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_18.png)

_Рисунок 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`. Исправим это и повторим моделирование.

> [!IMPORTANT]
> Обратите внимание, что поскольку мы изменили исходный код, в этот раз необходимо нажать на кнопку `Relaunch Simulation`, поскольку нужна повторная компиляция проекта.

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_19.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_19.png)

_Рисунок 19. Результат моделирования после исправления разрядности сигнала `min`._

В логе сообщается о 102 найденных ошибках. Ровно на одну ошибку меньше, чем было ранее. Это не означает, что в проекте осталось 102 ошибки, только то, что, исправив данную ошибку — мы действительно что-то исправили, и один из тестовых сценариев, который ранее завершался ошибкой, теперь завершился без неё.

Помните, что если в проекте много ошибок, то часть ошибок может выправлять поведение других ошибок (хоть и не всегда, но иногда минус на минус может выдать плюс в контексте ошибок проекта), поэтому надо с осторожностью полагаться на число найденных ошибок, если это число больше нуля.

Посмотрим на нашу временную диаграмму снова, и выберем дальнейшие действия:

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_20.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_20.png)

_Рисунок 20. Временная диаграмма после исправления разрядности сигнала `min`._

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

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_21.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_21.png)

_Рисунок 21. Первая ошибка в новом логе моделирования._

## Поиск ошибки в сигналах, формирующих проблемный сигнал

Мы видим, что первой ошибкой в логе стала не та ошибка, что была прежде. Раньше первый неверный результат мы видели в момент времени `5ns`, когда на схему подавались значения `0` и `0`, теперь же первой ошибкой стал момент времени `10ns`, когда на схему подаются значения `1` и `1`. Наше устройство считает, что результат должен равняться `3`, в то время как модель считает, что результат должен равняться `1`. Проверим, нет ли ошибки в модели и посчитаем результат самостоятельно:

Для определения приблизительной длины вектора в евклидовом пространстве (т.е. длины гипотенузы прямоугольного треугольника, которая равна квадратному корню из суммы квадратов катетов) можно воспользоваться формулой:

`sqrt(a^2 + b^2) ≈ max + min/2`, где `max` и `min` — большее и меньшее из пары чисел соответственно [**Ричард Лайонс: Цифровая обработка сигналов, стр. 475**].

Подставим наши числа в формулу (поскольку оба числа равны, не важно какое из них будет максимумом, а какое минимумом):

```text
1 + 1/2 = 1.5
```

Ни модель, ни схема не правы?

На самом деле, наше устройство поддерживает только целочисленную арифметику, поэтому результат будет:

```text
1 + 1/2 = 1 + 0 = 1
```

Модель правильно отразила особенность нашего устройства и дала корректный результат.

Значит надо смотреть как формируется результат в нашем устройстве. Посмотрим на выход `abs` в модуле `vector_abs`:

```Verilog
assign abs = max + min_half;
```

Выход `abs` зависит от двух внутренних сигналов: `max` и `min_half`. В соответствии с нашим алгоритмом, либо проблема в логике, связывающей эти два сигнала (операции сложения), либо в значении какого-то из этих сигналов, либо комбинации этих вариантов.

Изучив модуль, мы понимаем, что в логике этого присваивания проблем нет, т.к. оно повторяет логику формулы `max + min/2`, складывая максимум с половиной минимума. Значит проблема в значении какого-то из этих сигналов (или обоих из них). Посчитаем значения этих сигналов самостоятельно (для сложного проекта эти значения посчитала бы модель):

`1` и `0`.

Смотрим, какие значения установлены на сигналах `max` и `min_half` в момент времени `10ns`.

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_22.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_22.png)

_Рисунок 22. Значения сигналов `max` и `min_half` в момент времени `10 ns` (интересующие нас сигналы выделены зелёным)_

> [!IMPORTANT]
> Обратите внимание: вы можете менять цвета сигналов временной диаграммы через контекстное меню выделенных сигналов.

Мы видим, что в момент времени `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`, поэтому мы будем смотреть исходный код данного модуля:

```Verilog
module half_divider(
  input [31:0] numerator,
  output[31:0] quotient
);

  assign quotient = numerator << 1'b1;

endmodule
```

Что делает данный модуль? Он принимает на вход значение и делит его на два. На вход данного модуля будет приходить значение минимума из нашей формулы.

Выход данного модуля зависит от входа `numerator` и логики сдвига влево на 1. Это значит, что проблема либо в логике, либо в значении, подаваемом на вход. Выведем сигнал `numerator` на временную диаграмму и посмотрим на его значение в момент времени `10ns.

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_23.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_23.png)

_Рисунок 23. Значение сигнала `numerator` в момент времени `10 ns`._

Мы помним, что в момент, когда схема начала выдавать неправильный результат, на его входы подавались числа `1` и `1`, это значит, что на вход `numerator` пришло корректное значение: минимум из этих двух чисел и правда равен `1`. Проверим логику данного модуля.

## Исправление логики проблемного сигнала

Операция деления в цифровой схемотехнике является очень "дорогой" в плане ресурсов логических блоков и критического пути, поэтому этой операции часто стараются избегать. В нашем случае, нам не нужно обычное деление — нам нужно деление только напополам. В двоичной арифметике, для того чтобы разделить число на два, достаточно отбросить его младшую цифру. Вы часто пользуетесь подобной операцией в повседневной жизни при выполнении операции деления на 10: отбрасываете младшую цифру в десятичной арифметике.

Именно поэтому, когда мы в первый раз пытались посчитать результат "на бумаге", у нас было расхождение с моделью: когда мы делим 1 на 2, мы получаем 0.5, однако деление путем отбрасывания цифры округляет результат вниз (1/2=0, 15/10=1).

Как "отбросить" цифру средствами цифровой логики? Для этого используется операция сдвига вправо.

Операция сдвига вправо в **SystemVerilog** записывается оператором `>>`. Справа от оператора указывается число "отбрасываемых цифр", в нашем случае одна. Но постойте, в логике присваивания стоит оператор `<<`. Это ошибка, исправим ее!

Повторяем моделирование.

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_24.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_24.png)

_Рисунок 24. Результат моделирования после исправления оператора сдвига._

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

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_25.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_25.png)

_Рисунок 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`).

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_26.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_26.png)

_Рисунок 26. Поведение внутренних сигналов модуля `vector_abs` на временной диаграмме._

В глаза сразу же бросается, что сигнал `max` внешне отличается от всех остальных — он ведет себя как 1-битный сигнал. Если все остальные сигналы 32-разрядные, то и сигнал `max` должен быть таким же. Перейдем к объявлению этого сигнала, чтобы это исправить (нажав правой кнопкой мыши, и выбрав `Go To Source Code`):

```Verilog
module vector_abs(
  input  logic [31:0] x,
  input  logic [31:0] y,
  output logic [31:0] abs
);


  logic [31:0] min;
  logic [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` с корректной разрядностью и повторим моделирование.

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_27.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_27.png)

_Рисунок 27. Результат моделирования после объявления пропущенного сигнала._

## Самостоятельная работа

Число ошибок сократилось до 40! Мы явно на верном пути. Повторяем предыдущие шаги, вернувшись к первой ошибке:

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_28.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_28.png)

_Рисунок 28. Первая ошибка в повторном моделировании._

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

![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_29.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_29.png)

_Рисунок 29. Поведение внутренних сигналов модуля `vector_abs` на временной диаграмме._

Видим, что значение сигнала `min_half`, формирующего значение выхода `abs` неверно (минимумом из `3` и `4` является `3`, `3/2 = 1`).

Не отходя далеко от кассы, мы замечаем, что значение `min`, формирующее сигнал `min_half` неверно: его значение `4`, а должно быть `3`.

Используя файлы исходного кода [проекта](./vector_abs/), попробуйте разобраться в последней обнаруженной нами ошибке.
