2010-05-09 Joel Sherrill <joel.sherrill@oarcorp.com>

* timer/timer.c: Now runs on 486 and below again. Reformatted.
This commit is contained in:
Joel Sherrill
2010-05-10 00:29:30 +00:00
parent faeb54323b
commit 171f8346c6
2 changed files with 224 additions and 278 deletions

View File

@@ -1,3 +1,7 @@
2010-05-09 Joel Sherrill <joel.sherrill@oarcorp.com>
* timer/timer.c: Now runs on 486 and below again. Reformatted.
2010-04-30 Sebastian Huber <sebastian.huber@embedded-brains.de> 2010-04-30 Sebastian Huber <sebastian.huber@embedded-brains.de>
* Makefile.am, preinstall.am: Update for generic interrupt support * Makefile.am, preinstall.am: Update for generic interrupt support

View File

@@ -1,42 +1,31 @@
/*-------------------------------------------------------------------------+ /*
| timer.c v1.1 - PC386 BSP - 1997/08/07 * This file contains the PC386 timer package.
+--------------------------------------------------------------------------+ *
| This file contains the PC386 timer package. * Rosimildo daSilva -ConnectTel, Inc - Fixed infinite loop in the Calibration
+--------------------------------------------------------------------------+ * routine. I've seen this problems with faster machines ( pentiums ). Sometimes
| NOTE: It is important that the timer start/stop overhead be determined * RTEMS just hangs at startup.
| when porting or modifying this code. *
+--------------------------------------------------------------------------+ * Joel 9 May 2010: This is now seen sometimes on qemu.
| (C) Copyright 1997 - *
| - NavIST Group - Real-Time Distributed Systems and Industrial Automation * Modifications by:
| * (C) Copyright 1997 -
| http://pandora.ist.utl.pt * NavIST Group - Real-Time Distributed Systems and Industrial Automation
| * http://pandora.ist.utl.pt
| Instituto Superior Tecnico * Lisboa * PORTUGAL * Instituto Superior Tecnico * Lisboa * PORTUGAL
+--------------------------------------------------------------------------+ *
| Disclaimer: * This file is provided "AS IS" without warranty of any kind, either
| * expressed or implied.
| This file is provided "AS IS" without warranty of any kind, either *
| expressed or implied. * Based upon code by
+--------------------------------------------------------------------------+ * COPYRIGHT (c) 1989-1999.
| This code is base on: * On-Line Applications Research Corporation (OAR).
| timer.c,v 1.7 1995/12/19 20:07:43 joel Exp - go32 BSP *
| * The license and distribution terms for this file may be
| Rosimildo daSilva -ConnectTel, Inc - Fixed infinite loop in the Calibration * found in found in the file LICENSE in this distribution or at
| routine. I've seen this problems with faster machines ( pentiums ). Sometimes * http://www.rtems.com/license/LICENSE.
| RTEMS just hangs at startup. *
| * $Id$
| With the following copyright notice: */
| **************************************************************************
| * COPYRIGHT (c) 1989-1999.
| * On-Line Applications Research Corporation (OAR).
| *
| * The license and distribution terms for this file may be
| * found in found in the file LICENSE in this distribution or at
| * http://www.rtems.com/license/LICENSE.
| **************************************************************************
|
| $Id$
+--------------------------------------------------------------------------*/
#include <stdlib.h> #include <stdlib.h>
@@ -44,144 +33,115 @@
#include <bsp/irq.h> #include <bsp/irq.h>
#include <libcpu/cpuModel.h> #include <libcpu/cpuModel.h>
/*-------------------------------------------------------------------------+ /*
| Constants * Constants
+--------------------------------------------------------------------------*/ */
#define AVG_OVERHEAD 0 /* 0.1 microseconds to start/stop timer. */ #define AVG_OVERHEAD 0 /* 0.1 microseconds to start/stop timer. */
#define LEAST_VALID 1 /* Don't trust a value lower than this. */ #define LEAST_VALID 1 /* Don't trust a value lower than this. */
#define SLOW_DOWN_IO 0x80 /* io which does nothing */ #define SLOW_DOWN_IO 0x80 /* io which does nothing */
#define TWO_MS (uint32_t)(2000) /* TWO_MS = 2000us (sic!) */ #define TWO_MS (uint32_t)(2000) /* TWO_MS = 2000us (sic!) */
#define MSK_NULL_COUNT 0x40 /* bit counter available for reading */ #define MSK_NULL_COUNT 0x40 /* bit counter available for reading */
#define CMD_READ_BACK_STATUS 0xE2 /* command read back status */ #define CMD_READ_BACK_STATUS 0xE2 /* command read back status */
/*-------------------------------------------------------------------------+
| Global Variables /*
+--------------------------------------------------------------------------*/ * Global Variables
*/
volatile uint32_t Ttimer_val; volatile uint32_t Ttimer_val;
bool benchmark_timer_find_average_overhead = true; bool benchmark_timer_find_average_overhead = true;
volatile unsigned int fastLoop1ms, slowLoop1ms; volatile unsigned int fastLoop1ms, slowLoop1ms;
void (*benchmark_timer_initialize_function)(void) = 0; void (*benchmark_timer_initialize_function)(void) = 0;
uint32_t (*benchmark_timer_read_function)(void) = 0; uint32_t (*benchmark_timer_read_function)(void) = 0;
void (*Timer_exit_function)(void) = 0; void (*Timer_exit_function)(void) = 0;
/*-------------------------------------------------------------------------+ /* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */
| External Prototypes
+--------------------------------------------------------------------------*/
extern void timerisr(void); extern void timerisr(void);
/* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */
/*
* forward declarations
*/
void Timer_exit(void); void Timer_exit(void);
/*-------------------------------------------------------------------------+ /*
| Pentium optimized timer handling. * Pentium optimized timer handling.
+--------------------------------------------------------------------------*/ */
/*-------------------------------------------------------------------------+ /*
| Function: Timer_exit * Timer cleanup routine at RTEMS exit. NOTE: This routine is
| Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is * not really necessary, since there will be a reset at exit.
| not really necessary, since there will be a reset at exit. */
| Global Variables: None.
| Arguments: None. void tsc_timer_exit(void)
| Returns: Nothing.
+--------------------------------------------------------------------------*/
void
tsc_timer_exit(void)
{ {
} /* tsc_timer_exit */ }
/*-------------------------------------------------------------------------+ void tsc_timer_initialize(void)
| Function: benchmark_timer_initialize
| Description: Timer initialization routine.
| Global Variables: Ttimer_val.
| Arguments: None.
| Returns: Nothing.
+--------------------------------------------------------------------------*/
void
tsc_timer_initialize(void)
{ {
static bool First = true; static bool First = true;
if (First) if (First) {
{
First = false; First = false;
atexit(Timer_exit); /* Try not to hose the system at exit. */ atexit(Timer_exit); /* Try not to hose the system at exit. */
} }
Ttimer_val = rdtsc(); /* read starting time */ Ttimer_val = rdtsc(); /* read starting time */
} /* tsc_timer_initialize */ }
/*-------------------------------------------------------------------------+ /*
| Function: benchmark_timer_read *
| Description: Read hardware timer value. */
| Global Variables: Ttimer_val, benchmark_timer_find_average_overhead. uint32_t tsc_read_timer(void)
| Arguments: None.
| Returns: Nothing.
+--------------------------------------------------------------------------*/
uint32_t
tsc_read_timer(void)
{ {
register uint32_t total; register uint32_t total;
total = (uint32_t)(rdtsc() - Ttimer_val); total = (uint32_t)(rdtsc() - Ttimer_val);
if (benchmark_timer_find_average_overhead) if (benchmark_timer_find_average_overhead)
return total; return total;
else if (total < LEAST_VALID)
return 0; /* below timer resolution */
else
return (total - AVG_OVERHEAD);
} /* tsc_read_timer */
/*-------------------------------------------------------------------------+ if (total < LEAST_VALID)
| Non-Pentium timer handling. return 0; /* below timer resolution */
+--------------------------------------------------------------------------*/
#define US_PER_ISR 250 /* Number of micro-seconds per timer interruption */
/*-------------------------------------------------------------------------+ return (total - AVG_OVERHEAD);
| Function: Timer_exit
| Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is
| not really necessary, since there will be a reset at exit.
| Global Variables: None.
| Arguments: None.
| Returns: Nothing.
+--------------------------------------------------------------------------*/
static void
timerOff(const rtems_raw_irq_connect_data* used)
{
/*
* disable interrrupt at i8259 level
*/
BSP_irq_disable_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
/* reset timer mode to standard (DOS) value */
outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
outport_byte(TIMER_CNTR0, 0);
outport_byte(TIMER_CNTR0, 0);
} /* Timer_exit */
static void
timerOn(const rtems_raw_irq_connect_data* used)
{
/* load timer for US_PER_ISR microsecond period */
outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 0 & 0xff);
outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 8 & 0xff);
/*
* enable interrrupt at i8259 level
*/
BSP_irq_enable_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
} }
static int /*
timerIsOn(const rtems_raw_irq_connect_data *used) * Non-Pentium timer handling.
*/
#define US_PER_ISR 250 /* Number of micro-seconds per timer interruption */
/*
* Timer cleanup routine at RTEMS exit. NOTE: This routine is
* not really necessary, since there will be a reset at exit.
*/
static void timerOff(const rtems_raw_irq_connect_data* used)
{ {
return BSP_irq_enabled_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE); /*
* disable interrrupt at i8259 level
*/
BSP_irq_disable_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
/* reset timer mode to standard (DOS) value */
outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
outport_byte(TIMER_CNTR0, 0);
outport_byte(TIMER_CNTR0, 0);
}
static void timerOn(const rtems_raw_irq_connect_data* used)
{
/* load timer for US_PER_ISR microsecond period */
outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 0 & 0xff);
outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 8 & 0xff);
/*
* enable interrrupt at i8259 level
*/
BSP_irq_enable_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
}
static int timerIsOn(const rtems_raw_irq_connect_data *used)
{
return BSP_irq_enabled_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
} }
static rtems_raw_irq_connect_data timer_raw_irq_data = { static rtems_raw_irq_connect_data timer_raw_irq_data = {
@@ -192,39 +152,35 @@ static rtems_raw_irq_connect_data timer_raw_irq_data = {
timerIsOn timerIsOn
}; };
/*-------------------------------------------------------------------------+ /*
| Function: Timer_exit * Timer cleanup routine at RTEMS exit. NOTE: This routine is
| Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is * not really necessary, since there will be a reset at exit.
| not really necessary, since there will be a reset at exit. */ void
| Global Variables: None.
| Arguments: None.
| Returns: Nothing.
+--------------------------------------------------------------------------*/
void
i386_timer_exit(void) i386_timer_exit(void)
{ {
i386_delete_idt_entry (&timer_raw_irq_data); i386_delete_idt_entry (&timer_raw_irq_data);
} /* Timer_exit */ }
/*-------------------------------------------------------------------------+ extern void rtems_irq_prologue_0(void);
| Function: benchmark_timer_initialize void i386_timer_initialize(void)
| Description: Timer initialization routine.
| Global Variables: Ttimer_val.
| Arguments: None.
| Returns: Nothing.
+--------------------------------------------------------------------------*/
void
i386_timer_initialize(void)
{ {
static bool First = true; static bool First = true;
if (First) if (First) {
{ rtems_raw_irq_connect_data raw_irq_data = {
First = false; BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE,
rtems_irq_prologue_0,
NULL,
NULL,
NULL
};
atexit(Timer_exit); /* Try not to hose the system at exit. */ First = false;
i386_delete_idt_entry (&raw_irq_data);
atexit(Timer_exit); /* Try not to hose the system at exit. */
if (!i386_set_idt_entry (&timer_raw_irq_data)) { if (!i386_set_idt_entry (&timer_raw_irq_data)) {
printk("raw handler connexion failed\n"); printk("raw handler connection failed\n");
rtems_fatal_error_occurred(1); rtems_fatal_error_occurred(1);
} }
} }
@@ -233,17 +189,12 @@ i386_timer_initialize(void)
while (Ttimer_val == 0) while (Ttimer_val == 0)
continue; continue;
Ttimer_val = 0; Ttimer_val = 0;
} /* benchmark_timer_initialize */ }
/*-------------------------------------------------------------------------+ /*
| Function: benchmark_timer_read * Read hardware timer value.
| Description: Read hardware timer value. */
| Global Variables: Ttimer_val, benchmark_timer_find_average_overhead. uint32_t i386_read_timer(void)
| Arguments: None.
| Returns: Nothing.
+--------------------------------------------------------------------------*/
uint32_t
i386_read_timer(void)
{ {
register uint32_t total, clicks; register uint32_t total, clicks;
register uint8_t lsb, msb; register uint8_t lsb, msb;
@@ -256,10 +207,11 @@ i386_read_timer(void)
if (benchmark_timer_find_average_overhead) if (benchmark_timer_find_average_overhead)
return total; return total;
else if (total < LEAST_VALID)
return 0; /* below timer resolution */ if (total < LEAST_VALID)
else return 0; /* below timer resolution */
return (total - AVG_OVERHEAD);
return (total - AVG_OVERHEAD);
} }
/* /*
@@ -267,64 +219,56 @@ i386_read_timer(void)
* or interrupt-based implementation * or interrupt-based implementation
*/ */
void void benchmark_timer_initialize(void)
benchmark_timer_initialize(void)
{ {
static bool First = true; static bool First = true;
if (First) { if (First) {
if (x86_has_tsc()) { if (x86_has_tsc()) {
#if defined(DEBUG) #if defined(DEBUG)
printk("TSC: timer initialization\n"); printk("TSC: timer initialization\n");
#endif /* DEBUG */ #endif /* DEBUG */
benchmark_timer_initialize_function = &tsc_timer_initialize; benchmark_timer_initialize_function = &tsc_timer_initialize;
benchmark_timer_read_function = &tsc_read_timer; benchmark_timer_read_function = &tsc_read_timer;
Timer_exit_function = &tsc_timer_exit; Timer_exit_function = &tsc_timer_exit;
} } else {
else {
#if defined(DEBUG) #if defined(DEBUG)
printk("ISR: timer initialization\n"); printk("ISR: timer initialization\n");
#endif /* DEBUG */ #endif /* DEBUG */
benchmark_timer_initialize_function = &i386_timer_initialize; benchmark_timer_initialize_function = &i386_timer_initialize;
benchmark_timer_read_function = &i386_read_timer; benchmark_timer_read_function = &i386_read_timer;
Timer_exit_function = &i386_timer_exit; Timer_exit_function = &i386_timer_exit;
}
First = false;
} }
(*benchmark_timer_initialize_function)(); First = false;
}
(*benchmark_timer_initialize_function)();
} }
uint32_t uint32_t benchmark_timer_read(void)
benchmark_timer_read(void)
{ {
return (*benchmark_timer_read_function)(); return (*benchmark_timer_read_function)();
} }
void void Timer_exit(void)
Timer_exit(void)
{ {
return (*Timer_exit_function)(); return (*Timer_exit_function)();
} }
/*-------------------------------------------------------------------------+ /*
| Function: benchmark_timer_disable_subtracting_average_overhead * Set internal benchmark_timer_find_average_overhead flag value.
| Description: Set internal benchmark_timer_find_average_overhead flag value. */
| Global Variables: benchmark_timer_find_average_overhead. void benchmark_timer_disable_subtracting_average_overhead(bool find_flag)
| Arguments: find_flag - new value of the flag.
| Returns: Nothing.
+--------------------------------------------------------------------------*/
void
benchmark_timer_disable_subtracting_average_overhead(bool find_flag)
{ {
benchmark_timer_find_average_overhead = find_flag; benchmark_timer_find_average_overhead = find_flag;
} /* benchmark_timer_disable_subtracting_average_overhead */ }
static unsigned short lastLoadedValue; static unsigned short lastLoadedValue;
/*-------------------------------------------------------------------------+ /*
| Description: Loads timer 0 with value passed as arguemnt. * Loads timer 0 with value passed as arguemnt.
| Returns: Nothing. Loaded value must be a number of clock bits... *
+--------------------------------------------------------------------------*/ * Returns: Nothing. Loaded value must be a number of clock bits...
*/
void loadTimerValue( unsigned short loadedValue ) void loadTimerValue( unsigned short loadedValue )
{ {
lastLoadedValue = loadedValue; lastLoadedValue = loadedValue;
@@ -333,24 +277,28 @@ void loadTimerValue( unsigned short loadedValue )
outport_byte(TIMER_CNTR0, (loadedValue >> 8) & 0xff); outport_byte(TIMER_CNTR0, (loadedValue >> 8) & 0xff);
} }
/*-------------------------------------------------------------------------+ /*
| Description: Reads the current value of the timer, and converts the * Reads the current value of the timer, and converts the
| number of ticks to micro-seconds. * number of ticks to micro-seconds.
| Returns: number of clock bits elapsed since last load. *
+--------------------------------------------------------------------------*/ * Returns: number of clock bits elapsed since last load.
*/
unsigned int readTimer0(void) unsigned int readTimer0(void)
{ {
unsigned short lsb, msb; unsigned short lsb, msb;
unsigned char status; unsigned char status;
unsigned int count; unsigned int count;
outport_byte(TIMER_MODE, (TIMER_RD_BACK | (RB_COUNT_0 & ~(RB_NOT_STATUS | RB_NOT_COUNT)))); outport_byte(
TIMER_MODE,
(TIMER_RD_BACK | (RB_COUNT_0 & ~(RB_NOT_STATUS | RB_NOT_COUNT)))
);
inport_byte(TIMER_CNTR0, status); inport_byte(TIMER_CNTR0, status);
inport_byte(TIMER_CNTR0, lsb); inport_byte(TIMER_CNTR0, lsb);
inport_byte(TIMER_CNTR0, msb); inport_byte(TIMER_CNTR0, msb);
count = ( msb << 8 ) | lsb ; count = ( msb << 8 ) | lsb ;
if (status & RB_OUTPUT ) if (status & RB_OUTPUT )
count += lastLoadedValue; count += lastLoadedValue;
return (2*lastLoadedValue - count); return (2*lastLoadedValue - count);
} }
@@ -387,7 +335,7 @@ Calibrate_loop_1ms(void)
rtems_interrupt_level level; rtems_interrupt_level level;
#ifdef DEBUG_CALIBRATE #ifdef DEBUG_CALIBRATE
printk( "Calibrate_loop_1ms is starting, please wait ( but not too loooong. )\n" ); printk("Calibrate_loop_1ms is starting, please wait (but not too long.)\n");
#endif #endif
targetClockBits = US_TO_TICK(1000); targetClockBits = US_TO_TICK(1000);
@@ -459,9 +407,9 @@ Calibrate_loop_1ms(void)
targetClockBits += offset; targetClockBits += offset;
#ifdef DEBUG_CALIBRATE #ifdef DEBUG_CALIBRATE
printk("offset = %u, emptyCall = %u, targetClockBits = %u\n", printk("offset = %u, emptyCall = %u, targetClockBits = %u\n",
offset, emptyCall, targetClockBits); offset, emptyCall, targetClockBits);
printk("slowLoopGranularity = %u fastLoopGranularity = %u\n", printk("slowLoopGranularity = %u fastLoopGranularity = %u\n",
slowLoopGranularity, fastLoopGranularity); slowLoopGranularity, fastLoopGranularity);
#endif #endif
slowLoop1ms = (targetClockBits - emptyCall) / slowLoopGranularity; slowLoop1ms = (targetClockBits - emptyCall) / slowLoopGranularity;
if (slowLoop1ms != 0) { if (slowLoop1ms != 0) {
@@ -478,33 +426,33 @@ Calibrate_loop_1ms(void)
while(1) while(1)
{ {
int previousSign = 0; /* 0 = unset, 1 = incrementing, 2 = decrementing */ int previousSign = 0; /* 0 = unset, 1 = incrementing, 2 = decrementing */
Timer0Reset(); Timer0Reset();
slowLoop(slowLoop1ms); slowLoop(slowLoop1ms);
currentClockBits = readTimer0(); currentClockBits = readTimer0();
if (currentClockBits > targetClockBits) { if (currentClockBits > targetClockBits) {
if ((currentClockBits - targetClockBits) < slowLoopGranularity) { if ((currentClockBits - targetClockBits) < slowLoopGranularity) {
/* decrement loop counter anyway to be sure slowLoop(slowLoop1ms) < targetClockBits */ /* decrement loop counter anyway to be sure slowLoop(slowLoop1ms) < targetClockBits */
--slowLoop1ms; --slowLoop1ms;
break; break;
} }
else { else {
--slowLoop1ms; --slowLoop1ms;
if (slowLoop1ms == 0) break; if (slowLoop1ms == 0) break;
if (previousSign == 0) previousSign = 2; if (previousSign == 0) previousSign = 2;
if (previousSign == 1) break; if (previousSign == 1) break;
} }
} }
else { else {
if ((targetClockBits - currentClockBits) < slowLoopGranularity) { if ((targetClockBits - currentClockBits) < slowLoopGranularity) {
break; break;
} }
else { else {
++slowLoop1ms; ++slowLoop1ms;
if (previousSign == 0) previousSign = 1; if (previousSign == 0) previousSign = 1;
if (previousSign == 2) break; if (previousSign == 2) break;
} }
} }
} }
} }
/* /*
@@ -519,22 +467,22 @@ Calibrate_loop_1ms(void)
fastLoop(fastLoop1ms); fastLoop(fastLoop1ms);
currentClockBits = readTimer0(); currentClockBits = readTimer0();
if (currentClockBits > targetClockBits) { if (currentClockBits > targetClockBits) {
if ((currentClockBits - targetClockBits) < fastLoopGranularity) if ((currentClockBits - targetClockBits) < fastLoopGranularity)
break; break;
else { else {
--fastLoop1ms; --fastLoop1ms;
if (previousSign == 0) previousSign = 2; if (previousSign == 0) previousSign = 2;
if (previousSign == 1) break; if (previousSign == 1) break;
} }
} }
else { else {
if ((targetClockBits - currentClockBits) < fastLoopGranularity) if ((targetClockBits - currentClockBits) < fastLoopGranularity)
break; break;
else { else {
++fastLoop1ms; ++fastLoop1ms;
if (previousSign == 0) previousSign = 1; if (previousSign == 0) previousSign = 1;
if (previousSign == 2) break; if (previousSign == 2) break;
} }
} }
} }
} }
@@ -545,21 +493,15 @@ Calibrate_loop_1ms(void)
} }
/*-------------------------------------------------------------------------+ /*
| Function: Wait_X_1ms * loop which waits at least timeToWait ms
| Description: loop which waits at least timeToWait ms */
| Global Variables: loop1ms void Wait_X_ms( unsigned int timeToWait)
| Arguments: timeToWait {
| Returns: Nothing.
+--------------------------------------------------------------------------*/
void
Wait_X_ms( unsigned int timeToWait){
unsigned int j; unsigned int j;
for (j=0; j<timeToWait ; j++) { for (j=0; j<timeToWait ; j++) {
if (slowLoop1ms != 0) slowLoop(slowLoop1ms); if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
fastLoop(fastLoop1ms); fastLoop(fastLoop1ms);
} }
} }