This was done for an earlier version of Riscy Pygness that wrote extensions to the target's dictionary to flash. The current version writes extensions to the target's RAM. Until this sample session is updated, please ignore the parts about erasing flash and burning flash.
Here is a sample session that
To add a note of realism, we discover an actual bug near the end of the session.
Make a minor edit to the file riscy.asm so it will need to be reassembled. Open a shell and invoke make to do the work:
frank@bed:~/arm$ make preasm.lisp riscy.asm riscy.s arm-elf-as -mcpu=arm7tdmi -ahls -mapcs-32 -gstabs -ahls=riscy.lst -o riscy.o riscy.s ...linking riscy.elf arm-elf-ld -v -T lpc2106.ld -nostartfiles -o riscy.elf riscy.o GNU ld version 2.15.90.0.1.1 20040303 arm-elf-objdump -h riscy.elf > riscy.lnkh arm-elf-objdump -t riscy.elf > riscy.lnkt arm-elf-objcopy -O ihex riscy.elf riscy.hex arm-elf-objcopy -O binary riscy.elf riscy.bin frank@bed:~/arm$
Make (using the contents of makefile) controls the above process:
At this point, the primitives have been assembled and we have an image file (riscy.bin) and a table showing the addresses of the various routines (riscy.lnkt).
The next step is compile the high-level Forth files. Start up your Lisp. I use CLISP and I start it from within Emacs after I have opened the main Lisp file, arm.lisp. In the arm.lisp buffer, I type C-c C-z to start CLISP (if it has not yet been started) or to move to the CLISP buffer (if it is already running). If your Emacs is not yet setup to run CLISP, you can do it manually by typing C-u M-x run-lisp RET clisp RET. See the Emacs tutorial if you need to understand the above description of the keystrokes.
Alternatively, you can start CLISP from a shell prompt and just type in all the commands directly. Emacs is particularly convenient, though, because you can execute any Lisp expression (even an expression in a comment) by positioning the cursor just past the expression and typing C-u C-x C-e (which both executes the expression and moves to the CLISP buffer so you can see the results of running the expression.
Below, I will type the Lisp expressions to execute. Typically, they exist already as comments early in the file arm.lisp, so in real life I do not type them, I merely execute them as described above.
(load "arm") [15]> ;; Loading file /home/frank/arm/arm.lisp ... ;; Loading file /home/frank/arm/mem.lisp ... ;; Loaded file /home/frank/arm/mem.lisp ;; Loading file /home/frank/arm/parse.lisp ... ;; Loaded file /home/frank/arm/parse.lisp ;; Loading file /home/frank/arm/prim.lisp ... ;; Loaded file /home/frank/arm/prim.lisp ;; Loading file /home/frank/arm/forth.lisp ... ;; Loaded file /home/frank/arm/forth.lisp ;; Loading file /home/frank/arm/term.lisp ... ;; Loaded file /home/frank/arm/term.lisp duplicate name: EMIT (riscy.forth) duplicate name: KEY (riscy.forth) The compile was successful. The image size is 6148 bytes ;; Loaded file /home/frank/arm/arm.lisp T
The one line (load "arm") does all the work. The other lines
above are the output from CLISP. It loads the main file,
arm.lisp, which loads the other Lisp files that are needed, loads
the primitives from riscy.bin, and then loads the high-level
Forth files. See lines such as (forth-compile-file "lpc.forth")
You should read through the file arm.lisp. It is very short and
most of it is comments.
After loading the high-level Forth, all the primitives and high-level Forth words exist in a memory image on the PC. Then, this memory image is written to the file combo.bin. It is called "combo" because it is a combination of the primitives and the high-level words. Then, some statistics are displayed and the location counter is adjusted to what should represent blank (unprogrammed) flash memory on the target.
Do not exit from the Lisp. It contains the table holding the addresses of all the primitives and high-level words and it will provide the interactive terminal for communicating with the target ARM processor once the combo.bin image has been burned into the target's flash memory.
The next major step is burn the combo.bin image into the target. For this, use your bootloader (or JTAG connector, or however you ordinarily burn the flash on your target processor). If you use a serial bootloader, you need to configure your serial port first. That will not be described in this document, but there is a page on the website describing it.
I download combo.bin to the target by opening a shell prompt and running make
frank@bed:~/arm$ make combo.dl lpc21isp -bin combo.bin /dev/ttyS0 115200 14746 File combo.bin loaded... Position 0x14 patched: ivt_CRC = 0x94000006 COM-Port /dev/ttyS0 opened... Synchronizing Synchronized 0 Synchronized 1 Setting oscillator Unlock Read bootcode version Read part ID Writing Sector 0: =............................................................................................................................................... Sector 0 written Finished... frank@bed:~/arm$
I am using the Maurer bootloader. See the website for a link to it.
In the command
lpc21isp -bin combo.bin /dev/ttyS0 115200 14746
the "-bin" indicates the file is a binary file, "combo.bin" is the name of the file to be downloaded, "/dev/ttyS0" is the serial port (corresponding to COM1: under DOS), "115200" is the baud rate, and "14746" is the target's crystal speed in KHz. Edit the makefile to adjust the command to suit your situation. Note, if you use the serial port lines DTR and RTS to control the bootloader pin and the reset pin, then you would use a command such as
lpc21isp -control -bin combo.bin /dev/ttyS0 115200 14746
I do not use the DTR and RTS lines with the Olimex lpc2106 board, so
before saying make combo, I move a jumper (shorting block) to the
bootloader position (it grounds port 0, pin 14) and press the reset
button. After make combo finishes, I move the jumper to the normal
run position (and will need to press the reset button again, but I'll
get to that below).
If all went well, the target ARM chip now contains Pygmy Forth for the ARM.
Next, we start the interpreter/terminal from Lisp by typing (interp)
and then start the ARM by pressing the reset button.
(interp) [16]> hello .S Stack: 2231354805 289408121 1 ok 99 98 99 ok .s ".s" ? ok .S Stack: 99 98 99 ok
In the above, Pygmy sent its "hello" greeting out the serial port,
then I typed ".S" to show the top three items on the target's data
stack. Note that .S /always/ shows the top three items whether they
exist or not. Then I put three items on the stack, 99 98 99, that are
easy for me to remember. When I later type .S where I expect the
stack to be clean, the 99 98 99 helps to confirm my expectation.
Then, I typed .s forgetting that I had turned caps-lock off. Then I
put caps-lock back on and typed .S again and the interpreter in
cooperation with the target, showed me the top three items on the stack.
The image size displayed earlier was 6148 bytes. Each of the 16 flash sectors in the LPC2106 chip is 8K bytes. So, the location counter was set (at the end of arm.lisp) to the beginning of the next free sector (which would be sector 1).
In the following, we exit from the interpreter to Lisp by typing a
tilde at the start of the line, followed by Enter, then we check the
value of the location counter (i.e. "here") with *h* (I do not
usually do this, however), then I restart the interpreter and type
.S for comfort.
~ NIL [17]> *h* 8192 [18]> (interp) .S Stack: 99 98 99 ok
Note, the following describes how to erase flash sectors and reprogram them to extend the Forth dictionary. This is how it was done in the generation 1 or 2 versions but is no longer necessary in the current (generation 3) version. In the current version, incrementally defined Forth words go into RAM.
Yes, "here" (i.e. *h*) is pointing to the start of
sector 1. Let's see if it is blank, else we will not be able to use
it.
1 IAPBLANK? . 0 ok
Utoh, it was false. The sector is not blank. So, we must erase it first:
1 IAPERASE . 0 ok 1 IAPBLANK? . -1 ok
The erase command returned an error code of zero (i.e. no error) and then checking the sector with IAPBLANK? answers true. At this point, we can extend the flash memory with new high-level Forth words. The interpreter knows to send a line to the compiler if the very first character on the line is a colon. We "waste" 512 bytes of the flash for each line, so we put as much on the line as possible. Let's define the new word STAR and STARS:
.S Stack: 99 98 99 ok : STAR '* EMIT ; : STARS FOR STAR NEXT ; Programming the Flash for: [: STAR '* EMIT ; : STARS FOR STAR NEXT ;] from address 8192 to 8704 Please wait. Return value of zero means all is well. ok . 0 ok .S Stack: 99 98 99 ok
It looks ok. Now try printing some stars:
STAR * ok 3 STARS *** ok 8 STARS ******** ok 50 STARS ************************************************** ok
So far, so good. Let's see if we can configure the SPI interface (used by the MMC disk) and initialize the MMC disk and then read block 170.
SPI-CONFIGURE ok .S Stack: 99 98 99 ok MMC-INIT . 0 ok .S Stack: 99 98 99 ok 170 BLOCK .S Stack: 98 99 1073749132 ok 20 TYPE EEEEEEEEEEEEEEEEEEEE ok .S Stack: 99 98 99 ok
It appears that block 170 begins with lots of uppercase 'E's. Maybe we can fill the block with uppercase 'K's instead:
170 BLOCK 1024 'K FILL ok UPDATE ok FLUSH ok EMPTY-BUFFERS ok 170 BLOCK 32 TYPE KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK ok 170 BLOCK 1000 + 20 TYPE < 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0> ok 170 BLOCK 500 + 23 TYPE KKKKKKKKKKKK< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0>< 0> ok
The above also shows the use of TYPE as a quick and dirty version of
DUMP. That is, it has no problem showing non-printable characters, it
just displays their hex value between angle brackets.
Of course, the traditional Pygmy DUMP and DU commands also work as
shown below:
170 BLOCK 4 DU 4000188C 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B KKKKKKKK KKKKKKKK 4000189C 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B KKKKKKKK KKKKKKKK 400018AC 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B KKKKKKKK KKKKKKKK 400018BC 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B KKKKKKKK KKKKKKKK ok 170 BLOCK 512 + 16 - 4 DU 40001A7C 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B 4B KKKKKKKK KKKKKKKK 40001A8C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ 40001A9C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ 40001AAC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ ok
That basically concludes the sample session but I'll add a few notes:
If you get stuck, you can get out of (INTERP) by pressing C-c C-c (if
you are in a CLISP buffer within Emacs) or probably with just C-c
(i.e. Control-C) if you are running CLISP directly from a shell
prompt.
Then, type :Q to get back up to the top level of CLISP, then restart
the interpreter with (INTERP).
If loading arm.lisp fails, perhaps due to an error in one of the Forth source code files, the error message gives you a clue by displaying the unknown word and also displaying the last name successfully entered into the name/address table, and also displaying the current file name.
If you forget that you are in the interpreter and execute some Lisp, you'll get errors. Just type a tilde at the beginning of a line to get out of the interpreter and try again.