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}