- 指令与映像.md / 寄存器码.md:slot 语义 = 槽号(数据偏移 = 槽号 × 8),取消变宽对齐与 global_offset 映射 - Codegen.cpp:删除 width_of/align_up/global_offsets_;layout_data/layout_fb_instances 按槽号布局(字段 = 槽序号) - stb文件格式.md:line1 真实字节更新(224B、56B 数据段、starter 槽 4/5/6) - codegen_test:断言更新(槽号化 + 数据区字节数),189 断言 9/9 全绿
118 lines
5.8 KiB
Markdown
118 lines
5.8 KiB
Markdown
# .stb 映像文件结构
|
||
|
||
编译产物 `.stb` 的**面向使用者**的结构说明。规范以 [`指令与映像.md`](指令与映像.md) 为准,本文用 `line1.stb`(224 字节)做真实拆解。
|
||
|
||
## 概览
|
||
|
||
- 魔数 `STSC`,版本 `1`,**全部整数小端**
|
||
- 文件 = 头 + 五个段,段序:**常量表 → 函数表 → 字节码 → FB 布局 → 数据**
|
||
- 段偏移记在头里;`offset_const ≤ offset_funcs ≤ offset_code ≤ offset_fb ≤ offset_data ≤ 文件长度`
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────┐
|
||
│ 头(72 字节,偏移 0) │
|
||
├──────────────────────────────────────────────────┤
|
||
│ 常量表段 offset_const .. offset_funcs │
|
||
├──────────────────────────────────────────────────┤
|
||
│ 函数表段 offset_funcs .. offset_code │
|
||
├──────────────────────────────────────────────────┤
|
||
│ 字节码段 offset_code .. offset_fb │
|
||
├──────────────────────────────────────────────────┤
|
||
│ FB 布局段 offset_fb .. offset_data(v1 为空)│
|
||
├──────────────────────────────────────────────────┤
|
||
│ 数据段 offset_data .. 文件末尾 │
|
||
└──────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## 头(72 字节)
|
||
|
||
| 偏移 | 宽 | 字段 | 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` | 72 |
|
||
| 56 | 4 | `offset_funcs` | 72 |
|
||
| 60 | 4 | `offset_code` | 96 |
|
||
| 64 | 4 | `offset_fb` | 168 |
|
||
| 68 | 4 | `offset_data` | 168 |
|
||
|
||
## 常量表段(`offset_const` 起)
|
||
|
||
一行 12 字节:`[tag:u32][value:u64]`;`tag`:0=BOOL、1=INT、2=TIME。
|
||
|
||
line1 无字面量常量 → 段为空(`offset_const == offset_funcs`)。
|
||
|
||
## 函数表段(`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` |
|
||
|
||
## 字节码段(`offset_code` 起)
|
||
|
||
指令 4 字节一条:`[op:8 | rd:8 | a:8 | b:8]`(小端 u32)。
|
||
|
||
- `rd` 目的寄存器;`a|b` 视操作码为寄存器、槽号(u16)或跳转偏移(有符号 int16,**相对下一条指令**:目标 = 当前 + 1 + off)
|
||
- `CALL` 的 `fn_id` = 函数表行下标
|
||
- 调用约定:结果 r0、参数 r1..r7、变量与临时 r8 起
|
||
|
||
line1 `MAIN` 前 5 条(`0x60` 起,slot 为 8 字节定宽槽号):
|
||
|
||
| 偏移 | 字节 | 反汇编 | 含义 |
|
||
|---|---|---|---|
|
||
| 0x64 | `12 08 01 00` | `LOAD_I r8, 1` | I0_0(io.input 绑定 → LOAD_I,槽 1) |
|
||
| 0x68 | `17 08 04 00` | `STORE_GLOBAL r8, 4` | starter.start(槽 4)← I0_0 |
|
||
| 0x6C | `16 09 02 00` | `LOAD_GLOBAL r9, 2` | I0_1(槽 2) |
|
||
| 0x70 | `17 09 05 00` | `STORE_GLOBAL r9, 5` | starter.stop(槽 5)← I0_1 |
|
||
| 0x74 | `16 08 04 00` | `LOAD_GLOBAL r8, 4` | 内联体:读 starter.start(槽 4) |
|
||
|
||
## FB 布局段(`offset_fb` 起)
|
||
|
||
v1 为空(用户 FB 内联展开,字段偏移已算进数据段;内建 TON/TOF/CTU 布局冻结在编译器中)。行格式约定:`[field_count:u32][field_count × (tag:u32, offset:u32)]`,offset 相对实例基址。
|
||
|
||
## 数据段(`offset_data` 起)
|
||
|
||
**每槽 8 字节定宽**(12.9 方案 a):`BOOL` 用低 1 字节、`INT` 用低 2 字节(小端)、`TIME` 全 8 字节。指令 `slot` 操作数即**槽号**,数据偏移 = 槽号 × 8。
|
||
|
||
槽序:**全局块**(`n_globals` 个,声明序)→ **FB 实例块**(POU 收集序、实例声明序,每字段一槽)。
|
||
|
||
line1 数据段(56 字节 = 7 槽 × 8B,`0xA8` 起):
|
||
|
||
| 槽号 | 偏移 | 内容 |
|
||
|---|---|---|
|
||
| 0 | 0xA8 | EmergencyStop(BOOL,初值 0) |
|
||
| 1 | 0xB0 | I0_0(BOOL) |
|
||
| 2 | 0xB8 | I0_1(BOOL) |
|
||
| 3 | 0xC0 | Q0_0(BOOL) |
|
||
| 4 | 0xC8 | starter.start(BOOL) |
|
||
| 5 | 0xD0 | starter.stop(BOOL) |
|
||
| 6 | 0xD8 | starter.q(BOOL) |
|
||
|
||
## 用工具验证
|
||
|
||
- `ctest` 的 `isa_roundtrip` / `codegen_slice1` 覆盖编解码往返与映像校验
|
||
- `isa::ImageView` 提供只读视图(校验魔数/版本/段边界/入口 fn_id)
|
||
- 手工检查:`xxd line1.stb` 对照上表逐段核对
|
||
|
||
## 与执行器的关系
|
||
|
||
`BytecodeExecutor`(12.9/12.10 实现)只读本格式:头(`cycle_limit`/`dt_ms`)、函数表(帧 `nregs` + 代码定位)、数据段(全局 + FB 实例状态)。I/O 绑定**不在**映像里,在 sidecar `<name>.runtime.toml`(var → 槽号 → channel/bit)。
|