验证平台搭建案例(2) | Systemverilog 笔记 11

Posted by Kion on January 26, 2020

经过一段时间的学习。大概了解了SystemVerilog的语法,以及一个验证平台的搭建流程。可以说,验证平台的搭建方法是相对固定的。因为验证组件是确定的,无非就是generator,driver,monitor,scoreboard等。不过要真正掌握搭建过程,还是需要不断的练习。


下面验证如下一个加法器。

1uJR0K.png

验证平台的结构还是如下图所示。

`include "interface.sv"
`include "test.sv"
`include "adder.sv"
module top;
/* 最顶层的文件,用于连接DUT和验证平台;
包含DUT,测试以及接口实例;
接口能将DUT与验证平台相连 */
	
	bit clk;
	bit reset;
	
	/* 实例化接口 */
	intf tif(clk,reset);
	
	/* 实例化DUT,并配置接口 */
	adder DUT (
		.clk(tif.clk),
		.reset(tif.reset),
		.a(tif.a),
		.b(tif.b),
		.valid(tif.valid),
		.c(tif.c)
	);
	
	/* 连接测试接口 */
	test t1(tif);
	
	always #5 clk = ~clk;
	
	initial begin
		reset = 1;
		#5 reset=0;
	end
endmodule
`include"environment.sv"
program test(intf intf);
	/* 创建环境,配置验证条件,初始化验证 */
	
	environment env;
	
	initial begin
		/* 实例化环境类 */
		env = new(intf);
		
		/* 设置事务数目 */
		env.gen.numtr = 10;
		
		/* 启动环境 */
		env.run();
	end
	
endprogram
`include "trans.sv"
`include "generator.sv"
`include "driver.sv"
`include "monitor.sv"
`include "scoreboard.sv"

class environment;
	generator gen;
	driver	drv;
	monitor mon;
	scoreboard scb;
	
	mailbox gen2drv;
	mailbox mon2scb;
	
	/* generator结束生成 */
	event gen_ended;
	
	virtual intf vif;
	
	function new(virtual intf vif);
		this.vif = vif;
		
		gen2drv = new();
		mon2scb = new();
		
		gen = new(gen2drv,gen_ended);
		drv = new(vif,gen2drv);
		mon = new(vif,mon2scb);
		scb = new(mon2scb);
	endfunction
	
	/* 启动进行清零操作 */
	task pre_test();
		drv.reset();
	endtask
	
	/* 开始测试 */
	task test();
		fork
			gen.main();
			drv.main();
			mon.main();
			scb.main();
		join_any
	endtask
	
	/* 检测generator,driver,scoreboard是否结束 */
	task post_test();
		wait(gen_ended.triggered);
		wait(gen.numtr == drv.numtr);
		wait(gen.numtr == scb.numtr);
	endtask
	
	/* 运行测试 */
	task run;
		pre_test();
		test();
		post_test();
		$finish;
	endtask
endclass
class driver;
/* 接收generator生成的数据包,并转化成接口信号,驱动给dut */

	/* 生成虚接口 */
	virtual intf vif;
	
	/* 定义与driver通讯的信箱 */
	mailbox gen2drv;
	
	/* 总数据包 */
	int numtr;
	
	/* 构造函数,从上层获取接口以及信箱 */
	function new(virtual intf vif, mailbox gen2drv);
		this.vif = vif;
		this.gen2drv = gen2drv;
	endfunction
	
	/* 重启任务 */
	task reset;
		/* 等待重启信号 */
		wait(vif.reset);
		vif.a <= 0;
		vif.b <= 0;
		vif.valid <= 0;
		wait(!vif.reset);
		$display("----------[driver] reset----------");
	endtask
	
	/* 主任务 */
	task main;
		forever begin
			transaction tr;
			
			/* 接收信箱中来自generator的transaction */
			gen2drv.get(tr);
			$display("----------[driver] no.%0d----------", numtr);
			
			@(posedge vif.clk);
			vif.valid <= 1;
			vif.a <= tr.a;
			vif.b <= tr.b;
			$display("---------a = %0d; b = %0d----------", tr.a, tr.b);
			@(posedge vif.clk);
			vif.valid <= 0;
			
			numtr++;
		end
	endtask
endclass
class generator;
/* generator用于生成随机的transaction,即数据包,
并通过mailbox发送给driver */
	
	/* 定义一个随机的transaction句柄 */
	rand transaction tr;
	
	/* 与driver相通信的信箱 */
	mailbox gen2drv;
	
	/* 最大的transaction数目 */
	int numtr;
	
	/* transaction全部生成的标识 */
	event ended;
	
	function new(mailbox gen2drv, event ended);
		this.gen2drv = gen2drv;
		this.ended = ended;
	endfunction
	
	/* 主任务,生成随机的transaction,并放入信箱 */
	task main();
		repeat (numtr) begin
			tr = new();
			assert(tr.randomize());
			gen2drv.put(tr);
		end
		/* 所有测试用数据包生成完毕,触发结束事件 */
		->ended;
	endtask
endclass
class monitor;
/* 监控dut的接口信号,并转换为transaction,通过信箱送往scoreboard */
	virtual intf vif;
	
	mailbox mon2scb;
	
	function new(virtual intf vif, mailbox mon2scb);
		this.vif = vif;
		this.mon2scb = mon2scb;
	endfunction
	
	task main;
		forever begin
			transaction tr;
			tr = new();
			@(posedge vif.clk);
			wait(vif.valid);
			tr.a = vif.a;
			tr.b = vif.b;
			/* 等待dut计算结果 */
			@(posedge vif.clk);
			tr.c = vif.c;

			mon2scb.put(tr);
		end
	endtask
endclass
class scoreboard;
/* 接收监视器发来的数据包,比较期望数据与实际数据 */
	mailbox mon2scb;
	
	int numtr;
	
	/* 期望结果 */
	bit [7:0] c;
	
	function new(mailbox mon2scb);
		this.mon2scb = mon2scb;
		c = 7'b0;
	endfunction
	
	task main;
		transaction tr;
		
		forever begin
			mon2scb.get(tr);
			/* 计算期望结果 */
			c = tr.a + tr.b;
			if (c != tr.c)
				$display("---ERROR!!!---a = %0d; b = %0d; c_dut = %0d; c_rm = %0d-", 
				tr.a, tr.b, tr.c, c);
			else
				$display("---SUCCESS!!---a = %0d; b = %0d; c_dut = %0d; c_rm = %0d-", 
				tr.a, tr.b, tr.c, c);
			numtr++;
		end
	endtask
endclass
class transaction;
/* 一个transaction就是一个数据包,包含设计中的输入和输出 */
	rand bit [3:0] a;
	rand bit [3:0] b;
	bit [6:0] c;	
endclass


interface intf(input logic clk, reset);
/* 接口,连接端口信号,这个接口十分简单,没有设置modport*/

	logic [3:0] a;
	logic [3:0] b;
	logic valid;
	logic [6:0] c;
	
endinterface

参考