扩充内置功能块为 8 个:TON/TOF/TP/CTU/CTD/CTUD/R_TRIG/F_TRIG。

- isa:CAL_* 操作码连续占 24..31(CALL/RET 后移为 32/33,kOpCount=34);disasm 支持 5 个新操作码
- 词法/语法/链接:新 8 个关键字与冻结布局(TP=in/pt/q/et、CTD=cd/ld/pv/q/cv、
  CTUD=cu/cd/r/lu/pv/qu/qd/cv、R_TRIG/F_TRIG=clk/q)
- VM:do_cal 全 8 个语义(TP 脉冲、CTD 递减、CTUD 双向+装载、R_TRIG 上升沿、F_TRIG 下降沿);
  edge_prev_ 每槽 2 字节存边沿上次输入;exec_one 分发补 5 个新操作码
- 验证:TP 30ms 脉冲 2 周期、CTD 装载+递减到 q=1、CTUD cu/cd 双向、R/F_TRIG 交替;
  ctest 11/11 全绿
- 文档:指令与映像/词法/初步计划/符号表与链接/指令执行/扫描周期/使用说明 同步
This commit is contained in:
2026-08-21 20:38:27 +08:00
parent 66d9c6c548
commit 26256a36b9
18 changed files with 249 additions and 64 deletions
+116 -22
View File
@@ -35,7 +35,7 @@ bool Machine::create(const std::vector<uint8_t>& image, Machine* out,
// 数据区工作副本(8 字节定宽槽)
out->data_.assign(out->image_.data_bytes(),
out->image_.data_bytes() + out->image_.data_len());
out->ctu_prev_.assign(out->data_len() / 8, 0);
out->edge_prev_.assign(out->data_len() / 8 * 2, 0);
// MAIN 帧(跨周期保留)
const uint32_t entry = out->image_.header().entry_fn_id;
@@ -265,7 +265,12 @@ bool Machine::exec_one() {
return !ended_; // MAIN 的 RET → 周期结束(返回 false)
case isa::Op::CAL_TON:
case isa::Op::CAL_TOF:
case isa::Op::CAL_TP:
case isa::Op::CAL_CTU:
case isa::Op::CAL_CTD:
case isa::Op::CAL_CTUD:
case isa::Op::CAL_R_TRIG:
case isa::Op::CAL_F_TRIG:
fault_ = do_cal(static_cast<uint8_t>(op), isa::imm16(w));
return fault_ == Fault::None;
default:
@@ -340,15 +345,18 @@ Fault Machine::do_ret() {
Fault Machine::do_cal(uint8_t op, uint16_t slot) {
// 内建 FB 布局冻结(Doc/compiler/符号表与链接.md):
// TON/TOFin/pt/q/et(字段 0/1/2/3
// CTUcu/r/pv/q/cv字段 0/1/2/3/4
// 实例槽号从 slot(基槽)起;字段偏移 = 字段序号 × 8(8 字节定宽
// TON/TOF/TPin/pt/q/et(字段 0/1/2/3
// CTUcu/r/pv/q/cv0/1/2/3/4CTDcd/ld/pv/q/cv(同)
// CTUDcu/cd/r/lu/pv/qu/qd/cv0..7
// R_TRIG / F_TRIGclk/q0/1
// 字段偏移 = 字段序号 × 8(8 字节定宽槽);边沿存 edge_prev_
const int64_t dt = image_.header().dt_ms;
int64_t v = 0;
if (op == static_cast<uint8_t>(isa::Op::CAL_TON) ||
op == static_cast<uint8_t>(isa::Op::CAL_TOF)) {
// 字段:in=0 pt=1 q=2 et=3
const bool is_ton = op == static_cast<uint8_t>(isa::Op::CAL_TON);
const bool is_tof = op == static_cast<uint8_t>(isa::Op::CAL_TOF);
if (is_ton || is_tof) {
// in=0 pt=1 q=2 et=3
if (!slot_get(static_cast<uint16_t>(slot + 0), &v)) return Fault::BadSlot;
const bool in = v != 0;
if (!slot_get(static_cast<uint16_t>(slot + 1), &v)) return Fault::BadSlot;
@@ -356,7 +364,7 @@ Fault Machine::do_cal(uint8_t op, uint16_t slot) {
int64_t et = 0;
if (!slot_get(static_cast<uint16_t>(slot + 3), &et)) return Fault::BadSlot;
int64_t q = 0;
if (op == static_cast<uint8_t>(isa::Op::CAL_TON)) {
if (is_ton) {
// TONin 真 → et += dt(到 pt 停)、q = et ≥ ptin 假 → et = 0、q = 0
if (in) {
et += dt;
@@ -383,29 +391,115 @@ Fault Machine::do_cal(uint8_t op, uint16_t slot) {
return Fault::None;
}
if (op == static_cast<uint8_t>(isa::Op::CAL_CTU)) {
// 字段:cu=0 r=1 pv=2 q=3 cv=4
if (slot >= ctu_prev_.size()) return Fault::BadSlot;
if (op == static_cast<uint8_t>(isa::Op::CAL_TP)) {
// in=0 pt=1 q=2 et=3 + 上次 inedge_prev_[slot*2]
if (static_cast<size_t>(slot) * 2 + 1 >= edge_prev_.size()) return Fault::BadSlot;
if (!slot_get(static_cast<uint16_t>(slot + 0), &v)) return Fault::BadSlot;
const bool cu = v != 0;
const bool in = v != 0;
if (!slot_get(static_cast<uint16_t>(slot + 1), &v)) return Fault::BadSlot;
const bool r = v != 0;
const int64_t pt = v;
int64_t et = 0;
if (!slot_get(static_cast<uint16_t>(slot + 3), &et)) return Fault::BadSlot;
// TP(脉冲):in 上升沿启动 PT 时长的脉冲,期间 in 变化不影响
const bool prev = edge_prev_[slot * 2] != 0;
const bool rising = in && !prev;
int64_t q = 0;
if (rising) {
et = 0;
}
const bool timing = rising || (et > 0);
if (timing) {
et += dt;
if (pt > 0 && et >= pt) {
et = 0; // 脉冲结束:et 归零,下周期不再计时
q = 0;
} else {
q = 1;
}
} else {
et = 0;
q = 0;
}
edge_prev_[slot * 2] = in ? 1 : 0;
if (!slot_set(static_cast<uint16_t>(slot + 3), et)) return Fault::BadSlot;
if (!slot_set(static_cast<uint16_t>(slot + 2), q)) return Fault::BadSlot;
return Fault::None;
}
if (op == static_cast<uint8_t>(isa::Op::CAL_CTU) ||
op == static_cast<uint8_t>(isa::Op::CAL_CTD)) {
// CTUcu/r/pv/q/cvCTDcd/ld/pv/q/cv
if (static_cast<size_t>(slot) * 2 >= edge_prev_.size()) return Fault::BadSlot;
if (!slot_get(static_cast<uint16_t>(slot + 0), &v)) return Fault::BadSlot;
const bool edge_in = v != 0; // cuCTU/ cdCTD
if (!slot_get(static_cast<uint16_t>(slot + 1), &v)) return Fault::BadSlot;
const bool ld_in = v != 0; // rCTU/ ldCTD
if (!slot_get(static_cast<uint16_t>(slot + 2), &v)) return Fault::BadSlot;
const int64_t pv = v;
int64_t cv = 0;
if (!slot_get(static_cast<uint16_t>(slot + 4), &cv)) return Fault::BadSlot;
// 上升沿:本周期 cu 真且上次 cu 假 → cv + 1;r → 复位
const bool prev = ctu_prev_[slot] != 0;
const bool prev = edge_prev_[slot * 2] != 0;
if (ld_in) {
cv = pv;
} else if (edge_in && !prev) {
cv += (op == static_cast<uint8_t>(isa::Op::CAL_CTU)) ? 1 : -1;
}
edge_prev_[slot * 2] = edge_in ? 1 : 0;
const int64_t q = (op == static_cast<uint8_t>(isa::Op::CAL_CTU))
? ((cv >= pv) ? 1 : 0)
: ((cv <= 0) ? 1 : 0);
if (!slot_set(static_cast<uint16_t>(slot + 4), cv)) return Fault::BadSlot;
if (!slot_set(static_cast<uint16_t>(slot + 3), q)) return Fault::BadSlot;
return Fault::None;
}
if (op == static_cast<uint8_t>(isa::Op::CAL_CTUD)) {
// cu=0 cd=1 r=2 lu=3 pv=4 qu=5 qd=6 cv=7
if (static_cast<size_t>(slot) * 2 + 1 >= edge_prev_.size()) return Fault::BadSlot;
if (!slot_get(static_cast<uint16_t>(slot + 0), &v)) return Fault::BadSlot;
const bool cu = v != 0;
if (!slot_get(static_cast<uint16_t>(slot + 1), &v)) return Fault::BadSlot;
const bool cd = v != 0;
if (!slot_get(static_cast<uint16_t>(slot + 2), &v)) return Fault::BadSlot;
const bool r = v != 0;
if (!slot_get(static_cast<uint16_t>(slot + 3), &v)) return Fault::BadSlot;
const bool lu = v != 0;
if (!slot_get(static_cast<uint16_t>(slot + 4), &v)) return Fault::BadSlot;
const int64_t pv = v;
int64_t cv = 0;
if (!slot_get(static_cast<uint16_t>(slot + 7), &cv)) return Fault::BadSlot;
const bool prev_cu = edge_prev_[slot * 2] != 0;
const bool prev_cd = edge_prev_[slot * 2 + 1] != 0;
if (r) {
cv = 0;
} else if (cu && !prev) {
cv += 1;
}
ctu_prev_[slot] = cu ? 1 : 0; // 记录本次 cu(跨周期)
if (!slot_set(static_cast<uint16_t>(slot + 4), cv)) return Fault::BadSlot;
if (!slot_set(static_cast<uint16_t>(slot + 3), (cv >= pv) ? 1 : 0)) {
return Fault::BadSlot;
} else if (lu) {
cv = pv;
} else {
if (cu && !prev_cu) cv += 1;
if (cd && !prev_cd) cv -= 1;
}
edge_prev_[slot * 2] = cu ? 1 : 0;
edge_prev_[slot * 2 + 1] = cd ? 1 : 0;
const int64_t qu = (cv >= pv) ? 1 : 0;
const int64_t qd = (cv <= 0) ? 1 : 0;
if (!slot_set(static_cast<uint16_t>(slot + 7), cv)) return Fault::BadSlot;
if (!slot_set(static_cast<uint16_t>(slot + 5), qu)) return Fault::BadSlot;
if (!slot_set(static_cast<uint16_t>(slot + 6), qd)) return Fault::BadSlot;
return Fault::None;
}
if (op == static_cast<uint8_t>(isa::Op::CAL_R_TRIG) ||
op == static_cast<uint8_t>(isa::Op::CAL_F_TRIG)) {
// clk=0 q=1
if (static_cast<size_t>(slot) * 2 >= edge_prev_.size()) return Fault::BadSlot;
if (!slot_get(static_cast<uint16_t>(slot + 0), &v)) return Fault::BadSlot;
const bool clk = v != 0;
const bool prev = edge_prev_[slot * 2] != 0;
const int64_t q = (op == static_cast<uint8_t>(isa::Op::CAL_R_TRIG))
? ((clk && !prev) ? 1 : 0)
: ((!clk && prev) ? 1 : 0);
edge_prev_[slot * 2] = clk ? 1 : 0;
if (!slot_set(static_cast<uint16_t>(slot + 1), q)) return Fault::BadSlot;
return Fault::None;
}