扩充内置功能块为 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:
@@ -80,7 +80,7 @@ compiled: line1.stb (184 bytes, 2 functions, 4 globals)
|
||||
| `project.name` | 是 | 工程名 |
|
||||
| `project.entry` | 是 | 第一版必须 `"program MAIN"` |
|
||||
| `project.cycle_limit` | 是 | 每周期指令上限(>0) |
|
||||
| `project.dt_ms` | 是 | 本周期 Δt 毫秒(>0),给 TON/TOF/CTU |
|
||||
| `project.dt_ms` | 是 | 本周期 Δt 毫秒(>0),给内置 FB 定时器 |
|
||||
| `files.st` | 是 | 源文件列表(非空数组) |
|
||||
| `gvl.file` | 否 | 唯一允许 `VAR_GLOBAL` 的文件;已在 `files.st` 则不重复收录 |
|
||||
| `[[io.input]]` / `[[io.output]]` | 否 | `var` + `channel` + `bit`;`var` 必须已在 GVL 声明 |
|
||||
@@ -110,7 +110,7 @@ bit = 2
|
||||
|
||||
**变量段**:`VAR` / `VAR_INPUT` / `VAR_OUTPUT` / `VAR_GLOBAL`(仅 `gvl.file` 顶层)/ `VAR_EXTERNAL`。
|
||||
|
||||
**类型**:`BOOL` / `INT` / `TIME`;内建 FB 类型 `TON` / `TOF` / `CTU`(关键字,不可作变量名)。
|
||||
**类型**:`BOOL` / `INT` / `TIME`;内建 FB 类型 `TON` / `TOF` / `TP` / `CTU` / `CTD` / `CTUD` / `R_TRIG` / `F_TRIG`(关键字,不可作变量名)。
|
||||
|
||||
**语句**:赋值 `:=`、`IF / ELSIF / ELSE / END_IF`、`WHILE / END_WHILE`、FB 调用 `fb(in := ..., ...);`、字段读 `fb.Q`。
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ compiler 模块的符号收集与链接。输入:`Project`(toml)+ 各 `.st
|
||||
- `FUNCTION` 标无状态;`FUNCTION_BLOCK` 必须先有实例才能调用
|
||||
- `CALL` 目标解析成 `fn_id`(立即数),无函数指针
|
||||
- 校验:同名全局、未声明 `io.var`、重复 POU 名、未声明 FB 实例、函数循环调用 → 失败
|
||||
- FB 实例布局:按类型算字段清单(输入/输出/内部/内建 TON/TOF/CTU 固定字段)
|
||||
- FB 实例布局:按类型算字段清单(输入/输出/内部/内建 8 个 FB 固定字段(12.11 扩充))
|
||||
|
||||
**不做(第一版)**
|
||||
|
||||
@@ -70,15 +70,20 @@ struct Symbol {
|
||||
## FB 实例布局
|
||||
|
||||
- 用户 FB:字段 = 全部 `VAR_INPUT` + `VAR_OUTPUT` + `VAR`(内部),按声明段序排
|
||||
- 内建(冻结,isa 规格):
|
||||
- 内建(冻结,isa 规格;12.11 扩为 8 个):
|
||||
|
||||
| FB | 字段(名 → 类型) |
|
||||
|---|---|
|
||||
| TON | in BOOL, pt TIME, q BOOL, et TIME |
|
||||
| TOF | in BOOL, pt TIME, q BOOL, et TIME |
|
||||
| TP | in BOOL, pt TIME, q BOOL, et TIME |
|
||||
| CTU | cu BOOL, r BOOL, pv INT, q BOOL, cv INT |
|
||||
| CTD | cd BOOL, ld BOOL, pv INT, q BOOL, cv INT |
|
||||
| CTUD | cu BOOL, cd BOOL, r BOOL, lu BOOL, pv INT, qu BOOL, qd BOOL, cv INT |
|
||||
| R_TRIG | clk BOOL, q BOOL |
|
||||
| F_TRIG | clk BOOL, q BOOL |
|
||||
|
||||
- 实例布局 = 类型字段表的一份拷贝;字段偏移 12.8 按对齐规则算
|
||||
- 实例布局 = 类型字段表的一份拷贝;字段偏移 = 字段序号 × 8(8 字节定宽槽)
|
||||
|
||||
## 错误
|
||||
|
||||
|
||||
+2
-1
@@ -26,11 +26,12 @@ BOOL INT TIME
|
||||
IF ELSIF ELSE END_IF WHILE END_WHILE
|
||||
AND OR NOT
|
||||
TRUE FALSE
|
||||
TON TOF CTU
|
||||
TON TOF TP CTU CTD CTUD R_TRIG F_TRIG
|
||||
```
|
||||
|
||||
> 12.5 修订:`THEN`、`DO` 补入关键字表(`IF ... THEN` / `WHILE ... DO` 语法需要),
|
||||
> 与 12.1 原表合并,其余不变。
|
||||
> 12.11 修订:内置 FB 扩为 8 个(TON/TOF/TP/CTU/CTD/CTUD/R_TRIG/F_TRIG)。
|
||||
|
||||
其余单词一律当标识符;`VAR_IN_OUT`、`REF`、`CLASS` 等由语法层(12.5)出明确错误。
|
||||
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ line1 `MAIN` 前 5 条(`0x60` 起,slot 为 8 字节定宽槽号):
|
||||
|
||||
## FB 布局段(`offset_fb` 起)
|
||||
|
||||
v1 为空(用户 FB 内联展开,字段偏移已算进数据段;内建 TON/TOF/CTU 布局冻结在编译器中)。行格式约定:`[field_count:u32][field_count × (tag:u32, offset:u32)]`,offset 相对实例基址。
|
||||
v1 为空(用户 FB 内联展开,字段偏移已算进数据段;内建 8 个 FB 布局冻结在编译器中(12.11 扩充))。行格式约定:`[field_count:u32][field_count × (tag:u32, offset:u32)]`,offset 相对实例基址。
|
||||
|
||||
## 数据段(`offset_data` 起)
|
||||
|
||||
|
||||
+5
-1
@@ -106,11 +106,15 @@ JMP offset
|
||||
JT/JF r, offset
|
||||
LOAD_I / STORE_Q / LOAD_M / STORE_M
|
||||
LOAD_GLOBAL / STORE_GLOBAL
|
||||
CAL_TON / CAL_TOF / CAL_CTU
|
||||
CAL_TON / CAL_TOF / CAL_TP / CAL_CTU / CAL_CTD / CAL_CTUD / CAL_R_TRIG / CAL_F_TRIG
|
||||
CALL fn_id
|
||||
RET
|
||||
```
|
||||
|
||||
> 12.11 修订:内置功能块扩为 8 个(TON/TOF/TP/CTU/CTD/CTUD/R_TRIG/F_TRIG),
|
||||
> `CAL_*` 操作码**连续占 24..31**(原 CALL=27、RET=28 后移为 32、33)。
|
||||
> 操作码数值即编码,本表为准。
|
||||
|
||||
局部 / `VAR` = 固定寄存器;表达式临时值往后编号;`nregs` 写进函数头。
|
||||
`I`/`Q`/`M` 走映像指令,不是通用寄存器。
|
||||
FB 实例固定布局,字段偏移编译期算死。
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ vm 模块:寄存器虚拟机。CMake 目标:`vm`(`STATIC`),只依赖 [
|
||||
1. 按 sidecar 的 io 绑定,采样 → I / 输入全局
|
||||
2. 从 PROGRAM MAIN 的 pc=0 执行到 RET
|
||||
3. 写回 Q / 输出全局
|
||||
4. 用本周期 Δt 推进 TON / TOF / CTU(不用 wall clock)
|
||||
4. 用本周期 Δt 推进内置 FB(不用 wall clock)
|
||||
```
|
||||
|
||||
## 边界
|
||||
@@ -97,7 +97,7 @@ namespace vm {
|
||||
- `CALL fn_id`:压新帧(复制 r0..r7)→ pc=0;`RET`:复制 r0..r7 回调用方 → 弹栈(MAIN 的 RET = 周期结束)
|
||||
- `cycle_limit` 超限(用例 6)、栈深 > 64、`BadOp` / `BadSlot` / `BadConst` → 周期中止返回故障
|
||||
|
||||
### 步骤 4:定时器(CAL_TON / CAL_TOF / CAL_CTU)
|
||||
### 步骤 4:定时器与边沿(8 个 CAL_*)
|
||||
|
||||
- 执行时用映像 `dt_ms` 推进(每周期一次调用 = 推进一次),不读系统时钟
|
||||
- TON:in 真 → et += dt(到 pt 停)、q = et ≥ pt;in 假 → et = 0
|
||||
|
||||
+14
-10
@@ -80,7 +80,7 @@ STORE_* slot, rs → 写 data[slot*8 .. slot*8+8) = rs(8 字节原样)
|
||||
| `JT rd, off` / `JF rd, off` | `reg[rd] != 0`(JT)/ `== 0`(JF)时 `pc += off`,否则正常 +1 |
|
||||
| `LOAD_* rd, slot` | 见数据区访问 |
|
||||
| `STORE_* slot, rs` | 见数据区访问 |
|
||||
| `CAL_TON/TOF/CTU slot` | 见定时器推进 |
|
||||
| `CAL_TON/TOF/TP/CTU/CTD/CTUD/R_TRIG/F_TRIG slot` | 见定时器与边沿推进 |
|
||||
| `CALL fn_id` / `RET` | 见帧与调用栈 |
|
||||
|
||||
### 跳转语义
|
||||
@@ -95,26 +95,30 @@ STORE_* slot, rs → 写 data[slot*8 .. slot*8+8) = rs(8 字节原样)
|
||||
- 每条指令执行后 `cycle_count++`;超过映像头 `cycle_limit` → `CycleLimit` 故障(周期中止)
|
||||
- `JMP` / `JT` / `JF` / `CALL` 只算 1 条(cycle_limit 以"执行的指令数"计)
|
||||
|
||||
## 定时器推进(CAL_TON / CAL_TOF / CAL_CTU)
|
||||
## 定时器与边沿推进(CAL_TON / CAL_TOF / CAL_TP / CAL_CTU / CAL_CTD / CAL_CTUD / CAL_R_TRIG / CAL_F_TRIG)
|
||||
|
||||
`CAL_* slot` 的 `slot` 是实例**基槽号**;字段偏移 = 实例布局字段序号 × 8(内建布局冻结:TON/TOF = in/pt/q/et,CTU = cu/r/pv/q/cv)。每次执行 = 推进一次,Δt = 映像头 `dt_ms`(不读系统时钟):
|
||||
`CAL_* slot` 的 `slot` 是实例**基槽号**;字段偏移 = 字段序号 × 8(布局冻结:TON/TOF/TP = in/pt/q/et;CTU = cu/r/pv/q/cv;CTD = cd/ld/pv/q/cv;CTUD = cu/cd/r/lu/pv/qu/qd/cv;R_TRIG/F_TRIG = clk/q)。每次执行 = 推进一次,Δt = 映像头 `dt_ms`(不读系统时钟)。边沿检测的"上次输入"存 VM 侧 `edge_prev_`(每实例基槽 2 字节),跨周期保留:
|
||||
|
||||
```text
|
||||
CAL_TON:
|
||||
in = 槽[in] 非零;pt = 槽[pt];et = 槽[et];q = 槽[q]
|
||||
in 为真:et += dt(et ≥ pt 时停在 pt);q = (et ≥ pt) ? 1 : 0
|
||||
in 为假:et = 0;q = 0
|
||||
|
||||
CAL_TOF:
|
||||
in 为真:q = 1;et = 0
|
||||
in 掉电:et += dt;et ≥ 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
|
||||
CAL_TP(脉冲):
|
||||
in 上升沿启动 PT 时长脉冲(期间 in 变化不影响)
|
||||
计时中:et += dt;et ≥ pt → q = 0、et 归零(脉冲结束);否则 q = 1
|
||||
非计时:et = 0;q = 0
|
||||
|
||||
CAL_CTU:cu 上升沿 → cv += 1;r → cv = 0;q = (cv ≥ pv) ? 1 : 0
|
||||
CAL_CTD:cd 上升沿 → cv -= 1;ld → cv = pv;q = (cv ≤ 0) ? 1 : 0
|
||||
CAL_CTUD:cu 上升沿 → cv += 1;cd 上升沿 → cv -= 1;r → cv = 0;lu → cv = pv;
|
||||
qu = (cv ≥ pv) ? 1 : 0;qd = (cv ≤ 0) ? 1 : 0
|
||||
CAL_R_TRIG:q = clk 且上次 clk 假(上升沿)
|
||||
CAL_F_TRIG:q = !clk 且上次 clk 真(下降沿)
|
||||
```
|
||||
|
||||
## 故障
|
||||
|
||||
+6
-6
@@ -45,7 +45,7 @@
|
||||
1. 按 toml 的 io 绑定,采样 → I / 输入全局
|
||||
2. 从 PROGRAM MAIN 的 pc=0 执行到 RET
|
||||
3. 写回 Q / 输出全局
|
||||
4. 用本周期 Δt 推进 TON / TOF / CTU(不用 wall clock)
|
||||
4. 用本周期 Δt 推进内置 FB(TON/TOF/TP/CTU/CTD/CTUD/R_TRIG/F_TRIG,不用 wall clock)
|
||||
```
|
||||
|
||||
运行时是 **一台 VM、一个入口、一份映像**。多文件只影响源码怎么切、怎么链接。
|
||||
@@ -219,7 +219,7 @@ JMP offset
|
||||
JT/JF r, offset
|
||||
LOAD_I / STORE_Q / LOAD_M / STORE_M
|
||||
LOAD_GLOBAL / STORE_GLOBAL
|
||||
CAL_TON / CAL_TOF / CAL_CTU
|
||||
CAL_TON / CAL_TOF / CAL_TP / CAL_CTU / CAL_CTD / CAL_CTUD / CAL_R_TRIG / CAL_F_TRIG
|
||||
CALL fn_id
|
||||
RET
|
||||
```
|
||||
@@ -354,7 +354,7 @@ CMake 三个库能空编译:`isa`、`compiler`、`vm`;两个可执行 `STCom
|
||||
| `project.name` | 工程名 | 必填 |
|
||||
| `project.entry` | 入口 | 第一版必须是 `program MAIN` |
|
||||
| `project.cycle_limit` | 每周期指令上限 | 如 `100000`;超时是故障 |
|
||||
| `project.dt_ms` | 本周期 Δt | 给 TON / TOF / CTU,不是 wall clock |
|
||||
| `project.dt_ms` | 本周期 Δt | 给内置 FB 定时器,不是 wall clock |
|
||||
| `files.st` | 源文件列表 | 编译前全部已知 |
|
||||
| `gvl.file` | 唯一允许 `VAR_GLOBAL` 的文件 | 已在 `files.st` 则不重复收录 |
|
||||
| `[[io.input]]` / `[[io.output]]` | `var` + `channel` + `bit` | 可选;`var` 必须已在 GVL |
|
||||
@@ -370,7 +370,7 @@ CMake 三个库能空编译:`isa`、`compiler`、`vm`;两个可执行 `STCom
|
||||
`THEN` `DO` (12.5 修订补入:`IF…THEN` / `WHILE…DO` 语法需要)
|
||||
`AND` `OR` `NOT`
|
||||
`TRUE` `FALSE`
|
||||
`TON` `TOF` `CTU`
|
||||
`TON` `TOF` `TP` `CTU` `CTD` `CTUD` `R_TRIG` `F_TRIG` (12.11 修订:内置 FB 扩为 8 个)
|
||||
|
||||
其余一律当标识符或直接拒绝(`VAR_IN_OUT`、`REF`、`CLASS` 等出明确错误)。
|
||||
|
||||
@@ -449,7 +449,7 @@ CMake 三个库能空编译:`isa`、`compiler`、`vm`;两个可执行 `STCom
|
||||
|
||||
1. POU 外壳:`PROGRAM` / `FUNCTION` / `FUNCTION_BLOCK` 与对应 `END_*`
|
||||
2. 变量段:`VAR` / `VAR_INPUT` / `VAR_OUTPUT` / `VAR_GLOBAL` / `VAR_EXTERNAL`
|
||||
3. 类型名只接受 `BOOL` `INT` `TIME`;内置 FB 名 `TON` `TOF` `CTU`
|
||||
3. 类型名只接受 `BOOL` `INT` `TIME`;内置 FB 名 `TON` `TOF` `TP` `CTU` `CTD` `CTUD` `R_TRIG` `F_TRIG` (12.11 修订:内置 FB 扩为 8 个)
|
||||
4. 语句:赋值、`IF`、`WHILE`、FB 调用 `fb(in := …)`、字段 `fb.Q`
|
||||
5. 表达式:`NOT`、`AND`/`OR`(AST 里保留短路语义)、比较、加减乘除
|
||||
|
||||
@@ -512,7 +512,7 @@ CMake 三个库能空编译:`isa`、`compiler`、`vm`;两个可执行 `STCom
|
||||
此时才写执行循环。只认 `isa` 映像。
|
||||
|
||||
```text
|
||||
采样 I → pc=0 执行到 RET → 写回 Q → 用本周期 Δt 推进 TON/TOF/CTU
|
||||
采样 I → pc=0 执行到 RET → 写回 Q → 用本周期 Δt 推进内置 FB
|
||||
```
|
||||
|
||||
- 译码 + 大 `switch`;寄存器文件按函数头 `nregs` 分配。
|
||||
|
||||
Reference in New Issue
Block a user