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

二牛学FPGA
文章2026-04-18
87

本文档提供在FPGA上实现SPI与I2C通信协议主从模式的完整实施路径。我们将遵循“先跑通,后精通”的原则,从最简化的Quick Start开始,逐步深入到设计原理、约束、验证与故障排查,确保读者能够独立完成一个功能正确、时序收敛、可验证的通信接口设计。

Quick Start

  • 步骤1:环境准备。安装Vivado 2020.1或更高版本,准备一块带有PMOD或通用IO的FPGA开发板(如Basys3、Zybo等)。
  • 步骤2:创建工程。在Vivado中创建新工程,选择对应器件型号,工程类型为RTL Project。
  • 步骤3:添加源文件。将提供的spi_master.vspi_slave.vi2c_master.vi2c_slave.v以及对应的测试平台文件tb_spi.svtb_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 MHzFPGA内部系统时钟,用于生成通信时钟。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 MHzSCLK Max = clk/4 = 25 MHz @ clk=100MHzVivado 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 MHzSCL = 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高电平期间被拉低)。

      检查点</

  • 分类
    技术分享
    标签
    fpgaI2CSPI
    浏览 87
    分享:

    相关推荐

    同频道 · 相近分类

    暂无相关推荐

    作者

    二牛学FPGA查看主页

    同分类阅读

    文章

    延伸阅读与实操

    • 文章 + 课程联动深度文章常对应体系课章节,可一键选课。
    • 学习产出可参考笔记与作业案例在学习产出广场持续更新。

    探索全站