Проведение тестирования проекта с помощью VUnit
В мире ПЛИС проектирование и проверка идут рука об руку. Когда приходит время создать тестовую среду для особенно сложного модуля, который должен поддерживать несколько функций, это может оказаться довольно затруднительным - попытка выполнить все эти функции одновременно в одном тесте может быстро стать трудной и запутанной, и что еще хуже, что такой тест нельзя использовать, пока не будет завершен весь проект.
Что делать, если после завершения проектирования мы обнаруживаем ошибку, требующую значительного отката?
Что, если на выполнение полного теста уходит много времени, когда на самом деле мы просто хотим проверить определенную часть теста, которая связана с небольшим изменением проекта?
Это потенциально может занять много времени и затруднить отслеживание прогресса. Лучшим решением в такой ситуации является модульное тестирование . Разбив наш тест на части, чтобы проверить функцию за функцией, такая задача становится намного более достижимой. О прогрессе можно сообщать чаще и точнее, а проблемы можно обнаруживать на ранней стадии процесса проектирования, а не ждать его завершения.
Это прекрасно вписывается в Agile-фреймворк или методологию разработки через тестирование (TDD) , и в этом VUnit действительно блещет.
VUnit - это среда тестирования на основе Python, которая позволяет упростить организацию, контроль и выполнение определенных тестовых сценариев для проекта. Это позволяет проектировщику адаптировать отдельные тестовые наборы к конкретным функциям своих модулей и, таким образом, проверять их при разработке. VUnit предлагает множество функций, которые делают модульное тестирование намного более простой задачей по сравнению со стандартными подходами к тестированию. VUnit включает в себя:
- Автоматическое обнаружение тестбенчей и порядка компиляции.
- Возможность выполнять различные комбинации тестовых случаев с различными универсальными конфигурациями модуля с использованием простых аргументов командной строки.
- Доступ к нескольким библиотекам со встроенными функциями, упрощающими общие задачи проверки.
См. Руководство VUnit « Приступая к работе » для ознакомления с тем, как устроена тестовая среда VUnit: https://vunit.github.io/user_guide.html#introduction .
Пример работы VUnit с тестбенч
Чтобы продемонстрировать эту концепцию, мы будем использовать модуль полусумматора. Этот полусумматор будет иметь следующие порты:
entity design_top is port( term_1_in_p : in std_logic; term_2_in_p : in std_logic; sum_out_p : out std_logic; carry_out_p : out std_logic ); end entity design_top;
Хотя можно легко создать тестбенч, проверяющий все функции этого полусумматора, давайте представим, что это гораздо более сложный модуль, и мы хотим тестировать эти функции по очереди по мере их реализации. Полусумматор можно разбить на две функции:
- Расчет суммарного выхода (если только один из входных терминов равен «1», этот выход устанавливается равным 1).
- Расчет выхода переноса разряда (если оба входа равны «1», этот выход должен быть 1)
Сначала давайте спроектируем суммирующую функцию:
architecture rtl of design_top is
begin sum_out_p <= term_1_in_p xor term_2_in_p; end architecture rtl;
Используя VUnit, теперь мы можем добавить тестовый набор в наш набор тестов, который нацелен на функцию суммы нашего полусумматора, например:
test_runner_setup(runner,runner_cfg); -- simulation starts here if run("sum test, no carry") then write_terms(bfm_control_s,'0','0'); wait for 10 ns; write_terms(bfm_control_s,'1','0'); wait for 10 ns; write_terms(bfm_control_s,'0','1'); wait for 10 ns; end if; test_runner_cleanup(runner); -- simulation ends here
В нашем тестовом примере «тест суммы, без переноса» мы даем команду самопроверяющейся функциональной модели шины (BFM - Bus Functional Model), используя процедуру «write_terms (bfm_control_s, [term 1], [term 2]») для отправки входных значений, нацеленных на суммирование без переноса, поскольку варинт с переносом мы еще не реализовали. Мы устанавливаем только один бит за раз. BFM в этом случае будет отслеживать выходные данные и проверять их с помощью механизма утверждений (assertions) или с помощью встроенной библиотеки проверки VUnit.
Мы запускаем наши тестовые наборы из командной строки, выполняя run.py из VUnit . Это центральный скрипт для тестовой среды VUnit. Он используется для указания путей к тестбенчу и исходникам проекта, поддерживая несколько удобных флагов и аргументов для управления различными аспектами теста, например, какие тестовые наборы выполняются, если они запускаются в режиме графического интерфейса пользователя выбранного симулятора, если тест должен остановиться, если обнаружен сбой, и несколько других. Полный список этих аргументов можно найти в документации VUnit. По умолчанию run.py запускает все обнаруженные тестовые наборы в наборе тестов и отображает, прошли они или нет. Результат в нашем случае выглядит так:
Мы видим, что VUnit запускает наш тестовый пример «тест суммы, без переноса», и наша самопроверка BFM не запустила ни одно из утверждений (assertions), поэтому наш тест считается пройденным. Теперь мы можем предоставить обновление статуса проекта и сказать, что наш проект поддерживает суммирующую часть нашего полусумматора. Далее, давайте закончим наш проект, реализовав функцию переноса:
architecture rtl of design_top is begin sum_out_p <= term_1_in_p xor term_2_in_p; carry_out_p <= term_1_in_p and term_2_in_p; end architecture rtl;
А затем мы можем добавить дополнительный тестовый набор в наш набор тестов VUnit, например:
main : process is begin test_runner_setup(runner,runner_cfg); -- simulation starts here if run("sum test, no carry") then write_terms(bfm_control_s,'0','0'); wait for 10 ns; write_terms(bfm_control_s,'1','0'); wait for 10 ns; write_terms(bfm_control_s,'0','1'); wait for 10 ns; elsif run("carry test") then write_terms(bfm_control_s,'1','1');
Теперь у нас есть тестовый «тест с переносом» (carry test), который установит для обоих входов значение «1» и проверит, что выход переноса разряда из модуля становится рывным 1. Опять же, мы запускаем наш набор тестов VUnit, выполнив run.py :
Теперь мы видим, что VUnit запускает оба тестовых случая: «суммарный тест, без переноса» и «тест с переносом». Оба тестовых примера проходят через наши самопроверяющиеся утверждения BFM без сбоев. Теперь мы можем сообщить, что наш проект поддерживает все необходимые функции.
Предположим, что мы вносим изменения в проект в будущем и хотим убедиться, что соответствующий тестовый сценарий все еще проходит. Возможно, мы начали «модернизировать» нашу конструкцию с полусумматора до полного сумматора и попутно хотели бы убедиться, что наш первоначальный тест переноса все еще работает.
VUnit также позволяет нам выполнять определенные тесты из нашего набора тестов, например:
python run.py "lib.tb_example.carry test"
Таким образом, мы можем убедиться, что что-то по-прежнему работает, не дожидаясь выполнения всех тестовых случаев или изменения самого кода тестовой среды. VUnit предоставляет эти и многие другие удобства для управления тестом из командной строки.
Хотя это очень простой пример, он демонстрирует концепцию модульного тестирования и то, как VUnit может быть мощным инструментом в процессе поэтапного создания и проверки большого проекта, обеспечивая более частую обратную связь как с разработчиком, так и с руководством проекта.
Оригинал статьи можно найти по ссылке