Skip to content

Commit 8f63b56

Browse files
bors[bot]liebman
andauthored
Merge #716
716: implement Debug for readable fields of registers r=emilgardis,burrbull a=liebman contributes towards #48 Notes: - I've only run this with esp (esp32/esp32s3) pacs - I'd love some feedback on where I've implemented this and if there is a cleaner way Co-authored-by: Christopher Liebman <[email protected]>
2 parents 0bc16e2 + be920c0 commit 8f63b56

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
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
1414
- Fix dangling implicit derives
1515
- Fix escaping <> and & characters in doc attributes
1616
- Add `interrupt_link_section` config parameter for controlling the `#[link_section = "..."]` attribute of `__INTERRUPTS`
17+
- Add option to implement Debug for readable registers (#716)
1718

1819
## [v0.28.0] - 2022-12-25
1920

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,
@@ -260,16 +273,57 @@ pub fn render_register_mod(
260273
config,
261274
)?;
262275
}
276+
} else if !access.can_read() || register.read_action.is_some() {
277+
if let Some(feature) = &config.impl_debug_feature {
278+
r_debug_impl.extend(quote! {
279+
#[cfg(feature=#feature)]
280+
});
281+
}
282+
r_debug_impl.extend(quote! {
283+
impl core::fmt::Debug for crate::generic::Reg<#regspec_ident> {
284+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
285+
write!(f, "(not readable)")
286+
}
287+
}
288+
});
289+
} else {
290+
// no register fields are defined so implement Debug to get entire register value
291+
if let Some(feature) = &config.impl_debug_feature {
292+
r_debug_impl.extend(quote! {
293+
#[cfg(feature=#feature)]
294+
});
295+
}
296+
r_debug_impl.extend(quote! {
297+
impl core::fmt::Debug for R {
298+
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
299+
write!(f, "{}", self.bits())
300+
}
301+
}
302+
});
303+
if let Some(feature) = &config.impl_debug_feature {
304+
r_debug_impl.extend(quote! {
305+
#[cfg(feature=#feature)]
306+
});
307+
}
308+
r_debug_impl.extend(quote! {
309+
impl core::fmt::Debug for crate::generic::Reg<#regspec_ident> {
310+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
311+
self.read().fmt(f)
312+
}
313+
}
314+
});
263315
}
264316

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

274328
if can_write {
275329
mod_items.extend(quote! {
@@ -385,6 +439,95 @@ pub fn render_register_mod(
385439
Ok(mod_items)
386440
}
387441

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