Quick Start
- 步骤1:安装环境 下载并安装 Vivado 2025.2(或更高版本),确保包含仿真器(XSIM)。
- 步骤2:创建工程 打开 Vivado → Quick Start → Create Project → 选择器件(如 xc7a35tcsg324-1)→ Finish。
- 步骤3:编写组合逻辑模块 新建 Design Sources → 写一个 2 选 1 多路选择器(见下方代码)。
- 步骤4:仿真验证组合逻辑 新建 Simulation Sources → 写 testbench → Run Simulation → 观察波形确认输出正确。
- 步骤5:加入时序逻辑 在同一个模块中增加一个 D 触发器输出,形成“组合+时序”混合设计。
- 步骤6:综合与实现 点击 Synthesis → Run Synthesis → 查看资源报告;再 Run Implementation → 查看时序报告(WNS ≥ 0)。
- 步骤7:上板验证(可选) 生成 Bitstream → 下载到 FPGA 开发板,观察 LED 响应按键/拨码开关。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| FPGA 器件 | Xilinx Artix-7 (xc7a35tcsg324-1) | 其他 7 系列或 Spartan-7;Altera Cyclone V 也可(需对应 Quartus) | — |
| EDA 版本 | Vivado 2025.2(示例) | Vivado 2024.x / 2026.x;Vitis 仅用于嵌入式,非必需 | — |
| 仿真器 | Vivado XSIM(内置) | ModelSim / Questa / Verilator(需额外配置) | — |
| 时钟/复位 | 板载 50 MHz 有源晶振;低电平复位(按键) | 可改用 PLL 分频;高电平复位需调整逻辑 | — |
| 接口依赖 | 至少 2 个拨码开关(输入)、1 个 LED(输出) | 无板卡时仅做仿真 | — |
| 约束文件 | XDC 文件:定义时钟周期(create_clock -period 20.0 [get_ports clk]) | 无约束时综合仍可运行,但时序分析不准确 | — |
目标与验收标准
- 功能点:实现一个 2 选 1 多路选择器(组合逻辑),其输出经过 D 触发器寄存(时序逻辑),最终驱动 LED。
- 性能指标:系统时钟频率 ≥ 50 MHz(即周期 20 ns),建立时间余量(WNS)≥ 0。
- 资源占用:LUT ≤ 4 个,FF ≤ 2 个(典型值;实际因综合策略略有差异)。
- 仿真验收:testbench 中,输入变化后组合输出立即响应,时序输出在时钟上升沿后一个时钟周期才更新。
- 上板验收:拨码开关改变输入,LED 在下一个时钟沿反映新值,无毛刺或闪烁。
实施步骤
阶段一:工程结构与顶层模块
在 Vivado 中创建工程,添加一个顶层模块 top_mux_dff,包含时钟 clk、复位 rst_n、两个输入 a、b、选择信号 sel、输出 led。端口方向:clk、rst_n、a、b、sel 为 input;led 为 output reg(因为时序逻辑赋值)。常见坑:忘记将 led 声明为 reg 类型会导致综合错误;时钟和复位必须显式连接到顶层端口。
阶段二:组合逻辑模块(多路选择器)
// 2-to-1 multiplexer (combinational)
module mux2to1 (
input wire a,
input wire b,
input wire sel,
output wire y
);
assign y = sel ? b : a;
endmodule逐行说明
- 第 1 行:注释,说明模块功能为 2 选 1 多路选择器,纯组合逻辑。
- 第 2 行:模块声明,名称为 mux2to1,与文件名建议一致。
- 第 3–5 行:端口声明,全部为 input wire,因为组合逻辑输入不需要存储。
- 第 6 行:输出 y 声明为 output wire,因为使用 assign 连续赋值,wire 类型即可。
- 第 7 行:assign 语句实现选择:若 sel=1 则输出 b,否则输出 a。这是典型的组合逻辑描述,综合成 LUT。
- 第 8 行:模块结束。
阶段三:时序逻辑模块(D 触发器)
// D flip-flop with asynchronous reset (sequential)
module dff (
input wire clk,
input wire rst_n,
input wire d,
output reg q
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0;
else
q <= d;
end
endmodule逐行说明
- 第 1 行:注释,说明模块为 D 触发器,采用异步复位(原始注释有误,此处更正)。
- 第 2 行:模块名 dff。
- 第 3–6 行:端口声明,时钟和复位为 input,数据 d 为 input,输出 q 为 output reg(因为时序逻辑在 always 块中赋值)。
- 第 7 行:always @(posedge clk or negedge rst_n) 敏感列表,时钟上升沿或复位下降沿触发,这是异步复位写法。
- 第 8–11 行:if (!rst_n) 判断复位有效(低电平),将 q 清零;否则在时钟上升沿将 d 赋值给 q。使用非阻塞赋值 <= 是时序逻辑的标准写法,避免竞争。
- 第 12 行:模块结束。
阶段四:顶层模块(组合 + 时序)
// Top module: mux output -> DFF input
module top_mux_dff (
input wire clk,
input wire rst_n,
input wire a,
input wire b,
input wire sel,
output reg led
);
wire mux_out;
// Instantiate mux
mux2to1 u_mux (
.a(a),
.b(b),
.sel(sel),
.y(mux_out)
);
// Instantiate DFF
dff u_dff (
.clk(clk),
.rst_n(rst_n),
.d(mux_out),
.q(led)
);
endmodule逐行说明
- 第 1 行:注释,说明顶层将多路选择器输出连接到 DFF 输入。
- 第 2 行:模块名 top_mux_dff。
- 第 3–8 行:端口声明,led 为 output reg 因为顶层内部例化的 DFF 输出是 reg,但顶层端口可以声明为 wire 然后通过 assign 连接;此处直接 reg 更简单。
- 第 9 行:内部连线 mux_out,wire 类型,连接 mux 输出与 DFF 输入。
- 第 11–16 行:例化 mux2to1,使用命名端口连接。
- 第 18–23 行:例化 dff,将 mux_out 作为数据输入,led 作为输出。
- 第 24 行:模块结束。
阶段五:约束与综合
创建 XDC 文件,添加时钟约束:create_clock -period 20.0 [get_ports clk]。运行综合,检查资源报告:LUT 应约 2–3 个,FF 约 1 个。常见坑:未添加时钟约束时,时序分析会使用默认频率(可能不准确);综合后务必查看“Report Timing Summary”确认 WNS。
阶段六:仿真验证
// Testbench for top_mux_dff
module tb_top_mux_dff;
reg clk, rst_n, a, b, sel;
wire led;
top_mux_dff uut (
.clk(clk),
.rst_n(rst_n),
.a(a),
.b(b),
.sel(sel),
.led(led)
);
initial begin
clk = 0;
forever #10 clk = ~clk; // 50 MHz clock
end
initial begin
rst_n = 0;
#25 rst_n = 1;
#10 a = 1; b = 0; sel = 0; // select a -> led should become 1 after clock edge
#20 sel = 1; // select b -> led should become 0 after clock edge
#20 $finish;
end
initial begin
$monitor("Time=%0t, sel=%b, a=%b, b=%b, led=%b", $time, sel, a, b, led);
end
endmodule逐行说明
- 第 1 行:注释,testbench 模块。
- 第 2 行:模块名 tb_top_mux_dff。
- 第 3–4 行:声明输入激励为 reg,输出为 wire。
- 第 6–13 行:例化待测模块。
- 第 15–17 行:生成 50 MHz 时钟,每 10 ns 翻转一次。
- 第 19–24 行:复位序列:先拉低 25 ns,然后释放;接着改变输入,观察输出。
- 第 26–28 行:$monitor 打印变化,便于调试。
关键验收:仿真波形中,led 应在时钟上升沿后变化,且滞后于 mux_out 一个时钟周期。
原理与设计说明
为什么需要区分组合逻辑和时序逻辑?
组合逻辑的输出只取决于当前输入,没有记忆功能,适合做数据选择、译码等。时序逻辑的输出依赖于时钟沿和内部状态,可以存储数据、实现状态机。在 FPGA 中,组合逻辑由查找表(LUT)实现,时序逻辑由触发器(FF)实现。将组合逻辑的输出直接连接到触发器的输入,可以消除组合逻辑的毛刺,提高系统稳定性——这是本设计的核心思想。
Trade-off 分析
如果全部用组合逻辑,延迟小但易受毛刺干扰;全部用时序逻辑,面积大且延迟增加。本设计采用“组合+时序”混合,在关键路径上插入寄存器(流水线思想),平衡了吞吐与延迟。对于初学者,理解这个平衡是进阶的关键。
验证与结果
| 指标 | 测量值(示例) | 条件 |
|---|---|---|
| Fmax | ≥ 200 MHz | Vivado 2025.2,Artix-7 -1 速度等级,无时序违例 |
| LUT 使用 | 2 个 | 仅 mux 逻辑 |
| FF 使用 | 1 个 | 仅 D 触发器 |
| 组合延迟 | 约 0.5 ns | 从输入到 mux 输出 |
| 时钟到输出 | 约 1.2 ns | 从时钟沿到 led 输出 |
注:以上数值为典型配置下的示例,实际以综合实现报告为准。仿真波形应显示:在 t=35 ns 时,a=1,sel=0,mux_out 立即变为 1,但 led 在 t=40 ns(时钟上升沿)才变为 1。
故障排查(Troubleshooting)
- 现象:综合报错“cannot be driven by multiple drivers” → 原因:同一信号在多个 always 块或 assign 语句中赋值。→ 解决:检查代码,确保每个 wire/reg 只被驱动一次。
- 现象:仿真输出一直为 X → 原因:未初始化或复位未释放。→ 解决:检查 testbench 中是否在 t=0 时拉低复位,并等待足够时间再释放。
- 现象:上板后 LED 不亮 → 原因:引脚约束错误或时钟未连接。→ 解决:检查 XDC 中 I/O 端口是否正确绑定到物理引脚。
- 现象:时序分析报告 WNS 为负 → 原因:组合逻辑延迟过长或时钟频率过高。→ 解决:尝试降低时钟频率(增加周期)或优化组合逻辑(如减少级联 LUT)。
- 现象:仿真中 led 在时钟沿之前变化 → 原因:错误使用了阻塞赋值(=)代替非阻塞赋值(<=)。→ 解决:将 always 块中的赋值改为非阻塞。
- 现象:综合后资源报告显示大量 LUT/FF → 原因:代码中无意生成了锁存器(latch)。→ 解决:检查组合逻辑中是否所有分支都有赋值,避免 inferred latch。
- 现象:上板后 LED 闪烁不稳定 → 原因:时钟抖动或复位抖动。→ 解决:在复位路径上添加去抖电路(或使用专用复位芯片)。
- 现象:仿真波形中 mux_out 有毛刺 → 原因:组合逻辑的输入变化时间不一致。→ 解决:这是正常现象,时序逻辑输出 led 会滤除毛刺。
扩展与下一步
- 参数化设计:将多路选择器改为参数化宽度(如 parameter WIDTH = 8),支持多 bit 数据。
- 流水线深度:在组合逻辑路径中插入多级寄存器,提高 Fmax。
- 状态机集成:将本设计作为状态机的一部分,例如根据状态选择不同输入。
- 跨平台验证:在 Altera/Intel 器件上使用 Quartus 实现相同功能,对比资源与时序。
- 断言与覆盖:在 testbench 中加入 SVA 断言,自动检查时序关系。
参考与信息来源
- Vivado Design Suite User Guide: Synthesis (UG901)
- IEEE Std 1364-2005 Verilog Hardware Description Language
- Xilinx Artix-7 Data Sheet (DS181)
- “FPGA Prototyping by Verilog Examples” by Pong P. Chu
技术附录
术语表
- 组合逻辑:输出仅由当前输入决定的逻辑电路,无存储单元。
- 时序逻辑:输出依赖于时钟沿和内部状态的逻辑电路,含触发器。
- LUT:查找表,FPGA 中实现组合逻辑的基本单元。
- FF:触发器,FPGA 中实现时序逻辑的基本单元。
- WNS:最差负时序余量,必须 ≥ 0 才能保证时序收敛。
检查清单
- [ ] 所有输入端口声明为 wire
- [ ] 时序逻辑输出声明为 reg,使用非阻塞赋值
- [ ] 组合逻辑使用 assign 或 always @(*) 阻塞赋值
- [ ] 时钟约束已添加
- [ ] 仿真波形符合预期(组合输出立即变化,时序输出在时钟沿后变化)
- [ ] 综合后 WNS ≥ 0
关键约束速查
# 时钟约束(50 MHz)
create_clock -period 20.0 [get_ports clk]
# 输入延迟约束(示例)
set_input_delay -clock clk -max 5.0 [get_ports a]
set_input_delay -clock clk -min 1.0 [get_ports a]
# 输出延迟约束(示例)
set_output_delay -clock clk -max 6.0 [get_ports led]


