MCDF-实验4——从MCDT进入MCDF

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本文内容:
回顾:
第一:验证环境按照隔离的观念,应该分为硬件DUT,软件验证环境package,和处于信号媒介的借口interface
第二:对于软件验证环境,它需要经历建立阶段(build),连接阶段(connect),产生激励阶段(generator)和发送激励阶段(transfer)
当所有的激励发送完毕且比较完全后,才可以结束测试。
在MCDT基础上:

  • 与MCDT相比较,MCDF添加了寄存器控制和状态显示功能,同时也添加了一个重要的数据整形功能。
  • 实验4的文件增多是为了让整个验证环境的各组件相互独立,不同的package之间功能是相互独立的,同一个package中的个验证组件的功能也是独立的,而他们之间的同步和通信是依靠event和mailbox来实现。

提示:以下是本篇文章正文内容,请在了解mcdf设计文件及相关验证组件后阅读,内容仅供参考

一、编译-仿真

  • vsim -novopt -classdebug -solvefaildebug -sv_seed 0 +TESTNAME=mcdf_data_consistence_basic_test -l mcdf_data_consistence_basic_test_sim.log work.tb
    -classdebug,提供SV类调试功能
    -solvefaildebug,SV随机化失败时提示信息
    -sv_seed 0,固定种子值为0
    +TESTNAME=mcdf_data_consistence_basic_test,指定仿真选择的测试
    -l mcdf_data_consistence_basic_test_sim.log,让仿真记录保存在特定的测试文件名称中
    在这里插入图片描述
    信箱mailbox没有快速清空的delete()方法
    仿真波形
    在这里插入图片描述
    可以看到三个通道在某段时间是并行发送的,过了一段时间后变成了顺序发送,返回代码可以看到测试代码使用的是并行fork…join块,为何还会出现这样的情况?
  • 原因在于mcdf_data_consistence_basic_test类中在做寄存器配置do_reg时对通道的优先级作了规定,channel0>channel1>channel2

二、测试功能

1.寄存器读写测试

测试内容:所有控制寄存器的读写测试,所有状态寄存器的读写测试
测试类名:mcdf_reg_write_read_test

vsim -novopt -classdebug -solvefaildebug -sv_seed 0 +TESTNAME=mcdf_reg_write_read_test -l mcdf_reg_write_read_test_sim.log work.tb

测试代码

//寄存器读写测试,即检查读写值是否一致
	class mcdf_reg_write_read_test extends mcdf_base_test;
		function new(string name = "mcdf_reg_write_read_test");
      super.new(name);
    endfunction
    
    task do_reg();
    //两个动态数组,里面是3个读写寄存器和3个只读寄存器对应的地址
  		bit[7:0] chnl_rw_addrs[] = '{`SLV0_RW_ADDR, `SLV1_RW_ADDR, `SLV2_RW_ADDR};
  		bit[7:0] chnl_ro_addrs[] = '{`SLV0_R_ADDR, `SLV1_R_ADDR, `SLV2_R_ADDR};
  	//寄存器配置的位数pkg_length_width+prio_width+en_width = 6
  		int pwidth = `PAC_LEN_WIDTH +`PRIO_WIDTH +1;
  	//指定配置,这里给出了3个,就是之后要检查的
  		bit[31:0] check_pattern[] = '{((1<<pwidth)-1), 0, ((1<<pwidth)-1)};//(1<<pwidth)-1 = (32'b0100_0000)-1 即(64-1)=32'b0011_1111 (63) 
   	//表示寄存器读、写配置的变量	                               
  		bit[31:0] wr_val, rd_val;
  		
  		//RW register write and  read 
  		foreach(chnl_rw_addrs[i])begin
  			foreach(check_pattern[i])begin
  				wr_val = check_pattern[i];
  				this.write_reg(chnl_rw_addrs[i], wr_val);
  				this.read_reg(chnl_rw_addrs[i], rd_val);
  				void'(this.diff_value(wr_val, rd_val));
  			end
  		end
  		
  		//RO register read
  		foreach(chnl_ro_addrs[i]) begin
  			this.read_reg(chnl_ro_addrs[i], rd_val);
  		end
  		//send IDLE command
  		this.idle_reg();
  	endtask

仿真波形
在这里插入图片描述
可以看到,对于读写寄存器的写测试,重复三次【如果想要输入三次不同的配置,第二个foreach的循环变量应该区别于i】
写入寄存器给ch0~ch2的配置命令为32'h0000_003f,32'h0000_0000,32'h0000_003fcmd_data_i波形正确;
读测试,cmd_data_o波形正确
最后是只读寄存器的读测试,不能写入,从cmd_data_o的信号波形可以看出只读寄存器配置随机化后为32'h0000_0020

👻为什么选用((1<<pwidth)-1)作为测试的写入数据?
原因在于控制寄存器的的高26位是保留位,无法写入,在reg.v文件里,高26位是置零的,所以读出来的一定是低六位的数据,所以在做读写测试时写入的一定是正常值,读出来的才会符合期望,比较才不会出错。

测试通过!


2.寄存器稳定性测试

测试内容:所有控制,状态寄存器保留位的读写测试
测试类名:mcdf_reg_illegal_access_test

vsim -novopt -classdebug -solvefaildebug -sv_seed 0 +TESTNAME=mcdf_reg_illegal_access_test -l mcdf_reg_illegal_access_test_sim.log work.tb

class mcdf_reg_illegal_access_test extends mcdf_base_test;
	function new(string name = "mcdf_reg_write_read_test");
      super.new(name);
    endfunction
    
    task do_reg();
  		bit[7:0] chnl_rw_addrs[] = '{`SLV0_RW_ADDR, `SLV1_RW_ADDR, `SLV2_RW_ADDR};
  		bit[7:0] chnl_ro_addrs[] = '{`SLV0_R_ADDR, `SLV1_R_ADDR, `SLV2_R_ADDR};
  		int pwidth = `PAC_LEN_WIDTH +`PRIO_WIDTH +1;
  		bit[31:0] check_pattern[] = '{32'h0000_FF00, 32'hFFFF_0000};
  		bit[31:0] wr_val, rd_val;
  		//控制寄存器保留域读写
  		foreach(chnl_rw_addrs[i]) begin
  			foreach(check_pattern[j])begin
  				wr_val = check_pattern[j];
  				this.write_reg(chnl_rw_addrs[i], wr_val);
  				this.read_reg(chnl_rw_addrs[i], rd_val);
  				void'(this.diff_value(wr_val & ((1<<pwidth)-1), rd_val));
  			end
  		end
  		
  		//状态寄存器保留域读写
  		foreach(chnl_rw_addrs[i]) begin
  				wr_val = 32'hFFFF_FF00;
  				this.write_reg(chnl_ro_addrs[i], wr_val);
  				this.read_reg(chnl_ro_addrs[i], rd_val);
  				void'(this.diff_value(0, rd_val & 32‘hFFFF_FF00));
  		end
  				
  		//send IDLE command
  		this.idle_reg();
  	endtask
endclass

👻对读写寄存器的非法写入测试,为什么比较的是wr_val & ((1<<pwidth)-1)和rd_val?
读写寄存器正常工作下读取到的数据是非法数值的低6位,wr_val & ((1<<pwidth)-1)按位相与后就剩下低六位了。这样比较成功就表示寄存器即使输入了非法数值,也不会影响其对低六位有效值的读取,即寄存器的功能不会紊乱。
👻对只读寄存器的非法写入测试,为什么是0和rd_val & 32‘hFFFF_FF00比较?
状态寄存器的低8位是fifo余量,没写data所以是32,而高24位是我们需要检查是否读取到了,因此只取ra_val的高24位,正常情况下,我们读回来的高24位一定是0,因此期望值设置为0。这样比较就可以知道状态寄存器是否正常工作。

仿真波形
在这里插入图片描述

可以看到控制寄存器和状态寄存器的非法写入值cmd_data_i波形显示正常,并且虽然输入了非法值,但是寄存器命令的期望值cmd_data_o并没有发生紊乱,对于控制寄存器它的输出是0,因为低6位是0,符合期望,状态寄存器输出指令是32’h0000_0020 = 32 即fifo的余量,也符合期望。
寄存器对于非法地址的读写,寄存器正常工作,没有输出紊乱值。
测试通过!


3.数据通道开关测试

测试内容:对每个通道的en配置为0,测试写入值能否通过
测试类名:mcdf_channel_disable_test

vsim -novopt -classdebug -solvefaildebug -sv_seed 0 +TESTNAME=mcdf_channel_disable_test -l mcdf_channel_disable_test_sim.log work.tb

在checker中添加任务用于检查通道关闭时,slave的时序是否正确

    //检查通道关闭是否实现
    task do_channel_disable_check(int id);
    //after run to check it forever
    	forever begin
    		//toggle event: clk posedge and (rstn && chnl_en[id]===0) ==1
    		//clk,rstn rasied and chnnel disabled
    		@(posedge this.mcdf_vif.clk iff (this.mcdf_vif.rstn && this.mcdf_vif.mon_ck.chnl_en[id]===0));
    		
    		//the chnl[id]'s valid is high and ready is high, this is not allowed
    		//when channel disabled, valid should be low
    		if(this.chnl_vifs[id].mon_ck.ch_valid===1 && this.chnl_vifs[id].ch_ready===1)
    			rpt_pkg::rpt_msg("[CHECKER]",
    			$sformatf("ERROR!  %0t when channel disabled, ready signal rasied when valid high",$time),
    			rpt_pkg::ERROR,
    			rpt_pkg::TOP,
    			);
    	end
  	endtask

添加好该任务后,在checker.run中调用,以检查三个通道

    task run();
      fork
      	this.do_channel_disable_check(0);
      	this.do_channel_disable_check(1);
      	this.do_channel_disable_check(2);
        this.do_compare();
        this.refmod.run();
      join
    endtask

补全mcdf_intf接口

interface mcdf_intf(input clk, input rstn);
	logic chnl_en[3];//寄存器的使能端硬件信号

  clocking mon_ck @(posedge clk);
    default input #1ns output #1ns;
    input chnl_en;
  endclocking
endinterface

连接接口的另一端,设计文件中的寄存器实例的对应端口

 //mcdf interface monitoring MCDF ports and signals
  assign mcdf_if.chnl_en[0] = tb.dut.ctrl_regs_inst.slv0_en_o;
  assign mcdf_if.chnl_en[1] = tb.dut.ctrl_regs_inst.slv1_en_o;
  assign mcdf_if.chnl_en[2] = tb.dut.ctrl_regs_inst.slv2_en_o;
  • mcdf_channel_disable_test采用mcdf_data_consistence_basic_test代码,只需修改en的值,这里打开0通道,关闭1,2
    仿真波形如下:
    在这里插入图片描述
    从波形来看只有0通道数据输出,符合预期,但是仿真一直运行,没有report,没有$finish
    在这里插入图片描述
    有知道为什么的吗?试着跑了一下lab5的参考代码mcdf_full_random_test,也是会出现这样的情况,波形如下:
    在这里插入图片描述
    在这里插入图片描述
    测试结果符合预期但是仍不明白仿真不结束的原因。有知道的可以在评论区交流,开始下一个测试。

4.优先级测试

测试内容:在通道打开的情况下,设置相同或不同的优先级,查看结果。
测试类名:mcdf_aribiter_priority_test

在checker中添加任务用于检查当某一通道优先级最高时且slavx与formatter都发送了请求,仲裁器是否允许数据从端发送数据,详解看代码注释。

  	//仲裁器优先级检查
  	task do_arbiter_priority_check();
  		int id;
  		//after run to check it
  		forever begin
  			//toggle event : clk posedge and (rstn && f2s_id_req===1) == 1
  			//clk,rstn rasied and formatter send request to arbiter
  			@(posedge this.arb_vif.clk iff (this.arb_vif.rstn && this.arb_vif.mon_ck.f2a_id_req===1));
  			//get channel id
  			id = this.get_slave_id_with_prio();
  			//after got preferential channel id, check whether the priority takes effect
  			if(id >= 0) begin
  				@(posedge this.arb_vif.clk);
  				//formatter request to receive chnl_x data and slvx request send it but arbiter not granted slvx send data
  				//this is not allowed, when slvx.request and fmt.request ==1 ,the arbiter should allow slvx to send data  
  				if(this.arb_vif.mon_ck.a2s_acks[id] !== 1)
  					rpt_pkg::rpt_msg("[CHECKER]",
  					$sformatf("ERROR!  %0t arbiter received f2a_id_req===1 and channel[%0d] raising request with high priorty, but is not granted by arbiter",$time,id),
    			rpt_pkg::ERROR,
    			rpt_pkg::TOP,
    			);
  			end
  		end
  	endtask
 //获取优先级最高的通道id
   function int get_slave_id_with_prio();
  		int id = -1;
  		int prio = 999;
  		//use foeach loop,compare three channel priority,and only the highest priority channel id can be assigned to value id 
  		foreach(this.arb_vif.mon_ck.slv_prios[i]) begin
  			//when highest priority appear && slvx.request == 1, return id 
  			if(this.arb_vif.mon_ck.slv_prios[i] < prio && this.arb_vif.mon_ck.slv_reqs[i]===1) begin
  				id = i;
  				prio = this.arb_vif.mon_ck.slv_prios[i];
  			end
  		end
  		return id;
  	endfunction

添加好该任务后,在checker.run中调用,以检查仲裁器

    task run();
      fork
      	this.do_channel_disable_check(0);
      	this.do_channel_disable_check(1);
      	this.do_channel_disable_check(2);
      	this.do_arbiter_priority_check();
        this.do_compare();
        this.refmod.run();
      join
    endtask

arb_intf接口

interface arb_intf(input clk, input rstn);
  logic [1:0] slv_prios[3];//register port
  logic slv_reqs[3];//slave port
  logic a2s_acks[3];//slave port
  logic f2a_id_req;//formater port
  clocking mon_ck @(posedge clk);
		default input #1ns output #1ns;
		input slv_prios, slv_reqs, a2s_acks, f2a_id_req;
	endclocking
endinterface

连接接口的另一端,设计文件中的仲裁器实例的对应端口

  // arbiter interface monitoring arbiter ports
  assign arb_if.slv_prios[0] = tb.dut.arbiter_inst.slv0_prio_i;
  assign arb_if.slv_prios[1] = tb.dut.arbiter_inst.slv1_prio_i;
  assign arb_if.slv_prios[2] = tb.dut.arbiter_inst.slv2_prio_i;
  assign arb_if.slv_reqs[0] = tb.dut.arbiter_inst.slv0_req_i;
  assign arb_if.slv_reqs[1] = tb.dut.arbiter_inst.slv1_req_i;
  assign arb_if.slv_reqs[2] = tb.dut.arbiter_inst.slv2_req_i;
  assign arb_if.a2s_acks[0] = tb.dut.arbiter_inst.a2s0_ack_o;
  assign arb_if.a2s_acks[1] = tb.dut.arbiter_inst.a2s1_ack_o;
  assign arb_if.a2s_acks[2] = tb.dut.arbiter_inst.a2s2_ack_o;
  assign arb_if.f2a_id_req = tb.dut.arbiter_inst.f2a_id_req_i;

优先级不同时(优先级:00,10,01)的仿真波形:
在这里插入图片描述
优先级相同时(优先级:10,10,10)的仿真波形:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

同样的从arbiter设计文件中可以看到,1和2优先级相同时取的是1通道,2和0相同时取的0通道。所以当三个都相同时0>1>2,。

按照测试要求:如果优先级相同时,arbiter应该采取轮询机制从各个通道接收数据。

轮询机制的实现:就是在优先级相同的情况下,对存有通道id的变量id_sel_r进行判断,先让其等于任意通道id,这个是模拟其初始状态的可能值(每个可能的初始值都要有,用case实现),然后再询问出这个通道外其他两个通道的request信号slvx_req_i,如果该通道有发送请求(slvx_req_i = 1),那么通道id(id_sel_r)和数据包长度变量(a2f_pkglen_sel_r)都要更新成该通道的变量值。
👻使用case判断id时,如果初始id不是0,1,2(在这之前有判断复位信号所以id不可能是3);那么case语句默认(default)执行id为0的情况。
👻 这样在优先级相同的情况下,轮流询问了slave的request信号,当然可能会出现request都为0的情况因此整个轮询机制是在三个通道的req至少有1个是高电平,且优先级相同的情况下进行的。
👻当formatter或slvx没有发送请求时,则保持上一id和pkg_length状态
👻仿真后出现了信息报错,原因在于优先级相同时,执行了轮询机制,它是根据数据从端的req信号选择id_sel_r,与仲裁器的a2s_ack信号无关,如下图,id_sel_r在优先级不同时取决于prio;但在优先级相同时取决于slvx_req_i。
比如假设三个通道的优先级都是1,在获得最优id时,遍历过程是这样的(this.arb_vif.mon_ck.slv_prios[i] < prio),slv_prios[2]<999,所以id=2,prio=1;在下一次比较时,slv_prios[1]<1不成立,所以直接返回id=2,这时候该判断a2s_acks[2]是否不等于1了,那么如下图id_sel_r决定a2s_acks[x],这时候你返回的id就不一定等于id_sel_r,假设根据轮询机制id_sel_r=2’b00,那么a2s_acks[1] = 1, 满足if条件,就会报错。
在这里插入图片描述在这里插入图片描述
因此在判断a2s_acks[id]时先加上三个通道优先级不相等的条件
if(this.arb_vif.mon_ck.slv_prios[0] !== this.arb_vif.mon_ck.slv_prios[1] && this.arb_vif.mon_ck.slv_prios[1] !== this.arb_vif.mon_ck.slv_prios[2])

轮询机制代码:

always @ (posedge clk_i or negedge rstn_i)
begin : CHANEL_SELECT
  if (!rstn_i) id_sel_r  = 2'b11;
  else if (f2a_id_req_i)
  				begin
  					if({slv2_req_i,slv1_req_i,slv0_req_i} != 3'b000)
  						begin
  							if(slv0_prio_i == slv1_prio_i && slv1_prio_i == slv2_prio_i)
  									case(id_sel_i)
  										2'b00: if(slv1_req_i == 1)
  													begin
  														id_sel_r <= 2'b01;
														a2f_pkglen_sel_r = slv1_pkglen_i; 
  													end
  												else if(slv2_req_i == 1)
  													begin
  														id_sel_r <= 2'b10;
														a2f_pkglen_sel_r = slv2_pkglen_i; 
  													end
  															
  										2'b01: if(slv2_req_i == 1)
  													begin
  														id_sel_r <= 2'b10;
														a2f_pkglen_sel_r = slv2_pkglen_i; 
  													end
  												else if(slv0_req_i == 1)
  													begin
  														id_sel_r <= 2'b00;
														a2f_pkglen_sel_r = slv0_pkglen_i; 
  													end
  										
  										2'b10: if(slv0_req_i == 1)
  													begin
  														id_sel_r <= 2'b00;
														a2f_pkglen_sel_r = slv0_pkglen_i; 
  													end
  												else if(slv1_req_i == 1)
  													begin
  														id_sel_r <= 2'b01;
														a2f_pkglen_sel_r = slv1_pkglen_i; 
  													end
  										default: begin
  													id_sel_r <= 2'b00;
													a2f_pkglen_sel_r = slv0_pkglen_i; 
												end
										endcase
  										
  							else
         					case ({slv2_req_i,slv1_req_i,slv0_req_i})  
/原来优先级相同的id选择机制				
		     				endcase 
		     			end
		     		else//这里要再添一个else语句与if({slv2_req_i,slv1_req_i,slv0_req_i} != 3'b000)对应
		     			begin//when no request signal,keep old status
								id_sel_r <= id_sel_r;
								a2f_pkglen_sel_r <= a2f_pkglen_sel_r;
							end 
					end
       else //与if (f2a_id_req_i)对应
				begin//when no request signal,keep old status
				id_sel_r <= id_sel_r;
				a2f_pkglen_sel_r <= a2f_pkglen_sel_r;
				end 
end 

仿真部分波形及打印信息:
在这里插入图片描述
在这里插入图片描述
测试通过!


5.发包长度测试

测试内容:在通道打开的情况下,随机配置个通道的数据包长度,查看结果。
测试类名:mcdf_formatter_length_test
测试代码同mcdf_data_consistence_basic_test一样,只需要修改data_size和wr_val[2:1]为随机(使用$urandom_range随机),检查波形do_reg()里wr_val[2:1]配置的数据包长度和do_data()内随机的data_size是否保持一致。

class mcdf_formatter_length_test extends mcdf_base_test;
		function new(string name = "mcdf_formatter_length_test");
      super.new(name);
    endfunction
    
    task do_reg();
    	bit[31:0] wr_val, rd_val;
      // slv0 with len={4,8,16,32},  prio=0, en=0
      wr_val = ($urandom_range(0,3)<<3)+(0<<1)+1;//32'b 001_00_1,  1左移三位+0左移1位+1
      this.write_reg(`SLV0_RW_ADDR, wr_val);//write a value
      this.read_reg(`SLV0_RW_ADDR, rd_val);//read the value after write
      void'(this.diff_value(wr_val, rd_val, "SLV0_WR_REG"));//compare them

      // slv1 with len={4,8,16,32}, prio=1, en=0
      wr_val = ($urandom_range(0,3)<<3)+(1<<1)+1;//32'b 010_10_1
      this.write_reg(`SLV1_RW_ADDR, wr_val);
      this.read_reg(`SLV1_RW_ADDR, rd_val);
      void'(this.diff_value(wr_val, rd_val, "SLV1_WR_REG"));

      // slv2 with len={4,8,16,32}, prio=1, en=0
      wr_val = ($urandom_range(0,3)<<3)+(2<<1)+1;//32'b 011_10_1
      this.write_reg(`SLV2_RW_ADDR, wr_val);
      this.read_reg(`SLV2_RW_ADDR, rd_val);
      void'(this.diff_value(wr_val, rd_val, "SLV2_WR_REG"));

      // send IDLE command
      this.idle_reg();
  	endtask
  	
  	
  	task do_formatter();
      void'(fmt_gen.randomize() with {fifo == LONG_FIFO; bandwidth == HIGH_WIDTH;});
      fmt_gen.start();
    endtask
    
  	task do_data();
      void'(chnl_gens[0].randomize() with {ntrans==100; ch_id==0; data_nidles==0; pkt_nidles==1; data_size inside{8, 16, 32};});
      void'(chnl_gens[1].randomize() with {ntrans==100; ch_id==1; data_nidles==1; pkt_nidles==4; data_size inside{8, 16, 32};});
      void'(chnl_gens[2].randomize() with {ntrans==100; ch_id==2; data_nidles==2; pkt_nidles==8; data_size inside{8, 16, 32};});
      fork
        chnl_gens[0].start();
        chnl_gens[1].start();
        chnl_gens[2].start();
      join
      #10us; // wait until all data haven been transfered through MCDF
    endtask

这里不需要在checker中添加检查数据包长度的任务了,原因在于checker.do_compare()里会进行这一步,如下图:
事务mont是数据从端传递来的,事务expt是寄存器参考模型out_mb[x]同步过来的事务,前者包含数据包设置的长度data_size,后者包含寄存器配置wr_val[2:1]的数据包长度,二者比较即可完成数据包长度的检查。
在这里插入图片描述

仿真波形
在这里插入图片描述
与打印信息一致
在这里插入图片描述
在这里插入图片描述

  • checker的报告信息为什么计数不是三个100?
  • 为什么总结报告中计数是706,而不是707(参考代码是707)?

6.下行从端低带宽测试

测试内容:将下行fifo设置为小储存,低带宽类型,使gran拉高延迟;检查formatter的req信号拉高后,grant在至少2个clk_cycles后拉高,达到模拟下行从端数据余量不足的情况,并在发生10次这样的时序后停止测试。
测试类名:mcdf_formatter_grant_test
测试代码依旧是使用mcdf_data_consistence_basic_test,需要修改的是,fifo类型以及带宽,还有就是增加数据包的数量,减少data和data,packet和packet的空闲周期,以达到庞大的数据压力,消耗fifo数据余量,这样req信号就会频繁拉高,然后再检查grant时序是否符合期望。

class mcdf_formatter_grant_test extends mcdf_base_test;
	function new(string name = "mcdf_data_consistence_basic_test");
      super.new(name);
    endfunction
//配置了三个通道读写寄存器的写操作和读操作
    task do_reg();
      bit[31:0] wr_val, rd_val;
      // slv0 with len=8,  prio=0, en=1
      wr_val = (1<<3)+(0<<1)+1;//32'b 001_00_1,  1左移三位+0左移1位+1
      this.write_reg(`SLV0_RW_ADDR, wr_val);//write a value
      this.read_reg(`SLV0_RW_ADDR, rd_val);//read the value after write
      void'(this.diff_value(wr_val, rd_val, "SLV0_WR_REG"));//compare them

      // slv1 with len=16, prio=1, en=1
      wr_val = (2<<3)+(1<<1)+1;//32'b 010_01_1
      this.write_reg(`SLV1_RW_ADDR, wr_val);
      this.read_reg(`SLV1_RW_ADDR, rd_val);
      void'(this.diff_value(wr_val, rd_val, "SLV1_WR_REG"));

      // slv2 with len=32, prio=2, en=1
      wr_val = (3<<3)+(2<<1)+1;//32'b 011_10_1
      this.write_reg(`SLV2_RW_ADDR, wr_val);
      this.read_reg(`SLV2_RW_ADDR, rd_val);
      void'(this.diff_value(wr_val, rd_val, "SLV2_WR_REG"));

      // send IDLE command
      this.idle_reg();
    endtask
//对formatter下行buffer进行配置
    task do_formatter();
      void'(fmt_gen.randomize() with {fifo == SHORT_FIFO; bandwidth == LOW_WIDTH_WIDTH;});
      fmt_gen.start();
    endtask

    task do_data();//add packet numbers,reduce idles of data and packet, frequent data transition, for rasiing req signal constantly
      void'(chnl_gens[0].randomize() with {ntrans==300; ch_id==0; data_nidles==0; pkt_nidles==1; data_size==8; });
      void'(chnl_gens[1].randomize() with {ntrans==300; ch_id==1; data_nidles==0; pkt_nidles==1; data_size==16;});
      void'(chnl_gens[2].randomize() with {ntrans==300; ch_id==2; data_nidles==0; pkt_nidles==1; data_size==32;});
      fork
        chnl_gens[0].start();
        chnl_gens[1].start();
        chnl_gens[2].start();
      join
      #10us; // wait until all data haven been transfered through MCDF
    endtask
endclass

检查时序的代码

task check_req_grant(virtual fmt_intf fmt_vif); 
	this.fmt_vif = fmt_vif;
 forever begin
		@(posedge fmt_vif.clk iff (fmt_vif.rstn && fmt_vif.mon_ck.fmt_req === 1 ));
		this.delay_req_to_grant = 0;
	forever begin
		  @(posedge fmt_vif.clk);
		if(this.fmt_vif.mon_ck.fmt_grant == 1)
			break;
        else begin 
            @(posedge fmt_vif.clk);
            this.delay_req_to_grant++;
           end
      end
        if(this.delay_req_to_grant >= 2)
			this.delay_success_count++;
		else
			$error("[ERROR] CHECKER delay req to grant :%0t formatter req===1 , but delay req to grant not beyond two ", $time);
        if(this.delay_success_count == 10) begin
        	$display("check_delay_req_to_grant SUCCED 10 times");
        	$finish;
        end
        else continue;
 end
endtask

然后在tb中调用,并传递fmt_intf例化的接口fmt_if,这样可以不用在base_test中添加额外代码。(👻也可以在base_test内声明接口,然后在set_interface里同步接口,但是不要忘记在base_test中写一个内容为空的同名的虚方法)

 fork
      tests[name].run();
      if(name=="mcdf_formatter_grant_test") tests[name].check_req_grant(fmt_if);
 join_any

仿真波形
在这里插入图片描述
在这里插入图片描述

可以看到在前15次的req拉高以后(这里我的代码忘记添加error的计数了,如果加上的话最后计数应该会是15),紧接着下一个时钟周期grant就拉高了。在第16次以后才会出现下行从端fifo容量不足,grant延迟拉高的情况,然后在出现10次这种情况后就结束了测试。接下来可以试着修改fifo的类型和带宽为MED_FIFO和MED_WIDTH,然后查看仿真波形。
在这里插入图片描述在这里插入图片描述
仿真跑的时间更长了。大胆猜测一下如果让仿真一直跑下去,低容量,低带宽的fifo类型是不是会花费更多的仿真时间才能跑完整个测试。
完整测试:
{SHORT_FIFO,LOW_WIDTH}
在这里插入图片描述

{MED_FIFO,MED_WIDTH}
在这里插入图片描述
{LONG_FIFO,HIGH_WIDTH}
在这里插入图片描述

在这里插入图片描述

可以看到,大容量,大带宽的fifo确实可以减少仿真时间,但对于ntrans=300,且datasize=8,16,32的,LONG_FIFO,HIGH_FIFO已经是最快的了。
测试通过!


补充:不改变tb,检查延迟并实现10次后停止仿真

  • 首先是在测试类中完成check任务的编写,区别于之前在检查完10次后不直接调用系统函数$finish,使用break跳出循环结束任务。
  • 然后需要将任务用到的fmt_intf接口信号与tb层连接,我们可以直接将fmt_vif的声明放在base_test父类中,然后在set_interface里同步接口即可完成接口的连接。
  • 最后是调用check任务,这个任务不能调用需要和do_data()任务并行调用,且两个线程只要完成一个就可以进行do_report(),$finish(),若在do_data之前调用不会有数据发送,陷入死循环,也不能在其后调用,否则数据发送完毕依旧会陷入循环。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    仿真结果:
    在这里插入图片描述

在这里插入图片描述


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 路科验证v2 mcdf代码是用于验证MCDF(多配置自洽场)方法在路科库中的实现的一种代码。MCDF方法是一种计算分子的电子结构和能量的量子化学方法,它将电子排布在一组给定的分子轨道上,并利用自洽场方法来优化这些轨道和分子的电子能量。MCDF方法在化学计算中被广泛应用,用于预测分子的光谱性质、反应能垒等。 在路科验证v2 mcdf代码中,主要包括以下几个步骤: 1. 初始化:代码首先需要初始化所需的参数,例如分子结构、基组选择等。 2. 核心计算:接下来,代码会使用MCDF方法计算分子的电子结构和能量。这包括求解含时无关Hartree-Fock方程、计算分子的电子密度等。 3. 自洽场迭代:在MCDF方法中,自洽场迭代是一个重要的步骤。代码会通过调整分子轨道和电子密度来达到自洽场的要求,直到达到收敛条件(例如轨道能量变化很小)为止。 4. 结果输出:最后,代码将会输出计算得到的分子的能量、电子结构和其他相关的物理量。这些结果可以用于进一步的分析和研究。 总之,路科验证v2 mcdf代码是用于验证MCDF方法在路科库中的实现的一种代码,通过计算分子的电子结构和能量,为进一步的量子化学计算提供基础。 ### 回答2: 路科验证v2 MCDF代码是一种用于验证机器学习模型性能的工具。MCDF(模型复杂度和数据难度)是一种评估机器学习模型性能的指标,可以帮助我们了解模型的泛化能力以及在不同数据难度下的表现。 路科验证v2 MCDF代码包括几个步骤: 1. 数据准备:首先,需要准备数据集来进行模型验证。可以选择一个已有的数据集,也可以根据需求自己生成一个数据集。数据集的特征和标签应该是清晰明确的,且特征和标签的数量应该是一样的。 2. 模型选择:根据需求选择适合的机器学习模型,例如决策树、支持向量机等。根据选择的模型,确定需要调整的参数。 3. 路科验证:路科验证是一种交叉验证的方法,在训练和验证的过程中,将数据集划分为K个相等大小的折(或区间),其中K-1个折用于训练,剩下的1个折用于验证。该过程会进行K次,每次会选择不同的验证折。通过路科验证可以获得模型在不同数据子集上的表现情况。 4. MCDF计算:MCDF是根据不同的数据难度和模型复杂度计算的。数据难度可以通过调整不同的训练和验证集合比例来获得。模型复杂度则是根据选择的机器学习模型和调整的参数来得到。MCDF计算可以通过统计模型在不同数据子集上的准确率、精确率、召回率等指标来得到。 通过路科验证v2 MCDF代码,我们可以了解到机器学习模型在不同数据子集上的表现,评估模型的泛化能力,了解模型的优势和不足,并根据结果进一步调整模型和参数,提高模型的性能。 ### 回答3: 为了回答你的问题,我首先需要解释一下相关的背景信息。路科验证 (Lucas-Lehmer test) 是一种用于验证 Mersenne 数的质数性质的算法,而 v2 mcdf 代码则是实现了这种验证方法的计算机程序。 路科验证基于费马小定理和二次剩余定理,用于判断形如 2^n - 1 的数是否为质数。具体的算法如下: 1. 初始化:选择一个整数 n,通常要求 n 是质数,并且计算 s = 4 - 2^(n-1)。 2. 迭代计算:对于 i 从 2 到 n-1,重复以下步骤: a. 计算 s = (s^2 - 2) mod (2^n - 1)。 3. 结果验证:若 s 等于零,则 2^n - 1 是一个 Mersenne 质数。 v2 mcdf 代码是一种对路科验证算法的实现,用于在计算机上自动执行验证计算。这个代码可能是一种特定的程序或者函数,其输入为一个数字 n,通过计算得出验证结果。 使用 v2 mcdf 代码进行路科验证的步骤如下: 1. 根据你的需求选择一个合适的 n 值。 2. 利用 v2 mcdf 代码进行验证计算,输入 n,并获得验证结果。 3. 如果验证结果为真,则 2^n - 1 是一个 Mersenne 质数;如果验证结果为假,则不是。 需要注意的是,路科验证算法是一种可以在合理的时间内进行的算法,但对于非常大的 n 值,计算可能会非常耗时。因此,考虑到计算资源和时间限制,选择合适的 n 值进行验证是非常重要的。 希望这个回答能够帮助你了解路科验证和 v2 mcdf 代码的基本原理和使用方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值