Git Product home page Git Product logo

crect's Introduction

crect: A C++, compile-time, reactive RTOS

crect (pronounced correct) is a C++ library for generating a scheduler (at compile time) for Cortex-M series MCUs, which guarantees dead-lock free and data-race free execution. It utilizes the Nested Vector Interrupt Controller (NVIC) in Cortex-M processors to implement a Stack Resource Policy (SRP) based scheduler. Thanks to the compile time creation of the scheduler, the resource requirements at run-time are minimal with:

Initialization:

  • 4-5 instructions / job for initialization of the NVIC.
  • 2-3 instructions / queue element for initializing the async queue.
  • The static requirement for async is about 400 bytes (the linked list, SysTick and time implementation).

Compile time:

  • Uses the Kvasir MPL library for metaprogramming.

Runtime:

  • 3-4 instructions + 4 bytes of stack for a lock.
  • 1-3 instructions for an (implicit) unlock.
  • claim has zero overhead, it decomposes into a lock.
  • 5 instructions for a unique_lock (will change).
  • 2 instructions for a unique_unlock (will change).
  • 2-4 instructions for pend / clear.
  • About 20-30 instructions * number of items in queue for async.

In this scheduler, heavy use of C++ metaprogramming and C++14 allows, among other things, priority ceilings and interrupt masks to be automatically calculated at compile time, while resource locks are handled through RAII and resource access is handled via a monitor pattern. This minimizes user error without the need for an external extra compile step, as is currently being investigated in the RTFM-core language.

Videos / tutorials

YouTube video from embo++ 2018 describing the inner workings of crect: https://www.youtube.com/watch?v=SBij9W9GfBw

Give it a test

In the example folder a few example projects are setup for the NUCLEO-F411RE board. For example:

  • A program that will blink a LED every one seconds using crect primitives.
  • An example for how to use unique_lock with a data pumping peripheral (for example DMA or a communication interface).

It also contains examples of crect_system_config.hpp and crect_user_config.hpp, providing references until a documentation is available.

If there are any questions on the usage, throw me a message.

Tested on Ubuntu Linux using GCC 6.3.1 (arm-none-eabi) and a NUCLEO-F411RE for hardware testing. It is currently not working on Cortex-M0 devices.

License

Boost Software License - Version 1.0

Contributors

List of contributors in alphabetical order:

References

crect is based on the following academic papers:

  • T.P. Baker, "A Stack-Based Resource Allocation Policy for Realtime Processes", (general theory of SRP)
  • Johan Eriksson et. al., "Real-Time For the Masses, Step 1: Programming API and Static Priority SRP Kernel Primitives", (SRP primitives)
  • Per Lindgren et. al., "Abstract Timers and their Implementation onto the ARM Cortex-M family of MCUs", (async idea)

Definitions

Job:

  • A function that runs to completion in a finite time (no forever loop), not as a "normal" thread that has a forever loop.
  • Has a settable priority which indicates the urgency of the Job.

Resource:

  • An entity symbolizing something lockable, i.e. any locked Resource may only be accessed by a single Job at a time.

Lock:

  • A lock on a resource keeps other jobs, that will also take said resource, from running through manipulation of the systems NVIC/basepri settings. A lock can only be held within a job and must be released before the exit of a job.

Usage

Small description on how to use this.

Resource definition

A resource definition is as follows:

using Rled = crect::make_resource<
    CRECT_OBJECT_LINK(led_resource)  // Link to some object to be protected
  >;

Currently 2 system resources exists:

  1. The access to the async_queue is protected via crect::Rasync.
  2. For getting the current time via crect::clock::system::now() is protected via crect::Rsystem_clock - see claim for example usage.

Any job using these resources need to have the corresponding resource in its resource claim in crect_user_config.hpp.

Job definition

A job definition consists of a few parts:

  1. The priority of the Job, from 0 meaning low, to max_priority meaning max.
  2. An ISR the Job is connected to (peripheral ISRs, 0 is the lowest, negative numbers are the system ISRs). If it is not connected to any, take any random ISR number for now, in the future this will be automatic.
  3. The list of resources that the Job may claim.

The Job definitions are placed (directly or via include) in crect_user_config.hpp.

void job1(void);
using J1 = crect::job<
              1,                          // Priority (0 = low)
              crect::make_isr<job1, 1>,    // ISR connection and location
              R1, crect::Rasync            // List of possible resource claims
            >;

Each job need to be added to the user_job_list< Jobs... > in crect_user_config.hpp.

ISR definition

The ISR definitions available are split in the Peripheral ISRs (I >= 0), and System ISRs (I < 0).

// Peripheral ISR definition (I >= 0)
template <crect::details::isr_function_pointer<P, int I>
using make_isr = crect::details::isr<P, crect::details::index<I>>;

// System ISR definition (I < 0)
template <int I>
using make_system_isr = crect::details::isr<nullptr, crect::details::index<I>>;

lock

A lock keeps the system from running a job which will lock the same resource. The analysis to determine which job can take which resource is done at compile-time, which makes the lock very cheap to use as indicated at the start of this document. Lock should however be avoided by the user, use claim wherever possible.

// Lock the resource, remember locks are very cheap -- sprinkle them everywhere!
crect::lock< R1 > lock; // Locks are made in the constructor of the lock
// ...
// Unlock is automatic in the destructor of lock

There is no unlock, this is by design.

claim

Even with lock, it is easy to leak a resource, and to minimize this chance claim uses a Monitor Pattern to guard the resource. Hence the resource is only available within the lambda of claim:

// Access the LED resource through the claim following a monitor pattern (just as cheap as a lock)
crect::claim<Rled>([](auto &led){
  led.enable();
});

To guarantee the lock for resources with a return works just as good with claim (for example when getting the current time, as the system time is a shared resource):

// Resource is handled within the claim, no risk of a data-race.
auto current_time = crect::claim<crect::Rsystem_clock>([](auto &now){
  return now(); // now is a function reference
});

For a full example please see ./examples/blinky.

unique_lock

Add a unique resource and its corresponding lock is to support lock over job boundaries. This allows for producer/consumer patterns as the unique job can work by reading a queue of actions. Ex. a DMA, where jobs push to a "DMA transfer queue", and the job which has the unique DMA resource will read this queue and perform the desired transactions.

A unique_lock effectively decomposes into disabling the interrupt vector of the job, while the unique_unlock re-enables the interrupt vector.

For a full example please see ./examples/unique.

There is no unlock, this is by design.

pend / clear

pend directly sets a job for execution and will be as soon as its priority is the highest, while clear removes the job for execution.

// Compile time constant pend/clear
crect::pend<JobToPend>();
crect::clear<JobToPend>();

// Runtime dependent pend/clear
crect::pend(JobToPend_ISR_ID);
crect::clear(JobToPend_ISR_ID);

async

Async defers a jobs execution to some specific time.

// Using chrono to relate the system to time, the current max time is somewhere around 1500 years, depending on MCU :)
using namespace std::chrono_literals;

// Async in some specific duration using chrono
crect::async<JobToPend>(100ms);

// Async in some specific time using a specific time
auto time_to_execute = some_duration + crect::claim<crect::Rsystem_clock>([](auto &now){
  return now();
});
crect::async<JobToPend>(time_to_execute);

// Async can be used as pend for runtime dependent execution
crect::async(100ms, JobToPend_ISR_ID);
crect::async(time_to_execute, JobToPend_ISR_ID);

crect's People

Contributors

cvrxx avatar korken89 avatar odinthenerd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

crect's Issues

Add a debug mode

Add a debug mode for lock / claim / unique_lock, use IPSR to check that the ISR number is allowed to take the resource.

Add fix for Cortex-M7 Errata

According to the ARM errata for Cortex-M7, writes to BASEPRI does not take effect immediately, but one instruction later, and must be wrapped in an CPSID/CPSIE critical section.

However, only using an CPSID/CPSIE critical section is insufficient as the lock might already be inside a critical section and proper read and writes to PRIMASK must be performed instead. This will cause about 2x increase in number of instructions for a lock on an Cortex-M7...

Thanks ARM! -irony-

Cortex-M0 support

Cortex-M0 support, does not have basepri - will have to use interrupt masking.

Add more tutorials

  • Programming examples
  • How to design a system based on crect
  • Maybe YouTube tutorials?

Compatibility / Interop with other RTOS

I am very excited to try SRP-style scheduling for more of my projects.

The majority of applications I work on involve some form of mainstream wireless communication (bluetooth, wifi, etc). And, normally the IC mfr. provides some software integrating with a popular RTOS. For example, nrf52 Bluetooth using the Nordic Softdevice, or NimBLE stack running on Apache MyNewt/Zephyr RTOS. Preemptive scheduling or cooperative scheduling is typically used, and in my experience applications integrate + use the same RTOS.

My question is: can crect be used alongside these RTOSes? Does one scheduler fit within another (e.g. crect acts as a blackbox/task within the preemptive RTOS? (or, vice versa, perhaps the RTOS is treated as a blackbox/resource within crect?)

Any tips/suggestions/ideas are welcome (especially if it is a straightforward “no, you cannot do this”)

Add a way to have resources have state, store within crect

If resources can be considered to "live" inside crect, then there will not be need for any global structures, as is done in the blinky example right now.
More or less, allocate all resources within crect and, generally, do not have resources take references but the type.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.