--- name: fpga_update description: 通过网络远程升级 SMOD 板(192.168.1.143)上 ATSC 激励器工程的 FPGA 镜像——先用 Vivado + perl 生成 quickboot 的 _update.mcs,再用 TCP 1234 的私有协议把它流式烧进 SPI flash 的 update 槽。只要用户提到"远程升级"、"升级 mcs"、"把新 bit 升上去"、"update mcs"、"远程更新 FPGA"、"Xfpga_ru"、"生成 quickboot mcs"、"quickboot"、"RU_golden 之后怎么升级"、"192.168.1.143 升级",或者在 my_exciter 工程里跑完综合实现后想把结果固化到板子上,就必须用这个 skill——哪怕他们没说"skill"、没提 mcs,只说了"升上去"、"更新一下 FPGA"、"重新出个 mcs"。注意:这条路和网页上的 Remote Update 完全无关(那个只认 imx6 的 tar.gz 固件包)。JTAG 直烧打底镜像请改用 fpga_flash skill。 --- # SMOD FPGA 远程升级 把 `my_exciter` 工程实现出的 `.bit` 变成 quickboot 更新镜像,再通过网络烧进板子的 SPI flash。 板子必须**事先用 JTAG 烧过 `RU_golden`** 才支持远程升级 —— golden 提供上电时的兜底镜像和 IPROG 跳转头。没烧过 golden 就先走 `/fpga_flash`。 ## 先搞清楚你在走哪条路 这是最容易出错的地方,两条路互不相干: | | 这个 skill | 网页 Remote Update | |---|---|---| | 升什么 | **FPGA 镜像**(`.mcs`) | **imx6 固件包**(`.tar.gz`) | | 走哪 | TCP **1234**,私有帧协议 | HTTP `upload.cgi` → `check_package.sh` | 把 `.mcs` 传到网页上不会报错,但 `check_package.sh` 找不到 `*.tar.gz` 就会 `rm -rf /aw/upgrade/*` 把它删掉,什么都不会发生。原厂的 GUI 工具是 `tools/Xfpga_ru.exe`, 没有命令行接口;`scripts/fpga_ru.py` 是它的等价替代,而且会告诉你失败在第几帧。 ## 流程 ### 1. 生成 `_update.mcs` ```bash python scripts/gen_update_mcs.py # → bitstream/atsc_exciter_update.mcs python scripts/gen_update_mcs.py --tag smod_dpd # → bitstream/smod_dpd_update.mcs ``` 脚本按顺序跑项目里一直在用的那三个脚本(`post_bitstream.tcl` → `runScript_GenRu.tcl` → `MakeSpiFlashProgrammerMcsFiles_backup.pl -imagesize 64`),然后校验产物的起始地址是 `0x800000`(这等价于"确实是用 `-imagesize 64` 生成的")。 它还替你挡了一个坑:`runScript_GenRu.tcl` 里是 `glob *.bit`,而 `bitstream/` 目录里躺着二十几个 历史 `.bit`。脚本会把旧的临时挪走、跑完再放回去。**别绕过它去手动跑 tcl**,除非你已经清空了目录。 `-imagesize 64` 不要改。它和 `SpiFlashProgrammer.vhd` 里的 `cAddrUpdateStart/End` 是一对, 理由见 `references/quickboot.md`。 ### 2. 确认 ADPC 为 OFF(保险起见) DPD 的 sample&load 和升级数据流共用同一条 SPI1,而 FPGA 在升级期间会把 SPI1 上任何字都当成镜像数据。 这条路径没被验证过(三次连续升级成功那次 `adpc=0`),所以稳妥起见先关掉: ```bash # 登录后读 sys_cfg,第 3 个字段就是 adpc(0=OFF) curl -s -b "session=$SESS" "http://192.168.1.143/atsc_status_cfg.cgi?sys_cfg=&" ``` 网页账号是 **`anywavecom` / `anywavecom`**(不是 `root`;`root/anywavecom` 是 SSH 的)。 ### 3. 烧进去 ```bash python scripts/fpga_ru.py --mcs bitstream/atsc_exciter_update.mcs python scripts/fpga_ru.py --mcs ... --dry-run # 只校验文件,不碰硬件 ``` 先跑 `--dry-run`。它会检查行长、整行切分、EOF 记录、起始地址 —— 这些错误在真机上要等几分钟 才会以一个没有上下文的 `ABORT_ERROR` 暴露出来。 烧写要几分钟(8 MB,约 47600 帧)。**中途不要断电、不要重试。** 真断了也不会变砖:升级一开始就把 切换字擦掉了,冷启动会退回 golden。 成功后 app 会自动重启 imx6、复位 FPGA。然后**冷启动板子**(遵守 SMOD 上电时序)让 FPGA 从 flash 重新加载新镜像。 ### 4. 验证 抓 ILA、看 app 日志(`/var/module_log/imx_atsc_app.log`)、或者看频谱。 闭环调试交给 `fpga-imx6-flow` skill。 ## 排错 `fpga_ru.py` 会打印失败在哪一步、哪一帧、收到什么 ack。对着这张表看: - **擦除永不结束**(最容易误判的一个)→ 十有八九 programmer 已经报错退出了。 imx6 的 `POLL_ERASER` **不看 ERROR 位**,把"已失败"当成"还在擦",所以客户端只会一直等。 别信进度条,直接读设备日志: ```bash ssh -o HostKeyAlgorithms=+ssh-rsa root@192.168.1.143 \ "grep 'ing|end' /var/module_log/imx_atsc_app.log | tail -3" ``` `rstate=0x7`(ERROR=1, DONE=1, EraseOK=0)= programmer 中止;`rstate=0x0` 才是真的在擦, 擦完会看到 `rstate=0xc` + `UPSTATE_EARSE_END`。 历史上这里的元凶是 `smod_top.v` 没把 flash 的 SPI 引脚接出去,见 `references/protocol.md`。 - **`ABORT_ERROR` 出现在第一帧** → mcs 格式不对,先跑 `--dry-run`。 - **`ABORT_ERROR` 出现在数据流中后段** → 怀疑 SPI1 上有别人(DPD)在写,把 ADPC 关掉重试。 - **没有应答 / 超时** → app 的 `sys_ready` 还是 0,或者 app 没在跑。 `ssh root@192.168.1.143 'ps | grep atsc'`(SSH 密码 `anywavecom`,dropbear 老得很, 命令行 ssh 要加 `-o HostKeyAlgorithms=+ssh-rsa`)。 > 别用 `python ssh.py "tail -f ..."` 之类一次性读到 EOF 的封装去跟日志 —— 它永远不返回, > 什么也抓不到。升级完再 grep 日志文件即可,`log_printf` 已经落盘。 ## 不碰硬件地改协议客户端 `scripts/mock_device.py` 是照着 `tcp_task.c` 的状态机实现的假设备,包括那三个硬限制和 `':'` 重切分行为。改完 `fpga_ru.py` 先拿它跑一遍: ```bash python scripts/mock_device.py --port 12340 & python scripts/fpga_ru.py --mcs <某个>_update.mcs --host 127.0.0.1 --port 12340 --yes ``` 正确的一次会让 mock 收到**恰好 8388608 字节(8.000 MiB)**—— 正是 `cAddrUpdateEnd - cAddrUpdateStart`。 对不上就是客户端的分帧错了。`--not-ready-every N` / `--abort-at-frame N` 可以逼出重传和中止路径。 ## 参考 - `references/protocol.md` —— TCP 1234 帧格式、时序、三个硬限制、DPD 污染缺陷的完整证据链 - `references/quickboot.md` —— flash 布局、切换字兜底、为什么是 `-imagesize 64`、两条烧写路径的区别