Import drv/timer module.
authorbernie <bernie@38d2e660-2303-0410-9eaa-f027e97ec537>
Sun, 23 May 2004 18:23:30 +0000 (18:23 +0000)
committerbernie <bernie@38d2e660-2303-0410-9eaa-f027e97ec537>
Sun, 23 May 2004 18:23:30 +0000 (18:23 +0000)
git-svn-id: https://src.develer.com/svnoss/bertos/trunk@6 38d2e660-2303-0410-9eaa-f027e97ec537

drv/timer.c [new file with mode: 0755]
drv/timer.h [new file with mode: 0755]
drv/timer_avr.h [new file with mode: 0755]
drv/timer_dsp56k.h [new file with mode: 0755]
drv/timer_i196.h [new file with mode: 0755]

diff --git a/drv/timer.c b/drv/timer.c
new file mode 100755 (executable)
index 0000000..a01f6f3
--- /dev/null
@@ -0,0 +1,254 @@
+/*!
+ * \file
+ * <!--
+ * Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 2000 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ *
+ * \brief Hardware independent timer driver (implementation)
+ */
+
+/*
+ * $Log$
+ * Revision 1.1  2004/05/23 18:23:30  bernie
+ * Import drv/timer module.
+ *
+ */
+
+#include "hw.h"
+#include "kdebug.h"
+#include "timer.h"
+
+#ifdef CONFIG_KERN_SIGNALS
+#include <kern/proc.h>
+#endif
+
+#if (ARCH & ARCH_EMUL)
+
+# error To be recoded
+
+#elif defined(__AVR__)
+#      include "timer_avr.h"
+#elif defined(__IAR_SYSTEMS_ICC) || defined(__IAR_SYSTEMS_ICC__) /* 80C196 */
+#      include "timer_i196.h"
+#elif defined (__m56800__)
+#      include "timer_dsp56k.h"
+#else
+#      error Unknown system
+#endif
+
+
+/*! Number of available timers */
+#define MAX_TIMERS 4
+
+
+//! Master system clock (1ms accuracy)
+volatile time_t _clock;
+
+static Timer soft_timers[MAX_TIMERS];   /*!< Pool of Timer structures */
+static List timers_pool;                /*!< Pool of free timers */
+REGISTER static List timers_queue;      /*!< Active timers */
+
+
+/*!
+ * Return a new timer picking and removing it from the available
+ * timers pool. Return NULL if no more timers are available.
+ */
+Timer *timer_new(void)
+{
+       Timer *timer;
+       cpuflags_t flags;
+
+       DISABLE_IRQSAVE(flags);
+
+       /* Should never happen */
+       if (ISLISTEMPTY(&timers_pool))
+       {
+               ENABLE_IRQRESTORE(flags);
+               DB(kprintf("Tmrspool empty\n");)
+               return NULL;
+       }
+
+       /* Get a timer from the free pool */
+       timer = (Timer *)timers_pool.head;
+       REMOVE((Node *)timer);
+
+       ENABLE_IRQRESTORE(flags);
+
+       return timer;
+}
+
+
+/*!
+ * Delete a timer, putting it in the available timers queue.
+ */
+void timer_delete(Timer *timer)
+{
+       cpuflags_t flags;
+       DISABLE_IRQSAVE(flags);
+       ADDHEAD(&timers_pool, &timer->link);
+       ENABLE_IRQRESTORE(flags);
+}
+
+
+/*!
+ * Add the specified timer to the software timer service queue.
+ * When the delay indicated by the timer expires, the timer
+ * device will execute the event associated with it.
+ *
+ * \note Interrupt safe
+ */
+void timer_add(Timer *timer)
+{
+       Timer *node;
+       cpuflags_t flags;
+
+       DISABLE_IRQSAVE(flags);
+
+       /* Calculate expiration time for this timer */
+       timer->tick = _clock + timer->delay;
+
+       /* Search for the first node whose expiration time is
+        * greater than the timer we want to add.
+        */
+       node = (Timer *)timers_queue.head;
+       while (node->link.succ)
+       {
+               /* Stop just after the insert point */
+               if (node->tick > timer->tick)
+                       break;
+
+               /* Go to next node */
+               node = (Timer *)node->link.succ;
+       }
+
+       /* Enqueue timer request into the list */
+       INSERTBEFORE((Node *)timer, (Node *)node);
+
+       ENABLE_IRQRESTORE(flags);
+}
+
+
+/*!
+ * Remove a timer from the timer queue before it has expired
+ */
+Timer *timer_abort(Timer *timer)
+{
+       cpuflags_t flags;
+       DISABLE_IRQSAVE(flags);
+       REMOVE((Node *)timer);
+       ENABLE_IRQRESTORE(flags);
+
+       return timer;
+}
+
+
+/*!
+ * Wait for the specified amount of time (expressed in ms)
+ */
+void timer_delay(time_t time)
+{
+#ifdef CONFIG_KERN_SIGNALS
+       Timer t;
+
+       ASSERT(!sig_check(SIG_SINGLE));
+       timer_set_event_signal(&t, proc_current(), SIG_SINGLE);
+       timer_set_delay(&t, time);
+       timer_add(&t);
+       sig_wait(SIG_SINGLE);
+#else
+       time_t start = timer_gettick();
+
+       while (timer_gettick() - start < time) { /* nop */ }
+#endif
+}
+
+/*!
+ * Wait for the specified amount of time (expressed in microseconds)
+ */
+void timer_udelay(utime_t usec_delay)
+{
+       if (usec_delay > 1000)
+       {
+               timer_delay(usec_delay / 1000);
+               usec_delay %= 1000;
+       }
+
+       // FIXME: This multiplication is too slow at run-time. We should try and move it
+       //  to compile-time by exposing the TIMER_HW_HPTICKS_PER_SEC in the header
+       //  file.
+       hptime_t start = timer_hw_hpread();
+       hptime_t delay = (uint32_t)usec_delay * TIMER_HW_HPTICKS_PER_SEC / 1000000ul;
+
+       while (timer_hw_hpread() - start < delay) 
+       {}
+}
+
+
+/*!
+ * Timer interrupt handler. Find soft timers expired and
+ * trigger corresponding events.
+ */
+DEFINE_TIMER_ISR
+{
+       /* With the Metrowerks compiler, the only way to force the compiler generate
+          an interrupt service routine is to put a pragma directive within the function
+          body. */
+       #ifdef __MWERKS__
+       #pragma interrupt saveall
+       #endif
+
+       Timer *timer;
+
+       timer_hw_irq();
+
+       /* Update the master ms counter */
+       ++_clock;
+
+       /*
+        * Check the first timer request in the list and process
+        * it when it has expired. Repeat this check until the
+        * first node has not yet expired. Since the list is sorted
+        * by expiry time, all the following requests are guaranteed
+        * to expire later.
+        */
+       while ((timer = (Timer *)timers_queue.head)->link.succ)
+       {
+               /* This request in list has not yet expired? */
+               if (_clock < timer->tick)
+                       break;
+
+               /* Retreat the expired timer */
+               REMOVE((Node *)timer);
+
+               /* Execute the associated event */
+               DOEVENT_INTR(&timer->expire);
+       }
+}
+
+
+
+/*!
+ * Initialize timer
+ */
+void timer_init(void)
+{
+       int i;
+
+       INITLIST(&timers_queue);
+       INITLIST(&timers_pool);
+
+       /* Init all software timers in the free pool */
+       for (i = 0; i < MAX_TIMERS; i++)
+               ADDTAIL(&timers_pool, (Node *)&soft_timers[i]);
+
+       _clock = 0;
+
+       timer_hw_init();
+}
+
diff --git a/drv/timer.h b/drv/timer.h
new file mode 100755 (executable)
index 0000000..e1a3786
--- /dev/null
@@ -0,0 +1,105 @@
+/*!
+ * \file
+ * <!--
+ * Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 2000 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ *
+ * \brief Hardware independent timer driver (interface)
+ */
+
+/*
+ * $Log$
+ * Revision 1.1  2004/05/23 18:23:30  bernie
+ * Import drv/timer module.
+ *
+ */
+#ifndef DRV_TIMER_H
+#define DRV_TIMER_H
+
+#include "cpu.h"
+#include "compiler.h"
+#include <mware/list.h>
+#include <kern/event.h>
+
+/*! Number of timer ticks per second. */
+#define TICKS_PER_SEC       1000
+
+typedef struct Timer
+{
+       Node   link;      /*!< Link into timers queue */
+       time_t delay;     /*!< Timer delay in ms */
+       time_t tick;      /*!< Timer will expire at this tick */
+       Event  expire;    /*!< Event to execute when the timer expires */
+} Timer;
+
+extern void timer_init(void);
+extern Timer *timer_new(void);
+extern void timer_delete(Timer *timer);
+extern void timer_add(Timer *timer);
+extern Timer *timer_abort(Timer *timer);
+extern void timer_delay(time_t time);
+extern void timer_udelay(utime_t utime);
+extern inline time_t timer_gettick(void);
+extern inline time_t timer_gettick_irq(void);
+
+#ifdef CONFIG_KERN_SIGNALS
+/*! Set the timer so that it sends a signal when it expires */
+INLINE void timer_set_event_signal(Timer* timer, struct Process* proc, sigset_t sigs)
+{
+       INITEVENT_SIG(&timer->expire, proc, sigs);
+}
+#endif
+
+/*! Set the timer so that it calls an user hook when it expires */
+INLINE void timer_set_event_softint(Timer* timer, Hook func, void* user_data)
+{
+       INITEVENT_INT(&timer->expire, func, user_data);
+}
+
+/*! Set the timer delay (the time before the event will be triggered) */
+INLINE void timer_set_delay(Timer* timer, time_t delay)
+{
+       timer->delay = delay;
+}
+
+
+/*!
+ * Return the system tick counter (expressed in ms)
+ * This function must disable interrupts on 8/16bit CPUs because the
+ * clock variable is larger than the processor word size and can't
+ * be copied atomically.
+ */
+extern inline time_t timer_gettick(void)
+{
+       extern volatile time_t _clock;
+
+       time_t result;
+       cpuflags_t flags;
+
+       DISABLE_IRQSAVE(flags);
+       result = _clock;
+       ENABLE_IRQRESTORE(flags);
+
+       return result;
+}
+
+
+/*!
+ * Like \c timer_gettick, faster version to be called
+ * from interrupt context only.
+ */
+extern inline time_t timer_gettick_irq(void)
+{
+       extern volatile time_t _clock;
+
+       return _clock;
+}
+
+#endif /* DRV_TIMER_H */
+
diff --git a/drv/timer_avr.h b/drv/timer_avr.h
new file mode 100755 (executable)
index 0000000..359299d
--- /dev/null
@@ -0,0 +1,150 @@
+/*!
+ * \file
+ * <!--
+ * Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 2000 Bernardo Innocenti
+ * All Rights Reserved.
+ * -->
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ *
+ * \brief Low-level timer module for AVR
+ */
+
+/*
+ * $Log$
+ * Revision 1.1  2004/05/23 18:23:30  bernie
+ * Import drv/timer module.
+ *
+ */
+
+#ifndef DRV_TIMER_AVR_H
+#define DRV_TIMER_AVR_H
+
+#include <avr/wdt.h>
+
+#      define TIMER_RETRIGGER /* Not needed, timer retriggers automatically  */
+
+       /*!
+        * System timer: additional division after the prescaler
+        * 12288000 / 64 / 192 (0..191) = 1 ms
+        */
+#      define OCR_DIVISOR 191
+
+       /*! HW dependent timer initialization  */
+#if defined(CONFIG_TIMER_ON_TIMER0)
+
+#              define TIMER_INIT \
+               do { \
+                       DISABLE_INTS; \
+                       \
+                       /* Reset Timer flags */ \
+                       TIFR = BV(OCF0) | BV(TOV0); \
+                       \
+                       /* Setup Timer/Counter interrupt */ \
+                       ASSR = 0x00;                  /* internal system clock */ \
+                       TCCR0 = BV(WGM01) | BV(CS02); /* Clear on Compare match & prescaler = 64 */ \
+                       TCNT0 = 0x00;                 /* initialization of Timer/Counter */ \
+                       OCR0 = OCR_DIVISOR;           /* Timer/Counter Output Compare Register */ \
+                       \
+                       /* Enable timer interrupts: Timer/Counter2 Output Compare (OCIE2) */ \
+                       TIMSK &= ~BV(TOIE0); \
+                       TIMSK |= BV(OCIE0); \
+                       \
+                       ENABLE_INTS; \
+               } while (0)
+
+#elif defined(CONFIG_TIMER_ON_TIMER1_OVERFLOW)
+
+#              define TIMER_INIT \
+               do { \
+                       DISABLE_INTS; \
+                       \
+                       /* Reset Timer overflow flag */ \
+                       TIFR |= BV(TOV1); \
+                       \
+                       /* Fast PWM mode, 24 kHz, no prescaling */ \
+                       TCCR1A |= BV(WGM11); \
+                       TCCR1A &= ~BV(WGM10); \
+                       TCCR1B |= BV(WGM12) | BV(CS10); \
+                       TCCR1B &= ~(BV(WGM13) | BV(CS11) | BV(CS12)); \
+                       \
+                       TCNT1 = 0x00;         /* initialization of Timer/Counter */ \
+                       \
+                       /* Enable timer interrupt: Timer/Counter1 Overflow */ \
+                       TIMSK |= BV(TOIE1); \
+                       \
+                       ENABLE_INTS; \
+               } while (0)
+
+#elif defined(CONFIG_TIMER_ON_TIMER2)
+
+#              define TIMER_INIT \
+               do { \
+                       DISABLE_INTS; \
+                       \
+                       /* Reset Timer flags */ \
+                       TIFR = BV(OCF2) | BV(TOV2); \
+                       \
+                       /* Setup Timer/Counter interrupt */ \
+                       TCCR2 = BV(WGM21) | BV(CS21) | BV(CS20); \
+                                             /* Clear on Compare match & prescaler = 64, internal sys clock */ \
+                       TCNT2 = 0x00;         /* initialization of Timer/Counter */ \
+                       OCR2 = OCR_DIVISOR;   /* Timer/Counter Output Compare Register */ \
+                       \
+                       /* Enable timer interrupts: Timer/Counter2 Output Compare (OCIE2) */ \
+                       TIMSK &= ~BV(TOIE2); \
+                       TIMSK |= BV(OCIE2); \
+                       \
+                       ENABLE_INTS; \
+               } while (0)
+
+#else
+#              error Choose witch timer to use with CONFIG_TIMER_ON_TIMERx
+#endif /* CONFIG_TIMER_ON_TIMERx */
+
+
+#if defined(CONFIG_TIMER_ON_TIMER1_OVERFLOW)
+
+       #define DEFINE_TIMER_ISR        \
+               static void timer_handler(void)
+
+       /*
+       * Timer 1 overflow irq handler.
+       */
+       SIGNAL(SIG_OVERFLOW1)
+       {
+               /*!
+               * How many overflow we have to count before calling the true timer handler.
+               * If timer overflow is at 24 kHz, with a value of 24 we have 1 ms between
+               * each call.
+               */
+       #define TIMER1_OVF_COUNT 24
+
+               static uint8_t count = TIMER1_OVF_COUNT;
+
+               count--;
+               if (!count)
+               {
+                       timer_handler();
+                       count = TIMER1_OVF_COUNT;
+               }
+       }
+
+#elif defined (CONFIG_TIMER_ON_TIMER0)
+
+       #define DEFINE_TIMER_ISR        \
+               SIGNAL(SIG_OUTPUT_COMPARE0)
+
+#elif defined(CONFIG_TIMER_ON_TIMER2)
+
+       #define DEFINE_TIMER_ISR        \
+               SIGNAL(SIG_OUTPUT_COMPARE2)
+
+#else
+#      error Choose witch timer to use with CONFIG_TIMER_ON_TIMERx
+#endif /* CONFIG_TIMER_ON_TIMERx */
+
+#endif /* DRV_TIMER_AVR_H */
diff --git a/drv/timer_dsp56k.h b/drv/timer_dsp56k.h
new file mode 100755 (executable)
index 0000000..2b61e58
--- /dev/null
@@ -0,0 +1,95 @@
+/*!
+ * \file
+ * <!--
+ * Copyright 2004 Giovanni Bajo
+ * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
+ * All Rights Reserved.
+ * -->
+ *
+ * \version $Id$
+ *
+ * \author Giovanni Bajo <rasky@develer.com>
+ *
+ * \brief Driver module for DSP56K
+ */
+
+/*
+ * $Log$
+ * Revision 1.1  2004/05/23 18:23:30  bernie
+ * Import drv/timer module.
+ *
+ */
+
+#ifndef DRV_TIMER_DSP56K_H
+#define DRV_TIMER_DSP56K_H
+
+#include <DSP56F807.h>
+#include <compiler.h>
+
+//! The system timer for events is currently TMRA0. We prefer A/B over C/D because
+//  A/B share the pin with the quadrature decoder module, and we do not need
+//  pins for our system timer.
+//  If you want to change this setting, you need also to modify the IRQ vector table.
+#define REG_SYSTEM_TIMER          (REG_TIMER_A + 0)
+#define SYSTEM_TIMER_IRQ_VECTOR   42   /* TMRA0 */
+
+#define TIMER_PRESCALER           16
+
+//! Frequency of the hardware high precision timer
+#define TIMER_HW_HPTICKS_PER_SEC           (IPBUS_FREQ / TIMER_PRESCALER)
+
+//! Type of time expressed in ticks of the hardware high precision timer
+typedef uint16_t hptime_t;
+
+static void timer_hw_init(void)
+{
+       uint16_t compare;
+
+       // Clear compare flag status and enable interrupt on compare
+       REG_SYSTEM_TIMER->SCR &= ~REG_TIMER_SCR_TCF;
+       REG_SYSTEM_TIMER->SCR |= REG_TIMER_SCR_TCFIE;
+
+       // Calculate the compare value needed to generate an interrupt exactly
+       //  TICKS_PER_SEC times each second (usually, every millisecond). Check that
+       //  the calculation is accurate, otherwise there is a precision error
+       // (probably the prescaler is too big or too small).
+       compare = TIMER_HW_HPTICKS_PER_SEC / TICKS_PER_SEC;
+       ASSERT((uint32_t)compare * TICKS_PER_SEC == IPBUS_FREQ / TIMER_PRESCALER);
+       REG_SYSTEM_TIMER->CMP1 = compare;
+
+       // The value for reload (at initializationa and after compare is met) is zero
+       REG_SYSTEM_TIMER->LOAD = 0;
+
+       // Set the interrupt priority
+       irq_setpriority(SYSTEM_TIMER_IRQ_VECTOR, IRQ_PRIORITY_SYSTEM_TIMER);
+
+       // Small preprocessor trick to generate the REG_TIMER_CTRL_PRIMARY_IPBYNN macro
+       //  needed to set the prescaler
+       #define PP_CAT2(x,y)                      x ## y
+       #define PP_CAT(x,y)                       PP_CAT2(x,y)
+       #define REG_CONTROL_PRESCALER             PP_CAT(REG_TIMER_CTRL_PRIMARY_IPBY, TIMER_PRESCALER)
+
+       // Setup the counter and start counting
+       REG_SYSTEM_TIMER->CTRL =
+               REG_TIMER_CTRL_MODE_RISING    |          // count rising edges (normal)
+               REG_CONTROL_PRESCALER         |          // frequency (IPbus / TIMER_PRESCALER)
+               REG_TIMER_CTRL_LENGTH;                   // up to CMP1, then reload
+}
+
+INLINE void timer_hw_irq(void)
+{
+       // Clear the overflow flag so that we are ready for another interrupt
+       REG_SYSTEM_TIMER->SCR &= ~REG_TIMER_SCR_TCF;
+}
+
+INLINE hptime_t timer_hw_hpread(void)
+{
+       return REG_SYSTEM_TIMER->CNTR;
+}
+
+void system_timer_isr(void);
+
+#define DEFINE_TIMER_ISR \
+       void system_timer_isr(void)
+
+#endif /* DRV_TIMER_DSP56_H */
diff --git a/drv/timer_i196.h b/drv/timer_i196.h
new file mode 100755 (executable)
index 0000000..d36abf9
--- /dev/null
@@ -0,0 +1,46 @@
+/*!
+ * \file
+ * <!--
+ * Copyright (C) 2000 Bernardo Innocenti
+ * Copyright (C) 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * All Rights Reserved.
+ * -->
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ *
+ * \brief Low-level timer module for AVR
+ */
+
+/*
+ * $Log$
+ * Revision 1.1  2004/05/23 18:23:30  bernie
+ * Import drv/timer module.
+ *
+ */
+
+#ifndef TIMER_I196_H
+#define TIMER_I196_H
+
+       /*!
+        * Retrigger TIMER2, adjusting the time to account for
+        * the interrupt prologue latency.
+        */
+#      define TIMER_RETRIGGER (TIMER2 -= TICKS_RATE)
+
+#      define TIMER_INIT \
+               TIMER2 = (65535 - TICKS_RATE); \
+               INT_MASK1 |= INT1F_T2OVF; \
+               \
+               DISABLE_INTS; \
+               WSR = 1; \
+               IOC3 |= IOC3F_T2_ENA; \
+               WSR = 0; \
+               ENABLE_INTS
+
+#define DEFINE_TIMER_ISR \
+       INTERRUPT(0x38) void TM2_OVFL_interrupt(void);  \
+       INTERRUPT(0x38) void TM2_OVFL_interrupt(void)
+
+#endif /* DRV_TIMER_I196_H */