Skip to content

Commit 95e9c89

Browse files
committed
trace: use different DWT channels/units for enter/exit events
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.
1 parent ac43bd0 commit 95e9c89

File tree

2 files changed

+44
-24
lines changed

2 files changed

+44
-24
lines changed

cortex-m-rtic-trace/macros/src/lib.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,19 @@ pub fn trace(_attrs: TokenStream, item: TokenStream) -> TokenStream {
2222
.unwrap();
2323

2424
// Insert a statement at the start and end of the given function
25-
// that writes the unique task ID to the watchpoint address.
26-
//
27-
// TODO do not write 4 bytes. That denotes a trace clock
28-
// frequency.
29-
let trace_stmt = syn::parse2::<Stmt>(quote!(
30-
::cortex_m_rtic_trace::__write_trace_payload(#task_id);
25+
// that writes the unique task ID to the respecpive watchpoint
26+
// address.
27+
let prologue = syn::parse2::<Stmt>(quote!(
28+
::cortex_m_rtic_trace::__write_enter_id(#task_id);
3129
))
3230
.unwrap();
33-
let mut stmts = vec![trace_stmt.clone()];
31+
let epilogue = syn::parse2::<Stmt>(quote!(
32+
::cortex_m_rtic_trace::__write_exit_id(#task_id);
33+
))
34+
.unwrap();
35+
let mut stmts = vec![prologue.clone()];
3436
stmts.append(&mut fun.block.stmts);
35-
stmts.push(trace_stmt);
37+
stmts.push(epilogue);
3638
stmts
3739
};
3840

cortex-m-rtic-trace/src/lib.rs

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,14 @@
4747
/// function. Refer to crate example usage.
4848
pub use rtic_trace_macros::trace;
4949

50-
// TODO is there an even better way to store this?
51-
static mut WATCH_VARIABLE: u32 = 0;
50+
struct WatchVars {
51+
/// Watch variable to which the just entered software task ID is written to.
52+
enter: u32,
53+
54+
/// Watch variable to which the just exited software task ID is written to.
55+
exit: u32,
56+
}
57+
static mut WATCH_VARIABLES: WatchVars = WatchVars { enter: 0, exit: 0 };
5258

5359
/// Auxilliary functions for peripheral configuration. Should be called
5460
/// in the init-function, and preferably in order of (1)
@@ -109,29 +115,41 @@ pub mod setup {
109115
/// tracing. The unit is indirectly utilized by [super::trace]. Any
110116
/// changes to the unit after this function yields undefined
111117
/// behavior, in regards to RTIC task tracing.
112-
pub fn assign_dwt_unit(dwt: &Core::dwt::Comparator) {
113-
let watch_address: u32 = unsafe { &super::WATCH_VARIABLE as *const _ } as u32;
114-
// TODO do we need to clear the MATCHED, bit[24] after every match?
115-
dwt.configure(ComparatorFunction::Address(ComparatorAddressSettings {
116-
address: watch_address,
117-
mask: 0,
118-
emit: EmitOption::Data,
119-
access_type: AccessType::WriteOnly,
120-
}))
121-
.unwrap(); // NOTE safe: valid (emit, access_type) used
118+
pub fn assign_dwt_units(enter_dwt: &Core::dwt::Comparator, exit_dwt: &Core::dwt::Comparator) {
119+
let enter_addr: u32 = unsafe { &super::WATCH_VARIABLES.enter as *const _ } as u32;
120+
let exit_addr: u32 = unsafe { &super::WATCH_VARIABLES.exit as *const _ } as u32;
121+
122+
for (dwt, addr) in [(enter_dwt, enter_addr), (exit_dwt, exit_addr)] {
123+
// TODO do we need to clear the MATCHED, bit[24] after every match?
124+
dwt.configure(ComparatorFunction::Address(ComparatorAddressSettings {
125+
address: addr,
126+
mask: 0,
127+
emit: EmitOption::Data,
128+
access_type: AccessType::WriteOnly,
129+
}))
130+
.unwrap(); // NOTE safe: valid (emit, access_type) used
131+
}
122132
}
123133
}
124134

135+
// TODO only write as much as needed. e.g. for id < 256, only 8 bits
136+
// must be written.
137+
125138
/// The function utilized by [trace] to write the unique software task
126139
/// ID to the watch address. You are discouraged to use this function
127140
/// directly; [trace] uses a sequence of task IDs compatible with the
128141
/// `parsing` module. If used directly, task IDs must also be properly
129142
/// configured for the host application.
130143
#[inline]
131-
pub fn __write_trace_payload(id: u32) {
132-
// TODO only write as much as needed. e.g. for id < 256, only 8 bits
133-
// must be written.
144+
pub fn __write_enter_id(id: u32) {
145+
unsafe {
146+
WATCH_VARIABLES.enter = id;
147+
}
148+
}
149+
150+
#[inline]
151+
pub fn __write_exit_id(id: u32) {
134152
unsafe {
135-
WATCH_VARIABLE = id;
153+
WATCH_VARIABLES.exit = id;
136154
}
137155
}

0 commit comments

Comments
 (0)