First Foray into FlashForth for AVR

Arduino-FVM was a nice, easy introduction to Forth on AVR/Arduino, and has a few advantages as a Forth implementation. But I was interested in something more developed and full-featured. So today I got FlashForth installed on a 328P MC, running on an Arduino Uno.

Installing FlashForth on AVR requires setting fuse bits as well as burning a new flash image, so I had to wire up my Nano ArduinoISP programmer again. Pulling this off required mixing details from the ArduinoISP tutorial, my previous post on ArduinoISP, and instructions from the FlashForth Web site, none of which were quite sufficient information taken individually.

I used the precompiled 328P hex flash image that is included in the FlashForth git repo. I intend at some point to compile the source, but I needed to get over the initial hurdle of seeing if I could get FlashForth to run on an MC at all, and if I liked it. This was the avrdude call which worked for me:

christopher@theoden ~/Repos/flashforth/avr/hex$ avrdude -p m328p -c stk500v1 -P /dev/ttyUSB0 -b 19200 -e -u -U flash:w:328-16MHz-38400.hex:i -U efuse:w:0xff:m -U hfuse:w:0xda:m -U lfuse:w:0xff:m

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.02s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: erasing chip
avrdude: reading input file "328-16MHz-38400.hex"
avrdude: writing flash (32524 bytes):

Writing | ################################################## | 100% 0.00s

avrdude: 32524 bytes of flash written
avrdude: verifying flash memory against 328-16MHz-38400.hex:
avrdude: load data flash data from input file 328-16MHz-38400.hex:
avrdude: input file 328-16MHz-38400.hex contains 32524 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 0.00s

avrdude: verifying ...
avrdude: 32524 bytes of flash verified
avrdude: reading input file "0xff"
avrdude: writing efuse (1 bytes):

Writing | ################################################## | 100% 0.01s

avrdude: 1 bytes of efuse written
avrdude: verifying efuse memory against 0xff:
avrdude: load data efuse data from input file 0xff:
avrdude: input file 0xff contains 1 bytes
avrdude: reading on-chip efuse data:

Reading | ################################################## | 100% 0.01s

avrdude: verifying ...
avrdude: 1 bytes of efuse verified
avrdude: reading input file "0xda"
avrdude: writing hfuse (1 bytes):

Writing | ################################################## | 100% 0.02s

avrdude: 1 bytes of hfuse written
avrdude: verifying hfuse memory against 0xda:
avrdude: load data hfuse data from input file 0xda:
avrdude: input file 0xda contains 1 bytes
avrdude: reading on-chip hfuse data:

Reading | ################################################## | 100% 0.01s

avrdude: verifying ...
avrdude: 1 bytes of hfuse verified
avrdude: reading input file "0xff"
avrdude: writing lfuse (1 bytes):

Writing | ################################################## | 100% 0.01s

avrdude: 1 bytes of lfuse written
avrdude: verifying lfuse memory against 0xff:
avrdude: load data lfuse data from input file 0xff:
avrdude: input file 0xff contains 1 bytes
avrdude: reading on-chip lfuse data:

Reading | ################################################## | 100% 0.01s

avrdude: verifying ...
avrdude: 1 bytes of lfuse verified

avrdude done.  Thank you.

Here is the logon using picocom:

christopher@theoden ~/Repos/flashforth/avr/hex$ picocom -b 38400 -c /dev/ttyACM0picocom v3.1

port is        : /dev/ttyACM0
flowcontrol    : none
baudrate is    : 38400
parity is      : none
databits are   : 8
stopbits are   : 1
escape is      : C-a
local echo is  : yes
noinit is      : no
noreset is     : no
hangup is      : no
nolock is      : no
send_cmd is    : sz -vv
receive_cmd is : rz -vv -E
imap is        : 
omap is        : 
emap is        : crcrlf,delbs,
logfile is     : none
initstring     : none
exit_after is  : not set
exit is        : no

Type [C-a] [C-h] to see available commands
Terminal ready
E FlashForth 5 ATmega328 18.11.2020

Here are the words defined by default:

words 
p2+ pc@ @p hi d. ud. d> d< d= d0< d0= dinvert d2* d2/ d- d+ dabs ?dnegate dnegate s>d rdrop endit next for in, inline repeat while again until begin then else if zfl pfl xa> >xa x>r dump .s words >pr .id ms ticks r0 s0 latest state bl 2- ['] -@ ; :noname : ] [ does> postpone create cr [char] ihere ( char ' lit abort" ?abort ?abort? abort prompt quit true false .st inlined immediate shb interpret 'source >in tiu tib ti# number? >number ud/mod ud* sign? digit? find immed? (f) c>n n>c @+ c@+ place cmove word parse \ /string source user base pad hp task ulink rsave bin hex decimal . u.r u. sign #> #s # digit <# hold up min max ?negate tuck nip / u*/mod u/ * u/mod um/mod um* 'key? 'key 'emit p++ p+ pc! p! p@ r>p !p>r !p u> u< > < = 0< 0= <> within +! 2/ 2* >body 2+ 1- 1+ negate invert xor or and - m+ + abs dup r@ r> >r rot over swap drop allot ." ," s" (s" type accept 1 umax umin spaces space 2swap 2dup 2drop 2! 2@ cf, chars char+ cells cell+ aligned align cell c, , here dp ram eeprom flash >< rp@ sp@ 2constant constant 2variable variable @ex execute key? key emit Fcy mtst scan skip n= rshift lshift mclr mset ic, i, operator iflush cwd wd- wd+ pause turnkey to is defer value fl+ fl- c! c@ @ a> ! >a literal int! ;i di ei ver warm empty rx0? rx0 tx0 load- load+ busy idle exit 
marker  ok<#,ram>

I only had a few minutes to experiment with it, but here are a several things of interest:

  • One great thing about FlashForth is that all the three memory types – RAM, flash, and EEPROM, are mapped to a contiguous address space, and accessible with @ and ! memory words. So, you don’t have to do anything special to write to flash or EEPROM.
  • By default, all words you add to the dictionary are compiled to flash memory and therefore survive a chip reset. This is very convenient.
  • You are not actually allowed to redefine words (at least, in the usual way of defining words), but you are expected instead to use the marker Forth word to construct forgettable sections of words. Also the empty word will remove all words but the core FlashForth words.

XL6009 Voltage Boost Converters

XL6009 Boost Converter in NPN Transistor Switch

The large module is the the XL6009 Boost Converter, which is boosting a 5 V source to 21 V. There is a small screw on it that you can turn to select other voltages (i think it goes up to 35 V).

It is tied into an NPN transistor, set up for switching. The base of the switch is driven by a 5 V signal from the Uno, with a 10k resistor, which turns on the collector-emitter path, which being driven by the 21 V source, with a 1k resistor, and providing current for the LED.

I needed a 21 V source for my EPROM programming project, but needed it to be controlled by a 5 V AVR pin.

A pack of XL6009 Boost Converters is available for about $12 from Amazon.

EPROM Reader Rev. 2

Slightly improved hand-made reader for our old 32K and 64K EPROMs

The original design was rather painful to work with, and ugly, so I put this together which is a little nicer. The button, switch, chip socket, and cable socket are one ElectroCookie Snappable PCB. I like those PCBs as they have three-hole strips.

I found some decent sockets that fit the old chips (2732A and 2764 EPROMs). In retrospect, I maybe should have got a zero-insertion force sockets where you just drop the chip in and pull the lever to tighten.

With this setup, I can have one ribbon cable going from this board to the Mega. I didn’t know what kind of cable or cable-connectors I needed to fit this 36 pin setup, so I made a cable using the commonplace loose-end Arduino ribbon cable. The pins bend rather easily, but it is better than the previous setup.

reader connected to Mega

I’m still using magnet wire – for which I’ve developed a love/hate relationship. The magnet wire looks nice, is easy to cut, and one roll lasts pretty much forever. But it is rather a pain because (1) you have to boil the coating off the ends of each piece of wire with solder before you use it, and (2) you have to put a blob off solder in the hole first and then reheat it to stick the magnet wire in. Regular insulated wire, that is sized correctly for the hole, is sounding very appealing at this point. But, it works.

Magnet wire on bottom of PCB

Something fun I learned in the process: I wrote a Forth script for testing the pin connections. The output of the script comes from Mega over UART (serial), but I used vte style escape codes to keep the display fixed and updated on one part of the terminal screen.

Rev. 2 board Forth testing script

CKOUT: 328P as clock source

Left: Nano serving as fuse bit programmer. Middle: Uno with 328P CKOUT fuse bit programmed. Right, 16Mhz signal coming out of pin 8 (PB0) of Uno.

Wanting to work with an AD9833 signal generator chip, which needs an external master clock source, I was wondering if I could somehow tap into the 16 Mhz clock already feeding the 328P. The answer was yes, it is possible on the 328P to feed this to pin PB0 (a.k.a., digital pin 8).

328P datasheet: Clock Output Buffer

The only tricky part is you have to program a fuse bit on the chip, which can not be done through self-programming, the way you normally upload code through the arduino bootloader. You need to set bit 6 (CKOUT) in the lfuse byte to 0:

So, you have to have a separate programmer, and also you must write the whole byte, meaning you want to be careful to preserve the other bit settings. Fortunately, through the ArduinoISP sketch, you can use another Arduino to program it.

https://www.arduino.cc/en/Tutorial/BuiltInExamples/ArduinoISP/

This is fairly straightforward to wire up. However, somebody on #arduino IRC had to explain to me that it is necessary to put a 60 Ohm resistor across VCC and rst pins on the programmer, if using an Uno or Nano for the programmer. I didn’t catch that reading the tutorial.

Once wired up, you can use avrdude to write the lfuse byte. In my case.

avrdude -c stk500v1 -p m328p -P /dev/ttyUSB0 -b 19200 -U lfuse:w:0xBF:m

The last part of the command instructs avrdude to write byte 0xBF to the lfuse byte. A few things that might not be obvious about that: (1) in 328P fuse bytes, a “0” is considered the “programmed” state, the opposite of what you would intuitively expect; (2) I had to look at the boards.txt file in the arduino ide could to figure out that the “standard” byte value (for an MC used on an Arduino Uno board) is 0xFF. So, if I’m just changing bit 6 to zero, 0xFF needs to become 0xBF.

Screen photo of writing 0xBF to lfuse using avrdude

The fuse change was successful, and I was able to measure 16 Mhz out of PB0 (normally digital pin 8).

Forth SPI on Arduino

One byte (0xa6) transmitted from master out SPI pin using Forth

Arduino-FVM comes with only a few Arduino-function words, basically just a few for working with the digital pins. So the test for Forth was going to be: how difficult is it to access other microchip functionality using memory reads and writes? An encouraging first step was to implement SPI TX, using the information in the 328 datasheet.

C Code Example from 328 datasheet of SPI Master TX

First I needed some constants for the register addresses and bit numbers:

0x24 constant DDR_SPI
0x3 constant DD_MOSI
0x5 constant DD_SCK
0x2 constant DD_SS
0x4c constant SPCR
0x6 constant SPE
0x4 constant MSTR
0x0 constant SPR0
0x4e constant SPDR
0x4d constant SPSR
0x7 constant SPIF

Next I needed to set up the data direction bits for the MOSI, SCK, and SS pins, as well as set some bits in the SPCR register to control mode and communication frequency:

: setup-master-spi ( -- )
1 DD_MOSI lshift
1 DD_SCK lshift
1 DD_SS lshift
or or
DDR_SPI c!
1 SPE lshift
1 MSTR lshift
1 SPR0 lshift
or or
SPCR c!
;

And here is a function for transmitting a single byte, after dropping the byte on the stack:

: tx-master ( ch -- )
SPDR c!
begin
SPSR c@ 1 SPIF lshift and
until
;

And here is a demo procedure for sending byte 0xA6 repeatedly:

: spi-demo
setup-master-spi
begin
0xa6
tx-master
again
;

Now we can see that byte on the oscilloscope, from MOSI pin 11:

SPI signal for byte 0xA6

Byte 0xA6 equals 10100110, which you can understand from the signal image if you know that (1) signal low represents “1” and high represents “0”; (2) the X-scale is 2 microseconds, with one bit per microsecond; and (3) the first bit is the one microsecond of low signal just to the left of the Y axis.