Verilog HDL 入门
Verilog 文法:https://github.com/antlr/grammars-v4/blob/master/verilog/verilog/VerilogParser.g4
基础
常量
整数常量
18'hFF // 8 位十六进制常量
28'd255 // 8 位十进制常量
38'o377 // 8 位八进制常量
48'b11111111 // 8 位二进制常量
实数常量
11.0 // 十进制实数常量,将转换为整数 1
20.8 // 十进制实数常量,将转换为整数 1
实数通过四舍五入隐式地转换为最相近的整数
字符串常量
1"Hello World" // 将被转换成 ASCII 码序列 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100
注释
1// 单行注释
2/* 多行注释 */
类型
net 类型
net 表示物理连接,对应电路中的硬连线。位宽 1, 无符号。
具体类型
1net_type
2 : 'supply0' // 一个逻辑零电源(供应电源),用于强制将连续赋值的信号线上的值保持为逻辑零
3 | 'supply1' // 一个逻辑一电源(供应电源),用于强制将连续赋值的信号线上的值保持为逻辑一
4 | 'tri' // 三态信号线,可以具有高阻态,也可以由多个模块驱动
5 | 'triand' // 三态与门,用于将多个三态信号线的值进行与操作
6 | 'trior' // 三态或门,用于将多个三态信号线的值进行或操作
7 | 'tri0' // 三态信号线,其值可以是逻辑零、高阻态或由多个模块驱动
8 | 'tri1' // 三态信号线,其值可以是逻辑一、高阻态或由多个模块驱动
9 | 'uwire' // 未驱动连续赋值信号线,只能被一个单一的源所驱动,不能被多个模块或电路连接
10 | 'wire' // 普通连续赋值信号线,可以连接多个模块和逻辑电路,但不能驱动 reg 型的寄存器
11 | 'wand' // 三态与非门,用于将多个三态信号线的值进行与非操作
12 | 'wor' // 三态或非门,用于将多个三态信号线的值进行或非操作
13 ;
uwire wire 有什么不同?
在Verilog中,wire
和uwire
都是用于建立电路中的信号线(wire)的关键字,但它们有以下不同:
-
wire
关键字表示的是一个普通的连续赋值的信号线,它可以连接多个模块和逻辑电路,但不能驱动reg
型的寄存器。而uwire
关键字则表示的是一个未驱动的连续赋值的信号线,它只能被一个单一的源所驱动,不能被多个模块或电路连接,也不能驱动寄存器。 -
wire
关键字可以用来连接多个模块或电路,而uwire
关键字不能。这是因为uwire
关键字只能被一个单一的源所驱动,因此不能被多个模块或电路所连接。 -
wire
关键字通常用于建立连续赋值的信号线,而uwire
关键字则更适合用于建立连续赋值的单一源信号线。
在实际设计中,uwire
关键字很少使用,因为它的应用场景较为有限,通常只在特殊情况下使用。大多数情况下,wire
关键字可以满足设计需求。
variable 类型
variable 表示存储器,对应电路中的寄存器。
具体类型
在计算机编程中,variable(变量)是指一个存储数据值的内存位置,可以通过变量名来引用和访问该内存位置中存储的数据。在编程语言中,变量通常需要先声明并指定其类型,以便在程序运行时为其分配内存空间。
在 Verilog 中,变量(variable)也有类似的概念,不过其具体的类型和用法与其他编程语言可能有所不同。下面是 Verilog 中常见的一些变量类型:
-
reg
(寄存器):用于存储逻辑电路中的状态变量,可以通过赋值操作进行更新。其用法类似于其他编程语言中的变量,但不同的是,在 Verilog 中,reg
类型的变量可以不仅可以存储数字和布尔值,还可以存储多位二进制数(也可以存储 ASCII 字符)。 -
integer
(整数):用于存储整数类型的变量,可以进行加减乘除等基本算术运算。其范围为 -2^31 到 2^31 - 1。 -
real
(实数):用于存储浮点数类型的变量,可以进行基本的算术运算和一些特殊的数学函数运算。其精度和范围可能因实现而异。 -
time
(时间):用于存储时间类型的变量,可以表示电路中的时间间隔,通常以纳秒为单位。在仿真过程中,时间变量可以用于控制模拟过程的进行。 -
event
(事件):用于在电路中表示事件,可以通过事件触发器(Event Trigger)来控制模块的行为。 -
parameter
(参数):用于在模块内部定义常量,其值在模块实例化时被确定。在模块中,参数可以用于配置模块的行为。
除了上述的变量类型之外,Verilog 中还有其他一些变量类型,如bit
、byte
、logic
等,它们的具体用法和特点可能略有不同。在实际设计中,可以根据需要选择和使用适当的变量类型来实现设计。
向量
向量用于表示多位的二进制数。向量的位宽表示形式为:[MSB : LSB]
,其中 MSB 表示最高有效位(Most Significant Bit),LSB 表示最低有效位(Least Significant Bit)。
例子:
1wire [3:0] a; // 表示一个4位的二进制数,且从高位到低位排列。
2reg [0:7] b; // 表示一个8位的二进制数,且从低位到高位排列。
向量的类型
-
标量类向量。类似 C 中的一维数组。
1reg scalared [3:0] a; // 标量类向量
-
向量类向量。类似 C 中的多维数组。
1reg vectored [3:0][3:0] a; // 向量类向量,表示一个4x4的二维数组 2logic [1:0] [1:0] matrix [1:2]; // 向量类向量,表示一个3x2x2的三维数组
上面定义中,
matrix
是一个 3 位的向量类向量,包含三个元素,每个元素都是一个 2x2 的矩阵,其中matrix[1]
表示第一个矩阵,matrix[1][0]
表示第一个矩阵的第一行,第一个元素,即matrix[1][0][0]
表示第一个矩阵的左上角的元素。
运算符
算术运算符
运算符 | 说明 |
---|---|
+ | 加法 |
- | 减法 |
* | 乘法 |
/ | 除法 |
% | 取模 |
逻辑运算符
运算符 | 说明 |
---|---|
&& | 逻辑与 |
|| | 逻辑或 |
! | 逻辑非 |
位运算符
运算符 | 说明 |
---|---|
& | 位与 |
| | 位或 |
^ | 位异或 |
~ | 位取反 |
« | 位左移 |
» | 位右移 |
~^ | 位同或 |
比较运算符
运算符 | 说明 |
---|---|
== | 等于 |
=== | 全等于 |
!= | 不等于 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
注:===
运算符用于比较两个变量的值和类型是否相等,==
运算符用于比较两个变量的值是否相等。
缩位运算符
缩位运算符用于将一个向量的高位截断,只保留低位。
运算符 | 说明 |
---|---|
& | 与 |
~& | 与非 |
| | 或 |
~| | 或非 |
^ | 异或 |
~^ | 同或 |
三目运算符
三目运算符用于对两个表达式进行条件判断,如果条件为真,则返回第一个表达式的值,否则返回第二个表达式的值。
1condition ? expr1 : expr2
位拼接运算符
位拼接运算符用于将两个向量进行拼接,拼接后的向量的位宽为两个向量的位宽之和。
1{expr1, expr2, expr3, ...}
位选择运算符
位选择运算符用于从一个向量中选择一部分位,选择后的向量的位宽为选择的位数。
1expr[MSB:LSB]
语句
过程语句
initial 语句
initial
语句用于在仿真开始时对模块中的变量进行初始化。不能被综合。
1initial begin
2 // 初始化语句
3end
always 语句
always
语句用于描述模块中的时序逻辑。会不断执行。
1always @(<event>)
2begin
3 // 时序逻辑
4end
其中,<event>
表示事件触发器,用于指定时序逻辑的触发条件。可以看作一个依赖列表,当列表中的变量发生变化时,时序逻辑会被执行。(有点像 React 中的 useEffect)
例如:
-
always @(in1 or in2)
表示当in1
或in2
发生变化时,时序逻辑会被执行。 -
always @(posedge clk)
表示当clk
从低电平变为高电平时,时序逻辑会被执行。 -
always @(negedge clk)
表示当clk
从高电平变为低电平时,时序逻辑会被执行。 -
always @(*)
表示当模块中的任意变量发生变化时,时序逻辑会被执行。
赋值语句
assign
语句是持续赋值语句,用于描述模块中的组合逻辑。可以看作连线操作。
而对 reg 变量赋值,需要使用过程赋值语句,有以下两种形式:
-
<=
:非阻塞赋值语句,只能确保整个所在语句块结束时完成赋值。不保证立刻完成。可以并发执行。 -
=
:阻塞赋值语句,会立刻完成赋值。只能顺序执行。
条件语句
if 语句
1if (condition)
2 statements
3else if (condition)
4 statements
5else
6 statements
case 语句
普通 case 语句
1case (expression)
2 expression: statements
3 expression: statements
4 default: statements
casez 语句
casez
语句会忽略 Z 的位值。
1casez (expression)
2 expression: statements
3 expression: statements
4 default: statements
casex 语句
casex
语句会忽略 X 的位值。
循环语句
for 语句
1for (initialization; condition; step)
2 statements
例子:
1for (i = 0; i < 10; i = i + 1)
2 statements
repeat 语句
1repeat (expression)
2 statements
forever 语句
会一直执行。
1forever
2 statements
预处理
`define 宏定义
1`define 宏名 宏值
如果要在代码中引用宏,需要在宏名前加上一个反引号 `。
1`宏名
`ifdef 宏判断
1`ifdef 宏名
2 statements
3`endif
4
5`ifndef 宏名
6 statements
7`else
8 statements
9`endif
`include 文件包含
1`include "文件名"
需要注意 `include 不能被综合。
仿真控制
系统函数
$display
1$display("字符串", 表达式, ...)
用于在控制台输出信息。
$monitor
1$monitor("字符串", 表达式, ...)
用于在控制台输出信息,但是会在每个时钟周期都输出。
$time
1$display("%d", $time)
用于获取当前仿真时间。以纳秒为单位。
$finish
1$finish
用于结束仿真。
$stop
1$stop
用于暂停仿真。
$readmemh
1$readmemh("文件名", 变量名)
用于从文件中读取数据,存储到变量中。
文件格式:
1@地址 数据
2@地址 数据
3...
文件例子:
1@0 0
2@1 1
3@2 2
数据采用十六进制表示。每行默认是一个 32 位的数据。
如果地址从 0 开始,可以省略地址。
10
21
$writememh
1$writememh("文件名", 变量名)
用于将变量中的数据写入到文件中。