- 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 边界契约
160 lines
6.2 KiB
C++
160 lines
6.2 KiB
C++
/**
|
||
* @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 校验顺序:解析 .stb(Image::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 执行内置 FB(TON/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);
|
||
};
|
||
}
|