From 032a54c77f124922e4f73b5de2c954c8c0931079 Mon Sep 17 00:00:00 2001 From: Per <Per Lindgren> Date: Fri, 18 Jan 2019 15:47:36 +0100 Subject: [PATCH] marcus --- .vscode/launch.json | 34 +++- .vscode/tasks.json | 24 +++ Cargo.toml | 3 +- examples/bare4.rs | 1 - examples/bare5.rs | 1 - examples/bare7.rs | 1 - examples/equivalence.rs | 231 ++++++++++++++++++++++++ examples/marcus.rs | 388 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 678 insertions(+), 5 deletions(-) create mode 100644 examples/equivalence.rs create mode 100644 examples/marcus.rs diff --git a/.vscode/launch.json b/.vscode/launch.json index 8a61829..6ff145f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -641,7 +641,7 @@ "type": "cortex-debug", "request": "launch", "servertype": "openocd", - "name": "bare10 (reselase)", + "name": "bare10 (release)", "preLaunchTask": "cargo build --example bare10 --release", "executable": "./target/thumbv7em-none-eabihf/release/examples/bare10", "configFiles": [ @@ -701,5 +701,37 @@ "svdFile": "STM32F413.svd", "cwd": "${workspaceRoot}" }, + { + "type": "cortex-debug", + "request": "launch", + "servertype": "openocd", + "name": "marcus (release)", + "preLaunchTask": "cargo build --example marcus --release", + "executable": "./target/thumbv7em-none-eabihf/release/examples/marcus", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32f4x.cfg" + ], + "swoConfig": { + "enabled": true, + "cpuFrequency": 16000000, + "swoFrequency": 2000000, + "source": "probe", + "decoders": [ + { + "type": "console", + "label": "ITM0", + "port": 0 + }, + { + "type": "console", + "label": "ITM1", + "port": 1 + } + ] + }, + "svdFile": "STM32F413.svd", + "cwd": "${workspaceRoot}" + }, ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index bf7899a..69ff234 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -303,5 +303,29 @@ "isDefault": true } }, + { + "type": "shell", + "label": "cargo build --example marcus --release", + "command": "cargo build --example marcus --release --features \"hal rtfm-tq\"", + "problemMatcher": [ + "$rustc" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "shell", + "label": "cargo build --example equivalence --release", + "command": "cargo build --example equivalence --release --features \"hal rtfm-tq\"", + "problemMatcher": [ + "$rustc" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, ] } \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index a6f3ab5..030ea8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,9 +44,10 @@ features = ["stm32f413", "rt"] optional = true [features] +pac = ["stm32f4"] hal = ["stm32f4", "stm32f4xx-hal"] rtfm = ["cortex-m-rtfm"] -pac = ["stm32f4"] +rtfm-tq = ["cortex-m-rtfm/timer-queue"] # this lets you use `cargo fix`! [[bin]] diff --git a/examples/bare4.rs b/examples/bare4.rs index b5f1d5a..5b7184b 100644 --- a/examples/bare4.rs +++ b/examples/bare4.rs @@ -8,7 +8,6 @@ //! - busses and clocking //! - gpio -#![feature(uniform_paths)] // requires nightly #![no_std] #![no_main] diff --git a/examples/bare5.rs b/examples/bare5.rs index 3600a68..514f5a1 100644 --- a/examples/bare5.rs +++ b/examples/bare5.rs @@ -6,7 +6,6 @@ //! - abstractions in Rust //! - structs and implementations -#![feature(uniform_paths)] // requires nightly #![no_std] #![no_main] diff --git a/examples/bare7.rs b/examples/bare7.rs index f438100..3de0f05 100644 --- a/examples/bare7.rs +++ b/examples/bare7.rs @@ -5,7 +5,6 @@ //! - working with the svd2rust API #![deny(unsafe_code)] -#![feature(uniform_paths)] #![deny(warnings)] #![no_main] #![no_std] diff --git a/examples/equivalence.rs b/examples/equivalence.rs new file mode 100644 index 0000000..f629b00 --- /dev/null +++ b/examples/equivalence.rs @@ -0,0 +1,231 @@ +//! The RTFM framework +//! +//! What it covers: +//! - Priority based scheduling +//! - Message passing + +#![no_main] +#![no_std] + +extern crate panic_halt; + +use cortex_m::{asm, iprintln}; + +extern crate stm32f4xx_hal as hal; +// use stm32f4::stm32f413::GPIOA; +use crate::hal::prelude::*; +use crate::hal::serial::{self, config::Config, Rx, Serial, Tx}; +use hal::{gpio::Alternate, gpio::AF0, stm32::ITM}; + +// use heapless::consts::*; +// use heapless::spsc::{Consumer, Producer, Queue}; + +use nb::block; + +use rtfm::{app, Instant}; + +// Our error type +#[derive(Debug)] +pub enum Error { + RingBufferOverflow, + UsartSendOverflow, + UsartReceiveOverflow, +} + +#[derive(Debug)] +pub enum Event { + Timout, + ChannelA, + ChannelB, +} + +#[derive(Debug, Copy, Clone)] +pub struct Data { + a: bool, + b: bool, + event_counter: u32, + out: bool, +} + +const TIMEOUT: u32 = 16_000_000; + +#[app(device = hal::stm32)] +const APP: () = { + // Late resources + static mut TX: Tx<hal::stm32::USART2> = (); + static mut RX: Rx<hal::stm32::USART2> = (); + static mut ITM: ITM = (); + static mut LED: hal::gpio::gpioa::PA5<Alternate<AF0>> = (); + + // app resources + static mut DATA: Data = Data { + a: false, + b: false, + event_counter: 0, + out: false, + }; + + // init runs in an interrupt free section> + #[init] + fn init() { + let stim = &mut core.ITM.stim[0]; + iprintln!(stim, "start"); + + // power on GPIOA, RM0368 6.3.11 + device.RCC.ahb1enr.modify(|_, w| w.gpioaen().set_bit()); + // configure PA5 as output, RM0368 8.4.1 + device.GPIOA.moder.modify(|_, w| w.moder5().bits(1)); + + let rcc = device.RCC.constrain(); + + // 16 MHz (default, all clocks) + let clocks = rcc.cfgr.freeze(); + + let gpioa = device.GPIOA.split(); + + let tx = gpioa.pa2.into_alternate_af7(); + let rx = gpioa.pa3.into_alternate_af7(); + let led = gpioa.pa5.into_alternate_af0(); + + let mut serial = Serial::usart2( + device.USART2, + (tx, rx), + Config::default().baudrate(115_200.bps()), + clocks, + ) + .unwrap(); + + // generate interrupt on Rxne + serial.listen(serial::Event::Rxne); + // Separate out the sender and receiver of the serial port + let (tx, rx) = serial.split(); + + // pass on late resources + LED = led; + + // Our split serial + TX = tx; + RX = rx; + + // For debugging + ITM = core.ITM; + } + + // idle may be interrupted by other interrupt/tasks in the system + // #[idle(resources = [RX, TX, ITM])] + #[idle] + fn idle() -> ! { + loop { + asm::wfi(); + } + } + + #[task(priority = 1, resources = [ITM])] + fn trace_data(byte: u8) { + let stim = &mut resources.ITM.stim[0]; + iprintln!(stim, "data {}", byte); + } + + #[task(priority = 1, resources = [ITM])] + fn trace_error(error: Error) { + let stim = &mut resources.ITM.stim[0]; + iprintln!(stim, "{:?}", error); + } + + #[interrupt(priority = 3, resources = [RX], spawn = [trace_data, trace_error, echo])] + fn USART2() { + match resources.RX.read() { + Ok(byte) => { + spawn.echo(byte).unwrap(); + if spawn.trace_data(byte).is_err() { + spawn.trace_error(Error::RingBufferOverflow).unwrap(); + } + } + Err(_err) => { + spawn.trace_error(Error::UsartReceiveOverflow).unwrap() + } + } + } + + #[task(priority = 2, resources = [TX], spawn = [trace_error, event_a, event_b])] + fn echo(byte: u8) { + let tx = resources.TX; + + if block!(tx.write(byte)).is_err() { + spawn.trace_error(Error::UsartSendOverflow).unwrap(); + } + + match byte { + b'a' => spawn.event_a(false).unwrap(), + b'b' => spawn.event_b(false).unwrap(), + b'A' => spawn.event_a(true).unwrap(), + b'B' => spawn.event_b(true).unwrap(), + _ => (), + } + } + + #[task(priority = 1, resources = [ITM, DATA], schedule = [discrepency])] + fn event_a(val: bool) { + let stim = &mut resources.ITM.stim[0]; + iprintln!(stim, "event a {}", val); + + let data = &mut resources.DATA; + data.a = val; + // evaluate equivalenc. + data.event_counter += 1; + if data.a ^ data.b { + schedule + .discrepency(scheduled + TIMEOUT.cycles(), data.clone()) + .unwrap(); + data.out = false; + iprintln!(stim, "disc {:?}", data); + } + } + + #[task(priority = 1, resources = [ITM, DATA], spawn = [discrepency])] + fn event_b(val: bool) { + let stim = &mut resources.ITM.stim[0]; + iprintln!(stim, "event b {}", val); + //evaluate_equivalence(scheduled, &mut resources.DATA); + } + + #[task(priority = 1, resources = [ITM, DATA])] + fn discrepency(data: Data) { + let stim = &mut resources.ITM.stim[0]; + iprintln!(stim, "a {} b {}", data.a, data.b); + if data.event_counter == resources.DATA.event_counter { + iprintln!(stim, "timeout"); + // data.force_reinit = + } + } + + #[task (priority = 1, resources = [LED])] + fn periodic() { + static mut TOGGLE: bool = false; + + if *TOGGLE { + resources.LED.set_low(); + } else { + resources.LED.set_high(); + } + + *TOGGLE = !*TOGGLE; + } + + extern "C" { + fn EXTI0(); + fn EXTI1(); + fn EXTI2(); + fn EXTI3(); + fn EXTI4(); + } +}; + +// fn evaluate_equivalence(scheduled: Instant, data: &mut Data, d: event_a::Spawn) { +// data.EventCounter += 1; +// if data.A ^ data.B { + +// // spawn.discrepency(scheduled + TIMEOUT.cycles(), data.EventCounter, data.A, data.B); + +// } +// } diff --git a/examples/marcus.rs b/examples/marcus.rs new file mode 100644 index 0000000..0114062 --- /dev/null +++ b/examples/marcus.rs @@ -0,0 +1,388 @@ +//! The RTFM framework +//! +//! What it covers: +//! - Priority based scheduling +//! - Message passing + +#![no_main] +#![no_std] + +extern crate panic_halt; + +use cortex_m::{asm, iprintln}; + +extern crate stm32f4xx_hal as hal; +use crate::hal::prelude::*; +use crate::hal::serial::{self, config::Config, Rx, Serial, Tx}; +use hal::stm32::ITM; + +// use heapless::consts::*; +// use heapless::spsc::{Consumer, Producer, Queue}; + +use nb::block; + +use rtfm::{app, Instant}; + +// Our error type +#[derive(Debug)] +pub enum Error { + RingBufferOverflow, + UsartSendOverflow, + UsartReceiveOverflow, +} + +#[derive(Debug)] +pub enum Event { + Timout, + ChannelA, + ChannelB, +} + +#[derive(Debug, Copy, Clone)] +pub struct Data { + state: State, + a: bool, + b: bool, + event_counter: u32, + out: bool, +} + +#[derive(Debug, Copy, Clone)] +pub enum State { + S8000, + S8001, + S8004, + S8014, + S8005, + C001, + C002, + C003, +} + +const TIMEOUT: u32 = 16_000_000; + +#[app(device = hal::stm32)] +const APP: () = { + // Late resources + static mut TX: Tx<hal::stm32::USART2> = (); + static mut RX: Rx<hal::stm32::USART2> = (); + static mut ITM: ITM = (); + + // app resources + static mut DATA: Data = Data { + state: State::S8001, + a: false, + b: false, + event_counter: 0, + out: false, + }; + + // init runs in an interrupt free section> + #[init(resources = [DATA])] + fn init() { + let stim = &mut core.ITM.stim[0]; + iprintln!(stim, "Start {:?}", resources.DATA); + + let rcc = device.RCC.constrain(); + + // 16 MHz (default, all clocks) + let clocks = rcc.cfgr.freeze(); + + let gpioa = device.GPIOA.split(); + + let tx = gpioa.pa2.into_alternate_af7(); + let rx = gpioa.pa3.into_alternate_af7(); // try comment out + + let mut serial = Serial::usart2( + device.USART2, + (tx, rx), + Config::default().baudrate(115_200.bps()), + clocks, + ) + .unwrap(); + + // generate interrupt on Rxne + serial.listen(serial::Event::Rxne); + // Separate out the sender and receiver of the serial port + let (tx, rx) = serial.split(); + + // Our split serial + TX = tx; + RX = rx; + + // For debugging + ITM = core.ITM; + } + + // idle may be interrupted by other interrupt/tasks in the system + // #[idle(resources = [RX, TX, ITM])] + #[idle] + fn idle() -> ! { + loop { + asm::wfi(); + } + } + + #[task(priority = 1, resources = [ITM])] + fn trace_data(byte: u8) { + let stim = &mut resources.ITM.stim[0]; + iprintln!(stim, "data {}", byte); + } + + #[task(priority = 1, resources = [ITM])] + fn trace_error(error: Error) { + let stim = &mut resources.ITM.stim[0]; + iprintln!(stim, "{:?}", error); + } + + #[interrupt(priority = 3, resources = [RX], spawn = [trace_data, trace_error, echo])] + fn USART2() { + match resources.RX.read() { + Ok(byte) => { + spawn.echo(byte).unwrap(); + if spawn.trace_data(byte).is_err() { + spawn.trace_error(Error::RingBufferOverflow).unwrap(); + } + } + Err(_err) => { + spawn.trace_error(Error::UsartReceiveOverflow).unwrap() + } + } + } + + #[task(priority = 2, resources = [TX], spawn = [trace_error, event_a, event_b])] + fn echo(byte: u8) { + let tx = resources.TX; + + if block!(tx.write(byte)).is_err() { + spawn.trace_error(Error::UsartSendOverflow).unwrap(); + } + + match byte { + b'a' => spawn.event_a(false).unwrap(), + b'b' => spawn.event_b(false).unwrap(), + b'A' => spawn.event_a(true).unwrap(), + b'B' => spawn.event_b(true).unwrap(), + _ => (), + } + } + + #[task(priority = 1, resources = [ITM, DATA], schedule = [discrepency])] + fn event_a(val: bool) { + let stim = &mut resources.ITM.stim[0]; + iprintln!(stim, "event a {}", val); + + + let data = &mut resources.DATA; + data.a = val; + iprintln!(stim, "Start {:?}", data); + // let data = resources.DATA; + + match data.state { + State::S8000 => { + if data.a ^ data.b { + schedule + .discrepency(scheduled + TIMEOUT.cycles(), data.event_counter, data.state) + .unwrap(); + data.out = false; + data.state = State::S8005; + } else if !data.a & !data.b { + data.state = State::S8001 + } else { + return; + } + } + State::S8001 => { + if data.a & !data.b { + schedule + .discrepency(scheduled + TIMEOUT.cycles(), data.event_counter, data.state) + .unwrap(); + data.out = false; + data.state = State::S8004; + } else if !data.a & data.b { + schedule + .discrepency(scheduled + TIMEOUT.cycles(), data.event_counter, data.state) + .unwrap(); + data.out = false; + data.state = State::S8014; + } else if data.a & data.b { + data.out = true; + data.state = State::S8000; + } + } + State::S8004 => { + if !data.a { + data.event_counter += 1; + data.out = false; + data.state = State::S8001; + } else if data.b { + data.event_counter += 1; + data.out = true; + data.state = State::S8000; + } else { + data.out = false; + } + } + State::S8014 => { + if !data.b { + data.event_counter += 1; + data.out = false; + data.state = State::S8001; + } else if data.a { + data.event_counter += 1; + data.out = true; + data.state = State::S8000; + } else { + data.out = false; + } + } + State::S8005 => { + if !data.a & !data.b { + data.event_counter += 1; + data.out = false; + data.state = State::S8001; + } else { + data.out = false; + } + } + + _ => { + if !data.a & !data.b { + data.out = false; + data.state = State::S8001; + } else { + data.out = false; + } + } + } + iprintln!(stim, "Start {:?}", resources.DATA); + } + + #[task(priority = 1, resources = [ITM, DATA], schedule = [discrepency])] + fn event_b(val: bool) { + let stim = &mut resources.ITM.stim[0]; + iprintln!(stim, "event b {}", val); + + let data = &mut resources.DATA; + data.b = val; + iprintln!(stim, "Start {:?}", data); + + match data.state { + State::S8000 => { + if data.a ^ data.b { + schedule + .discrepency(scheduled + TIMEOUT.cycles(), data.event_counter, data.state) + .unwrap(); + data.out = false; + data.state = State::S8005; + } else if !data.a & !data.b { + data.state = State::S8001 + } else { + return; + } + } + State::S8001 => { + if data.a & !data.b { + schedule + .discrepency(scheduled + TIMEOUT.cycles(), data.event_counter, data.state) + .unwrap(); + data.out = false; + data.state = State::S8004; + } else if !data.a & data.b { + schedule + .discrepency(scheduled + TIMEOUT.cycles(), data.event_counter, data.state) + .unwrap(); + data.out = false; + data.state = State::S8014; + } else if data.a & data.b { + data.out = true; + data.state = State::S8000; + } + } + State::S8004 => { + if !data.a { + data.event_counter += 1; + data.out = false; + data.state = State::S8001; + } else if data.b { + data.event_counter += 1; + data.out = true; + data.state = State::S8000; + } else { + data.out = false; + } + } + State::S8014 => { + if !data.b { + data.event_counter += 1; + data.out = false; + data.state = State::S8001; + } else if data.a { + data.event_counter += 1; + data.out = true; + data.state = State::S8000; + } else { + data.out = false; + } + } + State::S8005 => { + if !data.a & !data.b { + data.event_counter += 1; + data.out = false; + data.state = State::S8001; + } else { + data.out = false; + } + } + + _ => { + if !data.a & !data.b { + data.out = false; + data.state = State::S8001; + } else { + data.out = false; + } + } + } + } + + #[task(priority = 1, resources = [ITM])] + fn discrepency(counter: u32, state:State) { + let stim = &mut resources.ITM.stim[0]; + iprintln!(stim, "counter {} state {:?}", counter, state); + // if data.event_counter == resources.DATA.event_counter { + // iprintln!(stim, "timeout"); + // // data.force_reinit = + // } + } + + extern "C" { + fn EXTI0(); + fn EXTI1(); + fn EXTI2(); + fn EXTI3(); + fn EXTI4(); + } +}; + +// fn evaluate_equivalence(scheduled: Instant, data: &mut Data, d: event_a::Spawn) { +// data.EventCounter += 1; +// if data.A ^ data.B { + +// // spawn.discrepency(scheduled + TIMEOUT.cycles(), data.EventCounter, data.A, data.B); + +// } +// } +// +// +// +// +// invariants +// out = true -> A & B +// +// event A false -> A = false +// event A true -> A = true +// event B false -> B = false +// event B true -> B = true +// +// A ^ B -- TIMEOUT --> !(A & B) -> out = false -- GitLab