- Thu Mar 10, 2022 2:26 pm
#93878
I have made some small changes to the code.
[list=]
[*]Your typedef for the datum was not working for some reason so i just omitted it.
[*]The builtin LED now blinks every newline.
[*]ESP32 RAM size was added.
[*]The dot and newline dividers can be adjusted bet MCU to try and speed up testing.
[/list]
Otherwise very nice. Thank you.
My Sketch:
Code: Select all/*************************************************************************
20161129 DdelV
Can ESP8266/ATmega328p RAM be tested?
Apparently so, if one is careful!
This code was written (and cribbed) to test ESP8266 RAM
Also works on Arduino chips, if there's a UART.
Allocate (just about) the largest chunk of
RAM that malloc() can be made to return, and tests that
(or most of it).
Works! (occasionally stack dumps on ESP8266)
*/
int stackBot()
{
int v; // declare a dynamic variable
return (int) &v; // return it's address, that's stack bottom (right now..)
}
/**********************************************************************
The following (3) routines are from:
http://www.barrgroup.com/Embedded-Systems/How-To/Memory-Test-Suite-C
**********************************************************************/
/**********************************************************************
Function: memTestDataBus()
Description: Test the data bus wiring in a memory region by
performing a walking 1's test at a fixed address
within that region. The address (and hence the
memory region) is selected by the caller.
Notes:
Returns: 0 if the test succeeds.
A non-zero result is the first pattern that failed.
**********************************************************************/
unsigned char memTestDataBus(volatile unsigned char * address)
{
unsigned char pattern;
/*
Perform a walking 1's test at the given address.
*/
for (pattern = 1; pattern != 0; pattern <<= 1)
{
/*
Write the test pattern.
*/
*address = pattern;
/*
Read it back (immediately is okay for this test).
*/
if (*address != pattern)
{
return (pattern);
}
}
return (0);
} /* memTestDataBus() */
/**********************************************************************
Function: memTestAddressBus()
Description: Test the address bus wiring in a memory region by
performing a walking 1's test on the relevant bits
of the address and checking for aliasing. This test
will find single-bit address failures such as stuck
-high, stuck-low, and shorted pins. The base address
and size of the region are selected by the caller.
Notes: For best results, the selected base address should
have enough LSB 0's to guarantee single address bit
changes. For example, to test a 64-Kbyte region,
select a base address on a 64-Kbyte boundary. Also,
select the region size as a power-of-two--if at all
possible.
Returns: NULL if the test succeeds.
A non-zero result is the first address at which an
aliasing problem was uncovered. By examining the
contents of memory, it may be possible to gather
additional information about the problem.
**********************************************************************/
unsigned char * memTestAddressBus(volatile unsigned char * baseAddress, unsigned long nBytes)
{
unsigned long addressMask = (nBytes / sizeof(unsigned char) - 1);
unsigned long offset;
unsigned long testOffset;
unsigned char pattern = (unsigned char) 0xAAAAAAAA;
unsigned char antipattern = (unsigned char) 0x55555555;
/*
Write the default pattern at each of the power-of-two offsets.
*/
for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
{
baseAddress[offset] = pattern;
}
/*
Check for address bits stuck high.
*/
testOffset = 0;
baseAddress[testOffset] = antipattern;
for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
{
if (baseAddress[offset] != pattern)
{
return ((unsigned char *) &baseAddress[offset]);
}
}
baseAddress[testOffset] = pattern;
/*
Check for address bits stuck low or shorted.
*/
for (testOffset = 1; (testOffset & addressMask) != 0; testOffset <<= 1)
{
baseAddress[testOffset] = antipattern;
if (baseAddress[0] != pattern)
{
return ((unsigned char *) &baseAddress[testOffset]);
}
for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
{
if ((baseAddress[offset] != pattern) && (offset != testOffset))
{
return ((unsigned char *) &baseAddress[testOffset]);
}
}
baseAddress[testOffset] = pattern;
}
return (NULL);
} /* memTestAddressBus() */
/**********************************************************************
Function: memTestDevice()
Description: Test the integrity of a physical memory device by
performing an increment/decrement test over the
entire region. In the process every storage bit
in the device is tested as a zero and a one. The
base address and the size of the region are
selected by the caller.
Notes:
Returns: NULL if the test succeeds.
A non-zero result is the first address at which an
incorrect value was read back. By examining the
contents of memory, it may be possible to gather
additional information about the problem.
**********************************************************************/
unsigned char * memTestDevice(volatile unsigned char * baseAddress, unsigned long nBytes)
{
unsigned long offset;
unsigned long nWords = nBytes / sizeof(unsigned char);
unsigned char pattern;
unsigned char antipattern;
/*
Fill memory with a known pattern.
*/
for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
{
baseAddress[offset] = pattern;
}
/*
Check each location and invert it for the second pass.
*/
for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
{
if (baseAddress[offset] != pattern)
{
return ((unsigned char *) &baseAddress[offset]);
}
antipattern = ~pattern;
baseAddress[offset] = antipattern;
}
/*
Check each location for the inverted pattern and zero it.
*/
for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
{
antipattern = ~pattern;
if (baseAddress[offset] != antipattern)
{
return ((unsigned char *) &baseAddress[offset]);
}
}
return (NULL);
} /* memTestDevice() */
// select an impossible RAM size based on processor
// (choosing too large a value confuses malloc() on Atmel chip.)
#if defined (__AVR_ATmega328P__)
unsigned int fR = 2048; // 100% of physical RAM
#define DOT_DIV 1
#define NL_DIV 128
#elif defined (ESP8266)
unsigned long fR = 81920L; // ESP8266
#define DOT_DIV 4
#define NL_DIV 512
#elif defined (ESP32)
unsigned long fR = 327680L; // ESP32
#ifndef LED_BUILTIN //Is not defined by default so....
#define LED_BUILTIN 2
#endif
#define DOT_DIV 4
#define NL_DIV 512
#else
#error Uh sorry I have no idea what MCU this is compiling for... Please add it around line 230 together with its RAM size.
#endif
void *hT; // these are global (static) because shared between setup() & loop()
// following constant tuned via trial & error.
const int callStack = 20; // allows called test routines to have an absolutely minimal stackframe.
//uint32_t pass = 0;
static bool led_state = false;
void setup() {
Serial.begin(1000000);
pinMode(LED_BUILTIN, OUTPUT);
// fR begins very large
while ((hT = malloc(fR)) == NULL) {
fR -= 32; // gradually get real
}
Serial.println();
Serial.print(fR, DEC);
Serial.print(F(" (0x"));
Serial.print(fR, HEX);
Serial.println(F(") bytes of RAM free."));
Serial.print(F("Heap top is 0x"));
Serial.println((int)hT, HEX);
Serial.print(F("Stack bottom is 0x"));
Serial.println(stackBot(), HEX);
Serial.print(F("Testing 0x"));
Serial.print(fR - callStack, HEX); // alloc size less stackframe
Serial.print(F(" bytes from 0x"));
Serial.print((int)hT, HEX); // base of RAM to test
Serial.print(F(" to 0x"));
Serial.println(stackBot() - callStack, HEX); // limit of testing
}
unsigned char i = 0; // global to preserve stack space during testing
void loop() {
unsigned char* bad;
unsigned char f;
if ((f = memTestDataBus((unsigned char *)hT)) != 0) {
Serial.print(F("memTestDataBus(0x"));
Serial.print((int)hT, HEX);
Serial.print(F(") fails with error 0x"));
Serial.println(f, HEX);
} else if ((bad = memTestAddressBus((unsigned char *)hT, (unsigned long)(fR - callStack))) != NULL) {
Serial.print(F("memTestAddressBus(0x"));
Serial.print((int)hT, HEX);
Serial.print(F(") fails at address 0x"));
Serial.println((int)bad, HEX);
} else if ((bad = memTestDevice((unsigned char *)hT, (unsigned long)(fR - callStack))) != NULL) {
Serial.print(F("memTestDevice(0x"));
Serial.print((int)hT, HEX);
Serial.print(F(") fails at address 0x"));
Serial.println((int)bad, HEX);
} else { // print milestones (so we know we didn't eat ourselves alive)
++i;
if ((i % DOT_DIV) == 0) {
Serial.print('.');
}
if ((i % NL_DIV) == 0) {
Serial.println();
led_state = !led_state;
digitalWrite(LED_BUILTIN, led_state);
}
}
}
Diff:
Code: Select all2,15c2,15
< * 20161129 DdelV
< *
< * Can ESP8266/ATmega328p RAM be tested?
< * Apparently so, if one is careful!
< *
< * This code was written (and cribbed) to test ESP8266 RAM
< * Also works on Arduino chips, if there's a UART.
< *
< * Allocate (just about) the largest chunk of
< * RAM that malloc() can be made to return, and tests that
< * (or most of it).
< *
< * Works! (occasionally stack dumps on ESP8266)
< */
---
> 20161129 DdelV
>
> Can ESP8266/ATmega328p RAM be tested?
> Apparently so, if one is careful!
>
> This code was written (and cribbed) to test ESP8266 RAM
> Also works on Arduino chips, if there's a UART.
>
> Allocate (just about) the largest chunk of
> RAM that malloc() can be made to return, and tests that
> (or most of it).
>
> Works! (occasionally stack dumps on ESP8266)
> */
24,30d23
< *
< * The following (3) routines are from:
< * http://www.barrgroup.com/Embedded-Systems/How-To/Memory-Test-Suite-C
< *
< **********************************************************************/
<
< typedef unsigned char datum; /* Set the data bus width to 8 bits. */
31a25,28
> The following (3) routines are from:
> http://www.barrgroup.com/Embedded-Systems/How-To/Memory-Test-Suite-C
>
> **********************************************************************/
34,46c31,43
< *
< * Function: memTestDataBus()
< *
< * Description: Test the data bus wiring in a memory region by
< * performing a walking 1's test at a fixed address
< * within that region. The address (and hence the
< * memory region) is selected by the caller.
< *
< * Notes:
< *
< * Returns: 0 if the test succeeds.
< * A non-zero result is the first pattern that failed.
< *
---
>
> Function: memTestDataBus()
>
> Description: Test the data bus wiring in a memory region by
> performing a walking 1's test at a fixed address
> within that region. The address (and hence the
> memory region) is selected by the caller.
>
> Notes:
>
> Returns: 0 if the test succeeds.
> A non-zero result is the first pattern that failed.
>
48,49c45
< datum
< memTestDataBus(volatile datum * address)
---
> unsigned char memTestDataBus(volatile unsigned char * address)
51c47
< datum pattern;
---
> unsigned char pattern;
53a50,54
> /*
> Perform a walking 1's test at the given address.
> */
> for (pattern = 1; pattern != 0; pattern <<= 1)
> {
55,57c56,63
< * Perform a walking 1's test at the given address.
< */
< for (pattern = 1; pattern != 0; pattern <<= 1)
---
> Write the test pattern.
> */
> *address = pattern;
>
> /*
> Read it back (immediately is okay for this test).
> */
> if (*address != pattern)
59,70c65
< /*
< * Write the test pattern.
< */
< *address = pattern;
<
< /*
< * Read it back (immediately is okay for this test).
< */
< if (*address != pattern)
< {
< return (pattern);
< }
---
> return (pattern);
71a67
> }
73c69
< return (0);
---
> return (0);
77,99c73,95
< *
< * Function: memTestAddressBus()
< *
< * Description: Test the address bus wiring in a memory region by
< * performing a walking 1's test on the relevant bits
< * of the address and checking for aliasing. This test
< * will find single-bit address failures such as stuck
< * -high, stuck-low, and shorted pins. The base address
< * and size of the region are selected by the caller.
< *
< * Notes: For best results, the selected base address should
< * have enough LSB 0's to guarantee single address bit
< * changes. For example, to test a 64-Kbyte region,
< * select a base address on a 64-Kbyte boundary. Also,
< * select the region size as a power-of-two--if at all
< * possible.
< *
< * Returns: NULL if the test succeeds.
< * A non-zero result is the first address at which an
< * aliasing problem was uncovered. By examining the
< * contents of memory, it may be possible to gather
< * additional information about the problem.
< *
---
>
> Function: memTestAddressBus()
>
> Description: Test the address bus wiring in a memory region by
> performing a walking 1's test on the relevant bits
> of the address and checking for aliasing. This test
> will find single-bit address failures such as stuck
> -high, stuck-low, and shorted pins. The base address
> and size of the region are selected by the caller.
>
> Notes: For best results, the selected base address should
> have enough LSB 0's to guarantee single address bit
> changes. For example, to test a 64-Kbyte region,
> select a base address on a 64-Kbyte boundary. Also,
> select the region size as a power-of-two--if at all
> possible.
>
> Returns: NULL if the test succeeds.
> A non-zero result is the first address at which an
> aliasing problem was uncovered. By examining the
> contents of memory, it may be possible to gather
> additional information about the problem.
>
101,102c97
< datum *
< memTestAddressBus(volatile datum * baseAddress, unsigned long nBytes)
---
> unsigned char * memTestAddressBus(volatile unsigned char * baseAddress, unsigned long nBytes)
104,106c99,104
< unsigned long addressMask = (nBytes/sizeof(datum) - 1);
< unsigned long offset;
< unsigned long testOffset;
---
> unsigned long addressMask = (nBytes / sizeof(unsigned char) - 1);
> unsigned long offset;
> unsigned long testOffset;
>
> unsigned char pattern = (unsigned char) 0xAAAAAAAA;
> unsigned char antipattern = (unsigned char) 0x55555555;
108,109d105
< datum pattern = (datum) 0xAAAAAAAA;
< datum antipattern = (datum) 0x55555555;
110a107,113
> /*
> Write the default pattern at each of the power-of-two offsets.
> */
> for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
> {
> baseAddress[offset] = pattern;
> }
112,115c115,123
< /*
< * Write the default pattern at each of the power-of-two offsets.
< */
< for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
---
> /*
> Check for address bits stuck high.
> */
> testOffset = 0;
> baseAddress[testOffset] = antipattern;
>
> for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
> {
> if (baseAddress[offset] != pattern)
117c125
< baseAddress[offset] = pattern;
---
> return ((unsigned char *) &baseAddress[offset]);
118a127
> }
120,123c129,135
< /*
< * Check for address bits stuck high.
< */
< testOffset = 0;
---
> baseAddress[testOffset] = pattern;
>
> /*
> Check for address bits stuck low or shorted.
> */
> for (testOffset = 1; (testOffset & addressMask) != 0; testOffset <<= 1)
> {
126c138
< for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
---
> if (baseAddress[0] != pattern)
128,131c140
< if (baseAddress[offset] != pattern)
< {
< return ((datum *) &baseAddress[offset]);
< }
---
> return ((unsigned char *) &baseAddress[testOffset]);
134,143c143
< baseAddress[testOffset] = pattern;
<
< /*
< * Check for address bits stuck low or shorted.
< */
< for (testOffset = 1; (testOffset & addressMask) != 0; testOffset <<= 1)
< {
< baseAddress[testOffset] = antipattern;
<
< if (baseAddress[0] != pattern)
---
> for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
145c145,148
< return ((datum *) &baseAddress[testOffset]);
---
> if ((baseAddress[offset] != pattern) && (offset != testOffset))
> {
> return ((unsigned char *) &baseAddress[testOffset]);
> }
148,157c151,152
< for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
< {
< if ((baseAddress[offset] != pattern) && (offset != testOffset))
< {
< return ((datum *) &baseAddress[testOffset]);
< }
< }
<
< baseAddress[testOffset] = pattern;
< }
---
> baseAddress[testOffset] = pattern;
> }
159c154
< return (NULL);
---
> return (NULL);
163,181c158,176
< *
< * Function: memTestDevice()
< *
< * Description: Test the integrity of a physical memory device by
< * performing an increment/decrement test over the
< * entire region. In the process every storage bit
< * in the device is tested as a zero and a one. The
< * base address and the size of the region are
< * selected by the caller.
< *
< * Notes:
< *
< * Returns: NULL if the test succeeds.
< *
< * A non-zero result is the first address at which an
< * incorrect value was read back. By examining the
< * contents of memory, it may be possible to gather
< * additional information about the problem.
< *
---
>
> Function: memTestDevice()
>
> Description: Test the integrity of a physical memory device by
> performing an increment/decrement test over the
> entire region. In the process every storage bit
> in the device is tested as a zero and a one. The
> base address and the size of the region are
> selected by the caller.
>
> Notes:
>
> Returns: NULL if the test succeeds.
>
> A non-zero result is the first address at which an
> incorrect value was read back. By examining the
> contents of memory, it may be possible to gather
> additional information about the problem.
>
183,184c178
< datum *
< memTestDevice(volatile datum * baseAddress, unsigned long nBytes)
---
> unsigned char * memTestDevice(volatile unsigned char * baseAddress, unsigned long nBytes)
186,187c180,181
< unsigned long offset;
< unsigned long nWords = nBytes / sizeof(datum);
---
> unsigned long offset;
> unsigned long nWords = nBytes / sizeof(unsigned char);
189,190c183,184
< datum pattern;
< datum antipattern;
---
> unsigned char pattern;
> unsigned char antipattern;
193,199c187,193
< /*
< * Fill memory with a known pattern.
< */
< for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
< {
< baseAddress[offset] = pattern;
< }
---
> /*
> Fill memory with a known pattern.
> */
> for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
> {
> baseAddress[offset] = pattern;
> }
201,204c195,200
< /*
< * Check each location and invert it for the second pass.
< */
< for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
---
> /*
> Check each location and invert it for the second pass.
> */
> for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
> {
> if (baseAddress[offset] != pattern)
206,212c202
< if (baseAddress[offset] != pattern)
< {
< return ((datum *) &baseAddress[offset]);
< }
<
< antipattern = ~pattern;
< baseAddress[offset] = antipattern;
---
> return ((unsigned char *) &baseAddress[offset]);
215,218c205,215
< /*
< * Check each location for the inverted pattern and zero it.
< */
< for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
---
> antipattern = ~pattern;
> baseAddress[offset] = antipattern;
> }
>
> /*
> Check each location for the inverted pattern and zero it.
> */
> for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
> {
> antipattern = ~pattern;
> if (baseAddress[offset] != antipattern)
220,224c217
< antipattern = ~pattern;
< if (baseAddress[offset] != antipattern)
< {
< return ((datum *) &baseAddress[offset]);
< }
---
> return ((unsigned char *) &baseAddress[offset]);
225a219
> }
227c221
< return (NULL);
---
> return (NULL);
234c228,244
< unsigned int fR=2048; // 100% of physical RAM
---
> unsigned int fR = 2048; // 100% of physical RAM
> #define DOT_DIV 1
> #define NL_DIV 128
>
> #elif defined (ESP8266)
> unsigned long fR = 81920L; // ESP8266
> #define DOT_DIV 4
> #define NL_DIV 512
>
> #elif defined (ESP32)
> unsigned long fR = 327680L; // ESP32
> #ifndef LED_BUILTIN //Is not defined by default so....
> #define LED_BUILTIN 2
> #endif
> #define DOT_DIV 4
> #define NL_DIV 512
>
236c246,247
< unsigned long fR=81920L; // default to ESP8266
---
> #error Uh sorry I have no idea what MCU this is compiling for... Please add it around line 230 together with its RAM size.
>
242a254,256
> //uint32_t pass = 0;
> static bool led_state = false;
>
245c259,260
< Serial.begin(115200);
---
> Serial.begin(1000000);
> pinMode(LED_BUILTIN, OUTPUT);
247c262
< // fR begins very large
---
> // fR begins very large
251c266
<
---
>
253c268
< Serial.print(fR,DEC);
---
> Serial.print(fR, DEC);
255c270
< Serial.print(fR,HEX);
---
> Serial.print(fR, HEX);
257c272
<
---
>
260c275
<
---
>
263c278
<
---
>
265c280
< Serial.print(fR-callStack, HEX); // alloc size less stackframe
---
> Serial.print(fR - callStack, HEX); // alloc size less stackframe
269c284
< Serial.println(stackBot()-callStack, HEX); // limit of testing
---
> Serial.println(stackBot() - callStack, HEX); // limit of testing
272c287,288
< unsigned char i=0; // global to preserve stack space during testing
---
> unsigned char i = 0; // global to preserve stack space during testing
>
274,276d289
<
< datum* bad;
< datum f;
278c291,294
< if ((f=memTestDataBus((datum *)hT)) != 0) {
---
> unsigned char* bad;
> unsigned char f;
>
> if ((f = memTestDataBus((unsigned char *)hT)) != 0) {
283c299
< } else if ((bad = memTestAddressBus((datum *)hT, (unsigned long)(fR-callStack))) != NULL) {
---
> } else if ((bad = memTestAddressBus((unsigned char *)hT, (unsigned long)(fR - callStack))) != NULL) {
288c304
< } else if ((bad = memTestDevice((datum *)hT, (unsigned long)(fR-callStack))) != NULL) {
---
> } else if ((bad = memTestDevice((unsigned char *)hT, (unsigned long)(fR - callStack))) != NULL) {
294d309
< Serial.print('.');
296c311,318
< if ((i % 128) == 0) Serial.println();
---
> if ((i % DOT_DIV) == 0) {
> Serial.print('.');
> }
> if ((i % NL_DIV) == 0) {
> Serial.println();
> led_state = !led_state;
> digitalWrite(LED_BUILTIN, led_state);
> }