@@ -576,6 +576,7 @@ pub fn clrex() void {
576576}
577577
578578const vector_count = @sizeOf (microzig .chip .VectorTable ) / @sizeOf (usize );
579+ const mregs = @import ("m-profile.zig" );
579580
580581var ram_vectors : [vector_count ]usize align (256 ) = undefined ;
581582
@@ -630,6 +631,15 @@ pub const startup_logic = struct {
630631 @memcpy (data_start [0.. data_len ], data_src [0.. data_len ]);
631632 }
632633
634+ // We want the hardfault to be split into smaller parts:
635+ mregs .registers .system_control_block .shcrs .modify (.{
636+ .memfault_enabled = true ,
637+ .busfault_enabled = true ,
638+ .usagefault_enabled = true ,
639+ });
640+
641+ enable_fault_irq ();
642+
633643 // Move vector table to RAM if requested
634644 if (interrupt .has_ram_vectors ()) {
635645 // Copy vector table to RAM and set VTOR to point to it
@@ -638,7 +648,6 @@ pub const startup_logic = struct {
638648 @export (& ram_vectors , .{
639649 .name = "_ram_vectors" ,
640650 .section = "ram_vectors" ,
641- .linkage = .strong ,
642651 });
643652 } else {
644653 @export (& ram_vectors , .{
@@ -665,6 +674,12 @@ pub const startup_logic = struct {
665674 var tmp : VectorTable = .{
666675 .initial_stack_pointer = microzig .config .end_of_stack ,
667676 .Reset = .{ .c = microzig .cpu .startup_logic ._start },
677+
678+ .NMI = panic_handler ("NMI" ),
679+ .HardFault = panic_handler ("HardFault" ),
680+ .MemManageFault = panic_handler ("MemManageFault" ),
681+ .BusFault = make_fault_handler (default_bus_fault_handler ), // Exception 5
682+ .UsageFault = make_fault_handler (default_usage_fault_handler ), // Exception 6
668683 };
669684
670685 for (@typeInfo (@TypeOf (microzig_options .interrupts )).@"struct" .fields ) | field | {
@@ -676,6 +691,109 @@ pub const startup_logic = struct {
676691
677692 break :blk tmp ;
678693 };
694+
695+ fn panic_handler (comptime msg : []const u8 ) microzig.interrupt.Handler {
696+ const T = struct {
697+ fn do_panic () callconv (.C ) noreturn {
698+ @panic (msg );
699+ }
700+ };
701+
702+ return .{ .c = T .do_panic };
703+ }
704+
705+ const ContextStateFrame = extern struct {
706+ r0 : u32 ,
707+ r1 : u32 ,
708+ r2 : u32 ,
709+ r3 : u32 ,
710+ r12 : u32 ,
711+ lr : u32 ,
712+ return_address : u32 ,
713+ xpsr : u32 ,
714+ };
715+
716+ fn make_fault_handler (comptime handler : * const fn (context : * ContextStateFrame ) callconv (.C ) void ) microzig.interrupt.Handler {
717+ const T = struct {
718+ fn invoke () callconv (.C ) void {
719+ // See this article on how we use that:
720+ // https://interrupt.memfault.com/blog/cortex-m-hardfault-debug
721+ asm volatile (
722+ \\
723+ // Check 2th bit of LR.
724+ \\tst lr, #4
725+ // Do "if then else" equal
726+ \\ite eq
727+ // if equals, we use the MSP
728+ \\mrseq r0, msp
729+ // otherwise, we use the PSP
730+ \\mrsne r0, psp
731+ // Then we branch to our handler:
732+ \\b %[handler]
733+ :
734+ : [handler ] "s" (handler ),
735+ );
736+ }
737+ };
738+
739+ return .{ .c = T .invoke };
740+ }
741+
742+ const logger = std .log .scoped (.cortex_m );
743+
744+ fn default_bus_fault_handler (context : * ContextStateFrame ) callconv (.C ) void {
745+ const bfsr = mregs .registers .system_control_block .bfsr .read ();
746+
747+ logger .err ("Bus Fault:" , .{});
748+ logger .err (" context = r0:0x{X:0>8} r1:0x{X:0>8} r2:0x{X:0>8} r3:0x{X:0>8}" , .{
749+ context .r0 ,
750+ context .r1 ,
751+ context .r2 ,
752+ context .r3 ,
753+ });
754+ logger .err (" r12:0x{X:0>8} lr:0x{X:0>8} ra:0x{X:0>8} xpsr:0x{X:0>8}" , .{
755+ context .r12 ,
756+ context .lr ,
757+ context .return_address ,
758+ context .xpsr ,
759+ });
760+ logger .err (" instruction bus error = {}" , .{bfsr .instruction_bus_error });
761+ logger .err (" precice data bus error = {}" , .{bfsr .precice_data_bus_error });
762+ logger .err (" imprecice data bus error = {}" , .{bfsr .imprecice_data_bus_error });
763+ logger .err (" unstacking exception error = {}" , .{bfsr .unstacking_exception_error });
764+ logger .err (" exception stacking error = {}" , .{bfsr .exception_stacking_error });
765+ logger .err (" busfault address register valid = {}" , .{bfsr .busfault_address_register_valid });
766+ if (bfsr .busfault_address_register_valid ) {
767+ const address = mregs .registers .system_control_block .bfar .read ().ADDRESS ;
768+ logger .err (" busfault address register = 0x{X:0>8}" , .{address });
769+ }
770+ }
771+
772+ fn default_usage_fault_handler (context : * ContextStateFrame ) callconv (.C ) void {
773+ const ufsr = mregs .registers .system_control_block .ufsr .read ();
774+
775+ logger .err ("Usage Fault:" , .{});
776+ logger .err (" context = r0:0x{X:0>8} r1:0x{X:0>8} r2:0x{X:0>8} r3:0x{X:0>8}" , .{
777+ context .r0 ,
778+ context .r1 ,
779+ context .r2 ,
780+ context .r3 ,
781+ });
782+ logger .err (" r12:0x{X:0>8} lr:0x{X:0>8} ra:0x{X:0>8} xpsr:0x{X:0>8}" , .{
783+ context .r12 ,
784+ context .lr ,
785+ context .return_address ,
786+ context .xpsr ,
787+ });
788+ logger .err (" undefined instruction = {}" , .{ufsr .undefined_instruction });
789+ logger .err (" invalid state = {}" , .{ufsr .invalid_state });
790+ logger .err (" invalid pc load = {}" , .{ufsr .invalid_pc_load });
791+ logger .err (" missing coprocessor usage = {}" , .{ufsr .missing_coprocessor_usage });
792+ logger .err (" unaligned memory access = {}" , .{ufsr .unaligned_memory_access });
793+ logger .err (" divide by zero = {}" , .{ufsr .divide_by_zero });
794+
795+ @panic ("usage fault" );
796+ }
679797};
680798
681799fn is_ramimage () bool {
0 commit comments