<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="http://www.simhard.com/wiki/skins/common/feed.css?303"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://www.simhard.com/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Anastasiya</id>
		<title>Wiki - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://www.simhard.com/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Anastasiya"/>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/Anastasiya"/>
		<updated>2026-04-11T09:20:35Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.21.3</generator>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5</id>
		<title>OVM/OVM методология/Transaction-Level моделирование</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5"/>
				<updated>2013-04-09T15:11:21Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: /*  */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Процесс проектирования электронной системы включает в себя последовательное замещение абстрактных идей  конкретной их реализацией, пока не будет достигнуто представление, которое может быть изготовлено на кремнии. С момента появления цифровой интегральной схемы, электронные сообщества тщательно определили и кодировали абстракции, начиная с переключателей и логических элементов, для обеспечения среды  в которой работает  проект. RTL является примером среды, часто используемой для создания проектов. Есть много инструментов на основе RTL, которые делают ее удобной для  создания проекта и его верификации.&lt;br /&gt;
Однако, как только проекты становятся больше и сложнее, становится все сложнее представлять их с помощью абстракции выше, чем RTL. Уровень транзакций становится популярным для создания первого представления проекта, которое может быть смоделировано и проанализировано.&lt;br /&gt;
В этой главе описаны основные понятия transaction-level моделирования (TLM). Модели  transaction-level состоят из нескольких процессов, которые обмениваются информацией посредством транзакций.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Абстракция ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
RTL моделирование использует дискретную модель времени. Связь между процессами осуществляется с помощью сетки (цепи), и процесс активации происходит при изменении значения во входной сети (цепи).&lt;br /&gt;
Для сравнения, transaction-level модели (моделирующей схемы) могут быть синхронизированы или не синхронизированы и использовать шину для обмена данными между процессами. Вместо того чтобы посылать отдельные биты туда и обратно, процессы общаются, посылая транзакции друг  другу посредством вызова функций.  Мир TLM включает в себя ряд моделей(моделирующих схем) вычислений с различным временем, системами связи и процессами активации моделей (моделирующей схемы). В каждом конкретном случае, содержание связей находится на более высоком уровне абстракции, чем отдельные биты. Таким образом, transaction-level модель находится на более высоком уровне абстракции (более абстрактна), чем RTL модель. Объединяя понятия абстракции и модели (моделирующей схемы) вычислений, видно, что создание абстрактных моделей означает абстрагирование времени, данных и функций. &lt;br /&gt;
&lt;br /&gt;
'''Абстракция времени.''' Время абстракция в симуляторе относится к тому, насколько внутренние состояния схемы совместимы. Модели, запущенные в управляемых событиями симуляторах (например, логических симуляторах) используют дискретное представление  времени, то есть события происходят в определенные моменты времени. События, как правило (хотя и не всегда) вызывают запущенный процесс. Чем больше событий происходит в симуляции, больше процессов вызывается, и с большим числом процессов происходит снижении скорости моделирования. Абстрагирование время уменьшает количество точек, где схема должна быть совместимой и общего числа событий и процессов, которые будут активированы. Например, в RTL модели, каждая сеть должна быть согласована после каждого изменения. В cycle-accurate абстракции, схема должна быть синхронизирована с синхросигналом, устраняя все события, что происходит между фронтами синхроимпульса. В transaction-level модели, состояния схемы должны быть синхронизированы в конце каждой операции, каждая из которых может охватывать много тактов.&lt;br /&gt;
&lt;br /&gt;
'''Абстракция данных'''. Данные относятся к объектам, передаваемым между компонентами. В RTL моделях, данные относятся к отдельным битам, которые передаются по сети между компонентами. В transaction-level моделях, данные представлены в виде транзакций, гетерогенных структур, которые содержат произвольные коллекции элементов.&lt;br /&gt;
Рассмотрим пакет в устройстве связи. На самом низком уровне детализации, пакет содержит бит начала и стоп-биты, заголовок, информацию исправления ошибок, размер передаваемых данных, данные, и пакет остановки передачи(последний пакет - trailer). В более абстрактной модели, только данные и размер данных может быть необходим. Другие  данные не нужны  для выполнения расчетов.&lt;br /&gt;
&lt;br /&gt;
'''Абстракция функций.''' Функция модели является набором всех операций, которая она делать на каждое событие. Абстрагирование функций уменьшает этот набор или заменяет его более простыми вычислениями. Например, в ALU, вы можете выбрать использование родной операции умножении поставляемой в язык моделирования, вместо кодирования  a shift-and-add алгоритма умножения. Последний может быть частью реализации, но на более высоком уровне, детали a shift-and-add - неважны. Базовые элементы, которые являются частью языка, определяет, как вы можете абстрагировать функцию. В gate-level  языке, например, можно создавать сложные поведения, начиная с логических элементов. В RTL языках, построение  поведения основывается на арифметических и логических операциях над регистрами. В TLM, вы реализуете функциональность схемы с помощью вызовов функций произвольной сложности.&lt;br /&gt;
&lt;br /&gt;
Для целей функциональной верификации, RTL самый низкий уровень абстракции, который мы должны рассмотреть. С синтезаторы могут эффективно конвертировать RTL в логические элементы, нам не нужно затрагивать  более низкий уровень детализации.&lt;br /&gt;
&lt;br /&gt;
==  Определение транзакции ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для более подробного рассмотрения TLM, мы должны сделать шаг назад и определить понятие транзакции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:61.png]]&lt;br /&gt;
&lt;br /&gt;
Это самое общее определение транзакции. Оно утверждает, что транзакция – это все, что происходит в проекте (или модуле или подсистеме проекта) между двумя точками времени. Хотя оно точное, оно является настолько общим, что не приводит к практическому применению. Более полезные определения  следующие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:62.png]]&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Транзакция — передача управления и данных между двумя элементами (компонентами)&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это аппаратно-ориентированные понятие транзакции. Когда смотришь на часть аппаратного обеспечения, вы можете легко определить элементы, между которыми передается управление или данные. В проекте с шинной архитектурой, чтение и запись на шину может быть вызвано транзакцией. В пакетной системе связи, отправка пакета представляет собой транзакцию.&lt;br /&gt;
&lt;br /&gt;
Ниже третье определение:&lt;br /&gt;
&lt;br /&gt;
[[Файл:63.png]]&lt;br /&gt;
&lt;br /&gt;
Это определение программно-ориентированного понятия транзакции. В transaction-level модели, деятельность инициируется путем вызова функции. Вызов функции содержит параметры, которые &amp;quot;посылаются&amp;quot; (передаются) вызываемой функции, а возвращаемое значение функции содержит данные, которые возвращаются вызванной функцией. Вызванная  функция может блокировать и вызвать задержку (в синхронной системе) или может вернуться (выполниться) немедленно.&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
This definition is the software-oriented notion of a transaction. In a&lt;br /&gt;
transaction-level model, activity is initiated by making function calls. The&lt;br /&gt;
function call contains parameters that are “sent” to the called function, and&lt;br /&gt;
the return value of the function contains data that is returned by the called&lt;br /&gt;
function. The called function could block and cause time to pass (in a timed&lt;br /&gt;
system) or it could return immediately.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Интерфейсы ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Прежде чем вдаваться в подробности о том, как построить transaction-level модель, мы сначала обсудим интерфейсы. Термин интерфейс используется в несколько случаях в OVM, каждый раз с немного другим смыслом. Это прискорбный факт истории, то что слово стало означать  различные вещи. В большинстве случаев значение будет понятно из контекста, в котором этот термин используется. Различных применения :&lt;br /&gt;
&lt;br /&gt;
* Интерфейс в SystemVerilog&lt;br /&gt;
* Интерфейс объекта&lt;br /&gt;
* Интерфейс DUT&lt;br /&gt;
&lt;br /&gt;
'''Интерфейс в SystemVerilog'''. SystemVerilog предоставляет конструкцию, называемую интерфейс, который является одним из основных объектов контейнера, из которого строится проект в SystemVerilog. Мы используем виртуальные интерфейсы, которые  указывают на интерфейсы для подключения module-based аппаратуры к  class-based testbenches.&lt;br /&gt;
&lt;br /&gt;
'''Интерфейс объекта.''' Общедоступные задачи и функции, доступные для объекта, образуют его интерфейс. Есть два небольших варианта этого значения интерфейса. Одним из них является прямой. Посмотрите на класс и определите, какие задачи и функций, доступны пользователю класса. Это его интерфейс. Другое значение состоит в ссылке на базовый класс, который определяет набор задач и функций, доступных для работы производного класса. Это значение интерфейса обычно используется в объектно-ориентированных языках, которые поддерживают множественное наследование, таких как C++ или Java. В этих  языках, вы можете установить требование, что производный класс определяет функциональность, наследуемую от интерфейса базового класса.&lt;br /&gt;
&lt;br /&gt;
[[Файл:64.png]]&lt;br /&gt;
&lt;br /&gt;
Интерфейс print_if определяет прототипы для функций печати.  Любой класс, который наследуется от print_if затем обязан осуществлять print()и sprint(). SystemVerilog не поддерживает множественное наследование, но он  поддерживает истинно виртуальные интерфейсы. Истинно  виртуальный интерфейс - интерфейс во втором контексте (базовый класс, который определяет набор задач и функций прототипов), который не имеет реализаций. Истинно  виртуальная версия  print_if  выглядит следующим образом в SystemVerilog:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;virtual class print_if;&lt;br /&gt;
pure virtual function void print();&lt;br /&gt;
pure virtual function string sprint();&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Даже если SystemVerilog не поддерживает множественное наследование, и OVM построено на SystemVerilog, важно понять истинно виртуальные интерфейсы и наследование интерфейсов, потому что они активно используются в OVM. В частности, TLM ports and exports являются производными от интерфейса класс tlm_if_base. &lt;br /&gt;
&lt;br /&gt;
'''DUT интерфейс.''' Часть аппаратуры, как правило, доступны через свои интерфейсы. В этом контексте, интерфейс состоит из контактов и протоколов используемых для связи с устройством. Например, устройство может иметь USB интерфейс.&lt;br /&gt;
&lt;br /&gt;
== TLM идиомы ==&lt;br /&gt;
&lt;br /&gt;
В данном разделе рассматриваются основные средства передачи транзакций между компонентами. Мы рассмотрим put, get, и transport формы связи транзакций. Эти примеры не используют библиотеку OVM, так как они предназначены для иллюстрации основных механизмов связи на transaction-level с минимальными затратами. В следующем разделе мы рассмотрим более полный пример, который использует OVM библиотека для связи.&lt;br /&gt;
&lt;br /&gt;
=== Put ===&lt;br /&gt;
В put  конфигурации, один компонент отправляет операции  другому компоненту. Операция называется put. Инициатором является компонентом, который инициирует передачу, а целевой компонент, который получает результат. Используя TLM номенклатуру, мы говорим, что инициатор put транзакции цели.&lt;br /&gt;
&lt;br /&gt;
[[Файл:65.png]]&lt;br /&gt;
 &lt;br /&gt;
Рисунок 3-2 показывает, что А предает операцию в пункт Б. Инициатор имеет порт, рисуется как квадрат, и цель имеет export, изображается в виде круга. Поток  управления направлен  от квадрата к кругу, то есть, А вызывает B, который содержит реализацию методов port. Стрелка показывает направление поток данных, и в этом случае, это означает, что данные будут двигаться от А до B.&lt;br /&gt;
Мы можем проиллюстрировать код для этих компонентов с producer и consumer. producer является инициатором и consumer является целью. Мы должны строить эти компоненты таким образом, что они не знали друг о друге априори. Чтобы сделать это, мы используем истинно виртуальный интерфейс для определения функции, который будет использоваться для передачи данных между инициатором и целью. Во-первых, давайте взглянем на SystemVerilog версию producer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;46 class producer;&lt;br /&gt;
47&lt;br /&gt;
48 put_if put_port;&lt;br /&gt;
49&lt;br /&gt;
50 task run();&lt;br /&gt;
51&lt;br /&gt;
52 int randval;&lt;br /&gt;
53&lt;br /&gt;
54 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
55 begin&lt;br /&gt;
56 randval = $random %100;&lt;br /&gt;
57 $display(“producer: sending %4d”, randval);&lt;br /&gt;
58 put_port.put(randval);&lt;br /&gt;
59 end&lt;br /&gt;
60&lt;br /&gt;
61 endtask&lt;br /&gt;
62&lt;br /&gt;
63 endclass : producer&lt;br /&gt;
file: 03_tlm/01_put/put.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
producer  - это класс, который  создается динамически. Он имеет два ключевых элемента, Run () задачу и put_port. Run () задача является простой задачей,  которая повторяется 10 раз и  передает 10 транзакций. Для простоты, наши операции являются целыми числами. На практике, транзакция может быть сколь угодно сложным  объектом, таким как структура или класс.&lt;br /&gt;
Чтобы передать операции, producer вызывает put() на put_port. Что такое put_port? Это не порт в традиционном смысле Verilog. Это ссылка на put_if. Что такое put_if? Put_if это виртуальный интерфейс класса расположенный между инициатором (producer) и целью (consumer).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;39 virtual class put_if;&lt;br /&gt;
40 pure virtual task put(int val);&lt;br /&gt;
41 endclass : put_if&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
put_if класс с истинно виртуальными задачами, т.е.  задачами, не имеющими реализацию. Без реализации всех своих задач и функций, виртуальный класс не может быть создан сам по себе. Он должен быть базовым классом другого класса, который создается. В нашем случае, класс, производный от истинно виртуального put_if является consumer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;68 class consumer extends put_if;&lt;br /&gt;
69 task put(int val);&lt;br /&gt;
70 $display(“consumer: receiving %4d”, val);&lt;br /&gt;
71 endtask : put&lt;br /&gt;
72 endclass : consumer&lt;br /&gt;
file: 03_tlm/01_put/put.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
consumer содержит реализацию put(); истинно виртуальные задачи определены в put_if.  Задача put() реализация принимает аргументы, переданные ей, и печатает их. put_if играет ключевую роль в соединении consumer и producer. Ссылка на него на стороне producer, которую мы называем порт, устанавливает требование, что должна быть реализация  функций и задач  интерфейса, к которому этот объект будет привязан. consumer является производным от интерфейса и, следовательно, в нем должны быть реализованы истинно виртуальные задачи, удовлетворяющие требованиям.&lt;br /&gt;
&lt;br /&gt;
Модуль верхнего уровня связывающий producer и consumer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;77 module top;&lt;br /&gt;
78&lt;br /&gt;
79 producer p;&lt;br /&gt;
80 consumer c;&lt;br /&gt;
81&lt;br /&gt;
82 initial begin&lt;br /&gt;
83 // instantiate producer and consumer&lt;br /&gt;
84 p = new();&lt;br /&gt;
85 c = new();&lt;br /&gt;
86 // connect producer and consumer&lt;br /&gt;
87 // through the put_if interface class&lt;br /&gt;
88 p.put_port = c;&lt;br /&gt;
89 p.run();&lt;br /&gt;
90 end&lt;br /&gt;
91 endmodule : top&lt;br /&gt;
file: 03_tlm/01_put/put.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что оператор присваивания:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;88 p.put_port = c;&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Он образует связь между producer и consumer. Когда new() вызывается на р, чтобы создать новый экземпляр producer, член put_port  не имеет значения. Во время выполнения произойдет ошибка, если put_port. put () вызывается до связи назначения. Назначение С к p.put_port дает  ссылку для consumer, который содержит реализацию задачи put() интерфейса.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Get ===&lt;br /&gt;
&lt;br /&gt;
Дополнением к ''put'' является ''get''. В этой конфигурации, инициатор получает транзакции от цели. Поток управления такой же как у ''put'' — от инициатора к цели, но направление потока данных обратное. Инициатор получает транзакции от цели. В этом случае ''consumer'' является инициатором (блок А), ''producer'' — целью (блок В). ''Consumer'' инициирует вызов ''producer'' для получения транзакции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:66.png]]&lt;br /&gt;
 &lt;br /&gt;
Рисунок 3-3 очень похож на рисунок 3-2. Разница лишь в том, что здесь стрелка от цели к инициатору, а не наоборот. Это означает, что потоки данных идут от цели к инициатору. Ниже SystemVerilog consumer (инициатор, блок А).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;62 class consumer;&lt;br /&gt;
63&lt;br /&gt;
64 get_if get_port;&lt;br /&gt;
65&lt;br /&gt;
66 task run();&lt;br /&gt;
67 int randval;&lt;br /&gt;
68 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
69 begin&lt;br /&gt;
70 get_port.get(randval);&lt;br /&gt;
71 $display(“consumer: receiving %4d”, randval);&lt;br /&gt;
72 end&lt;br /&gt;
73 endtask&lt;br /&gt;
74 endclass&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Класс ''consumer'' имеет задачу, ''run()'', в которой выполняется 10 транзакций. Так же как класс ''producer'' в конфигурации ''put'', в данном примере класс ''consumer'' имеет порт. Кроме того, как в ''put''  примере, порт ссылается на истинно (pure) виртуальный интерфейс, в данном случае он называется ''get_if''.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;41 virtual class get_if;&lt;br /&gt;
42 pure virtual task get(output int t);&lt;br /&gt;
43 endclass : get_if&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''get_if'' является истинно виртуальным интерфейсным классом, который определяет функцию get(). Цель (producer) строится аналогично, как и цель в put  примере. Она содержит реализацию функции интерфейса. Этот producer генерирует случайное число между 0 и 99.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;48 class producer extends get_if;&lt;br /&gt;
49&lt;br /&gt;
50 task get(output int t);&lt;br /&gt;
51 int randval;&lt;br /&gt;
52 randval = $random % 100;&lt;br /&gt;
53 $display(“producer: sending %4d”, randval);&lt;br /&gt;
54 t = randval;&lt;br /&gt;
55 endtask&lt;br /&gt;
56&lt;br /&gt;
57 endclass : producer&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Связи на высшем уровне будут выглядеть очень похожими.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;79 module top;&lt;br /&gt;
80&lt;br /&gt;
81 producer p;&lt;br /&gt;
82 consumer c;&lt;br /&gt;
83&lt;br /&gt;
84 initial begin&lt;br /&gt;
85 // instantiate producer and consumer&lt;br /&gt;
86 p = new();&lt;br /&gt;
87 c = new();&lt;br /&gt;
88 // connect producer and consumer through the get_if&lt;br /&gt;
89 // interface class&lt;br /&gt;
90 c.get_port = p;&lt;br /&gt;
91 c.run();&lt;br /&gt;
92 end&lt;br /&gt;
93 endmodule : top&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После создания объектов producer и consumer, используя операцию new(),  два объекта будут соединяться с помощью связей назначения.&lt;br /&gt;
&lt;br /&gt;
=== Transport === &lt;br /&gt;
&lt;br /&gt;
Transport является двунаправленным интерфейсом. Интерфейс обеспечивает передачу транзакций от инициатора к цели и от цели обратно к инициатору. Как правило, мы используем этот механизм, чтобы смоделировать запрос-ответ протоколы. Когда речь идет о компонентах с двунаправленным интерфейсов, мы используем термины master (ведущего) и slave (ведомого), а не инициатор и цель.&lt;br /&gt;
 &lt;br /&gt;
[[Файл:67.png]]&lt;br /&gt;
&lt;br /&gt;
master (A) делает и put и get  за один вызов функции. Как мы видели в предыдущих примерах, put() и get() принимают один аргумент, аргумент они помещают или извлекают. Тем не менее, функция transport() имеет два аргумента, запрос и ответ. Она посылает запрос и возвращает  ответ. slave (B) принимает запрос и возвращает ответ.&lt;br /&gt;
&lt;br /&gt;
Давайте сначала посмотрим на истинно виртуальный интерфейс.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;37 virtual class transport_if;&lt;br /&gt;
38 pure virtual task transport(input int request,&lt;br /&gt;
39 output int response);&lt;br /&gt;
40 endclass : transport_if&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Интерфейс содержит одну функцию, transport(), которая принимает два аргумента: запрос, который передается  цели и ответ, который возвращается  инициатору. &lt;br /&gt;
&lt;br /&gt;
Master вызывает transport(), создает запрос и отправляет его slave, используя transport. Он обрабатывает ответ, который возвращается.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;45 class master;&lt;br /&gt;
46&lt;br /&gt;
47 transport_if port;&lt;br /&gt;
48&lt;br /&gt;
49 task run();&lt;br /&gt;
50&lt;br /&gt;
51 int request;&lt;br /&gt;
52 int response;&lt;br /&gt;
53&lt;br /&gt;
54 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
55 begin &lt;br /&gt;
56 request = $random % 100;&lt;br /&gt;
57 $display(“master: sending request %4d”,&lt;br /&gt;
58 request);&lt;br /&gt;
59 port.transport(request, response);&lt;br /&gt;
60 $display(“master: receiving response %4d”,&lt;br /&gt;
61 response);&lt;br /&gt;
62 end&lt;br /&gt;
63&lt;br /&gt;
64 endtask&lt;br /&gt;
65 endclass : master&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
slave реализует функцию transport(). В нашем примере, она делает некоторые тривиальные обработки запроса для создания ответа.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;70 class slave extends transport_if;&lt;br /&gt;
71&lt;br /&gt;
72 task transport(input int request, output int response);&lt;br /&gt;
73 $display(“slave: receiving request %4d”,&lt;br /&gt;
74 request);&lt;br /&gt;
75 response = -request;&lt;br /&gt;
76 $display(“slave: sending response %4d”,&lt;br /&gt;
77 response);&lt;br /&gt;
78 endtask&lt;br /&gt;
79&lt;br /&gt;
80 endclass&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Связи верхнего уровня между master и slave работают точно так же как и в примере с put и get.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;85 module top;&lt;br /&gt;
86&lt;br /&gt;
87 master m;&lt;br /&gt;
88 slave s;&lt;br /&gt;
89&lt;br /&gt;
90 initial begin&lt;br /&gt;
91 // instantiate the master and slave&lt;br /&gt;
92 m = new();&lt;br /&gt;
93 s = new();&lt;br /&gt;
94&lt;br /&gt;
95 // connect the master and slave through&lt;br /&gt;
96 // the port interface&lt;br /&gt;
97 m.port = s;&lt;br /&gt;
98 m.run();&lt;br /&gt;
99 end&lt;br /&gt;
100&lt;br /&gt;
101 endmodule : top&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Связь назначения связывает master и slave. После выполнения соединения, master может использовать соединения для прямого вызова функций slave.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Блокирующие интерфейсы против не блокирующих ===&lt;br /&gt;
&lt;br /&gt;
Интерфейсы, которые мы рассмотрели ранее, блокирующие. Это означает, что функции и задачи блокируют поток выполнения до их завершения. Им не разрешается заканчиваться неудачей.  Не существует механизма для блокировки при аварийном завершении или возможности иным образом изменять поток управления. Поток управления просто ждет, пока запрос будет выполнен. В синхронизированной системе это означает, что может  пройти время между вызовом и возвращением полученного результата.&lt;br /&gt;
&lt;br /&gt;
В put конфигурации, у нас есть два компонента, producer и consumer. Producer  генерирует случайное число и посылает его к consumer, используя  put(). Прежде чем вызывается put(),  нет активности в consumer. Вызов put() вызывает активность в consumer, которая выводит значение аргумента. За то время, что consumer является активным, producer ожидает. Это природа блокирующего вызова. Вызывающий должен ждать, пока вызов завершиться для возобновления выполнения.&lt;br /&gt;
&lt;br /&gt;
Теперь сравните это описание с неблокирующим вызовом. Неблокирующий вызов возвращается немедленно. Семантика неблокирующего вызова гарантирует, что вызов возвращается в том же дельта цикле, в котором он был запущен, то есть без задержки, даже на один дельта цикл.&lt;br /&gt;
&lt;br /&gt;
Истинно  виртуальный интерфейс, который соединяет неблокирующего slave  и master выглядит так же, как другие истинно виртуальные интерфейсы, которые мы видели. Значительная разница в том, что nb_get () возвращает значение статуса вместо транзакции.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;41 virtual class get_if;&lt;br /&gt;
42 pure virtual function int nb_get(output int t);&lt;br /&gt;
43 endclass : get_if&lt;br /&gt;
file: 03_tlm/04_nonblocking/nbget.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
master (consumer) должен проверить состояние, возвращаемое из nb_get (), чтобы определить, завершена ли функция успешно. Отметим также, что мы ввели время в модель. Consumer  проверяет каждый 4 нс доступность значения.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;78 class consumer; &lt;br /&gt;
79&lt;br /&gt;
80 get_if get_port;&lt;br /&gt;
81&lt;br /&gt;
82 task run();&lt;br /&gt;
83 int randval;&lt;br /&gt;
84 int ok;&lt;br /&gt;
85&lt;br /&gt;
86 for(int i=0; i&amp;lt;20; i++)&lt;br /&gt;
87 begin&lt;br /&gt;
88 #4;&lt;br /&gt;
89 if(get_port.nb_get(randval))&lt;br /&gt;
90 $display(“%t: consumer: receiving %4d”, $time,&lt;br /&gt;
randval);&lt;br /&gt;
91 else&lt;br /&gt;
92 $display(“%t: consumer: no randval”, $time);&lt;br /&gt;
93 end&lt;br /&gt;
94 endtask&lt;br /&gt;
95 endclass&lt;br /&gt;
file: 03_tlm/04_nonblocking/nbget.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
producer  организован как функция и задача. Задача будет раздвоена (порождена), чтобы запуститься как непрерывный процесс. Он генерирует новые случайные значения, что consumer будет захватывать. Тем не менее, каждая случайная величина доступна только для 2 нс из 7 нс цикла. Функция - реализация nb_get, которая возвращает значение, запускает периодически выполнение run().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;48 class producer extends get_if;&lt;br /&gt;
49&lt;br /&gt;
50 int randval = 0;&lt;br /&gt;
51 int rand_avail = 0;&lt;br /&gt;
52&lt;br /&gt;
53 function int nb_get(output int t);&lt;br /&gt;
54 if(rand_avail) begin&lt;br /&gt;
55 $display(“%t: producer: sending %4d”,&lt;br /&gt;
56 $time, randval);&lt;br /&gt;
57 t = randval;&lt;br /&gt;
58 return 1;&lt;br /&gt;
59 end&lt;br /&gt;
60 return 0;&lt;br /&gt;
61 endfunction&lt;br /&gt;
62&lt;br /&gt;
63 task run();&lt;br /&gt;
64 forever begin;&lt;br /&gt;
65 #5;&lt;br /&gt;
66 randval = $random % 100;&lt;br /&gt;
67 rand_avail = 1;&lt;br /&gt;
68 #2;&lt;br /&gt;
69 rand_avail = 0;&lt;br /&gt;
70 end&lt;br /&gt;
71 endtask&lt;br /&gt;
72&lt;br /&gt;
73 endclass : producer&lt;br /&gt;
file: 03_tlm/04_nonblocking/nbget.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Когда мы запускаем пример, мы видим, что не каждый вызов nb_get () успешен.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;4: consumer: no randval&lt;br /&gt;
8: consumer: no randval&lt;br /&gt;
12: producer: sending -99&lt;br /&gt;
12: consumer: receiving -99&lt;br /&gt;
16: consumer: no randval&lt;br /&gt;
20: producer: sending -39&lt;br /&gt;
20: consumer: receiving -39&lt;br /&gt;
24: consumer: no randval&lt;br /&gt;
28: producer: sending -9&lt;br /&gt;
28: consumer: receiving -9&lt;br /&gt;
32: consumer: no randval&lt;br /&gt;
36: consumer: no randval&lt;br /&gt;
40: producer: sending 57&lt;br /&gt;
40: consumer: receiving 57&lt;br /&gt;
44: consumer: no randval&lt;br /&gt;
48: producer: sending -71&lt;br /&gt;
48: consumer: receiving -71&lt;br /&gt;
52: consumer: no randval&lt;br /&gt;
56: producer: sending -14&lt;br /&gt;
56: consumer: receiving -14&lt;br /&gt;
60: consumer: no randval&lt;br /&gt;
64: consumer: no randval&lt;br /&gt;
68: producer: sending 29&lt;br /&gt;
68: consumer: receiving 29&lt;br /&gt;
72: consumer: no randval&lt;br /&gt;
76: producer: sending 18&lt;br /&gt;
76: consumer: receiving 18&lt;br /&gt;
80: consumer: no randval&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Блокировать get конфигурацию смог только один процесс - consumer, который постоянно обращался с запросом к producer, чтобы тот отправил новое значение. Неблокирующий  вариант состоит из двух этапов: consumer регулярно опрашивает producer, чтобы получить новое значение, и producer генерирует новые значения асинхронно по отношению к consumer. Наши неблокирующий producer генерирует доступное случайное значение каждые 7 нс. Он ждет 5 нс, а затем создает новое значение и новое значение действительно в течение 2 нс. Флаг  rand_avail устанавливается, когда  случайное значение доступно и сбрасывается в противном случае.&lt;br /&gt;
&lt;br /&gt;
Реализация nb_get () в этом примере должна проверить rand_avail  есть ли  что отправить. Если нет, то она возвращает 0, чтобы указать, что запрос не удался. Если есть что-то, то он посылает его и возвращает 1, чтобы показать успех.&lt;br /&gt;
&lt;br /&gt;
Блокирующие интерфейсы полезны для работы с двумя компонентами синхронно. Вызовы блокируются до тех пор, пока запрошенная операция выполняется, не смотря на то, сколько это потребует времени. С другой стороны, неблокирующие интерфейсы полезны при асинхронной передаче данных. Они не ждут и могут быть использованы для опроса целей, как в приведенном примере.&lt;br /&gt;
&lt;br /&gt;
== Изоляция компонент с помощью каналов ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В предыдущем разделе обсуждались простые механизмы для перемещения транзакций между двумя процессами. В каждом, инициатор и цель были сильно  синхронизированы вызовом транзакции интерфейса. В этом разделе мы рассмотрим случай, когда инициатор и цель менее тесно связаны, это можно сделать с помощью канала, в этом случае FIFO, для управления синхронизацией между инициатором и целью, а не полагаться на взаимную синхронизацию самих компонент. Здесь мы имеем две компоненты, инициатор А и цель B, а также FIFO, соединяющий две компоненты.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:68.png]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В предыдущих примерах, одна компонента имела  порт, а другая  export.  Компонента с портом  обращается к  компоненте с  export. Здесь оба  А и В имеют порты. Вместо того, чтобы инициатор обращался к непосредственно к  цели, теперь у нас есть  инициатор  и цель,  которые обращаются к каналу FIFO. Канал обеспечивает функции, необходимые как инициатору, так и цели.&lt;br /&gt;
&lt;br /&gt;
Инициатор использует блокирующий put() для отправки транзакций на FIFO, и цель использует блокирующий get() для получения транзакций с FIFO. FIFO хранит транзакции и служит в качестве синхронизатора. Инициатор может помещать транзакции в FIFO, пока он не заполнится . Так как инициатора использует блокировку Put (), инициатор процесса будет блокироваться, когда FIFO заполнен.  Кроме того, цель использует блокирующий get() и будет блокироваться, когда FIFO является пустым. По сути, producer в этом примере, как producer в блокирующем  put  примере,  и consumer, как consumer в блокирующем get. FIFO  replaces цели и предоставляет задачи, необходимые для удовлетворения требований интерфейса, созданные портами  producer и consumer.&lt;br /&gt;
&lt;br /&gt;
Давайте посмотрим на код. Это первый пример, который использует OVM библиотеки. OVM библиотека включает в себя FIFO, называемый tlm_fifo, который является параметризованным классом с различными интерфейсами для поддержки блокирующих и неблокирующий операций. &lt;br /&gt;
&lt;br /&gt;
В этом случае producer выглядит как producer в блокирующем пут примере. Он имеет процесс, run(), который выполняется 10 раз, генерирует 10 случайных значений и &lt;br /&gt;
отправив их к цели через put_port.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;42 class producer extends ovm_component;&lt;br /&gt;
43&lt;br /&gt;
44 ovm_blocking_put_port#(int) put_port;&lt;br /&gt;
45&lt;br /&gt;
46 function new(string name, ovm_component p = null);&lt;br /&gt;
47 super.new(name,p);&lt;br /&gt;
48 put_port = new(“put_port”, this);&lt;br /&gt;
49 endfunction&lt;br /&gt;
50&lt;br /&gt;
51 task run();&lt;br /&gt;
52&lt;br /&gt;
53 int randval;&lt;br /&gt;
54 string s;&lt;br /&gt;
55&lt;br /&gt;
56 for(int i = 0; i &amp;lt; 10; i++)&lt;br /&gt;
57 begin&lt;br /&gt;
58 randval = $random % 100;&lt;br /&gt;
59 $sformat(s, “sending %4d”, randval);&lt;br /&gt;
60 ovm_report_info(“producer”, s);&lt;br /&gt;
61 put_port.put(randval);&lt;br /&gt;
62 end&lt;br /&gt;
63 global_stop_request(); // OK, we’re done now&lt;br /&gt;
64 endtask&lt;br /&gt;
65&lt;br /&gt;
66 endclass&lt;br /&gt;
file: 03_tlm/05_fifo/fifo.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Есть две вещи, на которые необходимо обратить внимание. Во-первых, компоненты, полученные из ovm_component, которая является базовым классом в библиотеке OVM, обеспечивает основные возможности для компонент. Это позволяет компонентам подключиться к иерархии названных компонент и обеспечивает процесс управления выполнения задачи. Выполнение задачи разветвляется при запуске и может быть приостановлена или возобновлена по желанию.&lt;br /&gt;
&lt;br /&gt;
Кроме того, необходимо заметить, как put_port объявлен. В наших простых примерах выше, мы создали наш собственный истинно виртуальный интерфейс для подключения инициатора  к цели. Библиотека OVM поставляет набор портов и экспорт объектов, которые являются обертками вокруг истинно виртуальных ссылок интерфейса. Порт и экспорт объектов, которые сами по себе именные компоненты, позволяют функции connect() создавать ассоциаций между  ports and exports.  Использование модели лучше по сравнению с использованием операторов присваивания. &lt;br /&gt;
&lt;br /&gt;
Consumer не сильно отличается от consumer в блокирующем get примере.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;71 class consumer extends ovm_component;&lt;br /&gt;
72&lt;br /&gt;
73 ovm_blocking_get_port#(int) get_port;&lt;br /&gt;
74&lt;br /&gt;
75 function new(string name, ovm_component p = null);&lt;br /&gt;
76 super.new(name,p);&lt;br /&gt;
77 get_port = new(“get_port”, this);&lt;br /&gt;
78 endfunction&lt;br /&gt;
79&lt;br /&gt;
80 task run();&lt;br /&gt;
81&lt;br /&gt;
82 int val;&lt;br /&gt;
83 string s;&lt;br /&gt;
84&lt;br /&gt;
85 forever&lt;br /&gt;
86 begin&lt;br /&gt;
87 get_port.get(val);&lt;br /&gt;
88 $sformat(s, “receiving %4d”, val);&lt;br /&gt;
89 ovm_report_info(“consumer”, s);&lt;br /&gt;
90 end&lt;br /&gt;
91&lt;br /&gt;
92 endtask&lt;br /&gt;
93&lt;br /&gt;
94 endclass&lt;br /&gt;
file: 03_tlm/05_fifo/fifo.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для подключения producer, consumer и FIFO, мы используем окружающую среду. Средой служит  вершина иерархии именованных компонент, и это организует построение иерархии и  выполнения  testbench.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;99 class env extends ovm_component;&lt;br /&gt;
100 producer p;&lt;br /&gt;
101 consumer c;&lt;br /&gt;
102 tlm_fifo #(int) f;&lt;br /&gt;
103&lt;br /&gt;
104 function new(string name, ovm_component parent = null);&lt;br /&gt;
105 super.new(name, parent);&lt;br /&gt;
106 endfunction&lt;br /&gt;
107&lt;br /&gt;
108 function void build();&lt;br /&gt;
109 p = new(“producer”, this);&lt;br /&gt;
110 c = new(“consumer”, this);&lt;br /&gt;
111 f = new(“fifo”, this);&lt;br /&gt;
112 endfunction&lt;br /&gt;
113&lt;br /&gt;
114 function void connect();&lt;br /&gt;
115 p.put_port.connect(f.blocking_put_export);&lt;br /&gt;
116 c.get_port.connect(f.blocking_get_export);&lt;br /&gt;
117 endfunction&lt;br /&gt;
118&lt;br /&gt;
119 endclass&lt;br /&gt;
file: 03_tlm/05_fifo/fifo.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция connect() создает связь между портами на producer и  consumer и соответствующего экспорта на FIFO.  Run ()  отвечает за контроль выполнения   testbench.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Формирование Transaction-Level подключений ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для формирования transaction-level подключения, необходимо указать три элемента: поток управления, поток данных, а также тип данных транзакций. Объявление подключения как порта или export определяет поток управления – поток управления движется(???) от порта к export. То есть, порт инициирует деятельность и export реагирует на нее. Интерфейс определяет поток данных. put интерфейс означает, что данных движутся от инициатора (левый борт) к цели ( сторона export), get интерфейс показывает, что данные идут от цели к инициатору, а transport или запрос-ответ, интерфейс указывает двунаправленность потоков данных.&lt;br /&gt;
&lt;br /&gt;
Мы объявляем put_port как порт, поэтому мы знаем, что устройство, в котором этот порт объявлен является инициатором. Тип  интерфейса - tlm_nonblocking_put_if &amp;lt;&amp;gt;, который является одним put интерфейсов, определенных в библиотеке TLM. Этот порт выход для объектов данных. Наконец, тип данных отправленного объекта является trans.&lt;br /&gt;
&lt;br /&gt;
Использование  OVM в SystemVerilog, объявление  портов и export охватывает эти три элемента. Вот пример:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;ovm_nonblocking_put_port #(trans) put_port;&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Суффикс тип объекта _port, указывает, что это порт. Exports используют суффикс _export.Тип интерфейса определяется по имени между префиксом ovm_ и _port или суффиксом _export. В этом случае, это имя nonblocking_put, который относится к tlm_nonblocking_put_if.&lt;br /&gt;
Мы рассмотрели producer и consumer, каждый из которых использует блокирующие задачи для получения и отправки транзакций. Блокирующие задачи находятся в FIFO, объект, который выступает в качестве посредника между двумя компонентами, в другом случае известный как канал. Канал передает данные между двумя компонентами, и  служит синхронизатором. Ввод FIFO между двумя компонентами для буферизации и синхронизации передачи является common idiom в TLM. &lt;br /&gt;
&lt;br /&gt;
== Заключение ==&lt;br /&gt;
&lt;br /&gt;
Put, get и transport являются основными средствами для синхронизации параллельных процессов и для обмена  информацией  между эти процессы на уровне transaction-level. Эти идеи широко используются в OVM  для построения transaction-level testbenches. В следующей главе мы будем углубляться  в ОВМ, чтобы показать, как строить произвольные иерархии компонент верификации, основанных на классах , связанных с помощью transaction-level интерфейсов.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%B2%D0%B5%D1%80%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D0%B8</id>
		<title>OVM/OVM методология/Основы верификации</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%B2%D0%B5%D1%80%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D0%B8"/>
				<updated>2013-04-09T13:45:47Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: /* Основы верификации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
== Основы верификации==&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Функционально проверить устройство означает, сравнить цель разработчика с полученным  поведением на  их эквивалентность. Мы считаем устройство проверенным, когда, к всеобщему удовлетворению, оно работает в соответствии с целью разработчика. Этот основной принцип часто теряется при использовании testbenches, среды контроля, отладки, моделирования , и все другие средства, применяемые в современных технологических проверках. Чтобы узнать, работает ли верно устройство, вы должны сравнить его с некоторым известным эталоном, который представляет цель разработчика. Каждый testbench имеет своего рода эталонную модель и средство для сравнения функциональность устройство с эталоном. &lt;br /&gt;
&lt;br /&gt;
	Когда мы говорим &amp;quot;устройство&amp;quot;, мы имеем в виду проверяемое устройство, часто называемое тестируемое устройство или DUT. Чтобы проверить, DUT, как правило, представлен  форме подходящей для производства, которое может быть перенесено на кремний с помощью автоматизированных и ручных средств. Мы различаем DUT от эскиза на обратной стороне салфетки или окончательного нанесенный на кристалл, ни один из которых не может быть проверен. Эталонная модель отражает цели разработчика, то есть то, что устройство должно делать.Эталон может принимать различные формы, такие как документ, описывающий работу DUT,  golden модель, которая содержит уникальный алгоритм, или assertions, которые представляют протокол.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:1.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для автоматизации сравнения поведения и эталона, оба должны быть представлены в форме, которую можно выполнить на компьютере с помощью некоторых программ, что делает  сравнение. &lt;br /&gt;
&lt;br /&gt;
==== Два вопроса====&lt;br /&gt;
&lt;br /&gt;
	Проверка устройства включает в себя два вопроса:Does it work? (Работает ли это устройство?) и Are we done? (Сделали ли мы?). Это основные, и некоторые сказали бы, очевидные вопросы. Тем не менее они мотивируют всю механику каждого проверочного потока. Первый вопрос - это Does it work? Этот вопрос исходит от основной идеи верификации. Он спрашивает, соответствует ли устройство эталону? Второй вопрос - Are we done? Он спрашивает, довольны ли мы сравнительным анализом полученной схемы и эталона для определения работает ли устройство согласно цели, если нет, то почему. Мы используем эти ценные вопросы для создания основы разработки эффективных testbenches.&lt;br /&gt;
&lt;br /&gt;
====Does it work?====&lt;br /&gt;
&lt;br /&gt;
	Does it work? Это не единственный вопрос, а категория вопросов, которые представляют природу DUT. Каждый проект будет иметь свой собственный набор Does-It-Work вопросов, чья роль заключается в определении правильного функционирования  устройства.  Функциональные вопросы определяют, ведет ли себя устройство должным образом в конкретных ситуациях. Мы получаем эти вопросы непосредственно из цели разработки устройства, и мы используем их, чтобы отразить цель проекта  в testbench.&lt;br /&gt;
&lt;br /&gt;
	Рассмотрим простой маршрутизатор пакетов в качестве примера. Это устройство маршрутизации пакетов от входа к одному из четырех портов вывода. Пакеты содержат адреса портов назначения и имеют различные длины полезной нагрузки. Каждый пакет имеет заголовок, трейлер, и два байта для циклического избыточного кода (CRC). Does-It-Work вопросы в этом случае должны быть следующие:&lt;br /&gt;
 &lt;br /&gt;
*пакет, поступающий на вход порта, адресованный на имя выходного порта 3, прибыл на порт  3? &lt;br /&gt;
*пакет длиной 16 пришел без изменений? &lt;br /&gt;
*CRC байты правильны, если полезная нагрузка [0f 73 a8 c2 3e 57 11 0a 88 FF 00 00 33 2b 4c 89]?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	Это простой пример полного набора вопросов. Для устройства даже такого простого как этот гипотетическое маршрутизатор, набор Does-It-Work вопросов может быть длинным. Чтобы построить план проверки и testbench, который поддерживает план, вы должны сначала перечислить все вопросы или показать как получит полный набор вопросов, а затем выберите те, которые являются интересными.&lt;br /&gt;
&lt;br /&gt;
	Продолжая пример маршрутизатора  пакетов,для того чтобы перечислить все Does-It-Work вопросы, вы можете создать таблицу:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:2.png]]&lt;br /&gt;
&lt;br /&gt;
Приведенная выше таблица содержит два вида вопросов, те,  на которые мы можем ответить сразу и те, которые мы можно разбить на более мелкие вопросы. Вопрос 1 из ряда вопросов, которые можно  представить в виде:&lt;br /&gt;
&lt;br /&gt;
[[Файл:3.png]]&lt;br /&gt;
[[Файл:4.png]]&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что мы формулируем все вопросы, так что на них можно ответить да или нет. В конце концов, схема  работает либо нет – она либо  готова для синтеза, размещения  и трассировке, либо нет. Если вы можете ответить на все вопросы утвердительно, тогда проект готов для следующего производственного шага.&lt;br /&gt;
При разработке набора does-it-work  вопросов, не забудьте сформулировать их  таким образом, чтобы на них можно было ответить да или нет. Ответ да положителен, то есть, ответить  да означает, что устройство работает правильно. Это будет сделать легче, чем пытаться отслеживать  на какие вопросы нужно ответить  да, и на какие следует ответить нет. Такой  вопрос как  Передает ли  маршрутизатор ошибочные пакеты?  требует ответа нет, чтобы  считаться успешным. Лучшей формулировкой вопроса будет: Отклоняет ли маршрутизатор ошибочные  пакеты? Вы должны сделать вопросы наиболее конкретными,  даже если лучшее звучание будет:  Когда ошибочный пакет попадает на входной порт,  маршрутизатор обнаруживает его, подает сигнал ошибки и отклоняет этот пакет? Имейте в виду, что более конкретные вопросы более подробно описывают устройство. Ваш testbench должен дать ответ да или нет.&lt;br /&gt;
Тщательно сформулированный да или нет вопрос содержит свои собственные критерии успеха. Он сообщает, что получает ответа да. Такой  вопрос  как, Средняя задержка менее 27 тактов? содержит метрику, 27 тактов, и форму сравнения, меньше. Если вопрос (неправильно) сформулирован, например, Какова средняя задержка прохождения пакетов через маршрутизатор? мы не будем знать, что считается приемлемым. Чтобы ответить на ваш вопрос, вы в первую очередь должны быть в состоянии определить среднее время задержки. Только при правильной постановке вопроса мы знаем, как сделать сравнение, чтобы определить, является ли результат  правильным. Метрика сама по себе не говорит нам о том,   функционирует ли схема как задумано. Когда мы сравниваем измеренное значение со значением в  спецификации, 27 тактов в этом примере, мы можем определить, работает ли схема.&lt;br /&gt;
Как это часто бывает, это не практично  перечислять каждый does-it-work вопрос. Чтобы убедиться, что каждое слово в памяти 1 Мб можно записать и читать,  непрактично и не необходимо записать один миллион вопросов. Вместо этого, вопрос-генератор,  вопрос, который генерирует многие другие, занимает место  миллиона отдельных вопросов. Может каждый  миллион слов в памяти быть успешно записан и считан? -является вопросом-генератором.&lt;br /&gt;
Другие вопросы могут сами по себе  представлять  классы вопросов. Вопрос 3, Является ли вычисление CRC корректным для каждого пакета? является примером такого вопроса. Тестирование CRC вычисления требует ряда тщательно продуманных тестов, для  определения является ли вычисления CRC корректным во всех случаях. &lt;br /&gt;
&lt;br /&gt;
==== Are We Done?====&lt;br /&gt;
&lt;br /&gt;
Для определения ответа на вопрос  Are we done?, мы должны знать, что мы ответили на достаточное количество does-it-work  вопросов, чтобы  утверждать, что у нас есть проверенная схема. Мы начинаем эту задачу путем разделения на приоритеты всех does-it-work  вопросов по двум направлениям:&lt;br /&gt;
&lt;br /&gt;
[[Файл:5.png]]&lt;br /&gt;
&lt;br /&gt;
Искусство создания testbench требует, чтобы на начальном этапе, мы определили множество вопросов и отсортировали их в соответствии с ценностью возвращаемой информации с точки зрения проверки схемы. Следующий шаг заключается в создании механизма, который  будет отвечать на вопросы и определять  на какие из них были даны ответы (и на какие нет).&lt;br /&gt;
&lt;br /&gt;
Are-we-done вопросы также называется вопросами функционального покрытия, вопросы, которые устанавливают  все ли состояния охватывают тесты с точки зрения  функций схемы. Как  и  does-it-work  вопросы, мы можем также разбить вопросы функционального охвата на более подробные вопросы. И как вопросы функциональной корректности, вопросы функционального охвата  должны также иметь ответы да или нет. Приведенный ниже список включает в себя примеры вопросы функционального охвата:&lt;br /&gt;
&lt;br /&gt;
•	Выполняется ли каждая команда процессора, по крайней мере, один раз?&lt;br /&gt;
&lt;br /&gt;
•	Проходит, по крайней мере, один пакет  от каждого входного порта до каждого выходного порта?&lt;br /&gt;
&lt;br /&gt;
•	Можно ли обратиться к памяти  с набором адресов, которые содержат каждый бит адреса как 1 и затем каждый  бит адреса 0, не включая 0xffffffff и 0x00000000?(?????????)&lt;br /&gt;
&lt;br /&gt;
Другим мнением об этих вопросах является то, о чем они спрашивают, Получен ли утвердительный ответ на необходимый does-it-work  вопрос? Когда мы думаем о функциональном охвате с этой точки зрения, то имеем виду покрытие множества does-it-work  вопросов. Кроме того, вопросы охвата распознают метрику и порог для сравнения. Покрытие будет  (то есть, охват вопрос можно ответить, да), когда метрика достигает порога.&lt;br /&gt;
&lt;br /&gt;
Таким образом, искусство построения testbench начинается с плана тестирования.  План тестирования начинается с тщательно продуманным набором does-it-work и Are-we-done вопросов.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Процесс(проверки) с двумя циклами (итерациями)====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Процесс ответа на does-it-work и Are-we-done вопросы может быть описан  простой схемой, как показано на рисунке 1-2. Все обусловлено функциональной спецификацией на проектирование. Из функциональной спецификации, вы можете получить саму схему и план верификации.  План верификации запускает testbench.&lt;br /&gt;
&lt;br /&gt;
Поток состоит из двух циклов, does-it-work цикла и Are-we-done цикл. Оба цикла начинаются с моделирования. Моделирование тестирует  схему при помощи  testbench и генерирует информацию, которую мы используем для ответа на вопросы. Сначала мы спрашиваем, Это работает? Если ответ нет, то необходимо отладить проект. Процесс отладки схемы может привести к изменениям  в реализации схемы.&lt;br /&gt;
Как только схема работает, то пора ответить на вопрос, Are-we-done? Мы ответим на этот вопрос, собирая информацию охвата и сравнения его с порогом, указанным в тестовом плане. Если мы не достигнем  порогов, то ответ нет, и мы должны изменить testbench для увеличения охвата. Тогда мы моделируем снова.&lt;br /&gt;
&lt;br /&gt;
Изменение testbench или воздействия может вызвать другие скрытые ошибки в схеме. Последующие итерации по циклу могут заставить нас вернуться к does-it-work циклу снова, чтобы исправить новые ошибки, которые появятся. Как вы можете видеть, полный процесс проверки колеблется вперед и назад между does-it-work и Are-we-done циклами, пока мы не можем утвердительно ответить на все вопросы  обоих категорий.&lt;br /&gt;
&lt;br /&gt;
[[Файл:6.png]]&lt;br /&gt;
&lt;br /&gt;
В идеальном мире, схема не имеет ошибок и с покрытием всегда полное, так что вам только надо пройти по каждому циклу один раз, чтобы получить ответы да на оба вопроса. В реальном мире, это может занять много итераций для достижения двух ответов да. Одна из целей хорошего процесса проверки свести к минимуму количество итераций для выполнения верификации проекта в кратчайшие сроки с использованием наименьшего количества ресурсов.&lt;br /&gt;
&lt;br /&gt;
== Первый Testbench==&lt;br /&gt;
&lt;br /&gt;
Давайте начнем с проверки одного из самых фундаментальных цифровых электронных устройств, схемы логического И. Логическое И вычисляет логическое умножение   входов. Функция этого устройства является тривиальной, а на практике, вряд ли должна иметь свой собственный  testbench. Так как она тривиальна, мы можем использовать ее для иллюстрации некоторых основных принципов проверки без необходимости вникать в более сложную схему.&lt;br /&gt;
&lt;br /&gt;
Рисунок 1-3 показывает условное обозначение  логического элемента умножения с двумя входами. Логический элемент имеет два входа, А и В, и один выход Y. Устройство вычисляет логическое умножение  А и В и помещает результат в Y.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:7.png]]&lt;br /&gt;
&lt;br /&gt;
Следующая таблица истинности описывает функцию устройства.&lt;br /&gt;
&lt;br /&gt;
[[Файл:8.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Таблица истинности является исчерпывающей: она содержит все возможные входы А и В и поэтому, все возможные значения для выхода Y.&lt;br /&gt;
Наша задача состоит в том, чтобы доказать, что наша схема,  логического И, работает правильно. Чтобы убедиться, что он действительно выполняет функцию логического умножения  правильно, нам в первую очередь необходимо перечислим вопросы. Таблица истинности поможет нам создать множество вопросов, необходимых для проверки. Каждая строка таблицы содержит вход для А и В и ожидаемый выход для Y. Так как таблица является исчерпывающей, наш вопрос-генератор будет:  Для каждой ли строки в таблице истинности, когда мы получаем значения А и B, определенные в этой строке,  устройство вычисляет ожидаемый результат для Y? Для ответа на Are-we-done вопрос,  мы определяем, проверили ли  мы  каждую строку в таблице истинности, и получил ли утвердительный ответ does-itwork вопрос для этой строки. Наш Are-we-done вопрос- это Работает на всех наборах?&lt;br /&gt;
Для автоматизации ответов как на does-itwork  та  и на Are-we-done  вопросы,  нам нужна следующая атрибутика:&lt;br /&gt;
&lt;br /&gt;
•	Модель, которая представляет DUT (в данном случае, логическое И)&lt;br /&gt;
&lt;br /&gt;
•	Концепция конструкции в форме, которую мы можем кодифицировать  как эталонную модель&lt;br /&gt;
&lt;br /&gt;
•	Некоторое воздействие для проверки схемы&lt;br /&gt;
&lt;br /&gt;
•	Способ сравнить результат с концепцией конструкции&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:9.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В то время как наш маленький testbench прост, он содержит ключевые элементы, которые можно найти в большинстве testbenches любого уровня сложности. Ключевыми элементами являются следующие:&lt;br /&gt;
&lt;br /&gt;
•	DUT&lt;br /&gt;
&lt;br /&gt;
•	Stimulus генератор, который генерирует последовательность сигналов для DUT&lt;br /&gt;
&lt;br /&gt;
•	Scoreboard, которое изображает  эталонную модель&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Табло следит за входами и выходом тестируемого устройства, выполняет же функции, что DUT за исключением более высокого уровня абстракции, и определяет, совпадает ли DUT и эталонная модель. Scoreboard помогает нам ответить на does-itwork  вопросы.&lt;br /&gt;
&lt;br /&gt;
=== DUT===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DUT- это наше логическое И с двумя входами. Мы реализуем логическое И, как модуль с двумя входами А и В, и одним выходом Y.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;44 module and2 (&lt;br /&gt;
45 output bit Y,&lt;br /&gt;
46 input A, B);&lt;br /&gt;
47&lt;br /&gt;
48 initial Y = A &amp;amp; B;&lt;br /&gt;
49&lt;br /&gt;
50 always @* Y = #1 A &amp;amp; B;&lt;br /&gt;
51 endmodule&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stimulus генератор в этом примере создает заданные сигналы. Каждое новое значение вычисляются в определенном порядке. Позже, мы будем смотреть на  генератор случайных сигналов, которые, как следует из их названия, генерирует случайные значения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;81 module stimulus(output bit A, B);&lt;br /&gt;
82&lt;br /&gt;
83 bit [1:0] stimulus_count = 0;&lt;br /&gt;
84&lt;br /&gt;
85 always&lt;br /&gt;
86 #10 {A,B} = stimulus_count++;&lt;br /&gt;
87&lt;br /&gt;
88 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Цель Stimulus генератор - получение значений на вход  DUT. Сигналы, двухбитная величина, содержащая значение для А и B. После того как он увеличивается при каждой последующей итерации, младший бит посылает на А, а старший бит на B.&lt;br /&gt;
&lt;br /&gt;
=== Scoreboard===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Scoreboard несет ответственность за ответы на  does-it-work  вопросы. Оно следит за работой DUT и сообщает работает ли оно корректно.  Обратите внимание на то, что структура Scoreboard поразительно похожа на структуру DUT. Это имеет смысл, когда вы считаете, что назначением Scoreboard является отслеживание активности DUT  и определение, работает ли DUT  как  ожидалось.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;59 module scoreboard(input bit Y, A, B);&lt;br /&gt;
60&lt;br /&gt;
61 reg Y_sb, truth_table[2][2];&lt;br /&gt;
62&lt;br /&gt;
63 initial begin&lt;br /&gt;
64 truth_table[0][0] = 0;&lt;br /&gt;
65 truth_table[0][1] = 0;&lt;br /&gt;
66 truth_table[1][0] = 0;&lt;br /&gt;
67 truth_table[1][1] = 1;&lt;br /&gt;
68 end&lt;br /&gt;
69&lt;br /&gt;
70 always @(A or B) begin&lt;br /&gt;
71 Y_sb = truth_table[A][B];&lt;br /&gt;
72 #2 $display(“@%4t - %b%b : Y_sb=%b, Y=%b (%0s)”,&lt;br /&gt;
73 $time, A, B, Y_sb, Y,&lt;br /&gt;
74 ((Y_sb == Y) ? “Match” : “Mis-match”));&lt;br /&gt;
75 end&lt;br /&gt;
76 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Scoreboard отображает пины всех входов. На Scoreboard не вызывает воздействия на  схему. Оно пассивно наблюдает за входами и выходами тестируемого устройства. &lt;br /&gt;
&lt;br /&gt;
Модуль верхнего уровня, как показано ниже, является полностью структурированным, он содержит  экземпляр тестируемого устройства, Scoreboard и Stimulus генератор,  с кодом, необходимым для их подключения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;93 module top;&lt;br /&gt;
94 stimulus s(A, B);&lt;br /&gt;
95 and2 a(Y, A, B);&lt;br /&gt;
96 scoreboard sb(Y, A, B);&lt;br /&gt;
97&lt;br /&gt;
98 initial&lt;br /&gt;
99 #100 $finish(2);&lt;br /&gt;
100 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Когда мы запускаем моделирования для нескольких итераций, вот что мы получаем:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:14.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Каждое сообщение состоит из двух частей. В первой части показаны применяемые сигналы. Вторая часть показывает результаты проверки Scoreboard, которое сравнивает соответствие DUT с  необходимыми результатами. Мы используем двоеточие для разделения двух частей. &lt;br /&gt;
&lt;br /&gt;
Этот простой  testbench  демонстрирует использование Stimulus генератор и Scoreboard, которое служит в качестве эталона. Несмотря на то, DUT – логическое И, все элементы полного testbench  присутствуют.&lt;br /&gt;
&lt;br /&gt;
== Второй Testbench==&lt;br /&gt;
&lt;br /&gt;
В предыдущий пример показывает элементарные понятия проверки с использованием  комбинационного дизайна.  Комбинационные конструкций,  по  природе, не поддерживают никаких статических данных. В нашем втором примере мы рассмотрим чуть более сложную конструкцию, которая сохраняет данные о состояниях и использует  тактовый сигнал  для перехода  между состояниями.&lt;br /&gt;
&lt;br /&gt;
Проблема верификации, связанная с синхронными (последовательными) схемами,  иная чем у  комбинационных конструкций. Все, что вам нужно знать о комбинационной схеме доступно на ее входах. Эталонная модель для  комбинационного устройства просто должна  вычислять F (X), где X представляет  входы  устройства и F – это реализуемая функция. Выходы же на последовательном устройстве  зависят от  входных сигналов и его внутреннего состояния. Дальнейшее  вычисление может изменить внутреннее состояние. Scoreboard должно отслеживать  внутреннее состояние тестируемого устройства и сравнить выходные данные.&lt;br /&gt;
&lt;br /&gt;
Комбинационное устройство может быть полностью проверено при подаче всех возможных комбинаций сигналов на входы. Для устройства  N входами, мы должны применить в общей сложности 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;  входных комбинаций. Число 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;  может быть большим, но вычисление такого количества  входов достаточно простое. Нам  просто необходимо иметь N-разрядный счетчик и посылать каждое значение счетчика на входы устройства. &lt;br /&gt;
Для последовательного устройства, понятие &amp;quot;done&amp;quot; должно распространяться и на покрытие не только  всевозможных сигналов на  входах, но и на число всевозможных внутренних состояний. Для устройства с n входами и m внутренних состояний, вы должны покрыть (2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt; входов) * (2&amp;lt;sup&amp;gt;m&amp;lt;/sup&amp;gt; внутренних состояний),   2&amp;lt;sup&amp;gt;n+m&amp;lt;/sup&amp;gt; – число всевозможных комбинаций внутренних состояний и входов. Для устройства с 64 входами  и одним 32-битный внутренним регистром, количество  комбинаций  2&amp;lt;sup&amp;gt;96&amp;lt;/sup&amp;gt; -очень большое число, не так ли!&lt;br /&gt;
&lt;br /&gt;
Даже для очень большого числа комбинаций, проверка  не была бы слишком трудной, если  бы было возможно  просто увеличивать счетчик для получения всех комбинаций, как мы делаем с комбинационными устройствами. К сожалению, это не представляется возможным. Внутреннее состояние напрямую не доступны извне устройства. Оно может быть изменено только путем манипулированием входов. И теперь проблема заключается в том, как добраться до всех состояний в устройстве только через входы. Это сложная проблема, которая требует глубокого понимания устройства, для генерации последовательности входов для получения всех состояний.&lt;br /&gt;
&lt;br /&gt;
Поскольку трудно получить  все состояния, становится  очевидным вопрос, Можем ли мы упростить задачу путем сокращения числа состояний, которые мы должны получить, чтобы показать, что устройство работает правильно? Ответ: да. Теперь возникает другой вопрос, Как мы решим, какие состояния можно пропустить?&lt;br /&gt;
&lt;br /&gt;
Эта тема является сложной. Тем не менее, мы можем дать интуитивный ответ на вопрос. Состояния, которые могут быть отмечены как недоступные, при использовании формальной проверки или других методов, не должны быть покрыты. Проектировщик должен рассмотреть вопрос об упрощении схемы для удаления недоступных состояний, так как они не представляют никакой ценности. Состояния, которые имеют низкую вероятность быть достигнутыми, также могут быть исключены из плана проверки. Определение порогового значения вероятности и вероятностей состояний такое же искусство, как наука. Она включает в себя понимание того, как устройство используется и какие входы необходимы (по сравнению с которые возможны).&lt;br /&gt;
&lt;br /&gt;
Это также позволяет устранить состояния, которые являются функционально эквивалентными. Рассмотрим устройство пакет коммуникаций. В теории, каждое возможное значение полезной нагрузки пакета представляет собой различное состояние (или множества состояний), которое принимает схема, и оно должно быть покрыто во время проверки. Однако, это, вероятно, не будет преувеличением считать, что произвольное ненулевое значение, для всех намерений и целей, эквивалентно. Конечно, могут быть некоторые интересные случаи, которые должны быть проверены, как все нули, все единицы, конкретные значения, которые могли бы бросить алгоритмы коррекции ошибок, и так далее. Изменения данных становятся интереснее, когда они влияют на поток управления.&lt;br /&gt;
&lt;br /&gt;
В общем, это более важно, чтобы охватить управляющие состояния, чем состояние данных. Простым способом уменьшить число состояний, необходимых для покрытия схемы является разделения данных и управления. Для конкретного пути контроля, данные могут быть произвольными. По некоторых данных, вы можете захотеть изменить путь управления. Например, в ALU  вы можете разделить функции контроля записи данных  и получения данных из регистров и установление арифметической операции для выполнения из результатов конкретные арифметических операций. Использование направленного управления, вы можете сделать данные случайные или  рассмотреть данные в специфичных случаях, таких как деление на 0 или умножение на 1.&lt;br /&gt;
&lt;br /&gt;
Для сложных последовательных конструкций, определения того, какие состояния должны быть покрыты(и какие не должны) и, как получить  эти состояния с минимальными усилиями является проблемой.&lt;br /&gt;
&lt;br /&gt;
=== 3-х разрядный счетчик===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Схема, приведенная на рисунке 1-5, представляет собой 3-разрядный счетчик с асинхронным сбросом. Каждый раз, когда тактовый сигнал высокий, счетчик увеличивается. Схема состоит из трех переключательных триггеров, каждый из которых поддерживает один бит счета(??). Триггеры связаны с некоторой комбинационной логикой для формирования счетчика . Каждый триггер переключается, когда на входе T сигнал высокой. При низком сигнале на Т , триггер сохраняет свое текущее состояние. Когда активный низкий сброса установлен в 0, триггер переходит в состояние 0.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:15.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Код для счетчика содержится в двух модулях. Одним из них является простой переключательный триггер, а другой подключает триггера с необходимой логикой для  формирования счетчика. Первый пример показывает  переключательный триггер.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;36 module toggle_ff (output bit q, input t, rst_n, clk);&lt;br /&gt;
37&lt;br /&gt;
38 always @ (posedge clk or negedge rst_n)&lt;br /&gt;
39 if (!rst_n) q &amp;lt;= ‘0;&lt;br /&gt;
40 else if (t) q &amp;lt;= ~q;&lt;br /&gt;
41&lt;br /&gt;
42 endmodule&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Счетчик состоит из трех переключательных триггеров и логического элемента И.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;47 module counter (output [2:0] q, input rst_n, clk);&lt;br /&gt;
48&lt;br /&gt;
49 toggle_ff ff0 (q[0], 1’b1, rst_n, clk);&lt;br /&gt;
50 toggle_ff ff1 (q[1], q[0], rst_n, clk);&lt;br /&gt;
51 toggle_ff ff2 (q[2], t2, rst_n, clk);&lt;br /&gt;
52 and a1 (t2, q[0], q[1]);&lt;br /&gt;
53&lt;br /&gt;
54 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Схема проста, но имеет характеристики, которые являются общими с реальными проектами и которые требуют  внимания для надлежащей проверки дизайна. Основными характеристиками является то, что конструкция работает по тактовому сигналу, и что она сохраняет внутреннее состояние. Логический элемент  И  из предыдущего примера не сохраняет никаких состояний. Вся информация о том, что  делает схема, находится на ее входах. В конструкциях с внутренними данными, что это не так.&lt;br /&gt;
&lt;br /&gt;
Эта разница отражается на схеме нашего табло. Рисунок 1-6 показывает схему  testbench  для 3х разрядного счетчика.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:18.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Во многих отношениях, testbench   для 3-разрядного счетчика очень похож на testbench для логического элемента И.  Оба имеют Scoreboard, роль которого состоит в наблюдении, что делает схема, и определить, выполняется ли операция правильно. Оба имеют устройства для  DUT. Тем не менее, мы выполняем операции по-разному для этих конструкций. Мы используем Stimulus генератор для логического элемента И, но мы используем контроллер для 3-х разрядного счетчика. 3-х разрядный счетчик автономное устройство. Пока он подключен к тактовому сигналу, он будет продолжать считать. Таким образом, нам не нужен Stimulus генератор, который мы использовали для логического И. Вместо этого, контроллер управляет работой тестируемого устройства и  testbench. Контроллер обеспечивает начальный сброс, так что отсчет начинается с известного значения. Он также останавливает моделирование в соответствующее время.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;93 module control(output bit rst_n, input clk);&lt;br /&gt;
94&lt;br /&gt;
95 initial begin&lt;br /&gt;
96 rst_n &amp;lt;= 0;&lt;br /&gt;
97 @(posedge clk);&lt;br /&gt;
98 @(negedge clk);&lt;br /&gt;
99 rst_n &amp;lt;= 1;&lt;br /&gt;
100 repeat (10) @(posedge clk);&lt;br /&gt;
101 $finish;&lt;br /&gt;
102 end&lt;br /&gt;
103&lt;br /&gt;
104 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Табло должны отслеживать внутреннее состояние DUT. Это делается с помощью переменной count. Как и DUT, когда reset активирован, счетчик устанавливается в 0. Каждый тактовый цикл count увеличивается на единицу, и новое значение  сравнивается с count, поступающего с DUT.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;71 module scoreboard (input [2:0] q, input rst_n, clk );&lt;br /&gt;
72&lt;br /&gt;
73 int count;&lt;br /&gt;
74&lt;br /&gt;
75 always @(posedge clk or negedge rst_n) begin&lt;br /&gt;
76 if(!rst_n) count &amp;lt;= 0;&lt;br /&gt;
77 else begin&lt;br /&gt;
78 if (count == q)&lt;br /&gt;
79 $display(“time =%4t q = %3b count = %0d match!”,&lt;br /&gt;
80 $time, q, count);&lt;br /&gt;
81 else&lt;br /&gt;
82 $display(“time =%4t q = %3b count = %0d &amp;lt;-- no&lt;br /&gt;
match”,&lt;br /&gt;
83 $time, q, count);&lt;br /&gt;
84 count &amp;lt;= (count + 1) % 8;&lt;br /&gt;
85 end&lt;br /&gt;
86 end&lt;br /&gt;
87&lt;br /&gt;
88 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Табло имеет модель счетчика высокого уровня. Он использует целую переменную и оператор сложения (+) , чтобы сформировать счетчик вместо триггеров и элемента И. Каждый раз, при изменении тактового импульса, он увеличивает свой счет, так же как и RTL счетчика. Он также проверяет, соответствует ли  ее внутренний счетчик  выходу счетчика DUT.&lt;br /&gt;
&lt;br /&gt;
Для полноты, пример показывает тактовый генератор и модуль топ-уровня. Тактовый генератор просто инициализирует тактовый сигнал 0, а затем переключает его каждые 5 нс.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;&lt;br /&gt;
59 module clkgen(output bit clk);&lt;br /&gt;
60&lt;br /&gt;
61 initial begin&lt;br /&gt;
62 clk &amp;lt;= 0;&lt;br /&gt;
63 forever #5 clk = ~clk;&lt;br /&gt;
64 end&lt;br /&gt;
65&lt;br /&gt;
66 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Модуля верхнего уровня является типичным для большинства testbenches. Он соединяет DUT  и компоненты testbench.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;109 module top;&lt;br /&gt;
110&lt;br /&gt;
111 wire [2:0] q;&lt;br /&gt;
112&lt;br /&gt;
113 clkgen ckgn (clk);&lt;br /&gt;
114 counter cntr (q, rst_n, clk);&lt;br /&gt;
115 control ctrl (rst_n, clk);&lt;br /&gt;
116 scoreboard score (q, rst_n, clk);&lt;br /&gt;
117&lt;br /&gt;
118 endmodule&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
Мы показали простой testbench, который содержит элементы, используемые в гораздо более сложных testbenches. Последовательные схемы, которые поддерживают внутреннее состояние, требуют табло, которое работает параллельно с DUT. Табло выполняет те же вычисления что и DUT, но на более высоком уровне абстракции. Табло также сравнивает свои вычисления с вычислениями, полученными от DUT.&lt;br /&gt;
&lt;br /&gt;
== Многоуровневая организация Testbenches==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Так  как схема представляет собой совокупность  компонент проекта,  testbench представляет собой  совокупность компонент верификации. OVM определяет верификацию компонент, их структуру,интерфейс. В этом разделе описываются  OVM компоненты.&lt;br /&gt;
&lt;br /&gt;
OVM testbenches организованы в виде уровней. Самый нижний слой -устройство RTL с pin-level интерфейсом (с интерфейсом на уровне выводов). Выше находится уровень транзакторов (посредник), устройств, которые преобразуют из уровня транзакций в pin-level. Все компоненты, находящиеся на этом уровне, являются компонентами уровня транзакций. Диаграмма, приведенная ниже, иллюстрирует уровневую организацию тестовых программ. Блок слева — название уровня, блок справа — перечень типов компонентов, входящих в него. Вертикальные стрелки показывают, какие уровни взаимодействуют на прямую. Например, управляющий уровень взаимодействует с уровнями анализа, операций, транзакторов, но с DUT напрямую не может.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:23.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Вы также можете рассмотреть testbench как концентрическую организацию компонент. Внутреннему кольцу соответствует нижний уровень, а внешнему кольцу - верхний уровень. Для некоторых будет проще понять отношения между уровнями, используя диаграмму в виде списка связей.&lt;br /&gt;
&lt;br /&gt;
[[Файл:24.png]]&lt;br /&gt;
&lt;br /&gt;
=== Transactors(Посредники)===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Роль посредников в testbench состоит в  преобразование потока транзакций в pin-level activity или наоборот. Транзакторы характеризуются наличием, по крайней  мере, одного pin-level интерфейса и хотя бы одного transaction-level интерфейса. Транзакторы могут принимать большое разнообразие форм, цветов и стилей. Мы сосредоточимся на мониторах, драйверах и передатчиках.&lt;br /&gt;
&lt;br /&gt;
'''Монитор'''. Монитор, как следует из названия, следит за шиной данных. Он следит за выводами и преобразует их в поток транзакций. Мониторы пассивные - это означает, что они не влияют на операции в DUT.&lt;br /&gt;
&lt;br /&gt;
'''Драйвер'''. Драйвер преобразует поток транзакций (или последовательность элементов) в pin-level activity.&lt;br /&gt;
&lt;br /&gt;
'''Передатчик'''.  Передатчик очень похож на драйвер, но он  реагирует на активность на выходе, а не на входную активность.&lt;br /&gt;
&lt;br /&gt;
===Операционные компоненты===&lt;br /&gt;
&lt;br /&gt;
Операционные компоненты – это набор компонент, которые обеспечивают все необходимые операции в DUT. Операционные компоненты отвечают за генерацию трафика для DUT. Все эти компоненты относятся к уровню транзакций и имеют только интерфейс уровня транзакций. Существуют различные варианты формирования сигнала, которые варьируются в зависимости от верифицируемого устройства. Мы рассмотрим три основных вида операционных компонентов: stimulus generators, masters, и slaves.&lt;br /&gt;
&lt;br /&gt;
'''Stimulus генераторы'''.  Генератор сигналов  создает поток транзакций для проверки DUT. Stimulus генераторы могут быть случайными, заданными или заданными случайно; они могут запускаться самостоятельно или контролируемо; и они могут быть независимыми или синхронизированными. Простейший stimulus генератор рандомизирует содержимое объекта запроса и посылает этот объект на драйвер. OVM также предоставляет модульные, динамические средства для построения сложных stimulus, называемыми последовательностями.&lt;br /&gt;
&lt;br /&gt;
'''Главное устройство'''.  Главное устройство - двунаправленная компонента, которая посылает запросы и принимает ответы.  Главное устройство инициирует активность. Как и Stimulus генераторы, оно может передавать отдельные рандомизированные транзакции или последовательности заданных или случайно заданных транзакций.  Главное устройство может использовать ответы для определения последующих действий.  Главное устройство также может быть реализовано как последовательность.&lt;br /&gt;
&lt;br /&gt;
'''Подчиненное устройство'''. Подчиненное устройство, как и главное устройство, является двунаправленной компонентой. Оно реагирует на запросы и возвращает ответы (в отличие от главного устройства, которое отправляют запросы, и принимает ответы).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:25.png]]&lt;br /&gt;
&lt;br /&gt;
=== Компоненты анализа===&lt;br /&gt;
&lt;br /&gt;
Компоненты анализа получают информацию о том, что происходит в testbench, и используют эту информацию, чтобы сделать некоторые заключения о правильности и полноты теста. Два наиболее распространенных вида анализа - это scoreboards и coverage collectors.&lt;br /&gt;
&lt;br /&gt;
'''Scoreboards'''. Табло используются для определения правильности DUT, чтобы ответить на does-it-work вопросы. Scoreboards перехватывает информацию, входящую и выходящую из DUT и определяют правильно ли DUT отвечает на данный stimulus.&lt;br /&gt;
&lt;br /&gt;
'''Coverage Collector'''. Coverage Collector используются для подсчета. Они перехватывают поток  транзакций, и считает транзакции или различные аспекты транзакций. Цель состоит в том, чтобы определить завершенность верификации, отвечая на are-we-done вопросы. Определенные параметры, которые coverage collectors считает, зависит от схемы и специфики тестов. Общие вещи, которые coverage collectors включают в себя: необработанные транзакции, транзакции, которые происходит в определенном сегменте адресного пространства, и протокол ошибок. Список неограничен.&lt;br /&gt;
&lt;br /&gt;
Coverage Collector могут также выполнять вычисления как часть полноты верификации. Например, coverage collectors может содержать скользящее среднее и среднее квадратичное отклонение данных, которые отслеживаются. Или они могут хранить отношения ошибок к выполнимым операциям.&lt;br /&gt;
&lt;br /&gt;
=== Контроллер===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Контроллеры формируют основной поток теста, и управляют работой. Как правило, контроллеры получают информацию от Scoreboard и coverage collectors, и  отправляют информацию  компонентам окружающей среды. &lt;br /&gt;
Например, контроллер может запустить Stimulus генератор, а затем ждать сигнала от coverage collectors, чтобы уведомить его, когда тест будет завершен. Контроллер, в свою очередь, останавливает Stimulus генератор. Более сложные вариации на эту тему возможны. В пример возможные конфигурации, контроллер инициализирует Stimulus генератор начальным набором ограничений и запускает его. При достижении определенного соотношения  типов пакетов, coverage collectors сигнализирует контроллеру. Вместо того, чтобы остановить Stimulus генератор, контроллер может отправить ему новый набор ограничений.&lt;br /&gt;
&lt;br /&gt;
== Два домена (Две области)==&lt;br /&gt;
&lt;br /&gt;
Мы можем разделить множество компонент в testbench на отдельные  области. Оперативная область - множество компонент, в том числе DUT, которые управляют DUT. Это Stimulus генератор, шина функциональной модели (BFM), и аналогичные компоненты, которые генерируют сигналы и возвращают ответы, которые управляют моделированием. DUT, ответчик, и драйвер операций, наряду с компонентами окружающей среды, которые непосредственно обращаются или отвечают драйверам и передатчикам ответа, включены в оперативную область. Остальные компоненты testbench –монитор транзакций, scoreboard, coverage collectors, и контроллера включены в область анализа. Это компоненты, которые собирают информацию из оперативной области.&lt;br /&gt;
&lt;br /&gt;
Данные должны быть перемещены из оперативной области в область анализа таким образом, чтобы не мешать работе DUT и сохранить оптимальный выбор событий. Это достигается с помощью специального интерфейса связи называемого портом анализа.  Порт анализа представляют собой особый вид  порта транзакций, в котором издатель передает данные одному или нескольким получателям. Издатель оповещает всех получателей, когда новые данные готовы.&lt;br /&gt;
&lt;br /&gt;
Одной из ключевых особенностей порта анализа  является то, что он имеет единственную функцию, write(). Очередь(fifo) анализа, каналы, используемые для подключения к порту анализа для анализа компонентов, неограниченные. Это гарантирует, что издатель не блокирует, а  передает свои данные в очередь (fifo) анализа в точно такой же дельта-цикл, в котором они были созданы.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:26.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как правило, оперативная и область анализа связаны портами анализа и интерфейсами управления и конфигурации. Порты анализа - шлюз, через который выходят данные относящиеся к операциям DUT. Эти данные могут включать шину транзакций, коммуникационные пакеты и информацию о состоянии (успех или ошибка конкретных операций). Компоненты  области анализа анализируют данные и принимают решения. Результатами этих решений могут быть сообщения оперативной области, используя  интерфейсы управления и конфигурирования. Интерфейсы управления и конфигурирования  могут быть использованы для запуска и остановки Stimulus генератор, изменения ограничений, изменения процента ошибок, или манипулирование другими параметрами, влияющих на работу testbench.&lt;br /&gt;
&lt;br /&gt;
== Заключение==&lt;br /&gt;
&lt;br /&gt;
В этой главе мы рассмотрели, как структурировать весь процесс проверки. Этот процесс основывается на двух фундаментальных вопросах, Does it work? и Are  we done? Простые примеры показали, как построить testbench  для аппаратного ответа на эти вопросы такими устройствами как генератор сигналов и табло. Далее будет показано, как применять на уровне транзакций методы моделирования для создания практических, масштабируемых, многоразовых  компонент testbench, которые отвечают на эти вопросы и показывают, как подключить их  testbenches.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM</id>
		<title>OVM/OVM методология/Механика OVM</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM"/>
				<updated>2013-04-01T10:35:31Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= Механика  OVM =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Библиотека OVM предоставляет много возможностей для построения testbenches. В этой главе мы ознакомимся с наиболее важными возможностями, которые вы будете использовать  почти во всех ваши testbenches.&lt;br /&gt;
&lt;br /&gt;
== Компоненты и иерархия ==&lt;br /&gt;
&lt;br /&gt;
Первичная структура для создания элементов testbench  является компонента. Компонента  в OVM – это аналогия  модулю в Verilog. Компонента OVM сконструирована на основе класса, который обеспечивает ему различные характеристики, чем модуль Verilog и имеет различные использования импликаций. Среди таких характеристик является то, что классы создаются во время выполнения, а не во время разработки, как модули. Таким образом, OVM отвечает за создание компонента и сборки их в иерархии.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|[[Файл:69.png]]&lt;br /&gt;
|&amp;lt;graph&amp;gt;&lt;br /&gt;
digraph G{&lt;br /&gt;
node[shape=&amp;quot;circle&amp;quot;,fontsize=&amp;quot;11&amp;quot;,margin=&amp;quot;0.03,0.03&amp;quot;]&lt;br /&gt;
edge[arrowhead=none]&lt;br /&gt;
env -&amp;gt; {c1  c2}&lt;br /&gt;
c1 -&amp;gt; {child11  child12}&lt;br /&gt;
c2 -&amp;gt; {child21  child22}&lt;br /&gt;
child11 [label=&amp;quot;child1&amp;quot;]&lt;br /&gt;
child12 [label=&amp;quot;child2&amp;quot;]&lt;br /&gt;
child21 [label=&amp;quot;child1&amp;quot;]&lt;br /&gt;
child22 [label=&amp;quot;child2&amp;quot;]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/graph&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Рисунок 4-1 иллюстрирует простую иерархию компонент. После  мы покажем,  как построить эту иерархию с помощью средств OVM  для создания компонент и объединения их в иерархии:&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Самый верхний узел, ENV, является корнем. Корень отличается тем, что он не имеет предка. Все остальные узлы имеют только одного предка. Каждый узел имеет имя. Расположение каждого узла в иерархии может быть идентифицировано уникальным full_name (путем), который строится на нанизывании имен всех узлов между корнем и узлов в запросе, разделяя их сепаратором иерархии, точкой (.). Например, путь к компоненту, второго потомка с2 -  top.c2.child2.&lt;br /&gt;
&lt;br /&gt;
Компонент в ОВМ является классом, производным от ovm_component. Простейшая компоненты -  листья, те, которые не имеют потомков.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;57 class child extends ovm_component;&lt;br /&gt;
58&lt;br /&gt;
59 function new(string name, ovm_component parent);&lt;br /&gt;
60 super.new(name, parent);&lt;br /&gt;
61 endfunction&lt;br /&gt;
62&lt;br /&gt;
63 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Конструктор имеет два параметра, имя компоненты и указатель на его предка. Название -  простое имя, а не иерархический путь. Предок  предоставляет место для подключения  новой компоненты в иерархии. Полный  путь потомка создается путем объединения имени потомка с полным именем предка, разделенный точкой (.).OVM предоставляет методы для получения и имени и полного пути компоненты:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;string get_name();&lt;br /&gt;
string get_full_name();&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подчиненные компоненты создаются  в функции build(), которая вызывается на этапе сборки (фазы объясняется далее в этой главе). Создание экземпляра компонента включает вызов new() для выделения памяти для нее и передачи соответствующих параметров в конструктор. В компоненте, показанной ниже, мы создаем две подчиненные компоненты, и child1 child2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;71 class component extends ovm_component;&lt;br /&gt;
72&lt;br /&gt;
73 child child1;&lt;br /&gt;
74 child child2;&lt;br /&gt;
75&lt;br /&gt;
76 function new(string name, ovm_component parent);&lt;br /&gt;
77 super.new(name, parent);&lt;br /&gt;
78 endfunction&lt;br /&gt;
79&lt;br /&gt;
80 function void build();&lt;br /&gt;
81 child1 = new(“child1”, this);&lt;br /&gt;
82 child2 = new(“child2”, this);&lt;br /&gt;
83 endfunction&lt;br /&gt;
84&lt;br /&gt;
85 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как component, ENV также создает экземпляры двух подчиненных компонент, c1 и c2. Вся иерархия  с корнем в модуле,  называемым top в нашем проекте.(??)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;131 module top;&lt;br /&gt;
132&lt;br /&gt;
133 env e;&lt;br /&gt;
134&lt;br /&gt;
135 initial begin&lt;br /&gt;
136 e = new(“env”);&lt;br /&gt;
137 run_test();&lt;br /&gt;
138 end&lt;br /&gt;
139&lt;br /&gt;
140 endmodule&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вызов new() создает верхний уровень окружающей среды. run_test () начинает выполнение testbench.&lt;br /&gt;
&lt;br /&gt;
В SystemVerilog, модули, интерфейсы и программные блоки создаются во время разработки, в то время как классы создаются после разработки, во время выполнения. Таким образом, чтобы создать иерархию классов, мы должны иметь интерфейс, модуль или программу, который содержит начальный блок, который начинает процесс создание компонент иерархии, основанных на классах. Интерфейсы предназначены для использования в качестве средства связи между двумя модулями и не очень хорошо подходят для использования в качестве корня иерархии классов. Программные  блоки или модули могут быть использованы как  корень (??). Для нашей простой иерархии, это не имеет значения. Позже, когда мы подключаем класс-компонент для модуля на основе аппаратных средств, мы увидим, что использование модуля предпочтительнее программным блокам.&lt;br /&gt;
&lt;br /&gt;
=== Перемещение по иерархии ===&lt;br /&gt;
&lt;br /&gt;
Мы можем исследовать структуры данных, используемые для реализации компонента иерархии с некоторыми методами, предусмотренными в ovm_component. Потомки компоненты хранятся в ассоциативном массиве. Этот массив напрямую не доступен, но он может быть доступен через иерархию API. Этот API-интерфейс похож на встроенные методы SystemVerilog, предназначенные для ассоциативных массивов.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;int get_first_child(ref string name);&lt;br /&gt;
int get_next_child(ref string name);&lt;br /&gt;
ovm_component get_child(string name);&lt;br /&gt;
int get_num_children();&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
get_first_child () и get_next_child () работают вместе для перебора множества потомков, содержащихся в компоненте. get_first_child () извлекает имя первого ребенка в списке. Он возвращает имя в качестве ссылки. get_next_child () возвращает имя следующего ребенка в списке. Он  возвращает 1, если имеется следующая имя потомка и 0, если был достигнут конец списка. get_child () преобразует имя в ссылку компоненты.&lt;br /&gt;
&lt;br /&gt;
Используя эти функции, мы можем пройти по компонентам иерархии.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;73 function void depth_first(ovm_component node,&lt;br /&gt;
74 int unsigned level = 0);&lt;br /&gt;
75&lt;br /&gt;
76 string name;&lt;br /&gt;
77&lt;br /&gt;
78 if(node == null)&lt;br /&gt;
79 return;&lt;br /&gt;
80&lt;br /&gt;
81 visit(node, level);&lt;br /&gt;
82&lt;br /&gt;
83 if(node.get_first_child(name))&lt;br /&gt;
84 do begin&lt;br /&gt;
85 depth_first(node.get_child(name), level+1);&lt;br /&gt;
86 end while(node.get_next_child(name));&lt;br /&gt;
87&lt;br /&gt;
88 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/utils/traverse.svh&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эта функция будет осуществляющая обход в глубину иерархии, называя visit(). Мы используем get_first_child () и get_next_child () для перебора списка каждого потомка в каждом узле. Для каждого итерации мы вызываем depth_first () рекурсивно. Для нашего маленького проекта, результат будет  следующий:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;pre&amp;gt;+ env&lt;br /&gt;
| + env.c1&lt;br /&gt;
| | env.c1.child1&lt;br /&gt;
| | env.c1.child2&lt;br /&gt;
| + env.c2&lt;br /&gt;
| | env.c2.child1&lt;br /&gt;
| | env.c2.child2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция visit() использует  глубину узла и  определяет является ли он конечным  для печати строки каждого узла.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Singleton Top ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Компоненты, которые не имеют предков (то есть,  параметр предок в конструкторе является нулевым) называют orphans.(??) В OVM вы можете создать столько компоненты без предков, сколько вам необходимо. Тем не менее, не существует такого понятия, как истинные orphans. Любой компонент, чей предок является  null,  относится к встроенному предку, называемому ovm_top. ovm_top состоит из одного экземпляра ovm_root. Это предок всех компонент, которые имеют предков. В самом деле, env  в нашем предыдущем примере является потомком ovm_top. Так как он не имеет предка, то автоматически получают ovm_top в качестве родителя.&lt;br /&gt;
&lt;br /&gt;
singleton -  широко известный  объектно-ориентированных шаблон проектирования, который имеет private (local) или protected конструкторы и статическую функцию get, которая возвращает тот же самый указатель не смотря на то, сколько раз она вызывается. Это означает, что возможно существование одного экземпляра, и что экземпляр создается  автоматически при вызове функции get. ovm_top содержит дескриптор экземпляра singleton  ovm_root(???). Он статически инициализируется при вызове ovm_root :: Get (). Вы можете вызвать ovm_root :: Get () в любое время, но в этом нет необходимости, так как ovm_top предусматривает это.&lt;br /&gt;
&lt;br /&gt;
Есть целый ряд полезных следствий существования singleton top-level компоненты. Одним из них является, что вы можете достичь любую компоненту из ovm_top. Если вы запустили алгоритм обхода иерархии, начиная с ovm_top, вы доберетесь до каждой компоненты в системе. Другим следствием является то, что любая компонента, включая порты, экспорт и каналы, которые создаются внутри модуля, достижима из ovm_top. Если вы хотите изменить отчет во всех обработчиках компонент, вы можете сделать это, вызвав  одну из иерархических функции отчетности в ovm_top. ovm_top содержит все механизмы синхронизации, которые объясняются далее в этой главе.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Connectivity ==&lt;br /&gt;
&lt;br /&gt;
Компоненты соединены друг с другом через TLM port и exports. Port и exports обеспечивают средства для компонент, или, точнее, процессы в компонентах для синхронизации и связи друг с другом. Port и exports -  объекты, которые образуют  пункт связывания для обеспечения межкомпонентной связи. Как уже говорилось в предыдущей главе, exports обеспечивают функции и задачи, которые могут быть вызваны из Port.&lt;br /&gt;
&lt;br /&gt;
[[Файл:70.png]]&lt;br /&gt;
 &lt;br /&gt;
Метод connect  портов и exports используется для связывания их вместе.&lt;br /&gt;
&lt;br /&gt;
initiator_port.connect(target.export)&lt;br /&gt;
&lt;br /&gt;
Этот метод создает ассоциацию, или связывание, между port и export так, что port может вызывать(запускать) задачи и функции из export. Для успешного соединения, типы  port и export должны совпадать. То есть, типы интерфейсов должны быть одинаковыми, и тип объекта, передающегося в интерфейс должны быть одинаковыми.&lt;br /&gt;
&lt;br /&gt;
=== Подключение через Иерархию ===&lt;br /&gt;
&lt;br /&gt;
Как и контактами в RTL дизайне, нам нужно подключиться к портам  TLM через иерархические границы. Рисунок 4-3 использует простой дизайн, чтобы показать, как сделать эти соединения. Этот проект содержит исходную компоненту с двумя port(выходами), которые в конечном итоге подключаются к двум входам(export). Для осуществления подключения между этими компонентами, мы должны расширить port и export на  уровень выше в иерархии.&lt;br /&gt;
 &lt;br /&gt;
[[Файл:71.png]]&lt;br /&gt;
&lt;br /&gt;
Компонент source содержит два порта, и first_put_port, second_put_port.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;65 class source extends ovm_component;&lt;br /&gt;
66&lt;br /&gt;
67 ovm_put_port #(trans_t) first_put_port;&lt;br /&gt;
68 ovm_put_port #(trans_t) second_put_port;&lt;br /&gt;
69&lt;br /&gt;
70 function new(string name, ovm_component parent);&lt;br /&gt;
71 super.new(name, parent);&lt;br /&gt;
72 endfunction&lt;br /&gt;
73&lt;br /&gt;
74 function void build();&lt;br /&gt;
75 first_put_port = new(“first_put_port”, this);&lt;br /&gt;
76 second_put_port = new(“second_put_port”, this);&lt;br /&gt;
77 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Кроме того, компонента sink создает экспорт и создает его в функции build. Экспорт связан с внутренним каналом, FIFO, от которого компонента может получать объекты во время выполнения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;126 class sink extends ovm_component;&lt;br /&gt;
127&lt;br /&gt;
128 ovm_put_export #(trans_t) put_export;&lt;br /&gt;
129 local tlm_fifo #(trans_t) fifo;&lt;br /&gt;
130&lt;br /&gt;
131 function new(string name, ovm_component parent);&lt;br /&gt;
132 super.new(name, parent);&lt;br /&gt;
133 endfunction&lt;br /&gt;
134&lt;br /&gt;
135 function void build();&lt;br /&gt;
136 put_export = new(“put_export”, this);&lt;br /&gt;
137 fifo = new(“fifo”, this);&lt;br /&gt;
138 endfunction&lt;br /&gt;
139&lt;br /&gt;
140 function void connect();&lt;br /&gt;
141 put_export.connect(fifo.put_export);&lt;br /&gt;
142 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
source_wrapper должен создать соединение между внутренним источником компоненты и  внешней ее границей. Он делает это соединение, создавая свои порты, которые имеют тот же тип, что и тип  портов lower-level, в нашем случае, тех, которые принадлежат к source.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;98 class source_wrapper extends ovm_component;&lt;br /&gt;
99&lt;br /&gt;
100 source s;&lt;br /&gt;
101 ovm_put_port #(trans_t) put_port1;&lt;br /&gt;
102 ovm_put_port #(trans_t) put_port2;&lt;br /&gt;
103&lt;br /&gt;
104 function new(string name, ovm_component parent);&lt;br /&gt;
105 super.new(name, parent);&lt;br /&gt;
106 endfunction&lt;br /&gt;
107&lt;br /&gt;
108 function void build();&lt;br /&gt;
109 s = new(“source”, this);&lt;br /&gt;
110 put_port1 = new(“put_port1”, this);&lt;br /&gt;
111 put_port2 = new(“put_port2”, this);&lt;br /&gt;
112 endfunction&lt;br /&gt;
113&lt;br /&gt;
114 function void connect();&lt;br /&gt;
115 s.first_put_port.connect(put_port1);&lt;br /&gt;
116 s.second_put_port.connect(put_port2);&lt;br /&gt;
117 endfunction&lt;br /&gt;
118&lt;br /&gt;
119 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После того как порты в source_wrapper были созданы, они подключаются к портам lower-level  компоненты source с помощью метода Connect. Видимость портов на более высоком уровне иерархии достигается  таким же образом, как мы видим  в sinker.&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
160 class sinker extends ovm_component;&lt;br /&gt;
161&lt;br /&gt;
162 ovm_put_export #(trans_t) first_put_export;&lt;br /&gt;
163 ovm_put_export #(trans_t) second_put_export;&lt;br /&gt;
164&lt;br /&gt;
165 sink sink1;&lt;br /&gt;
166 sink sink2;&lt;br /&gt;
167&lt;br /&gt;
168 function new(string name, ovm_component parent);&lt;br /&gt;
169 super.new(name, parent);&lt;br /&gt;
170 endfunction&lt;br /&gt;
171&lt;br /&gt;
172 function void build();&lt;br /&gt;
173 sink1 = new(“sink1”, this);&lt;br /&gt;
174 sink2 = new(“sink2”, this);&lt;br /&gt;
175 first_put_export = new(“first_put_export”, this);&lt;br /&gt;
176 second_put_export = new(“second_put_export”, this);&lt;br /&gt;
177 endfunction&lt;br /&gt;
178&lt;br /&gt;
179 function void connect();&lt;br /&gt;
180 first_put_export.connect(sink1.put_export);&lt;br /&gt;
181 second_put_export.connect(sink2.put_export);&lt;br /&gt;
182 endfunction&lt;br /&gt;
183&lt;br /&gt;
184 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Два нижних уровня компоненты sink и exports  создаются обычным способом. Затем они соединяются с помощью метода подключения export. Сейчас мы создаем port-export соединение между source_wrapper и sinker, также с помощью функции Connect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;192 class env extends ovm_component;&lt;br /&gt;
193&lt;br /&gt;
194 sinker s;&lt;br /&gt;
195 source_wrapper sw;&lt;br /&gt;
196&lt;br /&gt;
197 function new(string name, ovm_component parent = null);&lt;br /&gt;
198 super.new(name, parent);&lt;br /&gt;
199 endfunction&lt;br /&gt;
200&lt;br /&gt;
201 function void build();&lt;br /&gt;
202 s = new(“sinker”, this);&lt;br /&gt;
203 sw = new(“source_wrapper”, this);&lt;br /&gt;
204 endfunction&lt;br /&gt;
205&lt;br /&gt;
206 function void connect();&lt;br /&gt;
207 sw.put_port1.connect(s.first_put_export);&lt;br /&gt;
208 sw.put_port2.connect(s.second_put_export);&lt;br /&gt;
209 endfunction&lt;br /&gt;
210&lt;br /&gt;
211 task run; &lt;br /&gt;
212 global_stop_request();&lt;br /&gt;
213 endtask&lt;br /&gt;
214&lt;br /&gt;
215 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для новых пользователей часто может вызвать трудности определить,  порт или экспорт необходимо подключить и какой объект является аргументом. Вы можете легко понять это, следуя потоку управления  через систему. Общее правило  заключается в том, что запрашиваемый порт вызывается с помощью функции connect() и передачи в нее запрашиваемого port или export в качестве аргумента. Рисунок 4-4 показывает поток управления через иерархическую систему.&lt;br /&gt;
&lt;br /&gt;
[[Файл:72.png]]&lt;br /&gt;
 &lt;br /&gt;
Порт – объект  вызова и экспорт - объект вызванной функции или задачи. Вы можете воспринимать порты, как вызов экспорта. Так, в env, мы вызываем connect на put_ports с put_exports в качестве аргументов. Для port-to- port  и export-to-export иерархии соединений, вызывающий будет чуть менее очевиден. Если  вызов осуществляется со стороны порта, вы можете считать, что вызов lowest-level порта в иерархии является вызовом методов интерфейса  upper-level порта. Аналогично, если экспорт объект вызова, вы можете считать upper-level экспорт  вызовом lower-level export. В таблице ниже приведены возможные типы подключения:&lt;br /&gt;
 &lt;br /&gt;
[[Файл:73.png]]&lt;br /&gt;
&lt;br /&gt;
=== Примечание для пользователей AVM ===&lt;br /&gt;
&lt;br /&gt;
В AVM-3.0 соединения были сделаны аналогично: вызовам функции connect() из port и export. Кроме того, 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.)&lt;br /&gt;
&lt;br /&gt;
== Фазы ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для традиционных модулей Verilog  полная разработка  проекта и  его выполнение осуществялется в симуляторе. Так как OVM компоненты - это классы, они  создаются и подключаются, и их выполнение начинается за пределами Verilog разработчика. Компоненты создаются вызовом конструктора класса операцией new(), которая выделяет память и выполняет инициализацию. В то время как  Verilog время выполнения  run-time engine managing instantiation, разработка, и выполнение компонент на основе классов, функциональность  компонент разделена на фазы, и контроллер фазы OVM  управляет их исполнением. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Каждая фаза представлена в компоненте как виртуального метода (задачи или функция) с тривиальной реализацией по умолчанию. Эти  phase callbacks   реализуются разработчиком компоненты, который обеспечивает соответствующую функциональность. Фаза контроллера гарантирует, что фазы выполняются в надлежащем порядке. Набор стандартных фаз показано в следующей таблице:&lt;br /&gt;
&lt;br /&gt;
[[Файл:74.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:75.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Каждая фаза имеет определенную цель. разработчики Компонент  должны заботиться о том, чтобы функциональность реализованная в каждой фазе  callback соответствовала фазе определения.&lt;br /&gt;
&lt;br /&gt;
•	'''New''' технически не является фазой, в том, что она не управляется фазой контроллера. Тем не менее, для каждого компонента, конструктор должен быть выполнен и завершен в целях создания компоненты. Таким образом,  new должен выполниться до build() или любого другого последующего этапах  выполнения.&lt;br /&gt;
&lt;br /&gt;
•	'''build'''  это место, где новые компоненты, порты и экспорт создаются и настраиваются. Это также рекомендуемое место для вызова set_config_ * и get_config_ * (см. Section4.4). &lt;br /&gt;
&lt;br /&gt;
•	'''connect''' – место, где компоненты, порты и экспорт созданные в  build() связываются.&lt;br /&gt;
&lt;br /&gt;
•	'''end_of_elaboration''' здесь вы можете выполнить изменение конфигураций, зная, что разработка уже завершена. То есть, вы можете предположить, что все компоненты созданы и соединены. &lt;br /&gt;
&lt;br /&gt;
•	'''start_of_simulation''' выполняется только до времени 0.&lt;br /&gt;
&lt;br /&gt;
•	'''run'''    pre-defined task фазой. Все выполняемые задачи работают параллельно. Каждая выполняемая задача продолжается, пока ее управление не достигнет оператора EndTask или она явно будет прекращена. Позже в этой главе мы обсудим, как завершать testbenches.&lt;br /&gt;
&lt;br /&gt;
•	'''extract'''  предназначен для сбора информации, касающейся покрытия или другой информации о том, как ответить на  вопросы testbench.  &lt;br /&gt;
&lt;br /&gt;
•	'''проверка''' - место, где выполняется любая проверка правильности или проверка валидации извлеченных данных. &lt;br /&gt;
&lt;br /&gt;
•	'''отчет''' -место , где делаются окончательные отчеты.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ниже простой пример,  использующий ovm_report_info () для иллюстрации порядка выполнения фаз.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;38 class sub_component extends ovm_component;&lt;br /&gt;
39&lt;br /&gt;
40 function new(string name, ovm_component parent);&lt;br /&gt;
41 super.new(name, parent);&lt;br /&gt;
42 endfunction&lt;br /&gt;
43&lt;br /&gt;
44 function void build();&lt;br /&gt;
45 ovm_report_info(“build”, ““);&lt;br /&gt;
46 endfunction&lt;br /&gt;
47&lt;br /&gt;
48 function void connect();&lt;br /&gt;
49 ovm_report_info(“connect”, ““);&lt;br /&gt;
50 endfunction&lt;br /&gt;
51&lt;br /&gt;
52 function void end_of_elaboration();&lt;br /&gt;
53 ovm_report_info(“end_of_elaboration”, ““);&lt;br /&gt;
54 endfunction&lt;br /&gt;
55&lt;br /&gt;
56 function void start_of_simulation();&lt;br /&gt;
57 ovm_report_info(“start_of_simulation”, ““);&lt;br /&gt;
58 endfunction&lt;br /&gt;
59&lt;br /&gt;
60 task run();&lt;br /&gt;
61 ovm_report_info(“run”, ““);&lt;br /&gt;
62 endtask&lt;br /&gt;
63&lt;br /&gt;
64 function void extract();&lt;br /&gt;
65 ovm_report_info(“extract”, ““);&lt;br /&gt;
66 endfunction&lt;br /&gt;
67&lt;br /&gt;
68 function void check();&lt;br /&gt;
69 ovm_report_info(“check”, ““);&lt;br /&gt;
70 endfunction&lt;br /&gt;
71&lt;br /&gt;
72 function void report();&lt;br /&gt;
73 ovm_report_info(“report”, ““);&lt;br /&gt;
74 endfunction&lt;br /&gt;
75&lt;br /&gt;
76 endclass&lt;br /&gt;
file: 04_OVM_mechanics/03_phases/top.sv&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В компоненте верхнего уровня, мы создаем две  компоненты, каждая из которых в свою очередь создает две sub_component. sub_components являющимися такими же, как и сами компоненты; каждая фаза обратного  callback просто печатает строку, определяющую фазу. При выполнении, вы получите следующий результат:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;OVM_INFO @ 0 [RNTST] Running test ...&lt;br /&gt;
OVM_INFO @ 0: env.c1 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [run]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s1 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s2 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c1 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s1 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s2 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c2 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s1 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s2 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c1 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s1 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s2 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c2 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s1 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s2 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c1 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s1 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s2 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c2 [report]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Вы видите, что build()выполняется сверху вниз и остальные фазы запуска снизу вверх. Вы также можете видеть, что каждый этап завершается во всех компонентах до начала следующего этапа. Таким образом, в connect(), например, вы можете расчитывать, что build() завершена во всех компонентах. Вы также заметите, что время  запускается после  фазы run. В нашем примере задача run - тривиальна, она просто выполняет задержку времени на единицу (# 1).&lt;br /&gt;
&lt;br /&gt;
un_test (), упоминалось ранее в разделе 4.1, инициирует фазу выполнение. Он начинает выполнение фаз по порядку и контролирует, что каждый этап завершен до начала следующего.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==	Config ==&lt;br /&gt;
&lt;br /&gt;
Для увеличения повторного использования компонент, желательно объявит их с параметрами, которые могут быть внешне настроены. Конфигурационные средства обеспечивают возможности для этого. Они основаны на базе пар имя-значение, и называются configuration items, которые организованы иерархически. Каждая компонента содержит таблицу конфигураций элементов конфигурации,  так как компоненты организованы в качестве дерева, каждый элемент в базе данных может быть уникально размещен по расположению компоненты и имени элемента конфигурации&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:76.png]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Класс ovm_component содержит два набора методов для  конфигурации размещения элементов в базу данных и потом для их получения. Это  set_config_ * и * get_config_. В таблице ниже приведены оба набора.&lt;br /&gt;
&lt;br /&gt;
[[Файл:77.png]] &lt;br /&gt;
&lt;br /&gt;
Set_config_ * функция помещает элемент в конфигурационную базу данных текущего компонента, то есть в экземпляре компонента, в котором функция вызывается. Каждая из этих функций принимают три аргумента, имя, field_name и значение. Аргумент name -  имя пути, который представляет набор компонент, которые должны соответсвовать кофигурациям этого элемента. name используется в * get_config_, чтобы найти элементы в базе данных конфигурации.  field_name -  это имя поля и должно быть уникальным в пределах текущей базы конфигураций. value является частью пары имя-значение и его тип может быть строка, INT, или ovm_object, в зависимости от вызываемой функции. Кроме того, set_config_object принимает копию аргумента для указания, был ли объект передан в качестве значения так как значение должно быть клонировано прежде чем оно будет помещено в базу конфигурации. &lt;br /&gt;
Функции get_config_ * извлекает элементов из базы данных конфигураций.  Эти функции принимают только два аргумента, имя поля и INOUT переменную, который содержит значение расположения элемента. Они также возвращают бит, чтобы указать был ли удачно расположен запрашиваемый объект. * Get_config_ функции не принимают аргументы имени пути, как их set_config_ * коллеги, поскольку они используют путь к текущему компоненту, как ссылку, чтобы найти элементы конфигурации. Они разработаны, чтобы узнать, есть значение элемента конфигурации для текущего контекста, то есть, компонент, в котором функция get_config_ * вызывается.&lt;br /&gt;
&lt;br /&gt;
Алгоритм поиска для извлечения элементов конфигурации использует имя пути компонента запроса и путь к файлу вставляемый в каждый  item. Он начинается с поиска конфигурационных элементов в базе данных в верхней компоненте по field_name. Если такой элемент существует, то он затем спрашивает, соответствует ли указанный путь к файлу пути имени компоненты. Если элемента с указанным field_name нет или имена путей не совпадают, то поиск идет с дочерней компоненты. Этот процесс продолжается до соответствия или  до достижения компоненты начала поиска.&lt;br /&gt;
&lt;br /&gt;
Путь  каждого элемент конфигурации может быть регулярным выражением. Таким образом, мы  используем алгоритм соответствия регулярных выражений для нахождения совпадения с  путем запрошенной компоненты и именем пути элемента конфигурации. Эффект заключается в соответствии иерархической области. &lt;br /&gt;
В качестве примера, рассмотрим простую иерархию на рисунке 4-5. Давайте предположим, что в  env::build()мы запускаем два вызова set_config_ *:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;112 function void build();&lt;br /&gt;
113 c1 = new(“c1”, this);&lt;br /&gt;
114 c2 = new(“c2”, this);&lt;br /&gt;
115&lt;br /&gt;
116 set_config_int(“c2.*”, “i”, 42);&lt;br /&gt;
117 set_config_int(“*”, “t”, 19);&lt;br /&gt;
118 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/04_config/top.sv&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Это вызовет запись двух конфигурационных элементов в базу данных в env. Обратите внимание на звездочку (*) в имени пути. Имя пути в вызове set_config_ * -  регулярное выражение, и дикие символы  используются для указать несколько областей, к которыми элемент применяется. По элементу i, c2. * показывает, что в любой области ниже c2 в иерархии, i будет  определять указанное значение. В этом случае указанное значение -  42. Если вы опустите звездочку, то элемент конфигурации применяется только к c2, а не к любому из своих потомков. &lt;br /&gt;
Состояние конфигурации базы данных для каждого компонента в иерархии  после  вызова set_config_ * показано на рисунке 4-6,&lt;br /&gt;
&lt;br /&gt;
[[Файл:78.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
пусть в элементе top.c1.child1 мы осуществим вызов:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;int i;&lt;br /&gt;
...&lt;br /&gt;
get_config_int(“i”, i)&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Поиск задает вопрос: Каково значение конфигурации i в иерархических рамках top.c1.child1? Чтобы ответить на этот вопрос, конфигурации базы данных в env ищется в первую очередь. Запись i там говорит, что значение i совпадающее с  env.c2. *  -  42. Тем не менее, компоненты, из которых был сделан запрос, находятся в c1 sub-hierarchy. Таким образом,  не существует соответствия, а get_config_int () возвращает статус неудачи. Запрос в любую компоненту, которая является дочерней c2 будет успешно завершен и вернется значение 42. &lt;br /&gt;
&lt;br /&gt;
Ниже приведен код для функцию build() дочерней компоненты. Это место где эти компоненты ищут конфигурационные значения i и t.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;60 function void build();&lt;br /&gt;
61&lt;br /&gt;
62 string msg;&lt;br /&gt;
63&lt;br /&gt;
64 if(!get_config_int(“t”, t)) begin&lt;br /&gt;
65 $sformat(msg, “no value for t found in config&lt;br /&gt;
database, using default value of %0d”, t);&lt;br /&gt;
66 ovm_report_warning(“build”, msg);&lt;br /&gt;
67 end&lt;br /&gt;
68&lt;br /&gt;
69 if(!get_config_int(“i”, i)) begin&lt;br /&gt;
70 $sformat(msg, “no value for i found in config&lt;br /&gt;
database, using default value of %0d”, i);&lt;br /&gt;
71 ovm_report_warning(“build”, msg);&lt;br /&gt;
72 end&lt;br /&gt;
73&lt;br /&gt;
74 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/04_config/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующий пример показывает вывода  при выполнении этого проекта:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# OVM_INFO @ 0 [RNTST] Running test ...&lt;br /&gt;
# OVM_WARNING @ 0: env.c1.child1 [build] no value for i found in&lt;br /&gt;
config database, using default value of 91&lt;br /&gt;
# OVM_WARNING @ 0: env.c1.child2 [build] no value for i found in&lt;br /&gt;
config database, using default value of 91&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Запрос на  конфигурационный элемент t был успешным во всех контекстах с того момента как вызов set_config_int установил, что tдоступно во всех контекстах. Два запроса на элемент конфигурации i были успешными, и два нет. Результат получился такой потому что мы ограничены наличием i только компонентах на уровне или ниже env.c2. Компоненты на уровне или ниже c1 не могут видеть элемент конфигурации i из-за того, как мы создали базу данных конфигурации.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Конфигурация и фазы ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь, когда мы знаем, что набор вызовов для размещения и извлечение элементов в конфигурацию базы данных, следующей задачей, является эффективное применение этих функции для настройки компонент. Конфигурация может быть использована для изменения поведения или структуры testbench. Как правило, поведенческий  и структурный режимы определяются, когда testbench начинает работу, поэтому он является наиболее удобным местом для установки параметров конфигурации на одной из ранних стадий, таких как new, build или connect.&lt;br /&gt;
&lt;br /&gt;
Из таблицы фаз, вы можете увидеть, что new и build являются нисходящими фазами, в то время как все остальные фазы восходящие. Так что, если вы хотите установить элемент конфигурации в базу данных на более высокий уровень контекста, чтобы он был подхвачен более низким уровнем, вы должны вызвать функцию set_config_ * в new или build фазе. Фазы выполняется дискретно, это означает, что каждая фаза выполняется до начала следующей. Вы можете установить элементы конфигурации в new или build фазу и извлекать их для использования в установке поведенческого режима или изменения топологии на этапе build. В вашей функции build, сначала вызовите get_config_ * для извлечения элементов из более высоких уровней иерархии для управления конфигурацией текущего уровня. Затем добавьте вызов set_config_ * для  помещения элементов конфигурации в базу данных для использования компонентами на более низких уровнях иерархии. Наконец, используя соответствующие элементы конфигурации, обработайте компоненты. Важно вызвать get_config_ * вначале, потому, что указанная информация может влиять на значения, которые установлены на более низких уровнях иерархии.&lt;br /&gt;
&lt;br /&gt;
Например, настройка топологии включает в себя установку параметров топологии&lt;br /&gt;
на верхнем уровне среды, а затем применение эти параметров в различных компонентах, которые находятся ниже верхнего уровня среды в иерархии. Наш пример имеет шину, которая может иметь любое количество masters или slaves. Число masters и slaves расположены в верхнем уровне среды. Модель шину подхватывает эту конфигурационную информацию и использует ее для построения шины. В build функции верхнего уровня окружающей среды, мы создаем модель шины и настраиваем ее с числом masters и slaves.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;129 function void build();&lt;br /&gt;
130 set_config_int(“bus”, “masters”, 4);&lt;br /&gt;
131 set_config_int(“bus”, “slaves”, 8);&lt;br /&gt;
132 b = new(“bus”, this);&lt;br /&gt;
133 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/05_config_topo/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Модель шины построена таким образом, что число masters и slaves не фиксировано. Вместо этого,  количество приходит из конфигурации системы.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;90 function void build();&lt;br /&gt;
91&lt;br /&gt;
92 int unsigned i;&lt;br /&gt;
93&lt;br /&gt;
94 if(!get_config_int(“masters”, masters)) begin&lt;br /&gt;
95 $sformat(msg, “\”masters\” is not in the&lt;br /&gt;
configuration database, using default value of %0d”, masters);&lt;br /&gt;
96 ovm_report_warning(“build”, msg);&lt;br /&gt;
97 end&lt;br /&gt;
98&lt;br /&gt;
99 for(i = 0; i &amp;lt; masters; i++) begin&lt;br /&gt;
100 $sformat(name, “master_%0d”, i);&lt;br /&gt;
101 m = new(name, this);&lt;br /&gt;
102 end&lt;br /&gt;
103&lt;br /&gt;
104 if(!get_config_int(“slaves”, slaves)) begin&lt;br /&gt;
105 $sformat(msg, “\”slaves\” is not in the configuration&lt;br /&gt;
database, using default value of %0d”, slaves);&lt;br /&gt;
106 ovm_report_warning(“build”, msg);&lt;br /&gt;
107 end&lt;br /&gt;
108&lt;br /&gt;
109 for(i = 0; i &amp;lt; slaves; i++) begin&lt;br /&gt;
110 $sformat(name, “slave_%0d”, i);&lt;br /&gt;
111 s = new(name, this);&lt;br /&gt;
112 end&lt;br /&gt;
113&lt;br /&gt;
114 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/05_config_topo/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В функции build для модели шины, схема получает необходимую конфигурационную информацию, используя вызовы get_config_int. В каждом случае, Возвращаемое значение проверяется, чтобы определить, была ли получена запрошенная конфигурационная информация. Если нет, сообщение об ошибке предупреждает, что конфигурационный элемент не найден и что будет использовано значение по умолчанию. С точки зрения Best Practices, важно убедиться, что возвращается значение, проверено и выдается предупреждение, в случае ошибки. Без этой проверки, то, что  используется значения по умолчанию, могло остаться незамеченным. В некоторых случаях это может быть приемлемо, в других случаях - нет. Человек,  создающий модель шины не может знать всех обстоятельств, при которых модель будет использоваться. Поэтому важно сделать все возможное, чтобы  модель была надежной. Проверка возвращаемых значений и выдача соответствующих сообщений является одним из способов повышения надежности модели.&lt;br /&gt;
&lt;br /&gt;
В цикле, где мы строим masters, ссылки на каждого нового master сохраняются в той же переменной, m. Каждый новый master заменяет предыдущий. Мы не используем массив для хранения всех компонент. В каждой итерации, мы используем $ sformat, чтобы создать уникальное имя. Конструктор, new(), вызывает super.new (), конструктор в ovm_component базовом классе, который отвечает за вставку вновь созданную компоненту потомка в родительский список. Нет  необходимости явно сохранять компоненту, потому что родительская компонента сделает это за нас. Цикл для создания slaves организован таким же образом.&lt;br /&gt;
&lt;br /&gt;
== Factory(Фабрика) ==&lt;br /&gt;
&lt;br /&gt;
Структура testbench определяется организацией компонент в иерархии и то, как эти объекты связаны между собой. Поведение testbench определяется процедурным кодом в фазе callbacks—build, connect, run и так далее. Есть моменты, когда это необходимо изменить поведение или часть структуры извне(снаружи), то есть во время выполнения, не касаясь, кода testbench. Например, чтобы ввести ошибку в систему, вы можете заменить обычный драйвер  драйвером с ошибкой, который умышленно вводит ошибки. Вместо повторного кодирования окружающей среды для использования другого драйвера, вы можете использовать factory, чтобы сделать замену автоматически.&lt;br /&gt;
&lt;br /&gt;
Factory обеспечивает средства для замены одного объекта на другой без использования текстового редактора для изменения testbench. Вместо создания объект с помощью new(), вы вызываете функцию create  из factory. Factory  хранит список зарегистрированных объектов и, при необходимости, набор переопределения связанный с каждой из них. При создании объекта с помощью factory, список переопределения проверяется на наличие такого объекта. Если он есть, то переопределенный объект возвращается. В противном случае, зарегистрированный объект возвращается. &lt;br /&gt;
&lt;br /&gt;
Factory  является OVM структурой данных. Она  глобальна в области видимости, и только один элемент factory может существовать  (то есть, это singleton). Она служит в качестве полиморфных конструкторов, одна функция, которая позволяет создавать различные объекты. Она  обеспечивает средства для регистрации объектов, и для их переопределения. Объекты, зарегистрированные как переопределения,  должны быть производным от объекта, от которого они переопределялись. Для того, чтобы одна функция возвращала несколько объектов, каждый из этих объектов должны быть получен от общего базового класса.&lt;br /&gt;
Важным компонентом factory является оболочка, класс, который описывает объект, который мы хотим создать с  помощью factory. Структура данных factory основывается на таблице оболочек, проиндексированной по ключу. Оболочка имеет функцию create(), которая обращается к  конструктору обернутого объекта.&lt;br /&gt;
&lt;br /&gt;
Использование factory состоит из трех этапов: регистрация, настройки переопределение и создание. На первом этапе необходимо зарегистрировать объект с помощью factory. На втором шаге, добавляется переопределение зарегистрированного объекта. На третьем этапе, вы создаете объект с помощью factory, который будет возвращать либо первоначально зарегистрированный объект или переопределенный, в зависимости от того, переопределение было передано в качестве  запрашиваемого объекта.&lt;br /&gt;
&lt;br /&gt;
=== Как работает Фабрика ===&lt;br /&gt;
&lt;br /&gt;
Термин factory-Фабрика был придуман The Gang of Four для использования в программном обеспечении в их книге Design Patterns: Elements of Reusable Object-Oriented Software. В этой книге, они определили шаблон, который они называют abstract factory, как интерфейс для создания семейств связанных объектов. Они определили шаблон factory method  в качестве интерфейса для создания объектов, относящихся к подклассам для определения, какой объект создавать. Factory  в OVM является комбинацией этих двух  шаблонов. Она предоставляет средства для создания семейства объектов, а также предоставляет средства для определения, какой именно объект создавать структуре данных фабрики.&lt;br /&gt;
&lt;br /&gt;
Factory OVM основана на структуре данных, которая  отображает необходимые типы в переопределенные. По сути, организация представляет собой ассоциативный массив типов, ключом в котором является также тип. При регистрации типа при помощи фабрики, его переопределенным типом является он сам. Таким образом, по умолчанию, когда вы запрашиваете объект этого типа, вы получаете только тип. Фабрика  также предоставляет средства для замены переопределенных типов другими типами, чтобы вы могли получить переопределенные типы, которые отличаются от зарегистрированных типов.&lt;br /&gt;
&lt;br /&gt;
[[Файл:79.png]]&lt;br /&gt;
&lt;br /&gt;
Следующий пример является сильно упрощенной фабрикой, которая показывает,  как OVM фабрика работает. Фабрика  сохраняет основные структуры OVM фабрики, но многие детали были удалены для более понятной иллюстрации. Наша фабрика реализуется в четырех классах, двух базовых классах и двух производных классах, которые делают реальную работу. Двумя базовыми классами являются object_base и wrapper_base. Все объекты, зарегистрированные в фабрике, должны быть получены (в конечном счете) от object_base, а wrapper_base является базовым классом для параметризованной обертки. Factory  - это singleton, который содержит ассоциативный массив, содержащий экземпляры обертки. Наконец, обертки являются производными от wrapper_base и параметризованными классами, которые представляют уникальные типы. &lt;br /&gt;
&lt;br /&gt;
Для нашей фабрики, базовые классы тривиальны:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;46 class object_base;&lt;br /&gt;
47 virtual function void print();&lt;br /&gt;
48 $display(“object_base”);&lt;br /&gt;
49 endfunction&lt;br /&gt;
50 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&lt;br /&gt;
59 class wrapper_base;&lt;br /&gt;
60 virtual function object_base create_object();&lt;br /&gt;
61 return null;&lt;br /&gt;
62 endfunction&lt;br /&gt;
63 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
object_base имеет виртуальную функцию print(), которую мы используем для проверки типов, созданных на фабрике. wrapper_base имеет виртуальную функцию create(), полиморфный конструктор, который используется для создания новых объектов.&lt;br /&gt;
factory - это singleton, то есть его конструктор локальный, и он содержит статическую ссылку  на самого себя. Единственный способ создать экземпляр factory является вызов factory::get(). factory содержит ассоциативный массив, который отображает запрашиваемые типы wrapper_base в   переопределенные типы wrapper_base.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;73 class factory;&lt;br /&gt;
74&lt;br /&gt;
75 static factory f;&lt;br /&gt;
76 wrapper_base override_map[wrapper_base];&lt;br /&gt;
77&lt;br /&gt;
78 local function new();&lt;br /&gt;
79 endfunction&lt;br /&gt;
80&lt;br /&gt;
81 static function factory get();&lt;br /&gt;
82 if(f == null)&lt;br /&gt;
83 f = new();&lt;br /&gt;
84 return f;&lt;br /&gt;
85 endfunction&lt;br /&gt;
86&lt;br /&gt;
87 function void register(wrapper_base w);&lt;br /&gt;
88 override_map[w] = w;&lt;br /&gt;
89 endfunction&lt;br /&gt;
90&lt;br /&gt;
91 function void set_override(wrapper_base requested_type,&lt;br /&gt;
92 wrapper_base override_type);&lt;br /&gt;
93 override_map[requested_type] = override_type;&lt;br /&gt;
94 endfunction&lt;br /&gt;
95&lt;br /&gt;
96 function object_base create(wrapper_base&lt;br /&gt;
97 requested_type);&lt;br /&gt;
98 object_base obj;&lt;br /&gt;
99 wrapper_base override_type =&lt;br /&gt;
100 override_map[requested_type];&lt;br /&gt;
101 obj = override_type.create_object();&lt;br /&gt;
102 return obj;&lt;br /&gt;
103 endfunction&lt;br /&gt;
104&lt;br /&gt;
105 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Метод register() добавляет новую запись в карту. Первоначально, при регистрации, тип не имеет переопределения. Таким образом, мы записали в карту тип, указывающий сам на себя. Set_override () метод заменяет запись в карте новым переопределенным типом. create() метод ищет переопределения на указанный тип, инициирует создание переопределенного типа и возвращает вновь созданный объект.&lt;br /&gt;
Класс-оболочка является наиболее интересным классом в нашем созвездии factory-related классов. Хотя он довольно прост, он делает большую часть тяжелой работы. Это основной интерфейс для фабрики, и большинство операций, которые производятся фабрикой, проходят через интерфейс оболочки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;118 class wrapper #(type T=object_base) extends wrapper_base;&lt;br /&gt;
119&lt;br /&gt;
120 typedef wrapper#(T) this_type;&lt;br /&gt;
121&lt;br /&gt;
122 static this_type type_handle = get_type();&lt;br /&gt;
123&lt;br /&gt;
124 local function new();&lt;br /&gt;
125 endfunction&lt;br /&gt;
126&lt;br /&gt;
127 function object_base create_object();&lt;br /&gt;
128 T t = new();&lt;br /&gt;
129 return t;&lt;br /&gt;
130 endfunction&lt;br /&gt;
131&lt;br /&gt;
132 static function T create();&lt;br /&gt;
133 T obj;&lt;br /&gt;
134 factory f = factory::get();&lt;br /&gt;
135 assert($cast(obj, f.create(get_type())));&lt;br /&gt;
136 return obj;&lt;br /&gt;
137 endfunction&lt;br /&gt;
138&lt;br /&gt;
139 static function this_type get_type();&lt;br /&gt;
140 factory f;&lt;br /&gt;
141 if(type_handle == null) begin&lt;br /&gt;
142 type_handle = new();&lt;br /&gt;
143 f = factory::get();&lt;br /&gt;
144 f.register(type_handle);&lt;br /&gt;
145 end&lt;br /&gt;
146 return type_handle; &lt;br /&gt;
147 endfunction&lt;br /&gt;
148&lt;br /&gt;
149 static function void set_override(wrapper_base&lt;br /&gt;
150 override_type);&lt;br /&gt;
151 factory f = factory::get();&lt;br /&gt;
152 f.set_override(type_handle, override_type);&lt;br /&gt;
153 endfunction&lt;br /&gt;
154&lt;br /&gt;
155 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Все функции в wrapper#() являются статическими, за исключением конструктора и create_object (). Мы можем выполнить его статические функции, не заботясь о том, был ли они явно создан экземпляр класса. Так как каждая обертка специализации уникальна. Это означает, что статическая переменная type_handle является уникальной и может быть использован в качестве прокси-сервера для обернутого типа (то есть, типа, передающегося в качестве параметра, который используется для специализации класса). Так как тип является уникальным, и большинство методов являются статическими, мы можем рассматривать типа  больше как тип, а не объект.&lt;br /&gt;
&lt;br /&gt;
Тип инициализируется статически. Это происходит в следующей строке:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;static wrapper#(T) type_handle = get_type();&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Функция get_type () вызывается во время статической инициализации, которая не только создает экземпляр оболочки, но и регистрирует его в фабрике. Для регистрации класса в фабрике, сначала специализируется обертка с типом объекта, который вы оборачиваете. Используйте typedef  для специализации оболочки, как показано в следующем примере:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;typedef wrapper#(some_type) type_id;&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Это определение типа создает оболочку для SOME_TYPE типа, тип, производный от object_base.&lt;br /&gt;
Рисунок 4-8 иллюстрирует, как использовать фабрику с некоторыми  классами A, B, и С, которые являются производными от family_base.&lt;br /&gt;
&lt;br /&gt;
[[Файл:80.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Чтобы зарегистрировать классы с помощью фабрики, каждый из них имеет typedef  из обертки, параметризованной со своим типом. Ниже класс А. Класс B и C  похожи. Каждый из них имеет typedef, которая специализируется обертку.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;169 class A extends family_base;&lt;br /&gt;
170&lt;br /&gt;
171 typedef wrapper#(A) type_id;&lt;br /&gt;
172&lt;br /&gt;
173 virtual function void print();&lt;br /&gt;
174 $display(“A”);&lt;br /&gt;
175 endfunction&lt;br /&gt;
176 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
Ниже приводится короткая программа, которая использует фабрику.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;206 function void run();&lt;br /&gt;
207&lt;br /&gt;
208 f = factory::get();&lt;br /&gt;
209&lt;br /&gt;
210 h = family_base::type_id::create();&lt;br /&gt;
211 h.print();&lt;br /&gt;
212&lt;br /&gt;
213&lt;br /&gt;
family_base::type_id::set_override(B::type_id::get_type());&lt;br /&gt;
214&lt;br /&gt;
215 h = family_base::type_id::create();&lt;br /&gt;
216 h.print();&lt;br /&gt;
217&lt;br /&gt;
218 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При написании кода интенсивно используют оператор (::),  он используется для обращения к статической функции в factory и в wrapper(). Сначала, мы получаем уникальный экземпляр структуры данных фабрики, затем создаем экземпляр объекта family_base. family_base :: type_id :: get_type() является статической функцией внутри специально обвернутого family_base. Мы  проверяем, что экземпляр family_base создается путем вызова print(). Далее, определяем переопределение B для family_base. Опять же, мы создаем экземпляр family_base. На этот раз, так как переопределение сейчас находится на месте, вместо того, чтобы получить экземпляр family_base, мы получаем экземпляр B.&lt;br /&gt;
&lt;br /&gt;
Наша фабрика не содержит все функциональные возможности фабрики, реализованной в OVM. Фабрика OVM обеспечивает отображение  строк (имен) в типы. Это позволяет переопределить цепочки, в то время, как фабрика этого не делает. Например, если B переопределяет А и C переопределяет B, когда вы просите экземпляр А, вы получите экземпляр C. Фабрика  OVM поддерживает два начальных базовых класса, ovm_object и ovm_component для зарегистрированных объектов, и метод create() для обоих, в то время, фабрика имеет только один первичный класс, object_base.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== The OVM Factory API  ===&lt;br /&gt;
&lt;br /&gt;
В этом разделе мы рассмотрим более подробно API  OVM фабрики. Он состоит из двух частей, type-based фабрики и string-based фабрики. В string-based фабрике запрашиваемый тип идентифицируются строкой имени. В type-based фабрике, тип идентифицируются по поддерживаемому типу(????). Один тип может быть зарегистрирован двумя способами. Методы выполнения  трех этапов (регистрация, установка коррекции, и создание) немного отличаются в каждом случае. Сначала мы рассмотрим type-based фабрику. Вот компонент, называемый драйвер, который регистрирует себя на основе type-based  фабрики.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;49 class driver extends ovm_component;&lt;br /&gt;
50&lt;br /&gt;
51 typedef ovm_component_registry#(driver) type_id;&lt;br /&gt;
52&lt;br /&gt;
53 static function type_id get_type();&lt;br /&gt;
54 return type_id::get();&lt;br /&gt;
55 endfunction&lt;br /&gt;
56&lt;br /&gt;
57 function string get_type_name();&lt;br /&gt;
58 return “driver”;&lt;br /&gt;
59 endfunction&lt;br /&gt;
60&lt;br /&gt;
61 function new(string name, ovm_component parent);&lt;br /&gt;
62 super.new(name, parent);&lt;br /&gt;
63 endfunction&lt;br /&gt;
64&lt;br /&gt;
65 endclass&lt;br /&gt;
file: 04_OVM_mechanics/06_factory/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Есть две части для регистрации, поддержка  typedef ovm_component_registry # () и supplying статической функции get_type ().typedef создает специализацию ovm_component_registry помощью компонент типа драйвер в качестве параметра. ovm_component_registry # () класс имеет статический инициализатор, который регистрирует, используя  структуру фабрики. Таким образом, создание специализации, используя typedef , влечет за собой идентификацию класса по параметру, driver в этом случае, должен быть зарегистрирован в фабрике.&lt;br /&gt;
Задание переопределения является простой причиной вызова функции set_override и передача переопределенного типа в качестве аргумента, как показано ниже:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;105&lt;br /&gt;
driver::type_id::set_type_override(error_driver::get_type());&lt;br /&gt;
file: 04_OVM_mechanics/06_factory/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Эта строка выглядит довольно сложными, но на самом деле, это довольно просто. Давайте преобразуем выражение, чтобы полностью понять, что оно значит.&lt;br /&gt;
&lt;br /&gt;
driver – запрашиваемый тип.&lt;br /&gt;
&lt;br /&gt;
driver::type_id – тип обертки.&lt;br /&gt;
&lt;br /&gt;
driver::type_id::set_type_override – the set override function in the specialized wrapper. This is a static function, which is why &lt;br /&gt;
you need the :: scope operator to refer to it.&lt;br /&gt;
&lt;br /&gt;
error_driver – переопределенный тип.&lt;br /&gt;
&lt;br /&gt;
error_driver::get_type() – статическая функция, возвращаемая тип, поддерживаемый error_driver.&lt;br /&gt;
&lt;br /&gt;
Для создания экземпляра класса с помощью фабрики, мы называем метод создания фабрики, как показано ниже:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;102 d1 = driver::type_id::create(“d1”, this);&lt;br /&gt;
file: 04_OVM_mechanics/06_factory/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Синтаксис :: работает так же, как и в предыдущем примере - driver относится к запрашиваемому типу, driver::type_id относится к типу специализированной обертки и driver::type_id::create относится к  функции создать в специализированной обертке. Этот оператор создает экземпляр driver. Разница между вызовом Create () и new() является то, что create() обращаться к фабрике на наличие переопределенных типов. Если это так, Create ()  возвращает объект переопределенного типа.&lt;br /&gt;
&lt;br /&gt;
Теперь давайте рассмотрим string-based фабрику. Механизм регистрации string-based фабрики опирается на определение типа как на основе типа фабрики,  как показано ниже:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;48 typedef ovm_component_registry#(driver, “driver”)&lt;br /&gt;
type_id;&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Единственная разница заключается в добавлении второго параметра параметризованной обертки. Он определяет имя типа, в этом случае driver. Когда ovm_component_registry # () специализируется с двумя параметрами, тип и имя, оболочка регистрирует в с type-based  фабрике и на string-based фабрике. Чтобы установить переопределение, используя имя типа, необходимо вызвать API  фабрики напрямую, а не использовать обертку API, как показано ниже:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;106 factory.set_type_override_by_name(“driver”,&lt;br /&gt;
107 “error_driver”);&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это утверждение просто говорит, когда необходимо создать объект, имя типа которого driver, возвращать тип, имя которого error_driver вместо этого. Главная  разница между использованием string-based и type-based фабрик в том, как создаются объекты. В type-based API, вы получаете доступ к фабрике через оболочку. В string-based API, вы вызываете фабрику напрямую. Тип возвращаемого ovm_factory :: create_component_by_name () является ovm_component. Сравните это с возвращаемым типом &lt;br /&gt;
ovm_component_registry (T)::Create, который T. Чтобы получить доступ к предполагаемому типу возвращаемого объекта вам придется downcast его, как показано ниже:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;99 assert($cast(d1,&lt;br /&gt;
100 factory.create_component_by_name(“driver”,&lt;br /&gt;
101 ““,&lt;br /&gt;
102 “d1”,&lt;br /&gt;
103 this)));&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Вызов  factory.create_component_by_name () возвращает объект типа ovm_component.$ $cast downcasts возвращается объект типа d1, который является driver. Поскольку тип аргумента create_component_by_name () является строкой, нет никакой проверки во время компиляции, что тип возвращаемого объекта приведен к требуемому типу. Поэтому важно, чтобы проверялся код возвращаемого значения $cast, для определения удалось ли приведение. Фабрика  string-based API включает в себя create_object_by_name(). Она используется для создания объектов, производных от ovm_object. Вы должны вызвать $cast to downcast созданный объект по той же причине вы вызываете cast в компонентах, созданных string-based фабрики API.&lt;br /&gt;
&lt;br /&gt;
=== String-Based or Type-Based?  ===&lt;br /&gt;
&lt;br /&gt;
type-based и string-based фабрики API, каждая имеет свои плюсы и минусы, но, как правило, мы рекомендуем вам использовать type-based фабрику. Она  гораздо более надежная, защищена  от ошибок в строке названия. &lt;br /&gt;
&lt;br /&gt;
Иногда нет другого выбора; только на  string-based фабрику можно использовать. Важным примером этого является, когда вы хотите указать название теста с командной строки. run_test () принимает необязательный строковый аргумент, test_name. Кроме того, эта задача проверяет OVM_TESTNAME аргумент командной строки. Если тестовое имя поддерживается  командной строкой либо  списком аргументов, run_test () вызывает string-based фабрику для создания экземпляра объекта.&lt;br /&gt;
&lt;br /&gt;
string-based фабрика страдает от двух существенных недостатков. Один из них, уже упоминался,  это то, что легко неправильно ввести имя типа, когда вы пишете код. Это может привести к ошибке testbench, потому что невозможно найти объект, или in a subtle bug where the wrong object is instantiated. Вторым недостатком является то,  что это трудно, если нет возможности объявить параметризованный класс с использованием строки названия. Рассмотрим, например, параметризованный класс my_class.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class my_class #(type T=int) extends ovm_object;&lt;br /&gt;
typedef my_class#(T) this_t;&lt;br /&gt;
typedef ovm_object_registry#(this_t, “my_class#(T)”) type_id;&lt;br /&gt;
endclass&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Мы зарегистрировали его на string-based фабрики, используя имя my_class # (T). Кажется логичным. Теперь рассмотрим две специализации этого класса.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;typedef my_class#(A) C1;&lt;br /&gt;
typedef my_class#(B) C2;&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Не  существует  удобного способа регистрации их фабрикой с использованием имен, которые являются уникальными для специализации. В  наших классах в примере, С1 и С2 зарегистрированы с использованием имени my_class # (T). В структуре данных фабрики, это выглядит так, как будто вы пытаетесь зарегистрировать два объекта с одним и тем же именем, что является ошибкой. The remedy is to use the type-based factory API, which is not encumbered with strings&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class my_class #(type T=int) extends ovm_object;&lt;br /&gt;
typedef my_class#(T), this_t;&lt;br /&gt;
typedef ovm_object_registry#(this_t) type_id;&lt;br /&gt;
endclass&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Оставляя  второй аргумент в typedef из type_id, мы говорим фабрике зарегистрировать объект без имени, использовать только тип  как ключ поиска. Теперь у специализаций будут  свои собственные уникальные типы,  и не будет ошибочно считаться, что это один и тот же объект в фабрике.&lt;br /&gt;
&lt;br /&gt;
Недостатком использования type-based фабрики является то, что нет никакого способа поиска объекта строки. Это потому, что без второго аргумента в ovm_object_registry typedef, нет названия, под которым хранится объект. При первом использовании type-based фабрики, может ввести в замешательство, что нет  доступного имени. Вы быстро обнаружите, что строка имени не является действительно необходимой. В тех случаях, когда необходимо имя, например, когда вы получаете имена объектов из пользовательского ввода, тогда можно использовать string-based фабрику.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Прекращение работы Testbench ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Самым простым способом прекратить выполнение   testbench ОВМ является вызов глобальной функции global_stop_request (). Это запросы, которые  закрывают  testbench. Если нет никаких причин для закрытия, то выполнение  testbench будет приостановлено.  global_stop_request () представитель ovm_top.stop_request (). Эти два формы семантически эквивалентны. &lt;br /&gt;
Какие причины будут для того, чтобы не  допустить прекращения работы? Каждая компонента имеет виртуальные задачу stop(). Когда вы вызываете global_stop_request (), эта задача  вызывается для каждой компоненты, enable_stop_request устанавливается в 1. &lt;br /&gt;
Когда все  задачи остановки выполнились, testbench прекращает работу. &lt;br /&gt;
&lt;br /&gt;
Задача остановки может быть использована для очистки информации, скажите DUT, чтобы прекратил выполнение, служил в качестве объекта завершения работы, или что-нибудь еще вы хотели бы сделать до завершения выполняемой фазы. Поскольку это задача, stop()может потреблять время. stop() задача может запретить выключение блокировкой. она может подождать выполнения некоторого условия или задержки на фиксированное время. stop()задача случит для прекращения(отмены) выполнения запроса.  When stop()  returns, it allows the request to be granted.&lt;br /&gt;
&lt;br /&gt;
В следующем примере показано, как работает механизм остановки. Этот пример содержит из двух  producers  транзакций для отправки их к получателю через FIFO. Каждый  producers работает независимо от других. Мы хотим убедиться, что оба  producers закончили  свою работу. Когда один заканчивает, другой продолжает до тех пор, пока он не выполнит свою работу до конца.&lt;br /&gt;
 &lt;br /&gt;
[[Файл:81.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В build() функции для топ-уровня среды, в дополнение к экземплярам различных компонент, мы настроим различное число итераций для каждого  producer. Так как число итераций для каждого из них разные, один  producer завершится перед другими.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;127 function void build();&lt;br /&gt;
128 set_config_int(“producer1”, “iterations”, 5);&lt;br /&gt;
129 set_config_int(“producer2”, “iterations”, 9);&lt;br /&gt;
130 p1 = new(“producer1”, this);&lt;br /&gt;
131 p2 = new(“producer2”, this);&lt;br /&gt;
132 c = new(“consumer”, this);&lt;br /&gt;
133 f = new(“fifo”, this);&lt;br /&gt;
134 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/09_shutdown/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим, чтобы  testbench  завершил выполнение  в определенном порядке, когда все необходимая работа будет сделана, но мы не хотим закрывать преждевременно, когда первый producer завершит работу. Мы используем механизм остановки для достижения этой цели. Каждый producer имеет задачу остановки, которая ждет, пока done станет 1.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;77 task stop(string ph_name);&lt;br /&gt;
78 ovm_report_info(“stop”, “initating stop”);&lt;br /&gt;
79 wait(done == 1);&lt;br /&gt;
80 ovm_report_info(“stop”, “shutting down...”);&lt;br /&gt;
81 endtask&lt;br /&gt;
file: 04_OVM_mechanics/09_shutdown/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ph_name аргумент содержит имя фазs, в которой вызывается stop() . Хотя по умолчанию есть только на  task-based фаза, run() есть возможность произвольно добавить больше  task-based фаз (и функций на основе фаз, тоже). Остановка механизм запроса работает во всех  task-based фазах, а вызов global_stop_request () работает в текущей  task-based фазе. Это приводит к вызову stop(). Так как только одна задача с именем stop возможна в каждой компоненте, ph_name аргумент определяет фазу, в которой она была вызвана. Вы можете использовать это для изменения поведения stop() на основе имени task-based фазы. &lt;br /&gt;
Для остановки механизма запроса, мы должны отменить его, установив enable_stop_interrupt  в 1. Мы делаем это в конструкторе компоненты.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;42 function new(string name, ovm_component p = null);&lt;br /&gt;
43 super.new(name,p);&lt;br /&gt;
44 iterations = 10; // default value&lt;br /&gt;
45 done = 0;&lt;br /&gt;
46 enable_stop_interrupt = 1;&lt;br /&gt;
47 endfunction&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Основной цикл  producer прост. Каждая итерация цикла генерирует случайное число и передает его через выходной порт  к  consumer. В следующем примере, когда цикл завершается, мы устанавливаем done в 1, который запускает  задачу stop.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;66 for(int i = 0; i &amp;lt; iterations; i++) begin&lt;br /&gt;
67 randval = $random % 100;&lt;br /&gt;
68 $sformat(s, “sending %4d”, randval);&lt;br /&gt;
69 ovm_report_info(“producer”, s);&lt;br /&gt;
70 put_port.put(randval);&lt;br /&gt;
71 end&lt;br /&gt;
72&lt;br /&gt;
73 done = 1;&lt;br /&gt;
file: 04_OVM_mechanics/09_shutdown/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Заключительная часть -  задачи run верхнего уровня среды:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;142 task run();&lt;br /&gt;
143 ovm_report_info(“run”, “start”);&lt;br /&gt;
144 global_stop_request();&lt;br /&gt;
145 endtask&lt;br /&gt;
file: 04_OVM_mechanics/09_shutdown/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После запуска, задача немедленно вызывает global_stop_request (), которая &lt;br /&gt;
приводит к вызову задачи stop() в  producers  (потому что enable_stop_interrupt установлен в 1 в каждой). В свою очередь, каждый  producer блокируется до  соответствующего значения флага done. Когда  producer с наименьшими числом итераций заканчивает свое выполнение, о it triggers its local done  flag and its stop  task returns.   However, because there are outstanding blocked stop  tasks, the simulation continues. Только, когда все  задачи stop будут выполнены моделирование прекращается.&lt;br /&gt;
&lt;br /&gt;
=== Timeout ===&lt;br /&gt;
&lt;br /&gt;
Вполне возможно, что моделирование может остановиться, когда ошибка в stop задаче препятствует  возвращению значения, заблокированный вызов никогда не разблокируется или бесконечный цикл  никогда не остановится. Для предотвращения моделирования от зависания на неопределенный срок, OVM  предоставляет два  timeout механизма. Однин  для задач фаз, а другой для stop задачи.&lt;br /&gt;
ovm_root содержит две переменные, phase_timeout и stop_timeout. Их типа это  Verilog тип tim, и их значения могут быть установлены set_global_timeout и set_global_stop_timeout. Значение по умолчанию  переменных 0, что означает тайм-аут отключен. &lt;br /&gt;
&lt;br /&gt;
Когда задача на основе фаз выполняется, такие как Run (), и имеет phase_timeout был установлен в значение больше нуля, то отдельный процесс сторожевой  породил, который просто ждет, пока тайм-аута. fork/join конструкция используется для создания этих задач, поэтому, если run задачи завершились до тайм-аута, то тайм-аут игнорируется. С другой стороны, если тайм-аут истекает первым, то он будет инициировать отключение. Фрагмент кода в ovm_root :: run_global_phase () управляющий задачей  run и выключением заключается в следующем:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;fork : task_based_phase&lt;br /&gt;
m_stop_process();&lt;br /&gt;
begin&lt;br /&gt;
m_do_phase_all(this,m_curr_phase);&lt;br /&gt;
wait fork;&lt;br /&gt;
end&lt;br /&gt;
#timeout ovm_report_error(&amp;quot;TIMOUT&amp;quot;,&lt;br /&gt;
$psprintf(&amp;quot;Watchdog timeout of '%0t' expired.&amp;quot;, timeout));&lt;br /&gt;
join_any&lt;br /&gt;
disable task_based_phase;&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
fork состоит из трех процессов, в том числе m_stop_process (), который управляет остановкой запросов; m_do_phase_all (), который приводит к выполнению всех  задач run выполняться параллельно, и тайм-аут. &lt;br /&gt;
Выражение disable после join_any вызывает оставшиеся процессы будут убиты. Так что, если тайм-аут истекает первым, то затем процессы stop и run задача будут убиты. Если run задач завершится первой, то процессы stop и timeout будут убиты. Наконец, если global_stop_request () вызывается, вызывается stop задачи, все они выполняются до конца, а затем stop процесс завершится первым и run задача и  timeout будут убиты.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:81.png</id>
		<title>Файл:81.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:81.png"/>
				<updated>2013-04-01T10:26:42Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM</id>
		<title>OVM/OVM методология/Механика OVM</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM"/>
				<updated>2013-03-28T08:21:21Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= Механика  OVM =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Библиотека OVM предоставляет много возможностей для построения testbenches. В этой главе мы ознакомимся с наиболее важными возможностями, которые вы будете использовать  почти во всех ваши testbenches.&lt;br /&gt;
&lt;br /&gt;
== Компоненты и иерархия ==&lt;br /&gt;
&lt;br /&gt;
Первичная структура для создания элементов testbench  является компонента. Компонента  в OVM – это аналогия  модулю в Verilog. Компонента OVM сконструирована на основе класса, который обеспечивает ему различные характеристики, чем модуль Verilog и имеет различные использования импликаций. Среди таких характеристик является то, что классы создаются во время выполнения, а не во время разработки, как модули. Таким образом, OVM отвечает за создание компонента и сборки их в иерархии.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|[[Файл:69.png]]&lt;br /&gt;
|&amp;lt;graph&amp;gt;&lt;br /&gt;
digraph G{&lt;br /&gt;
node[shape=&amp;quot;circle&amp;quot;,fontsize=&amp;quot;11&amp;quot;,margin=&amp;quot;0.03,0.03&amp;quot;]&lt;br /&gt;
edge[arrowhead=none]&lt;br /&gt;
env -&amp;gt; {c1  c2}&lt;br /&gt;
c1 -&amp;gt; {child11  child12}&lt;br /&gt;
c2 -&amp;gt; {child21  child22}&lt;br /&gt;
child11 [label=&amp;quot;child1&amp;quot;]&lt;br /&gt;
child12 [label=&amp;quot;child2&amp;quot;]&lt;br /&gt;
child21 [label=&amp;quot;child1&amp;quot;]&lt;br /&gt;
child22 [label=&amp;quot;child2&amp;quot;]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/graph&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Рисунок 4-1 иллюстрирует простую иерархию компонент. После  мы покажем,  как построить эту иерархию с помощью средств OVM  для создания компонент и объединения их в иерархии:&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Самый верхний узел, ENV, является корнем. Корень отличается тем, что он не имеет предка. Все остальные узлы имеют только одного предка. Каждый узел имеет имя. Расположение каждого узла в иерархии может быть идентифицировано уникальным full_name (путем), который строится на нанизывании имен всех узлов между корнем и узлов в запросе, разделяя их сепаратором иерархии, точкой (.). Например, путь к компоненту, второго потомка с2 -  top.c2.child2.&lt;br /&gt;
&lt;br /&gt;
Компонент в ОВМ является классом, производным от ovm_component. Простейшая компоненты -  листья, те, которые не имеют потомков.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;57 class child extends ovm_component;&lt;br /&gt;
58&lt;br /&gt;
59 function new(string name, ovm_component parent);&lt;br /&gt;
60 super.new(name, parent);&lt;br /&gt;
61 endfunction&lt;br /&gt;
62&lt;br /&gt;
63 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Конструктор имеет два параметра, имя компоненты и указатель на его предка. Название -  простое имя, а не иерархический путь. Предок  предоставляет место для подключения  новой компоненты в иерархии. Полный  путь потомка создается путем объединения имени потомка с полным именем предка, разделенный точкой (.).OVM предоставляет методы для получения и имени и полного пути компоненты:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;string get_name();&lt;br /&gt;
string get_full_name();&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подчиненные компоненты создаются  в функции build(), которая вызывается на этапе сборки (фазы объясняется далее в этой главе). Создание экземпляра компонента включает вызов new() для выделения памяти для нее и передачи соответствующих параметров в конструктор. В компоненте, показанной ниже, мы создаем две подчиненные компоненты, и child1 child2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;71 class component extends ovm_component;&lt;br /&gt;
72&lt;br /&gt;
73 child child1;&lt;br /&gt;
74 child child2;&lt;br /&gt;
75&lt;br /&gt;
76 function new(string name, ovm_component parent);&lt;br /&gt;
77 super.new(name, parent);&lt;br /&gt;
78 endfunction&lt;br /&gt;
79&lt;br /&gt;
80 function void build();&lt;br /&gt;
81 child1 = new(“child1”, this);&lt;br /&gt;
82 child2 = new(“child2”, this);&lt;br /&gt;
83 endfunction&lt;br /&gt;
84&lt;br /&gt;
85 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как component, ENV также создает экземпляры двух подчиненных компонент, c1 и c2. Вся иерархия  с корнем в модуле,  называемым top в нашем проекте.(??)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;131 module top;&lt;br /&gt;
132&lt;br /&gt;
133 env e;&lt;br /&gt;
134&lt;br /&gt;
135 initial begin&lt;br /&gt;
136 e = new(“env”);&lt;br /&gt;
137 run_test();&lt;br /&gt;
138 end&lt;br /&gt;
139&lt;br /&gt;
140 endmodule&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вызов new() создает верхний уровень окружающей среды. run_test () начинает выполнение testbench.&lt;br /&gt;
&lt;br /&gt;
В SystemVerilog, модули, интерфейсы и программные блоки создаются во время разработки, в то время как классы создаются после разработки, во время выполнения. Таким образом, чтобы создать иерархию классов, мы должны иметь интерфейс, модуль или программу, который содержит начальный блок, который начинает процесс создание компонент иерархии, основанных на классах. Интерфейсы предназначены для использования в качестве средства связи между двумя модулями и не очень хорошо подходят для использования в качестве корня иерархии классов. Программные  блоки или модули могут быть использованы как  корень (??). Для нашей простой иерархии, это не имеет значения. Позже, когда мы подключаем класс-компонент для модуля на основе аппаратных средств, мы увидим, что использование модуля предпочтительнее программным блокам.&lt;br /&gt;
&lt;br /&gt;
=== Перемещение по иерархии ===&lt;br /&gt;
&lt;br /&gt;
Мы можем исследовать структуры данных, используемые для реализации компонента иерархии с некоторыми методами, предусмотренными в ovm_component. Потомки компоненты хранятся в ассоциативном массиве. Этот массив напрямую не доступен, но он может быть доступен через иерархию API. Этот API-интерфейс похож на встроенные методы SystemVerilog, предназначенные для ассоциативных массивов.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;int get_first_child(ref string name);&lt;br /&gt;
int get_next_child(ref string name);&lt;br /&gt;
ovm_component get_child(string name);&lt;br /&gt;
int get_num_children();&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
get_first_child () и get_next_child () работают вместе для перебора множества потомков, содержащихся в компоненте. get_first_child () извлекает имя первого ребенка в списке. Он возвращает имя в качестве ссылки. get_next_child () возвращает имя следующего ребенка в списке. Он  возвращает 1, если имеется следующая имя потомка и 0, если был достигнут конец списка. get_child () преобразует имя в ссылку компоненты.&lt;br /&gt;
&lt;br /&gt;
Используя эти функции, мы можем пройти по компонентам иерархии.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;73 function void depth_first(ovm_component node,&lt;br /&gt;
74 int unsigned level = 0);&lt;br /&gt;
75&lt;br /&gt;
76 string name;&lt;br /&gt;
77&lt;br /&gt;
78 if(node == null)&lt;br /&gt;
79 return;&lt;br /&gt;
80&lt;br /&gt;
81 visit(node, level);&lt;br /&gt;
82&lt;br /&gt;
83 if(node.get_first_child(name))&lt;br /&gt;
84 do begin&lt;br /&gt;
85 depth_first(node.get_child(name), level+1);&lt;br /&gt;
86 end while(node.get_next_child(name));&lt;br /&gt;
87&lt;br /&gt;
88 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/utils/traverse.svh&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эта функция будет осуществляющая обход в глубину иерархии, называя visit(). Мы используем get_first_child () и get_next_child () для перебора списка каждого потомка в каждом узле. Для каждого итерации мы вызываем depth_first () рекурсивно. Для нашего маленького проекта, результат будет  следующий:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;pre&amp;gt;+ env&lt;br /&gt;
| + env.c1&lt;br /&gt;
| | env.c1.child1&lt;br /&gt;
| | env.c1.child2&lt;br /&gt;
| + env.c2&lt;br /&gt;
| | env.c2.child1&lt;br /&gt;
| | env.c2.child2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция visit() использует  глубину узла и  определяет является ли он конечным  для печати строки каждого узла.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Singleton Top ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Компоненты, которые не имеют предков (то есть,  параметр предок в конструкторе является нулевым) называют orphans.(??) В OVM вы можете создать столько компоненты без предков, сколько вам необходимо. Тем не менее, не существует такого понятия, как истинные orphans. Любой компонент, чей предок является  null,  относится к встроенному предку, называемому ovm_top. ovm_top состоит из одного экземпляра ovm_root. Это предок всех компонент, которые имеют предков. В самом деле, env  в нашем предыдущем примере является потомком ovm_top. Так как он не имеет предка, то автоматически получают ovm_top в качестве родителя.&lt;br /&gt;
&lt;br /&gt;
singleton -  широко известный  объектно-ориентированных шаблон проектирования, который имеет private (local) или protected конструкторы и статическую функцию get, которая возвращает тот же самый указатель не смотря на то, сколько раз она вызывается. Это означает, что возможно существование одного экземпляра, и что экземпляр создается  автоматически при вызове функции get. ovm_top содержит дескриптор экземпляра singleton  ovm_root(???). Он статически инициализируется при вызове ovm_root :: Get (). Вы можете вызвать ovm_root :: Get () в любое время, но в этом нет необходимости, так как ovm_top предусматривает это.&lt;br /&gt;
&lt;br /&gt;
Есть целый ряд полезных следствий существования singleton top-level компоненты. Одним из них является, что вы можете достичь любую компоненту из ovm_top. Если вы запустили алгоритм обхода иерархии, начиная с ovm_top, вы доберетесь до каждой компоненты в системе. Другим следствием является то, что любая компонента, включая порты, экспорт и каналы, которые создаются внутри модуля, достижима из ovm_top. Если вы хотите изменить отчет во всех обработчиках компонент, вы можете сделать это, вызвав  одну из иерархических функции отчетности в ovm_top. ovm_top содержит все механизмы синхронизации, которые объясняются далее в этой главе.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Connectivity ==&lt;br /&gt;
&lt;br /&gt;
Компоненты соединены друг с другом через TLM port и exports. Port и exports обеспечивают средства для компонент, или, точнее, процессы в компонентах для синхронизации и связи друг с другом. Port и exports -  объекты, которые образуют  пункт связывания для обеспечения межкомпонентной связи. Как уже говорилось в предыдущей главе, exports обеспечивают функции и задачи, которые могут быть вызваны из Port.&lt;br /&gt;
&lt;br /&gt;
[[Файл:70.png]]&lt;br /&gt;
 &lt;br /&gt;
Метод connect  портов и exports используется для связывания их вместе.&lt;br /&gt;
&lt;br /&gt;
initiator_port.connect(target.export)&lt;br /&gt;
&lt;br /&gt;
Этот метод создает ассоциацию, или связывание, между port и export так, что port может вызывать(запускать) задачи и функции из export. Для успешного соединения, типы  port и export должны совпадать. То есть, типы интерфейсов должны быть одинаковыми, и тип объекта, передающегося в интерфейс должны быть одинаковыми.&lt;br /&gt;
&lt;br /&gt;
=== Подключение через Иерархию ===&lt;br /&gt;
&lt;br /&gt;
Как и контактами в RTL дизайне, нам нужно подключиться к портам  TLM через иерархические границы. Рисунок 4-3 использует простой дизайн, чтобы показать, как сделать эти соединения. Этот проект содержит исходную компоненту с двумя port(выходами), которые в конечном итоге подключаются к двум входам(export). Для осуществления подключения между этими компонентами, мы должны расширить port и export на  уровень выше в иерархии.&lt;br /&gt;
 &lt;br /&gt;
[[Файл:71.png]]&lt;br /&gt;
&lt;br /&gt;
Компонент source содержит два порта, и first_put_port, second_put_port.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;65 class source extends ovm_component;&lt;br /&gt;
66&lt;br /&gt;
67 ovm_put_port #(trans_t) first_put_port;&lt;br /&gt;
68 ovm_put_port #(trans_t) second_put_port;&lt;br /&gt;
69&lt;br /&gt;
70 function new(string name, ovm_component parent);&lt;br /&gt;
71 super.new(name, parent);&lt;br /&gt;
72 endfunction&lt;br /&gt;
73&lt;br /&gt;
74 function void build();&lt;br /&gt;
75 first_put_port = new(“first_put_port”, this);&lt;br /&gt;
76 second_put_port = new(“second_put_port”, this);&lt;br /&gt;
77 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Кроме того, компонента sink создает экспорт и создает его в функции build. Экспорт связан с внутренним каналом, FIFO, от которого компонента может получать объекты во время выполнения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;126 class sink extends ovm_component;&lt;br /&gt;
127&lt;br /&gt;
128 ovm_put_export #(trans_t) put_export;&lt;br /&gt;
129 local tlm_fifo #(trans_t) fifo;&lt;br /&gt;
130&lt;br /&gt;
131 function new(string name, ovm_component parent);&lt;br /&gt;
132 super.new(name, parent);&lt;br /&gt;
133 endfunction&lt;br /&gt;
134&lt;br /&gt;
135 function void build();&lt;br /&gt;
136 put_export = new(“put_export”, this);&lt;br /&gt;
137 fifo = new(“fifo”, this);&lt;br /&gt;
138 endfunction&lt;br /&gt;
139&lt;br /&gt;
140 function void connect();&lt;br /&gt;
141 put_export.connect(fifo.put_export);&lt;br /&gt;
142 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
source_wrapper должен создать соединение между внутренним источником компоненты и  внешней ее границей. Он делает это соединение, создавая свои порты, которые имеют тот же тип, что и тип  портов lower-level, в нашем случае, тех, которые принадлежат к source.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;98 class source_wrapper extends ovm_component;&lt;br /&gt;
99&lt;br /&gt;
100 source s;&lt;br /&gt;
101 ovm_put_port #(trans_t) put_port1;&lt;br /&gt;
102 ovm_put_port #(trans_t) put_port2;&lt;br /&gt;
103&lt;br /&gt;
104 function new(string name, ovm_component parent);&lt;br /&gt;
105 super.new(name, parent);&lt;br /&gt;
106 endfunction&lt;br /&gt;
107&lt;br /&gt;
108 function void build();&lt;br /&gt;
109 s = new(“source”, this);&lt;br /&gt;
110 put_port1 = new(“put_port1”, this);&lt;br /&gt;
111 put_port2 = new(“put_port2”, this);&lt;br /&gt;
112 endfunction&lt;br /&gt;
113&lt;br /&gt;
114 function void connect();&lt;br /&gt;
115 s.first_put_port.connect(put_port1);&lt;br /&gt;
116 s.second_put_port.connect(put_port2);&lt;br /&gt;
117 endfunction&lt;br /&gt;
118&lt;br /&gt;
119 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После того как порты в source_wrapper были созданы, они подключаются к портам lower-level  компоненты source с помощью метода Connect. Видимость портов на более высоком уровне иерархии достигается  таким же образом, как мы видим  в sinker.&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
160 class sinker extends ovm_component;&lt;br /&gt;
161&lt;br /&gt;
162 ovm_put_export #(trans_t) first_put_export;&lt;br /&gt;
163 ovm_put_export #(trans_t) second_put_export;&lt;br /&gt;
164&lt;br /&gt;
165 sink sink1;&lt;br /&gt;
166 sink sink2;&lt;br /&gt;
167&lt;br /&gt;
168 function new(string name, ovm_component parent);&lt;br /&gt;
169 super.new(name, parent);&lt;br /&gt;
170 endfunction&lt;br /&gt;
171&lt;br /&gt;
172 function void build();&lt;br /&gt;
173 sink1 = new(“sink1”, this);&lt;br /&gt;
174 sink2 = new(“sink2”, this);&lt;br /&gt;
175 first_put_export = new(“first_put_export”, this);&lt;br /&gt;
176 second_put_export = new(“second_put_export”, this);&lt;br /&gt;
177 endfunction&lt;br /&gt;
178&lt;br /&gt;
179 function void connect();&lt;br /&gt;
180 first_put_export.connect(sink1.put_export);&lt;br /&gt;
181 second_put_export.connect(sink2.put_export);&lt;br /&gt;
182 endfunction&lt;br /&gt;
183&lt;br /&gt;
184 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Два нижних уровня компоненты sink и exports  создаются обычным способом. Затем они соединяются с помощью метода подключения export. Сейчас мы создаем port-export соединение между source_wrapper и sinker, также с помощью функции Connect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;192 class env extends ovm_component;&lt;br /&gt;
193&lt;br /&gt;
194 sinker s;&lt;br /&gt;
195 source_wrapper sw;&lt;br /&gt;
196&lt;br /&gt;
197 function new(string name, ovm_component parent = null);&lt;br /&gt;
198 super.new(name, parent);&lt;br /&gt;
199 endfunction&lt;br /&gt;
200&lt;br /&gt;
201 function void build();&lt;br /&gt;
202 s = new(“sinker”, this);&lt;br /&gt;
203 sw = new(“source_wrapper”, this);&lt;br /&gt;
204 endfunction&lt;br /&gt;
205&lt;br /&gt;
206 function void connect();&lt;br /&gt;
207 sw.put_port1.connect(s.first_put_export);&lt;br /&gt;
208 sw.put_port2.connect(s.second_put_export);&lt;br /&gt;
209 endfunction&lt;br /&gt;
210&lt;br /&gt;
211 task run; &lt;br /&gt;
212 global_stop_request();&lt;br /&gt;
213 endtask&lt;br /&gt;
214&lt;br /&gt;
215 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для новых пользователей часто может вызвать трудности определить,  порт или экспорт необходимо подключить и какой объект является аргументом. Вы можете легко понять это, следуя потоку управления  через систему. Общее правило  заключается в том, что запрашиваемый порт вызывается с помощью функции connect() и передачи в нее запрашиваемого port или export в качестве аргумента. Рисунок 4-4 показывает поток управления через иерархическую систему.&lt;br /&gt;
&lt;br /&gt;
[[Файл:72.png]]&lt;br /&gt;
 &lt;br /&gt;
Порт – объект  вызова и экспорт - объект вызванной функции или задачи. Вы можете воспринимать порты, как вызов экспорта. Так, в env, мы вызываем connect на put_ports с put_exports в качестве аргументов. Для port-to- port  и export-to-export иерархии соединений, вызывающий будет чуть менее очевиден. Если  вызов осуществляется со стороны порта, вы можете считать, что вызов lowest-level порта в иерархии является вызовом методов интерфейса  upper-level порта. Аналогично, если экспорт объект вызова, вы можете считать upper-level экспорт  вызовом lower-level export. В таблице ниже приведены возможные типы подключения:&lt;br /&gt;
 &lt;br /&gt;
[[Файл:73.png]]&lt;br /&gt;
&lt;br /&gt;
=== Примечание для пользователей AVM ===&lt;br /&gt;
&lt;br /&gt;
В AVM-3.0 соединения были сделаны аналогично: вызовам функции connect() из port и export. Кроме того, 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.)&lt;br /&gt;
&lt;br /&gt;
== Фазы ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для традиционных модулей Verilog  полная разработка  проекта и  его выполнение осуществялется в симуляторе. Так как OVM компоненты - это классы, они  создаются и подключаются, и их выполнение начинается за пределами Verilog разработчика. Компоненты создаются вызовом конструктора класса операцией new(), которая выделяет память и выполняет инициализацию. В то время как  Verilog время выполнения  run-time engine managing instantiation, разработка, и выполнение компонент на основе классов, функциональность  компонент разделена на фазы, и контроллер фазы OVM  управляет их исполнением. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Каждая фаза представлена в компоненте как виртуального метода (задачи или функция) с тривиальной реализацией по умолчанию. Эти  phase callbacks   реализуются разработчиком компоненты, который обеспечивает соответствующую функциональность. Фаза контроллера гарантирует, что фазы выполняются в надлежащем порядке. Набор стандартных фаз показано в следующей таблице:&lt;br /&gt;
&lt;br /&gt;
[[Файл:74.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:75.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Каждая фаза имеет определенную цель. разработчики Компонент  должны заботиться о том, чтобы функциональность реализованная в каждой фазе  callback соответствовала фазе определения.&lt;br /&gt;
&lt;br /&gt;
•	'''New''' технически не является фазой, в том, что она не управляется фазой контроллера. Тем не менее, для каждого компонента, конструктор должен быть выполнен и завершен в целях создания компоненты. Таким образом,  new должен выполниться до build() или любого другого последующего этапах  выполнения.&lt;br /&gt;
&lt;br /&gt;
•	'''build'''  это место, где новые компоненты, порты и экспорт создаются и настраиваются. Это также рекомендуемое место для вызова set_config_ * и get_config_ * (см. Section4.4). &lt;br /&gt;
&lt;br /&gt;
•	'''connect''' – место, где компоненты, порты и экспорт созданные в  build() связываются.&lt;br /&gt;
&lt;br /&gt;
•	'''end_of_elaboration''' здесь вы можете выполнить изменение конфигураций, зная, что разработка уже завершена. То есть, вы можете предположить, что все компоненты созданы и соединены. &lt;br /&gt;
&lt;br /&gt;
•	'''start_of_simulation''' выполняется только до времени 0.&lt;br /&gt;
&lt;br /&gt;
•	'''run'''    pre-defined task фазой. Все выполняемые задачи работают параллельно. Каждая выполняемая задача продолжается, пока ее управление не достигнет оператора EndTask или она явно будет прекращена. Позже в этой главе мы обсудим, как завершать testbenches.&lt;br /&gt;
&lt;br /&gt;
•	'''extract'''  предназначен для сбора информации, касающейся покрытия или другой информации о том, как ответить на  вопросы testbench.  &lt;br /&gt;
&lt;br /&gt;
•	'''проверка''' - место, где выполняется любая проверка правильности или проверка валидации извлеченных данных. &lt;br /&gt;
&lt;br /&gt;
•	'''отчет''' -место , где делаются окончательные отчеты.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ниже простой пример,  использующий ovm_report_info () для иллюстрации порядка выполнения фаз.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;38 class sub_component extends ovm_component;&lt;br /&gt;
39&lt;br /&gt;
40 function new(string name, ovm_component parent);&lt;br /&gt;
41 super.new(name, parent);&lt;br /&gt;
42 endfunction&lt;br /&gt;
43&lt;br /&gt;
44 function void build();&lt;br /&gt;
45 ovm_report_info(“build”, ““);&lt;br /&gt;
46 endfunction&lt;br /&gt;
47&lt;br /&gt;
48 function void connect();&lt;br /&gt;
49 ovm_report_info(“connect”, ““);&lt;br /&gt;
50 endfunction&lt;br /&gt;
51&lt;br /&gt;
52 function void end_of_elaboration();&lt;br /&gt;
53 ovm_report_info(“end_of_elaboration”, ““);&lt;br /&gt;
54 endfunction&lt;br /&gt;
55&lt;br /&gt;
56 function void start_of_simulation();&lt;br /&gt;
57 ovm_report_info(“start_of_simulation”, ““);&lt;br /&gt;
58 endfunction&lt;br /&gt;
59&lt;br /&gt;
60 task run();&lt;br /&gt;
61 ovm_report_info(“run”, ““);&lt;br /&gt;
62 endtask&lt;br /&gt;
63&lt;br /&gt;
64 function void extract();&lt;br /&gt;
65 ovm_report_info(“extract”, ““);&lt;br /&gt;
66 endfunction&lt;br /&gt;
67&lt;br /&gt;
68 function void check();&lt;br /&gt;
69 ovm_report_info(“check”, ““);&lt;br /&gt;
70 endfunction&lt;br /&gt;
71&lt;br /&gt;
72 function void report();&lt;br /&gt;
73 ovm_report_info(“report”, ““);&lt;br /&gt;
74 endfunction&lt;br /&gt;
75&lt;br /&gt;
76 endclass&lt;br /&gt;
file: 04_OVM_mechanics/03_phases/top.sv&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В компоненте верхнего уровня, мы создаем две  компоненты, каждая из которых в свою очередь создает две sub_component. sub_components являющимися такими же, как и сами компоненты; каждая фаза обратного  callback просто печатает строку, определяющую фазу. При выполнении, вы получите следующий результат:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;OVM_INFO @ 0 [RNTST] Running test ...&lt;br /&gt;
OVM_INFO @ 0: env.c1 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [run]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s1 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s2 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c1 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s1 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s2 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c2 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s1 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s2 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c1 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s1 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s2 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c2 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s1 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s2 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c1 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s1 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s2 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c2 [report]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Вы видите, что build()выполняется сверху вниз и остальные фазы запуска снизу вверх. Вы также можете видеть, что каждый этап завершается во всех компонентах до начала следующего этапа. Таким образом, в connect(), например, вы можете расчитывать, что build() завершена во всех компонентах. Вы также заметите, что время  запускается после  фазы run. В нашем примере задача run - тривиальна, она просто выполняет задержку времени на единицу (# 1).&lt;br /&gt;
&lt;br /&gt;
un_test (), упоминалось ранее в разделе 4.1, инициирует фазу выполнение. Он начинает выполнение фаз по порядку и контролирует, что каждый этап завершен до начала следующего.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==	Config ==&lt;br /&gt;
&lt;br /&gt;
Для увеличения повторного использования компонент, желательно объявит их с параметрами, которые могут быть внешне настроены. Конфигурационные средства обеспечивают возможности для этого. Они основаны на базе пар имя-значение, и называются configuration items, которые организованы иерархически. Каждая компонента содержит таблицу конфигураций элементов конфигурации,  так как компоненты организованы в качестве дерева, каждый элемент в базе данных может быть уникально размещен по расположению компоненты и имени элемента конфигурации&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:76.png]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Класс ovm_component содержит два набора методов для  конфигурации размещения элементов в базу данных и потом для их получения. Это  set_config_ * и * get_config_. В таблице ниже приведены оба набора.&lt;br /&gt;
&lt;br /&gt;
[[Файл:77.png]] &lt;br /&gt;
&lt;br /&gt;
Set_config_ * функция помещает элемент в конфигурационную базу данных текущего компонента, то есть в экземпляре компонента, в котором функция вызывается. Каждая из этих функций принимают три аргумента, имя, field_name и значение. Аргумент name -  имя пути, который представляет набор компонент, которые должны соответсвовать кофигурациям этого элемента. name используется в * get_config_, чтобы найти элементы в базе данных конфигурации.  field_name -  это имя поля и должно быть уникальным в пределах текущей базы конфигураций. value является частью пары имя-значение и его тип может быть строка, INT, или ovm_object, в зависимости от вызываемой функции. Кроме того, set_config_object принимает копию аргумента для указания, был ли объект передан в качестве значения так как значение должно быть клонировано прежде чем оно будет помещено в базу конфигурации. &lt;br /&gt;
Функции get_config_ * извлекает элементов из базы данных конфигураций.  Эти функции принимают только два аргумента, имя поля и INOUT переменную, который содержит значение расположения элемента. Они также возвращают бит, чтобы указать был ли удачно расположен запрашиваемый объект. * Get_config_ функции не принимают аргументы имени пути, как их set_config_ * коллеги, поскольку они используют путь к текущему компоненту, как ссылку, чтобы найти элементы конфигурации. Они разработаны, чтобы узнать, есть значение элемента конфигурации для текущего контекста, то есть, компонент, в котором функция get_config_ * вызывается.&lt;br /&gt;
&lt;br /&gt;
Алгоритм поиска для извлечения элементов конфигурации использует имя пути компонента запроса и путь к файлу вставляемый в каждый  item. Он начинается с поиска конфигурационных элементов в базе данных в верхней компоненте по field_name. Если такой элемент существует, то он затем спрашивает, соответствует ли указанный путь к файлу пути имени компоненты. Если элемента с указанным field_name нет или имена путей не совпадают, то поиск идет с дочерней компоненты. Этот процесс продолжается до соответствия или  до достижения компоненты начала поиска.&lt;br /&gt;
&lt;br /&gt;
Путь  каждого элемент конфигурации может быть регулярным выражением. Таким образом, мы  используем алгоритм соответствия регулярных выражений для нахождения совпадения с  путем запрошенной компоненты и именем пути элемента конфигурации. Эффект заключается в соответствии иерархической области. &lt;br /&gt;
В качестве примера, рассмотрим простую иерархию на рисунке 4-5. Давайте предположим, что в  env::build()мы запускаем два вызова set_config_ *:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;112 function void build();&lt;br /&gt;
113 c1 = new(“c1”, this);&lt;br /&gt;
114 c2 = new(“c2”, this);&lt;br /&gt;
115&lt;br /&gt;
116 set_config_int(“c2.*”, “i”, 42);&lt;br /&gt;
117 set_config_int(“*”, “t”, 19);&lt;br /&gt;
118 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/04_config/top.sv&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Это вызовет запись двух конфигурационных элементов в базу данных в env. Обратите внимание на звездочку (*) в имени пути. Имя пути в вызове set_config_ * -  регулярное выражение, и дикие символы  используются для указать несколько областей, к которыми элемент применяется. По элементу i, c2. * показывает, что в любой области ниже c2 в иерархии, i будет  определять указанное значение. В этом случае указанное значение -  42. Если вы опустите звездочку, то элемент конфигурации применяется только к c2, а не к любому из своих потомков. &lt;br /&gt;
Состояние конфигурации базы данных для каждого компонента в иерархии  после  вызова set_config_ * показано на рисунке 4-6,&lt;br /&gt;
&lt;br /&gt;
[[Файл:78.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
пусть в элементе top.c1.child1 мы осуществим вызов:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;int i;&lt;br /&gt;
...&lt;br /&gt;
get_config_int(“i”, i)&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Поиск задает вопрос: Каково значение конфигурации i в иерархических рамках top.c1.child1? Чтобы ответить на этот вопрос, конфигурации базы данных в env ищется в первую очередь. Запись i там говорит, что значение i совпадающее с  env.c2. *  -  42. Тем не менее, компоненты, из которых был сделан запрос, находятся в c1 sub-hierarchy. Таким образом,  не существует соответствия, а get_config_int () возвращает статус неудачи. Запрос в любую компоненту, которая является дочерней c2 будет успешно завершен и вернется значение 42. &lt;br /&gt;
&lt;br /&gt;
Ниже приведен код для функцию build() дочерней компоненты. Это место где эти компоненты ищут конфигурационные значения i и t.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;60 function void build();&lt;br /&gt;
61&lt;br /&gt;
62 string msg;&lt;br /&gt;
63&lt;br /&gt;
64 if(!get_config_int(“t”, t)) begin&lt;br /&gt;
65 $sformat(msg, “no value for t found in config&lt;br /&gt;
database, using default value of %0d”, t);&lt;br /&gt;
66 ovm_report_warning(“build”, msg);&lt;br /&gt;
67 end&lt;br /&gt;
68&lt;br /&gt;
69 if(!get_config_int(“i”, i)) begin&lt;br /&gt;
70 $sformat(msg, “no value for i found in config&lt;br /&gt;
database, using default value of %0d”, i);&lt;br /&gt;
71 ovm_report_warning(“build”, msg);&lt;br /&gt;
72 end&lt;br /&gt;
73&lt;br /&gt;
74 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/04_config/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующий пример показывает вывода  при выполнении этого проекта:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# OVM_INFO @ 0 [RNTST] Running test ...&lt;br /&gt;
# OVM_WARNING @ 0: env.c1.child1 [build] no value for i found in&lt;br /&gt;
config database, using default value of 91&lt;br /&gt;
# OVM_WARNING @ 0: env.c1.child2 [build] no value for i found in&lt;br /&gt;
config database, using default value of 91&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Запрос на  конфигурационный элемент t был успешным во всех контекстах с того момента как вызов set_config_int установил, что tдоступно во всех контекстах. Два запроса на элемент конфигурации i были успешными, и два нет. Результат получился такой потому что мы ограничены наличием i только компонентах на уровне или ниже env.c2. Компоненты на уровне или ниже c1 не могут видеть элемент конфигурации i из-за того, как мы создали базу данных конфигурации.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Конфигурация и фазы ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь, когда мы знаем, что набор вызовов для размещения и извлечение элементов в конфигурацию базы данных, следующей задачей, является эффективное применение этих функции для настройки компонент. Конфигурация может быть использована для изменения поведения или структуры testbench. Как правило, поведенческий  и структурный режимы определяются, когда testbench начинает работу, поэтому он является наиболее удобным местом для установки параметров конфигурации на одной из ранних стадий, таких как new, build или connect.&lt;br /&gt;
&lt;br /&gt;
Из таблицы фаз, вы можете увидеть, что new и build являются нисходящими фазами, в то время как все остальные фазы восходящие. Так что, если вы хотите установить элемент конфигурации в базу данных на более высокий уровень контекста, чтобы он был подхвачен более низким уровнем, вы должны вызвать функцию set_config_ * в new или build фазе. Фазы выполняется дискретно, это означает, что каждая фаза выполняется до начала следующей. Вы можете установить элементы конфигурации в new или build фазу и извлекать их для использования в установке поведенческого режима или изменения топологии на этапе build. В вашей функции build, сначала вызовите get_config_ * для извлечения элементов из более высоких уровней иерархии для управления конфигурацией текущего уровня. Затем добавьте вызов set_config_ * для  помещения элементов конфигурации в базу данных для использования компонентами на более низких уровнях иерархии. Наконец, используя соответствующие элементы конфигурации, обработайте компоненты. Важно вызвать get_config_ * вначале, потому, что указанная информация может влиять на значения, которые установлены на более низких уровнях иерархии.&lt;br /&gt;
&lt;br /&gt;
Например, настройка топологии включает в себя установку параметров топологии&lt;br /&gt;
на верхнем уровне среды, а затем применение эти параметров в различных компонентах, которые находятся ниже верхнего уровня среды в иерархии. Наш пример имеет шину, которая может иметь любое количество masters или slaves. Число masters и slaves расположены в верхнем уровне среды. Модель шину подхватывает эту конфигурационную информацию и использует ее для построения шины. В build функции верхнего уровня окружающей среды, мы создаем модель шины и настраиваем ее с числом masters и slaves.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;129 function void build();&lt;br /&gt;
130 set_config_int(“bus”, “masters”, 4);&lt;br /&gt;
131 set_config_int(“bus”, “slaves”, 8);&lt;br /&gt;
132 b = new(“bus”, this);&lt;br /&gt;
133 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/05_config_topo/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Модель шины построена таким образом, что число masters и slaves не фиксировано. Вместо этого,  количество приходит из конфигурации системы.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;90 function void build();&lt;br /&gt;
91&lt;br /&gt;
92 int unsigned i;&lt;br /&gt;
93&lt;br /&gt;
94 if(!get_config_int(“masters”, masters)) begin&lt;br /&gt;
95 $sformat(msg, “\”masters\” is not in the&lt;br /&gt;
configuration database, using default value of %0d”, masters);&lt;br /&gt;
96 ovm_report_warning(“build”, msg);&lt;br /&gt;
97 end&lt;br /&gt;
98&lt;br /&gt;
99 for(i = 0; i &amp;lt; masters; i++) begin&lt;br /&gt;
100 $sformat(name, “master_%0d”, i);&lt;br /&gt;
101 m = new(name, this);&lt;br /&gt;
102 end&lt;br /&gt;
103&lt;br /&gt;
104 if(!get_config_int(“slaves”, slaves)) begin&lt;br /&gt;
105 $sformat(msg, “\”slaves\” is not in the configuration&lt;br /&gt;
database, using default value of %0d”, slaves);&lt;br /&gt;
106 ovm_report_warning(“build”, msg);&lt;br /&gt;
107 end&lt;br /&gt;
108&lt;br /&gt;
109 for(i = 0; i &amp;lt; slaves; i++) begin&lt;br /&gt;
110 $sformat(name, “slave_%0d”, i);&lt;br /&gt;
111 s = new(name, this);&lt;br /&gt;
112 end&lt;br /&gt;
113&lt;br /&gt;
114 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/05_config_topo/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В функции build для модели шины, схема получает необходимую конфигурационную информацию, используя вызовы get_config_int. В каждом случае, Возвращаемое значение проверяется, чтобы определить, была ли получена запрошенная конфигурационная информация. Если нет, сообщение об ошибке предупреждает, что конфигурационный элемент не найден и что будет использовано значение по умолчанию. С точки зрения Best Practices, важно убедиться, что возвращается значение, проверено и выдается предупреждение, в случае ошибки. Без этой проверки, то, что  используется значения по умолчанию, могло остаться незамеченным. В некоторых случаях это может быть приемлемо, в других случаях - нет. Человек,  создающий модель шины не может знать всех обстоятельств, при которых модель будет использоваться. Поэтому важно сделать все возможное, чтобы  модель была надежной. Проверка возвращаемых значений и выдача соответствующих сообщений является одним из способов повышения надежности модели.&lt;br /&gt;
&lt;br /&gt;
В цикле, где мы строим masters, ссылки на каждого нового master сохраняются в той же переменной, m. Каждый новый master заменяет предыдущий. Мы не используем массив для хранения всех компонент. В каждой итерации, мы используем $ sformat, чтобы создать уникальное имя. Конструктор, new(), вызывает super.new (), конструктор в ovm_component базовом классе, который отвечает за вставку вновь созданную компоненту потомка в родительский список. Нет  необходимости явно сохранять компоненту, потому что родительская компонента сделает это за нас. Цикл для создания slaves организован таким же образом.&lt;br /&gt;
&lt;br /&gt;
== Factory(Фабрика) ==&lt;br /&gt;
&lt;br /&gt;
Структура testbench определяется организацией компонент в иерархии и то, как эти объекты связаны между собой. Поведение testbench определяется процедурным кодом в фазе callbacks—build, connect, run и так далее. Есть моменты, когда это необходимо изменить поведение или часть структуры извне(снаружи), то есть во время выполнения, не касаясь, кода testbench. Например, чтобы ввести ошибку в систему, вы можете заменить обычный драйвер  драйвером с ошибкой, который умышленно вводит ошибки. Вместо повторного кодирования окружающей среды для использования другого драйвера, вы можете использовать factory, чтобы сделать замену автоматически.&lt;br /&gt;
&lt;br /&gt;
Factory обеспечивает средства для замены одного объекта на другой без использования текстового редактора для изменения testbench. Вместо создания объект с помощью new(), вы вызываете функцию create  из factory. Factory  хранит список зарегистрированных объектов и, при необходимости, набор переопределения связанный с каждой из них. При создании объекта с помощью factory, список переопределения проверяется на наличие такого объекта. Если он есть, то переопределенный объект возвращается. В противном случае, зарегистрированный объект возвращается. &lt;br /&gt;
&lt;br /&gt;
Factory  является OVM структурой данных. Она  глобальна в области видимости, и только один элемент factory может существовать  (то есть, это singleton). Она служит в качестве полиморфных конструкторов, одна функция, которая позволяет создавать различные объекты. Она  обеспечивает средства для регистрации объектов, и для их переопределения. Объекты, зарегистрированные как переопределения,  должны быть производным от объекта, от которого они переопределялись. Для того, чтобы одна функция возвращала несколько объектов, каждый из этих объектов должны быть получен от общего базового класса.&lt;br /&gt;
Важным компонентом factory является оболочка, класс, который описывает объект, который мы хотим создать с  помощью factory. Структура данных factory основывается на таблице оболочек, проиндексированной по ключу. Оболочка имеет функцию create(), которая обращается к  конструктору обернутого объекта.&lt;br /&gt;
&lt;br /&gt;
Использование factory состоит из трех этапов: регистрация, настройки переопределение и создание. На первом этапе необходимо зарегистрировать объект с помощью factory. На втором шаге, добавляется переопределение зарегистрированного объекта. На третьем этапе, вы создаете объект с помощью factory, который будет возвращать либо первоначально зарегистрированный объект или переопределенный, в зависимости от того, переопределение было передано в качестве  запрашиваемого объекта.&lt;br /&gt;
&lt;br /&gt;
=== Как работает Фабрика ===&lt;br /&gt;
&lt;br /&gt;
Термин factory-Фабрика был придуман The Gang of Four для использования в программном обеспечении в их книге Design Patterns: Elements of Reusable Object-Oriented Software. В этой книге, они определили шаблон, который они называют abstract factory, как интерфейс для создания семейств связанных объектов. Они определили шаблон factory method  в качестве интерфейса для создания объектов, относящихся к подклассам для определения, какой объект создавать. Factory  в OVM является комбинацией этих двух  шаблонов. Она предоставляет средства для создания семейства объектов, а также предоставляет средства для определения, какой именно объект создавать структуре данных фабрики.&lt;br /&gt;
&lt;br /&gt;
Factory OVM основана на структуре данных, которая  отображает необходимые типы в переопределенные. По сути, организация представляет собой ассоциативный массив типов, ключом в котором является также тип. При регистрации типа при помощи фабрики, его переопределенным типом является он сам. Таким образом, по умолчанию, когда вы запрашиваете объект этого типа, вы получаете только тип. Фабрика  также предоставляет средства для замены переопределенных типов другими типами, чтобы вы могли получить переопределенные типы, которые отличаются от зарегистрированных типов.&lt;br /&gt;
&lt;br /&gt;
[[Файл:79.png]]&lt;br /&gt;
&lt;br /&gt;
Следующий пример является сильно упрощенной фабрикой, которая показывает,  как OVM фабрика работает. Фабрика  сохраняет основные структуры OVM фабрики, но многие детали были удалены для более понятной иллюстрации. Наша фабрика реализуется в четырех классах, двух базовых классах и двух производных классах, которые делают реальную работу. Двумя базовыми классами являются object_base и wrapper_base. Все объекты, зарегистрированные в фабрике, должны быть получены (в конечном счете) от object_base, а wrapper_base является базовым классом для параметризованной обертки. Factory  - это singleton, который содержит ассоциативный массив, содержащий экземпляры обертки. Наконец, обертки являются производными от wrapper_base и параметризованными классами, которые представляют уникальные типы. &lt;br /&gt;
&lt;br /&gt;
Для нашей фабрики, базовые классы тривиальны:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;46 class object_base;&lt;br /&gt;
47 virtual function void print();&lt;br /&gt;
48 $display(“object_base”);&lt;br /&gt;
49 endfunction&lt;br /&gt;
50 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&lt;br /&gt;
59 class wrapper_base;&lt;br /&gt;
60 virtual function object_base create_object();&lt;br /&gt;
61 return null;&lt;br /&gt;
62 endfunction&lt;br /&gt;
63 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
object_base имеет виртуальную функцию print(), которую мы используем для проверки типов, созданных на фабрике. wrapper_base имеет виртуальную функцию create(), полиморфный конструктор, который используется для создания новых объектов.&lt;br /&gt;
factory - это singleton, то есть его конструктор локальный, и он содержит статическую ссылку  на самого себя. Единственный способ создать экземпляр factory является вызов factory::get(). factory содержит ассоциативный массив, который отображает запрашиваемые типы wrapper_base в   переопределенные типы wrapper_base.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;73 class factory;&lt;br /&gt;
74&lt;br /&gt;
75 static factory f;&lt;br /&gt;
76 wrapper_base override_map[wrapper_base];&lt;br /&gt;
77&lt;br /&gt;
78 local function new();&lt;br /&gt;
79 endfunction&lt;br /&gt;
80&lt;br /&gt;
81 static function factory get();&lt;br /&gt;
82 if(f == null)&lt;br /&gt;
83 f = new();&lt;br /&gt;
84 return f;&lt;br /&gt;
85 endfunction&lt;br /&gt;
86&lt;br /&gt;
87 function void register(wrapper_base w);&lt;br /&gt;
88 override_map[w] = w;&lt;br /&gt;
89 endfunction&lt;br /&gt;
90&lt;br /&gt;
91 function void set_override(wrapper_base requested_type,&lt;br /&gt;
92 wrapper_base override_type);&lt;br /&gt;
93 override_map[requested_type] = override_type;&lt;br /&gt;
94 endfunction&lt;br /&gt;
95&lt;br /&gt;
96 function object_base create(wrapper_base&lt;br /&gt;
97 requested_type);&lt;br /&gt;
98 object_base obj;&lt;br /&gt;
99 wrapper_base override_type =&lt;br /&gt;
100 override_map[requested_type];&lt;br /&gt;
101 obj = override_type.create_object();&lt;br /&gt;
102 return obj;&lt;br /&gt;
103 endfunction&lt;br /&gt;
104&lt;br /&gt;
105 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Метод register() добавляет новую запись в карту. Первоначально, при регистрации, тип не имеет переопределения. Таким образом, мы записали в карту тип, указывающий сам на себя. Set_override () метод заменяет запись в карте новым переопределенным типом. create() метод ищет переопределения на указанный тип, инициирует создание переопределенного типа и возвращает вновь созданный объект.&lt;br /&gt;
Класс-оболочка является наиболее интересным классом в нашем созвездии factory-related классов. Хотя он довольно прост, он делает большую часть тяжелой работы. Это основной интерфейс для фабрики, и большинство операций, которые производятся фабрикой, проходят через интерфейс оболочки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;118 class wrapper #(type T=object_base) extends wrapper_base;&lt;br /&gt;
119&lt;br /&gt;
120 typedef wrapper#(T) this_type;&lt;br /&gt;
121&lt;br /&gt;
122 static this_type type_handle = get_type();&lt;br /&gt;
123&lt;br /&gt;
124 local function new();&lt;br /&gt;
125 endfunction&lt;br /&gt;
126&lt;br /&gt;
127 function object_base create_object();&lt;br /&gt;
128 T t = new();&lt;br /&gt;
129 return t;&lt;br /&gt;
130 endfunction&lt;br /&gt;
131&lt;br /&gt;
132 static function T create();&lt;br /&gt;
133 T obj;&lt;br /&gt;
134 factory f = factory::get();&lt;br /&gt;
135 assert($cast(obj, f.create(get_type())));&lt;br /&gt;
136 return obj;&lt;br /&gt;
137 endfunction&lt;br /&gt;
138&lt;br /&gt;
139 static function this_type get_type();&lt;br /&gt;
140 factory f;&lt;br /&gt;
141 if(type_handle == null) begin&lt;br /&gt;
142 type_handle = new();&lt;br /&gt;
143 f = factory::get();&lt;br /&gt;
144 f.register(type_handle);&lt;br /&gt;
145 end&lt;br /&gt;
146 return type_handle; &lt;br /&gt;
147 endfunction&lt;br /&gt;
148&lt;br /&gt;
149 static function void set_override(wrapper_base&lt;br /&gt;
150 override_type);&lt;br /&gt;
151 factory f = factory::get();&lt;br /&gt;
152 f.set_override(type_handle, override_type);&lt;br /&gt;
153 endfunction&lt;br /&gt;
154&lt;br /&gt;
155 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Все функции в wrapper#() являются статическими, за исключением конструктора и create_object (). Мы можем выполнить его статические функции, не заботясь о том, был ли они явно создан экземпляр класса. Так как каждая обертка специализации уникальна. Это означает, что статическая переменная type_handle является уникальной и может быть использован в качестве прокси-сервера для обернутого типа (то есть, типа, передающегося в качестве параметра, который используется для специализации класса). Так как тип является уникальным, и большинство методов являются статическими, мы можем рассматривать типа  больше как тип, а не объект.&lt;br /&gt;
&lt;br /&gt;
Тип инициализируется статически. Это происходит в следующей строке:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;static wrapper#(T) type_handle = get_type();&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Функция get_type () вызывается во время статической инициализации, которая не только создает экземпляр оболочки, но и регистрирует его в фабрике. Для регистрации класса в фабрике, сначала специализируется обертка с типом объекта, который вы оборачиваете. Используйте typedef  для специализации оболочки, как показано в следующем примере:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;typedef wrapper#(some_type) type_id;&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Это определение типа создает оболочку для SOME_TYPE типа, тип, производный от object_base.&lt;br /&gt;
Рисунок 4-8 иллюстрирует, как использовать фабрику с некоторыми  классами A, B, и С, которые являются производными от family_base.&lt;br /&gt;
&lt;br /&gt;
[[Файл:80.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Чтобы зарегистрировать классы с помощью фабрики, каждый из них имеет typedef  из обертки, параметризованной со своим типом. Ниже класс А. Класс B и C  похожи. Каждый из них имеет typedef, которая специализируется обертку.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;169 class A extends family_base;&lt;br /&gt;
170&lt;br /&gt;
171 typedef wrapper#(A) type_id;&lt;br /&gt;
172&lt;br /&gt;
173 virtual function void print();&lt;br /&gt;
174 $display(“A”);&lt;br /&gt;
175 endfunction&lt;br /&gt;
176 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
Ниже приводится короткая программа, которая использует фабрику.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;206 function void run();&lt;br /&gt;
207&lt;br /&gt;
208 f = factory::get();&lt;br /&gt;
209&lt;br /&gt;
210 h = family_base::type_id::create();&lt;br /&gt;
211 h.print();&lt;br /&gt;
212&lt;br /&gt;
213&lt;br /&gt;
family_base::type_id::set_override(B::type_id::get_type());&lt;br /&gt;
214&lt;br /&gt;
215 h = family_base::type_id::create();&lt;br /&gt;
216 h.print();&lt;br /&gt;
217&lt;br /&gt;
218 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При написании кода интенсивно используют оператор (::),  он используется для обращения к статической функции в factory и в wrapper(). Сначала, мы получаем уникальный экземпляр структуры данных фабрики, затем создаем экземпляр объекта family_base. family_base :: type_id :: get_type() является статической функцией внутри специально обвернутого family_base. Мы  проверяем, что экземпляр family_base создается путем вызова print(). Далее, определяем переопределение B для family_base. Опять же, мы создаем экземпляр family_base. На этот раз, так как переопределение сейчас находится на месте, вместо того, чтобы получить экземпляр family_base, мы получаем экземпляр B.&lt;br /&gt;
&lt;br /&gt;
Наша фабрика не содержит все функциональные возможности фабрики, реализованной в OVM. Фабрика OVM обеспечивает отображение  строк (имен) в типы. Это позволяет переопределить цепочки, в то время, как фабрика этого не делает. Например, если B переопределяет А и C переопределяет B, когда вы просите экземпляр А, вы получите экземпляр C. Фабрика  OVM поддерживает два начальных базовых класса, ovm_object и ovm_component для зарегистрированных объектов, и метод create() для обоих, в то время, фабрика имеет только один первичный класс, object_base.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== The OVM Factory API  ===&lt;br /&gt;
&lt;br /&gt;
В этом разделе мы рассмотрим более подробно API  OVM фабрики. Он состоит из двух частей, type-based фабрики и string-based фабрики. В string-based фабрике запрашиваемый тип идентифицируются строкой имени. В type-based фабрике, тип идентифицируются по поддерживаемому типу(????). Один тип может быть зарегистрирован двумя способами. Методы выполнения  трех этапов (регистрация, установка коррекции, и создание) немного отличаются в каждом случае. Сначала мы рассмотрим type-based фабрику. Вот компонент, называемый драйвер, который регистрирует себя на основе type-based  фабрики.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;49 class driver extends ovm_component;&lt;br /&gt;
50&lt;br /&gt;
51 typedef ovm_component_registry#(driver) type_id;&lt;br /&gt;
52&lt;br /&gt;
53 static function type_id get_type();&lt;br /&gt;
54 return type_id::get();&lt;br /&gt;
55 endfunction&lt;br /&gt;
56&lt;br /&gt;
57 function string get_type_name();&lt;br /&gt;
58 return “driver”;&lt;br /&gt;
59 endfunction&lt;br /&gt;
60&lt;br /&gt;
61 function new(string name, ovm_component parent);&lt;br /&gt;
62 super.new(name, parent);&lt;br /&gt;
63 endfunction&lt;br /&gt;
64&lt;br /&gt;
65 endclass&lt;br /&gt;
file: 04_OVM_mechanics/06_factory/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Есть две части для регистрации, поддержка  typedef ovm_component_registry # () и supplying статической функции get_type ().typedef создает специализацию ovm_component_registry помощью компонент типа драйвер в качестве параметра. ovm_component_registry # () класс имеет статический инициализатор, который регистрирует, используя  структуру фабрики. Таким образом, создание специализации, используя typedef , влечет за собой идентификацию класса по параметру, driver в этом случае, должен быть зарегистрирован в фабрике.&lt;br /&gt;
Задание переопределения является простой причиной вызова функции set_override и передача переопределенного типа в качестве аргумента, как показано ниже:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;105&lt;br /&gt;
driver::type_id::set_type_override(error_driver::get_type());&lt;br /&gt;
file: 04_OVM_mechanics/06_factory/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Эта строка выглядит довольно сложными, но на самом деле, это довольно просто. Давайте преобразуем выражение, чтобы полностью понять, что оно значит.&lt;br /&gt;
&lt;br /&gt;
driver – запрашиваемый тип.&lt;br /&gt;
&lt;br /&gt;
driver::type_id – тип обертки.&lt;br /&gt;
&lt;br /&gt;
driver::type_id::set_type_override – the set override function in the specialized wrapper. This is a static function, which is why &lt;br /&gt;
you need the :: scope operator to refer to it.&lt;br /&gt;
&lt;br /&gt;
error_driver – переопределенный тип.&lt;br /&gt;
&lt;br /&gt;
error_driver::get_type() – статическая функция, возвращаемая тип, поддерживаемый error_driver.&lt;br /&gt;
&lt;br /&gt;
Для создания экземпляра класса с помощью фабрики, мы называем метод создания фабрики, как показано ниже:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;102 d1 = driver::type_id::create(“d1”, this);&lt;br /&gt;
file: 04_OVM_mechanics/06_factory/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Синтаксис :: работает так же, как и в предыдущем примере - driver относится к запрашиваемому типу, driver::type_id относится к типу специализированной обертки и driver::type_id::create относится к  функции создать в специализированной обертке. Этот оператор создает экземпляр driver. Разница между вызовом Create () и new() является то, что create() обращаться к фабрике на наличие переопределенных типов. Если это так, Create ()  возвращает объект переопределенного типа.&lt;br /&gt;
&lt;br /&gt;
Теперь давайте рассмотрим string-based фабрику. Механизм регистрации string-based фабрики опирается на определение типа как на основе типа фабрики,  как показано ниже:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;48 typedef ovm_component_registry#(driver, “driver”)&lt;br /&gt;
type_id;&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Единственная разница заключается в добавлении второго параметра параметризованной обертки. Он определяет имя типа, в этом случае driver. Когда ovm_component_registry # () специализируется с двумя параметрами, тип и имя, оболочка регистрирует в с type-based  фабрике и на string-based фабрике. Чтобы установить переопределение, используя имя типа, необходимо вызвать API  фабрики напрямую, а не использовать обертку API, как показано ниже:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;106 factory.set_type_override_by_name(“driver”,&lt;br /&gt;
107 “error_driver”);&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это утверждение просто говорит, когда необходимо создать объект, имя типа которого driver, возвращать тип, имя которого error_driver вместо этого. Главная  разница между использованием string-based и type-based фабрик в том, как создаются объекты. В type-based API, вы получаете доступ к фабрике через оболочку. В string-based API, вы вызываете фабрику напрямую. Тип возвращаемого ovm_factory :: create_component_by_name () является ovm_component. Сравните это с возвращаемым типом &lt;br /&gt;
ovm_component_registry (T)::Create, который T. Чтобы получить доступ к предполагаемому типу возвращаемого объекта вам придется downcast его, как показано ниже:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;99 assert($cast(d1,&lt;br /&gt;
100 factory.create_component_by_name(“driver”,&lt;br /&gt;
101 ““,&lt;br /&gt;
102 “d1”,&lt;br /&gt;
103 this)));&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Вызов  factory.create_component_by_name () возвращает объект типа ovm_component.$ $cast downcasts возвращается объект типа d1, который является driver. Поскольку тип аргумента create_component_by_name () является строкой, нет никакой проверки во время компиляции, что тип возвращаемого объекта приведен к требуемому типу. Поэтому важно, чтобы проверялся код возвращаемого значения $cast, для определения удалось ли приведение. Фабрика  string-based API включает в себя create_object_by_name(). Она используется для создания объектов, производных от ovm_object. Вы должны вызвать $cast to downcast созданный объект по той же причине вы вызываете cast в компонентах, созданных string-based фабрики API.&lt;br /&gt;
&lt;br /&gt;
=== String-Based or Type-Based?  ===&lt;br /&gt;
&lt;br /&gt;
type-based и string-based фабрики API, каждая имеет свои плюсы и минусы, но, как правило, мы рекомендуем вам использовать type-based фабрику. Она  гораздо более надежная, защищена  от ошибок в строке названия. &lt;br /&gt;
&lt;br /&gt;
Иногда нет другого выбора; только на  string-based фабрику можно использовать. Важным примером этого является, когда вы хотите указать название теста с командной строки. run_test () принимает необязательный строковый аргумент, test_name. Кроме того, эта задача проверяет OVM_TESTNAME аргумент командной строки. Если тестовое имя поддерживается  командной строкой либо  списком аргументов, run_test () вызывает string-based фабрику для создания экземпляра объекта.&lt;br /&gt;
&lt;br /&gt;
string-based фабрика страдает от двух существенных недостатков. Один из них, уже упоминался,  это то, что легко неправильно ввести имя типа, когда вы пишете код. Это может привести к ошибке testbench, потому что невозможно найти объект, или in a subtle bug where the wrong object is instantiated. Вторым недостатком является то,  что это трудно, если нет возможности объявить параметризованный класс с использованием строки названия. Рассмотрим, например, параметризованный класс my_class.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class my_class #(type T=int) extends ovm_object;&lt;br /&gt;
typedef my_class#(T) this_t;&lt;br /&gt;
typedef ovm_object_registry#(this_t, “my_class#(T)”) type_id;&lt;br /&gt;
endclass&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Мы зарегистрировали его на string-based фабрики, используя имя my_class # (T). Кажется логичным. Теперь рассмотрим две специализации этого класса.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;typedef my_class#(A) C1;&lt;br /&gt;
typedef my_class#(B) C2;&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Не  существует  удобного способа регистрации их фабрикой с использованием имен, которые являются уникальными для специализации. В  наших классах в примере, С1 и С2 зарегистрированы с использованием имени my_class # (T). В структуре данных фабрики, это выглядит так, как будто вы пытаетесь зарегистрировать два объекта с одним и тем же именем, что является ошибкой. The remedy is to use the type-based factory API, which is not encumbered with strings&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class my_class #(type T=int) extends ovm_object;&lt;br /&gt;
typedef my_class#(T), this_t;&lt;br /&gt;
typedef ovm_object_registry#(this_t) type_id;&lt;br /&gt;
endclass&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Оставляя  второй аргумент в typedef из type_id, мы говорим фабрике зарегистрировать объект без имени, использовать только тип  как ключ поиска. Теперь у специализаций будут  свои собственные уникальные типы,  и не будет ошибочно считаться, что это один и тот же объект в фабрике.&lt;br /&gt;
&lt;br /&gt;
Недостатком использования type-based фабрики является то, что нет никакого способа поиска объекта строки. Это потому, что без второго аргумента в ovm_object_registry typedef, нет названия, под которым хранится объект. При первом использовании type-based фабрики, может ввести в замешательство, что нет  доступного имени. Вы быстро обнаружите, что строка имени не является действительно необходимой. В тех случаях, когда необходимо имя, например, когда вы получаете имена объектов из пользовательского ввода, тогда можно использовать string-based фабрику.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM</id>
		<title>OVM/OVM методология/Механика OVM</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM"/>
				<updated>2013-03-25T11:06:20Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= Механика  OVM =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Библиотека OVM предоставляет много возможностей для построения testbenches. В этой главе мы ознакомимся с наиболее важными возможностями, которые вы будете использовать  почти во всех ваши testbenches.&lt;br /&gt;
&lt;br /&gt;
== Компоненты и иерархия ==&lt;br /&gt;
&lt;br /&gt;
Первичная структура для создания элементов testbench  является компонента. Компонента  в OVM – это аналогия  модулю в Verilog. Компонента OVM сконструирована на основе класса, который обеспечивает ему различные характеристики, чем модуль Verilog и имеет различные использования импликаций. Среди таких характеристик является то, что классы создаются во время выполнения, а не во время разработки, как модули. Таким образом, OVM отвечает за создание компонента и сборки их в иерархии.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|[[Файл:69.png]]&lt;br /&gt;
|&amp;lt;graph&amp;gt;&lt;br /&gt;
digraph G{&lt;br /&gt;
node[shape=&amp;quot;circle&amp;quot;,fontsize=&amp;quot;11&amp;quot;,margin=&amp;quot;0.03,0.03&amp;quot;]&lt;br /&gt;
edge[arrowhead=none]&lt;br /&gt;
env -&amp;gt; {c1  c2}&lt;br /&gt;
c1 -&amp;gt; {child11  child12}&lt;br /&gt;
c2 -&amp;gt; {child21  child22}&lt;br /&gt;
child11 [label=&amp;quot;child1&amp;quot;]&lt;br /&gt;
child12 [label=&amp;quot;child2&amp;quot;]&lt;br /&gt;
child21 [label=&amp;quot;child1&amp;quot;]&lt;br /&gt;
child22 [label=&amp;quot;child2&amp;quot;]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/graph&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Рисунок 4-1 иллюстрирует простую иерархию компонент. После  мы покажем,  как построить эту иерархию с помощью средств OVM  для создания компонент и объединения их в иерархии:&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Самый верхний узел, ENV, является корнем. Корень отличается тем, что он не имеет предка. Все остальные узлы имеют только одного предка. Каждый узел имеет имя. Расположение каждого узла в иерархии может быть идентифицировано уникальным full_name (путем), который строится на нанизывании имен всех узлов между корнем и узлов в запросе, разделяя их сепаратором иерархии, точкой (.). Например, путь к компоненту, второго потомка с2 -  top.c2.child2.&lt;br /&gt;
&lt;br /&gt;
Компонент в ОВМ является классом, производным от ovm_component. Простейшая компоненты -  листья, те, которые не имеют потомков.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;57 class child extends ovm_component;&lt;br /&gt;
58&lt;br /&gt;
59 function new(string name, ovm_component parent);&lt;br /&gt;
60 super.new(name, parent);&lt;br /&gt;
61 endfunction&lt;br /&gt;
62&lt;br /&gt;
63 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Конструктор имеет два параметра, имя компоненты и указатель на его предка. Название -  простое имя, а не иерархический путь. Предок  предоставляет место для подключения  новой компоненты в иерархии. Полный  путь потомка создается путем объединения имени потомка с полным именем предка, разделенный точкой (.).OVM предоставляет методы для получения и имени и полного пути компоненты:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;string get_name();&lt;br /&gt;
string get_full_name();&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подчиненные компоненты создаются  в функции build(), которая вызывается на этапе сборки (фазы объясняется далее в этой главе). Создание экземпляра компонента включает вызов new() для выделения памяти для нее и передачи соответствующих параметров в конструктор. В компоненте, показанной ниже, мы создаем две подчиненные компоненты, и child1 child2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;71 class component extends ovm_component;&lt;br /&gt;
72&lt;br /&gt;
73 child child1;&lt;br /&gt;
74 child child2;&lt;br /&gt;
75&lt;br /&gt;
76 function new(string name, ovm_component parent);&lt;br /&gt;
77 super.new(name, parent);&lt;br /&gt;
78 endfunction&lt;br /&gt;
79&lt;br /&gt;
80 function void build();&lt;br /&gt;
81 child1 = new(“child1”, this);&lt;br /&gt;
82 child2 = new(“child2”, this);&lt;br /&gt;
83 endfunction&lt;br /&gt;
84&lt;br /&gt;
85 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как component, ENV также создает экземпляры двух подчиненных компонент, c1 и c2. Вся иерархия  с корнем в модуле,  называемым top в нашем проекте.(??)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;131 module top;&lt;br /&gt;
132&lt;br /&gt;
133 env e;&lt;br /&gt;
134&lt;br /&gt;
135 initial begin&lt;br /&gt;
136 e = new(“env”);&lt;br /&gt;
137 run_test();&lt;br /&gt;
138 end&lt;br /&gt;
139&lt;br /&gt;
140 endmodule&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вызов new() создает верхний уровень окружающей среды. run_test () начинает выполнение testbench.&lt;br /&gt;
&lt;br /&gt;
В SystemVerilog, модули, интерфейсы и программные блоки создаются во время разработки, в то время как классы создаются после разработки, во время выполнения. Таким образом, чтобы создать иерархию классов, мы должны иметь интерфейс, модуль или программу, который содержит начальный блок, который начинает процесс создание компонент иерархии, основанных на классах. Интерфейсы предназначены для использования в качестве средства связи между двумя модулями и не очень хорошо подходят для использования в качестве корня иерархии классов. Программные  блоки или модули могут быть использованы как  корень (??). Для нашей простой иерархии, это не имеет значения. Позже, когда мы подключаем класс-компонент для модуля на основе аппаратных средств, мы увидим, что использование модуля предпочтительнее программным блокам.&lt;br /&gt;
&lt;br /&gt;
=== Перемещение по иерархии ===&lt;br /&gt;
&lt;br /&gt;
Мы можем исследовать структуры данных, используемые для реализации компонента иерархии с некоторыми методами, предусмотренными в ovm_component. Потомки компоненты хранятся в ассоциативном массиве. Этот массив напрямую не доступен, но он может быть доступен через иерархию API. Этот API-интерфейс похож на встроенные методы SystemVerilog, предназначенные для ассоциативных массивов.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;int get_first_child(ref string name);&lt;br /&gt;
int get_next_child(ref string name);&lt;br /&gt;
ovm_component get_child(string name);&lt;br /&gt;
int get_num_children();&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
get_first_child () и get_next_child () работают вместе для перебора множества потомков, содержащихся в компоненте. get_first_child () извлекает имя первого ребенка в списке. Он возвращает имя в качестве ссылки. get_next_child () возвращает имя следующего ребенка в списке. Он  возвращает 1, если имеется следующая имя потомка и 0, если был достигнут конец списка. get_child () преобразует имя в ссылку компоненты.&lt;br /&gt;
&lt;br /&gt;
Используя эти функции, мы можем пройти по компонентам иерархии.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;73 function void depth_first(ovm_component node,&lt;br /&gt;
74 int unsigned level = 0);&lt;br /&gt;
75&lt;br /&gt;
76 string name;&lt;br /&gt;
77&lt;br /&gt;
78 if(node == null)&lt;br /&gt;
79 return;&lt;br /&gt;
80&lt;br /&gt;
81 visit(node, level);&lt;br /&gt;
82&lt;br /&gt;
83 if(node.get_first_child(name))&lt;br /&gt;
84 do begin&lt;br /&gt;
85 depth_first(node.get_child(name), level+1);&lt;br /&gt;
86 end while(node.get_next_child(name));&lt;br /&gt;
87&lt;br /&gt;
88 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/utils/traverse.svh&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эта функция будет осуществляющая обход в глубину иерархии, называя visit(). Мы используем get_first_child () и get_next_child () для перебора списка каждого потомка в каждом узле. Для каждого итерации мы вызываем depth_first () рекурсивно. Для нашего маленького проекта, результат будет  следующий:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;pre&amp;gt;+ env&lt;br /&gt;
| + env.c1&lt;br /&gt;
| | env.c1.child1&lt;br /&gt;
| | env.c1.child2&lt;br /&gt;
| + env.c2&lt;br /&gt;
| | env.c2.child1&lt;br /&gt;
| | env.c2.child2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция visit() использует  глубину узла и  определяет является ли он конечным  для печати строки каждого узла.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Singleton Top ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Компоненты, которые не имеют предков (то есть,  параметр предок в конструкторе является нулевым) называют orphans.(??) В OVM вы можете создать столько компоненты без предков, сколько вам необходимо. Тем не менее, не существует такого понятия, как истинные orphans. Любой компонент, чей предок является  null,  относится к встроенному предку, называемому ovm_top. ovm_top состоит из одного экземпляра ovm_root. Это предок всех компонент, которые имеют предков. В самом деле, env  в нашем предыдущем примере является потомком ovm_top. Так как он не имеет предка, то автоматически получают ovm_top в качестве родителя.&lt;br /&gt;
&lt;br /&gt;
singleton -  широко известный  объектно-ориентированных шаблон проектирования, который имеет private (local) или protected конструкторы и статическую функцию get, которая возвращает тот же самый указатель не смотря на то, сколько раз она вызывается. Это означает, что возможно существование одного экземпляра, и что экземпляр создается  автоматически при вызове функции get. ovm_top содержит дескриптор экземпляра singleton  ovm_root(???). Он статически инициализируется при вызове ovm_root :: Get (). Вы можете вызвать ovm_root :: Get () в любое время, но в этом нет необходимости, так как ovm_top предусматривает это.&lt;br /&gt;
&lt;br /&gt;
Есть целый ряд полезных следствий существования singleton top-level компоненты. Одним из них является, что вы можете достичь любую компоненту из ovm_top. Если вы запустили алгоритм обхода иерархии, начиная с ovm_top, вы доберетесь до каждой компоненты в системе. Другим следствием является то, что любая компонента, включая порты, экспорт и каналы, которые создаются внутри модуля, достижима из ovm_top. Если вы хотите изменить отчет во всех обработчиках компонент, вы можете сделать это, вызвав  одну из иерархических функции отчетности в ovm_top. ovm_top содержит все механизмы синхронизации, которые объясняются далее в этой главе.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Connectivity ==&lt;br /&gt;
&lt;br /&gt;
Компоненты соединены друг с другом через TLM port и exports. Port и exports обеспечивают средства для компонент, или, точнее, процессы в компонентах для синхронизации и связи друг с другом. Port и exports -  объекты, которые образуют  пункт связывания для обеспечения межкомпонентной связи. Как уже говорилось в предыдущей главе, exports обеспечивают функции и задачи, которые могут быть вызваны из Port.&lt;br /&gt;
&lt;br /&gt;
[[Файл:70.png]]&lt;br /&gt;
 &lt;br /&gt;
Метод connect  портов и exports используется для связывания их вместе.&lt;br /&gt;
&lt;br /&gt;
initiator_port.connect(target.export)&lt;br /&gt;
&lt;br /&gt;
Этот метод создает ассоциацию, или связывание, между port и export так, что port может вызывать(запускать) задачи и функции из export. Для успешного соединения, типы  port и export должны совпадать. То есть, типы интерфейсов должны быть одинаковыми, и тип объекта, передающегося в интерфейс должны быть одинаковыми.&lt;br /&gt;
&lt;br /&gt;
=== Подключение через Иерархию ===&lt;br /&gt;
&lt;br /&gt;
Как и контактами в RTL дизайне, нам нужно подключиться к портам  TLM через иерархические границы. Рисунок 4-3 использует простой дизайн, чтобы показать, как сделать эти соединения. Этот проект содержит исходную компоненту с двумя port(выходами), которые в конечном итоге подключаются к двум входам(export). Для осуществления подключения между этими компонентами, мы должны расширить port и export на  уровень выше в иерархии.&lt;br /&gt;
 &lt;br /&gt;
[[Файл:71.png]]&lt;br /&gt;
&lt;br /&gt;
Компонент source содержит два порта, и first_put_port, second_put_port.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;65 class source extends ovm_component;&lt;br /&gt;
66&lt;br /&gt;
67 ovm_put_port #(trans_t) first_put_port;&lt;br /&gt;
68 ovm_put_port #(trans_t) second_put_port;&lt;br /&gt;
69&lt;br /&gt;
70 function new(string name, ovm_component parent);&lt;br /&gt;
71 super.new(name, parent);&lt;br /&gt;
72 endfunction&lt;br /&gt;
73&lt;br /&gt;
74 function void build();&lt;br /&gt;
75 first_put_port = new(“first_put_port”, this);&lt;br /&gt;
76 second_put_port = new(“second_put_port”, this);&lt;br /&gt;
77 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Кроме того, компонента sink создает экспорт и создает его в функции build. Экспорт связан с внутренним каналом, FIFO, от которого компонента может получать объекты во время выполнения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;126 class sink extends ovm_component;&lt;br /&gt;
127&lt;br /&gt;
128 ovm_put_export #(trans_t) put_export;&lt;br /&gt;
129 local tlm_fifo #(trans_t) fifo;&lt;br /&gt;
130&lt;br /&gt;
131 function new(string name, ovm_component parent);&lt;br /&gt;
132 super.new(name, parent);&lt;br /&gt;
133 endfunction&lt;br /&gt;
134&lt;br /&gt;
135 function void build();&lt;br /&gt;
136 put_export = new(“put_export”, this);&lt;br /&gt;
137 fifo = new(“fifo”, this);&lt;br /&gt;
138 endfunction&lt;br /&gt;
139&lt;br /&gt;
140 function void connect();&lt;br /&gt;
141 put_export.connect(fifo.put_export);&lt;br /&gt;
142 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
source_wrapper должен создать соединение между внутренним источником компоненты и  внешней ее границей. Он делает это соединение, создавая свои порты, которые имеют тот же тип, что и тип  портов lower-level, в нашем случае, тех, которые принадлежат к source.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;98 class source_wrapper extends ovm_component;&lt;br /&gt;
99&lt;br /&gt;
100 source s;&lt;br /&gt;
101 ovm_put_port #(trans_t) put_port1;&lt;br /&gt;
102 ovm_put_port #(trans_t) put_port2;&lt;br /&gt;
103&lt;br /&gt;
104 function new(string name, ovm_component parent);&lt;br /&gt;
105 super.new(name, parent);&lt;br /&gt;
106 endfunction&lt;br /&gt;
107&lt;br /&gt;
108 function void build();&lt;br /&gt;
109 s = new(“source”, this);&lt;br /&gt;
110 put_port1 = new(“put_port1”, this);&lt;br /&gt;
111 put_port2 = new(“put_port2”, this);&lt;br /&gt;
112 endfunction&lt;br /&gt;
113&lt;br /&gt;
114 function void connect();&lt;br /&gt;
115 s.first_put_port.connect(put_port1);&lt;br /&gt;
116 s.second_put_port.connect(put_port2);&lt;br /&gt;
117 endfunction&lt;br /&gt;
118&lt;br /&gt;
119 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После того как порты в source_wrapper были созданы, они подключаются к портам lower-level  компоненты source с помощью метода Connect. Видимость портов на более высоком уровне иерархии достигается  таким же образом, как мы видим  в sinker.&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
160 class sinker extends ovm_component;&lt;br /&gt;
161&lt;br /&gt;
162 ovm_put_export #(trans_t) first_put_export;&lt;br /&gt;
163 ovm_put_export #(trans_t) second_put_export;&lt;br /&gt;
164&lt;br /&gt;
165 sink sink1;&lt;br /&gt;
166 sink sink2;&lt;br /&gt;
167&lt;br /&gt;
168 function new(string name, ovm_component parent);&lt;br /&gt;
169 super.new(name, parent);&lt;br /&gt;
170 endfunction&lt;br /&gt;
171&lt;br /&gt;
172 function void build();&lt;br /&gt;
173 sink1 = new(“sink1”, this);&lt;br /&gt;
174 sink2 = new(“sink2”, this);&lt;br /&gt;
175 first_put_export = new(“first_put_export”, this);&lt;br /&gt;
176 second_put_export = new(“second_put_export”, this);&lt;br /&gt;
177 endfunction&lt;br /&gt;
178&lt;br /&gt;
179 function void connect();&lt;br /&gt;
180 first_put_export.connect(sink1.put_export);&lt;br /&gt;
181 second_put_export.connect(sink2.put_export);&lt;br /&gt;
182 endfunction&lt;br /&gt;
183&lt;br /&gt;
184 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Два нижних уровня компоненты sink и exports  создаются обычным способом. Затем они соединяются с помощью метода подключения export. Сейчас мы создаем port-export соединение между source_wrapper и sinker, также с помощью функции Connect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;192 class env extends ovm_component;&lt;br /&gt;
193&lt;br /&gt;
194 sinker s;&lt;br /&gt;
195 source_wrapper sw;&lt;br /&gt;
196&lt;br /&gt;
197 function new(string name, ovm_component parent = null);&lt;br /&gt;
198 super.new(name, parent);&lt;br /&gt;
199 endfunction&lt;br /&gt;
200&lt;br /&gt;
201 function void build();&lt;br /&gt;
202 s = new(“sinker”, this);&lt;br /&gt;
203 sw = new(“source_wrapper”, this);&lt;br /&gt;
204 endfunction&lt;br /&gt;
205&lt;br /&gt;
206 function void connect();&lt;br /&gt;
207 sw.put_port1.connect(s.first_put_export);&lt;br /&gt;
208 sw.put_port2.connect(s.second_put_export);&lt;br /&gt;
209 endfunction&lt;br /&gt;
210&lt;br /&gt;
211 task run; &lt;br /&gt;
212 global_stop_request();&lt;br /&gt;
213 endtask&lt;br /&gt;
214&lt;br /&gt;
215 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для новых пользователей часто может вызвать трудности определить,  порт или экспорт необходимо подключить и какой объект является аргументом. Вы можете легко понять это, следуя потоку управления  через систему. Общее правило  заключается в том, что запрашиваемый порт вызывается с помощью функции connect() и передачи в нее запрашиваемого port или export в качестве аргумента. Рисунок 4-4 показывает поток управления через иерархическую систему.&lt;br /&gt;
&lt;br /&gt;
[[Файл:72.png]]&lt;br /&gt;
 &lt;br /&gt;
Порт – объект  вызова и экспорт - объект вызванной функции или задачи. Вы можете воспринимать порты, как вызов экспорта. Так, в env, мы вызываем connect на put_ports с put_exports в качестве аргументов. Для port-to- port  и export-to-export иерархии соединений, вызывающий будет чуть менее очевиден. Если  вызов осуществляется со стороны порта, вы можете считать, что вызов lowest-level порта в иерархии является вызовом методов интерфейса  upper-level порта. Аналогично, если экспорт объект вызова, вы можете считать upper-level экспорт  вызовом lower-level export. В таблице ниже приведены возможные типы подключения:&lt;br /&gt;
 &lt;br /&gt;
[[Файл:73.png]]&lt;br /&gt;
&lt;br /&gt;
=== Примечание для пользователей AVM ===&lt;br /&gt;
&lt;br /&gt;
В AVM-3.0 соединения были сделаны аналогично: вызовам функции connect() из port и export. Кроме того, 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.)&lt;br /&gt;
&lt;br /&gt;
== Фазы ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для традиционных модулей Verilog  полная разработка  проекта и  его выполнение осуществялется в симуляторе. Так как OVM компоненты - это классы, они  создаются и подключаются, и их выполнение начинается за пределами Verilog разработчика. Компоненты создаются вызовом конструктора класса операцией new(), которая выделяет память и выполняет инициализацию. В то время как  Verilog время выполнения  run-time engine managing instantiation, разработка, и выполнение компонент на основе классов, функциональность  компонент разделена на фазы, и контроллер фазы OVM  управляет их исполнением. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Каждая фаза представлена в компоненте как виртуального метода (задачи или функция) с тривиальной реализацией по умолчанию. Эти  phase callbacks   реализуются разработчиком компоненты, который обеспечивает соответствующую функциональность. Фаза контроллера гарантирует, что фазы выполняются в надлежащем порядке. Набор стандартных фаз показано в следующей таблице:&lt;br /&gt;
&lt;br /&gt;
[[Файл:74.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:75.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Каждая фаза имеет определенную цель. разработчики Компонент  должны заботиться о том, чтобы функциональность реализованная в каждой фазе  callback соответствовала фазе определения.&lt;br /&gt;
&lt;br /&gt;
•	'''New''' технически не является фазой, в том, что она не управляется фазой контроллера. Тем не менее, для каждого компонента, конструктор должен быть выполнен и завершен в целях создания компоненты. Таким образом,  new должен выполниться до build() или любого другого последующего этапах  выполнения.&lt;br /&gt;
&lt;br /&gt;
•	'''build'''  это место, где новые компоненты, порты и экспорт создаются и настраиваются. Это также рекомендуемое место для вызова set_config_ * и get_config_ * (см. Section4.4). &lt;br /&gt;
&lt;br /&gt;
•	'''connect''' – место, где компоненты, порты и экспорт созданные в  build() связываются.&lt;br /&gt;
&lt;br /&gt;
•	'''end_of_elaboration''' здесь вы можете выполнить изменение конфигураций, зная, что разработка уже завершена. То есть, вы можете предположить, что все компоненты созданы и соединены. &lt;br /&gt;
&lt;br /&gt;
•	'''start_of_simulation''' выполняется только до времени 0.&lt;br /&gt;
&lt;br /&gt;
•	'''run'''    pre-defined task фазой. Все выполняемые задачи работают параллельно. Каждая выполняемая задача продолжается, пока ее управление не достигнет оператора EndTask или она явно будет прекращена. Позже в этой главе мы обсудим, как завершать testbenches.&lt;br /&gt;
&lt;br /&gt;
•	'''extract'''  предназначен для сбора информации, касающейся покрытия или другой информации о том, как ответить на  вопросы testbench.  &lt;br /&gt;
&lt;br /&gt;
•	'''проверка''' - место, где выполняется любая проверка правильности или проверка валидации извлеченных данных. &lt;br /&gt;
&lt;br /&gt;
•	'''отчет''' -место , где делаются окончательные отчеты.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ниже простой пример,  использующий ovm_report_info () для иллюстрации порядка выполнения фаз.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;38 class sub_component extends ovm_component;&lt;br /&gt;
39&lt;br /&gt;
40 function new(string name, ovm_component parent);&lt;br /&gt;
41 super.new(name, parent);&lt;br /&gt;
42 endfunction&lt;br /&gt;
43&lt;br /&gt;
44 function void build();&lt;br /&gt;
45 ovm_report_info(“build”, ““);&lt;br /&gt;
46 endfunction&lt;br /&gt;
47&lt;br /&gt;
48 function void connect();&lt;br /&gt;
49 ovm_report_info(“connect”, ““);&lt;br /&gt;
50 endfunction&lt;br /&gt;
51&lt;br /&gt;
52 function void end_of_elaboration();&lt;br /&gt;
53 ovm_report_info(“end_of_elaboration”, ““);&lt;br /&gt;
54 endfunction&lt;br /&gt;
55&lt;br /&gt;
56 function void start_of_simulation();&lt;br /&gt;
57 ovm_report_info(“start_of_simulation”, ““);&lt;br /&gt;
58 endfunction&lt;br /&gt;
59&lt;br /&gt;
60 task run();&lt;br /&gt;
61 ovm_report_info(“run”, ““);&lt;br /&gt;
62 endtask&lt;br /&gt;
63&lt;br /&gt;
64 function void extract();&lt;br /&gt;
65 ovm_report_info(“extract”, ““);&lt;br /&gt;
66 endfunction&lt;br /&gt;
67&lt;br /&gt;
68 function void check();&lt;br /&gt;
69 ovm_report_info(“check”, ““);&lt;br /&gt;
70 endfunction&lt;br /&gt;
71&lt;br /&gt;
72 function void report();&lt;br /&gt;
73 ovm_report_info(“report”, ““);&lt;br /&gt;
74 endfunction&lt;br /&gt;
75&lt;br /&gt;
76 endclass&lt;br /&gt;
file: 04_OVM_mechanics/03_phases/top.sv&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В компоненте верхнего уровня, мы создаем две  компоненты, каждая из которых в свою очередь создает две sub_component. sub_components являющимися такими же, как и сами компоненты; каждая фаза обратного  callback просто печатает строку, определяющую фазу. При выполнении, вы получите следующий результат:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;OVM_INFO @ 0 [RNTST] Running test ...&lt;br /&gt;
OVM_INFO @ 0: env.c1 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [run]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s1 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s2 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c1 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s1 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s2 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c2 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s1 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s2 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c1 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s1 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s2 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c2 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s1 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s2 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c1 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s1 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s2 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c2 [report]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Вы видите, что build()выполняется сверху вниз и остальные фазы запуска снизу вверх. Вы также можете видеть, что каждый этап завершается во всех компонентах до начала следующего этапа. Таким образом, в connect(), например, вы можете расчитывать, что build() завершена во всех компонентах. Вы также заметите, что время  запускается после  фазы run. В нашем примере задача run - тривиальна, она просто выполняет задержку времени на единицу (# 1).&lt;br /&gt;
&lt;br /&gt;
un_test (), упоминалось ранее в разделе 4.1, инициирует фазу выполнение. Он начинает выполнение фаз по порядку и контролирует, что каждый этап завершен до начала следующего.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==	Config ==&lt;br /&gt;
&lt;br /&gt;
Для увеличения повторного использования компонент, желательно объявит их с параметрами, которые могут быть внешне настроены. Конфигурационные средства обеспечивают возможности для этого. Они основаны на базе пар имя-значение, и называются configuration items, которые организованы иерархически. Каждая компонента содержит таблицу конфигураций элементов конфигурации,  так как компоненты организованы в качестве дерева, каждый элемент в базе данных может быть уникально размещен по расположению компоненты и имени элемента конфигурации&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:76.png]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Класс ovm_component содержит два набора методов для  конфигурации размещения элементов в базу данных и потом для их получения. Это  set_config_ * и * get_config_. В таблице ниже приведены оба набора.&lt;br /&gt;
&lt;br /&gt;
[[Файл:77.png]] &lt;br /&gt;
&lt;br /&gt;
Set_config_ * функция помещает элемент в конфигурационную базу данных текущего компонента, то есть в экземпляре компонента, в котором функция вызывается. Каждая из этих функций принимают три аргумента, имя, field_name и значение. Аргумент name -  имя пути, который представляет набор компонент, которые должны соответсвовать кофигурациям этого элемента. name используется в * get_config_, чтобы найти элементы в базе данных конфигурации.  field_name -  это имя поля и должно быть уникальным в пределах текущей базы конфигураций. value является частью пары имя-значение и его тип может быть строка, INT, или ovm_object, в зависимости от вызываемой функции. Кроме того, set_config_object принимает копию аргумента для указания, был ли объект передан в качестве значения так как значение должно быть клонировано прежде чем оно будет помещено в базу конфигурации. &lt;br /&gt;
Функции get_config_ * извлекает элементов из базы данных конфигураций.  Эти функции принимают только два аргумента, имя поля и INOUT переменную, который содержит значение расположения элемента. Они также возвращают бит, чтобы указать был ли удачно расположен запрашиваемый объект. * Get_config_ функции не принимают аргументы имени пути, как их set_config_ * коллеги, поскольку они используют путь к текущему компоненту, как ссылку, чтобы найти элементы конфигурации. Они разработаны, чтобы узнать, есть значение элемента конфигурации для текущего контекста, то есть, компонент, в котором функция get_config_ * вызывается.&lt;br /&gt;
&lt;br /&gt;
Алгоритм поиска для извлечения элементов конфигурации использует имя пути компонента запроса и путь к файлу вставляемый в каждый  item. Он начинается с поиска конфигурационных элементов в базе данных в верхней компоненте по field_name. Если такой элемент существует, то он затем спрашивает, соответствует ли указанный путь к файлу пути имени компоненты. Если элемента с указанным field_name нет или имена путей не совпадают, то поиск идет с дочерней компоненты. Этот процесс продолжается до соответствия или  до достижения компоненты начала поиска.&lt;br /&gt;
&lt;br /&gt;
Путь  каждого элемент конфигурации может быть регулярным выражением. Таким образом, мы  используем алгоритм соответствия регулярных выражений для нахождения совпадения с  путем запрошенной компоненты и именем пути элемента конфигурации. Эффект заключается в соответствии иерархической области. &lt;br /&gt;
В качестве примера, рассмотрим простую иерархию на рисунке 4-5. Давайте предположим, что в  env::build()мы запускаем два вызова set_config_ *:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;112 function void build();&lt;br /&gt;
113 c1 = new(“c1”, this);&lt;br /&gt;
114 c2 = new(“c2”, this);&lt;br /&gt;
115&lt;br /&gt;
116 set_config_int(“c2.*”, “i”, 42);&lt;br /&gt;
117 set_config_int(“*”, “t”, 19);&lt;br /&gt;
118 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/04_config/top.sv&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Это вызовет запись двух конфигурационных элементов в базу данных в env. Обратите внимание на звездочку (*) в имени пути. Имя пути в вызове set_config_ * -  регулярное выражение, и дикие символы  используются для указать несколько областей, к которыми элемент применяется. По элементу i, c2. * показывает, что в любой области ниже c2 в иерархии, i будет  определять указанное значение. В этом случае указанное значение -  42. Если вы опустите звездочку, то элемент конфигурации применяется только к c2, а не к любому из своих потомков. &lt;br /&gt;
Состояние конфигурации базы данных для каждого компонента в иерархии  после  вызова set_config_ * показано на рисунке 4-6,&lt;br /&gt;
&lt;br /&gt;
[[Файл:78.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
пусть в элементе top.c1.child1 мы осуществим вызов:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;int i;&lt;br /&gt;
...&lt;br /&gt;
get_config_int(“i”, i)&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Поиск задает вопрос: Каково значение конфигурации i в иерархических рамках top.c1.child1? Чтобы ответить на этот вопрос, конфигурации базы данных в env ищется в первую очередь. Запись i там говорит, что значение i совпадающее с  env.c2. *  -  42. Тем не менее, компоненты, из которых был сделан запрос, находятся в c1 sub-hierarchy. Таким образом,  не существует соответствия, а get_config_int () возвращает статус неудачи. Запрос в любую компоненту, которая является дочерней c2 будет успешно завершен и вернется значение 42. &lt;br /&gt;
&lt;br /&gt;
Ниже приведен код для функцию build() дочерней компоненты. Это место где эти компоненты ищут конфигурационные значения i и t.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;60 function void build();&lt;br /&gt;
61&lt;br /&gt;
62 string msg;&lt;br /&gt;
63&lt;br /&gt;
64 if(!get_config_int(“t”, t)) begin&lt;br /&gt;
65 $sformat(msg, “no value for t found in config&lt;br /&gt;
database, using default value of %0d”, t);&lt;br /&gt;
66 ovm_report_warning(“build”, msg);&lt;br /&gt;
67 end&lt;br /&gt;
68&lt;br /&gt;
69 if(!get_config_int(“i”, i)) begin&lt;br /&gt;
70 $sformat(msg, “no value for i found in config&lt;br /&gt;
database, using default value of %0d”, i);&lt;br /&gt;
71 ovm_report_warning(“build”, msg);&lt;br /&gt;
72 end&lt;br /&gt;
73&lt;br /&gt;
74 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/04_config/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующий пример показывает вывода  при выполнении этого проекта:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# OVM_INFO @ 0 [RNTST] Running test ...&lt;br /&gt;
# OVM_WARNING @ 0: env.c1.child1 [build] no value for i found in&lt;br /&gt;
config database, using default value of 91&lt;br /&gt;
# OVM_WARNING @ 0: env.c1.child2 [build] no value for i found in&lt;br /&gt;
config database, using default value of 91&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Запрос на  конфигурационный элемент t был успешным во всех контекстах с того момента как вызов set_config_int установил, что tдоступно во всех контекстах. Два запроса на элемент конфигурации i были успешными, и два нет. Результат получился такой потому что мы ограничены наличием i только компонентах на уровне или ниже env.c2. Компоненты на уровне или ниже c1 не могут видеть элемент конфигурации i из-за того, как мы создали базу данных конфигурации.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Конфигурация и фазы ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь, когда мы знаем, что набор вызовов для размещения и извлечение элементов в конфигурацию базы данных, следующей задачей, является эффективное применение этих функции для настройки компонент. Конфигурация может быть использована для изменения поведения или структуры testbench. Как правило, поведенческий  и структурный режимы определяются, когда testbench начинает работу, поэтому он является наиболее удобным местом для установки параметров конфигурации на одной из ранних стадий, таких как new, build или connect.&lt;br /&gt;
&lt;br /&gt;
Из таблицы фаз, вы можете увидеть, что new и build являются нисходящими фазами, в то время как все остальные фазы восходящие. Так что, если вы хотите установить элемент конфигурации в базу данных на более высокий уровень контекста, чтобы он был подхвачен более низким уровнем, вы должны вызвать функцию set_config_ * в new или build фазе. Фазы выполняется дискретно, это означает, что каждая фаза выполняется до начала следующей. Вы можете установить элементы конфигурации в new или build фазу и извлекать их для использования в установке поведенческого режима или изменения топологии на этапе build. В вашей функции build, сначала вызовите get_config_ * для извлечения элементов из более высоких уровней иерархии для управления конфигурацией текущего уровня. Затем добавьте вызов set_config_ * для  помещения элементов конфигурации в базу данных для использования компонентами на более низких уровнях иерархии. Наконец, используя соответствующие элементы конфигурации, обработайте компоненты. Важно вызвать get_config_ * вначале, потому, что указанная информация может влиять на значения, которые установлены на более низких уровнях иерархии.&lt;br /&gt;
&lt;br /&gt;
Например, настройка топологии включает в себя установку параметров топологии&lt;br /&gt;
на верхнем уровне среды, а затем применение эти параметров в различных компонентах, которые находятся ниже верхнего уровня среды в иерархии. Наш пример имеет шину, которая может иметь любое количество masters или slaves. Число masters и slaves расположены в верхнем уровне среды. Модель шину подхватывает эту конфигурационную информацию и использует ее для построения шины. В build функции верхнего уровня окружающей среды, мы создаем модель шины и настраиваем ее с числом masters и slaves.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;129 function void build();&lt;br /&gt;
130 set_config_int(“bus”, “masters”, 4);&lt;br /&gt;
131 set_config_int(“bus”, “slaves”, 8);&lt;br /&gt;
132 b = new(“bus”, this);&lt;br /&gt;
133 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/05_config_topo/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Модель шины построена таким образом, что число masters и slaves не фиксировано. Вместо этого,  количество приходит из конфигурации системы.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;90 function void build();&lt;br /&gt;
91&lt;br /&gt;
92 int unsigned i;&lt;br /&gt;
93&lt;br /&gt;
94 if(!get_config_int(“masters”, masters)) begin&lt;br /&gt;
95 $sformat(msg, “\”masters\” is not in the&lt;br /&gt;
configuration database, using default value of %0d”, masters);&lt;br /&gt;
96 ovm_report_warning(“build”, msg);&lt;br /&gt;
97 end&lt;br /&gt;
98&lt;br /&gt;
99 for(i = 0; i &amp;lt; masters; i++) begin&lt;br /&gt;
100 $sformat(name, “master_%0d”, i);&lt;br /&gt;
101 m = new(name, this);&lt;br /&gt;
102 end&lt;br /&gt;
103&lt;br /&gt;
104 if(!get_config_int(“slaves”, slaves)) begin&lt;br /&gt;
105 $sformat(msg, “\”slaves\” is not in the configuration&lt;br /&gt;
database, using default value of %0d”, slaves);&lt;br /&gt;
106 ovm_report_warning(“build”, msg);&lt;br /&gt;
107 end&lt;br /&gt;
108&lt;br /&gt;
109 for(i = 0; i &amp;lt; slaves; i++) begin&lt;br /&gt;
110 $sformat(name, “slave_%0d”, i);&lt;br /&gt;
111 s = new(name, this);&lt;br /&gt;
112 end&lt;br /&gt;
113&lt;br /&gt;
114 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/05_config_topo/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В функции build для модели шины, схема получает необходимую конфигурационную информацию, используя вызовы get_config_int. В каждом случае, Возвращаемое значение проверяется, чтобы определить, была ли получена запрошенная конфигурационная информация. Если нет, сообщение об ошибке предупреждает, что конфигурационный элемент не найден и что будет использовано значение по умолчанию. С точки зрения Best Practices, важно убедиться, что возвращается значение, проверено и выдается предупреждение, в случае ошибки. Без этой проверки, то, что  используется значения по умолчанию, могло остаться незамеченным. В некоторых случаях это может быть приемлемо, в других случаях - нет. Человек,  создающий модель шины не может знать всех обстоятельств, при которых модель будет использоваться. Поэтому важно сделать все возможное, чтобы  модель была надежной. Проверка возвращаемых значений и выдача соответствующих сообщений является одним из способов повышения надежности модели.&lt;br /&gt;
&lt;br /&gt;
В цикле, где мы строим masters, ссылки на каждого нового master сохраняются в той же переменной, m. Каждый новый master заменяет предыдущий. Мы не используем массив для хранения всех компонент. В каждой итерации, мы используем $ sformat, чтобы создать уникальное имя. Конструктор, new(), вызывает super.new (), конструктор в ovm_component базовом классе, который отвечает за вставку вновь созданную компоненту потомка в родительский список. Нет  необходимости явно сохранять компоненту, потому что родительская компонента сделает это за нас. Цикл для создания slaves организован таким же образом.&lt;br /&gt;
&lt;br /&gt;
== Фабрика ==&lt;br /&gt;
&lt;br /&gt;
Структура testbench определяется организацией компонент в иерархии и то, как эти объекты связаны между собой. Поведение testbench определяется процедурным кодом в фазе callbacks—build, connect, run и так далее. Есть моменты, когда это необходимо изменить поведение или часть структуры внешне, то есть во время выполнения, не касаясь, кода testbench. Например, чтобы ввести ошибку в систему, вы можете заменить обычный драйвер  драйвером с ошибкой, который умышленно вводит ошибки. Вместо повторного кодирования окружающей среды для использования другого драйвера, вы можете использовать фабрику, чтобы сделать замену автоматически.&lt;br /&gt;
&lt;br /&gt;
Фабрика обеспечивает средства для замены одного объекта на другой без использования текстового редактора для изменения testbench. Вместо создания объект с помощью new(), вы вызываете функцию create  фабрики. Фабрика  хранит список зарегистрированных объектов и, при необходимости, набор переопределения связанный с каждой из них. При создании объекта с помощью фабрики, список переопределения проверяется на наличие такого объекта. Если он есть, то переопределенный объект возвращается. В противном случае, зарегистрированный объект возвращается. &lt;br /&gt;
&lt;br /&gt;
Фабрика  является OVM структурой данных. Она  глобальна в области видимости, и только один элемент может существовать  (то есть, это singleton). Она служит в качестве полиморфных конструкторов, одна функция, которая позволяет создавать различные объекты. Она  обеспечивает средства для регистрации объектов, и для их переопределения. Объекты, зарегистрированные как переопределения,  должны быть производным от объекта, от которого они переопределялись. Для того, чтобы одна функция возвращала несколько объектов, каждый из этих объектов должны быть получен от общего базового класса.&lt;br /&gt;
Важным компонентом фабрики является оболочка, класс, который описывает объект, который мы хотим создать с  помощью фабрики. Структура данных фабрики основывается на таблице оболочек, проиндексированной по ключу. Оболочка имеет функцию create(), которая обращается к  конструктору обернутого объекта.&lt;br /&gt;
&lt;br /&gt;
Использование фабрики состоит из трех этапов: регистрация, настройки переопределение и создание. На первом этапе необходимо зарегистрировать объект с помощью фабрики. На втором шаге, добавляется переопределение зарегистрированного объекта. На третьем этапе, вы создаете объект с помощью фабрики, который будет возвращать либо первоначально зарегистрированный объект или переопределенный, в зависимости от того, переопределение было передано в качестве  запрашиваемого объекта.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Как работает фабрика ===&lt;br /&gt;
&lt;br /&gt;
Термин фабрика был придуман The Gang of Four для использования в программном обеспечении в их книге Design Patterns: Elements of Reusable Object-Oriented Software. В этой книге, они определили шаблон, который они называют abstract&lt;br /&gt;
factory, как интерфейс для создания семейств связанных объектов. Они определили шаблон factory method  в качестве интерфейса для создания объектов, относящихся к подклассам для определения, какой объект создавать. Фабрика OVM является комбинацией этих двух  шаблонов. Она предоставляет средства для создания семейства объектов, а также предоставляет средства для определения, какой именно объект создавать структуре данных фабрики.&lt;br /&gt;
&lt;br /&gt;
Фабрика OVM основана на структуре данных, которая  отображает необходимые типы в  переопределенные. По сути, организация представляет собой ассоциативный массив типов, ключом в котором является также тип. При регистрации типа при помощи фабрики, его переопределенным типом является он сам. Таким образом, по умолчанию, когда вы запрашиваете объект этого типа, вы получаете только тип. Фабрика  также предоставляет средства для замены переопределенных типов другими типами, чтобы вы могли получить переопределенные типы, которые отличаются от зарегистрированных типов.&lt;br /&gt;
&lt;br /&gt;
[[Файл:79.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Следующий пример является сильно упрощенной фабрикой, которая показывает,  как OVM фабрика работает. Фабрика  сохраняет основные структуры OVM фабрики, но многие детали были удалены для более понятной иллюстрации. Наша фабрика реализуется в четырех классах, двух базовых классах и двух производных классах, которые делают реальную работу. Двумя базовыми классами являются object_base и wrapper_base. Все объекты, зарегистрированные в фабрике, должны быть получены (в конечном счете) от object_base, а wrapper_base является базовым классом для параметризованной обертки. Factory  - это singleton, который содержит ассоциативный массив, содержащий экземпляры обертки. Наконец, обертки являются производными от wrapper_base и параметризованными классами, которые представляют уникальные типы. &lt;br /&gt;
&lt;br /&gt;
Для нашей фабрики, базовые классы тривиальны:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;46 class object_base;&lt;br /&gt;
47 virtual function void print();&lt;br /&gt;
48 $display(“object_base”);&lt;br /&gt;
49 endfunction&lt;br /&gt;
50 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&lt;br /&gt;
59 class wrapper_base;&lt;br /&gt;
60 virtual function object_base create_object();&lt;br /&gt;
61 return null;&lt;br /&gt;
62 endfunction&lt;br /&gt;
63 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
object_base имеет виртуальную функцию print(), которую мы используем для проверки типов, созданных на фабрике. wrapper_base имеет виртуальную функцию create(), полиморфный конструктор, который используется для создания новых объектов.&lt;br /&gt;
factory - это singleton, то есть его конструктор локальный, и он содержит статическую ссылку  на самого себя. Единственный способ создать экземпляр factory является вызов factory::get(). factory содержит ассоциативный массив, который отображает запрашиваемые типы wrapper_base в   переопределенные типы wrapper_base.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;73 class factory;&lt;br /&gt;
74&lt;br /&gt;
75 static factory f;&lt;br /&gt;
76 wrapper_base override_map[wrapper_base];&lt;br /&gt;
77&lt;br /&gt;
78 local function new();&lt;br /&gt;
79 endfunction&lt;br /&gt;
80&lt;br /&gt;
81 static function factory get();&lt;br /&gt;
82 if(f == null)&lt;br /&gt;
83 f = new();&lt;br /&gt;
84 return f;&lt;br /&gt;
85 endfunction&lt;br /&gt;
86&lt;br /&gt;
87 function void register(wrapper_base w);&lt;br /&gt;
88 override_map[w] = w;&lt;br /&gt;
89 endfunction&lt;br /&gt;
90&lt;br /&gt;
91 function void set_override(wrapper_base requested_type,&lt;br /&gt;
92 wrapper_base override_type);&lt;br /&gt;
93 override_map[requested_type] = override_type;&lt;br /&gt;
94 endfunction&lt;br /&gt;
95&lt;br /&gt;
96 function object_base create(wrapper_base&lt;br /&gt;
97 requested_type);&lt;br /&gt;
98 object_base obj;&lt;br /&gt;
99 wrapper_base override_type =&lt;br /&gt;
100 override_map[requested_type];&lt;br /&gt;
101 obj = override_type.create_object();&lt;br /&gt;
102 return obj;&lt;br /&gt;
103 endfunction&lt;br /&gt;
104&lt;br /&gt;
105 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Метод register() добавляет новую запись в карту. Первоначально, при регистрации, тип не имеет переопределения. Таким образом, мы записали в карту тип, указывающий сам на себя. Set_override () метод заменяет запись в карте новым переопределенным типом. create() метод ищет переопределения на указанный тип, инициирует создание переопределенного типа и возвращает вновь созданный объект.&lt;br /&gt;
Класс-оболочка является наиболее интересным классом в нашем созвездии factory-related классов. Хотя он довольно прост, он делает большую часть тяжелой работы. Это основной интерфейс для фабрики, и большинство операций, которые производятся фабрикой, проходят через интерфейс оболочки.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;118 class wrapper #(type T=object_base) extends wrapper_base;&lt;br /&gt;
119&lt;br /&gt;
120 typedef wrapper#(T) this_type;&lt;br /&gt;
121&lt;br /&gt;
122 static this_type type_handle = get_type();&lt;br /&gt;
123&lt;br /&gt;
124 local function new();&lt;br /&gt;
125 endfunction&lt;br /&gt;
126&lt;br /&gt;
127 function object_base create_object();&lt;br /&gt;
128 T t = new();&lt;br /&gt;
129 return t;&lt;br /&gt;
130 endfunction&lt;br /&gt;
131&lt;br /&gt;
132 static function T create();&lt;br /&gt;
133 T obj;&lt;br /&gt;
134 factory f = factory::get();&lt;br /&gt;
135 assert($cast(obj, f.create(get_type())));&lt;br /&gt;
136 return obj;&lt;br /&gt;
137 endfunction&lt;br /&gt;
138&lt;br /&gt;
139 static function this_type get_type();&lt;br /&gt;
140 factory f;&lt;br /&gt;
141 if(type_handle == null) begin&lt;br /&gt;
142 type_handle = new();&lt;br /&gt;
143 f = factory::get();&lt;br /&gt;
144 f.register(type_handle);&lt;br /&gt;
145 end&lt;br /&gt;
146 return type_handle; &lt;br /&gt;
147 endfunction&lt;br /&gt;
148&lt;br /&gt;
149 static function void set_override(wrapper_base&lt;br /&gt;
150 override_type);&lt;br /&gt;
151 factory f = factory::get();&lt;br /&gt;
152 f.set_override(type_handle, override_type);&lt;br /&gt;
153 endfunction&lt;br /&gt;
154&lt;br /&gt;
155 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Все функции в wrapper#() являются статическими, за исключением конструктора и create_object (). Мы можем выполнить его статические функции, не заботясь о том, был ли они явно создан экземпляр класса. Так как каждая обертка специализации уникальна. Это означает, что статическая переменная type_handle является уникальной и может быть использован в качестве прокси-сервера для обернутого типа (то есть, типа, передающегося в качестве параметра, который используется для специализации класса). Так как тип является уникальным, и большинство методов являются статическими, мы можем рассматривать типа  больше как тип, а не объект.&lt;br /&gt;
&lt;br /&gt;
Тип инициализируется статически. Это происходит в следующей строке:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;static wrapper#(T) type_handle = get_type();&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Функция get_type () вызывается во время статической инициализации, которая не только создает экземпляр оболочки, но и регистрирует его в фабрике. Для регистрации класса в фабрике, сначала специализируется обертка с типом объекта, который вы оборачиваете. Используйте typedef  для специализации оболочки, как показано в следующем примере:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;typedef wrapper#(some_type) type_id;&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Это определение типа создает оболочку для SOME_TYPE типа, тип, производный от object_base.&lt;br /&gt;
Рисунок 4-8 иллюстрирует, как использовать фабрику с некоторыми  классами A, B, и С, которые являются производными от family_base.&lt;br /&gt;
&lt;br /&gt;
[[Файл:80.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Чтобы зарегистрировать классы с помощью фабрики, каждый из них имеет typedef  из обертки, параметризованной со своим типом. Ниже класс А. Класс B и C  похожи. Каждый из них имеет typedef, которая специализируется обертку.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;169 class A extends family_base;&lt;br /&gt;
170&lt;br /&gt;
171 typedef wrapper#(A) type_id;&lt;br /&gt;
172&lt;br /&gt;
173 virtual function void print();&lt;br /&gt;
174 $display(“A”);&lt;br /&gt;
175 endfunction&lt;br /&gt;
176 endclass&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
Ниже приводится короткая программа, которая использует фабрику.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;206 function void run();&lt;br /&gt;
207&lt;br /&gt;
208 f = factory::get();&lt;br /&gt;
209&lt;br /&gt;
210 h = family_base::type_id::create();&lt;br /&gt;
211 h.print();&lt;br /&gt;
212&lt;br /&gt;
213&lt;br /&gt;
family_base::type_id::set_override(B::type_id::get_type());&lt;br /&gt;
214&lt;br /&gt;
215 h = family_base::type_id::create();&lt;br /&gt;
216 h.print();&lt;br /&gt;
217&lt;br /&gt;
218 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/07_toy_factory/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The code makes heavy use of the double-colon (::) scope operator. Он используется для обращения к статической функции в factory и в wrapper#(). Сначала, мы получаем уникальный экземпляр структуры данных фабрики, затем создаем экземпляр объекта family_base. family_base :: type_id :: get_type() является статической функции внутри оболочки специализации для family_base. Мы  проверяем, что экземпляр family_base создается путем вызова print(). Далее, определяем переопределение B для family_base. Опять же, мы создаем экземпляр family_base. На этот раз, так как переопределение сейчас находится на месте, вместо того, чтобы получить экземпляр family_base, мы получаем экземпляр B.&lt;br /&gt;
&lt;br /&gt;
Наша фабрика не содержит все функциональные возможности фабрики, реализованной в OVM. Фабрика OVM обеспечивает отображение  строк (имен) в типы. Это позволяет переопределить цепочки, в то время, как фабрика этого не делает. Например, если B переопределяет А и C переопределяет B, когда вы просите экземпляр А, вы получите экземпляр C. Фабрика  OVM поддерживает два начальных базовых класса, ovm_object и ovm_component для зарегистрированных объектов, и метод create() для обоих, в то время, фабрика имеет только один первичный класс, object_base.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:80.png</id>
		<title>Файл:80.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:80.png"/>
				<updated>2013-03-25T10:59:21Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:79.png</id>
		<title>Файл:79.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:79.png"/>
				<updated>2013-03-25T10:59:03Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM</id>
		<title>OVM/OVM методология/Механика OVM</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM"/>
				<updated>2013-03-25T08:55:15Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= Механика  OVM =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Библиотека OVM предоставляет много возможностей для построения testbenches. В этой главе мы ознакомимся с наиболее важными возможностями, которые вы будете использовать  почти во всех ваши testbenches.&lt;br /&gt;
&lt;br /&gt;
== Компоненты и иерархия ==&lt;br /&gt;
&lt;br /&gt;
Первичная структура для создания элементов testbench  является компонента. Компонента  в OVM – это аналогия  модулю в Verilog. Компонента OVM сконструирована на основе класса, который обеспечивает ему различные характеристики, чем модуль Verilog и имеет различные использования импликаций. Среди таких характеристик является то, что классы создаются во время выполнения, а не во время разработки, как модули. Таким образом, OVM отвечает за создание компонента и сборки их в иерархии.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|[[Файл:69.png]]&lt;br /&gt;
|&amp;lt;graph&amp;gt;&lt;br /&gt;
digraph G{&lt;br /&gt;
node[shape=&amp;quot;circle&amp;quot;,fontsize=&amp;quot;11&amp;quot;,margin=&amp;quot;0.03,0.03&amp;quot;]&lt;br /&gt;
edge[arrowhead=none]&lt;br /&gt;
env -&amp;gt; {c1  c2}&lt;br /&gt;
c1 -&amp;gt; {child11  child12}&lt;br /&gt;
c2 -&amp;gt; {child21  child22}&lt;br /&gt;
child11 [label=&amp;quot;child1&amp;quot;]&lt;br /&gt;
child12 [label=&amp;quot;child2&amp;quot;]&lt;br /&gt;
child21 [label=&amp;quot;child1&amp;quot;]&lt;br /&gt;
child22 [label=&amp;quot;child2&amp;quot;]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/graph&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Рисунок 4-1 иллюстрирует простую иерархию компонент. После  мы покажем,  как построить эту иерархию с помощью средств OVM  для создания компонент и объединения их в иерархии:&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Самый верхний узел, ENV, является корнем. Корень отличается тем, что он не имеет предка. Все остальные узлы имеют только одного предка. Каждый узел имеет имя. Расположение каждого узла в иерархии может быть идентифицировано уникальным full_name (путем), который строится на нанизывании имен всех узлов между корнем и узлов в запросе, разделяя их сепаратором иерархии, точкой (.). Например, путь к компоненту, второго потомка с2 -  top.c2.child2.&lt;br /&gt;
&lt;br /&gt;
Компонент в ОВМ является классом, производным от ovm_component. Простейшая компоненты -  листья, те, которые не имеют потомков.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;57 class child extends ovm_component;&lt;br /&gt;
58&lt;br /&gt;
59 function new(string name, ovm_component parent);&lt;br /&gt;
60 super.new(name, parent);&lt;br /&gt;
61 endfunction&lt;br /&gt;
62&lt;br /&gt;
63 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Конструктор имеет два параметра, имя компоненты и указатель на его предка. Название -  простое имя, а не иерархический путь. Предок  предоставляет место для подключения  новой компоненты в иерархии. Полный  путь потомка создается путем объединения имени потомка с полным именем предка, разделенный точкой (.).OVM предоставляет методы для получения и имени и полного пути компоненты:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;string get_name();&lt;br /&gt;
string get_full_name();&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подчиненные компоненты создаются  в функции build(), которая вызывается на этапе сборки (фазы объясняется далее в этой главе). Создание экземпляра компонента включает вызов new() для выделения памяти для нее и передачи соответствующих параметров в конструктор. В компоненте, показанной ниже, мы создаем две подчиненные компоненты, и child1 child2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;71 class component extends ovm_component;&lt;br /&gt;
72&lt;br /&gt;
73 child child1;&lt;br /&gt;
74 child child2;&lt;br /&gt;
75&lt;br /&gt;
76 function new(string name, ovm_component parent);&lt;br /&gt;
77 super.new(name, parent);&lt;br /&gt;
78 endfunction&lt;br /&gt;
79&lt;br /&gt;
80 function void build();&lt;br /&gt;
81 child1 = new(“child1”, this);&lt;br /&gt;
82 child2 = new(“child2”, this);&lt;br /&gt;
83 endfunction&lt;br /&gt;
84&lt;br /&gt;
85 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как component, ENV также создает экземпляры двух подчиненных компонент, c1 и c2. Вся иерархия  с корнем в модуле,  называемым top в нашем проекте.(??)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;131 module top;&lt;br /&gt;
132&lt;br /&gt;
133 env e;&lt;br /&gt;
134&lt;br /&gt;
135 initial begin&lt;br /&gt;
136 e = new(“env”);&lt;br /&gt;
137 run_test();&lt;br /&gt;
138 end&lt;br /&gt;
139&lt;br /&gt;
140 endmodule&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вызов new() создает верхний уровень окружающей среды. run_test () начинает выполнение testbench.&lt;br /&gt;
&lt;br /&gt;
В SystemVerilog, модули, интерфейсы и программные блоки создаются во время разработки, в то время как классы создаются после разработки, во время выполнения. Таким образом, чтобы создать иерархию классов, мы должны иметь интерфейс, модуль или программу, который содержит начальный блок, который начинает процесс создание компонент иерархии, основанных на классах. Интерфейсы предназначены для использования в качестве средства связи между двумя модулями и не очень хорошо подходят для использования в качестве корня иерархии классов. Программные  блоки или модули могут быть использованы как  корень (??). Для нашей простой иерархии, это не имеет значения. Позже, когда мы подключаем класс-компонент для модуля на основе аппаратных средств, мы увидим, что использование модуля предпочтительнее программным блокам.&lt;br /&gt;
&lt;br /&gt;
=== Перемещение по иерархии ===&lt;br /&gt;
&lt;br /&gt;
Мы можем исследовать структуры данных, используемые для реализации компонента иерархии с некоторыми методами, предусмотренными в ovm_component. Потомки компоненты хранятся в ассоциативном массиве. Этот массив напрямую не доступен, но он может быть доступен через иерархию API. Этот API-интерфейс похож на встроенные методы SystemVerilog, предназначенные для ассоциативных массивов.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;int get_first_child(ref string name);&lt;br /&gt;
int get_next_child(ref string name);&lt;br /&gt;
ovm_component get_child(string name);&lt;br /&gt;
int get_num_children();&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
get_first_child () и get_next_child () работают вместе для перебора множества потомков, содержащихся в компоненте. get_first_child () извлекает имя первого ребенка в списке. Он возвращает имя в качестве ссылки. get_next_child () возвращает имя следующего ребенка в списке. Он  возвращает 1, если имеется следующая имя потомка и 0, если был достигнут конец списка. get_child () преобразует имя в ссылку компоненты.&lt;br /&gt;
&lt;br /&gt;
Используя эти функции, мы можем пройти по компонентам иерархии.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;73 function void depth_first(ovm_component node,&lt;br /&gt;
74 int unsigned level = 0);&lt;br /&gt;
75&lt;br /&gt;
76 string name;&lt;br /&gt;
77&lt;br /&gt;
78 if(node == null)&lt;br /&gt;
79 return;&lt;br /&gt;
80&lt;br /&gt;
81 visit(node, level);&lt;br /&gt;
82&lt;br /&gt;
83 if(node.get_first_child(name))&lt;br /&gt;
84 do begin&lt;br /&gt;
85 depth_first(node.get_child(name), level+1);&lt;br /&gt;
86 end while(node.get_next_child(name));&lt;br /&gt;
87&lt;br /&gt;
88 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/utils/traverse.svh&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эта функция будет осуществляющая обход в глубину иерархии, называя visit(). Мы используем get_first_child () и get_next_child () для перебора списка каждого потомка в каждом узле. Для каждого итерации мы вызываем depth_first () рекурсивно. Для нашего маленького проекта, результат будет  следующий:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;pre&amp;gt;+ env&lt;br /&gt;
| + env.c1&lt;br /&gt;
| | env.c1.child1&lt;br /&gt;
| | env.c1.child2&lt;br /&gt;
| + env.c2&lt;br /&gt;
| | env.c2.child1&lt;br /&gt;
| | env.c2.child2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция visit() использует  глубину узла и  определяет является ли он конечным  для печати строки каждого узла.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Singleton Top ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Компоненты, которые не имеют предков (то есть,  параметр предок в конструкторе является нулевым) называют orphans.(??) В OVM вы можете создать столько компоненты без предков, сколько вам необходимо. Тем не менее, не существует такого понятия, как истинные orphans. Любой компонент, чей предок является  null,  относится к встроенному предку, называемому ovm_top. ovm_top состоит из одного экземпляра ovm_root. Это предок всех компонент, которые имеют предков. В самом деле, env  в нашем предыдущем примере является потомком ovm_top. Так как он не имеет предка, то автоматически получают ovm_top в качестве родителя.&lt;br /&gt;
&lt;br /&gt;
singleton -  широко известный  объектно-ориентированных шаблон проектирования, который имеет private (local) или protected конструкторы и статическую функцию get, которая возвращает тот же самый указатель не смотря на то, сколько раз она вызывается. Это означает, что возможно существование одного экземпляра, и что экземпляр создается  автоматически при вызове функции get. ovm_top содержит дескриптор экземпляра singleton  ovm_root(???). Он статически инициализируется при вызове ovm_root :: Get (). Вы можете вызвать ovm_root :: Get () в любое время, но в этом нет необходимости, так как ovm_top предусматривает это.&lt;br /&gt;
&lt;br /&gt;
Есть целый ряд полезных следствий существования singleton top-level компоненты. Одним из них является, что вы можете достичь любую компоненту из ovm_top. Если вы запустили алгоритм обхода иерархии, начиная с ovm_top, вы доберетесь до каждой компоненты в системе. Другим следствием является то, что любая компонента, включая порты, экспорт и каналы, которые создаются внутри модуля, достижима из ovm_top. Если вы хотите изменить отчет во всех обработчиках компонент, вы можете сделать это, вызвав  одну из иерархических функции отчетности в ovm_top. ovm_top содержит все механизмы синхронизации, которые объясняются далее в этой главе.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Connectivity ==&lt;br /&gt;
&lt;br /&gt;
Компоненты соединены друг с другом через TLM port и exports. Port и exports обеспечивают средства для компонент, или, точнее, процессы в компонентах для синхронизации и связи друг с другом. Port и exports -  объекты, которые образуют  пункт связывания для обеспечения межкомпонентной связи. Как уже говорилось в предыдущей главе, exports обеспечивают функции и задачи, которые могут быть вызваны из Port.&lt;br /&gt;
&lt;br /&gt;
[[Файл:70.png]]&lt;br /&gt;
 &lt;br /&gt;
Метод connect  портов и exports используется для связывания их вместе.&lt;br /&gt;
&lt;br /&gt;
initiator_port.connect(target.export)&lt;br /&gt;
&lt;br /&gt;
Этот метод создает ассоциацию, или связывание, между port и export так, что port может вызывать(запускать) задачи и функции из export. Для успешного соединения, типы  port и export должны совпадать. То есть, типы интерфейсов должны быть одинаковыми, и тип объекта, передающегося в интерфейс должны быть одинаковыми.&lt;br /&gt;
&lt;br /&gt;
=== Подключение через Иерархию ===&lt;br /&gt;
&lt;br /&gt;
Как и контактами в RTL дизайне, нам нужно подключиться к портам  TLM через иерархические границы. Рисунок 4-3 использует простой дизайн, чтобы показать, как сделать эти соединения. Этот проект содержит исходную компоненту с двумя port(выходами), которые в конечном итоге подключаются к двум входам(export). Для осуществления подключения между этими компонентами, мы должны расширить port и export на  уровень выше в иерархии.&lt;br /&gt;
 &lt;br /&gt;
[[Файл:71.png]]&lt;br /&gt;
&lt;br /&gt;
Компонент source содержит два порта, и first_put_port, second_put_port.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;65 class source extends ovm_component;&lt;br /&gt;
66&lt;br /&gt;
67 ovm_put_port #(trans_t) first_put_port;&lt;br /&gt;
68 ovm_put_port #(trans_t) second_put_port;&lt;br /&gt;
69&lt;br /&gt;
70 function new(string name, ovm_component parent);&lt;br /&gt;
71 super.new(name, parent);&lt;br /&gt;
72 endfunction&lt;br /&gt;
73&lt;br /&gt;
74 function void build();&lt;br /&gt;
75 first_put_port = new(“first_put_port”, this);&lt;br /&gt;
76 second_put_port = new(“second_put_port”, this);&lt;br /&gt;
77 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Кроме того, компонента sink создает экспорт и создает его в функции build. Экспорт связан с внутренним каналом, FIFO, от которого компонента может получать объекты во время выполнения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;126 class sink extends ovm_component;&lt;br /&gt;
127&lt;br /&gt;
128 ovm_put_export #(trans_t) put_export;&lt;br /&gt;
129 local tlm_fifo #(trans_t) fifo;&lt;br /&gt;
130&lt;br /&gt;
131 function new(string name, ovm_component parent);&lt;br /&gt;
132 super.new(name, parent);&lt;br /&gt;
133 endfunction&lt;br /&gt;
134&lt;br /&gt;
135 function void build();&lt;br /&gt;
136 put_export = new(“put_export”, this);&lt;br /&gt;
137 fifo = new(“fifo”, this);&lt;br /&gt;
138 endfunction&lt;br /&gt;
139&lt;br /&gt;
140 function void connect();&lt;br /&gt;
141 put_export.connect(fifo.put_export);&lt;br /&gt;
142 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
source_wrapper должен создать соединение между внутренним источником компоненты и  внешней ее границей. Он делает это соединение, создавая свои порты, которые имеют тот же тип, что и тип  портов lower-level, в нашем случае, тех, которые принадлежат к source.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;98 class source_wrapper extends ovm_component;&lt;br /&gt;
99&lt;br /&gt;
100 source s;&lt;br /&gt;
101 ovm_put_port #(trans_t) put_port1;&lt;br /&gt;
102 ovm_put_port #(trans_t) put_port2;&lt;br /&gt;
103&lt;br /&gt;
104 function new(string name, ovm_component parent);&lt;br /&gt;
105 super.new(name, parent);&lt;br /&gt;
106 endfunction&lt;br /&gt;
107&lt;br /&gt;
108 function void build();&lt;br /&gt;
109 s = new(“source”, this);&lt;br /&gt;
110 put_port1 = new(“put_port1”, this);&lt;br /&gt;
111 put_port2 = new(“put_port2”, this);&lt;br /&gt;
112 endfunction&lt;br /&gt;
113&lt;br /&gt;
114 function void connect();&lt;br /&gt;
115 s.first_put_port.connect(put_port1);&lt;br /&gt;
116 s.second_put_port.connect(put_port2);&lt;br /&gt;
117 endfunction&lt;br /&gt;
118&lt;br /&gt;
119 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После того как порты в source_wrapper были созданы, они подключаются к портам lower-level  компоненты source с помощью метода Connect. Видимость портов на более высоком уровне иерархии достигается  таким же образом, как мы видим  в sinker.&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
160 class sinker extends ovm_component;&lt;br /&gt;
161&lt;br /&gt;
162 ovm_put_export #(trans_t) first_put_export;&lt;br /&gt;
163 ovm_put_export #(trans_t) second_put_export;&lt;br /&gt;
164&lt;br /&gt;
165 sink sink1;&lt;br /&gt;
166 sink sink2;&lt;br /&gt;
167&lt;br /&gt;
168 function new(string name, ovm_component parent);&lt;br /&gt;
169 super.new(name, parent);&lt;br /&gt;
170 endfunction&lt;br /&gt;
171&lt;br /&gt;
172 function void build();&lt;br /&gt;
173 sink1 = new(“sink1”, this);&lt;br /&gt;
174 sink2 = new(“sink2”, this);&lt;br /&gt;
175 first_put_export = new(“first_put_export”, this);&lt;br /&gt;
176 second_put_export = new(“second_put_export”, this);&lt;br /&gt;
177 endfunction&lt;br /&gt;
178&lt;br /&gt;
179 function void connect();&lt;br /&gt;
180 first_put_export.connect(sink1.put_export);&lt;br /&gt;
181 second_put_export.connect(sink2.put_export);&lt;br /&gt;
182 endfunction&lt;br /&gt;
183&lt;br /&gt;
184 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Два нижних уровня компоненты sink и exports  создаются обычным способом. Затем они соединяются с помощью метода подключения export. Сейчас мы создаем port-export соединение между source_wrapper и sinker, также с помощью функции Connect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;192 class env extends ovm_component;&lt;br /&gt;
193&lt;br /&gt;
194 sinker s;&lt;br /&gt;
195 source_wrapper sw;&lt;br /&gt;
196&lt;br /&gt;
197 function new(string name, ovm_component parent = null);&lt;br /&gt;
198 super.new(name, parent);&lt;br /&gt;
199 endfunction&lt;br /&gt;
200&lt;br /&gt;
201 function void build();&lt;br /&gt;
202 s = new(“sinker”, this);&lt;br /&gt;
203 sw = new(“source_wrapper”, this);&lt;br /&gt;
204 endfunction&lt;br /&gt;
205&lt;br /&gt;
206 function void connect();&lt;br /&gt;
207 sw.put_port1.connect(s.first_put_export);&lt;br /&gt;
208 sw.put_port2.connect(s.second_put_export);&lt;br /&gt;
209 endfunction&lt;br /&gt;
210&lt;br /&gt;
211 task run; &lt;br /&gt;
212 global_stop_request();&lt;br /&gt;
213 endtask&lt;br /&gt;
214&lt;br /&gt;
215 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для новых пользователей часто может вызвать трудности определить,  порт или экспорт необходимо подключить и какой объект является аргументом. Вы можете легко понять это, следуя потоку управления  через систему. Общее правило  заключается в том, что запрашиваемый порт вызывается с помощью функции connect() и передачи в нее запрашиваемого port или export в качестве аргумента. Рисунок 4-4 показывает поток управления через иерархическую систему.&lt;br /&gt;
&lt;br /&gt;
[[Файл:72.png]]&lt;br /&gt;
 &lt;br /&gt;
Порт – объект  вызова и экспорт - объект вызванной функции или задачи. Вы можете воспринимать порты, как вызов экспорта. Так, в env, мы вызываем connect на put_ports с put_exports в качестве аргументов. Для port-to- port  и export-to-export иерархии соединений, вызывающий будет чуть менее очевиден. Если  вызов осуществляется со стороны порта, вы можете считать, что вызов lowest-level порта в иерархии является вызовом методов интерфейса  upper-level порта. Аналогично, если экспорт объект вызова, вы можете считать upper-level экспорт  вызовом lower-level export. В таблице ниже приведены возможные типы подключения:&lt;br /&gt;
 &lt;br /&gt;
[[Файл:73.png]]&lt;br /&gt;
&lt;br /&gt;
=== Примечание для пользователей AVM ===&lt;br /&gt;
&lt;br /&gt;
В AVM-3.0 соединения были сделаны аналогично: вызовам функции connect() из port и export. Кроме того, 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.)&lt;br /&gt;
&lt;br /&gt;
== Фазы ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для традиционных модулей Verilog  полная разработка  проекта и  его выполнение осуществялется в симуляторе. Так как OVM компоненты - это классы, они  создаются и подключаются, и их выполнение начинается за пределами Verilog разработчика. Компоненты создаются вызовом конструктора класса операцией new(), которая выделяет память и выполняет инициализацию. В то время как  Verilog время выполнения  run-time engine managing instantiation, разработка, и выполнение компонент на основе классов, функциональность  компонент разделена на фазы, и контроллер фазы OVM  управляет их исполнением. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Каждая фаза представлена в компоненте как виртуального метода (задачи или функция) с тривиальной реализацией по умолчанию. Эти  phase callbacks   реализуются разработчиком компоненты, который обеспечивает соответствующую функциональность. Фаза контроллера гарантирует, что фазы выполняются в надлежащем порядке. Набор стандартных фаз показано в следующей таблице:&lt;br /&gt;
&lt;br /&gt;
[[Файл:74.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:75.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Каждая фаза имеет определенную цель. разработчики Компонент  должны заботиться о том, чтобы функциональность реализованная в каждой фазе  callback соответствовала фазе определения.&lt;br /&gt;
&lt;br /&gt;
•	'''New''' технически не является фазой, в том, что она не управляется фазой контроллера. Тем не менее, для каждого компонента, конструктор должен быть выполнен и завершен в целях создания компоненты. Таким образом,  new должен выполниться до build() или любого другого последующего этапах  выполнения.&lt;br /&gt;
&lt;br /&gt;
•	'''build'''  это место, где новые компоненты, порты и экспорт создаются и настраиваются. Это также рекомендуемое место для вызова set_config_ * и get_config_ * (см. Section4.4). &lt;br /&gt;
&lt;br /&gt;
•	'''connect''' – место, где компоненты, порты и экспорт созданные в  build() связываются.&lt;br /&gt;
&lt;br /&gt;
•	'''end_of_elaboration''' здесь вы можете выполнить изменение конфигураций, зная, что разработка уже завершена. То есть, вы можете предположить, что все компоненты созданы и соединены. &lt;br /&gt;
&lt;br /&gt;
•	'''start_of_simulation''' выполняется только до времени 0.&lt;br /&gt;
&lt;br /&gt;
•	'''run'''    pre-defined task фазой. Все выполняемые задачи работают параллельно. Каждая выполняемая задача продолжается, пока ее управление не достигнет оператора EndTask или она явно будет прекращена. Позже в этой главе мы обсудим, как завершать testbenches.&lt;br /&gt;
&lt;br /&gt;
•	'''extract'''  предназначен для сбора информации, касающейся покрытия или другой информации о том, как ответить на  вопросы testbench.  &lt;br /&gt;
&lt;br /&gt;
•	'''проверка''' - место, где выполняется любая проверка правильности или проверка валидации извлеченных данных. &lt;br /&gt;
&lt;br /&gt;
•	'''отчет''' -место , где делаются окончательные отчеты.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ниже простой пример,  использующий ovm_report_info () для иллюстрации порядка выполнения фаз.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;38 class sub_component extends ovm_component;&lt;br /&gt;
39&lt;br /&gt;
40 function new(string name, ovm_component parent);&lt;br /&gt;
41 super.new(name, parent);&lt;br /&gt;
42 endfunction&lt;br /&gt;
43&lt;br /&gt;
44 function void build();&lt;br /&gt;
45 ovm_report_info(“build”, ““);&lt;br /&gt;
46 endfunction&lt;br /&gt;
47&lt;br /&gt;
48 function void connect();&lt;br /&gt;
49 ovm_report_info(“connect”, ““);&lt;br /&gt;
50 endfunction&lt;br /&gt;
51&lt;br /&gt;
52 function void end_of_elaboration();&lt;br /&gt;
53 ovm_report_info(“end_of_elaboration”, ““);&lt;br /&gt;
54 endfunction&lt;br /&gt;
55&lt;br /&gt;
56 function void start_of_simulation();&lt;br /&gt;
57 ovm_report_info(“start_of_simulation”, ““);&lt;br /&gt;
58 endfunction&lt;br /&gt;
59&lt;br /&gt;
60 task run();&lt;br /&gt;
61 ovm_report_info(“run”, ““);&lt;br /&gt;
62 endtask&lt;br /&gt;
63&lt;br /&gt;
64 function void extract();&lt;br /&gt;
65 ovm_report_info(“extract”, ““);&lt;br /&gt;
66 endfunction&lt;br /&gt;
67&lt;br /&gt;
68 function void check();&lt;br /&gt;
69 ovm_report_info(“check”, ““);&lt;br /&gt;
70 endfunction&lt;br /&gt;
71&lt;br /&gt;
72 function void report();&lt;br /&gt;
73 ovm_report_info(“report”, ““);&lt;br /&gt;
74 endfunction&lt;br /&gt;
75&lt;br /&gt;
76 endclass&lt;br /&gt;
file: 04_OVM_mechanics/03_phases/top.sv&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В компоненте верхнего уровня, мы создаем две  компоненты, каждая из которых в свою очередь создает две sub_component. sub_components являющимися такими же, как и сами компоненты; каждая фаза обратного  callback просто печатает строку, определяющую фазу. При выполнении, вы получите следующий результат:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;OVM_INFO @ 0 [RNTST] Running test ...&lt;br /&gt;
OVM_INFO @ 0: env.c1 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [build]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [connect]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [end_of_elaboration]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [start_of_simulation]&lt;br /&gt;
OVM_INFO @ 0: env.c2 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s2 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c2.s1 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c1 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s2 [run]&lt;br /&gt;
OVM_INFO @ 0: env.c1.s1 [run]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s1 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s2 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c1 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s1 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s2 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c2 [extract]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s1 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s2 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c1 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s1 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s2 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c2 [check]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s1 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c1.s2 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c1 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s1 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c2.s2 [report]&lt;br /&gt;
OVM_INFO @ 1: env.c2 [report]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Вы видите, что build()выполняется сверху вниз и остальные фазы запуска снизу вверх. Вы также можете видеть, что каждый этап завершается во всех компонентах до начала следующего этапа. Таким образом, в connect(), например, вы можете расчитывать, что build() завершена во всех компонентах. Вы также заметите, что время  запускается после  фазы run. В нашем примере задача run - тривиальна, она просто выполняет задержку времени на единицу (# 1).&lt;br /&gt;
&lt;br /&gt;
un_test (), упоминалось ранее в разделе 4.1, инициирует фазу выполнение. Он начинает выполнение фаз по порядку и контролирует, что каждый этап завершен до начала следующего.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==	Config ==&lt;br /&gt;
&lt;br /&gt;
Для увеличения повторного использования компонент, желательно объявит их с параметрами, которые могут быть внешне настроены. Конфигурационные средства обеспечивают возможности для этого. Они основаны на базе пар имя-значение, и называются configuration items, которые организованы иерархически. Каждая компонента содержит таблицу конфигураций элементов конфигурации,  так как компоненты организованы в качестве дерева, каждый элемент в базе данных может быть уникально размещен по расположению компоненты и имени элемента конфигурации&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:76.png]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Класс ovm_component содержит два набора методов для  конфигурации размещения элементов в базу данных и потом для их получения. Это  set_config_ * и * get_config_. В таблице ниже приведены оба набора.&lt;br /&gt;
&lt;br /&gt;
[[Файл:77.png]] &lt;br /&gt;
&lt;br /&gt;
Set_config_ * функция помещает элемент в конфигурационную базу данных текущего компонента, то есть в экземпляре компонента, в котором функция вызывается. Каждая из этих функций принимают три аргумента, имя, field_name и значение. Аргумент name -  имя пути, который представляет набор компонент, которые должны соответсвовать кофигурациям этого элемента. name используется в * get_config_, чтобы найти элементы в базе данных конфигурации.  field_name -  это имя поля и должно быть уникальным в пределах текущей базы конфигураций. value является частью пары имя-значение и его тип может быть строка, INT, или ovm_object, в зависимости от вызываемой функции. Кроме того, set_config_object принимает копию аргумента для указания, был ли объект передан в качестве значения так как значение должно быть клонировано прежде чем оно будет помещено в базу конфигурации. &lt;br /&gt;
Функции get_config_ * извлекает элементов из базы данных конфигураций.  Эти функции принимают только два аргумента, имя поля и INOUT переменную, который содержит значение расположения элемента. Они также возвращают бит, чтобы указать был ли удачно расположен запрашиваемый объект. * Get_config_ функции не принимают аргументы имени пути, как их set_config_ * коллеги, поскольку они используют путь к текущему компоненту, как ссылку, чтобы найти элементы конфигурации. Они разработаны, чтобы узнать, есть значение элемента конфигурации для текущего контекста, то есть, компонент, в котором функция get_config_ * вызывается.&lt;br /&gt;
&lt;br /&gt;
Алгоритм поиска для извлечения элементов конфигурации использует имя пути компонента запроса и путь к файлу вставляемый в каждый  item. Он начинается с поиска конфигурационных элементов в базе данных в верхней компоненте по field_name. Если такой элемент существует, то он затем спрашивает, соответствует ли указанный путь к файлу пути имени компоненты. Если элемента с указанным field_name нет или имена путей не совпадают, то поиск идет с дочерней компоненты. Этот процесс продолжается до соответствия или  до достижения компоненты начала поиска.&lt;br /&gt;
&lt;br /&gt;
Путь  каждого элемент конфигурации может быть регулярным выражением. Таким образом, мы  используем алгоритм соответствия регулярных выражений для нахождения совпадения с  путем запрошенной компоненты и именем пути элемента конфигурации. Эффект заключается в соответствии иерархической области. &lt;br /&gt;
В качестве примера, рассмотрим простую иерархию на рисунке 4-5. Давайте предположим, что в  env::build()мы запускаем два вызова set_config_ *:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;112 function void build();&lt;br /&gt;
113 c1 = new(“c1”, this);&lt;br /&gt;
114 c2 = new(“c2”, this);&lt;br /&gt;
115&lt;br /&gt;
116 set_config_int(“c2.*”, “i”, 42);&lt;br /&gt;
117 set_config_int(“*”, “t”, 19);&lt;br /&gt;
118 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/04_config/top.sv&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Это вызовет запись двух конфигурационных элементов в базу данных в env. Обратите внимание на звездочку (*) в имени пути. Имя пути в вызове set_config_ * -  регулярное выражение, и дикие символы  используются для указать несколько областей, к которыми элемент применяется. По элементу i, c2. * показывает, что в любой области ниже c2 в иерархии, i будет  определять указанное значение. В этом случае указанное значение -  42. Если вы опустите звездочку, то элемент конфигурации применяется только к c2, а не к любому из своих потомков. &lt;br /&gt;
Состояние конфигурации базы данных для каждого компонента в иерархии  после  вызова set_config_ * показано на рисунке 4-6,&lt;br /&gt;
&lt;br /&gt;
[[Файл:78.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
пусть в элементе top.c1.child1 мы осуществим вызов:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;int i;&lt;br /&gt;
...&lt;br /&gt;
get_config_int(“i”, i)&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Поиск задает вопрос: Каково значение конфигурации i в иерархических рамках top.c1.child1? Чтобы ответить на этот вопрос, конфигурации базы данных в env ищется в первую очередь. Запись i там говорит, что значение i совпадающее с  env.c2. *  -  42. Тем не менее, компоненты, из которых был сделан запрос, находятся в c1 sub-hierarchy. Таким образом,  не существует соответствия, а get_config_int () возвращает статус неудачи. Запрос в любую компоненту, которая является дочерней c2 будет успешно завершен и вернется значение 42. &lt;br /&gt;
&lt;br /&gt;
Ниже приведен код для функцию build() дочерней компоненты. Это место где эти компоненты ищут конфигурационные значения i и t.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;60 function void build();&lt;br /&gt;
61&lt;br /&gt;
62 string msg;&lt;br /&gt;
63&lt;br /&gt;
64 if(!get_config_int(“t”, t)) begin&lt;br /&gt;
65 $sformat(msg, “no value for t found in config&lt;br /&gt;
database, using default value of %0d”, t);&lt;br /&gt;
66 ovm_report_warning(“build”, msg);&lt;br /&gt;
67 end&lt;br /&gt;
68&lt;br /&gt;
69 if(!get_config_int(“i”, i)) begin&lt;br /&gt;
70 $sformat(msg, “no value for i found in config&lt;br /&gt;
database, using default value of %0d”, i);&lt;br /&gt;
71 ovm_report_warning(“build”, msg);&lt;br /&gt;
72 end&lt;br /&gt;
73&lt;br /&gt;
74 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/04_config/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Следующий пример показывает вывода  при выполнении этого проекта:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# OVM_INFO @ 0 [RNTST] Running test ...&lt;br /&gt;
# OVM_WARNING @ 0: env.c1.child1 [build] no value for i found in&lt;br /&gt;
config database, using default value of 91&lt;br /&gt;
# OVM_WARNING @ 0: env.c1.child2 [build] no value for i found in&lt;br /&gt;
config database, using default value of 91&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Запрос на  конфигурационный элемент t был успешным во всех контекстах с того момента как вызов set_config_int установил, что tдоступно во всех контекстах. Два запроса на элемент конфигурации i были успешными, и два нет. Результат получился такой потому что мы ограничены наличием i только компонентах на уровне или ниже env.c2. Компоненты на уровне или ниже c1 не могут видеть элемент конфигурации i из-за того, как мы создали базу данных конфигурации.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Конфигурация и фазы ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь, когда мы знаем, что набор вызовов для размещения и извлечение элементов в конфигурацию базы данных, следующей задачей, является эффективное применение этих функции для настройки компонент. Конфигурация может быть использована для изменения поведения или структуры testbench. Как правило, поведенческий  и структурный режимы определяются, когда testbench начинает работу, поэтому он является наиболее удобным местом для установки параметров конфигурации на одной из ранних стадий, таких как new, build или connect.&lt;br /&gt;
&lt;br /&gt;
Из таблицы фаз, вы можете увидеть, что new и build являются нисходящими фазами, в то время как все остальные фазы восходящие. Так что, если вы хотите установить элемент конфигурации в базу данных на более высокий уровень контекста, чтобы он был подхвачен более низким уровнем, вы должны вызвать функцию set_config_ * в new или build фазе. Фазы выполняется дискретно, это означает, что каждая фаза выполняется до начала следующей. Вы можете установить элементы конфигурации в new или build фазу и извлекать их для использования в установке поведенческого режима или изменения топологии на этапе build. В вашей функции build, сначала вызовите get_config_ * для извлечения элементов из более высоких уровней иерархии для управления конфигурацией текущего уровня. Затем добавьте вызов set_config_ * для  помещения элементов конфигурации в базу данных для использования компонентами на более низких уровнях иерархии. Наконец, используя соответствующие элементы конфигурации, обработайте компоненты. Важно вызвать get_config_ * вначале, потому, что указанная информация может влиять на значения, которые установлены на более низких уровнях иерархии.&lt;br /&gt;
&lt;br /&gt;
Например, настройка топологии включает в себя установку параметров топологии&lt;br /&gt;
на верхнем уровне среды, а затем применение эти параметров в различных компонентах, которые находятся ниже верхнего уровня среды в иерархии. Наш пример имеет шину, которая может иметь любое количество masters или slaves. Число masters и slaves расположены в верхнем уровне среды. Модель шину подхватывает эту конфигурационную информацию и использует ее для построения шины. В build функции верхнего уровня окружающей среды, мы создаем модель шины и настраиваем ее с числом masters и slaves.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;129 function void build();&lt;br /&gt;
130 set_config_int(“bus”, “masters”, 4);&lt;br /&gt;
131 set_config_int(“bus”, “slaves”, 8);&lt;br /&gt;
132 b = new(“bus”, this);&lt;br /&gt;
133 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/05_config_topo/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Модель шины построена таким образом, что число masters и slaves не фиксировано. Вместо этого,  количество приходит из конфигурации системы.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;90 function void build();&lt;br /&gt;
91&lt;br /&gt;
92 int unsigned i;&lt;br /&gt;
93&lt;br /&gt;
94 if(!get_config_int(“masters”, masters)) begin&lt;br /&gt;
95 $sformat(msg, “\”masters\” is not in the&lt;br /&gt;
configuration database, using default value of %0d”, masters);&lt;br /&gt;
96 ovm_report_warning(“build”, msg);&lt;br /&gt;
97 end&lt;br /&gt;
98&lt;br /&gt;
99 for(i = 0; i &amp;lt; masters; i++) begin&lt;br /&gt;
100 $sformat(name, “master_%0d”, i);&lt;br /&gt;
101 m = new(name, this);&lt;br /&gt;
102 end&lt;br /&gt;
103&lt;br /&gt;
104 if(!get_config_int(“slaves”, slaves)) begin&lt;br /&gt;
105 $sformat(msg, “\”slaves\” is not in the configuration&lt;br /&gt;
database, using default value of %0d”, slaves);&lt;br /&gt;
106 ovm_report_warning(“build”, msg);&lt;br /&gt;
107 end&lt;br /&gt;
108&lt;br /&gt;
109 for(i = 0; i &amp;lt; slaves; i++) begin&lt;br /&gt;
110 $sformat(name, “slave_%0d”, i);&lt;br /&gt;
111 s = new(name, this);&lt;br /&gt;
112 end&lt;br /&gt;
113&lt;br /&gt;
114 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/05_config_topo/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В функции build для модели шины, схема получает необходимую конфигурационную информацию, используя вызовы get_config_int. В каждом случае, Возвращаемое значение проверяется, чтобы определить, была ли получена запрошенная конфигурационная информация. Если нет, сообщение об ошибке предупреждает, что конфигурационный элемент не найден и что будет использовано значение по умолчанию. С точки зрения Best Practices, важно убедиться, что возвращается значение, проверено и выдается предупреждение, в случае ошибки. Без этой проверки, то, что  используется значения по умолчанию, могло остаться незамеченным. В некоторых случаях это может быть приемлемо, в других случаях - нет. Человек,  создающий модель шины не может знать всех обстоятельств, при которых модель будет использоваться. Поэтому важно сделать все возможное, чтобы  модель была надежной. Проверка возвращаемых значений и выдача соответствующих сообщений является одним из способов повышения надежности модели.&lt;br /&gt;
&lt;br /&gt;
В цикле, где мы строим masters, ссылки на каждого нового master сохраняются в той же переменной, m. Каждый новый master заменяет предыдущий. Мы не используем массив для хранения всех компонент. В каждой итерации, мы используем $ sformat, чтобы создать уникальное имя. Конструктор, new(), вызывает super.new (), конструктор в ovm_component базовом классе, который отвечает за вставку вновь созданную компоненту потомка в родительский список. Нет  необходимости явно сохранять компоненту, потому что родительская компонента сделает это за нас. Цикл для создания slaves организован таким же образом.&lt;br /&gt;
&lt;br /&gt;
== Фабрика ==&lt;br /&gt;
&lt;br /&gt;
Структура testbench определяется организацией компонент в иерархии и то, как эти объекты связаны между собой. Поведение testbench определяется процедурным кодом в фазе callbacks—build, connect, run и так далее. Есть моменты, когда это необходимо изменить поведение или часть структуры внешне, то есть во время выполнения, не касаясь, кода testbench. Например, чтобы ввести ошибку в систему, вы можете заменить обычный драйвер  драйвером с ошибкой, который умышленно вводит ошибки. Вместо повторного кодирования окружающей среды для использования другого драйвера, вы можете использовать фабрику, чтобы сделать замену автоматически.&lt;br /&gt;
&lt;br /&gt;
Фабрика обеспечивает средства для замены одного объекта на другой без использования текстового редактора для изменения testbench. Вместо создания объект с помощью new(), вы вызываете функцию create  фабрики. Фабрика  хранит список зарегистрированных объектов и, при необходимости, набор переопределения связанный с каждой из них. При создании объекта с помощью фабрики, список переопределения проверяется на наличие такого объекта. Если он есть, то переопределенный объект возвращается. В противном случае, зарегистрированный объект возвращается. &lt;br /&gt;
&lt;br /&gt;
Фабрика  является OVM структурой данных. Она  глобальна в области видимости, и только один элемент может существовать  (то есть, это singleton). Она служит в качестве полиморфных конструкторов, одна функция, которая позволяет создавать различные объекты. Она  обеспечивает средства для регистрации объектов, и для их переопределения. Объекты, зарегистрированные как переопределения,  должны быть производным от объекта, от которого они переопределялись. Для того, чтобы одна функция возвращала несколько объектов, каждый из этих объектов должны быть получен от общего базового класса.&lt;br /&gt;
Важным компонентом фабрики является оболочка, класс, который описывает объект, который мы хотим создать с  помощью фабрики. Структура данных фабрики основывается на таблице оболочек, проиндексированной по ключу. Оболочка имеет функцию create(), которая обращается к  конструктору обернутого объекта.&lt;br /&gt;
&lt;br /&gt;
Использование фабрики состоит из трех этапов: регистрация, настройки переопределение и создание. На первом этапе необходимо зарегистрировать объект с помощью фабрики. На втором шаге, добавляется переопределение зарегистрированного объекта. На третьем этапе, вы создаете объект с помощью фабрики, который будет возвращать либо первоначально зарегистрированный объект или переопределенный, в зависимости от того, переопределение было передано в качестве  запрашиваемого объекта.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

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

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:78.png</id>
		<title>Файл:78.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:78.png"/>
				<updated>2013-03-20T11:14:35Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:77.png</id>
		<title>Файл:77.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:77.png"/>
				<updated>2013-03-20T11:14:19Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:76.png</id>
		<title>Файл:76.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:76.png"/>
				<updated>2013-03-20T11:14:01Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:75.png</id>
		<title>Файл:75.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:75.png"/>
				<updated>2013-03-20T11:13:45Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:74.png</id>
		<title>Файл:74.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:74.png"/>
				<updated>2013-03-20T11:13:30Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM</id>
		<title>OVM/OVM методология/Механика OVM</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM"/>
				<updated>2013-03-19T09:10:20Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= Механика  OVM =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Библиотека OVM предоставляет много возможностей для построения testbenches. В этой главе мы ознакомимся с наиболее важными возможностями, которые вы будете использовать  почти во всех ваши testbenches.&lt;br /&gt;
&lt;br /&gt;
== Компоненты и иерархия ==&lt;br /&gt;
&lt;br /&gt;
Первичная структура для создания элементов testbench  является компонента. Компонента  в OVM – это аналогия  модулю в Verilog. Компонента OVM сконструирована на основе класса, который обеспечивает ему различные характеристики, чем модуль Verilog и имеет различные использования импликаций. Среди таких характеристик является то, что классы создаются во время выполнения, а не во время разработки, как модули. Таким образом, OVM отвечает за создание компонента и сборки их в иерархии.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|[[Файл:69.png]]&lt;br /&gt;
|&amp;lt;graph&amp;gt;&lt;br /&gt;
digraph G{&lt;br /&gt;
node[shape=&amp;quot;circle&amp;quot;,fontsize=&amp;quot;11&amp;quot;,margin=&amp;quot;0.03,0.03&amp;quot;]&lt;br /&gt;
edge[arrowhead=none]&lt;br /&gt;
env -&amp;gt; {c1  c2}&lt;br /&gt;
c1 -&amp;gt; {child11  child12}&lt;br /&gt;
c2 -&amp;gt; {child21  child22}&lt;br /&gt;
child11 [label=&amp;quot;child1&amp;quot;]&lt;br /&gt;
child12 [label=&amp;quot;child2&amp;quot;]&lt;br /&gt;
child21 [label=&amp;quot;child1&amp;quot;]&lt;br /&gt;
child22 [label=&amp;quot;child2&amp;quot;]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/graph&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Рисунок 4-1 иллюстрирует простую иерархию компонент. После  мы покажем,  как построить эту иерархию с помощью средств OVM  для создания компонент и объединения их в иерархии:&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Самый верхний узел, ENV, является корнем. Корень отличается тем, что он не имеет предка. Все остальные узлы имеют только одного предка. Каждый узел имеет имя. Расположение каждого узла в иерархии может быть идентифицировано уникальным full_name (путем), который строится на нанизывании имен всех узлов между корнем и узлов в запросе, разделяя их сепаратором иерархии, точкой (.). Например, путь к компоненту, второго потомка с2 -  top.c2.child2.&lt;br /&gt;
&lt;br /&gt;
Компонент в ОВМ является классом, производным от ovm_component. Простейшая компоненты -  листья, те, которые не имеют потомков.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;57 class child extends ovm_component;&lt;br /&gt;
58&lt;br /&gt;
59 function new(string name, ovm_component parent);&lt;br /&gt;
60 super.new(name, parent);&lt;br /&gt;
61 endfunction&lt;br /&gt;
62&lt;br /&gt;
63 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Конструктор имеет два параметра, имя компоненты и указатель на его предка. Название -  простое имя, а не иерархический путь. Предок  предоставляет место для подключения  новой компоненты в иерархии. Полный  путь потомка создается путем объединения имени потомка с полным именем предка, разделенный точкой (.).OVM предоставляет методы для получения и имени и полного пути компоненты:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;string get_name();&lt;br /&gt;
string get_full_name();&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подчиненные компоненты создаются  в функции build(), которая вызывается на этапе сборки (фазы объясняется далее в этой главе). Создание экземпляра компонента включает вызов new() для выделения памяти для нее и передачи соответствующих параметров в конструктор. В компоненте, показанной ниже, мы создаем две подчиненные компоненты, и child1 child2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;71 class component extends ovm_component;&lt;br /&gt;
72&lt;br /&gt;
73 child child1;&lt;br /&gt;
74 child child2;&lt;br /&gt;
75&lt;br /&gt;
76 function new(string name, ovm_component parent);&lt;br /&gt;
77 super.new(name, parent);&lt;br /&gt;
78 endfunction&lt;br /&gt;
79&lt;br /&gt;
80 function void build();&lt;br /&gt;
81 child1 = new(“child1”, this);&lt;br /&gt;
82 child2 = new(“child2”, this);&lt;br /&gt;
83 endfunction&lt;br /&gt;
84&lt;br /&gt;
85 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как component, ENV также создает экземпляры двух подчиненных компонент, c1 и c2. Вся иерархия  с корнем в модуле,  называемым top в нашем проекте.(??)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;131 module top;&lt;br /&gt;
132&lt;br /&gt;
133 env e;&lt;br /&gt;
134&lt;br /&gt;
135 initial begin&lt;br /&gt;
136 e = new(“env”);&lt;br /&gt;
137 run_test();&lt;br /&gt;
138 end&lt;br /&gt;
139&lt;br /&gt;
140 endmodule&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вызов new() создает верхний уровень окружающей среды. run_test () начинает выполнение testbench.&lt;br /&gt;
&lt;br /&gt;
В SystemVerilog, модули, интерфейсы и программные блоки создаются во время разработки, в то время как классы создаются после разработки, во время выполнения. Таким образом, чтобы создать иерархию классов, мы должны иметь интерфейс, модуль или программу, который содержит начальный блок, который начинает процесс создание компонент иерархии, основанных на классах. Интерфейсы предназначены для использования в качестве средства связи между двумя модулями и не очень хорошо подходят для использования в качестве корня иерархии классов. Программные  блоки или модули могут быть использованы как  корень (??). Для нашей простой иерархии, это не имеет значения. Позже, когда мы подключаем класс-компонент для модуля на основе аппаратных средств, мы увидим, что использование модуля предпочтительнее программным блокам.&lt;br /&gt;
&lt;br /&gt;
=== Перемещение по иерархии ===&lt;br /&gt;
&lt;br /&gt;
Мы можем исследовать структуры данных, используемые для реализации компонента иерархии с некоторыми методами, предусмотренными в ovm_component. Потомки компоненты хранятся в ассоциативном массиве. Этот массив напрямую не доступен, но он может быть доступен через иерархию API. Этот API-интерфейс похож на встроенные методы SystemVerilog, предназначенные для ассоциативных массивов.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;int get_first_child(ref string name);&lt;br /&gt;
int get_next_child(ref string name);&lt;br /&gt;
ovm_component get_child(string name);&lt;br /&gt;
int get_num_children();&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
get_first_child () и get_next_child () работают вместе для перебора множества потомков, содержащихся в компоненте. get_first_child () извлекает имя первого ребенка в списке. Он возвращает имя в качестве ссылки. get_next_child () возвращает имя следующего ребенка в списке. Он  возвращает 1, если имеется следующая имя потомка и 0, если был достигнут конец списка. get_child () преобразует имя в ссылку компоненты.&lt;br /&gt;
&lt;br /&gt;
Используя эти функции, мы можем пройти по компонентам иерархии.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;73 function void depth_first(ovm_component node,&lt;br /&gt;
74 int unsigned level = 0);&lt;br /&gt;
75&lt;br /&gt;
76 string name;&lt;br /&gt;
77&lt;br /&gt;
78 if(node == null)&lt;br /&gt;
79 return;&lt;br /&gt;
80&lt;br /&gt;
81 visit(node, level);&lt;br /&gt;
82&lt;br /&gt;
83 if(node.get_first_child(name))&lt;br /&gt;
84 do begin&lt;br /&gt;
85 depth_first(node.get_child(name), level+1);&lt;br /&gt;
86 end while(node.get_next_child(name));&lt;br /&gt;
87&lt;br /&gt;
88 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/utils/traverse.svh&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эта функция будет осуществляющая обход в глубину иерархии, называя visit(). Мы используем get_first_child () и get_next_child () для перебора списка каждого потомка в каждом узле. Для каждого итерации мы вызываем depth_first () рекурсивно. Для нашего маленького проекта, результат будет  следующий:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;pre&amp;gt;+ env&lt;br /&gt;
| + env.c1&lt;br /&gt;
| | env.c1.child1&lt;br /&gt;
| | env.c1.child2&lt;br /&gt;
| + env.c2&lt;br /&gt;
| | env.c2.child1&lt;br /&gt;
| | env.c2.child2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция visit() использует  глубину узла и  определяет является ли он конечным  для печати строки каждого узла.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Singleton Top ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Компоненты, которые не имеют предков (то есть,  параметр предок в конструкторе является нулевым) называют orphans.(??) В OVM вы можете создать столько компоненты без предков, сколько вам необходимо. Тем не менее, не существует такого понятия, как истинные orphans. Любой компонент, чей предок является  null,  относится к встроенному предку, называемому ovm_top. ovm_top состоит из одного экземпляра ovm_root. Это предок всех компонент, которые имеют предков. В самом деле, env  в нашем предыдущем примере является потомком ovm_top. Так как он не имеет предка, то автоматически получают ovm_top в качестве родителя.&lt;br /&gt;
&lt;br /&gt;
singleton -  широко известный  объектно-ориентированных шаблон проектирования, который имеет private (local) или protected конструкторы и статическую функцию get, которая возвращает тот же самый указатель не смотря на то, сколько раз она вызывается. Это означает, что возможно существование одного экземпляра, и что экземпляр создается  автоматически при вызове функции get. ovm_top содержит дескриптор экземпляра singleton  ovm_root(???). Он статически инициализируется при вызове ovm_root :: Get (). Вы можете вызвать ovm_root :: Get () в любое время, но в этом нет необходимости, так как ovm_top предусматривает это.&lt;br /&gt;
&lt;br /&gt;
Есть целый ряд полезных следствий существования singleton top-level компоненты. Одним из них является, что вы можете достичь любую компоненту из ovm_top. Если вы запустили алгоритм обхода иерархии, начиная с ovm_top, вы доберетесь до каждой компоненты в системе. Другим следствием является то, что любая компонента, включая порты, экспорт и каналы, которые создаются внутри модуля, достижима из ovm_top. Если вы хотите изменить отчет во всех обработчиках компонент, вы можете сделать это, вызвав  одну из иерархических функции отчетности в ovm_top. ovm_top содержит все механизмы синхронизации, которые объясняются далее в этой главе.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Connectivity ==&lt;br /&gt;
&lt;br /&gt;
Компоненты соединены друг с другом через TLM порта и exports. Порты и exports обеспечивают средства для компонент, или, точнее, процессы в компонентах для синхронизации и связи друг с другом. Порты и exports -  объекты, которые образуют  пункт связывания для обеспечения межкомпонентной связи. Как уже говорилось в предыдущей главе, exports обеспечивают функции и задачи, которые могут быть вызваны портами.&lt;br /&gt;
&lt;br /&gt;
[[Файл:70.png]]&lt;br /&gt;
 &lt;br /&gt;
Метод connect  портов и exports используется для связывания их вместе.&lt;br /&gt;
&lt;br /&gt;
initiator_port.connect(target.export)&lt;br /&gt;
&lt;br /&gt;
Этот метод создает ассоциацию, или связывание, между портом и export так что порт может теперь называем задач и функций export. Для успешного соединения, типы  портов и export должны совпадать. То есть, типы интерфейсов должны быть одинаковыми, и тип объекта, передающегося в интерфейс должны быть одинаковыми.&lt;br /&gt;
&lt;br /&gt;
=== Подключение через Иерархию ===&lt;br /&gt;
&lt;br /&gt;
Как и контактами в RTL дизайне, нам нужно подключиться к портам  TLM через иерархические границы. Рисунок 4-3 использует простой дизайн, чтобы показать, как сделать эти соединения. Этот проект содержит исходную компоненту с двумя портами, которые в конечном итоге подключаются к двум экспортам. Для осуществления подключения между этими компонентами, мы должны расширить порты и экспорта на  уровень выше в иерархии.&lt;br /&gt;
 &lt;br /&gt;
[[Файл:71.png]]&lt;br /&gt;
&lt;br /&gt;
Компонент source содержит два порта, и first_put_port, second_put_port.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;65 class source extends ovm_component;&lt;br /&gt;
66&lt;br /&gt;
67 ovm_put_port #(trans_t) first_put_port;&lt;br /&gt;
68 ovm_put_port #(trans_t) second_put_port;&lt;br /&gt;
69&lt;br /&gt;
70 function new(string name, ovm_component parent);&lt;br /&gt;
71 super.new(name, parent);&lt;br /&gt;
72 endfunction&lt;br /&gt;
73&lt;br /&gt;
74 function void build();&lt;br /&gt;
75 first_put_port = new(“first_put_port”, this);&lt;br /&gt;
76 second_put_port = new(“second_put_port”, this);&lt;br /&gt;
77 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Кроме того, компонента sink создает экспорт и создает его в функции build. Экспорт связан с внутренним каналом, FIFO, от которого компонента может получать объекты во время выполнения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;126 class sink extends ovm_component;&lt;br /&gt;
127&lt;br /&gt;
128 ovm_put_export #(trans_t) put_export;&lt;br /&gt;
129 local tlm_fifo #(trans_t) fifo;&lt;br /&gt;
130&lt;br /&gt;
131 function new(string name, ovm_component parent);&lt;br /&gt;
132 super.new(name, parent);&lt;br /&gt;
133 endfunction&lt;br /&gt;
134&lt;br /&gt;
135 function void build();&lt;br /&gt;
136 put_export = new(“put_export”, this);&lt;br /&gt;
137 fifo = new(“fifo”, this);&lt;br /&gt;
138 endfunction&lt;br /&gt;
139&lt;br /&gt;
140 function void connect();&lt;br /&gt;
141 put_export.connect(fifo.put_export);&lt;br /&gt;
142 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
source_wrapper должен создать соединение между внутренним источником компоненты и  внешней ее границей. Он делает это соединение, создавая свои порты, которые имеют тот же тип, что и тип  портов lower-level, в нашем случае, тех, которые принадлежат к source.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;98 class source_wrapper extends ovm_component;&lt;br /&gt;
99&lt;br /&gt;
100 source s;&lt;br /&gt;
101 ovm_put_port #(trans_t) put_port1;&lt;br /&gt;
102 ovm_put_port #(trans_t) put_port2;&lt;br /&gt;
103&lt;br /&gt;
104 function new(string name, ovm_component parent);&lt;br /&gt;
105 super.new(name, parent);&lt;br /&gt;
106 endfunction&lt;br /&gt;
107&lt;br /&gt;
108 function void build();&lt;br /&gt;
109 s = new(“source”, this);&lt;br /&gt;
110 put_port1 = new(“put_port1”, this);&lt;br /&gt;
111 put_port2 = new(“put_port2”, this);&lt;br /&gt;
112 endfunction&lt;br /&gt;
113&lt;br /&gt;
114 function void connect();&lt;br /&gt;
115 s.first_put_port.connect(put_port1);&lt;br /&gt;
116 s.second_put_port.connect(put_port2);&lt;br /&gt;
117 endfunction&lt;br /&gt;
118&lt;br /&gt;
119 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После того как порты в source_wrapper были созданы, они подключаются к портам lower-level  компоненты source с помощью метода Connect. Видимость портов на более высоком уровне иерархии достигается  таким же образом, как мы видим  в sinker.&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
160 class sinker extends ovm_component;&lt;br /&gt;
161&lt;br /&gt;
162 ovm_put_export #(trans_t) first_put_export;&lt;br /&gt;
163 ovm_put_export #(trans_t) second_put_export;&lt;br /&gt;
164&lt;br /&gt;
165 sink sink1;&lt;br /&gt;
166 sink sink2;&lt;br /&gt;
167&lt;br /&gt;
168 function new(string name, ovm_component parent);&lt;br /&gt;
169 super.new(name, parent);&lt;br /&gt;
170 endfunction&lt;br /&gt;
171&lt;br /&gt;
172 function void build();&lt;br /&gt;
173 sink1 = new(“sink1”, this);&lt;br /&gt;
174 sink2 = new(“sink2”, this);&lt;br /&gt;
175 first_put_export = new(“first_put_export”, this);&lt;br /&gt;
176 second_put_export = new(“second_put_export”, this);&lt;br /&gt;
177 endfunction&lt;br /&gt;
178&lt;br /&gt;
179 function void connect();&lt;br /&gt;
180 first_put_export.connect(sink1.put_export);&lt;br /&gt;
181 second_put_export.connect(sink2.put_export);&lt;br /&gt;
182 endfunction&lt;br /&gt;
183&lt;br /&gt;
184 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Два нижних уровня компоненты sink и exports  создаются обычным способом. Затем они соединяются с помощью метода подключения export. Сейчас мы создаем port-export соединение между source_wrapper и sinker, также с помощью функции Connect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;192 class env extends ovm_component;&lt;br /&gt;
193&lt;br /&gt;
194 sinker s;&lt;br /&gt;
195 source_wrapper sw;&lt;br /&gt;
196&lt;br /&gt;
197 function new(string name, ovm_component parent = null);&lt;br /&gt;
198 super.new(name, parent);&lt;br /&gt;
199 endfunction&lt;br /&gt;
200&lt;br /&gt;
201 function void build();&lt;br /&gt;
202 s = new(“sinker”, this);&lt;br /&gt;
203 sw = new(“source_wrapper”, this);&lt;br /&gt;
204 endfunction&lt;br /&gt;
205&lt;br /&gt;
206 function void connect();&lt;br /&gt;
207 sw.put_port1.connect(s.first_put_export);&lt;br /&gt;
208 sw.put_port2.connect(s.second_put_export);&lt;br /&gt;
209 endfunction&lt;br /&gt;
210&lt;br /&gt;
211 task run; &lt;br /&gt;
212 global_stop_request();&lt;br /&gt;
213 endtask&lt;br /&gt;
214&lt;br /&gt;
215 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для новых пользователей часто может вызвать трудности определить,  порт или экспорт необходимо подключить и какой объект является аргументом. Вы можете легко понять это, следуя потоку управления  через систему. Общее правило  заключается в том, что запрашиваемый порт вызывает connect() с помощью запрашиваемого порта или экспорта в качестве аргумента. Рисунок 4-4 показывает поток управления через иерархическую систему.&lt;br /&gt;
&lt;br /&gt;
[[Файл:72.png]]&lt;br /&gt;
 &lt;br /&gt;
Порт – объект  вызова и экспорт - объект вызванной функции или задачи. Вы можете воспринимать порты, как вызов экспорта. Так, в env, мы вызываем connect на put_ports с put_exports в качестве аргументов. Для port-to- port  и export-to-export иерархии соединений, вызывающий будет чуть менее очевиден. Если  вызов осуществляется со стороны порта, вы можете считать, что вызов lowest-level порта в иерархии является вызовом методов интерфейса  upper-level порта. Аналогично, если экспорт объект вызова, вы можете считать upper-level экспорт  вызовом lower-level export. В таблице ниже приведены возможные типы подключения:&lt;br /&gt;
 &lt;br /&gt;
[[Файл:73.png]]&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM</id>
		<title>OVM/OVM методология/Механика OVM</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM"/>
				<updated>2013-03-19T09:08:42Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= Механика  OVM =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Библиотека OVM предоставляет много возможностей для построения testbenches. В этой главе мы ознакомимся с наиболее важными возможностями, которые вы будете использовать  почти во всех ваши testbenches.&lt;br /&gt;
&lt;br /&gt;
== Компоненты и иерархия ==&lt;br /&gt;
&lt;br /&gt;
Первичная структура для создания элементов testbench  является компонента. Компонента  в OVM – это аналогия  модулю в Verilog. Компонента OVM сконструирована на основе класса, который обеспечивает ему различные характеристики, чем модуль Verilog и имеет различные использования импликаций. Среди таких характеристик является то, что классы создаются во время выполнения, а не во время разработки, как модули. Таким образом, OVM отвечает за создание компонента и сборки их в иерархии.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|[[Файл:69.png]]&lt;br /&gt;
|&amp;lt;graph&amp;gt;&lt;br /&gt;
digraph G{&lt;br /&gt;
node[shape=&amp;quot;circle&amp;quot;,fontsize=&amp;quot;11&amp;quot;,margin=&amp;quot;0.03,0.03&amp;quot;]&lt;br /&gt;
edge[arrowhead=none]&lt;br /&gt;
env -&amp;gt; {c1  c2}&lt;br /&gt;
c1 -&amp;gt; {child11  child12}&lt;br /&gt;
c2 -&amp;gt; {child21  child22}&lt;br /&gt;
child11 [label=&amp;quot;child1&amp;quot;]&lt;br /&gt;
child12 [label=&amp;quot;child2&amp;quot;]&lt;br /&gt;
child21 [label=&amp;quot;child1&amp;quot;]&lt;br /&gt;
child22 [label=&amp;quot;child2&amp;quot;]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/graph&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Рисунок 4-1 иллюстрирует простую иерархию компонент. После  мы покажем,  как построить эту иерархию с помощью средств OVM  для создания компонент и объединения их в иерархии:&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Самый верхний узел, ENV, является корнем. Корень отличается тем, что он не имеет предка. Все остальные узлы имеют только одного предка. Каждый узел имеет имя. Расположение каждого узла в иерархии может быть идентифицировано уникальным full_name (путем), который строится на нанизывании имен всех узлов между корнем и узлов в запросе, разделяя их сепаратором иерархии, точкой (.). Например, путь к компоненту, второго потомка с2 -  top.c2.child2.&lt;br /&gt;
&lt;br /&gt;
Компонент в ОВМ является классом, производным от ovm_component. Простейшая компоненты -  листья, те, которые не имеют потомков.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;57 class child extends ovm_component;&lt;br /&gt;
58&lt;br /&gt;
59 function new(string name, ovm_component parent);&lt;br /&gt;
60 super.new(name, parent);&lt;br /&gt;
61 endfunction&lt;br /&gt;
62&lt;br /&gt;
63 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Конструктор имеет два параметра, имя компоненты и указатель на его предка. Название -  простое имя, а не иерархический путь. Предок  предоставляет место для подключения  новой компоненты в иерархии. Полный  путь потомка создается путем объединения имени потомка с полным именем предка, разделенный точкой (.).OVM предоставляет методы для получения и имени и полного пути компоненты:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;string get_name();&lt;br /&gt;
string get_full_name();&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подчиненные компоненты создаются  в функции build(), которая вызывается на этапе сборки (фазы объясняется далее в этой главе). Создание экземпляра компонента включает вызов new() для выделения памяти для нее и передачи соответствующих параметров в конструктор. В компоненте, показанной ниже, мы создаем две подчиненные компоненты, и child1 child2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;71 class component extends ovm_component;&lt;br /&gt;
72&lt;br /&gt;
73 child child1;&lt;br /&gt;
74 child child2;&lt;br /&gt;
75&lt;br /&gt;
76 function new(string name, ovm_component parent);&lt;br /&gt;
77 super.new(name, parent);&lt;br /&gt;
78 endfunction&lt;br /&gt;
79&lt;br /&gt;
80 function void build();&lt;br /&gt;
81 child1 = new(“child1”, this);&lt;br /&gt;
82 child2 = new(“child2”, this);&lt;br /&gt;
83 endfunction&lt;br /&gt;
84&lt;br /&gt;
85 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как component, ENV также создает экземпляры двух подчиненных компонент, c1 и c2. Вся иерархия  с корнем в модуле,  называемым top в нашем проекте.(??)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;131 module top;&lt;br /&gt;
132&lt;br /&gt;
133 env e;&lt;br /&gt;
134&lt;br /&gt;
135 initial begin&lt;br /&gt;
136 e = new(“env”);&lt;br /&gt;
137 run_test();&lt;br /&gt;
138 end&lt;br /&gt;
139&lt;br /&gt;
140 endmodule&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вызов new() создает верхний уровень окружающей среды. run_test () начинает выполнение testbench.&lt;br /&gt;
&lt;br /&gt;
В SystemVerilog, модули, интерфейсы и программные блоки создаются во время разработки, в то время как классы создаются после разработки, во время выполнения. Таким образом, чтобы создать иерархию классов, мы должны иметь интерфейс, модуль или программу, который содержит начальный блок, который начинает процесс создание компонент иерархии, основанных на классах. Интерфейсы предназначены для использования в качестве средства связи между двумя модулями и не очень хорошо подходят для использования в качестве корня иерархии классов. Программные  блоки или модули могут быть использованы как  корень (??). Для нашей простой иерархии, это не имеет значения. Позже, когда мы подключаем класс-компонент для модуля на основе аппаратных средств, мы увидим, что использование модуля предпочтительнее программным блокам.&lt;br /&gt;
&lt;br /&gt;
=== Перемещение по иерархии ===&lt;br /&gt;
&lt;br /&gt;
Мы можем исследовать структуры данных, используемые для реализации компонента иерархии с некоторыми методами, предусмотренными в ovm_component. Потомки компоненты хранятся в ассоциативном массиве. Этот массив напрямую не доступен, но он может быть доступен через иерархию API. Этот API-интерфейс похож на встроенные методы SystemVerilog, предназначенные для ассоциативных массивов.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;int get_first_child(ref string name);&lt;br /&gt;
int get_next_child(ref string name);&lt;br /&gt;
ovm_component get_child(string name);&lt;br /&gt;
int get_num_children();&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
get_first_child () и get_next_child () работают вместе для перебора множества потомков, содержащихся в компоненте. get_first_child () извлекает имя первого ребенка в списке. Он возвращает имя в качестве ссылки. get_next_child () возвращает имя следующего ребенка в списке. Он  возвращает 1, если имеется следующая имя потомка и 0, если был достигнут конец списка. get_child () преобразует имя в ссылку компоненты.&lt;br /&gt;
&lt;br /&gt;
Используя эти функции, мы можем пройти по компонентам иерархии.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;73 function void depth_first(ovm_component node,&lt;br /&gt;
74 int unsigned level = 0);&lt;br /&gt;
75&lt;br /&gt;
76 string name;&lt;br /&gt;
77&lt;br /&gt;
78 if(node == null)&lt;br /&gt;
79 return;&lt;br /&gt;
80&lt;br /&gt;
81 visit(node, level);&lt;br /&gt;
82&lt;br /&gt;
83 if(node.get_first_child(name))&lt;br /&gt;
84 do begin&lt;br /&gt;
85 depth_first(node.get_child(name), level+1);&lt;br /&gt;
86 end while(node.get_next_child(name));&lt;br /&gt;
87&lt;br /&gt;
88 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/utils/traverse.svh&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эта функция будет осуществляющая обход в глубину иерархии, называя visit(). Мы используем get_first_child () и get_next_child () для перебора списка каждого потомка в каждом узле. Для каждого итерации мы вызываем depth_first () рекурсивно. Для нашего маленького проекта, результат будет  следующий:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;pre&amp;gt;+ env&lt;br /&gt;
| + env.c1&lt;br /&gt;
| | env.c1.child1&lt;br /&gt;
| | env.c1.child2&lt;br /&gt;
| + env.c2&lt;br /&gt;
| | env.c2.child1&lt;br /&gt;
| | env.c2.child2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция visit() использует  глубину узла и  определяет является ли он конечным  для печати строки каждого узла.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Singleton Top ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Компоненты, которые не имеют предков (то есть,  параметр предок в конструкторе является нулевым) называют orphans.(??) В OVM вы можете создать столько компоненты без предков, сколько вам необходимо. Тем не менее, не существует такого понятия, как истинные orphans. Любой компонент, чей предок является  null,  относится к встроенному предку, называемому ovm_top. ovm_top состоит из одного экземпляра ovm_root. Это предок всех компонент, которые имеют предков. В самом деле, env  в нашем предыдущем примере является потомком ovm_top. Так как он не имеет предка, то автоматически получают ovm_top в качестве родителя.&lt;br /&gt;
&lt;br /&gt;
singleton -  широко известный  объектно-ориентированных шаблон проектирования, который имеет private (local) или protected конструкторы и статическую функцию get, которая возвращает тот же самый указатель не смотря на то, сколько раз она вызывается. Это означает, что возможно существование одного экземпляра, и что экземпляр создается  автоматически при вызове функции get. ovm_top содержит дескриптор экземпляра singleton  ovm_root(???). Он статически инициализируется при вызове ovm_root :: Get (). Вы можете вызвать ovm_root :: Get () в любое время, но в этом нет необходимости, так как ovm_top предусматривает это.&lt;br /&gt;
&lt;br /&gt;
Есть целый ряд полезных следствий существования singleton top-level компоненты. Одним из них является, что вы можете достичь любую компоненту из ovm_top. Если вы запустили алгоритм обхода иерархии, начиная с ovm_top, вы доберетесь до каждой компоненты в системе. Другим следствием является то, что любая компонента, включая порты, экспорт и каналы, которые создаются внутри модуля, достижима из ovm_top. Если вы хотите изменить отчет во всех обработчиках компонент, вы можете сделать это, вызвав  одну из иерархических функции отчетности в ovm_top. ovm_top содержит все механизмы синхронизации, которые объясняются далее в этой главе.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Connectivity ==&lt;br /&gt;
&lt;br /&gt;
Компоненты соединены друг с другом через TLM порта и exports. Порты и exports обеспечивают средства для компонент, или, точнее, процессы в компонентах для синхронизации и связи друг с другом. Порты и exports -  объекты, которые образуют  пункт связывания для обеспечения межкомпонентной связи. Как уже говорилось в предыдущей главе, exports обеспечивают функции и задачи, которые могут быть вызваны портами.&lt;br /&gt;
&lt;br /&gt;
[[Файл:70.png]]&lt;br /&gt;
 &lt;br /&gt;
Метод connect  портов и exports используется для связывания их вместе.&lt;br /&gt;
&lt;br /&gt;
initiator_port.connect(target.export)&lt;br /&gt;
&lt;br /&gt;
Этот метод создает ассоциацию, или связывание, между портом и export так что порт может теперь называем задач и функций export. Для успешного соединения, типы  портов и export должны совпадать. То есть, типы интерфейсов должны быть одинаковыми, и тип объекта, передающегося в интерфейс должны быть одинаковыми.&lt;br /&gt;
&lt;br /&gt;
4.2.1 Подключение через Иерархию&lt;br /&gt;
&lt;br /&gt;
Как и контактами в RTL дизайне, нам нужно подключиться к портам  TLM через иерархические границы. Рисунок 4-3 использует простой дизайн, чтобы показать, как сделать эти соединения. Этот проект содержит исходную компоненту с двумя портами, которые в конечном итоге подключаются к двум экспортам. Для осуществления подключения между этими компонентами, мы должны расширить порты и экспорта на  уровень выше в иерархии.&lt;br /&gt;
 &lt;br /&gt;
[[Файл:71.png]]&lt;br /&gt;
&lt;br /&gt;
Компонент source содержит два порта, и first_put_port, second_put_port.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;65 class source extends ovm_component;&lt;br /&gt;
66&lt;br /&gt;
67 ovm_put_port #(trans_t) first_put_port;&lt;br /&gt;
68 ovm_put_port #(trans_t) second_put_port;&lt;br /&gt;
69&lt;br /&gt;
70 function new(string name, ovm_component parent);&lt;br /&gt;
71 super.new(name, parent);&lt;br /&gt;
72 endfunction&lt;br /&gt;
73&lt;br /&gt;
74 function void build();&lt;br /&gt;
75 first_put_port = new(“first_put_port”, this);&lt;br /&gt;
76 second_put_port = new(“second_put_port”, this);&lt;br /&gt;
77 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Кроме того, компонента sink создает экспорт и создает его в функции build. Экспорт связан с внутренним каналом, FIFO, от которого компонента может получать объекты во время выполнения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;126 class sink extends ovm_component;&lt;br /&gt;
127&lt;br /&gt;
128 ovm_put_export #(trans_t) put_export;&lt;br /&gt;
129 local tlm_fifo #(trans_t) fifo;&lt;br /&gt;
130&lt;br /&gt;
131 function new(string name, ovm_component parent);&lt;br /&gt;
132 super.new(name, parent);&lt;br /&gt;
133 endfunction&lt;br /&gt;
134&lt;br /&gt;
135 function void build();&lt;br /&gt;
136 put_export = new(“put_export”, this);&lt;br /&gt;
137 fifo = new(“fifo”, this);&lt;br /&gt;
138 endfunction&lt;br /&gt;
139&lt;br /&gt;
140 function void connect();&lt;br /&gt;
141 put_export.connect(fifo.put_export);&lt;br /&gt;
142 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
source_wrapper должен создать соединение между внутренним источником компоненты и  внешней ее границей. Он делает это соединение, создавая свои порты, которые имеют тот же тип, что и тип  портов lower-level, в нашем случае, тех, которые принадлежат к source.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;98 class source_wrapper extends ovm_component;&lt;br /&gt;
99&lt;br /&gt;
100 source s;&lt;br /&gt;
101 ovm_put_port #(trans_t) put_port1;&lt;br /&gt;
102 ovm_put_port #(trans_t) put_port2;&lt;br /&gt;
103&lt;br /&gt;
104 function new(string name, ovm_component parent);&lt;br /&gt;
105 super.new(name, parent);&lt;br /&gt;
106 endfunction&lt;br /&gt;
107&lt;br /&gt;
108 function void build();&lt;br /&gt;
109 s = new(“source”, this);&lt;br /&gt;
110 put_port1 = new(“put_port1”, this);&lt;br /&gt;
111 put_port2 = new(“put_port2”, this);&lt;br /&gt;
112 endfunction&lt;br /&gt;
113&lt;br /&gt;
114 function void connect();&lt;br /&gt;
115 s.first_put_port.connect(put_port1);&lt;br /&gt;
116 s.second_put_port.connect(put_port2);&lt;br /&gt;
117 endfunction&lt;br /&gt;
118&lt;br /&gt;
119 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После того как порты в source_wrapper были созданы, они подключаются к портам lower-level  компоненты source с помощью метода Connect. Видимость портов на более высоком уровне иерархии достигается  таким же образом, как мы видим  в sinker.&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
160 class sinker extends ovm_component;&lt;br /&gt;
161&lt;br /&gt;
162 ovm_put_export #(trans_t) first_put_export;&lt;br /&gt;
163 ovm_put_export #(trans_t) second_put_export;&lt;br /&gt;
164&lt;br /&gt;
165 sink sink1;&lt;br /&gt;
166 sink sink2;&lt;br /&gt;
167&lt;br /&gt;
168 function new(string name, ovm_component parent);&lt;br /&gt;
169 super.new(name, parent);&lt;br /&gt;
170 endfunction&lt;br /&gt;
171&lt;br /&gt;
172 function void build();&lt;br /&gt;
173 sink1 = new(“sink1”, this);&lt;br /&gt;
174 sink2 = new(“sink2”, this);&lt;br /&gt;
175 first_put_export = new(“first_put_export”, this);&lt;br /&gt;
176 second_put_export = new(“second_put_export”, this);&lt;br /&gt;
177 endfunction&lt;br /&gt;
178&lt;br /&gt;
179 function void connect();&lt;br /&gt;
180 first_put_export.connect(sink1.put_export);&lt;br /&gt;
181 second_put_export.connect(sink2.put_export);&lt;br /&gt;
182 endfunction&lt;br /&gt;
183&lt;br /&gt;
184 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Два нижних уровня компоненты sink и exports  создаются обычным способом. Затем они соединяются с помощью метода подключения export. Сейчас мы создаем port-export соединение между source_wrapper и sinker, также с помощью функции Connect.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;192 class env extends ovm_component;&lt;br /&gt;
193&lt;br /&gt;
194 sinker s;&lt;br /&gt;
195 source_wrapper sw;&lt;br /&gt;
196&lt;br /&gt;
197 function new(string name, ovm_component parent = null);&lt;br /&gt;
198 super.new(name, parent);&lt;br /&gt;
199 endfunction&lt;br /&gt;
200&lt;br /&gt;
201 function void build();&lt;br /&gt;
202 s = new(“sinker”, this);&lt;br /&gt;
203 sw = new(“source_wrapper”, this);&lt;br /&gt;
204 endfunction&lt;br /&gt;
205&lt;br /&gt;
206 function void connect();&lt;br /&gt;
207 sw.put_port1.connect(s.first_put_export);&lt;br /&gt;
208 sw.put_port2.connect(s.second_put_export);&lt;br /&gt;
209 endfunction&lt;br /&gt;
210&lt;br /&gt;
211 task run; &lt;br /&gt;
212 global_stop_request();&lt;br /&gt;
213 endtask&lt;br /&gt;
214&lt;br /&gt;
215 endclass&lt;br /&gt;
file: 04_OVM_mechanics/02_connectivity/top.sv&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для новых пользователей часто может вызвать трудности определить,  порт или экспорт необходимо подключить и какой объект является аргументом. Вы можете легко понять это, следуя потоку управления  через систему. Общее правило  заключается в том, что запрашиваемый порт вызывает connect() с помощью запрашиваемого порта или экспорта в качестве аргумента. Рисунок 4-4 показывает поток управления через иерархическую систему.&lt;br /&gt;
&lt;br /&gt;
[[Файл:72.png]]&lt;br /&gt;
 &lt;br /&gt;
Порт – объект  вызова и экспорт - объект вызванной функции или задачи. Вы можете воспринимать порты, как вызов экспорта. Так, в env, мы вызываем connect на put_ports с put_exports в качестве аргументов. Для port-to- port  и export-to-export иерархии соединений, вызывающий будет чуть менее очевиден. Если  вызов осуществляется со стороны порта, вы можете считать, что вызов lowest-level порта в иерархии является вызовом методов интерфейса  upper-level порта. Аналогично, если экспорт объект вызова, вы можете считать upper-level экспорт  вызовом lower-level export. В таблице ниже приведены возможные типы подключения:&lt;br /&gt;
 &lt;br /&gt;
[[Файл:73.png]]&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:73.png</id>
		<title>Файл:73.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:73.png"/>
				<updated>2013-03-19T08:59:40Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:72.png</id>
		<title>Файл:72.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:72.png"/>
				<updated>2013-03-19T08:59:25Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:71.png</id>
		<title>Файл:71.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:71.png"/>
				<updated>2013-03-19T08:59:12Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:70.png</id>
		<title>Файл:70.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:70.png"/>
				<updated>2013-03-19T08:58:56Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM</id>
		<title>OVM/OVM методология/Механика OVM</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM"/>
				<updated>2013-03-18T13:38:19Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= Механика  OVM =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Библиотека OVM предоставляет много возможностей для построения testbenches. В этой главе мы ознакомимся с наиболее важными возможностями, которые вы будете использовать  почти во всех ваши testbenches.&lt;br /&gt;
&lt;br /&gt;
== Компоненты и иерархия ==&lt;br /&gt;
&lt;br /&gt;
Первичная структура для создания элементов testbench  является компонента. Компонента  в OVM – это аналогия  модулю в Verilog. Компонента OVM сконструирована на основе класса, который обеспечивает ему различные характеристики, чем модуль Verilog и имеет различные использования импликаций. Среди таких характеристик является то, что классы создаются во время выполнения, а не во время разработки, как модули. Таким образом, OVM отвечает за создание компонента и сборки их в иерархии.&lt;br /&gt;
&lt;br /&gt;
[[Файл:69.png]]&lt;br /&gt;
&lt;br /&gt;
Рисунок 4-1 иллюстрирует простую иерархию компонент. После  мы покажем,  как построить эту иерархию с помощью средств OVM  для создания компонент и объединения их в иерархии:&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Самый верхний узел, ENV, является корнем. Корень отличается тем, что он не имеет предка. Все остальные узлы имеют только одного предка. Каждый узел имеет имя. Расположение каждого узла в иерархии может быть идентифицировано уникальным full_name (путем), который строится на нанизывании имен всех узлов между корнем и узлов в запросе, разделяя их сепаратором иерархии, точкой (.). Например, путь к компоненту, второго потомка с2 -  top.c2.child2.&lt;br /&gt;
&lt;br /&gt;
Компонент в ОВМ является классом, производным от ovm_component. Простейшая компоненты -  листья, те, которые не имеют потомков.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;57 class child extends ovm_component;&lt;br /&gt;
58&lt;br /&gt;
59 function new(string name, ovm_component parent);&lt;br /&gt;
60 super.new(name, parent);&lt;br /&gt;
61 endfunction&lt;br /&gt;
62&lt;br /&gt;
63 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Конструктор имеет два параметра, имя компоненты и указатель на его предка. Название -  простое имя, а не иерархический путь. Предок  предоставляет место для подключения  новой компоненты в иерархии. Полный  путь потомка создается путем объединения имени потомка с полным именем предка, разделенный точкой (.).OVM предоставляет методы для получения и имени и полного пути компоненты:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;string get_name();&lt;br /&gt;
string get_full_name();&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подчиненные компоненты создаются  в функции build(), которая вызывается на этапе сборки (фазы объясняется далее в этой главе). Создание экземпляра компонента включает вызов new() для выделения памяти для нее и передачи соответствующих параметров в конструктор. В компоненте, показанной ниже, мы создаем две подчиненные компоненты, и child1 child2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;71 class component extends ovm_component;&lt;br /&gt;
72&lt;br /&gt;
73 child child1;&lt;br /&gt;
74 child child2;&lt;br /&gt;
75&lt;br /&gt;
76 function new(string name, ovm_component parent);&lt;br /&gt;
77 super.new(name, parent);&lt;br /&gt;
78 endfunction&lt;br /&gt;
79&lt;br /&gt;
80 function void build();&lt;br /&gt;
81 child1 = new(“child1”, this);&lt;br /&gt;
82 child2 = new(“child2”, this);&lt;br /&gt;
83 endfunction&lt;br /&gt;
84&lt;br /&gt;
85 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как component, ENV также создает экземпляры двух подчиненных компонент, c1 и c2. Вся иерархия  с корнем в модуле,  называемым top в нашем проекте.(??)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;131 module top;&lt;br /&gt;
132&lt;br /&gt;
133 env e;&lt;br /&gt;
134&lt;br /&gt;
135 initial begin&lt;br /&gt;
136 e = new(“env”);&lt;br /&gt;
137 run_test();&lt;br /&gt;
138 end&lt;br /&gt;
139&lt;br /&gt;
140 endmodule&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вызов new() создает верхний уровень окружающей среды. run_test () начинает выполнение testbench.&lt;br /&gt;
&lt;br /&gt;
В SystemVerilog, модули, интерфейсы и программные блоки создаются во время разработки, в то время как классы создаются после разработки, во время выполнения. Таким образом, чтобы создать иерархию классов, мы должны иметь интерфейс, модуль или программу, который содержит начальный блок, который начинает процесс создание компонент иерархии, основанных на классах. Интерфейсы предназначены для использования в качестве средства связи между двумя модулями и не очень хорошо подходят для использования в качестве корня иерархии классов. Программные  блоки или модули могут быть использованы как  корень (??). Для нашей простой иерархии, это не имеет значения. Позже, когда мы подключаем класс-компонент для модуля на основе аппаратных средств, мы увидим, что использование модуля предпочтительнее программным блокам.&lt;br /&gt;
&lt;br /&gt;
=== Перемещение по иерархии ===&lt;br /&gt;
&lt;br /&gt;
Мы можем исследовать структуры данных, используемые для реализации компонента иерархии с некоторыми методами, предусмотренными в ovm_component. Потомки компоненты хранятся в ассоциативном массиве. Этот массив напрямую не доступен, но он может быть доступен через иерархию API. Этот API-интерфейс похож на встроенные методы SystemVerilog, предназначенные для ассоциативных массивов.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;int get_first_child(ref string name);&lt;br /&gt;
int get_next_child(ref string name);&lt;br /&gt;
ovm_component get_child(string name);&lt;br /&gt;
int get_num_children();&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
get_first_child () и get_next_child () работают вместе для перебора множества потомков, содержащихся в компоненте. get_first_child () извлекает имя первого ребенка в списке. Он возвращает имя в качестве ссылки. get_next_child () возвращает имя следующего ребенка в списке. Он  возвращает 1, если имеется следующая имя потомка и 0, если был достигнут конец списка. get_child () преобразует имя в ссылку компоненты.&lt;br /&gt;
&lt;br /&gt;
Используя эти функции, мы можем пройти по компонентам иерархии.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;73 function void depth_first(ovm_component node,&lt;br /&gt;
74 int unsigned level = 0);&lt;br /&gt;
75&lt;br /&gt;
76 string name;&lt;br /&gt;
77&lt;br /&gt;
78 if(node == null)&lt;br /&gt;
79 return;&lt;br /&gt;
80&lt;br /&gt;
81 visit(node, level);&lt;br /&gt;
82&lt;br /&gt;
83 if(node.get_first_child(name))&lt;br /&gt;
84 do begin&lt;br /&gt;
85 depth_first(node.get_child(name), level+1);&lt;br /&gt;
86 end while(node.get_next_child(name));&lt;br /&gt;
87&lt;br /&gt;
88 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/utils/traverse.svh&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эта функция будет осуществляющая обход в глубину иерархии, называя visit(). Мы используем get_first_child () и get_next_child () для перебора списка каждого потомка в каждом узле. Для каждого итерации мы вызываем depth_first () рекурсивно. Для нашего маленького проекта, результат будет  следующий:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;pre&amp;gt;+ env&lt;br /&gt;
| + env.c1&lt;br /&gt;
| | env.c1.child1&lt;br /&gt;
| | env.c1.child2&lt;br /&gt;
| + env.c2&lt;br /&gt;
| | env.c2.child1&lt;br /&gt;
| | env.c2.child2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция visit() использует  глубину узла и  определяет является ли он конечным  для печати строки каждого узла.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM</id>
		<title>OVM/OVM методология/Механика OVM</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM"/>
				<updated>2013-03-18T09:24:44Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= Механика  OVM =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Библиотека OVM предоставляет много возможностей для построения testbenches. В этой главе мы ознакомимся с наиболее важными возможностями, которые вы будете использовать  почти во всех ваши testbenches.&lt;br /&gt;
&lt;br /&gt;
== Компоненты и иерархия ==&lt;br /&gt;
&lt;br /&gt;
Первичная структура для создания элементов testbench  является компонента. Компонента  в OVM – это аналогия  модулю в Verilog. Компонента OVM сконструирована на основе класса, который обеспечивает ему различные характеристики, чем модуль Verilog и имеет различные использования импликаций. Среди таких характеристик является то, что классы создаются во время выполнения, а не во время разработки, как модули. Таким образом, OVM отвечает за создание компонента и сборки их в иерархии.&lt;br /&gt;
&lt;br /&gt;
[[Файл:69.png]]&lt;br /&gt;
&lt;br /&gt;
Рисунок 4-1 иллюстрирует простую иерархию компонент. После  мы покажем,  как построить эту иерархию с помощью средств OVM  для создания компонент и объединения их в иерархии:&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Самый верхний узел, ENV, является корнем. Корень отличается тем, что он не имеет предка. Все остальные узлы имеют только одного предка. Каждый узел имеет имя. Расположение каждого узла в иерархии может быть идентифицировано уникальным full_name (путем), который строится на нанизывании имен всех узлов между корнем и узлов в запросе, разделяя их сепаратором иерархии, точкой (.). Например, путь к компоненту, второго потомка с2 -  top.c2.child2.&lt;br /&gt;
&lt;br /&gt;
Компонент в ОВМ является классом, производным от ovm_component. Простейшая компоненты -  листья, те, которые не имеют потомков.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
57 class child extends ovm_component;&lt;br /&gt;
58&lt;br /&gt;
59 function new(string name, ovm_component parent);&lt;br /&gt;
60 super.new(name, parent);&lt;br /&gt;
61 endfunction&lt;br /&gt;
62&lt;br /&gt;
63 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Конструктор имеет два параметра, имя компоненты и указатель на его предка. Название -  простое имя, а не иерархический путь. Предок  предоставляет место для подключения  новой компоненты в иерархии. Полный  путь потомка создается путем объединения имени потомка с полным именем предка, разделенный точкой (.).OVM предоставляет методы для получения и имени и полного пути компоненты:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string get_name();&lt;br /&gt;
string get_full_name();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Подчиненные компоненты создаются  в функции build(), которая вызывается на этапе сборки (фазы объясняется далее в этой главе). Создание экземпляра компонента включает вызов new() для выделения памяти для нее и передачи соответствующих параметров в конструктор. В компоненте, показанной ниже, мы создаем две подчиненные компоненты, и child1 child2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
71 class component extends ovm_component;&lt;br /&gt;
72&lt;br /&gt;
73 child child1;&lt;br /&gt;
74 child child2;&lt;br /&gt;
75&lt;br /&gt;
76 function new(string name, ovm_component parent);&lt;br /&gt;
77 super.new(name, parent);&lt;br /&gt;
78 endfunction&lt;br /&gt;
79&lt;br /&gt;
80 function void build();&lt;br /&gt;
81 child1 = new(“child1”, this);&lt;br /&gt;
82 child2 = new(“child2”, this);&lt;br /&gt;
83 endfunction&lt;br /&gt;
84&lt;br /&gt;
85 endclass&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как component, ENV также создает экземпляры двух подчиненных компонент, c1 и c2. Вся иерархия  с корнем в модуле,  называемым top в нашем проекте.(??)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
131 module top;&lt;br /&gt;
132&lt;br /&gt;
133 env e;&lt;br /&gt;
134&lt;br /&gt;
135 initial begin&lt;br /&gt;
136 e = new(“env”);&lt;br /&gt;
137 run_test();&lt;br /&gt;
138 end&lt;br /&gt;
139&lt;br /&gt;
140 endmodule&lt;br /&gt;
file: 04_OVM_mechanics/01_hierarchy/top.sv&lt;br /&gt;
&lt;br /&gt;
Вызов new() создает верхний уровень окружающей среды. run_test () начинает выполнение testbench.&lt;br /&gt;
&lt;br /&gt;
В SystemVerilog, модули, интерфейсы и программные блоки создаются во время разработки, в то время как классы создаются после разработки, во время выполнения. Таким образом, чтобы создать иерархию классов, мы должны иметь интерфейс, модуль или программу, который содержит начальный блок, который начинает процесс создание компонент иерархии, основанных на классах. Интерфейсы предназначены для использования в качестве средства связи между двумя модулями и не очень хорошо подходят для использования в качестве корня иерархии классов. Программные  блоки или модули могут быть использованы как  корень (??). Для нашей простой иерархии, это не имеет значения. Позже, когда мы подключаем класс-компонент для модуля на основе аппаратных средств, мы увидим, что использование модуля предпочтительнее программным блокам.&lt;br /&gt;
&lt;br /&gt;
=== Перемещение по иерархии ===&lt;br /&gt;
&lt;br /&gt;
Мы можем исследовать структуры данных, используемые для реализации компонента иерархии с некоторыми методами, предусмотренными в ovm_component. Потомки компоненты хранятся в ассоциативном массиве. Этот массив напрямую не доступен, но он может быть доступен через иерархию API. Этот API-интерфейс похож на встроенные методы SystemVerilog, предназначенные для ассоциативных массивов.&lt;br /&gt;
&lt;br /&gt;
int get_first_child(ref string name);&lt;br /&gt;
int get_next_child(ref string name);&lt;br /&gt;
ovm_component get_child(string name);&lt;br /&gt;
int get_num_children();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
get_first_child () и get_next_child () работают вместе для перебора множества потомков, содержащихся в компоненте. get_first_child () извлекает имя первого ребенка в списке. Он возвращает имя в качестве ссылки. get_next_child () возвращает имя следующего ребенка в списке. Он  возвращает 1, если имеется следующая имя потомка и 0, если был достигнут конец списка. get_child () преобразует имя в ссылку компоненты.&lt;br /&gt;
&lt;br /&gt;
Используя эти функции, мы можем пройти по компонентам иерархии.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
73 function void depth_first(ovm_component node,&lt;br /&gt;
74 int unsigned level = 0);&lt;br /&gt;
75&lt;br /&gt;
76 string name;&lt;br /&gt;
77&lt;br /&gt;
78 if(node == null)&lt;br /&gt;
79 return;&lt;br /&gt;
80&lt;br /&gt;
81 visit(node, level);&lt;br /&gt;
82&lt;br /&gt;
83 if(node.get_first_child(name))&lt;br /&gt;
84 do begin&lt;br /&gt;
85 depth_first(node.get_child(name), level+1);&lt;br /&gt;
86 end while(node.get_next_child(name));&lt;br /&gt;
87&lt;br /&gt;
88 endfunction&lt;br /&gt;
file: 04_OVM_mechanics/utils/traverse.svh&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Эта функция будет осуществляющая обход в глубину иерархии, называя visit(). Мы используем get_first_child () и get_next_child () для перебора списка каждого потомка в каждом узле. Для каждого итерации мы вызываем depth_first () рекурсивно. Для нашего маленького проекта, результат будет  следующий:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
+ env&lt;br /&gt;
| + env.c1&lt;br /&gt;
| | env.c1.child1&lt;br /&gt;
| | env.c1.child2&lt;br /&gt;
| + env.c2&lt;br /&gt;
| | env.c2.child1&lt;br /&gt;
| | env.c2.child2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Функция visit() использует  глубину узла и  определяет является ли он конечным  для печати строки каждого узла.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:69.png</id>
		<title>Файл:69.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:69.png"/>
				<updated>2013-03-18T09:20:33Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM</id>
		<title>OVM/OVM методология/Механика OVM</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%BA%D0%B0_OVM"/>
				<updated>2013-03-18T09:20:11Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: Новая страница: «{{OVM TOC}} __TOC__  = Механика  OVM =»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= Механика  OVM =&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM</id>
		<title>OVM</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM"/>
				<updated>2013-03-18T09:17:50Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: /* Описание проекта */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM_TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
== Описание проекта ==&lt;br /&gt;
* [http://redmine.simhard.com/projects/ovm Контроль проекта]&lt;br /&gt;
* [http://simhard.com/websvn/listing.php?repname=ovm Просмотр репозитория проекта]&lt;br /&gt;
* [[OVM/OVM методология | OVM методология  ]]&lt;br /&gt;
**[[OVM/OVM методология/Введение| Введение]]&lt;br /&gt;
**[[OVM/OVM методология/Основы верификации| Основы верификации]]&lt;br /&gt;
**[[OVM/OVM методология/Основы объектно-ориентированного программирования| Основы объектно-ориентированного программирования]]&lt;br /&gt;
**[[OVM/OVM методология/Transaction-Level моделирование|Transaction-Level моделирование]]&lt;br /&gt;
**[[OVM/OVM методология/Механика  OVM|Механика  OVM]]&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* [[OVM/Ссылки | Ссылки ]]&lt;br /&gt;
* [[OVM/Книги | Книги ]]&lt;br /&gt;
* [[OVM/Стандарт OVM| Стандарт OVM ]]&lt;br /&gt;
== TO DO ==&lt;br /&gt;
* [[OVM/Описание пакетов | Описание пакетов ]]&lt;br /&gt;
* [[OVM/Описание функций | Описание функций ]]&lt;br /&gt;
* [[OVM/Тестовый план | Тестовый план ]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дипломы-2013]]&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5</id>
		<title>OVM/OVM методология/Transaction-Level моделирование</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5"/>
				<updated>2013-03-14T08:11:57Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Процесс проектирования электронной системы включает в себя последовательное замещение абстрактных идей  конкретной их реализацией, пока не будет достигнуто представление, которое может быть изготовлено на кремнии. С момента появления цифровой интегральной схемы, электронные сообщества тщательно определили и кодировали абстракции, начиная с переключателей и логических элементов, для обеспечения среды  в которой работает  проект. RTL является примером среды, часто используемой для создания проектов. Есть много инструментов на основе RTL, которые делают ее удобной для  создания проекта и его верификации.&lt;br /&gt;
Однако, как только проект становится больше и сложнее, становится  более представлять их с помощью абстракции выше, чем RTL. Уровень транзакций становится популярным для создания первого представления проекта, которое может быть смоделировано и проанализировано.&lt;br /&gt;
В этой главе описаны основные понятия transaction-level моделирования (TLM). Модели  transaction-level состоят из нескольких процессов, которые обмениваются информацией посредством транзакций.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Абстракция ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
RTL моделирование использует дискретную модель времени. Связь между процессами осуществляется с помощью сетки ( цепи), и процесс активации происходит при изменении значения во входной сети ( цепи).&lt;br /&gt;
Для сравнения, transaction-level модели (моделирующей схемы) могут быть синхронизированы или не синхронизированы и использовать шину для обмена данными между процессами. Вместо того чтобы посылать отдельные биты туда и обратно, процессы общаются, посылая транзакции друг  другу посредством вызова функций.  Мир TLM включает в себя ряд моделей(моделирующих схем) вычислений с различным временем, системами связи и процессами активации моделей (моделирующей схемы). В каждом конкретном случае, содержание связей находится на более высоком уровне абстракции, чем отдельные биты. Таким образом, transaction-level модель находится на более высоком уровне абстракции (более абстрактна), чем RTL модель. Объединяя понятия абстракции и модели (моделирующей схемы) вычислений, видно, что создание абстрактных моделей означает абстрагирование времени, данных и функций. &lt;br /&gt;
&lt;br /&gt;
'''Абстракция времени.''' Время абстракция в симуляторе относится к тому, насколько внутренние состояния схемы совместимы. Модели, запущенные в управляемых событиями симуляторах (например, логических симуляторах) используют дискретное представление  времени, то есть события происходят в определенные моменты времени. События, как правило (хотя и не всегда) вызывают запущенный процесс. Чем больше событий происходит в симуляции, больше процессов вызывается, и с большим числом процессов происходит снижении скорости моделирования. Абстрагирование время уменьшает количество точек, где схема должна быть совместимой и общего числа событий и процессов, которые будут активированы. Например, в RTL модели, каждая сеть должна быть согласована после каждого изменения. В cycle-accurate абстракции, схема должна быть синхронизирована с синхросигналом, устраняя все события, что происходит между фронтами синхроимпульса. В transaction-level модели, состояния схемы должны быть синхронизированы в конце каждой операции, каждая из которых может охватывать много тактов.&lt;br /&gt;
&lt;br /&gt;
'''Абстракция данных'''. Данные относятся к объектам, передаваемым между компонентами. В RTL моделях, данные относятся к отдельным битам, которые передаются по сети между компонентами. В transaction-level моделях, данных представлены в виде транзакций, гетерогенных структур, которые содержат произвольные коллекции элементов.&lt;br /&gt;
Рассмотрим пакет в устройстве связи. На самом низком уровне детализации, пакет содержит бит начала и стоп-биты, заголовок, информацию исправления ошибок, размер полезной нагрузки, нагрузки, и завершитель (??????). В более абстрактной модели, только нагрузка и размер нагрузки могут быть необходимы. Другие  данные не нужны  для выполнения расчетов.&lt;br /&gt;
&lt;br /&gt;
'''Абстракция функций.''' Функция модели является набором всех операций, которая она делать на каждое событие. Абстрагирование функций уменьшает этот набор или заменяет его более простыми вычислениями. Например, в ALU, вы можете выбрать использование родной операции умножении поставляемой в язык моделирования, вместо кодирования  a shift-and-add алгоритма умножения. Последний может быть частью реализации, но на более высоком уровне, детали a shift-and-add - неважны. Базовые элементы, которые являются частью языка, определяет, как вы можете абстрагировать функцию. В gate-level  языке, например, можно создавать сложные поведения, начиная с логических элементов. В RTL языка, построение  поведения основывается на арифметических и логических операциях над регистрами. В TLM, вы реализуете функциональность схемы с помощью вызовов функций произвольной сложности.&lt;br /&gt;
&lt;br /&gt;
Для целей функциональной верификации, RTL самый низкий уровень абстракции, который мы должны рассмотреть. С синтезаторы могут эффективно конвертировать RTL в логические элементы, нам не нужно затрагивать  более низкий уровень детализации. &lt;br /&gt;
&lt;br /&gt;
==  Определение транзакции ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для более подробного рассмотрения TLM, мы должны сделать шаг назад и определить понятие транзакции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:61.png]]&lt;br /&gt;
&lt;br /&gt;
Это самое общее определение транзакции. Оно утверждает, что транзакция – это все, что происходит в проекте (или модуле или подсистеме проекта) между двумя точками времени. Хотя оно точное, оно является настолько общим, что не приводит к практическому применению. Более полезные определения  следующие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:62.png]]&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Транзакция — передача управления и данных между двумя элементами (компонентами)&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это аппаратно-ориентированные понятие транзакции. Когда смотришь на часть аппаратного обеспечения, вы можете легко определить элементы, между которыми передается управление или данные. В проекте с шинной архитектурой, чтение и запись на шину может быть вызвано транзакцией. В пакетной системе связи, отправка пакета представляет собой транзакцию.&lt;br /&gt;
&lt;br /&gt;
Ниже третье определение:&lt;br /&gt;
&lt;br /&gt;
[[Файл:63.png]]&lt;br /&gt;
&lt;br /&gt;
Это определение программно-ориентированного понятия транзакции. В transaction-level модели, деятельность инициируется путем вызова функции. Вызов функции содержит параметры, которые &amp;quot;посылаются&amp;quot; (передаются) вызываемой функции, а возвращаемое значение функции содержит данные, которые возвращаются вызванной функцией. Вызванная  функция может блокировать и вызвать задержку (в синхронной системе) или может вернуться (выполниться) немедленно.&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
This definition is the software-oriented notion of a transaction. In a&lt;br /&gt;
transaction-level model, activity is initiated by making function calls. The&lt;br /&gt;
function call contains parameters that are “sent” to the called function, and&lt;br /&gt;
the return value of the function contains data that is returned by the called&lt;br /&gt;
function. The called function could block and cause time to pass (in a timed&lt;br /&gt;
system) or it could return immediately.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Интерфейсы ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Прежде чем вдаваться в подробности о том, как построить transaction-level модель, мы сначала обсудим интерфейсы. Термин интерфейс используется в несколько случаях в OVM, каждый раз с немного другим смыслом. Это прискорбный факт истории, то что слово стало означать  различные вещи. В большинстве случаев значение будет понятно из контекста, в котором этот термин используется. Различных применения :&lt;br /&gt;
&lt;br /&gt;
* Интерфейс в SystemVerilog&lt;br /&gt;
* Интерфейс объекта&lt;br /&gt;
* Интерфейс DUT&lt;br /&gt;
&lt;br /&gt;
'''Интерфейс в SystemVerilog'''. SystemVerilog предоставляет конструкцию, называемую интерфейс, который является одним из основных объектов контейнер, из которого строится проект в SystemVerilog. Мы используем виртуальные интерфейсы, которые  указывают на интерфейсы для подключения module-based аппаратуры к  class-based testbenches.&lt;br /&gt;
&lt;br /&gt;
'''Интерфейс объекта.''' Общедоступные задачи и функции, доступные для объекта, образуют его интерфейс. Есть два небольших варианта этого значения интерфейса. Одним из них является прямой. Посмотрите на класс и определите, какие задачи и функций, доступны пользователю класса. Это его интерфейс. Другое значение состоит в ссылке на базовый класс, который определяет набор задач и функций, доступных для работы производного класса. Это значение интерфейса обычно используется в объектно-ориентированных языках, которые поддерживают множественное наследование, таких как C++ или Java. В этих  языках, вы можете установить требование, что производный класс определяет функциональность, наследуемую от интерфейса базового класса.&lt;br /&gt;
&lt;br /&gt;
[[Файл:64.png]]&lt;br /&gt;
&lt;br /&gt;
Интерфейс print_if определяет прототипы для функций печати.  Любой класс, который наследуется от print_if затем обязан осуществлять print()и sprint(). SystemVerilog не поддерживает множественное наследование, но он  поддерживает истинно виртуальные интерфейсы. Истинно  виртуальный интерфейс - интерфейс во втором контексте (базовый класс, который определяет набор задач и функций прототипов), который не имеет реализаций. Истинно  виртуальная версия  print_if  выглядит следующим образом в SystemVerilog:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;virtual class print_if;&lt;br /&gt;
pure virtual function void print();&lt;br /&gt;
pure virtual function string sprint();&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Даже если SystemVerilog не поддерживает множественное наследование, и OVM построено на SystemVerilog, важно понять истинно виртуальные интерфейсы и наследование интерфейсов, потому что они активно используются в OVM. В частности, TLM ports and exports являются производными от интерфейса класс tlm_if_base. &lt;br /&gt;
&lt;br /&gt;
'''DUT интерфейс.''' Часть аппаратуры, как правило, доступны через свои интерфейсы. В этом контексте, интерфейс состоит из контактов и протоколов используемых для связи с устройством. Например, устройство может иметь USB интерфейс.&lt;br /&gt;
&lt;br /&gt;
== TLM идиомы ==&lt;br /&gt;
&lt;br /&gt;
В данном разделе рассматриваются основные средства передачи транзакций между компонентами. Мы put, get, и transport формы связи транзакций. Эти примеры не используют библиотеку OVM, так как они предназначены для иллюстрации основных механизмов связи на transaction-level с минимальными затратами. В следующем разделе мы рассмотрим более полный пример, который использует OVM библиотека для связи.&lt;br /&gt;
&lt;br /&gt;
=== Put ===&lt;br /&gt;
В put  конфигурации, один компонент отправляет операции  другому компоненту. Операция называется put. Инициатором является компонентом, который инициирует передачу, а целевой компонент, который получает результат. Используя TLM номенклатуру, мы говорим, что инициатор put транзакции цели.&lt;br /&gt;
&lt;br /&gt;
[[Файл:65.png]]&lt;br /&gt;
 &lt;br /&gt;
Рисунок 3-2 показывает, что А предает операцию в пункт Б. Инициатор имеет порт, рисуется как квадрат, и цель имеет export, изображается в виде круга. Поток  управления направлен  от квадрата к кругу, то есть, А вызывает B, который содержит реализацию методов port. Стрелка показывает направление поток данных, и в этом случае, это означает, что данные будут двигаться от А до B.&lt;br /&gt;
Мы можем проиллюстрировать код для этих компонентов с producer и consumer. producer является инициатором и consumer является целью. Мы должны строить эти компоненты таким образом, что они не знали друг о друге априори. Чтобы сделать это, мы используем истинно виртуальный интерфейс для определения функции, который будет использоваться для передачи данных между инициатором и целью. Во-первых, давайте взглянем на SystemVerilog версию producer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;46 class producer;&lt;br /&gt;
47&lt;br /&gt;
48 put_if put_port;&lt;br /&gt;
49&lt;br /&gt;
50 task run();&lt;br /&gt;
51&lt;br /&gt;
52 int randval;&lt;br /&gt;
53&lt;br /&gt;
54 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
55 begin&lt;br /&gt;
56 randval = $random %100;&lt;br /&gt;
57 $display(“producer: sending %4d”, randval);&lt;br /&gt;
58 put_port.put(randval);&lt;br /&gt;
59 end&lt;br /&gt;
60&lt;br /&gt;
61 endtask&lt;br /&gt;
62&lt;br /&gt;
63 endclass : producer&lt;br /&gt;
file: 03_tlm/01_put/put.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
producer  - это класс, который  создается динамически. Он имеет два ключевых элемента, Run () задачу и put_port. Run () задача является простой задачей,  которая повторяется 10 раз и  передает 10 транзакций. Для простоты, наши операции являются целыми числами. На практике, транзакция может быть сколь угодно сложным  объектом, таким как структура или класс.&lt;br /&gt;
Чтобы передать операции, producer вызывает put() на put_port. Что такое put_port? Это не порт в традиционном смысле Verilog. Это ссылка на put_if. Что такое put_if? Put_if это виртуальный интерфейс класса расположенный между инициатором (producer) и целью (consumer).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;39 virtual class put_if;&lt;br /&gt;
40 pure virtual task put(int val);&lt;br /&gt;
41 endclass : put_if&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
put_if класс с истинно виртуальными задачами, т.е.  задачами, не имеющими реализацию. Без реализации всех своих задач и функций, виртуальный класс не может быть создан сам по себе. Он должен быть базовым классом другого класса, который создается. В нашем случае, класс, производный от истинно виртуального put_if является consumer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;68 class consumer extends put_if;&lt;br /&gt;
69 task put(int val);&lt;br /&gt;
70 $display(“consumer: receiving %4d”, val);&lt;br /&gt;
71 endtask : put&lt;br /&gt;
72 endclass : consumer&lt;br /&gt;
file: 03_tlm/01_put/put.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
consumer содержит реализацию put(); истинно виртуальные задачи определены в put_if.  Задача put() реализация принимает аргументы, переданные ей, и печатает их. put_if играет ключевую роль в соединении consumer и producer. Ссылка на него на стороне producer, которую мы называем порт, устанавливает требование, что должна быть реализация  функций и задач  интерфейса, к которому этот объект будет привязан. consumer является производным от интерфейса и, следовательно, в нем должны быть реализованы истинно виртуальные задачи, удовлетворяющие требованиям.&lt;br /&gt;
&lt;br /&gt;
Модуль верхнего уровня связывающий producer и consumer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;77 module top;&lt;br /&gt;
78&lt;br /&gt;
79 producer p;&lt;br /&gt;
80 consumer c;&lt;br /&gt;
81&lt;br /&gt;
82 initial begin&lt;br /&gt;
83 // instantiate producer and consumer&lt;br /&gt;
84 p = new();&lt;br /&gt;
85 c = new();&lt;br /&gt;
86 // connect producer and consumer&lt;br /&gt;
87 // through the put_if interface class&lt;br /&gt;
88 p.put_port = c;&lt;br /&gt;
89 p.run();&lt;br /&gt;
90 end&lt;br /&gt;
91 endmodule : top&lt;br /&gt;
file: 03_tlm/01_put/put.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что оператор присваивания:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;88 p.put_port = c;&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Он образует связь между producer и consumer. Когда new() вызывается на р, чтобы создать новый экземпляр producer, член put_port  не имеет значения. Во время выполнения произойдет ошибка, если put_port. put () вызывается до связи назначения. Назначение С к p.put_port дает  ссылку для consumer, который содержит реализацию задачи put() интерфейса.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Get ===&lt;br /&gt;
&lt;br /&gt;
Дополнением к put является get. В этом устройстве, инициатор получает транзакции от цели. Поток управления такой же, от инициатора к цели, но направление потока данных  обратное. Инициатор получает транзакции от цели. В этом случае consumer является инициатором, producer - целью. Consumer  инициирует вызов producer для получения транзакции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:66.png]]&lt;br /&gt;
 &lt;br /&gt;
Рисунок 3-3 очень похож на рисунок 3-2. Разница лишь в том, что здесь стрелка от цели к инициатору, а не наоборот. Это означает, что потоки данных от цели к инициатору. Ниже SystemVerilog consumer (инициатор).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;62 class consumer;&lt;br /&gt;
63&lt;br /&gt;
64 get_if get_port;&lt;br /&gt;
65&lt;br /&gt;
66 task run();&lt;br /&gt;
67 int randval;&lt;br /&gt;
68 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
69 begin&lt;br /&gt;
70 get_port.get(randval);&lt;br /&gt;
71 $display(“consumer: receiving %4d”, randval);&lt;br /&gt;
72 end&lt;br /&gt;
73 endtask&lt;br /&gt;
74 endclass&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
consumer имеет задачу, run(), которая выполняется  10 раз, чтобы получить 10 транзакций. Producer   как в put  примере, consumer имеет здесь порт. Кроме того, как в put  примере, порт ссылается на истинно виртуальный интерфейс, в данном случае&lt;br /&gt;
он называется get_if.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;41 virtual class get_if;&lt;br /&gt;
42 pure virtual task get(output int t);&lt;br /&gt;
43 endclass : get_if&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
get_if&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
является истинно виртуальным  интерфейс, который определяет функцию get(). Цель (producer) строится аналогично, как и цель в put  примере. Она содержит реализацию функции интерфейса. Этот producer генерирует случайное число между 0 и 99.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;48 class producer extends get_if;&lt;br /&gt;
49&lt;br /&gt;
50 task get(output int t);&lt;br /&gt;
51 int randval;&lt;br /&gt;
52 randval = $random % 100;&lt;br /&gt;
53 $display(“producer: sending %4d”, randval);&lt;br /&gt;
54 t = randval;&lt;br /&gt;
55 endtask&lt;br /&gt;
56&lt;br /&gt;
57 endclass : producer&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Связи на высшем уровне будут выглядеть очень похожими.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;79 module top;&lt;br /&gt;
80&lt;br /&gt;
81 producer p;&lt;br /&gt;
82 consumer c;&lt;br /&gt;
83&lt;br /&gt;
84 initial begin&lt;br /&gt;
85 // instantiate producer and consumer&lt;br /&gt;
86 p = new();&lt;br /&gt;
87 c = new();&lt;br /&gt;
88 // connect producer and consumer through the get_if&lt;br /&gt;
89 // interface class&lt;br /&gt;
90 c.get_port = p;&lt;br /&gt;
91 c.run();&lt;br /&gt;
92 end&lt;br /&gt;
93 endmodule : top&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После создания объектов producer и consumer, используя операцию new(),  два объекта будут соединяться с помощью связей назначения.&lt;br /&gt;
&lt;br /&gt;
=== Transport === &lt;br /&gt;
&lt;br /&gt;
Transport является двунаправленным интерфейсом. Интерфейс обеспечивает передачу транзакций от инициатора к цели и от цели обратно к инициатору. Как правило, мы используем этот механизм, чтобы смоделировать запрос-ответ протоколы. Когда речь идет о компонентах с двунаправленным интерфейсов, мы используем термины master (ведущего) и slave (ведомого), а не инициатор и цель.&lt;br /&gt;
 &lt;br /&gt;
[[Файл:67.png]]&lt;br /&gt;
&lt;br /&gt;
master (A) делает и put и get  за один вызов функции. Как мы видели в предыдущих примерах, put() и get() принимают один аргумент, аргумент они помещают или извлекают. Тем не менее, функция transport() имеет два аргумента, запрос и ответ. Она посылает запрос и возвращает  ответ. slave (B) принимает запрос и возвращает ответ.&lt;br /&gt;
&lt;br /&gt;
Давайте сначала посмотрим на истинно виртуальный интерфейс.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;37 virtual class transport_if;&lt;br /&gt;
38 pure virtual task transport(input int request,&lt;br /&gt;
39 output int response);&lt;br /&gt;
40 endclass : transport_if&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Интерфейс содержит одну функцию, transport(), которая принимает два аргумента: запрос, который передается  цели и ответ, который возвращается  инициатору. &lt;br /&gt;
&lt;br /&gt;
Master вызывает transport(), создает запрос и отправляет его slave, используя transport. Он обрабатывает ответ, который возвращается.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;45 class master;&lt;br /&gt;
46&lt;br /&gt;
47 transport_if port;&lt;br /&gt;
48&lt;br /&gt;
49 task run();&lt;br /&gt;
50&lt;br /&gt;
51 int request;&lt;br /&gt;
52 int response;&lt;br /&gt;
53&lt;br /&gt;
54 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
55 begin &lt;br /&gt;
56 request = $random % 100;&lt;br /&gt;
57 $display(“master: sending request %4d”,&lt;br /&gt;
58 request);&lt;br /&gt;
59 port.transport(request, response);&lt;br /&gt;
60 $display(“master: receiving response %4d”,&lt;br /&gt;
61 response);&lt;br /&gt;
62 end&lt;br /&gt;
63&lt;br /&gt;
64 endtask&lt;br /&gt;
65 endclass : master&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
slave реализует функцию transport(). В нашем примере, она делает некоторые тривиальные обработки запроса для создания ответа.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;70 class slave extends transport_if;&lt;br /&gt;
71&lt;br /&gt;
72 task transport(input int request, output int response);&lt;br /&gt;
73 $display(“slave: receiving request %4d”,&lt;br /&gt;
74 request);&lt;br /&gt;
75 response = -request;&lt;br /&gt;
76 $display(“slave: sending response %4d”,&lt;br /&gt;
77 response);&lt;br /&gt;
78 endtask&lt;br /&gt;
79&lt;br /&gt;
80 endclass&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Связи верхнего уровня между master и slave работают точно так же как и в примере с put и get.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;85 module top;&lt;br /&gt;
86&lt;br /&gt;
87 master m;&lt;br /&gt;
88 slave s;&lt;br /&gt;
89&lt;br /&gt;
90 initial begin&lt;br /&gt;
91 // instantiate the master and slave&lt;br /&gt;
92 m = new();&lt;br /&gt;
93 s = new();&lt;br /&gt;
94&lt;br /&gt;
95 // connect the master and slave through&lt;br /&gt;
96 // the port interface&lt;br /&gt;
97 m.port = s;&lt;br /&gt;
98 m.run();&lt;br /&gt;
99 end&lt;br /&gt;
100&lt;br /&gt;
101 endmodule : top&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Связь назначения связывает master и slave. После выполнения соединения, master может использовать соединения для прямого вызова функций slave.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Блокирующие интерфейсы против не блокирующих ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Интерфейсы мы рассмотрели ранее блокирующие. Это означает, что функции и задачи блокируют выполнение до их завершения. Им не разрешается заканчиваться неудачей.  Не существует механизма для  блокировки при аварийном завершении или иным образом изменять поток управления. Они просто ждать, пока запрос будет выполнен. В синхронизированной системе это означает, что время может пройти между вызовом и возвращением полученного результата.&lt;br /&gt;
&lt;br /&gt;
В put конфигурации, у нас есть два компонента, producer и consumer. Producer  генерирует случайное число и посылает его к consumer, используя  put(). Прежде чем вызывается put(),  нет активности в consumer. Вызов put() вызывает активность в consumer, которая выводит значение аргумента. За то время, что consumer является активным, producer ожидает. Это природа блокирующего вызова. Вызывающий должен ждать, пока вызов завершиться для возобновления выполнения.&lt;br /&gt;
&lt;br /&gt;
Теперь сравните это описание с неблокирующим вызовом. Неблокирующий вызов возвращается немедленно. Семантика неблокирующего вызова гарантирует, что вызов возвращается в том же дельта цикле, в котором он был запущен, то есть без задержки, даже на один дельта цикл.&lt;br /&gt;
&lt;br /&gt;
Истинно  виртуальный интерфейс, который соединяет неблокирующего slave  и master выглядит так же, как другие истинно виртуальные интерфейсы, которые мы видели. Значительная разница в том, что nb_get () возвращает значение статуса вместо транзакции.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;41 virtual class get_if;&lt;br /&gt;
42 pure virtual function int nb_get(output int t);&lt;br /&gt;
43 endclass : get_if&lt;br /&gt;
file: 03_tlm/04_nonblocking/nbget.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
master (consumer) должен проверить состояние, возвращаемое из nb_get (), чтобы определить, завершена ли функция успешно. Отметим также, что мы ввели время в модель. Consumer  проверяет каждый 4 нс доступность значения.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;78 class consumer; &lt;br /&gt;
79&lt;br /&gt;
80 get_if get_port;&lt;br /&gt;
81&lt;br /&gt;
82 task run();&lt;br /&gt;
83 int randval;&lt;br /&gt;
84 int ok;&lt;br /&gt;
85&lt;br /&gt;
86 for(int i=0; i&amp;lt;20; i++)&lt;br /&gt;
87 begin&lt;br /&gt;
88 #4;&lt;br /&gt;
89 if(get_port.nb_get(randval))&lt;br /&gt;
90 $display(“%t: consumer: receiving %4d”, $time,&lt;br /&gt;
randval);&lt;br /&gt;
91 else&lt;br /&gt;
92 $display(“%t: consumer: no randval”, $time);&lt;br /&gt;
93 end&lt;br /&gt;
94 endtask&lt;br /&gt;
95 endclass&lt;br /&gt;
file: 03_tlm/04_nonblocking/nbget.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
producer  организован как функция и задача. Задача будет раздвоена (порождена), чтобы запуститься как непрерывный процесс. Он генерирует новые случайные значения, что consumer будет захватывать. Тем не менее, каждая случайная величина доступна только для 2 нс из 7 нс цикла. Функция - реализация nb_get, которая возвращает значение, запускает периодически выполнение run().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;48 class producer extends get_if;&lt;br /&gt;
49&lt;br /&gt;
50 int randval = 0;&lt;br /&gt;
51 int rand_avail = 0;&lt;br /&gt;
52&lt;br /&gt;
53 function int nb_get(output int t);&lt;br /&gt;
54 if(rand_avail) begin&lt;br /&gt;
55 $display(“%t: producer: sending %4d”,&lt;br /&gt;
56 $time, randval);&lt;br /&gt;
57 t = randval;&lt;br /&gt;
58 return 1;&lt;br /&gt;
59 end&lt;br /&gt;
60 return 0;&lt;br /&gt;
61 endfunction&lt;br /&gt;
62&lt;br /&gt;
63 task run();&lt;br /&gt;
64 forever begin;&lt;br /&gt;
65 #5;&lt;br /&gt;
66 randval = $random % 100;&lt;br /&gt;
67 rand_avail = 1;&lt;br /&gt;
68 #2;&lt;br /&gt;
69 rand_avail = 0;&lt;br /&gt;
70 end&lt;br /&gt;
71 endtask&lt;br /&gt;
72&lt;br /&gt;
73 endclass : producer&lt;br /&gt;
file: 03_tlm/04_nonblocking/nbget.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Когда мы запускаем пример, мы видим, что не каждый вызов nb_get () успешен.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;4: consumer: no randval&lt;br /&gt;
8: consumer: no randval&lt;br /&gt;
12: producer: sending -99&lt;br /&gt;
12: consumer: receiving -99&lt;br /&gt;
16: consumer: no randval&lt;br /&gt;
20: producer: sending -39&lt;br /&gt;
20: consumer: receiving -39&lt;br /&gt;
24: consumer: no randval&lt;br /&gt;
28: producer: sending -9&lt;br /&gt;
28: consumer: receiving -9&lt;br /&gt;
32: consumer: no randval&lt;br /&gt;
36: consumer: no randval&lt;br /&gt;
40: producer: sending 57&lt;br /&gt;
40: consumer: receiving 57&lt;br /&gt;
44: consumer: no randval&lt;br /&gt;
48: producer: sending -71&lt;br /&gt;
48: consumer: receiving -71&lt;br /&gt;
52: consumer: no randval&lt;br /&gt;
56: producer: sending -14&lt;br /&gt;
56: consumer: receiving -14&lt;br /&gt;
60: consumer: no randval&lt;br /&gt;
64: consumer: no randval&lt;br /&gt;
68: producer: sending 29&lt;br /&gt;
68: consumer: receiving 29&lt;br /&gt;
72: consumer: no randval&lt;br /&gt;
76: producer: sending 18&lt;br /&gt;
76: consumer: receiving 18&lt;br /&gt;
80: consumer: no randval&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Блокировать get конфигурацию смог только один процесс - consumer, который постоянно обращался с запросом к producer, чтобы тот отправил новое значение. Неблокирующий  вариант состоит из двух этапов: consumer регулярно опрашивает producer, чтобы получить новое значение, и producer генерирует новые значения асинхронно по отношению к consumer. Наши неблокирующий producer генерирует доступное случайное значение каждые 7 нс. Он ждет 5 нс, а затем создает новое значение и новое значение действительно в течение 2 нс. Флаг  rand_avail устанавливается, когда  случайное значение доступно и сбрасывается в противном случае.&lt;br /&gt;
&lt;br /&gt;
Реализация nb_get () в этом примере должна проверить rand_avail  есть ли  что отправить. Если нет, то она возвращает 0, чтобы указать, что запрос не удался. Если есть что-то, то он посылает его и возвращает 1, чтобы показать успех.&lt;br /&gt;
&lt;br /&gt;
Блокирующие интерфейсы полезны для работы с двумя компонентами синхронно. Вызовы блокируются до тех пор, пока запрошенная операция выполняется, не смотря на то, сколько это потребует времени. С другой стороны, неблокирующие интерфейсы полезны при асинхронной передаче данных. Они не ждут и могут быть использованы для опроса целей, как в приведенном примере.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Изоляция компонент с помощью каналов ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В предыдущем разделе обсуждались простые механизмы для перемещения транзакций между двумя процессами. В каждом, инициатор и цель были сильно  синхронизированы вызовом транзакции интерфейса. В этом разделе мы рассмотрим случай, когда инициатор и цель менее тесно связаны, это можно сделать с помощью канала, в этом случае FIFO, для управления синхронизацией между инициатором и целью, а не полагаться на взаимную синхронизацию самих компонент. Здесь мы имеем две компоненты, инициатор А и цель B, а также FIFO, соединяющий две компоненты.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:68.png]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В предыдущих примерах, одна компонента имела  порт, а другая  export.  Компонента с портом  обращается к  компоненте с  export. Здесь оба  А и В имеют порты. Вместо того, чтобы инициатор обращался к непосредственно к  цели, теперь у нас есть  инициатор  и цель,  которые обращаются к каналу FIFO. Канал обеспечивает функции, необходимые как инициатору, так и цели.&lt;br /&gt;
&lt;br /&gt;
Инициатор использует блокирующий put() для отправки транзакций на FIFO, и цель использует блокирующий get() для получения транзакций с FIFO. FIFO хранит транзакции и служит в качестве синхронизатора. Инициатор может помещать транзакции в FIFO, пока он не заполнится . Так как инициатора использует блокировку Put (), инициатор процесса будет блокироваться, когда FIFO заполнен.  Кроме того, цель использует блокирующий get() и будет блокироваться, когда FIFO является пустым. По сути, producer в этом примере, как producer в блокирующем  put  примере,  и consumer, как consumer в блокирующем get. FIFO  replaces цели и предоставляет задачи, необходимые для удовлетворения требований интерфейса, созданные портами  producer и consumer.&lt;br /&gt;
&lt;br /&gt;
Давайте посмотрим на код. Это первый пример, который использует OVM библиотеки. OVM библиотека включает в себя FIFO, называемый tlm_fifo, который является параметризованным классом с различными интерфейсами для поддержки блокирующих и неблокирующий операций. &lt;br /&gt;
&lt;br /&gt;
В этом случае producer выглядит как producer в блокирующем пут примере. Он имеет процесс, run(), который выполняется 10 раз, генерирует 10 случайных значений и &lt;br /&gt;
отправив их к цели через put_port.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;42 class producer extends ovm_component;&lt;br /&gt;
43&lt;br /&gt;
44 ovm_blocking_put_port#(int) put_port;&lt;br /&gt;
45&lt;br /&gt;
46 function new(string name, ovm_component p = null);&lt;br /&gt;
47 super.new(name,p);&lt;br /&gt;
48 put_port = new(“put_port”, this);&lt;br /&gt;
49 endfunction&lt;br /&gt;
50&lt;br /&gt;
51 task run();&lt;br /&gt;
52&lt;br /&gt;
53 int randval;&lt;br /&gt;
54 string s;&lt;br /&gt;
55&lt;br /&gt;
56 for(int i = 0; i &amp;lt; 10; i++)&lt;br /&gt;
57 begin&lt;br /&gt;
58 randval = $random % 100;&lt;br /&gt;
59 $sformat(s, “sending %4d”, randval);&lt;br /&gt;
60 ovm_report_info(“producer”, s);&lt;br /&gt;
61 put_port.put(randval);&lt;br /&gt;
62 end&lt;br /&gt;
63 global_stop_request(); // OK, we’re done now&lt;br /&gt;
64 endtask&lt;br /&gt;
65&lt;br /&gt;
66 endclass&lt;br /&gt;
file: 03_tlm/05_fifo/fifo.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Есть две вещи, на которые необходимо обратить внимание. Во-первых, компоненты, полученные из ovm_component, которая является базовым классом в библиотеке OVM, обеспечивает основные возможности для компонент. Это позволяет компонентам подключиться к иерархии названных компонент и обеспечивает процесс управления выполнения задачи. Выполнение задачи разветвляется при запуске и может быть приостановлена или возобновлена по желанию.&lt;br /&gt;
&lt;br /&gt;
Кроме того, необходимо заметить, как put_port объявлен. В наших простых примерах выше, мы создали наш собственный истинно виртуальный интерфейс для подключения инициатора  к цели. Библиотека OVM поставляет набор портов и экспорт объектов, которые являются обертками вокруг истинно виртуальных ссылок интерфейса. Порт и экспорт объектов, которые сами по себе именные компоненты, позволяют функции connect() создавать ассоциаций между  ports and exports.  Использование модели лучше по сравнению с использованием операторов присваивания. &lt;br /&gt;
&lt;br /&gt;
Consumer не сильно отличается от consumer в блокирующем get примере.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;71 class consumer extends ovm_component;&lt;br /&gt;
72&lt;br /&gt;
73 ovm_blocking_get_port#(int) get_port;&lt;br /&gt;
74&lt;br /&gt;
75 function new(string name, ovm_component p = null);&lt;br /&gt;
76 super.new(name,p);&lt;br /&gt;
77 get_port = new(“get_port”, this);&lt;br /&gt;
78 endfunction&lt;br /&gt;
79&lt;br /&gt;
80 task run();&lt;br /&gt;
81&lt;br /&gt;
82 int val;&lt;br /&gt;
83 string s;&lt;br /&gt;
84&lt;br /&gt;
85 forever&lt;br /&gt;
86 begin&lt;br /&gt;
87 get_port.get(val);&lt;br /&gt;
88 $sformat(s, “receiving %4d”, val);&lt;br /&gt;
89 ovm_report_info(“consumer”, s);&lt;br /&gt;
90 end&lt;br /&gt;
91&lt;br /&gt;
92 endtask&lt;br /&gt;
93&lt;br /&gt;
94 endclass&lt;br /&gt;
file: 03_tlm/05_fifo/fifo.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для подключения producer, consumer и FIFO, мы используем окружающую среду. Средой служит  вершина иерархии именованных компонент, и это организует построение иерархии и  выполнения  testbench.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;99 class env extends ovm_component;&lt;br /&gt;
100 producer p;&lt;br /&gt;
101 consumer c;&lt;br /&gt;
102 tlm_fifo #(int) f;&lt;br /&gt;
103&lt;br /&gt;
104 function new(string name, ovm_component parent = null);&lt;br /&gt;
105 super.new(name, parent);&lt;br /&gt;
106 endfunction&lt;br /&gt;
107&lt;br /&gt;
108 function void build();&lt;br /&gt;
109 p = new(“producer”, this);&lt;br /&gt;
110 c = new(“consumer”, this);&lt;br /&gt;
111 f = new(“fifo”, this);&lt;br /&gt;
112 endfunction&lt;br /&gt;
113&lt;br /&gt;
114 function void connect();&lt;br /&gt;
115 p.put_port.connect(f.blocking_put_export);&lt;br /&gt;
116 c.get_port.connect(f.blocking_get_export);&lt;br /&gt;
117 endfunction&lt;br /&gt;
118&lt;br /&gt;
119 endclass&lt;br /&gt;
file: 03_tlm/05_fifo/fifo.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция connect() создает связь между портами на producer и  consumer и соответствующего экспорта на FIFO.  Run ()  отвечает за контроль выполнения   testbench.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Формирование Transaction-Level подключений ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для формирования transaction-level подключения, необходимо указать три элемента: поток управления, поток данных, а также тип данных транзакций. Объявление подключения как порта или export определяет поток управления – поток управления движется(???) от порта к export. То есть, порт инициирует деятельность и export реагирует на нее. Интерфейс определяет поток данных. put интерфейс означает, что данных движутся от инициатора (левый борт) к цели ( сторона export), get интерфейс показывает, что данные идут от цели к инициатору, а transport или запрос-ответ, интерфейс указывает двунаправленность потоков данных.&lt;br /&gt;
&lt;br /&gt;
Мы объявляем put_port как порт, поэтому мы знаем, что устройство, в котором этот порт объявлен является инициатором. Тип  интерфейса - tlm_nonblocking_put_if &amp;lt;&amp;gt;, который является одним put интерфейсов, определенных в библиотеке TLM. Этот порт выход для объектов данных. Наконец, тип данных отправленного объекта является trans.&lt;br /&gt;
&lt;br /&gt;
Использование  OVM в SystemVerilog, объявление  портов и export охватывает эти три элемента. Вот пример:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;ovm_nonblocking_put_port #(trans) put_port;&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Суффикс тип объекта _port, указывает, что это порт. Exports используют суффикс _export.Тип интерфейса определяется по имени между префиксом ovm_ и _port или суффиксом _export. В этом случае, это имя nonblocking_put, который относится к tlm_nonblocking_put_if.&lt;br /&gt;
Мы рассмотрели producer и consumer, каждый из которых использует блокирующие задачи для получения и отправки транзакций. Блокирующие задачи находятся в FIFO, объект, который выступает в качестве посредника между двумя компонентами, в другом случае известный как канал. Канал передает данные между двумя компонентами, и  служит синхронизатором. Ввод FIFO между двумя компонентами для буферизации и синхронизации передачи является common idiom в TLM. &lt;br /&gt;
&lt;br /&gt;
== Заключение ==&lt;br /&gt;
&lt;br /&gt;
Put, get и transport являются основными средствами для синхронизации параллельных процессов и для обмена  информацией  между эти процессы на уровне transaction-level. Эти идеи широко используются в OVM  для построения transaction-level testbenches. В следующей главе мы будем углубляться  в ОВМ, чтобы показать, как строить произвольные иерархии компонент верификации, основанных на классах , связанных с помощью transaction-level интерфейсов.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5</id>
		<title>OVM/OVM методология/Transaction-Level моделирование</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5"/>
				<updated>2013-03-13T13:25:18Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Процесс проектирования электронной системы включает в себя последовательное замещение абстрактных идей  конкретной их реализацией, пока не будет достигнуто представление, которое может быть изготовлено на кремнии. С момента появления цифровой интегральной схемы, электронные сообщества тщательно определили и кодировали абстракции, начиная с переключателей и логических элементов, для обеспечения среды  в которой работает  проект. RTL является примером среды, часто используемой для создания проектов. Есть много инструментов на основе RTL, которые делают ее удобной для  создания проекта и его верификации.&lt;br /&gt;
Однако, как только проект становится больше и сложнее, становится  более представлять их с помощью абстракции выше, чем RTL. Уровень транзакций становится популярным для создания первого представления проекта, которое может быть смоделировано и проанализировано.&lt;br /&gt;
В этой главе описаны основные понятия transaction-level моделирования (TLM). Модели  transaction-level состоят из нескольких процессов, которые обмениваются информацией посредством транзакций.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Абстракция ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
RTL моделирование использует дискретную модель времени. Связь между процессами осуществляется с помощью сетки ( цепи), и процесс активации происходит при изменении значения во входной сети ( цепи).&lt;br /&gt;
Для сравнения, transaction-level модели (моделирующей схемы) могут быть синхронизированы или не синхронизированы и использовать шину для обмена данными между процессами. Вместо того чтобы посылать отдельные биты туда и обратно, процессы общаются, посылая транзакции друг  другу посредством вызова функций.  Мир TLM включает в себя ряд моделей(моделирующих схем) вычислений с различным временем, системами связи и процессами активации моделей (моделирующей схемы). В каждом конкретном случае, содержание связей находится на более высоком уровне абстракции, чем отдельные биты. Таким образом, transaction-level модель находится на более высоком уровне абстракции (более абстрактна), чем RTL модель. Объединяя понятия абстракции и модели (моделирующей схемы) вычислений, видно, что создание абстрактных моделей означает абстрагирование времени, данных и функций. &lt;br /&gt;
&lt;br /&gt;
'''Абстракция времени.''' Время абстракция в симуляторе относится к тому, насколько внутренние состояния схемы совместимы. Модели, запущенные в управляемых событиями симуляторах (например, логических симуляторах) используют дискретное представление  времени, то есть события происходят в определенные моменты времени. События, как правило (хотя и не всегда) вызывают запущенный процесс. Чем больше событий происходит в симуляции, больше процессов вызывается, и с большим числом процессов происходит снижении скорости моделирования. Абстрагирование время уменьшает количество точек, где схема должна быть совместимой и общего числа событий и процессов, которые будут активированы. Например, в RTL модели, каждая сеть должна быть согласована после каждого изменения. В cycle-accurate абстракции, схема должна быть синхронизирована с синхросигналом, устраняя все события, что происходит между фронтами синхроимпульса. В transaction-level модели, состояния схемы должны быть синхронизированы в конце каждой операции, каждая из которых может охватывать много тактов.&lt;br /&gt;
&lt;br /&gt;
'''Абстракция данных'''. Данные относятся к объектам, передаваемым между компонентами. В RTL моделях, данные относятся к отдельным битам, которые передаются по сети между компонентами. В transaction-level моделях, данных представлены в виде транзакций, гетерогенных структур, которые содержат произвольные коллекции элементов.&lt;br /&gt;
Рассмотрим пакет в устройстве связи. На самом низком уровне детализации, пакет содержит бит начала и стоп-биты, заголовок, информацию исправления ошибок, размер полезной нагрузки, нагрузки, и завершитель (??????). В более абстрактной модели, только нагрузка и размер нагрузки могут быть необходимы. Другие  данные не нужны  для выполнения расчетов.&lt;br /&gt;
&lt;br /&gt;
'''Абстракция функций.''' Функция модели является набором всех операций, которая она делать на каждое событие. Абстрагирование функций уменьшает этот набор или заменяет его более простыми вычислениями. Например, в ALU, вы можете выбрать использование родной операции умножении поставляемой в язык моделирования, вместо кодирования  a shift-and-add алгоритма умножения. Последний может быть частью реализации, но на более высоком уровне, детали a shift-and-add - неважны. Базовые элементы, которые являются частью языка, определяет, как вы можете абстрагировать функцию. В gate-level  языке, например, можно создавать сложные поведения, начиная с логических элементов. В RTL языка, построение  поведения основывается на арифметических и логических операциях над регистрами. В TLM, вы реализуете функциональность схемы с помощью вызовов функций произвольной сложности.&lt;br /&gt;
&lt;br /&gt;
Для целей функциональной верификации, RTL самый низкий уровень абстракции, который мы должны рассмотреть. С синтезаторы могут эффективно конвертировать RTL в логические элементы, нам не нужно затрагивать  более низкий уровень детализации. &lt;br /&gt;
&lt;br /&gt;
==  Определение транзакции ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для более подробного рассмотрения TLM, мы должны сделать шаг назад и определить понятие транзакции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:61.png]]&lt;br /&gt;
&lt;br /&gt;
Это самое общее определение транзакции. Оно утверждает, что транзакция – это все, что происходит в проекте (или модуле или подсистеме проекта) между двумя точками времени. Хотя оно точное, оно является настолько общим, что не приводит к практическому применению. Более полезные определения  следующие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:62.png]]&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Транзакция — передача управления и данных между двумя элементами (компонентами)&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это аппаратно-ориентированные понятие транзакции. Когда смотришь на часть аппаратного обеспечения, вы можете легко определить элементы, между которыми передается управление или данные. В проекте с шинной архитектурой, чтение и запись на шину может быть вызвано транзакцией. В пакетной системе связи, отправка пакета представляет собой транзакцию.&lt;br /&gt;
&lt;br /&gt;
Ниже третье определение:&lt;br /&gt;
&lt;br /&gt;
[[Файл:63.png]]&lt;br /&gt;
&lt;br /&gt;
Это определение программно-ориентированного понятия транзакции. В transaction-level модели, деятельность инициируется путем вызова функции. Вызов функции содержит параметры, которые &amp;quot;посылаются&amp;quot; (передаются) вызываемой функции, а возвращаемое значение функции содержит данные, которые возвращаются вызванной функцией. Вызванная  функция может блокировать и вызвать задержку (в синхронной системе) или может вернуться (выполниться) немедленно.&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
This definition is the software-oriented notion of a transaction. In a&lt;br /&gt;
transaction-level model, activity is initiated by making function calls. The&lt;br /&gt;
function call contains parameters that are “sent” to the called function, and&lt;br /&gt;
the return value of the function contains data that is returned by the called&lt;br /&gt;
function. The called function could block and cause time to pass (in a timed&lt;br /&gt;
system) or it could return immediately.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Интерфейсы ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Прежде чем вдаваться в подробности о том, как построить transaction-level модель, мы сначала обсудим интерфейсы. Термин интерфейс используется в несколько случаях в OVM, каждый раз с немного другим смыслом. Это прискорбный факт истории, то что слово стало означать  различные вещи. В большинстве случаев значение будет понятно из контекста, в котором этот термин используется. Различных применения :&lt;br /&gt;
&lt;br /&gt;
* Интерфейс в SystemVerilog&lt;br /&gt;
* Интерфейс объекта&lt;br /&gt;
* Интерфейс DUT&lt;br /&gt;
&lt;br /&gt;
'''Интерфейс в SystemVerilog'''. SystemVerilog предоставляет конструкцию, называемую интерфейс, который является одним из основных объектов контейнер, из которого строится проект в SystemVerilog. Мы используем виртуальные интерфейсы, которые  указывают на интерфейсы для подключения module-based аппаратуры к  class-based testbenches.&lt;br /&gt;
&lt;br /&gt;
'''Интерфейс объекта.''' Общедоступные задачи и функции, доступные для объекта, образуют его интерфейс. Есть два небольших варианта этого значения интерфейса. Одним из них является прямой. Посмотрите на класс и определите, какие задачи и функций, доступны пользователю класса. Это его интерфейс. Другое значение состоит в ссылке на базовый класс, который определяет набор задач и функций, доступных для работы производного класса. Это значение интерфейса обычно используется в объектно-ориентированных языках, которые поддерживают множественное наследование, таких как C++ или Java. В этих  языках, вы можете установить требование, что производный класс определяет функциональность, наследуемую от интерфейса базового класса.&lt;br /&gt;
&lt;br /&gt;
[[Файл:64.png]]&lt;br /&gt;
&lt;br /&gt;
Интерфейс print_if определяет прототипы для функций печати.  Любой класс, который наследуется от print_if затем обязан осуществлять print()и sprint(). SystemVerilog не поддерживает множественное наследование, но он  поддерживает истинно виртуальные интерфейсы. Истинно  виртуальный интерфейс - интерфейс во втором контексте (базовый класс, который определяет набор задач и функций прототипов), который не имеет реализаций. Истинно  виртуальная версия  print_if  выглядит следующим образом в SystemVerilog:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;virtual class print_if;&lt;br /&gt;
pure virtual function void print();&lt;br /&gt;
pure virtual function string sprint();&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Даже если SystemVerilog не поддерживает множественное наследование, и OVM построено на SystemVerilog, важно понять истинно виртуальные интерфейсы и наследование интерфейсов, потому что они активно используются в OVM. В частности, TLM ports and exports являются производными от интерфейса класс tlm_if_base. &lt;br /&gt;
&lt;br /&gt;
'''DUT интерфейс.''' Часть аппаратуры, как правило, доступны через свои интерфейсы. В этом контексте, интерфейс состоит из контактов и протоколов используемых для связи с устройством. Например, устройство может иметь USB интерфейс.&lt;br /&gt;
&lt;br /&gt;
== TLM идиомы ==&lt;br /&gt;
&lt;br /&gt;
В данном разделе рассматриваются основные средства передачи транзакций между компонентами. Мы put, get, и transport формы связи транзакций. Эти примеры не используют библиотеку OVM, так как они предназначены для иллюстрации основных механизмов связи на transaction-level с минимальными затратами. В следующем разделе мы рассмотрим более полный пример, который использует OVM библиотека для связи.&lt;br /&gt;
&lt;br /&gt;
=== Put ===&lt;br /&gt;
В put  конфигурации, один компонент отправляет операции  другому компоненту. Операция называется put. Инициатором является компонентом, который инициирует передачу, а целевой компонент, который получает результат. Используя TLM номенклатуру, мы говорим, что инициатор put транзакции цели.&lt;br /&gt;
&lt;br /&gt;
[[Файл:65.png]]&lt;br /&gt;
 &lt;br /&gt;
Рисунок 3-2 показывает, что А предает операцию в пункт Б. Инициатор имеет порт, рисуется как квадрат, и цель имеет export, изображается в виде круга. Поток  управления направлен  от квадрата к кругу, то есть, А вызывает B, который содержит реализацию методов port. Стрелка показывает направление поток данных, и в этом случае, это означает, что данные будут двигаться от А до B.&lt;br /&gt;
Мы можем проиллюстрировать код для этих компонентов с producer и consumer. producer является инициатором и consumer является целью. Мы должны строить эти компоненты таким образом, что они не знали друг о друге априори. Чтобы сделать это, мы используем истинно виртуальный интерфейс для определения функции, который будет использоваться для передачи данных между инициатором и целью. Во-первых, давайте взглянем на SystemVerilog версию producer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;46 class producer;&lt;br /&gt;
47&lt;br /&gt;
48 put_if put_port;&lt;br /&gt;
49&lt;br /&gt;
50 task run();&lt;br /&gt;
51&lt;br /&gt;
52 int randval;&lt;br /&gt;
53&lt;br /&gt;
54 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
55 begin&lt;br /&gt;
56 randval = $random %100;&lt;br /&gt;
57 $display(“producer: sending %4d”, randval);&lt;br /&gt;
58 put_port.put(randval);&lt;br /&gt;
59 end&lt;br /&gt;
60&lt;br /&gt;
61 endtask&lt;br /&gt;
62&lt;br /&gt;
63 endclass : producer&lt;br /&gt;
file: 03_tlm/01_put/put.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
producer  - это класс, который  создается динамически. Он имеет два ключевых элемента, Run () задачу и put_port. Run () задача является простой задачей,  которая повторяется 10 раз и  передает 10 транзакций. Для простоты, наши операции являются целыми числами. На практике, транзакция может быть сколь угодно сложным  объектом, таким как структура или класс.&lt;br /&gt;
Чтобы передать операции, producer вызывает put() на put_port. Что такое put_port? Это не порт в традиционном смысле Verilog. Это ссылка на put_if. Что такое put_if? Put_if это виртуальный интерфейс класса расположенный между инициатором (producer) и целью (consumer).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;39 virtual class put_if;&lt;br /&gt;
40 pure virtual task put(int val);&lt;br /&gt;
41 endclass : put_if&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
put_if класс с истинно виртуальными задачами, т.е.  задачами, не имеющими реализацию. Без реализации всех своих задач и функций, виртуальный класс не может быть создан сам по себе. Он должен быть базовым классом другого класса, который создается. В нашем случае, класс, производный от истинно виртуального put_if является consumer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;68 class consumer extends put_if;&lt;br /&gt;
69 task put(int val);&lt;br /&gt;
70 $display(“consumer: receiving %4d”, val);&lt;br /&gt;
71 endtask : put&lt;br /&gt;
72 endclass : consumer&lt;br /&gt;
file: 03_tlm/01_put/put.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
consumer содержит реализацию put(); истинно виртуальные задачи определены в put_if.  Задача put() реализация принимает аргументы, переданные ей, и печатает их. put_if играет ключевую роль в соединении consumer и producer. Ссылка на него на стороне producer, которую мы называем порт, устанавливает требование, что должна быть реализация  функций и задач  интерфейса, к которому этот объект будет привязан. consumer является производным от интерфейса и, следовательно, в нем должны быть реализованы истинно виртуальные задачи, удовлетворяющие требованиям.&lt;br /&gt;
&lt;br /&gt;
Модуль верхнего уровня связывающий producer и consumer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;77 module top;&lt;br /&gt;
78&lt;br /&gt;
79 producer p;&lt;br /&gt;
80 consumer c;&lt;br /&gt;
81&lt;br /&gt;
82 initial begin&lt;br /&gt;
83 // instantiate producer and consumer&lt;br /&gt;
84 p = new();&lt;br /&gt;
85 c = new();&lt;br /&gt;
86 // connect producer and consumer&lt;br /&gt;
87 // through the put_if interface class&lt;br /&gt;
88 p.put_port = c;&lt;br /&gt;
89 p.run();&lt;br /&gt;
90 end&lt;br /&gt;
91 endmodule : top&lt;br /&gt;
file: 03_tlm/01_put/put.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что оператор присваивания:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;88 p.put_port = c;&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Он образует связь между producer и consumer. Когда new() вызывается на р, чтобы создать новый экземпляр producer, член put_port  не имеет значения. Во время выполнения произойдет ошибка, если put_port. put () вызывается до связи назначения. Назначение С к p.put_port дает  ссылку для consumer, который содержит реализацию задачи put() интерфейса.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Get ===&lt;br /&gt;
&lt;br /&gt;
Дополнением к put является get. В этом устройстве, инициатор получает транзакции от цели. Поток управления такой же, от инициатора к цели, но направление потока данных  обратное. Инициатор получает транзакции от цели. В этом случае consumer является инициатором, producer - целью. Consumer  инициирует вызов producer для получения транзакции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:66.png]]&lt;br /&gt;
 &lt;br /&gt;
Рисунок 3-3 очень похож на рисунок 3-2. Разница лишь в том, что здесь стрелка от цели к инициатору, а не наоборот. Это означает, что потоки данных от цели к инициатору. Ниже SystemVerilog consumer (инициатор).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;62 class consumer;&lt;br /&gt;
63&lt;br /&gt;
64 get_if get_port;&lt;br /&gt;
65&lt;br /&gt;
66 task run();&lt;br /&gt;
67 int randval;&lt;br /&gt;
68 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
69 begin&lt;br /&gt;
70 get_port.get(randval);&lt;br /&gt;
71 $display(“consumer: receiving %4d”, randval);&lt;br /&gt;
72 end&lt;br /&gt;
73 endtask&lt;br /&gt;
74 endclass&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
consumer имеет задачу, run(), которая выполняется  10 раз, чтобы получить 10 транзакций. Producer   как в put  примере, consumer имеет здесь порт. Кроме того, как в put  примере, порт ссылается на истинно виртуальный интерфейс, в данном случае&lt;br /&gt;
он называется get_if.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;41 virtual class get_if;&lt;br /&gt;
42 pure virtual task get(output int t);&lt;br /&gt;
43 endclass : get_if&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
get_if&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
является истинно виртуальным  интерфейс, который определяет функцию get(). Цель (producer) строится аналогично, как и цель в put  примере. Она содержит реализацию функции интерфейса. Этот producer генерирует случайное число между 0 и 99.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;48 class producer extends get_if;&lt;br /&gt;
49&lt;br /&gt;
50 task get(output int t);&lt;br /&gt;
51 int randval;&lt;br /&gt;
52 randval = $random % 100;&lt;br /&gt;
53 $display(“producer: sending %4d”, randval);&lt;br /&gt;
54 t = randval;&lt;br /&gt;
55 endtask&lt;br /&gt;
56&lt;br /&gt;
57 endclass : producer&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Связи на высшем уровне будут выглядеть очень похожими.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;79 module top;&lt;br /&gt;
80&lt;br /&gt;
81 producer p;&lt;br /&gt;
82 consumer c;&lt;br /&gt;
83&lt;br /&gt;
84 initial begin&lt;br /&gt;
85 // instantiate producer and consumer&lt;br /&gt;
86 p = new();&lt;br /&gt;
87 c = new();&lt;br /&gt;
88 // connect producer and consumer through the get_if&lt;br /&gt;
89 // interface class&lt;br /&gt;
90 c.get_port = p;&lt;br /&gt;
91 c.run();&lt;br /&gt;
92 end&lt;br /&gt;
93 endmodule : top&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После создания объектов producer и consumer, используя операцию new(),  два объекта будут соединяться с помощью связей назначения.&lt;br /&gt;
&lt;br /&gt;
=== Transport === &lt;br /&gt;
&lt;br /&gt;
Transport является двунаправленным интерфейсом. Интерфейс обеспечивает передачу транзакций от инициатора к цели и от цели обратно к инициатору. Как правило, мы используем этот механизм, чтобы смоделировать запрос-ответ протоколы. Когда речь идет о компонентах с двунаправленным интерфейсов, мы используем термины master (ведущего) и slave (ведомого), а не инициатор и цель.&lt;br /&gt;
 &lt;br /&gt;
[[Файл:67.png]]&lt;br /&gt;
&lt;br /&gt;
master (A) делает и put и get  за один вызов функции. Как мы видели в предыдущих примерах, put() и get() принимают один аргумент, аргумент они помещают или извлекают. Тем не менее, функция transport() имеет два аргумента, запрос и ответ. Она посылает запрос и возвращает  ответ. slave (B) принимает запрос и возвращает ответ.&lt;br /&gt;
&lt;br /&gt;
Давайте сначала посмотрим на истинно виртуальный интерфейс.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;37 virtual class transport_if;&lt;br /&gt;
38 pure virtual task transport(input int request,&lt;br /&gt;
39 output int response);&lt;br /&gt;
40 endclass : transport_if&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Интерфейс содержит одну функцию, transport(), которая принимает два аргумента: запрос, который передается  цели и ответ, который возвращается  инициатору. &lt;br /&gt;
&lt;br /&gt;
Master вызывает transport(), создает запрос и отправляет его slave, используя transport. Он обрабатывает ответ, который возвращается.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;45 class master;&lt;br /&gt;
46&lt;br /&gt;
47 transport_if port;&lt;br /&gt;
48&lt;br /&gt;
49 task run();&lt;br /&gt;
50&lt;br /&gt;
51 int request;&lt;br /&gt;
52 int response;&lt;br /&gt;
53&lt;br /&gt;
54 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
55 begin &lt;br /&gt;
56 request = $random % 100;&lt;br /&gt;
57 $display(“master: sending request %4d”,&lt;br /&gt;
58 request);&lt;br /&gt;
59 port.transport(request, response);&lt;br /&gt;
60 $display(“master: receiving response %4d”,&lt;br /&gt;
61 response);&lt;br /&gt;
62 end&lt;br /&gt;
63&lt;br /&gt;
64 endtask&lt;br /&gt;
65 endclass : master&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
slave реализует функцию transport(). В нашем примере, она делает некоторые тривиальные обработки запроса для создания ответа.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;70 class slave extends transport_if;&lt;br /&gt;
71&lt;br /&gt;
72 task transport(input int request, output int response);&lt;br /&gt;
73 $display(“slave: receiving request %4d”,&lt;br /&gt;
74 request);&lt;br /&gt;
75 response = -request;&lt;br /&gt;
76 $display(“slave: sending response %4d”,&lt;br /&gt;
77 response);&lt;br /&gt;
78 endtask&lt;br /&gt;
79&lt;br /&gt;
80 endclass&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Связи верхнего уровня между master и slave работают точно так же как и в примере с put и get.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;85 module top;&lt;br /&gt;
86&lt;br /&gt;
87 master m;&lt;br /&gt;
88 slave s;&lt;br /&gt;
89&lt;br /&gt;
90 initial begin&lt;br /&gt;
91 // instantiate the master and slave&lt;br /&gt;
92 m = new();&lt;br /&gt;
93 s = new();&lt;br /&gt;
94&lt;br /&gt;
95 // connect the master and slave through&lt;br /&gt;
96 // the port interface&lt;br /&gt;
97 m.port = s;&lt;br /&gt;
98 m.run();&lt;br /&gt;
99 end&lt;br /&gt;
100&lt;br /&gt;
101 endmodule : top&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Связь назначения связывает master и slave. После выполнения соединения, master может использовать соединения для прямого вызова функций slave.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Блокирующие интерфейсы против не блокирующих ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Интерфейсы мы рассмотрели ранее блокирующие. Это означает, что функции и задачи блокируют выполнение до их завершения. Им не разрешается заканчиваться неудачей.  Не существует механизма для  блокировки при аварийном завершении или иным образом изменять поток управления. Они просто ждать, пока запрос будет выполнен. В синхронизированной системе это означает, что время может пройти между вызовом и возвращением полученного результата.&lt;br /&gt;
&lt;br /&gt;
В put конфигурации, у нас есть два компонента, producer и consumer. Producer  генерирует случайное число и посылает его к consumer, используя  put(). Прежде чем вызывается put(),  нет активности в consumer. Вызов put() вызывает активность в consumer, которая выводит значение аргумента. За то время, что consumer является активным, producer ожидает. Это природа блокирующего вызова. Вызывающий должен ждать, пока вызов завершиться для возобновления выполнения.&lt;br /&gt;
&lt;br /&gt;
Теперь сравните это описание с неблокирующим вызовом. Неблокирующий вызов возвращается немедленно. Семантика неблокирующего вызова гарантирует, что вызов возвращается в том же дельта цикле, в котором он был запущен, то есть без задержки, даже на один дельта цикл.&lt;br /&gt;
&lt;br /&gt;
Истинно  виртуальный интерфейс, который соединяет неблокирующего slave  и master выглядит так же, как другие истинно виртуальные интерфейсы, которые мы видели. Значительная разница в том, что nb_get () возвращает значение статуса вместо транзакции.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;41 virtual class get_if;&lt;br /&gt;
42 pure virtual function int nb_get(output int t);&lt;br /&gt;
43 endclass : get_if&lt;br /&gt;
file: 03_tlm/04_nonblocking/nbget.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
master (consumer) должен проверить состояние, возвращаемое из nb_get (), чтобы определить, завершена ли функция успешно. Отметим также, что мы ввели время в модель. Consumer  проверяет каждый 4 нс доступность значения.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;78 class consumer; &lt;br /&gt;
79&lt;br /&gt;
80 get_if get_port;&lt;br /&gt;
81&lt;br /&gt;
82 task run();&lt;br /&gt;
83 int randval;&lt;br /&gt;
84 int ok;&lt;br /&gt;
85&lt;br /&gt;
86 for(int i=0; i&amp;lt;20; i++)&lt;br /&gt;
87 begin&lt;br /&gt;
88 #4;&lt;br /&gt;
89 if(get_port.nb_get(randval))&lt;br /&gt;
90 $display(“%t: consumer: receiving %4d”, $time,&lt;br /&gt;
randval);&lt;br /&gt;
91 else&lt;br /&gt;
92 $display(“%t: consumer: no randval”, $time);&lt;br /&gt;
93 end&lt;br /&gt;
94 endtask&lt;br /&gt;
95 endclass&lt;br /&gt;
file: 03_tlm/04_nonblocking/nbget.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
producer  организован как функция и задача. Задача будет раздвоена (порождена), чтобы запуститься как непрерывный процесс. Он генерирует новые случайные значения, что consumer будет захватывать. Тем не менее, каждая случайная величина доступна только для 2 нс из 7 нс цикла. Функция - реализация nb_get, которая возвращает значение, запускает периодически выполнение run().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;48 class producer extends get_if;&lt;br /&gt;
49&lt;br /&gt;
50 int randval = 0;&lt;br /&gt;
51 int rand_avail = 0;&lt;br /&gt;
52&lt;br /&gt;
53 function int nb_get(output int t);&lt;br /&gt;
54 if(rand_avail) begin&lt;br /&gt;
55 $display(“%t: producer: sending %4d”,&lt;br /&gt;
56 $time, randval);&lt;br /&gt;
57 t = randval;&lt;br /&gt;
58 return 1;&lt;br /&gt;
59 end&lt;br /&gt;
60 return 0;&lt;br /&gt;
61 endfunction&lt;br /&gt;
62&lt;br /&gt;
63 task run();&lt;br /&gt;
64 forever begin;&lt;br /&gt;
65 #5;&lt;br /&gt;
66 randval = $random % 100;&lt;br /&gt;
67 rand_avail = 1;&lt;br /&gt;
68 #2;&lt;br /&gt;
69 rand_avail = 0;&lt;br /&gt;
70 end&lt;br /&gt;
71 endtask&lt;br /&gt;
72&lt;br /&gt;
73 endclass : producer&lt;br /&gt;
file: 03_tlm/04_nonblocking/nbget.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Когда мы запускаем пример, мы видим, что не каждый вызов nb_get () успешен.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;4: consumer: no randval&lt;br /&gt;
8: consumer: no randval&lt;br /&gt;
12: producer: sending -99&lt;br /&gt;
12: consumer: receiving -99&lt;br /&gt;
16: consumer: no randval&lt;br /&gt;
20: producer: sending -39&lt;br /&gt;
20: consumer: receiving -39&lt;br /&gt;
24: consumer: no randval&lt;br /&gt;
28: producer: sending -9&lt;br /&gt;
28: consumer: receiving -9&lt;br /&gt;
32: consumer: no randval&lt;br /&gt;
36: consumer: no randval&lt;br /&gt;
40: producer: sending 57&lt;br /&gt;
40: consumer: receiving 57&lt;br /&gt;
44: consumer: no randval&lt;br /&gt;
48: producer: sending -71&lt;br /&gt;
48: consumer: receiving -71&lt;br /&gt;
52: consumer: no randval&lt;br /&gt;
56: producer: sending -14&lt;br /&gt;
56: consumer: receiving -14&lt;br /&gt;
60: consumer: no randval&lt;br /&gt;
64: consumer: no randval&lt;br /&gt;
68: producer: sending 29&lt;br /&gt;
68: consumer: receiving 29&lt;br /&gt;
72: consumer: no randval&lt;br /&gt;
76: producer: sending 18&lt;br /&gt;
76: consumer: receiving 18&lt;br /&gt;
80: consumer: no randval&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Блокировать get конфигурацию смог только один процесс - consumer, который постоянно обращался с запросом к producer, чтобы тот отправил новое значение. Неблокирующий  вариант состоит из двух этапов: consumer регулярно опрашивает producer, чтобы получить новое значение, и producer генерирует новые значения асинхронно по отношению к consumer. Наши неблокирующий producer генерирует доступное случайное значение каждые 7 нс. Он ждет 5 нс, а затем создает новое значение и новое значение действительно в течение 2 нс. Флаг  rand_avail устанавливается, когда  случайное значение доступно и сбрасывается в противном случае.&lt;br /&gt;
&lt;br /&gt;
Реализация nb_get () в этом примере должна проверить rand_avail  есть ли  что отправить. Если нет, то она возвращает 0, чтобы указать, что запрос не удался. Если есть что-то, то он посылает его и возвращает 1, чтобы показать успех.&lt;br /&gt;
&lt;br /&gt;
Блокирующие интерфейсы полезны для работы с двумя компонентами синхронно. Вызовы блокируются до тех пор, пока запрошенная операция выполняется, не смотря на то, сколько это потребует времени. С другой стороны, неблокирующие интерфейсы полезны при асинхронной передаче данных. Они не ждут и могут быть использованы для опроса целей, как в приведенном примере.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Изоляция компонент с помощью каналов ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В предыдущем разделе обсуждались простые механизмы для перемещения транзакций между двумя процессами. В каждом, инициатор и цель были сильно  синхронизированы вызовом транзакции интерфейса. В этом разделе мы рассмотрим случай, когда инициатор и цель менее тесно связаны, это можно сделать с помощью канала, в этом случае FIFO, для управления синхронизацией между инициатором и целью, а не полагаться на взаимную синхронизацию самих компонент. Здесь мы имеем две компоненты, инициатор А и цель B, а также FIFO, соединяющий две компоненты.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:68.png]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В предыдущих примерах, одна компонента имела  порт, а другая  export.  Компонента с портом  обращается к  компоненте с  export. Здесь оба  А и В имеют порты. Вместо того, чтобы инициатор обращался к непосредственно к  цели, теперь у нас есть  инициатор  и цель,  которые обращаются к каналу FIFO. Канал обеспечивает функции, необходимые как инициатору, так и цели.&lt;br /&gt;
&lt;br /&gt;
Инициатор использует блокирующий put() для отправки транзакций на FIFO, и цель использует блокирующий get() для получения транзакций с FIFO. FIFO хранит транзакции и служит в качестве синхронизатора. Инициатор может помещать транзакции в FIFO, пока он не заполнится . Так как инициатора использует блокировку Put (), инициатор процесса будет блокироваться, когда FIFO заполнен.  Кроме того, цель использует блокирующий get() и будет блокироваться, когда FIFO является пустым. По сути, producer в этом примере, как producer в блокирующем  put  примере,  и consumer, как consumer в блокирующем get. FIFO  replaces цели и предоставляет задачи, необходимые для удовлетворения требований интерфейса, созданные портами  producer и consumer.&lt;br /&gt;
&lt;br /&gt;
Давайте посмотрим на код. Это первый пример, который использует OVM библиотеки. OVM библиотека включает в себя FIFO, называемый tlm_fifo, который является параметризованным классом с различными интерфейсами для поддержки блокирующих и неблокирующий операций. &lt;br /&gt;
&lt;br /&gt;
В этом случае producer выглядит как producer в блокирующем пут примере. Он имеет процесс, run(), который выполняется 10 раз, генерирует 10 случайных значений и &lt;br /&gt;
отправив их к цели через put_port.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;42 class producer extends ovm_component;&lt;br /&gt;
43&lt;br /&gt;
44 ovm_blocking_put_port#(int) put_port;&lt;br /&gt;
45&lt;br /&gt;
46 function new(string name, ovm_component p = null);&lt;br /&gt;
47 super.new(name,p);&lt;br /&gt;
48 put_port = new(“put_port”, this);&lt;br /&gt;
49 endfunction&lt;br /&gt;
50&lt;br /&gt;
51 task run();&lt;br /&gt;
52&lt;br /&gt;
53 int randval;&lt;br /&gt;
54 string s;&lt;br /&gt;
55&lt;br /&gt;
56 for(int i = 0; i &amp;lt; 10; i++)&lt;br /&gt;
57 begin&lt;br /&gt;
58 randval = $random % 100;&lt;br /&gt;
59 $sformat(s, “sending %4d”, randval);&lt;br /&gt;
60 ovm_report_info(“producer”, s);&lt;br /&gt;
61 put_port.put(randval);&lt;br /&gt;
62 end&lt;br /&gt;
63 global_stop_request(); // OK, we’re done now&lt;br /&gt;
64 endtask&lt;br /&gt;
65&lt;br /&gt;
66 endclass&lt;br /&gt;
file: 03_tlm/05_fifo/fifo.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Есть две вещи, на которые необходимо обратить внимание. Во-первых, компоненты, полученные из ovm_component, которая является базовым классом в библиотеке OVM, обеспечивает основные возможности для компонент. Это позволяет компонентам подключиться к иерархии названных компонент и обеспечивает процесс управления выполнения задачи. Выполнение задачи разветвляется при запуске и может быть приостановлена или возобновлена по желанию.&lt;br /&gt;
&lt;br /&gt;
Кроме того, необходимо заметить, как put_port объявлен. В наших простых примерах выше, мы создали наш собственный истинно виртуальный интерфейс для подключения инициатора  к цели. Библиотека OVM поставляет набор портов и экспорт объектов, которые являются обертками вокруг истинно виртуальных ссылок интерфейса. Порт и экспорт объектов, которые сами по себе именные компоненты, позволяют функции connect() создавать ассоциаций между  ports and exports.  Использование модели лучше по сравнению с использованием операторов присваивания. &lt;br /&gt;
&lt;br /&gt;
Consumer не сильно отличается от consumer в блокирующем get примере.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;71 class consumer extends ovm_component;&lt;br /&gt;
72&lt;br /&gt;
73 ovm_blocking_get_port#(int) get_port;&lt;br /&gt;
74&lt;br /&gt;
75 function new(string name, ovm_component p = null);&lt;br /&gt;
76 super.new(name,p);&lt;br /&gt;
77 get_port = new(“get_port”, this);&lt;br /&gt;
78 endfunction&lt;br /&gt;
79&lt;br /&gt;
80 task run();&lt;br /&gt;
81&lt;br /&gt;
82 int val;&lt;br /&gt;
83 string s;&lt;br /&gt;
84&lt;br /&gt;
85 forever&lt;br /&gt;
86 begin&lt;br /&gt;
87 get_port.get(val);&lt;br /&gt;
88 $sformat(s, “receiving %4d”, val);&lt;br /&gt;
89 ovm_report_info(“consumer”, s);&lt;br /&gt;
90 end&lt;br /&gt;
91&lt;br /&gt;
92 endtask&lt;br /&gt;
93&lt;br /&gt;
94 endclass&lt;br /&gt;
file: 03_tlm/05_fifo/fifo.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для подключения producer, consumer и FIFO, мы используем окружающую среду. Средой служит  вершина иерархии именованных компонент, и это организует построение иерархии и  выполнения  testbench.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;99 class env extends ovm_component;&lt;br /&gt;
100 producer p;&lt;br /&gt;
101 consumer c;&lt;br /&gt;
102 tlm_fifo #(int) f;&lt;br /&gt;
103&lt;br /&gt;
104 function new(string name, ovm_component parent = null);&lt;br /&gt;
105 super.new(name, parent);&lt;br /&gt;
106 endfunction&lt;br /&gt;
107&lt;br /&gt;
108 function void build();&lt;br /&gt;
109 p = new(“producer”, this);&lt;br /&gt;
110 c = new(“consumer”, this);&lt;br /&gt;
111 f = new(“fifo”, this);&lt;br /&gt;
112 endfunction&lt;br /&gt;
113&lt;br /&gt;
114 function void connect();&lt;br /&gt;
115 p.put_port.connect(f.blocking_put_export);&lt;br /&gt;
116 c.get_port.connect(f.blocking_get_export);&lt;br /&gt;
117 endfunction&lt;br /&gt;
118&lt;br /&gt;
119 endclass&lt;br /&gt;
file: 03_tlm/05_fifo/fifo.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция connect() создает связь между портами на producer и  consumer и соответствующего экспорта на FIFO.  Run ()  отвечает за контроль выполнения   testbench.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:68.png</id>
		<title>Файл:68.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:68.png"/>
				<updated>2013-03-13T13:18:35Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5</id>
		<title>OVM/OVM методология/Transaction-Level моделирование</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5"/>
				<updated>2013-03-12T08:55:47Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Процесс проектирования электронной системы включает в себя последовательное замещение абстрактных идей  конкретной их реализацией, пока не будет достигнуто представление, которое может быть изготовлено на кремнии. С момента появления цифровой интегральной схемы, электронные сообщества тщательно определили и кодировали абстракции, начиная с переключателей и логических элементов, для обеспечения среды  в которой работает  проект. RTL является примером среды, часто используемой для создания проектов. Есть много инструментов на основе RTL, которые делают ее удобной для  создания проекта и его верификации.&lt;br /&gt;
Однако, как только проект становится больше и сложнее, становится  более представлять их с помощью абстракции выше, чем RTL. Уровень транзакций становится популярным для создания первого представления проекта, которое может быть смоделировано и проанализировано.&lt;br /&gt;
В этой главе описаны основные понятия transaction-level моделирования (TLM). Модели  transaction-level состоят из нескольких процессов, которые обмениваются информацией посредством транзакций.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Абстракция ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
RTL моделирование использует дискретную модель времени. Связь между процессами осуществляется с помощью сетки ( цепи), и процесс активации происходит при изменении значения во входной сети ( цепи).&lt;br /&gt;
Для сравнения, transaction-level модели (моделирующей схемы) могут быть синхронизированы или не синхронизированы и использовать шину для обмена данными между процессами. Вместо того чтобы посылать отдельные биты туда и обратно, процессы общаются, посылая транзакции друг  другу посредством вызова функций.  Мир TLM включает в себя ряд моделей(моделирующих схем) вычислений с различным временем, системами связи и процессами активации моделей (моделирующей схемы). В каждом конкретном случае, содержание связей находится на более высоком уровне абстракции, чем отдельные биты. Таким образом, transaction-level модель находится на более высоком уровне абстракции (более абстрактна), чем RTL модель. Объединяя понятия абстракции и модели (моделирующей схемы) вычислений, видно, что создание абстрактных моделей означает абстрагирование времени, данных и функций. &lt;br /&gt;
&lt;br /&gt;
'''Абстракция времени.''' Время абстракция в симуляторе относится к тому, насколько внутренние состояния схемы совместимы. Модели, запущенные в управляемых событиями симуляторах (например, логических симуляторах) используют дискретное представление  времени, то есть события происходят в определенные моменты времени. События, как правило (хотя и не всегда) вызывают запущенный процесс. Чем больше событий происходит в симуляции, больше процессов вызывается, и с большим числом процессов происходит снижении скорости моделирования. Абстрагирование время уменьшает количество точек, где схема должна быть совместимой и общего числа событий и процессов, которые будут активированы. Например, в RTL модели, каждая сеть должна быть согласована после каждого изменения. В cycle-accurate абстракции, схема должна быть синхронизирована с синхросигналом, устраняя все события, что происходит между фронтами синхроимпульса. В transaction-level модели, состояния схемы должны быть синхронизированы в конце каждой операции, каждая из которых может охватывать много тактов.&lt;br /&gt;
&lt;br /&gt;
'''Абстракция данных'''. Данные относятся к объектам, передаваемым между компонентами. В RTL моделях, данные относятся к отдельным битам, которые передаются по сети между компонентами. В transaction-level моделях, данных представлены в виде транзакций, гетерогенных структур, которые содержат произвольные коллекции элементов.&lt;br /&gt;
Рассмотрим пакет в устройстве связи. На самом низком уровне детализации, пакет содержит бит начала и стоп-биты, заголовок, информацию исправления ошибок, размер полезной нагрузки, нагрузки, и завершитель (??????). В более абстрактной модели, только нагрузка и размер нагрузки могут быть необходимы. Другие  данные не нужны  для выполнения расчетов.&lt;br /&gt;
&lt;br /&gt;
'''Абстракция функций.''' Функция модели является набором всех операций, которая она делать на каждое событие. Абстрагирование функций уменьшает этот набор или заменяет его более простыми вычислениями. Например, в ALU, вы можете выбрать использование родной операции умножении поставляемой в язык моделирования, вместо кодирования  a shift-and-add алгоритма умножения. Последний может быть частью реализации, но на более высоком уровне, детали a shift-and-add - неважны. Базовые элементы, которые являются частью языка, определяет, как вы можете абстрагировать функцию. В gate-level  языке, например, можно создавать сложные поведения, начиная с логических элементов. В RTL языка, построение  поведения основывается на арифметических и логических операциях над регистрами. В TLM, вы реализуете функциональность схемы с помощью вызовов функций произвольной сложности.&lt;br /&gt;
&lt;br /&gt;
Для целей функциональной верификации, RTL самый низкий уровень абстракции, который мы должны рассмотреть. С синтезаторы могут эффективно конвертировать RTL в логические элементы, нам не нужно затрагивать  более низкий уровень детализации. &lt;br /&gt;
&lt;br /&gt;
==  Определение транзакции ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для более подробного рассмотрения TLM, мы должны сделать шаг назад и определить понятие транзакции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:61.png]]&lt;br /&gt;
&lt;br /&gt;
Это самое общее определение транзакции. Оно утверждает, что транзакция – это все, что происходит в проекте (или модуле или подсистеме проекта) между двумя точками времени. Хотя оно точное, оно является настолько общим, что не приводит к практическому применению. Более полезные определения  следующие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:62.png]]&lt;br /&gt;
&lt;br /&gt;
Это аппаратно-ориентированные понятие транзакции. Когда смотришь на часть аппаратного обеспечения, вы можете легко определить сущности, между которыми передается управление или данные. В  проекте с шинной архитектурой, чтение и запись на шину может быть вызвано транзакцией. В пакетной системе связи, отправка пакета представляет собой транзакцию.&lt;br /&gt;
&lt;br /&gt;
Ниже третье определение:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:63.png]]&lt;br /&gt;
&lt;br /&gt;
Это определение программно-ориентированного понятия транзакции. В transaction-level модели, деятельность инициируется путем вызова функции. Вызов функции содержит параметры, которые &amp;quot;послали&amp;quot;  вызываемой функции, и возвращаемого значения функции. Вызванная  функция может блокировать и cause time to pass or it could return immediately.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Интерфейсы ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Прежде чем вдаваться в подробности о том, как построить transaction-level модель, мы сначала обсудим интерфейсы. Термин интерфейс используется в несколько случаях в OVM, каждый раз с немного другим смыслом. Это прискорбный факт истории, то что слово стало означать  различные вещи. В большинстве случаев значение будет понятно из контекста, в котором этот термин используется. Различных применения :&lt;br /&gt;
&lt;br /&gt;
•	Интерфейс SystemVerilog&lt;br /&gt;
&lt;br /&gt;
•	Объектный интерфейс&lt;br /&gt;
&lt;br /&gt;
•	Интерфейс DUT&lt;br /&gt;
&lt;br /&gt;
'''SystemVerilog интерфейс'''. SystemVerilog предоставляет конструкцию, называемую интерфейс, который является одним из основных объектов контейнер, из которого строится проект в SystemVerilog. Мы используем виртуальные интерфейсы, которые  указывают на интерфейсы для подключения module-based аппаратуры к  class-based testbenches.&lt;br /&gt;
&lt;br /&gt;
'''Объектный интерфейс.''' Общедоступные задачи и функции, доступные для объекта образует его интерфейс. Есть два небольших изменения этого значения интерфейс. Одним из них является прямой. Посмотрите на класс и определите, какие задачи и функций, доступны пользователю класса. Это его интерфейс. Другой вариант состоит в ссылке на базовый класс, который определяет набор задач и функций, доступных для работы производного класса. Это значение интерфейса обычно используется объектно-ориентированных языков, которые поддерживают множественное наследование, таких как C + + или java. В этих  языках, вы можете установить требование, что производный класс определяет функциональность, наследуемую от интерфейса базового класса.&lt;br /&gt;
&lt;br /&gt;
[[Файл:64.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Интерфейс print_if определяет прототипы для функций печати.  Любой класс, который наследуется от print_if затем обязан осуществлять print()и sprint(). SystemVerilog не поддерживает множественное наследование, но он  поддерживает истинно виртуальные интерфейсы. Истинно  виртуальный интерфейс - интерфейс во втором контексте (базовый класс, который определяет набор задач и функций прототипов), который не имеет реализаций. Истинно  виртуальная версия  print_if  выглядит следующим образом в SystemVerilog:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;virtual class print_if;&lt;br /&gt;
pure virtual function void print();&lt;br /&gt;
pure virtual function string sprint();&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Даже если SystemVerilog не поддерживает множественное наследование, и OVM построено на SystemVerilog, важно понять истинно виртуальные интерфейсы и наследование интерфейсов, потому что они активно используются в OVM. В частности, TLM ports and exports являются производными от интерфейса класс tlm_if_base. &lt;br /&gt;
&lt;br /&gt;
'''DUT интерфейс.''' Часть аппаратуры, как правило, доступны через свои интерфейсы. В этом контексте, интерфейс состоит из контактов и протоколов используемых для связи с устройством. Например, устройство может иметь USB интерфейс.&lt;br /&gt;
&lt;br /&gt;
== TLM идиомы ==&lt;br /&gt;
&lt;br /&gt;
В данном разделе рассматриваются основные средства передачи транзакций между компонентами. Мы put, get, и transport формы связи транзакций. Эти примеры не используют библиотеку OVM, так как они предназначены для иллюстрации основных механизмов связи на transaction-level с минимальными затратами. В следующем разделе мы рассмотрим более полный пример, который использует OVM библиотека для связи.&lt;br /&gt;
&lt;br /&gt;
=== Put ===&lt;br /&gt;
В put  конфигурации, один компонент отправляет операции  другому компоненту. Операция называется put. Инициатором является компонентом, который инициирует передачу, а целевой компонент, который получает результат. Используя TLM номенклатуру, мы говорим, что инициатор put транзакции цели.&lt;br /&gt;
&lt;br /&gt;
[[Файл:65.png]]&lt;br /&gt;
 &lt;br /&gt;
Рисунок 3-2 показывает, что А предает операцию в пункт Б. Инициатор имеет порт, рисуется как квадрат, и цель имеет export, изображается в виде круга. Поток  управления направлен  от квадрата к кругу, то есть, А вызывает B, который содержит реализацию методов port. Стрелка показывает направление поток данных, и в этом случае, это означает, что данные будут двигаться от А до B.&lt;br /&gt;
Мы можем проиллюстрировать код для этих компонентов с producer и consumer. producer является инициатором и consumer является целью. Мы должны строить эти компоненты таким образом, что они не знали друг о друге априори. Чтобы сделать это, мы используем истинно виртуальный интерфейс для определения функции, который будет использоваться для передачи данных между инициатором и целью. Во-первых, давайте взглянем на SystemVerilog версию producer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;46 class producer;&lt;br /&gt;
47&lt;br /&gt;
48 put_if put_port;&lt;br /&gt;
49&lt;br /&gt;
50 task run();&lt;br /&gt;
51&lt;br /&gt;
52 int randval;&lt;br /&gt;
53&lt;br /&gt;
54 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
55 begin&lt;br /&gt;
56 randval = $random %100;&lt;br /&gt;
57 $display(“producer: sending %4d”, randval);&lt;br /&gt;
58 put_port.put(randval);&lt;br /&gt;
59 end&lt;br /&gt;
60&lt;br /&gt;
61 endtask&lt;br /&gt;
62&lt;br /&gt;
63 endclass : producer&lt;br /&gt;
file: 03_tlm/01_put/put.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
producer  - это класс, который  создается динамически. Он имеет два ключевых элемента, Run () задачу и put_port. Run () задача является простой задачей,  которая повторяется 10 раз и  передает 10 транзакций. Для простоты, наши операции являются целыми числами. На практике, транзакция может быть сколь угодно сложным  объектом, таким как структура или класс.&lt;br /&gt;
Чтобы передать операции, producer вызывает put() на put_port. Что такое put_port? Это не порт в традиционном смысле Verilog. Это ссылка на put_if. Что такое put_if? Put_if это виртуальный интерфейс класса расположенный между инициатором (producer) и целью (consumer).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;39 virtual class put_if;&lt;br /&gt;
40 pure virtual task put(int val);&lt;br /&gt;
41 endclass : put_if&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
put_if класс с истинно виртуальными задачами, т.е.  задачами, не имеющими реализацию. Без реализации всех своих задач и функций, виртуальный класс не может быть создан сам по себе. Он должен быть базовым классом другого класса, который создается. В нашем случае, класс, производный от истинно виртуального put_if является consumer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;68 class consumer extends put_if;&lt;br /&gt;
69 task put(int val);&lt;br /&gt;
70 $display(“consumer: receiving %4d”, val);&lt;br /&gt;
71 endtask : put&lt;br /&gt;
72 endclass : consumer&lt;br /&gt;
file: 03_tlm/01_put/put.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
consumer содержит реализацию put(); истинно виртуальные задачи определены в put_if.  Задача put() реализация принимает аргументы, переданные ей, и печатает их. put_if играет ключевую роль в соединении consumer и producer. Ссылка на него на стороне producer, которую мы называем порт, устанавливает требование, что должна быть реализация  функций и задач  интерфейса, к которому этот объект будет привязан. consumer является производным от интерфейса и, следовательно, в нем должны быть реализованы истинно виртуальные задачи, удовлетворяющие требованиям.&lt;br /&gt;
&lt;br /&gt;
Модуль верхнего уровня связывающий producer и consumer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;77 module top;&lt;br /&gt;
78&lt;br /&gt;
79 producer p;&lt;br /&gt;
80 consumer c;&lt;br /&gt;
81&lt;br /&gt;
82 initial begin&lt;br /&gt;
83 // instantiate producer and consumer&lt;br /&gt;
84 p = new();&lt;br /&gt;
85 c = new();&lt;br /&gt;
86 // connect producer and consumer&lt;br /&gt;
87 // through the put_if interface class&lt;br /&gt;
88 p.put_port = c;&lt;br /&gt;
89 p.run();&lt;br /&gt;
90 end&lt;br /&gt;
91 endmodule : top&lt;br /&gt;
file: 03_tlm/01_put/put.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что оператор присваивания:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;88 p.put_port = c;&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Он образует связь между producer и consumer. Когда new() вызывается на р, чтобы создать новый экземпляр producer, член put_port  не имеет значения. Во время выполнения произойдет ошибка, если put_port. put () вызывается до связи назначения. Назначение С к p.put_port дает  ссылку для consumer, который содержит реализацию задачи put() интерфейса.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Get ===&lt;br /&gt;
&lt;br /&gt;
Дополнением к put является get. В этом устройстве, инициатор получает транзакции от цели. Поток управления такой же, от инициатора к цели, но направление потока данных  обратное. Инициатор получает транзакции от цели. В этом случае consumer является инициатором, producer - целью. Consumer  инициирует вызов producer для получения транзакции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:66.png]]&lt;br /&gt;
 &lt;br /&gt;
Рисунок 3-3 очень похож на рисунок 3-2. Разница лишь в том, что здесь стрелка от цели к инициатору, а не наоборот. Это означает, что потоки данных от цели к инициатору. Ниже SystemVerilog consumer (инициатор).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;62 class consumer;&lt;br /&gt;
63&lt;br /&gt;
64 get_if get_port;&lt;br /&gt;
65&lt;br /&gt;
66 task run();&lt;br /&gt;
67 int randval;&lt;br /&gt;
68 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
69 begin&lt;br /&gt;
70 get_port.get(randval);&lt;br /&gt;
71 $display(“consumer: receiving %4d”, randval);&lt;br /&gt;
72 end&lt;br /&gt;
73 endtask&lt;br /&gt;
74 endclass&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
consumer имеет задачу, run(), которая выполняется  10 раз, чтобы получить 10 транзакций. Producer   как в put  примере, consumer имеет здесь порт. Кроме того, как в put  примере, порт ссылается на истинно виртуальный интерфейс, в данном случае&lt;br /&gt;
он называется get_if.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;41 virtual class get_if;&lt;br /&gt;
42 pure virtual task get(output int t);&lt;br /&gt;
43 endclass : get_if&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
get_if&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
является истинно виртуальным  интерфейс, который определяет функцию get(). Цель (producer) строится аналогично, как и цель в put  примере. Она содержит реализацию функции интерфейса. Этот producer генерирует случайное число между 0 и 99.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;48 class producer extends get_if;&lt;br /&gt;
49&lt;br /&gt;
50 task get(output int t);&lt;br /&gt;
51 int randval;&lt;br /&gt;
52 randval = $random % 100;&lt;br /&gt;
53 $display(“producer: sending %4d”, randval);&lt;br /&gt;
54 t = randval;&lt;br /&gt;
55 endtask&lt;br /&gt;
56&lt;br /&gt;
57 endclass : producer&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Связи на высшем уровне будут выглядеть очень похожими.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;79 module top;&lt;br /&gt;
80&lt;br /&gt;
81 producer p;&lt;br /&gt;
82 consumer c;&lt;br /&gt;
83&lt;br /&gt;
84 initial begin&lt;br /&gt;
85 // instantiate producer and consumer&lt;br /&gt;
86 p = new();&lt;br /&gt;
87 c = new();&lt;br /&gt;
88 // connect producer and consumer through the get_if&lt;br /&gt;
89 // interface class&lt;br /&gt;
90 c.get_port = p;&lt;br /&gt;
91 c.run();&lt;br /&gt;
92 end&lt;br /&gt;
93 endmodule : top&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После создания объектов producer и consumer, используя операцию new(),  два объекта будут соединяться с помощью связей назначения.&lt;br /&gt;
&lt;br /&gt;
=== Transport === &lt;br /&gt;
&lt;br /&gt;
Transport является двунаправленным интерфейсом. Интерфейс обеспечивает передачу транзакций от инициатора к цели и от цели обратно к инициатору. Как правило, мы используем этот механизм, чтобы смоделировать запрос-ответ протоколы. Когда речь идет о компонентах с двунаправленным интерфейсов, мы используем термины master (ведущего) и slave (ведомого), а не инициатор и цель.&lt;br /&gt;
 &lt;br /&gt;
[[Файл:67.png]]&lt;br /&gt;
&lt;br /&gt;
master (A) делает и put и get  за один вызов функции. Как мы видели в предыдущих примерах, put() и get() принимают один аргумент, аргумент они помещают или извлекают. Тем не менее, функция transport() имеет два аргумента, запрос и ответ. Она посылает запрос и возвращает  ответ. slave (B) принимает запрос и возвращает ответ.&lt;br /&gt;
&lt;br /&gt;
Давайте сначала посмотрим на истинно виртуальный интерфейс.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;37 virtual class transport_if;&lt;br /&gt;
38 pure virtual task transport(input int request,&lt;br /&gt;
39 output int response);&lt;br /&gt;
40 endclass : transport_if&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Интерфейс содержит одну функцию, transport(), которая принимает два аргумента: запрос, который передается  цели и ответ, который возвращается  инициатору. &lt;br /&gt;
&lt;br /&gt;
Master вызывает transport(), создает запрос и отправляет его slave, используя transport. Он обрабатывает ответ, который возвращается.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;45 class master;&lt;br /&gt;
46&lt;br /&gt;
47 transport_if port;&lt;br /&gt;
48&lt;br /&gt;
49 task run();&lt;br /&gt;
50&lt;br /&gt;
51 int request;&lt;br /&gt;
52 int response;&lt;br /&gt;
53&lt;br /&gt;
54 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
55 begin &lt;br /&gt;
56 request = $random % 100;&lt;br /&gt;
57 $display(“master: sending request %4d”,&lt;br /&gt;
58 request);&lt;br /&gt;
59 port.transport(request, response);&lt;br /&gt;
60 $display(“master: receiving response %4d”,&lt;br /&gt;
61 response);&lt;br /&gt;
62 end&lt;br /&gt;
63&lt;br /&gt;
64 endtask&lt;br /&gt;
65 endclass : master&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
slave реализует функцию transport(). В нашем примере, она делает некоторые тривиальные обработки запроса для создания ответа.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;70 class slave extends transport_if;&lt;br /&gt;
71&lt;br /&gt;
72 task transport(input int request, output int response);&lt;br /&gt;
73 $display(“slave: receiving request %4d”,&lt;br /&gt;
74 request);&lt;br /&gt;
75 response = -request;&lt;br /&gt;
76 $display(“slave: sending response %4d”,&lt;br /&gt;
77 response);&lt;br /&gt;
78 endtask&lt;br /&gt;
79&lt;br /&gt;
80 endclass&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Связи верхнего уровня между master и slave работают точно так же как и в примере с put и get.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;85 module top;&lt;br /&gt;
86&lt;br /&gt;
87 master m;&lt;br /&gt;
88 slave s;&lt;br /&gt;
89&lt;br /&gt;
90 initial begin&lt;br /&gt;
91 // instantiate the master and slave&lt;br /&gt;
92 m = new();&lt;br /&gt;
93 s = new();&lt;br /&gt;
94&lt;br /&gt;
95 // connect the master and slave through&lt;br /&gt;
96 // the port interface&lt;br /&gt;
97 m.port = s;&lt;br /&gt;
98 m.run();&lt;br /&gt;
99 end&lt;br /&gt;
100&lt;br /&gt;
101 endmodule : top&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Связь назначения связывает master и slave. После выполнения соединения, master может использовать соединения для прямого вызова функций slave.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Блокирующие интерфейсы против не блокирующих ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Интерфейсы мы рассмотрели ранее блокирующие. Это означает, что функции и задачи блокируют выполнение до их завершения. Им не разрешается заканчиваться неудачей.  Не существует механизма для  блокировки при аварийном завершении или иным образом изменять поток управления. Они просто ждать, пока запрос будет выполнен. В синхронизированной системе это означает, что время может пройти между вызовом и возвращением полученного результата.&lt;br /&gt;
&lt;br /&gt;
В put конфигурации, у нас есть два компонента, producer и consumer. Producer  генерирует случайное число и посылает его к consumer, используя  put(). Прежде чем вызывается put(),  нет активности в consumer. Вызов put() вызывает активность в consumer, которая выводит значение аргумента. За то время, что consumer является активным, producer ожидает. Это природа блокирующего вызова. Вызывающий должен ждать, пока вызов завершиться для возобновления выполнения.&lt;br /&gt;
&lt;br /&gt;
Теперь сравните это описание с неблокирующим вызовом. Неблокирующий вызов возвращается немедленно. Семантика неблокирующего вызова гарантирует, что вызов возвращается в том же дельта цикле, в котором он был запущен, то есть без задержки, даже на один дельта цикл.&lt;br /&gt;
&lt;br /&gt;
Истинно  виртуальный интерфейс, который соединяет неблокирующего slave  и master выглядит так же, как другие истинно виртуальные интерфейсы, которые мы видели. Значительная разница в том, что nb_get () возвращает значение статуса вместо транзакции.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;41 virtual class get_if;&lt;br /&gt;
42 pure virtual function int nb_get(output int t);&lt;br /&gt;
43 endclass : get_if&lt;br /&gt;
file: 03_tlm/04_nonblocking/nbget.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
master (consumer) должен проверить состояние, возвращаемое из nb_get (), чтобы определить, завершена ли функция успешно. Отметим также, что мы ввели время в модель. Consumer  проверяет каждый 4 нс доступность значения.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;78 class consumer; &lt;br /&gt;
79&lt;br /&gt;
80 get_if get_port;&lt;br /&gt;
81&lt;br /&gt;
82 task run();&lt;br /&gt;
83 int randval;&lt;br /&gt;
84 int ok;&lt;br /&gt;
85&lt;br /&gt;
86 for(int i=0; i&amp;lt;20; i++)&lt;br /&gt;
87 begin&lt;br /&gt;
88 #4;&lt;br /&gt;
89 if(get_port.nb_get(randval))&lt;br /&gt;
90 $display(“%t: consumer: receiving %4d”, $time,&lt;br /&gt;
randval);&lt;br /&gt;
91 else&lt;br /&gt;
92 $display(“%t: consumer: no randval”, $time);&lt;br /&gt;
93 end&lt;br /&gt;
94 endtask&lt;br /&gt;
95 endclass&lt;br /&gt;
file: 03_tlm/04_nonblocking/nbget.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
producer  организован как функция и задача. Задача будет раздвоена (порождена), чтобы запуститься как непрерывный процесс. Он генерирует новые случайные значения, что consumer будет захватывать. Тем не менее, каждая случайная величина доступна только для 2 нс из 7 нс цикла. Функция - реализация nb_get, которая возвращает значение, запускает периодически выполнение run().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;48 class producer extends get_if;&lt;br /&gt;
49&lt;br /&gt;
50 int randval = 0;&lt;br /&gt;
51 int rand_avail = 0;&lt;br /&gt;
52&lt;br /&gt;
53 function int nb_get(output int t);&lt;br /&gt;
54 if(rand_avail) begin&lt;br /&gt;
55 $display(“%t: producer: sending %4d”,&lt;br /&gt;
56 $time, randval);&lt;br /&gt;
57 t = randval;&lt;br /&gt;
58 return 1;&lt;br /&gt;
59 end&lt;br /&gt;
60 return 0;&lt;br /&gt;
61 endfunction&lt;br /&gt;
62&lt;br /&gt;
63 task run();&lt;br /&gt;
64 forever begin;&lt;br /&gt;
65 #5;&lt;br /&gt;
66 randval = $random % 100;&lt;br /&gt;
67 rand_avail = 1;&lt;br /&gt;
68 #2;&lt;br /&gt;
69 rand_avail = 0;&lt;br /&gt;
70 end&lt;br /&gt;
71 endtask&lt;br /&gt;
72&lt;br /&gt;
73 endclass : producer&lt;br /&gt;
file: 03_tlm/04_nonblocking/nbget.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Когда мы запускаем пример, мы видим, что не каждый вызов nb_get () успешен.&lt;br /&gt;
&lt;br /&gt;
4: consumer: no randval&lt;br /&gt;
&lt;br /&gt;
8: consumer: no randval&lt;br /&gt;
&lt;br /&gt;
12: producer: sending -99&lt;br /&gt;
&lt;br /&gt;
12: consumer: receiving -99&lt;br /&gt;
&lt;br /&gt;
16: consumer: no randval&lt;br /&gt;
&lt;br /&gt;
20: producer: sending -39&lt;br /&gt;
&lt;br /&gt;
20: consumer: receiving -39&lt;br /&gt;
&lt;br /&gt;
24: consumer: no randval&lt;br /&gt;
&lt;br /&gt;
28: producer: sending -9&lt;br /&gt;
&lt;br /&gt;
28: consumer: receiving -9&lt;br /&gt;
&lt;br /&gt;
32: consumer: no randval&lt;br /&gt;
&lt;br /&gt;
36: consumer: no randval&lt;br /&gt;
&lt;br /&gt;
40: producer: sending 57&lt;br /&gt;
&lt;br /&gt;
40: consumer: receiving 57&lt;br /&gt;
&lt;br /&gt;
44: consumer: no randval&lt;br /&gt;
&lt;br /&gt;
48: producer: sending -71&lt;br /&gt;
&lt;br /&gt;
48: consumer: receiving -71&lt;br /&gt;
&lt;br /&gt;
52: consumer: no randval&lt;br /&gt;
&lt;br /&gt;
56: producer: sending -14&lt;br /&gt;
&lt;br /&gt;
56: consumer: receiving -14&lt;br /&gt;
&lt;br /&gt;
60: consumer: no randval&lt;br /&gt;
&lt;br /&gt;
64: consumer: no randval&lt;br /&gt;
&lt;br /&gt;
68: producer: sending 29&lt;br /&gt;
&lt;br /&gt;
68: consumer: receiving 29&lt;br /&gt;
&lt;br /&gt;
72: consumer: no randval&lt;br /&gt;
&lt;br /&gt;
76: producer: sending 18&lt;br /&gt;
&lt;br /&gt;
76: consumer: receiving 18&lt;br /&gt;
&lt;br /&gt;
80: consumer: no randval&lt;br /&gt;
&lt;br /&gt;
Блокировать get конфигурацию смог только один процесс - consumer, который постоянно обращался с запросом к producer, чтобы тот отправил новое значение. Неблокирующий  вариант состоит из двух этапов: consumer регулярно опрашивает producer, чтобы получить новое значение, и producer генерирует новые значения асинхронно по отношению к consumer. Наши неблокирующий producer генерирует доступное случайное значение каждые 7 нс. Он ждет 5 нс, а затем создает новое значение и новое значение действительно в течение 2 нс. Флаг  rand_avail устанавливается, когда  случайное значение доступно и сбрасывается в противном случае.&lt;br /&gt;
&lt;br /&gt;
Реализация nb_get () в этом примере должна проверить rand_avail  есть ли  что отправить. Если нет, то она возвращает 0, чтобы указать, что запрос не удался. Если есть что-то, то он посылает его и возвращает 1, чтобы показать успех.&lt;br /&gt;
&lt;br /&gt;
Блокирующие интерфейсы полезны для работы с двумя компонентами синхронно. Вызовы блокируются до тех пор, пока запрошенная операция выполниться, не смотря на то, сколько это может занять. С другой стороны, неблокирующие интерфейсы полезны для использования в асинхронном режиме. Они не ждут и могут быть использованы для опроса цели, как в приведенном примере.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5</id>
		<title>OVM/OVM методология/Transaction-Level моделирование</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5"/>
				<updated>2013-03-12T07:51:19Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Процесс проектирования электронной системы включает в себя последовательное замещение абстрактных идей  конкретной их реализацией, пока не будет достигнуто представление, которое может быть изготовлено на кремнии. С момента появления цифровой интегральной схемы, электронные сообщества тщательно определили и кодировали абстракции, начиная с переключателей и логических элементов, для обеспечения среды  в которой работает  проект. RTL является примером среды, часто используемой для создания проектов. Есть много инструментов на основе RTL, которые делают ее удобной для  создания проекта и его верификации.&lt;br /&gt;
Однако, как только проект становится больше и сложнее, становится  более представлять их с помощью абстракции выше, чем RTL. Уровень транзакций становится популярным для создания первого представления проекта, которое может быть смоделировано и проанализировано.&lt;br /&gt;
В этой главе описаны основные понятия transaction-level моделирования (TLM). Модели  transaction-level состоят из нескольких процессов, которые обмениваются информацией посредством транзакций.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Абстракция ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
RTL моделирование использует дискретную модель времени. Связь между процессами осуществляется с помощью сетки ( цепи), и процесс активации происходит при изменении значения во входной сети ( цепи).&lt;br /&gt;
Для сравнения, transaction-level модели (моделирующей схемы) могут быть синхронизированы или не синхронизированы и использовать шину для обмена данными между процессами. Вместо того чтобы посылать отдельные биты туда и обратно, процессы общаются, посылая транзакции друг  другу посредством вызова функций.  Мир TLM включает в себя ряд моделей(моделирующих схем) вычислений с различным временем, системами связи и процессами активации моделей (моделирующей схемы). В каждом конкретном случае, содержание связей находится на более высоком уровне абстракции, чем отдельные биты. Таким образом, transaction-level модель находится на более высоком уровне абстракции (более абстрактна), чем RTL модель. Объединяя понятия абстракции и модели (моделирующей схемы) вычислений, видно, что создание абстрактных моделей означает абстрагирование времени, данных и функций. &lt;br /&gt;
&lt;br /&gt;
'''Абстракция времени.''' Время абстракция в симуляторе относится к тому, насколько внутренние состояния схемы совместимы. Модели, запущенные в управляемых событиями симуляторах (например, логических симуляторах) используют дискретное представление  времени, то есть события происходят в определенные моменты времени. События, как правило (хотя и не всегда) вызывают запущенный процесс. Чем больше событий происходит в симуляции, больше процессов вызывается, и с большим числом процессов происходит снижении скорости моделирования. Абстрагирование время уменьшает количество точек, где схема должна быть совместимой и общего числа событий и процессов, которые будут активированы. Например, в RTL модели, каждая сеть должна быть согласована после каждого изменения. В cycle-accurate абстракции, схема должна быть синхронизирована с синхросигналом, устраняя все события, что происходит между фронтами синхроимпульса. В transaction-level модели, состояния схемы должны быть синхронизированы в конце каждой операции, каждая из которых может охватывать много тактов.&lt;br /&gt;
&lt;br /&gt;
'''Абстракция данных'''. Данные относятся к объектам, передаваемым между компонентами. В RTL моделях, данные относятся к отдельным битам, которые передаются по сети между компонентами. В transaction-level моделях, данных представлены в виде транзакций, гетерогенных структур, которые содержат произвольные коллекции элементов.&lt;br /&gt;
Рассмотрим пакет в устройстве связи. На самом низком уровне детализации, пакет содержит бит начала и стоп-биты, заголовок, информацию исправления ошибок, размер полезной нагрузки, нагрузки, и завершитель (??????). В более абстрактной модели, только нагрузка и размер нагрузки могут быть необходимы. Другие  данные не нужны  для выполнения расчетов.&lt;br /&gt;
&lt;br /&gt;
'''Абстракция функций.''' Функция модели является набором всех операций, которая она делать на каждое событие. Абстрагирование функций уменьшает этот набор или заменяет его более простыми вычислениями. Например, в ALU, вы можете выбрать использование родной операции умножении поставляемой в язык моделирования, вместо кодирования  a shift-and-add алгоритма умножения. Последний может быть частью реализации, но на более высоком уровне, детали a shift-and-add - неважны. Базовые элементы, которые являются частью языка, определяет, как вы можете абстрагировать функцию. В gate-level  языке, например, можно создавать сложные поведения, начиная с логических элементов. В RTL языка, построение  поведения основывается на арифметических и логических операциях над регистрами. В TLM, вы реализуете функциональность схемы с помощью вызовов функций произвольной сложности.&lt;br /&gt;
&lt;br /&gt;
Для целей функциональной верификации, RTL самый низкий уровень абстракции, который мы должны рассмотреть. С синтезаторы могут эффективно конвертировать RTL в логические элементы, нам не нужно затрагивать  более низкий уровень детализации. &lt;br /&gt;
&lt;br /&gt;
==  Определение транзакции ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для более подробного рассмотрения TLM, мы должны сделать шаг назад и определить понятие транзакции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:61.png]]&lt;br /&gt;
&lt;br /&gt;
Это самое общее определение транзакции. Оно утверждает, что транзакция – это все, что происходит в проекте (или модуле или подсистеме проекта) между двумя точками времени. Хотя оно точное, оно является настолько общим, что не приводит к практическому применению. Более полезные определения  следующие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:62.png]]&lt;br /&gt;
&lt;br /&gt;
Это аппаратно-ориентированные понятие транзакции. Когда смотришь на часть аппаратного обеспечения, вы можете легко определить сущности, между которыми передается управление или данные. В  проекте с шинной архитектурой, чтение и запись на шину может быть вызвано транзакцией. В пакетной системе связи, отправка пакета представляет собой транзакцию.&lt;br /&gt;
&lt;br /&gt;
Ниже третье определение:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:63.png]]&lt;br /&gt;
&lt;br /&gt;
Это определение программно-ориентированного понятия транзакции. В transaction-level модели, деятельность инициируется путем вызова функции. Вызов функции содержит параметры, которые &amp;quot;послали&amp;quot;  вызываемой функции, и возвращаемого значения функции. Вызванная  функция может блокировать и cause time to pass or it could return immediately.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Интерфейсы ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Прежде чем вдаваться в подробности о том, как построить transaction-level модель, мы сначала обсудим интерфейсы. Термин интерфейс используется в несколько случаях в OVM, каждый раз с немного другим смыслом. Это прискорбный факт истории, то что слово стало означать  различные вещи. В большинстве случаев значение будет понятно из контекста, в котором этот термин используется. Различных применения :&lt;br /&gt;
&lt;br /&gt;
•	Интерфейс SystemVerilog&lt;br /&gt;
&lt;br /&gt;
•	Объектный интерфейс&lt;br /&gt;
&lt;br /&gt;
•	Интерфейс DUT&lt;br /&gt;
&lt;br /&gt;
'''SystemVerilog интерфейс'''. SystemVerilog предоставляет конструкцию, называемую интерфейс, который является одним из основных объектов контейнер, из которого строится проект в SystemVerilog. Мы используем виртуальные интерфейсы, которые  указывают на интерфейсы для подключения module-based аппаратуры к  class-based testbenches.&lt;br /&gt;
&lt;br /&gt;
'''Объектный интерфейс.''' Общедоступные задачи и функции, доступные для объекта образует его интерфейс. Есть два небольших изменения этого значения интерфейс. Одним из них является прямой. Посмотрите на класс и определите, какие задачи и функций, доступны пользователю класса. Это его интерфейс. Другой вариант состоит в ссылке на базовый класс, который определяет набор задач и функций, доступных для работы производного класса. Это значение интерфейса обычно используется объектно-ориентированных языков, которые поддерживают множественное наследование, таких как C + + или java. В этих  языках, вы можете установить требование, что производный класс определяет функциональность, наследуемую от интерфейса базового класса.&lt;br /&gt;
&lt;br /&gt;
[[Файл:64.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Интерфейс print_if определяет прототипы для функций печати.  Любой класс, который наследуется от print_if затем обязан осуществлять print()и sprint(). SystemVerilog не поддерживает множественное наследование, но он  поддерживает истинно виртуальные интерфейсы. Истинно  виртуальный интерфейс - интерфейс во втором контексте (базовый класс, который определяет набор задач и функций прототипов), который не имеет реализаций. Истинно  виртуальная версия  print_if  выглядит следующим образом в SystemVerilog:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;virtual class print_if;&lt;br /&gt;
pure virtual function void print();&lt;br /&gt;
pure virtual function string sprint();&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Даже если SystemVerilog не поддерживает множественное наследование, и OVM построено на SystemVerilog, важно понять истинно виртуальные интерфейсы и наследование интерфейсов, потому что они активно используются в OVM. В частности, TLM ports and exports являются производными от интерфейса класс tlm_if_base. &lt;br /&gt;
&lt;br /&gt;
'''DUT интерфейс.''' Часть аппаратуры, как правило, доступны через свои интерфейсы. В этом контексте, интерфейс состоит из контактов и протоколов используемых для связи с устройством. Например, устройство может иметь USB интерфейс.&lt;br /&gt;
&lt;br /&gt;
== TLM идиомы ==&lt;br /&gt;
&lt;br /&gt;
В данном разделе рассматриваются основные средства передачи транзакций между компонентами. Мы put, get, и transport формы связи транзакций. Эти примеры не используют библиотеку OVM, так как они предназначены для иллюстрации основных механизмов связи на transaction-level с минимальными затратами. В следующем разделе мы рассмотрим более полный пример, который использует OVM библиотека для связи.&lt;br /&gt;
&lt;br /&gt;
=== Put ===&lt;br /&gt;
В put  конфигурации, один компонент отправляет операции  другому компоненту. Операция называется put. Инициатором является компонентом, который инициирует передачу, а целевой компонент, который получает результат. Используя TLM номенклатуру, мы говорим, что инициатор put транзакции цели.&lt;br /&gt;
&lt;br /&gt;
[[Файл:65.png]]&lt;br /&gt;
 &lt;br /&gt;
Рисунок 3-2 показывает, что А предает операцию в пункт Б. Инициатор имеет порт, рисуется как квадрат, и цель имеет export, изображается в виде круга. Поток  управления направлен  от квадрата к кругу, то есть, А вызывает B, который содержит реализацию методов port. Стрелка показывает направление поток данных, и в этом случае, это означает, что данные будут двигаться от А до B.&lt;br /&gt;
Мы можем проиллюстрировать код для этих компонентов с producer и consumer. producer является инициатором и consumer является целью. Мы должны строить эти компоненты таким образом, что они не знали друг о друге априори. Чтобы сделать это, мы используем истинно виртуальный интерфейс для определения функции, который будет использоваться для передачи данных между инициатором и целью. Во-первых, давайте взглянем на SystemVerilog версию producer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;46 class producer;&lt;br /&gt;
47&lt;br /&gt;
48 put_if put_port;&lt;br /&gt;
49&lt;br /&gt;
50 task run();&lt;br /&gt;
51&lt;br /&gt;
52 int randval;&lt;br /&gt;
53&lt;br /&gt;
54 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
55 begin&lt;br /&gt;
56 randval = $random %100;&lt;br /&gt;
57 $display(“producer: sending %4d”, randval);&lt;br /&gt;
58 put_port.put(randval);&lt;br /&gt;
59 end&lt;br /&gt;
60&lt;br /&gt;
61 endtask&lt;br /&gt;
62&lt;br /&gt;
63 endclass : producer&lt;br /&gt;
file: 03_tlm/01_put/put.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
producer  - это класс, который  создается динамически. Он имеет два ключевых элемента, Run () задачу и put_port. Run () задача является простой задачей,  которая повторяется 10 раз и  передает 10 транзакций. Для простоты, наши операции являются целыми числами. На практике, транзакция может быть сколь угодно сложным  объектом, таким как структура или класс.&lt;br /&gt;
Чтобы передать операции, producer вызывает put() на put_port. Что такое put_port? Это не порт в традиционном смысле Verilog. Это ссылка на put_if. Что такое put_if? Put_if это виртуальный интерфейс класса расположенный между инициатором (producer) и целью (consumer).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;39 virtual class put_if;&lt;br /&gt;
40 pure virtual task put(int val);&lt;br /&gt;
41 endclass : put_if&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
put_if класс с истинно виртуальными задачами, т.е.  задачами, не имеющими реализацию. Без реализации всех своих задач и функций, виртуальный класс не может быть создан сам по себе. Он должен быть базовым классом другого класса, который создается. В нашем случае, класс, производный от истинно виртуального put_if является consumer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;68 class consumer extends put_if;&lt;br /&gt;
69 task put(int val);&lt;br /&gt;
70 $display(“consumer: receiving %4d”, val);&lt;br /&gt;
71 endtask : put&lt;br /&gt;
72 endclass : consumer&lt;br /&gt;
file: 03_tlm/01_put/put.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
consumer содержит реализацию put(); истинно виртуальные задачи определены в put_if.  Задача put() реализация принимает аргументы, переданные ей, и печатает их. put_if играет ключевую роль в соединении consumer и producer. Ссылка на него на стороне producer, которую мы называем порт, устанавливает требование, что должна быть реализация  функций и задач  интерфейса, к которому этот объект будет привязан. consumer является производным от интерфейса и, следовательно, в нем должны быть реализованы истинно виртуальные задачи, удовлетворяющие требованиям.&lt;br /&gt;
&lt;br /&gt;
Модуль верхнего уровня связывающий producer и consumer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;77 module top;&lt;br /&gt;
78&lt;br /&gt;
79 producer p;&lt;br /&gt;
80 consumer c;&lt;br /&gt;
81&lt;br /&gt;
82 initial begin&lt;br /&gt;
83 // instantiate producer and consumer&lt;br /&gt;
84 p = new();&lt;br /&gt;
85 c = new();&lt;br /&gt;
86 // connect producer and consumer&lt;br /&gt;
87 // through the put_if interface class&lt;br /&gt;
88 p.put_port = c;&lt;br /&gt;
89 p.run();&lt;br /&gt;
90 end&lt;br /&gt;
91 endmodule : top&lt;br /&gt;
file: 03_tlm/01_put/put.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что оператор присваивания:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;88 p.put_port = c;&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Он образует связь между producer и consumer. Когда new() вызывается на р, чтобы создать новый экземпляр producer, член put_port  не имеет значения. Во время выполнения произойдет ошибка, если put_port. put () вызывается до связи назначения. Назначение С к p.put_port дает  ссылку для consumer, который содержит реализацию задачи put() интерфейса.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Get ===&lt;br /&gt;
&lt;br /&gt;
Дополнением к put является get. В этом устройстве, инициатор получает транзакции от цели. Поток управления такой же, от инициатора к цели, но направление потока данных  обратное. Инициатор получает транзакции от цели. В этом случае consumer является инициатором, producer - целью. Consumer  инициирует вызов producer для получения транзакции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:66.png]]&lt;br /&gt;
 &lt;br /&gt;
Рисунок 3-3 очень похож на рисунок 3-2. Разница лишь в том, что здесь стрелка от цели к инициатору, а не наоборот. Это означает, что потоки данных от цели к инициатору. Ниже SystemVerilog consumer (инициатор).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;62 class consumer;&lt;br /&gt;
63&lt;br /&gt;
64 get_if get_port;&lt;br /&gt;
65&lt;br /&gt;
66 task run();&lt;br /&gt;
67 int randval;&lt;br /&gt;
68 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
69 begin&lt;br /&gt;
70 get_port.get(randval);&lt;br /&gt;
71 $display(“consumer: receiving %4d”, randval);&lt;br /&gt;
72 end&lt;br /&gt;
73 endtask&lt;br /&gt;
74 endclass&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
consumer имеет задачу, run(), которая выполняется  10 раз, чтобы получить 10 транзакций. Producer   как в put  примере, consumer имеет здесь порт. Кроме того, как в put  примере, порт ссылается на истинно виртуальный интерфейс, в данном случае&lt;br /&gt;
он называется get_if.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;41 virtual class get_if;&lt;br /&gt;
42 pure virtual task get(output int t);&lt;br /&gt;
43 endclass : get_if&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
get_if&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
является истинно виртуальным  интерфейс, который определяет функцию get(). Цель (producer) строится аналогично, как и цель в put  примере. Она содержит реализацию функции интерфейса. Этот producer генерирует случайное число между 0 и 99.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;48 class producer extends get_if;&lt;br /&gt;
49&lt;br /&gt;
50 task get(output int t);&lt;br /&gt;
51 int randval;&lt;br /&gt;
52 randval = $random % 100;&lt;br /&gt;
53 $display(“producer: sending %4d”, randval);&lt;br /&gt;
54 t = randval;&lt;br /&gt;
55 endtask&lt;br /&gt;
56&lt;br /&gt;
57 endclass : producer&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Связи на высшем уровне будут выглядеть очень похожими.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;79 module top;&lt;br /&gt;
80&lt;br /&gt;
81 producer p;&lt;br /&gt;
82 consumer c;&lt;br /&gt;
83&lt;br /&gt;
84 initial begin&lt;br /&gt;
85 // instantiate producer and consumer&lt;br /&gt;
86 p = new();&lt;br /&gt;
87 c = new();&lt;br /&gt;
88 // connect producer and consumer through the get_if&lt;br /&gt;
89 // interface class&lt;br /&gt;
90 c.get_port = p;&lt;br /&gt;
91 c.run();&lt;br /&gt;
92 end&lt;br /&gt;
93 endmodule : top&lt;br /&gt;
file: 03_tlm/02_get/get.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
После создания объектов producer и consumer, используя операцию new(),  два объекта будут соединяться с помощью связей назначения.&lt;br /&gt;
&lt;br /&gt;
=== Transport === &lt;br /&gt;
&lt;br /&gt;
Transport является двунаправленным интерфейсом. Интерфейс обеспечивает передачу транзакций от инициатора к цели и от цели обратно к инициатору. Как правило, мы используем этот механизм, чтобы смоделировать запрос-ответ протоколы. Когда речь идет о компонентах с двунаправленным интерфейсов, мы используем термины master (ведущего) и slave (ведомого), а не инициатор и цель.&lt;br /&gt;
 &lt;br /&gt;
[[Файл:67.png]]&lt;br /&gt;
&lt;br /&gt;
master (A) делает и put и get  за один вызов функции. Как мы видели в предыдущих примерах, put() и get() принимают один аргумент, аргумент они помещают или извлекают. Тем не менее, функция transport() имеет два аргумента, запрос и ответ. Она посылает запрос и возвращает  ответ. slave (B) принимает запрос и возвращает ответ.&lt;br /&gt;
&lt;br /&gt;
Давайте сначала посмотрим на истинно виртуальный интерфейс.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;37 virtual class transport_if;&lt;br /&gt;
38 pure virtual task transport(input int request,&lt;br /&gt;
39 output int response);&lt;br /&gt;
40 endclass : transport_if&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Интерфейс содержит одну функцию, transport(), которая принимает два аргумента: запрос, который передается  цели и ответ, который возвращается  инициатору. &lt;br /&gt;
&lt;br /&gt;
Master вызывает transport(), создает запрос и отправляет его slave, используя transport. Он обрабатывает ответ, который возвращается.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;45 class master;&lt;br /&gt;
46&lt;br /&gt;
47 transport_if port;&lt;br /&gt;
48&lt;br /&gt;
49 task run();&lt;br /&gt;
50&lt;br /&gt;
51 int request;&lt;br /&gt;
52 int response;&lt;br /&gt;
53&lt;br /&gt;
54 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
55 begin &lt;br /&gt;
56 request = $random % 100;&lt;br /&gt;
57 $display(“master: sending request %4d”,&lt;br /&gt;
58 request);&lt;br /&gt;
59 port.transport(request, response);&lt;br /&gt;
60 $display(“master: receiving response %4d”,&lt;br /&gt;
61 response);&lt;br /&gt;
62 end&lt;br /&gt;
63&lt;br /&gt;
64 endtask&lt;br /&gt;
65 endclass : master&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
slave реализует функцию transport(). В нашем примере, она делает некоторые тривиальные обработки запроса для создания ответа.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;70 class slave extends transport_if;&lt;br /&gt;
71&lt;br /&gt;
72 task transport(input int request, output int response);&lt;br /&gt;
73 $display(“slave: receiving request %4d”,&lt;br /&gt;
74 request);&lt;br /&gt;
75 response = -request;&lt;br /&gt;
76 $display(“slave: sending response %4d”,&lt;br /&gt;
77 response);&lt;br /&gt;
78 endtask&lt;br /&gt;
79&lt;br /&gt;
80 endclass&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Связи верхнего уровня между master и slave работают точно так же как и в примере с put и get.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;85 module top;&lt;br /&gt;
86&lt;br /&gt;
87 master m;&lt;br /&gt;
88 slave s;&lt;br /&gt;
89&lt;br /&gt;
90 initial begin&lt;br /&gt;
91 // instantiate the master and slave&lt;br /&gt;
92 m = new();&lt;br /&gt;
93 s = new();&lt;br /&gt;
94&lt;br /&gt;
95 // connect the master and slave through&lt;br /&gt;
96 // the port interface&lt;br /&gt;
97 m.port = s;&lt;br /&gt;
98 m.run();&lt;br /&gt;
99 end&lt;br /&gt;
100&lt;br /&gt;
101 endmodule : top&lt;br /&gt;
file: 03_tlm/03_transport/transport.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Связь назначения связывает master и slave. После выполнения соединения, master может использовать соединения для прямого вызова функций slave.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:67.png</id>
		<title>Файл:67.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:67.png"/>
				<updated>2013-03-12T07:43:13Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:66.png</id>
		<title>Файл:66.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:66.png"/>
				<updated>2013-03-12T07:42:56Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5</id>
		<title>OVM/OVM методология/Transaction-Level моделирование</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5"/>
				<updated>2013-03-10T16:27:31Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Процесс проектирования электронной системы включает в себя последовательное замещение абстрактных идей  конкретной их реализацией, пока не будет достигнуто представление, которое может быть изготовлено на кремнии. С момента появления цифровой интегральной схемы, электронные сообщества тщательно определили и кодировали абстракции, начиная с переключателей и логических элементов, для обеспечения среды  в которой работает  проект. RTL является примером среды, часто используемой для создания проектов. Есть много инструментов на основе RTL, которые делают ее удобной для  создания проекта и его верификации.&lt;br /&gt;
Однако, как только проект становится больше и сложнее, становится  более представлять их с помощью абстракции выше, чем RTL. Уровень транзакций становится популярным для создания первого представления проекта, которое может быть смоделировано и проанализировано.&lt;br /&gt;
В этой главе описаны основные понятия transaction-level моделирования (TLM). Модели  transaction-level состоят из нескольких процессов, которые обмениваются информацией посредством транзакций.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Абстракция ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
RTL моделирование использует дискретную модель времени. Связь между процессами осуществляется с помощью сетки ( цепи), и процесс активации происходит при изменении значения во входной сети ( цепи).&lt;br /&gt;
Для сравнения, transaction-level модели (моделирующей схемы) могут быть синхронизированы или не синхронизированы и использовать шину для обмена данными между процессами. Вместо того чтобы посылать отдельные биты туда и обратно, процессы общаются, посылая транзакции друг  другу посредством вызова функций.  Мир TLM включает в себя ряд моделей(моделирующих схем) вычислений с различным временем, системами связи и процессами активации моделей (моделирующей схемы). В каждом конкретном случае, содержание связей находится на более высоком уровне абстракции, чем отдельные биты. Таким образом, transaction-level модель находится на более высоком уровне абстракции (более абстрактна), чем RTL модель. Объединяя понятия абстракции и модели (моделирующей схемы) вычислений, видно, что создание абстрактных моделей означает абстрагирование времени, данных и функций. &lt;br /&gt;
&lt;br /&gt;
'''Абстракция времени.''' Время абстракция в симуляторе относится к тому, насколько внутренние состояния схемы совместимы. Модели, запущенные в управляемых событиями симуляторах (например, логических симуляторах) используют дискретное представление  времени, то есть события происходят в определенные моменты времени. События, как правило (хотя и не всегда) вызывают запущенный процесс. Чем больше событий происходит в симуляции, больше процессов вызывается, и с большим числом процессов происходит снижении скорости моделирования. Абстрагирование время уменьшает количество точек, где схема должна быть совместимой и общего числа событий и процессов, которые будут активированы. Например, в RTL модели, каждая сеть должна быть согласована после каждого изменения. В cycle-accurate абстракции, схема должна быть синхронизирована с синхросигналом, устраняя все события, что происходит между фронтами синхроимпульса. В transaction-level модели, состояния схемы должны быть синхронизированы в конце каждой операции, каждая из которых может охватывать много тактов.&lt;br /&gt;
&lt;br /&gt;
'''Абстракция данных'''. Данные относятся к объектам, передаваемым между компонентами. В RTL моделях, данные относятся к отдельным битам, которые передаются по сети между компонентами. В transaction-level моделях, данных представлены в виде транзакций, гетерогенных структур, которые содержат произвольные коллекции элементов.&lt;br /&gt;
Рассмотрим пакет в устройстве связи. На самом низком уровне детализации, пакет содержит бит начала и стоп-биты, заголовок, информацию исправления ошибок, размер полезной нагрузки, нагрузки, и завершитель (??????). В более абстрактной модели, только нагрузка и размер нагрузки могут быть необходимы. Другие  данные не нужны  для выполнения расчетов.&lt;br /&gt;
&lt;br /&gt;
'''Абстракция функций.''' Функция модели является набором всех операций, которая она делать на каждое событие. Абстрагирование функций уменьшает этот набор или заменяет его более простыми вычислениями. Например, в ALU, вы можете выбрать использование родной операции умножении поставляемой в язык моделирования, вместо кодирования  a shift-and-add алгоритма умножения. Последний может быть частью реализации, но на более высоком уровне, детали a shift-and-add - неважны. Базовые элементы, которые являются частью языка, определяет, как вы можете абстрагировать функцию. В gate-level  языке, например, можно создавать сложные поведения, начиная с логических элементов. В RTL языка, построение  поведения основывается на арифметических и логических операциях над регистрами. В TLM, вы реализуете функциональность схемы с помощью вызовов функций произвольной сложности.&lt;br /&gt;
&lt;br /&gt;
Для целей функциональной верификации, RTL самый низкий уровень абстракции, который мы должны рассмотреть. С синтезаторы могут эффективно конвертировать RTL в логические элементы, нам не нужно затрагивать  более низкий уровень детализации. &lt;br /&gt;
&lt;br /&gt;
==  Определение транзакции ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для более подробного рассмотрения TLM, мы должны сделать шаг назад и определить понятие транзакции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:61.png]]&lt;br /&gt;
&lt;br /&gt;
Это самое общее определение транзакции. Оно утверждает, что транзакция – это все, что происходит в проекте (или модуле или подсистеме проекта) между двумя точками времени. Хотя оно точное, оно является настолько общим, что не приводит к практическому применению. Более полезные определения  следующие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:62.png]]&lt;br /&gt;
&lt;br /&gt;
Это аппаратно-ориентированные понятие транзакции. Когда смотришь на часть аппаратного обеспечения, вы можете легко определить сущности, между которыми передается управление или данные. В  проекте с шинной архитектурой, чтение и запись на шину может быть вызвано транзакцией. В пакетной системе связи, отправка пакета представляет собой транзакцию.&lt;br /&gt;
&lt;br /&gt;
Ниже третье определение:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:63.png]]&lt;br /&gt;
&lt;br /&gt;
Это определение программно-ориентированного понятия транзакции. В transaction-level модели, деятельность инициируется путем вызова функции. Вызов функции содержит параметры, которые &amp;quot;послали&amp;quot;  вызываемой функции, и возвращаемого значения функции. Вызванная  функция может блокировать и cause time to pass or it could return immediately.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Интерфейсы ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Прежде чем вдаваться в подробности о том, как построить transaction-level модель, мы сначала обсудим интерфейсы. Термин интерфейс используется в несколько случаях в OVM, каждый раз с немного другим смыслом. Это прискорбный факт истории, то что слово стало означать  различные вещи. В большинстве случаев значение будет понятно из контекста, в котором этот термин используется. Различных применения :&lt;br /&gt;
&lt;br /&gt;
•	Интерфейс SystemVerilog&lt;br /&gt;
&lt;br /&gt;
•	Объектный интерфейс&lt;br /&gt;
&lt;br /&gt;
•	Интерфейс DUT&lt;br /&gt;
&lt;br /&gt;
'''SystemVerilog интерфейс'''. SystemVerilog предоставляет конструкцию, называемую интерфейс, который является одним из основных объектов контейнер, из которого строится проект в SystemVerilog. Мы используем виртуальные интерфейсы, которые  указывают на интерфейсы для подключения module-based аппаратуры к  class-based testbenches.&lt;br /&gt;
&lt;br /&gt;
'''Объектный интерфейс.''' Общедоступные задачи и функции, доступные для объекта образует его интерфейс. Есть два небольших изменения этого значения интерфейс. Одним из них является прямой. Посмотрите на класс и определите, какие задачи и функций, доступны пользователю класса. Это его интерфейс. Другой вариант состоит в ссылке на базовый класс, который определяет набор задач и функций, доступных для работы производного класса. Это значение интерфейса обычно используется объектно-ориентированных языков, которые поддерживают множественное наследование, таких как C + + или java. В этих  языках, вы можете установить требование, что производный класс определяет функциональность, наследуемую от интерфейса базового класса.&lt;br /&gt;
&lt;br /&gt;
[[Файл:64.png]]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Интерфейс print_if определяет прототипы для функций печати.  Любой класс, который наследуется от print_if затем обязан осуществлять print()и sprint(). SystemVerilog не поддерживает множественное наследование, но он  поддерживает истинно виртуальные интерфейсы. Истинно  виртуальный интерфейс - интерфейс во втором контексте (базовый класс, который определяет набор задач и функций прототипов), который не имеет реализаций. Истинно  виртуальная версия  print_if  выглядит следующим образом в SystemVerilog:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;virtual class print_if;&lt;br /&gt;
pure virtual function void print();&lt;br /&gt;
pure virtual function string sprint();&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Даже если SystemVerilog не поддерживает множественное наследование, и OVM построено на SystemVerilog, важно понять истинно виртуальные интерфейсы и наследование интерфейсов, потому что они активно используются в OVM. В частности, TLM ports and exports являются производными от интерфейса класс tlm_if_base. &lt;br /&gt;
&lt;br /&gt;
'''DUT интерфейс.''' Часть аппаратуры, как правило, доступны через свои интерфейсы. В этом контексте, интерфейс состоит из контактов и протоколов используемых для связи с устройством. Например, устройство может иметь USB интерфейс.&lt;br /&gt;
&lt;br /&gt;
== TLM идиомы ==&lt;br /&gt;
&lt;br /&gt;
В данном разделе рассматриваются основные средства передачи транзакций между компонентами. Мы put, get, и transport формы связи транзакций. Эти примеры не используют библиотеку OVM, так как они предназначены для иллюстрации основных механизмов связи на transaction-level с минимальными затратами. В следующем разделе мы рассмотрим более полный пример, который использует OVM библиотека для связи.&lt;br /&gt;
&lt;br /&gt;
=== Put ===&lt;br /&gt;
В put  конфигурации, один компонент отправляет операции  другому компоненту. Операция называется put. Инициатором является компонентом, который инициирует передачу, а целевой компонент, который получает результат. Используя TLM номенклатуру, мы говорим, что инициатор put транзакции цели.&lt;br /&gt;
&lt;br /&gt;
[[Файл:65.png]]&lt;br /&gt;
 &lt;br /&gt;
Рисунок 3-2 показывает, что А предает операцию в пункт Б. Инициатор имеет порт, рисуется как квадрат, и цель имеет export, изображается в виде круга. Поток  управления направлен  от квадрата к кругу, то есть, А вызывает B, который содержит реализацию методов port. Стрелка показывает направление поток данных, и в этом случае, это означает, что данные будут двигаться от А до B.&lt;br /&gt;
Мы можем проиллюстрировать код для этих компонентов с producer и consumer. producer является инициатором и consumer является целью. Мы должны строить эти компоненты таким образом, что они не знали друг о друге априори. Чтобы сделать это, мы используем истинно виртуальный интерфейс для определения функции, который будет использоваться для передачи данных между инициатором и целью. Во-первых, давайте взглянем на SystemVerilog версию producer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;46 class producer;&lt;br /&gt;
47&lt;br /&gt;
48 put_if put_port;&lt;br /&gt;
49&lt;br /&gt;
50 task run();&lt;br /&gt;
51&lt;br /&gt;
52 int randval;&lt;br /&gt;
53&lt;br /&gt;
54 for(int i=0; i&amp;lt;10; i++)&lt;br /&gt;
55 begin&lt;br /&gt;
56 randval = $random %100;&lt;br /&gt;
57 $display(“producer: sending %4d”, randval);&lt;br /&gt;
58 put_port.put(randval);&lt;br /&gt;
59 end&lt;br /&gt;
60&lt;br /&gt;
61 endtask&lt;br /&gt;
62&lt;br /&gt;
63 endclass : producer&lt;br /&gt;
file: 03_tlm/01_put/put.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
producer  - это класс, который  создается динамически. Он имеет два ключевых элемента, Run () задачу и put_port. Run () задача является простой задачей,  которая повторяется 10 раз и  передает 10 транзакций. Для простоты, наши операции являются целыми числами. На практике, транзакция может быть сколь угодно сложным  объектом, таким как структура или класс.&lt;br /&gt;
Чтобы передать операции, producer вызывает put() на put_port. Что такое put_port? Это не порт в традиционном смысле Verilog. Это ссылка на put_if. Что такое put_if? Put_if это виртуальный интерфейс класса расположенный между инициатором (producer) и целью (consumer).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;39 virtual class put_if;&lt;br /&gt;
40 pure virtual task put(int val);&lt;br /&gt;
41 endclass : put_if&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
put_if класс с истинно виртуальными задачами, т.е.  задачами, не имеющими реализацию. Без реализации всех своих задач и функций, виртуальный класс не может быть создан сам по себе. Он должен быть базовым классом другого класса, который создается. В нашем случае, класс, производный от истинно виртуального put_if является consumer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;68 class consumer extends put_if;&lt;br /&gt;
69 task put(int val);&lt;br /&gt;
70 $display(“consumer: receiving %4d”, val);&lt;br /&gt;
71 endtask : put&lt;br /&gt;
72 endclass : consumer&lt;br /&gt;
file: 03_tlm/01_put/put.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
consumer содержит реализацию put(); истинно виртуальные задачи определены в put_if.  Задача put() реализация принимает аргументы, переданные ей, и печатает их. put_if играет ключевую роль в соединении consumer и producer. Ссылка на него на стороне producer, которую мы называем порт, устанавливает требование, что должна быть реализация  функций и задач  интерфейса, к которому этот объект будет привязан. consumer является производным от интерфейса и, следовательно, в нем должны быть реализованы истинно виртуальные задачи, удовлетворяющие требованиям.&lt;br /&gt;
&lt;br /&gt;
Модуль верхнего уровня связывающий producer и consumer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;77 module top;&lt;br /&gt;
78&lt;br /&gt;
79 producer p;&lt;br /&gt;
80 consumer c;&lt;br /&gt;
81&lt;br /&gt;
82 initial begin&lt;br /&gt;
83 // instantiate producer and consumer&lt;br /&gt;
84 p = new();&lt;br /&gt;
85 c = new();&lt;br /&gt;
86 // connect producer and consumer&lt;br /&gt;
87 // through the put_if interface class&lt;br /&gt;
88 p.put_port = c;&lt;br /&gt;
89 p.run();&lt;br /&gt;
90 end&lt;br /&gt;
91 endmodule : top&lt;br /&gt;
file: 03_tlm/01_put/put.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что оператор присваивания:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;88 p.put_port = c;&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Он образует связь между producer и consumer. Когда new() вызывается на р, чтобы создать новый экземпляр producer, член put_port  не имеет значения. Во время выполнения произойдет ошибка, если put_port. put () вызывается до связи назначения. Назначение С к p.put_port дает  ссылку для consumer, который содержит реализацию задачи put() интерфейса.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:65.png</id>
		<title>Файл:65.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:65.png"/>
				<updated>2013-03-10T16:19:01Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:64.png</id>
		<title>Файл:64.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:64.png"/>
				<updated>2013-03-10T16:18:47Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5</id>
		<title>OVM/OVM методология/Transaction-Level моделирование</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5"/>
				<updated>2013-03-10T14:47:36Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Процесс проектирования электронной системы включает в себя последовательное замещение абстрактных идей  конкретной их реализацией, пока не будет достигнуто представление, которое может быть изготовлено на кремнии. С момента появления цифровой интегральной схемы, электронные сообщества тщательно определили и кодировали абстракции, начиная с переключателей и логических элементов, для обеспечения среды  в которой работает  проект. RTL является примером среды, часто используемой для создания проектов. Есть много инструментов на основе RTL, которые делают ее удобной для  создания проекта и его верификации.&lt;br /&gt;
Однако, как только проект становится больше и сложнее, становится  более представлять их с помощью абстракции выше, чем RTL. Уровень транзакций становится популярным для создания первого представления проекта, которое может быть смоделировано и проанализировано.&lt;br /&gt;
В этой главе описаны основные понятия transaction-level моделирования (TLM). Модели  transaction-level состоят из нескольких процессов, которые обмениваются информацией посредством транзакций.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Абстракция ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
RTL моделирование использует дискретную модель времени. Связь между процессами осуществляется с помощью сетки ( цепи), и процесс активации происходит при изменении значения во входной сети ( цепи).&lt;br /&gt;
Для сравнения, transaction-level модели (моделирующей схемы) могут быть синхронизированы или не синхронизированы и использовать шину для обмена данными между процессами. Вместо того чтобы посылать отдельные биты туда и обратно, процессы общаются, посылая транзакции друг  другу посредством вызова функций.  Мир TLM включает в себя ряд моделей(моделирующих схем) вычислений с различным временем, системами связи и процессами активации моделей (моделирующей схемы). В каждом конкретном случае, содержание связей находится на более высоком уровне абстракции, чем отдельные биты. Таким образом, transaction-level модель находится на более высоком уровне абстракции (более абстрактна), чем RTL модель. Объединяя понятия абстракции и модели (моделирующей схемы) вычислений, видно, что создание абстрактных моделей означает абстрагирование времени, данных и функций. &lt;br /&gt;
&lt;br /&gt;
'''Абстракция времени.''' Время абстракция в симуляторе относится к тому, насколько внутренние состояния схемы совместимы. Модели, запущенные в управляемых событиями симуляторах (например, логических симуляторах) используют дискретное представление  времени, то есть события происходят в определенные моменты времени. События, как правило (хотя и не всегда) вызывают запущенный процесс. Чем больше событий происходит в симуляции, больше процессов вызывается, и с большим числом процессов происходит снижении скорости моделирования. Абстрагирование время уменьшает количество точек, где схема должна быть совместимой и общего числа событий и процессов, которые будут активированы. Например, в RTL модели, каждая сеть должна быть согласована после каждого изменения. В cycle-accurate абстракции, схема должна быть синхронизирована с синхросигналом, устраняя все события, что происходит между фронтами синхроимпульса. В transaction-level модели, состояния схемы должны быть синхронизированы в конце каждой операции, каждая из которых может охватывать много тактов.&lt;br /&gt;
&lt;br /&gt;
'''Абстракция данных'''. Данные относятся к объектам, передаваемым между компонентами. В RTL моделях, данные относятся к отдельным битам, которые передаются по сети между компонентами. В transaction-level моделях, данных представлены в виде транзакций, гетерогенных структур, которые содержат произвольные коллекции элементов.&lt;br /&gt;
Рассмотрим пакет в устройстве связи. На самом низком уровне детализации, пакет содержит бит начала и стоп-биты, заголовок, информацию исправления ошибок, размер полезной нагрузки, нагрузки, и завершитель (??????). В более абстрактной модели, только нагрузка и размер нагрузки могут быть необходимы. Другие  данные не нужны  для выполнения расчетов.&lt;br /&gt;
&lt;br /&gt;
'''Абстракция функций.''' Функция модели является набором всех операций, которая она делать на каждое событие. Абстрагирование функций уменьшает этот набор или заменяет его более простыми вычислениями. Например, в ALU, вы можете выбрать использование родной операции умножении поставляемой в язык моделирования, вместо кодирования  a shift-and-add алгоритма умножения. Последний может быть частью реализации, но на более высоком уровне, детали a shift-and-add - неважны. Базовые элементы, которые являются частью языка, определяет, как вы можете абстрагировать функцию. В gate-level  языке, например, можно создавать сложные поведения, начиная с логических элементов. В RTL языка, построение  поведения основывается на арифметических и логических операциях над регистрами. В TLM, вы реализуете функциональность схемы с помощью вызовов функций произвольной сложности.&lt;br /&gt;
&lt;br /&gt;
Для целей функциональной верификации, RTL самый низкий уровень абстракции, который мы должны рассмотреть. С синтезаторы могут эффективно конвертировать RTL в логические элементы, нам не нужно затрагивать  более низкий уровень детализации. &lt;br /&gt;
&lt;br /&gt;
==  Определение транзакции ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для более подробного рассмотрения TLM, мы должны сделать шаг назад и определить понятие транзакции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:61.png]]&lt;br /&gt;
&lt;br /&gt;
Это самое общее определение транзакции. Оно утверждает, что транзакция – это все, что происходит в проекте (или модуле или подсистеме проекта) между двумя точками времени. Хотя оно точное, оно является настолько общим, что не приводит к практическому применению. Более полезные определения  следующие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:62.png]]&lt;br /&gt;
&lt;br /&gt;
Это аппаратно-ориентированные понятие транзакции. Когда смотришь на часть аппаратного обеспечения, вы можете легко определить сущности, между которыми передается управление или данные. В  проекте с шинной архитектурой, чтение и запись на шину может быть вызвано транзакцией. В пакетной системе связи, отправка пакета представляет собой транзакцию.&lt;br /&gt;
&lt;br /&gt;
Ниже третье определение:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:63.png]]&lt;br /&gt;
&lt;br /&gt;
Это определение программно-ориентированного понятия транзакции. В transaction-level модели, деятельность инициируется путем вызова функции. Вызов функции содержит параметры, которые &amp;quot;послали&amp;quot;  вызываемой функции, и возвращаемого значения функции. Вызванная  функция может блокировать и cause time to pass or it could return immediately.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5</id>
		<title>OVM/OVM методология/Transaction-Level моделирование</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/Transaction-Level_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5"/>
				<updated>2013-03-10T14:44:52Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: Новая страница: «Процесс проектирования электронной системы включает в себя последовательное замещение…»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Процесс проектирования электронной системы включает в себя последовательное замещение абстрактных идей  конкретной их реализацией, пока не будет достигнуто представление, которое может быть изготовлено на кремнии. С момента появления цифровой интегральной схемы, электронные сообщества тщательно определили и кодировали абстракции, начиная с переключателей и логических элементов, для обеспечения среды  в которой работает  проект. RTL является примером среды, часто используемой для создания проектов. Есть много инструментов на основе RTL, которые делают ее удобной для  создания проекта и его верификации.&lt;br /&gt;
Однако, как только проект становится больше и сложнее, становится  более представлять их с помощью абстракции выше, чем RTL. Уровень транзакций становится популярным для создания первого представления проекта, которое может быть смоделировано и проанализировано.&lt;br /&gt;
В этой главе описаны основные понятия transaction-level моделирования (TLM). Модели  transaction-level состоят из нескольких процессов, которые обмениваются информацией посредством транзакций.&lt;br /&gt;
&lt;br /&gt;
3.1 Абстракция&lt;br /&gt;
RTL моделирование использует дискретную модель времени. Связь между процессами осуществляется с помощью сетки ( цепи), и процесс активации происходит при изменении значения во входной сети ( цепи).&lt;br /&gt;
Для сравнения, transaction-level модели (моделирующей схемы) могут быть синхронизированы или не синхронизированы и использовать шину для обмена данными между процессами. Вместо того чтобы посылать отдельные биты туда и обратно, процессы общаются, посылая транзакции друг  другу посредством вызова функций.  Мир TLM включает в себя ряд моделей(моделирующих схем) вычислений с различным временем, системами связи и процессами активации моделей (моделирующей схемы). В каждом конкретном случае, содержание связей находится на более высоком уровне абстракции, чем отдельные биты. Таким образом, transaction-level модель находится на более высоком уровне абстракции (более абстрактна), чем RTL модель. Объединяя понятия абстракции и модели (моделирующей схемы) вычислений, видно, что создание абстрактных моделей означает абстрагирование времени, данных и функций. &lt;br /&gt;
Абстракция времени. Время абстракция в симуляторе относится к тому, насколько внутренние состояния схемы совместимы. Модели, запущенные в управляемых событиями симуляторах (например, логических симуляторах) используют дискретное представление  времени, то есть события происходят в определенные моменты времени. События, как правило (хотя и не всегда) вызывают запущенный процесс. Чем больше событий происходит в симуляции, больше процессов вызывается, и с большим числом процессов происходит снижении скорости моделирования. Абстрагирование время уменьшает количество точек, где схема должна быть совместимой и общего числа событий и процессов, которые будут активированы. Например, в RTL модели, каждая сеть должна быть согласована после каждого изменения. В cycle-accurate абстракции, схема должна быть синхронизирована с синхросигналом, устраняя все события, что происходит между фронтами синхроимпульса. В transaction-level модели, состояния схемы должны быть синхронизированы в конце каждой операции, каждая из которых может охватывать много тактов.&lt;br /&gt;
&lt;br /&gt;
Абстракция данных. Данные относятся к объектам, передаваемым между компонентами. В RTL моделях, данные относятся к отдельным битам, которые передаются по сети между компонентами. В transaction-level моделях, данных представлены в виде транзакций, гетерогенных структур, которые содержат произвольные коллекции элементов.&lt;br /&gt;
Рассмотрим пакет в устройстве связи. На самом низком уровне детализации, пакет содержит бит начала и стоп-биты, заголовок, информацию исправления ошибок, размер полезной нагрузки, нагрузки, и завершитель (??????). В более абстрактной модели, только нагрузка и размер нагрузки могут быть необходимы. Другие  данные не нужны  для выполнения расчетов.&lt;br /&gt;
Абстракция функций. Функция модели является набором всех операций, которая она делать на каждое событие. Абстрагирование функций уменьшает этот набор или заменяет его более простыми вычислениями. Например, в ALU, вы можете выбрать использование родной операции умножении поставляемой в язык моделирования, вместо кодирования  a shift-and-add алгоритма умножения. Последний может быть частью реализации, но на более высоком уровне, детали a shift-and-add - неважны. Базовые элементы, которые являются частью языка, определяет, как вы можете абстрагировать функцию. В gate-level  языке, например, можно создавать сложные поведения, начиная с логических элементов. В RTL языка, построение  поведения основывается на арифметических и логических операциях над регистрами. В TLM, вы реализуете функциональность схемы с помощью вызовов функций произвольной сложности.&lt;br /&gt;
Для целей функциональной верификации, RTL самый низкий уровень абстракции, который мы должны рассмотреть. С синтезаторы могут эффективно конвертировать RTL в логические элементы, нам не нужно затрагивать  более низкий уровень детализации. &lt;br /&gt;
&lt;br /&gt;
3.2 Определение транзакции&lt;br /&gt;
Для более подробного рассмотрения TLM, мы должны сделать шаг назад и определить понятие транзакции.&lt;br /&gt;
&lt;br /&gt;
[[Файл:61.png]]&lt;br /&gt;
&lt;br /&gt;
Это самое общее определение транзакции. Оно утверждает, что транзакция – это все, что происходит в проекте (или модуле или подсистеме проекта) между двумя точками времени. Хотя оно точное, оно является настолько общим, что не приводит к практическому применению. Более полезные определения  следующие:&lt;br /&gt;
&lt;br /&gt;
[[Файл:62.png]]&lt;br /&gt;
&lt;br /&gt;
Это аппаратно-ориентированные понятие транзакции. Когда смотришь на часть аппаратного обеспечения, вы можете легко определить сущности, между которыми передается управление или данные. В  проекте с шинной архитектурой, чтение и запись на шину может быть вызвано транзакцией. В пакетной системе связи, отправка пакета представляет собой транзакцию.&lt;br /&gt;
&lt;br /&gt;
Ниже третье определение:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:63.png]]&lt;br /&gt;
&lt;br /&gt;
Это определение программно-ориентированного понятия транзакции. В transaction-level модели, деятельность инициируется путем вызова функции. Вызов функции содержит параметры, которые &amp;quot;послали&amp;quot;  вызываемой функции, и возвращаемого значения функции. Вызванная  функция может блокировать и cause time to pass or it could return immediately.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:63.png</id>
		<title>Файл:63.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:63.png"/>
				<updated>2013-03-10T14:42:33Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:62.png</id>
		<title>Файл:62.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:62.png"/>
				<updated>2013-03-10T14:42:18Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:61.png</id>
		<title>Файл:61.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:61.png"/>
				<updated>2013-03-10T14:42:04Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM</id>
		<title>OVM</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM"/>
				<updated>2013-03-10T14:41:32Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: /* Описание проекта */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM_TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
== Описание проекта ==&lt;br /&gt;
* [http://redmine.simhard.com/projects/ovm Контроль проекта]&lt;br /&gt;
* [http://simhard.com/websvn/listing.php?repname=ovm Просмотр репозитория проекта]&lt;br /&gt;
* [[OVM/OVM методология | OVM методология  ]]&lt;br /&gt;
**[[OVM/OVM методология/Введение| Введение]]&lt;br /&gt;
**[[OVM/OVM методология/Основы верификации| Основы верификации]]&lt;br /&gt;
**[[OVM/OVM методология/Основы объектно-ориентированного программирования| Основы объектно-ориентированного программирования]]&lt;br /&gt;
**[[OVM/OVM методология/Transaction-Level моделирование|Transaction-Level моделирование]]&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* [[OVM/Ссылки | Ссылки ]]&lt;br /&gt;
* [[OVM/Книги | Книги ]]&lt;br /&gt;
* [[OVM/Стандарт OVM| Стандарт OVM ]]&lt;br /&gt;
== TO DO ==&lt;br /&gt;
* [[OVM/Описание пакетов | Описание пакетов ]]&lt;br /&gt;
* [[OVM/Описание функций | Описание функций ]]&lt;br /&gt;
* [[OVM/Тестовый план | Тестовый план ]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дипломы-2013]]&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%B2%D0%B5%D1%80%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D0%B8</id>
		<title>OVM/OVM методология/Основы верификации</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%B2%D0%B5%D1%80%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D0%B8"/>
				<updated>2013-03-05T13:29:53Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: /* 3-х разрядный счетчик */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
== Основы верификации==&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Функционально проверить устройство означает, сравнить цель разработчика с полученным  поведением на  их эквивалентность. Мы считаем устройство проверенным, когда, к всеобщему удовлетворению, оно работает в соответствии с целью разработчика. Этот основной принцип часто теряется при использовании testbenches, среды контроля, отладки, моделирования , и все другие средства, применяемые в современных технологических проверках. Чтобы узнать, работает ли верно устройство, вы должны сравнить его с некоторыми известными эталоном, который представляет цель разработчика. Каждый testbench имеет своего рода эталонную модель и средство для сравнения функциональность устройство с эталоном. &lt;br /&gt;
&lt;br /&gt;
	Когда мы говорим &amp;quot;устройство&amp;quot;, мы имеем в виду проверяемое устройство, часто называемое тестируемое устройство или DUT. Чтобы проверить, DUT, как правило, представлен  форме подходящей для производства, которое может быть перенесено на кремний с помощью автоматизированных и ручных средств. Мы различаем DUT от эскиза на обратной стороне салфетки или окончательного нанесенный на кристалл, ни один из которых не может быть проверен. Эталонная модель отражает цели разработчика, то есть то, что устройство должно делать.Эталон может принимать различные формы, такие как документ, описывающий работу DUT,  golden модель, которая содержит уникальный алгоритм, или assertions, которые представляют протокол.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:1.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для автоматизации сравнения поведения и эталона, оба должны быть представлены в форме, которую можно выполнить на компьютере с помощью некоторых программ, что делает  сравнение. &lt;br /&gt;
&lt;br /&gt;
==== Два вопроса====&lt;br /&gt;
&lt;br /&gt;
	Проверка устройства включает в себя два вопроса:Does it work? (Работает ли это устройство?) и Are we done? (Сделали ли мы?). Это основные, и некоторые сказали бы, очевидные вопросы. Тем не менее они &lt;br /&gt;
мотивируют всю механику каждой проверочного потока. Первый вопрос - это Does it work? Этот вопрос исходит от основной идеи верификации. Он спрашивает, Соответствует ли устройство эталону? Второй вопрос - Are we done? Он спрашивает, довольны ли мы сравнительным анализом полученной схемы и эталона для определения работает ли устройство согласно цели, если нет, то почему. Мы используем эти ценные вопросы создания основы для разработки эффективных testbenches.&lt;br /&gt;
&lt;br /&gt;
====Does it work?====&lt;br /&gt;
&lt;br /&gt;
	Does it work? Это не единственный вопрос, а категория вопросов, которые представляют природу DUT. Каждый проект будет иметь свой собственный набор Does-It-Work вопросов, чья роль заключается в определении правильного функционирования  устройства.  Функциональные вопросы определяют, ведет ли себя устройство должным образом в конкретных ситуациях. Мы получаем эти вопросы непосредственно из цели разработки устройства, и мы используем их, чтобы отразить цель проекта  в testbench.&lt;br /&gt;
&lt;br /&gt;
	Рассмотрим простой маршрутизатор пакетов в качестве примера. Это устройство маршрутизации пакетов от входа к одному из четырех портов вывода. Пакеты содержат адреса портов назначения и имеют различные длины полезной нагрузки. Каждый пакет имеет заголовок, трейлер, и два байта для циклического избыточного кода (CRC). Does-It-Work вопросы в этом случае должны быть следующие:&lt;br /&gt;
 &lt;br /&gt;
*пакет, поступающий на вход порта, адресованный на имя выходного порта 3, прибыл на порт  3? &lt;br /&gt;
*пакет длиной 16 пришел без изменений? &lt;br /&gt;
*CRC байты правильны, если полезная нагрузка [0f 73 a8 c2 3e 57 11 0a 88 FF 00 00 33 2b 4c 89]?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	Это простой пример полного набора вопросов. Для устройства даже такого простого как этот гипотетическое маршрутизатор, набор Does-It-Work вопросов может быть длинным. Чтобы построить план проверки и testbench, который поддерживает план, вы должны сначала перечислить все вопросы или показать как получит полный набор вопросов, а затем выберите те, которые являются интересными.&lt;br /&gt;
&lt;br /&gt;
	Продолжая пример маршрутизатора  пакетов,для того чтобы перечислить все Does-It-Work вопросы, вы можете создать таблицу:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:2.png]]&lt;br /&gt;
&lt;br /&gt;
Приведенная выше таблица содержит два вида вопросов, те,  на которые мы можем ответить сразу и те, которые мы можно разбить на более мелкие вопросы. Вопрос 1 из ряда вопросов, которые можно  представить в виде:&lt;br /&gt;
&lt;br /&gt;
[[Файл:3.png]]&lt;br /&gt;
[[Файл:4.png]]&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что мы формулируем все вопросы, так что на них можно ответить да или нет. В конце концов, схема  работает либо нет – она либо  готова для синтеза, размещения  и трассировке, либо нет. Если вы можете ответить на все вопросы утвердительно, тогда проект готов для следующего производственного шага.&lt;br /&gt;
При разработке набора does-it-work  вопросов, не забудьте сформулировать их  таким образом, чтобы на них можно было ответить да или нет. Ответ да положителен, то есть, ответить  да означает, что устройство работает правильно. Это будет сделать легче, чем пытаться отслеживать  на какие вопросы нужно ответить  да, и на какие следует ответить нет. Такой  вопрос как  Передает ли  маршрутизатор ошибочные пакеты?  требует ответа нет, чтобы  считаться успешным. Лучшей формулировкой вопроса будет: Отклоняет ли маршрутизатор ошибочные  пакеты? Вы должны сделать вопросы наиболее конкретными,  даже если лучшее звучание будет:  Когда ошибочный пакет попадает на входной порт,  маршрутизатор обнаруживает его, подает сигнал ошибки и отклоняет этот пакет? Имейте в виду, что более конкретные вопросы более подробно описывают устройство. Ваш testbench должен дать ответ да или нет.&lt;br /&gt;
Тщательно сформулированный да или нет вопрос содержит свои собственные критерии успеха. Он сообщает, что получает ответа да. Такой  вопрос  как, Средняя задержка менее 27 тактов? содержит метрику, 27 тактов, и форму сравнения, меньше. Если вопрос (неправильно) сформулирован, например, Какова средняя задержка прохождения пакетов через маршрутизатор? мы не будем знать, что считается приемлемым. Чтобы ответить на ваш вопрос, вы в первую очередь должны быть в состоянии определить среднее время задержки. Только при правильной постановке вопроса мы знаем, как сделать сравнение, чтобы определить, является ли результат  правильным. Метрика сама по себе не говорит нам о том,   функционирует ли схема как задумано. Когда мы сравниваем измеренное значение со значением в  спецификации, 27 тактов в этом примере, мы можем определить, работает ли схема.&lt;br /&gt;
Как это часто бывает, это не практично  перечислять каждый does-it-work вопрос. Чтобы убедиться, что каждое слово в памяти 1 Мб можно записать и читать,  непрактично и не необходимо записать один миллион вопросов. Вместо этого, вопрос-генератор,  вопрос, который генерирует многие другие, занимает место  миллиона отдельных вопросов. Может каждый  миллион слов в памяти быть успешно записан и считан? -является вопросом-генератором.&lt;br /&gt;
Другие вопросы могут сами по себе  представлять  классы вопросов. Вопрос 3, Является ли вычисление CRC корректным для каждого пакета? является примером такого вопроса. Тестирование CRC вычисления требует ряда тщательно продуманных тестов, для  определения является ли вычисления CRC корректным во всех случаях. &lt;br /&gt;
&lt;br /&gt;
==== Are We Done?====&lt;br /&gt;
&lt;br /&gt;
Для определения ответа на вопрос  Are we done?, мы должны знать, что мы ответили на достаточное количество does-it-work  вопросов, чтобы  утверждать, что у нас есть проверенная схема. Мы начинаем эту задачу путем разделения на приоритеты всех does-it-work  вопросов по двум направлениям:&lt;br /&gt;
&lt;br /&gt;
[[Файл:5.png]]&lt;br /&gt;
&lt;br /&gt;
Искусство создания testbench требует, чтобы на начальном этапе, мы определили множество вопросов и отсортировали их в соответствии с ценностью возвращаемой информации с точки зрения проверки схемы. Следующий шаг заключается в создании механизма, который  будет отвечать на вопросы и определять  на какие из них были даны ответы (и на какие нет).&lt;br /&gt;
&lt;br /&gt;
Are-we-done вопросы также называется вопросами функционального покрытия, вопросы, которые устанавливают  все ли состояния охватывают тесты с точки зрения  функций схемы. Как  и  does-it-work  вопросы, мы можем также разбить вопросы функционального охвата на более подробные вопросы. И как вопросы функциональной корректности, вопросы функционального охвата  должны также иметь ответы да или нет. Приведенный ниже список включает в себя примеры вопросы функционального охвата:&lt;br /&gt;
&lt;br /&gt;
•	Выполняется ли каждая команда процессора, по крайней мере, один раз?&lt;br /&gt;
&lt;br /&gt;
•	Проходит, по крайней мере, один пакет  от каждого входного порта до каждого выходного порта?&lt;br /&gt;
&lt;br /&gt;
•	Можно ли обратиться к памяти  с набором адресов, которые содержат каждый бит адреса как 1 и затем каждый  бит адреса 0, не включая 0xffffffff и 0x00000000?(?????????)&lt;br /&gt;
&lt;br /&gt;
Другим мнением об этих вопросах является то, о чем они спрашивают, Получен ли утвердительный ответ на необходимый does-it-work  вопрос? Когда мы думаем о функциональном охвате с этой точки зрения, то имеем виду покрытие множества does-it-work  вопросов. Кроме того, вопросы охвата распознают метрику и порог для сравнения. Покрытие будет  (то есть, охват вопрос можно ответить, да), когда метрика достигает порога.&lt;br /&gt;
&lt;br /&gt;
Таким образом, искусство построения testbench начинается с плана тестирования.  План тестирования начинается с тщательно продуманным набором does-it-work и Are-we-done вопросов.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Двухпетлевый поток (???????????????)====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Процесс ответа на does-it-work и Are-we-done вопросы может быть описан  простой схемой, как показано на рисунке 1-2. Все обусловлено функциональной спецификацией на проектирование. Из функциональной спецификации, вы можете получить саму схему и план верификации.  План верификации запускает testbench.&lt;br /&gt;
&lt;br /&gt;
Поток состоит из двух циклов, does-it-work цикла и Are-we-done цикл. Оба цикла начинаются с моделирования. Моделирование тестирует  схему при помощи  testbench и генерирует информацию, которую мы используем для ответа на вопросы. Сначала мы спрашиваем, Это работает? Если ответ нет, то необходимо отладить проект. Процесс отладки схемы может привести к изменениям  в реализации схемы.&lt;br /&gt;
Как только схема работает, то пора ответить на вопрос, Are-we-done? Мы ответим на этот вопрос, собирая информацию охвата и сравнения его с порогом, указанным в тестовом плане. Если мы не достигнем  порогов, то ответ нет, и мы должны изменить testbench для увеличения охвата. Тогда мы моделируем снова.&lt;br /&gt;
&lt;br /&gt;
Изменение testbench или воздействия может вызвать другие скрытые ошибки в схеме. Последующие итерации по циклу могут заставить нас вернуться к does-it-work циклу снова, чтобы исправить новые ошибки, которые появятся. Как вы можете видеть, полный процесс проверки колеблется вперед и назад между does-it-work и Are-we-done циклами, пока мы не можем утвердительно ответить на все вопросы  обоих категорий.&lt;br /&gt;
&lt;br /&gt;
[[Файл:6.png]]&lt;br /&gt;
&lt;br /&gt;
В идеальном мире, схема не имеет ошибок и с покрытием всегда полное, так что вам только надо пройти по каждому циклу один раз, чтобы получить ответы да на оба вопроса. В реальном мире, это может занять много итераций для достижения двух ответов да. Одна из целей хорошего потока проверки свести к минимуму количество итераций для выполнения верификации проекта в кратчайшие сроки с использованием наименьшего количества ресурсов.&lt;br /&gt;
&lt;br /&gt;
== Первый Testbench==&lt;br /&gt;
&lt;br /&gt;
Давайте начнем с проверки одного из самых фундаментальных цифровых электронных устройств, схемы логического И. Логическое И вычисляет логическое умножение   входов. Функция этого устройства является тривиальной, а на практике, вряд ли должна иметь свой собственный  testbench. Так как она тривиальна, мы можем использовать ее для иллюстрации некоторых основных принципов проверки без необходимости вникать в более сложную схему.&lt;br /&gt;
&lt;br /&gt;
Рисунок 1-3 показывает условное обозначение  логического элемента умножения с двумя входами. Логический элемент имеет два входа, А и В, и один выход Y. Устройство вычисляет логическое умножение  А и В и помещает результат в Y.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:7.png]]&lt;br /&gt;
&lt;br /&gt;
Следующая таблица истинности описывает функцию устройства.&lt;br /&gt;
&lt;br /&gt;
[[Файл:8.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Таблица истинности является исчерпывающей: она содержит все возможные входы А и В и поэтому, все возможные значения для выхода Y.&lt;br /&gt;
Наша задача состоит в том, чтобы доказать, что наша схема,  логического И, работает правильно. Чтобы убедиться, что он действительно выполняет функцию логического умножения  правильно, нам в первую очередь необходимо перечислим вопросы. Таблица истинности поможет нам создать множество вопросов, необходимых для проверки. Каждая строка таблицы содержит вход для А и В и ожидаемый выход для Y. Так как таблица является исчерпывающей, наш вопрос-генератор будет:  Для каждой ли строки в таблице истинности, когда мы получаем значения А и B, определенные в этой строке,  устройство вычисляет ожидаемый результат для Y? Для ответа на Are-we-done вопрос,  мы определяем, проверили ли  мы  каждую строку в таблице истинности, и получил ли утвердительный ответ does-itwork вопрос для этой строки. Наш Are-we-done вопрос- это Работает на всех наборах?&lt;br /&gt;
Для автоматизации ответов как на does-itwork  та  и на Are-we-done  вопросы,  нам нужна следующая атрибутика:&lt;br /&gt;
&lt;br /&gt;
•	Модель, которая представляет DUT (в данном случае, логическое И)&lt;br /&gt;
&lt;br /&gt;
•	Концепция конструкции в форме, которую мы можем кодифицировать  как эталонную модель&lt;br /&gt;
&lt;br /&gt;
•	Некоторое воздействие для проверки схемы&lt;br /&gt;
&lt;br /&gt;
•	Способ сравнить результат с концепцией конструкции&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:9.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В то время как наш маленький testbench прост, он содержит ключевые элементы, которые можно найти в большинстве testbenches любого уровня сложности. Ключевыми элементами являются следующие:&lt;br /&gt;
&lt;br /&gt;
•	DUT&lt;br /&gt;
&lt;br /&gt;
•	Stimulus генератор, который генерирует последовательность сигналов для DUT&lt;br /&gt;
&lt;br /&gt;
•	Scoreboard, которое изображает  эталонную модель&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Табло следит за входами и выходом тестируемого устройства, выполняет же функции, что DUT за исключением более высокого уровня абстракции, и определяет, совпадает ли DUT и эталонная модель. Scoreboard помогает нам ответить на does-itwork  вопросы.&lt;br /&gt;
&lt;br /&gt;
=== DUT===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DUT- это наше логическое И с двумя входами. Мы реализуем логическое И, как модуль с двумя входами А и В, и одним выходом Y.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;44 module and2 (&lt;br /&gt;
45 output bit Y,&lt;br /&gt;
46 input A, B);&lt;br /&gt;
47&lt;br /&gt;
48 initial Y = A &amp;amp; B;&lt;br /&gt;
49&lt;br /&gt;
50 always @* Y = #1 A &amp;amp; B;&lt;br /&gt;
51 endmodule&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stimulus генератор в этом примере создает заданные сигналы. Каждое новое значение вычисляются в определенном порядке. Позже, мы будем смотреть на  генератор случайных сигналов, которые, как следует из их названия, генерирует случайные значения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;81 module stimulus(output bit A, B);&lt;br /&gt;
82&lt;br /&gt;
83 bit [1:0] stimulus_count = 0;&lt;br /&gt;
84&lt;br /&gt;
85 always&lt;br /&gt;
86 #10 {A,B} = stimulus_count++;&lt;br /&gt;
87&lt;br /&gt;
88 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Цель Stimulus генератор - получение значений на вход  DUT. Сигналы, двухбитная величина, содержащая значение для А и B. После того как он увеличивается при каждой последующей итерации, младший бит посылает на А, а старший бит на B.&lt;br /&gt;
&lt;br /&gt;
=== Scoreboard===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Scoreboard несет ответственность за ответы на  does-it-work  вопросы. Оно следит за работой DUT и сообщает работает ли оно корректно.  Обратите внимание на то, что структура Scoreboard поразительно похожа на структуру DUT. Это имеет смысл, когда вы считаете, что назначением Scoreboard является отслеживание активности DUT  и определение, работает ли DUT  как  ожидалось.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;59 module scoreboard(input bit Y, A, B);&lt;br /&gt;
60&lt;br /&gt;
61 reg Y_sb, truth_table[2][2];&lt;br /&gt;
62&lt;br /&gt;
63 initial begin&lt;br /&gt;
64 truth_table[0][0] = 0;&lt;br /&gt;
65 truth_table[0][1] = 0;&lt;br /&gt;
66 truth_table[1][0] = 0;&lt;br /&gt;
67 truth_table[1][1] = 1;&lt;br /&gt;
68 end&lt;br /&gt;
69&lt;br /&gt;
70 always @(A or B) begin&lt;br /&gt;
71 Y_sb = truth_table[A][B];&lt;br /&gt;
72 #2 $display(“@%4t - %b%b : Y_sb=%b, Y=%b (%0s)”,&lt;br /&gt;
73 $time, A, B, Y_sb, Y,&lt;br /&gt;
74 ((Y_sb == Y) ? “Match” : “Mis-match”));&lt;br /&gt;
75 end&lt;br /&gt;
76 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Scoreboard отображает пины всех входов. На Scoreboard не вызывает воздействия на  схему. Оно пассивно наблюдает за входами и выходами тестируемого устройства. &lt;br /&gt;
&lt;br /&gt;
Модуль верхнего уровня, как показано ниже, является полностью структурированным, он содержит  экземпляр тестируемого устройства, Scoreboard и Stimulus генератор,  с кодом, необходимым для их подключения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;93 module top;&lt;br /&gt;
94 stimulus s(A, B);&lt;br /&gt;
95 and2 a(Y, A, B);&lt;br /&gt;
96 scoreboard sb(Y, A, B);&lt;br /&gt;
97&lt;br /&gt;
98 initial&lt;br /&gt;
99 #100 $finish(2);&lt;br /&gt;
100 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Когда мы запускаем моделирования для нескольких итераций, вот что мы получаем:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:14.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Каждое сообщение состоит из двух частей. В первой части показаны применяемые сигналы. Вторая часть показывает результаты проверки Scoreboard, которое сравнивает соответствие DUT с  необходимыми результатами. Мы используем двоеточие для разделения двух частей. &lt;br /&gt;
&lt;br /&gt;
Этот простой  testbench  демонстрирует использование Stimulus генератор и Scoreboard, которое служит в качестве эталона. Несмотря на то, DUT – логическое И, все элементы полного testbench  присутствуют.&lt;br /&gt;
&lt;br /&gt;
== Второй Testbench==&lt;br /&gt;
&lt;br /&gt;
В предыдущий пример показывает элементарные понятия проверки с использованием  комбинационного дизайна.  Комбинационные конструкций,  по  природе, не поддерживают никаких статических данных. В нашем втором примере мы рассмотрим чуть более сложную конструкцию, которая сохраняет данные о состояниях и использует  тактовый сигнал  для перехода  между состояниями.&lt;br /&gt;
&lt;br /&gt;
Проблема верификации, связанная с синхронными (последовательными) схемами,  иная чем у  комбинационных конструкций. Все, что вам нужно знать о комбинационной схеме доступно на ее входах. Эталонная модель для  комбинационного устройства просто должна  вычислять F (X), где X представляет  входы  устройства и F – это реализуемая функция. Выходы же на последовательном устройстве  зависят от  входных сигналов и его внутреннего состояния. Дальнейшее  вычисление может изменить внутреннее состояние. Scoreboard должно отслеживать  внутреннее состояние тестируемого устройства и сравнить выходные данные.&lt;br /&gt;
&lt;br /&gt;
Комбинационное устройство может быть полностью проверено при подаче всех возможных комбинаций сигналов на входы. Для устройства  N входами, мы должны применить в общей сложности 2n  входных комбинаций. Число 2n  может быть большим, но вычисление такого количества  входов достаточно простое. Нам  просто необходимо иметь N-разрядный счетчик и посылать каждое значение счетчика на входы устройства. &lt;br /&gt;
Для последовательного устройства, понятие &amp;quot;done&amp;quot; должно распространяться и на покрытие не только  всевозможных сигналов на  входах, но и на число всевозможных внутренних состояний. Для устройства с n входами и m внутренних состояний, вы должны покрыть (2n входов) * (2m внутренних состояний),   2n+m – число всевозможных комбинаций внутренних состояний и входов. Для устройства с 64 входами  и одним 32-битный внутренним регистром, количество  комбинаций  296 -очень большое число, не так ли!&lt;br /&gt;
&lt;br /&gt;
Даже для очень большого числа комбинаций, проверка  не была бы слишком трудной, если  бы было возможно  просто увеличивать счетчик для получения всех комбинаций, как мы делаем с комбинационными устройствами. К сожалению, это не представляется возможным. Внутреннее состояние напрямую не доступны извне устройства. Оно может быть изменено только путем манипулированием входов. И теперь проблема заключается в том, как добраться до всех состояний в устройстве только через входы. Это сложная проблема, которая требует глубокого понимания устройства, для генерации последовательности входов для получения всех состояний.&lt;br /&gt;
&lt;br /&gt;
Поскольку трудно получить  все состояния, становится  очевидным вопрос, Можем ли мы упростить задачу путем сокращения числа состояний, которые мы должны получить, чтобы показать, что устройство работает правильно? Ответ: да. Теперь возникает другой вопрос, Как мы решим, какие состояния можно пропустить?&lt;br /&gt;
&lt;br /&gt;
Эта тема является сложной. Тем не менее, мы можем дать интуитивный ответ на вопрос. Состояния, которые могут быть отмечены как недоступные, при использовании формальной проверки или других методов, не должны быть покрыты. Проектировщик должен рассмотреть вопрос об упрощении схемы для удаления недоступных состояний, так как они не представляют никакой ценности. Состояния, которые имеют низкую вероятность быть достигнутыми, также могут быть исключены из плана проверки. Определение порогового значения вероятности и вероятностей состояний такое же искусство, как наука. Она включает в себя понимание того, как устройство используется и какие входы необходимы (по сравнению с которые возможны).&lt;br /&gt;
&lt;br /&gt;
Это также позволяет устранить состояния, которые являются функционально эквивалентными. Рассмотрим устройство пакет коммуникаций. В теории, каждое возможное значение полезной нагрузки пакета представляет собой различное состояние (или множества состояний), которое принимает схема, и оно должно быть покрыто во время проверки. Однако, это, вероятно, не будет преувеличением считать, что произвольное ненулевое значение, для всех намерений и целей, эквивалентно. Конечно, могут быть некоторые интересные случаи, которые должны быть проверены, как все нули, все единицы, конкретные значения, которые могли бы бросить алгоритмы коррекции ошибок, и так далее. Изменения данных становятся интереснее, когда они влияют на поток управления.&lt;br /&gt;
&lt;br /&gt;
В общем, это более важно, чтобы охватить управляющие состояния, чем состояние данных. Простым способом уменьшить число состояний, необходимых для покрытия схемы является разделения данных и управления. Для конкретного пути контроля, данные могут быть произвольными. По некоторых данных, вы можете захотеть изменить путь управления. Например, в ALU  вы можете разделить функции контроля записи данных  и получения данных из регистров и установление арифметической операции для выполнения из результатов конкретные арифметических операций. Использование направленного управления, вы можете сделать данные случайные или  рассмотреть данные в специфичных случаях, таких как деление на 0 или умножение на 1.&lt;br /&gt;
&lt;br /&gt;
Для сложных последовательных конструкций, определения того, какие состояния должны быть покрыты(и какие не должны) и, как получить  эти состояния с минимальными усилиями является проблемой.&lt;br /&gt;
&lt;br /&gt;
=== 3-х разрядный счетчик===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Схема, приведенная на рисунке 1-5, представляет собой 3-разрядный счетчик с асинхронным сбросом. Каждый раз, когда тактовый сигнал высокий, счетчик увеличивается. Схема состоит из трех переключательных триггеров, каждый из которых поддерживает один бит счета(??). Триггеры связаны с некоторой комбинационной логикой для формирования счетчика . Каждый триггер переключается, когда на входе T сигнал высокой. При низком сигнале на Т , триггер сохраняет свое текущее состояние. Когда активный низкий сброса установлен в 0, триггер переходит в состояние 0.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:15.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Код для счетчика содержится в двух модулях. Одним из них является простой переключательный триггер, а другой подключает триггера с необходимой логикой для  формирования счетчика. Первый пример показывает  переключательный триггер.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;36 module toggle_ff (output bit q, input t, rst_n, clk);&lt;br /&gt;
37&lt;br /&gt;
38 always @ (posedge clk or negedge rst_n)&lt;br /&gt;
39 if (!rst_n) q &amp;lt;= ‘0;&lt;br /&gt;
40 else if (t) q &amp;lt;= ~q;&lt;br /&gt;
41&lt;br /&gt;
42 endmodule&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Счетчик состоит из трех переключательных триггеров и логического элемента И.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;47 module counter (output [2:0] q, input rst_n, clk);&lt;br /&gt;
48&lt;br /&gt;
49 toggle_ff ff0 (q[0], 1’b1, rst_n, clk);&lt;br /&gt;
50 toggle_ff ff1 (q[1], q[0], rst_n, clk);&lt;br /&gt;
51 toggle_ff ff2 (q[2], t2, rst_n, clk);&lt;br /&gt;
52 and a1 (t2, q[0], q[1]);&lt;br /&gt;
53&lt;br /&gt;
54 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Схема проста, но имеет характеристики, которые являются общими с реальными проектами и которые требуют  внимания для надлежащей проверки дизайна. Основными характеристиками является то, что конструкция работает по тактовому сигналу, и что она сохраняет внутреннее состояние. Логический элемент  И  из предыдущего примера не сохраняет никаких состояний. Вся информация о том, что  делает схема, находится на ее входах. В конструкциях с внутренними данными, что это не так.&lt;br /&gt;
&lt;br /&gt;
Эта разница отражается на схеме нашего табло. Рисунок 1-6 показывает схему  testbench  для 3х разрядного счетчика.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:18.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Во многих отношениях, testbench   для 3-разрядного счетчика очень похож на testbench для логического элемента И.  Оба имеют Scoreboard, роль которого состоит в наблюдении, что делает схема, и определить, выполняется ли операция правильно. Оба имеют устройства для  DUT. Тем не менее, мы выполняем операции по-разному для этих конструкций. Мы используем Stimulus генератор для логического элемента И, но мы используем контроллер для 3-х разрядного счетчика. 3-х разрядный счетчик автономное устройство. Пока он подключен к тактовому сигналу, он будет продолжать считать. Таким образом, нам не нужен Stimulus генератор, который мы использовали для логического И. Вместо этого, контроллер управляет работой тестируемого устройства и  testbench. Контроллер обеспечивает начальный сброс, так что отсчет начинается с известного значения. Он также останавливает моделирование в соответствующее время.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;93 module control(output bit rst_n, input clk);&lt;br /&gt;
94&lt;br /&gt;
95 initial begin&lt;br /&gt;
96 rst_n &amp;lt;= 0;&lt;br /&gt;
97 @(posedge clk);&lt;br /&gt;
98 @(negedge clk);&lt;br /&gt;
99 rst_n &amp;lt;= 1;&lt;br /&gt;
100 repeat (10) @(posedge clk);&lt;br /&gt;
101 $finish;&lt;br /&gt;
102 end&lt;br /&gt;
103&lt;br /&gt;
104 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Табло должны отслеживать внутреннее состояние DUT. Это делается с помощью переменной count. Как и DUT, когда reset активирован, счетчик устанавливается в 0. Каждый тактовый цикл count увеличивается на единицу, и новое значение  сравнивается с count, поступающего с DUT.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;71 module scoreboard (input [2:0] q, input rst_n, clk );&lt;br /&gt;
72&lt;br /&gt;
73 int count;&lt;br /&gt;
74&lt;br /&gt;
75 always @(posedge clk or negedge rst_n) begin&lt;br /&gt;
76 if(!rst_n) count &amp;lt;= 0;&lt;br /&gt;
77 else begin&lt;br /&gt;
78 if (count == q)&lt;br /&gt;
79 $display(“time =%4t q = %3b count = %0d match!”,&lt;br /&gt;
80 $time, q, count);&lt;br /&gt;
81 else&lt;br /&gt;
82 $display(“time =%4t q = %3b count = %0d &amp;lt;-- no&lt;br /&gt;
match”,&lt;br /&gt;
83 $time, q, count);&lt;br /&gt;
84 count &amp;lt;= (count + 1) % 8;&lt;br /&gt;
85 end&lt;br /&gt;
86 end&lt;br /&gt;
87&lt;br /&gt;
88 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Табло имеет модель счетчика высокого уровня. Он использует целую переменную и оператор сложения (+) , чтобы сформировать счетчик вместо триггеров и элемента И. Каждый раз, при изменении тактового импульса, он увеличивает свой счет, так же как и RTL счетчика. Он также проверяет, соответствует ли  ее внутренний счетчик  выходу счетчика DUT.&lt;br /&gt;
&lt;br /&gt;
Для полноты, пример показывает тактовый генератор и модуль топ-уровня. Тактовый генератор просто инициализирует тактовый сигнал 0, а затем переключает его каждые 5 нс.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;&lt;br /&gt;
59 module clkgen(output bit clk);&lt;br /&gt;
60&lt;br /&gt;
61 initial begin&lt;br /&gt;
62 clk &amp;lt;= 0;&lt;br /&gt;
63 forever #5 clk = ~clk;&lt;br /&gt;
64 end&lt;br /&gt;
65&lt;br /&gt;
66 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Модуля верхнего уровня является типичным для большинства testbenches. Он соединяет DUT  и компоненты testbench.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;109 module top;&lt;br /&gt;
110&lt;br /&gt;
111 wire [2:0] q;&lt;br /&gt;
112&lt;br /&gt;
113 clkgen ckgn (clk);&lt;br /&gt;
114 counter cntr (q, rst_n, clk);&lt;br /&gt;
115 control ctrl (rst_n, clk);&lt;br /&gt;
116 scoreboard score (q, rst_n, clk);&lt;br /&gt;
117&lt;br /&gt;
118 endmodule&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
Мы показали простой testbench, который содержит элементы, используемые в гораздо более сложных testbenches. Последовательные схемы, которые поддерживают внутреннее состояние, требуют табло, которое работает параллельно с DUT. Табло выполняет те же вычисления что и DUT, но на более высоком уровне абстракции. Табло также сравнивает свои вычисления с вычислениями, полученными от DUT.&lt;br /&gt;
&lt;br /&gt;
== Многоуровневая организация Testbenches==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Так  как схема представляет собой совокупность  компонент проекта,  testbench представляет собой  совокупность компонент верификации. OVM определяет верификацию компонент, их структуру,интерфейс. В этом разделе описываются  OVM компоненты.&lt;br /&gt;
&lt;br /&gt;
OVM testbenches организованы в виде уровней. Самый нижний слой -устройство RTL с pin-level интерфейсом (с интерфейсом на уровне выводов). Выше находится уровень транзакторов (посредник), устройств, которые преобразуют из уровня транзакций в pin-level. Все компоненты, находящиеся на этом уровне, являются компонентами уровня транзакций. Диаграмма, приведенная ниже, иллюстрирует уровневую организацию тестовых программ. Блок слева — название уровня, блок справа — перечень типов компонентов, входящих в него. Вертикальные стрелки показывают, какие уровни взаимодействуют на прямую. Например, управляющий уровень взаимодействует с уровнями анализа, операций, транзакторов, но с DUT напрямую не может.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:23.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Вы также можете рассмотреть testbench как концентрическую организацию компонент. Внутреннему кольцу соответствует нижний уровень, а внешнему кольцу - верхний уровень. Для некоторых будет проще понять отношения между уровнями, используя диаграмму в виде списка связей.&lt;br /&gt;
&lt;br /&gt;
[[Файл:24.png]]&lt;br /&gt;
&lt;br /&gt;
=== Transactors(?????????????????????????)===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Роль посредников в testbench состоит в  преобразование потока транзакций в pin-level activity или наоборот. Транзакторы характеризуются наличием, по крайней  мере, одного pin-level интерфейса и хотя бы одного transaction-level интерфейса. Транзакторы могут принимать большое разнообразие форм, цветов и стилей. Мы сосредоточимся на мониторах, драйверах и передатчиках.&lt;br /&gt;
&lt;br /&gt;
'''Монитор'''. Монитор, как следует из названия, следит за шиной данных. Он следит за выводами и преобразует их в поток транзакций. Мониторы пассивные - это означает, что они не влияют на операции в DUT.&lt;br /&gt;
&lt;br /&gt;
'''Драйвер'''. Драйвер преобразует поток транзакций (или последовательность элементов) в pin-level activity.&lt;br /&gt;
&lt;br /&gt;
'''Передатчик'''.  Передатчик очень похож на драйвер, но он  реагирует на активность на выходе, а не на входную активность.&lt;br /&gt;
&lt;br /&gt;
===Операционные компоненты===&lt;br /&gt;
&lt;br /&gt;
Операционные компоненты – это набор компонент,которые обеспечивают все необходимые операции в DUT. Операционные компоненты отвечают за генерацию трафика для DUT. Все эти компоненты относятся к уровню транзакций и имеют только интерфейс уровня транзакций.Существуют различные варианты формирования сигнала, которые варьируются в зависимости от верифицируемого устройства. Мы рассмотрим три основных вида операционных компонентов: stimulus generators, masters, и slaves.&lt;br /&gt;
&lt;br /&gt;
'''Stimulus генераторы'''.  Генератор сигналов  создает поток транзакций для проверки DUT. Stimulus генераторы могут быть случайными, заданными или заданными случайно; они могут запускаться самостоятельно или контролируемо; и они могут быть независимыми или синхронизированными. Простейший stimulus генератор рандомизирует содержимое объекта запроса и посылает этот объект на драйвер. OVM также предоставляет модульные, динамические средства для построения сложных stimulus, называемыми последовательностями.&lt;br /&gt;
&lt;br /&gt;
'''Главное устройство'''.  Главное устройство - двунаправленная компонента, которая посылает запросы и принимает ответы.  Главное устройство инициирует активность. Как и Stimulus генераторы, оно может передавать отдельные рандомизированные транзакции или последовательности заданных или случайно заданных транзакций.  Главное устройство может использовать ответы для определения последующих действий.  Главное устройство также может быть реализовано как последовательность.&lt;br /&gt;
&lt;br /&gt;
'''Подчиненное устройство'''. Подчиненное устройство, как и главное устройство, является двунаправленной компонентой. Оно реагирует на запросы и возвращает ответы (в отличие от главного устройства, которое отправляют запросы, и принимает ответы).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:25.png]]&lt;br /&gt;
&lt;br /&gt;
=== Компоненты анализа===&lt;br /&gt;
&lt;br /&gt;
Компоненты анализа получают информацию о том, что происходит в testbench, и используют эту информацию, чтобы сделать некоторые заключения о правильности и полноты теста. Два наиболее распространенных вида анализа - это scoreboards и coverage collectors.&lt;br /&gt;
&lt;br /&gt;
'''Scoreboards'''. Табло используются для определения правильности DUT, чтобы ответить на does-it-work вопросы.Scoreboards перехватывает информацию, входящую и выходящую из DUT и определяют правильно ли DUT отвечает на данный stimulus.&lt;br /&gt;
&lt;br /&gt;
'''Coverage Collector'''. Coverage Collector используются для подсчета. Они перехватывают поток  транзакций, и считает транзакции или различные аспекты транзакций. Цель состоит в том, чтобы определить завершенность верификации, отвечая на are-we-done вопросы. Определенные параметры, которые coverage collectors считает, зависит от схемы и специфики тестов. Общие вещи, которые coverage collectors включают в себя: необработанные транзакции, транзакции, которые происходит в определенном сегменте адресного пространства, и протокол ошибок. Список неограничен.&lt;br /&gt;
&lt;br /&gt;
Coverage Collector могут также выполнять вычисления как часть полноты верификации. Например, coverage collectors может содержать скользящее среднее и среднее квадратичное отклонение данных, которые отслеживаются. Или они могут хранить отношения ошибок к выполнимым операциям.&lt;br /&gt;
&lt;br /&gt;
=== Контроллер===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Контроллеры формируют основной поток теста, и управляют работой. Как правило, контроллеры получают информацию от Scoreboard и coverage collectors, и  отправляют информацию  компонентам окружающей среды. &lt;br /&gt;
Например, контроллер может запустить Stimulus генератор, а затем ждать сигнала от coverage collectors, чтобы уведомить его, когда тест будет завершен. Контроллер, в свою очередь, останавливает Stimulus генератор. Более сложные вариации на эту тему возможны. В пример возможные конфигурации, контроллер инициализирует Stimulus генератор начальным набором ограничений и запускает его. При достижении определенного соотношения  типов пакетов, coverage collectors сигнализирует контроллеру. Вместо того, чтобы остановить Stimulus генератор, контроллер может отправить ему новый набор ограничений.&lt;br /&gt;
&lt;br /&gt;
== Два домена( Две области)==&lt;br /&gt;
&lt;br /&gt;
Мы можем разделить множество компонент в testbench на отдельные  области. Оперативная область - множество компонент, в том числе DUT, которые управляют DUT. Это Stimulus генератор, шина функциональной модели (BFM), и аналогичные компоненты, которые генерируют сигналы и возвращают ответы, которые управляют моделированием. DUT, ответчик, и драйвер операций, наряду с компонентами окружающей среды, которые непосредственно обращаются или отвечают драйверам и передатчикам ответа, включены в оперативную область. Остальные компоненты testbench –монитор транзакций, scoreboard, coverage collectors, и контроллера включены в область анализа. Это компоненты, которые собирают информацию из оперативной области.&lt;br /&gt;
&lt;br /&gt;
Данные должны быть перемещены из оперативной области в область анализа таким образом, чтобы не мешать работе DUT и сохранить оптимальный выбор событий. Это достигается с помощью специального интерфейса связи называемого портом анализа.  Порт анализа представляют собой особый вид  порта транзакций, в котором издатель передает данные одному или нескольким получателям. Издатель оповещает всех получателей, когда новые данные готовы.&lt;br /&gt;
&lt;br /&gt;
Одной из ключевых особенностей порта анализа  является то, что он имеет единственную функцию, write(). Анализ FIFO(????), каналы, используемые для подключения к порту анализа для анализа компонентов, неограниченные. Это гарантирует, что издатель не блокирует, а  передает свои данные в анализ FIFO в точно такой же дельта-цикл, в котором они были созданы.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:26.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как правило, оперативная и область анализа связаны портами анализа и интерфейсами управления и конфигурации. Порты анализа кран подачи выпускают данные относящиеся к операциям DUT. Эти данные могут включать шину транзакций, коммуникационные пакеты и информацию о состоянии (успех или ошибка конкретных операций). Компоненты  области анализа анализируют данные и принимают решения. Результатами этих решений могут быть сообщения оперативной области, используя  интерфейсы управления и конфигурирования. Интерфейсы управления и конфигурирования  могут быть использованы для запуска и остановки Stimulus генератор, изменения ограничений, изменения процента ошибок, или манипулирование другими параметрами, влияющих на работу testbench.&lt;br /&gt;
&lt;br /&gt;
== Заключение==&lt;br /&gt;
&lt;br /&gt;
В этой главе мы рассмотрели, как структурировать весь процесс проверки. Этот процесс основывается на двух фундаментальных вопросах, Does it work? и Are  we done? Простые примеры показали, как построить testbench  для аппаратного ответа на эти вопросы такими устройствами как генератор сигналов и табло. Далее будет показано, как применять на уровне транзакций методы моделирования для создания практических, масштабируемых, многоразовых  компонент testbench, которые отвечают на эти вопросы и показывают, как подключить их  testbenches.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%B2%D0%B5%D1%80%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D0%B8</id>
		<title>OVM/OVM методология/Основы верификации</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%B2%D0%B5%D1%80%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D0%B8"/>
				<updated>2013-03-05T13:25:22Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: /* Scoreboard */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
== Основы верификации==&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Функционально проверить устройство означает, сравнить цель разработчика с полученным  поведением на  их эквивалентность. Мы считаем устройство проверенным, когда, к всеобщему удовлетворению, оно работает в соответствии с целью разработчика. Этот основной принцип часто теряется при использовании testbenches, среды контроля, отладки, моделирования , и все другие средства, применяемые в современных технологических проверках. Чтобы узнать, работает ли верно устройство, вы должны сравнить его с некоторыми известными эталоном, который представляет цель разработчика. Каждый testbench имеет своего рода эталонную модель и средство для сравнения функциональность устройство с эталоном. &lt;br /&gt;
&lt;br /&gt;
	Когда мы говорим &amp;quot;устройство&amp;quot;, мы имеем в виду проверяемое устройство, часто называемое тестируемое устройство или DUT. Чтобы проверить, DUT, как правило, представлен  форме подходящей для производства, которое может быть перенесено на кремний с помощью автоматизированных и ручных средств. Мы различаем DUT от эскиза на обратной стороне салфетки или окончательного нанесенный на кристалл, ни один из которых не может быть проверен. Эталонная модель отражает цели разработчика, то есть то, что устройство должно делать.Эталон может принимать различные формы, такие как документ, описывающий работу DUT,  golden модель, которая содержит уникальный алгоритм, или assertions, которые представляют протокол.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:1.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для автоматизации сравнения поведения и эталона, оба должны быть представлены в форме, которую можно выполнить на компьютере с помощью некоторых программ, что делает  сравнение. &lt;br /&gt;
&lt;br /&gt;
==== Два вопроса====&lt;br /&gt;
&lt;br /&gt;
	Проверка устройства включает в себя два вопроса:Does it work? (Работает ли это устройство?) и Are we done? (Сделали ли мы?). Это основные, и некоторые сказали бы, очевидные вопросы. Тем не менее они &lt;br /&gt;
мотивируют всю механику каждой проверочного потока. Первый вопрос - это Does it work? Этот вопрос исходит от основной идеи верификации. Он спрашивает, Соответствует ли устройство эталону? Второй вопрос - Are we done? Он спрашивает, довольны ли мы сравнительным анализом полученной схемы и эталона для определения работает ли устройство согласно цели, если нет, то почему. Мы используем эти ценные вопросы создания основы для разработки эффективных testbenches.&lt;br /&gt;
&lt;br /&gt;
====Does it work?====&lt;br /&gt;
&lt;br /&gt;
	Does it work? Это не единственный вопрос, а категория вопросов, которые представляют природу DUT. Каждый проект будет иметь свой собственный набор Does-It-Work вопросов, чья роль заключается в определении правильного функционирования  устройства.  Функциональные вопросы определяют, ведет ли себя устройство должным образом в конкретных ситуациях. Мы получаем эти вопросы непосредственно из цели разработки устройства, и мы используем их, чтобы отразить цель проекта  в testbench.&lt;br /&gt;
&lt;br /&gt;
	Рассмотрим простой маршрутизатор пакетов в качестве примера. Это устройство маршрутизации пакетов от входа к одному из четырех портов вывода. Пакеты содержат адреса портов назначения и имеют различные длины полезной нагрузки. Каждый пакет имеет заголовок, трейлер, и два байта для циклического избыточного кода (CRC). Does-It-Work вопросы в этом случае должны быть следующие:&lt;br /&gt;
 &lt;br /&gt;
*пакет, поступающий на вход порта, адресованный на имя выходного порта 3, прибыл на порт  3? &lt;br /&gt;
*пакет длиной 16 пришел без изменений? &lt;br /&gt;
*CRC байты правильны, если полезная нагрузка [0f 73 a8 c2 3e 57 11 0a 88 FF 00 00 33 2b 4c 89]?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	Это простой пример полного набора вопросов. Для устройства даже такого простого как этот гипотетическое маршрутизатор, набор Does-It-Work вопросов может быть длинным. Чтобы построить план проверки и testbench, который поддерживает план, вы должны сначала перечислить все вопросы или показать как получит полный набор вопросов, а затем выберите те, которые являются интересными.&lt;br /&gt;
&lt;br /&gt;
	Продолжая пример маршрутизатора  пакетов,для того чтобы перечислить все Does-It-Work вопросы, вы можете создать таблицу:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:2.png]]&lt;br /&gt;
&lt;br /&gt;
Приведенная выше таблица содержит два вида вопросов, те,  на которые мы можем ответить сразу и те, которые мы можно разбить на более мелкие вопросы. Вопрос 1 из ряда вопросов, которые можно  представить в виде:&lt;br /&gt;
&lt;br /&gt;
[[Файл:3.png]]&lt;br /&gt;
[[Файл:4.png]]&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что мы формулируем все вопросы, так что на них можно ответить да или нет. В конце концов, схема  работает либо нет – она либо  готова для синтеза, размещения  и трассировке, либо нет. Если вы можете ответить на все вопросы утвердительно, тогда проект готов для следующего производственного шага.&lt;br /&gt;
При разработке набора does-it-work  вопросов, не забудьте сформулировать их  таким образом, чтобы на них можно было ответить да или нет. Ответ да положителен, то есть, ответить  да означает, что устройство работает правильно. Это будет сделать легче, чем пытаться отслеживать  на какие вопросы нужно ответить  да, и на какие следует ответить нет. Такой  вопрос как  Передает ли  маршрутизатор ошибочные пакеты?  требует ответа нет, чтобы  считаться успешным. Лучшей формулировкой вопроса будет: Отклоняет ли маршрутизатор ошибочные  пакеты? Вы должны сделать вопросы наиболее конкретными,  даже если лучшее звучание будет:  Когда ошибочный пакет попадает на входной порт,  маршрутизатор обнаруживает его, подает сигнал ошибки и отклоняет этот пакет? Имейте в виду, что более конкретные вопросы более подробно описывают устройство. Ваш testbench должен дать ответ да или нет.&lt;br /&gt;
Тщательно сформулированный да или нет вопрос содержит свои собственные критерии успеха. Он сообщает, что получает ответа да. Такой  вопрос  как, Средняя задержка менее 27 тактов? содержит метрику, 27 тактов, и форму сравнения, меньше. Если вопрос (неправильно) сформулирован, например, Какова средняя задержка прохождения пакетов через маршрутизатор? мы не будем знать, что считается приемлемым. Чтобы ответить на ваш вопрос, вы в первую очередь должны быть в состоянии определить среднее время задержки. Только при правильной постановке вопроса мы знаем, как сделать сравнение, чтобы определить, является ли результат  правильным. Метрика сама по себе не говорит нам о том,   функционирует ли схема как задумано. Когда мы сравниваем измеренное значение со значением в  спецификации, 27 тактов в этом примере, мы можем определить, работает ли схема.&lt;br /&gt;
Как это часто бывает, это не практично  перечислять каждый does-it-work вопрос. Чтобы убедиться, что каждое слово в памяти 1 Мб можно записать и читать,  непрактично и не необходимо записать один миллион вопросов. Вместо этого, вопрос-генератор,  вопрос, который генерирует многие другие, занимает место  миллиона отдельных вопросов. Может каждый  миллион слов в памяти быть успешно записан и считан? -является вопросом-генератором.&lt;br /&gt;
Другие вопросы могут сами по себе  представлять  классы вопросов. Вопрос 3, Является ли вычисление CRC корректным для каждого пакета? является примером такого вопроса. Тестирование CRC вычисления требует ряда тщательно продуманных тестов, для  определения является ли вычисления CRC корректным во всех случаях. &lt;br /&gt;
&lt;br /&gt;
==== Are We Done?====&lt;br /&gt;
&lt;br /&gt;
Для определения ответа на вопрос  Are we done?, мы должны знать, что мы ответили на достаточное количество does-it-work  вопросов, чтобы  утверждать, что у нас есть проверенная схема. Мы начинаем эту задачу путем разделения на приоритеты всех does-it-work  вопросов по двум направлениям:&lt;br /&gt;
&lt;br /&gt;
[[Файл:5.png]]&lt;br /&gt;
&lt;br /&gt;
Искусство создания testbench требует, чтобы на начальном этапе, мы определили множество вопросов и отсортировали их в соответствии с ценностью возвращаемой информации с точки зрения проверки схемы. Следующий шаг заключается в создании механизма, который  будет отвечать на вопросы и определять  на какие из них были даны ответы (и на какие нет).&lt;br /&gt;
&lt;br /&gt;
Are-we-done вопросы также называется вопросами функционального покрытия, вопросы, которые устанавливают  все ли состояния охватывают тесты с точки зрения  функций схемы. Как  и  does-it-work  вопросы, мы можем также разбить вопросы функционального охвата на более подробные вопросы. И как вопросы функциональной корректности, вопросы функционального охвата  должны также иметь ответы да или нет. Приведенный ниже список включает в себя примеры вопросы функционального охвата:&lt;br /&gt;
&lt;br /&gt;
•	Выполняется ли каждая команда процессора, по крайней мере, один раз?&lt;br /&gt;
&lt;br /&gt;
•	Проходит, по крайней мере, один пакет  от каждого входного порта до каждого выходного порта?&lt;br /&gt;
&lt;br /&gt;
•	Можно ли обратиться к памяти  с набором адресов, которые содержат каждый бит адреса как 1 и затем каждый  бит адреса 0, не включая 0xffffffff и 0x00000000?(?????????)&lt;br /&gt;
&lt;br /&gt;
Другим мнением об этих вопросах является то, о чем они спрашивают, Получен ли утвердительный ответ на необходимый does-it-work  вопрос? Когда мы думаем о функциональном охвате с этой точки зрения, то имеем виду покрытие множества does-it-work  вопросов. Кроме того, вопросы охвата распознают метрику и порог для сравнения. Покрытие будет  (то есть, охват вопрос можно ответить, да), когда метрика достигает порога.&lt;br /&gt;
&lt;br /&gt;
Таким образом, искусство построения testbench начинается с плана тестирования.  План тестирования начинается с тщательно продуманным набором does-it-work и Are-we-done вопросов.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Двухпетлевый поток (???????????????)====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Процесс ответа на does-it-work и Are-we-done вопросы может быть описан  простой схемой, как показано на рисунке 1-2. Все обусловлено функциональной спецификацией на проектирование. Из функциональной спецификации, вы можете получить саму схему и план верификации.  План верификации запускает testbench.&lt;br /&gt;
&lt;br /&gt;
Поток состоит из двух циклов, does-it-work цикла и Are-we-done цикл. Оба цикла начинаются с моделирования. Моделирование тестирует  схему при помощи  testbench и генерирует информацию, которую мы используем для ответа на вопросы. Сначала мы спрашиваем, Это работает? Если ответ нет, то необходимо отладить проект. Процесс отладки схемы может привести к изменениям  в реализации схемы.&lt;br /&gt;
Как только схема работает, то пора ответить на вопрос, Are-we-done? Мы ответим на этот вопрос, собирая информацию охвата и сравнения его с порогом, указанным в тестовом плане. Если мы не достигнем  порогов, то ответ нет, и мы должны изменить testbench для увеличения охвата. Тогда мы моделируем снова.&lt;br /&gt;
&lt;br /&gt;
Изменение testbench или воздействия может вызвать другие скрытые ошибки в схеме. Последующие итерации по циклу могут заставить нас вернуться к does-it-work циклу снова, чтобы исправить новые ошибки, которые появятся. Как вы можете видеть, полный процесс проверки колеблется вперед и назад между does-it-work и Are-we-done циклами, пока мы не можем утвердительно ответить на все вопросы  обоих категорий.&lt;br /&gt;
&lt;br /&gt;
[[Файл:6.png]]&lt;br /&gt;
&lt;br /&gt;
В идеальном мире, схема не имеет ошибок и с покрытием всегда полное, так что вам только надо пройти по каждому циклу один раз, чтобы получить ответы да на оба вопроса. В реальном мире, это может занять много итераций для достижения двух ответов да. Одна из целей хорошего потока проверки свести к минимуму количество итераций для выполнения верификации проекта в кратчайшие сроки с использованием наименьшего количества ресурсов.&lt;br /&gt;
&lt;br /&gt;
== Первый Testbench==&lt;br /&gt;
&lt;br /&gt;
Давайте начнем с проверки одного из самых фундаментальных цифровых электронных устройств, схемы логического И. Логическое И вычисляет логическое умножение   входов. Функция этого устройства является тривиальной, а на практике, вряд ли должна иметь свой собственный  testbench. Так как она тривиальна, мы можем использовать ее для иллюстрации некоторых основных принципов проверки без необходимости вникать в более сложную схему.&lt;br /&gt;
&lt;br /&gt;
Рисунок 1-3 показывает условное обозначение  логического элемента умножения с двумя входами. Логический элемент имеет два входа, А и В, и один выход Y. Устройство вычисляет логическое умножение  А и В и помещает результат в Y.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:7.png]]&lt;br /&gt;
&lt;br /&gt;
Следующая таблица истинности описывает функцию устройства.&lt;br /&gt;
&lt;br /&gt;
[[Файл:8.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Таблица истинности является исчерпывающей: она содержит все возможные входы А и В и поэтому, все возможные значения для выхода Y.&lt;br /&gt;
Наша задача состоит в том, чтобы доказать, что наша схема,  логического И, работает правильно. Чтобы убедиться, что он действительно выполняет функцию логического умножения  правильно, нам в первую очередь необходимо перечислим вопросы. Таблица истинности поможет нам создать множество вопросов, необходимых для проверки. Каждая строка таблицы содержит вход для А и В и ожидаемый выход для Y. Так как таблица является исчерпывающей, наш вопрос-генератор будет:  Для каждой ли строки в таблице истинности, когда мы получаем значения А и B, определенные в этой строке,  устройство вычисляет ожидаемый результат для Y? Для ответа на Are-we-done вопрос,  мы определяем, проверили ли  мы  каждую строку в таблице истинности, и получил ли утвердительный ответ does-itwork вопрос для этой строки. Наш Are-we-done вопрос- это Работает на всех наборах?&lt;br /&gt;
Для автоматизации ответов как на does-itwork  та  и на Are-we-done  вопросы,  нам нужна следующая атрибутика:&lt;br /&gt;
&lt;br /&gt;
•	Модель, которая представляет DUT (в данном случае, логическое И)&lt;br /&gt;
&lt;br /&gt;
•	Концепция конструкции в форме, которую мы можем кодифицировать  как эталонную модель&lt;br /&gt;
&lt;br /&gt;
•	Некоторое воздействие для проверки схемы&lt;br /&gt;
&lt;br /&gt;
•	Способ сравнить результат с концепцией конструкции&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:9.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В то время как наш маленький testbench прост, он содержит ключевые элементы, которые можно найти в большинстве testbenches любого уровня сложности. Ключевыми элементами являются следующие:&lt;br /&gt;
&lt;br /&gt;
•	DUT&lt;br /&gt;
&lt;br /&gt;
•	Stimulus генератор, который генерирует последовательность сигналов для DUT&lt;br /&gt;
&lt;br /&gt;
•	Scoreboard, которое изображает  эталонную модель&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Табло следит за входами и выходом тестируемого устройства, выполняет же функции, что DUT за исключением более высокого уровня абстракции, и определяет, совпадает ли DUT и эталонная модель. Scoreboard помогает нам ответить на does-itwork  вопросы.&lt;br /&gt;
&lt;br /&gt;
=== DUT===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DUT- это наше логическое И с двумя входами. Мы реализуем логическое И, как модуль с двумя входами А и В, и одним выходом Y.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;44 module and2 (&lt;br /&gt;
45 output bit Y,&lt;br /&gt;
46 input A, B);&lt;br /&gt;
47&lt;br /&gt;
48 initial Y = A &amp;amp; B;&lt;br /&gt;
49&lt;br /&gt;
50 always @* Y = #1 A &amp;amp; B;&lt;br /&gt;
51 endmodule&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stimulus генератор в этом примере создает заданные сигналы. Каждое новое значение вычисляются в определенном порядке. Позже, мы будем смотреть на  генератор случайных сигналов, которые, как следует из их названия, генерирует случайные значения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;81 module stimulus(output bit A, B);&lt;br /&gt;
82&lt;br /&gt;
83 bit [1:0] stimulus_count = 0;&lt;br /&gt;
84&lt;br /&gt;
85 always&lt;br /&gt;
86 #10 {A,B} = stimulus_count++;&lt;br /&gt;
87&lt;br /&gt;
88 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Цель Stimulus генератор - получение значений на вход  DUT. Сигналы, двухбитная величина, содержащая значение для А и B. После того как он увеличивается при каждой последующей итерации, младший бит посылает на А, а старший бит на B.&lt;br /&gt;
&lt;br /&gt;
=== Scoreboard===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Scoreboard несет ответственность за ответы на  does-it-work  вопросы. Оно следит за работой DUT и сообщает работает ли оно корректно.  Обратите внимание на то, что структура Scoreboard поразительно похожа на структуру DUT. Это имеет смысл, когда вы считаете, что назначением Scoreboard является отслеживание активности DUT  и определение, работает ли DUT  как  ожидалось.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;59 module scoreboard(input bit Y, A, B);&lt;br /&gt;
60&lt;br /&gt;
61 reg Y_sb, truth_table[2][2];&lt;br /&gt;
62&lt;br /&gt;
63 initial begin&lt;br /&gt;
64 truth_table[0][0] = 0;&lt;br /&gt;
65 truth_table[0][1] = 0;&lt;br /&gt;
66 truth_table[1][0] = 0;&lt;br /&gt;
67 truth_table[1][1] = 1;&lt;br /&gt;
68 end&lt;br /&gt;
69&lt;br /&gt;
70 always @(A or B) begin&lt;br /&gt;
71 Y_sb = truth_table[A][B];&lt;br /&gt;
72 #2 $display(“@%4t - %b%b : Y_sb=%b, Y=%b (%0s)”,&lt;br /&gt;
73 $time, A, B, Y_sb, Y,&lt;br /&gt;
74 ((Y_sb == Y) ? “Match” : “Mis-match”));&lt;br /&gt;
75 end&lt;br /&gt;
76 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Scoreboard отображает пины всех входов. На Scoreboard не вызывает воздействия на  схему. Оно пассивно наблюдает за входами и выходами тестируемого устройства. &lt;br /&gt;
&lt;br /&gt;
Модуль верхнего уровня, как показано ниже, является полностью структурированным, он содержит  экземпляр тестируемого устройства, Scoreboard и Stimulus генератор,  с кодом, необходимым для их подключения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;93 module top;&lt;br /&gt;
94 stimulus s(A, B);&lt;br /&gt;
95 and2 a(Y, A, B);&lt;br /&gt;
96 scoreboard sb(Y, A, B);&lt;br /&gt;
97&lt;br /&gt;
98 initial&lt;br /&gt;
99 #100 $finish(2);&lt;br /&gt;
100 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Когда мы запускаем моделирования для нескольких итераций, вот что мы получаем:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:14.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Каждое сообщение состоит из двух частей. В первой части показаны применяемые сигналы. Вторая часть показывает результаты проверки Scoreboard, которое сравнивает соответствие DUT с  необходимыми результатами. Мы используем двоеточие для разделения двух частей. &lt;br /&gt;
&lt;br /&gt;
Этот простой  testbench  демонстрирует использование Stimulus генератор и Scoreboard, которое служит в качестве эталона. Несмотря на то, DUT – логическое И, все элементы полного testbench  присутствуют.&lt;br /&gt;
&lt;br /&gt;
== Второй Testbench==&lt;br /&gt;
&lt;br /&gt;
В предыдущий пример показывает элементарные понятия проверки с использованием  комбинационного дизайна.  Комбинационные конструкций,  по  природе, не поддерживают никаких статических данных. В нашем втором примере мы рассмотрим чуть более сложную конструкцию, которая сохраняет данные о состояниях и использует  тактовый сигнал  для перехода  между состояниями.&lt;br /&gt;
&lt;br /&gt;
Проблема верификации, связанная с синхронными (последовательными) схемами,  иная чем у  комбинационных конструкций. Все, что вам нужно знать о комбинационной схеме доступно на ее входах. Эталонная модель для  комбинационного устройства просто должна  вычислять F (X), где X представляет  входы  устройства и F – это реализуемая функция. Выходы же на последовательном устройстве  зависят от  входных сигналов и его внутреннего состояния. Дальнейшее  вычисление может изменить внутреннее состояние. Scoreboard должно отслеживать  внутреннее состояние тестируемого устройства и сравнить выходные данные.&lt;br /&gt;
&lt;br /&gt;
Комбинационное устройство может быть полностью проверено при подаче всех возможных комбинаций сигналов на входы. Для устройства  N входами, мы должны применить в общей сложности 2n  входных комбинаций. Число 2n  может быть большим, но вычисление такого количества  входов достаточно простое. Нам  просто необходимо иметь N-разрядный счетчик и посылать каждое значение счетчика на входы устройства. &lt;br /&gt;
Для последовательного устройства, понятие &amp;quot;done&amp;quot; должно распространяться и на покрытие не только  всевозможных сигналов на  входах, но и на число всевозможных внутренних состояний. Для устройства с n входами и m внутренних состояний, вы должны покрыть (2n входов) * (2m внутренних состояний),   2n+m – число всевозможных комбинаций внутренних состояний и входов. Для устройства с 64 входами  и одним 32-битный внутренним регистром, количество  комбинаций  296 -очень большое число, не так ли!&lt;br /&gt;
&lt;br /&gt;
Даже для очень большого числа комбинаций, проверка  не была бы слишком трудной, если  бы было возможно  просто увеличивать счетчик для получения всех комбинаций, как мы делаем с комбинационными устройствами. К сожалению, это не представляется возможным. Внутреннее состояние напрямую не доступны извне устройства. Оно может быть изменено только путем манипулированием входов. И теперь проблема заключается в том, как добраться до всех состояний в устройстве только через входы. Это сложная проблема, которая требует глубокого понимания устройства, для генерации последовательности входов для получения всех состояний.&lt;br /&gt;
&lt;br /&gt;
Поскольку трудно получить  все состояния, становится  очевидным вопрос, Можем ли мы упростить задачу путем сокращения числа состояний, которые мы должны получить, чтобы показать, что устройство работает правильно? Ответ: да. Теперь возникает другой вопрос, Как мы решим, какие состояния можно пропустить?&lt;br /&gt;
&lt;br /&gt;
Эта тема является сложной. Тем не менее, мы можем дать интуитивный ответ на вопрос. Состояния, которые могут быть отмечены как недоступные, при использовании формальной проверки или других методов, не должны быть покрыты. Проектировщик должен рассмотреть вопрос об упрощении схемы для удаления недоступных состояний, так как они не представляют никакой ценности. Состояния, которые имеют низкую вероятность быть достигнутыми, также могут быть исключены из плана проверки. Определение порогового значения вероятности и вероятностей состояний такое же искусство, как наука. Она включает в себя понимание того, как устройство используется и какие входы необходимы (по сравнению с которые возможны).&lt;br /&gt;
&lt;br /&gt;
Это также позволяет устранить состояния, которые являются функционально эквивалентными. Рассмотрим устройство пакет коммуникаций. В теории, каждое возможное значение полезной нагрузки пакета представляет собой различное состояние (или множества состояний), которое принимает схема, и оно должно быть покрыто во время проверки. Однако, это, вероятно, не будет преувеличением считать, что произвольное ненулевое значение, для всех намерений и целей, эквивалентно. Конечно, могут быть некоторые интересные случаи, которые должны быть проверены, как все нули, все единицы, конкретные значения, которые могли бы бросить алгоритмы коррекции ошибок, и так далее. Изменения данных становятся интереснее, когда они влияют на поток управления.&lt;br /&gt;
&lt;br /&gt;
В общем, это более важно, чтобы охватить управляющие состояния, чем состояние данных. Простым способом уменьшить число состояний, необходимых для покрытия схемы является разделения данных и управления. Для конкретного пути контроля, данные могут быть произвольными. По некоторых данных, вы можете захотеть изменить путь управления. Например, в ALU  вы можете разделить функции контроля записи данных  и получения данных из регистров и установление арифметической операции для выполнения из результатов конкретные арифметических операций. Использование направленного управления, вы можете сделать данные случайные или  рассмотреть данные в специфичных случаях, таких как деление на 0 или умножение на 1.&lt;br /&gt;
&lt;br /&gt;
Для сложных последовательных конструкций, определения того, какие состояния должны быть покрыты(и какие не должны) и, как получить  эти состояния с минимальными усилиями является проблемой.&lt;br /&gt;
&lt;br /&gt;
=== 3-х разрядный счетчик===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Схема, приведенная на рисунке 1-5, представляет собой 3-разрядный счетчик с асинхронным сбросом. Каждый раз, когда тактовый сигнал высокий, счетчик увеличивается. Схема состоит из трех переключательных триггеров, каждый из которых поддерживает один бит счета(??). Триггеры связаны с некоторой комбинационной логикой для формирования счетчика . Каждый триггер переключается, когда на входе T сигнал высокой. При низком сигнале на Т , триггер сохраняет свое текущее состояние. Когда активный низкий сброса установлен в 0, триггер переходит в состояние 0.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:15.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Код для счетчика содержится в двух модулях. Одним из них является простой переключательный триггер, а другой подключает триггера с необходимой логикой для  формирования счетчика. Первый пример показывает  переключательный триггер.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:16.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Счетчик состоит из трех переключательных триггеров и логического элемента И.&lt;br /&gt;
&lt;br /&gt;
[[Файл:17.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Схема проста, но имеет характеристики, которые являются общими с реальными проектами и которые требуют  внимания для надлежащей проверки дизайна. Основными характеристиками является то, что конструкция работает по тактовому сигналу, и что она сохраняет внутреннее состояние. Логический элемент  И  из предыдущего примера не сохраняет никаких состояний. Вся информация о том, что  делает схема, находится на ее входах. В конструкциях с внутренними данными, что это не так.&lt;br /&gt;
&lt;br /&gt;
Эта разница отражается на схеме нашего табло. Рисунок 1-6 показывает схему  testbench  для 3х разрядного счетчика.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:18.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Во многих отношениях, testbench   для 3-разрядного счетчика очень похож на testbench для логического элемента И.  Оба имеют Scoreboard, роль которого состоит в наблюдении, что делает схема, и определить, выполняется ли операция правильно. Оба имеют устройства для  DUT. Тем не менее, мы выполняем операции по-разному для этих конструкций. Мы используем Stimulus генератор для логического элемента И, но мы используем контроллер для 3-х разрядного счетчика. 3-х разрядный счетчик автономное устройство. Пока он подключен к тактовому сигналу, он будет продолжать считать. Таким образом, нам не нужен Stimulus генератор, который мы использовали для логического И. Вместо этого, контроллер управляет работой тестируемого устройства и  testbench. Контроллер обеспечивает начальный сброс, так что отсчет начинается с известного значения. Он также останавливает моделирование в соответствующее время.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:19.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Табло должны отслеживать внутреннее состояние DUT. Это делается с помощью переменной count. Как и DUT, когда reset активирован, счетчик устанавливается в 0. Каждый тактовый цикл count увеличивается на единицу, и новое значение  сравнивается с count, поступающего с DUT.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:20.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Табло имеет модель счетчика высокого уровня. Он использует целую переменную и оператор сложения (+) , чтобы сформировать счетчик вместо триггеров и элемента И. Каждый раз, при изменении тактового импульса, он увеличивает свой счет, так же как и RTL счетчика. Он также проверяет, соответствует ли  ее внутренний счетчик  выходу счетчика DUT.&lt;br /&gt;
&lt;br /&gt;
Для полноты, пример показывает тактовый генератор и модуль топ-уровня. Тактовый генератор просто инициализирует тактовый сигнал 0, а затем переключает его каждые 5 нс.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:21.png]]&lt;br /&gt;
&lt;br /&gt;
Модуля верхнего уровня является типичным для большинства testbenches. Он соединяет DUT  и компоненты testbench.&lt;br /&gt;
&lt;br /&gt;
[[Файл:22.png]]&lt;br /&gt;
&lt;br /&gt;
Мы показали простой testbench, который содержит элементы, используемые в гораздо более сложных testbenches. Последовательные схемы, которые поддерживают внутреннее состояние, требуют табло, которое работает параллельно с DUT. Табло выполняет те же вычисления что и DUT, но на более высоком уровне абстракции. Табло также сравнивает свои вычисления с вычислениями, полученными от DUT.&lt;br /&gt;
&lt;br /&gt;
== Многоуровневая организация Testbenches==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Так  как схема представляет собой совокупность  компонент проекта,  testbench представляет собой  совокупность компонент верификации. OVM определяет верификацию компонент, их структуру,интерфейс. В этом разделе описываются  OVM компоненты.&lt;br /&gt;
&lt;br /&gt;
OVM testbenches организованы в виде уровней. Самый нижний слой -устройство RTL с pin-level интерфейсом (с интерфейсом на уровне выводов). Выше находится уровень транзакторов (посредник), устройств, которые преобразуют из уровня транзакций в pin-level. Все компоненты, находящиеся на этом уровне, являются компонентами уровня транзакций. Диаграмма, приведенная ниже, иллюстрирует уровневую организацию тестовых программ. Блок слева — название уровня, блок справа — перечень типов компонентов, входящих в него. Вертикальные стрелки показывают, какие уровни взаимодействуют на прямую. Например, управляющий уровень взаимодействует с уровнями анализа, операций, транзакторов, но с DUT напрямую не может.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:23.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Вы также можете рассмотреть testbench как концентрическую организацию компонент. Внутреннему кольцу соответствует нижний уровень, а внешнему кольцу - верхний уровень. Для некоторых будет проще понять отношения между уровнями, используя диаграмму в виде списка связей.&lt;br /&gt;
&lt;br /&gt;
[[Файл:24.png]]&lt;br /&gt;
&lt;br /&gt;
=== Transactors(?????????????????????????)===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Роль посредников в testbench состоит в  преобразование потока транзакций в pin-level activity или наоборот. Транзакторы характеризуются наличием, по крайней  мере, одного pin-level интерфейса и хотя бы одного transaction-level интерфейса. Транзакторы могут принимать большое разнообразие форм, цветов и стилей. Мы сосредоточимся на мониторах, драйверах и передатчиках.&lt;br /&gt;
&lt;br /&gt;
'''Монитор'''. Монитор, как следует из названия, следит за шиной данных. Он следит за выводами и преобразует их в поток транзакций. Мониторы пассивные - это означает, что они не влияют на операции в DUT.&lt;br /&gt;
&lt;br /&gt;
'''Драйвер'''. Драйвер преобразует поток транзакций (или последовательность элементов) в pin-level activity.&lt;br /&gt;
&lt;br /&gt;
'''Передатчик'''.  Передатчик очень похож на драйвер, но он  реагирует на активность на выходе, а не на входную активность.&lt;br /&gt;
&lt;br /&gt;
===Операционные компоненты===&lt;br /&gt;
&lt;br /&gt;
Операционные компоненты – это набор компонент,которые обеспечивают все необходимые операции в DUT. Операционные компоненты отвечают за генерацию трафика для DUT. Все эти компоненты относятся к уровню транзакций и имеют только интерфейс уровня транзакций.Существуют различные варианты формирования сигнала, которые варьируются в зависимости от верифицируемого устройства. Мы рассмотрим три основных вида операционных компонентов: stimulus generators, masters, и slaves.&lt;br /&gt;
&lt;br /&gt;
'''Stimulus генераторы'''.  Генератор сигналов  создает поток транзакций для проверки DUT. Stimulus генераторы могут быть случайными, заданными или заданными случайно; они могут запускаться самостоятельно или контролируемо; и они могут быть независимыми или синхронизированными. Простейший stimulus генератор рандомизирует содержимое объекта запроса и посылает этот объект на драйвер. OVM также предоставляет модульные, динамические средства для построения сложных stimulus, называемыми последовательностями.&lt;br /&gt;
&lt;br /&gt;
'''Главное устройство'''.  Главное устройство - двунаправленная компонента, которая посылает запросы и принимает ответы.  Главное устройство инициирует активность. Как и Stimulus генераторы, оно может передавать отдельные рандомизированные транзакции или последовательности заданных или случайно заданных транзакций.  Главное устройство может использовать ответы для определения последующих действий.  Главное устройство также может быть реализовано как последовательность.&lt;br /&gt;
&lt;br /&gt;
'''Подчиненное устройство'''. Подчиненное устройство, как и главное устройство, является двунаправленной компонентой. Оно реагирует на запросы и возвращает ответы (в отличие от главного устройства, которое отправляют запросы, и принимает ответы).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:25.png]]&lt;br /&gt;
&lt;br /&gt;
=== Компоненты анализа===&lt;br /&gt;
&lt;br /&gt;
Компоненты анализа получают информацию о том, что происходит в testbench, и используют эту информацию, чтобы сделать некоторые заключения о правильности и полноты теста. Два наиболее распространенных вида анализа - это scoreboards и coverage collectors.&lt;br /&gt;
&lt;br /&gt;
'''Scoreboards'''. Табло используются для определения правильности DUT, чтобы ответить на does-it-work вопросы.Scoreboards перехватывает информацию, входящую и выходящую из DUT и определяют правильно ли DUT отвечает на данный stimulus.&lt;br /&gt;
&lt;br /&gt;
'''Coverage Collector'''. Coverage Collector используются для подсчета. Они перехватывают поток  транзакций, и считает транзакции или различные аспекты транзакций. Цель состоит в том, чтобы определить завершенность верификации, отвечая на are-we-done вопросы. Определенные параметры, которые coverage collectors считает, зависит от схемы и специфики тестов. Общие вещи, которые coverage collectors включают в себя: необработанные транзакции, транзакции, которые происходит в определенном сегменте адресного пространства, и протокол ошибок. Список неограничен.&lt;br /&gt;
&lt;br /&gt;
Coverage Collector могут также выполнять вычисления как часть полноты верификации. Например, coverage collectors может содержать скользящее среднее и среднее квадратичное отклонение данных, которые отслеживаются. Или они могут хранить отношения ошибок к выполнимым операциям.&lt;br /&gt;
&lt;br /&gt;
=== Контроллер===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Контроллеры формируют основной поток теста, и управляют работой. Как правило, контроллеры получают информацию от Scoreboard и coverage collectors, и  отправляют информацию  компонентам окружающей среды. &lt;br /&gt;
Например, контроллер может запустить Stimulus генератор, а затем ждать сигнала от coverage collectors, чтобы уведомить его, когда тест будет завершен. Контроллер, в свою очередь, останавливает Stimulus генератор. Более сложные вариации на эту тему возможны. В пример возможные конфигурации, контроллер инициализирует Stimulus генератор начальным набором ограничений и запускает его. При достижении определенного соотношения  типов пакетов, coverage collectors сигнализирует контроллеру. Вместо того, чтобы остановить Stimulus генератор, контроллер может отправить ему новый набор ограничений.&lt;br /&gt;
&lt;br /&gt;
== Два домена( Две области)==&lt;br /&gt;
&lt;br /&gt;
Мы можем разделить множество компонент в testbench на отдельные  области. Оперативная область - множество компонент, в том числе DUT, которые управляют DUT. Это Stimulus генератор, шина функциональной модели (BFM), и аналогичные компоненты, которые генерируют сигналы и возвращают ответы, которые управляют моделированием. DUT, ответчик, и драйвер операций, наряду с компонентами окружающей среды, которые непосредственно обращаются или отвечают драйверам и передатчикам ответа, включены в оперативную область. Остальные компоненты testbench –монитор транзакций, scoreboard, coverage collectors, и контроллера включены в область анализа. Это компоненты, которые собирают информацию из оперативной области.&lt;br /&gt;
&lt;br /&gt;
Данные должны быть перемещены из оперативной области в область анализа таким образом, чтобы не мешать работе DUT и сохранить оптимальный выбор событий. Это достигается с помощью специального интерфейса связи называемого портом анализа.  Порт анализа представляют собой особый вид  порта транзакций, в котором издатель передает данные одному или нескольким получателям. Издатель оповещает всех получателей, когда новые данные готовы.&lt;br /&gt;
&lt;br /&gt;
Одной из ключевых особенностей порта анализа  является то, что он имеет единственную функцию, write(). Анализ FIFO(????), каналы, используемые для подключения к порту анализа для анализа компонентов, неограниченные. Это гарантирует, что издатель не блокирует, а  передает свои данные в анализ FIFO в точно такой же дельта-цикл, в котором они были созданы.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:26.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как правило, оперативная и область анализа связаны портами анализа и интерфейсами управления и конфигурации. Порты анализа кран подачи выпускают данные относящиеся к операциям DUT. Эти данные могут включать шину транзакций, коммуникационные пакеты и информацию о состоянии (успех или ошибка конкретных операций). Компоненты  области анализа анализируют данные и принимают решения. Результатами этих решений могут быть сообщения оперативной области, используя  интерфейсы управления и конфигурирования. Интерфейсы управления и конфигурирования  могут быть использованы для запуска и остановки Stimulus генератор, изменения ограничений, изменения процента ошибок, или манипулирование другими параметрами, влияющих на работу testbench.&lt;br /&gt;
&lt;br /&gt;
== Заключение==&lt;br /&gt;
&lt;br /&gt;
В этой главе мы рассмотрели, как структурировать весь процесс проверки. Этот процесс основывается на двух фундаментальных вопросах, Does it work? и Are  we done? Простые примеры показали, как построить testbench  для аппаратного ответа на эти вопросы такими устройствами как генератор сигналов и табло. Далее будет показано, как применять на уровне транзакций методы моделирования для создания практических, масштабируемых, многоразовых  компонент testbench, которые отвечают на эти вопросы и показывают, как подключить их  testbenches.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%B2%D0%B5%D1%80%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D0%B8</id>
		<title>OVM/OVM методология/Основы верификации</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%B2%D0%B5%D1%80%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D0%B8"/>
				<updated>2013-03-05T13:23:38Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: /* DUT */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
== Основы верификации==&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Функционально проверить устройство означает, сравнить цель разработчика с полученным  поведением на  их эквивалентность. Мы считаем устройство проверенным, когда, к всеобщему удовлетворению, оно работает в соответствии с целью разработчика. Этот основной принцип часто теряется при использовании testbenches, среды контроля, отладки, моделирования , и все другие средства, применяемые в современных технологических проверках. Чтобы узнать, работает ли верно устройство, вы должны сравнить его с некоторыми известными эталоном, который представляет цель разработчика. Каждый testbench имеет своего рода эталонную модель и средство для сравнения функциональность устройство с эталоном. &lt;br /&gt;
&lt;br /&gt;
	Когда мы говорим &amp;quot;устройство&amp;quot;, мы имеем в виду проверяемое устройство, часто называемое тестируемое устройство или DUT. Чтобы проверить, DUT, как правило, представлен  форме подходящей для производства, которое может быть перенесено на кремний с помощью автоматизированных и ручных средств. Мы различаем DUT от эскиза на обратной стороне салфетки или окончательного нанесенный на кристалл, ни один из которых не может быть проверен. Эталонная модель отражает цели разработчика, то есть то, что устройство должно делать.Эталон может принимать различные формы, такие как документ, описывающий работу DUT,  golden модель, которая содержит уникальный алгоритм, или assertions, которые представляют протокол.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:1.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Для автоматизации сравнения поведения и эталона, оба должны быть представлены в форме, которую можно выполнить на компьютере с помощью некоторых программ, что делает  сравнение. &lt;br /&gt;
&lt;br /&gt;
==== Два вопроса====&lt;br /&gt;
&lt;br /&gt;
	Проверка устройства включает в себя два вопроса:Does it work? (Работает ли это устройство?) и Are we done? (Сделали ли мы?). Это основные, и некоторые сказали бы, очевидные вопросы. Тем не менее они &lt;br /&gt;
мотивируют всю механику каждой проверочного потока. Первый вопрос - это Does it work? Этот вопрос исходит от основной идеи верификации. Он спрашивает, Соответствует ли устройство эталону? Второй вопрос - Are we done? Он спрашивает, довольны ли мы сравнительным анализом полученной схемы и эталона для определения работает ли устройство согласно цели, если нет, то почему. Мы используем эти ценные вопросы создания основы для разработки эффективных testbenches.&lt;br /&gt;
&lt;br /&gt;
====Does it work?====&lt;br /&gt;
&lt;br /&gt;
	Does it work? Это не единственный вопрос, а категория вопросов, которые представляют природу DUT. Каждый проект будет иметь свой собственный набор Does-It-Work вопросов, чья роль заключается в определении правильного функционирования  устройства.  Функциональные вопросы определяют, ведет ли себя устройство должным образом в конкретных ситуациях. Мы получаем эти вопросы непосредственно из цели разработки устройства, и мы используем их, чтобы отразить цель проекта  в testbench.&lt;br /&gt;
&lt;br /&gt;
	Рассмотрим простой маршрутизатор пакетов в качестве примера. Это устройство маршрутизации пакетов от входа к одному из четырех портов вывода. Пакеты содержат адреса портов назначения и имеют различные длины полезной нагрузки. Каждый пакет имеет заголовок, трейлер, и два байта для циклического избыточного кода (CRC). Does-It-Work вопросы в этом случае должны быть следующие:&lt;br /&gt;
 &lt;br /&gt;
*пакет, поступающий на вход порта, адресованный на имя выходного порта 3, прибыл на порт  3? &lt;br /&gt;
*пакет длиной 16 пришел без изменений? &lt;br /&gt;
*CRC байты правильны, если полезная нагрузка [0f 73 a8 c2 3e 57 11 0a 88 FF 00 00 33 2b 4c 89]?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	Это простой пример полного набора вопросов. Для устройства даже такого простого как этот гипотетическое маршрутизатор, набор Does-It-Work вопросов может быть длинным. Чтобы построить план проверки и testbench, который поддерживает план, вы должны сначала перечислить все вопросы или показать как получит полный набор вопросов, а затем выберите те, которые являются интересными.&lt;br /&gt;
&lt;br /&gt;
	Продолжая пример маршрутизатора  пакетов,для того чтобы перечислить все Does-It-Work вопросы, вы можете создать таблицу:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:2.png]]&lt;br /&gt;
&lt;br /&gt;
Приведенная выше таблица содержит два вида вопросов, те,  на которые мы можем ответить сразу и те, которые мы можно разбить на более мелкие вопросы. Вопрос 1 из ряда вопросов, которые можно  представить в виде:&lt;br /&gt;
&lt;br /&gt;
[[Файл:3.png]]&lt;br /&gt;
[[Файл:4.png]]&lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что мы формулируем все вопросы, так что на них можно ответить да или нет. В конце концов, схема  работает либо нет – она либо  готова для синтеза, размещения  и трассировке, либо нет. Если вы можете ответить на все вопросы утвердительно, тогда проект готов для следующего производственного шага.&lt;br /&gt;
При разработке набора does-it-work  вопросов, не забудьте сформулировать их  таким образом, чтобы на них можно было ответить да или нет. Ответ да положителен, то есть, ответить  да означает, что устройство работает правильно. Это будет сделать легче, чем пытаться отслеживать  на какие вопросы нужно ответить  да, и на какие следует ответить нет. Такой  вопрос как  Передает ли  маршрутизатор ошибочные пакеты?  требует ответа нет, чтобы  считаться успешным. Лучшей формулировкой вопроса будет: Отклоняет ли маршрутизатор ошибочные  пакеты? Вы должны сделать вопросы наиболее конкретными,  даже если лучшее звучание будет:  Когда ошибочный пакет попадает на входной порт,  маршрутизатор обнаруживает его, подает сигнал ошибки и отклоняет этот пакет? Имейте в виду, что более конкретные вопросы более подробно описывают устройство. Ваш testbench должен дать ответ да или нет.&lt;br /&gt;
Тщательно сформулированный да или нет вопрос содержит свои собственные критерии успеха. Он сообщает, что получает ответа да. Такой  вопрос  как, Средняя задержка менее 27 тактов? содержит метрику, 27 тактов, и форму сравнения, меньше. Если вопрос (неправильно) сформулирован, например, Какова средняя задержка прохождения пакетов через маршрутизатор? мы не будем знать, что считается приемлемым. Чтобы ответить на ваш вопрос, вы в первую очередь должны быть в состоянии определить среднее время задержки. Только при правильной постановке вопроса мы знаем, как сделать сравнение, чтобы определить, является ли результат  правильным. Метрика сама по себе не говорит нам о том,   функционирует ли схема как задумано. Когда мы сравниваем измеренное значение со значением в  спецификации, 27 тактов в этом примере, мы можем определить, работает ли схема.&lt;br /&gt;
Как это часто бывает, это не практично  перечислять каждый does-it-work вопрос. Чтобы убедиться, что каждое слово в памяти 1 Мб можно записать и читать,  непрактично и не необходимо записать один миллион вопросов. Вместо этого, вопрос-генератор,  вопрос, который генерирует многие другие, занимает место  миллиона отдельных вопросов. Может каждый  миллион слов в памяти быть успешно записан и считан? -является вопросом-генератором.&lt;br /&gt;
Другие вопросы могут сами по себе  представлять  классы вопросов. Вопрос 3, Является ли вычисление CRC корректным для каждого пакета? является примером такого вопроса. Тестирование CRC вычисления требует ряда тщательно продуманных тестов, для  определения является ли вычисления CRC корректным во всех случаях. &lt;br /&gt;
&lt;br /&gt;
==== Are We Done?====&lt;br /&gt;
&lt;br /&gt;
Для определения ответа на вопрос  Are we done?, мы должны знать, что мы ответили на достаточное количество does-it-work  вопросов, чтобы  утверждать, что у нас есть проверенная схема. Мы начинаем эту задачу путем разделения на приоритеты всех does-it-work  вопросов по двум направлениям:&lt;br /&gt;
&lt;br /&gt;
[[Файл:5.png]]&lt;br /&gt;
&lt;br /&gt;
Искусство создания testbench требует, чтобы на начальном этапе, мы определили множество вопросов и отсортировали их в соответствии с ценностью возвращаемой информации с точки зрения проверки схемы. Следующий шаг заключается в создании механизма, который  будет отвечать на вопросы и определять  на какие из них были даны ответы (и на какие нет).&lt;br /&gt;
&lt;br /&gt;
Are-we-done вопросы также называется вопросами функционального покрытия, вопросы, которые устанавливают  все ли состояния охватывают тесты с точки зрения  функций схемы. Как  и  does-it-work  вопросы, мы можем также разбить вопросы функционального охвата на более подробные вопросы. И как вопросы функциональной корректности, вопросы функционального охвата  должны также иметь ответы да или нет. Приведенный ниже список включает в себя примеры вопросы функционального охвата:&lt;br /&gt;
&lt;br /&gt;
•	Выполняется ли каждая команда процессора, по крайней мере, один раз?&lt;br /&gt;
&lt;br /&gt;
•	Проходит, по крайней мере, один пакет  от каждого входного порта до каждого выходного порта?&lt;br /&gt;
&lt;br /&gt;
•	Можно ли обратиться к памяти  с набором адресов, которые содержат каждый бит адреса как 1 и затем каждый  бит адреса 0, не включая 0xffffffff и 0x00000000?(?????????)&lt;br /&gt;
&lt;br /&gt;
Другим мнением об этих вопросах является то, о чем они спрашивают, Получен ли утвердительный ответ на необходимый does-it-work  вопрос? Когда мы думаем о функциональном охвате с этой точки зрения, то имеем виду покрытие множества does-it-work  вопросов. Кроме того, вопросы охвата распознают метрику и порог для сравнения. Покрытие будет  (то есть, охват вопрос можно ответить, да), когда метрика достигает порога.&lt;br /&gt;
&lt;br /&gt;
Таким образом, искусство построения testbench начинается с плана тестирования.  План тестирования начинается с тщательно продуманным набором does-it-work и Are-we-done вопросов.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Двухпетлевый поток (???????????????)====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Процесс ответа на does-it-work и Are-we-done вопросы может быть описан  простой схемой, как показано на рисунке 1-2. Все обусловлено функциональной спецификацией на проектирование. Из функциональной спецификации, вы можете получить саму схему и план верификации.  План верификации запускает testbench.&lt;br /&gt;
&lt;br /&gt;
Поток состоит из двух циклов, does-it-work цикла и Are-we-done цикл. Оба цикла начинаются с моделирования. Моделирование тестирует  схему при помощи  testbench и генерирует информацию, которую мы используем для ответа на вопросы. Сначала мы спрашиваем, Это работает? Если ответ нет, то необходимо отладить проект. Процесс отладки схемы может привести к изменениям  в реализации схемы.&lt;br /&gt;
Как только схема работает, то пора ответить на вопрос, Are-we-done? Мы ответим на этот вопрос, собирая информацию охвата и сравнения его с порогом, указанным в тестовом плане. Если мы не достигнем  порогов, то ответ нет, и мы должны изменить testbench для увеличения охвата. Тогда мы моделируем снова.&lt;br /&gt;
&lt;br /&gt;
Изменение testbench или воздействия может вызвать другие скрытые ошибки в схеме. Последующие итерации по циклу могут заставить нас вернуться к does-it-work циклу снова, чтобы исправить новые ошибки, которые появятся. Как вы можете видеть, полный процесс проверки колеблется вперед и назад между does-it-work и Are-we-done циклами, пока мы не можем утвердительно ответить на все вопросы  обоих категорий.&lt;br /&gt;
&lt;br /&gt;
[[Файл:6.png]]&lt;br /&gt;
&lt;br /&gt;
В идеальном мире, схема не имеет ошибок и с покрытием всегда полное, так что вам только надо пройти по каждому циклу один раз, чтобы получить ответы да на оба вопроса. В реальном мире, это может занять много итераций для достижения двух ответов да. Одна из целей хорошего потока проверки свести к минимуму количество итераций для выполнения верификации проекта в кратчайшие сроки с использованием наименьшего количества ресурсов.&lt;br /&gt;
&lt;br /&gt;
== Первый Testbench==&lt;br /&gt;
&lt;br /&gt;
Давайте начнем с проверки одного из самых фундаментальных цифровых электронных устройств, схемы логического И. Логическое И вычисляет логическое умножение   входов. Функция этого устройства является тривиальной, а на практике, вряд ли должна иметь свой собственный  testbench. Так как она тривиальна, мы можем использовать ее для иллюстрации некоторых основных принципов проверки без необходимости вникать в более сложную схему.&lt;br /&gt;
&lt;br /&gt;
Рисунок 1-3 показывает условное обозначение  логического элемента умножения с двумя входами. Логический элемент имеет два входа, А и В, и один выход Y. Устройство вычисляет логическое умножение  А и В и помещает результат в Y.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:7.png]]&lt;br /&gt;
&lt;br /&gt;
Следующая таблица истинности описывает функцию устройства.&lt;br /&gt;
&lt;br /&gt;
[[Файл:8.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Таблица истинности является исчерпывающей: она содержит все возможные входы А и В и поэтому, все возможные значения для выхода Y.&lt;br /&gt;
Наша задача состоит в том, чтобы доказать, что наша схема,  логического И, работает правильно. Чтобы убедиться, что он действительно выполняет функцию логического умножения  правильно, нам в первую очередь необходимо перечислим вопросы. Таблица истинности поможет нам создать множество вопросов, необходимых для проверки. Каждая строка таблицы содержит вход для А и В и ожидаемый выход для Y. Так как таблица является исчерпывающей, наш вопрос-генератор будет:  Для каждой ли строки в таблице истинности, когда мы получаем значения А и B, определенные в этой строке,  устройство вычисляет ожидаемый результат для Y? Для ответа на Are-we-done вопрос,  мы определяем, проверили ли  мы  каждую строку в таблице истинности, и получил ли утвердительный ответ does-itwork вопрос для этой строки. Наш Are-we-done вопрос- это Работает на всех наборах?&lt;br /&gt;
Для автоматизации ответов как на does-itwork  та  и на Are-we-done  вопросы,  нам нужна следующая атрибутика:&lt;br /&gt;
&lt;br /&gt;
•	Модель, которая представляет DUT (в данном случае, логическое И)&lt;br /&gt;
&lt;br /&gt;
•	Концепция конструкции в форме, которую мы можем кодифицировать  как эталонную модель&lt;br /&gt;
&lt;br /&gt;
•	Некоторое воздействие для проверки схемы&lt;br /&gt;
&lt;br /&gt;
•	Способ сравнить результат с концепцией конструкции&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:9.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В то время как наш маленький testbench прост, он содержит ключевые элементы, которые можно найти в большинстве testbenches любого уровня сложности. Ключевыми элементами являются следующие:&lt;br /&gt;
&lt;br /&gt;
•	DUT&lt;br /&gt;
&lt;br /&gt;
•	Stimulus генератор, который генерирует последовательность сигналов для DUT&lt;br /&gt;
&lt;br /&gt;
•	Scoreboard, которое изображает  эталонную модель&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Табло следит за входами и выходом тестируемого устройства, выполняет же функции, что DUT за исключением более высокого уровня абстракции, и определяет, совпадает ли DUT и эталонная модель. Scoreboard помогает нам ответить на does-itwork  вопросы.&lt;br /&gt;
&lt;br /&gt;
=== DUT===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DUT- это наше логическое И с двумя входами. Мы реализуем логическое И, как модуль с двумя входами А и В, и одним выходом Y.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;44 module and2 (&lt;br /&gt;
45 output bit Y,&lt;br /&gt;
46 input A, B);&lt;br /&gt;
47&lt;br /&gt;
48 initial Y = A &amp;amp; B;&lt;br /&gt;
49&lt;br /&gt;
50 always @* Y = #1 A &amp;amp; B;&lt;br /&gt;
51 endmodule&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stimulus генератор в этом примере создает заданные сигналы. Каждое новое значение вычисляются в определенном порядке. Позже, мы будем смотреть на  генератор случайных сигналов, которые, как следует из их названия, генерирует случайные значения.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;verilog&amp;quot;&amp;gt;81 module stimulus(output bit A, B);&lt;br /&gt;
82&lt;br /&gt;
83 bit [1:0] stimulus_count = 0;&lt;br /&gt;
84&lt;br /&gt;
85 always&lt;br /&gt;
86 #10 {A,B} = stimulus_count++;&lt;br /&gt;
87&lt;br /&gt;
88 endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Цель Stimulus генератор - получение значений на вход  DUT. Сигналы, двухбитная величина, содержащая значение для А и B. После того как он увеличивается при каждой последующей итерации, младший бит посылает на А, а старший бит на B.&lt;br /&gt;
&lt;br /&gt;
=== Scoreboard===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Scoreboard несет ответственность за ответы на  does-it-work  вопросы. Оно следит за работой DUT и сообщает работает ли оно корректно.  Обратите внимание на то, что структура Scoreboard поразительно похожа на структуру DUT. Это имеет смысл, когда вы считаете, что назначением Scoreboard является отслеживание активности DUT  и определение, работает ли DUT  как  ожидалось.&lt;br /&gt;
&lt;br /&gt;
[[Файл:12.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Scoreboard отображает пины всех входов. На Scoreboard не вызывает воздействия на  схему. Оно пассивно наблюдает за входами и выходами тестируемого устройства. &lt;br /&gt;
&lt;br /&gt;
Модуль верхнего уровня, как показано ниже, является полностью структурированным, он содержит  экземпляр тестируемого устройства, Scoreboard и Stimulus генератор,  с кодом, необходимым для их подключения.&lt;br /&gt;
&lt;br /&gt;
[[Файл:13.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Когда мы запускаем моделирования для нескольких итераций, вот что мы получаем:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:14.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Каждое сообщение состоит из двух частей. В первой части показаны применяемые сигналы. Вторая часть показывает результаты проверки Scoreboard, которое сравнивает соответствие DUT с  необходимыми результатами. Мы используем двоеточие для разделения двух частей. &lt;br /&gt;
&lt;br /&gt;
Этот простой  testbench  демонстрирует использование Stimulus генератор и Scoreboard, которое служит в качестве эталона. Несмотря на то, DUT – логическое И, все элементы полного testbench  присутствуют.&lt;br /&gt;
&lt;br /&gt;
== Второй Testbench==&lt;br /&gt;
&lt;br /&gt;
В предыдущий пример показывает элементарные понятия проверки с использованием  комбинационного дизайна.  Комбинационные конструкций,  по  природе, не поддерживают никаких статических данных. В нашем втором примере мы рассмотрим чуть более сложную конструкцию, которая сохраняет данные о состояниях и использует  тактовый сигнал  для перехода  между состояниями.&lt;br /&gt;
&lt;br /&gt;
Проблема верификации, связанная с синхронными (последовательными) схемами,  иная чем у  комбинационных конструкций. Все, что вам нужно знать о комбинационной схеме доступно на ее входах. Эталонная модель для  комбинационного устройства просто должна  вычислять F (X), где X представляет  входы  устройства и F – это реализуемая функция. Выходы же на последовательном устройстве  зависят от  входных сигналов и его внутреннего состояния. Дальнейшее  вычисление может изменить внутреннее состояние. Scoreboard должно отслеживать  внутреннее состояние тестируемого устройства и сравнить выходные данные.&lt;br /&gt;
&lt;br /&gt;
Комбинационное устройство может быть полностью проверено при подаче всех возможных комбинаций сигналов на входы. Для устройства  N входами, мы должны применить в общей сложности 2n  входных комбинаций. Число 2n  может быть большим, но вычисление такого количества  входов достаточно простое. Нам  просто необходимо иметь N-разрядный счетчик и посылать каждое значение счетчика на входы устройства. &lt;br /&gt;
Для последовательного устройства, понятие &amp;quot;done&amp;quot; должно распространяться и на покрытие не только  всевозможных сигналов на  входах, но и на число всевозможных внутренних состояний. Для устройства с n входами и m внутренних состояний, вы должны покрыть (2n входов) * (2m внутренних состояний),   2n+m – число всевозможных комбинаций внутренних состояний и входов. Для устройства с 64 входами  и одним 32-битный внутренним регистром, количество  комбинаций  296 -очень большое число, не так ли!&lt;br /&gt;
&lt;br /&gt;
Даже для очень большого числа комбинаций, проверка  не была бы слишком трудной, если  бы было возможно  просто увеличивать счетчик для получения всех комбинаций, как мы делаем с комбинационными устройствами. К сожалению, это не представляется возможным. Внутреннее состояние напрямую не доступны извне устройства. Оно может быть изменено только путем манипулированием входов. И теперь проблема заключается в том, как добраться до всех состояний в устройстве только через входы. Это сложная проблема, которая требует глубокого понимания устройства, для генерации последовательности входов для получения всех состояний.&lt;br /&gt;
&lt;br /&gt;
Поскольку трудно получить  все состояния, становится  очевидным вопрос, Можем ли мы упростить задачу путем сокращения числа состояний, которые мы должны получить, чтобы показать, что устройство работает правильно? Ответ: да. Теперь возникает другой вопрос, Как мы решим, какие состояния можно пропустить?&lt;br /&gt;
&lt;br /&gt;
Эта тема является сложной. Тем не менее, мы можем дать интуитивный ответ на вопрос. Состояния, которые могут быть отмечены как недоступные, при использовании формальной проверки или других методов, не должны быть покрыты. Проектировщик должен рассмотреть вопрос об упрощении схемы для удаления недоступных состояний, так как они не представляют никакой ценности. Состояния, которые имеют низкую вероятность быть достигнутыми, также могут быть исключены из плана проверки. Определение порогового значения вероятности и вероятностей состояний такое же искусство, как наука. Она включает в себя понимание того, как устройство используется и какие входы необходимы (по сравнению с которые возможны).&lt;br /&gt;
&lt;br /&gt;
Это также позволяет устранить состояния, которые являются функционально эквивалентными. Рассмотрим устройство пакет коммуникаций. В теории, каждое возможное значение полезной нагрузки пакета представляет собой различное состояние (или множества состояний), которое принимает схема, и оно должно быть покрыто во время проверки. Однако, это, вероятно, не будет преувеличением считать, что произвольное ненулевое значение, для всех намерений и целей, эквивалентно. Конечно, могут быть некоторые интересные случаи, которые должны быть проверены, как все нули, все единицы, конкретные значения, которые могли бы бросить алгоритмы коррекции ошибок, и так далее. Изменения данных становятся интереснее, когда они влияют на поток управления.&lt;br /&gt;
&lt;br /&gt;
В общем, это более важно, чтобы охватить управляющие состояния, чем состояние данных. Простым способом уменьшить число состояний, необходимых для покрытия схемы является разделения данных и управления. Для конкретного пути контроля, данные могут быть произвольными. По некоторых данных, вы можете захотеть изменить путь управления. Например, в ALU  вы можете разделить функции контроля записи данных  и получения данных из регистров и установление арифметической операции для выполнения из результатов конкретные арифметических операций. Использование направленного управления, вы можете сделать данные случайные или  рассмотреть данные в специфичных случаях, таких как деление на 0 или умножение на 1.&lt;br /&gt;
&lt;br /&gt;
Для сложных последовательных конструкций, определения того, какие состояния должны быть покрыты(и какие не должны) и, как получить  эти состояния с минимальными усилиями является проблемой.&lt;br /&gt;
&lt;br /&gt;
=== 3-х разрядный счетчик===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Схема, приведенная на рисунке 1-5, представляет собой 3-разрядный счетчик с асинхронным сбросом. Каждый раз, когда тактовый сигнал высокий, счетчик увеличивается. Схема состоит из трех переключательных триггеров, каждый из которых поддерживает один бит счета(??). Триггеры связаны с некоторой комбинационной логикой для формирования счетчика . Каждый триггер переключается, когда на входе T сигнал высокой. При низком сигнале на Т , триггер сохраняет свое текущее состояние. Когда активный низкий сброса установлен в 0, триггер переходит в состояние 0.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:15.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Код для счетчика содержится в двух модулях. Одним из них является простой переключательный триггер, а другой подключает триггера с необходимой логикой для  формирования счетчика. Первый пример показывает  переключательный триггер.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:16.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Счетчик состоит из трех переключательных триггеров и логического элемента И.&lt;br /&gt;
&lt;br /&gt;
[[Файл:17.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Схема проста, но имеет характеристики, которые являются общими с реальными проектами и которые требуют  внимания для надлежащей проверки дизайна. Основными характеристиками является то, что конструкция работает по тактовому сигналу, и что она сохраняет внутреннее состояние. Логический элемент  И  из предыдущего примера не сохраняет никаких состояний. Вся информация о том, что  делает схема, находится на ее входах. В конструкциях с внутренними данными, что это не так.&lt;br /&gt;
&lt;br /&gt;
Эта разница отражается на схеме нашего табло. Рисунок 1-6 показывает схему  testbench  для 3х разрядного счетчика.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:18.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Во многих отношениях, testbench   для 3-разрядного счетчика очень похож на testbench для логического элемента И.  Оба имеют Scoreboard, роль которого состоит в наблюдении, что делает схема, и определить, выполняется ли операция правильно. Оба имеют устройства для  DUT. Тем не менее, мы выполняем операции по-разному для этих конструкций. Мы используем Stimulus генератор для логического элемента И, но мы используем контроллер для 3-х разрядного счетчика. 3-х разрядный счетчик автономное устройство. Пока он подключен к тактовому сигналу, он будет продолжать считать. Таким образом, нам не нужен Stimulus генератор, который мы использовали для логического И. Вместо этого, контроллер управляет работой тестируемого устройства и  testbench. Контроллер обеспечивает начальный сброс, так что отсчет начинается с известного значения. Он также останавливает моделирование в соответствующее время.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:19.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Табло должны отслеживать внутреннее состояние DUT. Это делается с помощью переменной count. Как и DUT, когда reset активирован, счетчик устанавливается в 0. Каждый тактовый цикл count увеличивается на единицу, и новое значение  сравнивается с count, поступающего с DUT.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:20.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Табло имеет модель счетчика высокого уровня. Он использует целую переменную и оператор сложения (+) , чтобы сформировать счетчик вместо триггеров и элемента И. Каждый раз, при изменении тактового импульса, он увеличивает свой счет, так же как и RTL счетчика. Он также проверяет, соответствует ли  ее внутренний счетчик  выходу счетчика DUT.&lt;br /&gt;
&lt;br /&gt;
Для полноты, пример показывает тактовый генератор и модуль топ-уровня. Тактовый генератор просто инициализирует тактовый сигнал 0, а затем переключает его каждые 5 нс.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:21.png]]&lt;br /&gt;
&lt;br /&gt;
Модуля верхнего уровня является типичным для большинства testbenches. Он соединяет DUT  и компоненты testbench.&lt;br /&gt;
&lt;br /&gt;
[[Файл:22.png]]&lt;br /&gt;
&lt;br /&gt;
Мы показали простой testbench, который содержит элементы, используемые в гораздо более сложных testbenches. Последовательные схемы, которые поддерживают внутреннее состояние, требуют табло, которое работает параллельно с DUT. Табло выполняет те же вычисления что и DUT, но на более высоком уровне абстракции. Табло также сравнивает свои вычисления с вычислениями, полученными от DUT.&lt;br /&gt;
&lt;br /&gt;
== Многоуровневая организация Testbenches==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Так  как схема представляет собой совокупность  компонент проекта,  testbench представляет собой  совокупность компонент верификации. OVM определяет верификацию компонент, их структуру,интерфейс. В этом разделе описываются  OVM компоненты.&lt;br /&gt;
&lt;br /&gt;
OVM testbenches организованы в виде уровней. Самый нижний слой -устройство RTL с pin-level интерфейсом (с интерфейсом на уровне выводов). Выше находится уровень транзакторов (посредник), устройств, которые преобразуют из уровня транзакций в pin-level. Все компоненты, находящиеся на этом уровне, являются компонентами уровня транзакций. Диаграмма, приведенная ниже, иллюстрирует уровневую организацию тестовых программ. Блок слева — название уровня, блок справа — перечень типов компонентов, входящих в него. Вертикальные стрелки показывают, какие уровни взаимодействуют на прямую. Например, управляющий уровень взаимодействует с уровнями анализа, операций, транзакторов, но с DUT напрямую не может.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:23.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Вы также можете рассмотреть testbench как концентрическую организацию компонент. Внутреннему кольцу соответствует нижний уровень, а внешнему кольцу - верхний уровень. Для некоторых будет проще понять отношения между уровнями, используя диаграмму в виде списка связей.&lt;br /&gt;
&lt;br /&gt;
[[Файл:24.png]]&lt;br /&gt;
&lt;br /&gt;
=== Transactors(?????????????????????????)===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Роль посредников в testbench состоит в  преобразование потока транзакций в pin-level activity или наоборот. Транзакторы характеризуются наличием, по крайней  мере, одного pin-level интерфейса и хотя бы одного transaction-level интерфейса. Транзакторы могут принимать большое разнообразие форм, цветов и стилей. Мы сосредоточимся на мониторах, драйверах и передатчиках.&lt;br /&gt;
&lt;br /&gt;
'''Монитор'''. Монитор, как следует из названия, следит за шиной данных. Он следит за выводами и преобразует их в поток транзакций. Мониторы пассивные - это означает, что они не влияют на операции в DUT.&lt;br /&gt;
&lt;br /&gt;
'''Драйвер'''. Драйвер преобразует поток транзакций (или последовательность элементов) в pin-level activity.&lt;br /&gt;
&lt;br /&gt;
'''Передатчик'''.  Передатчик очень похож на драйвер, но он  реагирует на активность на выходе, а не на входную активность.&lt;br /&gt;
&lt;br /&gt;
===Операционные компоненты===&lt;br /&gt;
&lt;br /&gt;
Операционные компоненты – это набор компонент,которые обеспечивают все необходимые операции в DUT. Операционные компоненты отвечают за генерацию трафика для DUT. Все эти компоненты относятся к уровню транзакций и имеют только интерфейс уровня транзакций.Существуют различные варианты формирования сигнала, которые варьируются в зависимости от верифицируемого устройства. Мы рассмотрим три основных вида операционных компонентов: stimulus generators, masters, и slaves.&lt;br /&gt;
&lt;br /&gt;
'''Stimulus генераторы'''.  Генератор сигналов  создает поток транзакций для проверки DUT. Stimulus генераторы могут быть случайными, заданными или заданными случайно; они могут запускаться самостоятельно или контролируемо; и они могут быть независимыми или синхронизированными. Простейший stimulus генератор рандомизирует содержимое объекта запроса и посылает этот объект на драйвер. OVM также предоставляет модульные, динамические средства для построения сложных stimulus, называемыми последовательностями.&lt;br /&gt;
&lt;br /&gt;
'''Главное устройство'''.  Главное устройство - двунаправленная компонента, которая посылает запросы и принимает ответы.  Главное устройство инициирует активность. Как и Stimulus генераторы, оно может передавать отдельные рандомизированные транзакции или последовательности заданных или случайно заданных транзакций.  Главное устройство может использовать ответы для определения последующих действий.  Главное устройство также может быть реализовано как последовательность.&lt;br /&gt;
&lt;br /&gt;
'''Подчиненное устройство'''. Подчиненное устройство, как и главное устройство, является двунаправленной компонентой. Оно реагирует на запросы и возвращает ответы (в отличие от главного устройства, которое отправляют запросы, и принимает ответы).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:25.png]]&lt;br /&gt;
&lt;br /&gt;
=== Компоненты анализа===&lt;br /&gt;
&lt;br /&gt;
Компоненты анализа получают информацию о том, что происходит в testbench, и используют эту информацию, чтобы сделать некоторые заключения о правильности и полноты теста. Два наиболее распространенных вида анализа - это scoreboards и coverage collectors.&lt;br /&gt;
&lt;br /&gt;
'''Scoreboards'''. Табло используются для определения правильности DUT, чтобы ответить на does-it-work вопросы.Scoreboards перехватывает информацию, входящую и выходящую из DUT и определяют правильно ли DUT отвечает на данный stimulus.&lt;br /&gt;
&lt;br /&gt;
'''Coverage Collector'''. Coverage Collector используются для подсчета. Они перехватывают поток  транзакций, и считает транзакции или различные аспекты транзакций. Цель состоит в том, чтобы определить завершенность верификации, отвечая на are-we-done вопросы. Определенные параметры, которые coverage collectors считает, зависит от схемы и специфики тестов. Общие вещи, которые coverage collectors включают в себя: необработанные транзакции, транзакции, которые происходит в определенном сегменте адресного пространства, и протокол ошибок. Список неограничен.&lt;br /&gt;
&lt;br /&gt;
Coverage Collector могут также выполнять вычисления как часть полноты верификации. Например, coverage collectors может содержать скользящее среднее и среднее квадратичное отклонение данных, которые отслеживаются. Или они могут хранить отношения ошибок к выполнимым операциям.&lt;br /&gt;
&lt;br /&gt;
=== Контроллер===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Контроллеры формируют основной поток теста, и управляют работой. Как правило, контроллеры получают информацию от Scoreboard и coverage collectors, и  отправляют информацию  компонентам окружающей среды. &lt;br /&gt;
Например, контроллер может запустить Stimulus генератор, а затем ждать сигнала от coverage collectors, чтобы уведомить его, когда тест будет завершен. Контроллер, в свою очередь, останавливает Stimulus генератор. Более сложные вариации на эту тему возможны. В пример возможные конфигурации, контроллер инициализирует Stimulus генератор начальным набором ограничений и запускает его. При достижении определенного соотношения  типов пакетов, coverage collectors сигнализирует контроллеру. Вместо того, чтобы остановить Stimulus генератор, контроллер может отправить ему новый набор ограничений.&lt;br /&gt;
&lt;br /&gt;
== Два домена( Две области)==&lt;br /&gt;
&lt;br /&gt;
Мы можем разделить множество компонент в testbench на отдельные  области. Оперативная область - множество компонент, в том числе DUT, которые управляют DUT. Это Stimulus генератор, шина функциональной модели (BFM), и аналогичные компоненты, которые генерируют сигналы и возвращают ответы, которые управляют моделированием. DUT, ответчик, и драйвер операций, наряду с компонентами окружающей среды, которые непосредственно обращаются или отвечают драйверам и передатчикам ответа, включены в оперативную область. Остальные компоненты testbench –монитор транзакций, scoreboard, coverage collectors, и контроллера включены в область анализа. Это компоненты, которые собирают информацию из оперативной области.&lt;br /&gt;
&lt;br /&gt;
Данные должны быть перемещены из оперативной области в область анализа таким образом, чтобы не мешать работе DUT и сохранить оптимальный выбор событий. Это достигается с помощью специального интерфейса связи называемого портом анализа.  Порт анализа представляют собой особый вид  порта транзакций, в котором издатель передает данные одному или нескольким получателям. Издатель оповещает всех получателей, когда новые данные готовы.&lt;br /&gt;
&lt;br /&gt;
Одной из ключевых особенностей порта анализа  является то, что он имеет единственную функцию, write(). Анализ FIFO(????), каналы, используемые для подключения к порту анализа для анализа компонентов, неограниченные. Это гарантирует, что издатель не блокирует, а  передает свои данные в анализ FIFO в точно такой же дельта-цикл, в котором они были созданы.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:26.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как правило, оперативная и область анализа связаны портами анализа и интерфейсами управления и конфигурации. Порты анализа кран подачи выпускают данные относящиеся к операциям DUT. Эти данные могут включать шину транзакций, коммуникационные пакеты и информацию о состоянии (успех или ошибка конкретных операций). Компоненты  области анализа анализируют данные и принимают решения. Результатами этих решений могут быть сообщения оперативной области, используя  интерфейсы управления и конфигурирования. Интерфейсы управления и конфигурирования  могут быть использованы для запуска и остановки Stimulus генератор, изменения ограничений, изменения процента ошибок, или манипулирование другими параметрами, влияющих на работу testbench.&lt;br /&gt;
&lt;br /&gt;
== Заключение==&lt;br /&gt;
&lt;br /&gt;
В этой главе мы рассмотрели, как структурировать весь процесс проверки. Этот процесс основывается на двух фундаментальных вопросах, Does it work? и Are  we done? Простые примеры показали, как построить testbench  для аппаратного ответа на эти вопросы такими устройствами как генератор сигналов и табло. Далее будет показано, как применять на уровне транзакций методы моделирования для создания практических, масштабируемых, многоразовых  компонент testbench, которые отвечают на эти вопросы и показывают, как подключить их  testbenches.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F</id>
		<title>OVM/OVM методология/Основы объектно-ориентированного программирования</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F"/>
				<updated>2013-03-05T13:17:58Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Разработка программного обеспечения, не связанная с физикой электричества и магнетизма, уже давно пытается создать многократно используемые, взаимозаменяемые, надежные компоненты. Важной моделью программирования, которая решает проблему, называется объектно-ориентированным программированием (ООП). Основная идея ООП заключается в том, что программы организуются как набор взаимодействующих объектов, каждый со своим собственным областью данных и функций. Объекты могут быть многократно использованы, потому что они инкапсулируют все, что им нужно для работы, могут быть построены с минимальным числом или без внешних зависимостей, и может быть высоко параметризованы. &lt;br /&gt;
В этой главе вводятся основные понятия объектно-ориентированного программирования, в том числе понятия инкапсуляции и интерфейса. Глава завершается обсуждением того, почему ООП является важным для построения testbenches.&lt;br /&gt;
&lt;br /&gt;
== Процедурные против ООП==&lt;br /&gt;
&lt;br /&gt;
Чтобы понять ООП и его роль в проверке, необходимо сначала понять традиционное процедурное программирование и его ограничения. Это даст основу для понимания того, как ООП может преодолеть эти ограничения. &lt;br /&gt;
В первые дни  языка  ассемблера, программисты и компьютерные архитекторы быстро обнаружили, что программы часто содержат последовательности инструкций, которые повторяются во всей программе. Повторение кода (в частности, с перфокарт) является трудоемким и порождает ошибки. Чтобы избежать трудоемкости и ошибок, вызванных повторением последовательностей,  была изобретена подпрограмма. Подпрограмма является единицей повторно используемого кода. Вместо кодирования той же последовательности встроенных инструкций, вы вызываете подпрограмму. Параметры, передаваемые в подпрограмму, позволяют динамически изменять код. То есть, каждый вызов подпрограммы с различными значениями параметров заставляет подпрограмму вести себя по-разному в зависимости от конкретных значений параметров.&lt;br /&gt;
&lt;br /&gt;
Каждый язык программирования любой значимости  имеет  конструкции для создания подпрограмм, процедур и функций, а также синтаксис для передачи в них параметров и возвращаемых значений. Эти функции могут использоваться для создания операций, которые часто используются. Тем не менее, некоторые операции очень распространены (таких как I / O, преобразования данных, численные методы и т. д.). И во избежание переписывания этих операций, программисты посчитали необходимым создать библиотеки часто используемых функций. В результате, большинство языков программирования включает такие библиотеки как часть пакета компилятора. Одним из наиболее известных примеров является библиотека C, которая поступает с каждым компилятором C. Она содержит полезные функции, такие как printf() (), COS (), atof (), и QSort (). Эти функции  практически каждый программист будет использовать в тот или иной момент.&lt;br /&gt;
Представьте себе, что писать собственные подпрограммы ввода / вывода или ваши собственные вычисления для преобразования чисел в строки и строк в числа. Был момент, когда программисты так и делали. Библиотеки универсальных функций все изменили и увеличили общую производительность программирования.&lt;br /&gt;
&lt;br /&gt;
Как показывает практика программного обеспечения и передовых технологий, программисты начали думать на более высоком уровне абстракции, чем инструкции и процедуры. Вместо написания отдельных инструкций, программисты пишут код на языках, которые обеспечивают более высокую модель абстракции компьютера, и компиляторы или интерпретаторы  переводят эти модели в конкретные инструкции. Библиотеки, такие как C библиотеки или STL в C + +, являются одной из форм абстракции. Они представляет собой набор функций, которые программисты могут использовать для создания все более сложных программы или абстракции.&lt;br /&gt;
В своей основополагающей книге Алгоритмы + Структуры данных = Программы, Никлаус Вирт объясняет, что для решения любой задачи программирования, вы должны разработать абстракцию реальности, которая имеет характеристики и свойства задачи, и игнорировать остальные детали. Он утверждает, что набор данных, которые вам нужны для решения проблемы, формирует абстракцию. Поэтому, прежде чем вы сможете решить проблема, в первую очередь необходимо определить, какие данные нужно иметь, чтобы создать решение.&lt;br /&gt;
Чтобы продолжить создание многократно используемой абстракции, мы должны создавать библиотеки данных объектов, которые могут быть многократно использованы для решения конкретных видов проблем. Поиск способа сделать это приводит к развитию объектно-ориентированных технологий.&lt;br /&gt;
&lt;br /&gt;
Объектно-ориентированный анализ и проектирование программ сосредоточен вокруг данных объектов, функциональности, связанной с каждым объектом, и отношения между объектами.&lt;br /&gt;
&lt;br /&gt;
Объектно-ориентированные языки предоставляют средства для отделения проблем программы, и  выделения каждой из них независимо друг от друга, и для инкапсуляции данных абстракций и представление их через строго определенные интерфейсы. Полностью объектно-ориентированная программа строится путем разделения функциональности программы в различные классы, определения интерфейса для каждого класса, а затем установления связей и взаимодействий между компонентами через их интерфейсы.&lt;br /&gt;
&lt;br /&gt;
==Классы и объекты==&lt;br /&gt;
&lt;br /&gt;
Основным модулем программирования на объектно-ориентированных языках, таких как SystemVerilog, является класс. Класс содержит элементы данных, называемые членами, и задач и функций, называемых методами. Для выполнения объектно-ориентированных программ, вы должны создать один или более классов в основной программе, а затем вызвать методы на различных объектах. Хотя термины класс и объект иногда используются как синонимы, как правило, термин класс относится к классу декларации объекта, а термин объект относится к экземпляру класса.&lt;br /&gt;
Чтобы проиллюстрировать эти понятия, ниже приведен пример простой класс register.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class register;&lt;br /&gt;
local bit[31:0] contents;&lt;br /&gt;
function void write(bit[31:0] d)&lt;br /&gt;
contents = d;&lt;br /&gt;
endfunction&lt;br /&gt;
function bit[31:0] read();&lt;br /&gt;
return contents;&lt;br /&gt;
endfunction&lt;br /&gt;
endclass&lt;br /&gt;
&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Это очень простой класс с одной переменной, и двумя методами, read() и write(). Чтобы использовать этот класс, вы создаете объекты путем создания экземпляра класса, а затем вызвать методы объекта, как показано ниже&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;module top;&lt;br /&gt;
register r;&lt;br /&gt;
bit[31:0] d;&lt;br /&gt;
initial begin&lt;br /&gt;
r = new();&lt;br /&gt;
r.write(32’h00ff72a8);&lt;br /&gt;
d = r.read();&lt;br /&gt;
end&lt;br /&gt;
endmodule&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Локальный  атрибут contents  класса указывает компилятору строго соблюдать границы класса. Если вы пытаетесь получить доступ к contents напрямую, компилятор выдаст ошибку. Вы можете получить доступ к содержимому через  доступные функции чтения и записи. Этот вид контроля доступа важен для  гарантирования  независимости от внутренностей класса и тем самым делает класс многократно используемым.&lt;br /&gt;
Вы можете использовать классы для создания новых типов данных, таких, как наш простой register. Использование классов для создания новых типов данных является важной частью ООП. Вы можете также использовать их для инкапсуляции математических вычислений или создания динамических структур данных, таких как стеки, списки, очереди, и так далее. Инкапсуляция организации структур данных или сведений о вычислениях в классе делает структуру данных или вычислений многократно используемыми.&lt;br /&gt;
В качестве более полного примера, давайте посмотрим на полезный тип данных, стек. Стек - это LIFO (&amp;quot;последним пришёл - первым обслужен&amp;quot;) структура. Элементы помещаются в стек командой push(), а  извлекаются из стека pop(). Pop () возвращает последний элемент, помещенный в стек,  и удаляет его из структуры данных. Внутренние элемент  stkptr  следит за вершиной стека. Элемент, на который он указывает, это вершина стека, а все, что ниже его (то есть, с меньшим индексом) ниже в стеке. Ниже приведена базовая реализация стека в SystemVerilog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;43 class stack;&lt;br /&gt;
44&lt;br /&gt;
45 typedef bit[31:0] data_t;&lt;br /&gt;
46 local data_t stk[20];&lt;br /&gt;
47 local int stkptr;&lt;br /&gt;
48&lt;br /&gt;
49 function new();&lt;br /&gt;
50 clear();&lt;br /&gt;
51 endfunction&lt;br /&gt;
52&lt;br /&gt;
53 function bit pop(output data_t data);&lt;br /&gt;
54&lt;br /&gt;
55 if(is_empty())&lt;br /&gt;
56 return 0;&lt;br /&gt;
57&lt;br /&gt;
58 data = stk[stkptr];&lt;br /&gt;
59 stkptr = stkptr - 1;&lt;br /&gt;
60 return 1;&lt;br /&gt;
61&lt;br /&gt;
62 endfunction&lt;br /&gt;
63&lt;br /&gt;
64 function bit push(data_t data);&lt;br /&gt;
65&lt;br /&gt;
66 if(is_full())&lt;br /&gt;
67 return 0;&lt;br /&gt;
68&lt;br /&gt;
69 stkptr = stkptr + 1;&lt;br /&gt;
70 stk[stkptr] = data;&lt;br /&gt;
71 return 1;&lt;br /&gt;
72&lt;br /&gt;
73 endfunction&lt;br /&gt;
74&lt;br /&gt;
75 function bit is_full();&lt;br /&gt;
76 return stkptr &amp;gt;= 19;&lt;br /&gt;
77 endfunction&lt;br /&gt;
78&lt;br /&gt;
79 function bit is_empty();&lt;br /&gt;
80 return stkptr &amp;lt; 0;&lt;br /&gt;
81 endfunction&lt;br /&gt;
82&lt;br /&gt;
83 function void clear();&lt;br /&gt;
84 stkptr = -1;&lt;br /&gt;
85 endfunction&lt;br /&gt;
86&lt;br /&gt;
87 function void dump();&lt;br /&gt;
88&lt;br /&gt;
89 $write(“stack:”);&lt;br /&gt;
90 if(is_empty()) begin&lt;br /&gt;
91 $display(“&amp;lt;empty&amp;gt;”);&lt;br /&gt;
92 return;&lt;br /&gt;
93 end&lt;br /&gt;
94&lt;br /&gt;
95 for(int i = 0; i &amp;lt;= stkptr; i = i + 1) begin&lt;br /&gt;
96 $write(“ %0d”, stk[i]);&lt;br /&gt;
97 end&lt;br /&gt;
98&lt;br /&gt;
99 if(is_full())&lt;br /&gt;
100 $write(“ &amp;lt;full&amp;gt;”);&lt;br /&gt;
101 $display(““);&lt;br /&gt;
102&lt;br /&gt;
103 endfunction&lt;br /&gt;
104 endclass&lt;br /&gt;
file: 02_intro_to_OOP/01_stack/stack.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Класс stack инкапсулирует все, что нужно знать о стеке данных структуры. Он содержит интерфейс и реализацию интерфейса. Интерфейс – это набор методов, которые используются для взаимодействия с классом. Реализация -  это код, который заставляет класс работать. Интерфейс нашего стека содержит следующие методы:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;function new();&lt;br /&gt;
function bit pop(output DATA data);&lt;br /&gt;
function bit push(DATA data);&lt;br /&gt;
function bit is_full();&lt;br /&gt;
function bit is_empty();&lt;br /&gt;
function void clear();&lt;br /&gt;
function void dump();&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Не  существует  другого способа взаимодействия со стеком, как с помощью этих методов. Есть также два элемента данных  класса, stk и stkptr, которые представляют фактическую структуру стека. Тем не менее, эти два элемента локальные, что означает, что компилятор  запретит любые попытки доступа к ним  за пределами класса. Для  предотвращения доступа к внутренней структуре данных извне, мы можем сделать некоторые гарантии о состоянии данных.  Например, Push () и Pop () могут рассчитывать на то, что stkptr корректно и  указывает на вершину стека. Если бы можно было изменить значение stkptr иными средствами, чем с помощью интерфейса функций, тогда Push () и Pop () пришлось бы прибегнуть к дополнительным затратам и возможностям для ненадежной проверки истинности stkptr.&lt;br /&gt;
Реализация интерфейса встроенная. Объявление класса содержит не только определение интерфейса, а также реализацию каждой функции интерфейса. Оба C + + и SystemVerilog позволяют отделить реализацию от интерфейса. Разделение интерфейса и реализации является важной концепцией. Программисты, пишущие на C + +, могут использовать файлы заголовков для ввода интерфейс и. куб (или. срр или любой другой компилятор использует) для реализации.&lt;br /&gt;
&lt;br /&gt;
Есть некоторые важные  явления доступа через класс интерфейсов. Одним из них является возможность многократного использования. Мы можем удобно использовать классы, интерфейсы которых четко определены и хорошо объяснены, чем те, чьи интерфейсы нечеткие. Другим важным явлением обеспечения доступа через класс интерфейсов является надежность. Авторы класса может гарантировать определенную инвариантность (например, stkptr меньше, чем размер имеющегося массива STK) когда они знают, что пользователи не будут изменять данные иначе, чем предоставляемыми средствами. Кроме того, пользователи могут ожидать, что состояние объекта будет предсказуемым, когда они придерживаются интерфейса. Ясность другое явление. Интерфейс  может описать всю семантику класса. Объект не будет делать ничего другого, чем выполнять операции, доступные через интерфейс. Это делает проще понять для тех, кто использует класс, что именно он будет делать.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Отношения между объектами ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Истинная сила ООП становится очевидной, когда объекты взаимосвязаны различными отношениями. Есть много видов отношений, которые возможны. Мы рассмотрим два из наиболее фундаментальных отношений  HAS-A и IS-A.&lt;br /&gt;
&lt;br /&gt;
=== HAS-A ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
HAS-A относится к концепции, когда один объект содержится или принадлежит другим. В нашем классе стека, например,  HAS-A указатель стека (stkptr) и массив стека. Это примитивные типы данных, а не классы, но применяется тоже понятие HAS-A. В &lt;br /&gt;
SystemVerilog вы можете создать HAS-отношения между классами с  ссылками или указателями. На рисунке ниже показаны основные памяти  модель для HAS-А отношений. Объект А содержит ссылку или указатель на  Объект B.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:35.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Unified Modeling Language (UML) является графическим языком для  представления систем, в частности, отношения между объектами в этих системах. UML для HAS-связи выражается линией между объектами и закрашенным ромбиком(??????), как показано на рисунке ниже.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:36.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Объект А принадлежит экземпляру объекта B. кодирование HAS-А отношений в &lt;br /&gt;
SystemVerilog заключается в включении  одного класса в другой или других способов предоставления доступа одного класса  к одному классу, который хранится внутри.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class B;&lt;br /&gt;
endclass&lt;br /&gt;
class A;&lt;br /&gt;
local B b;&lt;br /&gt;
function new();&lt;br /&gt;
b = new();&lt;br /&gt;
endfunction&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Класс А содержит ссылку на класс B. конструктор для класса A, метод new(), вызывает new()  в классе B, чтобы создать его экземпляр.  Член b содержит ссылку на вновь созданный экземпляр B.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== IS-A ===&lt;br /&gt;
&lt;br /&gt;
IS-А отношения чаще всего называют наследованием. Новый класс является производным от ранее существующего класса и наследует его характеристики.  Объекты, созданные с наследованием наследования, созданы с использованием отношения IS-A. Производный класс  является подклассом или более специализированная версия родительского объекта.  Для иллюстрации понятия наследования, рисунок 2-3 использует часть систематики млекопитающих.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:38.png]]&lt;br /&gt;
&lt;br /&gt;
Животные, которые являются членами китообразных, хищных, приматов -  млекопитающие. Эти очень разные виды существ имеют общие черты  млекопитающих. Тем не менее, у китообразных (киты, дельфины), плотоядных (собак, медведей, енотов),  и приматов (обезьян, людей), у каждого есть свои четкие и безошибочные  характеристики.  Для использования ОО терминологии, медведь - хищник и плотоядный  IS- А млекопитающего. Иными словами, медведь состоит из атрибутов и  плотоядные млекопитающие и плюс, дополнительные атрибуты, отличающие его от  других плотоядных животных.&lt;br /&gt;
&lt;br /&gt;
Для представления   IS-A  на языке  UML, мы рисуем линию между объектами с открытыми  стрелками, указывающие на базовый класс. Традиционно, мы изображаем базовый класс  выше производных классов, а стрелки указывают вверх, образуя дерево наследования (или ориентированный ациклический граф, который может быть реализован в  языках, таких как C + +, которые поддерживают множественное наследование).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:39.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При представлении двух объектов в компьютерной программе с использованием наследования, новый производный объект содержит характеристики родителей и обычно включает в себя дополнительные характеристики. На рисунке ниже показана базовая модель памяти для IS-А композиции. В этом примере класс  B является производным от А.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:40.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
SystemVerilog использует ключевое слово  extends на определения наследования между классами:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class A;&lt;br /&gt;
int i;&lt;br /&gt;
float f;&lt;br /&gt;
endclass&lt;br /&gt;
class B extends A;&lt;br /&gt;
string s;&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Класс B получена из A, поэтому он содержит все атрибуты A. Любой экземпляр B не только содержит строку S, но и объект с плавающей запятой F и целое i.&lt;br /&gt;
&lt;br /&gt;
== Виртуальные функции и полиморфизм ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Одной из причин для создания объектов, используя наследование, является определение  различного поведения для той же операции. Другими словами, поведение, определенное в производном классе переопределяет поведение, определенное в базовом классе. Это можно сделать, используя виртуальные функции. Виртуальная функция – функция, которую можно переопределить в производном классе. Рассмотрим следующий универсальный пакет класса.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class generic_packet;&lt;br /&gt;
addr_t src_addr;&lt;br /&gt;
addr_t dest_addr;&lt;br /&gt;
bit m_header [];&lt;br /&gt;
bit m_trailer []’&lt;br /&gt;
bit m_body [];&lt;br /&gt;
virtual function void set_header();&lt;br /&gt;
virtual function void set_trailer();&lt;br /&gt;
virtual function void set_body();&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Она имеет три виртуальных функций, чтобы установить содержимое пакета. Различные виды пакетов, требуют различного содержания. Мы используем  generic_packet как базовый класс и получают различные виды пакетов из него.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class packet_A extends generic packet;&lt;br /&gt;
virtual function void set_header();&lt;br /&gt;
endfunction&lt;br /&gt;
virtual function void set_trailer();&lt;br /&gt;
endfunction&lt;br /&gt;
virtual function void set_body();&lt;br /&gt;
endfunction&lt;br /&gt;
endclass&lt;br /&gt;
class packet_B extends generic_packet;&lt;br /&gt;
virtual function void set_header();&lt;br /&gt;
endfunction&lt;br /&gt;
virtual function void set_trailer();&lt;br /&gt;
endfunction&lt;br /&gt;
virtual function void set_body();&lt;br /&gt;
endfunction&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Оба packet_A и packet_B могут иметь различные заголовки и трейлеры и  различные форматы полезной нагрузки. Информация  о том, как части пакета  отформатированы, хранится локально внутри производных классов пакета. Виртуальные  функции set_header (), set_trailer (), и set_body () реализованы по-разному в каждом подклассе основываясь на типе пакета. Базовый класс  generic_packet устанавливает организацию классов и типы операций, которые возможны, и производные классы могут изменить поведение  этих операций.&lt;br /&gt;
&lt;br /&gt;
Виртуальные функции используются для поддержки полиморфизма: несколько классов, которые могут быть использованы как взаимозаменяемые, каждый с разным поведением. Например, некоторая обработка пакетов не нуждается в информации о том, какой пакет обрабатывается. Единственной необходимой информацией является то, что объект действительно пакет, то есть, он является производным от базового класса. Еще один способ показать, что пакет является предком базового класса - это через  IS-A отношения.  Виртуальные функции являются механизмом, с помощью которого мы можем переопределять&lt;br /&gt;
поведение для различных вариантов пакетов.&lt;br /&gt;
&lt;br /&gt;
Чтобы посмотреть немного глубже, как работают виртуальные функции, давайте рассмотрим три  класса, связанных друг с другом IS-А отношением.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:45.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
figure является базовым классом; polygon является производным от figure; square происходит от polygon. Каждый класс имеет две функции, draw(), которая является виртуальной, и compute_area (), которая не является виртуальной. Следующий пример показывает,  SystemVerilog код:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;38&lt;br /&gt;
39 class figure;&lt;br /&gt;
40&lt;br /&gt;
41 virtual function void draw();&lt;br /&gt;
42 $display(“figure::draw”);&lt;br /&gt;
43 endfunction&lt;br /&gt;
44&lt;br /&gt;
45 function void compute_area();&lt;br /&gt;
46 $display(“figure::compute_area”);&lt;br /&gt;
47 endfunction&lt;br /&gt;
48&lt;br /&gt;
49 endclass&lt;br /&gt;
50&lt;br /&gt;
51 class polygon extends figure;&lt;br /&gt;
52&lt;br /&gt;
53 virtual function void draw();&lt;br /&gt;
54 $display(“polygon::draw”);&lt;br /&gt;
55 endfunction&lt;br /&gt;
56&lt;br /&gt;
57 function void compute_area();&lt;br /&gt;
58 $display(“polygon::compute_area”);&lt;br /&gt;
59 endfunction&lt;br /&gt;
60&lt;br /&gt;
61 endclass&lt;br /&gt;
62&lt;br /&gt;
63 class square extends polygon;&lt;br /&gt;
64&lt;br /&gt;
65 virtual function void draw();&lt;br /&gt;
66 $display(“square::draw”);&lt;br /&gt;
67 endfunction&lt;br /&gt;
68&lt;br /&gt;
69 function void compute_area();&lt;br /&gt;
70 $display(“square::compute_area”);&lt;br /&gt;
71 endfunction&lt;br /&gt;
72&lt;br /&gt;
73 endclass&lt;br /&gt;
file: 02_intro_to_OOP/03_virtual/virtual.sv&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
Каждая функция выводит его полное имя в виде  class_name :: имя_функции. Мы можем написать простую программу, которая показывает, как виртуальные функции связаны.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
75 program top;&lt;br /&gt;
76 figure f;&lt;br /&gt;
77 polygon p;&lt;br /&gt;
78 square s;&lt;br /&gt;
79&lt;br /&gt;
80 initial begin&lt;br /&gt;
81 s = new();&lt;br /&gt;
82 f = s;&lt;br /&gt;
83 p = s;&lt;br /&gt;
84&lt;br /&gt;
85 p.draw();&lt;br /&gt;
86 p.compute_area();&lt;br /&gt;
87 f.draw();&lt;br /&gt;
88 f.compute_area();&lt;br /&gt;
89 s.draw();&lt;br /&gt;
90 s.compute_area();&lt;br /&gt;
91 end&lt;br /&gt;
92 endprogram&lt;br /&gt;
file: 02_intro_to_OOP/03_virtual/virtual.sv&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ниже показано, что происходит, когда мы запускаем эту программу:&lt;br /&gt;
&lt;br /&gt;
[[Файл:49.png]]&lt;br /&gt;
&lt;br /&gt;
Сначала мы создаем s, квадрат, а затем назначаем его на f и р. базовым классом для square является polygon и базовым классом polygon - figure. Из напечатанного вывода, мы можем заключить, что функции связаны в соответствии со  следующей таблице:&lt;br /&gt;
&lt;br /&gt;
[[Файл:50.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Во всех случаях, compute_area () была связана с конкретным compute_area () функцией с типом ссылки, которая ее определяет -р ссылки  на polygon, таким образом  связаны  polygon :: compute_area (). Так как  compute_area () не является виртуальной функцией. Компилятор может легко определить, какие  версии функций  вызывать, основываясь на типе объекта.&lt;br /&gt;
&lt;br /&gt;
Так как draw() является виртуальной, не всегда возможно для компилятора  определить, какую функцию вызывать. Решение принимается на этапе выполнения с помощью &lt;br /&gt;
виртуальной таблицы, таблицы привязки функций. Виртуальная таблица используется для привязки  функций, привязка не может быть полностью определена во время компиляции. &lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что хотя р представляет собой polygon, вызов p.draw () приводит к  square::draw() вызывается не polygon::draw(), как вы могли бы ожидать. &lt;br /&gt;
То же самое происходит и с F-f.draw () связан с кsquare::draw(). &lt;br /&gt;
объект, который мы первоначально создан представляет собой square,  он поддерживает различные типы операций но, то, что  это square не забывается. Это  работает только потому, что square является производным от polygon, который в свою очередь является производным figure, и потому draw() объявлен как виртуальный. Ошибка компиляции О несовместимости типа возникает при попытке присвоить ей к s&lt;br /&gt;
и p   так как с не получено от р.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Универсальное программирование==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Напомним, что объектно-ориентированные языки обладают средствами для разделения программ на задачи и рассмотрения их отдельно. Импликация отдельных задач заключается в том, что каждая задача представлена  один раз. Дублирование кода нарушает принцип. На практике, многие задачи очень схожи, и имеют похожее решение, но не идентичное. Интуитивно, мы хотим написать код, который может быть использован во многих ситуациях. Это интуиция подводит нас к написанию универсального кода, кода, который высоко параметризованный, так что он может быть легко использован в самых разнообразных ситуациях.&lt;br /&gt;
&lt;br /&gt;
Подробная информация об универсальном коде поставляется во время компиляции или во время выполнения вместо жесткого кодирования. Любой код, который имеет параметры, такие как вызов функций, может, считается универсальным, но этот термин обычно используется для кода с использованием  шаблонов (в C + +) или параметризованные классы (в SystemVerilog). Написание универсальных  программ согласуется с целью  ООП  о разделении задач. Таким образом, ООП языки обеспечивают условия для создания универсального кода.&lt;br /&gt;
&lt;br /&gt;
Параметризованный класс – это класс, который (очевидно) имеет параметры.  В синтаксисе  SystemVerilog для определения параметров используется знак # в заголовке класса, после следует  список параметров в скобках. В качестве примера, рассмотрим следующий параметризованный класс:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class param #(type T=int, int R=16);&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот класс имеет два параметра, T, который является фиктивным параметром и R, который является целочисленным параметром. Экземпляры параметризованных классов с определенными значениями параметров создают специализации, то есть версии кода с примененными параметрами.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;param #(real, 29) z;&lt;br /&gt;
param #(int unsigned, 12) q;&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Приведенные выше объявления создают специализации параметризованного класса&lt;br /&gt;
param. Имя класса и параметры определяют специализацию. Таким образом,&lt;br /&gt;
специализации, по сути, уникальные типы. Компилятор не позволит вам присвоить  q к z, или наоборот, потому что они являются объектами различных типов.&lt;br /&gt;
&lt;br /&gt;
Параметр type позволяет писать типа-независимый код, код, данные и алгоритмы которого могут работать с широким спектром типов данных. Например:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class maximizer #(type T=int);&lt;br /&gt;
function T max(T a, T b);&lt;br /&gt;
if( a &amp;gt; b )&lt;br /&gt;
return a;&lt;br /&gt;
else&lt;br /&gt;
return b;&lt;br /&gt;
endfunction&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Параметризованный класс maximizer имеет функцию max(), которая возвращает&lt;br /&gt;
максимальное из двух значений. Алгоритм max() такой же, независимо от типа сравнения объектов. В этом случае, единственным ограничением является то, что объекты должны быть сравнимы оператором больше чем (&amp;gt;).&lt;br /&gt;
Классы не могут сравниваться с использованием оператора большего чем, поэтому другая версия  maximizer должна работать с классами. Для создания версии maximizer, которая будет возвращать больший из двух  классов, мы должны определить метод в каждом классе, который будет сравнивать объекты.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class maximizer #(type T=int);&lt;br /&gt;
function T max( T a, T b);&lt;br /&gt;
if( a.comp(b) &amp;gt; 0 )&lt;br /&gt;
return a;&lt;br /&gt;
else&lt;br /&gt;
return b;&lt;br /&gt;
endfunction&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это предполагает, что параметр T типа type действительно класс, а не встроенного тип, такие как int или real. Кроме того, он предполагает, что T имеет функцию под названием Comp (), которая используется для сравнения себя с другим экземпляром. Библиотека OVM содержит параметризованный компонент, называемый ovm_in_order_comparator # (T), который используется для сравнения потоков операций. Она имеет два варианта, один для сравнения потоков встроенных типов, и один для сравнения потоков классов. Причина необходимости двух компараторов в том, что SystemVerilog не поддерживает операторы, которые могут работать и с классами  и  со встроенными типами.&lt;br /&gt;
&lt;br /&gt;
=== Универсальный стек ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Наш стек не особенно универсальный. Она имеет фиксированный размер  20, а&lt;br /&gt;
тип данных элементов хранящихся в стеке  Int. Ниже приводится более общий вид стека, который изменяет эти фиксированные характеристики параметризованными.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;53 class stack #(type T = int);&lt;br /&gt;
54&lt;br /&gt;
55 local T stk[];&lt;br /&gt;
56 local int stkptr;&lt;br /&gt;
57 local int size;&lt;br /&gt;
58 local int tp;&lt;br /&gt;
59&lt;br /&gt;
60 function new(int s = 20);&lt;br /&gt;
61 size = s;&lt;br /&gt;
62 stk = new [size];&lt;br /&gt;
63 clear();&lt;br /&gt;
64 endfunction&lt;br /&gt;
65&lt;br /&gt;
66 function bit pop(output T data);&lt;br /&gt;
67&lt;br /&gt;
68 if(is_empty())&lt;br /&gt;
69 return 0;&lt;br /&gt;
70&lt;br /&gt;
71 data = stk[stkptr];&lt;br /&gt;
72 stkptr = stkptr - 1;&lt;br /&gt;
73 return 1;&lt;br /&gt;
74&lt;br /&gt;
75 endfunction&lt;br /&gt;
76&lt;br /&gt;
77 function bit push(T data);&lt;br /&gt;
78&lt;br /&gt;
79 if(is_full())&lt;br /&gt;
80 return 0;&lt;br /&gt;
81&lt;br /&gt;
82 stkptr = stkptr + 1;&lt;br /&gt;
83 stk[stkptr] = data;&lt;br /&gt;
84 return 1;&lt;br /&gt;
85&lt;br /&gt;
86 endfunction&lt;br /&gt;
87&lt;br /&gt;
88 function bit is_full();&lt;br /&gt;
89 return stkptr &amp;gt;= (size - 1);&lt;br /&gt;
90 endfunction&lt;br /&gt;
91&lt;br /&gt;
92 function bit is_empty();&lt;br /&gt;
93 return stkptr &amp;lt; 0;&lt;br /&gt;
94 endfunction&lt;br /&gt;
95&lt;br /&gt;
96 function void clear();&lt;br /&gt;
97 stkptr = -1;&lt;br /&gt;
98 tp = stkptr;&lt;br /&gt;
99 endfunction&lt;br /&gt;
100&lt;br /&gt;
101 function void traverse_init();&lt;br /&gt;
102 tp = stkptr;&lt;br /&gt;
103 endfunction&lt;br /&gt;
104&lt;br /&gt;
105 function int traverse_next(output T t);&lt;br /&gt;
106 if(tp &amp;lt; 0)&lt;br /&gt;
107 return 0; // failure&lt;br /&gt;
108&lt;br /&gt;
109 t = stk[tp];&lt;br /&gt;
110 tp = tp - 1;&lt;br /&gt;
111 return 1;&lt;br /&gt;
112&lt;br /&gt;
113 endfunction&lt;br /&gt;
114&lt;br /&gt;
115 virtual function void print(input T t);&lt;br /&gt;
116 $display(“print is unimplemented”);&lt;br /&gt;
117 endfunction&lt;br /&gt;
118&lt;br /&gt;
119 function void dump();&lt;br /&gt;
120&lt;br /&gt;
121 T t;&lt;br /&gt;
122&lt;br /&gt;
123 $write(“stack:”);&lt;br /&gt;
124 if(is_empty()) begin&lt;br /&gt;
125 $display(“&amp;lt;empty&amp;gt;”);&lt;br /&gt;
126 return;&lt;br /&gt;
127 end&lt;br /&gt;
128&lt;br /&gt;
129 traverse_init();&lt;br /&gt;
130&lt;br /&gt;
131 while(traverse_next(t)) begin&lt;br /&gt;
132 print(t);&lt;br /&gt;
133 end&lt;br /&gt;
134 $display();&lt;br /&gt;
135&lt;br /&gt;
136 endfunction&lt;br /&gt;
137&lt;br /&gt;
138 endclass&lt;br /&gt;
file: 02_intro_to_OOP/02_generic_stack/stack.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Универсальный класс stack параметризован  типом объектов стека.&lt;br /&gt;
Параметр T содержит тип. В этом случае T может быть либо классом или встроенным&lt;br /&gt;
типом. В  любом месте  класса, где мы ранее использовали Int как тип стека, теперь мы используем Т. Например, push() теперь принимает аргумент типа T. Параметры  класса,&lt;br /&gt;
такие как T,  устанавливаются во время компиляции. Для специализации stack#(T), мы инициализируем его с определенным значением для данного типа. Например:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;stack #(real) real_stack;&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот оператор создает специализацию stack, который использует real, как тип объектов стека.&lt;br /&gt;
Размер стека больше не зафиксирована на уровне 20. Мы используем динамический массив для хранения стека, размер которого указан в качестве параметра конструктора. В отличие от T, аргумент size задается при выполнении  программы. Это позволяет нам создать несколько стеков, каждый с разным размером.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;stack #(real) big_stack;&lt;br /&gt;
stack #(real) little_stack;&lt;br /&gt;
...&lt;br /&gt;
big_stack = new(2048);&lt;br /&gt;
little_stack = new(6);&amp;lt;/source&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
big_stack и little_stack одного типа. Они используют одну и ту же специализацию stack#(T). Тем не менее, каждый из них создается с различными параметрами размера. &lt;br /&gt;
&lt;br /&gt;
В процессе создания универсального стека, мы сделали еще одно изменение. Мы заменили dump()  на  traverse_init () и traverse_next (). dump() зависит от типа элементов стека, которые не известны до компиляции. Мы должны иметь возможность пройти по стеку и отформатировать каждый элемент, не зависимо от типа элемента. Это может быть INT, или это может быть сложный класс с несколькими членами. Чтобы сохранить stack#(T) универсальным, мы должны исключить  любую зависимость от типа элементов стека.&lt;br /&gt;
&lt;br /&gt;
В то время как dump() пробежит по всем элементам стека и напечатает их в определенном порядке, traverse_init () устанавливает внутренний указатель(tp) на вершину стека, а traverse_next () возвращает текущий элемент и уменьшает tp. Стек поддерживает некоторую статическую информацию  обхода. Статическая   информация сбрасывается, когда вызывается traverse_init ().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Классы  и Модули==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Интересно, что HDLs, такие как Verilog и VHDL, хотя они не считаются объектно-ориентированными языками, строятся на концепции очень схожей  классам и объектам. Модули  в Verilog, например, являются объектами, каждый из которых имеет&lt;br /&gt;
свое собственное пространство данных и набор задач и функций. Так же, как объекты в ОО программах, каждый экземпляр модуля является независимой копией. Все экземпляры имеют один и тот же набор задач и функций и те же интерфейсы, но данные содержащихся внутри каждого является независимым от всех остальных экземпляров. Модули контролируются их интерфейсами. Verilog модули не поддерживают наследование или  параметризации типов, и они являются статическими, что делает их непригодными для истинного ООП.&lt;br /&gt;
&lt;br /&gt;
Сходство между классами и модулями открывает возможность для нас использовать объекты класса в аппаратном контексте. Мы можем создать компоненты верификации как экземпляры классов, что дает нам гибкость при подключении к аппаратным элементам. Дизайнеры SystemVerilog выигрывают  при расширении Verilog  классами, предоставляя возможность для классов работать как модули.&lt;br /&gt;
&lt;br /&gt;
В приведенной ниже таблице сравниваются особенности классов в Verilog, SystemVerilog, и C + +.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:60.png]]&lt;br /&gt;
&lt;br /&gt;
В SystemVerilog есть особенность, которая делает это возможным это виртуальный интерфейс. Виртуальный интерфейс является ссылкой на интерфейс (здесь мы имеем в виду SystemVerilog интерфейс). Мы можем написать класс, содержащий ссылки&lt;br /&gt;
к элементам внутри интерфейса, которых еще не существует (то есть, они не создаются). Когда создается экземпляр класса, виртуальный интерфейс подключается к реальному интерфейсу. Это делает возможным для объекта класса реагировать и отвечать на входные изменения. Модули  SystemC реализованы в виде классов и позволяют входам быть в списке портов, обеспечивая такую же структуру.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В HDLs, таких как Verilog и VHDL, не хватает многих возможностей ООП, и, следовательно, не хорошо подходят для построения testbenches. Основной единицей программирования в большинстве HDLs является модуль, и это статический объект. Модули создаются в самом начале программы и сохраняются не измененными до завершения программы. Они синтаксически статические и это означает, что&lt;br /&gt;
средства для изменения модулей, создания другого варианта, ограничены. Verilog позволяет параметризовать скалярные значения, но не типы. Часто все сводятся к резке и вставке кода, а затем внесению локальных изменений. Если у вас есть десять различных вариаций нужных в конкретном проекте, необходимо вставить десять копий в соответствующие места, а затем локально изменить каждую из них. &lt;br /&gt;
&lt;br /&gt;
== ООП и Верификация==&lt;br /&gt;
&lt;br /&gt;
Создание объектно-ориентированных программ и создания testbench мало чем отличается. testbench представляет собой сеть взаимодействующих компонентов. ООП работает с определением и анализом сетей взаимодействующих объектов. Объекты могут быть связаны IS-A или HAS-A отношениями, и они общаются через интерфейсы.&lt;br /&gt;
ООП просто естественно вписывается проблему создания testbenches.&lt;br /&gt;
&lt;br /&gt;
Языки, таких как SystemC / C + + и SystemVerilog, которые действительно поддерживают возможности ООП ,  лучше подходят для создания testbench, чем HDLs, такие как Verilog и VHDL. Используя динамические классы, параметризованные классы, наследование и параметризованные конструкторы, вы можете создать компоненты, которые являются гибкими, повторно используемыми и надежные. Потратив немного больше времени, чтобы построить универсальную компоненту, приведет вас приросту производительности, когда эта компонент повторно используется по-разному в разных местах.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F</id>
		<title>OVM/OVM методология/Основы объектно-ориентированного программирования</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F"/>
				<updated>2013-03-05T12:13:34Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Разработка программного обеспечения, не связанная с физикой электричества и магнетизма, уже давно пытается создать многократно используемые, взаимозаменяемые, надежные компоненты. Важной моделью программирования, которая решает проблему, называется объектно-ориентированным программированием (ООП). Основная идея ООП заключается в том, что программы организуются как набор взаимодействующих объектов, каждый со своим собственным областью данных и функций. Объекты могут быть многократно использованы, потому что они инкапсулируют все, что им нужно для работы, могут быть построены с минимальным числом или без внешних зависимостей, и может быть высоко параметризованы. &lt;br /&gt;
В этой главе вводятся основные понятия объектно-ориентированного программирования, в том числе понятия инкапсуляции и интерфейса. Глава завершается обсуждением того, почему ООП является важным для построения testbenches.&lt;br /&gt;
&lt;br /&gt;
== Процедурные против ООП==&lt;br /&gt;
&lt;br /&gt;
Чтобы понять ООП и его роль в проверке, необходимо сначала понять традиционное процедурное программирование и его ограничения. Это даст основу для понимания того, как ООП может преодолеть эти ограничения. &lt;br /&gt;
В первые дни  языка  ассемблера, программисты и компьютерные архитекторы быстро обнаружили, что программы часто содержат последовательности инструкций, которые повторяются во всей программе. Повторение кода (в частности, с перфокарт) является трудоемким и порождает ошибки. Чтобы избежать трудоемкости и ошибок, вызванных повторением последовательностей,  была изобретена подпрограмма. Подпрограмма является единицей повторно используемого кода. Вместо кодирования той же последовательности встроенных инструкций, вы вызываете подпрограмму. Параметры, передаваемые в подпрограмму, позволяют динамически изменять код. То есть, каждый вызов подпрограммы с различными значениями параметров заставляет подпрограмму вести себя по-разному в зависимости от конкретных значений параметров.&lt;br /&gt;
&lt;br /&gt;
Каждый язык программирования любой значимости  имеет  конструкции для создания подпрограмм, процедур и функций, а также синтаксис для передачи в них параметров и возвращаемых значений. Эти функции могут использоваться для создания операций, которые часто используются. Тем не менее, некоторые операции очень распространены (таких как I / O, преобразования данных, численные методы и т. д.). И во избежание переписывания этих операций, программисты посчитали необходимым создать библиотеки часто используемых функций. В результате, большинство языков программирования включает такие библиотеки как часть пакета компилятора. Одним из наиболее известных примеров является библиотека C, которая поступает с каждым компилятором C. Она содержит полезные функции, такие как printf() (), COS (), atof (), и QSort (). Эти функции  практически каждый программист будет использовать в тот или иной момент.&lt;br /&gt;
Представьте себе, что писать собственные подпрограммы ввода / вывода или ваши собственные вычисления для преобразования чисел в строки и строк в числа. Был момент, когда программисты так и делали. Библиотеки универсальных функций все изменили и увеличили общую производительность программирования.&lt;br /&gt;
&lt;br /&gt;
Как показывает практика программного обеспечения и передовых технологий, программисты начали думать на более высоком уровне абстракции, чем инструкции и процедуры. Вместо написания отдельных инструкций, программисты пишут код на языках, которые обеспечивают более высокую модель абстракции компьютера, и компиляторы или интерпретаторы  переводят эти модели в конкретные инструкции. Библиотеки, такие как C библиотеки или STL в C + +, являются одной из форм абстракции. Они представляет собой набор функций, которые программисты могут использовать для создания все более сложных программы или абстракции.&lt;br /&gt;
В своей основополагающей книге Алгоритмы + Структуры данных = Программы, Никлаус Вирт объясняет, что для решения любой задачи программирования, вы должны разработать абстракцию реальности, которая имеет характеристики и свойства задачи, и игнорировать остальные детали. Он утверждает, что набор данных, которые вам нужны для решения проблемы, формирует абстракцию. Поэтому, прежде чем вы сможете решить проблема, в первую очередь необходимо определить, какие данные нужно иметь, чтобы создать решение.&lt;br /&gt;
Чтобы продолжить создание многократно используемой абстракции, мы должны создавать библиотеки данных объектов, которые могут быть многократно использованы для решения конкретных видов проблем. Поиск способа сделать это приводит к развитию объектно-ориентированных технологий.&lt;br /&gt;
&lt;br /&gt;
Объектно-ориентированный анализ и проектирование программ сосредоточен вокруг данных объектов, функциональности, связанной с каждым объектом, и отношения между объектами.&lt;br /&gt;
&lt;br /&gt;
Объектно-ориентированные языки предоставляют средства для отделения проблем программы, и  выделения каждой из них независимо друг от друга, и для инкапсуляции данных абстракций и представление их через строго определенные интерфейсы. Полностью объектно-ориентированная программа строится путем разделения функциональности программы в различные классы, определения интерфейса для каждого класса, а затем установления связей и взаимодействий между компонентами через их интерфейсы.&lt;br /&gt;
&lt;br /&gt;
==Классы и объекты==&lt;br /&gt;
&lt;br /&gt;
Основным модулем программирования на объектно-ориентированных языках, таких как SystemVerilog, является класс. Класс содержит элементы данных, называемые членами, и задач и функций, называемых методами. Для выполнения объектно-ориентированных программ, вы должны создать один или более классов в основной программе, а затем вызвать методы на различных объектах. Хотя термины класс и объект иногда используются как синонимы, как правило, термин класс относится к классу декларации объекта, а термин объект относится к экземпляру класса.&lt;br /&gt;
Чтобы проиллюстрировать эти понятия, ниже приведен пример простой класс register.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:27.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:28.png]]&lt;br /&gt;
&lt;br /&gt;
Это очень простой класс с одной переменной, и двумя методами, read() и write(). Чтобы использовать этот класс, вы создаете объекты путем создания экземпляра класса, а затем вызвать методы объекта, как показано ниже&lt;br /&gt;
&lt;br /&gt;
[[Файл:29.png]]&lt;br /&gt;
&lt;br /&gt;
Локальный  атрибут contents  класса указывает компилятору строго соблюдать границы класса. Если вы пытаетесь получить доступ к contents напрямую, компилятор выдаст ошибку. Вы можете получить доступ к содержимому через  доступные функции чтения и записи. Этот вид контроля доступа важен для  гарантирования  независимости от внутренностей класса и тем самым делает класс многократно используемым.&lt;br /&gt;
Вы можете использовать классы для создания новых типов данных, таких, как наш простой register. Использование классов для создания новых типов данных является важной частью ООП. Вы можете также использовать их для инкапсуляции математических вычислений или создания динамических структур данных, таких как стеки, списки, очереди, и так далее. Инкапсуляция организации структур данных или сведений о вычислениях в классе делает структуру данных или вычислений многократно используемыми.&lt;br /&gt;
В качестве более полного примера, давайте посмотрим на полезный тип данных, стек. Стек - это LIFO (&amp;quot;последним пришёл - первым обслужен&amp;quot;) структура. Элементы помещаются в стек командой push(), а  извлекаются из стека pop(). Pop () возвращает последний элемент, помещенный в стек,  и удаляет его из структуры данных. Внутренние элемент  stkptr  следит за вершиной стека. Элемент, на который он указывает, это вершина стека, а все, что ниже его (то есть, с меньшим индексом) ниже в стеке. Ниже приведена базовая реализация стека в SystemVerilog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:30.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:31.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:32.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:33.png]]&lt;br /&gt;
&lt;br /&gt;
Класс stack инкапсулирует все, что нужно знать о стеке данных структуры. Он содержит интерфейс и реализацию интерфейса. Интерфейс – это набор методов, которые используются для взаимодействия с классом. Реализация -  это код, который заставляет класс работать. Интерфейс нашего стека содержит следующие методы:&lt;br /&gt;
&lt;br /&gt;
[[Файл:34.png]]&lt;br /&gt;
&lt;br /&gt;
Не  существует  другого способа взаимодействия со стеком, как с помощью этих методов. Есть также два элемента данных  класса, stk и stkptr, которые представляют фактическую структуру стека. Тем не менее, эти два элемента локальные, что означает, что компилятор  запретит любые попытки доступа к ним  за пределами класса. Для  предотвращения доступа к внутренней структуре данных извне, мы можем сделать некоторые гарантии о состоянии данных.  Например, Push () и Pop () могут рассчитывать на то, что stkptr корректно и  указывает на вершину стека. Если бы можно было изменить значение stkptr иными средствами, чем с помощью интерфейса функций, тогда Push () и Pop () пришлось бы прибегнуть к дополнительным затратам и возможностям для ненадежной проверки истинности stkptr.&lt;br /&gt;
Реализация интерфейса встроенная. Объявление класса содержит не только определение интерфейса, а также реализацию каждой функции интерфейса. Оба C + + и SystemVerilog позволяют отделить реализацию от интерфейса. Разделение интерфейса и реализации является важной концепцией. Программисты, пишущие на C + +, могут использовать файлы заголовков для ввода интерфейс и. куб (или. срр или любой другой компилятор использует) для реализации.&lt;br /&gt;
&lt;br /&gt;
Есть некоторые важные  явления доступа через класс интерфейсов. Одним из них является возможность многократного использования. Мы можем удобно использовать классы, интерфейсы которых четко определены и хорошо объяснены, чем те, чьи интерфейсы нечеткие. Другим важным явлением обеспечения доступа через класс интерфейсов является надежность. Авторы класса может гарантировать определенную инвариантность (например, stkptr меньше, чем размер имеющегося массива STK) когда они знают, что пользователи не будут изменять данные иначе, чем предоставляемыми средствами. Кроме того, пользователи могут ожидать, что состояние объекта будет предсказуемым, когда они придерживаются интерфейса. Ясность другое явление. Интерфейс  может описать всю семантику класса. Объект не будет делать ничего другого, чем выполнять операции, доступные через интерфейс. Это делает проще понять для тех, кто использует класс, что именно он будет делать.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Отношения между объектами ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Истинная сила ООП становится очевидной, когда объекты взаимосвязаны различными отношениями. Есть много видов отношений, которые возможны. Мы рассмотрим два из наиболее фундаментальных отношений  HAS-A и IS-A.&lt;br /&gt;
&lt;br /&gt;
=== HAS-A ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
HAS-A относится к концепции, когда один объект содержится или принадлежит другим. В нашем классе стека, например,  HAS-A указатель стека (stkptr) и массив стека. Это примитивные типы данных, а не классы, но применяется тоже понятие HAS-A. В &lt;br /&gt;
SystemVerilog вы можете создать HAS-отношения между классами с  ссылками или указателями. На рисунке ниже показаны основные памяти  модель для HAS-А отношений. Объект А содержит ссылку или указатель на  Объект B.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:35.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Unified Modeling Language (UML) является графическим языком для  представления систем, в частности, отношения между объектами в этих системах. UML для HAS-связи выражается линией между объектами и закрашенным ромбиком(??????), как показано на рисунке ниже.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:36.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Объект А принадлежит экземпляру объекта B. кодирование HAS-А отношений в &lt;br /&gt;
SystemVerilog заключается в включении  одного класса в другой или других способов предоставления доступа одного класса  к одному классу, который хранится внутри.&lt;br /&gt;
&lt;br /&gt;
[[Файл:37.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Класс А содержит ссылку на класс B. конструктор для класса A, метод new(), вызывает new()  в классе B, чтобы создать его экземпляр.  Член b содержит ссылку на вновь созданный экземпляр B.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== IS-A ===&lt;br /&gt;
&lt;br /&gt;
IS-А отношения чаще всего называют наследованием. Новый класс является производным от ранее существующего класса и наследует его характеристики.  Объекты, созданные с наследованием наследования, созданы с использованием отношения IS-A. Производный класс  является подклассом или более специализированная версия родительского объекта.  Для иллюстрации понятия наследования, рисунок 2-3 использует часть систематики млекопитающих.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:38.png]]&lt;br /&gt;
&lt;br /&gt;
Животные, которые являются членами китообразных, хищных, приматов -  млекопитающие. Эти очень разные виды существ имеют общие черты  млекопитающих. Тем не менее, у китообразных (киты, дельфины), плотоядных (собак, медведей, енотов),  и приматов (обезьян, людей), у каждого есть свои четкие и безошибочные  характеристики.  Для использования ОО терминологии, медведь - хищник и плотоядный  IS- А млекопитающего. Иными словами, медведь состоит из атрибутов и  плотоядные млекопитающие и плюс, дополнительные атрибуты, отличающие его от  других плотоядных животных.&lt;br /&gt;
&lt;br /&gt;
Для представления   IS-A  на языке  UML, мы рисуем линию между объектами с открытыми  стрелками, указывающие на базовый класс. Традиционно, мы изображаем базовый класс  выше производных классов, а стрелки указывают вверх, образуя дерево наследования (или ориентированный ациклический граф, который может быть реализован в  языках, таких как C + +, которые поддерживают множественное наследование).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:39.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При представлении двух объектов в компьютерной программе с использованием наследования, новый производный объект содержит характеристики родителей и обычно включает в себя дополнительные характеристики. На рисунке ниже показана базовая модель памяти для IS-А композиции. В этом примере класс  B является производным от А.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:40.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
SystemVerilog использует ключевое слово  extends на определения наследования между классами:&lt;br /&gt;
&lt;br /&gt;
[[Файл:41.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Класс B получена из A, поэтому он содержит все атрибуты A. Любой экземпляр B не только содержит строку S, но и объект с плавающей запятой F и целое i.&lt;br /&gt;
&lt;br /&gt;
== Виртуальные функции и полиморфизм ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Одной из причин для создания объектов, используя наследование, является определение  различного поведения для той же операции. Другими словами, поведение, определенное в производном классе переопределяет поведение, определенное в базовом классе. Это можно сделать, используя виртуальные функции. Виртуальная функция – функция, которую можно переопределить в производном классе. Рассмотрим следующий универсальный пакет класса.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:42.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:43.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Она имеет три виртуальных функций, чтобы установить содержимое пакета. Различные виды пакетов, требуют различного содержания. Мы используем  generic_packet как базовый класс и получают различные виды пакетов из него.&lt;br /&gt;
&lt;br /&gt;
[[Файл:44.png]]&lt;br /&gt;
&lt;br /&gt;
Оба packet_A и packet_B могут иметь различные заголовки и трейлеры и  различные форматы полезной нагрузки. Информация  о том, как части пакета  отформатированы, хранится локально внутри производных классов пакета. Виртуальные  функции set_header (), set_trailer (), и set_body () реализованы по-разному в каждом подклассе основываясь на типе пакета. Базовый класс  generic_packet устанавливает организацию классов и типы операций, которые возможны, и производные классы могут изменить поведение  этих операций.&lt;br /&gt;
&lt;br /&gt;
Виртуальные функции используются для поддержки полиморфизма: несколько классов, которые могут быть использованы как взаимозаменяемые, каждый с разным поведением. Например, некоторая обработка пакетов не нуждается в информации о том, какой пакет обрабатывается. Единственной необходимой информацией является то, что объект действительно пакет, то есть, он является производным от базового класса. Еще один способ показать, что пакет является предком базового класса - это через  IS-A отношения.  Виртуальные функции являются механизмом, с помощью которого мы можем переопределять&lt;br /&gt;
поведение для различных вариантов пакетов.&lt;br /&gt;
&lt;br /&gt;
Чтобы посмотреть немного глубже, как работают виртуальные функции, давайте рассмотрим три  класса, связанных друг с другом IS-А отношением.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:45.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
figure является базовым классом; polygon является производным от figure; square происходит от polygon. Каждый класс имеет две функции, draw(), которая является виртуальной, и compute_area (), которая не является виртуальной. Следующий пример показывает,  SystemVerilog код:&lt;br /&gt;
&lt;br /&gt;
[[Файл:46.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:47.png]]&lt;br /&gt;
&lt;br /&gt;
Каждая функция выводит его полное имя в виде  class_name :: имя_функции. Мы можем написать простую программу, которая показывает, как виртуальные функции связаны.&lt;br /&gt;
&lt;br /&gt;
[[Файл:48.png]]&lt;br /&gt;
&lt;br /&gt;
Ниже показано, что происходит, когда мы запускаем эту программу:&lt;br /&gt;
&lt;br /&gt;
[[Файл:49.png]]&lt;br /&gt;
&lt;br /&gt;
Сначала мы создаем s, квадрат, а затем назначаем его на f и р. базовым классом для square является polygon и базовым классом polygon - figure. Из напечатанного вывода, мы можем заключить, что функции связаны в соответствии со  следующей таблице:&lt;br /&gt;
&lt;br /&gt;
[[Файл:50.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Во всех случаях, compute_area () была связана с конкретным compute_area () функцией с типом ссылки, которая ее определяет -р ссылки  на polygon, таким образом  связаны  polygon :: compute_area (). Так как  compute_area () не является виртуальной функцией. Компилятор может легко определить, какие  версии функций  вызывать, основываясь на типе объекта.&lt;br /&gt;
&lt;br /&gt;
 Так как draw() является виртуальной, не всегда возможно для компилятора  определить, какую функцию вызывать. Решение принимается на этапе выполнения с помощью &lt;br /&gt;
виртуальной таблицы, таблицы привязки функций. Виртуальная таблица используется для привязки  функций, привязка не может быть полностью определена во время компиляции. &lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что хотя р представляет собой polygon, вызов p.draw () приводит к  square::draw() вызывается не polygon::draw(), как вы могли бы ожидать. &lt;br /&gt;
То же самое происходит и с F-f.draw () связан с кsquare::draw(). &lt;br /&gt;
объект, который мы первоначально создан представляет собой square,  он поддерживает различные типы операций но, то, что  это square не забывается. Это  работает только потому, что square является производным от polygon, который в свою очередь является производным figure, и потому draw() объявлен как виртуальный. Ошибка компиляции О несовместимости типа возникает при попытке присвоить ей к s&lt;br /&gt;
 и p   так как с не получено от р.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Универсальное программирование==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Напомним, что объектно-ориентированные языки обладают средствами для разделения программ на задачи и рассмотрения их отдельно. Импликация отдельных задач заключается в том, что каждая задача представлена  один раз. Дублирование кода нарушает принцип. На практике, многие задачи очень схожи, и имеют похожее решение, но не идентичное. Интуитивно, мы хотим написать код, который может быть использован во многих ситуациях. Это интуиция подводит нас к написанию универсального кода, кода, который высоко параметризованный, так что он может быть легко использован в самых разнообразных ситуациях.&lt;br /&gt;
&lt;br /&gt;
Подробная информация об универсальном коде поставляется во время компиляции или во время выполнения вместо жесткого кодирования. Любой код, который имеет параметры, такие как вызов функций, может, считается универсальным, но этот термин обычно используется для кода с использованием  шаблонов (в C + +) или параметризованные классы (в SystemVerilog). Написание универсальных  программ согласуется с целью  ООП  о разделении задач. Таким образом, ООП языки обеспечивают условия для создания универсального кода.&lt;br /&gt;
&lt;br /&gt;
Параметризованный класс – это класс, который (очевидно) имеет параметры.  В синтаксисе  SystemVerilog для определения параметров используется знак # в заголовке класса, после следует  список параметров в скобках. В качестве примера, рассмотрим следующий параметризованный класс:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class param #(type T=int, int R=16);&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот класс имеет два параметра, T, который является фиктивным параметром и R, который является целочисленным параметром. Экземпляры параметризованных классов с определенными значениями параметров создают специализации, то есть версии кода с примененными параметрами.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;param #(real, 29) z;&lt;br /&gt;
param #(int unsigned, 12) q;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Приведенные выше объявления создают специализации параметризованного класса&lt;br /&gt;
param. Имя класса и параметры определяют специализацию. Таким образом,&lt;br /&gt;
специализации, по сути, уникальные типы. Компилятор не позволит вам присвоить  q к z, или наоборот, потому что они являются объектами различных типов.&lt;br /&gt;
&lt;br /&gt;
Параметр type позволяет писать типа-независимый код, код, данные и алгоритмы которого могут работать с широким спектром типов данных. Например:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class maximizer #(type T=int);&lt;br /&gt;
function T max(T a, T b);&lt;br /&gt;
if( a &amp;gt; b )&lt;br /&gt;
return a;&lt;br /&gt;
else&lt;br /&gt;
return b;&lt;br /&gt;
endfunction&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Параметризованный класс maximizer имеет функцию max(), которая возвращает&lt;br /&gt;
максимальное из двух значений. Алгоритм max() такой же, независимо от типа сравнения объектов. В этом случае, единственным ограничением является то, что объекты должны быть сравнимы оператором больше чем (&amp;gt;).&lt;br /&gt;
Классы не могут сравниваться с использованием оператора большего чем, поэтому другая версия  maximizer должна работать с классами. Для создания версии maximizer, которая будет возвращать больший из двух  классов, мы должны определить метод в каждом классе, который будет сравнивать объекты.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class maximizer #(type T=int);&lt;br /&gt;
function T max( T a, T b);&lt;br /&gt;
if( a.comp(b) &amp;gt; 0 )&lt;br /&gt;
return a;&lt;br /&gt;
else&lt;br /&gt;
return b;&lt;br /&gt;
endfunction&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это предполагает, что параметр T типа type действительно класс, а не встроенного тип, такие как int или real. Кроме того, он предполагает, что T имеет функцию под названием Comp (), которая используется для сравнения себя с другим экземпляром. Библиотека OVM содержит параметризованный компонент, называемый ovm_in_order_comparator # (T), который используется для сравнения потоков операций. Она имеет два варианта, один для сравнения потоков встроенных типов, и один для сравнения потоков классов. Причина необходимости двух компараторов в том, что SystemVerilog не поддерживает операторы, которые могут работать и с классами  и  со встроенными типами.&lt;br /&gt;
&lt;br /&gt;
=== Универсальный стек ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Наш стек не особенно универсальный. Она имеет фиксированный размер  20, а&lt;br /&gt;
тип данных элементов хранящихся в стеке  Int. Ниже приводится более общий вид стека, который изменяет эти фиксированные характеристики параметризованными.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;53 class stack #(type T = int);&lt;br /&gt;
54&lt;br /&gt;
55 local T stk[];&lt;br /&gt;
56 local int stkptr;&lt;br /&gt;
57 local int size;&lt;br /&gt;
58 local int tp;&lt;br /&gt;
59&lt;br /&gt;
60 function new(int s = 20);&lt;br /&gt;
61 size = s;&lt;br /&gt;
62 stk = new [size];&lt;br /&gt;
63 clear();&lt;br /&gt;
64 endfunction&lt;br /&gt;
65&lt;br /&gt;
66 function bit pop(output T data);&lt;br /&gt;
67&lt;br /&gt;
68 if(is_empty())&lt;br /&gt;
69 return 0;&lt;br /&gt;
70&lt;br /&gt;
71 data = stk[stkptr];&lt;br /&gt;
72 stkptr = stkptr - 1;&lt;br /&gt;
73 return 1;&lt;br /&gt;
74&lt;br /&gt;
75 endfunction&lt;br /&gt;
76&lt;br /&gt;
77 function bit push(T data);&lt;br /&gt;
78&lt;br /&gt;
79 if(is_full())&lt;br /&gt;
80 return 0;&lt;br /&gt;
81&lt;br /&gt;
82 stkptr = stkptr + 1;&lt;br /&gt;
83 stk[stkptr] = data;&lt;br /&gt;
84 return 1;&lt;br /&gt;
85&lt;br /&gt;
86 endfunction&lt;br /&gt;
87&lt;br /&gt;
88 function bit is_full();&lt;br /&gt;
89 return stkptr &amp;gt;= (size - 1);&lt;br /&gt;
90 endfunction&lt;br /&gt;
91&lt;br /&gt;
92 function bit is_empty();&lt;br /&gt;
93 return stkptr &amp;lt; 0;&lt;br /&gt;
94 endfunction&lt;br /&gt;
95&lt;br /&gt;
96 function void clear();&lt;br /&gt;
97 stkptr = -1;&lt;br /&gt;
98 tp = stkptr;&lt;br /&gt;
99 endfunction&lt;br /&gt;
100&lt;br /&gt;
101 function void traverse_init();&lt;br /&gt;
102 tp = stkptr;&lt;br /&gt;
103 endfunction&lt;br /&gt;
104&lt;br /&gt;
105 function int traverse_next(output T t);&lt;br /&gt;
106 if(tp &amp;lt; 0)&lt;br /&gt;
107 return 0; // failure&lt;br /&gt;
108&lt;br /&gt;
109 t = stk[tp];&lt;br /&gt;
110 tp = tp - 1;&lt;br /&gt;
111 return 1;&lt;br /&gt;
112&lt;br /&gt;
113 endfunction&lt;br /&gt;
114&lt;br /&gt;
115 virtual function void print(input T t);&lt;br /&gt;
116 $display(“print is unimplemented”);&lt;br /&gt;
117 endfunction&lt;br /&gt;
118&lt;br /&gt;
119 function void dump();&lt;br /&gt;
120&lt;br /&gt;
121 T t;&lt;br /&gt;
122&lt;br /&gt;
123 $write(“stack:”);&lt;br /&gt;
124 if(is_empty()) begin&lt;br /&gt;
125 $display(“&amp;lt;empty&amp;gt;”);&lt;br /&gt;
126 return;&lt;br /&gt;
127 end&lt;br /&gt;
128&lt;br /&gt;
129 traverse_init();&lt;br /&gt;
130&lt;br /&gt;
131 while(traverse_next(t)) begin&lt;br /&gt;
132 print(t);&lt;br /&gt;
133 end&lt;br /&gt;
134 $display();&lt;br /&gt;
135&lt;br /&gt;
136 endfunction&lt;br /&gt;
137&lt;br /&gt;
138 endclass&lt;br /&gt;
file: 02_intro_to_OOP/02_generic_stack/stack.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Универсальный класс stack параметризован  типом объектов стека.&lt;br /&gt;
Параметр T содержит тип. В этом случае T может быть либо классом или встроенным&lt;br /&gt;
типом. В  любом месте  класса, где мы ранее использовали Int как тип стека, теперь мы используем Т. Например, push() теперь принимает аргумент типа T. Параметры  класса,&lt;br /&gt;
такие как T,  устанавливаются во время компиляции. Для специализации stack#(T), мы инициализируем его с определенным значением для данного типа. Например:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;stack #(real) real_stack;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот оператор создает специализацию stack, который использует real, как тип объектов стека.&lt;br /&gt;
Размер стека больше не зафиксирована на уровне 20. Мы используем динамический массив для хранения стека, размер которого указан в качестве параметра конструктора. В отличие от T, аргумент size задается при выполнении  программы. Это позволяет нам создать несколько стеков, каждый с разным размером.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;stack #(real) big_stack;&lt;br /&gt;
stack #(real) little_stack;&lt;br /&gt;
...&lt;br /&gt;
big_stack = new(2048);&lt;br /&gt;
little_stack = new(6);&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
big_stack и little_stack одного типа. Они используют одну и ту же специализацию stack#(T). Тем не менее, каждый из них создается с различными параметрами размера. &lt;br /&gt;
&lt;br /&gt;
В процессе создания универсального стека, мы сделали еще одно изменение. Мы заменили dump()  на  traverse_init () и traverse_next (). dump() зависит от типа элементов стека, которые не известны до компиляции. Мы должны иметь возможность пройти по стеку и отформатировать каждый элемент, не зависимо от типа элемента. Это может быть INT, или это может быть сложный класс с несколькими членами. Чтобы сохранить stack#(T) универсальным, мы должны исключить  любую зависимость от типа элементов стека.&lt;br /&gt;
&lt;br /&gt;
В то время как dump() пробежит по всем элементам стека и напечатает их в определенном порядке, traverse_init () устанавливает внутренний указатель(tp) на вершину стека, а traverse_next () возвращает текущий элемент и уменьшает tp. Стек поддерживает некоторую статическую информацию  обхода. Статическая   информация сбрасывается, когда вызывается traverse_init ().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Классы  и Модули==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Интересно, что HDLs, такие как Verilog и VHDL, хотя они не считаются объектно-ориентированными языками, строятся на концепции очень схожей  классам и объектам. Модули  в Verilog, например, являются объектами, каждый из которых имеет&lt;br /&gt;
свое собственное пространство данных и набор задач и функций. Так же, как объекты в ОО программах, каждый экземпляр модуля является независимой копией. Все экземпляры имеют один и тот же набор задач и функций и те же интерфейсы, но данные содержащихся внутри каждого является независимым от всех остальных экземпляров. Модули контролируются их интерфейсами. Verilog модули не поддерживают наследование или  параметризации типов, и они являются статическими, что делает их непригодными для истинного ООП.&lt;br /&gt;
&lt;br /&gt;
Сходство между классами и модулями открывает возможность для нас использовать объекты класса в аппаратном контексте. Мы можем создать компоненты верификации как экземпляры классов, что дает нам гибкость при подключении к аппаратным элементам. Дизайнеры SystemVerilog выигрывают  при расширении Verilog  классами, предоставляя возможность для классов работать как модули.&lt;br /&gt;
&lt;br /&gt;
В приведенной ниже таблице сравниваются особенности классов в Verilog, SystemVerilog, и C + +.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:60.png]]&lt;br /&gt;
&lt;br /&gt;
В SystemVerilog есть особенность, которая делает это возможным это виртуальный интерфейс. Виртуальный интерфейс является ссылкой на интерфейс (здесь мы имеем в виду SystemVerilog интерфейс). Мы можем написать класс, содержащий ссылки&lt;br /&gt;
к элементам внутри интерфейса, которых еще не существует (то есть, они не создаются). Когда создается экземпляр класса, виртуальный интерфейс подключается к реальному интерфейсу. Это делает возможным для объекта класса реагировать и отвечать на входные изменения. Модули  SystemC реализованы в виде классов и позволяют входам быть в списке портов, обеспечивая такую же структуру.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 В HDLs, таких как Verilog и VHDL, не хватает многих возможностей ООП, и, следовательно, не хорошо подходят для построения testbenches. Основной единицей программирования в большинстве HDLs является модуль, и это статический объект. Модули создаются в самом начале программы и сохраняются не измененными до завершения программы. Они синтаксически статические и это означает, что&lt;br /&gt;
средства для изменения модулей, создания другого варианта, ограничены. Verilog позволяет параметризовать скалярные значения, но не типы. Часто все сводятся к резке и вставке кода, а затем внесению локальных изменений. Если у вас есть десять различных вариаций нужных в конкретном проекте, необходимо вставить десять копий в соответствующие места, а затем локально изменить каждую из них. &lt;br /&gt;
&lt;br /&gt;
== ООП и Верификация==&lt;br /&gt;
&lt;br /&gt;
Создание объектно-ориентированных программ и создания testbench мало чем отличается. testbench представляет собой сеть взаимодействующих компонентов. ООП работает с определением и анализом сетей взаимодействующих объектов. Объекты могут быть связаны IS-A или HAS-A отношениями, и они общаются через интерфейсы.&lt;br /&gt;
ООП просто естественно вписывается проблему создания testbenches.&lt;br /&gt;
&lt;br /&gt;
Языки, таких как SystemC / C + + и SystemVerilog, которые действительно поддерживают возможности ООП ,  лучше подходят для создания testbench, чем HDLs, такие как Verilog и VHDL. Используя динамические классы, параметризованные классы, наследование и параметризованные конструкторы, вы можете создать компоненты, которые являются гибкими, повторно используемыми и надежные. Потратив немного больше времени, чтобы построить универсальную компоненту, приведет вас приросту производительности, когда эта компонент повторно используется по-разному в разных местах.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:60.png</id>
		<title>Файл:60.png</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/%D0%A4%D0%B0%D0%B9%D0%BB:60.png"/>
				<updated>2013-03-05T12:11:05Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F</id>
		<title>OVM/OVM методология/Основы объектно-ориентированного программирования</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F"/>
				<updated>2013-03-05T12:10:45Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Разработка программного обеспечения, не связанная с физикой электричества и магнетизма, уже давно пытается создать многократно используемые, взаимозаменяемые, надежные компоненты. Важной моделью программирования, которая решает проблему, называется объектно-ориентированным программированием (ООП). Основная идея ООП заключается в том, что программы организуются как набор взаимодействующих объектов, каждый со своим собственным областью данных и функций. Объекты могут быть многократно использованы, потому что они инкапсулируют все, что им нужно для работы, могут быть построены с минимальным числом или без внешних зависимостей, и может быть высоко параметризованы. &lt;br /&gt;
В этой главе вводятся основные понятия объектно-ориентированного программирования, в том числе понятия инкапсуляции и интерфейса. Глава завершается обсуждением того, почему ООП является важным для построения testbenches.&lt;br /&gt;
&lt;br /&gt;
== Процедурные против ООП==&lt;br /&gt;
&lt;br /&gt;
Чтобы понять ООП и его роль в проверке, необходимо сначала понять традиционное процедурное программирование и его ограничения. Это даст основу для понимания того, как ООП может преодолеть эти ограничения. &lt;br /&gt;
В первые дни  языка  ассемблера, программисты и компьютерные архитекторы быстро обнаружили, что программы часто содержат последовательности инструкций, которые повторяются во всей программе. Повторение кода (в частности, с перфокарт) является трудоемким и порождает ошибки. Чтобы избежать трудоемкости и ошибок, вызванных повторением последовательностей,  была изобретена подпрограмма. Подпрограмма является единицей повторно используемого кода. Вместо кодирования той же последовательности встроенных инструкций, вы вызываете подпрограмму. Параметры, передаваемые в подпрограмму, позволяют динамически изменять код. То есть, каждый вызов подпрограммы с различными значениями параметров заставляет подпрограмму вести себя по-разному в зависимости от конкретных значений параметров.&lt;br /&gt;
&lt;br /&gt;
Каждый язык программирования любой значимости  имеет  конструкции для создания подпрограмм, процедур и функций, а также синтаксис для передачи в них параметров и возвращаемых значений. Эти функции могут использоваться для создания операций, которые часто используются. Тем не менее, некоторые операции очень распространены (таких как I / O, преобразования данных, численные методы и т. д.). И во избежание переписывания этих операций, программисты посчитали необходимым создать библиотеки часто используемых функций. В результате, большинство языков программирования включает такие библиотеки как часть пакета компилятора. Одним из наиболее известных примеров является библиотека C, которая поступает с каждым компилятором C. Она содержит полезные функции, такие как printf() (), COS (), atof (), и QSort (). Эти функции  практически каждый программист будет использовать в тот или иной момент.&lt;br /&gt;
Представьте себе, что писать собственные подпрограммы ввода / вывода или ваши собственные вычисления для преобразования чисел в строки и строк в числа. Был момент, когда программисты так и делали. Библиотеки универсальных функций все изменили и увеличили общую производительность программирования.&lt;br /&gt;
&lt;br /&gt;
Как показывает практика программного обеспечения и передовых технологий, программисты начали думать на более высоком уровне абстракции, чем инструкции и процедуры. Вместо написания отдельных инструкций, программисты пишут код на языках, которые обеспечивают более высокую модель абстракции компьютера, и компиляторы или интерпретаторы  переводят эти модели в конкретные инструкции. Библиотеки, такие как C библиотеки или STL в C + +, являются одной из форм абстракции. Они представляет собой набор функций, которые программисты могут использовать для создания все более сложных программы или абстракции.&lt;br /&gt;
В своей основополагающей книге Алгоритмы + Структуры данных = Программы, Никлаус Вирт объясняет, что для решения любой задачи программирования, вы должны разработать абстракцию реальности, которая имеет характеристики и свойства задачи, и игнорировать остальные детали. Он утверждает, что набор данных, которые вам нужны для решения проблемы, формирует абстракцию. Поэтому, прежде чем вы сможете решить проблема, в первую очередь необходимо определить, какие данные нужно иметь, чтобы создать решение.&lt;br /&gt;
Чтобы продолжить создание многократно используемой абстракции, мы должны создавать библиотеки данных объектов, которые могут быть многократно использованы для решения конкретных видов проблем. Поиск способа сделать это приводит к развитию объектно-ориентированных технологий.&lt;br /&gt;
&lt;br /&gt;
Объектно-ориентированный анализ и проектирование программ сосредоточен вокруг данных объектов, функциональности, связанной с каждым объектом, и отношения между объектами.&lt;br /&gt;
&lt;br /&gt;
Объектно-ориентированные языки предоставляют средства для отделения проблем программы, и  выделения каждой из них независимо друг от друга, и для инкапсуляции данных абстракций и представление их через строго определенные интерфейсы. Полностью объектно-ориентированная программа строится путем разделения функциональности программы в различные классы, определения интерфейса для каждого класса, а затем установления связей и взаимодействий между компонентами через их интерфейсы.&lt;br /&gt;
&lt;br /&gt;
==Классы и объекты==&lt;br /&gt;
&lt;br /&gt;
Основным модулем программирования на объектно-ориентированных языках, таких как SystemVerilog, является класс. Класс содержит элементы данных, называемые членами, и задач и функций, называемых методами. Для выполнения объектно-ориентированных программ, вы должны создать один или более классов в основной программе, а затем вызвать методы на различных объектах. Хотя термины класс и объект иногда используются как синонимы, как правило, термин класс относится к классу декларации объекта, а термин объект относится к экземпляру класса.&lt;br /&gt;
Чтобы проиллюстрировать эти понятия, ниже приведен пример простой класс register.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:27.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:28.png]]&lt;br /&gt;
&lt;br /&gt;
Это очень простой класс с одной переменной, и двумя методами, read() и write(). Чтобы использовать этот класс, вы создаете объекты путем создания экземпляра класса, а затем вызвать методы объекта, как показано ниже&lt;br /&gt;
&lt;br /&gt;
[[Файл:29.png]]&lt;br /&gt;
&lt;br /&gt;
Локальный  атрибут contents  класса указывает компилятору строго соблюдать границы класса. Если вы пытаетесь получить доступ к contents напрямую, компилятор выдаст ошибку. Вы можете получить доступ к содержимому через  доступные функции чтения и записи. Этот вид контроля доступа важен для  гарантирования  независимости от внутренностей класса и тем самым делает класс многократно используемым.&lt;br /&gt;
Вы можете использовать классы для создания новых типов данных, таких, как наш простой register. Использование классов для создания новых типов данных является важной частью ООП. Вы можете также использовать их для инкапсуляции математических вычислений или создания динамических структур данных, таких как стеки, списки, очереди, и так далее. Инкапсуляция организации структур данных или сведений о вычислениях в классе делает структуру данных или вычислений многократно используемыми.&lt;br /&gt;
В качестве более полного примера, давайте посмотрим на полезный тип данных, стек. Стек - это LIFO (&amp;quot;последним пришёл - первым обслужен&amp;quot;) структура. Элементы помещаются в стек командой push(), а  извлекаются из стека pop(). Pop () возвращает последний элемент, помещенный в стек,  и удаляет его из структуры данных. Внутренние элемент  stkptr  следит за вершиной стека. Элемент, на который он указывает, это вершина стека, а все, что ниже его (то есть, с меньшим индексом) ниже в стеке. Ниже приведена базовая реализация стека в SystemVerilog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:30.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:31.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:32.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:33.png]]&lt;br /&gt;
&lt;br /&gt;
Класс stack инкапсулирует все, что нужно знать о стеке данных структуры. Он содержит интерфейс и реализацию интерфейса. Интерфейс – это набор методов, которые используются для взаимодействия с классом. Реализация -  это код, который заставляет класс работать. Интерфейс нашего стека содержит следующие методы:&lt;br /&gt;
&lt;br /&gt;
[[Файл:34.png]]&lt;br /&gt;
&lt;br /&gt;
Не  существует  другого способа взаимодействия со стеком, как с помощью этих методов. Есть также два элемента данных  класса, stk и stkptr, которые представляют фактическую структуру стека. Тем не менее, эти два элемента локальные, что означает, что компилятор  запретит любые попытки доступа к ним  за пределами класса. Для  предотвращения доступа к внутренней структуре данных извне, мы можем сделать некоторые гарантии о состоянии данных.  Например, Push () и Pop () могут рассчитывать на то, что stkptr корректно и  указывает на вершину стека. Если бы можно было изменить значение stkptr иными средствами, чем с помощью интерфейса функций, тогда Push () и Pop () пришлось бы прибегнуть к дополнительным затратам и возможностям для ненадежной проверки истинности stkptr.&lt;br /&gt;
Реализация интерфейса встроенная. Объявление класса содержит не только определение интерфейса, а также реализацию каждой функции интерфейса. Оба C + + и SystemVerilog позволяют отделить реализацию от интерфейса. Разделение интерфейса и реализации является важной концепцией. Программисты, пишущие на C + +, могут использовать файлы заголовков для ввода интерфейс и. куб (или. срр или любой другой компилятор использует) для реализации.&lt;br /&gt;
&lt;br /&gt;
Есть некоторые важные  явления доступа через класс интерфейсов. Одним из них является возможность многократного использования. Мы можем удобно использовать классы, интерфейсы которых четко определены и хорошо объяснены, чем те, чьи интерфейсы нечеткие. Другим важным явлением обеспечения доступа через класс интерфейсов является надежность. Авторы класса может гарантировать определенную инвариантность (например, stkptr меньше, чем размер имеющегося массива STK) когда они знают, что пользователи не будут изменять данные иначе, чем предоставляемыми средствами. Кроме того, пользователи могут ожидать, что состояние объекта будет предсказуемым, когда они придерживаются интерфейса. Ясность другое явление. Интерфейс  может описать всю семантику класса. Объект не будет делать ничего другого, чем выполнять операции, доступные через интерфейс. Это делает проще понять для тех, кто использует класс, что именно он будет делать.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Отношения между объектами ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Истинная сила ООП становится очевидной, когда объекты взаимосвязаны различными отношениями. Есть много видов отношений, которые возможны. Мы рассмотрим два из наиболее фундаментальных отношений  HAS-A и IS-A.&lt;br /&gt;
&lt;br /&gt;
=== HAS-A ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
HAS-A относится к концепции, когда один объект содержится или принадлежит другим. В нашем классе стека, например,  HAS-A указатель стека (stkptr) и массив стека. Это примитивные типы данных, а не классы, но применяется тоже понятие HAS-A. В &lt;br /&gt;
SystemVerilog вы можете создать HAS-отношения между классами с  ссылками или указателями. На рисунке ниже показаны основные памяти  модель для HAS-А отношений. Объект А содержит ссылку или указатель на  Объект B.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:35.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Unified Modeling Language (UML) является графическим языком для  представления систем, в частности, отношения между объектами в этих системах. UML для HAS-связи выражается линией между объектами и закрашенным ромбиком(??????), как показано на рисунке ниже.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:36.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Объект А принадлежит экземпляру объекта B. кодирование HAS-А отношений в &lt;br /&gt;
SystemVerilog заключается в включении  одного класса в другой или других способов предоставления доступа одного класса  к одному классу, который хранится внутри.&lt;br /&gt;
&lt;br /&gt;
[[Файл:37.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Класс А содержит ссылку на класс B. конструктор для класса A, метод new(), вызывает new()  в классе B, чтобы создать его экземпляр.  Член b содержит ссылку на вновь созданный экземпляр B.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== IS-A ===&lt;br /&gt;
&lt;br /&gt;
IS-А отношения чаще всего называют наследованием. Новый класс является производным от ранее существующего класса и наследует его характеристики.  Объекты, созданные с наследованием наследования, созданы с использованием отношения IS-A. Производный класс  является подклассом или более специализированная версия родительского объекта.  Для иллюстрации понятия наследования, рисунок 2-3 использует часть систематики млекопитающих.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:38.png]]&lt;br /&gt;
&lt;br /&gt;
Животные, которые являются членами китообразных, хищных, приматов -  млекопитающие. Эти очень разные виды существ имеют общие черты  млекопитающих. Тем не менее, у китообразных (киты, дельфины), плотоядных (собак, медведей, енотов),  и приматов (обезьян, людей), у каждого есть свои четкие и безошибочные  характеристики.  Для использования ОО терминологии, медведь - хищник и плотоядный  IS- А млекопитающего. Иными словами, медведь состоит из атрибутов и  плотоядные млекопитающие и плюс, дополнительные атрибуты, отличающие его от  других плотоядных животных.&lt;br /&gt;
&lt;br /&gt;
Для представления   IS-A  на языке  UML, мы рисуем линию между объектами с открытыми  стрелками, указывающие на базовый класс. Традиционно, мы изображаем базовый класс  выше производных классов, а стрелки указывают вверх, образуя дерево наследования (или ориентированный ациклический граф, который может быть реализован в  языках, таких как C + +, которые поддерживают множественное наследование).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:39.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При представлении двух объектов в компьютерной программе с использованием наследования, новый производный объект содержит характеристики родителей и обычно включает в себя дополнительные характеристики. На рисунке ниже показана базовая модель памяти для IS-А композиции. В этом примере класс  B является производным от А.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:40.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
SystemVerilog использует ключевое слово  extends на определения наследования между классами:&lt;br /&gt;
&lt;br /&gt;
[[Файл:41.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Класс B получена из A, поэтому он содержит все атрибуты A. Любой экземпляр B не только содержит строку S, но и объект с плавающей запятой F и целое i.&lt;br /&gt;
&lt;br /&gt;
== Виртуальные функции и полиморфизм ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Одной из причин для создания объектов, используя наследование, является определение  различного поведения для той же операции. Другими словами, поведение, определенное в производном классе переопределяет поведение, определенное в базовом классе. Это можно сделать, используя виртуальные функции. Виртуальная функция – функция, которую можно переопределить в производном классе. Рассмотрим следующий универсальный пакет класса.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:42.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:43.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Она имеет три виртуальных функций, чтобы установить содержимое пакета. Различные виды пакетов, требуют различного содержания. Мы используем  generic_packet как базовый класс и получают различные виды пакетов из него.&lt;br /&gt;
&lt;br /&gt;
[[Файл:44.png]]&lt;br /&gt;
&lt;br /&gt;
Оба packet_A и packet_B могут иметь различные заголовки и трейлеры и  различные форматы полезной нагрузки. Информация  о том, как части пакета  отформатированы, хранится локально внутри производных классов пакета. Виртуальные  функции set_header (), set_trailer (), и set_body () реализованы по-разному в каждом подклассе основываясь на типе пакета. Базовый класс  generic_packet устанавливает организацию классов и типы операций, которые возможны, и производные классы могут изменить поведение  этих операций.&lt;br /&gt;
&lt;br /&gt;
Виртуальные функции используются для поддержки полиморфизма: несколько классов, которые могут быть использованы как взаимозаменяемые, каждый с разным поведением. Например, некоторая обработка пакетов не нуждается в информации о том, какой пакет обрабатывается. Единственной необходимой информацией является то, что объект действительно пакет, то есть, он является производным от базового класса. Еще один способ показать, что пакет является предком базового класса - это через  IS-A отношения.  Виртуальные функции являются механизмом, с помощью которого мы можем переопределять&lt;br /&gt;
поведение для различных вариантов пакетов.&lt;br /&gt;
&lt;br /&gt;
Чтобы посмотреть немного глубже, как работают виртуальные функции, давайте рассмотрим три  класса, связанных друг с другом IS-А отношением.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:45.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
figure является базовым классом; polygon является производным от figure; square происходит от polygon. Каждый класс имеет две функции, draw(), которая является виртуальной, и compute_area (), которая не является виртуальной. Следующий пример показывает,  SystemVerilog код:&lt;br /&gt;
&lt;br /&gt;
[[Файл:46.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:47.png]]&lt;br /&gt;
&lt;br /&gt;
Каждая функция выводит его полное имя в виде  class_name :: имя_функции. Мы можем написать простую программу, которая показывает, как виртуальные функции связаны.&lt;br /&gt;
&lt;br /&gt;
[[Файл:48.png]]&lt;br /&gt;
&lt;br /&gt;
Ниже показано, что происходит, когда мы запускаем эту программу:&lt;br /&gt;
&lt;br /&gt;
[[Файл:49.png]]&lt;br /&gt;
&lt;br /&gt;
Сначала мы создаем s, квадрат, а затем назначаем его на f и р. базовым классом для square является polygon и базовым классом polygon - figure. Из напечатанного вывода, мы можем заключить, что функции связаны в соответствии со  следующей таблице:&lt;br /&gt;
&lt;br /&gt;
[[Файл:50.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Во всех случаях, compute_area () была связана с конкретным compute_area () функцией с типом ссылки, которая ее определяет -р ссылки  на polygon, таким образом  связаны  polygon :: compute_area (). Так как  compute_area () не является виртуальной функцией. Компилятор может легко определить, какие  версии функций  вызывать, основываясь на типе объекта.&lt;br /&gt;
&lt;br /&gt;
 Так как draw() является виртуальной, не всегда возможно для компилятора  определить, какую функцию вызывать. Решение принимается на этапе выполнения с помощью &lt;br /&gt;
виртуальной таблицы, таблицы привязки функций. Виртуальная таблица используется для привязки  функций, привязка не может быть полностью определена во время компиляции. &lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что хотя р представляет собой polygon, вызов p.draw () приводит к  square::draw() вызывается не polygon::draw(), как вы могли бы ожидать. &lt;br /&gt;
То же самое происходит и с F-f.draw () связан с кsquare::draw(). &lt;br /&gt;
объект, который мы первоначально создан представляет собой square,  он поддерживает различные типы операций но, то, что  это square не забывается. Это  работает только потому, что square является производным от polygon, который в свою очередь является производным figure, и потому draw() объявлен как виртуальный. Ошибка компиляции О несовместимости типа возникает при попытке присвоить ей к s&lt;br /&gt;
 и p   так как с не получено от р.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Универсальное программирование==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Напомним, что объектно-ориентированные языки обладают средствами для разделения программ на задачи и рассмотрения их отдельно. Импликация отдельных задач заключается в том, что каждая задача представлена  один раз. Дублирование кода нарушает принцип. На практике, многие задачи очень схожи, и имеют похожее решение, но не идентичное. Интуитивно, мы хотим написать код, который может быть использован во многих ситуациях. Это интуиция подводит нас к написанию универсального кода, кода, который высоко параметризованный, так что он может быть легко использован в самых разнообразных ситуациях.&lt;br /&gt;
&lt;br /&gt;
Подробная информация об универсальном коде поставляется во время компиляции или во время выполнения вместо жесткого кодирования. Любой код, который имеет параметры, такие как вызов функций, может, считается универсальным, но этот термин обычно используется для кода с использованием  шаблонов (в C + +) или параметризованные классы (в SystemVerilog). Написание универсальных  программ согласуется с целью  ООП  о разделении задач. Таким образом, ООП языки обеспечивают условия для создания универсального кода.&lt;br /&gt;
&lt;br /&gt;
Параметризованный класс – это класс, который (очевидно) имеет параметры.  В синтаксисе  SystemVerilog для определения параметров используется знак # в заголовке класса, после следует  список параметров в скобках. В качестве примера, рассмотрим следующий параметризованный класс:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class param #(type T=int, int R=16);&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот класс имеет два параметра, T, который является фиктивным параметром и R, который является целочисленным параметром. Экземпляры параметризованных классов с определенными значениями параметров создают специализации, то есть версии кода с примененными параметрами.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;param #(real, 29) z;&lt;br /&gt;
param #(int unsigned, 12) q;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Приведенные выше объявления создают специализации параметризованного класса&lt;br /&gt;
param. Имя класса и параметры определяют специализацию. Таким образом,&lt;br /&gt;
специализации, по сути, уникальные типы. Компилятор не позволит вам присвоить  q к z, или наоборот, потому что они являются объектами различных типов.&lt;br /&gt;
&lt;br /&gt;
Параметр type позволяет писать типа-независимый код, код, данные и алгоритмы которого могут работать с широким спектром типов данных. Например:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class maximizer #(type T=int);&lt;br /&gt;
function T max(T a, T b);&lt;br /&gt;
if( a &amp;gt; b )&lt;br /&gt;
return a;&lt;br /&gt;
else&lt;br /&gt;
return b;&lt;br /&gt;
endfunction&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Параметризованный класс maximizer имеет функцию max(), которая возвращает&lt;br /&gt;
максимальное из двух значений. Алгоритм max() такой же, независимо от типа сравнения объектов. В этом случае, единственным ограничением является то, что объекты должны быть сравнимы оператором больше чем (&amp;gt;).&lt;br /&gt;
Классы не могут сравниваться с использованием оператора большего чем, поэтому другая версия  maximizer должна работать с классами. Для создания версии maximizer, которая будет возвращать больший из двух  классов, мы должны определить метод в каждом классе, который будет сравнивать объекты.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;class maximizer #(type T=int);&lt;br /&gt;
function T max( T a, T b);&lt;br /&gt;
if( a.comp(b) &amp;gt; 0 )&lt;br /&gt;
return a;&lt;br /&gt;
else&lt;br /&gt;
return b;&lt;br /&gt;
endfunction&lt;br /&gt;
endclass&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это предполагает, что параметр T типа type действительно класс, а не встроенного тип, такие как int или real. Кроме того, он предполагает, что T имеет функцию под названием Comp (), которая используется для сравнения себя с другим экземпляром. Библиотека OVM содержит параметризованный компонент, называемый ovm_in_order_comparator # (T), который используется для сравнения потоков операций. Она имеет два варианта, один для сравнения потоков встроенных типов, и один для сравнения потоков классов. Причина необходимости двух компараторов в том, что SystemVerilog не поддерживает операторы, которые могут работать и с классами  и  со встроенными типами.&lt;br /&gt;
&lt;br /&gt;
=== Универсальный стек ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Наш стек не особенно универсальный. Она имеет фиксированный размер  20, а&lt;br /&gt;
тип данных элементов хранящихся в стеке  Int. Ниже приводится более общий вид стека, который изменяет эти фиксированные характеристики параметризованными.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;53 class stack #(type T = int);&lt;br /&gt;
54&lt;br /&gt;
55 local T stk[];&lt;br /&gt;
56 local int stkptr;&lt;br /&gt;
57 local int size;&lt;br /&gt;
58 local int tp;&lt;br /&gt;
59&lt;br /&gt;
60 function new(int s = 20);&lt;br /&gt;
61 size = s;&lt;br /&gt;
62 stk = new [size];&lt;br /&gt;
63 clear();&lt;br /&gt;
64 endfunction&lt;br /&gt;
65&lt;br /&gt;
66 function bit pop(output T data);&lt;br /&gt;
67&lt;br /&gt;
68 if(is_empty())&lt;br /&gt;
69 return 0;&lt;br /&gt;
70&lt;br /&gt;
71 data = stk[stkptr];&lt;br /&gt;
72 stkptr = stkptr - 1;&lt;br /&gt;
73 return 1;&lt;br /&gt;
74&lt;br /&gt;
75 endfunction&lt;br /&gt;
76&lt;br /&gt;
77 function bit push(T data);&lt;br /&gt;
78&lt;br /&gt;
79 if(is_full())&lt;br /&gt;
80 return 0;&lt;br /&gt;
81&lt;br /&gt;
82 stkptr = stkptr + 1;&lt;br /&gt;
83 stk[stkptr] = data;&lt;br /&gt;
84 return 1;&lt;br /&gt;
85&lt;br /&gt;
86 endfunction&lt;br /&gt;
87&lt;br /&gt;
88 function bit is_full();&lt;br /&gt;
89 return stkptr &amp;gt;= (size - 1);&lt;br /&gt;
90 endfunction&lt;br /&gt;
91&lt;br /&gt;
92 function bit is_empty();&lt;br /&gt;
93 return stkptr &amp;lt; 0;&lt;br /&gt;
94 endfunction&lt;br /&gt;
95&lt;br /&gt;
96 function void clear();&lt;br /&gt;
97 stkptr = -1;&lt;br /&gt;
98 tp = stkptr;&lt;br /&gt;
99 endfunction&lt;br /&gt;
100&lt;br /&gt;
101 function void traverse_init();&lt;br /&gt;
102 tp = stkptr;&lt;br /&gt;
103 endfunction&lt;br /&gt;
104&lt;br /&gt;
105 function int traverse_next(output T t);&lt;br /&gt;
106 if(tp &amp;lt; 0)&lt;br /&gt;
107 return 0; // failure&lt;br /&gt;
108&lt;br /&gt;
109 t = stk[tp];&lt;br /&gt;
110 tp = tp - 1;&lt;br /&gt;
111 return 1;&lt;br /&gt;
112&lt;br /&gt;
113 endfunction&lt;br /&gt;
114&lt;br /&gt;
115 virtual function void print(input T t);&lt;br /&gt;
116 $display(“print is unimplemented”);&lt;br /&gt;
117 endfunction&lt;br /&gt;
118&lt;br /&gt;
119 function void dump();&lt;br /&gt;
120&lt;br /&gt;
121 T t;&lt;br /&gt;
122&lt;br /&gt;
123 $write(“stack:”);&lt;br /&gt;
124 if(is_empty()) begin&lt;br /&gt;
125 $display(“&amp;lt;empty&amp;gt;”);&lt;br /&gt;
126 return;&lt;br /&gt;
127 end&lt;br /&gt;
128&lt;br /&gt;
129 traverse_init();&lt;br /&gt;
130&lt;br /&gt;
131 while(traverse_next(t)) begin&lt;br /&gt;
132 print(t);&lt;br /&gt;
133 end&lt;br /&gt;
134 $display();&lt;br /&gt;
135&lt;br /&gt;
136 endfunction&lt;br /&gt;
137&lt;br /&gt;
138 endclass&lt;br /&gt;
file: 02_intro_to_OOP/02_generic_stack/stack.sv&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Универсальный класс stack параметризован  типом объектов стека.&lt;br /&gt;
Параметр T содержит тип. В этом случае T может быть либо классом или встроенным&lt;br /&gt;
типом. В  любом месте  класса, где мы ранее использовали Int как тип стека, теперь мы используем Т. Например, push() теперь принимает аргумент типа T. Параметры  класса,&lt;br /&gt;
такие как T,  устанавливаются во время компиляции. Для специализации stack#(T), мы инициализируем его с определенным значением для данного типа. Например:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;stack #(real) real_stack;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Этот оператор создает специализацию stack, который использует real, как тип объектов стека.&lt;br /&gt;
Размер стека больше не зафиксирована на уровне 20. Мы используем динамический массив для хранения стека, размер которого указан в качестве параметра конструктора. В отличие от T, аргумент size задается при выполнении  программы. Это позволяет нам создать несколько стеков, каждый с разным размером.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;stack #(real) big_stack;&lt;br /&gt;
stack #(real) little_stack;&lt;br /&gt;
...&lt;br /&gt;
big_stack = new(2048);&lt;br /&gt;
little_stack = new(6);&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
big_stack и little_stack одного типа. Они используют одну и ту же специализацию stack#(T). Тем не менее, каждый из них создается с различными параметрами размера. &lt;br /&gt;
&lt;br /&gt;
В процессе создания универсального стека, мы сделали еще одно изменение. Мы заменили dump()  на  traverse_init () и traverse_next (). dump() зависит от типа элементов стека, которые не известны до компиляции. Мы должны иметь возможность пройти по стеку и отформатировать каждый элемент, не зависимо от типа элемента. Это может быть INT, или это может быть сложный класс с несколькими членами. Чтобы сохранить stack#(T) универсальным, мы должны исключить  любую зависимость от типа элементов стека.&lt;br /&gt;
&lt;br /&gt;
В то время как dump() пробежит по всем элементам стека и напечатает их в определенном порядке, traverse_init () устанавливает внутренний указатель(tp) на вершину стека, а traverse_next () возвращает текущий элемент и уменьшает tp. Стек поддерживает некоторую статическую информацию  обхода. Статическая   информация сбрасывается, когда вызывается traverse_init ().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Классы  и Модули==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Интересно, что HDLs, такие как Verilog и VHDL, хотя они не считаются объектно-ориентированными языками, строятся на концепции очень схожей  классам и объектам. Модули  в Verilog, например, являются объектами, каждый из которых имеет&lt;br /&gt;
свое собственное пространство данных и набор задач и функций. Так же, как объекты в ОО программах, каждый экземпляр модуля является независимой копией. Все экземпляры имеют один и тот же набор задач и функций и те же интерфейсы, но данные содержащихся внутри каждого является независимым от всех остальных экземпляров. Модули контролируются их интерфейсами. Verilog модули не поддерживают наследование или  параметризации типов, и они являются статическими, что делает их непригодными для истинного ООП.&lt;br /&gt;
&lt;br /&gt;
Сходство между классами и модулями открывает возможность для нас использовать объекты класса в аппаратном контексте. Мы можем создать компоненты верификации как экземпляры классов, что дает нам гибкость при подключении к аппаратным элементам. Дизайнеры SystemVerilog выигрывают  при расширении Verilog  классами, предоставляя возможность для классов работать как модули.&lt;br /&gt;
&lt;br /&gt;
В приведенной ниже таблице сравниваются особенности классов в Verilog, SystemVerilog, и C + +.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F</id>
		<title>OVM/OVM методология/Основы объектно-ориентированного программирования</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F"/>
				<updated>2013-03-04T13:33:45Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Разработка программного обеспечения, не связанная с физикой электричества и магнетизма, уже давно пытается создать многократно используемые, взаимозаменяемые, надежные компоненты. Важной моделью программирования, которая решает проблему, называется объектно-ориентированным программированием (ООП). Основная идея ООП заключается в том, что программы организуются как набор взаимодействующих объектов, каждый со своим собственным областью данных и функций. Объекты могут быть многократно использованы, потому что они инкапсулируют все, что им нужно для работы, могут быть построены с минимальным числом или без внешних зависимостей, и может быть высоко параметризованы. &lt;br /&gt;
В этой главе вводятся основные понятия объектно-ориентированного программирования, в том числе понятия инкапсуляции и интерфейса. Глава завершается обсуждением того, почему ООП является важным для построения testbenches.&lt;br /&gt;
&lt;br /&gt;
== Процедурные против ООП==&lt;br /&gt;
&lt;br /&gt;
Чтобы понять ООП и его роль в проверке, необходимо сначала понять традиционное процедурное программирование и его ограничения. Это даст основу для понимания того, как ООП может преодолеть эти ограничения. &lt;br /&gt;
В первые дни  языка  ассемблера, программисты и компьютерные архитекторы быстро обнаружили, что программы часто содержат последовательности инструкций, которые повторяются во всей программе. Повторение кода (в частности, с перфокарт) является трудоемким и порождает ошибки. Чтобы избежать трудоемкости и ошибок, вызванных повторением последовательностей,  была изобретена подпрограмма. Подпрограмма является единицей повторно используемого кода. Вместо кодирования той же последовательности встроенных инструкций, вы вызываете подпрограмму. Параметры, передаваемые в подпрограмму, позволяют динамически изменять код. То есть, каждый вызов подпрограммы с различными значениями параметров заставляет подпрограмму вести себя по-разному в зависимости от конкретных значений параметров.&lt;br /&gt;
&lt;br /&gt;
Каждый язык программирования любой значимости  имеет  конструкции для создания подпрограмм, процедур и функций, а также синтаксис для передачи в них параметров и возвращаемых значений. Эти функции могут использоваться для создания операций, которые часто используются. Тем не менее, некоторые операции очень распространены (таких как I / O, преобразования данных, численные методы и т. д.). И во избежание переписывания этих операций, программисты посчитали необходимым создать библиотеки часто используемых функций. В результате, большинство языков программирования включает такие библиотеки как часть пакета компилятора. Одним из наиболее известных примеров является библиотека C, которая поступает с каждым компилятором C. Она содержит полезные функции, такие как printf() (), COS (), atof (), и QSort (). Эти функции  практически каждый программист будет использовать в тот или иной момент.&lt;br /&gt;
Представьте себе, что писать собственные подпрограммы ввода / вывода или ваши собственные вычисления для преобразования чисел в строки и строк в числа. Был момент, когда программисты так и делали. Библиотеки универсальных функций все изменили и увеличили общую производительность программирования.&lt;br /&gt;
&lt;br /&gt;
Как показывает практика программного обеспечения и передовых технологий, программисты начали думать на более высоком уровне абстракции, чем инструкции и процедуры. Вместо написания отдельных инструкций, программисты пишут код на языках, которые обеспечивают более высокую модель абстракции компьютера, и компиляторы или интерпретаторы  переводят эти модели в конкретные инструкции. Библиотеки, такие как C библиотеки или STL в C + +, являются одной из форм абстракции. Они представляет собой набор функций, которые программисты могут использовать для создания все более сложных программы или абстракции.&lt;br /&gt;
В своей основополагающей книге Алгоритмы + Структуры данных = Программы, Никлаус Вирт объясняет, что для решения любой задачи программирования, вы должны разработать абстракцию реальности, которая имеет характеристики и свойства задачи, и игнорировать остальные детали. Он утверждает, что набор данных, которые вам нужны для решения проблемы, формирует абстракцию. Поэтому, прежде чем вы сможете решить проблема, в первую очередь необходимо определить, какие данные нужно иметь, чтобы создать решение.&lt;br /&gt;
Чтобы продолжить создание многократно используемой абстракции, мы должны создавать библиотеки данных объектов, которые могут быть многократно использованы для решения конкретных видов проблем. Поиск способа сделать это приводит к развитию объектно-ориентированных технологий.&lt;br /&gt;
&lt;br /&gt;
Объектно-ориентированный анализ и проектирование программ сосредоточен вокруг данных объектов, функциональности, связанной с каждым объектом, и отношения между объектами.&lt;br /&gt;
&lt;br /&gt;
Объектно-ориентированные языки предоставляют средства для отделения проблем программы, и  выделения каждой из них независимо друг от друга, и для инкапсуляции данных абстракций и представление их через строго определенные интерфейсы. Полностью объектно-ориентированная программа строится путем разделения функциональности программы в различные классы, определения интерфейса для каждого класса, а затем установления связей и взаимодействий между компонентами через их интерфейсы.&lt;br /&gt;
&lt;br /&gt;
==Классы и объекты==&lt;br /&gt;
&lt;br /&gt;
Основным модулем программирования на объектно-ориентированных языках, таких как SystemVerilog, является класс. Класс содержит элементы данных, называемые членами, и задач и функций, называемых методами. Для выполнения объектно-ориентированных программ, вы должны создать один или более классов в основной программе, а затем вызвать методы на различных объектах. Хотя термины класс и объект иногда используются как синонимы, как правило, термин класс относится к классу декларации объекта, а термин объект относится к экземпляру класса.&lt;br /&gt;
Чтобы проиллюстрировать эти понятия, ниже приведен пример простой класс register.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:27.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:28.png]]&lt;br /&gt;
&lt;br /&gt;
Это очень простой класс с одной переменной, и двумя методами, read() и write(). Чтобы использовать этот класс, вы создаете объекты путем создания экземпляра класса, а затем вызвать методы объекта, как показано ниже&lt;br /&gt;
&lt;br /&gt;
[[Файл:29.png]]&lt;br /&gt;
&lt;br /&gt;
Локальный  атрибут contents  класса указывает компилятору строго соблюдать границы класса. Если вы пытаетесь получить доступ к contents напрямую, компилятор выдаст ошибку. Вы можете получить доступ к содержимому через  доступные функции чтения и записи. Этот вид контроля доступа важен для  гарантирования  независимости от внутренностей класса и тем самым делает класс многократно используемым.&lt;br /&gt;
Вы можете использовать классы для создания новых типов данных, таких, как наш простой register. Использование классов для создания новых типов данных является важной частью ООП. Вы можете также использовать их для инкапсуляции математических вычислений или создания динамических структур данных, таких как стеки, списки, очереди, и так далее. Инкапсуляция организации структур данных или сведений о вычислениях в классе делает структуру данных или вычислений многократно используемыми.&lt;br /&gt;
В качестве более полного примера, давайте посмотрим на полезный тип данных, стек. Стек - это LIFO (&amp;quot;последним пришёл - первым обслужен&amp;quot;) структура. Элементы помещаются в стек командой push(), а  извлекаются из стека pop(). Pop () возвращает последний элемент, помещенный в стек,  и удаляет его из структуры данных. Внутренние элемент  stkptr  следит за вершиной стека. Элемент, на который он указывает, это вершина стека, а все, что ниже его (то есть, с меньшим индексом) ниже в стеке. Ниже приведена базовая реализация стека в SystemVerilog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:30.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:31.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:32.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:33.png]]&lt;br /&gt;
&lt;br /&gt;
Класс stack инкапсулирует все, что нужно знать о стеке данных структуры. Он содержит интерфейс и реализацию интерфейса. Интерфейс – это набор методов, которые используются для взаимодействия с классом. Реализация -  это код, который заставляет класс работать. Интерфейс нашего стека содержит следующие методы:&lt;br /&gt;
&lt;br /&gt;
[[Файл:34.png]]&lt;br /&gt;
&lt;br /&gt;
Не  существует  другого способа взаимодействия со стеком, как с помощью этих методов. Есть также два элемента данных  класса, stk и stkptr, которые представляют фактическую структуру стека. Тем не менее, эти два элемента локальные, что означает, что компилятор  запретит любые попытки доступа к ним  за пределами класса. Для  предотвращения доступа к внутренней структуре данных извне, мы можем сделать некоторые гарантии о состоянии данных.  Например, Push () и Pop () могут рассчитывать на то, что stkptr корректно и  указывает на вершину стека. Если бы можно было изменить значение stkptr иными средствами, чем с помощью интерфейса функций, тогда Push () и Pop () пришлось бы прибегнуть к дополнительным затратам и возможностям для ненадежной проверки истинности stkptr.&lt;br /&gt;
Реализация интерфейса встроенная. Объявление класса содержит не только определение интерфейса, а также реализацию каждой функции интерфейса. Оба C + + и SystemVerilog позволяют отделить реализацию от интерфейса. Разделение интерфейса и реализации является важной концепцией. Программисты, пишущие на C + +, могут использовать файлы заголовков для ввода интерфейс и. куб (или. срр или любой другой компилятор использует) для реализации.&lt;br /&gt;
&lt;br /&gt;
Есть некоторые важные  явления доступа через класс интерфейсов. Одним из них является возможность многократного использования. Мы можем удобно использовать классы, интерфейсы которых четко определены и хорошо объяснены, чем те, чьи интерфейсы нечеткие. Другим важным явлением обеспечения доступа через класс интерфейсов является надежность. Авторы класса может гарантировать определенную инвариантность (например, stkptr меньше, чем размер имеющегося массива STK) когда они знают, что пользователи не будут изменять данные иначе, чем предоставляемыми средствами. Кроме того, пользователи могут ожидать, что состояние объекта будет предсказуемым, когда они придерживаются интерфейса. Ясность другое явление. Интерфейс  может описать всю семантику класса. Объект не будет делать ничего другого, чем выполнять операции, доступные через интерфейс. Это делает проще понять для тех, кто использует класс, что именно он будет делать.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Отношения между объектами ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Истинная сила ООП становится очевидной, когда объекты взаимосвязаны различными отношениями. Есть много видов отношений, которые возможны. Мы рассмотрим два из наиболее фундаментальных отношений  HAS-A и IS-A.&lt;br /&gt;
&lt;br /&gt;
=== HAS-A ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
HAS-A относится к концепции, когда один объект содержится или принадлежит другим. В нашем классе стека, например,  HAS-A указатель стека (stkptr) и массив стека. Это примитивные типы данных, а не классы, но применяется тоже понятие HAS-A. В &lt;br /&gt;
SystemVerilog вы можете создать HAS-отношения между классами с  ссылками или указателями. На рисунке ниже показаны основные памяти  модель для HAS-А отношений. Объект А содержит ссылку или указатель на  Объект B.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:35.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Unified Modeling Language (UML) является графическим языком для  представления систем, в частности, отношения между объектами в этих системах. UML для HAS-связи выражается линией между объектами и закрашенным ромбиком(??????), как показано на рисунке ниже.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:36.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Объект А принадлежит экземпляру объекта B. кодирование HAS-А отношений в &lt;br /&gt;
SystemVerilog заключается в включении  одного класса в другой или других способов предоставления доступа одного класса  к одному классу, который хранится внутри.&lt;br /&gt;
&lt;br /&gt;
[[Файл:37.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Класс А содержит ссылку на класс B. конструктор для класса A, метод new(), вызывает new()  в классе B, чтобы создать его экземпляр.  Член b содержит ссылку на вновь созданный экземпляр B.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== IS-A ===&lt;br /&gt;
&lt;br /&gt;
IS-А отношения чаще всего называют наследованием. Новый класс является производным от ранее существующего класса и наследует его характеристики.  Объекты, созданные с наследованием наследования, созданы с использованием отношения IS-A. Производный класс  является подклассом или более специализированная версия родительского объекта.  Для иллюстрации понятия наследования, рисунок 2-3 использует часть систематики млекопитающих.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:38.png]]&lt;br /&gt;
&lt;br /&gt;
Животные, которые являются членами китообразных, хищных, приматов -  млекопитающие. Эти очень разные виды существ имеют общие черты  млекопитающих. Тем не менее, у китообразных (киты, дельфины), плотоядных (собак, медведей, енотов),  и приматов (обезьян, людей), у каждого есть свои четкие и безошибочные  характеристики.  Для использования ОО терминологии, медведь - хищник и плотоядный  IS- А млекопитающего. Иными словами, медведь состоит из атрибутов и  плотоядные млекопитающие и плюс, дополнительные атрибуты, отличающие его от  других плотоядных животных.&lt;br /&gt;
&lt;br /&gt;
Для представления   IS-A  на языке  UML, мы рисуем линию между объектами с открытыми  стрелками, указывающие на базовый класс. Традиционно, мы изображаем базовый класс  выше производных классов, а стрелки указывают вверх, образуя дерево наследования (или ориентированный ациклический граф, который может быть реализован в  языках, таких как C + +, которые поддерживают множественное наследование).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:39.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При представлении двух объектов в компьютерной программе с использованием наследования, новый производный объект содержит характеристики родителей и обычно включает в себя дополнительные характеристики. На рисунке ниже показана базовая модель памяти для IS-А композиции. В этом примере класс  B является производным от А.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:40.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
SystemVerilog использует ключевое слово  extends на определения наследования между классами:&lt;br /&gt;
&lt;br /&gt;
[[Файл:41.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Класс B получена из A, поэтому он содержит все атрибуты A. Любой экземпляр B не только содержит строку S, но и объект с плавающей запятой F и целое i.&lt;br /&gt;
&lt;br /&gt;
== Виртуальные функции и полиморфизм ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Одной из причин для создания объектов, используя наследование, является определение  различного поведения для той же операции. Другими словами, поведение, определенное в производном классе переопределяет поведение, определенное в базовом классе. Это можно сделать, используя виртуальные функции. Виртуальная функция – функция, которую можно переопределить в производном классе. Рассмотрим следующий универсальный пакет класса.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:42.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:43.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Она имеет три виртуальных функций, чтобы установить содержимое пакета. Различные виды пакетов, требуют различного содержания. Мы используем  generic_packet как базовый класс и получают различные виды пакетов из него.&lt;br /&gt;
&lt;br /&gt;
[[Файл:44.png]]&lt;br /&gt;
&lt;br /&gt;
Оба packet_A и packet_B могут иметь различные заголовки и трейлеры и  различные форматы полезной нагрузки. Информация  о том, как части пакета  отформатированы, хранится локально внутри производных классов пакета. Виртуальные  функции set_header (), set_trailer (), и set_body () реализованы по-разному в каждом подклассе основываясь на типе пакета. Базовый класс  generic_packet устанавливает организацию классов и типы операций, которые возможны, и производные классы могут изменить поведение  этих операций.&lt;br /&gt;
&lt;br /&gt;
Виртуальные функции используются для поддержки полиморфизма: несколько классов, которые могут быть использованы как взаимозаменяемые, каждый с разным поведением. Например, некоторая обработка пакетов не нуждается в информации о том, какой пакет обрабатывается. Единственной необходимой информацией является то, что объект действительно пакет, то есть, он является производным от базового класса. Еще один способ показать, что пакет является предком базового класса - это через  IS-A отношения.  Виртуальные функции являются механизмом, с помощью которого мы можем переопределять&lt;br /&gt;
поведение для различных вариантов пакетов.&lt;br /&gt;
&lt;br /&gt;
Чтобы посмотреть немного глубже, как работают виртуальные функции, давайте рассмотрим три  класса, связанных друг с другом IS-А отношением.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:45.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
figure является базовым классом; polygon является производным от figure; square происходит от polygon. Каждый класс имеет две функции, draw(), которая является виртуальной, и compute_area (), которая не является виртуальной. Следующий пример показывает,  SystemVerilog код:&lt;br /&gt;
&lt;br /&gt;
[[Файл:46.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:47.png]]&lt;br /&gt;
&lt;br /&gt;
Каждая функция выводит его полное имя в виде  class_name :: имя_функции. Мы можем написать простую программу, которая показывает, как виртуальные функции связаны.&lt;br /&gt;
&lt;br /&gt;
[[Файл:48.png]]&lt;br /&gt;
&lt;br /&gt;
Ниже показано, что происходит, когда мы запускаем эту программу:&lt;br /&gt;
&lt;br /&gt;
[[Файл:49.png]]&lt;br /&gt;
&lt;br /&gt;
Сначала мы создаем s, квадрат, а затем назначаем его на f и р. базовым классом для square является polygon и базовым классом polygon - figure. Из напечатанного вывода, мы можем заключить, что функции связаны в соответствии со  следующей таблице:&lt;br /&gt;
&lt;br /&gt;
[[Файл:50.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Во всех случаях, compute_area () была связана с конкретным compute_area () функцией с типом ссылки, которая ее определяет -р ссылки  на polygon, таким образом  связаны  polygon :: compute_area (). Так как  compute_area () не является виртуальной функцией. Компилятор может легко определить, какие  версии функций  вызывать, основываясь на типе объекта.&lt;br /&gt;
&lt;br /&gt;
 Так как draw() является виртуальной, не всегда возможно для компилятора  определить, какую функцию вызывать. Решение принимается на этапе выполнения с помощью &lt;br /&gt;
виртуальной таблицы, таблицы привязки функций. Виртуальная таблица используется для привязки  функций, привязка не может быть полностью определена во время компиляции. &lt;br /&gt;
&lt;br /&gt;
Обратите внимание, что хотя р представляет собой polygon, вызов p.draw () приводит к  square::draw() вызывается не polygon::draw(), как вы могли бы ожидать. &lt;br /&gt;
То же самое происходит и с F-f.draw () связан с кsquare::draw(). &lt;br /&gt;
объект, который мы первоначально создан представляет собой square,  он поддерживает различные типы операций но, то, что  это square не забывается. Это  работает только потому, что square является производным от polygon, который в свою очередь является производным figure, и потому draw() объявлен как виртуальный. Ошибка компиляции О несовместимости типа возникает при попытке присвоить ей к s&lt;br /&gt;
 и p   так как с не получено от р.&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	<entry>
		<id>http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F</id>
		<title>OVM/OVM методология/Основы объектно-ориентированного программирования</title>
		<link rel="alternate" type="text/html" href="http://www.simhard.com/wiki/index.php/OVM/OVM_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F"/>
				<updated>2013-03-04T13:13:17Z</updated>
		
		<summary type="html">&lt;p&gt;Anastasiya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{OVM TOC}}&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
= =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Разработка программного обеспечения, не связанная с физикой электричества и магнетизма, уже давно пытается создать многократно используемые, взаимозаменяемые, надежные компоненты. Важной моделью программирования, которая решает проблему, называется объектно-ориентированным программированием (ООП). Основная идея ООП заключается в том, что программы организуются как набор взаимодействующих объектов, каждый со своим собственным областью данных и функций. Объекты могут быть многократно использованы, потому что они инкапсулируют все, что им нужно для работы, могут быть построены с минимальным числом или без внешних зависимостей, и может быть высоко параметризованы. &lt;br /&gt;
В этой главе вводятся основные понятия объектно-ориентированного программирования, в том числе понятия инкапсуляции и интерфейса. Глава завершается обсуждением того, почему ООП является важным для построения testbenches.&lt;br /&gt;
&lt;br /&gt;
== Процедурные против ООП==&lt;br /&gt;
&lt;br /&gt;
Чтобы понять ООП и его роль в проверке, необходимо сначала понять традиционное процедурное программирование и его ограничения. Это даст основу для понимания того, как ООП может преодолеть эти ограничения. &lt;br /&gt;
В первые дни  языка  ассемблера, программисты и компьютерные архитекторы быстро обнаружили, что программы часто содержат последовательности инструкций, которые повторяются во всей программе. Повторение кода (в частности, с перфокарт) является трудоемким и порождает ошибки. Чтобы избежать трудоемкости и ошибок, вызванных повторением последовательностей,  была изобретена подпрограмма. Подпрограмма является единицей повторно используемого кода. Вместо кодирования той же последовательности встроенных инструкций, вы вызываете подпрограмму. Параметры, передаваемые в подпрограмму, позволяют динамически изменять код. То есть, каждый вызов подпрограммы с различными значениями параметров заставляет подпрограмму вести себя по-разному в зависимости от конкретных значений параметров.&lt;br /&gt;
&lt;br /&gt;
Каждый язык программирования любой значимости  имеет  конструкции для создания подпрограмм, процедур и функций, а также синтаксис для передачи в них параметров и возвращаемых значений. Эти функции могут использоваться для создания операций, которые часто используются. Тем не менее, некоторые операции очень распространены (таких как I / O, преобразования данных, численные методы и т. д.). И во избежание переписывания этих операций, программисты посчитали необходимым создать библиотеки часто используемых функций. В результате, большинство языков программирования включает такие библиотеки как часть пакета компилятора. Одним из наиболее известных примеров является библиотека C, которая поступает с каждым компилятором C. Она содержит полезные функции, такие как printf() (), COS (), atof (), и QSort (). Эти функции  практически каждый программист будет использовать в тот или иной момент.&lt;br /&gt;
Представьте себе, что писать собственные подпрограммы ввода / вывода или ваши собственные вычисления для преобразования чисел в строки и строк в числа. Был момент, когда программисты так и делали. Библиотеки универсальных функций все изменили и увеличили общую производительность программирования.&lt;br /&gt;
&lt;br /&gt;
Как показывает практика программного обеспечения и передовых технологий, программисты начали думать на более высоком уровне абстракции, чем инструкции и процедуры. Вместо написания отдельных инструкций, программисты пишут код на языках, которые обеспечивают более высокую модель абстракции компьютера, и компиляторы или интерпретаторы  переводят эти модели в конкретные инструкции. Библиотеки, такие как C библиотеки или STL в C + +, являются одной из форм абстракции. Они представляет собой набор функций, которые программисты могут использовать для создания все более сложных программы или абстракции.&lt;br /&gt;
В своей основополагающей книге Алгоритмы + Структуры данных = Программы, Никлаус Вирт объясняет, что для решения любой задачи программирования, вы должны разработать абстракцию реальности, которая имеет характеристики и свойства задачи, и игнорировать остальные детали. Он утверждает, что набор данных, которые вам нужны для решения проблемы, формирует абстракцию. Поэтому, прежде чем вы сможете решить проблема, в первую очередь необходимо определить, какие данные нужно иметь, чтобы создать решение.&lt;br /&gt;
Чтобы продолжить создание многократно используемой абстракции, мы должны создавать библиотеки данных объектов, которые могут быть многократно использованы для решения конкретных видов проблем. Поиск способа сделать это приводит к развитию объектно-ориентированных технологий.&lt;br /&gt;
&lt;br /&gt;
Объектно-ориентированный анализ и проектирование программ сосредоточен вокруг данных объектов, функциональности, связанной с каждым объектом, и отношения между объектами.&lt;br /&gt;
&lt;br /&gt;
Объектно-ориентированные языки предоставляют средства для отделения проблем программы, и  выделения каждой из них независимо друг от друга, и для инкапсуляции данных абстракций и представление их через строго определенные интерфейсы. Полностью объектно-ориентированная программа строится путем разделения функциональности программы в различные классы, определения интерфейса для каждого класса, а затем установления связей и взаимодействий между компонентами через их интерфейсы.&lt;br /&gt;
&lt;br /&gt;
==Классы и объекты==&lt;br /&gt;
&lt;br /&gt;
Основным модулем программирования на объектно-ориентированных языках, таких как SystemVerilog, является класс. Класс содержит элементы данных, называемые членами, и задач и функций, называемых методами. Для выполнения объектно-ориентированных программ, вы должны создать один или более классов в основной программе, а затем вызвать методы на различных объектах. Хотя термины класс и объект иногда используются как синонимы, как правило, термин класс относится к классу декларации объекта, а термин объект относится к экземпляру класса.&lt;br /&gt;
Чтобы проиллюстрировать эти понятия, ниже приведен пример простой класс register.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:27.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:28.png]]&lt;br /&gt;
&lt;br /&gt;
Это очень простой класс с одной переменной, и двумя методами, read() и write(). Чтобы использовать этот класс, вы создаете объекты путем создания экземпляра класса, а затем вызвать методы объекта, как показано ниже&lt;br /&gt;
&lt;br /&gt;
[[Файл:29.png]]&lt;br /&gt;
&lt;br /&gt;
Локальный  атрибут contents  класса указывает компилятору строго соблюдать границы класса. Если вы пытаетесь получить доступ к contents напрямую, компилятор выдаст ошибку. Вы можете получить доступ к содержимому через  доступные функции чтения и записи. Этот вид контроля доступа важен для  гарантирования  независимости от внутренностей класса и тем самым делает класс многократно используемым.&lt;br /&gt;
Вы можете использовать классы для создания новых типов данных, таких, как наш простой register. Использование классов для создания новых типов данных является важной частью ООП. Вы можете также использовать их для инкапсуляции математических вычислений или создания динамических структур данных, таких как стеки, списки, очереди, и так далее. Инкапсуляция организации структур данных или сведений о вычислениях в классе делает структуру данных или вычислений многократно используемыми.&lt;br /&gt;
В качестве более полного примера, давайте посмотрим на полезный тип данных, стек. Стек - это LIFO (&amp;quot;последним пришёл - первым обслужен&amp;quot;) структура. Элементы помещаются в стек командой push(), а  извлекаются из стека pop(). Pop () возвращает последний элемент, помещенный в стек,  и удаляет его из структуры данных. Внутренние элемент  stkptr  следит за вершиной стека. Элемент, на который он указывает, это вершина стека, а все, что ниже его (то есть, с меньшим индексом) ниже в стеке. Ниже приведена базовая реализация стека в SystemVerilog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:30.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:31.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:32.png]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:33.png]]&lt;br /&gt;
&lt;br /&gt;
Класс stack инкапсулирует все, что нужно знать о стеке данных структуры. Он содержит интерфейс и реализацию интерфейса. Интерфейс – это набор методов, которые используются для взаимодействия с классом. Реализация -  это код, который заставляет класс работать. Интерфейс нашего стека содержит следующие методы:&lt;br /&gt;
&lt;br /&gt;
[[Файл:34.png]]&lt;br /&gt;
&lt;br /&gt;
Не  существует  другого способа взаимодействия со стеком, как с помощью этих методов. Есть также два элемента данных  класса, stk и stkptr, которые представляют фактическую структуру стека. Тем не менее, эти два элемента локальные, что означает, что компилятор  запретит любые попытки доступа к ним  за пределами класса. Для  предотвращения доступа к внутренней структуре данных извне, мы можем сделать некоторые гарантии о состоянии данных.  Например, Push () и Pop () могут рассчитывать на то, что stkptr корректно и  указывает на вершину стека. Если бы можно было изменить значение stkptr иными средствами, чем с помощью интерфейса функций, тогда Push () и Pop () пришлось бы прибегнуть к дополнительным затратам и возможностям для ненадежной проверки истинности stkptr.&lt;br /&gt;
Реализация интерфейса встроенная. Объявление класса содержит не только определение интерфейса, а также реализацию каждой функции интерфейса. Оба C + + и SystemVerilog позволяют отделить реализацию от интерфейса. Разделение интерфейса и реализации является важной концепцией. Программисты, пишущие на C + +, могут использовать файлы заголовков для ввода интерфейс и. куб (или. срр или любой другой компилятор использует) для реализации.&lt;br /&gt;
&lt;br /&gt;
Есть некоторые важные  явления доступа через класс интерфейсов. Одним из них является возможность многократного использования. Мы можем удобно использовать классы, интерфейсы которых четко определены и хорошо объяснены, чем те, чьи интерфейсы нечеткие. Другим важным явлением обеспечения доступа через класс интерфейсов является надежность. Авторы класса может гарантировать определенную инвариантность (например, stkptr меньше, чем размер имеющегося массива STK) когда они знают, что пользователи не будут изменять данные иначе, чем предоставляемыми средствами. Кроме того, пользователи могут ожидать, что состояние объекта будет предсказуемым, когда они придерживаются интерфейса. Ясность другое явление. Интерфейс  может описать всю семантику класса. Объект не будет делать ничего другого, чем выполнять операции, доступные через интерфейс. Это делает проще понять для тех, кто использует класс, что именно он будет делать.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Отношения между объектами ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Истинная сила ООП становится очевидной, когда объекты взаимосвязаны различными отношениями. Есть много видов отношений, которые возможны. Мы рассмотрим два из наиболее фундаментальных отношений  HAS-A и IS-A.&lt;br /&gt;
&lt;br /&gt;
=== HAS-A ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
HAS-A относится к концепции, когда один объект содержится или принадлежит другим. В нашем классе стека, например,  HAS-A указатель стека (stkptr) и массив стека. Это примитивные типы данных, а не классы, но применяется тоже понятие HAS-A. В &lt;br /&gt;
SystemVerilog вы можете создать HAS-отношения между классами с  ссылками или указателями. На рисунке ниже показаны основные памяти  модель для HAS-А отношений. Объект А содержит ссылку или указатель на  Объект B.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:35.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Unified Modeling Language (UML) является графическим языком для  представления систем, в частности, отношения между объектами в этих системах. UML для HAS-связи выражается линией между объектами и закрашенным ромбиком(??????), как показано на рисунке ниже.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:36.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Объект А принадлежит экземпляру объекта B. кодирование HAS-А отношений в &lt;br /&gt;
SystemVerilog заключается в включении  одного класса в другой или других способов предоставления доступа одного класса  к одному классу, который хранится внутри.&lt;br /&gt;
&lt;br /&gt;
[[Файл:37.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Класс А содержит ссылку на класс B. конструктор для класса A, метод new(), вызывает new()  в классе B, чтобы создать его экземпляр.  Член b содержит ссылку на вновь созданный экземпляр B.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Virtual Functions and Polymorphism==&lt;br /&gt;
&lt;br /&gt;
IS-А отношения чаще всего называют наследованием. Новый класс является производным от ранее существующего класса и наследует его характеристики.  Объекты, созданные с наследованием наследования, созданы с использованием отношения IS-A. Производный класс  является подклассом или более специализированная версия родительского объекта.  Для иллюстрации понятия наследования, рисунок 2-3 использует часть систематики млекопитающих.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:38.png]]&lt;br /&gt;
&lt;br /&gt;
Животные, которые являются членами китообразных, хищных, приматов -  млекопитающие. Эти очень разные виды существ имеют общие черты  млекопитающих. Тем не менее, у китообразных (киты, дельфины), плотоядных (собак, медведей, енотов),  и приматов (обезьян, людей), у каждого есть свои четкие и безошибочные  характеристики.  Для использования ОО терминологии, медведь - хищник и плотоядный  IS- А млекопитающего. Иными словами, медведь состоит из атрибутов и  плотоядные млекопитающие и плюс, дополнительные атрибуты, отличающие его от  других плотоядных животных.&lt;br /&gt;
&lt;br /&gt;
Для представления   IS-A  на языке  UML, мы рисуем линию между объектами с открытыми  стрелками, указывающие на базовый класс. Традиционно, мы изображаем базовый класс  выше производных классов, а стрелки указывают вверх, образуя дерево наследования (или ориентированный ациклический граф, который может быть реализован в  языках, таких как C + +, которые поддерживают множественное наследование).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:38.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При представлении двух объектов в компьютерной программе с использованием &lt;br /&gt;
наследования, новый производный объект содержит характеристики родителей и обычно включает в себя дополнительные характеристики. На рисунке ниже показана &lt;br /&gt;
базовая модель памяти для IS-А композиции. В этом примере класс  B является производным от А.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:39.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
SystemVerilog использует ключевое слово  extends на определения наследования между классами:&lt;br /&gt;
&lt;br /&gt;
[[Файл:40.png]]&lt;/div&gt;</summary>
		<author><name>Anastasiya</name></author>	</entry>

	</feed>