#include "mss_uart.h"
#include "mss_hpdma.h"
#include "config.h"

uint32_t is_secded_error_sigle_bit();
uint32_t secded_error_count(uint8_t mode);
uint32_t array[2000];
uint32_t array1[2000];
void UartMsgHandler();
void ddr_onebit_init();
void ddr_twobit_init();
void init();
void init1();
uint32_t g_read_data = 0,g_counter1= 0,g_counter= 0;
int8_t    rx_buf  = 0;
int8_t    rx_size = 0;
uint8_t   key,dual_flag =0,error_2b=0;
uint8_t   UARTMsgRx[10];
uint8_t   UARTMsgTx[10];
int32_t   Received_ADDR;
int   Received_Data;
int32_t   Received_2bit_error_Data;
int8_t    send_err_cnt_fr_ind_op;
int8_t    edac_loop_test = 0,edac_loop_test_2e=0;

uint32_t *ddr_data_ptr,rd_data,rd_data1,Data,st_c = 0;

uint32_t  TX_ADDR,msg_rxed =0,address_2e;
int printnumber(int);

#if defined(__GNUC__)
__attribute__((__interrupt__)) void HardFault_Handler( void );
#else
void HardFault_Handler( void );
#endif
#if defined(__GNUC__)
__attribute__((__interrupt__)) void ECC_Error_IRQHandler( void );
#else
void ECC_Error_IRQHandler( void );
#endif

/*For a 2-bit error, when the ARM Cortex-M3 processor reads the data, the code execution goes in to
the hard fault handler, as the interrupt received is late for the processor to respond. By the time it
responds to the interrupt, it may have already passed the data and accidentally launched a
command. As a result, the HRESP stops processing the incorrect data. 2-bit error detection uses
HPDMA to read the data from the DDR address location, which instructs the processor that read
data has a 2-bit error and the system should take appropriate action to recover (ECC interrupt
Handler). */ 

__attribute__((__interrupt__)) void HardFault_Handler( void )
{
	if((*(volatile uint32_t *)0x4002093c & 7) == 2 || (*(volatile uint32_t *)0x4002093c & 7) == 6)
	{

		*(uint32_t *)Received_ADDR = 0x0;   //Reset the affected DDR memory location ,m1.
	}


}

void init()
{
	int i;
	*(volatile uint32_t *)0x40020940 = 0x1;
	ddr_data_ptr = (uint32_t *)0xA0000000;
	for(i=0;i<900;i++)
				{

					*ddr_data_ptr = 0x1;
					 ddr_data_ptr++;
				}

	*(volatile uint32_t *)0x40020940 = 0x1;
	init1();
}
void init1()
{
	int i;
	ddr_data_ptr = (uint32_t *)0xA0004000;
			for(i=0;i<900;i++)
			{
				*ddr_data_ptr = 0x1;
				 ddr_data_ptr++;
			}
	*(volatile uint32_t *)0x40020940 = 0x1;

}

void ddr_onebit_init()
{
	int i;
	*(volatile uint32_t *)0x40020940 = 0x1;
			ddr_data_ptr = (uint32_t *)0xA0008000;
			for(i=0;i<200;i++)
			{
				
				*ddr_data_ptr = 0x1;

				ddr_data_ptr++;
			}
	*(volatile uint32_t *)0x40020940 = 0x1;
}

void ddr_twobit_init()
{
	int i;
	*(volatile uint32_t *)0x40020940 = 0x1;
			ddr_data_ptr = (uint32_t *)0xA000C000;
			for(i=0;i<300;i++)
			{
				config_edac(ENBL_EDAC_H);
				*ddr_data_ptr = 0x1;
				delay(10000);
				ddr_data_ptr++;
			}
			delay(10000);
	*(volatile uint32_t *)0x40020940 = 0x1;
}
void uart0_rx_handler( mss_uart_instance_t * this_uart )
{
	rx_size = MSS_UART_get_rx( &g_mss_uart0, &key, 1);

	if(rx_size==1 && rx_buf < 10)
	{
		UARTMsgRx[rx_buf]=key;
		rx_buf++;
		rx_size  = 0;
	}

	if( rx_buf >= 10 )
	{
		rx_size = 0;
		//UartMsgHandler();
		msg_rxed = 1;
		rx_buf=0;
		switch(UARTMsgRx[0])
		{
		case LOOP_TEST_ON_1BIT:  //EDAC Test ON
			edac_loop_test = 1;
			break;
		case LOOP_TEST_ON_2BIT:
			edac_loop_test_2e = 2;
			break;
		case LOOP_TEST_OFF_1BIT: //EDAC Test OFF
			edac_loop_test = 0;
			break;
		case LOOP_TEST_OFF_2BIT:
			edac_loop_test_2e = 0;
			break;
			/*----------------------------------------------------------------------------------
			 * To connect the proper FTDI COM port
			 *----------------------------------------------------------------------------------*/
		case 'x':
			MSS_UART_polled_tx(&g_mss_uart0, (const uint8_t *) "y", 1);
			break;
		default:
			break;
		}
	}

}
uint32_t single_error_corrupted_address_offset()
{
	uint32_t data = 0,temp1;
	data = *(volatile uint32_t *)0x40020904;  //DDRC_LCE_SYNDROME_1_SR
	temp1 = *(volatile uint32_t *) 0x40020908 - 0x00004000;
	return (data+(0x00008000*temp1));
}
#if 1
void send_messages(uint8_t header, uint32_t tx_addr, uint32_t tx_data)
{
	UARTMsgTx[0] = header;
	UARTMsgTx[1] = tx_addr;
	UARTMsgTx[2] = tx_addr>>8;
	UARTMsgTx[3] = tx_addr>>16;
	UARTMsgTx[4] = tx_addr>>24;
	UARTMsgTx[5] = tx_data;
	UARTMsgTx[6] = tx_data>>8;
	UARTMsgTx[7] = tx_data>>16;
	UARTMsgTx[8] = tx_data>>24;
	UARTMsgTx[9]   =0x0;
	MSS_UART_polled_tx(&g_mss_uart0,UARTMsgTx,10);
}
__attribute__((__interrupt__)) void ECC_Error_IRQHandler( void )      //ECC interrupt handler for dual bit error
{
			    	if(dual_flag ==1)
		    	{
		    		dual_flag = 0;
		    		*(uint32_t *)Received_ADDR = 0; // resetting the data
		    	}
		    	else
		    	*(uint32_t *)ddr_data_ptr = 0x1; // resetting with original data
			    rd_data1=single_error_corrupted_address_offset();
			    NVIC_ClearPendingIRQ(ECC_Error_IRQn);
				NVIC_DisableIRQ( ECC_Error_IRQn );


}

void delay(volatile uint32_t n)
{
	while(n!=0)
	{
		n--;
	}
}
void config_edac(uint8_t edac_choice)
{
	if(edac_choice == DISBL_EDAC_H)
	{
		*(volatile uint32_t *)0x40020818 = 0x100;  //disable ddr EDAC
		*(volatile uint32_t *)0x4002089c = 0x4000;
		*(volatile uint32_t *)0x40020A34 = 0x3;
	}
	else
	{
		*(volatile uint32_t *)0x40020818 = 0x114;  //enable ddr EDAC
		*(volatile uint32_t *)0x4002089c = 0x4002;
		*(volatile uint32_t *)0x40020A34 = 0x15;
	}
}

void UartMsgHandler()
{
	uint32_t *ddr_addr_ptr;
	uint32_t data;
	int i;

    int32_t state0;

	switch(UARTMsgRx[0])
	{
	case INIT_DDR:
		init();
		//init1();
		delay(delay_count);
		send_messages(INIT_DONE, 0x000000000,0x00000000 );
		break;
	case RESET:
		*(volatile uint32_t *)0x40020940 = 0x1;
		break;
	case EDAC_ON_1BIT: //EDAC ON
	case EDAC_ON_2BIT: //EDAC ON
		send_messages(ENBL_EDAC_H, 0x00000000, 0x00000000);
		delay(delay_count);
		config_edac( ENBL_EDAC_H);


		break;
	case EDAC_OFF_1BIT: // EDAC OFF
	case EDAC_OFF_2BIT:

		send_messages(DISBL_EDAC_H, 0x00000000, 0x00000000);
		delay(delay_count);
		config_edac( DISBL_EDAC_H);
		error_2b = 0;

		break;
	case WRITE_1BIT:  //Write
	case WRITE_2BIT:  //Write
	Received_ADDR = ((int)UARTMsgRx[4]<<24)+((int)UARTMsgRx[3]<<16)+((int)UARTMsgRx[2]<<8)+UARTMsgRx[1];
	ddr_addr_ptr = (uint32_t *)Received_ADDR;
	Received_Data = ((int)UARTMsgRx[8]<<24)+((int)UARTMsgRx[7]<<16)+((int)UARTMsgRx[6]<<8)+UARTMsgRx[5];
	send_messages(DATA_W_H, Received_ADDR, Received_Data);
	*ddr_addr_ptr = Received_Data;
	break;
	case READ_1BIT: //Read
		Received_ADDR = ((int)UARTMsgRx[4]<<24)+((int)UARTMsgRx[3]<<16)+((int)UARTMsgRx[2]<<8)+UARTMsgRx[1];
		ddr_addr_ptr = (uint32_t *)Received_ADDR;
		Received_Data        = *ddr_addr_ptr;
		send_messages(READ_H, Received_Data, Received_ADDR);
		delay(delay_count);
		send_messages(DATA_R_H, Received_ADDR, Received_Data);
		delay(delay_count);
		send_messages(ERROR_CNT_H, secded_error_count(1), 0x00000000);
		delay(delay_count);
		break;
	case READ_2BIT: //Read
		Received_ADDR = ((int)UARTMsgRx[4]<<24)+((int)UARTMsgRx[3]<<16)+((int)UARTMsgRx[2]<<8)+UARTMsgRx[1];
		//(uint32_t *)Received_ADDR = data;
		ddr_addr_ptr = (uint32_t *)Received_ADDR;
		dual_flag =1;
		NVIC_EnableIRQ( ECC_Error_IRQn );

		MSS_HPDMA_start
		(
				ddr_addr_ptr,
				&Received_Data,
				LENGTH,
				HPDMA_FROM_DDR
		);
		state0 = MSS_HPDMA_get_transfer_status();
		do{
			state0 = MSS_HPDMA_get_transfer_status();
			if(state0 = HPDMA_COMPLETED)
			{
				break;
			}
			delay(delay_count);
		} while(0);
		if(error_2b == 1)
		{
			error_2b = 0;
			send_messages(ERROR_2B,0x00000000, 0x00000000);
			delay(delay_count);
		}
		delay(delay_count);
		send_messages(READ_H_2B, Received_Data, Received_ADDR);
		delay(delay_count);
		send_messages(DATA_R_H_2B, Received_ADDR, Received_Data);
		delay(delay_count);
		send_messages(ERROR_CNT_H_2B, secded_error_count(2), 0x00000000);
		delay(delay_count);
		break;
		/*----------------------------------------------------------------------------------
		 * To connect the proper FTDI COM port
		 *----------------------------------------------------------------------------------*/
	case 'x':
		MSS_UART_polled_tx(&g_mss_uart0, (const uint8_t *) "y", 1);
		break;
	default:
		break;
	}
}
#endif

uint32_t secded_error_count(uint8_t mode)
{
	uint32_t count = 0;
	if(mode == 1)
	{
		count = *(volatile uint32_t *)0x400208E8;/*DDRC_SINGLE_ERR_CNT_STATUS_SR*/
	}
	else
	{
		count = *(volatile uint32_t *)0x400208EC;/*DDRC_DOUBLE_ERR_CNT_STATUS_SR*/
	}
	return count;
}
uint32_t single_error_corrupted_data()
{
	uint32_t data = 0;
	///data = *(volatile uint32_t *)0x4002090c ;  //DDRC_LCE_SYNDROME_1_SR

	data = *(volatile uint32_t *)0x40020910 ;
	data = data<<16;
	data = (data | ( *(volatile uint32_t *)0x40020910) & 0xFFFFFFFF);
	return data;
}

uint32_t is_secded_error_sigle_bit()
{
	uint32_t result = 0;
	result = *(volatile uint32_t *)0x4002093c;/*DDRC_SINGLE_ERR_CNT_STATUS_SR*/
	result = result & 7 ;
	return result;
}

void EDAC_LOOP_TEST_1E ()
{

	//hpdma_status_t state0 ;
	uint32_t     rd_data,read_data;
	int          wr_data;
	volatile int i ;


	delay(delay_count);

	Data  = 0x00000001;

	ddr_data_ptr = (uint32_t *)0xA0008000;
	*(volatile uint32_t *)0x40020940 = 0x1;
	for (i=0;i<200;i++)
	{
		if(edac_loop_test == 1)
		{
			SYSREG -> EDAC_IRQ_ENABLE_CR = 0xFFFFFFFF;
			TX_ADDR = (uint32_t)ddr_data_ptr;
			send_messages(ENBL_EDAC_H, 0x00000000, 0x00000000);
			delay(delay_count);
			send_messages(DATA_W_H, TX_ADDR, Data);
			delay(delay_count);
			config_edac(ENBL_EDAC_H);
			*ddr_data_ptr = Data;
			//delay(delay_count);
			delay(delay_count);
			//wr_data        = *ddr_data_ptr;
			send_messages(DISBL_EDAC_H, 0x00000000, 0x00000000);
			delay(delay_count);
			/*------------------ DISABLE EDAC --------------------------------*/
			config_edac(DISBL_EDAC_H); // Disabling EDAC of DDR  //
			/*---------------------------------------------------------------*/

			/* Flipping Two bits */
			wr_data        = 0x00000000;
			*ddr_data_ptr = wr_data;


			send_messages(DATA_W_C_H, TX_ADDR, wr_data);
			delay(delay_count);
			send_messages(ENBL_EDAC_H, 0x00000000, 0x00000000);
			delay(delay_count);
			/*------------------ ENABLE EDAC --------------------------------*/
			config_edac(ENBL_EDAC_H); // Enabling EDAC of DDR  //
			/*---------------------------------------------------------------*/
			delay(delay_count);
			send_messages(LED_ON_H, 0x00000000, 0x00000000);

			delay(delay_count);
			read_data        = *ddr_data_ptr;
			delay(delay_count);

			SYSREG -> EDAC_IRQ_ENABLE_CR = 0x0;
			/*------------------    Increment data pointer --------------------------------*/

			rd_data = secded_error_count(1);
			send_messages(ERROR_CNT_H, rd_data, 0x00000000);

			delay(delay_count);
			send_messages(DATA_R_H, TX_ADDR, read_data);

			delay(delay_count);
			send_messages(LED_OFF_H, 0x00000000, 0x00000000);

			delay(delay_count);
			ddr_data_ptr++;
		}
	}

	//edac_loop_test = 0;
}
void EDAC_LOOP_TEST_2E ()
{

	hpdma_status_t state0 ;
	uint32_t     rd_data,temp;
	int          wr_data;
	volatile int i ;

	config_edac(ENBL_EDAC_H);
	delay(delay_count);
	delay(delay_count);
	send_messages(ERROR_CNT_H_2B, 0x00000000, 0x00000000);
	delay(delay_count);

	Data  = 0x00000001;
	send_messages(ENBL_EDAC_H, 0x00000000, 0x00000000);
	delay(delay_count);
	/*------------------ ENABLE EDAC --------------------------------*/
	config_edac(ENBL_EDAC_H); // Enabling EDAC of DDR  //
	/*---------------------------------------------------------------*/
	delay(delay_count);
	ddr_data_ptr = (uint32_t *)0xA000C000;
	*(volatile uint32_t *)0x40020940 = 0x1;
	for (i=0;i<200;i++)
	{
		if(edac_loop_test_2e == 2)
		{
			SYSREG -> EDAC_IRQ_ENABLE_CR = 0xFFFFFFFF;
			TX_ADDR = (uint32_t)ddr_data_ptr;
			send_messages(DATA_W_H, TX_ADDR, Data);
			delay(delay_count);
			*ddr_data_ptr = 0x1234;
			config_edac(ENBL_EDAC_H);
			*ddr_data_ptr = Data;
			wr_data        = *ddr_data_ptr;
			send_messages(DISBL_EDAC_H, 0x00000000, 0x00000000);
			delay(delay_count);
			/*------------------ DISABLE EDAC --------------------------------*/
			config_edac(DISBL_EDAC_H); // Disabling EDAC of DDR  //
			/*---------------------------------------------------------------*/

			/* Flipping Two bits */
			wr_data        = 0x2;
			*ddr_data_ptr = Data^3;
			//*ddr_data_ptr = Data^3;
			//wr_data        = *ddr_data_ptr;

			send_messages(DATA_W_C_H_2B, TX_ADDR, wr_data);
			delay(delay_count);
			send_messages(ENBL_EDAC_H, 0x00000000, 0x00000000);
			delay(delay_count);
			/*------------------ ENABLE EDAC --------------------------------*/
			config_edac(ENBL_EDAC_H); // Enabling EDAC of DDR  //
			/*---------------------------------------------------------------*/
			delay(delay_count);
			send_messages(LED_ON_H, 0x00000000, 0x00000000);

			NVIC_EnableIRQ( ECC_Error_IRQn );				//two bit interrupt handler

			MSS_HPDMA_start
			(
					ddr_data_ptr,
					&rd_data,
					4,
					HPDMA_FROM_DDR
			);

			state0 = MSS_HPDMA_get_transfer_status();				//HPDMA read from DDR address
			do {
				state0 = MSS_HPDMA_get_transfer_status();
				delay(delay_count);
			} while(HPDMA_IN_PROGRESS == state0);
			delay(delay_count);
			delay(delay_count);

			SYSREG -> EDAC_IRQ_ENABLE_CR = 0x0;

			/*------------------    Increment data pointer --------------------------------*/

			ddr_data_ptr++;
			delay(delay_count);
			send_messages( ERROR_2B, 0x0,0x0);
			delay(delay_count);
			rd_data = secded_error_count(2);
			send_messages(ERROR_CNT_H_2B, rd_data, 0x00000000);

			delay(delay_count);
			delay(delay_count);
			delay(delay_count);
			delay(delay_count);
			rd_data1=single_error_corrupted_address_offset();
			delay(delay_count);
			//send_messages( AFFECT_OFFSET_H_2E, (rd_data1+temp),0x0);
			temp=temp+4;
			delay(delay_count);delay(delay_count);
			send_messages(LED_OFF_H, 0x00000000, 0x00000000);

			delay(delay_count);
		}
	}

	//edac_loop_test = 0;
}

#if 1
int main()
{
	int rd_data,i=0;
	/* Turn off the watchdog */
	SYSREG->WDOG_CR = 0;
	SYSREG->SOFT_RST_CR &= ~SYSREG_HPDMA_SOFTRESET_MASK;// taking HPDMA out of reset
	MSS_HPDMA_init();
	init();
	MSS_UART_init( &g_mss_uart0, MSS_UART_57600_BAUD, MSS_UART_DATA_8_BITS | MSS_UART_NO_PARITY | MSS_UART_ONE_STOP_BIT );
	MSS_UART_set_rx_handler( &g_mss_uart0,uart0_rx_handler,MSS_UART_FIFO_SINGLE_BYTE);

	while (1)
	{

		if(edac_loop_test == 1)
		{
			edac_loop_test_2e = 0;
		    ddr_onebit_init();						//DDR memory initialization
			//config_edac(DISBL_EDAC_H);
			EDAC_LOOP_TEST_1E();
		}
		else if(edac_loop_test_2e == 2)
		{
			edac_loop_test = 0;
			ddr_twobit_init();					      //DDR memory initialization
			*(volatile uint32_t *)0x40020940 = 0x1;   //reset DDR EDAC error count register
			EDAC_LOOP_TEST_2E ();
		}
		if(msg_rxed == 1)
		{
			UartMsgHandler();
			msg_rxed = 0;
		}
	}


}
#endif


