... | ... | @@ -233,52 +233,88 @@ There are two primary objectives for layering the code: |
|
|
|
|
|
The layers are:
|
|
|
|
|
|
* `rtos_`: RTOS abstraction
|
|
|
* `app_ `: Application module (micro-app)
|
|
|
* `io_ `: Input/Output layer
|
|
|
* `hal_ `: Hardware Abstraction Layer
|
|
|
* `hw_ `: Hardware Layer
|
|
|
* `sl_ `: Standalone library
|
|
|
* `app_ `: Application Layer
|
|
|
* `dev_ `: Device Communication Layer
|
|
|
* `ecu_ `: ECU and Complex IO Layer
|
|
|
* `mcu_ `: Microcontroller and Low Level IO Layer
|
|
|
* `perif_`: Microcontroller Peripheral and CPU Core Layer
|
|
|
* `os_ `: OS Abstraction Layer (including RTOS)
|
|
|
* `sl_ `: Standalone and Utility Library Layer
|
|
|
|
|
|
`sl` is a code module that can be compiled independently of others. It has no linker dependency on another module, such as `sl_crc32.c`. However, it is okay if it uses function callbacks.
|
|
|
<details>
|
|
|
<summary>
|
|
|
Click to expand
|
|
|
|
|
|
### More about our Architecture and Layers
|
|
|
|
|
|
</summary>
|
|
|
|
|
|
### System Architecture
|
|
|
![Platform_Architecture-System_Architecture](uploads/fc8e94079d6622247ab119f029f40e38/Platform_Architecture-System_Architecture.png)
|
|
|
|
|
|
### Software Architecture
|
|
|
![Platform_Architecture-Software_Architecture](uploads/428707811b400f06074d31fef8aaf5e0/Platform_Architecture-Software_Architecture.png)
|
|
|
|
|
|
### Microcontroller Peripheral and CPU Core Layer
|
|
|
Used to interface with peripherals and CPU cores. This gives the ability to control and access a peripheral, without having to directly access registers or peripheral memory. The interfaces for a given module provide abstraction to the specifics of the peripheral, allowing for the same interfaces to be used across multiple variations of the peripheral. Variations might include the number of channels or number of instances of that peripheral. A peripheral may be used in various microcontroller families, so having an interface layer to the peripheral allows for reuse across microcontrollers.
|
|
|
|
|
|
### Microcontroller and Low Level IO Layer
|
|
|
Used to interface with microcontrollers, allowing control of the micro’s pins, and operating states, without having to directly access the micro’s peripherals or CPU cores. Modules at this layer implement basic drivers that utilize a particular microcontroller’s peripherals and CPU core. This layer abstracts away the microcontroller, allowing for a common interface layer across different microcontrollers.
|
|
|
|
|
|
### ECU and Complex IO Layer
|
|
|
Provides access to interface with ECU board level HW. These interfaces abstract away the HW details, such as which particular circuits or ICs are on the board, and instead provide a common interface layer to control the functions these circuits provide. This allows higher level control of ECU pins, abstracting away the details of the circuits driving those pins. This allows for a common interface to control an ECU, regardless of which variant of ECU hardware is actually being operated on.
|
|
|
|
|
|
### Device Communication Layer
|
|
|
The device layer provides access to physical devices that may be connected and controlled by an ECU. The API for modules in this layer provides the access and control of these devices, abstracting away the ECU HW that is used to physically control and communicate with them. Therefore, the module using the API can be abstracted from the ECU and become portable to any ECU that has implemented the device layer module. This layer is also abstracting away the physical location of the device being controlled, such that the device could actually be connected via a communication bus and commanded remotely. Therefore, this layer acts as a communication abstraction layer to other devices and ECUs.
|
|
|
|
|
|
### Application Layer
|
|
|
Application layer is the highest level SW layer, where the high level system behaviors and features are implemented.
|
|
|
|
|
|
### OS Abstraction Layer (including RTOS)
|
|
|
The OS layer provides a common abstraction layer from the underlying operating system (OS). This allows the code to be portable to any ECU, regardless of the underlying OS, even a bare-metal system.
|
|
|
|
|
|
### Standalone and Utility Library Layer
|
|
|
Code modules in this layer are implemented as pure functions or utility macros, or define standard types. These modules have no internal state, do not have external dependencies, and do not have run tasks. They are mathematical libraries, conversion functions, or standardized type definitions. The only external dependencies allowed would be other standard utility library modules.
|
|
|
|
|
|
</details>
|
|
|
|
|
|
## Double Underscore
|
|
|
|
|
|
Double underscores are used to quickly distinguish what layer and module something belongs to. Please look at the diagram below and the explanations that follow afterwards.
|
|
|
|
|
|
```
|
|
|
io\
|
|
|
dev\
|
|
|
|
|
|
|
+-- http\
|
|
|
| |
|
|
|
| +-- get\
|
|
|
| | |
|
|
|
| | +-- io_http_get.h
|
|
|
| +-- io_http_codes.h
|
|
|
| | +-- dev_http_get.h
|
|
|
| +-- dev_http_codes.h
|
|
|
|
|
|
|
+-- stream
|
|
|
|
|
|
|
+-- io_stream_dispatcher.h
|
|
|
+-- io_stream.h
|
|
|
+-- dev_stream_dispatcher.h
|
|
|
+-- dev_stream.h
|
|
|
```
|
|
|
|
|
|
Observe the following:
|
|
|
|
|
|
* `io_http_get.h`
|
|
|
* The name before the underscore shall be io_http_get.
|
|
|
* Example: a function with the name 'foo' would be called `io_http_get__foo()`
|
|
|
* `dev_http_get.h`
|
|
|
* The name before the underscore shall be `dev_http_get`.
|
|
|
* Example: a function with the name 'foo' would be called `dev_http_get__foo()`
|
|
|
|
|
|
* `io_http_codes.h`
|
|
|
* This is a helper file for the top level io http module. Hence, API's in this file should be called with the folder name structure even though it does not match this file name.
|
|
|
* Example: `io_http__foo()` for a function and `io_http__bar_e` for an enumeration type
|
|
|
* `dev_http_codes.h`
|
|
|
* This is a helper file for the top level `dev_http` module. Hence, API's in this file should be called with the folder name structure even though it does not match this file name.
|
|
|
* Example: `dev_http__foo()` for a function and `dev_http__bar_e` for an enumeration type
|
|
|
|
|
|
* `io_stream.h`
|
|
|
* API's in this file should start with folder structure io/stream
|
|
|
* Example: A public function foo would be named `io_stream__foo()`
|
|
|
* `dev_stream.h`
|
|
|
* API's in this file should start with folder structure `dev/stream`
|
|
|
* Example: A public function foo would be named `dev_stream__foo()`
|
|
|
|
|
|
* `io_stream_dispatcher.h`
|
|
|
* This is a helper file for the io stream module and located at io/stream. Hence, API's in this file should still start with io_stream__.
|
|
|
* Example: `io_stream__dispatcher_foo()`
|
|
|
* `dev_stream_dispatcher.h`
|
|
|
* This is a helper file for the `dev_stream` module and located at `dev/stream`. Hence, API's in this file should still start with `dev_stream__`.
|
|
|
* Example: `dev_stream__dispatcher_foo()`
|
|
|
|
|
|
|
|
|
|
... | ... | @@ -694,7 +730,7 @@ typedef struct { |
|
|
* Shall have the word `private` in it
|
|
|
* This improves code readability because in a large code module, it is easier to differentiate the function invocation internal or external to the code module
|
|
|
* Examples:
|
|
|
* `void uds_client__private_func(void);`
|
|
|
* `void sl_uds_client__private_func(void);`
|
|
|
* `app_battery__private_update_soc(void)`
|
|
|
* **Callback Functions**
|
|
|
* Callback functions should always contain an argument as this allows the caller to provide context to the function. This allows a single callback function to be used for multiple cases.
|
... | ... | @@ -742,6 +778,9 @@ typedef struct { |
|
|
* Example: `app_battery__status_codes_e;`
|
|
|
* Start the first enum value at zero
|
|
|
* Prefer to use an `invalid` value for the first enum
|
|
|
* However, if the enum represents items of an iterable list, an `invalid` value should be added last (after `count`).
|
|
|
* Define a `count` value for iterable lists
|
|
|
* This can be used for sizing arrays of structs, where each element is identified by an enum.
|
|
|
* Put a comma at the last enum value
|
|
|
* Creates less *diff* during code review
|
|
|
* Allows you to move enums without a compiler error
|
... | ... | @@ -757,6 +796,18 @@ typedef enum { |
|
|
APP_BATTERY__STATUS_CODE_IDLE,
|
|
|
} app_battery__status_code_e;
|
|
|
```
|
|
|
|
|
|
Example of a list
|
|
|
```c
|
|
|
typedef enum {
|
|
|
MCU_PORTPIN__PIN_CAN0_RX,
|
|
|
MCU_PORTPIN__PIN_CAN0_TX,
|
|
|
MCU_PORTPIN__PIN_CAN_ID0,
|
|
|
MCU_PORTPIN__PIN_CAN_STB,
|
|
|
MCU_PORTPIN__PIN_WATCHDOG,
|
|
|
MCU_PORTPIN__PIN_COUNT
|
|
|
} mcu_portpin__pin_e;
|
|
|
```
|
|
|
## Structs
|
|
|
|
|
|
* Pattern: `<layer>_<module>__struct_purpose_s`
|
... | ... | @@ -1004,7 +1055,7 @@ void my_api(module_s *this_ptr, void *ptr) { |
|
|
|
|
|
Do not re-use variables, and avoid changing the variables passed as copy. You can opt to make the parameters `const` to enforce this.
|
|
|
|
|
|
```
|
|
|
```c
|
|
|
sl_buffer_s sl_buffer__init(void *static_memory, size_t memory_size_bytes) {
|
|
|
memory_size_bytes = (NULL == static_memory) ? 0U : memory_size_bytes;
|
|
|
}
|
... | ... | @@ -1030,10 +1081,10 @@ Initialize the members by name. Use colon for C++ (it doesn’t support C dot in |
|
|
|
|
|
Example
|
|
|
```c
|
|
|
const hal_spi__channel_config_s hal_spi__channel_configs[HAL_SPI__CHANNEL_COUNT] = {
|
|
|
[HAL_SPI__CHANNEL_MOTOR] = { .master = true, .timing = FALLING_EDGE},
|
|
|
[HAL_SPI__CHANNEL_HSD] = { .master = false, .timing = FALLING_EDGE},
|
|
|
[HAL_SPI__CHANNEL_SBC] = { .master = true, .timing = RISING_EDGE},
|
|
|
const ecu_spi__channel_config_s ecu_spi__channel_configs[ECU_SPI__CHANNEL_COUNT] = {
|
|
|
[ECU_SPI__CHANNEL_MOTOR] = { .master = true, .timing = FALLING_EDGE},
|
|
|
[ECU_SPI__CHANNEL_HSD] = { .master = false, .timing = FALLING_EDGE},
|
|
|
[ECU_SPI__CHANNEL_SBC] = { .master = true, .timing = RISING_EDGE},
|
|
|
};
|
|
|
```
|
|
|
|
... | ... | |