I am having some trouble finding examples of how to organize embedded projects, especially for custom hardware. There are a lot of examples for how to do something on off-the-shelf hardware like Arduinos, but not so much when you are building potentially large workspaces for something that does not have a BSP.
So - the below is my understanding of the logical sections of a large workspace, including features like FPGA interfaces, reusable drivers, and interchangeable hardware. I would appreciate any sort of suggestions for anything that works better in practice.
Peripheral access controllers are standard and generated from svd files. FPGAs fabric that generates SVD would create one of these separate from the main core's SVD (e.g., zynqs)
HALs are handwritten and are meant to provide abstraction over PACs. They provide all kinds of interfaces, including ways to construct the
embedded-hal types. In general, these seem to get quite complicated with traits & types (e.g.
There might also be something custom that provides traits for things not in
Drivers are generic and depend only on
embedded-hal - should be generic over
embedded-hal traits. These provide functionality to use ICs that somehow interface with the main IC (e.g. tcal9539 i2c i/o expander, mt25q qspi flash, tmp107 uart temp sensor, tja1043 can controller, ethernet phys)
BSPs depend on HAL crates, and expose correctly named (and sometimes typed) interfaces. This should be set during PCB design, and can actually help check for pinout errors if the HAL has very strong typing. If pinout changes, this should be the only component to change (I think). BSPs for similar things should aim to export identical names, for easy reuse in application code. I don't think any reliance on drivers belongs here.
I have also seen helper functions for configuring peripherals (uart, i2c, etc), such as the samd-hal crate. These always seem a bit tricky to write, but potentially nice to have.
This is the actual entrypoint, imports the desired bsp as
bsp and performs the desired tasks. Which bsp it imports may be feature gated.
Different app code crates may depend on something like
embassy, etc as needed.
So, all that in mind, I'd wind up with a directory structure like this:
# Cargo.toml in each leaf directory ffi/ # Bindings to existing C code to be called drivers/ # Drivers for external interfaces pca9535/ tmp107/ pac/ # PACs for nonstandard peripherals fpga-config-1/ fpga-config-2/ hal/ # Nonstandard HALs, feature gated for fpga-hal/ # custom PACs bsp/ # Define interfaces to the main chip here pcb-1/ pcb-2-chip-1/ pcb-2-chip-2/ app/ # Run the actual code application-1/ application-2/ Cargo.toml # cargo workspace config
Please let me know what parts of the above structure work well, and what needs some tweaking to be more accurate to what works well.