Skip to content

Commit

Permalink
Add tickless idle support (suppress peridic timer interrupt when slee…
Browse files Browse the repository at this point in the history
…ping).
  • Loading branch information
AriZuu committed Apr 18, 2016
1 parent f87b0e3 commit 325f015
Show file tree
Hide file tree
Showing 8 changed files with 575 additions and 17 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ There is some information about these in [my blogs][2].

Other changes are:

- Power management API to provide framework for MCU power saving / sleeping features
- Suppression of timer interrupts during idle mode (tickless idle). Complete implementation is available in cortex-m/stm32 port.
- stdarg support for nano layer printf-functions
- Makefile system uses GNU make pattern rules for source directory handling (otherwise projects that had many directories run into troubles)
- Power management API to provide framework for MCU power saving / sleeping features


Updated doxygen manual is available [here][3].
Expand Down
14 changes: 12 additions & 2 deletions examples/poscfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -522,16 +522,26 @@
/** Enable power management api.
* If this definition is set to 1, pico]OS power management functions
* are included into kernel. Note that architecture port must support
* power management.
* power management. Architecture port is expected to provide
* implementation for ::p_pos_powerSleep.
*/
#define POSCFG_FEATURE_POWER 0

/** Enable interrupt wakeup support in power management.
* If this definition is set to 1, pico]OS allows system to
* stay in sleep until a interrupt causes context switch away
* from idle task.
* from idle task. If not enabled, it is assumed that
* interrupt will wake up a sleeping system. Architecture port
* is expected to privide implementation for ::p_pos_powerWakeup.
*/
#define POSCFG_FEATURE_POWER_WAKEUP 1

/** Enable supression of periodic timer interrupt when
* when system is sleeping to save power. Architecture port
* is expected to provide implementations for ::p_pos_powerTickSuspend and
* ::p_pos_powerTickResume.
*/
#define POSCFG_FEATURE_TICKLESS 1
/** @} */


Expand Down
51 changes: 50 additions & 1 deletion inc/picoos.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
* - idle system placed into sleep to save power
* - optional support for filtering out wakeups that won't result
* in task activation
* - tickless idle support
*
* <b>Miscellaneous:</b>
* - atomic variables
Expand Down Expand Up @@ -1512,8 +1513,32 @@ POSEXTERN void POSCALL c_pos_intExitQuick(void); /* picoos.c */
*/
POSEXTERN void POSCALL c_pos_timerInterrupt(void); /* picoos.c */

#if (DOX!=0) || POSCFG_FEATURE_POWER == 1
#if (DOX!=0) || POSCFG_FEATURE_TICKLESS == 1

/**
* Timer interrupt control function.
* This function can be called instead of ::c_pos_timerInterrupt when
* using dynamic tick rate (0 <= tick rate < HZ)to save power. Inside ::p_pos_powerSleep,
* ::c_pos_nextWakeup() can be used to obtain expected amount of idle time.
* Tick rate can then be slowed down or completely suspended until
* idle time has expired. Processor is then put into sleep. After wakeup,
* number of ticks spent in sleep should be calculated and
* passed to pico]OS kernel with this function.
*
* @sa p_pos_powerSleep, c_pos_nextWakeup
*/
POSEXTERN void POSCALL c_pos_timerStep(UVAR_t ticks); /* picoos.c */

/**
* Task function.
* Return number of ticks until next task should wake up.
* @return Number of ticks system can safely sleep or INFINITE.
*/
POSEXTERN UINT_t POSCALL c_pos_nextWakeup(void);

#endif

#if (DOX!=0) || POSCFG_FEATURE_POWER == 1
/**
* Called by idle task when system should fall in sleep.
* @note This function is not part of the pico]OS. It must be
Expand All @@ -1537,6 +1562,30 @@ POSFROMEXT void POSCALL p_pos_powerWakeup(void); /* arch_c.c */
#endif
#endif

#if (DOX!=0) || POSCFG_FEATURE_TICKLESS == 1
/**
* Called by p_pos_powerSleep to begin tickless idle period. Function
* should disable periodic timer interrupt and initialize a timer
* that wakes system up from sleep after given time has passed.
* @note This function is not part of the pico]OS. It must be
* provided by the user, since it is architecture specific.
* @sa p_pos_powerSleep, p_pos_powerTickResume
*/
POSFROMEXT void POSCALL p_pos_powerTickSuspend(UVAR_t ticks);

/**
* Called by p_pos_powerSleep to end tickless idle period. Function
* should cancel possible wakeup timer and update system tick count
* by calling c_pos_timerStep with amount of time slept. After that
* periodic timer tick must be re-enabled.
* @note This function is not part of the pico]OS. It must be
* provided by the user, since it is architecture specific.
* @sa p_pos_powerSleep, p_pos_powerTickSuspend
*/
POSFROMEXT void POSCALL p_pos_powerTickResume(void);

#endif

/** @} */


Expand Down
59 changes: 48 additions & 11 deletions ports/cortex-m/arch_c.c
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,31 @@ void p_pos_powerSleep()
return;

#endif
#endif

uint32_t oldStatus;

// Ensure flag that __WFE waits for is not set yet
__SEV();
__WFE();

#if POSCFG_FEATURE_TICKLESS

UVAR_t nextWake = c_pos_nextWakeup();
bool restoreTick = false;
bool restoreDeepSleep = false;

if (nextWake < MS(100)) {

restoreDeepSleep = SCB->SCR & SCB_SCR_SLEEPDEEP_Msk;
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
}
else {

p_pos_powerTickSuspend(nextWake);
restoreTick = true;
}

#endif

#if POSCFG_FEATURE_POWER_WAKEUP != 0
Expand All @@ -574,28 +599,40 @@ void p_pos_powerSleep()
SCB->SCR |= SCB_SCR_SLEEPONEXIT_Msk; // Sleep after interrupt
#endif

uint32_t oldStatus;

// Ensure flag that __WFE waits for is not set yet
__SEV();
__WFE();

#if __CORTEX_M < 3

oldStatus = __get_PRIMASK();
__disable_irq();
__WFE();
if (oldStatus)
__enable_irq();
__enable_irq();

#else

oldStatus = __get_BASEPRI();
__set_BASEPRI(0);

#endif

__DSB();
__WFE();

#if __CORTEX_M < 3

__disableirq();

#else

__set_BASEPRI(oldStatus);

#endif

#if POSCFG_FEATURE_TICKLESS

if (restoreDeepSleep)
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;

if (restoreTick)
p_pos_powerTickResume();

#endif

}

#endif
Expand Down
1 change: 1 addition & 0 deletions ports/cortex-m/port.h
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,7 @@ extern unsigned char *portIrqStack;
void portInitClock(void);
void portInitConsole(void);
void portSystemInit(void);
void portRestoreClocksAfterWakeup(void);

typedef void (* const PortExcHandlerFunc)(void);

Expand Down
36 changes: 34 additions & 2 deletions ports/cortex-m/stm32/con_usart.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ void portInitConsole(void)
USART_Init(USARTx, &init);
USART_Cmd(USARTx, ENABLE);

#if POSCFG_FEATURE_POWER

// Enable transmission complete interrupt
#endif

#if NOSCFG_FEATURE_CONIN == 1

// Enable receive data interrupt.
Expand All @@ -73,6 +78,10 @@ void portInitConsole(void)
NVIC_EnableIRQ(USARTx_IRQn);
}

#if POSCFG_FEATURE_POWER
static bool busy = false;
#endif

/*
* Uart interrupt handler.
*/
Expand All @@ -82,10 +91,22 @@ void USARTx_IRQHandler()
c_pos_intEnter();

#if NOSCFG_FEATURE_CONOUT == 1

#if POSCFG_FEATURE_POWER
if (USART_GetITStatus(USARTx, USART_IT_TC) == SET) {

USART_ITConfig(USARTx, USART_IT_TC, DISABLE);
if (busy) {

posPowerEnableSleep();
busy = false;
}
}
#endif

if (USART_GetITStatus(USARTx, USART_IT_TXE) == SET) {

USART_ITConfig (USARTx, USART_IT_TXE, DISABLE);
posPowerEnableSleep();
c_nos_putcharReady();
}
#endif
Expand Down Expand Up @@ -116,9 +137,20 @@ p_putchar(char c)
if (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET)
return 0;

posPowerDisableSleep();
USART_SendData(USARTx, c);
USART_ITConfig (USARTx, USART_IT_TXE, ENABLE);

#if POSCFG_FEATURE_POWER

if (!busy) {

USART_ITConfig(USARTx, USART_IT_TC, ENABLE);
posPowerDisableSleep();
busy = true;
}

#endif

return 1;
}
#endif
Expand Down
Loading

0 comments on commit 325f015

Please sign in to comment.