Design from Scratch using Custom Makefiles and Register Programming¶
Project Structure¶
Guide: The Best Project Structure for C/C++/MCU
A good project structure makes it easier to manage files, perform automations and prepare documentation. The structure suggested here is Pitchform Layout.
The project structure is of form:
.
├── build
│ ├── bin
│ │ └── a.out
│ └── obj
│ ├── main.o
│ └── module1.o
├── data
├── docs
│ ├── Applications
│ └── Images
│ └── image1.png
├── examples
├── external
│ └── printf
├── extras
├── include
│ ├── app
│ ├── common
│ │ └── defines.cpp
│ ├── drivers
│ ├── module1.cpp
│ └── test
├── libs
├── LICENSE.md
├── Makefile
├── README.md
├── src
│ ├── app
│ ├── common
│ │ └── defines.h
│ ├── drivers
│ ├── main.cpp
│ ├── module1.cpp
│ └── test
├── tests
└── tools
└── PLACEHOLDER
Tips and Tricks¶
- Some of the popular available Project Structure:
Version Control on Projects¶
Guide: How I version control with git (Best Practices)
Version control helps you keep track of changes that you make to the project. It helps you a restore point in case you mess-up some change (Believe me that it will happen in almost every project!!) while keeping track of all the progress you make.
There are many version control options: - Manual Versioning (You make copies of files with V.. next to the folder name. Kill me but don’t do this.) - Git - Plastic SCM - Many more..
Base Rules¶
There are some rules you should follow for a better version control over your project: - One Change per commit: Do not overcrowd too much changes in a single commit. Single change per commit ensures that each change is seperated and can be individually visited and modified rather than drastic changes. - Test and build commits: Make sure to automate tests for each commits or push to github so that the commits can verify the production. This ensures that wrong changes are not commited. - Good Commit Messages: Good Commit messages help others (and future you) to understand what exactly did a change do and how it will affect the code.
Tips¶
Static Analysis for C/C++¶
Guide: Static Analysis for C/C++ with cppcheck (+Makefile)
A static analysis acts like a proof-reader for your project. It
performs various checks like grammer check in MS Word. It suggest ways
to improve code flow, correct logical errors or reduce non-reachable
parts of code. There are many options to integrate Static Analysis to
your project. One of the opensource available option is
cppcheck
.
You can integrate cppcheck
in your Makefile to automate
the checking.
Install cppcheck
on your system from a new terminal:
Amend the Makefile to integrate cppcheck
as follows:
# CPPCHECK
CPPCHECK = cppcheck
CPPCHECK_FLAGS = --quiet --enable=all --error-exitcode=1 --inline-suppr
cppcheck:
$(CPPCHECK) $(CPPCHECK_FLAGS)\
-I $(INCLUDE_DIR)\
$(SOURCES)
Simple CI/CD with GitHub¶
Guide: Simple CI/CD with GitHub Actions and Docker (Compile+Analysis)
Basics¶
CI stands for Continuous Integration
CD stands for Continuous Delivery/Continuous Deployment.
CI/CD helps you to automate various processing and set rules to your git project. You can integrate docker with git to automatically host changes and build files on GitHub.
Truly, you can define work of CI/CD as a system that stops you from merging code that does not work. Correct code will the analysed and deployed whereas incorrect code will fail to merge.
Workflow can be as follows:
stateDiagram-v2
[*] --> Branch
Branch --> cc
cc --> bcpp
bcpp --> Commit
Commit --> Push
Push --> pr
pr --> ga
state ga {
[*] --> Make
Make --> Analyse
Analyse --> [*]
}
ga --> Merge : Success
ga --> Rework : Failure
Rework --> cc
Merge --> [*]
cc : Code Change
bcpp : Build + CppCheck
pr : Pull Request
ga : GitHub Actions
GitHub Actions runs the Job inside a Linux Virtual Machine. You can setup a docker container if you want to setup custom commands and develop cross-platform tools.
Setup Essentials¶
You firstly need docker in your system to make a docker image that can be hosted somewhere for GitHub to fetch during GiHub Actions(GA).
Install Docker:
Since we are going to work with this often and this requires admin
access, to avoid sudo
often, we will create a docker group
and add user to it.
Create Group by name docker
Add User to this group
dockerfile¶
Dockerfile is used to set the base os for the action. It can be used to install essential dependencies and needed tools.
Now move to the project directory, and create a new folder
tools
if it does not exist. cd
into
tools
.
Create a new file to write the docker commands
Make the dockerfile with the following code:
FROM ubuntu:23.04
# Install necessary packagesRUN DEBIAN_FRONTEND=noninteractive \ apt-get update \ && apt-get install -y wget bzip2 make unzip cppcheck
# Create a non-root user named "ubuntu"# But put it in root group since GitHub actions need permissions to create temp filesRUN useradd -rm -d /home/ubuntu -s /bin/bash -g root -G sudo \ -u 1001 msp
# Set correct ownership and permissionRUN chown -R msp:root /home/ubuntu
RUN chmod -R 755 /home/ubuntu
USER msp
WORKDIR /home/ubuntu
Code Explaination¶
Set the base OS Image for docker.
This sets the DEBIAN_FRONTEND environment variable to noninteractive, as explained in the previous responses. It ensures that package installations will not prompt for user input and will use default options instead.
This command updates the package lists from the repositories to ensure you are installing the latest versions of packages.
Install the essential packages needed for actions. -y
flag allows to automatically answer yes
to any prompts.
Create a new user named “ubuntu” with the following options: -
rm
: Remove the user’s home directory if it already exists
(prevents conflicts). - d /home/ubuntu
: Set the home
directory for the user to /home/ubuntu. - s /bin/bash
: Set
the default shell for the user to /bin/bash. - g root
: Add
the user to the “root” primary group. - G sudo
: Add the
user to the “sudo” supplementary group, which allows the user to execute
commands with elevated privileges when using the sudo command. -
u 1001
: Set the user ID to 1001 (it’s not recommended to
use the default user ID 1000 because it might conflict with a host user
with the same ID). - USER msp
: This sets the default user
to “msp” meaning any subsequent commands in the Dockerfile and when the
container runs will execute under this non-root user’s context. This
enhances security by avoiding running processes with root
privileges.
Set the working directory to /home/ubuntu, meaning any commands that follow will be executed relative to this directory.
Build the basic docker image¶
msp430gcc
is the docker container name. You can name it anything you want..
is the location of the directory. Since we are running it from thetools
directory, we use.
After the install is complete, look for all available docker images using:
The output will be as follows:
msp430gcc
is our base docker that we will use moving
forward to integrate our toolchains with it.
Adding MSP430¶
Toolchain to base docker image
We can choose to fetch the toolchain directly from the TI website during the CI process but we choose to create a docker image with a fixed version of MSP430 GCC Toolchain instead because as changes are made to the toolchain by msp430, the url might change. This might break the process of fetching. To avoid this issue, we will modify the docker image of ubuntu to have msp430 toolchain inside to avoid the fetching issue.
Login into the docker container using:
This will load the bash terminal of the docker image just created.
Open the TI’s website to download the latest version of ToolChain and Support Files available. At current time, it is: - Mitto Systems GCC 64-bit Linux - toolchain only(V9.3.1.11) - Header and Support Files
Download them into the launch docker container using wget:
wget https://dr-download.ti.com/software-development/ide-configuration-compiler-or-debugger/MD-LlCjWuAbzH/9.3.1.2/msp430-gcc-support-files-1.212.zip
wget https://dr-download.ti.com/software-development/ide-configuration-compiler-or-debugger/MD-LlCjWuAbzH/9.3.1.2/msp430-gcc-9.3.1.11_linux64.tar.bz2
Unzip the files:
Move the include
directory from MSP430 Support files to
include
directory of MSP430 toolchain.
Create the dev/tools/
directory like we have in our
computer for toolchain to be placed:
Clear all extra files and exit container:
Find the container ID of this docker session and commit it into a new image:
The output of all sessions will be as follows:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
090b39853eec msp430gcc "/bin/bash" 4 minutes ago Exited (0) 2 seconds ago sleepy_vaughan
ec716a782125 msp430gcc "/bin/bash" 43 minutes ago Exited (0) 40 minutes ago funny_tu
d31d9b3d22fe 87ada84a26e8 "/bin/bash" About an hour ago Exited (0) About an hour ago laughing_jackson
71e460a0c533 72c77c8a3a9e "/bin/bash" About an hour ago Exited (2) About an hour ago upbeat_kare
99c1ee5b66eb 89d2dbc25de6 "/bin/sh -c 'useradd…" About an hour ago Exited (9) About an hour ago pedantic_shannon
b0827366987b 6fee5ce39d5d "/bin/sh -c 'useradd…" 2 hours ago Exited (9) 2 hours ago exciting_keller
Save the topmost session as new docker image. Write the name of new
docker image in following format:
<docker-hub-username>/<toolchain-version>
. This
will allow for a simple push to the docker hub ahead.
Check for the new docker image by using:
Pushing to Docker Hub¶
For Github to fetch this image, we need to make sure that this is available somewhere to access it. We will be placing this docker image at dockerhub.
- Create Account/Login into docker hub.
- Configure connection to docker hub on your terminal using:
Enter the USER ID
and PASSWORD
from docker
hub to login into docker hub from terminal. 3. After successful login,
push the image to docker hub by:
bash docker push parth2007/msp430-gcc-9.3.1.11:latest
This will take some time but you will be able to push the image to the docker hub to access it from anywhere you want.
Creating CI System¶
GitHub requires the CI File to be stored at a specific location
<root>/.github/workflows
.
Make the ci.yml
from project root as follows:
Open the ci file and create the ci process as follows:
on: [push]jobs: build_and_static_analysis: runs-on: ubuntu-latest container: image: parth2007/msp430-gcc-9.3.1.11 steps: - name: Checkout the repository uses: actions/checkout@v3 - run: make - run: make cppcheck
This GitHub Actions CI (Continuous Integration) configuration file (ci.yml) automates the build and static analysis process for a repository. Let’s break down the contents of the file:
This line specifies the trigger event that activates the CI workflow. In this case, the workflow will be triggered whenever there is a “push” event to the repository. This means that the CI process will run whenever code is pushed to the repository.
jobs: build_and_static_analysis: runs-on: ubuntu-latest container: image: parth2007/msp430-gcc-9.3.1.11 steps: - name: Checkout the repository uses: actions/checkout@v3 - run: make - run: make cppcheck
This section defines the actual CI job named “build_and_static_analysis.” It specifies the operating environment for the job, which is “ubuntu-latest,” meaning the CI process will run on the latest version of Ubuntu available in the GitHub Actions environment.
The container
section allows the job to run inside a
Docker container with the specified image
(parth2007/msp430-gcc-9.3.1.11
). This image contains the
necessary tools and dependencies required for building and analyzing
MSP430 projects using GCC 9.3.1.11.
The steps
section lists the individual tasks that the CI
job will execute:
Checkout the repository
: This step uses theactions/checkout
GitHub Action to fetch the latest code from the repository. It ensures that the CI process has access to the repository’s source code.run: make
: This step executes themake
command. Themake
command is commonly used to build projects based on Makefiles. It will likely build the MSP430 project.run: make cppcheck
: This step executes themake cppcheck
command. It implies that there is a target in the project’s Makefile calledcppcheck
, which likely runs the static code analysis toolcppcheck
. Static code analysis helps identify potential bugs, coding issues, and other problems in the codebase without actually executing the code.
Overall, this CI workflow automates the process of building the MSP430 project and performing static code analysis on it whenever changes are pushed to the repository. The results of the CI process can be viewed in the GitHub Actions tab of the repository. If there are any build errors or static analysis issues, they will be reported in the CI log, allowing the developers to take corrective actions.
Adding Branch Protection¶
Rules
Branch Rules allow you to set a particular set of behavior for
developers to follow so that you can increase safety of the production
or main branch. One of those behaviors is to create branches from the
main
everytime a change to the design/code needs to be
made. This ensures that even if you change breaks the system or
introduces a new bug, anyone using or code from the main branch can
avoid any issues related to the changes you just made.
Your complete ci is going to fail after this because the Makefile still points towards the directory address of the toolchain in your computer rather than docker workchain. We need to ammend that.
Ammend Makefile for CI¶
Modify the Makefile to change the path of toolchain. This can be done
by introducing a variable that takes TOOLS_PATH
as
input.
TOOLS_DIR = $(TOOLS_PATH)
$(info $(TOOLS_DIR))
MSP430_ROOT_DIR = $(TOOLS_DIR)/msp430-gcc
MSP430_INCLUDE_DIR = $(MSP430_ROOT_DIR)/include/
MSP430_LINKER_DIR = $(MSP430_ROOT_DIR)/include/
INCLUDE_DIR = $(MSP430_INCLUDE_DIR)
LIB_DIR = $(MSP430_LINKER_DIR)
BUILD_DIR = build
OBJ_DIR = $(BUILD_DIR)/obj
BIN_DIR = $(BUILD_DIR)/bin
We also need to make changed to the ci.yml so we can pass the TOOLS_PATH as input to it.
on: [push]jobs: build_and_static_analysis: runs-on: ubuntu-latest container: image: parth2007/msp430-gcc-9.3.1.11 steps: - name: Checkout the repository uses: actions/checkout@v3 - run: TOOLS_PATH=/home/ubuntu/dev/tools make - run: TOOLS_PATH=/home/ubuntu/dev/tools make cppcheck
After making these changes, push your code to GitHub to start the implementation.
How to use it moving¶
forwards?
The process moving forwards is simple: 1. Create a new local branch.
2. Edit the code to make the changes needed. 3. Test the changes at
local level using your makefile. 4. Commit and Push the code to github
using: bash git push -u origin <branch-name>
5.
Accept the pull request on your mail branch. A CI Action will run before
the merge can be accepted. 6. If the CI Action is successful, confirm
merging with main branch. 7. Delete the branch both at local and remote
level. 8. Perform Git Pull on local branch main
.
Resources¶
Complete Guide to CI/CD from GitHub can be found at: - CI/CD: The what, why, and how - Learn GitHub Actions
Documentation and Clang¶
Guide: Documentation and Clang format
Documentaion¶
A good documentation README of a project contains following items: - Project Name on Top. - Photos Showing the Project/Functionality. - A Brief Writeup of how the project works. - A Small Write up on what technology and design procedure have been used in project. - Project Directory Structure showing how the files are organized. - Guide on how to build/run the project. - Small explaination on how to the project automation files work. - Write-up on any Tests present. - Commit Guide for a new change - Schematic Diagram of the Hardware if any. - Guide on how to generate/fetch Bill of Materials. - Software Architecture if any
Clang-Formatting¶
Most IDE that you use can install dependencies that can format code for you. But if you are working with multiple different systems, different IDEs or Formatters can format code in different ways. To create a uniform formatting pattern, external/manual formatting agents can be used.
clang-format is one of the commonly used free tool available out there which can be used to set code formatting rules.
Install clang-format on your system using:
After the install, you need to create a .clang-format
file which holds all the rules for Formatting. There are various
available formats of formatting availale: - LLVM
A style
complying with the LLVM coding standards - Google
A style
complying with Google’s C++ style guide - Chromium
A style
complying with Chromium’s style guide - Mozilla
A style
complying with Mozilla’s style guide - WebKit
A style
complying with WebKit’s style guide - Microsoft
A style
complying with Microsoft’s style guide - GNU
A style
complying with the GNU coding standards
Create the file in required format as follows:
This will create a .clang-format
as per the coding
standard available from these groups.
You can format a file using the command:
As you might have noticed, this is going to be tiring if you do this for every file available, So to automate formatting on all files, you can create a PHONY in Makefile.
Now, you can format all files by simply calling:
Integrating Formatting with¶
CI/CD
clang-format can be integrated in CI using docker.
Update the dockerfile
to install clang-format.
# Install necessary packagesRUN DEBIAN_FRONTEND=noninteractive \ apt-get update \ && apt-get install -y wget bzip2 make unzip cppcheck clang-format git
Follow the CI/CD Guide to update the image on dockerhub with this clang-format.
Update the CI.yml to run a formatting check task:
- run: make format && git diff --quiet- run: TOOLS_PATH=/home/ubuntu/dev/tools make- run: TOOLS_PATH=/home/ubuntu/dev/tools make cppcheck
Resources¶
- Clang Official Documentation
- Lei Mao’s Log Book: Format C/C++ Using Clang-format
- Clang Official Documentation: Configurable Format Style Options