OVM/OVM методология/Механика OVM — различия между версиями
Строка 311: | Строка 311: | ||
[[Файл:73.png]] | [[Файл:73.png]] | ||
+ | |||
+ | |||
+ | === Примечание для пользователей AVM === | ||
+ | |||
+ | В AVM-3.0 соединения были сделаны аналогично: вызовом функции connect портов и экспортов. Кроме того, export-to-export, port-to-export, и port-to-port вызовы были сделаны на разных фазах, export_connections (), Connect (), и import_connections (), соответственно. В OVM, порядок, в котором вызывается connect, не важен, он может быть вызван в любом порядке. Мы рекомендуем вам вызывать их в фазе connect()(?????????) (не следует путать с методом connect() на портов и экспортов). | ||
+ | OVM поддерживает позднее связывание, a feature where calls to connect() only | ||
+ | make a note that a connection is to be made.. Позже, перед end_of_elaboration, notes согласованы и выполнены соединения. Это позволяет более чистое использование модели much more forgiving of understandable errors where connect() calls were made in the wrong order. | ||
+ | |||
+ | |||
+ | == Фазы == | ||
+ | |||
+ | |||
+ | Для традиционных модулей Verilog полная разработка проекта и его выполнение осуществялется в симуляторе. Так как OVM компоненты - это классы, они создаются и подключаются, и их выполнение начинается за пределами Verilog разработчика. Компоненты создаются вызовом конструктора класса операцией new(), которая выделяет память и выполняет инициализацию. В то время как Verilog время выполнения run-time engine managing instantiation, разработка, и выполнение компонент на основе классов, функциональность компонент разделена на фазы, и контроллер фазы OVM управляет их исполнением. | ||
+ | |||
+ | |||
+ | Каждая фаза представлена в компоненте как виртуального метода (задачи или функция) с тривиальной реализацией по умолчанию. Эти phase callbacks реализуются разработчиком компоненты, который обеспечивает соответствующую функциональность. Фаза контроллера гарантирует, что фазы выполняются в надлежащем порядке. Набор стандартных фаз показано в следующей таблице: | ||
+ | |||
+ | [[Файл:74.png]] | ||
+ | |||
+ | [[Файл:75.png]] | ||
+ | |||
+ | |||
+ | Каждая фаза имеет определенную цель. разработчики Компонент должны заботиться о том, чтобы функциональность реализованная в каждой фазе callback соответствовала фазе определения. | ||
+ | |||
+ | • '''New''' технически не является фазой, в том, что она не управляется фазой контроллера. Тем не менее, для каждого компонента, конструктор должен быть выполнен и завершен в целях создания компоненты. Таким образом, new должен выполниться до build() или любого другого последующего этапах выполнения. | ||
+ | |||
+ | • '''build''' это место, где новые компоненты, порты и экспорт создаются и настраиваются. Это также рекомендуемое место для вызова set_config_ * и get_config_ * (см. Section4.4). | ||
+ | |||
+ | • '''connect''' – место, где компоненты, порты и экспорт созданные в build() связываются. | ||
+ | |||
+ | • '''end_of_elaboration''' здесь вы можете выполнить изменение конфигураций, зная, что разработка уже завершена. То есть, вы можете предположить, что все компоненты созданы и соединены. | ||
+ | |||
+ | • '''start_of_simulation''' выполняется только до времени 0. | ||
+ | |||
+ | • '''run''' pre-defined task фазой. Все выполняемые задачи работают параллельно. Каждая выполняемая задача продолжается, пока ее управление не достигнет оператора EndTask или она явно будет прекращена. Позже в этой главе мы обсудим, как завершать testbenches. | ||
+ | |||
+ | • '''extract''' предназначен для сбора информации, касающейся покрытия или другой информации о том, как ответить на вопросы testbench. | ||
+ | |||
+ | • '''проверка''' - место, где выполняется любая проверка правильности или проверка валидации извлеченных данных. | ||
+ | |||
+ | • '''отчет''' -место , где делаются окончательные отчеты. | ||
+ | |||
+ | |||
+ | Ниже простой пример, использующий ovm_report_info () для иллюстрации порядка выполнения фаз. | ||
+ | |||
+ | <big><source lang="cpp">38 class sub_component extends ovm_component; | ||
+ | 39 | ||
+ | 40 function new(string name, ovm_component parent); | ||
+ | 41 super.new(name, parent); | ||
+ | 42 endfunction | ||
+ | 43 | ||
+ | 44 function void build(); | ||
+ | 45 ovm_report_info(“build”, ““); | ||
+ | 46 endfunction | ||
+ | 47 | ||
+ | 48 function void connect(); | ||
+ | 49 ovm_report_info(“connect”, ““); | ||
+ | 50 endfunction | ||
+ | 51 | ||
+ | 52 function void end_of_elaboration(); | ||
+ | 53 ovm_report_info(“end_of_elaboration”, ““); | ||
+ | 54 endfunction | ||
+ | 55 | ||
+ | 56 function void start_of_simulation(); | ||
+ | 57 ovm_report_info(“start_of_simulation”, ““); | ||
+ | 58 endfunction | ||
+ | 59 | ||
+ | 60 task run(); | ||
+ | 61 ovm_report_info(“run”, ““); | ||
+ | 62 endtask | ||
+ | 63 | ||
+ | 64 function void extract(); | ||
+ | 65 ovm_report_info(“extract”, ““); | ||
+ | 66 endfunction | ||
+ | 67 | ||
+ | 68 function void check(); | ||
+ | 69 ovm_report_info(“check”, ““); | ||
+ | 70 endfunction | ||
+ | 71 | ||
+ | 72 function void report(); | ||
+ | 73 ovm_report_info(“report”, ““); | ||
+ | 74 endfunction | ||
+ | 75 | ||
+ | 76 endclass | ||
+ | file: 04_OVM_mechanics/03_phases/top.sv | ||
+ | |||
+ | </source></big> | ||
+ | |||
+ | В компоненте верхнего уровня, мы создаем две компоненты, каждая из которых в свою очередь создает две sub_component. sub_components являющимися такими же, как и сами компоненты; каждая фаза обратного callback просто печатает строку, определяющую фазу. При выполнении, вы получите следующий результат: | ||
+ | |||
+ | <pre>OVM_INFO @ 0 [RNTST] Running test ... | ||
+ | OVM_INFO @ 0: env.c1 [build] | ||
+ | OVM_INFO @ 0: env.c1.s1 [build] | ||
+ | OVM_INFO @ 0: env.c1.s2 [build] | ||
+ | OVM_INFO @ 0: env.c2 [build] | ||
+ | OVM_INFO @ 0: env.c2.s1 [build] | ||
+ | OVM_INFO @ 0: env.c2.s2 [build] | ||
+ | OVM_INFO @ 0: env.c1.s1 [connect] | ||
+ | OVM_INFO @ 0: env.c1.s2 [connect] | ||
+ | OVM_INFO @ 0: env.c1 [connect] | ||
+ | OVM_INFO @ 0: env.c2.s1 [connect] | ||
+ | OVM_INFO @ 0: env.c2.s2 [connect] | ||
+ | OVM_INFO @ 0: env.c2 [connect] | ||
+ | OVM_INFO @ 0: env.c1.s1 [end_of_elaboration] | ||
+ | OVM_INFO @ 0: env.c1.s2 [end_of_elaboration] | ||
+ | OVM_INFO @ 0: env.c1 [end_of_elaboration] | ||
+ | OVM_INFO @ 0: env.c2.s1 [end_of_elaboration] | ||
+ | OVM_INFO @ 0: env.c2.s2 [end_of_elaboration] | ||
+ | OVM_INFO @ 0: env.c2 [end_of_elaboration] | ||
+ | OVM_INFO @ 0: env.c1.s1 [start_of_simulation] | ||
+ | OVM_INFO @ 0: env.c1.s2 [start_of_simulation] | ||
+ | OVM_INFO @ 0: env.c1 [start_of_simulation] | ||
+ | OVM_INFO @ 0: env.c2.s1 [start_of_simulation] | ||
+ | OVM_INFO @ 0: env.c2.s2 [start_of_simulation] | ||
+ | OVM_INFO @ 0: env.c2 [start_of_simulation] | ||
+ | OVM_INFO @ 0: env.c2 [run] | ||
+ | OVM_INFO @ 0: env.c2.s2 [run] | ||
+ | OVM_INFO @ 0: env.c2.s1 [run] | ||
+ | OVM_INFO @ 0: env.c1 [run] | ||
+ | OVM_INFO @ 0: env.c1.s2 [run] | ||
+ | OVM_INFO @ 0: env.c1.s1 [run] | ||
+ | OVM_INFO @ 1: env.c1.s1 [extract] | ||
+ | OVM_INFO @ 1: env.c1.s2 [extract] | ||
+ | OVM_INFO @ 1: env.c1 [extract] | ||
+ | OVM_INFO @ 1: env.c2.s1 [extract] | ||
+ | OVM_INFO @ 1: env.c2.s2 [extract] | ||
+ | OVM_INFO @ 1: env.c2 [extract] | ||
+ | OVM_INFO @ 1: env.c1.s1 [check] | ||
+ | OVM_INFO @ 1: env.c1.s2 [check] | ||
+ | OVM_INFO @ 1: env.c1 [check] | ||
+ | OVM_INFO @ 1: env.c2.s1 [check] | ||
+ | OVM_INFO @ 1: env.c2.s2 [check] | ||
+ | OVM_INFO @ 1: env.c2 [check] | ||
+ | OVM_INFO @ 1: env.c1.s1 [report] | ||
+ | OVM_INFO @ 1: env.c1.s2 [report] | ||
+ | OVM_INFO @ 1: env.c1 [report] | ||
+ | OVM_INFO @ 1: env.c2.s1 [report] | ||
+ | OVM_INFO @ 1: env.c2.s2 [report] | ||
+ | OVM_INFO @ 1: env.c2 [report] | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | Вы видите, что build()выполняется сверху вниз и остальные фазы запуска снизу вверх. Вы также можете видеть, что каждый этап завершается во всех компонентах до начала следующего этапа. Таким образом, в connect(), например, вы можете расчитывать, что build() завершена во всех компонентах. Вы также заметите, что время запускается после фазы run. В нашем примере задача run - тривиальна, она просто выполняет задержку времени на единицу (# 1). | ||
+ | |||
+ | un_test (), упоминалось ранее в разделе 4.1, инициирует фазу выполнение. Он начинает выполнение фаз по порядку и контролирует, что каждый этап завершен до начала следующего. | ||
+ | |||
+ | |||
+ | == Config == | ||
+ | |||
+ | Для увеличения повторного использования компонент, желательно объявит их с параметрами, которые могут быть внешне настроены. Конфигурационные средства обеспечивают возможности для этого. Они основаны на базе пар имя-значение, и называются configuration items, которые организованы иерархически. Каждая компонента содержит таблицу конфигураций элементов конфигурации, так как компоненты организованы в качестве дерева, каждый элемент в базе данных может быть уникально размещен по расположению компоненты и имени элемента конфигурации | ||
+ | |||
+ | |||
+ | [[Файл:76.png]] | ||
+ | |||
+ | |||
+ | Класс ovm_component содержит два набора методов для конфигурации размещения элементов в базу данных и потом для их получения. Это set_config_ * и * get_config_. В таблице ниже приведены оба набора. | ||
+ | |||
+ | [[Файл:77.png]] | ||
+ | |||
+ | Set_config_ * функция помещает элемент в конфигурационную базу данных текущего компонента, то есть в экземпляре компонента, в котором функция вызывается. Каждая из этих функций принимают три аргумента, имя, field_name и значение. Аргумент name - имя пути, который представляет набор компонент, которые должны соответсвовать кофигурациям этого элемента. name используется в * get_config_, чтобы найти элементы в базе данных конфигурации. field_name - это имя поля и должно быть уникальным в пределах текущей базы конфигураций. value является частью пары имя-значение и его тип может быть строка, INT, или ovm_object, в зависимости от вызываемой функции. Кроме того, set_config_object принимает копию аргумента для указания, был ли объект передан в качестве значения так как значение должно быть клонировано прежде чем оно будет помещено в базу конфигурации. | ||
+ | Функции get_config_ * извлекает элементов из базы данных конфигураций. Эти функции принимают только два аргумента, имя поля и INOUT переменную, который содержит значение расположения элемента. Они также возвращают бит, чтобы указать был ли удачно расположен запрашиваемый объект. * Get_config_ функции не принимают аргументы имени пути, как их set_config_ * коллеги, поскольку они используют путь к текущему компоненту, как ссылку, чтобы найти элементы конфигурации. Они разработаны, чтобы узнать, есть значение элемента конфигурации для текущего контекста, то есть, компонент, в котором функция get_config_ * вызывается. | ||
+ | |||
+ | Алгоритм поиска для извлечения элементов конфигурации использует имя пути компонента запроса и путь к файлу вставляемый в каждый item. Он начинается с поиска конфигурационных элементов в базе данных в верхней компоненте по field_name. Если такой элемент существует, то он затем спрашивает, соответствует ли указанный путь к файлу пути имени компоненты. Если элемента с указанным field_name нет или имена путей не совпадают, то поиск идет с дочерней компоненты. Этот процесс продолжается до соответствия или до достижения компоненты начала поиска. | ||
+ | |||
+ | Путь каждого элемент конфигурации может быть регулярным выражением. Таким образом, мы используем алгоритм соответствия регулярных выражений для нахождения совпадения с путем запрошенной компоненты и именем пути элемента конфигурации. Эффект заключается в соответствии иерархической области. | ||
+ | В качестве примера, рассмотрим простую иерархию на рисунке 4-5. Давайте предположим, что в env::build()мы запускаем два вызова set_config_ *: | ||
+ | |||
+ | <big> | ||
+ | <source lang="cpp">112 function void build(); | ||
+ | 113 c1 = new(“c1”, this); | ||
+ | 114 c2 = new(“c2”, this); | ||
+ | 115 | ||
+ | 116 set_config_int(“c2.*”, “i”, 42); | ||
+ | 117 set_config_int(“*”, “t”, 19); | ||
+ | 118 endfunction | ||
+ | file: 04_OVM_mechanics/04_config/top.sv | ||
+ | |||
+ | </source></big> | ||
+ | |||
+ | |||
+ | Это вызовет запись двух конфигурационных элементов в базу данных в env. Обратите внимание на звездочку (*) в имени пути. Имя пути в вызове set_config_ * - регулярное выражение, и дикие символы используются для указать несколько областей, к которыми элемент применяется. По элементу i, c2. * показывает, что в любой области ниже c2 в иерархии, i будет определять указанное значение. В этом случае указанное значение - 42. Если вы опустите звездочку, то элемент конфигурации применяется только к c2, а не к любому из своих потомков. | ||
+ | Состояние конфигурации базы данных для каждого компонента в иерархии после вызова set_config_ * показано на рисунке 4-6, | ||
+ | |||
+ | [[Файл:78.png]] | ||
+ | |||
+ | |||
+ | пусть в элементе top.c1.child1 мы осуществим вызов: | ||
+ | |||
+ | |||
+ | <big><source lang="cpp">int i; | ||
+ | ... | ||
+ | get_config_int(“i”, i) | ||
+ | </source></big> | ||
+ | |||
+ | |||
+ | Поиск задает вопрос: Каково значение конфигурации i в иерархических рамках top.c1.child1? Чтобы ответить на этот вопрос, конфигурации базы данных в env ищется в первую очередь. Запись i там говорит, что значение i совпадающее с env.c2. * - 42. Тем не менее, компоненты, из которых был сделан запрос, находятся в c1 sub-hierarchy. Таким образом, не существует соответствия, а get_config_int () возвращает статус неудачи. Запрос в любую компоненту, которая является дочерней c2 будет успешно завершен и вернется значение 42. | ||
+ | |||
+ | Ниже приведен код для функцию build() дочерней компоненты. Это место где эти компоненты ищут конфигурационные значения i и t. | ||
+ | |||
+ | <big><source lang="cpp">60 function void build(); | ||
+ | 61 | ||
+ | 62 string msg; | ||
+ | 63 | ||
+ | 64 if(!get_config_int(“t”, t)) begin | ||
+ | 65 $sformat(msg, “no value for t found in config | ||
+ | database, using default value of %0d”, t); | ||
+ | 66 ovm_report_warning(“build”, msg); | ||
+ | 67 end | ||
+ | 68 | ||
+ | 69 if(!get_config_int(“i”, i)) begin | ||
+ | 70 $sformat(msg, “no value for i found in config | ||
+ | database, using default value of %0d”, i); | ||
+ | 71 ovm_report_warning(“build”, msg); | ||
+ | 72 end | ||
+ | 73 | ||
+ | 74 endfunction | ||
+ | file: 04_OVM_mechanics/04_config/top.sv | ||
+ | </source></big> | ||
+ | |||
+ | Следующий пример показывает вывода при выполнении этого проекта: | ||
+ | |||
+ | |||
+ | <pre># OVM_INFO @ 0 [RNTST] Running test ... | ||
+ | # OVM_WARNING @ 0: env.c1.child1 [build] no value for i found in | ||
+ | config database, using default value of 91 | ||
+ | # OVM_WARNING @ 0: env.c1.child2 [build] no value for i found in | ||
+ | config database, using default value of 91 | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | Запрос на конфигурационный элемент t был успешным во всех контекстах с того момента как вызов set_config_int установил, что tдоступно во всех контекстах. Два запроса на элемент конфигурации i были успешными, и два нет. Результат получился такой потому что мы ограничены наличием i только компонентах на уровне или ниже env.c2. Компоненты на уровне или ниже c1 не могут видеть элемент конфигурации i из-за того, как мы создали базу данных конфигурации. |
Версия 15:30, 20 марта 2013
Содержание |
Механика OVM
Библиотека OVM предоставляет много возможностей для построения testbenches. В этой главе мы ознакомимся с наиболее важными возможностями, которые вы будете использовать почти во всех ваши testbenches.
Компоненты и иерархия
Первичная структура для создания элементов testbench является компонента. Компонента в OVM – это аналогия модулю в Verilog. Компонента OVM сконструирована на основе класса, который обеспечивает ему различные характеристики, чем модуль Verilog и имеет различные использования импликаций. Среди таких характеристик является то, что классы создаются во время выполнения, а не во время разработки, как модули. Таким образом, OVM отвечает за создание компонента и сборки их в иерархии.
![]() |
Рисунок 4-1 иллюстрирует простую иерархию компонент. После мы покажем, как построить эту иерархию с помощью средств OVM для создания компонент и объединения их в иерархии:
Самый верхний узел, ENV, является корнем. Корень отличается тем, что он не имеет предка. Все остальные узлы имеют только одного предка. Каждый узел имеет имя. Расположение каждого узла в иерархии может быть идентифицировано уникальным full_name (путем), который строится на нанизывании имен всех узлов между корнем и узлов в запросе, разделяя их сепаратором иерархии, точкой (.). Например, путь к компоненту, второго потомка с2 - top.c2.child2.
Компонент в ОВМ является классом, производным от ovm_component. Простейшая компоненты - листья, те, которые не имеют потомков.
57 class child extends ovm_component; 58 59 function new(string name, ovm_component parent); 60 super.new(name, parent); 61 endfunction 62 63 endclass file: 04_OVM_mechanics/01_hierarchy/top.sv
Конструктор имеет два параметра, имя компоненты и указатель на его предка. Название - простое имя, а не иерархический путь. Предок предоставляет место для подключения новой компоненты в иерархии. Полный путь потомка создается путем объединения имени потомка с полным именем предка, разделенный точкой (.).OVM предоставляет методы для получения и имени и полного пути компоненты:
string get_name(); string get_full_name();
Подчиненные компоненты создаются в функции build(), которая вызывается на этапе сборки (фазы объясняется далее в этой главе). Создание экземпляра компонента включает вызов new() для выделения памяти для нее и передачи соответствующих параметров в конструктор. В компоненте, показанной ниже, мы создаем две подчиненные компоненты, и child1 child2.
71 class component extends ovm_component; 72 73 child child1; 74 child child2; 75 76 function new(string name, ovm_component parent); 77 super.new(name, parent); 78 endfunction 79 80 function void build(); 81 child1 = new(“child1”, this); 82 child2 = new(“child2”, this); 83 endfunction 84 85 endclass file: 04_OVM_mechanics/01_hierarchy/top.sv
Как component, ENV также создает экземпляры двух подчиненных компонент, c1 и c2. Вся иерархия с корнем в модуле, называемым top в нашем проекте.(??)
131 module top; 132 133 env e; 134 135 initial begin 136 e = new(“env”); 137 run_test(); 138 end 139 140 endmodule file: 04_OVM_mechanics/01_hierarchy/top.sv
Вызов new() создает верхний уровень окружающей среды. run_test () начинает выполнение testbench.
В SystemVerilog, модули, интерфейсы и программные блоки создаются во время разработки, в то время как классы создаются после разработки, во время выполнения. Таким образом, чтобы создать иерархию классов, мы должны иметь интерфейс, модуль или программу, который содержит начальный блок, который начинает процесс создание компонент иерархии, основанных на классах. Интерфейсы предназначены для использования в качестве средства связи между двумя модулями и не очень хорошо подходят для использования в качестве корня иерархии классов. Программные блоки или модули могут быть использованы как корень (??). Для нашей простой иерархии, это не имеет значения. Позже, когда мы подключаем класс-компонент для модуля на основе аппаратных средств, мы увидим, что использование модуля предпочтительнее программным блокам.
Перемещение по иерархии
Мы можем исследовать структуры данных, используемые для реализации компонента иерархии с некоторыми методами, предусмотренными в ovm_component. Потомки компоненты хранятся в ассоциативном массиве. Этот массив напрямую не доступен, но он может быть доступен через иерархию API. Этот API-интерфейс похож на встроенные методы SystemVerilog, предназначенные для ассоциативных массивов.
int get_first_child(ref string name); int get_next_child(ref string name); ovm_component get_child(string name); int get_num_children();
get_first_child () и get_next_child () работают вместе для перебора множества потомков, содержащихся в компоненте. get_first_child () извлекает имя первого ребенка в списке. Он возвращает имя в качестве ссылки. get_next_child () возвращает имя следующего ребенка в списке. Он возвращает 1, если имеется следующая имя потомка и 0, если был достигнут конец списка. get_child () преобразует имя в ссылку компоненты.
Используя эти функции, мы можем пройти по компонентам иерархии.
73 function void depth_first(ovm_component node, 74 int unsigned level = 0); 75 76 string name; 77 78 if(node == null) 79 return; 80 81 visit(node, level); 82 83 if(node.get_first_child(name)) 84 do begin 85 depth_first(node.get_child(name), level+1); 86 end while(node.get_next_child(name)); 87 88 endfunction file: 04_OVM_mechanics/utils/traverse.svh
Эта функция будет осуществляющая обход в глубину иерархии, называя visit(). Мы используем get_first_child () и get_next_child () для перебора списка каждого потомка в каждом узле. Для каждого итерации мы вызываем depth_first () рекурсивно. Для нашего маленького проекта, результат будет следующий:
+ env | + env.c1 | | env.c1.child1 | | env.c1.child2 | + env.c2 | | env.c2.child1 | | env.c2.child2
Функция visit() использует глубину узла и определяет является ли он конечным для печати строки каждого узла.
Singleton Top
Компоненты, которые не имеют предков (то есть, параметр предок в конструкторе является нулевым) называют orphans.(??) В OVM вы можете создать столько компоненты без предков, сколько вам необходимо. Тем не менее, не существует такого понятия, как истинные orphans. Любой компонент, чей предок является null, относится к встроенному предку, называемому ovm_top. ovm_top состоит из одного экземпляра ovm_root. Это предок всех компонент, которые имеют предков. В самом деле, env в нашем предыдущем примере является потомком ovm_top. Так как он не имеет предка, то автоматически получают ovm_top в качестве родителя.
singleton - широко известный объектно-ориентированных шаблон проектирования, который имеет private (local) или protected конструкторы и статическую функцию get, которая возвращает тот же самый указатель не смотря на то, сколько раз она вызывается. Это означает, что возможно существование одного экземпляра, и что экземпляр создается автоматически при вызове функции get. ovm_top содержит дескриптор экземпляра singleton ovm_root(???). Он статически инициализируется при вызове ovm_root :: Get (). Вы можете вызвать ovm_root :: Get () в любое время, но в этом нет необходимости, так как ovm_top предусматривает это.
Есть целый ряд полезных следствий существования singleton top-level компоненты. Одним из них является, что вы можете достичь любую компоненту из ovm_top. Если вы запустили алгоритм обхода иерархии, начиная с ovm_top, вы доберетесь до каждой компоненты в системе. Другим следствием является то, что любая компонента, включая порты, экспорт и каналы, которые создаются внутри модуля, достижима из ovm_top. Если вы хотите изменить отчет во всех обработчиках компонент, вы можете сделать это, вызвав одну из иерархических функции отчетности в ovm_top. ovm_top содержит все механизмы синхронизации, которые объясняются далее в этой главе.
Connectivity
Компоненты соединены друг с другом через TLM порта и exports. Порты и exports обеспечивают средства для компонент, или, точнее, процессы в компонентах для синхронизации и связи друг с другом. Порты и exports - объекты, которые образуют пункт связывания для обеспечения межкомпонентной связи. Как уже говорилось в предыдущей главе, exports обеспечивают функции и задачи, которые могут быть вызваны портами.
Метод connect портов и exports используется для связывания их вместе.
initiator_port.connect(target.export)
Этот метод создает ассоциацию, или связывание, между портом и export так что порт может теперь называем задач и функций export. Для успешного соединения, типы портов и export должны совпадать. То есть, типы интерфейсов должны быть одинаковыми, и тип объекта, передающегося в интерфейс должны быть одинаковыми.
Подключение через Иерархию
Как и контактами в RTL дизайне, нам нужно подключиться к портам TLM через иерархические границы. Рисунок 4-3 использует простой дизайн, чтобы показать, как сделать эти соединения. Этот проект содержит исходную компоненту с двумя портами, которые в конечном итоге подключаются к двум экспортам. Для осуществления подключения между этими компонентами, мы должны расширить порты и экспорта на уровень выше в иерархии.
Компонент source содержит два порта, и first_put_port, second_put_port.
65 class source extends ovm_component; 66 67 ovm_put_port #(trans_t) first_put_port; 68 ovm_put_port #(trans_t) second_put_port; 69 70 function new(string name, ovm_component parent); 71 super.new(name, parent); 72 endfunction 73 74 function void build(); 75 first_put_port = new(“first_put_port”, this); 76 second_put_port = new(“second_put_port”, this); 77 endfunction file: 04_OVM_mechanics/02_connectivity/top.sv
Кроме того, компонента sink создает экспорт и создает его в функции build. Экспорт связан с внутренним каналом, FIFO, от которого компонента может получать объекты во время выполнения.
126 class sink extends ovm_component; 127 128 ovm_put_export #(trans_t) put_export; 129 local tlm_fifo #(trans_t) fifo; 130 131 function new(string name, ovm_component parent); 132 super.new(name, parent); 133 endfunction 134 135 function void build(); 136 put_export = new(“put_export”, this); 137 fifo = new(“fifo”, this); 138 endfunction 139 140 function void connect(); 141 put_export.connect(fifo.put_export); 142 endfunction file: 04_OVM_mechanics/02_connectivity/top.sv
source_wrapper должен создать соединение между внутренним источником компоненты и внешней ее границей. Он делает это соединение, создавая свои порты, которые имеют тот же тип, что и тип портов lower-level, в нашем случае, тех, которые принадлежат к source.
98 class source_wrapper extends ovm_component; 99 100 source s; 101 ovm_put_port #(trans_t) put_port1; 102 ovm_put_port #(trans_t) put_port2; 103 104 function new(string name, ovm_component parent); 105 super.new(name, parent); 106 endfunction 107 108 function void build(); 109 s = new(“source”, this); 110 put_port1 = new(“put_port1”, this); 111 put_port2 = new(“put_port2”, this); 112 endfunction 113 114 function void connect(); 115 s.first_put_port.connect(put_port1); 116 s.second_put_port.connect(put_port2); 117 endfunction 118 119 endclass file: 04_OVM_mechanics/02_connectivity/top.sv
160 class sinker extends ovm_component; 161 162 ovm_put_export #(trans_t) first_put_export; 163 ovm_put_export #(trans_t) second_put_export; 164 165 sink sink1; 166 sink sink2; 167 168 function new(string name, ovm_component parent); 169 super.new(name, parent); 170 endfunction 171 172 function void build(); 173 sink1 = new(“sink1”, this); 174 sink2 = new(“sink2”, this); 175 first_put_export = new(“first_put_export”, this); 176 second_put_export = new(“second_put_export”, this); 177 endfunction 178 179 function void connect(); 180 first_put_export.connect(sink1.put_export); 181 second_put_export.connect(sink2.put_export); 182 endfunction 183 184 endclass file: 04_OVM_mechanics/02_connectivity/top.sv
Два нижних уровня компоненты sink и exports создаются обычным способом. Затем они соединяются с помощью метода подключения export. Сейчас мы создаем port-export соединение между source_wrapper и sinker, также с помощью функции Connect.
192 class env extends ovm_component; 193 194 sinker s; 195 source_wrapper sw; 196 197 function new(string name, ovm_component parent = null); 198 super.new(name, parent); 199 endfunction 200 201 function void build(); 202 s = new(“sinker”, this); 203 sw = new(“source_wrapper”, this); 204 endfunction 205 206 function void connect(); 207 sw.put_port1.connect(s.first_put_export); 208 sw.put_port2.connect(s.second_put_export); 209 endfunction 210 211 task run; 212 global_stop_request(); 213 endtask 214 215 endclass file: 04_OVM_mechanics/02_connectivity/top.sv
Для новых пользователей часто может вызвать трудности определить, порт или экспорт необходимо подключить и какой объект является аргументом. Вы можете легко понять это, следуя потоку управления через систему. Общее правило заключается в том, что запрашиваемый порт вызывает connect() с помощью запрашиваемого порта или экспорта в качестве аргумента. Рисунок 4-4 показывает поток управления через иерархическую систему.
Порт – объект вызова и экспорт - объект вызванной функции или задачи. Вы можете воспринимать порты, как вызов экспорта. Так, в env, мы вызываем connect на put_ports с put_exports в качестве аргументов. Для port-to- port и export-to-export иерархии соединений, вызывающий будет чуть менее очевиден. Если вызов осуществляется со стороны порта, вы можете считать, что вызов lowest-level порта в иерархии является вызовом методов интерфейса upper-level порта. Аналогично, если экспорт объект вызова, вы можете считать upper-level экспорт вызовом lower-level export. В таблице ниже приведены возможные типы подключения:
Примечание для пользователей AVM
В AVM-3.0 соединения были сделаны аналогично: вызовом функции connect портов и экспортов. Кроме того, export-to-export, port-to-export, и port-to-port вызовы были сделаны на разных фазах, export_connections (), Connect (), и import_connections (), соответственно. В OVM, порядок, в котором вызывается connect, не важен, он может быть вызван в любом порядке. Мы рекомендуем вам вызывать их в фазе connect()(?????????) (не следует путать с методом connect() на портов и экспортов). OVM поддерживает позднее связывание, a feature where calls to connect() only make a note that a connection is to be made.. Позже, перед end_of_elaboration, notes согласованы и выполнены соединения. Это позволяет более чистое использование модели much more forgiving of understandable errors where connect() calls were made in the wrong order.
Фазы
Для традиционных модулей Verilog полная разработка проекта и его выполнение осуществялется в симуляторе. Так как OVM компоненты - это классы, они создаются и подключаются, и их выполнение начинается за пределами Verilog разработчика. Компоненты создаются вызовом конструктора класса операцией new(), которая выделяет память и выполняет инициализацию. В то время как Verilog время выполнения run-time engine managing instantiation, разработка, и выполнение компонент на основе классов, функциональность компонент разделена на фазы, и контроллер фазы OVM управляет их исполнением.
Каждая фаза представлена в компоненте как виртуального метода (задачи или функция) с тривиальной реализацией по умолчанию. Эти phase callbacks реализуются разработчиком компоненты, который обеспечивает соответствующую функциональность. Фаза контроллера гарантирует, что фазы выполняются в надлежащем порядке. Набор стандартных фаз показано в следующей таблице:
Каждая фаза имеет определенную цель. разработчики Компонент должны заботиться о том, чтобы функциональность реализованная в каждой фазе callback соответствовала фазе определения.
• New технически не является фазой, в том, что она не управляется фазой контроллера. Тем не менее, для каждого компонента, конструктор должен быть выполнен и завершен в целях создания компоненты. Таким образом, new должен выполниться до build() или любого другого последующего этапах выполнения.
• build это место, где новые компоненты, порты и экспорт создаются и настраиваются. Это также рекомендуемое место для вызова set_config_ * и get_config_ * (см. Section4.4).
• connect – место, где компоненты, порты и экспорт созданные в build() связываются.
• end_of_elaboration здесь вы можете выполнить изменение конфигураций, зная, что разработка уже завершена. То есть, вы можете предположить, что все компоненты созданы и соединены.
• start_of_simulation выполняется только до времени 0.
• run pre-defined task фазой. Все выполняемые задачи работают параллельно. Каждая выполняемая задача продолжается, пока ее управление не достигнет оператора EndTask или она явно будет прекращена. Позже в этой главе мы обсудим, как завершать testbenches.
• extract предназначен для сбора информации, касающейся покрытия или другой информации о том, как ответить на вопросы testbench.
• проверка - место, где выполняется любая проверка правильности или проверка валидации извлеченных данных.
• отчет -место , где делаются окончательные отчеты.
Ниже простой пример, использующий ovm_report_info () для иллюстрации порядка выполнения фаз.
38 class sub_component extends ovm_component; 39 40 function new(string name, ovm_component parent); 41 super.new(name, parent); 42 endfunction 43 44 function void build(); 45 ovm_report_info(“build”, ““); 46 endfunction 47 48 function void connect(); 49 ovm_report_info(“connect”, ““); 50 endfunction 51 52 function void end_of_elaboration(); 53 ovm_report_info(“end_of_elaboration”, ““); 54 endfunction 55 56 function void start_of_simulation(); 57 ovm_report_info(“start_of_simulation”, ““); 58 endfunction 59 60 task run(); 61 ovm_report_info(“run”, ““); 62 endtask 63 64 function void extract(); 65 ovm_report_info(“extract”, ““); 66 endfunction 67 68 function void check(); 69 ovm_report_info(“check”, ““); 70 endfunction 71 72 function void report(); 73 ovm_report_info(“report”, ““); 74 endfunction 75 76 endclass file: 04_OVM_mechanics/03_phases/top.sv
В компоненте верхнего уровня, мы создаем две компоненты, каждая из которых в свою очередь создает две sub_component. sub_components являющимися такими же, как и сами компоненты; каждая фаза обратного callback просто печатает строку, определяющую фазу. При выполнении, вы получите следующий результат:
OVM_INFO @ 0 [RNTST] Running test ... OVM_INFO @ 0: env.c1 [build] OVM_INFO @ 0: env.c1.s1 [build] OVM_INFO @ 0: env.c1.s2 [build] OVM_INFO @ 0: env.c2 [build] OVM_INFO @ 0: env.c2.s1 [build] OVM_INFO @ 0: env.c2.s2 [build] OVM_INFO @ 0: env.c1.s1 [connect] OVM_INFO @ 0: env.c1.s2 [connect] OVM_INFO @ 0: env.c1 [connect] OVM_INFO @ 0: env.c2.s1 [connect] OVM_INFO @ 0: env.c2.s2 [connect] OVM_INFO @ 0: env.c2 [connect] OVM_INFO @ 0: env.c1.s1 [end_of_elaboration] OVM_INFO @ 0: env.c1.s2 [end_of_elaboration] OVM_INFO @ 0: env.c1 [end_of_elaboration] OVM_INFO @ 0: env.c2.s1 [end_of_elaboration] OVM_INFO @ 0: env.c2.s2 [end_of_elaboration] OVM_INFO @ 0: env.c2 [end_of_elaboration] OVM_INFO @ 0: env.c1.s1 [start_of_simulation] OVM_INFO @ 0: env.c1.s2 [start_of_simulation] OVM_INFO @ 0: env.c1 [start_of_simulation] OVM_INFO @ 0: env.c2.s1 [start_of_simulation] OVM_INFO @ 0: env.c2.s2 [start_of_simulation] OVM_INFO @ 0: env.c2 [start_of_simulation] OVM_INFO @ 0: env.c2 [run] OVM_INFO @ 0: env.c2.s2 [run] OVM_INFO @ 0: env.c2.s1 [run] OVM_INFO @ 0: env.c1 [run] OVM_INFO @ 0: env.c1.s2 [run] OVM_INFO @ 0: env.c1.s1 [run] OVM_INFO @ 1: env.c1.s1 [extract] OVM_INFO @ 1: env.c1.s2 [extract] OVM_INFO @ 1: env.c1 [extract] OVM_INFO @ 1: env.c2.s1 [extract] OVM_INFO @ 1: env.c2.s2 [extract] OVM_INFO @ 1: env.c2 [extract] OVM_INFO @ 1: env.c1.s1 [check] OVM_INFO @ 1: env.c1.s2 [check] OVM_INFO @ 1: env.c1 [check] OVM_INFO @ 1: env.c2.s1 [check] OVM_INFO @ 1: env.c2.s2 [check] OVM_INFO @ 1: env.c2 [check] OVM_INFO @ 1: env.c1.s1 [report] OVM_INFO @ 1: env.c1.s2 [report] OVM_INFO @ 1: env.c1 [report] OVM_INFO @ 1: env.c2.s1 [report] OVM_INFO @ 1: env.c2.s2 [report] OVM_INFO @ 1: env.c2 [report]
Вы видите, что build()выполняется сверху вниз и остальные фазы запуска снизу вверх. Вы также можете видеть, что каждый этап завершается во всех компонентах до начала следующего этапа. Таким образом, в connect(), например, вы можете расчитывать, что build() завершена во всех компонентах. Вы также заметите, что время запускается после фазы run. В нашем примере задача run - тривиальна, она просто выполняет задержку времени на единицу (# 1).
un_test (), упоминалось ранее в разделе 4.1, инициирует фазу выполнение. Он начинает выполнение фаз по порядку и контролирует, что каждый этап завершен до начала следующего.
Config
Для увеличения повторного использования компонент, желательно объявит их с параметрами, которые могут быть внешне настроены. Конфигурационные средства обеспечивают возможности для этого. Они основаны на базе пар имя-значение, и называются configuration items, которые организованы иерархически. Каждая компонента содержит таблицу конфигураций элементов конфигурации, так как компоненты организованы в качестве дерева, каждый элемент в базе данных может быть уникально размещен по расположению компоненты и имени элемента конфигурации
Класс ovm_component содержит два набора методов для конфигурации размещения элементов в базу данных и потом для их получения. Это set_config_ * и * get_config_. В таблице ниже приведены оба набора.
Set_config_ * функция помещает элемент в конфигурационную базу данных текущего компонента, то есть в экземпляре компонента, в котором функция вызывается. Каждая из этих функций принимают три аргумента, имя, field_name и значение. Аргумент name - имя пути, который представляет набор компонент, которые должны соответсвовать кофигурациям этого элемента. name используется в * get_config_, чтобы найти элементы в базе данных конфигурации. field_name - это имя поля и должно быть уникальным в пределах текущей базы конфигураций. value является частью пары имя-значение и его тип может быть строка, INT, или ovm_object, в зависимости от вызываемой функции. Кроме того, set_config_object принимает копию аргумента для указания, был ли объект передан в качестве значения так как значение должно быть клонировано прежде чем оно будет помещено в базу конфигурации. Функции get_config_ * извлекает элементов из базы данных конфигураций. Эти функции принимают только два аргумента, имя поля и INOUT переменную, который содержит значение расположения элемента. Они также возвращают бит, чтобы указать был ли удачно расположен запрашиваемый объект. * Get_config_ функции не принимают аргументы имени пути, как их set_config_ * коллеги, поскольку они используют путь к текущему компоненту, как ссылку, чтобы найти элементы конфигурации. Они разработаны, чтобы узнать, есть значение элемента конфигурации для текущего контекста, то есть, компонент, в котором функция get_config_ * вызывается.
Алгоритм поиска для извлечения элементов конфигурации использует имя пути компонента запроса и путь к файлу вставляемый в каждый item. Он начинается с поиска конфигурационных элементов в базе данных в верхней компоненте по field_name. Если такой элемент существует, то он затем спрашивает, соответствует ли указанный путь к файлу пути имени компоненты. Если элемента с указанным field_name нет или имена путей не совпадают, то поиск идет с дочерней компоненты. Этот процесс продолжается до соответствия или до достижения компоненты начала поиска.
Путь каждого элемент конфигурации может быть регулярным выражением. Таким образом, мы используем алгоритм соответствия регулярных выражений для нахождения совпадения с путем запрошенной компоненты и именем пути элемента конфигурации. Эффект заключается в соответствии иерархической области. В качестве примера, рассмотрим простую иерархию на рисунке 4-5. Давайте предположим, что в env::build()мы запускаем два вызова set_config_ *:
112 function void build(); 113 c1 = new(“c1”, this); 114 c2 = new(“c2”, this); 115 116 set_config_int(“c2.*”, “i”, 42); 117 set_config_int(“*”, “t”, 19); 118 endfunction file: 04_OVM_mechanics/04_config/top.sv
Это вызовет запись двух конфигурационных элементов в базу данных в env. Обратите внимание на звездочку (*) в имени пути. Имя пути в вызове set_config_ * - регулярное выражение, и дикие символы используются для указать несколько областей, к которыми элемент применяется. По элементу i, c2. * показывает, что в любой области ниже c2 в иерархии, i будет определять указанное значение. В этом случае указанное значение - 42. Если вы опустите звездочку, то элемент конфигурации применяется только к c2, а не к любому из своих потомков.
Состояние конфигурации базы данных для каждого компонента в иерархии после вызова set_config_ * показано на рисунке 4-6,
пусть в элементе top.c1.child1 мы осуществим вызов:
int i; ... get_config_int(“i”, i)
Поиск задает вопрос: Каково значение конфигурации i в иерархических рамках top.c1.child1? Чтобы ответить на этот вопрос, конфигурации базы данных в env ищется в первую очередь. Запись i там говорит, что значение i совпадающее с env.c2. * - 42. Тем не менее, компоненты, из которых был сделан запрос, находятся в c1 sub-hierarchy. Таким образом, не существует соответствия, а get_config_int () возвращает статус неудачи. Запрос в любую компоненту, которая является дочерней c2 будет успешно завершен и вернется значение 42.
Ниже приведен код для функцию build() дочерней компоненты. Это место где эти компоненты ищут конфигурационные значения i и t.
60 function void build(); 61 62 string msg; 63 64 if(!get_config_int(“t”, t)) begin 65 $sformat(msg, “no value for t found in config database, using default value of %0d”, t); 66 ovm_report_warning(“build”, msg); 67 end 68 69 if(!get_config_int(“i”, i)) begin 70 $sformat(msg, “no value for i found in config database, using default value of %0d”, i); 71 ovm_report_warning(“build”, msg); 72 end 73 74 endfunction file: 04_OVM_mechanics/04_config/top.sv
Следующий пример показывает вывода при выполнении этого проекта:
# OVM_INFO @ 0 [RNTST] Running test ... # OVM_WARNING @ 0: env.c1.child1 [build] no value for i found in config database, using default value of 91 # OVM_WARNING @ 0: env.c1.child2 [build] no value for i found in config database, using default value of 91
Запрос на конфигурационный элемент t был успешным во всех контекстах с того момента как вызов set_config_int установил, что tдоступно во всех контекстах. Два запроса на элемент конфигурации i были успешными, и два нет. Результат получился такой потому что мы ограничены наличием i только компонентах на уровне или ниже env.c2. Компоненты на уровне или ниже c1 не могут видеть элемент конфигурации i из-за того, как мы создали базу данных конфигурации.