概念先知道
- 压力测试(Stress Test):用各种数据模式反复读写内存,暴露位翻转、地址线错误、时序不稳等隐性故障,常用于产测与稳定性验证。
- PSRAM:外挂的内存芯片,容量由芯片型号决定。例程通过
bflb_efuse_get_device_info读取psram_info得到容量(4/8/16MB)。 - memtester:一个经典的开源内存测试程序(
memtester_main),支持多种测试算法:随机值、异或反转、8/16/32/64 位数据线模式等。 - 缓存(Cache)开关:测试可开启/关闭 D-Cache,关闭时直接读写 PSRAM,能暴露缓存掩盖下的时序问题。
- XIP 地址:例程用 0xA8000000(BL616)等地址直接访问 PSRAM,测试结束后用
div zero指令制造长时延停顿。
例程功能简介
本页对应博流官方 SDK 的 memtester 例程(examples/memtester),演示 PSRAM 全容量压力测试:
- 读取芯片
psram_info判断容量(4/8/16MB)与 PSRAM 时钟频率; - 打印测试参数:基址
0xA8000000、测试容量、循环 1000 次、页大小 1KB、缓存开关; - 调用
memtester_main运行完整压力测试套件,逐页逐位校验; - 测试结束打印
memtester stress test done!,随后进入长时延停顿循环(div zero指令),避免热耗与干扰。 - 同族例程:
examples/bflb_block_pool(内存池)、基础外设栏目「RAM Speed 内存带宽测试」(ram_speed)。
提示
跑完一轮全容量压测通常要几分钟;若中途打印错误信息,说明 PSRAM 或时序配置有问题,可先降频(dramFreq)或用无缓存模式定位。
操作步骤
需要带 PSRAM 的开发板(例程根据芯片 efuse 中的 psram_info 自动确定测试容量:4/8/16MB)。无 PSRAM 的板子会提示 This chip has no psram 后停住。
在终端进入 SDK 的 memtester 例程目录(前提:已按快速开始(Linux)或Windows搭建好环境):
cd examples/memtester执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可(例程 defconfig 已开启 CONFIG_PSRAM=y):
make CHIP=bl616 BOARD=bl616dk用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):
make flash CHIP=bl616 COMX=/dev/ttyUSB0打开串口助手(波特率 2000000)。例程打印 PSRAM 测试设置(基址、容量、循环次数 1000、频率、缓存开关),随后用 memtester 的多种测试算法(随机值、反转、8 位/16 位/32 位模式等)对整片 PSRAM 压测,全部通过后打印 memtester stress test done!。
代码执行流程
例程从启动到测试完成的流程如下:
例程调用的 API 介绍
bflb_efuse_get_device_info(&device_info)
读取芯片出厂信息,device_info.psram_info 表示 PSRAM 容量档位(1=4MB、2=8MB、3=16MB)。
参数:
device_info:bflb_efuse_device_info_type输出结构
返回值:0 成功;负值失败
memtester_main(base, size, &suffix, loops, page_size)
运行内存压力测试。例程传基址、测试容量、单位后缀、循环 1000 次、页大小 1KB。
参数:
base:测试起始地址size:测试字节数memsuffix:容量单位('B')loops:循环次数(例程 1000)page_size:页大小(例程 1KB)
返回值:无
Clock_Peripheral_Clock_Get(BL_PERIPHERAL_CLOCK_PSRAMB)
获取 PSRAM 总线时钟,例程用它计算并打印 PSRAM 频率(MHz)。
参数:
peripheral:外设时钟枚举(PSRAMB)
返回值:时钟频率
bflb_l1c_dcache_clean_all / bflb_l1c_dcache_disable()
关闭 D-Cache(仅在 enableCache=0 时执行),让测试直接读写 PSRAM,验证无缓存场景下的稳定性。
参数:无
返回值:无
完整代码
以下为 memtester/main.c 完整源码,与官方示例一致,默认折叠,点击展开:
📜 点击展开 memtester/main.c 完整代码
#include "bflb_mtimer.h"
#include "bflb_efuse.h"
#include "board.h"
#include "stdlib.h"
#include "string.h"
#include "memtester.h"
#if defined(BL616)
#include "bl616_common.h"
#include "bl616_glb.h"
// #include "bl616_sec_eng.h"
#include "bl616_tzc_sec.h"
#include "bl616_psram.h"
#endif
#if defined(BL616CL)
#include "bl616cl_common.h"
#include "bl616cl_glb.h"
// #include "bl616_sec_eng.h"
#include "bl616cl_tzc_sec.h"
#include "bl616cl_psram.h"
#endif
#if defined(BL618DG)
#include "bl618dg_common.h"
#include "bl618dg_glb.h"
// #include "bl606p_sec_eng.h"
#include "bl618dg_psram.h"
#endif
#define UHS_PSRAM_ADDR (0x50000000)
#define BL616_X8_PSRAM_ADDR (0xA8000000)
#define BL616CL_X8_PSRAM_ADDR (0x88000000)
#define BL618DG_X8_PSRAM_ADDR (0xB8000000)
#define MEMTESTER_M0
// #define MEMTESTER_D0
typedef struct _semc_test_config {
uint32_t baseAddr;
uint32_t testSize;
uint32_t loopNum;
uint32_t dramFreq;
uint32_t enableCache;
} semc_test_config_t;
int fail_stop = 1;
// uint64_t l1c_hit = 0, l1c_miss = 0;
// uint32_t read_hit_h = 0, read_hit_l = 0, read_miss_h = 0, read_miss_l = 0;
// uint32_t write_hit_h = 0, write_hit_l = 0, write_miss_h = 0, write_miss_l = 0;
int main(void)
{
bflb_efuse_device_info_type device_info;
board_init();
char memsuffix = 'B';
/* --------------- stress test --------------- */
semc_test_config_t testConfig;
#if defined(BL616)
testConfig.baseAddr = BL616_X8_PSRAM_ADDR;
testConfig.dramFreq = Clock_Peripheral_Clock_Get(BL_PERIPHERAL_CLOCK_PSRAMB) / 1000000;
#elif defined(BL616CL)
testConfig.baseAddr = BL616CL_X8_PSRAM_ADDR;
testConfig.dramFreq = Clock_Peripheral_Clock_Get(BL_PERIPHERAL_CLOCK_PSRAMB) / 1000000;
#elif defined(BL618DG)
testConfig.baseAddr = BL618DG_X8_PSRAM_ADDR;
testConfig.dramFreq = Clock_Peripheral_Clock_Get(BL_PERIPHERAL_CLOCK_PSRAMB) / 1000000;
#endif
testConfig.testSize = 0;
testConfig.loopNum = 1000;
testConfig.enableCache = 1;
printf("mtimer clk:%d\r\n", CPU_Get_MTimer_Clock());
printf("psram clk :%d\r\n", testConfig.dramFreq);
printf("Now CPU size_t:%d\r\n", sizeof(size_t));
bflb_efuse_get_device_info(&device_info);
if (device_info.psram_info == 0) {
printf("This chip has no psram, please disable CONFIG_PSRAM\r\n");
while (1) {}
}
#if 0
switch (device_info.psram_info) {
case 2:
testConfig.testSize = 32 * 1024 * 1024;
break;
case 3:
testConfig.testSize = 64 * 1024 * 1024;
break;
default:
printf("Psram info error\r\n");
while (1) {}
break;
}
#else
switch (device_info.psram_info) {
case 1:
testConfig.testSize = 4 * 1024 * 1024;
break;
case 2:
testConfig.testSize = 8 * 1024 * 1024;
break;
case 3:
testConfig.testSize = 16 * 1024 * 1024;
break;
default:
printf("Psram info error\r\n");
while (1) {}
break;
}
#endif
#if defined(MEMTESTER_M0)
if (!testConfig.enableCache) {
/* Disable D cache */
bflb_l1c_dcache_clean_all();
bflb_l1c_dcache_disable();
}
printf("\r\n########## Print out from target board ##########\r\n");
printf("\r\n PSRAM r/w test settings:\r\n");
printf(" Base Addr: 0x%x;\r\n", testConfig.baseAddr);
printf(" Test Size: %d Bytes;\r\n", testConfig.testSize);
printf(" Test Loop: %d;\r\n", testConfig.loopNum);
printf(" PSRAM PLL Freq: %d MHz;\r\n", testConfig.dramFreq);
printf(" Enable Cache: %d;\r\n\r\n", testConfig.enableCache);
/* Run memory stress test: 64MByte, loop=1, page_size = 1kbyte */
memtester_main(testConfig.baseAddr, testConfig.testSize, &memsuffix, testConfig.loopNum, (1 * 1024));
#endif
while (1) {
#ifdef __riscv_muldiv
int dummy;
/* In lieu of a halt instruction, induce a long-latency stall. */
__asm__ __volatile__("div %0, %0, zero"
: "=r"(dummy));
#endif
}
printf("memtester stress test done!\r\n");
while (1) {
bflb_mtimer_delay_ms(1000);
}
}FAQ
提示 “This chip has no psram” 怎么办?
说明芯片没有 PSRAM 或 efuse 未记录容量:确认开发板型号带 PSRAM(如 Ai-M61-32S-Kit / Ai-M62-32S);若确认有 PSRAM,检查 defconfig 是否开启 CONFIG_PSRAM=y。
测试跑到一半打印错误?
压力测试发现数据不一致,常见原因:PSRAM 频率过高(可降低 dramFreq 验证)、供电不足、板子布局干扰。可先用 enableCache=0(无缓存模式)复测缩小范围。
为什么测试完程序停在死循环里?
例程测试完成后用 div zero 指令制造长时延停顿(不是真正除零崩溃),目的是让 CPU 处于低功耗等待状态;想结束就按复位键。
测试要跑多久?
与 PSRAM 容量和频率有关:4MB 约几分钟,16MB 十几分钟以上。只想快速验证可把 testConfig.loopNum 改小(如 10)后重新编译。

