文档
Verilog / SystemVerilog 数字设计入门
本章目标
从零开始理解数字电路设计思想,掌握可综合 Verilog 编码规范,完成 FPGA 基础实验。
1. 硬件描述语言的思维方式
软件 vs 硬件
| 概念 | 软件 (C/Python) | 硬件 (Verilog) |
|---|---|---|
| 执行 | 顺序执行 | 并发执行 |
| 变量 | 按时间变化 | 代表物理连线 |
| 循环 | 重复执行指令 | 展开为重复电路 |
| "运行" | CPU 执行指令 | 电路持续工作 |
关键转变
不要写代码,要描述电路。你写的每行 Verilog 都应能在脑中想象出对应的逻辑门/触发器。
2. 组合逻辑 vs 时序逻辑
组合逻辑
// 纯组合:输出仅取决于当前输入
assign sum = a + b;
always @(*) begin
case (sel)
2'b00: out = a;
2'b01: out = b;
2'b10: out = c;
default: out = 0;
endcase
end
时序逻辑
// 时序:输出取决于时钟沿
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
count <= 0;
else if (enable)
count <= count + 1;
end
3. 可综合编码规范
✅ 可以综合
// 阻塞赋值用于组合逻辑
always @(*) y = a & b;
// 非阻塞赋值用于时序逻辑
always @(posedge clk) q <= d;
// 有限状态机
always @(posedge clk) state <= next_state;
❌ 不可综合
#10 clk = ~clk; // 延时
$display("hello"); // 系统任务
fork ... join // 不可综合的并发
initial begin ... end // 仅仿真
4. 状态机设计
Moore 型(输出仅取决于状态)
typedef enum logic [1:0] {
IDLE, WORKING, DONE
} state_t;
state_t state = IDLE, next_state;
// 状态转移
always_comb begin
next_state = state;
case (state)
IDLE: if (start) next_state = WORKING;
WORKING: if (finish) next_state = DONE;
DONE: if (ack) next_state = IDLE;
endcase
end
// 输出逻辑
always_comb begin
busy = (state == WORKING);
done = (state == DONE);
end
5. 仿真测试平台
module tb;
reg clk = 0, rst_n = 0;
reg start = 0;
wire busy, done;
// DUT 例化
my_module uut (.clk, .rst_n, .start, .busy, .done);
always #5 clk = ~clk; // 100MHz
initial begin
$dumpfile("tb.vcd");
$dumpvars(0, tb);
#10 rst_n = 1;
#10 start = 1;
#10 start = 0;
// 等待 done
@(posedge done);
$display("Test passed at t=%0t", $time);
#50 $finish;
end
endmodule
6. FPGA 开发流程
设计输入 (Verilog)
→ 行为仿真 (iverilog/Modelsim)
→ 综合 (Yosys/Vivado)
→ 布局布线 (nextpnr/Vivado)
→ 时序仿真
→ 比特流生成
→ FPGA 烧录
→ 板级验证
思考题
- 解释
blockingvsnon-blocking赋值在综合后的差异 - 什么是亚稳态?如何避免?
- 为什么 FPGA 设计中需要约束文件(.xdc/.sdc)?