Skip to content

概念先知道

  • 压力测试(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)或用无缓存模式定位。

操作步骤

1
硬件准备

需要带 PSRAM 的开发板(例程根据芯片 efuse 中的 psram_info 自动确定测试容量:4/8/16MB)。无 PSRAM 的板子会提示 This chip has no psram 后停住。

2
进入例程目录

在终端进入 SDK 的 memtester 例程目录(前提:已按快速开始(Linux)Windows搭建好环境):

cd examples/memtester
3
编译工程

执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可(例程 defconfig 已开启 CONFIG_PSRAM=y):

make CHIP=bl616 BOARD=bl616dk
4
烧录固件

用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):

make flash CHIP=bl616 COMX=/dev/ttyUSB0
5
运行验证

打开串口助手(波特率 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_infobflb_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 完整代码
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)后重新编译。

Released under the MIT License. Build Time 2026-09-11 14:52:23