//******************************************************************************
//* Timer with adjustable resolution.                                          *
//* Written by   : Tamas Raikovich                                             *
//* Version      : 1.0                                                         *
//* Last modified: 2012.10.11.                                                 *
//******************************************************************************
#include <xparameters.h>
#include <xintc_l.h>
#include <xtmrctr_l.h>
#include "timer.h"

//******************************************************************************
//* Constants related to the peripherals.                                      *
//******************************************************************************
#define INTC_BASEADDR		XPAR_INTC_0_BASEADDR
#define TIMER_BASEADDR		XPAR_AXI_TIMER_0_BASEADDR
#define BUS_CLKFREQ_HZ		XPAR_PROC_BUS_0_FREQ_HZ
#define TIMER_IRQ			XPAR_MICROBLAZE_0_INTC_AXI_TIMER_0_INTERRUPT_INTR


//******************************************************************************
//* Macro for register access.                                                 *
//******************************************************************************
#define REG32(addr) (*(volatile unsigned long *)(addr))


//******************************************************************************
//* Global variables.                                                          *
//******************************************************************************
volatile unsigned long timerCounter;


//******************************************************************************
//* Timer interrupt handler routine.                                           *
//******************************************************************************
void TimerInterruptHandler(void *instancePtr)
{
    unsigned long csr;

    //Decrement the timer interrupt counter.
    if (timerCounter != 0)
        timerCounter--;

    //Clear the timer interrupt flag.
    csr = XTmrCtr_GetControlStatusReg(TIMER_BASEADDR, 0);
    XTmrCtr_SetControlStatusReg(TIMER_BASEADDR, 0, csr);
}


//******************************************************************************
//* Initializes the timer.                                                     *
//******************************************************************************
void TimerInitialize(unsigned long ticksPerSecond)
{
    unsigned long ier;

    //Register the timer interrupt handler routine.
    XIntc_RegisterHandler(
    	INTC_BASEADDR,
    	TIMER_IRQ,
        (XInterruptHandler) TimerInterruptHandler,
        0
    );

    //Enable the timer interrupt in the interrupt controller.
    ier = REG32(INTC_BASEADDR + XIN_IER_OFFSET);
    XIntc_EnableIntr(INTC_BASEADDR, ier | (1 << TIMER_IRQ));

    //Set the value of the LOAD register.
    XTmrCtr_SetLoadReg(TIMER_BASEADDR, 0, (BUS_CLKFREQ_HZ / ticksPerSecond) - 1);

    //Reset the timer.
    XTmrCtr_SetControlStatusReg(
    	TIMER_BASEADDR,
        0,
        XTC_CSR_INT_OCCURED_MASK | XTC_CSR_LOAD_MASK
    );

    //Start the timer.
    XTmrCtr_SetControlStatusReg(
    	TIMER_BASEADDR,
        0,
        XTC_CSR_ENABLE_TMR_MASK | XTC_CSR_ENABLE_INT_MASK |
        XTC_CSR_AUTO_RELOAD_MASK | XTC_CSR_DOWN_COUNT_MASK
    );
}


//******************************************************************************
//* Sets the timer counter.                                                    *
//******************************************************************************
void SetTimer(unsigned long value)
{
    timerCounter = value;
}

//******************************************************************************
//* Returns the current value of the timer counter.                            *
//******************************************************************************
unsigned long GetTimer()
{
    return timerCounter;
}

