SystemVerilog课程笔记(六)

1. 将数据传输从结构体改造为类

1.1 改造

1.2 改造后的tb.sv

class rt_packet;
  bit [3:0] src;
  bit [3:0] dst;
  bit [7:0] data [];
  function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []);
	this.src = src;
	this.dst = dst;
	this.data = data;
  endfunction
endclass

interface rt_interface();
	logic clock;
	logic reset_n;
	logic [15:0] din;
	logic [15:0] frame_n;
	logic [15:0] valid_n;
	logic [15:0] dout;
	logic [15:0] valido_n;
	logic [15:0] busy_n;
	logic [15:0] frameo_n;
endinterface
//********************************** stimulator **********************************//
module rt_stimulator(
	rt_interface intf
);
//for debug purpose from waveform	//定义检测状态的变量
  typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t;
  drv_state_t dbg_state;  
  byte unsigned dbg_din_chnl0_data; 
  int src_chnl_status[int];  //关联数组,后面int:src_chnl的number;前面int:dest_chnl的Number ??
	
//generator传送p给stimulator
  rt_packet pkts[$];//定义stimulator中的的队列pkts
  function void put_pkt(input rt_packet p);
    pkts.push_back(p);	//将generator中传过来的p放入stimulator的pkts中(在pkts队列尾插入p)
  endfunction

//reset阶段,复位时,reset_n为低电平,frame_n和valid_n为高电平
initial begin : drive_reset_proc 
  drive_reset();
end
task drive_reset();
  @(negedge intf.reset_n);
  dbg_state <= DRV_RESET;
  intf.din <= 0;
  intf.frame_n <= '1;//等效16'hFFFF
  intf.valid_n <= '1;
endtask

// 发送数据
initial begin : drive_chnl_proc
	//rt_packet_t p;
  @(negedge intf.reset_n);
  repeat(10) @(posedge intf.clock);//延迟10个时钟周期
  forever begin	
	automatic rt_packet p;//声明一个动态的
	wait(pkts.size()>0);
	p = pkts.pop_front();	//将p从队列pkts里面取出
	fork//后台触发线程,触发线程在后台运行,继续执行剩下内容
		begin	
			wait_src_chnl_avail(p);//判断src chnl是否被占用,是否需要等待
			drive_chnl(p.src, p.dst, p.data);//从P中拿到发送端口,目标端口,发送数据
			set_src_chnl_avail(p);
		end
	join_none
	end
end

task automatic wait_src_chnl_avail(rt_packet p);//判断src chnl是否被占用,是否需要等待
	if(!src_chnl_status.exists(p.src))//src_chnl_status关联数组里面不含p.src,(表示未占用任何dest_chnl,一开始不满足)
		src_chnl_status[p.src] = p.dst;//表示当前正在使用src_chnl,对应dest_chnl就是p.dst(p.src端口在向p.dst端口发送数据)
	else if(src_chnl_status[p.src] >= 0)//如果在给0,1,2...dest_chnl发送数据(被占用),需要等待,,否则不需要等待
		wait(src_chnl_status[p.src] == -1);//直到src_chnl_status[p.src] == -1(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
endtask

function automatic set_src_chnl_avail(rt_packet p);
	src_chnl_status[p.src] = -1;//如果chnl发送完后,把src_chnl置回来(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
endfunction

task automatic drive_chnl(bit[3:0] saddr, bit [3:0] daddr, byte unsigned data[]);
  $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans started",$time,saddr,daddr);
	// drive address phase 输入地址位阶段
for(int i=0;i<4;i++)begin  //4 clock
  @(posedge intf.clock);
  dbg_state <=DRV_ADDR;	
  intf.din[saddr] <= daddr[i];
  intf.valid_n[saddr] <= $urandom_range(0,1);	//valid_n在din的地址输入时间段可为任意值x
  intf.frame_n[saddr] <= 1'b0;	//frame_n需要为低
end
	// drive pad phase //隔离阶段
  for (int i=0;i<5;i++)begin  //5 clock
    @(posedge intf.clock);
    dbg_state <=DRV_PAD;
    intf.din[saddr] <= 1'b1;
    intf.valid_n[saddr] <= 1'b1;	//valid_n需为高电平
    intf.frame_n[saddr] <= 1'b0; //frame_n需为低电平
  end
	// drive data phase 传输数据阶段
  foreach(data[id])begin
    for(int i=0;i<8;i++)begin
     @(posedge intf.clock);
      dbg_state <=DRV_DATA;
      dbg_din_chnl0_data <= data[id];
      intf.din[saddr] <= data[id][i];
      intf.valid_n[saddr] <=1'b0;
      intf.frame_n[saddr] <= (id == data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高
    end
  end
// drive idle phase 闲置(没有数据传输)阶段
  @(posedge intf.clock);
  dbg_state <=DRV_IDLE;
  dbg_din_chnl0_data <= 0;
  intf.din[saddr] <= 1'b0;
  intf.valid_n[saddr] <= 1'b1;
  intf.frame_n[saddr] <= 1'b1;
  $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,saddr,daddr);
endtask
endmodule

//********************************** generator **********************************//
module rt_generator;	//generator产生数据交给stimulator
  rt_packet pkts[$];	//定义队列

  function void put_pkt(input rt_packet p);
    pkts.push_back(p);	//将p放入队列pkts里面(在pkts队列尾插入p)
  endfunction
  
  task get_pkt(output rt_packet p);
    wait(pkts.size() >0 )	//队列不为空
      p = pkts.pop_front();	//将p从队列pkts里面取出,提取队列首元素
  endtask

  //generate a random packet
  function void gen_pkt(int src = -1, int dst = -1);
  endfunction
endmodule
//********************************** monitor **********************************//
module rt_monitor(rt_interface intf);
	rt_packet in_pkts[16][$];
	rt_packet out_pkts[16][$];
	initial begin : mon_chnl_proc
		foreach(in_pkts[i]) begin
			automatic int chid = i;
			fork
			mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入
			mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入
			join_none
		end
	end
	
task automatic mon_chnl_in (bit[3:0]id);//监测数据输入的任务
	rt_packet pkt;	//定义结构体变量
	forever begin
	//clear content for the same struct variable。清除pkt
	pkt = new();
	pkt.src = id;	//第id个输入端口
	//monitor specific channel-in data and put it into the queue
	// monitor address phase
	@(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动)
	for(int i=0; i<4; i++)begin
		@(negedge intf.clock);		//frame_n下降沿后监测4个clk negedge
		pkt.dst[i]= intf.din[id];
		end
		$display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst);
	//pass pad phase 不考虑pad阶段是否满足协议要求
	repeat(5) @(negedge intf.clock);
	do begin
		pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data
		for ( int i=0; i<8; i++) begin
			@(negedge intf.clock); //在8个clk negedge监测8bit数据
			pkt.data[pkt.data.size-1][i] = intf.din[id];
		end
	end while(!intf.frame_n[id]);
	in_pkts[id].push_back(pkt);	//将monitor拿到的数据放入in_pkts
	$display("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans [%0p] finished",$time,pkt.src,pkt.dst,pkt.data);
	end
endtask

task automatic mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务
	rt_packet pkt;
	forever begin
		//clear content for the same struct variable
		pkt = new();
		pkt.src = 0;
		pkt.dst = id;	
		@(negedge intf.frameo_n[id]);
		$display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst);
		do begin
		pkt.data = new [ pkt.data.size + 1](pkt.data);
		for(int i=0;i<8; i++) begin
			@(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低
			pkt.data[pkt.data.size-1][i]= intf.dout [id];
		end
	end while(!intf.frameo_n[id]);
	out_pkts[id].push_back(pkt);
	$display("@%0t: [MON] CH_OUT dest_chn1[%0d] data trans [%0p] finished",$time,pkt.dst,pkt.data);
	//monitor specific channel-out data and put it into the queue
	end
endtask

endmodule

//********************************** tb **********************************//
module tb;

bit clk,rstn;
logic [15:0] din, frame_n, valid_n;
logic [15:0] dout, valido_n, busy_n, frameo_n;

// 产生时钟,周期为10ns
initial 
    forever #5ns clk <= !clk;

// 产生复位信号
  initial begin
    #2ns rstn <= 1;
    #10ns rstn <= 0;
    #10ns rstn <= 1;
  end

//例化router为DUT
router dut(           
  .reset_n(rstn),
  .clock(clk),
  .frame_n(intf.frame_n),
  .valid_n(intf.valid_n),
  .din(intf.din),
  .dout(intf.dout),
  .busy_n(intf.busy_n),
  .valido_n(intf.valido_n),
  .frameo_n(intf.frameo_n)
);

rt_interface intf();//例化接口
	assign intf.reset_n = rstn;
	assign intf.clock = clk;
	
//例化stimulator
rt_stimulator stim(intf);
//例化Monitor
rt_monitor mon(intf);
//例化generator
rt_generator gen();
	
initial begin:generate_data_proc//产生数据
		rt_packet p;
		p = new();
		p.set_members(0,3,'{8'h33,8'h77});
		gen.put_pkt(p);
		p = new();//每次put_pkt都需要new一下
		p.set_members(0,5,'{8'h55,8'h66});
		gen.put_pkt(p);
		p = new();
		p.set_members(3,6,'{8'h77,8'h88,8'h22});
		gen.put_pkt(p);
		p = new();
		p.set_members(4,7,'{8'haa,8'hcc,8'h33});
		gen.put_pkt(p);
	end
initial begin:genarator_to_stimulator_proc//取出genarator中的数据给stimulator
	rt_packet p;
	forever begin
		gen.get_pkt(p);
		stim.put_pkt(p);
	end
end

endmodule 

1.3 仿真结果

2. 将drive_chnl任务的输入改为类

2.1 改造

(1)增加打印函数

  function string sprint();//打印packet信息的函数
	sprint = {sprint , $sformatf("src = %0d\n",src)};
	sprint = {sprint , $sformatf("dst = %0d\n",dst)};
	sprint = {sprint , $sformatf("data_length = %0d\n",data.size())};
	foreach(data[i])
		sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])};
  endfunction

(2)将drive_chnl任务的输入改为类

task automatic drive_chnl(rt_packet p);

2.2 改造后的tb.sv

class rt_packet;
  bit [3:0] src;
  bit [3:0] dst;
  bit [7:0] data [];
  function new();
  endfunction
  function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []);
	this.src = src;
	this.dst = dst;
	this.data = data;
  endfunction
  function string sprint();
	sprint = {sprint , $sformatf("src = %0d\n",src)};
	sprint = {sprint , $sformatf("dst = %0d\n",dst)};
	sprint = {sprint , $sformatf("data_length = %0d\n",data.size())};
	foreach(data[i])
		sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])};
	endfunction
    endclass
//**********************************  rt_stimulator  ***********************************//
interface rt_interface;
	logic clock;
	logic reset_n;
	logic [15:0] din;
	logic [15:0] frame_n;
	logic [15:0] valid_n;
	logic [15:0] dout;
	logic [15:0] valido_n;
	logic [15:0] busy_n;
	logic [15:0] frameo_n;
endinterface
	
module rt_stimulator(
  rt_interface intf
);
//for debug purpose from waveform
	typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t;
	drv_state_t dbg_state;	//定义状态检测变量dbg_state
	byte unsigned dbg_din_chnl0_data;
	rt_packet pkts[$];//队列里面存放的将是rt_packet句柄而不再是实际的对象
	int src_chnl_status[int];  //关联数组,后面int:src_chnl的number;前面int:dest_chnl的Number ??

	function void put_pkt(input rt_packet p);
		pkts.push_back(p);
	endfunction

	initial begin : drive_reset_proc
		drive_reset();
	end
	task drive_reset();
		@(negedge intf.reset_n);
		dbg_state <= DRV_RESET;
		intf.din <= 0;
		intf.frame_n <= '1;
		intf.valid_n <= '1;
	endtask

	initial begin : drive_chnl_proc
		@(negedge intf.reset_n);
		repeat(10) @(posedge intf.clock);
		forever begin 
			automatic rt_packet p;//声明一个动态的
			wait(pkts.size() > 0);
			p = pkts.pop_front();
			fork		//后台触发线程,触发线程在后台运行,继续执行剩下内容
				begin
				wait_src_chnl_avail(p);
				drive_chnl(p);
				set_src_chnl_avail(p);
				end
			join_none
		end
	end
	
	task automatic wait_src_chnl_avail(rt_packet p);//判断src chnl是否被占用,是否需要等待
	if(!src_chnl_status.exists(p.src))//动态数组为空
		src_chnl_status[p.src] = p.dst;//表示当前正在使用src_chnl,对应dest就是p.dst
	else if(src_chnl_status[p.src] >= 0)//如果给dest的0,1,2这样的chnl在发送数据,需要等待
		wait(src_chnl_status[p.src] == -1);//直到
	endtask
	
	function automatic set_src_chnl_avail(rt_packet p);
		src_chnl_status[p.src] = -1;//如果chnl发送完后,把src_chnl置回来
	endfunction

	task automatic drive_chnl(rt_packet p);
		$display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n %s",$time,p.src,p.dst,p.sprint());
// drive address phase 
		for(int i=0; i<4; i++)begin
		@(posedge intf.clock);
		dbg_state <=DRV_ADDR;
		intf.din[p.src] <= p.dst[i];
		intf.valid_n[p.src] <= $urandom_range(0,1);
		intf.frame_n[p.src] <= 1'b0;
	end
// drive pad phase 
  for (int i=0; i<5; i++)begin
    @(posedge intf.clock);
    dbg_state <= DRV_PAD;
	intf.din[p.src] <= 1'b1;
    intf.valid_n[p.src] <= 1'b1;
    intf.frame_n[p.src] <= 1'b0;
  end
// drive data phase
  foreach(p.data[id])begin
	for(int i=0;i<8; i++)begin
    @(posedge intf.clock);
    dbg_state <= DRV_DATA;
    dbg_din_chnl0_data <= p.data[id];
    intf.din[p.src] <= p.data[id][i];
    intf.valid_n[p.src] <=1'b0;
    intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;
    end
  end
// drive idle phase
  @(posedge intf.clock);
  dbg_state <= DRV_IDLE;
  dbg_din_chnl0_data <= 0;
  intf.din[p.src] <= 1'b0;
  intf.valid_n[p.src] <= 1'b1;
  intf.frame_n[p.src] <= 1'b1;
  $display("@%0t:[DRV] src_chnl[%0d] &dest_chnl[%0d ] finished",$time,p.src,p.dst);
endtask
endmodule

//**********************************  rt_generator  ************************************//
module rt_generator;
  rt_packet pkts[$];	//定义队列pkts

  function void put_pkt(input rt_packet p);
    pkts.push_back(p);	//在pkts队列尾插入p
  endfunction

  task get_pkt(output rt_packet p);
    wait(pkts.size() >0 )	
      p = pkts.pop_front();	//pkts.size()>0时执行,提取队列首元素,输出队列首元素
  endtask

  //generate a random packet
  function void gen_pkt(int src = -1, int dst = -1);
  endfunction
endmodule

module rt_monitor(rt_interface intf);
	rt_packet in_pkts[16][$];
	rt_packet out_pkts[16][$];
	
	initial begin: mon_chnl_proc
		foreach(in_pkts[i])begin
		automatic int chid = i;
		fork 
			mon_chnl_in (chid);
			mon_chnl_out (chid);
		join_none
		end
	end
	
	task automatic mon_chnl_in(bit[3:0] id);
	rt_packet pkt;
	forever begin
		//clear content for the same struct variable
		pkt = new();
		pkt.dst = id;
		//monitor specific channel-in data abd put in into the quene
		//monitor adress phase
		@(negedge intf.frame_n[id]);
		for (int i = 0;i<4;i++)begin
			@(negedge intf.clock);
			pkt.dst[i] = intf.din[id];
		end
		$display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ]data trans started",$time,pkt.src,pkt.dst);
		//pass pad phase 不考虑pad阶段
		repeat(5) @(negedge intf.clock);
		do begin
			pkt.data = new[pkt.data.size + 1] (pkt.data);
			for (int i=0;i<8;i++)begin
				@(negedge intf.clock);
				pkt.data[pkt.data.size - 1][i] = intf.din[id];
			end
		end while(intf.frame_n[id]);
		in_pkts[id].push_back(pkt);
		$display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n %s",$time,pkt.src,pkt.dst,pkt.sprint());
	end
	endtask
	
	task automatic mon_chnl_out(bit[3:0] id);
		rt_packet pkt;
	forever begin
		//clear content for the same struct variable
		pkt = new();
		pkt.src = 0;
		pkt.dst = id;
		@(negedge intf.frameo_n[id]);
		$display("@%0t:[MON] CH_OUT dest_chnl[%0d ]data trans started",$time,pkt.dst);
		do begin
			pkt.data = new[pkt.data.size + 1] (pkt.data);
			for (int i=0;i<8;i++)begin
				@(negedge intf.clock iff !intf.valido_n[id]);
				pkt.data[pkt.data.size - 1][i] = intf.din[id];
			end
		end while(!intf.frameo_n[id]);
		out_pkts[id].push_back(pkt);
		$display("@%0t:[MON] CH_OUT dest_chnl[%0d ] finished with packet: \n %s",$time,pkt.dst,pkt.sprint());
	end
	endtask	
endmodule

module rt_test_top;
endmodule

//********************************** tb  ************************************//
module tb;

	bit clk,rstn;
	logic [15:0] din, frame_n, valid_n;
	logic [15:0] dout, valido_n, busy_n, frameo_n;

	// generate clock
	initial 
		forever #5ns clk <= !clk;

	// genarate reset
	initial begin
		#2ns  rstn <= 1;
		#10ns rstn <= 0;
		#10ns rstn <= 1;
	end

	router dut(			//例化router为dut
		.reset_n(rstn)
		,.clock(clk)
		,.frame_n(intf.frame_n)
		,.valid_n(intf.valid_n)
		,.din(intf.din)
		,.dout(intf.dout)
		,.busy_n(intf.busy_n)
		,.valido_n(intf.valido_n)
		,.frameo_n(intf.frameo_n)
);

	rt_interface intf();	 //例化rt_interface为intf
	assign intf.reset_n = rstn;
	assign intf.clock = clk;
	
	rt_stimulator stim(intf);//例化rt_stimulator为stim
	
	rt_monitor mon(intf);
	
	rt_generator gen();		 //例化rt_generator为gen

 //generate and transmit packet 产生数据
	initial begin : generate_proc
		rt_packet p;
		p = new();
		p.set_members(0,3,'{8'h33,8'h77});
		gen.put_pkt(p);
		p = new();//每次put_pkt都需要new一下
		p.set_members(0,5,'{8'h55,8'h66});
		gen.put_pkt(p);
		p = new();
		p.set_members(3,6,'{8'h77,8'h88,8'h22});
		gen.put_pkt(p);
		p = new();
		p.set_members(4,7,'{8'haa,8'hcc,8'h33});
		gen.put_pkt(p);
	end
	
	initial begin : transmit_proc
		rt_packet p;
    forever  begin 
      gen.get_pkt(p);	//generator输出p,p为generator里pkts的首元素
      stim.put_pkt(p);	//把数据p输入stim里的pkts队列尾
   end
  end
  
endmodule

2.3 仿真结果

3. 将stimulator、generator、monitor改为类

3.1 改造

(1)将stimulator、generator、monitor从module改为class:

class rt_stimulator;
	...
endclass

class rt_generator;	
	...
endclass

class rt_monitor;
	...
endclass

(2)stimulator、generator、monitor的例化变为:

rt_stimulator stim;
rt_monitor mon;
rt_generator gen;
	
	initial begin : inst_init_proc
		//build stage,例化
		stim = new();
		gen = new();
		mon = new();
		//connect sage,连接
		stim.intf = intf;
		mon.intf = intf;
		//run stage,run
		fork
			stim.run();//class里面的函数不会自动调用,需要手动调用
			gen.run();	
			mon.run();
		join_none
	end

类默认为动态,所以使用的automatic可以去掉
(3)将drive_reset_proc和drive_chnl_proc这两个initial begin end语句改为task:

task run();
	fork
		drive_reset(); //reset动作
		get_packet_and_drive(); //drive_chnl动作
	join_none
endtask
	
task drive_reset();//reset
	...
endtask

task drive_chnl_proc();//drive_chnl
	...
endtask

3.2 改造后的tb.sv

class rt_packet;
  bit [3:0] src;
  bit [3:0] dst;
  bit [7:0] data [];
  function new();
  endfunction
  function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []);
	this.src = src;
	this.dst = dst;
	this.data = data;
  endfunction
  function string sprint();//打印packet信息的函数
	sprint = {sprint , $sformatf("src = %0d\n",src)};
	sprint = {sprint , $sformatf("dst = %0d\n",dst)};
	sprint = {sprint , $sformatf("data_length = %0d\n",data.size())};
	foreach(data[i])
		sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])};
  endfunction
endclass

interface rt_interface();
	logic clock;
	logic reset_n;
	logic [15:0] din;
	logic [15:0] frame_n;
	logic [15:0] valid_n;
	logic [15:0] dout;
	logic [15:0] valido_n;
	logic [15:0] busy_n;
	logic [15:0] frameo_n;
endinterface
//********************************** stimulator **********************************//
class rt_stimulator;
	virtual rt_interface intf;//class里的接口不能用端口的方式描述,需要添加virtual关键字,在类里面用接口的指针
//for debug purpose from waveform	//定义检测状态的变量
  typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t;
  drv_state_t dbg_state;  
  byte unsigned dbg_din_chnl0_data; 
  int src_chnl_status[int];  //关联数组,后面int:src_chnl的number;前面int:dest_chnl的Number ??
	
//generator传送p给stimulator
  rt_packet pkts[$];//定义stimulator中的的队列pkts
  function void put_pkt(input rt_packet p);
    pkts.push_back(p);	//将generator中传过来的p放入stimulator的pkts中(在pkts队列尾插入p)
  endfunction

task run();
	fork
		drive_reset(); //reset动作
		get_packet_and_drive(); //drive_chnl动作
	join_none
endtask
	
task drive_reset();//reset
	forever begin
		@(negedge intf.reset_n);
		dbg_state <= DRV_RESET;
		intf.din <= 0;
		intf.frame_n <= '1;//等效16'hFFFF
		intf.valid_n <= '1;
	end
endtask

// 发送数据
task get_packet_and_drive();//drive_chnl
	//rt_packet_t p;
  @(negedge intf.reset_n);
  repeat(10) @(posedge intf.clock);//延迟10个时钟周期
  forever begin	
	automatic rt_packet p;//声明一个动态的
	wait(pkts.size()>0);
	p = pkts.pop_front();	//将p从队列pkts里面取出
	fork//后台触发线程,触发线程在后台运行,继续执行剩下内容
		begin	
			wait_src_chnl_avail(p);//判断src chnl是否被占用,是否需要等待
			drive_chnl(p);//从P中拿到发送端口,目标端口,发送数据
			set_src_chnl_avail(p);
		end
	join_none
	end
endtask

task  wait_src_chnl_avail(rt_packet p);//判断src chnl是否被占用,是否需要等待
	if(!src_chnl_status.exists(p.src))//src_chnl_status关联数组里面不含p.src,(表示未占用任何dest_chnl,一开始不满足)
		src_chnl_status[p.src] = p.dst;//表示当前正在使用src_chnl,对应dest_chnl就是p.dst(p.src端口在向p.dst端口发送数据)
	else if(src_chnl_status[p.src] >= 0)//如果在给0,1,2...dest_chnl发送数据(被占用),需要等待,,否则不需要等待
		wait(src_chnl_status[p.src] == -1);//直到src_chnl_status[p.src] == -1(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
endtask

function  set_src_chnl_avail(rt_packet p);
	src_chnl_status[p.src] = -1;//如果chnl发送完后,把src_chnl置回来(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
endfunction

task  drive_chnl(rt_packet p);
  $display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n%s",$time,p.src,p.dst,p.sprint());
	// drive address phase 输入地址位阶段
for(int i=0;i<4;i++)begin  //4 clock
  @(posedge intf.clock);
  dbg_state <=DRV_ADDR;	
  intf.din[p.src] <= p.dst[i];
  intf.valid_n[p.src] <= $urandom_range(0,1);	//valid_n在din的地址输入时间段可为任意值x
  intf.frame_n[p.src] <= 1'b0;	//frame_n需要为低
end
	// drive pad phase //隔离阶段
  for (int i=0;i<5;i++)begin  //5 clock
    @(posedge intf.clock);
    dbg_state <=DRV_PAD;
    intf.din[p.src] <= 1'b1;
    intf.valid_n[p.src] <= 1'b1;	//valid_n需为高电平
    intf.frame_n[p.src] <= 1'b0; //frame_n需为低电平
  end
	// drive data phase 传输数据阶段
  foreach(p.data[id])begin
    for(int i=0;i<8;i++)begin
     @(posedge intf.clock);
      dbg_state <=DRV_DATA;
      dbg_din_chnl0_data <= p.data[id];
      intf.din[p.src] <= p.data[id][i];
      intf.valid_n[p.src] <=1'b0;
      intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高
    end
  end
// drive idle phase 闲置(没有数据传输)阶段
  @(posedge intf.clock);
  dbg_state <=DRV_IDLE;
  dbg_din_chnl0_data <= 0;
  intf.din[p.src] <= 1'b0;
  intf.valid_n[p.src] <= 1'b1;
  intf.frame_n[p.src] <= 1'b1;
  $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,p.src,p.dst);
endtask
endclass

//********************************** generator **********************************//
class rt_generator;	//generator产生数据交给stimulator
  rt_packet pkts[$];	//定义队列

  function void put_pkt(input rt_packet p);
    pkts.push_back(p);	//将p放入队列pkts里面(在pkts队列尾插入p)
  endfunction
  
  task get_pkt(output rt_packet p);
    wait(pkts.size() >0 )	//队列不为空
      p = pkts.pop_front();	//将p从队列pkts里面取出,提取队列首元素
  endtask

  //generate a random packet
  function void gen_pkt(int src = -1, int dst = -1);
  endfunction
  
  task run();
		//TODO
  endtask
	
endclass
//********************************** monitor **********************************//
class rt_monitor;
virtual rt_interface intf;
	rt_packet in_pkts[16][$];
	rt_packet out_pkts[16][$];
	
task run();
	fork
		mon_chnls();
	join_none
endtask
	
task mon_chnls;
	foreach(in_pkts[i]) begin
		automatic int chid = i;
		fork
		mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入
		mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入
		join_none
	end
endtask
	
task  mon_chnl_in (bit[3:0]id);//监测数据输入的任务
	rt_packet pkt;	//定义结构体变量
	forever begin
	//clear content for the same struct variable。清除pkt
	pkt = new();
	pkt.src = id;	//第id个输入端口
	//monitor specific channel-in data and put it into the queue
	// monitor address phase
	@(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动)
	for(int i=0; i<4; i++)begin
		@(negedge intf.clock);		//frame_n下降沿后监测4个clk negedge
		pkt.dst[i]= intf.din[id];
		end
		$display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst);
	//pass pad phase 不考虑pad阶段是否满足协议要求
	repeat(5) @(negedge intf.clock);
	do begin
		pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data
		for ( int i=0; i<8; i++) begin
			@(negedge intf.clock); //在8个clk negedge监测8bit数据
			pkt.data[pkt.data.size-1][i] = intf.din[id];
		end
	end while(!intf.frame_n[id]);
	in_pkts[id].push_back(pkt);	//将monitor拿到的数据放入in_pkts
	$display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n%s",$time,pkt.src,pkt.dst,pkt.sprint());
	end
endtask

task  mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务
	rt_packet pkt;
	forever begin
		//clear content for the same struct variable
		pkt = new();
		pkt.src = 0;
		pkt.dst = id;	
		@(negedge intf.frameo_n[id]);
		$display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst);
		do begin
		pkt.data = new [ pkt.data.size + 1](pkt.data);
		for(int i=0;i<8; i++) begin
			@(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低
			pkt.data[pkt.data.size-1][i]= intf.dout [id];
		end
	end while(!intf.frameo_n[id]);
	out_pkts[id].push_back(pkt);
	$display("@%0t: [MON] CH_OUT dest_chn1[%0d] data finished woth packet : \n %s",$time,pkt.dst,pkt.sprint());
	//monitor specific channel-out data and put it into the queue
	end
endtask

endclass

//********************************** tb **********************************//
module tb;

bit clk,rstn;
logic [15:0] din, frame_n, valid_n;
logic [15:0] dout, valido_n, busy_n, frameo_n;

// 产生时钟,周期为10ns
initial 
    forever #5ns clk <= !clk;

// 产生复位信号
  initial begin
    #2ns rstn <= 1;
    #10ns rstn <= 0;
    #10ns rstn <= 1;
  end

//例化router为DUT
router dut(           
  .reset_n(rstn),
  .clock(clk),
  .frame_n(intf.frame_n),
  .valid_n(intf.valid_n),
  .din(intf.din),
  .dout(intf.dout),
  .busy_n(intf.busy_n),
  .valido_n(intf.valido_n),
  .frameo_n(intf.frameo_n)
);

rt_interface intf();//例化接口
	assign intf.reset_n = rstn;
	assign intf.clock = clk;
	
//例化stimulator
rt_stimulator stim;
//例化Monitor
rt_monitor mon;
//例化generator
rt_generator gen;
	
	initial begin : inst_init_proc
		//build stage,例化
		stim = new();
		gen = new();
		mon = new();
		//connect sage,连接
		stim.intf = intf;
		mon.intf = intf;
		//run stage,run
		fork
			stim.run();//class里面的函数不会自动调用,需要手动调用
			gen.run();	
			mon.run();
		join_none
	end
	
initial begin:generate_data_proc//产生数据
		rt_packet p;
		#0; //wait for test components instantiated	以下句柄并没有在仿真开始前例化完成,需要进入上面initial里面,所以为了上述initial先执行,等module转化为class例化完成再使用这些句柄,添加延迟最小时间单元
		p = new();
		p.set_members(0,3,'{8'h33,8'h77});
		gen.put_pkt(p);
		p = new();//每次put_pkt都需要new一下
		p.set_members(0,5,'{8'h55,8'h66});
		gen.put_pkt(p);
		p = new();
		p.set_members(3,6,'{8'h77,8'h88,8'h22});
		gen.put_pkt(p);
		p = new();
		p.set_members(4,7,'{8'haa,8'hcc,8'h33});
		gen.put_pkt(p);
	end
initial begin:genarator_to_stimulator_proc//取出genarator中的数据给stimulator
	rt_packet p;
	#0; //wait for test components instantiated
	forever begin
		gen.get_pkt(p);
		stim.put_pkt(p);
	end
end

endmodule 

3.3 仿真结果

结果同上,硬件结构层次如下:

4. 增加checker组件

4.1 思路

Checker从Monitor中拿到monitor采样的输入数据为期望的输出、拿到monitor采样的输出数据为实际的输出,比较二者判断输入与输出是否相等。

4.2 改造后的tb.sv

class rt_packet;
  bit [3:0] src;
  bit [3:0] dst;
  bit [7:0] data [];
  function new();
  endfunction
  function void set_members(bit [3:0]src, bit [3:0]dst, bit[7:0]data []);
	this.src = src;
	this.dst = dst;
	this.data = data;
  endfunction
  function string sprint();//打印packet信息的函数
	sprint = {sprint , $sformatf("src = %0d\n",src)};
	sprint = {sprint , $sformatf("dst = %0d\n",dst)};
	sprint = {sprint , $sformatf("data_length = %0d\n",data.size())};
	foreach(data[i])
		sprint = {sprint , $sformatf("data[%0d] = 'h%0x\n", i, data[i])};
  endfunction
  function bit compare(rt_packet p);//输入exp_pkt
		if(dst == p.dst && data == p.data)
			compare = 1;
		else 
			compare = 0;
  endfunction
endclass

interface rt_interface();
	logic clock;
	logic reset_n;
	logic [15:0] din;
	logic [15:0] frame_n;
	logic [15:0] valid_n;
	logic [15:0] dout;
	logic [15:0] valido_n;
	logic [15:0] busy_n;
	logic [15:0] frameo_n;
endinterface
//********************************** stimulator **********************************//
class rt_stimulator;
	virtual rt_interface intf;//class里的接口不能用端口的方式描述,需要添加virtual关键字,在类里面用接口的指针
//for debug purpose from waveform	//定义检测状态的变量
  typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t;
  drv_state_t dbg_state;  
  byte unsigned dbg_din_chnl0_data; 
  int src_chnl_status[int];  //关联数组,后面int:src_chnl的number;前面int:dest_chnl的Number ??
	
//generator传送p给stimulator
  rt_packet pkts[$];//定义stimulator中的的队列pkts
  function void put_pkt(input rt_packet p);
    pkts.push_back(p);	//将generator中传过来的p放入stimulator的pkts中(在pkts队列尾插入p)
  endfunction

task run();
	fork
		drive_reset(); //reset动作
		get_packet_and_drive(); //drive_chnl动作
	join_none
endtask
	
task drive_reset();//reset
	forever begin
		@(negedge intf.reset_n);
		dbg_state <= DRV_RESET;
		intf.din <= 0;
		intf.frame_n <= '1;//等效16'hFFFF
		intf.valid_n <= '1;
	end
endtask

// 发送数据
task get_packet_and_drive();//drive_chnl
	//rt_packet_t p;
  @(negedge intf.reset_n);
  repeat(10) @(posedge intf.clock);//延迟10个时钟周期
  forever begin	
	automatic rt_packet p;//声明一个动态的
	wait(pkts.size()>0);
	p = pkts.pop_front();	//将p从队列pkts里面取出
	fork//后台触发线程,触发线程在后台运行,继续执行剩下内容
		begin	
			wait_src_chnl_avail(p);//判断src chnl是否被占用,是否需要等待
			drive_chnl(p);//从P中拿到发送端口,目标端口,发送数据
			set_src_chnl_avail(p);
		end
	join_none
	end
endtask

task  wait_src_chnl_avail(rt_packet p);//判断src chnl是否被占用,是否需要等待
	if(!src_chnl_status.exists(p.src))//src_chnl_status关联数组里面不含p.src,(表示未占用任何dest_chnl,一开始不满足)
		src_chnl_status[p.src] = p.dst;//表示当前正在使用src_chnl,对应dest_chnl就是p.dst(p.src端口在向p.dst端口发送数据)
	else if(src_chnl_status[p.src] >= 0)//如果在给0,1,2...dest_chnl发送数据(被占用),需要等待,,否则不需要等待
		wait(src_chnl_status[p.src] == -1);//直到src_chnl_status[p.src] == -1(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
endtask

function  set_src_chnl_avail(rt_packet p);
	src_chnl_status[p.src] = -1;//如果chnl发送完后,把src_chnl置回来(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
endfunction

task  drive_chnl(rt_packet p);
  $display("@%0t:[DRV] src_chnl[%0d] & dest_chnl[%0d] data trans started with packet: \n%s",$time,p.src,p.dst,p.sprint());
	// drive address phase 输入地址位阶段
for(int i=0;i<4;i++)begin  //4 clock
  @(posedge intf.clock);
  dbg_state <=DRV_ADDR;	
  intf.din[p.src] <= p.dst[i];
  intf.valid_n[p.src] <= $urandom_range(0,1);	//valid_n在din的地址输入时间段可为任意值x
  intf.frame_n[p.src] <= 1'b0;	//frame_n需要为低
end
	// drive pad phase //隔离阶段
  for (int i=0;i<5;i++)begin  //5 clock
    @(posedge intf.clock);
    dbg_state <=DRV_PAD;
    intf.din[p.src] <= 1'b1;
    intf.valid_n[p.src] <= 1'b1;	//valid_n需为高电平
    intf.frame_n[p.src] <= 1'b0; //frame_n需为低电平
  end
	// drive data phase 传输数据阶段
  foreach(p.data[id])begin
    for(int i=0;i<8;i++)begin
     @(posedge intf.clock);
      dbg_state <=DRV_DATA;
      dbg_din_chnl0_data <= p.data[id];
      intf.din[p.src] <= p.data[id][i];
      intf.valid_n[p.src] <=1'b0;
      intf.frame_n[p.src] <= (id == p.data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高
    end
  end
// drive idle phase 闲置(没有数据传输)阶段
  @(posedge intf.clock);
  dbg_state <=DRV_IDLE;
  dbg_din_chnl0_data <= 0;
  intf.din[p.src] <= 1'b0;
  intf.valid_n[p.src] <= 1'b1;
  intf.frame_n[p.src] <= 1'b1;
  $display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,p.src,p.dst);
endtask
endclass

//********************************** generator **********************************//
class rt_generator;	//generator产生数据交给stimulator
  rt_packet pkts[$];	//定义队列

  function void put_pkt(input rt_packet p);
    pkts.push_back(p);	//将p放入队列pkts里面(在pkts队列尾插入p)
  endfunction
  
  task get_pkt(output rt_packet p);
    wait(pkts.size() >0 )	//队列不为空
      p = pkts.pop_front();	//将p从队列pkts里面取出,提取队列首元素
  endtask

  //generate a random packet
  function void gen_pkt(int src = -1, int dst = -1);
  endfunction
  
  task run();
		//TODO
  endtask
	
endclass
//********************************** monitor **********************************//
class rt_monitor;
virtual rt_interface intf;
	rt_packet in_pkts[16][$];
	rt_packet out_pkts[16][$];
	
task run();
	fork
		mon_chnls();
	join_none
endtask
	
task mon_chnls;
	foreach(in_pkts[i]) begin
		automatic int chid = i;
		fork
		mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入
		mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入
		join_none
	end
endtask
	
task  mon_chnl_in (bit[3:0]id);//监测数据输入的任务
	rt_packet pkt;	//定义结构体变量
	forever begin
	//clear content for the same struct variable。清除pkt
	pkt = new();
	pkt.src = id;	//第id个输入端口
	//monitor specific channel-in data and put it into the queue
	// monitor address phase
	@(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动)
	for(int i=0; i<4; i++)begin
		@(negedge intf.clock);		//frame_n下降沿后监测4个clk negedge
		pkt.dst[i]= intf.din[id];
		end
		$display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst);
	//pass pad phase 不考虑pad阶段是否满足协议要求
	repeat(5) @(negedge intf.clock);
	do begin
		pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data
		for ( int i=0; i<8; i++) begin
			@(negedge intf.clock); //在8个clk negedge监测8bit数据
			pkt.data[pkt.data.size-1][i] = intf.din[id];
		end
	end while(!intf.frame_n[id]);
	in_pkts[id].push_back(pkt);	//将monitor拿到的数据放入in_pkts
	$display("@%0t:[MON] CH_IN src_chnl[%0d] &dest_chnl[%0d ] finished with packet: \n%s",$time,pkt.src,pkt.dst,pkt.sprint());
	end
endtask

task  mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务
	rt_packet pkt;
	forever begin
		//clear content for the same struct variable
		pkt = new();
		pkt.src = 0;
		pkt.dst = id;	
		@(negedge intf.frameo_n[id]);
		$display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst);
		do begin
		pkt.data = new [ pkt.data.size + 1](pkt.data);
		for(int i=0;i<8; i++) begin
			@(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低
			pkt.data[pkt.data.size-1][i]= intf.dout [id];
		end
	end while(!intf.frameo_n[id]);
	out_pkts[id].push_back(pkt);
	$display("@%0t: [MON] CH_OUT dest_chn1[%0d] data finished woth packet : \n %s",$time,pkt.dst,pkt.sprint());
	//monitor specific channel-out data and put it into the queue
	end
endtask

endclass
//********************************** checker **********************************//
class rt_checker;

	int unsigned compare_count;
	int unsigned error_count;
	
	function new();//赋初值,可有可无,整形变量默认初始值为0
		compare_count = 0;
		error_count = 0;
	endfunction
	
	rt_packet exp_out_pkts[16][$];
	rt_monitor mon;
	
	task run();
	foreach(exp_out_pkts[i])begin
		automatic int chid = i;
		fork
			do_routing(chid);
			do_compare(chid);
		join_none
		end
	endtask
	
	task do_routing(bit[3:0] id);//将monitor中采样到的输入数据放入期望的输出端队列中
		rt_packet pkt;
		forever begin
			wait(mon.in_pkts[id].size > 0);
			pkt = mon.in_pkts[id].pop_front();//从monitor中拿到in_pkts队列数据放入pkt
			exp_out_pkts[pkt.dst].push_back(pkt);//将pkt数据放入对应期望的dest_chnl
			end
	endtask 
	
	task do_compare(bit[3:0] id);//比较采集的实际输出与期望输出
		rt_packet exp_pkt, act_pkt;
		forever begin
			wait(mon.out_pkts[id].size > 0 && exp_out_pkts[id].size > 0);//实际采样数据与期望数据都有值
			act_pkt = mon.out_pkts[id].pop_front();//实际数据为monitor采样的输出数据
			exp_pkt = exp_out_pkts[id].pop_front();//期望数据为monitor采样到的输入数据
			if(act_pkt.compare(exp_pkt))begin//如果exp_pkt与act_pkt比较成功,返回1
				$display("[CHK] data compare success with packet : \n%s",act_pkt.sprint());
			end
			else begin
				$display("[CHK] data compare failure with actual packet : \n%s \nexpected packet : \n%s", act_pkt.sprint(), exp_pkt.sprint());
				error_count++;
			end
				compare_count++;
		end
	endtask 
	
	function void do_report();
		$display("TOTAL COMPARING %0d times",compare_count);
		if(!error_count)
			$display("TEST PASSED!");
		else begin
			$display("TEST FAILED!");
			$display("TOTAL ERROR %0d times", error_count);
		end
	endfunction
endclass
//********************************** tb **********************************//
module tb;

bit clk,rstn;
logic [15:0] din, frame_n, valid_n;
logic [15:0] dout, valido_n, busy_n, frameo_n;

// 产生时钟,周期为10ns
initial 
    forever #5ns clk <= !clk;

// 产生复位信号
  initial begin
    #2ns rstn <= 1;
    #10ns rstn <= 0;
    #10ns rstn <= 1;
  end

//例化router为DUT
router dut(           
  .reset_n(rstn),
  .clock(clk),
  .frame_n(intf.frame_n),
  .valid_n(intf.valid_n),
  .din(intf.din),
  .dout(intf.dout),
  .busy_n(intf.busy_n),
  .valido_n(intf.valido_n),
  .frameo_n(intf.frameo_n)
);

rt_interface intf();//例化接口
	assign intf.reset_n = rstn;
	assign intf.clock = clk;
	
//例化stimulator
rt_stimulator stim;
//例化Monitor
rt_monitor mon;
//例化generator
rt_generator gen;

rt_checker chk;
	
	initial begin : inst_init_proc
		//build stage,例化
		stim = new();
		gen = new();
		mon = new();
		chk = new();
		//connect sage,连接
		stim.intf = intf;
		mon.intf = intf;
		chk.mon = mon;//check拿monitor句柄,即拿monitor中in_pkts与out_pkts队列
		//run stage,run
		fork
			stim.run();//class里面的函数不会自动调用,需要手动调用
			gen.run();	
			mon.run();
			chk.run();
		join_none
	end
	
initial begin:generate_data_proc//产生数据
		rt_packet p;
		#0; //wait for test components instantiated	以下句柄并没有在仿真开始前例化完成,需要进入上面initial里面,所以为了上述initial先执行,等module转化为class例化完成再使用这些句柄,添加延迟最小时间单元
		p = new();
		p.set_members(0,3,'{8'h33,8'h77});
		gen.put_pkt(p);
		p = new();//每次put_pkt都需要new一下
		p.set_members(0,5,'{8'h55,8'h66});
		gen.put_pkt(p);
		p = new();
		p.set_members(3,6,'{8'h77,8'h88,8'h22});
		gen.put_pkt(p);
		p = new();
		p.set_members(4,7,'{8'haa,8'hcc,8'h33});
		gen.put_pkt(p);
	end
initial begin:genarator_to_stimulator_proc//取出genarator中的数据给stimulator
	rt_packet p;
	#0; //wait for test components instantiated
	forever begin
		gen.get_pkt(p);
		stim.put_pkt(p);
	end
end

endmodule 

4.3 仿真结果

DVE报告

dve> run 2us
2000000 ps
@105000:[DRV] src_chnl[0] & dest_chnl[3] data trans started with packet:
src = 0
dst = 3
data_length = 2
data[0] = 'h33
data[1] = 'h77

@105000:[DRV] src_chnl[3] & dest_chnl[6] data trans started with packet:
src = 3
dst = 6
data_length = 3
data[0] = 'h77
data[1] = 'h88
data[2] = 'h22

@105000:[DRV] src_chnl[4] & dest_chnl[7] data trans started with packet:
src = 4
dst = 7
data_length = 3
data[0] = 'haa
data[1] = 'hcc
data[2] = 'h33

@150000: [MON] src_chn1[0] & dest_chn1[3] data trans started
@150000: [MON] src_chn1[3] & dest_chn1[6] data trans started
@150000: [MON] src_chn1[4] & dest_chn1[7] data trans started
@205000: [MON] CH_OUT dest_chn1[3] data trans started
@205000: [MON] CH_OUT dest_chn1[6] data trans started
@205000: [MON] CH_OUT dest_chn1[7] data trans started
@360000:[MON] CH_IN src_chnl[0] &dest_chnl[3 ] finished with packet:
src = 0
dst = 3
data_length = 2
data[0] = 'h33
data[1] = 'h77

@365000: [DRV]src_chnl[0],dest_chnl[3] data trans finished
@365000:[DRV] src_chnl[0] & dest_chnl[5] data trans started with packet:
src = 0
dst = 5
data_length = 2
data[0] = 'h55
data[1] = 'h66

@380000: [MON] CH_OUT dest_chn1[3] data finished woth packet :
src = 0
dst = 3
data_length = 2
data[0] = 'h33
data[1] = 'h77

[CHK] data compare success with packet :
src = 0
dst = 3
data_length = 2
data[0] = 'h33
data[1] = 'h77

@410000: [MON] src_chn1[0] & dest_chn1[5] data trans started
@440000:[MON] CH_IN src_chnl[3] &dest_chnl[6 ] finished with packet:
src = 3
dst = 6
data_length = 3
data[0] = 'h77
data[1] = 'h88
data[2] = 'h22

@440000:[MON] CH_IN src_chnl[4] &dest_chnl[7 ] finished with packet:
src = 4
dst = 7
data_length = 3
data[0] = 'haa
data[1] = 'hcc
data[2] = 'h33

@445000: [DRV]src_chnl[3],dest_chnl[6] data trans finished
@445000: [DRV]src_chnl[4],dest_chnl[7] data trans finished
@460000: [MON] CH_OUT dest_chn1[6] data finished woth packet :
src = 0
dst = 6
data_length = 3
data[0] = 'h77
data[1] = 'h88
data[2] = 'h22

@460000: [MON] CH_OUT dest_chn1[7] data finished woth packet :
src = 0
dst = 7
data_length = 3
data[0] = 'haa
data[1] = 'hcc
data[2] = 'h33

[CHK] data compare success with packet :
src = 0
dst = 6
data_length = 3
data[0] = 'h77
data[1] = 'h88
data[2] = 'h22

[CHK] data compare success with packet :
src = 0
dst = 7
data_length = 3
data[0] = 'haa
data[1] = 'hcc
data[2] = 'h33

@465000: [MON] CH_OUT dest_chn1[5] data trans started
@620000:[MON] CH_IN src_chnl[0] &dest_chnl[5 ] finished with packet:
src = 0
dst = 5
data_length = 2
data[0] = 'h55
data[1] = 'h66

@625000: [DRV]src_chnl[0],dest_chnl[5] data trans finished
@640000: [MON] CH_OUT dest_chn1[5] data finished woth packet :
src = 0
dst = 5
data_length = 2
data[0] = 'h55
data[1] = 'h66

[CHK] data compare success with packet :
src = 0
dst = 5
data_length = 2
data[0] = 'h55
data[1] = 'h66

硬件结构层次如下:

5. TestBench概览

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值