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

4.115
Last change on this file since 441b90e was 13e4ab64, checked in by Joel Sherrill <joel.sherrill@…>, on 03/04/11 at 21:56:45

2011-03-04 Joel Sherrill <joel.sherrilL@…>

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