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)

AddressNamePurpose

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 (0xFFFF on both registers), the problem is on the bus itself — not in the link. See the hardware deep dive on pull-ups and address conflicts.

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 rangeTypical contentExample

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, EthTrcv_ReadMiiIndication/EthTrcv_WriteMiiIndication, through which Eth reported the result back to EthTrcv after the fact. Both callbacks were removed from the standard as of R24-11 — the current spec’s (R25-11) change history explicitly lists them as "Deleted Specification Items in R24-11". EthIf_ReadMii/EthIf_WriteMii have been purely synchronous ever since; a separate callback is no longer needed. If you run into the Indication functions in an older project or training material, that’s an earlier release generation, not a current implementation bug.

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 under EthTrcvConfig) picks between two mutually exclusive access paths:

    • EthTrcvMiiInterface — the transceiver hangs directly off an Ethernet controller’s MDIO bus. Configured via EthTrcvCtrlIdx (which Eth controller instance performs the access) and EthTrcvMiiIdx (the transceiver index passed through to EthIf_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 through EthIf_SwitchPortReadTrcvRegister/EthIf_SwitchPortWriteTrcvRegister, which EthIf in turn forwards to EthSwt_ReadTrcvRegister/EthSwt_WriteTrcvRegister on 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 EthIf_WriteMii — whether triggered by EthTrcv’s own state machine or replayed by diagnostic tooling that reuses the same path — can drop a live link instantly, for example if bit 15 (Reset) or bit 12 (Autonegotiation Enable) in the Basic Control Register gets changed unexpectedly. In production software, write access to MDIO registers should sit behind an explicit diagnostic session (UDS 0x27/0x10 or similar), regardless of which module physically issues the write.

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:

BitNameMeaning

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 goalWhere 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)NameDiagnostic 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 (LINK_STATUS field: Init/WaitLink/Active)

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, COMMUNICATION_STATUS (0x18) is often more informative than the standard Basic Status Register, because it directly exposes the state of the internal link state machine (INIT, WAIT_LINK, ACTIVE) instead of just a latched bit. Anyone working with TJA1100 boards regularly should include this register in every diagnostic script.

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.

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) → 0x0231

Both 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) = 1

Found 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 evaluated

A 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 = ACTIVE

The 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

TopicKey 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. COMMUNICATION_STATUS)

AUTOSAR API

EthTrcv synchronously calls EthIf_ReadMii/EthIf_WriteMii; EthIf forwards internally to Eth_ReadMii/Eth_WriteMii — no Indication callback since R24-11

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