README.md
should be stored in each component folder.
See below for programming guidelines for this repository.
Name | Designator | Path | Comments |
---|---|---|---|
Steering Wheel | stw | components/steering_wheel/ | Steering Wheel Controller |
BMS Boss | bmsb | components/bms_boss/ | Battery Management System Boss Controller |
BMS Worker | bmsw | components/bms_worker/ | Battery Management System Boss Controller |
Bootloader | bl | components/bootloader/ | Embedded Bootloaders for all Controllers |
Heartbeat | heartbeat | components/heartbeat/ | Generic Heartbeat Application for all Controllers |
Front Vehicle Controller | vcfront | components/vc/front | Vehicle Controller code specific to the Front Controller |
Power Distribution Unit | vcpdu | components/vc/pdu | Vehicle Controller code specific to the Power Distribution Unit |
Rear Vehicle Controller | vcrear | components/vc/rear | Vehicle Controller code specific to the Rear Controller |
Platform | Designator | Comments |
---|---|---|
CFR24 | cfr24 | Competition car for 2024 |
CFR25 | cfr25 | Competition car for 2025 |
- Each component is broken into 3 main code sources
- Source Code
- The source code is typically located in
./components/$COMPONENT_DESIGNATOR/
for each component - Hardware sources to be included
./components/$DESIGNATOR/HW/mcuConfig.yaml
unless multiple platforms are supported - The source code, while it varies between component, has these typical areas:
./components/$COMPONENT_DESIGNATOR/build/
- Contains all object files, binary files, etc...
./components/$COMPONENT_DESIGNATOR/generated/
- Contains code generated by scripts and/or programs which is used by the embedded system
./components/$COMPONENT_DESIGNATOR/HW/
- Contains all firmware source code and header files which interfaces between System Calls and the Hardware
./components/$COMPONENT_DESIGNATOR/include/
- Contains all System and Application header files
./components/$COMPONENT_DESIGNATOR/lib/
- Contains any library source code and header files which the System uses
./components/$COMPONENT_DESIGNATOR/RTOS
-Contains all source code and header files relating to the implementation of the System's RTOS./components/$COMPONENT_DESIGNATOR/src
- Contains all System and Application level source code
./components/$COMPONENT_DESIGNATOR/SConscript
- Is called by
./SConstruct
to run the component build script
- Is called by
./components/shared/
: Team libraries, configurations, drivers, firmware, and other...
- The source code is typically located in
- Embedded System
- Contains libraries, openocd configurations, and platform code accessible to all devices located in
./embedded/
- Select important sections:
./embedded/libs/CMSIS/
: Controller independant system level library- Used for RTOS and other controller independant systems
./embedded/platforms/
: Supported Hardware and MCU platforms- Contains the HAL/LL, startup code, and link script for the STM32 family of devices in
./embedded/platforms/stm32/
- Contains the HAL/LL, startup code, and link script for the STM32 family of devices in
- Contains libraries, openocd configurations, and platform code accessible to all devices located in
- Chip Configuration
- The chip configuration is stored in YAML files in
./site_scons/chips.yaml
and./site_scons/components.yaml
./site_scons/chips.yaml
contains the configuration of the chip source files and headers to be included by the build system./site_scons/components.yaml
contains the different components and their sources for the build system./site_scons/platforms.yaml
contains the different platforms and their component composition
- The chip configuration is stored in YAML files in
- Clone repository
- Clone with ssh link. This enables ssh when pushing and pulling
- Pull in submodules
git submodule update --init --recursive
- Install and start docker (OS-specific)
- Windows Users: Install bash emulator: cygwin, mingw64, WSL (test optimal tool)
- Install docker compose (OS-specific)
- Execute docker script from root directory
./buildroot.sh
- Change permissions if necessary:
chmod +x buildroot.sh
- Note: It has been noticed that a bug on windows and mac hosts creates unexpected build failures. Known workaround is to run it from a linux virtual machine.
- Change permissions if necessary:
- All components, and their designator, are listed in
./site_scons/components.yaml
- Building the program(s)
- Execute the SCons program from inside the env
scons --targets=$COMPONENT_DESIGNATOR build
is to build single components- ex: To build the stw, execute
scons --targets=stw build
. Build is the default action for a target and therefore does not need to be specified - Note:
build
is the default target, soscons --target=bmsb
is analogous toscons --target=bmsb build
- Build targets can be seperated by a pound
#
- The config ID of a component can be specified with a colon
:
and can be seperated with a comma,
- An example of a complex build is
scons --targetsbmsb:0#bmsw:1,2
- ex: To build the stw, execute
scons --platform=$PLATFORM_DESIGNATOR
is to build an entire platform
- Execute the SCons program from inside the env
- Uploading the program over SWD (Optional)
- The SCons build environment has an optional upload command that flashes the device and holds
- To flash and verify, execute
scons --targets=$COMPONENT_DESIGNATOR --upload
from the env
- Debugging the program
- More information and better debugging workflows can be found by researching OpenOCD and GDB commands
- Debugging in the env is done through GDB and OpenOCD
- To start the OpenOCD server and connect to the remote host in GDB, execute:
scons --targets=$COMPONENT_DESIGNATOR --debugger
from the env- You can chain multiple commands together. For example: 'scons --target=stw upload openocd-gdb' will build, upload, and start the debugger
- The standard GDB commands apply. To reset or halt the MCU through the SWD connection, execute:
monitor reset
,monitor reset halt
- It is always good when you first open the env to execute
st-info --probe
to see if the ST-LINK is recognized
- SCons Tools
- Located in
./site_scons/site_tools/
- Custom python scripts added to the SCons build environment
- Located in
- Embedded Systems
- Toolchains
- Downloaded from ARM Developper
- Located in
./embedded/toolchains
- USB Loader/Debugger
- Located in
./embedded/openocd
- Interfaces and stores configurations of different chips/boards
- Located in
- Platforms
- Supported hardware platforms
- Located in
./embedded/platforms/
- Toolchains
- SCons related scripts
./SConstruct
./components/$DESIGNATOR/Sconscript
- The script will set up the env for compiling - no need to run twice
- Due to the mounting feature of the docker container, anytime the container is open the changes applied to the files will automatically and immediately be accessible to the SCons tool
- This also means that, once built, the build dir will be on your local machine as well. Run scons --clean in container if you want to re-build from scratch
- If doing loading/debugging of physical hardware, the ST-LINK or other interface must be plugged in by USB before starting the container. Docker containers do not support dynamic loading of USB peripherals
- Note: This only works if no bootloader is installed. If a bootloader is installed and you are debugging through JTAG, you should still flash over CAN
Internal file templates and section templates located in ./shared/templates/
-
Use good modular design. Think carefully about the program structure, functions, and data structures before starting to write the code
-
Use proper error detection and handling. Always check return values from functions and handle errors appropriately.
Error_Handler()
is not proper error handling for production level code (typically). -
If the component uses dynamic memory allocation (typically, we do not support dynamic memory allocation in this framework) you must have code somewhere in the program to release memory.
-
Use descriptive names for functions and important variables. GetRadius() should be used instead of foo() for a function that returns the radius of a circle.
-
Define constants and use them, especially for configuration values. If you have a buffer and decide it should be 100 units long, then:
Compliant
#define BUFFER_SIZE 50U ... uint8_t buf[BUFFER_SIZE];
Non Compliant
uint8_t buf[50];
-
Keep each variable to the smallest scope possible. Do not have a variable global to a file if it is only used from one function. Avoid using global variables
-
Use standard types such as
uint8_t
,int32_t
, etc... Do not useint
orunsigned int
. -
Application level code should not contain system level details. System level code should not contain firmware level details. Etc... Utilize a top-down design
-
Line length should be kept relative. While we believe in monitors with unlimited width, do not have a line 200 characters long. A good rule of thumd is roughly 100 characters long, or just a bit biggetr than a section heading.
-
Documment your code, if you are doing something ambiguous, something that relates to hardware details, etc... detail the execution flow of your program. If you know what it does great, but once you are no longer on the team your knowledge will not help the next person unless you look forward to babysitting (for free)
- Every file, function, etc... should be documented in line with the Documentation Guidelines
-
Only single assignment per line, if assigning multiple items do it over multiple lines
Compliant
uint32_t a, b, c; a = 0; b = a; c = a;
Non Compliant
uint32_t a, b, c; a = b = c = 0;
-
Use proper formatting. Formatting details can be found in
.clang-format
-
Each file is broken into sections such as Includes, Defines, etc...
-
An empty line is to follow and come before the section header
Compliant Code
/****************************************************************************** * D E F I N E S ******************************************************************************/ #define ADC_PRECALIBRATION_DELAY_ADCCLOCKCYCLES 2U #define ADC_CALIBRATION_TIMEOUT 10U /****************************************************************************** * P U B L I C V A R S ******************************************************************************/
Non-Compliant Code
/****************************************************************************** * D E F I N E S ******************************************************************************/ #define ADC_PRECALIBRATION_DELAY_ADCCLOCKCYCLES 2U #define ADC_CALIBRATION_TIMEOUT 10U /****************************************************************************** * P U B L I C V A R S ******************************************************************************/
-
Macros
-
Macros are to be capitalized
Compliant
#define NEGATE(x) -(x)
Non Compliant
#define negate(x) -(x)
-
Reference to macro variables are to be in brackets
Compliant
#define NEGATE(x) -(x)
Non Compliant
#define NEGATE(x) -x
-
-
Defines
-
Define names are to be capitalized and seperated by an under-score:
CAPITALIZED_AND_SCORED
-
Defines must be given meaningful names.
#define TOTAL_MPRL_SENSORS 8
instead of#define N_SENS 8
-
Do not use magic numbers
Compliant
#define USE_THIS_THING 0 #define USE_THAT_THING 1 #define CONFIG_SETTING USE_THIS_THING
Non Compliant
#define CONFIG_SETTING 0
-
-
Header files
-
Contents should be protected from multiple inclusion with
#pragma once
Compliant
/* In showcase.h */ #pragma once /* header contents */ uint32_t func_showcase();
Non Compliant
/* In showcase.h */ /* header contents */ uint32_t func_showcase();
-
- Each name can be broken into its MODULE, LIBRARY, and Specifics
- Optional MODULE: To be capitalized
- Optional LIBRARY: To be capitalized
- Specifics: Case dependant
-
Files
- To be named with the module and library. Such as
HW_ADC.*
for the firmware of the ADC
- To be named with the module and library. Such as
-
Functions
- Names are to be given by
MODULE_LIBRARY_TaskSpecifics
. Such asHW_I2C_MasterTransmit
to transmit data as a master.TaskSpecifics
to be in PascalCase
- Names are to be given by
-
Types
- Structs
-
Follow the
MODULE_LIBRARY_Specifics
standard and be appended with_S
.Specifics
to be in PascalCaseCompliant
SYS_IO_S
Non Compliant
Io_s
-
Struct members should be miniscule, seperated by under-scores, and meaningful
Compliant
typedef struct { uint32_t pcb_temp; ... } HW_EnvValues_S;
Non Compliant
typedef struct { uint32_t a; ... } env_s;
-
- Enums
-
Follow the
MODULE_LIBRARY_Specifics
standard and be appended with_E
.Specifics
to be in PascalCaseCompliant
ADC_Channels_E
Non Compliant
AdcChannels
-
Members must be capitalized and seperated by underscore
Compliant
typedef enum { ADC_CHANNEL_CURRENT_SENSE = 0U, ... } ADC_Channels_E;
Non Compliant
typedef enum { currentSense = 0, ... } ADC_Channels_E;
-
Indexes are to be started with
= 0U
or= 0x00
-
- Structs
-
Variables
-
Generic
- Globals
- Must be
CAPITALIZED_AND_SCORED
- Must be given meaningful name based on
MODULE_LIBRARY_SPECIFICS
- Must be
- Local to module
- Must be given meaningful name based on
LIBRARY_specifics
.LIBRARY
to be capitalied andspecifics
to be miniscule - Library suffix is optional
- Must be given meaningful name based on
- Local to function
- Must be
miniscule_and_scored
- Must be given meaningful name
- Must be
- Intermediates
-
Intermediates should be given a meaningful name in
miniscule_and_scored
. They should be used or discardedCompliant
#define EINVAL 1 int32_t func_showcase(uint32_t param) { int32_t error; if (param < 32U) { error = 0; } else { error = -EINVAL; } return error; } void main(uint32_t index) { int32_t error; uint32_t test; uint32_t array_showcase[32]; error = func_showcase(index); func_showcase(index); // Also OK if (error == 0) { test = array_showcase[index]; } }
Non Compliant
#define EINVAL 22 int32_t func_showcase(uint32_t param) { int32_t error; if (param < 32U) { error = 0; } else { error = -EINVAL; } return error; } void main(uint32_t index) { int32_t error; uint32_t test; uint32_t array_showcase[32]; error = func_showcase(index); test = array_showcase[index]; }
-
- Globals
-
Structs
-
Must be named with camelCase and appended with
_S
Compliant
paddlesRaw_S
Non Compliant
paddles
-
-
Enums
-
Must be named with camelCase and appended with
_E
Compliant
currentState_E
Non Compliant
state
-
-
To be used with doxygen-style comments
- Some detailed rules are listed below to illustrate the comments format for each function:
- The comments block shall start with /** (slash-asterisk-asterisk) in a single line.
- The comments block shall end with */ (space-asterisk-slash) in a single line.
- Other than the first line and the last line, every line inside the comments block shall start with * (space-asterisk). It also applies to the line which is used to separate different paragraphs. We’ll call it a blank line for simplicity.
- Single line comments shall be C-style
// Code comment
-
Files
-
Each file must contain a header which details the file name and a brief description at a minimum
Compliant
/* in header.h */ /** * @file header.h * @brief Details the configuration of this implementation */
Non Compliant
/* in header.h */ // This code houses the configuration
-
-
Functions
-
For each function, following information shall be documented: brief description, detailed description, parameters description, pre-conditions, post-conditions, return value description, and comments explaining the actual return values. We’ll call each block of information a paragraph for simplicity. A paragraph may be removed from the list if it is not applicable for that function.
-
Each line shall only contain the description for one parameter, or one pre-condition, or one post-condition, or one actual return value. We’ll call each of these an element for simplicity.
-
A blank line shall separate different paragraphs. Inside each paragraph, a blank line is not required to separate each element.
-
The brief description of the function shall be documented with the format @brief .
-
No specific format is required for the detailed description of the function.
-
The description of the function parameter shall be documented with the format @param .
-
The pre-condition of the function shall be documented with the format @pre .
-
The post-condition of the function shall be documented with the format @post .
-
The brief description of the function return value shall be documented with the format @return .
-
A void-returning function shall be documented with the format @return None.
-
The comments explaining the actual return values shall be documented with the format @retval .
-
If the description of one element needs to span multiple lines, each line shall be aligned to the start of the description in the first line for that element.
-
The comments block shall appear immediately before the function definition/declaration in the C source file or header file.
Compliant
/** * @brief Brief description of the function. * * Detailed description of the function. Detailed description of the function. Detailed description of the * function. Detailed description of the function. * Application Constraints: Detailed description of application constraint. * * @param param_1 Parameter description for param_1. * @param param_2 Parameter description for param_2. * @param param_3 Parameter description for param_3. Parameter description for param_3. Parameter description * for param_3. Parameter description for param_3. Parameter description for param_3. Parameter * description for param_3. * * @pre param_1 != NULL * @pre param_2 <= 255U * * @post retval <= 0 * * @return Brief description of the return value. * * @retval 0 Success to handle specific case. * @retval -EINVAL Fail to handle specific case because the argument is invalid. * @retval -EBUSY Fail to handle specific case because the target is busy. * */ int32_t func_showcase(uint32_t *param_1, uint32_t param_2, uint32_t param_3);
Non Compliant
/* Brief description of the function. Detailed description of the function. Detailed description of the function. Detailed description of the function. Detailed description of the function. @param param_1 Parameter description for param_1. @param param_2 Parameter description for param_2. @param param_3 Parameter description for param_3. Parameter description for param_3. Parameter description for param_3. Parameter description for param_3. Parameter description for param_3. Parameter description for param_3. pre-conditions: param_1 != NULL, param_2 <= 255U post-conditions: retval <= 0 Brief description of the return value. */ int32_t func_showcase(uint32_t *param_1, uint32_t param_2, uint32_t param_3);
-
-
Types
-
Types are to be documented on the previous line of the definition and be enclosed in a doxygen-style block
Compliant
// @brief Documentation of the type typedef uint32_t Color;
Non Compliant
typedef uint32_t Color; // Documenting
-
-
Variables
-
Types are to be documented on the same line as the definition and be enclosed in a doxygen-style block
Compliant
uint32_t addr; // @brief Documentation of the type
Non Compliant
uint32_t a; // Documenting
-