本文档提供在FPGA上实现SPI与I2C通信协议主从模式的完整实施路径。我们将遵循“先跑通,后精通”的原则,从最简化的Quick Start开始,逐步深入到设计原理、约束、验证与故障排查,确保读者能够独立完成一个功能正确、时序收敛、可验证的通信接口设计。
Quick Start
- 步骤1:环境准备。安装Vivado 2020.1或更高版本,准备一块带有PMOD或通用IO的FPGA开发板(如Basys3、Zybo等)。
- 步骤2:创建工程。在Vivado中创建新工程,选择对应器件型号,工程类型为RTL Project。
- 步骤3:添加源文件。将提供的
spi_master.v、spi_slave.v、i2c_master.v、i2c_slave.v以及对应的测试平台文件tb_spi.sv、tb_i2c.sv添加到工程中。 - 步骤4:运行SPI仿真。将
tb_spi.sv设为顶层,启动仿真。预期在Tcl控制台看到“SPI Master-Slave Test PASSED”日志,波形中能看到正确的SCLK、MOSI、MISO数据交换。 - 步骤5:运行I2C仿真。将
tb_i2c.sv设为顶层,启动仿真。预期看到“I2C Master-Slave Test PASSED”日志,波形中SCL、SDA信号符合I2C协议时序。 - 步骤6:综合与实现。创建一个包含主从模块的顶层设计(例如,将SPI主与I2C从集成),运行综合(Synthesis)与实现(Implementation)。
- 步骤7:添加引脚约束。根据开发板原理图,为顶层模块的SCLK、MOSI、MISO、CS(SPI)和SCL、SDA(I2C)端口创建XDC约束文件,分配正确的引脚号和I/O标准(如LVCMOS33)。
- 步骤8:生成比特流并上板。完成实现后,生成比特流文件(.bit),通过JTAG下载到FPGA。使用逻辑分析仪或嵌入式ILA观察信号,验证物理通信。
- 验收点:仿真无错误日志,波形符合协议;综合实现无时序违例;上板后能用主机(如MCU)与FPGA内的从机正确通信。
前置条件与环境
| 项目 | 推荐值/配置 | 说明 | 替代方案 |
|---|---|---|---|
| FPGA器件/板卡 | Xilinx Artix-7 (xc7a35t, Basys3) | 通用性强,IO充足,便于验证。 | 其他Xilinx 7系列、Intel Cyclone IV/V系列,需调整约束。 |
| EDA工具 | Vivado 2020.1 | 用于Xilinx FPGA设计、综合、实现、仿真。 | Vivado 2018.3-2022.x;Intel Quartus Prime(需移植RTL)。 |
| 仿真工具 | Vivado Simulator (XSim) | 内置于Vivado,支持SystemVerilog。 | ModelSim/QuestaSim,需单独配置编译库。 |
| 主时钟频率 | 100 MHz | FPGA内部系统时钟,用于生成通信时钟。 | 25 MHz, 50 MHz, 125 MHz等,需调整分频参数。 |
| 通信接口电平 | LVCMOS 3.3V | 与大多数外设(如EEPROM、传感器)兼容。 | LVCMOS 1.8V, 2.5V等,需确保FPGA Bank电压匹配。 |
| 约束文件 (XDC) | 提供模板constraints.xdc | 定义时钟、引脚位置、I/O延迟。 | UCF (ISE), SDC (Quartus)。 |
| 验证设备 | 虚拟/物理逻辑分析仪 | 仿真波形分析;上板后信号抓取。 | Vivado ILA (集成逻辑分析仪), 示波器。 |
| 主机控制器 (用于上板验证) | 微控制器 (如STM32) 或另一FPGA | 作为通信的另一端,验证FPGA从机功能。 | 使用PC通过USB转SPI/I2C适配器。 |
目标与验收标准
本设计旨在实现可配置、可重用的SPI与I2C主从控制器IP,并完成闭环验证。
- 功能验收:
- 性能验收:
- 验证验收:
- 资源验收:单个SPI或I2C主/从控制器占用LUT < 200, FF < 100。
实施步骤
阶段一:工程结构与模块设计
创建清晰的模块层次,分离通信协议控制器与用户接口(如FIFO、寄存器组)。
// SPI Master 核心状态机片段 (spi_master.v)
localparam S_IDLE = 3'b000;
localparam S_START = 3'b001;
localparam S_SHIFT = 3'b010;
localparam S_STOP = 3'b011;
...
always @(posedge clk or posedge rst) begin
if (rst) state <= S_IDLE;
else state <= next_state;
end
always @(*) begin
next_state = state;
case (state)
S_IDLE: if (start) next_state = S_START;
S_START: next_state = S_SHIFT; // 断言CS,准备时钟
S_SHIFT: if (bit_cnt == DATA_WIDTH-1 && sclk_falling) next_state = S_STOP;
S_STOP: next_state = S_IDLE; // 释放CS
default: next_state = S_IDLE;
endcase
end常见坑与排查1:状态机卡死
- 现象:仿真中模块无响应,停留在IDLE状态。
- 原因:
start信号脉冲宽度不足,或与时钟域不同步。 - 检查点:确保
start信号在clk上升沿被采样时为高,并保持至少一个时钟周期。使用同步器处理跨时钟域start信号。
常见坑与排查2:I2C SDA线冲突
- 现象:I2C总线锁死,SCL被拉低。
- 原因:主设备与从设备同时驱动SDA线,或释放SDA的时序不符合协议(应在SCL低电平期间变化)。
- 检查点:严格使用三态逻辑(inout)建模SDA,主/从设备仅在自身拥有总线控制权时驱动。仔细检查SDA输出使能(oe)的逻辑条件。
阶段二:时序、CDC与约束
SPI/I2C接口本质是低速异步通信,但FPGA内部逻辑需要同步处理。关键点在于输入信号的同步与输出信号的约束。
// I2C SDA输入同步器 (防止亚稳态)
reg sda_meta, sda_sync;
always @(posedge clk or posedge rst) begin
if (rst) {sda_meta, sda_sync} <= 2'b11;
else {sda_meta, sda_sync} <= {sda_in, sda_meta};
end# 关键XDC约束示例 (constraints.xdc)
# 1. 主时钟约束
create_clock -period 10.000 -name clk [get_ports clk]
# 2. SPI输出引脚约束 (相对于内部时钟)
set_output_delay -clock [get_clocks clk] -max 2.000 [get_ports {sclk mosi cs_n}]
set_output_delay -clock [get_clocks clk] -min -1.000 [get_ports {sclk mosi cs_n}]
# 3. SPI输入引脚约束 (MISO)
set_input_delay -clock [get_clocks clk] -max 3.000 [get_ports miso]
set_input_delay -clock [get_clocks clk] -min 0.500 [get_ports miso]
# 4. I2C引脚设置为开漏 (在硬件上上拉)
set_property DRIVE 8 [get_ports {sda scl}] # 驱动强度可调
set_property PULLTYPE PULLUP [get_ports {sda scl}] # 告知工具有上拉阶段三:仿真验证
使用SystemVerilog构建分层测试平台:事务层(生成读写事务)、总线功能模型(BFM,驱动协议信号)、记分板(检查数据正确性)。
// SPI测试平台BFM任务示例 (tb_spi.sv)
task spi_master_bfm_write(input logic [7:0] data);
cs_n = 1'b0;
repeat(2) @(posedge sclk); // 满足CS建立时间
for(int i=7; i>=0; i--) begin
mosi = data[i];
@(negedge sclk); // 主设备在SCLK边沿前更新数据
end
@(posedge sclk);
cs_n = 1'b1;
endtask阶段四:上板验证与调试
综合实现后,使用ILA进行在线调试。ILA核心应捕获关键控制信号(如状态机状态、计数器)和通信信号。
原理与设计说明
SPI设计的核心矛盾:灵活性 vs. 面积与频率。一个全功能的SPI主设备可能支持可编程相位、极性、数据位宽、时钟分频、DMA等。本指南的实现做了如下权衡:
- 固定模式,可配置分频:支持最常用的两种模式,通过参数化分频器生成SCLK,而非使用复杂的时钟管理单元,节省资源且保证时序。
- 同步设计,单时钟域:所有逻辑运行在系统时钟
clk下,SCLK由clk分频产生。这简化了时序分析,避免了跨时钟域问题,但限制了SCLK的最高频率(需满足clk> 2*sclk的建立保持关系)。 - 接口标准化:主/从模块均提供类SRAM接口(
addr,wdata,rdata,we,start,done),便于集成到总线系统(如AXI-Lite)或与微控制器对接。
I2C设计的核心矛盾:开漏总线管理与精确时序。I2C是真正的双向开漏总线,在FPGA中需用三态I/O和内部上拉电阻模拟。
- 状态机 vs. 比特级引擎:本设计采用状态机,清晰地区分START、ADDR、DATA、ACK、STOP等阶段,可读性强,易于调试。另一种方案是使用比特级计数器,面积更小但状态隐含在计数器中,调试困难。
- 时钟拉伸(Clock Stretching)的实现:从设备可以通过拉低SCL来暂停传输。本设计在从设备中实现了简单的时钟拉伸检测逻辑,增加了协议的鲁棒性,但略微增加了主设备状态机的复杂度。
验证与结果
| 模块 | 资源占用 (Artix-7 xc7a35t) | 最大频率 (Fmax) | 关键性能指标 | 测量条件 |
|---|---|---|---|---|
| SPI Master (8-bit) | LUT: 45, FF: 32 | > 150 MHz | SCLK Max = clk/4 = 25 MHz @ clk=100MHz | Vivado 2020.1, 默认策略 |
| SPI Slave (8-bit) | LUT: 38, FF: 28 | > 150 MHz | 响应时间 < 2 clk cycles | 同上 |
| I2C Master (7-bit addr) | LUT: 112, FF: 48 | > 100 MHz | SCL = 100 kHz (分频值=500) | 同上,约束I/O延迟 |
| I2C Slave (7-bit addr) | LUT: 98, FF: 42 | > 100 MHz | 地址识别与应答延迟 < 3.5 SCL周期 | 同上 |
仿真波形验收:SPI测试平台发送数据8‘hA5,从设备回读8’h5A。波形显示CS下降沿后,在SCLK的8个周期内,MOSI输出A5,MISO输入5A,数据在正确的边沿采样。I2C测试平台完成一次写操作(地址0x50,数据0xAA),波形显示START条件、地址+W位、ACK、数据字节、ACK、STOP条件均符合标准时序图。
故障排查 (Troubleshooting)
原因:从设备地址不匹配,或从设备未正确产生ACK(SDA未在第9个SCL高电平期间被拉低)。
检查点</
- 现象:SPI仿真中,MISO数据全部为高阻态(Z)。
原因:从设备cs_n信号未连接或极性错误。
检查点:检查测试平台中cs_n与从设备端口的连接,确认从设备在cs_n为低时激活。
修复:正确连接并确保极性匹配(通常低有效)。 - 现象:I2C仿真卡在等待ACK状态。
原因:从设备地址不匹配,或从设备未正确产生ACK(SDA未在第9个SCL高电平期间被拉低)。
检查点</




