From 5c0a8ac8acb781d71ee7fdb6197b653d013ea44f Mon Sep 17 00:00:00 2001
From: Per <Per Lindgren>
Date: Mon, 19 Feb 2018 23:06:49 +0100
Subject: [PATCH] parser

---
 examples/parse.rs | 294 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 294 insertions(+)
 create mode 100644 examples/parse.rs

diff --git a/examples/parse.rs b/examples/parse.rs
new file mode 100644
index 0000000..4bfccab
--- /dev/null
+++ b/examples/parse.rs
@@ -0,0 +1,294 @@
+//! Serial interface loopback
+#![deny(unsafe_code)]
+//#![deny(warnings)]
+#![feature(proc_macro)]
+#![no_std]
+
+extern crate cortex_m_rtfm as rtfm;
+extern crate f4;
+extern crate heapless;
+
+#[macro_use]
+extern crate cortex_m_debug;
+
+use f4::prelude::*;
+use f4::{clock, Serial};
+use f4::time::Hertz;
+use heapless::Vec;
+use rtfm::{app, Resource, Threshold};
+
+// CONFIGURATION
+const BAUD_RATE: Hertz = Hertz(115_200);
+
+// RTFM FRAMEWORK
+app! {
+    device: f4::stm32f40x,
+
+    resources: {
+        static VECTOR: Vec<u8, [u8; 4]> = Vec::new();
+    },
+
+    tasks: {
+        USART2: {
+            path: rx,
+            priority: 2,
+            resources: [VECTOR, USART2],
+        },
+        EXTI1: {
+            path: trace,
+            priority: 1,
+            resources: [VECTOR],
+        }
+    },
+}
+
+// `rx` task trigger on arrival of a USART2 interrupt
+fn rx(t: &mut Threshold, r: USART2::Resources) {
+    let serial = Serial(&**r.USART2);
+
+    // we don't need to block waiting for data to arrive
+    // (as we were triggered) by the data arrival (or error)
+    match serial.read() {
+        Ok(byte) => {
+            // received byte correct
+            r.VECTOR.claim_mut(t, |vector, _| {
+                // critical section for the shared vector
+                let _ = vector.push(byte);
+                // here you could put your error handling for vector full
+            });
+            let _ = serial.write(byte);
+        }
+        Err(err) => {
+            // some transmission error
+            ipln!("Error {:?}", err);
+            r.USART2.dr.read(); // clear the error by reading the data register
+        }
+    }
+
+    // trigger the `trace` task
+    rtfm::set_pending(f4::stm32f40x::Interrupt::EXTI1);
+}
+
+// `trace` task triggered by the hight priority `rx` task
+// a low priority task for the background processing (like tracing)
+fn trace(t: &mut Threshold, r: EXTI1::Resources) {
+    let mut b = [0; 4]; // local buffer
+    let mut l = 0; // length of the received vector
+
+    r.VECTOR.claim(t, |vector, _| {
+        // critical section for the shared vector
+        // here the task `rx` will be blocked from executing
+        l = vector.len();
+        b[..l].copy_from_slice(&***vector); // efficent copy vector to the local buffer
+    });
+    // since we do the actual tracing (relatively slow)
+    // OUTSIDE the claim (critical section), there will be no
+    // additional blocking of `rx`
+    ipln!("Vec {:?}", &b[..l]);
+}
+
+macro_rules! scan {
+    ( $string:expr, $sep:expr, $( $x:ty ),+ ) => {{
+        let mut iter = $string.split($sep);
+        ($(iter.next().and_then(|word| word.parse::<$x>().ok()),)*)
+    }}
+}
+
+// [macro_use]
+// use core::fmt;
+
+#[derive(Debug)]
+enum Command {
+    Start,
+    Stop,
+    Freq(u32),
+}
+
+fn parse(s: &str) -> Result<Command, &str> {
+    let mut iter = s.split(' ').filter(|c| !(c == &""));
+
+    match iter.next() {
+        Some("Stop") => Ok(Command::Stop),
+        Some("Start") => Ok(Command::Start),
+        Some("Freq") => {
+
+            match iter.next() {
+                Some(fs) => {
+
+                    if let Ok(f) = fs.parse::<u32>() {
+                        Ok(Command::Freq(f))
+                    } else {
+                        Err("Invalid frequency")
+                    }
+
+                }
+                None => Err("No frequency")
+            }
+
+        }
+        Some(_) => {
+            Err("Invalid command")
+        },
+        None => Err("No input")
+        //Err(format!("Invald Command : {:?}", s)),
+    }
+}
+
+// Here we see the typical use of init INITIALIZING the system
+fn init(p: init::Peripherals, _r: init::Resources) {
+    //clock::set_84_mhz(p.RCC, p.FLASH);
+    ipln!("init");
+    ipln!("{:?}", parse("Start"));
+    ipln!("{:?}", parse("  Start  ")); // works with white spaces
+    ipln!("{:?}", parse("  Freq  122  ")); // works with white spaces
+    ipln!("{:?}", parse("  Freq  a122  ")); //
+    ipln!("{:?}", parse("  Freq    ")); //
+    ipln!("{:?}", parse("")); // error
+
+    let serial = Serial(p.USART2);
+
+    serial.init(BAUD_RATE.invert(), None, p.GPIOA, p.RCC);
+    // in effect telling the USART2 to trigger the `rx` task/interrupt
+    serial.listen(f4::serial::Event::Rxne);
+
+    // let s = scan!(" 55 123", |c| c == ' ', u32, u32);
+    // ipln!("{:?}", s);
+
+    // let b = char::is_whitespace;
+    // //let scanned = scan!("  55 123  ", core::char::is_whitespace, u32, u32);
+    // //ipln!("{:?}", scanned);
+
+    // let mut v: Vec<&str, [&str; 4]> = Vec::new();
+
+    // // collect into a vector of &str
+    // for i in s.split(" ") {
+    //     v.push(&i);
+    // }
+
+    // if let Some(command) = v.pop() {
+    //     ipln!("here {:?}", command);
+    //     match command {
+    //         "freq" => {
+    //             if let Some(freq) = v.pop() {
+    //                 if let Ok(f) = freq.parse::<i32>() {
+    //                     ipln!("freq {:?}", f);
+    //                 }
+    //             }
+    //         }
+    //         _ => {}
+    //     }
+    // }
+}
+
+// We will spend all time sleeping (unless we have work to do)
+// reactive programming in RTFM ftw!!!
+fn idle() -> ! {
+    // Sleep
+    loop {
+        rtfm::wfi();
+    }
+}
+
+// 1. compile and run the project at 16MHz
+// make sure its running (not paused)
+// start a terminal program, e.g., `moserial`
+// connect to the port
+//
+// Device       /dev/ttyACM0
+// Baude Rate   115200
+// Data Bits    8
+// Stop Bits    1
+// Parity       None
+// Handshake    None
+//
+// (this is also known in short as 15200 8N1)
+//
+// you should now be able to send data and recive an echo from the MCU
+//
+// try sending: "abcd" as a single sequence (set the option No end in moserial)
+// (don't send the quation marks, just abcd)
+//
+// what did you receive, and what was the output of the ITM trace
+// ** your answer here **
+//
+// did you experience any over-run errors?
+// ** your answer here **
+//
+// what is the key problem and its solution (try to follow the commented code)
+// ** your answer here **
+//
+// commit your answers (bare8_1)
+//
+// 2. now catch the case when we are trying to write to a full vector/buffer
+// and write a suiteble error message
+//
+// commit your answers (bare8_2)
+//
+// as a side note....
+//
+// The concurrency model behind RTFM offers
+// 1. Race-free resource access
+//
+// 2. Deadlock-free exection
+//
+// 3. Shared execution stack (no pre-allocated stack regions)
+//
+// 4. Bound priority inversion
+//
+// 5. Theoretical underpinning ->
+//    + proofs of soundness
+//    + schedulability analysis
+//    + response time analysis
+//    + stack memory analysis
+//    + ... leverages on 25 years of reseach in the real-time community
+//      based on the seminal work of Baker in the early 1990s
+//      (known as the Stack Resource Policy, SRP)
+//
+// Our implementation in Rust offers
+// 1. compile check and analysis of tasks and resources
+//    + the API implementation together with the Rust compiler will ensure that
+//      both RTFM (SRP) soundness and the Rust memory model invariants
+//      are upheld (under all circumpstances).
+//
+// 2. arguably the worlds fastest real time scheduler *
+//    + task invocation 0-cycle OH on top of HW interrupt handling
+//    + 2 cycle OH for locking a shared resource (on claim entry)
+//    + 1 cycle OH for releasineg a shared resoure (on claim exit)
+//
+// 3. arguably the worlds most memory efficient scheduler *
+//    + 1 byte stack memory OH for each (nested) claim
+//      (no additional book-keeping during run-time)
+//
+//    * applies to static task/resource models with single core
+//      pre-emptive, static priority scheduling
+//
+// in comparison "real-time" schedulers for threaded models like FreeRTOS
+//    - CPU and memory OH magnitudes larger (100s of cycles/kilobytes of memory)
+//    - ... and what's worse OH is typically unbound (no proofs of worst case)
+//    - potential race conditions (up to the user to verify)
+//    - potential dead-locks (up to the implementation)
+//    - potential unbound priority inversion (up to the implementation)
+//
+// Rust RTFM (currently) target ONLY STATIC SYSTEMS, there is no notion
+// of dynamically creating new executions contexts/threads
+// so a direct comparison is not completely fair.
+//
+// On the other hand, embedded applications are typically static by nature
+// so a STATIC model is to that end better suitable.
+//
+// RTFM is reactive by nature, a task execute to end, triggered
+// by an internal or external event, (where an interrupt is an external event
+// from the environment, like a HW peripheral such as the USART2).
+//
+// Threads on the other hand are concurrent and infinte by nature and
+// actively blocking/yeilding awaiting stimuli. Hence reactivity needs to be CODED.
+// This leads to an anomaly, the underlying HW is reactive (interrupts),
+// requiring an interrupt handler, that creates a signal to the scheduler.
+//
+// The scheduler then needs to keep track of all threads and at some point choose
+// to dispatch the awaiting thread. So reactivity is bottlenecked to the point
+// of scheduling by que management, context switching and other additional
+// book keeping.
+//
+// In essence, the thread scheduler tries to re-establish the reactivity that
+// were there (interrupts), a battle that cannot be won...
-- 
GitLab