[转]Sequence的层次化

一、概述

水平复用而言,在MCDF各个子模块的验证环境中,它指的是如何利用已有资源,完成高效的激励场景创建。而就垂直复用来看,它指的是在MCDF子系统验证中,可以完成结构复用激励场景复用两个方面。无论是水平复用还是垂直复用,激励场景的复用很大程度上取决于如何设计sequence,使得底层的sequence实现合理的粒度,帮助完成水平复用,进一步依托于底层激励场景,最终可以实现底层到高层的垂直复用。

二、Hierarchical Sequence介绍

在这里插入图片描述
在验证MCDF的寄存器模块时,将SV验证环境进化到了UVM环境之后,关于测试寄存器模块的场景可以将其拆分为:

  • 设置时钟和复位
  • 测试通道1的控制寄存器和只读寄存器
  • 测试通道2的控制寄存器和只读寄存器
  • 测试通道3的控制寄存器和只读寄存器

上面的测试场景拆解下的sequence需要挂载的都是reg_master_agent中的sequencer

typedef enum {CLKON, CLKOFF, RESET, WRREG, RDREG} cmd_t;
class bus_trans extends uvm_sequence_item;
	rand cmd_t cmd;
	rand int addr;
	rand int data;
	constraint cstr{
		soft addr == 'h0;
		soft data == 'h0;
	}
	...
endclass

class clk_rst_seq extends uvm_sequence;
	rand int freq;
	...
	
	task body();
		bus_trans req;
		`uvm_do_with(req, {cmd == CLKON; data == freq;})
		`uvm_do_with(req, {cmd == RESET;})
	endtask
endclass

class reg_test_seq extends uvm_sequence;
	rand int chnl;
	...
	
	task body();
		bus_trans req;
		//write and read test for WR register
		`uvm_do_with(req, {cmd == WRREG; addr == chnl * 'h4;})
		`uvm_do_with(req, {cmd == RDREG; addr == chnl * 'h4;})
		//read for the RD register
		`uvm_do_with(req, {cmd == RDREG; addr == chnl * 'h4 + 'h10;})
	endtask
endclass

class top_seq extends uvm_sequence;
	...
	
	task body();
		clk_rst_seq clkseq;
		reg_test_seq regseq0, regseq1, regseq2;
		//turn on clock with 150Mhz and assert reset
		`uvm_do_with(clkseq, {freq == 150;})
		//test the register of channel0
		`uvm_do_with(regseq0, {chnl == 0;})
		//test the register of channel1
		`uvm_do_with(regseq1, {chnl == 1;})
		//test the register of channel2
		`uvm_do_with(regseq2, {chnl == 2;})		
	endtask
endclass

class reg_master_sequencer extends uvm_sequencer;
	...
endclass

class reg_master_driver extends uvm_driver;
	...
	task run_phase(uvm_phase phase);
		REQ tmp;
		bus_trans req;
		forever begin
			seq_item_port.get_next_item(tmp);
			void'($cast(req, tmp));
			`uvm_info("DRV", $sformatf("got a item \n %s", req.sprint()), UVM_LOW)
			seq_item_port.item_done();
		end
	endtask
endclass

class reg_master_agent extends uvm_agent;
	reg_master_sequencer sqr;
	reg_master_driver drv;
	...
	
	function void build_phase(uvm_phase phase);
		sqr = reg_master_sequencer::type_id::create("sqr", this);
		drv = reg_master_driver::type_id::create("drv", this);
	endfunction
	
	function void connect_phase(uvm_phase phase);
		drv.seq_item_port.connect(sqr.seq_item_export);
	endfunction
endclass

itembus_trans包含了几个简单的域cmdaddrdata。在clk_rst_seqreg_test_seq这两个底层的sequence在例化和传送item时,就通过随机化bus_trans中的域来实现不同的命令和数据内容。通过不同的数据内容的item,最终可以实现不同的测试目的。在top_seq中,它就通过对clk_rst_seqreg_test_seq这两个element sequence进行组合和随机化赋值,最终实现了一个完整的测试场景,即先打开时钟和完成复位,其后对寄存器模块中的寄存器完成读写测试。所以如果将clk_rst_seqreg_test_seq作为底层sequence,或者称之为element sequencetop_seq作为一个更高层的协调sequence,它本身也会容纳,并对它们进行协调和随机限制,通过将这些element sequence进行有机的调度,最终完成一个期望的测试场景。那么这样的top_seq就可以成为Hierarchical Sequence,它内部 可以包含多个sequenceitem,而通过层层嵌套,最终完成测试序列的合理切分。验证时,有了粒度合适的element sequence,就会更容易在这些设计好的”轮子“上面,实现验证的加速过程。而水平复用,就非常依赖于hierarchical sequence的实现。
在这里插入图片描述

三、virtual sequence介绍

在这里插入图片描述

virtual sequence也是协调各个sequence,但hierarchical sequence面对的对象是同一个sequencer,即hierarchical sequence本身也会挂载到sequencer上面,而对于virtual sequence而言,它内部不同的sequence可以允许面向不同的sequencer种类。

在MCDF子系统验证环境集成过程中,完成了前期的结构垂直复用,就需要考虑如何各个模块的element sequencehierarchical sequence。对于更上层的环境,顶层的测试序列要协调的不再只是面向一个sequencersequence群,而是要面向多个sequencersequence群。面向单一的sequencer,可以通过uvm_sequence::start()来挂载root sequence,而在内部的child sequence则可以通过宏'uvm_do来实现。如果将各个模块环境的element sequencehierarchical sequence都作为可以复用的sequence资源,那么就需要一个可以容纳各个sequence的容器来承载它们,同时也需要一个合适的routing sequencer来组织不同结构中的sequencer,这样的sequencesequencer分别称之为virtual sequencevirtual sequencer

virtual sequence可以承载不同目标sequencersequence群落,而组织协调这些sequence的方式则类似于高层次的hierarchical sequencevirtual sequence一般只会挂载到virtual sequencer上面。virtual sequencer与普通的sequencer相比有着很大的不同,它们起到了桥接其它sequencer的作用,即virtual sequencer是一个链接所有底层sequencer句柄的地方,它是一个中心化的路由器。同时virtual sequencer本身并不会传送item数据对象,因此virtual sequencer不需要与任何的driver进行TLM连接。所以UVM需要在顶层的connect阶段,做好virtual sequencer中各个sequencer句柄与底层sequencer实体对象的一一对接,避免句柄悬空。

virtual sequencer示例

class mcdf_normal_seq extends uvm_sequence;
	`uvm_object_utils(mcdf_normal_seq)
	`uvm_declare_p_sequencer(mcdf_virtual_sequencer)	//p_sequencer是一个uvm_sequencer的子类
	//这一行的宏相当于,定义了mcdf_virtual_sequencer、p_sequencer,然后$(p_sequencer, mcdf_virtual_sequencer)做了转换,完成了父类句柄到子类句柄的转换,然后就可以访问子类所有的成员变量,进而实现路由功能
	...
	
	task body();
		clk_rst_seq clk_seq;
		reg_cfg_seq cfg_seq;
		data_trans_seq data_seq;
		fmt_slv_cfg_seq fmt_seq;
		//配置formatter slave agent
		`uvm_do_on(fmt_seq, p_sequencer.fmt_sqr)
		//打开时钟并完成复位
		`uvm_do_on(clk_seq, p_sequencer.cr_sqr)
		//配置MCDF寄存器
		`uvm_do_on(cfg_seq, p_sequencer.reg_sqr)
		//传递channel数据包
		fork
			`uvm_do_on(data_seq, p_sequencer.chnl_sqr0)
			`uvm_do_on(data_seq, p_sequencer.chnl_sqr1)
			`uvm_do_on(data_seq, p_sequencer.chnl_sqr2)
		join
	endtask
endclass

class mcdf_virtual_sequencer extends uvm_sequencer;
	cr_master_sequencer cr_sqr;
	reg_master_sequencer reg_sqr;
	chnl_master_sequencer chnl_sqr;
	chnl_master_sequencer chnl_sqr;
	chnl_master_sequencer chnl_sqr;
	fmt_slave_sequencer fmt_sqr;
	`uvm_component_utils(mcdf_virtual_sequencer)
	function new(string name, uvm_component parent);
		super.new(name, parent);
	endfunction
endclass

class mcdf_env extends uvm_env;
	cr_master_agent cr_agt;
	reg_master_agent reg_agt;
	chnl_master_agent chnl_agt0;
	chnl_master_agent chnl_agt1;
	chnl_master_agent chnl_agt2;
	fmt_slave_agent fmt_agt;
	mcdf_virtual_sequencer virt_sqr;
	`uvm_component_utils(mcdf_env)
	
	function new(string name, uvm_component parent);
		super.new(name, parent);
	endfunction
	
	function void build_phase(uvm_phase phase);
		cr_agt = cr_master_agent::type_id::create("cr_agt", this);
		reg_agt = reg_master_agent::type_id::create("reg_agt", this);
		chnl_agt0 = chnl_master_agent::type_id::create("chnl_agt0", this);
		chnl_agt1 = chnl_master_agent::type_id::create("chnl_agt1", this);
		chnl_agt2 = chnl_master_agent::type_id::create("chnl_agt2", this);
		fmt_agt = fmt_slave_agent::type_id::create("fmt_agt", this);
		virt_sqr = mcdf_virtual_sequencer::type_id::create("virt_sqr", this);
	endfunction
	
	function void connect_phase(uvm_phase phase);
		virt_sqr.cr_sqr = cr_agt.sqr;
		virt_sqr.reg_sqr = reg_agt.sqr;
		virt_sqr.chnl_sqr = chnl_agt.sqr;
		virt_sqr.chnl_sqr = chnl_agt.sqr;
		virt_sqr.chnl_sqr = chnl_agt.sqr;
		virt_sqr.fmt_sqr = fmt_agt.sqr;
	endfunction
endclass

class test1 extends uvm_test;
	mcdf_env e;
	...
	
	task run_phase(uvm_phase phase);
		mcdf_normal_seq seq;
		phase.raise_objection(phase);
		seq = new();
		seq.start(e.virt_sqr);
		phase.drop_objection(phase);
	endtask
endclass

在这里插入图片描述
对于virtual sequence mcdf_normal_seq而言,它可以承载各个子模块环境的element sequence,而通过最后挂载的virtual sequencer mcdf_virtual_sequencer中的各个底层sequencer句柄,各个element sequence可以分别挂载到对应的底层sequencer上。

尽管在最后test1中,将virtual sequence挂载到了virtual sequencer上面,但是这种挂载的根本目的是为了提供给virtual sequence一个中心化的sequencer路由,而借助在virtual sequence mcdf_normal_seq中使用了宏'uvm_declare_p_sequencer,使得virtual sequence可以使用声明后的成员变量p_sequencer(类型为mcdf_virtual_sequencer),来进一步回溯到virtual sequencer内部的各个sequencer句柄。在这里使用'uvm_declare_p_sequencer这个宏会在后台,新创建一个p_sequencer类型,而将m_sequencer的默认变量(uvm_sequencer_base类型)通过动态转换,变为类型为mcdf_virtual_sequencerp_sequencer。只要声明的挂载sequencer类型正确,可以通过这个宏,完成方便的类型转换,因此可以通过p_sequencer索引到在mcdf_virtual_sequencer中声明的各个sequencer句柄。

四、Layering Sequence介绍

如果在构建更加复杂的协议总线传输,例如PCie、USB3.0等,那么通过一个单一的传输层次会对以后的激励复用、上层控制不那么友好。对于这钟更深层次化的数据传输,在实际中无论是VIP还是自开发的环境,都倾向于通过若干抽象层次的sequence群落来模拟协议层次。通过层次化的sequence可以分别构建transaction layertransport layerphysical layer等从高抽象级到低抽象级的transaction转化。这种层次化的sequence构建方式,称之为layering sequence。例如在进行寄存器级别的访问操作,其需要通过transport layer转化,最终映射为具体的总线传输。

在这里插入图片描述

在这里插入图片描述
Layering Sequence示例

typedef enum {CLKON, CLKOFF, RESET, WRREG, RDREG} phy_cmd_t;
typedef enum {FREQ_LOW_TRANS, FREQ_MED_TRANS, FREQ_HIGH_TRANS}

	layer_cmd_t;
	class bus_trans extends uvm_sequence_item;
		rand phy_cmd_t cmd;
		rand int addr;
		rand int data;
		constraint cstr {
			soft addr == 'h0;
			soft data == 'h0;
		}
		...
		
	endclass
	
	class packet_seq extends uvm_sequence;
		rand int len;
		rand int addr;
		rand int data[];
		rand phy_cmd_t cmd;
		constraint cstr {
			soft len inside {[30:50]};
			soft addr[31:16] == 'hFF00;
			data.size() == len;
		}
		...
		task body();
			bus_trans req;
			foreach(data[i])
				`uvm_do_with(req, {cmd == local::cmd;
								   addr == local::addr;
								   data == local::data[i];})
		endtask
	endclass
	
	class layer_trans extends uvm_sequence_item;
		rand layer_cmd_t layer_cmd;
		rand int pkt_len;
		rand int pkt_idle;
		constraint cstr {
			soft pkt_len inside {[10:20]};
			layer_cmd == FREQ_LOW_TRANS -> pkt_idle inside {[300:400]};
			layer_cmd == FREQ_MED_TRANS -> pkt_idle inside {[100:200]};
			layer_cmd == FREQ_HIGH_TRANS -> pkt_idle inside {[20:40]};
			
		}
		
		...
		
	endclass
	
	class adapter_seq extends uvm_sequence;
		`uvm_object_utils(adapter_seq)
		`uvm_declare_p_sequencer(phy_master_sequencer)
		...
		
		task body();
			layer_trans trans;
			packet_seq pkt;
			forever begin
				p_sequencer.up_sqr.get_next_item(req);	//获得高抽象层的transaction
				void'h(trans, req);
				repeat(trans.pkt_len) begin
					`uvm_do(pkt)
					delay(trans.pkt_idle);
				end
				p_sequencer.up_sqr.item_done();
			end
		endtask
		virtual task delay(int delay);
		...
		endtask
	endclass
	
	class top_seq extends uvm_sequence;
		...
		task body();
			layer_trans trans;
			`uvm_do_with(trans, {layer_cmd == FREQ_LOW_TRANS;})
			`uvm_do_with(trans, {layer_cmd == FREQ_HIGH_TRANS;})
		endtask
	endclass
	
	class layering_sequencer extends uvm_sequencer;
		...
	endclass
	
	class phy_master_sequencer extends uvm_sequencer;
		layering_sequencer up_sqr;
		...
	endclass
	
	class phy_master_driver extends uvm_driver;
		...
		task run_phase(uvm_phase phase);
			REQ tmp;
			bus_trans req;
			forever begin
				seq_item_port.get_next_item(tmp);
				void'($cast(req, tmp));
				`uvm_info("DRV", $sformatf("got a item \n %s", req.sprint()), UVM_LOW)
				seq_item_port.item_done();
			end
		endtask
	endclass
	
	class phy_master_agent extends uvm_agent;
		phy_master_sequencer sqr;
		phy_master_driver drv;
		...
		function void build_phase(uvm_phase phase);
			sqr = phy_master_sequencer::type_id::create("sqr", this);
			drv = phy_master_driver::type_id::create("drv", this);
		endfunction
		
		function void connect_phase(uvm_phase phase);
			drv.seq_item_port.connect(sqr.seq_item_export);
		endfunction
	endclass
	
	class test1 extends uvm_test;
		layering_sequencer layer_sqr;
		phy_master_agent phy_agt;
		...
		function void build_phase(uvm_phase phase);
			layer_sqr = layering_sequencer::type_id::create("layer_sqr", this);
			phy_agt = phy_master_agent::type_id::create("phy_agt", this);
		endfunction
		function connect_phase(uvm_phase phase);
			phy_agt.sqr.up_sqr = layer_sqr;
		endfunction
		task run_phase(uvm_phase phase);
			top_seq seq;
			adapter_seq adapter;
			phase.raise_objection(phase);
			seq = new();
			adapter = new();
			fork
				adapter.start(phy_agt.sqr);
			join_none
			seq.start(layer_sqr);
			phase.drop_objection(phase);
		endtask
	endclass

通过上面的代码,可以得出一些关于如何实现sequence layer协议转换的方法:

  • 无论有多少抽象层次的transaction类定义,都应该有对应的sequencer作为transaction的路由通道。例如layer_sequencerphy_master_sequencer分别作为layer_transbus_trans的通道。
  • 在各个抽象级的sequencer中,需要有相应的转换方法,将从高层次的transaction从高层次的sequencer获取,继而转换为低层次的transaction,最终通过低层次的sequencer发送出去。例如adapter_seq负责从layer_sequencer获取layer_trans,再将其转换为phy_master_sequencer一侧对应的sequence或者transaction,最后将其从phy_master_sequencer发送出去。
  • 这些adaption sequence应该运行在低层次的sequencer一侧,作为“永动”的sequence时刻做好服务准备,从高层次的sequencer获取transaction,通过转化将其从低层次的sequencer一侧送出。例如上面的test1中,adapter sequence通过adapter.start(phy_agt.sqr)挂载到低层次的sequencer,做好转换transaction并将其发送的准备。

---------------------
作者:煎丶包
来源:CSDN
原文:https://blog.csdn.net/qq_39794062/article/details/114376055
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值