VAX780, PDP11 (all Unibus systems): Fix VH DHU11 device output activity

- Implement a per line transmit FIFO to properly reflect the DHU real hardware
- Output is rate limited based on the programmed port speeds
- Properly abort programmed output

As reported and discussed in #600 and #588
This commit is contained in:
Mark Pizzolato 2018-08-02 12:53:26 -07:00
parent dc13df7b7b
commit c7abd314d9

View file

@ -165,7 +165,7 @@ BITFIELD vh_csr_bits[] = {
#define XOFF (023) #define XOFF (023)
BITFIELD vh_rbuf_bits[] = { BITFIELD vh_rbuf_bits[] = {
BITF(RX_CHAR,4), /* Receive Character */ BITF(RX_CHAR,8), /* Receive Character */
BITF(RX_LINE,4), /* Receive Line */ BITF(RX_LINE,4), /* Receive Line */
BIT(PARITY_ERR), /* Parity Error */ BIT(PARITY_ERR), /* Parity Error */
BIT(FRAME_ERR), /* Frame Error */ BIT(FRAME_ERR), /* Frame Error */
@ -380,7 +380,7 @@ BITFIELD vh_tbuffct_bits[] = {
static uint16 vh_csr[VH_MUXES] = { 0 }; /* CSRs */ static uint16 vh_csr[VH_MUXES] = { 0 }; /* CSRs */
static uint16 vh_timer[VH_MUXES] = { 1 }; /* controller timeout */ static uint16 vh_timer[VH_MUXES] = { 1 }; /* controller timeout */
static uint16 vh_mcount[VH_MUXES] = { 0 }; static uint16 vh_mcount[VH_MUXES] = { 0 };
static uint32 vh_timeo[VH_MUXES] = { 0 }; static int32 vh_timeo[VH_MUXES] = { 0 };
static uint32 vh_ovrrun[VH_MUXES] = { 0 }; /* line overrun bits */ static uint32 vh_ovrrun[VH_MUXES] = { 0 }; /* line overrun bits */
/* XOFF'd channels, one bit/channel */ /* XOFF'd channels, one bit/channel */
static uint32 vh_stall[VH_MUXES] = { 0 }; static uint32 vh_stall[VH_MUXES] = { 0 };
@ -417,6 +417,11 @@ typedef struct {
uint16 tbuf1; uint16 tbuf1;
uint16 tbuf2; uint16 tbuf2;
uint16 txchar; /* single character I/O */ uint16 txchar; /* single character I/O */
#define TX_FIFO_SIZE 64
uint16 txfifo[TX_FIFO_SIZE];/* transmit FIFO - circular */
uint16 txfifo_idx; /* Extraction index */
uint16 txfifo_cnt; /* Count of FIFO entries */
uint16 txstate; /* transmit state */ uint16 txstate; /* transmit state */
#define TXS_IDLE 0 #define TXS_IDLE 0
#define TXS_PIO_START 1 #define TXS_PIO_START 1
@ -518,7 +523,7 @@ static const REG vh_reg[] = {
{ BRDATADF (CSR, vh_csr, DEV_RDX, 16, VH_MUXES, "control/status register, boards 0 to 3", vh_csr_bits) }, { BRDATADF (CSR, vh_csr, DEV_RDX, 16, VH_MUXES, "control/status register, boards 0 to 3", vh_csr_bits) },
{ BRDATAD (TIMER, vh_timer, DEV_RDX, 16, VH_MUXES, "controller timeout, boards 0 to 3") }, { BRDATAD (TIMER, vh_timer, DEV_RDX, 16, VH_MUXES, "controller timeout, boards 0 to 3") },
{ BRDATAD (MCOUNT, vh_mcount, DEV_RDX, 16, VH_MUXES, "count down timer, boards 0 to 3") }, { BRDATAD (MCOUNT, vh_mcount, DEV_RDX, 16, VH_MUXES, "count down timer, boards 0 to 3") },
{ BRDATAD (TIMEO, vh_timeo, DEV_RDX, 16, VH_MUXES, "control/status register, boards 0 to 3") }, { BRDATAD (TIMEO, vh_timeo, DEV_RDX, 16, VH_MUXES, "receive interrupt count down timer, boards 0 to 3") },
{ BRDATAD (OVRRUN, vh_ovrrun, DEV_RDX, 16, VH_MUXES, "line overrun bits, boards 0 to 3") }, { BRDATAD (OVRRUN, vh_ovrrun, DEV_RDX, 16, VH_MUXES, "line overrun bits, boards 0 to 3") },
{ BRDATAD (STALL, vh_stall, DEV_RDX, 16, VH_MUXES, "XOFF'd channels 1 bit/channel, boards 0 to 3") }, { BRDATAD (STALL, vh_stall, DEV_RDX, 16, VH_MUXES, "XOFF'd channels 1 bit/channel, boards 0 to 3") },
{ BRDATAD (LOOP, vh_loop, DEV_RDX, 16, VH_MUXES, "loopback status, boards 0 to 3") }, { BRDATAD (LOOP, vh_loop, DEV_RDX, 16, VH_MUXES, "loopback status, boards 0 to 3") },
@ -815,6 +820,41 @@ static int32 fifo_get ( int32 vh )
sim_clock_coschedule_abs (vh_poll_unit, tmxr_poll); sim_clock_coschedule_abs (vh_poll_unit, tmxr_poll);
return (data & 0177777); return (data & 0177777);
} }
/* TX FIFO get/put routines */
/* return 0 on success, -1 on FIFO overflow */
static int32 tx_fifo_free_count ( TMLX *lp)
{
return TX_FIFO_SIZE - lp->txfifo_cnt;
}
static int32 tx_fifo_put ( TMLX *lp,
int32 data )
{
int32 status = 0;
if (tx_fifo_free_count (lp) == 0)
return -1;
lp->txfifo[(lp->txfifo_idx + lp->txfifo_cnt) % TX_FIFO_SIZE] = data;
++lp->txfifo_cnt;
return 0;
}
static int32 tx_fifo_get ( TMLX *lp )
{
int32 data = lp->txfifo[lp->txfifo_idx];
if (lp->txfifo_cnt == 0)
return -1;
lp->txfifo[lp->txfifo_idx] = 0;
--lp->txfifo_cnt;
++lp->txfifo_idx;
if (lp->txfifo_idx == TX_FIFO_SIZE)
lp->txfifo_idx = 0;
return (data & 0177777);
}
/* TX Q manipulation */ /* TX Q manipulation */
static int32 dq_tx_report ( int32 vh ) static int32 dq_tx_report ( int32 vh )
@ -982,12 +1022,7 @@ static t_stat vh_rd ( int32 *data,
line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);
lp = &vh_parm[line]; lp = &vh_parm[line];
*data = (lp->lstat & ~0377) | /* modem status */ *data = (lp->lstat & ~0377) | /* modem status */
#if 0 tx_fifo_free_count (lp);
(64 - tmxr_tqln (lp->tmln));
fprintf (stderr, "\rtqln %d\n", 64 - tmxr_tqln (lp->tmln));
#else
64;
#endif
break; break;
case 4: /* LNCTRL */ case 4: /* LNCTRL */
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) {
@ -1061,10 +1096,7 @@ static t_stat vh_wr ( int32 ldata,
(vh_csr[vh] & 0377) | (data << 8) : (vh_csr[vh] & 0377) | (data << 8) :
(vh_csr[vh] & ~0377) | (data & 0377); (vh_csr[vh] & ~0377) | (data & 0377);
if (data & CSR_MASTER_RESET) { if (data & CSR_MASTER_RESET) {
if ((vh_unit[vh].flags & UNIT_MODEDHU) && (data & CSR_SKIP)) sim_cancel (vh_poll_unit);
data &= ~CSR_MASTER_RESET;
if (vh == 0) /* Only start unit service on the first unit. Units are polled there */
sim_clock_coschedule (vh_poll_unit, tmxr_poll);
vh_mcount[vh] = MS2SIMH (1200); /* 1.2 seconds */ vh_mcount[vh] = MS2SIMH (1200); /* 1.2 seconds */
sim_clock_coschedule (vh_timer_unit, tmxr_poll); sim_clock_coschedule (vh_timer_unit, tmxr_poll);
sim_debug (DBG_TIM, &vh_dev, "vh_wr() - Master Reset Timeout set vh=%d, timeout=%d\n", vh, vh_mcount[vh]); sim_debug (DBG_TIM, &vh_dev, "vh_wr() - Master Reset Timeout set vh=%d, timeout=%d\n", vh, vh_mcount[vh]);
@ -1134,12 +1166,9 @@ static t_stat vh_wr ( int32 ldata,
lp->txchar = data; /* TXCHAR */ lp->txchar = data; /* TXCHAR */
if (lp->txchar & TXCHAR_TX_DATA_VALID) { if (lp->txchar & TXCHAR_TX_DATA_VALID) {
if (lp->tbuf2 & TB2_TX_ENA) { if (lp->tbuf2 & TB2_TX_ENA) {
lp->txstate = TXS_PIO_START; tx_fifo_put (lp, TXCHAR_TX_DATA_VALID | (data & 0377));
if (vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), sim_debug (DBG_XMTSCH, &vh_dev, "VH-%d PIO Scheduling Start - 0x%X\n", (int)(lp - vh_parm), lp->txchar & 0xFF);
lp->txchar) != SCPE_STALL) sim_activate_abs (vh_xmit_unit, 0);
lp->txstate = TXS_PIO_PENDING;
sim_debug (DBG_XMTSCH, &vh_dev, "VH-%d PIO: %s - 0x%X\n", (int)(lp - vh_parm), (lp->txstate == TXS_PIO_PENDING) ? "Started" : "Scheduled Start", lp->txchar & 0xFF);
sim_activate_after_abs (vh_xmit_unit, lp->tmln->txdeltausecs);
} }
} }
} }
@ -1189,11 +1218,13 @@ static t_stat vh_wr ( int32 ldata,
/* transmit 1 or 2 characters */ /* transmit 1 or 2 characters */
if (!(lp->tbuf2 & TB2_TX_ENA)) if (!(lp->tbuf2 & TB2_TX_ENA))
break; break;
vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), data); tx_fifo_put (lp, TXCHAR_TX_DATA_VALID | (data & 0377));
q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE); if (lp->txfifo_cnt == 1) {
if (access != WRITEB) sim_debug (DBG_XMTSCH, &vh_dev, "VH-%d PIO Scheduling Start - 0x%X %s 0x%X\n", (int)(lp - vh_parm), data & 0xFF, (access == WRITEB) ? "" : "Extra Byte", data >> 8);
vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), sim_activate_abs (vh_xmit_unit, 0);
data >> 8); }
if (access != WRITEB) /* second character */
tx_fifo_put (lp, TXCHAR_TX_DATA_VALID | ((data >> 8)& 0377));
} }
break; break;
case 4: /* LNCTRL */ case 4: /* LNCTRL */
@ -1218,6 +1249,11 @@ static t_stat vh_wr ( int32 ldata,
lp->tbuf2 &= ~TB2_TX_DMA_START; lp->tbuf2 &= ~TB2_TX_DMA_START;
q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE); q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE);
} }
if ((lp->tbuf2 & TB2_TX_ENA) &&
(lp->txfifo_cnt != 0)) {
lp->txfifo_idx = lp->txfifo_cnt = 0;
q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE);
}
} }
/* Implement program-initiated flow control */ /* Implement program-initiated flow control */
if ( (data & LNCTRL_FORCE_XOFF) && if ( (data & LNCTRL_FORCE_XOFF) &&
@ -1380,6 +1416,10 @@ static t_stat vh_timersvc ( UNIT *uptr )
/* scan all DHU-mode muxes for RX FIFO timeout */ /* scan all DHU-mode muxes for RX FIFO timeout */
for (vh = 0; vh < vh_desc.lines/VH_LINES; vh++) { for (vh = 0; vh < vh_desc.lines/VH_LINES; vh++) {
if (vh_unit[vh].flags & UNIT_MODEDHU) { if (vh_unit[vh].flags & UNIT_MODEDHU) {
if ((vh_csr[vh] & (CSR_SKIP | CSR_MASTER_RESET)) == (CSR_SKIP | CSR_MASTER_RESET)) {
sim_activate (vh_poll_unit, tmxr_poll);
vh_csr[vh] &= ~CSR_MASTER_RESET;
}
if (vh_timeo[vh] && (vh_csr[vh] & CSR_RXIE)) { if (vh_timeo[vh] && (vh_csr[vh] & CSR_RXIE)) {
vh_timeo[vh] -= 1; vh_timeo[vh] -= 1;
if ((vh_timeo[vh] == 0) && rbuf_idx[vh]) { if ((vh_timeo[vh] == 0) && rbuf_idx[vh]) {
@ -1450,21 +1490,25 @@ static t_stat vh_xmt_svc ( UNIT *uptr )
TMLX *lp = &vh_parm[line]; TMLX *lp = &vh_parm[line];
/* process any pending programmed output */ /* process any pending programmed output */
if (lp->txchar & TXCHAR_TX_DATA_VALID) { if (lp->txfifo_cnt) {
if (lp->tbuf2 & TB2_TX_ENA) { if (lp->tbuf2 & TB2_TX_ENA) {
sim_debug (DBG_XMTSCH, &vh_dev, "VH-%d PIO: %s - 0x%X\n", (int)(lp - vh_parm), (lp->txstate == TXS_PIO_PENDING) ? "Pending" : ((lp->txstate == TXS_PIO_START) ? "Starting" : "Unknown"), lp->txchar & 0xFF); sim_debug (DBG_XMTSCH, &vh_dev, "VH-%d PIO: %s - 0x%X\n", (int)(lp - vh_parm), (lp->txstate == TXS_PIO_PENDING) ? "Pending" : ((lp->txstate == TXS_PIO_START) ? "Starting" : ((lp->txstate == TXS_IDLE) ? "Idle" : "Unknown")), lp->txfifo[lp->txfifo_idx] & 0xFF);
switch (lp->txstate) { switch (lp->txstate) {
case TXS_PIO_PENDING: case TXS_PIO_PENDING:
if (0 == tmxr_txdone_ln (lp->tmln)) /* actually done? */ if (0 == tmxr_txdone_ln (lp->tmln)) /* actually done? */
break; break;
tx_fifo_get (lp);
q_tx_report (vh, q_tx_report (vh,
CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE); CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE);
lp->txchar &= ~TXCHAR_TX_DATA_VALID;
lp->txstate = TXS_IDLE; lp->txstate = TXS_IDLE;
if (lp->txfifo_cnt == 0)
break; break;
/* fall through */
case TXS_IDLE:
lp->txstate = TXS_PIO_START;
case TXS_PIO_START: case TXS_PIO_START:
if (vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), if (vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]),
lp->txchar) != SCPE_STALL) lp->txfifo[lp->txfifo_idx]) != SCPE_STALL)
lp->txstate = TXS_PIO_PENDING; lp->txstate = TXS_PIO_PENDING;
break; break;
} }