/*******************************************************************************
 * (c) Copyright 2016 Microsemi Corporation.  All rights reserved.
 *
 */

/*-------------------------------------------------------------------------*//**
 * main() function.
 */

/*******************************************************************************
 * (c) Copyright 2017 Microsemi Corporation.  All rights reserved.
 *
 */

/* Memory map in RP demo Libero design
 *
 * PCIe APB IF       0x63000000
 * UART APB IF       0x64000000
 * PCIe RP AXI slave 0x70000000 - 0x7FFFFFFF
 *
 */

#include "drivers/CoreUARTapb/core_uart_apb.h"
#include "miv_rv32_hal/sample_hw_platform.h"
#include "pf_pcie.h"
#include "miv_rv32_hal/miv_rv32_hal.h"
#include "hal.h"

//#define MEM_TEST 1

/************************* RP design specific Address definitions**************/

/*pcie rp bridge address - APB*/
#define PCIE_RP_APB_ADDR            0x63000000

/* AXI slave address */
#define PCIE_RP_AXIS_IF             0x70000000

/*PCIe RP AXI Master source address defined in master ATR0 in Libero prj*/
#define PCIe_RP_AXIM_ADDR          0x10000000

/* AXI slave IF addr + 0x1000000 (as per slave1 configuration in Libero prj) */
#define EP_BAR0_ADDR                0x71000000

/* AXI slave IF addr + 0x2000000 (as per slave2 configuration in Libero prj) */
#define EP_BAR2_ADDR                0x72000000

/*RP memory addess to RISC-V*/
#define RP_MEM_ADDR                 0x73000000
/******************************************************************************/

/************************* EP design specific Address definitions**************/

//IO Registers base address connected to PCIe EP AXIM
#define PCIE_EP_IO_CTRL             0x10000000

//DDR3 address connected to PCIe EP AXIM
#define PCIE_EP_DDR3                0x20000000

//LSRAM address connected to PCIe EP AXIM
#define PCIE_EP_LSRAM               0x30000000

//DDR4 address connected to PCIe EP AXIM
#define PCIE_EP_DDR4                0x40000000
/******************************************************************************/

/************************* EP design specific register definitions*************/

#define PCIE_RW_REG        0x00/4
#define LED_ADDRESS        0x08/4
#define DIP_SWITCH_REG     0x10/4
#define DMA_CNTRL_REG      0x18/4
#define CLK_COUNT0_REG     0x20/4
#define CLK_COUNT1_REG     0x24/4
#define PCIE_DMA0_SIZE     0x38/4
#define CLK_FREQ_REG       0x50/4
/******************************************************************************/

/************************* Common Address definitions**************************/

//PCIe bridge space 0x8000
#define BRIDGE_REG_OFFSET           0x8000

//PCIe control space 0xA000
#define CTRL_REG_OFFSET             0xA000
/******************************************************************************/

/* 64kB ATR size */
#define ATR_64K_SIZE_EN             0x1F

/* 512kB ATR size */
#define ATR_512K_SIZE_EN             0x25

/* 256MB ATR size */
#define ATR_256M_SIZE_EN             0x37

/* BAR size mask */
#define BAR_SIZE_MASK               0x0FFFFFF0u

/* Memory access enable */
#define PCIE_CFG_CMD_MEM_EN         0x00000002

/* Bus master enable */
#define PCIE_CFG_CMD_BUSM_EN        0x00000004

/* device conrol status register: maxrdreq=512, maxpayloadsize=256 */
#define DEVICE_CTRL_STATUS_DATA     0x00FF2830u

#define LTSSM_L0_STATE 0x10

/******************************************************************************/

//Defining BAR0 and BAR2 pointers
uint32_t *EP_BAR0 = (uint32_t *)EP_BAR0_ADDR;
uint32_t *EP_BAR2 = (uint32_t *)EP_BAR2_ADDR;

//Defining PCIe RP APB register address
uint32_t PCIE_RP_CTRL_ADDR    =  PCIE_RP_APB_ADDR + CTRL_REG_OFFSET;
uint32_t PCIE_RP_BRIDGE_ADDR  =  PCIE_RP_APB_ADDR + BRIDGE_REG_OFFSET;

//Defining PCIe EP APB register address - EP BAR0 mapped to PCIe APB address
uint32_t PCIE_EP_CTRL_ADDR    =  EP_BAR0_ADDR     + CTRL_REG_OFFSET;
uint32_t PCIE_EP_BRIDGE_ADDR  =  EP_BAR0_ADDR     + BRIDGE_REG_OFFSET;

uint8_t rx_buff[1];
UART_instance_t this_uart;
//gpio_instance_t this_gpio;
uint8_t count1=0,count2=0,count3=0,count4=0;

/******RP PCIe Local interrupt ISR - for MSI count******/
uint8_t External_IRQHandler(void)
{
	uint32_t MSI_flag1 = 0, MSI_flag2 = 0;

	PCIE_BRIDGE_TypeDef * pcie_rp_bridge_reg;
	pcie_rp_bridge_reg =  ( PCIE_BRIDGE_TypeDef *)PCIE_RP_BRIDGE_ADDR;

	PCIE_BRIDGE_TypeDef * pcie_ep_bridge_reg;
	pcie_ep_bridge_reg =  ( PCIE_BRIDGE_TypeDef *)PCIE_EP_BRIDGE_ADDR;

	//to know whether interrupt source is MSI or not
	MSI_flag1 = pcie_rp_bridge_reg -> ISTATUS_LOCAL;
	//to get MSI number
	MSI_flag2 = pcie_rp_bridge_reg -> ISTATUS_MSI;

	//clear RP interrupt status
	pcie_rp_bridge_reg -> ISTATUS_MSI = 0xFFFFFFFF;
	pcie_rp_bridge_reg -> ISTATUS_LOCAL = 0xFFFFFFFF;

	//check bit[28] - MSI status
	if(MSI_flag1 & 0x10000000)
	{
		MSI_flag1 = 0;

		//increment interrupt counters
		if(MSI_flag2 == 1)
			count1++;
		else if(MSI_flag2 == 2)
			count2++;
		else if(MSI_flag2 == 4)
			count3++;
		else if(MSI_flag2 == 8)
			count4++;
	}

	//clear EP local interrupt
	pcie_ep_bridge_reg -> ISTATUS_LOCAL  = 0xFFFFFFFF; //istatus_local
	pcie_ep_bridge_reg -> ISTATUS_HOST   = 0xFFFFFFFF;

	//clear RP interrupt status
	MSI_flag2 = 0;

	return 0;
}

/******delay function******/
static void delay ( int n)
{
	while(n!=0)
	{
		n--;
	}
}

/******function to receive more than one character from UART******/
void uart_polled_rx(
		uint8_t * rx_buffer,
		size_t buff_size)
{
	uint32_t rx_size=0;

	while(rx_size < buff_size)
	{
		while(!(UART_get_rx(&this_uart, rx_buffer, 1 )));
		rx_size++;
		rx_buffer++;
	}

}

/******function to wait for PCIe link L0 state******/
void pcie_wait_for_L0()
{

	uint32_t  reg_val;

	PCIE_CTRL_TypeDef * pcie_rp_ctrl;
	pcie_rp_ctrl = ( PCIE_CTRL_TypeDef *) PCIE_RP_CTRL_ADDR;

	//Checking PCIe link status
	do{
		reg_val = pcie_rp_ctrl->LTSSM_STATE;
	}while(reg_val != LTSSM_L0_STATE);

	//disable ECC clear ECC interrupts
	pcie_rp_ctrl -> ECC_CONTROL        = 0x0F000000;
	pcie_rp_ctrl -> SEC_ERROR_INT      = 0x0000FFFF;
	pcie_rp_ctrl -> SEC_ERROR_INT_MASK = 0x0000FFFF;
	pcie_rp_ctrl -> DED_ERROR_INT      = 0x0000FFFF;
	pcie_rp_ctrl -> DED_ERROR_INT_MASK = 0x0000FFFF;

	//clear PCIe L2 Exit, Hot reset exit and DLUp exit interrupts
	reg_val = pcie_rp_ctrl ->PCIE_EVENT_INT = pcie_rp_ctrl ->PCIE_EVENT_INT | 0x7;
}

/******function to configure RP Type1 configuration******/
void pcie_rp_config_init()
{
	PCIE_BRIDGE_TypeDef *pcie_rp_bridge_reg;
	
	pcie_rp_bridge_reg =  ( PCIE_BRIDGE_TypeDef *)PCIE_RP_BRIDGE_ADDR;

	PCIE_ROOT_CONF_TypeDef *pcie_rp_config_space;
	pcie_rp_config_space = (PCIE_ROOT_CONF_TypeDef *)PCIE_RP_AXIS_IF;

	//PCIe AXIM source address
	pcie_rp_bridge_reg -> ATR0_PCIE_WIN0_SRCADDR_PARAM =
			(PCIe_RP_AXIM_ADDR | ATR_256M_SIZE_EN);//src addr

	//RP slave ATR0 configuration to access RP config space from AXI slave IF
	pcie_rp_bridge_reg -> ATR0_AXI4_SLV0_SRCADDR_PARAM =
			(PCIE_RP_AXIS_IF | ATR_64K_SIZE_EN);//src addr
	pcie_rp_bridge_reg -> ATR0_AXI4_SLV0_TRSL_ADDR_LSB =
			0x00000000;//trsl addr
	pcie_rp_bridge_reg -> ATR0_AXI4_SLV0_TRSL_PARAM    =
			0x00000001;//Selects PCIe Config space

	//RP ATR3 configuration to access RP config space from AXI slave IF
	pcie_rp_bridge_reg -> ATR3_AXI4_SLV0_TRSL_PARAM    =
			0x00000004;//Selects PCIe AXI master IF

	//Type1 config space
	//set CONFIG SPACE address Command register
	pcie_rp_config_space -> CFG_PRMSCR |=
			(PCIE_CFG_CMD_BUSM_EN | PCIE_CFG_CMD_MEM_EN);

	//Configuring RP BAR0 for 64-bit
	pcie_rp_config_space -> BAR0 = 0x0000000C;

	//Configuring RP BAR1
	pcie_rp_config_space -> BAR1  = 0x0;

	//Secondary Latency Timer,Subordinate BUS,Secondary BUS Primary BUS
	pcie_rp_config_space -> PRIM_SEC_BUS_NUM = 0x00010100;

	//Non-prefetchable Limit, Non-prefetchable Base
	pcie_rp_config_space -> MEM_LIMIT_BASE = 0xEEE0EEE0;

	//Prefetchable Limit, Prefetchable Base
	pcie_rp_config_space -> PREF_MEM_LIMIT_BASE = 0xFFF1FFF1;

	//Reading CONFIG SPACE address 0x28
	pcie_rp_config_space -> PREF_BASE_UPPER=0xFFFFFFFF;

	//Reading CONFIG SPACE address 0x2C
	pcie_rp_config_space -> PREF_LIMIT_UPPER= 0x00000000;

	//Reading CONFIG SPACE address 0x88
	pcie_rp_config_space ->DEVICE_CTRL_STAT = 0x0000202F;

	//allocate 4 MSI, enable MSI
	pcie_rp_config_space -> MSI_CAPAB_CTRL =
			(pcie_rp_config_space -> MSI_CAPAB_CTRL | 0x210000 );
	//set msi address
	pcie_rp_config_space -> MSI_LOWER_ADDRESS = 0x190;
	pcie_rp_config_space -> MSI_UPPER_ADDRESS = 0x0;
	//set MSI data
	pcie_rp_config_space -> MSI_DATA = 0x120;

}

/******function to configure EP Type0 configuration******/
//Accessing END POINT Type0 configuration space
//Enumerating PCIe EP
void pcie_ep_enumeration()
{
	uint32_t  reg_val;

	PCIE_BRIDGE_TypeDef *pcie_rp_bridge_reg;
	pcie_rp_bridge_reg =  ( PCIE_BRIDGE_TypeDef *)PCIE_RP_BRIDGE_ADDR;

	PCIE_END_CONF_TypeDef *pcie_ep_config_space;
	pcie_ep_config_space = (PCIE_END_CONF_TypeDef *)0x70100000;

	//EP config space - ECAM BDF (bus no:1, device & function no:0)
	//configuring RP slave ATR for EP configspace
	//source addr, 64KB size
	pcie_rp_bridge_reg -> ATR0_AXI4_SLV0_SRCADDR_PARAM =  0x7010001F;
	//trsl addr
	pcie_rp_bridge_reg -> ATR0_AXI4_SLV0_TRSL_ADDR_LSB = 0x70100000;
	//Selects PCIe Config space
	pcie_rp_bridge_reg -> ATR0_AXI4_SLV0_TRSL_PARAM    = 0x00000001;

	delay(100);

	//Type0 configspaace
	//device ID, vendor ID
	reg_val= pcie_ep_config_space -> VID_DEVID;

	//send device ID to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,2);

	//Enable bus master
	pcie_ep_config_space -> CFG_PRMSCR |=
			(PCIE_CFG_CMD_BUSM_EN | PCIE_CFG_CMD_MEM_EN);

	// BIST, Header Type, Master Latency timer, Cache Line size
	pcie_ep_config_space -> BIST_HEADER = 0x10;

	//link capabilities
	reg_val= pcie_ep_config_space -> LINK_CAPAB;
	//get EP supported link width
	reg_val = (reg_val>>4)&0xF;

	//send EP supported link width to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,1);

	//link capabilities
	reg_val= pcie_ep_config_space -> LINK_CAPAB;
	//get EP supported link speed
	reg_val = (reg_val)&0xF;

	//send EP supported link speed to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,1);

	//link status
	reg_val= pcie_ep_config_space -> LINK_CTRL_STAT;
	//get EP negotiated link width
	reg_val=(reg_val>>20)&0xF;

	//send EP negotiated link width to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,1);

	//link status
	reg_val= pcie_ep_config_space -> LINK_CTRL_STAT;
	//get EP negotiated link speed
	reg_val=(reg_val>>16)&0xF;

	//send EP negotiated link speed to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,1);

	//send number of BARs info to GUI
	reg_val = 2;
	UART_send(&this_uart,(uint8_t *)&reg_val,1);;

	//Accessing/Configuring BAR0
	pcie_ep_config_space -> BAR0 = 0xFFFFFFFF;
	reg_val= pcie_ep_config_space -> BAR0;

	//Calculate BAR0 size
	reg_val &= BAR_SIZE_MASK;
	reg_val = ~(reg_val) + 1u;
	reg_val=reg_val&0xFFFFF00;

	//send BAR0 size to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,4);

	//Configuring the EP BAR0 address
	if(reg_val == 0)//if no BAR
	{
		pcie_ep_config_space -> BAR0 = 0;
	}
	else
	{
		//configure BAR0 address
		pcie_ep_config_space -> BAR0 = EP_BAR0_ADDR;
		reg_val= pcie_ep_config_space -> BAR0;
	}

	//send BAR0 address to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,4);//BAR address

	//Accessing/Configuring BAR1
	pcie_ep_config_space -> BAR1 = 0xFFFFFFFF;
	reg_val= pcie_ep_config_space -> BAR1;

	//Calculate BAR1 size
	reg_val &= BAR_SIZE_MASK;
	reg_val = ~(reg_val) + 1u;
	reg_val=reg_val&0xFFFFF00;

	//send BAR1 size to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,4);//BAR size

	//Configuring the EP BAR1 address
	if(reg_val == 0)
	{
		pcie_ep_config_space -> BAR1 = 0;
	}
	else
	{
		reg_val= pcie_ep_config_space -> BAR1;
	}

	//send BAR1 address to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,4);//BAR address

	//Reading CONFIG SPACE address 0x18
	pcie_ep_config_space -> BAR2 = 0xFFFFFFFF;
	reg_val= pcie_ep_config_space -> BAR2;

	/* Calculate BAR2 size */
	reg_val &= BAR_SIZE_MASK;
	reg_val = ~(reg_val) + 1u;
	reg_val=reg_val&0xFFFFF00;

	//send BAR2 size to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,4);

	//Configuring the EP BAR2 address
	if(reg_val == 0)
	{
		pcie_ep_config_space -> BAR2 = 0;
	}
	else
	{
		pcie_ep_config_space -> BAR2 = EP_BAR2_ADDR;
		reg_val= pcie_ep_config_space -> BAR2;
	}

	//send BAR2 address to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,4);//BAR address

	//Accessing/Configuring BAR3
	pcie_ep_config_space -> BAR3 = 0xFFFFFFFF;
	reg_val= pcie_ep_config_space -> BAR3;

	//Calculate BAR1 size
	reg_val &= BAR_SIZE_MASK;
	reg_val = ~(reg_val) + 1u;
	reg_val=reg_val&0xFFFFF00;

	//send BAR3 size to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,4);//BAR size

	//Configuring the EP BAR3 address
	if(reg_val == 0)
	{
		pcie_ep_config_space -> BAR3 = 0;
	}
	else
	{
		reg_val= pcie_ep_config_space -> BAR3;
	}

	//send BAR3 address to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,4);//BAR address

	//Accessing/Configuring BAR4
	pcie_ep_config_space -> BAR4 = 0xFFFFFFFF;
	reg_val= pcie_ep_config_space -> BAR4;

	//Calculate BAR1 size
	reg_val &= BAR_SIZE_MASK;
	reg_val = ~(reg_val) + 1u;
	reg_val=reg_val&0xFFFFF00;

	//send BAR4 size to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,4);//BAR size

	//Configuring the EP BAR4 address
	if(reg_val == 0)
	{
		pcie_ep_config_space -> BAR4 = 0;
	}
	else
	{
		reg_val= pcie_ep_config_space -> BAR4;
	}

	//send BAR4 address to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,4);//BAR address

	//Accessing/Configuring BAR5
	pcie_ep_config_space -> BAR5 = 0xFFFFFFFF;
	reg_val= pcie_ep_config_space -> BAR5;

	//Calculate BAR1 size
	reg_val &= BAR_SIZE_MASK;
	reg_val = ~(reg_val) + 1u;
	reg_val=reg_val&0xFFFFF00;

	//send BAR5 size to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,4);//BAR size

	//Configuring the EP BAR5 address
	if(reg_val == 0)
	{
		pcie_ep_config_space -> BAR5 = 0;
	}
	else
	{
		reg_val= pcie_ep_config_space -> BAR5;
	}

	//send BAR2 address to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,4);//BAR address

	//allocate 4 MSI, enable MSI
	pcie_ep_config_space -> MSI_CAPAB_CTRL =
			(pcie_ep_config_space -> MSI_CAPAB_CTRL | 0x210000 );
	//set msi address
	pcie_ep_config_space -> MSI_LOWER_ADDRESS = 0x190;
	pcie_ep_config_space -> MSI_UPPER_ADDRESS = 0x0;
	//set MSI data
	pcie_ep_config_space -> MSI_DATA = 0x120;

	//Writing the PCIe Device CSR :  maxrdreq=512 maxpayloadsize=256
	pcie_ep_config_space->DEVICE_CTRL_STAT = DEVICE_CTRL_STATUS_DATA;
}

/******function to disable EP ECC, to clear/mask the EP interrupts******/
void  pcie_ep_access()
{
	static uint32_t i=0;

	PCIE_CTRL_TypeDef * pcie_ep_ctrl;
	pcie_ep_ctrl = ( PCIE_CTRL_TypeDef *) PCIE_EP_CTRL_ADDR;

	PCIE_BRIDGE_TypeDef * pcie_ep_bridge_reg;
	pcie_ep_bridge_reg =  ( PCIE_BRIDGE_TypeDef *)PCIE_EP_BRIDGE_ADDR;

	//disable ECC, disable  and clear ECC interrupts
	pcie_ep_ctrl -> ECC_CONTROL        = 0x0F000000;
	pcie_ep_ctrl -> SEC_ERROR_INT      = 0x0000FFFF;
	pcie_ep_ctrl -> SEC_ERROR_INT_MASK = 0x0000FFFF;
	pcie_ep_ctrl -> DED_ERROR_INT      = 0x0000FFFF;
	pcie_ep_ctrl -> DED_ERROR_INT_MASK = 0x0000FFFF;
	pcie_ep_ctrl ->PCIE_EVENT_INT = pcie_ep_ctrl ->PCIE_EVENT_INT | 0x7;
	//enable EP local interrupt for DMA throughput calculation
	pcie_ep_bridge_reg -> IMASK_LOCAL  = 0x0000FFFF; //imask_local

	//Enable MSI generation and clear EP host interrupt
	pcie_ep_bridge_reg -> IMASK_HOST     = 0xFF00FFFF;
	pcie_ep_bridge_reg -> ISTATUS_HOST   = 0xFFFFFFFF;

//4KB LSRAM init
	pcie_ep_bridge_reg -> ATR2_PCIE_WIN0_TRSL_ADDR_LSB =
			PCIE_EP_LSRAM;
    delay(100);
	for(i=0;i<1024;i++)
	{
		//write to BAR2
		*(EP_BAR2 + i) = i+1;
	}

//4KB DDR3 init
	pcie_ep_bridge_reg -> ATR2_PCIE_WIN0_TRSL_ADDR_LSB =
			PCIE_EP_DDR3;
	delay(100);
	for(i=0;i<1024;i++)
	{
		//write to BAR2
		*(EP_BAR2 + i) = 1024-i;
	}

	//4KB DDR4 init
	pcie_ep_bridge_reg -> ATR2_PCIE_WIN0_TRSL_ADDR_LSB =
				PCIE_EP_DDR4;
	delay(200);
	i=0;
	for(i=0;i<1024;i++)
	{
		//write to BAR2
		*(EP_BAR2 + i) = 0xFFFFFFFF-i;
	}

	pcie_ep_bridge_reg -> ATR2_PCIE_WIN0_TRSL_ADDR_LSB =
			PCIE_EP_IO_CTRL;

}

/******function to read EP configspace******/
void read_pcie_ep_configspace()
{
	uint16_t address_offset;
	uint32_t reg_val;
	uint32_t *EP_Config_Space = (uint32_t *)0x70100000;

	//get 2-byte address offset through UART from GUI
	uart_polled_rx((uint8_t *)&address_offset,2);

	//read configspace register with required address offset
	reg_val = *(EP_Config_Space + address_offset/4);

	//send 4-byte register value to GUI
	UART_send(&this_uart, (uint8_t *)&reg_val,4);

}

/******function to initiate EP DMA0******/
uint32_t dma_rp_to_ep(uint32_t rp_addr, uint32_t ep_addr, uint32_t dma_size )
{
	uint32_t dma_status,reg_val,clk_freq;

	PCIE_BRIDGE_TypeDef * pcie_ep_bridge_reg;
	pcie_ep_bridge_reg =  ( PCIE_BRIDGE_TypeDef *)PCIE_EP_BRIDGE_ADDR;

	//clear EP local interrupt
	pcie_ep_bridge_reg -> ISTATUS_LOCAL = 0x0000FFFF; //istatus_local

	//EP reading from PCIe link
	//EP DMA0 from RP to EP - source (PCIe) fixed
	//destination is configured to AXIM
	pcie_ep_bridge_reg -> DMA0_DESTPARAM    = 0x4;
	pcie_ep_bridge_reg -> DMA0_SRCADDR_LDW  = PCIe_RP_AXIM_ADDR + rp_addr;//source addr
	pcie_ep_bridge_reg -> DMA0_DESTADDR_LDW = ep_addr;//address in EP fabric
	pcie_ep_bridge_reg -> DMA0_LENGTH       = dma_size;//DMA length

	//send dma size to EP AXI logic for throughput calculation
	*(EP_BAR2 + PCIE_DMA0_SIZE) = dma_size;
	*(EP_BAR2 + DMA_CNTRL_REG) = 0xA1; //start counter0
	pcie_ep_bridge_reg -> DMA0_CONTROL = 0x00001021; //start DMA0

	do{
		dma_status = pcie_ep_bridge_reg -> DMA0_STATUS;//DMA0 status
		dma_status = dma_status & 0x00FFFFFF;
	}while(dma_status == 0);

	//DMA0 clks from EP throughput monitor logic
	reg_val = *(EP_BAR2 + CLK_COUNT0_REG);
	//EP AXI clock frequency
	clk_freq = *(EP_BAR2 + CLK_FREQ_REG);

	//throughput calculation
	reg_val = (clk_freq*dma_size)/reg_val;

	//send throughput to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,2);

	//send DMA status to GUI 0-for error/1-for success
	if(dma_status !=1)
		UART_send(&this_uart,(const uint8_t *)"0",1);
	else
		UART_send(&this_uart,(const uint8_t *)"1",1);

	return dma_status;
}

/******function to initiate EP DMA1******/
uint32_t dma_ep_to_rp(uint32_t rp_addr, uint32_t ep_addr, uint32_t dma_size)
{
	uint32_t dma_status,reg_val,clk_freq;

	PCIE_BRIDGE_TypeDef * pcie_ep_bridge_reg;
	pcie_ep_bridge_reg =  ( PCIE_BRIDGE_TypeDef *)PCIE_EP_BRIDGE_ADDR;

	//clear EP local interrupt
	pcie_ep_bridge_reg -> ISTATUS_LOCAL     = 0x0000FFFF; //istatus_local

	//EP writing to PCIe link
	//for DMA1 source (AXI) and destination (PCIe) are fixed
	pcie_ep_bridge_reg -> DMA1_SRCADDR_LDW  = ep_addr;//source addr in EP fabric
	pcie_ep_bridge_reg -> DMA1_DESTADDR_LDW = PCIe_RP_AXIM_ADDR + rp_addr;//dest addr
	pcie_ep_bridge_reg -> DMA1_LENGTH       = dma_size;//dma length

	*(EP_BAR2 + DMA_CNTRL_REG) = 0xA2; //start counter1
	pcie_ep_bridge_reg -> DMA1_CONTROL      = 0x00001321; //start DMA1

	do{
		dma_status = pcie_ep_bridge_reg -> DMA1_STATUS;//dma status
		dma_status = dma_status & 0x00FFFFFF;
	}while(dma_status == 0);

	//DMA1 clocks from EP throughput monitor logic
	reg_val = *(EP_BAR2 + CLK_COUNT1_REG);
	//EP AXI clock frequency
	clk_freq = *(EP_BAR2 + CLK_FREQ_REG);

	//throughput calculation
	reg_val = (clk_freq*dma_size)/reg_val;

	//send throughput to GUI
	UART_send(&this_uart,(uint8_t *)&reg_val,2);

	//send DMA status to GUI 0-for error/1-for success
	if(dma_status !=1)
		UART_send(&this_uart,(const uint8_t *)"0",1);
	else
		UART_send(&this_uart,(const uint8_t *)"1",1);

	return dma_status;
}

int main()
{

	uint32_t i,reg_val,ep_addr,dma_size,bar2_offset;
	uint32_t dma0_status,dma1_status;
	uint32_t *RP_MEM;
	uint32_t mem_pattern,rp_address_offset,ep_address_offset,data_offset;

	PCIE_BRIDGE_TypeDef * pcie_rp_bridge_reg;
	pcie_rp_bridge_reg =  ( PCIE_BRIDGE_TypeDef *)PCIE_RP_BRIDGE_ADDR;

	PCIE_BRIDGE_TypeDef * pcie_ep_bridge_reg;
	pcie_ep_bridge_reg =  ( PCIE_BRIDGE_TypeDef *)PCIE_EP_BRIDGE_ADDR;

//	//RISC-V interrupt initialization
      // MRV32_init();
//	//PLIC_SetPriority(External_1_IRQn, 1);
//MIVRV32_set_priority(MIVRV32_EXT_IRQn,1);

//	HAL_enable_interrupts();

#ifndef MIV_LEGACY_RV32

//	MRV_enable_local_irq(MIVRV32_EXT_IRQn);

#endif



//#ifndef MIV_LEGACY_RV32
//
 //  MRV_enable_local_irq(MIVRV32_EXT_IRQn);
//
//#endif

	//CoreUART initialization
	uint32_t temp;
	UART_init( &this_uart, COREUARTAPB0_BASE_ADDR ,
			BAUD_VALUE_115200, (DATA_8_BITS | NO_PARITY));
			
	temp = *((uint32_t*)(PCIE_RP_APB_ADDR + CTRL_REG_OFFSET +0x150));
	temp = (temp & 0xFFFFFFFE);
	*((uint32_t*)(PCIE_RP_APB_ADDR + CTRL_REG_OFFSET + 0x150)) = temp;
	delay(100000);
	*((uint32_t*)(PCIE_RP_APB_ADDR + CTRL_REG_OFFSET + 0x150)) = (temp | 0x00000001);
	delay(100000);

	//for handshake with GUI
	while(1)
	{
		while(!(UART_get_rx(&this_uart, rx_buff, 1 )));

		if(rx_buff[0] == 'h')
		{
			UART_send(&this_uart,(const uint8_t *)"b",1);
			break;
		}
	}

	pcie_wait_for_L0();
	pcie_rp_config_init();
	HAL_enable_interrupts();
	MRV_enable_local_irq(MRV32_EXT_IRQn);

	//PLIC_EnableIRQ(External_1_IRQn);
//MRV_enable_irq(MIVRV32_EXT_IRQn);

	while(1)
	{
		//enable local interrupt for MSI
		pcie_rp_bridge_reg -> IMASK_LOCAL   = 0xFF000000;
		//clear all local interrupts
		pcie_rp_bridge_reg -> ISTATUS_LOCAL = 0xFFFFFFFF;

		//get command from GUI
		while(!(UART_get_rx(&this_uart, rx_buff, 1 )));

		switch(rx_buff[0])
		{
		case '1':
			/* for blinking LEDs on EP board */

			//get byte value to write to LEDs
			while(!(UART_get_rx(&this_uart, rx_buff, 1 )));

			//write received value to EP LED register
			*(EP_BAR2 + LED_ADDRESS)= rx_buff[0];
			//send ACK to GUI - 1-for success
			UART_send(&this_uart,(const uint8_t *)"1",1);

			break;

		case '2':
			/* for DIP SW status on EP board */

			//get DIP SW status
			reg_val = *(EP_BAR2 + DIP_SWITCH_REG);
			//send DIP SW status to GUI
			UART_send(&this_uart,(uint8_t *)&reg_val,1);
			//send ACK to GUI - 1-for success
			UART_send(&this_uart,(const uint8_t *)"1",1);
			break;

		case '3':
			/* for interrupt count */
			HAL_enable_interrupts();
			MRV_enable_local_irq(MRV32_EXT_IRQn);
			//MRV_enable_local_irq(MIVRV32_EXT_IRQn);
			//get count clear - '1'/ read count - '0' command from GUI
			while(!(UART_get_rx(&this_uart, rx_buff, 1 )));

			if(rx_buff[0] == '0')//send interrupt count to GUI
			{
				UART_send(&this_uart,&count1,1);
				UART_send(&this_uart,&count2,1);
				UART_send(&this_uart,&count3,1);
				UART_send(&this_uart,&count4,1);

			}
			else//clear interrupt count
			{
				HAL_disable_interrupts();
				MRV_disable_local_irq(MRV32_EXT_IRQn);
				//MRV_disable_local_irq(MIVRV32_EXT_IRQn);
				count1 = 0;
				count2 = 0;
				count3 = 0;
				count4 = 0;
			}
			//send ACK to GUI - 1-for success
			UART_send(&this_uart,(const uint8_t *)"1",1);

			break;

		case '4':
			/* BAR read/write to DDR/LSRAM */

			//get ddr/lsram
			while(!(UART_get_rx(&this_uart, rx_buff, 1 )));

			//remapping BAR2 to EP LSRAM/DDR3 using ATR2
			if(rx_buff[0] == '1')//lsram
			{
				pcie_ep_bridge_reg -> ATR2_PCIE_WIN0_TRSL_ADDR_LSB =
						PCIE_EP_LSRAM;
			}
			else if(rx_buff[0] == '2')//ddr3
			{
				pcie_ep_bridge_reg -> ATR2_PCIE_WIN0_TRSL_ADDR_LSB =
						PCIE_EP_DDR3;
			}
			else //ddr4
			{
				pcie_ep_bridge_reg -> ATR2_PCIE_WIN0_TRSL_ADDR_LSB =
						PCIE_EP_DDR4;
			}

			//get read or write
			while(!(UART_get_rx(&this_uart, rx_buff, 1 )));

			//get address
			uart_polled_rx((uint8_t *)&bar2_offset,3);
			bar2_offset = bar2_offset & 0xFFFFFF;
			//write
			if(rx_buff[0] == '1')
			{
				//get data
				uart_polled_rx((uint8_t *)&reg_val,4);
				//write to BAR2
				*(EP_BAR2 + bar2_offset/4) = reg_val;
			}
			//read
			else if(rx_buff[0] == '2')
			{
				//BAR2 read
				reg_val = *(EP_BAR2 + bar2_offset/4);
			    //send read data to GUI
			    UART_send(&this_uart,(uint8_t *)&reg_val,4);
			}
			//4KB read
			else if(rx_buff[0] == '3')
			{
				//BAR2 read
				for(i=0;i<1024;i++)
				{
					reg_val = *(EP_BAR2 + bar2_offset/4 + i);
					//send read data to GUI
					UART_send(&this_uart,(uint8_t *)&reg_val,4);
				}
			}
			//send ACK to GUI - 1-for success
			UART_send(&this_uart,(const uint8_t *)"1",1);

			//re-mapping BAR2 to EP IO registers using ATR2
			pcie_ep_bridge_reg -> ATR2_PCIE_WIN0_TRSL_ADDR_LSB =
					PCIE_EP_IO_CTRL;
			break;

		case '5':
			/* DMA operation of type:
			1.	RP mem to EP LSRAM
			2.	EP LSRAM to RP mem
			3.	Both RP mem <--> EP LSRAM
			4.	RP mem to EP DDR3
			5.	EP DDR3 to RP mem
			6.	Both RP mem <--> EP DDR3
			7.	RP mem to EP DDR4
			8.	EP DDR4 to RP mem
			9.	Both RP mem <--> EP DDR4

			 */

			//dma size
			while(!(UART_get_rx(&this_uart, rx_buff, 1 )));
			dma_size = rx_buff[0] * 4096;

			//RP mem type
			while(!(UART_get_rx(&this_uart, rx_buff, 1 )));

			if(rx_buff[0] == 1)
			{
				pcie_rp_bridge_reg -> ATR0_PCIE_WIN0_TRSL_ADDR_LSB =
										0x00000000;//RP LSRAM
			}
			else
			{
				pcie_rp_bridge_reg -> ATR0_PCIE_WIN0_TRSL_ADDR_LSB =
										0x10000000;//RP DDR4
			}

			//dma type
			while(!(UART_get_rx(&this_uart, rx_buff, 1 )));

			//select EP memory for DMA
			if(rx_buff[0] == 1 || rx_buff[0] == 2 || rx_buff[0] == 3 )
				ep_addr = PCIE_EP_LSRAM;
			else if(rx_buff[0] == 4 || rx_buff[0] == 5 || rx_buff[0] == 6 )
				ep_addr = PCIE_EP_DDR3;
			else
				ep_addr = PCIE_EP_DDR4;

			rp_address_offset = 0;
			dma0_status = 1;
			dma1_status = 1;

			//EP to RP - DMA1
			if(rx_buff[0] == 2 || rx_buff[0] == 5 || rx_buff[0] == 8 )
			{
				//send dummy throughput to GUI for display DMA0 throughput
				reg_val =0;
				UART_send(&this_uart,(uint8_t *)&reg_val,2);
				//send dummy ACK to GUI - 1-for success
				UART_send(&this_uart,(uint8_t *)"1",1);

				//EP to RP DMA - DMA1
				dma1_status = dma_ep_to_rp(rp_address_offset,ep_addr, dma_size);
			}
			//RP to EP DMA - DMA0
			else if(rx_buff[0] == 1 || rx_buff[0] == 4 || rx_buff[0] == 7 )
			{
				//RP to EP DMA - DMA0
				dma0_status = dma_rp_to_ep(rp_address_offset,ep_addr, dma_size);

				//send dummy throughput to GUI for display DMA1 throughput
				reg_val =0;
				UART_send(&this_uart,(uint8_t *)&reg_val,2);
				//send ACK to GUI - 1-for success
				UART_send(&this_uart,(uint8_t *)"1",1);
			}
			else
			{
				//RP to EP DMA - DMA0
				dma0_status = dma_rp_to_ep(rp_address_offset,ep_addr, dma_size);
				//EP to RP DMA - DMA1
				dma1_status = dma_ep_to_rp(rp_address_offset,ep_addr, dma_size);
			}

			//send dma status to GUI on DMA0 error
			if(dma0_status != 1)
				UART_send(&this_uart,(uint8_t *)&dma0_status,4);

			//send dma status to GUI on DMA1 error
			if(dma1_status != 1)
				UART_send(&this_uart,(uint8_t *)&dma1_status,4);

			break;

		case '6':
			/******Mem test*****/
			//dma size
			while(!(UART_get_rx(&this_uart, rx_buff, 1 )));
			dma_size = rx_buff[0] * 4096;

			//mem pattern
			while(!(UART_get_rx(&this_uart, rx_buff, 1 )));
			mem_pattern = rx_buff[0];

			//user data
			uart_polled_rx((uint8_t *)&data_offset,4);

			//RP mem type
			while(!(UART_get_rx(&this_uart, rx_buff, 1 )));

			if(rx_buff[0] == 1)
			{
				pcie_rp_bridge_reg -> ATR0_PCIE_WIN0_TRSL_ADDR_LSB =
										0x00000000;//RP LSRAM

				pcie_rp_bridge_reg -> ATR3_AXI4_SLV0_TRSL_ADDR_LSB =
										0x00000000;//RP LSRAM
			}
			else
			{
				pcie_rp_bridge_reg -> ATR0_PCIE_WIN0_TRSL_ADDR_LSB =
										0x10000000;//RP DDR4

				pcie_rp_bridge_reg -> ATR3_AXI4_SLV0_TRSL_ADDR_LSB =
										0x10000000;//RP DDR4
			}

			//EP mem type
			while(!(UART_get_rx(&this_uart, rx_buff, 1 )));

			if(rx_buff[0] == 1 )
				ep_addr = PCIE_EP_LSRAM;
			else if(rx_buff[0] == 2)
				ep_addr = PCIE_EP_DDR3;
			else
				ep_addr = PCIE_EP_DDR4;

			//RP address offset
			uart_polled_rx((uint8_t *)&rp_address_offset,4);
			RP_MEM = (uint32_t *) (RP_MEM_ADDR + rp_address_offset);

			//EP address offset
			uart_polled_rx((uint8_t *)&ep_address_offset,4);

			ep_addr = ep_addr + ep_address_offset;

			//initialize RP mem with mem_pattern
			/*1.	Incremental from user defined data
			  2.	Decremental from user defined data
			  3.	All 1’s
			  4.	All 0’s
			  5.	All 5’s
			  6.	All A’s
			*/
			switch(mem_pattern)
			{
			case 1:
				for( i=0;i<dma_size/4; i++)
				{
					*(RP_MEM + i)= data_offset + i;
				}

				break;

			case 2:
				for( i=0;i<dma_size/4; i++)
				{
					*(RP_MEM + i)= data_offset - i;
				}

				break;

			case 3:
				for( i=0;i<dma_size/4; i++)
				{
					*(RP_MEM + i)= 0xFFFFFFFF;
				}

				break;

			case 4:
				for( i=0;i<dma_size/4; i++)
				{
					*(RP_MEM + i)= 0;
				}

				break;

			case 5:
				for( i=0;i<dma_size/4; i++)
				{
					*(RP_MEM + i)= 0x55555555;
				}

				break;

			case 6:
				for( i=0;i<dma_size/4; i++)
				{
					*(RP_MEM + i)= 0xAAAAAAAA;
				}

				break;

			default:
				for( i=0;i<dma_size/4; i++)
				{
					*(RP_MEM + i)= i;
				}

				break;

			}

			/*******************************/
			//dma from rp mem to ep mem - DMA0
			/*******************************/
			dma0_status = dma_rp_to_ep(rp_address_offset, ep_addr, dma_size);

			//erase RP mem
			for( i=0;i<dma_size/4; i++)
			{
				*(RP_MEM + i)= 0;
			}

			/*******************************/
			//dma from ep mem to rp mem - DMA1
			/*******************************/
			dma1_status = dma_ep_to_rp(rp_address_offset, ep_addr, dma_size);

			//compare RP mem for mem pattern
			switch(mem_pattern)
			{
			case 1:
				for( i=0;i<dma_size/4; i++)
				{
					reg_val = *(RP_MEM + i);
					if(reg_val != data_offset + i)
						break;
				}

				break;

			case 2:
				for( i=0;i<dma_size/4; i++)
				{
					reg_val = *(RP_MEM + i);
					if(reg_val != data_offset - i)
						break;
				}

				break;

			case 3:
				for( i=0;i<dma_size/4; i++)
				{
					reg_val =*(RP_MEM + i);
					if(reg_val != 0xFFFFFFFF)
						break;
				}

				break;

			case 4:
				for( i=0;i<dma_size/4; i++)
				{
					reg_val = *(RP_MEM + i);
					if(reg_val != 0)
						break;
				}

				break;

			case 5:
				for( i=0;i<dma_size/4; i++)
				{
					reg_val = *(RP_MEM + i);
					if(reg_val != 0x55555555)
						break;
				}

				break;

			case 6:
				for( i=0;i<dma_size/4; i++)
				{
					reg_val = *(RP_MEM + i);
					if (reg_val != 0xAAAAAAAA)
						break;
				}

				break;

			default:
				for( i=0;i<dma_size/4; i++)
				{
					reg_val = *(RP_MEM + i);
					if(reg_val != i)
						break;
				}

				break;
			}

			//send ack - 0 if mem error
			if(i != dma_size/4)
			{
				UART_send(&this_uart,(const uint8_t *)"0",1);

				//send RP address error offset
				UART_send(&this_uart,(uint8_t *)&i,4);
			}
			else
			{
				UART_send(&this_uart,(const uint8_t *)"1",1);
			}

			break;

		case '7':
			//read RP LSRAM/DDR4 memory
			//RP mem type
			while(!(UART_get_rx(&this_uart, rx_buff, 1 )));

			if(rx_buff[0] == 1)
			{
				pcie_rp_bridge_reg -> ATR3_AXI4_SLV0_TRSL_ADDR_LSB =
						0x00000000;//RP LSRAM
			}
			else
			{
				pcie_rp_bridge_reg -> ATR3_AXI4_SLV0_TRSL_ADDR_LSB =
						0x10000000;//RP DDR
			}

			//address offset
			//get 4-byte address offset through UART from GUI
			uart_polled_rx((uint8_t *)&rp_address_offset,4);

			RP_MEM = (uint32_t *) (RP_MEM_ADDR + rp_address_offset);

			//send 1kB data
			for(i=0;i<1024/4;i++)
			{
				reg_val = *(RP_MEM +i );
				UART_send(&this_uart,(uint8_t *)&reg_val,4);
			}

			//send ACK to GUI - 1-for success
			UART_send(&this_uart,(const uint8_t *)"1",1);

			break;

		case '8':
			/* read PCIe EP configspace from 2-byte offset address*/
			read_pcie_ep_configspace();
			//send ACK to GUI - 1-for success
			UART_send(&this_uart,(const uint8_t *)"1",1);
			break;

		case '9':
			//Perform PCIe EP enumeration and send info to GUI
			pcie_ep_enumeration();

			//BAR2 RW reg test
			pcie_ep_access();
			break;

		case 'h':
			//for handshake with GUI
			UART_send(&this_uart,(const uint8_t *)"b",1);
			break;

		default:
			break;
		}

	}

}

