From 0ee5914953e7d3de8e2aff544bd186c1a8e47a90 Mon Sep 17 00:00:00 2001 From: Logiase Song Date: Tue, 7 Jul 2026 18:32:05 +0800 Subject: [PATCH] Add SmartDMA documentation draft. --- AGENTS.md | 12 ++ README.md | 3 + docs/smartdma.md | 414 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 429 insertions(+) create mode 100644 AGENTS.md create mode 100644 README.md create mode 100644 docs/smartdma.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..d7d307a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,12 @@ +# sdmacc +`sdmacc` is a subset standalone C compiler for NXP SmartDMA. + +## Repository Structure +- All documentation stored in `docs` directory contains SmartDMA architecture and instructions. + +## Work Style +- Always ask permission when touching non-versioned files. + +## Note +### DO NOT send optional commentary +Spend time on thinking; you do not need to use the commentary channel to report progress to me. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5cd1d55 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# sdmacc + +`sdmacc` is a standalone subset C compiler for NXP SmartDMA. diff --git a/docs/smartdma.md b/docs/smartdma.md new file mode 100644 index 0000000..8474074 --- /dev/null +++ b/docs/smartdma.md @@ -0,0 +1,414 @@ +# SmartDMA / EZH 技术概要 + +本文档整理 NXP AN14650《SmartDMA Cookbook》中与 `sdmacc` 编译器设计相关的信息。SmartDMA 在文档中也称为 EZH,二者指同一类 NXP MCU 内的轻量可编程协处理单元。 + +## 资料来源 + +- NXP HTML 文档: +- NXP PDF 文档: +- 文档版本:AN14650 Rev. 1.0,2025-05-28。 + +## 定位 + +SmartDMA / EZH 不是替代 Arm 主核的通用 CPU,而是用于卸载重复性、事件驱动、I/O 密集和实时性敏感任务的辅助处理单元。 + +典型收益: + +- 减少 Arm 中断响应路径带来的延迟。 +- 对 GPIO、外部事件和布尔条件有更直接的硬件支持。 +- 可在数据搬运过程中做格式转换或简单处理,因此比普通 DMA 更灵活。 +- 支持确定性执行机制,例如心跳节拍、等待节拍、全指令节拍同步和硬件 tight loop。 +- 通过指令 AHB 与数据 AHB 分离,降低取指和数据访问互相阻塞的概率。 + +典型用途: + +- RGB565 到 RGB888 等数据格式转换。 +- 键盘扫描、GPIO 状态机、流式 GPIO 输出。 +- 软件模拟 USART、I2C、SPI、ISO7816、PWM 等简单时序协议。 +- CRC、SHA 等移位型算法中的辅助计算。 +- 图像或大块数据预处理。 +- 多路正交编码器检测。 + +## 系统结构 + +EZH 在系统中作为 AHB controller 工作,使用双总线结构: + +- `Inst-AHB`:专门用于取 opcode。 +- `Data-AHB`:用于 literal、只读数据、读写数据和片上 memory/peripheral 访问。 + +这种结构的一个重要语义是:PC 与取指流水线相关,EZH 的 `PC` 通常位于当前执行指令之后两条指令的位置。分支、literal 读取、PC 相对寻址和代码生成都必须考虑这一点。 + +EZH 还包含: + +- 轻量处理器核心。 +- ALU。 +- barrel shifter。 +- bit-slice 布尔检测逻辑。 +- GPIO 直接访问接口。 +- 中断/事件输出。 +- AHB 访问接口。 + +## 外设寄存器 + +EZH 控制寄存器位于 APB 地址空间。不同芯片族的基地址不同: + +| 芯片族 | EZH 控制寄存器基地址 | +| --- | --- | +| LPC5410x | `0x4004C000` | +| LPC54114 / LPC51U68 | `0x4001D000` | +| LPC55(S)6x / LPC55(S)2x | `0x4001D000` | +| LPC55(S)3x | `0x4001D000` | +| i.MX RT500 | `0x40027000` | +| MCXNx4x / MCXN23x | `0x40033000` | + +主要控制寄存器: + +| 名称 | 偏移 | 作用 | +| --- | ---: | --- | +| `EZHB_BOOT` | `0x20` | 32-bit 启动地址,需 4 字节对齐。 | +| `EZHB_CTRL` | `0x24` | 控制启动、外部 flag、AHB 同步、AHB error 策略等。高 16 位必须写 `0xC0DE`。 | +| `EZHB_PC` | `0x28` | 只读程序计数器。 | +| `EZHB_SP` | `0x2C` | 只读栈指针。 | +| `EZHB_BREAK_ADDR` | `0x30` | breakpoint 匹配地址。 | +| `EZHB_BREAK_VECT` | `0x34` | breakpoint 服务例程地址。 | +| `EZHB_EMER_VECT` | `0x38` | emergency 例程地址。 | +| `EZHB_EMER_SEL` | `0x3C` | software emergency 请求与使能。 | +| `EZHB_ARM2EZH` | `0x40` | Arm 到 EZH 的通用通信/控制字段。 | +| `EZHB_EZH2ARM` | `0x44` | EZH 到 Arm 的通用通信字段;在特定配置下写入可触发 Arm 中断。 | +| `EZHB_PENDTRAP` | `0x48` | pending trap enable、polarity、request/status。 | + +`EZHB_CTRL` 重点位: + +- `[31:16]`:必须为 `0xC0DE`。 +- `B4`:使能 AHB synchronization。 +- `B3`:选择 AHB write bufferable / nonbufferable。 +- `B2`:是否忽略 AHB bus error;为 0 时 bus error 触发 emergency。 +- `B1`:external flag,影响 `EX` / `NEX` 条件。 +- `B0`:start bit,置位后启动 EZH 执行。 + +## 内部寄存器 + +所有 EZH 内部寄存器为 32-bit。 + +| 寄存器 | 读写 | 作用 | +| --- | --- | --- | +| `R0` - `R7` | RW | 通用寄存器。 | +| `GPO` | RW | EZH GPIO 输出寄存器,bit 0-31 对应 GPIO 输出状态。 | +| `GPD` | RW | EZH GPIO 方向寄存器,1 为输出,0 为输入。 | +| `GPI` | RO | EZH GPIO 输入寄存器。 | +| `CFS` | RW | bit-slice source 配置;低 8 位反映当前 bit-slice 输入状态。 | +| `CFM` | RW | bit-slice event 配置;低 8 位控制输出路由。 | +| `SP` | RW | 栈指针;即使不主动使用栈也需要初始化。 | +| `PC` | RW | 程序计数器;通常领先当前执行指令两条。 | +| `RA` | RW | 返回地址寄存器。 | + +如果 `CFM`、`GPD`、`GPI` 等专用功能未使用,文档说明它们也可作为通用寄存器使用。编译器是否利用这些寄存器,需要结合 ABI 和目标程序特性谨慎定义。 + +## 执行模型 + +### ALU + +EZH ALU 支持 32-bit 加减和逻辑运算,常见操作包括: + +- `ADD` +- `SUB` +- `ADC` +- `SBC` +- `AND` +- `OR` +- `XOR` +- `ANDOR` + +EZH 的一个关键特性是可在单周期内组合执行 `FEND/FBIT + LSR + ALU + S` 这类操作。也就是说,预移位、字节序翻转、位翻转、ALU 运算和置 flag 可以组合进同一类指令形式。代码生成阶段应尽量利用这些融合形式。 + +### Barrel Shifter + +barrel shifter 支持: + +- `LSL` +- `LSR` +- `ASR` +- `ROR` + +立即数 shift 范围为 0-31。寄存器 shift 使用寄存器低 8 位作为 shift/rotate 数量。对于 32 或更大移位量,结果和 carry 行为有专门规则,后端生成移位代码时不能直接套用 C 语言的未定义行为。 + +### Flip / Invert + +EZH 支持在 ALU 前做: + +- `FEND`:字节序翻转。 +- `FBIT`:bit 顺序翻转。 +- invert:按位取反。 + +这对 CRC、哈希、字节序转换和像素格式转换很重要。 + +### Flags + +EZH 条件执行依赖 flag。flag 可来自 ALU/MOV/LOAD immediate、bit-slice 事件或 external flag。 + +ALU flag: + +| Flag | 近似 Arm 条件 | 作用 | +| --- | --- | --- | +| `EU` | 无 | 无条件执行。 | +| `ZE` | `EQ` | Zero。 | +| `PO` | `PL` / `GE` | Positive。 | +| `NE` | `MI` | Negative。 | +| `AZ` | `GT` | Above zero。 | +| `ZB` | `LE` | Zero or below。 | +| `CA` | `CS` | Carry set。 | +| `NC` | `CC` | Carry not set。 | +| `CZ` | 无 | Carry set and zero。 | + +算法/分支控制 flag: + +- `SPO` / `UNS` +- `SNE` / `NZS` + +它们可用于移位型算法和 scheduled branch。`SPO`、`SNE` 的更新规则不同于普通 `PO`、`NE`,适合实现“每轮都移位,但有条件执行 ALU”的算法。 + +外部/布尔检测 flag: + +- `EX`:external flag set。 +- `NEX`:external flag not set。 +- `BS`:bit-slice logical combiner set。 +- `NBS`:bit-slice logical combiner not set。 + +## GPIO + +EZH 支持 32-bit GPIO 直接访问: + +- `GPO` 控制输出值。 +- `GPD` 控制方向。 +- `GPI` 读取输入值。 + +与通过 AHB 访问普通 GPIO 外设相比,直接寄存器访问延迟更低。`E_MODIFY_GPO_BYTE` 可在单周期内对 `GPO` 低 8 位执行 clear/set/toggle 组合操作,适合严格时序的 GPIO 波形生成。 + +## Bit-slice 布尔检测 + +EZH 有 8 个 bit-slice 布尔检测引擎。每个 bit-slice 可从 `BS_INPUT0` 到 `BS_INPUT7` 中选择一个输入,输入来源通常通过芯片 INPUTMUX 绑定到 GPIO、中断、timer match 等触发源。 + +`CFS` 配置输入源: + +- `CFS[31:8]`:每个 bit-slice 使用 3 bit 选择输入。 +- `CFS[7:0]`:反映当前 bit-slice 输入状态。 + +`CFM` 配置事件检测: + +| 3-bit 配置 | 行为 | +| --- | --- | +| `000` | 恒为 1。 | +| `001` | sticky rising edge。 | +| `010` | sticky falling edge。 | +| `011` | sticky any edge。 | +| `100` | high level。 | +| `101` | low level。 | +| `110` | 恒为 0。 | +| `111` | any edge。 | + +sticky edge 需要软件写 `CFM` 清除后才能检测下一次事件。 + +每个 bit-slice 输出可以: + +- 路由到 logical combiner。 +- 级联到下一个 bit-slice。 + +`CFM[7:0]` 控制每个 bit-slice 的 OR enable。logical combiner 输出可驱动 `BS` / `NBS` 条件、`E_HOLD`、`E_VECTORED_HOLD` 和相关事件等待机制。 + +## HOLD 与事件向量 + +`E_HOLD` 会暂停执行,直到布尔条件匹配。 + +`E_VECTORED_HOLD` 可以等待多个 bit-slice 条件,并在某个条件触发后跳转到对应 vector table entry。它类似“事件等待 + 跳转/调用”。默认 vector table 有 8 个 entry,对应 8 个 bit-slice。 + +注意点: + +- `E_VECTORED_HOLD` 后面的两条指令会被流水线 flush,不执行,因此有额外事件处理延迟。 +- `E_ACC_VECTORED_HOLD` 可加速指定事件,保留后续指令在流水线中,使特定事件触发后更快执行。 +- large vector table 形式每个 entry 包含 4 条指令,便于 scheduled branch 加上若干立即执行指令。 +- 写 `CFM` 或 `CFS` 会清除 `BS` flag。 + +## Pending Trap + +`EZHB_PENDTRAP` 提供 pending trap 机制,可锁存输入 IO 边沿/电平变化,并作为 bit-slice 的触发源。 + +字段含义: + +- `[23:16]`:pending trap enable。 +- `[15:8]`:pending trap status polarity。 +- `[7:0]`:pending trap request/status。 + +用途: + +- 锁存输入变化,直到软件清除。 +- 通过 polarity 配置检测上升或下降方向。 +- 由 Arm 软件修改低 8 位,间接触发 bit-slice/vector 行为。 + +## Heartbeat Timer + +EZH 有 16-bit heartbeat down counter。`E_HEART_RYTHM` 设置 reload 值并启动计数。默认 reload 为 0,此时每个周期都有 beat;文档说明该模式下 counter 会 clock-gate 以节省功耗。 + +相关指令: + +- `E_HEART_RYTHM`:设置 heartbeat。 +- `E_WAIT_FOR_BEAT`:等待下一个 beat。 +- `E_SYNCH_ALL_TO_BEAT`:切换为所有指令按 beat 同步执行。 + +用途: + +- 串行采样等固定节拍任务。 +- 降低执行速率以省电。 +- 在共享 memory/AHB 场景下获得更确定的执行节奏。 + +## Tight Loop + +`E_TIGHT_LOOP` 是硬件循环机制,避免传统 decrement/compare/branch 循环的额外分支开销。 + +语义要点: + +- 循环执行次数为 `1 + loop_counter`。 +- `E_TIGHT_LOOP` 后的第一条指令只在初次进入时执行一次。 +- 设置 tight loop 使用的寄存器在循环体中可复用,硬件已捕获循环参数。 + +编译器后端可以将固定次数小循环或简单 counted loop 映射到 `E_TIGHT_LOOP`,但必须保证循环体边界和首条指令一次性执行语义正确。 + +## 栈与返回地址 + +EZH 有 `SP` 和 `RA`,但栈管理主要由软件负责: + +- `SP` 需要初始化。 +- `E_PUSH` / `E_POP` 是基于 `SP` 的 load/store 包装。 +- 当前文档描述的栈为 upward-growing stack。 +- `E_GOSUB` 和默认的 `E_VECTORED_HOLD` 会将返回地址备份到 `RA`。 +- 嵌套调用时,用户或编译器必须显式保存/恢复 `RA`。 +- `E_GOTO` 可通过 `L` 选项保存返回地址到 `RA`。 + +对 `sdmacc` 来说,需要尽早定义最小 ABI: + +- 哪些寄存器用于表达式临时值。 +- 是否支持函数调用。 +- `RA` 由 caller 还是 callee 保存。 +- 是否启用栈,以及栈空间由 Arm 侧启动代码如何传入。 + +## Breakpoint 与 Emergency + +Breakpoint: + +- `EZHB_BREAK_ADDR` 指定断点地址。 +- `EZHB_BREAK_VECT` 指定断点服务例程。 +- 执行到匹配地址时,EZH 用跳转到 vector 的方式替换该指令。 +- 不建议把断点放在 scheduled branch 附近,否则程序流难以还原。 + +Emergency: + +- `EZHB_EMER_VECT` 指定 emergency 例程。 +- external emergency 或 AHB error 可触发 emergency。 +- 进入 emergency 后不能通过普通代码退出,只能 reset EZH 或拉低 ignition/start。 + +## Arm / EZH 通信与中断 + +EZH 可通过三种机制向 Arm 触发中断: + +- `E_INT_TRIGGER(n)`:触发 common interrupt output 和具体 interrupt output channel。 +- `EZHB_ARM2EZH` / `EZHB_EZH2ARM` 握手:当 `EZHB_ARM2EZH[1:0] == 0b10`,写 `EZHB_EZH2ARM` 可触发 Arm 中断。 +- interrupt hijack:EZH 输出最多 32-bit interrupt bus,用于模拟不同外设中断源。 + +Arm 侧也需要: + +- 打开 EZH clock/reset。 +- 配置 PINMUX 和 INPUTMUX。 +- 配置 NVIC 中 EZH 的 IRQ。 +- 设置 `EZHB_BOOT`、`EZHB_ARM2EZH`、`EZHB_CTRL` 后启动 EZH。 + +## 指令集概要 + +AN14650 将 EZH 指令集概括为: + +| 指令族 | 功能 | +| --- | --- | +| `E_MOV`, `E_LOAD_SIMM`, `E_LOAD_IMM` | 数据移动和立即数加载。 | +| `E_ADD` | 加法,可带 postshift。 | +| `E_SUB` | 减法,可带 postshift。 | +| `E_ADC` | 带 carry 加法。 | +| `E_SBC` | 带 carry 减法。 | +| `E_AND` | 位与。 | +| `E_OR` | 位或。 | +| `E_XOR` | 位异或。 | +| `E_ANDOR` | AND 后 OR 的组合逻辑。 | +| `E_LSL`, `E_LSR`, `E_ASR`, `E_ROR` | 预移位/旋转和 ALU 组合。 | +| `E_FEND`, `E_FBIT` | 字节序/bit 顺序翻转后参与右移和 ALU。 | +| `E_RLSL`, `E_RLSR`, `E_RASR`, `E_RROR` | 寄存器控制的移位/旋转。 | +| `E_BTST`, `E_BSET`, `E_BCLR`, `E_BTOG` | bit test/set/clear/toggle。 | +| `E_MODIFY_GPO_BYTE` | 单周期修改 `GPO` 低字节。 | +| `E_TIGHT_LOOP` | 零额外分支开销循环。 | +| `E_HOLD` | 等待布尔模式匹配。 | +| `E_NOP` | 空操作。 | +| `E_HEART_RYTHM` | 设置 heartbeat counter。 | +| `E_SYNCH_ALL_TO_BEAT` | 所有指令同步到 beat。 | +| `E_WAIT_FOR_BEAT` | 等待下一次 beat。 | +| `E_INT_TRIGGER` | 触发 interrupt output。 | +| `E_GOTO` | 分支;scheduled branch 单周期,普通分支约三周期。 | +| `E_GOSUB` | 子程序调用,写 `RA`。 | +| `E_LDR` | 从片上地址读取数据到 EZH register。 | +| `E_STR` | 从 EZH register 写数据到片上地址。 | +| `E_LDR_REG` | register address 形式读取。 | +| `E_STR_REG` | register address 形式写入。 | +| `E_PER_READ`, `E_PER_WRITE` | 访问 Cortex-M peripheral region。 | +| `E_PUSH`, `E_POP` | 栈 push/pop。 | + +指令命名按流水线执行顺序组合。例如 `E_SUBN_LSLS` 表示 subtract、取反、logical shift left、set flags。 + +常见后缀/符号: + +| 符号 | 含义 | +| --- | --- | +| `COND` | 条件执行。 | +| `N` | ALU 结果取反。 | +| `S` | 设置 flags;在 `LDRB` 场景也可表示 signed access。 | +| `F` / `FEND` | 字节序翻转。 | +| `FBIT` | bit 顺序翻转。 | +| `LSL` / `LSR` / `ASR` / `ROR` | 移位/旋转。 | +| `IMM` | 立即数。 | +| `R` / `REG` | register 操作数。 | +| `NRA` | 不更新 `RA`。 | +| `LV` | large vector table。 | +| `ACC` | accelerated vectored hold。 | +| `PER` | peripheral access。 | +| `BTST` / `BSET` / `BCLR` / `BTOG` | bit 操作。 | + +## 对 sdmacc 的编译器设计影响 + +### 初始目标建议 + +第一阶段不要直接实现完整 C。建议先定义 SmartDMA 友好的 C 子集: + +- `uint32_t` / `int32_t` 为主。 +- 简单局部变量。 +- 常量表达式和寄存器内表达式。 +- `if` / `while` / 固定次数 `for`。 +- 指针读写,用于 memory/peripheral 访问。 +- 内建函数或扩展语法映射 GPIO、interrupt、hold、heartbeat、tight loop 等 EZH 特性。 + +### 后端优先级 + +后端应优先覆盖: + +1. 寄存器分配:先支持 `R0`-`R7`,再决定是否把未使用的专用寄存器纳入通用池。 +2. 立即数加载:`E_LOAD_IMM` 仅支持有限范围立即数,32-bit 常量需要多指令合成或 literal load。 +3. 条件执行:将简单 `if` 映射为条件指令,减少分支。 +4. 循环:识别 counted loop 并使用 `E_TIGHT_LOOP`。 +5. 内存访问:区分普通 memory load/store 与 peripheral access。 +6. 函数调用:若支持函数,必须明确 `RA` 和 `SP` 保存策略。 +7. 事件等待:用 intrinsic 暴露 `E_HOLD`、`E_VECTORED_HOLD`、heartbeat 等非 C 原生语义。 + +### 需要继续确认的信息 + +AN14650 对架构、寄存器、指令族和伪指令形式有较完整描述,但要做 standalone compiler 仍需进一步确认: + +- 每条指令的最终 32-bit binary encoding。 +- NXP SDK 头文件中 EZH pseudo instruction macro 的编码细节。 +- 不同芯片族是否存在 ISA 子集差异或寄存器行为差异。 +- SmartDMA 代码段放置、启动参数、栈地址和 Arm 侧 runtime glue 的约定。 +- 是否需要直接生成机器码,还是先生成 NXP macro assembly / C header,再由 Arm 工具链集成。 + +对本项目而言,建议下一步从 NXP SDK 的 `fsl_smartdma_prv.h` 或等价头文件提取 opcode encoding,形成 `docs/smartdma-isa.md`,再开始实现 lexer/parser 之前的目标 ISA 数据模型。