diff options
| author | sawi97 <[email protected]> | 2023-04-20 10:29:16 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-04-20 10:29:16 +0200 |
| commit | 43c20dbe65578931cd01fd3baa10c1e3e68ee138 (patch) | |
| tree | 4dbcba01035f6db834e2a4632ab0b44db32abad3 | |
| parent | 7d64de153f7f9528ddc1cc10b600fe917ea171d5 (diff) | |
| parent | 9b51c8f4d441a3e1550305c126e9f74fff030fd5 (diff) | |
Merge branch 'embassy-rs:master' into embassy-boot-stable
| -rw-r--r-- | .github/workflows/doc.yml | 3 | ||||
| -rw-r--r-- | embassy-boot/boot/README.md | 1 | ||||
| -rw-r--r-- | embassy-rp/src/float/add_sub.rs | 92 | ||||
| -rw-r--r-- | embassy-rp/src/float/cmp.rs | 201 | ||||
| -rw-r--r-- | embassy-rp/src/float/conv.rs | 157 | ||||
| -rw-r--r-- | embassy-rp/src/float/div.rs | 141 | ||||
| -rw-r--r-- | embassy-rp/src/float/functions.rs | 239 | ||||
| -rw-r--r-- | embassy-rp/src/float/mod.rs | 149 | ||||
| -rw-r--r-- | embassy-rp/src/float/mul.rs | 70 | ||||
| -rw-r--r-- | embassy-rp/src/lib.rs | 1 | ||||
| -rw-r--r-- | embassy-rp/src/rom_data.rs | 115 | ||||
| -rw-r--r-- | embassy-stm32/src/i2c/v2.rs | 110 | ||||
| -rw-r--r-- | tests/rp/.cargo/config.toml | 6 | ||||
| -rw-r--r-- | tests/rp/Cargo.toml | 2 | ||||
| -rw-r--r-- | tests/rp/src/bin/float.rs | 53 |
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 | |||
| 13 | The bootloader supports different hardware in separate crates: | 13 | The 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 | |||
| 4 | use super::{Float, Int}; | ||
| 5 | use crate::rom_data; | ||
| 6 | |||
| 7 | trait ROMAdd { | ||
| 8 | fn rom_add(self, b: Self) -> Self; | ||
| 9 | } | ||
| 10 | |||
| 11 | impl ROMAdd for f32 { | ||
| 12 | fn rom_add(self, b: Self) -> Self { | ||
| 13 | rom_data::float_funcs::fadd(self, b) | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | impl ROMAdd for f64 { | ||
| 18 | fn rom_add(self, b: Self) -> Self { | ||
| 19 | rom_data::double_funcs::dadd(self, b) | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | fn 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 | |||
| 54 | intrinsics! { | ||
| 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 | |||
| 4 | use super::Float; | ||
| 5 | use crate::rom_data; | ||
| 6 | |||
| 7 | trait ROMCmp { | ||
| 8 | fn rom_cmp(self, b: Self) -> i32; | ||
| 9 | } | ||
| 10 | |||
| 11 | impl ROMCmp for f32 { | ||
| 12 | fn rom_cmp(self, b: Self) -> i32 { | ||
| 13 | rom_data::float_funcs::fcmp(self, b) | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | impl ROMCmp for f64 { | ||
| 18 | fn rom_cmp(self, b: Self) -> i32 { | ||
| 19 | rom_data::double_funcs::dcmp(self, b) | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | fn 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 | |||
| 31 | fn 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 | |||
| 39 | intrinsics! { | ||
| 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 | |||
| 4 | use super::Float; | ||
| 5 | use 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 | |||
| 11 | intrinsics! { | ||
| 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 | |||
| 4 | use super::Float; | ||
| 5 | use 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] | ||
| 14 | fn save_divider_and_call<F, R>(f: F) -> R | ||
| 15 | where | ||
| 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 | |||
| 61 | fn save_divider<F, R>(f: F) -> R | ||
| 62 | where | ||
| 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 | |||
| 75 | trait ROMDiv { | ||
| 76 | fn rom_div(self, b: Self) -> Self; | ||
| 77 | } | ||
| 78 | |||
| 79 | impl 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 | |||
| 86 | impl 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 | |||
| 93 | fn 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 | |||
| 128 | intrinsics! { | ||
| 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 | |||
| 4 | use crate::float::{Float, Int}; | ||
| 5 | use crate::rom_data; | ||
| 6 | |||
| 7 | trait 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 | |||
| 19 | impl 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 | |||
| 59 | impl 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 | |||
| 98 | fn 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 | |||
| 108 | fn 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 | |||
| 116 | fn 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 | |||
| 124 | fn exp<F: Float + ROMFunctions>(f: F) -> F { | ||
| 125 | if f.is_nan() { | ||
| 126 | F::NAN | ||
| 127 | } else { | ||
| 128 | f.exp() | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | fn 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 | |||
| 140 | fn 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 | |||
| 148 | fn 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 | |||
| 156 | fn 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 | ||
| 165 | mod 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 | |||
| 4 | use core::ops; | ||
| 5 | |||
| 6 | // Borrowed and simplified from compiler-builtins so we can use bit ops | ||
| 7 | // on floating point without macro soup. | ||
| 8 | pub(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 | |||
| 33 | macro_rules! int_impl { | ||
| 34 | ($ty:ty) => { | ||
| 35 | impl Int for $ty { | ||
| 36 | const ZERO: Self = 0; | ||
| 37 | } | ||
| 38 | }; | ||
| 39 | } | ||
| 40 | |||
| 41 | int_impl!(u32); | ||
| 42 | int_impl!(u64); | ||
| 43 | |||
| 44 | pub(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 | |||
| 112 | macro_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 | |||
| 141 | float_impl!(f32, u32, 32, 23); | ||
| 142 | float_impl!(f64, u64, 64, 52); | ||
| 143 | |||
| 144 | mod add_sub; | ||
| 145 | mod cmp; | ||
| 146 | mod conv; | ||
| 147 | mod div; | ||
| 148 | mod functions; | ||
| 149 | mod 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 | |||
| 4 | use super::Float; | ||
| 5 | use crate::rom_data; | ||
| 6 | |||
| 7 | trait ROMMul { | ||
| 8 | fn rom_mul(self, b: Self) -> Self; | ||
| 9 | } | ||
| 10 | |||
| 11 | impl ROMMul for f32 { | ||
| 12 | fn rom_mul(self, b: Self) -> Self { | ||
| 13 | rom_data::float_funcs::fmul(self, b) | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | impl ROMMul for f64 { | ||
| 18 | fn rom_mul(self, b: Self) -> Self { | ||
| 19 | rom_data::double_funcs::dmul(self, b) | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | fn 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 | |||
| 57 | intrinsics! { | ||
| 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 | ||
| 13 | pub mod adc; | 13 | pub mod adc; |
| 14 | pub mod dma; | 14 | pub mod dma; |
| 15 | mod float; | ||
| 15 | pub mod gpio; | 16 | pub mod gpio; |
| 16 | pub mod i2c; | 17 | pub mod i2c; |
| 17 | pub mod interrupt; | 18 | pub 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))] | ||
| 372 | pub fn soft_float_table() -> *const usize { | 364 | pub 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))] | ||
| 382 | pub fn soft_double_table() -> *const usize { | 375 | pub 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 @@ | |||
| 1 | use core::cmp; | 1 | use core::cmp; |
| 2 | use core::future::poll_fn; | 2 | use core::future::poll_fn; |
| 3 | use core::sync::atomic::{AtomicUsize, Ordering}; | ||
| 4 | use core::task::Poll; | 3 | use core::task::Poll; |
| 5 | 4 | ||
| 6 | use embassy_embedded_hal::SetConfig; | 5 | use embassy_embedded_hal::SetConfig; |
| @@ -35,14 +34,12 @@ impl Default for Config { | |||
| 35 | 34 | ||
| 36 | pub struct State { | 35 | pub struct State { |
| 37 | waker: AtomicWaker, | 36 | waker: AtomicWaker, |
| 38 | chunks_transferred: AtomicUsize, | ||
| 39 | } | 37 | } |
| 40 | 38 | ||
| 41 | impl State { | 39 | impl 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] |
| 2 | build-std = ["core"] | 2 | # enabling these breaks the float tests during linking, with intrinsics |
| 3 | build-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" | |||
| 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } |
| 11 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } | 11 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics"] } |
| 12 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 12 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 13 | 13 | ||
| 14 | defmt = "0.3.0" | 14 | defmt = "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 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_rp::pac; | ||
| 8 | use embassy_time::{Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async 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 | } | ||
