偶尔看到一篇内存性能测试工具,是用在linux系统下的,以下是相关介绍的摘抄:
memtester是Simon Kirby在1999年编写的测试程序(v1版),后来由Charles Cazabon一直维护更新(v2及之后版本),主要面向Unix-like系统,官方主页上介绍的是“A userspace utility for testing the memory subsystem for faults.”,其实就是为了测试内存(主要DDR)的读写访问可靠性(仅正确性,与速度性能无关),这是验证板级硬件设备必不可少的一项测试。
整个memtester测试的视角就是从用户的角度来看的,从用户角度设立不同的测试场景即测试用例,然后针对性地进行功能测试,注意是从系统级来测试,也就是说关注的不单单是内存颗粒了,还有系统板级的连线、IO性能、PCB等等相关的因素,在这些因素的影响下,内存是否还能正常工作。
memtester程序的最新版本是4.5.0,早期的v1/v2/v3版本目前下载不到了,Charles Cazabon重写了程序并发布了全新v4.0.0,此后一直不定期更新,v4.x也是当前最流行的版本。
核心程序下载:http://pyropus.ca/software/memtester/
核心程序包下载后,在\memtester-4.5.0\下可找到源代码。
因为手上用了一块STM32H750核心板,板载16M * 32bit,共64MByte SDRAM,为了验证可靠性,所以准备移植过来看看。
下载好,解压到文件夹:
真正有用的文件,主要是tests.c和memtester.c
tests.c 里面有具体的功能测试函数
memtester.c 主要是和平台相关的一些参数,还有测试函数的调用
为了方便,去掉一大堆和平台相关的,替换随机数产生函数,和打印函数,编译下载,去掉几个兼容性BUG,就可以顺利运行了,参考以下代码:
/** memtester version 4** Very simple but very effective user-space memory tester.* Originally by Simon Kirby <sim@> <sim@>* Version 2 by Charles Cazabon <charlesc-memtester@pyropus.ca>* Version 3 not publicly released.* Version 4 rewrite:* Copyright (C) - Charles Cazabon <charlesc-memtester@pyropus.ca>* Licensed under the terms of the GNU General Public License version 2 (only).* See the file COPYING for details.**/#define __version__ "4.5.0"#include <stddef.h>#include <stdlib.h>#include <stdio.h>#include <stdint.h>#include <string.h>#include <limits.h>//#define TEST_NARROW_WRITEStypedef unsigned long ul;typedef unsigned long long ull;typedef unsigned long volatile ulv;typedef unsigned char volatile u8v;typedef unsigned short volatile u16v;uint32_t rand32_hw_get(void);#define rand32() rand32_hw_get()#define rand_ul() rand32()#define UL_ONEBITS 0xffffffff#define UL_LEN 32#define CHECKERBOARD1 0x55555555#define CHECKERBOARD2 0xaaaaaaaa#define UL_BYTE(x) ((x | x << 8 | x << 16 | x << 24))struct test {char *name;int (*fp)();};#define EXIT_FAIL_NONSTARTER 0x01#define EXIT_FAIL_ADDRESSLINES 0x02#define EXIT_FAIL_OTHERTEST0x04int test_stuck_address(unsigned long volatile *bufa, size_t count);int test_random_value(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_xor_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_sub_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_mul_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_div_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_or_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_and_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_seqinc_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_solidbits_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_checkerboard_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_blockseq_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_walkbits0_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_walkbits1_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_bitspread_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_bitflip_comparison(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);#ifdef TEST_NARROW_WRITES int test_8bit_wide_random(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);int test_16bit_wide_random(unsigned long volatile *bufa, unsigned long volatile *bufb, size_t count);#endifstruct test tests[] = {{"Random Value", test_random_value },{"Compare XOR",test_xor_comparison },{"Compare SUB",test_sub_comparison },{"Compare MUL",test_mul_comparison },{"Compare DIV",test_div_comparison },{"Compare OR", test_or_comparison },{"Compare AND",test_and_comparison },{"Sequential Increment", test_seqinc_comparison },{"Solid Bits", test_solidbits_comparison },{"Block Sequential",test_blockseq_comparison },{"Checkerboard", test_checkerboard_comparison },{"Bit Spread", test_bitspread_comparison },{"Bit Flip", test_bitflip_comparison },{"Walking Ones", test_walkbits1_comparison },{"Walking Zeroes", test_walkbits0_comparison },#ifdef TEST_NARROW_WRITES {"8-bit Writes", test_8bit_wide_random },{"16-bit Writes", test_16bit_wide_random },#endif{NULL, NULL }};#define printf rt_kprintf#define fflush(stdout)#include "stm32h7xx_ll_bus.h"#include "stm32h7xx_ll_rng.h"uint32_t rand32_hw_get(void){while (!LL_RNG_IsActiveFlag_DRDY(RNG));return LL_RNG_ReadRandData32(RNG);}void rand32_hw_init(void){LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_RNG);LL_AHB2_GRP1_ForceReset(LL_AHB2_GRP1_PERIPH_RNG);LL_AHB2_GRP1_ReleaseReset(LL_AHB2_GRP1_PERIPH_RNG);LL_RNG_Enable(RNG);}#ifdef _SC_PAGE_SIZEint memtester_pagesize(void) {int pagesize = sysconf(_SC_PAGE_SIZE);if (pagesize == -1) {perror("get page size failed");exit(EXIT_FAIL_NONSTARTER);}printf("pagesize is %ld\n", (long) pagesize);return pagesize;}#elseint memtester_pagesize(void) {printf("sysconf(_SC_PAGE_SIZE) not supported; using pagesize of 8192\n");return 8192;}#endif/* Global vars - so tests have access to this information */int use_phys = 0;off_t physaddrbase = 0;void sdram_tester(void *aligned, uint32_t bufsize){ul loops, loop, i;size_t halflen, count;ulv *bufa, *bufb;int exit_code = 0;ul testmask = 0;rand32_hw_init();printf("memtester version " __version__ " (%d-bit)\n", UL_LEN);printf("Copyright (C) 2001- Charles Cazabon.\n");printf("Licensed under the GNU General Public License version 2 (only).\n");printf("\n");halflen = bufsize / 2;count = halflen / sizeof(ul);bufa = (ulv *) aligned;bufb = (ulv *) ((size_t) aligned + halflen);loops = 1;for(loop=1; ((!loops) || loop <= loops); loop++) {printf("Loop %lu", loop);if (loops) {printf("/%lu", loops);}printf(":\n");printf(" %-20s: ", "Stuck Address");fflush(stdout);if (!test_stuck_address(aligned, bufsize / sizeof(ul))) {printf("ok\n");} else {exit_code |= EXIT_FAIL_ADDRESSLINES;}for (i=0;;i++) {if (!tests[i].name) break;/* If using a custom testmask, only run this test if thebit corresponding to this test was set by the user.*/if (testmask && (!((1 << i) & testmask))) {continue;}printf(" %-20s: ", tests[i].name);if (!tests[i].fp(bufa, bufb, count)) {printf("ok\n");} else {exit_code |= EXIT_FAIL_OTHERTEST;}fflush(stdout);}printf("\n");fflush(stdout);}printf("Done.\n");fflush(stdout);}char progress[] = "-\\|/";#define PROGRESSLEN 4#define PROGRESSOFTEN 2500#define ONE 0x00000001Lunion {unsigned char bytes[UL_LEN/8];ul val;} mword8;union {unsigned short u16s[UL_LEN/16];ul val;} mword16;/* Function definitions. */int compare_regions(ulv *bufa, ulv *bufb, size_t count) {int r = 0;size_t i;ulv *p1 = bufa;ulv *p2 = bufb;ul physaddr;for (i = 0; i < count; i++, p1++, p2++) {if (*p1 != *p2) {if (use_phys) {physaddr = physaddrbase + (i * sizeof(ul));fprintf(stderr, "FAILURE: 0x%08lx != 0x%08lx at physical address ""0x%08lx.\n", (ul) *p1, (ul) *p2, physaddr);} else {fprintf(stderr, "FAILURE: 0x%08lx != 0x%08lx at offset 0x%08lx.\n", (ul) *p1, (ul) *p2, (ul) (i * sizeof(ul)));}/* printf("Skipping to next test..."); */r = -1;}}return r;}int test_stuck_address(ulv *bufa, size_t count) {ulv *p1 = bufa;unsigned int j;size_t i;ul physaddr;printf(" ");fflush(stdout);for (j = 0; j < 16; j++) {printf("\b\b\b\b\b\b\b\b\b\b\b");p1 = (ulv *) bufa;printf("setting %3u", j);fflush(stdout);for (i = 0; i < count; i++) {*p1 = ((j + i) % 2) == 0 ? (ul) p1 : ~((ul) p1);*p1++;}printf("\b\b\b\b\b\b\b\b\b\b\b");printf("testing %3u", j);fflush(stdout);p1 = (ulv *) bufa;for (i = 0; i < count; i++, p1++) {if (*p1 != (((j + i) % 2) == 0 ? (ul) p1 : ~((ul) p1))) {if (use_phys) {physaddr = physaddrbase + (i * sizeof(ul));fprintf(stderr, "FAILURE: possible bad address line at physical ""address 0x%08lx.\n", physaddr);} else {fprintf(stderr, "FAILURE: possible bad address line at offset ""0x%08lx.\n", (ul) (i * sizeof(ul)));}printf("Skipping to next test...\n");fflush(stdout);return -1;}}}printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b");fflush(stdout);return 0;}int test_random_value(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;ul j = 0;size_t i;printf("%c", ' ');fflush(stdout);for (i = 0; i < count; i++) {*p1++ = *p2++ = rand_ul();if (!(i % PROGRESSOFTEN)) {printf("%c", '\b');printf("%c", progress[++j % PROGRESSLEN]);fflush(stdout);}}printf("\b \b");fflush(stdout);return compare_regions(bufa, bufb, count);}int test_xor_comparison(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;size_t i;ul q = rand_ul();for (i = 0; i < count; i++) {*p1++ ^= q;*p2++ ^= q;}return compare_regions(bufa, bufb, count);}int test_sub_comparison(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;size_t i;ul q = rand_ul();for (i = 0; i < count; i++) {*p1++ -= q;*p2++ -= q;}return compare_regions(bufa, bufb, count);}int test_mul_comparison(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;size_t i;ul q = rand_ul();for (i = 0; i < count; i++) {*p1++ *= q;*p2++ *= q;}return compare_regions(bufa, bufb, count);}int test_div_comparison(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;size_t i;ul q = rand_ul();for (i = 0; i < count; i++) {if (!q) {q++;}*p1++ /= q;*p2++ /= q;}return compare_regions(bufa, bufb, count);}int test_or_comparison(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;size_t i;ul q = rand_ul();for (i = 0; i < count; i++) {*p1++ |= q;*p2++ |= q;}return compare_regions(bufa, bufb, count);}int test_and_comparison(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;size_t i;ul q = rand_ul();for (i = 0; i < count; i++) {*p1++ &= q;*p2++ &= q;}return compare_regions(bufa, bufb, count);}int test_seqinc_comparison(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;size_t i;ul q = rand_ul();for (i = 0; i < count; i++) {*p1++ = *p2++ = (i + q);}return compare_regions(bufa, bufb, count);}int test_solidbits_comparison(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;unsigned int j;ul q;size_t i;printf(" ");fflush(stdout);for (j = 0; j < 64; j++) {printf("\b\b\b\b\b\b\b\b\b\b\b");q = (j % 2) == 0 ? UL_ONEBITS : 0;printf("setting %3u", j);fflush(stdout);p1 = (ulv *) bufa;p2 = (ulv *) bufb;for (i = 0; i < count; i++) {*p1++ = *p2++ = (i % 2) == 0 ? q : ~q;}printf("\b\b\b\b\b\b\b\b\b\b\b");printf("testing %3u", j);fflush(stdout);if (compare_regions(bufa, bufb, count)) {return -1;}}printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b");fflush(stdout);return 0;}int test_checkerboard_comparison(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;unsigned int j;ul q;size_t i;printf(" ");fflush(stdout);for (j = 0; j < 64; j++) {printf("\b\b\b\b\b\b\b\b\b\b\b");q = (j % 2) == 0 ? CHECKERBOARD1 : CHECKERBOARD2;printf("setting %3u", j);fflush(stdout);p1 = (ulv *) bufa;p2 = (ulv *) bufb;for (i = 0; i < count; i++) {*p1++ = *p2++ = (i % 2) == 0 ? q : ~q;}printf("\b\b\b\b\b\b\b\b\b\b\b");printf("testing %3u", j);fflush(stdout);if (compare_regions(bufa, bufb, count)) {return -1;}}printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b");fflush(stdout);return 0;}int test_blockseq_comparison(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;unsigned int j;size_t i;printf(" ");fflush(stdout);for (j = 0; j < 256; j++) {printf("\b\b\b\b\b\b\b\b\b\b\b");p1 = (ulv *) bufa;p2 = (ulv *) bufb;printf("setting %3u", j);fflush(stdout);for (i = 0; i < count; i++) {*p1++ = *p2++ = (ul) UL_BYTE(j);}printf("\b\b\b\b\b\b\b\b\b\b\b");printf("testing %3u", j);fflush(stdout);if (compare_regions(bufa, bufb, count)) {return -1;}}printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b");fflush(stdout);return 0;}int test_walkbits0_comparison(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;unsigned int j;size_t i;printf(" ");fflush(stdout);for (j = 0; j < UL_LEN * 2; j++) {printf("\b\b\b\b\b\b\b\b\b\b\b");p1 = (ulv *) bufa;p2 = (ulv *) bufb;printf("setting %3u", j);fflush(stdout);for (i = 0; i < count; i++) {if (j < UL_LEN) {/* Walk it up. */*p1++ = *p2++ = ONE << j;} else {/* Walk it back down. */*p1++ = *p2++ = ONE << (UL_LEN * 2 - j - 1);}}printf("\b\b\b\b\b\b\b\b\b\b\b");printf("testing %3u", j);fflush(stdout);if (compare_regions(bufa, bufb, count)) {return -1;}}printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b");fflush(stdout);return 0;}int test_walkbits1_comparison(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;unsigned int j;size_t i;printf(" ");fflush(stdout);for (j = 0; j < UL_LEN * 2; j++) {printf("\b\b\b\b\b\b\b\b\b\b\b");p1 = (ulv *) bufa;p2 = (ulv *) bufb;printf("setting %3u", j);fflush(stdout);for (i = 0; i < count; i++) {if (j < UL_LEN) {/* Walk it up. */*p1++ = *p2++ = UL_ONEBITS ^ (ONE << j);} else {/* Walk it back down. */*p1++ = *p2++ = UL_ONEBITS ^ (ONE << (UL_LEN * 2 - j - 1));}}printf("\b\b\b\b\b\b\b\b\b\b\b");printf("testing %3u", j);fflush(stdout);if (compare_regions(bufa, bufb, count)) {return -1;}}printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b");fflush(stdout);return 0;}int test_bitspread_comparison(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;unsigned int j;size_t i;printf(" ");fflush(stdout);for (j = 0; j < UL_LEN * 2; j++) {printf("\b\b\b\b\b\b\b\b\b\b\b");p1 = (ulv *) bufa;p2 = (ulv *) bufb;printf("setting %3u", j);fflush(stdout);for (i = 0; i < count; i++) {if (j < UL_LEN) {/* Walk it up. */*p1++ = *p2++ = (i % 2 == 0)? (ONE << j) | (ONE << (j + 2)): UL_ONEBITS ^ ((ONE << j)| (ONE << (j + 2)));} else {/* Walk it back down. */*p1++ = *p2++ = (i % 2 == 0)? (ONE << (UL_LEN * 2 - 1 - j)) | (ONE << (UL_LEN * 2 + 1 - j)): UL_ONEBITS ^ (ONE << (UL_LEN * 2 - 1 - j)| (ONE << (UL_LEN * 2 + 1 - j)));}}printf("\b\b\b\b\b\b\b\b\b\b\b");printf("testing %3u", j);fflush(stdout);if (compare_regions(bufa, bufb, count)) {return -1;}}printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b");fflush(stdout);return 0;}int test_bitflip_comparison(ulv *bufa, ulv *bufb, size_t count) {ulv *p1 = bufa;ulv *p2 = bufb;unsigned int j, k;ul q;size_t i;printf(" ");fflush(stdout);for (k = 0; k < UL_LEN; k++) {q = ONE << k;for (j = 0; j < 8; j++) {printf("\b\b\b\b\b\b\b\b\b\b\b");q = ~q;printf("setting %3u", k * 8 + j);fflush(stdout);p1 = (ulv *) bufa;p2 = (ulv *) bufb;for (i = 0; i < count; i++) {*p1++ = *p2++ = (i % 2) == 0 ? q : ~q;}printf("\b\b\b\b\b\b\b\b\b\b\b");printf("testing %3u", k * 8 + j);fflush(stdout);if (compare_regions(bufa, bufb, count)) {return -1;}}}printf("\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b");fflush(stdout);return 0;}#ifdef TEST_NARROW_WRITES int test_8bit_wide_random(ulv* bufa, ulv* bufb, size_t count) {u8v *p1, *t;ulv *p2;int attempt;unsigned int b, j = 0;size_t i;printf("%c", ' ');fflush(stdout);for (attempt = 0; attempt < 2; attempt++) {if (attempt & 1) {p1 = (u8v *) bufa;p2 = bufb;} else {p1 = (u8v *) bufb;p2 = bufa;}for (i = 0; i < count; i++) {t = mword8.bytes;*p2++ = mword8.val = rand_ul();for (b=0; b < UL_LEN/8; b++) {*p1++ = *t++;}if (!(i % PROGRESSOFTEN)) {printf("%c", '\b');printf("%c", progress[++j % PROGRESSLEN]);fflush(stdout);}}if (compare_regions(bufa, bufb, count)) {return -1;}}printf("\b \b");fflush(stdout);return 0;}int test_16bit_wide_random(ulv* bufa, ulv* bufb, size_t count) {u16v *p1, *t;ulv *p2;int attempt;unsigned int b, j = 0;size_t i;printf("%c", ' ' );fflush( stdout );for (attempt = 0; attempt < 2; attempt++) {if (attempt & 1) {p1 = (u16v *) bufa;p2 = bufb;} else {p1 = (u16v *) bufb;p2 = bufa;}for (i = 0; i < count; i++) {t = mword16.u16s;*p2++ = mword16.val = rand_ul();for (b = 0; b < UL_LEN/16; b++) {*p1++ = *t++;}if (!(i % PROGRESSOFTEN)) {printf("%c", '\b');printf("%c", progress[++j % PROGRESSLEN]);fflush(stdout);}}if (compare_regions(bufa, bufb, count)) {return -1;}}printf("\b \b");fflush(stdout);return 0;}#endif
下面是测试结果截图
没去具体研究测试函数的机理,这些比较偏硬件和理论
看起来应该有效果,64MB的SDRAM,测试耗时约3~5分钟