/******************************************************************************
 *
 *	RAM DIAGNOSTIC
 *
 * This module contains RAM diagnostic tests
 *
 * History:
 *   +----------+------------+------------------------------------------------+
 *   |   Date   |     by     |     Description                                |
 *   +----------+------------+------------------------------------------------+
 *   |          | Amir       | Written                                        |
 *   +----------+------------+------------------------------------------------+
 * 
 *****************************************************************************/

#include "cc_def.h"
#include "diag.h"
#if (BOARD == VECTOR)
#include <asis.h>
#endif


public	int ram_size__;
public	int dram_size__;
public	int sram_size__;
public	int nvram_size__;
public  int step_5n_flag__;

/* Forward private functions */
/*****************************/

private char *  data_walk_1_0();
private char *  address_walk_1_0();
private unsigned int random();
private char * dram_5N ();


#ifndef		DRAM_START
#    define     DRAM_START	0x0
#endif

#ifndef		DRAM_OFFSET
#    define         DRAM_OFFSET       1
#endif
#ifndef		SRAM_OFFSET
#    define         SRAM_OFFSET       1
#endif
#ifndef		NVRAM_OFFSET
#    define	    NVRAM_OFFSET	1
#endif
#ifndef		PC_EMUL_INIT
#    define	    PC_EMUL_INIT
#endif


/*****************************************************************************
 *
 *     Get_mask__()
 *
 *     Since the byte size of a memory space is not known in advence
 *     a check is done and a logical mask value is determined for further
 *     actions on the memory. Under the following assumptions:
 *     1) A continuance memory space is of the same byte size.
 *     2) A byte is 1, 2, 4 or 8 bits long, byte or word aligned.
 *     3) The bits are allways the LSB bits.
 *
 *     The check is done for each memory term. (SRAM DRAM ...).
 *     by writing and reading the first words of the given memory space.
 *
 ****************************************************************************/

private int get_mask(ram_start)
int * ram_start;
{
    int word_5a,word_a5,copy_ram_start,copy_plus_10, mask_byte;
    volatile int v;

    copy_ram_start = *ram_start;
    copy_plus_10 = *(ram_start + 0x10);

    *ram_start = 0x5a3d5a3d;
    *(ram_start + 0x10) = 0xa5d3a5d3;
    v = 0xc3c3c3c3;
    DELAY(1000);
    word_5a = *ram_start;
    word_a5 = *(ram_start + 0x10);
    if ((word_5a == 0x5a3d5a3d) && (word_a5 == 0xa5d3a5d3)) {
	mask_byte = 0xffffffff;
    }
    else if (((word_5a & 0x0f0f0f0f) == 0x0a0d0a0d) && 
    				((word_a5 & 0x0f0f0f0f) == 0x05030503)) {
        mask_byte = 0x0f0f0f0f;
    }
    else if (((word_5a & 0x03030303) == 0x02010201) &&
				((word_a5 & 0x03030303) == 0x01030103)) {
        mask_byte = 0x03030303;
    }
    else if (((word_5a & 0x00ff00ff) == 0x003d003d) &&
				((word_a5 & 0x00ff00ff) == 0x00d300d3)) {
        mask_byte = 0x00ff00ff;
    }
    else if (((word_5a & 0x000f000f) == 0x000d000d) &&
				((word_a5 & 0x000f000f) == 0x00030003)) {
        mask_byte = 0x000f000f;
    }
    else if (((word_5a & 0x00030003) == 0x00010001) &&
				((word_a5 & 0x00030003) == 0x00030003)) {
        mask_byte = 0x00030003;
    }
    else {
        mask_byte = 0x01010101;
    }
    *ram_start = copy_ram_start;
    *(ram_start + 0x10) = copy_plus_10;
    return mask_byte;
}

char *test_dram__();
/*****************************************************************************
 *
 *		get_ram_size__()
 *
 *	global ram size test return size of on board ram under the 
 *	following assumptions:
 *	1) Ram memory starts at 0 or at RAM_START and is continues.
 *	2) Minimum ram block is 16KB. (64KB for addresses 0x0 to 0x10000.)
 *	3) On end of ram block one of the following two might ocure:
 *	    A.  Memory write fails.
 *	    B.  Memory is overlapd to address 0 (Next memory window)
 *	4) Data Cache (if exist) disabled.
 *	
 *	The data bus sustaines the 'last data on bus' for a few seconds.
 *	This creates a need to compliment the data on the exect bus.
 *	Writing to addr + 16 ensures the compliment will be positioned
 *	in the same place of the same bank for an 8 16 32 64 bits bus.
 *
 ****************************************************************************/

public unsigned get_ram_size__(ram_start,mask_byte)
byte * ram_start;
byte   mask_byte;
{
    volatile byte *    addr;
    volatile byte * clr_data_bus;
    byte    copy0,copy16,addr_copy,clr_data_bus_copy;
    
    /*
     * Save & initialize first addresses
     */
    addr= ram_start;
    clr_data_bus = ram_start + 16;
    copy0 = *addr;
    copy16 = *clr_data_bus;
    *addr = 0;

    /*
     * Loop until write fails or overlap occurs
     */
    while ( (*ram_start & mask_byte) == 0) {
        addr += (unsigned) addr >= 0x10000 ? 0x4000 : 0x10000;
	if ((unsigned) addr > (unsigned) get_ram_size__ && 
			(unsigned) addr < (unsigned) test_dram__)
		continue;
	clr_data_bus = addr + 16;
	addr_copy = *addr;
	clr_data_bus_copy = *clr_data_bus;

        *addr = 0x5a;
	*clr_data_bus = 0xa5;	/* compliment the data bus on the same bank */
        if ((*addr & mask_byte) != (0x5a & mask_byte))
            break;
        *addr = 0xa5;		    /* Write the complement.		*/
	*clr_data_bus = 0x5a;	/* compliment the data bus on the same bank */
        if ((*addr & mask_byte) != (0xa5 & mask_byte))
            break;
        if ( (*ram_start & mask_byte) != 0) {
	    break;
	}
	*addr = addr_copy;
	*clr_data_bus = clr_data_bus_copy;
    }

    /*
     * Write back the original values
     */
    *addr = addr_copy;
    *clr_data_bus = clr_data_bus_copy;
    *ram_start = copy0;
    clr_data_bus = ram_start + 16;
    *clr_data_bus = copy16;
    PC_EMUL_INIT;		/* For a1d with PC emulator uart */
    if (debug_flag) {
	strcpy(msg__, "RAM SIZE: 0x");
	bin_to_hex__((addr - ram_start), &msg__[strlen(msg__)], 'D');
	print_str (msg__);
    }
    return (unsigned) (addr - ram_start);

}

/****************************************************************
 *
 *	test_dram__()
 *
 *  All the different ram tests (SRAM DRAM ...) are simmilar except
 *  for values of a few parameters.
 *  The test consist of:
 *   Determination of the byte size.
 *   Determination of memory size.
 *   walk 1 and walk 0 on data and address bus.
 *   Standard 5N test.
 *
 ****************************************************************/

public char * test_dram__()
{
    char * chp;
    unsigned int CFG_;
    int    mask_byte;
    byte * ram_start = (byte *) DRAM_START;
    byte   ram_offset = (byte) DRAM_OFFSET;

#ifdef CACHE
    _spr("cfg", &CFG_);
    cache_disable__();
#endif

    mask_byte = get_mask(ram_start);
    ram_size__ = get_ram_size__(ram_start,mask_byte);
    dram_size__ = ram_size__;
    if ((chp = data_walk_1_0()) == NULL)
	if ((chp = address_walk_1_0(ram_start,mask_byte,ram_offset)) == NULL)
	    chp = dram_5N(ram_start,mask_byte);

#ifdef CACHE
    _lpr("cfg", CFG_);
#endif

    return chp;
}

/*********************************************************************
 *
 *  test_sram__()
 *
 *********************************************************************/

#ifdef  	SRAM_START

private byte * sram_start[5];

public char * test_sram__()
{
    char * chp;
    unsigned int  CFG_, mask_byte, i = 0;
    byte * ram_start;
    byte   save_debug_flag, ram_offset = SRAM_OFFSET;

#ifdef CACHE
    _spr("cfg", &CFG_);
    cache_disable__();
#endif

    sram_start[0] = (byte *) SRAM_START;
    sram_start[1] = (byte *) SRAM_START1;
    sram_start[2] = (byte *) SRAM_START2;
    sram_start[3] = (byte *) SRAM_START3;

    ram_start = sram_start[i];
    while (ram_start != (byte *) LAST_ELEMENT) {
	mask_byte = get_mask(ram_start);
	save_debug_flag = debug_flag;
	debug_flag = FALSE;
        ram_size__ = get_ram_size__(ram_start,mask_byte);
	debug_flag = save_debug_flag;
	if (((mask_byte == 0xffffffff) OR (mask_byte == 0x0f0f0f0f) OR 
	        (mask_byte == 0x00ff00ff) OR (mask_byte == 0x000f000f)) AND 
		    ((unsigned) ram_size__ > 0x0))
	    break;
	ram_start = sram_start[++i];
    }
    if (ram_start == (byte *) LAST_ELEMENT) {
        strcpy(error, "ERROR: MEMORY READ FAIL AT FIRST ADDR");
        return error;
    }
    if (debug_flag) {
	strcpy(msg__, "SRAM IS AT ADDR: ");
	bin_to_hex__(ram_start, &msg__[strlen(msg__)], 'D');
	print_str (msg__);
    }
    sram_size__ = get_ram_size__(ram_start,mask_byte);
    if ((chp = data_walk_1_0()) == NULL)
	if ((chp = address_walk_1_0(ram_start,mask_byte,ram_offset)) == NULL)
	    chp = dram_5N(ram_start,mask_byte);

#ifdef CACHE
    _lpr("cfg", CFG_);
#endif

    return chp;
}
#endif

#ifdef NVRAM_START
/****************************************************************************
 *
 *	The NVRAM test.
 *
 ****************************************************************************/

public char * test_nvram__()
{
    char * chp;
    unsigned int CFG_;
    int    mask_byte;
    byte * ram_start = (byte *) NVRAM_START;
    byte   ram_offset = (byte) NVRAM_OFFSET;

#ifdef CACHE
    _spr("cfg",  &CFG_);
    cache_disable__();
#endif
    mask_byte = get_mask(ram_start);
    ram_size__ = NVRAM_SIZE;
    nvram_size__ = ram_size__;
    if ((chp = data_walk_1_0()) == NULL)
        if ((chp = address_walk_1_0(ram_start, mask_byte, ram_offset)) == NULL)
            if ((chp = dram_5N(ram_start, mask_byte)) == NULL)
#ifdef          NEVER
                chp = test_clock();
#else
                ;
#endif

#ifdef CACHE
    _lpr("cfg",  CFG_);
#endif

    return chp;
}


#endif /* NVRAM_START */

/*****************************************************************
 *
 *  data_walk_1_0()
 *
 *  This function writes a ones and zeros (00001 00010 00100 etc`) through 
 *  the data bus to memory, then reads it and compares.  
 *
 *****************************************************************/

private char *  data_walk_1_0()
{
    volatile unsigned pattern;
    int		i;

    for (i = 1; i < BUSWIDTH * 8; i++) {
	pattern = 1 << i;
	if (pattern != 1 << i) {
	    strcpy(error, "ERROR: DATA WALK 1, ");
	    bin_to_hex__(1 << i,   &error[strlen(error)],   'D');
	    return error;
	}
    }
    if (debug_flag) {
	strcpy(msg__, "DATA WALK 1 PASSED");
	print_str (msg__);
    }
    for (i = 1; i < BUSWIDTH * 8; i++) {
	pattern =  ~(1 << i);
	if (pattern != ~(1 << i)) {
	    strcpy(error, "ERROR: DATA WALK 0, ");
	    bin_to_hex__(~(1 << i),   &error[strlen(error)],   'D');
	    return error;
	}
    }
    if (debug_flag) {
	strcpy(msg__, "DATA WALK 0 PASSED");
	print_str (msg__);
    }
    return NULL;
}

/******************************************************************
 *
 *  address_walk_1_0()
 *
 *  This function performs a walk 1 and walk 0 on the address bus by writing
 *  a pattern to binary addresses 0000001 0000010 0000100 etc` checking
 *  That the pattern has not been writen to address 0000000 instead.
 *  The same is done with binary addresses 1111110 1111101 1111011 etc`
 *  checking with address 1111111.
 *
 *****************************************************************/

static char *  address_walk_1_0(ram_start,mask_byte,ram_offset)
int  mask_byte;
byte * ram_start;
unsigned int  ram_offset;
{
    volatile byte *base_addr, *walk_addr;
    byte save_walk_addr;
    int     i;
    unsigned int mask = 0xFFFFFFFF << ram_offset;

    /*
     * Walk 1 test
     */
    base_addr = ram_start;
    i = 0;
    *base_addr = 0;
    while ( (walk_addr = base_addr + (ram_offset << i++) ) 
				<  base_addr + ram_size__) {
	save_walk_addr = *walk_addr;
	*walk_addr = (byte ) 0x5a;
	if ( ( (int) *base_addr & mask_byte) != 0) {
	PC_EMUL_INIT;
	    strcpy(error, "ERROR: ADDRESS WALK 1, ");
	    bin_to_hex__(walk_addr , &error[strlen(error)], 'D');
	    return error;
	}
	*walk_addr = save_walk_addr;
    }
    if (debug_flag) {
    PC_EMUL_INIT;
	strcpy(msg__, "ADDRESS WALK 1 PASSED");
	print_str (msg__);
    }
    
    /*
     * Walk 0 test
     */
    base_addr = (byte *) ((int) ram_start + ram_size__ - (int) ram_offset);
    walk_addr = (byte *) ( (unsigned int ) base_addr & mask);
    i = 0;
    *base_addr = 0;
    while ((base_addr > walk_addr) && (walk_addr >= (byte *) ram_start)) {

	save_walk_addr = *walk_addr;
	*walk_addr = 0x5a;
	if ( ( (int) *base_addr & mask_byte) != 0) {
	PC_EMUL_INIT;
	    strcpy(error, "ERROR: ADDRESS WALK 0, ");
	    bin_to_hex__(walk_addr,   &error[strlen(error)],   'D');
	    return error;
	}
	*walk_addr = save_walk_addr;
	mask = (mask << 1) + ram_offset;
	walk_addr = (unsigned char *) ( (unsigned int ) base_addr & mask);
	i++;
    }
    PC_EMUL_INIT;
    if (debug_flag) {
	strcpy(msg__, "ADDRESS WALK 0 PASSED");
	print_str (msg__);
    }
    return NULL;
}

/****************************************************************************
 *
 *	random()
 *
 *  This function creates random numbers for the 5N test.
 *
 ****************************************************************************/

private unsigned int rseed;

private unsigned int random()
{
    unsigned int r;
    
    r  = rseed * 1103515245 + 12345;
    rseed = r & 0x7fffffff;
    return r;    
}

 /****************************************************************************
  * 
  *		5N test:
  *	    1.  Fill Block of memory with psudo random numbers.
  *	    2.  Fill the block with the complement of previous random numbers.
  *	    3.  Verify contenet of memory block.
  *	    4.	Fill the block with original numbers.
  *	    5.	Verify contenet of memory block.
  *
  * Assumption : Filling / Verifying of the memory takes time longer than 
  *		1 SEC -- No explicit wait is perform between write and read.
  *
  *		Tested block starts in start_5N constant (0x800 in real life)
  *		It is the diagnostic initializer
  *		responsabilty  to test lowest 0x800 bytes.
  *
  ****************************************************************************/

static char * dram_5N (ram_start,mask_byte)
byte        * ram_start;
int           mask_byte;
{
    volatile unsigned int *uaddr;
    volatile unsigned int *start_5N, *end_5N;
    char    *chp;
    int	     i;

extern unsigned block_start__;
extern unsigned start_diag__;

#define BLOCK_SIZE 0x8000

for(start_5N = (unsigned *) ram_start; start_5N < (unsigned *) 
		(ram_start + ram_size__); start_5N = end_5N) {
	
    end_5N = (unsigned *) ((unsigned) start_5N + BLOCK_SIZE);
    if (end_5N > (unsigned *) (ram_start + ram_size__) ) {
	end_5N = (unsigned *) (ram_start + ram_size__);	    
    }

#ifdef DEBUG
    if (start_5N < (unsigned *) 0x20000) {
	continue;
    }
#endif

    /*
     * Check if the monitor text resides within the 5N test range.
     * If so, skip this range
     */
    if ((unsigned) start_5N <= (unsigned) &start_diag__ && 
	(unsigned) end_5N >= (unsigned) &start_diag__ || 
	(unsigned) start_5N <= (unsigned) &start_diag__ + BLOCK_SIZE &&
	(unsigned) end_5N >= (unsigned) &start_diag__ + BLOCK_SIZE ) {
	continue;
    }

    /*
     * Check if the monitor data resides within the 5N test range.
     * If so, skip this range
     */
    if ((unsigned) start_5N <=  (unsigned) &block_start__ &&
        (unsigned) end_5N >= (unsigned) &block_start__ || 
	(unsigned) start_5N <=  (unsigned) &block_start__ + 0x1008 &&
	(unsigned) end_5N >= (unsigned) &block_start__ + 0x1008) {
	continue;
    }

    if (i == 0) {
	LED(LED_1);
	i = 1;
    }
    else {
	LED(LED_0);
	i = 0;
    }
    if (debug_flag) {
	strcpy(msg__, "5N TEST : ");
	bin_to_hex__(start_5N,   &msg__[strlen(msg__)],   'D');
	strcat(msg__, " - ");
	bin_to_hex__(end_5N,   &msg__[strlen(msg__)],   'D');
	print_str (msg__);
    }

    rseed = 15;
    for (uaddr = start_5N; uaddr < (end_5N - step_5n_flag__); uaddr +=
    							step_5n_flag__) {
        *uaddr = random();

    }

    rseed = 15;
    for (uaddr = start_5N; uaddr < (end_5N - step_5n_flag__); uaddr +=
    							step_5n_flag__) {
        *uaddr = ~(random());

    }

    rseed = 15;
    for (uaddr = start_5N; uaddr < (end_5N - step_5n_flag__); uaddr +=
    							step_5n_flag__) {
        if ((*uaddr & mask_byte) != (~(random()) & mask_byte) ) {
            strcpy(error, "ERROR: 5N TEST FAILD AT, ");
            bin_to_hex__(uaddr, &error[strlen(error)], 'D');
            return error;
        }

    }    

    rseed = 15;
    for (uaddr = start_5N; uaddr < (end_5N - step_5n_flag__); uaddr +=
    							step_5n_flag__) {
        *uaddr = random();

    }

    rseed = 15;
    for (uaddr = start_5N; uaddr < (end_5N - step_5n_flag__); uaddr +=
    							step_5n_flag__) {
        if ((*uaddr & mask_byte) != (random() & mask_byte) ) {
            strcpy(error, "ERROR: 5N TEST FAILD AT, ");
            bin_to_hex__(uaddr, &error[strlen(error)], 'D');
            return error;
        }

    }

}

return NULL;
}
