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

OVM/OVM методология/Основы Testbench

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

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

* OVM *

Содержание

Основы Testbench

Чтобы ответить на вопросы does-it-work, мы должны провести разработку с известными входными воздействиями и определить, реагирует ли конструкция так, как задумано. То есть, мы должны контролировать и наблюдать за DUT.

5.1 драйвера и мониторы

Два из наиболее фундаментальных объектов в тестбенчах являются драйвера и мониторы. Драйвер преобразует поток операций в переключения на выводе на уровне интерфейса. Монитор делает обратное, он преобразует переключения на уровне контактного интерфейса в поток операций. Драйверы используются для управления DUT путем применения воздействий, а в мониторы используются для наблюдения за ответами. Чтобы понять, как создавать и использовать драйверы и мониторов, мы начнем пример с уровня транзакций, который иллюстрирует генератор воздействий (memory master) связанный с управляемой памятью. Управляемая память находится напротив драйвера. В то время как истинный драйвер имеет пин-уровень связи, управляемая память его не имеет. Чтобы управляемая память и драйверы были вместе, их интерфейсы обмена интерфейсов и их архитектуры тоже вместе (объединены). После построения понимания на уровне транзакций, например, мы будем расширять его использование на контактный уровень общения, обмена. В memory master генерирует поток запросов операций(транзакций) и отправляет их через транспортный канал в memory slave. Ведомый обрабатывает каждый запрос и формирует ответ, который он посылает обратно к ведущему через транспортный канал.

Sc g 5.1 p1.png

Во-первых, давайте посмотрим на канал связи из этого примера, то есть через что происходит соединения и передача потоков данных между компонентами. Транспортный канал состоит из двух противоположных FIFO, один для запросов и один для ответов и транспортного интерфейса. Memory master подключается к транспортному интерфейсу канала. Как вы, возможно, помните из раздела 3.4.3, транспортный интерфейс позволяет гарантировать memory master, что запросы и ответы будут синхронизированы. Ведомый интерфейс, как следует из названия, предназначена для устройств, которые должны отвечать на запросы, в то время, транспортный интерфейс для устройств, которые генерируют запросы.

Sc g 5.1 p2.png

Memory master использует свой порт для повторного вызова функции transport(), которая вызывает запрос на посылку запроса в FIFO транспортный канал. transport() также блокируется, пока ответ доступен. Ведомый вызывает функцию get(), которая извлекает запрос, затем обрабатывает его, генерирует ответ, и посылает ответ обратно в FIFO используя put(). Наконец, функция transport(), которая была в состоянии ожидания ответа, теперь может вернуть ответ memory master.

Основной цикл master'a содержит тесты самоконтроля. Это порождает ряд записей в памятью и сохранение тех, кто записывает ссылку в очередь. Затем происходит считывание из всех ячеек памяти только то, что было записано, и сравниваются каждое считанное значение с стоящими в очереди.

62 
for(int i = 0; i < bursts; i++) begin
63 req = new();
64
65 addr = $random & addr_mask;
66 size = ($random & ‘h1f) +1; // size > 0 && size <= 32
67
68 // write loop
69 for(j = 0; j < size; j++) begin
70 req.set_addr(addr);
71 data = $random & data_mask;
72 refq.push_back(data);
73 req.set_wdata(data);
74 req.set_write();
75 req.set_slave_id(0);
76 transport_port.transport(req,rsp);
77 // ignore response
78 addr++;
79 #0;
80 end
81
82 // read loop
83 addr -= size;
84 for(j = 0; j < size; j++) begin
85 req.set_addr(addr);
86 req.set_wdata(0);
87 req.set_read();
88 req.set_slave_id(0);
89 transport_port.transport(req,rsp);
90 data = rsp.get_rdata();
91 refd = refq.pop_front();
92 if(data != refd) begin
93 $sformat(s, “data mismatch: %x != %x”,
94 data, refd);
95 ovm_report_error(“compare”, s);
96 end
97 addr++;
98 #0;
99 end
100 end

Основной цикл состоит из двух под-циклов, цикл записи и чтения цикла. Цикл записи генерирует случайное число для операций записи. Для каждой записи, он генерирует случайное значение данных, которые хранятся как в очереди ссылок (refq) и помещает его в объект запроса. Transport() отправляет запрос в канал запросов и блокируется, пока ответ не доступен. Цикл чтения считывает те же адреса в том же порядке и сравнивает каждое считанное значение со значением в очереди ссылок. Если есть несоответствие, то подается сигнал об ошибке. Основной цикл из memory slave получает каждый запрос, декодирует его, обрабатывает его и выдает ответ.

50 forever begin
51 slave_port.get(req);
52 assert($cast(rsp, req.clone()));
53
54 addr = req.get_addr();
55 if(req.is_read()) begin
56 data = m.read(addr);
57 rsp.set_rdata(data);
58 end
59 else begin
60 data = req.get_wdata();
61 m.write(addr, data);
62 end
63
64 slave_port.put(rsp);
65 #1;
66 end
67 endtask

Обратите внимание, что ведомый использует бесконечный цикл, в то время, как мастер имеет ограниченный цикл. Ведомый не имеет возможности узнать наперед сколько запросов будет обрабатывать. Мастер - тот кто определяет, сколько запросов будет обработано. Чтобы отправить ответ, мы создаем объект ответа, сделав точную копию запроса с помощью функция clone(), а затем если нужно заменяем в ответе поля. Этот тип событий представляет собой ссылки, когда объекты запроса и ответа имеют одинаковые типы, как в данном случае. Простое представление приемник-передатчка как генератор воздействий и драйвер есть общая идиома для OVM testbenches. Самым простым решением является прямая подачи из генератора воздействий, который посылает операций для управления пакетами в драйвере. Более сложные механизмы включают такие вещи, как несколько последовательностей работающих параллельно через (обрабочик событий)sequencer драйвера. Во всех этих случаях идея та же: один или несколько элементов тестбенча порождают операций и, возможно, (имеют возможность) получить ответы из подключенных драйверов. Драйвер преобразует поток транзакций на уровень переключений на контактах.

5.2 Введение в HFPB (Harry Foster peripheral bus)

В этой и следующей главе нескольких главах, мы проиллюстрируем построение тестбенча с использованием простого, не конвейерного протокола шины, который называют протоколом HFPB. HFPB является аббревиатурой, что расшифровывается как Гарри Фостер периферийная шина, которая названа в честь Гарри Фостера, который первый предложил этот формат. Гарри черпал вдохновение для протокола из AMBA ARM-APB протокола. В таблице ниже приводится краткая информация о шине сигналы для нашего простого примера nonpipelined шины.

Name Description
clk All bus transfers occur on the rising edge of clk.
rst An active high bus reset.
sel These signals indicate that a slave has been selected. Each slave has its own select (for example, sel[0] for slave 0). However, for our simple example, we assume a single slave.
en Strobe for active phase of bus.
write When high, write access. When low, read access.
addr[7:0] Address bus.
rdata[7:0] Read data bus driven when write is low.
wdata[7:0] Write data bus driven when write is high.

Эти сигналы связаны между ведущим и ведомым, как показано на следующем рисунке.

Sc g 5.2 p1.png

Протокол работает в трех состояниях, INACTIVE, START, ACTIVE. Отношения и переходы между состояниями показаны на рисунке 5-4.

Sc g 5.2 p2.png

После сброса (то есть, rst==1’b1), шина инициализирует по умолчанию INACTIVE состояние, а значит оба sel и en сняты. Чтобы начать передачу, SEL, выбирается один ведомый компонент. Шина остается в начальном состоянии START только в течение одного тактового цикла и будет переключаться ACTIVE состояние по следующему фронту тактового сигнала. ACTIVE состояние длится всего один такт для передачи данных. Затем шина перейдет обратно в начальное START состояние, если требуется еще одна передача, которая будет показана, когда сигнал выбора остается включенным. Однако, если не требуется еще одной посылки, шина возвращается в неактивное INACTIVE состояние, тогда мастер снимает сигнал выбора sel ведомого и включает сигнал активации шины (enable). Адрес ( addr[7:0]), контроль записи (write), и разрешение передачи (en) должны оставаться неизменными при переходе от START до ACTIVE состоянии. Однако, это не является обязательным требованием, то что эти сигналы должны оставаться неизменными при переходе от ACTIVE состояния назад к START состоянию.

5.2.1 HFPB Операция записи

Рисунок 5-5 иллюстрирует операцию записи для протокола шины HFPB с участием ведущей шины и одного ведомого устройства.

Sc g 5.2 p3.png

На первом такте, так как выбор ведомого (SEL) и разрешение шины (EN) сигналы не установлены, наша шина находится в INACTIVE состоянии, как мы ранее определен в нашей концептуальной машине состояния (см. Рисунок 5-4) и изображено на рисунке 5-5. Переменных состояния на рисунке 5-4 на самом деле является концептуальным изображением состояний шины, а не физической реализацией состояний в дизайне. Первое переключение вызывает START цикл, который инициирует ведущее устройство, включая одну из ведомых линий выбора(sel). Для нашего примера, мастер устанавливает SEL по переднему фронту на втором тактовом интервале. Во время START цикла, мастер посылает правильный адрес на шине и в следующем цикле ставит достоверных данные на шине. Эти данные будут записываться в выбранный ведомый компонент. Передача данных (называемый ACTIVE цикл) на самом деле происходит, когда ведущий устанавливает сигнал разрешения шины(en). В нашем случае его можно обнаружить на третьем тактовом интервале. Адреса, данные и сигналы управления все остается в силе на протяжении ACTIVE цикла. Когда ACTIVE цикл завершается, сигнал разрешения шины(EN) снимается управляющим шиной, и таким образом завершает один текущий цикл операции записи. Когда мастер закончит передачу всех данных в ведомое устройство, он снимет сигнал выбора ведомого устройства (например, SEL). В противном случае, сигнал выбора ведомого устройства остается включенным, и шина возвращается в START состояние цикла, чтобы начать другую операцию записи. Не является обязательным то, что бы значения адреса и данных были неизменны при переходе от ACTIVE цикла назад к START циклу.

Sc g 5.2 p4.png


5.2.1 HFPB Операция чтения

Рисунок 5-6 иллюстрирует HFPB операции чтения с участием устройства управления шиной и ведомым (нулем)(без ведомых). Так же, как и в операции записи, так как на первом такте сигналы выбора ведомого (SEL) и разрешения шины (EN) не установлены, наша шина находится в INACTIVE состоянии, как мы ранее определили в нашей концептуальной state-машине (см. рисунок 5-4). Временная диаграмма сигналов address, write, select, enable та же для операции чтения, как была и для операции записи. В случае чтения, ведомый должен поместить данные на шину для доступа ведущего во время ACTIVE цикла, который на Рисуноке 5-6 показан в третьем тактовом интервале. Как и операция записи, второй операции чтения следующей сразу за первой разрешается чтение от ранее выбранного ведомого устройства. Тем не менее, шина должен всегда возвращаться в START цикл после завершения каждого ACTIVE цикла.

5.3 RTL модель управляемой памяти

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

Sc g 5.3 p1.png

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

forever begin
@(posedge m_bus_if.master.clk) // переключение по тактам
...
case(m_state)                 // описание конечного автомата с использованием case
INACTIVE : begin
...
end
START : begin
...
end
ACTIVE : begin
...
end
endcase
end // forever

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

82 if(!slave_port.try_get(m_req)) begin
83 m_bus_if.master.sel <= 0;
84 m_state = INACTIVE;
85 continue;
86 end
file: 05_testbench_fundamentals/basic_hfpb/hfpb_driver.svh

Обратите внимание, что мы используем try_get () вместо того, чтобы get(). try_get() является неблокирующим вариантом функции get(). Эта функция, и, следовательно, она не может потреблять (останавливать выполнение) время. Если нет ничего в FIFO для получения в том момент когда происходит вызов, try_get() возвращает со статусом 0. Если есть что-то в FIFO, он вернет его вместе с кодом состояния 1. Причина по которой мы используем try_get() вместо get() в том, что шина управляется тактам, и мы хотим убедиться, что !остается дело. Мы не хотим получения нового запроса, который заблокирует шину и, возможно, приведет к неправильному функционированию других устройств, подключенных к шине. Если try_get () не находит элемент в FIFO для получения, то он выполняет холостой цикл. В ACTIVE состоянии после того, как транзакция завершится, мы отправить ответ обратно, используя try_put(). Мы используем try_put(), которая также неблокирующая, по той же причине, что и используем try_get() - потому что мы не хотим заблокировать шину.


139 if(!slave_port.try_put(m_rsp))
140 begin
141 ovm_report_error (“MASTER”,
142 “put response failed”);
143 end
file: 05_testbench_fundamentals/basic_hfpb/hfpb_driver.svh


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

78 START : begin
79
80 m_req = new();
81
82 if (m_bus_if.slave.write)
83 m_req.set_write();
84 else
85 m_req.set_read();
86 m_req.set_wdata(m_bus_if.slave.wdata);
87 m_req.set_addr(m_bus_if.slave.addr);
88 m_req.set_slave_id(id);
89
90 m_transport_channel.transport(m_req, m_rsp);
91
92 if(!m_bus_if.slave.write)
93 m_bus_if.slave.rdata = m_rsp.get_rdata();
94
95 end // START
file: 05_testbench_fundamentals/basic_hfpb/hfpb_slave.svh

Протокол HFPB находится в START состоянии, когда начинается транзакция. Действие HFPB (пин-уровня) slave для извлечения информации из pin-level следующие: определить, какая из частей транзакции находится в процессе, создать объекта запроса, и направить его на уровне транзакций в slave. На уровне транзакций slave делает фактическую обработку запроса и возвращает ответ обратно в контактный уровня slave. Контактный уровня slave затем помещает ответ на на шине после завершения транзакции. Для нашего протокола, только чтение приведет к изменению пинов на slave шины путем размещения данных, считанных с шины slave на уровне транзакций.


5.4 Мониторы и Analysis порты

Чтобы ответить на does-it-work? и are-we-done? вопросы, вы должны наблюдать за поведением DUT'ов. Вам необходимо получить информацию о поведении тестируемого устройства и передать его в анализирующее устройства, которому поручено отвечать на соответствующие вопросы. Основной способ сделать это в OVM является использование специальных портов для анализа analysis ports. Analysis ports образуют границу между оперативными областями и областями анализа в тестбенче. Областями анализа является набор компонентов в тестбенче, которые отвечают за анализ поведения, наблюдая через мониторы. Анализирующие компоненты получают данные из analysis ports. Монитор передает операций через analysis ports в analysis компоненту. Аnalysis ports и analysis компоненты вместе представляют собой модель наблюдения, известную, как объектно-ориентированная модель. В этой модели, издатель посылает данные и абонент принимает данные. Как и в случае с подпиской на журнал, каждому абоненту необходимо подписаться на издателя, прежде чем он может принимать данные. Данные передаются абонентам только тогда, когда издатель издает что-то. Кроме того, как и в случае с подпиской на журнал, каждый абонент получает дескриптор(заголовок,перечень) данных от издателя. На следующем рисунке показана организация элементов в analysis port:

Sc g 5.4 p1.png

Издатель ведет список подписчиков. В какой-то момент во время его работы, устройство, которое содержит analysis port, такой как монитор, называет write(), и переходит в transaction объект. Analysis port направляет вызов записи к каждому абоненту, и передает копию объекта транзакции к абоненту. Analysis components (абоненты) подключаются к портам анализа через интерфейс анализа(analysis interface). analysis interface содержит одну функцию write().

write() is a function in SystemVerilog (not a task); therefore, it never blocks. Imagine what can happen if write() were a blocking task instead. In that case, it could interfere with the operation of the monitor. Since subscribers must receive data in the same delta cycle that the write() call is made, write() must be nonblocking. Some analysis components may deliver more than one transaction through an analysis port in a single delta cycle. write() must return immediately, but the subscriber may do anything, including consume time. The consequence is that data can be lost if a subscriber is not prepared to deal with multiple transactions in one delta cycle. In this case, you can use an analysis FIFO to serve as a FIFO buffer between the analysis port and the analysis component. An analysis FIFO is an unbounded tlm_fifo with an analysis interface, that is, write(). Since the analysis_fifo is unbounded, write() will always be successful. The analysis component then, instead of having an analysis interface, connects to the analysis FIFO in the same way any component connects to a FIFO. It uses get() or try_get() to retrieve transactions. Of course, you can also design an analysis component that includes an analysis FIFO internally and makes the FIFO’s analysis export visible as its own. The monitor for our HFPB protocol uses the same skeleton as the driver and the slave. Through the exercise of the state machine, it is able to recognize bus transactions. As each transaction is recognized, it is sent to the analysis port using the write() call.

write() является функцией в SystemVerilog (не задачей), поэтому она никогда не блокируется. Представьте себе, что может произойти, если write() была бы блокирующей задачей вместо этого. В этом случае, она может повлиять на работу монитора. Так как абоненты должны получать данные в том же дельта цикле, где сделан вызов write(), write() должен быть не блокирующими. Некоторые analysis components может доставить больше, чем одну транзакцию через analysis port в одном цикле дельта. write() должен вернуть немедленно, но абонент может делать что угодно, в том числе потреблять время(работать во времени, по тактам). Следствием этого является то, что данные могут быть потеряны, если абонент не готовы иметь дело с несколькими операций в одном цикле дельта. В этом случае, вы можете использовать analysis FIFO, чтобы служить буфером FIFO между analysis port и analysis components. Analysis FIFO является неограниченным наследником tlm_fifo с analysis interface, то есть с функцией write(). Так как analysis_fifo неограниченным наследником, write() всегда будет выполняться успешно. Analysis components следовательно, так как содержит внутри analysis interface, подключается к analysis FIFO таким же образом, как и любой компонент, подключается к FIFO. Он использует функции get() или try_get() для получения транзакций. Конечно, вы также можете создавать analysis components, который включает в себя analysis FIFO внутри, и делать analysis export FIFO как свои собственные(во внутренней области видимости, как с локальной переменной обьекта).

Монитор для нашего протокола HFPB использует туже основу, как как драйвер и slave. Через выполнение состояний конечного автомата, можно распознавать транзакции на шине. Поскольку каждая транзакция распознается, конечный автомат отправляет вызов на использование команды write() в analysis port.


54 forever begin
55 @( posedge m_bus_if.monitor.clk );
56
57 if (m_bus_if.monitor.rst) continue;
58
59 state = state_t’({ (m_bus_if.monitor.sel != 0),
60 m_bus_if.monitor.en });
61 case( state )
62
63 INACTIVE : begin
64 end
65
66 START : begin
67 id = 0;
68 for(id = 0; id < 8; id++)
69 if(m_bus_if.monitor.sel[id])
70 break;
71 if(id >= 8)
72 id = 7;
73
74 m_trans = new();
75
76 m_trans.set_addr(m_bus_if.monitor.addr);
77 m_trans.set_wdata(m_bus_if.monitor.wdata);
78 m_trans.set_slave_id(id);
79
80 if (!m_bus_if.monitor.write)
81 continue;
82
83 m_trans.set_write();
84 analysis_port.write(m_trans);
85 ovm_report_info(“MONITOR”, m_trans.do_sprint());
86
87 end
88
89 ACTIVE : begin
90 if (m_bus_if.monitor.write)
91 continue;
92
93 m_trans.set_read();
94 m_trans.set_rdata(m_bus_if.monitor.rdata);
95 analysis_port.write(m_trans);
96 ovm_report_info(“MONITOR”, m_trans.do_sprint());
97 end
98
99 endcase
100
101 end
file: 05_testbench_fundamentals/basic_hfpb/hfpb_monitor.svh

Любой абонент, подключенный к analysis port получит транзакцию и использует ее для своих целей.

5.5 Выводы

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