mirror of https://github.com/ARMmbed/mbed-os.git
Add tickless support for devices without SysTick
Some Cortex-M0 devices, such as the nrf51, don't have the SysTick. Instead, these targets use a software interrupt to simulate SysTick. Add the hooks in the tickless code to support these devices. Targets which do not have SysTick should now define NO_SYSTICK in targets.json and implement mbed_get_m0_tick_irqn to add os suport. This patch also removes os tick handling from the existing devices (nrf51) since this is now handled in common code.pull/4991/head
parent
e44d94fa1e
commit
c3eae587eb
|
@ -37,14 +37,29 @@ using namespace mbed;
|
||||||
|
|
||||||
#ifdef MBED_TICKLESS
|
#ifdef MBED_TICKLESS
|
||||||
|
|
||||||
|
#if (defined(NO_SYSTICK))
|
||||||
|
/**
|
||||||
|
* Return an IRQ number that can be used in the absence of SysTick
|
||||||
|
*
|
||||||
|
* @return Free IRQ number that can be used
|
||||||
|
*/
|
||||||
|
extern "C" IRQn_Type mbed_get_m0_tick_irqn(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
class RtosTimer : private TimerEvent {
|
class RtosTimer : private TimerEvent {
|
||||||
public:
|
public:
|
||||||
RtosTimer(): TimerEvent(get_lp_ticker_data()), _start_time(0), _tick(0) {
|
RtosTimer(): TimerEvent(get_lp_ticker_data()), _start_time(0), _tick(0) {
|
||||||
_start_time = ticker_read_us(_ticker_data);
|
_start_time = ticker_read_us(_ticker_data);
|
||||||
|
#if (defined(NO_SYSTICK))
|
||||||
|
NVIC_SetVector(mbed_get_m0_tick_irqn(), (uint32_t)SysTick_Handler);
|
||||||
|
NVIC_SetPriority(mbed_get_m0_tick_irqn(), 0xFF); /* RTOS requires lowest priority */
|
||||||
|
NVIC_EnableIRQ(mbed_get_m0_tick_irqn());
|
||||||
|
#else
|
||||||
// Ensure SysTick has the correct priority as it is still used
|
// Ensure SysTick has the correct priority as it is still used
|
||||||
// to trigger software interrupts on each tick. The period does
|
// to trigger software interrupts on each tick. The period does
|
||||||
// not matter since it will never start counting.
|
// not matter since it will never start counting.
|
||||||
SysTick_Setup(16);
|
SysTick_Setup(16);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,7 +123,11 @@ public:
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void handler() {
|
void handler() {
|
||||||
|
#if (defined(NO_SYSTICK))
|
||||||
|
NVIC_SetPendingIRQ(mbed_get_m0_tick_irqn());
|
||||||
|
#else
|
||||||
SCB->ICSR = SCB_ICSR_PENDSTSET_Msk;
|
SCB->ICSR = SCB_ICSR_PENDSTSET_Msk;
|
||||||
|
#endif
|
||||||
_tick++;
|
_tick++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -299,24 +299,6 @@ void us_ticker_clear_interrupt(void)
|
||||||
|
|
||||||
#define MAX_RTC_COUNTER_VAL ((1uL << RTC_COUNTER_BITS) - 1)
|
#define MAX_RTC_COUNTER_VAL ((1uL << RTC_COUNTER_BITS) - 1)
|
||||||
|
|
||||||
/**
|
|
||||||
* The value previously set in the capture compare register of channel 1
|
|
||||||
*/
|
|
||||||
static uint32_t previous_tick_cc_value = 0;
|
|
||||||
|
|
||||||
/* The Period of RTC oscillator, unit [1/RTC1_CONFIG_FREQUENCY] */
|
|
||||||
static uint32_t os_rtc_period;
|
|
||||||
|
|
||||||
/* Variable for frozen RTC1 counter value. It is used when system timer is disabled. */
|
|
||||||
static uint32_t frozen_sub_tick = 0;
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef MBED_CONF_RTOS_PRESENT
|
|
||||||
#include "rtx_os.h" //import osRtxInfo, SysTick_Handler()
|
|
||||||
|
|
||||||
static inline void clear_tick_interrupt();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef RTC1_CONFIG_FREQUENCY
|
#ifndef RTC1_CONFIG_FREQUENCY
|
||||||
#define RTC1_CONFIG_FREQUENCY 32678 // [Hz]
|
#define RTC1_CONFIG_FREQUENCY 32678 // [Hz]
|
||||||
#endif
|
#endif
|
||||||
|
@ -325,210 +307,15 @@ static uint32_t frozen_sub_tick = 0;
|
||||||
|
|
||||||
void COMMON_RTC_IRQ_HANDLER(void)
|
void COMMON_RTC_IRQ_HANDLER(void)
|
||||||
{
|
{
|
||||||
if(nrf_rtc_event_pending(COMMON_RTC_INSTANCE, OS_TICK_EVENT)) {
|
if(!nrf_rtc_event_pending(COMMON_RTC_INSTANCE, OS_TICK_EVENT)) {
|
||||||
#ifdef MBED_CONF_RTOS_PRESENT
|
|
||||||
clear_tick_interrupt();
|
|
||||||
// Trigger the SysTick_Handler just after exit form RTC Handler.
|
|
||||||
NVIC_SetPendingIRQ(SWI3_IRQn);
|
|
||||||
|
|
||||||
nrf_gpio_pin_set(11);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
common_rtc_irq_handler();
|
common_rtc_irq_handler();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IRQn_Type mbed_get_m0_tick_irqn()
|
||||||
#ifdef MBED_CONF_RTOS_PRESENT
|
|
||||||
/**
|
|
||||||
* Return the next number of clock cycle needed for the next tick.
|
|
||||||
* @note This function has been carefully optimized for a systick occurring every 1000us.
|
|
||||||
*/
|
|
||||||
static uint32_t get_next_tick_cc_delta()
|
|
||||||
{
|
{
|
||||||
uint32_t delta = 0;
|
return SWI3_IRQn;
|
||||||
|
|
||||||
if (osRtxConfig.tick_freq != 1000) {
|
|
||||||
// In RTX, by default SYSTICK is is used.
|
|
||||||
// A tick event is generated every os_trv + 1 clock cycles of the system timer.
|
|
||||||
delta = os_rtc_period;
|
|
||||||
} else {
|
|
||||||
// If the clockrate is set to 1000us then 1000 tick should happen every second.
|
|
||||||
// Unfortunatelly, when clockrate is set to 1000, os_trv is equal to 31.
|
|
||||||
// If (os_trv + 1) is used as the delta value between two ticks, 1000 ticks will be
|
|
||||||
// generated in 32000 clock cycle instead of 32768 clock cycles.
|
|
||||||
// As a result, if a user schedule an OS timer to start in 100s, the timer will start
|
|
||||||
// instead after 97.656s
|
|
||||||
// The code below fix this issue, a clock rate of 1000s will generate 1000 ticks in 32768
|
|
||||||
// clock cycles.
|
|
||||||
// The strategy is simple, for 1000 ticks:
|
|
||||||
// * 768 ticks will occur 33 clock cycles after the previous tick
|
|
||||||
// * 232 ticks will occur 32 clock cycles after the previous tick
|
|
||||||
// By default every delta is equal to 33.
|
|
||||||
// Every five ticks (20%, 200 delta in one second), the delta is equal to 32
|
|
||||||
// The remaining (32) deltas equal to 32 are distributed using primes numbers.
|
|
||||||
static uint32_t counter = 0;
|
|
||||||
if ((counter % 5) == 0 || (counter % 31) == 0 || (counter % 139) == 0 || (counter == 503)) {
|
|
||||||
delta = 32;
|
|
||||||
} else {
|
|
||||||
delta = 33;
|
|
||||||
}
|
|
||||||
++counter;
|
|
||||||
if (counter == 1000) {
|
|
||||||
counter = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return delta;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void clear_tick_interrupt()
|
|
||||||
{
|
|
||||||
nrf_rtc_event_clear(COMMON_RTC_INSTANCE, OS_TICK_EVENT);
|
|
||||||
nrf_rtc_event_disable(COMMON_RTC_INSTANCE, OS_TICK_INT_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicate if a value is included in a range which can be wrapped.
|
|
||||||
* @param begin start of the range
|
|
||||||
* @param end end of the range
|
|
||||||
* @param val value to check
|
|
||||||
* @return true if the value is included in the range and false otherwise.
|
|
||||||
*/
|
|
||||||
static inline bool is_in_wrapped_range(uint32_t begin, uint32_t end, uint32_t val)
|
|
||||||
{
|
|
||||||
// regular case, begin < end
|
|
||||||
// return true if begin <= val < end
|
|
||||||
if (begin < end) {
|
|
||||||
if (begin <= val && val < end) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// In this case end < begin because it has wrap around the limits
|
|
||||||
// return false if end < val < begin
|
|
||||||
if (end < val && val < begin) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register the next tick.
|
|
||||||
*/
|
|
||||||
static void register_next_tick()
|
|
||||||
{
|
|
||||||
previous_tick_cc_value = nrf_rtc_cc_get(COMMON_RTC_INSTANCE, OS_TICK_CC_CHANNEL);
|
|
||||||
uint32_t delta = get_next_tick_cc_delta();
|
|
||||||
uint32_t new_compare_value = (previous_tick_cc_value + delta) & MAX_RTC_COUNTER_VAL;
|
|
||||||
|
|
||||||
// Disable irq directly for few cycles,
|
|
||||||
// Validation of the new CC value against the COUNTER,
|
|
||||||
// Setting the new CC value and enabling CC IRQ should be an atomic operation
|
|
||||||
// Otherwise, there is a possibility to set an invalid CC value because
|
|
||||||
// the RTC1 keeps running.
|
|
||||||
// This code is very short 20-38 cycles in the worst case, it shouldn't
|
|
||||||
// disturb softdevice.
|
|
||||||
__disable_irq();
|
|
||||||
uint32_t current_counter = nrf_rtc_counter_get(COMMON_RTC_INSTANCE);
|
|
||||||
|
|
||||||
// If an overflow occur, set the next tick in COUNTER + delta clock cycles
|
|
||||||
if (is_in_wrapped_range(previous_tick_cc_value, new_compare_value, current_counter + 1) == false) {
|
|
||||||
new_compare_value = current_counter + delta;
|
|
||||||
}
|
|
||||||
nrf_rtc_cc_set(COMMON_RTC_INSTANCE, OS_TICK_CC_CHANNEL, new_compare_value);
|
|
||||||
// Enable generation of the compare event for the value set above (this
|
|
||||||
// event will trigger the interrupt).
|
|
||||||
nrf_rtc_event_enable(COMMON_RTC_INSTANCE, OS_TICK_INT_MASK);
|
|
||||||
__enable_irq();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize alternative hardware timer as RTX kernel timer
|
|
||||||
* This function is directly called by RTX.
|
|
||||||
* @note this function shouldn't be called directly.
|
|
||||||
* @return IRQ number of the alternative hardware timer
|
|
||||||
*/
|
|
||||||
int32_t osRtxSysTimerSetup(void)
|
|
||||||
{
|
|
||||||
common_rtc_init();
|
|
||||||
|
|
||||||
os_rtc_period = (RTC1_CONFIG_FREQUENCY) / osRtxConfig.tick_freq;
|
|
||||||
|
|
||||||
return nrf_drv_get_IRQn(COMMON_RTC_INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start SysTickt timer emulation
|
|
||||||
void osRtxSysTimerEnable(void)
|
|
||||||
{
|
|
||||||
nrf_rtc_int_enable(COMMON_RTC_INSTANCE, OS_TICK_INT_MASK);
|
|
||||||
|
|
||||||
uint32_t current_cnt = nrf_rtc_counter_get(COMMON_RTC_INSTANCE);
|
|
||||||
nrf_rtc_cc_set(COMMON_RTC_INSTANCE, OS_TICK_CC_CHANNEL, current_cnt);
|
|
||||||
register_next_tick();
|
|
||||||
|
|
||||||
NVIC_SetVector(SWI3_IRQn, (uint32_t)SysTick_Handler);
|
|
||||||
NVIC_SetPriority(SWI3_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Emulated Systick Interrupt */
|
|
||||||
NVIC_EnableIRQ(SWI3_IRQn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop SysTickt timer emulation
|
|
||||||
void osRtxSysTimerDisable(void)
|
|
||||||
{
|
|
||||||
nrf_rtc_int_disable(COMMON_RTC_INSTANCE, OS_TICK_INT_MASK);
|
|
||||||
|
|
||||||
// RTC1 is free runing. osRtxSysTimerGetCount will return proper frozen value
|
|
||||||
// thanks to geting frozen value instead of RTC1 counter value
|
|
||||||
frozen_sub_tick = nrf_rtc_counter_get(COMMON_RTC_INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Acknowledge the tick interrupt.
|
|
||||||
* This function is called by the function OS_Tick_Handler of RTX.
|
|
||||||
* @note this function shouldn't be called directly.
|
|
||||||
*/
|
|
||||||
void osRtxSysTimerAckIRQ(void)
|
|
||||||
{
|
|
||||||
register_next_tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
// provide a free running incremental value over the entire 32-bit range
|
|
||||||
uint32_t osRtxSysTimerGetCount(void)
|
|
||||||
{
|
|
||||||
uint32_t current_cnt;
|
|
||||||
uint32_t sub_tick;
|
|
||||||
|
|
||||||
if (nrf_rtc_int_is_enabled(COMMON_RTC_INSTANCE, OS_TICK_INT_MASK)) {
|
|
||||||
// system timer is enabled
|
|
||||||
current_cnt = nrf_rtc_counter_get(COMMON_RTC_INSTANCE);
|
|
||||||
|
|
||||||
if (current_cnt >= previous_tick_cc_value) {
|
|
||||||
//0 prev current MAX
|
|
||||||
//|------|---------|------------|---->
|
|
||||||
sub_tick = current_cnt - previous_tick_cc_value;
|
|
||||||
} else {
|
|
||||||
//0 current prev MAX
|
|
||||||
//|------|---------|------------|---->
|
|
||||||
sub_tick = MAX_RTC_COUNTER_VAL - previous_tick_cc_value + current_cnt;
|
|
||||||
}
|
|
||||||
} else { // system timer is disabled
|
|
||||||
sub_tick = frozen_sub_tick;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (os_rtc_period * osRtxInfo.kernel.tick) + sub_tick;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Timer Tick frequency
|
|
||||||
uint32_t osRtxSysTimerGetFreq (void) {
|
|
||||||
return RTC1_CONFIG_FREQUENCY;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // #ifdef MBED_CONF_RTOS_PRESENT
|
|
||||||
|
|
||||||
#endif // defined(TARGET_MCU_NRF51822)
|
#endif // defined(TARGET_MCU_NRF51822)
|
||||||
|
|
|
@ -2939,7 +2939,8 @@
|
||||||
"S130",
|
"S130",
|
||||||
"TARGET_MCU_NRF51822",
|
"TARGET_MCU_NRF51822",
|
||||||
"CMSIS_VECTAB_VIRTUAL",
|
"CMSIS_VECTAB_VIRTUAL",
|
||||||
"CMSIS_VECTAB_VIRTUAL_HEADER_FILE=\"cmsis_nvic.h\""
|
"CMSIS_VECTAB_VIRTUAL_HEADER_FILE=\"cmsis_nvic.h\"",
|
||||||
|
"NO_SYSTICK"
|
||||||
],
|
],
|
||||||
"MERGE_BOOTLOADER": false,
|
"MERGE_BOOTLOADER": false,
|
||||||
"extra_labels": ["NORDIC", "MCU_NRF51", "MCU_NRF51822_UNIFIED", "NRF5", "SDK11"],
|
"extra_labels": ["NORDIC", "MCU_NRF51", "MCU_NRF51822_UNIFIED", "NRF5", "SDK11"],
|
||||||
|
|
Loading…
Reference in New Issue