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

4.104.114.84.95
Last change on this file since 6ff7e2c was d57c04e, checked in by Joel Sherrill <joel.sherrill@…>, on 12/05/00 at 16:49:23

2000-12-05 Eric Valette <valette@…>

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