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

Subversion Repositories or1k

[/] [or1k/] [tags/] [nog_patch_33/] [or1ksim/] [peripheral/] [eth.c] - Rev 696

Go to most recent revision | Compare with Previous | Blame | View Log

/* 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;
}
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

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