Skip to content

Commit

Permalink
Build system rewrite (#259)
Browse files Browse the repository at this point in the history
* import all used ports and return them to the user
* better cpu interface for ports to fix riscv features issue
* add post process function to target
* add build formats
* port lpc, linker script generation and refactor
* remove rp2040 stack end from linker script
* copy `MemoryRegion` to generate_linker_script.zig and make `BuildFormat.uf2` a custom enum
* replace port_select with microzig_options
* Make lazy dependencies work
* remove code duplication
* port stm32 examples to the new build system
* comments and implement the rest of the functionality
  • Loading branch information
tact1m4n3 authored Nov 21, 2024
1 parent 1bb11ec commit 62734a7
Show file tree
Hide file tree
Showing 49 changed files with 156,893 additions and 144,879 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ jobs:
run: zig build -Doptimize=ReleaseSmall

- name: Unit Test Ports
run: zig build run-port-tests -Doptimize=ReleaseSmall
run: |
cd port/raspberrypi/rp2xxx
zig build test
- name: Build Website
run: zig build
Expand Down
File renamed without changes.
4 changes: 4 additions & 0 deletions build-internals/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# build-internals

This package is meant to provide build related type definitions for
internal use in the repo (avoiding circular dependencies).
285 changes: 285 additions & 0 deletions build-internals/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
const std = @import("std");
const Build = std.Build;
const LazyPath = Build.LazyPath;
const Module = Build.Module;

pub fn build(b: *Build) void {
_ = b.addModule("build-internals", .{
.root_source_file = b.path("build.zig"),
});
}

/// A compilation target for MicroZig. Provides information about the chip,
/// hal, board and so on.
///
/// This is used instead of `std.Target.Query` to define a MicroZig Firmware.
pub const Target = struct {
/// The `*std.Build.Dependency` belonging of the port that created this target.
dep: *Build.Dependency,

/// The preferred binary format of this MicroZig target, if it has one.
preferred_binary_format: ?BinaryFormat = null,

/// The chip this target uses.
chip: Chip,

/// Usually, embedded projects are single-threaded and single-core applications. Platforms that
/// support multiple CPUs should set this to `false`.
single_threaded: bool = true,

/// Determines whether the compiler_rt package is bundled with the application or not.
/// This should always be true except for platforms where compiler_rt cannot be built right now.
bundle_compiler_rt: bool = true,

/// (optional) Provides a default hardware abstraction layer that is used.
/// If `null`, no `microzig.hal` will be available.
hal: ?HardwareAbstractionLayer = null,

/// (optional) Provides description of external hardware and connected devices
/// like oscillators and such.
///
/// This structure isn't used by MicroZig itself, but can be utilized from the HAL
/// if present.
board: ?Board = null,

/// (optional) Provide a custom linker script for the hardware or define a custom generation.
linker_script: ?LazyPath = null,

/// (optional) Post processing step that will patch up and modify the elf file if necessary.
patch_elf: ?*const fn (*Build.Dependency, LazyPath) LazyPath = null,

/// Things you can change by deriving from an already existing target.
pub const DeriveOptions = struct {
preferred_binary_format: ?BinaryFormat = null,
chip: ?Chip = null,
single_threaded: ?bool = null,
bundle_compiler_rt: ?bool = null,
hal: ?HardwareAbstractionLayer = null,
board: ?Board = null,
linker_script: ?LazyPath = null,
patch_elf: ?*const fn (*Build.Dependency, LazyPath) LazyPath = null,
};

/// Creates a new target from an existing one.
pub fn derive(from: *const Target, options: DeriveOptions) *Target {
const ret = from.dep.builder.allocator.create(Target) catch @panic("out of memory");
ret.* = .{
.dep = from.dep,
.preferred_binary_format = options.preferred_binary_format orelse from.preferred_binary_format,
.chip = options.chip orelse from.chip,
.single_threaded = options.single_threaded orelse from.single_threaded,
.bundle_compiler_rt = options.bundle_compiler_rt orelse from.bundle_compiler_rt,
.hal = options.hal orelse from.hal,
.board = options.board orelse from.board,
.linker_script = options.linker_script orelse from.linker_script,
.patch_elf = options.patch_elf orelse from.patch_elf,
};
return ret;
}
};

/// Defines a chip.
pub const Chip = struct {
/// The display name of the controller.
name: []const u8,

/// (optional) link to the documentation/vendor page of the controller.
url: ?[]const u8 = null,

/// The cpu target this controller uses.
cpu: std.Target.Query,

/// The provider for register definitions.
register_definition: union(enum) {
/// Use `regz` to create a zig file from a JSON schema.
json: LazyPath,

/// Use `regz` to create a json file from a SVD schema.
svd: LazyPath,

/// Use `regz` to create a zig file from an ATDF schema.
atdf: LazyPath,

/// Use the provided file directly as the chip file.
zig: LazyPath,
},

/// The memory regions that are present in this chip.
memory_regions: []const MemoryRegion,
};

/// Defines a hardware abstraction layer.
pub const HardwareAbstractionLayer = struct {
/// Provides the root source file for the HAL.
root_source_file: LazyPath,

/// Provides imports for the HAL. **Need to be heap allocated.**
imports: []const Module.Import = &.{},
};

/// Provides a description of a board.
///
/// Boards provide additional information to a chip and HAL package.
/// For example, they can list attached peripherials, external crystal frequencies,
/// flash sizes, ...
pub const Board = struct {
/// Display name of the board.
name: []const u8,

/// (optional) link to the documentation/vendor page of the board.
url: ?[]const u8 = null,

/// Provides the root source file for the board definition.
root_source_file: LazyPath,

/// (optional) Provides imports for the board definition. **Need to be heap allocated.**
imports: []const Module.Import = &.{},
};

/// Convenience struct for heap allocated imports.
pub const ModuleImports = struct {
/// List of imports.
list: []const Module.Import = &.{},

/// Initializes module imports on the heap.
pub fn init(allocator: std.mem.Allocator, imports: []const Module.Import) ModuleImports {
return .{
.list = allocator.dupe(imports) catch @panic("out of memory"),
};
}
};

/// The resulting binary format for the firmware file.
/// A lot of embedded systems don't use plain ELF files, thus we provide means
/// to convert the resulting ELF into other common formats.
pub const BinaryFormat = union(enum) {
/// [Executable and Linkable Format](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format), the standard output from the compiler.
elf,

/// A flat binary, contains only the loaded portions of the firmware with an unspecified base offset.
bin,

/// The [Intel HEX](https://en.wikipedia.org/wiki/Intel_HEX) format, contains
/// an ASCII description of what memory to load where.
hex,

/// A [Device Firmware Upgrade](https://www.usb.org/sites/default/files/DFU_1.1.pdf) file.
dfu,

/// The [USB Flashing Format (UF2)](https://github.com/microsoft/uf2) designed by Microsoft.
uf2: enum {
ATMEGA32,
SAML21,
NRF52,
ESP32,
STM32L1,
STM32L0,
STM32WL,
LPC55,
STM32G0,
GD32F350,
STM32L5,
STM32G4,
MIMXRT10XX,
STM32F7,
SAMD51,
STM32F4,
FX2,
STM32F2,
STM32F1,
NRF52833,
STM32F0,
SAMD21,
STM32F3,
STM32F407,
STM32H7,
STM32WB,
ESP8266,
KL32L2,
STM32F407VG,
NRF52840,
ESP32S2,
ESP32S3,
ESP32C3,
ESP32C2,
ESP32H2,
RP2040,
RP2XXX_ABSOLUTE,
RP2XXX_DATA,
RP2350_ARM_S,
RP2350_RISC_V,
RP2350_ARM_NS,
STM32L4,
GD32VF103,
CSK4,
CSK6,
M0SENSE,
},

/// The [firmware format](https://docs.espressif.com/projects/esptool/en/latest/esp32/advanced-topics/firmware-image-format.html) used by the [esptool](https://github.com/espressif/esptool) bootloader.
esp,

/// Custom option for non-standard formats.
custom: *Custom,

/// Returns the standard extension for the resulting binary file.
pub fn get_extension(format: BinaryFormat) []const u8 {
return switch (format) {
.elf => ".elf",
.bin => ".bin",
.hex => ".hex",
.dfu => ".dfu",
.uf2 => ".uf2",
.esp => ".bin",

.custom => |c| c.extension,
};
}

pub const Custom = struct {
/// The standard extension of the format.
extension: []const u8,

/// A function that will convert a given `elf` file into the custom output format.
convert: *const fn (*Build.Dependency, elf: Build.LazyPath) Build.LazyPath,
};
};

/// A descriptor for memory regions in a microcontroller.
pub const MemoryRegion = struct {
/// The type of the memory region for generating a proper linker script.
kind: Kind,
offset: u64,
length: u64,

pub const Kind = union(enum) {
/// This is a (normally) immutable memory region where the code is stored.
flash,

/// This is a mutable memory region for data storage.
ram,

/// This is a memory region that maps MMIO devices.
io,

/// This is a memory region that exists, but is reserved and must not be used.
reserved,

/// This is a memory region used for internal linking tasks required by the board support package.
private: PrivateRegion,
};

pub const PrivateRegion = struct {
/// The name of the memory region. Will not have an automatic numeric counter and must be unique.
name: []const u8,

/// Is the memory region executable?
executable: bool,

/// Is the memory region readable?
readable: bool,

/// Is the memory region writable?
writeable: bool,
};
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.{
.name = "build/definitions",
.name = "build-internals",
.version = "0.0.0",
.paths = .{
"LICENSE",
Expand Down
Loading

0 comments on commit 62734a7

Please sign in to comment.