极术小能手 · 2022年03月17日

【课程实验附录】 Verilog HDL基本语法

由于本实验的硬件设计部分使用 Verilog HDL 编写, 考虑到很多同学之前并没有接触过 Verilog 或者是使用的 VHDL 语言进行设计, 故本章主要目的是帮助同学们熟悉 Verilog HDL 硬件描述语言以及两个常用的模块: 使能信号和总线译码器, 从而更好的完成本节课的实验.

DL (hardware description language), 指出了我们学习 Verilog 的关键: 硬件描述. 要时刻牢记, Verilog 是硬件描述语言, 不要用执行软件的思路来理解学习 Verilog 代码.

常量类型

整数

  • 二进制整数 : b 或 B 表示
  • 十进制整数 : d 或 D 表示
  • 十六进制整数 : h 或 H 表示
  • 八进制整数 : o 或 O 表示

整数的表述方法一般有以下几种:

1.<位宽> <进制> <数字>方法,

例如想要表示整数 22, 位宽定义为 8, 则用二进制表示为 8'b00010110或8'b0001_0110; 用十进制表示为 8'd22; 用 16 进制表示为 8'h16; 用 8进制表示为 8'o26.

2.<进制> <数字> 方法, 一般来说, 这样表示默认位宽为32位.

仍然拿整数 22 举例, 进制为 16 进制, 则可以表示为 'h16. 与 32'h16等价.

3.<数字> 方法. 一般来说, 这样定义整数时, 默认进制为10, 默认位宽为32位.

  • 参数

定义参数时, 需要使用关键字 parameter 声明, 且只能赋值一次. 用法为 parameter 常量 = 整数.

例如定义整个模块中数据位宽为 32, 则 parameter data_width = 10'd32;

变量类型

  • net型

wire: 表示一般的连接线

tri: 表示电路连接是三态类型的, 使用时与 wire 等价, 可以增强可读性

  • variable型

reg: 默认位宽为 1 位的变量, 默认是无符号数
integer: 默认位宽为 32 位的变量, 通常用于描述整数参数.
变量定义的方法为 <变量类型> <位宽> <变量名称>.

例如定义一个 reg 变量, 位宽 2, 命名为 a, 则为 reg [1:0] a.

对于一个模块来说,输入端口可以由 reg / net 驱动, 但是输入端口本身只能是 net; 输出端口则可以是 reg / net 类型, 但是输出端口只能驱动 net 型. 下面举一个例子


                                 +------------+
           a-------------------> A           Y>--------> y
                                 |            |
                                 |            |
           b-------------------> B            |
                                 +------------+

对于上图这个模块来说, a, b 是外部输入给模块的, 所以 a, b 可以为 reg / net 型; 而对于模块内部定义的输入端口 A, B 来说, 必须是 wire 类型. 同样的, 模块内部定义的 Y 输出端口来说, 可以为 reg / net 型; 但是它只能驱动 net 型.

运算符

  • 算数运算符 : +、-、* 、/、%

算术运算符中, 进行乘法/除法运算时, 结果要略去小数部分,只取整数部分; 进行取模运算时, 可以使用 % 求余数, 结果的符号位采用模运算符中第一个操作数的符号.

例如, -10 % 3 结果为 -1, 11 % -3 结果为 2. 另外, 在进行算数运算时, 某一个操作数有不确定的值 X, 则整个运算的结果也为不确定的 X.

  • 按位运算符 : &、|、~ 、^(异或) 、~^(同或)

按位运算符要求对两个操作数的相应位数逐位进行计算,

例如 0101 & 1001 = 0001, 1100 & 1001 = 1000.

  • 缩位运算符 : &、|、~ 、^ 、~^、 ~&

缩位运算符作用于一个多位的变量, 作用是将多位变量的每一位进行逻辑运算.

如一个四位的变量 b, c = &b的含义为 c = (((b[0] & b[1]) & b[2]) & b[3]).

  • 逻辑运算符 : &&、||、!
逻辑运算符只区分真假, 而不管什么数值. 例如, 逻辑运算中, 输入 4'ha1 和 4'h01 是没有区别的, 都表示逻辑真; 而 0 为逻辑假. 所以一般来说, 逻辑运算的结果要么为真, 要么为假. 特例是如果有一个输入为未知 X, 那么结果也是 X.
  • 关系运算符 : <=、=>、<、>
相等运算符 : == 、! =、!==(不全等)、 ==\= (全等)
相等运算符中的 !== 和 ==\= 常用于 case 表达式的判别, 如果操作数中存在 Z 或者 X, 那么操作数必须完全相同结果才为 0/1, 否则为 1/0.
  • 移位运算符 : <<(左移)、>>(右移)

使用移位运算符时, a >> n 中 a 代表要进行右移的操作数, n 代表右移的位数; 左移同理. 移位产生的空位用 0 来填补.

例如 a 是一个 5 位的寄存器, 若 a = 4'b1001 << 1, 则 a 的结果为 5'b10010, 但如果 a 是一个 4 位的寄存器, 仍然对 a 做上述操作, 则 a 的结果为 4'b0010.

  • 拼接运算符 : { }

拼接运算符可以将变量任意组合后输出给另一个变量. 用法: {信号 1 的某几位, 信号2 的某几位, …… , 信号 n 的某几位}

例如: {a, b[3:0], w, 3'b101} 等同于 {a, b[3], b[2], b[1], b[0], w ,1b'1, 1'b0, 1'b1}, {4{w}} 等同于 {w,w,w,w}, 需要注意 4 这个位置必须是常量表达式.

  • 条件运算符 : ? :
条件运算符中的 ? 实现的是多路复用器, 用法为 assign wire变量 = 条件 ? 表达式 1: 表达式 2; 例如下面这个式子: assign out = sel ? in1 : in0; 描述了当 sel 为 1 的时候, out = in1, sel 为 0 的时候, out = in0.

和 C 语言一模一样?
是的, 你很敏锐, 在功能上运算符是和 c 语言类似的, 但是需要注意的是, Verilog 是硬件描述语言, 这其中每种运算符实际上都与电路对应. 如 + 运算符对应加法器, & 运算符对应与门, 等等.

表达式和操作数
表达式由操作符和操作数构成,其目的是根据操作符的意义得到一个计算结果。表达式可以在出现数值的任何地方使用。

操作数可以是任意的数据类型,只是某些特定的语法结构要求使用特定类型的操作数。操作数可以为常数,整数,实数,线网,寄存器,时间,位选,域选,存储器及函数调用等。

常用语句

  • if-esle 语句
  • case 语句
  • always 语句
  • assign 语句

这里着重介绍 always 语句和 assign 语句.

always 语句几乎等同于 VHDL 中的 process 语句, 区别在于在 always 语句中描述上升沿时, 可以直接使用 always (posedge clk) 描述.

assign 语句用于给 wire 型变量赋值, 使用方法为 assign wire型变量 = 常数.

例如给位宽为 4 的 data 变量赋值 10, 则为assign data = 4'd10;

请不要整活
也许你还看到了其他的一些语句, 诸如 for, function ,while 之类, 不要在设计过程中使用它们, 这会导致无法综合, 这些语句只是用来仿真的.

赋值

  • 阻塞赋值
  • 非阻塞赋值

阻塞赋值为顺序赋值, 下一条语句执行前, 当前语句一定会执行完成, 阻塞赋值使用 = 作为赋值符;

非阻塞赋值为并行赋值, 下一条语句和当前语句的执行时同时进行的, 非阻塞赋值使用 <= 作为赋值符.

赋值和电路的关系
描述组合逻辑电路时, 需要使用阻塞赋值, 描述时序逻辑电路时, 需要使用非阻塞赋值. 当使用 assign 语句时, 一定要使用阻塞赋值, 当使用 always 语句时, 阻塞赋值和非阻塞赋值均可使用.

question
使用条件运算符时, 可以使用非阻塞赋值吗?

END

文章来源:

推荐内容

更多内容请关注微处理器系统结构与嵌入式系统设计专栏
推荐阅读
关注数
117
内容数
20
电子科技大学示范性微电子学院开设的「微处理器系统结构与嵌入式系统设计」课程配套实验,原链接:[链接]
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息