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