Rev 268 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
#include "defines.h"#include "ETHERNET.h"static ETH_DATA *eth_data;/* Function to convert from virtual address to physical addressSee 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 tableseth_data = data;eth_data->tx_callback = tx_callback;eth_data->rx_callback = rx_callback;// Bring the PHY reset line high to initialize the PHYPHY_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 tablesuint8_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 descriptoreth_data->TX_ED_table.descriptor[i].EOWN = 0;// Assign a data buffer to each descriptoreth_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 descriptoreth_data->RX_ED_table.descriptor[i].EOWN = 1;// Assign a data buffer to each descriptoreth_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 listeth_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 registerETHTXST = 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 InitializationIEC1bits.ETHIE = 0; // Disable ethernet interruptsETHCON1bits.ON = 0; // Disable the ethernet moduleETHCON1bits.TXRTS = 0; // Stop transmit logicETHCON1bits.RXEN = 0; // Stop receive logicETHCON1bits.AUTOFC = 0;ETHCON1bits.MANFC = 0;while (ETHSTATbits.ETHBUSY);IFS1bits.ETHIF = 0; // Clear interrupt flagsETHIENCLR = 0xFFFF; // Clear the ETHIEN register (interrupt enable)// Part 2. MAC InitEMAC1CFG1bits.SOFTRESET = 1; // Put the MACMII in resetEMAC1CFG1bits.SOFTRESET = 0;// Default I/O configuration, RMII operating modeEMAC1SUPPbits.RESETRMII = 1; // Reset the MAC RMII moduleEMAC1MCFGbits.RESETMGMT = 1; // Reset the MII management moduleEMAC1MCFGbits.RESETMGMT = 0;EMAC1MCFGbits.CLKSEL = 0x8; // Set the MIIM PHY clock to SYSCLK/40while(EMAC1MINDbits.MIIMBUSY);// Part 3. PHY Init// Contrary to the ref manual, the ETH module needs to be enabled for the MIIM to workETHCON1bits.ON = 1; // Enable the ethernet moduleuint16_t value;// Reset the PHY chipETH_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 waitDelay_MS(5000);// Wait for auto-negotiation to finishdo {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 ConfigurationEMAC1CFG1bits.RXENABLE = 1; // Enable the MAC receiving of framesEMAC1CFG1bits.TXPAUSE = 1; // Enable PAUSE flow control framesEMAC1CFG1bits.RXPAUSE = 1; // Enable processing of PAUSE control framesEMAC1CFG2bits.AUTOPAD = 0; // No auto-detection for VLAN paddingEMAC1CFG2bits.VLANPAD = 0; // MAC does not perform padding of short framesEMAC1CFG2bits.PADENABLE = 1; // Pad all short framesEMAC1CFG2bits.CRCENABLE = 1; // Append a CRC to every frameEMAC1CFG2bits.HUGEFRM = 1; // Allow frames of any lengthEMAC1CFG2bits.LENGTHCK = 0; // Check the frame lengths to the length/type fieldif ((value & 0x14) || (value & 0x18)) {EMAC1CFG2bits.FULLDPLX = 1; // Operate in full-duplex modeEMAC1IPGT = 0x15; // Back-to-back interpacket gap @ 0.96us/9.6us// LED1_LAT = 1;} else {EMAC1CFG2bits.FULLDPLX = 0; // Operate in half-duplex modeEMAC1IPGT = 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 valuesEMAC1IPGRbits.NB2BIPKTGP2 = 0x12;EMAC1CLRTbits.CWINDOW = 0x37; // Set collision window to count of frame bytesEMAC1CLRTbits.RETX = 0xF; // Set number of retransmission attemptsEMAC1MAXF = 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-EBEMAC1SA0 = 0xEBD7;EMAC1SA1 = 0x003E;EMAC1SA2 = 0x1800;// Part 5. Ethernet Controller Initialization cont.// Flow control is off by default!ETHRXFCbits.HTEN = 0; // Disable hash table filteringETHRXFCbits.MPEN = 0; // Disable magic packet filteringETHRXFCbits.PMMODE = 0; // Disable pattern matchingETHRXFCbits.CRCERREN = 0; // Disable CRC error collection filteringETHRXFCbits.CRCOKEN = 0; // Disable CRC filteringETHRXFCbits.RUNTERREN = 0; // Disable runt error collection filteringETHRXFCbits.RUNTEN = 0; // Disable runt filteringETHRXFCbits.UCEN = 1; // Enable unicast filteringETHRXFCbits.NOTMEEN = 0; // Disable acceptance of packets to other destinationsETHRXFCbits.MCEN = 0; // Disable multicast filteringETHRXFCbits.BCEN = 0; // Disable broadcast filteringETHCON2bits.RXBUF_SZ = 0x7F; // Set RX data buffer size to 2032 bytesETHIENbits.TXBUSEIE = 1; // Enable interrupt on transmit BVCI bus errorETHIENbits.RXBUSEIE = 1; // Enable interrupt on receive BVCI bus errorETHIENbits.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 sentETHIENbits.TXABORTIE = 1; // Enable interrupt on packet send abortedIPC12bits.ETHIP = 1; // Set interrupt priority to 2IPC12bits.ETHIS = 1; // Set intererupt sub-priority to 2IEC1bits.ETHIE = 1; // Enable ethernet interruptsEMAC1SUPPbits.RESETRMII = 0; // Bring the RMII module out of resetETHCON1bits.RXEN = 1; // Start receive logicETHCON1bits.ON = 1; // Enable the ethernet moduleINTEnableInterrupts();}/* 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 setfor (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 buffersize = 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 descriptorseth_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 messageif (length > ETH_TX_ED_BUFFER_SIZE - 14)return 1;// Fill the descriptoreth_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 sendingwhile (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 doneReset_Board(BOARD_MODE_ETHERNET);ETHIRQbits.TXBUSE = 0;}if (ETHIRQbits.RXBUSE) {// RX bus error, something -should- be doneReset_Board(BOARD_MODE_ETHERNET);ETHIRQbits.RXBUSE = 0;}if (ETHIRQbits.RXDONE) {// Call the previously saved functionif (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 functionif (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 thisReset_Board(BOARD_MODE_ETHERNET);ETHIRQbits.RXBUFNA = 0;}if (ETHIRQbits.RXOVFLW) {// This is a serious error!// TODO: handle thisReset_Board(BOARD_MODE_ETHERNET);ETHIRQbits.RXOVFLW = 0;}IFS1bits.ETHIF = 0;}