Subversion Repositories Code-Repo

Rev

Blame | Last modification | View Log | RSS feed

#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
    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

    EMAC1SUPPbits.RESETRMII = 0;    // Bring the RMII module out of reset
    ETHCON1bits.ON = 1;             // Enable the ethernet module

    ETHCON1bits.RXEN = 1;       // Start receive logic

    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 1
    IPC12bits.ETHIS = 1;        // Set intererupt sub-priority to 1
    IEC1bits.ETHIE = 1;         // Enable ethernet interrupts

    INTEnableInterrupts();
}

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

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);
}

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)) {

            size = eth_data->RX_ED_table.descriptor[descriptor_index].BYTE_COUNT - 18;
            *length = size;
            for (j = 0; j < size - 18; j++) {
                buffer[j] = eth_data->RX_ED_buffer[descriptor_index][j+14];
            }
            
            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) {

        ETHIRQbits.TXBUSE = 0;
    }
    if (ETHIRQbits.RXBUSE) {

        ETHIRQbits.RXBUSE = 0;
    }
//    if (ETHIRQbits.RXDONE) {
//        ETHIRQbits.RXDONE = 0;
//    }
    if (ETHIRQbits.PKTPEND) {
        if (eth_data->rx_callback != NULL)
            (*eth_data->rx_callback)();
        ETHIRQbits.PKTPEND = 0;
    }
    if (ETHIRQbits.TXDONE) {
        if (eth_data->tx_callback != NULL)
            (*eth_data->tx_callback)();
        ETHIRQbits.TXDONE = 0;
    }
    if (ETHIRQbits.TXABORT) {

        ETHIRQbits.TXABORT = 0;
    }
    if (ETHIRQbits.RXBUFNA) {
        // This is a serious error!

        ETHIRQbits.RXBUFNA = 0;
    }
    if (ETHIRQbits.RXOVFLW) {
        // This is a serious error!

        ETHIRQbits.RXOVFLW = 0;
    }

    IFS1bits.ETHIF = 0;
}