Files

179 lines
10 KiB
Markdown
Raw Permalink 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
`vm` 模块的指令执行语义:取指、译码、执行、故障。规范以 [`指令与映像.md`](../isa/指令与映像.md) 为准,本文描述**执行器怎么跑一条指令**。执行模型见 [`初步计划.md`](../初步计划.md) 第 2 节。
## 执行模型
```text
加载(Machine::create):解析 .stb(vm 自读实现)→ 型号匹配(内建 STATOR2)→ SHA-256 校验
任一不符 → 直接拒绝(不创建机器)
值区(data_)取映像值段的写副本(周期内 FB 字段/全局可变)
边沿缓冲 edge_prev_(每槽 2 字节,跨周期保留)
栈区预分配 max_stack 字节(栈方案 C,编译期算出)
每个扫描周期(run_cycle):
1. cycle_count = 0pc = 0,从入口函数(MAIN)逐条执行
2. 执行到 MAIN 的 RET → 周期结束
3. 周期末清调用栈(保留栈底 MAIN 帧,sp 回退到 MAIN 帧顶)
4. I 采样 / Q 写回由 executor 按 sidecar 做,VM 只管值区)
5. 定时器在 CAL 指令执行时用映像 dt_ms 推进;RTC 用累计周期数 × dt_ms
```
- **pc**:当前指令在函数字节码段内的**字节**下标(从 0 起)
- **取指**:函数表 `func_row(fn_id)``code_offset` + `code_len`(字节)→ pc 越界 → `BadOp``instr_len` 按前缀定长(4/8/16/32B)搬字节
- **译码**:前缀分区 + `[func:4|空:4]` 字节1`op` 按前缀分区查名;寄存器越界按 `uses_*` 直推(RRR/CC/RR 等)→ `BadOp`
- **周期计数**:每条指令执行后 `cycle_count++`;超过映像头 `cycle_limit``CycleLimit`(周期中止)
- **pc 先指向下一条(字节)**;跳转指令再 `pc += off`(off 相对下一条,与编译端 patch_jump 一致)
## 帧与调用栈(栈方案 C
```cpp
// 栈区(stack_,预分配 max_stack 字节):
// [MAIN 帧][帧2][帧3]... 从低地址起连续压栈
// 每帧 = 12B 帧头 + nregs×8 寄存器文件
// 帧头:hdr[0]=fn_id hdr[1]=ret_pc(字节) hdr[2]=ret_fn_id
```
- 调用栈深度**上限 64**`frames_.size() >= 64` 时 CALL → `StackOverflow`);同时 `sp_ + 帧大小 > max_stack``StackOverflow`(双检查)
- **MAIN 帧跨周期保留**(PROGRAM 变量状态持久);周期结束只清调用栈(保留栈底 MAIN 帧)
- 帧寄存器初值 0
### CALL / RET(调用约定)
```text
CALL fn_id:
1. 新帧:regs 大小 = callee 的 nregs,全部 0
2. 复制当前帧 r0..r7 → 新帧 r0..r7(实参已在调用点 MOVE 进 r1..r7)——全宽复制
3. 新帧 ret_pc = 当前 pc(已指向 CALL 下一条,字节)、ret_fn_id = 当前函数
4. 压栈,pc = 0fn_id = callee
RET:
1. 若当前是 MAIN(栈深 1)→ 周期结束(ended_ = true
2. 否则:复制当前帧 r0..r7 → 调用方帧 r0..r7r0 = 结果,r1..r7 原样返回)
3. 弹栈,pc = ret_pcfn_id = ret_fn_id
```
## 值表示(func 位段驱动)
| func | 语义 | 宽度 | 读值扩展 | 类型 |
|---|---|---|---|---|
| 0 | 无符号 | 1B | 高位清 0 | BOOL/BYTE 等 |
| 1 | 有符号 | 1B | 符号扩展 | SINT |
| 2 | 无符号 | 2B | 高位清 0 | WORD/UINT |
| 3 | 有符号 | 2B | 符号扩展 | INT |
| 4 | 无符号 | 4B | 高位清 0 | DWORD/UDINT |
| 5 | 有符号 | 4B | 符号扩展 | DINT |
| 6 | 无符号 | 8B | 原样 | LWORD/ULINT |
| 7 | 有符号 | 8B | 原样 | LINT |
| 8 | F32 | 4B | 位模式(int64 低 32 位) | REAL |
| 9 | F64 | 8B | 位模式原样 | LREAL |
- 寄存器(int64)统一承载:窄类型符号扩展/清 0 后,**全宽 int64 比较等价于各类型自身比较**;类型语义由编译期类型检查保证,VM 只按 func 解释
- TIME/DATE/TOD/DT 走 func 6/764 位整数值)
## 数据区访问(槽表两级)
```text
槽号 slot(指令操作数,u32)→ 槽表条目(8B:addr:u32 + 预留:u32)→ 值区字节偏移 addr
LOAD rd, slot → rd = 读 value[addr .. addr+宽)(按 func 宽度 + 扩展)
STORE slot, rs → 写 value[addr .. addr+宽) = rs(按 func 宽度截断)
LOAD_OFF rd, slot, off → 读 value[addr+off .. ]FB 字段访问,绝对槽)
STORE_OFF slot, off, rs → 写 value[addr+off .. ]FB 字段访问,绝对槽)
LOAD_PTR rd, rs, off → 读 value[regs[rs] + off .. ](基址寻址:rs = 实例指针,FB 共享体函数体内)
STORE_PTR rd, rs, off → 写 value[regs[rs] + off .. ](基址寻址)
```
槽表条目数 = `n_slots`;槽号越界(`slot >= n_slots`)或地址 + 宽度越出值段 → `BadSlot`
## 指令执行表
| 指令 | 流程 |
|---|---|
| `MOVE rd, rs` | `reg[rd] = reg[rs]`func 0..3 限宽 1/2/4/8B,高位清 0 |
| `LOADK rd, cid` | 查常量表 `const_entry(cid)`(8B 原始值):按 func 读低 w 字节 + 扩展(同 read_valueF32/F64 位模式) |
| `NOT rd, rs` | **逻辑非**`reg[rd] = (reg[rs] == 0) ? 1 : 0`(恒产 0/1 |
| `AND/OR/XOR/NAND/NOR/XNOR rd, ra, rb` | **位串逐位**64 位整域);BOOL 输入 0/1 下与逻辑等价 |
| `ADD/SUB/MUL/DIV rd, ra, rb` | 按 func:整数饱和(__int128 中间量)、无符号、F32/F64 IEEE |
| `CMP_EQ/NE/LT/LE/GT/GE rd, ra, rb` | 按 func 符号性比较(浮点 IEEE)→ `1` / `0` |
| `NEG rd, rs` | func 8/9 浮点取负(位模式),整数取负 |
| `JMP off` | `pc += off` |
| `JT rd, off` / `JF rd, off` | `reg[rd] != 0`JT/ `== 0`JF)时 `pc += off` |
| `LOAD / STORE` | 见数据区访问(槽表两级) |
| `LOAD_OFF / STORE_OFF` | 见数据区访问(FB 字段,绝对实例槽) |
| `LOAD_PTR / STORE_PTR` | 见数据区访问(基址寻址:rs 寄存器 = 值区地址 + 偏移;FB 共享体) |
| `CAL fb_id, slot` | 见内置 FB15 条) |
| `CALL fn_id` / `RET` | 见帧与调用栈 |
| `NOP` | 空转 |
### 整数算术(除法 A:饱和 + 截断)
```text
整数(func 0..7):sat_add/sat_sub/sat_mul/sat_div__int128 中间量,结果夹到类型范围)
sat_div:除零 → 返回被除数原值(不抛错);MIN / -1 → 夹到 MAX;其余截断向零
浮点(func 8/9):IEEE 754(除零 → inf;编译期类型检查保证使用域)
```
## 内置 FBCAL15 条)
`CAL fb_id, slot``slot` 是实例**槽号**(槽表条目指向实例基址);字段偏移**编译期对齐计算**(1/2/4/8),字段类型按 machine.toml `[[fb]]` 表。每次执行 = 推进一次,Δt = 映像头 `dt_ms`(不读系统时钟)。边沿检测的"上次输入"存 VM 侧 `edge_prev_`(每槽 2 字节),跨周期保留:
| fb_id | FB | 字段布局(偏移:类型) | 语义 |
|---|---|---|---|
| 0 | TON | in@0:Bpt@8:TIMEq@16:Bet@24:TIME | in 真:et += dtet ≥ pt 停在 pt);q = (et ≥ pt) |
| 1 | TOF | 同 TON | in 真:q=1、et=0in 掉电:et += dtet ≥ pt → q=0 |
| 2 | TP | 同 TON | in 上升沿启动 PT 时长脉冲;计时 et += dt,期满 q=0、et=0 |
| 3 | CTU | cu@0:Br@1:Bpv@2:INTq@4:Bcv@6:INT | cu 上升沿 cv += 1r → cv=0q = (cv ≥ pv) |
| 4 | DCTU | cu@0:Br@1:Bpv@4:DINTq@8:Bcv@12:DINT | 同 CTUDINT |
| 5 | CTD | cd@0:Bld@1:Bpv@2:INTq@4:Bcv@6:INT | cd 上升沿 cv -= 1ld → cv=pvq = (cv ≤ 0) |
| 6 | DCTD | 同 DCTU 布局 | 同 CTDDINT |
| 7 | CTUD | cu@0:Bcd@1:Br@2:Blu@3:Bpv@4:INTqu@6:Bqd@7:Bcv@8:INT | cu 上升沿 +1、cd 上升沿 -1r → 0lu → pvqu=(cv≥pv)、qd=(cv≤0) |
| 8 | DCTUD | cu@0cd@1r@2lu@3:Bpv@4:DINTqu@8qd@9:Bcv@12:DINT | 同 CTUDDINT |
| 9 | R_TRIG | clk@0:Bq@1:B | q = clk 且上次 clk 假(上升沿) |
| 10 | F_TRIG | clk@0:Bq@1:B | q = !clk 且上次 clk 真(下降沿) |
| 11 | SR | S1@0:BR@1:BQ1@2:B | **置位优先**S1 → Q1=1;否则 R → Q1=0 |
| 12 | RS | SET@0:BRESET1@1:BQ1@2:B | **复位优先**RESET1 → Q1=0;否则 SET → Q1=1 |
| 13 | PWM | in@0:Bpt@8:TIMEduty@16:REALq@20:Bet@24:TIME | in 真:et += dt(≥ pt 回绕);q = (et < pt × duty)in 假:et=0、q=0 |
| 14 | RTC | enable@0:Bdate@4:DATEtod@8:TOD | 虚拟时钟:累计周期数 × dt_ms(1970 纪元);enable 真时写 date/tod |
## 故障
| 故障 | 触发 | 周期行为 |
|---|---|---|
| `None` | — | 正常完成 |
| `CycleLimit` | 周期指令数 > `cycle_limit`(用例 6 | 中止,返回故障 |
| `StackOverflow` | 调用栈深度 > 64 或 `sp_ + 帧大小 > max_stack` | 中止 |
| `BadOp` | pc 越界 / 指令截断 / 前缀 RESERVED / 寄存器越界 / CAL fb_id 未知 | 中止 |
| `BadSlot` | 槽号 ≥ n_slots,或地址 + 宽度越出值段 | 中止 |
| `BadConst` | LOADK const_id ≥ n_consts | 中止 |
## 示例:line1 MAIN 逐指令推演(V2
映像(`--disasm` 实取):`dt_ms=10``cycle_limit=100000`,值区 7 字节;槽 0..4 → 值区 0..4(紧凑 U80=EmergencyStop、1=I0_0、2=I0_1、3=Q0_0、4=starter 实例)。
```text
初始:MAIN 帧 regs[13] 全 0;槽 0..4 全 0(初值 0)
假定 executor 已采样:槽 0 = EmergencyStop = 0、槽 1 = I0_0 = 1、槽 2 = I0_1 = 0
pc 指令 执行后
── ───────────────────── ──────────────────────────────
0x9c LOAD.U8 r8, s1 r8 = value[1] = 1I0_0
0xa4 STORE_OFF.U8 r8, s4, 0 starter.S1(实例基址 4 + 0= 1
0xb4 LOAD.U8 r9, s2 r9 = value[2] = 0I0_1
0xbc STORE_OFF.U8 r9, s4, 1 starter.R4 + 1= 0
0xcc LOAD_OFF.U8 r8, s4, 0 r8 = 1S1
0xdc JF r8, +24 r8≠0 不跳;pc = 0xe4 + 0
0xe4 LOAD_OFF.U8 r10, s4, 1 r10 = 0R
0xf4 NOT r9, r10 r9 = 1
0xf8 MOVE r8, r9 r8 = 1
0xfc JF r8, +16 r8≠0 不跳;pc = 0x104
0x104 LOAD.U8 r12, s0 r12 = value[0] = 0EmergencyStop
0x10c NOT r11, r12 r11 = 1
0x110 MOVE r8, r11 r8 = 1
0x114 STORE_OFF.U8 r8, s4, 2 starter.Q14 + 2= 1
0x124 LOAD_OFF.U8 r8, s4, 2 r8 = 1Q1
0x134 STORE.U8 r8, s3 Q0_0(槽 3= 1
0x13c RET 周期结束;executor 读槽 3 → 硬件
```
结果:`Q0_0 = 1`start=1、stop=0、急停=0 → 电机启动)——与 `EXPECTED.md` 真值表一致(vm_test 94 checks + executor 端到端验证)。