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
|
||||
|
||||
#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 {
|
||||
public:
|
||||
RtosTimer(): TimerEvent(get_lp_ticker_data()), _start_time(0), _tick(0) {
|
||||
_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
|
||||
// to trigger software interrupts on each tick. The period does
|
||||
// not matter since it will never start counting.
|
||||
SysTick_Setup(16);
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -108,7 +123,11 @@ public:
|
|||
protected:
|
||||
|
||||
void handler() {
|
||||
#if (defined(NO_SYSTICK))
|
||||
NVIC_SetPendingIRQ(mbed_get_m0_tick_irqn());
|
||||
#else
|
||||
SCB->ICSR = SCB_ICSR_PENDSTSET_Msk;
|
||||
#endif
|
||||
_tick++;
|
||||
}
|
||||
|
||||
|
|
|
@ -299,24 +299,6 @@ void us_ticker_clear_interrupt(void)
|
|||
|
||||
#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
|
||||
#define RTC1_CONFIG_FREQUENCY 32678 // [Hz]
|
||||
#endif
|
||||
|
@ -325,210 +307,15 @@ static uint32_t frozen_sub_tick = 0;
|
|||
|
||||
void COMMON_RTC_IRQ_HANDLER(void)
|
||||
{
|
||||
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 {
|
||||
if(!nrf_rtc_event_pending(COMMON_RTC_INSTANCE, OS_TICK_EVENT)) {
|
||||
common_rtc_irq_handler();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#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()
|
||||
IRQn_Type mbed_get_m0_tick_irqn()
|
||||
{
|
||||
uint32_t delta = 0;
|
||||
|
||||
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;
|
||||
return SWI3_IRQn;
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -2939,7 +2939,8 @@
|
|||
"S130",
|
||||
"TARGET_MCU_NRF51822",
|
||||
"CMSIS_VECTAB_VIRTUAL",
|
||||
"CMSIS_VECTAB_VIRTUAL_HEADER_FILE=\"cmsis_nvic.h\""
|
||||
"CMSIS_VECTAB_VIRTUAL_HEADER_FILE=\"cmsis_nvic.h\"",
|
||||
"NO_SYSTICK"
|
||||
],
|
||||
"MERGE_BOOTLOADER": false,
|
||||
"extra_labels": ["NORDIC", "MCU_NRF51", "MCU_NRF51822_UNIFIED", "NRF5", "SDK11"],
|
||||
|
|
Loading…
Reference in New Issue