Line 1... |
Line 1... |
/* ethernet.c -- Simulation of Ethernet MAC
|
/* ethernet.c -- Simulation of Ethernet MAC
|
|
|
Copyright (C) 2001 by Erez Volk, erez@opencores.org
|
Copyright (C) 2001 by Erez Volk, erez@opencores.org
|
Ivan Guzvinec, ivang@opencores.org
|
Ivan Guzvinec, ivang@opencores.org
|
Copyright (C) 2008 Embecosm Limited
|
Copyright (C) 2008, 2001 Embecosm Limited
|
Copyright (C) 2010 ORSoC
|
Copyright (C) 2010 ORSoC
|
|
|
Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
|
Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
|
Contributor Julius Baxter <julius@orsoc.se>
|
Contributor Julius Baxter <julius@orsoc.se>
|
|
|
Line 61... |
Line 61... |
#include "pic.h"
|
#include "pic.h"
|
#include "sched.h"
|
#include "sched.h"
|
#include "toplevel-support.h"
|
#include "toplevel-support.h"
|
#include "sim-cmd.h"
|
#include "sim-cmd.h"
|
|
|
|
|
|
/* Control debug messages */
|
|
#ifndef ETH_DEBUG
|
|
# define ETH_DEBUG 1
|
|
#endif
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/*!Structure describing the Ethernet device */
|
|
/* -------------------------------------------------------------------------- */
|
struct eth_device
|
struct eth_device
|
{
|
{
|
/* Is peripheral enabled */
|
/* Basic stuff about the device */
|
int enabled;
|
int enabled; /* Is peripheral enabled */
|
|
oraddr_t baseaddr; /* Base address in memory */
|
|
unsigned long int base_vapi_id; /* Start of VAPI ID block */
|
|
|
/* Base address in memory */
|
/* DMA controller this MAC is connected to, and associated channels */
|
oraddr_t baseaddr;
|
|
|
|
/* Which DMA controller is this MAC connected to */
|
|
unsigned dma;
|
unsigned dma;
|
unsigned tx_channel;
|
unsigned tx_channel;
|
unsigned rx_channel;
|
unsigned rx_channel;
|
|
|
/* Our address */
|
/* Details of the hardware */
|
unsigned char mac_address[ETHER_ADDR_LEN];
|
unsigned char mac_address[ETHER_ADDR_LEN]; /* Ext HW address */
|
|
unsigned long int phy_addr; /* Int HW address */
|
/* interrupt line number */
|
unsigned long int mac_int; /* interrupt line number */
|
unsigned long mac_int;
|
int int_line_stat; /* interrupt line status */
|
|
|
/* interrupt line status */
|
/* External interface deatils */
|
int int_line_stat;
|
int rtx_type; /* Type of external i/f: FILE or TAP */
|
|
|
/* VAPI ID */
|
|
unsigned long base_vapi_id;
|
|
|
|
/* Ethernet PHY address */
|
|
unsigned long phy_addr;
|
|
|
|
/* What sort of external I/F: FILE or TAP */
|
|
int rtx_type;
|
|
|
|
/* RX and TX file names and handles for FILE type connection. */
|
/* RX and TX file names and handles for FILE type connection. */
|
char *rxfile, *txfile;
|
char *rxfile; /* Rx filename */
|
int txfd;
|
char *txfile; /* Tx filename */
|
int rxfd;
|
int txfd; /* Rx file handle */
|
off_t loopback_offset;
|
int rxfd; /* Tx file handle */
|
|
off_t loopback_offset; /* Circular buffer offset */
|
|
|
/* Info for TAP type connections */
|
/* Info for TAP type connections */
|
int rtx_fd;
|
char *tap_dev; /* The TAP device */
|
char *tap_dev;
|
int rtx_fd; /* TAP device handle */
|
|
|
/* Current TX state */
|
/* Current TX state */
|
struct
|
struct
|
{
|
{
|
unsigned long state;
|
unsigned long int bd_index;
|
unsigned long bd_index;
|
|
unsigned long bd;
|
|
unsigned long bd_addr;
|
|
unsigned working, waiting_for_dma, error;
|
|
long packet_length;
|
|
unsigned minimum_length, maximum_length;
|
|
unsigned add_crc;
|
|
unsigned crc_dly;
|
|
unsigned long crc_value;
|
|
long bytes_left, bytes_sent;
|
|
} tx;
|
} tx;
|
|
|
/* Current RX state */
|
/* Current RX state */
|
struct
|
struct
|
{
|
{
|
unsigned long state;
|
enum {
|
unsigned long bd_index;
|
ETH_RXSTATE_IDLE, /* Was set to 0 */
|
unsigned long bd;
|
ETH_RXSTATE_WAIT4BD, /* Was set to 10 */
|
unsigned long bd_addr;
|
ETH_RXSTATE_RECV, /* Was set to 20 */
|
|
ETH_RXSTATE_WRITEFIFO, /* Was set to 30 */
|
|
} state;
|
|
unsigned long int bd_index;
|
|
unsigned long int bd;
|
|
unsigned long int bd_addr;
|
int fd;
|
int fd;
|
off_t *offset;
|
off_t *offset;
|
unsigned working, error, waiting_for_dma;
|
unsigned int working;
|
long packet_length, bytes_read, bytes_left;
|
unsigned int waiting_for_dma;
|
|
unsigned int error;
|
|
long int packet_length;
|
|
long int bytes_read;
|
|
long int bytes_left;
|
} rx;
|
} rx;
|
|
|
/* Visible registers */
|
/* Visible registers */
|
struct
|
struct
|
{
|
{
|
unsigned long moder;
|
unsigned long int moder;
|
unsigned long int_source;
|
unsigned long int int_source;
|
unsigned long int_mask;
|
unsigned long int int_mask;
|
unsigned long ipgt;
|
unsigned long int ipgt;
|
unsigned long ipgr1;
|
unsigned long int ipgr1;
|
unsigned long ipgr2;
|
unsigned long int ipgr2;
|
unsigned long packetlen;
|
unsigned long int packetlen;
|
unsigned long collconf;
|
unsigned long int collconf;
|
unsigned long tx_bd_num;
|
unsigned long int tx_bd_num;
|
unsigned long controlmoder;
|
unsigned long int controlmoder;
|
unsigned long miimoder;
|
unsigned long int miimoder;
|
unsigned long miicommand;
|
unsigned long int miicommand;
|
unsigned long miiaddress;
|
unsigned long int miiaddress;
|
unsigned long miitx_data;
|
unsigned long int miitx_data;
|
unsigned long miirx_data;
|
unsigned long int miirx_data;
|
unsigned long miistatus;
|
unsigned long int miistatus;
|
unsigned long hash0;
|
unsigned long int hash0;
|
unsigned long hash1;
|
unsigned long int hash1;
|
|
|
/* Buffer descriptors */
|
/* Buffer descriptors */
|
unsigned long bd_ram[ETH_BD_SPACE / 4];
|
unsigned long int bd_ram[ETH_BD_SPACE / 4];
|
} regs;
|
} regs;
|
|
|
unsigned char rx_buff[ETH_MAXPL];
|
unsigned char rx_buff[ETH_MAXPL];
|
unsigned char tx_buff[ETH_MAXPL];
|
unsigned char tx_buff[ETH_MAXPL];
|
unsigned char lo_buff[ETH_MAXPL];
|
unsigned char lo_buff[ETH_MAXPL];
|
};
|
};
|
|
|
|
|
/* simulator interface */
|
|
static void eth_vapi_read (unsigned long id, unsigned long data, void *dat);
|
|
/* register interface */
|
|
static void eth_write32 (oraddr_t addr, uint32_t value, void *dat);
|
|
static uint32_t eth_read32 (oraddr_t addr, void *dat);
|
|
/* clock */
|
|
static void eth_controller_tx_clock (void *);
|
|
static void eth_controller_rx_clock (void *);
|
|
/* utility functions */
|
|
static ssize_t eth_read_rx_file (struct eth_device *, void *, size_t);
|
|
static void eth_skip_rx_file (struct eth_device *, off_t);
|
|
static void eth_rx_next_packet (struct eth_device *);
|
|
static void eth_write_tx_bd_num (struct eth_device *, unsigned long value);
|
|
static void eth_miim_trans (void *dat);
|
|
|
|
#define ETH_DEBUG 0
|
|
|
|
|
|
/* ========================================================================== */
|
|
/* Dummy socket routines. These are the points where we spoof an Ethernet */
|
|
/* network. */
|
|
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
|
/*!Utility function to read from the ethernet RX file.
|
|
|
|
Helper function when using file I/O.
|
|
|
/* ========================================================================= */
|
This function moves the file pointer to the current place in the packet
|
/* TX LOGIC */
|
before reading. The Ethernet device datastructure contains the file
|
/*---------------------------------------------------------------------------*/
|
descriptor and offset to use.
|
|
|
|
@param[in] eth Ethernet device datastruture.
|
|
@param[out] buf Buffer for read data.
|
|
@param[in] count Number of bytes to read.
|
|
|
|
@return Number of bytes read, or zero on end-of-file or -1 on error. */
|
|
/* -------------------------------------------------------------------------- */
|
|
static ssize_t
|
|
eth_read_rx_file (struct eth_device *eth,
|
|
void *buf,
|
|
size_t count)
|
|
{
|
|
ssize_t result;
|
|
|
|
if (eth->rx.fd <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (eth->rx.offset)
|
|
{
|
|
if (lseek (eth->rx.fd, *(eth->rx.offset), SEEK_SET) == (off_t) - 1)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
result = read (eth->rx.fd, buf, count);
|
|
|
|
if (eth->rx.offset && result >= 0)
|
|
{
|
|
*(eth->rx.offset) += result;
|
|
}
|
|
|
|
return result;
|
|
|
|
} /* eth_read_rx_file () */
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/*!Skip bytes in the RX file.
|
|
|
|
Helper function when using file I/O.
|
|
|
|
This just updates the offset pointer in the ethernet device datastructure.
|
|
|
|
@param[in] eth Ethernet device datastruture.
|
|
@param[in] count Number of bytes to skip. */
|
|
/* -------------------------------------------------------------------------- */
|
|
static void
|
|
eth_skip_rx_file (struct eth_device *eth,
|
|
off_t count)
|
|
{
|
|
eth->rx.offset += count;
|
|
|
|
} /* eth_skip_rx_file () */
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* Move to next buffer descriptor in RX file.
|
|
|
|
Helper function when using file I/O.
|
|
|
|
Skip any remaining bytes in the Rx file for this transaction.
|
|
|
|
@param[in] eth Ethernet device datastruture. */
|
|
/* -------------------------------------------------------------------------- */
|
|
static void
|
|
eth_rx_next_packet (struct eth_device *eth)
|
|
{
|
|
/* Skip any possible leftovers */
|
|
if (eth->rx.bytes_left)
|
|
{
|
|
eth_skip_rx_file (eth, eth->rx.bytes_left);
|
|
}
|
|
} /* eth_rx_next_packet () */
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/*!Emulate MIIM transaction to ethernet PHY
|
|
|
|
@param[in] eth Ethernet device datastruture. */
|
|
/* -------------------------------------------------------------------------- */
|
|
static void
|
|
eth_miim_trans (struct eth_device *eth)
|
|
{
|
|
switch (eth->regs.miicommand)
|
|
{
|
|
case ((1 << ETH_MIICOMM_WCDATA_OFFSET)):
|
|
/* Perhaps something to emulate here later, but for now do nothing */
|
|
break;
|
|
|
|
case ((1 << ETH_MIICOMM_RSTAT_OFFSET)):
|
/*
|
/*
|
* TX clock
|
printf("or1ksim: eth_miim_trans: phy %d\n",(int)
|
* Responsible for starting and finishing TX
|
((eth->regs.miiaddress >> ETH_MIIADDR_FIAD_OFFSET)&
|
|
ETH_MIIADDR_FIAD_MASK));
|
|
printf("or1ksim: eth_miim_trans: reg %d\n",(int)
|
|
((eth->regs.miiaddress >> ETH_MIIADDR_RGAD_OFFSET)&
|
|
ETH_MIIADDR_RGAD_MASK));
|
*/
|
*/
|
|
/*First check if it's the correct PHY to address */
|
|
if (((eth->regs.miiaddress >> ETH_MIIADDR_FIAD_OFFSET)&
|
|
ETH_MIIADDR_FIAD_MASK) == eth->phy_addr)
|
|
{
|
|
/* Correct PHY - now switch based on the register address in the PHY*/
|
|
switch ((eth->regs.miiaddress >> ETH_MIIADDR_RGAD_OFFSET)&
|
|
ETH_MIIADDR_RGAD_MASK)
|
|
{
|
|
case MII_BMCR:
|
|
eth->regs.miirx_data = BMCR_FULLDPLX;
|
|
break;
|
|
case MII_BMSR:
|
|
eth->regs.miirx_data = BMSR_LSTATUS | BMSR_ANEGCOMPLETE |
|
|
BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | BMSR_100FULL;
|
|
break;
|
|
case MII_PHYSID1:
|
|
eth->regs.miirx_data = 0x22; /* Micrel PHYID */
|
|
break;
|
|
case MII_PHYSID2:
|
|
eth->regs.miirx_data = 0x1613; /* Micrel PHYID */
|
|
break;
|
|
case MII_ADVERTISE:
|
|
eth->regs.miirx_data = ADVERTISE_FULL;
|
|
break;
|
|
case MII_LPA:
|
|
eth->regs.miirx_data = LPA_DUPLEX | LPA_100;
|
|
break;
|
|
case MII_EXPANSION:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_CTRL1000:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_STAT1000:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_ESTATUS:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_DCOUNTER:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_FCSCOUNTER:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_NWAYTEST:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_RERRCOUNTER:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_SREVISION:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_RESV1:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_LBRERROR:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_PHYADDR:
|
|
eth->regs.miirx_data = eth->phy_addr;
|
|
break;
|
|
case MII_RESV2:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_TPISTATUS:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_NCONFIG:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
default:
|
|
eth->regs.miirx_data = 0xffff;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
eth->regs.miirx_data = 0xffff; /* PHY doesn't exist, read all 1's */
|
|
}
|
|
|
|
break;
|
|
|
|
case ((1 << ETH_MIICOMM_SCANS_OFFSET)):
|
|
/* From MAC's datasheet:
|
|
A host initiates the Scan Status Operation by asserting the SCANSTAT
|
|
signal. The MIIM performs a continuous read operation of the PHY
|
|
Status register. The PHY is selected by the FIAD[4:0] signals. The
|
|
link status LinkFail signal is asserted/deasserted by the MIIM module
|
|
and reflects the link status bit of the PHY Status register. The
|
|
signal NVALID is used for qualifying the validity of the LinkFail
|
|
signals and the status data PRSD[15:0]. These signals are invalid
|
|
until the first scan status operation ends. During the scan status
|
|
operation, the BUSY signal is asserted until the last read is
|
|
performed (the scan status operation is stopped).
|
|
|
|
So for now - do nothing, leave link status indicator as permanently
|
|
with link.
|
|
*/
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
} /* eth_miim_trans () */
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/*!Tx clock function.
|
|
|
|
The original version had 4 states, which allowed modeling the transfer of
|
|
data one byte per cycle.
|
|
|
|
For now we use only the one state for efficiency. When we find something in
|
|
a buffer descriptor, we transmit it. We should wake up for this every 10
|
|
cycles.
|
|
|
|
We also remove numerous calculations that are not needed here.
|
|
|
|
@todo We should eventually reinstate the one byte per cycle transfer.
|
|
|
|
Responsible for starting and completing any TX actions.
|
|
|
|
@param[in] dat The Ethernet data structure, passed as a void pointer. */
|
|
/* -------------------------------------------------------------------------- */
|
static void
|
static void
|
eth_controller_tx_clock (void *dat)
|
eth_controller_tx_clock (void *dat)
|
{
|
{
|
struct eth_device *eth = dat;
|
struct eth_device *eth = dat;
|
int bAdvance = 1;
|
|
long nwritten = 0;
|
|
unsigned long read_word;
|
|
|
|
switch (eth->tx.state)
|
/* First word of BD is flags and length, second is pointer to buffer */
|
{
|
unsigned long int bd_info = eth->regs.bd_ram[eth->tx.bd_index];
|
case ETH_TXSTATE_IDLE:
|
unsigned long int bd_addr = eth->regs.bd_ram[eth->tx.bd_index + 1];
|
eth->tx.state = ETH_TXSTATE_WAIT4BD;
|
|
break;
|
|
case ETH_TXSTATE_WAIT4BD:
|
|
/* Read buffer descriptor */
|
|
eth->tx.bd = eth->regs.bd_ram[eth->tx.bd_index];
|
|
eth->tx.bd_addr = eth->regs.bd_ram[eth->tx.bd_index + 1];
|
|
|
|
if (TEST_FLAG (eth->tx.bd, ETH_TX_BD, READY))
|
/* If we have a buffer ready, get it and transmit it. */
|
|
if (TEST_FLAG (bd_info, ETH_TX_BD, READY))
|
{
|
{
|
/*****************/
|
long int packet_length;
|
/* initialize TX */
|
long int bytes_sent;
|
eth->tx.bytes_left = eth->tx.packet_length =
|
long int nwritten = 0;
|
GET_FIELD (eth->tx.bd, ETH_TX_BD, LENGTH);
|
|
eth->tx.bytes_sent = 0;
|
|
|
|
/* Initialize error status bits */
|
|
CLEAR_FLAG (eth->tx.bd, ETH_TX_BD, DEFER);
|
|
CLEAR_FLAG (eth->tx.bd, ETH_TX_BD, COLLISION);
|
|
CLEAR_FLAG (eth->tx.bd, ETH_TX_BD, RETRANSMIT);
|
|
CLEAR_FLAG (eth->tx.bd, ETH_TX_BD, UNDERRUN);
|
|
CLEAR_FLAG (eth->tx.bd, ETH_TX_BD, NO_CARRIER);
|
|
SET_FIELD (eth->tx.bd, ETH_TX_BD, RETRY, 0);
|
|
|
|
/* Find out minimum length */
|
|
if (TEST_FLAG (eth->tx.bd, ETH_TX_BD, PAD) ||
|
|
TEST_FLAG (eth->regs.moder, ETH_MODER, PAD))
|
|
eth->tx.minimum_length =
|
|
GET_FIELD (eth->regs.packetlen, ETH_PACKETLEN, MINFL);
|
|
else
|
|
eth->tx.minimum_length = eth->tx.packet_length;
|
|
|
|
/* Find out maximum length */
|
/* Get the packet length */
|
if (TEST_FLAG (eth->regs.moder, ETH_MODER, HUGEN))
|
packet_length = GET_FIELD (bd_info, ETH_TX_BD, LENGTH);
|
eth->tx.maximum_length = eth->tx.packet_length;
|
|
else
|
|
eth->tx.maximum_length =
|
|
GET_FIELD (eth->regs.packetlen, ETH_PACKETLEN, MAXFL);
|
|
|
|
/* Do we need CRC on this packet? */
|
/* Clear error status bits and retry count. */
|
if (TEST_FLAG (eth->regs.moder, ETH_MODER, CRCEN) ||
|
CLEAR_FLAG (bd_info, ETH_TX_BD, DEFER);
|
(TEST_FLAG (eth->tx.bd, ETH_TX_BD, CRC) &&
|
CLEAR_FLAG (bd_info, ETH_TX_BD, COLLISION);
|
TEST_FLAG (eth->tx.bd, ETH_TX_BD, LAST)))
|
CLEAR_FLAG (bd_info, ETH_TX_BD, RETRANSMIT);
|
eth->tx.add_crc = 1;
|
CLEAR_FLAG (bd_info, ETH_TX_BD, UNDERRUN);
|
else
|
CLEAR_FLAG (bd_info, ETH_TX_BD, NO_CARRIER);
|
eth->tx.add_crc = 0;
|
|
|
|
if (TEST_FLAG (eth->regs.moder, ETH_MODER, DLYCRCEN))
|
SET_FIELD (bd_info, ETH_TX_BD, RETRY, 0);
|
eth->tx.crc_dly = 1;
|
|
else
|
|
eth->tx.crc_dly = 0;
|
|
/* XXX - For now we skip CRC calculation */
|
|
|
|
if (eth->rtx_type == ETH_RTX_FILE)
|
/* Copy data from buffer descriptor address into our local tx_buff. */
|
|
for (bytes_sent = 0; bytes_sent < packet_length; bytes_sent +=4)
|
{
|
{
|
/* write packet length to file */
|
unsigned long int read_word =
|
nwritten =
|
eval_direct32 (bytes_sent + bd_addr, 0, 0);
|
write (eth->txfd, &(eth->tx.packet_length),
|
|
sizeof (eth->tx.packet_length));
|
eth->tx_buff[bytes_sent] = (unsigned char) (read_word >> 24);
|
|
eth->tx_buff[bytes_sent + 1] = (unsigned char) (read_word >> 16);
|
|
eth->tx_buff[bytes_sent + 2] = (unsigned char) (read_word >> 8);
|
|
eth->tx_buff[bytes_sent + 3] = (unsigned char) (read_word);
|
}
|
}
|
|
|
/************************************************/
|
/* Send packet according to interface type. */
|
/* start transmit with reading packet into FIFO */
|
|
eth->tx.state = ETH_TXSTATE_READFIFO;
|
|
}
|
|
|
|
/* stay in this state if (TXEN && !READY) */
|
|
break;
|
|
case ETH_TXSTATE_READFIFO:
|
|
//if (eth->tx.bytes_sent < eth->tx.packet_length)
|
|
while (eth->tx.bytes_sent < eth->tx.packet_length)
|
|
{
|
|
read_word =
|
|
eval_direct32 (eth->tx.bytes_sent + eth->tx.bd_addr, 0, 0);
|
|
eth->tx_buff[eth->tx.bytes_sent] =
|
|
(unsigned char) (read_word >> 24);
|
|
eth->tx_buff[eth->tx.bytes_sent + 1] =
|
|
(unsigned char) (read_word >> 16);
|
|
eth->tx_buff[eth->tx.bytes_sent + 2] =
|
|
(unsigned char) (read_word >> 8);
|
|
eth->tx_buff[eth->tx.bytes_sent + 3] = (unsigned char) (read_word);
|
|
eth->tx.bytes_sent += 4;
|
|
}
|
|
//else
|
|
// {
|
|
eth->tx.state = ETH_TXSTATE_TRANSMIT;
|
|
// }
|
|
break;
|
|
case ETH_TXSTATE_TRANSMIT:
|
|
/* send packet */
|
|
switch (eth->rtx_type)
|
switch (eth->rtx_type)
|
{
|
{
|
case ETH_RTX_FILE:
|
case ETH_RTX_FILE:
|
nwritten = write (eth->txfd, eth->tx_buff, eth->tx.packet_length);
|
/* write packet length to file */
|
|
nwritten =
|
|
write (eth->txfd, &(packet_length),
|
|
sizeof (packet_length));
|
|
/* write data to file */
|
|
nwritten = write (eth->txfd, eth->tx_buff, packet_length);
|
break;
|
break;
|
|
|
case ETH_RTX_TAP:
|
case ETH_RTX_TAP:
|
#if ETH_DEBUG
|
#if ETH_DEBUG
|
|
{
|
|
int j;
|
|
|
printf ("Writing TAP\n");
|
printf ("Writing TAP\n");
|
|
printf (" packet %d bytes:", (int) packet_length);
|
|
|
|
for (j = 0; j < packet_length; j++)
|
|
{
|
|
if (0 == (j % 16))
|
|
{
|
|
printf ("\n");
|
|
}
|
|
else if (0 == (j % 8))
|
|
{
|
|
printf (" ");
|
|
}
|
|
|
printf("packet %d bytes:",(int) eth->tx.packet_length );
|
|
int j; for (j=0;j<eth->tx.packet_length;j++)
|
|
{ if (j%16==0)printf("\n");
|
|
else if (j%8==0) printf(" ");
|
|
printf("%.2x ", eth->tx_buff[j]);
|
printf("%.2x ", eth->tx_buff[j]);
|
}
|
}
|
|
|
printf("\nend packet:\n");
|
printf("\nend packet:\n");
|
|
}
|
#endif
|
#endif
|
nwritten = write (eth->rtx_fd, eth->tx_buff, eth->tx.packet_length);
|
nwritten = write (eth->rtx_fd, eth->tx_buff, packet_length);
|
break;
|
break;
|
}
|
}
|
|
|
/* set BD status */
|
/* Set BD status. If we didn't write the whole packet, then we retry. */
|
if (nwritten == eth->tx.packet_length)
|
if (nwritten == packet_length)
|
{
|
{
|
CLEAR_FLAG (eth->tx.bd, 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);
|
|
|
eth->tx.state = ETH_TXSTATE_WAIT4BD;
|
|
}
|
}
|
else
|
else
|
{
|
{
|
/* XXX - implement retry mechanism here! */
|
/* Does this retry mechanism really work? */
|
CLEAR_FLAG (eth->tx.bd, ETH_TX_BD, READY);
|
CLEAR_FLAG (bd_info, ETH_TX_BD, READY);
|
CLEAR_FLAG (eth->tx.bd, 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);
|
|
#if ETH_DEBUG
|
eth->tx.state = ETH_TXSTATE_WAIT4BD;
|
printf ("Transmit retry request.\n");
|
|
#endif
|
}
|
}
|
|
|
eth->regs.bd_ram[eth->tx.bd_index] = eth->tx.bd;
|
/* Update the flags in the buffer descriptor */
|
|
eth->regs.bd_ram[eth->tx.bd_index] = bd_info;
|
|
|
|
/* This looks erroneous. Surely it will conflict with the retry flag */
|
SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, TXB);
|
SET_FLAG (eth->regs.int_source, ETH_INT_SOURCE, TXB);
|
|
|
/* generate OK interrupt */
|
/* Generate interrupt to indicate transfer complete, under the
|
if (TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, TXE_M) ||
|
following criteria all being met:
|
TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, TXB_M))
|
- either INT_MASK flag for Tx (OK or error) is set
|
{
|
- the bugger descriptor has its IRQ flag set
|
if (TEST_FLAG (eth->tx.bd, ETH_TX_BD, IRQ) && !eth->int_line_stat)
|
- there is no interrupt in progress.
|
|
|
|
@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) ||
|
|
TEST_FLAG (eth->regs.int_mask, ETH_INT_MASK, TXB_M)) &&
|
|
TEST_FLAG (bd_info, ETH_TX_BD, IRQ) &&
|
|
!eth->int_line_stat)
|
{
|
{
|
#if ETH_DEBUG
|
#if ETH_DEBUG
|
printf ("ETH_TXSTATE_TRANSMIT interrupt\n");
|
printf ("TRANSMIT interrupt\n");
|
#endif
|
#endif
|
report_interrupt (eth->mac_int);
|
report_interrupt (eth->mac_int);
|
eth->int_line_stat = 1;
|
eth->int_line_stat = 1;
|
}
|
}
|
|
else
|
|
{
|
|
#if ETH_DEBUG
|
|
printf ("Failed to send TRANSMIT interrupt\n");
|
|
#endif
|
}
|
}
|
|
|
/* advance to next BD */
|
/* Advance to next BD, wrapping around if appropriate. */
|
if (bAdvance)
|
if (TEST_FLAG (bd_info, ETH_TX_BD, WRAP) ||
|
{
|
|
if (TEST_FLAG (eth->tx.bd, ETH_TX_BD, WRAP) ||
|
|
eth->tx.bd_index >= ETH_BD_COUNT)
|
eth->tx.bd_index >= ETH_BD_COUNT)
|
|
{
|
eth->tx.bd_index = 0;
|
eth->tx.bd_index = 0;
|
|
}
|
else
|
else
|
|
{
|
eth->tx.bd_index += 2;
|
eth->tx.bd_index += 2;
|
}
|
}
|
|
|
break;
|
|
}
|
}
|
|
|
/* Reschedule */
|
/* Wake up again after 1 ticks (was 10, changed by Julius). */
|
SCHED_ADD (eth_controller_tx_clock, dat, 1);
|
SCHED_ADD (eth_controller_tx_clock, dat, 1);
|
}
|
|
|
} /* eth_controller_tx_clock () */
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/*!Rx clock function.
|
|
|
|
NEEDS WRITING
|
|
|
|
The original version had 4 states, which allowed modeling the transfer of
|
|
data one byte per cycle.
|
|
|
|
For now we use only the one state for efficiency. When we find something in
|
|
a buffer descriptor, we transmit it. We should wake up for this every 10
|
|
cycles.
|
|
|
|
We also remove numerous calculations that are not needed here.
|
|
|
|
@todo We should eventually reinstate the one byte per cycle transfer.
|
|
|
|
Responsible for starting and completing any TX actions.
|
|
|
|
@param[in] dat The Ethernet data structure, passed as a void pointer. */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
/* ========================================================================= */
|
/* ========================================================================= */
|
|
|
|
|
/* ========================================================================= */
|
/* ========================================================================= */
|
Line 683... |
Line 881... |
eth->rx.state = ETH_RXSTATE_IDLE;
|
eth->rx.state = ETH_RXSTATE_IDLE;
|
}
|
}
|
break;
|
break;
|
}
|
}
|
|
|
/* Reschedule */
|
/* Reschedule. Was 10 ticks when waiting (ETH_RXSTATE_RECV). Now always 1
|
|
tick. */
|
SCHED_ADD (eth_controller_rx_clock, dat, 1);
|
SCHED_ADD (eth_controller_rx_clock, dat, 1);
|
}
|
}
|
|
|
/* ========================================================================= */
|
/* ========================================================================= */
|
/* Move to next RX BD */
|
|
static void
|
|
eth_rx_next_packet (struct eth_device *eth)
|
|
{
|
|
/* Skip any possible leftovers */
|
|
if (eth->rx.bytes_left)
|
|
eth_skip_rx_file (eth, eth->rx.bytes_left);
|
|
}
|
|
|
|
/* "Skip" bytes in RX file */
|
/* ========================================================================= */
|
static void
|
|
eth_skip_rx_file (struct eth_device *eth, off_t count)
|
|
{
|
|
eth->rx.offset += count;
|
|
}
|
|
|
|
/*
|
/*
|
* Utility function to read from the ethernet RX file
|
* VAPI connection to outside
|
* This function moves the file pointer to the current place in the packet before reading
|
|
*/
|
*/
|
static ssize_t
|
static void
|
eth_read_rx_file (struct eth_device *eth, void *buf, size_t count)
|
eth_vapi_read (unsigned long id, unsigned long data, void *dat)
|
{
|
{
|
ssize_t result;
|
unsigned long which;
|
|
struct eth_device *eth = dat;
|
|
|
if (eth->rx.fd <= 0)
|
which = id - eth->base_vapi_id;
|
|
|
|
if (!eth)
|
{
|
{
|
return 0;
|
return;
|
}
|
}
|
|
|
if (eth->rx.offset)
|
switch (which)
|
if (lseek (eth->rx.fd, *(eth->rx.offset), SEEK_SET) == (off_t) - 1)
|
|
{
|
{
|
return 0;
|
case ETH_VAPI_DATA:
|
|
break;
|
|
case ETH_VAPI_CTRL:
|
|
break;
|
}
|
}
|
|
|
result = read (eth->rx.fd, buf, count);
|
|
if (eth->rx.offset && result >= 0)
|
|
*(eth->rx.offset) += result;
|
|
|
|
return result;
|
|
}
|
}
|
|
|
/* ========================================================================= */
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
/*!Reset the Ethernet.
|
/*!Reset the Ethernet.
|
|
|
Open the correct type of simulation interface to the outside world.
|
Open the correct type of simulation interface to the outside world.
|
|
|
Line 1093... |
Line 1275... |
return;
|
return;
|
case ETH_COLLCONF:
|
case ETH_COLLCONF:
|
eth->regs.collconf = value;
|
eth->regs.collconf = value;
|
return;
|
return;
|
case ETH_TX_BD_NUM:
|
case ETH_TX_BD_NUM:
|
eth_write_tx_bd_num (eth, value);
|
/* When TX_BD_NUM is written, also reset current RX BD index */
|
|
eth->regs.tx_bd_num = value & 0xFF;
|
|
eth->rx.bd_index = eth->regs.tx_bd_num << 1;
|
return;
|
return;
|
case ETH_CTRLMODER:
|
case ETH_CTRLMODER:
|
eth->regs.controlmoder = value;
|
eth->regs.controlmoder = value;
|
return;
|
return;
|
case ETH_MIIMODER:
|
case ETH_MIIMODER:
|
eth->regs.miimoder = value;
|
eth->regs.miimoder = value;
|
return;
|
return;
|
case ETH_MIICOMMAND:
|
case ETH_MIICOMMAND:
|
eth->regs.miicommand = value;
|
eth->regs.miicommand = value;
|
/* Perform MIIM transaction, if required */
|
/* Perform MIIM transaction, if required */
|
eth_miim_trans(dat);
|
eth_miim_trans (eth);
|
return;
|
return;
|
case ETH_MIIADDRESS:
|
case ETH_MIIADDRESS:
|
eth->regs.miiaddress = value;
|
eth->regs.miiaddress = value;
|
return;
|
return;
|
case ETH_MIITX_DATA:
|
case ETH_MIITX_DATA:
|
Line 1157... |
Line 1341... |
}
|
}
|
|
|
/* ========================================================================= */
|
/* ========================================================================= */
|
|
|
|
|
/*
|
|
* VAPI connection to outside
|
|
*/
|
|
static void
|
|
eth_vapi_read (unsigned long id, unsigned long data, void *dat)
|
|
{
|
|
unsigned long which;
|
|
struct eth_device *eth = dat;
|
|
|
|
which = id - eth->base_vapi_id;
|
|
|
|
if (!eth)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch (which)
|
|
{
|
|
case ETH_VAPI_DATA:
|
|
break;
|
|
case ETH_VAPI_CTRL:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* ========================================================================= */
|
/* ========================================================================= */
|
|
|
|
|
/* When TX_BD_NUM is written, also reset current RX BD index */
|
|
static void
|
|
eth_write_tx_bd_num (struct eth_device *eth, unsigned long value)
|
|
{
|
|
eth->regs.tx_bd_num = value & 0xFF;
|
|
eth->rx.bd_index = eth->regs.tx_bd_num << 1;
|
|
}
|
|
|
|
/* ========================================================================= */
|
/* ========================================================================= */
|
|
|
/*-----------------------------------------------[ Ethernet configuration ]---*/
|
/*-----------------------------------------------[ Ethernet configuration ]---*/
|
|
|
Line 1450... |
Line 1602... |
eth->phy_addr = val.int_val & ETH_MIIADDR_FIAD_MASK;
|
eth->phy_addr = val.int_val & ETH_MIIADDR_FIAD_MASK;
|
}
|
}
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Emulate MIIM transaction to ethernet PHY
|
|
|
|
@param[in] dat The config data structure */
|
|
/*---------------------------------------------------------------------------*/
|
|
static void
|
|
eth_miim_trans (void *dat)
|
|
{
|
|
struct eth_device *eth = dat;
|
|
switch (eth->regs.miicommand)
|
|
{
|
|
case ((1 << ETH_MIICOMM_WCDATA_OFFSET)):
|
|
/* Perhaps something to emulate here later, but for now do nothing */
|
|
break;
|
|
|
|
case ((1 << ETH_MIICOMM_RSTAT_OFFSET)):
|
|
/*
|
|
printf("or1ksim: eth_miim_trans: phy %d\n",(int)
|
|
((eth->regs.miiaddress >> ETH_MIIADDR_FIAD_OFFSET)&
|
|
ETH_MIIADDR_FIAD_MASK));
|
|
printf("or1ksim: eth_miim_trans: reg %d\n",(int)
|
|
((eth->regs.miiaddress >> ETH_MIIADDR_RGAD_OFFSET)&
|
|
ETH_MIIADDR_RGAD_MASK));
|
|
*/
|
|
/*First check if it's the correct PHY to address */
|
|
if (((eth->regs.miiaddress >> ETH_MIIADDR_FIAD_OFFSET)&
|
|
ETH_MIIADDR_FIAD_MASK) == eth->phy_addr)
|
|
{
|
|
/* Correct PHY - now switch based on the register address in the PHY*/
|
|
switch ((eth->regs.miiaddress >> ETH_MIIADDR_RGAD_OFFSET)&
|
|
ETH_MIIADDR_RGAD_MASK)
|
|
{
|
|
case MII_BMCR:
|
|
eth->regs.miirx_data = BMCR_FULLDPLX;
|
|
break;
|
|
case MII_BMSR:
|
|
eth->regs.miirx_data = BMSR_LSTATUS | BMSR_ANEGCOMPLETE |
|
|
BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | BMSR_100FULL;
|
|
break;
|
|
case MII_PHYSID1:
|
|
eth->regs.miirx_data = 0x22; /* Micrel PHYID */
|
|
break;
|
|
case MII_PHYSID2:
|
|
eth->regs.miirx_data = 0x1613; /* Micrel PHYID */
|
|
break;
|
|
case MII_ADVERTISE:
|
|
eth->regs.miirx_data = ADVERTISE_FULL;
|
|
break;
|
|
case MII_LPA:
|
|
eth->regs.miirx_data = LPA_DUPLEX | LPA_100;
|
|
break;
|
|
case MII_EXPANSION:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_CTRL1000:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_STAT1000:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_ESTATUS:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_DCOUNTER:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_FCSCOUNTER:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_NWAYTEST:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_RERRCOUNTER:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_SREVISION:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_RESV1:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_LBRERROR:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_PHYADDR:
|
|
eth->regs.miirx_data = eth->phy_addr;
|
|
break;
|
|
case MII_RESV2:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_TPISTATUS:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
case MII_NCONFIG:
|
|
eth->regs.miirx_data = 0;
|
|
break;
|
|
default:
|
|
eth->regs.miirx_data = 0xffff;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
eth->regs.miirx_data = 0xffff; /* PHY doesn't exist, read all 1's */
|
|
break;
|
|
|
|
case ((1 << ETH_MIICOMM_SCANS_OFFSET)):
|
|
/* From MAC's datasheet:
|
|
A host initiates the Scan Status Operation by asserting the SCANSTAT
|
|
signal. The MIIM performs a continuous read operation of the PHY
|
|
Status register. The PHY is selected by the FIAD[4:0] signals. The
|
|
link status LinkFail signal is asserted/deasserted by the MIIM module
|
|
and reflects the link status bit of the PHY Status register. The
|
|
signal NVALID is used for qualifying the validity of the LinkFail
|
|
signals and the status data PRSD[15:0]. These signals are invalid
|
|
until the first scan status operation ends. During the scan status
|
|
operation, the BUSY signal is asserted until the last read is
|
|
performed (the scan status operation is stopped).
|
|
|
|
So for now - do nothing, leave link status indicator as permanently
|
|
with link.
|
|
*/
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/*!Initialize a new Ethernet configuration
|
/*!Initialize a new Ethernet configuration
|
|
|
ALL parameters are set explicitly to default values. */
|
ALL parameters are set explicitly to default values. */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
static void *
|
static void *
|