Ethernet
TEMU Ethernet support is split into MAC, PHY, and link layers. The usual topology is:
MAC -> PHY -> EthernetLink -> PHY -> MAC
The MAC is the device model that target software controls, for example a board Ethernet controller.
The PHY models link state, MDIO/MII registers, auto-negotiation, and forwarding to the Ethernet link.
EthernetLink routes frames between connected PHY devices.
Implementing a MAC
A custom Ethernet controller should implement temu_MACIface, defined in temu-c/Bus/Ethernet.h.
The interface callbacks are:
-
connectedanddisconnected, called when the PHY is connected to or disconnected from a link. -
upanddown, called when the Ethernet link state changes. -
receive, called by the PHY when a frame is delivered to the MAC. -
getMACandsetMAC, used by the PHY and link routing code to track the active MAC address.
The MAC model should keep an interface reference to a PHY:
typedef struct {
temu_Object Super;
temu_PHYIfaceRef phy;
uint64_t mac;
} MyMAC;
The MAC interface forwards link events into the device model and exposes the currently configured address:
static void
connected(void *obj)
{
temu_logInfo(obj, "ethernet connected");
}
static void
disconnected(void *obj)
{
temu_logInfo(obj, "ethernet disconnected");
}
static void
up(void *obj)
{
temu_logInfo(obj, "ethernet link up");
}
static void
down(void *obj)
{
temu_logInfo(obj, "ethernet link down");
}
static int
receive(void *obj, temu_EthFrame *frame)
{
MyMAC *mac = (MyMAC *)obj;
/* Copy the frame into the receive DMA path and raise the device IRQ. */
return 0;
}
static uint64_t
getMAC(void *obj)
{
MyMAC *mac = (MyMAC *)obj;
return mac->mac;
}
static void
setMAC(void *obj, uint64_t addr)
{
MyMAC *mac = (MyMAC *)obj;
mac->mac = addr;
}
static temu_MACIface MACIface = {
connected, disconnected, up, down, receive, getMAC, setMAC};
When target software transmits a frame, the MAC model builds a temu_EthFrame and sends it through the connected PHY.
The frame buffer contains the Ethernet frame data, including destination MAC, source MAC, EtherType or length field, payload, and space for the CRC bytes.
The frame does not need a valid CRC unless the receiving model checks it:
static int
sendFrame(MyMAC *mac, temu_EthFrame *frame)
{
if (!mac->phy.Iface || !mac->phy.Iface->send) {
return 0;
}
return mac->phy.Iface->send(mac->phy.Obj, frame);
}
As for other bus models, avoid sending response frames directly from the receive path when the response is caused by an incoming frame. Post a TEMU event and send from that event instead, so the current Ethernet link activation can finish before another transmission starts.
The plugin registration should expose both the MAC interface and the PHY reference:
temu_addInterfaceReference(cls, "phy", offsetof(MyMAC, phy),
TEMU_PHY_IFACE_TYPE, 1, 0, NULL, NULL,
"PHY device.");
temu_addInterface(cls, "MACIface", TEMU_MAC_IFACE_TYPE, &MACIface, 0,
"Ethernet MAC interface.");
Using GenericPHY
For most simulated equipment, use GenericPHY instead of writing a PHY model.
It implements PHYIface and MDIOIface, provides standard MII registers, forwards frames between the MAC and the link, tracks link-up state, and participates in auto-negotiation.
Create an Ethernet link, create a generic PHY, connect the MAC to the PHY, connect the PHY back to the MAC, and then connect the PHY to the link:
$ temu --project=temu-project.yaml
temu> import BusModels
temu> EthernetLink.new name=eth0
temu> GenericPHY.new name=phy0
temu> MyMAC.new name=mac0
temu> connect a=mac0.phy b=phy0:PHYIface
temu> connect a=phy0.macDevice b=mac0:MACIface
temu> eth-connect link=eth0 device=phy0:PHYIface
The object method is equivalent:
temu> eth0.connect device=phy0:PHYIface
GenericPHY accepts optional creation arguments to restrict advertised speeds:
temu> GenericPHY.new name=phy10 base10=1
temu> GenericPHY.new name=phy100 base100=1
temu> GenericPHY.new name=phy1000 base1000=1
EthernetLink can also capture traffic to a PCAPNG file:
temu> eth-enable-capture link=eth0 file=eth0.pcapng
For live inspection with Wireshark, use a named pipe:
temu> eth-enable-capture link=eth0 file=/tmp/temu-eth0.pcapng named-pipe=1