简介面向嵌入式开发者的智多晶UART-FLASH设计方案聚焦通过UART接口实现Flash编程与固件在线升级适用于设备维护、日志存储等场景。压缩包约1.61MB共97个文件其中34个C头文件与21个C源文件构成完整工程代码另有汇编、Tcl脚本、hex/bin固件镜像、工程配置及串口在线升级说明文档覆盖从源码到烧录的完整链路。已有315人学习/下载。内容包含UART通信协议解析、Flash读写与擦除流程、CRC错误检测与重传机制、安全保护策略等细节并附带RTC固件、Flash扩展工具和串口调试助手方便开发者对照验证。通过该资源可快速掌握UART-FLASH的初始化、命令交互、数据传输与固件切换方法为嵌入式系统的远程升级与数据管理提供可直接落地的工程参考。 有段时间我经常跑外地调试设备。设备用的是智多晶的FPGA程序放在外挂的SPI Flash里。出门在外最怕的就是改完逻辑发现没带下载器或者下载器驱动在客户电脑上装不起来。后来我把UART-FLASH这套烧写方案在项目里正式落地——只用一根USB转串口线就能完成对FPGA外部配置Flash的擦除、写入和校验甚至能支撑现场和远程的固件升级。这套方案适合所有用智多晶FPGA做产品、又经常需要跟现场打交道的工程师尤其是设备已经部署出去、不方便开盖插JTAG的场景几乎是刚需。下面把我在智多晶平台上从方案设计到实际落地、再到踩坑修复的整个过程完整讲一遍。内容不涉及厂商私有协议属于通用做法照着搭就能跑。1. 为什么要把烧写链路从JTAG换成串口一次现场事故带来的需求先说我自己碰到的真实场景。那次去客户现场改一版逻辑板子装在一个密封壳里JTAG座子在PCB内侧要拆四颗螺丝排线还压在电源模块底下。客户只留了一个调试串口出来下载器又没带。最后只能让同事远程把新bitstream发过来我用串口线连上板子配合一个临时写的Bootloader把数据写进Flash板子重启后新逻辑直接跑起来。从那以后UART-FLASH就从“临时救火工具”变成了我项目里的标准配置。1.1 UART-FLASH到底解决了哪几类问题第一类是现场维护。产品交付后外壳基本不拆JTAG座子往往被结构件挡住但串口调试口一般会预留。有了UART-FLASH现场升级只需要一条串口线和一台笔记本不需要开盖、不需要专用下载器。第二类是远程升级。串口后面接一个4G透传模块或者WiFi串口服务器PC端工具在办公室就能把固件推过去。配合Bootloader里的双镜像机制升级失败还能自动回退设备不会变砖。第三类是产线批量烧写。很多生产工装只有串口接口一台工装拖好几块板子轮流烧比每次插JTAG效率高得多。产线上对速度更敏感可以把波特率拉高后面我会专门算一笔时间账。1.2 这条链路和JTAG烧写的本质区别JTAG烧写走的是FPGA芯片内部的调试访问端口由下载器直接控制Flash本质上是“外部设备操作FPGA再操作Flash”。UART-FLASH则走的是“上位机 → UART → FPGA内部Bootloader逻辑 → SPI Flash”这条路径。Bootloader可以是芯片出厂固化的也可以是我们自己用逻辑写进去的。智多晶平台上并不是所有型号都内置了串口Bootloader所以更通用的做法是先用JTAG往FPGA里烧一个“烧写器工程”也叫Golden Image这个工程上电后主动从Flash固定地址加载用户逻辑同时监听串口命令。后续所有升级都走串口不再需要JTAG。这套方案完全用FPGA普通逻辑资源和外部SPI Flash实现不依赖芯片私有功能换到其他FPGA平台也能平移。2. 方案选型与硬件链路从串口到Flash的每一步UART-FLASH不是光写逻辑就完事硬件链路先得把基础打对。链路从上到下依次是PC端工具 → USB转串口芯片 → FPGA的UART引脚 → 内部Bootloader → SPI控制器 → 外部SPI Flash。任何一个环节出问题烧写都会失败。2.1 器件选型Flash、串口芯片和电平标准SPI Flash我优先选Winbond W25Q系列或GigaDevice GD25Q系列容量按项目bitstream大小的4到8倍留。比如用户工程bitstream约256KBBootloader占一个镜像区用户镜像占一个区再加一个出厂备份区和配置参数区总共至少要1MB以上所以W25Q648MB属于非常稳妥的起步选择。选Flash时注意区分标准SPI和QSPI第一版方案建议只用标准SPI四线SCK、CS_N、MOSI、MISO兼容性和稳定性最好等跑通后再考虑上QSPI提速。UART接口这边如果板子上有USB转串口芯片CH340、CP2102、FT232都可以直接连FPGA的UART_RX/UART_TX如果是直连TTL电平则需要自己接一个USB转TTL小板。要注意的是FPGA的UART引脚和Flash引脚最好都放在同一个IO Bank且该Bank的VCCO电压一致。我见过有人把Flash放在3.3V的BankUART放在1.8V的Bank结果串口数据一直乱码排查半天才发现是电平域不统一导致的问题。2.2 引脚分配与PCB走线需要注意的细节SPI四根信号线尽量短走线长度差控制在几厘米以内避免高速通信时时钟和数据偏移过大。CS_N、MISO、MOSI上加上拉电阻一般4.7kΩ到10kΩ。Flash在FPGA未配置期间引脚是高阻态不加下拉或上拉容易受到干扰上电瞬间可能误触发写操作。配置模式引脚要预留拨码开关或0欧电阻位。FPGA上电时根据这些引脚的电平决定从哪个接口加载配置调试阶段方便在JTAG模式和主SPI模式之间切换。具体引脚命名和组合逻辑要查智多晶对应型号的手册不同器件定义不一样千万别想当然照搬其他厂家的习惯。串口的TX/RX如果走线较长加TVS管防静电。现场设备经常在恶劣环境里插拔串口线不加保护容易打坏FPGA引脚。2.3 一个容易被忽略的点Flash的IO电压版本Flash的VCC和IO电压必须匹配FPGA的Bank电压。3.3V FPGA配3.3V Flash是最常见的组合但如果你用的是1.8V Bank就得选W25Q64JW这类1.8V版本。我之前把3.3V Flash接到1.8V Bank上逻辑分析仪抓波形看着完全正常但Flash就是写不进去最后查手册才发现Flash的IO识别电平下限是2.0V左右1.8V信号根本达不到高电平阈值。这种问题不写进经验帖靠现场排查真的能卡一整天。3. Bootloader工程的逻辑实现串口协议与Flash控制状态机Bootloader是整个UART-FLASH方案的核心。它要做的事情很纯粹上电后等待串口命令收到合法命令后对Flash做擦除、写入、校验最后跳转加载用户逻辑。代码量不大但协议设计和状态机健壮性直接决定现场升级的成败。3.1 串口通信帧结构设计我用的帧格式如下每一帧固定以帧头开始帧尾结束中间所有字段参与CRC32校验。CRC用CRC-32/MPEG-2标准FPGA里用LFSR实现PC端用软件算两边对齐。字段长度字节说明帧头2固定0xAA 0x55命令字1握手/擦除/写页/读校验/跳转/状态查询地址4大端序Flash目标地址长度2数据区长度最大256数据0-256写页命令时携带一页数据CRC324对上述所有字段计算帧尾2固定0x0D 0x0A握手流程是PC发送握手帧Bootloader收到后回ACK和设备版本号如果CRC不对则回NAKPC端超时1秒重发。这个设计能快速判断串口链路是否正常避免一上来就盲目擦写Flash。3.2 SPI Flash操作状态机的关键设计Bootloader内部是一套典型的状态机IDLE → CMD_DECODE → FLASH_ERASE → WAIT_ERASE_BUSY → PAGE_PROGRAM → WAIT_PROGRAM_BUSY → VERIFY → ACK → IDLE每个环节都有几个硬性要求写Flash前必须先发送写使能命令0x06否则Flash直接忽略写操作。擦除和页编程完成后必须轮询Flash状态寄存器0x05的BUSY位直到bit0变0。不能只靠固定延时因为Flash擦除时间受温度和芯片个体差异影响很大。页编程一次最多写256字节跨页必须拆包。发0x02命令、24位地址、数据等待BUSY位结束后再发下一包。全程使用SPI Mode 0CPOL0CPHA0这是绝大多数SPI Flash和FPGA的SPI控制器默认支持的模式通用性最好。我把写数据路径上的FIFO深度设成512字节PC端按256字节一页发送FPGA收到一页后写入Flash写完回ACK再发下一页。这样即使UART中断响应不及时数据也不会溢出丢失。3.3 校验策略不能只依赖CRC每页数据写入前Bootloader就对这一页做CRC32实时累加同时PC端也在算同样的值。所有页写完后再做一次整体校验PC端发送读校验命令Bootloader把Flash中的数据读回来做CRC比对结果回传给PC两边一致才算升级成功。更稳妥的做法是再加一道“启动标志”机制Flash的配置区里固定放一个标志字升级开始时把标志写成0xA5A5A5A5表示升级中全部完成后改写成0x5A5A5A5A表示完成。上电时Bootloader检测到这个标志不对就认为上次升级异常中断自动进入等待重新升级的状态防止设备启动一个半截写入的镜像。4. 烧写时效与可靠性波特率、超时和断点续传UART-FLASH方案经常被质疑的一点是速度慢。确实是慢但慢不等于不能用关键在于把时间账算清楚并且把可靠性做足。4.1 115200波特率下烧写256KB需要多久8N1格式下一个字节要传10bit1起始位8数据位1停止位。115200bps的有效数据吞吐率是11520字节/秒。256KB的bitstream理论传输时间是256 × 1024 / 11520 ≈ 22.8秒再加上帧头、地址、长度、CRC这些协议开销以及每页等待ACK的往返时间实际传输大概要30到40秒。如果之前没擦除Flash还要加上擦除时间。W25Q64扇区擦除典型45ms全片擦除大概几十秒。所以整个升级流程压在60到90秒是比较真实的预期。如果嫌慢可以把波特率提到460800或者921600。USB转串口芯片配合质量好一点的线材跑921600很常见传输部分能从30秒压到4到6秒但Flash擦除时间省不掉。产线批量烧写时我一般用921600现场调试用115200稳定性优先。4.2 可靠性的几个工程细节每个数据包带序号PC端重发时会带上包序号Bootloader通过序号判断是不是重复包避免重复写入。超时策略分两级PC端发包后500ms内没收到ACK就重发Bootloader内部接收状态机空闲超时则回到IDLE等待下一帧帧头防止半包数据卡死链路。擦除操作放在写数据之前统一做不要写一页擦一页否则擦除时间会拖慢整体流程。如果对掉电特别敏感可以在Flash里做一个“临时缓冲镜像区”新镜像先写到临时区全部校验通过后再用一条“切换命令”把启动标志指向新镜像地址整个过程中旧镜像始终保留。这个做法在远程升级场景里价值很大。5. 上电加载流程与镜像回退机制烧写只是手段最终目的是让FPGA上电后能从Flash正确加载用户逻辑。这个过程看起来简单实际有坑。5.1 配置模式引脚与主SPI加载FPGA上电后会先读配置模式引脚的电平决定从哪个接口读取配置数据。UART-FLASH方案里Bootloader完成升级后会把FPGA重新配置一次此时需要让器件进入主SPI模式由FPGA作为SPI主机主动从外部Flash读取用户镜像。智多晶具体型号的配置模式引脚命名和电平组合在数据手册里有专门一节做PCB时必须认真核对。我在第一个版本里直接把某知名厂家的引脚映射习惯套过来结果FPGA一直进不了主SPI模式后来仔细查手册才发现引脚号和有效电平完全不同。这一步没有任何捷径只能一页一页看手册。5.2 Flash布局给双镜像和配置区留位置推荐的分区方式如下地址范围内容说明0x000000 - 0x0FFFFFBootloaderGolden Image上电后最先加载包含UART-FLASH逻辑0x100000 - 0x1FFFFF用户镜像A正常运行的应用逻辑0x200000 - 0x2FFFFF出厂固件/备份镜像用于紧急回退0x3F0000 - 0x3FFFFF配置区启动标志、版本号、CRC等Bootloader的体积通常几十KB用户镜像根据资源使用情况可能几百KB。Flash容量按我说的“4到8倍”预留就是为了给多个镜像和配置区腾出空间后续扩展也不至于推倒重来。5.3 回退逻辑别让设备变砖Bootloader上电后先读配置区的启动标志和当前应启动的镜像地址。如果用户镜像CRC校验失败或者启动标志显示“升级未完成”就做两件事如果存在出厂固件且出厂固件CRC有效则直接跳转到出厂固件地址启动否则停留在Bootloader模式通过串口等待重新升级。我见过很多公司做远程升级不考虑回退结果现场升级升到一半断电设备从此变砖只能寄回厂家拆壳用JTAG刷。有了这一层回退机制最坏情况也只是设备停在出厂版本设备还能工作后面找时间重新升级一次就行。6. 实测踩坑记录我在智多晶平台上踩过的五个坑这套方案写出来看着顺畅实际落地过程一波三折。下面这几个坑我觉得值得单独列出来供后面做的人参考。6.1 串口丢字节问题出在波特率误差第一版联调时PC端发给Bootloader的握手包经常没响应用串口助手看发出去的字节都正常但FPGA端收到的是乱码。排查方法是用逻辑分析仪抓UART波形测出实际波特率是114300左右跟标称115200差了将近0.8%。USB转串口芯片在不同线材和驱动状态下波特率误差不是固定值。后来我把Bootloader里的UART接收改成按实际测量值重新计算分频参数同时换上质量好一点的串口线问题才彻底解决。这也是为什么我建议现场维护时优先用115200劣质线材高波特率下丢字节概率会明显变大。6.2 Flash擦除超时固定延时不可靠刚开始图省事擦除命令发完后直接延时500ms再继续。结果有批Flash在低温环境下全片擦除要40多秒Bootloader早就超时返回错误了。后来改成轮询状态寄存器BUSY位擦多久都不怕。这个改动同时对不同批次Flash的个体差异免疫属于治本的做法。6.3 校验全过但加载失败字节序的锅这个坑是最隐蔽的。写页命令读回校验CRC全部正确但复位后FPGA就是起不来用JTAG读Flash内容跟PC端发的原始文件一对比发现数据字节没问题但每个字节内部的bit顺序反了。原因在于我当时用的Flash和FPGA的SPI控制器在位序定义上不一致一个按MSB first传输一个按LSB first传输。解决方法是写Flash前在Bootloader里把每个字节做一次位反转或者直接在PC端工具里先把数据按位反转再发送。遇到这类问题先用JTAG把Flash原始内容读出来跟源文件做二进制对比很快就能定位。6.4 SPI时钟分频参数写错Bootloader里SPI时钟由FPGA系统时钟分频产生。第一次我把分频参数算错SCK跑到了接近50MHz读操作勉强正常写操作时不时失败。Flash的数据手册里明确写着读时钟和写时钟的频率上限不同扇区擦除、写状态寄存器这类命令保守一点最好压在1MHz以下。后来我把SPI时钟统一分到10MHz以内读写都稳定了。这里没有性能焦虑的必要Bootloader的瓶颈本来就在串口SCK快一点慢一点都不影响整体时间。6.5 电平不匹配导致Flash写不进去前面硬件章节提到的1.8V Bank配3.3V Flash的问题实际定位过程很费劲。逻辑分析仪抓波形看起来都对CLK、MOSI、CS都正常Flash就是不响应写命令。最后查Flash手册发现是电平阈值问题。这个经验我建议所有人在画板阶段就确认一遍FPGA的Bank电压、Flash的IO电压、串口芯片的电平三者必须在同一电压域或者中间加电平转换芯片。写在后面的一些体会这套UART-FLASH方案我在智多晶平台上的几个项目里跑了一年多现场升级成功率明显比之前拆壳插JTAG高很多。关键点其实不在技术本身而在设计阶段就要把串口引出、配置模式引脚、Flash分区、Bootloader和用户镜像分开管理这些都规划好。Bootloader代码尽量少动每次只升级用户镜像稳定性会好很多。如果第一次接触这个方案建议先用最小验证板把串口协议和Flash状态机跑通再往正式项目里集成能省掉大量反复排查的时间。最后提醒一句别小看那几页数据手册里关于配置模式和Flash时序的表格我的大部分坑都是栽在“想当然”这三个字上。本文还有配套的精品资源点击获取