From 1d7dc0740761bba0f36bf441448f99791469e8d8 Mon Sep 17 00:00:00 2001
From: Terraneo Federico <fede.tft@miosix.org>
Date: Thu, 5 May 2016 15:11:58 +0200
Subject: [PATCH] Added comments to cstimer.h interface Renamed
 ContextSwitchTimer::setNextInterrupt() to IRQsetNextInterrupt(), as its
 implementation required interrupts disabled, and it was always called in
 interrupt context anyway. ContextSwitchTimer's constructor now no longer sets
 an interrupt to the next time quantum, it now has to be done manually with
 setNextInterrupt(). Started cleanup cstimer.cpp

---
 .../common/interfaces-impl/cstimer.cpp        | 233 ++++++++++--------
 .../common/interfaces-impl/portability.cpp    |   2 +-
 miosix/interfaces/cstimer.h                   |  77 ++++--
 miosix/kernel/kernel.cpp                      |   4 +-
 4 files changed, 193 insertions(+), 123 deletions(-)

diff --git a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp
index dde3b924..3f64b521 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 9f1884b8..3b6155cd 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 3da5d860..0e4ded18 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 c897e2aa..7df5ed20 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;
 }
-- 
GitLab