#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "memory_test.h"
#include "exo_osal.h"
#include "exo_hal_io_al_common.h"
#include "exo_ahw_al_ext_flash_common.h"
#include "exo_io_al_fmc_nor_flash_common.h"
#include "exo_hal_common.h"

#ifndef LINUX_TEMP_PORT
#include "stm32f7xx_hal.h"
#endif


#ifdef EXT_MEM_TEST_ENB

uint8_t buffer_memread_nor[4096];
uint8_t buffer_test_nor[4096];
uint32_t time_taken_mx25[10][2];
uint8_t qspi_test_buf[4096];

/*
 * this function call be called in main function to test qspi nor flash
 * It uses 4kb sector erase configuration
 * it write 1 mb data at start address 0x00000000 then read and compare the data
 *
 * It also calculates time usage of read write and erase operation
 *
 */
uint8_t qspi_test_code(void)
{
	uint32_t var;
	uint8_t enable_memmap=0;

  	if(enable_memmap) {
  		ahw_al_ext_mem_enable_memory_map(EXT_MEM_QSPI_FLASH);
  		return 1;
  	}

 	for (var = 0; var < 4096; var++) {
  		buffer_test_nor[var] = (var & 0xff);
  	}
  	time_taken_mx25[0][0] = HAL_GetTick();	// start time
  	ahw_al_ext_mem_erase(EXT_MEM_QSPI_FLASH, 0, 100*4096);
  	time_taken_mx25[1][0] = HAL_GetTick();                         // time taken to erase 400kb
  	time_taken_mx25[1][1] = HAL_GetTick() - time_taken_mx25[0][0]; // t = 2382 ms

	ahw_al_ext_mem_write(EXT_MEM_QSPI_FLASH, 0, buffer_test_nor, 4096);
  	time_taken_mx25[2][0] = HAL_GetTick();                         // time taken to write 4kb
  	time_taken_mx25[2][1] = HAL_GetTick() - time_taken_mx25[1][0]; // t = 10 ms

	ahw_al_ext_mem_write(EXT_MEM_QSPI_FLASH, 0x10000, buffer_test_nor, 4096);  //write 4kb
	ahw_al_ext_mem_write(EXT_MEM_QSPI_FLASH, 0x20000, buffer_test_nor, 4096);  //write 4kb
	ahw_al_ext_mem_write(EXT_MEM_QSPI_FLASH, 0x30000, buffer_test_nor, 4096);  //write 4kb
  	time_taken_mx25[3][0] = HAL_GetTick();                         // time taken to write 12kb
  	time_taken_mx25[3][1] = HAL_GetTick() - time_taken_mx25[2][0]; // t = 29 ms

	//ahw_al_ext_mem_enable_memory_map(&ext_mem_mx25);

  	for (var = 0; var < 4096; var++) {
  		buffer_test_nor[var] = 0;
  	}
  	time_taken_mx25[4][0] = HAL_GetTick(); // start time before read
  	time_taken_mx25[4][1] = HAL_GetTick() - time_taken_mx25[3][0]; //
	ahw_al_ext_mem_read(EXT_MEM_QSPI_FLASH, 0x30000, buffer_test_nor, 4096);
  	time_taken_mx25[5][0] = HAL_GetTick();                         // time taken to read 4kb
  	time_taken_mx25[5][1] = HAL_GetTick() - time_taken_mx25[4][0]; // t = 5 ms

	ahw_al_ext_mem_erase(EXT_MEM_QSPI_FLASH, 0x30000, 0x30000 + 4096);
  	time_taken_mx25[6][0] = HAL_GetTick();                         // time taken to erase 4kb
  	time_taken_mx25[6][1] = HAL_GetTick() - time_taken_mx25[5][0]; // t = 45 ms
  	ahw_al_ext_mem_read(EXT_MEM_QSPI_FLASH, 0x30000, buffer_test_nor, 4096);

  	for (var = 0; var < 4096; var++) {
  		buffer_test_nor[var] = var & 0xff;
  	}
	ahw_al_ext_mem_write(EXT_MEM_QSPI_FLASH, 0x30000, buffer_test_nor, 4096);

  	for (var = 0; var < 4096; var++) {
  		buffer_test_nor[var] = 0;
  	}

	ahw_al_ext_mem_read(EXT_MEM_QSPI_FLASH, 0x30000, buffer_test_nor, 4096);
  	time_taken_mx25[7][0] = HAL_GetTick();
  	time_taken_mx25[7][1] = HAL_GetTick() - time_taken_mx25[6][0]; //

    // Erase 1 mb from address 0x0000000
	ahw_al_ext_mem_erase(EXT_MEM_QSPI_FLASH, 0, 1024*1024);
  	time_taken_mx25[8][0] = HAL_GetTick();                         // time taken to erase 1mb
  	time_taken_mx25[8][1] = HAL_GetTick() - time_taken_mx25[7][0]; // t = 5974 ms

	/*
	 * If memory map is enabled , read write api will not work
	 * set breakpoint on if condition and set 1 to enable mem map
	 */
  	if(enable_memmap) {
  		ahw_al_ext_mem_enable_memory_map(EXT_MEM_QSPI_FLASH);
  		return 1;
  	}

  	for (var = 0; var < 4096; var++) {
  		buffer_test_nor[var] = var & 0xff;
  	}

  	/* write 1mb at address start from 0x00000000*/
  	for (var = 0; var < 256; var++)
  	{
		ahw_al_ext_mem_write(EXT_MEM_QSPI_FLASH, (var*4096), buffer_test_nor, 4096);

  	}
	time_taken_mx25[9][0] = HAL_GetTick();                         // time taken to write 1mb
	time_taken_mx25[9][1] = HAL_GetTick() - time_taken_mx25[8][0]; // t = 2542 ms

	/*
	 * If memory map is enabled , read write api will not work
	 * set breakpoint on if condition and set 1 to enable mem map
	 */
  	if(enable_memmap) {
  		ahw_al_ext_mem_enable_memory_map(EXT_MEM_QSPI_FLASH);
  		return 1;
  	}

  	/* Compare write and read data are identical */
  	for (var = 0; var < 256; var++)
  	{
		ahw_al_ext_mem_read(EXT_MEM_QSPI_FLASH, (var*4096), buffer_memread_nor, 4096);
		if(memcmp(buffer_test_nor,buffer_memread_nor,4096) != 0)
		{
			/*
			 * issue in write or erase operation
			 */
			while(1);
		}
  	}

  	return 1;

}

/*
 * this will be invoked by memcpy handler
 * it will enable memory map mode so user can directly see the data in memory browser window in stmcube ide
 * once the copy operation has been successfully completed
 */
static void qtest_cmplt_cblk(uint8_t sts)
{
	uint8_t test_var = 0;
	ahw_al_ext_mem_enable_memory_map(EXT_MEM_QSPI_FLASH);
	os_delay(100);
	test_var = test_var;
}


/*
 * This function can be called from thread context,
 * it will perform copy operation from sdram memory to qspi nor flash of 1200 kb data
 */
void qspi_test_thread_ctxt(void)
{

	uint32_t mem_qpi_test_inc =4096;
	uint8_t  mem_qpi_data_byte =0xff;
	uint32_t  mem_qpi_data_size =4096;

	uint32_t mem_qspi_cpy_size = 900*1024;
	uint32_t src_sdram_address = 0xc0000000;
	uint32_t dst_qspi_addr =0x0000000;
	uint32_t dst_iflash_addr =0x8100000;
	uint8_t test_on1 =0;
	uint8_t test_on2 =0;

	if(test_on1)
	{

		for (mem_qpi_test_inc =0; mem_qpi_test_inc < mem_qpi_data_size; mem_qpi_test_inc++)
		{
			qspi_test_buf[mem_qpi_test_inc] = mem_qpi_test_inc & mem_qpi_data_byte;
		}
		ahw_al_ext_mem_erase(EXT_MEM_QSPI_FLASH, dst_qspi_addr, dst_qspi_addr + mem_qpi_data_size);
		ahw_al_ext_mem_write(EXT_MEM_QSPI_FLASH, dst_qspi_addr, qspi_test_buf, mem_qpi_data_size);

		/* copy from sdram to qspi flash*/
		os_memcpy_extmem( (uint8_t*)dst_qspi_addr, (uint8_t*)src_sdram_address, mem_qspi_cpy_size, qtest_cmplt_cblk, 0,1);
	    test_on1 = 0;
	}

	if(test_on2)
	{
		/* copy from qspi flash to internal flash */
		os_memcpy_extmem( (uint8_t*)dst_iflash_addr, (uint8_t*)dst_qspi_addr, mem_qspi_cpy_size, qtest_cmplt_cblk, 0,1);
	    test_on2 = 0;
	}

}

uint8_t ext_flash_mem_test()
{
	uint8_t sts=1,x=0;
	uint32_t buffer_len=100*1024;
	uint8_t pattern=0xAA;
	uint8_t* dest_addr=(uint8_t*)0x90000000;      // QSPI flash -0,  FMC - Nor flash 0x60000000
	uint8_t* src_addr = os_malloc_eram(buffer_len);
	uint8_t* rcv_ptr = os_malloc_eram(buffer_len);
	memset(src_addr,pattern,buffer_len);
	if(x)
	{
		x=0;

		os_memcpy_extmem(rcv_ptr,dest_addr,buffer_len,NULL,1,1);
		for(int i = 0;i<buffer_len;i++)
		{
			if(rcv_ptr[i]==src_addr[i])
			{
				sts=0;
			}
			else
			{
				sts=1;
				break;
			}
		}

	os_memcpy_extmem(dest_addr,src_addr,buffer_len,NULL,1,1);
	os_memcpy_extmem(rcv_ptr,dest_addr,buffer_len,NULL,1,1);
	for(int i = 0;i<buffer_len;i++)
	{
		if(rcv_ptr[i]==src_addr[i])
		{
			sts=0;
		}
		else
		{
			sts=1;
			break;
		}
	}
	}
	os_free(src_addr);
	os_free(rcv_ptr);
	return sts;
}

#endif
