Отладка Verilog-схем: от сообщения об ошибке до виноватого провода

В цикле «От кода к кремнию» мы писали модули, подключали шины и тактировали регистры. Эта статья о следующем навыке: схема не собралась, симулятор ругается, и нужно быстро понять, что именно не так. Редактор Verilog в симуляторе содержит набор инструментов отладки, и о каждом из них стоит знать до того, как они понадобятся.

Сообщения об ошибках на русском

Ошибки парсера, предупреждения и сообщения симулятора выводятся на русском языке. Каждое сообщение называет конкретную причину: имя сигнала, номер строки и то, что парсер ожидал увидеть. Вместо загадочного syntax error вы прочтёте что-то вроде «строка 7: ожидалось имя, а встретилось число».

Незнакомый токен в сообщении расшифрован словами: «имя», «число», «символ», «ключевое слово». Если парсер споткнулся о 2x там, где ждал имя провода, сообщение скажет об этом прямо.

Клик по ошибке — переход к строке

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

Текущая строка редактора подсвечена фоном, поэтому после прыжка легко сориентироваться, даже если ошибка была в конце длинного модуля.

Предупреждения при наборе

Две самые частые «тихие» ошибки Verilog — объявил провод и забыл подключить, либо подключил выход, но не объявил провод. Редактор помечает такие случаи предупреждением прямо во время редактирования, до запуска теста:

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

Автодополнение и сниппеты

Наберите первые буквы имени — редактор предложит порты и провода текущего модуля. Для шины список содержит варианты с индексами: bus[0], bus[1] и так далее до старшего разряда. Автодополнение работает и внутри индексной скобки: наберите bus[, и список разрядов откроется сам.

В многофайловом проекте список пополняется модулями самого проекта: набрав FullAd, вы увидите FullAdder(a, b, cin, sum, cout) со всем позиционным списком портов. Наведение мыши работает не только на примитивах: подсказка с порядком портов всплывает и для встроенных модулей RAM256/ROM, и для модулей проекта — входы отделяются от выходов стрелкой. Открывающая скобка закрывается автоматически, парные скобки подсвечиваются, поэтому вложенные портовые списки набираются без потерь.

Из списка автодополнения вставляются заготовки: каркас module, блок generate for и банк из восьми dff для регистра. Tab принимает подстановку.

Побитовое сравнение шин

Когда тест не проходит на шине, смотреть два двоичных числа глазами утомительно. Таблица результатов подсвечивает несовпавшие разряды по отдельности: если из восьми битов верны семь, вы сразу увидите, какой именно бит уехал и в каком столбце таблицы. Над списком сообщений выводится краткий итог — «Ошибки: N · Предупреждения: M», а вкладки файлов многофайлового проекта несут счётчики: красный бейдж для ошибок, янтарный для предупреждений, видный даже когда файл не открыт.

Строки с внутренними сигналами помечены курсором-указателем. Клик по такой строке раскрывает дамп всех проводов схемы на этом такте. Переключатель BIN/HEX живёт в заголовке таблицы и меняет систему счисления сразу для таблицы и всех открытых дампов: совпавшие шины показываются в hex — удобно сравнивать с числами из условия, несовпавшие — всегда двоичными, чтобы побитовая подсветка не терялась.

Маркер первого сбойного такта

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

Пример: счётчик правильно считает первые пять тактов, а на шестом вместо 6 показывает 8. Маркер укажет на шестой такт, дамп проводов покажет значения на нём, и обычно виноват оказывается один неправильно подключённый разряд.

Цикл отладки

Инструменты складываются в короткий цикл:

Правило «одно изменение за прогон» работает и здесь: после каждого исправления ясно, что именно его починило.

Проверь себя

Что означает предупреждение undriven?

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

Почему маркер первого сбоя важнее последней ошибочной строки таблицы?

Последовательная схема «помнит» предыдущие такты: состояние накапливается. Первый сбойный такт — момент, где логика впервые разошлась с ожиданием; всё после него — последствия этого первого расхождения.

Как автодополнение помогает избежать опечатки в имени провода?

Список строится из реально объявленных портов и проводов модуля, поэтому выбор из списка исключает опечатку вроде dtaa[3] вместо data[3], которую парсер иначе сообщит как неизвестный идентификатор.

Что дальше

Отладка завершает цикл «От кода к кремнию»: писать модули, соединять их шинами, тактировать и находить ошибки. Следующая статья выводит проект за пределы симулятора — в ZIP-архив для платы Tang Nano 9K.

Попробовать в симуляторе →