From b3e48318159257db6233671f66667c5358f27267 Mon Sep 17 00:00:00 2001 From: Brian Cully Date: Mon, 12 Aug 2019 12:07:21 -0400 Subject: Get RTC logger working. --- ble/.gdbinit | 6 +-- ble/Cargo.lock | 35 +++++++++++++++ ble/Cargo.toml | 16 +++++++ ble/src/logger.rs | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ble/src/macros.rs | 15 +++++++ ble/src/main.rs | 70 ++++++++++++++++++++++-------- ble/src/rtc.rs | 68 +++++++++++++++++++++++++++++ 7 files changed, 316 insertions(+), 21 deletions(-) create mode 100644 ble/src/logger.rs create mode 100644 ble/src/macros.rs create mode 100644 ble/src/rtc.rs (limited to 'ble') diff --git a/ble/.gdbinit b/ble/.gdbinit index a32f910..c276c1a 100644 --- a/ble/.gdbinit +++ b/ble/.gdbinit @@ -7,7 +7,5 @@ set print asm-demangle on #target extended-remote :3333 target remote :3333 #monitor flash breakpoints 1 -#monitor semihosting enable -#monitor semihosting IOClient 3 -monitor reset -load +monitor arm semihosting enable +#monitor arm semihosting IOClient 3 diff --git a/ble/Cargo.lock b/ble/Cargo.lock index 42cc690..f706586 100644 --- a/ble/Cargo.lock +++ b/ble/Cargo.lock @@ -39,14 +39,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "ble" version = "0.1.0" dependencies = [ + "clint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m-rt 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-semihosting 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "nrf52840-hal 0.8.1", "nrf52840-mdk-bsp 0.1.0", "panic-semihosting 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "rubble 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "rubble-nrf52 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "starb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -59,6 +64,19 @@ name = "cast" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cfg-if" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clint" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cortex-m 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cortex-m" version = "0.6.0" @@ -123,6 +141,14 @@ dependencies = [ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nb" version = "0.1.2" @@ -270,6 +296,11 @@ name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "starb" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "syn" version = "0.15.44" @@ -321,6 +352,8 @@ dependencies = [ "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" +"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" +"checksum clint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "200296af81a4cbe7da52afd9e5c35b7782eca865678caf2280251b777c9c37aa" "checksum cortex-m 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3c18719fdc57db65668bfc977db9a0fa1a41d718c5d9cd4f652c9d4b0e0956a" "checksum cortex-m-rt 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "17805910e3ecf029bdbfcc42b7384d9e3d9e5626153fa810002c1ef9839338ac" "checksum cortex-m-rt-macros 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d7ae692573e0acccb1579fef1abf5a5bf1d2f3f0149a22b16870ec9309aee25f" @@ -328,6 +361,7 @@ dependencies = [ "checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b" "checksum fpa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f074479d683e5a8fd0bf1251d0a5d91b0d9178b867b44962191ed0eaaf8d4009" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" "checksum nrf52840-pac 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e375b19eec5ae6c75387ea2fff3d5a6f989d65a0af350fcfe854f31243bfdfe2" "checksum panic-semihosting 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "97cfb37c1d3b5f0cc18bf14485018cccd13bdd24f7b5bfd456c1d8760afef824" @@ -343,6 +377,7 @@ dependencies = [ "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +"checksum starb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "36de9f301031f2a692afe144c8a757950d2b60b7fbf447055b8e19a9c232fb2c" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" diff --git a/ble/Cargo.toml b/ble/Cargo.toml index ad7b888..ad11b18 100644 --- a/ble/Cargo.toml +++ b/ble/Cargo.toml @@ -11,8 +11,13 @@ rubble = "0.0" rubble-nrf52 = "0.0" cortex-m = "~0.6" cortex-m-rt = "~0.6" +embedded-hal = "0.2" panic-semihosting = "~0.5" nb = "~0.1" +log = "0.4" +starb = "0.1" +clint = "0.2" +cortex-m-semihosting = "0.3" [features] default = ["rt", "rubble-nrf52/52840"] @@ -20,3 +25,14 @@ rt = ["nrf52840-hal/rt"] [patch.crates-io] nrf52840-hal = { path = "../../nrf52-hal/nrf52840-hal" } + +[profile.dev] +incremental = false +codegen-units = 1 +debug = true +lto = false + +[profile.release] +debug = true +lto = true +opt-level = "s" diff --git a/ble/src/logger.rs b/ble/src/logger.rs new file mode 100644 index 0000000..f102b74 --- /dev/null +++ b/ble/src/logger.rs @@ -0,0 +1,127 @@ +use crate::rtc; + +use core::cell::UnsafeCell; +use core::fmt::{self, Write}; +use log::{Metadata, Record}; +use nrf52840_hal::{target::UARTE0, uarte, Uarte}; +use starb::{Reader, RingBuffer, Writer}; + +static mut UARTE0: usize = 0; + +struct JoinedRingBuffer<'a> { + lbr: Reader<'a, u8>, + lbw: Writer<'a, u8>, +} + +impl<'a> JoinedRingBuffer<'a> { + const fn new(rb: &'a RingBuffer) -> Self { + let (lbr, lbw) = rb.split(); + Self { lbr: lbr, lbw: lbw } + } +} + +impl fmt::Write for JoinedRingBuffer<'_> { + fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { + for b in s.bytes() { + if let Err(_) = self.lbw.unshift(b) { + // Ignore buffer full errors for logging. + return Ok(()); + } + } + Ok(()) + } +} + +static mut LB: RingBuffer = RingBuffer::::new(0); +static mut JRB: JoinedRingBuffer = unsafe { JoinedRingBuffer::new(&LB) }; + +// The UART isn't necessarily Sync, so wrap it in something that +// is. As long as flush() is only called from one thread, we're fine, +// but this is a guarantee that the logger module doesn't make. +pub struct WriteWrapper { + w: W, +} +impl WriteWrapper { + pub fn new(writer: W) -> Self { + Self { w: writer } + } +} +unsafe impl Sync for WriteWrapper {} + +pub struct SerialLogger { + writer: UnsafeCell>>, +} + +impl SerialLogger +where + W: uarte::Instance, +{ + pub fn new(writer: WriteWrapper>) -> Self { + // Stash this for unsafe usage in case there's an issue with + // the rest of the logging. + unsafe { UARTE0 = core::mem::transmute(&writer.w) }; + Self { + writer: UnsafeCell::new(writer), + } + } +} +unsafe impl Send for SerialLogger {} +unsafe impl Sync for SerialLogger {} + +impl log::Log for SerialLogger +where + W: uarte::Instance, +{ + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= log::max_level() + } + + fn log(&self, record: &Record) { + if !self.enabled(record.metadata()) { + return; + } + + let jrb = unsafe { &mut JRB }; + write!( + jrb, + "[{}] {} {} -- {}\r\n", + rtc::millis(), + record.level(), + record.target(), + record.args() + ) + .ok(); + } + + fn flush(&self) { + // Unsafe due to mutable static. We can only deal with the + // tail position of the buffer here to keep things safe. + let jrb = unsafe { &mut JRB }; + if jrb.lbr.is_empty() { + return; + } + + let writer = unsafe { &mut (*self.writer.get()) }; + while let Some(b) = jrb.lbr.shift() { + // TODO: The UARTE peripheral uses DMA to send a bunch of + // stuff at speed, in hardware. It would be nice to use + // it, but we can't just take a slice of the ring buffer, + // since we don't know about its internal structure here. + writer.w.write(&[b]).expect("writing log"); + } + } +} + +// Write to the UART right now, instead of putting it on a ring +// buffer. This function is a huge hack, and only useful for debugging +// either before the main loop starts or if the ring buffer is broken. +pub unsafe fn write_fmt_now(args: fmt::Arguments, nl: bool) { + if UARTE0 == 0 { + return; + } + let uart: &mut Uarte = core::mem::transmute(UARTE0); + fmt::write(uart, args).expect("writing fmt now to uart"); + if nl { + uart.write_str("\r\n").expect("writing nl now to uart"); + } +} diff --git a/ble/src/macros.rs b/ble/src/macros.rs new file mode 100644 index 0000000..46d2d07 --- /dev/null +++ b/ble/src/macros.rs @@ -0,0 +1,15 @@ +#[macro_export] +macro_rules! logln_now { + ($($arg:tt)*) => { + unsafe {crate::logger::write_fmt_now(format_args!($($arg)*), true);} + }; + (_) => {}; +} + +#[macro_export] +macro_rules! log_now { + ($($arg:tt)*) => { + unsafe {crate::logger::write_fmt_now(format_args!($($arg)*), false);} + }; + (_) => {}; +} diff --git a/ble/src/main.rs b/ble/src/main.rs index 8b8c513..6f167a5 100644 --- a/ble/src/main.rs +++ b/ble/src/main.rs @@ -1,39 +1,75 @@ //! BLE interface. + #![no_std] #![no_main] -use core::fmt::Write; +mod logger; +mod macros; +mod rtc; + +use clint::HandlerArray; +use core::mem; +use cortex_m::asm::wfi; use cortex_m_rt::entry; -use nb::block; +use log::{info, trace, LevelFilter}; #[allow(unused_imports)] extern crate panic_semihosting; use nrf52840_mdk_bsp::{ hal::{ - prelude::*, - timer::{self, Timer}, + target::{interrupt, Interrupt, UARTE0}, + Clocks, Rtc, }, Board, }; +// TODO: +// * set up serial reader for trinket +// * set up i²c interface for keyboard reports +// * and, finally, bluetooth + +// Interrupt handler table. +static HANDLERS: HandlerArray = HandlerArray::new(); + #[entry] fn main() -> ! { - let mut nrf52 = Board::take().unwrap(); + let nrf52 = Board::take().unwrap(); + + let uart_wrapped = logger::WriteWrapper::new(nrf52.cdc); + let logger = logger::SerialLogger::new(uart_wrapped); + let logger_ref: &'static logger::SerialLogger = unsafe { mem::transmute(&logger) }; + log::set_logger(logger_ref).expect("setting logger"); + log::set_max_level(LevelFilter::Trace); + + nrf52.RTC0.intenset.write(|w| w.tick().set()); + + let mut nvic = nrf52.NVIC; + let mut rtc_handler = rtc::setup(Rtc::new(nrf52.RTC0), Clocks::new(nrf52.CLOCK)); - let mut timer = Timer::new(nrf52.TIMER0); + logln_now!("logln_now?"); + HANDLERS.with_overrides(|hs| { + hs.register(0, &mut rtc_handler); + nvic.enable(Interrupt::RTC0); - write!(nrf52.cdc, "Bootstrap complete.").ok(); - loop { - write!(nrf52.cdc, ".").ok(); - delay(&mut timer, 1_000_000); // 1s - } + info!("Bootstrap complete."); + let mut last_tick = rtc::millis(); + last_tick -= last_tick % 1024; + loop { + let tick = rtc::millis(); + if tick >= last_tick + 1024 { + last_tick = tick; + last_tick -= last_tick % 1024; + trace!("."); + } + wfi(); + } + }); + unreachable!(); } -fn delay(timer: &mut Timer, cycles: u32) -where - T: timer::Instance, -{ - timer.start(cycles); - let _ = block!(timer.wait()); +// TODO: RTC0/1/2 exist. See which is best, if it matters. +#[interrupt] +fn RTC0() { + HANDLERS.call(0); } diff --git a/ble/src/rtc.rs b/ble/src/rtc.rs new file mode 100644 index 0000000..8fe581f --- /dev/null +++ b/ble/src/rtc.rs @@ -0,0 +1,68 @@ +use core::sync::atomic::{AtomicUsize, Ordering}; +use log::trace; +use nrf52840_hal::{ + clocks::LfOscStopped, + rtc::{self, RtcInterrupt}, + Clocks, Rtc, +}; + +struct MilliClock(AtomicUsize); +impl MilliClock { + const fn new() -> Self { + Self(AtomicUsize::new(0)) + } + + #[allow(unused)] + fn set(&self, millis: usize) { + self.0.store(millis, Ordering::SeqCst) + } + + #[allow(unused)] + fn add(&self, val: usize) -> usize { + self.0.fetch_add(val, Ordering::SeqCst) + } + + // Slightly less than 1ms, due to using a 32,768Hz clock, we can't + // hit exactly 1ms, so we shoot for a bit under. + fn millis(&self) -> usize { + self.0.load(Ordering::SeqCst) + } +} + +static CLOCK: MilliClock = MilliClock::new(); + +pub fn setup( + mut rtc: Rtc, + clock: Clocks, +) -> impl FnMut() +where + R: rtc::Instance, +{ + trace!("setting up clock"); + + clock.set_lfclk_src_rc().start_lfclk(); + + // Try to set clock to 1kHz. + match rtc.set_prescaler(32) { + Ok(()) => trace!("set prescaler!"), + Err(x) => trace!("couldn't set prescaler: {:?}", x), + } + // Make sure event is cleared before start. + let _ = rtc.get_event_triggered(RtcInterrupt::Tick, true); + + let mut rtc = rtc.enable_counter(); + move || handler(&mut rtc) +} + +pub fn millis() -> usize { + CLOCK.millis() +} + +fn handler(rtc: &mut Rtc) +where + R: rtc::Instance, +{ + let _ = rtc.get_event_triggered(RtcInterrupt::Tick, true); + CLOCK.add(1); + log::logger().flush(); +} -- cgit v1.2.3