RAM test for ESP8266 or ATmega328P
Posted: Wed Nov 30, 2016 3:28 pm
I don't know if this is even useful. I wrote it to diagnose/confirm troubles with an Amica NodeMCU module that only reports <ROM gobbledygook>MEMORY FAULT.
It didn't find the problem (which leaves me still in the dark), but I believe it would if the tested RAM where faulty.
Anyhow, here it is. Not polished. Let me know what you think if you play with it.
Have fun!
It didn't find the problem (which leaves me still in the dark), but I believe it would if the tested RAM where faulty.
Anyhow, here it is. Not polished. Let me know what you think if you play with it.
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
*
**********************************************************************/
typedef unsigned char datum; /* Set the data bus width to 8 bits. */
/**********************************************************************
*
* 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.
*
**********************************************************************/
datum
memTestDataBus(volatile datum * address)
{
datum 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.
*
**********************************************************************/
datum *
memTestAddressBus(volatile datum * baseAddress, unsigned long nBytes)
{
unsigned long addressMask = (nBytes/sizeof(datum) - 1);
unsigned long offset;
unsigned long testOffset;
datum pattern = (datum) 0xAAAAAAAA;
datum antipattern = (datum) 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 ((datum *) &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 ((datum *) &baseAddress[testOffset]);
}
for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
{
if ((baseAddress[offset] != pattern) && (offset != testOffset))
{
return ((datum *) &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.
*
**********************************************************************/
datum *
memTestDevice(volatile datum * baseAddress, unsigned long nBytes)
{
unsigned long offset;
unsigned long nWords = nBytes / sizeof(datum);
datum pattern;
datum 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 ((datum *) &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 ((datum *) &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
#else
unsigned long fR=81920L; // default to ESP8266
#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.
void setup() {
Serial.begin(115200);
// 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() {
datum* bad;
datum f;
if ((f=memTestDataBus((datum *)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((datum *)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((datum *)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)
Serial.print('.');
++i;
if ((i % 128) == 0) Serial.println();
}
}
Have fun!