tock_registers/
debug.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Register Debug Support Infrastructure
6//!
7//! This module provides optional infrastructure to query debug information from
8//! register types implementing the [`RegisterDebugInfo`] trait. This
9//! information can then be used by the [`RegisterDebugValue`] type to produce a
10//! human-readable representation of a register's fields and values.
11
12use core::fmt;
13use core::marker::PhantomData;
14
15use crate::{
16    fields::{Field, TryFromValue},
17    RegisterLongName, UIntLike,
18};
19
20/// `FieldValueEnumSeq` is a debug helper trait representing a sequence of
21/// [field enum types](crate::fields::Field::read_as_enum).
22///
23/// It provides methods to recurse through this sequence of types and
24/// thus call methods on them, such as
25/// [`try_from_value`](crate::fields::TryFromValue::try_from_value).
26///
27/// Its primary use lies in the [`RegisterDebugInfo`] trait. This trait provides
28/// facilities useful for providing information on the layout of a register
29/// (such as, which fields it has and which known values those fields can
30/// assume). Such information is usually only available at compile time. This
31/// trait makes runtime-representable data available at runtime. However,
32/// information encoded solely in types can't simply be used at runtime, which
33/// is where this trait comes in.
34pub trait FieldValueEnumSeq<U: UIntLike> {
35    /// Iterates over the sequence of types and performs the following steps:
36    ///
37    /// 1. Invokes the `data` function argument. This is expected to provide the
38    ///    numeric (`UIntLike`) value of the register field that the current type
39    ///    corresponds to.
40    ///
41    /// 2. Invoke [`try_from_value`](crate::fields::TryFromValue::try_from_value)
42    ///    on the current [field enum type](crate::fields::Field::read_as_enum),
43    ///    passing the value returned by `data`.
44    ///
45    /// 3. Provide the returned value to the `f` function argument. This is
46    ///    either the enum representation (if the field value belongs to a known
47    ///    variant, or the numeric field value returned by `data`.
48    ///
49    /// In practice, this method should be used to iterate over types and other
50    /// runtime-accessible information in tandem, to produce a human-readable
51    /// register dump.
52    ///
53    /// Importantly, `data` is invoked for every type in the sequence, and
54    /// every invocation of `data` is followed by a single invocation of `f`.
55    fn recurse_try_from_value(data: &mut impl FnMut() -> U, f: &mut impl FnMut(&dyn fmt::Debug));
56}
57
58/// End-of-list type for the [`FieldValueEnumSeq`] sequence.
59pub enum FieldValueEnumNil {}
60impl<U: UIntLike> FieldValueEnumSeq<U> for FieldValueEnumNil {
61    fn recurse_try_from_value(_data: &mut impl FnMut() -> U, _f: &mut impl FnMut(&dyn fmt::Debug)) {
62    }
63}
64
65/// List element for the [`FieldValueEnumSeq`] sequence.
66pub enum FieldValueEnumCons<
67    U: UIntLike,
68    H: TryFromValue<U, EnumType = H> + fmt::Debug,
69    T: FieldValueEnumSeq<U>,
70> {
71    // This variant can never be constructed, as `Infallible` can't be:
72    _Impossible(
73        core::convert::Infallible,
74        PhantomData<U>,
75        PhantomData<H>,
76        PhantomData<T>,
77    ),
78}
79impl<U: UIntLike, H: TryFromValue<U, EnumType = H> + fmt::Debug, T: FieldValueEnumSeq<U>>
80    FieldValueEnumSeq<U> for FieldValueEnumCons<U, H, T>
81{
82    fn recurse_try_from_value(data: &mut impl FnMut() -> U, f: &mut impl FnMut(&dyn fmt::Debug)) {
83        // Query debug information from first type, then recurse into the next.
84
85        // It's imprtant that we call `data` _exactly_ once here.
86        let extracted_value = data();
87
88        // It's important that we call `f` _exactly_ once here.
89        match H::try_from_value(extracted_value) {
90            Some(v) => f(&v),
91            None => f(&extracted_value),
92        }
93
94        // Continue the recursion:
95        T::recurse_try_from_value(data, f)
96    }
97}
98
99/// [`RegisterDebugInfo`] exposes debugging information from register types.
100///
101/// The exposed information is composed of both types (such as the individual
102/// [field enum types](crate::fields::Field::read_as_enum) generated by the
103/// [`crate::register_bitfields`] macro), as well as runtime-queryable
104/// information in the form of data.
105///
106/// Where applicable, the index of type information and runtime-queryable data
107/// match. For instance, the `i`th element of the
108/// [`RegisterDebugInfo::FieldValueEnumTypes`] associated type sequence
109/// corresponds to the `i`th element of the array returned by the
110/// [`RegisterDebugInfo::field_names`] method.
111pub trait RegisterDebugInfo<T: UIntLike>: RegisterLongName {
112    /// Associated type representing a sequence of all field-value enum types of
113    /// this [`RegisterLongName`] register.
114    ///
115    /// See [`FieldValueEnumSeq`]. The index of types in this sequence
116    /// correspond to indices of values returned from the [`fields`] and
117    /// [`field_names`] methods.
118    ///
119    /// [`field_names`]: RegisterDebugInfo::field_names
120    /// [`fields`]: RegisterDebugInfo::fields
121    type FieldValueEnumTypes: FieldValueEnumSeq<T>;
122
123    /// The name of the register.
124    fn name() -> &'static str;
125
126    /// The names of the fields in the register.
127    ///
128    /// The length of the returned slice is identical to the length of the
129    /// [`FieldValueEnumTypes`] sequence and [`fields`] return value. For every
130    /// index `i`, the element of this slice corresponds to the type at the
131    /// `i`th position in the [`FieldValueEnumTypes`] sequence and element at
132    /// the `i`th position in the [`fields`] return value.
133    ///
134    /// [`FieldValueEnumTypes`]: RegisterDebugInfo::FieldValueEnumTypes
135    /// [`fields`]: RegisterDebugInfo::fields
136    fn field_names() -> &'static [&'static str];
137
138    /// The fields of a register.
139    ///
140    /// The length of the returned slice is identical to the length of the
141    /// [`FieldValueEnumTypes`] sequence and [`field_names`] return value. For
142    /// every index `i`, the element of this slice corresponds to the type at
143    /// the `i`th position in the [`FieldValueEnumTypes`] sequence and element
144    /// at the `i`th position in the [`field_names`] return value.
145    ///
146    /// [`FieldValueEnumTypes`]: RegisterDebugInfo::FieldValueEnumTypes
147    /// [`field_names`]: RegisterDebugInfo::field_names
148    fn fields() -> &'static [Field<T, Self>]
149    where
150        Self: Sized;
151}
152
153/// `RegisterDebugValue` captures a register's value and implements
154/// [`fmt::Debug`] to provide a human-readable representation of the register
155/// state.
156///
157/// Its usage incurs the inclusion of additional data into the final binary,
158/// such as the names of all register fields and defined field value variants
159/// (see [`crate::fields::Field::read_as_enum`]).
160///
161/// This type contains a local copy of the register value used for providing
162/// debug information. It will not access the actual backing register.
163pub struct RegisterDebugValue<T, E>
164where
165    T: UIntLike,
166    E: RegisterDebugInfo<T>,
167{
168    pub(crate) data: T,
169    pub(crate) _reg: core::marker::PhantomData<E>,
170}
171
172impl<T, E> fmt::Debug for RegisterDebugValue<T, E>
173where
174    T: UIntLike + 'static,
175    E: RegisterDebugInfo<T> + 'static,
176{
177    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178        // This is using the core library's formatting facilities to produce an
179        // output similar to Rust's own derive-Debug implementation on structs.
180        //
181        // We start by printing the struct's name and opening braces:
182        let mut debug_struct = f.debug_struct(E::name());
183
184        // Now, obtain iterators over both the struct's field types and
185        // names. They are guaranteed to match up:
186        let mut names = E::field_names().iter();
187        let mut fields = E::fields().iter();
188
189        // To actually resolve the field's known values (encoded in the field
190        // enum type's variants), we need to recurse through those field
191        // types. Their ordering is guaranteed to match up with the above
192        // calls. For more information on what these closures do and how they
193        // are invoked, consult the documentation of `recurse_try_from_value`.
194        let mut data = || fields.next().unwrap().read(self.data);
195        let mut debug_field = |f: &dyn fmt::Debug| {
196            debug_struct.field(names.next().unwrap(), f);
197        };
198
199        // Finally, recurse through all the fields:
200        E::FieldValueEnumTypes::recurse_try_from_value(&mut data, &mut debug_field);
201
202        debug_struct.finish()
203    }
204}