• 牛客网verilog刷题知识点盘点(75道题的版本)


    牛客网verilog刷题知识点盘点(75道题的版本)
    还有几个坑没填

    任务和函数

    1.任务和函数必须在模块内定义,其作用范围仅适用于该模块,可以在模块内多次调用。
    2.任务和函数中可以声明局部变量,如寄存器,时间,整数,实数和事件,但是不能声明线网类型的变量。
    3.任务和函数中只能使用行为级语句,但是不能包含always和initial块,设计者可以在always和initial块中调用任务和函数。

    函数一定不能包含任何延迟,事件或者时序控制声明语句 任务可以包含延迟,事件或者时序控制声明语句

    任务

    特点:任务可以调用函数,任务可以作为一个单独的语句出现在语句块中,不能出现always和initial块,但是可以包含forever等其他延时语句。
    任务使用关键字task和endtask来进行声明,如果子程序满足下面任何一个条件,则必须使用任务而不能使用函数。

    1.子程序中包含有延迟,时序或者事件控制结构
    2.没有输出或者输出变量超过一个
    3.没有输入变量

    函数

    特点:函数可以调用函数,不可以调用任务。函数不可以作为一条单独的语句出现在语句块中

    函数使用关键字function和endfunction定义,对于子程序,如果满足下述所有条件则可以用函数来完成:

    1.在子程序中不含有延迟时序或者控制结构。
    2.子程序只有一个返回值。
    3.至少有一个输入变量。
    4.没有输出或者双向变量。
    5.不含有非阻塞赋值语句。

    由上述的特点决定:函数用于替代纯组合逻辑的verilog代码,而任务可以代替verilog的任何代码。

    状态转换图

    在状态转换图中以圆圈表示电路的各个状态, 以箭头表示状态转换的方向。同时, 还在箭头旁注明了状态转换前的输人变量取值和输出值。通常将输入变量取值写在斜线以上, 将输出值写在斜线以下

    乘法器的四种实现

    并行乘法器

    采用并行乘法设计的乘法器,在Verilog中直接采用 * 设计,这种方法设计出来的乘法器需要根据综合工具得到乘法结果,往往这种算法都是比较差的算法。
    特点:
    由乘法运算符描述、由EDA软件综合
    运算速度快、耗用资源多

    module multi(a,b,c);
    parameter size = 8;
    input [size-1:0] a,b;
    output [2*size-1:0] c; 	//输出位宽
    
    assign c = a*b;
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    移位相加乘法器

    原理:
    从被乘数的最低位开始判断,若为1,则乘数左移i(i=0,1(width-1))位后,与上一次和进行相加,若为0,则乘数左移i位后,以0相加。直到被乘数的最高位。
    实际是由移位运算和加法运算构成。每次运算速度快。

    优点:
    占用资源较少,主要在低速信号处理中。

    缺点:
    串行乘法器的速度比较慢,一个结果输出需要更多的时钟周期。在高位宽的乘法运算中非常明显。

    module multi_shift(a,b,c);  //不可综合提供算法
    parameter size = 8;
    input [size-1:0] a,b;
    output [2*size-1:0] c; 	//输出位宽
    
    reg[2*size-1:0] c;
    
    integer i;
    always@(a or b)
    begin
    	c=0;
    	for (i=1; 1<=size;i=i+1) begin
    		c = c + ((b[i] == 1)? (a<<[i-1]):0); 	//移位相加
    	end
    end
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    查找表乘法器

    原理:
    先将乘法的所有可能结果存储起来,然后将两个相乘的数据组合起来作为“地址”找到相应的结果。
    例如:
    设A,B为两个2位二进制数,则A,B各有4种取值可能,乘积有4*4=16种可能(排除重复的其实只有8种可能),我们先将{A,B}对应的16种可能结果存储起来,然后对于每一特点的输入组合{A,B},找到对应的输出即可。

    查找表乘法器特点:
    该方式速度很快,只取决于读取存储器的速度,但是预存结果要占用较多资源,因此是面积换取速度思想的体现。

    随着乘数位宽的增加,需要存储的结果迅速增加,不利于实现,因此该方式适用于位宽很小的情况。但是我们可以将高位宽的数据分解成低位宽的数据再调用查找表乘法器。

    适用情况:适合位数较小的乘法,特别适合有一个乘数为固定的乘法。

    module lookup22(dina,dinb,dout,clk);
    input [1:0] dina,dinb;
    input clk;
    output [3:0] dout;
    reg [3:0] dout;
    
    always @ ( posedge clk )
    begin
       case({dina,dinb})
          4'b0000:dout<=0;
          4'b0001:dout<=0;
          4'b0010:dout<=0;
          4'b0011:dout<=0;
          4'b0100:dout<=0;
          4'b0101:dout<=1;
          4'b0110:dout<=2;
          4'b0111:dout<=3;
          4'b1000:dout<=0;
          4'b1001:dout<=2;
          4'b1010:dout<=4;
          4'b1011:dout<=6;
          4'b1100:dout<=0;
          4'b1101:dout<=3;
          4'b1110:dout<=6;
          4'b1111:dout<=9;
          default: dout<=4'dx;
       endcase 
    end   
    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

    加法树乘法器

    原理:
    在这里插入图片描述

    实现:

    module multi_add_tree(a,b,clk,out);
    output [15:0] out;
    input [7:0] a,b;
    input clk;
    wire [15:0] out;
    
    wire [15:0] out1,c1;
    wire [13:0] out2;
    wire [11:0] out3.c2;
    wire [9:0] out4;
    
    reg [14:0] temp0; 
    reg [13:0] temp1;
    reg [12:0] temp2;
    reg [11:0] temp3;
    reg [10:0] temp4;
    reg [9:0] temp5;
    reg [8:0] temp6;
    reg [7:0] temp7;
    
    // 8*1乘法器
    
    function [7:0] mut8_1;
    input [7:0] operand;
    input sel;
    
    begin
    	mut8_1 = sel ? operand : 8'b0000_0000;
    end
    endfunction 
    
    //操作数b各位与操作数a相乘
    always @(posedge clk)
    begin
    	temp7 = mut8_1(a,b[0]);
    	temp6 = (mut8_1(a,b[1]))<<1;
    	temp5 = (mut8_1(a,b[2]))<<2;
    	temp4 = (mut8_1(a,b[3]))<<3;
    	temp3 = (mut8_1(a,b[4]))<<4;
    	temp2 = (mut8_1(a,b[5]))<<5;
    	temp1 = (mut8_1(a,b[6]))<<6;
    	temp0 = (mut8_1(a,b[7]))<<7;
    end
    
    //加法树运算
    assign out1 = temp0 + temp1;
    assign out2 = temp2 + temp3;
    assign out3 = temp4 + temp5;
    assign out4 = temp6 + temp7;
    assign c1 = out1 + out2;
    assign c1 = out3 + out4;
    assign out = c1 + c2;
    
    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

    verilog系统函数

    Verilog HDL语言中共有以下一些系统函数和任务:

    $bitstoreal, $rtoi, $display, $setup, $finish, $skew, $hold,
    $setuphold, $itor, $strobe, $period, $time, $printtimescale,
    $timefoemat, $realtime, $width, $real ,$tobits, $write, $recovery,
    
    • 1
    • 2
    • 3

    在Verilog HDL语言中每个系统函数和任务前面都用一个标识符"$"来加以确认。这些系统函数和任务提供了非常强大的功能。

    display

    函数系统任务的作用是用来在控制台输出信息。注意在控制台直接输出字符时,在没有其它控制字符的控制显示格式的情况下,字符串原封不动的显示在屏幕上。
    d i s p l a y ( “ 字 符 区 ” , 表 达 式 1 , 表 达 式 2 , … ) ; 显 示 系 统 时 间 , 系 统 是 时 间 是 由 display(“字符区” ,表达式1,表达式2,…); 显示系统时间,系统是时间是由 display(12);time标识,系统时间是指系统开始运行到目前为止消耗(或累积)的时间

    initial begin
    $display("simulation time= %t ; \n", $time);
    #10
    $display("simulation time= %t ; \n", $time);
    #20
    $display("simulation time = %t ; \n", $time);
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    显示结果为:

    simulation time=0;
    simulation time=10000;
    simulation time=30000;
    
    • 1
    • 2
    • 3

    在用十进制数格式输出时,输出结果前面的0值用空格来代替。对于其他进制,输出结果前面的0仍然显示出来。
    可以通过在(%)和表示进制的字符中间插入一个0自动调整显示输出数据宽度的方式:
    $display(“d=%0h a=%0h”, data, addr);
    这样在显示输出数据时,在经过格式转换以后,总是用最少的位数来显示表达式的当前值。
    例子:

    module printval;
      reg[11:0] r1;
      initial begin
        r1=10;
        $display("Printing with maximum size=%d=%h", r1, r1);
        $display("Printing with maximum size=%0d=%0h", r1, r1);
      end
    endmodule
    //输出结果为
    Printing with maximum size=10=00a;
    Printing with maximum size=10=a;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    如果输出列表中表达式的值包含有不确定的值或高阻值,其结果输出遵循以下规则:
    1 在输出格式为十进制的情况下:

    • 如果表达式值的所有位均为不定值,则输出结果为小写的x;
    • 如果表达式值的所有位均为高阻值,则输出结果为小写的z;
    • 如果表达式值的部分位均为不定值,则输出结果为大写的X;
    • 如果表达式值的部分位均为高阻值,则输出结果为大写的Z;

    2 在输出格式为十六进制和八进制的情况下:

    • 每4位二进制数为一组代表一位十六制数,每3位二进制数为一组代表一位八进制数;
    • 如果表达式值相对应的某进制数的所有位均为不定值,则该位进制数的输出的结果为小写的x;
    • 如果表达式值相对应的某进制数的所有位均为高阻值,则该位进制数的输出的结果为小写的z;
    • 如果表达式值相对应的某进制数的部分位为不定值,则该位进制数的输出的结果为大写的X;
    • 如果表达式值相对应的某进制数的部分位为高阻值,则该位进制数的输出的结果为大写的Z;

    3 对于二进制输出格式,表达式值的每一位的输出结果为0, 1, x, z。

    (display)自动地在输出后进行换行。(write)则在输出后不换行。
    另:
    $display命令的执行是安排在活动事件队列中,但排在非阻赋值赋值数据更新事件之前
    $strobe命令的执行是排在非阻塞赋值数据更新事件之后。
    $display适合用来显示阻塞语句的赋值
    $strobe适合用来显示非阻塞语句的赋值

    initial begin
        $dumpfile("dump.vcd");
        $dumpvars;
        a = 4'd2;
        b = 4'd3;
        c = 4'd4;
        #(PERIOD*15) $stop;
      end
      always @(posedge clk ) begin
        b <= c;
        a <= b;
        $display("display a = %d", a);
        $strobe("strobe a = %d", a);
      end
    //输出如下:
    //# KERNEL: display a =  2
    //# KERNEL: strobe a =  3
    //# KERNEL: display a =  3
    //# KERNEL: strobe a =  4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    $finish

    格式:
    $ finish;
    $ finish(n);
    系统任务$ finish的作用是退出仿真器,返回主操作系统。如果不带参数,默认$finish的参数值为1。
    各种参数代表的含义:
    0 不输出任何信息;
    1 输出当前仿真时刻和位置;
    2 输出当前仿真时刻、位置和在仿真过程中所用的memory及cpu时间统计。

    4 系统停止任务$stop

    格式:
    $stop;
    $stop(n);
    $stop任务的作用是是把EDA工具(例如仿真器)置成暂停模式,在仿真环境下给出一个交互式的命令提示符,将控制权交给用户·。根据参数值的不同,输出不同的信息,参数越大,输出的信息越多。

    (待填坑其他函数)

    UDP(User Defined Primitives) 原语

    UDP 多输入,单输出,最多10个输入,端口说明列表第一项必为输出端,真值表只允许出现0,1,X,不允许出现Z,只有输出端能被定义为Reg
    组合逻辑的UDP:输出仅取决于输入信号的逻辑,如四选一多路选择器。
    时序逻辑的UDP:下一个输出值不但取决于当前的组合逻辑,还取决于当前的内部状态,如锁存器和触发器。
    在UDP中不能调用(实例引用)其他模块或者其他原语,其调用方式和门级原语调用方式相同。

    1. UDP的组成
      定义关键字:primitive开始,原语名称、输出输入端口,initial语句(用于初始化时许逻辑UDP的输出端口)。UDP状态表是UDP最终要的部分,以关键字table开始,endtable结束,状态表定义了输入状态和当前状态得到的输出值,可以是一个查找表,也可类似于逻辑真值表。原语最终endprimitive结束。
    //定义一个原语
    primitive (<输出端口>,<输入端口1>,<输入端口2>,...<输入端口n>)  //第一个必须为输出
        output <输出端口名>;
        input  <输入端口名>;
        reg    <输出端口名>; //可选,只有表示时序逻辑的UDP才需要
     
        initial <端口名> = <值>;
    //UDP状态表
        table
            <状态表>
        endtable
    endprimitive
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.UDP的定义规则
    UDP只能有一个1位输出端口,输出列表的第一个必须是输出端口,不允许多个或多位输出。
    UDP只能采用标量(1位)输入端口 ,但是允许多个输入端口
    端口声明部分:输入端口声明input;输出端口声明output;不支持输入输出端口声明inout。因为表示时许逻辑的UDP需要保持状态,所以输出端口必须声明位reg类型。
    时序逻辑的UDP中,可以使用initial语句对reg类型变量(输出)进行赋初值。、
    状态表项可以包含值0,1或x。UDP不能处理z值,输入UDP的z值被当作x值处理。
    UDP与模块同级,因此UDP不能在模块内部定义,但可以在模块内部调用,调用方法和调用原语相同。
    3.组合逻辑的UDP定义:

    primitive udp_and(out, a, b);
        output out;
        inutput a,b;
        table
        //a  b : out;
          0  0 : 0;
          0  1 : 0;
          1  0 : 0;
          1  1 : 1;
        endtable
    endprimitive
    //或
    primitive udp_and(output out, input a, output b);
    ...
    endprimitive
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    状态表table 内:
    … :

    注意:

    状态表每一行的输入端口的顺序一定要和端口列表相同
    输入输出<:>隔开,<;>结束
    能够出现的输入项出现的组合必须全部列出。否则,如果在状态表各行中找不到与这组输入对应项,相对应的输出就是x。
    注意输入的值也最好为确定值,如果输入都非确定值即(x),那么输出也必然是x,那么就会出现x值传播下去。
    无关项可以用“?”来表示
    由于UDP限制过多且当输入增加时,状态表将成几何倍数增加,因此定义的UDP原语一般仅使用在基本的逻辑功能。

    primitive mux4_to_1( output i0, i1, i2, i3, s1 s0, EN,
                         input i0, i1, i2, i3, s1 s0, EN );
    table
    //  i0  i1  i2  i3  s1 s0 EN : output;
         ?   ?   ?   ?   ?  ?  1 :  0;
         1   ?   ?   ?   0  0  0 : 1;
         0   ?   ?   ?   0  0  0 : 0;
         ?   1   ?   ?   0  1  0 : 1;
         ?   0   ?   ?   0  1  0 : 0;
         ?   ?   1   ?   1  0  0 : 1;
         ?   ?   0   ?   1  0  0 : 0;
         ?   ?   ?   1   1  1  0 : 1;
         ?   ?   ?   0   1  1  0 : 0;
         ?   ?   ?   ?   x  ?  0 :  x;
         ?   ?   ?   ?   ?  x  0 : x;
    endtable
    endprimitive
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1. 时序逻辑UDP
      时序逻辑与组合逻辑UDP的区别:
    • 表示时序逻辑的UDP的输出必须声明为reg类型,并且可以使用initial进行初始化。
    • 表示时序逻辑的UDP,状态表:<输入1><输入2>…<输入N> : <当前状态> : <下一状态>
    • 表示时序逻辑的UDP,状态表的输入项可以是电平或者跳变沿的形式。
    • 当前状态是寄存器的当前值,下一状态是计算得到值会被存到寄存器中成为新值。

    电平敏感:

    primitive latch(output reg q = 0
                    input d, clock, clear);
    initial q = 0; 
    table  
        // d   clock   clear  :  q : q+;
           ?     ?       1    :  ? : 0;
    //clock = 1时将d值锁存到q中
           1     1       0    :  ? :1;
           0     1       0    :  ? : 0;
     
           ?     0       0    :  ? : -;
    endtable
    endprimitive
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    边沿敏感:

    //带清零的时钟下降沿触发的D触发器
    primitive edge_dff(output reg q=0
                        input d, clock, clear);
    table
    //  d  clock clear : q : q+;
        ?    ?    1    : ? : 0 ;
        ?    ?    (10) : ? : - ;    //(10) 由1向0跳变
        1    (10) 0    : ? : 1 ;
        0    (10) 0    : ? : 0 ;
        ?    (1x) 0    : ? : - ;    //(1x)由1向不确定状态跳变
        ?    (0?) 0    : ? : - ;    
        ?    (x1) 0    : ? : - ;
        (??) ?    0    : ? : - :    //(??)信号值再0,1,x三者之间任意跳变
    endtable
    endprimitive
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    其中UDP中的缩写符号含义:

    符号含义解释
    ?0,1,x不能用于输出
    b0,1不能用于输出
    -维持原值不变只能用在时序UDP的输出
    r(01)信号的上升沿
    f(10)信号的下降沿
    p(01),(0x),(x1)可能是信号的上升沿
    n(10),(1x),(x0)可能是信号的下降沿
    *(??)信号的任意变化
    5.额外说法
    设计功能模块时,是使用module还是使用UDP来实现模块功能,下面给出选择的指导原则:

    UDP只能进行功能建模,不能对电路时序和制造工艺(CMOS/TTL/ECL)进行建模。UDP是对模块功能的模型,而module是对于完整的模块模型。
    UDP只能有一个一位输出,且UDP的输入端口数由仿真器决定。
    UDP的描述方式是使用状态表,所以不适合输入参数太多的描述。
    尽可能完整的列出UDP中的状态表
    尽可能使用缩写符来表示状态表输入项
    当电平敏感的与边沿敏感的同时作用是,下一状态先由电平敏感的决定,即电平敏感的优先级高于边沿敏感。

    部分语句说明

    fork_join (并行块)

    • 块内语句是同时执行的,即程序流程一进入到该并行块,块内语句则开始同时并行地执行。
    • 块内每条语句的延迟时间是相对于程序流程控制进入到块内的仿真时间。
    • 延迟时间是用来给赋值语句提供执行时序的。
    • 当按时间顺序排序在最后的语句执行完后或一个disable语句执行时,程序流程控制跳出该程序块。
      fork:块名
      语句1;
      语句2;
      语句n;
      end
      fork后可加块名,语句块内的说明语句可以是参数说明语句、reg型变量声明语句、integer型变量声明语句、real型变量声明语句、time型变量声明语句和事件(event)说明语句。如果两条语句在同一时刻对同一个变量产生影响,那么将会引起隐含的竞争。
      注意:顺序块与并行块之间的根本区别在于:当控制转移到块语句的时刻,并行块中所有的语句同时开始执行,语句之间的先后顺序是无关紧要的。

    设计违例说明

    setup违例

    建立时间(setup time)

    hold违例

    (待填坑)

    可综合、不可综合

    Verilog是描述硬件电路的,它是建立在硬件电路的基础上的。有些语法结构是不能与实际硬件电路对应起来的,也就是说我们在把一个语言描述的程序映射成实际硬件电路中的结构时是不能实现的。

    task和function都是可综合的,不过综合出来的都是组合逻辑电路。要想可综合,task和function内部必须是组合逻辑。
    循环语句(repeat、while、for)也可以用于可综合电路设计,当采用循环语句进行计算和赋值操作时,可以综合得到逻辑电路。

    可综合语句

    input、output、parameter、reg、wire、always、assign、begin…end、case、posedge、negedge、or、and、default、if、function、generate、integer、`define,while、repeat 、for (while、repeat循环可综合时,要具有明确的循环表达式和循环条件,for可综合时也要有具体的循环范围)
    case,casex,casez
    instantitation

    不可综合语句

    initial、fork… join、wait、time、real、display、forever。
    force release
    保证Verilog HDL赋值语句的可综合性,在建模时应注意以下要点:

    • 不能使用initial,initial一般使用在测试程序,做初始化;
    • 不建议使用延时,#1,这种只是模拟数字电路中因为布线产生的信号延时,不可综合,但也不会报错;
    • 不能使用循环次数不确定的函数,但forever在综合设计中禁止使用,只能使用在仿真测试程序中;
    • 尽量使用同步电路设计方式;
    • 除非关键电路设计,一般不建议调用门级元件进行设计,一般使用行为级进行设计;
    • 当使用always进行组合逻辑设计时,敏感列表里面的要列出所有输入信号。
    • 在进行时序电路进行编写时,采样非阻塞赋值。组合逻辑设计时,采样阻塞赋值,但在同一个过程块中,最好不要同时用阻塞赋值和非阻塞赋值。。
    • 为避免产生锁存器,if、case要进行完整的语句赋值,且case语句中避免使用X值、Z值。
    • 避免混合使用上升沿和下降沿触发的触发器
    • 不使用用户自定义原语(UDP元件)
    • 所有的内部寄存器都应该能够被复位,在使用FPGA实现设计时,应尽量使用器件的全局复位端作为系统总的复位。
    • 避免混合使用上升沿和下降沿触发的触发器。
    • 避免在case语句的分支项中使用x值或z值。

    运算符优先级

    !,~ 高优先级
    *,/,%
    +,-
    << , >>
    < , <= , > , >=
    == , != , = = = , != =
    & , ~&
    ^ , ^~
    |, ~|
    &&
    ||
    ? : 低优先级

    延时模型

    在仿真中涉及延时表示的问题。延时包括门延时、assign赋值延时和连线延时等。门延时为从门的输入端发生变化到输出发生变化的延迟时间;assign赋值延时指等号右端某个值发生变化到等号左端发生相应变化的延迟时间;连线延时则体现了信号在连线上的传输延时。如果没有定义时延值,缺省时延为0.

    延时的表示方法

    # delaytime; // 表示延迟时间为delaytime
    # (d1, d2); // d1表示上升延迟,d2表示下降延迟
    # (d1, d2, d3); // d3则表示转换到高阻态z的延迟
    eg:
    not #4 gate1(out, in); // 延迟时间为4的非门
    and #(5, 7) gate2(out, a, b); // 与门的上升延迟为5,下降延迟为7
    or #5 gate3(out, a, b); // 或门的上升延迟和下降延迟都为5
    bufif0 #(3, 4, 6) gate4(cout, in, enable) // bufif0门的上升延迟为3,下降延迟为4,高阻延迟为6
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    延时说明块(specify块)

    路径延迟用关键字.specify 和 endspecify 关键字之间组成 specify 块语句。

    Verilog可对模块中某一指定的路径进行延迟定义,这一路径连接模块的输入端口(或inout端口)与输出端口(或inout端口),利用延迟定义块在一个独立的块结构中定义模块的延迟。在延迟定义块中,要描述模块中的不同路径并给这些路径赋值。

    延迟定义块的内容应放在关键字specify与endspecify之间,并且必须放在一个模块中,还可以使用specparam关键字定义参数。
    specify 是模块中独立的一部分,不能出现在其他语句块(initial, always 等)中。

    specify 块语句主要有以下功能:

    ◆指定所有路径中引脚到引脚的延迟;

    ◆定义 specparam 常量;

    ◆在电路中设置时序检查。

    eg:

    module delay(out,a,b,c);
    output out;
    input a,b,c;
    and a1(n1,a,b);
    or o1(out,c,n1);
      specify
      (d=>out) = 2;
      (b=>out) = 3;
      (c=>out) = 1;
      endspecify
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以用关键字 specparam 在 specify 块中定义延迟数值常量,然后赋值给路径延迟。
    specparam 定义的常量只能在 specify 块内部使用。

    specify
      specparam ab_2_out = 2.5 ;
      specparam cd_2_out = 3.5 ;
      
      (a => out) = ab_2_out ;
      (b => out) = ab_2_out ;
      (c => out) = cd_2_out ;
      (d => out) = cd_2_out ;
    endspecify
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    #状态机
    有限状态机FSM (Finite State Machine)
    电路中有限个状态之间的转移和动作的模型。优点/特点:

    1. 高效的顺序控制模型。
    2. 容易利用现成的EDA工具进行优化设计
    3. 性能稳定。容易构成性能良好的同步时序逻辑模块,对于解决大规模逻辑电路设计中的竞争冒险有好处。与其他设计相比,在消除电路毛刺,强化系统工作稳定性方面有更多的解决办法。
    4. 高速性能。在高速通信和控制方面有更大的优势。
    5. 高可靠性能。比CPU强,没有软件运行过程的固有缺陷。

    米粒、摩尔状态机区别

    有限状态机的输出z(t)只由当前的状态s(t)决定,则这种状态机被称为摩尔(Moore)状态机
    有限状态机的输出z(t)由当前的状态s(t)和现态输入x(t)共同决定,则这种状态机被称为米粒(Mealy)状态机

    一段式状态机

    只有一个always block,把所有的逻辑(输入、输出、状态)都在一个always block的时序逻辑中实现。这种写法看起来很简洁,但是不利于维护,如果状态复杂一些就很容易出错,在简单的状态机可以使用。

    二段式状态机

    有两个always block,把时序逻辑和组合逻辑分隔开来。时序逻辑里进行当前状态和下一状态的切换,组合逻辑实现各个输入、输出以及状态判断。这种写法不仅便于阅读、理解、维护,而且利于综合器优化代码,利于用户添加合适的时序约束条件,利于布局布线器实现设计。在两段式描述中,当前状态的输出用组合逻辑实现,可能存在竞争和冒险,产生毛刺。

    要求对状态机的输出用寄存器打一拍,但很多情况不允许插入寄存器节拍,此时使用三段式描述。其优势在于能够根据状态转移规律,在上一状态根据输入条件判断出当前状态的输出,从而不需要额外插入时钟节拍。

    三段式

    有三个always block,一个时序逻辑采用同步时序的方式描述状态转移,一个采用组合逻辑的方式判断状态转移条件、描述状态转移规律,第三个模块使用同步时序的方式描述每个状态的输出。代码容易维护,时序逻辑的输出解决了两段式组合逻辑的毛刺问题,但是从资源消耗的角度上看,三段式的资源消耗多一些。

    静态、动态时序模拟

    静态时序分析(STA)

    静态时序分析只能分析时序要求而不能进行功能验证。不需要测试向量,能比动态时序分析快地多的完成分析。静态时序分析只能对同步电路进行分析,而不能对异步电路进行时序分析。但是它却可以验证每一条路径,发现时序的重大问题,比如建立时间和保持时间冲突,slow path以及过大的时钟偏移。

    动态时序分析(DTA)

    动态时序分析就是通常我们所说的仿真,该仿真可以验证功能,也可以验证时序,首先确定测试向量,输入硬件模型,进行仿真。由于为了完整地测试每条路径的功能或者时序是否都满足,测试向量需要很大,也不能保证100%的覆盖率。如果到了门级的仿真将非常消耗时间。

    FIFO

    待填坑,重点是异步fifo

    零碎知识点

    1. 异步FIFO 地址产生最好用格雷码
    2. $可以出现在变量标识符中,但是开头只能是’_'或者字母
    3. $fmonitor $fstrobe $fdisplay 均用于写文件.
    4. FPGA 多为SRAM,flash工艺,CPLD多为EEPROM 工艺
    5. 米粒型输出与 输入当前状态有关
    6. 摩尔型输出只与当前状态有关
    7. caseX 无视x,z,caseZ 无视z
    8. inout必须为wire类型
    9. generate-for语句中begin后面的名字不能省略
    10. 时间尺度定义为 `timescale 10ns/100ps,时间单位10ns,时间精度100ps。
    11. " == " 只要有一端为x/z结果就为x," === "可以判断x,z的是否完全相等
    12. 敏感列表不完备也会产生锁存器
    13. 位拼接未指明位数则默认32位
    14. Verilog 里的模运算,先把各自符号位去掉运算,然后取第一个运算数的符号位,即都直接算 10 % 3 = 1,然后如果前面是 10 模式就是 1,前面是 -10 模值就是 -1;
  • 相关阅读:
    最多免费玩30分钟,“先试后买”会是Quest商店的救星吗?
    Hibernate 分页
    Tailscale的子网路由和出口节点
    AVR单片机及其编译软件
    Android Gldie复用只取之前decode过的缓存resource,Kotlin
    MIR7创建预制发票BAPI
    政安晨:【深度学习神经网络基础】(十三)—— 卷积神经网络
    【NSArray和NSDictionary的内存管理 Objective-C语言】
    Es集群部署
    【JVM笔记】类型转换字节码指令
  • 原文地址:https://blog.csdn.net/qq_31764341/article/details/128076736