Single Pair Ethernet (SPE) runs Ethernet over one twisted pair instead of four, and the same pair can also carry the power. For a machine whose devices sit in a line, that means one cable from device to device, carrying both data and power.
Most of what you read about SPE is about cables, connectors and PHYs. This post is about the firmware. As soon as devices are chained one behind the other, the software has to answer questions a star network never asks: Which neighbour is the controller? Where am I in the line? What happens to everyone behind me when I reboot?
In this article I go through the firmware lessons from bringing a 100BASE-T1 daisy chain into an industrial control system: one controller, a line of identical nodes, each node an i.MX RT (Cortex-M7) bridging frames between its two Ethernet ports.
The setup
- Physical layer: 100BASE-T1, point-to-point links chained into a line. Power for the nodes travels over the same pair (power over data line, PoDL).
- Nodes: identical hardware and one firmware image for all of them. Each node has two Ethernet MACs, one per PHY.
- Software: an RTOS, with a lower-priority forwarding task and a queue that bridge frames from one port to the other.
1. Only the upstream port decides “controller reachable”
The mental model: a node’s link to the controller is its upstream port. The downstream port is someone else’s problem.
A tempting first version marks the link as up when either PHY has a link:
up = upstream || downstream. That looks harmless. But every node except the last one
always has a live downstream link, so it never notices that the controller is gone and keeps
running.
The rule that works: the link state the protocol sees is the upstream PHY’s current link bit, and nothing else. The downstream bit only controls whether frames are forwarded. Keep the rule as a tiny pure function with host tests, so it cannot quietly regress.
Testing link loss when the cable carries power
One limit remains, and the diagram shows it: link state is local. A node only sees its own neighbour. Detecting a lost controller everywhere in a long chain needs an end-to-end heartbeat, not link state.
2. Position from the hop count, identity from silicon
The mental model: a node’s identity is fixed, its position is learnt.
With one firmware image for every node, nothing in flash says “you are node 3”. So the two questions are split:
- Identity comes from a unique serial number in silicon, for example from a secure element on each node. The node also derives its MAC address from that serial, setting the “locally administered” bit and clearing the “multicast” bit, before the Ethernet MACs start.
- Position is discovered at run time. The controller sends a discovery request with a hop counter of 0, and every node that forwards it downstream adds 1.
Two details make it robust:
- Commands go to a position, never to “everyone”. There is no broadcast fallback, because a broadcast reaches the wrong node when the order in the line is not the one that was accepted.
- Rediscovery waits for the line to settle. Nodes report a new downstream link a limited number of times, and the controller collects answers in a short window after the last link-up instead of reacting to each one.
3. Store the topology only once it is stable
The controller remembers which device sat at which position, so it can spot a swapped, replaced, missing or extra device later. The trap is when to store that map.
If you store the first discovery after power-up, you may store a half-built chain: the nodes further down are still booting. Later, the missing ones come up and get flagged as “extra devices”: a false fault.
The fix is a debounce. Every usable discovery restarts one timer, and the map is written only when the timer fires. A cold start where every node answers the first discovery will not catch the problem. Test with a growing chain, where nodes come up one after another, and check that the map is written exactly once.
Save the map atomically (write a temporary file, sync, then rename), with a CRC so a damaged file is detected.
Enforce at once, notify later
4. The receive interrupt is one bit: drain the ring
The mental model: “frames arrived” is a flag, not a count.
The i.MX RT Ethernet MAC raises one receive interrupt flag no matter how many frames landed. If the CPU was busy and three frames arrived, the interrupt still fires once. A handler that reads one frame per interrupt falls behind and stays behind, and no error counter moves.
In a daisy chain this hurts more than on a single device: every node behind the slow one also answers late.
The fix: on each interrupt, keep reading until the receive ring is empty, bounded by the ring size. Three companion changes go with it:
- Read and discard bad frames too, so their buffers are freed.
- Size the forwarding queue for a full ring from each port.
- Keep the frame buffer static, off the interrupt stack.
5. Keep slow hardware access off the critical path
The PHYs are managed over MDIO, which is often bit-banged on GPIO pins. Running that inside a high-priority task blocks the CPU for milliseconds at a time.
Move it to a low-priority worker and match the results back by a token. The high-priority task then only queues a request and picks up the answer later, and its worst-case runtime drops from milliseconds to microseconds.
Bit-bang timing is empirical
6. Updating every device on the chain
Firmware updates over the chain follow a few simple rules:
- Update the devices one after another, in list order, and skip devices that already run the target version.
- Wait for each device to report its new version before moving on. A restarting device can cut off every device behind it.
- Classify failures: per device (retry, continue, stop) or global (end the batch).
- Do not show topology faults while the batch runs. Devices dropping out during their own restart is expected.
Staging every device first and restarting them all together might be faster. Until you have measured that the chain survives it, keep the update sequential.
Summary
- One rule per question: only the upstream port defines “controller reachable”, the hop count defines position, and silicon defines identity.
- Wait for the line to settle: store the topology, and show faults, only once the chain has stopped changing.
- The general rule: in a daisy chain, every node’s behaviour affects everyone behind it, so design and test for the line, not for the single device.
Sources
- NXP, i.MX RT1060 Processor Reference Manual, Ethernet MAC (ENET) chapter.
- IEEE 802.3bw (100BASE-T1) and IEEE 802.3bu (PoDL).
💬 Questions, corrections, or your own war story? Leave a comment below.
💬 Discussion
Questions, corrections, or war stories from your own board? Leave a comment below — I read and answer all of them. (Sign-in with GitHub.)