Verilog 语言入门教程
第一章:初识 Verilog
1 什么是 Verilog?
想象一下,传统电路设计是工程师拿着铅笔和图纸,在面包板上焊接成千上万个电子元件,这个过程非常耗时且容易出错。

Verilog 是一种用于硬件描述语言,你可以把它理解为“电路的编程语言”,它不是用来写软件程序(比如计算器、游戏),而是用来描述数字电路的结构和行为。
- 类比:
- C/C++ 语言:描述计算机的软件行为(执行计算、控制流程)。
- Verilog 语言:描述数字电路的硬件行为(信号如何传输、逻辑如何运算)。
通过 Verilog,我们可以用代码的形式来设计一个复杂的芯片(CPU、内存控制器),然后通过工具(EDA工具)自动将代码转换为实际的电路图,并最终制造出芯片,这个过程被称为RTL(Register-Transfer Level,寄存器传输级)设计。
2 Verilog 的应用领域
Verilog 广泛应用于:
- ASIC (专用集成电路) 设计:为特定功能定制的芯片。
- FPGA (现场可编程门阵列) 开发:可反复编程的逻辑芯片,常用于原型验证、通信、图像处理等领域。
- 数字电路教学:是学习数字逻辑和计算机体系结构的必备工具。
第二章:第一个 Verilog 程序:与门
让我们从一个最简单的数字逻辑元件——与门 开始。

1 与门的行为
与门有两个输入和一个输出,其逻辑功能是:只有当两个输入都为 1 时,输出才为 1。
| 输入 A | 输入 B | 输出 Y |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
2 Verilog 代码实现
下面是用 Verilog 描述的与门代码:
// 文件名: and_gate.v
// 1. 模块定义:这是 Verilog 的基本构建块
// module <模块名> (<端口列表>);
module and_gate (
input a, // 定义一个名为 a 的输入端口
input b, // 定义一个名为 b 的输入端口
output y // 定义一个名为 y 的输出端口
);
// 2. 逻辑描述:这里使用 assign 语句来持续地描述输入和输出的关系
// assign <输出信号> = <逻辑表达式>;
assign y = a & b; // & 是 Verilog 中的按位与操作符
// 3. 模块定义结束
endmodule
3 代码解析
module ... endmodule:所有的 Verilog 代码都必须包含在一个module块中。module就像是一个“黑盒子”,它定义了电路的对外接口(输入、输出)。input a, b:声明a和b是输入端口,它们可以接收外部的信号。output y:声明y是输出端口,它将向外部输出信号。assign y = a & b;:这是最核心的部分。assign关键字表示这是一个持续赋值语句,这意味着只要a或b发生变化,y的值就会立即重新计算并更新,这完美地模拟了硬件中导线的连接关系。&是 Verilog 的按位与操作符。a & b就实现了与门的逻辑功能。
第三章:Verilog 的核心语法要素
1 模块
我们已经见过模块了,它是 Verilog 的基本单元。
module my_circuit (
input in1, in2,
output out1
);
// 内部逻辑描述
endmodule
2 信号类型
除了 input 和 output,还有一种在模块内部使用的信号,叫做 wire(线网)。

wire:它代表物理上的“一根导线”,它不能存储值,只能传递信号。assign语句的输出默认就是wire类型。wire temp_signal; // 定义一个 wire 类型的内部信号 assign temp_signal = a ^ b; // temp_signal 的值由 a 和 b 的异或结果决定
reg:代表“寄存器”,它可以存储值,在always块(后面会讲)中赋值的变量必须是reg类型,它不一定对应于物理上的触发器,但通常用于时序逻辑。reg counter; // 定义一个 reg 类型的变量,可以用来计数
3 操作符
Verilog 提供了丰富的操作符,类似于 C 语言。
| 类别 | 操作符 | 描述 | 示例 |
|---|---|---|---|
| 算术 | , , , | 加、减、乘、除 | assign c = a + b; |
| 逻辑 | &&, \\|\\|, |
逻辑与、或、非 | assign y = enable && (data == 8'hFF); |
| 按位 | &, \\|, ^, |
按位与、或、异或、取反 | assign y = a & b; (与门) |
| 关系 | >, <, >=, <= |
大于、小于等 | assign flag = (a > b); |
| 相等 | , | 相等、不等 | assign match = (data == pattern); |
| 缩减 | &, \\|, ^ |
将向量缩减为一位(与、或、异或) | assign all_ones = &{4'b1111}; // 结果为 1 |
4 常量和变量
- 常量:
- 二进制:
4'b1010(4位二进制数 1010) - 十进制:
16(默认是32位) - 十六进制:
8'hFF(8位十六进制数 FF)
- 二进制:
- 变量:
reg [7:0] my_byte;// 定义一个8位的 reg 类型变量wire [3:0] four_bits;// 定义一个4位的 wire 类型变量
第四章:更复杂的结构:always 块
assign 语句非常适合组合逻辑(门电路),但对于时序逻辑(如触发器、寄存器),我们需要使用 always 块。
always 块描述的是在特定条件下发生的行为。
1 D 触发器
D 触发器是最基本的时序元件,它在时钟的上升沿,将输入 D 的值“锁存”到输出 Q。
| 时钟 Clk | 输入 D | 输出 Q (上一时钟沿的值) |
|---|---|---|
| ↑ | 0 | (保持) |
| ↑ | 1 | (保持) |
Verilog 实现:
// 文件名: d_flip_flop.v
module d_flip_flop (
input clk, // 时钟信号
input rst_n, // 异步复位信号,低电平有效
input d, // 数据输入
output reg q // 数据输出,必须是 reg 类型,因为它在 always 块中被赋值
);
// always @(<敏感事件列表>):当列表中的事件发生时,块内的代码被执行
always @(posedge clk or negedge rst_n) begin
// @posedge clk:在 clk 的上升沿触发
// @negedge rst_n:在 rst_n 的下降沿触发(异步复位优先级更高)
if (!rst_n) begin
// 如果复位信号有效 (rst_n == 0)
q <= 1'b0; // 将 q 复位为 0
end else begin
// 否则,在时钟上升沿
q <= d; // 将 d 的值赋给 q
end
end
endmodule
2 代码解析
always @(posedge clk or negedge rst_n):这是always块的触发条件。posedge clk:表示当clk信号从 0 变为 1(上升沿)时,执行begin...end中的代码。negedge rst_n:表示当rst_n信号从 1 变为 0(下降沿)时,也执行代码,这种“或”关系意味着两个事件任意一个发生都会触发。
if (!rst_n):rst_n是低电平有效复位,所以当rst_n为 0 时,!rst_n为真,执行复位操作。q <= d;:<=是非阻塞赋值操作符,这是在always块中描述时序逻辑的标准方式。- 阻塞赋值 :立即执行,像 C 语言的赋值。
- 非阻塞赋值
<=:在当前时间步结束时才执行赋值,这确保了所有右侧表达式先计算完毕,然后左侧的变量才同时更新,避免了仿真时的竞争问题,是编写可综合时序逻辑的关键。
第五章:模块实例化
当你的设计变得复杂时,你会将大功能拆分成多个小模块,然后将这些小模块像搭积木一样组合起来,这个过程就是模块实例化。
1 实例化一个与门
假设我们有一个 and_gate 模块,我们想在另一个模块 top_module 中使用它。
// 文件名: top_module.v
// 假设我们已经定义了 and_gate.v
module top_module (
input in1, in2, in3,
output out
);
// 1. 声明内部 wire
wire w1;
// 2. 实例化 and_gate 模块
// <实例名> <模块名> ( .<端口名>(<连接信号>), ... );
and_gate u_and_gate1 (
.a(in1), // 将 u_and_gate1 的 a 端口连接到 in1
.b(in2), // 将 u_and_gate1 的 b 端口连接到 in2
.y(w1) // 将 u_and_gate1 的 y 端口连接到内部信号 w1
);
// 3. 再实例化一个 and_gate
and_gate u_and_gate2 (
.a(w1), // 将第一个与门的输出作为第二个与门的输入
.b(in3),
.y(out) // 最终输出
);
endmodule
这段代码描述了一个电路:out = (in1 & in2) & in3,通过实例化,我们轻松地复用了 and_gate 模块。
第六章:测试平台 - 验证你的设计
写好了代码,怎么知道它是对是错?我们需要一个测试平台来模拟输入信号,并观察输出是否符合预期。
测试平台本身也是一个 Verilog 模块,但它通常不会被综合成硬件,只用于仿真。
1 为与门创建测试平台
// 文件名: tb_and_gate.v
`timescale 1ns / 1ps // 定义时间尺度,1纳秒为基本单位,精度1皮秒
module tb_and_gate();
// 1. 定义内部信号,用于连接被测模块
reg a_in, b_in;
wire y_out;
// 2. 实例化被测模块
// 因为是测试,我们不需要输入输出端口,所以直接连接内部信号
and_gate uut ( // uut 是 Unit Under Test 的缩写
.a(a_in),
.b(b_in),
.y(y_out)
);
// 3. 生成激励信号
initial begin // initial 块中的代码从仿真开始时执行一次
// 打印仿真开始信息
$display("Simulation Start");
// 测试用例 1: a=0, b=0
a_in = 0; b_in = 0;
#10; // 延时 10 个时间单位 (10ns)
$display("a=0, b=0, y=%b", y_out); // 打印输出结果
// 测试用例 2: a=0, b=1
a_in = 0; b_in = 1;
#10;
$display("a=0, b=1, y=%b", y_out);
// 测试用例 3: a=1, b=0
a_in = 1; b_in = 0;
#10;
$display("a=1, b=0, y=%b", y_out);
// 测试用例 4: a=1, b=1
a_in = 1; b_in = 1;
#10;
$display("a=1, b=1, y=%b", y_out);
// 结束仿真
$display("Simulation End");
$finish; // 调用 $finish 结束仿真
end
endmodule
2 测试平台代码解析
initial块:initial块在仿真开始时执行一次,非常适合用于生成测试激励。#10:表示延时 10 个时间单位(根据timescale定义,这里是 10ns)。$display:类似于 C 语言的printf,用于在仿真控制台打印信息。$finish:结束当前仿真。
运行这个测试平台,你会在控制台看到与门真值表对应的输出,从而验证你的设计是正确的。
第七章:总结与下一步
1 本章要点回顾
- Verilog 是硬件描述语言,用于设计数字电路。
module是基本单元,定义了电路的接口。assign用于描述组合逻辑(如门电路)。always块用于描述时序逻辑(如触发器),使用posedge/negedge检测时钟边沿。- 非阻塞赋值
<=是编写时序逻辑的标准。 - 模块实例化 是模块化设计的基础。
- 测试平台 是验证设计正确性的必要工具。
2 推荐学习路径
- 掌握基础语法:熟练使用
assign,always,wire,reg, 各种操作符。 - 学习常用电路:用 Verilog 实现各种组合逻辑(多路选择器、编码器、译码器)和时序逻辑(计数器、移位寄存器、状态机)。
- 学习仿真工具:学会使用 ModelSim、Vivado Simulator 或 Quartus Prime 等工具进行代码仿真和波形查看。波形调试是数字设计的核心技能。
- 学习综合工具:了解 Synplify、Vivado、Quartus 等综合工具如何将 Verilog 代码转换为门级网表。
- 学习 FPGA 开发:选择一块开发板(如 Xilinx Artix-7 或 Intel Cyclone IV),学习如何将综合后的设计下载到 FPGA 中,并使用板载资源(LED、按键、七段管)进行验证。
3 推荐资源
- 书籍:
- 《Verilog HDL高级数字设计》 - Ashenden (经典,内容深入)
- 《数字设计和计算机体系结构》 - Harris (结合了 Verilog 和计算机体系结构,非常适合入门)
- 在线课程:
- Coursera / edX:搜索 "Digital Logic Design" 或 "Computer Architecture" 相关课程。
- YouTube:有很多优秀的 Verilog 教学频道。
- 工具:
- 仿真器:ModelSim (工业标准), Xilinx Vivado Simulator (免费)
- FPGA 开发套件:Xilinx Vivado (免费版), Intel Quartus Prime (免费版)
希望这份入门教程能帮助你顺利开启 Verilog 的学习之旅!祝你学习愉快!
