target-gen 与 CMSIS-Pack
主线位置:命令主线之外,目标描述的来源。内置的 242 个 YAML 里有 125 个家族标着
generated_from_pack: true,是工作区里的target-gen从芯片厂商发布的 CMSIS-Pack 转出来的。本页按源码讲它怎么转;本机没有装target-gen,也不联网下载 pack,所以没有实际跑。
flowchart TB pack["CMSIS-Pack:.pack 是一个 zip"] --> pdsc[".pdsc:XML,器件、处理器、内存、算法"] pdsc --> dev["每个 device"] dev --> cores["processor:Cortex-M 型号变成 CoreType,AP 号"] dev --> mem["memory:变成 Ram、Nvm 区"] dev --> algo[".FLM:flash 算法 ELF"] algo --> fd["符号 FlashDevice:名字、地址、页大小、扇区表"] algo --> blob["PrgCode、PrgData 段:拼成一段指令"] algo --> syms["符号 Init、EraseSector、ProgramPage 等:入口偏移"] cores --> fam["ChipFamily:算法按名字去重"] mem --> fam fd --> fam blob --> fam syms --> fam fam --> yaml["serialize_to_yaml_string:写 YAML"]
五个子命令
target-gen/src/main.rs 的 TargetGen 枚举:
| 子命令 | 输入 | 干什么 | 要硬件 / 网络吗 |
|---|---|---|---|
pack | 一个 .pack 文件或解开的目录 | 生成该 pack 里所有家族的 YAML | 都不要 |
arm | 可选的 --filter | 从 ARM 的索引下载 pack 再生成;不给过滤条件时只处理 probe-rs 已支持的家族 | 要网络(fetch.rs 用 reqwest 取 https://www.keil.com/pack/index.pidx;子命令的帮助文字写的是 Keil.pidx,和代码不一致) |
elf | 自己编译的 flash 算法 ELF | 抽出一个 RawFlashAlgorithm,可以更新已有 YAML | 都不要 |
test | 模板 YAML、ELF | 生成 YAML 后在接着的板子上实际擦写一遍,可当 cargo runner | 要探针 |
reformat | YAML 文件或目录 | 读进来再按规范格式写回 | 都不要 |
main 第一行同样注册了 ESP 插件,所以 target-gen 也依赖 probe-rs-espressif(源码地图)。
pack:从 pdsc 到 ChipFamily
visit_file 把 .pack 当 zip 打开,找到里面的 .pdsc,用 cmsis-pack crate 解析成 Package,再交给 extract_families。后者对每个器件(generate.rs:71):
- 按 pdsc 里的
family名找或新建一个ChipFamily,generated_from_pack: true,pack_file_release记 pack 的最新版本号。 - 处理器变成核:
core_to_probe_core是一张表,M0 / M0+ →Armv6m,M3 →Armv7m,M4 / M7 →Armv7em,M23 / M33 / M55 / M85 / STAR-MC1 →Armv8m,其他核报「not yet supported」。注意 M23 是基线 Armv8-M,在这里和 M33 归成同一种核类型,和 Cortex-M 内核里armv8m.rs固定main = true是同一个问题的两端。AP 号按 pdsc 的写法变成!v1 n或!v2 地址;targetsel、jtag一律不填(源码 TODO:「parse scan chain from sdf」),所以 RP2040 这种多点 SWD 芯片的 YAML 是手写的。 - 内存变成
Ram/Nvm区,再打几个补丁(比如只有一个 RAM 区时确保它可执行,flash 算法要在里面跑)。 - 每个算法文件交给
process_flash_algo,结果按内容去重后放进家族的flash_algorithms,型号只记名字。pdsc 给了RAMstart时,把它加上算法头的最大长度作为固定加载地址。
一个细节:只处理已支持家族(arm 子命令不带过滤条件时)的分支,遇到第一个不认识的家族写的是 return Ok(()),而不是 continue;注释说「Skipping ...」,但从代码看,同一个 pack 里排在它后面(器件按名字排序)的器件也都不会处理。pack 子命令传的是 false,不受影响。实际影响未核实。
flash 算法 ELF 里有什么
CMSIS 的 flash 算法(.FLM)是一个普通的 ELF,约定是:
- 一个叫
FlashDevice的全局结构,160 字节加一张扇区表。FlashDevice::new按固定偏移读:偏移 2 起 128 字节是名字,132 是起始地址,136 是大小,140 是页大小,148 是擦除后的字节值,152、156 是编程和擦除超时;之后每 8 字节一项(扇区大小, 起始偏移),直到读到0xFFFFFFFF结束标记。 - 代码在
PrgCode段,数据在PrgData段(.bss也叫PrgData,类型是NOBITS)。AlgorithmBinary按PT_LOAD段把它们拼成一段连续的字节,就是 YAML 里那串 base64 的instructions。 - 函数入口是符号:
Init、UnInit、EraseChip、EraseSector、ProgramPage、Verify、BlankCheck。probe-rs 另外认ReadFlash、_SEGGER_RTT(算法里的 RTT 控制块,flash 算法的--read-flasher-rtt用它)、PAGE_BUFFER(固定的页缓冲区地址),以及 0.32 新加的VendorFunc_前缀(#3989):VendorFunc_read_flash_size变成vendor_functions里的read_flash_size,取代了以前专门为 ESP32 硬编码的那个函数。
extract_flash_algo 把符号地址减去代码段的起点,得到相对偏移。STM32F1 的算法生成出来是这样:
# probe-rs/targets/STM32F1_Series.yaml
- name: stm32f10x_128
description: STM32F10x Med-density Flash
default: true
instructions: ELUDRgAgREwgYERIYGBESGBgIEbAaRDwBA8I0UXyVVBATCBgBiBgYED2/3Cg...
pc_init: 0x1
pc_uninit: 0x33
pc_program_page: 0xbd
pc_erase_sector: 0x7f
pc_erase_all: 0x45
data_section_offset: 0x128
flash_properties:
address_range:
start: 0x8000000
end: 0x8020000
page_size: 0x400
erased_byte_value: 0xff
program_page_timeout: 100
erase_sector_timeout: 500
sectors:
- size: 0x400
address: 0x0
pc_init: 0x1 的最低位是 Thumb 位。下一部分的实验里,probe-rs 把这段代码装到 0x20000000,前面加 4 字节的断点头,于是 Init 的地址是 0x20000004 + 0x1 = 0x20000005,static_base 是 0x20000004 + 0x128 = 0x2000012c,和这里的偏移对得上(从镜像到烧录计划、flash 算法)。
elf:自己写的算法
厂商没有提供 CMSIS 算法、或者想用 Rust 重写一个更快的算法时,用 target-gen elf:它对一个自己编译的 ELF 走同样的 extract_flash_algo。加 --fixed-load-address 时要求代码、数据段在 RAM 里首尾相接,并把链接地址记成 load_address;否则当作位置无关代码,加载地址由 probe-rs 在烧录时选。cmd_elf 还会把结果和已有 YAML 里的算法合并、压缩重复项。写好之后用 test 子命令在真板子上擦写验证,这一步待实测。
小结
- target-gen 从 CMSIS-Pack 的 pdsc 里抽器件、核、内存和算法,生成 probe-rs 的 YAML;内置 242 个 YAML 中 125 个家族由它生成。
- 核类型靠一张 Cortex-M 型号表,M23 被归为
Armv8m;targetsel、JTAG 扫描链不从 pack 里取,多点 SWD 芯片的 YAML 要手写。 - flash 算法 ELF 的约定:
FlashDevice结构给几何参数,PrgCode/PrgData段拼成指令,Init/EraseSector/ProgramPage等符号给入口偏移;probe-rs 另认_SEGGER_RTT、PAGE_BUFFER、VendorFunc_*。 pack、elf、reformat离线可用,arm要联网,test要硬件。