I was recently hooked on Atmel processors recently, as I was developing a "day in the life" camera for my girl as she leaves for a yearlong trip to Nicaragua, Kenya, and Australia (more on that another time; for now it will suffice to say that she's awesome and helping humanity in ways I only wish I could). So, of course, I started with Processing sketches on Arduino boards to learn the feel of the µC, and then moved to a more traditional makefile environment. And that was okay, but...
I was still writing in C++. Don't get me wrong, I've been developing in C++ for a very long time. But it just strikes me as a bad match for that platform. Looking through the resulting code, it's not very efficient. You pretty much have to go that route, though, especially when you're starting out because most of the libraries you want to use are also implemented in C++.
Next to Lisp and C++, another long-time favorite language would seem to be a shoe-in for this platform: Forth. If you use a PPC Apple Macintosh, or Sun SPARC (and UltraSPARC) hardware, you're using Forth in its OpenBoot (neé Open Firmware). Likewise, Forth finds its way into all sorts of small, resource limited environments, ranging from the ubiquitous, as in FedEx's handheld package scanners, to the extreme, such as NASA's NEAR mission to Eros where the spacecraft was reprogrammed in-flight to land on the asteroid when they found they had more fuel than anticipated near the end of the mission.
In short, Forth is the bomb for tight code, small environments, and so on. It nearly embodies refactoring, and has done so long before "refactoring" was ever an industry buzzword. It excels at avoiding bloat and repetition, and like Lisp, it's always been an essentially mutable language that you can form and shape and, yes, even reprogram to best solve the problem at hand.
So, I was surprised to see not so much in the way of industrial strength Forth in the Atmel world. It doesn't take long to find all kinds of Forth projects for AVR8 processors on the net—clearly there is interest—but they all pretty much share a single approach to implementation. I found:
- http://www.offete.com/328eForth.html An on-chip implementation specifically targeted to the ATmega328 and similar.
- http://nforth.berlios.de/ An on-chip kernel that is really stripped down in order to leave as much of the Flash space available to you.
- http://krue.net/avrforth/ Which looks great, but I just have some trouble getting a handle on for ATmega processors. The fault is mine, not theirs, of course (and, no, it's not because of the influence of ColorForth; I love ColorForth).
- http://amforth.sourceforge.net/ Another on-chip implementation that looks very complete and mature.
The thing that bugs me about these is that they are all embedded Forth environments. (Well, except for AVR Forth, which I think shares my goal below.) Embedded Forth environments are fantastic, don't get me wrong: they are the key to wonderfully flexible software that you can update even after you've burned code to ROM. ("Huh? What's he talking about?")
But they're only half the system.
I was surprised to not see more tethered Forth support like you saw in the past, or still see today in RTX and similar environments (like the one that drove the NEAR mission mentioned above). There you would have a small monitor running on the µC, and that usually includes a minimal Forth environment. But, you also have a Forth environment running on a local computer, with all the power you could possibly want. By virtue of the small monitor, you have the extended functionality you'd expect:
- Update code in Flash as necessary
- Define and run code on the target on the fly
- Write code on the host that becomes code on the target µC
- Write code on the host that directly modifies resources on the µC on the fly
- Write code on the host that interacts with other code on the target
- Write code on the host that writes code on the target
Once you've got such an environment and have coded an entire project locally, you could then easily analyze dictionaries and create downloads that only contain code essential to execution. No unused code, no junk in the program space. You can keep all the auxiliary code and definitions that helped you develop the application, of course; that stays up in the host environment.
Another thing that's missing from some of these environments is a real assembler. The heavily optimized and performant implementations of Forth in the past let you freely flip back and forth between high level words and low level assembly. Most of the Forths I see today, not just in the Atmel universe, are “Forth only” in that they don't support mixing assembly with Forth definitions.
To me, that will be essential to wringing the best performance out of these environments.
You see, the idea of an on-chip Forth is a good thing, but it must be minimal. Take an ATmega168: You've got 16 kB of Flash memory on a processor that spends typically two bytes on every instruction. That's just over eight thousand instructions, total, that the system can use. Memory spent in a monitor, or even in a Forth interpreter, is memory not available for your application. Heck, one of the Forths listed above takes up 12 kB out of the 16 kB of Flash. Now, while you can actually get a lot done in a 4 kB Forth dictionary (really), it's still a horrible price to pay.
On the other hand, a minimal Forth is fine, but you don't want to develop in it. Development environments should try not to be so limiting that they become awkward to use. That's where your host environment comes in. There, you've got all the memory and storage you could possibly want, you can afford to code as “meta” as you might like. Code writing code, that's where it's at! Maybe that's my Lisp showing again. ☺
So, that's what I'm after at this point. A tethered Forth environment primarily for Atmel AVR 8-bit processors like ATmega168 and such, but that should also support even the Tiny processors without too much work.
In grand tradition, though, first I need a name…
It's Alive! (2014 Mar 15)
(I wrote this about four days later in a seperate post, but later edited this one and folded them together to make life easier.)
That's right, things are already running! At present, I'm borrowing heavily from Frank Sergeant's A 3 Instruction Forth for Embedded Systems Work, as well as a few papers by Brad Rodriguez in this initial implementation. I wish I hadn't put off starting this; it was easier than I thought!
Target system: Arduino Uno (ATmega328), USB
I’ve written a rudimentary monitor now (actually a "four instruction forth", adding an operation so that I can re-synchronize between the host and target pretty easily) that I proved out with tip(1). I will replace this with something much better soon.
I then wrote some sloppy tty code in gforth that’s enough to interact with the monitor. I’m relying on the builtin umodem driver in FreeBSD (which just presents a serial tty-like interface to certain USB drivers). It was a couple quick hacks, but it was sufficient.
The result is that typing
20 blinks on the host system flashes the common pin 13 LED on the Arduino twenty times. E.g., you might type:
include test.fs 20 blinks
Although this file may be easily guessed by anyone already familiar with Forth, here is test.fs, which just opens up the tethered environment to the Arduino over the umodem tty at 9600 baud.
TC! is just like the standard Forth word
C! but "targeted" at the tethered Forth environment. (Similarly, there’s
Temit, and so on provided in my host.fs file.
include host.fs 9600 s" /dev/cuaU0" Tinit $23 constant PINB $24 constant DDRB $25 constant PORTB $20 constant LED : init LED DDRB TC! ; : on LED PORTB TC! ; : off 0 PORTB TC! ; : blinks init 0 do on 100 ms off 200 ms loop ;