(11)UVM 寄存器模型集成(上)

控制寄存器接口首先需要在每一个时钟解析cmd。
当cmd为写指令时,即需要把数据cmd_data_in写入到cmd_addr对应的寄存器中。
当cmd为读指令时,即需要从cmd_addr对应的寄存器中读取数据,在下一个周期,cmd_addr对应的寄存器数据被输送至cmd_data_out接口。

总线UVC示例

下面是一段8位地址线,32位数据线的总线UVC实现代码。

class mcdf_bus_trans extends uvm_sequence_item; 
	rand bit[1:0]cmd; 
	rand bit[7:0] addr; 
	rand bit[31:0] wdata; 
	bit[31:0]rdata; 
	`uvm_object_utils_begin(mcdf_bus_trans)
		...
	`uvm_object_utils_end 
endclass 
class mcdf_bus_sequencer extends uvm_sequencer; 
	virtual mcdf_if vif; 
	`uvm_component_utils(mcdf_bus_sequencer)
		...
	function void build_phase(uvm_phase phase); 
		if(!uvm_config_db#(virtual_mcdf_if)::get(this,"","vif",vif))begin; 
			`uvm_error("GETVIE","no virtual interface is assigned")
		end 
	endfunction 
endclass
class mcdf_bus_monitor extends uvm_monitor; 
	virtual mcdf_if vif; 
	uvm_analysis_port#(mcdf_bus_trans) ap;
	`uvm_component_utils(mcdf_bus_monitor)
	...
	function void build_phase(uvm_phase phase); 
		if(!uvm_config_db#(virtual mcdf_if)::get(this,"","vif",vif))begin
			`uvm_error("GETVIF","no virtual interface is assigned")
		end 
		ap=new("ap",this); 
	endfunction 
	task run_phase(uvm_phase phase); 
		forever begin 
			mon_trans(); 
		end 
	endtask
	task mon_trans(); 
		mcdf_bus_trans t; 
		@(posedge vif.clk); 
			if(vif.cmd==WRITE) begin 
				t=new(); 
				t.cmd=WRITE; 
				t.addr=vif.addr; 
				t.wdata=vif.wdata; 
				ap.write(t);
			end 
			else if(vif.cmd==READ) begin 
				t=new(); 
				t.cmd=READ; 
				t.addr=vif.addr; 
				fork 
					begin 
						@(posedge vif.clk);
						#10ps; 
						t.rdata=vif.rdata; 
						ap.write(t); 
					end 
				join_none 
			end 
	endtask 
endclass: mcdf_bus_monitor
class mcdf_bus_driver extends uvm_driver; 
	virtual mcdf_if vif;
	`uvm_component_utils(mcdf_bus_driver)
	...
	function void build_phase(uvm_phase phase); 
		if(!uvm_config_db#(virtual mcdf_if)::get(this,"","vif", vif)) begin 
			`uvm_error("GETVIF","no virtual interface is assigned")
		end 
	endfunction 
	task run_phase(uvm_phase phase); 
		REQ tmp; 
		mcdf_bus_trans req, rsp; 
		reset_listener(); 
		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)
			void'($cast(rsp, req. clone())); 
			rsp.set_sequence_id(req.get_sequence_id()); 
			rsp.set_transaction_id(req.get_transaction_id()); 
			drive_bus(rsp); 
			seq_item_port.item_done(rsp);
			`uvm_info("DRV",$sformatf("sent a item \n %s", rsp.sprint()), UVM_LOW)
		end 
	endtask
	task reset_listener(); 
		fork 
			forever begin 
				@(negedge vif.rstn) drive_idle(); 
			end 
		join_none 
	endtask
	task drive_bus(mcdf_bus_trans t); 
		case(t.cmd) 
			`WRITE: drive_write(t); 
			`READ: drive_read(t); 
			`IDLE: drive_idle(1); 
			default:`uvm_error("DRIVE","invalid mcdf command type received!")
		endcase
	endtask 
	task drive_write(mcdf_bus_trans t); 
		@(posedge vif.clk); 
			vif.cmd<=t.cmd; 
			vif.addr <=t.addr; 
			vif.wdata<=t.wdata; 
	endtask
	task drive_read(mcdf_bus_trans t);
		@(posedge vif.clk); 
			vif.cmd<=t.cmd; 
			vif.addr<=t.addr; 
		@(posedge vif.clk);
			#10ps; 
			t.rdata=vif.rdata; 
	endtask 
	task drive_idle(bit is_sync=0);
		if(is_sync)@(posedge vif.clk); 
		vif.cmd <='h0; 
		vif.addr <='h0; 
		vif.wdata<='h0; 
	endtask
endclass

总线UVC的解析

·示例囊括了mdf_bus_agent的所有组件:sequence item、sequencer、driver、monitor和agent。这些代码的部分实现给出解释如下:

  • mcdf_bus_trans包括了可随机化的数据成员cmd、addr、wdata和不可随机化的rdata。rdata之所以没有声明为rand类型,是因为它应从总线读出或者观察,不应随机化。
  • mcdf_bus_monitor会观测总线,其后通过analysis_port写出到目标analysis组件,稍后它将连接到uvm_reg_predictor。
  • mcdf_bus_driver主要实现了总线驱动和复位功能,通过模块化的方法reset_listener()、drive_bus()、drive_write()、drive_read()和drive_idle()可以解析三种命令模式IDLE、WRITE和READ,并且在READ模式下,将读回的数据通过item_done(rsp)写回到sequencer和sequence一侧。建议通过clone()命令创建RSP对象后,通过set_sequence_id()和set_transaction_id()两个函数保证REQ和RSP的中保留的ID信息一致。

MCDF寄存器设计代码

param_def.v

`define  ADDR_WIDTH 8
`define  CMD_DATA_WIDTH 32

`define  WRITE 2'b10          //Register operation command
`define  READ  2'b01
`define  IDLE  2'b00

`define SLV0_RW_ADDR 8'h00    //Register address 
`define SLV1_RW_ADDR 8'h04
`define SLV2_RW_ADDR 8'h08
`define SLV0_R_ADDR  8'h10
`define SLV1_R_ADDR  8'h14
`define SLV2_R_ADDR  8'h18

`define SLV0_RW_REG 0
`define SLV1_RW_REG 1
`define SLV2_RW_REG 2
`define SLV0_R_REG  3
`define SLV1_R_REG  4
`define SLV2_R_REG  5

`define FIFO_MARGIN_WIDTH 8

`define PRIO_WIDTH 2
`define PRIO_HIGH 2
`define PRIO_LOW  1

`define PAC_LEN_WIDTH 3
`define PAC_LEN_HIGH 5
`define PAC_LEN_LOW  3    

reg.v

   module ctrl_regs(	clk_i,
						rstn_i,
						cmd_i,
						cmd_addr_i,
						cmd_data_i,
						cmd_data_o,
			      		slv0_pkglen_o,
						slv1_pkglen_o,
						slv2_pkglen_o,
			     		slv0_prio_o,
						slv1_prio_o,
						slv2_prio_o,		
						slv0_margin_i,
						slv1_margin_i,
						slv2_margin_i,
					 	slv0_en_o,
						slv1_en_o,
						slv2_en_o);
						
input clk_i;
input rstn_i;
input [1:0] cmd_i;
input [`ADDR_WIDTH-1:0]  cmd_addr_i; 
input [`CMD_DATA_WIDTH-1:0]  cmd_data_i;
input [`FIFO_MARGIN_WIDTH-1:0] slv0_margin_i;
input [`FIFO_MARGIN_WIDTH-1:0] slv1_margin_i;
input [`FIFO_MARGIN_WIDTH-1:0] slv2_margin_i;

reg [`CMD_DATA_WIDTH-1:0] mem [5:0];
reg [`CMD_DATA_WIDTH-1:0] cmd_data_reg;

output  [`CMD_DATA_WIDTH-1:0] cmd_data_o;
output  [`PAC_LEN_WIDTH-1:0]  slv0_pkglen_o;
output  [`PAC_LEN_WIDTH-1:0]  slv1_pkglen_o;
output  [`PAC_LEN_WIDTH-1:0]  slv2_pkglen_o;
output  [`PRIO_WIDTH-1:0]  slv0_prio_o;
output  [`PRIO_WIDTH-1:0]  slv1_prio_o;
output  [`PRIO_WIDTH-1:0]  slv2_prio_o;
output   slv0_en_o;
output   slv1_en_o;
output   slv2_en_o;

always @ (posedge clk_i or negedge rstn_i) //Trace fifo's margin
begin 
  if (!rstn_i)
    begin
      mem [`SLV0_R_REG] <= 32'h00000020;   //FIFO's depth is 32
      mem [`SLV1_R_REG] <= 32'h00000020;
      mem [`SLV2_R_REG] <= 32'h00000020;
    end
    else 
    begin
      mem [`SLV0_R_REG] <= {24'b0,slv0_margin_i};
      mem [`SLV1_R_REG] <= {24'b0,slv1_margin_i};
      mem [`SLV2_R_REG] <= {24'b0,slv2_margin_i};
    end
end

always @ (posedge clk_i or negedge rstn_i)begin //write R&W register
	if (!rstn_i)begin 
    	mem [`SLV0_RW_REG] = 32'h00000007;
    	mem [`SLV1_RW_REG] = 32'h00000007;
   	 	mem [`SLV2_RW_REG] = 32'h00000007;
  	end
 	else if (cmd_i== `WRITE) begin
		case(cmd_addr_i)
			`SLV0_RW_ADDR: mem[`SLV0_RW_REG]<= {26'b0,cmd_data_i[`PAC_LEN_HIGH:0]};				
			`SLV1_RW_ADDR: mem[`SLV1_RW_REG]<= {26'b0,cmd_data_i[`PAC_LEN_HIGH:0]};			
			`SLV2_RW_ADDR: mem[`SLV2_RW_REG]<= {26'b0,cmd_data_i[`PAC_LEN_HIGH:0]};   
		endcase 
	end	
end 

always@ (posedge clk_i or negedge rstn_i) // read R&W, R register
  	if(!rstn_i)
  	  	cmd_data_reg <= 32'b0;
	else if(cmd_i == `READ)begin       
		case(cmd_addr_i)
			`SLV0_RW_ADDR:		cmd_data_reg  <= mem[`SLV0_RW_REG];
			`SLV1_RW_ADDR:		cmd_data_reg  <= mem[`SLV1_RW_REG];
			`SLV2_RW_ADDR:	  	cmd_data_reg  <= mem[`SLV2_RW_REG];					
			`SLV0_R_ADDR: 		cmd_data_reg  <= mem[`SLV0_R_REG];
			`SLV1_R_ADDR: 		cmd_data_reg  <= mem[`SLV1_R_REG];
			`SLV2_R_ADDR: 		cmd_data_reg  <= mem[`SLV2_R_REG];
		endcase
     end
assign  cmd_data_o  	= cmd_data_reg;
assign  slv0_pkglen_o  	= mem[`SLV0_RW_REG][`PAC_LEN_HIGH:`PAC_LEN_LOW];
assign  slv1_pkglen_o  	= mem[`SLV1_RW_REG][`PAC_LEN_HIGH:`PAC_LEN_LOW];
assign  slv2_pkglen_o 	= mem[`SLV2_RW_REG][`PAC_LEN_HIGH:`PAC_LEN_LOW];

assign  slv0_prio_o  = mem[`SLV0_RW_REG][`PRIO_HIGH:`PRIO_LOW];
assign  slv1_prio_o  = mem[`SLV1_RW_REG][`PRIO_HIGH:`PRIO_LOW];
assign  slv2_prio_o  = mem[`SLV2_RW_REG][`PRIO_HIGH:`PRIO_LOW];
  
assign  slv0_en_o = mem[`SLV0_RW_REG][0];
assign  slv1_en_o = mem[`SLV1_RW_REG][0];
assign  slv2_en_o = mem[`SLV2_RW_REG][0];

endmodule

在这里插入图片描述
这篇笔记参考《UVM实战》、《芯片验证漫游指南》和某验证视频整理而成,仅作学习心得交流,如果涉及侵权烦请请告知,我将第一时间处理。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数字ic攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值