Before we can install a Linux OS in our embedded system we have to add a timer IP. Now when we know how to add a new peripheral it will only take us a few minutes. We start by selecting the OPB Timer Counter from the IP catalog.
We add it and configure it.
We generate addresses and connect the ports and we are done.
Connect the interrupt signal
We will add the interrupt signal coming from the timer/counter to the interrupt controller. We give the highest priority to the timer interrupt. Select the Ports display and click the first port to display this window.
OPB Timer/Counter
The OPB Timer/Counter is a 32-bit timer module that attaches to the OPB (On-Chip Peripheral Bus). It includes two programmable interval timers with interrupt, event generation, and event capture capabilities. It also includes a Pulse Width Modulator (PWM) output and a configurable counter width. Each of the two timer modules is capable of holding the initial value of the counter for event generation or capture a value based on the mode of the timer.
(Courtesy of Xilinx)
Register address map
Register Name
Abbreviation
OPB Offset
Type
Control/Status Register 0
TSCR0
0x00
R/W
Load Register 0
TLR0
0x04
R/W
Timer/Counter Register 0
TCR0
0x08
R
Control/Status Register 1
TSCR1
0x10
R/W
Load Regster 1
TLR1
0x14
R/W
Timer/Counter Register 1
TCR1
0x18
R
The registers are organized as big-endian data. Library Generation
After running libgen we have copied the following source files to the libsrc directory.
xparameters.h
/* Definitions for driver TMRCTR */ #define XPAR_XTMRCTR_NUM_INSTANCES 1
/** * Defines the number of timer counters within a single hardware device. This * number is not currently parameterized in the hardware but may be in the * future. */ #define XTC_DEVICE_TIMER_COUNT 2
/* Each timer counter consumes 16 bytes of address space */
#define XTC_TIMER_COUNTER_OFFSET 16
/** @name Register Offset Definitions * Register offsets within a timer counter, there are multiple * timer counters within a single device * @{ */
/** @name Control Status Register Bit Definitions * Control Status Register bit masks * Used to configure the timer counter device. * @{ */
#define XTC_CSR_ENABLE_ALL_MASK 0x00000400 /**< Enables all timer counters */ #define XTC_CSR_ENABLE_PWM_MASK 0x00000200 /**< Enables the Pulse Width Modulation */ #define XTC_CSR_INT_OCCURED_MASK 0x00000100 /**< If bit is set, an interrupt has occured.*/ /**< If set and '1' is written to this bit position, bit is cleared. */ #define XTC_CSR_ENABLE_TMR_MASK 0x00000080 /**< Enables only the specific timer */ #define XTC_CSR_ENABLE_INT_MASK 0x00000040 /**< Enables the interrupt output. */ #define XTC_CSR_LOAD_MASK 0x00000020 /**< Loads the timer using the load value provided earlier in the Load Register, XTC_TLR_OFFSET. */ #define XTC_CSR_AUTO_RELOAD_MASK 0x00000010 /**< In compare mode, configures the timer counter to reload from the Load Register. The default mode causes the timer counter to hold when the compare value is hit. In capture mode, configures the timer counter to not hold the previous capture value if a new event occurs. The default mode cause the timer counter to hold the capture value until recognized. */ #define XTC_CSR_EXT_CAPTURE_MASK 0x00000008 /**< Enables the external input to the timer counter. */ #define XTC_CSR_EXT_GENERATE_MASK 0x00000004 /**< Enables the external generate output for the timer. */ #define XTC_CSR_DOWN_COUNT_MASK 0x00000002 /**< Configures the timer counter to count down fromstart value, the default is to count up. */ #define XTC_CSR_CAPTURE_MODE_MASK 0x00000001 /**< Enables the timer to capture the timer counter value when the external capture line is asserted. The default mode is compare mode.*/
Application program
We have taken the application program from the examples directory found in the EDK installation (..../edk91i/sw/XilinxProcessorIPLib/drivers/tmrctr_v1_00_b/examples). XStatus TmrCtrLowLevelExample(Xuint32 TmrCtrBaseAddress, Xuint8 TmrCtrNumber) { Xuint32 Value; Xuint32 ControlStatus; Xuint32 i;
/* * Set the master enable bit and enable hardware interrupts. We must set the * master enable bit before enabling interrupts otherwise we will get a spurious interrrupt (IRQ goes high) ??? */ XIntc_Out32(XPAR_OPB_INTC_0_BASEADDR + XIN_MER_OFFSET, XIN_INT_MASTER_ENABLE_MASK | XIN_INT_HARDWARE_ENABLE_MASK); /* * Enable interrupts from the timer/counter and the ETC */ XIntc_mEnableIntr(XPAR_OPB_INTC_0_BASEADDR, XPAR_OPB_TIMER_0_INTERRUPT_MASK | XPAR_ETC_0_O_INTERRUPT_MASK); /* * Clear the Timer Control Status Register */ XTmrCtr_mSetControlStatusReg(TmrCtrBaseAddress, TmrCtrNumber,0x0);
/* * Set the value that is loaded into the timer counter and cause it to * be loaded into the timer counter */ XTmrCtr_mSetLoadReg(TmrCtrBaseAddress, TmrCtrNumber, 0x100); XTmrCtr_mLoadTimerCounterReg(TmrCtrBaseAddress, TmrCtrNumber);
/* * Clear the Load Timer bit in the Control Status Register */ ControlStatus = XTmrCtr_mGetControlStatusReg(TmrCtrBaseAddress, TmrCtrNumber); /* * Setup the counter to count down and enable interrupt when counter rolls over */
/* * Start the timer counter such that it's decrementing. */
XTmrCtr_mEnable(TmrCtrBaseAddress, TmrCtrNumber);
/* * Read the value of the timer counter and wait for an interrupt */
while (1) { /* * If the interrupt occurred which is indicated by the global * variable which is set in the device driver handler, then * stop waiting */ Value = XTmrCtr_mGetTimerCounterReg(TmrCtrBaseAddress, TmrCtrNumber); if (InterruptProcessed) { break; } }
/* * Disable the timer counter such that it stops incrementing */
The counter will count down from 0x100 (256 decimal) to 0, when an interrupt will be generated. It takes 2590 ns from starting the timer to when the interrupt is generated. Clock frequency is 100 MHz (256*10 ns = 2560 ns).