ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AXI Quad SPI IP核配置与调试实战指南

AXI Quad SPI IP核配置与调试实战指南 做FPGA工程的人手里但凡有几块带Zynq或者带MicroBlaze的板子早晚会遇到一个需求要挂一块SPI Flash、接一个SPI接口的ADC或者跟某个SPI传感器通信。我第一次用AXI Quad SPI IP核的时候在Vivado里点完配置向导以为就万事大吉了结果板子上电一测MISO一直低电平SCK死活没有波形。后来翻手册看寄存器花了一下午才定位到问题。这篇文章就是把从添加IP核到最后收到正确数据的完整流程连带着我当时踩过的坑一起整理出来给正准备搭建SPI通信系统的朋友做个参考。内容会覆盖AXI Quad SPI的几种工作模式、Vivado里的参数怎么选、软件侧怎么收发数据以及几种常见故障的排查思路。1. 先搞明白AXI Quad SPI IP核到底是什么1.1 它解决什么问题和手写SPI控制器相比有什么优势很多FPGA开发者第一次接触SPI通信时第一反应是“SPI协议这么简单我自己写个状态机不就行了”。SPI确实只有SCK、MOSI、MISO、CS四根线单字节收发逻辑用Verilog写几十行就能完成但一旦落到实际工程里情况就会变得复杂得多。你的主控要跟谁通信是SPI Flash、SPI ADC、SPI DAC还是多个SPI从机每个从机的速率要求是多少时钟极性和相位是模式0还是模式3数据长度是8位还是16位要不要处理连续读写是用查询、中断还是DMA主控侧是AXI总线还是自定义总线这些如果全靠自己写RTL开发周期会被拉得非常长而且每一个需求变化都可能让你重写一遍状态机。AXI Quad SPI是Xilinx提供的官方IP核专门解决这类问题。它把SPI控制器、FIFO、中断管理、时钟分频、从机选择逻辑都封装好了对外提供标准的AXI接口。你不需要关心内部时序细节只要在Vivado里配置好参数然后在软件里写几行驱动就能完成数据收发。这样项目周期和调试成本能明显降下来。很多人还有个误区以为“AXI Quad SPI”只能接SPI Flash。实际上它支持三类用法标准SPI模式接普通SPI外设、双通道SPI模式、四通道SPI模式后两种多半用于SPI Flash高速读写。如果你只是接一个普通的SPI温度传感器用标准模式就够了。1.2 三种工作模式标准SPI、双通道SPI和四通道SPIAXI Quad SPI这个“Quad”指的是数据线可以扩展到四根对应三种工作模式标准SPI模式使用SCK、MOSI、MISO、片选CS四根线也就是大家熟悉的单通道全双工方式。Dual SPI模式把MOSI和MISO两根线复用来传输数据读操作时两根线都变成输入写操作时两根线都变成输出。这种方式通常用于SPI Flash的读加速。Quad SPI模式四根IO线全部参与数据传输读Flash的时候一次能搬4个bit。这是Xilinx这个IP核对SPI Flash高速访问的主要卖点配合DMA可以大幅提升吞吐率。配置的时候需要想清楚你当前项目到底需要哪种模式。如果只是跟ADC、DAC、传感器通信选Standard即可没必要为了“看起来高级”去选Quad模式因为Quad模式的数据引脚定义会变普通外设根本没法接。在IP配置向导里这个选项就是“SPI Mode”一般有三个选择Standard、Dual、Quad。选定之后对应的引脚名称也会变比如Quad模式下引脚会显示为IO0、IO1、IO2、IO3而不再叫MOSI/MISO。1.3 什么时候用静态配置什么时候用动态配置AXI Quad SPI在配置路径上分为两种静态配置和动态配置。这里的“静态”指的是在Vivado IP配置向导里直接把参数固化下来比如时钟分频系数、指令宽度、地址宽度、从机数量、是否启用FIFO等。这些参数在编译之后就写死在IP核内部软件运行期间不能改动。动态配置则是软件在运行时通过寄存器来改变某些行为。但要注意一个很多人踩过的坑AXI Quad SPI的时钟分频值并不支持运行时任意修改能通过寄存器动态设置的只是SPI模式CPOL/CPHA、主从模式、片选控制方式、指令/地址模式等一部分功能。分频比在生成IP时定了就是定了想改就得重新定制IP并重新生成比特流。这一点跟MCU上习惯不同。比如STM32的SPI外设波特率寄存器随时可以改嵌入式工程师很容易默认FPGA这边也一样。实际调试中我就遇到过有人想在软件里动态把SCK从10MHz改成1MHz来适配一块慢速传感器结果发现无论怎么写寄存器SCK频率都不变最后回头查IP配置才发现分频参数被固化在向导里了。所以做方案的时候先确认好各路SPI从机的工作频率上限再用最高的那个去反推分频系数留出余量这样才稳妥。2. 配置前必须理清的协议细节与总线关系2.1 SPI四种模式CPOL/CPHA和IP核的对应关系SPI协议本身并不复杂但模式选错是最常见的“第一坑”。SCK在空闲时是高还是低数据在SCK上升沿还是下降沿采样这两个维度组合出四种模式模式CPOLCPHA空闲电平采样边沿Mode 000低上升沿Mode 101低下降沿Mode 210高下降沿Mode 311高上升沿大多数SPI Flash和通用SPI外设默认支持Mode 0或Mode 3。配置AXI Quad SPI时CPOL和CPHA在Vivado里各有对应的下拉框但这两个参数同样可以在运行期间通过SPI控制寄存器CR里的位来调整。我个人建议是在硬件设计阶段就把每颗从机需要的模式记下来画一张简单的模式表贴在工作站旁边。调试时如果发现数据对不上先查对方数据手册的波形图再对着IP配置核对往往能省下半天时间。曾经有块SPI温度传感器数据手册时序图画得不太规范我按Mode 0怎么配都读不出正确数据后来用逻辑分析仪抓从机的输出才发现它对时钟无效边沿有特殊要求换成Mode 3才正常。2.2 硬件片选与软件片选一字之差坑却很大片选是SPI通信里最容易被忽略但又最容易出问题的信号。AXI Quad SPI支持两种片选管理方式硬件片选自动从机选择和软件片选手动从机选择。硬件片选模式下IP核在传输开始时会自动拉低对应的从机片选信号传输结束自动释放。软件只负责发起一次传输不用操心底层片选。这种方式用起来省心适合单从机或从机切换不频繁的场景。软件片选模式下片选信号的拉低和拉高完全由软件控制。你需要先写从机选择寄存器SSR选中某个从机然后再发起传输传输结束后还要记得释放片选。这个模式更灵活但也更容易出事故。我就见过有人在一次传输结束后忘了释放片选结果SPI总线上所有从机都被莫名其妙地选中出现总线冲突和数据错乱。在IP配置向导里这两个方式对应一个选项一般叫“Slave Select Mode”或“Manual Slave Select Enable”。如果是Zynq上跑Linux设备树里也会反映这个配置。选硬件片选还是软件片选我的经验是只有一根片选、从机固定不变用硬件自动控制如果有多从机且需要精确控制时序窗口或者从机要求片选在整个命令序列期间保持有效就用软件片选。2.3 AXI接口和SPI时钟域之间的FIFO数据通路AXI Quad SPI虽然对外提供的是AXI4-Lite接口但内部实际上是一个跨时钟域数据通路。AXI侧运行在S_AXI_ACLK下SCK则来自SPI时钟分频后的信号。数据从AXI总线写入发送FIFO再被SPI状态机取走按SCK节奏逐位发送接收方向类似SPI状态机把收到的数据写入接收FIFO软件再通过AXI总线的数据接收寄存器DRR读走。理解这条数据通路对后续定位问题非常关键。比如接收数据一直是0xFF不一定是SPI时序出错也可能是接收FIFO空的时候软件就发起了读操作。又比如发送数据发不出去可能是发送FIFO满软件写数据寄存器时被阻塞也可能是IP配置里FIFO深度太小一次DMA传输的数据量超过了FIFO容量。AXI Quad SPI内部FIFO的深度可以在Vivado配置向导里设置默认值通常是256字节。如果一次要传输大块数据建议开启DMA并选带AXI4 Full接口的版本让DMA直接搬运数据到FIFO避免CPU单点瓶颈。如果只是偶尔读写几个寄存器轮询方式配上默认FIFO就足够了不必上DMA。3. Vivado中IP核配置实操3.1 在Vivado中添加AXI Quad SPI IP核的完整过程在Vivado里搭建SPI通信系统的流程并不复杂但每一步都有容易出错的地方。我以Zynq平台为例把从零开始的操作串一遍。新建一个Block DesignBD工程在Diagram窗口空白处右键选择“Add IP”搜索“AXI Quad SPI”双击添加。IP核添加成功后Vivado会自动提示你连接AXI接口。如果芯片型号是Zynq把IP的S_AXI接口接到Zynq处理系统的M_AXI_GP0或M_AXI_GP1上S_AXI_ACLK连接到PS端对应的时钟输出上。尤其要注意S_AXI_ACLK和SPI时钟的关系。S_AXI_ACLK是整个IP寄存器访问和FIFO读写的工作时钟一般接100MHz或150MHz。SPI的SCK由这个时钟分频而来。有些设计里还有EXT_SPI_CLK引脚那是用于从模式下接收外部时钟的主模式一般不需要连接。添加完IP后别忘了运行“Connection Automation”自动连接工具它可以把片选信号、中断、时钟复位等自动接好。但自动连接并不一定完全正确特别是当有多个SPI从机或者有自定义引脚约束时还是需要手动检查一遍连线。3.2 关键参数逐项拆解AXI Quad SPI的配置向导里参数比较多但真正需要关注的核心参数其实就那么几个。我把常用配置整理成一个参考表参数名推荐值说明SPI ModeStandard / Dual / Quad按实际外设选择普通传感器选StandardTransaction Width8 bit大部分SPI外设是8位传输个别设备用16位Clock Divider根据输入时钟计算SCK S_AXI_ACLK / (2 × (分频值1))Slave Select ModeAuto / Manual单从机选Auto多从机或特殊时序选ManualEnable FIFO勾选建议默认开启中断和DMA依赖FIFOFIFO Depth256数据量小可以减小减少资源占用Instruction Width8 bit只有SPI Flash场景需要关注Address Width24 bit只有SPI Flash场景需要关注分频系数怎么算这里给个实际例子。如果S_AXI_ACLK是100MHz外设要求的最大SCK是10MHz那么理论上分频系数5就能得到10MHz但考虑到信号质量建议保守一点分频系数取8SCK就是100 / (2 × (81)) ≈ 5.56MHz。具体公式以IP数据手册为准不同版本可能有细微差别。Instruction Width和Address Width是给SPI Flash指令使用的。如果项目是用AXI Quad SPI去驱动SPI Flash这两个参数分别定义Flash命令字段和地址字段的位宽常见是8位指令和24位地址。如果接的是普通外设这两个参数可以设为1或置0没有影响。3.3 连接IP核、生成地址映射IP核参数配置完后回到Block Design连线。前期手动配置时可以用包含DMA的AXI Quad SPI版本也可以选只带AXI4-Lite的寄存器版本。如果选了带DMA的版本那么IP会有S_AXI4接口需要接AXI Interconnect再到PS端的高性能端口HPDMA搬运才能发挥带宽优势。连线完成后在Block Design工具栏里先做一次“Validate Design”检查有没有未连接的端口或错误的时钟/复位连接。然后右键IP的S_AXI_Lite接口选择“Make External”或者交给PS地址映射。编译前记得在Address Editor里给AXI Quad SPI分配一个地址段。假设分配基地址是0x41600000那么后续软件读写时就会用到这个地址。生成比特流之前建议先导出硬件Export Hardware包含bitstream再启动软件开发。如果是纯FPGA工程没有PS和SDK就用MicroBlaze加AXI Interconnect地址映射和软件操作方式类似只是时钟复位需要手动处理。3.4 生成比特流并导出硬件在Vivado里点击Generate Bitstream等待编译完成。如果SDK或后续测试需要启动“Export Hardware”勾选“Include bitstream”然后启动Xilinx SDK或Vitis。这里分享两个实操提醒第一每次修改IP配置并重新生成比特流后硬件导出必须同步更新否则软件侧用的寄存器地址和IP配置可能不一致第二如果添加了ILA调试核记得在保存波形配置时把探针信号一起保存否则下次打开工程又得重新设一遍触发条件。4. 嵌入式软件侧驱动与收发实现4.1 Zynq/SDK场合下的驱动初始化硬件导出到SDK/Vitis后可以通过现成的Xilinx驱动库来操作AXI Quad SPI。驱动库使用起来很直接核心就几个函数查找设备配置、初始化、设置从机选择、设置SPI模式、发起传输。初始化阶段的代码可以这样写#include xspi.h XSpi Spi; int SpiInit(void) { int Status; XSpi_Config *Config; Config XSpi_LookupConfig(XPAR_AXI_QUAD_SPI_0_DEVICE_ID); if (Config NULL) { return XST_FAILURE; } Status XSpi_CfgInitialize(Spi, Config, Config-BaseAddress); if (Status ! XST_SUCCESS) { return Status; } Status XSpi_Start(Spi); if (Status ! XST_SUCCESS) { return Status; } XSpi_IntrGlobalDisable(Spi); // 设置为Master模式CPOL0CPHA0 u32 Options XSP_MASTER_OPTION; XSpi_SetOptions(Spi, Options); // 选择第0个从机硬件片选模式下此调用可选 XSpi_SetSlaveSelect(Spi, 0x01); return XST_SUCCESS; }这段代码里XSpi_LookupConfig根据设备ID找到IP核的配置信息XSpi_CfgInitialize完成基地址和内部FIFO的初始化XSpi_Start让IP进入可工作状态。XSpi_SetOptions里设置SPI模式这里假设是Mode 0如果外设是Mode 3需要额外加上对应CLK_POLARITY和CLK_PHASE的选项具体宏定义在xspi.h里都有。一个小提醒如果IP配置向导里选的是软件片选那么XSpi_SetSlaveSelect这一步很重要不仅要选中从机还必须在每次传输结束后保持或释放片选否则后续传输会异常。硬件片选模式下可以忽略片选操作IP会在每次传输时自动处理。4.2 数据收发流程与轮询/中断方式的选择收发数据的最简单方式是轮询调用XSpi_Transfer。这个函数会把发送缓冲区数据写入发送FIFO同时从接收FIFO读回数据。函数内部会在数据全部发送且接收完成后返回适合数据量不大的场景。int SpiReadFlashId(u8 *Id) { u8 TxBuffer[4] {0x9F, 0x00, 0x00, 0x00}; u8 RxBuffer[4] {0}; int Status XSpi_Transfer(Spi, TxBuffer, RxBuffer, 4); if (Status ! XST_SUCCESS) { return Status; } // 读Flash的JEDEC ID时第一个字节是命令后三个字节是ID Id[0] RxBuffer[1]; Id[1] RxBuffer[2]; Id[2] RxBuffer[3]; return XST_SUCCESS; }这里收发共用同一个缓冲区长度参数SPI是全双工协议发送4个字节的同时也会接收4个字节。很多新手第一次用这个函数时容易把收发缓冲区当两个独立过程处理其实SPI收发是同步的每发送一个字节必然同时接收到一个字节即使对方没有数据返回接收缓冲里也会是固定电平对应的0xFF或0x00。工程中如果一次要传输几百字节甚至更多轮询方式会占用CPU大量时间。这时候就需要用中断或DMA。AXI Quad SPI带中断输出信号可以在发送FIFO半空、接收FIFO非空等条件下触发中断从而实现数据流的流水线处理。更高效的做法是配置IP为DMA模式使用AXI DMA把数据从DDR搬运到SPI FIFO再由IP自动转换位串行数据发出整个过程CPU只在传输完成时收到一次中断。中断模式和DMA模式的配置会比轮询复杂不少建议先把轮询跑通确认SPI链路没问题之后再考虑升级。很多人一开始直接上DMA一旦数据错位很难判断是DMA配置问题还是SPI时序问题调试难度会翻倍。4.3 Linux下通过spidev访问SPI设备如果Zynq上面的系统是Linux那就不必再裸机写驱动了。Linux内核里有现成的SPI子系统支持AXI Quad SPI设备树中把SPI控制器配置好用户态通过spidev节点就能访问SPI从机。设备树中SPI控制器节点的一个示意如下axi_quad_spi_0: axi-quad-spi41600000 { compatible xlnx,xps-spi-1.00.a; reg 0x41600000 0x10000; #address-cells 1; #size-cells 0; xlnx,num-transfer-bits 8; spidev0 { compatible spidev; reg 0; spi-max-frequency 5000000; }; };用户态程序打开/dev/spidev0.0后先用ioctl设置模式、速率、字长再通过SPI_IOC_MESSAGE来收发数据#include linux/spi/spidev.h int fd open(/dev/spidev0.0, O_RDWR); unsigned char mode SPI_MODE_0; unsigned int speed 5000000; unsigned char bits 8; ioctl(fd, SPI_IOC_WR_MODE, mode); ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, speed); ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, bits); unsigned char tx[4] {0x9F, 0x00, 0x00, 0x00}; unsigned char rx[4] {0}; struct spi_ioc_transfer tr { .tx_buf (unsigned long)tx, .rx_buf (unsigned long)rx, .len 4, .speed_hz speed, .bits_per_word bits, }; ioctl(fd, SPI_IOC_MESSAGE(1), tr);这种方式非常方便适合快速验证硬件链路。如果发现spidev节点没有生成大概率是设备树里compatible没配对或者内核没有使能CONFIG_SPI_SPIDEV选项。另外注意某些Linux内核版本会因为安全策略拒绝“spidev”作为兼容字符串这种情况可以在设备树里使用你的具体SPI芯片型号或者换成该芯片对应的兼容名。5. 调试经验与常见问题排查实录5.1 调试工具与抓波形准备SPI调试最有效的工具是逻辑分析仪其次是ILA和串口打印。条件允许的话强烈建议先在硬件上加ILA来抓SCK、MOSI、MISO、CS这几根关键信号。ILA是Xilinx的在线逻辑分析仪IP核可以通过JTAG实时观测内部信号不怕干扰抓完波形再分析比盲目打印高效得多。ILA的添加方式不复杂在Block Design里加一个ILA IP核把SPI引脚引出到ILA的探针端口。或者直接在顶层RTL里例化ILA把IP核输出引脚连过去。抓波形时注意先设置好触发条件比如用CS下降沿触发这样每次SPI传输开始都能被抓住。使用逻辑分析仪时探头地线要尽量靠近SPI芯片否则高速信号容易引起振铃和误采样。我自己遇到过一种情况逻辑分析仪显示SCK频率和数据内容都对但实际从机就是不响应。后来发现是逻辑分析仪的探头电容太大把本来就不太强的驱动信号拉变形了SPI速率降到1MHz以下才恢复正常。所以测量高频SPI时要选择低电容探头并且保证板子供电稳定。5.2 常见问题速查表调试过程中我积累了一些高频问题的处理思路列成表格方便查阅现象可能原因排查与解决办法SCK完全没有波形IP没有进入Master模式检查CR寄存器确认Master/Slave位配置检查IP是否已经StartSCK频率不对分频系数配置错误对照公式重新计算SCK注意分频参数与运行时间不可变MOSI有波形但MISO始终为高SPI模式不匹配核对CPOL/CPHA与外设要求用ILA抓波形确认采样点位置接收数据固定为0xFF接收FIFO为空时读了DRR确认每次传输后等待接收完成位检查RX_FIFO_NON_EMPTY标志接收数据固定为0x00MISO引脚被拉死或未正确连接用万用表测量MISO电平确认下拉/上拉电阻检查引脚约束片选信号只拉低了一次软件片选模式未手动释放在传输结束后的代码路径里重新写SSR寄存器释放片选多个从机互相干扰片选不生效导致总线冲突核实片选信号是否接到所有从机用示波器确认空闲时CS全部为高数据读写到寄存器时无响应地址映射错误检查Address Editor中分配的地址确认SDK里配置的基地址一致5.3 一个完整排查案例MISO全为高但有时能读到数据之前在某块载板上调一颗SPI DAC现象很怪异同一份代码复位后第一次写数据大概率成功第二次以后就失败并且MISO读到的一直是0xFF。从现象看像是时序退化于是先用ILA抓了一次正常情况下的波形CS、SCK、MOSI都没问题。奇怪的是MISO在DAC没有任何输出时确实是高电平这符合SPI三态总线空闲置高的惯例。继续往后查发现这块DAC在CS拉高之后有一个很短的“bus turnaround”时间窗口也就是说CS释放得太快DAC还没完全释放MISO总线主控就发起下一次传输导致主控端的输入采样点正好落在DAC三态输出的不确定区域。解决方法是把片选改成软件控制在两笔传输之间插入一小段延时保证MISO总线稳定后再发起新的SPI事务。这也解释了为什么恢复概率存在——它取决于上一次传输结束到下一次传输开始之间的间隔是否碰巧超过DAC的释放时间。这个案例说明SPI调试不能只看IP核本身配置还要结合具体芯片的时序要求来调整片选控制和传输节奏。硬件片选虽然省心但对这类对时序敏感的从机来说软件片选加延时反而更可靠。5.4 关于IP核版本和资料查询的几条经验Xilinx的文档体系比较庞大AXI Quad SPI的相关资料分散在PG153Product Guide以及各平台的Embedded Software Documentation里。我的经验是拿到一个新版本Vivado后先打开IP配置向导里的Datasheet链接确认当前版本支持的寄存器列表和引脚定义是否与旧版有差异。不同小版本的IP核在寄存器位定义上偶尔会有调整如果参考的是网上旧资料很容易踩坑。另外搜索问题时要多留意Xilinx官方论坛和GitHub上的Xilinx SDK代码很多异常波形都有现成的解释。但论坛方案不一定完全适合你的工程还是要回到自己的时序波形上验证。关于仿真Vivado的仿真环境对AXI Quad SPI支持得很好。建议在工程阶段先跑一遍行为仿真用逻辑分析仪脚本验证波形是否符合预期再进行板级调试。不过仿真通过并不代表硬件一定没问题尤其是信号完整性和片选时序这一类问题仿真里是看不出来的只能靠示波器和ILA实测。结尾的一点实际体会做了这么多SPI项目之后我最大的体会是AXI Quad SPI这个IP核本身并不复杂难点几乎都集中在“没看清参数含义”和“不够重视外设时序”这两个地方。特别是SPI模式、时钟分频、硬件片选与软件片选这几项配置之前一定要把数据手册和IP配置向导对照着看不要凭直觉选。另一个建议是调试顺序尽量循序渐进先把SCK和CS确认正常再验证单字节收发然后才上多字节、中断、DMA这些高级功能。每一步都确认了再往下走出问题时能快速缩小范围。如果你也正卡在某一个SPI通信环节不妨按这个思路把配置和波形逐步过一遍很多问题其实并不难解。
返回列表