---
title: FPGA实现SPI/I2C通信协议：主从模式设计与仿真验证
url: "https://www.fpgalearn.com/33440.html"
kind: article
id: 33440
excerpt: 本文档提供在FPGA上实现SPI与I2C通信协议主从模式的完整实施路径。我们将遵循“先跑通，后精通”的原则，从最简化的Quick Start开始，逐步深入到设计…
published_at: 2026-04-18
author: 二牛学FPGA
tags: fpga, I2C, SPI
word_count: 3613
source: "https://www.fpgalearn.com"
---

# FPGA实现SPI/I2C通信协议：主从模式设计与仿真验证

> 标签：fpga、I2C、SPI
> 本文档提供在FPGA上实现SPI与I2C通信协议主从模式的完整实施路径。我们将遵循“先跑通，后精通”的原则，从最简化的Quick Start开始，逐步深入到设计…

> 本文档提供在FPGA上实现SPI与I2C通信协议主从模式的完整实施路径。我们将遵循“先跑通，后精通”的原则，从最简化的Quick Start开始，逐步深入到设计…

本文档提供在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**：支持模式0(CPOL=0, CPHA=0)与模式3(CPOL=1, CPHA=1)；主设备能发起单次/连续传输；从设备能正确响应。
- **I2C**：支持标准模式(100 kHz)与快速模式(400 kHz)；主设备能完成START、ADDR+W/R、DATA、STOP序列；从设备能识别自身地址并应答。
- **性能验收**：
- 在100 MHz系统时钟下，SPI主时钟(SCLK)最高可达25 MHz（4分频）。
- I2C SCL时钟频率误差 < 2%。
- 综合后无时序违例，建立/保持时间均满足。
- **验证验收**：
- 仿真：测试平台通过自检，覆盖率（语句覆盖）> 95%。
- 上板：通过ILA或外部分析仪捕获到符合协议的真实波形，并与预期数据匹配。
- **资源验收**：单个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)
- **现象**：I2C仿真卡在等待ACK状态。
**原因**：从设备地址不匹配，或从设备未正确产生ACK（SDA未在第9个SCL高电平期间被拉低）。
检查点</
- **现象**：SPI仿真中，MISO数据全部为高阻态(Z)。
**原因**：从设备`cs_n`信号未连接或极性错误。
**检查点**：检查测试平台中`cs_n`与从设备端口的连接，确认从设备在`cs_n`为低时激活。
**修复**：正确连接并确保极性匹配（通常低有效）。
- **现象**：I2C仿真卡在等待ACK状态。
**原因**：从设备地址不匹配，或从设备未正确产生ACK（SDA未在第9个SCL高电平期间被拉低）。
检查点</
