SystemVerilog基础知识 2.2 interface实例

SystemVerilog 2.2 interface实例

SystemVerilog接口允许我们将多个信号组合在一起,并将它们表示为一个端口。所有这些信号都可以在一个地方声明和维护,并且易于维护。接口内的信号由接口实例句柄访问。

2.2.1 语法

接口模块的定义以interface和endinterface关键词开始和结束,可以和module一样被例化。

interface [name] ([port_list]);
	[list_of_signals]
endinterface

接口还可以包含函数、任务、变量和参数,使其更像类模板。它还能够通过modport构造以及带有时钟块的测试台同步功能为不同模块端口定义方向信息的策略。它还可以具有断言、覆盖记录和其他协议检查元素。最后但并非最不重要的是,它还可以包含初始和始终过程以及连续赋值语句。

注意:interface中不能例化module,但是在module中可以例化interface。

SystemVerilog是当前非常流行的硬件描述语言,接下来我们将以两个例子来看interface的定义和使用,其中会列出Verilog和SystemVerilog两种语言来描述相同的设计。

2.2.2 Verilog接口实例

让我们看看如何在测试台中使用接口,并通过端口列表连接到标准Verilog设计。下面显示的代码是Verilog中上下计数器的设计。此模块接受一个参数来决定计数器的宽度。它还接受仅当load_en为1时才加载到计数器中的输入负载值load。
在这里插入图片描述
当向下输入为1时,计数器开始向下计数,否则计数器向上计数。滚动输出指示计数器何时从max_value转换为0或从0转换为max_value。

module counter_ud 
  #(parameter WIDTH = 4)
  ( 
  input 					clk,
  input 					rstn,
  input wire [WIDTH-1:0]	load,
  input 					load_en,
  input 					down,
  output 					rollover,
  output reg [WIDTH-1:0]	count 
);
  
  always @ (posedge clk or negedge rstn) begin
    if (!rstn)
   		count <= 0;
    else 
      if (load_en)
        count <= load;
      else begin
      	if (down)
        	count <= count - 1;
      	else
        	count <= count + 1;
      end
  end
  
  assign rollover = &amp;count;
endmodule

下面声明了一个名为cnt_if的接口,其可参数化值为计数器信号的宽度。此任务还有一个任务init()来分配值。

interface cnt_if #(parameter WIDTH = 4) (input bit clk);
  logic 			rstn;
  logic 			load_en;
  logic [WIDTH-1:0] load;
  logic [WIDTH-1:0] count;
  logic 			down;
  logic 			rollover;
endinterface

module tb;
  reg clk;
  
  // TB Clock Generator used to provide the design
  // with a clock -> here half_period = 10ns => 50 MHz
  always #10 clk = ~clk;
  
  cnt_if 	  cnt_if0 (clk);
  counter_ud  c0 ( 	.clk 		(cnt_if0.clk),
                  	.rstn 		(cnt_if0.rstn),
                  	.load 		(cnt_if0.load),
                  	.load_en 	(cnt_if0.load_en),
                  	.down 		(cnt_if0.down),
                  	.rollover 	(cnt_if0.rollover),
                  	.count 		(cnt_if0.count));
  
  initial begin
    bit load_en, down;
    bit [3:0] load;
    
    $monitor("[%0t] down=%0b load_en=%0b load=0x%0h count=0x%0h rollover=%0b", 
    	$time, cnt_if0.down, cnt_if0.load_en, cnt_if0.load, cnt_if0.count, cnt_if0.rollover);
        
    // Initialize testbench variables
    clk <= 0;
    cnt_if0.rstn <= 0;
    cnt_if0.load_en <= 0;
    cnt_if0.load <= 0;
    cnt_if0.down <= 0;
    
    // Drive design out of reset after 5 clocks
    repeat (5) @(posedge clk);
    cnt_if0.rstn <= 1; // Drive stimulus -> repeat 5 times
    for (int i = 0; i < 5; i++) begin
      
      // Drive inputs after some random delay 
      int delay = $urandom_range (1,30);
      #(delay);
      
      // Randomize input values to be driven
      std::randomize(load, load_en, down);
     
      // Assign tb values to interface signals
      cnt_if0.load <= load;
      cnt_if0.load_en <= load_en;
      cnt_if0.down <= down;
    end
    
    // Wait for 5 clocks and finish simulation
    repeat(5) @ (posedge clk);
    $finish;
  end
endmodule

Simulation Log

> ncsim> run
[0] down=0 load_en=0 load=0x0 count=0x0 rollover=0
[96] down=1 load_en=1 load=0x1 count=0x0 rollover=0
[102] down=0 load_en=0 load=0x9 count=0x0 rollover=0
[108] down=1 load_en=1 load=0x1 count=0x0 rollover=0
[110] down=1 load_en=1 load=0x1 count=0x1 rollover=0
[114] down=1 load_en=0 load=0xc count=0x1 rollover=0
[120] down=1 load_en=0 load=0x7 count=0x1 rollover=0
[130] down=1 load_en=0 load=0x7 count=0x0 rollover=0
[150] down=1 load_en=0 load=0x7 count=0xf rollover=1
[170] down=1 load_en=0 load=0x7 count=0xe rollover=0
[190] down=1 load_en=0 load=0x7 count=0xd rollover=0
Simulation complete via $finish(1) at time 210 NS + 0

2.2.3 SystemVerilog接口实例

现在让我们看看如何在测试台中使用接口并将其连接到SystemVerilog设计模块。SystemVerilog允许模块接受接口作为端口列表,而不是单个信号。在下面所示的设计示例中,我们用一个用于定义设计功能的接口句柄替换了counter_ud的portlist。
在这里插入图片描述

`timescale 1ns/1ns

// This module accepts an interface object as the port list
module counter_ud  #(parameter WIDTH = 4) (cnt_if _if);
  always @ (posedge _if.clk or negedge _if.rstn) begin
    if (!_if.rstn)
   		_if.count <= 0;
    else 
      if (_if.load_en)
        _if.count <= _if.load;
      else begin
        if (_if.down)
        	_if.count <= _if.count - 1;
      	else
        	_if.count <= _if.count + 1;
      end
  end
  
  assign _if.rollover = &_if.count;
endmodule

设计实例被传递一个名为cnt_if的接口句柄,用于从测试台驱动设计输入。如果需要,可以使用相同的接口句柄来监控设计的输出。

// Interface definition is the same as before

module tb;
  reg clk;
  
  // TB Clock Generator used to provide the design
  // with a clock -> here half_period = 10ns => 50 MHz
  always #10 clk = ~clk;
  
  cnt_if 	  cnt_if0 (clk);
  
  // Note that here we just have to pass the interface handle
  // to the design instead of connecting each individual signal
  counter_ud  c0 (cnt_if0);
  
  // Stimulus remains the same as before

Simulation Log

*> ncsim> run
[0] down=0 load_en=0 load=0x0 count=0x0 rollover=0
[96] down=1 load_en=1 load=0x1 count=0x0 rollover=0
[102] down=0 load_en=0 load=0x9 count=0x0 rollover=0
[108] down=1 load_en=1 load=0x1 count=0x0 rollover=0
[110] down=1 load_en=1 load=0x1 count=0x1 rollover=0
[114] down=1 load_en=0 load=0xc count=0x1 rollover=0
[120] down=1 load_en=0 load=0x7 count=0x1 rollover=0
[130] down=1 load_en=0 load=0x7 count=0x0 rollover=0
[150] down=1 load_en=0 load=0x7 count=0xf rollover=1
[170] down=1 load_en=0 load=0x7 count=0xe rollover=0
[190] down=1 load_en=0 load=0x7 count=0xd rollover=0
Simulation complete via $finish(1) at time 210 NS + 0*

2.2.4 为什么二者不同?

Verilog通过其模块端口连接不同模块。对于大型设计,这种连接方法可能会变得更加耗时和重复。这些端口中的一些可能包括与总线协议相关的信号,如AXI/AHB、时钟和复位引脚、往返于RAM/存储器和其他外围设备的信号。

2.2.4.1 使用verilog 端口

这是Verilog中传统的端口连接方式。

	module d_slave ( input  clk,
                            reset,
                            enable,
                            // Many more input signals
                      output gnt,
                             irq,
                             // Many more output signals);
    // Some design functionality
    endmodule
                             
	module d_top ( [top_level_ports] );
	    reg [`NUM_SLAVES-1:0] clk;                  // Assume `NUM_SLAVES is a macro set to 2
	    reg [`NUM_SLAVES-1:0] tb_reset;
	    // Other declarations
	    
		d_slave slave_0  (  .clk   (d_clk[0]),      // These connections have to be 
		                  	.reset (d_reset[0]) 	// repeated for all other slave instances
		                  		...
		                  	.gnt (d_gnt[0]),
		                  	... );
		                  	
		d_slave slave_1  ( ... );
		d_slave slave_2  ( ... );
	endmodule

让我们考虑一个场景,上面所示的设计中有12个从设备。如果d_slave模块端口发生了更改,则该更改也必须反映在d_top中的所有12个从属实例连接中。

2.2.4.2 缺点

使用Verilog端口方法进行连接的一些缺点是:

  1. 易于跟踪、调试和维护
  2. 太容易创建或破坏设计功能
  3. 设计要求的变更可能需要对多个模块进行修改
  4. 在多个模块、通信协议和其他地方需要复制

2.2.4.3 使用SystemVerilog 端口

注意,模块d_top简单地使用接口与从实例连接,而不是如前所示重复声明与从块的每个信号的连接。

interface slave_if (input logic clk, reset);
	reg clk;
	reg reset;
	reg enable;
	
	reg gnt;
	// Declarations for other signals follow
endinterface

module d_slave (slave_if s_if);

	// Design functionality
	always (s_if.enable & s_if.gnt) begin 	// interface signals are accessed by the handle "s_if"
		// Some behavior
	end
endmodule

module d_top (input clk, reset);
	// Create an instance of the slave interface
	slave_if 	slave_if_inst ( .clk (clk),
	                            .reset (reset));
	
	d_slave 	slave_0 (.s_if (slave_if_inst));
	d_slave 	slave_1 (.s_if (slave_if_inst));
	d_slave 	slave_2 (.s_if (slave_if_inst));
endmodule

现在,如果从属接口中的一个信号发生了变化,它将自动应用于所有实例。在SystemVerilog中,模块端口列表也可以有一个具有接口类型的端口,而不是通常的输入、输出和inout。

2.2.5 接口阵列

在下面的示例中,在顶级测试台模块中创建并实例化了一个名为myInterface的带有空端口列表的接口。也可以省略空端口列表的括号,而用分号截断语句

// interface myInterface;
	
	interface myInterface ();
		reg 		gnt;
		reg 		ack;
		reg [7:0]	irq;
		
		... 
	endinterface
	
	module tb;
		// Single interface handle
		myInterface 	if0 ();   
		
		// An array of interfaces
		myInterface 	wb_if [3:0] ();
		
		// Rest of the testbench
	endmodule

可以实例化一个名为if0的接口,并且应该通过引用该句柄来访问该接口中的信号。然后,这可以用来驱动和采样去往DUT的信号。我们还可以有一系列接口。在这里,这个数组被称为wb_if,它有4个接口实例。

module myDesign ( myInterface dut_if, 
	                  input logic clk);
	                  
		always @(posedge clk)
			if (dut_if.ack)
				dut_if.gnt <= 1;
				
	endmodule

	module tb;
		reg clk;
	
		// Single interface handle connection
		myInterface  if0;
		myDesign 	 top (if0, clk);
		
		// Or connect by name
		// myDesign  top (.dut_if(if0), .clk(clk)); 
		
		// Multiple design instances connected to the appropriate
		// interface handle
		myDesign 	md0 (wb_if[0], clk);
		myDesign 	md1 (wb_if[1], clk);
		myDesign 	md2 (wb_if[2], clk);
		myDesign 	md3 (wb_if[3], clk);
		
	endmodule

当一个接口被引用为端口时,其中的变量和网络分别具有ref和inout访问权限。如果在设计中使用相同的标识符作为接口实例名和端口名,那么也可以使用隐式端口连接。

module tb;
		reg clk;
		
		myInterface 	dut_if();
		
		// Can use implicit port connection when all port signals have same name
		myDesign 		top (.*);
		
	endmodule

猜你喜欢

转载自blog.csdn.net/jihanjihan/article/details/128579359
2.2