Line 68... |
Line 68... |
#define ETH_DEBUG 0
|
#define ETH_DEBUG 0
|
#ifndef ETH_DEBUG
|
#ifndef ETH_DEBUG
|
# define ETH_DEBUG 1
|
# define ETH_DEBUG 1
|
#endif
|
#endif
|
|
|
/*! Period (clock cycles) for rescheduling Rx and Tx controllers. */
|
/*! Period (clock cycles) for rescheduling Rx and Tx controllers.
|
#define RTX_RESCHED_PERIOD 10000
|
Experimenting with FTP on one machine suggests that a value of 500-2000 is
|
|
optimal. It's a trade off between prompt response and extra computational
|
|
load for Or1ksim. */
|
|
#define RTX_RESCHED_PERIOD 2000
|
|
|
/*! MAC address that is always accepted. */
|
/*! MAC address that is always accepted. */
|
static const unsigned char mac_broadcast[ETHER_ADDR_LEN] =
|
static const unsigned char mac_broadcast[ETHER_ADDR_LEN] =
|
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
|
Line 287... |
Line 290... |
unsigned long int bd_info = eth->regs.bd_ram[eth->tx_bd_index];
|
unsigned long int bd_info = eth->regs.bd_ram[eth->tx_bd_index];
|
unsigned long int bd_addr = eth->regs.bd_ram[eth->tx_bd_index + 1];
|
unsigned long int bd_addr = eth->regs.bd_ram[eth->tx_bd_index + 1];
|
unsigned char buf[ETH_MAXPL];
|
unsigned char buf[ETH_MAXPL];
|
long int packet_length;
|
long int packet_length;
|
long int bytes_sent;
|
long int bytes_sent;
|
|
int ok_to_int_p;
|
|
|
/* Get the packet length */
|
/* Get the packet length */
|
packet_length = GET_FIELD (bd_info, ETH_TX_BD, LENGTH);
|
packet_length = GET_FIELD (bd_info, ETH_TX_BD, LENGTH);
|
|
|
/* Clear error status bits and retry count. */
|
/* Clear error status bits and retry count. */
|
Line 318... |
Line 322... |
write the whole packet, then we retry. */
|
write the whole packet, then we retry. */
|
if (eth_write_packet (eth, buf, packet_length) == packet_length)
|
if (eth_write_packet (eth, buf, packet_length) == packet_length)
|
{
|
{
|
CLEAR_FLAG (bd_info, ETH_TX_BD, READY);
|
CLEAR_FLAG (bd_info, ETH_TX_BD, READY);
|
SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, TXB);
|
SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, TXB);
|
|
ok_to_int_p = TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, TXB_M);
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Does this retry mechanism really work? */
|
/* Does this retry mechanism really work? */
|
CLEAR_FLAG (bd_info, ETH_TX_BD, READY);
|
CLEAR_FLAG (bd_info, ETH_TX_BD, READY);
|
CLEAR_FLAG (bd_info, ETH_TX_BD, COLLISION);
|
CLEAR_FLAG (bd_info, ETH_TX_BD, COLLISION);
|
SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, TXE);
|
SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, TXE);
|
|
ok_to_int_p = TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, TXE_M);
|
#if ETH_DEBUG
|
#if ETH_DEBUG
|
printf ("Transmit retry request.\n");
|
printf ("Transmit retry request.\n");
|
#endif
|
#endif
|
}
|
}
|
|
|
Line 336... |
Line 342... |
eth->regs.bd_ram[eth->tx_bd_index] = bd_info;
|
eth->regs.bd_ram[eth->tx_bd_index] = bd_info;
|
|
|
/* Generate interrupt to indicate transfer complete, under the
|
/* Generate interrupt to indicate transfer complete, under the
|
following criteria all being met:
|
following criteria all being met:
|
- either INT_MASK flag for Tx (OK or error) is set
|
- either INT_MASK flag for Tx (OK or error) is set
|
- the bugger descriptor has its IRQ flag set
|
- the buffer descriptor has its IRQ flag set
|
- there is no interrupt in progress.
|
- there is no interrupt in progress.
|
|
|
@todo We ought to warn if we get here and fail to set an IRQ. */
|
@todo We ought to warn if we get here and fail to set an IRQ. */
|
if ((TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, TXE_M) ||
|
if (ok_to_int_p && TEST_FLAG (bd_info, ETH_TX_BD, IRQ))
|
TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, TXB_M)) &&
|
|
TEST_FLAG (bd_info, ETH_TX_BD, IRQ))
|
|
{
|
{
|
if (eth->int_line_stat)
|
if (eth->int_line_stat)
|
{
|
{
|
fprintf (stderr, "Warning: Interrupt active during Tx.\n");
|
fprintf (stderr, "Warning: Interrupt active during Tx.\n");
|
}
|
}
|
Line 404... |
Line 408... |
{
|
{
|
eth_flush_bd (eth);
|
eth_flush_bd (eth);
|
}
|
}
|
}
|
}
|
|
|
/* Wake up again after 1 ticks (was 10, changed by Julius). */
|
/* Reschedule to wake up again. */
|
SCHED_ADD (eth_controller_tx_clock, dat, RTX_RESCHED_PERIOD);
|
SCHED_ADD (eth_controller_tx_clock, dat, RTX_RESCHED_PERIOD);
|
|
|
} /* eth_controller_tx_clock () */
|
} /* eth_controller_tx_clock () */
|
|
|
|
|
Line 833... |
Line 837... |
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Whatever happens, we reschedule a wake up in the future. This used to be
|
/* Whatever happens, we reschedule a wake up in the future. */
|
every 10 ticks, but now it is very 1 tick. */
|
|
SCHED_ADD (eth_controller_rx_clock, dat, RTX_RESCHED_PERIOD);
|
SCHED_ADD (eth_controller_rx_clock, dat, RTX_RESCHED_PERIOD);
|
|
|
} /* eth_controller_rx_clock () */
|
} /* eth_controller_rx_clock () */
|
|
|
|
|
Line 1357... |
Line 1360... |
|
|
|
|
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
/*!Write a register
|
/*!Write a register
|
|
|
|
@note Earlier versions of this code treated ETH_INT_SOURCE as an "interrupt
|
|
pending" register and reissued interrupts if ETH_INT_MASK was
|
|
changed, enabling an interrupt that had previously been cleared. This
|
|
led to spurious double interrupt. In the present version, the only
|
|
way an interrupt can be generated is at the time ETH_INT_SOURCE is
|
|
set in the Tx/Rx controllers and the only way an interrupt can be
|
|
cleared is by writing to ETH_INT_SOURCE.
|
|
|
@param[in] addr The address of the register to read (offset from base).
|
@param[in] addr The address of the register to read (offset from base).
|
@param[in] value The value to write.
|
@param[in] value The value to write.
|
@param[in] dat The Ethernet interface data structure, cast to a void
|
@param[in] dat The Ethernet interface data structure, cast to a void
|
pointer. */
|
pointer. */
|
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
Line 1369... |
Line 1380... |
{
|
{
|
struct eth_device *eth = dat;
|
struct eth_device *eth = dat;
|
|
|
#if ETH_DEBUG
|
#if ETH_DEBUG
|
/* Only trace registers of particular interest */
|
/* Only trace registers of particular interest */
|
|
|
switch (addr)
|
switch (addr)
|
{
|
{
|
case ETH_MODER:
|
case ETH_MODER:
|
case ETH_INT_SOURCE:
|
case ETH_INT_SOURCE:
|
case ETH_INT_MASK:
|
case ETH_INT_MASK:
|
Line 1384... |
Line 1394... |
case ETH_COLLCONF:
|
case ETH_COLLCONF:
|
case ETH_TX_BD_NUM:
|
case ETH_TX_BD_NUM:
|
case ETH_CTRLMODER:
|
case ETH_CTRLMODER:
|
case ETH_MAC_ADDR0:
|
case ETH_MAC_ADDR0:
|
case ETH_MAC_ADDR1:
|
case ETH_MAC_ADDR1:
|
printf ("eth_write32: 0x%08lx to %s ", (unsigned long) value,
|
printf ("eth_write32: 0x%08lx to %s ", (unsigned long int) value,
|
eth_regname (addr));
|
eth_regname (addr));
|
|
|
}
|
}
|
|
|
/* Detail register transitions on MODER, INT_SOURCE AND INT_MASK */
|
/* Detail register transitions on MODER, INT_SOURCE AND INT_MASK */
|
|
|
switch (addr)
|
switch (addr)
|
{
|
{
|
case ETH_MODER:
|
case ETH_MODER:
|
printf("0x%08lx -> ", (unsigned long) eth->regs.moder);
|
printf("0x%08lx -> ", (unsigned long) eth->regs.moder);
|
break;
|
break;
|
Line 1403... |
Line 1411... |
break;
|
break;
|
case ETH_INT_MASK:
|
case ETH_INT_MASK:
|
printf("0x%08lx -> ", (unsigned long) eth->regs.int_mask);
|
printf("0x%08lx -> ", (unsigned long) eth->regs.int_mask);
|
break;
|
break;
|
}
|
}
|
|
|
|
|
|
|
#endif
|
#endif
|
|
|
switch (addr)
|
switch (addr)
|
{
|
{
|
case ETH_MODER:
|
case ETH_MODER:
|
Line 1472... |
Line 1477... |
break;
|
break;
|
|
|
case ETH_INT_MASK:
|
case ETH_INT_MASK:
|
eth->regs.int_mask = value;
|
eth->regs.int_mask = value;
|
|
|
/* If we enable interrupts, the core is not currently processing an
|
/* The old code would report an interrupt if we enabled an interrupt
|
interrupt, and there is an interrupt pending, then report that
|
when if we enabled interrupts and the core was not currently
|
interrupt.
|
processing an interrupt, and there was an interrupt pending.
|
|
|
|
However this led (at least on some machines) to orphaned interrupts
|
|
in the device driver. So in this version of the code we do not report
|
|
interrupts on a mask change.
|
|
|
|
This is not apparently consistent with the Verilog, but it does mean
|
|
that the orphaned interrupts problem does not occur, and has no
|
|
apparent effect on Ethernet performance. More investigation is needed
|
|
to determine if this is a bug in Or1ksim interrupt handling, or a bug
|
|
in the device driver, which does not manifest with real HW.
|
|
|
Otherwise clear down the interrupt.
|
Otherwise clear down the interrupt.
|
|
|
@todo Is this really right. */
|
@todo Is this really right. */
|
if ((eth->regs.int_source & eth->regs.int_mask) && !eth->int_line_stat)
|
if ((eth->regs.int_source & eth->regs.int_mask) && !eth->int_line_stat)
|
{
|
{
|
report_interrupt (eth->mac_int);
|
#if ETH_DEBUG
|
|
printf ("ETH_MASK changed with apparent pending interrupt.\n");
|
|
#endif
|
}
|
}
|
else if (eth->int_line_stat)
|
else if (eth->int_line_stat)
|
{
|
{
|
clear_interrupt (eth->mac_int);
|
clear_interrupt (eth->mac_int);
|
eth->int_line_stat = 0;
|
eth->int_line_stat = 0;
|
}
|
}
|
|
|
break;
|
break;
|
|
|
case ETH_IPGT: eth->regs.ipgt = value; break;
|
case ETH_IPGT: eth->regs.ipgt = value; break;
|
case ETH_IPGR1: eth->regs.ipgr1 = value; break;
|
case ETH_IPGR1: eth->regs.ipgr1 = value; break;
|
case ETH_IPGR2: eth->regs.ipgr2 = value; break;
|
case ETH_IPGR2: eth->regs.ipgr2 = value; break;
|
Line 1546... |
Line 1564... |
}
|
}
|
break;
|
break;
|
}
|
}
|
|
|
#if ETH_DEBUG
|
#if ETH_DEBUG
|
|
|
switch (addr)
|
switch (addr)
|
{
|
{
|
case ETH_MODER:
|
case ETH_MODER:
|
printf("0x%08lx", (unsigned long) eth->regs.moder);
|
printf ("0x%08lx\n", (unsigned long) eth->regs.moder);
|
break;
|
break;
|
case ETH_INT_SOURCE:
|
case ETH_INT_SOURCE:
|
printf("0x%08lx", (unsigned long) eth->regs.int_source);
|
printf ("0x%08lx\n", (unsigned long) eth->regs.int_source);
|
break;
|
break;
|
case ETH_INT_MASK:
|
case ETH_INT_MASK:
|
printf("0x%08lx", (unsigned long) eth->regs.int_mask);
|
printf ("0x%08lx\n", (unsigned long) eth->regs.int_mask);
|
break;
|
break;
|
case ETH_IPGT:
|
case ETH_IPGT:
|
case ETH_IPGR1:
|
case ETH_IPGR1:
|
case ETH_IPGR2:
|
case ETH_IPGR2:
|
case ETH_PACKETLEN:
|
case ETH_PACKETLEN:
|
case ETH_COLLCONF:
|
case ETH_COLLCONF:
|
case ETH_TX_BD_NUM:
|
case ETH_TX_BD_NUM:
|
case ETH_CTRLMODER:
|
case ETH_CTRLMODER:
|
case ETH_MAC_ADDR0:
|
case ETH_MAC_ADDR0:
|
case ETH_MAC_ADDR1:
|
case ETH_MAC_ADDR1:
|
|
printf("\n");
|
break;
|
break;
|
}
|
}
|
|
|
printf("\n");
|
|
#endif
|
#endif
|
|
|
|
|
} /* eth_write32 () */
|
} /* eth_write32 () */
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Enable or disable the Ethernet interface
|
/*!Enable or disable the Ethernet interface
|