arm: systick: fix off-by-one in SysTick RELOAD programming

ARM specifies that SysTick RELOAD must be programmed with N-1 to get a period of N CPU cycles.
Adjust the timeout/reload conversions accordingly: subtract 1 when writing RELOAD and add 1 back when converting RELOAD to a timeout/interval.
Also clamp RELOAD to the valid 24-bit range (1..0x00ffffff) for Armv7-M/Armv8-M to avoid invalid values.
Reference: https://developer.arm.com/documentation/dui0646/c/Cortex-M7-Peripherals/System-timer--SysTick/SysTick-Reload-Value-Register

Signed-off-by: Gao Jiawei <gaojiawei@xiaomi.com>
This commit is contained in:
Gao Jiawei
2025-01-22 11:54:51 +08:00
committed by Xiang Xiao
parent 281f9fc0c0
commit 564c9d7dcd
5 changed files with 81 additions and 22 deletions
+29 -9
View File
@@ -30,6 +30,7 @@
#include <nuttx/irq.h>
#include <stdio.h>
#include <sys/param.h>
#include "nvic.h"
#include "systick.h"
@@ -37,6 +38,22 @@
#ifdef CONFIG_ARMV7M_SYSTICK
/****************************************************************************
* Pre-processor definitions
****************************************************************************/
/* According to what arm specified, we should set the
* RELOAD value to N-1 for a desired systick interval of
* N processor clock cycles.
* Therefore, when reading the RELOAD value, it is important
* to add one clock cycle back.
*/
#define RELOAD2TIMEOUT(reload) ((reload) + 1)
#define TIMEOUT2RELOAD(timeout) ((timeout) - 1)
#define CLAMP_RELOAD(reload) \
CLAMP(reload, NVIC_MIN_SYSTICK_CNT, NVIC_MAX_SYSTICK_CNT)
/****************************************************************************
* Private Types
****************************************************************************/
@@ -141,10 +158,10 @@ static int systick_getstatus(struct timer_lowerhalf_s *lower_,
status->flags = lower->callback ? TCFLAGS_HANDLER : 0;
status->flags |= systick_is_running() ? TCFLAGS_ACTIVE : 0;
status->timeout = usec_from_count(getreg32(NVIC_SYSTICK_RELOAD),
lower->freq);
status->timeleft = usec_from_count(getreg32(NVIC_SYSTICK_CURRENT),
lower->freq);
status->timeout = usec_from_count(
RELOAD2TIMEOUT(getreg32(NVIC_SYSTICK_RELOAD)), lower->freq);
status->timeleft = usec_from_count(
getreg32(NVIC_SYSTICK_CURRENT), lower->freq);
if (systick_irq_pending(lower))
{
@@ -185,8 +202,9 @@ static int systick_settimeout(struct timer_lowerhalf_s *lower_,
{
uint32_t reload;
reload = usec_to_count(timeout, lower->freq);
putreg32(reload, NVIC_SYSTICK_RELOAD);
reload = TIMEOUT2RELOAD(usec_to_count(timeout, lower->freq));
putreg32(CLAMP_RELOAD(reload), NVIC_SYSTICK_RELOAD);
if (systick_is_running())
{
if (reload != getreg32(NVIC_SYSTICK_CURRENT))
@@ -237,7 +255,8 @@ static int systick_interrupt(int irq, void *context, void *arg)
if (lower->callback && systick_is_running())
{
uint32_t reload = getreg32(NVIC_SYSTICK_RELOAD);
uint32_t interval = usec_from_count(reload, lower->freq);
uint32_t interval = usec_from_count(
RELOAD2TIMEOUT(reload), lower->freq);
uint32_t next_interval = interval;
lower->next_interval = &next_interval;
@@ -245,8 +264,9 @@ static int systick_interrupt(int irq, void *context, void *arg)
{
if (next_interval && next_interval != interval)
{
reload = usec_to_count(next_interval, lower->freq);
putreg32(reload, NVIC_SYSTICK_RELOAD);
reload = TIMEOUT2RELOAD(
usec_to_count(next_interval, lower->freq));
putreg32(CLAMP_RELOAD(reload), NVIC_SYSTICK_RELOAD);
putreg32(0, NVIC_SYSTICK_CURRENT);
}
}
+10 -3
View File
@@ -470,18 +470,25 @@
/* SysTick reload value register (SYSTICK_RELOAD) */
/* The armv7-m systick RELOAD regitser has 24 bits
* ranging from 0x1 ~ 0x00ffffff
* noting that, setting reload to 0 is valid but doesn't have any effects
*/
#define NVIC_MIN_SYSTICK_CNT (0x00000001)
#define NVIC_MAX_SYSTICK_CNT (0x00ffffff)
#define NVIC_SYSTICK_RELOAD_SHIFT 0 /* Bits 23-0: Timer reload value */
#define NVIC_SYSTICK_RELOAD_MASK (0x00ffffff << NVIC_SYSTICK_RELOAD_SHIFT)
#define NVIC_SYSTICK_RELOAD_MASK (NVIC_MAX_SYSTICK_CNT << NVIC_SYSTICK_RELOAD_SHIFT)
/* SysTick current value register (SYSTICK_CURRENT) */
#define NVIC_SYSTICK_CURRENT_SHIFT 0 /* Bits 23-0: Timer current value */
#define NVIC_SYSTICK_CURRENT_MASK (0x00ffffff << NVIC_SYSTICK_RELOAD_SHIFT)
#define NVIC_SYSTICK_CURRENT_MASK (NVIC_MAX_SYSTICK_CNT << NVIC_SYSTICK_RELOAD_SHIFT)
/* SysTick calibration value register (SYSTICK_CALIB) */
#define NVIC_SYSTICK_CALIB_TENMS_SHIFT 0 /* Bits 23-0: Calibration value */
#define NVIC_SYSTICK_CALIB_TENMS_MASK (0x00ffffff << NVIC_SYSTICK_CALIB_TENMS_SHIFT)
#define NVIC_SYSTICK_CALIB_TENMS_MASK (NVIC_MAX_SYSTICK_CNT << NVIC_SYSTICK_CALIB_TENMS_SHIFT)
#define NVIC_SYSTICK_CALIB_SKEW (1 << 30) /* Bit 30: Calibration value inexact */
#define NVIC_SYSTICK_CALIB_NOREF (1 << 31) /* Bit 31: No external reference clock */
+27 -7
View File
@@ -29,6 +29,7 @@
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <sys/param.h>
#include <stdio.h>
#include "nvic.h"
@@ -37,6 +38,22 @@
#ifdef CONFIG_ARMV8M_SYSTICK
/****************************************************************************
* Pre-processor definitions
****************************************************************************/
/* According to what arm specified, we should set the
* RELOAD value to N-1 for a desired systick interval of
* N processor clock cycles.
* Therefore, when reading the RELOAD value, it is important
* to add one clock cycle back.
*/
#define RELOAD2TIMEOUT(reload) ((reload) + 1)
#define TIMEOUT2RELOAD(timeout) ((timeout) - 1)
#define CLAMP_RELOAD(reload) \
CLAMP(reload, NVIC_MIN_SYSTICK_CNT, NVIC_MAX_SYSTICK_CNT)
/****************************************************************************
* Private Types
****************************************************************************/
@@ -141,8 +158,8 @@ static int systick_getstatus(struct timer_lowerhalf_s *lower_,
status->flags = lower->callback ? TCFLAGS_HANDLER : 0;
status->flags |= systick_is_running() ? TCFLAGS_ACTIVE : 0;
status->timeout = usec_from_count(getreg32(NVIC_SYSTICK_RELOAD),
lower->freq);
status->timeout = usec_from_count(
RELOAD2TIMEOUT(getreg32(NVIC_SYSTICK_RELOAD)), lower->freq);
status->timeleft = usec_from_count(getreg32(NVIC_SYSTICK_CURRENT),
lower->freq);
@@ -185,8 +202,9 @@ static int systick_settimeout(struct timer_lowerhalf_s *lower_,
{
uint32_t reload;
reload = usec_to_count(timeout, lower->freq);
putreg32(reload, NVIC_SYSTICK_RELOAD);
reload = TIMEOUT2RELOAD(usec_to_count(timeout, lower->freq));
putreg32(CLAMP_RELOAD(reload), NVIC_SYSTICK_RELOAD);
if (systick_is_running())
{
if (reload != getreg32(NVIC_SYSTICK_CURRENT))
@@ -237,7 +255,8 @@ static int systick_interrupt(int irq, void *context, void *arg)
if (lower->callback && systick_is_running())
{
uint32_t reload = getreg32(NVIC_SYSTICK_RELOAD);
uint32_t interval = usec_from_count(reload, lower->freq);
uint32_t interval = usec_from_count(
RELOAD2TIMEOUT(reload), lower->freq);
uint32_t next_interval = interval;
lower->next_interval = &next_interval;
@@ -245,8 +264,9 @@ static int systick_interrupt(int irq, void *context, void *arg)
{
if (next_interval && next_interval != interval)
{
reload = usec_to_count(next_interval, lower->freq);
putreg32(reload, NVIC_SYSTICK_RELOAD);
reload = TIMEOUT2RELOAD(
usec_to_count(next_interval, lower->freq));
putreg32(CLAMP_RELOAD(reload), NVIC_SYSTICK_RELOAD);
putreg32(0, NVIC_SYSTICK_CURRENT);
}
}
+10 -3
View File
@@ -544,18 +544,25 @@
/* SysTick reload value register (SYSTICK_RELOAD) */
/* The armv8-m systick RELOAD regitser has 24 bits
* ranging from 0x1 ~ 0x00ffffff
* noting that, setting reload to 0 is valid but doesn't have any effects
*/
#define NVIC_MIN_SYSTICK_CNT (0x00000001)
#define NVIC_MAX_SYSTICK_CNT (0x00ffffff)
#define NVIC_SYSTICK_RELOAD_SHIFT 0 /* Bits 23-0: Timer reload value */
#define NVIC_SYSTICK_RELOAD_MASK (0x00ffffff << NVIC_SYSTICK_RELOAD_SHIFT)
#define NVIC_SYSTICK_RELOAD_MASK (NVIC_MAX_SYSTICK_CNT << NVIC_SYSTICK_RELOAD_SHIFT)
/* SysTick current value register (SYSTICK_CURRENT) */
#define NVIC_SYSTICK_CURRENT_SHIFT 0 /* Bits 23-0: Timer current value */
#define NVIC_SYSTICK_CURRENT_MASK (0x00ffffff << NVIC_SYSTICK_RELOAD_SHIFT)
#define NVIC_SYSTICK_CURRENT_MASK (NVIC_MAX_SYSTICK_CNT << NVIC_SYSTICK_RELOAD_SHIFT)
/* SysTick calibration value register (SYSTICK_CALIB) */
#define NVIC_SYSTICK_CALIB_TENMS_SHIFT 0 /* Bits 23-0: Calibration value */
#define NVIC_SYSTICK_CALIB_TENMS_MASK (0x00ffffff << NVIC_SYSTICK_CALIB_TENMS_SHIFT)
#define NVIC_SYSTICK_CALIB_TENMS_MASK (NVIC_MAX_SYSTICK_CNT << NVIC_SYSTICK_CALIB_TENMS_SHIFT)
#define NVIC_SYSTICK_CALIB_SKEW (1 << 30) /* Bit 30: Calibration value inexact */
#define NVIC_SYSTICK_CALIB_NOREF (1 << 31) /* Bit 31: No external reference clock */
+5
View File
@@ -45,6 +45,11 @@
# define MAX(a,b) (((a) > (b)) ? (a) : (b))
#endif /* MAX */
#ifndef CLAMP
/* inclusively clamping a value into a given range */
# define CLAMP(x, min, max) ((x) <= (min) ? (min) : MIN(x, max))
#endif /* CLAMP */
/* Macros for number of items.
* (aka. ARRAY_SIZE, ArraySize, Size of an Array)
*/