3b2: Gracefully handle clock reset

Clock reset was not being handled gracefully at all, leading some
diagnostic tests not to pass, or not to pass consistently.

This change will "kick" the system clock whenever the timer divider is
reset, so the new divider is picked up immediately.
This commit is contained in:
Seth Morabito 2017-11-24 12:00:01 -08:00 committed by Mark Pizzolato
parent b23ebbe312
commit 690c30b6f4
8 changed files with 82 additions and 59 deletions

View file

@ -48,6 +48,9 @@ volatile uint32 abort_context;
/* Pointer to the last decoded instruction */
instr *cpu_instr;
/* The instruction to use if there is no history storage */
instr inst;
/* Circular buffer of instructions */
instr *INST = NULL;
uint32 cpu_hist_size = 0;
@ -1311,6 +1314,10 @@ t_bool cpu_on_interrupt(uint8 ipl)
uint32 new_pcbp;
uint16 id = ipl; /* TODO: Does this need to be uint16? */
sim_debug(IRQ_MSG, &cpu_dev,
"[%08x] [cpu_on_interrupt] ipl=%d\n",
R[NUM_PC], ipl);
/*
* "If a nonmaskable interrupt request is received, an auto-vector
* interrupt acknowledge cycle is performed (as if an autovector
@ -1368,8 +1375,6 @@ t_stat sim_instr(void)
uint32 width, offset;
t_uint64 mask;
instr inst;
operand *src1, *src2, *src3, *dst;
stop_reason = 0;

View file

@ -295,8 +295,10 @@ noret __libc_longjmp (jmp_buf buf, int val);
#define TMR_CLK 0 /* The clock responsible for IPL 15 interrupts */
#define TMR_TOD 1 /* The Time-of-Day clock */
#define TPS_CLK 100
#define TPS_TOD 10
#define CLK_MIN_TICKS 500 /* No fewer than 500 sim steps between ticks */
#define TPS_CLK 100 /* 100 ticks per second */
#define TPS_TOD 10 /* 10 ticks per second */
/* TIMING SECTION */
@ -304,7 +306,7 @@ noret __libc_longjmp (jmp_buf buf, int val);
/* Calculate delays (in simulator steps) for times */
/* System clock runs at 10MHz; 100ns period. */
#define US_PER_INST 0.9
#define US_PER_INST 1.0
#define INST_PER_MS (1000.0 / US_PER_INST)

View file

@ -250,28 +250,6 @@ void iu_txrdy_b_irq() {
}
}
t_stat iu_reset()
{
t_stat result;
result = tti_a_reset(&tti_a_dev);
if (result != SCPE_OK) {
return result;
}
result = tti_b_reset(&tti_b_dev);
if (result != SCPE_OK) {
return result;
}
result = iu_timer_reset(&iu_timer_dev);
if (result != SCPE_OK) {
return result;
}
return SCPE_OK;
}
t_stat tti_a_reset(DEVICE *dptr)
{
memset(&iu_state, 0, sizeof(IU_STATE));

View file

@ -180,9 +180,6 @@ typedef struct iu_timer_state {
extern IU_PORT iu_port_a;
extern IU_PORT iu_port_b;
/* Global reset */
extern t_stat iu_reset();
/* Function prototypes */
t_stat tti_a_reset(DEVICE *dptr);
t_stat tti_b_reset(DEVICE *dptr);

View file

@ -76,6 +76,18 @@ const char *sim_stop_messages[] = {
"Unimplemented MMU Feature"
};
void full_reset()
{
cpu_reset(&cpu_dev);
tti_a_reset(&tti_a_dev);
tti_b_reset(&tti_b_dev);
iu_timer_reset(&iu_timer_dev);
timer_reset(&timer_dev);
if_reset(&if_dev);
id_reset(&id_dev);
csr_reset(&csr_dev);
}
t_stat sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag)
{
int32 i;

View file

@ -39,6 +39,7 @@ extern REG *sim_PC;
extern int32 sim_emax;
extern DEVICE *sim_devices[];
void full_reset();
t_stat sim_load (FILE *fileref, CONST char *cptr, CONST char *fnam, int flag);
t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val,
int32 sw);

View file

@ -51,6 +51,8 @@ DEBTAB sys_deb_tab[] = {
{ NULL, 0 }
};
struct timer_ctr TIMERS[3];
uint32 *NVRAM = NULL;
extern DEVICE cpu_dev;
@ -151,8 +153,7 @@ void csr_write(uint32 pa, uint32 val, size_t size)
csr_data &= ~CSRPARE;
break;
case 0x0b: /* Set System Reset Request */
iu_reset();
cpu_reset(&cpu_dev);
full_reset();
cpu_boot(0, &cpu_dev);
break;
case 0x0f: /* Clear Memory Alignment Fault */
@ -171,9 +172,32 @@ void csr_write(uint32 pa, uint32 val, size_t size)
csr_data &= ~CSRFLOP;
break;
case 0x23: /* Set Inhibit Timers */
sim_debug(WRITE_MSG, &csr_dev,
"[%08x] SET INHIBIT TIMERS\n", R[NUM_PC]);
csr_data |= CSRITIM;
break;
case 0x27: /* Clear Inhibit Timers */
sim_debug(WRITE_MSG, &csr_dev,
"[%08x] CLEAR INHIBIT TIMERS\n", R[NUM_PC]);
/* A side effect of clearing the timer inhibit bit is to cause
* a simulated "tick" of any active timers. This is a hack to
* make diagnostics pass. This is not 100% accurate, but it
* makes SVR3 and DGMON tests happy.
*/
if (TIMERS[0].gate && TIMERS[0].enabled) {
TIMERS[0].val = TIMERS[0].divider - 1;
}
if (TIMERS[1].gate && TIMERS[1].enabled) {
TIMERS[1].val = TIMERS[1].divider - 1;
}
if (TIMERS[2].gate && TIMERS[2].enabled) {
TIMERS[2].val = TIMERS[2].divider - 1;
}
csr_data &= ~CSRITIM;
break;
case 0x2b: /* Set Inhibit Faults */
@ -368,8 +392,6 @@ void nvram_write(uint32 pa, uint32 val, size_t size)
*
*/
struct timer_ctr TIMERS[3];
/*
* The three timers, (A, B, C) run at different
* programmatially controlled frequencies, so each must be
@ -383,6 +405,8 @@ UNIT timer_unit[] = {
{ NULL }
};
UNIT *timer_clk_unit = &timer_unit[1];
REG timer_reg[] = {
{ HRDATAD(DIVA, TIMERS[0].divider, 16, "Divider A") },
{ HRDATAD(STA, TIMERS[0].mode, 16, "Mode A") },
@ -401,27 +425,11 @@ DEVICE timer_dev = {
DEV_DEBUG, 0, sys_deb_tab
};
#define TIMER_STP_US 10 /* 10 us delay per timer step */
#define TIMER_STP_US 10 /* 10 us delay per timer step */
#define tmrnum u3
#define tmr up7
#define DECR_STEPS 400
/*
* This is a hack to make diagnostics pass. If read immediately after
* being set, a counter should always return the initial value. If a
* certain number of steps have passed, it should have decremented a
* little bit, so we return a value one less than the initial value.
* This is not 100% accurate, but it makes SVR3 and DGMON tests happy.
*/
static SIM_INLINE uint16 timer_current_val(struct timer_ctr *ctr)
{
if ((sim_gtime() - ctr->stime) > DECR_STEPS) {
return ctr->divider - 1;
} else {
return ctr->divider;
}
}
t_stat timer_reset(DEVICE *dptr) {
int32 i, t;
@ -437,8 +445,8 @@ t_stat timer_reset(DEVICE *dptr) {
TIMERS[1].gate = 1;
if (!sim_is_running) {
t = sim_rtcn_init_unit(&timer_unit[1], TPS_CLK, TMR_CLK);
sim_activate_after(&timer_unit[1], 1000000 / t);
t = sim_rtcn_init_unit(timer_clk_unit, TPS_CLK, TMR_CLK);
sim_activate_after(timer_clk_unit, 1000000 / t);
}
return SCPE_OK;
@ -475,9 +483,11 @@ t_stat timer1_svc(UNIT *uptr)
}
ticks = ctr->divider / TIMER_STP_US;
if (ticks == 0) {
if (ticks < CLK_MIN_TICKS) {
ticks = TPS_CLK;
}
t = sim_rtcn_calb(ticks, TMR_CLK);
sim_activate_after(uptr, (uint32) (1000000 / ticks));
@ -517,10 +527,12 @@ uint32 timer_read(uint32 pa, size_t size)
case TIMER_REG_DIVA:
case TIMER_REG_DIVB:
case TIMER_REG_DIVC:
if (ctr->enabled && ctr->gate) {
ctr_val = timer_current_val(ctr);
} else {
ctr_val = ctr->divider;
ctr_val = ctr->val;
if (ctr_val != ctr->divider) {
sim_debug(READ_MSG, &timer_dev,
"[%08x] >>> ctr_val = %04x, ctr->divider = %04x\n",
R[NUM_PC], ctr_val, ctr->divider);
}
switch (ctr->mode & CLK_RW) {
@ -565,24 +577,39 @@ void handle_timer_write(uint8 ctrnum, uint32 val)
case 0x10:
ctr->divider &= 0xff00;
ctr->divider |= val & 0xff;
ctr->val = ctr->divider;
ctr->enabled = TRUE;
ctr->stime = sim_gtime();
sim_cancel(timer_clk_unit);
sim_activate_abs(timer_clk_unit, ctr->divider * TIMER_STP_US);
break;
case 0x20:
ctr->divider &= 0x00ff;
ctr->divider |= (val & 0xff) << 8;
ctr->val = ctr->divider;
ctr->enabled = TRUE;
ctr->stime = sim_gtime();
/* Kick the timer to get the new divider value */
sim_cancel(timer_clk_unit);
sim_activate_abs(timer_clk_unit, ctr->divider * TIMER_STP_US);
break;
case 0x30:
if (ctr->lmb) {
ctr->lmb = FALSE;
ctr->divider = (uint16) ((ctr->divider & 0x00ff) | ((val & 0xff) << 8));
ctr->val = ctr->divider;
ctr->enabled = TRUE;
ctr->stime = sim_gtime();
sim_debug(READ_MSG, &timer_dev,
"[%08x] Write timer %d val LMB (MSB): %02x\n",
R[NUM_PC], ctrnum, val & 0xff);
/* Kick the timer to get the new divider value */
sim_cancel(timer_clk_unit);
sim_activate_abs(timer_clk_unit, ctr->divider * TIMER_STP_US);
} else {
ctr->lmb = TRUE;
ctr->divider = (ctr->divider & 0xff00) | (val & 0xff);
ctr->val = ctr->divider;
}
break;
default:
@ -652,7 +679,7 @@ t_stat tod_reset(DEVICE *dptr)
if (!sim_is_running) {
t = sim_rtcn_init_unit(&tod_unit, TPS_TOD, TMR_TOD);
sim_activate_after(&tod_unit, 1000000 / TPS_TOD);
sim_activate_after(&tod_unit, 1000000 / t);
}
return SCPE_OK;

View file

@ -43,6 +43,7 @@ extern DEBTAB sys_deb_tab[];
struct timer_ctr {
uint16 divider;
uint16 val;
uint8 mode;
t_bool lmb;
t_bool enabled;