Skip to content

Commit 2b600a4

Browse files
authored
cortex_m: Enable fpu on startup (#749)
1 parent 33da8f0 commit 2b600a4

File tree

19 files changed

+189
-103
lines changed

19 files changed

+189
-103
lines changed

core/src/cpus/cortex_m.zig

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,25 @@ pub const InterruptOptions = microzig.utilities.GenerateInterruptOptions(&.{
3333
.{ .InterruptEnum = Interrupt, .HandlerFn = Handler },
3434
});
3535

36-
/// Allowable `platform` options for microzig.options.
37-
pub const CPU_Options = core.CPU_Options;
36+
/// Allowable `cpu` options for microzig.options.
37+
pub const CPU_Options = struct {
38+
/// If true, interrupt vectors are moved to RAM so handlers can be set at runtime.
39+
///
40+
/// NOTE: Not supported on cortex_m0.
41+
ram_vector_table: bool = false,
42+
43+
/// If true, the Cortex-M interrupts will be initialized with a more verbose variant
44+
/// of the interrupt handlers which print the interrupt name.
45+
///
46+
/// NOTE: This option is enabled in debug builds by default.
47+
verbose_unhandled_irq: bool = (builtin.mode == .Debug),
48+
49+
/// If true, the FPU will be enabled in the startup code.
50+
///
51+
/// NOTE: This option is enabled by default if hard float is enabled.
52+
/// NOTE: Not supported on cortex_m0, cortex_m0plus and cortex_m3.
53+
enable_fpu: bool = builtin.abi.float() == .hard,
54+
};
3855

3956
/// External Interrupts
4057
/// These are the interrupts generated by the NVIC.
@@ -643,6 +660,37 @@ pub const atomic = struct {
643660
}
644661
};
645662

663+
/// Enables the FPU.
664+
///
665+
/// NOTE: This function is automatically called on cpu startup if the cpu has
666+
/// an fpu and hard float is enabled. HALs also call this in the startup of
667+
/// other cores.
668+
pub inline fn enable_fpu() void {
669+
switch (cortex_m) {
670+
inline .cortex_m0,
671+
.cortex_m0plus,
672+
.cortex_m3,
673+
=> |flavour| @compileError("FPU not supported on " ++ @tagName(flavour)),
674+
else => {},
675+
}
676+
677+
// Taken from the rust crate cortex-m-rt.
678+
asm volatile (
679+
\\ldr r0, =0xE000ED88
680+
\\ldr r1, =(0b1111 << 20)
681+
\\ldr r2, [r0]
682+
\\orr r2, r2, r1
683+
\\str r2, [r0]
684+
\\dsb
685+
\\isb
686+
::: .{
687+
.r0 = true,
688+
.r1 = true,
689+
.r2 = true,
690+
.memory = true,
691+
});
692+
}
693+
646694
/// The RAM vector table used. You can swap interrupt handlers at runtime here.
647695
/// Available when using a RAM vector table or a RAM image.
648696
pub var ram_vector_table: VectorTable align(256) = if (using_ram_vector_table or is_ram_image)
@@ -654,7 +702,7 @@ else
654702
pub const startup_logic = struct {
655703
extern fn microzig_main() noreturn;
656704

657-
pub fn ram_image_start() linksection("microzig_ram_start") callconv(.naked) void {
705+
pub fn ram_image_start() linksection("microzig_ram_start") callconv(.naked) noreturn {
658706
const eos = comptime microzig.utilities.get_end_of_stack();
659707
asm volatile (
660708
\\
@@ -672,6 +720,11 @@ pub const startup_logic = struct {
672720
microzig.utilities.initialize_system_memories(.auto);
673721

674722
if (using_ram_vector_table or is_ram_image) {
723+
switch (cortex_m) {
724+
.cortex_m0 => @compileError("RAM image and RAM vector table are not supported on cortex_m0"),
725+
else => {},
726+
}
727+
675728
asm volatile (
676729
\\
677730
// Set VTOR to point to ram table
@@ -684,6 +737,25 @@ pub const startup_logic = struct {
684737
: .{ .memory = true, .r0 = true, .r1 = true });
685738
}
686739

740+
if (fpu_present and microzig.options.cpu.enable_fpu) {
741+
enable_fpu();
742+
} else if (!fpu_present and microzig.options.cpu.enable_fpu) {
743+
@compileError(
744+
\\FPU enable requested though the chip doesn't appear to have an
745+
\\FPU. If your chip does have an FPU please add the `fpu_present`
746+
\\equal to `true` property to your chip file, either manually or via
747+
\\patches. If you want to use patches, you can use something like
748+
\\this:
749+
\\```
750+
\\.{ .set_device_property = .{
751+
\\ .device_name = "CHIP_NAME",
752+
\\ .key = "fpu_present",
753+
\\ .value = "true"
754+
\\} },
755+
\\```
756+
);
757+
}
758+
687759
if (@hasField(types.peripherals.SystemControlBlock, "SHCSR")) {
688760
// Enable distinction between MemFault, BusFault and UsageFault:
689761
peripherals.scb.SHCSR.modify(.{
@@ -694,7 +766,9 @@ pub const startup_logic = struct {
694766
enable_fault_irq();
695767
}
696768

697-
microzig_main();
769+
// If the compiler gets too aggressive with inlining we might get some
770+
// floating point operations before the FPU is enabled.
771+
@call(.never_inline, microzig_main, .{});
698772
}
699773

700774
// Validate that the VectorTable type has all the fault handlers that the CPU expects
@@ -970,10 +1044,17 @@ const scb_base = scs_base + core.scb_base_offset;
9701044
const mpu_base = scs_base + 0x0D90;
9711045
const fpu_base = scs_base + 0x0F34;
9721046

973-
const properties = microzig.chip.properties;
9741047
// TODO: will have to standardize this with regz code generation
975-
const mpu_present = @hasDecl(properties, "cpu.mpuPresent") and std.mem.eql(u8, properties.@"cpu.mpuPresent", "true");
976-
const fpu_present = @hasDecl(properties, "cpu.fpuPresent") and std.mem.eql(u8, properties.@"cpu.fpuPresent", "true");
1048+
const mpu_present = @hasDecl(microzig.chip, "properties") and
1049+
@hasDecl(microzig.chip.properties, "mpu_present") and
1050+
is_property_true(microzig.chip.properties.mpu_present);
1051+
const fpu_present = @hasDecl(microzig.chip, "properties") and
1052+
@hasDecl(microzig.chip.properties, "fpu_present") and
1053+
is_property_true(microzig.chip.properties.fpu_present);
1054+
1055+
fn is_property_true(value: []const u8) bool {
1056+
return std.mem.eql(u8, value, "true") or std.mem.eql(u8, value, "1");
1057+
}
9771058

9781059
const core = blk: {
9791060
break :blk switch (cortex_m) {

core/src/cpus/cortex_m/m0.zig

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,6 @@ const builtin = @import("builtin");
33
const mmio = microzig.mmio;
44
const shared = @import("shared_types.zig");
55

6-
pub const CPU_Options = struct {
7-
/// If true, the Cortex-M interrupts will be initialized with a more verbose variant
8-
/// of the interrupt handlers which print the interrupt name.
9-
///
10-
/// NOTE: This option is enabled in debug builds by default.
11-
verbose_unhandled_irq: bool = (builtin.mode == .Debug),
12-
};
13-
146
pub const scb_base_offset = 0x0d00;
157

168
pub const SystemControlBlock = extern struct {

core/src/cpus/cortex_m/m0plus.zig

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ const mmio = microzig.mmio;
33

44
const shared = @import("shared_types.zig");
55

6-
pub const CPU_Options = shared.options.Ram_Vector_Options;
7-
86
pub const scb_base_offset = 0x0d00;
97

108
pub const SystemControlBlock = extern struct {

core/src/cpus/cortex_m/m3.zig

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ const mmio = microzig.mmio;
33

44
const shared = @import("shared_types.zig");
55

6-
pub const CPU_Options = shared.options.Ram_Vector_Options;
7-
86
pub const scb_base_offset = 0x0d00;
97

108
pub const SystemControlBlock = extern struct {

core/src/cpus/cortex_m/m33.zig

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ pub const cpu_flags: shared.CpuFlags = .{
1010
.has_usage_fault = true,
1111
};
1212

13-
pub const CPU_Options = shared.options.Ram_Vector_Options;
14-
1513
pub const scb_base_offset = 0x0cfc;
1614

1715
pub const SystemControlBlock = extern struct {

core/src/cpus/cortex_m/m4.zig

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ const mmio = microzig.mmio;
33

44
const shared = @import("shared_types.zig");
55

6-
pub const CPU_Options = shared.options.Ram_Vector_Options;
7-
86
pub const scb_base_offset = 0x0d00;
97

108
pub const SystemControlBlock = extern struct {

core/src/cpus/cortex_m/m55.zig

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ const mmio = microzig.mmio;
55

66
const shared = @import("shared_types.zig");
77

8-
pub const CPU_Options = shared.options.Ram_Vector_Options;
9-
108
pub const scb_base_offset = 0x0cfc;
119

1210
pub const SystemControlBlock = extern struct {

core/src/cpus/cortex_m/m7.zig

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ const mmio = microzig.mmio;
33

44
const shared = @import("shared_types.zig");
55

6-
pub const CPU_Options = shared.options.Ram_Vector_Options;
7-
86
pub const scb_base_offset = 0x0d00;
97

108
pub const SystemControlBlock = extern struct {

core/src/cpus/cortex_m/shared_types.zig

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,6 @@ pub const CpuFlags = struct {
1111
has_mem_manage_fault: bool,
1212
};
1313

14-
pub const options = struct {
15-
pub const Ram_Vector_Options = struct {
16-
/// If true, interrupt vectors are moved to RAM so handlers can be set at runtime.
17-
ram_vector_table: bool = false,
18-
19-
/// If true, the Cortex-M interrupts will be initialized with a more verbose variant
20-
/// of the interrupt handlers which print the interrupt name.
21-
///
22-
/// NOTE: This option is enabled in debug builds by default.
23-
verbose_unhandled_irq: bool = (builtin.mode == .Debug),
24-
};
25-
};
26-
2714
pub const scb = struct {
2815
pub const SHCSR = packed struct(u32) {
2916
/// [0]

port/raspberrypi/rp2xxx/src/hal.zig

Lines changed: 19 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,6 @@ comptime {
4343
// HACK: tests can't access microzig. maybe there's a better way to do this.
4444
if (!builtin.is_test and compatibility.chip == .RP2350) {
4545
_ = bootmeta;
46-
47-
if (compatibility.arch == .arm and microzig.options.hal.use_dcp) {
48-
_ = dcp;
49-
}
5046
}
5147

5248
// On the RP2040, we need to import the `atomic.zig` file to export some global
@@ -69,12 +65,6 @@ pub const HAL_Options = switch (compatibility.chip) {
6965
next_block: ?*const anyopaque = null,
7066
} = .{},
7167

72-
/// Enable the FPU and lazy state preservation. Leads to much faster
73-
/// single precision floating point arithmetic. If you want a custom
74-
/// setup set this to false and configure the fpu yourself. Ignored on
75-
/// riscv.
76-
enable_fpu: bool = is_fpu_used,
77-
7868
/// Enable the DCP and export intrinsics. Leads to faster double
7969
/// precision floating point arithmetic. Ignored on riscv.
8070
use_dcp: bool = true,
@@ -94,12 +84,17 @@ pub inline fn init() void {
9484
init_sequence(clock_config);
9585
}
9686

97-
const is_fpu_used: bool = builtin.abi.float() == .hard;
98-
9987
/// Allows user to easily swap in their own clock config while still
10088
/// using the recommended initialization sequence
10189
pub fn init_sequence(comptime clock_cfg: clocks.config.Global) void {
102-
maybe_enable_fpu_and_dcp();
90+
if (compatibility.chip == .RP2350 and compatibility.arch == .arm and
91+
microzig.options.hal.use_dcp)
92+
{
93+
// Export double floating point intrinsics
94+
_ = dcp;
95+
96+
enable_dcp();
97+
}
10398

10499
// Disable the watchdog as a soft reset doesn't disable the WD automatically!
105100
watchdog.disable();
@@ -129,38 +124,19 @@ pub fn init_sequence(comptime clock_cfg: clocks.config.Global) void {
129124
resets.unreset_block_wait(resets.masks.all);
130125
}
131126

132-
/// Enables fpu and/or dcp on RP2350 arm if requested in HAL options. On RP2350
133-
/// riscv and RP2040 this is a noop. Called in init_sequence and on core1
127+
/// Enables dcp on RP2350 arm.
128+
///
129+
/// NOTE: Called automatically in the hal startup sequence and in core1
134130
/// startup.
135-
pub fn maybe_enable_fpu_and_dcp() void {
136-
if (compatibility.chip == .RP2350 and
137-
compatibility.arch == .arm)
138-
{
139-
if (microzig.options.hal.enable_fpu) {
140-
if (is_fpu_used) {
141-
// enable lazy state preservation
142-
microzig.cpu.peripherals.fpu.FPCCR.modify(.{
143-
.ASPEN = 1,
144-
.LSPEN = 1,
145-
});
146-
147-
// enable the FPU for the current core
148-
microzig.cpu.peripherals.scb.CPACR.modify(.{
149-
.CP10 = .full_access,
150-
.CP11 = .full_access,
151-
});
152-
} else {
153-
@compileError("target doesn't have FPU features enabled");
154-
}
155-
}
156-
157-
if (microzig.options.hal.use_dcp) {
158-
// enable the DCP for the current core
159-
microzig.cpu.peripherals.scb.CPACR.modify(.{
160-
.CP4 = .full_access,
161-
});
162-
}
131+
pub inline fn enable_dcp() void {
132+
if (!(compatibility.chip == .RP2350 and compatibility.arch == .arm)) {
133+
@compileError("DCP is only available on RP2350 arm");
163134
}
135+
136+
// enable the DCP for the current core
137+
microzig.cpu.peripherals.scb.CPACR.modify(.{
138+
.CP4 = .full_access,
139+
});
164140
}
165141

166142
pub fn get_cpu_id() u32 {

0 commit comments

Comments
 (0)