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, ð, &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, ð, &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; |
} |