Sample Session

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.

Environment:

Host
OS
Linux (Slackware version 10) on an x86 PC
Lisp
CLISP
Target
processor
Philips LPC2106 ARM processor with 128K bytes of flash starting at 0x00000000 and 64K bytes of RAM starting at 0x40000000.
crystal speed
14.7456 MHz
board
Olimex LPC-P2106
Serial port
115200 bps, no flow control, DTR and RTS lines not connected

Assemble the primitives:

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


Utoh, it looks like the 'K's got written successfully to the first 512 bytes of the 1024-byte block but not to the last 512 bytes of the block. Well, back to the drawing board. Hopefully the next version of the code I post will have this minor problem fixed (update, yes, this should be fixed in any version dated 20040827 or newer).

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.