ITM (cortex-m): How to share 'stim' across functions?

Hello :crab:,

I want to share a mutable reference to cortex_m::peripheral::itm::Stim across multiple functions, and I'm not sure what is the ergonomic way to do so in Rust.

Most examples on using ITM looks similar to the program below, which takes ownership of the peripherals in fn main() and using them inside fn main().

#![no_main]
#![no_std]

use panic_itm as _;
use stm32f1xx_hal as _;

use cortex_m::{iprintln, Peripherals};
use cortex_m_rt::entry;

#[entry]
fn main() -> ! {
    let mut p = Peripherals::take().unwrap();
    let stim = &mut p.ITM.stim[0];

    iprintln!(stim, "Hello, world!");

    loop {}
}

I want to call iprintln!() from multiple functions, but unlike println!() it also takes a reference to
cortex_m::peripheral::itm::Stim as a parameter.

I initially had two options to emerge in mind, and both don't seem to be good solutions.

  1. passing a reference to Stim every time
    This option seems undesirable, since it incurs overhead of passing an extra parameter every time function foo() is called (Let's assume that the function being called is long to be inlined).

  2. Sharing Stim globally by defining stim as static mut?
    I think this option will make the program look very ugly and it just doesn't feel right..
    I assume the Peripherals::take() API intends to prevent data races to peripherals,
    and simply using a static mut just feels awkward.

Is there an ergonomic way to share Stim across multiple functions, or should I go with Option 2??

I'd really appreciate any advice! Thank you for reading along :cat:

// Example of passing `Stim` as parameter every time (Option 1)
#![no_main]
#![no_std]

use panic_itm as _;
use stm32f1xx_hal as _;

use cortex_m::peripheral::itm::Stim
use cortex_m::{iprintln, Peripherals};
use cortex_m_rt::entry;

fn foo(stim: &mut Stim) {
    iprintln!(stim, "HELLO WORLD");
    
    // Imagine very lo~~~ng code exists here
    // that makes it undesirable to inline this function
}

#[entry]
fn main() -> ! {
    let mut p = Peripherals::take().unwrap();
    let stim = &mut p.ITM.stim[0];

    iprintln!(stim, "Hello, world!");

    for _ in 10000 {
        foo(stim);
    }

    loop {}
}

I can think of 2 possible solutions here:

  1. Use cortex_m::peripheral::Peripherals::steal()
    This would require a call to an unsafe function, and could cause a data race, meaning it has the same downsides as the 2nd option you provided.
  2. Use the RTFM framework
    The RTFM framework is meant to solve problems exactly like this, using the Resources feature. The link takes you to the RTFM docs on resouces, and all the information you need should be in there.

Hope this helps. :grinning:

1 Like

Wow, this is really useful. Thank you for the pointers! :crab:

Just a follow-up...

Besides the steal API suggested by @Spadi0,
I found another way to share ITM across function boundaries
( iprintln!(unsafe { &mut (*ITM::ptr()).stim[0] }, "BAD CODE"); ).
It works, but it's not desirable due to data race issues.

One interesting thing I found was that using * on ITM::ptr() vs.
calling .read() on ITM::ptr() gives different results..
At the moment, I'm not sure whether this is a compilation bug, or
* operator and .read() on raw pointers have different semantics.

use cortex_m::peripheral::ITM;
#[entry]
fn main() -> ! {
	if let (Some(p), Some(cp)) = (stm32::Peripherals::take(), stm32::CorePeripherals::take()) {
	    // Constrain clock registers
	    let rcc = p.RCC.constrain();
	    // Configure clock to 168 MHz (i.e. the maximum) and freeze it
		rcc.cfgr.sysclk(168.mhz()).freeze();
		
	    let mut itm = cp.ITM;
	    let stim = &mut itm.stim[0];
		iprintln!(stim, "Hello, world!");

		// Below line doesn't print anything via ITM, and blocks any later prints to ITM
		// iprintln!(unsafe { &mut ITM::ptr().read().stim[0] }, "BAD CODE");

		// Below line works properly as expected!
		iprintln!(unsafe { &mut (*ITM::ptr()).stim[0] }, "BAD CODE");
	}
    loop {	}
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.