Skip to content
Snippets Groups Projects
Commit c01b066d authored by Sasan Golchin's avatar Sasan Golchin Committed by Federico
Browse files

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: default avatarSasan Golchin <ahmad.golchin@mail.polimi.it>
Signed-off-by: default avatarTerraneo Federico <fede.tft@miosix.org>
parent 2a0bfc50
Branches
No related tags found
2 merge requests!40Update to Miosix 2.7,!17Draft: Improved miosix build system and fixed cmake scripts
...@@ -9,14 +9,14 @@ using namespace miosix; ...@@ -9,14 +9,14 @@ using namespace miosix;
static void t1Task(void* p){ static void t1Task(void* p){
while (true){ while (true){
IRQbootlog("1\r\n"); IRQbootlog("1\r\n");
Thread::sleep(1000); Thread::sleep(500);
} }
} }
int main(){ int main(){
//ContextSwitchTimer::instance(); //ContextSwitchTimer::instance();
Thread::setPriority(1); 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); Thread *p=Thread::create(t1Task,512,1,NULL);
while (true){ while (true){
IRQbootlog("0\r\n"); IRQbootlog("0\r\n");
......
#include "miosix.h" #include "miosix.h"
#include "interfaces/cstimer.h" #include "interfaces/cstimer.h"
#include "interfaces/portability.h" #include "interfaces/portability.h"
#include "kernel/kernel.h"
#include "kernel/logging.h" #include "kernel/logging.h"
#include <cstdlib> #include <cstdlib>
using namespace miosix; 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_ms32time = 0; //most significant 32 bits of counter
static long long cst_ms32chkp = 0; //most significant 32 bits of check point static long long cst_ms32chkp = 0; //most significant 32 bits of check point
static bool lateIrq=false; 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) #define CST_ROLLOVER_INT (TIM2->SR & TIM_SR_UIF) && (TIM2->SR & TIM_SR_CC2IF)
namespace miosix_private{ namespace miosix_private{
...@@ -58,8 +63,12 @@ ContextSwitchTimer::ContextSwitchTimer() { ...@@ -58,8 +63,12 @@ ContextSwitchTimer::ContextSwitchTimer() {
* Other initializations * Other initializations
*/ */
// Set the first checkpoint interrupt // Set the first checkpoint interrupt
cst_prevChk = CST_QUANTOM; csRecord.p = 0;
setNextInterrupt(cst_prevChk); csRecord.wakeup_time = CST_QUANTOM;
csRecord.next = sleeping_list;
sleeping_list = &csRecord;
setNextInterrupt(CST_QUANTOM);
//IRQaddToSleepingList(&csRecord); //Recursive Initialization error
// Enable TIM2 Counter // Enable TIM2 Counter
cst_ms32time = 0; cst_ms32time = 0;
TIM2->EGR = TIM_EGR_UG; //To enforce the timer to apply PSC (and other non-immediate settings) 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(){ ...@@ -109,12 +118,34 @@ void __attribute__((used)) cstirqhnd(){
TIM2->SR = ~TIM_SR_CC1IF; TIM2->SR = ~TIM_SR_CC1IF;
if(cst_ms32time==cst_ms32chkp || lateIrq){ if(cst_ms32time==cst_ms32chkp || lateIrq){
lateIrq=false; lateIrq=false;
long long tick = ContextSwitchTimer::instance().getCurrentTick();
//Set next checkpoint interrupt //Set next checkpoint interrupt
////cst_prevChk += CST_QUANTOM; ////cst_prevChk += CST_QUANTOM;
////ContextSwitchTimer::instance().setNextInterrupt(cst_prevChk); ////ContextSwitchTimer::instance().setNextInterrupt(cst_prevChk);
ContextSwitchTimer::instance().setNextInterrupt( //ContextSwitchTimer::instance().setNextInterrupt(
ContextSwitchTimer::instance().getCurrentTick() + CST_QUANTOM); // ContextSwitchTimer::instance().getCurrentTick() + CST_QUANTOM);
//Call preempt routine
//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(); miosix_private::ISR_preempt();
} }
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "process.h" #include "process.h"
#include "kernel/scheduler/scheduler.h" #include "kernel/scheduler/scheduler.h"
#include "stdlib_integration/libc_integration.h" #include "stdlib_integration/libc_integration.h"
#include "interfaces/cstimer.h"
#include <stdexcept> #include <stdexcept>
#include <algorithm> #include <algorithm>
#include <string.h> #include <string.h>
...@@ -62,7 +63,7 @@ volatile Thread *cur=NULL;///<\internal Thread currently running ...@@ -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 ///\internal True if there are threads in the DELETED status. Used by idle thread
static volatile bool exist_deleted=false; 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 static volatile long long tick=0;///<\internal Kernel tick
...@@ -256,6 +257,10 @@ void IRQaddToSleepingList(SleepData *x) ...@@ -256,6 +257,10 @@ void IRQaddToSleepingList(SleepData *x)
cur=cur->next; 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() ...@@ -275,10 +280,14 @@ bool IRQwakeThreads()
if(sleeping_list==NULL) break;//If no item in list, return 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 //Since list is sorted, if we don't need to wake the first element
//we don't need to wake the other too //we don't need to wake the other too
if(tick != sleeping_list->wakeup_time) break; //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->p->flags.IRQsetSleep(false);//Wake thread
sleeping_list=sleeping_list->next;//Remove from list sleeping_list=sleeping_list->next;//Remove from list
result=true; result=true;
//update interrupt of context switch timer
ContextSwitchTimer::instance().setNextInterrupt(sleeping_list->wakeup_time);
} }
return result; return result;
} }
...@@ -343,9 +352,12 @@ bool Thread::testTerminate() ...@@ -343,9 +352,12 @@ bool Thread::testTerminate()
return const_cast<Thread*>(cur)->flags.isDeleting(); return const_cast<Thread*>(cur)->flags.isDeleting();
} }
void Thread::sleep(unsigned int ms) inline void Thread::tickSleepUntil(long long absTicks){
{ //absTicks: As it is in terms of real ticks of the kernel/timer, there's no
if(ms==0) return; //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 //The SleepData variable has to be in scope till Thread::yield() returns
//as IRQaddToSleepingList() makes it part of a linked list till the //as IRQaddToSleepingList() makes it part of a linked list till the
//thread wakes up (i.e: after Thread::yield() returns) //thread wakes up (i.e: after Thread::yield() returns)
...@@ -355,30 +367,33 @@ void Thread::sleep(unsigned int ms) ...@@ -355,30 +367,33 @@ void Thread::sleep(unsigned int ms)
{ {
FastInterruptDisableLock lock; FastInterruptDisableLock lock;
d.p=const_cast<Thread*>(cur); d.p=const_cast<Thread*>(cur);
if(((ms*TICK_FREQ)/1000)>0) d.wakeup_time=getTick()+(ms*TICK_FREQ)/1000; d.wakeup_time = absTicks;
//If tick resolution is too low, wait one tick
else d.wakeup_time=getTick()+1;
IRQaddToSleepingList(&d);//Also sets SLEEP_FLAG IRQaddToSleepingList(&d);//Also sets SLEEP_FLAG
} }
Thread::yield(); Thread::yield();
} }
void Thread::sleepUntil(long long absoluteTime) void Thread::nanoSleep(unsigned int ns){
{ if(ns==0) return; //To-Do: should be (ns &lt; resolution + epsilon)
//The SleepData variable has to be in scope till Thread::yield() returns long long ticks = ns * 0.084;//To-do: ns2tick fast conversion needed
//as IRQaddToSleepingList() makes it part of a linked list till the tickSleepUntil(ContextSwitchTimer::instance().getCurrentTick() + ticks);
//thread wakes up (i.e: after Thread::yield() returns) }
SleepData d;
//pauseKernel() here is not enough since even if the kernel is stopped void Thread::nanoSleepUntil(long long absoluteTime){
//the tick isr will wake threads, modifying the sleeping_list //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)
{ {
FastInterruptDisableLock lock; nanoSleep(ms * 1000000);
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();
void Thread::sleepUntil(long long absoluteTime)
{
nanoSleepUntil(absoluteTime * 1000000);
} }
Thread *Thread::getCurrentThread() Thread *Thread::getCurrentThread()
......
...@@ -497,7 +497,7 @@ public: ...@@ -497,7 +497,7 @@ public:
* CANNOT be called when the kernel is paused. * CANNOT be called when the kernel is paused.
*/ */
static void sleep(unsigned int ms); static void sleep(unsigned int ms);
static void nanoSleep(unsigned int ns);
/** /**
* Put the thread to sleep until the specified absolute time is reached. * Put the thread to sleep until the specified absolute time is reached.
* If the time is in the past, returns immediately. * If the time is in the past, returns immediately.
...@@ -521,7 +521,7 @@ public: ...@@ -521,7 +521,7 @@ public:
* CANNOT be called when the kernel is paused. * CANNOT be called when the kernel is paused.
*/ */
static void sleepUntil(long long absoluteTime); 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 Thread class of the current thread.
* \return a pointer to the current thread. * \return a pointer to the current thread.
...@@ -718,6 +718,18 @@ private: ...@@ -718,6 +718,18 @@ private:
//Unwanted methods //Unwanted methods
Thread(const Thread& p);///< No public copy constructor Thread(const Thread& p);///< No public copy constructor
Thread& operator = (const Thread& p);///< No publc operator = 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 class ThreadFlags
{ {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment