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

Last change on this file since ba1a2ff6 was 6a1382a9, checked in by Joel Sherrill <joel.sherrill@…>, on 09/04/03 at 18:44:04

2003-09-04 Joel Sherrill <joel@…>

  • clock/ckinit.c, clock/rtc.c, console/console.c, console/inch.c, console/outch.c, ide/ide.c, include/bsp.h, include/crt.h, ne2000/ne2000.c, start/start.S, startup/bspstart.c, startup/exit.c, startup/ldsegs.S, startup/linkcmds, timer/timer.c, timer/timerisr.S: URL for license changed.
  • 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.rtems.com/license/LICENSE.
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.