SV Testbench 案例学习与思考-1



引言

关于Systemverilog语法学习的专栏博客已经告一段落,现在结合 chipverify 官网给出的几个testbench 案例,利用 QuestaSim 平台实做一些练习。


设计

// ---- ---- Design module
// 简述:数据地址选通
module switch #(
parameter ADDR_WIDTH 	= 		8,
parameter DATA_WIDTH 	= 		16,
parameter ADDR_DIV   	= 		8'h3f 
)(
input 							clk,
input 							rstn,
input 							vld,
input [ADDR_WIDTH-1:0] 			addr,
input [DATA_WIDTH-1:0] 			data,

output reg [ADDR_WIDTH-1:0] 	addr_a,
output reg [DATA_WIDTH-1:0] 	data_a,

output reg [ADDR_WIDTH-1:0] 	addr_b,
output reg [DATA_WIDTH-1:0] 	data_b
);

always@(posedge clk)
begin
	if(~rstn)
	begin
		addr_a <= 0;
		data_a <= 0;
		addr_b <= 0;
		data_b <= 0;
	end
	else
	begin
		if(vld)
		begin
			if(addr >=0 & addr <= ADDR_DIV)
			begin
				addr_a <= addr;
				data_a <= data;
				addr_b <= 0;
				data_b <= 0;				
			end
			else
			begin
				addr_a <= 0;
				data_a <= 0;
				addr_b <= addr;
				data_b <= data;					
			end
		end
	end
end
endmodule

这个案例的设计模块其实逻辑很简单,就是一个地址数据选择赋值功能,当地址在前半区间时,将地址和数据一并赋值给 a 通道;否则赋值给 b 通道。

事务对象

// ---- ---- Transaction Object
// 简述:事务对象
class switch_item;
	rand bit [7:0]  addr;		
	rand bit [15:0] data;		
	     bit [7:0]  addr_a;		
	     bit [15:0] data_a;		
	     bit [7:0]  addr_b;		
	     bit [15:0] data_b;		

	function void print(string tag="");
		$display("T = %0t %s addr = 0x%0h data = 0x%0h addr_a = 0x%0h data_a = 0x%0h addr_b = 0x%0h data_b = 0x%0h",
				  $time,tag,addr,data,addr_a,data_a,addr_b,data_b);
	endfunction
endclass 

事务对象基本作为对象给驱动器提供被驱动事务,输入(激励)信号一般用 rand 关键字声明,因为在每次激励时产生随机数进行测试验证。输出(响应信号) 则不必用 rand 关键字。

数据生成器

// ---- ---- Generator
// 简述:测试数据生成
class generator;
	mailbox drv_mbx;
	event drv_done;
	int num = 20;

	task run();
		for(int i = 0; i < num; i++)
		begin
			switch_item item = new;
			item.randomize();
			$display("T = %0t [Generator] Loop : %0d/%0d creat next item",$time,i+1,num);
			drv_mbx.put(item);
			@(drv_done);
		end
		$display("T = %0t [Generator] Done generation of %0d items",$time,num);
	endtask
endclass

此部分代码主要是实现了一个数据生成器的类,类内的任务 run 每次创建一个新的事务对象,产生随即激励数据,通过 mailbox 传入驱动器。

接口

// ---- ---- Interface
interface switch_if (input bit clk);
	logic 			rstn;
	logic 			vld;
	logic [7:0] 	addr;
	logic [15:0] 	data;
	logic [7:0] 	addr_a;
	logic [15:0] 	data_a;
	logic [7:0] 	addr_b;
	logic [15:0] 	data_b;
endinterface

此处接口定义的地方也可以用 virtual 关键字,声明为虚接口; 

驱动器

// ---- ---- Driver
// 简述:驱动器
class driver;
	virtual switch_if vif;//虚拟接口:每个接口实例是独立的,互不影响
	event drv_done;
	mailbox drv_mbx;

	task run();
		$display("T = %0t [Driver] starting ...",$time);
		@(posedge vif.clk);

		forever
		begin
			switch_item item;

			$display("T = %0t [Driver] waiting for item ...",$time);
			drv_mbx.get(item);
			item.print("Driver");
			vif.vld <= 1;
			vif.addr <= item.addr;
			vif.data <= item.data;

			@(posedge vif.clk);
			vif.vld <= 0; ->drv_done;
		end
	endtask
endclass 

注意此处代码内对接口的实例化使用了 virtual 关键字;虚接口的实例彼此之间独立互不影响。

drv_mbx 通过 mailbox 获取事务对象,然后将 vld 有效信号拉高,地址和数据各自赋值。

最后将 vld 信号拉低 触发 drv_done 事件。

监视器

// ---- ---- Monitor
// 简述:
class monitor;
	virtual switch_if vif;
	mailbox scb_mbx;
	semaphore sema4;

	function new ();
		sema4 = new(1);
	endfunction 

	task run();
		$display("T = %0t [Monitor] starting ...",$time);

		fork
			sample_port("Thread0");
			sample_port("Thread1");
		join
	endtask

	task sample_port(string tag="");
		forever
		begin
			@(posedge vif.clk);
			if(vif.rstn & vif.vld)
			begin
				switch_item item = new;
				sema4.get();
				item.addr = vif.addr;
				item.data = vif.data;
				$display("T = %0t [Monitor] %s First part over",$time,tag);

				@(posedge vif.clk);
				sema4.put();
				item.addr_a = vif.addr_a;
				item.data_a = vif.data_a;
				item.addr_b = vif.addr_b;
				item.data_b = vif.data_b;
				$display("T = %0t [Monitor] %s Second part over",$time,tag);

				scb_mbx.put(item);
				item.print({"Monitor_",tag});
			end
		end
	endtask

endclass 

监视器部分的代码涉及到两个线程对同一个内存区域的读写操作,为避免冲突,此处引入 semaphore 。

计分板

// ---- ---- Scoreboard
// 简介:
class scoreboard;
	mailbox scb_mbx;

	task run();
		forever
		begin
			switch_item item;
			scb_mbx.get(item);

			if(item.addr inside{[0:'h3f]})
			begin
				if(item.addr_a != item.addr | item.data_a != item.data)
					$display("T = %0t [Scoreboard] ERRROR! Mismatch addr=0x%0h data=0x%0h addr_a=0x%0h data_a=0x%0h",$time,item.addr ,item.data ,item.addr_a ,item.data_a);
				else
					$display("T = %0t [Scoreboard] PASS! Match addr=0x%0h data=0x%0h addr_a=0x%0h data_a=0x%0h",$time,item.addr ,item.data ,item.addr_a ,item.data_a);
			end
			else
			begin
				if(item.addr_b != item.addr | item.data_b != item.data)
					$display("T = %0t [Scoreboard] ERRROR! Mismatch addr=0x%0h data=0x%0h addr_b=0x%0h data_b=0x%0h",$time,item.addr ,item.data ,item.addr_b ,item.data_b);
				else
					$display("T = %0t [Scoreboard] PASS! Match addr=0x%0h data=0x%0h addr_b=0x%0h data_b=0x%0h",$time,item.addr ,item.data ,item.addr_b ,item.data_b);
			end
		end
	endtask
endclass

主要就是对设计的功能作判断,对比输入输出是否一致。给出判断信息。

环境

// ---- ---- Environment
// 简介:
class env;
	driver d0;
	monitor m0;
	generator g0;
	scoreboard s0;

	mailbox drv_mbx;
	mailbox scb_mbx;
	event drv_done;

	virtual switch_if vif;

	function new ();
		d0 = new;
		m0 = new;
		g0 = new;
		s0 = new;
		drv_mbx = new();
		scb_mbx = new();

		d0.drv_mbx = drv_mbx;
		g0.drv_mbx = drv_mbx;
		m0.scb_mbx = scb_mbx;
		s0.scb_mbx = scb_mbx;

		d0.drv_done = drv_done;
		g0.drv_done = drv_done;
	endfunction 

	virtual task run();
		d0.vif = vif;
		m0.vif = vif;

		fork
			d0.run();
			m0.run();
			g0.run();
			s0.run();
		join_any
	endtask
endclass

 环境主要负责构建 testbench 的基本对象、事件、任务等。

测试

// ---- ---- Test
class test;
	env e0;

	function new ();
		e0 = new;
	endfunction 

	task run();
		e0.run();
	endtask
endclass 

测试可以看作是,执行环境中对应的任务。

测试顶层

// ---- ---- TB_TOP
module TB_TOP ();
reg clk;

always #10 clk=~clk;

switch_if _if(clk);

parameter ADDR_WIDTH 	= 		8;
parameter DATA_WIDTH 	= 		16;
parameter ADDR_DIV   	= 		8'h3f ;

	switch #(
			.ADDR_WIDTH(ADDR_WIDTH),
			.DATA_WIDTH(DATA_WIDTH),
			.ADDR_DIV(ADDR_DIV)
		) inst_switch (
			.clk    (clk),
			.rstn   (_if.rstn),
			.vld    (_if.vld),
			.addr   (_if.addr),
			.data   (_if.data),
			.addr_a (_if.addr_a),
			.data_a (_if.data_a),
			.addr_b (_if.addr_b),
			.data_b (_if.data_b)
		);


test t0;

initial
begin
	{clk,_if.rstn} <= 0;

	#20 _if.rstn <= 1;
	t0 = new;
	t0.e0.vif = _if;
	t0.run();

	#50 $finish;
end

endmodule

顶层需要例化设计文件,给时钟激励,执行测试。

为方便编译,建议将上述所有代码放在一个 sv 文件中。

QuestaSim 给出的仿真结果:

# T = 20 [Driver] starting ...
# T = 20 [Monitor] starting ...
# T = 20 [Generator] Loop : 1/20 creat next item
# T = 30 [Driver] waiting for item ...
# T = 30 Driver addr = 0xfb data = 0x78ad addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 50 [Driver] waiting for item ...
# T = 50 [Monitor] Thread0 First part over
# T = 50 [Generator] Loop : 2/20 creat next item
# T = 50 Driver addr = 0xce data = 0x6676 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 70 [Driver] waiting for item ...
# T = 70 [Monitor] Thread0 Second part over
# T = 70 Monitor_Thread0 addr = 0xfb data = 0x78ad addr_a = 0x0 data_a = 0x0 addr_b = 0xfb data_b = 0x78ad
# T = 70 [Generator] Loop : 3/20 creat next item
# T = 70 [Monitor] Thread1 First part over
# T = 70 [Scoreboard] PASS! Match addr=0xfb data=0x78ad addr_b=0xfb data_b=0x78ad
# T = 70 Driver addr = 0xc0 data = 0x69be addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 90 [Driver] waiting for item ...
# T = 90 [Monitor] Thread1 Second part over
# T = 90 Monitor_Thread1 addr = 0xce data = 0x6676 addr_a = 0x0 data_a = 0x0 addr_b = 0xce data_b = 0x6676
# T = 90 [Monitor] Thread0 First part over
# T = 90 [Generator] Loop : 4/20 creat next item
# T = 90 [Scoreboard] PASS! Match addr=0xce data=0x6676 addr_b=0xce data_b=0x6676
# T = 90 Driver addr = 0x36 data = 0x317 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 110 [Driver] waiting for item ...
# T = 110 [Monitor] Thread0 Second part over
# T = 110 Monitor_Thread0 addr = 0xc0 data = 0x69be addr_a = 0x0 data_a = 0x0 addr_b = 0xc0 data_b = 0x69be
# T = 110 [Monitor] Thread1 First part over
# T = 110 [Generator] Loop : 5/20 creat next item
# T = 110 [Scoreboard] PASS! Match addr=0xc0 data=0x69be addr_b=0xc0 data_b=0x69be
# T = 110 Driver addr = 0x71 data = 0x16ba addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 130 [Driver] waiting for item ...
# T = 130 [Monitor] Thread1 Second part over
# T = 130 Monitor_Thread1 addr = 0x36 data = 0x317 addr_a = 0x36 data_a = 0x317 addr_b = 0x0 data_b = 0x0
# T = 130 [Monitor] Thread0 First part over
# T = 130 [Generator] Loop : 6/20 creat next item
# T = 130 [Scoreboard] PASS! Match addr=0x36 data=0x317 addr_a=0x36 data_a=0x317
# T = 130 Driver addr = 0x6e data = 0x23f4 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 150 [Driver] waiting for item ...
# T = 150 [Monitor] Thread0 Second part over
# T = 150 Monitor_Thread0 addr = 0x71 data = 0x16ba addr_a = 0x0 data_a = 0x0 addr_b = 0x71 data_b = 0x16ba
# T = 150 [Monitor] Thread1 First part over
# T = 150 [Generator] Loop : 7/20 creat next item
# T = 150 [Scoreboard] PASS! Match addr=0x71 data=0x16ba addr_b=0x71 data_b=0x16ba
# T = 150 Driver addr = 0x47 data = 0x4fb2 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 170 [Driver] waiting for item ...
# T = 170 [Monitor] Thread1 Second part over
# T = 170 Monitor_Thread1 addr = 0x6e data = 0x23f4 addr_a = 0x0 data_a = 0x0 addr_b = 0x6e data_b = 0x23f4
# T = 170 [Monitor] Thread0 First part over
# T = 170 [Generator] Loop : 8/20 creat next item
# T = 170 [Scoreboard] PASS! Match addr=0x6e data=0x23f4 addr_b=0x6e data_b=0x23f4
# T = 170 Driver addr = 0xbb data = 0xcd69 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 190 [Driver] waiting for item ...
# T = 190 [Monitor] Thread0 Second part over
# T = 190 Monitor_Thread0 addr = 0x47 data = 0x4fb2 addr_a = 0x0 data_a = 0x0 addr_b = 0x47 data_b = 0x4fb2
# T = 190 [Monitor] Thread1 First part over
# T = 190 [Generator] Loop : 9/20 creat next item
# T = 190 [Scoreboard] PASS! Match addr=0x47 data=0x4fb2 addr_b=0x47 data_b=0x4fb2
# T = 190 Driver addr = 0x35 data = 0x9e58 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 210 [Driver] waiting for item ...
# T = 210 [Monitor] Thread1 Second part over
# T = 210 Monitor_Thread1 addr = 0xbb data = 0xcd69 addr_a = 0x0 data_a = 0x0 addr_b = 0xbb data_b = 0xcd69
# T = 210 [Monitor] Thread0 First part over
# T = 210 [Generator] Loop : 10/20 creat next item
# T = 210 [Scoreboard] PASS! Match addr=0xbb data=0xcd69 addr_b=0xbb data_b=0xcd69
# T = 210 Driver addr = 0x49 data = 0xd196 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 230 [Driver] waiting for item ...
# T = 230 [Monitor] Thread0 Second part over
# T = 230 Monitor_Thread0 addr = 0x35 data = 0x9e58 addr_a = 0x35 data_a = 0x9e58 addr_b = 0x0 data_b = 0x0
# T = 230 [Monitor] Thread1 First part over
# T = 230 [Generator] Loop : 11/20 creat next item
# T = 230 [Scoreboard] PASS! Match addr=0x35 data=0x9e58 addr_a=0x35 data_a=0x9e58
# T = 230 Driver addr = 0xca data = 0x1207 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 250 [Driver] waiting for item ...
# T = 250 [Monitor] Thread1 Second part over
# T = 250 Monitor_Thread1 addr = 0x49 data = 0xd196 addr_a = 0x0 data_a = 0x0 addr_b = 0x49 data_b = 0xd196
# T = 250 [Monitor] Thread0 First part over
# T = 250 [Generator] Loop : 12/20 creat next item
# T = 250 [Scoreboard] PASS! Match addr=0x49 data=0xd196 addr_b=0x49 data_b=0xd196
# T = 250 Driver addr = 0x5b data = 0x1d5b addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 270 [Driver] waiting for item ...
# T = 270 [Monitor] Thread0 Second part over
# T = 270 Monitor_Thread0 addr = 0xca data = 0x1207 addr_a = 0x0 data_a = 0x0 addr_b = 0xca data_b = 0x1207
# T = 270 [Monitor] Thread1 First part over
# T = 270 [Generator] Loop : 13/20 creat next item
# T = 270 [Scoreboard] PASS! Match addr=0xca data=0x1207 addr_b=0xca data_b=0x1207
# T = 270 Driver addr = 0xbc data = 0x2688 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 290 [Driver] waiting for item ...
# T = 290 [Monitor] Thread1 Second part over
# T = 290 Monitor_Thread1 addr = 0x5b data = 0x1d5b addr_a = 0x0 data_a = 0x0 addr_b = 0x5b data_b = 0x1d5b
# T = 290 [Monitor] Thread0 First part over
# T = 290 [Generator] Loop : 14/20 creat next item
# T = 290 [Scoreboard] PASS! Match addr=0x5b data=0x1d5b addr_b=0x5b data_b=0x1d5b
# T = 290 Driver addr = 0x97 data = 0x559d addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 310 [Driver] waiting for item ...
# T = 310 [Monitor] Thread0 Second part over
# T = 310 Monitor_Thread0 addr = 0xbc data = 0x2688 addr_a = 0x0 data_a = 0x0 addr_b = 0xbc data_b = 0x2688
# T = 310 [Monitor] Thread1 First part over
# T = 310 [Generator] Loop : 15/20 creat next item
# T = 310 [Scoreboard] PASS! Match addr=0xbc data=0x2688 addr_b=0xbc data_b=0x2688
# T = 310 Driver addr = 0x3c data = 0xe700 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 330 [Driver] waiting for item ...
# T = 330 [Monitor] Thread1 Second part over
# T = 330 Monitor_Thread1 addr = 0x97 data = 0x559d addr_a = 0x0 data_a = 0x0 addr_b = 0x97 data_b = 0x559d
# T = 330 [Monitor] Thread0 First part over
# T = 330 [Generator] Loop : 16/20 creat next item
# T = 330 [Scoreboard] PASS! Match addr=0x97 data=0x559d addr_b=0x97 data_b=0x559d
# T = 330 Driver addr = 0x79 data = 0x5f5f addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 350 [Driver] waiting for item ...
# T = 350 [Monitor] Thread0 Second part over
# T = 350 Monitor_Thread0 addr = 0x3c data = 0xe700 addr_a = 0x3c data_a = 0xe700 addr_b = 0x0 data_b = 0x0
# T = 350 [Monitor] Thread1 First part over
# T = 350 [Generator] Loop : 17/20 creat next item
# T = 350 [Scoreboard] PASS! Match addr=0x3c data=0xe700 addr_a=0x3c data_a=0xe700
# T = 350 Driver addr = 0x63 data = 0xd727 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 370 [Driver] waiting for item ...
# T = 370 [Monitor] Thread1 Second part over
# T = 370 Monitor_Thread1 addr = 0x79 data = 0x5f5f addr_a = 0x0 data_a = 0x0 addr_b = 0x79 data_b = 0x5f5f
# T = 370 [Monitor] Thread0 First part over
# T = 370 [Generator] Loop : 18/20 creat next item
# T = 370 [Scoreboard] PASS! Match addr=0x79 data=0x5f5f addr_b=0x79 data_b=0x5f5f
# T = 370 Driver addr = 0x3 data = 0x770e addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 390 [Driver] waiting for item ...
# T = 390 [Monitor] Thread0 Second part over
# T = 390 Monitor_Thread0 addr = 0x63 data = 0xd727 addr_a = 0x0 data_a = 0x0 addr_b = 0x63 data_b = 0xd727
# T = 390 [Monitor] Thread1 First part over
# T = 390 [Generator] Loop : 19/20 creat next item
# T = 390 [Scoreboard] PASS! Match addr=0x63 data=0xd727 addr_b=0x63 data_b=0xd727
# T = 390 Driver addr = 0xab data = 0x489a addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 410 [Driver] waiting for item ...
# T = 410 [Monitor] Thread1 Second part over
# T = 410 Monitor_Thread1 addr = 0x3 data = 0x770e addr_a = 0x3 data_a = 0x770e addr_b = 0x0 data_b = 0x0
# T = 410 [Monitor] Thread0 First part over
# T = 410 [Generator] Loop : 20/20 creat next item
# T = 410 [Scoreboard] PASS! Match addr=0x3 data=0x770e addr_a=0x3 data_a=0x770e
# T = 410 Driver addr = 0x63 data = 0x2527 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 430 [Driver] waiting for item ...
# T = 430 [Monitor] Thread0 Second part over
# T = 430 Monitor_Thread0 addr = 0xab data = 0x489a addr_a = 0x0 data_a = 0x0 addr_b = 0xab data_b = 0x489a
# T = 430 [Monitor] Thread1 First part over
# T = 430 [Generator] Done generation of 20 items
# T = 430 [Scoreboard] PASS! Match addr=0xab data=0x489a addr_b=0xab data_b=0x489a
# T = 450 [Monitor] Thread1 Second part over
# T = 450 Monitor_Thread1 addr = 0x63 data = 0x2527 addr_a = 0x0 data_a = 0x0 addr_b = 0x63 data_b = 0x2527
# T = 450 [Scoreboard] PASS! Match addr=0x63 data=0x2527 addr_b=0x63 data_b=0x2527

你可能感兴趣的:(SystemVerilog,&,Questa,Sim,学习,systemverilog,testbench)