From 852c73760589e663e484c1783bd5b73c7df6907c Mon Sep 17 00:00:00 2001
From: Brian Cully <bjc@kublai.com>
Date: Tue, 13 Aug 2019 20:35:16 -0400
Subject: Use bulk starb read/write operations.

This should speed things up a bit, but, unfortunately, doesn't solve
my serial1 problems.
---
 ble/src/i2c.rs    |   8 ++--
 ble/src/logger.rs |  26 ++++--------
 ble/src/main.rs   |  38 +++++------------
 ble/src/uarte1.rs | 119 +++++++++++++++++++++++++++++++++++++++++-------------
 4 files changed, 111 insertions(+), 80 deletions(-)

(limited to 'ble/src')

diff --git a/ble/src/i2c.rs b/ble/src/i2c.rs
index 0f7fe5e..b5a5114 100644
--- a/ble/src/i2c.rs
+++ b/ble/src/i2c.rs
@@ -39,11 +39,9 @@ where
         let mut buf: [u8; 255] = [0; 255];
         match twis.read(&mut buf) {
             Ok(len) => {
-                for b in &buf[0..len] {
-                    // Ignore unshift errors for now, as I don't think
-                    // there's much we can do about it.
-                    writer.unshift(*b).ok();
-                }
+                // Ignore unshift overflow for now, as I don't think
+                // there's much we can do about it.
+                writer.unshift_from(&buf[0..len]);
                 trace!("write done");
             }
             Err(e) => error!("{:?}", e),
diff --git a/ble/src/logger.rs b/ble/src/logger.rs
index f102b74..b4167b2 100644
--- a/ble/src/logger.rs
+++ b/ble/src/logger.rs
@@ -2,6 +2,7 @@ use crate::rtc;
 
 use core::cell::UnsafeCell;
 use core::fmt::{self, Write};
+use core::mem::MaybeUninit;
 use log::{Metadata, Record};
 use nrf52840_hal::{target::UARTE0, uarte, Uarte};
 use starb::{Reader, RingBuffer, Writer};
@@ -22,12 +23,9 @@ impl<'a> JoinedRingBuffer<'a> {
 
 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(());
-            }
-        }
+        // Drop anything that couldn't fit on the ground so we don't
+        // back up.
+        self.lbw.unshift_from(s.as_bytes());
         Ok(())
     }
 }
@@ -94,20 +92,12 @@ where
     }
 
     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");
+        let mut buf: [u8; 256] = unsafe { MaybeUninit::<[u8; 256]>::uninit().assume_init() };
+        let len = jrb.lbr.shift_into(&mut buf);
+        if len > 0 {
+            writer.w.write(&buf[0..len]).expect("writing log")
         }
     }
 }
diff --git a/ble/src/main.rs b/ble/src/main.rs
index 3c98b7e..71e3b82 100644
--- a/ble/src/main.rs
+++ b/ble/src/main.rs
@@ -90,38 +90,20 @@ fn main() -> ! {
                 last_tick -= last_tick % 1024;
             }
 
-            if !twis_reader.is_empty() {
-                let mut i = 0;
-                for b in &mut twis_reader {
-                    if i == buf.len() {
-                        info!("i²c data: {:?}", &buf[0..i]);
-                        i = 0;
-                    }
-                    buf[i] = b;
-                    i += 1;
-                }
-                info!("i²c data: {:?}", &buf[0..i]);
+            let mut len = twis_reader.shift_into(&mut buf);
+            while len > 0 {
+                info!("i²c data: {:?}", &buf[0..len]);
+                len = twis_reader.shift_into(&mut buf);
             }
 
-            if !uarte1_reader.is_empty() {
-                let mut i = 0;
-                for b in &mut uarte1_reader {
-                    if i == buf.len() {
-                        info!(
-                            "serial - {}: {}",
-                            i,
-                            core::str::from_utf8(&buf[0..i]).expect("utf8conv")
-                        );
-                        i = 0;
-                    }
-                    buf[i] = b;
-                    i += 1;
-                }
+            let mut len = uarte1_reader.shift_into(&mut buf);
+            while len > 0 {
                 info!(
-                    "serial - {}: {}",
-                    i,
-                    core::str::from_utf8(&buf[0..i]).expect("utf8conv")
+                    "serial {}: {}",
+                    len,
+                    core::str::from_utf8(&buf[0..len]).expect("utf8conv")
                 );
+                len = uarte1_reader.shift_into(&mut buf);
             }
             wfi();
         }
diff --git a/ble/src/uarte1.rs b/ble/src/uarte1.rs
index 61d631e..1afce29 100644
--- a/ble/src/uarte1.rs
+++ b/ble/src/uarte1.rs
@@ -4,6 +4,42 @@ use starb::{Reader, RingBuffer, Writer};
 
 static mut RB: RingBuffer<u8> = RingBuffer::new(0);
 
+const BUFLEN: usize = 128;
+
+struct DoubleBuffer {
+    bank0: [u8; BUFLEN],
+    bank1: [u8; BUFLEN],
+    is_bank0: bool,
+}
+impl DoubleBuffer {
+    const fn new() -> Self {
+        Self {
+            bank0: [0; BUFLEN],
+            bank1: [0; BUFLEN],
+            is_bank0: true,
+        }
+    }
+
+    fn cur_ptr(&mut self) -> *mut u8 {
+        if self.is_bank0 {
+            self.bank0.as_mut_ptr()
+        } else {
+            self.bank1.as_mut_ptr()
+        }
+    }
+
+    fn next_ptr(&mut self) -> *mut u8 {
+        self.is_bank0 = !self.is_bank0;
+        if self.is_bank0 {
+            self.bank1.as_mut_ptr()
+        } else {
+            self.bank0.as_mut_ptr()
+        }
+    }
+}
+
+static mut DBL_BUFF: DoubleBuffer = DoubleBuffer::new();
+
 pub fn setup<I>(mut uarte: Uarte<I>) -> (Reader<'static, u8>, impl FnMut())
 where
     I: uarte::Instance,
@@ -15,60 +51,73 @@ where
         w.rxstarted().set_bit()
     });
 
-    let mut buf: [u8; 255] = [0; 255];
-    uarte
-        .0
-        .rxd
-        .ptr
-        .write(|w| unsafe { w.ptr().bits(buf.as_mut_ptr() as _) });
+    let ptr = unsafe { DBL_BUFF.next_ptr() };
+    info!("setting up dma: p: {:x}, maxcnt: {}", ptr as u32, BUFLEN);
+    uarte.0.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
     uarte
         .0
         .rxd
         .maxcnt
-        .write(|w| unsafe { w.maxcnt().bits(buf.len() as _) });
+        .write(|w| unsafe { w.maxcnt().bits(BUFLEN as _) });
     uarte.0.tasks_startrx.write(|w| w.tasks_startrx().set_bit());
     let (rbr, mut rbw) = unsafe { RB.split() };
 
     // start rx into a buffer. it would be nice if we could do this
     // with rxrdy or something like i2c does.
-    let cl = move || handler(&mut uarte, &mut rbw, &mut buf);
+    let cl = move || handler(&mut uarte, &mut rbw);
     (rbr, cl)
 }
 
-fn handler<I>(uarte: &mut Uarte<I>, writer: &mut Writer<u8>, buf: &mut [u8])
+fn handler<I>(uarte: &mut Uarte<I>, writer: &mut Writer<u8>)
 where
     I: uarte::Instance,
 {
     if uarte.0.events_endrx.read().events_endrx().bit_is_set() {
         uarte.0.events_endrx.write(|w| w.events_endrx().clear_bit());
 
-        // Copy DMA buffer to ring buffer.
-        let len = uarte.0.rxd.amount.read().amount().bits();
-        info!("endrx - {}", len);
-        for i in 0..len {
-            writer.unshift(buf[i as usize]).ok();
+        // The buffer should be swapped from the RXSTARTED event, so
+        // we can restart the read immediately.
+        let optr = uarte.0.rxd.ptr.read().ptr().bits();
+        let ptr = unsafe { DBL_BUFF.cur_ptr() as u32 };
+        /*
+        if optr == ptr {
+            uarte
+                .0
+                .rxd
+                .ptr
+                .write(|w| unsafe { w.ptr().bits(DBL_BUFF.next_ptr() as _) });
         }
+         */
+        uarte.0.tasks_startrx.write(|w| w.tasks_startrx().set_bit());
 
-        // Reset the DMA buffer. TODO: this is probably unneccesary.
-        uarte
-            .0
-            .rxd
-            .ptr
-            .write(|w| unsafe { w.ptr().bits(buf.as_mut_ptr() as _) });
-        uarte
-            .0
-            .rxd
-            .maxcnt
-            .write(|w| unsafe { w.maxcnt().bits(buf.len() as _) });
+        // Copy DMA buffer to ring buffer.
+        let len = uarte.0.rxd.amount.read().amount().bits();
+        let mc = uarte.0.rxd.maxcnt.read().maxcnt().bits();
+        info!(
+            "endrx - l: {}, p: {:x}, mc: {}, b: {:x}",
+            len, optr, mc, ptr
+        );
+        flush_buf(writer, unsafe { DBL_BUFF.cur_ptr() }, len.into());
 
-        // restart the read.
-        uarte.0.tasks_startrx.write(|w| w.tasks_startrx().set_bit());
+    /*
+    uarte
+        .0
+        .rxd
+        .maxcnt
+        .write(|w| unsafe { w.maxcnt().bits(BUFLEN as _) });
+     */
     } else if uarte.0.events_error.read().events_error().bit_is_set() {
         uarte.0.events_error.write(|w| w.events_error().clear_bit());
-        info!("error: {:b}", uarte.0.errorsrc.read().bits());
+
+        let len = uarte.0.rxd.amount.read().amount().bits();
+        info!("error: {:b} {}b", uarte.0.errorsrc.read().bits(), len,);
     } else if uarte.0.events_rxto.read().events_rxto().bit_is_set() {
         uarte.0.events_rxto.write(|w| w.events_rxto().clear_bit());
-        info!("rxto");
+
+        let len = uarte.0.rxd.amount.read().amount().bits();
+        info!("rxto - {}", len);
+        flush_buf(writer, unsafe { DBL_BUFF.cur_ptr() }, len.into());
+        uarte.0.tasks_flushrx.write(|w| w.tasks_flushrx().set_bit());
     } else if uarte
         .0
         .events_rxstarted
@@ -81,5 +130,17 @@ where
             .events_rxstarted
             .write(|w| w.events_rxstarted().clear_bit());
         info!("rxstarted");
+
+        // Swap to the next buffer as soon as the transfer starts to
+        // try and lose as little data as possible.
+        let ptr = unsafe { DBL_BUFF.next_ptr() };
+        uarte.0.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
     }
 }
+
+fn flush_buf(writer: &mut Writer<u8>, ptr: *mut u8, len: usize) {
+    let buf = unsafe { core::slice::from_raw_parts(ptr, len) };
+    info!("flush start");
+    writer.unshift_from(buf);
+    info!("flush end");
+}
-- 
cgit v1.2.3