概念先知道
- 内存带宽:单位时间能搬多少数据(MB/s),决定大块数据搬运的性能上限。
- OCRAM vs PSRAM:OCRAM 是片上高速内存,PSRAM 是外挂内存,速度差异就是本页要测的。
- CPU 直拷 vs DMA:小数据量 CPU 直拷(带缓存)常常更快,大数据量 DMA 更优——本页给出对比数据。
例程功能简介
本页对应博流官方 SDK 的 ram_speed 例程(examples/peripherals/ram_speed),演示内存带宽测试:
- 搬运 32KB 数据,分别用 CPU memcpy 与 DMA 测量三组路径:OCRAM→OCRAM、OCRAM→PSRAM、PSRAM→PSRAM;
- 用
bflb_mtimer_get_time_us()计时,结果以 MB/s 打印; - 可直观对比 CPU 搬运与 DMA 搬运、片上内存与外挂 PSRAM 的速度差异。
操作步骤
本页不需要额外接线(需开发板带 PSRAM 才有完整结果)。在终端进入 SDK 的例程目录(前提:已按快速开始(Linux)或Windows搭建好环境):
cd examples/peripherals/ram_speed执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:
make CHIP=bl616 BOARD=bl616dk用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):
make flash CHIP=bl616 COMX=/dev/ttyUSB0打开串口助手(波特率 2000000)。例程用 memcpy 与 DMA 各测 3 组速度(OCRAM→OCRAM、OCRAM→PSRAM、PSRAM→PSRAM),打印形如 ocram2ocram speed:xxx MB/s 的结果。
代码执行流程
例程从启动到运行的完整流程如下(图中的循环箭头表示反复执行):
例程调用的 API 介绍
bflb_dma_channel_init / lli_reload / start / isbusy(...)
DMA 内存到内存搬运,bflb_dma_channel_isbusy 轮询等待传输完成(与 DMA 传输 相同用法)。
参数:同 DMA 例程
返回值:成功返回 0;失败返回负值错误码;isbusy 返回忙状态
ATTR_NOINIT_PSRAM_SECTION(宏)
把缓冲区放到 PSRAM(不初始化),配合 __attribute((aligned(32))) 对齐,用于测试 PSRAM 带宽。
参数:无
返回值:无
完整代码
以下为 ram_speed/main.c 完整源码,与官方示例(examples/peripherals/ram_speed)一致;点灯引脚已适配 Ai-M61/62-32S-Kit 板载 RGB 灯,默认折叠,点击展开:
📜 点击展开 ram_speed/main.c 完整代码
#include "bflb_dma.h"
#include "bflb_l1c.h"
#include "bflb_mtimer.h"
#include "board.h"
#define K_NUM 32
#define DMA_BUFFER_LENGTH (K_NUM * 1024)
__attribute((aligned(32))) uint8_t src1_buffer[DMA_BUFFER_LENGTH];
ATTR_NOINIT_PSRAM_SECTION __attribute((aligned(32))) uint8_t src2_buffer[DMA_BUFFER_LENGTH];
__attribute((aligned(32))) uint8_t dst1_buffer[DMA_BUFFER_LENGTH];
ATTR_NOINIT_PSRAM_SECTION __attribute((aligned(32))) uint8_t dst2_buffer[DMA_BUFFER_LENGTH];
struct bflb_dma_channel_lli_pool_s lli[20];
void memcpy_test(void)
{
uint64_t start_time = 0;
printf("memcpy speed test\r\n");
start_time = bflb_mtimer_get_time_us();
memcpy(dst1_buffer, src1_buffer, DMA_BUFFER_LENGTH);
printf("ocram2ocram speed:%d MB/s\r\n", K_NUM * 1000000 / 1024 / ((uint32_t)(bflb_mtimer_get_time_us() - start_time)));
start_time = bflb_mtimer_get_time_us();
memcpy(dst2_buffer, src1_buffer, DMA_BUFFER_LENGTH);
printf("ocram2psram speed:%d MB/s\r\n", K_NUM * 1000000 / 1024 / ((uint32_t)(bflb_mtimer_get_time_us() - start_time)));
start_time = bflb_mtimer_get_time_us();
memcpy(dst2_buffer, src2_buffer, DMA_BUFFER_LENGTH);
printf("psram2psram speed:%d MB/s\r\n", K_NUM * 1000000 / 1024 / ((uint32_t)(bflb_mtimer_get_time_us() - start_time)));
}
void dma_test()
{
struct bflb_device_s *dma_chx;
uint64_t start_time = 0;
printf("dma speed test\r\n");
dma_chx = bflb_device_get_by_name("dma0_ch0");
struct bflb_dma_channel_config_s config;
config.direction = DMA_MEMORY_TO_MEMORY;
config.src_req = 0;
config.dst_req = 0;
config.src_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
config.dst_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
config.src_burst_count = DMA_BURST_INCR4;
config.dst_burst_count = DMA_BURST_INCR4;
config.src_width = DMA_DATA_WIDTH_32BIT;
config.dst_width = DMA_DATA_WIDTH_32BIT;
bflb_dma_channel_init(dma_chx, &config);
struct bflb_dma_channel_lli_transfer_s transfers;
transfers.src_addr = (uint32_t)src1_buffer;
transfers.dst_addr = (uint32_t)dst1_buffer;
transfers.nbytes = DMA_BUFFER_LENGTH;
start_time = bflb_mtimer_get_time_us();
bflb_dma_channel_lli_reload(dma_chx, lli, 20, &transfers, 1);
bflb_dma_channel_start(dma_chx);
while (bflb_dma_channel_isbusy(dma_chx)) {}
printf("ocram2ocram speed:%d MB/s\r\n", K_NUM * 1000000 / 1024 / ((uint32_t)(bflb_mtimer_get_time_us() - start_time)));
transfers.src_addr = (uint32_t)src1_buffer;
transfers.dst_addr = (uint32_t)dst2_buffer;
transfers.nbytes = DMA_BUFFER_LENGTH;
start_time = bflb_mtimer_get_time_us();
bflb_dma_channel_lli_reload(dma_chx, lli, 20, &transfers, 1);
bflb_dma_channel_start(dma_chx);
while (bflb_dma_channel_isbusy(dma_chx)) {}
printf("ocram2psram speed:%d MB/s\r\n", K_NUM * 1000000 / 1024 / ((uint32_t)(bflb_mtimer_get_time_us() - start_time)));
transfers.src_addr = (uint32_t)src2_buffer;
transfers.dst_addr = (uint32_t)dst2_buffer;
transfers.nbytes = DMA_BUFFER_LENGTH;
start_time = bflb_mtimer_get_time_us();
bflb_dma_channel_lli_reload(dma_chx, lli, 20, &transfers, 1);
bflb_dma_channel_start(dma_chx);
while (bflb_dma_channel_isbusy(dma_chx)) {}
printf("psram2psram speed:%d MB/s\r\n", K_NUM * 1000000 / 1024 / ((uint32_t)(bflb_mtimer_get_time_us() - start_time)));
}
int main(void)
{
board_init();
memset(src1_buffer, 0x55, DMA_BUFFER_LENGTH);
memset(src2_buffer, 0x55, DMA_BUFFER_LENGTH);
memset(dst1_buffer, 0xaa, DMA_BUFFER_LENGTH);
memset(dst2_buffer, 0xaa, DMA_BUFFER_LENGTH);
memcpy_test();
dma_test();
while (1) {
}
}FAQ
PSRAM 相关速度打印为 0 或异常
开发板必须带 PSRAM,且内存分区里为 PSRAM 缓冲区预留了空间(见 defconfig);修改 K_NUM(KB)可改变搬运量。
memcpy 比 DMA 还快
小数据量下 CPU 直拷(含缓存)常常优于 DMA 建链开销,属正常现象;搬运量越大 DMA 优势越明显。结果受时钟/缓存状态影响,仅供参考。
想测别的内存区域
修改 src1_buffer/src2_buffer/dst1_buffer/dst2_buffer 的段属性(OCRAM/PSRAM)即可组合出不同路径。
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

