前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Verilog HDL 语法学习笔记

Verilog HDL 语法学习笔记

作者头像
FPGA技术江湖
发布2020-12-30 16:14:59
发布2020-12-30 16:14:59
2.1K00
代码可运行
举报
文章被收录于专栏:FPGA技术江湖FPGA技术江湖
运行总次数:0
代码可运行
大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。

今天给大侠带来Verilog HDL 语法学习笔记,话不多说,上货。

关于详细的VHDL语法以及Verilog HDL语法可参见往期文章。

一周掌握 FPGA VHDL Day 7 暨汇总篇

一周掌握FPGA Verilog HDL语法 汇总篇

Verilog HDL 语法学习笔记

一、Verilog HDL 简介

1.1 Verilog HDL 的历史

Verilog HDL 语 言 最 初 是 作为 Gateway Design Automation 公 司 ( Gateway DesignAutomation 公司后来被著名的 Cadence Design Systems 公司收购)模拟器产品开发的硬件建模语言。

开始 Verilog HDL 只是一种专用语言,随着 Gateway Design Automation 公司模拟、仿真器产品的广泛使用,Verilog HDL 便于使用、实用的语言逐渐为众多设计者所接受。1995年 Verilog HDL 正式成为 IEEE 标准,称为 IEEE Std 1364-1995。

1.2 Verilog HDL 的特点

Verilog HDL 语言不仅定义了语法,而且对每个语法结构都定义了清晰的模拟、仿真语义。使用这种语言编写的模型可以方便地使用 Verilog 仿真器进行验证。Verilog HDL 从 C 语言中继承了多种操作符和结构。Verilog HDL 提供了扩展的建模能力和扩展模块。Verilog HDL 语言的核心子集非常易于学习和使用,这对大多数建模应用来说已经足够。

Verilog HDL 之所以成为和 VHDL 并驾齐驱的硬件描述语言,是因为它具有如下特点:

• 基本逻辑门和开关级基本结构模型都内置在语言中;

• 可采用多种方式对设计建模,这些方式包括行为描述方式、数据流方式、结构化方式;

• Verilog HDL 中有线网(Wire)数据类型和寄存器(Reg)数据类型两类数据类型,线网类型表示构件间的物理连线,而寄存器类型表示抽象的数据存储元件;

• 能够描述层次设计,可使用模块实例结构描述任何层次;

• 设计的规模可以是任意的,语言不对设计的规模大小施加任何限制;

• Verilog HDL 不再是某些公司的专有语言而是 IEEE 标准;

• Verilog HDL 语言的描述能力能够通过使用编程语言接口(Programme LanguageInterface,简称 PLI)机制进一步扩展,PLI 允许外部函数访问 Verilog 模块内信息、允许设计者与模拟器交互的例程集合;

• 设计能够在多个层次上加以描述,从开关级、门级、寄存器传送级(RT L)到算法级,包括进程和队列级;

• Verilog HDL 能够监控模拟验证的执行,即模拟验证执行过程中设计的值能够被监控和显示,这些值也能够用于与期望值比较,在不匹配的情况下打印报告消息。

二、Verilog HDL 程序基本结构

模块是 Verilog 的基本描述单位,描述某个设计的功能或结构及其与其他模块通信的外部端口。一个模块的基本语法如下:

代码语言:javascript
代码运行次数:0
复制
module module_name//模块名称
  (port_list);//输入输出信号列表
  //说明
  reg //寄存器
  wire//线网
  parameter//参数
  input//输入信号
  output//输出信号
  inout//输入输出信号
  function//函数
  task//任务
  . . .//语句
  Initial statement
  Always statement
  Module instantiation//
  Gate instantiation//
  UDP instantiation//
  Continuous assignment//
endmodule

说明部分用于定义不同的项,例如模块描述中使用的寄存器和参数、语句定义设计的功能和结构。说明部分和语句可以放置在模块中的任何地方,但是变量、寄存器、线网和参数等的说明部分必须在使用前出现。为了使模块描述清晰和具有良好的可读性, 最好将所有的说明部分放在语句前。

图 1 所示的是一个半加器。

图 1 半加器

这个半加器用 Verilog HDL 实现,代码如下:

代码语言:javascript
代码运行次数:0
复制
module HalfAdder(A,B,Sum,Carry) ;
    
    input A,B;
    output Sum, Carry;
    
    assign #2 Sum = A ^ B;
    assign #5 Carry = A & B;
    
endmodule

模块的名字是 HalfAdder。模块有 4 个端口:两个输入端口 A 和 B,两个输出端口 Sum 和Carry。由于没有定义端口的位数,所有端口大小都为 1 位;同时由于没有各端口的数据类型说明,这 4 个端口都是线网数据类型。模块包含两条描述半加器数据流行为的连续赋值语句。从这种意义上讲,这些语句在模块中出现的顺序无关紧要,因为这些语句是并发的。每条语句的执行顺序依赖于发生在变量 A 和 B 上的事件。

三、Verilog HDL 语言的数据类型和运算符

本篇介绍 Verilog HDL 语言的基本要素,包括标识符、注释、数值、编译程序指令、系统任务和系统函数、两种主要的数据类型。

3.1 标识符

Verilog HDL 中的标识符可以是任意一组字母、数字、$符号和_(下划线)符号的组合,但标识符的第一个字符必须是字母或者下划线。另外,标识符是区分大小写的。以下是标识符的几个例子:

代码语言:javascript
代码运行次数:0
复制
Piero
PIERO / /与 Piero 不同。
_a1_b2
c00_68
ABC$

3.2 数据类型

Verilog HDL 有两大类数据类型:

• 线网类型,表示 Verilog HDL 结构化元件间的物理连线,它的值由驱动元件的值决定,例如连续赋值或门的输出,线网的缺省值为 z(高阻态);

• 寄存器类型,表示一个抽象的数据存储单元,它只能在 always 语句和 initial 语句中被赋值,并且它的值被保存下来,缺省值为 x(未知状态)。

1)线网类型

线网数据类型包含下述不同种类的线网子类型:wire、tri、wor、trior、wand、triand、trireg、tri1、tri0、supply0、supply1。简单的线网类型说明语法为:

代码语言:javascript
代码运行次数:0
复制
net_kind [msb:lsb] net1, net2, . . . , netN;

net_kind 是上述线网类型的一种。msb 和 lsb 是用于定义线网范围的常量表达式,范围定义是可选的;如果没有定义范围,缺省的线网类型为 1 位。下面是一个线网类型说明实例。

代码语言:javascript
代码运行次数:0
复制
wire Rdy, Start; //2 个 1 位的连线。
wand [2:0] Addr; //Addr 是 3 位线与。

2)寄存器类型

有 5 种不同的寄存器类型:reg、integer、time、real 和 realtime。寄存器数据类型 reg是最常见的数据类型。reg 类型使用保留字 reg 加以说明,形式如下:

代码语言:javascript
代码运行次数:0
复制
reg [msb: lsb] reg1, reg2, . . . regN;

msb 和 lsb 定义了范围,并且均为常数值表达式。范围定义是可选的,如果没有定义范围,缺省值为 1 位寄存器。例如:

代码语言:javascript
代码运行次数:0
复制
reg [3:0] Sat; //Sat 为 4 位寄存器。
reg Cnt; //1 位寄存器。
reg [1:32] Kisp, Pisp, Lisp;

3.3 模块端口

模块端口是指模块与外界交流信息的接口,包括 3 种:

• in:模块通过这个接口从外界环境读取数据,是不可写的;

• out:模块通过这个接口向外界环境输出数据,是不可读的;

• inout:模块可以通过这个接口从外界环境读取并输出数据,数据可以双向流通。

3.4 值集合

Verilog HDL 有下列 4 种基本的值:

• 0:逻辑 0 或“假”;

• 1:逻辑 1 或“真”;

• x:未知;

• z:高阻。

这 4 种值的解释都内置于语言中。如一个为 z 的值总是意味着高阻抗,一个为 0 的值通常是指逻辑 0。在门的输入或一个表达式中的为“z”的值通常解释成“x”。此外,x 值和 z 值都是不分大小写的。也就是说,值 0x1z 与值 0X1Z 相同。Verilog HDL 中的常量是由以上这四类基本值组成的。

Verilog HDL 中有 3 类常量:整型、实数型和字符串型。下划线符号(_)可以随意用在整数或实数中,它们就数量本身没有意义。它们能用来提高易读性;惟一的限制是下划线符号不能用作为首字符。

1)整型数

整型数可以按如下两种方式书写:简单的十进制数格式和基数格式。

简单的十进制形式的整数定义为带有一个可选的“+”(一元)或“-”(一元)操作符的数字序列。下面是这种简易十进制形式整数的例子:

代码语言:javascript
代码运行次数:0
复制
32;// 十进制数 32
-15;// 十进制数-15

基数格式的格式为:

代码语言:javascript
代码运行次数:0
复制
[size ] 'base value

size 定义以位计的常量的位长,base 为 o 或 O(表示八进制)、b 或 B(表示二进制)、d或 D(表示十进制)、h 或 H(表示十六进制),value 是基于 base 的值的数字序列。值 x 和 z以及十六进制中的 a 到 f 不区分大小写。下面是一些具体实例:

代码语言:javascript
代码运行次数:0
复制
5'O37;// 5 位八进制数
4'D2;//4 位十进制数
4'B1x_01;// 4 位二进制数
7'Hx ;//7 位 x(扩展的 x),即 xxxxxxx
4'hZ;// 4 位 z(扩展的 z) , 即 zzzz
4'd-4;//非法,数值不能为负
8'h 2 A;//在位长和字符之间,以及基数和数值之间允许出现空格
3' b001;//非法: `和基数 b 之间不允许出现空格
(2+3)'b10;// 非法:位长不能够为表达式

2)实数

实数可以用下列两种形式定义:

• 十进制计数法,例如 2.0、5.678、1、1572.12;

• 科学计数法,例如 23_5.1e2(其值为 23510.0,忽略下划线)、3.6E2(360.0)。

3)字符串

字符串是双引号内的字符序列。字符串不能分成多行书写,例如:

代码语言:javascript
代码运行次数:0
复制
"INTERNAL ERROR"
" REACHED->HERE "

用 8 位 ASCII 值表示的字符可看作是无符号整数。因此字符串是 8 位 ASCII 值的序列。为存储字符串“INTERNAL ERROR”,变量需要 8 * 1 4 位。

代码语言:javascript
代码运行次数:0
复制
reg [1 : 8*14] Message;
. . .
Message = "INTERNAL ERROR"

反斜线(\)用于对确定的特殊字符转义。

代码语言:javascript
代码运行次数:0
复制
\n 换行符
\t 制表符
\\ 字符\本身
\" 字符"
\206 八进制数 2 0 6 对应的字符

3.5 表达式

表达式是 Verilog HDL 语言中进行逻辑运算和表达最基本的元素。表达式由操作符和操作数按照一定的规则组合而成,下面进行详细介绍。

1)操作数

操作数的类型包括:常量、参数、线网、寄存器、存储器单元和函数调用等。

常量的使用规则在3.4 中进行说明,下面是一些实例:

代码语言:javascript
代码运行次数:0
复制
256,7 //非定长的十进制数
4'b10_11, 8'h0A // 定长的整型常量
'b1, 'hFBA // 非定长的整数常量
90.00006 // 实数型常量
"BOND" // 串常量;每个字符作为 8 位 ASCII 值存储

表达式中的整数值可被解释为有符号数或无符号数。参数类似于常量,并且使用参数声明进行说明。下面是参数说明实例:

代码语言:javascript
代码运行次数:0
复制
parameterLOAD = 4'd12, STORE = 4'd10;

LOAD 和 STORE 为参数的例子,值分别被声明为 12 和 10。

线网在表达式中可以分别按照标量和向量两种方式使用,下面是线网说明实例:

代码语言:javascript
代码运行次数:0
复制
wire [0:3] Prt; //Prt 为 4 位向量线网
wire Bdq; //Bbq 是标量线网

线网中的值被解释为无符号数。例如在连续赋值语句中:

代码语言:javascript
代码运行次数:0
复制
assign Prt = -3;

Prt 被赋于位向量 1101,实际上为十进制的 13,例如在下面的连续赋值中:

代码语言:javascript
代码运行次数:0
复制
assign Prt = 4'HA;

Prt 被赋于位向量 1010,即为十进制的 10。

寄存器也是可以按照标量和向量两种方式使用。寄存器变量使用寄存器声明进行说明,例如:

代码语言:javascript
代码运行次数:0
复制
integer TemA, TemB;
reg [1:5] State;
time Que [ 1:5 ] ;

整型寄存器中的值被解释为有符号的二进制补码数,而 reg 寄存器或时间寄存器中的值被解释为无符号数,实数和实数时间类型寄存器中的值被解释为有符号浮点数。例如下面的寄存器代码:

代码语言:javascript
代码运行次数:0
复制
TemA = -10; //TemA 值为位向量 10110,是 10 的二进制补码
TemA = 'b1011; //TemA 值为十进制数 11
State = -10; //State 值为位向量 10110,即十进制数 22
State = 'b1011; // State 值为位向量 01011,是十进制值 11

在 Verilog HDL 语言中,对于向量形式的线网和寄存器,都可以采用部分选择的方式使用向量中需要的部分。在部分选择中,向量的连续序列被选择,形式如下:

代码语言:javascript
代码运行次数:0
复制
net_or_reg_vector [msb_const_expr:1sb_const_expr]//部分选择的语法形式
State [1:4] //寄存器部分选择
Prt [1:3] // 线网部分选择

存储器单元的定义形式如下:

代码语言:javascript
代码运行次数:0
复制
memory [word_address] //定义形式
reg [1:8] Ack, Dram [ 0 : 6 3 ] ;//例子
. . .
Ack = Dram [60]; //存储器的第 6 0 个单元

不允许对存储器变量值部分选择或位选择。例如:

代码语言:javascript
代码运行次数:0
复制
Dram [60] [2] //使用错误
Dram [60] [2:4]//使用错误

在存储器中读取一个位或部分选择一个字的方法如下:将存储器单元赋值给寄存器变量,然后对该寄存器变量采用部分选择或位选择操作。

2)操作符

Verilog HDL语言中的操作符包括:

• 算术操作符 +(加法)、-(减法)、×(乘法)、÷(除法)和%(取模)。

• 关系操作符 >(大于)、<(小于)、≥(大于等于)和≤(小于等于),计算结果为真(1)或者假(0)。

• 相等操作符 ==(逻辑相等)、!=(逻辑不相等)、===(逻辑全等)和!==(非全等)。

• 逻辑操作符 &&(逻辑与)、||(逻辑或)、!(逻辑非)。

• 按位操作符 ~(一元非)、&(二元与)、|(二元或)、^(二元异或)和~^(二元异或非)。

• 移位操作符 <<(左移)、>>(右移)。

• 条件操作符 条件操作符根据条件表达式的值选择表达式。

四、Verilog HDL 语言的描述语句

Verilog HDL 语言的描述语句有结构化建模方式、数据流建模方式和行为建模方式 3 种。

4.1 结构化建模方式

Verilog HDL 中可以使用内置基本门来进行硬件描述。Verilog HDL 中提供下列内置基本门:

• 多输入门 and(与门)、nand(与非门)、or(或门)、nor(或非门)、xor(异或门)。

• 多输出门 buf(缓冲门)、not(取反)。

• 三态门 bufif0、bufif1、notif0、notif1。

• 上拉、下拉电阻 pullup(上拉电阻)、pulldown(下拉电阻)。

• MOS 开关 cmos、nmos、pmos、rcmos、rnmos、rpmos。

• 双向开关 tran、tranif0、tranif1、rtran、rtranif0、rtranif1。

门级逻辑设计描述中可使用具体的门实例语句。下面是简单的门实例语句的格式:

代码语言:javascript
代码运行次数:0
复制
gate_type[instance_name] (term1, term2, . . . ,termN ) ;

其中 instance_name 是可选的,gate_type 为前面列出的某种门类型。各 term 用于表示与门的输入/输出端口相连的线网或寄存器。同一门类型的多个实例能够在一个结构形式中定义。语法如下:

代码语言:javascript
代码运行次数:0
复制
gate_type
[instance_name1] (term11, term12, . . .,term1N ) ,
[instance_name2] (term21, term22, . . .,term2N ) ,
. . .
[instance_nameM] (termM1, termM2, . . .,termMN)

下面是一个用结构化建模方式实现的多路选择电路的例子,如图 2 所示。

图 2 多路选择电路

多路选择电路如果用结构化建模方式实现,代码如下:

代码语言:javascript
代码运行次数:0
复制
module MUX4x1 (Z , D0 , D1 , D2 , D3 , S0 , S1) ;

  input D0 , D1 , D2 , D3 , S0 , S1;
  
  output Z;
  
  and (T0 , D0 , S01 , S11) ,
      (T1 , D1 , S01, S1) ,
      (T2 , D2 , S0 , S11) ,
      (T3 , D3 , S0 , S1) ,
      not (S01, S0) ,
      (S11 , S1) ;
      or (Z , T0 , T1 , T2 , T3) ;
    
endmodule

4.2 数据流建模方式

Verilog HDL 中的数据流建模方式一般用连续赋值语句来实现。Verilog HDL 中有两种形式的赋值方式:连续赋值和过程赋值。其中过程赋值用于顺序行为建模,而组合逻辑电路的行为最好使用连续赋值语句建模。

连续赋值语句将值赋给线网(连续赋值不能为寄存器赋值),它的格式如下:

代码语言:javascript
代码运行次数:0
复制
assign LHS_target = RHS_expression;//定义格式
//例子
wire [3:0] Z, Preset, Clear; //线网说明
assign Z = Preset & Clear; //连续赋值语句

连续赋值的目标为 Z,表达式右端为“Preset & Clear”,连续赋值语句中的关键词为assign。只要在右端表达式的操作数上有事件(事件为值的变化)发生时,连续赋值语句即被计算,如果结果值有变化,新结果就赋给左边的线网。在上面的例子中,如果 Preset 或 Clear变化,就计算右边的整个表达式。如果结果变化,那么结果即赋值到线网 Z。

如图 3 所示的是主从触发器。

图 3 主从触发器

主从触发器用连续赋值语句实现的代码如下:

代码语言:javascript
代码运行次数:0
复制
module MSDFF_DF (D, C, Q, Qbar) ;

    input D, C;
    output Q, Qbar;
    
    wire NotC, NotD, NotY, Y, D1, D2, Ybar, Y1, Y2 ;
    
    assign NotD = ~ D;
    assign NotC = ~ C;
    assign NotY = ~ Y;
    assign D1 = ~ (D & C) ;
    assign D2 = ~ (C & NotD) ;
    assign Y = ~ (D1 & Ybar ) ;
    assign Ybar = ~ (Y & D2) ;
    assign Y1 = ~ (Y & NotC ) ;
    assign Y2 = ~ (NotY & NotC) ;
    assign Q = ~ (Qbar & Y1) ;
    assign Qbar = ~ (Y2 & Q) ;
    
endmodule

4.3 行为建模方式

行为建模方式是用过程赋值语句来实现的。下面对行为建模方式的各个部分进行详细介绍。

1)过程结构

Verilog HDL 中的主要行为通过两种语句来控制进行:

• initial 语句;

• always 语句。

initial 语句在模拟开始时执行,即在 0 时刻开始执行。initial 语句只执行一次,它的语法如下:

代码语言:javascript
代码运行次数:0
复制
initial
[timing_control] procedural_statement

这里的时序控制可以是延时控制,即等待一个确定的时间;或事件控制,即等待确定的事件发生或某一特定的条件为真。initial 语句的各个进程语句仅执行一次。initial 语句根据进程语句中出现的时间控制在以后的某个时间完成执行。下面是一个 initial 语句实例:

代码语言:javascript
代码运行次数:0
复制
  parameter SIZE = 1024;
  
  reg [7:0] RAM [ 0:SIZE- 1 ] ;
  reg RibReg;
  
  initial
  begin: SEQ_BLK_A
    integer Index;
      RibReg = 0;
    for (Index = 0; Index < SIZE; Index = Index + 1)
      RAM[Index] = 0;
  End

与 initial 语句相反,always 语句可重复执行。与 initial 语句类似,always 语句语法如下:

代码语言:javascript
代码运行次数:0
复制
always
[timing_control]procedural_statement

下面是一个 always 语句的实例。

代码语言:javascript
代码运行次数:0
复制
    reg [0:5] InstrReg;
    reg [3:0] Accum;
    
    wire ExecuteCycle;
    
    always@ (EcecuteCycle) //发生在某个时钟沿
    begin
        case(InstrReg[ 0 : 1 ] )//多路条件分支
            2'b00: Store (Accum, InstrReg[2:5]) ;//存储
            2'b11: Load (Accum, InstrReg[2:5]) ;//读取
            2'b01: Jump (InstrReg[ 2 : 5 ] ) ;//跳转
            2'b10: ;
        endcase
    end

2)时序控制

Verilog HDL 中进行时序控制分别通过下面两种方式进行:

• 延时控制;

• 事件控制。

延时控制的语法如下:

代码语言:javascript
代码运行次数:0
复制
#delay procedural_statement

延时控制定义为执行过程中首次遇到该语句与该语句的执行的时间间隔。延时控制表示在语句执行前的“等待时延”。下面是一个延时控制的例子:

代码语言:javascript
代码运行次数:0
复制
initial
begin
  #3 Wave = 'b0111;//3 个时间单位后执行
  #6 Wave = 'b1100; //6 个时间单位后执行
  #7 Wave = 'b0000; //7 个时间单位后执行
end

事件控制有两种方式:边沿触发事件控制和电平敏感事件控制。边沿触发事件是指指定信号的边沿发生跳变时发生指定的行为,下面是边沿触发事件控制的语法和实例:

代码语言:javascript
代码运行次数:0
复制
  @ event procedural_statement
  //实例
  time RiseEdge, OnDelay;
  
  initial
  begin
      //等待,直到在时钟上发生正边沿:
      @ (posedge ClockA) ;
      RiseEdge = $time;
      //等待,直到在时钟上发生负边沿:
      @ (negedge ClockA) ;
      OnDelay = $time - RiseEdge;
      $display ("The on-period of clock is %t.", Delay) ;
  end

在电平敏感事件控制中,进程语句或进程中的过程语句一直延迟到条件变为真后才执行。下面是电平敏感事件控制的语法和实例:

代码语言:javascript
代码运行次数:0
复制
wait (Condition)
procedural_statement
//实例
wait (Sum > 22) //直到 Sum 大于 22 时发生
Sum = 0;
wait (DataReady) //直到 DataReady 为高时发生
Data = Bus;
wait (Preset) ; //直到 Preset 为高时才能执行后面的语句

3)语句块

Verilog HDL 在执行语句时分为顺序和并行两种方式。在顺序语句块中,语句按给定次序顺序执行;在并行语句块中,语句并行执行。

顺序语句块的语法和实例如下:

代码语言:javascript
代码运行次数:0
复制
  begin
    [:block_id{declarations} ]
    procedural_statement(s)
  end
  //实例
  //产生波形
  begin
    #2 Stream = 1;
    #5 Stream = 0;
    #3 Stream = 1;
    #4 Stream = 0;
    #2 Stream = 1;
    #5 Stream = 0;
  end

并行语句块的语法和实例如下:

代码语言:javascript
代码运行次数:0
复制
  fork
    [:block_id{declarations} ]
    procedural_statement(s) ;
  join
  //实例
  //生成波形,生成的波形和前面使用顺序语句块的例子一样
  fork
    #2 Stream = 1;
    #7 Stream = 0;
    #10 Stream = 1;
    #14 Stream = 0;
    #16 Stream = 1;
    #21 Stream = 0;
  join

4)过程性赋值

过程性赋值是在 initial 语句或 always 语句内的赋值,它只能对寄存器数据类型的变量赋值。过程性赋值如下两类:

• 阻塞性过程赋值:赋值在其后所有语句执行前执行,即在下一语句执行前该赋值语句完成执行;

• 非阻塞性过程赋值:对目标的赋值是非阻塞的(因为时延),但可预定在将来某个时间发生。

阻塞性过程赋值用操作符“=”完成,例如下面的实例:

代码语言:javascript
代码运行次数:0
复制
  always @ (A or B or Cin)
  begin: CARRY_OUT
        reg T1,T2 , T3;
        T1 = A & B;
        T2 = B & Ci n;
        T3 = A & Ci n;
        Cout = T1|T2|T3;
  end

T1 赋值首先发生,计算 T1;接着执行第二条语句,T2 被赋值;然后执行第三条语句, T3被赋值,依此类推直到最后。

非阻塞性过程赋值用操作符“<=”完成,例如下面的实例:

代码语言:javascript
代码运行次数:0
复制
  initial
  begin
    Clr <= #5 1;
    Clr <= #4 0;
    Clr <= #10 0;
  End

第一条语句的执行使 Clr 在第 5 个时间单位被赋于值 1;第二条语句的执行使 Clr 第 4 个时间单位被赋值为 0(从 0 时刻开始的第 4 个时间单位);最终第 3 条语句的执行使 Clr 在第10 个时间单位被赋值为 0(从 0 时刻开始的第 1 0 个时间单位)。这 3 条语句都是在 0 时刻执行的。

5)流程控制语句

流程控制语句包括:

• if 语句;

• case 语句;

• 循环语句。

if 语句的语法如下:

代码语言:javascript
代码运行次数:0
复制
if(condition_1)
procedural_statement_1
{else if(condition_2)
procedural_statement_2}
{else
procedural_statement_3}

如果对 condition_1 求值的结果为一个非零值,那么 procedural_statement_1 被执行,如果 condition_1 的值为 0、x 或 z,那么 procedural_statement_1 不执行。如果存在一个 else分支,那么这个分支被执行。

下面是一个 if 语句的例子:

代码语言:javascript
代码运行次数:0
复制
  if(Sum < 60)
    begin
      Grade = C;
      Total_C = Total_c + 1;
    end
  else if(Sum < 75)
    begin
      Grade = B;
      Total_B = Total_B + 1;
    end
  else
    begin
      Grade = A;
      Total_A = Total_A + 1;
    End

case 语句是一个多路条件分支形式,其语法如下:

代码语言:javascript
代码运行次数:0
复制
case(case_expr)
case_item_expr{ ,case_item_expr} :procedural_statement
. . .
. . .
[default:procedural_statement]
endcase

case 语句首先对条件表达式 case_expr 求值,然后依次对各分支项求值并进行比较,第一个与条件表达式值相匹配的分支中的语句被执行。可以在 1 个分支中定义多个分支项,这些值不需要互斥。缺省分支覆盖所有没有被分支表达式覆盖的其他分支。

下面是 case 语句的一个实例:

代码语言:javascript
代码运行次数:0
复制
  always @ (A or B or OpCode)
  case (OpCode)
    ADD_INSTR: Z = A + B;
    SUB_INSTR: Z = A -B;
    MULT_INSTR: Z = A * B;
    DIV_INSTR: Z = A / B;
  endcase
  
endmodule

Verilog HDL 中提供 4 种循环语句:forever 循环,repeat 循环,while 循环和 for 循环。

forever 循环语句连续执行过程语句。因此为跳出这样的循环,中止语句可以与过程语句共同使用。同时,在过程语句中必须使用某种形式的时序控制,否则 forever 循环将在 0 延时后永远循环下去。forever 循环语句语法和实例如下:

代码语言:javascript
代码运行次数:0
复制
  forever
  procedural_statement
  //实例
  initial
  begin
      Clock = 0;
      # 5 forever
      #10 Clock = ~Clock;
  end

这一实例产生时钟波形:时钟首先初始化为 0,并一直保持到第 5 个时间单位;此后每隔10 个时间单位,时钟反相一次。

repeat 循环语句执行指定循环次数,如果循环计数表达式的值不确定,即为 x 或 z 时,那么循环次数按 0 处理。下面是 repeat 循环语句语法和实例如下:

代码语言:javascript
代码运行次数:0
复制
repeat(loop_count)
procedural _ statement
//实例
repeat(Count)
@ (posedge Clk) Sum = Sum + 1;//在时钟上升沿时每次加一

while 循环执行过程赋值语句直到指定的条件为假。如果表达式在开始时为假,那么过程语句便永远不会执行。如果条件表达式为 x 或 z,它也同样按 0(假)处理。

下面时 while 循环的语法和实例:

代码语言:javascript
代码运行次数:0
复制
    while(condition)
    procedural_statement
    //实例,一直执行直到 BY 小于等于 0
    while (BY > 0 )
      begin
        Acc = Acc << 1;
        By = By - 1;
      End

for 循环照指定的次数重复执行过程赋值语句。下面是 for 循环的语法和实例:

代码语言:javascript
代码运行次数:0
复制
  for(initial_assignment ; condition ; step_assignment)
  procedural_statement
  //实例中,MAX_RANGE 为循环计次的上限,每次循环加一
  for (K=0 ; K < MAX_RANGE ; K = K + 1)
    begin
      if(Abus[K] == 0)
        Abus[K] = 1;
      else if(Abus[k] == 1)
        Abus[K] = 0;
      else
        $display( "Abus[K] is an x or a z");
    end
 end

关于详细的VHDL语法以及Verilog HDL语法可参见往期文章。

一周掌握 FPGA VHDL Day 7 暨汇总篇

一周掌握FPGA Verilog HDL语法 汇总篇

END

后续会持续更新,带来Vivado、 ISE、Quartus II 、candence等安装相关设计教程,学习资源、项目资源、好文推荐等,希望大侠持续关注。

大侠们,江湖偌大,继续闯荡,愿一切安好,有缘再见!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FPGA技术江湖 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档