Verilog HDL 综合教程
目录
-
第一部分:入门基础
(图片来源网络,侵删)- 什么是 Verilog HDL?
- 为什么学习 Verilog HDL?
- 硬件描述语言 vs. 软件编程语言
- 开发流程:从代码到硬件
- 第一个程序:
Hello, World!的 Verilog 版本
-
第二部分:Verilog 基础语法
- 模块
- 端口
- 数据类型
- 操作符
- 基本语句:
assign和always
-
第三部分:行为建模与结构建模
- 结构建模:像搭积木一样设计
- 行为建模:像写 C 语言一样描述功能
always块详解:@posedge,@negedge,begin...end和fork...join- 条件语句:
if-else和case - 循环语句:
for,while,repeat,forever
-
第四部分:高级主题与关键概念
reg和wire的深入理解- 时序逻辑 vs. 组合逻辑
- 阻塞赋值 () vs. 非阻塞赋值 (
<=) initial块:用于测试和仿真- 参数化设计:
parameter和localparam - 任务 (
task) 和函数 (function)
-
第五部分:测试平台
(图片来源网络,侵删)- 什么是测试平台?
- 如何编写一个简单的测试平台
- 生成激励信号
- 使用
$display,$monitor进行调试
-
第六部分:实践项目与进阶
- 基本逻辑门
- 组合逻辑电路 - 多路选择器
- 时序逻辑电路 - D 触发器
- 计数器
- 状态机
- 进阶方向:SystemVerilog, UVM, 时序分析
-
第七部分:学习资源与工具推荐
第一部分:入门基础
什么是 Verilog HDL?
Verilog HDL (Hardware Description Language) 是一种用于电子系统级 和寄存器传输级 设计的硬件描述语言,它允许设计师用文本代码来描述数字逻辑电路的结构和行为,就像用 C 语言描述软件算法一样。
为什么学习 Verilog HDL?
- 抽象层次高:相比于手动绘制成千上万个逻辑门电路图,使用 HDL 可以在更高层次上进行设计,大大提高了设计效率和可读性。
- 可移植性强:同一份 Verilog 代码可以被综合成不同厂商(如 Intel/Altera, Xilinx, Lattice)的 FPGA 器件,或者用于 ASIC 芯片流片。
- 自动化设计流程:通过 EDA (Electronic Design Automation) 工具,可以自动完成从代码到电路网表、布局布图的复杂过程。
- 行业标准:Verilog 和 VHDL 是数字 IC 设计和 FPGA 开发领域的两大主流语言,是相关岗位的必备技能。
硬件描述语言 vs. 软件编程语言
| 特性 | Verilog HDL | 软件编程语言 (如 C/C++) |
|---|---|---|
| 执行模型 | 并行执行:所有语句在仿真时同时执行。 | 顺序执行:语句按顺序一行一行执行。 |
| 赋值操作 | 非阻塞赋值 (<=):更新发生在仿真时间步的末尾,影响的是“下一个”状态。 |
赋值 ():立即生效。 |
| 变量类型 | wire (物理连接), reg (存储单元) |
int, char, float 等 |
| 目标 | 描述硬件电路的结构和时序。 | 描述算法和软件行为。 |
开发流程:从代码到硬件
一个典型的 Verilog 设计流程如下:

- 设计:使用 Verilog 编写代码,描述电路功能。
- 仿真:编写测试平台,验证代码功能是否正确,这一步称为功能仿真,不考虑硬件延迟。
- 综合:使用综合工具(如 Vivado, Quartus, Synopsys DC)将 Verilog 代码转换成由基本逻辑门(与、或、非、触发器等)构成的网表。
- 实现:针对特定 FPGA 器件,进行布局布线,将网表映射到具体的硬件资源上。
- 时序仿真:考虑实际硬件的延迟,再次进行仿真,验证电路在高速下是否能正常工作。
- 下载/编程:将最终配置文件下载到 FPGA 芯片中,硬件开始工作。
第一个程序:Hello, World! 的 Verilog 版本
在软件中,"Hello, World!" 是打印一句话,在 Verilog 中,我们通过仿真在控制台打印一句话来验证环境。
// 这是一个注释,类似于 C++ 的 //
// `timescale 1ns / 1ps 定义仿真的时间尺度
// 1ns 为基本时间单位,1ps 为精度
`timescale 1ns / 1ps
module hello_world;
// initial 块中的代码从仿真时间 0 开始执行一次
initial begin
// $display 是一个系统任务,用于在控制台打印信息
// \n 表示换行
$display("Hello, Verilog World!\n");
// $finish 是一个系统任务,用于结束仿真
$finish;
end
endmodule
第二部分:Verilog 基础语法
模块
模块是 Verilog 的基本构成单元,它代表一个具有特定功能的硬件电路,比如一个加法器、一个 FIFO 或一个 CPU。
module module_name (
// 端口列表
input wire port1,
output reg port2,
inout port3
);
// 内部信号和变量声明
wire internal_signal;
// 逻辑描述
assign internal_signal = port1;
always @(*) begin
port2 = internal_signal;
end
endmodule
端口
端口是模块与外部世界的接口,分为三种:
input:输入端口,信号从外部流入模块。output:输出端口,信号从模块内部流向外部。inout:双向端口,信号可以双向流动。
数据类型
-
wire:最常用的类型,表示物理上的连线,它不能存储值,只能被连续赋值语句(如assign)或模块实例的输出驱动。wire没有被驱动,其值为高阻态z。wire a, b, c; assign c = a & b; // c 是 a 和 b 的与
-
reg:听起来像寄存器,但它的含义是“可以保持值的变量”,它只能在always块或initial块中被赋值,它不一定是物理寄存器,综合工具会根据上下文判断它最终是组合逻辑还是时序逻辑。reg out; always @(*) begin out = in1 ^ in2; // out 是 in1 和 in2 的异或 end
操作符
Verilog 提供了丰富的操作符,与 C 语言非常相似。
- 算术操作符:, , , ,
- 位操作符: (按位取反),
&(按位与), (按位或),^(按位异或) - 逻辑操作符:
&&(逻辑与), (逻辑或), (逻辑非) - 关系操作符:
>,<,>=,<= - 相等操作符: (逻辑相等, 包含 x,z), (全等, 严格比较)
- 移位操作符:
<<(左移),>>(右移) - 拼接操作符:,将多个信号拼接成一个宽信号。
{a, b, c} // 将 a, b, c 拼接成一个更宽的信号
基本语句:assign 和 always
-
assign(连续赋值):用于描述组合逻辑,只要右边的表达式发生变化,左边的wire就会立即被重新赋值。assign out = in1 & in2; // 一个与门
-
always(过程块):用于描述更复杂的逻辑,包括组合逻辑和时序逻辑,它需要一个事件控制表达式(如 或@posedge clk)来触发块内的代码执行。// 描述一个组合逻辑 always @(*) begin // @(*) 表示对括号内所有信号的变化敏感 out = in1 ^ in2; end // 描述一个时序逻辑 (D触发器) always @(posedge clk) begin // 只在 clk 的上升沿触发 q <= d; // 非阻塞赋值,用于时序逻辑 end
第三部分:行为建模与结构建模
结构建模
通过实例化已经存在的模块来构建更大的电路,就像搭乐高积木。
// 假设我们已经有一个 2 输入与门模块
module and_gate (input a, input b, output y);
assign y = a & b;
endmodule
// 现在我们用两个 and_gate 模块搭建一个半加器
module half_adder (
input a, b,
output sum, cout
);
wire w1;
// 实例化第一个与门
and_gate g1 (.a(a), .b(b), .y(w1));
// 实例化第二个与门
and_gate g2 (.a(a), .b(b), .y(cout));
// ... 异或逻辑可以用 assign 实现
assign sum = a ^ b;
endmodule
行为建模
通过高级语言结构(如 if-else, case, for 循环)来描述电路的功能,而不关心其内部的具体门级实现,综合工具会将其“翻译”成实际的硬件电路。
always 块详解
- 隐式敏感列表,自动包含块内所有用到的输入信号,这是描述组合逻辑的标准方式。
@posedge clk:对时钟的上升沿敏感,这是描述时序逻辑(触发器、寄存器)的标准方式。@negedge clk:对时钟的下降沿敏感。
begin...end 和 fork...join
begin...end:顺序块,块内的语句按顺序执行(在仿真时间上)。fork...join:并行块,块内的语句同时开始执行。
initial begin
$display("Sequential block starts.");
#10 $display("Time 10");
#20 $display("Time 30"); // 总共在 30ns 时执行
end
initial fork
$display("Parallel block starts.");
#10 $display("Time 10");
#20 $display("Time 20"); // 总共在 20ns 时执行,因为两个事件并行开始
join
条件语句:if-else 和 case
-
if-else:用于描述多路选择逻辑。always @(*) begin if (sel == 2'b00) out = a; else if (sel == 2'b01) out = b; else out = c; end -
case:当条件是多个互斥的值时,case语句更清晰。always @(*) begin case (sel) 2'b00: out = a; 2'b01: out = b; 2'b10: out = c; default: out = 1'b0; // default 是一个好习惯 endcase end
循环语句:for, while, repeat, forever
这些语句主要用于测试平台,在 RTL 设计中很少使用(因为硬件中不存在真正的“循环”概念,而是通过流水线或状态机来实现类似功能)。
// 在测试平台中生成一个时钟 initial begin clk = 0; forever #5 clk = ~clk; // 每 5 个时间单位翻转一次 clk end
第四部分:高级主题与关键概念
reg 和 wire 的深入理解
wire:代表物理连接,是数据流的通道,它必须被驱动(通过assign或另一个模块的输出),如果未被驱动,则为高阻z。reg:代表一个可以存储值的存储单元,它的值只能在always或initial块中改变,综合工具会根据always块的敏感列表来决定它最终是组合逻辑()还是时序逻辑(@posedge clk)。
时序逻辑 vs. 组合逻辑
- 组合逻辑:输出只取决于当前的输入,没有记忆功能,与门、或门、多路选择器。
- Verilog 描述:
assign语句或always @(*)块。
- Verilog 描述:
- 时序逻辑:输出不仅取决于当前的输入,还取决于电路之前的状态(即存储的值),有记忆功能,触发器、寄存器、计数器。
- Verilog 描述:
always @(posedge clk)或always @(negedge clk)块。
- Verilog 描述:
阻塞赋值 () vs. 非阻塞赋值 (<=)
这是 Verilog 中最重要、最容易出错的概念。
| 特性 | 阻塞赋值 () | 非阻塞赋值 (<=) |
|---|---|---|
| 执行方式 | 顺序执行,先执行完当前 always 块内的所有 赋值,再执行下一句。 |
并行执行。always 块内所有 <= 赋值语句的右侧表达式被同时计算,然后同时更新到左侧的寄存器。 |
| 用途 | 组合逻辑,描述组合逻辑的中间变量传递。 | 时序逻辑,描述在时钟边沿发生的、并行的状态更新。 |
| 比喻 | C 语言的赋值。 | 多个工人同时根据同一张图纸(当前状态)建造房子(下一状态)。 |
| 规则 | 在 always @(*) 中使用。 |
在 always @(posedge clk) 中使用。 |
错误示例(混合使用):
// 这是一个错误的写法!会导致仿真和综合结果不一致 always @(posedge clk) begin q = d; // 使用了阻塞赋值 q_bar = ~q; // 这里的 q 已经是 d 的值了,而不是旧的 q 值 end
正确示例(时序逻辑):
// 正确的 D 触发器写法 always @(posedge clk) begin q <= d; // 非阻塞赋值 q_bar <= ~d; // d 是当前输入,q 和 q_bar 都基于 d 的当前值更新 end
initial 块
initial 块中的代码从仿真时间 0 开始执行一次,然后就结束了,它主要用于测试平台的初始化和产生激励信号,不能被综合成实际的硬件电路。
参数化设计
使用 parameter 可以让模块更具通用性。
module adder #(
parameter WIDTH = 8 // 默认位宽为 8
) (
input [WIDTH-1:0] a, b,
output [WIDTH:0] sum // 和可能多一位
);
assign sum = a + b;
endmodule
// 实例化一个 16 位的加法器
adder #(.WIDTH(16)) my_adder (...);
任务 (task) 和函数 (function)
用于将代码模块化,提高可读性和复用性。
task:可以有输入、输出和双向端口,可以消耗时间(包含 延迟或 事件控制)。function:只有一个返回值,不能包含时间控制,不能调用task。
第五部分:测试平台
测试平台是验证 RTL 代码正确性的环境本身也是一个 Verilog 模块,但它通常不会被综合到硬件中。
// 待测试的模块
module simple_counter (
input clk,
input reset,
output reg [3:0] count
);
always @(posedge clk or posedge reset) begin
if (reset)
count <= 4'b0;
else
count <= count + 1;
end
endmodule
// 测试平台
module tb_simple_counter;
// 定义信号
reg clk, reset;
wire [3:0] count;
// 实例化待测模块
simple_counter uut (
.clk(clk),
.reset(reset),
.count(count)
);
// 生成时钟
initial begin
clk = 0;
forever #5 clk = ~clk; // 10ns 的时钟周期
end
// 生成激励
initial begin
// 初始化
reset = 1;
#20; // 等待 20ns
// 释放复位
reset = 0;
// 运行一段时间观察结果
#100;
// 再次复位
reset = 1;
#20;
// 结束仿真
$finish;
end
// 监控信号变化
initial begin
$monitor("Time = %0t, Reset = %b, Count = %d", $time, reset, count);
end
endmodule
第六部分:实践项目与进阶
基本逻辑门
- 目标:实现与门、或门、非门、异或门。
- 方法:使用
assign语句。
组合逻辑电路 - 多路选择器
- 目标:实现一个 4 选 1 的多路选择器。
- 方法:使用
assign和条件操作符 或case语句。
时序逻辑电路 - D 触发器
- 目标:实现一个带异步复位的 D 触发器。
- 方法:使用
always @(posedge clk or posedge reset)块和非阻塞赋值。
计数器
- 目标:实现一个模 10 的计数器(0-9)。
- 方法:在 D 触发器的基础上,增加反馈逻辑。
状态机
这是数字设计的核心。
- 目标:实现一个简单的交通灯控制器。
- 方法:
- 定义状态:
S_RED,S_GREEN,S_YELLOW。 - 使用
parameter或localparam为状态编码。 - 使用
always @(posedge clk)块描述状态寄存器(状态转移)。 - 使用
always @(*)或assign描述输出逻辑。
- 定义状态:
进阶方向
- SystemVerilog:Verilog 的超集,增加了面向对象编程、随机化、断言等强大功能,是现代 IC 和 FPGA 设计的主流语言。
- UVM (Universal Verification Methodology):基于 SystemVerilog 的标准化验证方法学,用于构建大型、可复用的验证平台。
- 时序分析:学习静态时序分析,理解建立时间、保持时间、时钟偏斜等概念,确保设计在目标频率下稳定工作。
第七部分:学习资源与工具推荐
工具推荐
- 仿真器:
- 免费:IVERILOG (GTKWave 配合查看波形), ModelSim (Intel Quartus/Xilinx Vivado 自带免费版)。
- 商业:VCS, ModelSim (高级版)。
- FPGA 开发套件:
- Xilinx (AMD): Vivado (用于高端 Artix/Kintex 系列), Vitis (用于 Zynq MPSoC)。
- Intel (Altera): Quartus Prime。
- Lattice: Diamond。
- 入门级 FPGA 板卡:Xilinx Basys 3, Digilent Arty Z7, Intel DE10-Lite。
书籍推荐
- 《Verilog HDL: A Guide to Digital Design and Synthesis》by Samir Palnitkar:经典入门书籍,讲解清晰,实例丰富。
- 《Digital Design and Computer Architecture》by David Harris and Sarah Harris:从数字逻辑基础讲到计算机体系结构,并用 Verilog 实现,非常适合系统学习。
- 《Advanced Chip Design, Practical Examples in Verilog》by Kishore Mishra:更偏向实践,包含了许多工业界的经验和技巧。
在线资源
- Nandland:提供大量关于 Verilog 和 FPGA 的入门教程和项目,非常适合初学者。
- FPGA4Student:网站上有丰富的 Verilog/VHDL 代码示例和教程。
- Coursera / edX:搜索 "Digital Logic Design" 或 "Computer Architecture" 等课程,通常会用 Verilog 作为实验语言。
- GitHub:搜索关键词如 "verilog projects", "verilog tutorial",可以找到大量开源项目和代码。
祝你学习顺利!从写一个简单的门电路开始,逐步挑战更复杂的项目,你很快就能掌握 Verilog HDL 的精髓。
