Published using Google Docs
Лекция 5 - Написание тестовых последовательностей (v0.2)
Updated automatically every 5 minutes

Лекция 5. Написание тестовых последовательностей (v0.3)

Транзакции

Класс, содержащий пакет данных, с помощью последовательности которых можно описать поведение устройства в среде верификации также называют транзакцией. Пакет UVM предоставляет набор классов для работы как с одиночными транзакциями, так и с последовательностями транзакций (сиквенсами), позволяет контролировать время жизни, реализует интерфейсы, позволяющие управлять множеством последовательностей.  Иерархия классов представлена на рисунке 6.1.

uvm_ref_sequence (1).gif

Рис. 6.1 Иерархия классов для работы с транзакциями

(uvm-1.1d/docs/html/images/uvm_ref_sequence.gif)

Класс uvm_transaction содержит методы и переменные позволяющие хранить и контролировать время создания,  выполнения, удаления транзакции, а также методы позволяющие логировать эту информацию и записывать транзакции для последующего их отображения и анализа на временных диаграммах.

Основные методы перечислены в следующей таблице.

accept_tr

Метод позволяет указать, что транзакция была получена принимающим компонентом. Обычно <uvm_driver> вызывает метод  uvm_component::access_tr, который в свою очередь вызывает метод из транзакции. Происходит это в тот момент, когда возвращается транзакция из методов get_next_item(), get(), peek(), вызванных драйвером из uvm_driver::seq_item_port.

В некоторых протоколах транзакция не запускается на выполнение немедленно после получения.  Например, при работе шины после получения транзакции ее выполнение может быть заблокировано до тех пор пока принимающее устройство не будет готово к приему. Поэтому эта функция выполняет следующие действия:

  • В транзакции сохраняется время обращения к этой транзакции.
  • Устанавливается переменная-триггер. Какие либо процессы, ожидающие это событие, будут после нее запущены в следующем дельта-цикле.
  • Также вызывает метод do_accept_tr, в котором возможно реализовать пользовательскую обработку транзакции, после того как она стала доступна.

do_accept_tr

Функция позволяет реализовать пользовательскую обработку транзакции. Вызывается автоматически в методе accept_tr. Для корректной работы необходимо в функции первым вызывать метод super.do_accept_tr

begin_tr

Вызов этой функции указывает на то, что начато выполнение транзакции и она не является потомком другой транзакции. Компонент, получивший транзакцию чаще всего  и начинает ее выполнение. Обычно драйвер через метод uvm_component::begin_tr, вызывает его перед фактическим выполнением транзакции. Транзакция, исполняемая драйвером, всегда должна принадлежать сиквенсе. В этом случае метод  begin_tr вызывается у предка, в данном случае у сиквенса, а затем выполняется метод begin_child_tr, который уже запускает метод из транзакции.

Функция выполняет следующие действия:

  • В переменную транзакции start_time помещается текущее время вызова функции или значение, хранящееся в переменной begin_time, если выполнение не заняло времени.  Переменная begin_time может хранить любое время, как меньше текущего значения так и больше, но оно не должно быть меньше значения переменной accept_time.
  • Если запись транзакций активирована, то в базу данных хранящих транзакции записывается транзакция со временем равным start_time.
  • Так же вызывается метод do_begin_tr, который позволяет реализовать последовательную обработку или дополнительные действия после запуска транзакции.
  • В транзакции активируется событие-триггер begin_event, все процессы ожидающие этого триггера возобновят выполнение в следующем дельта цикле.

Возвращаемое значение представляет собой указатель на транзакцию, если активирована запись транзакции в базу. Это значит, что наличие ненулевого указателя будет зависеть от конкретной реализации этого метода.

begin_child_tr

Запуск функции указывает на то, что транзакция запускается внутри сиквенсы или другой транзакции и был передан указатель на вызвавшую транзакцию. Обычно выполняющий транзакцию компонент, вызывает ее через базовый метод uvm_component::begin_child_tr, сигнализируя о том, что транзакция уже запущена на выполнение.

Если указатель родителя равен 0, то функция работает аналогично begin_tr.

do_begin_tr

Функция позволяющая реализовать пользовательскую обработку. Для корректной работы необходимо вызывать  super.do_begin_tr.

end_tr

Вызов функции сигнализирует о том что выполнение транзакции закончено. Обычно выполняющий транзакцию компонент вызывает эту функцию. Для корректного вызова этой функции вручную нужно учитывать, что перед ее вызовом должны отработать функции begin_tr и begin_child_tr. Обычно драйвер вызывает метод uvm_component::end_tr, внутри которого вызывается метод из транзакции, после получения подтверждения, что транзакция закончилась. Также драйвер в транзакцию всегда сохраняет указатель, вызвавшей ее сиквенсы. В этом случае  метод begin_tr передает этот указатель в метод begin_child_tr.

Функция выполняет следующие действия:

  • В переменную транзакции end_time помещается текущее значение времени симуляции, или значение переменной остается текущее значение перменной end_time. Значение переменной может как больше так и меньше текущего времени симуляции, но не должно быть меньше времени записанном в переменной begin_time.
  • Если запись транзакций активирована и база в которую записываются транзакции активна, то метод записи наследуемый от интерфейса в uvm_object вызывается и происходит запись последних значений параметров транзакции. Затем транзакция считается завершенной. если переменная free_handle установлена, то транзакция считается готовой и не может дальше выполнятся (если поддерживает реализация).
  • Вызывает метод do_end_tr, который позволяет реализовать пользовательскую обработку данных в конце выполнения транзакции.
  • В транзакции активируется end_event событие и все процессы ожидающие этого события возобновят выполнение в следующем дельта цикле.

do_end_tr

Функция позволяющая реализовать пользовательскую обработку. Для корректной работы необходимо вызывать  super.do_end_tr.

get_tr_handle

Функция возвращает указатель объкта из которого была вызвана транзакция на выполнение. Этот указатель используется в методах begin_child_tr и begin_tr для начала записи транзакции.

disable_recording

Выключает запись потока транзакций. Этот метод не имеет эффекта на метод записи компонентов.

enable_recording

Включает поток записи транзакций. Интерпретация этого метода зависит от симулятора и реализации этой функций записи в нем. По умолчанию должен быть реализован аргумент командной строки позволяющий активировать запись. Если запись в базу включена то она происходит когда транзакция стартует и когда транзакция заканчивается

is_recording_enabled

Возвращает 1, если запись транзакций включена,
иначе 0

is_active

Возвращает 1, если выполнение транзакции было начато, но еще не закончено. Возвращает 0, если транзакция еще не была запущена на выполнение.

get_event_pool

Возвращает пул событий связанных с этой транзакцией. По умолчанию пул содержит следующие события: begin, accespt, end.  В этот список могут быть добавлены события для производных объектов.  Пул событий имеет тип общий тип используемый для пулов в UVM <uvm_pool #(T)>, а именно uvm_pool#(uvm_event).

set_initiator

Устанавливает указатель на компонент, который был инициатором запуска этой транзакции. Инициатором может быть компонент, который запустил транзакцию. Разработчик транзакции может определять, какой компонент будет считаться инициатором

get_initiator

Возвращает указатель на компонент, который был инициатором выполнения транзакции и был установлен с помощью метода set_initiator.

get_accept_time

Возвращают значения времени доступа, начала и конца транзакции соответственно.

get_begin_time

get_end_time

set_transaction_id

Устанавливает идентификационный номер для транзакции. Если не установлен через этот метод, то по умолчанию значение равно -1.  При использовании сиквенса для генерации последовательности транзакций, номер назначаетсяа автоматически и позволяет сиквенсеру получать ответ на запросы с заданным идентификационным номером, тем самым можно установить соответствие между транзакциями

get_transaction_id

Возвращает идентификационный номер транзакции.

        Все перечисленные методы вызываются автоматически при использовании конструкций start_item/finish_item (или `uvm_do* macro), вызываемых внутри сиквенсы, наследуемой от uvm_sequence#(REQ,RSP). А именно будут автоматически назначаться события begin_event, end_events через вызов описанных выше методов. Для назначения разрешений драйверу заполнять поля времени жизни транзакции, симуляторы поддерживают параметр командной строки +define+UVM_DISABLE_AUTO_ITEM_RECORDING, который нужно задавать при компиляции uvm_pkg. Также пользователь самостоятельно может добавить функционал, выполняющий некоторые действия на основе событий расположенных в event_pool.

Для реализации конвейерного протокола, драйвер может сделать пометку в транзакции, что она выполнена (вызов метода finish_item() или `uvm_do макроса) до того как ее передача-выполнение реально завершится на интерфейсе, в этом случае сиквенс может блокировать выполнение до того момента, пока не получит подтверждение о выполнении транзакции через получение ответа. Например:

task uvm_execute(item, ...);
   // can use the `uvm_do macros as well
   start_item(item);
   item.randomize();
   finish_item(item);
   item.end_event.wait_on();
   // get_response(rsp, item.get_transaction_id()); //if needed
endtask

Простой драйвер с поддержкой конвейера может содержать фазы адреса и данных его реализация представлена на в примере ниже:

task run();
   // this driver supports a two-deep pipeline
   fork
     do_item();
     do_item();
   join
endtask
task do_item();
 forever begin
   mbus_item req;
   lock.get();
   seq_item_port.get(req); // Completes the sequencer-driver handshake
   accept_tr(req);
     // request bus, wait for grant, etc.
   begin_tr(req);
     // execute address phase
   // allows next transaction to begin address phase
   lock.put();
     // execute data phase
     // (may trigger custom "data_phase" event here)
   end_tr(req);
 end
endtask: do_item

Для создания собственных транзакций следует использовать класс uvm_sequence_item, наследуемый от uvm_transaction. Этот класс реализует дополнительные интерфейсы, позволяющие работать с сиквенсером, а также методы вывода сообщений. Этот класс является также базовым для uvm_sequence, поэтому в нем заложены методы позволяющие также работать с сиквенсами. Эти методы перечислены в следующей таблице.

get_sequence_id

Внутренний метод, который не предназначен для пользовательского кода. Переменная  sequence_id это не просто целое число, запрос с использованием этой функции на получение номера является сигналом для пользователя  о том, что созданная сиквенс маркирована и к ней можно получить доступ по этому номеру.

Этот номер используется для того, чтобы сиквенсер мог отправить ответ обратно в сиквенс транзакцию - ответ на конкретную транзакцию запрос.

Этот метод вызывается автоматически через сиквенсер, в тот момент когда инициируется выполнение сиквенсы на сиквенсере. sequence_id является уникальным номером до тех пор пока сиквенс  не завершится или не будет прервана методом kill. Однако допускается существование нескольких сиквенсов с одинаковым идентификационным номером. Если запустить сиквенс снова после того как она выполнилась и уже получила уникальный номер, то будет назначен новый уникальный номер.

В свою очередь переменная  transaction_id также  назначается автоматически внутри сиквенса каждый раз когда созданная транзакция отправляется в сиквенсер. Значение по умолчанию переменой transaction_id равно -1.  Если пользователь самостоятельно выставит значение переменной transaction_id  то оно будет сохранено и использоваться в дальнейшем для идентификации транзакции.

Ответ передаваемый обратно из драйвера в сиквенсер, а из него в сиквенс основывается на значении переменной sequence_id. В свою очередь сиквенсы могут использовать  transaction_id для получения соответствующей транзакции ответа на запрос отправленный ранее.

set_item_context

Метод помещает указатели сиквенсы и сиквенсера в транзакции которые выполняются в них

set_use_sequence_info

Этот метод позволяет установить и получить значение бита use_sequence_info. Этот бит контролирует печать, копирование или запись информации сиквенса. По умолчанию значение этого бита равно 0, и это означает что информация о сиквенсе нигде не храниться и не используется. Если установить этот бит то информация о сиквенсе будет храниться и передаваться при копировании и печати этой сиквенсы

get_use_sequence_info

set_id_info

Метод позволяет скопировать sequence_id и transaction_id из другой сиквенсы или транзакции в текущую.

Главное назначение этого метода в том, чтобы пользователь мог отправить транзакцию ответа, в этом случае необходима скопировать данные о уникальных номерах сиквенса и транзакции из транзакции запроса в транзакцию ответа с помощью этого метода.

set_sequencer

Метод позволяет установить сиквенсер по умолчанию на котором будет выполняться сиквенс или все ее внутренние таски. Вызов этого метода происходит автоматически если мы запускаем сиквенс через метод start(указатель_на_сиквенсер). Если мы хотим выполнять таски описанные внутри сиквенсы по своему усмотрению, то нужно с помощью этого метода указать на каком сиквенсере они будут выполняться а затем вызывать задачи(tasks) из сиквенсы.

Этот метод выполняется немедленно без ожидания каких либо событий, поэтому не следует в ходе выполнения какой либо сиквенсы изменять сиквенсер на другой.

get_sequencer

Возвращает указатель на сиквенсер, который установлен по умолчанию для сиквенсы.

set_parent_sequence

Методы позволяет установить указатель на сиквенс, которой принадлежит транзакция. По умолчанию транзакция не принадлежит сиквенсе и имеет нулевой указатель.

get_parent_sequence

Методы позволяет получить указатель на сиквенс, которой принадлежит транзакция. Если транзакция не создавалась/запускалась в сиквенсе, то будет возвращен нулевой указатель.

set_depth

Метод позволяет установить вложенность сиквенсы. По умолчанию вложенность вычисляется автоматически. Однако пользователь может её изменить.

get_depth

Возвращает значение вложенности сиквенсы. Если в сиквенсе есть подсиквенс, то ее вложенность будет равно 1, а подсиквенсы 2. Если в подсиквенсе запускается еще одна сиквенс то ее вложенность будет равно 3, и т.д.

is_item

Метод может быть вызван как в сиквенсе так и в транзакции и позволяет отличить одно от другого, если значение 1 то транзакция, если 0 то сиквенс.

get_root_sequence_name

Предоставляет имя сиквенсы самого верхнего уровня.

get_root_sequence

Предоставляет указатель на сиквенс самого верхнего уровня.

get_sequence_path

Возвращает строку представляющую собой полное иерархическое имя сиквены, имя каждой вложенной сиквенсы отделяется символом “.”.

uvm_report

Транзакция и сиквенс будут использовать сиквенсер для вывода сообщений, если сиквенсер не установлен, то используется глобальный объект вывода сообщений.  

Вызов этих методов осуществляется через методы вывода сообщений класса uvm_component или uvm_report_object.

uvm_report_info

uvm_report_warning

uvm_report_error

uvm_report_fatal

Объявление полей и задание ограничений

Рекомендуется классы транзакций, используемых в среде верификации, наследовать от класса uvm_sequence_item.  Также рекомендуется регистрировать транзакцию с помощью макросов регистрации `uvm_object_utils, и все поля транзакции с помощью макросов  `uvm_field_*. Эти макросы регистрируют объект для дальнейшего использования его в механизме фабрики (подмена типа объекта без изменения кода), а также реализуют все функции копирования, сравнения, клонирования и вывода для переменных класса.

Существует возможность в макросах для регистрации полей, указать какие методы в макросе не будут реализованы. Для этого в макрос регистрации поля передается параметр. Каждому значению параметра соответствует бит вектор, у которого установлен только один бит, кроме UVM_ALL_ON, UVM_ALL_ON. Поэтому допускается выбор одновременно нескольких параметров, и задаются они через знак “побитового или” - “|”. В следующей таблице представлено описание параметров, которые можно использовать при регистрации атрибутов класса транзакции.

UVM_ALL_ON

Включает реализацию всех доступных в макросе методов

UVM_DEFAULT

В версии пакета UVM-1.1d работает аналгично UVM_ALL_ON, но в дальнейшем если набор функций расширится то значение по умолчанию останется на текущем значении, а UVM_ON_ALL будет расширен

UVM_NOCOPY

Отключает реализацию метода копирования

UVM_NOCOMPARE

Отключает реализацию метода сравнения

UVM_NOPRINT

Отключает реализацию метода печати в лог

UVM_NOPACK

Отключает реализацию метода упаковки

Ниже представлен пример использования регистрации в фабрике при описании класса транзакции.

`ifndef APB_ITEM__SV

`define APB_ITEM__SV

class apb_item extends uvm_sequence_item;

   typedef enum {READ, WRITE} kind_e;

   rand bit     [31:0] addr;

   rand logic [31:0] data;

   rand kind_e kind;  

   rand bit     [3:0]   strb;

  `uvm_object_utils_begin(apb_item)

     `uvm_field_int(addr, UVM_ALL_ON | UVM_NOPACK| UVM_NOCOMPARE);

     `uvm_field_int(data, UVM_ALL_ON | UVM_NOPACK);

     `uvm_field_int(strb, UVM_ALL_ON | UVM_NOPACK);

     `uvm_field_enum(kind_e,kind, UVM_ALL_ON | UVM_NOPACK);

   `uvm_object_utils_end

   

   function new (string name = "apb_item");

      super.new(name);

   endfunction

   function string convert2string();

     return $sformatf("kind=%s addr=%0h data=%0h",kind,addr,data);

   endfunction

endclass: apb_item

`endif // APB_ITEM

Сиквенсы

Сиквенс представляет собой класс, наследуемый от uvm_sequence, в котором реализован механизм, позволяющий генерировать наборы транзакций, отправлять их на выполнение через сиквенсер в драйвер, а также управлять процессом выполнения этих транзакций. В свою очередь uvm_sequence наследуется от uvm_sequence_base.

Рассмотрим интерфейсы, реализованные в этих классах.

Методы создания и управления запуском

is_item

Возвращает 1, если на основе этого класса создана транзакция, или 0, если сиквенс. Так как, по умолчанию, этот класс используется как базовый для сиквенсы, то функция всегда возвращает 0.

get_sequence_state

Возвращает состояние сиквенса в виде перечислимого типа uvm_sequence_state_enum

UVM_CREATED

UVM_PRE_START

UVM_PRE_BODY

UVM_BODY

UVM_ENDED

UVM_POST_BODY

UVM_POST_START

UVM_STOPPED

UVM_FINISHED

по значениям которого можно отслеживать фазы выполнения сиквенсы.

wait_for_sequence_state

Метод позволяет блокировать поток выполнения до тех пор, пока сиквенс не перейдет в заданное состояние, если сиквенс уже в одном из указанных состояний то управление возвращается немедленно.

wait_for_sequence_state(STOPPED|FINISHED);

start

virtual task start (

  uvm_sequencer_base         sequencer,                  

  uvm_sequence_base         parent_sequence =        null,

  int         this_priority         =         -1,

  bit         call_pre_post         =         1

)

Основной метод запуска сиквенса на выполнение, он блокирует поток выполнения до тех пор, пока сиквенс не будет выполнена.

Передаваемый параметр sequencer представляет собой указатель на сиквенсер, на котором требуется запустить созданную сиквенс, при этом сиквенс и сиквенсер должны быть совместимы.

Если parent_sequence равен null, то эта сиквенс считается корневой, в противном случае сиквенс считается принадлежащей другой сиквенсе.

Методы pre_do, mid_do, post_do по умолчанию вызываются автоматически в процессе выполнения сиквенсы.

По умолчанию параметр priority равен параметру родительской сиквенсы и равен 100. Другие приоритеты могут быть заданы используя аргумент  this_priority. Чем больше число тем больше приоритет.

Параметр call_pre_post отвечает за вызов методов сиквенсы pre_body, post_body. По умолчанию его значение равно 1, поэтому, если не задать обратного методы будут вызываться по умолчанию также.

pre_start

Метод, позволяет реализовать пользовательский функционал, который будет вызваться перед выполнением pre_body. Не следует вызывать его вручную.

pre_body

Метод, позволяет реализовать пользовательский функционал, который будет вызываться перед выполнением pre_body. Этот метод будет выполняться только, если сиквенс запускается через метод start. Если start вызывается с параметром call_pre_post  равным 0, то метод pre_body не будет вызван. Этот метод не должен вызываться пользователем вручную.

pre_do

Это пользовательская задача, реализующий пользовательский функционал. Задача запускается для сиквенсы родителя, если какая-либо сиквенс выполнила запрос wait_for_grant() и сиквенсер выбрал ее на выполнение. Или если выполняется транзакция, то перед ее рандомизацией.

Не смотря на то, что pre_do это задача, при ее реализации не рекомендуется реализовывать действия с потреблением времени моделирования, это может привести к непредсказуемому поведению драйвера. Этот метод не должен вызываться пользователем вручную.

mid_do

Функция позволяет реализовать пользовательскую обработку, причем ее вызов происходит автоматически, после того как транзакция была рандомизирована, но перед тем как отправлена в драйвер. Метод не должен вызываться пользователем вручную.

body

Эта задача, в которой размещается весь основный код пользовательского алгоритма генерации транзакции. Метод не следует вызывать вручную.

post_do

Функция реализуется пользователем и вызывается автоматически после того как драйвер сообщит о том что транзакция завершена, используя item_done или put метод. Метод не следует вызывать вручную.

post_body               

Метод реализуется пользователем и вызывается автоматически после того как выполнился метод body, в том случае если для запуска сиквенсы использовался метод start(). Если start(...) был вызван с параметром call_pre_post = 0, то post_body  не будет вызываться.  Задачу не следует вызывать вручную.

post_start

Метод реализуется пользователем и вызывается автоматически после того как выполнится post_body.

Метод не следует вызывать вручную.

starting_phase

Переменная содержит указатель на объект фазы, если он не нулевой то его значение будет определять фазу в которой была запущена сиквенс. Переменная  starting_phase устанавливается автоматически, если мы используем метод запуска сиквенса через назначение сиквенсы по умолчанию. Если мы не используем назначение сиквенсы по умолчанию, то значение переменной будет назначено в методе uvm_sequencer_base::start_phase_sequence, и будет соответствовать фазе сиквенсера. В свою очередь uvm_sequencer_base::start_phase_sequence запускается с помощью задач обработки фаз компонент (uvm_task_phase.svh)

 Эта переменная позволяет управлять процессом моделирования. Обычно приходится управлять объектами выполнения вручную, но можно добавить в сиквенс проверку и включение объекта выполнения  автоматически.

virtual task user_sequence::body();
if (starting_phase != null)

    starting_phase.raise_objection(this,

"user_seq not finished");
  ...

if (starting_phase != null)

    starting_phase.drop_objection(this,

"user_seq finished");
endtask

Методы для управления приоритетом запуска

set_priority

Приоритет последовательности может изменять в различные моменты времени. Если приоритет был изменен то сиквенсер будет использовать его в дальнейшем при запуске сиквенсов.

Значение по умолчаниб для сиквенса равно 100. Чем выше значение тем выше приоритет выполнения у сиквенсы.

get_priority

Функция позволяет получить текущий приоритет сиквенсы

is_relevant

По умолчанию функция возвращает значение 1, которое указывает что сиквенс всегда актуальна.

Пользователь может переопределить поведение функции, для того чтобы указывать сиквенсеру что после выполнения функции сиквенс больше не актуальна. Перед запуском сиквенсер каждый раз запрашивает состояние сиквенсы и приоритет, если сиквенс скажет что она не актуальна то сиквенсер не будет ее больше запускать пока она снова не станет актуальной в будущем.

Если все сиквенсы которые может опрасить сиквенсер не актуальны то вызывается задача  wait_for_relevant для всех сиквенсов и выполняется подсчет и опрос приоритетов по новому.

Каждый кто реализует функцию is_relevant() должен реализовывать метод  wait_for_relevan()t, который позволит сиквенсеру приостановить выполнение и подождать пока не появиться актуальных сиквенсов

wait_for_relevant

Этот метод вызывается сиквенсером, если все запрошенные сиквенсы не актуальны. Когда wait_for_relevant возвращает поток управления сиквенсер выполняет операцию арбитража заново.

Возвращения потока выполнения не гарантирует того, что появились актуальные сиквенсы, хотя в идеале так должно быть. Этот метод представляет задержку опроса сиквенсов в бесконечном цикле.

 

Если метод is_relevant реализован таким образом, что он может возвращать статус “не актуально”, (по умолчанию метод всегда говорит, что сиквенса актуальна), то сиквенса должна обязана иметь реализацию wait_for_relevant

lock

Функция выполняет запрос на блокировку и перехват потока выполнения у сиквенсера. Если сиквенсер задан как null, то запрос блокировки будет выполнен на текущем сиквенсере, по умолчанию. Этот запрос обрабатывается аналогично, остальным запросам. Запрос lock получит поток выполнения после того как все запросы вызванные перед этим выполняться, и после никакой другой заброс lock или grab не сможет получить доступ к потоку выполнения до тех пор пока блокировка не будет снята или запущенная сиквенс выполнится.

grab

Выполняет запрос на блокировку на заданном сиквенсере. Если не передавать аргументы, запрос блокировки будет выполнен на текущем сиквенсере по умолчанию.

Запрос grab поставит в сиквенс на выполнение перед всеми другими сиквенсами расположенными в очереди приоритетов. Т.е. при следующей проверка статуса сиквенса, самой приоритетной будет вызвавшая этот запрос, и она без ожидания завершения предыдущих сиквенсов начнет подавать транзакции. Это основное отличие от запроса lock.

После завершения поток выполнения будет снова передан для определения приоритета выполнения.  Запрос grab получит поток выполнения для сиквенсы, если никакие другие сиквенсы не выполняют в этот момент grab или lock сиквенсера.

unlock

Удаляет все запросы lock или grab выполненные из сиквенсы для сиквенсера. Если сиквенсер null, запрос будет выполнен на сиквенсере по умолчанию.

ungrab

is_blocked

Функция возвращает бит который указывает на то является ли сиквенс заблокированной в настоящий момент из-за того что был сделан запрос lock или grab из другой сиквенсы. Если возвращает 1, то сиквенс заблокирована, иначе нет.

 Если сиквенс не заблокирована, то любая другая сиквенс может прервать и заблокировать ее выполнение.

has_lock

Возвращает 1 если в сиквенс был вызван запрос lock, иначе 0.

Если сиквенс вызвала lock, дочерние сиквенсы могут также вызывать запрос lock самостоятельно, в этом случае дочерняя сиквенс будет иметь статус блокированной для выполнения ее на сиквенсере.

kill

Эта функция прервет выполнение сиквенсы и снимет все блокировки с сиквенсера по умолчанию, если во время выполнения сиквенсы такие были.

Сиквенсер перейдет в состояние STOPPED,и методы  post_body() и post_start() исполняться не будут.

Если сиквенс выполнила lock или grab запрос на сиквенсер отличный от сиквенсера по умолчанию, следует позаботиться, чтобы обязательно выполняется отмена регистрации сиквенса на сиквенсере, с помощью метода сиквенсера unregister_sequence().

do_kill

Функцию можно переопределить и реализовать в ней дополнительный функционал. Вызов будет происходить автоматически в тот момент, когда выполнение сиквенсы прерывается функциями sequence.kill() или sequencer.stop_sequences() (вызов последней более эффективен).

create_item

Позволяет создать и инициализировать транзакцию или сиквенс с использованием фабрики. Также позволяет ассоциировать создаваемую транзакцию с сиквенсером на котором ее в дальнейшем будут запускать.

start_item

Метода start_item, finish_item вместе запускают обработку транзакции. Если транзакция не создавалась перед этим с использованием create_item, то в этом методе она будет назначена на выполнение на сиквенсер по умолчанию, указатель на который храниться в переменной m_sequencer. Рекомендуется рандомизацию транзакции делать между вызовами функций start_item, finish_item, это гарантирует, что транзакция сгенерируется перед отправлением ее на драйвер.

finish_item

Метод сигнализиует о том что транзакция создана и заполнена. Между вызовами методов start_item/finish_item не должно быть дельта-циклов или задержек с потреблением времени, допускается любая обработка транзакции или ее генерация без потребления времени.

wait_for_grant

Эта задача отправляет запрос на разрешение выполнения сиквенсы на сиквенсере. Если параметр  item_priority не установлен, то будет использовано текущее значение приоритета сиквенсы. Если был выполнен запрос lock_request, в тот момент когда сиквенсер выполнял другую транзакцию, то блокировка будет выполнена немедленно перед завершением запроса на разрешение.

 (Важно что блокировка сиквенсы может быть выполнена и без того что сиквенс получила разрешение на выполнение, при условии что параметр is_relevant не установлен. )

Когда задача заканчивает выполнение, сиквенсер выбирает сиквенс для выполнения, и она должна послать send_request без какой либо задержки симуляционного времени, даже без дельта цикла. Вызов метода send_request отправит драйверу следующую транзакцию.

send_request

Эту функцию допускается вызывать только после wait_for_grant. Ее вызов отправит запрошенную транзакцию сиквенсеру, который пришлет ее драйверу. Если бит, отвечающий за рандомизацию установлен в 1, то перед тем как отправить транзакцию драйверу, она будет рандомизирована.

wait_for_item_done

Внутри сиквенса допускается по необходимости пользователя вызывать задачу wait_for_item_done.

Эта задача блокирует выполнение внутри сиквенса до тех пор пока драйвер не вызовет item_done или put. Если параметр transaction_id  не установлен, то вызов этого метода вернет выполнение, когда драйвер вышлет подтверждение для соответствующей транзакции.

Стоит обратить внимание что если transaction_id был задан и драйвер уже выполнил item_done или put, для этой транзакции, то задача продолжит выполнение, пропустив предыдущее уведомление.

Методы для получения ответа

use_response_handler

function void use_response_handler(bit enable)

Если вызвать метод с входным параметром равным 1, то ответ будет высылаться через вызов метода  response_handler, иначе ответ должен быть извлечен с помощью метода get_response.

По умолчанию ответ передаётся через вызов метода get_response, альтернативой этому методу является вызов функции response_handler.

get_use_response_handler

Возвращает значение use_response_handler бита.

response_handler

Если бит use_response_handler  имеет значение 1, то эта задача вызывается сиквенсером для получения ответа и передачи его в сиквенс.

set_response_queue_error_report_disabled

По умолчанию флаг отвечающий за выдачу сообщений об ошибке при переполнении включен.

Если много ответов было выслано в сиквенсер, и при этом ответы никто не забирает, то очередь ответов при достижении заданного значения считается переполненной и будет выдано предупреждение что ответов получено больше чем запрошено. Если установить бит в 0, то генерация ошибки будет отключена.

get_response_queue_error_report_disabled

Функция позволяет получить значение бита отвечающего за генерацию ошибки при переполнении очереди ответов.

set_response_queue_depth

Позволяет установить размер очереди ответов, по умолчанию она имеет величину равную 8. Если установить значение равное -1, то это будет означать что очередь ответов не имеет ограничения на глубину и не будет проверятся.

get_response_queue_depth

Функция позволяет получить текущее значение глубины очереди ответов

clear_response_queue

Функция очищает очередь ответов, для сиквенсы из которой была вызвана.

Методы класса uvm_sequence

send_request

Этот метод позволяет отправить транзакцию запроса в сиквенсер, который переправит ее драйверу. Если бит рандомизации включен, то транзакция перед отправкой в драйвер будет рандомизирована.  Эту функцию допускается вызывать только после выполнения задачи uvm_sequence_base::wait_for_grand

get_current_item

Функция возвращает транзакцию которая в текущий момент выполняется на сиквенсере. Если сиквенсер не выполняет в текущий момент никаких транзакций, метод вернет null.

Транзакция считается выполняющейся на сиквенсере, в моменты времени после вызова метода get_next_item() или peek() до  момента вызова метода get() или item_done().

Стоит обратить внимание что драйвер который вызывает только метод get() никогда не будет иметь текущей исполняемой транзакции, так как элемент будет считаться завершенным сразу же как он был получен.

get_response

Метод который по умолчанию должен использоваться в  сикевенсах для получения ответа. Если transaction_id не задан при вызове этого метода, эта задача вернет следующий ответ отправленный в сиквенс. Если нет доступных ответов в очереди, то задача заблокирует поток выполнения до тех пор пока не появится в очереди транзакция ответа.

Если при вызове задачи указан transaction_id, задача заблокирует поток выполнения до тех пор пока в очереди ответов не появится транзакция с заданным id.

По умолчанию размер очереди ответов равен 8. Поэтому для корректной работы по умолчанию задержка вызова метода получения ответа не должна превышать 8 транзакций, иначе будет генерироваться ошибка переполнения очереди ответов, и последующие ответы будут теряться.

Генерацию ошибки и потерю транзакций можно избежать вызвав метод set_response_queue_error_report_disabled, и при этом изменив размер очереди с помощью метода set_response_queue_depth.

Для запуска сиквенсы на выполнение, нужно указать сиквенсер, на котором она будет выполняться, и вызвать метод стартующий ее выполнение. Или запустить используя  макрос.

sub_seq.randomize(...); // optional
sub_seq.start(seqr, parent_seq, priority, call_pre_post)

После вызова этого метода из сиквенсы будет назначен сиквенсер, а также выполнены методы сиквенса в следующем порядке:

sub_seq.pre_start()        (task)
sub_seq.pre_body()         (task)  if call_pre_post==1
 parent_seq.pre_do(0)     (task)  if parent_sequence!=null
 parent_seq.mid_do(this)  (func)  if parent_sequence!=null
sub_seq.body               (task)  YOUR STIMULUS CODE
 parent_seq.post_do(this) (func)  if parent_sequence!=null
sub_seq.post_body()        (task)  if call_pre_post==1
sub_seq.post_start()       (task)

Последовательный запуск перечисленных выше методов следует учитывать и использовать при проектировании сиквенсов. Обычно в методах pre_body и post_body проверятся создан и активен ли объект выполнения, и если нет, то активируется. Это позволяет сиквенсе самой инициировать выполнение и гарантирует, что она выполнится до конца независимо от внешнего управления.

Макросы для генерации и запуска

Далее рассматриваются UVM макросы, которые позволяют запустить сиквенс на сиквенсере по умолчанию. При этом нет необходимости создавать сиквенс (объект класса) с помощью вызова конструктора new, т.к. в зависимости от макроса объект сиквенса будет создан и рандомизирован внутри макроса.

`uvm_create

`uvm_create(SEQ_OR_ITEM)

Макрос содержит код, который создает транзакцию или сиквенс с использованием фабрики. Макрос делает немедленную обработку без потребления времени. После выполнения макроса, пользователь может устанавливать значения транзакции вручную, также устанавливать режим рандомизации, включать или выключать ограничения.

`uvm_do        

`uvm_do(SEQ_OR_ITEM)

Макрос получает на вход аргумент являющийся  uvm_sequence_item null-указателем или указателем, который хранит ссылку на уже созданный объект. Макрос пересоздает объект с указанным в аргументе именем с помощью `uvm_create. Если переданный указатель/объект представляет собой тип транзакции (uvm_sequence_item), то она будет запущена на сиквенсере с помощью конструкции start_item()/randomize()/finish_item, если в макрос передается сиквенс (класс производный от uvm_sequence_base), то будет вызван метод uvm_sequence_base::start(), при этом методы pre и post в сиквенсах будут отключены

Для транзакции будут производиться следующие действия

  `uvm_create(item)
 sequencer.wait_for_grant(prior) (Задача)
 this.pre_do(1)                  (Функция)
 item.randomize()
 this.mid_do(item)               (Функция)
 sequencer.send_request(item)    (Функция)
 sequencer.wait_for_item_done()  (Задача)
 this.post_do(item)              (Функция)

Для сиквенсы следующие действия

  `uvm_create(sub_seq)
 sub_seq.randomize()
 sub_seq.pre_start()         (Задача)
 this.pre_do(0)              (Задача)
 this.mid_do(sub_seq)        (Функция)
 sub_seq.body()              (Задача)
 this.post_do(sub_seq)       (Функция)
 sub_seq.post_start()        (Задача)

`uvm_do_pri

`uvm_do_pri(SEQ_OR_ITEM, PRIORITY)

Макрос работает аналогично макросу `uvm_do за исключением, что транзакция или сиквенс исполняются с заданным приоритетом. Величина приоритета находится в диапазоне значений от 0 до 100, это значение определяет, какая из запущенных одновременно в параллель сиквенсов получает управление при запуске на сиквенсере.

`uvm_do_with        

`uvm_do_with(SEQ_OR_ITEM, CONSTRAINTS)

Макрос работает аналогично макросу `uvm_do только позволяет во втором аргументе добавить ограничения на генерацию переменных внутри сиквенсы. Ограничения задаются в фигурных скобках “{}” и будут добавлен при вызове функции randomize() with CONSTRAINTS;

`uvm_do_pri_with

`uvm_do_pri_with(SEQ_OR_ITEM, PRIORITY, CONSTRAINTS)

Макрос работает аналогично макросу `uvm_do_pri только позволяет добавить в третьем аргументе ограничения на генерацию переменных перед выполнением.

Также в UVM реализованы макросы, которые используются для запуска сиквенсов и позволяют указать сиквенсер, на котором они будут запущены. Эти макросы также создадут и рандомизируют объект сиквенса.

`uvm_create_on

`uvm_create_on(SEQ_OR_ITEM, SEQR)

`uvm_do_on(SEQ_OR_ITEM, SEQR)

`uvm_do_on_pri(SEQ_OR_ITEM, SEQR, PRIORITY)

`uvm_do_on_with(SEQ_OR_ITEM, SEQR, CONSTRAINTS)

`uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS)

Эти макросы работает также как и аналогичные макросы из предыдущей  таблицы, только позволяет вдобавок к существующему функционалу указать вторым параметром сиквенсер, на котором нужно запустить выполнение транзакции или сиквенса.

`uvm_do_on        

`uvm_do_on_pri

`uvm_do_on_with        

`uvm_do_on_pri_with

В UVM также реализованы макросы, которые используются для запуска сиквенсов, которые уже созданы, т.е. сиквенc не будет создаваться перед запуском.

`uvm_send

`uvm_send(SEQ_OR_ITEM)

Макрос аналогичен макросу `uvm_do только без создания и рандомизации объекта.

`uvm_send_pri

`uvm_send_pri(SEQ_OR_ITEM, PRIORITY)

Макрос аналогичен макросу `uvm_send, вдобавок позволяет задать приоритет запускаемой транзакции или сиквенса.

`uvm_rand_send

`uvm_rand_send(SEQ_OR_ITEM)

Макрос аналогичен макросу `uvm_do только без создания объекта

`uvm_rand_send_pri

`uvm_rand_send_pri(SEQ_OR_ITEM, PRIORITY)

Макрос аналогичен макросу `uvm_rand_send, вдобавок позволяет задать приоритет запускаемой транзакции или сиквенса.

`uvm_rand_send_with

`uvm_rand_send_with(SEQ_OR_ITEM, CONSTRAINTS)

макрос аналогичен макросу `uvm_rand_send с той лишь разницей, что этот макрос позволяет добавить дополнительные ограничения, которые будут добавлены при вызове функции randomize () with CONSTRAiNT.

`uvm_rand_send_pri_with

`uvm_rand_send_pri_with(SEQ_OR_ITEM, PRIORITY, CONSTRAINTS)

Макрос выполняет аналогичные `uvm_rand_send_pri макросу действия  с той лишь разницей что этот макрос позволяет добавить дополнительные ограничения, которые будут добавлены при вызове функции randomize () with CONSTRAINT.

        

Также в UVM при разработке сиквенсов которые возможно запускать только на определённых сиквенсерах, реализован макрос, который позволяет установить сиквенсер по умолчанию. Это в свою очередь позволит получить доступ к переменным сиквенсера.

`uvm_declare_p_sequencer

`uvm_declare_p_sequencer(SEQUENCER)

Этот макрос используется для задания переменной p_sequencer внутри сиквенсы и переопределения метода m_set_p_sequencer. Что в свою очередь при попытке запустить сиквенс на отличном от указанного сиквенсере (не являющимся расширением указанного) приведет к ошибке и сообщению о том что сиквенс не предназначена для этого сиквенсера. Такой макрос удобно использовать, если в сиквенсе используются поля сиквенсера (порты, переменные, указатель на компонент, и т.д.) Пример использования этого макроса приведен ниже.

class mysequence extends uvm_sequence#(mydata);
 `uvm_object_utils(mysequence)
 `uvm_declare_p_sequencer(some_seqr_type) //после макроса регистрации задаем сиквенсер
 task body;
   //Далее можем использовать переменные заданного сиквенсера
   if(p_sequencer.some_variable) begin
     ...
   end
 endtask
endclass

Запуск сиквенсы по умолчанию

В UVM реализован механизм запуска сиквенс на сиквенсере, для этого достаточно после создания сиквенсы передать указатель на нее через UVM базу конфигурации uvm_config_db. Если это выполнить, то сиквенсер в заданной фазе автоматически проверит назначена ли сиквенс и выполнит все сопутствующие действия - запустит ее на выполнение. Сделать это можно двумя способами.

  myseq_t myseq = new("myseq");

  myseq.randomize() with { ... };

  uvm_config_db #(uvm_sequence_base)::set(null, "top.agent.myseqr.main_phase",

                                          "default_sequence",

                                          myseq);

Конфигурация с использованием типа транзакции короче, но есть еще один способ с использованием механизма фабрики.

  uvm_config_db #(uvm_object_wrapper)::set(null, "top.agent.myseqr.main_phase",

                                           "default_sequence",

                                           myseq_type::type_id::get());

Также можно использовать uvm_resource_db для настройки сиквенсы выполняемой по умолчанию.

  myseq_t myseq = new("myseq");

  myseq.randomize() with { ... };

  uvm_resource_db #(uvm_sequence_base)::set({get_full_name(), ".myseqr.main_phase",

                                            "default_sequence",

                                            myseq, this);

Или еще один способ.

  uvm_resource_db #(uvm_object_wrapper)::set({get_full_name(), ".myseqr.main_phase",

                                             "default_sequence",

                                             myseq_t::type_id::get(),

                                             this );

        Назначение сиквенсы по умолчанию удобно использовать для запуска различных сиквенсов из одного теста (см. Переопределение в тесте запускаемых сиквенсов).

Генерация заданных последовательностей транзакций.

Допустим интерфейс содержит сигналы, задающие стробы записи и требуется их проверить. В таком случае нужно генерировать последовательность транзакций содержащих все возможные состояния. Можно создавать множество случайных транзакций общим количеством примерно 1/6 от всего числа возможных состояний.

Если требуется перебрать случайным образом все значения без повторений, то нужно объявить проверяемые поля как тип randc и выполнить рандомизацию поля n-е число раз, где n - число возможных состояний это поля.

Класс сиквенса параметризуется типом используемой транзакции. Внутри сиквенсы объявляется объект транзакции. Регистрируется класс с использованием `uvm_object_util макроса, а также объекты транзакции, это позволит использовать механизм фабрики для подмены сиквенсы, а также позволит реализовать методы копирования, сравнения и печати для сиквенсы.

В методе body() создается транзакция с помощью механизма фабрики (используется метод create), далее вызывается встроенный метод генерации случайных значений для всех переменных объявленных как rand, randc, без дополнительных ограничений. Далее выполняется метод start_item(item), передающий транзакцию драйверу. Драйвер же ожидает вызова этого метода, и после начинает обработку транзакции. Далее в сиквенсе вызывается метод finish_item(), который блокирует выполнение сиквенса и ждет выполнения функции item_done() внутри драйвера. После того как драйвер вызывает item_done(), сиквенс продолжает выполнение. Диаграмма общения драйвер-сиквенсер-сиквенс в данном случае выглядит следующим образом, как показано на рисунке.

500px-Get_next_item_item_done_uml.gif

http://uvm-stage.mentor.com//w/images/thumb/9/9d/Get_next_item_item_done_uml.gif/500px-Get_next_item_item_done_uml.gif

В следующем примере создается 20 случайных транзакций, для генерации значений полей транзакции можно использовать “лямбда” выражения, которые позволяют добавить ограничения или выбрать требуемые значения.

class apb_base_seq extends uvm_sequence#(apb_item);

  apb_item item;

 `uvm_object_utils_begin(apb_base_seq)

    `uvm_object_field (item, UVM_ALL_ON | UVM_NOPACK);

 `uvm_object_utils_end

  function new(string name="apb_base_seq");

     super.new(name);

  endfunction

endclass : apb_base_seq

class apb_rand_seq extends apb_base_seq;

  `uvm_object_utils(apb_rand_seq)

   

  function new(string name="apb_rand_seq");

    super.new(name);

  endfunction

 

  virtual task body();

    repeat (20) begin

            item = apb_item::type_id::create("item");

            item.randomize();

            start_item(item);

            finish_item(item);

    //`uvm_do(req)

    end

  endtask

 

endclass : apb_rand_seq

Если требуется проверить несколько различных последовательностей транзакций, то удобно описать несколько отдельных методов внутри класса сиквесы и далее в методе body() вызвать их в нужном порядке. Данный подход демонстрируется в следующем примере.

class apb_incr_seq extends apb_base_seq;

  `uvm_object_utils(apb_incr_seq)

   

  int addr_i;

  int strb_i;

  int data_i;

  function new(string name="apb_incr_seq");

    super.new(name);

  endfunction

  // генерация данных в порядке уменьшения

  virtual task decr_data();

    for(int i=0; i < (1<<4); i++) begin

            item = apb_item::type_id::create("item");

            item.randomize() with {

          data == data_i;

      };

            start_item(item);

      finish_item(item);

      data -= 1000;

    end

  endtask  

  // генерация поля strb в порядке возрастания

  virtual task incr_strb();

    for(int i=0; i < (1<<4); i++) begin

            item = apb_item::type_id::create("item");

            item.randomize() with {

           strb == strb_i;

      };

            start_item(item);

      finish_item(item);

      strb_i++;

    end

  endtask

  //генерация поля  addr в порядке возрастания

  virtual task incr_addr();

    for(int i=0; i < (1<<32); i++) begin

            item = apb_item::type_id::create("item");

            item.randomize() with {

                addr == addr_i;

      };

            start_item(item);

      finish_item(item);

      addr_i++;

    end

  endtask

  // генерация поля strb и addr

  virtual task strb_addr();

    for(int i=0; i < (1<<32); i++) begin

            item = apb_item::type_id::create("item");

            item.randomize() with {

                strb inside {4’b1111, 4’b1110,4’b1100,4’b1000 };

                addr == addr_i;

                data[31] == 1;

                data[7:0] <20;

                data[7:0] >2;

      };

            start_item(item);

      finish_item(item);

      addr_i++;

    end

  endtask

  //Главное тело сиквенса, к котором вызываются таски

  virtual task body();

      strb_addr ();

      incr_addr ();

      incr_strb ();

      decr_data ();

  endtask

endclass : apb_incr_seq

Для примера выше тело метода run_phase драйвера будет следующим.

task run_phase( uvm_phase phase );
 bus_seq_item req_item;
 
 forever begin
   
seq_item_port.get_next_item(req_item); // Блокирующий запрос на получение следущей транзакции из сиквенса
   @(posedge vif.clk);
   vif.addr = req_item.address; // Устанавливаются значения на шину
   // Конец выполнения цикла конвертации транзакции в активность на шине
   if(req_item.read_or_write == READ) begin //Заполняется в этой же транзакции поле ответа (если есть в этом необходимость и позволяет  протокол)
     req_item.rdata = vif.rdata;
   end
  req_item.resp = vif.error;
   
seq_item_port.item_done(); // Метод, который сигнализирует сиквенсу о том, что драйвер закончил обработку транзакции
 end
endtask: run

Сиквенсы с обратной связью

Сиквенсы с обратной связью используются в агентах, реализующих функционал пассивных блоков или блоков, работающих в режиме ведомого (slave). В этом случае драйвер получает по интерфейсу запрос с какими-то параметрами, затем этот  запрос попадает в сиквенс, где транзакция запроса должна быть заполнена и отправлена на выполнение на драйвер, как ответ на транзакцию запроса. Для получения транзакции ответа от драйвера к сиквенсу реализован метод get_responce(rsp), который позволяет забрать транзакцию ответа. К примеру мы хотим вычитать по интерфейсу значение, инвертировать его и записать в следующую транзакцию, и так для всех ячеек памяти. В этом случае получим следующий код.

class apb_rand_seq extends apb_base_seq;

  apb_item resp_item;

  bit [31:0] rdata_i;

  `uvm_object_utils(apb_rand_seq)

   

  function new(string name="apb_rand_seq");

    super.new(name);

  endfunction

 

  virtual task body();

    // запускаем пустую транзакцию для получения первого ответа с данными

    for(int i=0; i < (1<<32); i++) begin

        item = apb_item::type_id::create("item");

            item.randomize() with {

                kind == READ;

                addr == addr_i;

      };

        start_item(item);

      finish_item(item);

      get_responce(resp_item);   // получаем ответ от драйвера содержащий данные

      rdata_i = resp_item.read;    // сохраняет значение

      rdata_i = ~ rdata_i;              // инвертируем

                                        // создаем и отправляем транзакцию на запись

            item = apb_item::type_id::create("item");

            item.randomize() with {

                kind == WRITE;

                strb inside {4’b1111, 4’b1110,4’b1100,4’b1000 };

                addr == addr_i;

                read == read_i;  

            };

            start_item(item);

      finish_item(item);

      addr_i++;

    end

  endtask

endclass : apb_rand_seq

В случае ожидания транзакции ответа, важно не забывать что драйверу следует выслать ответ используя в данном случае метод put(), иначе выполнение сиквенсы будет заблокировано.

//
// Метод run внутри драйвера
//
task run_phase( uvm_phase phase );
 REQ req_item; //REQ is parameterized type for requests
 RSP rsp_item; //RSP is parameterized type for responses
 
 forever begin
 
 seq_item_port.get(req_item); // finish_item in sequence is unblocked
   @(posedge vif.clk);
   vif.addr = req_item.addr;
   //
   // etc
   //
   // End of bus transfer
  $cast(rsp_item, req_item.clone()); // Клониурем запрос на чтение
  rsp_item.set_id_info(req_item);//Устанавливаем ID ответа, равный ID  запроса
  if(req_item.read_or_write == READ) begin //Заполянем поле с данными прочитанными по запрошенному адресу
    rsp_item.read_data = vif.rdata;
  end
  rsp_item.resp = vif.error;
 
seq_item_port.put(rsp_item); // Отправляем обратно ответ который ожидается в сиквенсе
 end
endtask

В этом случае диаграмма общения сиквенсер-драйвер выглядит следующим образом.

Get_put_uml.gif

http://uvm-stage.mentor.com//w/images/1/16/Get_put_uml.gif

Создание и запуск случайных сиквенсов

Встречаются задачи, в которых необходимо запускать однообразные сиквенсы, отличающиеся одним или несколькими параметрами. В этом случае удобно сделать сиквенс, которую можно параметризовать.  Также удобно часть параметров определить в сиквенсе как rand, и перед запуском рандомизировать сиквенс. В этом случае можно проверить сценарии, в которых сиквеннсы запускаются последовательно со случайными параметрами. При этом существует возможность задать ограничения, как в сиквенсе при ее определили, так и при ее рандомизации.

Далее приведен пример сиквенс со случайными параметрами.

class apb_rand_seq extends apb_base_seq;

  apb_item resp_item;

  rand rw_type rw_l;

  rand length_l;

 

  `uvm_object_utils(apb_rand_seq)

   

  function new(string name="apb_rand_seq");

    super.new(name);

  endfunction

 

  virtual task body();

    for(int i=0; i < length_l; i++) begin

        item = apb_item::type_id::create("item");

            item.randomize() with {

                kind == rw_l;

      };

        start_item(item);

      finish_item(item);

    end

  endtask

endclass : apb_rand_seq

 

Далее показан тест для запуска сиквенса без дополнительных ограничений на параметры.

class run_item_test extends apb_base_test;

   typedef apb_rand_seq RUN_SEQ;

   RUN_SEQ seq0;

  `uvm_component_utils(run_item_test)

   function new(string name, uvm_component parent = null);

      super.new(name, parent);

   endfunction

   task main_phase(uvm_phase phase);

        phase.raise_objection(this);

     `uvm_info ("TB/TRACE/RUN_ITEM","start sequence", UVM_NONE)

        repeat (100) begin

          seq0 = RUN_SEQ::type_id::create("seq0");

          seq0.randomize();

          seq0.start(env.apb.sqr);

        end

     `uvm_info ("TB/TRACE/RUN_ITEM","finish sequence", UVM_NONE)

             #1000;

        phase.drop_objection(this);

   endtask

endclass

 

Далее показан пример теста для запуска сиквенса с дополнительными ограничениями для случайных параметров.

class run_item_rand_test extends apb_base_test;

   typedef apb_rand_seq RUN_SEQ;

   RUN_SEQ seq0;

  `uvm_component_utils(run_item_rand_test)

   function new(string name, uvm_component parent = null);

      super.new(name, parent);

   endfunction

   task main_phase(uvm_phase phase);

        phase.raise_objection(this);

     `uvm_info ("TB/TRACE/RUN_ITEM","start sequence", UVM_NONE)

        repeat (100) begin

          seq0 = RUN_SEQ::type_id::create("seq0");

          seq0.randomize() with {

          length_l > 10;

          length_l < 100;

          rw_l dist { READ := 10 , WRITE := 90 };

          };

          seq0.start(env.apb.sqr);

        end

     `uvm_info ("TB/TRACE/RUN_ITEM","finish sequence", UVM_NONE)

             #1000;

        phase.drop_objection(this);

   endtask

endclass

Одновременный запуск в тесте нескольких сиквенсов

В UVM библиотеке для компонента uvm_sequencer реализована возможность запуска нескольких сиквенсов одновременно в параллель. В этом случае сиквенсер анализирует очередь сиквенсов и запускает их в зависимости от приоритета, заданного для каждой сиквенсы. По умолчанию приоритет сиквенсов будет одинаковый и можно наблюдать равномерный запуск каждой из запущенных сиквенсов.

class run_item_prior_test extends apb_base_test;

   typedef apb_rand_seq RUN_SEQ;

   RUN_SEQ seq0;

   RUN_SEQ seq_er_inj;

  `uvm_component_utils(run_item_prior_test)

   function new(string name, uvm_component parent = null);

      super.new(name, parent);

   endfunction

   task main_phase(uvm_phase phase);

       phase.raise_objection(this);

       `uvm_info ("TB/TRACE/RUN_ITEM","start sequence", UVM_NONE)

          seq0 = RUN_SEQ::type_id::create("seq0");

          seq0.randomize() with {

          length_l > 10;

          length_l < 100;

          rw_l dist { READ := 10 , WRITE := 90 };

          };

          seq_er_inj = RUN_SEQ::type_id::create("seq_er_inj");

          seq_er_inj.randomize();

          fork

              seq0.start(.sequencer(env.apb.sqr), .this_priority(90));

              seq_er_inj.start(.sequencer(env.apb.sqr), .this_priority(10));

          join

       `uvm_info ("TB/TRACE/RUN_ITEM","finish sequence", UVM_NONE)

               #1000;

        phase.drop_objection(this);

    endtask

endclass

Управление процессом выполнения сиквенсов

Во время выполнения одной сиквенсы на сиквенсере, существует возможность принудительно запустить другую сиквен. К примеру во время выполнения транзакции проверяющей штатный режим работы устройства,  требуется запустить сиквенс генерирующую ошибочные воздействия, для проверки устройства на предмет зависании или обработки исключительных ситуаций. Для этого в сиквенсе и в сиквенсере реализованы методы lock\unlcok, grab\ungrab.

class apb_error_inj_seq extends apb_base_seq;

  apb_item resp_item;

  rand rw_type rw_l;

  rand length_l;

 

  `uvm_object_utils(apb_error_inj_seq )

   

  function new(string name="apb_rand_seq");

    super.new(name);

  endfunction

 

  virtual task body();

      grab(); // перехватываем поток управления у всех остальных сиквенсов запущенных на m_sequenser

        item = apb_item::type_id::create("item");

            item.randomize() with {

                error_inj == 1;

      };

        start_item(item);

      finish_item(item);

   ungrab();// возвращаем поток управления у всех остальных сиквенсов запущенных на m_sequenser

endclass : apb_error_inj_seq  

Также существует возможность изменить алгоритм управления приоритетами для сиквенсера. Для этого сиквенсер имеет интерфейс set_arbitration() и user_priority_arbitration(). Метод  set_arbitration() позволяет выбрать возможный алгоритм из уже реализованных, по умолчанию используется SEQ_ARB_FIFO алгоритм.

SEQ_ARB_FIFO

Запросы получают приоритет в порядке FIFO, т.е. ID следующей сиквенсы на выполнение будет находится в avail_sequences[0].

SEQ_ARB_WEIGHTED

Запросы получают приоритет случайным образом согласно весовым значениям приоритета, заданного при старте.

SEQ_ARB_RANDOM

Запросы получают приоритет случайным образом.

SEQ_ARB_STRICT_FIFO

Запросы с самым большим приоритетов имеют приоритет выполнения и выполняются в порядке FIFO

SEQ_ARB_STRICT_RANDOM

Запросы с самым высоким приоритетом получают приоритет выполнения в строгом случайном порядке

SEQ_ARB_USER

Алгоритм приоритета передаётся пользовательской функции,

user_priority_arbitration.

В этой функции определяет очередность выполнения сиквенсов.

Функция user_priority_arbitration() позволяет пользователю определить собственный алгоритм выбора сиквенсов. На вход функции подается очередь содержащая идентификаторы сиквенсов, и она возвращает идентификатор сиквенсы, которая должна выполняться следующей.

Виртуальные сиквенсы

В тестовом окружении, которое проверяет цифровой блок, содержащий более чем один  физический интерфейс (например, APB, AXI, PCIe и пр.), встает необходимость синхронизации запусков несколько сиквенсов на разных агентах в определенной последовательности.

Например, изначально нужно запустить сиквенс, которая генерирует сигналы сброса по всем интерфейсам. Затем запустить сиквенс, которая записывает конфигурацию регистров по APB шине, затем запустить сиквенс, выполняющую проверку подключенного устройства по третьему интерфейсу. И только после этих сиквенсов можно проверять обмен данными этого устройства. Получается, каждый раз нужно запускать три различных типа сиквенсов на разных сиквенсерах, в этом случае придется весь код дублировать в большинстве тестах.

Поэтому в UVM методологии верификации предлагается использовать сиквенс, которая будет представлять собой  объект обвертку для других сиквенсов. Она будет содержать указатели на все сиквенсеры, на которых нужно запускать сиквенсы, а также в ее методе body() будет реализован алгоритм запуска и синхронизации работы всех сиквенсов.

Запускать эту сиквенс можно из теста без сиквенсера, в этом случае она будет запускаться на сиквенсере null.

// Для удобства можно объявить тип виртуальной сиквенсы
typedef uvm_sequence #(uvm_sequence_item) uvm_virtual_sequence;

class init_vseq extends uvm_virtual_sequence;
 apb_base_seq    cfg_seq;

  reset_base_seq  reset_seq;

  axi_base_seq    read_seq;

  axi_base_seq    write_seq;
 // Указатели на сиквенсы используемые для запуска
 axi_sequencer_t   axi_sqr;
 apb_sequencer_t   apb_sqr;
 reset_sequencer_t reset_sqr;

  `uvm_object_utils(init_vseq)

   

  function new(string name="init_vseq");

    super.new(name);

  endfunction


 virtual task body();

        cfg_seq   = new();

      reset_seq = new();

      read_seq  = new();

      write_seq = new();

      cfg_seq.randomize();

      reset_seq.randomize();

      read_seq.randomize() with { rw == READ; };

      cfg_seq.randomize() with { rw == WRITE; };

     

      if (axi_sqr == null) `uvm_fatal(“BASE_VSEQ”,”AXI_SQR is not set”)

      //проверяем перед запуском что указатели на сиквенсеры реальные
     //Запуск сиквенсов в заданном порядке  
     reset_seq.start(reset_sqr,this);// второй параметр задает родительскую сиквенс
     apb_seq.start(apb_sqr,this);

      fork

          read_seq.start(axi_sqr, this);

          write_seq.start(axi_sqr, this);

      join
 endtask
endclass

Далее показан пример теста, в котором происходит инициализация и запуск виртуальной сиквенсы vseq без сиквенсера.

class run_vseq_test extends apb_base_test;

   typedef init_vseq RUN_SEQ;

   RUN_SEQ vseq;

 

  `uvm_component_utils(run_vseq_test)

   function new(string name, uvm_component parent = null);

      super.new(name, parent);

   endfunction

   task main_phase(uvm_phase phase);

       phase.raise_objection(this);

       `uvm_info ("TB/TRACE/RUN_ITEM","start sequence", UVM_NONE)

          vseq = RUN_SEQ::type_id::create("vseq");

          //задаем сиквенсеры

          vseq.axi_sqr   = env.subenv1.axi_agent.sequencer;
         vseq.apb_sqr   = env.subenv2.subsubenv1.apb_agent.sqr;
         vseq.reset_sqr = env.reset_agent.sqr;

          vseq.start(null);

       `uvm_info ("TB/TRACE/RUN_ITEM","finish sequence", UVM_NONE)

               #1000;

        phase.drop_objection(this);

    endtask

endclass

Запуск и управление выполнением сиквенсов на виртуальном сиквенсере

Также виртуальную сиквенс можно запускать на виртуальном сиквенсере. Виртуальный сиквенср это компонент, который также наследуется от uvm_sequencer и содержит в своем теле указатели на сиквенсеры, на которых будет запускаться сиквенсы.

Необходимость в виртуальном сиквенсере возникает в том случае, если для генерации новых воздействий или для реализации алгоритма запуска транзакций необходимо анализировать проходящие в тестовом окружении транзакции от мониторов различных интерфейсов. В этом случае придется подключить порты компонентов, к портам мониторов и наблюдать с их помощью за транзакциями. А для корректной работы с портами (объявление, создание, подключение) они должны создаваться с использованием стандартных фаз, реализованных в uvm_component. Поэтому использование виртуальной сиквенсы без сиквенсера не приемлемо.  

Для назначения передачи сиквенсеров для внутренних сиквенсов, удобно использовать переопределённый метод start для виртуального сиквенса. В этом случае нет необходимости в тесте передавать каждый раз сиквенсеры, достаточно это сделать один раз при создании виртуального сиквенсера.

Далее показан пример виртуальной сиквенсы, предназначенной для запуска на виртуальном сиквенсере.

// Для удобства можно объявить тип виртуальной сиквенсы
typedef uvm_sequence #(uvm_sequence_item) uvm_virtual_sequence;

class init_vseq extends uvm_virtual_sequence;
 apb_base_seq    cfg_seq;

  reset_base_seq  reset_seq;

  axi_base_seq    read_seq;

  axi_base_seq    write_seq;
 // Указатели на сиквенсы используемые для запуска
 axi_sequencer_t   axi_sqr;
 apb_sequencer_t   apb_sqr;
 reset_sequencer_t reset_sqr;

  `uvm_object_utils(init_vseq)

   

  function new(string name="init_vseq");

    super.new(name);

  endfunction

  virtual task start (uvm_sequencer_base         sequencer, uvm_sequence_base parent_sequence = null, int this_priority = -1, bit         call_pre_post = 1)

      axi_sqr   = sequencer.axi_sqr;
     apb_sqr   = sequencer.apb_sqr;
     reset_sqr = sequencer.reset_sqr;

      super.start(sequencer,parent_sequence,this_priority, call_pre_post);

  endtask : start

 
 virtual task body();

        cfg_seq   = new();

      reset_seq = new();

      read_seq  = new();

      write_seq = new();

      cfg_seq.randomize();

      reset_seq.randomize();

      read_seq.randomize() with { rw == READ; };

      cfg_seq.randomize() with { rw == WRITE; };

     

      if (axi_sqr == null) `uvm_fatal(“BASE_VSEQ”,”AXI_SQR is not set”)

  ....//проверяем перед запуском что указатели на сиквенсеры реальные
     //Запуск сиквенсов в заданном порядке  
     reset_seq.start(reset_sqr,this);// второй параметр задает родительскую сиквенс
     apb_seq.start(apb_sqr,this);

      fork

          read_seq.start(axi_sqr, this);

          write_seq.start(axi_sqr, this);

      join
 endtask : body
endclass : init_vseq

Пример запуска виртуальной сиквенсы на виртуальном сиквенсере из теста

class run_vseq_test extends apb_base_test;

   typedef init_vseq RUN_SEQ;

   typedef init_vsqr VSQR;

   RUN_SEQ vseq;

   VSQR vsqr;

  `uvm_component_utils(run_vseq_test)

   function new(string name, uvm_component parent = null);

      super.new(name, parent);

   endfunction : new

   function void build_phase(uvm_phase phase);
      vsqr
= VSQR::type_id::create("vsqr", this);
 
endfunction: build_phase

   function void connect_phase( uvm_phase phase );
 
    vsqr.axi_sqr   = env.subenv1.axi_agent.sequencer;
      vsqr.apb_sqr   = env.subenv2.subsubenv1.apb_agent.sqr;
      vsqr.reset_sqr = env.reset_agent.sqr;

  endfunction: connect_phase

   task main_phase(uvm_phase phase);

       phase.raise_objection(this);

       `uvm_info ("TB/TRACE/RUN_ITEM","start sequence", UVM_NONE)

        vseq = RUN_SEQ::type_id::create("vseq");

        vseq.start(vsqr);

       `uvm_info ("TB/TRACE/RUN_ITEM","finish sequence", UVM_NONE)

               #1000;

        phase.drop_objection(this);

    endtask : main_phase

endclass : run_vseq_test

Тесты

        Выше уже были рассмотрены примеры тестов, в которых запускаются сиквенсы. Для запуска теста в среде верификации используется задача run_test(). Вызов этой задачи осуществляется из модуля в блоке initial. Так как при создании компонентов теста используется механизм фабрики, то это позволяет симулятору при запуске анализировать параметр командной строки +UVM_TESTNAME и запускать класс с указанным в этой строке именем. Если задать такое имя теста, которого не было зарегистрировано с помощью фабрики, то при запуске будет выведено сообщение об ошибке.

Переопределение в тесте запускаемых сиквенсов.

Можно реализовать один базовый тест, который в зависимости от имени будет подменять сиквенс по умолчанию на соответствующую имени сиквенс. Для этого используется механизм подмены типов с помощью фабрики. (см. запуск сиквенсы по умолчанию). Аналогичный механизм уже реализован для самого класса uvm_test, поэтому используемый подход выбирается в зависимости от требуемой задачи. Все тесты будут представлять собой однообразный запуск сиквенсов, а изменяться будет лишь их имя, то данный метод позволит уменьшить число файлов входящих в тестовое окружение.

`ifndef TX_BE_LENGTH2_TEST_SV

`define TX_BE_LENGTH2_TEST_SV

class tx_be_length2_test extends tx_test_base;

    `uvm_component_utils(tx_be_length2_test)

    string test_name;

    myseq_t myseq;

    function new(string name="tx_be_length2_test", uvm_component parent = null);

        super.new(name,parent);

        $value$plusargs("UVM_TESTNAME=%s" , test_name);

    endfunction : new

 

    virtual function void build_phase(uvm_phase phase);

        `uvm_info("build_phase", "Entry", UVM_HIGH)

        super.build_phase(phase);

         

        set_inst_override_by_name("myseq",

                              myseq_t::get_type_name(),

                              test_name_to_vseq_name(test_name));

        myseq = myseq_t::type_id::create("myseq");

         

        uvm_config_db #(uvm_sequence_base)::set(

                                          null,

                                          "top.agent.myseqr.main_phase",

                                          "default_sequence",

                                          myseq);

        `uvm_info("build_phase","Exit",UVM_HIGH)

    endfunction

    virtual function string test_name_to_vseq_name(string inline);

      ...

    endfunction

endclass: tx_be_length2_test

`endif // TX_BE_LENGTH2_TEST_SV

Параметры командной строки

Кроме имени теста удобно использовать другие параметры командной строки для параметризации тестов, сиквенсов или режимов работы окружения. Этот подход позволяет изменять конфигурации без пере-компиляции тестового окружения. Параметры могут быть различных типов: строковый (%s), битовый (%b), численные (%d, %h). Для проверки значения параметра, заданного в командной строке при запуске моделирования, используется функция $value$plusargs.

$value$plusargs("ИМЯ_ПАРАМЕТРА=%b", целевая_переменная);

 

Далее показан пример базового теста, в котором используются значения различных параметров, передаваемых из командной строки.

class base_test extends uvm_test;

 

    int               s0_tr_num  = 10;

    bit               xor_enable = 0;

    bit               data_type  = 1;

    bit               delay_en   = 1;

    bit               perf_mode  = 0;

    int               error_num  = 1;

    int               aport_num  = 0;

    bit [2:0]         cbm_rw_cfg = 0;

    bit               pl_mode    = 1;

    bit [1:0]         rw_mode    = 3;

    `uvm_component_utils_begin(base_test)

    `uvm_component_utils_end

     extern function void get_plusarg();

endclass : base_test

function void base_test::get_plusarg();

  $value$plusargs("CBM_XOR_EN=%b"        , xor_enable);

  $value$plusargs("CBM_S0_NBURST=%d"     , s0_tr_num);

  $value$plusargs("CBM_DTYPE=%b"         , data_type);

  $value$plusargs("CBM_DELAY=%b"         , delay_en);

  $value$plusargs("CBM_PERF_MODE=%b"     , perf_mode);

  $value$plusargs("CBM_ERROR_INJ=%d"     , error_num);

  $value$plusargs("CBM_APORT_NUM=%d"     , aport_num);

  $value$plusargs("CBM_RW_CFG=%b"        , cbm_rw_cfg);

  $value$plusargs("CBM_PL_MODE=%b"       , pl_mode);

  $value$plusargs("CBM_RW_MODE=%b"       , rw_mode);

endfunction : get_plusarg

function base_test::new(string name, uvm_component parent);

    super.new(name, parent);

    get_plusarg();

endfunction : new

Класс UVM_CMDLINE_PROC

Класс предоставляет интерфейсы для работы с аргументами командной строки, которые были переданы симулятору. Класс предназначен для использования как статический, но это не обязательное требование. Генерация структуры данных, которые заполняются аргументами командной строки, происходит в момент создания объекта класса. Глобальная переменная uvm_cmdline_proc создается в нулевой момент времени и может использоваться для доступа к информации переданной в командной строке.

Класс uvm_cmdline_processor также предоставляет поддержку установок различных UVM переменных из командной строки, например, таких как задание чувствительности вывода сообщений для компонентов и различные строковые и численные параметры. Далее в таблице описаны реализованные параметры и интерфейсы для работы с ними.

get_inst

Возвращает указатель на статический объект класса обработки параметров командной строки

get_args

Функция возвращает очередь со всеми аргументами командной строки, которые были заданы во время запуска моделирования.

Стоит обратить внимание на то, что элемент очереди с номером 0 будет всегда хранить имя исполняемого файла, который начал симуляцию.

get_plusargs

Эта функция возвращает очередь, содержащую все аргументы со знаком “+”, плюс-аргументы, которые были переданы во время запуска моделирования.

Плюс-аргументы могут использоваться разработчиками симулятора или пользователем по их усмотрению.

Плюс-аргументы никогда не имеют дополнительных аргументов, т.е. если в командной строке есть один плюс-аргумент и для него задано значение, то следующий за ним аргумент будет новым независимым аргументом, а никак не расширением предыдущего.  

get_uvmargs

Эта функция возвращает очередь со всеми uvm аргументами которые были переданы в командной строке при запуске моделирования. Все uvm аргументы передаются с помощью ключа +\- и все они имеют в названии в начале три буквы “UVM”.

get_arg_matches

Эта функция заполняет очередь аргументами, которые соответствуют заданному выражению и возвращает количество таких аргументов. Если входное выражение задано между символами “//”, это выражение рассматривается как встроенное регулярное выражение для поиска имени аргумента, которое содержит заданную строку.

string myargs[$]
initial begin

void'(uvm_cmdline_proc.get_arg_matches("+foo",myargs)); //совпадает с условием +foo, +foobar                                                           //не совпадает         +barfoo

void'(uvm_cmdline_proc.get_arg_matches("/foo/",myargs));

//подходит +foo, +foobar, foo.sv, barfoo, etc.


void'(uvm_cmdline_proc.get_arg_matches("/^foo.*\.sv",myargs));

//Подходит matches foo.sv и foo123.sv,

//Не подходит barfoo.sv.

end

get_arg_value

Эта функция находит первый аргумент, который совпадает с заданным именем. Эта функция аналогична системной функции $value$plusargs, но не поддерживает строку формата ввода данных. Возвращает количество найденных аргументов с заданным именем и возвращает значение первого найденного подходящего аргументы.

get_arg_values

Эта функция находит все аргументы, которые совпали с заданным именем и возвращает значения найденных аргументов в виде списка. Число найденных значений можно получить вызвав функцию получения размера списка найденных аргументов size()

Например:

Строка запуска:

  ‘+foo=1,yes,on +foo=5,no,off’

а в окружении исполняется следующий код:

  string foo_values[$]
   initial begin

       void'(uvm_cmdline_proc.get_arg_values
("+foo=",foo_values));

    end

То очередь значений будет содержать два найденных параметра:

”1,yes,on”

”5,no,off”

При обработке переданных значений лучше всего пользователю разделить полученные строки используя uvm_split_string() функцию

get_tool_name

Функция возвращает название симулятора. Разработчик среды моделирования реализует эту функцию.

get_tool_version

Функция возвращает информацию о симуляторе. Разработчик среды моделирования реализует эту функцию.

В следущей таблице перечислены основные плюс-параметры, используемые в пакете UVM.

+UVM_DUMP_CMDLINE_ARGS        

Параметр позволяет пользователю сохранять все аргументы командной строки используя механизм вывода сообщений. Для вывода параметров будет использоваться формат tree.

+UVM_TESTNAME

Параметр позволяет задать пользователю какой тест или какой компонент верхнего уровня должен быть создан и запущен в UVM фазах. Если задано несколько аналогичных  параметров,  то будет использоваться только первый, и будет выдано предупреждение.

<sim_command> +UVM_TESTNAME=read_modify_write_test

+UVM_VERBOSITY

Параметр позволяет задать порог чувствительности для всех компонентов при выводе сообщений с помощью макроса `uvm_info. Если задано несколько аналогичных  параметров,  то будет использоваться только первый, и будет выдано предупреждение.

<sim_command> +UVM_VERBOSITY=UVM_HIGH

Управление временем выполнения теста.

Для управления временем выполнения теста в UVM реализован класс uvm_objection. Этот класс содержит методы, которые указывают симулятору, что тестовое окружение еще работает или оно уже закончило свое выполнение. В этом классе реализован метод set_drain_time, который позволяет задать время, которое симулятор будет продолжать работать после того как подаст сигнал о завершении последний объект класса uvm_objection.

Можно использовать функцию set_timeout класса uvm_root, которая позволяет задать время, после превышения которого тест принудительно завершиться.


Литература

  1. https://verificationacademy.com/cookbook/sequences/sequencelibrary
  2. https://verificationacademy.com/verification-methodology-reference/uvm/docs_1.2/html/
  3. https://verificationacademy.com/cookbook/driver/sequence_api


Глоссарий

Термин

Значение

Транзакция (transaction)

Класс, содержащий набор различных данных, с помощью последовательности которых можно описать модель поведения устройства в среде верификации

Сиквенс (sequence)

Класс, генерирующий последовательность (набор) транзакций

Сиквенсер (sequencer)

UVM компонент выполняющий арбитраж запущенных сиквенсов.