//*******************************************
// Dot Product With Timers
// G.S for teaching pourpouse
//
// first relaease jul 2022
// May 2024. Update for Vitis Unified 2023.2
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xil_printf.h"
#include "xiltimer.h"
#include "xparameters.h"


/* N should be power of 2, and larger than 8 */
#define N            1024*16
#define TEST_ROUNDS  10
#define APU_FREQ     XPAR_CPU_CORE_CLOCK_FREQ_HZ

#define GOLDEN_RESULT 357389824
float a[N],b[N];


float dot_product_0(float *pa, float *pb, unsigned int n, unsigned int x )
{
	float sum=0.0;
    unsigned int i;

    for(i = 0; i < (n); i++)
        sum += pa[i] *pb[i];
    return sum;
}

float dot_product_1(float * __restrict pa, float *  __restrict pb, unsigned int n, unsigned int x )
{
	float sum=0.0;
    unsigned int i;

    for(i = 0; i < (n& ~3); i++)
        sum += pa[i] *pb[i];
    return sum;
}

float dot_product_2(float * __restrict pa, float *  __restrict pb, unsigned int n, unsigned int x )
{
	float sum[4]={0.0,0.0,0.0,0.0};
    unsigned int i;

    for(i = 0; i < (n& ~3); i+=4)
    {
        sum[0] += pa[i] *pb[i];
        sum[1] += pa[i+1] *pb[i+1];
        sum[2] += pa[i+2] *pb[i+2];
        sum[3] += pa[i+3] *pb[i+3];
    }
    return sum[0]+sum[1]+sum[2]+sum[3];
}

int main()
{
	unsigned int i;
	float fResult0, fResult1, fResult2;
	XTime tStart, tEnd, tInterm1, tInterm2, calibrationPS;
	u64 run_time_p0, run_time_p1, run_time_p2 = 0;
	float time_us;

	// Calibrate PS timer
	XTime_GetTime(&tStart);
	XTime_GetTime(&tEnd);
	calibrationPS = tEnd - tStart;
	time_us = 1.0 * calibrationPS / (COUNTS_PER_SECOND/1000000);
	printf("\n** Calibrating the PS timer:\r\n");
	printf("init_time: %lu cycles.\r\n", tStart);
	printf("curr_time: %lu cycles.\r\n", tEnd);
	printf("CalibrationPS: %lu PS clock cycles (%.3f us).\r\n", calibrationPS, time_us);

	float ratio_PS_TIMER = (1.0*APU_FREQ) / COUNTS_PER_SECOND;
	printf("Freq PS: %d, Freq TIMER: %d. Ratio: %f\n\r", APU_FREQ, COUNTS_PER_SECOND, ratio_PS_TIMER);

	printf("\r\n----Calling Dot Product function----\r\n");

	// We need to validate the algorithm's correctness
	for(i=0;i<N;i++)
	{
          a[i]=i+1;
          b[i]=3*i;
	}

	XTime_GetTime(&tStart);
	fResult0 = dot_product_0(a,b,N,0);
	XTime_GetTime(&tInterm1);
	fResult1 = dot_product_1(a,b,N,0);
	XTime_GetTime(&tInterm2);  
	fResult2 = dot_product_2(a,b,N,0);
	XTime_GetTime(&tEnd);

	printf("Iterations N=%i\r\n", N);
	printf("Result0=%f\r\n", fResult0);
        printf("Result1=%f\r\n", fResult1);
	printf("Result2=%f\r\n\n", fResult2);

	run_time_p0 = tInterm1 - tStart - calibrationPS;
        run_time_p1 = tInterm2 - tInterm1 - calibrationPS;
	run_time_p2 = tEnd - tInterm2 - calibrationPS;
	printf("Total run time prod0 (bare metal) is %ld PS cycles.\r\n", run_time_p0);
        printf("Total run time prod1 (bare metal) is %ld PS cycles.\r\n", run_time_p1);
	printf("Total run time prod2 (bare metal) is %ld PS cycles.\r\n", run_time_p2);

        printf("----Completed----\r\n");

	return 0;
}



