uvm-1.2 examples —— 2.7 sequence

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去注册类变量,这样代码量少,支持的功能也非常强大。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值