高速缓存 (Cache)
在 STM32 H7/F7 等带 Cache 的系列中,LibXR 的驱动在 DMA 传输前后维护数据 Cache,在 CubeMX 中开启 I-Cache 和 D-Cache 即可。
代码生成中与 Cache 相关的设置是 dma_section,用于把各外设的 DMA 缓冲区放到指定的 section;MPU 与 Cache 策略在 CubeMX 和链接脚本中配置。
Cache 配置基础
- 参考
ST AN4839:MPU 关闭时,SRAM 区域默认是WBWA (Write-Back, Write-Allocate)。 - 开启 MPU 后,Cache 策略可以继续自定义,这里不展开。
- 需要确定的是:DMA 缓冲区放在哪块 RAM、这块 RAM 能否被 DMA 访问,以及是否需要 Cache 同步。
DMA 缓冲区内存分区
以 STM32H750 为例,其内部常见 RAM 区域可以粗略分成下面几类:
AXI RAM、SRAM1~4:DMA 可访问,但 DMA 与 CPU 共享访问时需要 Cache 同步ITCMRAM、DTCMRAM:CPU 直连,通常不能给 DMA 当缓冲区SRAM4:在 STM32H7 上也是BDMA可访问区域
各 RAM 区域能否用作 DMA 缓冲区如下表(简化):
| AXI RAM | SRAM1 | SRAM2 | SRAM3 | SRAM4 | ITCMRAM | DTCMRAM | |
|---|---|---|---|---|---|---|---|
| CPU | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| DMA1 | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
| DMA2 | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
| BDMA | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
说明:
AXI RAM、SRAM1~4适合做大容量 DMA 缓冲区,但 DMA 访问后需要处理 Cache 一致性;这一层由 LibXR 负责ITCMRAM、DTCMRAM适合放 CPU 高频访问的数据,但不适合做 DMA BufferSRAM4更适合给BDMA这类受限 DMA 控制器用
链接脚本把 .bss 放在 DMA 不能访问的 RAM 中时(例如 STM32CubeMX 为 STM32H7 生成的链接脚本中的 DTCMRAM),或需要显式控制 DMA 缓冲区所在区域时,在链接脚本里增加 section,例如:
.ram_d3 (NOLOAD) :
{
. = ALIGN(4);
*(.ram_d3)
*(.ram_d3*)
. = ALIGN(4);
} >RAM_D3
.axi_ram (NOLOAD) :
{
. = ALIGN(4);
*(.axi_ram)
*(.axi_ram*)
. = ALIGN(4);
} >RAM
配置文件
代码生成工具会直接读取 dma_section,并把对应缓冲区放进指定 section。例如:
SPI:
spi4:
tx_buffer_size: 32
rx_buffer_size: 32
dma_section: '.axi_ram'
dma_enable_min_size: 3
I2C:
i2c1:
buffer_size: 32
dma_section: '.axi_ram'
dma_enable_min_size: 3
USART:
usart1:
tx_buffer_size: 128
rx_buffer_size: 128
dma_section: '.axi_ram'
tx_queue_size: 5
ADC:
adc3:
buffer_size: 128
dma_section: '.ram_d3'
vref: 3.3
生成规则:
libxr gen读取每个实例的dma_section,把该实例的 DMA 缓冲区放进这个 section;- 未填写时
dma_section为空字符串,缓冲区放在默认的.bss中。STM32CubeMX 为 STM32H7 生成的链接脚本把.data和.bss放在 DTCMRAM,DMA1、DMA2 和 BDMA 都不能访问,这类工程需要为每个使用 DMA 的实例设置dma_section; - STM32F7、H7、H7RS 和 N6 有数据 Cache,每个缓冲区按 32 字节的 Cache 行对齐,数组长度向上取整到整行,缓冲区两端不与其他数据共用 Cache 行,传给驱动的长度仍是设置的大小;其他系列的缓冲区按 4 字节对齐。系列不在生成器的已知列表中时按有数据 Cache 处理,并给出警告。
生成结果
重新生成后,缓冲区带上对应的 section 属性。下面是一个 STM32H723 工程中的部分缓冲区,adc1 和 spi2 的 dma_section 为 .axi_ram,spi6 的为 .ram_d3(节选):
// DMA buffers (STM32H723VG: D-cache, 32-byte lines)
alignas(32) static uint16_t adc1_buf[128] __attribute__((section(".axi_ram")));
alignas(32) static uint8_t spi2_rx_buf[32] __attribute__((section(".axi_ram")));
alignas(32) static uint8_t spi2_tx_buf[32] __attribute__((section(".axi_ram")));
alignas(32) static uint8_t spi6_tx_buf[32] __attribute__((section(".ram_d3")));
// ...
usart1 的 tx_buffer_size 为 100、dma_section 为空时生成 alignas(32) static uint8_t usart1_tx_buf[128];,构造函数中写作 {usart1_tx_buf, 100}。
使用
使用方式与无 Cache 时一致,业务代码不需要改动。需要完成的是:
- 在
CubeMX里把 Cache 打开 - 把 DMA Buffer 放到 DMA 可访问的内存区域
- 手工指定区域时,链接脚本中的 section 名与
libxr_config.yaml中的dma_section保持一致