当前位置:   article > 正文

verilog基本语法总结_verilog中{{3,0}1'b1}

verilog中{{3,0}1'b1}

最近学习了verilog基本语法,总结如下

数值表示

Verilog HDL 有下列四种基本的值来表示硬件电路中的电平逻辑:

  • 0:逻辑 0 或 “假”
  • 1:逻辑 1 或 “真”
  • x 或 X:未知
  • z 或 Z:高阻

整数数值表示方法
十进制('d 或 'D),十六进制('h 或 'H),二进制('b 或 'B),八进制('o 或 'O)
指明位宽

4'b1011         // 4bit 数值  
32'h3022_c0de   // 32bit 的数值
  • 1
  • 2

下划线 _ 是为了增强代码的可读性。
不指明位宽

counter = 'd100 ; //一般会根据编译器自动分频位宽,常见的为32bit  
counter = 100 ;  
counter = 32'h64 ;
  • 1
  • 2
  • 3

数据类型

wire 表示硬件单元间的物理连线
reg 存储单元,保持数据原有的值直到被改写(寄存器)
integer 整数
real 实数
time 时间
parameter 常量/参数

数组:在 Verilog 中允许声明 reg, wire, integer, time, real 及其向量类型的数组。

integer          flag [7:0] ; //8个整数组成的数组  
reg  [3:0]       counter [3:0] ; //由4个4bit计数器组成的数组  
wire [7:0]       addr_bus [3:0] ; //由4个8bit wire型变量组成的数组  
wire             data_bit[7:0][5:0] ; //声明1bit wire型变量的二维数组  
reg [31:0]       data_4d[11:0][3:0][3:0][255:0] ; //声明4维的32bit数据变量数组
  • 1
  • 2
  • 3
  • 4
  • 5

reg 数组构成存储器

reg               membit[0:255] ;  //256bit的1bit存储器  
reg  [7:0]        mem[0:1023] ;    //1Kbyte存储器,位宽8bit  
mem[511] = 8'b0 ;                  //令第512个8bit的存储单元值为0
  • 1
  • 2
  • 3

字符串

字符串保存在 reg 类型的变量中,每个字符占用一个字节(8bit)。因此寄存器变量的宽度应该足够大,以保证不会溢出。

reg [0: 14*8-1]       str ;  
initial begin  
    str = "run.runoob.com";  
end
  • 1
  • 2
  • 3
  • 4

表达式

操作符操作符号优先级
单目运算+ - ! ~最高
乘、除、取模* / % 
加减+ - 
移位<<  >> 
关系<  <=  >  >= 
等价==  !=  ===  !=== 
归约& ~& 
 ^ ~^ 
 | ~| 
逻辑&& 
 || 
条件?:最低

算数操作符

  • 单目操作符
  • 双目操作符

双目操作符

乘(*)、除(/)、加(+)、减(-)、求幂(**)、取模(%)。
注意不要溢出

关系操作符

大于(>),小于(<),大于等于(>=),小于等于(<=)
如果操作数中有一位为 x 或 z,则关系表达式的结果为 x。

A = 4 ;  
B = 3 ;  
X = 3'b1xx ;  
     
A > B     //为真  
A <= B    //为假  
A >= Z    //为X,不确定
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

等价操作符

逻辑相等(==),逻辑不等(!=),全等(===),非全等(!==)

逻辑相等/不等操作符不能比较 x 或 z,当操作数包含一个 x 或 z,则结果为不确定值。

全等比较时,如果按位比较有相同的 x 或 z,返回结果也可以为 1,即全等比较可比较 x 或 z。所以,全等比较的结果一定不包含 x

A = 4 ;  
B = 8'h04 ;  
C = 4'bxxxx ;  
D = 4'hx ;  
A == B        //为真  
A == (B + 1)  //为假  
A == C        //为X,不确定  
A === C       //为假,返回值为0  
C === D       //为真,返回值为1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

逻辑操作符

&&(逻辑与), ||(逻辑或),!(逻辑非)。

逻辑操作符的计算结果是一个 1bit 的值,0 表示假,1 表示真,x 表示不确定。

如果一个操作数不为 0,它等价于逻辑 1;如果一个操作数等于 0,它等价于逻辑 0。如果它任意一位为 x 或 z,它等价于 x。

A = 3;  
B = 0;  
C = 2'b1x ;  
     
A && B    //     为假  
A || B    //     为真  
! A       //     为假  
! B       //     为真  
A && C    //     为X,不确定  
A || C    //     为真,因为A为真  
(A==2) && (! B)  //为真,此时第一个操作数为表达式
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

按位操作符

取反(),与(&),或(|),异或(^),同或(^)
按位操作符对 2 个操作数的每 1bit 数据进行按位操作。
如果 2 个操作数位宽不相等,则用 0 向左扩展补充较短的操作数。
取反操作符只有一个操作数,它对操作数的每 1bit 数据进行取反操作。

A = 4'b0101 ;  
B = 4'b1001 ;  
C = 4'bx010 ;  
     
~A        //4'b1010  
A & B     //4'b0001  
A | B     //4'b1101  
A^B       //4'b1100  
A ~^ B    //4'b0011  
B | C     //4'b1011  
B&C       //4'bx000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

归约操作符

归约与(&),归约与非(&),归约或(|),归约或非(|),归约异或(),归约同或(~
归约操作符只有一个操作数,它对这个向量操作数逐位进行操作,最终产生一个 1bit 结果。

A = 4'b1010 ;
&A ;      //结果为 1 & 0 & 1 & 0 = 1'b0,可用来判断变量A是否全1
~|A ;     //结果为 ~(1 | 0 | 1 | 0) = 1'b0, 可用来判断变量A是否为全0
^A ;      //结果为 1 ^ 0 ^ 1 ^ 0 = 1'b0
  • 1
  • 2
  • 3
  • 4

移位操作符

移位操作符包括左移(<<),右移(>>),算术左移(<<<),算术右移(>>>)。

移位操作符是双目操作符,两个操作数分别表示要进行移位的向量信号(操作符左侧)与移动的位数(操作符右侧)。

算术左移和逻辑左移时,右边低位会补 0。

逻辑右移时,左边高位会补 0;而算术右移时,左边高位会补充符号位(正/负),以保证数据缩小后值的正确性。

A = 4'b1100 ;  
B = 4'b0010 ;  
A = A >> 2 ;        //结果为 4'b0011  
A = A << 1;         //结果为 4'b1000  
A = A <<< 1 ;       //结果为 4'b1000  
C = B + (A>>>2);    //结果为 2 + (-4/4) = 1, 4'b0001
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

拼接操作符

用大括号 {,} 来表示,用于将多个操作数(向量)拼接成新的操作数(向量),信号间用逗号隔开。

A = 4'b1010 ;  
B = 1'b1 ;  
Y1 = {B, A[3:2], A[0], 4'h3 };  //结果为Y1='b1100_0011  
Y2 = {4{B}, 3'd4};  //结果为 Y2=7'b111_1100  
Y3 = {32{1'b0}};  //结果为 Y3=32h0,常用作寄存器初始化时匹配位宽的赋初值
  • 1
  • 2
  • 3
  • 4
  • 5

条件操作符

condition_expression ? true_expression : false_expression

编译指令

//define
`define
// ifdef ... 
// else
// endif
`ifndef     WINDOW  
    parameter DATA_DW = 32 ;    
 `else  
    parameter DATA_DW = 64 ;  
 `endif
 //include
`include         "../../param.v"
`include         "header.v"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

timescale
在 Verilog 模型中,时延有具体的单位时间表述,并用 `timescale 编译指令将时间单位与实际时间相关联。

该指令用于定义时延、仿真的单位和精度,格式为:

`timescale      time_unit / time_precision
  • 1

time_unit 表示时间单位,time_precision 表示时间精度,它们均是由数字以及单位 s(秒),ms(毫秒),us(微妙),ns(纳秒),ps(皮秒)和 fs(飞秒)组成。时间精度可以和时间单位一样,但是时间精度大小不能超过时间单位大小

`timescale 1ns/100ps    //时间单位为1ns,精度为100ps,合法  
//`timescale 100ps/1ns  //不合法  
module AndFunc(Z, A, B);  
    output Z;  
    input A, B ;  
    assign #5.207 Z = A & B  
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

ATTITION

在编译过程中,`timescale 指令会影响后面所有模块中的时延值,直至遇到另一个 `timescale 指令或 `resetall 指令。

由于在 Verilog 中没有默认的 `timescale,如果没有指定 `timescale,Verilog 模块就有会继承前面编译模块的 `timescale 参数。有可能导致设计出错。

过程结构

过程结构语句有 2 种,initial 与 always 语句。它们是行为级建模的 2 种基本语句。
一个模块中可以包含多个 initial 和 always 语句,但 2 种语句不能嵌套使用。
这些语句在模块间并行执行,与其在模块的前后顺序没有关系。
但是 initial 语句或 always 语句内部可以理解为是顺序执行的(非阻塞赋值除外)。
每个 initial 语句或 always 语句都会产生一个独立的控制流,执行时间都是从 0 时刻开始。

initial

initial 语句从 0 时刻开始执行,只执行一次,多个 initial 块之间是相互独立的。
如果 initial 块内包含多个语句,需要使用关键字 begin 和 end 组成一个块语句。
如果 initial 块内只要一条语句,关键字 begin 和 end 可使用也可不使用。
initial 理论上来讲是不可综合的,多用于初始化、信号检测等。

`timescale 1ns/1ns  
   
module test ;  
    reg  ai, bi ;  
   
    initial begin  
        ai         = 0 ;  
        #25 ;      ai        = 1 ;  
        #35 ;      ai        = 0 ;        //absolute 60ns  
        #40 ;      ai        = 1 ;        //absolute 100ns  
        #10 ;      ai        = 0 ;        //absolute 110ns  
    end  
   
    initial begin  
        bi         = 1 ;  
        #70 ;      bi        = 0 ;        //absolute 70ns  
        #20 ;      bi        = 1 ;        //absolute 90ns  
    end  
   
    //at proper time stop the simulation  
    initial begin  
        forever begin  
            #100;  
            //$display("---gyc---%d", $time);  
            if ($time >= 1000) begin  
                $finish ;  
            end  
        end  
   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
  • 30
  • 31

always

always 语句是重复执行的。always 语句块从 0 时刻开始执行其中的行为语句;当执行完最后一条语句后,便再次执行语句块中的第一条语句,如此循环反复。
不同always块同步执行
由于循环执行的特点,always 语句多用于仿真时钟的产生,信号行为的检测等。
下面用 always 产生一个 100MHz 时钟源,并在 1010ns 时停止仿真代码如下。

`timescale 1ns/1ns  
   
module test ;  
   
    parameter CLK_FREQ   = 100 ; //100MHz  
    parameter CLK_CYCLE  = 1e9 / (CLK_FREQ * 1e6) ;   //switch to ns  
   
    reg  clk ;  
    initial      clk = 1'b0 ;      //clk is initialized to "0"  
    always     # (CLK_CYCLE/2) clk = ~clk ;       //generating a real clock by reversing  
   
    always begin  
        #10;  
        if ($time >= 1000) begin  
            $finish ;  
        end  
    end  
   
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

连续赋值

assign

#组合逻辑 #阻塞赋值
连续赋值语句是 Verilog 数据流建模的基本语句,用于对 wire 型变量进行赋值。:
assign LHS_target = RHS_expression;
特点

  • LHS_target 必须是一个标量或者线型向量,而不能是寄存器类型。
  • RHS_expression 的类型没有要求,可以是标量或线型或存器向量,也可以是函数调用。
  • 只要 RHS_expression 表达式的操作数有事件发生(值的变化)时,RHS_expression 就会立刻重新计算,同时赋值给 LHS_target。
wire      Cout, A, B ;
assign    Cout  = A & B ;     //实现计算A与B的功能
  • 1
  • 2

设计全加器

module full_adder1(  
    input    Ai, Bi, Ci  
    output   So, Co);  
   
    assign {Co, So} = Ai + Bi + Ci ;  
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

过程赋值

#并行 #非阻塞赋值

非阻塞赋值属于并行执行语句,即下一条语句的执行和当前语句的执行是同时进行的,它不会阻塞位于同一个语句块中后面语句的执行。

非阻塞赋值语句使用小于等于号 <=作为赋值符。

对比

`timescale 1ns/1ns  
   
module test ;  
    reg [3:0]   ai, bi ;  
    reg [3:0]   ai2, bi2 ;  
    reg [3:0]   value_blk ;  
    reg [3:0]   value_non ;  
    reg [3:0]   value_non2 ;  
   
    initial begin  
        ai            = 4'd1 ;   //(1)  
        bi            = 4'd2 ;   //(2)  
        ai2           = 4'd7 ;   //(3)  
        bi2           = 4'd8 ;   //(4)  
        #20 ;                    //(5)  
   
        //non-block-assigment with block-assignment  
        ai            = 4'd3 ;     //(6)  
        bi            = 4'd4 ;     //(7)  
        value_blk     = ai + bi ;  //(8)  
        value_non     <= ai + bi ; //(9)  
   
        //non-block-assigment itself  
        ai2           <= 4'd5 ;           //(10)  
        bi2           <= 4'd6 ;           //(11)  
        value_non2    <= ai2 + bi2 ;      //(12)  
    end  
   
   //stop the simulation  
    always begin  
        #10 ;  
        if ($time >= 1000) $finish ;  
    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
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

对比

always @(posedge clk) begin  
    a = b ;  
end  
   
always @(posedge clk) begin  
    b = a;  
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
always @(posedge clk) begin  
    a <= b ;  
end  
   
always @(posedge clk) begin  
    b <= a;  
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

时序控制

时延

常规时延
语句需要等待一定时间,然后将计算结果赋值给目标信号。

格式为:#delay procedural_statement;

reg  value_test ;
reg  value_general ;
#10  value_general    = value_test ;
  • 1
  • 2
  • 3

内嵌时延

遇到内嵌延时时,该语句先将计算结果保存,然后等待一定的时间后赋值给目标信号。

reg  value_test ;
reg  value_embed ;
value_embed        = #10 value_test ;
  • 1
  • 2
  • 3

边沿触发事件

在 Verilog 中,事件是指某一个 reg 或 wire 型变量发生了值的变化。

基于事件触发的时序控制又主要分为以下几种。

一般事件控制

事件控制用符号 `@ 表示。

语句执行的条件是信号的值发生特定的变化。

关键字 posedge 指信号发生边沿正向跳变,negedge 指信号发生负向边沿跳变,未指明跳变方向时,则 2 种情况的边沿变化都会触发相关事件。

//信号clk只要发生变化,就执行q<=d,双边沿D触发器模型  
always @(clk) q <= d ;                  
//在信号clk上升沿时刻,执行q<=d,正边沿D触发器模型  
always @(posedge clk) q <= d ;    
//在信号clk下降沿时刻,执行q<=d,负边沿D触发器模型  
always @(negedge clk) q <= d ;  
//立刻计算d的值,并在clk上升沿时刻赋值给q,不推荐这种写法  
q = @(posedge clk) d ;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

命名事件控制

用户可以声明 event(事件)类型的变量,并触发该变量来识别该事件是否发生。命名事件用关键字 event 来声明,触发信号用 -> 表示。

event     start_receiving ;  
always @( posedge clk_samp) begin  
        -> start_receiving ;       //采样时钟上升沿作为时间触发时刻  
end  
   
always @(start_receiving) begin  
    data_buf = {data_if[0], data_if[1]} ; //触发时刻,对多维数据整合  
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

敏感列表

当多个信号或事件中任意一个发生变化都能够触发语句的执行时,Verilog 中使用"或"表达式来描述这种情况,用关键字 or 连接多个事件或信号。这些事件或信号组成的列表称为"敏感列表"。当然,or 也可以用逗号, 来代替。例如:

//带有低有效复位端的D触发器模型  
always @(posedge clk or negedge rstn)    begin        
//always @(posedge clk , negedge rstn)    begin        
//也可以使用逗号陈列多个事件触发  
    if(! rstn)begin  
        q <= 1'b ;        
    end  
    else begin  
        q <= d ;  
    end  
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

电平敏感事件控制

电平作为敏感信号来控制时序,即后面语句的执行需要等待某个条件为真。Verilog 中使用关键字 wait 来表示这种电平敏感情况。例如:

initial begin  
    wait (start_enable) ;      //等待 start 信号  
    forever begin  
        //start信号使能后,在clk_samp上升沿,对数据进行整合  
        @(posedge clk_samp)  ;  
        data_buf = {data_if[0], data_if[1]} ;        
    end  
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

语句块

顺序块

begin end
顺序块用关键字 beginend 来表示。
顺序块中的语句是一条条执行的。当然,非阻塞赋值除外。
顺序块中每条语句的时延总是与其前面语句执行的时间相关。

并行块

fork join
并行块有关键字 forkjoin 来表示
并行块中的语句是并行执行的,即便是阻塞形式的赋值。
并行块中每条语句的时延都是与块语句开始执行的时间相关。

命名块

我们可以给块语句结构命名。
命名的块中可以声明局部变量,通过层次名引用的方法对变量进行访问。

`timescale 1ns/1ns  
   
module test;  
   
    initial begin: runoob   //命名模块名字为runoob,分号不能少  
        integer    i ;       //此变量可以通过test.runoob.i 被其他模块使用  
        i = 0 ;  
        forever begin  
            #10 i = i + 10 ;        
        end  
    end  
   
    reg stop_flag ;  
    initial stop_flag = 1'b0 ;  
    always begin : detect_stop  
        if ( test.runoob.i == 100) begin //i累加10次,即100ns时停止仿真  
            $display("Now you can stop the simulation!!!");  
            stop_flag = 1'b1 ;  
        end  
        #10 ;  
    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

命名的块也可以被禁用,用关键字 disable 来表示。

disable 可以终止命名块的执行,可以用来从循环中退出、处理错误等。

与 C 语言中 break 类似,但是 break 只能退出当前所在循环,而 disable 可以禁用设计中任何一个命名的块。

`timescale 1ns/1ns  
   
module test;  
   
    initial begin: runoob_d //命名模块名字为runoob_d  
        integer    i_d ;  
        i_d = 0 ;  
        while(i_d<=100) begin: runoob_d2  
            # 10 ;  
            if (i_d >= 50) begin       //累加5次停止累加  
                disable runoob_d3.clk_gen ;//stop 外部block: clk_gen  
                disable runoob_d2 ;       //stop 当前block: runoob_d2  
            end  
            i_d = i_d + 10 ;  
        end  
    end  
   
    reg clk ;  
    initial begin: runoob_d3  
        while (1) begin: clk_gen  //时钟产生模块  
            clk=1 ;      #10 ;  
            clk=0 ;      #10 ;  
        end  
    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

逻辑语句

条件语句

if...else 结合 begin...end

if(en) begin  
    if(sel == 2'b1) begin  
        sout = p1s ;  
    end  
    else begin  
        sout = p0 ;  
    end  
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

case

case(case_expr)
    condition1     :             true_statement1 ;
    condition2     :             true_statement2 ;
    ……
    default        :             default_statement ;
endcase
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

四路选择器

module mux4to1(  
    input [1:0]     sel ,  
    input [1:0]     p0 ,  
    input [1:0]     p1 ,  
    input [1:0]     p2 ,  
    input [1:0]     p3 ,  
    output [1:0]    sout);  
   
    reg [1:0]     sout_t ;  
    always @(*)  
        case(sel)  
            2'b00:   begin        
                    sout_t = p0 ;  
                end  
            2'b01:       sout_t = p1 ;  
            2'b10:       sout_t = p2 ;  
            default:     sout_t = p3 ;  
        endcase  
    assign sout = sout_t ;  
   
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
循环语句

包含 while,for,repeat,和 forever 循环。循环语句只能在 always 或 initial 块中使用,但可以包含延迟表达式。

//while 循环中止条件为 condition 为假。
//如果开始执行到 while 循环时 condition 已经为假,那么循环语句一次也不会执行。
while (condition) begin
    …
end

//initial_assignment 为初始条件。
//condition 为终止条件,condition 为假时,立即跳出循环。
//step_assignment 为改变控制变量的过程赋值语句,通常为增加或减少循环变量计数。
for(initial_assignment; condition ; step_assignment)  begin
    …
end

//repeat 的功能是执行固定次数的循环,它不能像 while 循环那样用一个逻辑表达式来确定循环是否继续执行。repeat 循环的次数必须是一个常量、变量或信号。如果循环次数是变量信号,则循环次数是开始执行 repeat 循环时变量信号的值。即便执行期间,循环次数代表的变量信号值发生了变化,repeat 执行次数也不会改变。
repeat (loop_times) begin
    …
end

//forever 语句表示永久循环,不包含任何条件表达式,一旦执行便无限的执行下去,系统函数 $finish 可退出 forever。
//forever 相当于 while(1) 。
forever begin
    …
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

模块与端口

模块

module module_name 
#(parameter_list)
(port_list) ;
              Declarations_and_Statements ;
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5

![[Pasted image 20230510212756.png]]

端口

端口是模块与外界交互的接口。对于外部环境来说,模块内部是不可见的,对模块的调用只能通过端口连接进行。

端口列表
模块的定义中包含一个可选的端口列表,一般将不带类型、不带位宽的信号变量罗列在模块声明里。

module pad(
    DIN, OEN, PULL,
    DOUT, PAD);
  • 1
  • 2
  • 3

端口声明

端口信号在端口列表中罗列出来以后,就可以在模块实体中进行声明了。
端口类型有 3 种: 输入(input),输出(output)和双向端口(inout)。
input、inout 类型不能声明为 reg 数据类型,因为 reg 类型是用于保存数值的,而输入端口只能反映与其相连的外部信号的变化,不能保存这些信号的值。
output 可以声明为 wire 或 reg 数据类型。
eg:

module pad(  
    input        DIN, OEN ,  
    input [1:0]  PULL ,  
    inout        PAD ,  
    output reg   DOUT  
    );  
   
module pad(  
    input        DIN, OEN ,  
    input [1:0]  PULL ,  
    inout        PAD ,  
    output       DOUT  
    );  
   
    reg        DOUT ;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

模块例化

eg

module full_adder1(  
    input    Ai, Bi, Ci  
    output   So, Co);  
   
    assign {Co, So} = Ai + Bi + Ci ;  
endmodule

//例化
full_adder1  u_adder0(  
    .Ai     (a[0]),  
    .Bi     (b[0]),  
    .Ci     (c==1'b1 ? 1'b0 : 1'b1),  
    .So     (so_bit0),  
    .Co     (co_temp[0]));

//输出端口可悬空或删除
//output 端口 Co 悬空  
full_adder1  u_adder0(  
    .Ai     (a[0]),  
    .Bi     (b[0]),  
    .Ci     (c==1'b1 ? 1'b0 : 1'b1),  
    .So     (so_bit0),  
    .Co     ());  
   
//output 端口 Co 删除  
full_adder1  u_adder0(  
    .Ai     (a[0]),  
    .Bi     (b[0]),  
    .Ci     (c==1'b1 ? 1'b0 : 1'b1),  
    .So     (so_bit0));
//顺序实例化,需一一对应
full_adder1  u_adder1(
    a[1], b[1], co_temp[0], so_bit1, co_temp[1]);

  • 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

带参实例化

例化模块时,将新的参数值写入模块例化语句,以此来改写原有 module 的(parameter)参数值。
eg:

module  ram  
    #(  parameter       AW = 2 ,  
        parameter       DW = 3 )  
    (  
        input                   CLK ,  
        input [AW-1:0]          A ,  
        input [DW-1:0]          D ,  
        input                   EN ,  
        input                   WR ,    //1 for write and 0 for read  
        output reg [DW-1:0]     Q  
     );  
   
    reg [DW-1:0]         mem [0:(1<<AW)-1] ;  
    always @(posedge CLK) begin  
        if (EN && WR) begin  
            mem[A]  <= D ;  
        end  
        else if (EN && !WR) begin  
            Q       <= mem[A] ;  
        end  
    end  
   
endmodule

//带参数例化,改变parameter从而改变位宽和字节

ram #(.AW(4), .DW(4))  
    u_ram  
    (  
        .CLK    (clk),  
        .A      (a[AW-1:0]),  
        .D      (d),  
        .EN     (en),  
        .WR     (wr),    //1 for write and 0 for read  
        .Q      (q)  
     );
  • 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

函数

函数只能在模块中定义,位置任意,并在模块的任何地方引用,作用范围也局限于此模块。函数主要有以下几个特点:

  • 1)不含有任何延迟、时序或时序控制逻辑
  • 2)至少有一个输入变量
  • 3)只有一个返回值,且没有输出
  • 4)不含有非阻塞赋值语句
  • 5)函数可以调用其他函数,但是不能调用任务
function [range-1:0]     function_id ;
input_declaration ;
 other_declaration ;
procedural_statement ;
endfunction
  • 1
  • 2
  • 3
  • 4
  • 5

函数的返回值通过这个function_id变量进行传递。(有点像matlab的函数)

eg:数据大小端转换

module endian_rvs  
    #(parameter N = 4)  
        (  
            input             en,     //enable control  
            input [N-1:0]     a ,  
            output [N-1:0]    b  
    );  
           
        reg [N-1:0]          b_temp ;  
        always @(*) begin  
        if (en) begin  
                b_temp =  data_rvs(a);  
            end  
            else begin  
                b_temp = 0 ;  
            end  
    end  
        assign b = b_temp ;  
           
    //function entity  
        function [N-1:0]     data_rvs ;  
            input     [N-1:0] data_in ;  
            parameter         MASK = 32'h3 ;  
            integer           k ;  
            begin  
                for(k=0; k<N; k=k+1) begin  
                    data_rvs[N-k-1]  = data_in[k] ;    
                end  
            end  
    endfunction  
           
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

任务

module 里的module

状态机

有限状态机(Finite-State Machine,FSM),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。状态机不仅是一种电路的描述工具,而且也是一种思想方法,在电路设计的系统级和 RTL 级有着广泛的应用。

状态机类型

Verilog 中状态机主要用于同步时序逻辑的设计,能够在有限个状态之间按一定要求和规律切换时序电路的状态。状态的切换方向不但取决于各个输入值,还取决于当前所在状态。 状态机可分为 2 类:Moore 状态机和 Mealy 状态机。
Moore 型状态机

Moore 型状态机的输出只与当前状态有关,与当前输入无关。

输出会在一个完整的时钟周期内保持稳定,即使此时输入信号有变化,输出也不会变化。输入对输出的影响要到下一个时钟周期才能反映出来。这也是 Moore 型状态机的一个重要特点:输入与输出是隔离开来的。

Mealy 型状态机

Mealy 型状态机的输出,不仅与当前状态有关,还取决于当前的输入信号。

Mealy 型状态机的输出是在输入信号变化以后立刻发生变化,且输入变化可能出现在任何状态的时钟周期内。因此,同种逻辑下,Mealy 型状态机输出对输入的响应会比 Moore 型状态机早一个时钟周期。

状态机设计:3 段式(推荐)

状态机设计如下:

  • (0) 首先,根据状态机的个数确定状态机编码。利用编码给状态寄存器赋值,代码可读性更好。
  • (1) 状态机第一段,时序逻辑,非阻塞赋值,传递寄存器的状态。
  • (2) 状态机第二段,组合逻辑,阻塞赋值,根据当前状态和当前输入,确定下一个状态机的状态。
  • (3) 状态机第三段,时序逻辑,非阻塞赋值,因为是 Mealy 型状态机,根据当前状态和当前输入,确定输出信号。

eg:
自动售卖机
自动售卖机的功能描述如下:

饮料单价 2 元,该售卖机只能接受 0.5 元、1 元的硬币。考虑找零和出货。投币和出货过程都是一次一次的进行,不会出现一次性投入多币或一次性出货多瓶饮料的现象。每一轮售卖机接受投币、出货、找零完成后,才能进入到新的自动售卖状态。

// vending-machine  
// 2 yuan for a bottle of drink  
// only 2 coins supported: 5 jiao and 1 yuan  
// finish the function of selling and changing  
  
module  vending_machine_p3  (  
    input           clk ,  
    input           rstn ,  
    input [1:0]     coin ,     //01 for 0.5 jiao, 10 for 1 yuan  
  
    output [1:0]    change ,  
    output          sell    //output the drink  
    );  
  
    //machine state decode  
    parameter            IDLE   = 3'd0 ;  
    parameter            GET05  = 3'd1 ;  
    parameter            GET10  = 3'd2 ;  
    parameter            GET15  = 3'd3 ;  
  
    //machine variable  
    reg [2:0]            st_next ;  
    reg [2:0]            st_cur ;  
  
    //(1) state transfer  
    always @(posedge clk or negedge rstn) begin  
        if (!rstn) begin  
            st_cur      <= 'b0 ;  
        end  
        else begin  
            st_cur      <= st_next ;  
        end  
    end  
  
    //(2) state switch, using block assignment for combination-logic  
    //all case items need to be displayed completely      
    always @(*) begin  
        //st_next = st_cur ;//如果条件选项考虑不全,可以赋初值消除latch  
        case(st_cur)  
            IDLE:  
                case (coin)  
                    2'b01:     st_next = GET05 ;  
                    2'b10:     st_next = GET10 ;  
                    default:   st_next = IDLE ;  
                endcase  
            GET05:  
                case (coin)  
                    2'b01:     st_next = GET10 ;  
                    2'b10:     st_next = GET15 ;  
                    default:   st_next = GET05 ;  
                endcase  
  
            GET10:  
                case (coin)  
                    2'b01:     st_next = GET15 ;  
                    2'b10:     st_next = IDLE ;  
                    default:   st_next = GET10 ;  
                endcase  
            GET15:  
                case (coin)  
                    2'b01,2'b10:  
                               st_next = IDLE ;  
                    default:   st_next = GET15 ;  
                endcase  
            default:    st_next = IDLE ;  
        endcase  
    end  
  
    //(3) output logic, using non-block assignment  
    reg  [1:0]   change_r ;  
    reg          sell_r ;  
    always @(posedge clk or negedge rstn) begin  
        if (!rstn) begin  
            change_r       <= 2'b0 ;  
            sell_r         <= 1'b0 ;  
        end  
        else if ((st_cur == GET15 && coin ==2'h1)  
               || (st_cur == GET10 && coin ==2'd2)) begin  
            change_r       <= 2'b0 ;  
            sell_r         <= 1'b1 ;  
        end  
        else if (st_cur == GET15 && coin == 2'h2) begin  
            change_r       <= 2'b1 ;  
            sell_r         <= 1'b1 ;  
        end  
        else begin  
            change_r       <= 2'b0 ;  
            sell_r         <= 1'b0 ;  
        end  
    end  
    assign       sell    = sell_r ;  
    assign       change  = change_r ;  
  
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
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

参考文献
https://www.runoob.com/w3cnote_genre/verilog

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/101531
推荐阅读
相关标签
  

闽ICP备14008679号