Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cortex-m: Add initial dwt and dcb support #3246

Closed
wants to merge 5 commits into from

Conversation

codingHahn
Copy link
Contributor

Pull Request Overview

The DWT and DCB peripherals are used to access various debug functionality in the chip, such as various counters.
This commit adds register descriptions for most of the DWT and some of the DCB. It also adds a very simple capsule that allows reading the cycle counter from userspace.

Testing Strategy

This pull request was tested by getting the cycle count and it checks out.

TODO or Help Wanted

This pull request still needs the rest of the registers of some peripherals.

The capsule needs to be added to every supported board and could be expanded to grant access to the other capabilities of the peripherals. We also need to consider security implications for allowing debug peripherals in userspace.

Documentation Updated

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

Formatting

  • Ran make prepush.

The DWT and DCB peripherals are used to access various debug functionality
in the chip, such as various counters.
This commits adds register descriptions for most of DWT and some of DCB.
It also adds a very simple capsule that allows reading the cycle counter from
userspace.
arch/cortex-m/src/dcb.rs Outdated Show resolved Hide resolved
arch/cortex-m/src/dwt.rs Outdated Show resolved Hide resolved
const DWT: StaticRef<DwtRegisters> = unsafe { StaticRef::new(0xE0001000 as *const DwtRegisters) };

/// Enable the cycle counter
/// [`DCB::enable_debug_and_trace()`] needs to be enabled for this to work
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not just do that then?

@@ -8,3 +8,4 @@ edition = "2021"
kernel = { path = "../kernel" }
enum_primitive = { path = "../libraries/enum_primitive" }
tickv = { path = "../libraries/tickv" }
cortexm = { path = "../arch/cortex-m" }
Copy link
Contributor

Choose a reason for hiding this comment

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

We cannot make capsules dependent on something chip specific.

Instead, we have to use a HIL to abstract HW from capsules.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I'll take a look and amend the pr

use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite};
use kernel::utilities::StaticRef;

//TODO: Rest of the Registers
Copy link
Member

Choose a reason for hiding this comment

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

image

There's only three more here; I think we'd ask for the register definition to be fully implemented before pulling this in (i.e., flesh out the rest of register_structs and register bitfields; don't need all the getters/setters/syscalls at first).

arch/cortex-m/src/dcb.rs Show resolved Hide resolved
register_bitfields![u32,
DebugExceptionAndMonitorControl [
/// Write 1 to globally enable all DWT and ITM features.
/// RW.
Copy link
Member

Choose a reason for hiding this comment

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

This whole register is declared ReadWrite already; are the RO/WO fields? If not, these RWs should probably be removed.

Comment on lines 47 to 57
/// Enable the Debug and Trace unit `DWT`
/// This has to be enabled before using any feature of the `DWT`
pub fn enable_debug_and_trace() {
DCB.demcr
.modify(DebugExceptionAndMonitorControl::TRCENA::SET);
}

/// Disable the Debug and Trace unit `DWT`
pub unsafe fn disable_debug_and_trace() {
DCB.demcr
.modify(DebugExceptionAndMonitorControl::TRCENA::CLEAR);
Copy link
Member

Choose a reason for hiding this comment

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

Consider wrapping DWT in a PeripheralManager instance (e.g.

impl PeripheralManagement<TWIMClock> for I2CHw {
) to automate these calls and ensure enable-before-access.

@github-actions github-actions bot added the HIL This affects a Tock HIL interface. label Sep 29, 2022
Comment on lines +1 to +3
/// Interface for interacting with debug hardware integrated in various SoCs.
/// Currently allows reading the cycle counter and can be expanded to allow
/// access to other features in the future.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// Interface for interacting with debug hardware integrated in various SoCs.
/// Currently allows reading the cycle counter and can be expanded to allow
/// access to other features in the future.
//! Interface for interacting with debug hardware integrated in various SoCs.
//!
//! Currently allows reading the cycle counter.

use crate::ErrorCode;

pub trait PerformanceCounters {
/// Enable the cycle counter. Returns an error, if the cycle counter is not present.
Copy link
Contributor

Choose a reason for hiding this comment

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

Which error?

/// access to other features in the future.
use crate::ErrorCode;

pub trait PerformanceCounters {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pub trait PerformanceCounters {
pub trait CycleCounter {

?

@@ -0,0 +1,15 @@
/// Interface for interacting with debug hardware integrated in various SoCs.
Copy link
Contributor

Choose a reason for hiding this comment

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

"debug.rs" is somewhat generic, and there is the potential for confusion with kernel/src/debug.rs. Is there a name that would be somewhat more informative? Maybe hw_debug?

@@ -0,0 +1,51 @@
//! Provides a performance counter interface for userspace.
Copy link
Contributor

Choose a reason for hiding this comment

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

perf.rs is too short. If this is for cycle counts, let's name it that.

hudson-ayers
hudson-ayers previously approved these changes Oct 4, 2022
Copy link
Contributor

@hudson-ayers hudson-ayers left a comment

Choose a reason for hiding this comment

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

I would love to see DWT support merged, I end up using some modified version of what Amit wrote for the PowerClocks paper all the time when taking performance measurements, and its always a pain to have to throw it somewhere in the kernel.

While I don't think this PR has to be updated to include this, I do think it would be really nice to see this functionality added to kernel/src/debug.rs (similar to debug!() and debug_gpio!()) so that it is easy to take performance measurements from anywhere. This would require some sort of global that boards would initialize with the appropriate implementation of the HIL, or perhaps we could configure it using KernelResources

Comment on lines +87 to +118
/*
// TODO: register_bitfields
// Peripheral Identification Register 4
(0xFD0 => pid4: ReadOnly<u32, Pid4::Register>),

// Peripheral Identification Register 5
(0xFD4 => pid5: ReadOnly<u32, Pid5::Register>),

// Peripheral Identification Register 6
(0xFD8 => pid6: ReadOnly<u32, Pid6::Register>),

// Peripheral Identification Register 7
(0xFDC => pid7: ReadOnly<u32, Pid7::Register>),

// Peripheral Identification Register 0
(0xFE0 => pid0: ReadOnly<u32, Pid0::Register>),

// Peripheral Identification Register 1
(0xFE4 => pid1: ReadOnly<u32, Pid1::Register>),

// Peripheral Identification Register 2
(0xFE8 => pid2: ReadOnly<u32, Pid2::Register>),

// Peripheral Identification Register 3
(0xFEC => pid3: ReadOnly<u32, Pid3::Register>),

// Component Identification Register 0
(0xFF0 => cid0: ReadOnly<u32, Cid0::Register>),

// Component Identification Register 1
(0xFF4 => cid1: ReadOnly<u32, Cid1::Register>),

Copy link
Contributor

Choose a reason for hiding this comment

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

Lets just delete the unused peripheral identification registers unless you anticipate needing those for something in the short term.

}

impl hil::debug::PerformanceCounters for Dwt {
fn enable_cycle_counter() -> Result<(), ErrorCode> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Personally I would call this start_cycle_counter() since it actually starts it counting up, rather than just enabling the functionality. Same for disable_cycle_counter() -- I would just call that stop_cycle_counter(), and not have it disable the DCB.

}
}

impl hil::debug::PerformanceCounters for Dwt {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would also be helpful to add a reset_cycle_counter() method to this HIL to reset the counter to 0. This saves from having to get the current count and then perform subtraction, and it just an easy interface (imo). Amit wrote a rough DWT implementation a few years ago that I often use when benchmarking, and it just does the following:

pub fn reset_timer() {
    DEMCR.set(DEMCR.get() | 0x01000000);
    DWT.cycnt.set(0); // reset the counter
    DWT.ctrl.set(0); // disable counter;
}

Keeping in mind that on something like the nrf52 this counter will overflow in 67 seconds, it can actually be pretty helpful to be able to reset to 0 rather than have to consider the (somewhat high) possibility of overflow.

Err(ErrorCode::NOSUPPORT)
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

One other useful function from Amit's old DWT implementation was bench() which could be used to easily benchmark in-kernel code:

pub unsafe fn bench<F: FnOnce()>(f: F) -> u32 {
    reset_timer();
    start_timer();
    f();
    stop_timer();
    get_time()
}

I think it would be nice to see something like this included here.

Copy link
Contributor

@hudson-ayers hudson-ayers left a comment

Choose a reason for hiding this comment

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

@codingHahn will you have a chance to revisit this? If not, I will try to find time to update this with the various suggestions

@codingHahn
Copy link
Contributor Author

Yeah, sorry. I finished my bachelors thesis and had to settle in to working a full time job. I'd love to apply all your suggestions. Problem is that I don't have the board anymore because it belonged to my employer.

@hudson-ayers
Copy link
Contributor

Replaced by #3934

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
HIL This affects a Tock HIL interface.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants