Added MicroVAX I network boot support

pdp11_xq.c, pdp11_xq.h
	- Added emulation and visibility to the LEDs which were on the physical DEQNA/DELQA network boards.  "show xq: will now display the LED state in addition to the other useful things.
	- Added debugging of loopback packet data
	- Avoided padding on short loopback packets
	- Added support for extended length loopback packets (up to 1600 bytes) which is described in the DEQNA manual and used by the MicroVAX I boot ROM.  Recieve such packets with the LONG error indicator.
	- Returned 'reserved' status bits as 1's in received packet status word 1.
	- Added debug display of transmit and receive Buffer Descriptor List contents.

sim_ether.c, sim_ether.h
	- Added support for extended/oversized packets.
This commit is contained in:
Mark Pizzolato 2012-11-05 15:12:36 -08:00
parent 69666f1480
commit f1e3216d99
4 changed files with 149 additions and 20 deletions

View file

@ -256,6 +256,7 @@ extern int32 tmxr_poll;
extern int32 tmr_poll, clk_tps; extern int32 tmr_poll, clk_tps;
extern t_bool sim_idle_enab; extern t_bool sim_idle_enab;
extern FILE* sim_deb; extern FILE* sim_deb;
extern int32 sim_switches;
extern FILE *sim_log; extern FILE *sim_log;
extern char* read_line (char *ptr, int32 size, FILE *stream); extern char* read_line (char *ptr, int32 size, FILE *stream);
@ -278,6 +279,7 @@ t_stat xq_show_sanity (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat xq_set_sanity (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat xq_set_sanity (UNIT* uptr, int32 val, char* cptr, void* desc);
t_stat xq_show_poll (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat xq_show_poll (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat xq_set_poll (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat xq_set_poll (UNIT* uptr, int32 val, char* cptr, void* desc);
t_stat xq_show_leds (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat xq_process_xbdl(CTLR* xq); t_stat xq_process_xbdl(CTLR* xq);
t_stat xq_dispatch_xbdl(CTLR* xq); t_stat xq_dispatch_xbdl(CTLR* xq);
t_stat xq_process_turbo_rbdl(CTLR* xq); t_stat xq_process_turbo_rbdl(CTLR* xq);
@ -458,6 +460,8 @@ MTAB xq_mod[] = {
#endif #endif
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "SANITY", "SANITY={ON|OFF}", { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "SANITY", "SANITY={ON|OFF}",
&xq_set_sanity, &xq_show_sanity, NULL }, &xq_set_sanity, &xq_show_sanity, NULL },
{ MTAB_XTD | MTAB_VDV , 0, "LEDS", "LEDS",
NULL, &xq_show_leds, NULL },
{ 0 }, { 0 },
}; };
@ -822,6 +826,16 @@ t_stat xq_set_sanity (UNIT* uptr, int32 val, char* cptr, void* desc)
return SCPE_OK; return SCPE_OK;
} }
t_stat xq_show_leds (FILE* st, UNIT* uptr, int32 val, void* desc)
{
CTLR* xq = xq_unit2ctlr(uptr);
fprintf(st, "leds=(%s,%s,%s)", xq->var->setup.l1 ? "ON" : "OFF",
xq->var->setup.l2 ? "ON" : "OFF",
xq->var->setup.l3 ? "ON" : "OFF");
return SCPE_OK;
}
/*============================================================================*/ /*============================================================================*/
t_stat xq_nxm_error(CTLR* xq) t_stat xq_nxm_error(CTLR* xq)
@ -985,6 +999,8 @@ t_stat xq_process_rbdl(CTLR* xq)
item = &xq->var->ReadQ.item[xq->var->ReadQ.head]; item = &xq->var->ReadQ.item[xq->var->ReadQ.head];
rbl = item->packet.len; rbl = item->packet.len;
rbuf = item->packet.msg; rbuf = item->packet.msg;
if (item->packet.oversize)
rbuf = item->packet.oversize;
/* see if packet must be size-adjusted or is splitting */ /* see if packet must be size-adjusted or is splitting */
if (item->packet.used) { if (item->packet.used) {
@ -992,8 +1008,11 @@ t_stat xq_process_rbdl(CTLR* xq)
rbl -= used; rbl -= used;
rbuf = &item->packet.msg[used]; rbuf = &item->packet.msg[used];
} else { } else {
/* adjust runt packets */ /* there should be no need to adjust runt packets
if (rbl < ETH_MIN_PACKET) { the physical layer (sim_ether) won't deliver any short packets
via eth_read, so the only short packets which get here are loopback
packets sent by the host diagnostics */
if ((item->type != 1) && (rbl < ETH_MIN_PACKET)) {
xq->var->stats.runt += 1; xq->var->stats.runt += 1;
sim_debug(DBG_WRN, xq->dev, "Runt detected, size = %d\n", rbl); sim_debug(DBG_WRN, xq->dev, "Runt detected, size = %d\n", rbl);
/* pad runts with zeros up to minimum size - this allows "legal" (size - 60) /* pad runts with zeros up to minimum size - this allows "legal" (size - 60)
@ -1003,12 +1022,14 @@ t_stat xq_process_rbdl(CTLR* xq)
}; };
/* adjust oversized packets */ /* adjust oversized packets */
if (rbl > ETH_MAX_PACKET) { if (rbl > ETH_FRAME_SIZE) {
xq->var->stats.giant += 1; xq->var->stats.giant += 1;
sim_debug(DBG_WRN, xq->dev, "Giant detected, size=%d\n", rbl); sim_debug(DBG_WRN, xq->dev, "Giant detected, size=%d\n", rbl);
/* trim giants down to maximum size - no documentation on how to handle the data loss */ /* trim giants down to maximum size - no documentation on how to handle the data loss */
item->packet.len = ETH_MAX_PACKET; if (rbl > XQ_MAX_RCV_PACKET) {
rbl = ETH_MAX_PACKET; item->packet.len = XQ_MAX_RCV_PACKET;
rbl = XQ_MAX_RCV_PACKET;
}
}; };
}; };
@ -1038,6 +1059,7 @@ t_stat xq_process_rbdl(CTLR* xq)
case 2: /* normal packet */ case 2: /* normal packet */
rbl -= 60; /* keeps max packet size in 11 bits */ rbl -= 60; /* keeps max packet size in 11 bits */
xq->var->rbdl_buf[4] = (rbl & 0x0700); /* high bits of rbl */ xq->var->rbdl_buf[4] = (rbl & 0x0700); /* high bits of rbl */
xq->var->rbdl_buf[4] |= 0x00f8; /* set reserved bits to 1 */
break; break;
} }
if (item->packet.used < item->packet.len) if (item->packet.used < item->packet.len)
@ -1048,12 +1070,16 @@ t_stat xq_process_rbdl(CTLR* xq)
xq->var->rbdl_buf[4] |= 0x0001; /* set overflow bit */ xq->var->rbdl_buf[4] |= 0x0001; /* set overflow bit */
xq->var->stats.dropped += xq->var->ReadQ.loss; xq->var->stats.dropped += xq->var->ReadQ.loss;
xq->var->ReadQ.loss = 0; /* reset loss counter */ xq->var->ReadQ.loss = 0; /* reset loss counter */
} }
if ((rbl + ((item->type == 2) ? 60 : 0)) > ETH_MAX_PACKET)
xq->var->rbdl_buf[4] |= 0x4000; /* set Error bit (LONG) */
/* update read status words*/ /* update read status words*/
wstatus = Map_WriteW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); wstatus = Map_WriteW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]);
if (wstatus) return xq_nxm_error(xq); if (wstatus) return xq_nxm_error(xq);
sim_debug(DBG_TRC, xq->dev, "xq_process_rdbl(bd=0x%X, addr=0x%X, size=0x%X, len=0x%X, st1=0x%04X, st2=0x%04X)\n", xq->var->rbdl_ba, address, b_length, rbl + ((item->type == 2) ? 60 : 0), xq->var->rbdl_buf[4], xq->var->rbdl_buf[5]);
/* remove packet from queue */ /* remove packet from queue */
if (item->packet.used >= item->packet.len) if (item->packet.used >= item->packet.len)
ethq_remove(&xq->var->ReadQ); ethq_remove(&xq->var->ReadQ);
@ -1263,6 +1289,8 @@ t_stat xq_process_xbdl(CTLR* xq)
/* clear write buffer */ /* clear write buffer */
xq->var->write_buffer.len = 0; xq->var->write_buffer.len = 0;
free (xq->var->write_buffer.oversize);
xq->var->write_buffer.oversize = NULL;
/* process buffer descriptors until not valid */ /* process buffer descriptors until not valid */
while (1) { while (1) {
@ -1297,9 +1325,12 @@ t_stat xq_process_xbdl(CTLR* xq)
} }
/* add to transmit buffer, making sure it's not too big */ /* add to transmit buffer, making sure it's not too big */
if ((xq->var->write_buffer.len + b_length) > sizeof(xq->var->write_buffer.msg)) if ((xq->var->write_buffer.len + b_length) > sizeof(xq->var->write_buffer.msg)) {
b_length = (uint16)(sizeof(xq->var->write_buffer.msg) - xq->var->write_buffer.len); xq->var->write_buffer.oversize = realloc (xq->var->write_buffer.oversize, xq->var->write_buffer.len + b_length);
rstatus = Map_ReadB(address, b_length, &xq->var->write_buffer.msg[xq->var->write_buffer.len]); if (xq->var->write_buffer.len <= sizeof(xq->var->write_buffer.msg))
memcpy (xq->var->write_buffer.oversize, xq->var->write_buffer.msg, xq->var->write_buffer.len);
}
rstatus = Map_ReadB(address, b_length, xq->var->write_buffer.oversize ? &xq->var->write_buffer.oversize[xq->var->write_buffer.len] : &xq->var->write_buffer.msg[xq->var->write_buffer.len]);
if (rstatus) return xq_nxm_error(xq); if (rstatus) return xq_nxm_error(xq);
xq->var->write_buffer.len += b_length; xq->var->write_buffer.len += b_length;
@ -1315,6 +1346,8 @@ t_stat xq_process_xbdl(CTLR* xq)
} else { /* loopback */ } else { /* loopback */
/* put packet in read buffer */ /* put packet in read buffer */
ethq_insert (&xq->var->ReadQ, 1, &xq->var->write_buffer, 0); ethq_insert (&xq->var->ReadQ, 1, &xq->var->write_buffer, 0);
if ((DBG_PCK & xq->dev->dctrl) && xq->var->etherface)
eth_packet_trace_ex(xq->var->etherface, xq->var->write_buffer.msg, xq->var->write_buffer.len, "xq-write-loopback", DBG_DAT & xq->dev->dctrl, DBG_PCK);
} }
/* update write status */ /* update write status */
@ -1323,6 +1356,8 @@ t_stat xq_process_xbdl(CTLR* xq)
/* clear write buffer */ /* clear write buffer */
xq->var->write_buffer.len = 0; xq->var->write_buffer.len = 0;
free (xq->var->write_buffer.oversize);
xq->var->write_buffer.oversize = NULL;
/* reset sanity timer */ /* reset sanity timer */
xq_reset_santmr(xq); xq_reset_santmr(xq);
@ -1361,6 +1396,54 @@ t_stat xq_process_xbdl(CTLR* xq)
} /* while */ } /* while */
} }
void xq_show_debug_bdl(CTLR* xq, uint32 bdl_ba)
{
uint16 bdl_buf[6];
uint16 b_length, w_length;
uint32 address, initial_bdl_ba = bdl_ba;
int32 rstatus;
sim_debug(DBG_TRC, xq->dev, " Descriptor list at: 0x%X\n", bdl_ba);
while (1) {
/* get the beginning of the buffer descriptor */
rstatus = Map_ReadW (bdl_ba, 4, &bdl_buf[0]);
if (rstatus) return;
/* invalid buffer? */
if (~bdl_buf[1] & XQ_DSC_V)
break;
/* get the rest of the buffer descriptor */
rstatus = Map_ReadW (bdl_ba + 4, 8, &bdl_buf[2]);
if (rstatus) return;
/* explicit chain buffer? */
if (bdl_buf[1] & XQ_DSC_C) {
sim_debug(DBG_TRC, xq->dev, " descriptor=0x%X, flags=0x%04X, chain=0x%X\n", bdl_ba, bdl_buf[0], ((bdl_buf[1] & 0x3F) << 16) | bdl_buf[2]);
bdl_ba = ((bdl_buf[1] & 0x3F) << 16) | bdl_buf[2];
if (initial_bdl_ba == bdl_ba)
break;
continue;
}
/* get host memory address */
address = ((bdl_buf[1] & 0x3F) << 16) | bdl_buf[2];
/* decode buffer length - two's complement (in words) */
w_length = ~bdl_buf[3] + 1;
b_length = w_length * 2;
if (bdl_buf[1] & XQ_DSC_H) b_length -= 1;
if (bdl_buf[1] & XQ_DSC_L) b_length -= 1;
sim_debug(DBG_TRC, xq->dev, " descriptor=0x%X, flags=0x%04X, bits=0x%04X, addr=0x%X, len=0x%X, st1=0x%04X, st2=0x%04X\n",
bdl_ba, bdl_buf[0], bdl_buf[1] & 0xFFC0, address, b_length, bdl_buf[4], bdl_buf[5]);
bdl_ba += 12;
}
}
t_stat xq_dispatch_rbdl(CTLR* xq) t_stat xq_dispatch_rbdl(CTLR* xq)
{ {
int i; int i;
@ -1388,7 +1471,10 @@ t_stat xq_dispatch_rbdl(CTLR* xq)
if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { if (~xq->var->rbdl_buf[1] & XQ_DSC_V) {
xq_csr_set_clr(xq, XQ_CSR_RL, 0); xq_csr_set_clr(xq, XQ_CSR_RL, 0);
return SCPE_OK; return SCPE_OK;
} }
/* When debugging, walk and display the buffer descriptor list */
xq_show_debug_bdl(xq, xq->var->rbdl_ba);
/* process any waiting packets in receive queue */ /* process any waiting packets in receive queue */
if (xq->var->ReadQ.count) if (xq->var->ReadQ.count)
@ -1413,10 +1499,15 @@ t_stat xq_dispatch_xbdl(CTLR* xq)
/* clear transmit buffer */ /* clear transmit buffer */
xq->var->write_buffer.len = 0; xq->var->write_buffer.len = 0;
free (xq->var->write_buffer.oversize);
xq->var->write_buffer.oversize = NULL;
/* get base address of first transmit descriptor */ /* get base address of first transmit descriptor */
xq->var->xbdl_ba = ((xq->var->xbdl[1] & 0x3F) << 16) | (xq->var->xbdl[0] & ~01); xq->var->xbdl_ba = ((xq->var->xbdl[1] & 0x3F) << 16) | (xq->var->xbdl[0] & ~01);
/* When debugging, walk and display the buffer descriptor list */
xq_show_debug_bdl(xq, xq->var->xbdl_ba);
/* process xbdl */ /* process xbdl */
status = xq_process_xbdl(xq); status = xq_process_xbdl(xq);
@ -1475,8 +1566,8 @@ t_stat xq_process_turbo_rbdl(CTLR* xq)
rbl -= used; rbl -= used;
rbuf = &item->packet.msg[used]; rbuf = &item->packet.msg[used];
} else { } else {
/* adjust runt packets */ /* adjust non loopback runt packets */
if (rbl < ETH_MIN_PACKET) { if ((item->type != 1) && (rbl < ETH_MIN_PACKET)) {
xq->var->stats.runt += 1; xq->var->stats.runt += 1;
sim_debug(DBG_WRN, xq->dev, "Runt detected, size = %d\n", rbl); sim_debug(DBG_WRN, xq->dev, "Runt detected, size = %d\n", rbl);
/* pad runts with zeros up to minimum size - this allows "legal" (size - 60) /* pad runts with zeros up to minimum size - this allows "legal" (size - 60)
@ -1564,6 +1655,8 @@ t_stat xq_process_turbo_xbdl(CTLR* xq)
/* clear transmit buffer */ /* clear transmit buffer */
xq->var->write_buffer.len = 0; xq->var->write_buffer.len = 0;
free (xq->var->write_buffer.oversize);
xq->var->write_buffer.oversize = NULL;
/* Process each descriptor in the transmit ring */ /* Process each descriptor in the transmit ring */
do { do {
@ -1587,10 +1680,13 @@ t_stat xq_process_turbo_xbdl(CTLR* xq)
address = ((xq->var->xring[i].hadr & 0x3F ) << 16) | xq->var->xring[i].ladr; address = ((xq->var->xring[i].hadr & 0x3F ) << 16) | xq->var->xring[i].ladr;
b_length = (xq->var->xring[i].tmd3 & XQ_TMD3_BCT); b_length = (xq->var->xring[i].tmd3 & XQ_TMD3_BCT);
/* add to transmit buffer, making sure it's not too big */ /* add to transmit buffer, accomodating it if it is too big */
if ((xq->var->write_buffer.len + b_length) > sizeof(xq->var->write_buffer.msg)) if ((xq->var->write_buffer.len + b_length) > sizeof(xq->var->write_buffer.msg)) {
b_length = (uint16)(sizeof(xq->var->write_buffer.msg) - xq->var->write_buffer.len); xq->var->write_buffer.oversize = realloc (xq->var->write_buffer.oversize, xq->var->write_buffer.len + b_length);
status = Map_ReadB(address, b_length, &xq->var->write_buffer.msg[xq->var->write_buffer.len]); if (xq->var->write_buffer.len <= sizeof(xq->var->write_buffer.msg))
memcpy (xq->var->write_buffer.oversize, xq->var->write_buffer.msg, xq->var->write_buffer.len);
}
status = Map_ReadB(address, b_length, xq->var->write_buffer.oversize ? &xq->var->write_buffer.oversize[xq->var->write_buffer.len] : &xq->var->write_buffer.msg[xq->var->write_buffer.len]);
if (status != SCPE_OK) if (status != SCPE_OK)
return xq_nxm_error(xq); return xq_nxm_error(xq);
@ -2086,6 +2182,11 @@ t_stat xq_process_bootrom (CTLR* xq)
/* reset sanity timer */ /* reset sanity timer */
xq_reset_santmr(xq); xq_reset_santmr(xq);
/* Turn on all 3 DEQNA Leds */
xq->var->setup.l1 = 1;
xq->var->setup.l2 = 1;
xq->var->setup.l3 = 1;
return SCPE_OK; return SCPE_OK;
} }
#endif /* ifdef VM_PDP11 */ #endif /* ifdef VM_PDP11 */
@ -2387,6 +2488,13 @@ t_stat xq_reset(DEVICE* dptr)
xq->var->sanity.quarter_secs = XQ_HW_SANITY_SECS * 4/*qsec*/; xq->var->sanity.quarter_secs = XQ_HW_SANITY_SECS * 4/*qsec*/;
} }
if (sim_switches & SWMASK ('P')) { /* Powerup? */
/* Turn on all 3 DEQNA Leds */
xq->var->setup.l1 = 1;
xq->var->setup.l2 = 1;
xq->var->setup.l3 = 1;
}
return auto_config (0, 0); /* run autoconfig */ return auto_config (0, 0); /* run autoconfig */
} }

View file

@ -96,6 +96,8 @@ extern int32 int_req[IPL_HLVL];
#define XQ_HW_SANITY_SECS 240 /* seconds before HW sanity timer expires */ #define XQ_HW_SANITY_SECS 240 /* seconds before HW sanity timer expires */
#define XQ_MAX_CONTROLLERS 2 /* maximum controllers allowed */ #define XQ_MAX_CONTROLLERS 2 /* maximum controllers allowed */
#define XQ_MAX_RCV_PACKET 1600 /* Maximum receive packet data */
enum xq_type {XQ_T_DEQNA, XQ_T_DELQA, XQ_T_DELQA_PLUS}; enum xq_type {XQ_T_DEQNA, XQ_T_DELQA, XQ_T_DELQA_PLUS};
struct xq_sanity { struct xq_sanity {

View file

@ -757,6 +757,14 @@ t_stat ethq_destroy(ETH_QUE* que)
void ethq_clear(ETH_QUE* que) void ethq_clear(ETH_QUE* que)
{ {
int i;
/* free up any extended packets */
for (i=0; i<que->max; ++i)
if (que->item[i].packet.oversize) {
free (que->item[i].packet.oversize);
que->item[i].packet.oversize = NULL;
}
/* clear packet array */ /* clear packet array */
memset(que->item, 0, sizeof(struct eth_item) * que->max); memset(que->item, 0, sizeof(struct eth_item) * que->max);
/* clear rest of structure */ /* clear rest of structure */
@ -768,6 +776,8 @@ void ethq_remove(ETH_QUE* que)
struct eth_item* item = &que->item[que->head]; struct eth_item* item = &que->item[que->head];
if (que->count) { if (que->count) {
if (item->packet.oversize)
free (item->packet.oversize);
memset(item, 0, sizeof(struct eth_item)); memset(item, 0, sizeof(struct eth_item));
if (++que->head == que->max) if (++que->head == que->max)
que->head = 0; que->head = 0;
@ -804,15 +814,23 @@ void ethq_insert_data(ETH_QUE* que, int32 type, const uint8 *data, int used, int
item->packet.len = len; item->packet.len = len;
item->packet.used = used; item->packet.used = used;
item->packet.crc_len = crc_len; item->packet.crc_len = crc_len;
memcpy(item->packet.msg, data, ((len > crc_len) ? len : crc_len)); if (len <= sizeof (item->packet.msg)) {
if (crc_data && (crc_len > len)) memcpy(item->packet.msg, data, ((len > crc_len) ? len : crc_len));
memcpy(&item->packet.msg[len], crc_data, ETH_CRC_SIZE); if (crc_data && (crc_len > len))
memcpy(&item->packet.msg[len], crc_data, ETH_CRC_SIZE);
}
else {
item->packet.oversize = realloc (item->packet.oversize, ((len > crc_len) ? len : crc_len));
memcpy(item->packet.oversize, data, ((len > crc_len) ? len : crc_len));
if (crc_data && (crc_len > len))
memcpy(&item->packet.oversize[len], crc_data, ETH_CRC_SIZE);
}
item->packet.status = status; item->packet.status = status;
} }
void ethq_insert(ETH_QUE* que, int32 type, ETH_PACK* pack, int32 status) void ethq_insert(ETH_QUE* que, int32 type, ETH_PACK* pack, int32 status)
{ {
ethq_insert_data(que, type, pack->msg, pack->used, pack->len, pack->crc_len, NULL, status); ethq_insert_data(que, type, pack->oversize ? pack->oversize : pack->msg, pack->used, pack->len, pack->crc_len, NULL, status);
} }
/*============================================================================*/ /*============================================================================*/

View file

@ -173,6 +173,7 @@
struct eth_packet { struct eth_packet {
uint8 msg[ETH_FRAME_SIZE]; /* ethernet frame (message) */ uint8 msg[ETH_FRAME_SIZE]; /* ethernet frame (message) */
uint8 *oversize; /* oversized frame (message) */
uint32 len; /* packet length without CRC */ uint32 len; /* packet length without CRC */
uint32 used; /* bytes processed (used in packet chaining) */ uint32 used; /* bytes processed (used in packet chaining) */
int status; /* transmit/receive status */ int status; /* transmit/receive status */