Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Верификация цифровых устройств. Курс лекций. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
06.09.2026
Размер:
1 Мб
Скачать
Рис. 4.2. Подключение порта к экспорту
Генератор имеет порт put_port, в который записывает сгенери­рованные элементы. При этом при создании порта в качестве пара­метра указывается тип передаваемых элементов:
class producer extends uvm_component;
uvm_blocking_put_port #(uart_trans) put_port;
function new(string name, uvm_component parent);
put_port =new(“put_port”,this);
...
endfunction
virtual task run();
uart_trans t;
for(int i = 0; i < N; i++)begin
put_port.put(t);
end
endtask
На стороне получателя аналогично объявляется экспорт и пере­гружается функция, которая будет запускаться при получении дан­ных (рис. 4.3). Т.е. фактически, когда генератор вызывает функцию
put(), он вызовет функцию получателя:
class consumer extends uvm_component;
uvm_blocking_put_imp #(uart_trans, consumer) put_export;
...
task put(uart_trans t); ... // обработка транзакции endtask
endclass
Рис. 4.3. Подключение экспорта к порту
– 41 –
В случае, когда получатель инициирует передачу, используется другой тип интерфейсов – get. Теперь уже у генератора реализует­ся задача get(), которую вызывает получатель:
class get_consumer extends uvm_component;
uvm_blocking_get_port #( uart_trans) get_port;
function new(string name, uvm_component parent);
get_port =new(“get_port”,this);
...
class get_producer extends uvm_component;
uvm_blocking_get_imp #( uart_trans, get_producer) get_export;
...
task get(output uart_trans t);
Но недостаточно объявить порты в составе классов. Необходи­мо установить соединение между конкретными экземплярами этих портов. Это делается в более высоком по иерархии модуле. Для этого вызывается метод
put или get какого объекта надо запустить:
connect(), который указывает, функцию
class my_env extends uvm_env;
...
virtual function void connect_phase(uvm_phase phase);
producer.put_port.connect(consumer.put_export);
get_consumer.get_port.connect(get_producer.get_export);
...
endfunction
endclass
В приведенном выше базовом примере для дет активен только при вызове метода
put(). Однако часто бывает
put получатель бу-
необходимо, чтобы компоненты работали независимо, когда гене­ратор создает транзакции в одном процессе, в то время как получа­тель должен работать с этими транзакциями в другом. Для этих целей в UVM предусмотрен канал
uvm_tlm_fifo. Uvm_tlm_fifo ре-
ализует все методы интерфейса TLM, поэтому генератор помещает транзакцию в транзакцию из
uvm_tlm_fifo, а получатель независимо получает
fifo. Экземпляр этого класса создается и подключа-
ется к портам генератора и получателя (рис. 4.4).
– 42 –
Рис. 4.4. Использование TLM_FIFO
Все рассмотренные ранее задачи чтения/записи в порты являют­ся блокирующими, т.е. они не могут завершиться неудачно. Вы­звавший их процесс останавливается до тех пор, пока не будет вы­полнено необходимое действие. Существуют также неблокирую­щие порты:
class consumer extends uvm_component;
uvm_get_port #(uart_trans) get_port;
...
for(int i=0; i<10; i++)
if(get_port.try_get(t))
...
endclass
try_get() – функция (в противовес задачам для блокирующих
портов), которая пытается прочитать данные, и возвращает FLASE, если это не получилось. Гарантируется, что функция завершается в том же дельта-цикле, когда вызвана. Аналогично работают функ­ции try_peek() и try_put().
Еще один момент, который следует рассмотреть, – это примене­ние TLM портов при иерархическом тестовом окружении. В основе методологии UVM лежит разделение тестового окружения на мак­симально универсальные, повторно используемые и взаимозаменя­емые блоки. Это приводит к тому, что нельзя просто подключаться к портам внутри компонента, хотя язык это позволяет (
agent.sequencer.put_port). В этом случае тестовое окружение
становится жестко привязанным к данному элементу. Вместо этого надо создавать порты во всей иерархии элементов и подключать их друг к другу (рис. 4.5).
Рис. 4.5. Пример иерархического подключения портов
– 43 –
В данной системе связи А, B, F, D соответствуют уже рассмот­ренным ранее соединениям порта и экспорта. Но С и E – это соеди­нение порта с портом и экспорта с экспортом, которое также вы­полняется с помощью функции connect. При этом функция con-
nect вызывается для источника данных. Пример реализации полу-
чателя в таком случае:
class consumer extends uvm_component;
uvm_put_export #(uart_trans) put_export;
uvm_tlm_fifo #(uart_trans) fifo;
...
function void connect_phase(uvm_phase phase);
put_export.connect(fifo.put_export);
...
endfunction
endclass
Все рассмотренные ранее порты предполагали соединение точ­ка-точка, т.е. всегда был генератор данных и их получатель. Но при построении верификационного окружения часто возникает задача передать данные нескольким получателям одновременно – напри­мер, для анализа данных в нескольких элементах проверки.
Для таких целей в UVM введен личие от
put/get портов данный порт не требует, чтобы он был
analysis_port (рис. 4.6). В от-
обязательно подключен, равно как он может быть подключен к не­скольким получателям ( реализацию функции
export). Каждый получатель должен иметь
write, которая вызывается для всех получа-
телей, подключенных к порту.
Рис. 4.6. Подключение analysis порта
– 44 –
Пример записи в порт:
uvm_analysis_port#(uart_trans) ap;
...
ap.write(t);
и чтения из порта:
class uart_sub#(type T =uart_trans)extends uvm_subscriber #(T);
...
function void write(T t);
...
При этом в данном примере класс sub1 унаследован от класса
uvm_subscriber. В этом классе уже объявлен analysis_export порт.
Поэтому требуется только перегрузить реализацию функции write.
Подключение этого типа портов выполняется аналогично – для порта вызывается функция
connect для каждого получателя дан-
ных:
Rx.ap.connect(sub1.analysis_export);
Rx.ap.connect(sub2.analysis_export);
UVM также включает значение uvm_tlm_analysis_fifo, кото­рое является аналогом uvm_tlm_fifo с соответствующим типом портов. Данное FIFO не ограничено по размеру. Источник данных может записать в него данные, а получатель прочитать их позднее.
– 45 –
Лекция 5. UVM базовые элементы окружения и фазы теста
Введение
Важное место в работе тестового окружения занимают меха­низмы синхронизации работы всех элементов – фазы. В лекции рассмотрен механизм работы фаз. Вторая часть лекции посвящена описанию устройства и процессу разработки одного из основных элементов окружения – агента – и его составляющих – драйвера и монитора.
5.1. Фазы теста
стового окружения, построенного по методологии UVM. Построе­ние этого окружения выполняется с использованием входящих в состав библиотеки базовых классов, которые заранее реализуют основной функционал каждой компоненты. Эти классы могут вза­имодействовать между собой, передавая и принимая транзакции. И все эти элементы работают параллельно. Возникает вопрос: как обеспечить синхронизацию работы этих компонент? Ведь, если генератор начнет передавать пакеты в неподключенный порт или драйвер попробует прочитать данные из порта, который еще даже не создан, – это может приводить к ошибкам.
используется механизм фаз работы теста. Фазы являются вирту­альными методами, которые определены в базовом классе
uvm_component. При реализации этих методов в расширенных клас-
сах-наследниках надо явно вызывать метод родителя, включив оператор низируется средами UVM – переход к следующей фазе теста вы­полняется только после того, как все компоненты сняли ограниче­ние на этот переход, т.е. завершили текущую фазу.
В предыдущей лекции была рассмотрена общая структура те-
Для решения задачи синхронизации работы компонент в UVM
super. <Имя фазы> _phase (phase); Работа фаз синхро-
– 46 –
Всего определено три группы фаз:
сборки;
выполнения;
очистки.
5.1.1. Фазы сборки
Фазы сборки – это build phase, connect phase и end_of_elaboration phase. Все эти фазы являются функциями, ко-
торые выполняются до нулевого момента времени моделирования. Целью этих этапов является создание тестовых компонентов и их соединение для создания тестового окружения. В тестовых окру­жениях на Verilog эти шаги не были необходимыми, поскольку все компоненты являлись статическими модулями. Все статические компоненты создаются во время компиляции. Однако компоненты в UVM являются классами, которые являются динамическими объ­ектами, поэтому необходимо явно их создавать.
Фаза build_phase используется для создания компонентов те­стового окружения. После запуска теста (вызов функции
()) создается корневой компонент тестового окружения и для него
запускается функция
build_phase(). Далее функция вызывается
run_test
при создании объектов по нисходящему прицепу. Задача данной фазы, помимо непосредственно создания объектов, – получение их конфигураций и формирование конфигураций для нижележащих компонент. После завершения фазы гарантируется, что все объек­ты, унаследованные от
uvm_component, были созданы.
Следующая фаза – connectphase. После того, как все компонен­ты созданы, начинается их соединение. Прежде всего это соедине­ние TLM портов. К моменту завершения работы данной фазы все соединения между компонентами должны быть установлены.
Фаза end_of_elaboration phase – заключительная фаза сборки окружения. После того, как все компоненты собраны и подключе­ны на вышеуказанных этапах, может потребоваться внести коррек­тировки в тестовое окружение – эти операции могут выполняться в данной фазе. Также эта фаза используется для печати итоговой конфигурации тестового окружения.
– 47 –
5.1.2. Фазы выполнения
К моменту запуска фаз выполнения время моделирования все­еще находится на отметке ноль, хотя некоторое количество дельта­циклов могло выполниться к этому моменту. Фазы выполнения: start_of_simulation_phase и run_phase, которая имеет 12 подфаз.
Фаза start_of_simulation вызывается между созданием тестово­го окружения и фактическим моделированием. Обычно она ис­пользуется для печати сообщений или отображения информации о конфигурации.
run_phase – единственная фаза, которая реализуется не функци­ей, а задачей. Она имеет 12 подфаз: reset_phase, configure_phase, main_phase, shutdown_phase, а также версии фаз до (pre_reset_phase) и после (post_reset_phase). Эти подфазы выпол­няются последовательно. При этом параллельно подфазам выпол­няется основная фаза – run_phase. Т.е. фаза pre_reset запустится одновременно с run_phase.
run_phase используется для генерации тестовых воздействий, подачи воздействий на тестируемое устройство и мониторинга вы­ходных данных. Компоненты, такие как драйвер и монитор, долж­ны реализовывать задачу run_phase.
Рассмотрим коротко назначение подфаз:
pre_reset_phase используется для выполнения операций перед применением сброса к DUT. Например, дождаться включения устройства.
reset_phase – для генерации сброса и применения к DUT или любому интерфейсу.
post_reset_phase – для выполнения любых операций после при­менения сброса. Например, анализ и вывод результатов выполне­ния сброса.
pre_configure_phase – для сбора информации о конфигурации и ожидания готовности компонентов к конфигурации после сброса.
configure_phase – для настройки DUT и инициализации памяти в тестовом стенде.
post_configure_phase – для ожидания передачи информации о конфигурации в DUT. Она нужна для гарантии, что основной те­стовый сценарий может быть запущен.
– 48 –
pre_main_phase – проверка готовности всех компонент начать моделирование.
main_phase – основная фаза. Используется для генерации и пе­редачи тестовых воздействий на DUT.
post_main_phase – эта фаза должна обеспечить завершение всех операций, запущенных в main_phase.
pre_shutdown_phase – ожидание завершения генерации воз­действий.
shutdown_phase – используется, чтобы гарантировать, что все тестовые воздействия достигли проверяемого устройства, а выход­ные данные получены из него.
post_shutdown_phase – заключительная фаза моделирования. Используется для выполнения любых необходимых действий.
5.1.3. Фазы очистки
Фазы очистки используются для извлечения информации из табло (scoreboards) и мониторов покрытия, чтобы определить, прошел ли тест, достигнуты ли его цели покрытия. Этапы очистки реализованы в виде функций, и поэтому для их выполнения не тре­буется модельное время. К фазам очитки относятся extract_phase,
check_phase, report_phase и final_phase.
extract_phase – фаза извлечения; она предназначена для извле-
чения и обработки информации из табло и мониторов функцио­нального покрытия. Может включать расчет статистической ин­формации, используемой на этапе отчета. Этот этап обычно ис­пользуется компонентами анализа.
check_phase – фаза проверки, используется для проверки пра­вильности поведения проверяемого устройства и выявления любых ошибок, которые могли возникнуть во время выполнения теста.
Фаза report_phase нужна для отображения результатов модели­рования или записи результатов в файлы. Этот этап обычно ис­пользуется компонентами анализа.
Заключительная фаза – final_phase; используется для выполне­ния любых операций, которые нужны в тестовом окружении.
Завершая рассмотрение механизма фаз, надо отметить еще две возможности. Во-первых, фазы синхронизированы и выполняются последовательно, так как находятся в одном домене синхрониза-
– 49 –
ции. Но при необходимости возможно создание нескольких доме­нов, в каждом их которых фазы теста будут выполняться независи­мо от другого домена.
Во-вторых, в UVM есть возможность создания своих фаз теста, если это необходимо. Хотя, учитывая и так большое их количество, это требуется крайне редко.
5.2. Базовые элементы тестового окружения
В состав агентов тестового окружения, как правило, входят драйвер, монитор, генератор последовательностей,
5.2.1. Драйвер
Драйвер – компонент, который выступает мостом между TLM и интерфейсами DUT. Драйвер извлекает транзакции из генератора последовательностей и отправляет их на интерфейсы тестируемого устройства. Драйвер во время фазы run вычитывает из входного порта
seq_item_port данные для отправки, формирует необходи-
мые для передачи данных сигналы и передает их в интерфейс DUT. После передачи драйвер записывает в порт подтверждение отправ­ки и считывает следующие данные.
Рассмотрим пример простого драйвера для работы с интерфей­сом uart:
class uart_driver extends uvm_driver#(uart_transaction);
`uvm_component_utils(uart_driver)
uart_transaction u_item;
virtual uart_if vif;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction:new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual uart_if)::get(this, “”,"vif",vif))
`uvm_fatal("NOVIF",{"virtual interface must be set
for: ", get_full_name(),".vif"});
endfunction: build_phase
– 50 –