A concurrency framework for building real-time systems

Overview

Real-Time Interrupt-driven Concurrency

A concurrency framework for building real-time systems.

Formerly known as Real-Time For the Masses.

crates.io docs.rs book matrix Meeting notes

Features

  • Tasks as the unit of concurrency 1. Tasks can be event triggered (fired in response to asynchronous stimuli) or spawned by the application on demand.

  • Message passing between tasks. Specifically, messages can be passed to software tasks at spawn time.

  • A timer queue 2. Software tasks can be scheduled to run at some time in the future. This feature can be used to implement periodic tasks.

  • Support for prioritization of tasks and, thus, preemptive multitasking.

  • Efficient and data race free memory sharing through fine grained priority based critical sections 1.

  • Deadlock free execution guaranteed at compile time. This is a stronger guarantee than what's provided by the standard Mutex abstraction.

  • Minimal scheduling overhead. The task scheduler has minimal software footprint; the hardware does the bulk of the scheduling.

  • Highly efficient memory usage: All the tasks share a single call stack and there's no hard dependency on a dynamic memory allocator.

  • All Cortex-M devices are fully supported.

  • This task model is amenable to known WCET (Worst Case Execution Time) analysis and scheduling analysis techniques.

Crate cortex-m 0.6 vs 0.7 in RTIC 0.5.x

The crate cortex-m 0.7 started using trait InterruptNumber for interrupts instead of Nr from bare-metal. In order to preserve backwards compatibility, RTIC 0.5.x will keep using cortex-m 0.6 by default. cortex-m 0.7 can be enabled using the feature cortex-m-7 and disabling default features:

cortex-m-rtic = { version = "0.5.8", default-features = false, features = ["cortex-m-7"] }

RTIC 1.0.0 already uses cortex-m 0.7 by default.

User documentation

Documentation for the development version.

API reference

Community provided examples repo

Chat

Join us and talk about RTIC in the Matrix room.

Weekly meeting notes can be found over at HackMD

Contributing

New features and big changes should go through the RFC process in the dedicated RFC repository.

Running tests locally

To check all Run-pass tests locally on your thumbv6m-none-eabi or thumbv7m-none-eabi target device, run

$ cargo xtask --target <your target>
#                       ˆˆˆˆˆˆˆˆˆˆˆˆ
#                   e.g. thumbv7m-none-eabi

Acknowledgments

This crate is based on the Real-Time For the Masses language created by the Embedded Systems group at Luleå University of Technology, led by Prof. Per Lindgren.

References

License

All source code (including code snippets) is licensed under either of

at your option.

The written prose contained within the book is licensed under the terms of the Creative Commons CC-BY-SA v4.0 license (LICENSE-CC-BY-SA or https://creativecommons.org/licenses/by-sa/4.0/legalcode).

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.

Footnotes

  1. Eriksson, J., Häggström, F., Aittamaa, S., Kruglyak, A., & Lindgren, P. (2013, June). Real-time for the masses, step 1: Programming API and static priority SRP kernel primitives. In Industrial Embedded Systems (SIES), 2013 8th IEEE International Symposium on (pp. 110-113). IEEE. 2

  2. Lindgren, P., Fresk, E., Lindner, M., Lindner, A., Pereira, D., & Pinho, L. M. (2016). Abstract timers and their implementation onto the arm cortex-m family of mcus. ACM SIGBED Review, 13(1), 48-53.

Comments
  • rtfm based stm32f030 debug build causes relcation truncated error

    rtfm based stm32f030 debug build causes relcation truncated error

    here is an example project: https://gitlab.com/xnor/stm32f0308-disco-rust

    If I build it without --release I get

    target/thumbv6m-none-eabi/debug   /deps/libstm32f030-5466fdead1a18a6d.rlib(stm32f030-5466fdead1a18a6d.0.o): In function `WWDG':
          stm32f030.cgu-0.rs:(.text+0x0): relocation truncated to fit: R_ARM_THM_JUMP11 against symbol `DEFAULT_HANDLER' defined in .text.DEFAULT_HANDLER section in /home/alex/projects/modular/threshpan/target/thumbv6m-none-eabi/debug/deps/libcortex_m_rt-881d17200def560b.rlib(cortex_m_rt-881d17200def560b.0.o)
    
    opened by x37v 41
  • app! macro take 2

    app! macro take 2

    Update

    More ports from RTFM v1 to this macro:


    Full example

    ^ This is semantically the same example presented in #31, but with this new macro 33% less code is required.

    Next iteration of the rtfm! macro presented in #31. The most notable change wrt the previous version is that the macro has become a ~~compiler plugin~~ procedural macro.

    Pros

    • Automatic ceiling derivation. The ceiling will be computed from the information about which tasks use which resources.

    • Simpler task and resource specification. There's no split between interrupts used as tasks and exceptions used as tasks; there's only tasks now. Likewise there's no distinction between peripherals used as resources and data resources; there's only resources now.

    • More flexible syntax. The fields can be shuffled around without breaking the parser.

    • Simpler signatures as there's no Peripherals / Resources split.

    • Adaptive signatures. The signature of idle will be fn() -> ! if it makes no uses of resources and has no local data, but it will change to fn(idle::Local) if local data is used, etc.

    • Less claims. Full access to resources is given to tasks whose priority matches the resource ceiling.

    • More control over error messages related to the parsing and expansion of rtfm!.

    Cons

    rtfm! is now a compiler plugin. This makes the chance of it breaking across nightly updates greater than zero. However, I've implemented the compiler plugin on top of token streams so we should be safe from compiler changes related to the language syntax and parser. Once function style macros becomes available via the more stable proc-macros feature it should be straightforward to move to it. And yes, I tried proc-macro-hack which allows function style proc macros on top of custom derive proc macros but it doesn't work for no-std / cross compilation because the hack requires a proc-macro crate compiled for the target (which is not possible for thumb because proc-macro contains Strings).

    Other changes

    • I realized that the change to scope based safety fixes #13 so I've added a borrow / borrow_mut method to resources to get direct access to them within a global critical section.

    • Given that we pretty much generate the implementation of the claim methods in the application crate I have changed things such that (NVIC) PRIORITY_BITS is no longer in the cortex-m-rtfm, but rather it's expected to be provided by the device crate. This way we don't need a bunch of P{2,3,..} Cargo features in the cortex-m-rtfm crate. OTOH, device generated crates now must provide a NVIC_PRIO_BITS constant that provides this information. SVD files contain this information in the form of the optional <nvicPrioBits> field. svd2rust will be taught to generate the NVIC_PRIO_BITS constant.

    • The declaration of task local data and the task function path has been moved into a task! macro. This macro doesn't have to be in the root of the crate.

    cc @whitequark

    opened by japaric 27
  • Build error 'identifier appears more than once in list' after update to rust 1.50

    Build error 'identifier appears more than once in list' after update to rust 1.50

    Hi.

    I get this error when I use my macro to generate the rtic::app structure.

    If I expand the code of my macro only with cargo expand and compile it, it works on rust v 1.50 too.

    Rolling back to rust v 1.46 has solved this problem for me.

    But I want use latest rust.

    opened by andrey-shigantsov 25
  • My project upgraded to 1.1.0, but compilation fails

    My project upgraded to 1.1.0, but compilation fails

    My project upgraded to 1.1.0, but compilation fails most likely due to cortex-m-rtic-macros not being updated to 1.1.0 in main Cargo.toml. The root cause seems to be this commit missing (MASKS missing in macros/src/codegen/util.rs).

    opened by mryndzionek 22
  • First attempt into rtfm, init function doubt

    First attempt into rtfm, init function doubt

    I have the following code:

    // #![deny(unsafe_code)]
    // #![deny(warnings)]
    #![no_main]
    #![no_std]
    
    use stm32f1::stm32f103;
    use rtfm::app;
    
    #[app(device = stm32f1)]
    const APP: () = {
        static mut PERIPHERALS: stm32f103::Peripherals = stm32f103::Peripherals::take().unwrap();
        
        #[init(spawn = [task1, task2], resources = [PERIPHERALS])]
        fn init(c: config::Context) {
            let rcc = c.resources.PERIPHERALS.RCC;
            let gpioc = c.resources.PERIPHERALS.GPIOC;
    
            rcc.apb2enr.write(|w| w.iopcen().set_bit());
            gpioc.crh.write(|w| unsafe{
                w.mode13().bits(0b11);
                w.cnf13().bits(0b00)
            });
    
            c.spawn.task1().unwrap();
            c.spawn.task2().unwrap();
        }
    
        #[idle]
        fn idle() -> ! {
            loop {}
        }
    
        #[task(resources = [PERIPHERALS])]
        fn task1(c: task1::Context) {
            let gpioc = c.resources.PERIPHERALS.GPIOC;
            loop {
                gpioc.bsrr.write(|w| w.bs13().set_bit());
                cortex_m::asm::delay(2000000);
            }
        }
    
        #[task(resources = [PERIPHERALS])]
        fn task2(c: task1::Context) {
            let gpioc = c.resources.PERIPHERALS.GPIOC;
            loop {
                gpioc.brr.write(|w| w.br13().set_bit());
                cortex_m::asm::delay(2000000);
            }
        }
    
        extern "C" {
            fn TIM2();
        }
    };
    

    What i get from compilation is the following error:

    `init` must have type signature `[unsafe] fn() [-> init::LateResources]`
    

    I don't know how to fix it, I'm reading the examples that seems to have the same syntax of mine. For now I don't care about the correctness of the tasks

    opened by thymbahutymba 20
  • Allow initialization of resources in `init`.

    Allow initialization of resources in `init`.

    Closes #37

    TODO:

    • [X] Open dependent PRs
      • [x] Get them merged
    • [X] Add an example ~~(I cannot currently run any of the examples since I "only" have an stm32f401)~~
    • [x] Add a cfail test ensuring that init cannot access uninitialized resources (and possibly other tests)
      • Figure out why I cannot run these tests
    • [x] Simplify code, it's quite ugly
    opened by jonas-schievink 20
  • [RFC] The `Static` wrapper is dead, long live safe `&'static mut` references!

    [RFC] The `Static` wrapper is dead, long live safe `&'static mut` references!

    History: The Static wrapper

    If you have been using RTFM claims you probably have noticed this "pattern":

    r.FOO.claim_mut(|foo| {
        **foo += 1;
    });
    

    Here you need a double dereference because claim returns a &mut Static<T>, instead of a plain mutable reference (&mut T). Static<T> is a newtype over T that Derefs to T. You normally won't notice the Static wrapper when using methods because of the Deref implementation, but the wrapper becomes apparent when you need to assign some new value to a resource.

    DMA transfers

    So, why is Static being used here? The main reason is that I needed some (zero cost) abstraction to make DMA transfers memory safe. You can't build a safe DMA API on top of plain (non-static) references. See below:

    impl Serial {
        fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> Transfer<'a> { .. }
    }
    
    impl<'a> Transfer<'a> {
        fn wait(self) {
            drop(self)
        }
    }
    
    impl<'a> Drop for Transfer<'a> {
        fn drop(&mut self) {
            // waits until the DMA transfer finishes
        }
    }
    
    let mut on_the_stack = [0; 16];
    {
        let transfer = serial.read_exact(&mut on_the_stack);
    
        // meanwhile, do some other stuff
    
        // on_the_stack[0] = 1;
        //~^ error `on_the_stack`
    
        transfer.wait();
    }
    
    // use `on_the_stack`
    

    At first glance, this looks like an OK DMA API. While the DMA transfer is writing to the buffer you can't access the buffer (on_the_stack is "frozen" by the outstanding borrow). The Transfer value represents the on-going transfer and upon destruction (when dropped) it waits for the transfer to finish. You can use the wait method to make the wait operation more explicit.

    However, this API is not safe because you can safely mem::forget the Transfer value to gain access to the buffer while the transfer is on-going:

    let mut on_the_stack = [0; 16];
    {
        let transfer = serial.read_exact(&mut on_the_stack);
    
        // destructor not run
        mem::forget(transfer);
    }
    
    // the transfer may not be over at this point
    on_the_stack[0] = 1;
    assert_eq!(on_the_stack[0], 1);
    

    This doesn't look too dangerous but it's a violation of Rust aliasing model and will result in UB. In the last line two mutable references to on_the_stack exist: one is being used in the indexing operation, and the other is owned by the DMA (external hardware).

    It gets much worse though because this mem::forget hole can be used to corrupt stack memory:

    fn foo() {
        let mut on_the_stack = [0; 16];
    
        mem::forget(serial.read_exact(&mut on_the_stack));
    }
    
    fn bar() {
        // do stuff while the DMA transfer is on going
    }
    
    foo();
    bar();
    

    Here foo starts a DMA transfer that modifies some stack allocation but then immediately returns, releasing the stack allocation. Next bar starts while the DMA is still on going; the problem is that the DMA transfer will write into the stack potentially overwriting bar's local variables and causing undefined behavior.

    How does Static help?

    We can prevent the memory corruption by having the API only accept references that point into memory that will never be de-allocated. And that's what the Static wrapper represents: &Static<T> is a reference into a statically allocated (i.e. stored in a static variable) value of type T. With this change the API would look like this:

    impl Serial {
        fn read_all<'a>(&'a mut self, buf: &'a mut Static<[u8]>) -> Transfer<'a> { .. }
    }
    

    (Note that this change is not enough to prevent the aliasing problem caused by mem::forget. Discussing a solution for that issue is out of scope for this RFC though. The RefCell-like Buffer abstraction in the blue-pill crate prevents the mem::forget aliasing problem showcased above but it still has other issues like mem::swap aliasing and that you can e.g. still use Serial while the transfer is in progress)

    A value can't be safely wrapped in Static but RTFM does that for you in every claim and that lets you use the memory safer DMA API from above.

    Changing buf's type to &'static mut would also have worked but there's no way to safely create &'static mut references, or rather there wasn't a way before this RFC.

    Motivation

    Being able to safely create &'static mut references.

    Why? &'static mut references have interesting properties that I think will enable the creation of novel APIs:

    &'static mut T has move semantics. See below:

    fn reborrow<'a, T>(x: &'a mut T) { .. }
    
    fn consume<T>(x: &'static mut T) { .. }
    
    fn foo<T>(x: &'static mut T) {
        reborrow(x);
    
        // OK to call again
        reborrow(x);
    
        // actually the compiler is doing this in each `reborrow` call
        reborrow(&mut *x);
    
        // this is different: this takes ownership of `x`
        consume(x);
    
        // now you can't use `x` anymore
        //consume(x);
        //~^ error `x` has been already moved
    
        //reborrow(x);
        //~^ error `x` has been already moved
    }
    

    &'static mut T has 'static lifetime (gasp!) so, unlike its non-static cousin, it can be stored in a static variable and thus we can have a resource that protects a &'static mut T.

    &'static mut T is pointer sized. If you need to send (transfer ownership) of a buffer from one task (execution context) to another then it's cheaper to send &'static mut [u8; 256] than to send [u8; 256] even though they are both semantically a move.

    So &'static mut T is a bit like Box<T>: both have move semantics and are pointer sized but the former doesn't need dynamic memory allocation (it's statically allocated!) and we know that T's destructor will never run ('static lifetime).

    Use case: memory safe DMA transfer

    We can revise the DMA API to make it truly memory safe:

    impl Serial {
        fn read_exact(self, buf: &'static mut [u8]) -> Transfer { .. }
    }
    
    impl Transfer {
        fn wait(self) -> (Serial, &'static mut [u8]) { .. }
    }
    
    let buf: &'static mut [u8] = /* created somehow */;
    
    let transfer = serial.read_exact(&mut on_the_stack);
    
    // can't use Serial while the DMA transfer is in progress
    // let byte = serial.read();
    //~^ error `serial` has been moved
    
    // can't access `buf` while the transfer is in progress
    // buf[0] = 1;
    //~^ error `buf` has been moved
    
    // meanwhile, do other stuff
    
    // block until the transfer finishes
    let (serial, buf) = transfer.wait();
    
    // now you can use `serial` and access the `buf`fer again
    

    Here if you mem::forget the transfer then you can't never access serial or the buffer again, which may seem overkill but fulfills the memory safety requirement.

    Use case: SPSC ring buffer

    This use case prompted the original RFC (cf. #47). Basically a ring buffer queue can be split into one producer end point and one consumer end point. Each end point can locklessly enqueue or dequeue items into / from the same queue -- even if the end points are in different execution contexts (e.g. threads or interrupts).

    The API for this already exists in the heapless crate but the Consumer and Producer end points have a lifetime parameter that matches the lifetime of the ring buffer queue. To put these end points in resources the lifetime would have to be 'static and that requires a &'static mut RingBuffer, which can't be safely created without this RFC.

    Detailed design

    init.resources

    We add a resources field to app.init. The value of this field is a list of resources, previously declared in app.resources, that init will own for the rest of the program. The resources in this list will appear under the init::Resources struct as &'static mut references. Example:

    app! {
        device: stm32f103xx,
    
        resources: {
            static BUFFER: [u8; 16] = [0; 16];
            static SHARED: bool = false;
        },
    
        init: {
            // NEW!
            resources: [BUFFER],
        },
    
        idle: {
            resources: [SHARED],
        },
    
        tasks: {
            EXTI0: {
                path: exti0,
                resources: [SHARED],
            },
        }
    }
    
    fn init(p: init::Peripherals, r: init::Resources) {
        // static lifetime
        let buf: &'static mut [u8; 16] = r.BUFFER;
    
        // non-static lifetime
        let shared: &mut bool = r.SHARED;
    }
    
    // ..
    

    Some constraints apply to init owned resources:

    • These resources must have an initial value; i.e. they can't be "late" resources.

    • Resources assigned to init can't appear in idle.resources or in tasks.$T.resources. Basically, the resources are owned by init so they can't be shared with other tasks, or with idle.

    These constraints will be enforced by the app! macro. An error will be thrown before expansion if any constraint is violated.

    Drop the Static wrapper

    Since this RFC brings proper support for &'static mut references to the table I think the Static wrapper is no longer useful -- memory safe DMA APIs can be built without it and that was its main reason for existing.

    This will be implementing by changing all &[mut] Static<T> to &[mut] T. This means you will no longer need to doubly dereference to assign a new value to a resource.

    Downsides

    This is a breaking change, but we are breaking things due to #50 so it's not much of a problem.

    Alternatives

    A pre_init function

    A pre_init function with signature fn() -> T could be run before init. The value returned by this function would be passed as &'static mut T to init. Unlike the main proposal this value would be created at runtime so const eval limitations would not apply; also the value would be allocated in the stack (in the first frame, which will never be deallocated), not in .bss / .data.

    With code it would look like this:

    app! {
        device: stm32f103xx,
    
        pre_init: pre_init,
    }
    
    struct Root {
        buffer: [u8; 16],
    }
    
    fn pre_init() -> Root {
        Root { buffer: [0; 16] }
    }
    
    fn init(root: &'static mut Root, p: init::Peripherals, r: init::Resources) { .. }
    

    I think it may make sense to also support this because it potentially lets you use a different memory region -- think of the case of microcontrollers with two RAM regions the stack could be on one region and .bss / .data could be on the other. However, if we get better support for multiple memory regions in cortex-m-rt and support for placing resources in custom linker sections in cortex-m-rtfm then there is no longer a need for this, I think, because then you can place an init owned resource in any memory region (in RAM, e.g. .bss1, or in core-coupled RAM, .bss2).

    I'm not too concerned about the const eval limitation that affects the main proposal because, in my limited experience, the T in the &'static mut T references one creates is usually an array ([T; N]) or a thin abstraction over uninitialized arrays (e.g. heapless::RingBuffer).

    Implementation

    See #58

    cc @pftbest @jonas-schievink @hannobraun

    opened by japaric 17
  • compiling for different devices

    compiling for different devices

    (Somewhat related to #387 and #197. Also, I'm new to may parts of this so may be missing something obvious. Simple explanations appreciated.)

    I have been trying to construct examples that I can run with different device crates. The following example blinks the (onboard) LED with a short pulse very second and a longer blink every ten seconds. Two processes are scheduled, one for pulse and ten for the longer blink. These spawn a blink process that turns the led on and schedules another process to turn it off.

    With minor modifications I can compile and run on bluepill and blackpill stm32f411. (See BEWARE below before flashing this.)

    Click to expand code
    //! Blink (onboard) LED with short pulse very second and longer blink every ten seconds.
    //! Two processes are scheduled, `one` for pulse and `ten` for longer blink. These spawn 
    //! a `blink` process that turns the led on and schedules another process to turn it off.
    
    #![deny(unsafe_code)]
    #![no_std]
    #![no_main]
    
    #[cfg(debug_assertions)]
    use panic_semihosting as _;
    
    #[cfg(not(debug_assertions))]
    use panic_halt as _;
    
    use embedded_hal::digital::v2::OutputPin;
    
    use rtic::app;
    use rtic::cyccnt::U32Ext;
    
    pub trait LED {
        // depending on board wiring, on may be set_high or set_low, with off also reversed
        // implementation should deal with this difference
        fn on(&mut self) -> ();
        fn off(&mut self) -> ();
    }
    
    
    #[cfg(feature = "stm32f1xx")]
    const MILLISECOND :  u32 = 8_000;  // Duration of a millisecond in cyccnt (syst ticks)
    
    #[cfg(feature = "stm32f4xx")]
    const MILLISECOND :  u32 = 16_000;  // Duration of a millisecond in cyccnt (syst ticks)
    
    const PULSE:  u32 = 1_000 * MILLISECOND;   // 1 second
    const PERIOD: u32 = 10 * PULSE; // 10 seconds
    
    #[cfg(feature = "stm32f1xx")]
    use stm32f1xx_hal::{
        gpio::{Output, PushPull, State,
            gpioc::PC13,
        },
        pac,
        pac::{Peripherals}, 
        prelude::*,
    };
    
    #[cfg(feature = "stm32f1xx")]
    type LedType  =  PC13<Output<PushPull>>;
    
    #[cfg(feature = "stm32f1xx")]
    fn setup(dp: Peripherals) -> LedType {
    
         let mut rcc = dp.RCC.constrain();
    
         let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
         let mut led = gpioc.pc13.into_push_pull_output_with_state(&mut gpioc.crh, State::Low);
         //let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
    
         impl LED for PC13<Output<PushPull>> {
                 fn on(&mut self) -> () {
                 self.set_low().unwrap()
                 }
                 fn off(&mut self) -> () {
                     self.set_high().unwrap()
                 }
         }
         led.off();
    
         led
    }
    
    
    #[cfg(feature = "stm32f4xx")]
    use stm32f4xx_hal::{
        gpio::{Output, PushPull,
            gpioc::PC13,
        },
        pac,
        pac::{Peripherals,},
        prelude::*,
    };
    
    #[cfg(feature = "stm32f4xx")]
    type LedType  =  PC13<Output<PushPull>>;
    
    #[cfg(feature = "stm32f4xx")]
    fn setup(dp: Peripherals) -> LedType {
    
         let gpioc = dp.GPIOC.split();
         let led = gpioc.pc13.into_push_pull_output();  
    
         impl LED for PC13<Output<PushPull>> {
                 fn on(&mut self) -> () {
                 self.set_low().unwrap()
                 }
                 fn off(&mut self) -> () {
                     self.set_high().unwrap()
                 }
         }
    
         led
    }
    
    //  NEED TO SPECIFY DEVICE HERE FOR DIFFERENT HALs
    
    #[app(device = stm32f1xx_hal::pac, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)]
    //#[app(device = stm32f4xx_hal::pac, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)]
    
    
    const APP: () = {
        struct Resources {
            led: LedType,
        }
    
        #[init(schedule = [one, ten])]
        fn init(cx: init::Context) -> init::LateResources {
            
            //rtt_init_print!();
            //rprintln!("blink example");
    
            let mut core = cx.core;
            core.DCB.enable_trace();
            cortex_m::peripheral::DWT::unlock(); // needed on some devices
            core.DWT.enable_cycle_counter();
    
            let device: Peripherals = cx.device;
    
            let mut led = setup(device);
            
            led.off();
            
            cx.schedule.ten(cx.start + PERIOD.cycles()).unwrap();
            // offset 700 ms so blinks do not overlap
            cx.schedule.one(cx.start + (PULSE - 700 * MILLISECOND).cycles()).unwrap();
    
            init::LateResources {
                led,
            }
        }
    
        #[task(resources = [led], spawn = [blink], schedule = [one] )]
        fn one(cx: one::Context) {
           cx.spawn.blink(5u32).unwrap();
           cx.schedule.one(cx.scheduled + PULSE.cycles()).unwrap();
        }
    
        #[task(resources = [led], spawn = [blink], schedule = [ten] )]
        fn ten(cx: ten::Context) {
           cx.spawn.blink(500u32).unwrap();        
           cx.schedule.ten(cx.scheduled + PERIOD.cycles()).unwrap();
        }
    
        #[task(priority = 3, capacity = 3, resources = [led] )]
        fn led_on(cx: led_on::Context) {
           cx.resources.led.on();
        }
    
        #[task(priority = 3, capacity = 3, resources = [led] )]
        fn led_off(cx: led_off::Context) {
           cx.resources.led.off();
        }
    
        #[task(priority = 2, capacity = 3 , spawn = [led_on], schedule = [led_off] )]
        fn blink(cx: blink::Context, time : u32) {  //time in ms
           cx.schedule.led_off(cx.scheduled + (time * MILLISECOND).cycles()).unwrap();
           cx.spawn.led_on().unwrap();
        }
    
        //#[cfg(feature = "stm32f1xx")]   CFG DOES NOT WORK HERE
        extern "C" {
            fn TIM2();
            fn TIM3();
            fn TIM4();
            fn QEI0();
        }
    
    //    #[cfg(feature = "stm32f4xx")]CFG DOES NOT WORK HERE
    //    extern "C" {
    //        fn EXTI0();
    //        fn SDIO();
    //        fn TIM1();
    //        fn TIM2();
    //        fn TIM3();
    //        fn QEI0();
    //    }
    };
    

    The minor modifications are to change the #[app ... to indicate the device stm32f1xx_hal::pac or stm32f4xx_hal::pac and the section following extern "C" { to indicate the device's free interrupts. All my attempts to conditionally compile these using #[cfg(... attributes have failed.

    Being able to do this would greatly simplify CI testing for multiple device crates. I would like to be able to use

    #[cfg(feature = "stm32f4xx")]
    #[app(device = stm32f4xx_hal::pac, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)]
    

    or

    #[cfg(feature = "stm32f4xx")]
    use stm32f4xx_hal::pac;
     
    #[app(device = pac, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)]
    

    Is this possible?

    BEWARE that flashing this code will mean the processor is often in sleep/low power mode and there will be a problem reconnecting the probe, giving an error message Error: jtag status contains invalid mode value - communication failure. Among other places, this is discussed at https://github.com/japaric/f3/issues/84. For a bluepill fix (hold reset while loading openocd) see https://github.com/TeXitoi/blue-pill-quickstart. This is also discussed for probe.rs at https://github.com/probe-rs/probe-rs/issues/350.

    enhancement 
    opened by pdgilbert 16
  • Can't use early, shared resource in init in `0.6.0-alpha.0`

    Can't use early, shared resource in init in `0.6.0-alpha.0`

    The following example gives cannot find value priority in this scope.

    #![no_std]
    #![no_main]
    
    use panic_halt as _;
    
    #[rtic::app(device = atsamd51n)]
    mod app {
        #[resources]
        struct Resources {
            #[init(Default::default())]
            ping_buf: [i16; 8],
            #[init(Default::default())]
            pong_buf: [i16; 8],
        }
    
        #[init(resources = [ping_buf, pong_buf])]
        fn init(mut ctx: init::Context) -> init::LateResources {
            init::LateResources{}
        }
    }
    

    It looks like the generated code is trying to assign a priority for the lock, but there is no priority.

        impl<'a> initResources<'a> {
            #[inline(always)]
            pub unsafe fn new() -> Self {
                initResources {
                    ping_buf: resources::ping_buf::new(priority),
                    pong_buf: resources::pong_buf::new(priority),
                }
            }
        }
    

    That leads to another question. Why are locks necessary for early resources in init in the first place?

    In the meantime, do you have any suggested workarounds? I need the static address of the buffers in init. I guess I could do it myself, outside of the Resources struct, but I'd rather not have to go that far.

    opened by bradleyharden 16
  • Use embedded-time for Clock/Instant/Duration implementation

    Use embedded-time for Clock/Instant/Duration implementation

    This PR is an example implementation using the embedded-time crate. It will allow usage of human time units.

    For example, it replaces the cycles with real time units:

        #[task(resources = [led1], schedule = [turn_off_led1])]
        fn turn_on_led1(cx: turn_on_led1::Context) {
            let led1 = cx.resources.led1;
            led1.set_low().unwrap();
    
            cx.schedule
                .turn_off_led1(cx.scheduled + 250.milliseconds())
                .unwrap();
        }
    

    embedded-time

    embedded-time provides a comprehensive library for implementing abstractions over hardware and work with instants and durations in an intuitive way.

    • Clock trait allowing abstraction of hardware timers/counters for timekeeping.
    • Work with time using milliseconds, seconds, etc. rather than cycles or ticks.
    • Includes examples for the nRF52_DK development kit board.

    Fixes #309 #219

    Todo

    • [x] Add SysTimer/SysTick clock speed parameter
    • [x] Make sys_timer_freq optional (only required when using scheduling)
    opened by PTaylor-us 16
  • [RFC] change the late resource syntax

    [RFC] change the late resource syntax

    Current behavior

    The current syntax for late resources is very similar to the syntax for normal resources: one simply uses the unit value (() ) as the "initial value" for the late resource.

    #[rtfm::app(device = ..)]
    const APP: () = {
        static mut EARLY: u32 = 0;
        static mut LATE: u32 = ();
    };
    

    Rationale

    Some people have expressed their dissatisfaction with the current syntax because it wouldn't type check in normal Rust code; they would expect the initial value to be omitted as semantically there's no initial value. However, dropping the initial value would render the RTFM syntax un-rustfmt-able.

    #[rtfm::app(device = ..)]
    const APP: () = {
        static mut EARLY: u32 = 0;
        static mut LATE: u32; // <- not valid Rust syntax so it can't be rustfmt-ed
    };
    

    Proposal

    This RFC proposes using the extern static syntax to specify late resources. This lets us omit the initial value in the syntax while keeping the syntax rustfmt-able.

    Detailed design

    #[rtfm::app(device = ..)]
    const APP: () = {
        static mut EARLY: u32 = 0;
    
        extern "C" {
            // runtime initialized resource
            static mut LATE: u32;
    
            // runtime initialized `static` variable
            static ALSO_LATE: u32;
        }
    };
    

    Drawbacks

    There would be an ABI attached to the late resources. This gives the false impression that late resources use the "C" ABI:

    struct SomeStruct {
        // ..
    }
    
    #[rtfm::app(device = ..)]
    const APP: () = {
        // Rust ABI
        static mut EARLY: SomeStruct = SomeStruct::new();
    
        extern "C" {
            // C ABI?
            static mut LATE: SomeStruct;
        }
    
        // ..
    };
    

    Which is not true because the ABI is a property of the type not of the declaration.

    Changing the declaration to extern "Rust" doesn't really help because the resource could actually be using a C representation:

    #[repr(C)]
    struct SomeStruct {
        // ..
    }
    
    #[rtfm::app(device = ..)]
    const APP: () = {
        extern "Rust" {
            // Rust ABI?
            static mut LATE: SomeStruct;
        }
    
        // ..
    };
    

    Alternatives

    RFC #143 proposes using an struct syntax to specify resources. That proposal has two issues which this proposal doesn't have:

    • The syntax doesn't let the user distinguish resources (today's static mut variables) from static variables -- the distinction is important because static variables are treated differently in the Send / Sync analysis

    • It gets rid of non-late resources so one can no longer specify resources, or static variables, whose initial values are known at compile time -- this could have a small negative impact on the initialization time and program size of all RTFM applications.


    Another alternative would be to special case core::mem::MaybeUninit to mean late resources so one would write:

    use core::mem::MaybeUninit;
    
    #[rtfm::app(device = ..)]
    const APP: () = {
        static mut LATE: MaybeUninit<SomeStruct> = MaybeUninit::uninit();
    
        #[init]
        fn init(_: init::Context) -> init::LateResources {
            init::LateResources { LATE: SomeStruct::new() }
        }
    };
    

    However, this gets tricky quickly because the macro has no way to know whether the identifier MaybeUninit refers to core::mem::MaybeUninit or some user defined type.

    // user defined
    struct MaybeUninit<T> {
        always_initialized: T,
    }
    
    #[rtfm::app(device = ..)]
    const APP: () = {
        static mut LATE: MaybeUninit<SomeStruct> = MaybeUninit::uninit();
    
        #[init]
        fn init(_: init::Context) -> init::LateResources {
            init::LateResources { LATE: SomeStruct::new() }
        }
    };
    

    cc @TeXitoi

    RFC 
    opened by japaric 16
  • Automate so dependabot PRs get the `skip-changelog` label

    Automate so dependabot PRs get the `skip-changelog` label

    Saves a lot of clicks, and dependabot deals with behind the scenes things mainly. If need be it is possible to add manual changelog entry to the same PR.

    Dependabot makes detailed commit messages documenting the change anyhow

    opened by AfoHT 0
  • Update cortex-m-semihosting requirement from 0.3.3 to 0.5.0

    Update cortex-m-semihosting requirement from 0.3.3 to 0.5.0

    Updates the requirements on cortex-m-semihosting to permit the latest version.

    Changelog

    Sourced from cortex-m-semihosting's changelog.

    [v0.5.0] - 2018-05-11

    Added

    • DebugMonitor and SecureFault variants to the Exception enumeration.

    • An optional "inline-asm" feature

    Changed

    • [breaking-change] This crate now requires arm-none-eabi-gcc to be installed and available in $PATH when built with the "inline-asm" feature disabled (which is disabled by default).

    • [breaking-change] The register::{apsr,lr,pc} modules are now behind the "inline-asm" feature.

    • [breaking-change] Some variants of the Exception enumeration are no longer available on thumbv6m-none-eabi. See API docs for details.

    • [breaking-change] Several of the variants of the Exception enumeration have been renamed to match the CMSIS specification.

    • [breaking-change] fixed typo in shcrs field of scb::RegisterBlock; it was previously named shpcrs.

    • [breaking-change] removed several fields from scb::RegisterBlock on ARMv6-M. These registers are not available on that sub-architecture.

    • [breaking-change] changed the type of scb::RegisterBlock.shpr from RW<u8> to RW<u32> on ARMv6-M. These registers are word accessible only on that sub-architecture.

    • [breaking-change] renamed the mmar field of scb::RegisterBlock to mmfar to match the CMSIS name.

    • [breaking-change] removed the iabr field from scb::RegisterBlock on ARMv6-M. This register is not available on that sub-architecture.

    • [breaking-change] removed several fields from cpuid::RegisterBlock on ARMv6-M. These registers are not available on that sub-architecture.

    • [breaking-change] The Mutex.new constructor is not a const fn by default. To make it a const fn you have to opt into the "const-fn" feature, which was added in v0.5.1, and switch to a nightly compiler.

    Removed

    • [breaking-change] The exception module has been removed. A replacement for Exception::active can be found in SCB::vect_active. A modified version exception::Exception can be found in the peripheral::scb module.

    [v0.4.3] - 2018-01-25

    ... (truncated)

    Commits
    • a448e91 v0.5.0
    • e3217ad Merge #88
    • 05bbc3b always list all the peripherals in Peripherals
    • 550f949 fix build for ARMv7E-M + "inline-asm"
    • 7d51707 simplify #[cfg]s
    • 2cd6092 ARMv6-M: remove fields that are not available from cpuid::RegisterBlock
    • 17bd0c8 fix x86_64 tests
    • c290aa4 ARMv6-M: remove fields that are not available from NVIC and SCB
    • 716398c fix build on ARMv6-M
    • 1d68643 fix build on ARMv7E-M
    • Additional commits viewable in compare view

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    skip-changelog dependencies rust 
    opened by dependabot[bot] 2
  • Add documentation for different Cortex-M architectures

    Add documentation for different Cortex-M architectures

    Most of the RTIC documentation focuses on ARMv7-M architectures. Here's some initial thoughts on useful information I would have liked to know before starting with RTIC on ARMv6-M.

    opened by n8tlarsen 5
  • Why are exceptions with shared resources not allowed when compiling for thumbv6?

    Why are exceptions with shared resources not allowed when compiling for thumbv6?

    I came across a compile error (__rtic_internal_V6_ERROR) today and traced it back to this snippet from src/export.rs:

    #[cfg(not(have_basepri))]
    pub const fn no_basepri_panic() {
        panic!("Exceptions with shared resources are not allowed when compiling for thumbv6 or thumbv8m.base. Use local resources or `#[lock_free]` shared resources");
    }
    

    I'm well aware of the limitations for RTIC presented by the Armv6-M architecture (specifically no basepri present), but I'm not following why entering an infinite loop as given by

    #[panic_handler]
    fn panic(_info: &PanicInfo) -> ! {
        loop {
            atomic::compiler_fence(Ordering::SeqCst);
        }
    }
    

    in panic_halt should be reason not to allow shared variables. Maybe the error is meant to prevent other panic behaviors, but in that case the blanket error seems rather pedantic. Compiling with panic_abort on nightly also fails. Could someone shed some light on this design decision for me?

    opened by n8tlarsen 5
  • Tasks bound to interrupts can't be fully excluded via #[cfg]

    Tasks bound to interrupts can't be fully excluded via #[cfg]

    Modifying cfg-whole-task example:

    //! examples/cfg-whole-task.rs
    
    #![deny(unsafe_code)]
    #![deny(warnings)]
    #![no_main]
    #![no_std]
    
    use panic_semihosting as _;
    
    #[rtic::app(device = lm3s6965,  dispatchers = [SSI0, QEI0])]
    mod app {
        use cortex_m_semihosting::debug;
    
        #[shared]
        struct Shared {
            #[cfg(never)]
            unused: u32,
            count: u32,
        }
    
        #[local]
        struct Local {
    
        }
    
        #[init]
        fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
    
            (
                Shared {
                    #[cfg(never)]
                    unused: 1,
                    count: 0,
                },
                Local {
                },
                init::Monotonics(),
            )
        }
    
        #[idle]
        fn idle(_: idle::Context) -> ! {
            debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
    
            loop {
                cortex_m::asm::nop();
            }
        }
    
    
    
        #[task(binds=UART2, shared = [count])]
        fn foo(mut cx: foo::Context) {
            cx.shared.count.lock(|count| *count += 1);
        }
    
        // The whole task should disappear,
        // currently still present in the Tasks enum
        #[cfg(never))]
        #[task(binds=UART1, shared = [count])]
        fn foo2(mut cx: foo2::Context) {
            cx.shared.count.lock(|count| *count += 10);
        }
    }
    
    

    building it yields:

    error[E0412]: cannot find type `Context` in module `foo2`
      --> examples/cfg-whole-task.rs:10:1
       |
    10 | #[rtic::app(device = lm3s6965,  dispatchers = [SSI0, QEI0])]
       | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in `foo2`
       |
       = note: this error originates in the attribute macro `rtic::app` (in Nightly builds, run with -Z macro-backtrace for more info)
    help: consider importing one of these items
       |
    12 |     use core::task::Context;
       |
    12 |     use crate::app::foo::Context;
       |
    12 |     use crate::app::idle::Context;
       |
    12 |     use crate::app::init::Context;
       |
    
    For more information about this error, try `rustc --explain E0412`.
    error: could not compile `cortex-m-rtic` due to previous error
    

    macro expansion:

    #![feature(prelude_import)]
    //! examples/cfg-whole-task.rs
    #![deny(unsafe_code)]
    #![deny(warnings)]
    #![no_main]
    #![no_std]
    #[prelude_import]
    use core::prelude::rust_2021::*;
    #[macro_use]
    extern crate core;
    #[macro_use]
    extern crate compiler_builtins;
    use panic_semihosting as _;
    /// The RTIC application module
    pub mod app {
        /// Always include the device crate which contains the vector table
        use lm3s6965 as you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml;
        use cortex_m_semihosting::debug;
        /// User code from within the module
        /// User code end
        #[inline(always)]
        #[allow(non_snake_case)]
        fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
            (Shared { count: 0 }, Local {}, init::Monotonics())
        }
        #[allow(non_snake_case)]
        fn idle(_: idle::Context) -> ! {
            use rtic::Mutex as _;
            use rtic::mutex::prelude::*;
            debug::exit(debug::EXIT_SUCCESS);
            loop {
                cortex_m::asm::nop();
            }
        }
        #[allow(non_snake_case)]
        fn foo(mut cx: foo::Context) {
            use rtic::Mutex as _;
            use rtic::mutex::prelude::*;
            cx.shared.count.lock(|count| *count += 1);
        }
        #[allow(non_snake_case)]
        fn foo2(mut cx: foo2::Context) {
            use rtic::Mutex as _;
            use rtic::mutex::prelude::*;
            cx.shared.count.lock(|count| *count += 10);
        }
        struct Shared {
            count: u32,
        }
        struct Local {}
        /// Monotonics used by the system
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        pub struct __rtic_internal_Monotonics();
        /// Execution context
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        pub struct __rtic_internal_init_Context<'a> {
            /// Core (Cortex-M) peripherals
            pub core: rtic::export::Peripherals,
            /// Device peripherals
            pub device: lm3s6965::Peripherals,
            /// Critical section token for init
            pub cs: rtic::export::CriticalSection<'a>,
        }
        impl<'a> __rtic_internal_init_Context<'a> {
            #[inline(always)]
            pub unsafe fn new(core: rtic::export::Peripherals) -> Self {
                __rtic_internal_init_Context {
                    device: lm3s6965::Peripherals::steal(),
                    cs: rtic::export::CriticalSection::new(),
                    core,
                }
            }
        }
        #[allow(non_snake_case)]
        ///Initialization function
        pub mod init {
            pub use super::__rtic_internal_Monotonics as Monotonics;
            pub use super::__rtic_internal_init_Context as Context;
        }
        /// Execution context
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        pub struct __rtic_internal_idle_Context {}
        impl __rtic_internal_idle_Context {
            #[inline(always)]
            pub unsafe fn new(priority: &rtic::export::Priority) -> Self {
                __rtic_internal_idle_Context {}
            }
        }
        #[allow(non_snake_case)]
        ///Idle loop
        pub mod idle {
            pub use super::__rtic_internal_idle_Context as Context;
        }
        mod shared_resources {
            use rtic::export::Priority;
            #[doc(hidden)]
            #[allow(non_camel_case_types)]
            pub struct count_that_needs_to_be_locked<'a> {
                priority: &'a Priority,
            }
            impl<'a> count_that_needs_to_be_locked<'a> {
                #[inline(always)]
                pub unsafe fn new(priority: &'a Priority) -> Self {
                    count_that_needs_to_be_locked { priority }
                }
                #[inline(always)]
                pub unsafe fn priority(&self) -> &Priority {
                    self.priority
                }
            }
        }
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        ///Shared resources `foo` has access to
        pub struct __rtic_internal_fooSharedResources<'a> {
            pub count: shared_resources::count_that_needs_to_be_locked<'a>,
        }
        /// Execution context
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        pub struct __rtic_internal_foo_Context<'a> {
            /// Shared Resources this task has access to
            pub shared: foo::SharedResources<'a>,
        }
        impl<'a> __rtic_internal_foo_Context<'a> {
            #[inline(always)]
            pub unsafe fn new(priority: &'a rtic::export::Priority) -> Self {
                __rtic_internal_foo_Context {
                    shared: foo::SharedResources::new(priority),
                }
            }
        }
        #[allow(non_snake_case)]
        ///Hardware task
        pub mod foo {
            #[doc(inline)]
            pub use super::__rtic_internal_fooSharedResources as SharedResources;
            pub use super::__rtic_internal_foo_Context as Context;
        }
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        ///Shared resources `foo2` has access to
        pub struct __rtic_internal_foo2SharedResources<'a> {
            pub count: shared_resources::count_that_needs_to_be_locked<'a>,
        }
        #[allow(non_snake_case)]
        ///Hardware task
        pub mod foo2 {
            #[doc(inline)]
            pub use super::__rtic_internal_foo2SharedResources as SharedResources;
        }
        #[allow(non_camel_case_types)]
        #[allow(non_upper_case_globals)]
        #[doc(hidden)]
        #[link_section = ".uninit.rtic1"]
        static __rtic_internal_shared_resource_count: rtic::RacyCell<core::mem::MaybeUninit<u32>> =
            rtic::RacyCell::new(core::mem::MaybeUninit::uninit());
        impl<'a> rtic::Mutex for shared_resources::count_that_needs_to_be_locked<'a> {
            type T = u32;
            #[inline(always)]
            fn lock<RTIC_INTERNAL_R>(
                &mut self,
                f: impl FnOnce(&mut u32) -> RTIC_INTERNAL_R,
            ) -> RTIC_INTERNAL_R {
                /// Priority ceiling
                const CEILING: u8 = 1u8;
                unsafe {
                    rtic::export::lock(
                        __rtic_internal_shared_resource_count.get_mut() as *mut _,
                        self.priority(),
                        CEILING,
                        lm3s6965::NVIC_PRIO_BITS,
                        &__rtic_internal_MASKS,
                        f,
                    )
                }
            }
        }
        #[doc(hidden)]
        #[allow(non_upper_case_globals)]
        const __rtic_internal_MASK_CHUNKS: usize = rtic::export::compute_mask_chunks([
            lm3s6965::Interrupt::UART2 as u32,
            lm3s6965::Interrupt::UART1 as u32,
        ]);
        #[doc(hidden)]
        #[allow(non_upper_case_globals)]
        const __rtic_internal_MASKS: [rtic::export::Mask<__rtic_internal_MASK_CHUNKS>; 3] = [
            rtic::export::create_mask([
                lm3s6965::Interrupt::UART2 as u32,
                lm3s6965::Interrupt::UART1 as u32,
            ]),
            rtic::export::create_mask([]),
            rtic::export::create_mask([]),
        ];
        #[allow(non_snake_case)]
        #[no_mangle]
        unsafe fn UART2() {
            const PRIORITY: u8 = 1u8;
            rtic::export::run(PRIORITY, || {
                foo(foo::Context::new(&rtic::export::Priority::new(PRIORITY)))
            });
        }
        impl<'a> __rtic_internal_fooSharedResources<'a> {
            #[inline(always)]
            pub unsafe fn new(priority: &'a rtic::export::Priority) -> Self {
                __rtic_internal_fooSharedResources {
                    count: shared_resources::count_that_needs_to_be_locked::new(priority),
                }
            }
        }
        impl<'a> __rtic_internal_foo2SharedResources<'a> {
            #[inline(always)]
            pub unsafe fn new(priority: &'a rtic::export::Priority) -> Self {
                __rtic_internal_foo2SharedResources {
                    count: shared_resources::count_that_needs_to_be_locked::new(priority),
                }
            }
        }
        #[doc(hidden)]
        mod rtic_ext {
            use super::*;
            #[no_mangle]
            unsafe extern "C" fn main() -> ! {
                rtic::export::assert_send::<u32>();
                const _CONST_CHECK: () = {
                    if !rtic::export::have_basepri() {
                        if (lm3s6965::Interrupt::UART2 as usize) >= (__rtic_internal_MASK_CHUNKS * 32) {
                            ::core::panicking::panic_fmt(::core::fmt::Arguments::new_v1(
                                &["An interrupt out of range is used while in armv6 or armv8m.base"],
                                &[],
                            ));
                        }
                        if (lm3s6965::Interrupt::UART1 as usize) >= (__rtic_internal_MASK_CHUNKS * 32) {
                            ::core::panicking::panic_fmt(::core::fmt::Arguments::new_v1(
                                &["An interrupt out of range is used while in armv6 or armv8m.base"],
                                &[],
                            ));
                        }
                    } else {
                    }
                };
                let _ = _CONST_CHECK;
                rtic::export::interrupt::disable();
                let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into();
                let _ = you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::interrupt::SSI0;
                let _ = you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::interrupt::QEI0;
                const _: () = if (1 << lm3s6965::NVIC_PRIO_BITS) < 1u8 as usize {
                    :: core :: panicking :: panic_fmt (:: core :: fmt :: Arguments :: new_v1 (& ["Maximum priority used by interrupt vector \'UART2\' is more than supported by hardware"] , & [])) ;
                };
                core.NVIC.set_priority(
                    you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::interrupt::UART2,
                    rtic::export::logical2hw(1u8, lm3s6965::NVIC_PRIO_BITS),
                );
                rtic::export::NVIC::unmask(
                    you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::interrupt::UART2,
                );
                const _: () = if (1 << lm3s6965::NVIC_PRIO_BITS) < 1u8 as usize {
                    :: core :: panicking :: panic_fmt (:: core :: fmt :: Arguments :: new_v1 (& ["Maximum priority used by interrupt vector \'UART1\' is more than supported by hardware"] , & [])) ;
                };
                core.NVIC.set_priority(
                    you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::interrupt::UART1,
                    rtic::export::logical2hw(1u8, lm3s6965::NVIC_PRIO_BITS),
                );
                rtic::export::NVIC::unmask(
                    you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::interrupt::UART1,
                );
                #[inline(never)]
                fn __rtic_init_resources<F>(f: F)
                where
                    F: FnOnce(),
                {
                    f();
                }
                __rtic_init_resources(|| {
                    let (shared_resources, local_resources, mut monotonics) =
                        init(init::Context::new(core.into()));
                    __rtic_internal_shared_resource_count
                        .get_mut()
                        .write(core::mem::MaybeUninit::new(shared_resources.count));
                    rtic::export::interrupt::enable();
                });
                idle(idle::Context::new(&rtic::export::Priority::new(0)))
            }
        }
    }
    

    cfg-whole-task without modifications can be compiled fine, with following expansion:

    #![feature(prelude_import)]
    //! examples/cfg-whole-task.rs
    #![deny(unsafe_code)]
    #![deny(warnings)]
    #![no_main]
    #![no_std]
    #[prelude_import]
    use core::prelude::rust_2021::*;
    #[macro_use]
    extern crate core;
    #[macro_use]
    extern crate compiler_builtins;
    use panic_semihosting as _;
    /// The RTIC application module
    pub mod app {
        /// Always include the device crate which contains the vector table
        use lm3s6965 as you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml;
        use cortex_m_semihosting::debug;
        #[cfg(debug_assertions)]
        use cortex_m_semihosting::hprintln;
        /// User code from within the module
        /// User code end
        #[inline(always)]
        #[allow(non_snake_case)]
        fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
            foo::spawn().unwrap();
            foo::spawn().unwrap();
            (Shared { count: 0 }, Local {}, init::Monotonics())
        }
        #[allow(non_snake_case)]
        fn idle(_: idle::Context) -> ! {
            use rtic::Mutex as _;
            use rtic::mutex::prelude::*;
            debug::exit(debug::EXIT_SUCCESS);
            loop {
                cortex_m::asm::nop();
            }
        }
        #[allow(non_snake_case)]
        fn foo(mut _cx: foo::Context) {
            use rtic::Mutex as _;
            use rtic::mutex::prelude::*;
            #[cfg(debug_assertions)]
            {
                _cx.shared.count.lock(|count| *count += 1);
                log::spawn(_cx.shared.count.lock(|count| *count)).unwrap();
            }
        }
        #[cfg(debug_assertions)]
        #[allow(non_snake_case)]
        fn log(_: log::Context, n: u32) {
            use rtic::Mutex as _;
            use rtic::mutex::prelude::*;
            ::cortex_m_semihosting::export::hstdout_fmt(::core::fmt::Arguments::new_v1(
                &["foo has been called ", " time", "\n"],
                &[
                    ::core::fmt::ArgumentV1::new_display(&n),
                    ::core::fmt::ArgumentV1::new_display(&if n == 1 { "" } else { "s" }),
                ],
            ))
            .ok();
        }
        struct Shared {
            count: u32,
        }
        struct Local {}
        /// Monotonics used by the system
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        pub struct __rtic_internal_Monotonics();
        /// Execution context
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        pub struct __rtic_internal_init_Context<'a> {
            /// Core (Cortex-M) peripherals
            pub core: rtic::export::Peripherals,
            /// Device peripherals
            pub device: lm3s6965::Peripherals,
            /// Critical section token for init
            pub cs: rtic::export::CriticalSection<'a>,
        }
        impl<'a> __rtic_internal_init_Context<'a> {
            #[inline(always)]
            pub unsafe fn new(core: rtic::export::Peripherals) -> Self {
                __rtic_internal_init_Context {
                    device: lm3s6965::Peripherals::steal(),
                    cs: rtic::export::CriticalSection::new(),
                    core,
                }
            }
        }
        #[allow(non_snake_case)]
        ///Initialization function
        pub mod init {
            pub use super::__rtic_internal_Monotonics as Monotonics;
            pub use super::__rtic_internal_init_Context as Context;
        }
        /// Execution context
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        pub struct __rtic_internal_idle_Context {}
        impl __rtic_internal_idle_Context {
            #[inline(always)]
            pub unsafe fn new(priority: &rtic::export::Priority) -> Self {
                __rtic_internal_idle_Context {}
            }
        }
        #[allow(non_snake_case)]
        ///Idle loop
        pub mod idle {
            pub use super::__rtic_internal_idle_Context as Context;
        }
        mod shared_resources {
            use rtic::export::Priority;
            #[doc(hidden)]
            #[allow(non_camel_case_types)]
            pub struct count_that_needs_to_be_locked<'a> {
                priority: &'a Priority,
            }
            impl<'a> count_that_needs_to_be_locked<'a> {
                #[inline(always)]
                pub unsafe fn new(priority: &'a Priority) -> Self {
                    count_that_needs_to_be_locked { priority }
                }
                #[inline(always)]
                pub unsafe fn priority(&self) -> &Priority {
                    self.priority
                }
            }
        }
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        ///Shared resources `foo` has access to
        pub struct __rtic_internal_fooSharedResources<'a> {
            pub count: shared_resources::count_that_needs_to_be_locked<'a>,
        }
        /// Execution context
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        pub struct __rtic_internal_foo_Context<'a> {
            /// Shared Resources this task has access to
            pub shared: foo::SharedResources<'a>,
        }
        impl<'a> __rtic_internal_foo_Context<'a> {
            #[inline(always)]
            pub unsafe fn new(priority: &'a rtic::export::Priority) -> Self {
                __rtic_internal_foo_Context {
                    shared: foo::SharedResources::new(priority),
                }
            }
        }
        /// Spawns the task directly
        pub fn __rtic_internal_foo_spawn() -> Result<(), ()> {
            let input = ();
            unsafe {
                if let Some(index) = rtic::export::interrupt::free(|_| {
                    (&mut *__rtic_internal_foo_FQ.get_mut()).dequeue()
                }) {
                    (&mut *__rtic_internal_foo_INPUTS.get_mut())
                        .get_unchecked_mut(usize::from(index))
                        .as_mut_ptr()
                        .write(input);
                    rtic::export::interrupt::free(|_| {
                        (&mut *__rtic_internal_P1_RQ.get_mut()).enqueue_unchecked((P1_T::foo, index));
                    });
                    rtic::pend(lm3s6965::interrupt::SSI0);
                    Ok(())
                } else {
                    Err(input)
                }
            }
        }
        #[allow(non_snake_case)]
        ///Software task
        pub mod foo {
            #[doc(inline)]
            pub use super::__rtic_internal_fooSharedResources as SharedResources;
            pub use super::__rtic_internal_foo_Context as Context;
            pub use super::__rtic_internal_foo_spawn as spawn;
        }
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        ///Shared resources `foo2` has access to
        pub struct __rtic_internal_foo2SharedResources<'a> {
            pub count: shared_resources::count_that_needs_to_be_locked<'a>,
        }
        #[cfg(debug_assertions)]
        /// Execution context
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        pub struct __rtic_internal_log_Context {}
        #[cfg(debug_assertions)]
        impl __rtic_internal_log_Context {
            #[inline(always)]
            pub unsafe fn new(priority: &rtic::export::Priority) -> Self {
                __rtic_internal_log_Context {}
            }
        }
        #[cfg(debug_assertions)]
        /// Spawns the task directly
        pub fn __rtic_internal_log_spawn(_0: u32) -> Result<(), u32> {
            let input = _0;
            unsafe {
                if let Some(index) = rtic::export::interrupt::free(|_| {
                    (&mut *__rtic_internal_log_FQ.get_mut()).dequeue()
                }) {
                    (&mut *__rtic_internal_log_INPUTS.get_mut())
                        .get_unchecked_mut(usize::from(index))
                        .as_mut_ptr()
                        .write(input);
                    rtic::export::interrupt::free(|_| {
                        (&mut *__rtic_internal_P1_RQ.get_mut()).enqueue_unchecked((P1_T::log, index));
                    });
                    rtic::pend(lm3s6965::interrupt::SSI0);
                    Ok(())
                } else {
                    Err(input)
                }
            }
        }
        #[allow(non_snake_case)]
        #[cfg(debug_assertions)]
        ///Software task
        pub mod log {
            #[cfg(debug_assertions)]
            pub use super::__rtic_internal_log_Context as Context;
            #[cfg(debug_assertions)]
            pub use super::__rtic_internal_log_spawn as spawn;
        }
        /// app module
        #[allow(non_camel_case_types)]
        #[allow(non_upper_case_globals)]
        #[doc(hidden)]
        #[link_section = ".uninit.rtic0"]
        static __rtic_internal_shared_resource_count: rtic::RacyCell<core::mem::MaybeUninit<u32>> =
            rtic::RacyCell::new(core::mem::MaybeUninit::uninit());
        impl<'a> rtic::Mutex for shared_resources::count_that_needs_to_be_locked<'a> {
            type T = u32;
            #[inline(always)]
            fn lock<RTIC_INTERNAL_R>(
                &mut self,
                f: impl FnOnce(&mut u32) -> RTIC_INTERNAL_R,
            ) -> RTIC_INTERNAL_R {
                /// Priority ceiling
                const CEILING: u8 = 1u8;
                unsafe {
                    rtic::export::lock(
                        __rtic_internal_shared_resource_count.get_mut() as *mut _,
                        self.priority(),
                        CEILING,
                        lm3s6965::NVIC_PRIO_BITS,
                        &__rtic_internal_MASKS,
                        f,
                    )
                }
            }
        }
        #[doc(hidden)]
        #[allow(non_upper_case_globals)]
        const __rtic_internal_MASK_CHUNKS: usize =
            rtic::export::compute_mask_chunks([lm3s6965::Interrupt::SSI0 as u32]);
        #[doc(hidden)]
        #[allow(non_upper_case_globals)]
        const __rtic_internal_MASKS: [rtic::export::Mask<__rtic_internal_MASK_CHUNKS>; 3] = [
            rtic::export::create_mask([lm3s6965::Interrupt::SSI0 as u32]),
            rtic::export::create_mask([]),
            rtic::export::create_mask([]),
        ];
        #[allow(non_camel_case_types)]
        #[allow(non_upper_case_globals)]
        #[doc(hidden)]
        static __rtic_internal_foo_FQ: rtic::RacyCell<rtic::export::SCFQ<3>> =
            rtic::RacyCell::new(rtic::export::Queue::new());
        #[link_section = ".uninit.rtic2"]
        #[allow(non_camel_case_types)]
        #[allow(non_upper_case_globals)]
        #[doc(hidden)]
        static __rtic_internal_foo_INPUTS: rtic::RacyCell<[core::mem::MaybeUninit<()>; 2]> =
            rtic::RacyCell::new([
                core::mem::MaybeUninit::uninit(),
                core::mem::MaybeUninit::uninit(),
            ]);
        impl<'a> __rtic_internal_fooSharedResources<'a> {
            #[inline(always)]
            pub unsafe fn new(priority: &'a rtic::export::Priority) -> Self {
                __rtic_internal_fooSharedResources {
                    count: shared_resources::count_that_needs_to_be_locked::new(priority),
                }
            }
        }
        #[allow(non_camel_case_types)]
        #[allow(non_upper_case_globals)]
        #[doc(hidden)]
        static __rtic_internal_foo2_FQ: rtic::RacyCell<rtic::export::SCFQ<3>> =
            rtic::RacyCell::new(rtic::export::Queue::new());
        #[link_section = ".uninit.rtic3"]
        #[allow(non_camel_case_types)]
        #[allow(non_upper_case_globals)]
        #[doc(hidden)]
        static __rtic_internal_foo2_INPUTS: rtic::RacyCell<[core::mem::MaybeUninit<()>; 2]> =
            rtic::RacyCell::new([
                core::mem::MaybeUninit::uninit(),
                core::mem::MaybeUninit::uninit(),
            ]);
        impl<'a> __rtic_internal_foo2SharedResources<'a> {
            #[inline(always)]
            pub unsafe fn new(priority: &'a rtic::export::Priority) -> Self {
                __rtic_internal_foo2SharedResources {
                    count: shared_resources::count_that_needs_to_be_locked::new(priority),
                }
            }
        }
        #[allow(non_camel_case_types)]
        #[allow(non_upper_case_globals)]
        #[doc(hidden)]
        static __rtic_internal_log_FQ: rtic::RacyCell<rtic::export::SCFQ<3>> =
            rtic::RacyCell::new(rtic::export::Queue::new());
        #[link_section = ".uninit.rtic4"]
        #[allow(non_camel_case_types)]
        #[allow(non_upper_case_globals)]
        #[doc(hidden)]
        static __rtic_internal_log_INPUTS: rtic::RacyCell<[core::mem::MaybeUninit<u32>; 2]> =
            rtic::RacyCell::new([
                core::mem::MaybeUninit::uninit(),
                core::mem::MaybeUninit::uninit(),
            ]);
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        #[doc(hidden)]
        pub enum P1_T {
            foo,
            #[cfg(debug_assertions)]
            log,
        }
        #[automatically_derived]
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        impl ::core::clone::Clone for P1_T {
            #[inline]
            fn clone(&self) -> P1_T {
                *self
            }
        }
        #[automatically_derived]
        #[allow(non_snake_case)]
        #[allow(non_camel_case_types)]
        impl ::core::marker::Copy for P1_T {}
        #[doc(hidden)]
        #[allow(non_camel_case_types)]
        #[allow(non_upper_case_globals)]
        static __rtic_internal_P1_RQ: rtic::RacyCell<rtic::export::SCRQ<P1_T, 7>> =
            rtic::RacyCell::new(rtic::export::Queue::new());
        #[allow(non_snake_case)]
        ///Interrupt handler to dispatch tasks at priority 1
        #[no_mangle]
        unsafe fn SSI0() {
            /// The priority of this interrupt handler
            const PRIORITY: u8 = 1u8;
            rtic::export::run(PRIORITY, || {
                while let Some((task, index)) =
                    (&mut *__rtic_internal_P1_RQ.get_mut()).split().1.dequeue()
                {
                    match task {
                        P1_T::foo => {
                            let () = (&*__rtic_internal_foo_INPUTS.get())
                                .get_unchecked(usize::from(index))
                                .as_ptr()
                                .read();
                            (&mut *__rtic_internal_foo_FQ.get_mut())
                                .split()
                                .0
                                .enqueue_unchecked(index);
                            let priority = &rtic::export::Priority::new(PRIORITY);
                            foo(foo::Context::new(priority))
                        }
                        #[cfg(debug_assertions)]
                        P1_T::log => {
                            let _0 = (&*__rtic_internal_log_INPUTS.get())
                                .get_unchecked(usize::from(index))
                                .as_ptr()
                                .read();
                            (&mut *__rtic_internal_log_FQ.get_mut())
                                .split()
                                .0
                                .enqueue_unchecked(index);
                            let priority = &rtic::export::Priority::new(PRIORITY);
                            log(log::Context::new(priority), _0)
                        }
                    }
                }
            });
        }
        #[doc(hidden)]
        mod rtic_ext {
            use super::*;
            #[no_mangle]
            unsafe extern "C" fn main() -> ! {
                rtic::export::assert_send::<u32>();
                const _CONST_CHECK: () = {
                    if !rtic::export::have_basepri() {
                    } else {
                    }
                };
                let _ = _CONST_CHECK;
                rtic::export::interrupt::disable();
                (0..2u8).for_each(|i| (&mut *__rtic_internal_foo_FQ.get_mut()).enqueue_unchecked(i));
                (0..2u8).for_each(|i| (&mut *__rtic_internal_foo2_FQ.get_mut()).enqueue_unchecked(i));
                (0..2u8).for_each(|i| (&mut *__rtic_internal_log_FQ.get_mut()).enqueue_unchecked(i));
                let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into();
                let _ = you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::interrupt::SSI0;
                let _ = you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::interrupt::QEI0;
                const _: () = if (1 << lm3s6965::NVIC_PRIO_BITS) < 1u8 as usize {
                    :: core :: panicking :: panic_fmt (:: core :: fmt :: Arguments :: new_v1 (& ["Maximum priority used by interrupt vector \'SSI0\' is more than supported by hardware"] , & [])) ;
                };
                core.NVIC.set_priority(
                    you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::interrupt::SSI0,
                    rtic::export::logical2hw(1u8, lm3s6965::NVIC_PRIO_BITS),
                );
                rtic::export::NVIC::unmask(
                    you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::interrupt::SSI0,
                );
                #[inline(never)]
                fn __rtic_init_resources<F>(f: F)
                where
                    F: FnOnce(),
                {
                    f();
                }
                __rtic_init_resources(|| {
                    let (shared_resources, local_resources, mut monotonics) =
                        init(init::Context::new(core.into()));
                    __rtic_internal_shared_resource_count
                        .get_mut()
                        .write(core::mem::MaybeUninit::new(shared_resources.count));
                    rtic::export::interrupt::enable();
                });
                idle(idle::Context::new(&rtic::export::Priority::new(0)))
            }
        }
    }
    
    

    As one can see, when no interrupts are used task disappears completely.

    #[cfg] 
    opened by little-arhat 0
  • Support for #[cfg] on #[monotonic]

    Support for #[cfg] on #[monotonic]

    I would like to feature-gate the monotonic type to only have it in some builds and not others. I tried the following but it doesn't work:

        #[cfg(feature = "log")]
        #[monotonic(binds = SysTick, default = true)]
        type MonoSysTick = Systick<100>;
    

    Then in init I have:

            (
                Shared { ... },
                Local { ... },
                #[cfg(not(feature = "log"))]
                init::Monotonics(),
                #[cfg(feature = "log")]
                init::Monotonics(systick),
            )
    
    

    Here's what I get when building without the feature (it works with the feature):

    error[E0412]: cannot find type `MonoSysTick` in module `super::super`
      --> src/main.rs:51:10
       |
    51 |     type MonoSysTick = Systick<100>;
       |          ^^^^^^^^^^^ not found in `super::super`
    
    error[E0412]: cannot find type `Systick` in this scope
      --> src/main.rs:51:24
       |
    51 |     type MonoSysTick = Systick<100>;
       |                        ^^^^^^^ not found in this scope
    
    error[E0061]: this struct takes 1 argument but 0 arguments were supplied
       --> src/main.rs:117:13
        |
    117 |             init::Monotonics(),
        |             ^^^^^^^^^^^^^^^^-- an argument is missing
        |
    

    Is this something that might eventually get support for?

    #[cfg] 
    opened by ia0 0
Releases(v0.4.2)
  • v0.4.2(Feb 27, 2019)

    Added

    • Duration now has an as_cycles method to get the number of clock cycles contained in it.

    • An opt-in "nightly" feature that reduces static memory usage, shortens initialization time and reduces runtime overhead has been added. To use this feature you need a nightly compiler!

    • RFC 128 has been implemented. The exception and interrupt have gained a binds argument that lets you give the handler an arbitrary name. For example:

    // on v0.4.1 you had to write
    #[interrupt]
    fn USART0() { .. }
    
    // on v0.4.2 you can write
    #[interrupt(binds = USART0)]
    fn on_new_frame() { .. }
    

    Changed

    • Builds are now reproducible. cargo build; cargo clean; cargo build will produce binaries that are exactly the same (after objcopy -O ihex). This wasn't the case before because we used randomly generated identifiers for memory safety but now all the randomness is gone.

    Fixed

    • Fixed a non_camel_case_types warning that showed up when using a recent nightly.

    • Fixed a bug that allowed you to enter the capacity and priority arguments in the task attribute more than once. Now all arguments can only be stated once in the list, as it should be.

    Source code(tar.gz)
    Source code(zip)
Owner
Real-Time Interrupt-driven Concurrency (RTIC)
Organization for the RTIC scheduler and ecosystem
Real-Time Interrupt-driven Concurrency (RTIC)
A Real Time Operating System in Rust for Cortex M3 embedded systems

A Real Time Operating System in Rust for Cortex M3 embedded systems

Manuel Forcén Muñoz 5 Jun 1, 2022
A comparison of operating systems written in Rust

Rust OS comparison A comparison of operating systems written in Rust. There are several open source operating systems written in Rust. Most of them ar

Markus Kohlhase 492 Jan 8, 2023
Hubris is a microcontroller operating environment designed for deeply-embedded systems

A lightweight, memory-protected, message-passing kernel for deeply embedded systems.

Oxide Computer Company 2.1k Jan 6, 2023
A Rust-based userland which also adds compile-time assurances to seL4 development.

ferros Overview A Rust library to add extra assurances to seL4 development. ferros provides smart type-safe wrappers around seL4 features with an emph

Auxon Corporation 68 Dec 25, 2022
Easy c̵̰͠r̵̛̠ö̴̪s̶̩̒s̵̭̀-t̶̲͝h̶̯̚r̵̺͐e̷̖̽ḁ̴̍d̶̖̔ ȓ̵͙ė̶͎ḟ̴͙e̸̖͛r̶̖͗ë̶̱́ṉ̵̒ĉ̷̥e̷͚̍ s̷̹͌h̷̲̉a̵̭͋r̷̫̊ḭ̵̊n̷̬͂g̵̦̃ f̶̻̊ơ̵̜ṟ̸̈́ R̵̞̋ù̵̺s̷̖̅ţ̸͗!̸̼͋

Rust S̵̓i̸̓n̵̉ I̴n̴f̶e̸r̵n̷a̴l mutability! Howdy, friendly Rust developer! Ever had a value get m̵̯̅ð̶͊v̴̮̾ê̴̼͘d away right under your nose just when

null 294 Dec 23, 2022
Real Time For the Masses (RTFM), a framework for building concurrent applications, for MSP430 MCUs

msp430-rtfm Real Time For the Masses (RTFM), a framework for building concurrent applications, for MSP430 MCUs License Licensed under either of Apache

Jorge Aparicio 9 Sep 10, 2022
A Real Time Operating System in Rust for Cortex M3 embedded systems

A Real Time Operating System in Rust for Cortex M3 embedded systems

Manuel Forcén Muñoz 5 Jun 1, 2022
Nimbus is a framework for building parachain consensus systems on cumulus-based parachains.

Cumulo -- Nimbus ⛈️ Nimbus is a framework for building parachain consensus systems on cumulus-based parachains. Given the regular six-second pulse-lik

null 36 Dec 14, 2022
Thalo is an event-sourcing framework for building large scale systems

Thalo Event sourcing framework for building microservices. Overview Thalo is an event-sourcing framework for building large scale systems based on the

null 548 Jan 3, 2023
Open-source Rust framework for building event-driven live-trading & backtesting systems

Barter Barter is an open-source Rust framework for building event-driven live-trading & backtesting systems. Algorithmic trade with the peace of mind

Barter 157 Feb 18, 2023
Scalable and fast data store optimised for time series data such as financial data, events, metrics for real time analysis

OnTimeDB Scalable and fast data store optimised for time series data such as financial data, events, metrics for real time analysis OnTimeDB is a time

Stuart 2 Apr 5, 2022
`fugit` provides a comprehensive library of `Duration` and `Instant` for the handling of time in embedded systems, doing all it can at compile time.

fugit fugit provides a comprehensive library of Duration and Instant for the handling of time in embedded systems, doing all it can at compile time. T

Emil Fresk 40 Oct 2, 2022
A universal load testing framework for Rust, with real-time tui support.

rlt A Rust Load Testing framework with real-time tui support. rlt provides a simple way to create load test tools in Rust. It is designed to be a univ

Wenxuan 129 Jul 20, 2024
Concurrency extensions for Future

futures-concurrency Concurrency extensions for Future API Docs | Releases | Contributing Installation $ cargo add futures-concurrency Contributing Wan

Yoshua Wuyts 169 Dec 27, 2022
Loom is a concurrency permutation testing tool for Rust.

Loom is a testing tool for concurrent Rust code

Tokio 1.4k Jan 9, 2023
A prisma query-engine concurrency runner

Smash A prisma query-engine concurrency runner. Smash can be used to run concurrent requests against the prisma query engine. Currently it has workloa

garren smith 2 Jan 26, 2022
High concurrency, RealTime, In-memory storage inspired by erlang mnesia

DarkBird is a Document oriented, high concurrency in-memory Storage, also persist data to disk to avoid loss any data The darkbird provides the follow

DanyalMh 25 Dec 15, 2022
Rust Concurrency Cheat Sheet

Rust ensures data race safety through the type system (Send and Sync marker traits) as well as the ownership and borrowing rules: it is not allowed to alias a mutable reference, so it is not possible to perform a data race.

null 327 Dec 19, 2022
Optimistic multi-version concurrency control (MVCC) for main memory databases, written in Rust.

MVCC for Rust This is a work-in-progress the Hekaton optimistic multiversion concurrency control library in Rust. The aim of the project is to provide

Pekka Enberg 32 Apr 20, 2023
The Amp programming language: a language designed for building high performance systems.

A language designed for building high performance systems. Platform Support x86_64-pc-windows ✅ x86_64-unknown-linux ⚠️ untested x86_64-unknown-darwin

The Amp Programming Language 5 Mar 17, 2023