Files
Interpreter/vm/include/vm/Machine.h
T
Admin fff7aef968 vm 模块注释完善:Doxygen 风格。
- Image.h/.cpp:头 104 布局逐字段、SHA-256 增量状态机(update/process/final)、
  校验项清单(from)、型号标识契约、func_row/const_entry/data_len 等访问器语义
- Machine.h/.cpp:Fault 枚举逐项、create 校验顺序(解析→SHA→型号)、
  调用约定(r0 结果/r1..r7 实参)、exec_one 取指/计数/分发流程、
  do_call/do_ret 帧语义、do_cal 八 FB 字段布局、slot_get/set 边界契约
2026-08-21 23:21:51 +08:00

160 lines
6.2 KiB
C++
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.
/**
* @file Machine.h
* @brief 寄存器虚拟机(12.9
* @author
* @date 2026-08-21
*
* @details 设计说明(详见 Doc/vm/扫描周期.md 与 Doc/vm/指令执行.md):
* - 只认 .stb 映像(vm 自带读实现;Machine::create 做型号/SHA-256 校验);
* 无 GC、无堆、无线程
* - 扫描周期:MAIN pc=0 → RET;MAIN 帧跨周期保留,调用栈深度上限 64
* - 帧:{ fn_id, regs[nregs], ret_pc, ret_fn_id }CALL 压帧复制 r0..r7
* RET 复制回调用方(r0=结果)
* - 数据区:8 字节定宽槽(方案 a),指令 slot = 槽号,偏移 = slot × 8
* - 可观察性(v1 预留):step / pc / fn_id / cycle_count / call_depth / reg
*/
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include "vm/Image.h"
#include "isa/Instr.h"
namespace vm {
/**
* @brief 周期/指令故障码。
*/
enum class Fault {
None, ///< 正常
CycleLimit, ///< 本周期指令数超过映像头 cycle_limit(用例 6
StackOverflow, ///< 调用栈深度超过 64
BadOp, ///< 非法操作码 / 寄存器越界 / CALL 目标越出函数表
BadSlot, ///< slot × 8 + 8 越出数据区
BadConst, ///< const_id 越出常量表
};
/**
* @brief 寄存器虚拟机:按扫描周期执行 .stb 映像。
* @details 一个实例 = 一台机器:持有映像只读视图 + 数据区工作副本 + 调用栈。
* MAIN 帧跨周期保留(PROGRAM 变量状态持久);每周期从 pc=0 执行到
* MAIN 的 RET,期间调用栈可进可出(上限 64 帧)。
* I 采样写入 / Q 读回由外部(executor)经 data() 完成,VM 只管数据区。
*/
class Machine {
public:
/**
* @brief 从映像字节构建机器(校验 + 拷贝数据段 + 建 MAIN 帧)。
* @param image .stb 映像字节
* @param out 输出 Machine(未初始化)
* @param err 错误输出;可为 nullptr(静默)
* @return true 成功;false(映像非法,err 已写)
* @details 校验顺序:解析 .stbImage::from)→ SHA-256 → 型号标识匹配
* (内建 kModelName + kModelVersion),任一不符直接拒绝。
*/
static bool create(const std::vector<uint8_t>& image, Machine* out,
std::string* err);
/**
* @brief 跑一个扫描周期:MAIN pc=0 执行到 RET。
* @return 故障(None = 正常完成);调用栈清空但 MAIN 帧保留
*/
Fault run_cycle();
// ---- 可观察性(v1 预留,12.11 供单步/回放/TUI----
/**
* @brief 执行一条指令,停在指令边界。
* @return true 继续;false 周期结束(MAIN 的 RET)或发生故障
*/
bool step();
/// @brief 最近一次故障(step 返回 false 后查询)。
Fault fault() const;
/// @brief 周期是否已结束(MAIN 的 RET 后为 true)。
bool ended() const;
/// @brief 映像头(dt_ms / cycle_limit 等)。
const vm::ImageHeader& header() const;
/// @brief 当前指令下标(相对当前函数字节码段)。
uint32_t pc() const;
/// @brief 当前函数 fn_id。
uint32_t fn_id() const;
/// @brief 本周期已执行指令数。
uint32_t cycle_count() const;
/// @brief 调用栈深度(MAIN 帧 = 1)。
uint32_t call_depth() const;
/// @brief 数据区(I 采样写入 / Q 读回由外部做;8 字节定宽槽)。
uint8_t* data();
const uint8_t* data() const;
/// @brief 数据区字节数。
size_t data_len() const;
/// @brief 当前帧寄存器值(i < nregs)。
int64_t reg(uint8_t i) const;
/// @brief 当前帧寄存器数(函数头 nregs)。
uint32_t nregs() const;
/// @brief 当前指令(pc_ 处;单步/观察用;pc 越界时返回 RET 哨兵)。
isa::Instr cur_instr() const;
private:
/**
* @brief 调用帧。
*/
struct Frame {
uint32_t fn_id = 0; ///< 本帧函数
std::vector<int64_t> regs; ///< 寄存器文件(大小 = 函数头 nregs)
uint32_t ret_pc = 0; ///< CALL 的下一条(非 MAIN 帧有意义)
uint32_t ret_fn_id = 0; ///< 返回目标函数
};
vm::Image image_; ///< 映像只读视图(vm 自实现,指向外部缓冲)
std::vector<uint8_t> data_; ///< 数据区工作副本(8 字节定宽槽)
std::vector<Frame> frames_; ///< 调用栈([0] = MAIN,跨周期保留)
/**
* @brief 边沿检测上次输入(每槽 2 字节:[slot*2] 第一边沿、[slot*2+1] 第二边沿;
* CTU/CTD/R_TRIG/F_TRIG 用第一、CTUD 用两个),跨周期保留。
*/
std::vector<uint8_t> edge_prev_;
uint32_t pc_ = 0; ///< 当前 pc(相对本帧函数字节码段)
uint32_t cycle_count_ = 0; ///< 本周期指令数
Fault fault_ = Fault::None;///< 最近一次故障
bool ended_ = false; ///< step 后周期是否已结束
/// @brief 当前(栈顶)帧引用。
Frame& cur_frame();
/// @brief 当前(栈顶)帧常量引用。
const Frame& cur_frame() const;
/// @brief 译码执行一条;false = 故障或周期结束。
bool exec_one();
/// @brief 压帧(复制 r0..r7);fn_id 越界以 BadOp 表示。
Fault do_call(uint32_t fn_id);
/// @brief 弹帧 / 周期结束(复制 r0..r7 回调用方)。
Fault do_ret();
/// @brief 执行内置 FBTON/TOF/TP/CTU/CTD/CTUD/R_TRIG/F_TRIG)。
Fault do_cal(uint8_t op, uint16_t slot);
/// @brief 读数据区槽(8 字节定宽;越界返回 false)。
bool slot_get(uint16_t slot, int64_t* out) const;
/// @brief 写数据区槽(8 字节定宽;越界返回 false)。
bool slot_set(uint16_t slot, int64_t v);
};
}