Use local patched jlink_rtt module
This commit is contained in:
parent
53f2596665
commit
963a1a2bbf
|
@ -22,7 +22,7 @@ default-target = "targets/aarch64-vesper-metta.json"
|
||||||
unstable = []
|
unstable = []
|
||||||
realtime = []
|
realtime = []
|
||||||
noserial = []
|
noserial = []
|
||||||
jlink = ['jlink_rtt']
|
jlink = [] #'jlink_rtt'
|
||||||
|
|
||||||
#[lib]
|
#[lib]
|
||||||
#name = "nucleus"
|
#name = "nucleus"
|
||||||
|
@ -37,7 +37,7 @@ bitflags = "1.0.1"
|
||||||
register = "0.3.2"
|
register = "0.3.2"
|
||||||
cortex-a = "2.4"
|
cortex-a = "2.4"
|
||||||
#embedded-serial = "0.5.0"
|
#embedded-serial = "0.5.0"
|
||||||
jlink_rtt = { version = "0.1.0", optional = true }
|
# jlink_rtt = { version = "0.1.0", optional = true }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
panic = "abort" # @todo try panic_rtt when feature jlink
|
panic = "abort" # @todo try panic_rtt when feature jlink
|
||||||
|
|
|
@ -0,0 +1,244 @@
|
||||||
|
// Custom implementation of JLink RTT debug protocol
|
||||||
|
// jlink_rtt crate has too many strange bugs
|
||||||
|
|
||||||
|
/// This module implements a limited version of the Segger
|
||||||
|
/// Real Time Transfer protocol between the debugger host
|
||||||
|
/// and the target program.
|
||||||
|
/// RTT works by scanning memory to look for a control block
|
||||||
|
/// containing a magic string (it is also possible to tell
|
||||||
|
/// the monitor exactly where to find this block).
|
||||||
|
/// The control block defines a set of "up" channels
|
||||||
|
/// and "down" channels that are named pipes of communication
|
||||||
|
/// between the two systems.
|
||||||
|
/// Each of these channels is implemented as a simple
|
||||||
|
/// ring buffer.
|
||||||
|
/// The cost of logging data to RTT is the cost of formatting
|
||||||
|
/// and writing it to the ring buffer in memory.
|
||||||
|
use core::fmt;
|
||||||
|
use core::ptr;
|
||||||
|
|
||||||
|
static mut UP_BUF: [u8; 1024] = [0u8; 1024];
|
||||||
|
static mut DOWN_BUF: [u8; 16] = [0u8; 16];
|
||||||
|
|
||||||
|
/// Ring buffer for communicating between target and host.
|
||||||
|
/// This must be binary compatible with the RTT implementation
|
||||||
|
/// in the JLINK device.
|
||||||
|
#[repr(C)]
|
||||||
|
struct Buffer {
|
||||||
|
name: *const u8,
|
||||||
|
buf_start: *mut u8,
|
||||||
|
size_of_buffer: u32,
|
||||||
|
/// Position of next item to be written
|
||||||
|
/// Volatile as the host may change it.
|
||||||
|
write_offset: u32,
|
||||||
|
/// Position of next item to be read by host.
|
||||||
|
/// Volatile as the host may change it.
|
||||||
|
read_offset: u32,
|
||||||
|
/// In the segger library these flags control blocking
|
||||||
|
/// or non-blocking behavior. Those functions are
|
||||||
|
/// implemented differently here.
|
||||||
|
flags: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Buffer {
|
||||||
|
fn init(&mut self, buf: &mut [u8]) {
|
||||||
|
self.name = b"Terminal\0".as_ptr();
|
||||||
|
self.buf_start = buf.as_mut_ptr();
|
||||||
|
self.size_of_buffer = buf.len() as u32;
|
||||||
|
self.write_offset = 0;
|
||||||
|
self.read_offset = 0;
|
||||||
|
self.flags = 0; // Non-blocking mode
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_read_offset(&self) -> u32 {
|
||||||
|
unsafe { ptr::read_volatile(&self.read_offset as *const u32) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn set_read_offset(&mut self, offset: u32) {
|
||||||
|
unsafe {
|
||||||
|
ptr::write_volatile(&mut self.read_offset as *mut u32, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_write_offset(&self) -> u32 {
|
||||||
|
unsafe { ptr::read_volatile(&self.write_offset as *const u32) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_write_offset(&mut self, offset: u32) {
|
||||||
|
unsafe {
|
||||||
|
ptr::write_volatile(&mut self.write_offset as *mut u32, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write data to the ring buffer.
|
||||||
|
/// Returns true if all of the data was written, which
|
||||||
|
/// will always be the case if blocking==true.
|
||||||
|
/// Returns false if blocking==false and the buffer was
|
||||||
|
/// full.
|
||||||
|
fn write(&mut self, buf: &[u8], blocking: bool) -> bool {
|
||||||
|
let mut buf = buf;
|
||||||
|
let mut write_off = self.get_write_offset() as usize;
|
||||||
|
let size_of_buffer = self.size_of_buffer as usize;
|
||||||
|
while buf.len() > 0 {
|
||||||
|
let read_off = self.get_read_offset() as usize;
|
||||||
|
|
||||||
|
let wrapping_capacity = if read_off > write_off {
|
||||||
|
read_off - write_off - 1
|
||||||
|
} else {
|
||||||
|
size_of_buffer - (write_off - read_off + 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we're full and non-blocking, return now.
|
||||||
|
// Otherwise, we'll spin with a series of 0 byte
|
||||||
|
// length increments until the host consumes data
|
||||||
|
// from the ring buffer.
|
||||||
|
if wrapping_capacity == 0 && !blocking {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let flat_capacity = size_of_buffer - write_off;
|
||||||
|
|
||||||
|
let to_copy = buf.len().min(flat_capacity).min(wrapping_capacity);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ptr::copy(
|
||||||
|
buf.as_ptr(),
|
||||||
|
self.buf_start.offset(write_off as isize),
|
||||||
|
to_copy,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_off += to_copy;
|
||||||
|
if write_off == size_of_buffer {
|
||||||
|
write_off = 0;
|
||||||
|
}
|
||||||
|
self.set_write_offset(write_off as u32);
|
||||||
|
|
||||||
|
buf = &buf[to_copy..];
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The ControlBlock is the magic struct that the JLINK looks
|
||||||
|
/// for to discover the ring buffers.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ControlBlock {
|
||||||
|
/// Initialized to "SEGGER RTT"
|
||||||
|
id: [u8; 16],
|
||||||
|
/// Initialized to NUM_UP
|
||||||
|
max_up_buffers: i32,
|
||||||
|
/// Initialized to NUM_DOWN
|
||||||
|
max_down_buffers: i32,
|
||||||
|
/// Note that RTT allows for this to be an array of
|
||||||
|
/// "up" buffers of size max_up_buffers, but for simplicity
|
||||||
|
/// just a single buffer is implemented here.
|
||||||
|
up: Buffer,
|
||||||
|
/// Note that RTT allows for this to be an array of
|
||||||
|
/// "down" buffers of size max_down_buffers, but for simplicity
|
||||||
|
/// just a single buffer is implemented here.
|
||||||
|
down: Buffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Sync for ControlBlock {}
|
||||||
|
|
||||||
|
impl ControlBlock {
|
||||||
|
fn init(&mut self) {
|
||||||
|
if self.id[0] == b'S' {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsafe: use of mutable static
|
||||||
|
// mutable statics can be mutated by multiple threads: aliasing violations
|
||||||
|
// or data races will cause undefined behavior
|
||||||
|
unsafe {
|
||||||
|
self.up.init(&mut UP_BUF);
|
||||||
|
self.down.init(&mut DOWN_BUF);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compose the ident string such that we won't
|
||||||
|
// emit the string sequence in flash
|
||||||
|
self.id.copy_from_slice(b"_EGGER:RTT\0\0\0\0\0\0");
|
||||||
|
self.id[0] = b'S';
|
||||||
|
self.id[6] = b' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub static mut _SEGGER_RTT: ControlBlock = ControlBlock {
|
||||||
|
id: [0u8; 16],
|
||||||
|
max_up_buffers: 1,
|
||||||
|
max_down_buffers: 1,
|
||||||
|
up: Buffer {
|
||||||
|
name: 0 as *const u8,
|
||||||
|
buf_start: 0 as *mut u8,
|
||||||
|
read_offset: 0,
|
||||||
|
write_offset: 0,
|
||||||
|
flags: 0,
|
||||||
|
size_of_buffer: 0,
|
||||||
|
},
|
||||||
|
down: Buffer {
|
||||||
|
name: 0 as *const u8,
|
||||||
|
buf_start: 0 as *mut u8,
|
||||||
|
write_offset: 0,
|
||||||
|
read_offset: 0,
|
||||||
|
flags: 0,
|
||||||
|
size_of_buffer: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A blocking output stream allowing data to be logged from the
|
||||||
|
/// target to the host.
|
||||||
|
/// Implements fmt::Write.
|
||||||
|
pub struct Output {}
|
||||||
|
|
||||||
|
impl Output {
|
||||||
|
/// Create a blocking output stream
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
unsafe {
|
||||||
|
_SEGGER_RTT.init();
|
||||||
|
}
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Write for Output {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
unsafe {
|
||||||
|
_SEGGER_RTT.up.write(s.as_bytes(), true);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A non-blocking output stream allowing data to be logged from the
|
||||||
|
/// target to the host.
|
||||||
|
/// Implements fmt::Write.
|
||||||
|
pub struct NonBlockingOutput {
|
||||||
|
blocked: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NonBlockingOutput {
|
||||||
|
/// Create a non-blocking output stream
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { blocked: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Write for NonBlockingOutput {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
if !self.blocked {
|
||||||
|
unsafe {
|
||||||
|
_SEGGER_RTT.init();
|
||||||
|
if !_SEGGER_RTT.up.write(s.as_bytes(), false) {
|
||||||
|
self.blocked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ extern crate rlibc;
|
||||||
pub mod arch;
|
pub mod arch;
|
||||||
pub use arch::*;
|
pub use arch::*;
|
||||||
mod devices;
|
mod devices;
|
||||||
|
mod jlink_rtt;
|
||||||
mod macros;
|
mod macros;
|
||||||
pub mod platform;
|
pub mod platform;
|
||||||
mod sync;
|
mod sync;
|
||||||
|
|
Loading…
Reference in New Issue