Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

Amjad50
Copy link
Contributor

@Amjad50 Amjad50 commented Dec 22, 2023

Pull Request Overview

So, the idea is to get an easy view of the data inside, especially for more complex fields.

Currently it does the following:

  • It will try to parse the enum if it can, otherwise it will just print the value as a number (not hex).
  • If the value is not enum (wasn't created with []), it will just print the number.
  • Unless its 1 bit, it will print it as a bool.

This change is completely hidden from the API, and shouldn't change anything, the feature is not enabled by default.
When the feature is disabled, the resulting code is exactly the same before this commit.

Testing Strategy

I didn't add tests, since its not that simple as we would need to use String to perform fmt::Debug in memory.
But if its needed, please let me know.

TODO or Help Wanted

All good as per the use case I was thinking of, but we can discuss more on the features that we can add and the behavior of the debug for various types of field. Like, with 1 bit, should we print as bool or just keep it a number.

Documentation Updated

I don't see tock_registers mentioned in /docs so not sure if we should add some docs for this change.

  • Updated the relevant files in /docs, or no updates are required.

Formatting

  • Ran make prepush.

So, the idea is to get an easy view of the data inside, especially for
more complex fields.

Currently it does the following:
- It will try to parse the enum if it can, otherwise it will just print
  the value as a number (not hex).
- If the value is not enum (wasn't created with [<fields>]), it will
  just print the number.
- Unless its 1 bit, it will print it as a bool.

This change is completely hidden from the API, and shouldn't change
anything, the feature is not enabled by default.
When the feature is disabled, the resulting code is exactly the same
before this commit.
@github-actions github-actions bot added the tock-libraries This affects libraries supported by the Tock project label Dec 22, 2023
@bradjc
Copy link
Contributor

bradjc commented Jan 4, 2024

Can you include an example output?

Also, the relevant doc is probably just the readme for the crate.

@Amjad50
Copy link
Contributor Author

Amjad50 commented Jan 5, 2024

Example: I'm using the example shown in the crate itself

tock_registers::register_bitfields! [u32,
    Control [
        RANGE OFFSET(4) NUMBITS(2) [
            VeryHigh = 0,
            High = 1,
            Low = 2
        ],
        EN  OFFSET(3) NUMBITS(1) [],
        INT OFFSET(2) NUMBITS(1) []
    ],
    Status [
        TXCOMPLETE  OFFSET(0) NUMBITS(1) [],
        TXINTERRUPT OFFSET(1) NUMBITS(1) [],
        RXCOMPLETE  OFFSET(2) NUMBITS(1) [],
        RXINTERRUPT OFFSET(3) NUMBITS(1) [],
        MODE        OFFSET(4) NUMBITS(3) [
            FullDuplex = 0,
            HalfDuplex = 1,
            Loopback = 2,
            Disabled = 3
        ],
        ERRORCOUNT OFFSET(6) NUMBITS(3) []
    ],
    InterruptFlags [
        UNDES   10,
        TXEMPTY  9,
        NSSR     8,
        OVRES    3,
        MODF     2,
        TDRE     1,
        RDRF     0
    ]
];

fn main() {
    let control = LocalRegisterCopy::<u32, Control::Register>::new(0x0);
    let status = LocalRegisterCopy::<u32, Status::Register>::new(0x0);
    let flags = LocalRegisterCopy::<u32, InterruptFlags::Register>::new(0xFF);

    println!("control = {:?}", control);
    println!("status = {:?}", status);
    println!("flags = {:?}", flags);
}

With the feature debug_registers it will output

control = Control { RANGE: VeryHigh, EN: false, INT: false }
status = Status { TXCOMPLETE: false, TXINTERRUPT: false, RXCOMPLETE: false, RXINTERRUPT: false, MODE: FullDuplex, ERRORCOUNT: 0 }
flags = InterruptFlags { UNDES: false, TXEMPTY: false, NSSR: false, OVRES: true, MODF: true, TDRE: true, RDRF: true }

Here we are using boolean for 1 bit fields, enums are printed correctly, and if the value is not 1 bit and not enum it is printed as a number

Without this feature it will just output normal

control = 0
status = 0
flags = 255

I put this specifically in LocalRegisterCopy only so that we don't issue multiple reads to the register which may cause unwanted behavior.

@lschuermann
Copy link
Member

lschuermann commented Jan 5, 2024

Thank you for this contribution. In general, we're somewhat hesitant to add compilation features to crates in the Tock ecosystem -- they are not easily testable in CI (in particular, all permutations of features), and also not fine grained. Given that many crates rely on the tock-registers crate, any single such reverse-dependency enabling this feature will cause it to be enabled globally, for all users of tock-registers. I think that the proposed feature is very useful, but it has the potential to increase flash size, possibly exceeding a board's flash space if enabled globally, for all debugged registers.

Another way to selectively add such more verbose debugging output could be a wrapper type, such as (with the trait named RegisterDebugInfo):

trait RegisterDebugInfo {
    // methods to query the required debug information

    fn debug<'a>(&'a self) -> RegisterDebug<'a, Self> {
        RegisterDebug(self)
    }
}

pub struct RegisterDebug<'a, R: RegisterDebugInfo>(&'a R); 

impl<'a, R: RegisterDebugInfo> fmt::Debug for RegisterDebug<'a, R> { ... }

This would allow us to avoid conditional compilation, while not generating any additional code if this infrastructure is not used.

Another improvement could be to avoid the use of macros to both parse and format our custom register_bitfields definitions, and to represent the various register fields through proper Rust types, accessible & discoverable through this RegisterDebug trait. Retrieving this information might require extending the code & interfaces we generate in the main register_bitfields macro though.

@Amjad50
Copy link
Contributor Author

Amjad50 commented Jan 7, 2024

Thanks @lschuermann , these are really nice ideas for optimization and reducing code.

I'll work on it.

Use very minimal info from the macro generating the fields, and instead
use the types system to opt into Debug.

This allows more flexible usage and also doesn't affect old implementation
Made it much simpler, now we just call `debug` and we pass the `DebugInfo` of the register.

This way we need to be explicit when passing the argument.

Not sure if its better this way?
@Amjad50 Amjad50 marked this pull request as draft January 15, 2024 13:47
Fixed compilation when we don't specify the flag.

TODO: we still need to modify the documentation.
Removed depndancy on `RegisterLongName`.

Added a new struct `DebugInfo` instead of a type, this will hold register debug information
We don't implement `DebugField` for `(Field,)` which is a valid tuple.

We don't need to do that, just fix how we create the tuple
This was removed by mistake while implemting `debug` for `LocalRegisterCopy`
Support up to 64 fields

I would really like to change this to something better somehow.
@Amjad50 Amjad50 marked this pull request as ready for review January 15, 2024 15:42
@Amjad50
Copy link
Contributor Author

Amjad50 commented Jan 15, 2024

Hello @lschuermann , I have implemented a new way to use debug.

It's called with reg.debug::<Register::Debug>().
This way we can opt in debugging easily, and all other registers will be ignored.

There are some stuff we can discuss.

  • We can make something like RegisterLongName that contain the details of the debug info that would return it instead of having to provide it manually in the debug function.
    I was trying to add a type field into RegisterLongName, but its implemented for () by default which made things more difficult, probably a new trait would be best here if we choose to.
  • For the parsing, I don't think we can expand on the bitmask macro sadly since we are processing registers as a whole and not fields.

Let me know what you think.

Thanks

Build docs for this feature as well
We don't need to implement for infinite numbers ourselves, since this is auto generated by macro, users won't need to write this structure

Since thats the case, we can do recursion.
For my testing on `tock` this didn't result in invocation overflow which is good
…s only

This is better in the sense that we can return an array of fields directly.
Copy link
Member

@lschuermann lschuermann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, the type & trait structure works out exactly as I have imagined and the implementation is very clean. I'm still a little concerned about the macro duplication we have going on here, so merging the impl_register_debug! with the register_bitfields! macros would be great.

…ebug` module by default

Now the `debug` traits and structs will be available by default, but the `debug` function won't be, so it should be all optimized away
@Amjad50
Copy link
Contributor Author

Amjad50 commented Jan 17, 2024

I'm thinking of removing the Debug struct and instead implement the RegisterDebugInfo in the Register itself, this will provide less code and better interface. But I'm not sure if this will result in more code getting added to the flash even if we didn't call debug()

@lschuermann
Copy link
Member

I'm thinking of removing the Debug struct and instead implement the RegisterDebugInfo in the Register itself, this will provide less code and better interface. But I'm not sure if this will result in more code getting added to the flash even if we didn't call debug()

I think that's a good idea. If those functions are not used, they shouldn't have any runtime overheads (flash or RAM). One exception has historically been when you convert such a type into a trait object, which will generate a vtable that then holds references to these methods. If this is still an issue it needs to be solved upstream with the Rust compiler, and we can work around it by simply avoiding conversions into trait objects.

I was also thinking about removing the feature-flag entirely. As long as we actively have to call debug(), and there is no overhead to this infrastructure otherwise, it's fine to always include it.

…bug`

This is much simpler to user, then the rest is handled by the Rust
compiler for optimization.

From `lschuermann`:
One exception has historically been when you convert such a type into a trait object,
which will generate a vtable that then holds references to these methods.
If this is still an issue it needs to be solved upstream with the Rust compiler,
and we can work around it by simply avoiding conversions into trait objects.
@Amjad50 Amjad50 requested review from lschuermann and bradjc January 17, 2024 14:27
bradjc
bradjc previously approved these changes Jan 22, 2024
Copy link
Contributor

@bradjc bradjc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks fine to me, but I'm not really a maintainer of the registers library.

@lschuermann
Copy link
Member

@Amjad50 I spent some good amount of time to look through all the code, and this is pretty awesome. The idea to build a sequence of types in the macro and then recurse through them, taking two mutable closures with you, is very clever. It took me quite some time to really understand why all of this works.

In hopes to improve on that a bit, I took the liberty to push a commit to this PR:

  • It improves documentation. In particular, it attempts to clearly communicate guarantees offered by the various APIs introduced, such as in the FieldValueEnumSeq (né EnumDebug) or RegisterDebugInfo trait. This should make it a little easier to understand what's going on.

  • It fixes a minor issue where the Debuggable trait description in the interfaces module documentation was misplaced.

  • It introduces a proper field value enum type sequence trait (FieldValueEnumSeq), which has a cons FieldValueEnumCons and nil FieldValueEnumNil constructor. While tuples offer a more compact way to represent the same, they are incredibly hard to read with many levels of nesting, and don't offer type uniqueness. Now when something goes wrong, we have a slightly more clear output (in the form of FieldValueEnumCons<u38, Uart::ENABLE::Value, FieldValueEnumNil>).

    Also, this new type sequence uses cons and nil constructors, versus a cons / last style sequence. This happens to be closer to constructs found in other functional languages and avoid special-casing the empty-sequence case.

  • Adds a small test + demo of the .debug() method to the fields module doc-comment.

Let me know whether these changes look good to you.

This commit refactors the newly introduced RegisterDebugInfo /
RegisterDebugValue infrastructure:

- It improves documentation. In particular, it attempts to clearly
  communicate guarantees offered by the various APIs introduced, such
  as in the `FieldValueEnumSeq` (né `EnumDebug`) or
  `RegisterDebugInfo` trait. It fixes a minor issue where the
  `Debuggable` trait description in the `interfaces` module
  documentation was misplaced.

- It introduces a proper field value enum type sequence trait
  (`FieldValueEnumSeq`), which has a cons `FieldValueEnumCons` and nil
  `FieldValueEnumNil` constructor. While tuples offer a more compact
  way to represent the same, they are incredibly hard to read with
  many levels of nesting, and don't offer type uniqueness.

  Also, this new type sequence uses _cons_ and _nil_ constructors,
  versus a _cons_ / _last_ style sequence. This happens to be closer
  to constructs found in other functional languages and avoid
  special-casing the empty-sequence case.

- Adds a small test + demo of the `.debug()` method to the `fields`
  module doc-comment.
@Amjad50
Copy link
Contributor Author

Amjad50 commented Jan 27, 2024

Thanks @lschuermann, these are really nice additions and improvements.
I feel sorry for making it hard to understand. Thanks for the fixes and documentation.
I like the new way of recursing into a nil better than doing it in pairs.

Beside the above small comment, I think the new changes are very good.

Copy link
Member

@lschuermann lschuermann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking over my commit. I think this is a pretty cool and solid addition. Also, the size-check benchmarks confirm that this doesn't have any impact on flash size: https://github.com/tock/tock/actions/runs/7557181806/job/20654379855 (the small differences can be explain with a diverging base branch).

I feel sorry for making it hard to understand.

Oh, no worries -- it wasn't particularly hard to understand. Sometimes it just helps to have a second set of eyes look over it, in particular for changes that include a lot of macro-rules magic.

Can you perhaps update the PR title to reflect that this no longer requires the feature? I think then this is good to go.

@Amjad50 Amjad50 changed the title tock-registers: added debug_registers feat to generate better Debug tock-registers: added debug() method in registers for better human readable debug output Jan 27, 2024
@Amjad50
Copy link
Contributor Author

Amjad50 commented Jan 27, 2024

Updated!

@lschuermann lschuermann requested a review from ppannuto January 27, 2024 19:12
@lschuermann lschuermann added the last-call Final review period for a pull request. label Jan 27, 2024
@bradjc bradjc added this pull request to the merge queue Jan 30, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jan 30, 2024
@lschuermann
Copy link
Member

Error: fatal: unable to access 'https://github.com/tock/tock/': Could not resolve host: github.com
Error: The process '/usr/local/bin/git' failed with exit code 128

Seems to have been a fluke with GitHub's Mac runner.

@bradjc bradjc added this pull request to the merge queue Jan 30, 2024
Merged via the queue into tock:master with commit 84a281c Jan 30, 2024
@lschuermann lschuermann mentioned this pull request Jun 27, 2025
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
last-call Final review period for a pull request. tock-libraries This affects libraries supported by the Tock project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants