aboutsummaryrefslogtreecommitdiffstats
path: root/tools/ioemu/iodev/virt_timer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ioemu/iodev/virt_timer.cc')
-rw-r--r--tools/ioemu/iodev/virt_timer.cc552
1 files changed, 552 insertions, 0 deletions
diff --git a/tools/ioemu/iodev/virt_timer.cc b/tools/ioemu/iodev/virt_timer.cc
new file mode 100644
index 0000000000..eb108a025b
--- /dev/null
+++ b/tools/ioemu/iodev/virt_timer.cc
@@ -0,0 +1,552 @@
+////////////////////////////////////////////////////////////////////////
+// $Id: virt_timer.cc,v 1.19.2.1 2004/02/06 22:14:36 danielg4 Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+/////////////////////////////////////////////////////////////////////////
+//
+//Realtime Algorithm (with gettimeofday)
+// HAVE:
+// Real number of usec.
+// Emulated number of usec.
+// WANT:
+// Number of ticks to use.
+// Number of emulated usec to wait until next try.
+//
+// ticks=number of ticks needed to match total real usec.
+// if(desired ticks > max ticks for elapsed real time)
+// ticks = max ticks for elapsed real time.
+// if(desired ticks > max ticks for elapsed emulated usec)
+// ticks = max ticks for emulated usec.
+// next wait ticks = number of ticks until next event.
+// next wait real usec = (current ticks + next wait ticks) * usec per ticks
+// next wait emulated usec = next wait real usec * emulated usec / real usec
+// if(next wait emulated usec < minimum emulated usec for next wait ticks)
+// next wait emulated usec = minimum emulated usec for next wait ticks.
+// if(next wait emulated usec > max emulated usec wait)
+// next wait emulated usec = max emulated usec wait.
+//
+// How to calculate elapsed real time:
+// store an unused time value whenever no ticks are used in a given time.
+// add this to the current elapsed time.
+// How to calculate elapsed emulated time:
+// same as above.
+// Above can be done by not updating last_usec and last_sec.
+//
+// How to calculate emulated usec/real usec:
+// Each time there are actual ticks:
+// Alpha_product(old emulated usec, emulated usec);
+// Alpha_product(old real usec, real usec);
+// Divide resulting values.
+//
+/////////////////////////////////////////////////////////////////////////
+
+#include "bochs.h"
+
+#define BX_USE_VIRTUAL_TIMERS 1
+#define BX_VIRTUAL_TIMERS_REALTIME 1
+
+//Important constant #defines:
+#define USEC_PER_SECOND (1000000)
+
+
+// define a macro to convert floating point numbers into 64-bit integers.
+// In MSVC++ you can convert a 64-bit float into a 64-bit signed integer,
+// but it will not convert a 64-bit float into a 64-bit unsigned integer.
+// This macro works around that.
+#define F2I(x) ((Bit64u)(Bit64s) (x))
+#define I2F(x) ((double)(Bit64s) (x))
+
+//CONFIGURATION #defines:
+
+
+//MAINLINE Configuration (For realtime PIT):
+
+//How much faster than real time we can go:
+#define MAX_MULT (1.25)
+
+//Minimum number of emulated useconds per second.
+// Now calculated using BX_MIN_IPS, the minimum number of
+// instructions per second.
+#define MIN_USEC_PER_SECOND (((((Bit64u)USEC_PER_SECOND)*((Bit64u)BX_MIN_IPS))/((Bit64u)(bx_options.Oips->get())))+(Bit64u)1)
+
+
+//DEBUG configuration:
+
+//Debug with printf options.
+#define DEBUG_REALTIME_WITH_PRINTF 0
+
+//Use to test execution at multiples of real time.
+#define TIME_DIVIDER (1)
+#define TIME_MULTIPLIER (1)
+#define TIME_HEADSTART (0)
+
+
+#define GET_VIRT_REALTIME64_USEC() (((bx_get_realtime64_usec()*(Bit64u)TIME_MULTIPLIER/(Bit64u)TIME_DIVIDER)))
+//Set up Logging.
+#define LOG_THIS bx_virt_timer.
+
+//A single instance.
+bx_virt_timer_c bx_virt_timer;
+
+
+//Generic MAX and MIN Functions
+#define BX_MAX(a,b) ( ((a)>(b))?(a):(b) )
+#define BX_MIN(a,b) ( ((a)>(b))?(b):(a) )
+
+
+//USEC_ALPHA is multiplier for the past.
+//USEC_ALPHA_B is 1-USEC_ALPHA, or multiplier for the present.
+#define USEC_ALPHA ((double)(.8))
+#define USEC_ALPHA_B ((double)(((double)1)-USEC_ALPHA))
+#define USEC_ALPHA2 ((double)(.5))
+#define USEC_ALPHA2_B ((double)(((double)1)-USEC_ALPHA2))
+#define ALPHA_LOWER(old,new) ((Bit64u)((old<new)?((USEC_ALPHA*(I2F(old)))+(USEC_ALPHA_B*(I2F(new)))):((USEC_ALPHA2*(I2F(old)))+(USEC_ALPHA2_B*(I2F(new))))))
+
+
+//Conversion between emulated useconds and optionally realtime ticks.
+#define TICKS_TO_USEC(a) ( ((a)*usec_per_second)/ticks_per_second )
+#define USEC_TO_TICKS(a) ( ((a)*ticks_per_second)/usec_per_second )
+
+bx_virt_timer_c::bx_virt_timer_c( void )
+{
+ put("VTIMER");
+ settype(VTIMERLOG);
+
+ numTimers = 0;
+ current_timers_time = 0;
+ timers_next_event_time = BX_MAX_VIRTUAL_TIME;
+ last_sequential_time = 0;
+ in_timer_handler = 0;
+ virtual_next_event_time = BX_MAX_VIRTUAL_TIME;
+ current_virtual_time = 0;
+
+ use_virtual_timers = BX_USE_VIRTUAL_TIMERS;
+ init_done = 0;
+}
+
+bx_virt_timer_c::~bx_virt_timer_c( void )
+{
+}
+
+
+
+const Bit64u bx_virt_timer_c::NullTimerInterval = BX_MAX_VIRTUAL_TIME;
+
+void
+bx_virt_timer_c::nullTimer(void* this_ptr) {
+ UNUSED(this_ptr);
+}
+
+void
+bx_virt_timer_c::periodic(Bit64u time_passed) {
+ //Assert that we haven't skipped any events.
+ BX_ASSERT (time_passed <= timers_next_event_time);
+ BX_ASSERT(!in_timer_handler);
+
+ //Update time variables.
+ timers_next_event_time -= time_passed;
+ current_timers_time += time_passed;
+
+ //If no events are occurring, just pass the time and we're done.
+ if( time_passed < timers_next_event_time ) {
+ return;
+ }
+ //Starting timer handler calls.
+ in_timer_handler = 1;
+ //Otherwise, cause any events to occur that should.
+ unsigned i;
+ for(i=0;i<numTimers;i++) {
+ if( timer[i].inUse && timer[i].active ) {
+ //Assert that we haven't skipped any timers.
+ BX_ASSERT(current_timers_time <= timer[i].timeToFire);
+ if(timer[i].timeToFire == current_timers_time) {
+ if(timer[i].continuous) {
+ timer[i].timeToFire+=timer[i].period;
+ } else {
+ timer[i].active = 0;
+ }
+ //This function MUST return, or the timer mechanism
+ // will be broken.
+ timer[i].funct(timer[i].this_ptr);
+ }
+ }
+ }
+ //Finished timer handler calls.
+ in_timer_handler = 0;
+ //Use a second FOR loop so that a timer function call can
+ // change the behavior of another timer.
+ //timers_next_event_time normally contains a cycle count, not a cycle time.
+ // here we use it as a temporary variable that IS a cycle time,
+ // but then convert it back to a cycle count afterwards.
+ timers_next_event_time = current_timers_time + BX_MAX_VIRTUAL_TIME;
+ for(i=0;i<numTimers;i++) {
+ if( timer[i].inUse && timer[i].active && ((timer[i].timeToFire)<timers_next_event_time) ) {
+ timers_next_event_time = timer[i].timeToFire;
+ }
+ }
+ timers_next_event_time-=current_timers_time;
+ next_event_time_update();
+ //FIXME
+}
+
+
+//Get the current virtual time.
+// This may return the same value on subsequent calls.
+Bit64u
+bx_virt_timer_c::time_usec(void) {
+ if(!use_virtual_timers) {
+ return bx_pc_system.time_usec();
+ }
+
+ //Update the time here only if we're not in a timer handler.
+ //If we're in a timer handler we're up-to-date, and otherwise
+ // this prevents call stack loops.
+ if(!in_timer_handler) {
+ timer_handler();
+ }
+
+ return current_timers_time;
+}
+
+//Get the current virtual time.
+// This will return a monotonically increasing value.
+// MUST NOT be called from within a timer interrupt.
+Bit64u
+bx_virt_timer_c::time_usec_sequential(void) {
+ if(!use_virtual_timers) {
+ return bx_pc_system.time_usec_sequential();
+ }
+
+ //Can't prevent call stack loops here, so this
+ // MUST NOT be called from within a timer handler.
+ BX_ASSERT(timers_next_event_time>0);
+ BX_ASSERT(!in_timer_handler);
+
+ if(last_sequential_time >= current_timers_time) {
+ periodic(1);
+ last_sequential_time = current_timers_time;
+ }
+ return current_timers_time;
+}
+
+
+
+//Register a timer handler to go off after a given interval.
+//Register a timer handler to go off with a periodic interval.
+int
+bx_virt_timer_c::register_timer( void *this_ptr, bx_timer_handler_t handler,
+ Bit32u useconds,
+ bx_bool continuous, bx_bool active,
+ const char *id) {
+ if(!use_virtual_timers) {
+ return bx_pc_system.register_timer(this_ptr, handler, useconds,
+ continuous, active, id);
+ }
+
+ //We don't like starting with a zero period timer.
+ BX_ASSERT((!active) || (useconds>0));
+
+ //Search for an unused timer.
+ unsigned int i;
+ for (i=0; i < numTimers; i++) {
+ if (timer[i].inUse == 0 || i==numTimers)
+ break;
+ }
+ // If we didn't find a free slot, increment the bound, numTimers.
+ if (i==numTimers)
+ numTimers++; // One new timer installed.
+ BX_ASSERT(numTimers<BX_MAX_VIRTUAL_TIMERS);
+
+ timer[i].inUse = 1;
+ timer[i].period = useconds;
+ timer[i].timeToFire = current_timers_time + (Bit64u)useconds;
+ timer[i].active = active;
+ timer[i].continuous = continuous;
+ timer[i].funct = handler;
+ timer[i].this_ptr = this_ptr;
+ strncpy(timer[i].id, id, BxMaxTimerIDLen);
+ timer[i].id[BxMaxTimerIDLen-1]=0; //I like null terminated strings.
+
+ if(useconds < timers_next_event_time) {
+ timers_next_event_time = useconds;
+ next_event_time_update();
+ //FIXME
+ }
+ return i;
+}
+
+//unregister a previously registered timer.
+unsigned
+bx_virt_timer_c::unregisterTimer(int timerID) {
+ if(!use_virtual_timers) {
+ return bx_pc_system.unregisterTimer(timerID);
+ }
+
+ BX_ASSERT(timerID >= 0);
+ BX_ASSERT(timerID < BX_MAX_VIRTUAL_TIMERS);
+
+ if (timer[timerID].active) {
+ BX_PANIC(("unregisterTimer: timer '%s' is still active!", timer[timerID].id));
+ return(0); // Fail.
+ }
+
+
+ //No need to prevent doing this to unused timers.
+ timer[timerID].inUse = 0;
+ return(1);
+}
+
+void
+bx_virt_timer_c::start_timers(void) {
+ if(!use_virtual_timers) {
+ bx_pc_system.start_timers();
+ return;
+ }
+ //FIXME
+}
+
+//activate a deactivated but registered timer.
+void
+bx_virt_timer_c::activate_timer( unsigned timer_index, Bit32u useconds,
+ bx_bool continuous ) {
+ if(!use_virtual_timers) {
+ bx_pc_system.activate_timer(timer_index, useconds, continuous);
+ return;
+ }
+
+ BX_ASSERT(timer_index >= 0);
+ BX_ASSERT(timer_index < BX_MAX_VIRTUAL_TIMERS);
+
+ BX_ASSERT(timer[timer_index].inUse);
+ BX_ASSERT(useconds>0);
+
+ timer[timer_index].period=useconds;
+ timer[timer_index].timeToFire = current_timers_time + (Bit64u)useconds;
+ timer[timer_index].active=1;
+ timer[timer_index].continuous=continuous;
+
+ if(useconds < timers_next_event_time) {
+ timers_next_event_time = useconds;
+ next_event_time_update();
+ //FIXME
+ }
+}
+
+//deactivate (but don't unregister) a currently registered timer.
+void
+bx_virt_timer_c::deactivate_timer( unsigned timer_index ) {
+ if(!use_virtual_timers) {
+ bx_pc_system.deactivate_timer(timer_index);
+ return;
+ }
+
+ BX_ASSERT(timer_index >= 0);
+ BX_ASSERT(timer_index < BX_MAX_VIRTUAL_TIMERS);
+
+ //No need to prevent doing this to unused/inactive timers.
+ timer[timer_index].active = 0;
+}
+
+void
+bx_virt_timer_c::advance_virtual_time(Bit64u time_passed) {
+ BX_ASSERT(time_passed <= virtual_next_event_time);
+
+ current_virtual_time += time_passed;
+ virtual_next_event_time -= time_passed;
+
+ if(current_virtual_time > current_timers_time) {
+ periodic(current_virtual_time - current_timers_time);
+ }
+}
+
+//Called when next_event_time changes.
+void
+bx_virt_timer_c::next_event_time_update(void) {
+ virtual_next_event_time = timers_next_event_time + current_timers_time - current_virtual_time;
+ if(init_done) {
+ bx_pc_system.deactivate_timer(system_timer_id);
+ BX_ASSERT(virtual_next_event_time);
+ bx_pc_system.activate_timer(system_timer_id,
+ (Bit32u)BX_MIN(0x7FFFFFFF,BX_MAX(1,TICKS_TO_USEC(virtual_next_event_time))),
+ 0);
+ }
+}
+
+void
+bx_virt_timer_c::init(void) {
+
+ if ( (bx_options.clock.Osync->get ()!=BX_CLOCK_SYNC_REALTIME)
+ && (bx_options.clock.Osync->get ()!=BX_CLOCK_SYNC_BOTH) )
+ virtual_timers_realtime = 0;
+ else
+ virtual_timers_realtime = 1;
+
+ if (virtual_timers_realtime) {
+ BX_INFO(("using 'realtime pit' synchronization method"));
+ }
+
+ register_timer(this, nullTimer, (Bit32u)NullTimerInterval, 1, 1, "Null Timer");
+
+ system_timer_id = bx_pc_system.register_timer(this, pc_system_timer_handler,virtual_next_event_time , 0, 1, "Virtual Timer");
+
+ //Real time variables:
+#if BX_HAVE_REALTIME_USEC
+ last_real_time=GET_VIRT_REALTIME64_USEC()+(Bit64u)TIME_HEADSTART*(Bit64u)USEC_PER_SECOND;
+#endif
+ total_real_usec=0;
+ last_realtime_delta=0;
+ //System time variables:
+ last_usec = 0
+;
+ usec_per_second = USEC_PER_SECOND;
+ stored_delta=0;
+ last_system_usec=0;
+ em_last_realtime=0;
+ //Virtual timer variables:
+ total_ticks=0;
+ last_realtime_ticks=0;
+ ticks_per_second = USEC_PER_SECOND;
+
+ init_done = 1;
+}
+
+void
+bx_virt_timer_c::timer_handler(void) {
+ if(!virtual_timers_realtime) {
+ Bit64u temp_final_time = bx_pc_system.time_usec();
+ temp_final_time-=current_virtual_time;
+ while(temp_final_time) {
+ if((temp_final_time)>(virtual_next_event_time)) {
+ temp_final_time-=virtual_next_event_time;
+ advance_virtual_time(virtual_next_event_time);
+ } else {
+ advance_virtual_time(temp_final_time);
+ temp_final_time-=temp_final_time;
+ }
+ }
+ bx_pc_system.activate_timer(system_timer_id,
+ (Bit32u)BX_MIN(0x7FFFFFFF,(virtual_next_event_time>2)?(virtual_next_event_time-2):1),
+ 0);
+ return;
+ }
+
+ Bit64u usec_delta = bx_pc_system.time_usec()-last_usec;
+
+ if (usec_delta) {
+#if BX_HAVE_REALTIME_USEC
+ Bit64u ticks_delta = 0;
+ Bit64u real_time_delta = GET_VIRT_REALTIME64_USEC() - last_real_time;
+ Bit64u real_time_total = real_time_delta + total_real_usec;
+ Bit64u system_time_delta = (Bit64u)usec_delta + (Bit64u)stored_delta;
+ if(real_time_delta) {
+ last_realtime_delta = real_time_delta;
+ last_realtime_ticks = total_ticks;
+ }
+ ticks_per_second = USEC_PER_SECOND;
+
+ //Start out with the number of ticks we would like
+ // to have to line up with real time.
+ ticks_delta = real_time_total - total_ticks;
+ if(real_time_total < total_ticks) {
+ //This slows us down if we're already ahead.
+ // probably only an issue on startup, but it solves some problems.
+ ticks_delta = 0;
+ }
+ if(ticks_delta + total_ticks - last_realtime_ticks > (F2I(MAX_MULT * I2F(last_realtime_delta)))) {
+ //This keeps us from going too fast in relation to real time.
+#if 0
+ ticks_delta = (F2I(MAX_MULT * I2F(last_realtime_delta))) + last_realtime_ticks - total_ticks;
+#endif
+ ticks_per_second = F2I(MAX_MULT * I2F(USEC_PER_SECOND));
+ }
+ if(ticks_delta > system_time_delta * USEC_PER_SECOND / MIN_USEC_PER_SECOND) {
+ //This keeps us from having too few instructions between ticks.
+ ticks_delta = system_time_delta * USEC_PER_SECOND / MIN_USEC_PER_SECOND;
+ }
+ if(ticks_delta > virtual_next_event_time) {
+ //This keeps us from missing ticks.
+ ticks_delta = virtual_next_event_time;
+ }
+
+ if(ticks_delta) {
+
+# if DEBUG_REALTIME_WITH_PRINTF
+ //Every second print some info.
+ if(((last_real_time + real_time_delta) / USEC_PER_SECOND) > (last_real_time / USEC_PER_SECOND)) {
+ Bit64u temp1, temp2, temp3, temp4;
+ temp1 = (Bit64u) total_real_usec;
+ temp2 = (total_real_usec);
+ temp3 = (Bit64u)total_ticks;
+ temp4 = (Bit64u)((total_real_usec) - total_ticks);
+ printf("useconds: %llu, ",temp1);
+ printf("expect ticks: %llu, ",temp2);
+ printf("ticks: %llu, ",temp3);
+ printf("diff: %llu\n",temp4);
+ }
+# endif
+
+ last_real_time += real_time_delta;
+ total_real_usec += real_time_delta;
+ last_system_usec += system_time_delta;
+ stored_delta = 0;
+ total_ticks += ticks_delta;
+ } else {
+ stored_delta = system_time_delta;
+ }
+
+
+ Bit64u a,b;
+ a=(usec_per_second);
+ if(real_time_delta) {
+ //FIXME
+ Bit64u em_realtime_delta = last_system_usec + stored_delta - em_last_realtime;
+ b=((Bit64u)USEC_PER_SECOND * em_realtime_delta / real_time_delta);
+ em_last_realtime = last_system_usec + stored_delta;
+ } else {
+ b=a;
+ }
+ usec_per_second = ALPHA_LOWER(a,b);
+#else
+ BX_ASSERT(0);
+#endif
+#if BX_HAVE_REALTIME_USEC
+ advance_virtual_time(ticks_delta);
+#endif
+ }
+
+ last_usec=last_usec + usec_delta;
+ bx_pc_system.deactivate_timer(system_timer_id);
+ BX_ASSERT(virtual_next_event_time);
+ bx_pc_system.activate_timer(system_timer_id,
+ (Bit32u)BX_MIN(0x7FFFFFFF,BX_MAX(1,TICKS_TO_USEC(virtual_next_event_time))),
+ 0);
+
+}
+
+void
+bx_virt_timer_c::pc_system_timer_handler(void* this_ptr) {
+ ((bx_virt_timer_c *)this_ptr)->timer_handler();
+}
+