am79c90: fixes (nw)

* took over copyright due to near-total rewrite
* tweaked logging
* corrected tx/rx on/off behaviour
* block register access while not stopped
* crc error is an error
This commit is contained in:
Patrick Mackinlay 2018-11-22 14:46:45 +07:00
parent 7c932d2b5c
commit 3349e3e990
2 changed files with 75 additions and 74 deletions

View File

@ -1,13 +1,43 @@
// license:BSD-3-Clause // license:BSD-3-Clause
// copyright-holders:Ryan Holtz // copyright-holders:Patrick Mackinlay
/*****************************************************************************
AMD Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE) /*
* AMD Am7990 Local Area Network Controller for Ethernet (LANCE) and Am79C90
TODO: * CMOS Local Area Network Controller for Ethernet (C-LANCE).
- Error handling *
* Sources:
*****************************************************************************/ *
* https://datasheet.datasheetarchive.com/originals/distributors/Datasheets-115/DSAP00824.pdf
*
* TODO
* - hp9k/3xx diagnostic failures
*
* _____ _____
* Vss 1 |* \_/ | 48 Vdd
* DAL7 2 | | 47 DAL8
* DAL6 3 | | 46 DAL9
* DAL5 4 | | 45 DAL10
* DAL4 5 | | 44 DAL11
* DAL3 6 | | 43 DAL12
* DAL2 7 | | 42 DAL13
* DAL1 8 | | 41 DAL14
* DAL0 9 | | 40 DAL15
* READ 10 | | 39 A16
* /INTR 11 | | 38 A17
* /DALI 12 | Am79C90 | 37 A18
* /DALI 13 | | 36 A19
* /DAS 14 | | 35 A20
* /BM0,BYTE 15 | | 34 A21
* /BM1,/BUSAKO 16 | | 33 A22
* /HOLD,/BUSRQ 17 | | 32 A23
* ALE,/AS 18 | | 31 RX
* /HLDA 19 | | 30 RENA
* /CS 20 | | 29 TX
* ADR 21 | | 28 CLSN
* /READY 22 | | 27 RCLK
* /RESET 23 | | 26 TENA
* Vss 24 |_____________| 25 TCLK
*/
#include "emu.h" #include "emu.h"
#include "am79c90.h" #include "am79c90.h"
@ -89,8 +119,6 @@ void am7990_device_base::device_reset()
m_csr[3] = 0; m_csr[3] = 0;
m_lb_length = 0; m_lb_length = 0;
m_transmit_poll->enable(false);
update_interrupts(); update_interrupts();
} }
@ -103,6 +131,7 @@ void am7990_device_base::update_interrupts()
{ {
m_intr_out_state = !m_intr_out_state; m_intr_out_state = !m_intr_out_state;
m_intr_out_cb(m_intr_out_state); m_intr_out_cb(m_intr_out_state);
LOG("interrupt asserted\n");
} }
} }
else else
@ -171,6 +200,8 @@ int am7990_device_base::receive(u8 *buf, int length)
u32 const rx_buf_address = (u32(m_rx_md[1] & 0xff) << 16) | m_rx_md[0]; u32 const rx_buf_address = (u32(m_rx_md[1] & 0xff) << 16) | m_rx_md[0];
int const rx_buf_length = -s16(m_rx_md[2]); int const rx_buf_length = -s16(m_rx_md[2]);
LOGMASKED(LOG_RXTX, "receive buffer address 0x%06x length %d\n", rx_buf_address, rx_buf_length);
// write the data to memory // write the data to memory
int const count = std::min(length - offset, rx_buf_length); int const count = std::min(length - offset, rx_buf_length);
dma_out(rx_buf_address, &buf[offset], count); dma_out(rx_buf_address, &buf[offset], count);
@ -213,7 +244,7 @@ int am7990_device_base::receive(u8 *buf, int length)
{ {
LOGMASKED(LOG_RXTX, "receive incorrect fcs 0x%08x\n", ~crc); LOGMASKED(LOG_RXTX, "receive incorrect fcs 0x%08x\n", ~crc);
m_rx_md[1] |= RMD1_CRC; m_rx_md[1] |= RMD1_ERR | RMD1_CRC;
} }
} }
@ -250,7 +281,7 @@ void am7990_device_base::recv_complete_cb(int result)
void am7990_device_base::transmit_poll(void *ptr, s32 param) void am7990_device_base::transmit_poll(void *ptr, s32 param)
{ {
// receive pending internal loopback data // receive pending internal loopback data
if (m_lb_length && (m_mode & MODE_LOOP) && (m_mode & MODE_INTL)) if (m_lb_length && (m_mode & MODE_LOOP) && (m_mode & MODE_INTL) && (m_csr[0] & CSR0_RXON))
{ {
int const result = receive(m_lb_buf, m_lb_length); int const result = receive(m_lb_buf, m_lb_length);
m_lb_length = 0; m_lb_length = 0;
@ -259,12 +290,17 @@ void am7990_device_base::transmit_poll(void *ptr, s32 param)
return; return;
} }
u32 const ring_address = (m_tx_ring_base + (m_tx_ring_pos << 3)) & RING_ADDR_MASK; if (m_csr[0] & CSR0_TXON)
m_tx_md[1] = m_dma_in_cb(ring_address | 2); {
m_csr[0] &= ~CSR0_TDMD;
// start transmitting if we own the current descriptor u32 const ring_address = (m_tx_ring_base + (m_tx_ring_pos << 3)) & RING_ADDR_MASK;
if (m_tx_md[1] & TMD1_OWN) m_tx_md[1] = m_dma_in_cb(ring_address | 2);
transmit();
// start transmitting if we own the current descriptor
if (m_tx_md[1] & TMD1_OWN)
transmit();
}
} }
void am7990_device_base::transmit() void am7990_device_base::transmit()
@ -293,7 +329,7 @@ void am7990_device_base::transmit()
m_transmit_poll->enable(false); m_transmit_poll->enable(false);
// check whether to append fcs or not // check whether to append fcs or not
bool const add_fcs = !(m_mode & MODE_DTCR) || ((type() == AM79C90) && (m_tx_md[1] & TMD1_ADD_FCS)); bool const append_fcs = !(m_mode & MODE_DTCR) || ((type() == AM79C90) && (m_tx_md[1] & TMD1_ADD_FCS));
u8 buf[4096]; u8 buf[4096];
int length = 0; int length = 0;
@ -308,6 +344,8 @@ void am7990_device_base::transmit()
u32 const tx_buf_address = (u32(m_tx_md[1] & 0xff) << 16) | m_tx_md[0]; u32 const tx_buf_address = (u32(m_tx_md[1] & 0xff) << 16) | m_tx_md[0];
u16 const tx_buf_length = -s16(m_tx_md[2]); u16 const tx_buf_length = -s16(m_tx_md[2]);
LOGMASKED(LOG_RXTX, "transmit buffer address 0x%06x length %d\n", tx_buf_address, tx_buf_length);
// minimum length 100 when chaining, or 64 when not (except for internal loopback mode) // minimum length 100 when chaining, or 64 when not (except for internal loopback mode)
if (!length && tx_buf_length < ((m_tx_md[1] & TMD1_ENP) ? 64 : 100) && ((m_mode & (MODE_LOOP | MODE_INTL)) != (MODE_LOOP | MODE_INTL))) if (!length && tx_buf_length < ((m_tx_md[1] & TMD1_ENP) ? 64 : 100) && ((m_mode & (MODE_LOOP | MODE_INTL)) != (MODE_LOOP | MODE_INTL)))
logerror("first transmit buffer length %d less than required minimum\n", tx_buf_length); logerror("first transmit buffer length %d less than required minimum\n", tx_buf_length);
@ -343,7 +381,6 @@ void am7990_device_base::transmit()
// turn off the transmitter // turn off the transmitter
m_csr[0] &= ~CSR0_TXON; m_csr[0] &= ~CSR0_TXON;
m_transmit_poll->enable(false);
break; break;
} }
} }
@ -352,7 +389,7 @@ void am7990_device_base::transmit()
} }
// compute and append the fcs // compute and append the fcs
if (add_fcs) if (append_fcs)
{ {
u32 const crc = util::crc32_creator::simple(buf, length); u32 const crc = util::crc32_creator::simple(buf, length);
@ -380,7 +417,7 @@ void am7990_device_base::transmit()
return; return;
} }
int const fcs_length = add_fcs ? 4 : 0; int const fcs_length = append_fcs ? 4 : 0;
if ((length - fcs_length) < 8 || (length - fcs_length) > 32) if ((length - fcs_length) < 8 || (length - fcs_length) > 32)
{ {
@ -432,10 +469,8 @@ void am7990_device_base::send_complete_cb(int result)
m_csr[0] |= CSR0_TINT | CSR0_INTR; m_csr[0] |= CSR0_TINT | CSR0_INTR;
update_interrupts(); update_interrupts();
// TODO: back-to-back transmit // resume transmit polling (back-to-back)
m_transmit_poll->adjust(attotime::zero, 0, TX_POLL_PERIOD);
// resume transmit polling
m_transmit_poll->enable(true);
} }
READ16_MEMBER(am7990_device_base::regs_r) READ16_MEMBER(am7990_device_base::regs_r)
@ -444,7 +479,10 @@ READ16_MEMBER(am7990_device_base::regs_r)
{ {
LOGMASKED(LOG_REG, "regs_r csr%d data 0x%04x (%s)\n", m_rap, m_csr[m_rap], machine().describe_context()); LOGMASKED(LOG_REG, "regs_r csr%d data 0x%04x (%s)\n", m_rap, m_csr[m_rap], machine().describe_context());
return m_csr[m_rap]; if (m_rap && !(m_csr[0] & CSR0_STOP))
return space.unmap();
else
return m_csr[m_rap];
} }
else else
return m_rap; return m_rap;
@ -528,14 +566,14 @@ WRITE16_MEMBER(am7990_device_base::regs_w)
m_csr[0] &= ~CSR0_TXON; m_csr[0] &= ~CSR0_TXON;
else else
m_csr[0] |= CSR0_TXON; m_csr[0] |= CSR0_TXON;
if (m_csr[0] & CSR0_TXON)
m_transmit_poll->enable(true);
} }
// transmit demand // transmit demand
if ((data & CSR0_TDMD) && (m_csr[0] & CSR0_TXON)) if ((data & CSR0_TDMD) && !(m_csr[0] & CSR0_TDMD))
{
m_csr[0] |= CSR0_TDMD;
m_transmit_poll->adjust(attotime::zero, 0, TX_POLL_PERIOD); m_transmit_poll->adjust(attotime::zero, 0, TX_POLL_PERIOD);
}
// ERR == BABL || CERR || MISS || MERR // ERR == BABL || CERR || MISS || MERR
if (m_csr[0] & CSR0_ANY_ERR) if (m_csr[0] & CSR0_ANY_ERR)
@ -560,7 +598,8 @@ WRITE16_MEMBER(am7990_device_base::regs_w)
// Datasheet says "must be zero", but doesn't indicate what // Datasheet says "must be zero", but doesn't indicate what
// happens if it's written non-zero. Must be writable to pass // happens if it's written non-zero. Must be writable to pass
// system diagnostic on MIPS RS2030. // system diagnostic on MIPS RS2030.
m_csr[1] = data; if (m_csr[0] & CSR0_STOP)
m_csr[1] = data;
break; break;
case 2: // Most significant 8 bits of the Initialization Block case 2: // Most significant 8 bits of the Initialization Block
@ -568,11 +607,13 @@ WRITE16_MEMBER(am7990_device_base::regs_w)
// write as zero, while LANCE datasheet just says "reserved". // write as zero, while LANCE datasheet just says "reserved".
// MIPS RS2030 diagnostic requires these bits to be writable, // MIPS RS2030 diagnostic requires these bits to be writable,
// so assuming this is older device behaviour. // so assuming this is older device behaviour.
m_csr[2] = (type() == AM7990) ? data : (data & 0x00ff); if (m_csr[0] & CSR0_STOP)
m_csr[2] = (type() == AM7990) ? data : (data & 0x00ff);
break; break;
case 3: // Bus master interface case 3: // Bus master interface
m_csr[3] = data & CSR3_MASK; if (m_csr[0] & CSR0_STOP)
m_csr[3] = data & CSR3_MASK;
break; break;
} }
} }
@ -588,10 +629,7 @@ void am7990_device_base::initialize()
LOG("INITIALIZE initialization block address 0x%08x\n", init_addr); LOG("INITIALIZE initialization block address 0x%08x\n", init_addr);
for (int i = 0; i < 12; i++) for (int i = 0; i < 12; i++)
{
init_block[i] = m_dma_in_cb(init_addr + i * 2); init_block[i] = m_dma_in_cb(init_addr + i * 2);
LOGMASKED(LOG_INIT, "IADR +%02d: 0x%04x\n", i * 2, init_block[i]);
}
m_mode = init_block[0]; m_mode = init_block[0];
@ -623,8 +661,6 @@ void am7990_device_base::initialize()
m_csr[0] |= CSR0_IDON | CSR0_INIT; m_csr[0] |= CSR0_IDON | CSR0_INIT;
m_csr[0] &= ~CSR0_STOP; m_csr[0] &= ~CSR0_STOP;
m_transmit_poll->enable(false);
} }
void am7990_device_base::dma_in(u32 address, u8 *buf, int length) void am7990_device_base::dma_in(u32 address, u8 *buf, int length)

View File

@ -1,40 +1,5 @@
// license:BSD-3-Clause // license:BSD-3-Clause
// copyright-holders:Ryan Holtz // copyright-holders:Patrick Mackinlay
/*****************************************************************************
AMD Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)
TODO:
- Error handling
******************************************************************************
_____ _____
Vss 1 |* \_/ | 48 Vdd
DAL7 2 | | 47 DAL8
DAL6 3 | | 46 DAL9
DAL5 4 | | 45 DAL10
DAL4 5 | | 44 DAL11
DAL3 6 | | 43 DAL12
DAL2 7 | | 42 DAL13
DAL1 8 | | 41 DAL14
DAL0 9 | | 40 DAL15
READ 10 | | 39 A16
/INTR 11 | | 38 A17
/DALI 12 | Am79C90 | 37 A18
/DALI 13 | | 36 A19
/DAS 14 | | 35 A20
/BM0,BYTE 15 | | 34 A21
/BM1,/BUSAKO 16 | | 33 A22
/HOLD,/BUSRQ 17 | | 32 A23
ALE,/AS 18 | | 31 RX
/HLDA 19 | | 30 RENA
/CS 20 | | 29 TX
ADR 21 | | 28 CLSN
/READY 22 | | 27 RCLK
/RESET 23 | | 26 TENA
Vss 24 |_____________| 25 TCLK
**********************************************************************/
#ifndef MAME_MACHINE_AM79C90_H #ifndef MAME_MACHINE_AM79C90_H
#define MAME_MACHINE_AM79C90_H #define MAME_MACHINE_AM79C90_H