你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【STM32H5 评测】5、Flash和RAM读写速度测试

[复制链接]
EPTmachine 发布时间:2026-11-15 18:10

STM32H5E5ZJ有4MB的片上Flash,1.5MB的SRAM,GPDMA支持RAM到RAM,Flash到RAM的数据搬运。本文介绍STM32H5E5ZJ片上RAM、Flash的读写测试,以及开启DMA数据传输的性能。

在 STM32H5E5ZJ 上量化对比以下三类操作的耗时与吞吐:

  1. 片上 RAM 的读 / 写性能(CPU 不同位宽访问、调用 memcpy实现);
  2. 使用 GPDMA 进行数据搬运的耗时(RAM↔RAM、Flash→RAM),与 CPU 方式直接对比;
  3. 片上 Flash 的擦除 / 编程 / 读取性能(含写后读回校验)。

所有时间测量统一使用调试组件的 DWT 周期计数器 (CYCCNT),精度为 1 个 CPU 周期(250 MHz 下 4 ns),消除 HAL_GetTick 毫秒级粒度带来的误差。每组非破坏性测试固定搬运 1 MB 数据,块大小取 4/16/64/256 KB,直接对比 CPU 与 DMA:

测试项 说明
RAM write 8/16/32-bit (CPU) 不同位宽写 SRAM 的吞吐
RAM read 32-bit (CPU) CPU 顺序读 SRAM
RAM memcpy (CPU libc) newlib 库搬运
RAM→RAM DMA (GPDMA) GPDMA1 存储-存储搬运,与上面 memcpy 对比
Flash read 32-bit (CPU) CPU 顺序读片上 Flash
Flash→RAM DMA (GPDMA) DMA 从 Flash 搬到 RAM,与上面对比
Flash erase / program 擦除 32 个 8KB 扇区 + quad-word(16B) 编程 256KB(破坏性,仅上电跑一次),含读回校验

1、CubeMX设置

设置MCU使用外部晶振,MCU主频调整为250MHz。

MCU_Clock.png

使能GPDMA1的Ch0,用于测试RAM↔RAM、Flash→RAM的数据传输

GPDMA1_Ch0_Config.png

保存并生成代码

2、测试程序

片上RAM和Falsh的测试程序的框架如下

┌─────────────────────────────────────────────────────────────┐
│  应用层 (Application)                                         │
│    main()  ── 调度:Banner → Flash写(一次) → RAM/DMA/读(循环) │
└───────────────┬─────────────────────────────────────────────┘
│ 调用
┌───────────────▼─────────────────────────────────────────────┐
│  基准层 (Benchmark)  benchmark.c                              │
│    ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐  │
│    │ RAM 测试     │  │ DMA 测试     │  │ Flash 测试       │  │
│    │ 写(8/16/32b) │  │ RAM→RAM      │  │ 擦除/编程/校验   │  │
│    │ 读(32b)      │  │ Flash→RAM    │  │ 读(CPU)          │  │
│    │ memcpy       │  │              │  │                  │  │
│    └──────┬───────┘  └──────┬───────┘  └────────┬─────────┘  │
│           │ 计时            │ 完成回调           │ HAL        │
│    ┌──────▼─────────────────▼────────────────────▼─────────┐  │
│    │ DWT->CYCCNT 计时子系统  +  report() 串口格式化输出      │  │
│    └───────────────────────────────────────────────────────┘  │
└───────────────────────────┬─────────────────────────────────┘
│ 依赖
┌───────────────────────────▼─────────────────────────────────┐
│  HAL / CMSIS 驱动层 (来自 STM32Cube_FW_H5)                    │
│    GPIO · RCC · PWR · UART · DMA(GPDMA) · FLASH · ICACHE    │
└─────────────────────────────────────────────────────────────┘

测试过程的流程图如下。
Workflow.png

2.1 执行时间计算

使能 Cortex-M33 的 DWT 周期计数器,在250MHz主频下,计数器单个计数对应4ns。BENCH_Init中初始化DWT 周期计数器

/* ---- DWT cycle counter helpers ---- */
void BENCH_Init(void)
{
  CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
  DWT->CYCCNT = 0U;
  DWT->CTRL  |= DWT_CTRL_CYCCNTENA_Msk;
}

static inline uint32_t cycles(void)
{
  return DWT->CYCCNT;
}

2.2 Flash 测试区规划

应用程序的存测试相关的宏定义、测试函数的声明如下。

#include "stm32h5xx_hal.h"

/* ---- Platform constants (derived from STM32H5E5ZJ) ---- */
#define BENCH_CORE_HZ             (250000000UL)   /* SYSCLK = 250 MHz */

/* STM32H5 embedded Flash: 2 banks x 2 MB, 256 sectors/bank, 8 KB/sector,
   programmed by 16-byte quad-words. */
#define BENCH_FLASH_SECTOR_SIZE   (0x2000U)       /* 8 KB */
#define BENCH_FLASH_QUADWORD      (16U)           /* 16 bytes per program */
#define BENCH_FLASH_BASE          (0x08000000U)
#define BENCH_FLASH_TOTAL         (0x400000U)     /* 4 MB */
#define BENCH_FLASH_BANK2_BASE    (0x08200000U)

/* Non-destructive test region: last 256 KB of the embedded Flash (Bank2).
   The application image lives at the very beginning of Flash, so this region
   is untouched by the program. */
#define BENCH_FLASH_ADDR          (BENCH_FLASH_BASE + BENCH_FLASH_TOTAL - 0x40000U)
#define BENCH_FLASH_SIZE          (0x40000U)      /* 256 KB */

/* RAM working buffers (placed in .bss of internal SRAM, 1.5 MB available). */
#define BENCH_MAX_SIZE            (0x40000U)      /* 256 KB */

/* Block sizes exercised by the benchmark (bytes). All are multiples of 16 so
   that Flash quad-word programming stays naturally aligned. */
#define BENCH_BLOCK_COUNT         (4U)

/* ---- Public API ---- */
void BENCH_Init(void);                 /* enable DWT cycle counter */
void BENCH_PrintBanner(void);          /* print platform info */
void BENCH_RunRAM_DMARead(void);       /* RAM + DMA + Flash-read, non-destructive */
void BENCH_RunFlashWrite(void
参数 值 说明
Flash 总大小 4 MB (0x400000) 双 Bank,每 Bank 2 MB
Bank2 基址 0x08200000 测试区位于 Bank2 末尾
测试区起始 0x083C0000 片上 Flash 最后 256 KB
测试区大小 256 KB (0x40000) 不覆盖程序本体(程序在 Flash 起始处,仅 ~38 KB)
扇区大小 8 KB (0x2000) H5 Flash 按扇区擦除
起始扇区号 224 (0x083C0000 - 0x08200000) / 0x2000
擦除扇区数 32 256 KB / 8 KB,覆盖扇区 224~255
编程粒度 16 字节 (quad-word) H5 仅支持 FLASH_TYPEPROGRAM_QUADWORD

测试区放在 Flash 末尾而非起始,确保擦写不会破坏应用程序向量表与代码段。

2.3 基准测试模块

工作缓冲区为两个 256 KB 的全局数组(32 字节对齐,位于内部 SRAM 的 .bss 段):


/* ---- RAM working buffers (internal SRAM, 32-byte aligned) ---- */
static uint8_t src_buf[BENCH_MAX_SIZE] __attribute__((aligned(32)));
static uint8_t dst_buf[BENCH_MAX_SIZE] __attribute__((aligned(32)));

#define FLASH_WIN   ((const uint8_t *)BENCH_FLASH_ADDR)

/* Block sizes exercised (bytes), all multiples of 16 (Flash quad-word). */
static const uint32_t block_sizes[BENCH_BLOCK_COUNT] =
{
4U * 1024U, 16U * 1024U, 64U * 1024U, 256U * 1024U
};

/* Total bytes moved per non-destructive test (1 MiB) for fair comparison. */
#define BENCH_TOTAL_BYTES   (1024U * 1024U)

/* ---- DMA transfer synchronization ---- */
static volatile uint8_t  dma_done;
static volatile uint8_t  dma_error;

void dma_tc_callback(DMA_HandleTypeDef *hdma)
{
(void)hdma;
dma_done = 1U;
}
void dma_err_callback(DMA_HandleTypeDef *hdma)
{
(void)hdma;
dma_error = 1U;
dma_done  = 1U;
}


每个测试函数遵循统一模式:

  1. 预热 (warmup):先执行一次传输,使 Cache / 总线进入稳态;
  2. 计时:t0 = cycles() → 循环 iters 次 → cycles() - t0;
  3. 返回周期数(错误返回 0)。

2.4 破坏性测试(仅上电执行一次)

测试函数 操作
bench_flash_erase HAL_FLASHEx_Erase 擦除 Bank2 的 32 个扇区 (256 KB),测擦除耗时
bench_flash_program 循环 HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, ...) 写满 256 KB(16384 次 quad-word 编程),测编程耗时
flash_verify memcmp 读回 Flash 与 src_buf 比对,验证写入正确性
/* ========================================================================== */
/*  Flash erase + program (CPU, destructive, measured once over 256 KiB)      */
/* ========================================================================== */
static uint32_t bench_flash_erase(void)
{
  FLASH_EraseInitTypeDef erase = {0};
  uint32_t sector_error = 0xFFFFFFFFU;

  erase.TypeErase = FLASH_TYPEERASE_SECTORS;
  erase.Banks     = FLASH_BANK_2;
  erase.Sector    = (BENCH_FLASH_ADDR - BENCH_FLASH_BANK2_BASE) / BENCH_FLASH_SECTOR_SIZE;
  erase.NbSectors = BENCH_FLASH_SIZE / BENCH_FLASH_SECTOR_SIZE;

  HAL_FLASH_Unlock();
  uint32_t t0 = cycles();
  HAL_StatusTypeDef st = HAL_FLASHEx_Erase(&erase, §or_error);
  uint32_t cyc = cycles() - t0;
  HAL_FLASH_Lock();

  if (st != HAL_OK || sector_error != 0xFFFFFFFFU)
  {
    return 0U;   /* signal error */
  }
  return cyc;
}

static uint32_t bench_flash_program(void)
{
  const uint32_t n = BENCH_FLASH_SIZE / BENCH_FLASH_QUADWORD;
  uint32_t  flash_addr = BENCH_FLASH_ADDR;
  uint8_t  *psrc       = src_buf;

  HAL_FLASH_Unlock();
  uint32_t t0 = cycles();
  for (uint32_t i = 0; i < n; i++)
  {
    if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, flash_addr, (uint32_t)psrc) != HAL_OK)
    {
      HAL_FLASH_Lock();
      return 0U;
    }
    flash_addr += BENCH_FLASH_QUADWORD;
    psrc       += BENCH_FLASH_QUADWORD;
  }
  uint32_t cyc = cycles() - t0;
  HAL_FLASH_Lock();
  return cyc;
}

static int flash_verify(void)
{
  return (memcmp((const void *)BENCH_FLASH_ADDR, src_buf, BENCH_FLASH_SIZE) == 0) ? 1 : 0;
}

2.5 非破坏性测试(每轮固定搬运 1 MB)

测试块大小集合:{4 KB, 16 KB, 64 KB, 256 KB},迭代次数 iters = 1 MB / block,保证不同块大小下总数据量相同,结果可直接对比。

测试函数 操作 对比意义
bench_ram_write_wide (width=4/2/1) CPU 以 32/16/8-bit 顺序写 dst_buf 位宽对 SRAM 写吞吐的影响
bench_ram_read32 CPU 以 32-bit 顺序读 src_buf 并累加(防编译器优化) SRAM 读吞吐
bench_memcpy newlib 库 memcpy(dst, src, bytes) CPU 搬运基线
bench_dma_m2m GPDMA src_buf → dst_buf DMA vs CPU memcpy 的加速比
bench_flash_read32 CPU 以 32-bit 顺序读 Flash 测试区 Flash 读吞吐(CPU)
bench_dma_flash2ram GPDMA Flash → dst_buf DMA vs CPU 读 Flash 的加速比

bench_ram_write_wide 、bench_ram_read32以及 bench_memcpy用于CPU访问RAM的读写速度测试,代码如下

/* ========================================================================== */
/*  RAM access (CPU)                                                           */
/* ========================================================================== */
static uint32_t bench_ram_write_wide(uint32_t bytes, uint32_t iters, uint32_t width)
{
  /* warmup */
  if (width == 4U)
  {
    volatile uint32_t *p = (volatile uint32_t *)dst_buf;
    uint32_t n = bytes / 4U;
    for (uint32_t i = 0; i < n; i++) { p[i] = i; }
  }
  else if (width == 2U)
  {
    volatile uint16_t *p = (volatile uint16_t *)dst_buf;
    uint32_t n = bytes / 2U;
    for (uint32_t i = 0; i < n; i++) { p[i] = (uint16_t)i; }
  }
  else
  {
    volatile uint8_t *p = (volatile uint8_t *)dst_buf;
    for (uint32_t i = 0; i < bytes; i++) { p[i] = (uint8_t)i; }
  }

  uint32_t t0 = cycles();
  if (width == 4U)
  {
    volatile uint32_t *p = (volatile uint32_t *)dst_buf;
    uint32_t n = bytes / 4U;
    for (uint32_t k = 0; k < iters; k++)
      for (uint32_t i = 0; i < n; i++) { p[i] = i; }
  }
  else if (width == 2U)
  {
    volatile uint16_t *p = (volatile uint16_t *)dst_buf;
    uint32_t n = bytes / 2U;
    for (uint32_t k = 0; k < iters; k++)
      for (uint32_t i = 0; i < n; i++) { p[i] = (uint16_t)i; }
  }
  else
  {
    volatile uint8_t *p = (volatile uint8_t *)dst_buf;
    for (uint32_t k = 0; k < iters; k++)
      for (uint32_t i = 0; i < bytes; i++) { p[i] = (uint8_t)i; }
  }
  return cycles() - t0;
}

static uint32_t bench_ram_read32(uint32_t bytes, uint32_t iters)
{
  volatile uint32_t sink = 0;
  const volatile uint32_t *p = (const volatile uint32_t *)src_buf;
  uint32_t n = bytes / 4U;
  (void)sink;

  uint32_t t0 = cycles();
  for (uint32_t k = 0; k < iters; k++)
    for (uint32_t i = 0; i < n; i++) { sink += p[i]; }
  return cycles() - t0;
}

/* ---- CPU memcpy (newlib) ---- */
static uint32_t bench_memcpy(uint32_t bytes, uint32_t iters)
{
  memcpy(dst_buf, src_buf, bytes);                 /* warmup */
  uint32_t t0 = cycles();
  for (uint32_t k = 0; k < iters; k++)
  {
    memcpy(dst_buf, src_buf, bytes);
  }
  return cycles() - t0;
}

bench_dma_m2m用于测试使用GPDMA在RAM之间进行数据搬运的效果

/* ========================================================================== */
/*  GPDMA memory-to-memory (src_buf -> dst_buf)                                */
/* ========================================================================== */

/* GPDMA single-block transfer is limited to 65535 bytes: the block-length
 * field CBR1.BNDT is only 16 bits. Any length >= 65536 gets truncated to 0
 * and the channel reports an error. Chunk big transfers into sub-blocks that
 * stay <= 65520 bytes (largest multiple of 16 below 65536, keeping burst=4
 * word alignment). The whole block (all chunks) is timed end-to-end. */
#define DMA_MAX_CHUNK  (65520U)   /* 0xFFF0 */

static int dma_xfer(uint32_t src, uint32_t dst, uint32_t bytes)
{
  uint32_t off = 0U;
  while (off < bytes)
  {
    uint32_t chunk = bytes - off;
    if (chunk > DMA_MAX_CHUNK) { chunk = DMA_MAX_CHUNK; }

    dma_done  = 0U;
    dma_error = 0U;
    if (HAL_DMA_Start_IT(&handle_GPDMA1_Channel0, src + off, dst + off, chunk) != HAL_OK)
    {
      return 0;
    }
    while (!dma_done) { }
    if (dma_error) { return 0; }

    off += chunk;
  }
  return 1;
}

static uint32_t bench_dma_m2m(uint32_t bytes, uint32_t iters)
{
  /* warmup */
  if (!dma_xfer((uint32_t)src_buf, (uint32_t)dst_buf, bytes)) { return 0U; }

  uint32_t t0 = cycles();
  for (uint32_t k = 0; k < iters; k++)
  {
    if (!dma_xfer((uint32_t)src_buf, (uint32_t)dst_buf, bytes)) { return 0U; }
  }
  return cycles() - t0;
}

bench_flash_read32、bench_dma_flash2ram测试Flash在CPU读取和DMA读取是的速度。

/* ========================================================================== */
/*  Flash read (CPU)                                                           */
/* ========================================================================== */
static uint32_t bench_flash_read32(uint32_t bytes, uint32_t iters)
{
  volatile uint32_t sink = 0;
  const volatile uint32_t *p = (const volatile uint32_t *)FLASH_WIN;
  uint32_t n = bytes / 4U;
  (void)sink;

  uint32_t t0 = cycles();
  for (uint32_t k = 0; k < iters; k++)
    for (uint32_t i = 0; i < n; i++) { sink += p[i]; }
  return cycles() - t0;
}

/* ---- GPDMA Flash -> RAM ---- */
static uint32_t bench_dma_flash2ram(uint32_t bytes, uint32_t iters)
{
  /* warmup */
  if (!dma_xfer((uint32_t)FLASH_WIN, (uint32_t)dst_buf, bytes)) { return 0U; }

  uint32_t t0 = cycles();
  for (uint32_t k = 0; k < iters; k++)
  {
    if (!dma_xfer((uint32_t)FLASH_WIN, (uint32_t)dst_buf, bytes)) { return 0U; }
  }
  return cycles() - t0;
}

2.6 测试信息输出

通过调用测试函数,将测试结果通过串口发送到终端显示,以下为函数调用的具体实现。

void BENCH_PrintBanner(void)
{
  printf("\r\n");
  printf("==============================================================\r\n");
  printf(" STM32H5E5ZJ  Flash / RAM / DMA  Performance Benchmark\r\n");
  printf(" Core: Cortex-M33 @ %lu MHz   |   4 MB Flash   1.5 MB SRAM\r\n",
         (unsigned long)(BENCH_CORE_HZ / 1000000UL));
  printf(" Timer: DWT->CYCCNT (1 tick = 4 ns)\r\n");
  printf(" UART:  USART3 PD8/PD9 115200 8N1 (ST-LINK VCP)\r\n");
  printf(" Flash test window: 0x%08lX - 0x%08lX (%lu KiB, Bank2)\r\n",
         (unsigned long)BENCH_FLASH_ADDR,
         (unsigned long)(BENCH_FLASH_ADDR + BENCH_FLASH_SIZE - 1U),
         (unsigned long)(BENCH_FLASH_SIZE / 1024U));
  printf(" Non-destructive tests move 1 MiB per row.\r\n");
  printf("==============================================================\r\n");
}

void BENCH_RunFlashWrite(void)
{
  printf("\r\n----- Flash write test (DESTRUCTIVE, runs once) -----\r\n");

  fill_src(BENCH_FLASH_SIZE);

  uint32_t cyc_e = bench_flash_erase();
  if (cyc_e == 0U)
  {
    report_error("Flash erase (32x8K sector)");
  }
  else
  {
    report("Flash erase (32x8K sector)", BENCH_FLASH_SIZE, 1U, cyc_e);
  }

  uint32_t cyc_p = bench_flash_program();
  if (cyc_p == 0U)
  {
    report_error("Flash program (quad-word)");
  }
  else
  {
    report("Flash program (quad-word)", BENCH_FLASH_SIZE, 1U, cyc_p);
  }

  if (flash_verify())
  {
    printf("Flash read-back verify: PASSED (%lu KiB)\r\n",
           (unsigned long)(BENCH_FLASH_SIZE / 1024U));
  }
  else
  {
    printf("Flash read-back verify: FAILED\r\n");
  }
}

void BENCH_RunRAM_DMARead(void)
{
  printf("\r\n----- RAM / DMA / Flash-read test (non-destructive) -----\r\n");
  printf("%-26s | %7s x%3s = %9s | %10s     | %9s     | %7s\r\n",
         "Test", "Block", "It", "Total B", "cycles", "us", "MB/s");
  printf("---------------------------+--------------------------+-----------------+--------------+---------\r\n");

  fill_src(BENCH_MAX_SIZE);

  for (uint32_t b = 0; b < BENCH_BLOCK_COUNT; b++)
  {
    uint32_t bytes = block_sizes[b];
    uint32_t iters = BENCH_TOTAL_BYTES / bytes;
    uint32_t cyc;

    cyc = bench_ram_write_wide(bytes, iters, 4U);
    if (cyc) report("RAM write 32-bit (CPU)", bytes, iters, cyc); else report_error("RAM write 32-bit (CPU)");

    cyc = bench_ram_write_wide(bytes, iters, 2U);
    if (cyc) report("RAM write 16-bit (CPU)", bytes, iters, cyc); else report_error("RAM write 16-bit (CPU)");

    cyc = bench_ram_write_wide(bytes, iters, 1U);
    if (cyc) report("RAM write 8-bit (CPU)", bytes, iters, cyc); else report_error("RAM write 8-bit (CPU)");

    cyc = bench_ram_read32(bytes, iters);
    if (cyc) report("RAM read 32-bit (CPU)", bytes, iters, cyc); else report_error("RAM read 32-bit (CPU)");

    cyc = bench_memcpy(bytes, iters);
    if (cyc) report("RAM memcpy (CPU libc)", bytes, iters, cyc); else report_error("RAM memcpy (CPU libc)");

    cyc = bench_dma_m2m(bytes, iters);
    if (cyc) report("RAM->RAM DMA (GPDMA)", bytes, iters, cyc); else report_error("RAM->RAM DMA (GPDMA)");

    cyc = bench_flash_read32(bytes, iters);
    if (cyc) report("Flash read 32-bit (CPU)", bytes, iters, cyc); else report_error("Flash read 32-bit (CPU)");

    cyc = bench_dma_flash2ram(bytes, iters);
    if (cyc) report("Flash->RAM DMA (GPDMA)", bytes, iters, cyc); else report_error("Flash->RAM DMA (GPDMA)");

    printf("\r\n");
  }
}

主函数中调用接口函数,进行RAM和Flash的Benchmark测试。

BENCH_Init();
  BENCH_PrintBanner();

  /* Run the destructive Flash write test once after reset. */
  BENCH_RunFlashWrite();
  BENCH_RunRAM_DMARead();

编译烧录程序到开发板,测试结果如下

DMA_fix.png

赞 收藏 评论0 发布时间:2026-11-15 18:10

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版