... | ... | @@ -700,6 +700,53 @@ Shown below are two ways to include a code module. We recommend going with Optio |
|
|
* If there are conflicts to file includes (customer `common.h` vs. our `common.h`) then we have plans to mitigate the problem
|
|
|
* Our code modules have layer names, and the chance of conflict are minimal .i.e: `sl_common.h`
|
|
|
* One time software integration offsets the cost of relative includes
|
|
|
|
|
|
## Module Design
|
|
|
|
|
|
* Some of the main things included in a great module design are: data hiding, full code testability and compile time configuration of the module (so there are less pointers and no dynamic memory allocations, etc).
|
|
|
* **One producer, many consumers**
|
|
|
* Only one module should be allowed to set (i.e. produce) a particular variable. For example, only the seats module should be allowed to set the value for the `seats_occupied` variable.
|
|
|
|
|
|
* **No public data**
|
|
|
* If the variable is required for some other behavior outside of the seats module, then it should only be accessed via get functions.
|
|
|
* **Use accessor functions to access data** that is internal to a module. Using functions instead of global variables helps improve trace-ability of where a variable is used, prevents accidental writes by other modules, and helps create stub functions when we are writing unit tests.
|
|
|
|
|
|
* **No global variables in the header file**
|
|
|
* You do not want to expose your data to anyone without an explicit API
|
|
|
* You cannot declare instances of variables in a header file because each file including it will have its own instance leading to linking error with multiple definitions of the same name
|
|
|
* Do not use `extern` in a header file
|
|
|
|
|
|
* **No inline functions**
|
|
|
* Inline functions cannot be mocked for unit testing. When mocking a typical function we replace a functions definition with our own definition (this is done by compiling with a mock.c file that replaces of the real one). However we keep the header file with the function declarations because the code we're testing relies on this. Because inline functions are both declared and defined in the header file we can't replace the already existing definition and therefore are unable to create mocks for inline functions that exist in header files.
|
|
|
* Inline functions are not picked up by Doxygen
|
|
|
* Inline is just a recommendation for some compilers
|
|
|
* Too many invocations to the inline function will bloat code, and actually make it run slower (due to cache misses)
|
|
|
|
|
|
* **Private struct for all working variables**
|
|
|
* All variables required by a module shall be in a private struct that is defined inside the C source file. This makes it easy to see at a glance all the working variables of a module.
|
|
|
* It makes the code easier to multi-thread in the future (because a pointer to the struct can be passed in to each of the functions)
|
|
|
* The struct should be named like `<layer>_<module>_data_s` and the name of the variable should be `<layer>_<module>_data`. It is important to specify the layer in the name, only because we have both `app_seats.c` and `io_seats.c` in the code base and the working variables for each should be differentiated.
|
|
|
* Example:
|
|
|
- In `app_seats.c`, it would be called `app_seats_data_s` and the name of the variable would be `app_seats_data`
|
|
|
- In `io_seats.c`, it would be called `io_seats_data_s` and the name of the variable would be `io_seats_data`
|
|
|
|
|
|
* **No access from lower layers to higher layers**
|
|
|
* Horizontal or downward code access only.
|
|
|
* Code in a particular layer can only call other code in that layer or in the layer that is immediately below it. So, for example, app_seats.c can call app_airbag.c because it is another app. It can also call dev_seats because dev is the layer immediately below app.
|
|
|
|
|
|
* **No static variables inside functions**
|
|
|
(Does not apply to static const)
|
|
|
* They are hard to test since they do not `tear down` correctly
|
|
|
* They create cross-thread race conditions as it is a single instance that may be accessed and manipulated across multiple tasks (or threads).
|
|
|
* Prefer to add any such memory within the main module `struct` instead
|
|
|
* If it is indeed a single `app` then declare it global in the source file rather than inside of a function
|
|
|
* Example:
|
|
|
```c
|
|
|
void func1(void) {
|
|
|
// NO STATIC VARIABLES INSIDE FUNCTIONS!
|
|
|
static uint8_t local = 0;
|
|
|
}
|
|
|
```
|
|
|
----
|
|
|
# Software Layering
|
|
|
|
... | ... | @@ -916,6 +963,8 @@ The order of includes is just like an ordinary header file, except that we inclu |
|
|
----
|
|
|
|
|
|
# Naming Convention
|
|
|
|
|
|
## General Rules
|
|
|
* **Snake Case**
|
|
|
|
|
|
* After some research, we decided to go with `snake_case` or the `underscore` convention, primarily due to the following reasons:
|
... | ... | @@ -940,7 +989,7 @@ The order of includes is just like an ordinary header file, except that we inclu |
|
|
* If there are private enums, typedefs, or functions, then also create the private module
|
|
|
* Example: `app_my_module.h`, `app_my_module.c`, `app_my_module_private.h`, `test_app_my_module.c`
|
|
|
|
|
|
* **Acronyms in lowercase**
|
|
|
* **All code, file names and folder names in lowercase**
|
|
|
- All acronyms shall be lowercase, just like all other names, whether they are part of functions, variables, or any other types
|
|
|
- The reason for this is to keep naming consistent with the file naming convention, which uses all lowercase
|
|
|
- Example: your module for Unified Diagnostic Services shall be abbreviated as `uds` and not `UDS`
|
... | ... | @@ -966,7 +1015,6 @@ The order of includes is just like an ordinary header file, except that we inclu |
|
|
- `app_battery__calculate_soc()`
|
|
|
- This allows for the reader of the code to be able to easily understand the action that the function is taking.
|
|
|
|
|
|
|
|
|
## Variable Names
|
|
|
* Use standard capitalization rules for measurement variables even if it breaks acronym and abbreviation naming rules. This is done to reduce confusion of someone reading the code and expecting certain standard capitalization for measurement variables (i.e. meter = m, watt = W, pascal = Pa).
|
|
|
- `speed_mph`
|
... | ... | @@ -1017,10 +1065,6 @@ typedef struct { |
|
|
} buffer_s;
|
|
|
```
|
|
|
|
|
|
----
|
|
|
|
|
|
# Standards for Constructs
|
|
|
|
|
|
## Types
|
|
|
|
|
|
We should be able to decipher the type looking at the name of the type. Therefore, follow the following naming convention for new types.
|
... | ... | @@ -1385,54 +1429,6 @@ static int wait_for_read(int socket, uint32_t timeout_ms) { |
|
|
}
|
|
|
|
|
|
```
|
|
|
----
|
|
|
|
|
|
# Module Design
|
|
|
|
|
|
* Some of the main things included in a great module design are: data hiding, full code testability and compile time configuration of the module (so there are less pointers and no dynamic memory allocations, etc).
|
|
|
* **One producer, many consumers**
|
|
|
* Only one module should be allowed to set (i.e. produce) a particular variable. For example, only the seats module should be allowed to set the value for the `seats_occupied` variable.
|
|
|
|
|
|
* **No public data**
|
|
|
* If the variable is required for some other behavior outside of the seats module, then it should only be accessed via get functions.
|
|
|
* **Use accessor functions to access data** that is internal to a module. Using functions instead of global variables helps improve trace-ability of where a variable is used, prevents accidental writes by other modules, and helps create stub functions when we are writing unit tests.
|
|
|
|
|
|
* **No global variables in the header file**
|
|
|
* You do not want to expose your data to anyone without an explicit API
|
|
|
* You cannot declare instances of variables in a header file because each file including it will have its own instance leading to linking error with multiple definitions of the same name
|
|
|
* Do not use `extern` in a header file
|
|
|
|
|
|
* **No inline functions**
|
|
|
* Inline functions cannot be mocked for unit testing. When mocking a typical function we replace a functions definition with our own definition (this is done by compiling with a mock.c file that replaces of the real one). However we keep the header file with the function declarations because the code we're testing relies on this. Because inline functions are both declared and defined in the header file we can't replace the already existing definition and therefore are unable to create mocks for inline functions that exist in header files.
|
|
|
* Inline functions are not picked up by Doxygen
|
|
|
* Inline is just a recommendation for some compilers
|
|
|
* Too many invocations to the inline function will bloat code, and actually make it run slower (due to cache misses)
|
|
|
|
|
|
* **Private struct for all working variables**
|
|
|
* All variables required by a module shall be in a private struct that is defined inside the C source file. This makes it easy to see at a glance all the working variables of a module.
|
|
|
* It makes the code easier to multi-thread in the future (because a pointer to the struct can be passed in to each of the functions)
|
|
|
* The struct should be named like `<layer>_<module>_data_s` and the name of the variable should be `<layer>_<module>_data`. It is important to specify the layer in the name, only because we have both `app_seats.c` and `io_seats.c` in the code base and the working variables for each should be differentiated.
|
|
|
* Example:
|
|
|
- In `app_seats.c`, it would be called `app_seats_data_s` and the name of the variable would be `app_seats_data`
|
|
|
- In `io_seats.c`, it would be called `io_seats_data_s` and the name of the variable would be `io_seats_data`
|
|
|
|
|
|
* **No access from lower layers to higher layers**
|
|
|
* Horizontal or downward code access only.
|
|
|
* Code in a particular layer can only call other code in that layer or in the layer that is immediately below it. So, for example, app_seats.c can call app_airbag.c because it is another app. It can also call dev_seats because dev is the layer immediately below app.
|
|
|
|
|
|
* **No static variables inside functions**
|
|
|
(Does not apply to static const)
|
|
|
* They are hard to test since they do not `tear down` correctly
|
|
|
* They create cross-thread race conditions as it is a single instance that may be accessed and manipulated across multiple tasks (or threads).
|
|
|
* Prefer to add any such memory within the main module `struct` instead
|
|
|
* If it is indeed a single `app` then declare it global in the source file rather than inside of a function
|
|
|
* Example:
|
|
|
```c
|
|
|
void func1(void) {
|
|
|
// NO STATIC VARIABLES INSIDE FUNCTIONS!
|
|
|
static uint8_t local = 0;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
----
|
|
|
|
... | ... | |