aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsawi97 <[email protected]>2023-04-20 10:29:16 +0200
committerGitHub <[email protected]>2023-04-20 10:29:16 +0200
commit43c20dbe65578931cd01fd3baa10c1e3e68ee138 (patch)
tree4dbcba01035f6db834e2a4632ab0b44db32abad3
parent7d64de153f7f9528ddc1cc10b600fe917ea171d5 (diff)
parent9b51c8f4d441a3e1550305c126e9f74fff030fd5 (diff)
Merge branch 'embassy-rs:master' into embassy-boot-stable
-rw-r--r--.github/workflows/doc.yml3
-rw-r--r--embassy-boot/boot/README.md1
-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
-rw-r--r--embassy-rp/src/lib.rs1
-rw-r--r--embassy-rp/src/rom_data.rs115
-rw-r--r--embassy-stm32/src/i2c/v2.rs110
-rw-r--r--tests/rp/.cargo/config.toml6
-rw-r--r--tests/rp/Cargo.toml2
-rw-r--r--tests/rp/src/bin/float.rs53
15 files changed, 1213 insertions, 127 deletions
diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml
index cb222803b..411b7589f 100644
--- a/.github/workflows/doc.yml
+++ b/.github/workflows/doc.yml
@@ -61,6 +61,7 @@ jobs:
61 mkdir crates 61 mkdir crates
62 builder ./embassy-boot/boot crates/embassy-boot/git.zup 62 builder ./embassy-boot/boot crates/embassy-boot/git.zup
63 builder ./embassy-boot/nrf crates/embassy-boot-nrf/git.zup 63 builder ./embassy-boot/nrf crates/embassy-boot-nrf/git.zup
64 builder ./embassy-boot/rp crates/embassy-boot-rp/git.zup
64 builder ./embassy-boot/stm32 crates/embassy-boot-stm32/git.zup 65 builder ./embassy-boot/stm32 crates/embassy-boot-stm32/git.zup
65 builder ./embassy-cortex-m crates/embassy-cortex-m/git.zup 66 builder ./embassy-cortex-m crates/embassy-cortex-m/git.zup
66 builder ./embassy-embedded-hal crates/embassy-embedded-hal/git.zup 67 builder ./embassy-embedded-hal crates/embassy-embedded-hal/git.zup
@@ -84,5 +85,3 @@ jobs:
84 echo "${{secrets.KUBECONFIG}}" > ~/.kube/config 85 echo "${{secrets.KUBECONFIG}}" > ~/.kube/config
85 POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) 86 POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
86 kubectl cp crates $POD:/data 87 kubectl cp crates $POD:/data
87
88 \ No newline at end of file
diff --git a/embassy-boot/boot/README.md b/embassy-boot/boot/README.md
index 414405377..56f4a6279 100644
--- a/embassy-boot/boot/README.md
+++ b/embassy-boot/boot/README.md
@@ -13,6 +13,7 @@ By design, the bootloader does not provide any network capabilities. Networking
13The bootloader supports different hardware in separate crates: 13The bootloader supports different hardware in separate crates:
14 14
15* `embassy-boot-nrf` - for the nRF microcontrollers. 15* `embassy-boot-nrf` - for the nRF microcontrollers.
16* `embassy-boot-rp` - for the RP2040 microcontrollers.
16* `embassy-boot-stm32` - for the STM32 microcontrollers. 17* `embassy-boot-stm32` - for the STM32 microcontrollers.
17 18
18## Minimum supported Rust version (MSRV) 19## Minimum supported Rust version (MSRV)
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}
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index 1d63f6c2e..3841bb83a 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -12,6 +12,7 @@ mod intrinsics;
12 12
13pub mod adc; 13pub mod adc;
14pub mod dma; 14pub mod dma;
15mod float;
15pub mod gpio; 16pub mod gpio;
16pub mod i2c; 17pub mod i2c;
17pub mod interrupt; 18pub mod interrupt;
diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs
index 757a27114..805c1f09f 100644
--- a/embassy-rp/src/rom_data.rs
+++ b/embassy-rp/src/rom_data.rs
@@ -56,56 +56,32 @@ macro_rules! declare_rom_function {
56 fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty 56 fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
57 $lookup:block 57 $lookup:block
58 ) => { 58 ) => {
59 #[doc = r"Additional access for the `"] 59 declare_rom_function!{
60 #[doc = stringify!($name)] 60 __internal ,
61 #[doc = r"` ROM function."] 61 $(#[$outer])*
62 pub mod $name { 62 fn $name( $($argname: $ty),* ) -> $ret
63 /// Retrieve a function pointer. 63 $lookup
64 #[cfg(not(feature = "rom-func-cache"))]
65 pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
66 let p: *const u32 = $lookup;
67 unsafe {
68 let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
69 func
70 }
71 }
72
73 /// Retrieve a function pointer.
74 #[cfg(feature = "rom-func-cache")]
75 pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
76 use core::sync::atomic::{AtomicU16, Ordering};
77
78 // All pointers in the ROM fit in 16 bits, so we don't need a
79 // full width word to store the cached value.
80 static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
81 // This is safe because the lookup will always resolve
82 // to the same value. So even if an interrupt or another
83 // core starts at the same time, it just repeats some
84 // work and eventually writes back the correct value.
85 let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
86 0 => {
87 let raw: *const u32 = $lookup;
88 CACHED_PTR.store(raw as u16, Ordering::Relaxed);
89 raw
90 },
91 val => val as *const u32,
92 };
93 unsafe {
94 let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
95 func
96 }
97 }
98 } 64 }
65 };
99 66
100 $(#[$outer])* 67 (
101 pub extern "C" fn $name( $($argname: $ty),* ) -> $ret { 68 $(#[$outer:meta])*
102 $name::ptr()($($argname),*) 69 unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
70 $lookup:block
71 ) => {
72 declare_rom_function!{
73 __internal unsafe ,
74 $(#[$outer])*
75 fn $name( $($argname: $ty),* ) -> $ret
76 $lookup
103 } 77 }
104 }; 78 };
105 79
106 ( 80 (
81 __internal
82 $( $maybe_unsafe:ident )? ,
107 $(#[$outer:meta])* 83 $(#[$outer:meta])*
108 unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty 84 fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
109 $lookup:block 85 $lookup:block
110 ) => { 86 ) => {
111 #[doc = r"Additional access for the `"] 87 #[doc = r"Additional access for the `"]
@@ -114,43 +90,58 @@ macro_rules! declare_rom_function {
114 pub mod $name { 90 pub mod $name {
115 /// Retrieve a function pointer. 91 /// Retrieve a function pointer.
116 #[cfg(not(feature = "rom-func-cache"))] 92 #[cfg(not(feature = "rom-func-cache"))]
117 pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { 93 pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret {
118 let p: *const u32 = $lookup; 94 let p: *const u32 = $lookup;
119 unsafe { 95 unsafe {
120 let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); 96 let func : $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret
97 = core::mem::transmute(p);
121 func 98 func
122 } 99 }
123 } 100 }
124 101
102 #[cfg(feature = "rom-func-cache")]
103 // unlike rp2040-hal we store a full word, containing the full function pointer.
104 // rp2040-hal saves two bytes by storing only the rom offset, at the cost of
105 // having to do an indirection and an atomic operation on every rom call.
106 static mut CACHE: $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret
107 = trampoline;
108
109 #[cfg(feature = "rom-func-cache")]
110 $( $maybe_unsafe )? extern "C" fn trampoline( $($argname: $ty),* ) -> $ret {
111 use core::sync::atomic::{compiler_fence, Ordering};
112
113 let p: *const u32 = $lookup;
114 #[allow(unused_unsafe)]
115 unsafe {
116 CACHE = core::mem::transmute(p);
117 compiler_fence(Ordering::Release);
118 CACHE($($argname),*)
119 }
120 }
121
125 /// Retrieve a function pointer. 122 /// Retrieve a function pointer.
126 #[cfg(feature = "rom-func-cache")] 123 #[cfg(feature = "rom-func-cache")]
127 pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { 124 pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret {
128 use core::sync::atomic::{AtomicU16, Ordering}; 125 use core::sync::atomic::{compiler_fence, Ordering};
129 126
130 // All pointers in the ROM fit in 16 bits, so we don't need a
131 // full width word to store the cached value.
132 static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
133 // This is safe because the lookup will always resolve 127 // This is safe because the lookup will always resolve
134 // to the same value. So even if an interrupt or another 128 // to the same value. So even if an interrupt or another
135 // core starts at the same time, it just repeats some 129 // core starts at the same time, it just repeats some
136 // work and eventually writes back the correct value. 130 // work and eventually writes back the correct value.
137 let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { 131 //
138 0 => { 132 // We easily get away with using only compiler fences here
139 let raw: *const u32 = $lookup; 133 // because RP2040 SRAM is not cached. If it were we'd need
140 CACHED_PTR.store(raw as u16, Ordering::Relaxed); 134 // to make sure updates propagate quickly, or just take the
141 raw 135 // hit and let each core resolve every function once.
142 }, 136 compiler_fence(Ordering::Acquire);
143 val => val as *const u32,
144 };
145 unsafe { 137 unsafe {
146 let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); 138 CACHE
147 func
148 } 139 }
149 } 140 }
150 } 141 }
151 142
152 $(#[$outer])* 143 $(#[$outer])*
153 pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret { 144 pub $( $maybe_unsafe )? extern "C" fn $name( $($argname: $ty),* ) -> $ret {
154 $name::ptr()($($argname),*) 145 $name::ptr()($($argname),*)
155 } 146 }
156 }; 147 };
@@ -369,6 +360,7 @@ pub fn fplib_start() -> *const u8 {
369} 360}
370 361
371/// See Table 180 in the RP2040 datasheet for the contents of this table. 362/// See Table 180 in the RP2040 datasheet for the contents of this table.
363#[cfg_attr(feature = "rom-func-cache", inline(never))]
372pub fn soft_float_table() -> *const usize { 364pub fn soft_float_table() -> *const usize {
373 rom_table_lookup(DATA_TABLE, *b"SF") 365 rom_table_lookup(DATA_TABLE, *b"SF")
374} 366}
@@ -379,6 +371,7 @@ pub fn fplib_end() -> *const u8 {
379} 371}
380 372
381/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table. 373/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table.
374#[cfg_attr(feature = "rom-func-cache", inline(never))]
382pub fn soft_double_table() -> *const usize { 375pub fn soft_double_table() -> *const usize {
383 if rom_version_number() < 2 { 376 if rom_version_number() < 2 {
384 panic!( 377 panic!(
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 39e6702e5..853bc128f 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -1,6 +1,5 @@
1use core::cmp; 1use core::cmp;
2use core::future::poll_fn; 2use core::future::poll_fn;
3use core::sync::atomic::{AtomicUsize, Ordering};
4use core::task::Poll; 3use core::task::Poll;
5 4
6use embassy_embedded_hal::SetConfig; 5use embassy_embedded_hal::SetConfig;
@@ -35,14 +34,12 @@ impl Default for Config {
35 34
36pub struct State { 35pub struct State {
37 waker: AtomicWaker, 36 waker: AtomicWaker,
38 chunks_transferred: AtomicUsize,
39} 37}
40 38
41impl State { 39impl State {
42 pub(crate) const fn new() -> Self { 40 pub(crate) const fn new() -> Self {
43 Self { 41 Self {
44 waker: AtomicWaker::new(), 42 waker: AtomicWaker::new(),
45 chunks_transferred: AtomicUsize::new(0),
46 } 43 }
47 } 44 }
48} 45}
@@ -130,10 +127,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
130 let isr = regs.isr().read(); 127 let isr = regs.isr().read();
131 128
132 if isr.tcr() || isr.tc() { 129 if isr.tcr() || isr.tc() {
133 let state = T::state(); 130 T::state().waker.wake();
134 let transferred = state.chunks_transferred.load(Ordering::Relaxed);
135 state.chunks_transferred.store(transferred + 1, Ordering::Relaxed);
136 state.waker.wake();
137 } 131 }
138 // The flag can only be cleared by writting to nbytes, we won't do that here, so disable 132 // The flag can only be cleared by writting to nbytes, we won't do that here, so disable
139 // the interrupt 133 // the interrupt
@@ -457,12 +451,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
457 TXDMA: crate::i2c::TxDma<T>, 451 TXDMA: crate::i2c::TxDma<T>,
458 { 452 {
459 let total_len = write.len(); 453 let total_len = write.len();
460 let completed_chunks = total_len / 255;
461 let total_chunks = if completed_chunks * 255 == total_len {
462 completed_chunks
463 } else {
464 completed_chunks + 1
465 };
466 454
467 let dma_transfer = unsafe { 455 let dma_transfer = unsafe {
468 let regs = T::regs(); 456 let regs = T::regs();
@@ -480,7 +468,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
480 }; 468 };
481 469
482 let state = T::state(); 470 let state = T::state();
483 state.chunks_transferred.store(0, Ordering::Relaxed);
484 let mut remaining_len = total_len; 471 let mut remaining_len = total_len;
485 472
486 let on_drop = OnDrop::new(|| { 473 let on_drop = OnDrop::new(|| {
@@ -495,33 +482,35 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
495 } 482 }
496 }); 483 });
497 484
498 // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
499 if first_slice {
500 unsafe {
501 Self::master_write(
502 address,
503 total_len.min(255),
504 Stop::Software,
505 (total_chunks != 1) || !last_slice,
506 &check_timeout,
507 )?;
508 }
509 } else {
510 unsafe {
511 Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice, &check_timeout)?;
512 T::regs().cr1().modify(|w| w.set_tcie(true));
513 }
514 }
515
516 poll_fn(|cx| { 485 poll_fn(|cx| {
517 state.waker.register(cx.waker()); 486 state.waker.register(cx.waker());
518 let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed);
519 487
520 if chunks_transferred == total_chunks { 488 let isr = unsafe { T::regs().isr().read() };
489 if remaining_len == total_len {
490 // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
491 if first_slice {
492 unsafe {
493 Self::master_write(
494 address,
495 total_len.min(255),
496 Stop::Software,
497 (total_len > 255) || !last_slice,
498 &check_timeout,
499 )?;
500 }
501 } else {
502 unsafe {
503 Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, &check_timeout)?;
504 T::regs().cr1().modify(|w| w.set_tcie(true));
505 }
506 }
507 } else if !(isr.tcr() || isr.tc()) {
508 // poll_fn was woken without an interrupt present
509 return Poll::Pending;
510 } else if remaining_len == 0 {
521 return Poll::Ready(Ok(())); 511 return Poll::Ready(Ok(()));
522 } else if chunks_transferred != 0 { 512 } else {
523 remaining_len = remaining_len.saturating_sub(255); 513 let last_piece = (remaining_len <= 255) && last_slice;
524 let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice;
525 514
526 // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers 515 // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
527 unsafe { 516 unsafe {
@@ -531,6 +520,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
531 T::regs().cr1().modify(|w| w.set_tcie(true)); 520 T::regs().cr1().modify(|w| w.set_tcie(true));
532 } 521 }
533 } 522 }
523
524 remaining_len = remaining_len.saturating_sub(255);
534 Poll::Pending 525 Poll::Pending
535 }) 526 })
536 .await?; 527 .await?;
@@ -559,12 +550,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
559 RXDMA: crate::i2c::RxDma<T>, 550 RXDMA: crate::i2c::RxDma<T>,
560 { 551 {
561 let total_len = buffer.len(); 552 let total_len = buffer.len();
562 let completed_chunks = total_len / 255;
563 let total_chunks = if completed_chunks * 255 == total_len {
564 completed_chunks
565 } else {
566 completed_chunks + 1
567 };
568 553
569 let dma_transfer = unsafe { 554 let dma_transfer = unsafe {
570 let regs = T::regs(); 555 let regs = T::regs();
@@ -580,7 +565,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
580 }; 565 };
581 566
582 let state = T::state(); 567 let state = T::state();
583 state.chunks_transferred.store(0, Ordering::Relaxed);
584 let mut remaining_len = total_len; 568 let mut remaining_len = total_len;
585 569
586 let on_drop = OnDrop::new(|| { 570 let on_drop = OnDrop::new(|| {
@@ -593,27 +577,29 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
593 } 577 }
594 }); 578 });
595 579
596 // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
597 unsafe {
598 Self::master_read(
599 address,
600 total_len.min(255),
601 Stop::Software,
602 total_chunks != 1,
603 restart,
604 &check_timeout,
605 )?;
606 }
607
608 poll_fn(|cx| { 580 poll_fn(|cx| {
609 state.waker.register(cx.waker()); 581 state.waker.register(cx.waker());
610 let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed);
611 582
612 if chunks_transferred == total_chunks { 583 let isr = unsafe { T::regs().isr().read() };
584 if remaining_len == total_len {
585 // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
586 unsafe {
587 Self::master_read(
588 address,
589 total_len.min(255),
590 Stop::Software,
591 total_len > 255,
592 restart,
593 &check_timeout,
594 )?;
595 }
596 } else if !(isr.tcr() || isr.tc()) {
597 // poll_fn was woken without an interrupt present
598 return Poll::Pending;
599 } else if remaining_len == 0 {
613 return Poll::Ready(Ok(())); 600 return Poll::Ready(Ok(()));
614 } else if chunks_transferred != 0 { 601 } else {
615 remaining_len = remaining_len.saturating_sub(255); 602 let last_piece = remaining_len <= 255;
616 let last_piece = chunks_transferred + 1 == total_chunks;
617 603
618 // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers 604 // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
619 unsafe { 605 unsafe {
@@ -623,6 +609,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
623 T::regs().cr1().modify(|w| w.set_tcie(true)); 609 T::regs().cr1().modify(|w| w.set_tcie(true));
624 } 610 }
625 } 611 }
612
613 remaining_len = remaining_len.saturating_sub(255);
626 Poll::Pending 614 Poll::Pending
627 }) 615 })
628 .await?; 616 .await?;
diff --git a/tests/rp/.cargo/config.toml b/tests/rp/.cargo/config.toml
index 9611db3a0..e1744c703 100644
--- a/tests/rp/.cargo/config.toml
+++ b/tests/rp/.cargo/config.toml
@@ -1,6 +1,8 @@
1[unstable] 1[unstable]
2build-std = ["core"] 2# enabling these breaks the float tests during linking, with intrinsics
3build-std-features = ["panic_immediate_abort"] 3# duplicated between embassy-rp and compilter_builtins
4#build-std = ["core"]
5#build-std-features = ["panic_immediate_abort"]
4 6
5[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 7[target.'cfg(all(target_arch = "arm", target_os = "none"))']
6#runner = "teleprobe client run --target rpi-pico --elf" 8#runner = "teleprobe client run --target rpi-pico --elf"
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml
index 36ff735ec..6778f53d7 100644
--- a/tests/rp/Cargo.toml
+++ b/tests/rp/Cargo.toml
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
8embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] }
11embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } 11embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics"] }
12embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 12embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
13 13
14defmt = "0.3.0" 14defmt = "0.3.0"
diff --git a/tests/rp/src/bin/float.rs b/tests/rp/src/bin/float.rs
new file mode 100644
index 000000000..6715271e6
--- /dev/null
+++ b/tests/rp/src/bin/float.rs
@@ -0,0 +1,53 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::pac;
8use embassy_time::{Duration, Timer};
9use {defmt_rtt as _, panic_probe as _};
10
11#[embassy_executor::main]
12async fn main(_spawner: Spawner) {
13 embassy_rp::init(Default::default());
14 info!("Hello World!");
15
16 const PI_F: f32 = 3.1415926535f32;
17 const PI_D: f64 = 3.14159265358979323846f64;
18
19 unsafe {
20 pac::BUSCTRL
21 .perfsel(0)
22 .write(|r| r.set_perfsel(pac::busctrl::vals::Perfsel::ROM));
23 }
24
25 for i in 0..=360 {
26 let rad_f = (i as f32) * PI_F / 180.0;
27 info!(
28 "{}° float: {=f32} / {=f32} / {=f32} / {=f32}",
29 i,
30 rad_f,
31 rad_f - PI_F,
32 rad_f + PI_F,
33 rad_f % PI_F
34 );
35 let rad_d = (i as f64) * PI_D / 180.0;
36 info!(
37 "{}° double: {=f64} / {=f64} / {=f64} / {=f64}",
38 i,
39 rad_d,
40 rad_d - PI_D,
41 rad_d + PI_D,
42 rad_d % PI_D
43 );
44 Timer::after(Duration::from_millis(10)).await;
45 }
46
47 let rom_accesses = unsafe { pac::BUSCTRL.perfctr(0).read().perfctr() };
48 // every float operation used here uses at least 10 cycles
49 defmt::assert!(rom_accesses >= 360 * 12 * 10);
50
51 info!("Test OK");
52 cortex_m::asm::bkpt();
53}