source: rtems/c/src/lib/libbsp/i386/pc386/timer/timer.c @ e75e10d

Last change on this file since e75e10d was fb573a71, checked in by Joel Sherrill <joel.sherrill@…>, on 10/05/05 at 02:39:35

2005-08-23 Karel Gardas <kgardas@…>>

  • timer/timer.c: Enhance to use either interupt-based timer functions on older CPUs or to use TSC-based timer functions on more recent (Pentium and above) CPUs. The decision is made in Timer_initialize function when it is called for the first time based on a result obtained from cpuid instruction during the BSP initialization phase. During the first call, there are also late bindings to the implementation functions initialized to appropriate values.
  • Property mode set to 100644
File size: 17.8 KB
Line 
1/*-------------------------------------------------------------------------+
2| timer.c v1.1 - PC386 BSP - 1997/08/07
3+--------------------------------------------------------------------------+
4| This file contains the PC386 timer package.
5+--------------------------------------------------------------------------+
6| NOTE: It is important that the timer start/stop overhead be determined
7|       when porting or modifying this code.
8+--------------------------------------------------------------------------+
9| (C) Copyright 1997 -
10| - NavIST Group - Real-Time Distributed Systems and Industrial Automation
11|
12| http://pandora.ist.utl.pt
13|
14| Instituto Superior Tecnico * Lisboa * PORTUGAL
15+--------------------------------------------------------------------------+
16| Disclaimer:
17|
18| This file is provided "AS IS" without warranty of any kind, either
19| expressed or implied.
20+--------------------------------------------------------------------------+
21| This code is base on:
22|   timer.c,v 1.7 1995/12/19 20:07:43 joel Exp - go32 BSP
23|
24| Rosimildo daSilva -ConnectTel, Inc - Fixed infinite loop in the Calibration
25| routine. I've seen this problems with faster machines ( pentiums ). Sometimes
26| RTEMS just hangs at startup.
27|
28| With the following copyright notice:
29| **************************************************************************
30| *  COPYRIGHT (c) 1989-1999.
31| *  On-Line Applications Research Corporation (OAR).
32| *
33| *  The license and distribution terms for this file may be
34| *  found in found in the file LICENSE in this distribution or at
35| *  http://www.rtems.com/license/LICENSE.
36| **************************************************************************
37|
38|  $Id$
39+--------------------------------------------------------------------------*/
40
41#include <stdlib.h>
42
43#include <bsp.h>
44#include <irq.h>
45
46/*-------------------------------------------------------------------------+
47| Constants
48+--------------------------------------------------------------------------*/
49#define AVG_OVERHEAD  0              /* 0.1 microseconds to start/stop timer. */
50#define LEAST_VALID   1              /* Don't trust a value lower than this.  */
51#define SLOW_DOWN_IO  0x80      /* io which does nothing */
52
53#define TWO_MS  (uint32_t)(2000)     /* TWO_MS = 2000us (sic!) */
54
55#define MSK_NULL_COUNT 0x40     /* bit counter available for reading */
56
57#define CMD_READ_BACK_STATUS 0xE2   /* command read back status */
58/*-------------------------------------------------------------------------+
59| Global Variables
60+--------------------------------------------------------------------------*/
61volatile uint32_t         Ttimer_val;
62rtems_boolean             Timer_driver_Find_average_overhead = TRUE;
63volatile unsigned int     fastLoop1ms, slowLoop1ms;
64void (*Timer_initialize_function)(void) = 0;
65uint32_t (*Read_timer_function)(void) = 0;
66void (*Timer_exit_function)(void) = 0;
67
68/*-------------------------------------------------------------------------+
69| External Prototypes
70+--------------------------------------------------------------------------*/
71extern void timerisr(void);
72       /* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */
73extern int x86_capability;
74
75/*
76 * forward declarations
77 */
78
79void Timer_exit();
80
81/*-------------------------------------------------------------------------+
82| Pentium optimized timer handling.
83+--------------------------------------------------------------------------*/
84
85/*-------------------------------------------------------------------------+
86|         Function: rdtsc
87|      Description: Read the value of PENTIUM on-chip cycle counter.
88| Global Variables: None.
89|        Arguments: None.
90|          Returns: Value of PENTIUM on-chip cycle counter.
91+--------------------------------------------------------------------------*/
92static inline unsigned long long
93rdtsc(void)
94{
95  /* Return the value of the on-chip cycle counter. */
96  unsigned long long result;
97  asm volatile(".byte 0x0F, 0x31" : "=A" (result));
98  return result;
99} /* rdtsc */
100
101/*-------------------------------------------------------------------------+
102|         Function: Timer_exit
103|      Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is
104|                   not really necessary, since there will be a reset at exit.
105| Global Variables: None.
106|        Arguments: None.
107|          Returns: Nothing.
108+--------------------------------------------------------------------------*/
109void
110tsc_timer_exit(void)
111{
112} /* tsc_timer_exit */
113
114/*-------------------------------------------------------------------------+
115|         Function: Timer_initialize
116|      Description: Timer initialization routine.
117| Global Variables: Ttimer_val.
118|        Arguments: None.
119|          Returns: Nothing.
120+--------------------------------------------------------------------------*/
121void
122tsc_timer_initialize(void)
123{
124  static rtems_boolean First = TRUE;
125
126  if (First)
127  {
128    First = FALSE;
129
130    atexit(Timer_exit); /* Try not to hose the system at exit. */
131  }
132  Ttimer_val = rdtsc(); /* read starting time */
133} /* tsc_timer_initialize */
134
135/*-------------------------------------------------------------------------+
136|         Function: Read_timer
137|      Description: Read hardware timer value.
138| Global Variables: Ttimer_val, Timer_driver_Find_average_overhead.
139|        Arguments: None.
140|          Returns: Nothing.
141+--------------------------------------------------------------------------*/
142uint32_t
143tsc_read_timer(void)
144{
145  register uint32_t         total;
146
147  total =  (uint32_t)(rdtsc() - Ttimer_val);
148
149  if (Timer_driver_Find_average_overhead)
150    return total;
151  else if (total < LEAST_VALID)
152    return 0; /* below timer resolution */
153  else
154    return (total - AVG_OVERHEAD);
155} /* tsc_read_timer */
156
157/*-------------------------------------------------------------------------+
158| Non-Pentium timer handling.
159+--------------------------------------------------------------------------*/
160#define US_PER_ISR   250  /* Number of micro-seconds per timer interruption */
161
162/*-------------------------------------------------------------------------+
163|         Function: Timer_exit
164|      Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is
165|                   not really necessary, since there will be a reset at exit.
166| Global Variables: None.
167|        Arguments: None.
168|          Returns: Nothing.
169+--------------------------------------------------------------------------*/
170static void
171timerOff(const rtems_raw_irq_connect_data* used)
172{
173    /*
174     * disable interrrupt at i8259 level
175     */
176     BSP_irq_disable_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
177     /* reset timer mode to standard (DOS) value */
178     outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
179     outport_byte(TIMER_CNTR0, 0);
180     outport_byte(TIMER_CNTR0, 0);
181} /* Timer_exit */
182
183static void
184timerOn(const rtems_raw_irq_connect_data* used)
185{
186     /* load timer for US_PER_ISR microsecond period */
187     outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
188     outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 0 & 0xff);
189     outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 8 & 0xff);
190    /*
191     * enable interrrupt at i8259 level
192     */
193     BSP_irq_enable_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
194}
195
196static int
197timerIsOn(const rtems_raw_irq_connect_data *used)
198{
199     return BSP_irq_enabled_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
200}
201
202static rtems_raw_irq_connect_data timer_raw_irq_data = {
203  BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE,
204  timerisr,
205  timerOn,
206  timerOff,
207  timerIsOn
208};
209
210/*-------------------------------------------------------------------------+
211|         Function: Timer_exit
212|      Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is
213|                   not really necessary, since there will be a reset at exit.
214| Global Variables: None.
215|        Arguments: None.
216|          Returns: Nothing.
217+--------------------------------------------------------------------------*/
218void
219i386_timer_exit(void)
220{
221  i386_delete_idt_entry (&timer_raw_irq_data);
222} /* Timer_exit */
223
224/*-------------------------------------------------------------------------+
225|         Function: Timer_initialize
226|      Description: Timer initialization routine.
227| Global Variables: Ttimer_val.
228|        Arguments: None.
229|          Returns: Nothing.
230+--------------------------------------------------------------------------*/
231void
232i386_timer_initialize(void)
233{
234  static rtems_boolean First = TRUE;
235
236  if (First)
237  {
238    First = FALSE;
239
240    atexit(Timer_exit); /* Try not to hose the system at exit. */
241    if (!i386_set_idt_entry (&timer_raw_irq_data)) {
242      printk("raw handler connexion failed\n");
243      rtems_fatal_error_occurred(1);
244    }
245  }
246  /* wait for ISR to be called at least once */
247  Ttimer_val = 0;
248  while (Ttimer_val == 0)
249    continue;
250  Ttimer_val = 0;
251} /* Timer_initialize */
252
253/*-------------------------------------------------------------------------+
254|         Function: Read_timer
255|      Description: Read hardware timer value.
256| Global Variables: Ttimer_val, Timer_driver_Find_average_overhead.
257|        Arguments: None.
258|          Returns: Nothing.
259+--------------------------------------------------------------------------*/
260uint32_t
261i386_read_timer(void)
262{
263  register uint32_t         total, clicks;
264  register uint8_t          lsb, msb;
265
266  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);
267  inport_byte(TIMER_CNTR0, lsb);
268  inport_byte(TIMER_CNTR0, msb);
269  clicks = (msb << 8) | lsb;
270  total  = (Ttimer_val * US_PER_ISR) + (US_PER_ISR - TICK_TO_US(clicks));
271
272  if (Timer_driver_Find_average_overhead)
273    return total;
274  else if (total < LEAST_VALID)
275    return 0; /* below timer resolution */
276  else
277    return (total - AVG_OVERHEAD);
278}
279
280/*
281 * General timer functions using either TSC-based implementation
282 * or interrupt-based implementation
283 */
284
285void
286Timer_initialize(void)
287{
288    static rtems_boolean First = TRUE;
289
290    if (First) {
291        if (x86_capability & (1 << 4) ) {
292#if defined(DEBUG)
293            printk("TSC: timer initialization\n");
294#endif
295            Timer_initialize_function = &tsc_timer_initialize;
296            Read_timer_function = &tsc_read_timer;
297            Timer_exit_function = &tsc_timer_exit;
298        }
299        else {
300#if defined(DEBUG)
301            printk("ISR: timer initialization\n");
302#endif
303            Timer_initialize_function = &i386_timer_initialize;
304            Read_timer_function = &i386_read_timer;
305            Timer_exit_function = &i386_timer_exit;
306        }
307        First = FALSE;
308    }
309    (*Timer_initialize_function)();
310}
311
312uint32_t
313Read_timer()
314{
315    return (*Read_timer_function)();
316}
317
318void
319Timer_exit()
320{
321    return (*Timer_exit_function)();
322}
323
324/*-------------------------------------------------------------------------+
325|         Function: Empty_function
326|      Description: Empty function used in time tests.
327| Global Variables: None.
328|        Arguments: None.
329|          Returns: Nothing.
330+--------------------------------------------------------------------------*/
331rtems_status_code Empty_function(void)
332{
333  return RTEMS_SUCCESSFUL;
334} /* Empty function */
335
336/*-------------------------------------------------------------------------+
337|         Function: Set_find_average_overhead
338|      Description: Set internal Timer_driver_Find_average_overhead flag value.
339| Global Variables: Timer_driver_Find_average_overhead.
340|        Arguments: find_flag - new value of the flag.
341|          Returns: Nothing.
342+--------------------------------------------------------------------------*/
343void
344Set_find_average_overhead(rtems_boolean find_flag)
345{
346  Timer_driver_Find_average_overhead = find_flag;
347} /* Set_find_average_overhead */
348
349static unsigned short lastLoadedValue;
350
351/*-------------------------------------------------------------------------+
352| Description: Loads timer 0 with value passed as arguemnt.
353| Returns: Nothing. Loaded value must be a number of clock bits...
354+--------------------------------------------------------------------------*/
355void loadTimerValue( unsigned short loadedValue )
356{
357  lastLoadedValue = loadedValue;
358  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_SQWAVE);
359  outport_byte(TIMER_CNTR0, loadedValue & 0xff);
360  outport_byte(TIMER_CNTR0, (loadedValue >> 8) & 0xff);
361}
362
363/*-------------------------------------------------------------------------+
364| Description: Reads the current value of the timer, and converts the
365|                          number of ticks to micro-seconds.
366| Returns: number of clock bits elapsed since last load.
367+--------------------------------------------------------------------------*/
368unsigned int readTimer0()
369{
370  unsigned short lsb, msb;
371  unsigned char  status;
372  unsigned int   count;
373
374  outport_byte(TIMER_MODE, (TIMER_RD_BACK | (RB_COUNT_0 & ~(RB_NOT_STATUS | RB_NOT_COUNT))));
375  inport_byte(TIMER_CNTR0, status);
376  inport_byte(TIMER_CNTR0, lsb);
377  inport_byte(TIMER_CNTR0, msb);
378  count = ( msb << 8 ) | lsb ;
379  if (status & RB_OUTPUT )
380        count += lastLoadedValue;
381
382  return (2*lastLoadedValue - count);
383}
384
385void Timer0Reset()
386{
387  loadTimerValue(0xffff);
388  readTimer0();
389}
390
391void fastLoop (unsigned int loopCount)
392{
393  unsigned int i;
394  for( i=0; i < loopCount; i++ )outport_byte( SLOW_DOWN_IO, 0 );
395}
396
397void slowLoop (unsigned int loopCount)
398{
399  unsigned int j;
400  for (j=0; j <100 ;  j++) {
401    fastLoop (loopCount);
402  }
403}
404
405/*
406 * #define DEBUG_CALIBRATE
407 */
408void
409Calibrate_loop_1ms(void)
410{
411  unsigned int offset, offsetTmp, emptyCall, emptyCallTmp, res, i, j;
412  unsigned int targetClockBits, currentClockBits;
413  unsigned int slowLoopGranularity, fastLoopGranularity;
414  rtems_interrupt_level  level;
415
416#ifdef DEBUG_CALIBRATE
417  printk( "Calibrate_loop_1ms is starting,  please wait ( but not too loooong. )\n" );
418#endif
419  targetClockBits = US_TO_TICK(1000);
420
421  rtems_interrupt_disable(level);
422  /*
423   * Fill up the cache to get a correct offset
424   */
425  Timer0Reset();
426  readTimer0();
427  /*
428   * Compute the minimal offset to apply due to read counter register.
429   */
430  offset = 0xffffffff;
431  for (i=0; i <1000; i++) {
432    Timer0Reset();
433    offsetTmp = readTimer0();
434    offset += offsetTmp;
435  }
436  offset = offset / 1000; /* compute average */
437  /*
438   * calibrate empty call
439   */
440  fastLoop (0);
441  emptyCall = 0;
442  j = 0;
443  for (i=0; i <10; i++) {
444    Timer0Reset();
445    fastLoop (0);
446    res =  readTimer0();
447    /* res may be inferior to offset on fast
448     * machine because we took an average for offset
449     */
450    if (res >  offset) {
451      ++j;
452      emptyCallTmp = res - offset;
453      emptyCall += emptyCallTmp;
454    }
455  }
456  if (j == 0) emptyCall = 0;
457  else emptyCall = emptyCall / j; /* compute average */
458  /*
459   * calibrate fast loop
460   */
461  Timer0Reset();
462  fastLoop (10000);
463  res = readTimer0() - offset;
464  if (res < emptyCall) {
465     printk("Problem #1 in offset computation in Calibrate_loop_1ms in file libbsp/i386/pc386/timer/timer.c\n");
466    while (1);
467  }
468  fastLoopGranularity = (res - emptyCall) / 10000;
469  /*
470   * calibrate slow loop
471   */
472  Timer0Reset();
473  slowLoop(10);
474  res = readTimer0();
475  if (res < offset + emptyCall) {
476     printk("Problem #2 in offset computation in Calibrate_loop_1ms in file libbsp/i386/pc386/timer/timer.c\n");
477    while (1);
478  }
479  slowLoopGranularity = (res - offset - emptyCall)/ 10;
480
481  if (slowLoopGranularity == 0) {
482    printk("Problem #3 in Calibrate_loop_1ms in file libbsp/i386/pc386/timer/timer.c\n");
483    while (1);
484  }
485
486  targetClockBits += offset;
487#ifdef DEBUG_CALIBRATE
488  printk("offset = %u, emptyCall = %u, targetClockBits = %u\n",
489         offset, emptyCall, targetClockBits);
490  printk("slowLoopGranularity = %u fastLoopGranularity =  %u\n",
491         slowLoopGranularity, fastLoopGranularity);
492#endif
493  slowLoop1ms = (targetClockBits - emptyCall) / slowLoopGranularity;
494  if (slowLoop1ms != 0) {
495    fastLoop1ms = targetClockBits % slowLoopGranularity;
496    if (fastLoop1ms > emptyCall) fastLoop1ms -= emptyCall;
497  }
498  else
499    fastLoop1ms = targetClockBits - emptyCall / fastLoopGranularity;
500
501  if (slowLoop1ms != 0) {
502    /*
503     * calibrate slow loop
504     */
505
506    while(1)
507      {
508        int previousSign = 0; /* 0 = unset, 1 = incrementing,  2 = decrementing */
509        Timer0Reset();
510        slowLoop(slowLoop1ms);
511        currentClockBits = readTimer0();
512        if (currentClockBits > targetClockBits) {
513          if ((currentClockBits - targetClockBits) < slowLoopGranularity) {
514            /* decrement loop counter anyway to be sure slowLoop(slowLoop1ms) < targetClockBits */
515            --slowLoop1ms;
516            break;
517          }
518          else {
519            --slowLoop1ms;
520            if (slowLoop1ms == 0) break;
521            if (previousSign == 0) previousSign = 2;
522            if (previousSign == 1) break;
523          }
524        }
525        else {
526          if ((targetClockBits - currentClockBits) < slowLoopGranularity) {
527            break;
528          }
529          else {
530            ++slowLoop1ms;
531            if (previousSign == 0) previousSign = 1;
532            if (previousSign == 2) break;
533          }
534        }
535      }
536  }
537  /*
538   * calibrate fast loop
539   */
540
541  if (fastLoopGranularity != 0 ) {
542    while(1) {
543      int previousSign = 0; /* 0 = unset, 1 = incrementing,  2 = decrementing */
544      Timer0Reset();
545      if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
546      fastLoop(fastLoop1ms);
547      currentClockBits = readTimer0();
548      if (currentClockBits > targetClockBits) {
549        if ((currentClockBits - targetClockBits) < fastLoopGranularity)
550          break;
551        else {
552          --fastLoop1ms;
553          if (previousSign == 0) previousSign = 2;
554          if (previousSign == 1) break;
555        }
556      }
557      else {
558        if ((targetClockBits - currentClockBits) < fastLoopGranularity)
559          break;
560        else {
561          ++fastLoop1ms;
562          if (previousSign == 0) previousSign = 1;
563          if (previousSign == 2) break;
564        }
565      }
566    }
567  }
568#ifdef DEBUG_CALIBRATE
569  printk("slowLoop1ms = %u, fastLoop1ms = %u\n", slowLoop1ms, fastLoop1ms);
570#endif
571  rtems_interrupt_enable(level);
572
573}
574
575/*-------------------------------------------------------------------------+
576|         Function: Wait_X_1ms
577|      Description: loop which waits at least timeToWait ms
578| Global Variables: loop1ms
579|        Arguments: timeToWait
580|          Returns: Nothing.
581+--------------------------------------------------------------------------*/
582void
583Wait_X_ms( unsigned int timeToWait){
584
585  unsigned int j;
586
587  for (j=0; j<timeToWait ; j++) {
588    if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
589    fastLoop(fastLoop1ms);
590  }
591
592}
Note: See TracBrowser for help on using the repository browser.