Hello! Welcome back to our course on C pointers.
In our last lesson, we built a robust inter-process communication system using shared memory and semaphores. We saw how the producer-consumer pattern, protected by synchronization primitives, allows for the safe exchange of data. This fundamental pattern is at the heart of many complex software systems, including the ones you work with every day.
Today, we will bridge that low-level concept to your professional domain. The learning outcome for this lesson is to analyze pointer usage patterns in AUTOSAR communication middleware. You'll see that the entire AUTOSAR communication stack, from the Application Layer down to the drivers, can be viewed as a highly structured, multi-stage producer-consumer system, where pointers are the primary mechanism for passing data between layers.
Given your extensive experience with AUTOSAR middleware, we won't dwell on the high-level concepts. Instead, we'll dive straight into the C-level implementation details, focusing on how pointers make it all work.
1. The Big Picture: Data Flow and the PduInfoType
At every boundary in the AUTOSAR communication stack, data is passed in the form of a Protocol Data Unit (PDU). The "vehicle" for carrying this data between layers is a standard structure defined by AUTOSAR: PduInfoType.
typedef struct {
uint8* SduDataPtr; // Pointer to the Service Data Unit (the payload)
PduLengthType SduLength; // Length of the payload
} PduInfoType;
This simple structure is central to our discussion. Notice its key member: SduDataPtr, a pointer to an array of bytes that contains the actual data payload. Almost every function in the communication stack, from Com to PduR to CanIf, will pass data by passing a pointer to a PduInfoType variable.

Let's trace the journey of a signal from an Application Software Component (SWC) all the way to the CAN bus, examining the pointer patterns at each step.
2. The "Write" Path: From Application to Driver
This section will follow the lifecycle of a signal being transmitted. We will see a recurring pattern: data is passed down the stack by passing pointers as function arguments.
The following resource provides a fantastic, concise summary of the function calls involved in transmitting a signal. We will use it as a reference throughout this section.
1/3) 1. Rte_Write_Xxx() Purpose: Used by the Application ...
This document, '1/3) 1. Rte_Write_Xxx() Purpose: Used by the Application ...', provides a clear, step-by-step trace of a signal transmission through the AUTOSAR stack, including the C function prototypes. It's a perfect guide for our analysis.
Read through the four main points in this section, from Rte_Write_Xxx() to CanIf_Transmit(). Pay close attention to the function prototypes and how pointers are used in each call, specifically const DataType* Data, const void* SignalDataPtr, and const PduInfoType* PduInfoPtr.
Let's break down that flow.
Step 1: Application SWC to RTE
- Function:
Std_ReturnType Rte_Write_P_Port_DE_Element(const DataType* Data); - Pointer Pattern: The application calls
Rte_Writeand passes the address of the variable it wants to send (e.g.,Rte_Write_SpeedPort_VehicleSpeed(&VehicleSpeed);). This is the classic "pass-by-reference" pattern using a pointer. The RTE can now access the application's data. constCorrectness: Theconstqualifier onconst DataType* Datais a contract. It tells the application, "The RTE promises not to modify your original data; it will only read it."
Step 2: RTE to COM
- Function:
Std_ReturnType Com_SendSignal(SignalIdType SignalId, const void* SignalDataPtr); - Pointer Pattern: The RTE, having received the data, now passes it to the Communication (COM) module. It again passes a pointer to the data.
void*for Genericity: Notice the use ofvoid*. The COM module is generic; it handles signals of all different data types (uint8, uint16, arrays, etc.). Avoid*allowsCom_SendSignalto accept a pointer to any kind of data. The COM module knows the true data type from its configuration (viaSignalId) and will perform the necessary type casting internally to handle the data correctly.
Step 3: COM to PDU Router (PduR)
- Function:
Std_ReturnType PduR_ComTransmit(PduIdType PduId, const PduInfoType* PduInfoPtr); - Pointer Pattern: This is a key step. The COM module has packed the signal (and others) into a PDU buffer. It populates a
PduInfoTypestruct, settingSduDataPtrto point to this buffer. It then callsPduR_ComTransmit, passing a pointer to thePduInfoTypestruct. - Multi-level Pointers: We now have a pointer (
PduInfoPtr) to a struct that itself contains a pointer (SduDataPtr). This is a very common and efficient pattern for passing compound data objects without copying them.
Step 4: PduR to CAN Interface (CanIf)
- Function:
Std_ReturnType CanIf_Transmit(PduIdType Hth, const PduInfoType* PduInfoPtr); - Pointer Pattern: The PduR's job is to route. Based on the
PduId, it determines that this PDU goes to the CAN bus and callsCanIf_Transmit. Critically, it just forwards the same pointer (PduInfoPtr) it received from COM. No data is copied. The PduR is simply redirecting the flow of information by redirecting the pointer.
From CanIf, the pointer is passed to the CAN Driver, which finally reads the data from the memory location pointed to by PduInfoPtr->SduDataPtr and transmits it over the hardware.
3. The "Read" Path and Function Pointers for Callbacks
The process for receiving data is largely the reverse, but it introduces another critical pointer pattern: function pointers for callbacks.
When a CAN frame arrives, the flow is roughly:
- CAN Driver -> CanIf: A hardware interrupt occurs. The driver reads the frame into a buffer.
- CanIf -> PduR:
CanIfdetermines thePduIdfrom the CAN ID and notifies the PduR by callingPduR_CanIfRxIndication(PduId, &pduInfo). - PduR -> COM: PduR routes the PDU to COM by calling
Com_RxIndication(PduId, &pduInfo). - COM -> RTE -> App SWC: COM unpacks the signals from the PDU buffer. The application can then read the value using
Rte_Read_Port_Element(&data).
The ...RxIndication functions are callbacks. The upper layer (e.g., PduR) provides the lower layer (e.g., CanIf) with a function to call when data is ready. In a typical AUTOSAR configuration, these connections are stored in configuration tables as function pointers.
// Simplified example of a CanIf configuration table
const CanIf_RxPduConfigType CanIf_RxPduConfig[] = {
// ... other PDUs
{
.PduId = 10,
.TargetPduId = 10,
.UserRxIndication = &PduR_CanIfRxIndication // Function pointer!
},
// ...
};
// Inside CanIf, when PDU with ID 10 is received:
PduInfoType receivedPdu = { .SduDataPtr = rxBuffer, .SduLength = 8 };
// Retrieve the function pointer from the config table and call it
CanIf_RxPduConfig[config_index].UserRxIndication(
CanIf_RxPduConfig[config_index].TargetPduId,
&receivedPdu
);
This callback mechanism, enabled by function pointers, is fundamental to the event-driven nature of the communication stack.
The following video provides a more detailed look at how the RTE manages the data exchange between SWCs and how these high-level interactions are translated into C code.
Deep Dive into AUTOSAR Run Time Environment
The video 'Deep Dive into AUTOSAR Run Time Environment' explains how the RTE facilitates communication, mapping AUTOSAR concepts to C code. This will help connect the Rte_Write/Rte_Read APIs to the underlying data exchange.
Watch the section from 23:33 to 29:10. Focus on the C code examples for Rte_Write and Rte_Read. Notice how the Write API passes the data to the RTE, and the Read API uses a pointer argument to return the data to the calling SWC.
Test your understanding!
You are debugging a new SWC. The SWC is supposed to read a uint32_t sensor value via Rte_Read_SensorPort_Value(), but the variable in your SWC is never updated and remains zero. A junior colleague wrote the call like this:
Rte_Read_SensorPort_Value(mySensorValue);
What is the likely error, and how would you correct it?
Show answer
The error is that the value of the variable mySensorValue is being passed to the function, not its memory address. This is pass-by-value. The Rte_Read function needs to know where to store the sensor data it has, so it requires a pointer to the variable.
The correction is to use the address-of operator (&) to pass a pointer to the variable:
Rte_Read_SensorPort_Value(&mySensorValue);
This is a classic "pass-by-reference" error. The Rte_Read function's prototype is Std_ReturnType Rte_Read_...(uint32_t* data), and it will write the sensor value to the memory location pointed to by data.
4. Pointers in Gatewaying
Your experience in gateway development makes this a particularly relevant area. From a C implementation perspective, PDU-level gatewaying is an exercise in pointer manipulation.
When the PduR receives a PDU on one bus (e.g., CAN) that needs to be transmitted on another (e.g., Automotive Ethernet), it performs these steps:
- The
PduR_CanIfRxIndicationfunction is called with aPduInfoType*pointing to the received CAN PDU data. - The PduR's routing logic determines that this PDU must be gatewayed.
- The PduR may need to acquire a new buffer for the outgoing Ethernet PDU, as frame formats and sizes can differ. It copies the data from the source buffer (via
SduDataPtr) to the destination buffer. - The PduR then calls the transmit function for the target bus, e.g.,
SoAd_IfTransmit(), with a newPduInfoType*pointing to the outgoing PDU data.
The video below explains the different routing paths within PduR, including gatewaying.
PDUR Module & Communication Gateway configuration in Autosar (AutoEConnect Sessions)
The video 'PDUR Module & Communication Gateway' explains how the PduR handles routing, including the distinction between API forwarding (up/down the stack) and Gateway routing (between different channels).
Watch from 16:06 to 22:20. Focus on the explanation of 'API forwarding' versus 'Gateway routing'. Visualize how in gatewaying, a PDU pointer received from one interface (like CanIf) is processed and then a new PDU pointer is sent to another interface (like Socket Adapter).
5. Adaptive AUTOSAR: Smart Pointers and Zero-Copy
The world is moving towards software-defined vehicles, and with it, Adaptive AUTOSAR (ara::com). Here, the C++ language allows for even more sophisticated pointer patterns to optimize communication, particularly using smart pointers to achieve zero-copy data transfer.
In ara::com, a service provider (Skeleton) can send an event using an "Allocate/Send" pattern:
Allocate(): The provider asks the middleware to allocate memory for the data sample by calling a function likemyEvent.Allocate(). This function returns aara::com::SampleAllocateePtr<SampleType>, which behaves like astd::unique_ptr. Crucially, the middleware can be smart and allocate this memory in a shared memory region accessible by both the provider and the consumer.- Fill Data: The provider uses this smart pointer to fill the allocated memory with data.
Send(): The provider callsmyEvent.Send(std::move(mySamplePtr)). Thestd::movetransfers ownership of the smart pointer (and the underlying memory) to the middleware. The provider can no longer access it. The middleware then notifies the consumer that data is available in the shared buffer.
This pattern minimizes data copies. Instead of copying data from the application's heap to a middleware buffer and then to a driver buffer, the data is written once into a shared buffer, and only lightweight pointers and notifications are passed around.
This is a more advanced topic, but it's the modern evolution of the same fundamental problems we've been discussing. The following resource explains this concept in detail.
The official 'Explanation of ara::com API' document describes the advanced communication patterns used in Adaptive AUTOSAR. We will focus on the parts that explain the zero-copy mechanism enabled by smart pointers.
Please read the following two sections: Section 6.3.5 Events (from page 78 to 81): Focus on the two Send variants and the Allocate method. Understand why the Allocate/Send pattern with SampleAllocateePtr is provided as an optimization. Section 9.1.1 Zero-Copy implications (page 101): This section explicitly discusses the goal of zero-copy, the role of shared memory, and how serialization needs can sometimes complicate this ideal.
Conclusion
Today we've peeled back the layers of AUTOSAR's communication middleware and found pointers at every turn. They are not just an implementation detail; they are fundamental to the architecture's efficiency and design.
Key Takeaways:
- Data Transport: The
PduInfoTypestruct, with itsSduDataPtrmember, is the standard vehicle for data. Pointers to this struct (PduInfoType*) are passed between all layers of the COM stack. - API Design: Functions like
Rte_WriteandCom_SendSignaluse pointers to data (const T*) for efficient pass-by-reference semantics, whileRte_Readuses pointers (T*) as "out" parameters. - Callbacks: The event-driven nature of the stack relies heavily on function pointers, which are used to register callback functions like
PduR_RxIndicationin configuration tables. - Gatewaying: PDU gatewaying in PduR is fundamentally a process of receiving a pointer to a data buffer from one bus interface and forwarding that data (via another pointer) to a different bus interface.
- Modern Patterns: Adaptive AUTOSAR (
ara::com) evolves these concepts by using C++ smart pointers (SampleAllocateePtr) to manage memory ownership and enable high-performance, zero-copy communication through shared memory.
Preview of the Next Lesson
Understanding these patterns is one thing; debugging them when they go wrong is another. Pointer-related bugs in an embedded, multi-process environment like an ECU can be notoriously difficult to track down.
In our next lesson, we will focus on practice: Debug pointer-related issues in an embedded context using a hardware debugger (e.g., Trace32). We'll discuss common issues like null pointer dereferences, buffer overflows, and corrupted data within PDU buffers, and how to use professional tools to diagnose them.