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
122 lines
6.5 KiB
Markdown
122 lines
6.5 KiB
Markdown
---
|
||
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`、两条烧写路径的区别
|