From 59d7ff31596348dc47dd356f49a3299f06d33126 Mon Sep 17 00:00:00 2001 From: chentianya Date: Tue, 25 Aug 2026 11:29:15 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=B6=E6=AE=B51-1=EF=BC=9A=E6=8C=87?= =?UTF-8?q?=E4=BB=A4=E4=B8=8E=E6=98=A0=E5=83=8F.md=20=E9=87=8D=E5=86=99?= =?UTF-8?q?=E4=B8=BA=20V2=20=E8=A7=84=E6=A0=BC=EF=BC=88STATOR2=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - §2 特性总表(变长指令/前缀分区/func/栈方案C/除法A) - §3 指令编码(前缀分区表 + func 位段 + 操作数区位号 + uses_* 直推) - §4 寄存器与调用栈(帧按 nregs、max_stack、递归编译期报错) - §5 类型系统(19 类型→func 映射、BOOL 强制、日期表示) - §6 指令语义(除法 A、浮点 IEEE、跳转字节化、内建 FB 14 条) - §7 数据模型(槽表→值区)、§8 映像格式(头 128B/段序/校验) - §9 sidecar、§10 反汇编(类型后缀)、§11 目录、§12 实现约定 --- Doc/isa/指令与映像.md | 463 +++++++++++++++++++++++++----------------- 1 file changed, 279 insertions(+), 184 deletions(-) diff --git a/Doc/isa/指令与映像.md b/Doc/isa/指令与映像.md index 354fee2..f8619a7 100644 --- a/Doc/isa/指令与映像.md +++ b/Doc/isa/指令与映像.md @@ -1,10 +1,14 @@ -# 指令与映像 +# 指令与映像(V2 规格,STATOR2) -isa 模块规范:指令集 + 映像 + 定宽类型。不是编译器,不把 `.st` 编成映像。 +> 状态:**V2 规格(阶段 1 重写)**。本文件为指令编码、类型、数据模型、映像格式的 +> **规范**,头文件与代码实现本文,不另当规范。 +> 定案依据:[`阶段1规格总览.md`](阶段1规格总览.md)(主参考)、[`指令编码.md`](指令编码.md)、 +> [`指令集清单.md`](指令集清单.md)、[`指令集优化.md`](指令集优化.md)。 +> 与 V1 关系:V1(32bit 指令、8 字节定宽槽、STATOR1)已实现并冻结;本文为**全新 V2**, +> 头 `version = 2`、型号 `STATOR2`,老映像被版本/型号校验拒绝。 -`compiler` 写 `.stb`、`vm` 读 `.stb`,两边**各自实现**读写;`isa` 归执行器侧只管指令定义。指令 opcode / 类型 / FB 布局的登记见 [`指令配置.md`](指令配置.md)(`compiler/machine.toml`,与代码强校验)。 - -全工程定案见 [`初步计划.md`](../初步计划.md)。**类型、指令编码、映像布局以本文为准**,不要另写一份互相打架的表。头文件实现本文,不另当规范。 +`compiler` 写 `.stb`、`vm` 读 `.stb`,两边**各自实现**读写;`isa` 归执行器侧只管指令定义。 +指令 opcode / 类型 / FB 布局的登记见 [`指令配置.md`](指令配置.md)(`compiler/machine.toml`,与代码强校验)。 ```text STCompiler → compiler (编码查 machine.toml;自带 .stb 写) @@ -15,13 +19,13 @@ CMake 目标:`isa`(`STATIC`),无依赖;`vm` PUBLIC 链 `isa`。公开 --- -## 做 / 不做 +## 1. 做 / 不做 **做** -- 操作码与 32 位指令布局 -- 映像头与段(函数字节码、全局 / FB 槽、工程哈希) -- `BOOL` / `INT` / `TIME` 宽度与溢出规则 +- 操作码(前缀分区)与**变长指令**布局(4/8/16/32B) +- 映像头 128B 与段(常量表 / 函数表 / 字节码 / 槽表段 / 值段 / 元数据段) +- 19 种 IEC 类型 → 10 种 func 的宽度/符号表、饱和与除法边界 - 编解码、工程哈希、一条指令的文本转储(反汇编,给 STCompiler `--disasm` 与测试打印) **不做** @@ -34,209 +38,304 @@ CMake 目标:`isa`(`STATIC`),无依赖;`vm` PUBLIC 链 `isa`。公开 --- -## 特性 +## 2. 特性总表(V2) -| 项 | 第一版 | +| 项 | V2 定案 | |---|---| -| `BOOL` | `uint8_t`,只允许 0/1 | -| `INT` | `int16_t`(IEC `INT`) | -| `TIME` | `int64_t` 毫秒(有符号);与 `INT` 不混用 | -| 整数溢出 | **饱和**(夹到 `INT` 最小/最大) | +| 指令宽度 | **变长 4/8/16/32B**,长度与形态由**前缀分区**表达 | +| 前缀 | `11`=RR、`10`=NONE、`011`=RRR、`010 000..101`=IMM/SLOT/JMP/JC/CALL/CAL、`0010`=SLOT+off、`0011`=IMM64、`0001`=预留(32B) | +| 头部 | 字节 0 = `[前缀+分区\|op]`;字节 1 = `[func:4\|空:4]`(空位延后定) | +| func | 4 位:0..7 = 宽度×符号(1/2/4/8B×有/无)、8=F32、9=F64;无类型指令 = 0 | +| 寄存器 | 编号 8 位(**256 = 寻址上限**),值 int64;**每帧按函数头 nregs 动态分配** | +| 调用约定 | r0 结果、r1..r7 参数(≤7)、r8+ 变量/临时;CALL/RET 复制 r0..r7 全宽(64B) | +| 调用栈 | 预分配栈区(max_stack 编译期算)、帧头 12B、溢出双检查(字节+深度 64);**递归 = 编译期报错** | +| 跳转 | **字节单位**,相对下一条(目标 = 当前 + off32) | +| 类型 | 19 IEC 类型 → 10 种 func 编译期映射;禁隐式变量转换 | +| 整数溢出 | **饱和**(各宽度模板);除零返回被除数;MIN/-1 饱和;截断除法;MOD 符号随被除数 | +| 浮点 | IEEE 754(溢出/零除 → ±inf、NaN 传播) | +| 变量访问 | **槽表(引用)→ 值区(变宽紧凑)**两级间接;全 32 位寻址 | | 字节序 | 小端 | -| 指令宽度 | 32 bit,一种主布局 | -| 寄存器 | 下标 0..255;每函数实际个数写在函数头 `nregs` | -| 跳转 | 相对 offset,单位是**指令条数**,不是字节 | -| `CALL` | 立即数 `fn_id`(u16),无函数指针 | - -### 调用约定(冻结,compiler 12.8 与 VM 12.9 共同遵守) - -- 结果寄存器 **r0**;参数寄存器 **r1..r7**(最多 7 个输入,按声明序) -- 变量与表达式临时**全部从 r8 起**(调用约定区 r0..r7 不受用户数据占用) -- `CALL`:VM 复制当前帧 r0..r7 到新帧 r0..r7 -- `RET`:VM 复制当前帧 r0..r7 回调用方帧 r0..r7(r0 = 结果;r1..r7 原样返回) -- 函数输入在函数体内只读(compiler 拒绝写入) -| 映像魔数 | `STSC`,版本 `1` | +| 映像 | 魔数 `STSC`、**version = 2**、型号 `STATOR2` | | 工程哈希 | 源文件路径排序后对内容做 FNV-1a 64(非密码学) | -| I/O 绑定 | **不进映像**;编译期把 `var` 收成槽号,channel/bit 由 STCompiler 写进 sidecar(`.runtime.toml`),BytecodeExecutor 读 sidecar 采样 | -| 不做 | `REAL`、隐式宽化 | +| 完整性 | 文件尾 SHA-256[32];型号标识[32]@72 | +| I/O 绑定 | **不进映像**;sidecar(`.runtime.toml`)绑定 var→槽号→channel/bit,粒度不变 | +| 内建 FB | 14 条(CAL + fb_id 16 位);字段布局见 [`指令集清单.md`](指令集清单.md) §8.1 | --- -## 类型 - -C++20 定宽: - -- `BOOL`:`uint8_t`,0/1 -- `INT`:`int16_t`;加减乘除溢出饱和 -- `TIME`:`int64_t` 毫秒,有符号 - -`compiler` 与 `vm` 都按同一规则,不再另选溢出策略。 - -`TIME` 选 `int64_t` 的理由(冻结,不再反复问): - -- 有符号:`TOF` 剩余时间、两个时间点相减会出负值,免去无符号边界处理 -- 64 位:为将来 `DATE_AND_TIME`(毫秒时间戳)与 `TON` / `TOF` / `TP` 的 64 位定时器预留;毫秒量程约 2.9 亿年,扫描周期场景永不溢出 -- 代价:槽位 8 字节对齐。**槽位规则**(12.9 方案 a):每槽 8 字节定宽,`BOOL` 用低 1 字节、`INT` 用低 2 字节(小端)、`TIME` 全 8 字节;槽号 × 8 = 数据偏移,无 padding。 - ---- - -## 指令 - -定宽 32 bit: +## 3. 指令编码(详见指令编码.md) ```text -[ op:8 | rd:8 | a:8 | b:8 ] +指令 = 字节0 + 字节1 + 操作数区(按形态) + +字节 0 字节 1 +[长度前缀+形态分区 | op] [func:4 | 空:4] ``` -- 三寄存器:`a`/`b` 为 `ra`/`rb`(`MOVE`/`NOT` 只用 `rd`+`a`) -- `LOADK`:`a|b` 为 `const_id`(u16) -- `JMP` / `JT` / `JF`:`a|b` 为有符号相对 offset(指令条数),**相对下一条指令**:目标下标 = 当前下标 + 1 + off -- `CALL`:`a|b` 为 `fn_id`(u16) +### 3.1 前缀分区与 op 空间 -操作码一次列全(`CMP_xx` 冻下面六种): +| 前缀 | 长度 | 形态 | op 位宽 | op 空间 | 识别 | +|---|---|---|---|---|---| +| `11` | 4B | RR | 6 位 | 64 | 前缀直推 | +| `10` | 4B | NONE | 6 位 | 64 | 前缀直推 | +| `011` | 8B | RRR | 5 位 | 32 | 前缀直推 | +| `010 000` | 8B | IMM | 2 位 | 4 | 前缀直推 | +| `010 001` | 8B | SLOT | 2 位 | 4 | 前缀直推 | +| `010 010` | 8B | JMP | 2 位 | 4 | 前缀直推 | +| `010 011` | 8B | JC | 2 位 | 4 | 前缀直推 | +| `010 100` | 8B | CALL | 2 位 | 4 | 前缀直推 | +| `010 101` | 8B | CAL | 2 位 | 4 | 前缀直推 | +| `010 110/111` | 8B | 预留 | 2 位 | 8 | — | +| `0010` | 16B | SLOT+off | 4 位 | 16 | 前缀直推 | +| `0011` | 16B | IMM64 | 4 位 | 16 | 前缀直推 | +| `0001` | 32B | 预留(N2) | 4 位 | 16 | — | + +- op 按分区独立编号(0 起);取指:读字节 0 → 前缀定长 → 搬整条 +- **uses_rd/uses_ra/uses_rb 全部前缀直推**(零查表):RRR 三寄存器全真;RR 的 rd/rs;IMM/SLOT/JC/SLOT+off/IMM64 仅 rd;JMP/CALL/CAL/NONE 无 + +### 3.2 func 位段(字节 1 高 4 位) + +| func | 语义 | 覆盖类型 | +|---|---|---| +| 0 | 1B 无符号 | BOOL、BYTE、USINT | +| 1 | 1B 有符号 | SINT | +| 2 | 2B 无符号 | WORD、UINT | +| 3 | 2B 有符号 | INT | +| 4 | 4B 无符号 | DWORD、UDINT、DATE | +| 5 | 4B 有符号 | DINT | +| 6 | 8B 无符号 | LWORD、ULINT、DT、TOD | +| 7 | 8B 有符号 | LINT、TIME | +| 8 | F32 | REAL | +| 9 | F64 | LREAL | +| 10..15 | 预留 | | + +- **func 语义随 op**:MOVE=宽度;LOAD/STORE=宽度×符号;ADD..=宽度×符号+浮点;逻辑=宽度;无类型指令=0 +- 字节 1 低 4 位暂空(N1,延后定) + +### 3.3 操作数区位号(bit16 起;pad = 对齐填充,编码固定 0) + +| 形态 | 操作数区 | +|---|---| +| RR | rd=23..16、rs=31..24 | +| NONE | pad=31..16 | +| RRR | rd=23..16、rs1=31..24、rs2=39..32、pad=63..40 | +| IMM | rd=23..16、imm32=55..24、pad=63..56 | +| SLOT | rd=23..16、slot32=55..24、pad=63..56 | +| JMP | off32=47..16、pad=63..48 | +| JC | rd=23..16、off32=55..24、pad=63..56 | +| CALL | fn_id32=47..16、pad=63..48(**rd 恒 0**) | +| CAL | fb_id16=31..16、slot32=63..32(**rd 恒 0**) | +| SLOT+off | rd=23..16、slot32=55..24、off32=87..56、pad=127..88 | +| IMM64 | rd=23..16、imm64=87..24、pad=127..88 | + +--- + +## 4. 寄存器与调用栈 + +- **寄存器编号 8 位**:256 为寻址上限(不是每帧分配数);**每帧按函数头 nregs 动态分配**(nregs×8B,百字节级) +- 寄存器值 int64;codegen `register overflow (>256)` 检查 +- **调用约定**:r0 结果、r1..r7 参数(≤7)、r8+ 变量/临时;CALL/RET 复制 r0..r7 全宽(64B);函数输入只读(compiler 拒绝写入) + +### 调用栈(方案 C) + +- **递归禁止**:链接层调用图环检测(自调用/循环调用)→ 编译期报错 `link error: recursive call` +- **max_stack**:MAIN 帧 + DAG 最坏调用路径 Σ(帧需求),帧需求 = 帧头 12B + nregs×8;写入映像头 @116 +- **预分配栈区**:Machine 构造时一次性分配 max_stack 连续内存,运行期零分配 +- 帧结构:帧头 `{ fn_id:u32, ret_pc:u32, ret_fn_id:u32 }`(12B)+ 寄存器区 nregs×8 +- 压帧:栈顶取帧需求,sp += 需求;弹帧:sp -= 需求 +- **溢出双检查**:剩余栈 < 帧需求 → `StackOverflow`(字节主检查)+ 深度 ≤64(双保险) +- MAIN 帧常驻栈底(跨周期保留),周期结束回退 sp 到 MAIN 帧顶 + +--- + +## 5. 类型系统 + +### 5.1 19 种 IEC 基础类型(编译期概念 → func 映射) + +| 类型 | 宽度 | 符号 | func | 类型 | 宽度 | 符号 | func | +|---|---|---|---|---|---|---|---| +| BOOL | 1B | — | 0 | LINT | 8B | 有 | 7 | +| BYTE | 1B | 无 | 0 | USINT | 1B | 无 | 0 | +| WORD | 2B | 无 | 2 | UINT | 2B | 无 | 2 | +| DWORD | 4B | 无 | 4 | UDINT | 4B | 无 | 4 | +| LWORD | 8B | 无 | 6 | ULINT | 8B | 无 | 6 | +| SINT | 1B | 有 | 1 | TIME | 8B | 有(ms) | 7 | +| INT | 2B | 有 | 3 | REAL | 4B | IEEE754 | 8 | +| DINT | 4B | 有 | 5 | LREAL | 8B | IEEE754 | 9 | +| | | | | DATE | 4B | 无(天) | 4 | +| | | | | TOD | 4B | 无(ms) | 6 | +| | | | | DT | 8B | 无 | 6 | + +- 编译器类型检查**严格比对**(不匹配编译期报错);**BOOL 编译器强制 0/1**(越界报错) +- **禁隐式变量转换**;**字面量按上下文适配**(`5` 可赋 INT/REAL/DINT…,编译期解释),默认 DINT 兜底 +- 进制前缀 `2#`/`8#`/`16#` 字面量(浮点无前缀) +- **DATE/TOD/DT**:v1 不运算(仅存取/比较);DT = u64 `{date:32 | tod:32}` 拼接(高 32 位日期) + +### 5.2 值表示(寄存器 / 槽统一约定) + +| 类型 | 寄存器(int64 位模式) | +|---|---| +| BOOL | 低 1 字节(0/1) | +| 有符号整数 | 按 func 宽度符号扩展 | +| 无符号/位串 | 按 func 宽度零扩展 | +| TIME/LREAL/DT | 全 64 位 | +| REAL | 低 4 字节(float 位模式) | + +全宽 int64 比较等价于各类型自身比较(编译层保证类型语义)。 + +--- + +## 6. 指令语义(清单见指令集清单.md) + +### 6.1 数据搬运 + +- `MOVE rd, rs`(RR,func=宽度 0..3):寄存器间按宽度搬运(高位清 0),不管符号/浮点 +- `LOAD rd, slot` / `STORE slot, rs`(SLOT,func 全矩阵):槽表[slot].addr → 值区按 func 读/写宽度 +- `LOADK rd, const_id`(IMM,func 全矩阵):常量表条目 8B 原始值,按 func 解释 +- `LOAD_OFF rd, slot, off` / `STORE_OFF slot, off, rs`(SLOT+off):字段访问(结构体/FB 共享体统一) + +### 6.2 逻辑与位运算 + +- `NOT`(RR)、`AND/OR/XOR`(RRR):按 func 宽度逐位(BOOL 由编译器保证 0/1) + +### 6.3 算术 + +- `ADD/SUB/MUL/DIV`(RRR,func 全矩阵):整数饱和到 func 宽度 / 浮点 IEEE +- **整数除法边界(A 定案)**:除零 → 返回被除数;MIN/-1 → 饱和夹界;截断除法(向零) +- `MOD`:符号随被除数(-7 MOD 3 = -1);除零同 DIV +- `NEG/ABS/INC/DEC`(RR);浮点单目/舍入(RR,func 8/9);`CONV`(RR,func = 目标类型) + +### 6.4 比较 + +- `CMP_EQ..GE`(RRR,func 全矩阵):按 func 有/无符号、浮点 IEEE(NaN 传播) + +### 6.5 控制流 + +- `JMP off32`:pc += off(字节,相对下一条);`JT/JF rd, off`:条件跳转 +- `CALL fn_id`(rd 恒 0):压帧(复制 r0..r7 全宽);`RET`:弹帧 / MAIN 周期结束 +- `CAL fb_id, slot`(rd 恒 0):内建 FB 调用,fb_id 查 [[fb]] 表 +- 周期指令计数:每条 +1,超 `cycle_limit` → `CycleLimit` 故障 + +### 6.6 内建 FB(14 条) + +| fb_id | FB | fb_id | FB | +|---|---|---|---| +| 0 | TON | 7 | CTUD | +| 1 | TOF | 8 | DCTUD | +| 2 | TP | 9 | R_TRIG | +| 3 | CTU | 10 | F_TRIG | +| 4 | DCTU | 11 | SR | +| 5 | CTD | 12 | RS | +| 6 | DCTD | 13 | PWM | +| | | 14 | RTC | + +字段布局(类型化 + 对齐 1/2/4/8 + 偏移)见 [`指令集清单.md`](指令集清单.md) §8.1。 +RTC 时钟源 = **虚拟时钟**(周期计数 × dt_ms,确定性,不读系统时钟)。 + +--- + +## 7. 数据模型:槽表 → 值区 ```text -MOVE rd, rs -LOADK rd, const_id -NOT rd, rs -AND/OR rd, ra, rb (* 已算好的值;短路用跳转 *) -ADD/SUB/MUL/DIV rd, ra, rb -CMP_EQ/NE/LT/LE/GT/GE rd, ra, rb -JMP offset -JT/JF r, offset -LOAD_I / STORE_Q / LOAD_M / STORE_M -LOAD_GLOBAL / STORE_GLOBAL -CAL_TON / CAL_TOF / CAL_TP / CAL_CTU / CAL_CTD / CAL_CTUD / CAL_R_TRIG / CAL_F_TRIG -CALL fn_id -RET +槽表段(静态引用表,8B/条) 值段(初值,紧凑变宽) +{ addr:u32, 预留:u32 } ──地址──▶ BOOL 1B / INT 2B / DINT·REAL 4B / TIME·LREAL 8B +槽 0..n_slots-1 结构体 = 连续字段块(v1 预留) ``` -> 12.11 修订:内置功能块扩为 8 个(TON/TOF/TP/CTU/CTD/CTUD/R_TRIG/F_TRIG), -> `CAL_*` 操作码**连续占 24..31**(原 CALL=27、RET=28 后移为 32、33)。 -> 操作码数值即编码,本表为准。 - -局部 / `VAR` = 固定寄存器;表达式临时值往后编号;`nregs` 写进函数头。 -`I`/`Q`/`M` 走映像指令,不是通用寄存器。 -FB 实例固定布局,字段偏移编译期算死。 - -### 反汇编格式(disasm) - -`disasm` 输出一条指令的一行文本,格式冻结: - -| 形态 | 格式 | 示例 | -|---|---|---| -| 二寄存器 | `MOVE rd, rs` / `NOT rd, rs` | `MOVE r1, r2` | -| 三寄存器 | `OP rd, ra, rb`(逻辑 / 算术 / 比较) | `ADD r1, r2, r3` | -| 常量 | `LOADK rd, imm` | `LOADK r0, 3` | -| 无条件跳转 | `JMP off` | `JMP +4` | -| 条件跳转 | `JT rd, off` / `JF rd, off` | `JT r0, -1` | -| I/Q/M/全局 | `LOAD_I rd, slot` 等;STORE 同形 `STORE_Q rs, slot` | `LOAD_I r0, 2` | -| FB 内置 | `CAL_TON instance` 等 | `CAL_TON 0` | -| 调用 | `CALL fn_id` | `CALL 1` | -| 返回 | `RET` | `RET` | -| 未知操作码 | `??? 0x` | — | - -跳转偏移带符号、恒显正负号(`%+d`);寄存器一律 `r`;常量/槽号/`fn_id` 无符号十进制。 +- **访问**:`LOAD rd, slot` → `addr = 槽表[slot].addr` → 值区按 func 读宽度;字段访问 `LOAD rd, slot, off` → `addr + off` +- **两级间接**:每次访问多一次内存读(PLC 规模无感) +- **对齐**:1/2/4/8B,编译器布局保证;值段起点 16 对齐 +- **初值**:编译期写值段(变量初值、FB 实例字段 0);运行时拷贝工作副本,文件只读 +- **寻址**:槽号 u32(2^32 引用)、值区地址 u32(4GB) +- 槽表分配:n_slots = n_globals(全局声明序)+ FB 实例/字段槽数 --- -## 映像 +## 8. 映像格式 -魔数 `STSC`,版本 `1`。全部整数小端。文件 = 头 + 五个段 + **文件尾校验**,段序:常量表 → 函数表 → 字节码 → FB 布局 → 数据。偏移记在头里,段必须连续、偏移单调不减,末段终点不超过文件长度(文件尾在数据段之后)。 +### 8.1 头(128 字节,16 对齐) -> 12.13 修订:头新增 **型号标识[32]**(原头 72 字节 → 104 字节,段偏移基准同步后移); -> 文件尾新增 **SHA-256[32]**(对文件尾之前全部内容计算,compiler 写侧 / vm 读侧各自实现)。 -> 执行器读取时:型号标识与自身支持型号不匹配 → 报错;SHA-256 不匹配 → 报错(损坏/篡改)。 - -### 头(104 字节) - -| 偏移 | 宽 | 字段 | +| 偏移 | 字段 | 说明 | |---|---|---| -| 0 | 4 | 魔数 `STSC` | -| 4 | 4 | 版本 `1` | -| 8 | 4 | `cycle_limit` | -| 12 | 4 | `dt_ms` | -| 16 | 8 | 工程哈希(FNV-1a 64) | -| 24 | 4 | 入口 `entry_fn_id` | -| 28 | 4 | 全局槽数 `n_globals` | -| 32 | 4 | I 槽数 `n_i` | -| 36 | 4 | Q 槽数 `n_q` | -| 40 | 4 | M 槽数 `n_m` | -| 44 | 4 | 常量数 `n_consts` | -| 48 | 4 | 函数数 `n_funcs` | -| 52 | 4 | `offset_const` | -| 56 | 4 | `offset_funcs` | -| 60 | 4 | `offset_code` | -| 64 | 4 | `offset_fb` | -| 68 | 4 | `offset_data` | -| 72 | 32 | **型号标识**(定长 ASCII,含版本,如 `"STATOR" + "1"` 拼 32 字节补 `'\0'`) | +| 0 | magic | `STSC`(不变) | +| 4 | version | **= 2**(V1 = 1) | +| 8 | cycle_limit | 每周期指令数上限 | +| 12 | dt_ms | 周期时长(毫秒) | +| 16 | project_hash | FNV-1a 64 | +| 24 | entry_fn_id | 入口函数(MAIN) | +| 28 | n_globals | 全局槽数 | +| 32/36/40 | n_i / n_q / n_m | **保留恒 0**(V1 遗产,兼容/将来语义) | +| 44 | n_consts | 常量表条目数 | +| 48 | n_funcs | 函数表行数 | +| 52/56/60 | offset_const / funcs / code | 段偏移 | +| 64 | offset_slots | 槽表段偏移 | +| 68 | offset_values | 值段偏移(16 对齐) | +| 72..103 | 型号标识[32] | `STATOR2`(name+version 补零) | +| 104 | n_slots | 槽表条目数(= n_globals + 实例/字段槽数) | +| 108 | values_size | 值段字节数 | +| 112 | offset_meta | 元数据段偏移(v1 = 0 空段) | +| 116 | max_stack | 栈区字节数(编译期算) | +| 120..127 | 预留 | 8 字节 | -### 文件尾(SHA-256) +### 8.2 段序 -数据段之后追加 **32 字节 SHA-256**:对文件尾之前全部内容计算。执行器必须校验通过才运行;compiler 写入。 - -### 常量表一行(12 字节) - -`[tag:u32][value:u64]`;`tag`:0=BOOL、1=INT、2=TIME。`value` 按 tag 解释,未用高位为 0。 - -### 函数表一行(12 字节) - -`[nregs:u32][code_offset:u32][code_len:u32]`;行下标即 `fn_id`,`code_offset` 相对字节码段起点,`code_len` 为指令条数。 - -### 字节码段 - -指令按 `u32` 连续排;`CALL` 的 `fn_id` 是函数表行下标。 - -### FB 布局段 - -第一版为空;每类型一行 `[field_count:u32][field_count × (tag:u32, offset:u32)]`,offset 相对实例基址,12.8 落地 codegen 时冻精确表。 - -### 数据段 - -槽初值按序排:全局(`n_globals`,v1 不拆 I/Q/M)→ FB 实例块(每字段一槽)。**每槽 8 字节定宽**(12.9 方案 a):`BOOL` 用低 1 字节、`INT` 用低 2 字节(小端)、`TIME` 全 8 字节。指令 `slot` 操作数即**槽号**,数据偏移 = 槽号 × 8;符号表 `address` 与指令 slot 一致,无映射。 - -工程哈希:收录源文件路径排序后对内容做 **FNV-1a 64**,只保证回放槽位一致,不是密码学哈希。FNV-1a 64:basis `0xcbf29ce484222325`,prime `0x100000001b3`。 - -`[[io.input]]` / `[[io.output]]` 不进映像。 - ---- - -## 映像文件(`.stb`)与 sidecar - -编译产物是单文件 `.stb`:映像头 + 各段按本文布局连续排,魔数 `STSC`、版本 `1`、小端。`STCompiler` 写,`BytecodeExecutor` 读。 - -- `dt_ms` / `cycle_limit` 在映像头,执行器只认映像,不重复配置。 -- I/O 绑定不进映像:`STCompiler` 另写 sidecar `.runtime.toml`,`BytecodeExecutor` 读 sidecar 做采样与写回,不创造变量。 - -sidecar 格式(TOML 子集,冻结): - -```toml -[[io.input]] # 或 [[io.output]] -var = "I0_0" # 已在 GVL 声明的名字 -slot = 1 # 全局槽号(编译期已解析) -channel = 0 -bit = 0 +```text +头(128) → 常量表(8B/条) → 函数表(12B/行) → 字节码(16 对齐) → +槽表段(8B/条) → 值段 → 元数据段(v1 空) → SHA-256[32] ``` -每行一个绑定;`var` 顺序不要求与映像槽序一致(`slot` 为准)。 +### 8.3 段细节 + +- **常量表行(8B)**:原始 64 位值(无 tag),LOADK 按 func 解释,按位模式去重 +- **函数表行(12B)**:`{ nregs:u32, code_offset:u32(字节), code_len:u32(字节) }` +- **槽表行(8B)**:`{ addr:u32, 预留:u32 }` +- **值段**:全局块 + FB 实例块(1/2/4/8 对齐,编译器布局) +- **元数据段**:v1 0 长度(offset_meta = 0) +- **文件尾**:SHA-256[32](对去掉尾的内容计算) + +### 8.4 校验(vm 读侧) + +1. 结构校验:魔数/版本/段偏移单调且在 SHA 前/常量表·函数表尺寸一致/代码段对齐/入口 fn_id 范围 +2. **SHA-256**:完整性(防篡改) +3. **型号匹配**:`STATOR2`(name + version=2 补零 32 字节逐字节比对) --- -## 目录与 CMake +## 9. `.stb` 与 sidecar + +- `.stb`(映像)+ `.runtime.toml`(sidecar:`[[io.input]]`/`[[io.output]]`,var→slot→channel/bit) +- I/O 绑定**不进映像**;绑定粒度 `slot = 槽号`(指向槽表条目),channel/bit 语义不变 +- executor 按 sidecar 采样 I / 写回 Q;`dt_ms`/`cycle_limit` 从映像头取 + +--- + +## 10. 反汇编格式(A7 定案) + +按 func 显示**类型后缀**: + +```text +LOAD.I16 r5, s3 STORE.U32 s1, r7 ADD.F64 r1, r2, r3 +MOVE r1, r2 LOAD_OFF.I8 r4, s2, 8 +JMP +4 JT r0, -1 CALL 1 CAL.TON s5 +``` + +前缀分区决定打印模板;010 区/16B 区按形态直推。 + +--- + +## 11. 目录与 CMake ```text isa/ CMakeLists.txt - README.md # 代码目录入口,指向本文 + README.md include/isa/ - Types.h # 宽度、饱和、槽值表示 - Op.h # 操作码枚举 + 助记符 - Instr.h # 32-bit 打包/拆字段(inline) - Image.h # 头、段、函数表/常量表/FB 布局 - Encode.h # 编解码、哈希、反汇编一行 + Types.h # 宽度/符号/饱和、除零边界 + Op.h # 前缀分区 + 操作码 + 形态 + Instr.h # 变长 pack/拆字段(inline) + Encode.h # encode / decode / disasm src/ Encode.cpp - Image.cpp ``` ```cmake @@ -248,12 +347,8 @@ target_include_directories(isa PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) --- -## 实现顺序(对齐初步计划 12.0~12.1) +## 12. 实现约定 -1. `Types.h`:三类型 + `sat_add` 等饱和函数(inline 即可) -2. `Op.h` + `Instr.h`:枚举与 32-bit 编解码 -3. `Encode.cpp`:`encode` / `decode` / `disasm` 一条指令 -4. `Image.h` + `Image.cpp`:头布局、FNV-1a、读写缓冲(先能写空映像 + 一条 `RET`) -5. 测试随后在 `tests/` 加编解码往返;本阶段以头文件和库能链为准 - -不在本模块做:VM `switch`、toml、关键字、符号表。 +- machine.toml(V2 schema)与代码**强校验**:前缀分区/op/func 互锁(见指令配置.md,阶段 1 重写) +- 操作码有变:先改 machine.toml + 本目录 + 测试,再动两端 +- 错误类别:`isa` 层无错误前缀(编解码纯函数);vm/compiler 各自带前缀