(23)UVM 层次化sequence构建方式(layering sequence)

UVM 层次化sequence构建方式(layering sequence)

文章目录

layering sequence介绍

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

layering sequence示例

(23)UVM 层次化sequence构建方式(layering sequence)

typedef enum{CLKON,CLKOFF,RESET,WRREG,RDREG} phy_cmd_t;
typedef enum{FREG_LOW_TRANS,FREG_MED_TRANS,FREG_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:26]=='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_sequence 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);
			void'($cast(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;
	...
endclasss

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_driver drv;
	phy_master_sequencer sqr;
	...
	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

class test1 extends uvm_test;
	layering_sequencer layer_sqr;
	phy_master_agent phy_agt;
	...
	function void build_phae(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 void 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

不是driver,可以调用get_next_item吗?也可以,因为get_next_item方法实际上是属于sqr的,那为什么driver可以直接通过port调用该方法呢?这是因为port与sqr的import相连。

我们可以得出一些关于如何实现sequencer layer协议转换的方法:

  • 无论有多少抽象层次的transaction类定义,都应该有对应的sequencer作为transaction的路由通道。例如layer_sequencer和phy_master_sequencer分别作为layer_trans和bus_trans的通道。
  • 在各个抽象级的sequence中,需要有相应的转换方法,将从高层次的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并将其发送的准备。
  • 至于需要定义多少个层次的transaction item类,上面的例子仅仅是为了说明layer sequence的一般方法,对于实际中的层次定义和transaction item定义,我们还需要具体问题具体处理。

layering sequence解析

我们可以看到各个sequence类别对应的sequencer,同时也有sequence item发送和转换的方法。经过转换,最终高层次的transaction内容可以落实到低层次的protocol agent和physical interface上面。

例码中没有给出读回路的处理,即从physical interface穿过physical agent,最终抵达layering sequencer的通信。在实际中我们可以通过monitor转换抽象级返回item的方式来实现。

也可以通过不同层次的monitor采集response transcation,最终通过monitor转换抽象级返回item的方式来实现。

至于选择哪一种反馈回路,这与底层agent反馈回路的实现方式有关,即如果原有方法通过driver一侧返回response,那么我们建议继续在该反馈链条上进行从低级transaction到高级transaction的转换,如果原有方式通过monitor一侧返回response,那么我们也建议创建对应的高层次monitor,抽线抽象层次转换。

关注作者

  • 自述
    作者是一位中科大数字设计专业的研究生,水平有限,如有错误,请大家指正,想要与大家一同进步。
  • 经历
    曾获得国家奖学金,“高教社杯”数学建模国家二等奖等
  • 陆续更新:
    1.与UVM验证相关的system verilog后续内容;
    2.与verilog数字设计相关的一些基础模块设计,例如FIFO,UART,I2C等的书写。
    3.保研与竞赛经历等
  • 微信公众号
    欢迎大家关注公众号“数字IC小白的日常修炼”,期待与大家一同仗剑遨游数字IC世界。
上一篇:[源码解析] PyTorch 流水线并行实现 (5)--计算依赖


下一篇:【AI 全栈 SOTA 综述 】这些你都不知道,怎么敢说会 AI?【语音识别原理 + 实战】