Files
Interpreter/Doc/isa/指令与映像.md
T
Admin 59d7ff3159 阶段1-1:指令与映像.md 重写为 V2 规格(STATOR2)
- §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 实现约定
2026-08-25 11:29:15 +08:00

355 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 指令与映像(V2 规格,STATOR2)
> 状态:**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`,与代码强校验)。
```text
STCompiler → compiler (编码查 machine.toml;自带 .stb 写)
BytecodeExecutor → vm → isa isa 只管指令;vm 自带 .stb 读)
```
CMake 目标:`isa``STATIC`),无依赖;`vm` PUBLIC 链 `isa`。公开头:`isa/include/isa/`
---
## 1. 做 / 不做
**做**
- 操作码(前缀分区)与**变长指令**布局(4/8/16/32B
- 映像头 128B 与段(常量表 / 函数表 / 字节码 / 槽表段 / 值段 / 元数据段)
- 19 种 IEC 类型 → 10 种 func 的宽度/符号表、饱和与除法边界
- 编解码、工程哈希、一条指令的文本转储(反汇编,给 STCompiler `--disasm` 与测试打印)
**不做**
- toml 字段、关键字表、符号表、AST
- 扫描周期循环、I/O 采样
- 短路语义(`AND`/`OR` 指令只组合已算好的值;短路由 `compiler` 编成跳转)
指令表可作为内部 IR 的文本形态,不必当主语言。
---
## 2. 特性总表(V2
| 项 | V2 定案 |
|---|---|
| 指令宽度 | **变长 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 位寻址 |
| 字节序 | 小端 |
| 映像 | 魔数 `STSC`、**version = 2**、型号 `STATOR2` |
| 工程哈希 | 源文件路径排序后对内容做 FNV-1a 64(非密码学) |
| 完整性 | 文件尾 SHA-256[32];型号标识[32]@72 |
| I/O 绑定 | **不进映像**sidecar`.runtime.toml`)绑定 var→槽号→channel/bit,粒度不变 |
| 内建 FB | 14 条(CAL + fb_id 16 位);字段布局见 [`指令集清单.md`](指令集清单.md) §8.1 |
---
## 3. 指令编码(详见指令编码.md)
```text
指令 = 字节0 + 字节1 + 操作数区(按形态)
字节 0 字节 1
[长度前缀+形态分区 | op] [func:4 | 空:4]
```
### 3.1 前缀分区与 op 空间
| 前缀 | 长度 | 形态 | 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/rsIMM/SLOT/JC/SLOT+off/IMM64 仅 rdJMP/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,百字节级)
- 寄存器值 int64codegen `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`SLOTfunc 全矩阵):槽表[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`RRfunc = 目标类型)
### 6.4 比较
- `CMP_EQ..GE`RRRfunc 全矩阵):按 func 有/无符号、浮点 IEEENaN 传播)
### 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 内建 FB14 条)
| 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
槽表段(静态引用表,8B/条) 值段(初值,紧凑变宽)
{ addr:u32, 预留:u32 } ──地址──▶ BOOL 1B / INT 2B / DINT·REAL 4B / TIME·LREAL 8B
槽 0..n_slots-1 结构体 = 连续字段块(v1 预留)
```
- **访问**`LOAD rd, slot``addr = 槽表[slot].addr` → 值区按 func 读宽度;字段访问 `LOAD rd, slot, off``addr + off`
- **两级间接**:每次访问多一次内存读(PLC 规模无感)
- **对齐**:1/2/4/8B,编译器布局保证;值段起点 16 对齐
- **初值**:编译期写值段(变量初值、FB 实例字段 0);运行时拷贝工作副本,文件只读
- **寻址**:槽号 u32(2^32 引用)、值区地址 u324GB)
- 槽表分配:n_slots = n_globals(全局声明序)+ FB 实例/字段槽数
---
## 8. 映像格式
### 8.1 头(128 字节,16 对齐)
| 偏移 | 字段 | 说明 |
|---|---|---|
| 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 字节 |
### 8.2 段序
```text
头(128) → 常量表(8B/条) → 函数表(12B/行) → 字节码(16 对齐) →
槽表段(8B/条) → 值段 → 元数据段(v1 空) → SHA-256[32]
```
### 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 字节逐字节比对)
---
## 9. `.stb` 与 sidecar
- `<name>.stb`(映像)+ `<name>.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
include/isa/
Types.h # 宽度/符号/饱和、除零边界
Op.h # 前缀分区 + 操作码 + 形态
Instr.h # 变长 pack/拆字段(inline
Encode.h # encode / decode / disasm
src/
Encode.cpp
```
```cmake
add_library(isa STATIC src/Encode.cpp)
target_include_directories(isa PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
```
`vm``target_link_libraries(vm PUBLIC isa)``compiler` 不链 `isa`
---
## 12. 实现约定
- machine.tomlV2 schema)与代码**强校验**:前缀分区/op/func 互锁(见指令配置.md,阶段 1 重写)
- 操作码有变:先改 machine.toml + 本目录 + 测试,再动两端
- 错误类别:`isa` 层无错误前缀(编解码纯函数);vm/compiler 各自带前缀