MCDF实验_lab2(2)

一、实验要求

  1. 定义slave与MCDF之间的接口为chnl_intf,并在接口中定义时钟块消除可能存在的竞争问题

  2. 用类实现
    激励产生器:chnl_generator
    激励发送器:chnl_initiator;调用 chnl_iniaitor 中的方法来完成接口的传递
    发送数据的类:chnl_trans

  3. 用类实现测试
    chnl_basic_test 实现三个 chnl_initiator 同时发送数据的要求,能结束测试;
    chnl_burst_test 实现每个 chnl_initiator 的idle_cycles设置为0,同时发送 500 个数据, 最后结束测试;
    chnl_basic_test 实现在三个 channel 都拉低过 ready 时(不必要同时拉低,
    先后拉低即可), 可以立即结束测试。

  4. 将各个类封装到package chnl_pkg 中
    package chnl_pkg; endpackge: chnl_pkg

二、代码分析

时间单位

`timescale 1ns/1ps// 单位/精度

信号

每个通道的数据通过实现的接口输入到dut中

logic          clk;
logic          rstn;

logic [31:0]   mcdt_data;//DUT输出当前输出的数据
logic          mcdt_val; //DUT输出当前输出的有效信号
logic [ 1:0]   mcdt_id;  //DUT输出当前输出的通道号

例化

将tb的信号与dut的端口相连

mcdt dut(
     .clk_i       (clk                )
    ,.rstn_i      (rstn               )
    ,.ch0_data_i  (chnl0_if.ch_data   )
    ,.ch0_valid_i (chnl0_if.ch_valid  )
    ,.ch0_ready_o (chnl0_if.ch_ready  )
    ,.ch0_margin_o(chnl0_if.ch_margin )
    ,.ch1_data_i  (chnl1_if.ch_data   )
    ,.ch1_valid_i (chnl1_if.ch_valid  )
    ,.ch1_ready_o (chnl1_if.ch_ready  )
    ,.ch1_margin_o(chnl1_if.ch_margin )
    ,.ch2_data_i  (chnl2_if.ch_data   )
    ,.ch2_valid_i (chnl2_if.ch_valid  )
    ,.ch2_ready_o (chnl2_if.ch_ready  )
    ,.ch2_margin_o(chnl2_if.ch_margin )
    ,.mcdt_data_o (mcdt_data          )
    ,.mcdt_val_o  (mcdt_val           )
    ,.mcdt_id_o   (mcdt_id            )
  );

产生时钟

initial begin 
    clk <= 0;
    forever begin
      #5 clk <= !clk;
    end
end

产生复位

initial begin 
    #10 rstn <= 0;
    repeat(10) @(posedge clk);
    rstn <= 1;
end

接口chnl_intf

slave与dut之间的接口,四个传输的数据;
定义clocking,所有和clocking有关的信号都与时序逻辑有关;在时钟上升沿前一秒采样输入信号,在时钟上升沿后一秒采样输出信号

interface chnl_intf(input clk, input rstn);
  logic [31:0] ch_data;
  logic        ch_valid;
  logic        ch_ready;
  logic [ 5:0] ch_margin;
  clocking drv_ck @(posedge clk);
    default input #1ns output #1ns;
    output ch_data, ch_valid;
    input ch_ready, ch_margin;
  endclocking
endinterface

发生数据的类chnl_trans

数据;通道号;第几个数据

class chnl_trans;
	int data;
	int id;
	int num;
endclass: chnl_trans

激励发送器chnl_initiator

调用 chnl_iniaitor 中的方法来完成接口的传递
输入:name;cycle;接口
通过接口号可以判断发送的通道号
接口一定要用virtual修饰

class chnl_iniaitor;
	local string name;//激励发生器的名字
	local int idle_cycles;//间隔几个cycle发送激励
	local virtual chnl_intf intf;//接口
	
	function new(string name = "chnl_initiator");//构造函数,初始化name和cycle
		this.name = name;
		this.idle_cycles = 1;
	endfunction
	
	function void set_name(string s);//外部可以修改name
      this.name = s;
    endfunction
    
	function void set_idle_cycles(int n);//外部可以修改cycle
      this.idle_cycles = n;
    endfunction
	
	function void set_interface(virtual chnl_intf intf)//外部将接口传入
		if(intf == null)//传入为空指针,则报错,提醒需要实例化接口
			$error("interface handle is NULL, please check if target interface has been intantiated");
		else
        	this.intf = intf;
    endfunction

	task chnl_write(input chnl_trans t);//写数据task;将数据写入接口
		@(posedge intf.clk);//模拟时序逻辑
		intf.drv_ck.ch_valid <= 1;
		intf.drv_ck.ch_data  <= t.data;
		
		@(negedge intf.clk);//不然可能采到发送data之前的ready信号

		wait(intf.ch_ready === 'b1);//模拟组合逻辑
		$display("%t channel initiator [%s] sent data %x", $time, name, t.data);
		
		repeat(this.idle_cycles) chnl_idle();//插入无效序列
	endtask
	task chnl_idle(input chnl_trans t)
		@(posedge intf.clk);//模拟时序逻辑
		intf.drv_ck.ch_valid <= 0;
		intf.drv_ck.ch_data  <= 0;
	endtask
endclass: chnl_iniaitor

激励产生器:chnl_generator

队列存储数据
获取该队列中数据的函数
输入:id用于得到不同通道的不同数据

class chnl_generator;
	chnl_trans trans[$];//该实验没用该队列,应该把每次的激励序列存储起来
    int num;
    int id;
	function new(int chnl);
      this.id = chnl;
      this.num = 0;
    endfunction
	function chnl_trans get_trans();
		chnl_trans t = new();//创建对象
		t.data = 'h00C0_0000 + (this.id << 16) + this.num;
		t.id   = this.id;
		t.num  = this.num;
		this.num++;
		this.trans.push_back(t);
		return t;
	endfunction
endclass: chnl_generator

数据发送chnl_agent:将chnl_generator与chnl_initiator封装在一起

利用chnl_initiator与chnl_generator发送多个数据
将接口传入chnl_initiator
run为运行函数,需要实例化后逐层调用
输入通道名称,通道id

class chnl_agent;
	chnl_generator gen;
    chnl_initiator init;
    local int ntrans;//设置数据个数
    virtual chnl_intf vif;
	
	function new(string name = "chnl_agent", int id = 0, int ntrans = 1)
		this.gen = new(id);
		this.init = new(name);
		this.ntrans = ntrans;
	endfuntion

	function void set_ntrans(int n);
      this.ntrans = n;
    endfunction
	
	function void set_interface(virtual chnl_intf vif);
      this.vif = vif;
      init.set_interface(vif);
    endfunction
	
	task run();
		repeat(this.ntrans) this.init.chnl_write(this.gen.get_trans());
		this.gen.chnl_idle();// set idle after all data sent out
	endtask
endclass: chnl_agent

测试基函数chnl_root_test

调用agent的run函数开始测试用例
将接口传入agent中

class chnl_root_test;
	protected string name;
	
	//声明与实例化三个agent
	chnl_agent agent[3];
	function new(int ntrans = 100, string name = "chnl_root_test");
		foreach(agent[i]) begin
			this.agent[i] = new($psprintf("chnl_agent%0d",i), i, ntrans);
		end
		this.name = name;
		$display("%s instantiate objects", this.name);
	endfunction
	
	task run();
      $display("%s started testing DUT", this.name);
      fork//并行运行,发送数据
        agent[0].run();
        agent[1].run();
        agent[2].run();
      join
      $display("%s waiting DUT transfering all of data", this.name);
      
	  fork//直到数据全部发送完成,fifo全为满
        wait(agent[0].vif.ch_margin == 'h20);
        wait(agent[1].vif.ch_margin == 'h20);
        wait(agent[2].vif.ch_margin == 'h20);
      join
      $display("%s: 3 channel fifos have transferred all data", this.name);
      $display("%s finished testing DUT", this.name);
	endtask
	
	function void set_interface(virtual chnl_intf ch0_vif, virtual chnl_intf ch1_vif, virtual chnl_intf ch2_vif);//将接口传入agent中
      agent[0].set_interface(ch0_vif);
      agent[1].set_interface(ch1_vif);
      agent[2].set_interface(ch2_vif);  
    endfunction
endclass: chnl_root_test

chnl_basic_test

发送200数据,之间有随机个空闲状态,能结束测试

class chnl_basic_test extends chnl_root_test;
	function new(int ntrans = 200, string name = "chnl_basic_test");
		super.new(ntrans, name);
		foreach(agent[i]) begin
			this.agent[i].init.set_idle_cycles($urandom(1,3));
		end
		$display("%s configured objects", this.name);
	endfunction
endclass: chnl_basic_test

chnl_burst_test

每个 chnl_initiator 的idle_cycles设置为0,同时发送 500 个数据, 最后结束测试;

class chnl_burst_test extends chnl_root_test;
	function new(int ntrans = 500, string name = "chnl_burst_test");
		super.new();
		foreach(agent[i]) begin
			this.agent[i].init.set_idle_cycles(0);
		end
		$display("%s configured objects", this.name);
	endfunction
endclass: chnl_burst_test

chnl_fifo_full_test

三个 channel 都拉低过 ready (fifo为空) 时(不必要同时拉低,先后拉低即可), 可以立即结束测试
fork join_none多线程
开辟子线程端口发送数据;主线程检测接口的fifo是否为空,三个都为空时关闭子线程,子线程不再发送数据;主线程端口发送无效数据,确保所有激励发送器处于idle状态;等待fifo为满结束测试

class chnl_fifo_full_test extends chnl_root_test;
   
    function new(int ntrans = 1_000_000, string name = "chnl_fifo_full_test");
    	super.new(ntrans, name);
    	foreach(agent[i]) begin
	       this.agent[i].init.set_idle_cycles(0);
        end
      	$display("%s configured objects", this.name);
    endfunction

	task run();
		$display("%s started testing DUT", this.name);
		fork: fork_all_run
	        agent[0].run();
	        agent[1].run();
	        agent[2].run();
	    join_none
        $display("%s: 3 agents running now", this.name);
        $display("%s: waiting 3 channel fifos to be full", this.name);
        fork
	        wait(agent[0].vif.ch_margin == 0);
	        wait(agent[1].vif.ch_margin == 0);
	        wait(agent[2].vif.ch_margin == 0);
	    join
	    $display("%s: 3 channel fifos have reached full", this.name);
      	$display("%s: stop 3 agents running", this.name);
      	
		disable fork_all_run;//关闭发送数据的线程

		$display("%s: set and ensure all agents' initiator are idle state", this.name);
        fork
	        agent[0].init.chnl_idle();
	        agent[1].init.chnl_idle();
	        agent[2].init.chnl_idle();
        join
		
		$display("%s waiting DUT transfering all of data", this.name);
		fork
	        wait(agent[0].vif.ch_margin == 'h20);
	        wait(agent[1].vif.ch_margin == 'h20);
	        wait(agent[2].vif.ch_margin == 'h20);
	    join
	    
        $display("%s: 3 channel fifos have transferred all data", this.name);
        $display("%s finished testing DUT", this.name);
  
	endtask
endclass: chnl_fifo_full_test

导包并实例化接口与测试类

接口中的.*是什么意思呢?

import chnl_pkg::*;
//实例化三个接口
chnl_intf chnl0_if(.*);
chnl_intf chnl1_if(.*);
chnl_intf chnl2_if(.*);
//实例化三个要运行的测试类
chnl_basic_test basic_test;
chnl_burst_test burst_test;
chnl_fifo_full_test fifo_full_test;

为激励发生器的对象指定接口句柄,开始测试

initial begin
	basic_test = new();//创建测试对象
	burst_test = new();
    fifo_full_test = new();
    
	basic_test.set_interface(chnl0_if, chnl1_if, chnl2_if);//将实例化的接口通过测试类逐级传入
    burst_test.set_interface(chnl0_if, chnl1_if, chnl2_if);
    fifo_full_test.set_interface(chnl0_if, chnl1_if, chnl2_if);
    
	basic_test.run(); //逐级调用run()运行测试用例
    burst_test.run();
    fifo_full_test.run();
    $display("*****************all of tests have been finished********************");
    $finish();

end

三、Questasim的Makefile

#############################
# User variables
#############################
TB       = tb1
DFILES   = {arbiter.v,slave_fifo.v,mcdt.v}
VFILES   = $(TB).v 
#############################
# Environment variables
#############################
VLAB			     = vlib work #创建名为work的lib
VCOMP          		 = vlog -l com.log #vlog编译;-l com.log将日志存放到com.log中
VSTART				 = vsim -l sim.log #vsim启动questa
VSIMULATE			 = -voptargs=+acc #simulate
VADDWAVE 			 = add wave -position insertpoint sim:/$(TB)/*#添加top文件的波形
VSAVE          		 = log -r /*#生成波形文件
RUN            		 = run 1us #运行1ns
VQUIT				 = quit -f #退出

all: create_lib compile simulate #make要执行的顺序

create_lib:
	$(VLAB)
	
compile:
	$(VCOMP) $(DFILES) $(VFILES)

simulate:
	#$(VSTART) -c $(VSIMULATE) work.$(TB) -do "$(SAVE) $(RUN);$(VQUIT)"
	$(VSTART) $(VSIMULATE) work.$(TB) -do "$(VADDWAVE);$(VSAVE);$(RUN)"
	
clean:
	rm -rf work mti_lib transcript modelsim.ini *.log vsim.wlf

四、波形

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小鱼爱学习,每天好心情

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

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

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

打赏作者

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

抵扣说明:

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

余额充值