Files

415 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SmartDMA / EZH 技术概要
本文档整理 NXP AN14650《SmartDMA Cookbook》中与 `sdmacc` 编译器设计相关的信息。SmartDMA 在文档中也称为 EZH,二者指同一类 NXP MCU 内的轻量可编程协处理单元。
## 资料来源
- NXP HTML 文档:<https://docs.nxp.com/bundle/AN14650/page/topics/introduction.html>
- NXP PDF 文档:<https://www.nxp.com/docs/en/application-note/AN14650.pdf>
- 文档版本:AN14650 Rev. 1.02025-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 hijackEZH 输出最多 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 数据模型。