mirror of
https://github.com/apache/nuttx.git
synced 2026-05-23 23:28:29 +08:00
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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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)
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user