aboutsummaryrefslogtreecommitdiff
path: root/embassy-mspm0/src/mathacl.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-mspm0/src/mathacl.rs')
-rw-r--r--embassy-mspm0/src/mathacl.rs255
1 files changed, 255 insertions, 0 deletions
diff --git a/embassy-mspm0/src/mathacl.rs b/embassy-mspm0/src/mathacl.rs
new file mode 100644
index 000000000..e29f4a59e
--- /dev/null
+++ b/embassy-mspm0/src/mathacl.rs
@@ -0,0 +1,255 @@
1//! MATHACL
2//!
3//! This HAL implements mathematical calculations performed by the CPU.
4
5#![macro_use]
6
7use core::f32::consts::PI;
8use core::marker::PhantomData;
9
10use embassy_hal_internal::PeripheralType;
11use micromath::F32Ext;
12
13use crate::Peri;
14use crate::pac::mathacl::{Mathacl as Regs, vals};
15
16pub enum Precision {
17 High = 31,
18 Medium = 15,
19 Low = 1,
20}
21
22/// Serial error
23#[derive(Debug, Eq, PartialEq, Copy, Clone)]
24#[cfg_attr(feature = "defmt", derive(defmt::Format))]
25#[non_exhaustive]
26pub enum Error {
27 ValueInWrongRange,
28 NBitsTooBig,
29}
30
31pub struct Mathacl<'d> {
32 regs: &'static Regs,
33 _phantom: PhantomData<&'d mut ()>,
34}
35
36impl<'d> Mathacl<'d> {
37 /// Mathacl initialization.
38 pub fn new<T: Instance>(_instance: Peri<'d, T>) -> Self {
39 // Init power
40 T::regs().gprcm(0).rstctl().write(|w| {
41 w.set_resetstkyclr(vals::Resetstkyclr::CLR);
42 w.set_resetassert(vals::Resetassert::ASSERT);
43 w.set_key(vals::ResetKey::KEY);
44 });
45
46 // Enable power
47 T::regs().gprcm(0).pwren().write(|w| {
48 w.set_enable(true);
49 w.set_key(vals::PwrenKey::KEY);
50 });
51
52 // init delay, 16 cycles
53 cortex_m::asm::delay(16);
54
55 Self {
56 regs: T::regs(),
57 _phantom: PhantomData,
58 }
59 }
60
61 /// Internal helper SINCOS function.
62 fn sincos(&mut self, rad: f32, precision: Precision, sin: bool) -> Result<f32, Error> {
63 self.regs.ctl().write(|w| {
64 w.set_func(vals::Func::SINCOS);
65 w.set_numiter(precision as u8);
66 });
67
68 if rad > PI || rad < -PI {
69 return Err(Error::ValueInWrongRange);
70 }
71
72 // TODO: make f32 division on CPU
73 let native = rad / PI;
74
75 match signed_f32_to_register(native, 0) {
76 Ok(val) => self.regs.op1().write(|w| {
77 w.set_data(val);
78 }),
79 Err(er) => return Err(er),
80 };
81
82 // check if done
83 while self.regs.status().read().busy() == vals::Busy::NOTDONE {}
84
85 match sin {
86 true => register_to_signed_f32(self.regs.res2().read().data(), 0),
87 false => register_to_signed_f32(self.regs.res1().read().data(), 0),
88 }
89 }
90
91 /// Calsulates trigonometric sine operation in the range [-1,1) with a give precision.
92 pub fn sin(&mut self, rad: f32, precision: Precision) -> Result<f32, Error> {
93 self.sincos(rad, precision, true)
94 }
95
96 /// Calsulates trigonometric cosine operation in the range [-1,1) with a give precision.
97 pub fn cos(&mut self, rad: f32, precision: Precision) -> Result<f32, Error> {
98 self.sincos(rad, precision, false)
99 }
100}
101
102pub(crate) trait SealedInstance {
103 fn regs() -> &'static Regs;
104}
105
106/// Mathacl instance trait
107#[allow(private_bounds)]
108pub trait Instance: SealedInstance + PeripheralType {}
109
110macro_rules! impl_mathacl_instance {
111 ($instance: ident) => {
112 impl crate::mathacl::SealedInstance for crate::peripherals::$instance {
113 fn regs() -> &'static crate::pac::mathacl::Mathacl {
114 &crate::pac::$instance
115 }
116 }
117
118 impl crate::mathacl::Instance for crate::peripherals::$instance {}
119 };
120}
121
122/// Convert f32 data to understandable by M0 format.
123fn signed_f32_to_register(data: f32, n_bits: u8) -> Result<u32, Error> {
124 let mut res: u32 = 0;
125 // check if negative
126 let negative = data < 0.0;
127
128 // absolute value for extraction
129 let abs = data.abs();
130
131 // total integer bit count
132 let total_bits = 31;
133
134 // Validate n_bits
135 if n_bits > 31 {
136 return Err(Error::NBitsTooBig);
137 }
138
139 // number of fractional bits
140 let shift = total_bits - n_bits;
141
142 // Compute masks
143 let (n_mask, m_mask) = if n_bits == 0 {
144 (0, 0x7FFFFFFF)
145 } else if n_bits == 31 {
146 (0x7FFFFFFF, 0)
147 } else {
148 ((1u32 << n_bits) - 1, (1u32 << shift) - 1)
149 };
150
151 // calc. integer(n) & fractional(m) parts
152 let n = abs.floor() as u32;
153 let mut m = ((abs - abs.floor()) * (1u32 << shift) as f32).round() as u32;
154
155 // Handle trimming integer part
156 if n_bits == 0 && n > 0 {
157 m = 0x7FFFFFFF;
158 }
159
160 // calculate result
161 if n_bits > 0 {
162 res = n << shift & n_mask;
163 }
164 if shift > 0 {
165 res = res | m & m_mask;
166 }
167
168 // if negative, do 2’s compliment
169 if negative {
170 res = !res + 1;
171 }
172 Ok(res)
173}
174
175/// Reversely converts M0-register format to native f32.
176fn register_to_signed_f32(data: u32, n_bits: u8) -> Result<f32, Error> {
177 // Validate n_bits
178 if n_bits > 31 {
179 return Err(Error::NBitsTooBig);
180 }
181
182 // total integer bit count
183 let total_bits = 31;
184
185 let negative = (data >> 31) == 1;
186
187 // number of fractional bits
188 let shift = total_bits - n_bits;
189
190 // Compute masks
191 let (n_mask, m_mask) = if n_bits == 0 {
192 (0, 0x7FFFFFFF)
193 } else if n_bits == 31 {
194 (0x7FFFFFFF, 0)
195 } else {
196 ((1u32 << n_bits) - 1, (1u32 << shift) - 1)
197 };
198
199 // Compute n and m
200 let mut n = if n_bits == 0 {
201 0
202 } else if shift >= 32 {
203 data & n_mask
204 } else {
205 (data >> shift) & n_mask
206 };
207 let mut m = data & m_mask;
208
209 // if negative, do 2’s compliment
210 if negative {
211 n = !n & n_mask;
212 m = (!m & m_mask) + 1;
213 }
214
215 let mut value = (n as f32) + (m as f32) / (1u32 << shift) as f32;
216 if negative {
217 value = -value;
218 }
219 return Ok(value);
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225
226 #[test]
227 fn mathacl_convert_func_errors() {
228 assert_eq!(signed_f32_to_register(0.0, 32), Err(Error::NBitsTooBig));
229 assert_eq!(register_to_signed_f32(0, 32), Err(Error::NBitsTooBig));
230 }
231
232 #[test]
233 fn mathacl_signed_f32_to_register() {
234 let mut test_float = 1.0;
235 assert_eq!(signed_f32_to_register(test_float, 0).unwrap(), 0x7FFFFFFF);
236
237 test_float = 0.0;
238 assert_eq!(signed_f32_to_register(test_float, 0).unwrap(), 0x0);
239
240 test_float = -1.0;
241 assert_eq!(signed_f32_to_register(test_float, 0).unwrap(), 0x80000001);
242 }
243
244 #[test]
245 fn mathacl_register_to_signed_f32() {
246 let mut test_u32: u32 = 0x7FFFFFFF;
247 assert_eq!(register_to_signed_f32(test_u32, 0u8).unwrap(), 1.0);
248
249 test_u32 = 0x0;
250 assert_eq!(register_to_signed_f32(test_u32, 0u8).unwrap(), 0.0);
251
252 test_u32 = 0x80000001;
253 assert_eq!(register_to_signed_f32(test_u32, 0u8).unwrap(), -1.0);
254 }
255}