Files
sdmacc/docs/smartdma.md
T

16 KiB
Raw Blame History

SmartDMA / EZH 技术概要

本文档整理 NXP AN14650《SmartDMA Cookbook》中与 sdmacc 编译器设计相关的信息。SmartDMA 在文档中也称为 EZH,二者指同一类 NXP MCU 内的轻量可编程协处理单元。

资料来源

定位

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。
  • B1external flag,影响 EX / NEX 条件。
  • B0start 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 返回地址寄存器。

如果 CFMGPDGPI 等专用功能未使用,文档说明它们也可作为通用寄存器使用。编译器是否利用这些寄存器,需要结合 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:字节序翻转。
  • FBITbit 顺序翻转。
  • 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。SPOSNE 的更新规则不同于普通 PONE,适合实现“每轮都移位,但有条件执行 ALU”的算法。

外部/布尔检测 flag

  • EXexternal flag set。
  • NEXexternal flag not set。
  • BSbit-slice logical combiner set。
  • NBSbit-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_INPUT0BS_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_HOLDE_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 加上若干立即执行指令。
  • CFMCFS 会清除 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 有 SPRA,但栈管理主要由软件负责:

  • 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_BOOTEZHB_ARM2EZHEZHB_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. 函数调用:若支持函数,必须明确 RASP 保存策略。
  7. 事件等待:用 intrinsic 暴露 E_HOLDE_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 数据模型。