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

4.104.115
Last change on this file since 959f887a was 959f887a, checked in by Joel Sherrill <joel.sherrill@…>, on 12/03/08 at 17:28:10

2008-12-03 Joel Sherrill <joel.sherrill@…>

Michael South <msouth@…>

PR 1344/bsps

  • configure.ac, clock/ckinit.c, timer/timer.c: Add use of TSC for nanoseconds granularity. i8254 is very slow on some systems. TSC use is auto-detected by default.
  • Property mode set to 100644
File size: 17.1 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#include <libcpu/cpuModel.h>
46
47/*-------------------------------------------------------------------------+
48| Constants
49+--------------------------------------------------------------------------*/
50#define AVG_OVERHEAD  0              /* 0.1 microseconds to start/stop timer. */
51#define LEAST_VALID   1              /* Don't trust a value lower than this.  */
52#define SLOW_DOWN_IO  0x80      /* io which does nothing */
53
54#define TWO_MS  (uint32_t)(2000)     /* TWO_MS = 2000us (sic!) */
55
56#define MSK_NULL_COUNT 0x40     /* bit counter available for reading */
57
58#define CMD_READ_BACK_STATUS 0xE2   /* command read back status */
59/*-------------------------------------------------------------------------+
60| Global Variables
61+--------------------------------------------------------------------------*/
62volatile uint32_t         Ttimer_val;
63bool                      benchmark_timer_find_average_overhead = true;
64volatile unsigned int     fastLoop1ms, slowLoop1ms;
65
66void     (*benchmark_timer_initialize_function)(void) = 0;
67uint32_t (*benchmark_timer_read_function)(void) = 0;
68void     (*Timer_exit_function)(void) = 0;
69
70/*-------------------------------------------------------------------------+
71| External Prototypes
72+--------------------------------------------------------------------------*/
73extern void timerisr(void);
74       /* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */
75
76/*
77 * forward declarations
78 */
79
80void Timer_exit(void);
81
82/*-------------------------------------------------------------------------+
83| Pentium optimized timer handling.
84+--------------------------------------------------------------------------*/
85
86/*-------------------------------------------------------------------------+
87|         Function: Timer_exit
88|      Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is
89|                   not really necessary, since there will be a reset at exit.
90| Global Variables: None.
91|        Arguments: None.
92|          Returns: Nothing.
93+--------------------------------------------------------------------------*/
94void
95tsc_timer_exit(void)
96{
97} /* tsc_timer_exit */
98
99/*-------------------------------------------------------------------------+
100|         Function: benchmark_timer_initialize
101|      Description: Timer initialization routine.
102| Global Variables: Ttimer_val.
103|        Arguments: None.
104|          Returns: Nothing.
105+--------------------------------------------------------------------------*/
106void
107tsc_timer_initialize(void)
108{
109  static bool First = true;
110
111  if (First)
112  {
113    First = false;
114
115    atexit(Timer_exit); /* Try not to hose the system at exit. */
116  }
117  Ttimer_val = rdtsc(); /* read starting time */
118} /* tsc_timer_initialize */
119
120/*-------------------------------------------------------------------------+
121|         Function: benchmark_timer_read
122|      Description: Read hardware timer value.
123| Global Variables: Ttimer_val, benchmark_timer_find_average_overhead.
124|        Arguments: None.
125|          Returns: Nothing.
126+--------------------------------------------------------------------------*/
127uint32_t
128tsc_read_timer(void)
129{
130  register uint32_t         total;
131
132  total =  (uint32_t)(rdtsc() - Ttimer_val);
133
134  if (benchmark_timer_find_average_overhead)
135    return total;
136  else if (total < LEAST_VALID)
137    return 0; /* below timer resolution */
138  else
139    return (total - AVG_OVERHEAD);
140} /* tsc_read_timer */
141
142/*-------------------------------------------------------------------------+
143| Non-Pentium timer handling.
144+--------------------------------------------------------------------------*/
145#define US_PER_ISR   250  /* Number of micro-seconds per timer interruption */
146
147/*-------------------------------------------------------------------------+
148|         Function: Timer_exit
149|      Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is
150|                   not really necessary, since there will be a reset at exit.
151| Global Variables: None.
152|        Arguments: None.
153|          Returns: Nothing.
154+--------------------------------------------------------------------------*/
155static void
156timerOff(const rtems_raw_irq_connect_data* used)
157{
158    /*
159     * disable interrrupt at i8259 level
160     */
161     BSP_irq_disable_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
162     /* reset timer mode to standard (DOS) value */
163     outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
164     outport_byte(TIMER_CNTR0, 0);
165     outport_byte(TIMER_CNTR0, 0);
166} /* Timer_exit */
167
168static void
169timerOn(const rtems_raw_irq_connect_data* used)
170{
171     /* load timer for US_PER_ISR microsecond period */
172     outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
173     outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 0 & 0xff);
174     outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 8 & 0xff);
175    /*
176     * enable interrrupt at i8259 level
177     */
178     BSP_irq_enable_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
179}
180
181static int
182timerIsOn(const rtems_raw_irq_connect_data *used)
183{
184     return BSP_irq_enabled_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
185}
186
187static rtems_raw_irq_connect_data timer_raw_irq_data = {
188  BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE,
189  timerisr,
190  timerOn,
191  timerOff,
192  timerIsOn
193};
194
195/*-------------------------------------------------------------------------+
196|         Function: Timer_exit
197|      Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is
198|                   not really necessary, since there will be a reset at exit.
199| Global Variables: None.
200|        Arguments: None.
201|          Returns: Nothing.
202+--------------------------------------------------------------------------*/
203void
204i386_timer_exit(void)
205{
206  i386_delete_idt_entry (&timer_raw_irq_data);
207} /* Timer_exit */
208
209/*-------------------------------------------------------------------------+
210|         Function: benchmark_timer_initialize
211|      Description: Timer initialization routine.
212| Global Variables: Ttimer_val.
213|        Arguments: None.
214|          Returns: Nothing.
215+--------------------------------------------------------------------------*/
216void
217i386_timer_initialize(void)
218{
219  static bool First = true;
220
221  if (First)
222  {
223    First = false;
224
225    atexit(Timer_exit); /* Try not to hose the system at exit. */
226    if (!i386_set_idt_entry (&timer_raw_irq_data)) {
227      printk("raw handler connexion failed\n");
228      rtems_fatal_error_occurred(1);
229    }
230  }
231  /* wait for ISR to be called at least once */
232  Ttimer_val = 0;
233  while (Ttimer_val == 0)
234    continue;
235  Ttimer_val = 0;
236} /* benchmark_timer_initialize */
237
238/*-------------------------------------------------------------------------+
239|         Function: benchmark_timer_read
240|      Description: Read hardware timer value.
241| Global Variables: Ttimer_val, benchmark_timer_find_average_overhead.
242|        Arguments: None.
243|          Returns: Nothing.
244+--------------------------------------------------------------------------*/
245uint32_t
246i386_read_timer(void)
247{
248  register uint32_t         total, clicks;
249  register uint8_t          lsb, msb;
250
251  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);
252  inport_byte(TIMER_CNTR0, lsb);
253  inport_byte(TIMER_CNTR0, msb);
254  clicks = (msb << 8) | lsb;
255  total  = (Ttimer_val * US_PER_ISR) + (US_PER_ISR - TICK_TO_US(clicks));
256
257  if (benchmark_timer_find_average_overhead)
258    return total;
259  else if (total < LEAST_VALID)
260    return 0; /* below timer resolution */
261  else
262    return (total - AVG_OVERHEAD);
263}
264
265/*
266 * General timer functions using either TSC-based implementation
267 * or interrupt-based implementation
268 */
269
270void
271benchmark_timer_initialize(void)
272{
273    static bool First = true;
274
275    if (First) {
276        if (x86_has_tsc()) {
277#if defined(DEBUG)
278            printk("TSC: timer initialization\n");
279#endif /* DEBUG */
280            benchmark_timer_initialize_function = &tsc_timer_initialize;
281            benchmark_timer_read_function = &tsc_read_timer;
282            Timer_exit_function = &tsc_timer_exit;
283        }
284        else {
285#if defined(DEBUG)
286            printk("ISR: timer initialization\n");
287#endif /* DEBUG */
288            benchmark_timer_initialize_function = &i386_timer_initialize;
289            benchmark_timer_read_function = &i386_read_timer;
290            Timer_exit_function = &i386_timer_exit;
291        }
292        First = false;
293    }
294    (*benchmark_timer_initialize_function)();
295}
296
297uint32_t
298benchmark_timer_read(void)
299{
300    return (*benchmark_timer_read_function)();
301}
302
303void
304Timer_exit(void)
305{
306    return (*Timer_exit_function)();
307}
308
309/*-------------------------------------------------------------------------+
310|         Function: benchmark_timer_disable_subtracting_average_overhead
311|      Description: Set internal benchmark_timer_find_average_overhead flag value.
312| Global Variables: benchmark_timer_find_average_overhead.
313|        Arguments: find_flag - new value of the flag.
314|          Returns: Nothing.
315+--------------------------------------------------------------------------*/
316void
317benchmark_timer_disable_subtracting_average_overhead(bool find_flag)
318{
319  benchmark_timer_find_average_overhead = find_flag;
320} /* benchmark_timer_disable_subtracting_average_overhead */
321
322static unsigned short lastLoadedValue;
323
324/*-------------------------------------------------------------------------+
325| Description: Loads timer 0 with value passed as arguemnt.
326| Returns: Nothing. Loaded value must be a number of clock bits...
327+--------------------------------------------------------------------------*/
328void loadTimerValue( unsigned short loadedValue )
329{
330  lastLoadedValue = loadedValue;
331  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_SQWAVE);
332  outport_byte(TIMER_CNTR0, loadedValue & 0xff);
333  outport_byte(TIMER_CNTR0, (loadedValue >> 8) & 0xff);
334}
335
336/*-------------------------------------------------------------------------+
337| Description: Reads the current value of the timer, and converts the
338|                          number of ticks to micro-seconds.
339| Returns: number of clock bits elapsed since last load.
340+--------------------------------------------------------------------------*/
341unsigned int readTimer0(void)
342{
343  unsigned short lsb, msb;
344  unsigned char  status;
345  unsigned int   count;
346
347  outport_byte(TIMER_MODE, (TIMER_RD_BACK | (RB_COUNT_0 & ~(RB_NOT_STATUS | RB_NOT_COUNT))));
348  inport_byte(TIMER_CNTR0, status);
349  inport_byte(TIMER_CNTR0, lsb);
350  inport_byte(TIMER_CNTR0, msb);
351  count = ( msb << 8 ) | lsb ;
352  if (status & RB_OUTPUT )
353        count += lastLoadedValue;
354
355  return (2*lastLoadedValue - count);
356}
357
358void Timer0Reset(void)
359{
360  loadTimerValue(0xffff);
361  readTimer0();
362}
363
364void fastLoop (unsigned int loopCount)
365{
366  unsigned int i;
367  for( i=0; i < loopCount; i++ )outport_byte( SLOW_DOWN_IO, 0 );
368}
369
370void slowLoop (unsigned int loopCount)
371{
372  unsigned int j;
373  for (j=0; j <100 ;  j++) {
374    fastLoop (loopCount);
375  }
376}
377
378/*
379 * #define DEBUG_CALIBRATE
380 */
381void
382Calibrate_loop_1ms(void)
383{
384  unsigned int offset, offsetTmp, emptyCall, emptyCallTmp, res, i, j;
385  unsigned int targetClockBits, currentClockBits;
386  unsigned int slowLoopGranularity, fastLoopGranularity;
387  rtems_interrupt_level  level;
388
389#ifdef DEBUG_CALIBRATE
390  printk( "Calibrate_loop_1ms is starting,  please wait ( but not too loooong. )\n" );
391#endif
392  targetClockBits = US_TO_TICK(1000);
393
394  rtems_interrupt_disable(level);
395  /*
396   * Fill up the cache to get a correct offset
397   */
398  Timer0Reset();
399  readTimer0();
400  /*
401   * Compute the minimal offset to apply due to read counter register.
402   */
403  offset = 0xffffffff;
404  for (i=0; i <1000; i++) {
405    Timer0Reset();
406    offsetTmp = readTimer0();
407    offset += offsetTmp;
408  }
409  offset = offset / 1000; /* compute average */
410  /*
411   * calibrate empty call
412   */
413  fastLoop (0);
414  emptyCall = 0;
415  j = 0;
416  for (i=0; i <10; i++) {
417    Timer0Reset();
418    fastLoop (0);
419    res =  readTimer0();
420    /* res may be inferior to offset on fast
421     * machine because we took an average for offset
422     */
423    if (res >  offset) {
424      ++j;
425      emptyCallTmp = res - offset;
426      emptyCall += emptyCallTmp;
427    }
428  }
429  if (j == 0) emptyCall = 0;
430  else emptyCall = emptyCall / j; /* compute average */
431  /*
432   * calibrate fast loop
433   */
434  Timer0Reset();
435  fastLoop (10000);
436  res = readTimer0() - offset;
437  if (res < emptyCall) {
438     printk("Problem #1 in offset computation in Calibrate_loop_1ms in file libbsp/i386/pc386/timer/timer.c\n");
439    while (1);
440  }
441  fastLoopGranularity = (res - emptyCall) / 10000;
442  /*
443   * calibrate slow loop
444   */
445  Timer0Reset();
446  slowLoop(10);
447  res = readTimer0();
448  if (res < offset + emptyCall) {
449     printk("Problem #2 in offset computation in Calibrate_loop_1ms in file libbsp/i386/pc386/timer/timer.c\n");
450    while (1);
451  }
452  slowLoopGranularity = (res - offset - emptyCall)/ 10;
453
454  if (slowLoopGranularity == 0) {
455    printk("Problem #3 in Calibrate_loop_1ms in file libbsp/i386/pc386/timer/timer.c\n");
456    while (1);
457  }
458
459  targetClockBits += offset;
460#ifdef DEBUG_CALIBRATE
461  printk("offset = %u, emptyCall = %u, targetClockBits = %u\n",
462         offset, emptyCall, targetClockBits);
463  printk("slowLoopGranularity = %u fastLoopGranularity =  %u\n",
464         slowLoopGranularity, fastLoopGranularity);
465#endif
466  slowLoop1ms = (targetClockBits - emptyCall) / slowLoopGranularity;
467  if (slowLoop1ms != 0) {
468    fastLoop1ms = targetClockBits % slowLoopGranularity;
469    if (fastLoop1ms > emptyCall) fastLoop1ms -= emptyCall;
470  }
471  else
472    fastLoop1ms = targetClockBits - emptyCall / fastLoopGranularity;
473
474  if (slowLoop1ms != 0) {
475    /*
476     * calibrate slow loop
477     */
478
479    while(1)
480      {
481        int previousSign = 0; /* 0 = unset, 1 = incrementing,  2 = decrementing */
482        Timer0Reset();
483        slowLoop(slowLoop1ms);
484        currentClockBits = readTimer0();
485        if (currentClockBits > targetClockBits) {
486          if ((currentClockBits - targetClockBits) < slowLoopGranularity) {
487            /* decrement loop counter anyway to be sure slowLoop(slowLoop1ms) < targetClockBits */
488            --slowLoop1ms;
489            break;
490          }
491          else {
492            --slowLoop1ms;
493            if (slowLoop1ms == 0) break;
494            if (previousSign == 0) previousSign = 2;
495            if (previousSign == 1) break;
496          }
497        }
498        else {
499          if ((targetClockBits - currentClockBits) < slowLoopGranularity) {
500            break;
501          }
502          else {
503            ++slowLoop1ms;
504            if (previousSign == 0) previousSign = 1;
505            if (previousSign == 2) break;
506          }
507        }
508      }
509  }
510  /*
511   * calibrate fast loop
512   */
513
514  if (fastLoopGranularity != 0 ) {
515    while(1) {
516      int previousSign = 0; /* 0 = unset, 1 = incrementing,  2 = decrementing */
517      Timer0Reset();
518      if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
519      fastLoop(fastLoop1ms);
520      currentClockBits = readTimer0();
521      if (currentClockBits > targetClockBits) {
522        if ((currentClockBits - targetClockBits) < fastLoopGranularity)
523          break;
524        else {
525          --fastLoop1ms;
526          if (previousSign == 0) previousSign = 2;
527          if (previousSign == 1) break;
528        }
529      }
530      else {
531        if ((targetClockBits - currentClockBits) < fastLoopGranularity)
532          break;
533        else {
534          ++fastLoop1ms;
535          if (previousSign == 0) previousSign = 1;
536          if (previousSign == 2) break;
537        }
538      }
539    }
540  }
541#ifdef DEBUG_CALIBRATE
542  printk("slowLoop1ms = %u, fastLoop1ms = %u\n", slowLoop1ms, fastLoop1ms);
543#endif
544  rtems_interrupt_enable(level);
545
546}
547
548/*-------------------------------------------------------------------------+
549|         Function: Wait_X_1ms
550|      Description: loop which waits at least timeToWait ms
551| Global Variables: loop1ms
552|        Arguments: timeToWait
553|          Returns: Nothing.
554+--------------------------------------------------------------------------*/
555void
556Wait_X_ms( unsigned int timeToWait){
557
558  unsigned int j;
559
560  for (j=0; j<timeToWait ; j++) {
561    if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
562    fastLoop(fastLoop1ms);
563  }
564
565}
Note: See TracBrowser for help on using the repository browser.