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_csr = 0; /* control/status */
|
||||||
uint32 pclk_csb = 0; /* count set buffer */
|
uint32 pclk_csb = 0; /* count set buffer */
|
||||||
uint32 pclk_ctr = 0; /* counter */
|
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 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_rd (int32 *data, int32 PA, int32 access);
|
||||||
t_stat pclk_wr (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_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);
|
t_stat pclk_show_freq (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||||||
const char *pclk_description (DEVICE *dptr);
|
const char *pclk_description (DEVICE *dptr);
|
||||||
void pclk_tick (void);
|
|
||||||
|
|
||||||
/* PCLK data structures
|
/* PCLK data structures
|
||||||
|
|
||||||
|
@ -174,7 +175,6 @@ REG pclk_reg[] = {
|
||||||
{ FLDATA (RUN, pclk_csr, CSR_V_GO) },
|
{ FLDATA (RUN, pclk_csr, CSR_V_GO) },
|
||||||
{ BRDATA (TIME, xtim, 10, 32, 4), REG_NZ + PV_LEFT },
|
{ BRDATA (TIME, xtim, 10, 32, 4), REG_NZ + PV_LEFT },
|
||||||
{ BRDATA (TPS, rate, 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 (DEVADDR, pclk_dib.ba, 32), REG_HRO },
|
||||||
{ ORDATA (DEVVEC, pclk_dib.vec, 16), REG_HRO },
|
{ ORDATA (DEVVEC, pclk_dib.vec, 16), REG_HRO },
|
||||||
{ NULL }
|
{ NULL }
|
||||||
|
@ -218,7 +218,7 @@ switch ((PA >> 1) & 03) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 02: /* counter */
|
case 02: /* counter */
|
||||||
*data = pclk_ctr & DMASK; /* return counter */
|
*data = pclk_get_ctr () & DMASK; /* return counter */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,21 +236,25 @@ switch ((PA >> 1) & 03) {
|
||||||
pclk_csr = data & PCLKCSR_WRMASK; /* clear and write */
|
pclk_csr = data & PCLKCSR_WRMASK; /* clear and write */
|
||||||
CLR_INT (PCLK); /* clr intr */
|
CLR_INT (PCLK); /* clr intr */
|
||||||
rv = CSR_GETRATE (pclk_csr); /* new rate */
|
rv = CSR_GETRATE (pclk_csr); /* new rate */
|
||||||
pclk_unit.wait = xtim[rv]; /* new delay */
|
|
||||||
if ((pclk_csr & CSR_GO) == 0) { /* stopped? */
|
if ((pclk_csr & CSR_GO) == 0) { /* stopped? */
|
||||||
|
pclk_ctr = pclk_get_ctr (); /* save current value */
|
||||||
sim_cancel (&pclk_unit); /* cancel */
|
sim_cancel (&pclk_unit); /* cancel */
|
||||||
if (data & CSR_FIX) /* fix? tick */
|
if (data & CSR_FIX) { /* fix? tick */
|
||||||
pclk_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? */
|
else if (((old_csr & CSR_GO) == 0) || /* run 0 -> 1? */
|
||||||
(rv != CSR_GETRATE (old_csr))) { /* rate change? */
|
(rv != CSR_GETRATE (old_csr))) { /* rate change? */
|
||||||
sim_cancel (&pclk_unit); /* cancel */
|
sim_cancel (&pclk_unit); /* cancel */
|
||||||
sim_activate_after (&pclk_unit, xtim[rv]); /* start clock */
|
pclk_set_ctr (pclk_csb); /* start clock */
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 01: /* buffer */
|
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 */
|
pclk_csr = pclk_csr & ~(CSR_ERR | CSR_DONE); /* clr err, done */
|
||||||
CLR_INT (PCLK); /* clr intr */
|
CLR_INT (PCLK); /* clr intr */
|
||||||
break;
|
break;
|
||||||
|
@ -262,40 +266,57 @@ switch ((PA >> 1) & 03) {
|
||||||
return SCPE_OK;
|
return SCPE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clock tick (automatic or manual) */
|
static void pclk_set_ctr (uint32 val)
|
||||||
|
|
||||||
void pclk_tick (void)
|
|
||||||
{
|
{
|
||||||
if (pclk_csr & CSR_UPDN) /* up or down? */
|
if ((pclk_csr & CSR_GO) == 0) /* stopped? */
|
||||||
pclk_ctr = (pclk_ctr + 1) & DMASK; /* 1 = up */
|
pclk_ctr = val; /* save */
|
||||||
else pclk_ctr = (pclk_ctr - 1) & DMASK; /* 0 = down */
|
else {
|
||||||
if (pclk_ctr == 0) { /* reached zero? */
|
uint32 delay = DMASK & ((pclk_csr & CSR_UPDN) ? (DMASK + 1 - val) : val);
|
||||||
if (pclk_csr & CSR_DONE) /* done already set? */
|
int32 rv;
|
||||||
pclk_csr = pclk_csr | CSR_ERR; /* set error */
|
|
||||||
else pclk_csr = pclk_csr | CSR_DONE; /* else set done */
|
rv = CSR_GETRATE (pclk_csr); /* get rate */
|
||||||
if (pclk_csr & CSR_IE) /* if IE, set int */
|
sim_activate_after (&pclk_unit, xtim[rv] * delay); /* schedule interrupt */
|
||||||
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 */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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 */
|
/* Clock service */
|
||||||
|
|
||||||
t_stat pclk_svc (UNIT *uptr)
|
t_stat pclk_svc (UNIT *uptr)
|
||||||
{
|
{
|
||||||
int32 rv;
|
int32 rv;
|
||||||
|
|
||||||
pclk_tick (); /* tick clock */
|
|
||||||
if ((pclk_csr & CSR_GO) == 0) /* done? */
|
|
||||||
return SCPE_OK;
|
|
||||||
rv = CSR_GETRATE (pclk_csr); /* get rate */
|
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;
|
return SCPE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,7 +329,6 @@ pclk_csb = 0;
|
||||||
pclk_ctr = 0;
|
pclk_ctr = 0;
|
||||||
CLR_INT (PCLK); /* clear int */
|
CLR_INT (PCLK); /* clear int */
|
||||||
sim_cancel (&pclk_unit); /* cancel */
|
sim_cancel (&pclk_unit); /* cancel */
|
||||||
pclk_unit.wait = xtim[CSR_GETRATE (pclk_csr)]; /* reset delay */
|
|
||||||
return auto_config (0, 0);
|
return auto_config (0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue