Intuitive language for PRU which compiles down to PRU C.
Explore the docs »
Report Bug
·
Tutorials
.
Request Feature
·
Send a Pull Request
- Table of contents
- Brief overview of PRU-ICSS
- What was the need for simpPRU ?
- How does simpPRU simplify things ?
- Implementation details of simpPRU
- Usage
- Language Reference
- Examples
- Building from source
- Building using Docker Image
- Installation from debian package
- Contributing
- License
- Acknowledgements
- Known Issues
- Notes
- Resources
The PRU is a dual core micro-controller system present on the AM335x SoC which powers the BeagleBone. It is meant to be used for high speed jitter free IO control. Being independent from the linux scheduler and having direct access to the IO pins of the BeagleBone Black, the PRU is ideal for offloading IO intensive tasks.
It is a low-latency micro-controller subsystem, which provides real-time processing capability lacking in Linux. PRUs are part of the PRU-ICSS, “Industrial Communications Subsystem”. They are free from running on an operating system, and thus can be dedicated to a single function.
The PRU cores were created to be non-pipelined, single-cycle execution engines. This means that all instructions (except for memory reads and writes) complete in a single cycle. Since there is no pipeline, the PRU cores are completely deterministic: the PRU cores will not rearrange instructions during execution.
The PRU-ICSS can also read from or write to external pins in a single cycle using a PRU core register (R30/R31). With a PRU core running at 200 MHz, this means that an external signal change can be detected in as little as 5 nanoseconds. This also means that the PRU can toggle external pins with as fast as 5 nanoseconds.
The PRU-ICSS also has an interrupt controller, it does not support asynchronous interrupts as they introduce jitter in execution time and reduce determinism. Instead it supports an efficient polling system.
Programming the PRU is a uphill task for a beginner, since it involves several steps, writing the firmware for the PRU, writing a loader program. This can be a easy task for a experienced developer, but intimidating for a beginner.
This problem was solved earlier by pruspeaks project, but it inflicts serious restrictions on PRU, as PRU0 runs a bot speaks interpreter and PRU1 takes care of generating PWM signals. Already PRU has meagre resources, so emulating a VM on top of PRU leaves very less room to use PRU for what it is meant, deterministic control and fast read writes.
simpPRU simplifies this process to a great extent. Since it has a syntax quite similar to python, it is easier to code in simpPRU. It emits code in C, which is then compiled into binary using pru-gcc. So, it is as good as writing code in PRU C in terms of efficiency and functionality and far less in complexity.
Since, loading the firmware using sysfs class is a challenging and time consuming task, simppru helps in doing that by loading the firmware into /lib/firmware. Since, using sysfs classes to use RPROC and RPMSG is challenging, simppru-console takes care of it, and provides a intuitive and usable GUI to start/stop PRU and read/write to PRU using remoteproc.
simpPRU is a compiler which uses flex to generate lexical analyser, and bison to generate a parser. It uses custom code printer written in C. It emits code in PRU C.
A compiler has 3 main components:
- Lexical analyser
- Parser
- Code printer
This is the simplest step. Given our grammar, we need to break down our input into a list of known tokens. As mentioned before, our grammar has tokens, namely, identifiers, numbers (integers and floats), the mathematical operators, parentheses, braces, keywords, etc.
This step basically reads from the input files, and generates tokens according to the regex exp specified in the flex file. The tokens are defined in lexer.l, and the actions that need to be taken after detecting a token is specified in this file itself. For example, if it finds, "int" in the input file, lexer will return a token say KW_INT to the parser.
This is the most challenging part. We need to parse the list of tokens that we received from lexical analyzer, and evaluate these using language grammar to make sense of the tokens. The language grammar is defined in a bison file named parser.y.
We use a GLR Parser, all tokens are stored in the symbol table, which is a hash table which stores values of the variables, with the variable name as the key, symbol table is defined in symbol_table.c.
After this step, we do semantic parsing whose result is an abstract syntax tree, AST is a tree data structure which stores various token as it nodes, such that it can represent the code in an abstract way in memory. AST is what represents our language in memory, AST related functions are defined in ast.c.
The next step in a compiler is to naturally take this AST and turn it into machine code. This means converting each semantic node into the equivalent machine instruction or equivalent C code in this case.
For this step, we traverse the AST in a breadth first depth first approach, and print the equivalent C code in a output file. In this step it also keeps track of the GPO/GPI pins used, and sets them to appropriate mode using config-pin, relevant functions are defined in pin_config.c.
It prints wrapper functions in the generated code, like simple functions to digital read, digital write, send rpmsg, start/stop counter, delay, read counter, etc.
This code printer is defined in code_printer.c
Please refer to this simppru usage instruction page on readthedocs
Please refer to this simppru-console usage instruction page on readthedocs
Please refer to this language reference page on readthedocs
For example codes, please check example page on readthedocs
- flex
- bison
- gcc
- gcc-pru
- gnuprumcu
- cmake
git clone https://github.com/VedantParanjape/simpPRU.git
cd simpPRU
mkdir build
cd build
cmake ..
make
sudo make install
sudo make package
- amd64 Image: https://hub.docker.com/repository/docker/simppru/amd64-build-image
- arm32 Image: https://hub.docker.com/repository/docker/simppru/arm32-build-image
These images have all dependencies installed, just run the commands described in build, inside the docker image and then run sudo make package
to generate a debian package.
Download the latest debian package from: https://github.com/VedantParanjape/simpPRU/releases/latest
Download amd64 package for x86_64 systems and armhf package for arm32v7 systems
sudo dpkg -i simppru-<version_number>-amd64.deb
replace <version_number>
with the appropriate version of the downloaded package
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
Distributed under the MIT License. See LICENSE
for more information.
- Abhishek Kumar for being an amazing mentor
- Pratim Ugale for being an amazing mentor and helpful senior
- Arthur Sonzogni for helping me out with FTXUI
- Dimitar Dimitrov for helping me out with pru-gcc
- Control statements (break/continue) can be called inside any compound statement, so calling it inside if loop will throw a error while compiling the generated C code.
- Return statements can only be called at the end of the function, i.e, it must be the last statement in the function declaration, other wise it won't register a return statement and either throw a error or exhibit undefined behaviour.
When we pass -mmcu=am335x.pru0
to pru-gcc compiler, a macro is defined by the compiler which can be used in the code. Macro is __AM335X_PRU0__
. It is set to 1
if pru0
option is passed, and it is undefined if pru1
option is used. It can be used as follows:
#ifdef __AM335X_PRU0__
#define PRU_NUM 0
#else
#define PRU_NUM 1
#endif
Refer: dinuxbg/gnupru#36
- https://github.com/drifter1/compiler
- http://web.cs.ucla.edu/classes/spring08/cs259/llvm-2.2/docs/WritingAnLLVMBackend.html
- https://babel.ls.fi.upm.es/~pablo/Papers/Toys/C-compiler/intro.html
- https://github.com/anthony-y/toy-compiler
- https://github.com/camilne/toy-compiler
- https://github.com/ehostunreach/toy
- https://aquamentus.com/flex_bison.html
- https://www.youtube.com/playlist?list=PLIrl0f9NJZy4oOOAVPU6MyRdFjJFGtceu
- https://www.jonathanbeard.io/tutorials/FlexBisonC++
- https://gnuu.org/2009/09/18/writing-your-own-toy-compiler/
- https://www.youtube.com/playlist?list=PLkB3phqR3X43IRqPT0t1iBfmT5bvn198Z
- https://medium.com/@ilyarudyak/flex-tutorial-9ed34fd1ff28
- https://www.youtube.com/playlist?list=PLEbnTDJUr_IcPtUXFy2b1sGRPsLFMghhS
- https://www.youtube.com/playlist?list=PLEbnTDJUr_IcPtUXFy2b1sGRPsLFMghhS
- https://ruslanspivak.com/lsbasi-part7/
- https://medium.com/basecs/leveling-up-ones-parsing-game-with-asts-d7a6fc2400ff
- https://github.com/attractivechaos/klib
- https://github.com/rxi/vec