#include <stdio.h>
#include <stdlib.h>
#include "platform.h"
#include "xparameters.h"
#include "xstatus.h"
#include "xtmrctr.h"
#include "xaverage_calculator.h"

#define KBYTE 1024
#define MBYTE 1024 * KBYTE

#define ROWS 40
#define COLS 25

#define TIMER_COUNTER_0 0

static XAverage_calculator average_calculator;
static XAverage_calculator_Config *average_calculator_config_ptr;

static XTmrCtr timer;

u32 *source_address = (u32 *)(XPAR_PS7_RAM_0_S_AXI_BASEADDR + (4 *MBYTE));
u32 *destination_address = (u32 *)(XPAR_PS7_RAM_0_S_AXI_BASEADDR + (8 *MBYTE));

int setup_average_calculator()
{
	int status;

	average_calculator_config_ptr = XAverage_calculator_LookupConfig(XPAR_AVERAGE_CALCULATOR_DEVICE_ID);

	if (average_calculator_config_ptr == NULL)
	{
		print("Setting-up Average Calculator Configuration Structure: FAILURE\r\n");
		return XST_FAILURE;
	}
	else
	{
		print("Setting-up Average Calculator Configuration Structure: SUCCESS\r\n");
	}



	status = XAverage_calculator_CfgInitialize(&average_calculator, average_calculator_config_ptr);

	if (status != XST_SUCCESS)
	{
		print("Initializing Average Calculator Instance: FAILURE\r\n");
		return XST_FAILURE;
	}
	else
	{
		print("Initializing Average Calculator Instance: SUCCESS\r\n");
	}

	return XST_SUCCESS;
}

int setup_timer()
{
	int timer_status;
	/*
	 * Initialize the timer counter so that it's ready to use,
	 * specify the device ID that is generated in xparameters.h
	 */
	timer_status = XTmrCtr_Initialize(&timer, XPAR_TIMER_DEVICE_ID);

	if (timer_status != XST_SUCCESS) {

		print("Setting-up Timer: FAILURE\r\n");

		return XST_FAILURE;
	}
	else
	{
		print("Setting-up Timer: SUCCESS\r\n");
	}

	/*
	 * Perform a self-test to ensure that the hardware was built
	 * correctly, use the 1st timer in the device (0)
	 */

	timer_status = XTmrCtr_SelfTest(&timer, TIMER_COUNTER_0);

	if (timer_status != XST_SUCCESS) {

		print("Self-Testing Timer: FAILURE\r\n");

		return XST_FAILURE;
	}
	else
	{
		print("Self-Testing Timer: SUCCESS\r\n");
	}

	return XST_SUCCESS;
}

int main()
{
	int status;
	int repeat;
	int row;
	int col;
	int timer_value;
	int average_value = 0;
	int sum_value = 0;

    init_platform();

    xil_printf("%c[2J",27);

    print("Starting Average Calculator Test\n\r");


    /*
     * Set Up the Average Calculator Peripheral
     */
    status = setup_average_calculator();

    if(status == XST_FAILURE)
    {
    	return status;
    }

    /*
     * Set Up the Timer Peripheral
     */
    status = setup_timer();

    if(status == XST_FAILURE)
    {
    	return status;
    }


    /*
     * Fill the Source Memory with 1000 Integers
     */
    for(repeat = 0; repeat < 1000; repeat++)
    {
    	source_address[repeat] = repeat + 1;
    }


    print("The Source Memory Integer Values are:\n\r\r\n");

    for(row = 0; row < ROWS; row++)
    {
        for(col = 0; col < COLS; col++)
        {
        	xil_printf("  %03d  ", source_address[(row * COLS) + col]);
        }
        print("\r\n");
    }

    /*
     * Set the Average Calculator Peripheral with the Data Source and Destination Memories
     */
    XAverage_calculator_Set_source_data_address(&average_calculator, (u32)source_address);
    XAverage_calculator_Set_destination_data_address(&average_calculator, (u32)destination_address);


    /*
     * Start the Timer to Count the Hardware Execution Time
     */
    timer_value = 0;
	XTmrCtr_Start(&timer, TIMER_COUNTER_0);

    /*
     * Start the Average Calculator to Test the Hardware Approach for Calculating Average Values
     */
    XAverage_calculator_Start(&average_calculator);

    /*
     * Stop the Timer
     */
	XTmrCtr_Stop(&timer, TIMER_COUNTER_0);
	timer_value = XTmrCtr_GetValue(&timer, TIMER_COUNTER_0);

	xil_printf("Hardware Average Calculator Execution Time in Cycles: %d\n\r", timer_value);
	xil_printf("The Calculated Average Value is: %d\n\r", destination_address[0]);

    /*
     * Reset the Timer
     */
	XTmrCtr_Reset(&timer, TIMER_COUNTER_0);
    /*
     * Start the Timer to Count the Software Execution Time
     */
    timer_value = 0;
	XTmrCtr_Start(&timer, TIMER_COUNTER_0);


    /*
     * Calculate the Average Value with the Software Approach Executed by the ARM CPU
     */

	for(repeat = 0; repeat < 1000; repeat++)
	{
		sum_value = sum_value + source_address[repeat];
	}

	/*
	 * Divide the Previous Sum Value by 1000 to Get the Average Value
	 *
	 */
	average_value = sum_value / 1000;

    /*
     * Stop the Timer
     */
	XTmrCtr_Stop(&timer, TIMER_COUNTER_0);
	timer_value = XTmrCtr_GetValue(&timer, TIMER_COUNTER_0);

	xil_printf("Software Average Calculator Execution Time in Cycles = %d\n\r", average_value);

    /*
     * Reset the Timer
     */
	XTmrCtr_Reset(&timer, TIMER_COUNTER_0);

    cleanup_platform();

    return XST_SUCCESS;
}
