2.7 sequence
前言
本文以uvm-1.2/examples/simple/sequence/basic_read_write_sequence为例,通过代码了解UVM中的一些用法。通过这个例子可以基本了解以下知识点:
- 什么是sequence
- 如何启动带参数req和rsp的sequence
- 如何编写带参数req和rsp的driver
- 两种不同方法注册类变量
- 类中静态变量的使用方法
- 带参数的类如何注册到UVM工厂中
一、基本介绍
首先介绍一下什么是sequence。在张强的《UVM实战》中将UVM的环境和枪支做了一个形象的类比,sequence相当于子弹,sequencer相当于弹夹,而driver和env则相当于枪身。因此,sequence是仿真过程中的实际激励,只是需要借助于sequencer,diriver和env将激励注入待测设计DUT中。
在这个示例中,例化了带参数req和rsp的类,分别是sequence和driver,并将这两个参数话的类,注册到UVM的工厂中。
另外,通过宏定义USE_FIELD_MACROS,展示了两种不同的方法处理类变量,通过对比实现代码以及log,可以看出调用UVM的field机制,处理类变量的方法,代码实现更加简单,打印的log信息也更加的详细,所以,在实际搭建UVM的验证环境的过程中,推荐使用UVM的field机制来处理类变量。
二、代码分析
1.top.sv
`include "uvm_macros.svh"
`define NUM_SEQS 1
`define NUM_LOOPS 5
// NOTE: a simple convenience shortcut to illustrate two different paths
// generating a string representatin for an object
`define toSTRING(X) \
`ifdef USE_FIELD_MACROS \
X.sprint() \
`else \
X.convert2string() \
`endif
/*
Remark 1:
- this example shows two alternative approaches to handle local properties -
When the define USE_FIELD_MACROS is set, the code is utilizing the uvm_field_xxx macros
to mark transaction fields as "uvm fields". Doing so automatically infers code which handles
compare,print,copy,record,pack,unpack without any additional implementation from the user side.
This is typically shorter to write and easier to maintain on the user side. On the other hand a generic
implementation might not be as fast and custom tailored.
The two styles can be mixed for most of the uvm_field functionality (manually handling few fields and
use a default implementation for some others). Only the component level auto configuration for fields is for
`uvm_field_* annotated members only. it means that only when the uvm_field_* macros are used these fields will be
set to their configured values automatically.
Remark 2:
All classes in this example utilize the uvm_(object|component)[_param]_utils macro. Althrough its not mandatory
for UVM it is good practice since this macro will register the class in the UVM factory automatically. See the user
guide for more information regarding uvm factory use models
*/
package user_pkg;
import uvm_pkg::*;
typedef enum { BUS_READ, BUS_WRITE } bus_op_t;
typedef enum { STATUS_OK, STATUS_NOT_OK } status_t;
//--------------------------------------------------------------------
// bus_trans
//--------------------------------------------------------------------
class bus_trans extends uvm_sequence_item;
bit [11:0] addr;
bit [7:0] data;
bus_op_t op;
`ifdef USE_FIELD_MACROS
`uvm_object_utils_begin(bus_trans)
`uvm_field_int(addr,UVM_DEFAULT)
`uvm_field_int(data,UVM_DEFAULT)
`uvm_field_enum(bus_op_t,op,UVM_DEFAULT)
`uvm_field_utils_end
`else
`uvm_object_utils(bus_trans)
virtual function void do_copy (uvm_object rhs);
bus_trans rhs_;
if(!$cast(rhs_, rhs))
`uvm_error("do_copy", "cast failed, check type compatability")
super.do_copy(rhs);
addr = rhs_.addr;
data = rhs_.data;
op = rhs_.op;
endfunction
virtual function bit do_compare(uvm_object rhs,uvm_comparer comparer);
bus_trans rhs_;
if(!$cast(rhs_, rhs))
`uvm_fatal("do_compare", "cast failed, check type compatability")
return ((op == rhs_.op) && (addr == rhs_.addr) && (data == rhs_.data));
endfunction
virtual function string convert2string();
return $sformatf("op %s: addr=%03x, data=%02x", op.name(), addr, data);
endfunction
// NOTE: in contrast to the USE_FIELD_MACROS version this doesnt implement pack/unpack/print/record/...
`endif
function new(string name="");
super.new(name);
endfunction
endclass
//--------------------------------------------------------------------
// bus_req
//--------------------------------------------------------------------
class bus_req extends bus_trans;
`uvm_object_utils(bus_req)
function new(string name="");
super.new(name);
endfunction
endclass
//--------------------------------------------------------------------
// bus_rsp
//--------------------------------------------------------------------
class bus_rsp extends bus_trans;
status_t status;
function new(string name="");
super.new(name);
endfunction
`ifdef USE_FIELD_MACROS
`uvm_object_utils_begin(bus_rsp)
`uvm_field_enum(status_t,status,UVM_DEFAULT)
`uvm_object_utils_end
`else
`uvm_object_utils(bus_rsp)
virtual function void do_copy (uvm_object rhs);
bus_rsp rhs_;
if(!$cast(rhs_, rhs))
`uvm_fatal("do_copy", "cast failed, check type compatability")
super.do_copy(rhs_);
status = rhs_.status;
endfunction
virtual function string convert2string();
return $sformatf("op %s, status=%s", super.convert2string(), status.name());
endfunction
// NOTE: in contrast to the USE_FIELD_MACROS version this doesnt implement pack/unpack/print/record/...
`endif
endclass
class my_driver #(type REQ = uvm_sequence_item,
type RSP = uvm_sequence_item) extends uvm_driver #(REQ, RSP);
`uvm_component_param_utils(my_driver#(REQ,RSP))
local int data_array[511:0];
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual task run_phase(uvm_phase phase);
REQ req;
RSP rsp;
forever begin
seq_item_port.get(req);
rsp = new();
rsp.set_id_info(req);
// Actually do the read or write here
if (req.op == BUS_READ) begin
rsp.addr = req.addr[8:0];
rsp.data = data_array[rsp.addr];
`uvm_info("sending",`toSTRING(rsp),UVM_MEDIUM)
end else begin
data_array[req.addr[8:0]] = req.data;
`uvm_info("sending",`toSTRING(req),UVM_MEDIUM)
end
seq_item_port.put(rsp);
end
endtask
endclass
class sequenceA #(type REQ = uvm_sequence_item,
type RSP = uvm_sequence_item) extends uvm_sequence #(REQ, RSP);
`uvm_object_param_utils(sequenceA#(REQ,RSP))
local static integer g_my_id = 1;
local integer my_id;
function new(string name="");
super.new(name);
my_id = g_my_id++;
endfunction
virtual task body();
REQ req;
RSP rsp;
`uvm_info("sequenceA", "Starting sequence", UVM_MEDIUM)
for(int unsigned i = 0; i < `NUM_LOOPS; i++) begin
`uvm_create(req)
req.addr = (my_id * `NUM_LOOPS) + i;
req.data = my_id + i + 55;
req.op = BUS_WRITE;
`uvm_send(req)
get_response(rsp);
`uvm_create(req)
req.addr = (my_id * `NUM_LOOPS) + i;
req.data = 0;
req.op = BUS_READ;
`uvm_send(req)
get_response(rsp);
if (rsp.data != (my_id + i + 55)) begin
`uvm_error("SequenceA", $sformatf("Error, addr: %0d, expected data: %0d, actual data: %0d",
req.addr, req.data, rsp.data))
end
end
`uvm_info("sequenceA", "Finishing sequence", UVM_MEDIUM)
endtask // body
endclass
class env extends uvm_env;
`uvm_component_utils(env)
local uvm_sequencer #(bus_req, bus_rsp) sqr;
local my_driver #(bus_req, bus_rsp) drv ;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
sqr = new("sequence_controller", this);
// create and connect driver
drv = new("my_driver", this);
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
virtual task run_phase(uvm_phase phase);
phase.raise_objection(this);
for (int i = 0; i < `NUM_SEQS; i++) begin
fork begin
sequenceA #(bus_req, bus_rsp) the_sequence= new("sequence");
the_sequence.start(sqr, null);
end
join_none
end
wait fork;
phase.drop_objection(this);
endtask
endclass
endpackage
module top;
import user_pkg::*;
import uvm_pkg::*;
env e;
initial begin
`uvm_info("top","In top initial block",UVM_MEDIUM)
e = new("env", null);
run_test();
end
endmodule
第9到14行,通过宏定义的方式,声明了一个函数toSTRING(),如果定义了宏USE_FIELD_MACROS,则调用UVM的field机制自带的函数sprint(),否则调用自己定义的函数convert2string()。
第42和262行,通过package关键字,将需要用到的一些组件进行打包。
第46和47行,定义了两个枚举类型,分别反应总线的状态以及响应的状态。
第52到100行,通过类封装了一个总线传输的包bus_trans,包中的变量包括地址addr,数据data,以及总线的读写传输状态op。第58到66行,如果定义了宏USE_FIELD_MACROS,则调用UVM的field宏,将变量注册到UVM工厂中;否则,第68到95行,需要自己去写一些处理变量的函数。最后,第97到99行,是bus_trans这个类的构造函数。
第105到110行,定义了从bus_trans继承过来类bus_req。首先,调用了宏`uvm_object_utils(bus_req),将bus_req类注册到UVM的工厂中,其次,声明了一下bus_req这个类的构造函数。
第115到145行,定义了从bus_trans继承过来类bus_rsp。
首先,定义了一个枚举变量status,用于反应响应的状态;
其次,声明了这个类的构造函数;
最后,如果定义了宏USE_FIELD_MACROS,则将变量status注册到UVM的field机制中,否则,重新编写处理变量的函数。注意,这里处理的变量的函数名和基类的函数名一致,但不会冲突,这是因为这些函数前面添加了virtual关键字,子类的同名函数会替换基类的函数。
第147到179行,定义了带两个参数REQ和RSP的my_driver类,继承自UVM中的uvm_driver类。
首先,调用了`uvm_component_param_utils(my_driver#(REQ,RSP)),将my_dirver类注册到UVM的工厂中,注意这里使用到的宏与前面bus_trans注册用到的宏不一致,这是因为他们继承自不同的父类,bus_trans是从uvm_sequence_item继承过来的,所以,注册时调用的是宏uvm_object_utils,而my_dirver是从uvm_driver继承过来的,uvm_driver来自uvm_component,所以,注册时要用宏uvm_component_param_utils;
其次,定义了一个较大的本地数组,用于存储读写的数据;然后,定义了这个类的构造函数;
最后,在run_phase函数中不断循环,获取请求,并处理请求,再将响应返回回去,其中,由于rsp在164行新建,不包含任何信息,所以,需要第165行将req的ID信息复制到rsp中,这样在176行将响应返回回去的时候才能够和请求匹配上。另外,168到175行是对数据的读写处理,并打印相关的信息。
第183到228行,定义了带两个参数REQ和RSP的sequenceA类,继承自UVM中的uvm_sequence类。
首先,由于sequenceA是带参数的类,所以调用`uvm_object_param_utils(sequenceA#(REQ,RSP))将sequenceA注册到UVM的工厂中;
其次,定义了两个变量,其中变量g_my_id是静态变量,也就是所有例化的sequenceA类,共用这一个变量,这个变量一旦声明就分配了存储空间,每个例化的sequenceA都可以去改变它的值,其中,191到194行定义了构造函数,这中间每例化一次,静态变量就自加一次,并将结果保存给my_id。
最后,在body任务中,将激励下发下去,在for循环中,先写一个与my_id相关的数据下去,再将数据读上来,与之前写下去的数据进行比对。
第230到261行,定义了env类。231行将env类注册到UVM的工厂中,232和233行分别声明了sequencer和driver,325到237行是构造函数,239到246行在build_phase中例化sequencer和driver,并将其进行了连接,248到259行在run_phase中进行“举手”和“放手”,利用for循环将sequence中的激励下发,for循环内部采用fork——join_none并行连接,这种并行连接方式类似于非阻塞,不等前面的执行完成,后面的会继续发送。
第264到274行,定义了top这个module。265和266行利用关键字import,导入了uvm的包以及前面我们自己封装的包,267行声明了env,269到273行,在initial中例化了env,并通过run_test()启动仿真。
2.仿真结果
编译的命令如下,可以通过在编译命令中,加入+define+USE_FIELD_MACROS这个宏定义,来选择是否调用UVM的field机制。
comp:
$(VCS) +incdir+. top.sv +define+USE_FIELD_MACROS
在Makefile.vcs中,加入+define+USE_FIELD_MACROS宏定义,调用的是UVM的field机制,仿真的log如下所示:
UVM_INFO top.sv(289) @ 0: reporter [top] In top initial block
UVM_INFO @ 0: reporter [RNTST] Running test ...
UVM_INFO top.sv(220) @ 0: env.sequence_controller@@sequence [sequenceA] Starting sequence
UVM_INFO top.sv(194) @ 0: env.my_driver [sending] -------------------------------------------------------------------------------
Name Type Size Value
-------------------------------------------------------------------------------
req bus_req - @543
addr integral 12 'h5
data integral 8 'h38
op bus_op_t 32 BUS_WRITE
begin_time time 64 0
depth int 32 'd2
parent sequence (name) string 8 sequence
parent sequence (full name) string 32 env.sequence_controller.sequence
sequencer string 23 env.sequence_controller
-------------------------------------------------------------------------------
UVM_INFO top.sv(191) @ 0: env.my_driver [sending] ------------------------------------
Name Type Size Value
------------------------------------
<unnamed> bus_rsp - @561
addr integral 12 'h5
data integral 8 'h38
op bus_op_t 32 BUS_READ
status status_t 32 STATUS_OK
------------------------------------
UVM_INFO top.sv(194) @ 0: env.my_driver [sending] -------------------------------------------------------------------------------
Name Type Size Value
-------------------------------------------------------------------------------
req bus_req - @567
addr integral 12 'h6
data integral 8 'h39
op bus_op_t 32 BUS_WRITE
begin_time time 64 0
depth int 32 'd2
parent sequence (name) string 8 sequence
parent sequence (full name) string 32 env.sequence_controller.sequence
sequencer string 23 env.sequence_controller
-------------------------------------------------------------------------------
UVM_INFO top.sv(191) @ 0: env.my_driver [sending] ------------------------------------
Name Type Size Value
------------------------------------
<unnamed> bus_rsp - @583
addr integral 12 'h6
data integral 8 'h39
op bus_op_t 32 BUS_READ
status status_t 32 STATUS_OK
------------------------------------
UVM_INFO top.sv(194) @ 0: env.my_driver [sending] -------------------------------------------------------------------------------
Name Type Size Value
-------------------------------------------------------------------------------
req bus_req - @589
addr integral 12 'h7
data integral 8 'h3a
op bus_op_t 32 BUS_WRITE
begin_time time 64 0
depth int 32 'd2
parent sequence (name) string 8 sequence
parent sequence (full name) string 32 env.sequence_controller.sequence
sequencer string 23 env.sequence_controller
-------------------------------------------------------------------------------
UVM_INFO top.sv(191) @ 0: env.my_driver [sending] ------------------------------------
Name Type Size Value
------------------------------------
<unnamed> bus_rsp - @603
addr integral 12 'h7
data integral 8 'h3a
op bus_op_t 32 BUS_READ
status status_t 32 STATUS_OK
------------------------------------
UVM_INFO top.sv(194) @ 0: env.my_driver [sending] -------------------------------------------------------------------------------
Name Type Size Value
-------------------------------------------------------------------------------
req bus_req - @609
addr integral 12 'h8
data integral 8 'h3b
op bus_op_t 32 BUS_WRITE
begin_time time 64 0
depth int 32 'd2
parent sequence (name) string 8 sequence
parent sequence (full name) string 32 env.sequence_controller.sequence
sequencer string 23 env.sequence_controller
-------------------------------------------------------------------------------
UVM_INFO top.sv(191) @ 0: env.my_driver [sending] ------------------------------------
Name Type Size Value
------------------------------------
<unnamed> bus_rsp - @625
addr integral 12 'h8
data integral 8 'h3b
op bus_op_t 32 BUS_READ
status status_t 32 STATUS_OK
------------------------------------
UVM_INFO top.sv(194) @ 0: env.my_driver [sending] -------------------------------------------------------------------------------
Name Type Size Value
-------------------------------------------------------------------------------
req bus_req - @631
addr integral 12 'h9
data integral 8 'h3c
op bus_op_t 32 BUS_WRITE
begin_time time 64 0
depth int 32 'd2
parent sequence (name) string 8 sequence
parent sequence (full name) string 32 env.sequence_controller.sequence
sequencer string 23 env.sequence_controller
-------------------------------------------------------------------------------
UVM_INFO top.sv(191) @ 0: env.my_driver [sending] ------------------------------------
Name Type Size Value
------------------------------------
<unnamed> bus_rsp - @647
addr integral 12 'h9
data integral 8 'h3c
op bus_op_t 32 BUS_READ
status status_t 32 STATUS_OK
------------------------------------
UVM_INFO top.sv(244) @ 0: env.sequence_controller@@sequence [sequenceA] Finishing sequence
UVM_INFO ../../../../src/base/uvm_objection.svh(1270) @ 0: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO ../../../../src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
** Report counts by severity
UVM_INFO : 16
UVM_WARNING : 0
UVM_ERROR : 0
UVM_FATAL : 0
** Report counts by id
[RNTST] 1
[TEST_DONE] 1
[UVM/RELNOTES] 1
[sending] 10
[sequenceA] 2
[top] 1
$finish called from file "../../../../src/base/uvm_root.svh", line 517.
$finish at simulation time 0
V C S S i m u l a t i o n R e p o r t
Time: 0 ns
不加+define+USE_FIELD_MACROS宏定义,调用的是自己定义的函数,仿真的log如下:
UVM_INFO top.sv(289) @ 0: reporter [top] In top initial block
UVM_INFO @ 0: reporter [RNTST] Running test ...
UVM_INFO top.sv(220) @ 0: env.sequence_controller@@sequence [sequenceA] Starting sequence
UVM_INFO top.sv(194) @ 0: env.my_driver [sending] op BUS_WRITE: addr=005, data=38
UVM_INFO top.sv(191) @ 0: env.my_driver [sending] op op BUS_READ: addr=005, data=38, status=STATUS_OK
UVM_INFO top.sv(194) @ 0: env.my_driver [sending] op BUS_WRITE: addr=006, data=39
UVM_INFO top.sv(191) @ 0: env.my_driver [sending] op op BUS_READ: addr=006, data=39, status=STATUS_OK
UVM_INFO top.sv(194) @ 0: env.my_driver [sending] op BUS_WRITE: addr=007, data=3a
UVM_INFO top.sv(191) @ 0: env.my_driver [sending] op op BUS_READ: addr=007, data=3a, status=STATUS_OK
UVM_INFO top.sv(194) @ 0: env.my_driver [sending] op BUS_WRITE: addr=008, data=3b
UVM_INFO top.sv(191) @ 0: env.my_driver [sending] op op BUS_READ: addr=008, data=3b, status=STATUS_OK
UVM_INFO top.sv(194) @ 0: env.my_driver [sending] op BUS_WRITE: addr=009, data=3c
UVM_INFO top.sv(191) @ 0: env.my_driver [sending] op op BUS_READ: addr=009, data=3c, status=STATUS_OK
UVM_INFO top.sv(244) @ 0: env.sequence_controller@@sequence [sequenceA] Finishing sequence
UVM_INFO ../../../../src/base/uvm_objection.svh(1270) @ 0: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO ../../../../src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
** Report counts by severity
UVM_INFO : 16
UVM_WARNING : 0
UVM_ERROR : 0
UVM_FATAL : 0
** Report counts by id
[RNTST] 1
[TEST_DONE] 1
[UVM/RELNOTES] 1
[sending] 10
[sequenceA] 2
[top] 1
$finish called from file "../../../../src/base/uvm_root.svh", line 517.
$finish at simulation time 0
V C S S i m u l a t i o n R e p o r t
Time: 0 ns
通过对比两个log文件,可以看到调用UVM的field机制打印出来的信息更加详细,通过代码分析也可以看出,在代码实现上,调用UVM的field机制也更简单,所以,在处理类变量的过程中建议调用UVM的feild机制。
总结
通过这个例子,主要可以了解带REQ和RSP的sequence应该如何去编写。另外,在处理类变量的时候,尽量采用UVM的field去注册类变量,这样代码量少,支持的功能也非常强大。