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
Russ Butler 2017-08-29 20:48:39 -05:00 committed by Russ Butler
parent e44d94fa1e
commit c3eae587eb
3 changed files with 24 additions and 217 deletions

View File

@ -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++;
}

View File

@ -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)

View File

@ -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"],