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}