Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Верификация цифровых устройств. Курс лекций. Учебное пособие
.pdf
super.connect_phase(phase);
u_agent.uart_ap.connect(u_sb.sb_export_before);
b_agent.bus_ap.connect(u_sb.sb_export_after);
endfunction: connect_phase
endclass: uart_env
В класс окружения в данном примере подключаются два агента – для интерфейса
uart и для шины. При этом интерфейс шины
настраивается на пассивный режим работы в фазе build. Также
подключается табло для сбора транзакций. Порты TLM соединяются с агентами в фазе connect. Данное простое окружение для блока
uart может в дальнейшем, при переходе на другой уровень отлад-
ки, быть интегрировано в окружение более высокого уровня, по
аналогии с подключением агентов здесь.
7.2. Тест
После того как готово UVM окружение, начинается разработка
теста. Очень часто, особенно для крупных проектов, разработкой
теста и разработкой тестового окружения занимаются разные люди. Тестовое окружение универсально и используется в нескольких
тестах. Тесты же направлены на проверку заданных функций и покрытия конкретных пунктов тестового плана. Как правило, создается базовый тестовый класс, в который подключается тестовое
окружение и выполняются общие для всех тестов действия, а далее
отдельные тесты наследуются от этого базового класса для реализации требуемого от них функционала. Рассмотрим класс базового
теста:
class base_uart_test extends uvm_test;
uart_env m_top_env;
uart_cfg m_cfg0;
`uvm_component_utils (base_test)
function new(string name,uvm_component parent=null);
super.new (name, parent);
endfunction
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
– 71 –

m_top_env = uart_env::type_id::create
("m_top_env",this);
m_cfg0 = uart_cfg::type_id::create
("m_cfg0",this);
set_cfg_params ();
uvm_config_db #(uart_cfg):: set (this,
"m_top_env.b_agent", "m_cfg0", m_cfg0);
endfunction
virtual function voidset_cfg_params ();
if(! uvm_config_db #(virtualuart_if):: get (this,
"", "uart_if", m_cfg0.vif))
begin
`uvm_error (get_type_name (), "DUT Interface not
found !")
end
m_cfg0.active = UVM_ACTIVE;
endfunction
virtual function void end_of_elaboration_phase
(uvm_phase phase);
uvm_top.print_topology ();
endfunction
function void start_of_simulation_phase (uvm_phase
phase);
super.start_of_simulation_phase (phase);
uvm_config_db#(uvm_object_wrapper)::set(this,"m_top_env.uar
t_agent.sequencer.main_phase","default_sequence",
base_sequence::type_id::get());
("m_seq");
endclass
endfunction
virtualtask run_phase (uvm_phase phase);
uart_seq m_seq = uart_seq::type_id::create
super.run_phase(phase);
phase.raise_objection (this);
m_seq.start(m_env.seqr);
phase.drop_objection (this);
endtask
– 72 –

Все тесты наследуются от класса. В начале теста создаются дескрипторы используемых в тесте UVM окружения и конфигурационного объекта. Конфигурационный объект – класс, унаследованный от
uvm_object и включающий в себя все необходимые поля
для настройки окружения. В приведенном примере он будет иметь
только одно поле active для выбора режима работы агентов.
В фазе build создаются экземпляры всех объектов и вызывается
функция set_cfg_params, которая выполняет настройку конфигурационного объекта, после чего этот объект заносится в конфигурационную базу UVM. На работе конфигурационной базы следует
остановиться чуть подробнее.
Конфигурационная база uvm_config_db обеспечивает доступ к
централизованной базе данных, где можно хранить и получать информацию о типе. config_db может содержать скалярные объекты,
дескрипторы классов, очереди, списки или даже виртуальные интерфейсы. База данных имеет две таблицы – имен и типов, и каждый ресурс, помещенный в базу данных, заносится в обе. Таким
образом, каждый ресурс может быть извлечен по имени или типу.
База данных доступна во всех элементах тестового окружения. Для
сохранения и извлечения данных из базы используются статические методы uvm_config_db :: set и uvm_config_db :: get соответственно.
void uvm_config_db#(type T = int)::set(uvm_component
cntxt, string inst_name, string field_name, T value);
typeT используется в качестве параметра класса uvm_config_db
для идентификации типа объекта, который заносится в
uvm_config_db. Для переменных перечислимого типа надо исполь-
зовать
int.
cntxt – указатель на класс-наследник класса uvm_component,
этот параметр определяет область видимости записи.
inst_name – имя объекта относительно cntxt, в котором видно
запись.
field_name – имя записи, value – записываемое значение.
Параметры функции
set () – cntxt, inst_name и field_name
позволяют использовать несколько разных путей к одному и тому
же объекту.
Cntxt использует фактическую иерархию объектов,
– 73 –

тогда как inst_name и field_name используют путь иерархии с
именами, данными объектам в методе
В полях
inst_name и field_name допускается использование
create () / new ().
символов «*», «+», «?» для обозначения произвольных символов.
«*» означает ноль или больше произвольных символов, «+» – один
и больше, а
Для извлечения данных из базы данных используется метод
uvm_config_db#(<type>)::get(uvm_component cntxt, string
inst_name, string field_name, ref value);
«?» – строго один.
get:
Параметры данной функции аналогичны функции set, за исключением того, что функция использует их не для записи, а для
поиска.
Еще одна полезная функция UVM – это build-time
configuration, т.е. настройка во время сборки. Во время фазы
build, при вызове родительской реализации функции для
uvm_component, происходит автоматический вызов функции get
для всех полей класса, которые есть в базе денных. Для этого поля
должны быть зарегистрированы в базе uvm с помощью макросов, и
super.build_phase(phase) должна вызываться в фазе build.
Касательно применения uvm_config_db. В состав окружения в
общем случае входят несколько агентов, в каждый агент, как минимум, входит драйвер, монитор. Используя uvm_config_db, можно
задать значения конфигураций для всех компонент теста напрямую, прописав верные пути. Но правильнее передавать конфигурации по иерархии. Т.е. тест задает настройки окружения, окружение
передает их агенту, при необходимости внеся правки и задав значения по умолчанию, если от теста ничего не получено. И так же
драйвер получает данные от агента.
7.3. Виртуальные последовательности
и генераторы последовательностей
В примерах последовательностей, которые были рассмотрены
ранее, каждая последовательность управляла только одним драйвером и исполнялась только на одном генераторе последовательностей. Это могли быть и вложенные последовательности, где одна
– 74 –

последовательность могла поочередно запускать подпоследовательности, но тем не менее, все они исполнялись только на одном
генераторе.
В реальности последовательности за исключением наиболее
простых и базовых требуют одновременного взаимодействия с несколькими интерфейсами. Это, в свою очередь, требует наличия
генератора последовательностей, которые будут иметь доступ к
нескольким агентам.
Генераторы последовательностей, которые содержат в себе дескрипторы нескольких дочерних генераторов, называют виртуаль-
ными генераторами последовательностей. Соответственно, последовательности, вызывающие запуск дочерних на нескольких
генераторах, – виртуальными последовательностями. Термин
«виртуальный» применяется к генераторам и последовательностям,
так как ни те, ни другие не взаимодействуют напрямую с драйвером, т.е. не генерируют транзакции. Виртуальные генераторы последовательностей не исполняют последовательности сами, а запускают их на дочерних генераторах, а виртуальные последовательности не генерируют транзакции, а контролируют взаимную
работу других последовательностей.
Рассмотрим следующий пример:
class uart_v_seqr extends uvm_sequencer;
uvm_sequencer#(uart_item)u_seqr;
uvm_sequencer#(bus_item) b_seqr;
function new(string name='uart_v_seqr', uvm_component
parent);
...
endfunction
endclass
class uart_v_seq_base extends uvm_sequence;
`uvm_object_utils(uart_v_seq_base)
`uvm_declare_p_sequencer(uart_v_seqr)
function new(string name='uart_v_seq_base');
super.new(name)
endfunction
endclass
– 75 –

class uart_v_seq1 extends uart_v_seq_base;
`uvm_object_utils(uart_v_seq1)
function new(string name='uart_v_seq1');
super.new(name)
endfunction
task body();
uart_sequence u_seq;
bus_sequence b_seq;
u_seq = uart_sequence::type_id::create('u_seq');
b_seq = bus_sequence::type_id::create('b_seq');
fork begin
u_seq.start(p_sequencer.u_seqr);
b_seq.start(p_sequencer.b_seqr);
end
...
join
endtask
endclass
class base_test extends uvm_test;
`uvm_component_utils(base_test);
uart_v_seqr v_sqr;
uart_env env;
function new(string name='base_test',uvm_component parent =null);
endfunction
function build_phase(uvm_phase phase);
env = uart_env::type_id::create('env');
v_sqr = uart_v_seqr::type_id::create('v_sqr');
endfunction
function connect_phase(uvm_phase phase);
v_sqr.u_seqr = env.u_agent.sequencer;
v_sqr.b_seqr = env.b_agent.sequencer;
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection();
v_seq1 v_seq;
vseq = v_seq1::type_id::create('v_seq');
v_seq.start(v_sqr);
phase.drop_objection();
endtask
endclass
– 76 –

Класс uart_v_seqr описывает виртуальный генератор последовательностей. Этот генератор содержит в себе два дескриптора дочерних генераторов – u_seqr и b_seqr, которые позволяют исполняемой на нем последовательности получать доступ в два различных агента, подключенных к интерфейсу uart и шине.
uart_v_seq_base – базовая виртуальная последовательность,
которая является основой для создания последовательностей в тесте. Используя макрос `uvm_declare_p_sequencer, она получает
дескриптор виртуального генератора последовательностей, с помощью которого последовательности, унаследованные от данной,
например uart_v_seq1, смогут получать к нему доступ. Как рассказывалось в одной из предыдущих лекций, этот макрос создает переменную p_sequencer, являющуюся указателем на генератор последовательностей, на котором запущена вызвавшая его последовательность.
Класс uart_v_seq1 – виртуальная последовательность, которая
создает две подпоследовательности и запускает их на двух различных генераторах, получив к ним доступ через указатель на свой
генератор последовательности p_sequencer.
В base_test иллюстрируется работа с виртуальным генератором и виртуальными последовательностями. При создании окружения виртуальный генератор последовательностей создается точно так же, как и все компоненты – с использованием функции cre-
ate. В фазе подключения два дескриптора этого генератора под-
ключаются к двум, соответствующим им генераторам последовательностей в агентах окружения.
В фазе run создается экземпляр виртуальной последовательности, которая и запускается на виртуальном генераторе последовательностей.
В данном примере следует обратить внимание еще на одну деталь. В начале фазы run теста вызывается метод фазы
phase.raise_objection().Этот метод накладывает возражение на
переключение фаз, и пока это возражение не будет снято, фазы не
будут переключены. По завершении выполнения виртуальной последовательности метод phase.drop_objection(); снимает возражение на завершение фазы. Если нет возражений от других элементов окружения, фаза будет завершена.
– 77 –

Лекция 8. Утверждения и функциональное покрытие
Введение
После того как тестовое окружение готово и тесты написаны,
возникают вопросы: как убедиться, что тесты проверили все, что
надо? Что не было нарушений протокола, которые не отразились на
результате, но могут стать причиной фатальных ошибок в реальном устройстве? Для этого язык SystemVerilog содержит такие конструкции, как assert и covergroup. Эти два механизма подробно
рассмотрены в лекции.
8.1. Утверждения
В SystemVerilog есть два вида утверждений: непосредственные
(assert) и параллельные (assert property).
Непосредственные утверждения являются процедурными операторами. Assert – утверждение, что что-то должно быть истиной,
подобно оператору if. Разница в том, что оператор if не утверждает,
что выражение истинно, а просто проверяет его истинность.
if(A == B)...
assert(A == B);
В отличие от if, если утверждение не верно, то генерируется
ошибка. По аналогии с if, assert имеет две ветки возможных действий – на случай успешного и неуспешного выполнения утверждения. Причем любое действие может быть пропущено.
assert(A==B) $display("OK. A equals B");
else $error("It's gone wrong");
Нарушение утверждений имеет связанные с ними уровни важности. Существуют три системных уровня важности, которые могут быть включены в ветку нарушения: $fatal, $error (по умолчанию), $warning или $info. В качестве действия в случае успеха
– 78 –

или неудачи может выступать любое процедурное выражение языка. В том числе объединенное в
begin…end.
Параллельные утверждения устанавливают, что указанные
свойства должны быть истинными. Например, «сигналы чтения и
записи никогда не должны использоваться вместе».
assert property(!(Read&&Write));
Параллельные утверждения проверяются на протяжении всего
моделирования. Они обычно появляются вне
initial или always
блоков в модулях, интерфейсах и программах.
При этом параллельные утверждения могут выполнять не только одномоментные сравнения, но и контролировать последовательности действий.
assert property (@ (posedgeClk) Req | -> ## [1: 2] Ack);
Req – простая последовательность (просто логическое выраже-
ние), а ##[1: 2]Ack – более сложное выражение последовательности, означающее, что Ack имеет значение true хотя бы на одном из
двух следующих тактов.
Конструкция импликации «|->» позволяет пользователю контролировать последовательности на соответствие определенным
критериям, например, прикрепить условие к последовательности и
учитывать последовательность, только если условие является истинным. Левый операнд импликации называется предшествующей
последовательностью, а правый – последующей.
Если нет совпадения с выражением предшествующей последовательности, импликация завершается успешно, возвращая
true.
Если есть совпадение, то для каждого успешного завершения
предшествующей последовательности проверяется выражение последующей последовательности.
Существует две формы импликации: перекрывающиеся с помощью оператора «|->» и не перекрывающиеся с помощью оператора «|=>».
Для перекрывающейся импликации, если есть совпадение в выражении предшествующей последовательности, первый элемент
– 79 –

последующего выражения последовательности оценивается на том
же такте:
s1 |-> s2;
Например, если последовательность s1 выполняется, то и s2
должна выполняться. Если s1 не выполняется, то s2 не важно.
Для параллельных утверждений свойства могут указываться как
непосредственно в операторе, так и создаваться отдельно, и затем
использоваться. Аналогичным образом последовательности могут
выделяться в отдельные блоки:
sequence req_sq
Req;
endsequence
sequence ack_sq
##[1:2] Ack;
endsequence
property not_read_and_write_p;
not(Read && Write);
endproperty
property handshake;
@(posedge Clk) req_sq |-> ack_sq;
endproperty
assert property(not_read_and_write_p);
assert property(handshake);
В приведенном примере одно из свойств работает асинхронно, а
второе срабатывает по переднему фронту синхроимпульса. Синхронизация может задаваться в последовательности, свойстве или
указываться перед утверждением в always блоке:
always @(posedge clk) assert property (p);
Большинство систем имеет сигнал сброса. И зачастую работа
утверждения во время сброса может нарушаться. По этой причине
в утверждениях надо указывать, что они не должны проверяться
– 80 –
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
