AUTOSAR Classic — MCAL, BSW & RTE¶
Every S32K driver you have written so far — FlexCAN, LPUART, FlexTimer — was a hand-rolled register wrapper. That works for one project. It stops working the moment a Tier-1 supplier has to reuse the same application logic across three microcontroller vendors, or a second team has to integrate your CAN driver without reading your header files. AUTOSAR Classic Platform is the industry's answer: a layered software architecture, standardized down to the C function signature, so that a body-control application written against the AUTOSAR Com stack runs unchanged whether the MCU underneath is an S32K, a TriCore, or a Renesas RH850. NXP ships an AUTOSAR-compliant Real-Time Drivers (RTD) package for S32K3, and even on S32K1 projects that don't run full AUTOSAR, the layering vocabulary — MCAL, ECU abstraction, BSW, RTE, SWC — is how every OEM statement of work describes the software.
The layers¶
┌─────────────────────────────────────────────┐
│ Application Layer (SWCs) │ Body control, lighting, doors...
├─────────────────────────────────────────────┤
│ RTE (Runtime Environment) │ Generated glue code
├──────────────────┬────────────────────────────┤
│ Services (BSW) │ ECU Abstraction │ NvM, Dcm, ComM | CanIf, PduR
├──────────────────┴────────────────────────────┤
│ MCAL (Microcontroller Abstraction Layer) │ Can, Port, Dio, Mcu, Fee
├─────────────────────────────────────────────┤
│ Hardware (S32K MCU: FlexCAN, PORT, PCC...) │
└─────────────────────────────────────────────┘
MCAL is the only layer that touches registers. It exposes vendor-
standard APIs — Can_Init(), Port_SetPinMode(), Dio_WriteChannel() —
that hide FLEXCAN0->MCR and PORTC->PCR[6] entirely. Above it, ECU
Abstraction modules like CanIf decouple the application from which
CAN controller instance or channel a message rides on. BSW Services
(NvM non-volatile memory manager, Dcm diagnostic communication
manager, ComM communication manager) provide OS-and-hardware-independent
functionality. The RTE is generated code, not hand-written: it wires
each Software Component's (SWC) ports to the BSW and to other SWCs,
so an SWC calls Rte_Write_DoorLockStatus(LOCKED) without knowing whether
that value crosses a CAN bus, a local variable, or another core entirely.
A minimal SWC in C¶
AUTOSAR SWCs are described in ARXML and code-generated, but the shape of the generated interface is fixed and worth internalizing directly in C:
/* Rte_DoorControl.h — generated RTE header (illustrative) */
typedef enum {
DOOR_LOCKED = 0x00u,
DOOR_UNLOCKED = 0x01u
} DoorLockStatus_t;
/* Sender-receiver port: this SWC reads a value written elsewhere */
Std_ReturnType Rte_Read_DoorControl_RPort_VehicleSpeed(uint16 *speed_kph);
/* Sender-receiver port: this SWC publishes a value */
Std_ReturnType Rte_Write_DoorControl_PPort_DoorLockStatus(DoorLockStatus_t status);
/* Client-server port: this SWC calls a service another SWC provides */
Std_ReturnType Rte_Call_DoorControl_RPort_Nvm_WriteBlock(uint16 blockId);
/* DoorControl.c — the runnable, scheduled by the RTE per its ARXML period */
void DoorControl_MainFunction_10ms(void)
{
uint16 speed_kph = 0u;
DoorLockStatus_t desired;
/* Std_ReturnType: E_OK, E_NOT_OK, or a module-specific NvM/Com code */
if (Rte_Read_DoorControl_RPort_VehicleSpeed(&speed_kph) != E_OK) {
return; /* stale/unavailable signal — do not act on garbage */
}
/* Auto-lock above 15 km/h is a real OEM body-control requirement */
desired = (speed_kph > 15u) ? DOOR_LOCKED : DOOR_UNLOCKED;
(void)Rte_Write_DoorControl_PPort_DoorLockStatus(desired);
}
Note the pattern: every RTE call returns Std_ReturnType and every
caller checks it. A sender-receiver read can legitimately return
RTE_E_INVALID if the upstream SWC hasn't run yet this cycle — treating
that as "speed is 0" instead of "signal unavailable" is a defect class
unique to layered architectures, because the failure is silent at the C
level and only visible in the ARXML timing configuration.
MCAL under RTD: a real S32K3 pattern¶
Below the RTE, RTD generates configuration structures rather than magic numbers — this is what changes when you move from bare-metal SDK code (Level 1/2) to an AUTOSAR MCAL:
/* Generated by S32 Configuration Tools from your .mex/ARXML config —
you do not hand-write Can_47_FlexCAN_Cfg, you generate it. */
extern const Can_ConfigType Can_Config_0;
void EcuM_Init(void)
{
Mcu_Init(&Mcu_Config_0); /* clocks, PLL, flash wait states */
Mcu_InitClock(McuClockSettingConfig_0);
Port_Init(&Port_Config_0); /* every pin mux, all at once */
Can_Init(&Can_Config_0); /* replaces FLEXCAN0->MCR writes */
Can_SetControllerMode(0, CAN_T_START);
CanIf_Init(&CanIf_Config); /* maps HRH/HTH handles to SWCs */
}
The MCAL API is standardized across NXP, Infineon, STMicro, and every
other AUTOSAR-certified vendor — Can_Write() takes the same argument
shape everywhere. What differs per vendor is only the generated
Can_Config_0 content, produced by each vendor's configuration tool from
the same ARXML schema.
Automotive-MCU concerns¶
- The RTE hides timing, it does not remove it. A 10 ms runnable that
blocks on
Rte_Call_..._Nvm_WriteBlock()(which internally waits on a flash-erase-in-progress from module 6's Level 2 driver) will overrun its budget and the AUTOSAR OS will flag a protection-hook violation. Never call a potentially blocking BSW service from a runnable shorter than the service's worst-case latency; use the asynchronousNvM_WriteBlock()+ callback pattern instead of a synchronous wrapper. - MCAL modules are not reentrant by default.
Can_Write()on the same hardware object from two different cores (Level 4 territory) or from a runnable and an ISR simultaneously corrupts the mailbox descriptor unless the module'sMulticoreSupport/CAN_ENABLE_SECURITY_EVENTconfiguration explicitly enables locking. Read the MCAL's reentrancy column in its BSW module description before assuming thread safety. - ARXML is the real interface contract, not the C header. Two SWCs compiled against mismatched RTE generations (stale ARXML) can both build cleanly and still corrupt a signal at runtime because the generated struct layouts drifted. Regenerate the RTE for all SWCs any time a single one's port interface changes.
- Vendor-specific extensions leak through the "standard" API. NXP's
RTD adds S32K-specific driver features via
Can_47_FlexCAN_*extended APIs that sit alongside the standardCan_*API. Using them ties your application to NXP silicon even though the surrounding architecture claims portability — a real trade every project must decide on deliberately, not by accident.
Cheat sheet¶
| Layer | Owns | Example S32K module |
|---|---|---|
| Application (SWC) | Feature logic | DoorControl, LightingManager |
| RTE | Generated glue, all inter-SWC and SWC-BSW calls | Rte_Write_*, Rte_Read_*, Rte_Call_* |
| Services (BSW) | OS/HW-independent services | NvM, Dcm, ComM, Dem |
| ECU Abstraction | Decouple app from HW instance/channel | CanIf, PduR, IoHwAb |
| MCAL | Direct register access, standardized API | Can, Port, Dio, Mcu, Fee |
| Return type | Meaning | Where used |
|---|---|---|
E_OK / E_NOT_OK |
Generic success/failure | Every BSW/MCAL call |
Std_ReturnType |
AUTOSAR's uniform return type | RTE and BSW APIs |
RTE_E_INVALID |
Sender-receiver signal not yet written | Rte_Read_* checks |
| Config artifact | Generated by | Consumed by |
|---|---|---|
| ARXML | S32 Configuration Tools / DaVinci Configurator | RTE generator, MCAL generator |
Can_ConfigType |
Configuration tool, from ARXML | Can_Init() |
How It Actually Works¶
AUTOSAR Classic's layered architecture (Application → RTE → BSW → MCAL) maps onto real hardware boundaries more directly than it might appear from the API surface. The MCAL (Microcontroller Abstraction Layer) is the only software permitted to touch S32K peripheral registers directly — every Dio_WriteChannel() or Can_Write() call ultimately executes the exact same PORT-mux, GPIO-latch, or FlexCAN-message-buffer operations covered in earlier modules; AUTOSAR doesn't change what the silicon does, it standardizes the software contract sitting on top of it.
The RTE (Runtime Environment) generates code that turns AUTOSAR's "virtual function bus" (SWCs communicating via ports) into direct function calls or, for cross-core/cross-partition communication, actual shared-memory reads/writes guarded by the same MPU regions used for freedom-from-interference — this is why RTE-generated code looks deceptively simple: the complexity of guaranteeing memory safety between independently-developed software components is pushed down into the same MPU hardware region-checking mechanism the CPU already uses for fault detection, not invented fresh by AUTOSAR.
The OS layer (AUTOSAR OS, based on OSEK) schedules tasks using the same underlying Cortex-M4F hardware mechanisms as FreeRTOS — PendSV-equivalent context switching, NVIC priority levels mapped to OSEK task priorities, and SysTick or FTM-based time bases — but AUTOSAR OS additionally enforces static, compile-time-known worst-case execution ordering (fixed task-to-priority mapping, no dynamic priority changes) specifically because ISO 26262 timing analysis requires provable, not merely observed, worst-case behavior; this determinism requirement is what shapes AUTOSAR OS's stricter API compared to a general-purpose RTOS.
(Described from the AUTOSAR Classic Platform specifications and S32K reference manual; not measured on physical silicon in this course.)
Exercise¶
Take your Level 1 CAN-bus capstone node and re-express its architecture
in AUTOSAR terms without necessarily running a real AUTOSAR stack. (1)
Draw the layer diagram for your existing code: which functions are really
MCAL (direct register access), which are ECU abstraction (channel-to-
message mapping), and which are application logic. Most bare-metal code
mixes all three in one function — identify every place that happens.
(2) Refactor one such function into three: an MCAL-style function that
only touches FLEXCAN0, an abstraction function that maps a logical
signal name to a mailbox index, and an application function that contains
only decision logic and calls the other two. (3) Write the Std_ReturnType
contract for your new abstraction function and make every caller check
it — introduce a deliberate fault (call it before init) and confirm the
error propagates instead of silently reading zero. (4) If NXP S32 Design
Studio's AUTOSAR configuration tooling is available to you, generate a
trivial one-SWC RTD project for S32K3 and compare its generated
Can_Init() call graph against your hand-written equivalent.