MicroZed Chronicles: Arm Cortex-M1 Core and Timing
How to use the SysTick timer in the Arm M1 core within our Xilinx FPGA.
One of the key elements of any embedded system is the ability to perform operations at specific intervals, like reading a sensor or updating calculations for example. The best way to do this is to use a timer which triggers a periodic interrupt, indicating to the system that it is time to perform the required action.
In the Arm Cortex-M1 core, we have been examining the timer which is called the system timer and is controlled by the SysTick registers. There are a total of four registers which the SysTick timer uses:
- SysTick Status and Control Register – Controls enabling of the SysTick timer and interrupt generation.
- SysTick Reload Value Register – The value to be reloaded into the SysTick timer when the timer reaches zero. This is a 24-bit value and must be set between 0x1 and 0xFFFFFF.
- ·SysTick Current Value Register – The current value of the SysTick timer.
- SysTick Calibration Value Register – Calibration register that can provide timing information for more accurate SW timing. (Not implemented in this instantiation.)
You will find detailed information in the Arm Cortex-M1 Technical Reference Manual, which can be found here.
Once our design configures and enables the SysTick, the interrupt will fire and raise a SW exception that is handled by the exception handler.
Using the SysTick timer is very straight forward in our designs. The first thing we need to do is define the addresses of the three registers of interest – control, reload, and current value.
We also need to define the bit positions in the control word used to configure the SysTick timer in addition to defining the reload value.
We can then use these register addresses, bit positions, and reload value at the appropriate point of our application to configure and start the SysTick timer.
Now, we need to create an interrupt service routine (ISR) which is activated each time the SysTick fires. We can name this anything we want however by default — it is called SysTick_Handler and is defined within the startup.s file created from CMSIS.
You will find this file under the Device element of the project items.
Within this file, you will see the list of exceptions and interrupts with the SysTick indicated by its default name.
If we wish to change the name of the SysTick_Handler or define any other interrupt service routine, we need to define it within the __Vector definition and within the Handler_Name macro.
Once we have updated the name as desired for the SysTick handler, we are able to write the interrupt service routine.
When we write the ISR, we should ensure the following points are addressed to get the best performance.
- Speed of the ISR is key. Keep it is as simple as possible for example if the ISR is to be used to perform sampling of sensors. Do not perform the sampling in the ISR but instead, set a simple flag in the ISR and perform the sampling in the main body of the code.
- Do not allow servicing of other interrupts in the ISR.
- Make sure interrupt flags are cleared as necessary when you leave the ISR.
- Ensure interrupts are enabled when the ISR is completed.
I created a simple example to demonstrate how to work with the SysTick timer. This example illustrates how that when the SysTick timer expired, it printed out the message to a console. When the interrupt was triggered in more complex applications, the algorithm or sensors sampling could be executed.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#include "xil_printf.h"
#define STCTRL (*( ( volatile unsigned long *) 0xE000E010 ))
#define STRELOAD (*( ( volatile unsigned long *) 0xE000E014 ))
#define STCURR (*( ( volatile unsigned long *) 0xE000E018 ))
#define SBIT_ENABLE 0
#define SBIT_TICKINT 1
#define SBIT_CLKSOURCE 2
#define RELOAD_VALUE 98999999
bool sample;
int main()
{
print("MicroZed Chronicles 345 Arm M1 from Scratch\n\r");
STRELOAD = RELOAD_VALUE;
STCTRL = (1<<SBIT_ENABLE) | (1<<SBIT_TICKINT) | (1<<SBIT_CLKSOURCE);
while(1){
if (sample == TRUE){
print("Interrupt Occurred\n\r");
sample = FALSE;
}
}
return 0;
}
void Timer_Handler(void)
{
sample = TRUE;
}
Over the last few blogs, we have looked at how we implement Arm Cortex-M1 processors within our Xilinx FPGA. Now we understand how we can use the SysTick timer and work with other interrupts in our embedded system.
See My FPGA / SoC Projects: Adam Taylor on Hackster.io
Get the Code: ATaylorCEngFIET (Adam Taylor)
Access the MicroZed Chronicles Archives with over 300 articles on the FPGA / Zynq / Zynq MpSoC updated weekly at MicroZed Chronicles.