【IC验证】verilog及systemverilog特殊特性的分析

1.概述

本文主要汇总分析verilog及systemverilog学习过程中出现的特殊问题;

2.systremverilog特殊小点

(1)接口
接口是静态的,要在类中使用接口句柄必须先用virtual声明;
(2)测试平台的建立
为保证先建立平台再开始测试,建立测试平台的执行应该先于测试,所以测试代码开始前要先延迟(#0);

3.赋值延迟

(0)总结

always过程块实现时序逻辑和组合逻辑和连续赋值语句赋值要消耗仿真时间;
接口内部的时钟,会比外部提供的时钟,存在延迟;
触发事件,@xxx,存在延迟;

(1)情况一:initial中进行阻塞赋值和非阻塞赋值(不延迟)

a代码

module test_tb;
    reg [3:0]   data1;
    reg [3:0]   data2;
    initial begin
        data1 = 0;
        data2 <= 0;
        #5;
        data1 = 1;
        data2 <= 1;
    end
endmodule

b 电路图

initial过程块不可综合;

c 结果

结论:initial过程块中直接赋值不消耗时间;
在这里插入图片描述

(2)时钟

a 代码

module test_tb;
	reg clk;
    initial begin
        clk = 1'b0;
    end
    always#(10) clk = !clk;
endmodule

b 电路图

延时语句不可综合;

c 结果

结论:时钟赋值不消耗时间;
在这里插入图片描述

(3)always过程块实现时序逻辑(延迟)

a 代码

module test_tb;
	parameter T =20;
    reg clk;
    reg rst_n;
    initial begin
        clk = 1'b0;
        rst_n = 1'b0;
        #(T);
        rst_n = 1'b1;
    end
    always#(T/2) clk = !clk;
    reg [3:0] cnt1;
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            cnt1 <= 'd0;
        else
            cnt1 <= cnt1+1'b1;
    end
endmodule

b 电路图

在这里插入图片描述

c 结果

结论:综合出寄存器,赋值需要消耗时间;
在这里插入图片描述

(4)always过程块实现组合逻辑(延迟)

a 代码

module test_tb;
	parameter T =20;
    reg clk;
    reg rst_n;
    initial begin
        clk = 1'b0;
        rst_n = 1'b0;
        #(T);
        rst_n = 1'b1;
    end
    always#(T/2) clk = !clk;
    reg [3:0] cnt1;
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            cnt1 <= 'd0;
        else
            cnt1 <= cnt1+1'b1;
    end
    reg [3:0] cnt2;
    always@(*)begin
        cnt2 = cnt1;
    end
endmodule

b 电路图

在这里插入图片描述

c 结果

结论:综合出数据线,赋值需要消耗时间
在这里插入图片描述

(5)assign连续赋值语句实现组合逻辑(延迟)

a 代码

module test_tb;
	parameter T =20;
    reg clk;
    reg rst_n;
    initial begin
        clk = 1'b0;
        rst_n = 1'b0;
        #(T);
        rst_n = 1'b1;
    end
    always#(T/2) clk = !clk;
    reg [3:0] cnt1;
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            cnt1 <= 'd0;
        else
            cnt1 <= cnt1+1'b1;
    end
    wire [3:0] cnt2;
    assign cnt2 = (cnt1 == 1)?1'b1:1'b0;

endmodule


b 电路图

在这里插入图片描述

c 结果

结论:综合出组合逻辑电路,赋值需要消耗时间
在这里插入图片描述

(6)接口中的时钟(延迟)

a 代码

interface my_if(input bit clk);
    bit [7:0] sig1;
    bit [7:0] sig2;
    clocking cb@(posedge clk);
        input sig1;
        output sig2;
    endclocking 
endinterface 
module test_blocking1_test;
    reg clk;
    my_if mif(clk);
    initial begin
        clk = 0;
    end
    always#3 clk = !clk;

endmodule

b 电路图

不可综合语句;

c 结果

结论:
接口中的时钟跳变,会比顶层时钟跳变,延迟;

(7)触发事件(延迟)

a代码

module test_tb;
    parameter T = 20;
    reg clk;
    reg rst_n;
    reg cnt;
    initial begin
        clk = 0;
        rst_n = 0;
        cnt = 0;
        #(T);
        rst_n = 1;
        #(T);
        @(posedge clk);
        cnt = 1;
    end
    always#(T/2) clk = !clk;

endmodule

b 电路图

不可综合语句;

c 结果

在这里插入图片描述

4.仿真软件的采样时刻

(1)在过程块中:时钟触发沿和数据改变沿同时刻

a代码

说明:时钟clk1T/2倍数处改变,输入数据in1 也在T/2倍数处改变;

module test_tb;
    parameter T = 20;
    reg             clk1;
    reg     [3:0]   in1;
    reg             rst_n;
    initial begin
        clk1 = 1'b0;
        rst_n = 1'b0;
        in1  = 'd0;
        #(T);
        rst_n = 1'b1;
    end
    always#(T/2) in1 = in1 + 1'b1;
    always#(T/2) clk1 = !clk1;
    reg [3:0] data_out1;
    always@(posedge clk1 or negedge rst_n)begin
        if(!rst_n)
            data_out1 <= 'd0;
        else 
            data_out1 <= in1;
    end

endmodule

b结果

questasim:
结论:仿真器采样为数据跳变沿右侧的值;
在这里插入图片描述
vcs(DVE):
结论:仿真器采样为数据跳变沿右侧的值;
在这里插入图片描述
vcs(verdi):
结论:仿真器采样为数据跳变沿右侧的值;
在这里插入图片描述

(2)在clocking block中:时钟触发沿和数据改变沿同时刻(完全一致)

a代码

在这里插入图片描述

b结果

questasim:
结论:仿真器采样为采样时刻左侧的值;
在这里插入图片描述
vcs(DVE):
结论:仿真器采样为采样时刻右侧的值;
在这里插入图片描述

vcs(verdi):
结论:仿真器采样为采样时刻右侧的值;
在这里插入图片描述

说明:时钟clk13倍数处改变,输入数据sig1 第二次变化在时钟跳变沿处改变;


为什么两种clk翻转时刻不同???

5.并行块代码的执行

1.代码的执行时间(按照仿真结果总结出的,不具有理论依据)

(1)在verilog实现的硬件电路中,同一时刻不同过程块中的并行语句,是并行执行;
(2)在verilog和systemverilog仿真时,同一时刻同一过程块中的串行语句,是串行执行;
见例子1中,“aaa”、“bbb”、“ccc”的打印语句,是同一过程块中的串行语句,是串行执行;
(3)在verilog和systemverilog仿真时,同一时刻不同过程块中的并行语句,是串行执行;
见例子1中“aaa”、“bbb”、“ccc”三个打印语句和"eee"打印语句,是不同过程块中的并行语句,所以会串行执行;
(因为“aaa”、“bbb”、“ccc”打印语句过程块在前,优先打印)
(4)在verilog和systemverilog仿真时,部分语句会消耗一个很小的时刻的;
如:“#”
见例子2中,两过程块均延时1个时间单位,然后分别打印aaaccc,所以结果aaa在前、ccc在后;
而在第一个过程块中通过执行两次**#1表示延迟两个时间单位,第二个过程块通过执行一次#2同样表示延迟两个时间单位。但是过程块1用了两次#会消耗两个很小的时刻,过程块2只用了1次#只会消耗1个很小的时刻,所以会先打印ddd再打印bbb**
(5)例子
例子1

module test_ft_tb;
    initial begin
        $display("aaa");
        $display("bbb");
	$display("ccc");
        #10;
        $display("ddd");
    end
    initial begin
	$display("eee");
        #10;
        $display("fff");
    end
endmodule

结果:
在这里插入图片描述
例子2

module test_ft_tb;
    initial begin
        #1;
        $display("aaa");
	#1;
	#1;
        $display("bbb");
    end
    initial begin
        #1;
        $display("ccc");
	#2;
	$display("ddd");
    end
endmodule

结果:
在这里插入图片描述

6.对模块中成员的调用

(1)问题

systemverilog中,在一个区域中,声明了一个类(或模块),那么在这个区域中就可以调用这个类(或模块)中的成员(变量和方法);

(2)例子(以模块为例)

代码:

module add_module;
    int data_arr [$];
    task automatic get_data(input int data_in);
        data_arr.push_back(data_in);
    endtask 
    task automatic add_data(output int add_re);
        foreach(data_arr[i])begin
            add_re = add_re + data_arr[i];
        end
    endtask
endmodule
module test_ft_tb1;
   add_module u1();
   initial begin
        int re;
        u1.get_data(1);
        u1.get_data(2);
        u1.get_data(3);
        $display("data_arr = %p",u1.data_arr);
        u1.add_data(re);
        $display("result is %0d",re);
   end
endmodule

结果:
在这里插入图片描述

7.模块和类的静态与动态

模块是静态的,创建于编译时刻;
类是动态的,创建于实例化时刻。

8.do while和while do循环

(1)do while
说明
先执行,再判定条件是否成立,成立继续执行循环体;
语法

do begin
	//循环体
	...
end while(condition);

(2)while do
说明
先判定条件是否成立,成立则继续执行循环体;
语法

while(condition)begin
	//循环体
	...
end

(3)例子

module tb;
    int i = 2;
    int j = 2;
    initial begin
        while(i<2)begin
            i = i +1;
            $display("i = %0d",i);
        end
        do begin
            j = j+1;
            $display("j = %0d",j);
        end while(j <2);
    end
endmodule

结果:
在这里插入图片描述
说明:
do while的循环体至少会执行一次。

9.fork join_none代码的执行顺序

(1)说明
fork join_none后续代码和内部代码是并行执行的,在同一时刻后续代码执行是先于内部的;
(2)例子
代码:

module tb;
    initial begin
        fork 
            $display("aaa");
            $display("bbb");
        join_none
        $display("ccc");
        $display("ddd");
    end
endmodule

结果:
后续代码先执行

10.敏感事件列表中的iff限定符

(1)说明
当敏感事件到来的同时满足限定要求,才会执行后续代码;
(2)语法
@(敏感事件列表 iff(condition));
//后续代码
(3)例子

module tb;
    parameter  T =20;
    bit clk;
    bit a = 0;
    int b = 0;
    always#(T/2) clk = !clk;
    initial begin
        #25;
        a = 1;
    end
    initial begin
        @(posedge clk iff(a));
        b = 1;
    end

endmodule

结果:
在这里插入图片描述

11.initial块的执行时刻

(1)说明
initial块是在仿真后执行一次,只不过initial块内声明的静态变量的在编译时就创建了;
如果initial过程块内的静态变量,在声明时就赋了初值,那么该变量在编译完就被赋了值;
如果initial过程块内的静态变量,声明和赋值分开实现的,那么该变量在编译时创建(为默认初值),仿真是才会被赋值;
(2)例子
代码:
在这里插入图片描述
编译后的变量:
在这里插入图片描述
仿真开始后的变量:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值