Verilog 速查表
最后更新
模块结构与端口
模块是最基本的构建单元,它有一个名字和一个端口列表。
| 语法 | 含义 |
|---|---|
module adder(...); ... endmodule | 定义一个模块及其主体 |
input clk; | 输入端口 |
output q; | 输出端口 |
output reg q; | 由 always 块驱动的输出 |
input [7:0] data; | 8 位输入总线(向量) |
inout sda; | 双向端口 |
adder u1 (.a(x), .b(y), .sum(s)); | 按端口名例化一个模块 |
数据类型
wire 表示一条连线;reg 保存由过程代码赋的值。
| 元素 | 作用 |
|---|---|
wire w; | 由 assign 或模块输出驱动的线网 |
reg r; | 保存在 always/initial 块中赋的值 |
wire [3:0] bus; | 4 位向量(从第 3 位到第 0 位) |
reg [7:0] count; | 8 位寄存器 |
bus[2] | 选取单个位 |
bus[3:1] | 选取一段连续的位(部分选择) |
reg mem [0:255]; | 存储器:由寄存器组成的数组 |
parameter WIDTH = 8; | 模块中的命名常量 |
数字字面量
格式为 <size>'<base><value>:size 是位宽,base 可以是 b/o/d/h。
| 语法 | 含义 |
|---|---|
4'b1010 | 4 位二进制,值为 10 |
8'hFF | 8 位十六进制,值为 255 |
8'hff | 同上,十六进制数字不区分大小写 |
16'd255 | 16 位十进制,值为 255 |
3'o7 | 3 位八进制,值为 7 |
8'b1010_1100 | 用下划线给数字分组,便于阅读 |
1'bx | 未知值(x) |
1'bz | 高阻态(z) |
运算符
按位、逻辑、比较和归约运算符。
| 运算符 | 作用 |
|---|---|
& | ^ ~ | 按位与、或、异或、非 |
&& || ! | 逻辑与、或、非(结果为 1 位) |
== != < > <= >= | 比较运算符 |
<< >> | 左移和右移 |
+ - * / % | 算术运算符 |
&bus | 归约与(所有位相与) |
|bus | 归约或(所有位相或) |
^bus | 归约异或(所有位的奇偶校验) |
{a, b} | 位拼接;{4{1'b1}} 复制得到 4'b1111 |
连续赋值
assign 持续驱动一个 wire,用来描述组合逻辑。
| 语法 | 含义 |
|---|---|
assign y = a & b; | 用 a 和 b 相与的结果驱动 y |
assign sum = a + b; | 持续计算两数之和 |
assign y = sel ? a : b; | 三目运算:一个二选一多路选择器 |
assign {c, s} = a + b; | 同时得到进位和和 |
assign y = ~en; | 用取反后的信号驱动 wire |
过程块
always 和 initial 用来执行语句,敏感列表决定何时执行。
| 语法 | 含义 |
|---|---|
always @(*) begin ... end | 组合逻辑(任一输入变化时执行) |
always @(posedge clk) | 每个时钟上升沿执行(时序逻辑) |
always @(negedge clk) | 每个时钟下降沿执行 |
always @(posedge clk or posedge rst) | 带异步复位的时钟块 |
initial begin ... end | 在 0 时刻执行一次(测试平台,仅限仿真) |
begin ... end | 把多条语句组合在一起 |
#10 | 延迟 10 个时间单位(仅限仿真) |
阻塞与非阻塞赋值
组合逻辑用 =,时钟驱动的时序逻辑用 <=。
| 语法 | 含义 |
|---|---|
a = b; | 阻塞赋值:按顺序立即执行 |
a <= b; | 非阻塞赋值:先读取所有右侧表达式,再统一赋值 |
always @(*) y = a & b; | 组合逻辑:用阻塞赋值 = |
always @(posedge clk) q <= d; | 触发器:用非阻塞赋值 <= |
在同一个块中混用 = 和 <= | 不要这样做,这是常见的 bug 来源 |
控制流
过程块中的条件判断和多路分支。
| 语法 | 含义 |
|---|---|
if (cond) ... else ... | 条件语句 |
if (a) x = 1; else x = 0; | 单行 if/else |
case (sel) ... endcase | 根据一个值进行多路分支 |
2'b00: y = a; | 一个 case 分支 |
default: y = 0; | 默认分支 |
for (i = 0; i < 8; i = i + 1) | 循环(常用于 generate 或仿真中) |
repeat (4) @(posedge clk); | 将一条语句重复 N 次 |
常用写法
反复会用到的基础电路,以及测试平台中的常用任务。
| 写法 | 语法 |
|---|---|
| D 触发器 | always @(posedge clk) q <= d; |
| 带复位的寄存器 | always @(posedge clk) if (rst) q <= 0; else q <= d; |
| 计数器 | always @(posedge clk) count <= count + 1; |
| 状态机的状态寄存器 | always @(posedge clk) state <= next_state; |
| 状态机的次态逻辑 | always @(*) case (state) ... endcase |
| 打印一个值 | $display("q = %b", q); |
| 值变化时打印 | $monitor("t=%0t q=%b", $time, q); |
| 结束仿真 | $finish; |
常用的 Verilog 语法都在这一页。这份 Verilog 速查表是这门硬件描述语言的快速参考:声明模块和端口、选择数据类型、编写组合逻辑和时序逻辑,以及描述真实电路的 always 块写法。
这里的语法是标准 Verilog(IEEE 1364),可以在 Icarus Verilog、Verilator 等常用仿真器中使用。需要什么就直接复制,也可以在 Verilog 在线编辑器里动手试试:写一个模块,运行仿真,在浏览器里直接看到输出。
Verilog 速查表常见问题
这份 Verilog 速查表免费吗?
免费。这份 Verilog 速查表完全免费,无需注册。收藏这一页,需要查语法、运算符或 always 块写法时随时回来看。
wire 和 reg 有什么区别?
wire 表示一条物理连线,必须被持续驱动(由 assign 语句或模块输出驱动),所以用于组合逻辑的线网。reg 会一直保持它的值,直到过程代码修改它,所以凡是在 always 或 initial 块中赋值的信号都用 reg。虽然名字叫 reg,但它不一定会综合成硬件寄存器,它只表示这个值是通过过程语句赋的。阻塞赋值和非阻塞赋值有什么区别?
=)像普通的程序语句一样,按顺序立即执行,所以在 always @(*) 块中描述组合逻辑时使用它。非阻塞赋值(<=)先计算所有右侧表达式,再同时更新所有左侧变量,能正确地描述触发器,所以在时钟驱动的 always @(posedge clk) 块中使用它。在同一个块中混用两者,是仿真 bug 的经典来源。可以在线练习 Verilog 吗?
可以。打开 Verilog 在线编辑器,写一个模块,运行仿真,就能在浏览器里看到输出,无需安装任何软件。想系统学习的话,Coddy 免费的互动式 Verilog 课程会一步步带你从模块和数据类型学到 always 块和有限状态机。
这份速查表适合初学者吗?
适合。它从基础内容(模块、端口和数据类型)开始,一直讲到时序逻辑和状态机写法,你可以从上面的部分学起,逐步学会描述真实的电路。