Riscy Pygness User Manual

Table of Contents

1 Introduction

Riscy Pygness is a 32-bit multitasking Pygmy Forth for the ARM. It includes full source code for both the host (your desktop PC) and target (your ARM development board). The license is BSD/MIT-like so you can do (nearly) anything you like with it.

It is aimed at relatively small embedded ARM systems rather than desktop ARM systems or large embedded ARM systems running an operating system (OS). Riscy Pygness is a stand-alone system that is its own multitasking OS. The current version needs about 4 KB of flash and 1.5 KB RAM. (The size can be reduced further depending on your needs.) This makes it suitable for use in even the smaller ARM variants such as these NXP (formerly Philips) chips

variantflashRAM
LPC21018 KB2 KB
LPC210216 KB4 KB
LPC210332 KB8 KB

It can address the full 4 GB address space, so it can take advantage of all the flash and RAM available in the larger variants.

During development, the host communicates with the target via a serial port. The host provides the smart terminal and the compiling services. The host can generate a new, customized Forth image for the target.

The Forth itself runs on the target but you interact with it by typing commands on the host, much as you would with a Forth running locally on the host.

As you type each line of Forth words (commands) at the terminal, the line is compiled on the host then transmitted to the target either to be executed immediately (when "interpreting") or to extend the dictionary on the target (when "compiling"). (Yes, in either case, it is compiled on the host.) Numbers typed at the terminal are sent to the target to be put on the target's data stack. Word headers are kept on the host, not the target, and all compilation work is done on the host.

The host and the target, by working together this way, provide the effect of a fully interactive Forth running on the target while conserving the limited resources of the target.

The ideal way to run it is to use Linux or Unix. This can be your main computer or a spare computer or even (via a live CD or a USB stick) a temporary computer.

There is no particular reason you couldn't use a Microsoft OS, but I think you will find it easier to use Linux, at least to start with. If you don't already have a Linux computer, you can boot your computer temporarily to Linux using one of the Live CDs (either from an actual CD or from a USB stick), without disturbing your main computer or its hard drive. Another alternative is to dedicate an old computer to this purpose. *Choosing a computer for the host describes several approaches.

If you are new to computers, Linux, and the command line, it might make reading this manual easier to take a quick look at the Linux and the command line section of the Appendix.

2 Installation

2.1 Choosing a computer for the host

If you already have an Intel (or AMD) i386 machine (i.e., an ordinary "PC") running a 32-bit version of Linux, you are all set.

If not, and if you would rather not replace the OS on your main machine with a 32-bit Linux, then there are two main choices to consider:

2.1.1 Run Linux temporarily on your main computer

There are several ways to do this, such as

  • boot a Linux live CD (such as the Ubuntu 10.04 LTS from http://www.ubuntu.com/desktop/get-ubuntu/download). Note, I recommend 10.04, since that is the version I used to compile the bundle of tools. Still, other versions of Ubuntu, or other live CDs, might work fine.

    You might tire of installing the bundle of tools everytime you boot, so I suggest putting the live CD onto a USB stick. See Putting Ubuntu on a USB stick. That way your the changes you make will be persistent.

  • install Linux on an external disk drive (either a flash USB stick or an external USB disk drive). Then, boot to this external drive whenever you wish to work with Riscy Pygness.

2.1.2 Run Linux on a spare computer

Install Linux on some other computer than your main workstation. Then

  • sit at this other computer to work with Riscy Pygness, or
  • sit at your main workstation and connect to the other computer through your local network. You could think of this other computer as an adaptor that connects your ARM board to your main computer, even a main computer running a Microsoft OS. You would make this connection with the ssh program (the modern equivalent of telnet) or with VNC.

    Once Linux was installed, you wouldn't particularly need to leave a monitor or keyboard attached to the spare computer.

This spare computer can be almost any old junk computer you have lying around. In a pinch, you could get by with 128M of RAM and a small hard drive and possibly run Linux without a GUI (without the X Window system). It needs a serial port, but you could use a USB-to-serial cable.

2.2 Installing the system tools

To run Riscy Pygness, the computer needs to have certain tools installed, such as Tcl, an ARM cross assembler and linker, make, and a downloader (to program a binary image into the ARM CPU's flash memory).

This step typically needs to be done just once.

2.2.1 GNU toolchain for the ARM (and Tclkit)

The easy way is to download http://pygmy.utoh.org/riscy/arm-toolchain.tar.bz2 then uncompress it.

$ wget http://pygmy.utoh.org/riscy/arm-toolchain.tar.bz2
$ sudo tar -xjvf arm-toolchain.tar.bz2 --absolute-names --keep-old-files

The first line above downloads a bundle of tools and the second line uncompresses the bundle and places the tools where they should go.

It installs the following

  • cross-compiling binutils to supply the ARM assembler, linker, etc.
  • Tclkit to supply Tcl (the assembler pre-processor and the Riscy Pygness smart terminal/compiler are written in Tcl)
  • lpc21isp (to download code to the ARM CPU's flash memory)
  • GDB (the GNU debugger for the ARM) in case you wish to single-step through any assembly language code.

Note, this bundle of tools is compiled for a 32-bit version of Linux running on Intel (AMD, etc.) hardware.

The hard way would be to install and/or compile and install those tools yourself, one by one. In which case, you still might wish to download the bundle of tools and then run the following command to see which files and needed and where they go:

$ tar -tf arm-toolchain.tar.bz2

2.2.2 Emacs and Make

You also need to have an editor (I recommend Emacs) and the Make program. Maybe your version of Linux already has one or both of these. You can find out if they are installed by running

$ which emacs
$ which make

If not installed, install them with the package manager that your version of Linux uses. For example, if you are running Debian or Ubuntu, you could install them with

$ sudo apt-get update
$ sudo apt-get install emacs
$ sudo apt-get install make

2.3 Installing Riscy Pygness

Download the current version named something like riscypygness-20101113.tar.bz2 from http://pygmy.utoh.org/riscy/ then uncompress it. Here is an example (but change the file name to use the current version shown on the web site):

$ cd                     # move to your home directory
$ wget http://pygmy.utoh.org/riscy/riscypygness-20101113.tar.bz2
$ mkdir riscy            # create a directory for your Riscy Pygness work
$ cd riscy               # change to that directory
$ tar -xjvf ../riscypygness-20101113.tar.bz2  # uncompress the files

You will need to customize a few variables in makefile to tell it which ARM CPU variant you are using and which serial port is connected to the ARM board. See *makefile variables.

One of the files is named .emacs-example. If you do not already have a .emacs file in your home directory, you could use it to get you started:

$ cp ~/riscy/.emacs-example ~/.emacs

3 Getting started using an existing image

The Riscy Pygness distribution comes with several pre-built, ready-to-run kernel images. If you have an ARM board that matches one of these images, burn the *.bin file into the ARM's flash, then run with the matching *.dictionary file.

For example, if you have the Olimex LPC-P2106 board (http://olimex.com/dev/lpc-p1.html) (also available from Spark Fun http://www.sparkfun.com/commerce/product_info.php?products_id=269), look for the files

kernel-lpc2106.bin
this is the binary image to be burned into the LPC2106's flash

Burn kernel-lpc2106.bin into the LPC2106. You can use whatever method you are already familiar with, or you can use the program lpc21isp. See the Flash utilities section for examples.

This step needs to be done just once (unless/until you generate a new kernel).

kernel-lpc2106.dictionary
this is the matching dictionary file

Run Riscy Pygness via the riscy.tcl program, passing it the name of the dictionary file and, optionally, the name of your serial port.

$ ./riscy.tcl -image kernel-lpc2106  -port /dev/ttyS0

Of course, substitute the correct serial port name if you are not using /dev/ttyS0.

This is a fairly long command line to type. You might prefer to set up a shell script or shell alias to give you a much shorter command to type. I use a script named r which is short for "run" to type the command for me – the distribution includes this example script. So, I start Riscy Pygness interactively by typing

$ ./r

(be sure to edit it for your environment before using it). Or, create a shell alias with a command such as

$ alias r='./riscy.tcl -image kernel-lpc2106 -port /dev/ttyS0'

and put it in your ~/.bashrc file. Then, you can start Riscy Pygness by typing

$ r

4 Source code

4.1 Install Emacs and forthblocks mode

Of course, you can edit your source code files with any text editor. If you have a favorite, perhaps you are a vi enthusiast, feel free to continue to use it. Otherwise you can install Emacs with

# apt-get install emacs

There are some advantages to using the Emacs editor, along with the forthblocks.el extension (included in Riscy Pygness). Forthblocks mode allows you to work with plain text Forth code as if (almost as if) you were working with block files. These are very flexible blocks in that they can be as large or small as you like. See the Emacs section. The beginning of the file forthblocks.el contains details as to how to use and install it.

Even if you are a vi enthusiast, you might like to use Emacs to edit Forth source code, perhaps by installing the Emacs viper package so you can use your familiar vi keystrokes.

4.2 Blocks

4.2.1 blocks for source code

Traditionally, Forth code was kept in "screens" or "blocks". Each was a fixed size of 1024 bytes (characters), presented to the user in an editor that showed 16 lines of 64 characters.

This size is very convenient for source code and encourages modularity and short definitions.

In Riscy Pygness, we fake true 1024-byte blocks by using a text file divided into logical blocks by special comment lines.

Each "block" starts with a special Forth comment in the first line. The left parenthesis must be in the first column, followed by a space and either the word "block" or the word "shadow", followed by a space and a decimal number (the block number). Additional text may follow, but the comment must end eventually with a right parenthesis.

When we LOAD a block, the host program searches for that marker to know where to extract the text to be interpreted.

The procedure filenameFromBlockNumber (near the top of riscy.tcl) maps block number ranges to file names. Edit it if you wish to add additional block files or to change the ranges.

Block numbers should be in numerical order, else searching might fail. However, "holes" (missing block numbers) are ok.

riscy.tcl reads the entire file into the host's memory, but only when the file changes.

Here are some examples of special comment lines that would mark the beginning of a block.

( block 0  )
( block 1   ------------------  load block)
( block 2  miscellaneous)
( shadow 2 )
( block 3  )

When you type a Forth command such as 22 LOAD, the procedure filenameFromBlockNumber finds that block 22 belongs to the file named kernel.fth. If this is the current file, then riscy.tcl has already read it into memory (on the host), otherwise it becomes the current file and is read into memory. Then, using the block comment lines, riscy.tcl finds the range of lines to load.

Using the forthblocks mode in Emacs, a single block is displayed at a time. PgDn (or C-v) moves to the following block. PgUp (or M-v) moves to the previous block. M-a ("a" for "alternate") toggles between a block and its corresponding shadow block. Traditionally, Forth code with short comments goes on the "block" and lengthier comments go on the "shadow".

What I usually do when I start a new Forth file is to write one comment line for a block and another for a shadow, e.g.,

( block 0 )
( shadow 0 )

Then copy and paste the two a bunch of times, producing something like

( block 0 )
( shadow 0 )
( block 0 )
( shadow 0 )
( block 0 )
( shadow 0 )
( block 0 )
( shadow 0 )

Then run the Emacs forthblocks mode command M-x renumber-blocks to fix up the numbers automatically, producing

( block 0 )
( shadow 0 )
( block 1 )
( shadow 1 )
( block 2 )
( shadow 2 )
( block 3 )
( shadow 3 )

See the file forthblocks.el for more details on how to set it up and use it.

4.2.2 blocks for data

Another traditional use of 1024-byte blocks is for data storage. This use is not particularly addressed by the text-file-based block system described in the previous subsection. Nothing precludes the possibility of using certain block ranges for text-file-based blocks for source code storage while using different block ranges for pure 1024-byte data blocks (located perhaps in RAM, flash, or SD cards).

4.3 Text files

Some people apparently really dislike using blocks for source code. If you are one of those people, you could try the hybrid approach discussed above (where you use plain text files for source except that special comments mark the logical blocks) to see if you grow to like it.

Otherwise, for the source for your application, use a plain text file but start it with a single comment such as

( block 2000 -- this entire file file is block 2000 )

then forget about blocks. The entire text file will be in that single block. Then

2000 LOAD

will load the entire text file.

Note, do not do this with the file kernel.fth as that is used to generate the kernel.

5 Creating a new kernel image

Why would you wish to create a new kernel? There are serveral reasons:

  • perhaps a pre-built kernel is not available for your ARM board
  • you wish to add, delete, or modify some of the Forth primitives
  • you wish to move more of your application into the kernel (and thus into flash memory)

The usual procedure is to edit riscy.asm (to change any primitives) and/or to edit kernel.fth (to change any high-level that will be part of the kernel), then to use the makefile to generate the new kernel image binary file (to be burned into flash) and the matching dictionary file.

Be sure to look through makefile and edit any settings that need to be changed for your environment (such as the serial port name or the CPU clock speed), then run

$ make

Which will do any pre-processing and assembly steps and then run riscy.tcl to create the new binary and dictionary files for all the supported boards.

6 Multitasking

Each task has a TCB (Task Control Block) in RAM. Each task also has its own data stack area and return stack area. The task's stacks would not necessarily need to be adjacent to the task's TCB, but that is where we place them. The address of the task is the start of its TCB. That is, the task is its TCB or the TCB is the task.

The UP register (we use R7 for this) holds the address of the current task (i.e., the address of the current task's TCB). UP stands for "User Pointer". It points to task-specific values – values local to the task.

The TCB has 3 named slots plus 5 unnamed slots. Each slot is 32-bits wide, thus the TCB takes 32 bytes. Traditionally, the TCB would have even more named slots. Feel free to add them if your application needs them, but these should do for most applications.

  • the 3 named slots are
    LINK
    this slot holds the address of the next task (the task that will get control when the current task pauses).
    SP0
    this slot holds the starting address (the high address, because the stack grows toward lower memory) of the data stack. It is used by the word SP! to reinitialize the task's data stack pointer.
    RP0
    this slot holds the starting address (the high address, because the stack grows toward lower memory) of the return stack. It is used by the word RP! to reinitialize the task's return stack pointer.
  • The 5 unnamed slots form a save area for storing a task's state when it is not running. These slots save the five registers
    tos
    the cached top of stack value (register R0 or TOS)
    ip
    the instruction pointer register (register R9 or IPTR)
    dstk
    the current data stack pointer (register R10)
    rstk
    the current return stack pointer (register R11)
    rloop
    the current loop counter (register R12)

Traditionally, PAUSE would first store all the state onto the data stack and thus need just one slot to store the data stack pointer. However, to speed up PAUSE, we take advantage of the ARM's store multiple and load multiple instructions, at the cost of several extra slots in each TCB.

There is always at least one task. This is the "foreground" task. It and its TCB are configured automatically. The word PAUSE pauses the current task and turns control over to the next task. Each task, in its TCB, has a LINK slot that holds the address of the next task to run. When there is only one task, that task's LINK slot points to itself. Thus, when PAUSE executes, it saves the state of the foreground task, then immediately restores the state of the foreground task, and thus continues running the foreground task. That is, with only one task, PAUSE jumps back to the foreground task.

If you really don't plan to use more than a single task in your application, you could consider changing PAUSE to a no-op, as it doesn't really shine until more than one tasks are present.

Here is what the TCB looks like, with the offset relative to the start of the TCB (and thus relative to the value in UP):

 +    0   LINK   holds the address of the next task to execute
 +    4          holds saved TOS
 +    8          holds saved IP
 + 0x0C          holds saved DSTK
 + 0x10          holds saved RSTK
 + 0x14          holds saved RLOOP
 + 0x18   SP0    holds address of start of data stack
 + 0x1C   RP0    holds address of start of return stack
often the data stack will start here (at offset 0x20)
with the return stack starting just above the data stack

6.1 Creating a task

There is normally no need to create tasks, as three are created for you automatically.

  • FOREGROUND (also known as TERMINAL)
  • TASK1
  • TASK2

See riscy.asm if you wish to create more or fewer.

Until/unless you initialize and wake TASK1 and TASK2, only the FOREGROUND task runs.

Suppose we want TASK1 to run a Forth word that increments a variable named TICKS every 1.5 seconds.

 VARIABLE TICKS
 : TICKER ( -)  0 TICKS !   BEGIN  1500 MS  1 TICKS +!  AGAIN  ;
 ' TICKER  TASK1  TASK!  
 TASK1 WAKE

Let's take those lines one at a time:

VARIABLE TICKS

this creates the variable (in RAM of course) that will be incremented

: TICKER ... ;

this defines a Forth word named TICKER that first initializes the variable to zero. Then it runs a endless loop that, over and over, kills 1.5 seconds (1500 milliseconds) and increments the variable. Note that we did not put a PAUSE inside the loop. We could have, but it is not necessary in this case because the word MS includes a PAUSE. It is very important that each task PAUSE appropriately, otherwise, once that task gets control, it will never release control and no other task will be able to run. So, if you create an endless loop, if no word executed within your loop calls PAUSE, then you must put an explicit PAUSE in your loop.

' TICKER TASK1 TASK!

this tells the TASK1 what word it should execute. Note that we "tick" TICKER to get its address then store that word's address into TASK1.

Note also that we showed the initialization as being done interactively. This is fine during testing. Normally, though, for an application, you will initialize the task within the definition of another word, probably your application's boot word, e.g.,

: MY-APP ( -)  
  ...
  ' TICKER  TASK1 TASK! 
  ' SOMEOTHERWORD  TASK2  TASK!
  TASK1 WAKE  TASK2 WAKE
  ...
;

After initializing the task with TASK!, the task is still not running until it is awakened with WAKE. You can use the word AWAKE? to see whether a particular task is running. You can verify TICKER in TASK1 is running by checking the variable (TICKS ?) or by asking the task whether it is awake or not (TASK1 AWAKE? .).

TASK1 WAKE

this starts the task running (by inserting it into the active task list).

6.2 Stopping and starting a task

The previous section showed how to create a task and how to start it. To stop a task by name, say TASK1 SLEEP then to wake it again, say TASK1 WAKE. Note that you cannot put the TERMINAL (i.e., the FOREGROUND) task to sleep.

A task can put itself to sleep with STOP.

7 The Forth model

7.1 Brief description

  1. during development the address space is split between
    • Flash (the kernel)
    • RAM (the extensions)
  2. host side "smart terminal" is written in Tcl
  3. primitives are written in assembly language and assembled with the GNU ARM assembler.
  4. the host program runs in one of two modes;
    • generate a kernel image to be burned into the ARM's flash

      This is a batch mode. It runs entirely on the host and does not need a connection to the target. It combines the primitives with high-level Forth and saves the result into two files:

      *.bin
      a binary image file, e.g., kernel-lpc2294.bin, to be burned into the target's flash.
      *.dictionary
      a matching dictionary file, e.g., kernel-lpc2294.dictionary, for use when running in interactive mode, to map Forth word names to target addresses.
    • run interactively

      This is the interactive mode. This does require a connection to the target. It is started by passing it the name of a kernel image, e.g., kernel-lpc2294, which causes it to load a dictionary file, e.g., kernel-lpc2294.dictionary, that matches the binary image running on the target.

      ./riscy.tcl -image kernel-lpc2294 -port /dev/ttyS0

      (Well, really, you would set up a shell script or shell alias to type the above line for you. See elsewhere in this manual.)

      In this mode, the user's keyboard input is executed by the target. New Forth words can be defined. Block files can be loaded. All new words defined reside in RAM on the target.

      The main development mode this is: Define and exercise new words. From time to time, when you are satisfied with the new words, generate a new kernel (to be burned into flash) that includes the new words.

      When running interactively, all numbers go to the target's data stack (when the host needs a number, it asks the target to send it back).

7.2 Additional notes

  • Symbolic constants

    Suppose you would like to refer to the address of one the many ARM CPU configuration registers, such as PINMODE3. That configuration register address is 0xE002C04C. In Forth, you could say

    $E002C04C

    but your code would be more readable if you referred to it by name as in

    PINMODE3

    Yet, there are so many of these configuration registers (see the file equates-lpc2xxx.asm), that you would hate to use dictionary space on the target to define them all.

    Fortunately, you do not need to define them. You can just use them. riscy.tcl knows how to look them up automatically.

    As riscy.tcl collects each word as it is compiling, it first checks to see if it an immediate word. If so, it runs just on the host. Otherwise, it checks to see if it is a word that has been defined for the target. If so, it compiles the word to be sent to the target (either to be compiled or interpreted on the target). Otherwise, it checks the list of known constants (where, for example, it would find PINMODE3). If so, it compiles the literal number. Otherwise, tries to view the word as a number of some sort. If so, it compiles the literal number. Otherwise, it reports an error.

    Here are some examples of literal numbers:

$03F8
't
79
  • Riscy Pygness has some colorForth and/or cmFORTH features such as
    • "tail call optimization" (i.e., dropping EXIT when possible by changing the previous call to a jump).
    • capability of having multiple entry points (Forth word names) and multiple exit points for a Forth word.
    • FOR ... NEXT
    • simple recursion (with no "smudging" of word names), e.g., the first and second versions below are equivalent:
      : COUNT-DOWN ( u -)  ?DUP IF DUP . 1-  COUNT-DOWN ; THEN ; 
      
      : COUNT-DOWN ( u -)  BEGIN ?DUP WHILE DUP . 1- REPEAT ; 
      
  • Threading

    Tokens use 13 bits (the most significant 13 bits of the token) to hold a token number, plus 3 bits for flags. The 13-bit token number is used to index into a table of addresses to look up a full 32-bit address (in either the flash token table or in the RAM token table).

  • Flags
    • Bit 0 of the token is the Exit Flag
    • Bit 1 indicates whether to use the flash token table or the RAM token table,
    • Bit 2 of the token is the Enter Flag (it indicates whether the word is a primitive or a high-level Forth word).
  • Heads

    Heads are stored only on the host, thus taking no space on the target chip.

  • New definitions are compiled by the host then sent to the target's RAM.
  • Typical usage

    Interactive poking at your hardware and testing snippets from the keyboard are perhaps the strongest advantages of Forth for developing embedded systems. These definitions go into RAM. We work awhile and then, from time to time, the results are incorporated into the main source code and a new kernel (flash image) is generated.

7.3 Internals and Design

Riscy Pygness is a member of the Pygmy Forth family but is quite different from the 16-bit DOS Pygmy Forth. It takes a number of ideas from Charles Moore's colorForth, yet uses conventional Forth-style notation, rather than using color.

There are many possible ways of threading the words within a higher-level word. The method in this version is token-threaded code with 16-bit tokens. This is a compromise between speed and conciseness that favors conciseness. By using 16-bit tokens, a lot of code can be packed into the available flash (and RAM). We could modify the threading to favor speed or even to favor greater conciseness. Generally, though, I lean toward the usual Forth philosophy of ignoring performance until the application is working and then convert just the bottlenecks to CODE words (i.e., Forth words written in assembly language).

The Forth image that is burned into flash consists of the primitives (which are written in assembly language and assembled by the GNU ARM assembler) and the high-level Forth (which is compiled by a Tcl program). The Tcl program takes the binary image and a list of symbols and addresses produced by the assembler and merges them into an in-memory image on the host, along with the higher-level Forth, then writes that image to a file named, for example, kernel-lpc2103.bin, ready to be burned into flash on the target. At the same time, it writes a matching dictionary file, such as kernel-lpc2103.dictionary that the host uses to map Forth word names to their addresses on the target.

Typical development style is interactive, exercising the words in the dictionary and extending the dictionary on the fly to test and experiment. From time to time, the high-level Forth that will go into flash is extended, based on these interactive sessions, and a new kernel (flash image) is created. This cycle is repeated until the application is finished and all of it is in flash.

In other words, you start with a base image in flash and do most of your work interactively (with extensions going into RAM). Periodically, you generate a new base image to be burned into flash that includes your new work.

8 Documentation for individual program files

This section holds documentation specific to various files in the distribution. For better understanding of a system program (such as make or objcopy) you can view the manual for that program with a command such as one of the following

  • $ man make
  • $ info make

or by googling.

8.1 makefile

The work of building the various parts of Riscy Pygness is controlled by a makefile named makefile. The program make (as guided by the file makefile) takes care of the details.

You need to look over, and possibly change, several variables for your system.

See the section on makefile variables for the ones you may need to edit.

To assemble a file named led1-2294.asm, creating an Intel hex file suitable for downloading to the LPC-L2294 ARM board, type

$ make led1-2294.bin

Then, or instead, you could type

$ make led1-2294.dl

in order to assemble, if necessary, and then download using the lpc21isp downloader.

To assemble riscy.asm to produce the riscy.bin file of primitives that will go into the Forth kernel, just type

$ make

Make will assemble riscy.asm and combine it with some high-level Forth to produce a kernel consisting of a *.bin file and a matching *.dictionary file.

The process is slightly more complex in that make does not directly assemble riscy.asm. Instead, riscy.asm serves as a template from which board-specific versions are created automatically. It is these resulting files (such as riscy-lpc2294.asm) that are assembled. This lets the makefile create kernel images for all the supported ARM/board variants at one time. If, for some reason, you would like to limit the work to just a single board variant, you could type

$ make lpc2106

8.1.1 downloading to the flash

Then, burn the *.bin (e.g., kernel-lpc2103.bin) into the ARM's flash with

$ make kernel-lpc2103.dl

Note that some flash utilities (for example, the lpc21isp flash utility when given the -control option) will control the serial port's DTR and RTS lines in an attempt reset the ARM while holding the bootloader request line low. This works only if your ARM board supports it. For example, on the Olimex LPC-P2378 board, short the two jumpers ISP_E and RST_E if you wish this to work. On hardware that does not support this, you need to change jumpers and/or push reset buttons manually in order for the bootloading to work.

On the Olimex LPC-L2294 board, it is probably best to leave RST_E open and to manually short the ISP_E jumper and press the reset button when downloading a program. Then, open the ISP_E jumper and press the reset button once again so the newly downloaded program can run.

8.1.2 makefile variables

Edit the following variables in makefile depending on the particular ARM chip, serial port, path to the assembler, etc. that apply to your environment.

The only ones you normally need to verify/edit are CCLK and PORT.

CCLK
the CPU clock speed of your board in KHz. This is used by both the lpc_prog and the lpc21isp flash utilities. Note that this is the external crystal speed when using the LPC2106 and some others but is always 14748 when using the LPC2378.
PORT
the full path of your PC's serial port, such as /dev/ttyS0 or COM1: for the first serial port.
BIN
the full path to the directory that contains the ARM assembler (arm-elf-as) and other binutils utilities such as arm-elf-objdump.
DLBAUD
the serial rate you wish to use for downloading. This defaults to 38400. Other possible speeds are 115200, 57600, 19200, 9600, 4800, etc.
PREASM
the full (or relative) path to the preprocessor that converts *.asm files into *.s files. Normally the executable file is preasm.tcl but you could write a replacement in Python or sed or whatever if you prefer. Its main purpose at the moment is to replace semicolons (my preferred comment character) with at-signs (the GNU ARM assembler's preferred comment character).
LNKFLAGS
this includes the link script as well as flags.
ASMFLAGS
flags passed to the assembler.
LNKFLAGS
flags passed to the linker.
ZIPFILES
a list of the files to be bundled together to create a Riscy Pygness distribution when I say make bzip. You would not ordinarily need to do this.

8.2 riscy.tcl

riscy.tcl is a Tcl program that runs on the host. It implements the host side of Riscy Pygness, i.e., the smart terminal.

8.2.1 starting the program

riscy.tcl program serves two purposes:

  1. Create a kernel image file suitable for burning into the ARM chip's flash. This step also creates a matching dictionary file. The command line for this would be something like

    $ ./riscy.tcl -flash 1

    but you would not run this manually. It would be done automatically by the makefile.

  2. Run Riscy Pygness interactively, using the dictionary file previously created, and the matching kernel image file previously burned into the ARM chip's flash. For example, if the kernel image and dictionary files are named kernel-lpc2106.bin and kernel-lpc2106.dictionary, your command line would be

    $ ./riscy.tcl -image kernel-lpc2106 -port /dev/ttyS0

    to run interactively (using the /dev/ttyS0 serial port). Of course, you could set up a shell script or alias to reduce your typing. See the shell scripts r and burn for examples.

See below for more information and/or run riscy.tcl file with no arguments to see a help message describing how to use it.

8.2.2 Variables

There are certain variables (and one procedure) in riscy.tcl that you should verify or change to suit your environment. All of these are located near the top of the file.

isUnix
Set to 1 to run on Linux (or other Unixes). If you wish to try running this on a Microsoft OS, change its value to 0.
::debug
Leave this set to 0 for normal use. Changing it to 1 causes (way too many) debugging messages to be printed. This setting can also be changed interactively with the Forth word DEBUG.
::serialPort1
Set this to the name of the serial port you use to connect to the target ARM board. Under Linux, this is usually either /dev/ttyS0 or /dev/ttyS1. Instead of setting it here, you can can specify it with the -port command line option, which will override the setting inside the program.
::baudrate
Set this to the same baud rate that the ARM chip is set to use. For the pre-built kernels, this is 38400 bps.
filenameFromBlockNumber
This procedure maps Forth block numbers to file names. You may add additional ranges and file names or change the existing ranges. It is best not to change the first one, which maps the first thousand blocks (0 through 999) to the filename kernel.fth.

8.3 riscy.asm

There is nothing to customize in this file for normal use. Of course, if you wish to add or delete or modify the primitives, this is where to do it.

This file is not assembled directly. Instead, it serves as a template file. The makefile takes riscy.asm and modifies the following equate

.equ <BOARD>, 1

to replace "<BOARD>" with the appropriate symbol for a specific ARM/board variant. For example, when assembling the primitives for the Olimex LPC-P2106 board, makefile replaces "<BOARD>" with "lpc2106" so the line becomes

.equ lpc2106, 1

in a new file named riscy-lpc2106.asm. The file riscy-lpc2106.asm is the one actually assembled (after pre-processing it).

If you add a new ARM/board variant, you will need to pick a new symbol to represent that variant, then add it to makefile and also to riscy.asm.

8.4 custom include files

Rather than putting lots of .ifdef statements throughout riscy.asm to adjust for the various ARM/board variants, we factor the differences for each variant into a custom include file (with a name such as custom-lpc2106.asm or custom-lpc2294.asm). A single conditional directive in riscy.asm picks the appropriate include file.

9 Support Software

9.1 Binutils (assembler, etc.)

The GNU ARM toolchain supplies the assembler (and linker and debugger, etc.).

The full GNU ARM toolchain (with gcc, the C compiler) is not needed, just the binutils package, which is easy to install. Binutils supplies the assembler and linker and various object file tools. It can be installed as a binary package – there are a number of sites on the web supplying precompiled ARM toolchains or you can compile binutils yourself.

Note, the GNU tools can be given a prefix when you compile them so that there is no conflict between the ones for the ARM and the ones for your native Linux system. For example, I use 'arm-elf-' as the prefix, so my ARM assembler is named 'arm-elf-as' while my native x86 assembler is named simply 'as'.

Also, note that the assembler is used only for the primitives. Some Forth implementations cram the high-level definitions into the assembly source in a highly unreadable fashion, but Riscy Pygness expresses the high-level definitions in straightforward Forth.

9.2 Gdb or Insight

It might be convenient to use the GNU debugger in a few cases to trace through and correct certain primitives.

To use the GNU debugger, you also need to install the gdb package for the ARM.

Insight is a GUI front end to gdb.

Because the version of GDB included with Insight is on old version, the bundle of tools includes just the newer GDB. It works well from within Emacs, so we don't need Insight.

9.3 Tcl

The host-side of Riscy Pygness is written in Tcl. See riscy.tcl.

We use a recent version of Tclkit for a 32-bit i386 Linux. It is stored in /usr/local/bin and symlinked (soft linked) to the name /usr/local/bin/tclkit. If you use a different path to Tcl, then edit the top line of riscy.tcl.

9.4 Flash Utilities (downloaders)

There are several ways to burn the Forth image into the target's flash.

The NXP LPC ARM (and other manufacturers') chips contain a serial bootloader.

A serial bootloader is a program residing in the target chip's flash that allows a flash utility on the host (the desktop PC) to burn a program into the target's flash memory via a serial line.

The host-side program is often called a flash utility or a downloader.

NXP has an official flash utility that runs only on a Microsoft OS. Fortunately, several people have written flash utilities that are more platform independent.

Another way of burning a program into flash is to use a JTAG in connection with a program on the host such as OpenOCD.

I have used the serial flash utilities by Martin Maurer and by Edwin Olson very happily, and sometimes OpenOCD with an Olimex JTAG cable. lpc21isp is included in the bundle of tools.

9.4.1 running lpc21isp

I download code to be burned into the onboard Flash using the chip's built-in bootloader combined with Martin Maurer's lpc21isp flash utility program that runs under either Linux or Microsoft Windows.

Martin Maurer's lpc21isp is available from http://tech.groups.yahoo.com/group/lpc21isp (the Yahoo lpc21ispgroup) and a version is also included on the Riscy Pygness web site.

I run it to download the binary file kernel-lpc2106.bin, with the command

$ lpc21isp -verify -bin kernel-lpc2106.bin /dev/ttyS0 38400 14746

where /dev/ttyS0 is the serial port I am using and 38400 is the baud rate and 14746 is the speed of the crystal in KHz of the board I'm using (i.e., 14.746 MHz).

9.4.2 Compiling lpc21isp

Download the source, unzip it into /usr/local/src, then compile it, then soft-link it into the /usr/local/bin directory.

It is a very easy compile. Just unzip, cd to its directory, then type make. Then, make sure the resulting binary is in your path. (Of course, you have to have make and a C compiler installed.)

What I do is to unzip it (untar it, whatever) into /usr/local/src/ which creates a subdirectory named lpc21isp. Then I rename that subdirectory to include its version number, e.g.,

# mv lpc21isp lpc21isp_179

Then I cd to that directory and run make. This produces the binary /usr/local/src/lpc21isp_179/lpc21isp. Then I soft-link it to /usr/local/bin/lpc21isp so it will be in my path.

# ln -s /usr/local/src/lpc21isp_179/lpc21isp /usr/local/bin/lpc21isp

I am using version 1.79. Here is the source code:

http://pygmy.utoh.org/riscy/lpc21isp-1.79.tar.gz

You can look for newer source either in the Yahoo lpc21isp group or at http://sourceforge.net/projects/lpc21isp/.

9.4.3 Example of programming the LPC2106

First, orient your board so "Olimex" reads correctly from left to right, then the serial connector is at the top left and the power connector is at the top right and the blank wire-wrap area is at the bottom.

Here is how I have my board jumpered:

  • Debug (upper right corner) is open.
  • J1 (middle of top edge) is open.
  • LED_J (just to the right of the red LED) is shorted.
  • BSL (top left) is closed when programming the flash and open when not programming the flash

Then I open a terminal and get my lpc21isp command ready to go, but don't hit RET yet. Then, I short the BSL jumper. Then I press RET at about the same time I press and release the reset button on the board (RST is the small push button just to the right of the CPU chip).

After successfully downloading to the flash, the lpc21isp program may say it has jumped to the program, but of course it has not, and cannot, because BSL is still shorted. Once the flash has been programmed, I open the BSL jumper and press the reset button again. If all is well, the LED should blink 6 times (because riscy.asm contains the code to blink the LED 6 times).

9.4.4 Other bootloader flash utilities

lpc2kpgm
http://www.pjrc.com/arm/lpc2k_pgm
lpcprog

http://www.blisstonia.com/software/lpc_prog_20060709.tgz by Edwin Olson, license GPL, per his posting to the lpc2000@yahoogroups.com mailing list. (There may be newer versions by the time you read this). The '2006' is a typo in the file name and the referenced file is the version that was current as of July 9, 2007. (I have used the earlier version of 20070503 successfully with the lpc2378.)

9.5 Make

make is a program for executing commands based on declared dependencies. It allows you to say

$ make xxx.bin

and have the appropriate commands executed automatically to

  • preprocess xxx.asm to produce xxx.s
  • assemble xxx.s to produce xxx.o
  • link xxx.o to produce xxx.elf (and produce a list of symbols)
  • run objcopy on xxx.elf to produce xxx.bin

However, make checks the timestamps on the files and only does the above steps that need doing.

See the file makefile in the distribution for the details. You will need to check the settings and adjust them for your environment, as described in the makefile variables section. For example, you will need to set the BIN variable to point to the correct path to your arm-elf-as file.

9.6 Terminal

You can run the smart terminal (i.e., riscy.tcl) directly from a terminal shell (a command line prompt) but it is often more convenient to run it from within Emacs. Emacs provides command history and command completion. Emacs even provides a text editor. See Emacs for some details about how to learn and use it.

10 Limitations and cautions

10.1 Switching back to interactive mode with ;;

When you are typing interactively or when you LOAD from a block, riscy.tcl needs to be able to tell whether to compile or interpret. It does this a string at a time. When typing, the string is the single line. When you LOAD a block, the string is the entire block.

The rule that riscy.tcl follows is that it starts each string in interpret mode and stays in interpret mode unless it sees a colon. Once it sees a colon, it switches to compiling mode and stays in compiling mode until it reaches the end of the string unless it sees a double semicolon (;;).

You rarely need to use ;;. Mostly, it would be used on a block that defined one or more words and then needed to switch back to interpreting. For most blocks, you would stay entirely in one mode or the other and would not need to use ;;.

Here is an example typed at the keyboard that would not work:

: STARS  ( n -)  FOR  '* EMIT  NEXT  ;   3 STARS

Because it is all typed on a single line, it would switch to compiling mode when it saw the : and stay in compiling mode until the end of the string, thus it would not execute the 3 STARS part. You might be asking why the semicolon does not switch back to interpreting mode. The answer is that a single Forth word can have multiple exits.

The solution to the first example could be

: STARS  ( n -)  FOR  '* EMIT  NEXT  ;   ;;  3 STARS

but that is awkward and unnecessary. Instead, when typing interactively, just use two lines:

: STARS  ( n -)  FOR  '* EMIT  NEXT  ;   
3 STARS

and no problem.

10.2 Interactive strings

If you type

 " THIS IS A STRING"

it will "interpret" the string, leaving the address of the string on the data stack. However, that won't be of much use, as the next thing you type will overlay the string. So, don't expect this to work

 " THIS IS A STRING"  
 COUNT TYPE

The solution in this case is to do it all on a single line:

 " THIS IS A STRING"  COUNT TYPE

Of course, you could compile a string in a colon definition and the string would not get overwritten. You could do this:

 : MSG ( - a)  " THIS IS A STRING"  ;
 MSG COUNT TYPE
 MSG
 COUNT
 TYPE

10.3 C,

For now, at least, C, works only in non-interactive mode. So, it could still be useful in populating a table in flash, e.g.,

  TABLE XX
    $46 C,
    $47 C,
    $48 C,
    $49 C,

but cannot be used or tested when running interactively (an error message will suggest using , instead).

The work-around is to use , instead of C, and pad out the tail end of the table to a full-word boundary, e.g.,

  TABLE YY
    $49484746 ,

(Remember, the ARM in the LPC chips is little endian.)

10.4 Constants and LOAD

When generating a kernel image, LOAD requires an actual number, rather than a Forth constant. So,

3 LOAD

appearing on a LOAD block would cause trouble if 3 had been defined as a constant.

We are unlikely to need to load block -1 or block -2, so -1 and -2 are defined as constants.

We could define 0, 1, 2. 3 or whatever as constants if we changed the numbers passed to LOAD to actual numbers, e.g.,

03 LOAD

would work, providing there was no Forth word defined named 03.

11 Appendix

11.1 Linux and the command line

To read this manual and to work with Riscy Pygness, it helps to have a certain familiarity with computers, Linux, and the command line. You probably have that already if you are working with target ARM boards.

Nevertheless, here is a quick rundown on various topics with suggestions on where to get more information if you need it.

11.1.1 terminal

This gives you a place to type commands. (Actually, the commands are typed into the shell program, typically bash, that is running in the terminal.) I usually run the program gnome-terminal for this, but any such terminal program will work.

In this manual, often the commands to be typed will be shown preceded by $ or #. Do not type this initial $ or #. It merely represents the terminal's (the shell's) prompt. # indicates the command is typed as the superuser. $ indicates the command is typed as an ordinary user. Note that a # appearing later in the command is completely different: it means the rest of the line is a comment (and you would not type the comment).

11.1.2 command line

See terminal above.

11.1.3 command prompt

See terminal above.

11.1.4 shell

See terminal above.

11.1.5 directory listings

To list the contents of a directory (in a terminal), type

ls -als

To see the various options for the ls command, type

man ls

I usually set up a dir alias so that when I type dir, what gets executed is ls -als. Such aliases are typically set up by editing your ~/.bashrc file, but they can be set up on the fly at a command prompt, e.g.,

alias dir='ls -als'

11.1.6 becoming root

Some commands need to be typed as the superuser (also known as root).

Some Linux distributions use a root account with a password. In that case, type su then enter the password when prompted.

In other Linux distributions, you may need to type something like:

 $ sudo -i

or

  $ sudo su

Otherwise, look for how to do it in your distribution's documentation or on Google.

11.1.7 unzipping

I use this as a general term for uncompressing a compressed file, regardless of whether it was zip'd, tar'd, gzip'd, bzip2'd, etc. The extension on the file generally indicates which program you should use. Here are some examples:

xyz.zip
unzip xyz.zip
xyz.tar
tar -xvf xyz.tar
xyz.tar.gz
tar -xzvf xyz.tar.gz
xyz.tar.bz2
tar -xjvf xyz.tar.bz2

If in doubt about the type of file, run the file command on it. E.g., file xyz.zip should indicate that it is a zip file.

11.1.8 soft linking

This is sometimes referred to as a symlink or a symbolic link. It let's you store a file somewhere (perhaps /usr/local/src/lpc21isp_179/lpc21isp) yet refer to it via a directory that is in your path, such as /usr/local/bin/lpc21isp.

We could set up the above link this way (as root):

# ln -s /usr/local/src/lpc21isp_179/lpc21isp  /usr/local/bin/lpc21isp

If we later install a newer version, say as /usr/local/src/lpc21isp_180/lpc21isp, we can change the soft link with

# ln -sf /usr/local/src/lpc21isp_180/lpc21isp  /usr/local/bin/lpc21isp

(Here we added the -f option to "force" the overwrite of the existing link.)

Another place we do this is with Tclkit.

11.1.9 path

This refers to a file name, including the directory information needed to find it. The path can be absolute, such as /usr/local/bin/tclkit (note the leading /) or it can be relative to the current directory, such as tclkit (note the absence of a leading /).

11.1.10 target

This is your ARM board

11.1.11 host

This is your Linux desktop (or laptop or whatever) computer.

11.1.12 local documentation

The typical Linux computer is filled with documentation.

  • Look for a doc directory, e.g., /usr/share/doc/ and then for a subdirectory named after the program you want documentation for, e.g., /usr/share/doc/openocd/.
  • the man command (short for "manual"), e.g., man openocd or man bash or man ls
  • the info command, e.g., info emacs
  • the apropos command, e.g., apropos terminal, to give you clues as to which programs on your system my be appropriate.
  • the search function within your Linux package manager (such as synaptic in a Debian or Ubuntu system). This can give you ideas as to what programs (already installed or not) might be useful.

11.1.13 external documentation

Google or some other search engine should take care of anything else. For example, in the Getting started section, there is a mention of shell aliases. If you do not know what that means, you could Google for "shell aliases".

11.1.14 keystrokes

This manual uses the Emacs convention for representing keystrokes. For example, C-c means to press and release the "c" key while holding down the Control key. Below are some more examples, but for a full description, run the Emacs tutorial (from within Emacs).

C-h
while holding down the Control key, press and release the "h" key.
C-ht
Control h followed by t (within Emacs, this starts the tutorial)
RET
press the Return (or Enter) key
M-x
while holding down the "meta" key (i.e., the Alt key on most keyboards), press and release the "x" key.

11.2 Putting Ubuntu on a USB stick

Download a copy of Ubuntu 10.04.1 (the 32-bit i386 desktop version) available from http://ubuntu.com. Save this to disk. It is a CD image file named something like ubuntu-10.04.1-desktop-i386.iso.

Write that CD image file to a blank CD-R disc to create a bootable CD. This installation CD is also a live CD that you can boot and run without disturbing your computer's hard drive.

Boot a PC with the Ubuntu Linux installation CD. You may need to alter your computer's set up settings to allow booting from a CD. Some computers, such as HP, allow you to bring up a boot menu by pressing Esc or whatever as you power up the computer.

Plug a flash USB stick into your PC.

While running Ubuntu, click on the "System" menu at the top of the screen. Click on "Administration". Click on "Startup Disk Creator".

You will see a window titled "Make Startup Disk". In the "Source disc image (.iso) or CD:" field, choose the CD you booted from, or else choose the CD image file (ubuntu-10.04.1-desktop-i386.iso).

In the "Disk to use:" field, select the flash USB stick. If you have more than one USB disk connected, be sure to choose the correct one. Click on the "Erase Disk" button.

Under the "Stored in reserved extra space" radio button, slide the "How much:" slider to as reasonable size. I would give it everything over 1G. So, if you are using a 2G USB stick, give it 1G. If you are using a 4G USB stick, give it 3G. Then click the "Make Startup Disk" button.

When it finishes, you should have a USB stick that can be used to boot the computer. Furthermore, it is a USB stick which will save your settings and added software. It becomes an entire Riscy Pygness development system that you can carry on your key chain (of course it needs a real computer, if only temporarily, to do anything useful).

You only need to do this step once, so borrow a PC if yours does not have a CD drive.

11.3 Board notes

11.3.1 Olimex LPC-P2103 board

Riscy Pygness works well on this chip and board. The main Riscy Pygness download also includes led1-2103.asm as a small test program to flash the Olimex board's LED, as a way of verifying the board and your connections to it.

What about board jumper settings? This is what worked for lpc21isp:

DBG_E
shorted or open
JTAG cable
plugged in or not
JRST
open
BSL
shorted when running lpc21isp but open to then run the Forth

Note, however, that if OpenOCD had been running, you have to power off the ARM board after killing OpenOCD. It is not enough to merely press the reset button; the board must be power cycled.

For JTAG, something like

$ openocd -f interface/olimex-jtag-tiny.cfg -f target/lpc2103.cfg

might do it. Note, though, that the lpc2103.cfg file has this line

flash bank lpc2000 0x0 0x8000 0 0 0 lpc2000_v2 12000 calc_checksum

but my Olimex LPC-P2103 board has a 14.7456 crystal.

So, I created a custom lpc2103.cfg file in my working directory that changes the flash line to

flash bank lpc2000 0x0 0x8000 0 0 0 lpc2000_v2 14746 calc_checksum

and I also added this line

jtag_khz 50

to slow down the interface enough that it would work.

For this to work, I short the DBG_E jumper (and, of course, I plug in the JTAG cable).

Note, sometimes I need to start the openocd program more than once before it finally comes up without errors. I suppose I could make it more likely to work by reducing the jtag_khz 50 line even further.

The button on this board is normally open. The button signal is pulled up, so it is normally high. When the button is pressed, it shorts the button signal to ground. The button signal is connected to U1 (the LPC2103 chip) pin 45, P0.15/EINT2/RI1.

See custom-lpc2103.asm. That pin (P0.15), as with all the I/O pins, has been configured as an I/O pin and is an input pin, which is just what we want. We also use the fast I/O facility.

So, for example, to read the button we could use

  : BIT15 ( - mask)  $00008000  ;
  : BUT? ( - f) FIO0PIN @ BIT15 AND 0= ;  ( returns true if the button is pressed)

11.3.2 LPC2106

Here are some thumbnails (just click on them to see the full size images) of the Olimex LPC-P2106 board and my MMC/SD interface to it.

11.3.3 LPC2378 (LPC23XX)

The main Riscy Pygness download includes led1-2378.asm as a small test program to flash the Olimex board's LED.

Pin-out diagrams for the connectors on the Olimex LPC P2378 development board.

11.4 Tools summary

To generate Riscy Pygness from the bottom up, the tools needed are

binutils for the ARM
Note this provides the cross-assembler (and the cross-linker, etc.) It runs on the host but handles assembling and linking for the target ARM CPU. This package provides the assembler, linker, object file manipulator, etc. It is very easy to compile the binutils package. See the GNU Toolchain section.
make
One way of looking at the make program is that it remembers the complicated steps that need to be taken so that you don't have to remember them. The file makefile is where these details are stored.
flash utility
You need something to burn a binary image into the ARM's flash (OpenOCD with JTAG, lpc21isp, etc.). I usually use lpc21isp for this.
Tcl
I use Tclkit, a single-file distribution of Tcl/Tk. (We don't need Tk.) There are versions of Tclkit for lots of different machines. I put the one for my machine in /usr/local/bin/ and symlink it to the name tclkit. (See the first line in the file riscy.tcl.)

11.5 FAQ

Where is the Forth compiled?

The Forth code is compiled on the host (i.e., the desktop PC). The Tcl program riscy.tcl serves two purposes, controlled by options on the command line.

  • generate an image that can be burned into the ARM's flash

    It creates two files, e.g., kernel-lpc2106.bin (to be burned into the flash) and kernel-lpc2106.dictionary (to be used by riscy.tcl when running interactively).

    This can be thought of as a batch mode. It combines the primitives with some essential high-level Forth code (plus any additonal high-level Forth code you wish to include).

    The primitives are created by assembling the file riscy.asm (actually the makefile takes care of this by assembling an automatically-generated board-specific variant of riscy.asm such as riscy-lpc2106.asm).

    The high-level Forth code includes some essentials required to make a kernel that can extend itself. It can also include any additional high-level Forth code, perhaps your entire application, or perhaps just the well tested parts of your application.

    For example, the file kernel.fth contains the essential code needed for the kernel. Its block 1 is its load block. If your application is in the same file and you have edited block 1 to also load your application, you could generate a kernel image with

    ./riscy.tcl -flash 1
    

    but you would ordinarily just run make and let make generate the kernel image automatically. See makefile, riscy.tcl, and riscy.fth for more details.

  • run interactively

    This provides the smart terminal to let you interact with your ARM board. When you start it, you specify the image on the command line. For example, if you created a kernel image named kernel-lpc2106 (consisting of the two files kernel-lpc2106.bin and kernel-lpc2106.dictionary), and you are using the first serial port, you could start it with

    ./riscy.tcl -image kernel-lpc2106 -port /dev/ttyS0

Give me a quick overview of Riscy Pygness

The Riscy Pygness system involves software on the host and on the target:

target

A Forth image that runs on a target board (with an ARM CPU).

The image can be thought of as having 3 pieces:

  • primitives written in ARM assembly language in flash,
  • high-level Forth in flash,
  • high-level Forth in RAM.
host

An assembler and compiler and smart terminal that run on the host (i.e., your desktop PC).

The assembler and compiler let you generate new Forth images for the target's flash and the compiler lets you extend the target's Forth dictionary by defining new words interactively into RAM.

The smart terminal provides the communication link over a serial port through which you interact with the target, exercising words in the target's dictionary and/or defining new words.

Do I need to install the entire GNU Tool Chain?

No, but you must install at least binutils for the assembler and related utility programs if you wish to make changes to the primitives (in riscy.asm).

You do not need to install GCC.

Do I need to install Tcl on the host?

Yes. You need Tcl but not necessarily Tk. The easiest way is download a version of Tclkit and (as root) put it in the directory /usr/local/bin/. Then soft link it to the name tclkit. The top line of riscy.tcl expects it in that location with that name. Otherwise, edit the top line of riscy.tcl accordingly.

Are the new high-level Forth words stored in on-chip Flash?

As you work interactively, new words that you define are stored in RAM. If you reset your target board or power it off, the definitions vanish from the target's RAM. So, rather than typing lots of definitions interactively, you should type them into a file and then load them from the file. For example, riscy.tcl maps (allocates) the block range 2000 through 2999 to the file s2.fth. You could use this file for your application. Set up block 2001 as your load block. Put some code you wish to load into RAM into block 2008. Then, say 2008 LOAD to load block 2008. (Eventually, you would edit block 1 in kernel.fth so it would load your application when it creates a kernel image.)

As you finish testing parts of your application, from time to time you will want to generate a new Forth image for the flash memory that will contain your new/tested words.

Once the compiler combines the primitives with the high-level Forth words and produces a kernel image that you burn into the Flash, can you extend the system with additional words interactively?

Yes! When you are in the interpreter and type a colon at the beginning of a line, the interpreter knows to compile that line (thus updating the target image on the host) and then to send that target image extension to the target for it to program into its RAM. Also, you can compile a region (some text that you have highlighted) in your editor on the host (if you are running Emacs) and have that region compiled and sent to the target with an Emacs keystroke (I suspect that this is a feature planned for the future).

What files need to be modified to port Riscy to new hardware … i.e., if I have an LPC2124 based system?

See makefile and the various *.asm files to see how customizations are done for different ARM variants.

First, study the manual for the LPC2124 to see how closely the configuration addresses (for timers, I/O ports, interrupts, etc.) match those of the LPC210x chips, etc. As long as the symbols do not conflict, just extend equates-lpc2xxx.asm to include any new symbols needed. Then create a new file named something like custom-lpc2124.asm and olimex-lpc2124-equates.asm with the CPU or board-specific information such as what pin is connected to the LED. Start by copying one of the other custom-*.asm files. The idea is to localize chip-specific items in these files that riscy.asm includes.

The main source code files are riscy.asm (for primitives) and kernel.fth (for essential high-level Forth code). CPU-specific items go in a CPU-specific assembly file (see custom-lpc2106.asm, custom-lpc23xx.asm, etc.). Here are the items that come to mind (but let's extend them as we run across others):

  • Primitives
    • Part of the initialization sets the serial port baud rate to 38,400 bps, based on the clock speed (especially the PCLK speed). This will need to be adjusted depending on the desired host's serial port speed and on the PCLK speed of your hardware. Some CPUs in the LPC series allow fractional baudrate divisors. See the file custom-lpc23xx.asm for an example.
  • High-level
    • The timing delay for MS might need to be adjusted to suit the hardware speed.

11.6 JTAG and OpenOCD

ARM chips typically have a JTAG interface. To use it, you need a JTAG cable. Olimex and SparkFun sell several inexpensive JTAG cables that work fine. I started with a parallel port cable but now I use the Olimex ARM-USB-TINY JTAG cable.

You need some software also, in the form of OpenOCD. You can use OpenOCD in two ways. In either case, you first start it running in a terminal.

  • connect to it via telnet from another terminal and issue OpenOCD commands
  • connect to it via GDB (perhaps from within Emacs) and issue GDB commands

11.6.1 Starting OpenOCD

Fortunately, OpenOCD comes with a good user manual. OpenOCD comes with some prewritten configuration files. If configuration files for your board and/or ARM variant are present (in /usr/share/openocd/scripts/ on an Ubuntu system), running it can be as simple as specifying one config file for your JTAG cable and another for your board, e.g., if you use the same JTAG cable I use and an Olimex LPH-H2148 board, you would start OpenOCD in a terminal with this command:

$ openocd -s /usr/share/openocd/scripts -f interface/olimex-jtag-tiny.cfg -f board/olimex_lpc_h2148.cfg

OpenOCD version 0.2.0-in-development (2009-06-30-01:11) does not come with configuration files for the Olimex LPC-P2106 board or chip, so I created a target config file named lpc2106.cfg based on a similar target config file. I hope to post it to the OpenOCD site, so perhaps it will be included in a future OpenOCD version. Meanwhile, you can use it directly from within your working directory. The idea is that its path would eventually be /usr/share/openocd/scripts/target/lpc2106.cfg.

Rather than typing a long command line such as

$ openocd -f /usr/share/openocd/scripts/interface/olimex-jtag-tiny.cfg -f lpc2106.cfg

I created a config file named openocd2106.cfg that goes in my working directory. The file, at a minimum, has these contents:

# This file is for use with the Olimex LPC2106 board.  
# It is named openocd2106.cfg.  Run it this way:
#    $ openocd -f openocd2106.cfg

# This is the JTAG connector I use
source [find interface/olimex-jtag-tiny.cfg]

# The following file, for now, is in my current directory
source [find lpc2106.cfg]

Once you run openocd -f openocd2106.cfg in a terminal, OpenOCD is running as a daemon. To kill it, type C-c in the terminal.

Sometimes I need to start it several times before it comes up without any error messages. (Start it, see errors, kill it. Start it, see errors, kill it. Start it, no errors, good, leave it running.) You need to have the JTAG cable connected, and the DEBUG jumper (labeled "DEBUG" on the LPC-P2106 board) shorted.

11.6.2 Running OpenOCD

After starting OpenOCD running as a daemon as described in the previous section, you can open a new terminal and connect via telnet;

$ telnet localhost 4444

See the OpenOCD manual for the commands you can use. Following are some examples.

  • Miscellaneous
    > poll
    > reg
    > reg r0
    > reg pc
    > reg r0 0x12345678
    > reg r0
    > halt
    > resume
    > step [address]
    > step 0
    > reset
    > reset run
    > reset halt
    > (for memory display of word, half-word, or byte, use 'mdw addr [count]' etc.)
    > mdw 0 8
    > mdb 0 32
    > (for writing, use 'mww addr value', 'mwh addr value', 'mwb addr value')
    > mww 0x40000000 0x12345678
    > mww 0x40000004 0x55553333
    > mdw 0x40000000 4
    > armv4_5 disassemble 0 20
    > arm7_9 fast_memory_access enable
    > flash banks
    > flash info
    
  • Dump the flash contents

    Here is an example to verify the contents of the flash. I (thought I) had programmed the flash with the file kernel-lpc2106.bin, a 3584-byte file at the time. So, I wanted to dump the first 3584 bytes of the flash to another file so I could compare the two files.

    I dumped the flash to a file named dump1.bin, via OpenOCD with

    $ dump_image dump1.bin 0 3584
    

    Then I compared the original file kernel-lpc2106.bin with dump1.bin with the hexdiff program, i.e.,

     $ hexdiff kernel-lpc2106.bin dump1.bin
    

    Note, if the only difference is the 4 bytes at address 0x00000018, that is not necessarily a problem. Those 4 bytes hold a checksum (the 32-bit two's complement of the sum of the other 7 vectors) used by the LPC2106 bootloader. So, if you add all 8 vectors, you should get a number whose rightmost 32 bits are zeroes. riscy.tcl calculates this checksum automatically when it creates a kernel image.

  • Erase flash sector 0
     flash erase_check 0 
     flash protect_check 0
     flash info 0
     flash protect 0 0 0 off    # if sector 0 had been protected
     flash protect_check 0
     flash info 0
     flash erase_sector 0 0 0
    

    The first command lists the sectors of bank 0 to see which are already erased. The second erases, in bank zero, all the sectors from number zero through number zero. I.e., it erases sector 0.

    Note, on the LPC-P2103 (and probably on the LPC-P2106) it never shows the protection turned off, yet that did not prevent me erasing or reprogramming sectors.

  • Reprogram the flash
    flash write_bank 0 kernel-lpc2106.bin 0
    

    This reflashes the chip with the Riscy Pygness kernel into bank 0 starting at offset 0.

11.7 GDB

For tracing through tricky assembly language routines, you might find GDB (the GNU Debugger) useful. It can be run various ways. I often use it from within Emacs.

To use GDB with a JTAG connector, first start OpenOCD as described in the JTAG and OpenOCD section. Then instead of connecting to OpenOCD via telnet, connect to it via GDB.

Before running GDB for the first time, take a look at the included .gdbinit file. You may need to edit it for your system. For example, it contains a line to change to my working directory and to open a file such as riscy-lpc2103.elf. Be sure to change those lines to your working directory and to the *.elf file that you will be debugging.

Then, in Emacs, start GDB with M-x gdb. It will prompt you for the GDB command to use. I use

arm-elf-gdb --annotate=3

A common error is to run gdb (which runs the GDB for your desktop CPU) rather than running arm-elf-gdb (which runs the cross-target GDB for your ARM CPU).

In the Emacs *gud* buffer, you can issue GDB commands directly or you can issue OpenOCD commands by preceding the OpenOCD command with the GDB command mon. E.e., to run the OpenOCD poll command from within the *gdb* buffer, type mon poll.

OpenOCD (and thus GDB) has a limit of two hardware breakpoints. So, when debugging in flash, you need to be careful not to set more than 2 breakpoints at a time.

11.7.1 Example

Here's an example of using GDB to step through the Forth word C@ on the Olimex LPC-P2103 board, which uses the NXP LPC2103 ARM chip.

First start OpenOCD as described earlier. I open a terminal, cd to my working directory, and start OpenOCD with

$ openocd -f openocd2103.cfg

If it reports errors, kill it (with C-c) then try again, until it comes up with no errors. I have sometimes needed to alter the jtag_khz xx setting to slow down the clock, e.g., jtag_khz 32. Once it starts sucessfully, you can ignore this terminal.

Then start up Emacs in another terminal (or wherever), if not already running. Edit the .gdbinit file in the working directory to be sure GDB will cd to your correct working directory and to open the correct *.elf file – look for a line like

file ~/riscy/lpc2103/riscy-lpc2103.elf

Then, start GDB with M-x gdb and specify

arm-elf-gdb --annotate=3

as the actual GDB command to run.

At this point, we have one terminal running OpenOCD as a daemon in the background. This terminal does not need any further attention. Then, we have Emacs running (in a terminal or in its own window) with GDB running in the Emacs *gud* buffer, where we will interact directly with GDB.

We then start another terminal in which to run Riscy Pygness. This terminal may actually be a shell running inside another instance of Emacs (to give us convenient command recall and command completion) or it could be your usual terminal program (such as gnome-terminal). At some point, we start Riscy Pygness in this terminal, with

$ ./riscy.tcl -image kernel-lpc2103 -port /dev/ttyS0

Since we wish to step through the code for C@, we need to find its address and tell GDB to set a breakpoint there.

There are two ways to find this address. One is from within Forth by running

' C@ .H

(which prints the address in hexadecimal). The other is by consulting the linker listing from when the Forth kernel was generated. In this example, that file is named riscy-lpc2103.lnkt. We won't find C@ listed under "C@" here (although we would find DUP listed under "DUP"). The reason is that "C@" is not a valid label name to the assembler. If we search for "C@" in riscy.asm, we will discover the label name used for the Forth word C@ is "Cfetch". (Thus, we would search for "Cfetch" when looking in the linker file.) In this example, its address is 0x0374. Since our .gdbinit file sets the radix to hexadecimal, within the *gud* buffer, we can specify that address as 374.

Note, since we told Emacs to open the file riscy-lpc2103.elf it knows to find the source code in the file named riscy-lpc2103.s (which was derived by preprocessing the file riscy-lpc2103.asm which was derived from riscy.asm by including the proper include files for the LPC2103 chip). As you jump and step within the *gud* buffer, a marker should move around accordingly in the riscy-lpc2103.s buffer.

In the Emacs *gud* buffer, set the breakpoint:

 mon poll        # make sure the ARM is halted
 mon reset halt  # in case above showed it was not halted
 i b             # show current breakpoints
 del 2           # delete any other than the one at _start (at 0x20)
 i b             # confirm only the one at 0x20 is set
 b Cfetch        # set our second breakpoint at the start of the C@ primitive 
 j _start        # jump to the start of the program
 c               # "continue" to start the program running so our Forth buffer can run

Then, in the Forth buffer, type some command involving C@ to make it hit the breakpoint, if it has not already stopped at that breakpoint, such as $4444 C@.

Then, back in the Emacs *gud* buffer, examine registers, single step, etc. See the GDB users manual for details.

 p $r0           # display register zero (which holds the top-of-stack)
 s               # single step
 ...             
 i reg           # show values (and names) of the CPU registers
 p $cpsr         # display the condition code register
                 #   the left 4 bits (NZCV) are often the most interesting
                 #     bit 31  N
                 #     bit 30  Z
                 #     bit 29  C
                 #     bit 28  V
                 # e.g., 0x200000d3 means the carry bit is set
 display $r1
 display $r2
 display $cpsr
 display $r0     # set up the 4 values we wish to see after every step command

11.8 Emacs

11.8.1 Learning Emacs

Emacs has extensive built-in help. If you are not yet familiar with Emacs, start it up and work through its built-in tutorial. Start the tutorial by pressing C-h t (i.e., press control h then press t) or by clicking on the **Help** menu and choosing **Emacs Tutorial**.

11.8.2 Emacs mode for Riscy Pygness

The file forthblocks.el (in the distribution) is an Emacs mode for editing Forth. Put that file into your home directory.

Put the following into your ~/.emacs file so the forthblocks mode will be run automatically when you open a file whose name ends in .fth.

(setq auto-mode-alist
      (cons '("\\.fth" . forthblocks-mode) auto-mode-alist))
(autoload 'forthblocks-mode "~/forthblocks.el" "Forth blocks editing mode." t)

If you don't yet have a .emacs file, copy the example file to your home directory. It already contains the above lines.

$ cp ~/riscy/.emacs-example ~/.emacs
$ cp ~/riscy/forthblocks.el ~/

Read the file forthblocks.el for details. The quick summary is that it works with plain text files (not traditional Forth 1024-byte blocks) but simulates blocks by recognizing comments such as

( block 1   ------------------  load block)
( shadow 1 )
( block 2  miscellaneous)
( shadow 2 miscellaneous )

as block markers. This gives you "logical" blocks which should be kept short, but are variable length. For me, this gives a pretty good compromise for source code: modular Forth blocks and the ability to use my favorite text editor.

See forthblocks.el for the keystrokes that move from block to block, that toggle between a block and its associated shadow block, that renumber the blocks, etc.

If you really don't like even this form of blocks, then just put a single block comment at the top of the file, such as

( block 2000   everything in this file is in block number 2000)

then everything in the file will be in a single (gigantic) block. To load the entire file, say

2000 LOAD

See also riscy.tcl for how to map block ranges to file names.

11.9 Antecedents

The current version of Riscy Pygness was derived from various previous versions of Riscy Pygness and from Pygmy Forth for the PC (a 16-bit DOS Forth), which itself was derived from Charles Moore's cmFORTH.

However, Riscy Pygness is quite different from Pygmy Forth for the PC, not just because it is a 32-bit Forth for an entirely different processor, but also because it borrows a few ideas from Charles Moore's colorForth.

11.10 colorForth

Chuck Moore's colorForth is a very interesting system. It has inspired some of the features of Riscy Pygness.

Use of color to "tag" words in the source

Rather than tagging words in the source with color, Riscy Pygness tags words with tags, so to speak.

Specifically,

  • A double quote followed by a space tags the following text up to an ending double quote as a string,
  • A colon followed by a space tags the following word as a label,
  • Literal numbers
    • A dollar sign at the beginning of a number tags it as a hexadecimal number (e.g., $03F8),
    • A single quote mark followed by a letter tags it as as the ASCII value of the character (e.g., 'A is the number 65),
    • A word that is a valid decimal number but is not in the table of label names does not need a tag.
    • The word VARIABLE tags the following word as a variable.

So, other than the verbosity of using symbols rather than color for tags, this is similar to both colorForth and classic Forth.

Just two tasks

colorForth appears to have exactly two tasks, a foreground task and a background task.

Riscy Pygness also has a fixed number number of tasks (three in the current version, but this is adjustable).

Some unusual simplifications

In colorForth, OR means exclusive OR and there is no inclusive OR

Riscy Pygness retains both the usual OR and XOR.

Multiple entry points and exit points and optimized tail recursion

Yes, Riscy Pygness does this also.

  • Riscy Pygness allows multiple labels per word. (This makes Forth word names virtually identical to assembler labels.)
  • It allows multiple exits from a word (using ; to represent the exit).
  • When encountering a semicolon, if possible, it changes the preceding word from a call to a jump and omits compiling an actual EXIT. In the source code of the compiler, you will see the definition of cut (which I believe was written as // in colorForth and/or some of Chuck's other Forths), which is used to mark special cases where the use of a semicolon must not cause the preceding subword to be changed to a jump (i.e., where an actual EXIT must be compiled), such as after a THEN.

11.11 GNU Toolchain

If you installed the bundle of tools from the Riscy Pygness website (http://pygmy.utoh.org/riscy/arm-toolchain.tar.bz2), then you can ignore this section.

11.11.1 Installing the GNU Toolchain

In order to create a new kernel, you need to have at least parts of the GNU Toolchain for the ARM installed. In particular, you need the assembler, linker, and some object file manipulating utilities – all of which are bundled together in the binutils package.

Your two main choices are to install a pre-compiled GNU tool chain or to compile the binutils software yourself.

Binutils is very easy to compile. That is the only part of the tool chain you really need in order to work with Riscy Pygness. You might also wish to compile the GNU debugger. You do not need the C compiler for the ARM.

11.11.2 Compiling the Tool Chain

If compiling from source, you might find the Lewin Edwards book Embedded System Design on a Shoestring helpful, although it isn't needed if you are compiling just binutils.

Here is an example of how to compile binutils.

First, download the source from http://ftp.gnu.org/gnu/binutils/, choosing one of the more recent versions, such as http://ftp.gnu.org/gnu/binutils/binutils-2.18.tar.bz2.

In general, for each package, I download the *.tar.gz or *.tgz source code package and uncompress it in /tmp, e.g.,

# cd /tmp
# tar -xzvf binutils-2.15.90.0.1.1.tgz

then create a build directory at the same level, e.g.,

# cd /tmp
# mkdir binutils-make

then configure, compile, and install the package, with something like the following

# cd binutils-make
# ../binutils-2.15.90.0.1.1/configure --target=arm-elf --prefix=/usr/local/arm
# make
# make install

Of course, adjust the file names to suit the actual version you are compiling and installing.

Note, you must be root, at least for the install step. See the section on becoming root.

Then, if you want to compile gdb or insight (insight contains gdb, so no need to compile both of them), do it generally as above but with a configure command such as

# ../insight-6.1/configure --target=arm-elf --prefix=/usr/local/arm/

11.11.3 Notes about generating the bundle of tools

Here are some notes about generating the bundle of tools (http://pygmy.utoh.org/riscy/arm-toolchain.tar.bz2). I used Ubuntu 10.04.1 which comes with make and gcc but we need to install the texinfo and libncurses-dev packages for the compiles to succeed.

  • Compiling binutils

    I downloaded the source from http://ftp.gnu.org/gnu/binutils/.

    # apt-get install texinfo
    # cd /tmp
    # mkdir binutils-build
    # cd binutils-build
    # make /usr/local/src/binutils-2.20.1/configure --target=arm-elf --prefix=/usr/local/arm
    # make
    # make install
    
  • Compiling GDB

    The most recent version appears to be version 7.2 of 2 Sept 2010. If we want Insight, then we get an older version of GDB (6.8). I chose to build just the newer GDB and forget about Insight. If we run GDB, we will do so from within Emacs.

    I downloaded the source from http://ftp.gnu.org/gnu/gdb/gdb-7.2.tar.bz2.

    # apt-get install libncurses-dev
    # cd /usr/local/src
    # gpg --verify gdb-7.2.tar.bz2.sig gdb-7.2.tar.bz2
    # tar -xjf gdb-7.2.tar.bz2
    # cd /tmp
    # mkdir gdb-build
    # cd gdb-build
    # /usr/local/src/gdb-7.2/configure --target=arm-elf --prefix=/usr/local/arm
    # make
    # make install
    
  • Compiling lpc21isp

    I downloaded lpc21isp-1.79.tar.gz into /usr/local/src/ and then compiled it and linked to a shorter name. The process was approximately the following (I don't think it needed a configure or install step, but I might have misremembered).

    # cd /usr/local/src/
    # tar -xzvf lpc21isp-1.79.tar.gz
    # cd lpc21i*
    # make
    # cp lpc21isp-1.79 /usr/local/bin
    # ln -s /usr/local/bin/lpc21isp-1.79 /usr/local/bin/lpc21isp
    

    See also the notes here at *Compiling lpc21isp.

11.12 Calling routines written in C

I have not tried this yet, but I believe you would need to link the C routines into the kernel so they would be present in riscy.bin (and riscy.elf) and also add a primitive to riscy.asm to serve as an assembly language wrapper for each C routine to be called (to handle moving the parameters to and from the data stack).

11.13 Makefile automatic variables

This is a quick reference to the meaning of some automatic variables used in a makefile.

Given a rule such as %.s: %.asm which says how to make an assembly file suitable for passing to the GNU assembler (e.g., led1.s) from a preprocessable assembly source file (e.g., led1.asm),

$*
the stem (e.g., led1)
$@
the target file (e.g., led1.o)
$<
the source file (e.g., led1.s), i.e., the first prerequisite
$^
the list of all the prerequisites

11.14 Troubleshooting

11.14.1 Bad flash

One symptom might be that things suddenly make no sense.

You've been working away with Riscy Pygness and your target ARM board, then you make a change, reflash the target, and nothing seems to work quite right. What could it be?

I'm not even going to mention the possibility that you forgot to turn on the power or perhaps left the boot jumper shorted after flashing.

One possibility is that the target's flash did not actually get programmed correctly. I believe I had this happen when flashing with version 1.48 of the lpc21isp, even though I used the -verify option. lpc21isp should have complained, but, no, it just silently pretended the flashing had succeeded.

To verify the flash was not programmed correctly, I used the JTAG interface and OpenOCD. First, I was single stepping through the code and the wrong value for the flash token table appeared to be loaded. That was my first clue.

Then, I used OpenOCD to dump the actual contents of the flash to a file and then compared that with the original kernel-lpc2106.bin, using a program named hexdiff. The two files should have been identical (other than perhaps the checksum vector at address 0x00000018), but they were not.

To fix it, I then used OpenOCD to erase the flash and then used OpenOCD to reprogram the flash. Then things worked properly once more.

See the JTAG section for details on how to dump, erase, and reprogram the flash.

I upgraded to version 1.79 of lpc21isp and tried again with the same board. This version, fortunately, actually verifies the flash. It warned me that it couldn't program a sector, rather than failing silently.

11.14.2 Blinking LEDs

See the label blink: in riscy.asm. This routine blinks an LED on the target board (if the board and the custom-*.asm file support this). Near the start of the routine the count is set. You can change how many times the LED blinks. If, when you reset the board, you see the LED blink the appropriate number of times, then you know the program has progressed to that point.

11.14.3 Serial port problems

As distributed, Riscy Pygness is set to use a serial port speed of 38,400 bps. This is a conservative value and you probably could increase it to 115,200 bps. If you change it, be sure to change it everywhere: makefile, riscy.tcl, and custom-*.asm.

See the label greet: in riscy.asm. Prior to blinking the LEDs, the target sends a greeting out the serial port. The greeting consists of a CRLF (carriage/linefeed), followed by "hi", followed by CRLF.

In normal use, i.e., starting the program with

./riscy.tcl -image kernel-lpc2103 ...,

you will not see this greeting because riscy.tcl silently eats the greeting (since it is waiting for a properly formatted message from the target and the greeting is deliberately not so formatted).

However, if you are having trouble with the serial communication, you can start a serial terminal program such as minicom instead of riscy.tcl. Then, when you boot or reset the target, you should see the greeting in the serial terminal. If you do not see the greeting, check your cables and check your settings in minicom. Are you using the correct serial port name? Have you set the baud rate correctly (this should be 38,400 bps unless you have changed it everywhere)? 8 data bits, no parity, 1 stop bit? Have you turned off both hardware and software flow control? Until you see the greeting, there is no point in wondering about why the Forth itself doesn't work.

I have run into the problem after rebooting the host where the host programs (either riscy.tcl or lpc21isp) were unable to set the baud rate. With riscy.tcl, the symptom is that, although a message such as

  Welcome to Riscy Pygness
  Loading the dictionary from kernel-lpc2103.dictionary

appears as usual, the expected prompt never appears. That is, this is what should appear

  Welcome to Riscy Pygness
  Loading the dictionary from kernel-lpc2103.dictionary
  >

A cure for this is to bring up minicom and set the baud rate to 38400 then exit minicom. I imagine this will last until the host is rebooted. Thus, you probably only need to do this once each time you reboot the host.

Be sure not run minicom and riscy.tcl at the same time (as they will fight each other for control of the serial port).

I have had a report that the mbed board works better if the serial port is set to 2 stop bits in riscy.tcl. I expect I will make that change, but, meanwhile, if you run into trouble, try changing it yourself.

11.14.4 Assembler errors

If you see errors like this when trying to assemble a kernel

riscy-lpc2294.s:281: Error: register expected, not 'DSTK,=dstk1' -- `ldr DSTK,=dstk1'
.....
riscy-lpc2294.s:1733: Error: register expected, not 'DSTK,#-4]!' -- `str r1,[DSTK,#-4]!'

[FIXME] the solution is probably [to be filled in later]

11.14.5 lpc21isp doesn't work

I have had a report that the "-donotstart" option is required in some cases (even though I don't see why it should ever be needed). So, if you have trouble with lpc21isp, add the "-donotstart" option to see if it helps.

11.15 Supported Processors

11.15.1 LPC2xxx

Riscy Pygness should be easy to port to any ARM processor (ARM 7, ARM9). So far, I have run it on the NXP LPC family of ARM processors. It currently runs on at least the LPC2106, LPC2103, LPC2294, and the LPC2378. The modifications necessary for other LPC ARM7 processors should be minimal. It has not yet been ported to the ARM Cortex.

Riscy Pygness currently works on at least the following ARM CPU and board variants.

  • Olimex LPC-P2106 board
  • Olimex LPC-P2103 board
  • Olimex LPC-P2378 board
  • Olimex LPC-P2294 board
  • New Micros Tini2106 board

We can expect it to run on the entire LPC family of chips with minor adjustments for clock speed, and such, on specific boards.

11.15.2 ARM Cortex

Riscy Pygness has now been ported to several ARM Cortex variants, including the LPC17xx and the STM32 families. See http://pygmy.utoh.org/riscy/cortex for more information.

r p has ported it to the MBED board. See http://mbed.org/cookbook/MbedForth for more details.

The Appendix includes some information for several variants.

11.15.3 other ARM CPUs

I have attempted to isolate the CPU-specific and/or development board-specific part of the code in separate files. See custom-lpc2106.asm, custom-lpc2103.asm, and custom-lpc23xx.asm in the distribution. The main assembly language file, riscy.asm, contains one conditional directive which includes the appropriate CPU-specific file based on a symbol defined near the top of riscy.asm. The makefile edits that symbol automatically to match the particular processor.

The file equates-lpc2xxx.asm holds register definitions that apply to all of the NXP LPC2000 family members. In addition, there are board-specific equates files: olimex-lpc2106-equates.asm, olimex-lpc2103-equates.asm, and olimex-lpc2378-equates.asm.

To port to a different ARM 7 or ARM 9 processor, copy whichever of the above files are most similar to your processor, and modify them. The goal, of course, is to isolate the parts that change from the parts that do not change, thus not cluttering the main source code with tons of conditional directives.

The main task will be to adjust for differences in how I/O and peripherals are handled, setting the clock speed, noting which pin (if any) connects to an LED, etc.

12 Contact Information

Email: frank@pygmy.utoh.org

Home page: http://pygmy.utoh.org

Main Riscy Pygness page: http://pygmy.utoh.org/riscy

Author: Frank Sergeant <frank@pygmy.utoh.org>

Date: 2010-11-10 Wed

HTML generated by org-mode 6.33x in emacs 23