diff --git a/miosix/arch/cortexM3_efm32gg/common/interfaces-impl/portability.cpp b/miosix/arch/cortexM3_efm32gg/common/interfaces-impl/portability.cpp index c5312bebf740cec4a4776b4ee1ff2f884c5e30ac..09f8d064fede69c5180229ff8ca3eb59509c75fe 100644 --- a/miosix/arch/cortexM3_efm32gg/common/interfaces-impl/portability.cpp +++ b/miosix/arch/cortexM3_efm32gg/common/interfaces-impl/portability.cpp @@ -97,7 +97,7 @@ namespace miosix_private { void ISR_preempt() __attribute__((noinline)); void ISR_preempt() { - miosix::IRQtimerInterrupt(); + miosix::IRQtimerInterrupt(0 /*TODO dummy parameter */); } /** diff --git a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp index 8bbbce9c8d8b07e5ae74fc40dcb6a2139cc696a3..a62a00247fdf90d9a37f421e72fb278d8f2a180a 100644 --- a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp +++ b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp @@ -8,17 +8,11 @@ using namespace miosix; -namespace miosix { -void IRQaddToSleepingList(SleepData *x); -extern SleepData *sleeping_list; -} - //TODO: comment me static const uint32_t threshold = 0xffffffff/4*3; static long long ms32time = 0; //most significant 32 bits of counter static long long ms32chkp = 0; //most significant 32 bits of check point static bool lateIrq=false; -static SleepData csRecord; static inline long long nextInterrupt() { @@ -54,33 +48,8 @@ void __attribute__((used)) cstirqhnd() if(ms32time==ms32chkp || lateIrq) { lateIrq=false; - long long tick = IRQgetTick(); - - //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_QUANTUM; - IRQaddToSleepingList(&csRecord); //It would also set the next timer interrupt - } - IRQtimerInterrupt(); + IRQtimerInterrupt(nextInterrupt()); } } @@ -166,13 +135,6 @@ ContextSwitchTimer::ContextSwitchTimer() TIM2->PSC = 0; TIM2->ARR = 0xFFFFFFFF; - // Other initializations - // Set the first checkpoint interrupt - csRecord.p = 0; //FIXME: remove these when removing direct sleeping_list access - csRecord.wakeup_time = CST_QUANTUM; - csRecord.next = sleeping_list; - sleeping_list = &csRecord; - // IRQaddToSleepingList(&csRecord); //Recursive Initialization error FIXME: why commented? // Enable TIM2 Counter ms32time = 0; TIM2->EGR = TIM_EGR_UG; //To enforce the timer to apply PSC (and other non-immediate settings) diff --git a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp index 3fe65752b17e7c497e222f427162ca3a6fcc9d7a..6f548766f4ad793e8a3e8f48d9353a78a170e353 100644 --- a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp +++ b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp @@ -106,7 +106,7 @@ namespace miosix_private { void ISR_preempt() __attribute__((noinline)); void ISR_preempt() { - miosix::IRQtimerInterrupt(); + miosix::IRQtimerInterrupt(0 /*TODO dummy parameter */); } #endif //USE_CSTIMER @@ -312,8 +312,6 @@ void IRQportableStartKernel() SysTick->LOAD=SystemCoreClock/miosix::TICK_FREQ; SysTick->CTRL=SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_CLKSOURCE_Msk; -#else //USE ContextSwitchTimer class (TIM2) - miosix::ContextSwitchTimer::instance().IRQsetNextInterrupt(CST_QUANTUM); #endif #ifdef WITH_PROCESSES miosix::IRQenableMPUatBoot(); diff --git a/miosix/interfaces/cstimer.h b/miosix/interfaces/cstimer.h index f11337824ad1eb33348d054f6e072e91e5751d38..6bb06eac4481dd6cacb27e7aa280294869c16caa 100644 --- a/miosix/interfaces/cstimer.h +++ b/miosix/interfaces/cstimer.h @@ -3,8 +3,6 @@ namespace miosix { -#define CST_QUANTUM 84000 ///FIXME: remove - /** * This class is a low level interface to a hardware timer, that is used as * the basis for the Miosix timing infrastructure. In detail, it allows to diff --git a/miosix/kernel/kernel.cpp b/miosix/kernel/kernel.cpp index 924db01a8e9795d5ecb690a5bdeef06e08879a3b..79e8c50762446ae8278b28c461245f74b8c5c7ab 100644 --- a/miosix/kernel/kernel.cpp +++ b/miosix/kernel/kernel.cpp @@ -81,6 +81,13 @@ bool kernel_started=false;///<\internal becomes true after startKernel. /// calls to these functions. static unsigned char interruptDisableNesting=0; +#ifdef USE_CSTIMER + +/// Used for context switches with the high resolution timer +static SleepData csRecord; + +#endif //USE_CSTIMER + #ifdef WITH_PROCESSES /// The proc field of the Thread class for kernel threads points to this object @@ -200,9 +207,16 @@ void startKernel() // Make the C standard library use per-thread reeentrancy structure setCReentrancyCallback(Thread::getCReent); - // Now kernel is started - // Dispatch the task to the architecture-specific function + #ifdef USE_CSTIMER + // Set the first checkpoint interrupt +#define CST_QUANTUM 84000 //FIXME: find a way to propagate this here!!! + csRecord.p = 0; + csRecord.wakeup_time = CST_QUANTUM; + csRecord.next = sleeping_list; + sleeping_list = &csRecord; + miosix::ContextSwitchTimer::instance().IRQsetNextInterrupt(CST_QUANTUM); + #endif //USE_CSTIMER miosix_private::IRQportableStartKernel(); kernel_started=true; miosix_private::IRQportableFinishKernelStartup(); @@ -279,10 +293,34 @@ void IRQaddToSleepingList(SleepData *x) * It is used by the kernel, and should not be used by end users. * \return true if some thread was woken. */ -bool IRQwakeThreads() +bool IRQwakeThreads(long long currentTick, unsigned int burst) { #ifndef USE_CSTIMER tick++;//Increment tick + #else //USE_CSTIMER + //Add next context switch time to the sleeping list iff this is a + //context switch + if(currentTick >= 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 += burst; + IRQaddToSleepingList(&csRecord); //It would also set the next timer interrupt + } #endif //USE_CSTIMER bool result=false; for(;;) diff --git a/miosix/kernel/kernel.h b/miosix/kernel/kernel.h index a3dabf55f6d7d1b4145f29a9ab48a18e519c2bd4..50c8d951cde98f90da3480f891c44b313f75c8b2 100644 --- a/miosix/kernel/kernel.h +++ b/miosix/kernel/kernel.h @@ -1010,7 +1010,7 @@ private: //Need access to status friend void IRQaddToSleepingList(SleepData *x); //Needs access to status - friend bool IRQwakeThreads(); + friend bool IRQwakeThreads(long long currentTick, unsigned int burst); //Needs access to watermark, status, next friend void *idleThread(void *argv); //Needs to create the idle thread diff --git a/miosix/kernel/scheduler/priority/priority_scheduler.cpp b/miosix/kernel/scheduler/priority/priority_scheduler.cpp index 6f58904e0999509934f648934700e028c5c3b133..3645335486f8b2fe47a534a8d50eac72eb5a197a 100644 --- a/miosix/kernel/scheduler/priority/priority_scheduler.cpp +++ b/miosix/kernel/scheduler/priority/priority_scheduler.cpp @@ -29,6 +29,8 @@ #include "kernel/error.h" #include "kernel/process.h" +static const unsigned int cstQuantum=84000; ///FIXME: remove + #ifdef SCHED_TYPE_PRIORITY namespace miosix { @@ -197,9 +199,9 @@ void PriorityScheduler::IRQsetIdleThread(Thread *idleThread) idle=idleThread; } -void PriorityScheduler::IRQfindNextThread() +unsigned int PriorityScheduler::IRQfindNextThread() { - if(kernel_running!=0) return;//If kernel is paused, do nothing + if(kernel_running!=0) return cstQuantum;//If kernel is paused, do nothing for(int i=PRIORITY_MAX-1;i>=0;i--) { if(thread_list[i]==NULL) continue; @@ -226,7 +228,7 @@ void PriorityScheduler::IRQfindNextThread() //Rotate to next thread so that next time the list is walked //a different thread, if available, will be chosen first thread_list[i]=temp; - return; + return cstQuantum; } else temp=temp->schedData.next; if(temp==thread_list[i]->schedData.next) break; } @@ -237,6 +239,7 @@ void PriorityScheduler::IRQfindNextThread() #ifdef WITH_PROCESSES MPUConfiguration::IRQdisable(); #endif //WITH_PROCESSES + return cstQuantum; } Thread *PriorityScheduler::thread_list[PRIORITY_MAX]={0}; diff --git a/miosix/kernel/scheduler/priority/priority_scheduler.h b/miosix/kernel/scheduler/priority/priority_scheduler.h index 510042980fcd4eea989626c3929f6a773d98bd0e..bc7d335fe5fd9cce3be47391be2d72f11fd7eb61 100644 --- a/miosix/kernel/scheduler/priority/priority_scheduler.h +++ b/miosix/kernel/scheduler/priority/priority_scheduler.h @@ -136,8 +136,10 @@ public: * If the kernel is paused does nothing. * It's behaviour is to modify the global variable miosix::cur which always * points to the currently running thread. + * \return the burst or time quantum. That is, the time till the next + * preemption */ - static void IRQfindNextThread(); + static unsigned int IRQfindNextThread(); private: diff --git a/miosix/kernel/scheduler/scheduler.h b/miosix/kernel/scheduler/scheduler.h index 08e1f1620a5b94a3ff6e92ec762b2830f5c1f451..566c98cc0f374f4d12fe673fa00e6b4aeef71058 100644 --- a/miosix/kernel/scheduler/scheduler.h +++ b/miosix/kernel/scheduler/scheduler.h @@ -163,10 +163,12 @@ public: * If the kernel is paused does nothing. * It's behaviour is to modify the global variable miosix::cur which always * points to the currently running thread. + * \return the burst or time quantum. That is, the time till the next + * preemption */ - static void IRQfindNextThread() + static unsigned int IRQfindNextThread() { - T::IRQfindNextThread(); + return T::IRQfindNextThread(); } }; diff --git a/miosix/kernel/scheduler/timer_interrupt.h b/miosix/kernel/scheduler/timer_interrupt.h index bd0d94ce84b89a4b1106a6dfafa197688c0f5930..f7d05a7415b9fda36adfb1c4fcf63ef593825dc7 100644 --- a/miosix/kernel/scheduler/timer_interrupt.h +++ b/miosix/kernel/scheduler/timer_interrupt.h @@ -40,38 +40,53 @@ namespace miosix { extern volatile int kernel_running;///\internal Do not use outside the kernel extern volatile bool tick_skew;///\internal Do not use outside the kernel extern volatile Thread *cur;///\internal Do not use outside the kernel -extern bool IRQwakeThreads();///\internal Do not use outside the kernel +extern bool IRQwakeThreads(long long currentTick, unsigned int burst);///\internal Do not use outside the kernel -inline void IRQtimerInterrupt() +inline void IRQtimerInterrupt(long long currentTick) { miosix_private::IRQstackOverflowCheck(); - bool woken=IRQwakeThreads();//Increment tick and wake threads,if any - (void)woken; //Avoid unused variable warning. - - #ifdef SCHED_TYPE_PRIORITY - //With the priority scheduler every tick causes a context switck - Scheduler::IRQfindNextThread();//If the kernel is running, preempt + + unsigned int burst=Scheduler::IRQfindNextThread();//If the kernel is running, preempt if(kernel_running!=0) tick_skew=true; - #elif defined(SCHED_TYPE_CONTROL_BASED) - //Normally, with the control based scheduler, preemptions do not happen - //here, but in the auxiliary timer interrupt to take into account variable - //bursts. But there is one exception: when a thread wakes up from sleep - //and the idle thread is running. - if(woken && cur==ControlScheduler::IRQgetIdleThread()) - { - Scheduler::IRQfindNextThread(); - if(kernel_running!=0) tick_skew=true; - } - #elif defined(SCHED_TYPE_EDF) - //With the EDF scheduler a preemption happens only if a thread with a closer - //deadline appears. So by deafult there is no need to call the scheduler; - //only if some threads were woken, they may have closer deadlines - if(woken) - { - Scheduler::IRQfindNextThread(); - if(kernel_running!=0) tick_skew=true; - } + IRQwakeThreads(currentTick,burst); + + + #ifndef SCHED_TYPE_PRIORITY + //TODO: the old tick scheduler called this function periodically, + //so the EDF scheduler would have to be called only if a thread was woken. + //Now we can have no periodic preemption, and so if we are called (and EDF + //is selected), at least one thread has woken up, so there's no need for woken + //Modify the code below accordingly + #error "FIXME" #endif + +// miosix_private::IRQstackOverflowCheck(); +// bool woken=IRQwakeThreads();//Increment tick and wake threads,if any +// (void)woken; //Avoid unused variable warning. +// #ifdef SCHED_TYPE_PRIORITY +// //With the priority scheduler every tick causes a context switck +// Scheduler::IRQfindNextThread();//If the kernel is running, preempt +// if(kernel_running!=0) tick_skew=true; +// #elif defined(SCHED_TYPE_CONTROL_BASED) +// //Normally, with the control based scheduler, preemptions do not happen +// //here, but in the auxiliary timer interrupt to take into account variable +// //bursts. But there is one exception: when a thread wakes up from sleep +// //and the idle thread is running. +// if(woken && cur==ControlScheduler::IRQgetIdleThread()) +// { +// Scheduler::IRQfindNextThread(); +// if(kernel_running!=0) tick_skew=true; +// } +// #elif defined(SCHED_TYPE_EDF) +// //With the EDF scheduler a preemption happens only if a thread with a closer +// //deadline appears. So by deafult there is no need to call the scheduler; +// //only if some threads were woken, they may have closer deadlines +// if(woken) +// { +// Scheduler::IRQfindNextThread(); +// if(kernel_running!=0) tick_skew=true; +// } +// #endif } }