Русский
Русский
English
Статистика
Реклама

Прокачиваем скрипты симуляции HDL с помощью Python и PyTest

Все делают это. Ну ладно, не все, но большинство. Пишут скрипты, чтобы симулировать свои проекты на Verilog, SystemVerilog и VHDL. Однако, написание и поддержка таких скриптов часто бывает довольно непроста для типично используемых Bash/Makefile/Tcl. Особенно, если необходимо не только открывать GUI для одного тестбенча и смотреть в диаграммы, но и запускать пачки параметризированных тестов для различных блоков, контролировать результат, параллелизировать их выполнение и т.д. Оказалось, что всё это можно закрыть довольно прозрачным и легко поддерживаемым кодом на Python, что мне даже обидно становится от того, как я страдал ранее и сколько странного bash-кода родил.

Конечно, я не первый кто задумывается о подобном. Уже даже существует целый фреймворк VUnit. Однако, как показывает практика и опросы в профильных чатах, такие фреймворки используются нечасто. Вероятно потому, что они предъявляют требования к внутренней структуре самих тестбенчей, с чем наверное можно мириться только на новых проектах, без обширной кодовой базы. Ну и вообще, куда ж без таких вещей как "у нас так не принято" и "not invented here".

Собственно, речь пойдет не о написании очередного фреймворка, а просто о замене одних скриптов симуляции на другие, более дружелюбные к расширению и поддержке. Без каких-либо требований к коду, тестбенчам и архитектуре их построения.

Задачи

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

  • Одиночный запуск. Запустить симуляцию выбранного тестбенча, посмотреть временные диаграммы, внести коррективы, повторить.

  • Одиночный запуск без GUI. То же самое, но без временных диаграмм, оценить результат по выводу в консоль.

  • Параметризированный запуск. Запуск симуляции с GUI или без, но с параметрами (дефайнами), передаваемыми в скрипт из консоли.

  • Запуск с пре-/постпроцессингом. Например, для теста должны быть подготовлены данные. Или сам тест порождает данные, которые должны быть проверены вне HDL.

  • Массовый запуск. Прогнать симуляцию всех существующих тестбенчей.

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

  • Параллельные запуски. Тесты могут легко идти минуты/часы и последовательное исполнение может занимать слишком много времени. Скрипты должны позволять параллельное исполнение нескольких тестбенчей.

  • Поддержка нескольких симуляторов. Сделать так, чтобы всё вышеперечисленное работало в разных симуляторах.

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

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

Идея

Все симуляторы запускаются примерно одинаково в большинстве случаев:

  • собираем список всех исходников (опционально делим на несколько списков по языку);

  • собираем список всех директорий для поиска исходников (нужно для include);

  • собираем список всех дефайнов;

  • сообщаем имя библиотеки, куда всё будем компилировать (или нескольких);

  • сообщаем имя верхнего модуля (обычно это имя тестбенча);

  • передаём это всё симулятору в виде ключей, файлов со списками и т.д.

А что если написать модуль на Python, в котором обернуть нужные симуляторы в один класс Simulator, вынести общие вещи в атрибуты и реализовать метод run(), который запустит симуляцию с помощью выбранного симулятора? В целом, именно это я и сделал для Icarus Verilog, Modelsim и Vivado Simulator, используя модуль subprocessпод капотом. Также я добавил класс CliArgs, основанный на модуле argparse, чтобы иметь возможность управлять запуском из консоли. Ну и написал некоторое количество вспомогательных функций, которые пригодятся в процессе. Получился файл sim.py.

Фактически, я постарался свести всё к тому, что в новом проекте нужно всего-лишь закинуть этот файл, создать рядом еще один скрипт на Python, импортировать необходимое из sim.py и начать работу.

Тестовый проект

Для демонстрации я вытянул модуль пошагового вычисления квадратного корня из одного старого проекта, чтобы тестовый дизайн был хоть чуточку сложнее счётчика или сумматора. Код основан на публикации An FPGA Implementation of a Fixed-Point Square Root Operation.

Репозиторий проекта pyhdlsim на GitHub.

Иерархия проекта проста:

$ tree -a -I .git. .github    workflows # Github Actions        icarus-test.yml # запуск всех тестов в Icarus Verilog после каждого пуша на github        modelsim-test.yml # запуск всех тестов в Modelsim после каждого пуша на github .gitignore LICENSE.txt README.md sim # скрипты для запуска симуляции    conftest.py    sim.py    test_sqrt.py src # исходники     beh # поведенчесие описания и модели        sqrt.py     rtl # синтезируемый HDL код        sqrt.v     tb # HDL код тестбенчей         tb_sqrt.sv

Сам тестбенч tb_sqrt.sv тоже довольно примитивен: подготавливается массив входных значений, вычисляются "идеальные" значения с помощью $sqrt(), входные значения проталкиваются в модуль корня, выходные значения сохраняются в массив, происходит сравнение ожидаемых значений и фактических.

В принципе, краткое описание есть в самом репозитории и на этом можно закругляться, однако, думаю что будет гораздо нагляднее, если показать весь путь написания тестового окружения (будем считать что весь HDL и файл sim.py уже написаны). Всё действо будет происходить внутри папки sim. Осторожно, букв много впереди ожидает.

Одиночный запуск

Создадим файл test_sqrt.py для запуска тестбенча.

#!/usr/bin/env python3from sim import Simulatorsim = Simulator(name='icarus', gui=True, cwd='work')sim.incdirs += ["../src/tb", "../src/rtl", sim.cwd]sim.sources += ["../src/rtl/sqrt.v", "../src/tb/tb_sqrt.sv"]sim.top = "tb_sqrt"sim.setup()sim.run()

Тест будем прогонять в Icarus с открытием GTKWave для просмотра диаграмм. Пути до исходников задаются относительно самого скрипта. Задавать директории поиска инклудов для данного проекта не обязательно, и сделано лишь для демонстрации. Чтобы не загрязнять директорию со скриптами - с помощью sim.setup() будет создана рабочая папка work (а если она существовала, то она будет удалена и создана заново) внутри которой симулятор и будет запущен (sim.run()).

Делаем скрипт исполняемым и запускаем:

chmod +x test_sqrt.py./test_sqrt.py

Симуляция должна пройти успешно и должно появиться окно GTKWave.

Одиночный запуск без GUI

Можно конечно без конца править сам скрипт, но более верным решением будет добавить управление деталями запуска из консоли. Добавляем парсер аргументов CliArgs. Тест вынес в отдельную функцию и добавил стандартную проверку, чтобы парсер запускался только когда мы непосредственно исполняем сам файл.

#!/usr/bin/env python3from sim import Simulator, CliArgsdef test(tmpdir, defines, simtool, gui):    sim = Simulator(name=simtool, gui=gui, cwd=tmpdir)    sim.incdirs += ["../src/tb", "../src/rtl", sim.cwd]    sim.sources += ["../src/rtl/sqrt.v", "../src/tb/tb_sqrt.sv"]    sim.defines += defines    sim.top = "tb_sqrt"    sim.setup()    sim.run()if __name__ == '__main__':    # run script with key -h to see help    args = CliArgs(default_test="test").parse()    test(tmpdir='work', simtool=args.simtool, gui=args.gui, defines=args.defines)

Посмотрим что нам доступно:

$ ./test_sqrt.py -husage: test_sqrt.py [-h] [-t <name>] [-s <name>] [-b] [-d <def> [<def> ...]]optional arguments:  -h, --help            show this help message and exit  -t <name>             test <name>; default is 'test'  -s <name>             simulation tool <name>; default is 'icarus'  -b                    enable batch mode (no GUI)  -d <def> [<def> ...]  define <name>; option can be used multiple times

Теперь мы можем запустить тест в консольном режиме:

$ ./test_sqrt.py -bRun Icarus (cwd=/space/projects/pyhdlsim/simtmp/work)TOP_NAME=tb_sqrt SIMiverilog -I /space/projects/pyhdlsim/src/tb -I /space/projects/pyhdlsim/src/rtl -I /space/projects/pyhdlsim/simtmp/work -D TOP_NAME=tb_sqrt -D SIM -g2005-sv -s tb_sqrt -o worklib.vvp /space/projects/pyhdlsim/src/rtl/sqrt.v /space/projects/pyhdlsim/src/tb/tb_sqrt.svvvp worklib.vvp -lxt2LXT2 info: dumpfile dump.vcd opened for output.Test started. Will push 8 words to DUT.!@# TEST PASSED #@!

Или запустить в другом симуляторе:

# как в консоли./test_sqrt.py -s modelsim -b# так и с GUI./test_sqrt.py -s modelsim

Параметризированный запуск

Также теперь можно контролировать дефайны из консоли, и, например, увеличить количество подаваемых данных:

$ ./test_sqrt.py -b -d ITER_N=42Run Icarus (cwd=/space/projects/pyhdlsim/simtmp/work)TOP_NAME=tb_sqrt SIMiverilog -I /space/projects/pyhdlsim/src/tb -I /space/projects/pyhdlsim/src/rtl -I /space/projects/pyhdlsim/simtmp/work -D TOP_NAME=tb_sqrt -D SIM -g2005-sv -s tb_sqrt -o worklib.vvp /space/projects/pyhdlsim/src/rtl/sqrt.v /space/projects/pyhdlsim/src/tb/tb_sqrt.svvvp worklib.vvp -lxt2LXT2 info: dumpfile dump.vcd opened for output.Test started. Will push 42 words to DUT.!@# TEST PASSED #@!

Запуск с пре-/постпроцессингом

Часто бывает так, что сгенерировать данные для теста невозможно внутри тестбенча и должны быть применены внешние генераторы. Сделаем еще один тест, где будем сверять работу модуля на Verilog с идеальной моделью, написанной на Python. Алгоритм работы уже был представлен выше - просто перепишем его на Python, не забывая проверить что он на самом деле работает. Результатом будет файл src/beh/sqrt.py. Оттуда нам нужна будет лишь одна функция nrsqrt().

Переименуем старый тест, который ориентируется на данные, полученные внутри тестбенча, в test_sv. И создадим новый test_py, который будет готовить данные с помощью функции nrsqrt().

#!/usr/bin/env python3from sim import Simulator, CliArgs, path_join, write_memfileimport randomimport syssys.path.append('../src/beh')from sqrt import nrsqrtdef create_sim(cwd, simtool, gui, defines):    sim = Simulator(name=simtool, gui=gui, cwd=cwd)    sim.incdirs += ["../src/tb", "../src/rtl", cwd]    sim.sources += ["../src/rtl/sqrt.v", "../src/tb/tb_sqrt.sv"]    sim.defines += defines    sim.top = "tb_sqrt"    return simdef test_sv(tmpdir, defines, simtool, gui):    sim = create_sim(tmpdir, simtool, gui, defines)    sim.setup()    sim.run()def test_py(tmpdir, defines, simtool, gui=False, pytest_run=True):    # prepare simulator    sim = create_sim(tmpdir, simtool, gui, defines)    sim.setup()    # prepare model data    try:        din_width = int(sim.get_define('DIN_W'))    except TypeError:        din_width = 32    iterations = 100    stimuli = [random.randrange(2 ** din_width) for _ in range(iterations)]    golden = [nrsqrt(d, din_width) for d in stimuli]    write_memfile(path_join(tmpdir, 'stimuli.mem'), stimuli)    write_memfile(path_join(tmpdir, 'golden.mem'), golden)    sim.defines += ['ITER_N=%d' % iterations]    sim.defines += ['PYMODEL', 'PYMODEL_STIMULI="stimuli.mem"', 'PYMODEL_GOLDEN="golden.mem"']    # run simulation    sim.run()if __name__ == '__main__':    args = CliArgs(default_test="test_sv").parse()    try:        globals()[args.test](tmpdir='work', simtool=args.simtool, gui=args.gui, defines=args.defines)    except KeyError:        print("There is no test with name '%s'!" % args.test)

Теперь, когда тестов несколько, можно воспользоваться ключом выбора теста:

# аргумент должен совпадать с именем функции./test_sqrt.py -t test_py

Аналогичным образом можно организовать и постпроцессинг внутри запускающего скрипта при желании.

Массовый запуск

Сейчас у нас уже есть 2 теста, а ведь завтра может быть и 202, а значит уже можно переживать о том, что нужен способ как их все прогнать за раз. И вот тут на сцене появляется pytest.

Нано-ликбез по pytest.

  • Начиная с директории запуска, pytest рекурсивно ищёт всё начинающееся на test* и исполняет: модули, функции, классы, методы.

  • Тест считается выполненным, если не возникло исключений (типичным является использование assert для контроля).

  • Для формирования тестового окружения используются фикстуры (fixtures). Например, что подставлять в тест test_a(a) в качестве аргумента при выполнении как раз определяется фикстурой.

  • Можно создать дополнительный файл conftest.py, в котором разместить код для более тонкого контроля выполнения тестов и их окружения.

Типичные сценарии запуска:

  • pytest - рекурсивный поиск и исполнение всех тестов, начиная с текущей директории;

  • pytest -v - выполнить тесты и показать болеe подробную информацию о ходе выполнении тестов;

  • pytest -rP - выполнить тесты и показать вывод в stdout тех тестов, что завершились успешно;

  • pytest test_sqrt.py::test_sv - выполнить указанный тест.

Для того чтобы адаптировать текущий скрипт под pytest нужно совсем немного. Импортируем сам pytest. Добавим пару фикстур для таких аргументов теста как simtool и defines. Значение, возвращаемое фикстурами будет использовано в качестве аргумента во всех тестах. Два других аргумента gui и pytest_run снабжаем значениями по умолчанию. Фактически их тоже можно было сделать фикстурами, но т.к. для запуска pytest они не должны принимать никакое другое значение, то сделал так.

Да, кстати, появился аргумент pytest_run который сообщает о том, выполняется ли сейчас pytest, или просто тест запущен отдельно из консоли.

С аргументом tmpdir я схитрил - это имя стандартной фикстуры, которая возвращает путь к временной папке, уникальной для каждого теста. Т.е. сами тесты будут прогонятся где-то во временных директориях и не засорять содержимое sim.

Имена тестов тоже изменять не нужно - они будут найдены pytest, т.к. имеют префикс test_.

Решение о том, прошел тест или нет принимается по состоянию аттрибута is_passed симулятора. Он возвращает истину, если увидел ключевую фразу !@# TEST PASSED #@! в stdout. Очевидно, если компиляция была неудачной, или тест завершился с ошибкой, то этой фразы выведено не будет. Это самый простой способ оценить результат, но возможности для его кастомизации здесь ограничены лишь фантазией. Можно получить stdout через sim.stdout и искать там что угодно.

#!/usr/bin/env python3import pytestfrom sim import Simulator, CliArgs, path_join, write_memfileimport randomimport syssys.path.append('../src/beh')from sqrt import nrsqrt@pytest.fixture()def defines():    return []@pytest.fixturedef simtool():    return 'icarus'def create_sim(cwd, simtool, gui, defines):    sim = Simulator(name=simtool, gui=gui, cwd=cwd, passed_marker='!@# TEST PASSED #@!')    sim.incdirs += ["../src/tb", "../src/rtl", cwd]    sim.sources += ["../src/rtl/sqrt.v", "../src/tb/tb_sqrt.sv"]    sim.defines += defines    sim.top = "tb_sqrt"    return simdef test_sv(tmpdir, defines, simtool, gui=False, pytest_run=True):    sim = create_sim(tmpdir, simtool, gui, defines)    sim.setup()    sim.run()    if pytest_run:        assert sim.is_passeddef test_py(tmpdir, defines, simtool, gui=False, pytest_run=True):    # prepare simulator    sim = create_sim(tmpdir, simtool, gui, defines)    sim.setup()    # prepare model data    try:        din_width = int(sim.get_define('DIN_W'))    except TypeError:        din_width = 32    iterations = 100    stimuli = [random.randrange(2 ** din_width) for _ in range(iterations)]    golden = [nrsqrt(d, din_width) for d in stimuli]    write_memfile(path_join(tmpdir, 'stimuli.mem'), stimuli)    write_memfile(path_join(tmpdir, 'golden.mem'), golden)    sim.defines += ['ITER_N=%d' % iterations]    sim.defines += ['PYMODEL', 'PYMODEL_STIMULI="stimuli.mem"', 'PYMODEL_GOLDEN="golden.mem"']    # run simulation    sim.run()    if pytest_run:        assert sim.is_passedif __name__ == '__main__':    args = CliArgs(default_test="test_sv").parse()    try:        globals()[args.test](tmpdir='work', simtool=args.simtool, gui=args.gui, defines=args.defines, pytest_run=False)    except KeyError:        print("There is no test with name '%s'!" % args.test)

Прогоним все тесты несколько раз:

$ pytest========== test session starts ===========platform linux -- Python 3.8.5, pytest-6.2.1, py-1.10.0, pluggy-0.13.1rootdir: /space/projects/misc/habr-publications/pyhdlsim/pyhdlsim/simtmpplugins: xdist-2.2.0, forked-1.3.0collected 2 itemstest_sqrt.py ..                    [100%]=========== 2 passed in 0.08s ============$ pytest -v========== test session starts ===========platform linux -- Python 3.8.5, pytest-6.2.1, py-1.10.0, pluggy-0.13.1 -- /usr/bin/python3cachedir: .pytest_cacherootdir: /space/projects/misc/habr-publications/pyhdlsim/pyhdlsim/simtmpplugins: xdist-2.2.0, forked-1.3.0collected 2 itemstest_sqrt.py::test_sv PASSED       [ 50%]test_sqrt.py::test_py PASSED       [100%]=========== 2 passed in 0.08s ============

Массовый параметризированный запуск

Неплохо было бы прогнать тесты для разной величины ширины данных. Очевидно, что руками запускать тесты N раз, каждый раз анализируя глазами логи в поисках фразы об успешном завершении, довольно утомительно. Нужно использовать pytest.

Модификация будет минимальной, нужно лишь обновить фикстуру defines:

# заменим это@pytest.fixture()def defines():    return []# на это@pytest.fixture(params=[[], ['DIN_W=16'], ['DIN_W=18'], ['DIN_W=25'], ['DIN_W=32']])def defines(request):    return request.param

Теперь фикстура может принимать одно из 5 значений. Запустим тесты:

$ pytest -v================== test session starts ==================platform linux -- Python 3.8.5, pytest-6.2.1, py-1.10.0, pluggy-0.13.1 -- /usr/bin/python3cachedir: .pytest_cacherootdir: /space/projects/misc/habr-publications/pyhdlsim/pyhdlsim/simtmpplugins: xdist-2.2.0, forked-1.3.0collected 10 itemstest_sqrt.py::test_sv[defines0] PASSED            [ 10%]test_sqrt.py::test_sv[defines1] PASSED            [ 20%]test_sqrt.py::test_sv[defines2] PASSED            [ 30%]test_sqrt.py::test_sv[defines3] PASSED            [ 40%]test_sqrt.py::test_sv[defines4] PASSED            [ 50%]test_sqrt.py::test_py[defines0] PASSED            [ 60%]test_sqrt.py::test_py[defines1] PASSED            [ 70%]test_sqrt.py::test_py[defines2] PASSED            [ 80%]test_sqrt.py::test_py[defines3] PASSED            [ 90%]test_sqrt.py::test_py[defines4] PASSED            [100%]================== 10 passed in 0.28s ===================

Как видим, теперь каждый тест запустился по 5 раз с разным дефайном.

Параллельные запуски

Тут тоже всё довольно просто и работает почти из коробки. Ставим один плагин:

python3 -m pip install pytest-xdist

И теперь можем запускать тесты в несколько параллельных потоков, например, в 4:

# можно также использовать значение auto, pytest задействует все доступные ядраpytest -n 4

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

def test_slow(tmpdir, defines, simtool, gui=False, pytest_run=True):    sim = create_sim(tmpdir, simtool, gui, defines)    sim.defines += ['ITER_N=500000']    sim.setup()    sim.run()    if pytest_run:        assert sim.is_passed

Запустим последовательное и параллельное исполнение (тестов теперь стало 3*5=15):

$ pytest=================== test session starts ====================platform linux -- Python 3.8.5, pytest-6.2.1, py-1.10.0, pluggy-0.13.1rootdir: /space/projects/misc/habr-publications/pyhdlsim/pyhdlsim/simtmpplugins: xdist-2.2.0, forked-1.3.0collected 15 itemstest_sqrt.py ...............                         [100%]============== 15 passed in 242.74s (0:04:02) ==============$ pytest -n auto=================== test session starts ====================platform linux -- Python 3.8.5, pytest-6.2.1, py-1.10.0, pluggy-0.13.1rootdir: /space/projects/misc/habr-publications/pyhdlsim/pyhdlsim/simtmpplugins: xdist-2.2.0, forked-1.3.0gw0 [15] / gw1 [15] / gw2 [15] / gw3 [15]...............                                      [100%]============== 15 passed in 145.66s (0:02:25) ==============

Результат, как говорится, видно невооруженным взглядом.

Поддержка нескольких симуляторов

Ранее уже было показано, что при выполнении теста без pytest можно было выбрать симулятор с помощью ключа -s. Теперь же добавим выбор симулятора для pytest. Очевидно, нужно что-то сделать с фикстурой simtool.

Тут нам пригодится знание о существовании файла conftest.py, необходимого для кастомизации запусков pytest. Создадим такой файл рядом с sim.py и добавим туда следующий код:

def pytest_addoption(parser):    parser.addoption("--sim", action="store", default="icarus")

В файле теста test_sqrt.py обновим фикстуру simtool:

@pytest.fixturedef simtool(pytestconfig):    return pytestconfig.getoption("sim")

Теперь можно прогнать все тесты в другом симуляторе:

pytest --sim modelsim -n auto

Поддержка CI. Github Actions + (Modelsim | Icarus)

Ну и бонусом будет часть о непрерывной интеграции (CI). В репозиторий добавлены два файла .github/workflows/icarus-test.yml и .github/workflows/modelsim-test.yml. Это так называемые Github Actions - по определенному событию будет выполнено их содержимое внутри виртуального окружения, предоставляемого Github. В данном случае, после каждого пуша будут прогнаны все тесты в двух симуляторах.

В Icarus Verilog:

- name: Install dependencies  run: |    python -m pip install --upgrade pip    pip install pytest pytest-xdist    sudo apt-get install iverilog- name: Test code  working-directory: ./sim  run: |    pytest -n auto

И в Modelsim Intel Starter Pack:

- name: Install dependencies  run: |    python -m pip install --upgrade pip      pip install pytest pytest-xdist      sudo dpkg --add-architecture i386      sudo apt update      sudo apt install -y libc6:i386 libxtst6:i386 libncurses5:i386 libxft2:i386 libstdc++6:i386 libc6-dev-i386 lib32z1 libqt5xml5 liblzma-dev    wget https://download.altera.com/akdlm/software/acdsinst/20.1std/711/ib_installers/ModelSimSetup-20.1.0.711-linux.run        chmod +x ModelSimSetup-20.1.0.711-linux.run    ./ModelSimSetup-20.1.0.711-linux.run --mode unattended --accept_eula 1 --installdir $HOME/ModelSim-20.1.0 --unattendedmodeui none    echo "$HOME/ModelSim-20.1.0/modelsim_ase/bin" >> $GITHUB_PATH- name: Test code  working-directory: ./sim  run: |    pytest -n auto --sim modelsim

Тут кстати очень порадовала последняя версия Modelsim. Они наконец-то починили её! Каждый кто хоть раз устанавливал его на Ubuntu/Fedora поймёт о чём я (вот, например, инструкция для Quartus+Modelsim 19.1 и Fedora 29).

Ну и сравнение времени выполнения после очередного пуша в репозиторий:

Даже не смотря на то, что скачивание 1.3GB установочника Modelsim и его распаковка занимают некоторое время (которое тем не менее, очень мало!), он оказывается в итоге ещё и быстрее моментально развертываемого Icarus.

Тут конечно можно пойти дальше и подготовить Docker-образ с Modelsim, чтобы не качать его каждый раз сайта, но я, пожалуй, здесь остановлюсь.

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

Все финальные версии скриптов лежат в репозитории pyhdlsim на GitHub.

Источник: habr.com
К списку статей
Опубликовано: 17.01.2021 20:13:46
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Python

Fpga

Simulation

Verilog

Systemverilog

Vhdl

Modelsim

Icarus

Vivado

Pytest

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru