• 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

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

    interface

    在verilog中,不同的块之间的交流是通过模块的端。systemverilog添加了interface,封装了模块之间的通信。它把testbench和dut的信号和连线捆绑在一起。下图,是我们verilog testbench和dut的连接关系
    在这里插入图片描述
    下面这个图展示了有interface的design和testbench的连线。
    在这里插入图片描述

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

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

    interface interface_name;
    	...
    	interface items
    	...
    endinterface
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 一个接口可以被声明和例化(和模块一样)。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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    执行结果如下:
    在这里插入图片描述

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

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

  • 相关阅读:
    注解(Annotation)基础
    【计算机基础】数据结构一览
    语音特征:spectrogram、Fbank(fiterbank)、MFCC
    简易介绍如何使用淘宝商品详情 API(关键词搜索商品列表API)
    利用ffmpeg将任意格式的视频转码为h264编码的mp4格式视频
    移动应用测试场景的五个重点
    优先级总结
    springcloud gateway网关浅析
    【定义】线性方程组
    使用$test$plusargs提高RTL验收速度
  • 原文地址:https://blog.csdn.net/weixin_45614076/article/details/126398877