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

4.104.114.95
Last change on this file since 4296ec6 was 4296ec6, checked in by Ralf Corsepius <ralf.corsepius@…>, on 09/02/08 at 13:55:05

Eliminate rtems_boolean.

  • Property mode set to 100644
File size: 17.7 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 <bsp/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;
62bool              benchmark_timer_find_average_overhead = true;
63volatile unsigned int     fastLoop1ms, slowLoop1ms;
64void (*benchmark_timer_initialize_function)(void) = 0;
65uint32_t (*benchmark_timer_read_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(void);
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: benchmark_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 bool 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: benchmark_timer_read
137|      Description: Read hardware timer value.
138| Global Variables: Ttimer_val, benchmark_timer_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 (benchmark_timer_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: benchmark_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} /* benchmark_timer_initialize */
252
253/*-------------------------------------------------------------------------+
254|         Function: benchmark_timer_read
255|      Description: Read hardware timer value.
256| Global Variables: Ttimer_val, benchmark_timer_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 (benchmark_timer_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
286benchmark_timer_initialize(void)
287{
288    static bool First = true;
289
290    if (First) {
291        if (x86_capability & (1 << 4) ) {
292#if defined(DEBUG)
293            printk("TSC: timer initialization\n");
294#endif /* DEBUG */
295            benchmark_timer_initialize_function = &tsc_timer_initialize;
296            benchmark_timer_read_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 /* DEBUG */
303            benchmark_timer_initialize_function = &i386_timer_initialize;
304            benchmark_timer_read_function = &i386_read_timer;
305            Timer_exit_function = &i386_timer_exit;
306        }
307        First = false;
308    }
309    (*benchmark_timer_initialize_function)();
310}
311
312uint32_t
313benchmark_timer_read(void)
314{
315    return (*benchmark_timer_read_function)();
316}
317
318void
319Timer_exit(void)
320{
321    return (*Timer_exit_function)();
322}
323
324/*-------------------------------------------------------------------------+
325|         Function: benchmark_timer_disable_subtracting_average_overhead
326|      Description: Set internal benchmark_timer_find_average_overhead flag value.
327| Global Variables: benchmark_timer_find_average_overhead.
328|        Arguments: find_flag - new value of the flag.
329|          Returns: Nothing.
330+--------------------------------------------------------------------------*/
331void
332benchmark_timer_disable_subtracting_average_overhead(rtems_boolean find_flag)
333{
334  benchmark_timer_find_average_overhead = find_flag;
335} /* benchmark_timer_disable_subtracting_average_overhead */
336
337static unsigned short lastLoadedValue;
338
339/*-------------------------------------------------------------------------+
340| Description: Loads timer 0 with value passed as arguemnt.
341| Returns: Nothing. Loaded value must be a number of clock bits...
342+--------------------------------------------------------------------------*/
343void loadTimerValue( unsigned short loadedValue )
344{
345  lastLoadedValue = loadedValue;
346  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_SQWAVE);
347  outport_byte(TIMER_CNTR0, loadedValue & 0xff);
348  outport_byte(TIMER_CNTR0, (loadedValue >> 8) & 0xff);
349}
350
351/*-------------------------------------------------------------------------+
352| Description: Reads the current value of the timer, and converts the
353|                          number of ticks to micro-seconds.
354| Returns: number of clock bits elapsed since last load.
355+--------------------------------------------------------------------------*/
356unsigned int readTimer0(void)
357{
358  unsigned short lsb, msb;
359  unsigned char  status;
360  unsigned int   count;
361
362  outport_byte(TIMER_MODE, (TIMER_RD_BACK | (RB_COUNT_0 & ~(RB_NOT_STATUS | RB_NOT_COUNT))));
363  inport_byte(TIMER_CNTR0, status);
364  inport_byte(TIMER_CNTR0, lsb);
365  inport_byte(TIMER_CNTR0, msb);
366  count = ( msb << 8 ) | lsb ;
367  if (status & RB_OUTPUT )
368        count += lastLoadedValue;
369
370  return (2*lastLoadedValue - count);
371}
372
373void Timer0Reset(void)
374{
375  loadTimerValue(0xffff);
376  readTimer0();
377}
378
379void fastLoop (unsigned int loopCount)
380{
381  unsigned int i;
382  for( i=0; i < loopCount; i++ )outport_byte( SLOW_DOWN_IO, 0 );
383}
384
385void slowLoop (unsigned int loopCount)
386{
387  unsigned int j;
388  for (j=0; j <100 ;  j++) {
389    fastLoop (loopCount);
390  }
391}
392
393/*
394 * #define DEBUG_CALIBRATE
395 */
396void
397Calibrate_loop_1ms(void)
398{
399  unsigned int offset, offsetTmp, emptyCall, emptyCallTmp, res, i, j;
400  unsigned int targetClockBits, currentClockBits;
401  unsigned int slowLoopGranularity, fastLoopGranularity;
402  rtems_interrupt_level  level;
403
404#ifdef DEBUG_CALIBRATE
405  printk( "Calibrate_loop_1ms is starting,  please wait ( but not too loooong. )\n" );
406#endif
407  targetClockBits = US_TO_TICK(1000);
408
409  rtems_interrupt_disable(level);
410  /*
411   * Fill up the cache to get a correct offset
412   */
413  Timer0Reset();
414  readTimer0();
415  /*
416   * Compute the minimal offset to apply due to read counter register.
417   */
418  offset = 0xffffffff;
419  for (i=0; i <1000; i++) {
420    Timer0Reset();
421    offsetTmp = readTimer0();
422    offset += offsetTmp;
423  }
424  offset = offset / 1000; /* compute average */
425  /*
426   * calibrate empty call
427   */
428  fastLoop (0);
429  emptyCall = 0;
430  j = 0;
431  for (i=0; i <10; i++) {
432    Timer0Reset();
433    fastLoop (0);
434    res =  readTimer0();
435    /* res may be inferior to offset on fast
436     * machine because we took an average for offset
437     */
438    if (res >  offset) {
439      ++j;
440      emptyCallTmp = res - offset;
441      emptyCall += emptyCallTmp;
442    }
443  }
444  if (j == 0) emptyCall = 0;
445  else emptyCall = emptyCall / j; /* compute average */
446  /*
447   * calibrate fast loop
448   */
449  Timer0Reset();
450  fastLoop (10000);
451  res = readTimer0() - offset;
452  if (res < emptyCall) {
453     printk("Problem #1 in offset computation in Calibrate_loop_1ms in file libbsp/i386/pc386/timer/timer.c\n");
454    while (1);
455  }
456  fastLoopGranularity = (res - emptyCall) / 10000;
457  /*
458   * calibrate slow loop
459   */
460  Timer0Reset();
461  slowLoop(10);
462  res = readTimer0();
463  if (res < offset + emptyCall) {
464     printk("Problem #2 in offset computation in Calibrate_loop_1ms in file libbsp/i386/pc386/timer/timer.c\n");
465    while (1);
466  }
467  slowLoopGranularity = (res - offset - emptyCall)/ 10;
468
469  if (slowLoopGranularity == 0) {
470    printk("Problem #3 in Calibrate_loop_1ms in file libbsp/i386/pc386/timer/timer.c\n");
471    while (1);
472  }
473
474  targetClockBits += offset;
475#ifdef DEBUG_CALIBRATE
476  printk("offset = %u, emptyCall = %u, targetClockBits = %u\n",
477         offset, emptyCall, targetClockBits);
478  printk("slowLoopGranularity = %u fastLoopGranularity =  %u\n",
479         slowLoopGranularity, fastLoopGranularity);
480#endif
481  slowLoop1ms = (targetClockBits - emptyCall) / slowLoopGranularity;
482  if (slowLoop1ms != 0) {
483    fastLoop1ms = targetClockBits % slowLoopGranularity;
484    if (fastLoop1ms > emptyCall) fastLoop1ms -= emptyCall;
485  }
486  else
487    fastLoop1ms = targetClockBits - emptyCall / fastLoopGranularity;
488
489  if (slowLoop1ms != 0) {
490    /*
491     * calibrate slow loop
492     */
493
494    while(1)
495      {
496        int previousSign = 0; /* 0 = unset, 1 = incrementing,  2 = decrementing */
497        Timer0Reset();
498        slowLoop(slowLoop1ms);
499        currentClockBits = readTimer0();
500        if (currentClockBits > targetClockBits) {
501          if ((currentClockBits - targetClockBits) < slowLoopGranularity) {
502            /* decrement loop counter anyway to be sure slowLoop(slowLoop1ms) < targetClockBits */
503            --slowLoop1ms;
504            break;
505          }
506          else {
507            --slowLoop1ms;
508            if (slowLoop1ms == 0) break;
509            if (previousSign == 0) previousSign = 2;
510            if (previousSign == 1) break;
511          }
512        }
513        else {
514          if ((targetClockBits - currentClockBits) < slowLoopGranularity) {
515            break;
516          }
517          else {
518            ++slowLoop1ms;
519            if (previousSign == 0) previousSign = 1;
520            if (previousSign == 2) break;
521          }
522        }
523      }
524  }
525  /*
526   * calibrate fast loop
527   */
528
529  if (fastLoopGranularity != 0 ) {
530    while(1) {
531      int previousSign = 0; /* 0 = unset, 1 = incrementing,  2 = decrementing */
532      Timer0Reset();
533      if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
534      fastLoop(fastLoop1ms);
535      currentClockBits = readTimer0();
536      if (currentClockBits > targetClockBits) {
537        if ((currentClockBits - targetClockBits) < fastLoopGranularity)
538          break;
539        else {
540          --fastLoop1ms;
541          if (previousSign == 0) previousSign = 2;
542          if (previousSign == 1) break;
543        }
544      }
545      else {
546        if ((targetClockBits - currentClockBits) < fastLoopGranularity)
547          break;
548        else {
549          ++fastLoop1ms;
550          if (previousSign == 0) previousSign = 1;
551          if (previousSign == 2) break;
552        }
553      }
554    }
555  }
556#ifdef DEBUG_CALIBRATE
557  printk("slowLoop1ms = %u, fastLoop1ms = %u\n", slowLoop1ms, fastLoop1ms);
558#endif
559  rtems_interrupt_enable(level);
560
561}
562
563/*-------------------------------------------------------------------------+
564|         Function: Wait_X_1ms
565|      Description: loop which waits at least timeToWait ms
566| Global Variables: loop1ms
567|        Arguments: timeToWait
568|          Returns: Nothing.
569+--------------------------------------------------------------------------*/
570void
571Wait_X_ms( unsigned int timeToWait){
572
573  unsigned int j;
574
575  for (j=0; j<timeToWait ; j++) {
576    if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
577    fastLoop(fastLoop1ms);
578  }
579
580}
Note: See TracBrowser for help on using the repository browser.