Files
Interpreter/Doc/vm/指令执行.md
T
Admin 26256a36b9 扩充内置功能块为 8 个:TON/TOF/TP/CTU/CTD/CTUD/R_TRIG/F_TRIG。
- isa:CAL_* 操作码连续占 24..31(CALL/RET 后移为 32/33,kOpCount=34);disasm 支持 5 个新操作码
- 词法/语法/链接:新 8 个关键字与冻结布局(TP=in/pt/q/et、CTD=cd/ld/pv/q/cv、
  CTUD=cu/cd/r/lu/pv/qu/qd/cv、R_TRIG/F_TRIG=clk/q)
- VM:do_cal 全 8 个语义(TP 脉冲、CTD 递减、CTUD 双向+装载、R_TRIG 上升沿、F_TRIG 下降沿);
  edge_prev_ 每槽 2 字节存边沿上次输入;exec_one 分发补 5 个新操作码
- 验证:TP 30ms 脉冲 2 周期、CTD 装载+递减到 q=1、CTUD cu/cd 双向、R/F_TRIG 交替;
  ctest 11/11 全绿
- 文档:指令与映像/词法/初步计划/符号表与链接/指令执行/扫描周期/使用说明 同步
2026-08-21 20:38:27 +08:00

165 lines
7.3 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.
# 指令执行流程(12.9
`vm` 模块的指令执行语义:取指、译码、执行、故障。规范以 [`指令与映像.md`](../isa/指令与映像.md) 为准,本文描述**执行器怎么跑一条指令**。执行模型见 [`初步计划.md`](../初步计划.md) 第 2 节。
## 执行模型
```text
每个扫描周期(run_cycle):
1. pc = 0,从入口函数(MAIN)开始逐条执行
2. 执行到 MAIN 的 RET → 周期结束
3. I 采样 / Q 写回由 executor 按 sidecar 做,VM 只管数据区)
4. 定时器在 CAL_* 指令执行时用映像 dt_ms 推进
```
- **pc**:当前指令在函数字节码段内的下标(指令条数单位,从 0 起)
- **取指**:函数表 `func_row(fn_id)``code_offset`(字节)→ `code_bytes[code_offset/4 + pc]` 读 u32(小端)
- **译码**:拆 `op / rd / a / b` 四字段,`a|b` 按操作码解释为寄存器对、槽号(u16)、或跳转偏移(int16)
## 帧与调用栈
```cpp
struct Frame {
uint32_t fn_id; // 当前函数
std::vector<int64_t> regs; // 寄存器文件,大小 = 函数头 nregs
uint32_t ret_pc; // CALL 后应恢复的 pc(仅非 MAIN 帧有意义)
uint32_t ret_fn_id; // 返回目标函数(调用方)
};
```
- 调用栈 `std::vector<Frame>`**深度上限 64**;超限 → `StackOverflow` 故障
- **MAIN 帧跨周期保留**(PROGRAM 变量状态持久);周期结束只清调用栈(保留栈底 MAIN 帧)
- 帧寄存器初值 0
### CALL / RET(调用约定,冻结于 12.8)
```text
CALL fn_id:
1. 新帧:regs 大小 = callee 的 nregs,全部 0
2. 复制当前帧 r0..r7 → 新帧 r0..r7(实参已在调用点 MOVE 进 r1..r7
3. 新帧 ret_pc = 当前 pc + 1CALL 的下一条)、ret_fn_id = 当前函数
4. 压栈,pc = 0fn_id = callee
RET:
1. 若当前是 MAIN(栈深 0)→ 周期结束
2. 否则:复制当前帧 r0..r7 → 调用方帧 r0..r7r0 = 结果,r1..r7 原样返回)
3. 弹栈,pc = ret_pcfn_id = ret_fn_id
```
## 值表示(寄存器 / 槽统一约定)
| 类型 | 表示 | 说明 |
|---|---|---|
| BOOL | `0` / `1` | 逻辑指令产出、字面量 TRUE=1 |
| INT | `int16_t` 符号扩展 | `sat_*` 返回 int16,赋给 int64 寄存器自动符号扩展 |
| TIME | 全 64 位 | 毫秒 |
推论:**全宽 int64 比较等价于各类型自身比较**(INT 符号扩展后 -5 < 3 正确)。类型语义由编译期类型检查保证,VM 不查类型。
## 数据区访问(8 字节定宽槽,方案 a)
```text
槽号 slot(指令操作数)→ 数据偏移 = slot × 8
LOAD_* rd, slot → rd = 读 data[slot*8 .. slot*8+8)8 字节原样)
STORE_* slot, rs → 写 data[slot*8 .. slot*8+8) = rs8 字节原样)
```
`LOAD_I` / `LOAD_M` / `LOAD_GLOBAL` / `STORE_Q` / `STORE_M` / `STORE_GLOBAL` 执行逻辑完全相同(v1 单一数据区,操作码只作语义标签)。
## 指令执行表
| 指令 | 流程 |
|---|---|
| `MOVE rd, rs` | `reg[rd] = reg[rs]` |
| `LOADK rd, cid` | 查常量表 `const_entry(cid)`BOOL → 0/1INT → `(int16_t)value` 符号扩展;TIME → 全宽 |
| `NOT rd, rs` | `reg[rd] = (reg[rs] == 0) ? 1 : 0` |
| `AND/OR rd, ra, rb` | `reg[rd] = (reg[ra] != 0 && reg[rb] != 0) ? 1 : 0`(或 OR |
| `ADD/SUB/MUL/DIV rd, ra, rb` | `sat_add((int16)ra, (int16)rb)` 等,结果符号扩展入 rd |
| `CMP_xx rd, ra, rb` | 全宽 int64 比较 → `1` / `0` |
| `JMP off` | `pc += off`(见跳转语义) |
| `JT rd, off` / `JF rd, off` | `reg[rd] != 0`JT/ `== 0`JF)时 `pc += off`,否则正常 +1 |
| `LOAD_* rd, slot` | 见数据区访问 |
| `STORE_* slot, rs` | 见数据区访问 |
| `CAL_TON/TOF/TP/CTU/CTD/CTUD/R_TRIG/F_TRIG slot` | 见定时器与边沿推进 |
| `CALL fn_id` / `RET` | 见帧与调用栈 |
### 跳转语义
```text
指令执行后 pc 先 +1(指向下一条),跳转指令再 pc += off
⇒ 目标下标 = 当前 + 1 + off(与编译端 patch_jump 一致)
```
### 周期指令计数
- 每条指令执行后 `cycle_count++`;超过映像头 `cycle_limit``CycleLimit` 故障(周期中止)
- `JMP` / `JT` / `JF` / `CALL` 只算 1 条(cycle_limit 以"执行的指令数"计)
## 定时器与边沿推进(CAL_TON / CAL_TOF / CAL_TP / CAL_CTU / CAL_CTD / CAL_CTUD / CAL_R_TRIG / CAL_F_TRIG
`CAL_* slot``slot` 是实例**基槽号**;字段偏移 = 字段序号 × 8(布局冻结:TON/TOF/TP = in/pt/q/etCTU = cu/r/pv/q/cvCTD = cd/ld/pv/q/cvCTUD = cu/cd/r/lu/pv/qu/qd/cvR_TRIG/F_TRIG = clk/q)。每次执行 = 推进一次,Δt = 映像头 `dt_ms`(不读系统时钟)。边沿检测的"上次输入"存 VM 侧 `edge_prev_`(每实例基槽 2 字节),跨周期保留:
```text
CAL_TON
in 为真:et += dtet ≥ pt 时停在 pt);q = (et ≥ pt) ? 1 : 0
in 为假:et = 0q = 0
CAL_TOF
in 为真:q = 1et = 0
in 掉电:et += dtet ≥ pt 时 q = 0
CAL_TP(脉冲):
in 上升沿启动 PT 时长脉冲(期间 in 变化不影响)
计时中:et += dtet ≥ pt → q = 0、et 归零(脉冲结束);否则 q = 1
非计时:et = 0q = 0
CAL_CTUcu 上升沿 → cv += 1r → cv = 0q = (cv ≥ pv) ? 1 : 0
CAL_CTDcd 上升沿 → cv -= 1ld → cv = pvq = (cv ≤ 0) ? 1 : 0
CAL_CTUDcu 上升沿 → cv += 1cd 上升沿 → cv -= 1r → cv = 0lu → cv = pv
qu = (cv ≥ pv) ? 1 : 0qd = (cv ≤ 0) ? 1 : 0
CAL_R_TRIGq = clk 且上次 clk 假(上升沿)
CAL_F_TRIGq = !clk 且上次 clk 真(下降沿)
```
## 故障
| 故障 | 触发 | 周期行为 |
|---|---|---|
| `None` | — | 正常完成 |
| `CycleLimit` | 周期指令数 > `cycle_limit`(用例 6 | 中止,返回故障 |
| `StackOverflow` | 调用栈深度 > 64 | 中止 |
| `BadOp` | op ≥ 29 | 中止 |
| `BadSlot` | slot × 8 + 8 > 数据区长度 | 中止 |
| `BadConst` | const_id ≥ n_consts | 中止 |
## 示例:line1 MAIN 逐指令推演
映像:`dt_ms=10``cycle_limit=100000`,数据区 56 字节(7 槽 × 8B,方案 a)。
```text
初始:MAIN 帧 regs[13] 全 0;槽 0..6 全 0(初值 0)
假定 executor 已采样:槽 0 = EmergencyStop = 0、槽 1 = I0_0 = 1、槽 2 = I0_1 = 0
pc 指令 执行后
── ───────────────────── ──────────────────────────────
0 LOAD_I r8, 1 r8 = data[8..16) 低字节 = 1I0_0
1 STORE_GLOBAL r8, 4 槽 4starter.start= 1
2 LOAD_GLOBAL r9, 2 r9 = 0I0_1
3 STORE_GLOBAL r9, 5 槽 5starter.stop= 0
4 LOAD_GLOBAL r8, 8 内联体:r8 = 槽 4 = 1start
5 JF r8, +3 r8≠0 不跳;pc = 6
6 LOAD_GLOBAL r10, 9 r10 = 槽 5 = 0stop
7 NOT r9, r10 r9 = 1
8 MOVE r8, r9 r8 = 1
9 JF r8, +3 r8≠0 不跳;pc = 10
10 LOAD_I r12, 0 r12 = 槽 0 = 0EmergencyStop
11 NOT r11, r12 r11 = 1
12 MOVE r8, r11 r8 = 1
13 STORE_GLOBAL r8, 10 槽 6starter.q= 1
14 LOAD_GLOBAL r8, 10 r8 = 槽 6 = 1
15 STORE_Q r8, 3 槽 3Q0_0= 1
16 RET 周期结束;executor 读槽 3 → 硬件
```
结果:`Q0_0 = 1`start=1、stop=0、急停=0 → 电机启动)——与 `EXPECTED.md` 真值表一致。