④ 三后端生成器(generator/)

生成器把 .ris + .dspec + .bind 变成可编译的 C。三个后端共享 common.py 的 lowering 基础设施,各自实现发射策略:

后端文件目标形态
userspace harnessharness.py(358 行)用户态可运行、带 trace 输出的 MMIO 仿真
bare-metalbaremetal.py(350 行)裸机(mmio_read8/16/32 抽象)
Linux 内核模块linux.py(2,925 行)真实 Kbuild 模块 + 回调注册 + registration AST

4.1 绑定层:.bind 把语义映射到后端

artifacts/output/portable-gpio-pl061/gpio-pl061.bind 三段式设计 —— 一个文件描述三个后端:

backend harness for device gpio-pl061 {
  type DeviceState -> "struct gpio_pl061"
  type MmioBase -> "uintptr_t"
  map dev.base -> "dev->base"
  map MmioRead(B4) -> "harness_read32"     -- MMIO 访问映射到仿真器
  map MmioWrite(B4) -> "harness_write32"
}

backend baremetal for device gpio-pl061 {
  export read_config as "gpio-pl061_read_config"   -- 导出为独立符号
  map MmioRead(B1) -> "mmio_read8"                -- 宽度分派
  map MmioRead(B2) -> "mmio_read16"
  map MmioRead(B4) -> "mmio_read32"
}

backend linux for device gpio-pl061 {
  include <linux/io.h>
  include <linux/platform_device.h>
  type MmioBase -> "void __iomem *"
  type LogicalIRQ -> "struct irq_data *"
  callback gpio_chip.get_direction = pl061_get_direction   -- 回调表槽位 → 函数
  callback irq_chip.irq_set_type = pl061_irq_type
  callback amba_driver.probe = pl061_probe
  map MmioRead(B4) -> "readl"                   -- 内核访问器
  map MmioWrite(B4) -> "writel"
}

同一份 DeviceSpec,三个后端各取所需:harness 只关心 MMIO 映射;baremetal 导出函数符号;linux 填 callback 表并选择内核访问器 —— 语义(做什么)与绑定(怎么做)彻底分离。

4.2 生成契约与 lowering receipt(核心机制)

生成 C 里每个 Read/Write/RMW 都必须携带 op_id + canonical digest

4.3 共享 lowering(common.py,856 行)

4.4 harness 后端(harness.py + 真实产物)

生成的 userspace harness(generated/harness.c 节选):

/* Auto-generated userspace harness for gpio-pl061 (reharness) */
#include <stdint.h>
#include <stdio.h>
#define readl(a) harness_read32((uintptr_t)(a))     -- 内核访问器 → 仿真器
#define readb(a) ((uint8_t)harness_read32((uintptr_t)(a)))
#define writel(v, a) harness_write32((uint32_t)(v), (uintptr_t)(a))
#define mdelay(n) (0)                               -- 内核 API 桩

#define MMIO_SIZE 0x1000
static uint32_t mmio_region[MMIO_SIZE / 4];         -- 4KB 仿真内存
static unsigned long trace_count = 0;

static inline uint32_t harness_read32(uintptr_t a) {
    uint32_t v = mmio_region[(a & 0xfff) / 4];
    printf("[trace %lu] R 0x%03lx = 0x%08x\n", trace_count++, (a & 0xfff), v);
    return v;
}
static inline void harness_write32(uint32_t v, uintptr_t a) {
    printf("[trace %lu] W 0x%03lx = 0x%08x\n", trace_count++, (a & 0xfff), v);
    mmio_region[(a & 0xfff) / 4] = v;
}
#define GPIODIR 0x400                               -- 寄存器偏移常量
#define GPIOIS  0x404

设计要点:trace 即产物 —— harness 的每次 MMIO 访问都打印 [trace N] R/W addr = value,这就是 runtime trace 的来源,直接与 trace_protocol.py 的比对逻辑对接(原驱动 trace vs 候选 trace 逐事件 diff)。

4.5 bare-metal 后端

最小抽象层:mmio_read8/16/32mmio_write8/16/32 由平台实现;export 把每个 FunctionSpec 导出为独立符号(gpio-pl061_probe 等),供裸机测试框架直接调用 —— 没有内核 API、没有回调表。

4.6 Linux 内核后端(linux.py,2,925 行)

最复杂的后端,职责分层:

Linux 端当前边界(README C20):FTGPIO 35/35 通过 leaf AST + registration;DWC2 2023 个 candidate 全过 leaf AST 但仅 62 个落入 v1 支持的 registration route → Linux strict 仍 false。证明尚不覆盖 kernel callback invocation、callback 内路径语义、USB endpoint/gadget/HCD lifecycle —— 全部显式 fail-closed。

4.7 LLM 合成:可选的第二路径

LLM 不是确定性测试/矩阵/QEMU 结果的依赖,但闭环实验里它是候选生成器:

LLM 直出驱动的已知问题记录在 docs/plans/llm-limitations.md;Codex 当工程代理的边界在 docs/retrospectives/*.md —— 这些复盘是论文「能力边界」论述的一手材料。
下一章:生成完怎么证明它对?QA oracle 家族、QEMU 运行时、strict readiness 与实验编排。