Спец курс (Верификация цифровых схем)/Практические задания 3 — различия между версиями
Материал из Wiki
				
								
				
				
																
				
				
								
				Vidokq  (обсуждение | вклад)  (→Как выбрать варинат)  | 
			Vidokq  (обсуждение | вклад)   | 
			||
| (не показаны 40 промежуточных версий 3 участников) | |||
| Строка 1: | Строка 1: | ||
{{ВЦС_TOC}}  | {{ВЦС_TOC}}  | ||
| − | == Как выбрать   | + | = Общие для всех вариантов шаги лаб 3 =  | 
| − | {{ЖЛампа|24px}}Для задания 1  | + | |
| + | == Шаг 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 - имя, которые вы дали классу теста.  | ||
| + | |||
| + | = Как выбрать вариант =  | ||
| + | {{ЖЛампа|24px}}Для задания 1 варианты выбираются согласно  [[Спец курс (Верификация цифровых схем)/Табель успеваемости | табелю успеваемости]].  | ||
| + | |||
| + | Например: если вариант 3.2 - это значит что необходимо разработать транзакцию SPI без использования UVM макросов.  | ||
| + | |||
| + | == Спецификации ==  | ||
| + | # APB  | ||
| + | # UART  | ||
| + | # SPI  | ||
== Задание ==  | == Задание ==  | ||
| − | # По спецификации интерфейса (*) блока разработать транзакцию, позволяющую описать все доступные операции на заданном интерфейсе с использованием UVM макросов.  | + | # По спецификации интерфейса (*) блока разработать транзакцию, позволяющую описать все доступные операции на заданном интерфейсе:  | 
| − | ##   | + | ## с использованием UVM макросов.  | 
| − | + | ## без использования UVM макросов.  | |
| − | + | # Создать пакет, реализующий весь функционал агента (сиквенсер, драйвер, монитор) из существующих файлов описания всех составных блоков и скомпилировать его. (Можно использовать файлы из проекта [https://www.edaplayground.com/x/3ru7 https://www.edaplayground.com/x/3ru7])  | |
| − | + | # Реализовать объект конфигурации агента, который содержит методы настройки в три режима: по умолчанию активный ведущий, пассивный, активный ведомый режимы  | |
| − | + | ||
| − | + | == Дополнительно ==  | |
| − | + | ||
| − | # Создать пакет, реализующий весь функционал агента из существующих файлов описания всех составных блоков и скомпилировать его. (  | + | |
| − | # Реализовать объект конфигурации агента, который содержит методы настройки в три режима: по умолчанию активный ведущий, пассивный, активный ведомый режимы    | + | |
# Реализовать фазу сброса в драйвере  | # Реализовать фазу сброса в драйвере  | ||
# Реализовать фазу, предшествующую фазе сброса  | # Реализовать фазу, предшествующую фазе сброса  | ||
| − | # Подменить драйвер в   | + | # Подменить драйвер в агенте, из окружения в фазе создания компонент  | 
# Создать домен фазы выполнения для агента и подключить агент к этому домену  | # Создать домен фазы выполнения для агента и подключить агент к этому домену  | ||
== Спецификация APB интерфейса ==  | == Спецификация APB интерфейса ==  | ||
* [[File: IHI0024C_amba_apb_protocol_spec.pdf | IHI0024C_amba_apb_protocol_spec.pdf ]]  | * [[File: IHI0024C_amba_apb_protocol_spec.pdf | IHI0024C_amba_apb_protocol_spec.pdf ]]  | ||
| + | == Спецификация CSR интерфейса ==  | ||
| + | |||
== Спецификация UART интерфейса ==  | == Спецификация UART интерфейса ==  | ||
* [https://ru.wikipedia.org/wiki/Универсальный_асинхронный_приёмопередатчик Статья с википедии про UART]  | * [https://ru.wikipedia.org/wiki/Универсальный_асинхронный_приёмопередатчик Статья с википедии про UART]  | ||
== Спецификация SPI интерфейса ==  | == Спецификация SPI интерфейса ==  | ||
* [https://ru.wikipedia.org/wiki/Serial_Peripheral_Interface Cтатья с википедии про SPI]  | * [https://ru.wikipedia.org/wiki/Serial_Peripheral_Interface Cтатья с википедии про SPI]  | ||
Текущая версия на 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)
 - Реализовать объект конфигурации агента, который содержит методы настройки в три режима: по умолчанию активный ведущий, пассивный, активный ведомый режимы
 
Дополнительно
- Реализовать фазу сброса в драйвере
 - Реализовать фазу, предшествующую фазе сброса
 - Подменить драйвер в агенте, из окружения в фазе создания компонент
 - Создать домен фазы выполнения для агента и подключить агент к этому домену