diff --git a/boards/acd52832/src/main.rs b/boards/acd52832/src/main.rs index c844ccaf..d60bbc00 100644 --- a/boards/acd52832/src/main.rs +++ b/boards/acd52832/src/main.rs @@ -304,23 +304,26 @@ pub unsafe fn reset_handler() { ); // RTT communication channel + let name = b"Terminal\0"; + let up_buffer_name = name; + let down_buffer_name = name; + let up_buffer = static_init!([u8; 1024], [0; 1024]); + let down_buffer = static_init!([u8; 32], [0; 32]); + let rtt_memory = static_init!( capsules::segger_rtt::SeggerRttMemory, - capsules::segger_rtt::SeggerRttMemory::new( - b"Terminal\0", - &mut capsules::segger_rtt::UP_BUFFER, - b"Terminal\0", - &mut capsules::segger_rtt::DOWN_BUFFER + capsules::segger_rtt::SeggerRttMemory::new_raw( + up_buffer_name, + up_buffer.as_ptr(), + up_buffer.len(), + down_buffer_name, + down_buffer.as_ptr(), + down_buffer.len() ) ); let rtt = static_init!( capsules::segger_rtt::SeggerRtt>, - capsules::segger_rtt::SeggerRtt::new( - virtual_alarm_rtt, - rtt_memory, - &mut capsules::segger_rtt::UP_BUFFER, - &mut capsules::segger_rtt::DOWN_BUFFER - ) + capsules::segger_rtt::SeggerRtt::new(virtual_alarm_rtt, rtt_memory, up_buffer, down_buffer) ); hil::time::Alarm::set_client(virtual_alarm_rtt, rtt); diff --git a/boards/nordic/nrf52840_dongle/src/main.rs b/boards/nordic/nrf52840_dongle/src/main.rs index 31d0b392..5a9da538 100644 --- a/boards/nordic/nrf52840_dongle/src/main.rs +++ b/boards/nordic/nrf52840_dongle/src/main.rs @@ -11,7 +11,7 @@ use kernel::component::Component; #[allow(unused_imports)] use kernel::{debug, debug_gpio, debug_verbose, static_init}; use nrf52840::gpio::Pin; -use nrf52dk_base::{SpiPins, UartPins}; +use nrf52dk_base::{SpiPins, UartChannel, UartPins}; // The nRF52840 Dongle LEDs const LED1_PIN: Pin = Pin::P0_06; @@ -130,7 +130,7 @@ pub unsafe fn reset_handler() { LED2_G_PIN, LED2_B_PIN, led, - &UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD), + UartChannel::Pins(UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD)), &SpiPins::new(SPI_MOSI, SPI_MISO, SPI_CLK), &None, button, diff --git a/boards/nordic/nrf52840dk/Cargo.toml b/boards/nordic/nrf52840dk/Cargo.toml index 171c497f..9d16f679 100644 --- a/boards/nordic/nrf52840dk/Cargo.toml +++ b/boards/nordic/nrf52840dk/Cargo.toml @@ -17,6 +17,10 @@ lto = true opt-level = "z" debug = true +[features] +usb_debugging = [] +default = ["usb_debugging"] + [dependencies] components = { path = "../../components" } cortexm4 = { path = "../../../arch/cortex-m4" } diff --git a/boards/nordic/nrf52840dk/src/io.rs b/boards/nordic/nrf52840dk/src/io.rs index 4c4f3bf3..608fa9ca 100644 --- a/boards/nordic/nrf52840dk/src/io.rs +++ b/boards/nordic/nrf52840dk/src/io.rs @@ -1,19 +1,46 @@ use core::fmt::Write; use core::panic::PanicInfo; use cortexm4; +#[cfg(feature = "usb_debugging")] +use kernel::common::cells::TakeCell; use kernel::debug; use kernel::debug::IoWrite; use kernel::hil::led; +#[cfg(not(feature = "usb_debugging"))] use kernel::hil::uart::{self, Configure}; use nrf52840::gpio::Pin; use crate::PROCESSES; struct Writer { + #[cfg(not(feature = "usb_debugging"))] initialized: bool, + #[cfg(feature = "usb_debugging")] + rtt_memory: TakeCell<'static, capsules::segger_rtt::SeggerRttMemory<'static>>, } +#[cfg(not(feature = "usb_debugging"))] static mut WRITER: Writer = Writer { initialized: false }; +#[cfg(feature = "usb_debugging")] +static mut WRITER: Writer = Writer { + rtt_memory: TakeCell::empty(), +}; + +#[cfg(feature = "usb_debugging")] +fn wait() { + let mut x = 0; + for i in 0..5000 { + unsafe { core::ptr::write_volatile(&mut x as *mut _, i) }; + } +} + +/// Set the RTT memory buffer used to output panic messages. +#[cfg(feature = "usb_debugging")] +pub unsafe fn set_rtt_memory( + rtt_memory: &'static mut capsules::segger_rtt::SeggerRttMemory<'static>, +) { + WRITER.rtt_memory.replace(rtt_memory); +} impl Write for Writer { fn write_str(&mut self, s: &str) -> ::core::fmt::Result { @@ -23,6 +50,7 @@ impl Write for Writer { } impl IoWrite for Writer { + #[cfg(not(feature = "usb_debugging"))] fn write(&mut self, buf: &[u8]) { let uart = unsafe { &mut nrf52840::uart::UARTE0 }; if !self.initialized { @@ -42,6 +70,30 @@ impl IoWrite for Writer { while !uart.tx_ready() {} } } + + #[cfg(feature = "usb_debugging")] + fn write(&mut self, buf: &[u8]) { + // TODO: initialize if needed. + self.rtt_memory.map(|rtt_memory| { + let up_buffer = &mut rtt_memory.up_buffer; + let buffer_len = up_buffer.length.get(); + let buffer = unsafe { + core::slice::from_raw_parts_mut( + up_buffer.buffer.get() as *mut u8, + buffer_len as usize, + ) + }; + + let mut write_position = up_buffer.write_position.get(); + + for &c in buf { + buffer[write_position as usize] = c; + write_position = (write_position + 1) % buffer_len; + up_buffer.write_position.set(write_position); + wait(); + } + }); + } } #[cfg(not(test))] diff --git a/boards/nordic/nrf52840dk/src/main.rs b/boards/nordic/nrf52840dk/src/main.rs index 57c42436..0b19ea3f 100644 --- a/boards/nordic/nrf52840dk/src/main.rs +++ b/boards/nordic/nrf52840dk/src/main.rs @@ -68,7 +68,9 @@ use kernel::component::Component; #[allow(unused_imports)] use kernel::{debug, debug_gpio, debug_verbose, static_init}; use nrf52840::gpio::Pin; -use nrf52dk_base::{SpiMX25R6435FPins, SpiPins, UartPins}; +#[cfg(not(feature = "usb_debugging"))] +use nrf52dk_base::UartPins; +use nrf52dk_base::{SpiMX25R6435FPins, SpiPins, UartChannel}; // The nRF52840DK LEDs (see back of board) const LED1_PIN: Pin = Pin::P0_13; @@ -83,9 +85,13 @@ const BUTTON3_PIN: Pin = Pin::P0_24; const BUTTON4_PIN: Pin = Pin::P0_25; const BUTTON_RST_PIN: Pin = Pin::P0_18; +#[cfg(not(feature = "usb_debugging"))] const UART_RTS: Pin = Pin::P0_05; +#[cfg(not(feature = "usb_debugging"))] const UART_TXD: Pin = Pin::P0_06; +#[cfg(not(feature = "usb_debugging"))] const UART_CTS: Pin = Pin::P0_07; +#[cfg(not(feature = "usb_debugging"))] const UART_RXD: Pin = Pin::P0_08; const SPI_MOSI: Pin = Pin::P0_20; @@ -123,6 +129,37 @@ pub unsafe fn reset_handler() { // Loads relocations and clears BSS nrf52840::init(); + // Initialize Segger RTT as early as possible so that any panic beyond this point can use the + // RTT memory object. + #[cfg(feature = "usb_debugging")] + let (up_buffer, down_buffer, rtt_memory) = { + let name = b"Terminal\0"; + let up_buffer_name = name; + let down_buffer_name = name; + let up_buffer = static_init!([u8; 1024], [0; 1024]); + let down_buffer = static_init!([u8; 32], [0; 32]); + + let rtt_memory = static_init!( + capsules::segger_rtt::SeggerRttMemory, + capsules::segger_rtt::SeggerRttMemory::new_raw( + up_buffer_name, + up_buffer.as_ptr(), + up_buffer.len(), + down_buffer_name, + down_buffer.as_ptr(), + down_buffer.len() + ) + ); + + (up_buffer, down_buffer, rtt_memory) + }; + + // XXX: This is inherently unsafe as it aliases the mutable reference to rtt_memory. This + // aliases reference is only used inside a panic handler, which should be OK, but maybe we + // should use a const reference to rtt_memory and leverage interior mutability instead. + #[cfg(feature = "usb_debugging")] + self::io::set_rtt_memory(&mut *(rtt_memory as *mut _)); + let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&PROCESSES)); let gpio = components::gpio::GpioComponent::new(board_kernel).finalize( components::gpio_component_helper!( @@ -198,7 +235,10 @@ pub unsafe fn reset_handler() { LED2_PIN, LED3_PIN, led, - &UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD), + #[cfg(feature = "usb_debugging")] + UartChannel::Rtt(up_buffer, down_buffer, rtt_memory), + #[cfg(not(feature = "usb_debugging"))] + UartChannel::Pins(UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD)), &SpiPins::new(SPI_MOSI, SPI_MISO, SPI_CLK), &Some(SpiMX25R6435FPins::new( SPI_MX25R6435F_CHIP_SELECT, diff --git a/boards/nordic/nrf52dk/src/main.rs b/boards/nordic/nrf52dk/src/main.rs index 693f1e18..b49518ff 100644 --- a/boards/nordic/nrf52dk/src/main.rs +++ b/boards/nordic/nrf52dk/src/main.rs @@ -68,7 +68,7 @@ use kernel::component::Component; #[allow(unused_imports)] use kernel::{debug, debug_gpio, debug_verbose, static_init}; use nrf52832::gpio::Pin; -use nrf52dk_base::{SpiPins, UartPins}; +use nrf52dk_base::{SpiPins, UartChannel, UartPins}; // The nRF52 DK LEDs (see back of board) const LED1_PIN: Pin = Pin::P0_17; @@ -199,7 +199,7 @@ pub unsafe fn reset_handler() { LED2_PIN, LED3_PIN, led, - &UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD), + UartChannel::Pins(UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD)), &SpiPins::new(SPI_MOSI, SPI_MISO, SPI_CLK), &None, button, diff --git a/boards/nordic/nrf52dk_base/src/lib.rs b/boards/nordic/nrf52dk_base/src/lib.rs index 17be93a4..ddac9dbd 100644 --- a/boards/nordic/nrf52dk_base/src/lib.rs +++ b/boards/nordic/nrf52dk_base/src/lib.rs @@ -70,6 +70,15 @@ impl UartPins { } } +pub enum UartChannel<'a> { + Pins(UartPins), + Rtt( + &'a mut [u8], + &'a mut [u8], + &'a mut capsules::segger_rtt::SeggerRttMemory<'a>, + ), +} + /// Supported drivers by the platform pub struct Platform { ble_radio: &'static capsules::ble_advertising_driver::BLE< @@ -136,7 +145,7 @@ pub unsafe fn setup_board( debug_pin2_index: Pin, debug_pin3_index: Pin, led: &'static capsules::led::LED<'static>, - uart_pins: &UartPins, + uart_channel: UartChannel<'static>, spi_pins: &SpiPins, mx25r6435f: &Option, button: &'static capsules::button::Button<'static>, @@ -232,6 +241,38 @@ pub unsafe fn setup_board( let alarm = components::alarm::AlarmDriverComponent::new(board_kernel, mux_alarm) .finalize(components::alarm_component_helper!(nrf52::rtc::Rtc)); + let channel: &dyn kernel::hil::uart::Uart = match uart_channel { + UartChannel::Pins(uart_pins) => { + nrf52::uart::UARTE0.initialize( + nrf52::pinmux::Pinmux::new(uart_pins.txd as u32), + nrf52::pinmux::Pinmux::new(uart_pins.rxd as u32), + Some(nrf52::pinmux::Pinmux::new(uart_pins.cts as u32)), + Some(nrf52::pinmux::Pinmux::new(uart_pins.rts as u32)), + ); + &nrf52::uart::UARTE0 + } + UartChannel::Rtt(up_buffer, down_buffer, rtt_memory) => { + // Virtual alarm for the Segger RTT communication channel + let virtual_alarm_rtt = static_init!( + capsules::virtual_alarm::VirtualMuxAlarm<'static, nrf52::rtc::Rtc>, + capsules::virtual_alarm::VirtualMuxAlarm::new(mux_alarm) + ); + + // RTT communication channel + let rtt = static_init!( + capsules::segger_rtt::SeggerRtt>, + capsules::segger_rtt::SeggerRtt::new( + virtual_alarm_rtt, + rtt_memory, + up_buffer, + down_buffer + ) + ); + hil::time::Alarm::set_client(virtual_alarm_rtt, rtt); + rtt + } + }; + let dynamic_deferred_call_clients = static_init!([DynamicDeferredCallClientState; 2], Default::default()); let dynamic_deferred_caller = static_init!( @@ -241,19 +282,10 @@ pub unsafe fn setup_board( DynamicDeferredCall::set_global_instance(dynamic_deferred_caller); // Create a shared UART channel for the console and for kernel debug. - let uart_mux = components::console::UartMuxComponent::new( - &nrf52::uart::UARTE0, - 115200, - dynamic_deferred_caller, - ) - .finalize(()); - - nrf52::uart::UARTE0.initialize( - nrf52::pinmux::Pinmux::new(uart_pins.txd as u32), - nrf52::pinmux::Pinmux::new(uart_pins.rxd as u32), - Some(nrf52::pinmux::Pinmux::new(uart_pins.cts as u32)), - Some(nrf52::pinmux::Pinmux::new(uart_pins.rts as u32)), - ); + let uart_mux = + components::console::UartMuxComponent::new(channel, 115200, dynamic_deferred_caller) + .finalize(()); + let pconsole = components::process_console::ProcessConsoleComponent::new(board_kernel, uart_mux) .finalize(()); diff --git a/capsules/src/segger_rtt.rs b/capsules/src/segger_rtt.rs index 1bf93fc1..33103c57 100644 --- a/capsules/src/segger_rtt.rs +++ b/capsules/src/segger_rtt.rs @@ -91,67 +91,69 @@ //! ``` use core::cell::Cell; -use kernel::common::cells::{OptionalCell, TakeCell}; +use core::marker::PhantomData; +use kernel::common::cells::{OptionalCell, TakeCell, VolatileCell}; use kernel::hil; use kernel::hil::time::Frequency; use kernel::hil::uart; use kernel::ReturnCode; -/// Buffer for transmitting to the host. -pub static mut UP_BUFFER: [u8; 1024] = [0; 1024]; - -/// Buffer for receiving messages from the host. -pub static mut DOWN_BUFFER: [u8; 32] = [0; 32]; - /// This structure is defined by the segger RTT protocol. It must exist in /// memory in exactly this form so that the segger JTAG tool can find it in the /// chip's memory and read and write messages to the appropriate buffers. #[repr(C)] -pub struct SeggerRttMemory { - id: [u8; 16], - number_up_buffers: u32, - number_down_buffers: u32, - up_buffer: SeggerRttBuffer, - down_buffer: SeggerRttBuffer, +pub struct SeggerRttMemory<'a> { + id: VolatileCell<[u8; 16]>, + number_up_buffers: VolatileCell, + number_down_buffers: VolatileCell, + pub up_buffer: SeggerRttBuffer<'a>, + down_buffer: SeggerRttBuffer<'a>, } #[repr(C)] -pub struct SeggerRttBuffer { - name: *const u8, // Pointer to the name of this channel. Must be a 4 byte thin pointer. - buffer: *const u8, // Pointer to the buffer for this channel. - length: u32, - write_position: u32, - read_position: u32, - flags: u32, +pub struct SeggerRttBuffer<'a> { + name: VolatileCell<*const u8>, // Pointer to the name of this channel. Must be a 4 byte thin pointer. + pub buffer: VolatileCell<*const u8>, // Pointer to the buffer for this channel. + pub length: VolatileCell, + pub write_position: VolatileCell, + read_position: VolatileCell, + flags: VolatileCell, + _lifetime: PhantomData<&'a [u8]>, } -impl SeggerRttMemory { - pub fn new( +impl SeggerRttMemory<'a> { + pub fn new_raw( up_buffer_name: &'a [u8], - up_buffer: &'static mut [u8], - down_buffer_name: &'static [u8], - down_buffer: &'static mut [u8], - ) -> SeggerRttMemory { + up_buffer_ptr: *const u8, + up_buffer_len: usize, + down_buffer_name: &'a [u8], + down_buffer_ptr: *const u8, + down_buffer_len: usize, + ) -> SeggerRttMemory<'a> { SeggerRttMemory { + // TODO: only write this ID when the object is fully initialized, to avoid having + // these bytes elsewhere in (flash) memory. // Must be "SEGGER RTT". - id: *b"SEGGER RTT\0\0\0\0\0\0", - number_up_buffers: 1, - number_down_buffers: 1, + id: VolatileCell::new(*b"SEGGER RTT\0\0\0\0\0\0"), + number_up_buffers: VolatileCell::new(1), + number_down_buffers: VolatileCell::new(1), up_buffer: SeggerRttBuffer { - name: up_buffer_name.as_ptr(), - buffer: up_buffer.as_ptr(), - length: 1024, - write_position: 0, - read_position: 0, - flags: 0, + name: VolatileCell::new(up_buffer_name.as_ptr()), + buffer: VolatileCell::new(up_buffer_ptr), + length: VolatileCell::new(up_buffer_len as u32), + write_position: VolatileCell::new(0), + read_position: VolatileCell::new(0), + flags: VolatileCell::new(0), + _lifetime: PhantomData, }, down_buffer: SeggerRttBuffer { - name: down_buffer_name.as_ptr(), - buffer: down_buffer.as_ptr(), - length: 32, - write_position: 0, - read_position: 0, - flags: 0, + name: VolatileCell::new(down_buffer_name.as_ptr()), + buffer: VolatileCell::new(down_buffer_ptr), + length: VolatileCell::new(down_buffer_len as u32), + write_position: VolatileCell::new(0), + read_position: VolatileCell::new(0), + flags: VolatileCell::new(0), + _lifetime: PhantomData, }, } } @@ -159,9 +161,9 @@ impl SeggerRttMemory { pub struct SeggerRtt<'a, A: hil::time::Alarm<'a>> { alarm: &'a A, // Dummy alarm so we can get a callback. - config: TakeCell<'a, SeggerRttMemory>, - up_buffer: TakeCell<'static, [u8]>, - _down_buffer: TakeCell<'static, [u8]>, + config: TakeCell<'a, SeggerRttMemory<'a>>, + up_buffer: TakeCell<'a, [u8]>, + _down_buffer: TakeCell<'a, [u8]>, client: OptionalCell<&'a dyn uart::TransmitClient>, client_buffer: TakeCell<'static, [u8]>, tx_len: Cell, @@ -170,9 +172,9 @@ pub struct SeggerRtt<'a, A: hil::time::Alarm<'a>> { impl<'a, A: hil::time::Alarm<'a>> SeggerRtt<'a, A> { pub fn new( alarm: &'a A, - config: &'a mut SeggerRttMemory, - up_buffer: &'static mut [u8], - down_buffer: &'static mut [u8], + config: &'a mut SeggerRttMemory<'a>, + up_buffer: &'a mut [u8], + down_buffer: &'a mut [u8], ) -> SeggerRtt<'a, A> { SeggerRtt { alarm: alarm, @@ -205,15 +207,15 @@ impl<'a, A: hil::time::Alarm<'a>> uart::Transmit<'a> for SeggerRtt<'a, A> { // Copy the incoming data into the buffer. Once we increment // the `write_position` the RTT listener will go ahead and read // the message from us. - let mut index = config.up_buffer.write_position as usize; - let buffer_len = config.up_buffer.length as usize; + let mut index = config.up_buffer.write_position.get() as usize; + let buffer_len = config.up_buffer.length.get() as usize; for i in 0..tx_len { buffer[(i + index) % buffer_len] = tx_data[i]; } index = (index + tx_len) % buffer_len; - config.up_buffer.write_position = index as u32; + config.up_buffer.write_position.set(index as u32); self.tx_len.set(tx_len); // Save the client buffer so we can pass it back with the callback. self.client_buffer.replace(tx_data);