Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ jobs:

build-sorcerer:
name: Build Sorcerer
if: false # Temporarily disabled: Sorcerer dependency fetches are flaky on CI.
continue-on-error: true
strategy:
matrix:
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/unused-imports.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ jobs:

- name: Build zigimports
run: |
git clone --depth 1 https://github.com/tusharsadhwani/zigimports.git /tmp/zigimports
git clone https://github.com/tusharsadhwani/zigimports.git /tmp/zigimports
cd /tmp/zigimports
git checkout 837804f1a677578bca7c5e028baf25684b85e6b4
zig build --release=safe
# Binary is named zigimports-<arch>-<os>, find and symlink it
ln -s /tmp/zigimports/zig-out/bin/zigimports-* /tmp/zigimports/zig-out/bin/zigimports
Expand Down
38 changes: 13 additions & 25 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ pub const LinkerScript = internals.LinkerScript;
pub const Stack = internals.Stack;
pub const MemoryRegion = internals.MemoryRegion;

const regz = @import("tools/regz");

// If more ports are available, the error "error: evaluation exceeded 1000 backwards branches" may occur.
// In such cases, consider increasing the argument value for @setEvalBranchQuota().
const port_list: []const struct {
Expand All @@ -36,15 +34,6 @@ const port_list: []const struct {
.{ .name = "tm4c", .dep_name = "port/texasinstruments/tm4c" },
};

const exe_targets: []const std.Target.Query = &.{
.{ .cpu_arch = .aarch64, .os_tag = .macos },
.{ .cpu_arch = .aarch64, .os_tag = .linux },
.{ .cpu_arch = .aarch64, .os_tag = .windows },
.{ .cpu_arch = .x86_64, .os_tag = .macos },
.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl },
.{ .cpu_arch = .x86_64, .os_tag = .windows },
};

pub fn build(b: *Build) void {
const optimize = b.standardOptimizeOption(.{});

Expand Down Expand Up @@ -485,30 +474,32 @@ pub fn MicroBuild(port_select: PortSelect) type {
core_mod.addImport("board", board_mod);
}

// The user's main.zig is the executable's root module. The
// application opts into microzig's startup via
// comptime { _ = microzig.export_startup(); }
// in its root source file, and re-exports microzig's `panic`.
// microzig's core module reads the root via `@import("root")`,
// so there's no "app" import to wire.
const app_mod = mb.builder.createModule(.{
.root_source_file = options.root_source_file,
.imports = options.imports,
.target = zig_resolved_target,
.optimize = options.optimize,
.single_threaded = options.single_threaded orelse target.single_threaded,
.strip = options.strip,
.unwind_tables = options.unwind_tables,
.error_tracing = options.error_tracing,
.dwarf_format = options.dwarf_format,
});
app_mod.addImport("microzig", core_mod);
core_mod.addImport("app", app_mod);

const fw = mb.builder.allocator.create(Firmware) catch @panic("out of memory");
fw.* = .{
.mb = mb,
.core_mod = core_mod,
.artifact = mb.builder.addExecutable(.{
.name = options.name,
.root_module = b.createModule(.{
.optimize = options.optimize,
.target = zig_resolved_target,
.root_source_file = mb.core_dep.path("src/start.zig"),
.single_threaded = options.single_threaded orelse target.single_threaded,
.strip = options.strip,
.unwind_tables = options.unwind_tables,
.error_tracing = options.error_tracing,
.dwarf_format = options.dwarf_format,
}),
.root_module = app_mod,
.linkage = .static,
}),
.app_mod = app_mod,
Expand All @@ -524,9 +515,6 @@ pub fn MicroBuild(port_select: PortSelect) type {
fw.artifact.link_data_sections = options.strip_unused_symbols;
fw.artifact.entry = options.entry orelse target.entry orelse .default;

fw.artifact.root_module.addImport("microzig", core_mod);
fw.artifact.root_module.addImport("app", app_mod);

const linker_script_options = options.linker_script orelse target.linker_script;
const linker_script = blk: {
const GenerateLinkerScriptArgs = @import("tools/generate_linker_script.zig").Args;
Expand Down
3 changes: 0 additions & 3 deletions core/src/cpus/cortex_m.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ const std = @import("std");
const builtin = @import("builtin");
const microzig = @import("microzig");
const mmio = microzig.mmio;
const app = microzig.app;
const shared = @import("cortex_m/shared_types.zig");
const VectorTable = microzig.chip.VectorTable;

const Core = enum {
Expand Down Expand Up @@ -1026,7 +1024,6 @@ pub fn export_startup_logic() void {

const scs_base = 0xE000E000;
const itm_base = 0xE0000000;
const dwt_base = 0xE0001000;
const tpi_base = 0xE0040000;

const coredebug_base = 0xE000EDF0;
Expand Down
149 changes: 124 additions & 25 deletions core/src/microzig.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@

const std = @import("std");
const root = @import("root");
const builtin = @import("builtin");

/// The app that is currently built.
pub const app = @import("app");

/// Contains build-time generated configuration options for microzig.
/// Contains a CPU target description, chip, board and cpu information
Expand Down Expand Up @@ -67,26 +63,6 @@ pub const CPU_Options = if (@hasDecl(cpu, "CPU_Options")) cpu.CPU_Options else s
pub const HAL_Options = if (config.has_hal and @hasDecl(hal, "HAL_Options")) hal.HAL_Options else struct {};

pub const Options = struct {
log_level: std.log.Level = std.log.default_level,
log_scope_levels: []const std.log.ScopeLevel = &.{},
logFn: fn (
comptime message_level: std.log.Level,
comptime scope: @TypeOf(.enum_literal),
comptime format: []const u8,
args: anytype,
) void = struct {
fn log(
comptime message_level: std.log.Level,
comptime scope: @Type(.enum_literal),
comptime format: []const u8,
args: anytype,
) void {
_ = message_level;
_ = scope;
_ = format;
_ = args;
}
}.log,
interrupts: InterruptOptions = .{},
overwrite_hal_interrupts: bool = false, //force overwrite the Hal default interrupts
cpu: CPU_Options = .{},
Expand All @@ -103,7 +79,71 @@ pub const Options = struct {
simple_panic_if_main_errors: bool = false,
};

pub const options: Options = if (@hasDecl(app, "microzig_options")) app.microzig_options else .{};
pub const options: Options = if (@hasDecl(root, "microzig_options")) root.microzig_options else .{};

/// Overrides accepted by `microzig.std_options`. Mirrors only the subset of
/// `std.Options` fields that are meaningful on freestanding/embedded targets.
///
/// Included fields (and why):
/// * `log_level` — controls verbosity of `std.log` calls.
/// * `log_scope_levels` — per-scope filtering for fine-grained logging.
/// * `logFn` — the logging callback. This is the *critical* one: stdlib's
/// default writes to stderr, which doesn't exist on freestanding targets
/// and causes link failures whenever any reachable code touches
/// `std.log.*`. Microzig defaults this to a no-op.
///
/// Fields from `std.Options` that are intentionally NOT exposed here:
/// * `enable_segfault_handler`, `signal_stack_size` — POSIX signal
/// plumbing; no OS-level signals on freestanding.
/// * `page_size_min`, `page_size_max`, `queryPageSize` — OS virtual-memory
/// page-size configuration; embedded firmware doesn't have VM.
/// * `fmt_max_depth` — stdlib fmt recursion bound; rarely customized and
/// orthogonal to embedded concerns.
/// * `http_disable_tls`, `http_enable_ssl_key_log_file` — `std.http.Client`
/// tuning; the HTTP client is hosted-only.
/// * `side_channels_mitigations` — `std.crypto` side-channel mitigations;
/// hosted-oriented, and embedded projects typically select crypto at
/// the module level.
/// * `allow_stack_tracing` — pulls in `std.debug.ElfFile` / debug-info
/// loaders that assume a hosted filesystem.
/// * `networking` — gates `std.Io` networking; hosted-only. (Microzig
/// embedded networking is in `modules/network`.)
/// * `unexpected_error_tracing` — default debug-mode tracing that relies
/// on stderr.
///
/// Users who genuinely need one of the excluded fields can still declare
/// their own `pub const std_options = std.Options{ ... }` directly and skip
/// this helper.
pub const StdOptions = struct {
log_level: std.log.Level = std.log.default_level,
log_scope_levels: []const std.log.ScopeLevel = &.{},
logFn: fn (
comptime message_level: std.log.Level,
comptime scope: @TypeOf(.enum_literal),
comptime format: []const u8,
args: anytype,
) void = no_op_log,
};

/// Build a `std.Options` with microzig's freestanding-safe defaults. Users
/// re-export from their root source file:
///
/// pub const std_options = microzig.std_options(.{}); // defaults only
/// pub const std_options = microzig.std_options(.{ .log_level = .info }); // with overrides
pub fn std_options(comptime overrides: StdOptions) std.Options {
return .{
.log_level = overrides.log_level,
.log_scope_levels = overrides.log_scope_levels,
.logFn = overrides.logFn,
};
}

fn no_op_log(
comptime _: std.log.Level,
comptime _: @TypeOf(.enum_literal),
comptime _: []const u8,
_: anytype,
) void {}

/// Hangs the processor and will stop doing anything useful. Use with caution!
pub fn hang() noreturn {
Expand All @@ -114,6 +154,65 @@ pub fn hang() noreturn {
}
}

/// Emit microzig's firmware startup symbols. Must be invoked from a
/// `comptime` block in the root source file of every firmware:
///
/// comptime { _ = microzig.export_startup(); }
///
/// Emits the CPU-specific `_start` symbol (and vector table where
/// applicable) and the `microzig_main` wrapper that the CPU startup calls
/// once `.data`/`.bss` have been initialized.
pub fn export_startup() void {
cpu.export_startup_logic();
@export(&microzig_main, .{ .name = "microzig_main" });
}

/// Called from the CPU-specific `_start` once `.data`/`.bss` are live. Reads
/// `main` from the root source file and supports HAL `init` / root `init` /
/// error-returning `main`.
fn microzig_main() callconv(.c) noreturn {
// A HAL may define `init` (e.g. clocks, PLL) that runs before main. The
// user's root source file may define its own `init` to override the HAL
// default.
if (@hasDecl(root, "init"))
root.init()
else if (hal != void and @hasDecl(hal, "init"))
hal.init();

const main = @field(root, "main");
const return_type = @typeInfo(@TypeOf(main)).@"fn".return_type orelse
@compileError("microzig: `main` must have a return type");

if (@typeInfo(return_type) == .error_union) {
main() catch |err| {
const msg_base = "main() returned error.";

if (!options.simple_panic_if_main_errors) {
const max_error_size = comptime blk: {
var max: usize = 0;
const err_type = @typeInfo(return_type).error_union.error_set;
if (@typeInfo(err_type).error_set) |err_set| {
for (err_set) |current_err| {
max = @max(max, current_err.name.len);
}
}
break :blk max;
};

var buf: [msg_base.len + max_error_size]u8 = undefined;
const msg = std.fmt.bufPrint(&buf, "{s}{s}", .{ msg_base, @errorName(err) }) catch @panic(msg_base);
@panic(msg);
} else {
@panic(msg_base);
}
};
} else {
main();
}

hang();
}

test {
_ = utilities;
_ = Allocator;
Expand Down
95 changes: 0 additions & 95 deletions core/src/start.zig

This file was deleted.

Loading
Loading