接口interface的定义和使用(SystemVerilog)

本文详细介绍了接口在硬件设计和验证中的重要性,包括接口简化模块间连接、modport的信号分组、clocking时钟块确保同步、参数传递增强重用性、SVA断言及覆盖率检查以及生成时钟和复位信号的功能。通过实例展示了接口如何提高设计的可读性和可维护性,以及如何利用接口进行有效的时序验证和协议检查。
摘要由CSDN通过智能技术生成

概念

首先,要知道为什么要使用接口?带来了什么样的便利?随着设计的日益复杂,设计中的不同功能的设计会被切割成不同的模块,模块与模块之间通信端口随着设计的复杂程度增加而变得很多,测试平台与不同模块和子模块DUT之间的端口连接也就变得更加复杂。这时,如果我们仍然采用对每个端口一一连接的方式,显然是有很承重的负担,日后代码的维护工作也变得很困难。接口就起到一根很粗电缆线的作用,连接着设计与验证环境。
如下,当两个模块module之间不采用接口连接时:

//子模块1
module memMod(  input logic req,
				input logic clk,
				input logic start,
				input logic [1:0] mode,
				input logic [7:0] addr,
				inout wire [7:0] data,
				output bit gnt,
				output bit rdy );
	logic avail;
...
endmodule

//子模块2
module cpuMod(  input logic clk,
				input logic gnt,
				input logic rdy,
				inout wire [7:0] data,
				output logic req,
				output logic start,
				output logic [7:0] addr,
				output logic [1:0] mode );
...
endmodule

//顶层模块,将模块间的信号做连接
module top;
	logic req, gnt, start, rdy;
	logic clk = 0;
	logic [1:0] mode;
	logic [7:0] addr;
	wire [7:0] data;
	memMod mem(req, clk, start, mode, addr, data, gnt, rdy);
	cpuMod cpu(clk, gnt, rdy, data, req, start, addr, mode);
endmodule

如下,当设计之间使用interface做连接时:

interface simple_bus; // 定义接口
	logic req, gnt;
	logic [7:0] addr, data;
	logic [1:0] mode;
	logic start, rdy;
endinterface: simple_bus

module memMod( top_vif vif, //传递接口
			   input logic clk);
	logic avail;	
	always @(posedge clk) a.gnt <= a.req & avail;//调用通过接口传递的参数
endmodule

module cpuMod( top_vif vif, input logic clk);
	...
endmodule

//两个module的顶层模块
module top;
	logic clk = 0;
	simple_bus top_vif(); // 例化接口
	memMod mem(top_vif, clk); // 写法一:将接口与module连接
	cpuMod cpu(.top_vif(top_vif), .clk(clk)); //写法二: 
endmodule

很明显对比发现,在设计的RTL中采用接口对端口的连接会更简便,维护也更容易。在验证环境中,发激励的driver和采样的monitor都需要直接与设计之间“沟通”,同样的,如果没有接口,试想每次写driver都要自己手动连接与设计上的信号,估计你会分分钟删库跑路吧。所以,以下将更多的是从验证的角度阐述接口的特点和使用。

一、特点

  • 接口在module可以直接声明例化,但在class中只能声明为virtual。正是通过虚接口连接到硬件上,使得“软件”部分可以操作硬件设计;
  • interface可以在module中声明和实例化,但是module不能存在interface中;
  • 接口内可以定义参数、变量、initial和always块、function、task等;
  • 接口内的信号需要DUT连接,因此要用四值逻辑来确保有x和z状态,而非二值逻辑。接口中最好可以使用 logic 做为信号的类型。因为logic可以直接赋值,而 wire 必须要被连续赋值语句来驱动。

二、modport

modport可以简单的理解成电线收纳盒。他可以将接口内的信号分组,并定义它们的方向。如下:

interface i2;
	logic a, b, c, d;
	modport master (input a, b, output c, d); 
	modport slave (output a, b, input c, d);
endinterface

module m (i2 vif);
...
endmodule
module s (i2 vif);
...
endmodule

module top;
	i2 vif();//例化接口
	
	//与设计中的信号做连接
	m u1(vif.master);//连接方式一:直接连接接口中的对应modport
	s u2(.vif(vif.slave));//连接方式二:

	//将虚接口传递给验证组件
	//1.如果是纯SV的环境,可以在test--> env --> agent 每一层都定义一个set_intf()函数,在其中将虚接口句柄做连接。
	//2.如果是UVM环境,在这里可以通过uvm_config_db给对应层次路径传递一个虚接口,如下:
	uvm_config_db#(virtual i2)::set(uvm_root::get(), "uvm_test_top.env.agent", "vif", vif);
endmodule

注意:modport起到的就是整理interface内部的信号的作用,所以它整理的信号必须要是事先在interface中定义好的信号。

三、clocking时钟块

时钟块可以保证接口内的信号可以同步地驱动和采样。每一个时钟块都有一个时钟表达式,对应一个时钟域。还可以通过default指定一个输出和输入信号采样的时钟偏移。

interface A_Bus( input logic clk );
	logic req, gnt;
	logic [7:0] addr, data;
	
	clocking drv_cb @(posedge clk);
		default input #1ps output #1ps;
		input gnt;
		output req, addr;
		inout data;
	endclocking
	
	clocking mon_cb @(posedge clk);
		default input #1ps;
		input gnt, req, addr, data;
	endclocking
endinterface	

给clocking时钟块内input和output采样和驱动加上一个很小的延时,目的也就是为了防止采样时的竞争问题,具体可以看:clocking块解决采样时的竞争

四、参数传递

interface可以和module一样,在其中定义参数,然后可以在例化的时候传递进来新的参数,极大的增加interface的重用性。如下:

interface simple_bus #(AWIDTH = 8, DWIDTH = 8)
					  (input logic clk); //定义参数,在例化时传递进来
	logic req, gnt;
	logic [AWIDTH-1:0] addr;
	logic [DWIDTH-1:0] data;
	logic [1:0] mode;
	logic start, rdy;
	
	modport master( input gnt, rdy, clk,
				    output req, addr, mode, start,
					ref data,
					import task masterRead(input logic [AWIDTH-1:0] raddr),
					import task masterWrite(input logic [AWIDTH-1:0] waddr));

	task masterWrite(input logic [AWIDTH-1:0] waddr);
		...
	endtask
	
	task masterRead(input logic [AWIDTH-1:0] raddr); 
		...
	endtask
endinterface

module top;
	simple_bus #(.DWIDTH(16)) wide_intf(clk);
endmodule

注:modport中也可以将interface中的方法进行整理,用import将方法引入到modport中,它们对任何使用这个modport的块都是可见的。也就是说在上面的例子中,拿到这个虚接口 vif 的组件可以通过 vif.master.masterRead 的方式,直接调用接口内 masterRead() 任务。

五、SVA断言和断言覆盖率

对于一般的协议接口,因为协议有对时序是有要求,所以在接口中定义断言对协议的时序进行检查,来调试设计中遇到的问题。同时断言也可以触发功能覆盖率和断言覆盖率,所以也可以在接口中定义断言covergroup。
以一个APB协议的接口为例,如下:

interface apb_vif (input clk, input rstn);

  logic [31:0] paddr;
  logic        pwrite;
  logic        psel;
  logic        penable;
  logic [31:0] pwdata;
  logic [31:0] prdata;
  logic        pready;
  logic        pslverr;

	... //clocking的定义省略
  
	//---------APB 读写操作的时序覆盖------------
    covergroup apb_write_read @(posedge clk iff (rstn && penable));
    	write_read_order: coverpoint pwrite{
		      bins write_write = (1 => 1);
		      bins write_read  = (1 => 0);
		      bins read_write  = (0 => 1);
		      bins read_read   = (0 => 0);
  		  } 
    endgroup

	//----------例化covergroup--------
	initial begin
		automatic  apb_write_read  cg = new();
	end
	
	 //--------收集断言覆盖率--------
	  property p_write_read_burst_trans;
	    	logic[31:0] addr;
	    	@(posedge clk) 
	    	($rose(penable) && pwrite, addr=paddr) 
	    		|-> (##2 ($rose(penable) && !pwrite && addr==paddr)); 
	  endproperty: p_write_read_burst_trans
	  cover property(p_write_read_burst_trans);
		
	 //--------用断言检查时序---------
	  property p_paddr_no_x;
	    	@(posedge clk) 
	    	psel |-> !$isunknown(paddr);
	  endproperty: p_paddr_no_x
	  assert property(p_paddr_no_x) else `uvm_error("ASSERT", "PADDR is unknown when PSEL is high")

	 //--------断言控制----------
	 initial begin
	    fork
	      forever begin
	        wait(rstn == 0);
	        $assertoff(); //关闭断言
	        wait(rstn == 1);
	        $asserton(); //打开断言
	      end
	    join_none
     end

endinterface

六、产生时钟和复位信号

在一些设计中,我们可能需要单独的设置一个reset_agent,专门用于对设计的复位信号进行测试。由此我们可以定义一个reset_vif,用来产生时钟和复位信号的控制。如下:

interface reset_if(input logic clk);
  logic reset;
  
  //-----产生时钟-----
  initial begin 
	 logic clk <= 0;
	 forever begin
		#5ns clk <= ~clk;
	 end	
  end

//------产生复位信号-----  	
  task reset_dut;
	reset = 1'b0;
	#5ns;
	reset = 1'b1;
	repeat (100) @(negedge clk);
	#2ns;
	reset = 1'b0;
  endtask
endinterface
  • 11
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小小verifier

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

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

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

打赏作者

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

抵扣说明:

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

余额充值