# Flash 布局与 QuickBoot 兜底 板上 flash:Winbond **w25q128bv**,128 Mbit = 16 MB,SPIx1,**24-bit 地址**,64 KB sector / 4 KB subsector。 ## 三个地址,一个都不能记错 来自 `my_exciter.srcs/sources_1/quickboot/SpiFlashProgrammer.vhd`(**RU_golden 和 my_exciter 引用的是同一份文件**, 见 `RU_golden.xpr` 里的 `$PPRDIR/../my_exciter/.../SpiFlashProgrammer.vhd`,所以两个工程的常数天然一致): ```vhdl cAddrUpdateStart = X"00400000" -- update 镜像起点 cAddrUpdateEnd = X"00C00000" -- update 镜像终点+1 → 槽宽 8 MB cAddrSwitchWordN25Q = X"00000FFC" -- 切换字 cAddrWidth = 24 -- 和 w25q128bv 匹配 ``` flash 实际布局(从 `golden_design_initial.mcs` 解码得到,不是推测): ``` 0x000000..0x000FFB FF FF ... 空 0x000FFC 00 99 55 66 切换字(AA 被写成 00 = 无效) 0x001000 20000000 NOOP 30020001 00400000 写 WBSTAR = 0x400000 30008001 0000000F 写 CMD = IPROG → 跳到 update 镜像 0x001050 AA995566 golden bitstream 自己的同步字 ..0x3FFFFF golden bitstream(RU_golden_top.bit,3.4 MB) 0x400000..0xBFFFFF update 镜像(8 MB,日常远程升级写这里) 0xC00000..0xFFFFFF 未用 ``` **兜底机制**:上电时 FPGA 从地址 0 扫描同步字。 - 切换字 = `AA995566` → 在 0xFFC 同步 → 执行 0x1000 处的 IPROG → 加载 update 镜像。 - 切换字 ≠ `AA995566`(擦除后是 `FFFFFFFF`)→ 0xFFC 不同步 → 继续扫 → 在 0x1050 撞上 golden 的同步字 → **加载 golden**。 远程升级一开始就把切换字所在的 subsector 擦掉(`sProgrammerEraseSwitchWord`,SSE 命令, 擦 `0x000000..0x000FFF`),写完校验通过后再把切换字写回去。所以**升级中途断电只会退回 golden,不会变砖**。 > subsector 是 4 KB,擦 0xFFC 只波及 `0x000000..0x000FFF`。IPROG 头在 `0x001000`,属于下一个 > subsector,**不会被擦到**。golden 是安全的 —— 如果怀疑"升级把 golden 弄坏了",可以排除这条。 ## 为什么 `-imagesize 64` perl 脚本的模型是"golden 和 update 一样大、update 紧跟 golden": `imageSizeBytes = imagesize(Mbit) × 131072`,`updateStartAddr = imageSizeBytes`。 - **golden**(`MakeSpiFlashProgrammerOnlyInitMcs.pl -imagesize 32`)→ 4 MB → IPROG 里的 WBSTAR = `0x400000`,和 `cAddrUpdateStart` 对上。日志见 `RU_golden/bitstream/gen_initial.log`。 - **update**(`MakeSpiFlashProgrammerMcsFiles_backup.pl -imagesize 64`)→ 8 MB → 负载 `pad(bitstream, 0x7FFFFC) + crc32` 正好 8 MB,和槽宽 `0xC00000-0x400000` 对上。 两个 imagesize 不一样是**故意的**(VHDL 注释里的 "Plan B: golden enlarged to 4MB"),别去"统一"它们。 `_update.mcs` 里的 Intel-HEX 地址写的是 `0x800000`,看起来和 `cAddrUpdateStart=0x400000` 矛盾 —— **它不矛盾,因为地址被忽略**。imx6 的 `recv_upgrade_data_parse()` 只从 `:10xxxx00<32 hex>` 记录里抠出 16 个数据字节,扩展线性地址记录 `:02000004` 直接当"中间控制字段"丢掉。FPGA 从 `cAddrUpdateStart` 顺序写。所以对远程升级而言,`_update.mcs` 只有**负载长度**和**CRC**是有意义的。 校验 `_update.mcs` 的起始地址是 `0x800000`,本质上是在校验"这个文件是用 `-imagesize 64` 生成的"。 ## 两条烧写路径,别搞混 | | JTAG 直烧 | 网络远程升级 | |---|---|---| | 文件 | `RU_golden/bitstream/golden_design_initial.mcs` | `bitstream/_update.mcs` | | 工具 | `/fpga_flash` skill(flash 型号 `w25q128bv`) | `scripts/fpga_ru.py` | | 写到 | `0x000000..0x3FFFFF` | `0x400000..0xBFFFFF` | | 何时用 | 首次装机、板子变砖、换打底镜像 | 日常迭代 | `_update.mcs` **不能**拿去 JTAG 烧写:它只是 8 MB 裸数据 + CRC,没有 bitstream 头。 反过来 `_initial.mcs` 也不能拿去远程升级。 严守上电时序:断电 → 断 USB-Blast → 上电 → 连 USB-Blast → 烧写 → 断电 → 断 USB-Blast → 上电 → 连 USB-Blast。 ## 调试期的第三条路 `program_device` 直接把 `.bit` 下到 FPGA,掉电即失,不碰 flash,配 `.ltx` 还能抓 ILA。 迭代验证用它最快,功能确认后再走远程升级固化。固化用的 bit 要**去掉 ILA 重新实现**。