1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
#![no_std]
/// 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 {
    unsafe 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 {
            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 {
        Self {}
    }
}

impl fmt::Write for Output {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        unsafe {
            _SEGGER_RTT.init();
            _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(), true) {
                    self.blocked = true;
                }
            }
        }
        Ok(())
    }
}