Ratel

Zero-cost abstractions for better next generation embedded programming

Get started!

What is Ratel?

Abstraction is the goal of any programming language or library. Take something complex, and make it more accessible. Instead of hand-writing CPU instructions we use programming languages, and instead of manually performing tasks in said languages we use libraries that hides the nitty-gritty details from us. However abstractions often come at a cost, every transformation might introduce overhead, and every data structure might waste bytes of data. This might not be such a big issue on modern computers, the speed of development vs. the speed of execution is a balance that is often tipped towards development speed. But on microcontrollers these kinds of abstractions can quickly chew up a considerable amount of program size and execution speed. The goal of Ratel is to provide zero-cost abstractions which allows you to write simple code that reads easily, but which compiles down to become as small as hand-written C code.

import board
import board / [times, serial, progmem]

Serial.init(9600.Hz)
Serial.send p"Hello world\n"
Led.output()

while true:
  Led.high()
  Serial.send p"Led is on\n"
  delayMs(1000)
  Led.low()
  Serial.send p"Led is off\n"
  delayMs(1000)
An example of Ratel code

The above code probably looks familiar if you've ever written code in Arduino. What won't be familiar is the size of the program that is uploaded to your microcontroller. A similar program implemented across many popular frameworks - all compiled to be size-efficient - shows us that while all the code looks fairly similar the frameworks have quite different costs of abstraction. The following graph shows the total program and data memory of the above program (and similar implementations in other languages) compiled for the Arduino Uno. Keep in mind that the program memory for the microcontroller on this board is 32Kb and the data memory is a mere 2Kb.

Comparisson
Comparison of test program across various frameworks

How does Ratel achieve this?

The secret to generate such tiny code is simple, let the computer compiling do the work. Ratel leverages the Nim programming language and its powerful meta-programming and compile-time execution powers. This also means that not only does it generate tiny programs, but it also stays at a high level of abstraction. By using compile-time objects and distinct types we have a much stricter type system than just writing pure C code. An example of this is how program memory is handled, you might have missed it but in the above example the strings are put in program memory and selecting the correct serial send procedure is done by an override. This gives Ratel a lot of flexibility while still staying much safer than code which would hand pointers to data around.