PBP 运行时:pbp_cfg.bin 解析全流程

从编辑 JSON 到 PBP 运行时解析 的顺序,把 pbp_cfg.bin 逐步说清楚(工具链以 tools/scripts/mk_private_resource.py 为准)。


总览:pbp_cfg.bin 是什么

pbp_cfg.bin 不是可执行代码,而是给 闭源 d13x.pbp 读的 私有配置容器 :一串按类型拼接的 数据段(section),每段格式统一为:

text 复制代码
+0x00  u32  data_type   // 段类型魔数,如 0x41490006 = PSRAM
+0x04  u32  data_len    // 本段 payload 长度(不含这 8 字节头)
+0x08  ...  payload     // 具体结构,依类型而定
...
最后一段: data_type = 0x4149FFFF, data_len = 0  (结束标记)

PBP 启动时由 BROM/镜像头得知 private 数据在 RAM 中的地址 (常通过寄存器 a1 传给后续阶段),再按 data_type 依次解析各段。


Step 0:涉及哪些文件

文件 角色
target/<chip>/<board>/pack/pbp_cfg.json 人工维护:PSRAM 参数、UART、JTAG、升级脚等
output/.../partition.json 编译时自动生成 (来自 image_cfg.json
tools/scripts/mk_private_resource.py 生成 pbp_cfg.bin
tools/scripts/mk_prebootprog.py 可选:给 private2 包 PBP2 头(多数板子用 private 不会改 bin)
tools/scripts/mk_image.py pbp_cfg.bin 打进 pbp_ext.aic / loader.aic
d13x.pbp PBP 程序本体 (与 pbp_cfg.bin 分开)

以你打开的 demo68-nor 为例:image_cfg.json"private": "pbp_cfg.bin""pbp": "d13x.pbp" 成对出现。


Step 1:何时、谁触发编译

RT-Thread 应用 (非 baremetal bootloader)时,aic_build.py 在打包镜像前执行:

text 复制代码
1) gen_partition_table.py
     image_cfg.json  →  output/.../partition.json
                      +  partition_table.h

2) mk_private_resource.py
     -l pbp_cfg.json,partition.json  →  pbp_cfg.bin

3) (若 image_cfg 含 private2)mk_prebootprog.py -m PDAT ...
     多数 d13x 板只用 private,此步跳过

4) mk_image.py
     生成 pbp_ext.aic、loader.aic、bootloader.aic ...

对应命令等价于:

bash 复制代码
python3 tools/scripts/mk_private_resource.py \
  -v -l target/d13x/demo68-nor/pack/pbp_cfg.json,output/.../partition.json \
  -o output/.../pbp_cfg.bin

-l 表示 多个 JSON 合并(后写的 key 会覆盖先前的同名 key)。


Step 2:解析 pbp_cfg.json

parse_private_data_cfg() 做三件事:

  1. 读文件,去掉 // 注释行或行尾注释
  2. 去掉 JSON 尾逗号(,}}
  3. json.loads(..., object_pairs_hook=OrderedDict) 保持字段顺序

合并后顶层 key 典型顺序(demo68-nor / demo88-nand):

text 复制代码
"psram"        ← 来自 pbp_cfg.json
"system"       ← 来自 pbp_cfg.json
"partitions"   ← 来自 partition.json(编译生成)

(若还有 dram 段,则是 D21x 等 DDR 方案,d13x 用 psram。)


Step 3:partition.json 如何进 bin

gen_partition_table.py 根据 image_cfg.jsonspi-nor / spi-nandpartitions 生成,例如 demo68-nor:

json 复制代码
{
  "partitions": {
    "mtd": "spi0:512k(spl),128k(env),...(os),...(rodata),...(data)",
    "type": ["mtd"]
  }
}

gen_partition_table()gen_bytes_of_part_str() 把分区表压成 ASCII 字符串

text 复制代码
spi0:512k(spl),128k(env),2m(os),12m(rodata),1m(data)

再写入 PARTITION 段 (见 Step 4.6)。

这样 PBP / Bootloader 早期就能按 mtd 字符串 访问 Flash,而不依赖 OS 已启动。


Step 4:按顺序生成各数据段(核心)

gen_private_data(cfg)顶层 key 顺序 调用各 gen_*,最后 gen_end_flag()

段类型常量(小端 u32):

JSON 顶层/子项
DATA_SECT_TYPE_DRAM 0x41490001 "dram"(D21x DDR)
DATA_SECT_TYPE_SYS_UART 0x41490002 system.uart
DATA_SECT_TYPE_SYS_JTAG 0x41490003 system.jtag
DATA_SECT_TYPE_SYS_UPGMODE 0x41490004 system.upgmode
DATA_SECT_TYPE_PARTITION 0x41490005 "partitions"
DATA_SECT_TYPE_PSRAM 0x41490006 "psram"
DATA_SECT_TYPE_SYS_REGCFG 0x41490007 system.regcfg
DATA_SECT_TYPE_END 0x4149FFFF 固定结尾

下面按 d13x 常见顺序 说明(psramsystempartitions)。


Step 4.1 --- PSRAM 段(0x41490006

JSONpbp_cfg.json"psram""cfg0", "cfg1", ...(多套颗粒,PBP 用 getid 匹配)

二进制布局

text 复制代码
u32  0x41490006          // type
u32  payload_len        // 下面 payload 字节数
u32  entry_cnt           // cfg 个数(如 4)
重复 entry_cnt 次 {
    struct psram {       // 每套 42 个 u32 = 168 字节
        common[15]      // clock, cs0/1_pins, xspi_*, psram_cfg*, iocfg*
        reset[2]        // proto, buf
        getid[3]        // proto, id, buf
        init[8]         // proto0..3, buf0..3
        xip_cfg[4]      // wr/rd proto/buf
        backup[10]      // buf0..9(training 等)
    }
}

以 cfg0(APS3208K 8M)为例,JSON 字段如何进 bin

JSON 路径 含义 写入字段
common.clock XSPI/PSRAM 时钟 Hz 198000000
common.xspi_ctl/tcr/cfg/ldo 控制器寄存器初值 十六进制串 → u32
common.psram_cfg0/1 PSRAM 控制器配置 u32
common.xspi_cs*_iocfg* 引脚驱动/时序 u32
reset.proto/buf 复位序列 SPI 协议字 + 数据
getid.proto/id/buf 读 ID,与硬件颗粒比对 id: 0x80c980c9
init.proto0..3 初始化命令序列 注释里 cmd/dummy/addr/len
xip_cfg.* XIP 读写协议 可选执行态
backup.buf0..9 Training 图案/ hold 参数 0xAA55AA55

缺省字段在工具里多为 0xFFFFFFFF(见 get_bytes_by_str(..., "0xFFFFFFFF"))。

PBP 运行时逻辑(概念)


读 PSRAM 段
entry_cnt = N
对 cfg0..cfgN-1 发 getid
ID 匹配?
reset → init → xip_cfg → backup training
PSRAM 可用 @ 0x40000000 一带


Step 4.2 --- SYSTEM:upgmode(0x41490004

JSONsystem.upgmode

Payload(6×u32,共 24 字节 + 8 字节头):

字段 demo88-nand 示例 作用
upgmode_pin_cfg_reg/val 0x18700080 / 0x10321 配置 PA0 等为升级检测脚
upgmode_pin_input_reg 0x18700000 GPIO 输入寄存器
upgmode_pin_input_msk/val 0x1 / 0x0 检测电平
upgmode_pin_pullup_dly 500 上拉稳定延时 µs

upgmode_pin_cfg_reg = "0"关闭 PBP 内 bootpin 检测(JSON 注释)。


Step 4.3 --- SYSTEM:uart(0x41490002

JSONsystem.uart → 可多个子项(通常只有 "main"

每个 UART 配置 5×u32

字段 含义
uart_id 0=UART0, 1=UART1, ...
uart_tx_pin_cfg_reg/val Pinmux 寄存器地址与写入值
uart_rx_pin_cfg_reg/val RX 引脚
  • demo88-nand :UART0,PA0/PA1(0x335)→ PBP 阶段就能打 log
  • demo68-nor (你打开的板级):UART1 ,PA2/PA3(0x325),UART0 整段被注释掉

删掉整个 system.uart 块 → PBP 内无串口 log(JSON 注释说明)。


Step 4.4 --- SYSTEM:jtag(0x41490003

JSONsystem.jtag

Payload 结构

text 复制代码
u32  jtag_only          // 1 = PSRAM/init 后在 PBP 内停住等 JTAG
对每个子项 "main" 等(OrderedDict):
    u32  jtag_id
    u32  jtag_do/di/ms/ck 的 pin_cfg_reg + pin_cfg_val  (各 2 个 u32)
  • demo88-nand:PA8--PA11 四线 JTAG
  • demo68-nor :只配了 ms + ck(PA10/PA11),do/di 被注释------与硬件走线一致即可

jtag_only: "1" 用于极早期调试:init 完成后 不继续跳 loader,先接调试器。


Step 4.5 --- SYSTEM:regcfg(0x41490007,可选)

JSONsystem.regcfg(很多板子没有)

text 复制代码
u32  entry_cnt
每条约:
    u32  reg    // 寄存器地址
    u32  val    // 写入值
    u32  dly    // 延时

用于 PBP 里 批量写 SoC 寄存器(上电时序、电源域等)。


Step 4.6 --- PARTITION 段(0x41490005

JSON :合并进来的 partition.json

Payload

text 复制代码
ASCII 字符串: "spi0:512k(spl),128k(env),...(os),..."
+ 0~3 字节 padding 到 4 字节对齐

gen_bytes_of_part_str() 规则:

text 复制代码
对 type 数组里每种类型(如 mtd、nftl):
  "{type}={layout};"
例如:
  mtd=spi0:512k(spl),128k(env),2m(os),12m(rodata),1m(data);
  nftl=data:40m(data);   // 若有 NFTL 分区

PBP / tinySPL 用该字符串解析 spl/os/rodata 等偏移,与 image_cfg.json 分区定义一致。


Step 4.7 --- 结束段(0x4149FFFF

text 复制代码
u32  0x4149FFFF
u32  0x00000000

d13x.pbp 解析到此停止。


Step 5:写出 pbp_cfg.bin

python 复制代码
data = gen_private_data(cfg)
open("pbp_cfg.bin", "wb").write(data)

无额外文件头、无对齐填充(整文件就是各段首尾相接)。

-v 可在构建 log 里看详细过程。


Step 6:可选后处理(private2 / PDAT)

image_cfg.json 某处使用 "private2"(安全启动场景),会再跑:

bash 复制代码
mk_prebootprog.py -m PDAT -i pbp_cfg.bin -o pbp_cfg.bin

整份 private 数据PBP2 魔数头、校验和、可选 AES 加密。
demo68-nor / demo88-nand 通常只有 "private": "pbp_cfg.bin" ,因此 Step 5 的输出即为最终 bin


Step 7:打进启动镜像(与 d13x.pbp 的关系)

mk_image.py 制作 pbp_ext.aic 时资源区大致为:

text 复制代码
[AIC 镜像头 256B]
[resource: d13x.pbp]      ← 可执行 PBP,带 PBP2 头
[resource: pbp_cfg.bin]   ← 本文件,原始 section 流
[可选: loader / signature ...]

bootloader.aic = pbp_ext.aic + loader.aic(concatenate)。

运行时

  1. BROM 加载 bootloader.aic 到 RAM
  2. 执行 d13x.pbp,根据镜像头里的 private 偏移/长度 找到 pbp_cfg.bin
  3. 按 Step 4 顺序解析:PSRAM → upgmode/uart/jtag → partitions → END
  4. PBP 完成后跳转到 bootloader.bin(tinySPL)
  5. SPL 的 save_boot_params 仍可通过 a0/a1 使用 BROM 传下来的参数(boot_param.c

Step 8:整文件逻辑布局示意(d13x demo68-nor 典型)

text 复制代码
偏移    内容
------  ------------------------------------------
0x0000  [PSRAM]     type=0x41490006, len=..., cnt=4, cfg0..cfg3 × 168B
0x????  [UPGMODE]   type=0x41490004, len=24, 6×u32
0x????  [UART]      type=0x41490002, len=20, uart1@PA2/PA3
0x????  [JTAG]      type=0x41490003, jtag_only + partial pins
0x????  [PARTITION] type=0x41490005, "spi0:512k(spl),..."
0x????  [END]       type=0x4149FFFF, len=0

精确偏移需对生成的 pbp_cfg.binhexdump;段长由 entry_cnt 和字符串长度决定。


Step 9:改配置时改哪里(实操清单)

目的 改什么
换 PSRAM 颗粒 pbp_cfg.json → 新增/改 getid.id + init 序列
PBP 串口无输出 打开 system.uart,对齐硬件 UART 与引脚
换调试串口引脚 uart_*_pin_cfg_reg/val(寄存器地址查 TRM / pinmux)
误进 USB 升级 upgmode 或设 upgmode_pin_cfg_reg: "0"
早期 JTAG jtag_only: "1" + system.jtag 引脚
分区与烧录不一致 image_cfg.json 分区 → 重编自动生成 partition.json
仅改 Boot 不改编应用 pack/pbp_cfg.json 后需 重编应用工程 才会重新生成 bin 并打包

Step 10:与 ddr_init.bin 的对比(避免混用)

项目 DDR 方案 (D21x) PSRAM 方案 (d13x)
JSON ddr_init.json pbp_cfg.json
输出 bin ddr_init.bin pbp_cfg.bin
段类型 0x41490001 DRAM 0x41490006 PSRAM
工具 同一 mk_private_resource.py 同上

小结

pbp_cfg.bin 的生成可以记成 10 步

  1. 维护 pbp_cfg.json(PSRAM + system)
  2. 编应用时由 image_cfg.json 生成 partition.json
  3. mk_private_resource.py 合并两个 JSON
  4. 按序序列化各 DATA_SECT_TYPE_* 段(小端 u32)
  5. 写裸 bin
  6. (多数情况)不做 PDAT 封装
  7. mk_imaged13x.pbp 一起打进 bootloader.aic
  8. 上电后 PBP 解析 bin,完成 PSRAM/引脚/分区表
  9. 再进 tinySPL
  10. 调试 PBP 阶段主要看 uart 段psram getid
相关推荐
xzl042 天前
D13x开发实战:从QEMU到真机全流程
artinchip