OVM/OVM методология/Механика OVM
Содержание |
Механика 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() использует глубину узла и определяет является ли он конечным для печати строки каждого узла.