Riscy Pygness User Manual
Table of Contents
- 1 Introduction
- 2 Installation
- 3 Getting started using an existing image
- 4 Source code
- 5 Creating a new kernel image
- 6 Multitasking
- 7 The Forth model
- 8 Documentation for individual program files
- 9 Support Software
- 10 Limitations and cautions
- 11 Appendix
- 11.1 Linux and the command line
- 11.2 Putting Ubuntu on a USB stick
- 11.3 Board notes
- 11.4 Tools summary
- 11.5 FAQ
- 11.6 JTAG and OpenOCD
- 11.7 GDB
- 11.8 Emacs
- 11.9 Antecedents
- 11.10 colorForth
- 11.11 GNU Toolchain
- 11.12 Calling routines written in C
- 11.13 Makefile automatic variables
- 11.14 Troubleshooting
- 11.15 Supported Processors
- 12 Contact Information
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
variant | flash | RAM |
---|---|---|
LPC2101 | 8 KB | 2 KB |
LPC2102 | 16 KB | 4 KB |
LPC2103 | 32 KB | 8 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 oftelnet
) 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 programlpc21isp
. 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 asTERMINAL
) -
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
-
during development the address space is split between
- Flash (the kernel)
- RAM (the extensions)
- host side "smart terminal" is written in Tcl
- primitives are written in assembly language and assembled with the GNU ARM assembler.
-
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).
-
generate a kernel image to be burned into the ARM's flash
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 findPINMODE3
). 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 ;
-
"tail call optimization" (i.e., dropping
-
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 thelpc21isp
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
orCOM1:
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 asarm-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 ispreasm.tcl
but you could write a replacement in Python orsed
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:
-
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.
-
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
andkernel-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 scriptsr
andburn
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 producexxx.s
-
assemble
xxx.s
to producexxx.o
-
link
xxx.o
to producexxx.elf
(and produce a list of symbols) -
run objcopy on
xxx.elf
to producexxx.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
orman bash
orman 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 filemakefile
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 uselpc21isp
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 nametclkit
. (See the first line in the fileriscy.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) andkernel-lpc2106.dictionary
(to be used byriscy.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 ofriscy.asm
such asriscy-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 letmake
generate the kernel image automatically. Seemakefile
,riscy.tcl
, andriscy.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 fileskernel-lpc2106.bin
andkernel-lpc2106.dictionary
), and you are using the first serial port, you could start it with./riscy.tcl -image kernel-lpc2106 -port /dev/ttyS0
-
generate an image that can be burned into the ARM's flash
- 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 nametclkit
. The top line ofriscy.tcl
expects it in that location with that name. Otherwise, edit the top line ofriscy.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 files2.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, say2008 LOAD
to load block 2008. (Eventually, you would edit block 1 inkernel.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 likecustom-lpc2124.asm
andolimex-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 othercustom-*.asm
files. The idea is to localize chip-specific items in these files thatriscy.asm
includes.The main source code files are
riscy.asm
(for primitives) andkernel.fth
(for essential high-level Forth code). CPU-specific items go in a CPU-specific assembly file (seecustom-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.
-
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
-
High-level
- The timing delay for MS might need to be adjusted to suit the hardware speed.
-
Primitives
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
withdump1.bin
with thehexdiff
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.
-
A dollar sign at the beginning of a number tags it as a
hexadecimal number (e.g.,
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 ORRiscy Pygness retains both the usual
OR
andXOR
. - 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 ofcut
(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 actualEXIT
must be compiled), such as after aTHEN
.
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
Date: 2010-11-10 Wed
HTML generated by org-mode 6.33x in emacs 23