2022-08-09 09:50:11 +03:00

217 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

UTest
=====
UTest - приложение для одиночного или группового запуска и контроля выполнения тестбенчей на языке Verilog/SystemVerilog.
На данный момент реализована поддержка только [Icarus Verilog](http://iverilog.icarus.com/), но в дальнейшем
планирую добавить поддержку [Verilator](https://www.veripool.org/verilator/) и [SymbiYosys](https://github.com/YosysHQ/sby).
Параметры командной строки
--------------------------
Если вызвать приложение с параметром `--help`, она выведет текст справки с описанием параметров:
```
Usage: ./utest.scm [OPTION]... [FILE|PATH]
Run testbenches with recursive search in the PATH, or in the current folder
if PATH is not specified. If argument is a file, testbench is launched from FILE.
Options:
-k, --keep Do not delete work directory if test is pass.
-d, --dump Force dump waveforms.
-r, --norestart Do not restart testbench with waveform dump enabled if
test failed (true by default)
-s, --static Use static work dir for initial debug purposes
-n, --nocolor Do not use color for print log
-j, --jobs NUM Use NUM threads for running testbenches. If <=0
use as many threads as there are processors in the system.
-f, --defines Print useful Verilog defines
-c, --clean Delete work folders that have a corresponding makefile.
--force-clean Delete all work folders regardless of the presence of a makefile.
-v, --verbose Verbose output
-h, --help Print this message and exit
```
При успешном завершении теста программа по удаляет рабочую папку. Запретить это делать можно опцией `--keep`.
По умолчанию, для ускорения прохождения тестов создание дампов сигналов отключено. При этом, в случае неудачного
завершения тест автоматически перезапускается с созданием дампа. Однако, если тест длительный, потеря времени на
перезапуск может перекрыть выигрыш от несоздания дампа. Для управления этим поведением в программе есть две
опции - `--dump` и `--norestart`. Опция `--dump` заставляет программу делать дампы для всех тестов, как удачных, так
и неудачных, что позволяет избежать перезапуска для создания дампа. Опция `--no-restart` запрещает перезапуск неудачных
тестов, что позволяет максимально сэкономить время, но оставляет без дампов.
Чтобы старые рабочие папки не засоряли файловую систему, их можно удалить запуском программы с опцией `--clean`.
Программа удалит все папки, для которых есть сценарий тестбенча. Если файла сценария больше нет, то можно воспользоваться
опцией `--force-clean`.
По умолчанию программа запускает тесты в нескольких потоках, количество которых зависит от числа процессоров в системе.
Опцией `--jobs` можно управлять количеством потоков - от одного и более.
Параметр `--verbose` управляет количеством информации, которая будет выведена на экран в процессе тестирования. Без неё
для успешных тестов будет выведена только важные сообщения. С этой опцией для всех тестов будет показан полный лог
вызова компилятора и симулятора, а так же все строки, которые будут выведены тестбенчем. При этом, независимо от этой опции
весь вывод тестбенчей будет сохранён в логи в рабочих папках (если указано опция `--keep` или тест завершился неудачей).
Опция `--nocolor` отключает раскраску вывода. Это может быть полезно в случае перенаправления вывода в файл.
Опция `--defines` выводит на экран исходник include-файла с макросами вывода информационных сообщений в тестбенче на Verilog:
```verilog
`ifndef UTEST_VERILOG_DEFINES
`define UTEST_VERILOG_DEFINES
// Log level string prefixes for use with $display function.
// Example usage: $display("%sError message", `LOG_ERR);
`define LOG_INFO "INFO#"
`define LOG_WARN "WARN#"
`define LOG_ERR "FAIL#"
// Dirty hacked redefine of $display function. Must be used with two parentheses.
// Example usage: `log_info(("Information message"));
`define log_info(msg) begin $display({`LOG_INFO, $sformatf msg}); end
`define log_warn(msg) begin $display({`LOG_WARN, $sformatf msg}); end
`define log_error(msg) begin $display({`LOG_ERR, $sformatf msg}); end
`endif
```
Эти макросы можно применять в тестбенче на Verilog для вывода информационных сообщений, в частности для оповещения
UTest об ошибке в симуляции. К сожалению, в Icarus Verilog это пока единственная возможность сообщить об ошибке в тесте,
т.к. при вызове функций `$error` и `$fatal` симулятор возвращает нулевой exit code. В следующей версии авторы обещают
исправить это досадное недоразумение.
Сценарии запуска тестбенчей
---------------------------
Программа рекурсивно ищет все файлы с расширением `*.utest` в папке `PATH` и использует их в качестве сценариев запуска
тестбенчей. Если параметром указать файл, то будет использован только этот файл. Если запустить программу без параметров,
она будет искать сценарии в текущей папке.
Сценарии запуска тестбенчей описываются на языке Scheme (а именно [Guile](https://www.gnu.org/software/guile/), на котором
и написана программа), и представляют из себя скрипт, который возвращает функцию (или список функций). Эта функция
(функции) будет вызвана в процессе запуска тестов, и в зависимости от результатов её выполнения тест будет помечен как
успешный или неуспешный. Функция вызывается в контексте приложения, по этому ей доступны все переменные и функции,
объявленные в коде приложения.
Функция возвращает булево значение, и имеет один опциональный аргумент типа symbol.
Если функция вызвана без аргументов, она должна выполнить тестбенч и вернуть `#true` или `#false` в зависимости
от результата его выполнения. Если функции передается агрумент, то она должна вернуть некоторые метаданные в зависимости
от значения аргумента, или `#false` если таких метаданных нет. Сейчас используется два типа метаданных - название теста
(агрумент `'name`) и описание теста (аргумент `'description`).
Для того, чтобы не писать вручную диспетчеризацию аргументов в приложении есть макрос `utest/tb`, который упрощает
описание сценариев:
```scheme
(utest/tb
("Name"
"First line of testbench description"
"Second line of testbench description")
;; testbench body
)
```
После имени макроса в скобках указывается имя тестбенча и произвольное количество строк описания. Если аргументов нет,
тестбенч будет помечен как `noname`. Имя тестбенча используется для именования рабочей папки, при этом пробелы заменяются
на подчеркивания, а заглавные буквы на прописные. Желательно не употреблять в имени тестбенча специальные символы и не
использовать слишком длинные имена.
После скобочек с именем и описанием следует тело тестбенча. Тело - это любой валидный код на Guile, который будет выполнен
в процессе запуска тестбенчей. Результат работы этого кода должно быть булево значение, показывающее успешность выполнения
теста. Весь вывод в `stdout` будет перехвачен и отфильтрован в соответствии со значением флага `--verbose` и типом
выводимого текста.
Для типизации вывода можно использовать функцию `utest/log`. Первый параметр функции - тип выводимого
текста (опциональный), второй - строка формата как в функции `format`. Возможны три варианта значения типа: `'info`,
`'warning` и `'error`. Сообщения всех трёх типов будет выведено на экран вне зависимости от флага `--verbose`, но если
тип не указать, сообщение будет выведено только в режиме `--verbose`. Независимо от типа сообщения и флага `--verbose` все
сообщения будут сохранены в логе в рабочей папке теста.
Тесты можно собрать в список для запуска нескольких вариантов с разными параметрами, дефайнами, исходниками или другими
опциями.
В теле тестбенча в общем случае можно выполнять любые действия, не только запуск симуляции RTL. Но т.к. программа была
написана предназначена для симуляции RTL, в ней есть некоторые полезные для этого функции.
Запуск симуляции в Icarus Verilog выполняется с помощью функции `utest/run-simulation-iverilog`. Функция принимает два
обязательных параметра и несколько опциональных:
```scheme
(utest/run-simulation-iverilog
sources ; List of source files
top ; Name of the top module
#:iverilog-executable "iverilog" ; Icarus Verilog compiler executable path
#:vvp-executable "vvp" ; Icarus Verilog simularot executable path
#:modpaths '() ; iverilog -y
#:modtypes '(".v" ".sv") ; iverilog -Y
#:includes '() ; ivetilog -I
#:lang "2012" ; Language version (1995, 2001, 2005, 2005-sv, 2009, 2012)
#:parameters '() ; Top module parameters (list (list NAME VALUE))
#:defines '() ; Preprocessor defines (list NAME|(list NAME VALUE))
#:features '() ; iverilog -g
#:separate #f ; iverilog -u
#:plusargs '() ; List of plusargs
#:vpimods '() ; iverilog -m, vvp -m
#:vpipaths '() ; iverilog -L, vvp -M
#:warnings "all" ; List of warning classes (iverilog -W...)
#:dumpformat 'fst ; Format of dump files. Posibly values: vcd, lxt, lxt2, fst
#:timeout '(1 ms) ; Testbench execution timeout (in simulation time)
)
```
*Все аргументы, кроме `parameters` и `defines`, которые принимают список, так же могут принимать и одиночные значения.
В список `includes` по-умолчанию включена папка с файлом сценария*
Функция возвращает `#true` в случае успешного завершения теста, и `#false` в случае ошибки. Далее приведен пример использования:
```scheme
(map
(lambda (n data-width)
(utest/tb
((format "uart_tx_test~a" n)
"Testbench for the UART transmitter RTL code"
(format "DATA_WIDTH = ~a" data-width))
(utest/log 'info "Example information message")
;; Start simulation
(utest/run-simulation-iverilog
'("uart_tx.sv" "uart_tx_tb.sv")
"uart_tx_tb"
#:includes "."
#:parameters `((DATA_WIDTH ,data-width))
#:defines '(TESTBENCH)
#:timeout '(5 ms))))
'(1 2 3)
'(8 16 24))
```
Сценарий возвращает три теста с разным значением параметра DATA_WIDTH. В случае успешного выполнения на экран будет выведен
следующий текст:
![output example](output-example.png)
В случае неудачного завершения теста тест перезапускается с дампом всех сигналов в файл (это поведение можно отключить
опцией `--no-restart`). Кроме того, в случае ошибки будет выведен полный лог симулятора и его параметры.
Все относительные пути в аргументах функции `utest/run-simulation-iverilog` считаются относительными папке со сценарием.
Тестбенч Verilog
----------------
Тестбенч на Verilog ни чем не отличается от самого обычного тестбенча, кроме того, что он должен сигнализировать об ошибке
с помощью вывода информационного сообщения `log_error((...))`.
Для удобства работы программа предопределяет два макроопределения:
- `UTEST_BASE_DIR` - макрос с путём к папке, в которой находится сценарий запуска. Может быть нужен, например для указания
на файл с входными данными для теста.
- `UTEST_WORK_DIR` - путь ко временной рабочей папке теста. Сюда можно сохранить результаты тестбенча для последующей
проверки в коде сценария.
Примеры
-------
В папке [examples](examples) находятся примеры сценариев и тестбенча.