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