先楫SPI Data Merge 功能详解和实践
HPM SPI Data Merge 功能详解和使用
一、问题背景
SPI 主机通过 DMA 发送数据时,默认每次 DMA 事务只搬运 1 字节(DMA_TRANSFER_WIDTH_BYTE)到 SPI 的 DATA 寄存器。这导致每发送一个字节,DMA 就要完成一次请求→响应→写入的完整握手流程,字节之间产生显著的时间间隔。
以 50MHz SCLK 为例,理论吞吐量 25MB/s,但实测字节间隔可能让有效速率大幅下降。
二、Data Merge 的工作原理
Data Merge 是 SPI TRANSFMT 寄存器的 bit7(DATAMERGE_MASK = 0x80):
SPI_TRANSFMT.DATAMERGE = 1 时:
- DMA 按 32-bit WORD 宽度写入 SPI DATA 寄存器
- SPI 控制器内部自动将 1 个 WORD拆解为 4 个连续字节串行发出
- 发送端:4 字节仅触发 1 次 DMA 事务,而非 4 次
- 接收端:SPI 将 4 个连续字节打包成 1 个 WORD,DMA 按 WORD 宽度一次性搬走
效果:DMA 事务数减少 4 倍,字节间间隔几乎消除,有效吞吐量逼近理论值。
前提条件:Data Merge 仅在 DATALEN=8bit(即 data_len_in_bits=8)时可用。因为 Merge 的本质是将 4 个 8-bit 数据单元合并为 1 个 32-bit WORD,当数据单元本身就是 16/32-bit或者其他bit时,无法再做 4:1 合并。
Data Merge vs 直接 32-bit DATALEN
一个容易混淆的问题:为什么不直接把 DATALEN 设为 32-bit 来获得 WORD 宽度传输?两者有本质区别:
| 对比项 | Data Merge (DATALEN=8bit + DATAMERGE=1) | 直接 32-bit DATALEN |
|---|---|---|
| SPI 总线帧格式 | 每个 SCLK 周期传输 1 bit,4 个 8-bit 帧依次发出 | 每个 SCLK 周期传输 1 bit,1 个 32-bit 帧整体发出 |
| 对端设备感知 | 收到 4 个独立的 8-bit 数据,与普通 SPI 8-bit 传输完全一致 | 收到 1 个 32-bit 数据帧,需要对端也支持 32-bit 帧格式 |
| 适用对象 | 标准 8-bit SPI 外设(NOR Flash、传感器等) | 仅 32-bit SPI 外设(某些 ADC/DAC、并行数据接口) |
| 长度灵活性 | 可动态开关 merge,非对齐时回退 BYTE | 必须 32-bit 对齐,无回退机制 |
| DMA 宽度 | WORD(合并 4 个 BYTE) | WORD(天然 32-bit) |
总结:Data Merge 是在 保持 8-bit SPI 协议不变 的前提下,仅优化 DMA 与 SPI 控制器之间的搬运效率;而 32-bit DATALEN 是真正改变了 SPI 总线上的数据帧长度,对端设备必须能解析 32-bit 帧。大多数 SPI 外设(NOR Flash、显示屏等)只支持 8-bit 帧,此时只能用 Data Merge。
性能对比
| 模式 | DMA Width | 128 字节的事务数 | 字节间隔 |
|---|---|---|---|
| 无 Merge | BYTE | 128 次 | 明显 |
| 有 Merge | WORD | 32 次 | 几乎消除 |
实测数据(传输 512 字节):
| 配置 | SCLK | Merge | 耗时 | 实测带宽 | 理论带宽 | 结论 |
|---|---|---|---|---|---|---|
| 66M SCLK + Merge | 66MHz | ✅ | 15.352μs | ~33MB/s | 33MB/s | 实测与理论值接近 |
| 80M SCLK + Merge | 80MHz | ✅ | 12.794μs | ~40MB/s | 40MB/s | 实测与理论值接近 |
| 无 Merge(任意频率) | - | ❌ | - | - | - | 字节间隔显著,有效速率远低于理论值 |
关键现象:无 Merge 时 SCLK 时钟脉冲不连续,逻辑分析仪实测 SCLK 频率低于配置值(DMA 每字节握手产生间隙);开启 Merge 后时钟连续,实测带宽吻合理论计算。
下图展示了无 Merge 时 SCLK 的不连续现象,60MHz SPI SCLK, 字节之间出现明显间隔:

下图展示了开启 Merge 后 SCLK 连续输出,字节间无间隔:
在66M的SPI SCLK时钟下,逻辑分析仪抓到的SCLK能保持连续,并且数据能对的上。512字节耗时15.352us,合计33MB/S左右,与理论速度33MB/S接近。


三、使用方法
以下代码模式在 SDK 中多处使用,主要参考来源:
| 来源 | 路径 | 说明 |
|---|---|---|
| Serial NOR Flash 驱动 | components/serial_nor/interface/spi/hpm_serial_nor_host_spi.c |
TX/RX merge 动态开关,最完整的实战示例 |
| SPI 驱动 API | drivers/inc/hpm_spi_drv.h |
spi_enable/disable_data_merge 定义 |
| 寄存器定义 | soc/HPM5100/ip/hpm_spi_regs.h |
TRANSFMT_DATAMERGE_MASK(0x80) 定义 |
3.1 发送(TX)场景
/* 条件:数据长度必须是 4 的整数倍 */
if ((len % 4) == 0) {
spi_enable_data_merge(SPI_PTR); // 开启 merge
data_width = DMA_TRANSFER_WIDTH_WORD; // DMA 用 WORD 宽度
} else {
data_width = DMA_TRANSFER_WIDTH_BYTE; // 不能 merge,回退到 BYTE
}
/* TX DMA 配置时建议降低 FIFO 阈值,减少启动延迟 */
spi_set_tx_fifo_threshold(SPI_PTR, 3);
/* 传输结束后关闭 merge,避免影响下次非对齐传输 */
spi_disable_data_merge(SPI_PTR);
3.2 接收(RX)场景
/* RX DMA 总是开启 merge,提升接收吞吐量 */
spi_enable_data_merge(SPI_PTR);
data_width = DMA_TRANSFER_WIDTH_WORD;
/* 非 4 对齐的长度:DMA 传输尺寸向上取整到 4 字节 */
if ((len % 4) == 0) {
dma_send_size = len;
} else {
dma_send_size = ((len >> 2) + 1) << 2; // 多读几个字节
}
/* DMA RX 以 WORD 宽度搬运 dma_send_size 字节 */
spi_rx_trigger_dma(..., DMA_TRANSFER_WIDTH_WORD, dma_send_size);
3.3 Serial NOR Flash 驱动中的典型模式
参考 hpm_serial_nor_host_spi.c:
| 操作 | Data Merge | DMA Width | 长度限制 |
|---|---|---|---|
| 写 Flash | 仅 len%4==0 时开启 |
WORD 或 BYTE | 非 4 对齐用 BYTE |
| 读 Flash | 总是开启 | WORD | DMA 尺寸向上取整到 4 |
四、注意事项
- 数据位宽限制:Data Merge 仅适用于
data_len_in_bits=8(8-bit 数据单元) - 长度对齐:TX 方向数据长度必须 4 字节对齐才能开启 merge;RX 方向可以向上取整(多读几个无效字节不影响正确数据)
- 及时关闭:merge 开启后影响后续传输的 DMA 宽度,每次传输结束后必须
spi_disable_data_merge - TX FIFO 阈值:开启 merge 写时建议
spi_set_tx_fifo_threshold(ptr, 3),降低阈值加速 DMA 填充启动 - Cache 操作:merge + DMA 使用 WORD 宽度访问 buffer,若 buffer 在可缓存区域需确保 writeback/invalidate 覆盖完整 cache line;若 buffer 放在
.noncacheable区域(如 AHB SRAM)则无需 cache 操作