Поскольку мне нужно использовать моделирование UVM в работе, я записал процесс обучения и написал серию статей об обучении UVM. Большая часть содержания статьи взята из книги «UVM на практике», а также я нашел некоторую общедоступную информацию. Интернет и создание среды проверки UVM с нуля, которая содержит использование многих функций UVM, я считаю, что это может лучше помочь инженерам, которые только начинают понимать рабочий механизм UVM.
Отложив сначала UVM, подумайте, нужно ли нам обязательно внедрить стимул в DUT после написания программы, а затем получить результаты от DUT и сравнить их с эталонным модулем, чтобы проверить, верны ли результаты. Точно так же, как на картинке ниже:
image-20240226160314434
В UVM представлен механизм последовательности. Самая большая роль этого механизма заключается в разделении тестового примера и тестового стенда. Для проекта testbench — это относительно стабильная среда, и каждый модуль имеет разное тестовое содержимое, поэтому конкретные тестовые примеры сильно различаются. В UVM классы тестов и последовательностей всегда появляются парами, реализуя комбинацию тестового стенда и конкретных тестовых случаев. В классе тестов вы можете создать некоторые дифференцированные конфигурации тестового стенда на основе конкретного содержимого теста, а в классе последовательности вы можете реализовать конкретные детали тестового примера.
Поэтому в UVM для генерации транзакции используется последовательность, которая затем передается драйверу секвенсором, а затем передается DUT в качестве стимула. Другими словами, драйвер отвечает только за управление транзакциями, но не за их создание.
image-20240226160336255
В то же время UVM инкапсулирует секвенсор, драйвер и монитор для мониторинга стимула в in_agent, а монитор для мониторинга вывода DUT — в out_agent для улучшения возможности повторного использования кода. Модель выглядит следующим образом:
image-20240226160355173
Давайте объясним вышеперечисленные модули более подробно. Не имеет значения, если вы их еще не понимаете. Просто сначала разработайте концепцию. После того, как вы узнаете больше о следующих вещах, вы, естественно, поймете эти концепции.
Компонент и объект — это две самые основные концепции в UVM, а также те две концепции, которые новички, скорее всего, путают. uvm_object — самый простой класс в UVM. Почти все классы, о которых читатели могут подумать, наследуются от uvm_object, включая uvm_comComponent.
У uvm_comComponent есть две основные функции, которых нет у uvm_object:
Как видно из рисунка, две ветви являются производными от uvm_object. Все узлы дерева UVM состоят из uvm_comComponent. Только производные классы, основанные на uvm_comComponent, могут стать узлами дерева UVM или классом, непосредственно производным от него; uvm_object не может появиться в дереве UVM в виде узла.
UVM реализует древовидную структуру через uvm_comComponent. Все узлы дерева UVM по сути являются uvm_comComponent. У каждого uvm_компонента есть характеристика: когда они новые, им необходимо указать переменную типа uvm_comComponent и имени родителя:
function new(string name, uvm_component parent);
Все компоненты, составляющие среду, наследуются от класса uvm_comComponent. Это связано с тем, что все они наследуют механизм фаз от класса uvm_comComponent и проходят различные этапы фазы. Содержание фазы мы представим позже.
image-20240226160506470
Все драйверы должны быть производными от uvm_driver. Основная функция драйвера — запросить у секвенсора элемент последовательности (транзакцию) и передать информацию из элемента последовательности в порт тестируемого устройства. Это эквивалентно завершению преобразования с уровня транзакции в информацию уровня порта, которую получает тестируемое устройство. могу принять.
Все мониторы должны быть производными от uvm_monitor. Монитор делает противоположное драйверу: драйвер отправляет данные на вывод DUT, в то время как монитор получает данные от вывода DUT, преобразует полученные данные в последовательность_элементов уровня транзакции, а затем отправляет преобразованные данные на табло для сравнения. По сравнению с uvm_comComponent, uvm_monitor практически не имеет расширения.
Все последовательности должны быть получены из uvm_sequencer. Функция секвенсора заключается в организации последовательности и управлении ею. Когда драйвер запрашивает данные, он пересылает элемент последовательности, сгенерированный последовательностью, драйверу.
Обычно табло является производным от uvm_scoreboard. Функция табло заключается в сравнении данных, отправленных эталонной моделью и монитором соответственно, и на основе результатов сравнения судить о правильности работы ИУ.
Для эталонной модели в UVM не определен класс. Итак, вообще говоря, эталонные модели напрямую являются производными от uvm_comComponent. Функция эталонной модели — имитировать ИУ и выполнять те же функции, что и ИУ. DUT представляет собой последовательную схему, написанную на Verilog, и эталонная модель может напрямую использовать функции языка высокого уровня SystemVerilog, а также вызывать другие языки через интерфейсы, такие как DPI, для выполнения тех же функций, что и DUT.
Все агенты должны быть производными от uvm_agent. По сравнению с предыдущими роль uvm_agent не так очевидна. Он просто инкапсулирует драйвер и монитор вместе и решает, создавать ли экземпляр только монитора или и драйвера, и монитора, на основе значений параметров. Использование агентов в основном рассматривается с точки зрения возможности повторного использования. Если возможность повторного использования не учитывается при построении платформы проверки, то агент фактически не нужен.
Все env (сокращение от Environment) должны быть производными от uvm_env. env инкапсулирует все фиксированные компоненты, используемые на платформе проверки. Таким образом, когда вы хотите запустить разные тестовые примеры, вы просто создаете экземпляр этого окружения в тестовом примере.
Все тестовые примеры должны быть производными от uvm_test или его производных классов. Различные тестовые примеры сильно различаются, поэтому классы, производные от uvm_test, различны. В любом производном тестовом примере необходимо создать экземпляр env. Только в этом случае, когда тестовый пример выполняется, данные могут быть отправлены в DUT нормально и данные DUT могут быть получены нормально.
image-20240226160535285
image-20240226160543590
В предыдущем разделе мы говорили о Занятиях. в УВМ, вы также можете увидеть отношения наследования различных классов в UVM. Поняв это, давайте посмотрим на древовидную в УВМ. структура。UVMвsequencer、driver、monitor、agent、model、 Табло, окружение и т. д. — все это узлы дерева. Зачем организовывать его в виде дерева? Потому что в качестве платформы проверки древовидная структура является очень подходящим методом управления.
Полная древовидная структура UVM показана на рисунке ниже:
uvm_top — глобальная переменная, экземпляр uvm_root, а также единственный экземпляр. Ее реализация очень умна. А uvm_root является производным от uvm_comComponent, поэтому uvm_top по сути является uvm_comComponent, который является корнем дерева. Родительским элементом uvm_test_top является uvm_top, а родительским элементом uvm_top является null.
uvm_top предоставляет ряд методов для управления симуляцией, таких как фазовый механизм, объектный механизм для предотвращения выхода из симуляции и т. д.
Функции, связанные с иерархией
UVM предоставляет ряд интерфейсных функций для доступа к узлам дерева UVM. Наиболее важными из них являются следующие:
extern virtual function uvm_component get_parent ();
extern function uvm_component get_child (string name);
extern function void get_children(ref uvm_component children[$]);
uvm_component array[$];
my_comp.get_children(array);
foreach(array[i])
do_something(array[i]);
extern function int get_num_children();
extern function int get_first_child (ref string name);
extern function int get_next_child (ref string name);
string name;
uvm_component child;
if (comp.get_first_child(name))
do begin
child=comp.get_child(name);
child.print();
end while (comp.get_next_child(name));