/*
 * Copyright (c) 2013 - 2015, Freescale Semiconductor, Inc.
 * Copyright 2016-2023 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "board.h"
#include "fsl_ctimer.h"
#include "fsl_spi.h"
/*
 * Pins used in this application:
 *
 * P0_5  [D:I] -  J30.1: GPI - CTIMER_INP1... connect to SPI5_CLK
 * P0_6  [D:O] -  J30.2: GPO - SYNCHRO indicator (active high)
 * P0_15 [D:O] -  JS6.2: GPO - TEST indicator (active high)
 * P0_16 [D:O] -  JS5.2: GPO - AUX0 indicator (test parameters indicator)
 *
 * P0_13 [D:O] -  J30.4: CTIMER0_MAT1 - add-on AHB traffic indicator (edge)
 *
 * P1_3  [D:O] - JP26.4: SPI5_CLK
 * P1_4  [D:I] - JP26.3: SPI5_MISO
 * P1_5  [D:O] - JP26.2: SPI5_MOSI
 * P1_6  [D:O] - JP26.1: SPI5_SS0
 *
 * P2_29 [D:O] - J18.1 : CLKOUT, main clock/1
 *
 * P2_15 [D:O] - J47.10: GPO - error indicator (active high)
 *
 * P5_0  [D:O] - J28.3 : GPO - RXed data bit 0
 * P5_1  [D:O] - J28.4 : GPO - RXed data bit 1
 * P5_2  [D:O] - J28.5 : GPO - RXed data bit 2
 * P5_3  [D:O] - J28.6 : GPO - RXed data bit 3
 *
 * P0_11 [D:O] - J47.8 : GPO - AHB port0 add-on traffic/DMA activity indicator (edge)
 * P1_10 [D:O] - J27.2 : GPO - AHB port1 add-on traffic/DMA activity indicator (edge)
 *
 */

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define IOCON_IN_NOPUPD(x)	\
	(IOPCTL_PIO_FSEL(x) 	|	\
	 IOPCTL_PIO_PUPDENA(0)	| IOPCTL_PIO_IBENA(1)	| IOPCTL_PIO_FULLDRIVE(0)	| \
     IOPCTL_PIO_AMENA(0)	| IOPCTL_PIO_ODENA(0)	| IOPCTL_PIO_IIENA(0))

// FC5 SPI
#define SPI_SS_PORT		1
#define SPI_SS_PIN		6
#define	SPI_SS_FUNC		1

#define SPI_CLK_PORT	1
#define SPI_CLK_PIN		3
#define	SPI_CLK_FUNC	1

#define SPI_MOSI_PORT	1
#define SPI_MOSI_PIN	5
#define	SPI_MOSI_FUNC	1

#define SPI_MISO_PORT	1
#define SPI_MISO_PIN	4
#define	SPI_MISO_FUNC	1

#define	SYNCHRO_PORT	0
#define	SYNCHRO_PIN		6
#define	TEST_PORT		0
#define	TEST_PIN		15
#define	AUX0_PORT		0
#define	AUX0_PIN		16
#define ERROR_PORT		2
#define	ERROR_PIN		15

#define CLKOUT_PORT		2
#define	CLKOUT_PIN		29
#define	CLKOUT_FUNC		5

#define	CTIMER_MAT_PORT	0
#define CTIMER_MAT_PIN	13
#define	CTIMER_MAT_FUNC	4

#define	CTIMER_INP_PORT	0
#define	CTIMER_INP_PIN	5
#define	CTIMER_INP_FUNC	4

#define	RXDATA0_PORT	5
#define RXDATA0_PIN		0
#define	RXDATA1_PORT	5
#define RXDATA1_PIN		1
#define	RXDATA2_PORT	5
#define RXDATA2_PIN		2
#define	RXDATA3_PORT	5
#define RXDATA3_PIN		3

#define TEST_ARRAY_SIZE	63

#define MAIN2FCLK_MIN	1
#define MAIN2FCLK_MAX	1
#define	FCLK2SCLK_MIN	3
#define	FCLK2SCLK_MAX	3
#define CPUCLKDIV_MIN	1
#define	CPUCLKDIV_MAX	1

#define DEMO_DMA_CH_PERI_REQ	37
enum demo_dma_main_descriptor_enum
{
	dma_desc_fc5_rx_main     = 10,
	dma_desc_fc5_tx_main     = 11,
	dma_desc_port0_wr_main   = 28,
	dma_desc_port1_wr_main   = 29,
	dma_desc_timer_ctrl_main = 30
};
struct DEMO_DESCRIPTOR_TYPE_T
{
	uint32_t xfercfg;
	uint32_t src_addr;
	uint32_t des_addr;
	uint32_t link;
};
__attribute__((aligned(1024))) volatile struct DEMO_DESCRIPTOR_TYPE_T demo_dma_main_descriptor[DEMO_DMA_CH_PERI_REQ];

#define SPI_DATA_SIZE	8
#define	SPI_DATA_MASK	((1<<SPI_DATA_SIZE)-1)

#define TEST_CPOL		0
#define TEST_CPHA		0

#define	ADDONT_0_PORT	0
#define	ADDONT_0_PIN	11
#define	ADDONT_1_PORT	1
#define	ADDONT_1_PIN	10

#define	DESTINATION_SRAM	0
#define DESTINATION_PORT	1
#define	TEST_DESTINATION	DESTINATION_PORT

// test case : multiple failures
#define DMACH_RX_PRIO		2
#define DMACH_TX_PRIO		3
#define DMACH_ADDLOAD0_PRIO	0
#define DMACH_ADDLOAD1_PRIO	1

#define	ADDLOAD1_BURST		2
#define	FRAME_DELAY			0

#define ENABLE_ADDLOAD0		1
#define ENABLE_ADDLOAD1		1

#define BYTES_PER_TEST		2
#define	STOP_AND_GO			0

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
volatile uint32_t sw_gate, sw_gate_count;
volatile uint32_t main_loop_count;
volatile uint32_t dummy32bit;
volatile uint32_t spi_tx_array[TEST_ARRAY_SIZE], spi_rx_array[TEST_ARRAY_SIZE];

volatile uint32_t i_aux;
volatile uint32_t i_cpuclkdiv, i_main2fclk, i_fclk2sclk;
volatile uint32_t i_tx, i_rx;

volatile uint32_t spi_isr_count;

void delay_sw(uint32_t count);
void init_mcu(void);
void init_spi(void);
void init_dma(void);
void prepare_arrays(void);
void update_spi(uint32_t spi_div);
void verify_received_data(void);
void init_ctimer0(void);
void update_ctimer0(uint32_t match, uint32_t period);
void stop_ctimer0(void);

void init_ctimer0_dma_addon(void);
void update_ctimer0_dma_addon(void);

#define  PORT_WR_ARRAY 64
volatile uint32_t port0_wr_mask[PORT_WR_ARRAY], port1_wr_mask[PORT_WR_ARRAY], dma_timer_tcr;

volatile uint32_t test_period, i_test_period;

spi_master_config_t masterConfig;
/*******************************************************************************
 * Code
 ******************************************************************************/
/*!
 * @brief Main function
 */
int main(void)
{
    /* Init board hardware. */
    BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();

    init_mcu();
    init_spi();
	init_ctimer0();
    init_dma();

#if ((ENABLE_ADDLOAD0 == 1) || (ENABLE_ADDLOAD1 == 1))
    init_ctimer0_dma_addon();
#endif

    // synchro pulse - use this to trigger the scope/logic
    GPIO->CLR[SYNCHRO_PORT] = 1<<SYNCHRO_PIN;
    delay_sw(200);
    GPIO->SET[SYNCHRO_PORT] = 1<<SYNCHRO_PIN;
    delay_sw(200);
    GPIO->CLR[SYNCHRO_PORT] = 1<<SYNCHRO_PIN;
    delay_sw(200);

    for (i_cpuclkdiv = CPUCLKDIV_MIN; i_cpuclkdiv != (CPUCLKDIV_MAX+1); i_cpuclkdiv++)
    {
		for (i_main2fclk = MAIN2FCLK_MAX; i_main2fclk != (MAIN2FCLK_MIN-1); i_main2fclk--)
		{
			for (i_fclk2sclk = FCLK2SCLK_MAX; i_fclk2sclk != (FCLK2SCLK_MIN-1); i_fclk2sclk--)
			{

				// test_period = 8 bits * bytes_per_test * 2 (edges, CTIMER0 counts both re and fe)
				test_period = 8*BYTES_PER_TEST*2;
				for (i_test_period = 2; i_test_period != (test_period)-2; i_test_period++)
				{

					// indicate test start
					GPIO->SET[TEST_PORT] = 1<<TEST_PIN;
					delay_sw(50);

					// output test parameters
					for (i_aux = 0; i_aux != i_test_period; i_aux++)
					{
						GPIO->SET[AUX0_PORT] = 1<<AUX0_PIN;
						delay_sw(10);
						GPIO->CLR[AUX0_PORT] = 1<<AUX0_PIN;
						delay_sw(10);
					}
					delay_sw(50);

					for (i_aux = 0; i_aux != i_cpuclkdiv; i_aux++)
					{
						GPIO->SET[AUX0_PORT] = 1<<AUX0_PIN;
						delay_sw(10);
						GPIO->CLR[AUX0_PORT] = 1<<AUX0_PIN;
						delay_sw(10);
					}
					delay_sw(50);

					for (i_aux = 0; i_aux != i_main2fclk; i_aux++)
					{
						GPIO->SET[AUX0_PORT] = 1<<AUX0_PIN;
						delay_sw(10);
						GPIO->CLR[AUX0_PORT] = 1<<AUX0_PIN;
						delay_sw(10);
					}
					delay_sw(50);

					for (i_aux = 0; i_aux != i_fclk2sclk; i_aux++)
					{
						GPIO->SET[AUX0_PORT] = 1<<AUX0_PIN;
						delay_sw(10);
						GPIO->CLR[AUX0_PORT] = 1<<AUX0_PIN;
						delay_sw(10);
					}
					delay_sw(50);

				    // SYSCPUAHBCLKDIV = i_cpuclkdiv
				    // halt, reset, dir, release reset, release halt
				    CLKCTL0->SYSCPUAHBCLKDIV = 1<<30;
				    CLKCTL0->SYSCPUAHBCLKDIV = 1<<30 | 1<<29 | (i_main2fclk-1)<<0;
				    CLKCTL0->SYSCPUAHBCLKDIV = 1<<30 | 0<<29 | (i_main2fclk-1)<<0;
				    CLKCTL0->SYSCPUAHBCLKDIV = 0<<30 | 0<<29 | (i_main2fclk-1)<<0;

				    // FRGPLLCLKDIV = i_main2fclk
				    // halt, reset, dir, release reset, release halt
				    CLKCTL1->FRGPLLCLKDIV = 1<<30;
				    CLKCTL1->FRGPLLCLKDIV = 1<<30 | 1<<29 | (i_main2fclk-1)<<0;
				    CLKCTL1->FRGPLLCLKDIV = 1<<30 | 0<<29 | (i_main2fclk-1)<<0;
				    CLKCTL1->FRGPLLCLKDIV = 0<<30 | 0<<29 | (i_main2fclk-1)<<0;

				    // CLKOUTFCLKDIV = 1: halt, reset, dir, release reset, release halt
				    CLKCTL1->CLKOUTFCLKDIV = 1<<30;
				    CLKCTL1->CLKOUTFCLKDIV = 1<<30 | 1<<29 | (1-1)<<0;
				    CLKCTL1->CLKOUTFCLKDIV = 1<<30 | 0<<29 | (1-1)<<0;
				    CLKCTL1->CLKOUTFCLKDIV = 0<<30 | 0<<29 | (1-1)<<0;


					update_spi(i_fclk2sclk);
					prepare_arrays();

					stop_ctimer0();
					update_ctimer0(i_test_period, test_period);

#if ((ENABLE_ADDLOAD0 == 1) || (ENABLE_ADDLOAD1 == 1))
					update_ctimer0_dma_addon();
#endif

					// make sure SSD @ STAT is 0
					SPI5->STAT = 1<<5;


					//
					// prepare SPI rx DMA channel
					//===========================
					demo_dma_main_descriptor[dma_desc_fc5_rx_main].xfercfg	= 0;
					demo_dma_main_descriptor[dma_desc_fc5_rx_main].src_addr	= (uint32_t)&SPI5->FIFORD;

				#if TEST_DESTINATION ==	DESTINATION_SRAM
					demo_dma_main_descriptor[dma_desc_fc5_rx_main].des_addr	= (uint32_t)&spi_rx_array[TEST_ARRAY_SIZE-1];
				#else
					demo_dma_main_descriptor[dma_desc_fc5_rx_main].des_addr	= (uint32_t)&GPIO->PIN[5];
				#endif
					demo_dma_main_descriptor[dma_desc_fc5_rx_main].link		= 0;

					DMA0->CHANNEL[dma_desc_fc5_rx_main].XFERCFG =
						1<<0 	|	// valid configuration
						0<<1 	|	// no link/reload
						1<<2	|	// sw trigger
						1<<3	|	// clear trigger at the end
						0<<4 	|	// no int A at the end
						0<<5	|	// no int B at the end
						2<<8 	|	// 32-bit transfers
						0<<12	|	// src: +0
#if TEST_DESTINATION ==	DESTINATION_SRAM
						1<<14	|	// dst: +1
#else
						0<<14	|	// dst: +0
#endif
						(TEST_ARRAY_SIZE-1)<<16;	//transfer count...;


					//
					// prepare SPI tx DMA channel
					//===========================
					demo_dma_main_descriptor[dma_desc_fc5_tx_main].xfercfg	= 0;
					demo_dma_main_descriptor[dma_desc_fc5_tx_main].src_addr	= (uint32_t)&spi_tx_array[TEST_ARRAY_SIZE-1];
					demo_dma_main_descriptor[dma_desc_fc5_tx_main].des_addr	= (uint32_t)&SPI5->FIFOWR;
					demo_dma_main_descriptor[dma_desc_fc5_tx_main].link		= 0;

					DMA0->CHANNEL[dma_desc_fc5_tx_main].XFERCFG =
						1<<0 	|	// valid configuration
						0<<1 	|	// no link/reload
						1<<2	|	// sw trigger
						1<<3	|	// clear trigger at the end
						0<<4 	|	// no int A at the end
						0<<5	|	// no int B at the end
						2<<8 	|	// 32-bit transfers
						1<<12	|	// src: +1
						0<<14	|	// dst: +0
						(TEST_ARRAY_SIZE-1)<<16;	//transfer count...;


					// hw ready, go to sleep mode and let CTIMER0, SPI5 and DMA run the demo
					__WFI();

					// SSD will wake up device before DMA is done, wait for rx data to be drained
					while((SPI5->FIFOSTAT & SPI_FIFOSTAT_RXNOTEMPTY_MASK) != 0);

					// update ERROR indicator based on the RXERR bit
					if ((SPI5->FIFOSTAT & SPI_FIFOSTAT_RXERR_MASK) != 0)
					{
						GPIO->SET[ERROR_PORT] = 1<<ERROR_PIN;
						delay_sw(20);
						GPIO->CLR[ERROR_PORT] = 1<<ERROR_PIN;
						delay_sw(20);
					}

					stop_ctimer0();

#if TEST_DESTINATION ==	DESTINATION_SRAM
					verify_received_data();
#endif

					// clear the rx port
					GPIO->CLR[RXDATA0_PORT] = 1<<RXDATA0_PIN | 1<<RXDATA1_PIN | 1<<RXDATA2_PIN | 1<<RXDATA3_PIN;

					// indicate test stop
					GPIO->CLR[TEST_PORT] = 1<<TEST_PIN;
				}// end of CTIMER0 generated offset loop

				delay_sw(50);

			}// end of FCLK2SCLK loop
		}// end of MAIN2FCLK loop
    }// end of CPUCLKDIV loop

    main_loop_count = 0;
    while(1)
    {
    	main_loop_count++;
    }
}


void delay_sw(uint32_t count)
{
	while(count != 0)
	{
		count--;
	}

	return;
}


void init_mcu(void)
{
	/* make sure all GPIOs are available */
	CLOCK_EnableClock(kCLOCK_HsGpio0);
	CLOCK_EnableClock(kCLOCK_HsGpio1);
	CLOCK_EnableClock(kCLOCK_HsGpio2);
	CLOCK_EnableClock(kCLOCK_HsGpio3);
	CLOCK_EnableClock(kCLOCK_HsGpio4);
	CLOCK_EnableClock(kCLOCK_HsGpio5);

	RESET_PeripheralReset(kHSGPIO0_RST_SHIFT_RSTn);
	RESET_PeripheralReset(kHSGPIO1_RST_SHIFT_RSTn);
	RESET_PeripheralReset(kHSGPIO2_RST_SHIFT_RSTn);
	RESET_PeripheralReset(kHSGPIO3_RST_SHIFT_RSTn);
	RESET_PeripheralReset(kHSGPIO4_RST_SHIFT_RSTn);
	RESET_PeripheralReset(kHSGPIO5_RST_SHIFT_RSTn);

	CLOCK_EnableClock(kCLOCK_InputMux);
	RESET_PeripheralReset(kINPUTMUX_RST_SHIFT_RSTn);

	/* CLKOUT: SEL0 = Main Clock (main_clk), SEL1 = SEL0, divide by 100 */
	CLKCTL1->CLKOUTSEL0 = CLKCTL1_CLKOUTSEL0_SEL(3);
	CLKCTL1->CLKOUTSEL1 = CLKCTL1_CLKOUTSEL1_SEL(0);
	CLKCTL1->CLKOUTFCLKDIV = CLKCTL1_CLKOUTFCLKDIV_HALT(1);
	CLKCTL1->CLKOUTFCLKDIV = CLKCTL1_CLKOUTFCLKDIV_HALT(1) | CLKCTL1_CLKOUTFCLKDIV_RESET(1) | CLKCTL1_CLKOUTFCLKDIV_DIV(1-1);
	CLKCTL1->CLKOUTFCLKDIV = CLKCTL1_CLKOUTFCLKDIV_HALT(1) | CLKCTL1_CLKOUTFCLKDIV_RESET(0) | CLKCTL1_CLKOUTFCLKDIV_DIV(1-1);
	CLKCTL1->CLKOUTFCLKDIV = CLKCTL1_CLKOUTFCLKDIV_HALT(0) | CLKCTL1_CLKOUTFCLKDIV_RESET(0) | CLKCTL1_CLKOUTFCLKDIV_DIV(1-1);
	IOPCTL->PIO[CLKOUT_PORT][CLKOUT_PIN] = IOCON_IN_NOPUPD(CLKOUT_FUNC);

    // prime the test indicators
    //==========================
    GPIO->CLR[SYNCHRO_PORT]    = 1<<SYNCHRO_PIN;
    GPIO->DIRSET[SYNCHRO_PORT] = 1<<SYNCHRO_PIN;

    GPIO->CLR[TEST_PORT]    = 1<<TEST_PIN;
    GPIO->DIRSET[TEST_PORT] = 1<<TEST_PIN;

    GPIO->CLR[AUX0_PORT]    = 1<<AUX0_PIN;
    GPIO->DIRSET[AUX0_PORT] = 1<<AUX0_PIN;

    GPIO->CLR[ERROR_PORT]    = 1<<ERROR_PIN;
    GPIO->DIRSET[ERROR_PORT] = 1<<ERROR_PIN;

    // update received data bus
    GPIO->CLR[RXDATA0_PORT]    = 1<<RXDATA0_PIN;
    GPIO->DIRSET[RXDATA0_PORT] = 1<<RXDATA0_PIN;
    GPIO->CLR[RXDATA1_PORT]    = 1<<RXDATA1_PIN;
    GPIO->DIRSET[RXDATA1_PORT] = 1<<RXDATA1_PIN;
    GPIO->CLR[RXDATA2_PORT]    = 1<<RXDATA2_PIN;
    GPIO->DIRSET[RXDATA2_PORT] = 1<<RXDATA2_PIN;
    GPIO->CLR[RXDATA3_PORT]    = 1<<RXDATA3_PIN;
    GPIO->DIRSET[RXDATA3_PORT] = 1<<RXDATA3_PIN;

	return;
}


void init_spi(void)
{
	CLOCK_SetFRGClock(&(const clock_frg_clk_config_t){5, kCLOCK_FrgMainClk, 255, 0});
	CLOCK_AttachClk(kFRG_to_FLEXCOMM5);
	CLOCK_EnableClock(kCLOCK_Spi5);

	// select FC5 as SPI
	FLEXCOMM5->PSELID = FLEXCOMM_PSELID_PERSEL(2);


	// FC5_SCK @ ...
	IOPCTL->PIO[SPI_SS_PORT  ][SPI_SS_PIN  ] = IOCON_IN_NOPUPD(SPI_SS_FUNC  );
	IOPCTL->PIO[SPI_CLK_PORT ][SPI_CLK_PIN ] = IOCON_IN_NOPUPD(SPI_CLK_FUNC );
	IOPCTL->PIO[SPI_MOSI_PORT][SPI_MOSI_PIN] = IOCON_IN_NOPUPD(SPI_MOSI_FUNC);
	IOPCTL->PIO[SPI_MISO_PORT][SPI_MISO_PIN] = IOCON_IN_NOPUPD(SPI_MISO_FUNC);

	SPI_MasterGetDefaultConfig(&masterConfig);
	SPI_MasterInit(SPI5, &masterConfig, CLOCK_GetFlexcommClkFreq(5));

    // get ready for future transfers
    while((SPI5->FIFOSTAT & SPI_FIFOSTAT_RXNOTEMPTY_MASK) != 0)
    {
    	dummy32bit = SPI5->FIFORD;
    }

    // get ready for some interrupts
    spi_isr_count = 0;

	return;
}


void init_dma(void)
{
	// DMA, INPUTMUX
	CLOCK_EnableClock(kCLOCK_Dmac0);
	RESET_PeripheralReset(kDMAC0_RST_SHIFT_RSTn);

	CLOCK_EnableClock(kCLOCK_InputMux);
	RESET_PeripheralReset(kINPUTMUX_RST_SHIFT_RSTn);

    DMA0->SRAMBASE = (uint32_t)&demo_dma_main_descriptor[0];
    DMA0->CTRL = 1;


    INPUTMUX->DMAC0_REQ_ENA0_SET = 1<<dma_desc_fc5_rx_main;
	demo_dma_main_descriptor[dma_desc_fc5_rx_main].xfercfg	= 0;
	demo_dma_main_descriptor[dma_desc_fc5_rx_main].src_addr	= (uint32_t)&SPI5->FIFORD;

#if TEST_DESTINATION ==	DESTINATION_SRAM
	demo_dma_main_descriptor[dma_desc_fc5_rx_main].des_addr	= (uint32_t)&spi_rx_array[TEST_ARRAY_SIZE-1];
#else
	demo_dma_main_descriptor[dma_desc_fc5_rx_main].des_addr	= (uint32_t)&GPIO->PIN[5];
#endif

	demo_dma_main_descriptor[dma_desc_fc5_rx_main].link		= 0;

	DMA0->COMMON[0].ENABLESET = 1<<dma_desc_fc5_rx_main;
	DMA0->COMMON[0].SETVALID  = 1<<dma_desc_fc5_rx_main;

	DMA0->CHANNEL[dma_desc_fc5_rx_main].CFG =
		1<<0	|	// peripheral req enabled
		0<<1	|	// hw trigger not used
		DMACH_RX_PRIO<<16;		// priority = 0

	DMA0->CHANNEL[dma_desc_fc5_rx_main].XFERCFG =
		1<<0 	|	// valid configuration
		0<<1 	|	// no link/reload
		0<<2	|	// no sw trigger
		1<<3	|	// clear trigger at the end
		0<<4 	|	// no int A at the end
		0<<5	|	// no int B at the end
		2<<8 	|	// 32-bit transfers
		0<<12	|	// src: +0
#if TEST_DESTINATION ==	DESTINATION_SRAM
		1<<14	|	// dst: +1
#else
		0<<14	|	// dst: +0
#endif
		(TEST_ARRAY_SIZE-1)<<16;	//transfer count...;


    INPUTMUX->DMAC0_REQ_ENA0_SET = 1<<dma_desc_fc5_tx_main;
	demo_dma_main_descriptor[dma_desc_fc5_tx_main].xfercfg	= 0;
	demo_dma_main_descriptor[dma_desc_fc5_tx_main].src_addr	= (uint32_t)&spi_tx_array[TEST_ARRAY_SIZE-1];
	demo_dma_main_descriptor[dma_desc_fc5_tx_main].des_addr	= (uint32_t)&SPI5->FIFOWR;
	demo_dma_main_descriptor[dma_desc_fc5_tx_main].link		= 0;

	DMA0->COMMON[0].ENABLESET = 1<<dma_desc_fc5_tx_main;
	DMA0->COMMON[0].SETVALID  = 1<<dma_desc_fc5_tx_main;

	DMA0->CHANNEL[dma_desc_fc5_tx_main].CFG =
		1<<0	|	// peripheral req enabled
		0<<1	|	// hw trigger not used
		1<<16;		// priority = 1

	DMA0->CHANNEL[dma_desc_fc5_tx_main].XFERCFG =
		1<<0 	|	// valid configuration
		0<<1 	|	// no link/reload
		0<<2	|	// no sw trigger
		1<<3	|	// clear trigger at the end
		0<<4 	|	// no int A at the end
		0<<5	|	// no int B at the end
		2<<8 	|	// 32-bit transfers
		1<<12	|	// src: +1
		0<<14	|	// dst: +0
		(TEST_ARRAY_SIZE-1)<<16;	//transfer count...;

	return;
}


void prepare_arrays(void)
{
	uint32_t i_loc;

	// rx array
	for (i_loc = 0; i_loc != TEST_ARRAY_SIZE; i_loc++)
	{
		spi_rx_array[i_loc] = 0xDEADC0DE;
	}

	for (i_loc = 0; i_loc != TEST_ARRAY_SIZE; i_loc++)
	{
		spi_tx_array[i_loc] =
				SPI_FIFOWR_LEN(SPI_DATA_SIZE-1) |	// len
				SPI_FIFOWR_TXIGNORE(0)			|	// TXIGNORE
				SPI_FIFOWR_RXIGNORE(0)			|	// RXIGNORE
				SPI_FIFOWR_EOF(1)				|	// EOF
				SPI_FIFOWR_EOT(0)				|	// EOT
				SPI_FIFOWR_TXSSEL3_N(1)			|	// TXSSEL3
				SPI_FIFOWR_TXSSEL2_N(1)			|	// TXSSEL2
				SPI_FIFOWR_TXSSEL1_N(1)			|	// TXSSEL1
				SPI_FIFOWR_TXSSEL0_N(0)			|	// TXSSEL0
				SPI_FIFOWR_TXDATA((i_loc+1) & SPI_DATA_MASK); // TXDATA
	}

	spi_tx_array[TEST_ARRAY_SIZE-1] |= SPI_FIFOWR_EOT(1);	// EOT @ last frame

	return;
}


void update_spi(uint32_t spi_div)
{
	NVIC_DisableIRQ(FLEXCOMM5_IRQn);

	// reset FC5/SPI5
	RESET_PeripheralReset(kFC5_RST_SHIFT_RSTn);

	// select FC5 as SPI
	FLEXCOMM5->PSELID = FLEXCOMM_PSELID_PERSEL(2);

	SPI_MasterGetDefaultConfig(&masterConfig);
	masterConfig.baudRate_Bps = (CLOCK_GetFlexcommClkFreq(5) / spi_div);
	SPI_MasterInit(SPI5, &masterConfig, CLOCK_GetFlexcommClkFreq(5));
	SPI_EnableTxDMA(SPI5, true);
	SPI_EnableRxDMA(SPI5, true);

    // get ready for future transfers
    while((SPI5->FIFOSTAT & SPI_FIFOSTAT_RXNOTEMPTY_MASK) != 0)
    {
    	dummy32bit = SPI5->FIFORD;
    }

    // configure SSD based interrupt
	SPI5->STAT     = 1<<5;
	SPI5->INTENSET = 1<<5;
	NVIC_ClearPendingIRQ(FLEXCOMM5_IRQn);
	NVIC_SetPriority(FLEXCOMM5_IRQn, 2);
	NVIC_EnableIRQ(FLEXCOMM5_IRQn);

	return;
}


void FLEXCOMM5_IRQHandler(void)
{
    GPIO->SET[AUX0_PORT] = 1<<AUX0_PIN;

    spi_isr_count++;

    // this is SSD driven interrupt, clear SSD flag
	SPI5->STAT = 1<<5;

    GPIO->CLR[AUX0_PORT] = 1<<AUX0_PIN;

	return;
}


void verify_received_data(void)
{
	uint32_t i_loc;
	uint32_t error_loc;

	error_loc = 0;
	for (i_loc = 0; i_loc != TEST_ARRAY_SIZE; i_loc++)
	{
		if (((spi_tx_array[i_loc]^spi_rx_array[i_loc]) & SPI_DATA_MASK) != 0)
		{
			error_loc++;
		}
	}

	if (error_loc != 0)
	{
		GPIO->SET[ERROR_PORT] = 1<<ERROR_PIN;
		delay_sw(50);
		GPIO->CLR[ERROR_PORT] = 1<<ERROR_PIN;
		delay_sw(50);
	}

	return;
}


// use CTIMER0 driven by the main clock to generate add on AHB traffic
void init_ctimer0(void)
{
	ctimer_config_t config;

	IOPCTL->PIO[CTIMER_INP_PORT][CTIMER_INP_PIN] = IOCON_IN_NOPUPD(CTIMER_INP_FUNC);
	//
	// CTIMER0 begin
	//==============

	// CTIMER0_MAT1 @...
	IOPCTL->PIO[CTIMER_MAT_PORT][CTIMER_MAT_PIN] = IOCON_IN_NOPUPD(CTIMER_MAT_FUNC);

	// CTIMER0_CAP1 = INP1
	INPUTMUX->CT32BIT_CAP_SEL[0][1] = 1;

	CTIMER_GetDefaultConfig(&config);

	config.mode  = kCTIMER_IncreaseOnBothEdge;
	config.input = kCTIMER_Capture_1;

	CTIMER_Init(CTIMER0, &config);

	// reset timer
	CTIMER_Reset(CTIMER0);

	// prime the counter & MR0/1/2
	CTIMER0->TC    = 0x80000000;
	CTIMER0->MR[0] = 0x1000;
	CTIMER0->MR[1] = 0x2000;
	CTIMER0->MR[2] = 0x3000;

#if STOP_AND_GO == 0
	// MR2R
	CTIMER0->MCR   = 1<<7;
#else
	// MR2S, MR2R
	CTIMER0->MCR   = 1<<8 | 1<<7;
#endif

	// toggle @ EMC1 & EM1 = 0
	CTIMER0->EMR   = 3<<6;
	CTIMER0->IR    = 0xFF;

	return;
}


void update_ctimer0(uint32_t match, uint32_t period)
{
	// stop (do not reset), prime the counter, EM1 = 0, MR0/1/2, clear flags, let it run
	CTIMER0->TCR   = 0<<1 | 0<<0;
	CTIMER0->TC    = 0x80000000;
	CTIMER0->EMR   = 3<<6;
	CTIMER0->MR[0] = match;
	CTIMER0->MR[1] = match;
	CTIMER0->MR[2] = period;
	CTIMER0->TC    = 0;
	CTIMER0->IR    = 0xFF;
	CTIMER0->TCR   = 0<<1 | 1<<0;

	return;
}


void stop_ctimer0(void)
{
	DMA0->COMMON[0].ENABLECLR = 1<<dma_desc_port0_wr_main;
	while((DMA0->COMMON[0].BUSY & (1<<dma_desc_port0_wr_main)) != 0);
	DMA0->COMMON[0].ABORT     = 1<<dma_desc_port0_wr_main;

	DMA0->COMMON[0].ENABLECLR = 1<<dma_desc_port1_wr_main;
	while((DMA0->COMMON[0].BUSY & (1<<dma_desc_port1_wr_main)) != 0);
	DMA0->COMMON[0].ABORT     = 1<<dma_desc_port1_wr_main;

#if STOP_AND_GO == 1
	DMA0->COMMON[0].ENABLECLR = 1<<dma_desc_timer_ctrl_main;
	while((DMA0->COMMON[0].BUSY & (1<<dma_desc_timer_ctrl_main)) != 0);
	DMA0->COMMON[0].ABORT     = 1<<dma_desc_timer_ctrl_main;
#endif

	// stop timer, prime counter and EM1
	CTIMER0->TCR = 0<<1 | 0<<0;
	CTIMER0->TC  = 0x80000000;
	CTIMER0->EMR = 3<<6;
	CTIMER0->IR  = 0xFF;

	return;
}


void init_ctimer0_dma_addon(void)
{
	uint32_t i_loc;

	for (i_loc = 0; i_loc != PORT_WR_ARRAY; i_loc++)
	{
		port0_wr_mask[i_loc] = 1<<ADDONT_0_PIN;
		port1_wr_mask[i_loc] = 1<<ADDONT_1_PIN;;
	}

#if STOP_AND_GO == 1
	dma_timer_tcr   = 0<<1 | 1<<0;
#endif

	GPIO->CLR[ADDONT_0_PORT] = 1<<ADDONT_0_PIN;
	GPIO->DIRSET[ADDONT_0_PORT] = 1<<ADDONT_0_PIN;
    IOPCTL->PIO[ADDONT_0_PORT][ADDONT_0_PIN] = IOCON_IN_NOPUPD(0);

	GPIO->CLR[ADDONT_1_PORT] = 1<<ADDONT_1_PIN;
	GPIO->DIRSET[ADDONT_1_PORT] = 1<<ADDONT_1_PIN;
    IOPCTL->PIO[ADDONT_1_PORT][ADDONT_1_PIN] = IOCON_IN_NOPUPD(0);

    INPUTMUX->DMAC0_ITRIG_ENA0_SET = 1<<4;					// enable T0_DMAREQ_M0 trigger
    INPUTMUX->DMAC0_ITRIG_SEL[dma_desc_port0_wr_main] = 4;	// dma_desc_port0_wr_main trigger = T0_DMAREQ_M0
    INPUTMUX->DMAC0_ITRIG_SEL[dma_desc_port1_wr_main] = 4;	// dma_desc_port1_wr_main trigger = T0_DMAREQ_M0

#if STOP_AND_GO == 1
    INPUTMUX->DMAC0_ITRIG_SEL[dma_desc_timer_ctrl_main] = 4;// dma_desc_timer_ctrl_main trigger = T0_DMAREQ_M0
#endif

	return;
}


void update_ctimer0_dma_addon(void)
{
	GPIO->CLR[ADDONT_0_PORT] = 1<<ADDONT_0_PIN;
	GPIO->CLR[ADDONT_1_PORT] = 1<<ADDONT_1_PIN;

	//
	// dma_desc_port0_wr_main BEGIN
	//=============================
	DMA0->COMMON[0].ENABLECLR = 1<<dma_desc_port0_wr_main;
	while((DMA0->COMMON[0].BUSY & (1<<dma_desc_port0_wr_main)) != 0);
	DMA0->COMMON[0].ABORT     = 1<<dma_desc_port0_wr_main;

	demo_dma_main_descriptor[dma_desc_port0_wr_main].xfercfg = 0;
	demo_dma_main_descriptor[dma_desc_port0_wr_main].src_addr = (uint32_t)&port0_wr_mask[15];
	demo_dma_main_descriptor[dma_desc_port0_wr_main].des_addr = (uint32_t)&GPIO->NOT[0];
	demo_dma_main_descriptor[dma_desc_port0_wr_main].link	  = 0;

	DMA0->CHANNEL[dma_desc_port0_wr_main].CFG =
		0<<0	|	// peripheral req disable
		1<<1	|	// hw trigger
		1<<4	|	// rising edge...
		0<<5	| 	// ...
		1<<6	|	// burst transfer(s)
		4<<8	| 	// burst size = 16
		1<<14	|	// source burst wrap
		0<<15	|	// no destination burst wrap
		DMACH_ADDLOAD0_PRIO<<16;	// priority =...

	DMA0->CHANNEL[dma_desc_port0_wr_main].XFERCFG =
		1<<0 	|	// valid configuration
		0<<1 	|	// no link/reload
		0<<2	|	// no sw trigger
		1<<3	|	// clear trigger at the end
		0<<4 	|	// no int A at the end
		0<<5	|	// no int B at the end
		2<<8 	|	// 32-bit transfers
		1<<12	|	// src: +1
		0<<14	|	// dst: +0
		(1024-1)<<16;	//transfer count...;

	DMA0->COMMON[0].ENABLESET = 1<<dma_desc_port0_wr_main;
	DMA0->COMMON[0].SETVALID  = 1<<dma_desc_port0_wr_main;
	//
	// dma_desc_port0_wr_main END
	//---------------------------


	//
	// dma_desc_port1_wr_main BEGIN
	//=============================
	DMA0->COMMON[0].ENABLECLR = 1<<dma_desc_port1_wr_main;
	while((DMA0->COMMON[0].BUSY & (1<<dma_desc_port1_wr_main)) != 0);
	DMA0->COMMON[0].ABORT     = 1<<dma_desc_port1_wr_main;

	demo_dma_main_descriptor[dma_desc_port1_wr_main].xfercfg  = 0;
	demo_dma_main_descriptor[dma_desc_port1_wr_main].src_addr = (uint32_t)&port1_wr_mask[0];
	demo_dma_main_descriptor[dma_desc_port1_wr_main].des_addr = (uint32_t)&GPIO->NOT[1];
	demo_dma_main_descriptor[dma_desc_port1_wr_main].link	  = 0;

	DMA0->CHANNEL[dma_desc_port1_wr_main].CFG =
						  0<<0	|	// peripheral req disable
						  1<<1	|	// hw trigger
						  0<<4	|	// falling edge...
						  0<<5	| 	// ...
						  1<<6	|	// burst transfer(s)
			 ADDLOAD1_BURST<<8	| 	// burst size =...
		DMACH_ADDLOAD1_PRIO<<16;		// priority =...

	DMA0->CHANNEL[dma_desc_port1_wr_main].XFERCFG =
		1<<0 	|	// valid configuration
		0<<1 	|	// no link/reload
		0<<2	|	// no sw trigger
		1<<3	|	// clear trigger at the end
		0<<4 	|	// no int A at the end
		0<<5	|	// no int B at the end
		2<<8 	|	// 32-bit transfers
		0<<12	|	// src: +0
		0<<14	|	// dst: +0
		(1024-1)<<16;	//transfer count...;

	DMA0->COMMON[0].ENABLESET = 1<<dma_desc_port1_wr_main;
	DMA0->COMMON[0].SETVALID  = 1<<dma_desc_port1_wr_main;
	//
	// dma_desc_port1_wr_main END
	//---------------------------


#if STOP_AND_GO == 1
	//
	// dma_desc_timer_ctrl_main BEGIN
	//===============================
	DMA0->COMMON[0].ENABLECLR = 1<<dma_desc_timer_ctrl_main;
	while((DMA0->COMMON[0].BUSY & (1<<dma_desc_timer_ctrl_main)) != 0);
	DMA0->COMMON[0].ABORT     = 1<<dma_desc_timer_ctrl_main;

	demo_dma_main_descriptor[dma_desc_timer_ctrl_main].xfercfg  = 0;
	demo_dma_main_descriptor[dma_desc_timer_ctrl_main].src_addr = (uint32_t)&dma_timer_tcr;
	demo_dma_main_descriptor[dma_desc_timer_ctrl_main].des_addr = (uint32_t)&CTIMER0->TCR;
	demo_dma_main_descriptor[dma_desc_timer_ctrl_main].link	    = 0;

	DMA0->CHANNEL[dma_desc_timer_ctrl_main].CFG =
		0<<0	|	// peripheral req disable
		1<<1	|	// hw trigger
		0<<4	|	// falling edge...
		0<<5	| 	// ...
		1<<6	|	// burst transfer(s)
		0<<8	| 	// burst size = 1
		DMACH_ADDLOADC_PRIO<<16;		// priority =...

	DMA0->CHANNEL[dma_desc_timer_ctrl_main].XFERCFG =
		1<<0 	|	// valid configuration
		0<<1 	|	// no link/reload
		0<<2	|	// no sw trigger
		1<<3	|	// clear trigger at the end
		0<<4 	|	// no int A at the end
		0<<5	|	// no int B at the end
		2<<8 	|	// 32-bit transfers
		0<<12	|	// src: +0
		0<<14	|	// dst: +0
		(512-1)<<16;	//transfer count...;

	DMA0->COMMON[0].ENABLESET = 1<<dma_desc_timer_ctrl_main;
	DMA0->COMMON[0].SETVALID  = 1<<dma_desc_timer_ctrl_main;
	//
	// dma_desc_timer_ctrl_main END
	//-----------------------------
#endif



	return;
}
