Files
skills-win/fpga_update/SKILL.md
T
boweiandClaude Opus 5 42edf13936 sync: Shanghai-U9 2026-09-03 硬编码审计清掉 0819/0826 挂了两轮的文档类遗留
smod_atsc_dev(SKILL.md / references/imx6.md / references/fpga.md)、
fpga_update(SKILL.md / references/protocol.md)、fpga-imx6-flow(SKILL.md)
正文表格与 shell 示例里的 E:\Xilinx_design\smod\SMOD_ATSC\... 和 192.168.1.143
改成引用 skills_env.md 的 SMOD_REPO_ROOT / SMOD_BOARD_IP;shell 块统一用 $BOARD
并在块内注明取值出处。13 处锚点全部唯一命中,无行为改动。

刻意未改:各 skill 的 YAML description 里的同名字面量(那是触发关键词,不是
脚本取值,改了降低匹配率);脚本里 _env_lookup(KEY, 字面量) 形式的回落默认值;
fpga_flash/SKILL.md 的 SMOD_DTMB_CG 命令示例(属另一个工程,无对应 env 键)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018FykpdV2Y4RUWXU5PtoLqh
2026-09-03 02:52:05 +08:00

122 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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
# BOARD = ~\skills_env.md 的 SMOD_BOARD_IP
# 登录后读 sys_cfg,第 3 个字段就是 adpc(0=OFF)
curl -s -b "session=$SESS" "http://$BOARD/atsc_status_cfg.cgi?sys_cfg=&"
```
网页账号见 `~\skills_env.md` 的 `SMOD_WEB_USER` / `SMOD_WEB_PASS`(不是 `root`;SSH 凭据是 `SMOD_SSH_USER` / `SMOD_SSH_PASS`)。
### 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
# BOARD = ~\skills_env.md 的 SMOD_BOARD_IP
ssh -o HostKeyAlgorithms=+ssh-rsa root@$BOARD \
"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@$BOARD 'ps | grep atsc'`(`$BOARD` = `SMOD_BOARD_IP`、SSH 密码见 `~\skills_env.md` 的 `SMOD_SSH_PASS`,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`、两条烧写路径的区别