#include <stdio.h>
#include <eti/mm.h>

#define MAX_MEM (64<<20)

/* Uncomment for 8-byte loads/stores */
#define ACCESS_8_BYTE

/* Uncomment to retry when there's a mismatch */
/*  #define RETRY_FAILURES */

/* align the malloc'd buffer */
 #define ALIGN 8
/* #define ALIGN (16<<10) */

/* Bypass the D$ ? */
#define VOLATILE
/*  #define VOLATILE volatile __uncached__ */

#ifdef ACCESS_8_BYTE
#define ACCESS_TYPE unsigned long long
#define MSG "8-byte"
#define FMT "0x%016llx"
#else
#define ACCESS_TYPE unsigned long
#define MSG "4-byte"
#define FMT "0x%08x"
#endif /* ACCESS_8_BYTE */
#define ACCESS_POINTER ACCESS_TYPE VOLATILE *

/* How many bytes between each write attempt */
#define INCREMENT (sizeof(ACCESS_TYPE))

/* Write to the first and last word in a page */
/*  #define KJH_HACK */
#ifdef KJH_HACK
#undef INCREMENT
#define INCREMENT (16<<10)
#endif

int sdram_exhaustive(void *start, void *end) {
    unsigned char c;
    unsigned long i, temp;
    ACCESS_POINTER p;
    ACCESS_TYPE wval, rval;
    
    printf("Doing exhaustive memory test using %s accesses [0x%08x-0x%08x].\n",
	   MSG, (unsigned)start, (unsigned)end);

    for(i=0; i<=0xff; i++) {
	c = (unsigned char)i;
	temp = (c << 24) | (c << 16) | (c << 8) | c;
#ifdef ACCESS_8_BYTE
	wval = hmpv_combine_64(temp, temp);
#else
	wval = temp;
#endif	
	fputc('.', stdout);
	fflush(stdout);

	/* Write */
	p = (ACCESS_POINTER)start;
	while(p < end) {
	    *p = wval;  /* write */
#ifdef KJH_HACK
	    /* Write the end of the page also */
	    *(p + ((16<<10)/sizeof(p)) - 1) = wval;
#endif	    
	    p = (ACCESS_POINTER)((unsigned)p + INCREMENT);
	}

	/* Read and compare */
	p = (ACCESS_POINTER)start;
	while (p < end) {
	    rval = *p; /* read back */
	    if (wval != rval) {
		printf("\nDiff 0x%08x (0x%x): "
		       "wrote " FMT " read " FMT "\n",
		       (int)p, c, wval, rval);
#ifdef RETRY_FAILURES
		/* Retry */
		*p = wval;
		rval = *p;
		if (wval == rval) {
		    printf("   Retry Succeeded.\n");
		} else {
		    printf("   Retry 0x%08x (0x%x): "
			   "wrote " FMT " read " FMT "\n", 
			   (unsigned)p, c, wval, rval);
		}
#endif /* RETRY_FAILURES */
	    }

#ifdef KJH_HACK
	    rval = *(p + ((16<<10)/sizeof(p)) - 1); /* read back */
	    if (wval != rval) {
		printf("KJH Diff 0x%08x (0x%x): "
		       "wrote " FMT " read " FMT "\n",
		       (int)p, c, wval, rval);
	    }
#endif

	    p = (ACCESS_POINTER)((unsigned)p + INCREMENT);
	}
    }
    return 0;
}

int main(int argc, char *argv[]) {
    void *p = NULL, *start, *end;
    unsigned long mem_size = MAX_MEM;
    unsigned long reduce = 1<<20;

    /* Get me a big buffer */
    while(p == NULL) {
	p = (void *)malloc(mem_size);
	if (p == NULL) {
            if (mem_size > (1<<20))
                mem_size -= 1<<20;
            else
                mem_size -= 1<<10;
        }

        if (mem_size <= 0) {
            printf("Unable to get any memory.\n");
            return 1;
        }
    }
    start = (void *)p;
    end = (void *)((int)p + mem_size);
    printf("Buffer: [0x%08x-0x%08x] (%d %s).\n",
	   (unsigned)start, (unsigned)end,
           (mem_size >> 20) ? : (mem_size >> 10),
           (mem_size >> 20) ? "Mb" : "Kb");

    /* 8-byte align buffer */
    start = (void *)(((unsigned)p + (ALIGN-1)) & ~(ALIGN-1));
    end = (void *)(((unsigned)end - (ALIGN-1)) & ~(ALIGN-1));
    printf("Aligned [0x%08x-0x%08x].\n", 
	   (unsigned)start, (unsigned)end);

    sdram_exhaustive(start, end);

    free(p);
}
