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

4.115
Last change on this file since df40cc9 was c499856, checked in by Chris Johns <chrisj@…>, on 03/20/14 at 21:10:47

Change all references of rtems.com to rtems.org.

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