Powering Real-Time Decisions with Continuous Data Streams
#OSSPARIS19 : A virtual machine approach for microcontroller programming : the OMicroB project -
1. A Generic Virtual Machine Approach for Programming
Microcontrollers : the OMicroB Project
Steven Varoumas 1,2,3 Basile Pesin 1
Benoît Vaugon 4 Emmanuel Chailloux 1,2
1
Sorbonne Université
2
Laboratoire d’Informatique de Paris 6 (LIP6) - Sorbonne Université
3
Centre d’Étude et De Recherche en Informatique et Communications (CÉDRIC) - CNAM
4
Armadillo, 46 bis, rue de la République, 92170 Vanves, France
IRILL, Sorbonne Université
10/12/2019
2. 1/19
Outline
1 Context
– Programming microcontrollers : the virtual machine approach
– Motivations of the OMicroB project
– The OCaml language
2 OMicroB : a generic OCaml VM designed for microcontrollers
– Compilation chain
– Optimizations
– Measurements
3 Extensions
- Hardware abstractions with functors
- Synchronous programming for critical softwares
4 Conclusion
3. 2/19
Context
Microcontrollers
• Simplified computer (computation unit, memory, inputs/output peripherals)
• Typically programmed in C / assembly language
• Used in (sometimes critical) embedded systems (automotive, trains, . . .)
– Low cost
– Low energy use
Various specifications
• Various architectures (PIC, AVR, ARM, . . .)
• 8 bits, 16 bits, 32 bits architectures
• Wide range of CPU speeds : from 1MHz to 300MHz
• RAM (data) : from a few bytes upto 1MiB
• Flash (program) : from 4 KiB to a few MiB
4. 3/19
The VM approach to microcontrollers’ programming
Microcontrollers (µC) can be difficult to program
• C / assembly programming can be error-prone
• Offers little hardware abstraction
• Few checks at compile time
• Hard to debug
The Virtual Machine (VM) Approach
VM of a programming langage implemented for microcontrollers
• Allows running bytecode of higher-level languages directly on µC (easier, safer
programming)
• Offers a layer of abstraction over the hardware
• Increases the portability of code
• Can decrease the size of code
5. 4/19
The VM approach to microcontrollers’ programming : examples
High level languages on microcontrollers
• Picobit : Scheme on PIC 18
• MicroPython : Python on STM32 and ARM
• MicroEJ : Java on ARM and RX600
• OCaPIC : OCaml for PIC
– OCaml virtual machine implemented in PIC18 assembly.
– Allows the execution of the OCaml language on a PIC18.
– Comes with : simulators, library for external displays . . .
– OCamlClean : tool for removing dead code and useless memory allocations
But ...
Most of these VMs are specific to some hardware architecture(s)
6. 5/19
Motivations of the OMicroB project
What we wanted
• A modern, expressive, high level language (OCaml)
• Used for programming microcontrollers starting with ones with very limited
resources (a few kB of RAM)
• Portable accross various architectures and configurable
ATmega2560
8 bits
16 MHz
8 KiB of RAM
256 KiB of flash memory
ATmega32u4
8 bits
16 MHz
2.5 KiB of RAM
32 KiB of flash memory
7. 6/19
The OCaml language
Chosen language : OCaml (https://ocaml.org/)
• Developped by INRIA (Gallium team)
• Multi paradigms programming language (functional, imperative, object-oriented)
• High-level language with powerful constructions and expressiveness
(parametrized modules, pattern matching, lambdas, objects, exceptions . . .)
• Improved safety by static typing with type inference.
• Its Virtual Machine (the ZAM) is very lightweight (148 bytecode instructions)
and efficient
(* map : (’a -> ’b) -> ’a list -> ’b list *)
let rec map f l =
match l with
| [] -> []
| h::t -> (f h)::( map f t)
(* filter : (’a -> bool) -> ’a list -> ’a list *)
let rec filter f l =
match l with
| [] -> []
| h::t -> if (f h) then h::( filter f t) else (filter f t)
let main =
let l1 = [ 1 ; 2 ; 3 ; 4 ] in
let l2 = filter (fun x -> x mod 2 = 0) l1 in
map (fun x -> x + 1) l2
8. 7/19
OMicroB : Generic OCaml VM for microcontrollers
Our contribution : OMicroB
• OCaml on Microcontroller’s Boards
• OCaml virtual machine implemented in C
• Generic : execution of all of the OCaml language on all microcontrollers with a
C compiler (Arduino/AVR, PIC, Nucleo/ARM, . . .)
• Offers hardware abstraction : automatic memory management (garbage collector)
• Configurable (size of values, GC, . . .)
• Static analysis dedicated at optimising performances (size and speed)
• Factorizable analyses (over the bytecode and the C interpreter)
• Free software (CeCILL License)
10. 9/19
OMicroB : Example (ocamlc & ocamlclean)
(* int -> int *)
let rec facto n =
match n with
| 0 -> 1
| x -> facto (x -1) * x
let main =
Arduboy.init ();
Arduboy.print_int (facto 4);
Arduboy.display ()
OCaml bytecode :
0 BRANCH 12
1 ACC 0
2 BRANCHIFNOT 10
3 ACC 0
4 PUSHACC 1
5 OFFSETINT -1
6 PUSHOFFSETCLOSURE 0
7 APPLY 1
8 MULINT
9 RETURN 1
10 CONST 1
11 RETURN 1
12 CLOSUREREC 1 0 1 []
13 CONST 0
14 CCALL 1 0 (* ocaml_arduboy_init() *)
15 CONST 4
16 PUSHACC 1
17 APPLY 1
18 CCALL 1 1 (* ocaml_arduboy_print_int() *)
19 CONST 0
20 CCALL 1 2 (* ocaml_arduboy_display() *)
21 POP 1
22 STOP
11. 10/19
Bytecode embedding in a C file : bc2c
bc2c
The bc2c tool “transforms” the bytecode file into a C file by embeding the OCaml
program into various C variables :
• The bytecode instructions (array of “opcode_t”) in flash memory
• The OCaml stack (array of “value”)
• The OCaml heap (array of “value”)
• The OCaml global variables (array of “value”)
• An array of pointers to C primitives
• A global accumulator variable
N.B : The OCaml VM uses an uniform representation of values (of type “value”)
12. 11/19
bc2c : optimizations
bc2c performs several static analysis at compile time in order to optimize speed and
memory use :
Compacting bytecode
• Instructions encoded on 8 bits + specialized instructions for arguments < 4 bytes
⇒ Reduces size of code and read speed by a factor of almost 4
Tailor-made interpreter
• Only the code for the used bytecode instructions is compiled in the final program.
Partial evaluation of the program
• The OCaml program is executed at compile time upto the first I/O
• Dump of the state of the memory (heap & stack) into the C file
⇒ Quicker and lighter programs
14. 13/19
Interpreter and runtime
Intepreting bytecode
The interpreter reads the program (stored in flash) generated by bc2c and modify the
different variables (heap, stack, accumulator, . . .) depending on the instruction :
Example :
/* ... */
/* ‘ASSIGN n‘ assigns the nth level of the stack to the current acc value */
#ifdef OCAML_ASSIGN
case OCAML_ASSIGN : {
TRACE_INSTRUCTION ("ASSIGN");
sp[read_uint8 ()] = acc;
acc = Val_unit;
break;
}
#endif
/* ... */
The runtime
• Standard runtime library able of handling lists, arrays, floats, references, strings,
generic comparison, . . .
• Memory reuse with two Garbage Collection algorithms (Stop and Copy, Mark and
Compact)
• Possibilities to interface OCaml with C code (FFI)
15. 14/19
Simulation
Simulator
• A simulator runs the VM and displays the state of input/output pins of the
microcontroller
• As well as the interaction with various external components (matrix display,
buttons, led, . . .) by describing the circuit in a text file
• Various levels of trace
• Not yet generic
⇒ Debugging is made easier by not flashing the program
16. 15/19
Benchs and results
Measurements done on PC, and on Arduino Mega board :
• ATmega2560 microcontroller : 16MHz ; Flash : 256kB ; Ram : 8kB
• GC : Stop and Copy
• Length of values : 32 bits
• Compiler : avr-gcc with -O2 optimization level
Speed measurements :
Program Time (ocamlrun) Time (OMicroB Time (OMicroB RAM usage
(s) on PC) (s) on MCU) (s) (heap+stack) (B)
oddeven 0.28 0.62 2.262 1003
sieve 0.04 0.07 0.390 1825
deriv 0.03 0.05 0.250 1946
integr 0.04 0.06 0.379 1849
Size measurements :
Program Bytecode Size of the RAM stack size heap size
size (B) exec. (B) (B) (words) (words)
Manhattan distance 744 24 210 5555 900 350
Solilet 413 18 322 5069 800 450
Snake 1376 21 238 1768 35 300
Flash and RAM size for the snake game :
Taylor-made Size in flash Size in RAM Stack-size Heap-size
interpreter
no 27.9 ko 2*1.33 ko 2*64 values 2*300 values
yes 17.3 ko
Bytecode size optimizations (snake) : 30.16 ko
ocamlclean
−−−−−−→ 6.9 ko
bc2c
−−−→ 2.1 ko
17. 16/19
Hardware abstractions using functors
(** Interface for SPI communication *)
module type SPI = sig
val init: unit -> unit
val transmit: char -> char
end
(** Connect as a slave *)
module SPISlave: Circuits.SPI
(** Connect as a master, with a SS pin *)
module SPIMaster(SC: sig
val ssPin : pin
end): Circuits.SPI
Master (BBC micro :bit) code
module%comp SPIM = SPIMaster(ssPin = PIN0)
let _ =
SPIM.init ();
let c = SPIM.transmit ’m’ in
Screen. print_string (String.make 1 c)
Slave (Arduino) code
module%comp MyDisp = MakeLCD(
rsPin = PIN9;
enablePin = PIN8;
d4Pin = PIN5;
d5Pin = PIN4;
d6Pin = PIN3;
d7Pin = PIN2;
)
let _ =
MyDisp.init ();
SPISlave.init ();
while true do
let c = SPISlave.transmit ’s’ in
MyDisp. print_string (String.make 1 c)
done
19. 18/19
Extension to synchronous programming / critical systems
Synchronous Programming
• Synchronous programming is well adapted to microcontrollers
ERTSS 2016 “Concurrent Programming of Microcontrollers, a Virtual Machine
Approach”
• Model of concurrent programming on critical applications
• No allocation during a synchronous instant (→ no GC)
• No unbound loops
Example (OCaLustre) :
let%node rising_edge (in_f) ~return:(out_f) =
out_f := in_f && (not (false ->> in_f ))
let%node press_switch (button) ~return:(led) =
button_pressed := rising_edge (button );
led := false ->> (if button_pressed then (not led) else led)
(N.B. a ->> b ≡ a0, b0, b1, b2, . . . )
Worst-Case Execution Time (WCET) computation
• We can associate a WCET for each (non-allocating) bytecode instruction
• Deduce the WCET of a synchronous instant
20. 19/19
Conclusion
Our solution offers
• A lightweight virtual machine
• providing an expressive programming model
• that offers safety (static typing) and guarantees (WCET for the synchronous
extension)
• as well as an easier debug process
• all while being portable and configurable
• and keeping good performances
⇒ Well suited to (critical) embedded systems
Future works
• Integrating new architectures into the OMicroB project (ESP, PIC32, ...)
• User-friendly Development Environment
• A more generic simulator (bundled with the IDE)
https://github.com/stevenvar/OMicroB