This post is part of the EthTrcv series and builds on the previous post on loopback modes. That post was about deliberately short-circuiting the data path through the PHY to narrow down hardware faults. This post goes one level deeper: how do you read the PHY registers themselves — the ones that tell you why a link refuses to come up, before you even reach for loopback?
Anyone who has diagnosed a link failure based on EthTrcv_GetLinkState alone knows the
problem: the return value is binary, ETHTRCV_LINK_STATE_DOWN or
ETHTRCV_LINK_STATE_ACTIVE. The actual root cause — an autonegotiation mismatch, a
cable fault, crosstalk — lives in the PHY registers, not in the AUTOSAR return value.
What Is MDIO? — A Quick Recap
MDIO (Management Data Input/Output) is the serial control bus between the MAC/SoC and
the PHY chip, over which EthTrcv_Init and every register access run — not over
MII/RGMII. IEEE 802.3 defines two frame formats: Clause 22 (classic, 32 registers
per PHY) and Clause 45 (extended, with a DEVAD address space, mandatory for
1000BASE-T1 and safety PHYs).
Electrical details (pull-up resistor, MDC timing, bus topology with multiple PHYs) are deliberately left out here — they are covered in depth in the Hardware Deep Dive, section "The MDIO Bus". This post assumes the bus works cleanly at the electrical level and focuses on what you read over it and how to interpret the values. |
A quick refresher on the frame format, since the field names keep coming up below:
Clause 22: ST | OP | PHYAD[4:0] | REGAD[4:0] | TA | DATA[15:0]
Clause 45: ST | OP | PRTAD[4:0] | DEVAD[5:0] | TA | ADDR/DATA[15:0]PHYAD/PRTAD addresses the chip on the bus, REGAD/DEVAD+address addresses the
register inside the chip. Those register addresses are exactly what this post is about.
PHY Register Map
Every IEEE-compliant PHY implements a set of standard registers that sit at the same address and carry the same meaning across vendors. On top of that, every vendor defines vendor-specific registers for diagnostic features that go beyond the IEEE standard (cable diagnostics, extended error counters, LED configuration).
Standard Registers (Clause 22, Registers 0–15)
| Address | Name | Purpose |
|---|---|---|
0 | Basic Control Register (BCR) | Reset, enable/restart autonegotiation, force speed/duplex, power-down |
1 | Basic Status Register (BSR) | Link status, autonegotiation-complete, supported capabilities |
2 | PHY Identifier 1 | Upper 16 bits of the Organizationally Unique Identifier (OUI) |
3 | PHY Identifier 2 | Lower OUI bits + model number + revision number |
4 | Auto-Negotiation Advertisement | Which modes this PHY offers to the link partner |
5 | Auto-Negotiation Link Partner Ability | Which modes the link partner offers (valid once negotiation has completed) |
6 | Auto-Negotiation Expansion | Additional negotiation info, e.g. whether the link partner supports autonegotiation at all |
9–10 | 1000BASE-T Control/Status (if supported) | Master/slave configuration, gigabit-specific capabilities |
Registers 2 and 3 (PHY Identifier) are the first thing to check in any MDIO diagnostic
session, even before looking at link status. If the PHY doesn’t respond to this read at
all ( |
Vendor-Specific Registers (Example Range)
Automotive PHYs such as the TJA1100/TJA1101 (NXP), DP83TC81x (TI), or 88Q2xxx (Marvell) typically populate registers starting at address 16 (0x10) with vendor-specific functions. Exact addresses differ between vendors — the datasheet is mandatory reading here.
| Address range | Typical content | Example |
|---|---|---|
0x10–0x12 | Extended status, interrupt mask/status | Enable a "link down" interrupt, read the cause from the status register |
0x13–0x17 | Cable diagnostics (TDR result) | Cable length, short/open detection, affected wire |
0x18–0x1F | Symbol error counters, SQI (Signal Quality Index) | Estimate the ongoing bit error rate during operation |
MDIO via AUTOSAR
Normally, application code does not access MDIO directly — and the function EthTrcv calls
for it doesn’t belong to EthTrcv either, it belongs to EthIf: EthIf_ReadMii/
EthIf_WriteMii (declared in EthIf.h). EthIf is only a broker here, not the executor:
per the spec (SWS_EthIf_00706/SWS_EthIf_00707), EthIf forwards the call internally to
<EthDrv>_ReadMii/<EthDrv>_WriteMii of the responsible Ethernet Controller Driver — in
practice Eth_ReadMii/Eth_WriteMii of the Eth module, which is what actually
toggles MDC/MDIO on the wire.
Std_ReturnType EthIf_ReadMii(
uint8 CtrlIdx,
uint8 TrcvIdx,
uint8 RegIdx,
uint16* RegValPtr
);
Std_ReturnType EthIf_WriteMii(
uint8 CtrlIdx,
uint8 TrcvIdx,
uint8 RegIdx,
uint16 RegVal
);CtrlIdx selects the Ethernet controller (MAC) that owns the MDIO bus in question,
TrcvIdx the transceiver on that bus, and RegIdx the register address per the PHY
register map. Both functions are explicitly synchronous per the spec
(Sync/Async: Synchronous) — they block until the MDIO transaction has completed and
return the result directly as Std_ReturnType; EthIf_ReadMii additionally returns the
register value read via RegValPtr.
Earlier AUTOSAR releases (up through R23-11) used an asynchronous callback pair,
|
For EthTrcv, that means a single blocking call per register access is enough. That’s
simpler than the earlier request/indication pattern, but it has a flip side —
EthTrcv_MainFunction must not block on an MDIO transaction long enough to starve the
driver’s other duties. How long a call actually blocks depends on the timing of the
underlying Eth driver and the MDC frequency.
Whether EthTrcv talks to Eth via MII at all is a configuration choice, not a given. AUTOSAR models this as a choice container in the ECU configuration:
EthTrcvMgmtInterface(a choice container underEthTrcvConfig) picks between two mutually exclusive access paths:EthTrcvMiiInterface— the transceiver hangs directly off an Ethernet controller’s MDIO bus. Configured viaEthTrcvCtrlIdx(which Eth controller instance performs the access) andEthTrcvMiiIdx(the transceiver index passed through toEthIf_ReadMii/EthIf_WriteMii).EthTrcvSwitchInterface— the transceiver sits behind an Ethernet switch instead. In that case there is no direct MDIO access by EthTrcv; register reads/writes instead go throughEthIf_SwitchPortReadTrcvRegister/EthIf_SwitchPortWriteTrcvRegister, which EthIf in turn forwards toEthSwt_ReadTrcvRegister/EthSwt_WriteTrcvRegisteron the switch driver — mirroring the MII path.
There is no EthTrcvReadMiiApi parameter — which access path is active is decided by
which of the two containers is present in the ARXML, not by a boolean flag.
Because the MDIO transaction ultimately runs through Eth, a badly timed |
MDIO Directly in Diagnostic Code
Not every debugging situation has access to the full AUTOSAR stack — during hardware bring-up there is often no RTE running yet, or you’re debugging on an evaluation board without AUTOSAR at all. In that case, two paths remain:
Path 1 — bare-metal driver: a minimal MDIO driver that talks directly to the SoC’s MAC registers for MDIO transactions (most SoC MACs have their own MDIO controller register set, independent of the actual Ethernet data path).
// Pseudocode — SoC-specific, register names vary
MDIO_ADDR_REG = (PhyAddr << 5) | RegAddr;
MDIO_CTRL_REG = MDIO_OP_READ | MDIO_START;
while (!(MDIO_STATUS_REG & MDIO_BUSY_CLEAR)) { /* wait */ }
uint16_t value = MDIO_DATA_REG;Path 2 — external MDIO adapter (see the "Tooling" section below): if even the SoC cannot be trusted (e.g. because you want to distinguish a MAC fault from a PHY fault), an external USB MDIO adapter can bypass the target SoC entirely and access the MDIO bus directly.
Both paths are strictly bring-up/lab tools. In production software, MDIO access runs exclusively through the Eth/EthTrcv collaboration in the AUTOSAR stack — anything else undermines the timing and state guarantees that collaboration maintains internally. |
Key Diagnostic Registers
For day-to-day troubleshooting, a small, recurring set of registers is enough. The table below shows the standard bits in the Basic Status Register (address 1) that are relevant to link diagnostics:
| Bit | Name | Meaning |
|---|---|---|
15 | 100BASE-T4 | Capability flag, mostly irrelevant on automotive PHYs (0) |
5 | Auto-Negotiation Complete | 1 = negotiation finished; if 0, the link may be stuck in a negotiation loop |
4 | Remote Fault | Link partner is reporting a fault — the cause is on the far side |
3 | Auto-Negotiation Ability | PHY supports autonegotiation (static capability bit) |
2 | Link Status | 1 = link up. Latched-low: once it drops to 0, it stays 0 until the next read — read it twice to get the current state! |
1 | Jabber Detect | PHY has detected an excessively long transmission from the link partner |
0 | Extended Capability | PHY supports extended registers beyond 0–5 |
Bit 2 (Link Status) is latched-low — this is one of the most common pitfalls in MDIO-based diagnostics. A single read right after a brief link flap will falsely report "link down", even though the link is long back up by the time you read it. The standard workaround: read it twice in a row. The first read returns the latched (possibly stale) state, the second read returns the actual current state. |
There are no dedicated bits for speed and duplex in the Basic Status Register — those live either in the Auto-Negotiation Link Partner Ability register (5) combined with the Advertisement register (4), or directly in a vendor status register that many PHYs provide as a convenient shortcut (e.g. "auto-negotiated speed/duplex" in a single register instead of deriving it from two capability registers).
| Diagnostic goal | Where to look |
|---|---|
Link up/down (current) | Basic Status Register bit 2, read twice |
Autonegotiation complete? | Basic Status Register bit 5 |
Negotiated speed/duplex | Vendor status register (vendor-specific) or derive from Reg. 4 ∩ Reg. 5 |
Error flags (CRC, symbol errors) | Vendor-specific error counter register, usually read-clear-on-read |
Cable diagnostics result (length, fault location) | Vendor-specific TDR register, must be started via command first |
Vendor Example: NXP TJA1100
The TJA1100 is one of the most widely used 100BASE-T1 PHYs in automotive designs and makes a good concrete example, since its register set is well documented and representative of the class of 100BASE-T1 automotive PHYs.
| Register (address) | Name | Diagnostic information |
|---|---|---|
0x00 | MII_BASIC_CONTROL | Standard register, reset/power-down/loopback control |
0x01 | MII_BASIC_STATUS | Standard register, link status (latched-low as described above) |
0x11 | EXT_CTRL_STATUS | Configuration flags: master/slave role, wake-request configuration |
0x12 | PHY_CONTROL | Operating mode (normal / silent / loopback sub-variants) |
0x13 | PHY_STATUS | Summarized state: link status, local/remote wake request, power mode |
0x14 | PHY_INTERRUPT_STATUS | Interrupt causes: link status change, thermal fault, undervoltage |
0x15 | PHY_INTERRUPT_ENABLE | Masking which interrupt sources drive the INT_N pin |
0x18 | COMMUNICATION_STATUS | Internal state machine’s link state ( |
0x1A | CABLE_TEST | Starts cable diagnostics and returns the result (short/open/length estimate) |
0x1B | SYMBOL_ERROR_COUNTER | Counts faulty PAM3 symbols since the last read (read-clear) |
On the TJA1100, |
Tooling
For MDIO diagnostics outside the target SoC, a few established tool classes exist:
External MDIO USB adapters — e.g. based on FTDI bit-bang modes or dedicated MDIO master chips. They allow register reads/writes from a PC, independent of the target SoC. Useful for ruling out target software (MAC driver, EthTrcv init ordering) as the root cause.
Diagnostic scripts — Python scripts talking to the adapter over pyserial/USB-HID that query a PHY’s register map and format it into something readable. In practice, a small script that automatically dumps PHY ID, link status (read twice), autonegotiation result, and error counters on startup is worth building early — it’s the "first minute" of every link diagnosis.
Protocol/bus analyzers — logic analyzers with an MDIO/MDC decoder plugin capture the complete bus traffic at the signal level. Necessary when a timing issue is suspected (see setup/hold times in the hardware deep dive) rather than incorrect register values.
Vendor tools — many PHY vendors ship their own GUI tools (e.g. the NXP TJA110x Config Tool) that present registers over a reference adapter with bitfield labels included. Handy for one-off analysis, less suited to automated regression testing.
A diagnostic script that translates raw register values into human-readable statements ("Link: UP, Speed: 100 Mbit/s, Duplex: Full, last autonegotiation succeeded 3 cycles ago") saves more time in practice than any single improvement to the hardware diagnostics itself. It’s worth building this script early in a project and maintaining it throughout. |
Case Study — Link Stays DOWN
Starting point: an ECU with a TJA1100 PHY consistently reports
ETHTRCV_LINK_STATE_DOWN via EthTrcv_GetLinkState. The cable is visibly plugged in
correctly, and the far side (switch port) shows no link either. Step-by-step diagnosis
via MDIO registers:
Step 1 — Does the PHY respond at all?
Read register 0x02 (PHY Identifier 1) → 0x0180
Read register 0x03 (PHY Identifier 2) → 0x0231Both values match the known TJA1100 OUI. The PHY responds — so the MDIO bus, pull-up,
and PHY address are fine. Had this returned 0xFFFF, the fault would be at the bus
level (see the hardware deep dive), not in the link itself.
Step 2 — Check the Basic Control Register: is the PHY in power-down or reset?
Read register 0x00 (Basic Control) → 0x0800
Bit 11 (Power Down) = 1Found it: bit 11 is set, the PHY is in power-down mode. That fully explains the missing link — a PHY in power-down drives no signal onto the line.
Step 3 — Narrow down the cause of the power-down
Read register 0x11 (EXT_CTRL_STATUS) → 0x0004
Bit 2 = CONFIG_EN — configuration pins were last evaluatedA look at the TJA1100 datasheet reveals: bit 11 in the Basic Control Register gets set
automatically at power-on depending on the state of the CONFIG pin. In this case, a
pull resistor on the evaluation board’s CONFIG pin was populated incorrectly — a board
assembly variant intended for a different power-on behavior.
Step 4 — Confirm with a software workaround: clear power-down
Write register 0x00 → 0x1200 // clear bit 11, set bit 12 (Autonegotiation Enable)
Read register 0x01 (Basic Status) → 0x0004 // first read: stale latched state
Read register 0x01 (Basic Status) → 0x0024 // second read: bit 2 = 1 → link up!After clearing power-down and restarting autonegotiation, the link actually comes up.
Root cause confirmed: a hardware assembly fault on the CONFIG pin, not a software or
cable fault.
Step 5 — Verify the negotiated mode
Read register 0x18 (COMMUNICATION_STATUS) → 0x0002
LINK_STATUS field = ACTIVEThe TJA1100’s internal state machine confirms ACTIVE — diagnosis complete. The actual
fix happens at the hardware level (assembly correction in the next board revision); until
then, the MDIO write from step 4 serves as a bench workaround, not a production fix.
This case study happened to have a purely hardware root cause — but the path to finding it went entirely through software-readable registers. That is the real value of MDIO register diagnostics: it reliably separates "PHY doesn’t respond" (bus level), "PHY responds but is in the wrong state" (configuration/hardware straps), and "PHY is in the right state but the link still won’t come up" (cable/far side). |
Summary
| Topic | Key takeaway for software developers |
|---|---|
Standard registers (0–6) | Read the PHY ID first — a response (or lack of one) separates bus faults from link faults |
Link status bit | Latched-low — always read it twice to get the current state |
Vendor registers | The datasheet is mandatory; often more informative than standard registers (e.g. |
AUTOSAR API | EthTrcv synchronously calls |
Bare-metal access | Bring-up/lab only — in production software everything runs through the Eth/EthTrcv collaboration |
Case study | A simple register path (PHY ID → Basic Control → vendor status) narrows down most link-down causes within minutes |
Next in the EthTrcv series: EthTrcv & IEEE 802.1AS — gPTP and Hardware Timestamping in the PHY