Skip to content

Commit c3eae58

Browse files
committed
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.
1 parent e44d94f commit c3eae58

File tree

3 files changed

+24
-217
lines changed

3 files changed

+24
-217
lines changed

rtos/TARGET_CORTEX/mbed_rtx_idle.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,29 @@ using namespace mbed;
3737

3838
#ifdef MBED_TICKLESS
3939

40+
#if (defined(NO_SYSTICK))
41+
/**
42+
* Return an IRQ number that can be used in the absence of SysTick
43+
*
44+
* @return Free IRQ number that can be used
45+
*/
46+
extern "C" IRQn_Type mbed_get_m0_tick_irqn(void);
47+
#endif
48+
4049
class RtosTimer : private TimerEvent {
4150
public:
4251
RtosTimer(): TimerEvent(get_lp_ticker_data()), _start_time(0), _tick(0) {
4352
_start_time = ticker_read_us(_ticker_data);
53+
#if (defined(NO_SYSTICK))
54+
NVIC_SetVector(mbed_get_m0_tick_irqn(), (uint32_t)SysTick_Handler);
55+
NVIC_SetPriority(mbed_get_m0_tick_irqn(), 0xFF); /* RTOS requires lowest priority */
56+
NVIC_EnableIRQ(mbed_get_m0_tick_irqn());
57+
#else
4458
// Ensure SysTick has the correct priority as it is still used
4559
// to trigger software interrupts on each tick. The period does
4660
// not matter since it will never start counting.
4761
SysTick_Setup(16);
62+
#endif
4863
};
4964

5065
/**
@@ -108,7 +123,11 @@ class RtosTimer : private TimerEvent {
108123
protected:
109124

110125
void handler() {
126+
#if (defined(NO_SYSTICK))
127+
NVIC_SetPendingIRQ(mbed_get_m0_tick_irqn());
128+
#else
111129
SCB->ICSR = SCB_ICSR_PENDSTSET_Msk;
130+
#endif
112131
_tick++;
113132
}
114133

targets/TARGET_NORDIC/TARGET_NRF5/us_ticker.c

Lines changed: 3 additions & 216 deletions
Original file line numberDiff line numberDiff line change
@@ -299,24 +299,6 @@ void us_ticker_clear_interrupt(void)
299299

300300
#define MAX_RTC_COUNTER_VAL ((1uL << RTC_COUNTER_BITS) - 1)
301301

302-
/**
303-
* The value previously set in the capture compare register of channel 1
304-
*/
305-
static uint32_t previous_tick_cc_value = 0;
306-
307-
/* The Period of RTC oscillator, unit [1/RTC1_CONFIG_FREQUENCY] */
308-
static uint32_t os_rtc_period;
309-
310-
/* Variable for frozen RTC1 counter value. It is used when system timer is disabled. */
311-
static uint32_t frozen_sub_tick = 0;
312-
313-
314-
#ifdef MBED_CONF_RTOS_PRESENT
315-
#include "rtx_os.h" //import osRtxInfo, SysTick_Handler()
316-
317-
static inline void clear_tick_interrupt();
318-
#endif
319-
320302
#ifndef RTC1_CONFIG_FREQUENCY
321303
#define RTC1_CONFIG_FREQUENCY 32678 // [Hz]
322304
#endif
@@ -325,210 +307,15 @@ static uint32_t frozen_sub_tick = 0;
325307

326308
void COMMON_RTC_IRQ_HANDLER(void)
327309
{
328-
if(nrf_rtc_event_pending(COMMON_RTC_INSTANCE, OS_TICK_EVENT)) {
329-
#ifdef MBED_CONF_RTOS_PRESENT
330-
clear_tick_interrupt();
331-
// Trigger the SysTick_Handler just after exit form RTC Handler.
332-
NVIC_SetPendingIRQ(SWI3_IRQn);
333-
334-
nrf_gpio_pin_set(11);
335-
#endif
336-
} else {
310+
if(!nrf_rtc_event_pending(COMMON_RTC_INSTANCE, OS_TICK_EVENT)) {
337311
common_rtc_irq_handler();
338312
}
339313
}
340314

341-
342-
#ifdef MBED_CONF_RTOS_PRESENT
343-
/**
344-
* Return the next number of clock cycle needed for the next tick.
345-
* @note This function has been carefully optimized for a systick occurring every 1000us.
346-
*/
347-
static uint32_t get_next_tick_cc_delta()
348-
{
349-
uint32_t delta = 0;
350-
351-
if (osRtxConfig.tick_freq != 1000) {
352-
// In RTX, by default SYSTICK is is used.
353-
// A tick event is generated every os_trv + 1 clock cycles of the system timer.
354-
delta = os_rtc_period;
355-
} else {
356-
// If the clockrate is set to 1000us then 1000 tick should happen every second.
357-
// Unfortunatelly, when clockrate is set to 1000, os_trv is equal to 31.
358-
// If (os_trv + 1) is used as the delta value between two ticks, 1000 ticks will be
359-
// generated in 32000 clock cycle instead of 32768 clock cycles.
360-
// As a result, if a user schedule an OS timer to start in 100s, the timer will start
361-
// instead after 97.656s
362-
// The code below fix this issue, a clock rate of 1000s will generate 1000 ticks in 32768
363-
// clock cycles.
364-
// The strategy is simple, for 1000 ticks:
365-
// * 768 ticks will occur 33 clock cycles after the previous tick
366-
// * 232 ticks will occur 32 clock cycles after the previous tick
367-
// By default every delta is equal to 33.
368-
// Every five ticks (20%, 200 delta in one second), the delta is equal to 32
369-
// The remaining (32) deltas equal to 32 are distributed using primes numbers.
370-
static uint32_t counter = 0;
371-
if ((counter % 5) == 0 || (counter % 31) == 0 || (counter % 139) == 0 || (counter == 503)) {
372-
delta = 32;
373-
} else {
374-
delta = 33;
375-
}
376-
++counter;
377-
if (counter == 1000) {
378-
counter = 0;
379-
}
380-
}
381-
return delta;
382-
}
383-
384-
static inline void clear_tick_interrupt()
385-
{
386-
nrf_rtc_event_clear(COMMON_RTC_INSTANCE, OS_TICK_EVENT);
387-
nrf_rtc_event_disable(COMMON_RTC_INSTANCE, OS_TICK_INT_MASK);
388-
}
389-
390-
/**
391-
* Indicate if a value is included in a range which can be wrapped.
392-
* @param begin start of the range
393-
* @param end end of the range
394-
* @param val value to check
395-
* @return true if the value is included in the range and false otherwise.
396-
*/
397-
static inline bool is_in_wrapped_range(uint32_t begin, uint32_t end, uint32_t val)
398-
{
399-
// regular case, begin < end
400-
// return true if begin <= val < end
401-
if (begin < end) {
402-
if (begin <= val && val < end) {
403-
return true;
404-
} else {
405-
return false;
406-
}
407-
} else {
408-
// In this case end < begin because it has wrap around the limits
409-
// return false if end < val < begin
410-
if (end < val && val < begin) {
411-
return false;
412-
} else {
413-
return true;
414-
}
415-
}
416-
417-
}
418-
419-
/**
420-
* Register the next tick.
421-
*/
422-
static void register_next_tick()
423-
{
424-
previous_tick_cc_value = nrf_rtc_cc_get(COMMON_RTC_INSTANCE, OS_TICK_CC_CHANNEL);
425-
uint32_t delta = get_next_tick_cc_delta();
426-
uint32_t new_compare_value = (previous_tick_cc_value + delta) & MAX_RTC_COUNTER_VAL;
427-
428-
// Disable irq directly for few cycles,
429-
// Validation of the new CC value against the COUNTER,
430-
// Setting the new CC value and enabling CC IRQ should be an atomic operation
431-
// Otherwise, there is a possibility to set an invalid CC value because
432-
// the RTC1 keeps running.
433-
// This code is very short 20-38 cycles in the worst case, it shouldn't
434-
// disturb softdevice.
435-
__disable_irq();
436-
uint32_t current_counter = nrf_rtc_counter_get(COMMON_RTC_INSTANCE);
437-
438-
// If an overflow occur, set the next tick in COUNTER + delta clock cycles
439-
if (is_in_wrapped_range(previous_tick_cc_value, new_compare_value, current_counter + 1) == false) {
440-
new_compare_value = current_counter + delta;
441-
}
442-
nrf_rtc_cc_set(COMMON_RTC_INSTANCE, OS_TICK_CC_CHANNEL, new_compare_value);
443-
// Enable generation of the compare event for the value set above (this
444-
// event will trigger the interrupt).
445-
nrf_rtc_event_enable(COMMON_RTC_INSTANCE, OS_TICK_INT_MASK);
446-
__enable_irq();
447-
}
448-
449-
450-
/**
451-
* Initialize alternative hardware timer as RTX kernel timer
452-
* This function is directly called by RTX.
453-
* @note this function shouldn't be called directly.
454-
* @return IRQ number of the alternative hardware timer
455-
*/
456-
int32_t osRtxSysTimerSetup(void)
457-
{
458-
common_rtc_init();
459-
460-
os_rtc_period = (RTC1_CONFIG_FREQUENCY) / osRtxConfig.tick_freq;
461-
462-
return nrf_drv_get_IRQn(COMMON_RTC_INSTANCE);
463-
}
464-
465-
// Start SysTickt timer emulation
466-
void osRtxSysTimerEnable(void)
467-
{
468-
nrf_rtc_int_enable(COMMON_RTC_INSTANCE, OS_TICK_INT_MASK);
469-
470-
uint32_t current_cnt = nrf_rtc_counter_get(COMMON_RTC_INSTANCE);
471-
nrf_rtc_cc_set(COMMON_RTC_INSTANCE, OS_TICK_CC_CHANNEL, current_cnt);
472-
register_next_tick();
473-
474-
NVIC_SetVector(SWI3_IRQn, (uint32_t)SysTick_Handler);
475-
NVIC_SetPriority(SWI3_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Emulated Systick Interrupt */
476-
NVIC_EnableIRQ(SWI3_IRQn);
477-
}
478-
479-
// Stop SysTickt timer emulation
480-
void osRtxSysTimerDisable(void)
315+
IRQn_Type mbed_get_m0_tick_irqn()
481316
{
482-
nrf_rtc_int_disable(COMMON_RTC_INSTANCE, OS_TICK_INT_MASK);
483-
484-
// RTC1 is free runing. osRtxSysTimerGetCount will return proper frozen value
485-
// thanks to geting frozen value instead of RTC1 counter value
486-
frozen_sub_tick = nrf_rtc_counter_get(COMMON_RTC_INSTANCE);
487-
}
488-
489-
490-
491-
/**
492-
* Acknowledge the tick interrupt.
493-
* This function is called by the function OS_Tick_Handler of RTX.
494-
* @note this function shouldn't be called directly.
495-
*/
496-
void osRtxSysTimerAckIRQ(void)
497-
{
498-
register_next_tick();
499-
}
500-
501-
// provide a free running incremental value over the entire 32-bit range
502-
uint32_t osRtxSysTimerGetCount(void)
503-
{
504-
uint32_t current_cnt;
505-
uint32_t sub_tick;
506-
507-
if (nrf_rtc_int_is_enabled(COMMON_RTC_INSTANCE, OS_TICK_INT_MASK)) {
508-
// system timer is enabled
509-
current_cnt = nrf_rtc_counter_get(COMMON_RTC_INSTANCE);
510-
511-
if (current_cnt >= previous_tick_cc_value) {
512-
//0 prev current MAX
513-
//|------|---------|------------|---->
514-
sub_tick = current_cnt - previous_tick_cc_value;
515-
} else {
516-
//0 current prev MAX
517-
//|------|---------|------------|---->
518-
sub_tick = MAX_RTC_COUNTER_VAL - previous_tick_cc_value + current_cnt;
519-
}
520-
} else { // system timer is disabled
521-
sub_tick = frozen_sub_tick;
522-
}
523-
524-
return (os_rtc_period * osRtxInfo.kernel.tick) + sub_tick;
525-
}
526-
527-
// Timer Tick frequency
528-
uint32_t osRtxSysTimerGetFreq (void) {
529-
return RTC1_CONFIG_FREQUENCY;
317+
return SWI3_IRQn;
530318
}
531319

532-
#endif // #ifdef MBED_CONF_RTOS_PRESENT
533320

534321
#endif // defined(TARGET_MCU_NRF51822)

targets/targets.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2939,7 +2939,8 @@
29392939
"S130",
29402940
"TARGET_MCU_NRF51822",
29412941
"CMSIS_VECTAB_VIRTUAL",
2942-
"CMSIS_VECTAB_VIRTUAL_HEADER_FILE=\"cmsis_nvic.h\""
2942+
"CMSIS_VECTAB_VIRTUAL_HEADER_FILE=\"cmsis_nvic.h\"",
2943+
"NO_SYSTICK"
29432944
],
29442945
"MERGE_BOOTLOADER": false,
29452946
"extra_labels": ["NORDIC", "MCU_NRF51", "MCU_NRF51822_UNIFIED", "NRF5", "SDK11"],

0 commit comments

Comments
 (0)