diff --git a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp index dde3b92463f58a4caf48f9a01989125086e9ca4f..3f64b521ef6edc4d9fcb8abc4ccc4073b3794d84 100644 --- a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp +++ b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp @@ -4,111 +4,39 @@ #include "kernel/kernel.h" #include "kernel/logging.h" #include <cstdlib> + using namespace miosix; -namespace miosix{ + +namespace miosix { void IRQaddToSleepingList(SleepData *x); extern SleepData *sleeping_list; } +namespace miosix_private { +void ISR_preempt(); +} + 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 SleepData csRecord; -#define CST_QUANTOM 84000*4 -#define CST_ROLLOVER_INT (TIM2->SR & TIM_SR_UIF) && (TIM2->SR & TIM_SR_CC2IF) - -namespace miosix_private{ - void ISR_preempt(); -} -ContextSwitchTimer& ContextSwitchTimer::instance() { - static ContextSwitchTimer _instance; - return _instance; -} - -ContextSwitchTimer::ContextSwitchTimer() { - /* - * TIM2 Source Clock (from APB1) Enable - */ - { - InterruptDisableLock idl; - RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; - RCC_SYNC(); - DBGMCU->APB1FZ|=DBGMCU_APB1_FZ_DBG_TIM2_STOP; //Tim2 stops while debugging - } - /* - * Setup TIM2 base configuration - * Mode: Up-counter - * Interrupts: counter overflow, Compare/Capture on channel 1 - */ - TIM2->CR1=TIM_CR1_URS; - TIM2->DIER=TIM_DIER_UIE | TIM_DIER_CC1IE; - NVIC_SetPriority(TIM2_IRQn,3); - NVIC_EnableIRQ(TIM2_IRQn); - /* - * Configure channel 1 as: - * Output channel (CC1S=0) - * No preload(OC1PE=0), hence TIM2_CCR1 can be written at anytime - * No effect on the output signal on match (OC1M = 0) - */ - TIM2->CCMR1 = 0; - TIM2->CCR1 = 0; - /* - * TIM2 Operation Frequency Configuration: Max Freq. and longest period - */ - TIM2->PSC = 0; - TIM2->ARR = 0xFFFFFFFF; - //printf("PSCFreq: %lu PCS: %lu ARR: %lu CCR1: %lu SR: %lu CNT: %lu\n",pcsfreq,TIM2->PSC,TIM2->ARR,TIM2->CCR1,TIM2->SR,TIM2->CNT); - /* - * Other initializations - */ - // Set the first checkpoint interrupt - 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) - TIM2->CR1 |= TIM_CR1_CEN; -} - -long long ContextSwitchTimer::getCurrentTick() { - bool interrupts=areInterruptsEnabled(); - if(interrupts) disableInterrupts(); - //If overflow occurs while interrupts disabled - /* - * counter should be checked before rollover interrupt flag - * - */ - uint32_t counter = TIM2->CNT; - if (CST_ROLLOVER_INT && counter < 0x80000000){ - long long result=(cst_ms32time | counter) + 0x100000000ll; - if(interrupts) enableInterrupts(); - return result; - } - long long result=cst_ms32time | counter; - if(interrupts) enableInterrupts(); - return result; +static inline long long nextInterrupt() +{ + return cst_ms32chkp | TIM2->CCR1; } -void ContextSwitchTimer::setNextInterrupt(long long tick) { - cst_ms32chkp = tick & 0xFFFFFFFF00000000; - TIM2->CCR1 = (uint32_t)(tick & 0xFFFFFFFF); - if (getCurrentTick() > getNextInterrupt()) - { - NVIC_SetPendingIRQ(TIM2_IRQn); - lateIrq=true; - } -} +#define CST_ROLLOVER_INT (TIM2->SR & TIM_SR_UIF) && (TIM2->SR & TIM_SR_CC2IF) -long long ContextSwitchTimer::getNextInterrupt(){ - return cst_ms32chkp | TIM2->CCR1; +void __attribute__((naked)) TIM2_IRQHandler() +{ + saveContext(); + asm volatile ("bl _Z9cstirqhndv"); + restoreContext(); } -void __attribute__((used)) cstirqhnd(){ +void __attribute__((used)) cstirqhnd() +{ //IRQbootlog("TIM2-IRQ\r\n"); if (TIM2->SR & TIM_SR_CC1IF || lateIrq){ //Checkpoint met @@ -119,16 +47,11 @@ void __attribute__((used)) cstirqhnd(){ 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); //Add next context switch time to the sleeping list iff this is a //context switch - if (tick > csRecord.wakeup_time){ + if (tick >= csRecord.wakeup_time){ //Remove the cs item from the sleeping list manually if (sleeping_list==&csRecord) sleeping_list=sleeping_list->next; @@ -143,14 +66,14 @@ void __attribute__((used)) cstirqhnd(){ //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; + csRecord.wakeup_time += CST_QUANTUM; IRQaddToSleepingList(&csRecord); //It would also set the next timer interrupt } miosix_private::ISR_preempt(); } } - //Rollover (Update and CC2 that is always set on zero) + //Rollover //On the initial update SR = UIF (ONLY) if (TIM2->SR & TIM_SR_UIF){ TIM2->SR = ~TIM_SR_UIF; //w0 clear @@ -158,12 +81,116 @@ void __attribute__((used)) cstirqhnd(){ } } -void __attribute__((naked)) TIM2_IRQHandler(){ - saveContext(); - asm volatile ("bl _Z9cstirqhndv"); - restoreContext(); +// +// class ContextSwitchTimer +// + +namespace miosix { + +ContextSwitchTimer& ContextSwitchTimer::instance() +{ + static ContextSwitchTimer instance; + return instance; +} + +void ContextSwitchTimer::IRQsetNextInterrupt(long long tick) +{ + cst_ms32chkp = tick & 0xFFFFFFFF00000000; + TIM2->CCR1 = static_cast<unsigned int>(tick & 0xFFFFFFFF); + if(getCurrentTick() > nextInterrupt()) + { + NVIC_SetPendingIRQ(TIM2_IRQn); + lateIrq=true; + } +} + +long long ContextSwitchTimer::getNextInterrupt() const +{ + return nextInterrupt(); } -ContextSwitchTimer::~ContextSwitchTimer() { +long long ContextSwitchTimer::getCurrentTick() const +{ + bool interrupts=areInterruptsEnabled(); + if(interrupts) disableInterrupts(); + //If overflow occurs while interrupts disabled + /* + * counter should be checked before rollover interrupt flag + * + */ + uint32_t counter = TIM2->CNT; + if (CST_ROLLOVER_INT && counter < 0x80000000){ + long long result=(cst_ms32time | counter) + 0x100000000ll; + if(interrupts) enableInterrupts(); + return result; + } + long long result=cst_ms32time | counter; + if(interrupts) enableInterrupts(); + return result; +} + +ContextSwitchTimer::~ContextSwitchTimer() {} + +ContextSwitchTimer::ContextSwitchTimer() +{ + /* + * TIM2 Source Clock (from APB1) Enable + */ + { + InterruptDisableLock idl; + RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; + RCC_SYNC(); + DBGMCU->APB1FZ|=DBGMCU_APB1_FZ_DBG_TIM2_STOP; //Tim2 stops while debugging + } + /* + * Setup TIM2 base configuration + * Mode: Up-counter + * Interrupts: counter overflow, Compare/Capture on channel 1 + */ + TIM2->CR1=TIM_CR1_URS; + TIM2->DIER=TIM_DIER_UIE | TIM_DIER_CC1IE; + NVIC_SetPriority(TIM2_IRQn,3); //High priority for TIM2 (Max=0, min=15) + NVIC_EnableIRQ(TIM2_IRQn); + /* + * Configure channel 1 as: + * Output channel (CC1S=0) + * No preload(OC1PE=0), hence TIM2_CCR1 can be written at anytime + * No effect on the output signal on match (OC1M = 0) + */ + TIM2->CCMR1 = 0; + TIM2->CCR1 = 0; + /* + * TIM2 Operation Frequency Configuration: Max Freq. and longest period + */ + TIM2->PSC = 0; + TIM2->ARR = 0xFFFFFFFF; + + /* + * Other initializations + */ + // Set the first checkpoint interrupt + csRecord.p = 0; + csRecord.wakeup_time = CST_QUANTUM; + csRecord.next = sleeping_list; + sleeping_list = &csRecord; + //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) + TIM2->CR1 |= TIM_CR1_CEN; + + // The global variable SystemCoreClock from ARM's CMSIS allows to know + // the CPU frequency. + timerFreq=SystemCoreClock; + // The timer frequency may however be a submultiple of the CPU frequency, + // due to the bus at whch the periheral is connected being slower. The + // RCC->CFGR register tells us how slower the APB1 bus is running. + // This formula takes into account that if the APB1 clock is divided by a + // factor of two or greater, the timer is clocked at twice the bus + // interface. After this, the freq variable contains the frequency in Hz + // at which the timer prescaler is clocked. + if(RCC->CFGR & RCC_CFGR_PPRE1_2) timerFreq/=1<<((RCC->CFGR>>10) & 0x3); } + +} //namespace miosix diff --git a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp index 9f1884b856f0c7eb3d19da9295dbc2f9523cbc72..3b6155cdf105b2391e3f7ac7cdaba1825b53e6e5 100644 --- a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp +++ b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp @@ -313,7 +313,7 @@ void IRQportableStartKernel() SysTick->CTRL=SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_CLKSOURCE_Msk; #else //USE ContextSwitchTimer class (TIM2) - miosix::ContextSwitchTimer::instance(); + 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 3da5d860a272c26f8fce73af7865db3a4e74c579..0e4ded1800680763cb1847c797319edf5b95c7a5 100644 --- a/miosix/interfaces/cstimer.h +++ b/miosix/interfaces/cstimer.h @@ -1,21 +1,64 @@ #ifndef CSTIMER_H #define CSTIMER_H -namespace miosix{ - class ContextSwitchTimer{ - public: - static ContextSwitchTimer& instance(); - void setNextInterrupt(long long tick); //timer tick - long long getNextInterrupt(); - /** - * Could be call both when the interrupts are enabled/disabled! - * @return the current tick count of the timer - */ - long long getCurrentTick(); - virtual ~ContextSwitchTimer(); - private: - ContextSwitchTimer(); - }; -} -#endif /* TIMER_H */ +namespace miosix { +#define CST_QUANTUM 42000//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 + * set interrupts used both for thread wakeup from sleep, and for preemption. + */ +class ContextSwitchTimer +{ +public: + /** + * \return an instance of this class (singleton) + */ + static ContextSwitchTimer& instance(); + + /** + * Set the next interrupt. + * Can be called with interrupts disabled or within an interrupt. + * \param tick the time when the interrupt will be fired, in timer ticks + */ + void IRQsetNextInterrupt(long long tick); + + /** + * \return the time when the next interrupt will be fired. + * That is, the last value passed to setNextInterrupt(). + */ + long long getNextInterrupt() const; + + /** + * Could be call both when the interrupts are enabled/disabled! + * \return the current tick count of the timer + */ + long long getCurrentTick() const; + + /** + * \return the timer frequency in Hz + */ + unsigned int getTimerFrequency() const + { + return timerFreq; + } + + /** + * Destructor + */ + virtual ~ContextSwitchTimer(); + +private: + /** + * Constructor, private because it's a singleton + */ + ContextSwitchTimer(); + + unsigned int timerFreq; +}; + +} //namespace miosix + +#endif //TIMER_H diff --git a/miosix/kernel/kernel.cpp b/miosix/kernel/kernel.cpp index c897e2aad07375c73ccd4fa2741913d8d3f3243e..7df5ed20951e13224117509771e20d690985ce67 100644 --- a/miosix/kernel/kernel.cpp +++ b/miosix/kernel/kernel.cpp @@ -260,7 +260,7 @@ void IRQaddToSleepingList(SleepData *x) //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); + ContextSwitchTimer::instance().IRQsetNextInterrupt(sleeping_list->wakeup_time); } /** @@ -287,7 +287,7 @@ bool IRQwakeThreads() sleeping_list=sleeping_list->next;//Remove from list result=true; //update interrupt of context switch timer - ContextSwitchTimer::instance().setNextInterrupt(sleeping_list->wakeup_time); + ContextSwitchTimer::instance().IRQsetNextInterrupt(sleeping_list->wakeup_time); } return result; }