| /* sim_timer.c: simulator timer library | |
| Copyright (c) 1993-2010, Robert M Supnik | |
| Permission is hereby granted, free of charge, to any person obtaining a | |
| copy of this software and associated documentation files (the "Software"), | |
| to deal in the Software without restriction, including without limitation | |
| the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| and/or sell copies of the Software, and to permit persons to whom the | |
| Software is furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in | |
| all copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| Except as contained in this notice, the name of Robert M Supnik shall not be | |
| used in advertising or otherwise to promote the sale, use or other dealings | |
| in this Software without prior written authorization from Robert M Supnik. | |
| 21-Oct-11 MP Fixed throttling in several ways: | |
| - Sleep for the observed clock tick size while throttling | |
| - Recompute the throttling wait once every 10 seconds | |
| to account for varying instruction mixes during | |
| different phases of a simulator execution or to | |
| accommodate the presence of other load on the host | |
| system. | |
| - Each of the pre-existing throttling modes (Kcps, | |
| Mcps, and %) all compute the appropriate throttling | |
| interval dynamically. These dynamic computations | |
| assume that 100% of the host CPU is dedicated to | |
| the current simulator during this computation. | |
| This assumption may not always be true and under | |
| certain conditions may never provide a way to | |
| correctly determine the appropriate throttling | |
| wait. An additional throttling mode has been added | |
| which allows the simulator operator to explicitly | |
| state the desired throttling wait parameters. | |
| These are specified by: | |
| SET THROT insts/delay | |
| where 'insts' is the number of instructions to | |
| execute before sleeping for 'delay' milliseconds. | |
| 22-Apr-11 MP Fixed Asynch I/O support to reasonably account cycles | |
| when an idle wait is terminated by an external event | |
| 05-Jan-11 MP Added Asynch I/O support | |
| 29-Dec-10 MP Fixed clock resolution determination for Unix platforms | |
| 22-Sep-08 RMS Added "stability threshold" for idle routine | |
| 27-May-08 RMS Fixed bug in Linux idle routines (from Walter Mueller) | |
| 18-Jun-07 RMS Modified idle to exclude counted delays | |
| 22-Mar-07 RMS Added sim_rtcn_init_all | |
| 17-Oct-06 RMS Added idle support (based on work by Mark Pizzolato) | |
| Added throttle support | |
| 16-Aug-05 RMS Fixed C++ declaration and cast problems | |
| 02-Jan-04 RMS Split out from SCP | |
| This library includes the following routines: | |
| sim_timer_init - initialize timing system | |
| sim_rtc_init - initialize calibration | |
| sim_rtc_calb - calibrate clock | |
| sim_timer_init - initialize timing system | |
| sim_idle - virtual machine idle | |
| sim_os_msec - return elapsed time in msec | |
| sim_os_sleep - sleep specified number of seconds | |
| sim_os_ms_sleep - sleep specified number of milliseconds | |
| sim_idle_ms_sleep - sleep specified number of milliseconds | |
| or until awakened by an asynchronous | |
| event | |
| The calibration, idle, and throttle routines are OS-independent; the _os_ | |
| routines are not. | |
| */ | |
| #include "sim_defs.h" | |
| #include <ctype.h> | |
| t_bool sim_idle_enab = FALSE; /* global flag */ | |
| t_bool sim_idle_wait = FALSE; /* global flag */ | |
| static uint32 sim_idle_rate_ms = 0; | |
| static uint32 sim_idle_stable = SIM_IDLE_STDFLT; | |
| static uint32 sim_throt_ms_start = 0; | |
| static uint32 sim_throt_ms_stop = 0; | |
| static uint32 sim_throt_type = 0; | |
| static uint32 sim_throt_val = 0; | |
| static uint32 sim_throt_state = 0; | |
| static uint32 sim_throt_sleep_time = 0; | |
| static int32 sim_throt_wait = 0; | |
| extern int32 sim_interval, sim_switches; | |
| extern FILE *sim_log; | |
| extern UNIT *sim_clock_queue; | |
| t_stat sim_throt_svc (UNIT *uptr); | |
| UNIT sim_throt_unit = { UDATA (&sim_throt_svc, 0, 0) }; | |
| /* OS-dependent timer and clock routines */ | |
| /* VMS */ | |
| #if defined (VMS) | |
| #if defined (__VAX) | |
| #define sys$gettim SYS$GETTIM | |
| #define sys$setimr SYS$SETIMR | |
| #define lib$emul LIB$EMUL | |
| #define sys$waitfr SYS$WAITFR | |
| #define lib$subx LIB$SUBX | |
| #define lib$ediv LIB$EDIV | |
| #endif | |
| #include <starlet.h> | |
| #include <lib$routines.h> | |
| #include <unistd.h> | |
| const t_bool rtc_avail = TRUE; | |
| uint32 sim_os_msec () | |
| { | |
| uint32 quo, htod, tod[2]; | |
| int32 i; | |
| sys$gettim (tod); /* time 0.1usec */ | |
| /* To convert to msec, must divide a 64b quantity by 10000. This is actually done | |
| by dividing the 96b quantity 0'time by 10000, producing 64b of quotient, the | |
| high 32b of which are discarded. This can probably be done by a clever multiply... | |
| */ | |
| quo = htod = 0; | |
| for (i = 0; i < 64; i++) { /* 64b quo */ | |
| htod = (htod << 1) | ((tod[1] >> 31) & 1); /* shift divd */ | |
| tod[1] = (tod[1] << 1) | ((tod[0] >> 31) & 1); | |
| tod[0] = tod[0] << 1; | |
| quo = quo << 1; /* shift quo */ | |
| if (htod >= 10000) { /* divd work? */ | |
| htod = htod - 10000; /* subtract */ | |
| quo = quo | 1; /* set quo bit */ | |
| } | |
| } | |
| return quo; | |
| } | |
| void sim_os_sleep (unsigned int sec) | |
| { | |
| sleep (sec); | |
| return; | |
| } | |
| uint32 sim_os_ms_sleep_init (void) | |
| { | |
| #if defined (__VAX) | |
| return 10; /* VAX/VMS is 10ms */ | |
| #else | |
| return 1; /* Alpha/VMS is 1ms */ | |
| #endif | |
| } | |
| uint32 sim_os_ms_sleep (unsigned int msec) | |
| { | |
| uint32 stime = sim_os_msec (); | |
| uint32 qtime[2]; | |
| int32 nsfactor = -10000; | |
| static int32 zero = 0; | |
| lib$emul (&msec, &nsfactor, &zero, qtime); | |
| sys$setimr (2, qtime, 0, 0); | |
| sys$waitfr (2); | |
| return sim_os_msec () - stime; | |
| } | |
| #if defined(SIM_ASYNCH_IO) | |
| #ifdef NEED_CLOCK_REALTIME | |
| int clock_gettime(int clk_id, struct timespec *tp) | |
| { | |
| uint32 secs, ns, tod[2], unixbase[2] = {0xd53e8000, 0x019db1de}; | |
| if (clk_id != CLOCK_REALTIME) | |
| return -1; | |
| sys$gettim (tod); /* time 0.1usec */ | |
| lib$subx(tod, unixbase, tod); /* convert to unix base */ | |
| lib$ediv(&10000000, tod, &secs, &ns); /* isolate seconds & 100ns parts */ | |
| tp->tv_sec = secs; | |
| tp->tv_nsec = ns*100; | |
| return 0; | |
| } | |
| #endif /* CLOCK_REALTIME */ | |
| #endif /* SIM_ASYNCH_IO */ | |
| #elif defined (_WIN32) | |
| /* Win32 routines */ | |
| #include <windows.h> | |
| const t_bool rtc_avail = TRUE; | |
| uint32 sim_os_msec () | |
| { | |
| if (sim_idle_rate_ms) | |
| return timeGetTime (); | |
| else return GetTickCount (); | |
| } | |
| void sim_os_sleep (unsigned int sec) | |
| { | |
| Sleep (sec * 1000); | |
| return; | |
| } | |
| void sim_timer_exit (void) | |
| { | |
| timeEndPeriod (sim_idle_rate_ms); | |
| return; | |
| } | |
| uint32 sim_os_ms_sleep_init (void) | |
| { | |
| TIMECAPS timers; | |
| if (timeGetDevCaps (&timers, sizeof (timers)) != TIMERR_NOERROR) | |
| return 0; | |
| if ((timers.wPeriodMin == 0) || (timers.wPeriodMin > SIM_IDLE_MAX)) | |
| return 0; | |
| if (timeBeginPeriod (timers.wPeriodMin) != TIMERR_NOERROR) | |
| return 0; | |
| atexit (sim_timer_exit); | |
| Sleep (1); | |
| Sleep (1); | |
| Sleep (1); | |
| Sleep (1); | |
| Sleep (1); | |
| return timers.wPeriodMin; /* sim_idle_rate_ms */ | |
| } | |
| uint32 sim_os_ms_sleep (unsigned int msec) | |
| { | |
| uint32 stime = sim_os_msec(); | |
| Sleep (msec); | |
| return sim_os_msec () - stime; | |
| } | |
| #if defined(NEED_CLOCK_REALTIME) | |
| int clock_gettime(int clk_id, struct timespec *tp) | |
| { | |
| t_uint64 now, unixbase; | |
| if (clk_id != CLOCK_REALTIME) | |
| return -1; | |
| unixbase = 116444736; | |
| unixbase *= 1000000000; | |
| GetSystemTimeAsFileTime((FILETIME*)&now); | |
| now -= unixbase; | |
| tp->tv_sec = (long)(now/10000000); | |
| tp->tv_nsec = (now%10000000)*100; | |
| return 0; | |
| } | |
| #endif | |
| #elif defined (__OS2__) | |
| /* OS/2 routines, from Bruce Ray */ | |
| const t_bool rtc_avail = FALSE; | |
| uint32 sim_os_msec () | |
| { | |
| return 0; | |
| } | |
| void sim_os_sleep (unsigned int sec) | |
| { | |
| return; | |
| } | |
| uint32 sim_os_ms_sleep_init (void) | |
| { | |
| return FALSE; | |
| } | |
| uint32 sim_os_ms_sleep (unsigned int msec) | |
| { | |
| return 0; | |
| } | |
| /* Metrowerks CodeWarrior Macintosh routines, from Ben Supnik */ | |
| #elif defined (__MWERKS__) && defined (macintosh) | |
| #include <Timer.h> | |
| #include <Mactypes.h> | |
| #include <sioux.h> | |
| #include <unistd.h> | |
| #include <siouxglobals.h> | |
| #define NANOS_PER_MILLI 1000000 | |
| #define MILLIS_PER_SEC 1000 | |
| const t_bool rtc_avail = TRUE; | |
| uint32 sim_os_msec (void) | |
| { | |
| unsigned long long micros; | |
| UnsignedWide macMicros; | |
| unsigned long millis; | |
| Microseconds (&macMicros); | |
| micros = *((unsigned long long *) &macMicros); | |
| millis = micros / 1000LL; | |
| return (uint32) millis; | |
| } | |
| void sim_os_sleep (unsigned int sec) | |
| { | |
| sleep (sec); | |
| return; | |
| } | |
| uint32 sim_os_ms_sleep_init (void) | |
| { | |
| return 1; | |
| } | |
| uint32 sim_os_ms_sleep (unsigned int milliseconds) | |
| { | |
| uint32 stime = sim_os_msec (); | |
| struct timespec treq; | |
| treq.tv_sec = milliseconds / MILLIS_PER_SEC; | |
| treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI; | |
| (void) nanosleep (&treq, NULL); | |
| return sim_os_msec () - stime; | |
| } | |
| #if defined(NEED_CLOCK_REALTIME) | |
| int clock_gettime(int clk_id, struct timespec *tp) | |
| { | |
| struct timeval cur; | |
| struct timezone foo; | |
| if (clk_id != CLOCK_REALTIME) | |
| return -1; | |
| gettimeofday (&cur, &foo); | |
| tp->tv_sec = cur.tv_sec; | |
| tp->tv_nsec = cur.tv_usec*1000; | |
| return 0; | |
| } | |
| #endif | |
| #else | |
| /* UNIX routines */ | |
| #include <time.h> | |
| #include <sys/time.h> | |
| #include <unistd.h> | |
| #define NANOS_PER_MILLI 1000000 | |
| #define MILLIS_PER_SEC 1000 | |
| #define sleep1Samples 100 | |
| const t_bool rtc_avail = TRUE; | |
| uint32 sim_os_msec () | |
| { | |
| struct timeval cur; | |
| struct timezone foo; | |
| uint32 msec; | |
| gettimeofday (&cur, &foo); | |
| msec = (((uint32) cur.tv_sec) * 1000) + (((uint32) cur.tv_usec) / 1000); | |
| return msec; | |
| } | |
| void sim_os_sleep (unsigned int sec) | |
| { | |
| sleep (sec); | |
| return; | |
| } | |
| uint32 sim_os_ms_sleep_init (void) | |
| { | |
| uint32 i, t1, t2, tot, tim; | |
| for (i = 0, tot = 0; i < sleep1Samples; i++) { | |
| t1 = sim_os_msec (); | |
| sim_os_ms_sleep (1); | |
| t2 = sim_os_msec (); | |
| tot += (t2 - t1); | |
| } | |
| tim = (tot + (sleep1Samples - 1)) / sleep1Samples; | |
| if (tim > SIM_IDLE_MAX) | |
| tim = 0; | |
| return tim; | |
| } | |
| #if !defined(_POSIX_SOURCE) && defined(SIM_ASYNCH_IO) | |
| #ifdef NEED_CLOCK_REALTIME | |
| typedef int clockid_t; | |
| int clock_gettime(clockid_t clk_id, struct timespec *tp) | |
| { | |
| struct timeval cur; | |
| struct timezone foo; | |
| if (clk_id != CLOCK_REALTIME) | |
| return -1; | |
| gettimeofday (&cur, &foo); | |
| tp->tv_sec = cur.tv_sec; | |
| tp->tv_nsec = cur.tv_usec*1000; | |
| return 0; | |
| } | |
| #endif /* CLOCK_REALTIME */ | |
| #endif /* !defined(_POSIX_SOURCE) && defined(SIM_ASYNCH_IO) */ | |
| uint32 sim_os_ms_sleep (unsigned int milliseconds) | |
| { | |
| uint32 stime = sim_os_msec (); | |
| struct timespec treq; | |
| treq.tv_sec = milliseconds / MILLIS_PER_SEC; | |
| treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI; | |
| (void) nanosleep (&treq, NULL); | |
| return sim_os_msec () - stime; | |
| } | |
| #endif | |
| /* diff = min - sub */ | |
| void | |
| sim_timespec_diff (struct timespec *diff, struct timespec *min, struct timespec *sub) | |
| { | |
| /* move the minuend value to the difference and operate there. */ | |
| *diff = *min; | |
| /* Borrow as needed for the nsec value */ | |
| if (sub->tv_nsec > min->tv_nsec) { | |
| --diff->tv_sec; | |
| diff->tv_nsec += 1000000000; | |
| } | |
| diff->tv_nsec -= sub->tv_nsec; | |
| diff->tv_sec -= sub->tv_sec; | |
| } | |
| #if defined(SIM_ASYNCH_IO) | |
| uint32 sim_idle_ms_sleep (unsigned int msec) | |
| { | |
| uint32 start_time = sim_os_msec(); | |
| struct timespec done_time; | |
| clock_gettime(CLOCK_REALTIME, &done_time); | |
| done_time.tv_sec += (msec/1000); | |
| done_time.tv_nsec += 1000000*(msec%1000); | |
| if (done_time.tv_nsec > 1000000000) { | |
| done_time.tv_sec += 1; | |
| done_time.tv_nsec = done_time.tv_nsec%1000000000; | |
| } | |
| pthread_mutex_lock (&sim_asynch_lock); | |
| sim_idle_wait = TRUE; | |
| pthread_cond_timedwait (&sim_asynch_wake, &sim_asynch_lock, &done_time); | |
| sim_idle_wait = FALSE; | |
| pthread_mutex_unlock (&sim_asynch_lock); | |
| return sim_os_msec() - start_time; | |
| } | |
| #define SIM_IDLE_MS_SLEEP sim_idle_ms_sleep | |
| #else | |
| #define SIM_IDLE_MS_SLEEP sim_os_ms_sleep | |
| #endif | |
| /* OS independent clock calibration package */ | |
| static int32 rtc_ticks[SIM_NTIMERS] = { 0 }; /* ticks */ | |
| static int32 rtc_hz[SIM_NTIMERS] = { 0 }; /* tick rate */ | |
| static uint32 rtc_rtime[SIM_NTIMERS] = { 0 }; /* real time */ | |
| static uint32 rtc_vtime[SIM_NTIMERS] = { 0 }; /* virtual time */ | |
| static uint32 rtc_nxintv[SIM_NTIMERS] = { 0 }; /* next interval */ | |
| static int32 rtc_based[SIM_NTIMERS] = { 0 }; /* base delay */ | |
| static int32 rtc_currd[SIM_NTIMERS] = { 0 }; /* current delay */ | |
| static int32 rtc_initd[SIM_NTIMERS] = { 0 }; /* initial delay */ | |
| static uint32 rtc_elapsed[SIM_NTIMERS] = { 0 }; /* sec since init */ | |
| void sim_rtcn_init_all (void) | |
| { | |
| uint32 i; | |
| for (i = 0; i < SIM_NTIMERS; i++) { | |
| if (rtc_initd[i] != 0) sim_rtcn_init (rtc_initd[i], i); | |
| } | |
| return; | |
| } | |
| int32 sim_rtcn_init (int32 time, int32 tmr) | |
| { | |
| if (time == 0) | |
| time = 1; | |
| if ((tmr < 0) || (tmr >= SIM_NTIMERS)) | |
| return time; | |
| rtc_rtime[tmr] = sim_os_msec (); | |
| rtc_vtime[tmr] = rtc_rtime[tmr]; | |
| rtc_nxintv[tmr] = 1000; | |
| rtc_ticks[tmr] = 0; | |
| rtc_hz[tmr] = 0; | |
| rtc_based[tmr] = time; | |
| rtc_currd[tmr] = time; | |
| rtc_initd[tmr] = time; | |
| rtc_elapsed[tmr] = 0; | |
| return time; | |
| } | |
| int32 sim_rtcn_calb (int32 ticksper, int32 tmr) | |
| { | |
| uint32 new_rtime, delta_rtime; | |
| int32 delta_vtime; | |
| if ((tmr < 0) || (tmr >= SIM_NTIMERS)) | |
| return 10000; | |
| rtc_hz[tmr] = ticksper; | |
| rtc_ticks[tmr] = rtc_ticks[tmr] + 1; /* count ticks */ | |
| if (rtc_ticks[tmr] < ticksper) /* 1 sec yet? */ | |
| return rtc_currd[tmr]; | |
| rtc_ticks[tmr] = 0; /* reset ticks */ | |
| rtc_elapsed[tmr] = rtc_elapsed[tmr] + 1; /* count sec */ | |
| if (!rtc_avail) /* no timer? */ | |
| return rtc_currd[tmr]; | |
| new_rtime = sim_os_msec (); /* wall time */ | |
| if (new_rtime < rtc_rtime[tmr]) { /* time running backwards? */ | |
| rtc_rtime[tmr] = new_rtime; /* reset wall time */ | |
| return rtc_currd[tmr]; /* can't calibrate */ | |
| } | |
| delta_rtime = new_rtime - rtc_rtime[tmr]; /* elapsed wtime */ | |
| rtc_rtime[tmr] = new_rtime; /* adv wall time */ | |
| rtc_vtime[tmr] = rtc_vtime[tmr] + 1000; /* adv sim time */ | |
| if (delta_rtime > 30000) /* gap too big? */ | |
| return rtc_initd[tmr]; /* can't calibr */ | |
| if (delta_rtime == 0) /* gap too small? */ | |
| rtc_based[tmr] = rtc_based[tmr] * ticksper; /* slew wide */ | |
| else rtc_based[tmr] = (int32) (((double) rtc_based[tmr] * (double) rtc_nxintv[tmr]) / | |
| ((double) delta_rtime)); /* new base rate */ | |
| delta_vtime = rtc_vtime[tmr] - rtc_rtime[tmr]; /* gap */ | |
| if (delta_vtime > SIM_TMAX) /* limit gap */ | |
| delta_vtime = SIM_TMAX; | |
| else if (delta_vtime < -SIM_TMAX) | |
| delta_vtime = -SIM_TMAX; | |
| rtc_nxintv[tmr] = 1000 + delta_vtime; /* next wtime */ | |
| rtc_currd[tmr] = (int32) (((double) rtc_based[tmr] * (double) rtc_nxintv[tmr]) / | |
| 1000.0); /* next delay */ | |
| if (rtc_based[tmr] <= 0) /* never negative or zero! */ | |
| rtc_based[tmr] = 1; | |
| if (rtc_currd[tmr] <= 0) /* never negative or zero! */ | |
| rtc_currd[tmr] = 1; | |
| AIO_SET_INTERRUPT_LATENCY(rtc_currd[tmr]*ticksper); /* set interrrupt latency */ | |
| return rtc_currd[tmr]; | |
| } | |
| /* Prior interfaces - default to timer 0 */ | |
| int32 sim_rtc_init (int32 time) | |
| { | |
| return sim_rtcn_init (time, 0); | |
| } | |
| int32 sim_rtc_calb (int32 ticksper) | |
| { | |
| return sim_rtcn_calb (ticksper, 0); | |
| } | |
| /* sim_timer_init - get minimum sleep time available on this host */ | |
| t_bool sim_timer_init (void) | |
| { | |
| sim_idle_enab = FALSE; /* init idle off */ | |
| sim_idle_rate_ms = sim_os_ms_sleep_init (); /* get OS timer rate */ | |
| return (sim_idle_rate_ms != 0); | |
| } | |
| /* sim_idle - idle simulator until next event or for specified interval | |
| Inputs: | |
| tmr = calibrated timer to use | |
| Must solve the linear equation | |
| ms_to_wait = w * ms_per_wait | |
| Or | |
| w = ms_to_wait / ms_per_wait | |
| */ | |
| t_bool sim_idle (uint32 tmr, t_bool sin_cyc) | |
| { | |
| static uint32 cyc_ms; | |
| uint32 w_ms, w_idle, act_ms; | |
| int32 act_cyc; | |
| if ((sim_clock_queue == NULL) || /* clock queue empty? */ | |
| ((sim_clock_queue->flags & UNIT_IDLE) == 0) || /* event not idle-able? */ | |
| (rtc_elapsed[tmr] < sim_idle_stable)) { /* timer not stable? */ | |
| if (sin_cyc) | |
| sim_interval = sim_interval - 1; | |
| return FALSE; | |
| } | |
| if (!cyc_ms) | |
| cyc_ms = (rtc_currd[tmr] * rtc_hz[tmr]) / 1000; /* cycles per msec */ | |
| if ((sim_idle_rate_ms == 0) || (cyc_ms == 0)) { /* not possible? */ | |
| if (sin_cyc) | |
| sim_interval = sim_interval - 1; | |
| return FALSE; | |
| } | |
| w_ms = (uint32) sim_interval / cyc_ms; /* ms to wait */ | |
| w_idle = w_ms / sim_idle_rate_ms; /* intervals to wait */ | |
| if (w_idle == 0) { /* none? */ | |
| if (sin_cyc) | |
| sim_interval = sim_interval - 1; | |
| return FALSE; | |
| } | |
| act_ms = SIM_IDLE_MS_SLEEP (w_ms); /* wait */ | |
| act_cyc = act_ms * cyc_ms; | |
| if (act_ms < w_ms) /* awakened early? */ | |
| act_cyc += (cyc_ms * sim_idle_rate_ms) / 2; /* account for half an interval's worth of cycles */ | |
| if (sim_interval > act_cyc) | |
| sim_interval = sim_interval - act_cyc; | |
| else sim_interval = 0; | |
| return TRUE; | |
| } | |
| /* Set idling - implicitly disables throttling */ | |
| t_stat sim_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc) | |
| { | |
| t_stat r; | |
| uint32 v; | |
| if (sim_idle_rate_ms == 0) | |
| return SCPE_NOFNC; | |
| if ((val != 0) && (sim_idle_rate_ms > (uint32) val)) | |
| return SCPE_NOFNC; | |
| if (cptr) { | |
| v = (uint32) get_uint (cptr, 10, SIM_IDLE_STMAX, &r); | |
| if ((r != SCPE_OK) || (v < SIM_IDLE_STMIN)) | |
| return SCPE_ARG; | |
| sim_idle_stable = v; | |
| } | |
| sim_idle_enab = TRUE; | |
| if (sim_throt_type != SIM_THROT_NONE) { | |
| sim_set_throt (0, NULL); | |
| printf ("Throttling disabled\n"); | |
| if (sim_log) | |
| fprintf (sim_log, "Throttling disabled\n"); | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Clear idling */ | |
| t_stat sim_clr_idle (UNIT *uptr, int32 val, char *cptr, void *desc) | |
| { | |
| sim_idle_enab = FALSE; | |
| return SCPE_OK; | |
| } | |
| /* Show idling */ | |
| t_stat sim_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc) | |
| { | |
| if (sim_idle_enab) { | |
| fprintf (st, "idle enabled"); | |
| if (sim_switches & SWMASK ('D')) | |
| fprintf (st, ", stability wait = %ds, minimum sleep resolution = %dms", sim_idle_stable, sim_idle_rate_ms); | |
| } | |
| else fputs ("idle disabled", st); | |
| return SCPE_OK; | |
| } | |
| /* Throttling package */ | |
| t_stat sim_set_throt (int32 arg, char *cptr) | |
| { | |
| char *tptr, c; | |
| t_value val, val2; | |
| if (arg == 0) { | |
| if ((cptr != 0) && (*cptr != 0)) | |
| return SCPE_ARG; | |
| sim_throt_type = SIM_THROT_NONE; | |
| sim_throt_cancel (); | |
| } | |
| else if (sim_idle_rate_ms == 0) | |
| return SCPE_NOFNC; | |
| else { | |
| val = strtotv (cptr, &tptr, 10); | |
| if (cptr == tptr) | |
| return SCPE_ARG; | |
| sim_throt_sleep_time = sim_idle_rate_ms; | |
| c = toupper (*tptr++); | |
| if (c == '/') | |
| val2 = strtotv (tptr, &tptr, 10); | |
| if ((*tptr != 0) || (val == 0)) | |
| return SCPE_ARG; | |
| if (c == 'M') | |
| sim_throt_type = SIM_THROT_MCYC; | |
| else if (c == 'K') | |
| sim_throt_type = SIM_THROT_KCYC; | |
| else if ((c == '%') && (val > 0) && (val < 100)) | |
| sim_throt_type = SIM_THROT_PCT; | |
| else if ((c == '/') && (val2 != 0)) { | |
| sim_throt_type = SIM_THROT_SPC; | |
| } | |
| else return SCPE_ARG; | |
| if (sim_idle_enab) { | |
| printf ("Idling disabled\n"); | |
| if (sim_log) | |
| fprintf (sim_log, "Idling disabled\n"); | |
| sim_clr_idle (NULL, 0, NULL, NULL); | |
| } | |
| sim_throt_val = (uint32) val; | |
| if (sim_throt_type == SIM_THROT_SPC) { | |
| if (val2 >= sim_idle_rate_ms) | |
| sim_throt_sleep_time = (uint32) val2; | |
| else { | |
| sim_throt_sleep_time = (uint32) (val2 * sim_idle_rate_ms); | |
| sim_throt_val = (uint32) (val * sim_idle_rate_ms); | |
| } | |
| } | |
| } | |
| return SCPE_OK; | |
| } | |
| t_stat sim_show_throt (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) | |
| { | |
| if (sim_idle_rate_ms == 0) | |
| fprintf (st, "Throttling not available\n"); | |
| else { | |
| switch (sim_throt_type) { | |
| case SIM_THROT_MCYC: | |
| fprintf (st, "Throttle = %d megacycles\n", sim_throt_val); | |
| break; | |
| case SIM_THROT_KCYC: | |
| fprintf (st, "Throttle = %d kilocycles\n", sim_throt_val); | |
| break; | |
| case SIM_THROT_PCT: | |
| fprintf (st, "Throttle = %d%%\n", sim_throt_val); | |
| break; | |
| case SIM_THROT_SPC: | |
| fprintf (st, "Throttle = %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_val); | |
| break; | |
| default: | |
| fprintf (st, "Throttling disabled\n"); | |
| break; | |
| } | |
| if (sim_switches & SWMASK ('D')) { | |
| fprintf (st, "minimum sleep resolution = %d ms\n", sim_idle_rate_ms); | |
| if (sim_throt_type != 0) | |
| fprintf (st, "Throttle interval = %d cycles\n", sim_throt_wait); | |
| } | |
| } | |
| return SCPE_OK; | |
| } | |
| void sim_throt_sched (void) | |
| { | |
| sim_throt_state = 0; | |
| if (sim_throt_type) | |
| sim_activate (&sim_throt_unit, SIM_THROT_WINIT); | |
| } | |
| void sim_throt_cancel (void) | |
| { | |
| sim_cancel (&sim_throt_unit); | |
| } | |
| /* Throttle service | |
| Throttle service has three distinct states used while dynamically | |
| determining a throttling interval: | |
| 0 take initial measurement | |
| 1 take final measurement, calculate wait values | |
| 2 periodic waits to slow down the CPU | |
| */ | |
| t_stat sim_throt_svc (UNIT *uptr) | |
| { | |
| uint32 delta_ms; | |
| double a_cps, d_cps; | |
| if (sim_throt_type == SIM_THROT_SPC) { /* Non dynamic? */ | |
| sim_throt_state = 2; /* force state */ | |
| sim_throt_wait = sim_throt_val; | |
| } | |
| switch (sim_throt_state) { | |
| case 0: /* take initial reading */ | |
| sim_throt_ms_start = sim_os_msec (); | |
| sim_throt_wait = SIM_THROT_WST; | |
| sim_throt_state = 1; /* next state */ | |
| break; /* reschedule */ | |
| case 1: /* take final reading */ | |
| sim_throt_ms_stop = sim_os_msec (); | |
| delta_ms = sim_throt_ms_stop - sim_throt_ms_start; | |
| if (delta_ms < SIM_THROT_MSMIN) { /* not enough time? */ | |
| if (sim_throt_wait >= 100000000) { /* too many inst? */ | |
| sim_throt_state = 0; /* fails in 32b! */ | |
| return SCPE_OK; | |
| } | |
| sim_throt_wait = sim_throt_wait * SIM_THROT_WMUL; | |
| sim_throt_ms_start = sim_throt_ms_stop; | |
| } | |
| else { /* long enough */ | |
| a_cps = ((double) sim_throt_wait) * 1000.0 / (double) delta_ms; | |
| if (sim_throt_type == SIM_THROT_MCYC) /* calc desired cps */ | |
| d_cps = (double) sim_throt_val * 1000000.0; | |
| else if (sim_throt_type == SIM_THROT_KCYC) | |
| d_cps = (double) sim_throt_val * 1000.0; | |
| else d_cps = (a_cps * ((double) sim_throt_val)) / 100.0; | |
| if (d_cps >= a_cps) { | |
| sim_throt_sched (); /* start over */ | |
| return SCPE_OK; | |
| } | |
| sim_throt_wait = (int32) /* time between waits */ | |
| ((a_cps * d_cps * ((double) sim_idle_rate_ms)) / | |
| (1000.0 * (a_cps - d_cps))); | |
| if (sim_throt_wait < SIM_THROT_WMIN) { /* not long enough? */ | |
| sim_throt_sched (); /* start over */ | |
| return SCPE_OK; | |
| } | |
| sim_throt_ms_start = sim_throt_ms_stop; | |
| sim_throt_state = 2; | |
| // fprintf (stderr, "Throttle values a_cps = %f, d_cps = %f, wait = %d\n", | |
| // a_cps, d_cps, sim_throt_wait); | |
| } | |
| break; | |
| case 2: /* throttling */ | |
| sim_os_ms_sleep (sim_throt_sleep_time); | |
| delta_ms = sim_os_msec () - sim_throt_ms_start; | |
| if ((sim_throt_type != SIM_THROT_SPC) && /* when dynamic throttling */ | |
| (delta_ms >= 10000)) { /* recompute every 10 sec */ | |
| sim_throt_ms_start = sim_os_msec (); | |
| sim_throt_wait = SIM_THROT_WST; | |
| sim_throt_state = 1; /* next state */ | |
| } | |
| break; | |
| } | |
| sim_activate (uptr, sim_throt_wait); /* reschedule */ | |
| return SCPE_OK; | |
| } |