Asynchronous Support

scp.c, scp.h
	- added sim_uname (Unit Name) API to simplify places which might want to display it (mostly debug messages).
	- added support for clock co-scheduling
	- added debugging to trace event queue activities

    sim_defs.h
	- added support for clock co-scheduling
	- added support for sim_uname (Thread local storage macro)
	- added support for debugging to trace event queue activities
	- simplified debug code by using sim_uname
	- fixed support macro for sim_is_active when asynch timers are in use

   sim_rev.h
	- fixed nested comments

    sim_tmxr.c, sim_tmxr.h
	- added support for clock co-scheduling
	- simplified debug code by using sim_uname
	- added support for devices which poll for output on different units

    sim_timer.c, sim_timer.h
	- added support for clock co-scheduling
	- fixed asynchronous clock calibration to smooth out calibration adjustments
	- simplified debug code by using sim_uname
	- added ability (when running with asynchronous support) to explicitly disable or enable asynchronous timer support.
	- changed sim_timer_inst_per_sec to return a double value since the result is always used in a double expression and integer overflow could occur under strange timing conditions

    vax/vax_stddev.c
	- converted from simulator specific clock co-scheduling to generic clock co-scheduling.

    vax/vax_cpu.c
	- added EVENT and ACTIVATE debug flag (SET CPU DEBUG=EVENT;ACTIVATE) support

    pdp11/pdp11_dz.c
	- converted from simulator specific clock co-scheduling to generic clock co-scheduling.

    pdp11/pdp11_vh.c
	- converted from simulator specific clock co-scheduling to generic clock co-scheduling.

    pdp11/pdp11_xq.c
	- converted from simulator specific clock co-scheduling to generic clock co-scheduling.
This commit is contained in:
Mark Pizzolato 2012-11-12 15:33:35 -08:00
parent 030d790b4c
commit 7c7df669ad
14 changed files with 367 additions and 115 deletions

View file

@ -272,6 +272,34 @@ drifted some 4 minutes in 35 minutes time (approximately 10%). The same OS
disk also running with idling enabled booted for 4 hours had less that 5 disk also running with idling enabled booted for 4 hours had less that 5
seconds of clock drift (approximately 0.03%). seconds of clock drift (approximately 0.03%).
Co-Scheduling Clock and Multiplexer (or other devices)
Many simulator devices have needs to periodically executed with timing on the
order of the simulated system's clock ticks. There are numerous reasons for
this type of execution. Meanwhile, many of these events aren't particular
about exactly when they execute as long as they execute frequently enough.
Frequently executing events has the potential to interfere with a simulator's
attempts to idle when the simulated system isn't actually doing useful work.
Interactions with attempts to 'co-schedule' multiplexer polling with clock
ticks can cause strange simulator behaviors. These strange behaviors only
happen under a combination of conditions:
1) a multiplexer device is defined in the simulator configuration,
2) the multiplexor device is NOT attached, and thus is not being managed by
the asynchronous multiplexer support
3) the multiplexer device schedules polling (co-scheduled) when not
attached (such polling will never produce anything input).
In prior simh versions support for clock co-scheduling was implmented
separately by each simulator, and usually was expressed by code of the form:
sim_activate (uptr, clk_cosched (tmxr_poll));
As a part of asynchronous timer support, the simulator framework has been
extended to generically provide clock co-scheduling support. The use of this
new capability requires an initial call (usually in the clock device reset
routing) of the form:
sim_register_clock_unit (&clk_unit);
Once the clock unit has been registered, co-scheduling is achieved by replacing
the earlier sim_activate with the following:
sim_clock_coschedule (&dz_unit, tmxr_poll);
Run time requirements to use SIM_ASYNCH_IO. Run time requirements to use SIM_ASYNCH_IO.
The Posix threads API (pthreads) is required for asynchronous execution. The Posix threads API (pthreads) is required for asynchronous execution.

View file

@ -334,7 +334,7 @@ switch ((PA >> 1) & 03) { /* case on PA<2:1> */
if (data & CSR_CLR) /* clr? reset */ if (data & CSR_CLR) /* clr? reset */
dz_clear (dz, FALSE); dz_clear (dz, FALSE);
if (data & CSR_MSE) /* MSE? start poll */ if (data & CSR_MSE) /* MSE? start poll */
sim_activate (&dz_unit, clk_cosched (tmxr_poll)); sim_clock_coschedule (&dz_unit, tmxr_poll);
else dz_csr[dz] &= ~(CSR_SA | CSR_RDONE | CSR_TRDY); else dz_csr[dz] &= ~(CSR_SA | CSR_RDONE | CSR_TRDY);
if ((data & CSR_RIE) == 0) /* RIE = 0? */ if ((data & CSR_RIE) == 0) /* RIE = 0? */
dz_clr_rxint (dz); dz_clr_rxint (dz);
@ -439,7 +439,7 @@ if (t) { /* any enabled? */
dz_update_rcvi (); /* upd rcv intr */ dz_update_rcvi (); /* upd rcv intr */
tmxr_poll_tx (&dz_desc); /* poll output */ tmxr_poll_tx (&dz_desc); /* poll output */
dz_update_xmti (); /* upd xmt intr */ dz_update_xmti (); /* upd xmt intr */
sim_activate (uptr, clk_cosched (tmxr_poll)); /* reactivate */ sim_clock_coschedule (uptr, tmxr_poll); /* reactivate */
} }
return SCPE_OK; return SCPE_OK;
} }

View file

@ -343,7 +343,7 @@ static DIB vh_dib = {
static UNIT vh_unit[VH_MUXES] = { static UNIT vh_unit[VH_MUXES] = {
{ UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) }, { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) },
{ UDATA (&vh_timersvc, 0, 0) }, { UDATA (&vh_timersvc, UNIT_IDLE, 0) },
}; };
static const REG vh_reg[] = { static const REG vh_reg[] = {
@ -841,9 +841,9 @@ static t_stat vh_wr ( int32 data,
if ((vh_unit[vh].flags & UNIT_MODEDHU) && (data & CSR_SKIP)) if ((vh_unit[vh].flags & UNIT_MODEDHU) && (data & CSR_SKIP))
data &= ~CSR_MASTER_RESET; data &= ~CSR_MASTER_RESET;
if (vh == 0) /* Only start unit service on the first unit. Units are polled there */ if (vh == 0) /* Only start unit service on the first unit. Units are polled there */
sim_activate (&vh_unit[0], clk_cosched (tmxr_poll)); sim_clock_coschedule (&vh_unit[0], tmxr_poll);
vh_mcount[vh] = MS2SIMH (1200); /* 1.2 seconds */ vh_mcount[vh] = MS2SIMH (1200); /* 1.2 seconds */
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); sim_clock_coschedule (&vh_unit[1], tmxr_poll);
} }
if ((data & CSR_RXIE) == 0) if ((data & CSR_RXIE) == 0)
vh_clr_rxint (vh); vh_clr_rxint (vh);
@ -875,7 +875,7 @@ static t_stat vh_wr ( int32 data,
break; break;
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1; vh_mcount[vh] = 1;
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); sim_clock_coschedule (&vh_unit[1], tmxr_poll);
break; break;
} }
if (vh_unit[vh].flags & UNIT_MODEDHU) { if (vh_unit[vh].flags & UNIT_MODEDHU) {
@ -920,7 +920,7 @@ static t_stat vh_wr ( int32 data,
case 2: /* LPR */ case 2: /* LPR */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1; vh_mcount[vh] = 1;
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); sim_clock_coschedule (&vh_unit[1], tmxr_poll);
break; break;
} }
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -947,7 +947,7 @@ static t_stat vh_wr ( int32 data,
case 3: /* STAT/FIFODATA */ case 3: /* STAT/FIFODATA */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1; vh_mcount[vh] = 1;
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); sim_clock_coschedule (&vh_unit[1], tmxr_poll);
break; break;
} }
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -971,7 +971,7 @@ static t_stat vh_wr ( int32 data,
case 4: /* LNCTRL */ case 4: /* LNCTRL */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1; vh_mcount[vh] = 1;
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); sim_clock_coschedule (&vh_unit[1], tmxr_poll);
break; break;
} }
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -1039,7 +1039,7 @@ static t_stat vh_wr ( int32 data,
case 5: /* TBUFFAD1 */ case 5: /* TBUFFAD1 */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1; vh_mcount[vh] = 1;
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); sim_clock_coschedule (&vh_unit[1], tmxr_poll);
break; break;
} }
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -1055,7 +1055,7 @@ static t_stat vh_wr ( int32 data,
case 6: /* TBUFFAD2 */ case 6: /* TBUFFAD2 */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1; vh_mcount[vh] = 1;
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); sim_clock_coschedule (&vh_unit[1], tmxr_poll);
break; break;
} }
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -1076,7 +1076,7 @@ static t_stat vh_wr ( int32 data,
case 7: /* TBUFFCT */ case 7: /* TBUFFCT */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1; vh_mcount[vh] = 1;
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); sim_clock_coschedule (&vh_unit[1], tmxr_poll);
break; break;
} }
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -1154,7 +1154,7 @@ static t_stat vh_timersvc ( UNIT *uptr )
} }
} }
if (again) if (again)
sim_activate (uptr, clk_cosched (tmxr_poll)); /* requeue ourselves */ sim_clock_coschedule (uptr, tmxr_poll); /* requeue ourselves */
return (SCPE_OK); return (SCPE_OK);
} }

View file

@ -2139,13 +2139,20 @@ void xq_start_receiver(CTLR* xq)
return; return;
/* start the read service timer or enable asynch reading as appropriate */ /* start the read service timer or enable asynch reading as appropriate */
if (xq->var->must_poll) if (xq->var->must_poll) {
sim_activate(xq->unit, (sim_idle_enab ? clk_cosched(tmxr_poll) : (tmr_poll*clk_tps)/xq->var->poll)); if (sim_idle_enab)
sim_clock_coschedule(xq->unit, tmxr_poll);
else
sim_activate(xq->unit, (tmr_poll*clk_tps)/xq->var->poll);
}
else else
if ((xq->var->poll == 0) || (xq->var->mode == XQ_T_DELQA_PLUS)) if ((xq->var->poll == 0) || (xq->var->mode == XQ_T_DELQA_PLUS))
eth_set_async(xq->var->etherface, xq->var->coalesce_latency_ticks); eth_set_async(xq->var->etherface, xq->var->coalesce_latency_ticks);
else else
sim_activate(xq->unit, (sim_idle_enab ? clk_cosched(tmxr_poll) : (tmr_poll*clk_tps)/xq->var->poll)); if (sim_idle_enab)
sim_clock_coschedule(xq->unit, tmxr_poll);
else
sim_activate(xq->unit, (tmr_poll*clk_tps)/xq->var->poll);
} }
void xq_stop_receiver(CTLR* xq) void xq_stop_receiver(CTLR* xq)
@ -2520,7 +2527,10 @@ t_stat xq_svc(UNIT* uptr)
/* resubmit service timer */ /* resubmit service timer */
if ((xq->var->must_poll) || (xq->var->poll && (xq->var->mode != XQ_T_DELQA_PLUS))) if ((xq->var->must_poll) || (xq->var->poll && (xq->var->mode != XQ_T_DELQA_PLUS)))
sim_activate(uptr, (sim_idle_enab ? clk_cosched(tmxr_poll) : (tmr_poll*clk_tps)/xq->var->poll)); if (sim_idle_enab)
sim_clock_coschedule(uptr, tmxr_poll);
else
sim_activate(uptr, (tmr_poll*clk_tps)/xq->var->poll);
return SCPE_OK; return SCPE_OK;
} }

View file

@ -491,6 +491,8 @@ DEBTAB cpu_deb[] = {
{ "INTEXC", LOG_CPU_I }, { "INTEXC", LOG_CPU_I },
{ "REI", LOG_CPU_R }, { "REI", LOG_CPU_R },
{ "CONTEXT", LOG_CPU_P }, { "CONTEXT", LOG_CPU_P },
{ "EVENT", SIM_DBG_EVENT },
{ "ACTIVATE", SIM_DBG_ACTIVATE },
{ NULL, 0 } { NULL, 0 }
}; };

View file

@ -321,7 +321,7 @@ t_stat tti_svc (UNIT *uptr)
{ {
int32 c; int32 c;
sim_activate (uptr, KBD_WAIT (uptr->wait, clk_cosched (tmr_poll))); sim_clock_coschedule (uptr, KBD_WAIT (uptr->wait, tmr_poll));
/* continue poll */ /* continue poll */
if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */
return c; return c;
@ -405,16 +405,6 @@ if (!todr_blow && todr_reg) /* if running? */
return SCPE_OK; return SCPE_OK;
} }
/* Clock coscheduling routine */
int32 clk_cosched (int32 wait)
{
int32 t;
t = sim_is_active (&clk_unit);
return (t? t - 1: wait);
}
int32 todr_rd (void) int32 todr_rd (void)
{ {
TOY *toy = (TOY *)clk_unit.filebuf; TOY *toy = (TOY *)clk_unit.filebuf;
@ -498,6 +488,7 @@ t_stat clk_reset (DEVICE *dptr)
{ {
int32 t; int32 t;
sim_register_clock_unit (&clk_unit);
clk_csr = 0; clk_csr = 0;
CLR_INT (CLK); CLR_INT (CLK);
if (!sim_is_running) { /* RESET (not IORESET)? */ if (!sim_is_running) { /* RESET (not IORESET)? */

46
scp.c
View file

@ -313,6 +313,7 @@ pthread_t sim_asynch_main_threadid;
UNIT * volatile sim_asynch_queue = QUEUE_LIST_END; UNIT * volatile sim_asynch_queue = QUEUE_LIST_END;
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;
UNIT * volatile sim_clock_cosched_queue = QUEUE_LIST_END;
int32 sim_asynch_check; int32 sim_asynch_check;
int32 sim_asynch_latency = 4000; /* 4 usec interrupt latency */ int32 sim_asynch_latency = 4000; /* 4 usec interrupt latency */
int32 sim_asynch_inst_latency = 20; /* assume 5 mip simulator */ int32 sim_asynch_inst_latency = 20; /* assume 5 mip simulator */
@ -2311,11 +2312,11 @@ int32 accum;
if (cptr && (*cptr != 0)) if (cptr && (*cptr != 0))
return SCPE_2MARG; return SCPE_2MARG;
if (sim_clock_queue == QUEUE_LIST_END) if (sim_clock_queue == QUEUE_LIST_END)
fprintf (st, "%s event queue empty, time = %.0f\n", fprintf (st, "%s event queue empty, time = %.0f, executing %.0f instructios/sec\n",
sim_name, sim_time); sim_name, sim_time, sim_timer_inst_per_sec ());
else { else {
fprintf (st, "%s event queue status, time = %.0f\n", fprintf (st, "%s event queue status, time = %.0f, executing %.0f instructions/sec\n",
sim_name, sim_time); sim_name, sim_time, sim_timer_inst_per_sec ());
accum = 0; accum = 0;
for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) { for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) {
if (uptr == &sim_step_unit) if (uptr == &sim_step_unit)
@ -2348,6 +2349,19 @@ else {
fprintf (st, " after %d usec\n", uptr->a_usec_delay); fprintf (st, " after %d usec\n", uptr->a_usec_delay);
} }
} }
if (sim_clock_cosched_queue != QUEUE_LIST_END) {
fprintf (st, "%s clock co-schedule event queue status, time = %.0f\n",
sim_name, sim_time);
for (uptr = sim_clock_cosched_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) {
if ((dptr = find_dev_from_unit (uptr)) != NULL) {
fprintf (st, " %s", sim_dname (dptr));
if (dptr->numunits > 1)
fprintf (st, " unit %d", (int32) (uptr - dptr->units));
}
else fprintf (st, " Unknown");
fprintf (st, "\n");
}
}
pthread_mutex_unlock (&sim_timer_lock); pthread_mutex_unlock (&sim_timer_lock);
pthread_mutex_lock (&sim_asynch_lock); pthread_mutex_lock (&sim_asynch_lock);
fprintf (st, "asynchronous pending event queue\n"); fprintf (st, "asynchronous pending event queue\n");
@ -3090,6 +3104,19 @@ char *sim_dname (DEVICE *dptr)
return (dptr->lname? dptr->lname: dptr->name); return (dptr->lname? dptr->lname: dptr->name);
} }
/* Get unit display name */
char *sim_uname (UNIT *uptr)
{
DEVICE *d = find_dev_from_unit(uptr);
static AIO_TLS char uname[CBUFSIZE];
if (d->numunits == 1)
return sim_dname (d);
sprintf (uname, "%s%d", sim_dname (d), (int)(uptr-d->units));
return uname;
}
/* Save command /* Save command
sa[ve] filename save state to specified file sa[ve] filename save state to specified file
@ -5303,6 +5330,7 @@ if (stop_cpu) /* stop CPU? */
UPDATE_SIM_TIME; /* update sim time */ UPDATE_SIM_TIME; /* update sim time */
if (sim_clock_queue == QUEUE_LIST_END) { /* queue empty? */ if (sim_clock_queue == QUEUE_LIST_END) { /* queue empty? */
sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */ sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */
sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Queue Emptry New Interval = %d\n", sim_interval);
return SCPE_OK; return SCPE_OK;
} }
do { do {
@ -5314,6 +5342,7 @@ do {
sim_interval = sim_clock_queue->time; sim_interval = sim_clock_queue->time;
else else
sim_interval = noqueue_time = NOQUEUE_WAIT; sim_interval = noqueue_time = NOQUEUE_WAIT;
sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Event for %s\n", sim_uname (uptr));
AIO_EVENT_BEGIN(uptr); AIO_EVENT_BEGIN(uptr);
if (uptr->action != NULL) if (uptr->action != NULL)
reason = uptr->action (uptr); reason = uptr->action (uptr);
@ -5324,9 +5353,12 @@ do {
(sim_interval <= 0) && (sim_interval <= 0) &&
(sim_clock_queue != QUEUE_LIST_END)); (sim_clock_queue != QUEUE_LIST_END));
if (sim_clock_queue == QUEUE_LIST_END) /* queue empty? */ if (sim_clock_queue == QUEUE_LIST_END) { /* queue empty? */
sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */ sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */
sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Queue Complete New Interval = %d\n", sim_interval);
}
else
sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Queue Complete New Interval = %d(%s)\n", sim_interval, sim_uname(sim_clock_queue));
return reason; return reason;
} }
@ -5354,6 +5386,8 @@ if (sim_is_active_bool (uptr)) /* already active? */
return SCPE_OK; return SCPE_OK;
UPDATE_SIM_TIME; /* update sim time */ UPDATE_SIM_TIME; /* update sim time */
sim_debug (SIM_DBG_ACTIVATE, sim_dflt_dev, "Activating %s delay=%d\n", sim_uname (uptr), event_time);
prvptr = NULL; prvptr = NULL;
accum = 0; accum = 0;
for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) { for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {

1
scp.h
View file

@ -103,6 +103,7 @@ t_stat deassign_device (DEVICE *dptr);
t_stat reset_all (uint32 start_device); t_stat reset_all (uint32 start_device);
t_stat reset_all_p (uint32 start_device); t_stat reset_all_p (uint32 start_device);
char *sim_dname (DEVICE *dptr); char *sim_dname (DEVICE *dptr);
char *sim_uname (UNIT *dptr);
t_stat get_yn (char *ques, t_stat deflt); t_stat get_yn (char *ques, t_stat deflt);
char *get_sim_opt (int32 opt, char *cptr, t_stat *st); char *get_sim_opt (int32 opt, char *cptr, t_stat *st);
char *get_glyph (char *iptr, char *optr, char mchar); char *get_glyph (char *iptr, char *optr, char mchar);

View file

@ -527,6 +527,9 @@ struct sim_debtab {
#define DEBUG_PRI(d,m) (sim_deb && (d.dctrl & (m))) #define DEBUG_PRI(d,m) (sim_deb && (d.dctrl & (m)))
#define DEBUG_PRJ(d,m) (sim_deb && (d->dctrl & (m))) #define DEBUG_PRJ(d,m) (sim_deb && (d->dctrl & (m)))
#define SIM_DBG_EVENT 0x10000
#define SIM_DBG_ACTIVATE 0x20000
/* File Reference */ /* File Reference */
struct sim_fileref { struct sim_fileref {
char name[CBUFSIZE]; /* file name */ char name[CBUFSIZE]; /* file name */
@ -579,6 +582,7 @@ typedef struct sim_fileref FILEREF;
#include "sim_timer.h" #include "sim_timer.h"
#include "sim_ether.h" #include "sim_ether.h"
#include "sim_fio.h" #include "sim_fio.h"
#include "sim_tmxr.h"
/* Asynch/Threaded I/O support */ /* Asynch/Threaded I/O support */
@ -598,11 +602,25 @@ extern pthread_cond_t sim_tmxr_poll_cond;
extern pthread_mutex_t sim_tmxr_poll_lock; extern pthread_mutex_t sim_tmxr_poll_lock;
extern pthread_t sim_asynch_main_threadid; extern pthread_t sim_asynch_main_threadid;
extern UNIT * volatile sim_asynch_queue; extern UNIT * volatile sim_asynch_queue;
extern UNIT * volatile sim_clock_cosched_queue;
extern volatile t_bool sim_idle_wait; extern volatile t_bool sim_idle_wait;
extern int32 sim_asynch_check; extern int32 sim_asynch_check;
extern int32 sim_asynch_latency; extern int32 sim_asynch_latency;
extern int32 sim_asynch_inst_latency; extern int32 sim_asynch_inst_latency;
/* Thread local storage */
#if defined(__GNUC__) && !defined(__APPLE__)
#define AIO_TLS __thread
#elif defined(__DECC_VER) || defined(_MSC_VER)
#define AIO_TLS __declspec(thread)
#else
/* Other compiler environment, then don't worry about thread local storage. */
/* It is primarily used only used in debugging messages */
#define AIO_TLS
#endif
#define AIO_INIT \ #define AIO_INIT \
if (1) { \ if (1) { \
sim_asynch_main_threadid = pthread_self(); \ sim_asynch_main_threadid = pthread_self(); \
@ -662,6 +680,23 @@ extern int32 sim_asynch_inst_latency;
} \ } \
if ((uptr)->next == NULL) \ if ((uptr)->next == NULL) \
(uptr)->a_due_time = (uptr)->a_usec_delay = 0; \ (uptr)->a_due_time = (uptr)->a_usec_delay = 0; \
else { \
nptr = QUEUE_LIST_END; \
if ((uptr) == sim_clock_cosched_queue) { \
sim_clock_cosched_queue = (uptr)->next; \
(uptr)->next = NULL; \
} \
else \
for (cptr = sim_clock_cosched_queue; \
(cptr != QUEUE_LIST_END); \
cptr = cptr->next) \
if (cptr->next == (uptr)) { \
cptr->next = (uptr)->next; \
nptr = cptr; \
(uptr)->next = NULL; \
break; \
} \
} \
pthread_mutex_unlock (&sim_timer_lock); \ pthread_mutex_unlock (&sim_timer_lock); \
} \ } \
} \ } \
@ -674,10 +709,12 @@ extern int32 sim_asynch_inst_latency;
cptr != QUEUE_LIST_END; \ cptr != QUEUE_LIST_END; \
cptr = cptr->next) \ cptr = cptr->next) \
if ((uptr) == cptr) { \ if ((uptr) == cptr) { \
int32 inst_per_sec = sim_timer_inst_per_sec (); \ double inst_per_sec = sim_timer_inst_per_sec (); \
int32 result; \ int32 result; \
\ \
result = (int32)(((uptr)->a_due_time - sim_timenow_double())*inst_per_sec);\ result = (int32)(((uptr)->a_due_time - sim_timenow_double())*inst_per_sec);\
if (result < 0) \
result = 0; \
pthread_mutex_unlock (&sim_timer_lock); \ pthread_mutex_unlock (&sim_timer_lock); \
return result + 1; \ return result + 1; \
} \ } \
@ -756,10 +793,8 @@ extern int32 sim_asynch_inst_latency;
} else (void)0 } else (void)0
#define AIO_ACTIVATE(caller, uptr, event_time) \ #define AIO_ACTIVATE(caller, uptr, event_time) \
if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \ if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \
DEVICE *d; \ UNIT *ouptr = (uptr); \
if (sim_deb) \ sim_debug (TIMER_DBG_QUEUE, &sim_timer_dev, "asynch event on %s after %d instructions\n", sim_uname(uptr), event_time);\
d = find_dev_from_unit(uptr); \
sim_debug (TIMER_DBG_QUEUE, &sim_timer_dev, "asynch event on %s after %d instructions\n", d->name, event_time);\
if (uptr->a_next) { /* already queued? */ \ if (uptr->a_next) { /* already queued? */ \
uptr->a_activate_call = sim_activate_abs; \ uptr->a_activate_call = sim_activate_abs; \
} else { \ } else { \
@ -781,7 +816,7 @@ extern int32 sim_asynch_inst_latency;
} \ } \
sim_asynch_check = 0; /* try to force check */ \ sim_asynch_check = 0; /* try to force check */ \
if (sim_idle_wait) { \ if (sim_idle_wait) { \
sim_debug (TIMER_DBG_IDLE, &sim_timer_dev, "waking due to event on %s after %d instructions\n", d->name, event_time);\ sim_debug (TIMER_DBG_IDLE, &sim_timer_dev, "waking due to event on %s after %d instructions\n", sim_uname(ouptr), event_time);\
pthread_cond_signal (&sim_idle_wake); \ pthread_cond_signal (&sim_idle_wake); \
} \ } \
return SCPE_OK; \ return SCPE_OK; \
@ -858,6 +893,7 @@ extern int32 sim_asynch_inst_latency;
#define AIO_EVENT_BEGIN(uptr) #define AIO_EVENT_BEGIN(uptr)
#define AIO_EVENT_COMPLETE(uptr, reason) #define AIO_EVENT_COMPLETE(uptr, reason)
#define AIO_SET_INTERRUPT_LATENCY(instpersec) #define AIO_SET_INTERRUPT_LATENCY(instpersec)
#define AIO_TLS
#endif /* SIM_ASYNCH_IO */ #endif /* SIM_ASYNCH_IO */
#endif #endif

View file

@ -356,7 +356,7 @@ patch date module(s) and fix(es)
- moved all Qbus devices to BR4; deleted RP definitions - moved all Qbus devices to BR4; deleted RP definitions
/* V3.8 revision history V3.8 revision history
1 08-Feb-09 scp.c: 1 08-Feb-09 scp.c:
- revised RESTORE unit logic for consistency - revised RESTORE unit logic for consistency
@ -709,7 +709,7 @@ patch date module(s) and fix(es)
- fixed declarations (Mark Pizzolato) - fixed declarations (Mark Pizzolato)
/* V3.7 revision history V3.7 revision history
3 02-Sep-07 scp.c: 3 02-Sep-07 scp.c:
- fixed bug in SET THROTTLE command - fixed bug in SET THROTTLE command
@ -981,7 +981,7 @@ patch date module(s) and fix(es)
- fixed operand order in EIS instructions (W.F.J. Mueller) - fixed operand order in EIS instructions (W.F.J. Mueller)
/* V3.6 revision history V3.6 revision history
1 25-Jul-06 sim_console.c: 1 25-Jul-06 sim_console.c:
- implemented SET/SHOW PCHAR - implemented SET/SHOW PCHAR
@ -1155,7 +1155,7 @@ patch date module(s) and fix(es)
- fixed bug in reported VA on faulting cross-page write - fixed bug in reported VA on faulting cross-page write
/* V3.5 revision history V3.5 revision history
patch date module(s) and fix(es) patch date module(s) and fix(es)
@ -1291,7 +1291,7 @@ patch date module(s) and fix(es)
vax_io.c: revised autoconfiguration algorithm and interface vax_io.c: revised autoconfiguration algorithm and interface
/* V3.4 revision history V3.4 revision history
0 01-May-04 scp.c: 0 01-May-04 scp.c:
- fixed ASSERT code - fixed ASSERT code
@ -1329,7 +1329,7 @@ patch date module(s) and fix(es)
pdp11_tu.c: fixed error reporting pdp11_tu.c: fixed error reporting
/* V3.3 revision history V3.3 revision history
2 08-Mar-05 scp.c: added ASSERT command (Dave Bryan) 2 08-Mar-05 scp.c: added ASSERT command (Dave Bryan)
@ -1581,7 +1581,7 @@ patch date module(s) and fix(es)
- split from vax_sys.c - split from vax_sys.c
- removed PTR, PTP - removed PTR, PTP
/* V3.2 revision history V3.2 revision history
3 03-Sep-04 scp.c: 3 03-Sep-04 scp.c:
- added ECHO command (Dave Bryan) - added ECHO command (Dave Bryan)
@ -1780,7 +1780,7 @@ patch date module(s) and fix(es)
- added PC read fault in EXTxV - added PC read fault in EXTxV
- fixed PC write fault in INSV - fixed PC write fault in INSV
/* V3.1 revision history V3.1 revision history
0 29-Dec-03 sim_defs.h, scp.c: added output stall status 0 29-Dec-03 sim_defs.h, scp.c: added output stall status
@ -1893,7 +1893,7 @@ patch date module(s) and fix(es)
- revised instruction decoding - revised instruction decoding
- added instruction history - added instruction history
/* V3.0 revision history V3.0 revision history
2 15-Sep-03 scp.c: 2 15-Sep-03 scp.c:
- fixed end-of-file problem in dep, idep - fixed end-of-file problem in dep, idep
@ -2059,7 +2059,7 @@ patch date module(s) and fix(es)
gri_cpu.c: fixed bug in SC queue pointer management gri_cpu.c: fixed bug in SC queue pointer management
/* V2.10 revision history V2.10 revision history
4 03-Mar-03 scp.c 4 03-Mar-03 scp.c
- added .ini startup file capability - added .ini startup file capability
@ -2357,7 +2357,7 @@ patch date module(s) and fix(es)
IBM 1620: first release IBM 1620: first release
/* V2.9 revision history V2.9 revision history
11 20-Jul-02 i1401_mt.c: on read, end of record stores group mark 11 20-Jul-02 i1401_mt.c: on read, end of record stores group mark
without word mark (Van Snyder) without word mark (Van Snyder)

View file

@ -79,6 +79,8 @@
routines are not. routines are not.
*/ */
#define NOT_MUX_USING_CODE /* sim_tmxr library provider or agnostic */
#include "sim_defs.h" #include "sim_defs.h"
#include <ctype.h> #include <ctype.h>
#include <math.h> #include <math.h>
@ -99,7 +101,13 @@ static uint32 sim_throt_val = 0;
static uint32 sim_throt_state = 0; static uint32 sim_throt_state = 0;
static uint32 sim_throt_sleep_time = 0; static uint32 sim_throt_sleep_time = 0;
static int32 sim_throt_wait = 0; static int32 sim_throt_wait = 0;
static UNIT *sim_clock_unit = NULL;
static t_bool sim_asynch_timer =
#if defined (SIM_ASYNCH_IO)
TRUE;
#else
FALSE;
#endif
extern int32 sim_interval, sim_switches; extern int32 sim_interval, sim_switches;
extern FILE *sim_log; extern FILE *sim_log;
extern UNIT *sim_clock_queue; extern UNIT *sim_clock_queue;
@ -577,17 +585,20 @@ int32 sim_rtcn_calb (int32 ticksper, int32 tmr)
uint32 new_rtime, delta_rtime; uint32 new_rtime, delta_rtime;
int32 delta_vtime; int32 delta_vtime;
double new_gtime; double new_gtime;
int32 new_currd;
if ((tmr < 0) || (tmr >= SIM_NTIMERS)) if ((tmr < 0) || (tmr >= SIM_NTIMERS))
return 10000; return 10000;
rtc_hz[tmr] = ticksper; rtc_hz[tmr] = ticksper;
rtc_ticks[tmr] = rtc_ticks[tmr] + 1; /* count ticks */ rtc_ticks[tmr] = rtc_ticks[tmr] + 1; /* count ticks */
if (rtc_ticks[tmr] < ticksper) /* 1 sec yet? */ if (rtc_ticks[tmr] < ticksper) { /* 1 sec yet? */
return rtc_currd[tmr]; return rtc_currd[tmr];
}
rtc_ticks[tmr] = 0; /* reset ticks */ rtc_ticks[tmr] = 0; /* reset ticks */
rtc_elapsed[tmr] = rtc_elapsed[tmr] + 1; /* count sec */ rtc_elapsed[tmr] = rtc_elapsed[tmr] + 1; /* count sec */
if (!rtc_avail) /* no timer? */ if (!rtc_avail) { /* no timer? */
return rtc_currd[tmr]; return rtc_currd[tmr];
}
new_rtime = sim_os_msec (); /* wall time */ new_rtime = sim_os_msec (); /* wall time */
sim_debug (DBG_TRC, &sim_timer_dev, "sim_rtcn_calb(ticksper=%d, tmr=%d) rtime=%d\n", ticksper, tmr, new_rtime); sim_debug (DBG_TRC, &sim_timer_dev, "sim_rtcn_calb(ticksper=%d, tmr=%d) rtime=%d\n", ticksper, tmr, new_rtime);
if (sim_idle_idled) { if (sim_idle_idled) {
@ -609,21 +620,32 @@ rtc_rtime[tmr] = new_rtime; /* adv wall time */
rtc_vtime[tmr] = rtc_vtime[tmr] + 1000; /* adv sim time */ rtc_vtime[tmr] = rtc_vtime[tmr] + 1000; /* adv sim time */
if (delta_rtime > 30000) { /* gap too big? */ if (delta_rtime > 30000) { /* gap too big? */
rtc_currd[tmr] = rtc_initd[tmr]; rtc_currd[tmr] = rtc_initd[tmr];
rtc_gtime[tmr] = sim_gtime(); /* save instruction time */
sim_debug (DBG_CAL, &sim_timer_dev, "gap too big: delta = %d - result: %d\n", delta_rtime, rtc_initd[tmr]); sim_debug (DBG_CAL, &sim_timer_dev, "gap too big: delta = %d - result: %d\n", delta_rtime, rtc_initd[tmr]);
return rtc_initd[tmr]; /* can't calibr */ return rtc_initd[tmr]; /* can't calibr */
} }
new_gtime = sim_gtime(); new_gtime = sim_gtime();
if (sim_asynch_enabled) if (sim_asynch_enabled && sim_asynch_timer)
if (rtc_elapsed[tmr] > sim_idle_stable) { if (rtc_elapsed[tmr] > sim_idle_stable) {
/* An asynchronous clock, merely needs to divide the number of */ /* An asynchronous clock, merely needs to divide the number of */
/* instructions actually executed by the clock rate. */ /* instructions actually executed by the clock rate. */
rtc_currd[tmr] = (int32)((new_gtime - rtc_gtime[tmr])/ticksper); new_currd = (int32)((new_gtime - rtc_gtime[tmr])/ticksper);
/* avoid excessive swings in the calibrated result */
if (new_currd > 10*rtc_currd[tmr]) /* don't swing big too fast */
new_currd = 10*rtc_currd[tmr];
else
if (new_currd < rtc_currd[tmr]/10) /* don't swing small too fast */
new_currd = rtc_currd[tmr]/10;
rtc_currd[tmr] = new_currd;
rtc_gtime[tmr] = new_gtime; /* save instruction time */ rtc_gtime[tmr] = new_gtime; /* save instruction time */
if (rtc_currd[tmr] == 127)
sim_debug (DBG_CAL, &sim_timer_dev, "asynch calibration small: %d\n", rtc_currd[tmr]);
sim_debug (DBG_CAL, &sim_timer_dev, "asynch calibration result: %d\n", rtc_currd[tmr]); sim_debug (DBG_CAL, &sim_timer_dev, "asynch calibration result: %d\n", rtc_currd[tmr]);
return rtc_currd[tmr]; /* calibrated result */ return rtc_currd[tmr]; /* calibrated result */
} }
else { else {
rtc_currd[tmr] = rtc_initd[tmr]; rtc_currd[tmr] = rtc_initd[tmr];
rtc_gtime[tmr] = new_gtime; /* save instruction time */
sim_debug (DBG_CAL, &sim_timer_dev, "asynch not stable calibration result: %d\n", rtc_initd[tmr]); sim_debug (DBG_CAL, &sim_timer_dev, "asynch not stable calibration result: %d\n", rtc_initd[tmr]);
return rtc_initd[tmr]; /* initial result until stable */ return rtc_initd[tmr]; /* initial result until stable */
} }
@ -685,7 +707,7 @@ for (tmr=0; tmr<SIM_NTIMERS; ++tmr) {
if (0 == rtc_initd[tmr]) if (0 == rtc_initd[tmr])
continue; continue;
fprintf (st, "%s%sTimer %d:\n", sim_asynch_enabled ? "Asynchronous " : "", rtc_hz[tmr] ? "Calibrated " : "Uncalibrated ", tmr); fprintf (st, "%s%sTimer %d:\n", (sim_asynch_enabled && sim_asynch_timer) ? "Asynchronous " : "", rtc_hz[tmr] ? "Calibrated " : "Uncalibrated ", tmr);
if (rtc_hz[tmr]) { if (rtc_hz[tmr]) {
fprintf (st, " Running at: %dhz\n", rtc_hz[tmr]); fprintf (st, " Running at: %dhz\n", rtc_hz[tmr]);
fprintf (st, " Ticks in current second: %d\n", rtc_ticks[tmr]); fprintf (st, " Ticks in current second: %d\n", rtc_ticks[tmr]);
@ -693,7 +715,7 @@ for (tmr=0; tmr<SIM_NTIMERS; ++tmr) {
fprintf (st, " Seconds Running: %u\n", rtc_elapsed[tmr]); fprintf (st, " Seconds Running: %u\n", rtc_elapsed[tmr]);
fprintf (st, " Calibrations: %u\n", rtc_calibrations[tmr]); fprintf (st, " Calibrations: %u\n", rtc_calibrations[tmr]);
fprintf (st, " Instruction Time: %.0f\n", rtc_gtime[tmr]); fprintf (st, " Instruction Time: %.0f\n", rtc_gtime[tmr]);
if (!sim_asynch_enabled) { if (!(sim_asynch_enabled && sim_asynch_timer)) {
fprintf (st, " Real Time: %u\n", rtc_rtime[tmr]); fprintf (st, " Real Time: %u\n", rtc_rtime[tmr]);
fprintf (st, " Virtual Time: %u\n", rtc_vtime[tmr]); fprintf (st, " Virtual Time: %u\n", rtc_vtime[tmr]);
fprintf (st, " Next Interval: %u\n", rtc_nxintv[tmr]); fprintf (st, " Next Interval: %u\n", rtc_nxintv[tmr]);
@ -706,6 +728,7 @@ for (tmr=0; tmr<SIM_NTIMERS; ++tmr) {
} }
return SCPE_OK; return SCPE_OK;
} }
REG sim_timer_reg[] = { REG sim_timer_reg[] = {
{ DRDATA (TICKS_PER_SEC, rtc_hz[0], 32), PV_RSPC|REG_RO}, { DRDATA (TICKS_PER_SEC, rtc_hz[0], 32), PV_RSPC|REG_RO},
{ DRDATA (INSTS_PER_TICK, rtc_currd[0], 32), PV_RSPC|REG_RO}, { DRDATA (INSTS_PER_TICK, rtc_currd[0], 32), PV_RSPC|REG_RO},
@ -725,7 +748,39 @@ REG sim_timer_reg[] = {
{ NULL } { NULL }
}; };
/* Clear, Set and show asynch */
/* Clear asynch */
t_stat sim_timer_clr_async (UNIT *uptr, int32 val, char *cptr, void *desc)
{
if (sim_asynch_timer) {
sim_asynch_timer = FALSE;
sim_timer_change_asynch ();
}
return SCPE_OK;
}
t_stat sim_timer_set_async (UNIT *uptr, int32 val, char *cptr, void *desc)
{
if (!sim_asynch_timer) {
sim_asynch_timer = TRUE;
sim_timer_change_asynch ();
}
return SCPE_OK;
}
t_stat sim_timer_show_async (FILE *st, UNIT *uptr, int32 val, void *desc)
{
fprintf (st, "%s", (sim_asynch_enabled && sim_asynch_timer) ? "Asynchronous" : "Synchronous");
return SCPE_OK;
}
MTAB sim_timer_mod[] = { MTAB sim_timer_mod[] = {
#if defined (SIM_ASYNCH_IO)
{ MTAB_XTD|MTAB_VDV, 0, "ASYNC", "ASYNC", &sim_timer_set_async, &sim_timer_show_async },
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOASYNC", &sim_timer_clr_async, NULL },
#endif
{ 0 }, { 0 },
}; };
@ -755,11 +810,11 @@ static uint32 cyc_ms = 0;
uint32 w_ms, w_idle, act_ms; uint32 w_ms, w_idle, act_ms;
int32 act_cyc; int32 act_cyc;
sim_idle_idled = TRUE; /* record idle attempt */ //sim_idle_idled = TRUE; /* record idle attempt */
if ((!sim_idle_enab) || /* idling disabled */ if ((!sim_idle_enab) || /* idling disabled */
((sim_clock_queue == QUEUE_LIST_END) && /* or clock queue empty? */ ((sim_clock_queue == QUEUE_LIST_END) && /* or clock queue empty? */
#if defined(SIM_ASYNCH_IO) #if defined(SIM_ASYNCH_IO)
(!sim_asynch_enabled)) || /* and not asynch? */ (!(sim_asynch_enabled && sim_asynch_timer)))|| /* and not asynch? */
#else #else
(TRUE)) || (TRUE)) ||
#endif #endif
@ -789,10 +844,8 @@ if (w_idle == 0) { /* none? */
} }
if (sim_clock_queue == QUEUE_LIST_END) if (sim_clock_queue == QUEUE_LIST_END)
sim_debug (DBG_IDL, &sim_timer_dev, "sleeping for %d ms - pending event in %d instructions\n", w_ms, sim_interval); sim_debug (DBG_IDL, &sim_timer_dev, "sleeping for %d ms - pending event in %d instructions\n", w_ms, sim_interval);
else { else
DEVICE *d = find_dev_from_unit(sim_clock_queue); sim_debug (DBG_IDL, &sim_timer_dev, "sleeping for %d ms - pending event on %s in %d instructions\n", w_ms, sim_uname(sim_clock_queue), sim_interval);
sim_debug (DBG_IDL, &sim_timer_dev, "sleeping for %d ms - pending event on %s in %d instructions\n", w_ms, d->name, sim_interval);
}
act_ms = SIM_IDLE_MS_SLEEP (w_ms); /* wait */ act_ms = SIM_IDLE_MS_SLEEP (w_ms); /* wait */
act_cyc = act_ms * cyc_ms; act_cyc = act_ms * cyc_ms;
if (act_ms < w_ms) /* awakened early? */ if (act_ms < w_ms) /* awakened early? */
@ -802,10 +855,8 @@ if (sim_interval > act_cyc)
else sim_interval = 0; /* or fire immediately */ else sim_interval = 0; /* or fire immediately */
if (sim_clock_queue == QUEUE_LIST_END) if (sim_clock_queue == QUEUE_LIST_END)
sim_debug (DBG_IDL, &sim_timer_dev, "slept for %d ms - pending event in %d instructions\n", act_ms, sim_interval); sim_debug (DBG_IDL, &sim_timer_dev, "slept for %d ms - pending event in %d instructions\n", act_ms, sim_interval);
else { else
DEVICE *d = find_dev_from_unit(sim_clock_queue); sim_debug (DBG_IDL, &sim_timer_dev, "slept for %d ms - pending event on %s in %d instructions\n", act_ms, sim_uname(sim_clock_queue), sim_interval);
sim_debug (DBG_IDL, &sim_timer_dev, "slept for %d ms - pending event on %s in %d instructions\n", act_ms, d->name, sim_interval);
}
return TRUE; return TRUE;
} }
@ -1099,21 +1150,19 @@ sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - starting\n");
pthread_mutex_lock (&sim_timer_lock); pthread_mutex_lock (&sim_timer_lock);
pthread_cond_signal (&sim_timer_startup_cond); /* Signal we're ready to go */ pthread_cond_signal (&sim_timer_startup_cond); /* Signal we're ready to go */
while (sim_asynch_enabled && sim_is_running) { while (sim_asynch_enabled && sim_asynch_timer && sim_is_running) {
struct timespec start_time, stop_time; struct timespec start_time, stop_time;
struct timespec due_time; struct timespec due_time;
double wait_usec; double wait_usec;
int32 inst_delay; int32 inst_delay;
int32 inst_per_sec; double inst_per_sec;
UNIT *uptr; UNIT *uptr;
DEVICE *d;
if (sim_wallclock_entry) { /* something to insert in queue? */ if (sim_wallclock_entry) { /* something to insert in queue? */
UNIT *cptr, *prvptr; UNIT *cptr, *prvptr;
d = find_dev_from_unit(sim_wallclock_entry); sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - timing %s for %d usec\n",
sim_debug (DBG_TIM, &sim_timer_dev, (d->numunits > 1) ? "_timer_thread() - timing %s%d for %d usec\n" : "_timer_thread() - timing %s%.0d for %d usec\n", sim_uname(sim_wallclock_entry), sim_wallclock_entry->time);
d->name, (int)(sim_wallclock_entry-d->units), sim_wallclock_entry->time);
uptr = sim_wallclock_entry; uptr = sim_wallclock_entry;
sim_wallclock_entry = NULL; sim_wallclock_entry = NULL;
@ -1140,7 +1189,6 @@ while (sim_asynch_enabled && sim_is_running) {
/* the goal being to let the last fractional part of the due time */ /* the goal being to let the last fractional part of the due time */
/* be done by counting instructions */ /* be done by counting instructions */
_double_to_timespec (&due_time, sim_wallclock_queue->a_due_time-(((double)sim_idle_rate_ms)*0.0005)); _double_to_timespec (&due_time, sim_wallclock_queue->a_due_time-(((double)sim_idle_rate_ms)*0.0005));
d = find_dev_from_unit(sim_wallclock_queue); /* find this before waiting */
} }
else { else {
due_time.tv_sec = 0x7FFFFFFF; /* Sometime when 32 bit time_t wraps */ due_time.tv_sec = 0x7FFFFFFF; /* Sometime when 32 bit time_t wraps */
@ -1171,9 +1219,16 @@ while (sim_asynch_enabled && sim_is_running) {
inst_delay = (int32)(inst_per_sec*(_timespec_to_double(&due_time)-_timespec_to_double(&stop_time))); inst_delay = (int32)(inst_per_sec*(_timespec_to_double(&due_time)-_timespec_to_double(&stop_time)));
uptr->a_last_fired_time = uptr->a_due_time; uptr->a_last_fired_time = uptr->a_due_time;
} }
sim_debug (DBG_TIM, &sim_timer_dev, (d->numunits > 1) ? "_timer_thread() - slept %.0fms - activating(%s%d,%d)\n" : "_timer_thread() - slept %.0fms - activating(%s%.0d,%d)\n", sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - slept %.0fms - activating(%s,%d)\n",
1000.0*(_timespec_to_double (&stop_time)-_timespec_to_double (&start_time)), d->name, (int)(uptr-d->units), inst_delay); 1000.0*(_timespec_to_double (&stop_time)-_timespec_to_double (&start_time)), sim_uname(uptr), inst_delay);
sim_activate (uptr, inst_delay); sim_activate (uptr, inst_delay);
if (sim_clock_unit == uptr)
while (sim_clock_cosched_queue != QUEUE_LIST_END) {
uptr = sim_clock_cosched_queue;
sim_clock_cosched_queue = uptr->next;
uptr->next = NULL;
sim_activate (uptr, inst_delay);
}
} }
else /* Something wants to adjust the queue */ else /* Something wants to adjust the queue */
if (sim_timer_event_canceled) if (sim_timer_event_canceled)
@ -1195,7 +1250,7 @@ void sim_start_timer_services (void)
{ {
#if defined(SIM_ASYNCH_IO) #if defined(SIM_ASYNCH_IO)
pthread_mutex_lock (&sim_timer_lock); pthread_mutex_lock (&sim_timer_lock);
if (sim_asynch_enabled) { if (sim_asynch_enabled && sim_asynch_timer) {
pthread_attr_t attr; pthread_attr_t attr;
UNIT *cptr; UNIT *cptr;
double delta_due_time; double delta_due_time;
@ -1247,7 +1302,7 @@ else
t_stat sim_timer_change_asynch (void) t_stat sim_timer_change_asynch (void)
{ {
#if defined(SIM_ASYNCH_IO) #if defined(SIM_ASYNCH_IO)
if (sim_asynch_enabled) if (sim_asynch_enabled && sim_asynch_timer)
sim_start_timer_services (); sim_start_timer_services ();
else { else {
UNIT *uptr; UNIT *uptr;
@ -1270,13 +1325,17 @@ else {
return SCPE_OK; return SCPE_OK;
} }
int32 sim_timer_inst_per_sec (void) /* Instruction Execution rate. */
/* returns a double since it is mostly used in double expressions and
to avoid overflow if/when strange timing delays might produce unexpected results */
double sim_timer_inst_per_sec (void)
{ {
int32 inst_per_sec; double inst_per_sec = SIM_INITIAL_IPS;
if (sim_calb_tmr == -1) if (sim_calb_tmr == -1)
return SIM_INITIAL_IPS; return inst_per_sec;
inst_per_sec = rtc_currd[sim_calb_tmr]*rtc_hz[sim_calb_tmr]; inst_per_sec = ((double)rtc_currd[sim_calb_tmr])*rtc_hz[sim_calb_tmr];
if (0 == inst_per_sec) if (0 == inst_per_sec)
inst_per_sec = SIM_INITIAL_IPS; inst_per_sec = SIM_INITIAL_IPS;
return inst_per_sec; return inst_per_sec;
@ -1285,23 +1344,20 @@ return inst_per_sec;
t_stat sim_timer_activate_after (UNIT *uptr, int32 usec_delay) t_stat sim_timer_activate_after (UNIT *uptr, int32 usec_delay)
{ {
int32 inst_delay; int32 inst_delay;
int32 inst_per_sec; double inst_per_sec;
DEVICE *d;
AIO_VALIDATE; AIO_VALIDATE;
if (sim_is_active_bool (uptr)) /* already active? */ if (sim_is_active_bool (uptr)) /* already active? */
return SCPE_OK; return SCPE_OK;
inst_per_sec = sim_timer_inst_per_sec (); inst_per_sec = sim_timer_inst_per_sec ();
inst_delay = (int32)((((double)inst_per_sec)*usec_delay)/1000000.0); inst_delay = (int32)((inst_per_sec*usec_delay)/1000000.0);
#if defined(SIM_ASYNCH_IO) #if defined(SIM_ASYNCH_IO)
if ((sim_calb_tmr == -1) || /* if No timer initialized if ((sim_calb_tmr == -1) || /* if No timer initialized */
(inst_delay < rtc_currd[sim_calb_tmr]) || /* or sooner than next clock tick? */ (inst_delay < rtc_currd[sim_calb_tmr]) || /* or sooner than next clock tick? */
(rtc_elapsed[sim_calb_tmr] < sim_idle_stable) || /* or not idle stable yet */ (rtc_elapsed[sim_calb_tmr] < sim_idle_stable) || /* or not idle stable yet */
(!sim_asynch_enabled)) { /* or asynch disabled */ (!(sim_asynch_enabled && sim_asynch_timer))) { /* or asynch disabled */
if (sim_deb) sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after() - activating %s after %d instructions\n",
d = find_dev_from_unit(uptr); sim_uname(uptr), inst_delay);
sim_debug (DBG_TIM, &sim_timer_dev, (d->numunits > 1) ? "sim_timer_activate_after() - activating %s%d after %d instructions\n" : "sim_timer_activate_after() - activating %s%.0d after %d instructions\n" ,
d->name, (int)(uptr-d->units), inst_delay);
return _sim_activate (uptr, inst_delay); /* queue it now */ return _sim_activate (uptr, inst_delay); /* queue it now */
} }
if (1) { if (1) {
@ -1349,10 +1405,8 @@ if (1) {
} }
uptr->time = usec_delay; uptr->time = usec_delay;
if (sim_deb) sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after() - queue addition %s at %.6f\n",
d = find_dev_from_unit(uptr); sim_uname(uptr), uptr->a_due_time);
sim_debug (DBG_TIM, &sim_timer_dev, (d->numunits > 1) ? "sim_timer_activate_after() - queue addition %s%d at %.6f\n" : "sim_timer_activate_after() - queue addition %s%.0d at %.6f\n" ,
d->name, (int)(uptr-d->units), uptr->a_due_time);
} }
pthread_mutex_lock (&sim_timer_lock); pthread_mutex_lock (&sim_timer_lock);
sim_wallclock_entry = uptr; sim_wallclock_entry = uptr;
@ -1364,3 +1418,36 @@ return _sim_activate (uptr, inst_delay); /* queue it now */
#endif #endif
} }
/* Clock coscheduling routines */
t_stat sim_register_clock_unit (UNIT *uptr)
{
sim_clock_unit = uptr;
return SCPE_OK;
}
t_stat sim_clock_coschedule (UNIT *uptr, int32 interval)
{
if (NULL == sim_clock_unit)
return sim_activate (uptr, interval);
else
if (sim_asynch_enabled && sim_asynch_timer) {
if (!sim_is_active_bool (uptr)) { /* already active? */
#if defined(SIM_ASYNCH_IO)
sim_debug (DBG_TIM, &sim_timer_dev, "sim_clock_coschedule() - queueing %s for clock co-schedule\n", sim_uname (uptr));
pthread_mutex_lock (&sim_timer_lock);
uptr->next = sim_clock_cosched_queue;
sim_clock_cosched_queue = uptr;
pthread_mutex_unlock (&sim_timer_lock);
#endif
}
return SCPE_OK;
}
else {
int32 t;
t = sim_is_active (sim_clock_unit);
return sim_activate (uptr, t? t - 1: interval);
}
}

View file

@ -111,7 +111,9 @@ void sim_start_timer_services (void);
void sim_stop_timer_services (void); void sim_stop_timer_services (void);
t_stat sim_timer_change_asynch (void); t_stat sim_timer_change_asynch (void);
t_stat sim_timer_activate_after (UNIT *uptr, int32 usec_delay); t_stat sim_timer_activate_after (UNIT *uptr, int32 usec_delay);
int32 sim_timer_inst_per_sec (void); t_stat sim_register_clock_unit (UNIT *uptr);
t_stat sim_clock_coschedule (UNIT *uptr, int32 interval);
double sim_timer_inst_per_sec (void);
extern t_bool sim_idle_enab; /* idle enabled flag */ extern t_bool sim_idle_enab; /* idle enabled flag */
extern volatile t_bool sim_idle_wait; /* idle waiting flag */ extern volatile t_bool sim_idle_wait; /* idle waiting flag */

View file

@ -699,6 +699,25 @@ for (i = 0; i < mp->lines; i++) { /* initialize lines */
return SCPE_OK; return SCPE_OK;
} }
/* Declare which unit polls for input
Inputs:
*mp = the mux
line = the line number
*uptr_poll = the unit which polls
Outputs:
none
Implementation note:
Only devices which poll on a unit different from the unit provided
at MUX attach time need call this function. Calling this API is
necessary for asynchronous multiplexer support and unnecessary
otherwise.
*/
t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll) t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll)
{ {
if ((line < 0) || (line >= mp->lines)) if ((line < 0) || (line >= mp->lines))
@ -720,6 +739,33 @@ else
return SCPE_OK; return SCPE_OK;
} }
/* Declare which unit polls for output
Inputs:
*mp = the mux
line = the line number
*uptr_poll = the unit which polls for output
Outputs:
none
Implementation note:
Only devices which poll on a unit different from the unit provided
at MUX attach time need call this function AND different from the
unit which polls for input. Calling this API is necessary for
asynchronous multiplexer support and unnecessary otherwise.
*/
t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll)
{
if ((line < 0) || (line >= mp->lines))
return SCPE_ARG;
mp->ldsc[line].o_uptr = uptr_poll;
return SCPE_OK;
}
static TMXR **tmxr_open_devices = NULL; static TMXR **tmxr_open_devices = NULL;
static int tmxr_open_device_count = 0; static int tmxr_open_device_count = 0;
@ -770,7 +816,7 @@ while (sim_asynch_enabled) {
if ((tmxr_open_device_count == 0) || (!sim_is_running)) { if ((tmxr_open_device_count == 0) || (!sim_is_running)) {
for (j=0; j<wait_count; ++j) { for (j=0; j<wait_count; ++j) {
sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Removing interest in %s%d. Other interest: %d\n", d->name, (int)(activated[j]-d->units), activated[j]->a_poll_waiter_count); sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Removing interest in %s. Other interest: %d\n", sim_uname(activated[j]), activated[j]->a_poll_waiter_count);
--activated[j]->a_poll_waiter_count; --activated[j]->a_poll_waiter_count;
--sim_tmxr_poll_count; --sim_tmxr_poll_count;
} }
@ -827,7 +873,7 @@ while (sim_asynch_enabled) {
mp->uptr->a_polling_now = TRUE; mp->uptr->a_polling_now = TRUE;
mp->uptr->a_poll_waiter_count = 0; mp->uptr->a_poll_waiter_count = 0;
d = find_dev_from_unit(mp->uptr); d = find_dev_from_unit(mp->uptr);
sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Activating %s%d to poll connect\n", d->name, (int)(mp->uptr-d->units)); sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Activating %s to poll connect\n", sim_uname(mp->uptr));
pthread_mutex_unlock (&sim_tmxr_poll_lock); pthread_mutex_unlock (&sim_tmxr_poll_lock);
_sim_activate (mp->uptr, 0); _sim_activate (mp->uptr, 0);
pthread_mutex_lock (&sim_tmxr_poll_lock); pthread_mutex_lock (&sim_tmxr_poll_lock);
@ -847,8 +893,8 @@ while (sim_asynch_enabled) {
mp->ldsc[j].uptr->a_polling_now = TRUE; mp->ldsc[j].uptr->a_polling_now = TRUE;
mp->ldsc[j].uptr->a_poll_waiter_count = 0; mp->ldsc[j].uptr->a_poll_waiter_count = 0;
d = find_dev_from_unit(mp->ldsc[j].uptr); d = find_dev_from_unit(mp->ldsc[j].uptr);
sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Line %d Activating %s%d to poll data: %d/%d\n", sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Line %d Activating %s to poll data: %d/%d\n",
j, d->name, (int)(mp->ldsc[j].uptr-d->units), tmxr_tqln(&mp->ldsc[j]), tmxr_rqln (&mp->ldsc[j])); j, sim_uname(mp->ldsc[j].uptr), tmxr_tqln(&mp->ldsc[j]), tmxr_rqln (&mp->ldsc[j]));
pthread_mutex_unlock (&sim_tmxr_poll_lock); pthread_mutex_unlock (&sim_tmxr_poll_lock);
_sim_activate (mp->ldsc[j].uptr, 0); _sim_activate (mp->ldsc[j].uptr, 0);
pthread_mutex_lock (&sim_tmxr_poll_lock); pthread_mutex_lock (&sim_tmxr_poll_lock);
@ -884,14 +930,14 @@ while (sim_asynch_enabled) {
activated[j]->a_polling_now = TRUE; activated[j]->a_polling_now = TRUE;
activated[j]->a_poll_waiter_count = 1; activated[j]->a_poll_waiter_count = 1;
d = find_dev_from_unit(activated[j]); d = find_dev_from_unit(activated[j]);
sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Activating for data %s%d\n", d->name, (int)(activated[j]-d->units)); sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Activating for data %s\n", sim_uname(activated[j]));
pthread_mutex_unlock (&sim_tmxr_poll_lock); pthread_mutex_unlock (&sim_tmxr_poll_lock);
_sim_activate (activated[j], 0); _sim_activate (activated[j], 0);
pthread_mutex_lock (&sim_tmxr_poll_lock); pthread_mutex_lock (&sim_tmxr_poll_lock);
} }
else { else {
d = find_dev_from_unit(activated[j]); d = find_dev_from_unit(activated[j]);
sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Already Activated %s%d %d times\n", d->name, (int)(activated[j]-d->units), activated[j]->a_poll_waiter_count); sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Already Activated %s%d %d times\n", sim_uname(activated[j]), activated[j]->a_poll_waiter_count);
++activated[j]->a_poll_waiter_count; ++activated[j]->a_poll_waiter_count;
} }
} }
@ -1040,10 +1086,8 @@ else {
for (j = 0; j < mp->lines; j++) { for (j = 0; j < mp->lines; j++) {
lp = mp->ldsc + j; lp = mp->ldsc + j;
fprintf (st, "Line: %d", j); fprintf (st, "Line: %d", j);
if (lp->uptr && (lp->uptr != lp->mp->uptr)) { if (lp->uptr && (lp->uptr != lp->mp->uptr))
DEVICE *dptr = find_dev_from_unit (lp->uptr); fprintf (st, " - Unit: %s\n", sim_uname (lp->uptr));
fprintf (st, " - Unit: %s%d\n", dptr->name, (int)(lp->uptr-dptr->units));
}
else else
fprintf (st, "\n"); fprintf (st, "\n");
if (!lp->conn) if (!lp->conn)
@ -1157,6 +1201,19 @@ return _sim_activate_after (uptr, usecs_walltime);
#endif #endif
} }
t_stat tmxr_clock_coschedule (UNIT *uptr, int32 interval)
{
#if defined(SIM_ASYNCH_IO)
if ((!(uptr->flags & UNIT_TM_POLL)) ||
(!sim_asynch_enabled)) {
return sim_clock_coschedule (uptr, interval);
}
return SCPE_OK;
#else
return sim_clock_coschedule (uptr, interval);
#endif
}
/* Stub examine and deposit */ /* Stub examine and deposit */
t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)

View file

@ -91,7 +91,8 @@ struct tmln {
char rbr[TMXR_MAXBUF]; /* rcv break */ char rbr[TMXR_MAXBUF]; /* rcv break */
char *txb; /* xmt buffer */ char *txb; /* xmt buffer */
TMXR *mp; /* back pointer to mux */ TMXR *mp; /* back pointer to mux */
UNIT *uptr; /* pointer to receive poll unit */ UNIT *uptr; /* input polling unit (default to mp->uptr) */
UNIT *o_uptr; /* output polling unit (default to lp->uptr)*/
}; };
struct tmxr { struct tmxr {
@ -101,7 +102,7 @@ struct tmxr {
TMLN *ldsc; /* line descriptors */ TMLN *ldsc; /* line descriptors */
int32 *lnorder; /* line connection order */ int32 *lnorder; /* line connection order */
DEVICE *dptr; /* multiplexer device */ DEVICE *dptr; /* multiplexer device */
UNIT *uptr; /* polling unit */ UNIT *uptr; /* polling unit (connection) */
char logfiletmpl[FILENAME_MAX]; /* template logfile name */ char logfiletmpl[FILENAME_MAX]; /* template logfile name */
int32 txcount; /* count of transmit bytes */ int32 txcount; /* count of transmit bytes */
int32 buffered; /* Buffered Line Behavior and Buffer Size Flag */ int32 buffered; /* Buffered Line Behavior and Buffer Size Flag */
@ -119,6 +120,7 @@ t_stat tmxr_close_master (TMXR *mp);
t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, char *cptr, t_bool async); t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, char *cptr, t_bool async);
t_stat tmxr_detach (TMXR *mp, UNIT *uptr); t_stat tmxr_detach (TMXR *mp, UNIT *uptr);
t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll); t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll);
t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll);
t_stat tmxr_set_console_input_unit (UNIT *uptr); t_stat tmxr_set_console_input_unit (UNIT *uptr);
t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
@ -140,6 +142,7 @@ t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, char* desc); t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, char* desc);
t_stat tmxr_activate (UNIT *uptr, int32 interval); t_stat tmxr_activate (UNIT *uptr, int32 interval);
t_stat tmxr_activate_after (UNIT *uptr, int32 usecs_walltime); t_stat tmxr_activate_after (UNIT *uptr, int32 usecs_walltime);
t_stat tmxr_clock_coschedule (UNIT *uptr, int32 interval);
t_stat tmxr_change_async (void); t_stat tmxr_change_async (void);
t_stat tmxr_startup (void); t_stat tmxr_startup (void);
t_stat tmxr_shutdown (void); t_stat tmxr_shutdown (void);
@ -156,6 +159,7 @@ extern FILE *sim_deb; /* debug file */
#if (!defined(NOT_MUX_USING_CODE)) #if (!defined(NOT_MUX_USING_CODE))
#define sim_activate tmxr_activate #define sim_activate tmxr_activate
#define sim_activate_after tmxr_activate_after #define sim_activate_after tmxr_activate_after
#define sim_clock_coschedule tmxr_clock_coschedule
#endif #endif
#else #else
#define tmxr_attach(mp, uptr, cptr) tmxr_attach_ex(mp, uptr, cptr, FALSE) #define tmxr_attach(mp, uptr, cptr) tmxr_attach_ex(mp, uptr, cptr, FALSE)