regmock_rs/utils/
mod.rs

1//! Collection of data structures and functions that power `regmock_rs`.
2use std::cell::RefCell;
3use std::collections::HashMap;
4use std::fmt::Debug;
5use std::rc::Rc;
6
7use derive_builder::Builder;
8use serde::Deserialize;
9use serde_json;
10
11/// Enum representing types of register accesses.
12#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
13#[serde(rename_all = "lowercase")]
14pub enum RegisterAccessType {
15    #[serde(alias = "r")]
16    READ,
17    #[serde(alias = "w")]
18    WRITE,
19    #[cfg(feature = "aurix")]
20    LDMST,
21}
22
23/// Stores information of a specific registers access.
24///
25/// Members are all optional to allow for easy partial comparison. See the
26/// [`PartialEq`](#impl-PartialEq%3CRegisterAccess%3E-for-RegisterAccess)
27/// implementation for more details and specific examples.
28///
29/// See the convenience functions [`access_gen::read`], [`access_gen::read_value`],
30/// [`access_gen::write`] and [`access_gen::write_value`] for a shorthand ways to
31/// construct `RegisterAccess` structs.
32#[derive(Default, Clone, Eq, Builder, Deserialize)]
33#[builder(default)]
34#[serde(default)]
35pub struct RegisterAccess {
36    /// Type of the register access.
37    #[serde(alias = "type")]
38    #[builder(setter(into, strip_option))]
39    pub ty: Option<RegisterAccessType>,
40    /// Address of accessed register.
41    #[builder(setter(into, strip_option))]
42    pub addr: Option<usize>,
43    /// Length of the access mask in bytes.
44    #[builder(setter(into, strip_option))]
45    pub len: Option<usize>,
46    /// Value of the register before the access.
47    #[builder(setter(into, strip_option))]
48    pub before: Option<u64>,
49    /// Value of the register after the access.
50    #[builder(setter(into, strip_option))]
51    pub after: Option<u64>,
52}
53
54impl Debug for RegisterAccess {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        let mut debug_struct = f.debug_struct("RegisterAccess");
57        if let Some(ty) = &self.ty {
58            debug_struct.field("ty", ty);
59        }
60        if let Some(addr) = &self.addr {
61            debug_struct.field("addr", addr);
62        }
63        if let Some(len) = &self.len {
64            debug_struct.field("len", len);
65        }
66        if let Some(before) = &self.before {
67            debug_struct.field("before", before);
68        }
69        if let Some(after) = &self.after {
70            debug_struct.field("after", after);
71        }
72        debug_struct.finish()
73    }
74}
75
76/// Compares only part of the struct that are `Some()` on `&self`. As this struct
77/// should only be used for writing unit tests, it is useful to provide
78/// an implementation of `PartialEq` that checks values that are there.
79/// Consequent this means that a `RegisterAccess` struct with all members
80/// `None` is equal to every `RegisterAccess` struct.
81///
82/// # Examples
83///
84/// ```rust
85/// use regmock_rs::utils::*;
86///
87/// let full  = RegisterAccess::new(RegisterAccessType::READ,
88///     0xDEADC0DE,
89///     8,
90///     0x0,
91///     0xC0FFEE,
92/// );
93/// let mut partial = RegisterAccess::default();
94/// partial.ty = Some(RegisterAccessType::READ);
95/// assert_eq!(partial, full);
96/// ```
97impl PartialEq for RegisterAccess {
98    fn eq(&self, other: &Self) -> bool {
99        let mut ret = true;
100        if self.ty.is_some() && other.ty.is_some() {
101            ret = ret && self.ty.eq(&other.ty);
102        }
103        if self.addr.is_some() && other.addr.is_some() {
104            ret = ret && self.addr.eq(&other.addr);
105        }
106        if self.len.is_some() && other.len.is_some() {
107            ret = ret && self.len.eq(&other.len);
108        }
109        if self.before.is_some() && other.before.is_some() {
110            ret = ret && self.before.eq(&other.before);
111        }
112        if self.after.is_some() && other.after.is_some() {
113            ret = ret && self.after.eq(&other.after);
114        }
115        ret
116    }
117}
118
119impl RegisterAccess {
120    /// Constructor for `RegisterAccess` that takes all its members as arguments.
121    ///
122    /// See also the shorthand constructors [`access_gen::read`], [`access_gen::read_value`],
123    /// [`access_gen::write`] and [`access_gen::write_value`] for constructing partial
124    /// `RegisterAccess` structs.
125    pub fn new(ty: RegisterAccessType, addr: usize, len: usize, before: u64, after: u64) -> Self {
126        Self {
127            ty: Some(ty),
128            addr: Some(addr),
129            len: Some(len),
130            before: Some(before),
131            after: Some(after),
132        }
133    }
134    /// Deserialize a sequence of register accesses from a JSON array.
135    pub fn seq_from_json(data: &str) -> Vec<RegisterAccess> {
136        serde_json::from_str(data).unwrap()
137    }
138}
139
140/// Short-hand constructors for various [`RegisterAccess`] types. Useful when
141/// constructing sequences to match logs against.
142pub mod access_gen {
143    use super::RegisterAccess;
144    use super::RegisterAccessType::{READ, WRITE};
145
146    /// Construct a [`RegisterAccess`] of type [`READ`]
147    /// from a specific register.
148    pub fn read(address: usize) -> RegisterAccess {
149        RegisterAccess {
150            ty: Some(READ),
151            addr: Some(address),
152            len: None,
153            before: None,
154            after: None,
155        }
156    }
157
158    /// Construct a [`RegisterAccess`] of type [`READ`]
159    /// that from a specific register with a read value.
160    pub fn read_value(address: usize, value: u64) -> RegisterAccess {
161        RegisterAccess {
162            ty: Some(READ),
163            addr: Some(address),
164            len: None,
165            before: None,
166            after: Some(value),
167        }
168    }
169
170    /// Construct a [`RegisterAccess`] of type [`WRITE`]
171    /// that from a specific register.
172    pub fn write(address: usize) -> RegisterAccess {
173        RegisterAccess {
174            ty: Some(WRITE),
175            addr: Some(address),
176            len: None,
177            before: None,
178            after: None,
179        }
180    }
181
182    /// Construct a [`RegisterAccess`] of type [`WRITE`]
183    /// that from a specific register with a written value.
184    pub fn write_value(address: usize, value: u64) -> RegisterAccess {
185        RegisterAccess {
186            ty: Some(WRITE),
187            addr: Some(address),
188            len: None,
189            before: None,
190            after: Some(value),
191        }
192    }
193}
194
195/// List of [`RegisterAccess`]'s where **`READ`** accesses are run-length-encoded.
196#[derive(Debug, Clone, Default)]
197pub struct RegmockLog {
198    /// List of register accesses with run-length-encoded **`READ`** access.
199    pub log: Vec<(RegisterAccess, usize)>,
200}
201
202impl RegmockLog {
203    // Add new log entry to the log. Reads accesses are run-length-encoded.
204    pub(crate) fn push_log_entry(&mut self, entry: RegisterAccess) {
205        match self.log.last_mut() {
206            Some(ref mut last) => {
207                if entry
208                    .ty
209                    .as_ref()
210                    .is_some_and(|ty| *ty == RegisterAccessType::READ)
211                    && last.0 == entry
212                {
213                    last.1 += 1;
214                } else {
215                    self.log.push((entry, 1));
216                }
217            }
218            None => {
219                self.log.push((entry, 1));
220            }
221        }
222    }
223
224    /// Check if specified register is currently being polled for at least `count` times.
225    pub(crate) fn is_being_polled(&self, addr: usize, count: usize) -> bool {
226        self.log.last().is_some_and(|last| {
227            last.0.addr.as_ref().is_some_and(|a| *a == addr)
228                && last
229                    .0
230                    .ty
231                    .as_ref()
232                    .is_some_and(|ty| ty == &RegisterAccessType::READ)
233                && last.1 > count
234        })
235    }
236
237    /// Get a [`IterRegmockLogNoPolling`] iterator without polling **READ**'s.
238    pub fn iter(&self) -> IterRegmockLogNoPolling<'_> {
239        IterRegmockLogNoPolling {
240            inner: self,
241            pos: 0,
242        }
243    }
244
245    /// Get a [`IterRegmockLogDecoded`] iterator with **all** recorded accesses.
246    pub fn iter_full(&self) -> IterRegmockLogDecoded<'_> {
247        IterRegmockLogDecoded {
248            inner: self,
249            pos: 0,
250            counter: 0,
251        }
252    }
253
254    /// Count number of **all** recorded register accesses in the log.
255    pub fn len_full(&self) -> usize {
256        self.iter_full().count()
257    }
258}
259
260/// Iterator over [`RegisterAccess`] values without sequences of duplicate
261/// **`READ`** entries (i.e. skips register polling accesses).
262pub struct IterRegmockLogNoPolling<'a> {
263    inner: &'a RegmockLog,
264    pos: usize,
265}
266
267impl<'a> Iterator for IterRegmockLogNoPolling<'a> {
268    type Item = &'a RegisterAccess;
269    fn next(&mut self) -> Option<Self::Item> {
270        if self.pos >= self.inner.log.len() {
271            None
272        } else {
273            self.pos += 1;
274            match self.inner.log.get(self.pos - 1) {
275                Some(e) => Some(&e.0),
276                None => None,
277            }
278        }
279    }
280}
281
282/// Iterator over [`RegisterAccess`] values including **all** sequences of duplicate
283/// log entries (i.e. decodes the RLE encoded `log` of [`RegmockLog`])
284///
285/// # Note
286/// This iterator skips entries in [`RegmockLog`] that have a run-length of 0.
287pub struct IterRegmockLogDecoded<'a> {
288    inner: &'a RegmockLog,
289    pos: usize,
290    counter: usize,
291}
292
293impl<'a> Iterator for IterRegmockLogDecoded<'a> {
294    type Item = &'a RegisterAccess;
295    fn next(&mut self) -> Option<Self::Item> {
296        if self.pos >= self.inner.log.len() {
297            None
298        } else {
299            match self.inner.log.get(self.pos) {
300                Some(current) => {
301                    if self.counter >= current.1 {
302                        self.counter = 0;
303                        self.pos += 1;
304                        self.next()
305                    } else {
306                        self.counter += 1;
307                        Some(&current.0)
308                    }
309                }
310                None => None,
311            }
312        }
313    }
314}
315
316/// Type used to mock registers. Export to make typing in tests more readable.
317/// Used by [`Regmock`]
318pub type RegisterMap = HashMap<usize, u64>;
319/// Type of read-access callback functions that can be registered in [`Regmock::read_fn`].
320pub type ReadFunction = Box<dyn FnMut(&mut RegisterMap, u64) -> u64 + Send>;
321/// Type of write-access callback functions that can be registered in [`Regmock::write_fn`].
322pub type WriteFunction = Box<dyn FnMut(&mut RegisterMap, u64, u64) -> u64 + Send>;
323
324/// Mock and record register accesses of embedded devices.
325/// # Regmock
326///
327/// [`Regmock`] is the heart of `regmock-rs` and serves both as mock for
328/// registers of embedded devices, as well as a recorder access logs.
329///
330/// In addition to mocking and logging register accesses, it allows registering
331/// callbacks with specific registers, that get executed when a specific
332/// type of access happens to a register. See [`read_fn`](#structfield.read_fn) and [`write_fn`](#structfield.write_fn).
333///
334/// # Examples
335///
336/// The code below creates a new [`Regmock`] struct and sets it as the
337/// `thread_local` mock object.
338/// ```rust
339/// use std::sync::{Arc, Mutex};
340/// use regmock_rs::{init_regmock, utils::Regmock};
341/// init_regmock(Arc::new(Mutex::new(Regmock::default())));
342/// ```
343pub struct Regmock {
344    /// List of [`RegisterAccess`] that happen through the PAC library.
345    ///
346    /// Stores tuples of [`RegisterAccess`] structs and counter for run-length-encoding.
347    /// To circumvent the logging, either use [`crate::silent()`].
348    pub log: RegmockLog,
349
350    /// Register mocks.
351    ///
352    /// ## Note:
353    /// Registers are initialized to 0x0 and not the chip specific initial
354    /// register value. Developers shall use an unrecorded call to `pac::REGISTER::init()`
355    /// or write the desired initial value directly into [`register_mocks`](#register_mocks)
356    /// object to have correct initial register values if absolutely needed.
357    pub register_mocks: RegisterMap,
358
359    /// A map from register addresses to [`ReadFunction`] that gets called
360    /// every time a specific register is *read* from through the PAC.
361    ///
362    /// # Closure
363    /// When reading from a specific register though the PAC and a
364    /// [`ReadFunction`] exists in [`super::read_fn`], the closure gets called,
365    /// and the result is treated as the value that was read from
366    /// the register.
367    ///
368    /// If no [`ReadFunction`] exists for a specific address, the read
369    /// is passed though to the mocked register in [`register_mock`](crate::utils::Regmock#structfield.register_mock)
370    ///
371    /// ## Arguments
372    /// When a function is called, it gets a mutable reference to the
373    /// [`register_mocks`](#register_mocks) member and
374    /// (this will probably be removed) the value that was read from the mocked
375    /// register.
376    /// The value returned from this function is treated as the value that
377    /// was read from the register is passed back to the PAC.
378    ///
379    /// # Examples
380    ///
381    /// A function that modify the `pac::PERIPHERAL.register_x()` register, when a
382    /// read of `pac::PERIPHERAL.register_y()` happens.
383    ///
384    /// ```rust,ignore
385    /// let mut mock = regmock_rs::utils::Regmock::default();
386    /// let callback = |registers: &mut HashMap<usize, u64>, val: u64| -> u64 {
387    ///     registers.insert(pac::PERIPHERAL.register_x().addr(), 0xDEADC0DE);
388    ///     val
389    /// };
390    /// mock
391    ///     .read_fn
392    ///     .insert(pac::PERIPHERAL.register_y().addr(), Box::new(callback));
393    /// ```
394    ///
395    /// # Limitations
396    ///
397    /// Due to lifetime reasons, it is currently not possible to capture
398    /// the environment by reference. Only `move` captures are allowed.
399    ///
400    /// To have references to local values in the callbacks, store the
401    /// local value in a `Rc<T>` and move a cloned value of that `Rc<T>` into
402    /// the closure.
403    ///
404    /// ```rust,ignore
405    /// let mut mock = regmock_rs::utils::Regmock::default();
406    /// let local_value = std::rc::Rc::new(std::cell::RefCell::new(0));
407    /// let local_value_clone = local_value.clone();
408    /// let callback = |_: &mut HashMap<usize, u64>, val| -> u64 {
409    ///     *local_value_clone.borrow_mut() +=1;
410    ///     val
411    /// };
412    /// mock.read_fn
413    ///     .insert(pac::PERIPHERAL.register_y().addr(), Box::new(callback));
414    /// let _ = unsafe { pac::PERIPHERAL.register_y().read() };
415    /// assert_eq!(local_value.borrow(), 1);
416    /// ```
417    ///
418    /// See crate level examples and tests for more.
419    pub read_fn: HashMap<usize, ReadFunction>,
420
421    /// A map of register addresses to [`WriteFunction`] that get called
422    /// every time a specific register is *written* to through the PAC.
423    ///
424    /// # Closure
425    /// When writing to a specific register though the PAC and a
426    /// [`WriteFunction`] exists for that register, the [`WriteFunction`] gets
427    /// called.
428    ///
429    /// If no [`WriteFunction`] exists for a specific address, the write
430    /// is passed though to the mock.
431    ///
432    /// ## Arguments
433    /// When a function is called, it gets a mutable reference to the
434    /// [`register_mocks`](#register_mocks) member and
435    /// (this will probably be removed) the value that was read from the mocked
436    /// register.
437    /// The value returned from this function is treated as the value that
438    /// was read from the register is passed back to the PAC.
439    ///
440    /// The key is the physical address of the register. The associated
441    /// value is a [`WriteFunction`].
442    ///
443    /// When a function is called, it gets passed
444    /// - a mutable reference to the [`register_mocks`](#register_mocks) member
445    /// - the value of the register before the write.
446    /// - the value that should be written to the register.
447    ///
448    /// The value returned from this function is treated as the value that
449    /// is written to the register.
450    ///
451    /// ## Examples
452    ///
453    /// A function that modifies the `pac::PERIPHERAL::register_x()` register,
454    /// when a write to `pac::PERIPHERAL::register_y()` happens.
455    ///
456    /// ```rust,ignore
457    /// let a = |registers: &mut HashMap<usize, u64>, before: u64, val: u64| -> u64 {
458    ///     registers.insert(GPIO0.r#in().addr(), 0xDEADC0DE);
459    ///     val
460    /// };
461    /// ```
462    ///
463    /// # Limitations
464    ///
465    /// See [`Regmock::read_fn`] limitations section.
466    pub write_fn: HashMap<usize, WriteFunction>,
467
468    /// Controls if the register accesses get logged.
469    /// Defaults to `true`.
470    pub log_enabled: bool,
471
472    /// Controls if the register callback functions get executed. Used
473    /// by the `no_log!` macro to bypass the logging and callbacks.
474    /// Defaults to `true`.
475    pub callback_enabled: bool,
476
477    /// Function to resolve the address of a register to its name
478    ///
479    /// Consider using [`Regmock::get_reg_name`] which provides a simpler interface
480    pub name_resolver: Option<Box<dyn Fn(u64) -> Option<&'static &'static str> + Send>>,
481}
482
483impl Debug for Regmock {
484    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
485        f.debug_struct("Regmock")
486            .field("log", &self.log)
487            .field("read_fn", &"TODO: HashMap<usize, ReadFunction>")
488            .field("write_fn", &"TODO: HashMap<usize, WriteFunction>")
489            .field("log_enabled", &self.log_enabled)
490            .field("callback_enabled", &self.callback_enabled)
491            .finish()
492    }
493}
494
495impl Default for Regmock {
496    /// Construct a default [`Regmock`] struct.
497    fn default() -> Self {
498        Self {
499            log: Default::default(),
500            register_mocks: Default::default(),
501            read_fn: Default::default(),
502            write_fn: Default::default(),
503            log_enabled: true,
504            callback_enabled: true,
505            name_resolver: None,
506        }
507    }
508}
509
510impl Regmock {
511    pub fn with_resolver<T>(resolver: &'static T) -> Self
512    where
513        T: Fn(u64) -> Option<&'static &'static str> + Send + Sync,
514    {
515        Self {
516            log: Default::default(),
517            register_mocks: Default::default(),
518            read_fn: Default::default(),
519            write_fn: Default::default(),
520            log_enabled: true,
521            callback_enabled: true,
522            name_resolver: Some(Box::new(resolver)),
523        }
524    }
525
526    fn get_reg_value(&mut self, addr: usize) -> u64 {
527        let register_mocks = &mut self.register_mocks;
528        if let std::collections::hash_map::Entry::Vacant(e) = register_mocks.entry(addr) {
529            e.insert(0u64);
530            0u64
531        } else {
532            *register_mocks.get(&addr).unwrap()
533        }
534    }
535    /// Execute the register specific `read_fn` callback/closure thing if there
536    /// exists one for the current register.
537    fn exec_read_fn(&mut self, addr: usize, before: u64) -> u64 {
538        if !self.read_fn.contains_key(&addr) || !self.callback_enabled {
539            before
540        } else {
541            let cb = self.read_fn.get_mut(&addr).unwrap();
542            cb(&mut self.register_mocks, before)
543        }
544    }
545
546    /// Execute the register specific `read_fn` callback/closure thing if there
547    /// exists one for the current register.
548    fn exec_write_fn(&mut self, addr: usize, before: u64, val: u64) -> u64 {
549        if !self.write_fn.contains_key(&addr) || !self.callback_enabled {
550            val
551        } else {
552            let cb = self.write_fn.get_mut(&addr).unwrap();
553            cb(&mut self.register_mocks, before, val)
554        }
555    }
556
557    /// Construct a default [`Regmock`].
558    pub fn new() -> Rc<RefCell<Self>> {
559        Rc::new(RefCell::new(Self::default()))
560    }
561
562    /// Get a copy of the recorded register accesses.
563    pub fn get_logs(&self) -> RegmockLog {
564        self.log.clone()
565    }
566
567    pub fn get_reg_name(&self, addr: usize) -> Option<&'static str> {
568        self.name_resolver
569            .as_ref()
570            .and_then(|r| r(addr as u64))
571            .copied()
572    }
573}
574
575impl Regmock {
576    /// Read from the mocked register and return the read value.
577    ///
578    /// This function can be registered as the function to call when reading
579    /// from a register with `feature=tracing` enabled.
580    /// # Examples
581    ///
582    /// ```rust,ignore
583    /// pac::tracing::set_read_fn(regmock_rs::read_fn).unwrap();
584    /// ```
585    /// To register the function with the PAC library. Consult the documentation
586    /// of your specific PAC for more information.
587    pub fn read_volatile(&mut self, addr: usize, len: usize) -> u64 {
588        let before = self.get_reg_value(addr);
589        let after = self.exec_read_fn(addr, before);
590
591        if self.log_enabled {
592            self.log.push_log_entry(RegisterAccess::new(
593                RegisterAccessType::READ,
594                addr,
595                len,
596                before,
597                after,
598            ));
599        }
600        after
601    }
602
603    /// Write value to a mocked register.
604    ///
605    /// This function can be registered in the used `PAC`, as the function to
606    /// call when writing to a register with `feature=tracing` enabled.
607    ///
608    /// # Examples
609    ///
610    /// ```rust,ignore
611    /// pac::tracing::set_write_fn(regmock_rs::write_fn).unwrap();
612    /// ```
613    /// To register the function with the PAC library. Consult the documentation
614    /// of your specific PAC for more information.
615    pub fn write_volatile(&mut self, addr: usize, len: usize, val: u64) {
616        let before = self.get_reg_value(addr);
617        let after = self.exec_write_fn(addr, before, val);
618
619        if self.log_enabled {
620            self.log.push_log_entry(RegisterAccess::new(
621                RegisterAccessType::WRITE,
622                addr,
623                len,
624                before,
625                after,
626            ));
627        }
628        self.register_mocks.insert(addr, after);
629    }
630
631    #[cfg(feature = "aurix")]
632    /// Basic impl of the LMST tracing interace for the Aurix chip.
633    /// TODO: maybe add a dedicated field for callbacks for lmst register
634    /// accesses.
635    pub fn load_modify_store(&mut self, addr: usize, len: usize, val: u64) {
636        let before = self.get_reg_value(addr);
637        let after = self.exec_write_fn(addr, before, val);
638
639        if !self.log_enabled {
640            self.log.push_log_entry(RegisterAccess::new(
641                RegisterAccessType::WRITE,
642                addr,
643                len,
644                before,
645                after,
646            ));
647        }
648        self.register_mocks.insert(addr, after);
649    }
650}