Skip to content
Merged
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
194 changes: 181 additions & 13 deletions core/src/cpus/cortex_m.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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 @@ -683,26 +684,46 @@ pub const startup_logic = struct {
: .{ .memory = true, .r0 = true, .r1 = true });
}

if (@hasField(types.peripherals.SystemControlBlock, "SHCSR")) {
// Enable distinction between MemFault, BusFault and UsageFault:
peripherals.scb.SHCSR.modify(.{
.MEMFAULTENA = 1,
.BUSFAULTENA = 1,
.USGFAULTENA = 1,
});
enable_fault_irq();
}

microzig_main();
}

const DummyVectorTable = extern struct {
initial_stack_pointer: usize,
Reset: Handler,
};
// Validate that the VectorTable type has all the fault handlers that the CPU expects
comptime {
if (@hasDecl(core, "cpu_flags")) {
const flags = core.cpu_flags;
if ((flags.has_hard_fault and !@hasField(VectorTable, HardFault_name)) or
(flags.has_bus_fault and !@hasField(VectorTable, BusFault_name)) or
(flags.has_mem_manage_fault and !@hasField(VectorTable, MemManageFault_name)) or
(flags.has_usage_fault and !@hasField(VectorTable, UsageFault_name)))
@compileError("The CPU configures a fault vector, but it is not present in the VectorTable type!");
}
}

// If we are using a RAM vector table, we can use a dummy one (only 8 bytes) that only provides
// the reset vector and the initial stack pointer.
const FlashVectorTable = if (using_ram_vector_table)
DummyVectorTable
extern struct {
initial_stack_pointer: usize,
Reset: Handler,
}
else
VectorTable;

// If we are using a RAM vector table, we can use a dummy one (only 8
// bytes) that only provides the reset vector and the initial stack
// pointer.
// Must be aligned to 256 as VTOR ignores the lower 8 bits of the address.
const _vector_table: FlashVectorTable align(256) = if (is_ram_image)
@compileError("`_vector_table` is not available in a RAM image")
else if (using_ram_vector_table)
// The vector table in flash must be aligned to 256 as VTOR ignores the lower 8 bits of the
// address.
const _vector_table: FlashVectorTable align(256) = if (is_ram_image) {
@compileError("`_vector_table` is not available in a RAM image. Use `ram_vector_table` instead.");
} else if (using_ram_vector_table)
.{
.initial_stack_pointer = microzig.config.end_of_stack,
.Reset = .{ .c = microzig.cpu.startup_logic._start },
Expand Down Expand Up @@ -749,18 +770,165 @@ pub const startup_logic = struct {
}

fn DebugExceptionHandler(comptime name: []const u8) type {
if (microzig.options.cpu.verbose_unhandled_irq) {
return struct {
pub const handle = debugExceptionHandler(name);
};
}
return ReleaseExceptionHandler;
}

const IrqHandlerFn = *const fn () callconv(.c) void;

fn debugExceptionHandler(comptime name: []const u8) IrqHandlerFn {
// Only use verbose fault handlers on cores that have the required registers
const has_hfsr = @hasField(types.peripherals.SystemControlBlock, "HFSR");
const has_cfsr = @hasField(types.peripherals.SystemControlBlock, "CFSR");

if (comptime std.mem.eql(u8, name, HardFault_name) and has_hfsr)
return debug.hard_fault_handler;
if (comptime std.mem.eql(u8, name, BusFault_name) and has_cfsr)
return debug.bus_fault_handler;
if (comptime std.mem.eql(u8, name, MemManageFault_name))
return debug.mem_manage_fault_handler;
if (comptime std.mem.eql(u8, name, UsageFault_name) and has_cfsr)
return debug.usage_fault_handler;

return struct {
fn handle() callconv(.c) void {
@panic("Unhandled exception: " ++ name);
}
};
}.handle;
}

const ReleaseExceptionHandler = struct {
fn handle() callconv(.c) void {
@panic("Unhandled exception");
}
};

const HardFault_name = "HardFault";
const BusFault_name = "BusFault";
const MemManageFault_name = "MemManageFault";
const UsageFault_name = "UsageFault";
};

/// Implements several mechanisms to easy debugging with Cortex-M cpus.
///
/// Read more here:
/// https://interrupt.memfault.com/blog/cortex-m-hardfault-debug
pub const debug = struct {
const logger = std.log.scoped(.cortex_m_debug);

/// This frame is pushed mostly by the CPU itself, and we move it into
/// the parameter register, so we can inspect it.
pub const ContextStateFrame = extern struct {
r0: u32,
r1: u32,
r2: u32,
r3: u32,
r12: u32,
lr: u32,
return_address: u32,
xpsr: u32,
};

/// Wraps `handler` in a small asm block that ensures that it is a regular interrupt handler
/// function, but also provides us with a ContextStateFrame fetched from the system status:
pub fn make_fault_handler(comptime handler: *const fn (context: *ContextStateFrame) callconv(.c) void) *const fn () callconv(.c) void {
return struct {
fn invoke() callconv(.c) void {
// See this article on how we use that:
// https://interrupt.memfault.com/blog/cortex-m-hardfault-debug
asm volatile (
\\
// Check 2th bit of LR.
\\tst lr, #4
// Do "if then else" equal
\\ite eq
// if equals, we use the MSP
\\mrseq r0, msp
// otherwise, we use the PSP
\\mrsne r0, psp
// Then we branch to our handler:
\\b %[handler]
:
: [handler] "s" (handler),
);
}
}.invoke;
}

pub fn hard_fault_handler() callconv(.c) void {
const hfsr = peripherals.scb.HFSR.read();

logger.err("Hard Fault:", .{});
logger.err(" VECTTBL: {}", .{hfsr.VECTTBL});
logger.err(" FORCED: {}", .{hfsr.FORCED});
logger.err(" DEBUGEVT: {}", .{hfsr.DEBUGEVT});

@panic("Hard fault");
}

pub fn mem_manage_fault_handler() callconv(.c) void {
@panic("Memory fault");
}

pub const bus_fault_handler = make_fault_handler(handle_bus_fault_wrapped);

fn handle_bus_fault_wrapped(context: *const ContextStateFrame) callconv(.c) void {
const bfsr = peripherals.scb.CFSR.read().BFSR;

logger.err("Bus Fault:", .{});
logger.err(" context = r0:0x{X:0>8} r1:0x{X:0>8} r2:0x{X:0>8} r3:0x{X:0>8}", .{
context.r0,
context.r1,
context.r2,
context.r3,
});
logger.err(" r12:0x{X:0>8} lr:0x{X:0>8} ra:0x{X:0>8} xpsr:0x{X:0>8}", .{
context.r12,
context.lr,
context.return_address,
context.xpsr,
});
logger.err(" instruction bus error = {}", .{bfsr.instruction_bus_error});
logger.err(" precice data bus error = {}", .{bfsr.precice_data_bus_error});
logger.err(" imprecice data bus error = {}", .{bfsr.imprecice_data_bus_error});
logger.err(" unstacking exception error = {}", .{bfsr.unstacking_exception_error});
logger.err(" exception stacking error = {}", .{bfsr.exception_stacking_error});
logger.err(" busfault address register valid = {}", .{bfsr.busfault_address_register_valid});
if (bfsr.busfault_address_register_valid) {
const address = peripherals.scb.BFAR;
logger.err(" busfault address register = 0x{X:0>8}", .{address});
}

@panic("Bus fault");
}

pub const usage_fault_handler = make_fault_handler(handle_usage_fault_wrapped);

fn handle_usage_fault_wrapped(context: *const ContextStateFrame) callconv(.c) void {
const ufsr = peripherals.scb.CFSR.read().UFSR;

logger.err("Usage Fault:", .{});
logger.err(
" context = r0:0x{X:0>8} r1:0x{X:0>8} r2:0x{X:0>8} r3:0x{X:0>8}",
.{ context.r0, context.r1, context.r2, context.r3 },
);
logger.err(
" r12:0x{X:0>8} lr:0x{X:0>8} ra:0x{X:0>8} xpsr:0x{X:0>8}",
.{ context.r12, context.lr, context.return_address, context.xpsr },
);
logger.err(" undefined instruction = {}", .{ufsr.undefined_instruction});
logger.err(" invalid state = {}", .{ufsr.invalid_state});
logger.err(" invalid pc load = {}", .{ufsr.invalid_pc_load});
logger.err(" missing coprocessor usage = {}", .{ufsr.missing_coprocessor_usage});
logger.err(" unaligned memory access = {}", .{ufsr.unaligned_memory_access});
logger.err(" divide by zero = {}", .{ufsr.divide_by_zero});

@panic("Usage fault");
}
};

const is_ram_image = microzig.config.ram_image;
Expand Down
12 changes: 10 additions & 2 deletions core/src/cpus/cortex_m/m0.zig
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
const microzig = @import("microzig");
const builtin = @import("builtin");
const mmio = microzig.mmio;
const shared = @import("shared_types.zig");

pub const CPU_Options = struct {};
pub const CPU_Options = struct {
/// If true, the Cortex-M interrupts will be initialized with a more verbose variant
/// of the interrupt handlers which print the interrupt name.
///
/// NOTE: This option is enabled in debug builds by default.
verbose_unhandled_irq: bool = (builtin.mode == .Debug),
};

pub const scb_base_offset = 0x0d00;

Expand Down Expand Up @@ -127,7 +135,7 @@ pub const SystemControlBlock = extern struct {
///
/// The processor also wakes up on execution of an SEV instruction or an external event.
SEVONPEND: u1,
reserved2: u17 = 0,
reserved2: u27 = 0,
}),
/// Configuration Control Register.
CCR: mmio.Mmio(packed struct(u32) {
Expand Down
7 changes: 3 additions & 4 deletions core/src/cpus/cortex_m/m0plus.zig
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
const microzig = @import("microzig");
const mmio = microzig.mmio;

pub const CPU_Options = struct {
/// When true, the vector table lives in RAM.
ram_vector_table: bool = false,
};
const shared = @import("shared_types.zig");

pub const CPU_Options = shared.options.Ram_Vector_Options;

pub const scb_base_offset = 0x0d00;

Expand Down
11 changes: 5 additions & 6 deletions core/src/cpus/cortex_m/m3.zig
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
const microzig = @import("microzig");
const mmio = microzig.mmio;

pub const CPU_Options = struct {
/// When true, the vector table lives in RAM.
ram_vector_table: bool = false,
};
const shared = @import("shared_types.zig");

pub const CPU_Options = shared.options.Ram_Vector_Options;

pub const scb_base_offset = 0x0d00;

Expand Down Expand Up @@ -65,7 +64,7 @@ pub const SystemControlBlock = extern struct {
/// System Handlers Priority Registers.
SHPR: [3]u32,
/// System Handler Control and State Register.
SHCSR: u32,
SHCSR: mmio.Mmio(shared.scb.SHCSR),
/// Configurable Fault Status Register.
CFSR: mmio.Mmio(packed struct(u32) {
/// MemManage Fault Register.
Expand All @@ -76,7 +75,7 @@ pub const SystemControlBlock = extern struct {
UFSR: u16,
}),
/// HardFault Status Register.
HFSR: u32,
HFSR: mmio.Mmio(shared.scb.HFSR),
reserved1: u32 = 0,
/// MemManage Fault Address Register.
MMFAR: u32,
Expand Down
20 changes: 12 additions & 8 deletions core/src/cpus/cortex_m/m33.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
//! - ARM Cortex-M33 Reference: https://developer.arm.com/documentation/100230/latest/
const microzig = @import("microzig");
const mmio = microzig.mmio;
const shared = @import("shared_types.zig");

pub const CPU_Options = struct {
/// When true, the vector table lives in RAM.
ram_vector_table: bool = false,
pub const cpu_flags: shared.CpuFlags = .{
.has_bus_fault = true,
.has_mem_manage_fault = true,
.has_usage_fault = true,
};

pub const CPU_Options = shared.options.Ram_Vector_Options;

pub const scb_base_offset = 0x0cfc;

pub const SystemControlBlock = extern struct {
Expand Down Expand Up @@ -217,18 +221,18 @@ pub const SystemControlBlock = extern struct {
/// System Handler Priority Registers.
SHPR: [12]u8,
/// System Handler Control and State Register.
SHCSR: u32,
SHCSR: mmio.Mmio(shared.scb.SHCSR),
/// Configurable Fault Status Register.
CFSR: mmio.Mmio(packed struct(u32) {
/// MemManage Fault Register.
MMFSR: u8,
MMFSR: shared.scb.MMFSR,
/// BusFault Status Register.
BFSR: u8,
BFSR: shared.scb.BFSR,
/// Usage Fault Status Register.
UFSR: u16,
UFSR: shared.scb.UFSR,
}),
/// HardFault Status Register.
HFSR: u32,
HFSR: mmio.Mmio(shared.scb.HFSR),
reserved0: u32 = 0,
/// MemManage Fault Address Register.
MMFAR: u32,
Expand Down
17 changes: 8 additions & 9 deletions core/src/cpus/cortex_m/m4.zig
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
const microzig = @import("microzig");
const mmio = microzig.mmio;

pub const CPU_Options = struct {
/// When true, the vector table lives in RAM.
ram_vector_table: bool = false,
};
const shared = @import("shared_types.zig");

pub const CPU_Options = shared.options.Ram_Vector_Options;

pub const scb_base_offset = 0x0d00;

Expand Down Expand Up @@ -65,18 +64,18 @@ pub const SystemControlBlock = extern struct {
/// System Handlers Priority Registers.
SHP: [12]u8,
/// System Handler Contol and State Register.
SHCSR: u32,
SHCSR: mmio.Mmio(shared.scb.SHCSR),
/// Configurable Fault Status Register.
CFSR: mmio.Mmio(packed struct(u32) {
/// MemManage Fault Register.
MMFSR: u8,
MMFSR: shared.scb.MMFSR,
/// BusFault Status Register.
BFSR: u8,
BFSR: shared.scb.BFSR,
/// Usage Fault Status Register.
UFSR: u16,
UFSR: shared.scb.UFSR,
}),
/// HardFault Status Register.
HFSR: u32,
HFSR: mmio.Mmio(shared.scb.HFSR),
/// Debug Fault Status Register.
DFSR: u32,
/// MemManage Fault Address Register.
Expand Down
Loading
Loading