如何使用 svd2rust 生成 PAC?

解读

在国内嵌入式 Rust 岗位面试中,“能不能把厂商提供的 SVD 文件变成可编译的 PAC” 是区分“写过裸机 Rust”与“只写过 std 层” 的试金石。
面试官真正想听的是:

  1. 完整跑通过一次 svd2rust 工具链,知道每一步生成的产物与潜在坑点;
  2. 能在 CI 或 Makefile 里固化流程,而不是手动点点点;
  3. 对生成的 unsafe 边界有认知,能在后续 HAL 层做好封装。
    回答时务必体现“工程落地”与“安全责任”两点,避免只背命令。

知识点

  • SVD(System View Description):ARM Cortex-M 生态的标准 XML,描述片上外设的地址偏移、位域、中断号等。
  • PAC(Peripheral Access Crate):svd2rust 生成的底层 crate,为每个外设提供类型安全的寄存器访问 API,零运行时开销
  • svd2rust 工作流
    1. 校验 SVD → 2. 生成 lib.rs → 3. 依赖 cortex-m 与 bare-metal → 4. 用 form 模块打散 → 5. cargo fmt + clippy 修复 warning。
  • 关键编译标志--target thumbv7em-none-eabihf-Z build-std=core;国内很多候选人因忘记加 target 而被现场 pass。
  • 常见坑
    – 厂商 SVD 里寄存器重叠位域命名重复,需先拿 svdtools patch 修;
    – 中断枚举值冲突,需手动在 device.x 里重定向;
    – 生成的代码体积过大,需在 Cargo.toml 里开 features=["rt"] 并做 linker-script 裁剪。
  • 国内加速:在 ~/.cargo/config 里换中科大镜像,否则 cargo fetch 阶段就超时。
  • 法律合规:部分国产 MCU 的 SVD 文件含 NDA 条款,面试时可提及“已在公司内网搭建私有 registry,避免外泄”,体现职业操守。

答案

  1. 环境准备

    # 国内镜像加速
    export CARGO_NET_GIT_FETCH_WITH_CLI=true
    rustup target add thumbv7em-none-eabihf
    cargo install svd2rust form rustfmt
    
  2. 校验并修补 SVD

    # 以某国产 CH32V307 为例
    svdtools patch ch32v307.yaml   # 提前写好的 YAML 补丁,解决重名位域
    
  3. 生成 PAC

    svd2rust -i ch32v307.svd.patched \
             --target cortex-m \
             --generic_mod \
             --atomics \
             --keep_list
    form -i lib.rs -o src
    rm lib.rs
    cargo fmt
    
  4. 补全 Cargo.toml

    [package]
    name    = "ch32v307-pac"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    cortex-m     = "0.7"
    vcell        = "0.1"
    bare-metal   = "1.0"
    critical-section = "1.1"
    
    [features]
    rt = ["cortex-m-rt/device"]
    
  5. 验证

    cargo build --target thumbv7em-none-eabihf -Z build-std=core
    # 确保 .a 文件体积 < 200 KB,否则开 lto = true
    
  6. 发布到内部 git 仓库

    git tag 0.1.0
    cargo publish --registry=company-index
    

    至此,PAC 已可供 HAL 层依赖,并可在 CI 中 cargo install --version 0.1.0 ch32v307-pac 直接拉取。

拓展思考

  • 如何持续集成:在 GitHub Actions 或 Gitee Go 里,把“svd 更新 → patch → svd2rust → 版本号自增 → 发布”做成一条流水线,每次厂商发新 SVD 后 10 分钟内自动出包
  • 安全责任边界:PAC 层允许 unsafe 操作寄存器,HAL 层需用零开销抽象unsafe 限定在模块内部,并对外提供 &mut self 独占访问,杜绝多线程数据竞争
  • 与 embassy 集成: embassy 的 embassy-stm32 已把 PAC 作为子模块,国内可借鉴其 meta/Cargo.toml 模板,svd2rust --pacs 一次性生成多系列 PAC,减少重复劳动。
  • 法律与合规:若 SVD 含 NDA,需在 crate 的 README 顶部加“本 crate 仅授权公司内部使用,禁止二次分发”,并在 Cargo.toml 里把 publish = false避免法务风险