aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp/src/float
diff options
context:
space:
mode:
authorpennae <[email protected]>2023-04-19 01:57:37 +0200
committerpennae <[email protected]>2023-04-19 23:04:47 +0200
commitfdd6e08ed6b5445a53c8cd0752f80f203c4860ac (patch)
tree9b997c5db2d55c911a6fd4cfdce38141e372b762 /embassy-rp/src/float
parenta673b9aa294a55c441f3f61c48481484629b4347 (diff)
rp: hook up softfloat rom intrinsics
rp-hal has done this very well already, so we'll just copy their entire impl again. only div.rs needed some massaging because our sio access works a little differently, everything else worked as is.
Diffstat (limited to 'embassy-rp/src/float')
-rw-r--r--embassy-rp/src/float/add_sub.rs92
-rw-r--r--embassy-rp/src/float/cmp.rs201
-rw-r--r--embassy-rp/src/float/conv.rs157
-rw-r--r--embassy-rp/src/float/div.rs141
-rw-r--r--embassy-rp/src/float/functions.rs239
-rw-r--r--embassy-rp/src/float/mod.rs149
-rw-r--r--embassy-rp/src/float/mul.rs70
7 files changed, 1049 insertions, 0 deletions
diff --git a/embassy-rp/src/float/add_sub.rs b/embassy-rp/src/float/add_sub.rs
new file mode 100644
index 000000000..673544cfe
--- /dev/null
+++ b/embassy-rp/src/float/add_sub.rs
@@ -0,0 +1,92 @@
1// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/add_sub.rs
3
4use super::{Float, Int};
5use crate::rom_data;
6
7trait ROMAdd {
8 fn rom_add(self, b: Self) -> Self;
9}
10
11impl ROMAdd for f32 {
12 fn rom_add(self, b: Self) -> Self {
13 rom_data::float_funcs::fadd(self, b)
14 }
15}
16
17impl ROMAdd for f64 {
18 fn rom_add(self, b: Self) -> Self {
19 rom_data::double_funcs::dadd(self, b)
20 }
21}
22
23fn add<F: Float + ROMAdd>(a: F, b: F) -> F {
24 if a.is_not_finite() {
25 if b.is_not_finite() {
26 let class_a = a.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK);
27 let class_b = b.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK);
28
29 if class_a == F::Int::ZERO && class_b == F::Int::ZERO {
30 // inf + inf = inf
31 return a;
32 }
33 if class_a == F::SIGN_MASK && class_b == F::SIGN_MASK {
34 // -inf + (-inf) = -inf
35 return a;
36 }
37
38 // Sign mismatch, or either is NaN already
39 return F::NAN;
40 }
41
42 // [-]inf/NaN + X = [-]inf/NaN
43 return a;
44 }
45
46 if b.is_not_finite() {
47 // X + [-]inf/NaN = [-]inf/NaN
48 return b;
49 }
50
51 a.rom_add(b)
52}
53
54intrinsics! {
55 #[alias = __addsf3vfp]
56 #[aeabi = __aeabi_fadd]
57 extern "C" fn __addsf3(a: f32, b: f32) -> f32 {
58 add(a, b)
59 }
60
61 #[bootrom_v2]
62 #[alias = __adddf3vfp]
63 #[aeabi = __aeabi_dadd]
64 extern "C" fn __adddf3(a: f64, b: f64) -> f64 {
65 add(a, b)
66 }
67
68 // The ROM just implements subtraction the same way, so just do it here
69 // and save the work of implementing more complicated NaN/inf handling.
70
71 #[alias = __subsf3vfp]
72 #[aeabi = __aeabi_fsub]
73 extern "C" fn __subsf3(a: f32, b: f32) -> f32 {
74 add(a, -b)
75 }
76
77 #[bootrom_v2]
78 #[alias = __subdf3vfp]
79 #[aeabi = __aeabi_dsub]
80 extern "C" fn __subdf3(a: f64, b: f64) -> f64 {
81 add(a, -b)
82 }
83
84 extern "aapcs" fn __aeabi_frsub(a: f32, b: f32) -> f32 {
85 add(b, -a)
86 }
87
88 #[bootrom_v2]
89 extern "aapcs" fn __aeabi_drsub(a: f64, b: f64) -> f64 {
90 add(b, -a)
91 }
92}
diff --git a/embassy-rp/src/float/cmp.rs b/embassy-rp/src/float/cmp.rs
new file mode 100644
index 000000000..e540e3918
--- /dev/null
+++ b/embassy-rp/src/float/cmp.rs
@@ -0,0 +1,201 @@
1// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/cmp.rs
3
4use super::Float;
5use crate::rom_data;
6
7trait ROMCmp {
8 fn rom_cmp(self, b: Self) -> i32;
9}
10
11impl ROMCmp for f32 {
12 fn rom_cmp(self, b: Self) -> i32 {
13 rom_data::float_funcs::fcmp(self, b)
14 }
15}
16
17impl ROMCmp for f64 {
18 fn rom_cmp(self, b: Self) -> i32 {
19 rom_data::double_funcs::dcmp(self, b)
20 }
21}
22
23fn le_abi<F: Float + ROMCmp>(a: F, b: F) -> i32 {
24 if a.is_nan() || b.is_nan() {
25 1
26 } else {
27 a.rom_cmp(b)
28 }
29}
30
31fn ge_abi<F: Float + ROMCmp>(a: F, b: F) -> i32 {
32 if a.is_nan() || b.is_nan() {
33 -1
34 } else {
35 a.rom_cmp(b)
36 }
37}
38
39intrinsics! {
40 #[slower_than_default]
41 #[bootrom_v2]
42 #[alias = __eqsf2, __ltsf2, __nesf2]
43 extern "C" fn __lesf2(a: f32, b: f32) -> i32 {
44 le_abi(a, b)
45 }
46
47 #[slower_than_default]
48 #[bootrom_v2]
49 #[alias = __eqdf2, __ltdf2, __nedf2]
50 extern "C" fn __ledf2(a: f64, b: f64) -> i32 {
51 le_abi(a, b)
52 }
53
54 #[slower_than_default]
55 #[bootrom_v2]
56 #[alias = __gtsf2]
57 extern "C" fn __gesf2(a: f32, b: f32) -> i32 {
58 ge_abi(a, b)
59 }
60
61 #[slower_than_default]
62 #[bootrom_v2]
63 #[alias = __gtdf2]
64 extern "C" fn __gedf2(a: f64, b: f64) -> i32 {
65 ge_abi(a, b)
66 }
67
68
69 #[slower_than_default]
70 #[bootrom_v2]
71 extern "aapcs" fn __aeabi_fcmple(a: f32, b: f32) -> i32 {
72 (le_abi(a, b) <= 0) as i32
73 }
74
75 #[slower_than_default]
76 #[bootrom_v2]
77 extern "aapcs" fn __aeabi_fcmpge(a: f32, b: f32) -> i32 {
78 (ge_abi(a, b) >= 0) as i32
79 }
80
81 #[slower_than_default]
82 #[bootrom_v2]
83 extern "aapcs" fn __aeabi_fcmpeq(a: f32, b: f32) -> i32 {
84 (le_abi(a, b) == 0) as i32
85 }
86
87 #[slower_than_default]
88 #[bootrom_v2]
89 extern "aapcs" fn __aeabi_fcmplt(a: f32, b: f32) -> i32 {
90 (le_abi(a, b) < 0) as i32
91 }
92
93 #[slower_than_default]
94 #[bootrom_v2]
95 extern "aapcs" fn __aeabi_fcmpgt(a: f32, b: f32) -> i32 {
96 (ge_abi(a, b) > 0) as i32
97 }
98
99 #[slower_than_default]
100 #[bootrom_v2]
101 extern "aapcs" fn __aeabi_dcmple(a: f64, b: f64) -> i32 {
102 (le_abi(a, b) <= 0) as i32
103 }
104
105 #[slower_than_default]
106 #[bootrom_v2]
107 extern "aapcs" fn __aeabi_dcmpge(a: f64, b: f64) -> i32 {
108 (ge_abi(a, b) >= 0) as i32
109 }
110
111 #[slower_than_default]
112 #[bootrom_v2]
113 extern "aapcs" fn __aeabi_dcmpeq(a: f64, b: f64) -> i32 {
114 (le_abi(a, b) == 0) as i32
115 }
116
117 #[slower_than_default]
118 #[bootrom_v2]
119 extern "aapcs" fn __aeabi_dcmplt(a: f64, b: f64) -> i32 {
120 (le_abi(a, b) < 0) as i32
121 }
122
123 #[slower_than_default]
124 #[bootrom_v2]
125 extern "aapcs" fn __aeabi_dcmpgt(a: f64, b: f64) -> i32 {
126 (ge_abi(a, b) > 0) as i32
127 }
128
129
130 #[slower_than_default]
131 #[bootrom_v2]
132 extern "C" fn __gesf2vfp(a: f32, b: f32) -> i32 {
133 (ge_abi(a, b) >= 0) as i32
134 }
135
136 #[slower_than_default]
137 #[bootrom_v2]
138 extern "C" fn __gedf2vfp(a: f64, b: f64) -> i32 {
139 (ge_abi(a, b) >= 0) as i32
140 }
141
142 #[slower_than_default]
143 #[bootrom_v2]
144 extern "C" fn __gtsf2vfp(a: f32, b: f32) -> i32 {
145 (ge_abi(a, b) > 0) as i32
146 }
147
148 #[slower_than_default]
149 #[bootrom_v2]
150 extern "C" fn __gtdf2vfp(a: f64, b: f64) -> i32 {
151 (ge_abi(a, b) > 0) as i32
152 }
153
154 #[slower_than_default]
155 #[bootrom_v2]
156 extern "C" fn __ltsf2vfp(a: f32, b: f32) -> i32 {
157 (le_abi(a, b) < 0) as i32
158 }
159
160 #[slower_than_default]
161 #[bootrom_v2]
162 extern "C" fn __ltdf2vfp(a: f64, b: f64) -> i32 {
163 (le_abi(a, b) < 0) as i32
164 }
165
166 #[slower_than_default]
167 #[bootrom_v2]
168 extern "C" fn __lesf2vfp(a: f32, b: f32) -> i32 {
169 (le_abi(a, b) <= 0) as i32
170 }
171
172 #[slower_than_default]
173 #[bootrom_v2]
174 extern "C" fn __ledf2vfp(a: f64, b: f64) -> i32 {
175 (le_abi(a, b) <= 0) as i32
176 }
177
178 #[slower_than_default]
179 #[bootrom_v2]
180 extern "C" fn __nesf2vfp(a: f32, b: f32) -> i32 {
181 (le_abi(a, b) != 0) as i32
182 }
183
184 #[slower_than_default]
185 #[bootrom_v2]
186 extern "C" fn __nedf2vfp(a: f64, b: f64) -> i32 {
187 (le_abi(a, b) != 0) as i32
188 }
189
190 #[slower_than_default]
191 #[bootrom_v2]
192 extern "C" fn __eqsf2vfp(a: f32, b: f32) -> i32 {
193 (le_abi(a, b) == 0) as i32
194 }
195
196 #[slower_than_default]
197 #[bootrom_v2]
198 extern "C" fn __eqdf2vfp(a: f64, b: f64) -> i32 {
199 (le_abi(a, b) == 0) as i32
200 }
201}
diff --git a/embassy-rp/src/float/conv.rs b/embassy-rp/src/float/conv.rs
new file mode 100644
index 000000000..021826e28
--- /dev/null
+++ b/embassy-rp/src/float/conv.rs
@@ -0,0 +1,157 @@
1// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/conv.rs
3
4use super::Float;
5use crate::rom_data;
6
7// Some of these are also not connected in the Pico SDK. This is probably
8// because the ROM version actually does a fixed point conversion, just with
9// the fractional width set to zero.
10
11intrinsics! {
12 // Not connected in the Pico SDK
13 #[slower_than_default]
14 #[aeabi = __aeabi_i2f]
15 extern "C" fn __floatsisf(i: i32) -> f32 {
16 rom_data::float_funcs::int_to_float(i)
17 }
18
19 // Not connected in the Pico SDK
20 #[slower_than_default]
21 #[aeabi = __aeabi_i2d]
22 extern "C" fn __floatsidf(i: i32) -> f64 {
23 rom_data::double_funcs::int_to_double(i)
24 }
25
26 // Questionable gain
27 #[aeabi = __aeabi_l2f]
28 extern "C" fn __floatdisf(i: i64) -> f32 {
29 rom_data::float_funcs::int64_to_float(i)
30 }
31
32 #[bootrom_v2]
33 #[aeabi = __aeabi_l2d]
34 extern "C" fn __floatdidf(i: i64) -> f64 {
35 rom_data::double_funcs::int64_to_double(i)
36 }
37
38 // Not connected in the Pico SDK
39 #[slower_than_default]
40 #[aeabi = __aeabi_ui2f]
41 extern "C" fn __floatunsisf(i: u32) -> f32 {
42 rom_data::float_funcs::uint_to_float(i)
43 }
44
45 // Questionable gain
46 #[bootrom_v2]
47 #[aeabi = __aeabi_ui2d]
48 extern "C" fn __floatunsidf(i: u32) -> f64 {
49 rom_data::double_funcs::uint_to_double(i)
50 }
51
52 // Questionable gain
53 #[bootrom_v2]
54 #[aeabi = __aeabi_ul2f]
55 extern "C" fn __floatundisf(i: u64) -> f32 {
56 rom_data::float_funcs::uint64_to_float(i)
57 }
58
59 #[bootrom_v2]
60 #[aeabi = __aeabi_ul2d]
61 extern "C" fn __floatundidf(i: u64) -> f64 {
62 rom_data::double_funcs::uint64_to_double(i)
63 }
64
65
66 // The Pico SDK does some optimization here (e.x. fast paths for zero and
67 // one), but we can just directly connect it.
68 #[aeabi = __aeabi_f2iz]
69 extern "C" fn __fixsfsi(f: f32) -> i32 {
70 rom_data::float_funcs::float_to_int(f)
71 }
72
73 #[bootrom_v2]
74 #[aeabi = __aeabi_f2lz]
75 extern "C" fn __fixsfdi(f: f32) -> i64 {
76 rom_data::float_funcs::float_to_int64(f)
77 }
78
79 // Not connected in the Pico SDK
80 #[slower_than_default]
81 #[bootrom_v2]
82 #[aeabi = __aeabi_d2iz]
83 extern "C" fn __fixdfsi(f: f64) -> i32 {
84 rom_data::double_funcs::double_to_int(f)
85 }
86
87 // Like with the 32 bit version, there's optimization that we just
88 // skip.
89 #[bootrom_v2]
90 #[aeabi = __aeabi_d2lz]
91 extern "C" fn __fixdfdi(f: f64) -> i64 {
92 rom_data::double_funcs::double_to_int64(f)
93 }
94
95 #[slower_than_default]
96 #[aeabi = __aeabi_f2uiz]
97 extern "C" fn __fixunssfsi(f: f32) -> u32 {
98 rom_data::float_funcs::float_to_uint(f)
99 }
100
101 #[slower_than_default]
102 #[bootrom_v2]
103 #[aeabi = __aeabi_f2ulz]
104 extern "C" fn __fixunssfdi(f: f32) -> u64 {
105 rom_data::float_funcs::float_to_uint64(f)
106 }
107
108 #[slower_than_default]
109 #[bootrom_v2]
110 #[aeabi = __aeabi_d2uiz]
111 extern "C" fn __fixunsdfsi(f: f64) -> u32 {
112 rom_data::double_funcs::double_to_uint(f)
113 }
114
115 #[slower_than_default]
116 #[bootrom_v2]
117 #[aeabi = __aeabi_d2ulz]
118 extern "C" fn __fixunsdfdi(f: f64) -> u64 {
119 rom_data::double_funcs::double_to_uint64(f)
120 }
121
122 #[bootrom_v2]
123 #[alias = __extendsfdf2vfp]
124 #[aeabi = __aeabi_f2d]
125 extern "C" fn __extendsfdf2(f: f32) -> f64 {
126 if f.is_not_finite() {
127 return f64::from_repr(
128 // Not finite
129 f64::EXPONENT_MASK |
130 // Preserve NaN or inf
131 ((f.repr() & f32::SIGNIFICAND_MASK) as u64) |
132 // Preserve sign
133 ((f.repr() & f32::SIGN_MASK) as u64) << (f64::BITS-f32::BITS)
134 );
135 }
136 rom_data::float_funcs::float_to_double(f)
137 }
138
139 #[bootrom_v2]
140 #[alias = __truncdfsf2vfp]
141 #[aeabi = __aeabi_d2f]
142 extern "C" fn __truncdfsf2(f: f64) -> f32 {
143 if f.is_not_finite() {
144 let mut repr: u32 =
145 // Not finite
146 f32::EXPONENT_MASK |
147 // Preserve sign
148 ((f.repr() & f64::SIGN_MASK) >> (f64::BITS-f32::BITS)) as u32;
149 // Set NaN
150 if (f.repr() & f64::SIGNIFICAND_MASK) != 0 {
151 repr |= 1;
152 }
153 return f32::from_repr(repr);
154 }
155 rom_data::double_funcs::double_to_float(f)
156 }
157}
diff --git a/embassy-rp/src/float/div.rs b/embassy-rp/src/float/div.rs
new file mode 100644
index 000000000..094dec446
--- /dev/null
+++ b/embassy-rp/src/float/div.rs
@@ -0,0 +1,141 @@
1// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/conv.rs
3
4use super::Float;
5use crate::rom_data;
6
7// Make sure this stays as a separate call, because when it's inlined the
8// compiler will move the save of the registers used to contain the divider
9// state into the function prologue. That save and restore (push/pop) takes
10// longer than the actual division, so doing it in the common case where
11// they are not required wastes a lot of time.
12#[inline(never)]
13#[cold]
14fn save_divider_and_call<F, R>(f: F) -> R
15where
16 F: FnOnce() -> R,
17{
18 let sio = rp_pac::SIO;
19
20 unsafe {
21 // Since we can't save the signed-ness of the calculation, we have to make
22 // sure that there's at least an 8 cycle delay before we read the result.
23 // The Pico SDK ensures this by using a 6 cycle push and two 1 cycle reads.
24 // Since we can't be sure the Rust implementation will optimize to the same,
25 // just use an explicit wait.
26 while !sio.div().csr().read().ready() {}
27
28 // Read the quotient last, since that's what clears the dirty flag
29 let dividend = sio.div().udividend().read();
30 let divisor = sio.div().udivisor().read();
31 let remainder = sio.div().remainder().read();
32 let quotient = sio.div().quotient().read();
33
34 // If we get interrupted here (before a write sets the DIRTY flag) its fine, since
35 // we have the full state, so the interruptor doesn't have to restore it. Once the
36 // write happens and the DIRTY flag is set, the interruptor becomes responsible for
37 // restoring our state.
38 let result = f();
39
40 // If we are interrupted here, then the interruptor will start an incorrect calculation
41 // using a wrong divisor, but we'll restore the divisor and result ourselves correctly.
42 // This sets DIRTY, so any interruptor will save the state.
43 sio.div().udividend().write_value(dividend);
44 // If we are interrupted here, the the interruptor may start the calculation using
45 // incorrectly signed inputs, but we'll restore the result ourselves.
46 // This sets DIRTY, so any interruptor will save the state.
47 sio.div().udivisor().write_value(divisor);
48 // If we are interrupted here, the interruptor will have restored everything but the
49 // quotient may be wrongly signed. If the calculation started by the above writes is
50 // still ongoing it is stopped, so it won't replace the result we're restoring.
51 // DIRTY and READY set, but only DIRTY matters to make the interruptor save the state.
52 sio.div().remainder().write_value(remainder);
53 // State fully restored after the quotient write. This sets both DIRTY and READY, so
54 // whatever we may have interrupted can read the result.
55 sio.div().quotient().write_value(quotient);
56
57 result
58 }
59}
60
61fn save_divider<F, R>(f: F) -> R
62where
63 F: FnOnce() -> R,
64{
65 let sio = rp_pac::SIO;
66 if unsafe { !sio.div().csr().read().dirty() } {
67 // Not dirty, so nothing is waiting for the calculation. So we can just
68 // issue it directly without a save/restore.
69 f()
70 } else {
71 save_divider_and_call(f)
72 }
73}
74
75trait ROMDiv {
76 fn rom_div(self, b: Self) -> Self;
77}
78
79impl ROMDiv for f32 {
80 fn rom_div(self, b: Self) -> Self {
81 // ROM implementation uses the hardware divider, so we have to save it
82 save_divider(|| rom_data::float_funcs::fdiv(self, b))
83 }
84}
85
86impl ROMDiv for f64 {
87 fn rom_div(self, b: Self) -> Self {
88 // ROM implementation uses the hardware divider, so we have to save it
89 save_divider(|| rom_data::double_funcs::ddiv(self, b))
90 }
91}
92
93fn div<F: Float + ROMDiv>(a: F, b: F) -> F {
94 if a.is_not_finite() {
95 if b.is_not_finite() {
96 // inf/NaN / inf/NaN = NaN
97 return F::NAN;
98 }
99
100 if b.is_zero() {
101 // inf/NaN / 0 = NaN
102 return F::NAN;
103 }
104
105 return if b.is_sign_negative() {
106 // [+/-]inf/NaN / (-X) = [-/+]inf/NaN
107 a.negate()
108 } else {
109 // [-]inf/NaN / X = [-]inf/NaN
110 a
111 };
112 }
113
114 if b.is_nan() {
115 // X / NaN = NaN
116 return b;
117 }
118
119 // ROM handles X / 0 = [-]inf and X / [-]inf = [-]0, so we only
120 // need to catch 0 / 0
121 if b.is_zero() && a.is_zero() {
122 return F::NAN;
123 }
124
125 a.rom_div(b)
126}
127
128intrinsics! {
129 #[alias = __divsf3vfp]
130 #[aeabi = __aeabi_fdiv]
131 extern "C" fn __divsf3(a: f32, b: f32) -> f32 {
132 div(a, b)
133 }
134
135 #[bootrom_v2]
136 #[alias = __divdf3vfp]
137 #[aeabi = __aeabi_ddiv]
138 extern "C" fn __divdf3(a: f64, b: f64) -> f64 {
139 div(a, b)
140 }
141}
diff --git a/embassy-rp/src/float/functions.rs b/embassy-rp/src/float/functions.rs
new file mode 100644
index 000000000..de29ce336
--- /dev/null
+++ b/embassy-rp/src/float/functions.rs
@@ -0,0 +1,239 @@
1// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/functions.rs
3
4use crate::float::{Float, Int};
5use crate::rom_data;
6
7trait ROMFunctions {
8 fn sqrt(self) -> Self;
9 fn ln(self) -> Self;
10 fn exp(self) -> Self;
11 fn sin(self) -> Self;
12 fn cos(self) -> Self;
13 fn tan(self) -> Self;
14 fn atan2(self, y: Self) -> Self;
15
16 fn to_trig_range(self) -> Self;
17}
18
19impl ROMFunctions for f32 {
20 fn sqrt(self) -> Self {
21 rom_data::float_funcs::fsqrt(self)
22 }
23
24 fn ln(self) -> Self {
25 rom_data::float_funcs::fln(self)
26 }
27
28 fn exp(self) -> Self {
29 rom_data::float_funcs::fexp(self)
30 }
31
32 fn sin(self) -> Self {
33 rom_data::float_funcs::fsin(self)
34 }
35
36 fn cos(self) -> Self {
37 rom_data::float_funcs::fcos(self)
38 }
39
40 fn tan(self) -> Self {
41 rom_data::float_funcs::ftan(self)
42 }
43
44 fn atan2(self, y: Self) -> Self {
45 rom_data::float_funcs::fatan2(self, y)
46 }
47
48 fn to_trig_range(self) -> Self {
49 // -128 < X < 128, logic from the Pico SDK
50 let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS;
51 if exponent < 134 {
52 self
53 } else {
54 self % (core::f32::consts::PI * 2.0)
55 }
56 }
57}
58
59impl ROMFunctions for f64 {
60 fn sqrt(self) -> Self {
61 rom_data::double_funcs::dsqrt(self)
62 }
63
64 fn ln(self) -> Self {
65 rom_data::double_funcs::dln(self)
66 }
67
68 fn exp(self) -> Self {
69 rom_data::double_funcs::dexp(self)
70 }
71
72 fn sin(self) -> Self {
73 rom_data::double_funcs::dsin(self)
74 }
75
76 fn cos(self) -> Self {
77 rom_data::double_funcs::dcos(self)
78 }
79 fn tan(self) -> Self {
80 rom_data::double_funcs::dtan(self)
81 }
82
83 fn atan2(self, y: Self) -> Self {
84 rom_data::double_funcs::datan2(self, y)
85 }
86
87 fn to_trig_range(self) -> Self {
88 // -1024 < X < 1024, logic from the Pico SDK
89 let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS;
90 if exponent < 1033 {
91 self
92 } else {
93 self % (core::f64::consts::PI * 2.0)
94 }
95 }
96}
97
98fn is_negative_nonzero_or_nan<F: Float>(f: F) -> bool {
99 let repr = f.repr();
100 if (repr & F::SIGN_MASK) != F::Int::ZERO {
101 // Negative, so anything other than exactly zero
102 return (repr & (!F::SIGN_MASK)) != F::Int::ZERO;
103 }
104 // NaN
105 (repr & (F::EXPONENT_MASK | F::SIGNIFICAND_MASK)) > F::EXPONENT_MASK
106}
107
108fn sqrt<F: Float + ROMFunctions>(f: F) -> F {
109 if is_negative_nonzero_or_nan(f) {
110 F::NAN
111 } else {
112 f.sqrt()
113 }
114}
115
116fn ln<F: Float + ROMFunctions>(f: F) -> F {
117 if is_negative_nonzero_or_nan(f) {
118 F::NAN
119 } else {
120 f.ln()
121 }
122}
123
124fn exp<F: Float + ROMFunctions>(f: F) -> F {
125 if f.is_nan() {
126 F::NAN
127 } else {
128 f.exp()
129 }
130}
131
132fn sin<F: Float + ROMFunctions>(f: F) -> F {
133 if f.is_not_finite() {
134 F::NAN
135 } else {
136 f.to_trig_range().sin()
137 }
138}
139
140fn cos<F: Float + ROMFunctions>(f: F) -> F {
141 if f.is_not_finite() {
142 F::NAN
143 } else {
144 f.to_trig_range().cos()
145 }
146}
147
148fn tan<F: Float + ROMFunctions>(f: F) -> F {
149 if f.is_not_finite() {
150 F::NAN
151 } else {
152 f.to_trig_range().tan()
153 }
154}
155
156fn atan2<F: Float + ROMFunctions>(x: F, y: F) -> F {
157 if x.is_nan() || y.is_nan() {
158 F::NAN
159 } else {
160 x.to_trig_range().atan2(y)
161 }
162}
163
164// Name collisions
165mod intrinsics {
166 intrinsics! {
167 extern "C" fn sqrtf(f: f32) -> f32 {
168 super::sqrt(f)
169 }
170
171 #[bootrom_v2]
172 extern "C" fn sqrt(f: f64) -> f64 {
173 super::sqrt(f)
174 }
175
176 extern "C" fn logf(f: f32) -> f32 {
177 super::ln(f)
178 }
179
180 #[bootrom_v2]
181 extern "C" fn log(f: f64) -> f64 {
182 super::ln(f)
183 }
184
185 extern "C" fn expf(f: f32) -> f32 {
186 super::exp(f)
187 }
188
189 #[bootrom_v2]
190 extern "C" fn exp(f: f64) -> f64 {
191 super::exp(f)
192 }
193
194 #[slower_than_default]
195 extern "C" fn sinf(f: f32) -> f32 {
196 super::sin(f)
197 }
198
199 #[slower_than_default]
200 #[bootrom_v2]
201 extern "C" fn sin(f: f64) -> f64 {
202 super::sin(f)
203 }
204
205 #[slower_than_default]
206 extern "C" fn cosf(f: f32) -> f32 {
207 super::cos(f)
208 }
209
210 #[slower_than_default]
211 #[bootrom_v2]
212 extern "C" fn cos(f: f64) -> f64 {
213 super::cos(f)
214 }
215
216 #[slower_than_default]
217 extern "C" fn tanf(f: f32) -> f32 {
218 super::tan(f)
219 }
220
221 #[slower_than_default]
222 #[bootrom_v2]
223 extern "C" fn tan(f: f64) -> f64 {
224 super::tan(f)
225 }
226
227 // Questionable gain
228 #[bootrom_v2]
229 extern "C" fn atan2f(a: f32, b: f32) -> f32 {
230 super::atan2(a, b)
231 }
232
233 // Questionable gain
234 #[bootrom_v2]
235 extern "C" fn atan2(a: f64, b: f64) -> f64 {
236 super::atan2(a, b)
237 }
238 }
239}
diff --git a/embassy-rp/src/float/mod.rs b/embassy-rp/src/float/mod.rs
new file mode 100644
index 000000000..945afff90
--- /dev/null
+++ b/embassy-rp/src/float/mod.rs
@@ -0,0 +1,149 @@
1// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mod.rs
3
4use core::ops;
5
6// Borrowed and simplified from compiler-builtins so we can use bit ops
7// on floating point without macro soup.
8pub(crate) trait Int:
9 Copy
10 + core::fmt::Debug
11 + PartialEq
12 + PartialOrd
13 + ops::AddAssign
14 + ops::SubAssign
15 + ops::BitAndAssign
16 + ops::BitOrAssign
17 + ops::BitXorAssign
18 + ops::ShlAssign<i32>
19 + ops::ShrAssign<u32>
20 + ops::Add<Output = Self>
21 + ops::Sub<Output = Self>
22 + ops::Div<Output = Self>
23 + ops::Shl<u32, Output = Self>
24 + ops::Shr<u32, Output = Self>
25 + ops::BitOr<Output = Self>
26 + ops::BitXor<Output = Self>
27 + ops::BitAnd<Output = Self>
28 + ops::Not<Output = Self>
29{
30 const ZERO: Self;
31}
32
33macro_rules! int_impl {
34 ($ty:ty) => {
35 impl Int for $ty {
36 const ZERO: Self = 0;
37 }
38 };
39}
40
41int_impl!(u32);
42int_impl!(u64);
43
44pub(crate) trait Float:
45 Copy
46 + core::fmt::Debug
47 + PartialEq
48 + PartialOrd
49 + ops::AddAssign
50 + ops::MulAssign
51 + ops::Add<Output = Self>
52 + ops::Sub<Output = Self>
53 + ops::Div<Output = Self>
54 + ops::Rem<Output = Self>
55{
56 /// A uint of the same with as the float
57 type Int: Int;
58
59 /// NaN representation for the float
60 const NAN: Self;
61
62 /// The bitwidth of the float type
63 const BITS: u32;
64
65 /// The bitwidth of the significand
66 const SIGNIFICAND_BITS: u32;
67
68 /// A mask for the sign bit
69 const SIGN_MASK: Self::Int;
70
71 /// A mask for the significand
72 const SIGNIFICAND_MASK: Self::Int;
73
74 /// A mask for the exponent
75 const EXPONENT_MASK: Self::Int;
76
77 /// Returns `self` transmuted to `Self::Int`
78 fn repr(self) -> Self::Int;
79
80 /// Returns a `Self::Int` transmuted back to `Self`
81 fn from_repr(a: Self::Int) -> Self;
82
83 /// Return a sign swapped `self`
84 fn negate(self) -> Self;
85
86 /// Returns true if `self` is either NaN or infinity
87 fn is_not_finite(self) -> bool {
88 (self.repr() & Self::EXPONENT_MASK) == Self::EXPONENT_MASK
89 }
90
91 /// Returns true if `self` is infinity
92 fn is_infinity(self) -> bool {
93 (self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) == Self::EXPONENT_MASK
94 }
95
96 /// Returns true if `self is NaN
97 fn is_nan(self) -> bool {
98 (self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) > Self::EXPONENT_MASK
99 }
100
101 /// Returns true if `self` is negative
102 fn is_sign_negative(self) -> bool {
103 (self.repr() & Self::SIGN_MASK) != Self::Int::ZERO
104 }
105
106 /// Returns true if `self` is zero (either sign)
107 fn is_zero(self) -> bool {
108 (self.repr() & (Self::SIGNIFICAND_MASK | Self::EXPONENT_MASK)) == Self::Int::ZERO
109 }
110}
111
112macro_rules! float_impl {
113 ($ty:ident, $ity:ident, $bits:expr, $significand_bits:expr) => {
114 impl Float for $ty {
115 type Int = $ity;
116
117 const NAN: Self = <$ty>::NAN;
118
119 const BITS: u32 = $bits;
120 const SIGNIFICAND_BITS: u32 = $significand_bits;
121
122 const SIGN_MASK: Self::Int = 1 << (Self::BITS - 1);
123 const SIGNIFICAND_MASK: Self::Int = (1 << Self::SIGNIFICAND_BITS) - 1;
124 const EXPONENT_MASK: Self::Int = !(Self::SIGN_MASK | Self::SIGNIFICAND_MASK);
125
126 fn repr(self) -> Self::Int {
127 self.to_bits()
128 }
129
130 fn from_repr(a: Self::Int) -> Self {
131 Self::from_bits(a)
132 }
133
134 fn negate(self) -> Self {
135 -self
136 }
137 }
138 };
139}
140
141float_impl!(f32, u32, 32, 23);
142float_impl!(f64, u64, 64, 52);
143
144mod add_sub;
145mod cmp;
146mod conv;
147mod div;
148mod functions;
149mod mul;
diff --git a/embassy-rp/src/float/mul.rs b/embassy-rp/src/float/mul.rs
new file mode 100644
index 000000000..ceb0210e3
--- /dev/null
+++ b/embassy-rp/src/float/mul.rs
@@ -0,0 +1,70 @@
1// Credit: taken from `rp-hal` (also licensed Apache+MIT)
2// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mul.rs
3
4use super::Float;
5use crate::rom_data;
6
7trait ROMMul {
8 fn rom_mul(self, b: Self) -> Self;
9}
10
11impl ROMMul for f32 {
12 fn rom_mul(self, b: Self) -> Self {
13 rom_data::float_funcs::fmul(self, b)
14 }
15}
16
17impl ROMMul for f64 {
18 fn rom_mul(self, b: Self) -> Self {
19 rom_data::double_funcs::dmul(self, b)
20 }
21}
22
23fn mul<F: Float + ROMMul>(a: F, b: F) -> F {
24 if a.is_not_finite() {
25 if b.is_zero() {
26 // [-]inf/NaN * 0 = NaN
27 return F::NAN;
28 }
29
30 return if b.is_sign_negative() {
31 // [+/-]inf/NaN * (-X) = [-/+]inf/NaN
32 a.negate()
33 } else {
34 // [-]inf/NaN * X = [-]inf/NaN
35 a
36 };
37 }
38
39 if b.is_not_finite() {
40 if a.is_zero() {
41 // 0 * [-]inf/NaN = NaN
42 return F::NAN;
43 }
44
45 return if b.is_sign_negative() {
46 // (-X) * [+/-]inf/NaN = [-/+]inf/NaN
47 b.negate()
48 } else {
49 // X * [-]inf/NaN = [-]inf/NaN
50 b
51 };
52 }
53
54 a.rom_mul(b)
55}
56
57intrinsics! {
58 #[alias = __mulsf3vfp]
59 #[aeabi = __aeabi_fmul]
60 extern "C" fn __mulsf3(a: f32, b: f32) -> f32 {
61 mul(a, b)
62 }
63
64 #[bootrom_v2]
65 #[alias = __muldf3vfp]
66 #[aeabi = __aeabi_dmul]
67 extern "C" fn __muldf3(a: f64, b: f64) -> f64 {
68 mul(a, b)
69 }
70}