diff --git a/main.cpp b/main.cpp index 69d1f944145575126cf1c1e30992727caf8ce558..83f1cf3764713f91f1660ff3363754e8fe04feed 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 75b9370f109d88f5946d04089f6a15544880d3d3..dde3b92463f58a4caf48f9a01989125086e9ca4f 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 ba5dd9403477843b9837b6d9f501cc32e02cd8dd..c897e2aad07375c73ccd4fa2741913d8d3f3243e 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 9aeb07459e601a5325b7bbe641e01f13a1498aa4..5c3f5c54b94884fe67b6193c09bfbf325646d375 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: