Skip to content

Commit

Permalink
Add Barr's memory test for debugging
Browse files Browse the repository at this point in the history
Signed-off-by: Jukka Laitinen <[email protected]>
  • Loading branch information
jlaitine committed Oct 30, 2023
1 parent 5722d68 commit 1ab8a89
Show file tree
Hide file tree
Showing 4 changed files with 346 additions and 2 deletions.
1 change: 1 addition & 0 deletions arch/risc-v/src/mpfs/Make.defs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ endif

ifeq (${CONFIG_MPFS_DDR_INIT},y)
CHIP_CSRCS += mpfs_ddr.c
CHIP_CSRCS += memtest.c
endif

ifeq (${CONFIG_MPFS_BOOTLOADER},y)
Expand Down
208 changes: 208 additions & 0 deletions arch/risc-v/src/mpfs/memtest.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/**********************************************************************
*
* Filename: memtest.c
*
* Description: General-purpose memory testing functions.
*
* Notes: This software can be easily ported to systems with
* different data bus widths by redefining 'datum'.
*
*
* Copyright (c) 1998 by Michael Barr. This software is placed into
* the public domain and may be used for any purpose. However, this
* notice must not be changed or removed and no warranty is either
* expressed or implied by its publication or distribution.
**********************************************************************/


#include "memtest.h"


/**********************************************************************
*
* 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() */
41 changes: 41 additions & 0 deletions arch/risc-v/src/mpfs/memtest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**********************************************************************
*
* Filename: memtest.h
*
* Description: Memory-testing module API.
*
* Notes: The memory tests can be easily ported to systems with
* different data bus widths by redefining 'datum' type.
*
*
* Copyright (c) 2000 by Michael Barr. This software is placed into
* the public domain and may be used for any purpose. However, this
* notice must not be changed or removed and no warranty is either
* expressed or implied by its publication or distribution.
**********************************************************************/

#ifndef _memtest_h
#define _memtest_h


/*
* Define NULL pointer value.
*/
#ifndef NULL
#define NULL (void *) 0
#endif

/*
* Set the data bus width.
*/
typedef unsigned datum;

/*
* Function prototypes.
*/
datum memTestDataBus(volatile datum *address);
datum *memTestAddressBus(volatile datum *baseAddress, unsigned long nBytes);
datum *memTestDevice(volatile datum *baseAddress, unsigned long nBytes);


#endif /* _memtest_h */
98 changes: 96 additions & 2 deletions arch/risc-v/src/mpfs/mpfs_ddr.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
#include "hardware/mpfs_ddr.h"
#include "hardware/mpfs_sgmii.h"

#include "memtest.h"

/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
Expand Down Expand Up @@ -145,6 +147,13 @@
#define MPFS_DEFAULT_RETRIES 0xffff
#define MPFS_LONG_RETRIES 0xffffff

/* Uncomment to enable additional memory test for debugging */

#define MEMORYTEST
#define KILOBYTE (1024)
#define MEGABYTE (1024 * KILOBYTE)
#define GIGABYTE (1024ul * MEGABYTE)

/****************************************************************************
* Private Types
****************************************************************************/
Expand Down Expand Up @@ -283,6 +292,85 @@ static uint64_t prng_state[2] =
* Private Functions
****************************************************************************/

/* Test memory in 64MB blocks */

#ifdef MEMORYTEST
static int devtest64m(uint64_t start, size_t size)
{
datum *addr;
uint64_t start_address = start;
uint64_t end_address = start_address + size;

while (start_address < end_address)
{
_alert("test %p - %p\n", start_address, start_address + 64 * MEGABYTE);
addr = memTestDevice((volatile datum *)start_address, 64 * MEGABYTE);
if (addr != NULL)
{
_alert("dev test fail at address %"PRIxPTR"\n", addr);
return -EIO;
}

start_address += 64 * MEGABYTE;
}

return 0;
}
#endif

static int memorytest(void)
{
#ifdef MEMORYTEST
/* Test DDR in 2 1GB blocks
* 1 block is at 0x8000 0000 - 0xBFFF FFFF
* 1 block is at 0x10 0000 0000 - 0x10 3FFF FFFF
*/

unsigned long data = memTestDataBus((volatile datum *)0x80000000);

if (data != 0)
{
_alert("data bus test fail %ux\n", data);
return -EIO;
}
else
{
_alert("data bus test OK\n");
}

datum *addr = memTestAddressBus((volatile datum *)0x80000000, GIGABYTE);

if (addr == NULL)
{
addr = memTestAddressBus((volatile datum *)0x1000000000ul, GIGABYTE);
}

if (addr)
{
_alert("address bus test fail at address %"PRIxPTR"\n", addr);
return -EIO;
}
else
{
_alert("address bus test OK\n");
}

if (devtest64m(0x80000000, GIGABYTE))
{
return -EIO;
}

if (devtest64m(0x1000000000, GIGABYTE))
{
return -EIO;
}

_alert("dev test OK\n");
#endif

return 0;
}

/****************************************************************************
* Name: mpfs_wait_cycles
*
Expand Down Expand Up @@ -3941,14 +4029,20 @@ int mpfs_ddr_init(void)

if (ddr_status == 0)
{
minfo("DDR setup successfully\n");
_alert("DDR setup successfully\n");
}
else
{
minfo("DDR setup returned error: %d\n", ddr_status);
_alert("DDR setup returned error: %d\n", ddr_status);
mpfs_ddr_fail(priv);
}

if (ddr_status == 0 && memorytest())
{
_alert("Performing extra memory test FAILED\n");
ddr_status = -EIO;
}

minfo("Done ddr setup\n");

return ddr_status;
Expand Down

0 comments on commit 1ab8a89

Please sign in to comment.