Quick Start(快速上手)
- 安装 Vivado 2023.1 以上版本,并新建一个 RTL 工程,目标器件选择 Xilinx Artix-7 XC7A35T。
- 创建顶层模块,例化一个异步 FIFO IP 核(FIFO Generator),配置深度为 16、数据位宽为 8、使用独立时钟 wr_clk 和 rd_clk。
- 编写 Testbench,生成两个不同频率的时钟:wr_clk = 100 MHz,rd_clk = 75 MHz,并产生写使能和读使能信号。
- 实现一个简单的握手协议模块:发送端产生 req 信号,接收端检测到 req 后拉高 ack,发送端检测到 ack 后拉低 req,完成一次数据传输。
- 运行行为仿真(behavioral simulation),观察 FIFO 的 empty、full、wr_ack 和 rd_ack 信号是否按预期变化。
- 执行综合(synthesize)并查看时序报告(timing summary),确保所有跨时钟路径(wr_clk → rd_clk)的 setup/hold 约束满足。
- 完成实现(implement)并生成比特流,下载到开发板(如 Nexys A7),通过逻辑分析仪(ILA)抓取握手信号和 FIFO 数据。
- 验收:确认握手协议的数据传输无丢失,FIFO 未出现 overflow/underflow,Fmax 满足设计要求。
前置条件与环境
| 项目 | 推荐值 | 替代方案 |
|---|---|---|
| 器件/板卡 | Xilinx Artix-7 XC7A35T (Nexys A7) | Intel Cyclone IV / Lattice ECP5 |
| EDA 版本 | Vivado 2023.1 | Quartus Prime 22.1 / Radiant 2022.2 |
| 仿真器 | Vivado Simulator (Xsim) | ModelSim / Questa / Verilator |
| 时钟/复位 | 外部 100 MHz 晶振,板上复位按钮(低有效) | PLL 分频生成多时钟 |
| 接口依赖 | UART 或 GPIO 用于数据观察 | ILA / VIO 核 |
| 约束文件 | XDC 文件需包含时钟定义、异步时钟组(set_clock_groups) | SDC 文件(Intel) |
说明:以上环境为推荐配置,替代方案在工具链命令和约束语法上略有差异,但设计原理完全一致。
目标与验收标准
本实战的目标是掌握跨时钟域(CDC)设计的两种经典方案:异步 FIFO 和握手协议。验收标准如下:
- 功能点:握手协议能可靠地传递单比特或多比特数据(每次传输一个字节),FIFO 能缓存多个数据并保证无丢失。
- 性能指标:FIFO 最大工作频率(Fmax)≥ 150 MHz(Artix-7 典型值),握手协议在 100 MHz 时钟下无亚稳态错误。
- 资源消耗:FIFO 使用 1 个 BRAM(深度 16),握手协议使用约 20 个 LUT + 10 个 FF。
- 验收方式:仿真波形中握手信号无毛刺、FIFO 的 empty/full 标志正确;上板后通过 ILA 抓取的数据与发送端一致。
实施步骤
1. 工程结构与模块划分
推荐工程结构如下(以 Vivado 为例):
src/
├── top.v # 顶层模块,例化 FIFO 和握手模块
├── fifo_wrapper.v # 异步 FIFO IP 核的包装器
├── handshake_tx.v # 发送端握手模块
├── handshake_rx.v # 接收端握手模块
└── tb_top.v # 测试平台
constraints/
└── top.xdc # 时序约束注意:FIFO IP 核使用独立时钟模式,写时钟域和读时钟域分别连接不同的时钟源。
2. 关键模块实现
握手协议(发送端):
module handshake_tx (
input wire clk,
input wire rst_n,
input wire [7:0] data_in,
input wire send_en,
output reg req,
input wire ack,
output reg [7:0] data_out
);
// 状态机:IDLE -> WAIT_ACK -> DONE
localparam IDLE = 2'b00, WAIT_ACK = 2'b01, DONE = 2'b10;
reg [1:0] state, next_state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= IDLE;
else
state <= next_state;
end
always @(*) begin
next_state = state;
case (state)
IDLE: if (send_en) next_state = WAIT_ACK;
WAIT_ACK: if (ack) next_state = DONE;
DONE: next_state = IDLE;
default: next_state = IDLE;
endcase
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
req <= 1'b0;
data_out <= 8'b0;
end else begin
case (state)
IDLE: if (send_en) begin req <= 1'b1; data_out <= data_in; end
WAIT_ACK: ; // 等待 ack
DONE: req <= 1'b0;
endcase
end
end
endmodule握手协议(接收端):
module handshake_rx (
input wire clk,
input wire rst_n,
input wire req,
output reg ack,
output reg [7:0] data_out
);
// 双触发器同步 req 信号
reg req_sync1, req_sync2;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
req_sync1 <= 1'b0;
req_sync2 <= 1'b0;
end else begin
req_sync1 <= req;
req_sync2 <= req_sync1;
end
end
// 状态机:IDLE -> WAIT_REQ_LOW
localparam IDLE = 1'b0, WAIT_REQ_LOW = 1'b1;
reg state, next_state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= IDLE;
else
state <= next_state;
end
always @(*) begin
next_state = state;
case (state)
IDLE: if (req_sync2) next_state = WAIT_REQ_LOW;
WAIT_REQ_LOW: if (!req_sync2) next_state = IDLE;
default: next_state = IDLE;
endcase
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
ack <= 1'b0;
data_out <= 8'b0;
end else begin
case (state)
IDLE: if (req_sync2) begin ack <= 1'b1; data_out <= 8'hA5; end // 示例数据
WAIT_REQ_LOW: if (!req_sync2) ack <= 1'b0;
endcase
end
end
endmodule异步 FIFO 包装器:
module fifo_wrapper (
input wire wr_clk,
input wire rd_clk,
input wire rst_n,
input wire [7:0] din,
input wire wr_en,
input wire rd_en,
output wire [7:0] dout,
output wire full,
output wire empty
);
// 例化 FIFO Generator IP 核(深度16,位宽8,独立时钟)
fifo_generator_0 u_fifo (
.wr_clk(wr_clk),
.rd_clk(rd_clk),
.rst(~rst_n), // IP 核通常高有效复位
.din(din),
.wr_en(wr_en),
.rd_en(rd_en),
.dout(dout),
.full(full),
.empty(empty)
);
endmodule3. 时序约束
在 XDC 文件中定义时钟并设置异步时钟组,避免工具对跨时钟路径进行不必要的时序分析:
create_clock -name wr_clk -period 10.000 [get_ports wr_clk]
create_clock -name rd_clk -period 13.333 [get_ports rd_clk]
set_clock_groups -asynchronous -group [get_clocks wr_clk] -group [get_clocks rd_clk]原因分析:如果不设置异步时钟组,时序分析工具会尝试分析 wr_clk 到 rd_clk 的路径,导致大量 false violation,掩盖真正的时序问题。
4. 仿真验证
编写 Testbench 时,注意以下要点:
- 生成两个独立时钟,频率分别为 100 MHz 和 75 MHz,相位随机。
- 模拟写满和读空的边界条件:连续写入 16 个数据后观察 full 信号,连续读取 16 个数据后观察 empty 信号。
- 握手协议测试:发送端随机间隔发送数据,接收端随机间隔应答,验证数据完整性。
仿真波形应观察到:FIFO 的 wr_ack 在写入数据后拉高,rd_ack 在读取数据后拉高;握手协议的 req 和 ack 信号无毛刺、无亚稳态。
5. 上板调试
使用 ILA(集成逻辑分析仪)抓取关键信号:
- 设置触发条件:例如 wr_en 上升沿或 rd_en 上升沿。
- 抓取深度建议 1024,确保能捕获多次传输。
- 对比仿真与上板结果,确认一致性。
风险边界:上板后若发现数据丢失,首先检查复位同步是否在每个时钟域内独立实现;其次确认 FIFO 深度是否满足最差情况下的数据缓存需求。
验证结果
通过仿真和上板验证,预期结果如下:
- 握手协议在 100 MHz 时钟下无亚稳态错误,数据传输正确率 100%。
- FIFO 在 100 MHz 写入、75 MHz 读取时,无 overflow/underflow 发生。
- FIFO 的 Fmax 达到 150 MHz 以上(Artix-7 典型值)。
排障指南
- 问题:FIFO 出现 overflow → 检查写使能信号是否在 full 为高时仍然有效;增加 FIFO 深度或降低写入速率。
- 问题:握手协议数据丢失 → 确认接收端 req 同步使用了双触发器;检查 ack 信号是否在发送端正确同步。
- 问题:综合时序不满足 → 确认 XDC 中设置了异步时钟组;检查跨时钟路径是否被误约束。
扩展与进阶
- 深度扩展:将 FIFO 深度扩展到 256 或 1024,测试不同深度下的资源消耗和 Fmax。
- 性能优化:在握手协议中引入流水线,提高吞吐量。
- 多比特 CDC:使用格雷码指针的异步 FIFO 处理多比特数据跨时钟域传输。
参考与附录
- Xilinx UG172: FIFO Generator v13.2 Product Guide
- Clifford E. Cummings, "Simulation and Synthesis Techniques for Asynchronous FIFO Design"
- 附录 A:完整 Testbench 代码(略)
- 附录 B:ILA 配置步骤(略)




