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

OVM/OVM методология/Transaction-Level моделирование

Материал из Wiki
< OVM‎ | OVM методология
Версия от 19:11, 9 апреля 2013; Anastasiya (обсуждение | вклад)

(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Это снимок страницы. Он включает старые, но не удалённые версии шаблонов и изображений.
Перейти к: навигация, поиск
Проект Диплом

Вебинар Basic OVM
Литература

* OVM *

Содержание


Процесс проектирования электронной системы включает в себя последовательное замещение абстрактных идей конкретной их реализацией, пока не будет достигнуто представление, которое может быть изготовлено на кремнии. С момента появления цифровой интегральной схемы, электронные сообщества тщательно определили и кодировали абстракции, начиная с переключателей и логических элементов, для обеспечения среды в которой работает проект. RTL является примером среды, часто используемой для создания проектов. Есть много инструментов на основе RTL, которые делают ее удобной для создания проекта и его верификации. Однако, как только проекты становятся больше и сложнее, становится все сложнее представлять их с помощью абстракции выше, чем RTL. Уровень транзакций становится популярным для создания первого представления проекта, которое может быть смоделировано и проанализировано. В этой главе описаны основные понятия transaction-level моделирования (TLM). Модели transaction-level состоят из нескольких процессов, которые обмениваются информацией посредством транзакций.


Абстракция

RTL моделирование использует дискретную модель времени. Связь между процессами осуществляется с помощью сетки (цепи), и процесс активации происходит при изменении значения во входной сети (цепи). Для сравнения, transaction-level модели (моделирующей схемы) могут быть синхронизированы или не синхронизированы и использовать шину для обмена данными между процессами. Вместо того чтобы посылать отдельные биты туда и обратно, процессы общаются, посылая транзакции друг другу посредством вызова функций. Мир TLM включает в себя ряд моделей(моделирующих схем) вычислений с различным временем, системами связи и процессами активации моделей (моделирующей схемы). В каждом конкретном случае, содержание связей находится на более высоком уровне абстракции, чем отдельные биты. Таким образом, transaction-level модель находится на более высоком уровне абстракции (более абстрактна), чем RTL модель. Объединяя понятия абстракции и модели (моделирующей схемы) вычислений, видно, что создание абстрактных моделей означает абстрагирование времени, данных и функций.

Абстракция времени. Время абстракция в симуляторе относится к тому, насколько внутренние состояния схемы совместимы. Модели, запущенные в управляемых событиями симуляторах (например, логических симуляторах) используют дискретное представление времени, то есть события происходят в определенные моменты времени. События, как правило (хотя и не всегда) вызывают запущенный процесс. Чем больше событий происходит в симуляции, больше процессов вызывается, и с большим числом процессов происходит снижении скорости моделирования. Абстрагирование время уменьшает количество точек, где схема должна быть совместимой и общего числа событий и процессов, которые будут активированы. Например, в RTL модели, каждая сеть должна быть согласована после каждого изменения. В cycle-accurate абстракции, схема должна быть синхронизирована с синхросигналом, устраняя все события, что происходит между фронтами синхроимпульса. В transaction-level модели, состояния схемы должны быть синхронизированы в конце каждой операции, каждая из которых может охватывать много тактов.

Абстракция данных. Данные относятся к объектам, передаваемым между компонентами. В RTL моделях, данные относятся к отдельным битам, которые передаются по сети между компонентами. В transaction-level моделях, данные представлены в виде транзакций, гетерогенных структур, которые содержат произвольные коллекции элементов. Рассмотрим пакет в устройстве связи. На самом низком уровне детализации, пакет содержит бит начала и стоп-биты, заголовок, информацию исправления ошибок, размер передаваемых данных, данные, и пакет остановки передачи(последний пакет - trailer). В более абстрактной модели, только данные и размер данных может быть необходим. Другие данные не нужны для выполнения расчетов.

Абстракция функций. Функция модели является набором всех операций, которая она делать на каждое событие. Абстрагирование функций уменьшает этот набор или заменяет его более простыми вычислениями. Например, в ALU, вы можете выбрать использование родной операции умножении поставляемой в язык моделирования, вместо кодирования a shift-and-add алгоритма умножения. Последний может быть частью реализации, но на более высоком уровне, детали a shift-and-add - неважны. Базовые элементы, которые являются частью языка, определяет, как вы можете абстрагировать функцию. В gate-level языке, например, можно создавать сложные поведения, начиная с логических элементов. В RTL языках, построение поведения основывается на арифметических и логических операциях над регистрами. В TLM, вы реализуете функциональность схемы с помощью вызовов функций произвольной сложности.

Для целей функциональной верификации, RTL самый низкий уровень абстракции, который мы должны рассмотреть. С синтезаторы могут эффективно конвертировать RTL в логические элементы, нам не нужно затрагивать более низкий уровень детализации.

Определение транзакции

Для более подробного рассмотрения TLM, мы должны сделать шаг назад и определить понятие транзакции.

61.png

Это самое общее определение транзакции. Оно утверждает, что транзакция – это все, что происходит в проекте (или модуле или подсистеме проекта) между двумя точками времени. Хотя оно точное, оно является настолько общим, что не приводит к практическому применению. Более полезные определения следующие:

62.png

Транзакция — передача управления и данных между двумя элементами (компонентами)

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

Ниже третье определение:

63.png

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

Интерфейсы

Прежде чем вдаваться в подробности о том, как построить transaction-level модель, мы сначала обсудим интерфейсы. Термин интерфейс используется в несколько случаях в OVM, каждый раз с немного другим смыслом. Это прискорбный факт истории, то что слово стало означать различные вещи. В большинстве случаев значение будет понятно из контекста, в котором этот термин используется. Различных применения :

  • Интерфейс в SystemVerilog
  • Интерфейс объекта
  • Интерфейс DUT

Интерфейс в SystemVerilog. SystemVerilog предоставляет конструкцию, называемую интерфейс, который является одним из основных объектов контейнера, из которого строится проект в SystemVerilog. Мы используем виртуальные интерфейсы, которые указывают на интерфейсы для подключения module-based аппаратуры к class-based testbenches.

Интерфейс объекта. Общедоступные задачи и функции, доступные для объекта, образуют его интерфейс. Есть два небольших варианта этого значения интерфейса. Одним из них является прямой. Посмотрите на класс и определите, какие задачи и функций, доступны пользователю класса. Это его интерфейс. Другое значение состоит в ссылке на базовый класс, который определяет набор задач и функций, доступных для работы производного класса. Это значение интерфейса обычно используется в объектно-ориентированных языках, которые поддерживают множественное наследование, таких как C++ или Java. В этих языках, вы можете установить требование, что производный класс определяет функциональность, наследуемую от интерфейса базового класса.

64.png

Интерфейс print_if определяет прототипы для функций печати. Любой класс, который наследуется от print_if затем обязан осуществлять print()и sprint(). SystemVerilog не поддерживает множественное наследование, но он поддерживает истинно виртуальные интерфейсы. Истинно виртуальный интерфейс - интерфейс во втором контексте (базовый класс, который определяет набор задач и функций прототипов), который не имеет реализаций. Истинно виртуальная версия print_if выглядит следующим образом в SystemVerilog:


virtual class print_if;
pure virtual function void print();
pure virtual function string sprint();
endclass


Даже если SystemVerilog не поддерживает множественное наследование, и OVM построено на SystemVerilog, важно понять истинно виртуальные интерфейсы и наследование интерфейсов, потому что они активно используются в OVM. В частности, TLM ports and exports являются производными от интерфейса класс tlm_if_base.

DUT интерфейс. Часть аппаратуры, как правило, доступны через свои интерфейсы. В этом контексте, интерфейс состоит из контактов и протоколов используемых для связи с устройством. Например, устройство может иметь USB интерфейс.

TLM идиомы

В данном разделе рассматриваются основные средства передачи транзакций между компонентами. Мы рассмотрим put, get, и transport формы связи транзакций. Эти примеры не используют библиотеку OVM, так как они предназначены для иллюстрации основных механизмов связи на transaction-level с минимальными затратами. В следующем разделе мы рассмотрим более полный пример, который использует OVM библиотека для связи.

Put

В put конфигурации, один компонент отправляет операции другому компоненту. Операция называется put. Инициатором является компонентом, который инициирует передачу, а целевой компонент, который получает результат. Используя TLM номенклатуру, мы говорим, что инициатор put транзакции цели.

65.png

Рисунок 3-2 показывает, что А предает операцию в пункт Б. Инициатор имеет порт, рисуется как квадрат, и цель имеет export, изображается в виде круга. Поток управления направлен от квадрата к кругу, то есть, А вызывает B, который содержит реализацию методов port. Стрелка показывает направление поток данных, и в этом случае, это означает, что данные будут двигаться от А до B. Мы можем проиллюстрировать код для этих компонентов с producer и consumer. producer является инициатором и consumer является целью. Мы должны строить эти компоненты таким образом, что они не знали друг о друге априори. Чтобы сделать это, мы используем истинно виртуальный интерфейс для определения функции, который будет использоваться для передачи данных между инициатором и целью. Во-первых, давайте взглянем на SystemVerilog версию producer.


46 class producer;
47
48 put_if put_port;
49
50 task run();
51
52 int randval;
53
54 for(int i=0; i<10; i++)
55 begin
56 randval = $random %100;
57 $display(“producer: sending %4d”, randval);
58 put_port.put(randval);
59 end
60
61 endtask
62
63 endclass : producer
file: 03_tlm/01_put/put.sv


producer - это класс, который создается динамически. Он имеет два ключевых элемента, Run () задачу и put_port. Run () задача является простой задачей, которая повторяется 10 раз и передает 10 транзакций. Для простоты, наши операции являются целыми числами. На практике, транзакция может быть сколь угодно сложным объектом, таким как структура или класс. Чтобы передать операции, producer вызывает put() на put_port. Что такое put_port? Это не порт в традиционном смысле Verilog. Это ссылка на put_if. Что такое put_if? Put_if это виртуальный интерфейс класса расположенный между инициатором (producer) и целью (consumer).


39 virtual class put_if;
40 pure virtual task put(int val);
41 endclass : put_if


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


68 class consumer extends put_if;
69 task put(int val);
70 $display(“consumer: receiving %4d”, val);
71 endtask : put
72 endclass : consumer
file: 03_tlm/01_put/put.sv


consumer содержит реализацию put(); истинно виртуальные задачи определены в put_if. Задача put() реализация принимает аргументы, переданные ей, и печатает их. put_if играет ключевую роль в соединении consumer и producer. Ссылка на него на стороне producer, которую мы называем порт, устанавливает требование, что должна быть реализация функций и задач интерфейса, к которому этот объект будет привязан. consumer является производным от интерфейса и, следовательно, в нем должны быть реализованы истинно виртуальные задачи, удовлетворяющие требованиям.

Модуль верхнего уровня связывающий producer и consumer.


77 module top;
78
79 producer p;
80 consumer c;
81
82 initial begin
83 // instantiate producer and consumer
84 p = new();
85 c = new();
86 // connect producer and consumer
87 // through the put_if interface class
88 p.put_port = c;
89 p.run();
90 end
91 endmodule : top
file: 03_tlm/01_put/put.sv


Обратите внимание, что оператор присваивания:


88 p.put_port = c;


Он образует связь между producer и consumer. Когда new() вызывается на р, чтобы создать новый экземпляр producer, член put_port не имеет значения. Во время выполнения произойдет ошибка, если put_port. put () вызывается до связи назначения. Назначение С к p.put_port дает ссылку для consumer, который содержит реализацию задачи put() интерфейса.


Get

Дополнением к put является get. В этой конфигурации, инициатор получает транзакции от цели. Поток управления такой же как у put — от инициатора к цели, но направление потока данных обратное. Инициатор получает транзакции от цели. В этом случае consumer является инициатором (блок А), producer — целью (блок В). Consumer инициирует вызов producer для получения транзакции.

66.png

Рисунок 3-3 очень похож на рисунок 3-2. Разница лишь в том, что здесь стрелка от цели к инициатору, а не наоборот. Это означает, что потоки данных идут от цели к инициатору. Ниже SystemVerilog consumer (инициатор, блок А).

62 class consumer;
63
64 get_if get_port;
65
66 task run();
67 int randval;
68 for(int i=0; i<10; i++)
69 begin
70 get_port.get(randval);
71 $display(“consumer: receiving %4d”, randval);
72 end
73 endtask
74 endclass
file: 03_tlm/02_get/get.sv

Класс consumer имеет задачу, run(), в которой выполняется 10 транзакций. Так же как класс producer в конфигурации put, в данном примере класс consumer имеет порт. Кроме того, как в put примере, порт ссылается на истинно (pure) виртуальный интерфейс, в данном случае он называется get_if.

41 virtual class get_if;
42 pure virtual task get(output int t);
43 endclass : get_if
file: 03_tlm/02_get/get.sv

get_if является истинно виртуальным интерфейсным классом, который определяет функцию get(). Цель (producer) строится аналогично, как и цель в put примере. Она содержит реализацию функции интерфейса. Этот producer генерирует случайное число между 0 и 99.

48 class producer extends get_if;
49
50 task get(output int t);
51 int randval;
52 randval = $random % 100;
53 $display(“producer: sending %4d”, randval);
54 t = randval;
55 endtask
56
57 endclass : producer
file: 03_tlm/02_get/get.sv


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


79 module top;
80
81 producer p;
82 consumer c;
83
84 initial begin
85 // instantiate producer and consumer
86 p = new();
87 c = new();
88 // connect producer and consumer through the get_if
89 // interface class
90 c.get_port = p;
91 c.run();
92 end
93 endmodule : top
file: 03_tlm/02_get/get.sv

После создания объектов producer и consumer, используя операцию new(), два объекта будут соединяться с помощью связей назначения.

Transport

Transport является двунаправленным интерфейсом. Интерфейс обеспечивает передачу транзакций от инициатора к цели и от цели обратно к инициатору. Как правило, мы используем этот механизм, чтобы смоделировать запрос-ответ протоколы. Когда речь идет о компонентах с двунаправленным интерфейсов, мы используем термины master (ведущего) и slave (ведомого), а не инициатор и цель.

67.png

master (A) делает и put и get за один вызов функции. Как мы видели в предыдущих примерах, put() и get() принимают один аргумент, аргумент они помещают или извлекают. Тем не менее, функция transport() имеет два аргумента, запрос и ответ. Она посылает запрос и возвращает ответ. slave (B) принимает запрос и возвращает ответ.

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

37 virtual class transport_if;
38 pure virtual task transport(input int request,
39 output int response);
40 endclass : transport_if
file: 03_tlm/03_transport/transport.sv


Интерфейс содержит одну функцию, transport(), которая принимает два аргумента: запрос, который передается цели и ответ, который возвращается инициатору.

Master вызывает transport(), создает запрос и отправляет его slave, используя transport. Он обрабатывает ответ, который возвращается.

45 class master;
46
47 transport_if port;
48
49 task run();
50
51 int request;
52 int response;
53
54 for(int i=0; i<10; i++)
55 begin 
56 request = $random % 100;
57 $display(“master: sending request %4d”,
58 request);
59 port.transport(request, response);
60 $display(“master: receiving response %4d”,
61 response);
62 end
63
64 endtask
65 endclass : master
file: 03_tlm/03_transport/transport.sv

slave реализует функцию transport(). В нашем примере, она делает некоторые тривиальные обработки запроса для создания ответа.

70 class slave extends transport_if;
71
72 task transport(input int request, output int response);
73 $display(“slave: receiving request %4d”,
74 request);
75 response = -request;
76 $display(“slave: sending response %4d”,
77 response);
78 endtask
79
80 endclass
file: 03_tlm/03_transport/transport.sv


Связи верхнего уровня между master и slave работают точно так же как и в примере с put и get.

85 module top;
86
87 master m;
88 slave s;
89
90 initial begin
91 // instantiate the master and slave
92 m = new();
93 s = new();
94
95 // connect the master and slave through
96 // the port interface
97 m.port = s;
98 m.run();
99 end
100
101 endmodule : top
file: 03_tlm/03_transport/transport.sv

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


Блокирующие интерфейсы против не блокирующих

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

В put конфигурации, у нас есть два компонента, producer и consumer. Producer генерирует случайное число и посылает его к consumer, используя put(). Прежде чем вызывается put(), нет активности в consumer. Вызов put() вызывает активность в consumer, которая выводит значение аргумента. За то время, что consumer является активным, producer ожидает. Это природа блокирующего вызова. Вызывающий должен ждать, пока вызов завершиться для возобновления выполнения.

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

Истинно виртуальный интерфейс, который соединяет неблокирующего slave и master выглядит так же, как другие истинно виртуальные интерфейсы, которые мы видели. Значительная разница в том, что nb_get () возвращает значение статуса вместо транзакции.


41 virtual class get_if;
42 pure virtual function int nb_get(output int t);
43 endclass : get_if
file: 03_tlm/04_nonblocking/nbget.sv


master (consumer) должен проверить состояние, возвращаемое из nb_get (), чтобы определить, завершена ли функция успешно. Отметим также, что мы ввели время в модель. Consumer проверяет каждый 4 нс доступность значения.


78 class consumer; 
79
80 get_if get_port;
81
82 task run();
83 int randval;
84 int ok;
85
86 for(int i=0; i<20; i++)
87 begin
88 #4;
89 if(get_port.nb_get(randval))
90 $display(%t: consumer: receiving %4d”, $time,
randval);
91 else
92 $display(%t: consumer: no randval”, $time);
93 end
94 endtask
95 endclass
file: 03_tlm/04_nonblocking/nbget.sv


producer организован как функция и задача. Задача будет раздвоена (порождена), чтобы запуститься как непрерывный процесс. Он генерирует новые случайные значения, что consumer будет захватывать. Тем не менее, каждая случайная величина доступна только для 2 нс из 7 нс цикла. Функция - реализация nb_get, которая возвращает значение, запускает периодически выполнение run().


48 class producer extends get_if;
49
50 int randval = 0;
51 int rand_avail = 0;
52
53 function int nb_get(output int t);
54 if(rand_avail) begin
55 $display(%t: producer: sending %4d”,
56 $time, randval);
57 t = randval;
58 return 1;
59 end
60 return 0;
61 endfunction
62
63 task run();
64 forever begin;
65 #5;
66 randval = $random % 100;
67 rand_avail = 1;
68 #2;
69 rand_avail = 0;
70 end
71 endtask
72
73 endclass : producer
file: 03_tlm/04_nonblocking/nbget.sv


Когда мы запускаем пример, мы видим, что не каждый вызов nb_get () успешен.

4: consumer: no randval
8: consumer: no randval
12: producer: sending -99
12: consumer: receiving -99
16: consumer: no randval
20: producer: sending -39
20: consumer: receiving -39
24: consumer: no randval
28: producer: sending -9
28: consumer: receiving -9
32: consumer: no randval
36: consumer: no randval
40: producer: sending 57
40: consumer: receiving 57
44: consumer: no randval
48: producer: sending -71
48: consumer: receiving -71
52: consumer: no randval
56: producer: sending -14
56: consumer: receiving -14
60: consumer: no randval
64: consumer: no randval
68: producer: sending 29
68: consumer: receiving 29
72: consumer: no randval
76: producer: sending 18
76: consumer: receiving 18
80: consumer: no randval
Блокировать get конфигурацию смог только один процесс - consumer, который постоянно обращался с запросом к producer, чтобы тот отправил новое значение. Неблокирующий вариант состоит из двух этапов: consumer регулярно опрашивает producer, чтобы получить новое значение, и producer генерирует новые значения асинхронно по отношению к consumer. Наши неблокирующий producer генерирует доступное случайное значение каждые 7 нс. Он ждет 5 нс, а затем создает новое значение и новое значение действительно в течение 2 нс. Флаг rand_avail устанавливается, когда случайное значение доступно и сбрасывается в противном случае.

Реализация nb_get () в этом примере должна проверить rand_avail есть ли что отправить. Если нет, то она возвращает 0, чтобы указать, что запрос не удался. Если есть что-то, то он посылает его и возвращает 1, чтобы показать успех.

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

Изоляция компонент с помощью каналов

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


68.png


В предыдущих примерах, одна компонента имела порт, а другая export. Компонента с портом обращается к компоненте с export. Здесь оба А и В имеют порты. Вместо того, чтобы инициатор обращался к непосредственно к цели, теперь у нас есть инициатор и цель, которые обращаются к каналу FIFO. Канал обеспечивает функции, необходимые как инициатору, так и цели.

Инициатор использует блокирующий put() для отправки транзакций на FIFO, и цель использует блокирующий get() для получения транзакций с FIFO. FIFO хранит транзакции и служит в качестве синхронизатора. Инициатор может помещать транзакции в FIFO, пока он не заполнится . Так как инициатора использует блокировку Put (), инициатор процесса будет блокироваться, когда FIFO заполнен. Кроме того, цель использует блокирующий get() и будет блокироваться, когда FIFO является пустым. По сути, producer в этом примере, как producer в блокирующем put примере, и consumer, как consumer в блокирующем get. FIFO replaces цели и предоставляет задачи, необходимые для удовлетворения требований интерфейса, созданные портами producer и consumer.

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

В этом случае producer выглядит как producer в блокирующем пут примере. Он имеет процесс, run(), который выполняется 10 раз, генерирует 10 случайных значений и отправив их к цели через put_port.

42 class producer extends ovm_component;
43
44 ovm_blocking_put_port#(int) put_port;
45
46 function new(string name, ovm_component p = null);
47 super.new(name,p);
48 put_port = new(“put_port”, this);
49 endfunction
50
51 task run();
52
53 int randval;
54 string s;
55
56 for(int i = 0; i < 10; i++)
57 begin
58 randval = $random % 100;
59 $sformat(s, “sending %4d”, randval);
60 ovm_report_info(“producer”, s);
61 put_port.put(randval);
62 end
63 global_stop_request(); // OK, we’re done now
64 endtask
65
66 endclass
file: 03_tlm/05_fifo/fifo.sv

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

Кроме того, необходимо заметить, как put_port объявлен. В наших простых примерах выше, мы создали наш собственный истинно виртуальный интерфейс для подключения инициатора к цели. Библиотека OVM поставляет набор портов и экспорт объектов, которые являются обертками вокруг истинно виртуальных ссылок интерфейса. Порт и экспорт объектов, которые сами по себе именные компоненты, позволяют функции connect() создавать ассоциаций между ports and exports. Использование модели лучше по сравнению с использованием операторов присваивания.

Consumer не сильно отличается от consumer в блокирующем get примере.

71 class consumer extends ovm_component;
72
73 ovm_blocking_get_port#(int) get_port;
74
75 function new(string name, ovm_component p = null);
76 super.new(name,p);
77 get_port = new(“get_port”, this);
78 endfunction
79
80 task run();
81
82 int val;
83 string s;
84
85 forever
86 begin
87 get_port.get(val);
88 $sformat(s, “receiving %4d”, val);
89 ovm_report_info(“consumer”, s);
90 end
91
92 endtask
93
94 endclass
file: 03_tlm/05_fifo/fifo.sv


Для подключения producer, consumer и FIFO, мы используем окружающую среду. Средой служит вершина иерархии именованных компонент, и это организует построение иерархии и выполнения testbench.

99 class env extends ovm_component;
100 producer p;
101 consumer c;
102 tlm_fifo #(int) f;
103
104 function new(string name, ovm_component parent = null);
105 super.new(name, parent);
106 endfunction
107
108 function void build();
109 p = new(“producer”, this);
110 c = new(“consumer”, this);
111 f = new(“fifo”, this);
112 endfunction
113
114 function void connect();
115 p.put_port.connect(f.blocking_put_export);
116 c.get_port.connect(f.blocking_get_export);
117 endfunction
118
119 endclass
file: 03_tlm/05_fifo/fifo.sv

Функция connect() создает связь между портами на producer и consumer и соответствующего экспорта на FIFO. Run () отвечает за контроль выполнения testbench.


Формирование Transaction-Level подключений

Для формирования transaction-level подключения, необходимо указать три элемента: поток управления, поток данных, а также тип данных транзакций. Объявление подключения как порта или export определяет поток управления – поток управления движется(???) от порта к export. То есть, порт инициирует деятельность и export реагирует на нее. Интерфейс определяет поток данных. put интерфейс означает, что данных движутся от инициатора (левый борт) к цели ( сторона export), get интерфейс показывает, что данные идут от цели к инициатору, а transport или запрос-ответ, интерфейс указывает двунаправленность потоков данных.

Мы объявляем put_port как порт, поэтому мы знаем, что устройство, в котором этот порт объявлен является инициатором. Тип интерфейса - tlm_nonblocking_put_if <>, который является одним put интерфейсов, определенных в библиотеке TLM. Этот порт выход для объектов данных. Наконец, тип данных отправленного объекта является trans.

Использование OVM в SystemVerilog, объявление портов и export охватывает эти три элемента. Вот пример:

ovm_nonblocking_put_port #(trans) put_port;

Суффикс тип объекта _port, указывает, что это порт. Exports используют суффикс _export.Тип интерфейса определяется по имени между префиксом ovm_ и _port или суффиксом _export. В этом случае, это имя nonblocking_put, который относится к tlm_nonblocking_put_if. Мы рассмотрели producer и consumer, каждый из которых использует блокирующие задачи для получения и отправки транзакций. Блокирующие задачи находятся в FIFO, объект, который выступает в качестве посредника между двумя компонентами, в другом случае известный как канал. Канал передает данные между двумя компонентами, и служит синхронизатором. Ввод FIFO между двумя компонентами для буферизации и синхронизации передачи является common idiom в TLM.

Заключение

Put, get и transport являются основными средствами для синхронизации параллельных процессов и для обмена информацией между эти процессы на уровне transaction-level. Эти идеи широко используются в OVM для построения transaction-level testbenches. В следующей главе мы будем углубляться в ОВМ, чтобы показать, как строить произвольные иерархии компонент верификации, основанных на классах , связанных с помощью transaction-level интерфейсов.