Clicker 2, FORTHdsPIC and more Bare-Metal
View of the Clicker 2 development system with a ‘jacked-up’ Parallax robot being used to test the revised FORTHdsPIC code. The robot’s on-board regulators provide the motor power and tacho O/C pull-ups via a separate 7.5V power supply plugged into the Arduino interface Vin socket. Everything else is USB-powered.
In a blog post a short while ago I described how I ported my dsPIC-based embedded Forth language compiler/interpreter to a new development board, the Clicker 2 from MikroElektronika. The FORTHdsPIC project started a few years ago to update a version of Forth I’d written for the Z80 microprocessor back in the 1980s. LUTForth enabled the microprocessor-controlled instruments I was developing at the time to be programmed efficiently in a high-level language. Nowadays we refer to these as ‘embedded’ systems. But why Forth, and why go to all the trouble creating it with assembler-language (“Bare-Metal”) programming? Well, for many years Forth was the language of choice for embedded real-time control applications: robots and spacecraft for example. It takes up little space, under 8Kbytes in the case of FORTHdsPIC, and is really fast. As for the bare-metal programming, it’s a bit like rock climbing without a safety line. Alright, coding mistakes aren’t usually fatal unless a driverless car or an aircraft is involved, but you may need the patience of a crash investigator to track those bugs down.
The main reason for the change to the ‘E’ part on the Clicker board was to get access to a lot more pins, more timers, and faster speed. Here are some more of the fun and games I’ve had with the Clicker 2 port.
It’s all in the Timing
The new chip will run at 70MHz, nearly doubling the processor speed from the old chip’s 40MIPS to 70MIPS. This meant that a lot of timing constants within the code had to be increased by a factor of 7/4. The critical settings that had to be changed first included those affecting the system clock generator and terminal communication UART baud rate generator. With those two areas covered FORTHdsPIC booted up and announced itself in the terminal emulator running on my laptop. All basic Forth words were available and working.
Flushed with success, I set about modifying the code that allows Forth programs to be stored in the non-volatile Flash memory. Without that working, it could hardly be called an embedded system. Changes were necessary because Microchip had added new features to the Flash programming hardware. At this point, I should say that I’d assumed the new part would be functionally the same as the old one apart from the speed increase and more of everything. That was a mistake. Yes, there are more general-purpose timers, but other basic things like the UARTs, Input Capture (for reading wheel tachometers), Output Compare (for generating PWM waveforms) have acquired lots of extra features with more control registers to set up. My original code would require extensive modification.
Getting the Flash programmer to work took hours of frustrating effort. I had consulted the relevant section of the user manual and taken into account the various register changes, but still no joy. I got to the point where I decided the internal programming voltage circuit must have been damaged and I obtained a replacement board. The new board was no better. When I eventually spotted the problem, I at first cursed myself for being so careless, and then Microchip for not updating the assembler code example in the manual. The C code example had been changed, but not the assembler. The problem is that the Flash timing in the 40MHz part only requires a couple of ‘NOP’ (No Operation) instructions after the programming ‘trigger’ instruction. The 70MHz part needs the same wait period, but of course, the NOP instructions take just over half as long to execute (Fig.1a). It was easily solved by using a loop that only exits when a Ready flag is set (Fig.1b).The moral of this sorry tale is:
- Always read the manual thoroughly.
- Don’t believe everything you read in manuals.
- Never use fixed timing delays (padding) in your code.
The Trouble with Tachometers
I was never really satisfied with the wheel rotation speed measurement code I outlined in my blog post on robot mobility control. Because of a shortage of timers, the two tachometer (Input Capture) channels had to share the same GP Timer 3. This caused two irritating limitations:
- The timer had to free-run and interval measurements were taken by subtracting the ‘captured’ timer value on a tacho pulse interrupt from the previous one.
- At very slow wheel speed, the interval between tacho pulses exceeds the 16-bit range of the timer and erroneous data is returned.
If each channel had its own timer then it could be restarted from zero after each capture, eliminating the need for any subtractions. Moreover, if the timer reached 0xFFFF and rolled over, it would generate an interrupt which could be used to set a flag indicating invalid data. The dsPIC33E has a load more timers. Problem solved? Yes and No. The new Input Capture hardware features a specially dedicated timer for each channel. Yay! Unfortunately, it can’t be cleared by software and there is no overflow interrupt available. Cue howls of frustration. Luckily, I spotted a solution to the overflow problem before I treated the board to a rather more literal interpretation of the term ‘boot-up’.
Despite having its own dedicated timer, each IC channel still needs a GP Timer unit to provide its clock signal. All it actually uses are the GP Timer’s input prescaler and gating circuits. The timer-counter itself is unused and just sits there counting away for no purpose. Until that is, I realised that, because it shares its clock, and can be cleared by software – it provides a perfect ‘watchdog’ generating an overflow interrupt whenever the IC timer receives more than 65535 clock pulses between adjacent tacho pulses. The capture data subtraction is still needed but at least the first few dodgy tacho data samples from rest are all replaced with 0xFFFF which helps the PID controller lock on more quickly.
Other Changes and Additions
The picture below (Fig.2) provides a guide to the functions available on this new version 0.8 of FORTHdsPIC based on a Clicker 2 board. There are four PWM servomotor channels plus two more with full PWM-programmability: Pulse Frequency from 50 to 10000Hz, and Duty Cycle from 0 to 100%. These can be used for the speed control of two PMDC motors via suitable driver circuits. Although not fully coded yet, I have added drivers for a second UART driving the Aux Tx/Rx pins on the mikroBUS Click socket 2. The plan is to use this for a wireless channel providing a remote-control facility for a mobile robot. MikroElektronika has a huge range of wireless Click boards covering Bluetooth, WiFi, LoRa and other, simpler protocols. I think I’ll go for a basic 434/868MHz ISM-band ‘cable-replacement’ type, at least initially. Three reasons:
- Driver software is simple for peer-to-peer remote-control communication.
- The control unit will be like that used for R/C model vehicles, using joysticks. Do you ever see professionals trying to drive an AUV or UAV from a touch-screen on a phone or tablet?
- Wide channel bandwidth is not needed, because only low data rates will be used.
- I have a spare Clicker 2 board for the remote! (see above)
Of course, should I want to connect it to an IoT network based on LoRaWAN, for example, (almost) all I have to do is change the Click board.
Is it really faster?
The new board ought to be able to run Forth code 75% faster than the old one on the basis of the clock speed alone. Only one way to find out; run some benchmarks. Developers of new versions of Forth have always had the benefit of a simple set of benchmark routines, each designed to exercise different features of the language. I have the results for the 40MHz part and was looking forward to seeing that big improvement. But they showed only a 20% increase in speed. Why? After a bit of head-scratching I went back to the manuals, in particular the dsPIC33E instruction set which tells you how many clock cycles each instruction takes to execute. And there it was: all conditional branch instructions now take four cycles instead of two, if the condition is true. The unconditional branch also takes four with subroutine and interrupt returns doubling to six cycles. These changes are undoubtedly the cause of the less than impressive benchmark performance. To be fair, the tests involve an awful lot of branching relative to the execution of normal single-cycle instructions. A ‘real’ program may well perform much better. Still, it just goes to show that reading manuals is very important – including all the fine print!
I’ve attached the fully-annotated source code for version 0.8 below, together with the .hex (machine code) file which can be Flashed straight into the Clicker 2 board using MikroElektronika’s free programming suite.
If you're stuck for something to do, follow my posts on Twitter. I link to interesting articles on new electronics and related technologies, retweeting posts I spot about robots, space exploration, and other issues.
CommentsAdd a comment
Like Boss, I had some exposure to Forth (and a variant of it) back in the '80s, on an IBM minicomputer. It shared (along with the APL language) a reputation for code that was powerful but more writable than readable. I only used it for a short while before work projects in other languages shifted my focus. But, like APL and some other languages computer languages that are now infrequently used, Forth had some unique ways of approaching some problems that I absorbed and later adapted for use even when I was programming in other languages. One big strength of Forth was its extensibility - the way you could easily turn snippets of native and/or Forth code into additional "words" in the language.
On a later project where I implemented a GPIB (HPIB at the time) interface for an avionics data analyzer instrument, we needed a way to allow the user to customize the output formatting (and statistical processing) of the data being captured by the analyzer. The desired formatting and record-handling operations could vary significantly. Some users might be formatting the output to go directly to a printer in human-friendly units. Others might be sending the data as compactly and quickly as possible back to the computer controlling the GPIB-based instruments. I borrowed from Forth the idea of translating the formatting command string (ASCII) from the user into a series of addresses of corresponding command-word routines to be executed when processing incoming data captures. Essentially, this compiled the command string into a buffer containing the series of formatting and manipulation routine calls that could carry out the formatting command string's intent. When the actual data was received from the avionics bus, all it took was a call to that compiled command buffer to carry out the intent of the formatting command string, without having to re-parse the formatting command string each time new data came in. That let us maximize the capture and processing rate, despite the relatively slow microprocessors of the day.
(We used the NSC800 at another place I worked at, implementing an energy management system with both local and remote data terminals. A multi-tasking dual-user system with a processor running at under 1 Mhz.)
Interesting! I used Forth back in the 80's but we ended up dropping it for C later mainly due to code support issues between the team. We found we could quickly write fast code, but when we needed to change anything it was very difficult for the person who wrote it and almost impossible for anyone else. It may have been our documentation method or lack of! But once you got the hang of Forth it was almost 'fun'.
We 'almost' ported it to the NSC800 CMOS microcontroller from memory using a large printed document from FIG Forth (?). All typed byte by byte into EPROM, saddly there must have been an error as a few functions crashed.
I even had Forth on a Sinclair Spectrum, but that was doomed with cassette tape loading issues... Those were the days :)
I can only admire what you have achieved, great work.
The one funny event I recall was in the early days when we had onsite training, the presenter ended every sentence he spoke with.... "Okay" :)
The irony! Having a good deal of experience with bare-metal PIC assembly coding to compare with the worst two months of my career trying to program in Forth, I would categorically state that the one way to make PIC coding harder is to implement a Forth interpreter! I have little difficulty with any other language from Cobol to Python, but for the sake of my sanity I would not touch Forth again with a barge pole!
Forth had its place in the early days of computing because it could get decent performance out of really simplistic processor architectures, but making a dsPIC, let alone a human being, operate at that level is just cruel!
Please tell us your STEM activities have no intersection with Forth? The thought of this fossil being inflicted on another generation would be too horrible!
@JohnAHind Well, it's often been said that Forth is the Marmite of computer languages! The FORTHdsPIC project started as a way of comparing the performance of an embedded Forth I wrote for the 8-bit Z80 back in the 1980's with one based on a modern processor. Forth is a 16-bit stack-oriented language so the dsPIC seemed an ideal choice. Most of the articles I've written on the project so far concentrate on the bit I'm really addicted to - assembler level programming.