Ethernet is an asynchronous Carrier Sense Multiple Access with Collision Detect (CSMA/CD) protocol/interface.
It is generally not well suited for low power applications. However, with ubiquitous deployment, internet connectivity, high data rates and limitless rage expandability, Ethernet can accommodate nearly all wired communications.
Normal IEEE 802.3 compliant Ethernet frames are between 64 and 1518 bytes in length. They are made up of five or six different fields: a destination MAC address (DA), a source MAC address (SA), a type/length field, data payload, an optional padding field and a Cyclic Redundancy Check (CRC).
Additionally, when transmitted on the Ethernet medium, a 7-byte preamble field and Start-of-Frame (SOF) delimiter byte are appended to the beginning of the Ethernet packet.
Thus the traffic on the twist-pair cabling will appear as shown blow:
The preamble contains seven bytes of ``55H``, it allows the receiver to lock onto the stream of data before the actual frame arrives.
The Start-of-Frame Delimiter (SFD) is a binary sequence ``10101011`` (as seen on the physical medium). It is sometimes considered to be part of the preamble.
Packets with multi-cast destination addresses are designed to arrive and be important to a selected group of Ethernet nodes.
If the destination address field is the reserved multi-cast address, i.e. FF-FF-FF-FF-FF-FF, the packet is a broadcast packet and it will be directed to everyone sharing the network.
If the Least Significant bit in the first byte of the MAC address is clear, the address is a uni-cast address and will be designed for usage by only the addressed node.
Normally the EMAC controller incorporates receive filters which can be used to discard or accept packets with multi-cast, broadcast and/or uni-cast destination addresses.
When transmitting packets, the host controller is responsible for writing the desired destination address into the transmit buffer.
Source Address
^^^^^^^^^^^^^^
The source address field contains a 6-byte length MAC address of the node which created the Ethernet packet.
Users of Ethernet must generate a unique MAC address for each controller used.
MAC addresses consist of two portions. The first three bytes are known as the Organizationally Unique Identifier (OUI). OUIs are distributed by the IEEE.
The last three bytes are address bytes at the discretion of the company that purchased the OUI. More information about MAC Address used in ESP-IDF, please see :ref:`MAC Address Allocation <MAC-Address-Allocation>`.
When transmitting packets, the assigned source MAC address must be written into the transmit buffer by the host controller.
Type / Length
^^^^^^^^^^^^^
The type/length field is a 2-byte field, if the value in this field is <= 1500 (decimal), it is considered a length field and it specifies the amount of non-padding data which follows in the data field.
If the value is >= 1536, it represents the protocol the following packet data belongs to. The following are the most common type values:
Users implementing proprietary networks may choose to treat this field as a length field, while applications implementing protocols such as the Internet Protocol (IP) or Address Resolution Protocol (ARP), should program this field with the appropriate type defined by the protocol’s specification when transmitting packets.
Payload
^^^^^^^
The payload field is a variable length field, anywhere from 0 to 1500 bytes. Larger data packets will violate Ethernet standards and will be dropped by most Ethernet nodes.
This field contains the client data, such as an IP datagram.
Padding and FCS
^^^^^^^^^^^^^^^
The padding field is a variable length field added to meet IEEE 802.3 specification requirements when small data payloads are used.
The DA, SA, type, payload and padding of an Ethernet packet must be no smaller than 60 bytes.
If the data field is less than 46 bytes long, a padding field is required.
The FCS field is a 4-byte field which contains an industry standard 32-bit CRC calculated with the data from the DA, SA, type, payload and padding fields.
Given the complexity of calculating a CRC, the hardware normally will automatically generate a valid CRC and transmit it. Otherwise, the host controller must generate the CRC and place it in the transmit buffer.
Normally, the host controller does not need to concern itself with padding and the CRC which the hardware EMAC will also be able to automatically generate when transmitting and verify when receiving.
However, the padding and CRC fields will be written into the receive buffer when packets arrive, so they may be evaluated by the host controller if needed.
The communication between MAC and PHY can have diverse choices: **MII** (Media Independent Interface), **RMII** (Reduced Media Independent Interface) and etc.
One of the obvious difference between MII and RMII is the signal consumption. For MII, it usually costs up to 18 signals. Instead, RMII interface can reduce the consumption to 9.
In RMII mode, both the receiver and transmitter signals are referenced to the ``REF_CLK``. **REF_CLK must be stable during any access to PHY and MAC**.
Generally there're three ways to generate the ``REF_CLK`` depending on the PHY device in your design:
* Some PHY chip can derive the ``REF_CLK`` from its external connected 25MHz crystal oscillator (as seen the option *a* in the picture). In this case, you should select ``CONFIG_ETH_RMII_CLK_INPUT`` in :ref:`CONFIG_ETH_RMII_CLK_MODE`.
* Some PHY chip uses an external connected 50MHz crystal oscillator or other clock source, which can also be used as the ``REF_CLK`` for MAC side (as seen the option *b* in the picture). In this case, you still need to select ``CONFIG_ETH_RMII_CLK_INPUT`` in :ref:`CONFIG_ETH_RMII_CLK_MODE`.
* Some EMAC controller can generate the ``REF_CLK`` using its internal high precision PLL (as seen the option *c* in the picture). In this case, you should select ``CONFIG_ETH_RMII_CLK_OUTPUT`` in :ref:`CONFIG_ETH_RMII_CLK_MODE`.
``REF_CLK`` is configured via Project Configuration as described above by default. However, it can be overwritten from user application code by appropriately setting :cpp:member:`eth_esp32_emac_config_t::interface` and :cpp:member:`eth_esp32_emac_config_t::clock_config` members. See :cpp:enum:`emac_rmii_clock_mode_t` and :cpp:enum:`emac_rmii_clock_gpio_t` for more details.
If the RMII clock mode is selected to ``CONFIG_ETH_RMII_CLK_OUTPUT``, then ``GPIO0`` can be used to output the ``REF_CLK`` signal. See :ref:`CONFIG_ETH_RMII_CLK_OUTPUT_GPIO0` for more information.
What's more, if you're not using PSRAM in your design, GPIO16 and GPIO17 are also available to output the reference clock. See :ref:`CONFIG_ETH_RMII_CLK_OUT_GPIO` for more information.
If the RMII clock mode is selected to ``CONFIG_ETH_RMII_CLK_INPUT``, then ``GPIO0`` is the only choice to input the ``REF_CLK`` signal.
Please note that, ``GPIO0`` is also an important strapping GPIO on ESP32.
If GPIO0 samples a low level during power up, ESP32 will go into download mode. The system will get halted until a manually reset.
The workaround of this issue is disabling the ``REF_CLK`` in hardware by default, so that the strapping pin won't be interfered by other signals in boot stage. Then re-enable the ``REF_CLK`` in Ethernet driver installation stage.
The ways to disable the ``REF_CLK`` signal can be:
* Disable or power down the crystal oscillator (as the case *b* in the picture).
* Force the PHY device in reset status (as the case *a* in the picture). **This could fail for some PHY device** (i.e. it still outputs signal to GPIO0 even in reset state).
We need to setup necessary parameters for MAC and PHY respectively based on your Ethernet board design and then combine the two together, completing the driver installation.
*:cpp:member:`eth_mac_config_t::sw_reset_timeout_ms`: software reset timeout value, in milliseconds, typically MAC reset should be finished within 100ms.
*:cpp:member:`eth_mac_config_t::rx_task_stack_size` and :cpp:member:`eth_mac_config_t::rx_task_prio`: the MAC driver creates a dedicated task to process incoming packets, these two parameters are used to set the stack size and priority of the task.
*:cpp:member:`eth_mac_config_t::flags`: specifying extra features that the MAC driver should have, it could be useful in some special situations. The value of this field can be OR'd with macros prefixed with ``ETH_MAC_FLAG_``. For example, if the MAC driver should work when cache is disabled, then you should configure this field with :c:macro:`ETH_MAC_FLAG_WORK_WITH_CACHE_DISABLE`.
:SOC_EMAC_SUPPORTED:* :cpp:member:`eth_esp32_emac_config_t::smi_mdc_gpio_num` and :cpp:member:`eth_esp32_emac_config_t::smi_mdio_gpio_num`: the GPIO number used to connect the SMI signals.
:SOC_EMAC_SUPPORTED:* :cpp:member:`eth_esp32_emac_config_t::interface`: configuration of MAC Data interface to PHY (MII/RMII).
:SOC_EMAC_SUPPORTED:* :cpp:member:`eth_esp32_emac_config_t::clock_config`: configuration of EMAC Interface clock (``REF_CLK`` mode and GPIO number in case of RMII).
*:cpp:member:`eth_phy_config_t::phy_addr`: multiple PHY device can share the same SMI bus, so each PHY needs a unique address. Usually this address is configured during hardware design by pulling up/down some PHY strapping pins. You can set the value from 0 to 15 based on your Ethernet board. Especially, if the SMI bus is shared by only one PHY device, setting this value to -1 can enable the driver to detect the PHY address automatically.
*:cpp:member:`eth_phy_config_t::reset_timeout_ms`: reset timeout value, in milliseconds, typically PHY reset should be finished within 100ms.
*:cpp:member:`eth_phy_config_t::autonego_timeout_ms`: auto-negotiation timeout value, in milliseconds. Ethernet driver will start negotiation with the peer Ethernet node automatically, to determine to duplex and speed mode. This value usually depends on the ability of the PHY device on your board.
*:cpp:member:`eth_phy_config_t::reset_gpio_num`: if your board also connect the PHY reset pin to one of the GPIO, then set it here. Otherwise, set this field to -1.
* When creating MAC and PHY instance for SPI-Ethernet modules (e.g. DM9051), the constructor function must have the same suffix (e.g. `esp_eth_mac_new_dm9051` and `esp_eth_phy_new_dm9051`). This is because we don't have other choices but the integrated PHY.
* We have to create an SPI device handle firstly and then pass it to the MAC constructor function. More instructions on creating SPI device handle, please refer to :doc:`SPI Master <../peripherals/spi_master>`.
* The SPI device configuration (i.e. `spi_device_interface_config_t`) can be different for other Ethernet modules. Please check out your module's spec and the examples in esp-idf.
To install the Ethernet driver, we need to combine the instance of MAC and PHY and set some additional high-level configurations (i.e. not specific to either MAC or PHY) in :cpp:class:`esp_eth_config_t`:
*:cpp:member:`esp_eth_config_t::mac`: instance that created from MAC generator (e.g. :cpp:func:`esp_eth_mac_new_esp32`).
*:cpp:member:`esp_eth_config_t::phy`: instance that created from PHY generator (e.g. :cpp:func:`esp_eth_phy_new_ip101`).
*:cpp:member:`esp_eth_config_t::check_link_period_ms`: Ethernet driver starts an OS timer to check the link status periodically, this field is used to set the interval, in milliseconds.
*:cpp:member:`esp_eth_config_t::stack_input`: In most of Ethernet IoT applications, any Ethernet frame that received by driver should be passed to upper layer (e.g. TCP/IP stack). This field is set to a function which is responsible to deal with the incoming frames. You can even update this field at runtime via function :cpp:func:`esp_eth_update_input_path` after driver installation.
*:cpp:member:`esp_eth_config_t::on_lowlevel_init_done` and :cpp:member:`esp_eth_config_t::on_lowlevel_deinit_done`: These two fields are used to specify the hooks which get invoked when low level hardware has been initialized or de-initialized.
Ethernet driver also includes event-driven model, which will send useful and important event to user space. We need to initialize the event loop before installing the Ethernet driver. For more information about event-driven programming, please refer to :doc:`ESP Event <../system/esp_event>`.
esp_event_loop_create_default(); // create a default event loop that running in background
esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL); // register Ethernet event handler (to deal with user specific stuffs when event like link up/down happened)
Start Ethernet Driver
---------------------
After driver installation, we can start Ethernet immediately.
..highlight:: c
::
esp_eth_start(eth_handle); // start Ethernet driver state machine
.._connect-driver-to-stack:
Connect Driver to TCP/IP Stack
------------------------------
Up until now, we have installed the Ethernet driver. From the view of OSI (Open System Interconnection), we're still on level 2 (i.e. Data Link Layer). We can detect link up and down event, we can gain MAC address in user space, but we can't obtain IP address, let alone send HTTP request.
The TCP/IP stack used in ESP-IDF is called LwIP, for more information about it, please refer to :doc:`LwIP <../../api-guides/lwip>`.
To connect Ethernet driver to TCP/IP stack, these three steps need to follow:
It is recommended to fully initialize the Ethernet driver and network interface prior registering user's Ethernet/IP event handlers, i.e. register the event handlers as the last thing prior starting the Ethernet driver. Such approach ensures that Ethernet/IP events get executed first by the Ethernet driver or network interface and so the system is in expected state when executing user's handlers.
Ethernet on MCU usually has a limitation in the number of frames it can handle during network congestion, because of the limitation in RAM size. A sending station might be transmitting data faster than the peer end can accept it. Ethernet flow control mechanism allows the receiving node to signal the sender requesting suspension of transmissions until the receiver catches up. The magic behind that is the pause frame, which was defined in IEEE 802.3x.
Pause frame is a special Ethernet frame used to carry the pause command, whose EtherType field is 0x8808, with the Control opcode set to 0x0001. Only stations configured for full-duplex operation may send pause frames. When a station wishes to pause the other end of a link, it sends a pause frame to the 48-bit reserved multicast address of 01-80-C2-00-00-01. The pause frame also includes the period of pause time being requested, in the form of a two-byte integer, ranging from 0 to 65535.
One thing should be kept in mind, is that the pause frame ability will be advertised to peer end by PHY during auto negotiation. Ethernet driver sends pause frame only when both sides of the link support it.
There are multiple PHY manufactures with their wide portfolios of chips available. The ESP-IDF already supports several PHY chips however one can easily get to a point where none of them satisfies user's actual needs due to either price, features, stock availability etc.
Luckily, a management interface between EMAC and PHY is standardized by IEEE 802.3 in 22.2.4 Management functions section. It defines provisions of so called “MII Management Interface” for the purposes of controlling the PHY and gathering status from the PHY. A set of management registers is defined to control chip behavior, link properties, auto-negotiation configuration etc. This basic management functionality is addressed by :component_file:`esp_eth/src/esp_eth_phy_802_3.c` in ESP-IDF and so it makes a creation of new custom PHY chip driver quite a simple task.
..note::
Always consult with PHY datasheet since some PHY chips may not comply with IEEE 802.3, Section 22.2.4. It does not mean you are not able to create a custom PHY driver, it will just require more effort. You will have to define all PHY management functions.
Majority of PHY management functionality required by the ESP-IDF Ethernet driver is covered by the :component_file:`esp_eth/src/esp_eth_phy_802_3.c` however, the following may require developing chip specific management functions:
* link status which is almost always chip specific,
* chip initialization, even though it is not strictly required, should be customized to at least ensure that expected chip is used and
* chip specific features configuration.
**Steps to create custom PHY driver:**
1. Define vendor specific registry layout based on PHY datasheet. See :component_file:`esp_eth/src/esp_eth_phy_ip101.c` as an example.
2. Prepare derived PHY management object infostructure which
* must contain at least parent IEEE 802.3 :cpp:class:`phy_802_3_t` object and
* optionally contain additional variables needed to support non-IEEE 802.3 or customized functionality. See :component_file:`esp_eth/src/esp_eth_phy_ksz80xx.c` as an example.
3. Define chip specific management call-back functions.
4. Initialize parent IEEE 802.3 object and re-assign chip specific management call-back functions.
Once you finish the new custom PHY driver implementation, consider sharing it among with other users via `IDF Component Registry <https://components.espressif.com/>`_.