1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! Enables the CPU's hardware floating-point / SIMD unit (VFP + NEON).
//!
//! The Cortex-A7 (BCM2836), the Cortex-A53 (BCM2837) and the
//! ARM1176JZF-S (BCM2835) all ship with the FP unit disabled out of
//! reset: executing any VFP or NEON instruction before enabling
//! coprocessor access traps -- an undefined instruction on AArch32, an
//! EL1 FP/SIMD trap on AArch64. What the unit *is* differs (VFPv2 on the
//! ARM1176, VFPv4 plus NEON on the later cores), but not how it is
//! switched on.
//!
//! `rt`'s boot sequence calls [`enable`] (via the
//! `rpi_hal_fpu_init` symbol below) on *every* core before any Rust code runs
//! -- the primary
//! core in `boot.s`/`boot64.s` and each secondary in its bring-up path --
//! so even a hard-float build, where the compiler may emit FP/SIMD
//! instructions in ordinary code (register spills, `memcpy`), cannot
//! execute one ahead of the enable.
//!
//! The default targets are soft-float, so ordinary float arithmetic
//! lowers to `compiler_builtins` software routines and never touches the
//! unit regardless: enabling it costs nothing until something is built
//! against a hard-float target (`armv7a-none-eabihf` /
//! `armv6-none-eabihf` / `aarch64-unknown-none`). See
//! `examples/fpu_demo.rs` for such a build,
//! including how to confirm hardware FP opcodes are actually emitted.
//!
//! One thing to keep in mind if that ever changes: with the unit enabled,
//! the FP/SIMD registers become live state. Nothing in this crate saves
//! or restores them across an interrupt today, which is fine because the
//! IRQ path is integer-only. If FP is ever used inside an interrupt
//! handler, or a preemptive scheduler is added that switches between
//! tasks that use FP, the ~32 D-registers + `FPSCR`/`FPCR` would need
//! saving on that boundary.
/// Enables the VFP/NEON floating-point unit on the calling core.
///
/// Idempotent and safe to call from any core at PL1 (AArch32) or EL1
/// (AArch64). `rt`'s boot code already calls this on every core before
/// `kmain`, so an application using the default features never needs to.
/// A consumer supplying their own boot sequence
/// (`default-features = false`) must call it themselves, on each core,
/// before running any hard-float code.
/// The enable primitive, also called directly from the boot stubs
/// (`boot.s`/`boot64.s`/`secondary64.s`) via its unmangled name, before
/// the first Rust call. `#[naked]` so its body is exactly the assembly
/// below with no compiler-inserted prologue/epilogue -- on a hard-float
/// build that guarantees the routine that *turns FP on* cannot itself be
/// the first thing to touch an FP register.
unsafe extern "C"
/// See the ARMv7-A sibling above; the sequence is the same one, in the
/// spellings ARMv6 has.
///
/// Two differences, both forced by the core rather than chosen. The
/// ARM1176JZF-S has VFPv2 and no NEON, so the `.fpu` directive names
/// that — `vmsr fpexc` encodes identically either way, but an assembler
/// told `neon-vfpv4` would accept instructions this core cannot run.
/// And `isb` is an ARMv7 mnemonic: ARMv6 has the same barrier only as a
/// CP15 operation (see [`crate::barrier`], which this cannot call — the
/// function is `naked`, so its body is exactly this assembly).
unsafe extern "C"
/// See the AArch32 sibling above.
unsafe extern "C"