【verilog】verilog语法刷题知识点总结

1.状态机

(1)三段式状态机的组成:三段式状态机,第一段用时序逻辑描述(现态);第二段用组合逻辑描述状态转移(次态);第三段用时序逻辑描述输出,第三段可以是多个always块。
(2)二段状态机中,不能为了减少代码长度,以便综合的面积,可以减少else情况,if else中少写else会产生锁存器。

2.任务和函数的区别

不同点函数(function)任务(task)
时序逻辑不能包含延时和时序控制逻辑,不能有非阻塞性赋值可以包含延时和时序控制逻辑
输入至少有一个输入可以没有输入或可以有多个输入
输出没有输出可以没有输出或有多个输出
返回值有一个返回值没有返回值
调用关系可以调用其他函数,不能调用任务可以调用函数和任务
综合性可综合有的编译器可综合,有的编译器不可综合

3.case,casez和casex

(1)case:敏感事件表达式和各项之间的匹配是一种全等匹配,完全相同才匹配;
(2)casez:敏感事件表达式和各项之间的匹配不考虑高阻态z
(3)casex:敏感事件表达式和各项之间的匹配不考虑高阻态z和随机状态x
在这里插入图片描述

4.随机数产生关键字

(1)情况一:b>0,产生一个(-b+1)~(b-1)间的随机数;

	parameter  b = 60;
	reg	[23:0] rand;
	rand = $random%b;

(2)情况二:b>0,产生一个0~(b-1)间的随机数;

	parameter  b = 60;
	reg	[23:0] rand;
	rand = {$random}%b;

(2)情况二:max>0且min>0,产生一个min~max间的随机数;

	parameter  max = 60;
	parameter  min = 30;
	reg	[23:0] rand;
	rand = min+{$random}%(max-min+1);

5.运算符优先级

在这里插入图片描述

6.运算符的特殊注意点及特殊运算符

(1)移位运算符

移位运算符分为分为两种,逻辑移位运算符算数移位运算符
A逻辑移位运算符
特点:用0填补空位
“>>”:逻辑右移运算符;
“<<”:逻辑左移运算符;
B算术移位运算符
特点:只有算术右移运算符,用原最高位填补空位,通过算术右移可以实现有符号数的除法;
“>>>”:逻辑右移运算符;

(2)等式运算符

有四个运算符:
等于:=====
不等于:!=!==
区别:
==!= 不考虑高阻态z未知状态x
===!== 要考虑高阻态z未知状态x
注意:(与casez和casex不同)
==!=:只要表达式两边有一个存在高阻态z未知状态x,判定结果为未知状态x
casez和casex:只要表达式中存在高阻态z高阻态z未知状态x时,对应位判定为匹配。
===== 为例:
在这里插入图片描述

(3)动态位宽截取运算符

语法:vect[base ± \pm ±:width]
说明:base表示起始位,+表示升序截取,-表示降序截取,width表示截取位宽;
注意:base可变但是width必须为常量;
例子:将data_in,从第3位起,正序截取4位给data_out
代码

module function_test_top(
    input       [7:0]   data_in,
    output      [3:0]   data_out  
);
    assign data_out = data_in[3+:4];
endmodule

testbench

`timescale 1ns/1ns
module function_test_tb();
    reg     [7:0]   data_in     ;
    wire    [3:0]   data_out    ;
    initial begin
        data_in = 8'b0000_0000;
        #20;
        data_in = 8'b0110_1100;
        #20;
        data_in = 8'b1101_0110;
        #20;
        data_in = 8'b1001_1100;
    end
    function_test_top u_function_test_top(
        .data_in    (data_in),
        .data_out   (data_out)
    );
endmodule

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

(4)求余运算符(%)

问题:10%(3),10%(-3),(-10)%3,(-10)%(-3)四个表达式结果为多少?
答案:1,1,-1,-1
注意点:前面的数决定符号
代码:

`timescale 1ns / 1ps
module remainder(
    output          [7:0]       out_one     ,
    output          [7:0]       out_two     ,
    output          [7:0]       out_three   ,
    output          [7:0]       out_four    
);
    assign out_one = 10%(3);
    assign out_two = 10%(-3);
    assign out_three = (-10)%(3);
    assign out_four = (-10)%(-3);
endmodule

testbench

`timescale 1ns / 1ps
module remainder_tb();
    wire          [7:0]       out_one       ;
    wire          [7:0]       out_two       ;
    wire          [7:0]       out_three     ;
    wire          [7:0]       out_four      ;
    remainder u_remainder(
        .out_one     (out_one   ),
        .out_two     (out_two   ),
        .out_three   (out_three ),
        .out_four    (out_four  )
);
endmodule

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

7.testbench知识点

(1)初始化时用阻塞赋值非阻塞赋值的不同
A描述
当初始化采用阻塞赋值,值的变化和时钟跳变同时发生,当前时钟边沿时可以采到数据变化的;
当初始化采用非阻塞赋值,值的变化会在时钟跳变发生,下一个时钟边沿才能采集到数据变化;
B例子:采集一拍数据变化
a代码

module function_test_top(
    input       sys_clk     ,
    input       sys_rst_n   ,
    input       data_in     ,
    output  reg data_out    
);
    always@(posedge sys_clk or negedge sys_rst_n)begin
        if(!sys_rst_n)
            data_out <= 1'b0;
        else
            data_out <= data_in;
    end
endmodule

b 情况一:testbench初始化阻塞赋值
testbench

`timescale 1ns/1ns
module function_test_tb();
    reg     sys_clk     ;
    reg     sys_rst_n   ;
    reg     data_in     ;
    wire    data_out    ;

    parameter T = 20;
    initial begin
        sys_clk   = 1'b1;
        sys_rst_n = 1'b0;
        data_in   = 1'b0;
        #(2*T);
        sys_rst_n = 1'b1;
        #(2*T);
        data_in  = 1'b1;
    end   
    // initial begin
    //     sys_clk   <= 1'b1;
    //     sys_rst_n <= 1'b0;
    //     data_in   <= 1'b0;
    //     #(2*T);
    //     sys_rst_n <= 1'b1;
    //     #(2*T);
    //     data_in  <= 1'b1;
    // end
    always#(T/2) sys_clk = !sys_clk;
    function_test_top u_function_test_top(
        .sys_clk     (sys_clk  ),
        .sys_rst_n   (sys_rst_n),
        .data_in     (data_in  ),
        .data_out    (data_out )
    );
endmodule

结果:
在这里插入图片描述
c 情况一:testbench初始化非阻塞赋值

`timescale 1ns/1ns
module function_test_tb();
    reg     sys_clk     ;
    reg     sys_rst_n   ;
    reg     data_in     ;
    wire    data_out    ;

    parameter T = 20;
    // initial begin
    //     sys_clk   = 1'b1;
    //     sys_rst_n = 1'b0;
    //     data_in   = 1'b0;
    //     #(2*T);
    //     sys_rst_n = 1'b1;
    //     #(2*T);
    //     data_in  = 1'b1;
    // end   
    initial begin
        sys_clk   <= 1'b1;
        sys_rst_n <= 1'b0;
        data_in   <= 1'b0;
        #(2*T);
        sys_rst_n <= 1'b1;
        #(2*T);
        data_in  <= 1'b1;
    end
    always#(T/2) sys_clk = !sys_clk;
    function_test_top u_function_test_top(
        .sys_clk     (sys_clk  ),
        .sys_rst_n   (sys_rst_n),
        .data_in     (data_in  ),
        .data_out    (data_out )
    );
endmodule

结果:
在这里插入图片描述
(2)begin end语句块和fork join语句块的不同
A begin end
描述:串行语句块
特点:
块内语句顺序执行,块内上一条语句执行完才能执行下一条语句;
最后一条语句执行完,程序跳出该语句块。
例子:

`timescale 1ns/1ns
module function_test_tb();
    reg     data_a      ;
    reg     data_b      ;
    reg     data_c      ;
    initial begin
        data_a = 1'b0;
        data_b = 1'b0;
        data_c = 1'b0;
        #(20) data_a = 1'b1;
        #(10) data_b = 1'b1;
        #(30) data_c = 1'b1;
    end
endmodule

结果:数据变化发生在20ns,30ns和60ns
在这里插入图片描述B fork join
描述:并行语句块
特点:
块内语句同时执行;
耗时最长的语句块执行完后,程序跳出该语句块。
例子

`timescale 1ns/1ns
module function_test_tb();
    reg     data_a      ;
    reg     data_b      ;
    reg     data_c      ;
    initial fork
        data_a = 1'b0;
        data_b = 1'b0;
        data_c = 1'b0;
        #(20) data_a = 1'b1;
        #(10) data_b = 1'b1;
        #(30) data_c = 1'b1;
    join 
endmodule

结果:数据变化发生在20ns,10ns和30ns
在这里插入图片描述
C 两种语句块可以嵌套使用
例子:

`timescale 1ns/1ns
module function_test_tb();
    reg     data_a      ;
    reg     data_b      ;
    reg     data_c      ;
    initial begin
        data_a = 1'b0;
        data_b = 1'b0;
        data_c = 1'b0;
        #(10) data_a = 1'b1;
        #(20) data_b = 1'b1;
        #(30) data_c = 1'b1;
        fork
            #(10) data_a = 1'b0;
            #(20) data_b = 1'b0;
            #(30) data_c = 1'b0;
        join
    end
endmodule

结果:数据上升沿发生于10ns,30ns,60ns;数据下降沿发生于70ns,80ns,90ns。
在这里插入图片描述

8.乘法器

(1)分类

verilog中根据实现方法的不同,将乘法器分为串行移位乘法器、并行乘法器、查找表乘法器和加法树乘法器。

(2)串行移位加法器

优缺点:优点是资源消耗较少,缺点是比较慢(低位算了才能算高位);
实现方法:(和我们小学学的乘法运算步骤一样,只不过需要先转换为2进制)
第一步:将被乘数和乘数换算为二进制;
第二步:将被乘数乘以乘数的每一位;
第三步:将上一步的结果左移乘数的对应位;
第四步:结果相加输出;
例子:4d’11乘以4’d5

module function_test_top(
    input           [3:0]       mul_one,
    input           [3:0]       mul_two,
    output    reg   [7:0]       data_out
);
    integer i;
    always@(*)begin
        data_out = 'd0;
        for(i=0;i<8;i=i+1)begin
            if(mul_two[i])
                data_out = data_out + (mul_one<<i);
        end
    end
endmodule
`timescale 1ns/1ns
module function_test_tb();
    reg     [3:0]   mul_one ;
    reg     [3:0]   mul_two ;
    wire    [7:0]   data_out;
    initial begin
        mul_one  = 'd0;
        mul_two  = 'd0;
        #20;
        mul_one  = 4'd11;
        mul_two  = 4'd5;      
        #20;
        mul_one  = 4'd6;
        mul_two  = 4'd3;       
    end
    function_test_top u_function_test_top(
        .mul_one        (mul_one ),
        .mul_two        (mul_two ),
        .data_out       (data_out)
);
endmodule

电路:
在这里插入图片描述

结果:(结果以10进制数显示)
在这里插入图片描述

(3)并行乘法器

优缺点:优点速度快(高低位同时算),缺点是资源消耗较多;
实现方法:直接用乘法运算符实现;
例子:4d’11乘以4’d5

`timescale 1ns / 1ps
module mutplier_test(
    input           [3:0]       mul_one,
    input           [3:0]       mul_two,
    output          [7:0]       data_out
);
    assign data_out = mul_one*mul_two;
endmodule

testbench

`timescale 1ns / 1ps
module mutiplier_test_tb();
    reg     [3:0]   mul_one ;
    reg     [3:0]   mul_two ;
    wire    [7:0]   data_out;
    initial begin
        mul_one  = 'd0;
        mul_two  = 'd0;
        #20;
        mul_one  = 4'd11;
        mul_two  = 4'd5;      
        #20;
        mul_one  = 4'd6;
        mul_two  = 4'd3;       
    end
    mutplier_test u_mutplier_test(
        .mul_one        (mul_one ),
        .mul_two        (mul_two ),
        .data_out       (data_out)
);
endmodule

电路:
在这里插入图片描述

在这里插入图片描述

(4)查找表乘法器和加法树乘法器这里不再详述

9.用户定义原语(UDP)

(1)简述

用户定义原语由User Defined Primitives直接翻译得到,简称UDP。主要是用来自定义仿真使用的基本逻辑器件模块。UDP是由查找表的方法确定输出,用仿真器仿真时,速度快只能描述能用简单真值表表示的组合或者时序逻辑

(2)语法

primitive  元件名(输出端口名,输入端口名1,输入端口名2,输入端口名3,...... ,输入端口名n)
    output  输出端口名;
    input   输入端口名1,输入端口名2,输入端口名3,...... ,输入端口名n;
    reg     输出端口名;
    initial begin
        输出端口寄存器或时序逻辑内部寄存器赋初值(0,1或X);
    end
    table
     //输入1    输入2    输入3    输入4    ...    输入n       :输出     ;
      逻辑值    逻辑值   逻辑值   逻辑值   ...     逻辑值      :逻辑值   ;
      逻辑值    逻辑值   逻辑值   逻辑值   ...     逻辑值      :逻辑值   ;
      逻辑值    逻辑值   逻辑值   逻辑值   ...     逻辑值      :逻辑值   ;
      逻辑值    逻辑值   逻辑值   逻辑值   ...     逻辑值      :逻辑值   ;
      endtable

endprimitive

(3)注意事项

A 可以有多个输入端,但是只能有一个输出端;
B 所有端口只能有一位位宽;
C 在真值表中只能出现0,1,x三种逻辑,高阻态z是不允许的;
D initial语句用于时序电路内部寄存器赋初值,只允许0,1,x三种逻辑值;
E 表示时序逻辑的UDP输出必须声明位reg类型,并且可以使用initial进行初始化;
F 表示时序逻辑的UDP,状态表:<输入1><输入2>…<输入N> : <当前状态> : <下一状态>

10.generate-for注意点

(1)generate-for循环必须使用genvar关键字定义循环变量;
(2)generate-for循环中的内容必须使用begin-end包括起来,哪怕只有一句;
(3)generate-for循环begin后的名字不可以省略;
(4)generate-for循环同一个模块内的begin-end块名字不能一样。

11.标识符要求

(1)标识符可以由字母、数字、下划线和$组成;
(2)标识符必须以字母或下划线(_)开头;
(3)标识符的长度不能超过1024个字符;
(4)标识符区分大小写;
(5)标识符不能是Verilog关键字,例如"module"、"endmodule"等;
(6)标识符不能包含特殊字符,例如空格、制表符等。
请注意,虽然Verilog标识符允许使用数字,但是最好将数字用于表示信号位宽或者其它与数字相关的意义,而不是用作变量名的一部分。这有助于提高代码的可读性和可维护性
在这里插入图片描述

12.有符号数与无符号数

(1)注意

本题均以求余运算为例,求余运算注意点:被余数决定符号。
(2)整数
A)10进制数
特点:10进制数默认为有符号数。
例子:2%3和4294967294%3
说明
因为10进制数默认位宽为32位,所以2和4294967294分别表示32’b0000_0000_0000_0000_0000_0000_0000_0010和32’b1111_1111_1111_1111_1111_1111_1111_1110,实际分别为有符号数2和-2,所以余3的结果为2和-2。
代码:

`timescale 1ns / 1ps
module remainder(
    output [31:0]   out_data_1,
    output [31:0]   out_data_2
    );
    assign out_data_1 = 2%3;
    assign out_data_2 = 4294967294%3;
endmodule
`timescale 1ns / 1ps
module remainder_tb();
    wire [31:0] out_data_1;
    wire [31:0] out_data_2;
    remainder u_remainder(
        .out_data_1 (out_data_1),
        .out_data_2 (out_data_2)
    );
endmodule

结果:
在这里插入图片描述
B)未声明符号位的
特点:未声明符号位的默认为无符号数。
例子:8’b0000_0010%3和8’b1111_1110%3。
说明:
8’b0000_0010和8’b1111_1110,实际分别为无符号数2和254,所以余3的结果均为2。
代码:

`timescale 1ns / 1ps
module remainder(
    output  [7:0]   out_data_1,
    output  [7:0]   out_data_2
    );
    assign out_data_1 = (8'b0000_0010)%3;
    assign out_data_2 = (8'b1111_1110)%3;
endmodule

testbench

`timescale 1ns / 1ps
module remainder_tb();
    wire [7:0] out_data_1;
    wire [7:0] out_data_2;
    remainder u_remainder(
        .out_data_1 (out_data_1 ),
        .out_data_2 (out_data_2 )
    );
endmodule

结果:
在这里插入图片描述
C)声明了符号位的
特点:声明符号位的为有符号数。
例子:(声明有符号在进制符号前加s
8’sb0000_0010%3和8’sb1111_1110%3。
说明:
8’sb0000_0010和8’sb1111_1110%3,实际分别为有符号数2和-2,所以余3的结果为2和-2。
代码:

`timescale 1ns / 1ps
module remainder(
    output  [7:0]   out_data_1,
    output  [7:0]   out_data_2
    );
    assign out_data_1 = (8'sb0000_0010)%3;
    assign out_data_2 = (8'sb1111_1110)%3;
endmodule

testbench

`timescale 1ns / 1ps
module remainder_tb();
    wire [7:0] out_data_1;
    wire [7:0] out_data_2;
    remainder u_remainder(
        .out_data_1 (out_data_1 ),
        .out_data_2 (out_data_2 )
    );
endmodule

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

(2)变量

A)注意点
一个任意类型的数(无论是有符号数还是无符号),赋值给一个变量后,其类型由被赋值的类型确定;
wire和reg类型变量,默认类型为无符号数,要定义其为有符号数要用关键字signed;
integer类型变量默认为有符号数,不用声明其类型;
B wirereg
例子+说明:
四个无符号数8’b0000_0010,8’b1111_1110和8’b0000_0010,8’b1111_1110;
赋值给无符号变量后实际值为2,254和2,-2;
所以余3的结果为:2,2,和2,-2。
代码:

`timescale 1ns / 1ps
module remainder(
        input               [7:0]   indata_uns_one ,
        input               [7:0]   indata_uns_two ,
        input     signed    [7:0]   indata_sig_one ,
        input     signed    [7:0]   indata_sig_two ,
        output              [7:0]   out_data_1     ,
        output              [7:0]   out_data_2     ,
        output              [7:0]   out_data_3     , 
        output              [7:0]   out_data_4        
    );
    assign out_data_1 = indata_uns_one%3;
    assign out_data_2 = indata_uns_two%3;
    assign out_data_3 = indata_sig_one%3;
    assign out_data_4 = indata_sig_two%3;
endmodule
`timescale 1ns / 1ps
module remainder_tb();
    reg             [7:0]  indata_uns_one;
    reg             [7:0]  indata_uns_two;
    reg  signed     [7:0]  indata_sig_one;
    reg  signed     [7:0]  indata_sig_two;
    wire            [7:0]  out_data_1;
    wire            [7:0]  out_data_2;
    wire            [7:0]  out_data_3;
    wire            [7:0]  out_data_4;
    initial begin
        indata_uns_one = 8'b0;
        indata_uns_two = 8'b0;
        indata_sig_one = 8'b0;
        indata_sig_two = 8'b0;
        #20;
        indata_uns_one = 8'b0000_0010;
        indata_uns_two = 8'b1111_1110;
        indata_sig_one = 8'b0000_0010;
        indata_sig_two = 8'b1111_1110;
    end
    remainder u_remainder(
        .indata_uns_one (indata_uns_one),
        .indata_uns_two (indata_uns_two),
        .indata_sig_one (indata_sig_one),
        .indata_sig_two (indata_sig_two),
        .out_data_1     (out_data_1 ),
        .out_data_2     (out_data_2 ),
        .out_data_3     (out_data_3 ),
        .out_data_4     (out_data_4 )
    );
endmodule

结果
在这里插入图片描述

(3)有符号数和无符号数运算

规则:
两个无符号数,按照无符号数规则运算;
两个有符号数,按照无符号数规则运算;
一个有符号数一个无符号数,按照无符号数规则运算。
例子+说明:
情况一:两个无符号数8’b1111_1110和8’b0000_0011,实际值为254和3,求余结果为8‘b0000_0010即2;
情况二:两个有符号数8’b1111_1110和8’b0000_0011,实际值为-2和3,求余结果为8‘b1111_1110即-2;
情况二:有符号数8’b1111_1110和无符号数8’b0000_0011,实际值为-2和3均转换为无符号数为254和3,求余结果为8‘b0000_0010即-2。

(4) #### 注意

所有运算分两步看:以(3)情况二为例
第一步:在求赋值前的值,两个有符号数-3余3为-2,即8’b1111_1110;
第二步:由被赋值变量看形式,8’b1111_1110是无符号数254还是有符号数-2,由存储他的变量out_data_2决定。out_data_2是无符号变量,结果8’b1111_1110就是无符号数254;out_data_2是有符号变量,结果8’b1111_1110就是有符号数-2。

13.参数

verilog中定义参数有三种方法:
(1)parameter
参数,作用域在模块内部,调用模块时可以在参数列表内传递参数;
(2)localparam
局部参数,作用域在模块内部,只能在模块内部定义,不能传参;
(3)`define
宏定义,作用域在整个工程;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值