355 lines
15 KiB
Markdown
355 lines
15 KiB
Markdown
# 指令与映像(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 | 15 条(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/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) | 4 |
|
||
| | | | | 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(15 条)
|
||
|
||
| 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 引用)、值区地址 u32(4GB)
|
||
- 槽表分配: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.toml(V2 schema)与代码**强校验**:前缀分区/op/func 互锁(见指令配置.md,阶段 1 重写)
|
||
- 操作码有变:先改 machine.toml + 本目录 + 测试,再动两端
|
||
- 错误类别:`isa` 层无错误前缀(编解码纯函数);vm/compiler 各自带前缀
|