systemverilog学习 ---- program和interfece

program

在组合逻辑中,同一个信号由于不同路径的延迟不同,到达电路某一汇合点的时间会有先后,就会产生竞争,从而产生一个尖峰脉冲。systemverilog为了避免竞争的问题,引入program,所有与设计相关的线程在module内执行,所有与验证有关的线程在program内执行。program内所有的元素都是在reactive region内执行,而module内的非堵塞赋值在active region,二者隔离开来,从而避免了竞争。
program的声明形式如下:

program test(input clk, input [7:0]	addr, output [7:0] wdata);
endprogram
	or
program test (interfece mem_intf);

endprogram
  • program块可以实例化,可以连接端口像一个module一样
  • 可以包含一个或者多个初始块
  • 不能包含always语句块,module,interface和其它的program
  • 在program语句块中,对于变量的赋值只能使用阻塞赋值,而不能使用非阻塞赋值,否则会报错。
    以下的例子对比module和program两者的不同。
//design code
module design_ex(output bit [7:0]   addr);

    initial begin
        addr <= 10;
    end

endmodule

//testbench
module  testbench(input bit [7:0]   addr);
    initial begin
        $display("\t Addr = %0d", addr);
    end
endmodule

//testbench top
module tbench_top;

    //design instance
    wire [7:0]  addr;
    design_ex dut(addr);

    //testbench instance
    testbench test(addr);

endmodule

module版本,下例中dut模块design_ex,initial块用非阻塞赋值驱动addr为10,而在testbench模块中,initial采样addr的值打印输出。这个时候,就存在先非阻塞赋值还是先采样打印的问题了,即竞争。通过verilog仿真事件队列,我们知道display处于active事件,会先执行,而非阻塞赋值的更新会后执行,因此addr打印为0.执行结果:
在这里插入图片描述

//design code

module design_ex(output bit [7:0]   addr);

    initial begin
        addr <= 10;
    end
endmodule

//testbench

program testbench(input bit [7:0]   addr);

    initial begin
        $display("\t Addr = %0d", addr);
    end

endprogram

//testbench top

module tbench_top;

    wire [7:0]  addr;

    //design instance
    design_ex dut(addr);
    //testbench instance
    testbench test(addr);

endmodule

而program版本,则是先执行module内的语句,然后执行program的语句打印结果,另外program把initial执行完后,会结束仿真。
在这里插入图片描述

interface

在verilog中,不同的块之间的交流是通过模块的端。systemverilog添加了interface,封装了模块之间的通信。它把testbench和dut的信号和连线捆绑在一起。下图,是我们verilog testbench和dut的连接关系
systemverilog学习 ---- program和interfece_第1张图片
下面这个图展示了有interface的design和testbench的连线。
systemverilog学习 ---- program和interfece_第2张图片

  • 接口时一捆命名的线,目的是封装通信。
  • 同时包含信号的方向(输入还是输出)、时序信息(时钟块)
  • 接口也可以有参数、常量、变量、函数和任务。

一个简单的接口声明如下:

interface interface_name;
	...
	interface items
	...
endinterface
  • 一个接口可以被声明和例化(和模块一样)。interfaca_name inst_name。
  • 一个接口可以有参数、常量、变量、函数和任务。
interface intf;
    //declaring the signals
    logic [3:0] a;
    logic [3:0] b;
    logic [6:0] c;
endinterface //intf

module tb;

    //creating the instance of interface
    intf i_intf();

    //DUT instance
    adder DUT(
        .a(i_intf.a),
        .b(i_intf.b),
        .c(i_intf.c)
    );

    initial begin
        i_intf.a = 6;
        i_intf.b = 8;
        $display("Value of a = %0d, b= %0d", i_intf.a, i_intf.b);
        #5;
        $display("Sum of a and b, c = %0d", i_intf.c);
        i_intf.a = 3;
        i_intf.b = 8;
        $display("Value of a = %0d, b= %0d", i_intf.a, i_intf.b);
        #5;
        $display("Sum of a and b, c = %0d", i_intf.c);
    end
endmodule

module adder(
    input   [3:0]   a   ,
    input   [3:0]   b   ,
    output  [6:0]   c   
);
    assign c = a + b;
endmodule

执行结果如下:
systemverilog学习 ---- program和interfece_第3张图片

vertual interface

sv还可以用virtual 修饰interface,虚接口virtual interface表示一个接口实例。虚接口必须先初始化才能使用,即必须连接或者指向实际的接口,访问没有初始化的虚接口会导致运行时错误。虚接口也可以作为类成员,可以通过new方法初始化。虚接口变量可以作为参数传递给任务、函数和方法。通过虚接口句柄virtual_interface.variable可以访问所有的接口变量和方法。一个虚接口变量,可以在仿真时的不同时刻代表不同的接口实例。
虚接口仅支持操作符=,==,!=三种,另一个操作数可以是相同类型的虚接口,相同类型的接口实例或者空null。
我们的接口作为连接testbench和dut的连线,本质上是静态的,而类是动态的。因此我们不能在类内声明接口,但可以用一个变量指向接口。虚接口就是类访问接口信号的帮手。

interface intf;
    //declaring the signals
    logic [3:0] a   ;
    logic [3:0] b   ;
    logic [6:0] c   ;

endinterface

class environment;
    //virtual interface
    virtual intf vif;
    //constructor
    function new(virtual intf vif);
        //get the interface from test
        this.vif = vif;
    endfunction
    //run tasks
    task run;
        vif.a = 6;
        vif.b = 4;
        $display("Value of a = %0d, b = %0d", vif.a, vif.b);
        #5;
        $display("Sum of a and b, c = %0d", vif.c);
        $finish;
    endtask
endclass

program test(intf i_intf);
    //declaring environment instance
    environment env;
    initial begin
        //creating environment
        env = new(i_intf);
        //calling run of env
        env.run();
    end
endprogram

module tbench_top;
    //creating instance of interface
    intf i_intf();
    //testcase instance
    test t1(i_intf);
    //DUT instance,interface signals are connected to the DUT ports
    adder DUT(
        .a(i_intf.a),
        .b(i_intf.b),
        .c(i_intf.c)
    );
endmodule

module adder(
    input   wire    [3:0]   a   ,
    input   wire    [3:0]   b   ,

    output  wire    [6:0]   c  
);
    assign c = a + b;
    
endmodule

来看看我们改版的testbench,首先定义接口,把dut的端口加入,接口把变量设置为logic类型,并给出位宽。然后定义类environment,该类包含虚接口vif,构造函数将传入的虚接口赋值给类内虚接口成员,还定义了任务run,run通过虚接口访问接口的信号,对其赋值给出激励。在program test中,传入虚接口,声明一个environment句柄,在initial块将其实例化,然后运行其方法run。
在tbench_top中,代码就很简洁了,创建接口实例,然后将其作为参数传入test中,就是我们的testcase。然后利用接口实例连接dut
执行结果如下:
在这里插入图片描述
波形如下:
systemverilog学习 ---- program和interfece_第4张图片

你可能感兴趣的:(systemverilog学习,学习,fpga开发)