16 KiB
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.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 加减和逻辑运算,常见操作包括:
ADDSUBADCSBCANDORXORANDOR
EZH 的一个关键特性是可在单周期内组合执行 FEND/FBIT + LSR + ALU + S 这类操作。也就是说,预移位、字节序翻转、位翻转、ALU 运算和置 flag 可以组合进同一类指令形式。代码生成阶段应尽量利用这些融合形式。
Barrel Shifter
barrel shifter 支持:
LSLLSRASRROR
立即数 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/UNSSNE/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会清除BSflag。
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 特性。
后端优先级
后端应优先覆盖:
- 寄存器分配:先支持
R0-R7,再决定是否把未使用的专用寄存器纳入通用池。 - 立即数加载:
E_LOAD_IMM仅支持有限范围立即数,32-bit 常量需要多指令合成或 literal load。 - 条件执行:将简单
if映射为条件指令,减少分支。 - 循环:识别 counted loop 并使用
E_TIGHT_LOOP。 - 内存访问:区分普通 memory load/store 与 peripheral access。
- 函数调用:若支持函数,必须明确
RA和SP保存策略。 - 事件等待:用 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 数据模型。