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(¤t.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}