English version draft

Assisted-by: Claude:claude-4.6-sonnet
This commit is contained in:
Andrei Solodovnikov
2026-04-12 13:53:25 +03:00
parent 63260f434e
commit f3fcd27387
74 changed files with 5133 additions and 5875 deletions

View File

@@ -1,103 +1,105 @@
# Руководство по поиску функциональных ошибок
# Functional Bug Hunting Guide
## Цель
## Goal
При выполнении лабораторных работ вы непременно будете сталкиваться с множеством ошибок. И это нормально: **"Не ошибается тот, кто ничего не делает" — © Джейсон Стейтем**.
When completing lab assignments, you will inevitably encounter many bugs. This is perfectly normal: **"Only those who do nothing make no mistakes" — © Jason Statham**.
Важно воспитать в себе положительное восприятие обнаружения ошибок (ведь это приводит к улучшению вашего творения). Если относиться к обнаружению ошибок отрицательно, то вы подсознательно будете пытаться найти ошибки спустя рукава, но, если вы "в домике", и ошибок не видите — это не значит, что их нет.
It is important to develop a positive attitude toward finding bugs (since discovering them leads to improvements in your design). If you approach bug detection negatively, you will subconsciously try to find bugs half-heartedly — but not seeing bugs does not mean they are not there.
При должном отношении, поиск ошибок может превратиться в увлекательное детективное расследование, где у вас есть "место преступления" (обнаруженное несоответствие в поведении, обычно это не сама ошибка, а ее следствие, круги на воде) и какой-то "набор улик" (фрагменты лога, исходный код). И вы, по чуть-чуть, будете разматывать "нераспутываемую паутину лжи", получая всё новые улики, ведущие к истинной ошибке.
With the right mindset, debugging can turn into an exciting detective investigation, where you have a "crime scene" (an observed behavioral discrepancy — usually not the bug itself, but its consequence, ripples on the water) and a "set of clues" (log fragments, source code). Step by step, you will unravel what seems like an impenetrable web, uncovering new clues leading to the true root cause.
Этот документ представляет собой практикум по поиску подобных ошибок в **SystemVerilog**-коде.
This document is a hands-on guide to finding such bugs in **SystemVerilog** code.
> [!IMPORTANT]
> Обратите внимание на то, как ставится ударение в словосочетании "временна́я диаграмма" (не "вре́менная"). В обиходе это словосочетание заменяется словом "времянка".
> Note: throughout this guide, the term "waveform" refers to the signal timeline display in the Vivado simulator.
- [Руководство по поиску функциональных ошибок](#Руководство-по-поиску-функциональных-ошибок)
- [Цель](#Цель)
- [Алгоритм поиска ошибок](#Алгоритм-поиска-ошибок)
- [Работа с логом при появлении ошибок](#Работа-с-логом-при-появлении-ошибок)
- [Поиск ошибки на временной диаграмме](#Поиск-ошибки-на-временной-диаграмме)
- [Открытие файла исходного кода проблемного сигнала](#Открытие-файла-исходного-кода-проблемного-сигнала)
- [Добавление сигналов объектов на временную диаграмму](#Добавление-сигналов-объектов-на-временную-диаграмму)
- [Сброс симуляции и ее повтор, установка времени моделирования](#Сброс-симуляции-и-ее-повтор-установка-времени-моделирования)
- [Исправление сигналов с Z-состоянием](#Исправление-сигналов-с-z-состоянием)
- [Поиск ошибки в сигналах, формирующих проблемный сигнал](#Поиск-ошибки-в-сигналах-формирующих-проблемный-сигнал)
- [Исправление логики проблемного сигнала](#Исправление-логики-проблемного-сигнала)
- [Проблема необъявленных сигналов](#Проблема-необъявленных-сигналов)
- [Самостоятельная работа](#Самостоятельная-работа)
- [Functional Bug Hunting Guide](#functional-bug-hunting-guide)
- [Goal](#goal)
- [Bug Hunting Algorithm](#bug-hunting-algorithm)
- [Working with the Log When Errors Appear](#working-with-the-log-when-errors-appear)
- [Locating the Bug on the Waveform](#locating-the-bug-on-the-waveform)
- [Opening the Source File of the Problematic Signal](#opening-the-source-file-of-the-problematic-signal)
- [Adding Object Signals to the Waveform](#adding-object-signals-to-the-waveform)
- [Restarting the Simulation and Setting Simulation Time](#restarting-the-simulation-and-setting-simulation-time)
- [Fixing Signals in the Z-State](#fixing-signals-in-the-z-state)
- [Tracing the Bug Through the Signals Driving the Problematic Signal](#tracing-the-bug-through-the-signals-driving-the-problematic-signal)
- [Fixing the Logic of the Problematic Signal](#fixing-the-logic-of-the-problematic-signal)
- [The Problem of Undeclared Signals](#the-problem-of-undeclared-signals)
- [Independent Exercise](#independent-exercise)
## Алгоритм поиска ошибок
## Bug Hunting Algorithm
1. Обычно всё начинается с сообщения в логе тестов (никто не проверяет глазами временную диаграмму сложных проектов, состоящую из тысяч сигналов, меняющихся миллионы раз за микросекунду), но на наших лабораторных работах с относительно простыми модулями, этот шаг иногда может быть и пропущен.
Сообщение в логе обычно содержит следующую ключевую информацию: имя сигнала, на котором установилось неверное значение, и время, когда это произошло. Чем лучше написано верификационное окружение, тем больше ключевой информации будет отражено в сообщении, поэтому его написание является своего рода искусством.
2. Получив имя сигнала и время, мы отправляемся на временную диаграмму и проверяем нашу ошибку. Как это сделать? Необходимо определить по коду, какие сигналы и каким образом управляют нашим сигналом. Вариантов может быть несколько:
1. Управляющие сигналы имеют корректное значение, но логика, по которой они управляют сигналом неверна, из-за этого на нем возникает неверное значение.
Это идеальный случай, при возникновении которого мы сразу же находим причину проблемы и исправляем ее.
2. Логика управления верна, а какая-то часть управляющих сигналов имеет неверное значение (пусть для примера, неверное значение будет на управляющем сигнале `X`). Это означает, что обнаруженное несоответствие сигналов является уже следствием какой-то ошибки, и мы должны вернуться к шагу 2, проверяя источники для сигнала со значением `X`. Так происходит до тех пор, пока мы не попадаем в тип 1.
3. Логика управления и значения управляющих сигналов верны. Это самый сложный тип ошибок, который заключается либо в ошибке в спецификации разрабатываемого устройства, либо в САПРе или компонентах, влияющих на его работу. В рамках данного курса вас не должны заботить данные ошибки, и при их возникновении вам стоит обратиться к преподавателю (предварительно убедившись, что ошибка совершенно точно не подходит под первые два варианта).
4. Любая возможная комбинация всех предыдущих типов.
3. Обнаружив первопричину ошибки, мы исправляем ее (возможно дополняя набор тестов, или внеся правки в спецификацию), и повторно запускаем все тесты, чтобы убедиться в двух вещах:
1. ошибка действительно исправлена
2. исправление ошибки не породило новых ошибок
1. The process usually starts with a message in the test log (nobody manually inspects a waveform of a complex project with thousands of signals changing millions of times per microsecond), but in our lab assignments with relatively simple modules, this step may sometimes be skipped.
The log message typically contains the following key information: the name of the signal that received an incorrect value, and the time at which this occurred. The better the testbench is written, the more useful information the message will contain — writing a good testbench is something of an art.
Давайте отработаем эти шаги на примере отладки ошибок в [проекте](./vector_abs/) по вычислению приблизительной длины вектора, создание которого было описано в документе "[Менеджер проекта](./03.%20Project%20manager.md)".
2. Having the signal name and timestamp, we go to the waveform and investigate the error. How do we do this? We need to determine from the code which signals drive our signal of interest and how. There are several possibilities:
1. The driving signals have correct values, but the logic by which they drive the target signal is wrong, causing it to receive an incorrect value.
This is the ideal case — we immediately identify the root cause and fix it.
2. The driving logic is correct, but one or more of the driving signals has an incorrect value (let's call that signal `X`). This means the observed discrepancy is a symptom of some other error, and we must return to step 2, now investigating the sources of signal `X`. This repeats until we reach case 1.
3. Both the driving logic and the values of the driving signals are correct. This is the most complex type of error — it implies either a bug in the specification of the device being developed, or a problem in the EDA tool or its components. In the context of this course, you should not be concerned about such errors; if they arise, consult your instructor (after confirming that the error definitely does not fall under cases 1 or 2).
4. Any combination of the above.
## Работа с логом при появлении ошибок
3. Once the root cause is identified, we fix it (possibly extending the test suite or revising the specification) and rerun all tests to verify two things:
1. The bug is actually fixed.
2. The fix did not introduce new bugs.
После запуска симуляции мы видим в логе множество ошибок:
Let us practice these steps by debugging errors in the [project](./vector_abs/) that computes the approximate magnitude of a vector, as described in the "[Project Manager](./03.%20Project%20manager.md)" document.
## Working with the Log When Errors Appear
After running the simulation, we see multiple errors in the log:
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_01.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_01.png)
_Рисунок 1. Пример сообщения об ошибках в тесте._
_Figure 1. Example of test error messages._
В любой ситуации с множеством ошибок, сначала надо разбираться с самой первой из них, поскольку она может быть причиной появления всех остальных. Поэтому листаем лог до момента первой ошибки:
When faced with many errors, always start with the very first one, since it may be the cause of all the others. Scroll the log to find the first error:
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_02.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_02.png)
_Рисунок 2. Пример конкретной ошибки в тесте._
_Figure 2. Example of a specific test error._
В логе сказано, что в момент времени `5ns`, на вход схемы подавались координаты вектора, равные `0` и `0`, модель посчитала, что длина вектора равна нулю, в то время как схема вернула значение `x`.
The log states that at time `5ns`, the circuit received vector coordinates of `0` and `0`, the reference model computed a vector magnitude of zero, while the circuit returned `x`.
## Поиск ошибки на временной диаграмме
## Locating the Bug on the Waveform
Давайте найдем это место на временной диаграмме. Обычно, сразу после запуска симуляции на временной диаграмме отображено место, где симуляция остановилась (возможно с очень неподходящим масштабом). Для начала подгоним масштаб таким образом, чтобы вся временная диаграмма умещалась в окне. Это делается либо нажатием правой кнопкой мыши по области отображения сигналов, с выбором "Full View" во всплывающем меню, либо нажатием соответствующей кнопки на панели временной диаграммы (см. _рис. 4_), либо нажатием комбинации клавиш `Ctrl+0`. Затем найдем приблизительное место рядом с тем временем, что нас интересует, установим там курсор, и приблизим масштаб (покрутив колесиком мыши при зажатой клавише `Ctrl`), периодически уточняя местоположения курсора, пока не найдем интересующее нас место.
Let us find this location on the waveform. Immediately after a simulation run, the waveform typically shows the point where the simulation stopped (possibly at an inconvenient zoom level). First, adjust the zoom so that the entire waveform fits in the window. This can be done by right-clicking in the signal display area and selecting "Full View", by clicking the corresponding button on the waveform toolbar (see _Fig. 4_), or by pressing `Ctrl+0`. Then find the approximate location near the time of interest, place the cursor there, and zoom in (scroll with the mouse wheel while holding `Ctrl`), periodically adjusting the cursor position until you reach the location of interest.
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_03.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_03.png)
_Рисунок 3. Пример временной диаграммы сразу поле остановки моделирования._
_Figure 3. Example of the waveform immediately after the simulation stops._
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_04.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_04.png)
_Рисунок 4. Пример установки масштаба временной диаграммы таким образом, чтобы та помещалась в текущем окне._
_Figure 4. Example of fitting the entire waveform into the current window._
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_05.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_05.png)
_Рисунок 5. Пример временной диаграммы после подгонки масштаба._
_Figure 5. Example of the waveform after adjusting the zoom._
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_06.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_06.png)
_Рисунок 6. Установка курсора в начало моделирования, чтобы, при увеличении масштаба, временная диаграмма сходилась к началу._
_Figure 6. Placing the cursor at the start of simulation so that zooming in converges toward the beginning._
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_07.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_07.png)
_Рисунок 7. Временная диаграмма, отмасштабированная к времени ошибки с рис. 2._
_Figure 7. Waveform zoomed in to the error time from Fig. 2._
Мы видим ровно ту информацию, которую нам предоставил тестбенч. Теперь надо разобраться в причинах возникновения X-состояния. Такое может произойти по множеству причин, вот три из них:
We see exactly the information the testbench reported. Now we need to determine what is causing the X-state. This can happen for several reasons, including:
1. какой-то из сигналов, формирующих этот находится в `X` или `Z` состоянии;
2. два каких-то сигнала одновременно пытаются выставить разные значения на целевой сигнал;
3. этот сигнал является выходом модуля, но был описан с ключевым словом `input`.
1. One of the signals driving this signal is in the `X` or `Z` state.
2. Two signals are simultaneously trying to drive the target signal to different values.
3. This signal is a module output but was declared with the `input` keyword.
## Открытие файла исходного кода проблемного сигнала
## Opening the Source File of the Problematic Signal
В любом случае, первым делом необходимо определить, источник формирования значения сигнала `res`. Откроем файл с исходным кодом, где определен данный сигнал. Для этого, нажмем правой кнопкой мыши по имени сигнала на временной диаграмме, и выберем `Go To Source Code`:
In any case, the first step is to identify the source that drives the value of signal `res`. Open the source file where this signal is defined. To do so, right-click the signal name in the waveform and select `Go To Source Code`:
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_08.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_08.png)
_Рисунок 8. Переход к месту объявления "проблемного" сигнала._
_Figure 8. Navigating to the declaration of the "problematic" signal._
Откроется код, представленный в _листинге 1_ (с курсором на строчке `logic [31:0] res;`):
The code shown in _Listing 1_ will open (with the cursor on the line `logic [31:0] res;`):
```Verilog
module tb_vector_abs();
@@ -114,97 +116,97 @@ vector_abs dut(
//...
```
_Листинг 1. Начало кода симулируемого тестбенча._
_Listing 1. Beginning of the simulated testbench code._
Выделив `res` мы видим, что у нас подсветился `res` в строке `abs(res)`. Это означает, что мы завели наш провод внутрь объекта `dut` модуля `vector_abs`, и у нас проблема второго типа (в логике работы провода `res` нет ошибок, он принял некорректное значение, поскольку ему таковое передали).
Selecting `res`, we see it is also highlighted in the line `abs(res)`. This means we connected our wire into the `dut` instance of module `vector_abs`, and the problem is of the second type (there is no error in the logic of wire `res` itself — it received an incorrect value because that value was passed to it from the inside).
В этом можно убедиться, если вытащить сигналы модуля `vector_abs` на временную диаграмму. Чтобы это сделать, надо переключиться на окно `Scope`, где размещена иерархия моделируемых объектов.
This can be confirmed by pulling the signals of module `vector_abs` onto the waveform. To do this, switch to the `Scope` window, which shows the hierarchy of simulated objects.
## Добавление сигналов объектов на временную диаграмму
## Adding Object Signals to the Waveform
> [!IMPORTANT]
> Обратите внимание, что в иерархии окна `Scope` находятся не имена модулей, а имена сущностей модуля. В приведенном выше листинге кода мы создали сущность модуля `vector_abs` с именем `dut`, поэтому в иерархии `Scope` мы видим внутри модуля верхнего уровня объект `dut` (не `vector_abs`), так будет и со всеми вложенными объектами.
> Note that the `Scope` window hierarchy shows instance names, not module type names. In the code listing above, we created an instance of module `vector_abs` named `dut`, so inside the top-level module we see the object `dut` (not `vector_abs`) in the `Scope` hierarchy. The same applies to all nested instances.
Выделим объект `dut`. В окне `Objects` справа отобразятся все внутренние сигналы (входы/выходы, внутренние провода и регистры) объекта `dut`:
Select the `dut` object. The `Objects` window on the right will display all internal signals (ports, internal wires, and registers) of the `dut` instance:
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_09.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_09.png)
_Рисунок 9. Отображение внутренних сигналов проверяемого модуля._
_Figure 9. Displaying the internal signals of the module under test._
Вообще говоря, мы уже видим, что выход `abs` (к которому подключен наш провод `res`) находится в X-состоянии, но для отработки навыков, разберемся с добавлением новых сигналов на временную диаграмму. Можно поступить двумя способами:
We can already see that the `abs` output (which is connected to our wire `res`) is in the X-state, but for the sake of practice, let us walk through how to add new signals to the waveform. There are two ways:
1. Добавить все сигналы (то, что видно в окне `Objects` на временную диаграмму) из окна `Scope` для этого, либо перетаскиваем нужный нам объект, зажав левую кнопку мыши на временную диаграмму, либо жмем правой кнопкой мыши по нужному объекту, и выбираем `Add to Wave Window`
2. Добавить отдельные сигналы из окна `Objects`. Для этого выделяем их (возможно множественное выделение через модификаторы `shift` или `ctrl`), и как и в прошлом случае, либо перетаскиваем сигналы левой кнопкой мыши, либо добавляем их через правую кнопку мыши.
1. Add all signals visible in the `Objects` window from the `Scope` window: either drag the desired instance onto the waveform while holding the left mouse button, or right-click the instance and select `Add to Wave Window`.
2. Add individual signals from the `Objects` window: select them (multiple selection via `Shift` or `Ctrl` modifiers), then either drag them onto the waveform or add them via right-click.
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_10.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_10.png)
_Рисунок 10. Добавление сигналов модуля на временную диаграмму._
_Figure 10. Adding module signals to the waveform._
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_11.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_11.png)
_Рисунок 11. Результат добавления сигналов модуля на временную диаграмму._
_Figure 11. Result of adding module signals to the waveform._
По мере роста сложности проекта, число сигналов на временной диаграмме будет постоянно расти, в связи с чем встает вопрос группировки сигналов.
As a project grows in complexity, the number of signals on the waveform increases, which makes signal grouping important.
Для того чтобы объединить сигналы в группу, необходимо их выделить. Это можно сделать двумя способами:
To group signals together, first select them. This can be done in two ways:
1. кликнув левой кнопкой мыши по каждому из интересующих сигналов при зажатой клавише `Ctrl`;
2. если речь идет о диапазоне сигналов, можно выбрать сигнал с одного края, после чего, при зажатой клавише `Shift`, выбрать сигнал с другого края этого диапазона.
1. Left-click each signal of interest while holding `Ctrl`.
2. For a range of signals, click the signal at one end, then hold `Shift` and click the signal at the other end of the range.
После выбора, необходимо нажать правой кнопкой мыши по выделенным сигналам, и в низу выпадающего списка выбрать `New Group`.
After selecting, right-click the highlighted signals and choose `New Group` from the bottom of the context menu.
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_12.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_12.png)
_Рисунок 12. Пример создания группы сигналов (контекстное меню было обрезано для удобства отображения)._
_Figure 12. Example of creating a signal group (the context menu has been cropped for clarity)._
После создания группы, ей нужно будет дать имя. В случае, если все сигналы принадлежат одному модулю, удобно называть группу сигналов именем этого модуля.
After creating the group, assign it a name. When all signals belong to the same module, it is convenient to name the group after that module.
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_13.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_13.png)
_Рисунок 13. Пример созданной группы сигналов._
_Figure 13. Example of a created signal group._
Данную группу можно сворачивать и разворачивать, нажимая на соответствующую стрелку слева от имени группы.
The group can be collapsed and expanded by clicking the corresponding arrow to the left of the group name.
> [!IMPORTANT]
> Обратите внимание, что часть сигналов отображают какое-то значение (сигнал `abs` отображает X-состояние), а часть не отображают ничего. Так произошло, потому что провод `abs` **непрерывно связан** с проводом `res`. С точки зрения симулятора это одна сущность, и записывая во время моделирования значения для сигнала `res`, он неявно записывал значения и для сигнала `abs`. Этого нельзя сказать про остальные сигналы, которых не было во время моделирования на временной диаграмме.
> Notice that some signals display a value (the `abs` signal shows an X-state) while others show nothing. This is because wire `abs` is **continuously connected** to wire `res`. From the simulator's perspective, they are the same entity, and when the simulator recorded values for `res` during simulation, it implicitly recorded them for `abs` as well. This does not apply to the other signals that were not present on the waveform during the simulation run.
## Сброс симуляции и ее повтор, установка времени моделирования
## Restarting the Simulation and Setting Simulation Time
Для того, чтобы получить отсутствующие значения, необходимо повторить моделирование. Для этого, необходимо сбросить время моделирования в 0 и запустить его снова.
To obtain the missing values of the newly added signals, we need to repeat the simulation. To do so, reset the simulation time to 0 and run it again.
Для этого, необходимо на панели симуляции нажать кнопку `Restart` (`|◀`), а затем кнопку `Run all` (`▶`) или `Run for` (`▶t`). Положение кнопок в окне Vivado иллюстрирует _рис. 14_.
Click the `Restart` button (`|◀`) on the simulation toolbar, then click `Run all` (`▶`) or `Run for` (`▶t`). The button positions in the Vivado window are shown in _Fig. 14_.
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_14.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_14.png)
_Рисунок 14. Расположение кнопок, управляющих моделированием в окне Vivado._
_Figure 14. Location of the simulation control buttons in the Vivado window._
Панель управления симуляции с кнопками:
Simulation control toolbar buttons:
1. `Restart`, горячие клавиши: `Ctrl+Shift+F5`;
2. `Run all`, горячая клавиша: `F3`;
3. `Run for`, горячие клавиши: `Shift+F2`;
1. `Restart`, keyboard shortcut: `Ctrl+Shift+F5`;
2. `Run all`, keyboard shortcut: `F3`;
3. `Run for`, keyboard shortcut: `Shift+F2`;
4. `Relaunch Simulation`.
`Run for` выполняет моделирование указанного количества времени, после чего моделирование приостанавливается. Моделирование может быть остановлено так же и вручную, либо вызовом соответствующей инструкции из кода теста.
`Run for` runs the simulation for the specified amount of time, after which the simulation pauses. The simulation can also be stopped manually or by calling the appropriate instruction from the test code.
`Run all` отличается от `Run for` тем, что в качестве количества моделируемого времени указывается "бесконечность", и моделирование будет остановлено только вручную, либо вызовом соответствующей инструкции.
`Run all` differs from `Run for` in that it runs indefinitely and stops only when manually interrupted or when the appropriate instruction is called from the test code.
> [!IMPORTANT]
> Обратите внимание, что для добавления недостающих значений добавленных сигналов лучше всего выполнять описанную выше инструкцию. Аналогичного результата можно добиться и нажатием на кнопку `Relaunch Simulation`, однако эта команда работает дольше и, если вы не меняли исходный код модулей, не нужна.
> To populate the missing values for newly added signals, it is best to follow the procedure described above. A similar result can be achieved by clicking `Relaunch Simulation`, but this command takes longer and is unnecessary if you have not modified any source files.
Кроме того, чтобы курсор и лог снова не ушли далеко от места первой ошибки, можно сразу указать, необходимое нам время моделирования перед выполнением команды `Run for`: `5ns`.
Additionally, to prevent the cursor and log from jumping far away from the first error, you can specify the desired simulation time before clicking `Run for`: `5ns`.
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_15.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_15.png)
_Рисунок 15. Пример моделирования 5ns._
_Figure 15. Example of simulating 5 ns._
На _рис. 16_ представлен результат моделирования с новыми сигналами.
_Fig. 16_ shows the simulation result with the new signals.
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_16.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_16.png)
_Рисунок 16. Результат повторного моделирования после добавления на временную диаграмму новых сигналов._
_Figure 16. Result of re-running the simulation after adding new signals to the waveform._
Видим два сигнала в Z-состоянии и один сигнал в X-состоянии. Обычно, сигналы с Z-состоянием проще всего исправить, т.к. зачастую это забытое или некорректное подключение провода. Кроме того, сигнал, зависящий от сигнала с Z-состоянием, может оказаться в X-состоянии, так что это может быть решением нашей проблемы, поэтому проверим провода `min` и `min_half`. Сперва займемся сигналом `min` и перейдем к шагу 2 нашего алгоритма (нажимаем правой кнопкой мыши и выбираем `Go To Source Code`):
We see two signals in the Z-state and one signal in the X-state. Signals in the Z-state are usually the easiest to fix, as they typically indicate a forgotten or incorrect wire connection. Furthermore, a signal that depends on a Z-state signal may itself end up in an X-state — so fixing the Z-state issue might resolve our problem. Let us inspect wires `min` and `min_half`. Start with `min` and go to step 2 of our algorithm (right-click and select `Go To Source Code`):
```Verilog
module vector_abs(
@@ -226,105 +228,105 @@ module vector_abs(
//...
```
## Исправление сигналов с Z-состоянием
## Fixing Signals in the Z-State
Мы видим, что сигнал `min` подключен к выходу `min` объекта `max_min_unit` модуля `max_min`. Добавим сигналы этого модуля на временную диаграмму. Для этого, необходимо раскрыть список объектов, содержащихся в объекте `dut` иерархии объектов `Scope` и выбрать там объект `max_min_unit`.
We can see that signal `min` is connected to the `min` output of the `max_min_unit` instance of module `max_min`. Let us add the signals of this module to the waveform. To do so, expand the list of objects inside the `dut` instance in the `Scope` hierarchy and select the `max_min_unit` object.
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_17.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_17.png)
_Рисунок 17. Добавление сигналов вложенных модулей на временную диаграмму._
_Figure 17. Adding signals from a submodule to the waveform._
Добавляем внутренние сигналы на временную диаграмму, группируем их под именем `max_min`, и повторяем моделирование.
Add the internal signals to the waveform, group them under the name `max_min`, and re-run the simulation.
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_18.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_18.png)
_Рисунок 18. Результат добавления и группировки сигналов подмодуля `max_min`._
_Figure 18. Result of adding and grouping signals from the `max_min` submodule._
Произошло что-то странное: все внутренние сигналы объекта `max_min_unit` "зеленые" (не имеющие X или Z состояния), однако подключенный к выходу этого модуля сигнал `min` находится в Z-состоянии. Как такое могло произойти?
Something strange happened: all internal signals of the `max_min_unit` instance are "green" (no X or Z states), yet the signal `min` connected to this module's output is in the Z-state. How is that possible?
Если присмотреться к сигналу `min`, находящемуся в Z-состоянии, можно заметить, что младшая цифра находится не в Z-состоянии, а в состоянии `0`, такое же значение стоит и на сигнале `min` объекта `max_min_unit`. Это интересно.
If you look closely at the `min` signal in the Z-state, you will notice that its least significant digit is not in the Z-state but shows `0` — the same value shown by the `min` signal of the `max_min_unit` instance. Interesting.
Если присмотреться к этим двум сигналам еще пристальней, то можно увидеть, что у сигнала `min` объекта `dut` разрядность 32 бита, в то время как разрядность сигнала `min` объекта `max_min_unit` составляет 4 бита.
Looking even more closely at these two signals, you can see that the `min` signal of the `dut` instance is 32 bits wide, while the `min` signal of the `max_min_unit` instance is only 4 bits wide.
Это и является проблемой: мы подключили 4 бита 4-разрядного сигнала `min` к младшим 4 битам 32-разрядного сигнала `min`, а остальные разряды остались не подключенными.
This is the problem: we connected the 4 bits of a 4-bit `min` signal to the lower 4 bits of a 32-bit `min` signal, leaving the remaining bits unconnected.
По всей видимости, при написании модуля `max_min`, была указана неверная разрядность сигнала `min`, вместо `31` было написано `3`. Исправим это и повторим моделирование.
Apparently, when writing the `max_min` module, the width of the `min` signal was specified incorrectly: `3` was written instead of `31`. Let us fix this and re-run the simulation.
> [!IMPORTANT]
> Обратите внимание, что поскольку мы изменили исходный код, в этот раз необходимо нажать на кнопку `Relaunch Simulation`, поскольку нужна повторная компиляция проекта.
> Note that since we modified the source code, this time we must click `Relaunch Simulation` to trigger recompilation of the project.
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_19.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_19.png)
_Рисунок 19. Результат моделирования после исправления разрядности сигнала `min`._
_Figure 19. Simulation result after fixing the bit-width of signal `min`._
В логе сообщается о 102 найденных ошибках. Ровно на одну ошибку меньше, чем было ранее. Это не означает, что в проекте осталось 102 ошибки, только то, что, исправив данную ошибку — мы действительно что-то исправили, и один из тестовых сценариев, который ранее завершался ошибкой, теперь завершился без неё.
The log now reports 102 errors — exactly one fewer than before. This does not mean there are 102 bugs remaining; it simply confirms that fixing this particular bug actually changed something, and one test scenario that previously failed now passes.
Помните, что если в проекте много ошибок, то часть ошибок может выправлять поведение других ошибок (хоть и не всегда, но иногда минус на минус может выдать плюс в контексте ошибок проекта), поэтому надо с осторожностью полагаться на число найденных ошибок, если это число больше нуля.
Keep in mind that when a project has many bugs, some bugs may be masking the effects of others (two wrongs can sometimes make a right in the context of bug interactions), so be cautious about relying on the error count when it is greater than zero.
Посмотрим на нашу временную диаграмму снова, и выберем дальнейшие действия:
Let us look at the waveform again and decide on the next steps:
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_20.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_20.png)
_Рисунок 20. Временная диаграмма после исправления разрядности сигнала `min`._
_Figure 20. Waveform after fixing the bit-width of signal `min`._
Мы видим, что на временной диаграмме не осталось сигналов в X или Z-состоянии, а значит мы собрали все "низковисящие" улики нашего с вами расследования. Вернемся к месту преступления и попробуем поискать новые улики:
We see that no signals remain in the X or Z state, meaning we have collected all the "low-hanging fruit" in our investigation. Let us return to the scene of the crime and look for new clues:
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_21.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_21.png)
_Рисунок 21. Первая ошибка в новом логе моделирования._
_Figure 21. First error in the new simulation log._
## Поиск ошибки в сигналах, формирующих проблемный сигнал
## Tracing the Bug Through the Signals Driving the Problematic Signal
Мы видим, что первой ошибкой в логе стала не та ошибка, что была прежде. Раньше первый неверный результат мы видели в момент времени `5ns`, когда на схему подавались значения `0` и `0`, теперь же первой ошибкой стал момент времени `10ns`, когда на схему подаются значения `1` и `1`. Наше устройство считает, что результат должен равняться `3`, в то время как модель считает, что результат должен равняться `1`. Проверим, нет ли ошибки в модели и посчитаем результат самостоятельно:
The first error in the log is now different from before. Previously, the first incorrect result appeared at time `5ns` with inputs `0` and `0`; now the first error occurs at `10ns` with inputs `1` and `1`. Our circuit computes the result as `3`, while the reference model says it should be `1`. Let us compute it manually to verify the model:
Для определения приблизительной длины вектора в евклидовом пространстве (т.е. длины гипотенузы прямоугольного треугольника, которая равна квадратному корню из суммы квадратов катетов) можно воспользоваться формулой:
To approximate the Euclidean magnitude of a vector (i.e., the hypotenuse of a right triangle, equal to the square root of the sum of the squares of its legs), we use the formula:
`sqrt(a^2 + b^2) ≈ max + min/2`, где `max` и `min` — большее и меньшее из пары чисел соответственно [**Ричард Лайонс: Цифровая обработка сигналов, стр. 475**].
`sqrt(a^2 + b^2) ≈ max + min/2`, where `max` and `min` are the larger and smaller of the pair, respectively [**Richard Lyons: Understanding Digital Signal Processing, p. 475**].
Подставим наши числа в формулу (поскольку оба числа равны, не важно какое из них будет максимумом, а какое минимумом):
Substituting our values (since both numbers are equal, it does not matter which is max and which is min):
```text
1 + 1/2 = 1.5
```
Ни модель, ни схема не правы?
So neither the model nor the circuit is correct?
На самом деле, наше устройство поддерживает только целочисленную арифметику, поэтому результат будет:
Actually, our device supports only integer arithmetic, so the result is:
```text
1 + 1/2 = 1 + 0 = 1
```
Модель правильно отразила особенность нашего устройства и дала корректный результат.
The model correctly accounted for this property of our device and produced the correct result.
Значит надо смотреть как формируется результат в нашем устройстве. Посмотрим на выход `abs` в модуле `vector_abs`:
So we need to look at how the result is computed inside our circuit. Let us inspect the `abs` output in module `vector_abs`:
```Verilog
assign abs = max + min_half;
```
Выход `abs` зависит от двух внутренних сигналов: `max` и `min_half`. В соответствии с нашим алгоритмом, либо проблема в логике, связывающей эти два сигнала (операции сложения), либо в значении какого-то из этих сигналов, либо комбинации этих вариантов.
The `abs` output depends on two internal signals: `max` and `min_half`. According to our algorithm, the problem is either in the logic connecting these two signals (the addition operation), in the value of one of them, or a combination of both.
Изучив модуль, мы понимаем, что в логике этого присваивания проблем нет, т.к. оно повторяет логику формулы `max + min/2`, складывая максимум с половиной минимума. Значит проблема в значении какого-то из этих сигналов (или обоих из них). Посчитаем значения этих сигналов самостоятельно (для сложного проекта эти значения посчитала бы модель):
Examining the module, we conclude that the assignment logic is correct — it implements `max + min/2` by adding the maximum to half the minimum. So the problem must be in the value of one (or both) of these signals. Let us compute the expected values ourselves (in a complex project, this would be done by the reference model):
`1` и `0`.
`1` and `0`.
Смотрим, какие значения установлены на сигналах `max` и `min_half` в момент времени `10ns`.
Now let us check the actual values of `max` and `min_half` at time `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` (интересующие нас сигналы выделены зелёным)_
_Figure 22. Values of signals `max` and `min_half` at time `10 ns` (signals of interest highlighted in green)_
> [!IMPORTANT]
> Обратите внимание: вы можете менять цвета сигналов временной диаграммы через контекстное меню выделенных сигналов.
> Note: you can change the colors of waveform signals through the context menu of the selected signals.
Мы видим, что в момент времени `10 ns` значения `max` и `min_half` изменились как `1 -> 4` и `2 -> 8` соответственно. Нас интересуют значения `1` и `2`, т.к. в момент времени `10ns` на выходе схемы в этот момент был установившийся результат для предыдущих значений (еще не успел посчитаться результат для новых значений).
We see that at time `10 ns`, the values of `max` and `min_half` transition as `1 -> 4` and `2 -> 8` respectively. We are interested in the values `1` and `2`, since at time `10ns` the circuit's output still holds the settled result for the previous inputs (the output for the new inputs has not yet been computed).
Значение `max=1` совпадает с ожидаемым, в то время как `min_half=2` явно нет.
The value `max=1` matches the expected value, while `min_half=2` clearly does not.
Мы нашли причину неправильного вычисления результата: и правда, `1+2=3`, теперь необходимо найти ошибку в вычислении сигнала `min_half`.
We have identified the cause of the incorrect result: indeed, `1+2=3`. Now we need to locate the bug in the computation of signal `min_half`.
Как и с сигналом `abs`, необходимо определить сигналы, влияющие на значение сигнала `min_half`. Данный сигнал подключен к выходу `quotient` модуля `half_divider`, поэтому мы будем смотреть исходный код данного модуля:
As with signal `abs`, we need to identify the signals that drive `min_half`. This signal is connected to the `quotient` output of the `half_divider` module, so let us inspect its source code:
```Verilog
module half_divider(
@@ -337,41 +339,41 @@ module half_divider(
endmodule
```
Что делает данный модуль? Он принимает на вход значение и делит его на два. На вход данного модуля будет приходить значение минимума из нашей формулы.
What does this module do? It receives a value and divides it by two. The minimum value from our formula is fed to its input.
Выход данного модуля зависит от входа `numerator` и логики сдвига влево на 1. Это значит, что проблема либо в логике, либо в значении, подаваемом на вход. Выведем сигнал `numerator` на временную диаграмму и посмотрим на его значение в момент времени `10ns.
The output of this module depends on the `numerator` input and a left-shift-by-1 operation. So the problem is either in the logic or in the value being fed to the input. Let us add the `numerator` signal to the waveform and check its value at time `10ns`.
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_23.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_23.png)
_Рисунок 23. Значение сигнала `numerator` в момент времени `10 ns`._
_Figure 23. Value of signal `numerator` at time `10 ns`._
Мы помним, что в момент, когда схема начала выдавать неправильный результат, на его входы подавались числа `1` и `1`, это значит, что на вход `numerator` пришло корректное значение: минимум из этих двух чисел и правда равен `1`. Проверим логику данного модуля.
We recall that when the circuit started producing an incorrect result, the inputs were `1` and `1`, so the `numerator` received the correct value: the minimum of the two numbers is indeed `1`. Let us now check the module's logic.
## Исправление логики проблемного сигнала
## Fixing the Logic of the Problematic Signal
Операция деления в цифровой схемотехнике является очень "дорогой" в плане ресурсов логических блоков и критического пути, поэтому этой операции часто стараются избегать. В нашем случае, нам не нужно обычное деление — нам нужно деление только напополам. В двоичной арифметике, для того чтобы разделить число на два, достаточно отбросить его младшую цифру. Вы часто пользуетесь подобной операцией в повседневной жизни при выполнении операции деления на 10: отбрасываете младшую цифру в десятичной арифметике.
Division is a very "expensive" operation in digital logic in terms of resources and critical path, so it is often avoided. In our case, we do not need general-purpose division — we only need to divide by two. In binary arithmetic, dividing a number by two is equivalent to discarding its least significant bit. You routinely do the same in decimal arithmetic when dividing by 10: you simply drop the last digit.
Именно поэтому, когда мы в первый раз пытались посчитать результат "на бумаге", у нас было расхождение с моделью: когда мы делим 1 на 2, мы получаем 0.5, однако деление путем отбрасывания цифры округляет результат вниз (1/2=0, 15/10=1).
This is exactly why our first manual calculation differed from the model: dividing 1 by 2 gives 0.5, but discarding the last digit rounds the result down (1/2 = 0, 15/10 = 1).
Как "отбросить" цифру средствами цифровой логики? Для этого используется операция сдвига вправо.
How do we "discard a digit" in digital logic? We use the right-shift operation.
Операция сдвига вправо в **SystemVerilog** записывается оператором `>>`. Справа от оператора указывается число "отбрасываемых цифр", в нашем случае одна. Но постойте, в логике присваивания стоит оператор `<<`. Это ошибка, исправим ее!
The right-shift operator in **SystemVerilog** is `>>`. The number of bits to shift (i.e., digits to discard) is specified to the right of the operator — in our case, 1. But wait — the assignment currently uses the `<<` operator. That is the bug; let us fix it!
Повторяем моделирование.
Re-run the simulation.
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_24.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_24.png)
_Рисунок 24. Результат моделирования после исправления оператора сдвига._
_Figure 24. Simulation result after fixing the shift operator._
Снова на одну ошибку меньше. Не унываем, вряд ли в проекте число ошибок больше, чем число непустых строк самого проекта. Возвращаемся к начальной ошибке:
One fewer error again. Do not be discouraged — the number of bugs in a project is unlikely to exceed the number of non-empty lines in the code. Let us return to the first error:
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_25.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_25.png)
_Рисунок 25. Первая ошибка в повторном моделировании._
_Figure 25. First error in the re-run simulation._
Мы продвинулись во времени безошибочного моделирования до `15 ns`, начинаем наше расследование с начала:
We have now advanced the error-free simulation time to `15 ns`. Let us start our investigation from the beginning:
На вход схемы подаются значения `3` и `4`, схема считает, что результатом вычисления `max + min/2` будет `2`, модель считает, что `5`. Посчитаем сами:
Inputs `3` and `4` are applied to the circuit. The circuit thinks the result of `max + min/2` is `2`, but the model says it should be `5`. Let us compute manually:
```text
max=4
@@ -379,17 +381,17 @@ min=3
max + min/2 = 4 + 3/2 = 4 + 1 = 5
```
И снова модель выдала правильный результат. Разберемся в значениях сигналов, формирующих сигнал `abs`.
Once again, the model produced the correct result. Let us examine the values of the signals that form the `abs` output.
## Проблема необъявленных сигналов
## The Problem of Undeclared Signals
К этому моменту на вашей временной диаграмме скорей всего стало уже очень много сигналов. Уберем лишние, оставив только внутренние сигналы модуля `vector_abs` (для этого выделяем ненужные сигналы, и удаляем их с помощью клавиши `Delete`).
By this point, the waveform likely has many signals. Remove the unnecessary ones, keeping only the internal signals of module `vector_abs` (select the unwanted signals and delete them with the `Delete` key).
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_26.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_26.png)
_Рисунок 26. Поведение внутренних сигналов модуля `vector_abs` на временной диаграмме._
_Figure 26. Behavior of internal signals of module `vector_abs` on the waveform._
В глаза сразу же бросается, что сигнал `max` внешне отличается от всех остальных — он ведет себя как 1-битный сигнал. Если все остальные сигналы 32-разрядные, то и сигнал `max` должен быть таким же. Перейдем к объявлению этого сигнала, чтобы это исправить (нажав правой кнопкой мыши, и выбрав `Go To Source Code`):
It is immediately apparent that signal `max` looks different from all the others — it behaves like a 1-bit signal. If all other signals are 32-bit, `max` should be as well. Let us navigate to the declaration of this signal to fix it (right-click and select `Go To Source Code`):
```Verilog
module vector_abs(
@@ -411,30 +413,30 @@ module vector_abs(
//...
```
Это странно, курсор был установлен на строку `.max(max)`, хотя раньше в этом случае курсор устанавливался на строку, где объявлялся выбранный сигнал. Но вот в чем дело, если мы просмотрим файл внимательно, то не обнаружим объявления сигнала вовсе. Как так вышло, что мы использовали необъявленный сигнал, а САПР не выдал нам ошибку? Дело в том, что стандарт [IEEE 1364-2005](https://ieeexplore.ieee.org/document/1620780) для языка **SystemVerilog** допускает подобное использование необъявленного сигнала. В этом случае, синтезатор неявно создаст одноименный одноразрядный сигнал, что и произошло.
This is strange — the cursor was placed on the line `.max(max)`, whereas previously it was placed on the line where the selected signal was declared. The reason is that if we look through the file carefully, we find no declaration of this signal at all. How did we use an undeclared signal without the EDA tool reporting an error? The [IEEE 1364-2005](https://ieeexplore.ieee.org/document/1620780) standard for **SystemVerilog** permits this usage. In such a case, the synthesizer implicitly creates a 1-bit signal with the same name — which is exactly what happened.
Для исправления этой ошибки, объявим сигнал `max` с корректной разрядностью и повторим моделирование.
To fix this error, declare the signal `max` with the correct bit-width and re-run the simulation.
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_27.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_27.png)
_Рисунок 27. Результат моделирования после объявления пропущенного сигнала._
_Figure 27. Simulation result after declaring the missing signal._
## Самостоятельная работа
## Independent Exercise
Число ошибок сократилось до 40! Мы явно на верном пути. Повторяем предыдущие шаги, вернувшись к первой ошибке:
The error count dropped to 40! We are clearly on the right track. Repeat the previous steps, returning to the first error:
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_28.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_28.png)
_Рисунок 28. Первая ошибка в повторном моделировании._
_Figure 28. First error in the re-run simulation._
В этот раз первая ошибка осталась прежней, только теперь схема считает, что результат должен равняться шести (в прошлый раз схема выдавала `2`). Мы уже убедились, что в этом случае модель дает правильный результат, поэтому сразу перейдем к формирующим результат сигналам:
This time the first error is the same, except now the circuit computes the result as six (previously it returned `2`). We have already confirmed that the model gives the correct result here, so let us go straight to the signals that form the output:
![../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_29.png](../.pic/Vivado%20Basics/05.%20Bug%20hunting/fig_29.png)
_Рисунок 29. Поведение внутренних сигналов модуля `vector_abs` на временной диаграмме._
_Figure 29. Behavior of internal signals of module `vector_abs` on the waveform._
Видим, что значение сигнала `min_half`, формирующего значение выхода `abs` неверно (минимумом из `3` и `4` является `3`, `3/2 = 1`).
We can see that the value of signal `min_half`, which contributes to output `abs`, is incorrect (the minimum of `3` and `4` is `3`, and `3/2 = 1`).
Не отходя далеко от кассы, мы замечаем, что значение `min`, формирующее сигнал `min_half` неверно: его значение `4`, а должно быть `3`.
Looking closely, we also notice that the value of `min`, which drives `min_half`, is incorrect: it is `4`, but should be `3`.
Используя файлы исходного кода [проекта](./vector_abs/), попробуйте разобраться в последней обнаруженной нами ошибке.
Using the source files of the [project](./vector_abs/), try to identify the last bug we found.