«…лишь недалекие люди боятся конкуренции, а люди подлинного творчества ценят общение с каждым талантом…» А. Бек, Талант.

OVM/Basic OVM/Session6 - Introducing Transactions

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

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

* OVM *

Module basic ovm session6 introducing transactions jaynsley.pdf

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

Module basic ovm session6 introducing transactions jaynsley.pdf

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

Module basic ovm session6 introducing transactions jaynsley.pdf

Но сначала покажем, какое место в этой картине занимает иерархия классов OVM, с которой мы познакомились на предыдущем занятии. Мы видели, что в OVM имеется целое иерархически организованное семейство классов для описания компонентов, то есть структурных элементов. Имеется также параллельная иерархия классов для описания транзакций, или данных. Базовым для всех транзакций является класс ovm_transaction. Последовательность, строго говоря, представляется классом, производным от ovm_sequence_item. Таким образом, класс ovm_sequence расширяет ovm_sequence_item, который в свою очередь расширяет ovm_transaction, ну а тот расширяет ovm_object. Транзакции и последовательности - это данные в противоположность компонентам, которые суть элементы структуры. Транзакции не являются частью иерархии компонентов. Если у компонента OVM обычно имеется родительский компонент, то у транзакций, передаваемых от одного компонента другому, родителей нет.

Module basic ovm session6 introducing transactions jaynsley.pdf

Давайте посмотрим, как можно определить в OVM свою транзакцию. Мы создадим пользовательский класс my_transaction, расширяющий ovm_sequence_item. Отметим, что мы расширяем класс ovm_sequence_item, а не ovm_transaction, потому что создаваемая транзакция, скорее всего, будет частью последовательности. Наследование от ovm_sequence_item вместо ovm_transaction как раз и позволяет транзакции быть частью последовательности. Поэтому в общем случае классы транзакций следует создавать путем расширения ovm_sequence_item.

Module basic ovm session6 introducing transactions jaynsley.pdf

Во второй строке нашего класса мы регистрируем его в качестве транзакции, применяя еще один стандартный макрос OVM. Обратите внимание, что мы используем ovm_object_utils, а не ovm_component_utils. Мы встречались с двумя стандартными макросами для регистрации классов:ovm_component_utils и ovm_object_utils. Крайне важно использовать правильный макрос.

Module basic ovm session6 introducing transactions jaynsley.pdf

Далее идут поля - свойства транзакции. В данном случае транзакция содержит команду, адрес и данные. Они и составляют свойства класса. Объявлению каждого свойства предшествует ключевое слово rand. В языке SystemVerilog слово rand означает, что после создания и рандомизации экземпляра данного класса значения объявленных таким образом свойств будут случайными. И это правильно, потому что мы собираемся использовать сгенерированные транзакции как стимулы для подачи на входы тестируемого устройства. Наконец, после объявления свойств мы видим первый пример ограничений SystemVerilog. Настоятельно рекомендуется включать такие ограничения в определение транзакции,чтобы ее свойства гарантированно имели осмысленные значения по умолчанию.Тогда, что бы ни происходило в конкретных тестах, если мы сгенерируем экземпляр класса my_transaction по умолчанию, что мы и собираемся сделать, наличие ограничений дает уверенность, что свойства такого экземпляра будут иметь разумные значения. Мы задали ограничения так, чтобы адрес и данные находились в допустимом диапазоне. Касательно ограничений стоит сделать одно важное замечание: поскольку это ограничения, а не процедурный код, впоследствии их можно будет переопределить. Стало быть, ограничения по природе своей не жесткие, вы всегда можете изменить решение. Пока что мы сказали, что адрес лежит в диапазоне от 0 до 256, и, значит, обычно он будет принадлежать этому диапазону. Но позже, если возникнет такая необходимость, мы сможем переопределить это ограничение, заменив его чем-то совершенно иным.

Module basic ovm session6 introducing transactions jaynsley.pdf

Далее следует конструктор, как во всяком пользовательском классе. У любого класса в языке SystemVerilog должен быть конструктор, и здесь мы включили в конструктор типичный трафаретный код. Однако отметим, что этот конструктор несколько отличается от конструктора класса, производного от ovm_component. Поскольку транзакция не является частью иерархии компонентов, у нее нет родительского компонента. Поэтому у конструктора отсутствует второй аргумент, parent. Это, конечно, очень простая транзакция, но ее уже достаточно для работы.

Module basic ovm session6 introducing transactions jaynsley.pdf

Итак, транзакцию мы определили, теперь перейдем к контроллеру. Контроллер - это стандартный компонент OVM, задача которого – генерировать случайную последовательность транзакций.

Module basic ovm session6 introducing transactions jaynsley.pdf

Для создания контроллера мы определим пользовательский класс, расширяющий ovm_sequencer - еще один базовый класс из библиотеки OVM.

Module basic ovm session6 introducing transactions jaynsley.pdf

ovm_sequencer - это пример параметризованного класса в SystemVerilog. Для тех, кто знаком с объектно-ориентированным программированием, скажу, что параметризованные классы в SystemVerilog похожи на шаблоны классов в C++. Параметризованный класс позволяет модифицировать некоторые свойства класса SystemVerilog в момент его создания. В данном случае при инстанцировании класса ovm_sequencer мы задаем вид транзакции, генерируемой этим контроллером. Таким образом, класс my_sequencer расширяет ovm_sequencer с целью генерировать транзакции типа my_transaction.

Module basic ovm session6 introducing transactions jaynsley.pdf

Поскольку этот класс - стандартный компонент OVM,его код должен следовать стандарту написания компонентов.Мы регистрируем компонент макросом ovm_component_utils и включаем стандартный конструктор. Больше ничего не требуется. Это очень простой контроллер и тем не менее в него встроена возможность генерировать последовательность транзакций. Далее мы увидим, как можно заставить контроллер делать то, что нам нужно в этом конкретном случае.

Module basic ovm session6 introducing transactions jaynsley.pdf

Мы изменяем контроллер так, чтобы он производил нужные нам действия, запуская на нем последовательность. В этом месте терминология OVM становится несколько путаной. На предыдущем слайде был показан класс ovm_sequencer с буквой r в конце. А теперь мы встречаем класс ovm_sequence. Напомним, что контроллер (sequencer) - это компонент, структурный элемент, принадлежащий иерархии компонентов. С другой стороны, последовательность (sequence) - это класс, расширяющий ovm_transaction. Последовательность состоит из данных, динамически изменяющихся во времени. Последовательность запускается на контроллере. На данном слайде показана определенная пользователем последовательность my_sequence. Она расширяет базовый класс ovm_sequence - еще один пример параметризованного класса. Он параметризуется типом транзакций, из которых составлена последовательность. Последовательность ovm_sequence всегда состоит из транзакций. То есть последовательность - это на самом деле последовательность транзакций.

Module basic ovm session6 introducing transactions jaynsley.pdf

Ну а далее идет стандартный код. Мы должны зарегистрировать последовательность с помощью макроса ovm_object_utils. У последовательности имеется стандартный конструктор. А теперь более интересная часть - у последовательности есть метод body.

Module basic ovm session6 introducing transactions jaynsley.pdf

С этим мы еще не сталкивались. Весь содержательный код, описывающий фактическое поведение последовательности, должен находиться в определяемой пользователем задаче body. В каком-то смысле метод body аналогичен фазовым методам, хотя и не принадлежит к числу стандартных фаз OVM. Метод body применяется только в последовательностях и определяет существенное поведение последовательности.

Module basic ovm session6 introducing transactions jaynsley.pdf

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

Module basic ovm session6 introducing transactions jaynsley.pdf

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

Module basic ovm session6 introducing transactions jaynsley.pdf

Это был первый шаг - создание транзакции. Последующие шаги со второго по четвертый составляют стандартный шаблон кода. На шаге 2 вызывается метод start_item, на шаге 3 транзакция рандомизируется, а на шаге 4 вызывается метод finish item. Итак, мы создаем транзакцию, сигнализируем инфраструктуре, что начинаем процесс ее обработки, рандомизируем объект транзакции и вызываем finsh_transaction, сообщая о том, что обработка закончена. Методы start_item и finish_item запускают внутренние механизмы взаимодействия с драйвером, который будет потребителем данной транзакции.

Module basic ovm session6 introducing transactions jaynsley.pdf

Итак, я показал, как создать экземпляр контроллера, еще одного стандартного компонента OVM; и как создать последовательность, которая будет выполняться на данном контроллере. Теперь пришло время взглянуть на драйвер.

Module basic ovm session6 introducing transactions jaynsley.pdf

Драйвер - это тоже стандартный компонент OVM. Класс пользовательского драйвера расширяет класс ovm_driver, который параметризован типом транзакции, потребляемой данным драйвером.

Module basic ovm session6 introducing transactions jaynsley.pdf

При написании класса мы делаем все, что необходимо для компонента OVM. Как видим, присутствует регистрация драйвера, объявление виртуального интерфейса, конструктор, методы new и build и, наконец, задача run.

Module basic ovm session6 introducing transactions jaynsley.pdf

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

Module basic ovm session6 introducing transactions jaynsley.pdf

Этот конкретный драйвер взаимодействует с RTL-кодом, в самом начале он синхронизируется по фронту синхроимпульса в интерфейсе тестируемого устройства. Это делается в предложении @(posedge dut_vi.clock).

Module basic ovm session6 introducing transactions jaynsley.pdf

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

Module basic ovm session6 introducing transactions jaynsley.pdf

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

Module basic ovm session6 introducing transactions jaynsley.pdf

И наконец, потребив все четыре транзакции, драйвер может вызвать метод stop_request, чтобы прекратить моделирование. Таким образом, драйвер потребляет транзакции и подает сигналы на входы тестируемого устройства.

Module basic ovm session6 introducing transactions jaynsley.pdf

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