15 KiB
15 KiB
指令与映像(V2 规格,STATOR2)
状态:V2 规格(阶段 1 重写)。本文件为指令编码、类型、数据模型、映像格式的 规范,头文件与代码实现本文,不另当规范。 定案依据:
阶段1规格总览.md(主参考)、指令编码.md、指令集清单.md、指令集优化.md。 与 V1 关系:V1(32bit 指令、8 字节定宽槽、STATOR1)已实现并冻结;本文为全新 V2, 头version = 2、型号STATOR2,老映像被版本/型号校验拒绝。
compiler 写 .stb、vm 读 .stb,两边各自实现读写;isa 归执行器侧只管指令定义。
指令 opcode / 类型 / FB 布局的登记见 指令配置.md(compiler/machine.toml,与代码强校验)。
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 / RS+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 §8.1 |
3. 指令编码(详见指令编码.md)
指令 = 字节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 / RS+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/RS+off/IMM64 的 rd;RS+off 另含 rs(基址寄存器);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 |
| RS+off | rd=23..16、rs=31..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):字段访问(绝对实例槽,调用点/输出读用)LOAD_PTR rd, rs, off/STORE_PTR rd, rs, off(RS+off):基址寻址——rs 寄存器 = 值区地址(实例指针),addr+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);除零同 DIVNEG/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 §8.1。
RTC 时钟源 = 虚拟时钟(周期计数 × dt_ms,确定性,不读系统时钟)。
7. 数据模型:槽表 → 值区
槽表段(静态引用表,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 段序
头(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 读侧)
- 结构校验:魔数/版本/段偏移单调且在 SHA 前/常量表·函数表尺寸一致/代码段对齐/入口 fn_id 范围
- SHA-256:完整性(防篡改)
- 型号匹配:
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 显示类型后缀:
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
isa/
CMakeLists.txt
README.md
include/isa/
Types.h # 宽度/符号/饱和、除零边界
Op.h # 前缀分区 + 操作码 + 形态
Instr.h # 变长 pack/拆字段(inline)
Encode.h # encode / decode / disasm
src/
Encode.cpp
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 各自带前缀