Students: Or Ostrovsky, Ofek Kirzner
Porting picol to CC2650 Launchpad
As our final project in the course, we aimed to simplify the task of programming useful software, and to ease the process of interfacing with the CC2650 Launchpad board, made by Texas Instruments. Throughout the course, we have put a lot of efforts to accomplish simple tasks, such as manipulating the input and output pins, responding to interrupts, and utilizing various modules embedded in the processor. This is due to the need to understand the internals of the board, and the need to compile, load and debug one's code. We have come to realize that like scripting languages simplify PC programming, it can simplify embedded programming. And so, we have decided to port the TCL language to the CC2650 Launchpad MCU.
The platform at hand required the interpreter to meet the following requirements:
Apparently, most scripting engines have taken into account the last requirement, and may be easily embedded inside existing code bases. The other requirements are not so simple, since the more complex and usable the language, so it requires more memory. This rules out most common languages, like python or ruby. Even Lua, which is considered memory efficient, requires at least 32KB of RAM to operate. We briefly examined FORTH, which is a very small language that requires few KB's of RAM to function, but we have decided that any meaningful program written in it, will be more complex than the equivalent C code.
The perfect interpreter (and language) was Picol. Picol is a micro-implementation of the TCL language, which supports the core features of the language. It is also a single file implementation, and easily allows to add new commands if the need arises.
The homepage of the picol project is here.
The biggest challenge we faced, was the fact that the board has a very small amount of RAM, only 28KB. To address this challenge we did several adjustments to the engine:
sprintf
(ltoa
is more economical), and by using static buffers.if
statement syntax in order to simplify code execution - Regular TCL allows syntactic sugar
inside the if
statement condition. Most TCL commands look like != $a $b
, but if
statements
allow for the more convenient $a != $b
syntax. But, since this syntactic sugar requires more memory, we disabled it.We wanted the interpreter to execute commands upon input from one of many sources. The interpreter should wait until input arrives from any source, and execute the relevant code. That code might send textual output via UART, or change the state of the board hardware in another way. Currently, we support three sources: * The UART input * GPIO interrupts * Clock generated interrupts
Since the interpreter doesn't support multithreading, only one task may use it at once. Also, since it requires a large stack, only one task may use it at all. This results in the following architecture:
Event_pend
on a tirtos
Event
.
Any task may post events, which will cause main task to stop waiting and to execute the relevant code
for that event.Event_post
), and the main task executes the command from, which is stored in a global buffer.Using tirtos
's Event
s allow to add a large number of input sources in the future, since it supports up to 32
distinct events.
Also, since all event sources are handled equally without any busy-waiting, the interpreter is responsive for all of them.
After Connecting using a UART (using putty for example), with 9600 baud rate, one can try the following commands:
setgpio <pin id> <value>
- Sets pin id to the given value.getgpio <pin id>
- Returns the value of the pin id.gpio_set_inter <pin id> <tcl function>
- Sets an interrupt handler on the given pin id.gpio_clear_inter <pin id>
- Deletes the interrupt handler previously set on the given pin id.sha256 <value>
- Computes the SHA256 of the given value.aes_set_iv <iv>
- Sets the iv of a following AES encryption.aes_set_key <key>
- sets the key of a following AES encryption.aes_cbc_enc <value>
- Returns the AES encryption of a given string.aes_cbc_dec <value>
- Returns the decryption of a given string.true_rand <n>
- Returns a truly random number in the range 0-n.true_randstr <n>
- Returns a true random string of length n.clock_set <command> <timeout> <period?>
- Creates a clock that executes the command after the timeout.
If a period is specified, the command will be executed again after that period. The command returns the
number of the clock used. Both times are specified in milliseconds.clock_clear <clock_id>
- Clears the clock with the given id, which is returned by the previous commend.All gpio commands currently support only LED0, LED1, BUTTON0 and BUTTON1, but may be expanded in the future.
All demonstrations are also available in the DEMO.tcl
file.
+ 1 1
set x 0
incr x
puts $x
if {!= $x 0} {puts "hello"} else {puts "world"}
for {set i 0} {< $i 5} {incr i} {puts [* 2 $i]}
proc foo {arg1} {puts $arg1}
foo "me"
set key [true_randstr 16]
set iv [true_randstr 16]
aes_set_key $key
aes_set_iv $iv
set crypt [aes_cbc_enc "Hello world"]
aes_cbc_dec $crypt
sha256 "hello world"
setgpio 6 1
getgpio 6
setgpio 6 0
proc flip_gpio {pin} {set x [getgpio $pin]; setgpio $pin [! $x]}
gpio_set_inter 13 {flip_gpio 6}
gpio_set_inter 14 {flip_gpio 7}
set pushes 0
gpio_clear_inter 13
gpio_set_inter 13 {uplevel 1 {incr pushes}}
clock_set {flip_gpio 6} 1000 1000
clock_clear 0