kvasir-io / kvasir Goto Github PK
View Code? Open in Web Editor NEWC++ Metaprogramming library enabling better static checking and register abstraction in embedded software
License: Apache License 2.0
C++ Metaprogramming library enabling better static checking and register abstraction in embedded software
License: Apache License 2.0
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.
maskFromRange(31,0) should turn into 0xFFFFFFFF but gives error because I used an int rather than unsigned, essentially everything that is a mask or an address should be unsigned in all of Kvasir::Register.
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:
...
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 have a somewhat working TC16B0 implemented, we need to implement the rest of the functionality in it and also implement the rest of the timers on the chip, at least theoretically for now.
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).
0x400@0x48000014
constexpr auto s = set(Kvasir::GpiobOdr::odr8),set(Kvasir::GpioaOdr::odr8),set(Kvasir::GpioaOdr::odr10));
apply(s);
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));
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.
right now we divide into sequence points and then merge so register are only merges when they are from the same initialization list and have no sequence points between them. It would be more efficient to combine the first sequence of one module with the first sequence of all other modules and so on.
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.
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
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.
We have decided to use this issue tracker, however in the wicki and maybe in some files there are still remnants of the old system, we should fix that.
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)))) ) {}
}
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.
Design goals for the USB abstraction were
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).
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.
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.
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.
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.
the order of the arguements in MPL::Find seems odd, we should discuss it
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:
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.
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.
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?
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.
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.
I did not implement sequence point conscious merging into PowerClockInitialization, the code does work but if a chip has multi step powerClockEnable lists the generated code is very inefficient.
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.
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?
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));.
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.
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'
this is long overdue and pretty trivial. We should however make it c++11 compatible as opposed to hana
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
If someone tries to read an not existent alias the code still compiles.
An errormessage would help to find this kind of bugs.
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.
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)>>{});
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.
MPL::Find returns an index or Int<-1> if not found, MPL::At can use this index to get the actual type do we need another function which does both in one step? What should it return in the case of not found?
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
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.
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!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.