PDP11: Reworked PCLK device to minimize scheduled events
Rather than use a simh event to increment/decrement the counter register for each 'clock cycle', compute the time the interrupt will eventually fire and schedule a single event to deal with that. If the clock counter is referenced before the interrupt fires, interpolate the value based on the number of instructions that have actually been executed. sim_activate_after() is used here, and the time value for the scheduled activation is a uint32 representing microseconds. The range of values that the PCLK can programmatically specify could be large enough to exceed an uint32 when number of microseconds is converted to a count of instructions. This is an issue that has minimal consequences, but should be addressed internal to the implementation of sim_activate_after().
This commit is contained in:
parent
acaf118cf7
commit
25b9fc22dc
1 changed files with 53 additions and 33 deletions
|
@ -133,8 +133,10 @@ extern int32 int_req[IPL_HLVL];
|
|||
uint32 pclk_csr = 0; /* control/status */
|
||||
uint32 pclk_csb = 0; /* count set buffer */
|
||||
uint32 pclk_ctr = 0; /* counter */
|
||||
static void pclk_set_ctr (uint32 val);
|
||||
static uint32 pclk_get_ctr (void);
|
||||
static uint32 rate[4] = { 100000, 10000, 60, 10 }; /* ticks per second */
|
||||
static uint32 xtim[4] = { 10, 100, 16667, 100000 }; /* nominal time delay */
|
||||
static uint32 xtim[4] = { 10, 100, 16667, 100000 }; /* nominal usec delay per inc/dec */
|
||||
|
||||
t_stat pclk_rd (int32 *data, int32 PA, int32 access);
|
||||
t_stat pclk_wr (int32 data, int32 PA, int32 access);
|
||||
|
@ -143,7 +145,6 @@ t_stat pclk_reset (DEVICE *dptr);
|
|||
t_stat pclk_set_line (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
||||
t_stat pclk_show_freq (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||||
const char *pclk_description (DEVICE *dptr);
|
||||
void pclk_tick (void);
|
||||
|
||||
/* PCLK data structures
|
||||
|
||||
|
@ -174,7 +175,6 @@ REG pclk_reg[] = {
|
|||
{ FLDATA (RUN, pclk_csr, CSR_V_GO) },
|
||||
{ BRDATA (TIME, xtim, 10, 32, 4), REG_NZ + PV_LEFT },
|
||||
{ BRDATA (TPS, rate, 10, 32, 4), REG_NZ + PV_LEFT },
|
||||
{ DRDATA (CURTIM, pclk_unit.wait, 32), REG_HRO },
|
||||
{ ORDATA (DEVADDR, pclk_dib.ba, 32), REG_HRO },
|
||||
{ ORDATA (DEVVEC, pclk_dib.vec, 16), REG_HRO },
|
||||
{ NULL }
|
||||
|
@ -218,7 +218,7 @@ switch ((PA >> 1) & 03) {
|
|||
break;
|
||||
|
||||
case 02: /* counter */
|
||||
*data = pclk_ctr & DMASK; /* return counter */
|
||||
*data = pclk_get_ctr () & DMASK; /* return counter */
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -236,21 +236,25 @@ switch ((PA >> 1) & 03) {
|
|||
pclk_csr = data & PCLKCSR_WRMASK; /* clear and write */
|
||||
CLR_INT (PCLK); /* clr intr */
|
||||
rv = CSR_GETRATE (pclk_csr); /* new rate */
|
||||
pclk_unit.wait = xtim[rv]; /* new delay */
|
||||
if ((pclk_csr & CSR_GO) == 0) { /* stopped? */
|
||||
pclk_ctr = pclk_get_ctr (); /* save current value */
|
||||
sim_cancel (&pclk_unit); /* cancel */
|
||||
if (data & CSR_FIX) /* fix? tick */
|
||||
pclk_tick ();
|
||||
if (data & CSR_FIX) { /* fix? tick */
|
||||
pclk_ctr = DMASK & (pclk_ctr + (pclk_csr & CSR_UPDN)? 1 : -1);
|
||||
if (pclk_ctr == 0)
|
||||
pclk_svc (&pclk_unit);
|
||||
}
|
||||
}
|
||||
else if (((old_csr & CSR_GO) == 0) || /* run 0 -> 1? */
|
||||
(rv != CSR_GETRATE (old_csr))) { /* rate change? */
|
||||
sim_cancel (&pclk_unit); /* cancel */
|
||||
sim_activate_after (&pclk_unit, xtim[rv]); /* start clock */
|
||||
pclk_set_ctr (pclk_csb); /* start clock */
|
||||
}
|
||||
break;
|
||||
|
||||
case 01: /* buffer */
|
||||
pclk_csb = pclk_ctr = data; /* store ctr */
|
||||
pclk_csb = data; /* store ctr */
|
||||
pclk_set_ctr (data);
|
||||
pclk_csr = pclk_csr & ~(CSR_ERR | CSR_DONE); /* clr err, done */
|
||||
CLR_INT (PCLK); /* clr intr */
|
||||
break;
|
||||
|
@ -262,40 +266,57 @@ switch ((PA >> 1) & 03) {
|
|||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Clock tick (automatic or manual) */
|
||||
|
||||
void pclk_tick (void)
|
||||
static void pclk_set_ctr (uint32 val)
|
||||
{
|
||||
if (pclk_csr & CSR_UPDN) /* up or down? */
|
||||
pclk_ctr = (pclk_ctr + 1) & DMASK; /* 1 = up */
|
||||
else pclk_ctr = (pclk_ctr - 1) & DMASK; /* 0 = down */
|
||||
if (pclk_ctr == 0) { /* reached zero? */
|
||||
if (pclk_csr & CSR_DONE) /* done already set? */
|
||||
pclk_csr = pclk_csr | CSR_ERR; /* set error */
|
||||
else pclk_csr = pclk_csr | CSR_DONE; /* else set done */
|
||||
if (pclk_csr & CSR_IE) /* if IE, set int */
|
||||
SET_INT (PCLK);
|
||||
if (pclk_csr & CSR_MODE) /* if rpt, reload */
|
||||
pclk_ctr = pclk_csb;
|
||||
else {
|
||||
pclk_csb = 0; /* else clr ctr */
|
||||
pclk_csr = pclk_csr & ~CSR_GO; /* and clr go */
|
||||
if ((pclk_csr & CSR_GO) == 0) /* stopped? */
|
||||
pclk_ctr = val; /* save */
|
||||
else {
|
||||
uint32 delay = DMASK & ((pclk_csr & CSR_UPDN) ? (DMASK + 1 - val) : val);
|
||||
int32 rv;
|
||||
|
||||
rv = CSR_GETRATE (pclk_csr); /* get rate */
|
||||
sim_activate_after (&pclk_unit, xtim[rv] * delay); /* schedule interrupt */
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static uint32 pclk_get_ctr (void)
|
||||
{
|
||||
uint32 val;
|
||||
int32 rv;
|
||||
|
||||
if (!sim_is_active (&pclk_unit))
|
||||
return pclk_ctr;
|
||||
|
||||
rv = CSR_GETRATE (pclk_csr); /* get rate */
|
||||
val = (uint32)((sim_activate_time (&pclk_unit) / sim_timer_inst_per_sec ()) * (1000000 / xtim[rv]));
|
||||
val &= DMASK;
|
||||
if (pclk_csr & CSR_UPDN)
|
||||
val = DMASK + 1 - val;
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/* Clock tick (automatic or manual) */
|
||||
|
||||
/* Clock service */
|
||||
|
||||
t_stat pclk_svc (UNIT *uptr)
|
||||
{
|
||||
int32 rv;
|
||||
|
||||
pclk_tick (); /* tick clock */
|
||||
if ((pclk_csr & CSR_GO) == 0) /* done? */
|
||||
return SCPE_OK;
|
||||
rv = CSR_GETRATE (pclk_csr); /* get rate */
|
||||
sim_activate_after (&pclk_unit, xtim[rv]);
|
||||
if (pclk_csr & CSR_DONE) /* done already set? */
|
||||
pclk_csr = pclk_csr | CSR_ERR; /* set error */
|
||||
else
|
||||
pclk_csr = pclk_csr | CSR_DONE; /* else set done */
|
||||
if (pclk_csr & CSR_IE) /* if IE, set int */
|
||||
SET_INT (PCLK);
|
||||
if (pclk_csr & CSR_MODE) /* if rpt, reload */
|
||||
pclk_set_ctr (pclk_csb);
|
||||
else {
|
||||
pclk_csb = 0; /* else clr ctr */
|
||||
pclk_csr = pclk_csr & ~CSR_GO; /* and clr go */
|
||||
}
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
|
@ -308,7 +329,6 @@ pclk_csb = 0;
|
|||
pclk_ctr = 0;
|
||||
CLR_INT (PCLK); /* clear int */
|
||||
sim_cancel (&pclk_unit); /* cancel */
|
||||
pclk_unit.wait = xtim[CSR_GETRATE (pclk_csr)]; /* reset delay */
|
||||
return auto_config (0, 0);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue