Files
Interpreter/Doc/vm/指令执行.md
T
Admin 3c42a72e86 新增指令执行流程文档(12.9 前置)。
- Doc/vm/指令执行.md:执行模型、帧/调用栈(CALL/RET 复制 r0..r7)、值表示约定、
  8 字节定宽槽访问、29 条指令执行表、跳转语义、cycle_limit 计数、
  CAL_TON/TOF/CTU Δt 推进、五类故障
- line1 MAIN 逐指令推演(I0_0=1、stop=0、急停=0 → Q0_0=1,对照真值表)
- 索引登记
2026-08-21 19:50:39 +08:00

6.8 KiB
Raw Blame History

指令执行流程(12.9

vm 模块的指令执行语义:取指、译码、执行、故障。规范以 指令与映像.md 为准,本文描述执行器怎么跑一条指令。执行模型见 初步计划.md 第 2 节。

执行模型

每个扫描周期(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)

帧与调用栈

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)

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

槽号 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] != 0JT/ == 0JF)时 pc += off,否则正常 +1
LOAD_* rd, slot 见数据区访问
STORE_* slot, rs 见数据区访问
CAL_TON/TOF/CTU slot 见定时器推进
CALL fn_id / RET 见帧与调用栈

跳转语义

指令执行后 pc 先 +1(指向下一条),跳转指令再 pc += off
⇒ 目标下标 = 当前 + 1 + off(与编译端 patch_jump 一致)

周期指令计数

  • 每条指令执行后 cycle_count++;超过映像头 cycle_limitCycleLimit 故障(周期中止)
  • JMP / JT / JF / CALL 只算 1 条(cycle_limit 以"执行的指令数"计)

定时器推进(CAL_TON / CAL_TOF / CAL_CTU

CAL_* slotslot 是实例基槽号;字段偏移 = 实例布局字段序号 × 8(内建布局冻结:TON/TOF = in/pt/q/etCTU = cu/r/pv/q/cv)。每次执行 = 推进一次,Δt = 映像头 dt_ms(不读系统时钟):

CAL_TON
  in = 槽[in] 非零;pt = 槽[pt]et = 槽[et]q = 槽[q]
  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
  in 再次为真 → 重新 q = 1, et = 0

CAL_CTU
  上升沿检测:本周期 cu 为真且上次 cu 为假 → cv += 1
  (上次 cu 存 VM 侧 vector,按实例基槽索引,跨周期保留)
  r 为真 → cv = 0
  q = (cv ≥ pv) ? 1 : 0

故障

故障 触发 周期行为
None 正常完成
CycleLimit 周期指令数 > cycle_limit(用例 6 中止,返回故障
StackOverflow 调用栈深度 > 64 中止
BadOp op ≥ 29 中止
BadSlot slot × 8 + 8 > 数据区长度 中止
BadConst const_id ≥ n_consts 中止

示例:line1 MAIN 逐指令推演

映像:dt_ms=10cycle_limit=100000,数据区 56 字节(7 槽 × 8B,方案 a)。

初始: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 = 1start=1、stop=0、急停=0 → 电机启动)——与 EXPECTED.md 真值表一致。