«Бог не меняет того, что (происходит) с людьми, пока они сами не изменят своих помыслов.» Коран, Сура 12:13

OVM/OVM методология/Повторное использование Testbench

Материал из Wiki

Перейти к: навигация, поиск
Проект Диплом

Вебинары
Литература

* OVM *

Содержание

Reuse

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

6.1 Типы Повторное использование (или повторное использование типов)

Основное средство, чтобы сделать компоненты пригодными для повторного использования - это инкапсуляции(объединение) всех данных и функций внутри четко определенного интерфейса. Интерфейс определяет, как вы можете изменить, обращаться и запрашивать (извлекать данные из) компонент. Всякий доступ будет запрещен кроме того, что конкретно разрешен в интерфейсе. Мы рассмотрим четыре метода для создания многоразовых элементов тестбенча: вызовы функций, параметризованные классы, наследование и конфигурации. Каждый из этих методов представляет собой разные способы изменить структуру или поведение, используя интерфейс. В каждом из этих методов, информация подаваемая снаружи вызывает изменение структуры или поведение элемента. Первые три способа сделать элементы многоразовыми упоминались в главе 2, где мы обсуждали объектно-ориентированное программирование.

  • Вызов функций. - алгоритм или другая функциональность заключенная в вызове функции. Всякий раз, когда вы нуждаетесь в этой функциональности, вы можете просто вызвать функцию, а не копировать и вставить код или переписать его полностью в этом место. Функции могут принимать параметры, значения которых изменяют поведение функции.
  • Наследование. Инкапсуляция данных и функциональности произвольной сложности в единый объект, который скрывает сложность, так что объект может быть опущен на место и осуществляет свою деятельность через интерфейсы. Добавление или изменение функциональности по наследству - есть способ повторного использования базового объекта и воспользоваться любой функциональностью, которая в нем содержится.
  • Параметризованные классы обеспечивают способ создания повторно используемых классов. Классы с параметрами образует template1, которые могут быть созданы несколько раз с различными параметрами образуют семейство классов. Скалярные значения и типы могут быть использованы в качестве параметров. Каждый экземпляр класса параметризованных называется специализацией. Для определения специализации параметры становятся частью типа.
  • Конфигурации в процессе выполнения. Настраиваемые элементы могут изменить свое поведение или структуру через установку флагов, переключателей, или конфигурации переменных.

6.2 Многоразовые компоненты

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

Sc g 6.2 p1.png

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

27 class hfpb_random_mem_master
28 #(int DATA_SIZE=8, int ADDR_SIZE=16)
29 extends hfpb_master_base #(DATA_SIZE, ADDR_SIZE);

Мы сделали предположение, что пользователи протокол HFPB, скорее всего, строить различные виды устройств управления операциями на шине HFPB. Hfpb_master_base позволяет нам поместить структуры и функции в базовом классе, который будет использоваться всеми шинами. Таким образом, в строительстве нашего УУ(устройство управления), hfpb_random_mem_master, мы повторно будем использовать функциональные возможности, предоставляемые в базовом классе. HFPB УУ на основе базового класса содержит множество вещей.

36 class hfpb_master_base
37 #(int DATA_SIZE=8, int ADDR_SIZE=16)
38 extends ovm_component;
39
40 typedef hfpb_master_base
41 #(DATA_SIZE, ADDR_SIZE) this_type;
42 typedef ovm_component_registry
43 #(this_type) type_id;
44
45 ‘include “hfpb_parameters.svh”
46
47 ovm_transport_port
48 #(hfpb_tr_t, hfpb_tr_t) transport_port;
49
50 ovm_barrier objection;
51 protected hfpb_addr_map #(ADDR_SIZE) addr_map;

Он содержит определение типа из ovm_component_registry, что приводит тому что компоненты должны быть зарегистрированы в фабрике. Он содержит транспортный порт, масив обьектов, и карту адресов. Все эти объекты, которые могут быть использованы masters, производные от этого класса. База HFPB мастер-класс является примером использования объектно-ориентированного наследования, техники повторного использования. Преимущество использования наследования является то, что вам нежно только писать и проверять код для объектов в базовом классе один раз. Каждый раз, когда вы используете повторно базовый класс для создания нового устройства управления шиной, вы гарантированно наследуете структуру мастера. Например, вы всегда будете знать, что мастера, полученные из hfpb_master_base будет иметь транспортный порт, и его имя будет transport_port. hfpb_random_mem_master, полученный мастером, сможет рандомизировать последовательность транзакций в память. Некоторые параметры, max_bursts и max_burst_size, будут управлять рандомизацией. max_bursts максимальное количество очередей, которые будут выпущены мастером в тесте, и max_burst_size максимальное количество транзакций в одной очереди. Вместо жесткого кодирования этих значений, мы делаем их доступными для объекта через средство конфигурации.

54 max_burst_size = 16;
55 if(!get_config_int(“max_burst_size”, max_burst_size))
begin
56 $sformat(s, “max burst size not specified, using
default of %0d”, max_burst_size);
57 ovm_report_warning(“build”, s);
58 end
59 $sformat(s, “max burst size: %0d”, max_burst_size);
60 ovm_report_info(“build”, s);
61
62 max_bursts = 100;
63 if(!get_config_int(“max_bursts”, max_bursts)) begin
64 $sformat(s, “max bursts not specified, using default
of %0d”, max_bursts);
65 ovm_report_warning(“build”, s);
66 end
67 $sformat(s, “max bursts: %0d”, max_bursts);
68 ovm_report_info(“build”, s);
Для каждого из двух параметров, мы сначала установить значение по умолчанию 16 для max_burst_size и 100 для max_bursts. Затем, для каждого из них, мы называем get_config_int чтобы увидеть, если значение не задано извне. Если нет, то выдается предупреждение, чтобы предупредить пользователя, что значение не было указано и использовать по умолчанию. Важно, чтобы выдавалось предупреждающее сообщение, если get_config_ * возвращает 0. Без ошибки, без выдачи сообщения компонента использует значение по умолчанию, даже если это не было предусмотрено. Это возможно, например, если тест писатель забыл указать значение в базе данных конфигурации для max_burst_size и max_bursts. Или, в вызове set_config_int, вполне возможно, что тест писатель сделал опечатку в одном из имен. В этом случае, не понимая ошибки, тест писатель поверил бы, что величина устанавливается правильно. В этом случае, из-за незамеченной опечатки, get_config_int вызов не находит значение для извлечения, а вместо этого, он использует значение по умолчанию. Кроме того, поведение теста отличается, от того что предполагалось, без предупреждения, о том что все может быть не так. Если было предупреждение, то пользователь может позже посмотреть на прогон теста, чтобы определить, если тест вел себя, как предполагалось. Это пример применения конфигурации объекта, чтобы сделать компонент многократного использования. Вместо того чтобы создавать отдельные версии компонента, каждый с различными характеристиками, мы определим характеристики, которые могут изменить и обеспечить средства для их быть изменены без того, чтобы изменять компоненту.
23 class hfpb_driver #(int DATA_SIZE=8, int ADDR_SIZE=16)
24 extends
25 ovm_driver #(hfpb_seq_item #(DATA_SIZE, ADDR_SIZE),
26 hfpb_seq_item #(DATA_SIZE, ADDR_SIZE));

Драйвер имеет два параметра, DATA_SIZE и ADDR_SIZE. Каждый имеет значение по умолчанию, так что если вы не предоставите один, по умолчанию используется. Каждый раз при объявлении объекта параметризованного типа, необходимо создать специализацию, копию кода со значением параметра и заменить его имя. Вы никогда не увидеть специализированный код, компилятор берет на себя управление им без вашего вмешательства. Драйвер является производным от ovm_driver, базовый класс, который рассматривается в главе 8. ovm_driver класс с параметризованной последовательностью типов элементов посылаемых между ним и секвенсером. В случае протокол HFPB, последовательностью параметризованных элементов является DATA_SIZE и ADDR_SIZE. Код драйвера написан таким образом, что любое место, где есть нужна либо размерность шины данных или размер адресной шины, используются DATA_SIZE и ADDR_SIZE параметры в качестве постоянных. Мы можем использовать параметризованные компоненты в различных ситуациях, и, таким образом, мы поддерживаем независимость любого конкретного значения либо DATA_SIZE или ADDR_SIZE. Затем, изменяя параметры, мы можем влиять на структуру компонента. Таким образом, мы можем использовать наши параметризованные компоненты в системах с другим адресом и шириной шины данных.

Создавая все HFPB компоненты так, чтобы они были параметризованы таким же образом, мы можем построить целый испытательный стенд, что не зависит от ширины адреса и шины данных. Начиная с верхнего уровня, мы передаем ADDR_SIZE и DATA_SIZE параметров в самый верхний модуль. Это единственное место, где необходимо указать значения для DATA_SIZE и ADDR_SIZE. Везде, значения, будут получены через параметры классов.

83 module top;
84
85 parameter int DATA_SIZE = 8;
86 parameter int ADDR_SIZE = 9;
87
88 env #(DATA_SIZE, ADDR_SIZE) e;
89
90 initial begin
91 e = new(“env”);
92 run_test();
93 end
94
95 endmodule
file: 06_reuse/01_TL/top.sv

Эти параметры передаются в ENV, самый верхний уровень компонента тестбенча, создан как специально параметризованный класс. ENV, конечно, это параметризованный компонент, параметры которых также DATA_SIZE и ADDR_SIZE. Кроме того, любой компонент создаваемый в ENV, которые зависят от адреса или размера шины данных, аналогичным образом параметризованы.

42 class env #(int DATA_SIZE=8, int ADDR_SIZE=16)
43 extends ovm_component;
44
45 hfpb_mem #(DATA_SIZE, ADDR_SIZE) mem;
46 hfpb_random_mem_master #(DATA_SIZE, ADDR_SIZE) mm;
47 hfpb_addr_map #(ADDR_SIZE) addr_map;
48
49 tlm_transport_channel
50 #(hfpb_transaction #(DATA_SIZE, ADDR_SIZE),
51 hfpb_transaction #(DATA_SIZE, ADDR_SIZE))
52 transport_channel;
file: 06_reuse/01_TL/top.sv

6.3 Агенты

Это довольно часто распространено когда при создании testbenches вы можете увидеть много повторяющихся экземпляров и соединений. Это типично для подключения драйверов и мониторов во многом похожим способом. Кроме того, вы будете часто замечать что, когда вы повторяете определенные блоки компоненты, вы захотите, чтобы они были всегда сразу настроены. Индивидуальная настройка экземпляров и компонентов может внести скучные ошибоки. Агенты решают эту проблему. Агенты все для организации повторного использования. Они могут повторно использовать мониторы, драйвера и другие компоненты, которые являются частью конкретного протокола, и они сами образуют компоненты многократного использования путем создания интерфейса по всем подчиненным компонентам. Агент содержит все элементы протокола внутри вместе в одном пакете. Вы можете легко применять протокол в испытательном стенде путем создания экземпляра этого пакета, а не по отдельности экземпляр драйвера, монитор и других специфичных для протокола компонентов.

Агент является оберткой всех компонент, которые реализуют протокол. Он служит в качестве интерфейса для всех компонентов протокола. Интерфейс имеет несколько форм: параметры класса, port и export, виртуальный интерфейс и интерфейс конфигурации. Параметры класса передаются на подчиненные компоненты протокола. Ports и exports обеспечивают вход и выход пунктов для транзакций; виртуальный интерфейс обеспечивает вывод на уровне подключения контактов и настройки интерфейса позволяет включить или отключить различные компоненты для настройки поведения агента для конкретных приложений.

Рисунок 6-2 показывает простой агент только с драйвером и монитором. Внешние интерфейсы включают экспорт для передачи транзакции, analysis port (который делает доступными все шины транзакций), а виртуальный интерфейс для подключения к шине на уровне контактов.

Sc g 6.3 p1.png

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

Sc g 6.3 p3.png

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

Sc g 6.3 p2.png

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

6.4 Многоразовое использование HFBP

Агент HFPB является весьма параметризованным и настраиваемым устройством. Он содержит все специфичные для протокола компоненты, необходимые для протокола HFPB в одном экземпляров компонента. Это включает в себя мастера, драйвер (мы объясним разницу в ближайшее время), секвенсор, salve, и coverage collector (коллекция покрытия). Связи между этими компонентами указанна в агенте. Как уже говорилось выше этот простой агент обладает параметрами класса, коллекцией на уровне транзакций port export, виртуальный интерфейс для подключения на уровне соединения и конфигурацию интерфейса.

Sc g 6.4 p1.png

Параметры в заголовке класса агента, DATA_SIZE и ADDR_SIZE, используется при создании внутренней структуры агента.

138 class hfpb_agent #(int DATA_SIZE=8, ADDR_SIZE=16)
139 extends ovm_agent;

Внешние соединения включают в себя виртуальный интерфейс для подключения на уровне соединения, канал export для традиционного TLM, порт последовательность для подключения к секвенсор, массив salave export (по одному для каждого slave), и analysis port для передачи операции признанных на шине на уровне подключения.

142 virtual hfpb_if #(DATA_SIZE, ADDR_SIZE) m_bus_if;
143
144 ovm_transport_export
145 #(hfpb_transaction #(DATA_SIZE, ADDR_SIZE),
146 hfpb_transaction #(DATA_SIZE, ADDR_SIZE))
147 transport_export;
148
149 ovm_seq_item_pull_port
150 #(hfpb_seq_item#(DATA_SIZE, ADDR_SIZE),
151 hfpb_seq_item#(DATA_SIZE, ADDR_SIZE))
152 seq_item_port;
153
154 ovm_slave_export
155 #(hfpb_transaction #(DATA_SIZE, ADDR_SIZE),
156 hfpb_transaction #(DATA_SIZE, ADDR_SIZE))
157 slave_export [];
158
159 ovm_analysis_port
160 #(hfpb_transaction #(DATA_SIZE, ADDR_SIZE))
161 analysis_port;

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

164 local hfpb_master #(DATA_SIZE, ADDR_SIZE) master;
165 local hfpb_driver #(DATA_SIZE, ADDR_SIZE) driver;
166 local hfpb_slave #(DATA_SIZE, ADDR_SIZE) slave [];
167 local hfpb_monitor #(DATA_SIZE, ADDR_SIZE) monitor;
168 local hfpb_coverage #(DATA_SIZE, ADDR_SIZE) cov;
169 local hfpb_talker #(DATA_SIZE, ADDR_SIZE) talker;
170 hfpb_sequencer #(DATA_SIZE, ADDR_SIZE) sequencer;

Причина, по которой секвенсор не объявлены как локальные, потому что это необходимо для получения доступа секвенсор для того, чтобы управлять им. В нашем HFPB агенте у нас есть и мастер и драйвер. Разница между master и драйвером состоит в том, что мастер содержит транспортный export, а драйвер seq_item_pull_port для последовательностей, которые связаны с секвенсором. (Мы обсудим последовательности и секвенсоры в главе 8). Кроме того, объекты которые принимаются на входе мастера должны быть производными от ovm_transaction и наборы значений, которые принимаются драйвером должны быть производным от ovm_sequence_item. Это два разных средства для перемещения операции запрос и ответ внутрь и из агента. Мастер используется для традиционных структурных моделей уровня транзакций, и драйвер для обработки транзакций (в виде последовательности элементов), генерации последовательностей. Протокол HFBP позволяет работать ровно с одним мастером и одним или несколькими slave, поэтому мастера и драйвера являются взаимоисключающими (взаимозаменяемыми). Это не имеет смысла в данном протоколе, чтобы иметь более одного устройства управляющего шиной. Важно заметить, что интерфейсы и внутренние компоненты имеют класс параметров, которые совпадают с агентом, то есть DATA_SIZE и ADDR_SIZE. При создании семейства компонентов для протокола HFPB, которые параметризованы одинаково, мы можем перейти к параметрам и не указывать значения этих параметров более одного раза. Не все внутренние компоненты и интерфейсы экземпляров используются каждый раз, когда и агент. Создаются только те, которые необходимы для указанной конфигурации. Вместо того, чтобы создавать отдельные агенты для каждой возможной конфигурации, которые были бы очень неуклюжими, мы будем использовать конфигурацию объекта в OVM, чтобы изменить структуру агента. Наши HFPB агент имеет ряд параметров конфигурации, которые контролируют его структуру.

175 local bit has_monitor;
176 local bit has_coverage;
177 local bit has_talker;
178 local bit has_master;
179 local bit has_driver;
180 local bit has_sequencer;
181 local int unsigned slaves;
182 local hfpb_vif #(DATA_SIZE, ADDR_SIZE) vif;

В build() и connect() агент использует значения этих параметров конфигурации для управления тем как создаются суб-компоненты и как они связаны. Значение для каждого параметра конфигурации получены с помощью вызовов get_config_int (или, в случае VIF, get_config_obj). В функции агента build() представлен серию вызовов get_config_*, чтобы получить все необходимые сведения о конфигурации этого компонента.

199 if(!get_config_int(“has_monitor”, has_monitor)) begin
200 has_monitor = 0;
201 monitor = null;
202 end
203
204 if(!get_config_int(“has_coverage”,
205 has_coverage)) begin
206 has_coverage = 0;
207 cov = null;
208 end
. . .

Как только функция build() получает все параметры конфигурации, она также делает любую необходимую проверку ошибок и проверку их согласованности. Каждый вызов get_config_* заключен в условный оператор, который проверяет состояние вызова. Если вызов не удается, значит нет пункта с указанным именем для текущей области в базе данных конфигурации, то нужно убедится, что параметр конфигурации установлен на правильное значение. А также другие переменные параметры, по мере необходимости. Например, если has_monitor не передается, то убедитесь, что он установлен в 0 и указатель монитора является недействительным(null). Если has_coverage установлен в 1, мы также устанавливаем has_monitor в 1, поскольку не имело бы никакого смысла иметь коллектор(корзину) покрытия без монитора. И так далее. Дальше в по порядку, мы используем параметры конфигурации для построения внутренностей агента. В качестве примера, если has_master установлен, то мы создаем экземпляр компонента master и transport export, который эта компонента будет использовать для подключения к внешним компонентам.


284 if(has_master) begin
285 master = new(“master”, this);
286 transport_export = new(“transport_export”, this);
287 end

Позже, в фазе connect, мы снова будем использовать параметр has_master конфигурации, на этот раз, чтобы определить есть ли подключение к transport export.

340 if(has_master) begin
341 transport_export.connect(master.transport_export);
342 end

Эта проверка необходима, потому что если has_master равен 0, то мы не знаем есть ли хоть один экземпляр компонента мастер, ни transport export экземпляр. Проверка значения has_master снова гарантирует, что мы не пытаемся подключить компоненты, экземпляров которых не было. VIF виртуальной интерфейс также получит через конфигурацию объекта, используя технику интерфейс объекта, описанного в разделе 4.7. Строго говоря, интерфейс объекта это не параметр конфигурации. Виртуальные интерфейсы являются частью соединения конструкции. Если вы составите агент из виртуального интерфейса, вероятно, агент не будет работать правильно в дизайне. Мы можем использовать наши высоко настраиваемые агенты в различных направлениях. В таблице ниже приведены некоторые интересные конфигурации

Mode Config Settings
Monitor
has_monitor = 1
has_coverage = don't care
has_talker = don't care
has_master = 0
has_driver = 0
has_sequencer = 0
slaves = 0
Master
has_monitor = don't care
has_coverage = don't care
has_talker = don't care
has_master = 1
has_driver = 0
has_sequencer = 0
slaves = 0
Driver
has_monitor = don't care
has_coverage = don't care
has_talker = don't care
has_master = 0
has_driver = 1
has_sequencer = 0
slaves = 0
Sequencer
has_monitor = don't care
has_coverage = don't care
has_talker = don't care
has_master = 0
has_driver = 1
has_sequencer = 1
slaves = 0

В режиме монитора, все компоненты выключен, кроме монитора. Затем агент действует строго в качестве монитора. Таким же образом можно настроить aster режим, выключив все, кроме master, а затем работать с агентом исключительно как с мастером. Вы можете включить или отключить любой компонент или подмножество компонентов агента. Так произошла инкапсуляции всех протоколов отдельных компонентов в единый компонент, называемый агентом, то используя OVM объекты конфигурации, вы можете создать один компонент, который может быть использован для различных приложений. Одной из причин всего этого конфигурирования является обеспечение повторного использования компонентов тестбенча на уровне блоков, без изменений, в системного уровня тестбенча. Мы обсудим это подробнее в главе 9.

6.5 Пример агента

As an example of using an agent, we’ll transform the design shown in Figure 6-1 to use an agent instead of a transport channel. We’ll also add a second memory.

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

Sc g 6.5 p1.png

В этом примере, агент выступает в качестве модели шины. Она содержит мастер и slave, и все связи между ними. Кроме того, он содержит монитор. В этом примере мы настроим агентом следующим образом:

80 set_config_int(“hfpb_agent”, “has_monitor”, 0);
81 set_config_int(“hfpb_agent”, “has_master”, 1);
82 set_config_int(“hfpb_agent”, “slaves”, 2);
83 set_config_int(“hfpb_agent”, “has_talker”, 0);
84 set_config_object(*, “addr_map”, addr_map, 0);
file: 06_reuse/02_RTL/top.sv

Монитор, мастер, и talker включен, и настроена на два slave. Кроме того, в поставляется карта адреса, в который определяет, какая часть адресного пространства занимает памяти каждого slave. talker является абонентом устройства, подключенного к порту анализа. Он просто выводит транзакции, распознанные монитором. Talker включается, когда вы хотите видеть печатных отчет обо всех операциях, которые проходят через агента. С учетом этой конкретной конфигурации, мы можем эффективно построить топологию показанную на рисунке 6-7.

Sc g 6.5 p2.png

Верхний уровень модуля для этой конструкции аналогичен описанной в примере в разделе 6.2. Так, что, например, значения для параметров DATA_SIZE и ADDR_SIZE задаются здесь и передаются окружения построенного как класс, которое, в свою очередь, передает их на агента.
120 module top;
121
122 parameter int DATA_SIZE = 8;
123 parameter int ADDR_SIZE = 9;
124
125 env #(DATA_SIZE, ADDR_SIZE) e;
126 hfpb_vif #(DATA_SIZE, ADDR_SIZE) hfpb_vif_obj;
127
128 clk_rst cr();
129 clock_reset ck (cr);
130 hfpb_if #(DATA_SIZE, ADDR_SIZE) bus_if (cr.clk, cr.rst);
131
132 initial begin
133
134 e = new(“env”);
135 hfpb_vif_obj = new(bus_if);
136 set_config_object(*, “hfpb_vif”, hfpb_vif_obj, 0);
137
138 fork
139 ck.run();
140 join_none
141
142 run_test();
143 end
144
145 endmodule
file: 06_reuse/02_RTL/top.sv

Кроме того, мы используем технику интерфейс объекта, описанного в разделе 4.7 для передачи виртуального интерфейса в окружении построенного как класс. За счет применения параметризованных классов и конфигураций OVM объектов мы можем создать единый компонент, агент HFPB, что позволяет реализовать различные топологии и конфигурации компонентов протокола HFPB.

6.6 Итог

Одним из основных ключей к повышению производительности и надежности вашей процесса верификации есть повторное использование. Повторное использование компонентов экономит время, не занимая времени для написания нового кода. Бывшие в употреблении компонентами являются более надежными, в силу того, что они были использованы в нескольких приложениях. Многоразовые компоненты не пропадают бесследно, вы должны поставить некоторые мысли о том, как вы будете повторно использовать конкретную компоненту. К счастью, SystemVerilog и OVM обеспечивают некоторые средства, которые способствуют разработке элементов тестбенча для многоразового использования.