From 0c684419b5d31ba1e0a702a210e142dfd167e6b7 Mon Sep 17 00:00:00 2001
From: Sasan Golchin <ahmad.golchin@mail.polimi.it>
Date: Mon, 26 Sep 2016 16:04:43 +0200
Subject: [PATCH] Aperiodic CSTimer support added for cortexM3_efm32gg/wandstem
 board

---
 .../interfaces-impl/cstimer.cpp               | 193 ++++++++++++++++++
 miosix/config/Makefile.inc                    |   3 +-
 .../efm32gg332f1024_wandstem/board_settings.h |   2 +
 3 files changed, 197 insertions(+), 1 deletion(-)
 create mode 100644 miosix/arch/cortexM3_efm32gg/efm32gg332f1024_wandstem/interfaces-impl/cstimer.cpp

diff --git a/miosix/arch/cortexM3_efm32gg/efm32gg332f1024_wandstem/interfaces-impl/cstimer.cpp b/miosix/arch/cortexM3_efm32gg/efm32gg332f1024_wandstem/interfaces-impl/cstimer.cpp
new file mode 100644
index 00000000..7ad008ad
--- /dev/null
+++ b/miosix/arch/cortexM3_efm32gg/efm32gg332f1024_wandstem/interfaces-impl/cstimer.cpp
@@ -0,0 +1,193 @@
+#include "interfaces/cstimer.h"
+#include "interfaces/arch_registers.h"
+#include "kernel/kernel.h"
+#include "kernel/scheduler/timer_interrupt.h"
+#include "kernel/timeconversion.h"
+#include "../../../../../debugpin.h"
+#include "CMSIS/Include/core_cm3.h"
+#include "bsp_impl.h"
+
+using namespace miosix;
+
+const unsigned int timerBits=32;
+const unsigned long long overflowIncrement=(1LL<<timerBits);
+const unsigned long long lowerMask=overflowIncrement-1;
+const unsigned long long upperMask=0xFFFFFFFFFFFFFFFFLL-lowerMask;
+
+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 inline long long nextInterrupt()
+{
+    return ms32chkp | TIMER3->CC[1].CCV<<16 | TIMER1->CC[1].CCV;
+}
+
+static inline unsigned int IRQread32Timer(){
+    unsigned int high=TIMER3->CNT;
+    unsigned int low=TIMER1->CNT;
+    unsigned int high2=TIMER3->CNT;
+    if(high==high2){
+        return (high<<16)|low;
+    }
+    return high2<<16|TIMER1->CNT;
+}
+
+static inline long long IRQgetTick(){
+    //PENDING BIT TRICK
+    unsigned int counter=IRQread32Timer();
+    if((TIMER3->IF & _TIMER_IFC_OF_MASK) && IRQread32Timer()>=counter)
+        return (ms32time | static_cast<long long>(counter)) + overflowIncrement;
+    return ms32time | static_cast<long long>(counter);
+}
+
+void __attribute__((naked)) TIMER3_IRQHandler()
+{
+    saveContext();
+    asm volatile("bl _Z10cstirqhnd3v");
+    restoreContext();
+}
+
+void __attribute__((naked)) TIMER1_IRQHandler()
+{
+    saveContext();
+    asm volatile("bl _Z10cstirqhnd1v");
+    restoreContext();
+}
+void __attribute__((naked)) TIMER2_IRQHandler()
+{
+    saveContext();
+    asm volatile("bl _Z10cstirqhnd2v");
+    restoreContext();
+}
+
+void __attribute__((used)) cstirqhnd3(){
+    //Checkpoint
+    if (lateIrq){
+        IRQtimerInterrupt(tc->tick2ns(IRQgetTick()));
+        lateIrq = false;
+    }else if (TIMER3->IF & TIMER_IF_CC1){
+        TIMER3->IFC = TIMER_IFC_CC1;
+        if (ms32time == ms32chkp){
+            TIMER1->IFC = TIMER_IFC_CC1;
+            NVIC_ClearPendingIRQ(TIMER1_IRQn);
+            NVIC_EnableIRQ(TIMER1_IRQn);
+            if (TIMER1->CNT >= TIMER1->CC[1].CCV){
+                TIMER1->IFC = TIMER_IFC_CC1;
+                NVIC_DisableIRQ(TIMER1_IRQn);
+                IRQtimerInterrupt(tc->tick2ns(IRQgetTick()));
+            }
+        }
+    }   
+ 
+    //rollover
+    if (TIMER3->IF & TIMER_IF_OF){
+        TIMER3->IFC = TIMER_IFC_OF;
+        ms32time += overflowIncrement;
+    }
+}
+void __attribute__((used)) cstirqhnd1(){
+    //Checkpoint
+    if (TIMER1->IF & TIMER_IF_CC1){
+        TIMER1->IFC = TIMER_IFC_CC1;
+        NVIC_ClearPendingIRQ(TIMER1_IRQn);
+        NVIC_DisableIRQ(TIMER1_IRQn);
+        IRQtimerInterrupt(tc->tick2ns(IRQgetTick()));
+    }
+}
+
+void __attribute__((used)) cstirqhnd2(){
+
+}
+
+//
+// class ContextSwitchTimer
+//
+
+namespace miosix {
+    
+    ContextSwitchTimer& ContextSwitchTimer::instance()
+    {
+        static ContextSwitchTimer instance;
+        return instance;
+    }
+    
+    void ContextSwitchTimer::IRQsetNextInterrupt(long long tick){
+        ms32chkp = tick & upperMask;
+        TIMER3->CC[1].CCV = static_cast<unsigned int>((tick & 0xFFFF0000)>>16);
+        TIMER1->CC[1].CCV = static_cast<unsigned int>(tick & 0xFFFF);
+        /*
+        In order to prevent back to back interrupts due to a checkpoint
+        set in the past, raise lateIrq flag to indicate this IRQ should
+        be processed only once
+        */
+        if(IRQgetTick() >= nextInterrupt())
+        {
+            NVIC_SetPendingIRQ(TIMER3_IRQn);
+            lateIrq=true;
+        }
+    }
+    
+    long long ContextSwitchTimer::getNextInterrupt() const
+    {
+        return nextInterrupt();
+    }
+    
+    long long ContextSwitchTimer::getCurrentTick() const
+    {
+        bool interrupts=areInterruptsEnabled();
+        //TODO: optimization opportunity, if we can guarantee that no call to this
+        //function occurs before kernel is started, then we can use
+        //fastInterruptDisable())
+        if(interrupts) disableInterrupts();
+        long long result=IRQgetTick();
+        if(interrupts) enableInterrupts();
+        return result;
+    }
+    
+    long long ContextSwitchTimer::IRQgetCurrentTick() const
+    {
+        return IRQgetTick();
+    }
+    
+    ContextSwitchTimer::~ContextSwitchTimer(){}
+    
+    ContextSwitchTimer::ContextSwitchTimer()
+    {
+        //Power the timers up
+        {
+            InterruptDisableLock l;
+            CMU->HFPERCLKEN0 |= CMU_HFPERCLKEN0_TIMER1 | CMU_HFPERCLKEN0_TIMER2
+                    | CMU_HFPERCLKEN0_TIMER3;
+        }
+        //Configure Timers
+        TIMER1->CTRL = TIMER_CTRL_MODE_UP | TIMER_CTRL_CLKSEL_PRESCHFPERCLK 
+                | TIMER_CTRL_PRESC_DIV1 | TIMER_CTRL_SYNC;
+        TIMER2->CTRL = TIMER_CTRL_MODE_UP | TIMER_CTRL_CLKSEL_PRESCHFPERCLK 
+                | TIMER_CTRL_PRESC_DIV1 | TIMER_CTRL_SYNC;
+        TIMER3->CTRL = TIMER_CTRL_MODE_UP | TIMER_CTRL_CLKSEL_TIMEROUF 
+                | TIMER_CTRL_SYNC;
+        
+        //Enable necessary interrupt lines
+        TIMER1->IEN = TIMER_IEN_CC1;
+        TIMER3->IEN = TIMER_IEN_OF | TIMER_IEN_CC1;
+
+        TIMER1->CC[1].CTRL = TIMER_CC_CTRL_MODE_OUTPUTCOMPARE;
+        TIMER3->CC[1].CTRL = TIMER_CC_CTRL_MODE_OUTPUTCOMPARE;
+
+        NVIC_SetPriority(TIMER1_IRQn,3);
+        NVIC_SetPriority(TIMER3_IRQn,3);
+        NVIC_ClearPendingIRQ(TIMER3_IRQn);
+        NVIC_ClearPendingIRQ(TIMER1_IRQn);
+        NVIC_EnableIRQ(TIMER3_IRQn);
+        NVIC_DisableIRQ(TIMER1_IRQn);
+        //Start timers
+        TIMER1->CMD = TIMER_CMD_START;
+        //Setup tick2ns conversion tool
+        timerFreq = 48000000;
+        tc=new TimeConversion(timerFreq);
+    }
+
+TimeConversion *tc;
+
+}
\ No newline at end of file
diff --git a/miosix/config/Makefile.inc b/miosix/config/Makefile.inc
index f031af98..8a96f785 100644
--- a/miosix/config/Makefile.inc
+++ b/miosix/config/Makefile.inc
@@ -2111,7 +2111,8 @@ else ifeq ($(ARCH),cortexM3_efm32gg)
         $(BOARD_INC)/interfaces-impl/power_manager.cpp  \
         $(BOARD_INC)/interfaces-impl/gpioirq.cpp        \
         $(BOARD_INC)/interfaces-impl/hardware_timer.cpp \
-        $(BOARD_INC)/interfaces-impl/transceiver.cpp
+        $(BOARD_INC)/interfaces-impl/transceiver.cpp    \
+        $(BOARD_INC)/interfaces-impl/cstimer.cpp
 
         ## Add a #define to allow querying board name
         CFLAGS_BASE   += -DEFM32GG332F1024 -D_BOARD_WANDSTEM
diff --git a/miosix/config/arch/cortexM3_efm32gg/efm32gg332f1024_wandstem/board_settings.h b/miosix/config/arch/cortexM3_efm32gg/efm32gg332f1024_wandstem/board_settings.h
index 44ff1253..b40896c9 100644
--- a/miosix/config/arch/cortexM3_efm32gg/efm32gg332f1024_wandstem/board_settings.h
+++ b/miosix/config/arch/cortexM3_efm32gg/efm32gg332f1024_wandstem/board_settings.h
@@ -43,6 +43,8 @@
  */
 #define WANDSTEM_HW_REV 14
 
+#define USE_CSTIMER //FIXME: remove when cstimer becomes default
+
 namespace miosix {
 
 /**
-- 
GitLab