/******************************************************************************
*
* Copyright (C) 2009 - 2014 Xilinx, Inc.  All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/

/*
 * helloworld.c: simple test application
 *
 * This application configures UART 16550 to baud rate 9600.
 * PS7 UART (Zynq) is not initialized by this application, since
 * bootrom/bsp configures it to baud rate 115200
 *
 * ------------------------------------------------
 * | UART TYPE   BAUD RATE                        |
 * ------------------------------------------------
 *   uartns550   9600
 *   uartlite    Configurable only in HW design
 *   ps7_uart    115200 (configured by bootrom/bsp)
 */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "Membench.h"
#include "prem_counter.h"
#include "csaccess.h"
#include "xil_io.h"
#include "xscugic.h"

void pl_irq_ack_configure(int irq, void * handler)
{

	/*
	 * Initialize the interrupt controller driver so that it is ready to
	 * use.
	 */
	XScuGic_Config  * GicConfig = XScuGic_LookupConfig(XPAR_SCUGIC_0_DEVICE_ID);
	XScuGic Intc;
	assert(!XScuGic_CfgInitialize(&Intc, GicConfig,
					GicConfig->CpuBaseAddress));
	/*
	 * Setup the Interrupt System
	 * Connect the interrupt controller interrupt handler to the hardware
	 * interrupt handling logic in the ARM processor.
	 */
	// set level triggered
	XScuGic_SetPriorityTriggerType(&Intc, irq, 0xa0, 0b10);
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
			(Xil_ExceptionHandler) handler,
			&Intc);
	/*
	 * Enable interrupts in the ARM
	 */
	Xil_ExceptionEnable();

	/*
	 * Connect a device driver handler that will be called when an
	 * interrupt for the device occurs, the device driver handler performs
	 * the specific interrupt processing for the device
	 */
	XScuGic_Connect(&Intc, irq,
			   (Xil_ExceptionHandler) handler,
			   (void *)&Intc);


	/*
	 * Enable the interrupt for the device and then cause (simulate) an
	 * interrupt so the handlers will be called
	 */
	XScuGic_Enable(&Intc, irq);
}

volatile int limit_irq_num = 0;

void pl_limit_irq_handler()
{

	prem_conf_s prem_conf0 = {
			.lim_prefetch = 0xFFFFFFFF,
			.lim_compute = 0xFFFFFFFF,
			.lim_writeback = 0xFFFFFFFF
	};

	prem_configure(0,&prem_conf0);
	limit_irq_num++;
}

void prefetch()
{
	prem_set_phase(0, PREM_PHASE_PREF);
	struct cfg bench = {
		.sequential = false,
		.num_threads = 1,
		.size = MAX_WSS,
		.read_count = 0x200000,
		.write = false,
		.ofs = 0,
		.use_cycles = true,
		.repeats = 1,
		.cache_miss = true,
		.cache_miss_pl = true
	};

	printf("prefetch: read %u bytes randomly\n\r", bench.size);
	do_benchmark(&bench);
}

void compute()
{
	prem_set_phase(0, PREM_PHASE_COMP);
	struct cfg bench = {
		.sequential = true,
		.num_threads = 1,
		.size = 0x100000,
		.read_count = 0x200000,
		.write = false,
		.ofs = 0,
		.use_cycles = true,
		.repeats = 1,
		.cache_miss = true,
		.cache_miss_pl = true
	};

	printf("compute: read %u bytes sequentially\n\r", bench.size);
	do_benchmark(&bench);
}

void writeback()
{
	prem_set_phase(0, PREM_PHASE_WB);
	struct cfg bench = {
		.sequential = false,
		.num_threads = 1,
		.size = 0x100000,
		.read_count = 0x200000,
		.write = true,
		.ofs = 0,
		.use_cycles = true,
		.repeats = 1,
		.cache_miss = true,
		.cache_miss_pl = true
	};

	printf("writeback: write %u bytes randomly\n\r", bench.size);
	do_benchmark(&bench);
}

int main()
{
	printf("\n\r=================Start!=======================\n\r");
	printf("\n\r=================Read pretest=======================\n\r");
	prem_print_state(0);
	printf("\n\r=================Configure!=======================\n\r");

	//pl_irq_ack_configure(121, pl_limit_irq_handler);

	cs_init();
	cs_prem_count_init();
	cs_prem_count_percpu_init(0);

	prem_conf_s prem_conf0 = {
			.lim_prefetch = 0xFFFFFFFF,
			.lim_compute = 16,
			.lim_writeback = 0xFFFFFFFF
	};

	prem_configure(0,&prem_conf0);
	prem_print_state(0);

	prefetch();
	prem_print_state(0);
	compute();
	prem_print_state(0);
	writeback();
	prem_print_state(0);

	printf("int: %d\n\r", limit_irq_num);
	fflush(0);
	printf("\n\r================Work again, please!=======================\n\r");
	prem_conf_s prem_conf2 = {
			.lim_prefetch = 0xFFFFFFFF,
			.lim_compute = 0xFFFFFFFF,
			.lim_writeback = 0xFFFFFFFF
	};

	prem_configure(0,&prem_conf2);
	prefetch();
	prem_print_state(0);
	compute();
	prem_print_state(0);
	writeback();
	prem_print_state(0);

	fflush(0);
    return 0;
}
