This post builds on EthTrcv Part 1 — Basics & Classification.
That post covered the role of EthTrcv in the AUTOSAR Ethernet architecture.
This one goes deeper: how does EthTrcv actually get from UNINIT to a state where frames flow —
and which API calls make that happen?
Anyone who has debugged an Ethernet link bring-up knows the pattern: EthTrcv_Init gets called,
everything looks plausible, and the link still doesn’t come up. Narrowing down cases like that
requires knowing which state should be reached when — and which function is responsible for it.
The EthTrcv State Machine
Many developers conflate two concepts that the AUTOSAR specification deliberately keeps apart: the driver’s initialization status and the transceiver’s requested operating mode. That distinction is actually the more interesting part of the story — the two live in different types with different purposes, and not knowing the split makes it easy to chase the wrong identifier while debugging.
Initialization status: EthTrcv_StateType
EthTrcv_StateType (SWS_EthTrcv_00101) explicitly does not model an operating state,
according to the spec — it is plain "status supervision used for Development Error Detection",
meant for debugging, with exactly two values:
EthTrcv_Init()
┌────────────────────────┐ ──────────────────► ┌─────────────────────┐
│ ETHTRCV_STATE_UNINIT │ │ ETHTRCV_STATE_INIT │
│ (0x00) │ │ (0x01) │
└────────────────────────┘ └─────────────────────┘| Value | Meaning |
|---|---|
| "Driver is not yet configured" — initial state after power-on or reset. |
| "Driver is configured" — |
That’s the whole enum — there is no ACTIVE, DOWN, or SLEEP value. The UNINIT → INIT
transition happens exactly once per power cycle and, per the SWS, must not be repeated without
first returning to UNINIT (e.g. via a reset). Calling EthTrcv_Init a second time during
normal operation is a configuration error and most implementations flag it via a DET report,
provided EthTrcvDevErrorDetect is enabled.
Operating mode: Eth_ModeType
What most people mean by "active" or "down" is not a state of the EthTrcv driver in the strict
sense at all — it is the transceiver’s requested mode, set via
EthTrcv_SetTransceiverMode(TrcvIdx, TrcvMode). TrcvMode is of type Eth_ModeType, the same
type used by the Eth module (defined in Eth_GeneralTypes.h, not as an EthTrcv-specific type).
There are three literals:
EthTrcv_SetTransceiverMode(TrcvIdx, TrcvMode) // TrcvMode : Eth_ModeType
┌──────────────────┐ ETH_MODE_ACTIVE ┌────────────────────┐
│ ETH_MODE_DOWN │ ────────────────────►│ ETH_MODE_ACTIVE │
└──────────────────┘ ◄────────────────────└──────────┬─────────┘
ETH_MODE_DOWN │ ETH_MODE_ACTIVE_WITH_WAKEUP_REQUEST
▼
┌───────────────────────────────────────┐
│ ETH_MODE_ACTIVE_WITH_WAKEUP_REQUEST │
└───────────────────────────────────────┘| Literal | Meaning |
|---|---|
| Transceiver is disabled. |
| Transceiver is enabled — per the SWS, this also covers a passive wake-up. |
| Transceiver active plus an active wake-up request. Per SWS_EthTrcv_00049,
|
There is no |
The Initialization Sequence in Detail
EthTrcv does not initialize itself. The call comes from outside — specifically from the init callback that the EcuM invokes as part of post-build loop initialization. The order in which this happens is not arbitrary; it is dictated by dependencies between the modules.
EcuM_Init()
│
├─► EcuM_AL_DriverInitZero() ── basic drivers (Port, Dio, ...)
│
└─► EcuM_AL_DriverInitOne() ── EthTrcv, Eth, EthIf, CanIf, ...
│
├─► EthTrcv_Init(EthTrcv_ConfigType* config)
│ └─ PHY hardware reset, MDIO baseline configuration,
│ register programming per ARXML
│
├─► Eth_Init(Eth_ConfigType* config)
│ └─ configure MAC controller, set up DMA descriptors
│
└─► EthIf_Init(EthIf_ConfigType* config)
└─ logically links EthTrcv and Eth instances,
provides the unified API for upper layers (TcpIp, ...)The order EthTrcv before Eth before EthIf is not a coincidence. |
Inside EthTrcv_Init itself, the following steps typically run:
1. Validate configuration pointer (NULL check, DET if enabled)
2. Set internal state variables to defaults
3. Trigger PHY hardware reset (RST_N pin or soft reset via MDIO)
4. Wait for PHY boot time (vendor-specific, see datasheet)
5. Read PHY identification (PHY ID register via MDIO) and check against configuration
6. Program static PHY registers per ARXML configuration
(auto-negotiation advertisement, interface mode, delays, ...)
7. Internal state: UNINIT → INIT
8. Return E_OK (or E_NOT_OK on failure, state remains UNINIT)Step 5 — the PHY ID check — is easy to overlook in practice, but it is one of the cheapest error detectors available: if the PHY ID read via MDIO does not match the expected one, you are looking at an assembly error, an MDIO address problem, or a power-sequencing fault — long before you’d otherwise end up debugging the link itself. |
Key API Functions
EthTrcv_Init — Pass Configuration, Reset the PHY
FUNC(void, ETHTRCV_CODE) EthTrcv_Init(
P2CONST(EthTrcv_ConfigType, AUTOMATIC, ETHTRCV_APPL_CONST) CfgPtr
);EthTrcv_Init receives a pointer to the generated configuration structure — in the post-build
case, this pointer comes directly from the ARXML code generator; in the pre-compile case it is
usually NULL_PTR and configuration is referenced through compiled constants. Per the SWS the
function has no return value; errors go through DEM instead: per SWS_EthTrcv_00040, if the
access check to the transceiver fails, the function reports the production error
ETHTRCV_E_ACCESS, not via a Std_ReturnType.
This is exactly where a lot of integrations trip up: anyone expecting |
EthTrcv_SetTransceiverMode — Activate or Shut Down the Transceiver
FUNC(Std_ReturnType, ETHTRCV_CODE) EthTrcv_SetTransceiverMode(
uint8 TrcvIdx,
Eth_ModeType TrcvMode
);Controls the transition between ETH_MODE_DOWN, ETH_MODE_ACTIVE, and
ETH_MODE_ACTIVE_WITH_WAKEUP_REQUEST. TrcvIdx addresses the specific transceiver instance
(relevant for multi-PHY configurations). Return values:
| Return value | Meaning |
|---|---|
| Mode change was triggered (not necessarily completed yet — auto-negotiation takes time). |
| Mode change not possible, e.g. because the transceiver is still in |
|
EthTrcv_GetLinkState — Query the Link Status
FUNC(Std_ReturnType, ETHTRCV_CODE) EthTrcv_GetLinkState(
uint8 TrcvIdx,
P2VAR(EthTrcv_LinkStateType, AUTOMATIC, ETHTRCV_APPL_DATA) LinkStatePtr
);Returns ETHTRCV_LINK_STATE_ACTIVE or ETHTRCV_LINK_STATE_DOWN via the output parameter.
There are two fundamental strategies for determining the link state:
| Strategy | How it works | Trade-off |
|---|---|---|
Polling |
| Simple, deterministic, but detection latency for a link change equals the polling interval. |
Interrupt-driven | PHY signals link changes via a dedicated interrupt pin; the ISR sets a flag evaluated in task context, which triggers | Low latency, but requires an extra hardware pin and ISR handling; not every PHY supports it. |
Many production projects combine both approaches: an interrupt for fast reaction to link loss (safety-relevant, e.g. on backbone connections), plus polling as a fallback in case the interrupt path misses an event for some reason. |
EthTrcv_GetTransceiverMode — Query the Current Mode
FUNC(Std_ReturnType, ETHTRCV_CODE) EthTrcv_GetTransceiverMode(
uint8 TrcvIdx,
P2VAR(Eth_ModeType, AUTOMATIC, ETHTRCV_APPL_DATA) TrcvModePtr
);The counterpart to EthTrcv_SetTransceiverMode — reads back the most recently set (not
necessarily the physically already-reached) mode. Useful for diagnostics and for upper layers
that want to check whether a mode change is even necessary before calling SetTransceiverMode again.
Error Handling
EthTrcv distinguishes between two error categories that are handled differently:
| Category | Example | Reporting mechanism |
|---|---|---|
Development error | NULL pointer as configuration, invalid | DET ( |
Runtime / production error | PHY stops responding on MDIO, link drops unexpectedly, PHY over-temperature (if supported) | DEM ( |
A typical DEM-relevant failure case is a PHY failure during operation: a previously successfully initialized PHY suddenly stops responding to MDIO accesses — for example due to a power glitch, an ESD event, or a hardware defect.
Cyclic task
│
├─► EthTrcv_GetLinkState() → E_NOT_OK (MDIO timeout)
│
├─► Increment internal retry counter
│
├─► Threshold exceeded?
│ │
│ ├─ No → keep polling
│ │
│ └─ Yes → Dem_SetEventStatus(ETHTRCV_E_ACCESS, DEM_EVENT_STATUS_FAILED)
│ → diagnostic event set, DTC potentially maturing
│ → EthIf notified of link loss via callback
│
└─► EthTrcv_GetLinkState() back to E_OK?
└─ Yes → Dem_SetEventStatus(..., DEM_EVENT_STATUS_PASSED)A single failed MDIO access is not by itself grounds for a DEM event — MDIO bus glitches caused by EMC can happen occasionally. Common implementations use debounce counters (several consecutive failed attempts) before setting a DTC, to avoid flickering diagnostic entries. |
Code Example — Init and Link-Check Flow
The following example shows a realistic, simplified flow for how an application task monitors link state and reacts to loss after EcuM has already run the init sequence.
#define ETHTRCV_IDX_MAIN 0u
#define LINK_LOSS_THRESHOLD 5u
static uint8 linkLossCounter = 0u;
static boolean linkWasUp = FALSE;
/* Called cyclically from a 10ms task, after EcuM has already
* run the init sequence (EthTrcv_Init -> Eth_Init -> EthIf_Init). */
void EthLink_MainFunction(void)
{
Std_ReturnType retVal;
EthTrcv_LinkStateType linkState;
/* Activate the transceiver if not already done */
Eth_ModeType currentMode;
retVal = EthTrcv_GetTransceiverMode(ETHTRCV_IDX_MAIN, ¤tMode);
if ((retVal == E_OK) && (currentMode != ETH_MODE_ACTIVE))
{
(void)EthTrcv_SetTransceiverMode(ETHTRCV_IDX_MAIN, ETH_MODE_ACTIVE);
/* Return value deliberately not treated as fatal here --
* activation will be retried next cycle if it failed. */
}
/* Query link state */
retVal = EthTrcv_GetLinkState(ETHTRCV_IDX_MAIN, &linkState);
if (retVal != E_OK)
{
/* MDIO access failed - PHY possibly unreachable */
if (linkLossCounter < LINK_LOSS_THRESHOLD)
{
linkLossCounter++;
}
}
else if (linkState == ETHTRCV_LINK_STATE_ACTIVE)
{
linkLossCounter = 0u;
if (!linkWasUp)
{
linkWasUp = TRUE;
Dem_SetEventStatus(ETHTRCV_E_ACCESS, DEM_EVENT_STATUS_PASSED);
/* Application-side reaction to link-up, e.g. notify
* the TcpIp stack via an EthIf callback. */
}
}
else /* ETHTRCV_LINK_STATE_DOWN */
{
if (linkLossCounter < LINK_LOSS_THRESHOLD)
{
linkLossCounter++;
}
}
if (linkLossCounter >= LINK_LOSS_THRESHOLD)
{
if (linkWasUp)
{
linkWasUp = FALSE;
Dem_SetEventStatus(ETHTRCV_E_ACCESS, DEM_EVENT_STATUS_FAILED);
}
}
}This example is deliberately simplified — in a production application this logic usually lives
inside |
Series
| Part | Topic | Status |
|---|---|---|
1 | Done | |
2 | API & Initialization Sequence | This post |
3 | Coming up | |
4 | Coming up |
Summary
| Topic | Key takeaway for software developers |
|---|---|
State machine |
|
Init order | EcuM calls |
| No |
| Only enables the transmission path, does not guarantee a physical link |
| Polling or interrupt-driven; production projects often combine both |
Error handling | Development errors → DET (usually disabled in production builds); runtime errors → DEM with debounce before the DTC |
Next in the EthTrcv series: EthTrcv Part 3 — ARXML Configuration in Practice