Вы находитесь на странице: 1из 30

3.

Создание интерфейсных UVC


Для реализации процесса генерирования тестовых данных в UVM определены следующие
классы:
1. Sequence item. (Данные, представляющие транзакцию)
2. Sequence (Последовательность транзакций)
3. Sequencer (Управляет последовательностью или последовательностями, запускает
генерирование данных)
4. Driver (Получает данные от секвенсора и передает их в DUT)
(cм более детальное описание классов в лк 1 по UVM)
Преимущества использования UVM-последовательностей (Sequence)
● Последовательность допускает возможность повторного использования.
● Генерирование тестовых наборов независимо от testbench.
● Легкость в управление генерированием транзакций.
● Последовательности могут соединены последовательно или иерархически.

Моделирование данных - класс транзакций


Класс транзакции создается на основе uvm_sequence_item. Листинг 3.1 и 3.2 представляют
создание класса с ограничениями для формирования значений.

Листинг 3.1 APB Transfer


typedef enum bit {APB_READ, APB_WRITE} apb_direction_enum;
class apb_transfer extends uvm_sequence_item;
rand bit[31:0] addr;
rand bit [31:0] data;
rand apb_direction_enum direction;
//Управляющее поле, не передается на уровень сигналов
rand int unsigned transmit_delay;

constraint c_delay {transmit_delay inside {[0:100]};}


constraint c_addr {addr[1:0] == 2'b00;}

`uvm_object_utils_begin(apb_transfer)
`uvm_field_int(addr, UVM_DEFAULT)
`uvm_field_int(data, UVM_DEFAULT)
`uvm_field_enum(apb_direction_enum, direction, UVM_DEFAULT)
`uvm_field_int(transmit_delay, UVM_DEFAULT|UVM_NOCOMPARE|UVM_NOPACK)
`uvm_object_utils_end

function new(string name="apb_transfer");


super.new(name);
endfunction: new
endclass: apb_transfer

Определение управляющих полей


Пример листинга 3.1 дополняется типом данных apb_dly_enum, который позволяет задавать
различный диапазон значений для поля transmit_delay.
Листинг 3.2 - Введение дополнительного управления формированием значений полей
typedef enum bit {APB_READ, APB_WRITE} apb_direction_enum;
typedef enum {ZERO, SHORT, MEDIUM, LONG, MAX} apb_dly_enum;
class apb_transfer extends uvm_sequence_item;
rand bit[31:0] addr;
rand bit [31:0] data;
rand apb_direction_enum direction;
//Control field- does not translate into signal data
rand apb_dly_enum delay_kind;
rand int unsigned transmit_delay;

constraint c_addr_valid {addr[1:0] == 2'b00;}


constraint c_delay { solve delay_kind before transmit_delay;
transmit_delay >= 0; transmit_delay <= 100;
(delay_kind == ZERO) -> transmit_delay == 0;
(delay_kind == SHORT) -> transmit_delay inside {[1:10]};
(delay_kind == MEDIUM) -> transmit_delay inside {[11:29]};
(delay_kind == LONG) -> transmit_delay inside {[30:100]};
(delay_kind == MAX) -> transmit_delay == 100;
}
constraint c_delay_kind_dist {delay_kind dist {ZERO:=2, SHORT:=2,
MEDIUM:=1, LONG:=1, MAX:=2};}

`uvm_object_utils_begin(apb_transfer)
`uvm_field_int(addr, UVM_DEFAULT)
`uvm_field_int(data, UVM_DEFAULT)
`uvm_field_enum(apb_direction_enum, direction, UVM_DEFAULT)
`uvm_field_enum(apb_dly_enum, delay_kind, UVM_DEFAULT | UVM_NOCOMPARE |
UVM_NOPACK)
`uvm_field_int(transmit_delay, UVM_DEFAULT | UVM_NOCOMPARE | UVM_NOPACK)
`uvm_object_utils_end

1
function new(string name="apb_transfer");
super.new(name);
endfunction: new
endclass: apb_transfer

Наследования и слои ограничений


Ограничения можно создавать в классах наследниках
class short_delay_transfer extends apb_transfer
constraint c_short_delay {transmit_delay <= 3;}
endclass: short_delay_transfer

Простой тест для данных


Листинг 3.3 Test for the apb_transfer Data Item
module automation_example;
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "apb_transfer.sv"

apb_transfer my_xfer;

initial begin
my_xfer = apb_transfer::type_id::create("my_xfer");
repeat(3) begin
if(!my_xfer.randomize())
`uvm_fatal("RNADFALL", "cant not randomaze my_xfer")
my_xfer.print();
my_xfer.print(uvm_default_tree_printer);
end
repeat(3) begin
if(!my_xfer.randomize() with {addr inside {['h0000:'hFFFF]};
direction == APB_WRITE;})
`uvm_fatal("RNADFALL", "cant not randomaze my_xfer")
my_xfer.print();
my_xfer.print(uvm_default_tree_printer);
end
end

2
endmodule

Transaction-Level Components
Минимальная архитектура среды верификации представлена на рис

Рис 10—Simplified Transaction-Level Testbench

Создание драйвера
Разработка пользовательского класса драйвера включает следующие шаги
a) Создается на основе класса uvm_driver
b) При необходимости можно добавить UVM-макрос для регистрации фабрики и поддержки
методов класса print, copy, compare и тд.(см лекцию UVM1).
c) В драйвере указывается виртуальный интерфейс для подключения к DUT.
d) Получение данных от секвенсора и передача их в DUT.
Листинг 3.4 представляет пример создания пользовательского класса apb_master_driver для
работы с интерфейсом APB.
Листинг 4 APB Master Driver Definition

3
class apb_master_driver extends uvm_driver #(apb_transfer);
// виртуальный интерфейс используется для управления и наблюдения за HDL сигналами
virtual apb_if vif;
// UVM automation macros для основных компонентов
`uvm_component_utils(apb_master_driver)
// Конструктор
function new (string name = "apb_master_driver", uvm_component parent);
super.new(name, parent);
endfunction : new

function void build_phase(uvm_phase phase);


string inst_name;
super.build_phase(phase);
if(!uvm_config_db#(virtual apb_if)::get(this,"","vif",vif))
`uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(),".vif"});
endfunction : build_phase
// Дополнительные методы класса
extern virtual task run_phase(uvm_phase phase);
extern virtual protected task get_and_drive();
extern virtual protected task reset_signals();
extern virtual protected task drive_transfer(apb_transfer trans);
endclass

task apb_master_driver::run_phase(uvm_phase phase);


fork
get_and_drive();
reset_signals()
join
endtask : run_phase

task apb_master_driver::get_and_drive();
forever begin
// Получение данных от секвенсора (может блокировать).
seq_item_port.get_next_item(req);
// Передача транзакции
drive_transfer(req);
seq_item_port.item_done(); // Уничтожение запроса
end

4
endtask : get_and_drive

task apb_master_driver::drive_transfer(apb_transfer trans);


... // Add your logic here.
endtask : drive_transfer

Вызов get_next_item() запрашивает и получает следующую транзакцию от секвенсора. Оператор


seq_item_port.item_done(); сообщает секвенсору, что обработка текущих данных завершена.
Задача drive_transfer содержит пользовательскую логику для передачи данных в DUT.

Uvm_config_db это расположенная на верхнем уровне база данных ресурсов, которая


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

static function bit get( uvm_component cntxt, string inst_name, string field_name, inout T value)
static function void set(uvm_component cntxt, string inst_name, string field_name, T value)
static function bit exists(uvm_component cntxt, string inst_name, string field_name, bit spell_chk =)
static task wait_modified(uvm_component cntxt,string inst_name,string field_name)

SystemVerilog Interface and Virtual Interface


(Тема интерфейсов и виртуальных интерфейсов рассматривалась ранее в лекциях по
SystemVerilog)
Пример представляет описание интерфейса для APB шины.
Листинг 5 Описание интерфейса для APB протокола
interface apb_if(input pclock, input preset);
// Сигналы шины APB
logic [31:0] paddr;

5
logic prwd;
logic [31:0] pwdata;
logic penable;
logic [15:0] psel;
logic [31:0] prdata;
logic pslverr;
logic pready;

// Флаги управления
bit has_checks = 1;
bit has_coverage = 1;

endinterface: apb_if

Реализация функции drive_transfer


task apb_master_driver::drive_transfer(apb_transfer trans);
int slave_indx;
if (trans.transmit_delay > 0)
repeat(trans.transmit_delay) @(posedge vif.pclock);

//Управление фазой адреса


slave_indx = cfg.get_slave_psel_by_addr(trans.addr);
vif.paddr <= trans.addr;
vif.psel <= (1<<slave_indx);
vif.penable <= 0;
if (trans.direction == APB_READ)
vif.prwd <=0;
else begin //APB_WRITE
vif.prwd <= 1'b1;
vif.pwdata <= trans.data;
end
// Фаза данных - считывание данных, если выполняется режим APB_READ
@(posedge vif.pclock)
vif.penable <= 1;
@(posedge vif.pclock);
if (trans.direction == APB_READ)
trans.data = vif.prdata;
vif.penable <= 0;

6
vif.psel <= 0;

endtask : drive_transfer

Соединение драйвера и секвесора

Рис Sequencer-Driver Interface

● Совместный контроль за тестовыми наборами


● Устранение неопределенностей
● Возврат значений от драйвера генератору
● Пре-генерирование
● Генерирование транзакций индивидуально или в указанном порядке

Библиотека классов UVMy предлагает базовый класс uvm_sequencer, в который в качестве


параметров получает типы данных для request и response и содержит все необходимые
функциональности для связи с драйвером.
Драйвер и секвенсор соединяются через tlm-порты, при этом порт seq_item_port драйвера
подключается к порту seq_item_export секвенсора. Компонент, который будет содержать
драйвер и секвенсор должен реализовывать соединение между портами компонентов.
Базовое взаимодействие драйвера и секвенсора осуществляется функциями get_next_item() и
item_done(). Первая функция используется драйвером для захвата следующих для передачи
данных, затем с помощью функции item_done() драйвер сигнализирует секвернсору, что
данные переданы. Типичный базовый цикл в драйвере выглядит
forever begin

7
get_next_item(req);
// Передача данных в соответствии с протоколом
item_done();
end
Функция get_next_item() - блокирующая, кроме нее драйвер имеет функцию try_get_itam(),
которая возвращает управление в точку вызова, даже если транзакция для передачи не была
сформирована.
task run_phase(uvm_phase phase);
forever begin
// Попытка получить следующие данные от секвенсора
seq_item_port.try_next_item(s_item);
if (s_item == null) begin
// Нет данных для выполнения, посылается пустая транзакция

end
else begin
// Данные от секвенсора получены, они передаются

// Сообщение секвенсору, что данные транзакции переданы
seq_item_port.item_done();
end
end
endtask: run

Вернуть данные секвенсору драйверу может через метод item_done(),


seq_item_port.item_done(rsp);
или с помощью put_response(),
seq_item_port.put_response(rsp);
или через встроенный в uvm_driver метод порта analysis port.
rsp_port.write(rsp);

Порт seq_item_port двунаправленный, он имеет также стандартные TLM методы get() и peek()
для запроса данных от секвенсора, и метод put для формирования ответа.
Листинг 7 APB Master Sequencer
class apb_master_sequencer extends uvm_sequencer #(apb_transfer);

// UVM -макрос для секвенсоров


`uvm_sequencer_utils(apb_master_sequencer)

8
//Constructor - required UVM Syntax
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass: apb_master_sequencer

Пример * Простейший драйвер для конвейерного протокола


class simple_driver extends uvm_driver #(simple_transfer);
simple_transfer req_list[$];
int max_pipelint_depth=4;
//утилита фабрики для построения компонента, конструктор
...
taskrun_phase(uvm_phase phase);
fork
get_and_drive();
drive_transfers();
Join
endtask: run
//Захват данных от секвенсора для заполнения конвейера
task get_and_dtive();
while (req_list.size() < max_pipeline_depth) begin
seq_item_port.get_next_item(req);
$cast(rsp, req.clone());
rsp.set_id_info(req);
drive_transfer(rsp);
seq_item_port.item_done(rsp);
End
endtask: get_and_drive
// Обработка данных из req_list и их передача согласно протоколу
task drive_transfer();
simple_transfer cur_req;
while (1) begin
wait (req_list.size() > 0);
cur_req = req_list.pop_back();
send_to_dut(cur_req);
rsp_port.write(cur_req());
end
endtask: drive_tansfer

9
task send_to_dut(simple_transfer trans);
//логика для управления передачей нескольких данных транзакции
endtask : send_to_dut
ensclass :simple_driver

Creating Monitor
Листинг представляет пример класса монитора.
Листинг Monitor
class master_monitor extends uvm_monitor;
virtual apb_if xmi; // SystemVerilog-виртуальный интерфейс
bit checks_enable = 1; // Управление выполнением проверки в мониторе и интерфейсе.
bit coverage_enable = 1; // Управление работой coverage в мониторе и интерфейсе.
uvm_analysis_port #(apb_transfer) item_collected_port;

event cov_transaction; // Событие, необходимое для запуска covergroup

protected apb_transfer trans_collected;


`uvm_component_utils_begin(master_monitor)
`uvm_field_int(checks_enable, UVM_ALL_ON)
`uvm_field_int(coverage_enable, UVM_ALL_ON)
`uvm_component_utils_end

covergroup cov_trans @cov_transaction;


option.per_instance = 1;
TRANS_ADDR: coverpoint trans_collected.addr { // Определение корзин покрытия
bins ZERO = {0};
bins NON_ZERO = {[1:8'h7f]};}
TRANS_DIRECTION : coverpoint trans_collected.direction;
TRANS_ADDR_X_TRANS_DIRECTION : cross TRANS_ADDR, TRANS_DIRECTION;
endgroup : cov_trans

function new (string name, uvm_component parent);


super.new(name, parent);
cov_trans = new();
cov_trans.set_inst_name({get_full_name(), ".cov_trans"});
trans_collected = new();
item_collected_port = new("item_collected_port", this);

10
endfunction : new

virtual task run_phase(uvm_phase phase);


collect_transactions(); // сбор транзакций
endtask : run_phase

virtual protected task collect_transactions();


forever begin
@(posedge xmi.pclock);
// ...// Сбор данных с шины в trans_collected
if (checks_enable) perform_transfer_checks();
if (coverage_enable) perform_transfer_coverage();
item_collected_port.write(trans_collected);
end
endtask : collect_transactions

virtual protected function void perform_transfer_coverage();


-> cov_transaction;
endfunction : perform_transfer_coverage

virtual protected function void perform_transfer_checks();


//... // Проверка данных из trans_collected.
endfunction : perform_transfer_checks
endclass : master_monitor

Создание агента
Рисунок представляет структуру агента в UVM. Листинг содержит пример кода для
пользовательского класса агента apb_master_agent.

11
Рис Agent
Листинг Описание класса агента для шины apb
class apb_master_agent extends uvm_agent;
uvm_active_passive_enum is_active = UVM_ACTIVE;
// uvm_sequencer #(simple_item) sequencer;
apb_master_sequencer sequencer;
apb_master_driver driver;
master_monitor monitor;

// Конструктор и макросы UVM automation macros


`uvm_component_utils_begin(apb_master_agent)
`uvm_field_enum(uvm_active_passive_enum, is_active, UVM_DEFAULT)
`uvm_component_utils_end

function new (string name, uvm_component parent);


super.new(name, parent);
endfunction : new

// Создание во время фазы build_phase объектов компонентов, входящих агента


virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);

12
monitor = master_monitor::type_id::create("monitor",this);
if (is_active == UVM_ACTIVE) begin
// Создание объекта секвесора и драйвера
sequencer =
apb_master_sequencer#(simple_item)::type_id::create("sequencer",this);
driver = apb_master_driver::type_id::create("driver",this);
end
endfunction : build_phase

virtual function void connect_phase(uvm_phase phase);


if(is_active == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction : connect_phase
endclass : apb_master_agent

Создание среды верификации


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

13
Рис Typical UVM Environment Architecture

Листинг Пример создания класса среды верификации.


class apb_env extends uvm_env;
apb_config cfg;// класс конфигурации для среды
apb_master_agent master; // APB master (bridge)
apb_slave_agent slaves[]; // APB может иметь несколько агентов slave
apb_bus_monitor bus_monitor; // общий монитор шины

`uvm_component_utils_begin(apb_env)
`uvm_filed_object(cfg, UVM_DEFAULT)
`uvm_component_utils_end

function new(string name, uvm_component parent);


super.new(name, parent);
endfunction : new
// Дополнительные методы класса

14
extern virtual function void build();
extern virtual function void assign_vi(virtual apb_if_vif);
endclass : apb_env

function void apb_env::build


uvm_object config_obj;
super.build();
if (cfg == null) begin
`uvm_info(get_type_name(), “Using default_apb_config”, UVM_MEDIUM)
$cast(cfg, factory.create_object_by_name(“default_apb_config”, “cfg”));
end
if (cfg.has_bus_monitor) begin
bus_monitor = apb_bus_monitor::type_id::create(“bus_monitor”, this)
bus_monitor.cfg = cfg;
end
master = apb_master_agent::type_id::create(“master”, this);
master.cfg =cfg;
// Build slaves
slaves = new[cfg.slave_configs.size()];
// Create multiple slaves and give each a unique name
foreach slaves[i] begin
slave[i] = ahb_slave_agent::type_id::create($psprintf(“slave[%0d]”, i), this);
slave[i].cfg = cfg.slave_configs[i];
end
endfunction :: build
function void apb_env::assign_vi(virtual apb_if vif);
// Based on the configuration, assign master, slave and bus monitor signals
endfunction :: assign_vi

Контрольные вопросы и задания


1. Какие UVM-классы участвуют в генерировании транзации?
2. Какие существуют разновидности агентов?
3. Для каких блоков агент служит оболочкой?
4. Какой класс описывает транзакцию?
5. Какой класс передает тестовые наборы в DUT?
6. Через какой механизм драйвер подключается к DUT?
7. Какой класс определяет порядок формирования последовательностей?
8. Какой класс выполняет генерирование последовательностей?
9. Какие порты используются для передачи данных между секвесором и драйвером?

15
10. Какие классы непосредственно составляют среду верификации в UVM?

16
17
18
19
20
21
22
23
24
25
26
27
28
29