From f5fcbad323f3cb21647662d41f2eaf0e0f1c24fe Mon Sep 17 00:00:00 2001
From: Sasan Golchin <ahmad.golchin@mail.polimi.it>
Date: Tue, 15 Dec 2015 08:48:36 +0100
Subject: [PATCH] Context Switch Timer Interface

A generic interface miosix::ContextSwitchTimer has been added to the
kernel that can play the role of either periodic/aperiodic system timer.
Like other kernel interfaces, there should be provided a proper
implementation for cstimer.

Currently the only available implementation is done for cortexM4_stm32f
which is based on TIM2 timer. Although the implementation is an
aperiodic timer, it is set to act like a periodic timer with T=1ms.

Signed-off-by: Sasan Golchin <ahmad.golchin@mail.polimi.it>
Signed-off-by: Terraneo Federico <fede.tft@miosix.org>
---
 Makefile                                      |  2 +-
 main.cpp                                      | 47 ++++--------
 .../common/interfaces-impl/cstimer.cpp        | 71 +++++++++++--------
 .../common/interfaces-impl/portability.cpp    |  8 +++
 miosix/config/Makefile.inc                    |  1 +
 cstimer.h => miosix/interfaces/cstimer.h      |  2 +-
 6 files changed, 69 insertions(+), 62 deletions(-)
 rename cstimer.cpp => miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp (63%)
 rename cstimer.h => miosix/interfaces/cstimer.h (89%)

diff --git a/Makefile b/Makefile
index 69ace418..1f279089 100644
--- a/Makefile
+++ b/Makefile
@@ -19,7 +19,7 @@ SUBDIRS := $(KPATH)
 ## List here your source files (both .s, .c and .cpp)
 ##
 SRC :=                                  \
-main.cpp cstimer.cpp
+main.cpp
 
 ##
 ## List here additional static libraries with relative path
diff --git a/main.cpp b/main.cpp
index 5e20d2f8..69d1f944 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,42 +1,25 @@
-#include <cstdio>
-#include <cmath>
+#include <stdio.h>
+#include <pthread.h>
 #include "miosix.h"
 #include "kernel/logging.h"
-#include "e20/e20.h"
-#include "cstimer.h"
+#include "interfaces/cstimer.h"
 using namespace std;
 using namespace miosix;
 
-void basicTests(){
-    ContextSwitchTimer t = ContextSwitchTimer::instance();
-    long long chkpoint = 0xFFFFFFFFll+1899760;
-    //chkpoint = 0xFFFFFFFFll-20; //Test Case: CHKPNT right really before rollover -> OK
-    //chkpoint = 0xFFFFFFFFll-2000; //Test Case: CHKPNT right before rollover -> OK
-    //chkpoint = 0xFFFFFFFFll+2000; //Test Case: CHKPNT right afrer rollover -> OK
-    //chkpoint = 0xFFFFFFFFll+20; //Test Case: CHKPNT right really afrer rollover -> OK
-    t.setNextInterrupt(chkpoint); //checkpoint near the rollover
-    while(1){
-        Thread::sleep(10000);
-        printf("-CNT: %llu , NEXT CHKPNT: %llu\n",t.getCurrentTick(),t.getNextInterrupt());
-
-    }
-}
-void rightAfterCurrentTime(){
-    ContextSwitchTimer t = ContextSwitchTimer::instance();
-
-    while(1){
-        Thread::sleep(10000);
-        //checkpoint near the current tick
-        //A chkpoint with dist. less than 80 is not set in time => lost!
-        t.setNextInterrupt(t.getCurrentTick()+80);
-        printf("-CNT: %llu , NEXT CHKPNT: %llu\n",t.getCurrentTick(),t.getNextInterrupt());
-
+static void t1Task(void* p){
+    while (true){
+        IRQbootlog("1\r\n");
+        Thread::sleep(1000);
     }
 }
 
 int main(){
-    iprintf("Context Switch Timer Driver Test1 (80)\n");
-
-    //basicTests(); //passed
-    rightAfterCurrentTime();
+    //ContextSwitchTimer::instance();
+    Thread::setPriority(1);
+    printf("Context Switch Timer ....T=1ms\n");
+    Thread *p=Thread::create(t1Task,512,1,NULL);
+    while (true){
+        IRQbootlog("0\r\n");
+        Thread::sleep(250);
+    }
 }
diff --git a/cstimer.cpp b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp
similarity index 63%
rename from cstimer.cpp
rename to miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp
index 600f84f6..e2c2f80d 100644
--- a/cstimer.cpp
+++ b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/cstimer.cpp
@@ -1,5 +1,6 @@
 #include "miosix.h"
-#include "cstimer.h"
+#include "interfaces/cstimer.h"
+#include "interfaces/portability.h"
 #include "kernel/logging.h"
 #include <cstdlib>
 using namespace miosix;
@@ -7,7 +8,14 @@ using namespace miosix;
 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 volatile bool cst_chkProcessed;
+static long long cst_prevChk;
+
+#define CST_QUANTOM 84000
 #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;
@@ -18,7 +26,7 @@ ContextSwitchTimer::ContextSwitchTimer() {
      *  TIM2 Source Clock (from APB1) Enable
      */
     {
-        FastInterruptDisableLock idl;
+        InterruptDisableLock idl;
         RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
         RCC_SYNC();
     }
@@ -33,10 +41,10 @@ ContextSwitchTimer::ContextSwitchTimer() {
     TIM2->CCR3=0;
     TIM2->CCR4=0;
     TIM2->DIER=TIM_DIER_UIE | TIM_DIER_CC1IE;
-    NVIC_SetPriority(TIM2_IRQn,1); //TO-DO: WHICH IRQ Priority to use? 0?
+    NVIC_SetPriority(TIM2_IRQn,3);
     NVIC_EnableIRQ(TIM2_IRQn);
     /*
-     * Configure channel 1 as: 
+     * 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)
@@ -44,24 +52,21 @@ ContextSwitchTimer::ContextSwitchTimer() {
     TIM2->CCMR1 = 0;
     TIM2->CCR1 = 0;
     /*
-     * TIM2 Operation Frequency Configuration
+     * TIM2 Operation Frequency Configuration: Max Freq. and longest period
      */
-    uint32_t pcsfreq = SystemCoreClock;
-    uint32_t apb1_div = (RCC->CFGR & RCC_CFGR_PPRE1)>>10;
-    //True assumption: AHB frequency is not divided :D
-    if (apb1_div & 0x4) //APB1 Divider is active
-        pcsfreq /= ((apb1_div & 0x3)+1)<<1;
-    
     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
+    cst_prevChk = CST_QUANTOM;
+    setNextInterrupt(cst_prevChk);
     // 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;
-    cst_ms32time = 0;
 }
 
 long long ContextSwitchTimer::getCurrentTick() {
@@ -70,7 +75,7 @@ long long ContextSwitchTimer::getCurrentTick() {
     //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){
@@ -87,6 +92,7 @@ void ContextSwitchTimer::setNextInterrupt(long long tick) {
     cst_ms32chkp = tick & 0xFFFFFFFF00000000;
     TIM2->CCR1 = (uint32_t)(tick & 0xFFFFFFFF);
     cst_chkProcessed = false;
+    //if (getCurrentTick() > getNextInterrupt()) IRQbootlog("TIM2 - CHECK POINT SET IN THE PAST\r\n");
 }
 
 long long ContextSwitchTimer::getNextInterrupt(){
@@ -94,26 +100,35 @@ long long ContextSwitchTimer::getNextInterrupt(){
 }
 
 void __attribute__((used)) cstirqhnd(){
-    if ((TIM2->SR & TIM_SR_CC1IF) && (cst_ms32time==cst_ms32chkp)){
+    //IRQbootlog("TIM2-IRQ\r\n");
+    if (TIM2->SR & TIM_SR_CC1IF){
         //Checkpoint met
+        //The interrupt flag must be cleared unconditionally whether we are in the
+        //correct epoch or not otherwise the interrupt will happen even in unrelated
+        //epochs and slowing down the whole system.
         TIM2->SR = ~TIM_SR_CC1IF;
-        IRQbootlog(" (CHK POINT) \r\n");
-        cst_chkProcessed = true;
-        
+        if(cst_ms32time==cst_ms32chkp){
+            //Set next checkpoint interrupt
+            ////cst_prevChk += CST_QUANTOM;
+            ////ContextSwitchTimer::instance().setNextInterrupt(cst_prevChk);
+            ContextSwitchTimer::instance().setNextInterrupt(
+                ContextSwitchTimer::instance().getCurrentTick() + CST_QUANTOM);
+            cst_chkProcessed = true;
+            //Call preempt routine
+            miosix_private::ISR_preempt();
+        }
+
     }
-    if (CST_ROLLOVER_INT){
-        //Rollover (Update and CC2 that is always set on zero)
-        //On the initial update SR = UIF (ONLY)
+    //Rollover (Update and CC2 that is always set on zero)
+    //On the initial update SR = UIF (ONLY)
+    if (TIM2->SR & TIM_SR_UIF){
         TIM2->SR = ~TIM_SR_UIF; //w0 clear
-        IRQbootlog(" (ROLLOVER) \r\n");
-        cst_ms32time += 0x100000000;
-        
+        if (TIM2->SR & TIM_SR_CC2IF){
+            TIM2->SR = ~TIM_SR_CC2IF;
+            //IRQbootlog(" (ROLLOVER) \r\n");
+            cst_ms32time += 0x100000000;
+        }
     }
-    /*
-    if (!cst_chkProcessed && cst_ms32chkp < (cst_ms32time | TIM2->CNT) && cst_ms32time==cst_ms32chkp){
-        IRQbootlog(" (CHK POINT) \r\n");
-        cst_chkProcessed = true;
-    }*/ 
 }
 
 void __attribute__((naked)) TIM2_IRQHandler(){
diff --git a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp
index 1f4cc55f..9f1884b8 100644
--- a/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp
+++ b/miosix/arch/cortexM4_stm32f4/common/interfaces-impl/portability.cpp
@@ -38,6 +38,8 @@
 #include <cstdio>
 #include <cstring>
 #include <cassert>
+#include "interfaces/cstimer.h"
+//#define CNTX_SWITCH_USE_SYSTICK
 
 /**
  * \internal
@@ -49,10 +51,12 @@
 void SysTick_Handler()   __attribute__((naked));
 void SysTick_Handler()
 {
+#ifdef CNTX_SWITCH_USE_SYSTICK
     saveContext();
     //Call ISR_preempt(). Name is a C++ mangled name.
     asm volatile("bl _ZN14miosix_private11ISR_preemptEv");
     restoreContext();
+#endif
 }
 
 /**
@@ -304,9 +308,13 @@ void IRQportableStartKernel()
     NVIC_SetPriority(SVCall_IRQn,3);//High priority for SVC (Max=0, min=15)
     NVIC_SetPriority(SysTick_IRQn,3);//High priority for SysTick (Max=0, min=15)
     NVIC_SetPriority(MemoryManagement_IRQn,2);//Higher priority for MemoryManagement (Max=0, min=15)
+#ifdef CNTX_SWITCH_USE_SYSTICK
     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();
+#endif
     #ifdef WITH_PROCESSES
     miosix::IRQenableMPUatBoot();
     #endif //WITH_PROCESSES
diff --git a/miosix/config/Makefile.inc b/miosix/config/Makefile.inc
index 62737f90..39452470 100644
--- a/miosix/config/Makefile.inc
+++ b/miosix/config/Makefile.inc
@@ -1737,6 +1737,7 @@ else ifeq ($(ARCH),cortexM4_stm32f4)
     $(ARCH_INC)/interfaces-impl/portability.cpp              \
     $(ARCH_INC)/interfaces-impl/delays.cpp                   \
     $(ARCH_INC)/interfaces-impl/gpio_impl.cpp                \
+    $(ARCH_INC)/interfaces-impl/cstimer.cpp		     \
     arch/common/drivers/sd_stm32f2_f4.cpp                    \
     arch/common/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c
 
diff --git a/cstimer.h b/miosix/interfaces/cstimer.h
similarity index 89%
rename from cstimer.h
rename to miosix/interfaces/cstimer.h
index 8b9491cb..3da5d860 100644
--- a/cstimer.h
+++ b/miosix/interfaces/cstimer.h
@@ -9,7 +9,7 @@ namespace miosix{
         long long getNextInterrupt();
         /**
          * Could be call both when the interrupts are enabled/disabled!
-         * @return 
+         * @return the current tick count of the timer
          */
         long long getCurrentTick();
         virtual ~ContextSwitchTimer();
-- 
GitLab