diff --git a/firmware/defmt-rtt/README.md b/firmware/defmt-rtt/README.md index 7c69179c..72cbe0c9 100644 --- a/firmware/defmt-rtt/README.md +++ b/firmware/defmt-rtt/README.md @@ -12,6 +12,10 @@ The fastest way to get started with `defmt` is to use our [app-template] to set For more details about the framework check the book at https://defmt.ferrous-systems.com +## Customization + +The RTT buffer size (default: 1024) can be configured with the `DEFMT_RTT_BUFFER_SIZE` environment variable in a tight memory situation. Use a power of 2 for best performance. + ## Support `defmt-rtt` is part of the [Knurling] project, [Ferrous Systems]' effort at diff --git a/firmware/defmt-rtt/build.rs b/firmware/defmt-rtt/build.rs new file mode 100644 index 00000000..fa9aaf8d --- /dev/null +++ b/firmware/defmt-rtt/build.rs @@ -0,0 +1,21 @@ +use std::{env, path::PathBuf}; + +fn main() { + println!("cargo:rerun-if-env-changed=DEFMT_RTT_BUFFER_SIZE"); + + let size = env::var("DEFMT_RTT_BUFFER_SIZE") + .map(|s| { + s.parse() + .expect("could not parse DEFMT_RTT_BUFFER_SIZE as usize") + }) + .unwrap_or(1024_usize); + + let out_dir_path = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let out_file_path = out_dir_path.join("consts.rs"); + + std::fs::write( + out_file_path, + format!("pub(crate) const BUF_SIZE: usize = {};", size), + ) + .unwrap(); +} diff --git a/firmware/defmt-rtt/src/channel.rs b/firmware/defmt-rtt/src/channel.rs index ebe44bf7..3293bfe1 100644 --- a/firmware/defmt-rtt/src/channel.rs +++ b/firmware/defmt-rtt/src/channel.rs @@ -3,7 +3,7 @@ use core::{ sync::atomic::{AtomicUsize, Ordering}, }; -use crate::{MODE_BLOCK_IF_FULL, MODE_MASK, SIZE}; +use crate::{consts::BUF_SIZE, MODE_BLOCK_IF_FULL, MODE_MASK}; #[repr(C)] pub(crate) struct Channel { @@ -45,9 +45,9 @@ impl Channel { let available = if read > write { read - write - 1 } else if read == 0 { - SIZE - write - 1 + BUF_SIZE - write - 1 } else { - SIZE - write + BUF_SIZE - write }; if available == 0 { @@ -58,9 +58,9 @@ impl Channel { let len = bytes.len().min(available); unsafe { - if cursor + len > SIZE { + if cursor + len > BUF_SIZE { // split memcpy - let pivot = SIZE - cursor; + let pivot = BUF_SIZE - cursor; ptr::copy_nonoverlapping(bytes.as_ptr(), self.buffer.add(cursor), pivot); ptr::copy_nonoverlapping(bytes.as_ptr().add(pivot), self.buffer, len - pivot); } else { @@ -69,7 +69,7 @@ impl Channel { } } self.write - .store(write.wrapping_add(len) % SIZE, Ordering::Release); + .store(write.wrapping_add(len) % BUF_SIZE, Ordering::Release); len } @@ -77,13 +77,13 @@ impl Channel { fn nonblocking_write(&self, bytes: &[u8]) -> usize { let write = self.write.load(Ordering::Acquire); let cursor = write; - // NOTE truncate at SIZE to avoid more than one "wrap-around" in a single `write` call - let len = bytes.len().min(SIZE); + // NOTE truncate atBUF_SIZE to avoid more than one "wrap-around" in a single `write` call + let len = bytes.len().min(BUF_SIZE); unsafe { - if cursor + len > SIZE { + if cursor + len > BUF_SIZE { // split memcpy - let pivot = SIZE - cursor; + let pivot = BUF_SIZE - cursor; ptr::copy_nonoverlapping(bytes.as_ptr(), self.buffer.add(cursor), pivot); ptr::copy_nonoverlapping(bytes.as_ptr().add(pivot), self.buffer, len - pivot); } else { @@ -92,7 +92,7 @@ impl Channel { } } self.write - .store(write.wrapping_add(len) % SIZE, Ordering::Release); + .store(write.wrapping_add(len) % BUF_SIZE, Ordering::Release); len } diff --git a/firmware/defmt-rtt/src/consts.rs b/firmware/defmt-rtt/src/consts.rs new file mode 100644 index 00000000..849c10fc --- /dev/null +++ b/firmware/defmt-rtt/src/consts.rs @@ -0,0 +1,2 @@ +// see `build.rs` for contents +include!(concat!(env!("OUT_DIR"), "/consts.rs")); diff --git a/firmware/defmt-rtt/src/lib.rs b/firmware/defmt-rtt/src/lib.rs index bdc68778..023a66c1 100644 --- a/firmware/defmt-rtt/src/lib.rs +++ b/firmware/defmt-rtt/src/lib.rs @@ -28,6 +28,12 @@ use cortex_m::{interrupt, register}; use crate::channel::Channel; +mod consts; + +/// RTT buffer size. Default: 1024; can be customized by setting the `DEFMT_RTT_BUFFER_SIZE` environment variable. +/// Use a power of 2 for best performance. +use crate::consts::BUF_SIZE; + #[defmt::global_logger] struct Logger; @@ -94,10 +100,6 @@ const MODE_BLOCK_IF_FULL: usize = 2; /// Don't block if the RTT buffer is full. Truncate data to output as much as fits. const MODE_NON_BLOCKING_TRIM: usize = 1; -// TODO make configurable -// NOTE use a power of 2 for best performance -const SIZE: usize = 1024; - // make sure we only get shared references to the header/channel (avoid UB) /// # Safety /// `Channel` API is not re-entrant; this handle should not be held from different execution @@ -115,7 +117,7 @@ unsafe fn handle() -> &'static Channel { up_channel: Channel { name: NAME as *const _ as *const u8, buffer: unsafe { &mut BUFFER as *mut _ as *mut u8 }, - size: SIZE, + size: BUF_SIZE, write: AtomicUsize::new(0), read: AtomicUsize::new(0), flags: AtomicUsize::new(MODE_NON_BLOCKING_TRIM), @@ -124,7 +126,7 @@ unsafe fn handle() -> &'static Channel { #[cfg_attr(target_os = "macos", link_section = ".uninit,defmt-rtt.BUFFER")] #[cfg_attr(not(target_os = "macos"), link_section = ".uninit.defmt-rtt.BUFFER")] - static mut BUFFER: [u8; SIZE] = [0; SIZE]; + static mut BUFFER: [u8; BUF_SIZE] = [0; BUF_SIZE]; static NAME: &[u8] = b"defmt\0";