From c01b066d86915a052a862b813e48ece21c7361a0 Mon Sep 17 00:00:00 2001 From: Sasan Golchin <ahmad.golchin@mail.polimi.it> Date: Sun, 10 Jan 2016 19:39:01 +0100 Subject: [PATCH] Aperiodic Timer - Tickless Kernel The kernel's API has been amended such that the periodic context switch works along with exact awakening of the sleeping threads: 1) SleepData is not in terms of kernel's tick anymore but directly in terms of context switch timer tick. 2) Sleeping threads are being awakened in the time they have requested rather than just waiting for the context switch to happen. 3) Three functions are added to the kernel's API: -tickSleepUntil(tick) which is private to the kernel and is the main reference of other sleep functions of a thread and it's implemented as an inline function to keep both maintainability and efficiency -Thread::nanoSleep -Thread::nanoSleepUntil 4) Besides sleep functions, the following functions has been updated in order to keep the timer interrupt sync with the head of the sleeping list. I'm not sure about this being correct and it may have some conflicts with the scheduler's policy: -IRQwakeThreads: After removing the head of the sleeping list, the next interrupt should be set to the head again! Another change in this is that it does not expect the exact equivalence between timer's tick and wakeup_time as when the kernel reaches that particular point the timer's tick has already passed the wakeup_time. -IRQaddToSleepingList: Due to the change in order of items in the sleeping list, the interrupt should be set again. 5) Constructor and Interrupt handler of the context switch timer has been amended in order to put the next context switch time as a record in the sleeping list. As the meaning of tick in sleep functions has been changed, the test suit does not work properly with option t anymore but some critical tests has been done on the current state of the project and everything seems to work fine but some optimizations are needed specially in the case that the time window between items in the list is very short. Signed-off-by: Sasan Golchin <ahmad.golchin@mail.polimi.it> Signed-off-by: Terraneo Federico <fede.tft@miosix.org> --- main.cpp | 4 +- .../common/interfaces-impl/cstimer.cpp | 45 +++++++++++--- miosix/kernel/kernel.cpp | 61 ++++++++++++------- miosix/kernel/kernel.h | 18 +++++- 4 files changed, 93 insertions(+), 35 deletions(-) diff --git a/main.cpp b/main.cpp index 69d1f944..83f1cf37 100644 --- a/main.cpp +++ b/main.cpp @@ -9,14 +9,14 @@ using namespace miosix; static void t1Task(void* p){ while (true){ IRQbootlog("1\r\n"); - Thread::sleep(1000); + Thread::sleep(500); } } int main(){ //ContextSwitchTimer::instance(); Thread::setPriority(1); - printf("Context Switch Timer ....T=1ms\n"); + printf("Context Switch Timer (APERIODIC) ....T=1ms\n"); Thread *p=Thread::create(t1Task,512,1,NULL); while (true){ IRQbootlog("0\r\n"); diff --git a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp index 75b9370f..dde3b924 100644 --- a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp +++ b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp @@ -1,16 +1,21 @@ #include "miosix.h" #include "interfaces/cstimer.h" #include "interfaces/portability.h" +#include "kernel/kernel.h" #include "kernel/logging.h" #include <cstdlib> using namespace miosix; +namespace miosix{ +void IRQaddToSleepingList(SleepData *x); +extern SleepData *sleeping_list; +} static long long cst_ms32time = 0; //most significant 32 bits of counter static long long cst_ms32chkp = 0; //most significant 32 bits of check point static bool lateIrq=false; -static long long cst_prevChk; +static SleepData csRecord; -#define CST_QUANTOM 84000 +#define CST_QUANTOM 84000*4 #define CST_ROLLOVER_INT (TIM2->SR & TIM_SR_UIF) && (TIM2->SR & TIM_SR_CC2IF) namespace miosix_private{ @@ -58,8 +63,12 @@ ContextSwitchTimer::ContextSwitchTimer() { * Other initializations */ // Set the first checkpoint interrupt - cst_prevChk = CST_QUANTOM; - setNextInterrupt(cst_prevChk); + csRecord.p = 0; + csRecord.wakeup_time = CST_QUANTOM; + csRecord.next = sleeping_list; + sleeping_list = &csRecord; + setNextInterrupt(CST_QUANTOM); + //IRQaddToSleepingList(&csRecord); //Recursive Initialization error // Enable TIM2 Counter cst_ms32time = 0; TIM2->EGR = TIM_EGR_UG; //To enforce the timer to apply PSC (and other non-immediate settings) @@ -109,12 +118,34 @@ void __attribute__((used)) cstirqhnd(){ TIM2->SR = ~TIM_SR_CC1IF; if(cst_ms32time==cst_ms32chkp || lateIrq){ lateIrq=false; + long long tick = ContextSwitchTimer::instance().getCurrentTick(); //Set next checkpoint interrupt ////cst_prevChk += CST_QUANTOM; ////ContextSwitchTimer::instance().setNextInterrupt(cst_prevChk); - ContextSwitchTimer::instance().setNextInterrupt( - ContextSwitchTimer::instance().getCurrentTick() + CST_QUANTOM); - //Call preempt routine + //ContextSwitchTimer::instance().setNextInterrupt( + // ContextSwitchTimer::instance().getCurrentTick() + CST_QUANTOM); + + //Add next context switch time to the sleeping list iff this is a + //context switch + + if (tick > csRecord.wakeup_time){ + //Remove the cs item from the sleeping list manually + if (sleeping_list==&csRecord) + sleeping_list=sleeping_list->next; + SleepData* slp = sleeping_list; + while (slp!=NULL){ + if (slp->next==&csRecord){ + slp->next=slp->next->next; + break; + } + slp = slp->next; + } + //Add next cs item to the list via IRQaddToSleepingList + //Note that the next timer interrupt is set by IRQaddToSleepingList + //according to the head of the list! + csRecord.wakeup_time += CST_QUANTOM; + IRQaddToSleepingList(&csRecord); //It would also set the next timer interrupt + } miosix_private::ISR_preempt(); } diff --git a/miosix/kernel/kernel.cpp b/miosix/kernel/kernel.cpp index ba5dd940..c897e2aa 100644 --- a/miosix/kernel/kernel.cpp +++ b/miosix/kernel/kernel.cpp @@ -37,6 +37,7 @@ #include "process.h" #include "kernel/scheduler/scheduler.h" #include "stdlib_integration/libc_integration.h" +#include "interfaces/cstimer.h" #include <stdexcept> #include <algorithm> #include <string.h> @@ -62,7 +63,7 @@ volatile Thread *cur=NULL;///<\internal Thread currently running ///\internal True if there are threads in the DELETED status. Used by idle thread static volatile bool exist_deleted=false; -static SleepData *sleeping_list=NULL;///<\internal list of sleeping threads +SleepData *sleeping_list=NULL;///list of sleeping threads static volatile long long tick=0;///<\internal Kernel tick @@ -256,6 +257,10 @@ void IRQaddToSleepingList(SleepData *x) cur=cur->next; } } + //Upon any change to the sleeping_list the ContextSwitchTimer should have + //its interrupt set to the head of the list in order to keep it sync with + //the list + ContextSwitchTimer::instance().setNextInterrupt(sleeping_list->wakeup_time); } /** @@ -275,10 +280,14 @@ bool IRQwakeThreads() if(sleeping_list==NULL) break;//If no item in list, return //Since list is sorted, if we don't need to wake the first element //we don't need to wake the other too - if(tick != sleeping_list->wakeup_time) break; - sleeping_list->p->flags.IRQsetSleep(false);//Wake thread + //if(tick != sleeping_list->wakeup_time) break; + if(ContextSwitchTimer::instance().getCurrentTick() < sleeping_list->wakeup_time) break; + if (sleeping_list->p != 0) //distinguish between context switches and sleeps + sleeping_list->p->flags.IRQsetSleep(false);//Wake thread sleeping_list=sleeping_list->next;//Remove from list result=true; + //update interrupt of context switch timer + ContextSwitchTimer::instance().setNextInterrupt(sleeping_list->wakeup_time); } return result; } @@ -343,9 +352,12 @@ bool Thread::testTerminate() return const_cast<Thread*>(cur)->flags.isDeleting(); } -void Thread::sleep(unsigned int ms) -{ - if(ms==0) return; +inline void Thread::tickSleepUntil(long long absTicks){ + //absTicks: As it is in terms of real ticks of the kernel/timer, there's no + //resolution issues here. + //This function does not care about setting the wakeup_time in the past + //as it should be based on the policy taken into account by IRQwakeThreads + //The SleepData variable has to be in scope till Thread::yield() returns //as IRQaddToSleepingList() makes it part of a linked list till the //thread wakes up (i.e: after Thread::yield() returns) @@ -355,30 +367,33 @@ void Thread::sleep(unsigned int ms) { FastInterruptDisableLock lock; d.p=const_cast<Thread*>(cur); - if(((ms*TICK_FREQ)/1000)>0) d.wakeup_time=getTick()+(ms*TICK_FREQ)/1000; - //If tick resolution is too low, wait one tick - else d.wakeup_time=getTick()+1; + d.wakeup_time = absTicks; IRQaddToSleepingList(&d);//Also sets SLEEP_FLAG } Thread::yield(); } +void Thread::nanoSleep(unsigned int ns){ + if(ns==0) return; //To-Do: should be (ns < resolution + epsilon) + long long ticks = ns * 0.084;//To-do: ns2tick fast conversion needed + tickSleepUntil(ContextSwitchTimer::instance().getCurrentTick() + ticks); +} + +void Thread::nanoSleepUntil(long long absoluteTime){ + //To-Do: The absolute time should be rounded w.r.t. the timer resolution + long long ticks = absoluteTime * 0.084;//To-do: ns2tick fast conversion needed + if (ticks <= ContextSwitchTimer::instance().getCurrentTick()) return; + tickSleepUntil(ticks); +} + +void Thread::sleep(unsigned int ms) +{ + nanoSleep(ms * 1000000); +} + void Thread::sleepUntil(long long absoluteTime) { - //The SleepData variable has to be in scope till Thread::yield() returns - //as IRQaddToSleepingList() makes it part of a linked list till the - //thread wakes up (i.e: after Thread::yield() returns) - SleepData d; - //pauseKernel() here is not enough since even if the kernel is stopped - //the tick isr will wake threads, modifying the sleeping_list - { - FastInterruptDisableLock lock; - if(absoluteTime<=getTick()) return; //Wakeup time in the past, return - d.p=const_cast<Thread*>(cur); - d.wakeup_time=absoluteTime; - IRQaddToSleepingList(&d);//Also sets SLEEP_FLAG - } - Thread::yield(); + nanoSleepUntil(absoluteTime * 1000000); } Thread *Thread::getCurrentThread() diff --git a/miosix/kernel/kernel.h b/miosix/kernel/kernel.h index 9aeb0745..5c3f5c54 100644 --- a/miosix/kernel/kernel.h +++ b/miosix/kernel/kernel.h @@ -497,7 +497,7 @@ public: * CANNOT be called when the kernel is paused. */ static void sleep(unsigned int ms); - + static void nanoSleep(unsigned int ns); /** * Put the thread to sleep until the specified absolute time is reached. * If the time is in the past, returns immediately. @@ -521,7 +521,7 @@ public: * CANNOT be called when the kernel is paused. */ static void sleepUntil(long long absoluteTime); - + static void nanoSleepUntil(long long absoluteTime); /** * Return a pointer to the Thread class of the current thread. * \return a pointer to the current thread. @@ -718,7 +718,19 @@ private: //Unwanted methods Thread(const Thread& p);///< No public copy constructor Thread& operator = (const Thread& p);///< No publc operator = - + /** + * This is the base function for adding a thread to the sleeping list. + * Other functions such as nanoSleep, sleep and sleepUntil are base on + * appropriate calls to tickSleepUntil. + * To both keep maintainability and fast response, it is implemented as an + * inline function. It's better for the caller to put this call in the end + * of its code flow as tickSleepUntil would make the thread to yield. + * @param absTicks: For a tickless kernel (i.e. it uses an external aperiodic + * timer) absTicks is in terms of aperiodic timer tick otherwise it is in + * terms of kernel's tick! + */ + static void tickSleepUntil(long long absTicks); + class ThreadFlags { public: -- GitLab