/*================================================================================================*
 * Memory test
 *================================================================================================*/

#include "memtest.h"

/*================================================================================================*/
/*------------------------------------------------------------------------------------------------*/
/* Quick memory test (write sequential numbers)
   Returns: NULL on success or fault address
 */
t_memtest_result memtest_quick_32bit(
    void* memory,
    size_t block_size,
    size_t nblocks,
    memtest_callback_t callback
    )
    {
    typedef uint32_t memword_t;
    typedef volatile memword_t* memptr_t;
    const size_t blklen = block_size / sizeof(memword_t);
    const memptr_t mem_start = (memptr_t)memory;
    const memptr_t mem_end = mem_start + blklen * nblocks;
    t_memtest_result res;
    res.addr = NULL;

#define DO_UP(stmt) do { \
    memptr_t p;                                     \
    for (p = mem_start; p != mem_end; )             \
        {                                           \
        memptr_t eblk = p + blklen;                 \
        for (; p != eblk; p++)                      \
           { stmt; }                                \
        if (callback) callback();                   \
        }                                           \
    } while (0)

    DO_UP( *p = (memword_t)p );
    DO_UP( uint32_t rd_val = *p; if (rd_val != (memword_t)p) {res.addr = (void*)p; res.wr_val = (uint32_t)p; res.rd_val = rd_val; return res;} );

#undef DO_UP

    return res;
    }
/*------------------------------------------------------------------------------------------------*/

/*------------------------------------------------------------------------------------------------*/
/* "March C-" on 16-bit memory.
   Returns: NULL on success or fault address
 */
void* memtest_march_c_16bit
    (
    void* memory,
    size_t block_size,
    size_t nblocks,
    memtest_callback_t callback
    )
    {
    typedef uint16_t memword_t;
    typedef volatile memword_t* memptr_t;
    const size_t blklen = block_size / sizeof(memword_t);
    const memptr_t mem_start = (memptr_t)memory;
    const memptr_t mem_end = mem_start + blklen * nblocks;
    memword_t x = 0;
    unsigned int nbit = sizeof(memword_t) * 8;

#define DO_UP(stmt) do { \
    memptr_t p;                                     \
    for (p = mem_start; p != mem_end; )             \
        {                                           \
        memptr_t eblk = p + blklen;                 \
        for (; p != eblk; p++)                      \
           { stmt; }                                \
        if (callback) callback();                   \
        }                                           \
    } while (0)

#define DO_DN(stmt) do { \
    memptr_t p;                                     \
    for (p = mem_end; p != mem_start; )             \
        {                                           \
        memptr_t eblk = p - blklen;                 \
        for (; p != eblk; )                         \
           { p--; stmt; }                           \
        if (callback) callback();                   \
        }                                           \
    } while (0)

    do
        {
        memword_t not_x = (memword_t)~x;

        /* write x, any direction */
        DO_UP( *p = x );

        /* read x, write ~x, upwards */
        DO_UP( if (*p != x) return (void*)p; *p = not_x );

        /* read ~x, write x, upwards */
        DO_UP( if (*p != not_x) return (void*)p; *p = x );

        /* read x, write ~x, downwards */
        DO_DN( if (*p != x) return (void*)p; *p = not_x );

        /* read ~x, write x, downwards */
        DO_DN(if (*p != not_x) return (void*)p; *p = x );

        /* read x, any direction */
        DO_UP( if (*p != x) return (void*)p );

        /* choose next pattern: 0000, FF00, F0F0, CCCC, AAAA */
        nbit >>= 1;
        x ^= (memword_t)(~x << nbit);
        }
    while (nbit != 0);

#undef DO_DN
#undef DO_UP

    return NULL;
    }
/*------------------------------------------------------------------------------------------------*/
/*================================================================================================*/
