Files
Admin 6873b5ee0b 文档同步:machine.toml 配置驱动架构收尾。
- README.md:依赖方向(STCompiler→compiler 不链 isa)、模块表、isa 归执行器侧、
  数据流图(machine.toml + 型号/SHA)
- isa/README.md:重写——只管指令定义(Types/Op/Instr/Encode),删 Image 部分
- 指令与映像.md:依赖图与 CMake(compiler 不链 isa)
- 编译管线.md:管线首步加载 machine.toml、产物含型号/SHA
- 初步计划.md:12.10/12.13 修订(--machine、依赖方向)
- 扫描周期.md / 指令执行.md:12.9 步骤标注已落地、vm 读校验说明
- compiler/vm/executor README:依赖与边界同步
2026-08-21 22:39:27 +08:00

168 lines
7.5 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.
# 指令执行流程(12.9
`vm` 模块的指令执行语义:取指、译码、执行、故障。规范以 [`指令与映像.md`](../isa/指令与映像.md) 为准,本文描述**执行器怎么跑一条指令**。执行模型见 [`初步计划.md`](../初步计划.md) 第 2 节。
## 执行模型
```text
加载(Machine::create):解析 .stb(vm 自读实现)→ 型号匹配(内建 STATOR1)→ SHA-256 校验
任一不符 → 直接拒绝(不创建机器)
每个扫描周期(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` 真值表一致。