TIMER: Calibrated clock fixes

- Properly handle clock transitions when control flows back and forth
  between instruction execution and simh commands.
- Changed Internal Timer from 10 Hz to the MAX(100Hz, HostOSClockHz)
- Changed default idle calibration percent to 50%
- Make sure that error cases (backwards and gap too big) properly advance
  the real time while avoiding calibration.
- Fix selection of the calibrated clock.
- Fix logic that sets the idle percentage that controls calibration.
This commit is contained in:
Mark Pizzolato 2019-05-28 22:47:06 -07:00
parent 7c27f77beb
commit 48db10994d
2 changed files with 49 additions and 39 deletions

Binary file not shown.

View file

@ -153,7 +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_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_stop_time = 0; /* time when sim_stop_timer_services was called */
static uint32 sim_stop_time = 0; /* time when sim_stop_timer_services was called */
static uint32 sim_idle_rate_ms = 0;
static uint32 sim_os_sleep_min_ms = 0;
@ -161,7 +161,7 @@ static uint32 sim_os_sleep_inc_ms = 0;
static uint32 sim_os_clock_resoluton_ms = 0;
static uint32 sim_os_tick_hz = 0;
static uint32 sim_idle_stable = SIM_IDLE_STDFLT;
static uint32 sim_idle_calib_pct = 0;
static uint32 sim_idle_calib_pct = 50;
static double sim_timer_stop_time = 0;
static uint32 sim_rom_delay = 0;
static uint32 sim_throt_ms_start = 0;
@ -176,7 +176,7 @@ static double sim_throt_inst_start;
static uint32 sim_throt_sleep_time = 0;
static int32 sim_throt_wait = 0;
static uint32 sim_throt_delay = 3;
#define CLK_TPS 10
#define CLK_TPS 100
#define CLK_INIT (SIM_INITIAL_IPS/CLK_TPS)
static int32 sim_int_clk_tps;
static UNIT *sim_clock_unit[SIM_NTIMERS+1] = {NULL};
@ -781,6 +781,7 @@ UNIT sim_timer_units[SIM_NTIMERS+1]; /* Clock assist units
/* clock if no clocks are registered. */
UNIT sim_stop_unit; /* Stop unit */
UNIT sim_internal_timer_unit; /* Internal calibration timer */
int32 sim_internal_timer_time; /* Pending internal timer delay */
UNIT sim_throttle_unit; /* one for throttle */
t_stat sim_throt_svc (UNIT *uptr);
@ -858,7 +859,7 @@ if (uptr) {
sim_register_clock_unit_tmr (uptr, tmr);
}
rtc_clock_start_gtime[tmr] = sim_gtime();
rtc_rtime[tmr] = sim_os_msec ();
rtc_rtime[tmr] = sim_is_running ? sim_os_msec () : sim_stop_time;
rtc_vtime[tmr] = rtc_rtime[tmr];
rtc_nxintv[tmr] = 1000;
rtc_ticks[tmr] = 0;
@ -900,6 +901,8 @@ else {
return 10000;
}
if (rtc_hz[tmr] != ticksper) { /* changing tick rate? */
uint32 prior_hz = rtc_hz[tmr];
if (rtc_hz[tmr] == 0)
rtc_clock_tick_start_time[tmr] = sim_timenow_double ();
if ((rtc_last_hz[tmr] != 0) &&
@ -911,10 +914,10 @@ if (rtc_hz[tmr] != ticksper) { /* changing tick rate? *
_rtcn_configure_calibrated_clock (tmr);
if (ticksper != 0) {
rtc_clock_tick_size[tmr] = 1.0 / ticksper;
sim_debug (DBG_CAL, &sim_timer_dev, "sim_rtcn_calb(ticksper=%d,tmr=%d) currd=%d\n", ticksper, tmr, rtc_currd[tmr]);
sim_debug (DBG_CAL, &sim_timer_dev, "sim_rtcn_calb(ticksper=%d,tmr=%d) currd=%d, prior_hz=%d\n", ticksper, tmr, rtc_currd[tmr], (int)prior_hz);
}
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]);
sim_debug (DBG_CAL, &sim_timer_dev, "sim_rtcn_calb(ticksper=%d,tmr=%d) timer stopped currd was %d, prior_hz=%d\n", ticksper, tmr, rtc_currd[tmr], (int)prior_hz);
}
if (ticksper == 0) /* running? */
return 10000;
@ -947,7 +950,8 @@ if (new_rtime < rtc_rtime[tmr]) { /* time running backward
/* for 49 days approximately every 49 days thereafter. */
++rtc_clock_calib_backwards[tmr]; /* Count statistic */
sim_debug (DBG_CAL, &sim_timer_dev, "time running backwards - OldTime: %u, NewTime: %u, result: %d\n", rtc_rtime[tmr], new_rtime, rtc_currd[tmr]);
rtc_rtime[tmr] = new_rtime; /* reset wall time */
rtc_vtime[tmr] = rtc_rtime[tmr] = new_rtime; /* reset wall time */
rtc_nxintv[tmr] = 1000;
return rtc_currd[tmr]; /* can't calibrate */
}
delta_rtime = new_rtime - rtc_rtime[tmr]; /* elapsed wtime */
@ -1022,7 +1026,8 @@ if (rtc_based[tmr] <= 0) /* never negative or zer
rtc_based[tmr] = 1;
if (rtc_currd[tmr] <= 0) /* never negative or zero! */
rtc_currd[tmr] = 1;
sim_debug (DBG_CAL, &sim_timer_dev, "calibrated tmr=%d, tickper=%d (base=%d, nxintv=%u, result: %d)\n", tmr, ticksper, rtc_based[tmr], rtc_nxintv[tmr], rtc_currd[tmr]);
sim_debug (DBG_CAL, &sim_timer_dev, "calibrated tmr=%d, tickper=%d (delta_rtime=%d, delta_vtime=%d, base=%d, nxintv=%u, result: %d)\n",
tmr, ticksper, (int)delta_rtime, (int)delta_vtime, rtc_based[tmr], rtc_nxintv[tmr], rtc_currd[tmr]);
/* Adjust calibration for other timers which depend on this timer's calibration */
for (itmr=0; itmr<=SIM_NTIMERS; itmr++)
if ((itmr != tmr) && (rtc_hz[itmr] != 0))
@ -1066,7 +1071,7 @@ sim_idle_enab = FALSE; /* init idle off */
sim_idle_rate_ms = sim_os_ms_sleep_init (); /* get OS timer rate */
sim_set_rom_delay_factor (sim_get_rom_delay_factor ()); /* initialize ROM delay factor */
clock_last = clock_start = sim_os_msec ();
sim_stop_time = clock_last = clock_start = sim_os_msec ();
sim_os_clock_resoluton_ms = 1000;
do {
uint32 clock_diff;
@ -1354,9 +1359,9 @@ char gbuf[CBUFSIZE];
if (cptr == NULL)
return SCPE_ARG;
cptr = get_glyph_nc (cptr, gbuf, 0); /* get argument */
cptr = get_glyph (cptr, gbuf, 0); /* get argument */
if (isdigit (gbuf[0]))
newpct = (int32) get_uint (cptr, 10, 100, &r);
newpct = (int32) get_uint (gbuf, 10, 100, &r);
else {
if (MATCH_CMD (gbuf, "ALWAYS") == 0)
newpct = 0;
@ -1521,6 +1526,8 @@ int32 act_cyc;
static t_bool in_nowait = FALSE;
double cyc_since_idle;
if (rtc_hz[tmr] == 0) /* specified timer is not running? */
tmr = sim_calb_tmr; /* use calibrated timer instead */
if (rtc_clock_catchup_pending[tmr]) { /* Catchup clock tick pending? */
sim_debug (DBG_CAL, &sim_timer_dev, "sim_idle(tmr=%d, sin_cyc=%d) - accelerating pending catch-up tick before idling %s\n", tmr, sin_cyc, sim_uname (sim_clock_unit[tmr]));
sim_activate_abs (&sim_timer_units[tmr], 0);
@ -1532,7 +1539,7 @@ if ((!sim_idle_enab) || /* idling disabled */
(!sim_asynch_timer))|| /* and not asynch? */
((sim_clock_queue != QUEUE_LIST_END) && /* or clock queue not empty */
((sim_clock_queue->flags & UNIT_IDLE) == 0))|| /* and event not idle-able? */
(rtc_elapsed[tmr] < sim_idle_stable)) { /* or timer not stable? */
(rtc_elapsed[tmr] < sim_idle_stable)) { /* or calibrated timer not stable? */
sim_debug (DBG_IDL, &sim_timer_dev, "Can't idle: %s - elapsed: %d.%03d\n", !sim_idle_enab ? "idle disabled" :
((rtc_elapsed[tmr] < sim_idle_stable) ? "not stable" :
((sim_clock_queue != QUEUE_LIST_END) ? sim_uname (sim_clock_queue) :
@ -2282,17 +2289,18 @@ return NULL;
#endif /* defined(SIM_ASYNCH_CLOCKS) */
/*
In the event that there are no active clock devices, no instruction
rate calibration will be performed. This is more likely on simpler
simulators which don't have a full spectrum of standard devices or
possibly when a clock device exists but its use is optional.
In the event that there are no active calibrated clock devices,
no instruction rate calibration will be performed. This is more
likely on simpler simulators which don't have a full spectrum of
standard devices or possibly when a clock device exists but its
use is optional.
Additonally, when a host system has a natural clock tick (or minimal
sleep time) which is greater than the tick size that a simulator
wants to run a clock at, we run this clock at the rate implied by
the host system's minimal sleep time or 50Hz.
Additonally, when a host system has a natural clock tick (
or minimal sleep time) which is greater than the tick size that
a simulator wants to run a clock at, we run this clock at the
rate implied by the host system's minimal sleep time or 50Hz.
To solve this we merely run an internal clock at 10Hz.
To solve this we merely run an internal clock at 100Hz.
*/
static t_stat sim_timer_clock_tick_svc (UNIT *uptr)
@ -2414,24 +2422,25 @@ return SCPE_OK;
void sim_start_timer_services (void)
{
/*
* 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]);
int32 tmr;
uint32 sim_prompt_time = sim_os_msec () - sim_stop_time;
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);
for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
if (rtc_hz[tmr]) { /* calibrated clock running? */
rtc_rtime[tmr] += sim_prompt_time;
rtc_vtime[tmr] += sim_prompt_time;
sim_debug (DBG_TRC, &sim_timer_dev, "sim_start_timer_services(tmr=%d) - adjusting calibration real time by %d ms\n", tmr, (int)sim_prompt_time);
}
}
if (sim_calb_tmr == -1) {
sim_debug (DBG_TRC, &sim_timer_dev, "sim_start_timer_services() - starting from scratch\n");
_rtcn_configure_calibrated_clock (sim_calb_tmr);
}
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);
if (sim_calb_tmr == SIM_NTIMERS) {
sim_debug (DBG_TRC, &sim_timer_dev, "sim_start_timer_services() - restarting internal timer after %d cycles\n", sim_internal_timer_time);
sim_activate (&SIM_INTERNAL_UNIT, sim_internal_timer_time);
}
}
if (sim_timer_stop_time > sim_gtime())
sim_activate_abs (&sim_stop_unit, (int32)(sim_timer_stop_time - sim_gtime()));
@ -2457,7 +2466,7 @@ void sim_stop_timer_services (void)
{
int tmr;
sim_debug (DBG_TRC, &sim_timer_dev, "sim_stop_timer_services(sim_interval=%d)\n", sim_interval);
sim_debug (DBG_TRC, &sim_timer_dev, "sim_stop_timer_services(sim_interval=%d, sim_calb_tmr=%d)\n", sim_interval, sim_calb_tmr);
if (sim_interval < 0)
sim_interval = 0; /* No catching up after stopping */
@ -2496,12 +2505,13 @@ for (tmr=0; tmr<=SIM_NTIMERS; tmr++) {
}
}
if (sim_calb_tmr == SIM_NTIMERS)
sim_internal_timer_time = sim_activate_time (&SIM_INTERNAL_UNIT) - 1;
sim_cancel (&SIM_INTERNAL_UNIT); /* Make sure Internal Timer is stopped */
sim_cancel (&sim_timer_units[SIM_NTIMERS]);
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_calb_tmr = -1;
sim_stop_time = sim_timenow_double (); /* record when execution stopped */
sim_stop_time = sim_os_msec (); /* record when execution stopped */
#if defined(SIM_ASYNCH_CLOCKS)
pthread_mutex_lock (&sim_timer_lock);
if (sim_timer_thread_running) {