This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C129DNCPDT: Configuration of MCD and MDIO lines to access external transceiver register values

Part Number: TM4C129DNCPDT
Other Parts Discussed in Thread: TLK10031,

Hello,

I am trying to read and write registers of TLK10031 transceiver using TM4C129DNCPDT microcontroller's MDC and MDIO pins(PB2 and PB3).

So to use these GPIO pins(PB2 & PB3) in MDC and MDIO functionality, what all the configurations I need to do?

Like any specific API's are there to enable MDC and MDIO functionality?

Currently I have configured these GPIO pins as output pins only.

And one more question is how to generate exact 1 micro second delay using this microcontroller. As I know SysCtlDelay() API only takes clock cycles as input. So I have tried to generate 1 micro second delay using below method. Please let me know whether it is correct or I need to use any other method.

SysCtlDelay((clock_frequency/3) * 0.000001); //clock_freuency is our system clock i.e. 40MHz.

Thank you in advance.

  • Hello Balaji,

    The MDIO / MDC pins on the device are for Ethernet functionality and specifically to interface with an external Ethernet PHY. I don't believe it is possible to use them with the TLK10031 and looking at the datasheet I don't believe any TM4C MCU is equipped to interface with such a device.

    An excerpt from the D/S regarding that functionality:

    The Ethernet MAC has the ability to program an external PHY through the external EN0MDIO and EN0MDC signals. The EN0MDC signal is a 2.5 MHz clock that is sourced from System Clock (SYSCLK) and then divided down to the required frequency by programming the CR field in the Ethernet MAC MII Address (EMACMIIADDR) register. The available addresses for the PHY are 0x01 to 1F.

    And one more question is how to generate exact 1 micro second delay using this microcontroller. As I know SysCtlDelay() API only takes clock cycles as input. So I have tried to generate 1 micro second delay using below method. Please let me know whether it is correct or I need to use any other method.

    That method will only be precise if no interrupts occur. An interrupt could make the delay longer. See the API comments for full understanding of how it functions:

    //! This function provides a means of generating a delay by executing a simple
    //! 3 instruction cycle loop a given number of times.  It is written in
    //! assembly to keep the loop instruction count consistent across tool chains.
    //!
    //! It is important to note that this function does NOT provide an accurate
    //! timing mechanism.  Although the delay loop is 3 instruction cycles long,
    //! the execution time of the loop will vary dramatically depending upon the
    //! application's interrupt environment (the loop will be interrupted unless
    //! run with interrupts disabled and this is generally an unwise thing to do)
    //! and also the current system clock rate and flash timings (wait states and
    //! the operation of the prefetch buffer affect the timing).
    //!
    //! For better accuracy, the ROM version of this function may be used.  This
    //! version will not suffer from flash- and prefect buffer-related timing
    //! variability but will still be delayed by interrupt service routines.
    //!
    //! For best accuracy, a system timer should be used with code either polling
    //! for a particular timer value being exceeded or processing the timer
    //! interrupt to determine when a particular time period has elapsed.

    Best Regards,

    Ralph Jacobi

  • Hello Balaji,

    To add a bit more detail here, the TLK10031 states the following: It can be used as a XAUI to 10GBASE-KR transceiver, as a general-purpose 8b/10b multi-rate 4:1, 2:1, or 1:1 serializer/deserializer, or can be used in 1G-KX mode.

    The TM4C129x family supports 10/100 Mbps Ethernet in s full-duplex and half-duplex (CSMA/CD) operation. So I don't believe that it is possible to interface this Ethernet interface with the TLK10031 based on what the TLK10031 describes in its specifications.

    Best Regards,

    Ralph Jacobi

  • Thank you for your response Ralph Jacobi,

    As per the datasheet it's true that TM4C129x supports 10/100 Mbps. But we are using MDC/MDIO pins for interfacing ethernet PHY. We Just wants to use them to access TLK10031 registers.

    We have connected MDC and MDIO lines of TM4C129DNCPDT to MDC and MDIO lines of TLK10031 transceiver respectively.

    Does MDC/MDIO lines functionality changes with respect different microcontrollers?

    As I know MDC/MDIO pins functionality is same with respect to any microcontroller. If so then why can't we use these lines to access(read/write) TLK10031 registers. 

    For example if a microcontroller has I2C pins means, we can interface any I2C sensor to that microcontroller right? In the same way why can't we interface TM4C129DNCPDT with TLK10031 using MDC and MDIO pins.

    Thank you.

  • Hi Balaji,

    So the intention is just to use those lines to check on the registers and not to drive the whole TLK10031 from the TM4C then?

    Please understand that we have never considered in the past about how interfacing with the TLK10031 would work since it is not a typical device to use with our MCUs so these questions are a bit of new territory for us. If the idea is just to use TM4C in that limited fashion then it is a bit different since when I first looked at the device it simply seemed overspec'd for our capabilities.

    To use MDC/MDIO you'd use the following API's to enable them:

            MAP_GPIOPinConfigure(GPIO_PB2_EN0MDC);

            MAP_GPIOPinConfigure(GPIO_PB3_EN0MDIO);

    We don't really have examples on how to use them to read registers but I can loop in our Ethernet expert as well for any added comments here.

    Best Regards,

    Ralph Jacobi

  • Hi Balaji,

      Other than calling the MAP_GPIOPinConfigure to configure PB2/PB3 for MDC/MDIO pins, I supposed you have also configured the pins using the API GPIOPinTypeEthernetMII for these two pins.

  • Thank you Ralph Jacobi for your help,

    Yes our intention is just to read TLK registers. We have tried to implementing but still not able to read/write.

    We have tried using MII EMAC API but we are not able set proper clock at MDC pin as we don't have any reference code for setting MDC clock and to read/write using MDIO pin.

    We have found a source code for read/write operation where they are working with only GPIO read and write(they have not used any specific method related MII/EMAC). Directly using GPIO registers they are able to read/write.

    So we have tried using only GPIO read and write API's (as per the code we found in web), but still not able to read/write.

    I am attaching the code i have written and the reference source document which i have found from web. Please check it and tell me where we are doing wrong.

    I am having doubt in delay generation, are we using proper delay for MDC clocking.

    In source code they are generating delay using two methods as below

    #define delay() __nop(); __nop(); __nop();
    and
    static void delay (void) {;};

    but we are using SysCtlDelay(1); //system clock is 120MHz. 

    Please check the code and tell where we are doing or is our method is only wrong?

    Thank you.

    tm4c_mdc_mdio.c
    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_memmap.h"
    #include "driverlib/debug.h"
    #include "driverlib/gpio.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/timer.h"
    #include "driverlib/interrupt.h"
    #include "inc/tm4c1294ncpdt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    
    
    #define MDC GPIO_PIN_2
    #define MDIO GPIO_PIN_3
    
    uint32_t rec_data = 0x0000;
    uint32_t clck=0;
    
    
    void delay_us(int delay){
        MAP_SysCtlDelay(delay * ((clck/3)/1000000));
    }
    
    
    void turnaround_MDIO (void) {
    
       /* Turnaround MDO is tristated. */
       GPIOPinTypeGPIOInput(GPIO_PORTB_BASE,MDIO);
       //GPIOPadConfigSet(GPIO_PORTB_BASE,MDIO,GPIO_STRENGTH_4MA,GPIO_PIN_TYPE_STD_WPU);
       GPIOPinWrite(GPIO_PORTB_BASE, MDC,MDC);
       MAP_SysCtlDelay(1);
       GPIOPinWrite(GPIO_PORTB_BASE, MDC,0x00);
       MAP_SysCtlDelay(1);
    }
    
    void output_MDIO(uint32_t val, uint32_t n)
    {
    	   for (val <<= (32 - n); n; val <<= 1, n--) {
    	      if (val & 0x80000000) {
    	    	  GPIOPinWrite(GPIO_PORTB_BASE, MDIO, MDIO);
    	      }
    	      else {
    	    	  GPIOPinWrite(GPIO_PORTB_BASE, MDIO, 0x00);
    	      }
    	      MAP_SysCtlDelay(1);
    	      GPIOPinWrite(GPIO_PORTB_BASE, MDC,MDC);
    	      MAP_SysCtlDelay(1);
    	      GPIOPinWrite(GPIO_PORTB_BASE, MDC,0x00);
    	   }
    }
    
    uint32_t input_MDIO (void) {
    
       /* Input a value from the MII PHY management interface. */
       uint32_t i,val = 0;
       GPIOPinTypeGPIOInput(GPIO_PORTB_BASE,MDIO);
       
       for (i = 0; i < 16; i++)
       {
          val <<= 1;
          GPIOPinWrite(GPIO_PORTB_BASE, MDC,MDC);
          MAP_SysCtlDelay(1);
          GPIOPinWrite(GPIO_PORTB_BASE, MDC,0x00);
          if(GPIOPinRead(GPIO_PORTB_BASE, MDIO) & MDIO)
          {
             val |= 1;
          }
       }
       return (val);
    }
    
    uint32_t mdio_read(int PhyReg) {
       uint32_t val;
    
       /* Configure MDIO pin as output pin */
       GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE,MDIO);
    
       /* 32 consecutive ones on MDO to establish sync */
       output_MDIO(0xFFFFFFFF, 32);
    
       /* start code (01), address command (10) */
       output_MDIO(0x06, 4);
    
    
       /* write PHY address */
       output_MDIO(0x1E, 5);
    
    
       /* write register address (1,0)*/
       output_MDIO (PhyReg, 5);
    
       /* turnaround MDO is tristated */
       turnaround_MDIO ();
    
       /* read the data value */
       val = input_MDIO ();
    
       /* turnaround MDIO is tristated */
       turnaround_MDIO ();
    
       return (val);
    }
    
    void mdio_write(int PhyReg, int Value) {
    
      /* Configuring MDC on P2.8 and MDIO on P2.9 */
    	GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE,MDIO);
    
      /* 32 consecutive ones on MDO to establish sync */
      output_MDIO (0xFFFFFFFF, 32);
    
      /* start code (01), write command (01) */
      output_MDIO (0x05, 4);
    
      /* write PHY address */
      output_MDIO (0x1E, 5);
    
      /* write the PHY register to write */
      output_MDIO (PhyReg, 5);
    
      /* turnaround MDIO (1,0)*/
      output_MDIO (0x02, 2);
    
      /* write the data value */
      output_MDIO (Value, 16);
    
      /* turnaround MDO is tristated */
      turnaround_MDIO ();
    }
    
    
    
    int main(void)
    {
        uint32_t val1;
        clck = SysCtlClockFreqSet(SYSCTL_OSC_INT | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_320,120000000);
    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_EMAC0);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOM);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOH);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK);
        GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, MDC | MDIO);
        GPIOPinTypeGPIOOutput(GPIO_PORTM_BASE, GPIO_PIN_5 | GPIO_PIN_6); //MUX_MDIO_SEL0 & SEL1
        GPIOPinTypeGPIOOutput(GPIO_PORTH_BASE, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
        GPIOPinTypeGPIOOutput(GPIO_PORTK_BASE, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
    
    //    GPIOPinTypeGPIOOutput(GPIO_PORTH_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
       // GPIOPinTypeEthernetMII(GPIO_PORTB_BASE, GPIO_PIN_2 | GPIO_PIN_3);
    
    //    GPIOPinConfigure(GPIO_PB2_EN0MDC);
    //    GPIOPinConfigure(GPIO_PB3_EN0MDIO);
    
       // GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0|GPIO_PIN_1,0x03); // LINK_A_MODE_SEl & LINK_A_ST for clause 22
        GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_2|GPIO_PIN_3,0x0C); // PD_LINK_A & PRBSEN_LINK_A
        GPIOPinWrite(GPIO_PORTK_BASE, GPIO_PIN_0|GPIO_PIN_1,0x00); // LINK_B_MODE_SEl & LINK_B_ST fpr clause 45
        GPIOPinWrite(GPIO_PORTK_BASE, GPIO_PIN_2|GPIO_PIN_3,0x0C); // PD_LINK_B & PRBSEN_LINK_B
    
        GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_1 | GPIO_PIN_2, 0x00);
        GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_5 | GPIO_PIN_6, 0x00); // TLK_LINK_A select
    
        GPIOPinWrite(GPIO_PORTB_BASE, MDIO,MDIO);
    
        delay_us(1000);
    
        mdio_write(0x0000,0x8000);
        delay_us(10);
    
        val1 = mdio_read(0x0000);
    
        GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE,MDIO);
    
        while(1)
        {
            mdio_write(0x0001,0x0B00);
            delay_us(10);
        	   val1 = mdio_read(0x0001);
        	   delay_us(10);
        }
    
    
    }
    
    
    
    referance_code.c
    #include "mdio.h"
    #include "emac.h"
    #include <LPC17xx.h>                    /* LPC17xx definitions               */
    
    static void delay (void) {;};
    
    
    /*--------------------------- output_MDIO -----------------------------------*/
    
    static void output_MDIO (U32 val, U32 n) {
    
       /* Output a value to the MII PHY management interface. */
       for (val <<= (32 - n); n; val <<= 1, n--) {
          if (val & 0x80000000) {
             GPIO2->FIOSET = MDIO;
          }
          else {
             GPIO2->FIOCLR = MDIO;
          }
          delay ();
          GPIO2->FIOSET = MDC;
          delay ();
          GPIO2->FIOCLR = MDC;
       }
    }
    
    /*--------------------------- turnaround_MDIO -------------------------------*/
    
    static void turnaround_MDIO (void) {
    
       /* Turnaround MDO is tristated. */
       GPIO2->FIODIR &= ~MDIO;
       GPIO2->FIOSET  = MDC;
       delay ();
       GPIO2->FIOCLR  = MDC;
       delay ();
    }
    
    /*--------------------------- input_MDIO ------------------------------------*/
    
    static U32 input_MDIO (void) {
    
       /* Input a value from the MII PHY management interface. */
       U32 i,val = 0;
    
       for (i = 0; i < 16; i++) {
          val <<= 1;
          GPIO2->FIOSET = MDC;
          delay ();
          GPIO2->FIOCLR = MDC;
          if (GPIO2->FIOPIN & MDIO) {
             val |= 1;
          }
       }
       return (val);
    }
    
    
    U32 mdio_read(int PhyReg) {
       U32 val;
    
       /* Configuring MDC on P2.8 and MDIO on P2.9 */
       GPIO2->FIODIR |= MDIO;
    
       /* 32 consecutive ones on MDO to establish sync */
       output_MDIO (0xFFFFFFFF, 32);
    
       /* start code (01), read command (10) */
       output_MDIO (0x06, 4);
    
       /* write PHY address */
       output_MDIO (DP83848C_DEF_ADR >> 8, 5);
    
       /* write the PHY register to write */
       output_MDIO (PhyReg, 5);
    
       /* turnaround MDO is tristated */
       turnaround_MDIO ();
    
       /* read the data value */
       val = input_MDIO ();
    
       /* turnaround MDIO is tristated */
       turnaround_MDIO ();
    
       return (val);
    }
    
    
    void mdio_write(int PhyReg, int Value) {
    
      /* Configuring MDC on P2.8 and MDIO on P2.9 */
      GPIO2->FIODIR |= MDIO;
    
      /* 32 consecutive ones on MDO to establish sync */
      output_MDIO (0xFFFFFFFF, 32);
    
      /* start code (01), write command (01) */
      output_MDIO (0x05, 4);
    
      /* write PHY address */
      output_MDIO (DP83848C_DEF_ADR >> 8, 5);
    
      /* write the PHY register to write */
      output_MDIO (PhyReg, 5);
    
      /* turnaround MDIO (1,0)*/
      output_MDIO (0x02, 2);
      
      /* write the data value */
      output_MDIO (Value, 16);
    
      /* turnaround MDO is tristated */
      turnaround_MDIO ();
    }
    
    mdc_mdio_programming.pdf

  • Thank you Charles Tsai for your response,

    I have updated the current status of our implementation and problem we are facing. Please help us with resolving the issue. 

    Thank you.

  • but we are using SysCtlDelay(1); //system clock is 120MHz. 

    Hi Balaji,

      I'm not that familiar with MDC protocol but reading MDC description in Wikipedia, it is mentioned that the max MDC clock is 2.5Mhz. Perhaps you are running too fast. Please double check.  Please note that a call to SysCtlDelay(1) is equal to 3 cycles, not one cycle. The best way to debug this would be to use a scope or logic analyzer to look at your MDC/MDIO waveforms against the protocol. Check if you are meeting the timings and protocol.  

  • Thank You Charles Tsai for your suggestions.

    We have tried in all the possible ways that we know. Still not able access the PHY registers. 

    Is there any example/demo code for access external PHY(any external PHY) using EN0MDC and EN0MDIO line?

    Thank you.

  • Hi,

      Sorry, we don't have any example code to access external PHY. You are trying to bit-bang the MDIO/MDC signals, correct?  If you believe your waveform is proper and you still cannot access the external PHY then I will suggest you open a new thread with the Interface Forum (specifying the part number as TLK10031) so the experts there can provide guidance. If your waveform is not correct then please show your waveform here. Please elaborate what is not working? Is it during writes or during reads? What is written to PHY and what are read back?

  • Hi Charles Tsai,

    Yes we are trying bit-bang method on MDC/MDIO signals.

    Current progress in our project is we are able read few registers from TLK10031 and few we are not able read. So I have opened new thread in Interface forum. I think I will get further guidance from Interface forum. Thank you for your help.