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:
parent
a56e55b8ac
commit
3a9a15f3a9
1 changed files with 45 additions and 14 deletions
59
sim_timer.c
59
sim_timer.c
|
@ -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");
|
/*
|
||||||
_rtcn_configure_calibrated_clock (sim_calb_tmr);
|
* 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);
|
||||||
|
}
|
||||||
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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue