Use local patched jlink_rtt module

This commit is contained in:
Berkus Decker 2019-03-03 04:32:12 +02:00
parent 53f2596665
commit 963a1a2bbf
3 changed files with 247 additions and 2 deletions

View File

@ -22,7 +22,7 @@ default-target = "targets/aarch64-vesper-metta.json"
unstable = []
realtime = []
noserial = []
jlink = ['jlink_rtt']
jlink = [] #'jlink_rtt'
#[lib]
#name = "nucleus"
@ -37,7 +37,7 @@ bitflags = "1.0.1"
register = "0.3.2"
cortex-a = "2.4"
#embedded-serial = "0.5.0"
jlink_rtt = { version = "0.1.0", optional = true }
# jlink_rtt = { version = "0.1.0", optional = true }
[profile.dev]
panic = "abort" # @todo try panic_rtt when feature jlink

244
src/jlink_rtt.rs Normal file
View File

@ -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(())
}
}

View File

@ -28,6 +28,7 @@ extern crate rlibc;
pub mod arch;
pub use arch::*;
mod devices;
mod jlink_rtt;
mod macros;
pub mod platform;
mod sync;