Skip to content

Commit

Permalink
Use critical-section 1.0 for locking.
Browse files Browse the repository at this point in the history
This allows having no platform-specific code in rtt-target at all,
as long as the user imports the right critical-section implementation.
  • Loading branch information
Dirbaio committed Aug 19, 2022
1 parent f1d5b9c commit 98854f6
Show file tree
Hide file tree
Showing 5 changed files with 19 additions and 133 deletions.
17 changes: 3 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,13 @@ Target side implementation of the RTT (Real-Time Transfer) I/O protocol. RTT imp

## Platform support

While this crate is platform agnostic, some platform-specific code is needed for locking if you want to use the global `rprintln!` macro.
To use the global `rprintln!` macro, a platform-specific [`critical-section`](https://github.com/rust-embedded/critical-section) implementation is needed for locking.

If using Cortex-M or RISC-V, there is built-in support with a feature flag:

```toml
# Cargo.toml
rtt-target = { version = "x.y.z", features = ["cortex-m"] }
# or
rtt-target = { version = "x.y.z", features = ["riscv"] }
```

Otherwise, check the documentation for the `set_print_channel_cs` function.

Output directly to a channel object with `write!` or the binary `write` method does not require locking and therefore does not need any platform-specific code.
Output directly to a channel object with `write!` or the binary `write` method does not require locking and therefore does not need any platform-specific critical section.

## Usage

With a platform support feature, printing is as simple as:
With a platform-specific critical section in use, printing is as simple as:

```rust
use rtt_target::{rtt_init_print, rprintln};
Expand Down
6 changes: 3 additions & 3 deletions examples-cortex-m/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ authors = ["Matti Virkkunen <mvirkkunen@gmail.com>"]
edition = "2018"

[dependencies]
cortex-m = "0.7.1"
cortex-m-rt = "0.6.12"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7"
panic-halt = "0.2.0"
rtt-target = { path = "../rtt-target", features = ["cortex-m"] }
rtt-target = { path = "../rtt-target" }
ufmt = "0.1.0"

[[bin]]
Expand Down
2 changes: 1 addition & 1 deletion panic-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ authors = ["Matti Virkkunen <mvirkkunen@gmail.com>"]
edition = "2018"

[dependencies]
cortex-m-rt = "0.6.12"
cortex-m-rt = "0.7"
panic-rtt-target = { path = "../panic-rtt-target", features = ["cortex-m"] }
rtt-target = { path = "../rtt-target" }
7 changes: 1 addition & 6 deletions rtt-target/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,5 @@ repository = "https://github.com/mvirkkunen/rtt-target"

[dependencies]
ufmt-write = "0.1.0"
critical-section = "1.0.0"

# Platform specific stuff
cortex-m = { version = "0.7.1", optional = true }
riscv = { version = "0.6.0", optional = true }

[package.metadata.docs.rs]
features = ["cortex-m"]
120 changes: 11 additions & 109 deletions rtt-target/src/print.rs
Original file line number Diff line number Diff line change
@@ -1,80 +1,16 @@
use crate::{TerminalChannel, TerminalWriter, UpChannel};
use core::cell::RefCell;
use core::fmt::{self, Write as _};
use core::mem::MaybeUninit;
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};

static CRITICAL_SECTION: AtomicPtr<CriticalSectionFunc> = AtomicPtr::new(core::ptr::null_mut());
static mut PRINT_TERMINAL: MaybeUninit<TerminalChannel> = MaybeUninit::uninit();

/// Type-erased critical section function used to synchronize printing.
///
/// When called, the function must establish a critical section and call `f` within it, passing
/// `arg` as the argument.
pub type CriticalSectionFunc = fn(arg: *mut (), f: fn(arg: *mut ()) -> ()) -> ();
use critical_section::Mutex;

/// Sets the channel to use for [`rprint`] and [`rprintln`] and the critical section function used
/// to synchronize printing. You should only use this function if the [`set_print_channel`] function
/// isn't available on your platform.
///
/// # Example
///
/// Because the function takes a *static reference to a function pointer* as an argument, the call
/// requires a manual cast. Luckily Rust will automatically promote a reference to a suitable
/// closure to `'static`. In this example, `interrupt::free` is a function that establishes a
/// critical section and calls the supplied function.
///
/// ```
/// use rtt_target::{rtt_init_detault, rprintln};
/// use platform_specific::interrupt;
///
/// fn main() -> ! {
/// let channels = rtt_init_detault!();
///
/// unsafe {
/// rtt_target::set_print_channel_cs(
/// channels.up.0,
/// &((|arg, f| interrupt::free(|_| f(arg))) as rtt_target::CriticalSectionFunc),
/// );
/// }
///
/// loop {
/// rprintln!("Hello, world!");
/// }
/// }
/// ```
///
/// # Safety
///
/// This function is unsafe because the user must guarantee that the `cs` function pointer passed in
/// adheres to the [`CriticalSectionFunc`] specification.
pub unsafe fn set_print_channel_cs(channel: UpChannel, cs: &'static CriticalSectionFunc) {
cs(channel.0 as *mut (), |channel_ptr| {
ptr::write(
PRINT_TERMINAL.as_mut_ptr(),
TerminalChannel::new(UpChannel(channel_ptr as *mut crate::rtt::RttChannel)),
);
});
use crate::{TerminalChannel, TerminalWriter, UpChannel};

CRITICAL_SECTION.store(cs as *const _ as *mut _, Ordering::SeqCst);
}
static PRINT_TERMINAL: Mutex<RefCell<Option<TerminalChannel>>> = Mutex::new(RefCell::new(None));

/// Sets the channel to use for [`rprint`] and [`rprintln`].
///
/// This function is available only if you have enabled a platform support feature. Otherwise,
/// [`set_print_channel_cs`] must be used.
#[cfg(any(feature = "cortex-m", feature = "riscv"))]
pub fn set_print_channel(channel: UpChannel) {
#[cfg(feature = "cortex-m")]
use cortex_m as arch;
#[cfg(feature = "riscv")]
use riscv as arch;
unsafe {
set_print_channel_cs(
channel,
&((|arg, f| arch::interrupt::free(|_| f(arg))) as CriticalSectionFunc),
);
}
critical_section::with(|cs| {
*PRINT_TERMINAL.borrow_ref_mut(cs) = Some(TerminalChannel::new(UpChannel(channel.0)))
});
}

/// Public due to access from macro.
Expand All @@ -83,22 +19,11 @@ pub mod print_impl {
use super::*;

fn with_writer<F: Fn(TerminalWriter) -> ()>(number: u8, f: F) {
let cs = CRITICAL_SECTION.load(Ordering::SeqCst);

if !cs.is_null() {
// If the critical section pointer has been set, PRINT_TERMINAL must also have been set.

let args = (number, f);

unsafe {
(&*cs)(&args as *const _ as *mut (), |args_ptr| {
let args = &*(args_ptr as *const (u8, F));
let term = &mut *PRINT_TERMINAL.as_mut_ptr();

(args.1)(term.write(args.0));
});
critical_section::with(|cs| {
if let Some(term) = &mut *PRINT_TERMINAL.borrow_ref_mut(cs) {
f(term.write(number))
}
}
});
}

/// Public due to access from macro.
Expand Down Expand Up @@ -177,10 +102,6 @@ macro_rules! rprintln {
///
/// The optional arguments specify the blocking mode (default: `NoBlockSkip`) and size of the buffer
/// in bytes (default: 1024). See [`rtt_init`] for more details.
///
/// This macro is defined only if the [`set_print_channel`] function is available, i.e. if you have
/// enabled a platform support feature.
#[cfg(any(feature = "cortex-m", feature = "riscv"))]
#[macro_export]
macro_rules! rtt_init_print {
($mode:ident, $size:literal) => {
Expand All @@ -205,22 +126,3 @@ macro_rules! rtt_init_print {
$crate::rtt_init_print!(NoBlockSkip, 1024);
};
}

/// This version of the macro only is defined if no platform support feature is enabled and outputs
/// a more friendly error message.
#[cfg(not(any(feature = "cortex-m", feature = "riscv")))]
#[macro_export]
macro_rules! rtt_init_print {
($($_:tt)*) => {
compile_error!(concat!(
"rtt_init_print! is only available if a platform support feature is enabled.\r\n",
"Solutions:\r\n",
"- Enable a platform support feature:\r\n",
" # Cargo.toml\r\n",
" rtt-target = { version = \"x.y.z\", features = [\"cortex-m\"] }\r\n",
" # or",
" rtt-target = { version = \"x.y.z\", features = [\"riscv\"] }\r\n",
"- OR use set_print_channel_cs() instead if you want to provide your own locking.\r\n"
))
};
}

0 comments on commit 98854f6

Please sign in to comment.