Skip to content

Commit be920c0

Browse files
committed
implement Debug for readable blocks and registers
1 parent e6a6d15 commit be920c0

File tree

6 files changed

+195
-3
lines changed

6 files changed

+195
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
1212
- Fix dangling implicit derives
1313
- Fix escaping <> and & characters in doc attributes
1414
- Add `interrupt_link_section` config parameter for controlling the `#[link_section = "..."]` attribute of `__INTERRUPTS`
15+
- Add option to implement Debug for readable registers (#716)
1516

1617
## [v0.28.0] - 2022-12-25
1718

src/generate/peripheral.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,18 @@ fn register_or_cluster_block(
644644
}
645645
}
646646

647+
let mut derive_debug = TokenStream::new();
648+
if config.impl_debug {
649+
if let Some(feature_name) = &config.impl_debug_feature {
650+
derive_debug.extend(quote! {
651+
#[cfg_attr(feature = #feature_name, derive(Debug))]
652+
});
653+
} else {
654+
derive_debug.extend(quote! {
655+
#[derive(Debug)]
656+
});
657+
}
658+
}
647659
let name = if let Some(name) = name {
648660
name.to_constant_case_ident(span)
649661
} else {
@@ -663,6 +675,7 @@ fn register_or_cluster_block(
663675
Ok(quote! {
664676
///Register block
665677
#[repr(C)]
678+
#derive_debug
666679
pub struct #name {
667680
#rbfs
668681
}

src/generate/register.rs

Lines changed: 146 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,14 @@ pub fn render_register_mod(
230230
}
231231

232232
let mut r_impl_items = TokenStream::new();
233+
let mut r_debug_impl = TokenStream::new();
233234
let mut w_impl_items = TokenStream::new();
234235
let mut zero_to_modify_fields_bitmap = 0;
235236
let mut one_to_modify_fields_bitmap = 0;
236237

238+
let open = Punct::new('{', Spacing::Alone);
239+
let close = Punct::new('}', Spacing::Alone);
240+
237241
if let Some(cur_fields) = register.fields.as_ref() {
238242
// filter out all reserved fields, as we should not generate code for
239243
// them
@@ -243,6 +247,15 @@ pub fn render_register_mod(
243247
.collect();
244248

245249
if !cur_fields.is_empty() {
250+
if config.impl_debug {
251+
r_debug_impl.extend(render_register_mod_debug(
252+
register,
253+
&access,
254+
&cur_fields,
255+
config,
256+
))
257+
}
258+
246259
(
247260
r_impl_items,
248261
w_impl_items,
@@ -261,16 +274,57 @@ pub fn render_register_mod(
261274
config,
262275
)?;
263276
}
277+
} else if !access.can_read() || register.read_action.is_some() {
278+
if let Some(feature) = &config.impl_debug_feature {
279+
r_debug_impl.extend(quote! {
280+
#[cfg(feature=#feature)]
281+
});
282+
}
283+
r_debug_impl.extend(quote! {
284+
impl core::fmt::Debug for crate::generic::Reg<#regspec_ident> {
285+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
286+
write!(f, "(not readable)")
287+
}
288+
}
289+
});
290+
} else {
291+
// no register fields are defined so implement Debug to get entire register value
292+
if let Some(feature) = &config.impl_debug_feature {
293+
r_debug_impl.extend(quote! {
294+
#[cfg(feature=#feature)]
295+
});
296+
}
297+
r_debug_impl.extend(quote! {
298+
impl core::fmt::Debug for R {
299+
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
300+
write!(f, "{}", self.bits())
301+
}
302+
}
303+
});
304+
if let Some(feature) = &config.impl_debug_feature {
305+
r_debug_impl.extend(quote! {
306+
#[cfg(feature=#feature)]
307+
});
308+
}
309+
r_debug_impl.extend(quote! {
310+
impl core::fmt::Debug for crate::generic::Reg<#regspec_ident> {
311+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
312+
self.read().fmt(f)
313+
}
314+
}
315+
});
264316
}
265317

266-
let open = Punct::new('{', Spacing::Alone);
267-
let close = Punct::new('}', Spacing::Alone);
268-
269318
if can_read && !r_impl_items.is_empty() {
270319
mod_items.extend(quote! {
271320
impl R #open #r_impl_items #close
272321
});
273322
}
323+
if !r_debug_impl.is_empty() {
324+
mod_items.extend(quote! {
325+
#r_debug_impl
326+
});
327+
}
274328

275329
if can_write {
276330
mod_items.extend(quote! {
@@ -386,6 +440,95 @@ pub fn render_register_mod(
386440
Ok(mod_items)
387441
}
388442

443+
fn render_register_mod_debug(
444+
register: &Register,
445+
access: &Access,
446+
cur_fields: &[&Field],
447+
config: &Config,
448+
) -> Result<TokenStream> {
449+
let name = util::name_of(register, config.ignore_groups);
450+
let span = Span::call_site();
451+
let regspec_ident = format!("{name}_SPEC").to_constant_case_ident(span);
452+
let open = Punct::new('{', Spacing::Alone);
453+
let close = Punct::new('}', Spacing::Alone);
454+
let mut r_debug_impl = TokenStream::new();
455+
456+
// implement Debug for register readable fields that have no read side effects
457+
if access.can_read() && register.read_action.is_none() {
458+
if let Some(feature) = &config.impl_debug_feature {
459+
r_debug_impl.extend(quote! {
460+
#[cfg(feature=#feature)]
461+
});
462+
}
463+
r_debug_impl.extend(quote! {
464+
impl core::fmt::Debug for R #open
465+
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result #open
466+
f.debug_struct(#name)
467+
});
468+
for &f in cur_fields.iter() {
469+
let field_access = match &f.access {
470+
Some(a) => a,
471+
None => access,
472+
};
473+
let bit_or_bits = if f.bit_width() > 1 { "bits" } else { "bit" };
474+
let bit_or_bits = syn::Ident::new(bit_or_bits, span);
475+
log::debug!("register={} field={}", name, f.name);
476+
if field_access.can_read() && f.read_action.is_none() {
477+
if let Field::Array(_, de) = &f {
478+
for (_, suffix) in de.indexes().enumerate() {
479+
let f_name_n = util::replace_suffix(&f.name, &suffix)
480+
.to_snake_case_ident(Span::call_site());
481+
let f_name_n_s = format!("{f_name_n}");
482+
r_debug_impl.extend(quote! {
483+
.field(#f_name_n_s, &format_args!("{}", self.#f_name_n().#bit_or_bits()))
484+
});
485+
}
486+
} else {
487+
let f_name = util::replace_suffix(&f.name, "");
488+
let f_name = f_name.to_snake_case_ident(span);
489+
let f_name_s = format!("{f_name}");
490+
r_debug_impl.extend(quote! {
491+
.field(#f_name_s, &format_args!("{}", self.#f_name().#bit_or_bits()))
492+
});
493+
}
494+
}
495+
}
496+
r_debug_impl.extend(quote! {
497+
.finish()
498+
#close
499+
#close
500+
});
501+
if let Some(feature) = &config.impl_debug_feature {
502+
r_debug_impl.extend(quote! {
503+
#[cfg(feature=#feature)]
504+
});
505+
}
506+
r_debug_impl.extend(quote! {
507+
impl core::fmt::Debug for crate::generic::Reg<#regspec_ident> {
508+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
509+
self.read().fmt(f)
510+
}
511+
}
512+
});
513+
} else if !access.can_read() || register.read_action.is_some() {
514+
if let Some(feature) = &config.impl_debug_feature {
515+
r_debug_impl.extend(quote! {
516+
#[cfg(feature=#feature)]
517+
});
518+
}
519+
r_debug_impl.extend(quote! {
520+
impl core::fmt::Debug for crate::generic::Reg<#regspec_ident> {
521+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
522+
write!(f, "(not readable)")
523+
}
524+
}
525+
});
526+
} else {
527+
warn!("not implementing debug for {name}");
528+
}
529+
Ok(r_debug_impl)
530+
}
531+
389532
#[allow(clippy::too_many_arguments)]
390533
pub fn fields(
391534
mut fields: Vec<&Field>,

src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,13 +495,29 @@
495495
//! `portable-atomic` v0.3.16 must be added to the dependencies, with default features off to
496496
//! disable the `fallback` feature.
497497
//!
498+
//! ## the `--impl_debug` flag
499+
//!
500+
//! The `--impl_debug` option will cause svd2rust to generate `core::fmt::Debug` implementations for
501+
//! all registers and blocks. If a register is readable and has fields defined then each field value
502+
//! will be printed - if no fields are defined then the value of the register will be printed. Any
503+
//! register that has read actions will not be read and printed as `(not read/has read action!)`.
504+
//! Registers that are not readable will have `(write only register)` printed as the value.
505+
//!
506+
//! The `--impl_debug_feature` flad can also be specified to include debug implementations conditionally
507+
//! behind the supplied feature name.
508+
//!
498509
//! Usage examples:
499510
//!
500511
//! ```ignore
501512
//! // These can be called from different contexts even though they are modifying the same register
502513
//! P1.p1out.set_bits(|w| unsafe { w.bits(1 << 1) });
503514
//! P1.p1out.clear_bits(|w| unsafe { w.bits(!(1 << 2)) });
504515
//! P1.p1out.toggle_bits(|w| unsafe { w.bits(1 << 4) });
516+
//! // if impl_debug was used one can print Registers or RegisterBlocks
517+
//! // print single register
518+
//! println!("RTC_CNT {:#?}", unsafe { &*esp32s3::RTC_CNTL::ptr() }.options0);
519+
//! // print all registers for peripheral
520+
//! println!("RTC_CNT {:#?}", unsafe { &*esp32s3::RTC_CNTL::ptr() });
505521
//! ```
506522
#![recursion_limit = "128"]
507523

src/main.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,19 @@ fn run() -> Result<()> {
118118
.action(ArgAction::SetTrue)
119119
.help("Use array increment for cluster size"),
120120
)
121+
.arg(
122+
Arg::new("impl_debug")
123+
.long("impl_debug")
124+
.action(ArgAction::SetTrue)
125+
.help("implement Debug for readable blocks and registers"),
126+
)
127+
.arg(
128+
Arg::new("impl_debug_feature")
129+
.long("impl_debug_feature")
130+
.help("add feature gating for block and register debug implementation")
131+
.action(ArgAction::Set)
132+
.value_name("FEATURE"),
133+
)
121134
.arg(
122135
Arg::new("make_mod")
123136
.long("make_mod")

src/util.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ pub struct Config {
5151
pub feature_peripheral: bool,
5252
#[cfg_attr(feature = "serde", serde(default))]
5353
pub max_cluster_size: bool,
54+
#[cfg_attr(feature = "serde", serde(default))]
55+
pub impl_debug: bool,
56+
#[cfg_attr(feature = "serde", serde(default))]
57+
pub impl_debug_feature: Option<String>,
5458
#[cfg_attr(feature = "serde", serde(default = "current_dir"))]
5559
pub output_dir: PathBuf,
5660
#[cfg_attr(feature = "serde", serde(default))]
@@ -118,6 +122,8 @@ impl Default for Config {
118122
feature_group: false,
119123
feature_peripheral: false,
120124
max_cluster_size: false,
125+
impl_debug: false,
126+
impl_debug_feature: None,
121127
output_dir: current_dir(),
122128
input: None,
123129
source_type: SourceType::default(),

0 commit comments

Comments
 (0)