Skip to content

Commit

Permalink
trace: use different DWT channels/units for enter/exit events
Browse files Browse the repository at this point in the history
As per #47, using a single DWT channel is less robust than using two. By
using two, a state must not be kept on the host-side, and it allows us
to catch re-entry errors.

Do we really need to use two DWT units here? We should be able to watch
both the enter and exit addresses, and when one of them writes, the ITM
package should indicate which variable was written to.
  • Loading branch information
tmplt committed Oct 31, 2021
1 parent ac43bd0 commit 95e9c89
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 24 deletions.
18 changes: 10 additions & 8 deletions cortex-m-rtic-trace/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,19 @@ pub fn trace(_attrs: TokenStream, item: TokenStream) -> TokenStream {
.unwrap();

// Insert a statement at the start and end of the given function
// that writes the unique task ID to the watchpoint address.
//
// TODO do not write 4 bytes. That denotes a trace clock
// frequency.
let trace_stmt = syn::parse2::<Stmt>(quote!(
::cortex_m_rtic_trace::__write_trace_payload(#task_id);
// that writes the unique task ID to the respecpive watchpoint
// address.
let prologue = syn::parse2::<Stmt>(quote!(
::cortex_m_rtic_trace::__write_enter_id(#task_id);
))
.unwrap();
let mut stmts = vec![trace_stmt.clone()];
let epilogue = syn::parse2::<Stmt>(quote!(
::cortex_m_rtic_trace::__write_exit_id(#task_id);
))
.unwrap();
let mut stmts = vec![prologue.clone()];
stmts.append(&mut fun.block.stmts);
stmts.push(trace_stmt);
stmts.push(epilogue);
stmts
};

Expand Down
50 changes: 34 additions & 16 deletions cortex-m-rtic-trace/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,14 @@
/// function. Refer to crate example usage.
pub use rtic_trace_macros::trace;

// TODO is there an even better way to store this?
static mut WATCH_VARIABLE: u32 = 0;
struct WatchVars {
/// Watch variable to which the just entered software task ID is written to.
enter: u32,

/// Watch variable to which the just exited software task ID is written to.
exit: u32,
}
static mut WATCH_VARIABLES: WatchVars = WatchVars { enter: 0, exit: 0 };

/// Auxilliary functions for peripheral configuration. Should be called
/// in the init-function, and preferably in order of (1)
Expand Down Expand Up @@ -109,29 +115,41 @@ pub mod setup {
/// tracing. The unit is indirectly utilized by [super::trace]. Any
/// changes to the unit after this function yields undefined
/// behavior, in regards to RTIC task tracing.
pub fn assign_dwt_unit(dwt: &Core::dwt::Comparator) {
let watch_address: u32 = unsafe { &super::WATCH_VARIABLE as *const _ } as u32;
// TODO do we need to clear the MATCHED, bit[24] after every match?
dwt.configure(ComparatorFunction::Address(ComparatorAddressSettings {
address: watch_address,
mask: 0,
emit: EmitOption::Data,
access_type: AccessType::WriteOnly,
}))
.unwrap(); // NOTE safe: valid (emit, access_type) used
pub fn assign_dwt_units(enter_dwt: &Core::dwt::Comparator, exit_dwt: &Core::dwt::Comparator) {
let enter_addr: u32 = unsafe { &super::WATCH_VARIABLES.enter as *const _ } as u32;
let exit_addr: u32 = unsafe { &super::WATCH_VARIABLES.exit as *const _ } as u32;

for (dwt, addr) in [(enter_dwt, enter_addr), (exit_dwt, exit_addr)] {
// TODO do we need to clear the MATCHED, bit[24] after every match?
dwt.configure(ComparatorFunction::Address(ComparatorAddressSettings {
address: addr,
mask: 0,
emit: EmitOption::Data,
access_type: AccessType::WriteOnly,
}))
.unwrap(); // NOTE safe: valid (emit, access_type) used
}
}
}

// TODO only write as much as needed. e.g. for id < 256, only 8 bits
// must be written.

/// The function utilized by [trace] to write the unique software task
/// ID to the watch address. You are discouraged to use this function
/// directly; [trace] uses a sequence of task IDs compatible with the
/// `parsing` module. If used directly, task IDs must also be properly
/// configured for the host application.
#[inline]
pub fn __write_trace_payload(id: u32) {
// TODO only write as much as needed. e.g. for id < 256, only 8 bits
// must be written.
pub fn __write_enter_id(id: u32) {
unsafe {
WATCH_VARIABLES.enter = id;
}
}

#[inline]
pub fn __write_exit_id(id: u32) {
unsafe {
WATCH_VARIABLE = id;
WATCH_VARIABLES.exit = id;
}
}

0 comments on commit 95e9c89

Please sign in to comment.