From 7745dadd2d3583e19a48335799ca68590dbb37eb Mon Sep 17 00:00:00 2001 From: chentianya Date: Tue, 25 Aug 2026 17:35:29 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=B6=E6=AE=B51-3=EF=BC=9Astb=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=A0=BC=E5=BC=8F.md=20=E9=87=8D=E5=86=99=E4=B8=BA=20?= =?UTF-8?q?V2=20=E7=BB=93=E6=9E=84=EF=BC=88=E5=A4=B4=20128B/=E6=A7=BD?= =?UTF-8?q?=E8=A1=A8=E6=AE=B5/=E5=80=BC=E6=AE=B5/=E5=85=83=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=AE=B5=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 段序:常量表→函数表→字节码→槽表段→值段→元数据段→SHA - 头 128B 全字段表(version=2、n_slots/values_size/offset_meta/max_stack) - 常量表 8B 无 tag、函数表 code_len 字节化、变长指令段 16 对齐 - 槽表 8B {addr,预留}、值段变宽紧凑(FB 布局引用清单 §8.1) - 读取校验链(结构/SHA/型号);line1 真实拆解标注阶段 5(C5) --- Doc/isa/stb文件格式.md | 165 +++++++++++++++++++++-------------------- 1 file changed, 86 insertions(+), 79 deletions(-) diff --git a/Doc/isa/stb文件格式.md b/Doc/isa/stb文件格式.md index 3f59a55..a0c0827 100644 --- a/Doc/isa/stb文件格式.md +++ b/Doc/isa/stb文件格式.md @@ -1,127 +1,134 @@ -# .stb 映像文件结构 +# .stb 映像文件结构(V2,STATOR2) -编译产物 `.stb` 的**面向使用者**的结构说明。规范以 [`指令与映像.md`](指令与映像.md) 为准,本文用 `line1.stb`(288 字节,12.13 修订后)做真实拆解。 +编译产物 `.stb` 的**面向使用者**的结构说明。规范以 [`指令与映像.md`](指令与映像.md) 为准,本文给结构与示例。 +真实 `line1.stb` 字节拆解在**阶段 5**(`line1` 推演重算,C5)补充;本文示例为概念性布局。 ## 概览 -- 魔数 `STSC`,版本 `1`,**全部整数小端** -- 文件 = 头 + 五个段,段序:**常量表 → 函数表 → 字节码 → FB 布局 → 数据** -- 段偏移记在头里;`offset_const ≤ offset_funcs ≤ offset_code ≤ offset_fb ≤ offset_data ≤ 文件长度` +- 魔数 `STSC`,**版本 `2`**,型号标识 `STATOR2`,全部整数小端 +- 文件 = 头 + 七个段,段序:**常量表 → 函数表 → 字节码 → 槽表段 → 值段 → 元数据段 → SHA-256 尾** +- 段偏移记在头里,单调:`offset_const ≤ offset_funcs ≤ offset_code ≤ offset_slots ≤ offset_values ≤ 文件长度` ``` ┌──────────────────────────────────────────────────┐ -│ 头(104 字节,偏移 0,含型号标识[32]) │ +│ 头(128 字节,偏移 0,含型号标识[32]) │ ├──────────────────────────────────────────────────┤ -│ 常量表段 offset_const .. offset_funcs │ +│ 常量表段 offset_const .. offset_funcs │ ├──────────────────────────────────────────────────┤ -│ 函数表段 offset_funcs .. offset_code │ +│ 函数表段 offset_funcs .. offset_code │ ├──────────────────────────────────────────────────┤ -│ 字节码段 offset_code .. offset_fb │ +│ 字节码段 offset_code .. offset_slots(16 对齐)│ ├──────────────────────────────────────────────────┤ -│ FB 布局段 offset_fb .. offset_data(v1 为空)│ +│ 槽表段 offset_slots .. offset_values │ ├──────────────────────────────────────────────────┤ -│ 数据段 offset_data .. offset_data+len │ +│ 值段 offset_values .. offset_meta(或文件尾)│ +├──────────────────────────────────────────────────┤ +│ 元数据段 offset_meta (v1 = 0,空段) │ ├──────────────────────────────────────────────────┤ │ SHA-256 文件尾(32 字节,对文件尾之前全部内容) │ └──────────────────────────────────────────────────┘ ``` -## 头(104 字节) +## 头(128 字节,16 对齐) -| 偏移 | 宽 | 字段 | line1 实际值 | +| 偏移 | 宽 | 字段 | 说明 | |---|---|---|---| -| 0 | 4 | 魔数 `STSC` | `53 53 54 43` | -| 4 | 4 | 版本 `1` | 1 | -| 8 | 4 | `cycle_limit`(每周期指令上限) | 100000 | -| 12 | 4 | `dt_ms`(本周期 Δt) | 10 | -| 16 | 8 | 工程哈希(FNV-1a 64) | `0xc3fe4ae74ad45900` | -| 24 | 4 | 入口 `entry_fn_id` | 1(MAIN) | -| 28 | 4 | 全局槽数 `n_globals` | 4 | -| 32 | 4 | I 槽数 `n_i` | 0(v1 不拆) | -| 36 | 4 | Q 槽数 `n_q` | 0 | -| 40 | 4 | M 槽数 `n_m` | 0 | -| 44 | 4 | 常量数 `n_consts` | 0 | -| 48 | 4 | 函数数 `n_funcs` | 2 | -| 52 | 4 | `offset_const` | 104(`0x68`) | -| 56 | 4 | `offset_funcs` | 104 | -| 60 | 4 | `offset_code` | 128(`0x80`) | -| 64 | 4 | `offset_fb` | 200(`0xC8`) | -| 68 | 4 | `offset_data` | 200 | -| 72 | 32 | **型号标识**(定长 ASCII 含版本,补 `'\0'`) | `"STATOR1"`(`0x48..0x67`) | - -> 12.13 修订:型号标识由 `machine.toml [meta] name + version` 拼成(如 `STATOR + 1` → `"STATOR1"`); -> 执行器读取时型号不匹配 → 直接报错。 +| 0 | 4 | 魔数 `STSC` | 不变(V1 沿用) | +| 4 | 4 | 版本 `2` | V1 = 1 | +| 8 | 4 | `cycle_limit` | 每周期指令数上限 | +| 12 | 4 | `dt_ms` | 周期时长(毫秒,定时器推进步长) | +| 16 | 8 | 工程哈希(FNV-1a 64) | 源文件路径排序后对内容计算 | +| 24 | 4 | 入口 `entry_fn_id` | MAIN 函数表行下标 | +| 28 | 4 | 全局槽数 `n_globals` | 槽表内全局块条目数 | +| 32/36/40 | 4×3 | `n_i` / `n_q` / `n_m` | **保留恒 0**(V1 遗产,兼容 + 将来语义) | +| 44 | 4 | 常量数 `n_consts` | 常量表条目数 | +| 48 | 4 | 函数数 `n_funcs` | 函数表行数 | +| 52/56/60 | 4×3 | `offset_const` / `offset_funcs` / `offset_code` | 段偏移 | +| 64 | 4 | `offset_slots` | 槽表段偏移 | +| 68 | 4 | `offset_values` | 值段偏移(**16 对齐**) | +| 72 | 32 | **型号标识**(定长 ASCII 含版本,补 `'\0'`) | `"STATOR2"`(name + version=2) | +| 104 | 4 | `n_slots` | 槽表条目数(= n_globals + FB 实例/结构体字段槽数) | +| 108 | 4 | `values_size` | 值段字节数 | +| 112 | 4 | `offset_meta` | 元数据段偏移(**v1 = 0** = 空段) | +| 116 | 4 | `max_stack` | 栈区字节数(编译期调用图最坏路径) | +| 120 | 8 | 预留 | 未来扩展 | ## 常量表段(`offset_const` 起) -一行 12 字节:`[tag:u32][value:u64]`;`tag`:0=BOOL、1=INT、2=TIME。 +一行 **8 字节**:`[value:u64]`(原始位模式,**无 tag**——类型由 LOADK 指令的 func 位段解释)。 -line1 无字面量常量 → 段为空(`offset_const == offset_funcs`)。 +- 按位模式去重(同值多类型复用:`5` 可被 func=INT 与 func=REAL 的 LOADK 引用) +- `const_id` = 行下标(u32,IMM 形态 imm32 字段) ## 函数表段(`offset_funcs` 起) 一行 12 字节:`[nregs:u32][code_offset:u32][code_len:u32]` -- **行下标即 `fn_id`**;`code_offset` 相对字节码段起点(**字节**);`code_len` 为**指令条数** -- 入口 = 头的 `entry_fn_id` 指向的行 - -line1(2 行): - -| fn_id | nregs | code_offset | code_len | 内容 | -|---|---|---|---|---| -| 0 | 8 | 0 | 1 | `MotorStarter` 占位(内联展开,仅 RET) | -| 1 | 13 | 4 | 17 | `MAIN` | +- **行下标即 `fn_id`**;`code_offset` 相对字节码段起点(**字节**);`code_len` 为**字节数**(变长指令,字节单位) +- 入口 = 头 `entry_fn_id` 指向的行 +- `nregs` = 本函数帧寄存器数(帧分配 = nregs×8 + 帧头 12B;max_stack 由编译期按调用图算) ## 字节码段(`offset_code` 起) -指令 4 字节一条:`[op:8 | rd:8 | a:8 | b:8]`(小端 u32)。 +**变长指令 4/8/16/32B**,段起点 16 对齐。指令字: -- `rd` 目的寄存器;`a|b` 视操作码为寄存器、槽号(u16)或跳转偏移(有符号 int16,**相对下一条指令**:目标 = 当前 + 1 + off) -- `CALL` 的 `fn_id` = 函数表行下标 -- 调用约定:结果 r0、参数 r1..r7、变量与临时 r8 起 +```text +字节 0 字节 1 +[长度前缀+形态分区 | op] [func:4 | 空:4] +``` -line1 `MAIN` 前 5 条(`0x84` 起;`0x80` 是 `fn 0` 的占位 `RET`,slot 为 8 字节定宽槽号): +- 形态(前缀直推):`11`=RR、`10`=NONE、`011`=RRR、`010 000..101`=IMM/SLOT/JMP/JC/CALL/CAL、`0010`=SLOT+off、`0011`=IMM64、`0001`=预留 +- 编码明细(位号/操作数区/uses_*)见 [`指令编码.md`](指令编码.md)、清单见 [`指令集清单.md`](指令集清单.md) +- 跳转偏移 off32 **字节单位,相对下一条**(目标 = 当前指令地址 + 指令长 + off) +- 调用约定:结果 r0、参数 r1..r7、变量与临时 r8 起;CALL/RET 复制 r0..r7 全宽 -| 偏移 | 字节 | 反汇编 | 含义 | -|---|---|---|---| -| 0x84 | `12 08 01 00` | `LOAD_I r8, 1` | I0_0(io.input 绑定 → LOAD_I,槽 1) | -| 0x88 | `17 08 04 00` | `STORE_GLOBAL r8, 4` | starter.start(槽 4)← I0_0 | -| 0x8C | `16 09 02 00` | `LOAD_GLOBAL r9, 2` | I0_1(槽 2) | -| 0x90 | `17 09 05 00` | `STORE_GLOBAL r9, 5` | starter.stop(槽 5)← I0_1 | -| 0x94 | `16 08 04 00` | `LOAD_GLOBAL r8, 4` | 内联体:读 starter.start(槽 4) | +## 槽表段(`offset_slots` 起) -## FB 布局段(`offset_fb` 起) +一行 **8 字节**:`[addr:u32][预留:u32]`(addr = 值段内字节偏移,指向变量/FB 实例起始)。 -v1 为空(用户 FB 内联展开,字段偏移已算进数据段;内建 8 个 FB 布局冻结在编译器中(12.11 扩充))。行格式约定:`[field_count:u32][field_count × (tag:u32, offset:u32)]`,offset 相对实例基址。 +- **行下标即槽号**(slot u32);`n_slots` 行 +- 槽序:全局块(声明序,`n_globals` 个)→ FB 实例块(POU 收集序、实例声明序) -## 数据段(`offset_data` 起) +## 值段(`offset_values` 起) -**每槽 8 字节定宽**(12.9 方案 a):`BOOL` 用低 1 字节、`INT` 用低 2 字节(小端)、`TIME` 全 8 字节。指令 `slot` 操作数即**槽号**,数据偏移 = 槽号 × 8。 +**变宽紧凑布局**(对齐 1/2/4/8B,编译器保证;段起点 16 对齐): -槽序:**全局块**(`n_globals` 个,声明序)→ **FB 实例块**(POU 收集序、实例声明序,每字段一槽)。 +- 变量按类型占宽:BOOL 1B、INT 2B、DINT/REAL 4B、TIME/LREAL 8B…(func 宽度表) +- 变量初值、FB 实例字段初值(0)写于此;运行时拷入工作副本,文件只读 +- 字段访问:`LOAD/STORE rd, slot, off32`——addr = 槽表[slot].addr + off(结构体/FB 共享体统一) -line1 数据段(56 字节 = 7 槽 × 8B,`0xC8` 起): +FB 实例布局(字段偏移/大小,对齐规则)见 [`指令集清单.md`](指令集清单.md) §8.1,例: -| 槽号 | 偏移 | 内容 | +| FB | 布局(偏移) | 大小 | |---|---|---| -| 0 | 0xC8 | EmergencyStop(BOOL,初值 0) | -| 1 | 0xD0 | I0_0(BOOL) | -| 2 | 0xD8 | I0_1(BOOL) | -| 3 | 0xE0 | Q0_0(BOOL) | -| 4 | 0xE8 | starter.start(BOOL) | -| 5 | 0xF0 | starter.stop(BOOL) | -| 6 | 0xF8 | starter.q(BOOL) | +| TON | in=0、pt=8、q=16、et=24 | 32B | +| CTU | cu=0、r=1、pv=2、q=4、cv=6 | 8B | +| DCTU | cu=0、r=1、pad 2..3、pv=4、q=8、pad 9..11、cv=12 | 16B | + +## 元数据段(`offset_meta` 起) + +v1 为空段(`offset_meta = 0`)。将来承载结构体/字符串/数组的类型描述表(字段表),schema 延后(U12)。 ## SHA-256 文件尾(32 字节) -数据段之后追加 32 字节 SHA-256:**对文件尾之前全部内容计算**(`0x100..0x11F`)。执行器必须校验通过才运行;任一字节被篡改 → 直接拒绝。compiler 写侧与 vm 读侧**各自实现**一份 SHA-256。 +文件尾追加 32 字节 SHA-256:**对文件尾之前全部内容计算**。compiler 写侧与 vm 读侧**各自实现**一份。执行器校验不通过 → 直接拒绝。 + +## 读取校验链(vm 读侧) + +1. **结构校验**:魔数/版本/头大小/段偏移单调且在 SHA 前/常量表·函数表尺寸一致/代码段 16 对齐/入口 fn_id 范围 +2. **SHA-256**:完整性(防篡改) +3. **型号匹配**:头内 32 字节与 `"STATOR2"` 补零逐字节比对 + +任一不符 → `Machine::create` 拒绝,不创建机器。 + +## 与 sidecar / 执行器的关系 + +- `.stb` + `.runtime.toml`(sidecar:`[[io.input]]`/`[[io.output]]`,var → 槽号 → channel/bit) +- I/O 绑定**不进映像**;绑定粒度 `slot = 槽号`(指向槽表条目),channel/bit 语义不变 +- executor 按 sidecar 采样 I / 写回 Q;`dt_ms`/`cycle_limit` 从映像头取;`max_stack` 供 VM 预分配栈区 ## 用工具验证 -- `ctest`:`codegen_slice1`(写侧 model/sha 自检)、`vm_cycles`(读侧型号/SHA 拒绝)、`machine_config` -- `STCompiler x.stb --disasm --machine machine.toml`:显示 `model=` 与 `sha=ok|BAD` -- 手工检查:`xxd line1.stb` 对照上表逐段核对 - -## 与执行器的关系 - -`BytecodeExecutor` 只读本格式:头(`cycle_limit`/`dt_ms`/型号标识)、函数表(帧 `nregs` + 代码定位)、数据段(全局 + FB 实例状态)。读取时**型号匹配 + SHA-256 校验**,任一不符 → 报错拒绝。I/O 绑定**不在**映像里,在 sidecar `.runtime.toml`(var → 槽号 → channel/bit)。 +- `ctest`:`isa_roundtrip`(编解码)、`vm_cycles`(型号/SHA 拒绝)、`machine_config`(V2 schema 强校验) +- `STCompiler x.stb --disasm --machine machine.toml`:显示 `model=STATOR2` 与 `sha=ok|BAD` +- 阶段 5 后:`xxd line1.stb` 对照本文逐段核对(C5 line1 推演重算)