Files
Interpreter/Doc/vm/指令执行.md
T

9.8 KiB
Raw Blame History

指令执行流程(V2

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

执行模型

加载(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 越界 → BadOpinstr_len 按前缀定长(4/8/16/32B)搬字节
  • 译码:前缀分区 + [func:4|空:4] 字节1op 按前缀分区查名;寄存器越界按 uses_* 直推(RRR/CC/RR 等)→ BadOp
  • 周期计数:每条指令执行后 cycle_count++;超过映像头 cycle_limitCycleLimit(周期中止)
  • pc 先指向下一条(字节);跳转指令再 pc += off(off 相对下一条,与编译端 patch_jump 一致)

帧与调用栈(栈方案 C

// 栈区(stack_,预分配 max_stack 字节):
// [MAIN 帧][帧2][帧3]... 从低地址起连续压栈
// 每帧 = 12B 帧头 + nregs×8 寄存器文件
// 帧头:hdr[0]=fn_id  hdr[1]=ret_pc(字节)  hdr[2]=ret_fn_id
  • 调用栈深度上限 64frames_.size() >= 64 时 CALL → StackOverflow);同时 sp_ + 帧大小 > max_stackStackOverflow(双检查)
  • MAIN 帧跨周期保留(PROGRAM 变量状态持久);周期结束只清调用栈(保留栈底 MAIN 帧)
  • 帧寄存器初值 0

CALL / RET(调用约定)

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 位整数值)

数据区访问(槽表两级)

槽号 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 字段访问)

槽表条目数 = 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] != 0JT/ == 0JF)时 pc += off
LOAD / STORE 见数据区访问(槽表两级)
LOAD_OFF / STORE_OFF 见数据区访问(FB 字段)
CAL fb_id, slot 见内置 FB15 条)
CALL fn_id / RET 见帧与调用栈
NOP 空转

整数算术(除法 A:饱和 + 截断)

整数(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, slotslot 是实例槽号(槽表条目指向实例基址);字段偏移编译期对齐计算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=10cycle_limit=100000,值区 7 字节;槽 0..4 → 值区 0..4(紧凑 U80=EmergencyStop、1=I0_0、2=I0_1、3=Q0_0、4=starter 实例)。

初始: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 = 1start=1、stop=0、急停=0 → 电机启动)——与 EXPECTED.md 真值表一致(vm_test 94 checks + executor 端到端验证)。