0,0 → 1,356 |
#include "defines.h" |
#include "ETHERNET.h" |
|
static ETH_DATA *eth_data; |
|
/* Function to convert from virtual address to physical address |
See 3.4.1 in reference manual for explanation */ |
uint32_t VA_TO_PA(uint32_t ptr) { |
uint32_t ret = ptr & 0x1FFFFFFF; |
return ret; |
} |
|
void ETH_Init(ETH_DATA *data, void(*tx_callback)(void), void(*rx_callback)(void)) { |
// Save a pointer to the descriptor tables |
eth_data = data; |
eth_data->tx_callback = tx_callback; |
eth_data->rx_callback = rx_callback; |
|
// Bring the PHY reset line high to initialize the PHY |
PHY_RESET_TRIS = 0; |
PHY_RESET_LAT = 0; |
Delay_US(100); |
PHY_RESET_LAT = 1; |
|
INTDisableInterrupts(); |
|
// Initialize the I/O lines (dont actually need this) |
ETH_MDC_TRIS = 0; |
ETH_MDIO_TRIS = 1; |
ETH_TXEN_TRIS = 0; |
ETH_TXD0_TRIS = 0; |
ETH_TXD1_TRIS = 0; |
ETH_RXCLK_TRIS = 1; |
ETH_RXDV_TRIS = 1; |
ETH_RXD0_TRIS = 1; |
ETH_RXD1_TRIS = 1; |
ETH_RXERR_TRIS = 1; |
|
eth_data->TX_descriptor_index = 0; |
eth_data->RX_descriptor_index = 0; |
|
// Initialize values in the descriptor tables |
uint8_t i; |
for (i = 0; i < ETH_TX_DESCRIPTOR_COUNT; i++) { |
// Set the NPV values for each descriptor (linear list) |
eth_data->TX_ED_table.descriptor[i].NPV = 0; |
|
// Set the EOWN values for each descriptor |
eth_data->TX_ED_table.descriptor[i].EOWN = 0; |
|
// Assign a data buffer to each descriptor |
eth_data->TX_ED_table.descriptor[i].BUFFER_ADDR = VA_TO_PA((uint32_t)eth_data->TX_ED_buffer[i]); |
} |
for (i = 0; i < ETH_RX_DESCRIPTOR_COUNT; i++) { |
// Set the NPV values for each descriptor (linear list) |
eth_data->RX_ED_table.descriptor[i].NPV = 0; |
|
// Set the EOWN values for each descriptor |
eth_data->RX_ED_table.descriptor[i].EOWN = 1; |
|
// Assign a data buffer to each descriptor |
eth_data->RX_ED_table.descriptor[i].BUFFER_ADDR = VA_TO_PA((uint32_t)eth_data->RX_ED_buffer[i]); |
} |
|
// On the last descriptor, save the address to the beginning of the list |
eth_data->TX_ED_table.descriptor[ETH_TX_DESCRIPTOR_COUNT-1].NPV = 1; |
eth_data->RX_ED_table.descriptor[ETH_RX_DESCRIPTOR_COUNT-1].NPV = 1; |
|
// Set the last RX descriptor EOWN to software, thus using list configuration |
// eth_data->TX_ED_table.descriptor[ETH_TX_DESCRIPTOR_COUNT-1].EOWN = 0; |
// eth_data->RX_ED_table.descriptor[ETH_RX_DESCRIPTOR_COUNT-1].EOWN = 0; |
|
// Loop the end of the descriptor table to the beginning (ring configuration) |
eth_data->TX_ED_table.next_ED = VA_TO_PA((uint32_t)eth_data->TX_ED_table.descriptor); |
eth_data->RX_ED_table.next_ED = VA_TO_PA((uint32_t)eth_data->RX_ED_table.descriptor); |
|
// Save the head of the table to the corresponding ETH register |
ETHTXST = VA_TO_PA((uint32_t)eth_data->TX_ED_table.descriptor); |
ETHRXST = VA_TO_PA((uint32_t)eth_data->RX_ED_table.descriptor); |
|
|
// Ethernet Initialization Sequence: see section 35.4.10 in the PIC32 Family Reference Manual |
|
// Part 1. Ethernet Controller Initialization |
IEC1bits.ETHIE = 0; // Disable ethernet interrupts |
ETHCON1bits.ON = 0; // Disable the ethernet module |
ETHCON1bits.TXRTS = 0; // Stop transmit logic |
ETHCON1bits.RXEN = 0; // Stop receive logic |
ETHCON1bits.AUTOFC = 0; |
ETHCON1bits.MANFC = 0; |
while (ETHSTATbits.ETHBUSY); |
IFS1bits.ETHIF = 0; // Clear interrupt flags |
ETHIENCLR = 0xFFFF; // Clear the ETHIEN register (interrupt enable) |
|
// Part 2. MAC Init |
EMAC1CFG1bits.SOFTRESET = 1; // Put the MACMII in reset |
EMAC1CFG1bits.SOFTRESET = 0; |
// Default I/O configuration, RMII operating mode |
EMAC1SUPPbits.RESETRMII = 1; // Reset the MAC RMII module |
EMAC1MCFGbits.RESETMGMT = 1; // Reset the MII management module |
EMAC1MCFGbits.RESETMGMT = 0; |
EMAC1MCFGbits.CLKSEL = 0x8; // Set the MIIM PHY clock to SYSCLK/40 |
while(EMAC1MINDbits.MIIMBUSY); |
|
// Part 3. PHY Init |
// Contrary to the ref manual, the ETH module needs to be enabled for the MIIM to work |
|
ETHCON1bits.ON = 1; // Enable the ethernet module |
|
uint16_t value; |
// Reset the PHY chip |
ETH_PHY_Write(PHY_ADDRESS, 0x0, 0x8000); |
do { |
value = ETH_PHY_Read(PHY_ADDRESS, 0x0); |
} while (value & 0x8000 != 0); |
|
// Delay to wait for the link to be established |
// Something needs to be done about this. 5s is WAY too long to wait |
Delay_MS(5000); |
|
// Wait for auto-negotiation to finish |
do { |
value = ETH_PHY_Read(PHY_ADDRESS, 0x1F); // Acquire link status |
} while (value & 0x1000 == 0); |
|
ETHCON1bits.ON = 0; // Disable the ethernet module before changing other settings |
|
// Part 4. MAC Configuration |
EMAC1CFG1bits.RXENABLE = 1; // Enable the MAC receiving of frames |
EMAC1CFG1bits.TXPAUSE = 1; // Enable PAUSE flow control frames |
EMAC1CFG1bits.RXPAUSE = 1; // Enable processing of PAUSE control frames |
EMAC1CFG2bits.AUTOPAD = 0; // No auto-detection for VLAN padding |
EMAC1CFG2bits.VLANPAD = 0; // MAC does not perform padding of short frames |
EMAC1CFG2bits.PADENABLE = 1; // Pad all short frames |
EMAC1CFG2bits.CRCENABLE = 1; // Append a CRC to every frame |
EMAC1CFG2bits.HUGEFRM = 1; // Allow frames of any length |
EMAC1CFG2bits.LENGTHCK = 0; // Check the frame lengths to the length/type field |
if ((value & 0x14) || (value & 0x18)) { |
EMAC1CFG2bits.FULLDPLX = 1; // Operate in full-duplex mode |
EMAC1IPGT = 0x15; // Back-to-back interpacket gap @ 0.96us/9.6us |
// LED1_LAT = 1; |
} else { |
EMAC1CFG2bits.FULLDPLX = 0; // Operate in half-duplex mode |
EMAC1IPGT = 0x12; // Back-to-back interpacket gap @ 0.96us/9.6us |
// LED2_LAT = 1; |
} |
if ((value & 0x08) || (value & 0x18)) { |
EMAC1SUPPbits.SPEEDRMII = 1; // 100Mbps mode |
// LED3_LAT = 1; |
} else { |
EMAC1SUPPbits.SPEEDRMII = 0; // 10Mbps mode |
// LED4_LAT = 1; |
} |
EMAC1IPGRbits.NB2BIPKTGP1 = 0xC; // Set some other delay gap values |
EMAC1IPGRbits.NB2BIPKTGP2 = 0x12; |
EMAC1CLRTbits.CWINDOW = 0x37; // Set collision window to count of frame bytes |
EMAC1CLRTbits.RETX = 0xF; // Set number of retransmission attempts |
EMAC1MAXF = 0x7F4; // Set the maximum frame length to 2046 bits |
// Default MAC address is 00-04-A3-1A-4C-FC |
// Set MAC address to 00-18-3E-00-D7-EB |
EMAC1SA0 = 0xEBD7; |
EMAC1SA1 = 0x003E; |
EMAC1SA2 = 0x1800; |
|
// Part 5. Ethernet Controller Initialization cont. |
// Flow control is off by default! |
ETHRXFCbits.HTEN = 0; // Disable hash table filtering |
ETHRXFCbits.MPEN = 0; // Disable magic packet filtering |
ETHRXFCbits.PMMODE = 0; // Disable pattern matching |
ETHRXFCbits.CRCERREN = 0; // Disable CRC error collection filtering |
ETHRXFCbits.CRCOKEN = 0; // Disable CRC filtering |
ETHRXFCbits.RUNTERREN = 0; // Disable runt error collection filtering |
ETHRXFCbits.RUNTEN = 0; // Disable runt filtering |
ETHRXFCbits.UCEN = 1; // Enable unicast filtering |
ETHRXFCbits.NOTMEEN = 0; // Disable acceptance of packets to other destinations |
ETHRXFCbits.MCEN = 0; // Disable multicast filtering |
ETHRXFCbits.BCEN = 0; // Disable broadcast filtering |
|
ETHCON2bits.RXBUF_SZ = 0x7F; // Set RX data buffer size to 2032 bytes |
|
ETHIENbits.TXBUSEIE = 1; // Enable interrupt on transmit BVCI bus error |
ETHIENbits.RXBUSEIE = 1; // Enable interrupt on receive BVCI bus error |
ETHIENbits.RXDONEIE = 1; // Enable interrupt on packet received |
// ETHIENbits.PKTPENDIE = 1; // Enable interrupt on packet pending |
// ETHIENbits.RXACTIE = 1; |
ETHIENbits.TXDONEIE = 1; // Enable interrupt on packet sent |
ETHIENbits.TXABORTIE = 1; // Enable interrupt on packet send aborted |
|
IPC12bits.ETHIP = 1; // Set interrupt priority to 2 |
IPC12bits.ETHIS = 1; // Set intererupt sub-priority to 2 |
IEC1bits.ETHIE = 1; // Enable ethernet interrupts |
|
EMAC1SUPPbits.RESETRMII = 0; // Bring the RMII module out of reset |
ETHCON1bits.RXEN = 1; // Start receive logic |
ETHCON1bits.ON = 1; // Enable the ethernet module |
|
INTEnableInterrupts(); |
} |
|
/* Reads from the specified register on the PHY chip */ |
uint16_t ETH_PHY_Read(uint8_t address, uint8_t reg) { |
EMAC1MADR = reg | (address << 8); |
EMAC1MCMDbits.READ = 1; |
Nop();Nop();Nop(); |
while (EMAC1MINDbits.MIIMBUSY); |
EMAC1MCMDbits.READ = 0; |
return EMAC1MRDD; |
} |
|
/* Write to the specified register on the PHY chip */ |
void ETH_PHY_Write(uint8_t address, uint8_t reg, uint16_t value) { |
EMAC1MADR = reg | (address << 8); |
EMAC1MWTD = value; |
Nop();Nop();Nop(); |
while (EMAC1MINDbits.MIIMBUSY); |
} |
|
/* Queries the number of pending packets */ |
uint8_t ETH_Recv_Queue(void) { |
return ETHSTATbits.BUFCNT; |
} |
|
/* Function to read a single packet (<2014 bytes) */ |
uint8_t ETH_Read_Packet(uint8_t *buffer, uint16_t *length) { |
uint16_t i, j; |
uint16_t size; |
uint8_t descriptor_index = eth_data->RX_descriptor_index; |
|
// Look for the first descriptor where EOWN is cleared and SOP/EOP is set |
for (i = 0; i < ETH_RX_DESCRIPTOR_COUNT; i++) { |
if ((eth_data->RX_ED_table.descriptor[descriptor_index].EOWN == 0) && |
(eth_data->RX_ED_table.descriptor[descriptor_index].SOP == 1) && |
(eth_data->RX_ED_table.descriptor[descriptor_index].EOP == 1)) { |
|
// Read the packet data values into the buffer |
size = eth_data->RX_ED_table.descriptor[descriptor_index].BYTE_COUNT - 18; |
for (j = 0; j < size; j++) { |
buffer[j] = eth_data->RX_ED_buffer[descriptor_index][j+14]; |
} |
*length = size; |
|
// Reset the descriptors |
eth_data->RX_ED_table.descriptor[descriptor_index].SOP = 0; |
eth_data->RX_ED_table.descriptor[descriptor_index].EOP = 0; |
eth_data->RX_ED_table.descriptor[descriptor_index].EOWN = 1; |
|
eth_data->RX_descriptor_index = (descriptor_index == ETH_RX_DESCRIPTOR_COUNT - 1) ? 0 : descriptor_index + 1; |
|
ETHCON1bits.BUFCDEC = 1; |
|
return 0; |
|
} else { |
descriptor_index = (descriptor_index == ETH_RX_DESCRIPTOR_COUNT - 1) ? 0 : descriptor_index + 1; |
} |
} |
|
return 1; |
} |
|
/* Function to send a single packet (<2018 bytes) */ |
uint8_t ETH_Write_Packet(ETH_MAC_ADDRESS dest, ETH_MAC_ADDRESS src, uint16_t length, uint8_t *buffer) { |
uint16_t i; |
uint16_t write_index = 0; |
uint16_t read_index = 0; |
uint16_t descriptor_index = eth_data->TX_descriptor_index; |
|
// Do a quick sanity check to ensure that we have enough memory to send the message |
if (length > ETH_TX_ED_BUFFER_SIZE - 14) |
return 1; |
|
// Fill the descriptor |
eth_data->TX_ED_table.descriptor[descriptor_index].TSV.registers[0] = 0; |
eth_data->TX_ED_table.descriptor[descriptor_index].TSV.registers[1] = 0; |
eth_data->TX_ED_table.descriptor[descriptor_index].EOWN = 1; |
eth_data->TX_ED_table.descriptor[descriptor_index].SOP = 1; |
eth_data->TX_ED_table.descriptor[descriptor_index].EOP = 1; |
|
for (i = 0; i < 6; i++) { |
eth_data->TX_ED_buffer[descriptor_index][write_index] = dest.bytes[i]; |
write_index++; |
} |
for (i = 0; i < 6; i++) { |
eth_data->TX_ED_buffer[descriptor_index][write_index] = src.bytes[i]; |
write_index++; |
} |
eth_data->TX_ED_buffer[descriptor_index][write_index] = length >> 8; |
eth_data->TX_ED_buffer[descriptor_index][write_index+1] = length; |
write_index += 2; |
|
|
eth_data->TX_ED_table.descriptor[descriptor_index].BYTE_COUNT = length + 14; |
|
for (i = 0; i < length; i++) { |
eth_data->TX_ED_buffer[descriptor_index][write_index] = buffer[read_index]; |
write_index++; |
read_index++; |
} |
|
// Wait for any previous transmits to finish before sending |
while (ETHSTATbits.TXBUSY); |
ETHCON1bits.TXRTS = 1; |
while (ETHSTATbits.TXBUSY); |
|
eth_data->TX_descriptor_index = (descriptor_index == ETH_TX_DESCRIPTOR_COUNT - 1) ? 0 : descriptor_index + 1; |
|
return 0; |
} |
|
void __ISR(_ETH_VECTOR, ipl1) __ETH_Interrupt_Handler(void) { |
// uint32_t value = ETHIRQ; |
if (ETHIRQbits.TXBUSE) { |
// TX bus error, something -should- be done |
Reset_Board(BOARD_MODE_IDLE); |
ETHIRQbits.TXBUSE = 0; |
} |
if (ETHIRQbits.RXBUSE) { |
// RX bus error, something -should- be done |
Reset_Board(BOARD_MODE_IDLE); |
ETHIRQbits.RXBUSE = 0; |
} |
if (ETHIRQbits.RXDONE) { |
// Call the previously saved function |
if (eth_data->rx_callback != NULL) |
(*eth_data->rx_callback)(); |
ETHIRQbits.RXDONE = 0; |
} |
// if (ETHIRQbits.PKTPEND) { |
// |
// ETHIRQbits.PKTPEND = 0; |
// } |
if (ETHIRQbits.TXDONE) { |
// Call the previously saved function |
if (eth_data->tx_callback != NULL) |
(*eth_data->tx_callback)(); |
ETHIRQbits.TXDONE = 0; |
} |
if (ETHIRQbits.TXABORT) { |
// TX aborted, do we care? |
ETHIRQbits.TXABORT = 0; |
} |
if (ETHIRQbits.RXBUFNA) { |
// This is a serious error! |
// TODO: handle this |
Reset_Board(BOARD_MODE_IDLE); |
ETHIRQbits.RXBUFNA = 0; |
} |
if (ETHIRQbits.RXOVFLW) { |
// This is a serious error! |
// TODO: handle this |
Reset_Board(BOARD_MODE_IDLE); |
ETHIRQbits.RXOVFLW = 0; |
} |
|
IFS1bits.ETHIF = 0; |
} |