TIMER: Allow short duration sim_instr() exits to avoid recalibrating timers

- sim_instr() returns to scp (during script execution) that start simulation
  again in less than 1 tick of the calibrated clock now leverage the previous
  calibration state when instruction execution resumes.
- Only generate catchup ticks for clocks that are still running.
- Revert windows sim_os_msec() implementation back to use the multi-media
  timer which is required on Windows XP and shouldn't be affected by dynamic
  OS time adjustments that do affect System Time.
This commit is contained in:
Mark Pizzolato 2018-09-28 18:04:34 -07:00
parent a56e55b8ac
commit 3a9a15f3a9

View file

@ -153,6 +153,7 @@ volatile t_bool sim_idle_wait = FALSE; /* global flag */
static int32 sim_calb_tmr = -1; /* the system calibrated timer */ static int32 sim_calb_tmr = -1; /* the system calibrated timer */
static int32 sim_calb_tmr_last = -1; /* shadow value when at sim> prompt */ static int32 sim_calb_tmr_last = -1; /* shadow value when at sim> prompt */
static double sim_inst_per_sec_last = 0; /* shadow value when at sim> prompt */ static double sim_inst_per_sec_last = 0; /* shadow value when at sim> prompt */
static double sim_stop_time = 0; /* time when sim_stop_timer_services was called */
static uint32 sim_idle_rate_ms = 0; static uint32 sim_idle_rate_ms = 0;
static uint32 sim_os_sleep_min_ms = 0; static uint32 sim_os_sleep_min_ms = 0;
@ -187,6 +188,7 @@ static t_bool sim_catchup_ticks = TRUE;
#endif #endif
t_bool sim_asynch_timer = FALSE; t_bool sim_asynch_timer = FALSE;
#if defined (SIM_ASYNCH_CLOCKS) #if defined (SIM_ASYNCH_CLOCKS)
UNIT * volatile sim_wallclock_queue = QUEUE_LIST_END; UNIT * volatile sim_wallclock_queue = QUEUE_LIST_END;
UNIT * volatile sim_wallclock_entry = NULL; UNIT * volatile sim_wallclock_entry = NULL;
@ -419,10 +421,7 @@ const t_bool rtc_avail = TRUE;
uint32 sim_os_msec (void) uint32 sim_os_msec (void)
{ {
__int64 nowTime; return timeGetTime (); /* use Multi-Media time source */
GetSystemTimeAsFileTime ((FILETIME *)&nowTime);
return (uint32)(((nowTime + 5000) / 10000) & 0xFFFFFFFF);
} }
void sim_os_sleep (unsigned int sec) void sim_os_sleep (unsigned int sec)
@ -796,6 +795,7 @@ t_stat sim_timer_stop_svc (UNIT *uptr);
#define DBG_ACK 0x080 /* interrupt acknowledgement activities */ #define DBG_ACK 0x080 /* interrupt acknowledgement activities */
#define DBG_CHK 0x100 /* check scheduled activation time*/ #define DBG_CHK 0x100 /* check scheduled activation time*/
#define DBG_INT 0x200 /* internal timer activities */ #define DBG_INT 0x200 /* internal timer activities */
#define DBG_GET 0x400 /* get_time activities */
DEBTAB sim_timer_debug[] = { DEBTAB sim_timer_debug[] = {
{"TRACE", DBG_TRC, "Trace routine calls"}, {"TRACE", DBG_TRC, "Trace routine calls"},
{"IDLE", DBG_IDL, "Idling activities"}, {"IDLE", DBG_IDL, "Idling activities"},
@ -803,6 +803,7 @@ DEBTAB sim_timer_debug[] = {
{"IACK", DBG_ACK, "interrupt acknowledgement activities"}, {"IACK", DBG_ACK, "interrupt acknowledgement activities"},
{"CALIB", DBG_CAL, "Calibration activities"}, {"CALIB", DBG_CAL, "Calibration activities"},
{"TIME", DBG_TIM, "Activation and scheduling activities"}, {"TIME", DBG_TIM, "Activation and scheduling activities"},
{"GETTIME", DBG_GET, "get_time activities"},
{"INTER", DBG_INT, "Internal timer activities"}, {"INTER", DBG_INT, "Internal timer activities"},
{"THROT", DBG_THR, "Throttling activities"}, {"THROT", DBG_THR, "Throttling activities"},
{"MUX", DBG_MUX, "Tmxr scheduling activities"}, {"MUX", DBG_MUX, "Tmxr scheduling activities"},
@ -899,13 +900,19 @@ else {
if (rtc_hz[tmr] != ticksper) { /* changing tick rate? */ if (rtc_hz[tmr] != ticksper) { /* changing tick rate? */
if (rtc_hz[tmr] == 0) if (rtc_hz[tmr] == 0)
rtc_clock_tick_start_time[tmr] = sim_timenow_double (); rtc_clock_tick_start_time[tmr] = sim_timenow_double ();
if ((rtc_last_hz[tmr] != 0) &&
(rtc_last_hz[tmr] != ticksper) &&
(ticksper != 0))
rtc_currd[tmr] = (int32)(sim_timer_inst_per_sec () / ticksper);
rtc_last_hz[tmr] = rtc_hz[tmr]; rtc_last_hz[tmr] = rtc_hz[tmr];
rtc_hz[tmr] = ticksper; rtc_hz[tmr] = ticksper;
_rtcn_configure_calibrated_clock (tmr); _rtcn_configure_calibrated_clock (tmr);
if (ticksper != 0) { if (ticksper != 0) {
rtc_clock_tick_size[tmr] = 1.0 / ticksper; rtc_clock_tick_size[tmr] = 1.0 / ticksper;
rtc_currd[tmr] = (int32)(sim_timer_inst_per_sec () / ticksper); sim_debug (DBG_CAL, &sim_timer_dev, "sim_rtcn_calb(ticksper=%d,tmr=%d) currd=%d\n", ticksper, tmr, rtc_currd[tmr]);
} }
else
sim_debug (DBG_CAL, &sim_timer_dev, "sim_rtcn_calb(ticksper=%d,tmr=%d) timer stopped currd was %d\n", ticksper, tmr, rtc_currd[tmr]);
} }
if (ticksper == 0) /* running? */ if (ticksper == 0) /* running? */
return 10000; return 10000;
@ -2038,7 +2045,7 @@ return SCPE_STOP;
void sim_rtcn_get_time (struct timespec *now, int tmr) void sim_rtcn_get_time (struct timespec *now, int tmr)
{ {
sim_debug (DBG_CAL, &sim_timer_dev, "sim_rtcn_get_time(tmr=%d)\n", tmr); sim_debug (DBG_GET, &sim_timer_dev, "sim_rtcn_get_time(tmr=%d)\n", tmr);
clock_gettime (CLOCK_REALTIME, now); clock_gettime (CLOCK_REALTIME, now);
} }
@ -2097,7 +2104,8 @@ if ((rtc_hz[tmr] > sim_os_tick_hz) && /* faster than host tick */
sim_debug (DBG_QUE, &sim_timer_dev, "_rtcn_tick_catchup_check() - Enabling catchup ticks for %s\n", sim_uname (sim_clock_unit[tmr])); sim_debug (DBG_QUE, &sim_timer_dev, "_rtcn_tick_catchup_check() - Enabling catchup ticks for %s\n", sim_uname (sim_clock_unit[tmr]));
return TRUE; return TRUE;
} }
if (rtc_clock_catchup_eligible[tmr]) if ((rtc_hz[tmr] > 0) &&
rtc_clock_catchup_eligible[tmr])
{ {
double tnow = sim_timenow_double(); double tnow = sim_timenow_double();
@ -2278,13 +2286,14 @@ return SCPE_OK;
faster than the host system's clock. This is optimal so that accurate faster than the host system's clock. This is optimal so that accurate
time measurements are taken. If the simulated system doesn't have a time measurements are taken. If the simulated system doesn't have a
clock with an appropriate tick rate, an internal clock is run that meets clock with an appropriate tick rate, an internal clock is run that meets
this requirement, this requirement, OR when asynch clocks are enabled, the internal clock
is always run.
*/ */
static void _rtcn_configure_calibrated_clock (int32 newtmr) static void _rtcn_configure_calibrated_clock (int32 newtmr)
{ {
int32 tmr; int32 tmr;
/* Look for a timer running slower than the host system clock */ /* Look for a timer running slower or the same as the host system clock */
sim_int_clk_tps = MIN(CLK_TPS, sim_os_tick_hz); sim_int_clk_tps = MIN(CLK_TPS, sim_os_tick_hz);
for (tmr=0; tmr<SIM_NTIMERS; tmr++) { for (tmr=0; tmr<SIM_NTIMERS; tmr++) {
if ((rtc_hz[tmr]) && if ((rtc_hz[tmr]) &&
@ -2381,8 +2390,25 @@ return SCPE_OK;
void sim_start_timer_services (void) void sim_start_timer_services (void)
{ {
sim_debug (DBG_TRC, &sim_timer_dev, "sim_start_timer_services()\n"); /*
* If we're quickly running again after being stopped for less than
* the time of one calibrated clock tick, then don't force a complete
* recalibration of any timers that may have been previously running.
*/
if ((sim_calb_tmr_last != -1) &&
((sim_timenow_double () - sim_stop_time) < rtc_clock_tick_size[sim_calb_tmr_last])) {
int32 clock_time = sim_activate_time (sim_clock_unit[sim_calb_tmr_last]);
sim_calb_tmr = sim_calb_tmr_last;
sim_cancel (sim_clock_unit[sim_calb_tmr]);
sim_activate (&sim_timer_units[sim_calb_tmr], clock_time);
sim_debug (DBG_TRC, &sim_timer_dev, "sim_start_timer_services() - restarting with previously calibrated timer %d (%s) at %d\n", sim_calb_tmr, sim_uname (sim_clock_unit[sim_calb_tmr]), clock_time);
}
else {
sim_debug (DBG_TRC, &sim_timer_dev, "sim_start_timer_services() - starting from scratch\n");
sim_rtcn_init_all (); /* re-init clocks */
_rtcn_configure_calibrated_clock (sim_calb_tmr); _rtcn_configure_calibrated_clock (sim_calb_tmr);
}
if (sim_timer_stop_time > sim_gtime()) if (sim_timer_stop_time > sim_gtime())
sim_activate_abs (&sim_stop_unit, (int32)(sim_timer_stop_time - sim_gtime())); sim_activate_abs (&sim_stop_unit, (int32)(sim_timer_stop_time - sim_gtime()));
#if defined(SIM_ASYNCH_CLOCKS) #if defined(SIM_ASYNCH_CLOCKS)
@ -2431,23 +2457,27 @@ for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
accum = sim_cosched_interval[tmr]; accum = sim_cosched_interval[tmr];
while (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) { while (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) {
UNIT *cptr = sim_clock_cosched_queue[tmr]; UNIT *cptr = sim_clock_cosched_queue[tmr];
double usecs_remaining = cptr->usecs_remaining;
sim_clock_cosched_queue[tmr] = cptr->next; sim_clock_cosched_queue[tmr] = cptr->next;
cptr->next = NULL; cptr->next = NULL;
cptr->cancel = NULL; cptr->cancel = NULL;
accum += cptr->time; accum += cptr->time;
cptr->usecs_remaining = 0.0;
_sim_activate (cptr, clock_time); _sim_activate (cptr, clock_time);
cptr->usecs_remaining = cptr->usecs_remaining + floor(1000000.0 * (accum - ((accum > 0) ? 1 : 0)) * rtc_clock_tick_size[tmr]); cptr->usecs_remaining = usecs_remaining + floor(1000000.0 * (accum - ((accum > 0) ? 1 : 0)) * rtc_clock_tick_size[tmr]);
sim_debug (DBG_QUE, &sim_timer_dev, "sim_stop_timer_services() - tmr=%d scheduling %s after %d and %.0f usecs\n", tmr, sim_uname (cptr), clock_time, cptr->usecs_remaining); sim_debug (DBG_QUE, &sim_timer_dev, "sim_stop_timer_services() - tmr=%d scheduling %s after %d and %.0f usecs\n", tmr, sim_uname (cptr), clock_time, cptr->usecs_remaining);
} }
sim_cosched_interval[tmr] = 0; sim_cosched_interval[tmr] = 0;
} }
} }
sim_cancel (&SIM_INTERNAL_UNIT); /* Make sure Internal Timer is stopped */ sim_cancel (&SIM_INTERNAL_UNIT); /* Make sure Internal Timer is stopped */
sim_cancel (&sim_timer_units[SIM_NTIMERS]); sim_cancel (&sim_timer_units[SIM_NTIMERS]);
sim_calb_tmr_last = sim_calb_tmr; /* Save calibrated timer value for display */ sim_calb_tmr_last = sim_calb_tmr; /* Save calibrated timer value for display */
sim_inst_per_sec_last = sim_timer_inst_per_sec (); /* Save execution rate for display */ sim_inst_per_sec_last = sim_timer_inst_per_sec (); /* Save execution rate for display */
sim_calb_tmr = -1; sim_calb_tmr = -1;
sim_stop_time = sim_timenow_double (); /* record when execution stopped */
#if defined(SIM_ASYNCH_CLOCKS) #if defined(SIM_ASYNCH_CLOCKS)
pthread_mutex_lock (&sim_timer_lock); pthread_mutex_lock (&sim_timer_lock);
if (sim_timer_thread_running) { if (sim_timer_thread_running) {
@ -2867,12 +2897,13 @@ if (uptr->a_next) {
if (uptr == sim_wallclock_entry) { /* Pending on the queue? */ if (uptr == sim_wallclock_entry) { /* Pending on the queue? */
sim_wallclock_entry = NULL; sim_wallclock_entry = NULL;
uptr->a_next = NULL; uptr->a_next = NULL;
sim_debug (DBG_QUE, &sim_timer_dev, "Canceled Queue Pending Timer Event for %s\n", sim_uname(uptr));
} }
else { else {
if (uptr == sim_wallclock_queue) { if (uptr == sim_wallclock_queue) {
sim_wallclock_queue = uptr->a_next; sim_wallclock_queue = uptr->a_next;
uptr->a_next = NULL; uptr->a_next = NULL;
sim_debug (DBG_QUE, &sim_timer_dev, "Canceling Timer Event for %s\n", sim_uname(uptr)); sim_debug (DBG_QUE, &sim_timer_dev, "Canceled Top Timer Event for %s\n", sim_uname(uptr));
pthread_cond_signal (&sim_timer_wake); pthread_cond_signal (&sim_timer_wake);
} }
else { else {
@ -2888,7 +2919,7 @@ if (uptr->a_next) {
} }
} }
} }
if (uptr->a_next == NULL) { if (uptr->a_next == NULL) { /* Was canceled? */
uptr->a_due_time = uptr->a_due_gtime = uptr->a_usec_delay = 0; uptr->a_due_time = uptr->a_due_gtime = uptr->a_usec_delay = 0;
uptr->cancel = NULL; uptr->cancel = NULL;
uptr->a_is_active = NULL; uptr->a_is_active = NULL;