OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /
    from Rev 695 to Rev 696
    Reverse comparison

Rev 695 → Rev 696

/trunk/or1ksim/peripheral/eth.c
0,0 → 1,417
/* ethernet.c -- Simulation of Ethernet MAC
Copyright (C) 2001 by Erez Volk, erez@opencores.org
Ivan Guzvinec, ivang@opencores.org
 
This file is part of OpenRISC 1000 Architectural Simulator.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
 
#include "abstract.h"
#include "ethernet_i.h"
#include "dma.h"
#include "sim-config.h"
#include "fields.h"
#include "crc32.h"
 
 
static struct eth_device eths[MAX_ETHERNETS];
 
/* register interface */
static void eth_write32( unsigned long addr, unsigned long value );
static unsigned long eth_read32( unsigned long addr );
/* clock */
static void eth_controller_tx_clock( struct eth_device * );
static void eth_controller_rx_clock( struct eth_device * );
/* utility functions */
static int eth_find_controller( unsigned long addr, struct eth_device **eth, unsigned long *reladdr );
 
/* ========================================================================= */
/* TX LOGIC */
/*---------------------------------------------------------------------------*/
 
/*
* TX clock
* Responsible for starting and finishing TX
*/
void eth_controller_tx_clock( struct eth_device *eth )
{
unsigned long breakpoint = 0;
 
switch (eth->tx.state) {
case ETH_TXSTATE_IDLE:
if ( TEST_FLAG( eth->regs.moder, ETH_MODER, TXEN ) || (eth->txfd <= 0) ) {
 
/* wait for TxBuffer to be ready */
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 ) ) {
/*****************/
/* initialize TX */
eth->tx.bytes_left = eth->tx.packet_length = 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 */
if ( TEST_FLAG( eth->regs.moder, ETH_MODER, HUGEN ) )
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? */
if ( TEST_FLAG( eth->regs.moder, ETH_MODER, CRCEN ) ||
(TEST_FLAG( eth->tx.bd, ETH_TX_BD, CRC) &&
TEST_FLAG( eth->tx.bd, ETH_TX_BD, LAST)) )
eth->tx.add_crc = 1;
else
eth->tx.add_crc = 0;
eth_initialize_tx_crc( eth );
debug( 3, "Ethernet: Starting TX of %u bytes (min. %u, max. %u)\n", eth->tx.packet_length,
eth->tx.minimum_length, eth->tx.maximum_length );
/* Write "header" to file */
length_with_crc = eth->tx.add_crc ? (eth->tx.packet_length + 4) : eth->tx.packet_length;
write( eth->txfd, &length_with_crc, sizeof(length_with_crc) );
/************************************************/
/* start transmit with reading packet into FIFO */
eth->tx.state = ETH_TXSTATE_READFIFO;
}
else if ( !TEST_FLAG( eth->regs.moder, ETH_MODER, TXEN ) ) {
/* stop TX logic */
eth->tx.state = ETH_TXSTATE_IDLE;
}
 
/* stay in this state if (TXEN && !READY) */
break;
case ETH_TXSTATE_READFIFO:
if ( eth->tx.bytes_sent < eth->tx.packet_length ) {
eth->tx_buff[eth->tx.bytes_sent\4] = eval_mem32(eth->tx.bytes_sent + eth->tx.bd_addr, &breakpoint)
eth->tx.bytes_sent += 4;
}
else {
eth->tx.state = ETH_TXSTATE_TRANSMIT;
}
break;
case ETH_TXSTATE_TRANSMIT:
write( eth->txfd, eth->tx_buff, eth->tx.packet_length );
eth->tx.state = ETH_TXSTATE_IDLE;
break;
}
}
/* ========================================================================= */
 
 
/* ========================================================================= */
/* RX LOGIC */
/*---------------------------------------------------------------------------*/
 
/*
* RX clock
* Responsible for starting and finishing RX
*/
void eth_controller_rx_clock( struct eth_device *eth )
{
switch (eth->rx.state) {
case ETH_RXSTATE_IDLE:
break;
case ETH_RXSTATE_WAIT4BD:
break;
case ETH_RXSTATE_RECV:
break;
case ETH_RXSTATE_WRITEFIFO:
break;
}
}
/* ========================================================================= */
 
 
/*
Reset. Initializes all registers to default and places devices in
memory address space.
*/
void eth_reset()
{
static int first_time = 1;
unsigned i;
if (!config.nethernets)
return;
if ( first_time ) {
memset( eths, 0, sizeof(eths) );
first_time = 0;
}
for ( i = 0; i < MAX_ETHERNETS; ++ i ) {
struct eth_device *eth = &(eths[i]);
eth->eth_number = i;
eth->baseaddr = config.ethernets[i].baseaddr;
if ( eth->baseaddr != 0 ) {
/* Mark which DMA controller and channels */
eth->dma = config.ethernets[i].dma;
eth->tx_channel = config.ethernets[i].tx_channel;
eth->rx_channel = config.ethernets[i].rx_channel;
/* (Re-)open TX/RX files */
eth->rxfile = config.ethernets[i].rxfile;
eth->txfile = config.ethernets[i].txfile;
eth_close_files( eth );
if ( (eth->rxfd = open( eth->rxfile, O_RDONLY )) < 0 )
fprintf( stderr, "Cannot open Ethernet RX file \"%s\"\n", eth->rxfile );
if ( (eth->txfd = open( eth->txfile,
O_RDWR | O_CREAT | O_APPEND | O_SYNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH )) < 0 )
fprintf( stderr, "Cannot open Ethernet TX file \"%s\"\n", eth->txfile );
eth->loopback_offset = lseek( eth->txfd, 0, SEEK_END );
/* Set registers to default values */
memset( &(eth->regs), 0, sizeof(eth->regs) );
eth->regs.moder = 0x0000A000;
eth->regs.ipgt = 0x00000012;
eth->regs.ipgr1 = 0x0000000C;
eth->regs.ipgr2 = 0x00000012;
eth->regs.packetlen = 0x003C0600;
eth->regs.collconf = 0x000F003F;
eth->regs.miimoder = 0x00000064;
eth->regs.tx_tb_num = 0x00000080;
/* Initialize TX/RX status */
memset( &(eth->tx), 0, sizeof(eth->tx) );
memset( &(eth->rx), 0, sizeof(eth->rx) );
eth->rx.bd_index = eth->regs.tx_bd_num;
/* Register memory range */
register_memoryarea( eth->baseaddr, ETH_ADDR_SPACE, 4, eth_read32, eth_write32 );
}
}
}
/* ========================================================================= */
 
 
/*
Print register values on stdout
*/
void eth_status( void )
{
unsigned i;
for ( i = 0; i < MAX_ETHERNETS; ++ i ) {
struct eth_device *eth = &(eths[i]);
if ( eth->baseaddr == 0 )
continue;
printf( "\nEthernet MAC %u at 0x%08X:\n", i, eth->baseaddr );
printf( "MODER : 0x%08lX\n", eth->regs.moder );
printf( "INT_SOURCE : 0x%08lX\n", eth->regs.int_source );
printf( "INT_MASK : 0x%08lX\n", eth->regs.int_mask );
printf( "IPGT : 0x%08lX\n", eth->regs.ipgt );
printf( "IPGR1 : 0x%08lX\n", eth->regs.ipgr1 );
printf( "IPGR2 : 0x%08lX\n", eth->regs.ipgr2 );
printf( "PACKETLEN : 0x%08lX\n", eth->regs.packetlen );
printf( "COLLCONF : 0x%08lX\n", eth->regs.collconf );
printf( "TX_BD_NUM : 0x%08lX\n", eth->regs.tx_bd_num );
printf( "CTRLMODER : 0x%08lX\n", eth->regs.controlmoder );
printf( "MIIMODER : 0x%08lX\n", eth->regs.miimoder );
printf( "MIICOMMAND : 0x%08lX\n", eth->regs.miicommand );
printf( "MIIADDRESS : 0x%08lX\n", eth->regs.miiaddress );
printf( "MIITX_DATA : 0x%08lX\n", eth->regs.miitx_data );
printf( "MIIRX_DATA : 0x%08lX\n", eth->regs.miirx_data );
printf( "MIISTATUS : 0x%08lX\n", eth->regs.miistatus );
printf( "MAC Address : %02X:%02X:%02X:%02X:%02X:%02X\n",
eth->mac_address[0], eth->mac_address[1], eth->mac_address[2],
eth->mac_address[3], eth->mac_address[4], eth->mac_address[5] );
}
}
/* ========================================================================= */
 
 
/*
Simulation hook. Must be called every clock cycle to simulate Ethernet MAC.
*/
void eth_clock()
{
unsigned i;
for ( i = 0; i < config.nethernets; ++ i ) {
eth_controller_tx_clock( &(eths[i]) );
eth_controller_rx_clock( &(eths[i]) );
}
}
/* ========================================================================= */
 
 
/*
Read a register
*/
unsigned long eth_read32( unsigned long addr )
{
struct eth_device *eth;
if ( !eth_find_controller( addr, &eth, &addr ) ) {
printf( "eth_read32( 0x%08lX ): Not in registered range(s)\n", addr );
return 0;
}
 
switch( addr ) {
case ETH_MODER: return eth->regs.moder;
case ETH_INT_SOURCE: return eth->regs.int_source;
case ETH_INT_MASK: return eth->regs.int_mask;
case ETH_IPGT: return eth->regs.ipgt;
case ETH_IPGR1: return eth->regs.ipgr1;
case ETH_IPGR2: return eth->regs.ipgr2;
case ETH_PACKETLEN: return eth->regs.packetlen;
case ETH_COLLCONF: return eth->regs.collconf;
case ETH_TX_BD_NUM: return eth->regs.tx_bd_num;
case ETH_CTRLMODER: return eth->regs.controlmoder;
case ETH_MIIMODER: return eth->regs.miimoder;
case ETH_MIICOMMAND: return eth->regs.miicommand;
case ETH_MIIADDRESS: return eth->regs.miiaddress;
case ETH_MIITX_DATA: return eth->regs.miitx_data;
case ETH_MIIRX_DATA: return eth->regs.miirx_data;
case ETH_MIISTATUS: return eth->regs.miistatus;
case ETH_MAC_ADDR0: return (((unsigned long)eth->mac_address[3]) << 24) |
(((unsigned long)eth->mac_address[2]) << 16) |
(((unsigned long)eth->mac_address[1]) << 8) |
(unsigned long)eth->mac_address[0];
case ETH_MAC_ADDR1: return (((unsigned long)eth->mac_address[5]) << 8) |
(unsigned long)eth->mac_address[4];
case ETH_DMA_RX_TX: return eth_rx( eth );
}
 
if ( (addr >= ETH_BD_BASE) && (addr < ETH_BD_BASE + ETH_BD_SPACE) )
return eth->regs.bd_ram[(addr - ETH_BD_BASE) / 4];
printf( "eth_read32( 0x%08lX ): Illegal address\n", addr + eth->baseaddr );
cont_run = 0;
return 0;
}
/* ========================================================================= */
 
 
/*
Write a register
*/
void eth_write32( unsigned long addr, unsigned long value )
{
struct eth_device *eth;
if ( !eth_find_controller( addr, &eth, &addr ) ) {
printf( "eth_write32( 0x%08lX ): Not in registered range(s)\n", addr );
return;
}
switch( addr ) {
case ETH_MODER: eth->regs.moder = value; return;
case ETH_INT_SOURCE: eth->regs.int_source = value; return;
case ETH_INT_MASK: eth->regs.int_mask = value; return;
case ETH_IPGT: eth->regs.ipgt = value; return;
case ETH_IPGR1: eth->regs.ipgr1 = value; return;
case ETH_IPGR2: eth->regs.ipgr2 = value; return;
case ETH_PACKETLEN: eth->regs.packetlen = value; return;
case ETH_COLLCONF: eth->regs.collconf = value; return;
case ETH_TX_BD_NUM: eth_write_tx_bd_num( eth, value ); return;
case ETH_CTRLMODER: eth->regs.controlmoder = value; return;
case ETH_MIIMODER: eth->regs.miimoder = value; return;
case ETH_MIICOMMAND: eth->regs.miicommand = value; return;
case ETH_MIIADDRESS: eth->regs.miiaddress = value; return;
case ETH_MIITX_DATA: eth->regs.miitx_data = value; return;
case ETH_MIIRX_DATA: eth->regs.miirx_data = value; return;
case ETH_MIISTATUS: eth->regs.miistatus = value; return;
case ETH_MAC_ADDR0:
eth->mac_address[0] = value & 0xFF;
eth->mac_address[1] = (value >> 8) & 0xFF;
eth->mac_address[2] = (value >> 16) & 0xFF;
eth->mac_address[3] = (value >> 24) & 0xFF;
return;
case ETH_MAC_ADDR1:
eth->mac_address[4] = value & 0xFF;
eth->mac_address[5] = (value >> 8) & 0xFF;
return;
case ETH_DMA_RX_TX: eth_tx( eth, value ); return;
}
 
if ( (addr >= ETH_BD_BASE) && (addr < ETH_BD_BASE + ETH_BD_SPACE) ) {
eth->regs.bd_ram[(addr - ETH_BD_BASE) / 4] = value;
return;
}
printf( "eth_write32( 0x%08lX ): Illegal address\n", addr + eth->baseaddr );
cont_run = 0;
return;
}
/* ========================================================================= */
 
 
/*
Convert a memory address to a oontroller struct and relative address.
Return nonzero on success
*/
int eth_find_controller( unsigned long addr, struct eth_device **eth, unsigned long *reladdr )
{
unsigned i;
*eth = NULL;
for ( i = 0; i < MAX_ETHERNETS && *eth == NULL; ++ i ) {
if ( (addr >= eths[i].baseaddr) && (addr < eths[i].baseaddr + ETH_ADDR_SPACE) )
*eth = &(eths[i]);
}
/* verify we found a controller */
if ( *eth == NULL )
return 0;
/* Verify legal address */
if ( (addr - (*eth)->baseaddr) % 4 != 0 )
return 0;
*reladdr = addr - (*eth)->baseaddr;
return 1;
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.