add ethernet to a micromod format teensy

I'd like to chime in here as I'm working on gettin ENET2 to work on a MicroMod. I wrote a driver for QNEthernet and FNET that uses ENET2 and I can get some packets in (and out?) but it's far from reliable. This is with a LAN8720A PHY. Clock is generated on ENET2_REF_CLK2 and I see a 50 MHz signal on my scope. Even without any load the signal is very "slow" - so bad in integrity that I think the PHY continuously restarts or looses sync. Also Vpp is around 2V only (because of the clock module rails?). DSE and Speed on the pad is set to max of course - playing around with the settings don't change anything - it's either off or on but bad.

Seeing that on the MM the only physical available pin is SD_B0_01/ pin36 that can be routed from ENET2_REF_CLK2 using ALT9 (and using SION). Somewhere on a NXP thread I read that this GPIO bank is considered to be "slow" and that nobody successfully did ENET2 without external clock because of the driving and speed characteristics on this pin/pad. I can't find the URL to this thread but I remember it even mentioned the Errata. ENET_REF_CLK (ENET1) is actually not available on the MicroMod - I was thinking to use that signal as on the T41 this pin can drive the onboard PHY. I was also thinkig about using the XBAR to send the REF2 clock to a pad that is "stronger", but it seems it can't be cross barred. I also remember that another i.MX6 chip had introduced a feature to route this CLK2 to a GPIO16 for such reasons. I'm confused as you can see.

I don't need the SD card pins, so that conflict is not an issue, I also think all the other signals are fine and something is weird on the REF_CLK2.

Do you have some ideas what to do before switching to a prototype PCB with an external clock generator? can I cascade something? Can you confirm that this is a "hard-coded" problem or should this pad/pin easily drive 50 MHz with 3V swing? Or is my teensy broken?!
 
Are you saying you wrote two drivers, one for FNET and one for QNEthernet? Or are you saying you’re trying to get FNET working with QNEthernet? QNEthernet uses lwIP under the covers for its TCP/IP stack, not FNET, which is a different stack.
 
Are you saying you wrote two drivers, one for FNET and one for QNEthernet? Or are you saying you’re trying to get FNET working with QNEthernet? QNEthernet uses lwIP under the covers for its TCP/IP stack, not FNET, which is a different stack.
yes two drivers one for each library - but I stick the QNEthernet until I get all the settings right and might only then continue with FNET. The library is not the problem nor is it my driver (I guess) - it's either a weird hardware problem or conceptually REF_CLK2 is hard to get right on ENET2 of the MUX simply conflicts with the MM pinout. I think with an external clock this would work much better but this requires a new prototype pcb.
 
Seeing that on the MM the only physical available pin is SD_B0_01/ pin36 that can be routed from ENET2_REF_CLK2 using ALT9 (and using SION). Somewhere on a NXP thread I read that this GPIO bank is considered to be "slow" and that nobody successfully did ENET2 without external clock because of the driving and speed characteristics on this pin/pad.

This seems very unlikely. SD_B0_01 is normally used for SD card where the clock is usually 48 MHz, unless the card says it's limited to only 25 MHz. SD cards work quite well, so I seriously doubt that particular pin is somehow "slow".

As a quick test, I wrote this small program to turn on the 50 MHz ethernet clock on MicroMod pin 36.

Code:
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
#define RMII_PAD_CLOCK          0x00E9

void setup() {
  Serial.begin(9600);
  Serial.print("Ethernet Testing");
  Serial.print("----------------\n");

#if 0
  pinMode(36, OUTPUT);
  while (1) {
    digitalWriteFast(36, HIGH);
    delayNanoseconds(100);
    digitalWriteFast(36, LOW);
    delayNanoseconds(100);
  }
#endif

  CCM_CCGR7 |= CCM_CCGR7_ENET2(CCM_CCGR_ON);
 
  // configure PLL6 for 50 MHz, pg 1173
  CCM_ANALOG_PLL_ENET_CLR = CCM_ANALOG_PLL_ENET_POWERDOWN
                            | CCM_ANALOG_PLL_ENET_BYPASS | 0x0F;
  CCM_ANALOG_PLL_ENET_SET = CCM_ANALOG_PLL_ENET_ENABLE | CCM_ANALOG_PLL_ENET_BYPASS
                            | CCM_ANALOG_PLL_ENET_ENET2_REF_EN | CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN
                            | CCM_ANALOG_PLL_ENET_ENET2_DIV_SELECT(1) | CCM_ANALOG_PLL_ENET_DIV_SELECT(1);
  while (!(CCM_ANALOG_PLL_ENET & CCM_ANALOG_PLL_ENET_LOCK)) ; // wait for PLL lock
  CCM_ANALOG_PLL_ENET_CLR = CCM_ANALOG_PLL_ENET_BYPASS;
  Serial.printf("PLL6 = %08X (should be 80202001)\n", CCM_ANALOG_PLL_ENET);

  // configure REFCLK to be driven as output by PLL6, pg 326
  CLRSET(IOMUXC_GPR_GPR1, IOMUXC_GPR_GPR1_ENET2_CLK_SEL,
         IOMUXC_GPR_GPR1_ENET2_TX_CLK_DIR | IOMUXC_GPR_GPR1_ENET_IPG_CLK_S_EN);
  Serial.printf("GPR1 = %08X\n", IOMUXC_GPR_GPR1);

  // configure pin to output clock
  IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_01 = RMII_PAD_CLOCK;
  IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_01 = 9 | 0x10; // ENET2_REF_CLK2  SD_B0_01 Alt9, pg 537
  Serial.println("done");
}

void loop() {
}

Here is the waveform my oscilloscope sees on pin 36.

My scope is also only rated for 200 MHz bandwidth, so it's a bit challenged to really measure 50 MHz. Still, you can see the waveform is pretty solidly over 3 volts (scales is 2 volts/division) for the logic high time and near 0 volts for logic low. Looks like a plenty strong enough digital signal.

file.png


Measuring high frequency signals is always a bit tricky. Even a relatively short ground wire causes quite a bit of ringing and distortion. Here's a photo of my attempt to minimize ground lead length. There's still some overshoot and ringing, but this is the best I'm going to do for a quick test. Hopefully it's good enough.

1722553128638.png
 
If you try this code, you might adjust the RMII_PAD_CLOCK setting. 3 of the bits control the pin's drive strength. You also want to make sure you have fast slew rate configured.

sc.png
 
This seems very unlikely. SD_B0_01 is normally used for SD card where the clock is usually 48 MHz, unless the card says it's limited to only 25 MHz. SD cards work quite well, so I seriously doubt that particular pin is somehow "slow".

As a quick test, I wrote this small program to turn on the 50 MHz ethernet clock on MicroMod pin 36.

Code:
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
#define RMII_PAD_CLOCK          0x00E9

void setup() {
  Serial.begin(9600);
  Serial.print("Ethernet Testing");
  Serial.print("----------------\n");

#if 0
  pinMode(36, OUTPUT);
  while (1) {
    digitalWriteFast(36, HIGH);
    delayNanoseconds(100);
    digitalWriteFast(36, LOW);
    delayNanoseconds(100);
  }
#endif

  CCM_CCGR7 |= CCM_CCGR7_ENET2(CCM_CCGR_ON);
 
  // configure PLL6 for 50 MHz, pg 1173
  CCM_ANALOG_PLL_ENET_CLR = CCM_ANALOG_PLL_ENET_POWERDOWN
                            | CCM_ANALOG_PLL_ENET_BYPASS | 0x0F;
  CCM_ANALOG_PLL_ENET_SET = CCM_ANALOG_PLL_ENET_ENABLE | CCM_ANALOG_PLL_ENET_BYPASS
                            | CCM_ANALOG_PLL_ENET_ENET2_REF_EN | CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN
                            | CCM_ANALOG_PLL_ENET_ENET2_DIV_SELECT(1) | CCM_ANALOG_PLL_ENET_DIV_SELECT(1);
  while (!(CCM_ANALOG_PLL_ENET & CCM_ANALOG_PLL_ENET_LOCK)) ; // wait for PLL lock
  CCM_ANALOG_PLL_ENET_CLR = CCM_ANALOG_PLL_ENET_BYPASS;
  Serial.printf("PLL6 = %08X (should be 80202001)\n", CCM_ANALOG_PLL_ENET);

  // configure REFCLK to be driven as output by PLL6, pg 326
  CLRSET(IOMUXC_GPR_GPR1, IOMUXC_GPR_GPR1_ENET2_CLK_SEL,
         IOMUXC_GPR_GPR1_ENET2_TX_CLK_DIR | IOMUXC_GPR_GPR1_ENET_IPG_CLK_S_EN);
  Serial.printf("GPR1 = %08X\n", IOMUXC_GPR_GPR1);

  // configure pin to output clock
  IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_01 = RMII_PAD_CLOCK;
  IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_01 = 9 | 0x10; // ENET2_REF_CLK2  SD_B0_01 Alt9, pg 537
  Serial.println("done");
}

void loop() {
}

Here is the waveform my oscilloscope sees on pin 36.

My scope is also only rated for 200 MHz bandwidth, so it's a bit challenged to really measure 50 MHz. Still, you can see the waveform is pretty solidly over 3 volts (scales is 2 volts/division) for the logic high time and near 0 volts for logic low. Looks like a plenty strong enough digital signal.

View attachment 35307

Measuring high frequency signals is always a bit tricky. Even a relatively short ground wire causes quite a bit of ringing and distortion. Here's a photo of my attempt to minimize ground lead length. There's still some overshoot and ringing, but this is the best I'm going to do for a quick test. Hopefully it's good enough.

View attachment 35308
ok that's very helpful. Thanks a lot! I will try that tomorrow and see the clock on my scope. I'm aware of the probing, grounding etc. issues and still can improve my setup in this regard. The thing is, RXD0, RXD1, TXD0, TXD1 and the other signals look much better - it's only the clock that worries me... shouldn't they also be near 50 MHz?
 
If the data pins are changing at maximum possible rate, that is alternating 0s and 1s on every clock cycle, you'd expect to see 25 MHz square waves because the date changes once for each full cycle of the 50 MHz clock.

You can also see in the screenshot that my scope measured 49.998 MHz for the clock signal. But my scope isn't connected to a high accurate time base, so I wouldn't necessarily take that to mean the clock is off by 2 kHz.
 
However, I do have a frequency counter which is connected to a high accuracy GPS disciplined over oscillator.

1722554657387.png


Turns out my scope's frequency reference is probably pretty good afterall.

In other news, MicroMod pin 36 is able to drive about 6 feet of coax cable to reach the frequency counter...
 
[SOLVED SEE BELOW]

Many thanks for your valuable information. I have now a solid clock and packets going in. It was a combination of ground bounce and probing (forgot to switch to 1:10). The code was fine. My lab setup was improved and yes the clock is good enough now.

However, and I think I might need Shawn, I don’t get an IP. DHCP Client is running and sending raw frames (enabled in the options) at least toggles the TX lines and TX enable. PHY LED is even dancing to my sending rhythm. Upon starting the program I see (on the TXEN) two frames being sent that are not from me, so I guess that could be arp with dhcp. But I don’t get an IP and I‘m not sure if what I send is correctly placed on the media (kinda hard to measure). Using QN’s Frame Loopback I don’t see my own frames neither.
Could this be a skew dataline in regards to phase lock to the clock? That would explain that my scope sees TX activity but it’s simply not a well formed TX frame. Or my ENET2 port misses something for TX but why do I then see TX-EN/D0/D1 activity.

If you can think of anything that I should try to circle in to the problem, that would be great before I will share the code.

Thanks!

EDIT:
using direct connection to my laptop running WireShark it turns out my raw frames are correctly transmitted. It's as soon as I go Layer 3 no packets are sent as if lwip wouldn't be registered to form a packet. Do I have to do anything else to QN and lwip to register my driver? Again, Layer 2 is working.

EDIT2:
I forgot to set the administrative link up/down in my driver towards the upper layer (lwip) and was only setting it in my driver - therefore raw frames were working but no L3. It's running now! Will consolidate everything and post again.
 
Last edited:
While I have finished my work with great success. One last thing bugs me:
I need to use:
C:
ENET2_MSCR = ENET_MSCR_MII_SPEED(0x33);  // gives around 1.4 MHz
while the original divider sets a clock way too high too around 7.5MHz
C:
ENET2_MSCR = ENET_MSCR_MII_SPEED(9);  // gives around 7.5 MHz why?!?!? - maximum is 2.5 MHz
why?!

anyway, I wrote this message in the discussion threat on github of ssilverman/QNEthernet:

success! I managed to get ENET2 working on MicroMod with a LAN8720A PHY. I will also design a custom board with a DP83825IRMQR PHY (the same as on the Teensy 4.1) which needs less external components (the mdi is voltage driven and it doesn't need external resistors) and it also needs other register and strap config.
For the LAN8720A an additional RESET line is needed that the main program should set towards QNEthernet. For the DP83825 two additional lines are needed. This is because on the teensy 4.1 onboard PHY these lines are predefines whereas on MircoMod the user can pick any two GPIO pins that are available on his project. The pins used on the 4.1 board are not brought out on the MicroMod so a value of the pins must be set and we can't assume defaults. For the LAN8720 with 1 RESET line I was tempted to use driver_set_chip_select_pin() which is available on the driver interface but maybe we should support Varargs on the init() to take all the pins depending on the PHY. This is a minor work put best in the hands of you as it changes things outside the driver folder.
Also I created a #define QNETHERNET_INTERNAL_DRIVER_TEENSYMM guard on my additional driver file driver_teensymm.c and driver_teensymm.h.
Then in lwip_driver.h the driver selection was extended to:
C:
// Select a driver
#if defined(QNETHERNET_DRIVER_W5500)
#include "drivers/driver_w5500.h"
#define QNETHERNET_INTERNAL_DRIVER_W5500
#elif defined(ARDUINO_TEENSY41)
#include "drivers/driver_teensy41.h"
#define QNETHERNET_INTERNAL_DRIVER_TEENSY41
#elif defined(ARDUINO_TEENSY_MICROMOD)
#include "drivers/driver_teensymm.h"
#define QNETHERNET_INTERNAL_DRIVER_TEENSYMM
#else
#include "drivers/driver_unsupported.h"
#define QNETHERNET_INTERNAL_DRIVER_UNSUPPORTED
#endif  // Driver selection

When I have a clear guideance on how to manage the additional user pins, I can push my code.
 
While I have finished my work with great success. One last thing bugs me:
I need to use:
C:
ENET2_MSCR = ENET_MSCR_MII_SPEED(0x33);  // gives around 1.4 MHz
while the original divider sets a clock way too high too around 7.5MHz
C:
ENET2_MSCR = ENET_MSCR_MII_SPEED(9);  // gives around 7.5 MHz why?!?!? - maximum is 2.5 MHz
why?!

anyway, I wrote this message in the discussion threat on github of ssilverman/QNEthernet:

success! I managed to get ENET2 working on MicroMod with a LAN8720A PHY. I will also design a custom board with a DP83825IRMQR PHY (the same as on the Teensy 4.1) which needs less external components (the mdi is voltage driven and it doesn't need external resistors) and it also needs other register and strap config.
For the LAN8720A an additional RESET line is needed that the main program should set towards QNEthernet. For the DP83825 two additional lines are needed. This is because on the teensy 4.1 onboard PHY these lines are predefines whereas on MircoMod the user can pick any two GPIO pins that are available on his project. The pins used on the 4.1 board are not brought out on the MicroMod so a value of the pins must be set and we can't assume defaults. For the LAN8720 with 1 RESET line I was tempted to use driver_set_chip_select_pin() which is available on the driver interface but maybe we should support Varargs on the init() to take all the pins depending on the PHY. This is a minor work put best in the hands of you as it changes things outside the driver folder.
Also I created a #define QNETHERNET_INTERNAL_DRIVER_TEENSYMM guard on my additional driver file driver_teensymm.c and driver_teensymm.h.
Then in lwip_driver.h the driver selection was extended to:
C:
// Select a driver
#if defined(QNETHERNET_DRIVER_W5500)
#include "drivers/driver_w5500.h"
#define QNETHERNET_INTERNAL_DRIVER_W5500
#elif defined(ARDUINO_TEENSY41)
#include "drivers/driver_teensy41.h"
#define QNETHERNET_INTERNAL_DRIVER_TEENSY41
#elif defined(ARDUINO_TEENSY_MICROMOD)
#include "drivers/driver_teensymm.h"
#define QNETHERNET_INTERNAL_DRIVER_TEENSYMM
#else
#include "drivers/driver_unsupported.h"
#define QNETHERNET_INTERNAL_DRIVER_UNSUPPORTED
#endif  // Driver selection

When I have a clear guideance on how to manage the additional user pins, I can push my code.
Hey, nice work! This Teensy MM driver, is it something you can share?
 
Back
Top