The most commonly used programming language for embedded applications is C, with C++ gradually becoming more popular. C was never designed for embedded use, but a handful of languages were intended from day 1 to be used for this purpose. One of those languages is Forth. Although it may be argued that the language's heyday was about 30 years ago, there is still a loyal band of Forth fanatics who keep the language alive. This article is a very brief introduction to the language, and we consider whether it might have a place in modern embedded systems.
How I got hooked
My attention was originally drawn to Forth when somebody told me that there was a programming language that facilitated the generation of code using less memory than an assembly language implementation of the same functionality. I did not believe them, but it hooked me in ...
Forth is a threaded Interpretive Language, which means that the code is compiled into an internal form read by runtime software to give the effect of execution, much the same way that Java is normally implemented. It is possible to include native assembly language (maybe originally C) components as required.
A Forth program is comprised of a number of "words" - entities that are basically similar to functions in C. Each word consists of references to other words, which may be predefined or may be created previously by the developer. A word may also be an assembly language routine. In source form, a word is any sequence of non-space characters; when compiled, they become a list of addresses that point to other words or assembly language.
A fundamental aspect of Forth is its use of a stack, which it uses for most data manipulation. Words tend to operate on the data on top of the stack, maybe removing it, and place results there.
Forth may be used interactively, where each line of code is executed when the RETURN key is pressed, or it may be compiled to create new words. For example, you could type this line:
3 dup + .
The interpreter would (probably) not find a word called "3", so it would attempt to treat this as a constant and place the value on the stack.
The word dup results in the duplication of the top stack item (so now there are two stack entries of the number 3).
It is probably obvious that + adds together the two top items on the stack and pushes the result (6) there.
The last word, ".", pops a value off the stack and displays it.
If this were an operation that you wanted to perform frequently, you could define a word of your own to do it, thus:
: show2times dup + . ;
The word : tells the interpreter to start compiling a word with the name that follows - in this case show2times. The ; marks the end of the text to be compiled. Having done this, you can double and print 3 by typing:
Now for a more “embedded” example (Figure 1). Here is some code that sets a bit on a write-only port. I have played fast and loose with the syntax, so the code may not work on any real Forth system, but I hope it illustrates the idea.
0x100000 constant woport
0 variable shadow
: update_shadow 1 swap shift_left shadow @ or shadow ! ;
: write_port shadow @ woport ! ;
: setbit update_shadow write_port ;
To set a bit on the port just needs the bit number pushed on the stack and an invocation of setbit, thus:
Now to analyze how the code works:
A constant, woport, is defined to represent the address of the write only port and a variable, shadow, is used to store the shadow copy of the data.
The update_shadow word expects a bit number on the stack. It uses this to create a bit mask by shifting a 1 left the appropriate number of places. It then gets the value of shadow, using @, ORs in the bit mask, and stores it back to shadow using !.
The write_port word gets the value of shadow and writes it to woport.
The final word setbit simply invokes update_shadow and write_port in sequence.
In Forth, code may often be refined by encapsulating an operation in a new word. This might be useful, for example, where a variable’s address is obtained twice. In this case, it might be useful to create a word to OR a value into a variable, thus:
: or! dup @ rot or swap ! ;
This expects a value and an address on top of the stack. The word update_shadow can then be simplified:
: update_shadow 1 swap shift_left shadow or! ;
From this short description and trivially simple examples, I hope that you can see that Forth is conceptually quite different from C and other more familiar languages and also has a lot of flexibility and expressiveness. Many people criticize Forth for being a "write only language", as it is easy to write code in Forth that is impossible to read. Although I would agree that this is true, I promise I could write some completely unintelligible (but valid) C, if required to do so.
Writing code in Forth is a process of extending the language. As you add new words, the customized language of your particular project grows and become more and more application specific. It can be argued that, in this way, the language is perfectly suited to each and every application.
The future of Forth
A while ago, I attended a seminar where one of the speakers was discussing the history of their company over the last 25 years. He talked about their first project being done in Forth and commented that this was unlikely to be the way things would be done today. I wonder if Forth will ever see a large scale revival. A little Web surfing reveals that interest (and, I suppose, usage) of the language is surprisingly widespread.
If it is accepted that the virtues of Forth are the ability to rapidly create complex applications that are incredibly memory efficient, it seems to me that it might be the ideal tool for utilizing very low cost 32-bit microcontrollers, which have minimal on-board memory. Internet of Things applications might be a great starting point.
Colin Walls has over thirty years experience in the electronics industry, largely dedicated to embedded software. A frequent presenter at conferences and seminars and author of numerous technical articles and two books on embedded software, Colin is an embedded software technologist with Mentor Embedded (the Mentor Graphics Embedded Software Division), and is based in the UK. His regular blog is located at: blogs.mentor.com/colinwalls. He may be reached by email at email@example.com.