Giter Site home page Giter Site logo

kvasir-io / kvasir Goto Github PK

View Code? Open in Web Editor NEW
408.0 46.0 40.0 18.82 MB

C++ Metaprogramming library enabling better static checking and register abstraction in embedded software

License: Apache License 2.0

C++ 100.00% Python 0.01% CMake 0.01% C 0.01%

kvasir's Introduction

What is Kvasir?

Build Status Join the chat at https://gitter.im/kvasir-io/Kvasir

Kvasir is a collection of modern C++ microcontroller libraries which share a common vision and work well together. Kind of like boost but for a different domain. The most developed library is the Kvasir::Register library which enables full static checking ans simple SFR abstraction in embedded software. Despite the fact that we use modern C++ tools under the hood the public interface is quite "C like" and familiar to most embedded developers.

Why Kvasir?

Efficiency

By fully abstracting the hardware Kvasir is able to use optimizations under the hood which are either too obscure or too ugly to appear in user code. The header only nature of Kvasir and very sophisticated use of the volatile keyword give the optimizer more room to do its thing without breaking anything. We often reach severalfold improvements over hand written C code!

Security

As mentioned above Kvasir register interactions are fully statically checked. It is essentially impossible to write to reserved bits, overflow the destination bit field, use a wrong mask or suffer from off by one errors when shifting. There are also a lot of atomic and synchronization features being developed in order to handle shared data much more efficiently.

Portability

Kvasir works on essentially every ARM Cortex (we generate the chip specific code from the vendor provided CMSIS-SWD files). Although different chips will still have different peripherals because of Kvasirs level of abstraction it is still far easier and safer when porting.

Testability

Kvasir introduces a seam right above the bare metal in a zero cost manner using a traits class. This means unit test of embedded code which interacts directly with registers is finally possible without big tools.

Getting started with Kvasir

We took great care to reduce dependencies, therefore Kvasir should be easy to add to an existing embedded project. To use the library simply pull the folder Lib from this repository and add it to your compilers include paths (-I directive). Be sure and set your compiler dialect to C++11 at least. It is also recommended to set optimization to -Og rather than -O0 in order to get smaller yet debugable binaries. Thats it, you're ready to go. -I<PathToKvasir>/Lib/ -Og -std=c++11

Basic concepts

In Kvasir we view registers as tuples of BitFields, which are accessible to the user, and reserved bits which are not. The type of a BitField is not always int, as is common practice in the embedded domain, rather it is often a strongly classed enum where it is more fitting. The bool type also often represents single bits.

We also make a distinction between compile time known values and runtime known values. For example 10 is a runtime known value, when passed to a function it must be copied at runtime and when saved in a variable it requires RAM. In order to save work at runtime and RAM we can also work with compile time values which only live in the compiler, these can be passed to functions without needing to be copied at runtime and do not use ram. Kvasir::Register::value<10>() is a compile time value where Kvasir::Register::value< >() is a wrapper around any unsigned int.

Reading from or writing to a Field is known as an Action. When we combine an Action and a Field plus a value in the case of write we call this a FieldAction. FieldActions are produced by factory functions such as read(), write(), set() etc. These factory functions do not actually perform the action, rather they return a FieldAction which can be passed to the apply() function which performs the action. We typically pass many FieldActions to apply at once. Putting off the action until the last minute and only returning what to do rather than actually doing it is called lazy evaluation. It is important to be lazy in this case because the later we actually do the work the more we know about the other work going on around us and the more we can merge these tasks and optimize them.

kvasir's People

Contributors

andi-d avatar gitter-badger avatar legalizeadulthood avatar maxclerkwell avatar odinthenerd avatar sickeroni 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kvasir's Issues

Shared inteerrupts on M0 are not supported

The current code will work if one of the modules which shares the interrupt use it. However if both register the interrupt the compiler will issue an error. We need a work around eventually.

Blinky demo does not compile

I use revision 729fa5c and the LPC1549 in the current lpcxpresso 8.0.
I get lots of compile errors (>80), e.g.
Kvasir/Lib/StartUp/StartUp.hpp:111:17: error: 'FlattenT' is not a member of 'Kvasir::MPL'

Implement clock initialization in KVASIR_STARTUP

We need to implement a clock initialization step before we initialize other modules. should be realized by treating the first parameter passed to the KVASIR_STARTUP macro as a functor and executing it first. We can build a factory allowing the user to create such a functor from desired parameters.

Usb documentation needed

Design goals for the USB abstraction were

  • complete static binding
  • fully configurable (to the point of micromanaging memory strategy)
  • unit tested for everything upwards from the HAL

Although I think these can be/have been met the interfaces between the HAL and the USB::Device as well as the interface between the USB::Device and the device classes could not be implemented as cleanly as I had hoped.
We need to do a good job of documenting the contracts between the Usb::Device and the Hal as well as the contracts between the Usb::Device and the interface class(es).

Should Register::Option become Register::Action with more functionality?

Right now we have a register option which consists of an address with a set and clear mask. In addition there is a bit of a hack which allows write only registers to be used like the bit toggle register in GPIO of the M0 core. It does not seem that this is powerful enough, we cannot easily detect clashes between writes for example and we need a better way of handling write only registers or registers where read and write are separate functions. We also need to allow the optimization of writing directly to a register if all the bits are changed by the option(s) after merging. Also we need to provide enough info for the init routine to load defaults to fill unused bits rather than reading, this should save a lot of code space.

Redesign of Device initialization needed in order to power on and provide clock to all peripheral devices used

the power on and clock configuration steps are the only unique thing in the initialization lists of most modules, it is also often the only reason one may need a sequence point. I think default behavior should be to turn on all Peripheral devices and configure them to use the fastest clock possible for every device mentioned in the KVASIR_START macro. If the user wants to save power they should have some way of overriding this and can surely reconfigure the initialization list if desired.
A work around could be to just turn everything on and then refine the process under the hood from there.

Kvasir licencing and copyright

I'm writing the contribution file for Kvasir at the moment and I'm doubting how to handle copyright.

The whole Kvasir name space will be licenced under the apache. @odinthenerd and I were thinking on putting all the contributes in the readme for each repo in de the order of there first commit. This would work except for the copyright. Who is going to have copyright in the license of each file? Do we refer to the readme?

Would love your feedback!

@odinthenerd @Sickeroni @chieltbest

documentation generation

I know there are automatic c++ documentation generators with github integration. We need to decide which one to use and start using it to generate documentation.

Ignore of Mask in Register::Action

Mask is ignored on writeactions. Means wrong bits could be set.
tested only with Register/Seam.hpp, not on real hardwarecompile

testcode:
apply(Kvasir::Register::Action<Kvasir::Register::FieldLocation<Kvasir::Register::Address<0x13371337,0x00000000>,(0b01<<30)>,Kvasir::Register::WriteLiteralAction<(0b11<<30)>>{});
apply(Kvasir::Register::Action<Kvasir::Register::FieldLocation<Kvasir::Register::Address<0x13381338,0x00000000>,(0b11<<30)>,Kvasir::Register::WriteLiteralAction<(0b11<<30)>>{});

Kvasir::Register::apply should be refactored to allow for smaller debug builds

at least in the case where everythign is compile time known we should use recursion to walk the list of register actions rather than flat expansion. The advantage is we can force inline in debug builds and also give the user a better "step through experience". The release codegen should be identical according to my gut feeling.

no type named 'DataType'

Hi,
while including "Chip/nrf52.hpp", I get following compiler message:

/Users/todi/Kvasir/Lib/Chip/CM4/Nordic/nrf52/RADIO.hpp:207:89: error: no type named 'DataType' in 'struct Kvasir::Register::FieldValue<Kvasir::Register::FieldLocation<Kvasir::Register::Address<1073746692u, 4294953728u, 0u, unsigned int>, 16u, Kvasir::Register::Access<(Kvasir::Register::AccessType)2>, Kvasir::RadioIntenset::DisabledVal>, (Kvasir::RadioIntenset::DataType)0>' constexpr Register::FieldValue<decltype(disabled)::Type,DisabledVal::enabled> enabled{};

Any idea, how to fix this?

TIA,
Torsten

Harmonize Nvic::Index with regard to pointer array offset

It seems most Cortex use ISR indexes which start at 0, the LPC175x6x however essentially starts at -16, we need to harmonize this so that less code needs to be chip specific, maybe just add 16 to the LPC17xx indexes and leave a comment? I'm not sure many users will actually look at the index numbers we are using if a plain text name is available.

Cleanup in ChipFromSvdGenerator

I'm not the most Pythonic Pythonista who ever Pythoned but I have a few ideas for cleaning up the chip file generator.

It may make sense to use EmPy templates instead of concatenating formatted strings representing C++ code. It may make the process a bit more readable, since you could have a template file that looks a bit more like the resulting C++ code with placeholders that get filled in from the parsed xml. I have some experience with EmPy, so I could do this if others think it would be useful.

I couldn't find tests in place for results of the generator; if the chip generator gets rewritten it would be good to have those in place beforehand, to make sure the rewrite preserves functionality. Are there IP issues with adding example CMSIS SVD files to this repo for testing?

EDIT: just realized that the SVD files are in https://github.com/posborne/cmsis-svd/tree/master/data, cool. For now I can test any refactoring I do on my own by diffing against the state of the header files in master.

many chip headers not compiling

The example header Chip/MKL27Z4.hpp compiles OK. But most of the others I tried do not, producing many errors. This was using gcc-arm-none-eabi-5_3-2016q1 from the GCC ARM Embedded project.

The errors vary with the chip but for example
main.cpp:

include "Chip/STM32F30x.hpp"

...

arm-none-eabi-g++ -o build/example-main.o -c -Og -mcpu=cortex-m4 -mthumb -std=gnu++11 -Ibuild/.:./lib -I.:./lib -I/home/jd/output/Kvasir/Lib main.cpp
In file included from /home/jd/output/Kvasir/Lib/Chip/STM32F30x.hpp:2:0,
from main.cpp:2:
/home/jd/output/Kvasir/Lib/Chip/Unknown/STMicro/STM32F30x/GPIOA.hpp:8:15: error: too many decimal points in number
0..15)
^
/home/jd/output/Kvasir/Lib/Chip/Unknown/STMicro/STM32F30x/GPIOA.hpp:11:15: error: too many decimal points in number
0..15)
^
(continues for many screenfuls)

We need to change timer match registers to use tags

Currently we handle the 4 match registers separately on the Lpc11u6x and LPC175x6x but the LPC15xx hat 16 match registers, this is too many to handle separately, we need a better way, the user should also be able to changes match registers dynamically from the hardware.h

Using tags should solve this eg. MatchRegister::makeSetValue<1000>(match0) or static void onMatch(Match0){ ... }; and we are already using this approach with ADC channels.

Indexing in multiple read apply does not work.

If i try to compile the following code the ADL fails.

#include <Chip/nrf52.hpp>
#include <Io/Io.hpp>

constexpr auto button1 = Kvasir::Io::makePinLocation(Kvasir::Io::port0, Kvasir::Io::pin13);
constexpr auto button2 = Kvasir::Io::makePinLocation(Kvasir::Io::port0, Kvasir::Io::pin14);

int main()
{


                        if (get<0>(apply(read(button1), (read(button2))))  ) {}
}

Should we change our read abstractions to reguard bit banding?

So far we have left bit banding out. In the case where a register action only effects one bit and is not merged with anything a write using bit banding would surely be better than a read-mask-or-write like we are doing now. Implementing that optimization can be done after the fact with the same interface.

With regard to single bit reads however there is an interface decision to be made. If the user reads a register and is interested in only a single bit then bit banding is surely more efficient. However if the user reads a register using a pod conversion policy the returned POD value is not marked volatile, thus the optimizer has much more room to operate especially when the user tests multiple bits.

Our objective is clear, optimize for the common case, however I can't say definitively what the common case is. It probably depends on the register in question, however a piecemeal solution would be less logical to the end user.

Apply should take tuples of runtime write commands

in some cases we need to build up the contents of a register in a tree of ifs and then flush it all at the same time. This would be much easier and more efficient if we could make a tuple of writes, work on it and then flush it to the register.

Make NVIC actions work like current IO actions

I am very happy with the current IO action factory structure, eg. apply(makeOutput(myPin)); I think we should use the same architecture for NVIC configuration except with Nvic::Index rather than Io::PinLocation eg. apply(makeEnable(Nvic::uart0));.

apply, write etc missing on STM32F072x

apply, write set etc don't work.
works on MKL27Z4 trough generated "Chip/MKL27Z4.hpp" thats include generated "Io.hpp" in that includes "Register/Register.hpp". Io.hpp is missing on STM32F072x and therefore Registers.hpp.

helperscript must be modified for this.

We need a fieldEquals([FieldValue]) function

in code we find a common pattern:
if(apply(read([fieldValue])) == [fieldValue]){}

where [fieldValue] is usually long.

we could save a serious amount of typeing with a fieldEquals([fieldValue]) function.

We may need a multi step action

In some cases we have multiple actions which need to be executed in a certain order with regard to each other but not with regard to other actions. Using a sequence point would be over kill here because we don't care about unrelated actions being moved across in order to merge or save address loads. A single action should be mergable with any action step with the same address, another multi step action should be mergable if all steps in the smaller list have common addresses with a sequence in the larger list.
Before writing I would like to compile a list of places that this would come in handy. Writing PowerClockInit routines for the LPC15xx is a good example, different modules need to access power clock and reset registers in a distinct order regarding other actions from the same peripheral, however with regard to other peripherals the order is not important. If one peripheral is power on by default but does need to enable clock or reset it self then we must remember to leave an extra sequence point in or generate less efficient code. Any other places this is needed?

Compilation error in Register/Utility.hpp

My compiler (gcc 5.1) reports the following error:

Lib/Register/Utility.hpp:28:23:  in constexpr expansion of 'Kvasir::Register::maskFromRange(high, low)'
Lib/Register/Utility.hpp:24:22:  error: right operand of shift expression '(4294967295u >> 32)' is >= than the precision of the left operand
   return (0xFFFFFFFF >> (32-(high-low)))<<low;
                      ^

This then causes a cascade of error messages. I fixed this by changing the constant to 0xFFFFFFFFLLU

Register::apply needs to be refactored to allow for locking policies

We need to expand the apply steps so we can support locking policies and decide if we need to lock at the largest granularity possible. I would expect the following steps in apply are needed:

  • index input
  • flatten input
  • determine return object type
  • determine atomic policy
  • split by sequence point
  • sort by address
  • path1 (taken if there are any atomics)
    • merge(each address will be merged unless atomic behavior is only possible with bit banding)
    • BME or bit banding as an optimization will be taken into account
    • check if atomic policy is possible
  • path 2 (taken if there are no atomics or if intrinsic atomics are not possible)
    • merge all addressed
    • make chip specific substitutions only if size or speed if positively affected
    • enable fall back locking policy
  • lock if needed
  • forward arguments to next helper (we need to expand our list of merged actions)
    • expand new action list
    • determine input parameters
    • forward input args to single actions
      • lock if needed
      • execute single action
      • compile returns
      • unlock if needed

We should refactor kvasir to use an external metaprogramming library

Back in the beginning of 2013 when I started working on Kvasir I was not able to find a good MPL which did not rely on the STL but did make use of variadic templates, so I wrote my own. Now almost 3 years later metaprogramming has greatly advanced especially in terms of finding ungodly performance tricks.
Couppled with the fact that I did not document my MPL well/at all it is high time it get swapped out with a new/faster/documented one.

Should ChipFromSvdGenerator get his own repo.

Because of the fact that the CFSG is a tool and not part of the kvasir registery abstraction I think it should have its own repo. In this way we could also version it as a standalone tool. Would also enable other users to use it for different purposes.

Should we start with a more release based workflow.

If we want to make kvasir usable for production code we should be more save. With more safe I mean using a git policy that allows us to indicate when a version is stable and ready for use.

We could implement this by using git flow. Als we should do code reviews before any master merge. Main work should be done off the master branch.

I think we have to discuss this and write a git and test plan for this repo and the whole kvasir idea. What do you think?

wrong apply in seam with sets in list-constexpr

if saving list in constexpr & using more than one set() than seamwrite is only showing one set();

testcases:

fails with

0x400@0x48000014
constexpr auto s = set(Kvasir::GpiobOdr::odr8),set(Kvasir::GpioaOdr::odr8),set(Kvasir::GpioaOdr::odr10));
apply(s);

works with

0x100@48000414
0x500@48000014

constexpr auto s = (set(Kvasir::GpiobOdr::odr8, Kvasir::GpioaOdr::odr8, Kvasir::GpioaOdr::odr10));
apply(s);

apply(set(Kvasir::GpiobOdr::odr8,Kvasir::GpioaOdr::odr8, Kvasir::GpioaOdr::odr10));

apply(set(Kvasir::GpiobOdr::odr8),set(Kvasir::GpioaOdr::odr8),set(Kvasir::GpioaOdr::odr10));

Should we rename namespaces to lowercase?

In the interest of breaking things now rather than later it seems people don't like my style ;) basically types being UpperCamelCase and values and functions being lowerCamelCase is ok by most people but that fact that namespaces are also upper camel case bugs a lot of people. So we should probably change it.

The one breach in this style is the nested type in Metafunction::type. The problem is that TMP convention is to use lower case type but it is a type so according to this style guide it should be upper case. I think we can get away with this breach in our style guide and use lower case though because the user should never see this, its just library internals.

TMP refactoring in Kvasir

There are still a good number of things in Kvasir which are not in line with modern TMP style. Especially offending are the uppercase nested Types (rather than lowercase) and the idiom of returning by inheritance. This is actually starting to infect things outside of core like MakeAction in Io and should be refactored soon.

Things could also run much faster if we made many more type empty inside and relied on pattern matching to work on them (instantiating is less costly for the compiler if the struct is truly empty).

How to deal with Dont-Touch Registers?

While writing Chipfile of UM10736 I found register 0x4007 4124
bits 11:0 and 31:14 have to be written back as read.
what does an action like this look like? should it be available at all to touch them? how can we secure that they are never touched?

->lpc15xx manual
->chapter 3.6.32
->syscon::flashcfg

havealookj

RegisterAction should be renamed FieldAction

looking at the continuity of our naming conventions a Register is essentially a Tuple of (bit)Fields. Since a RegisterAction acts on a Field rather than on the whole register (usually reads and writes the whole register but only because of hardware constraints and not in the case of of BME ore bit banding) it should be named FieldAction.

Kvasir::Register types should be refactored to be empty

In order to have lightning speed we need to refactor types like address to have an empty body. Things like nested type and values are not needed as they can be found through pattern matching. Plus this would mitigate my original sin of naming the nested types uppercase 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.