当前位置: 首页 > news >正文

从零到上板:用FPGA实现SPI主从机完整数据回环(Vivado ILA抓波形实战)

从零到上板:用FPGA实现SPI主从机完整数据回环(Vivado ILA抓波形实战)

在嵌入式系统开发中,SPI协议因其高速、全双工的特性成为芯片间通信的首选方案之一。本文将带您完成一个完整的FPGA开发流程:从Verilog代码编写、功能仿真到实际上板调试,最终通过Vivado的ILA工具实时捕获SPI总线波形,验证主从机数据回环的正确性。不同于单纯的模块仿真,我们聚焦于系统级集成与硬件调试技巧,适合已经掌握SPI基础理论、需要实战经验的开发者。

1. SPI主从机系统架构设计

1.1 整体框架规划

一个完整的SPI回环系统需要包含以下核心模块:

  • 主机发送模块:负责生成SCK时钟、MOSI数据流和CS片选信号
  • 从机接收模块:同步捕获主机发送的数据
  • 数据回环通路:将接收数据反馈给主机形成闭环验证
  • 调试接口:集成ILA核用于实时信号捕获

关键信号定义如下表:

信号名称方向描述
sys_clk输入系统时钟(50MHz)
rst_n输入低电平有效的全局复位
spi_clk主机输出SPI串行时钟(可配置极性)
spi_mosi主机输出主机输出从机输入数据线
spi_miso从机输出主机输入从机输出数据线
spi_cs主机输出从机片选信号(低电平有效)

1.2 时钟域处理要点

由于SPI时钟(SCK)由主机动态生成,而从机需要同步捕获该时钟域的数据,必须特别注意跨时钟域处理:

// 三级寄存器同步链消除亚稳态 always @(posedge sys_clk or negedge rst_n) begin if(!rst_n) begin spi_clk_sync <= 3'b0; spi_mosi_sync <= 3'b0; end else begin spi_clk_sync <= {spi_clk_sync[1:0], spi_clk}; spi_mosi_sync <= {spi_mosi_sync[1:0], spi_mosi}; end end

提示:对于高速SPI通信(>10MHz),建议使用IDDR原语处理时钟边沿检测

2. 主机发送模块实现细节

2.1 可配置时钟生成

主机模块需要根据系统时钟分频产生SPI时钟,关键参数包括:

  • CPOL:时钟空闲状态极性(0=低电平,1=高电平)
  • CPHA:数据采样相位(0=第一个边沿,1=第二个边沿)

时钟分频计数器实现如下:

localparam CLK_DIV = SYS_CLK_FREQ / SPI_CLK_FREQ / 2; always @(posedge sys_clk or negedge rst_n) begin if(!rst_n) begin clk_cnt <= 0; spi_clk_int <= CPOL; end else if(spi_en) begin if(clk_cnt == CLK_DIV-1) begin clk_cnt <= 0; spi_clk_int <= ~spi_clk_int; end else begin clk_cnt <= clk_cnt + 1; end end else begin spi_clk_int <= CPOL; end end

2.2 数据移位控制

根据CPHA配置,数据需要在时钟的特定边沿更新:

always @(*) begin case({CPOL, CPHA}) 2'b00: data_update_edge = ~spi_clk_int & (clk_cnt == CLK_DIV-1); 2'b01: data_update_edge = spi_clk_int & (clk_cnt == CLK_DIV-1); // 其他模式组合... endcase end always @(posedge sys_clk or negedge rst_n) begin if(!rst_n) begin shift_reg <= 8'h00; bit_cnt <= 0; end else if(data_update_edge) begin shift_reg <= {shift_reg[6:0], 1'b0}; bit_cnt <= bit_cnt + 1; end end

3. 从机接收模块设计要点

3.1 边沿检测电路

从机需要精确检测SPI时钟的有效边沿进行数据采样:

// 上升沿检测 assign pos_edge = (spi_clk_sync[2:1] == 2'b01); // 下降沿检测 assign neg_edge = (spi_clk_sync[2:1] == 2'b10); always @(*) begin case({CPOL, CPHA}) 2'b00: sample_en = pos_edge; 2'b01: sample_en = neg_edge; // 其他模式组合... endcase end

3.2 数据对齐与校验

接收到的串行数据需要转换为并行格式,并添加有效性校验:

always @(posedge sys_clk or negedge rst_n) begin if(!rst_n) begin rx_data <= 8'h00; rx_valid <= 1'b0; end else if(spi_cs_sync[3]) begin rx_valid <= 1'b0; // CS无效时清除有效标志 end else if(sample_en) begin rx_data <= {rx_data[6:0], spi_mosi_sync[3]}; if(bit_cnt == 7) rx_valid <= 1'b1; end end

4. 系统集成与ILA调试实战

4.1 回环测试顶层设计

将主从机模块集成到顶层,建立数据回环通路:

spi_master #( .CPOL(0), .CPHA(0) ) u_master ( .sys_clk(sys_clk), .rst_n(rst_n), .spi_clk(spi_clk), .spi_mosi(spi_mosi), .spi_miso(spi_miso), .spi_cs(spi_cs) ); spi_slave #( .CPOL(0), .CPHA(0) ) u_slave ( .sys_clk(sys_clk), .rst_n(rst_n), .spi_clk(spi_clk), .spi_mosi(spi_mosi), .spi_miso(spi_miso), .spi_cs(spi_cs), .rx_data(rx_data), .rx_valid(rx_valid) );

4.2 ILA核配置技巧

在Vivado中添加ILA核时需注意:

  1. 设置足够深的采样存储(至少4096点)
  2. 添加关键信号:spi_clk、spi_mosi、spi_miso、spi_cs
  3. 配置触发条件:如rx_valid上升沿

TCL配置示例:

create_debug_core u_ila ila set_property C_DATA_DEPTH 8192 [get_debug_cores u_ila] set_property C_TRIGIN_EN false [get_debug_cores u_ila] add_probe spi_clk [get_debug_ports u_ila/clk] add_probe spi_mosi [get_debug_ports u_ila/probe0] add_probe rx_valid [get_debug_ports u_ila/probe1]

4.3 上板调试常见问题

  • 时钟偏移问题:如果采样数据不稳定,尝试调整ILA采样时钟相位
  • 触发条件设置:复杂触发可使用触发状态机(TSM)
  • 数据对齐异常:检查CPOL/CPHA配置是否与从设备一致

实际捕获的波形示例:

CS __----____________________________________ CLK _-_-_-_-_-_-_-_-_________________________ MOSI _X_X_X_X_X_X_X_X_________________________ MISO _X_X_X_X_X_X_X_X_________________________ | | | | | | | | 0 1 2 3 4 5 6 7 (bit位置)

在Xilinx Artix-7开发板上实测时,发现当SPI时钟超过25MHz时,需要插入IO延迟约束才能保证稳定采样。通过ILA的波形对比功能,可以直观验证发送与接收数据的比特对齐情况。

http://www.jsqmd.com/news/740118/

相关文章:

  • 2026 降 AI 软件排行别只看价格!这 5 大降 AI 误区毕业生踩了几个? - 我要发一区
  • 告别乱码!树莓派5与Windows电脑串口调试最全指南(含CH340驱动)
  • Agent Browser:统一管理MCP服务器,告别多客户端重复配置
  • 10分钟掌握物理知情神经网络:用PyTorch轻松求解偏微分方程
  • 别再只用交叉熵了!手把手教你用PyTorch实现Soft IoU Loss,搞定语义分割中的小目标难题
  • 别再傻傻分不清!STM32 HAL库的HAL_SPI_Receive和HAL_SPI_Receive_IT到底怎么选?(附实战避坑指南)
  • 2026 降 AI 软件排行只看效果不够,这 3 项售后承诺决定了不延毕。 - 我要发一区
  • 终极暗黑3按键助手:5分钟快速上手指南,告别手动重复操作
  • 技术文章系列整理(持续更新)
  • 超图记忆HGMEM:复杂推理与高阶关联的AI解决方案
  • 人工智能篇---信号与系统、通信原理和深度学习的关系
  • live-to-100-skills:基于行为心理学的Windows桌面健康习惯养成工具实践
  • YOLOv7实战:如何将它集成到车载DMS系统,并优化抽烟、打电话等行为检测?
  • 别再死记硬背了!用这5个神州数码交换机/路由器实战场景,帮你真正理解配置命令
  • Taotoken的用量告警与成本分析功能如何助力项目精细化运营
  • 别再傻傻分不清了!5分钟搞懂UART、RS232、RS485的区别与选型(附STM32+Proteus仿真接线图)
  • 别再只盯着主站了!手把手教你用树莓派+EtherCAT HAT搭建一个低成本从站(附避坑指南)
  • 从CD到5G:BCH码这个“老古董”是如何在存储和通信里默默干活的?
  • 动手实验:用Python模拟UFS RPMB的认证读写流程(附代码)
  • Android 11系统层“骚操作”:一行代码让向日葵远程控制免弹窗(RK3568实测)
  • 别再只抓包了!手把手教你用OpenSSL验证‘挑战-响应’身份鉴别的签名(附完整数据包分析)
  • AI模型幻觉:行业上一些一本正经胡说八道的影响
  • 光伏MPPT金豺算法应用【附Matlab代码】
  • 本地化AI开发实践:从开源模型部署到生产级API服务
  • 别再手动画箭头了!用MATLAB的m_quiver函数5分钟搞定专业风场图
  • 【第三单元】Python基础语法
  • Python 3.15新调度架构实测:3步启用多解释器并行,吞吐量提升4.7倍(附可运行conf.toml模板)
  • ARM SVE2浮点运算指令FMINNM与FMLA详解
  • 别再手动调时序了!用Verilog手搓一个可配置的VTC模块,轻松适配多种显示器
  • 给AXI事务属性配个‘管家’:手把手教你用Verilog配置AxCACHE信号(附Memory类型对照表)