Спец курс (Верификация цифровых схем)/Практические задания 3 — различия между версиями
Материал из Wiki
Vidokq (обсуждение | вклад) (→Шаг 9. Сиквенсер) |
Vidokq (обсуждение | вклад) |
||
(не показаны 22 промежуточные версии 1 участника) | |||
Строка 1: | Строка 1: | ||
{{ВЦС_TOC}} | {{ВЦС_TOC}} | ||
= Общие для всех вариантов шаги лаб 3 = | = Общие для всех вариантов шаги лаб 3 = | ||
+ | |||
+ | == Шаг 0. Создание класса транзакции == | ||
+ | # Создать описание класса seq_item. | ||
+ | # Класс должен содержать битовое поле data | ||
+ | |||
== Шаг 1. Создание интерфейса == | == Шаг 1. Создание интерфейса == | ||
− | # | + | # В отдельном файле my_vif.sv объявить интерфейс с имененем типа my_vif, который в списке входных портов содержит тактовый сигнал clk, сигнал сброса rst, и сигнал data. (все сигналы однобитные) |
− | # Создать модуль | + | # Создать модуль с именем top в файле top.sv. |
− | # Объявить | + | # Используя `include "my_vif.sv" подключить интерфейс в самом начале файла top.sv. |
+ | # В top модуле инстанциировать интерфейс vif (my_vif vif (.clk(....), ......)). | ||
+ | # Объявить две переменные типа reg по имени clk_i, rst_i. | ||
# В initial блоке создать генератор тактового сигнала и сброса используя, переменные clk_i, rst_i; | # В initial блоке создать генератор тактового сигнала и сброса используя, переменные clk_i, rst_i; | ||
− | # Подключить сигналы clk_i, rst_i | + | ## initial forever clk_i = ~ clk_i; |
+ | ## initial clk_i = 0; | ||
+ | ## initial begin rst_i <=0; # 40 ns; rst_i<= 1; end | ||
+ | # Подключить сигналы генератора тактового сигнала и сброса к соответствующим сигналам clk_i, rst_i в интерфейсе. | ||
− | == Шаг 2. Создание класса конвертации транзакции | + | == Шаг 2. Создание класса конвертации транзакции в воздействие на проводном интерфейсе == |
− | # Создать класс с названием driver_t. | + | # Создать класс с названием driver_t в отдельном файле driver_t.sv. |
− | # В классе объявить переменную типа virtual | + | # В классе объявить переменную типа virtual my_vif vif. |
+ | # Объявить переменную типа seq_item item1; | ||
# Объявить event get_next_item_e; | # Объявить event get_next_item_e; | ||
− | # Реализовать virtual task mrun_phase | + | # Реализовать таск virtual task mrun_phase() в этом классе. |
+ | ## Таск должен содержать бесконечный цикл forever, который ожидает события get_next_item_e. | ||
+ | ## После получения ожидаем события posedge на сигнале clk (из интерфейса @(posedge vif.clk)) | ||
+ | ## После берем значения из объекта транзакции item1.data и назначаем на сигнал интерфейса vif.data | ||
+ | ## Цикл повторяется | ||
== Шаг 3. Запуск и проверка конвертации == | == Шаг 3. Запуск и проверка конвертации == | ||
− | # | + | # Подключить файл класса в top.sv файле после подключения интерфейса (`include "driver_t.sv") |
− | # Передать указатель на интерфейс в объект driver | + | # Объявить и создать объект driver типа driver_t в top модуле (driver_t driver = new (); ), |
− | # Запустить task mrun_phase | + | # Передать указатель на интерфейс в объект driver. (driver.vif = vif;) |
− | # Создать в модуле класс транзакции, | + | # Запустить task mrun_phase из обьекта driver. (driver.mrun_phase();) |
− | # Вызвать триггер для события get_next_item_e. | + | # Создать в top модуле класс транзакции item, объявленной на шаге 0 (seq_item item = new(); ). |
− | # Повторить генерацию транзакций 10 раз | + | # Передать указатель на транзакцию в объект driver класса driver_t (driver.item = item; ). |
+ | # Вызвать триггер для события get_next_item_e. ( -> driver.get_next_item_e ;) | ||
+ | # Повторить в цикле генерацию транзакций 10 раз. | ||
+ | # Пауза между генерацией каждой транзакции должна выбираться случайным образом в интервале от 1 до 10 us. | ||
+ | # Если выполняете задание на https://www.edaplayground.com, то добавить конструкцию сохранения временных диаграмм. | ||
+ | ## | ||
+ | //enabling the wave dump | ||
+ | initial begin | ||
+ | $dumpfile("dump.vcd"); | ||
+ | $dumpvars; | ||
+ | end | ||
+ | # Убедиться, что переключения отображаются на интерфейсе. | ||
== Шаг 4. Создание класса, получения транзакции с проводного интерфейса == | == Шаг 4. Создание класса, получения транзакции с проводного интерфейса == | ||
− | # Создать класс с названием monitor_t. | + | # Создать класс с названием monitor_t в отдельном файле (monitor_t.sv). |
− | # В классе объявить переменную типа virtual | + | # В классе объявить переменную типа virtual my_vif vif. |
− | # Реализовать virtual task mrun_phase в этом классе. Должен содержать бесконечный цикл. | + | # Объявить переменную типа seq_item item_collect; |
+ | # Реализовать таск virtual task mrun_phase () в этом классе. | ||
+ | ## Таск Должен содержать бесконечный цикл forever. | ||
+ | ## В цикле ожидаем события posedge на сигнале clk (@(posedge vif.clk);) | ||
+ | ## Создаем объект item_collect (item_collect = new();) | ||
+ | ## В цикле ожидаем события posedge на сигнале clk из интерфейса (@(posedge vif.clk);) | ||
+ | ## После события берем значение сигнала data из интерфейса, помещаем его значение в поле data транзакции (item_collect.data = vif.data); | ||
+ | ## Печатаем содержимое транзакции в консоль ($display ("TR data: %b", item_collect.data);). | ||
== Шаг 5. Запуск монитора == | == Шаг 5. Запуск монитора == | ||
− | # | + | # Подключить файл класса monitor в top.sv файле после подключения драйвера (`include "monitor_t.sv") |
− | # Передать | + | # Объявить и создать объект monitor класса monitor_t в top модуле (monitor_t monitor = new() ;) |
− | # Запустить в топ модуле task mrun_phase | + | # Передать указатель на интерфейс в объект monitor. (monitor.vif = vif;) |
+ | # Запустить в топ модуле task mrun_phase (); | ||
+ | # Убедиться, что 10 транзакций, которые мы отправили, были получены монитором и напечатаны в лог. | ||
− | == Шаг 6. | + | == Шаг 6. Создание класса Agent == |
# Создать класс Agent_t | # Создать класс Agent_t | ||
− | # | + | # Объявить и создать объект monitor типа monitor_t в классе agent_t (monitor_t monitor = new() ;) |
− | # | + | # Объявить и создать объект driver типа driver_t в классе agent_t (driver_t driver = new (); ) |
− | # В классе объявить переменную virtual | + | # Реализовать таск virtual task mrun_phase () в этом классе, в котором запускаются mrun_phase из классов monitor,driver. |
− | + | # В классе объявить переменную virtual my_vif vif | |
− | + | # В mrun_phase передать ссылку на интерфейс должна в драйвер и монитор (driver.vif=vif; monitor.vif=vif;) | |
− | + | ||
− | == Шаг 7. Наследуемся от UVM == | + | == Шаг 7. Подлкючаем агент в top модуле == |
+ | # Подключить файл класса agent в top.sv файле после подключения монитора (`include "agent_t.sv") | ||
+ | # Обьявить указатель agent типа agent_t в top модуле (agent_t agent;) | ||
+ | # Создать объект класса agent_t в top модуле; (intial begin agent = new(); end) | ||
+ | # Передать в объект agent ссылку на интерфейс (agent.vif = vif;) | ||
+ | # Запустить в task mrun_phase из объекта agent. (agent.mrun_phase()) | ||
+ | |||
+ | == Шаг 8. Использование монитора и драйвера из агента (Аналогично шаг 1,3,5) == | ||
+ | # В top модуле инстанциировать еще один интерфейс vifa (my_vif vifa (.clk(....), ......)) и подключить сигналы сброса и такстирования. | ||
+ | # Передать указатель на интерфейс в объект agent. (agent.vif = vifa;) | ||
+ | # Создать в top модуле класс транзакции item_a, объявленной на шаге 0 (seq_item item_a = new(); ). | ||
+ | # Передать указатель на транзакцию в объект driver из обьекта agent (agent.driver.item = item; ). | ||
+ | # Вызвать триггер для события get_next_item_e. ( -> agent.driver.get_next_item_e ;) | ||
+ | # Повторить в цикле генерацию транзакций 10 раз. | ||
+ | # Пауза между генерацией каждой транзакции должна выбираться случайным образом в интервале от 1 до 10 us. | ||
+ | # Убедиться, что переключения отображаются на интерфейсе. | ||
+ | # Убедиться, что 10 транзакций, которые мы отправили, были получены монитором и напечатаны в лог. | ||
+ | |||
+ | = Общие для всех вариантов шаги лаб 3 при переходе к UVM = | ||
+ | |||
+ | == Шаг 1. Наследуемся от UVM == | ||
# Создать класс my_driver_t, наследуемый от uvm_driver | # Создать класс my_driver_t, наследуемый от uvm_driver | ||
# Функционал класса driver_t перенести в my_driver_t | # Функционал класса driver_t перенести в my_driver_t | ||
Строка 47: | Строка 101: | ||
# Функционал класса monitor_t перенести в my_monitor_t | # Функционал класса monitor_t перенести в my_monitor_t | ||
# Создать класс my_agent_t, наследуемый от uvm_agent | # Создать класс my_agent_t, наследуемый от uvm_agent | ||
− | # Функционал класса agent_t перенести в my_agent_t | + | # Функционал класса agent_t перенести в my_agent_t |
+ | # В каждом из описанных шагов дополнительно | ||
+ | ## Переопределить конструктор | ||
+ | ## Поменять конструктор в точке вызова (передать строковый параметр - имя модуля, и указатель на компонент создающий данный объект) | ||
+ | ### при вызове конструктора в топ модуле вторым параметром можно использовать uvm_root::get() | ||
+ | ## Добавить макросы регистрации класса | ||
+ | ## В каждом описанном классе добавить описание 2 функций | ||
+ | ### function void build_phase (uvm_phase phase) | ||
+ | ### function void connect_phase (uvm_phase phase) | ||
+ | ## В каждом описанном классе добавить описание 1 дополнительного таска | ||
+ | ### task run_phase (uvm_phase phase) | ||
− | == Шаг | + | == Шаг 2. Сиквенсер == |
+ | # Создать класс наследуемый от uvm_sequencer | ||
+ | ## Добавить регистрацию класса в фабрике с помощью макроса | ||
+ | == Шаг 3.0 Агент == | ||
+ | # Добавить регистрацию класса в фабрике с помощью макроса | ||
+ | # В классе агента объявить указатели на драйвер и монитор | ||
+ | # Перенести создание объектов драйвера и монитора в build_phase агента из топ модуля | ||
+ | ## не забудьте поменять второй параметр конструктора (uvm_root::get() -> this); | ||
+ | ## при создании используйте new() или create() | ||
+ | |||
+ | == Шаг 3. Соединить драйвер и сиквенсер == | ||
+ | # В классе агента в методе connect_phase соединить встроенные в драйвер и сиквенсер порты seq_item_port и seq_item_export | ||
+ | |||
+ | == Шаг 4. Сиквенс == | ||
# Создать класс наследуемый от uvm_sequence | # Создать класс наследуемый от uvm_sequence | ||
− | # Реализовать метод класса body | + | # Реализовать метод класса body () |
− | ## создать транзакцию tr | + | ## объявить указатель на транзакцию tr |
+ | ## создать транзакцию tr | ||
## вызывать метод класса start_item(tr); | ## вызывать метод класса start_item(tr); | ||
## вызвать tr.randomize() для объекта транзакции; | ## вызвать tr.randomize() для объекта транзакции; | ||
Строка 58: | Строка 136: | ||
# повторить перечисленные выше действия 10 раз; | # повторить перечисленные выше действия 10 раз; | ||
− | == Шаг | + | == Шаг 5. Заменить вывод сообщений на UVM INFO макросы == |
− | # Создать класс наследуемый от | + | |
+ | == Шаг 6. Соединить интерфейс , драйвер , монитор == | ||
+ | # В топ модуле после создания интерфейса реализовать initial begin end | ||
+ | ## В блоке begin end вызвать метод помещающий интерфейс в базу конфигурации | ||
+ | ## uvm_config_db#(virtual interface):: set (null, "*", "vif", vif); | ||
+ | # В дравере и мониторе в build_phase таск добавить вызов метода получающего указатель на интерфейс из базы конфигурации | ||
+ | ## uvm_config_db#(virtual interface):: get (this, "", "vif", vif); | ||
+ | |||
+ | == Шаг 7. Тест/Окружение == | ||
+ | #Создать класс наследуемый от uvm_test с именем my_uvm_test | ||
+ | #Реализовать метод build_phase этого класса | ||
+ | ##Создание объекта агента | ||
+ | ##Создание объекта сиквенсы | ||
+ | #Реализовать run_phase теста : | ||
+ | ## Поднять объект управления (raise_objection) | ||
+ | ## Запустить сиквенс на сиквенсере использую метод start() | ||
+ | ## Снять объект управления (drop_objection) | ||
+ | |||
+ | == Шаг 8. Запустить тест на выполнение == | ||
+ | # В топ модуле после создания интерфейса реализовать initial begin end | ||
+ | ## В блоке begin end вызвать метод run_test ("my_uvm_test"); my_uvm_test - имя, которые вы дали классу теста. | ||
= Как выбрать вариант = | = Как выбрать вариант = |
Текущая версия на 20:31, 3 июля 2020
Лекции ВЦС
Лекции
Практические задания
|
Тесты
Табель успеваемости
Экзамен
Доп. материалы
Общие для всех вариантов шаги лаб 3
Шаг 0. Создание класса транзакции
- Создать описание класса seq_item.
- Класс должен содержать битовое поле data
Шаг 1. Создание интерфейса
- В отдельном файле my_vif.sv объявить интерфейс с имененем типа my_vif, который в списке входных портов содержит тактовый сигнал clk, сигнал сброса rst, и сигнал data. (все сигналы однобитные)
- Создать модуль с именем top в файле top.sv.
- Используя `include "my_vif.sv" подключить интерфейс в самом начале файла top.sv.
- В top модуле инстанциировать интерфейс vif (my_vif vif (.clk(....), ......)).
- Объявить две переменные типа reg по имени clk_i, rst_i.
- В initial блоке создать генератор тактового сигнала и сброса используя, переменные clk_i, rst_i;
- initial forever clk_i = ~ clk_i;
- initial clk_i = 0;
- initial begin rst_i <=0; # 40 ns; rst_i<= 1; end
- Подключить сигналы генератора тактового сигнала и сброса к соответствующим сигналам clk_i, rst_i в интерфейсе.
Шаг 2. Создание класса конвертации транзакции в воздействие на проводном интерфейсе
- Создать класс с названием driver_t в отдельном файле driver_t.sv.
- В классе объявить переменную типа virtual my_vif vif.
- Объявить переменную типа seq_item item1;
- Объявить event get_next_item_e;
- Реализовать таск virtual task mrun_phase() в этом классе.
- Таск должен содержать бесконечный цикл forever, который ожидает события get_next_item_e.
- После получения ожидаем события posedge на сигнале clk (из интерфейса @(posedge vif.clk))
- После берем значения из объекта транзакции item1.data и назначаем на сигнал интерфейса vif.data
- Цикл повторяется
Шаг 3. Запуск и проверка конвертации
- Подключить файл класса в top.sv файле после подключения интерфейса (`include "driver_t.sv")
- Объявить и создать объект driver типа driver_t в top модуле (driver_t driver = new (); ),
- Передать указатель на интерфейс в объект driver. (driver.vif = vif;)
- Запустить task mrun_phase из обьекта driver. (driver.mrun_phase();)
- Создать в top модуле класс транзакции item, объявленной на шаге 0 (seq_item item = new(); ).
- Передать указатель на транзакцию в объект driver класса driver_t (driver.item = item; ).
- Вызвать триггер для события get_next_item_e. ( -> driver.get_next_item_e ;)
- Повторить в цикле генерацию транзакций 10 раз.
- Пауза между генерацией каждой транзакции должна выбираться случайным образом в интервале от 1 до 10 us.
- Если выполняете задание на https://www.edaplayground.com, то добавить конструкцию сохранения временных диаграмм.
//enabling the wave dump initial begin $dumpfile("dump.vcd"); $dumpvars; end
- Убедиться, что переключения отображаются на интерфейсе.
Шаг 4. Создание класса, получения транзакции с проводного интерфейса
- Создать класс с названием monitor_t в отдельном файле (monitor_t.sv).
- В классе объявить переменную типа virtual my_vif vif.
- Объявить переменную типа seq_item item_collect;
- Реализовать таск virtual task mrun_phase () в этом классе.
- Таск Должен содержать бесконечный цикл forever.
- В цикле ожидаем события posedge на сигнале clk (@(posedge vif.clk);)
- Создаем объект item_collect (item_collect = new();)
- В цикле ожидаем события posedge на сигнале clk из интерфейса (@(posedge vif.clk);)
- После события берем значение сигнала data из интерфейса, помещаем его значение в поле data транзакции (item_collect.data = vif.data);
- Печатаем содержимое транзакции в консоль ($display ("TR data: %b", item_collect.data);).
Шаг 5. Запуск монитора
- Подключить файл класса monitor в top.sv файле после подключения драйвера (`include "monitor_t.sv")
- Объявить и создать объект monitor класса monitor_t в top модуле (monitor_t monitor = new() ;)
- Передать указатель на интерфейс в объект monitor. (monitor.vif = vif;)
- Запустить в топ модуле task mrun_phase ();
- Убедиться, что 10 транзакций, которые мы отправили, были получены монитором и напечатаны в лог.
Шаг 6. Создание класса Agent
- Создать класс Agent_t
- Объявить и создать объект monitor типа monitor_t в классе agent_t (monitor_t monitor = new() ;)
- Объявить и создать объект driver типа driver_t в классе agent_t (driver_t driver = new (); )
- Реализовать таск virtual task mrun_phase () в этом классе, в котором запускаются mrun_phase из классов monitor,driver.
- В классе объявить переменную virtual my_vif vif
- В mrun_phase передать ссылку на интерфейс должна в драйвер и монитор (driver.vif=vif; monitor.vif=vif;)
Шаг 7. Подлкючаем агент в top модуле
- Подключить файл класса agent в top.sv файле после подключения монитора (`include "agent_t.sv")
- Обьявить указатель agent типа agent_t в top модуле (agent_t agent;)
- Создать объект класса agent_t в top модуле; (intial begin agent = new(); end)
- Передать в объект agent ссылку на интерфейс (agent.vif = vif;)
- Запустить в task mrun_phase из объекта agent. (agent.mrun_phase())
Шаг 8. Использование монитора и драйвера из агента (Аналогично шаг 1,3,5)
- В top модуле инстанциировать еще один интерфейс vifa (my_vif vifa (.clk(....), ......)) и подключить сигналы сброса и такстирования.
- Передать указатель на интерфейс в объект agent. (agent.vif = vifa;)
- Создать в top модуле класс транзакции item_a, объявленной на шаге 0 (seq_item item_a = new(); ).
- Передать указатель на транзакцию в объект driver из обьекта agent (agent.driver.item = item; ).
- Вызвать триггер для события get_next_item_e. ( -> agent.driver.get_next_item_e ;)
- Повторить в цикле генерацию транзакций 10 раз.
- Пауза между генерацией каждой транзакции должна выбираться случайным образом в интервале от 1 до 10 us.
- Убедиться, что переключения отображаются на интерфейсе.
- Убедиться, что 10 транзакций, которые мы отправили, были получены монитором и напечатаны в лог.
Общие для всех вариантов шаги лаб 3 при переходе к UVM
Шаг 1. Наследуемся от UVM
- Создать класс my_driver_t, наследуемый от uvm_driver
- Функционал класса driver_t перенести в my_driver_t
- Создать класс my_monitor_t, наследуемый от uvm_monitor
- Функционал класса monitor_t перенести в my_monitor_t
- Создать класс my_agent_t, наследуемый от uvm_agent
- Функционал класса agent_t перенести в my_agent_t
- В каждом из описанных шагов дополнительно
- Переопределить конструктор
- Поменять конструктор в точке вызова (передать строковый параметр - имя модуля, и указатель на компонент создающий данный объект)
- при вызове конструктора в топ модуле вторым параметром можно использовать uvm_root::get()
- Добавить макросы регистрации класса
- В каждом описанном классе добавить описание 2 функций
- function void build_phase (uvm_phase phase)
- function void connect_phase (uvm_phase phase)
- В каждом описанном классе добавить описание 1 дополнительного таска
- task run_phase (uvm_phase phase)
Шаг 2. Сиквенсер
- Создать класс наследуемый от uvm_sequencer
- Добавить регистрацию класса в фабрике с помощью макроса
Шаг 3.0 Агент
- Добавить регистрацию класса в фабрике с помощью макроса
- В классе агента объявить указатели на драйвер и монитор
- Перенести создание объектов драйвера и монитора в build_phase агента из топ модуля
- не забудьте поменять второй параметр конструктора (uvm_root::get() -> this);
- при создании используйте new() или create()
Шаг 3. Соединить драйвер и сиквенсер
- В классе агента в методе connect_phase соединить встроенные в драйвер и сиквенсер порты seq_item_port и seq_item_export
Шаг 4. Сиквенс
- Создать класс наследуемый от uvm_sequence
- Реализовать метод класса body ()
- объявить указатель на транзакцию tr
- создать транзакцию tr
- вызывать метод класса start_item(tr);
- вызвать tr.randomize() для объекта транзакции;
- вызвать метод класса finish_item();
- повторить перечисленные выше действия 10 раз;
Шаг 5. Заменить вывод сообщений на UVM INFO макросы
Шаг 6. Соединить интерфейс , драйвер , монитор
- В топ модуле после создания интерфейса реализовать initial begin end
- В блоке begin end вызвать метод помещающий интерфейс в базу конфигурации
- uvm_config_db#(virtual interface):: set (null, "*", "vif", vif);
- В дравере и мониторе в build_phase таск добавить вызов метода получающего указатель на интерфейс из базы конфигурации
- uvm_config_db#(virtual interface):: get (this, "", "vif", vif);
Шаг 7. Тест/Окружение
- Создать класс наследуемый от uvm_test с именем my_uvm_test
- Реализовать метод build_phase этого класса
- Создание объекта агента
- Создание объекта сиквенсы
- Реализовать run_phase теста :
- Поднять объект управления (raise_objection)
- Запустить сиквенс на сиквенсере использую метод start()
- Снять объект управления (drop_objection)
Шаг 8. Запустить тест на выполнение
- В топ модуле после создания интерфейса реализовать initial begin end
- В блоке begin end вызвать метод run_test ("my_uvm_test"); my_uvm_test - имя, которые вы дали классу теста.
Как выбрать вариант
Для задания 1 варианты выбираются согласно табелю успеваемости.
Например: если вариант 3.2 - это значит что необходимо разработать транзакцию SPI без использования UVM макросов.
Спецификации
- APB
- UART
- SPI
Задание
- По спецификации интерфейса (*) блока разработать транзакцию, позволяющую описать все доступные операции на заданном интерфейсе:
- с использованием UVM макросов.
- без использования UVM макросов.
- Создать пакет, реализующий весь функционал агента (сиквенсер, драйвер, монитор) из существующих файлов описания всех составных блоков и скомпилировать его. (Можно использовать файлы из проекта https://www.edaplayground.com/x/3ru7)
- Реализовать объект конфигурации агента, который содержит методы настройки в три режима: по умолчанию активный ведущий, пассивный, активный ведомый режимы
Дополнительно
- Реализовать фазу сброса в драйвере
- Реализовать фазу, предшествующую фазе сброса
- Подменить драйвер в агенте, из окружения в фазе создания компонент
- Создать домен фазы выполнения для агента и подключить агент к этому домену