Changeset dcbc0a89 in rtems


Ignore:
Timestamp:
Dec 3, 2008, 5:28:30 PM (11 years ago)
Author:
Joel Sherrill <joel.sherrill@…>
Branches:
4.9
Children:
c6016f1
Parents:
5fe13356
Message:

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.
Location:
c/src/lib/libbsp/i386/pc386
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • c/src/lib/libbsp/i386/pc386/ChangeLog

    r5fe13356 rdcbc0a89  
     12008-12-03      Joel Sherrill <joel.sherrill@OARcorp.com>
     2                Michael South <msouth@msouth.org>
     3
     4        PR 1344/bsps
     5        * configure.ac, clock/ckinit.c, timer/timer.c: Add use of TSC for
     6        nanoseconds granularity. i8254 is very slow on some systems. TSC use
     7        is auto-detected by default.
     8
    192008-09-06      Ralf Corsépius <ralf.corsepius@rtems.org>
    210
  • c/src/lib/libbsp/i386/pc386/clock/ckinit.c

    r5fe13356 rdcbc0a89  
    2525#include <bsp/irq.h>
    2626#include <bspopts.h>
     27#include <libcpu/cpuModel.h>
    2728
    2829#define CLOCK_VECTOR 0
     
    3233uint32_t pc386_clock_click_count;
    3334
     35/*
     36 * Roughly the number of cycles per tick and per nanosecond. Note that these
     37 * will be wildly inaccurate if the chip speed changes due to power saving
     38 * or thermal modes.
     39 *
     40 * NOTE: These are only used when the TSC method is used.
     41 */
     42uint64_t pc586_tsc_per_tick;
     43uint64_t pc586_nanoseconds_per_tick;
     44
     45uint64_t pc586_tsc_at_tick;
    3446
    3547/* this driver may need to count ISRs per tick */
    36 
    3748#define CLOCK_DRIVER_ISRS_PER_TICK pc386_isrs_per_tick
    3849
    39 #define Clock_driver_support_at_tick()
     50#define READ_8254( _lsb, _msb )                               \
     51  do { outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);      \
     52     inport_byte(TIMER_CNTR0, _lsb);                          \
     53     inport_byte(TIMER_CNTR0, _msb);                          \
     54  } while (0)
     55
     56
     57/*
     58 *  Hooks which get swapped based upon which nanoseconds since last
     59 *  tick method is preferred.
     60 */
     61void     (*Clock_driver_support_at_tick)(void) = NULL;
     62uint32_t (*Clock_driver_nanoseconds_since_last_tick)(void) = NULL;
     63
     64/*
     65 *  What do we do at each clock tick?
     66 */
     67void Clock_driver_support_at_tick_tsc(void)
     68{
     69  pc586_tsc_at_tick = rdtsc();
     70}
     71
     72void Clock_driver_support_at_tick_empty(void)
     73{
     74}
    4075
    4176#define Clock_driver_support_install_isr( _new, _old ) \
     
    4580extern volatile uint32_t Clock_driver_isrs;
    4681
    47 uint32_t bsp_clock_nanoseconds_since_last_tick(void)
    48 {
     82uint32_t bsp_clock_nanoseconds_since_last_tick_tsc(void)
     83{
     84  /******
     85   * Get nanoseconds using Pentium-compatible TSC register
     86   ******/
     87 
     88  uint64_t                 diff_nsec;
     89
     90  diff_nsec = rdtsc() - pc586_tsc_at_tick;
     91
     92  /*
     93   * At this point, with a hypothetical 10 GHz CPU clock and 100 Hz tick
     94   * clock, diff_nsec <= 27 bits.
     95   */
     96  diff_nsec *= pc586_nanoseconds_per_tick; /* <= 54 bits */
     97  diff_nsec /= pc586_tsc_per_tick;
     98
     99  if (diff_nsec > pc586_nanoseconds_per_tick)
     100    /*
     101     * Hmmm... Some drift or rounding. Pin the value to 1 nanosecond before
     102     * the next tick.
     103     */
     104    /*    diff_nsec = pc586_nanoseconds_per_tick - 1; */
     105    diff_nsec = 12345;
     106
     107  return (uint32_t)diff_nsec;
     108}
     109 
     110uint32_t bsp_clock_nanoseconds_since_last_tick_i8254(void)
     111{
     112
     113  /******
     114   * Get nanoseconds using 8254 timer chip
     115   ******/
     116
    49117  uint32_t                 usecs, clicks, isrs;
    50118  uint32_t                 usecs1, usecs2;
     
    56124   */
    57125  rtems_interrupt_disable(level);
    58     outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);
    59     inport_byte(TIMER_CNTR0, lsb);
    60     inport_byte(TIMER_CNTR0, msb);
     126    READ_8254(lsb, msb);
    61127    isrs = Clock_driver_isrs;
    62128  rtems_interrupt_enable(level);
     
    86152  /* return it in nanoseconds */
    87153  return usecs * 1000;
    88 }
    89 
    90 #define Clock_driver_nanoseconds_since_last_tick \
    91   bsp_clock_nanoseconds_since_last_tick
     154
     155}
     156
     157/*
     158 * Calibrate CPU cycles per tick. Interrupts should be disabled.
     159 */
     160static void calibrate_tsc(void)
     161{
     162  uint64_t              begin_time;
     163  uint8_t               then_lsb, then_msb, now_lsb, now_msb;
     164  uint32_t              i;
     165
     166  pc586_nanoseconds_per_tick =
     167    rtems_configuration_get_microseconds_per_tick() * 1000;
     168
     169  /*
     170   * We just reset the timer, so we know we're at the beginning of a tick.
     171   */
     172
     173  /*
     174   * Count cycles. Watching the timer introduces a several microsecond
     175   * uncertaintity, so let it cook for a while and divide by the number of
     176   * ticks actually executed.
     177   */
     178
     179  begin_time = rdtsc();
     180
     181  for (i = rtems_clock_get_ticks_per_second() * pc386_isrs_per_tick;
     182       i != 0; --i ) {   
     183    /* We know we've just completed a tick when timer goes from low to high */
     184    then_lsb = then_msb = 0xff;
     185    do {
     186      READ_8254(now_lsb, now_msb);
     187      if ((then_msb < now_msb) ||
     188          ((then_msb == now_msb) && (then_lsb < now_lsb)))
     189        break;
     190      then_lsb = now_lsb;
     191      then_msb = now_msb;
     192    } while (1);
     193  }
     194
     195  pc586_tsc_per_tick = rdtsc() - begin_time;
     196
     197  /* Initialize "previous tick" counters */
     198  pc586_tsc_at_tick = rdtsc();
     199
     200#if 1
     201  printk( "CPU clock at %u MHz\n", (uint32_t)(pc586_tsc_per_tick / 1000000));
     202#endif
     203 
     204  pc586_tsc_per_tick /= rtems_clock_get_ticks_per_second();
     205}
    92206
    93207static void clockOn(
     
    115229  outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 0 & 0xff);
    116230  outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 8 & 0xff);
     231
     232  /*
     233   * Now calibrate cycles per tick. Do this every time we
     234   * turn the clock on in case the CPU clock speed has changed.
     235   */
     236  if ( x86_has_tsc() )
     237    calibrate_tsc();
    117238}
    118239
     
    143264};
    144265
    145 #define Clock_driver_support_initialize_hardware() \
    146   do { \
    147     if (!BSP_install_rtems_irq_handler (&clockIrqData)) { \
    148       printk("Unable to initialize system clock\n"); \
    149       rtems_fatal_error_occurred(1); \
    150     } \
    151   } while (0)
     266void Clock_driver_support_initialize_hardware()
     267{
     268  bool use_tsc = false;
     269  bool use_8254 = false;
     270 
     271  #if (CLOCK_DRIVER_USE_TSC == 1)
     272    use_tsc = true;
     273  #endif
     274
     275  #if (CLOCK_DRIVER_USE_8254 == 1)
     276    use_8254 = true;
     277  #endif
     278 
     279  if ( !use_tsc && !use_8254 ) {
     280    if ( x86_has_tsc() ) use_tsc  = true;
     281    else                 use_8254 = true;
     282  }
     283
     284  if ( use_8254 ) {
     285    printk( "Use 8254\n" );
     286    Clock_driver_support_at_tick = Clock_driver_support_at_tick_empty;
     287    bsp_clock_nanoseconds_since_last_tick =
     288      bsp_clock_nanoseconds_since_last_tick_tsc;
     289
     290  } else {
     291    printk( "Use TSC\n" );
     292    Clock_driver_support_at_tick = Clock_driver_support_at_tick_tsc;
     293    bsp_clock_nanoseconds_since_last_tick =
     294      bsp_clock_nanoseconds_since_last_tick_i88254;
     295  }
     296
     297  if (!BSP_install_rtems_irq_handler (&clockIrqData)) {
     298    printk("Unable to initialize system clock\n");
     299    rtems_fatal_error_occurred(1);
     300  }
     301}
    152302
    153303#define Clock_driver_support_shutdown_hardware() \
  • c/src/lib/libbsp/i386/pc386/configure.ac

    r5fe13356 rdcbc0a89  
    4747])
    4848
     49RTEMS_BSPOPTS_SET([CLOCK_DRIVER_USE_TSC],[*],[0])
     50RTEMS_BSPOPTS_HELP([CLOCK_DRIVER_USE_TSC],
     51[If enabled, the clock driver will use the TSC register available
     52 with Pentium-class CPUs to report close to nanosecond-accuracy
     53 clock times.
     54 Enable it, if:
     55 - you have nanosecond timing enabled (you do NOT have
     56   USE_TICKS_FOR_CPU_USAGE_STATISTICS enabled)
     57 - you do NOT have CLOCK_DRIVER_USE_8254 enabled (use one, the other,
     58   or neither)
     59 - you have a Pentium which supports TSC (all Intels, and probably
     60   all or most clones)
     61 - you do not have a variable-speed CPU clock. Note that some
     62   motherboard BIOS will automatically vary clock speed for thermal
     63   control. Note also, however, that really new Pentium-class chips
     64   from Intel and AMD will maintain a constant-rate TSC regardless.
     65])
     66
     67RTEMS_BSPOPTS_SET([CLOCK_DRIVER_USE_8254],[*],[0])
     68RTEMS_BSPOPTS_HELP([CLOCK_DRIVER_USE_8254],
     69[If enabled, the clock driver will use the good old 8254 chip
     70 to report microsecond-accuracy clock times.
     71 Enable it, if:
     72 - you have nanosecond timing enabled (you do NOT have
     73   USE_TICKS_FOR_CPU_USAGE_STATISTICS enabled)
     74 - you do NOT have CLOCK_DRIVER_USE_TSC enabled (use one, the other,
     75   or neither)
     76 - you do not mind adding roughly 5 microseconds to each context switch.
     77])
     78
     79if test X${CLOCK_DRIVER_USE_TSC} = X1 -a X${CLOCK_DRIVER_USE_8254} = X1 ; then
     80  AC_MSG_ERROR([pc386 both TSC and 8254 specified for clock driver])
     81fi
     82
     83#define CLOCK_DRIVER_USE_8254 $CLOCK_DRIVER_USE_8254
    4984## if this is an i386, does gas have good code16 support?
    5085RTEMS_I386_GAS_CODE16
  • c/src/lib/libbsp/i386/pc386/timer/timer.c

    r5fe13356 rdcbc0a89  
    4343#include <bsp.h>
    4444#include <bsp/irq.h>
     45#include <libcpu/cpuModel.h>
    4546
    4647/*-------------------------------------------------------------------------+
     
    6061+--------------------------------------------------------------------------*/
    6162volatile uint32_t         Ttimer_val;
    62 bool              benchmark_timer_find_average_overhead = true;
     63bool                      benchmark_timer_find_average_overhead = true;
    6364volatile unsigned int     fastLoop1ms, slowLoop1ms;
    64 void (*benchmark_timer_initialize_function)(void) = 0;
     65
     66void     (*benchmark_timer_initialize_function)(void) = 0;
    6567uint32_t (*benchmark_timer_read_function)(void) = 0;
    66 void (*Timer_exit_function)(void) = 0;
     68void     (*Timer_exit_function)(void) = 0;
    6769
    6870/*-------------------------------------------------------------------------+
     
    7173extern void timerisr(void);
    7274       /* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */
    73 extern int x86_capability;
    7475
    7576/*
     
    8283| Pentium optimized timer handling.
    8384+--------------------------------------------------------------------------*/
    84 
    85 /*-------------------------------------------------------------------------+
    86 |         Function: rdtsc
    87 |      Description: Read the value of PENTIUM on-chip cycle counter.
    88 | Global Variables: None.
    89 |        Arguments: None.
    90 |          Returns: Value of PENTIUM on-chip cycle counter.
    91 +--------------------------------------------------------------------------*/
    92 static inline unsigned long long
    93 rdtsc(void)
    94 {
    95   /* Return the value of the on-chip cycle counter. */
    96   unsigned long long result;
    97   asm volatile(".byte 0x0F, 0x31" : "=A" (result));
    98   return result;
    99 } /* rdtsc */
    10085
    10186/*-------------------------------------------------------------------------+
     
    289274
    290275    if (First) {
    291         if (x86_capability & (1 << 4) ) {
     276        if (x86_has_tsc()) {
    292277#if defined(DEBUG)
    293278            printk("TSC: timer initialization\n");
Note: See TracChangeset for help on using the changeset viewer.