告别笨重MCU:用纯Verilog在FPGA里实现I2C Slave与EEPROM通信
纯Verilog实现FPGA内I2C从机与EEPROM仿真实战指南当树莓派需要通过I2C读取传感器数据时传统方案需要外挂一颗AT24C02之类的EEPROM芯片。但如果你手头正好有闲置的FPGA完全可以用硬件描述语言在可编程逻辑内部虚拟出一个I2C从设备既能节省电路板空间又能灵活定制存储行为。本文将手把手带你用Verilog构建一个支持随机读写的虚拟EEPROM重点解决三大核心问题如何用状态机精准模拟I2C协议中的起止条件、应答机制怎样在FPGA内部构建可寻址的存储阵列与真实微控制器联调时的时序对齐技巧1. I2C协议精要与FPGA实现难点I2C总线虽然只有SCL和SDA两根线但协议层隐藏着诸多细节要求。在开始编写Verilog代码前需要明确几个关键约束条件电气特性标准模式100kHz时钟下建立时间(t_SU;DAT)要求数据在SCL上升沿前至少100ns稳定状态转换起始条件(S)定义为SCL高电平时SDA下降沿停止条件(P)则是SCL高电平时SDA上升沿字节格式每个字节传输后必须跟随一个应答位(ACK)由接收方将SDA拉低// 起始/停止条件检测电路示例 reg sda_prev, scl_prev; wire start_cond scl_prev scl_prev ~sda sda_prev; wire stop_cond scl_prev scl_prev sda ~sda_prev; always (posedge clk) begin sda_prev SDA; scl_prev SCL; endFPGA实现时的特殊考量避免直接使用SCL作为时钟易受毛刺影响输入信号需要同步化处理防止亚稳态存储单元建议采用寄存器而非Block RAM以获得确定性时序2. 虚拟EEPROM架构设计我们以常见的AT24C02为蓝本设计一个256字节的存储体。与物理芯片不同FPGA内部的存储阵列可以定制特殊功能特性物理EEPROMFPGA虚拟实现写周期时间5ms典型值立即完成寿命100万次擦写无限次页写入8字节限制可配置页大小地址范围固定运行时可动态调整核心模块划分如下module i2c_slave_eeprom ( input wire scl, // I2C时钟线 inout wire sda, // I2C数据线 input wire clk, // FPGA系统时钟(至少10倍于I2C频率) input wire rst_n // 异步复位 ); // 状态机控制核心 i2c_protocol_engine u_engine(.*); // 存储阵列 reg [7:0] memory [0:255]; // 地址指针寄存器 reg [7:0] address_ptr; endmodule3. 状态机实现细节I2C从设备需要处理11种可能的状态转换IDLE等待起始条件ADDR接收设备地址读写位ACK_ADDR发送地址应答MEM_ADDR接收内存地址ACK_MEM发送内存地址应答WRITE_DATA接收写入数据ACK_WRITE发送写入应答READ_DATA发送读取数据WAIT_ACK等待主机应答STOP检测停止条件ERROR协议异常处理// 状态机片段示例 always (posedge clk or negedge rst_n) begin if(!rst_n) begin state IDLE; end else begin case(state) IDLE: if(start_cond) state ADDR; ADDR: if(bit_cnt 8) state ACK_ADDR; ACK_ADDR: if(rd_wr_n) state READ_DATA; else state MEM_ADDR; // ...其他状态转换逻辑 endcase end end关键时序参数配置// 时序参数单位时钟周期 parameter T_HD_STA 10; // 起始条件保持时间 parameter T_SU_STO 8; // 停止条件建立时间 parameter T_BUF 12; // 停止到起始间隔4. 与微控制器实战联调当FPGA作为从设备与树莓派等主控连接时常遇到三类问题电平匹配3.3V MCU连接5V FPGA时需要电平转换器开漏输出必须外接上拉电阻典型值4.7kΩ时序调试技巧先降低I2C时钟频率到10kHz进行基础通信测试使用FPGA内部的ILA集成逻辑分析仪捕获信号检查SCL/SDA的上升时间是否符合规范典型故障排查表现象可能原因解决方案收不到ACK地址不匹配检查从机地址配置数据位错乱时序约束未满足增加时钟同步寄存器随机停止条件SDA毛刺添加施密特触发器输入写操作无效果存储阵列未正确更新检查写使能信号时序联调时可借助以下调试命令验证基本功能# 在树莓派上安装i2c-tools sudo apt install i2c-tools # 扫描I2C总线设备 sudo i2cdetect -y 1 # 读取前16字节数据 sudo i2cdump -y 1 0x505. 性能优化进阶技巧基础功能实现后可通过以下手段提升可靠性时钟抖动过滤// 数字滤波器消除SCL毛刺 reg [2:0] scl_sync; always (posedge clk) begin scl_sync {scl_sync[1:0], SCL}; end wire scl_clean (scl_sync[2:1] 2b11) ? 1b1 : (scl_sync[2:1] 2b00) ? 1b0 : scl_sync[2];存储保护机制添加写保护寄存器位实现密码保护区域设计循环冗余校验(CRC)功能多从设备模拟 通过动态重配置技术单个FPGA可以时分复用模拟多个I2C设备// 多从机地址支持 parameter [6:0] ADDR_BASE 7h50; parameter NUM_SLAVES 4; reg [7:0] mem_array [0:NUM_SLAVES-1][0:255]; wire [1:0] slave_sel (i2c_addr - ADDR_BASE) % NUM_SLAVES;在Xilinx Artix-7上的实测数据显示优化后的设计仅占用如下资源资源类型使用量占比LUT2173.2%FF1842.7%BRAM11.5%这个虚拟EEPROM方案已经成功应用于多个需要灵活配置存储或快速原型验证的场景。比如在某个电机控制项目中我们用它来动态存储PID参数相比物理EEPROM修改参数后的写入速度提升了1000倍以上。