Ethernet hardware and Mongoose built-in TCP/IP stack
Overview
Mongoose Library provides its own TCP/IP stack. Mongoose Wizard generates code for several standard boards; but you may have one that we didn't try, or even one you designed yourself
Microcontroller
If there already is a board with that microcontroller, and it is supported in Mongoose Wizard, then you have a working driver, you need to check if the MAC to PHY connection type and PHY configuration in this board and your board are the same.
If there is not, look for a microcontroller in the same family as the one you are using, chances are they work just the same.
Otherwise, look for a microcontroller that has a similar Ethernet controller.
Ethernet (MAC) controller
Many microcontroller families share the same Ethernet controller or a very close relative, for example, all STM32F2, F4 and F7 chips have a very similar ETH controller. Even different vendors may have a similar instantiation of a third-party IP.
If you find that there already is a driver for that controller, you are covered. Otherwise, resort to a CMSIS driver or write your own driver, as described in this tutorial
MAC to PHY connection
The purpose of the PHY is to relieve the MAC controller from the subtleties of the physical layer. Most microcontrollers do not have a built-in PHY, though some, like the TM4C1294 and siblings, do. If your microcontroller has a built-in PHY, some of the options below are probably fixed inside the micro; yet you should know about them and check your vendor documentation.
If your microcontroller is connected to an external PHY, there are several ways of doing that. The most common are:
- MII (Media Independent interface), that uses 4 data pins and a 25MHz clock to get 100Mbps
- RMII (Reduced MII), that uses 2 data pins and a 50MHz clock
- Their Gigabit cousins
There are two main cases here: either the MAC clocks the PHY, or the PHY clocks the MAC. This is easily determined by observing the schematic circuit, and reading the PHY datasheet; see examples in the Appendix below.
Ethernet pinout
There are usually no more than a couple of possibilities to choose from, but you have to pick the right one. Those signals described in the MAC to PHY connection paragraph above have to be configured in the right pin. Which signals has to be configured for which pin, is determined by observing the board schematics; how to do it, depends on your IDE. Most vendor IDEs have a way to enable the Ethernet controller peripheral and just assign pins for it; see this Cube tutorial, for example.
If you are just using make, then your chip datasheet and Reference Manual will carry that information; pay attention to the direction of clocking as you will likely need to configure one pin to either receive the PHY clock or deliver it to the PHY; see examples in the Appendix below.
PHY configuration
Mongoose drivers do not configure the PHY for a specific link, they honor whatever has been defined in hardware by board designers. Most commonly, boards are designed to configure the PHY for auto-negotiation. Once the PHY connects with a switch, it reports whether it is working in 10 or 100 Mbps and in full- or half-duplex; Mongoose then configures the Ethernet controller to work accordingly, as clock frequency and data format in data pins depend on this.
// Hw pull-ups on PHY RXD0,1,DV to enable autonegotiation
static inline void ethernet_init(void) {
// Initialise Ethernet. Enable MAC GPIO pins, see
// https://www.st.com/resource/en/user_manual/um2407-stm32h7-nucleo144-boards-mb1364-stmicroelectronics.pdf
The way your board is configured, can be determined by observing the board schematics and reading the PHY datasheet; see examples in the Appendix below.
Mongoose does not use PHY reset pins, it assumes your PHY has already been reset when the driver init function is called, so if your board has a PHY reset pin, you must release reset before initializing Mongoose. You can check our hal.h
files to see how we do that in our baremetal examples:
// - PHY RST connected to GPIO1.4
static inline void ethernet_init(void) {
gpio_init(PIN('1', 4), GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL,
GPIO_SPEED_MEDIUM, GPIO_PULL_UP); // set GPIO1.4 as GPIO (PHY \RST)
gpio_write(PIN('1', 4), 0); // reset PHY
...
spin(10000); // keep PHY RST low for a while
gpio_write(PIN('1', 4), 1); // deassert RST
...
}
If your board does not configure the PHY pins, then you will have to do it yourself by properly setting those pins before releasing the PHY from reset:
// - PHY RXD0,1,DV = 1 on RST enable autonegotiation, no hw pull-ups
static inline void ethernet_init(void) {
...
gpio_init(PIN(5, 8), GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_LOW,
GPIO_PULL_NONE, 0); // set P5_8 as GPIO (PHY \RST)
gpio_write(PIN(5, 8), 0); // reset PHY
gpio_init(PIN(1, 13), GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,
GPIO_PULL_UP, 9); // set P1_13 as ENET0_RXDV <--- notice we configure pull-ups in these pins
gpio_init(PIN(1, 14), GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,
GPIO_PULL_UP, 9); // set P1_14 as ENET0_RXD0
gpio_init(PIN(1, 15), GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,
GPIO_PULL_UP, 9); // set P1_15 as ENET0_RXD1
...
spin(10000); // keep PHY RST low for a while
gpio_write(PIN(5, 8), 1); // deassert RST
...
}
Configuring Mongoose drivers
All Mongoose Ethernet drivers have a data structure that carries configuration data. Usually this is Ethernet controller configuration data, like the MDC divider, and PHY configuration data like its SMI address. Some drivers also support clocking configuration.
PHY initialization
PHY initialization is done as part of the driver initialization process.
On drivers that support PHY configuration, the driver header file (e.g.: stm32h.h
for the STM32H driver, amalgamated into mongoose.h
) will show default values for some of these macros:
MG_TCPIP_PHY_ADDR
- PHY SMI address, will be stored in thephy_addr
element of the data structureMG_TCPIP_PHY_CONF
- PHY-specific configuration data, will be stored in thephy_conf
element of the data structure (if present), and may contain a combination (OR) of these flags:MG_PHY_CLOCKS_MAC
- set when PHY clocks MAC. Otherwise, MAC clocks PHYMG_PHY_LEDS_ACTIVE_HIGH
- set if PHY LEDs are connected to ground, very few PHYs require this
Example defaults for the STM32H driver:
#ifndef MG_TCPIP_PHY_CONF
#define MG_TCPIP_PHY_CONF MG_PHY_CLOCKS_MAC
#endif
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 0
#endif
Driver initialization
Driver initialization is usually performed as part of the TCP/IP stack initialization, executing the contents of the MG_TCPIP_DRIVER_INIT
macro, defined in the driver header file (e.g.: stm32f.h
for the STM32F driver, amalgamated into mongoose.h
)
It can also be done explicitly by the user, by manually initializing the TCP/IP stack; see TCP/IP initialization below.
Default auto-initialization
In addition to those macros described for PHY initialization, each driver has some parameters that can be setup. The most common is the clock divider to get the SMI clock (MDC signal) for that management interface (part of the MII spec).
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 4
#endif
As you can see, there is a check for these macros to have a prior definition, so you can change any one of them in your mongoose_config.h
file; in fact, that is what you should do, as can be seen in Wizard-generated code:
#define MG_ENABLE_DRIVER_STM32H 1
// #define MG_DRIVER_MDC_CR 4 // RMII MDC clock divider, from 0 to 5
// #define MG_TCPIP_PHY_ADDR 0 // PHY address
// For static IP configuration, define MG_TCPIP_{IP,MASK,GW}
// By default, those are set to zero, meaning that DHCP is used
//
// #define MG_TCPIP_IP MG_IPV4(192, 168, 0, 10) // IP
// #define MG_TCPIP_GW MG_IPV4(192, 168, 0, 1) // Gateway
// #define MG_TCPIP_MASK MG_IPV4(255, 255, 255, 0) // Netmask
// Construct MAC address from the MCU unique ID. It is defined in the
// ST CMSIS header as UID_BASE
#define MGUID ((uint32_t *) 0x1FF1E800) // Unique 96-bit chip ID
#define MG_SET_MAC_ADDRESS(mac) \
do { \
mac[0] = 2; \
mac[1] = MGUID[0] & 255; \
mac[2] = (MGUID[0] >> 10) & 255; \
mac[3] = (MGUID[0] >> 19) & 255; \
mac[4] = MGUID[1] & 255; \
mac[5] = MGUID[2] & 255; \
} while (0)
As said, each driver has its own initialization macro, as each one may have its unique parameters. For example, this one is for the STM32H driver:
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_stm32h_data driver_data_; /* this structure contains driver and PHY config parameters */ \
static struct mg_tcpip_if mif_; /* this structure contains TCP/IP stack config parameters */ \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; /* clock divider for SMI (MDC + MDIO) */ \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; /* PHY address, usually 0, some boards use 1 */ \
driver_data_.phy_conf = MG_TCPIP_PHY_CONF; /* PHY-specific config, only some drivers require this */ \
mif_.ip = MG_TCPIP_IP; /* static IP, defaults to 0 to enable DHCP */ \
mif_.mask = MG_TCPIP_MASK; /* same as above */ \
mif_.gw = MG_TCPIP_GW; /* same as above */ \
mif_.driver = &mg_tcpip_driver_stm32h; /* pointer to specific driver functions */ \
mif_.driver_data = &driver_data_; /* pointer to driver config structure above, can be NULL */ \
MG_SET_MAC_ADDRESS(mif_.mac); /* user macro/function to set MAC address based on some unique characteristic */ \
mg_tcpip_init(mgr, &mif_); /* the actual initialization call */ \
MG_INFO(("Driver: stm32h, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
Manual (user) initialization
As said, you can run your own driver initialization process as part of your TCP/IP stack manual initialization; see TCP/IP initialization below for more information.
Most drivers use a struct mg_tcpip_driver_<drivername>
to hold driver and PHY configuration. In case this structure is not provided (there is a NULL pointer in that struct mg_tcpip_if
element), the driver will choose the most common defaults based on already-known boards. If you pass a pointer, you must initialize proper configuration parameters for that driver. See auto-initialization above for more information
TCP/IP initialization
TCP/IP stack initialization is usually performed as part of Mongoose initialization; this depends on the MG_ENABLE_TCPIP_DRIVER_INIT
macro value.
Mongoose usually defaults to MG_ENABLE_TCPIP_DRIVER_INIT = 1
and MG_TCPIP_DRIVER_INIT
contains proper code, as explained above.
To perform a manual initialization, #define MG_ENABLE_TCPIP_DRIVER_INIT 0
and then write your own code based, for example, in what has been explained above.
We have tutorials for each vendor, so most likely there is one covering your case.
Appendix
The following schematic snippet shows the typical STM32 board design where the PHY clocks the MAC.
- Resistor pull-ups in MODE0, MODE1, MODE2: these set auto-negotiation on
- Resistor pull-down on PHYAD0 sets the PHY address to 0
- Resistor pull-down on nINTSEL sets PHY pin 14 as REFCLKO, reference clock output. The PHY takes its clock reference from a 25MHz low-cost crystal and an internal PLL doubles it to 50MHz. REFCLKO is connected to the Ethernet controller clock reference
These are specific to this PHY chip, though the main principles apply.
The following schematic snippet shows the typical NXP iMXRT board design where the MAC clocks the PHY. Observe that there is no crystal, and the PHY receives a clock signal from the microcontroller:
This snippet does not include information on resistor pull-ups in pins PHYAD0, PHYAD1, PHYAD2, thing we need to check to determine this PHY address, that according to the datasheet defaults to 1.
Every PHY chip has its subtleties, careful reading of its datasheet will shine light on how to configure for it.