NLC Compiler

自然语言编译器系统设计

以自然语言为中间表示的模型驱动编译系统,面向 RP2040 嵌入式平台的完整架构设计。

维度说明
目标平台RP2040 (Cortex-M0+)
文档版本v0.1 — Draft
日期2026-08-03

目录

  1. 系统概览
  2. 设计哲学
  3. NLC 中间表示设计
  4. 编译流水线
  5. 验证与证书体系
  6. RP2040 目标平台
  7. 开发计划
  8. 模型训练与数据策略
  9. 后端小模型设计与实现

01 系统概览

整体架构、核心目标,以及与 TVM 的分层类比。

1.1 什么是 NLC Compiler

NLC Compiler(Natural Language Code Compiler)是一种模型驱动的分层编译系统。它的核心思路是:将传统编译器的"前端解析器 → 计算图 IR → 后端代码生成"三段式结构,重新定义为"大模型前端 → 自由自然语言 IR → 专用小模型后端"的变换流水线。程序员用自然语言提示词描述意图,编译器将其编译为自由自然语言中间表示(NLC IR),人可以在 NLC IR 层面阅读、调试、迭代需求;随后后端小模型直接阅读自然语言 IR,理解意图后编译为机器可执行的二进制代码或汇编代码,烧录到目标芯片运行。

这套系统借鉴了 TVM 的分层编译哲学[1],但将每一层的介质做了替换:

维度TVMNLC Compiler
输入DL 框架模型 (PyTorch / TF)自然语言提示词
前端编译Relay 前端(确定性解析)大模型 (LLM)
中间表示Relay IR(计算图)NLC IR(自由自然语言)
后端编译LLVM / CUDA 后端专用小模型(针对特定 MCU)
调试层面IR 可视化工具自然语言层面调试
正确性保证形式化 pass 变换(可证明)规格提取 + 形式化验证

1.2 核心设计目标

指标
人类需查代码行数0
编译分层(前端/后端)2
变换可验证覆盖率100%
目标平台 RAM264KB

系统围绕以下三个核心目标设计:

  • 认知经济:人类只在自然语言层面工作,关注意图和需求,不接触语法细节、寄存器分配、指令选择等机器细节。
  • 变换可验证:从提示词到机器码的每一跳变换都必须签发可机器校验的规格满足证书,不通过则回退重生成,而非"看起来对"就放行。
  • 目标适配:后端针对特定 MCU/CPU 优化,小模型蒸馏该平台的指令集、寄存器约束、外设特性,生成高效的机器码。

1.3 系统架构总览

flowchart LR
  subgraph HumanPlane["人类平面 · 自然语言"]
    direction LR
    P["提示词"] --> NLC["NLC IR\n自由自然语言"]
    NLC -.->|人阅读/调试| DBG["自然语言调试"]
    DBG -.->|反馈修改| NLC
  end

  subgraph VerifyBridge["验证桥"]
    SPEC["规格提取"]
    CERT["证书签发\n+ 回退"]
    SPEC -->|规格满足验证| CERT
    CERT -.->|失败| NLC
  end

  subgraph MachinePlane["机器平面 · 执行语言"]
    ASM["ASM / 机器码"]
    HW["RP2040 硬件"]
    ASM -->|UF2 烧录| HW
    HW -.->|诊断映射| DBG
  end

  P -->|大模型前端| NLC
  NLC -->|小模型后端| ASM
  NLC -->|规格提取| SPEC
  CERT -->|通过| ASM

图 1:NLC Compiler 系统架构 — 两个平面 + 验证桥

系统分为两个平面和一座验证桥。人类平面包含提示词输入、NLC IR 编辑与自然语言调试,全部以人类可读的自然语言为介质。机器平面包含 ASM/机器码生成与硬件执行,以机器可读的二进制为介质。两平面之间的变换经过验证桥——先由规格提取器从自然语言 IR 中提取形式化规格,再由验证器证明 ASM 满足该规格,只有签发证书的变换才被允许跨越平面。


02 设计哲学

验证与确认的分界、两个平面的受管理分离、抽象泄漏的主动管理。

2.1 验证与确认的分工

软件工程区分两种性质的检查:验证(Verification)回答"代码是否符合规格",确认(Validation)回答"规格是否符合真实意图"。传统编程中程序员两者都做——既查代码细节(验证),又想需求对不对(确认)。NLC Compiler 的核心主张是:将验证全部交给机器,人只做确认。

这意味着人的精力从"读代码找 bug"转移到"读规格验意图"。这不是消除人的工作,而是重定位到更高杠杆的环节。验证可被自动化(规格提取、形式化验证、符号执行、SMT 求解),确认则依赖人对真实世界的理解——后者无法自动化,但值得投入全部注意力。

Design Principle

人不需要确认代码的准确性,只需要确认对话内容的需求和要求是否准确。前提是:机器那一半必须给出形式化证书(可机器校验的规格满足证明),而非"看起来生成了对的代码"。

2.2 两个平面的受管理分离

理想状态下,人类平面和机器平面完全独立——人用自然语言描述意图,机器用自己的语言执行,互不干扰。这带来了显著的认知红利:程序员不再支付"语法税"(分号、类型标注、API 签名、构建配置),而是把全部算力投到意图本身。

但完全干净的分离不存在。抽象泄漏定律决定了机器平面的概念会穿透上来:cache miss 导致的性能问题、2KB RAM 的资源约束、中断响应的时序截止期——这些概念在自然语言中没有对应词汇。如果硬把它们翻译成自然语言,要么不够精确,要么等于把机器概念重新塞回人类平面,抵消抽象红利。

因此系统采用受管理的分离策略:

  • 默认走人类平面:逻辑层、算法层、业务流程——这几类代码抽象泄漏极少,自然语言调试完全够用,红利吃满。
  • 必要时受控逃逸:当泄漏发生(性能问题、资源溢出、时序违规),不让人直接读机器码,而是让机器把底层状态翻译成一份自然语言诊断报告(例如"数据布局导致 cache 命中率仅 40%"),人以对话形式处理。
  • 逃逸口而非漏洞:设计显式的"给我看内存布局""给我看时序"这类命令,把抽象泄漏变成可控动作,而非随机发生。

2.3 调试映射

自然语言层面调试需要 source map——把运行时状态(寄存器、内存、PC)映射回 NLC IR 语句。这是一条从机器平面向人类平面的诊断通道,是系统不可或缺的另一半。没有它,人就被迫裸眼对机器码,分离就塌了。

诊断通道的具体机制:机器码中的每条指令或指令块都带有 NLC IR 语句的溯源标记(source map)。当运行时发生异常或断点触发,调试器读取寄存器/内存快照,通过 source map 定位到对应的 NLC IR 语句,再将底层状态翻译为自然语言因果叙述呈现给人。


03 NLC 中间表示设计

自由自然语言 IR:无固定文法、无确定性解析、小模型直接理解并编译。

3.1 NLC IR 是自由自然语言

NLC IR 是自由自然语言——没有固定文法,没有受限词汇,没有确定性解析器。程序员或前端 LLM 用日常语言描述程序逻辑,小模型直接阅读这段自然语言,理解意图,生成机器码。这是与传统编译器的根本区别:传统编译器将 C/C++/Python 等形式语言翻译为 ASM,NLC Compiler 将自由自然语言翻译为 ASM。

这个设计选择背后的核心主张是:IR 不需要固定的语句映射。同一条逻辑意图可以用多种自然语言表述,小模型负责理解这些表述的共性,而非机械地将固定关键字翻译为固定指令。如果 IR 被设计成受限文法(受控自然语言),它就退化成了一门形式语言,小模型退化为一个传统编译器——那它就没有存在的意义了。小模型的价值恰恰在于它能理解自然语言的歧义,在多种实现中做出选择。

Design Decision

NLC IR 不是"形式语言穿了件自然语言的外衣"。它就是自然语言。没有 EBNF 文法,没有 AST,没有确定性解析器。理解它需要语言理解能力,这正是小模型存在的理由。

3.2 NLC IR 示例

以下是一个 RP2040 上的 LED 呼吸灯程序,以自由自然语言描述。对比之前受限文法的版本,这段描述没有任何固定关键字或语法约束——它就是人说话的方式:

LED 呼吸灯

LED 接在 GP25 引脚上。让它的亮度从完全熄灭开始,
逐渐变亮直到最亮,然后再逐渐变暗回到完全熄灭,
如此循环往复。整个从暗到亮再到暗的过程大约持续 5 秒。

亮度的变化要平滑,肉眼看不出台阶。每个亮度步骤之间
等待一小段时间,让眼睛能感知到渐变效果。

这个呼吸效果要一直持续运行,不需要停止条件。

关键特性

这段 IR 对任何会说中文的人完全可读——不需要任何编程知识。它没有声明语句、没有类型标注、没有固定控制流关键字。小模型需要从中理解出:需要 PWM 或循环调光、亮度范围 0 到最亮、周期约 5 秒、需要延时、无限循环。这种理解能力正是模型区别于传统编译器的地方。

3.3 为什么选择自由自然语言而非受限文法

在最初的设计中,我们考虑过受控自然语言(CNL)——固定语法、受限词汇、可确定性解析。但深入分析后发现,CNL 会破坏整个系统的核心价值主张:

维度受控自然语言 (CNL)自由自然语言
人类认知负担需学习 CNL 关键字和语法规则零 — 用日常语言即可
小模型角色机械翻译器(可被传统编译器替代)理解者 + 策略选择者(不可替代)
实现路径选择IR 已固定映射,无法选路径模型理解意图后自由选择最优实现
调试体验在受限语法层面调试在自然语言层面调试 — 读一句话改一句话
确定性高(可确定性解析)低(需模型理解)
验证方式AST ↔ ASM 等价证明 (SMT)规格提取 → 规格验证(见第 05 章)

关键取舍在于确定性:CNL 牺牲了小模型的价值(理解能力、路径选择)换取了确定性解析能力;自由自然语言保留了小模型的全部价值,代价是丢失确定性——这个代价通过规格提取验证来补偿(详见第 05 章)。系统选择了后者,因为小模型的理解能力正是整个方案区别于传统编译器的核心。

3.4 调试在自然语言上进行

因为 NLC IR 是自由自然语言,调试也完全在自然语言上进行。人不需要看任何形式化结构——读一段话、改一段话即可。当运行时发生异常,诊断通道将底层状态翻译为自然语言因果叙述,与 NLC IR 的自然语言文本一起呈现给人。

例如,如果呼吸灯的亮度变化不流畅,诊断系统可能返回:"亮度从 120 跳到 135,因为 PWM 分辨率只有 8 位(0-255),5 秒周期内 256 级变化在每步 20ms 的延时下产生了可见台阶。建议缩短延时或降低周期。"人读完后修改 NLC IR 中的描述("亮度的变化要非常平滑,每个步骤之间的延时尽量短"),重新编译。整个过程没有代码、没有寄存器、没有 AST。

溯源映射通过语义段落锚定实现:小模型在生成 ASM 时,为每段指令标注其对应的 NLC IR 段落编号(自然语言文本的自然分段)。运行时调试器通过段落编号定位到对应的自然语言文本,而非逐行映射。这是粗粒度的映射——一段自然语言可能对应数十条 ASM 指令——但对于自然语言层面的调试足够了,因为人关注的是意图层面的问题,不是逐指令的问题。


04 编译流水线

前端(LLM)生成自由自然语言 IR,小模型直接理解并编译为 ASM,验证器通过规格提取验证正确性。

4.1 流水线总览

flowchart TD
  P["自然语言提示词"] --> FE["前端:大模型 (LLM)"]
  FE -->|生成| NL["NLC IR\n自由自然语言"]
  NL -->|提取| SE["规格提取器\n(独立模型)"]
  SE --> SPEC["形式化规格\n前置/后置条件 · I/O 契约"]
  NL -->|理解 NL 生成| BE["后端:专用小模型"]
  BE --> ASM["ARM Thumb-2 汇编"]
  ASM --> V{"验证器\nASM 是否满足形式化规格"}
  SPEC --> V
  V -->|满足| CERT["签发证书"]
  V -.->|不满足| BE
  CERT --> BIN["UF2 二进制"]
  BIN --> FLASH["烧录 RP2040"]

图 2:编译流水线 — LLM 生成自由 NL,小模型理解编译,规格提取验证

流水线分三个阶段,没有确定性解析层。前端 LLM 将提示词扩展为完整的自由自然语言 IR 文本;规格提取器从自然语言 IR 中提取形式化规格;后端小模型直接阅读自然语言 IR,理解意图后生成 ASM。验证器检查 ASM 是否满足形式化规格。整个流水线中,自然语言 IR 始终是自由文本,不经过任何确定性解析或文法约束。

实现状态(v0.1)

流水线已落地为 nlc/compiler.py 中的 compile_with_model()ModelCompileResult,并通过 nlc/cli.py 暴露三个子命令:model-compile / extract-spec / expand-ir。前端、规格提取、后端三段分别由 nlc/model/frontend.pynlc/model/spec_extractor.pynlc/model/backend_model.py 实现,LLM 适配统一走 nlc/model/llm_adapter.pymake_adapter 工厂(含 MockLLMAdapterAPILLMAdapter)。

4.2 前端:大模型生成自然语言 IR

前端接收自然语言提示词,由大模型扩展为完整的 NLC IR 文本。与之前设计不同,前端不再需要将输出约束为受限文法——它生成的是自由自然语言,和人类说话的方式一致。前端的任务是理解用户的简短提示词,补充实现细节(引脚编号、时序参数、边界条件),输出一段完整、清晰的自然语言程序描述。

前端的 LLM 需要满足以下要求:

  • 意图理解:从简短提示词中推断完整程序意图,补充用户未明确但必要的细节。
  • 目标平台感知:LLM 需知道目标平台的基本约束(RP2040 有 264KB SRAM、无 FPU、双核 Cortex-M0+),在自然语言描述中体现平台约束(例如不描述硬件浮点运算)。
  • 完整性:输出需包含足够的细节让后端小模型能独立编译——引脚分配、时序要求、边界条件、循环行为都需要明确描述。

因为输出是自由自然语言,不需要文法约束或结构化输出。LLM 做它最擅长的事——理解和生成自然语言——这正是它被训练来做的事,不存在冷启动问题。

4.3 规格提取:自然语言到形式化规格

这是自由自然语言 IR 设计的关键补偿机制。由于 NLC IR 没有确定性解析器,无法直接做形式化验证。系统引入一个独立的规格提取器——一个专门的模型,从自由自然语言 IR 中提取一份形式化规格。

规格提取器输出的是结构化的形式化规格,包含:

  • I/O 契约:输入信号(引脚状态、ADC 值、串口数据)与输出信号(引脚电平、PWM 输出、串口发送)的映射关系。
  • 时序约束:响应延迟上限、周期要求、信号持续时间。
  • 不变量:程序运行期间必须始终成立的条件(如"亮度值始终在 0-255 范围内")。
  • 行为规约:程序在不同输入条件下的预期行为,以前置条件 → 后置条件的形式表达。
// 规格提取示例:LED 呼吸灯

// 输入规格
//   无外部输入(自主运行)

// 输出规格
//   GP25 输出 PWM 信号
//   占空比 duty ∈ [0, 255]
//   duty 随时间单调递增至 255 后单调递减至 0,循环往复

// 时序约束
//   一个完整周期 T_cycle ≈ 5s (±10%)
//   每步延时 dt ∈ [1ms, 50ms]

// 不变量
//   ∀t: 0 ≤ duty(t) ≤ 255
//   duty(t) 单调递增或单调递减(不允许跳变)

// 行为规约
//   初始: duty = 0, direction = increasing
//   当 duty = 255 且 direction = increasing:
//     direction := decreasing
//   当 duty = 0 且 direction = decreasing:
//     direction := increasing
//   每步: duty := duty ± step (step 由 dt 和 T_cycle 决定)

Two-Hop Verification

验证链条分两跳:第一跳是 NL → 形式规格(规格提取器,模型步骤,可能出错);第二跳是规格 → ASM(确定性验证,SMT 求解器)。第一跳的正确性由人确认——人阅读提取出的规格,判断是否符合自然语言 IR 的意图。这种分工把模型的不确定性限制在"规格提取"这一步,后续验证是确定性的。

4.4 后端:小模型直接编译自然语言

后端接收自由自然语言 IR 文本,小模型直接阅读并理解这段文本,生成目标平台的汇编代码。这是整个系统中模型价值最集中的环节——小模型需要同时完成三件事:

  • 语言理解:从自由自然语言中提取程序逻辑(循环结构、条件分支、时序要求、I/O 操作)。
  • 策略选择:在多种合法实现路径中选择最优(忙等待 vs Timer 中断 vs PWM 硬件 vs PIO 状态机)。
  • 代码生成:将选定的实现策略落地为 ARMv6-M Thumb-2 汇编指令序列,完成寄存器分配、指令调度、中断配置。

这三件事是传统编译器做不到的——传统编译器只能做第三件(代码生成),前两件需要人在写 C 代码时完成。小模型的存在意义就是覆盖前两件,这正是"自然语言到 ASM"编译与传统"C 到 ASM"编译的根本区别。

但小模型同样不保证 100% 正确。因此后端采用小模型 + 合法性检查 + 规格验证的三重架构:

flowchart LR
  NL["NLC IR 自由自然语言"] --> SM["小模型\n理解 + 策略选择 + 代码生成"]
  SM -->|候选 ASM| LC["合法性检查\n指令集 · 寄存器 · 寻址模式"]
  LC -->|合法 ASM| SV["规格验证器\nASM 是否满足形式化规格"]
  SV -->|满足| CERT["签发证书"]
  SV -.->|不满足| SM
  LC -.->|非法指令| SM

图 3:后端三重架构 — 小模型理解生成、合法性检查、规格验证签发证书

小模型负责"快和理解"——理解自然语言、选择最优实现路径、生成高效指令序列;合法性检查负责"合法"——检查指令是否属于 ARMv6-M 指令集、寄存器分配是否冲突、寻址模式是否合法;规格验证器负责"对"——用 SMT 求解器或符号执行证明生成的 ASM 满足规格提取器输出的形式化规格。三者任一失败,回退给小模型重新生成,保证系统永不输出未验证的代码。

实现状态(v0.1)

  • 三重架构已实现:BackendModelnlc/model/backend_model.py)封装 RAG 检索 + 策略选择 + 反馈重试 + parse_asm_textnlc/model/strategy.py 给出 4 个策略(busy_wait / timer_irq / pwm_hardware / pio_state_machine)及 select_strategy;合法性/规格层通过 compile_with_model() 内部串联。
  • 提示词构建由 nlc/model/prompt.py 统一负责(build_frontend / build_spec_extractor / build_backend messages + make_feedback),RAG 由 nlc/model/rag.pyRetriever 提供(3 个子库:pico_sdk / datasheet / pio,TF-IDF cosine 相似度)。
  • 关键设计不变量:模型负责"快和理解",确定性工具负责"合法和正确" — 已通过 tests/test_model.py 92 条用例(全量 142 passed)固化。

05 验证与证书体系

规格提取验证:从自由自然语言提取形式化规格,验证 ASM 是否满足规格,签发证书。

5.1 验证架构:从等价证明到规格满足

由于 NLC IR 是自由自然语言,没有 AST,无法做"NL IR ↔ ASM"的直接等价证明。系统改用规格提取验证:先从自然语言 IR 中提取形式化规格,再验证 ASM 是否满足该规格。验证目标从"等价"(ASM 与 IR 做的事完全一样)变为"满足"(ASM 满足 IR 提取出的规格)。这是一个比等价证明弱但完全兼容自由自然语言的验证方式。

flowchart LR
  NL["NLC IR\n自由自然语言"] -->|第一跳:规格提取\n模型步骤(人确认)| SPEC["形式化规格"]
  SPEC -->|第二跳:规格满足验证\n确定性 SMT 求解| ASM["ASM"]

图 4:规格提取验证 — 两跳验证,人确认规格,机器验证 ASM 满足规格

验证链条分两跳:第一跳是 NL → 形式规格(规格提取器,模型步骤,可能出错),这一跳的正确性由人确认——人阅读提取出的形式化规格,判断是否符合自然语言 IR 的意图。第二跳是规格 → ASM(确定性验证,SMT 求解器),验证 ASM 是否满足规格中的 I/O 契约、时序约束、不变量、行为规约。第二跳是确定性的,不依赖模型。

5.2 规格提取器

规格提取器是一个独立的模型,专门训练用于从自然语言程序描述中提取形式化规格。它的输入是自由自然语言 IR 文本,输出是结构化的形式化规格(JSON 或 SMT-LIB 格式)。

规格提取器与后端小模型独立运行——两个模型分别读同一段自然语言 IR,各自输出。这种独立性是刻意的:如果规格提取和代码生成用同一个模型,模型的理解错误会同时污染规格和 ASM,验证器即使通过也毫无意义。两个独立模型各自理解同一段文本,如果其中一个理解错了,验证器会捕获到不一致。

Human-in-the-loop

规格提取是模型步骤,可能出错。因此提取出的规格必须由人确认。人阅读形式化规格(它比自然语言更精确、更短),判断是否符合自己在自然语言 IR 中描述的意图。如果规格不符合意图,说明规格提取器理解错了——人可以修正规格或重写自然语言 IR 使其更清晰。这是人在整个系统中唯一需要做的"确认"工作。

5.3 规格验证方法

规格验证器的任务是证明 ASM 程序满足形式化规格。系统采用符号执行 + SMT 求解的组合方法,针对规格中的每一类约束进行验证:

规格类别验证方法工具
I/O 契约符号执行 ASM,验证输入到输出的映射关系满足契约KLEE / angr 符号执行引擎
时序约束静态分析指令周期数,验证响应延迟在约束范围内指令周期模型 + 约束求解
不变量归纳证明:初始状态满足不变量,且每条指令的执行保持不变量Z3 SMT 求解器
行为规约对每个前置条件 → 后置条件规则,符号执行验证满足前置条件时后置条件成立KLEE + Z3

Scope Limitation

规格满足验证比等价证明弱:它只验证 ASM 满足规格,不验证 ASM 没有规格外的副作用。例如,规格说"GP25 输出 PWM",验证器确认 GP25 确实输出了 PWM,但不检查 ASM 是否意外修改了其他引脚。系统通过要求规格提取器输出"负规格"(明确列出不应发生的行为)来部分缓解。此外,符号执行在高复杂度程序上会遭遇状态爆炸,系统通过限制程序规模、分块验证、超时回退到测试验证来缓解。

5.4 三级验证强度

不同程序的安全等级不同,系统提供三级验证强度,由程序类型自动选择或由人指定:

等级验证方式覆盖率适用场景
Level 1:形式化符号执行 + SMT 求解,验证不变量和行为规约高(但受状态爆炸限制)安全关键、实时控制
Level 2:测试从规格自动生成测试向量,ASM 在模拟器上运行验证中(有覆盖盲区)一般逻辑、I/O 操作
Level 3:往返模型将 ASM 反编译回自然语言,与原始 NLC IR 对比语义一致性低(LLM-as-judge)原型验证、非关键程序

证书中标注验证等级,人可以根据等级判断信任程度。Level 1 适用于控制物理硬件的安全关键程序,Level 2 适用于一般逻辑,Level 3 仅用于快速原型验证。系统默认对涉及 GPIO、PWM、中断的程序使用 Level 1,对纯逻辑程序使用 Level 2。

5.5 证书格式

// 证书格式 (JSON) — 规格满足验证
{
  "version": "0.2",
  "verification_type": "spec-satisfaction",  // 验证类型
  "level": 1,                                // 验证等级
  "spec_extractor": "spec-model-v0.1",       // 规格提取器版本
  "verifier": "z3-4.12 + klee-2.3",          // 验证工具
  "nlc_ir_hash": "sha256: a1b2c3...",        // NLC IR 文本哈希
  "spec_hash": "sha256: e5f6g7...",          // 形式化规格哈希
  "asm_hash": "sha256: d4e5f6...",           // ASM 哈希
  "human_confirmed": true,                   // 人是否确认了规格
  "invariants_verified": 3,                  // 验证通过的不变量数
  "behaviors_verified": 5,                   // 验证通过的行为规约数
  "behaviors_timeout": 0,                    // 超时行为数
  "result": "satisfied",                     // 满足 / 不满足 / 部分满足
  "timestamp": "2026-08-03T14:22:00Z",
  "signature": "ed25519: 9f8e7d..."          // 系统签名
}

5.6 失败回退策略

flowchart TD
  V{"规格验证器"}
  V -->|满足| OK["签发证书 → 生成 UF2"]
  V -->|不满足 (retry < 3)| SM["回退小模型重新生成 ASM\n(附带不满足的规格项)"]
  SM --> V
  V -->|不满足 (retry >= 3)| SPEC_RETRY["规格可能提取错误\n重新提取规格"]
  SPEC_RETRY --> CONFIRM["人重新确认规格"]
  CONFIRM --> V
  V -->|超时/状态爆炸| TEST["回退 Level 2 测试验证"]
  TEST -->|通过| OK
  TEST -->|失败| ERR["报错终止\n人类介入"]

图 5:失败回退策略 — 重新生成、重新提取、降级验证、终止

回退策略分四级:第一级是回退小模型重新生成 ASM(附带不满足的规格项作为反馈);第二级是重新提取规格并请人确认(可能是规格提取器理解错了);第三级是降级到测试验证(符号执行超时时);第四级是报错终止并请求人类介入。系统绝不输出未通过验证的代码——这是"人不需要查代码准确性"这一承诺的底牌。

实现状态(v0.1)

  • 规格提取器已落地为 nlc/model/spec_extractor.pySpecExtractor,独立调用 LLM,与后端 BackendModel 解耦,保证"两个模型各自理解同一段文本"的不变量。
  • 证书 JSON 结构(5.5)与 ModelCompileResult 字段对齐:nlc_ir_hash / spec_hash / asm_hash / human_confirmed 等已纳入结果对象,便于后续签发与签名扩展。
  • 失败回退(5.6)由 BackendModel 内置的反馈重试循环 + make_feedback() 实现:合法性检查或规格验证失败时,将"错误类型 / 错误位置 / 修正建议"三要素追加到 prompt 重新调用模型,重试上限 3 次后回退到规格重新提取。
  • SMT 求解器(Z3)与符号执行(KLEE)的外部集成尚未接入,目前 Retriever + 规格提取 + 模型重试构成"软"验证闭环;硬验证器为后续工作。

06 RP2040 目标平台

芯片规格、平台约束、后端适配策略、PIO 专用生成、烧录流程。

6.1 RP2040 硬件规格

RP2040 是 Raspberry Pi 设计的微控制器芯片,采用 40nm 工艺,7×7mm QFN-56 封装[2][3]。其核心规格如下:

维度规格对编译器的影响
CPU双核 ARM Cortex-M0+ @ 133MHz(最高 200MHz)支持并行代码生成;ARMv6-M Thumb 指令集
SRAM264 kB(6 bank:4×64kB + 2×4kB)小内存约束,后端需做寄存器分配优化
Flash无片上 Flash,外部 QSPI XIP,Pico 板载 2MB代码从 XIP 执行,需考虑缓存策略
Boot ROM16 kB,含 UF2 bootloader 和浮点优化函数可调用 ROM 中的优化浮点函数
PIO2 块 × 4 状态机 = 8 个,独立访问任意 GPIO需要专门的 PIO 代码生成后端
FPU无,软件浮点(ROM 优化 + pico_float 库)后端需将浮点运算转为软件调用或定点运算
中断每核 NVIC,32 IRQ(26 连接),4 级优先级需生成中断向量表和 ISR 注册代码
工具链arm-none-eabi-gcc + CMake + pico-sdk后端输出可对接现有工具链
烧录UF2 拖拽(BOOTSEL)/ SWD / picotool最终产物为 .uf2 文件

6.2 平台约束与后端适配

6.2.1 无 FPU 的浮点处理

Cortex-M0+ 不含硬件浮点单元[4]。后端在遇到 NLC IR 中的浮点运算时,有三种处理策略:

  • 定点转换(优先):如果精度要求允许,将浮点运算转换为定点整数运算,利用 Cortex-M0+ 的硬件整数除法器加速。
  • ROM 函数调用:对于必须使用浮点的场景,生成对 Boot ROM 中优化浮点函数的调用,这些函数由 Raspberry Pi 手写汇编优化,比编译器生成的软件浮点更快更小。
  • pico_float 库链接:如果 ROM 函数不覆盖所需运算,链接 SDK 的 pico_float 库。

6.2.2 内存布局适配

RP2040 的 264KB SRAM 分为 6 个 bank[5]。后端需要感知内存布局以生成高效代码:

  • 条带化 SRAM(0x20000000):默认存放堆栈和常用变量,两核并行访问时由硬件交错避免冲突。
  • 非条带化 SRAM(0x21000000):用于将变量固定到特定 bank,避免双核同时访问同一 bank 的结构冒险。
  • SRAM4/5(4KB each):用于 DMA 缓冲区或栈,避免与主数据竞争 bank。

6.2.3 PIO 代码生成

PIO 是 RP2040 的标志性特性——8 个可编程状态机,可独立执行自定义 I/O 协议,不占用 CPU[2]。自然语言 IR 中可以描述需要 PIO 实现的任务,后端小模型理解后为这些任务生成 PIO 汇编代码而非 ARM 汇编。PIO 有独立的 9 条指令集,代码生成空间完全不同,因此需要小模型在 PIO 指令集上单独蒸馏。

WS2812 LED 灯带驱动

使用 RP2040 的 PIO 状态机来驱动 WS2812 LED 灯带。
分配 PIO0 的状态机 0 来执行这个任务,数据线接在 GP16 引脚上。
PIO 时钟分频设为 2(即系统时钟的一半)。

PIO 状态机的工作流程:不断从 FIFO 中拉取 24 位的颜色数据,
然后逐位输出到 GP16 引脚。每位数据的时序要求很精确——
高电平持续约 0.8 微秒表示数据位 1,高电平持续约 0.4 微秒
表示数据位 0,每位的总周期为 1.25 微秒。

这个 PIO 程序会持续运行,CPU 只需向 FIFO 写入颜色数据即可。

后端小模型从这段自然语言描述中理解出 PIO 的使用意图,翻译为 PIO 汇编指令(pulloutsetjmp 等),而非 ARM Thumb 指令。这要求小模型同时掌握两套指令集——ARMv6-M 和 PIO,在训练数据中区分两种代码模式。

6.3 烧录流程

最终产物为 UF2 文件[6]。编译完成后,系统自动生成 .uf2 文件,用户通过以下方式烧录:

  1. 按住 BOOTSEL 按钮,USB 连接 Pico 到电脑。
  2. Pico 识别为名为 RPI-RP2 的 U 盘。
  3. 将 .uf2 文件拖拽到该盘。
  4. Pico 自动重启并运行新固件。

系统也支持通过 SWD 接口使用 OpenOCD 烧录和调试,便于断点调试和寄存器检查。

6.4 双核代码生成

RP2040 的双核 Cortex-M0+ 通过 SIO(Single-cycle I/O)块进行核间通信[7]。当自然语言 IR 中描述了并行行为时,后端将其映射到双核代码生成:

ADC 数据采集与 USB 上传

系统需要同时做两件事:持续读取 ADC 通道 0 的电压值存入缓冲区,
同时通过 USB 将缓冲区中的数据发送到电脑。

这两件事要并行运行,互不阻塞。ADC 读取和 USB 发送共享同一个
缓冲区,需要注意数据一致性——USB 读取的必须是 ADC 已写入的
完整数据,不能读到写了一半的状态。

利用 RP2040 的双核特性,Core 0 负责读取 ADC,Core 1 负责
USB 发送。两个核心通过 SIO 的邮箱 FIFO 传递缓冲区指针,
通过事件信号(SEV/WFE)同步。

后端小模型从这段自然语言描述中理解出并行意图,生成双核代码:Core 0 的 ADC 读取循环、Core 1 的 USB 发送循环、核间通信代码自动插入、共享变量的互斥保护、中断亲和性分配。这些对人类是底层细节,但对后端小模型是必须掌握的代码模式。


07 开发计划

从最小原型到完整系统的四个阶段。

阶段一:规格提取器 + 规格验证器(3-4 周)

训练或选择一个模型作为规格提取器,从自然语言程序描述中提取形式化规格(I/O 契约、时序约束、不变量、行为规约)。实现规格验证器原型(Z3 SMT 求解器 + KLEE 符号执行引擎),验证给定的 ASM 是否满足形式化规格。产出:一个能从自然语言文本提取规格、并对 ASM 做规格满足验证的工具链。

验收标准:给定一段自然语言程序描述,规格提取器输出结构化规格;给定一段 ARM Thumb-2 汇编和对应的规格,验证器能判定 ASM 是否满足规格中的不变量和行为规约。

阶段二:确定性后端 + 手写 NL IR(3-4 周)

实现一个纯确定性的 RP2040 后端(不用小模型),由人手工将自然语言 IR 的意图翻译为 ARM Thumb-2 汇编。覆盖基本外设:GPIO、UART、PWM、Timer、ADC。接入阶段一的规格验证器,验证生成的 ASM 是否满足从自然语言 IR 提取的规格。产出:从手写自然语言 IR 到可烧录 UF2 并签发证书的完整流水线(确定性后端版本)。

验收标准:编译出的 UF2 能在真实 Pico 上运行;规格验证器能对至少 80% 的代码块签发规格满足证书;未验证的块通过测试向量兜底。

阶段三:LLM 前端接入(2-3 周)

选择一个 LLM(本地部署或 API),编写 system prompt 使其将用户简短提示词扩展为完整的自由自然语言 IR 文本。因为输出是自由自然语言,不需要文法约束或结构化输出。实现前端→规格提取→后端的完整流水线。加入失败回退机制(规格验证失败时带不满足项反馈重新生成)。产出:从自然语言提示词到 UF2 的端到端流水线。

验收标准:给定自然语言提示词(如"让 GP25 的 LED 每 500ms 闪烁一次"),系统自动生成自然语言 IR、提取规格、编译为 UF2、签发证书、烧录到 Pico 运行成功。

阶段四:小模型后端 + PIO 生成(4-6 周)

在 RP2040 的 pico-sdk 代码库和 PIO 示例上微调一个小模型(如 CodeLlama-7B 或更小的专用模型),使其直接阅读自由自然语言 IR,理解意图后生成 RP2040 专用的 ASM 和 PIO 代码。实现小模型→合法性检查→规格验证的三重后端架构。加入 PIO 代码生成支持。产出:小模型驱动的后端,生成效率优于纯确定性后端的代码。

验收标准:小模型后端生成的代码在代码大小和执行效率上优于纯确定性后端 15% 以上;所有输出仍通过规格满足验证;PIO 代码能在真实硬件上运行自定义协议(如 WS2812)。

7.1 最小可验证原型(阶段一+二 MVP)

建议首先完成阶段一和阶段二,构成最小可验证原型。这个原型不用 LLM 也不用小模型——人手写自然语言 IR 文本,确定性后端手工编译为 UF2,规格提取器提取规格,规格验证器签发证书。这个原型验证了整个系统最核心的主张:自由自然语言 IR 可以被编译为 RP2040 机器码,且编译过程可通过规格满足验证。如果这一步成立,后续接入 LLM 和小模型只是"快"和"高效"的增量改进,不影响正确性根基。


08 模型训练与数据策略

前端 LLM、规格提取器、后端小模型三类模型的训练数据构建路径。

8.1 问题本质:自然语言理解的门槛

现代代码大模型(CodeLlama、DeepSeek-Coder、StarCoder 等)擅长生成 C++、Python、Java,根本原因是训练数据引力——GitHub 上有数百亿行形式语言代码,模型在这些数据上训练,自然擅长生成这些语言。NLC IR 作为自由自然语言,情况截然不同:模型需要从自然语言文本中理解程序意图并生成机器码,这比"形式语言到形式语言"的翻译难度更高——它要求语义理解能力而非语法翻译能力。

关键洞察在于:大模型生成形式语言好,不是因为形式语言"更高级",而是因为它同时满足两个条件——文法规律性高(确定性语法、明确语义)和训练数据量大。自由自然语言放弃了文法规律性,换来的是零认知负担和模型理解能力的充分发挥。但代价是:小模型必须具备足够的语言理解能力才能从自由文本中提取程序语义。因此核心任务是训练小模型理解自然语言程序描述并生成高效机器码

flowchart LR
  subgraph S1["编译任务难度 = 理解难度 × 生成难度"]
    direction LR
    A["形式语言 → 形式语言\n(C → ASM)\n低理解 · 低生成"] -->|传统编译器| R1["✓ 确定性可解"]
    B["自然语言 → 形式语言\n(NL IR → ASM)\n高理解 · 低生成"] -->|需模型理解| R2["? 核心挑战"]
    C["自然语言 → 自然语言\n(NL → NL IR)\n高理解 · 高生成"] -->|LLM 擅长| R3["✓ 大模型可解"]
    B -->|能力缺口| SOL["两条解决路径"]
  end

图 6:编译任务难度谱系 — 自然语言到 ASM 的理解是核心挑战

8.2 前端 LLM:自然语言扩展自然语言

前端 LLM 的任务是将用户的简短提示词扩展为完整的自由自然语言 IR 文本。这是"自然语言到自然语言"的生成任务——正是 LLM 被训练来做的事,不存在冷启动问题。前端不需要文法约束或结构化输出,只需将用户的简短意图扩展为包含足够细节(引脚编号、时序参数、边界条件)的完整程序描述。

前端 LLM 的训练分两条路径,可分阶段递进:

8.2.1 路径一:Prompt 工程(原型验证,1-2 周)

最快速的方式:通过 system prompt + few-shot 示例引导 LLM 将简短提示词扩展为完整的自然语言 IR。因为输出是自由自然语言,不需要文法约束,强模型(GPT-4 / Claude 级别)在 few-shot 示例下即可生成高质量的自然语言程序描述。

# System Prompt 示例(精简版)

你是一个嵌入式程序设计助手。将用户的简短描述扩展为完整的
自然语言程序规格,供后续编译为 RP2040 机器码。

## 要求
- 用日常中文描述,不使用任何编程语法
- 包含:引脚分配、时序要求、边界条件、循环行为
- 描述要足够详细,让另一个工程师能完全理解程序行为
- 考虑 RP2040 的约束:264KB SRAM、无 FPU、双核 Cortex-M0+

## 示例
用户:让 GP25 的 LED 每 500 毫秒闪烁一次
扩展:
  LED 闪烁灯。LED 接在 GP25 引脚上,配置为输出模式。
  LED 以 500 毫秒的间隔交替亮起和熄灭——亮 500 毫秒,
  灭 500 毫秒,如此循环往复。亮灭切换要干脆,不需要渐变。
  这个闪烁效果要一直持续运行,不需要停止条件。

## 约束
- 只输出自然语言描述,不输出任何代码
- 所有时序参数必须明确数值
- 所有引脚必须明确编号

Limitation

Prompt 工程的输出质量受 LLM 能力影响,复杂程序(嵌套控制流、双核并行、PIO 声明)的描述完整度会下降。此路径仅适合原型验证和小规模程序,生产环境需要微调。

8.2.2 路径二:合成数据微调(生产级,3-6 周)

这是生产级解决方案。虽然 LLM 天生擅长生成自然语言,但它需要学会在嵌入式上下文中补充正确的细节(引脚编号、时序参数、平台约束)。核心思路是构建一个"自然语言 IR 语料工厂",生产大量高质量的 (简短提示词, 完整自然语言 IR) 训练对。

数据生成流水线分三个来源:

数据来源方法样本量级质量
嵌入式 C 代码逆向描述从 pico-sdk 示例、Arduino 示例中提取程序逻辑,用 LLM 逆向生成自然语言描述,再人工审核5K-20K高(真实程序结构)
LLM 辅助生成用 GPT-4 生成 (简短提示词, 完整自然语言 IR) 对,人工审核过滤不合理的样本10K-30K中(需过滤)
人工编写由嵌入式工程师手写高质量 (提示词, 自然语言 IR) 对,覆盖典型场景1K-5K极高(金标准)

三种来源互补:C 代码逆向保证真实程序结构多样性,LLM 辅助生成保证描述多样性,人工编写提供金标准质量锚点。微调策略采用 LoRA,在通用 LLM 基座上适配嵌入式程序描述风格。经验估计:5K-20K 高质量样本即可使输出质量达到生产级。

8.3 规格提取器:自然语言到形式规格

规格提取器是自由自然语言 IR 设计中新增的关键模型。它的任务是从自由自然语言 IR 中提取形式化规格——I/O 契约、时序约束、不变量、行为规约。这是"自然语言到形式语言"的提取任务,需要模型同时理解自然语言语义和形式化规格语言。

8.3.1 训练数据构建

规格提取器的训练数据是 (自然语言 IR, 形式化规格) 对。数据来源:

  • 嵌入式 C 代码 + 形式化规格:从 pico-sdk 示例中提取 C 代码,用 LLM 生成对应的自然语言描述和形式化规格,形成三元组 (自然语言, C 代码, 形式化规格),取 (自然语言, 形式化规格) 作为训练对。
  • LLM 辅助生成:用 GPT-4 从自然语言程序描述中提取形式化规格,人工审核过滤不准确样本。
  • 人工编写:由形式化方法工程师手写高质量 (自然语言, 规格对),覆盖典型嵌入式场景。

8.3.2 独立性保证

规格提取器与后端小模型必须独立训练、独立部署。两个模型分别读同一段自然语言 IR,各自输出。如果用同一个模型同时提取规格和生成代码,模型的理解错误会同时污染规格和 ASM,验证器即使通过也毫无意义。两个独立模型各自理解同一段文本,如果其中一个理解错了,验证器会捕获到不一致。训练数据的重叠也应最小化——两个模型看的是同一段自然语言,但训练目标完全不同(一个输出规格,一个输出 ASM)。

8.4 后端小模型:自然语言到 ASM

后端小模型的任务是直接阅读自由自然语言 IR,理解意图后生成 ARM Thumb-2 汇编。这是"自然语言到形式语言"的翻译任务——比传统编译器的"形式语言到形式语言"难度更高,因为需要语义理解能力。但比"自然语言到自然语言"简单,因为 ASM 的生成空间更受限。

8.4.1 预训练基础:C → ARM 汇编对应数据

arm-none-eabi-gcc 编译 C 代码时会生成对应的 ARM 汇编。可以收集大量嵌入式 C 代码,用 GCC 编译为汇编,形成 (C 代码, ARM 汇编) 训练对。这是现成的、海量的数据源——GitHub 上有大量嵌入式项目,pico-sdk 本身就是一个高质量语料库。小模型先在这些数据上预训练,学习"高级语言 → ARM 汇编"的基本翻译能力。

8.4.2 微调适配:自然语言 → ARM 汇编

预训练后,用合成数据微调适配自然语言输入。数据来源:

  • 自然语言 → C → ASM 三元组:从 pico-sdk 示例中提取 C 代码,用 LLM 逆向生成自然语言描述,再用 GCC 编译为 ASM,形成 (自然语言, ASM) 训练对。这利用了现有 C 代码库的海量资源。
  • 平台特性注入:在训练数据中混入 RP2040 特有的代码模式——SIO 核间通信、PIO 指令序列、DMA 通道配置、中断向量表设置——使小模型学会平台特异代码生成。
  • 实现策略标注:对同一自然语言 IR,提供多种实现路径的 ASM(忙等待 vs Timer 中断 vs PWM 硬件 vs PIO),标注每种路径的 CPU 占用、代码大小、功耗特征,训练小模型做策略选择。

8.4.3 PIO 代码生成的独立训练

PIO 有独立的 9 条指令集,与 ARM 指令集完全不同。后端小模型需要同时掌握两套指令集。训练策略:在预训练阶段将 PIO 汇编代码作为额外语种加入(类似多语言代码模型处理多语言),在微调阶段用自然语言 PIO 描述 → PIO 汇编的合成对适配。Raspberry Pi 官方提供了大量 PIO 示例代码,可作为 PIO 语料基础。

8.5 模型选型建议

角色推荐基座参数量部署方式选型理由
前端 LLM(原型期)GPT-4 / Claude API云端 API强推理能力,prompt 工程即可验证可行性
前端 LLM(生产期)CodeLlama-7B / Qwen-Code7B本地 GPU可 LoRA 微调,本地部署保护代码隐私
规格提取器CodeLlama-7B / Qwen-Code7B本地需理解自然语言并输出形式化规格,独立于后端训练
后端小模型CodeLlama-7B 或更小专用模型1B-7B本地自然语言理解 + ASM 生成,需平台特异数据微调
验证器不需要模型本地Z3 SMT 求解器 + 符号执行引擎,确定性

8.6 数据质量保障

合成数据的最大风险是"看起来合理但语义错误"——模型学会了生成自然语言或 ASM 的表面模式,但生成的逻辑不对。系统通过三道关卡保障数据质量:

  1. 规格验证过滤:对后端训练数据中的 (自然语言 IR, ASM) 对,先用规格提取器提取规格,再用规格验证器(SMT + 符号执行)验证 ASM 是否满足规格。只有通过验证的对才进入训练集。这保证了"教师"不会教错。
  2. 人工审核:对规格提取器的训练数据,由形式化方法工程师人工审核提取出的规格是否准确反映自然语言 IR 的意图。人工审核虽慢,但为训练集提供高质量锚点。
  3. 多样性度量:监控训练集的程序结构覆盖率(循环、条件、I/O 操作、并行、PIO)、平台特性覆盖率(GPIO、PWM、UART、ADC、中断)、自然语言表述风格多样性,确保模型见过足够的变体,避免在未覆盖的场景上退化。

Design Invariant

无论前端、规格提取器还是后端,模型输出的每一段代码都必须经过规格验证器签发证书才能进入最终产物。训练数据质量影响的是"一次通过率"(效率指标),规格验证器保障的是"正确性"(安全指标)。即使模型训练不足导致一次通过率只有 50%,系统仍然安全——只是需要更多次回退重试。这是"模型负责快,确定性工具负责对"分工的最终体现。

8.7 路径递进策略

前端 LLM 的两条路径不是互斥的,而是按时间递进的演进路线

flowchart LR
  P1["路径一:Prompt 工程\n1-2 周"] -->|验证可行性| P2["路径二:合成数据微调\n3-6 周"]
  P2 -->|生产级| GOAL["端到端:提示词 → 自然语言 IR → 规格提取 → ASM → UF2\n规格满足验证 100%"]

图 7:前端 LLM 两路径递进 — 从原型验证到生产级

路径一用最低成本验证"自然语言 IR 可被 LLM 生成"这个核心假设。路径二用合成数据微调达到生产级质量。两条路径的每一步都不影响系统正确性根基——因为规格验证器始终在模型输出之后把守,模型质量的提升只影响效率(一次通过率),不影响安全(证书签发)。规格提取器和后端小模型可并行训练,它们的训练进度同样只影响效率,不影响安全。

实现状态(v0.1)

  • 三类模型的角色已在代码层落位:FrontendLLMnlc/model/frontend.py)、SpecExtractornlc/model/spec_extractor.py)、BackendModelnlc/model/backend_model.py)共用 nlc/model/llm_adapter.py 的 LLM 适配层(MockLLMAdapter 用于测试、APILLMAdapter 用于真实推理、make_adapter 工厂统一构造)。
  • 训练数据策略已落地为 nlc/model/training.py:定义 PretrainPair / FinetunePair / DPOPreference 三类样本,并通过 filter_verified 质量门实现"规格验证过滤"——未通过验证的对不进入训练集。
  • 训练三阶段脚本 scripts/train_backend_model.py 已具备完整骨架:Stage1 预训练(C→ASM)、Stage2 微调(NL→ASM, spec-verified)、Stage3 DPO(策略选择);数据源为 E:\github\pico-examples + F:\Autoresearch 训练框架参考;LoRA 微调 Qwen-Code-7B;大文件工作区 F:\NLC(models/data/checkpoints);脚本内嵌详细 loss 诊断日志(7 个关键节点 + spike/NaN/grad explosion 预警)。

09 后端小模型设计与实现

模型架构选型、训练数据构建、策略选择机制、推理优化与部署方案。

后端小模型是 NLC Compiler 中模型价值最集中的环节。它直接阅读自由自然语言 IR,同时完成语言理解、实现策略选择、代码生成三件事——这三件事是传统编译器做不到的。本章详细展开后端小模型的设计与实现。

9.1 模型架构选型

9.1.1 基座模型

基座模型选 CodeLlama-7B 或 Qwen-Code-7B。7B 是理解能力与部署成本的平衡点:1-3B 参数量难以从自由自然语言中准确提取程序语义,13B+ 部署成本高且嵌入式代码生成空间相对受限。Qwen-Code 在中文理解上更有优势,适合以中文为主的 NLC IR;CodeLlama 在英文和汇编代码上覆盖更广。两者均可,选型取决于 NLC IR 的主语言。

9.1.2 微调方式

微调用 LoRA(Low-Rank Adaptation)而非全量微调。基座模型已具备代码生成和语言理解能力,微调只需教会它"自然语言 → RP2040 ASM"的新映射。LoRA 的低秩适配矩阵(训练参数量 0.1-1%)足以完成这个任务,且训练完成后可与基座权重合并,推理时无额外开销。

9.1.3 Tokenizer 适配

Tokenizer 需同时处理中文自然语言和 ARM 汇编。CodeLlama 对中文覆盖不足——中文文本被切分为大量单字 token,导致序列过长、理解能力下降。解决方案是扩展词表(增加 5K-10K 中文 token),对新增 token 做 embedding 初始化(用现有中文 token 的平均向量初始化)。Qwen-Code 中英双语覆盖更均衡,是更省事的选择,可跳过这一步。

9.2 推理流水线

flowchart TD
  NL["NLC IR 自然语言"] --> RAG["上下文检索 (RAG)\nRP2040 datasheet · pico-sdk 示例"]
  RAG --> SM["专用小模型 (1B-7B)"]
  SM -->|语言理解 + 策略选择 + 代码生成| ASM["候选 ASM"]
  ASM --> LC{"合法性检查\n指令集 · 寄存器 · 寻址模式"}
  LC -->|合法| SV{"规格验证\nSMT 求解 · 符号执行"}
  LC -.->|非法指令| SM
  SV -->|满足| CERT["签发证书 → UF2"]
  SV -.->|不满足| SM

图 8:后端推理流水线 — RAG 增强、模型生成、合法性检查、规格验证

推理流水线分六步。模型在单次前向传播中完成理解、选策略、生成代码三件事,之后由确定性工具(合法性检查 + 规格验证)把关。整个流水线是"模型负责快和理解,确定性工具负责合法和对"的分工。

9.3 RAG 上下文增强

7B 模型的参数量不足以记住 RP2040 datasheet 的所有细节——264KB SRAM 的 bank 布局、PIO 的 9 条指令、SIO 寄存器地址、PWM slice 的通道映射。RAG(Retrieval-Augmented Generation)在推理时动态注入这些信息,让模型"查阅手册"而非"背诵手册"。

向量数据库分三个子库:

  • pico-sdk 示例库:examples 目录中所有 C 示例,每段代码配自然语言描述。NLC IR 描述"ADC 读取"时,检索到 adc_read_battery.c 的代码模式。
  • Datasheet 段落库:按章节切分的 datasheet 段落。NLC IR 描述"PWM 输出"时,检索到 PWM slice 的寄存器配置说明。
  • PIO 示例库:Raspberry Pi 官方的 PIO 示例(WS2812、SPI、VGA),每个配自然语言描述。

检索用 sentence-transformer 编码 NLC IR 文本,余弦相似度取 Top-3,拼接到模型 prompt 中。检索不应过度——Top-3 足以覆盖当前任务的关键信息,过多上下文反而稀释模型注意力。

// RAG 增强的推理 Prompt 结构

[系统指令]
你是一个 RP2040 专用代码生成模型。阅读自然语言程序描述,
理解意图后生成 ARM Thumb-2 汇编代码。

[检索到的上下文 (Top-3)]
--- pico-sdk 示例 ---
// pwm_slice_default: PWM slice 默认配置
pwm_set_wrap(slice_num, 65535);
pwm_set_chan_level(slice_num, chan, 0);
pwm_set_enabled(slice_num, true);

--- Datasheet 段落 ---
PWM slice 0: GP0(GP16) / GP1(GP17) 双通道
寄存器: PWM_CSR, PWM_DIV, PWM_CTR, PWM_CC, PWM_TOP
分辨率: 16 位 (0-65535)

--- PIO 示例 ---
// WS2812 PIO: 800KHz 数据传输
// 每个 bit: 0.8us 高 + 0.45us 低 (bit=1)
//            0.4us 高 + 0.85us 低 (bit=0)

[自然语言 IR]
LED 呼吸灯。LED 接在 GP25 引脚上。让它的亮度从完全熄灭开始,
逐渐变亮直到最亮,然后再逐渐变暗回到完全熄灭,如此循环往复。
整个从暗到亮再到暗的过程大约持续 5 秒。

[输出]
ARM Thumb-2 汇编代码

9.4 训练三阶段

flowchart LR
  P1["预训练\nC → ARM 汇编\nGitHub 嵌入式项目"] --> P2["微调适配\n自然语言 → ASM\n合成训练对"]
  P2 --> P3["平台特化\nRP2040 代码模式\nPIO · 策略选择"]

图 9:后端小模型训练三阶段 — 预训练 → 微调适配 → 平台特化

9.4.1 阶段一:预训练(C → ARM 汇编)

数据是现成的:收集 GitHub 上的嵌入式 C 代码,用 arm-none-eabi-gcc -S 编译为汇编,形成 (C, ASM) 对。pico-sdk 的 examples 目录本身就是一个高质量语料库,覆盖 GPIO、UART、PWM、I2C、SPI、ADC 等常见外设。这一阶段不需要自然语言,只教模型"C → ASM"的基本翻译能力。

样本量级 10K-50K。如果基座模型已有 CodeLlama 级别的预训练,这一阶段可大幅缩短——CodeLlama 已在 GitHub 代码上预训练,汇编生成能力已具备,只需少量嵌入式专用语料做域适配。

9.4.2 阶段二:微调适配(自然语言 → ASM)

这是核心难点。数据来源是"自然语言 → C → ASM"三元组:从 pico-sdk 示例中提取 C 代码,用 GPT-4 逆向生成自然语言描述(人工审核),再用 GCC 编译为 ASM,取 (自然语言, ASM) 作为训练对。

关键质量控制:每对数据先用规格提取器提取规格,再用规格验证器验证 ASM 是否满足规格。只有通过验证的对才进入训练集——这保证了"教师"不会教错。未通过验证的对说明逆向生成的自然语言描述与实际 C 代码语义不一致,丢弃或修正后重新验证。

样本量级 5K-20K。微调策略用 LoRA,rank=8-16,学习率 1e-4 到 5e-4,训练 3-5 个 epoch。

9.4.3 阶段三:平台特化(RP2040 专有模式)

注入 RP2040 独有的代码模式。对同一自然语言 IR,提供多种实现路径的 ASM,每种标注特征向量(CPU 占用、代码大小、功耗),训练模型根据自然语言中的约束线索选择最优实现。

实现路径CPU 占用代码大小功耗适用场景
忙等待循环100%最小原型验证
Timer 中断10-20%需 CPU 做其他任务
PWM 硬件0%标准模拟输出
PIO 状态机0%非标准时序协议

样本量级 1K-5K。这一阶段的数据需要人工编写或精心标注,因为策略选择的正确性依赖对 RP2040 硬件特性的深入理解。

9.5 策略选择机制

策略选择是后端小模型区别于传统编译器的核心能力。传统编译器只能机械翻译——C 代码写了 while(1) { ... } 就生成忙等待循环,无法自主选择更高效的 Timer 中断实现。小模型能从自然语言中读出约束线索,在多种合法实现中选择最优。

以"LED 呼吸灯"为例,模型可能选择四种路径。如果 IR 提到"亮度变化要平滑"且"CPU 需要做其他任务",模型应选 PWM 硬件;如果 IR 只说"快速验证效果",模型可选最简单的忙等待。这种选择能力来自阶段三的训练——模型学会了将自然语言中的约束线索映射到实现策略。

训练方法用 DPO(Direct Preference Optimization):对同一段自然语言 IR 生成多种实现,根据 IR 中的约束线索标注"优选"和"劣选",训练模型在给定约束下偏好正确策略。DPO 比 RLHF 更简单——不需要训练 reward model,直接在偏好数据上优化。偏好数据格式:

// DPO 偏好训练数据示例
{
  "nlc_ir": "LED 呼吸灯。亮度变化要非常平滑。CPU 还需要处理串口数据。",
  "chosen": "// PWM 硬件实现\n@ PWM slice 0, channel A, GP25\n...",
  "rejected": "// 忙等待循环\nloop:\n  ldr r0, [brightness]\n  str r0, [gpio25_set]\n  bl delay\n  b loop"
}
// 约束线索 "CPU 还需要处理串口数据" → 优选 PWM 硬件(CPU 零占用)
//                              → 劣选 忙等待循环(CPU 100% 占用)

9.6 部署与推理优化

9.6.1 模型量化

用 GPTQ 或 AWQ 将模型从 FP16 量化到 INT8。显存占用减半(7B 模型从 14GB 降至 7GB),推理速度提升 30-50%,对代码生成质量的影响可忽略——INT8 量化在代码生成任务上的 perplexity 增加通常小于 2%。如果部署在边缘设备上,可进一步量化到 INT4(AWQ),显存降至 4GB,质量损失略大但可接受。

9.6.2 推理引擎

用 vLLM 或 TensorRT-LLM 部署,支持 PagedAttention 和 KV-cache。编译场景下 batch size 通常为 1(一次编译一个程序),但 KV-cache 仍能显著加速重复前缀的推理——ASM 代码有大量重复的指令模式(如 push {r4, r5, lr}),KV-cache 避免对这些重复前缀重复计算。

9.6.3 Speculative Decoding

可选加速方案。用 1B 参数的 draft model 快速生成候选 token,7B 大模型验证。ASM 是高度结构化的输出——指令助记符、寄存器名、立即数都有强模式——draft model 接受率可达 60-70%,端到端加速 1.5-2x。代价是部署两个模型,增加系统复杂度,适合对推理延迟敏感的场景。

9.6.4 模式缓存

对相同的 NLC IR 片段(如"等待 500 毫秒"这种常见模式),缓存模型输出避免重复推理。嵌入式程序的重复模式较多——延时、GPIO 设置、UART 初始化等——缓存命中率可达 20-30%。缓存 key 用 NLC IR 片段的语义哈希(而非精确字符串匹配),允许表述差异。

9.7 反馈重试机制

合法性检查或规格验证失败时,将失败信息追加到 prompt 中重新调用模型。重试上限 3 次,超过后回退到规格重新提取。这个反馈循环让模型从错误中修正,实测 3 次重试内通过率可达 90%+。

// 反馈重试的 Prompt 演进

// 第一次调用(原始 prompt)
[自然语言 IR]
LED 呼吸灯。GP25,周期 5 秒。

[输出]
// ASM 候选 1
@ 使用 PWM slice 0
movs r0, #0
str r0, [r1, #PWM_CC_OFFSET]  @ 错误: r1 未初始化

// 第二次调用(追加失败反馈)
[自然语言 IR]
LED 呼吸灯。GP25,周期 5 秒。

[上次生成的代码错误]
- 错误: r1 未初始化就使用
- 原因: PWM 寄存器基地址 0x40050000 未加载到 r1

[输出]
// ASM 候选 2
@ 使用 PWM slice 0
ldr r1, =0x40050000  @ PWM 基地址
movs r0, #0
str r0, [r1, #PWM_CC_OFFSET]  @ 正确: r1 已初始化

反馈信息应包含三类内容:错误类型(非法指令 / 寄存器冲突 / 规格不满足)、错误位置(哪条指令 / 哪个规格项)、修正建议(需要加载什么地址 / 需要满足什么约束)。建议越具体,模型修正成功率越高。

9.8 完整部署架构

flowchart TD
  subgraph Cloud["云端(开发期)"]
    UI["Web/CLI 界面"] -->|提示词| LLM_API["LLM API\n生成 NLC IR"]
  end

  subgraph Local["本地(编译期)"]
    RAG_DB["RAG 向量数据库\npico-sdk · datasheet · PIO"]
    CACHE["模式缓存\nRedis / SQLite"]
    LLM_API -->|NLC IR| SM_Q["后端小模型 (INT8)\nvLLM 推理引擎"]
    RAG_DB -->|Top-3 上下文| SM_Q
    CACHE -->|命中| SM_Q
    SM_Q -->|候选 ASM| LC["合法性检查器"]
    LC -->|合法 ASM| VERIFY["规格验证器\nZ3 + KLEE"]
    LC -.->|非法| SM_Q
    VERIFY -->|满足| UF2["UF2 生成器"]
    VERIFY -.->|不满足| SM_Q
  end

  subgraph Device["设备端"]
    UF2 -->|烧录| PICO["RP2040 Pico\nUF2 烧录运行"]
  end

图 10:完整部署架构 — 云端生成 IR,本地编译验证,设备端运行

云端只跑 LLM 前端——它生成 NLC IR 后就完成了使命,不接触机器码。本地运行后端小模型、RAG 数据库、验证器——代码隐私不出本地。设备端只接收已通过验证的 UF2 二进制。这种分层部署让代码逻辑留在本地,同时利用云端 LLM 的强推理能力。

Design Summary

后端小模型的设计核心是"理解 + 选择 + 生成"三位一体。RAG 补充硬件知识,三阶段训练建立从自然语言到 ASM 的映射能力,DPO 训练策略选择,反馈重试保证正确性。最终,模型负责"快和理解",确定性工具(合法性检查 + 规格验证)负责"合法和对",两者分工构成完整的后端架构。

实现状态(v0.1)

  • 9.1 模型架构:基座选型已确定为 Qwen-Code-7B + LoRA 微调(scripts/train_backend_model.py);Mock 适配器(MockLLMAdapter)让 9.2–9.7 的流水线在不依赖 GPU 的前提下完整可跑。
  • 9.2 推理流水线BackendModel.generate() 已实现"RAG 检索 → 拼接 prompt → 调用 LLM → parse_asm_text 提取 ASM"全链路;图 8 对应代码路径完整。
  • 9.3 RAGnlc/model/rag.pyRetriever 实现三个子库(pico_sdk / datasheet / pio),采用 TF-IDF + 余弦相似度(轻量、零依赖、可在 CPU 上跑),Top-K 检索结果按子库聚合并去重。当前为预研版向量库,后续可平滑替换为 sentence-transformer。
  • 9.4 训练三阶段scripts/train_backend_model.py 落地 Stage1(预训练 C→ASM)/ Stage2(微调 NL→ASM, spec-verified)/ Stage3(DPO 策略选择);数据源 E:\github\pico-examples + F:\Autoresearch 训练框架参考;大文件工作区 F:\NLC(models/data/checkpoints);脚本内嵌详细 loss 诊断日志(7 个关键节点 + spike/NaN/grad explosion 预警)。nlc/model/training.py 提供 PretrainPair / FinetunePair / DPOPreference 数据类与 filter_verified 质量门。
  • 9.5 策略选择nlc/model/strategy.py 提供 4 个策略(busy_wait / timer_irq / pwm_hardware / pio_state_machine)及其元数据(CPU 占用 / 代码大小 / 功耗 / 适用场景),select_strategy() 根据自然语言中的约束线索选择最优路径;DPO 偏好数据格式与 DPOPreference 对齐。
  • 9.6 部署优化:量化 / vLLM / Speculative Decoding / 模式缓存属于部署期工作,当前 v0.1 聚焦流水线正确性,部署优化在模型训练完成后接入。
  • 9.7 反馈重试BackendModel 内置 retry 循环,nlc/model/prompt.pymake_feedback() 生成包含"错误类型 / 错误位置 / 修正建议"三要素的反馈消息,重试上限 3 次,超过后回退到规格重新提取——与文中描述一致。
  • 9.8 部署架构:云端/本地/设备三段分层部署尚未搭建,当前 nlc/cli.py 提供本地单机端到端入口(model-compile / extract-spec / expand-ir),未来按图 10 拆分。
  • 测试覆盖:tests/test_model.py 92 条用例,全量 142 passed;关键设计不变量"模型负责快和理解,确定性工具负责合法和正确"已被测试固化为行为契约。

Sources

  1. Apache TVM, Compiler Infrastructure for ML. TVM 分层编译架构参考。 https://tvm.apache.org/docs/arch/index.html
  2. Raspberry Pi, RP2040 Datasheet. 芯片架构、PIO、外设规格。 https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf
  3. Raspberry Pi Documentation, Microcontroller Chips. RP2040 官方文档。 https://www.raspberrypi.com/documentation/microcontrollers/microcontroller-chips.html
  4. pico_float 库文档. RP2040 软件浮点实现与 Boot ROM 优化函数。 https://www.pidoc.cn/docs/pico-sdk/runtime/pico-float/
  5. RP2040 可执行内存区域与内存映射详解. SRAM bank 结构与 XIP 缓存策略。 https://captdam.com/pico-execmem/en
  6. Microsoft, UF2 File Format. USB 烧录文件格式规范。 https://github.com/microsoft/uf2
  7. Raspberry Pi Pico C/C++ SDK Manual. SIO 核间通信、多核编程 API。 https://datasheets.raspberrypi.com/pico/raspberry-pi-pico-c-sdk.pdf
Last modification:August 4, 2026
If you think my article is useful to you, please feel free to appreciate