home *** CD-ROM | disk | FTP | other *** search
-
- Hardware Diagnostics in Forth
- *****************************
-
-
- fine tools ...
- ==============
-
- Forth has been long used for large numbers of hardware projects, both
- commercial and private. RISC OS Forthmacs is exceptionally stable and needs
- only very few resources on the computer.
-
- Here's how you can write Forth programs to diagnose your hardware. The
- programs can range from very simple things like reading a register to very
- complicated things like performing communications protocols. Each higher
- level of tests can build on the lower levels. At any time, you can
- interactively execute any part of the test, without having to build a command
- interpreter into your program.
-
- This document describes how to do very simple things interactively. It is
- also possible to save your work in a file and to make sequences of tests run
- automatically.
-
-
- Warning
- =======
-
- The examples in the rest of this document assume that you are running on a
- stand-alone system, and that there really are I/O devices located at the
- addresses mentioned. If you are running RISC OS Forthmacs under RISC OS or
- Unix, both won't allow you to access device registers, so the examples in this
- paper will cause a core dump of the Forth process.
-
- In fact, most of these examples assume that you are trying to debug a board
- whose registers begin at virtual address (hex) 3340000. In RISC OS computers
- this would be a Simple Exp. card with fast access, this might not be right
- for your system, so please pick an address for your system where you know
- there is some device or memory.
-
-
- Getting started
- ===============
-
- The way to get Forth running on your machine is system-dependent, so we won't
- go into that topic here. We assume that you have already figured out how to
- get Forth going, and that it is prompting you with 'ok' .
-
-
- Poking at registers
- ===================
-
- The first hurdle to cross when debugging a new board is usually reading and
- writing the device registers. With most programming environments or monitors,
- if the register doesn't work, you are stuck. Not so with Forth. Suppose that
- you have a 32-bit register at address (hex) 3340000. You can read it with:
- 3340000 @ .
- @ says to read a 32-bit word from the preceding address. . says to print the
- result. @ is pronounced "fetch". In general, the @ symbol is pronounced
- 'fetch' in Forth terminology. There must be one or more spaces separating
- symbols!
-
- Note: The Forth parser is really simple; it just grabs the next sequence of
- non-blank characters (called a 'word' in the jargon) from the terminal and
- looks up that word in its internal dictionary. If it finds the word, it
- executes some associated code. If it doesn't find the word, it tries to parse
- the word as a number. If that fails, it complains.
-
- Note: @ accesses a 32-bit location, the address must be long-aligned. If your
- device is 8 bits wide, use c@ in this and future examples; also use c! instead
- of ! .
-
- There is a problem with 16-bit registers in ARM based computers, ARM cpus
- don't support word-wide 16-bit data access. You can use W@ and W! but both
- instructions use two byte-wide memory accesses, so this might not be what you
- wanted. Or you use 32-bit normal accesses and mask-off the other
-
- The most likely result of trying this exercise on a prototype peripheral board
- is that the board won't respond to the cycle, so the CPU will get a bus error,
- print a message, and abort back to Forth.
-
- On a working device, instead of getting an error, Forth will display the
- contents of the register you accessed.
-
-
- Scope Loops
- ===========
-
- No, you don't have to get out your assembly language reference manual and try
- to figure out how to poke in a tiny loop. Here's how to make a loop:
-
- 3340000 constant reg-addr
- : test begin reg-addr @ drop key? until ;
-
- This creates a loop which will repeatedly read a register at location 3340000.
- begin ... key? until means to keep doing everything between the begin and the
- key? until a key is typed on the keyboard. The drop is needed to get rid of
- the value that was read from the register, which is left on a stack. That
- stack would eventually overflow if not for the drop. The loop is called TEST ,
- and it is a new command which you have just created, you could have called it
- anything you wanted, instead of TEST .
-
- Remember that there will be an address exception if the physical address
- hasn't been accepted by the MMU. Now you can try the loop.
- test
-
- In general, the way to execute a Forth command is by typing its name.
-
- So now the machine is sitting there banging away at your register. You can
- try to find a scope that still has some probes attached and figure out why
- your register isn't responding.
-
- It wasn't actually necessary to have given the loop a name. You could have
- just typed:
- begin reg-addr @ drop again
-
- This is different from almost all Forth dialects, RISC OS Forthmacs knows
- about temporary compilation and forgets about the compiled code afterwards.
-
- However, by giving the command a name, you save it away so you can use it
- later, just by typing the name. It's not saved on disk, just in memory, so if
- you reboot, the new command will be lost. It would be nice if you could save
- your work on disk, but in a lot of stand-alone debugging cases there is no
- disk on the machine. To learn how you can save your work, read the "Creating
- Stand-Alone Forths" chapter
-
-
- Writing to registers
- ====================
-
- Now that you can read your register, no doubt you want to write to it too.
- 1234 reg-addr !
- writes the 32-bit word 1234 (hex) to the address left by the word REG-ADDR
- (which we defined earlier). If you want to write a byte instead of a 32-bit
- word, use c!.
- reg-addr @ .
- reads back the register and prints the value, so you can verify that the write
- actually worked.
-
-
- Do Loops
- ========
-
- An obvious thing to do now is to write a bunch of different values to the
- register and see if they all work.
-
- : test-loop
- ffff 0 do
- i reg-addr ! ( write a value to the register )
- reg-addr @ ( read it back ) ( register value on stack )
- i <> ( see if the value read back is different from the one written )
- if ." Error - wrote " i . ." read " reg-addr @ . cr
- then
- loop ;
-
- The indentation is optional. If you were writing this test on-the-fly while
- sitting in the lab, you would probably not bother with indentation.
- Similarly, everything inside parentheses is a comment and may be omitted.
- When you are writing Forth programs to save (presumably using a Unix editor),
- please don't omit the comments or the indentation, because that would make
- your work hard to understand later.
-
- How does this test-loop work? Let's go over it line-by-line. 'ffff 0' are the
- arguments to the do ... loop construct. The loop starts at 0 and ends when
- the loop index reaches (hex) ffff. The last time through the loop, the index
- has the value (hex) fffe. The firs thing inside the loop is 'i reg-addr !' .
- Previously we used the literal number '1234' as the value to store into
- location reg-addr. This time we use the loop index I . The loop index is
- 'always' called I . If you use nested loops, the index of the next outer loop
- is called J .
-
- The next thing we do inside the loop is read back the register. Previously we
- printed the value as soon as we read it; this time we will let the program
- look at and decide if it's okay. But where is the value kept? It's on the
- stack, just like on an HP calculator. In fact almost every operator in Forth
- takes its operands from the stack and leaves its results on the same stack. I
- will assume that this concept is familiar to you; if it isn't, let me know and
- I will either explain it to you or loan you a book which does so. Anyway, the
- register value is now sitting on the stack. The next thing we do is compare
- that value to the loop index I . The operator <> (not-equal) compares the top
- two things on the stack and leaves true if they are not equal or false if they
- are equal.
-
- If the numbers are equal, all is well. If they are different, we need to
- print an error message. That is where the if ... then construct comes in.
- Here is the strange part: The stuff you want to do if the condition is true
- goes between if and then, not after then as one would expect. This is
- unfortunate, but it is not the end of the world. The condition that is tested
- comes BEFORE the if; in this case the condition is the true/false value left
- on the stack by the <> operator. If this seems strange to you, consider that
- it is very simple, yet completely general. It is also possible to specify an
- else clause (details later).
-
- The only thing remaining for this test-loop is to describe how the error
- message is printed. The construct '." ... "' , pronounced "dot-quote, prints
- whatever is inside the quotes. The first space after the first quote is
- mandatory and is not printed. Any subsequent spaces before the next quote are
- part of the string and are printed. Next we print the loop index with I . .
- As you have probably guessed, . just means print whatever number is on the
- stack. Next we print another string, followed by the value read back from the
- register. Finally, cr prints a carriage-return and linefeed.
-
-
- Extensibility
- =============
-
- Earlier we saw how to make a word called 'test' which could then be executed
- by typing its name. Once you have made a word, you can then use it as part of
- another word, thus building on top of your previous work. For example,
- suppose that there is a dma address register on your board, and that its
- address is (hex) 3340804. You can define a word to store a value into that
- register as follows:
- : dma! 3340804 ! ;
- This defines a new word called DMA! which takes an argument and stores it into
- the prescribed location. This word can be used as:
- f00000 dma!
- which will store f00000 into the dma register. Now, suppose that as part of a
- test, you need to automatically set the dma register. You can use your word
- dma! as part of another word.
- : init-dma f00000 dma! ;
- This is a trivial example, but it serves to illustrate the style of building
- up your application in small incremental steps. Don't hesitate to build words
- which only have a few components; the overhead of calling a word from one at
- higher level is quite small, and the advantages of small words are many
- (readability, ease of debugging, possibility of reuse).
-
-
- Variables
- =========
-
- Define a variable with
- variable foo
- The new variable FOO has space for a 32-bit word. Put a number in the
- variable with:
- 129876 foo !
- and get it back with
- foo @
- The number to be stored is taken from the stack, and the number fetched is
- left on the stack. When you typed the 129876, that number was actually left
- on the stack, and FOO ! picked it up and put in the variable foo. FOO @
- retrieved it from the variable and returned it to the stack.
-
-
- Constants
- =========
-
- A constant is a symbolic name for a number. In other words, when you type the
- name of a constant, it just leaves its number on the stack. One way of making
- a constant is the obvious:
- : mem-base 100000 ;
- Now the word mem-base is equivalent to the number 100000. A slightly more
- efficient form of this is:
- 100000 constant mem-base
- A word defined with constant will execute somewhat faster than one defined the
- other way (but you would probably never notice the difference).
-
-
- C Language Analogies
- ====================
-
- C Forth
- while( condition ) { BEGIN condition WHILE
- loop-body loop-body
- } REPEAT
- do { BEGIN
- loop-body loop-body
- until ( condition ) condition UNTIL
- for( i=start_value; end_value
- i<end_value; start_value
- i += increment ) { DO
- loop-body loop-body
- } increment +LOOP
- for( i=start value; end value
- i<end value; start value
- i++ ) { DO
- loop-body loop-body
- } LOOP
- if ( condition ) { condition
- true_clause IF true_clause
- } else { ELSE false_clause
- false_clause THEN
- }
- if ( condition ) { condition
- true_clause IF true_clause
- } THEN
-
- Forth Notes:
-
- "condition" is any sequence of Forth words that has the effect of leaving a
- number on the stack. If the number the stack is 0, the condition value is
- false, otherwise it is true.
-
- Within a do loop, the word I will put the loop index on the stack.
-
-
- One More Thing ...
- ==================
-
- You may want to do a scope loop which can be easily interrupted. You can
- always abort back to RISC OS Forthmacs with Shift-Ctrl-F12. A nicer way,
- however, is the following:
- : scope-loop begin 1234 reg-addr ! key? until ;
- This word will continuously write 1234 to location 'reg-addr' until you type
- any key. The word key? returns true (which happens to be equal to -1) if a
- key has been depressed, and false (0) if not.
-
-
- Other Wonderful Features
- ========================
-
- Forth includes, among other things, a resident assembler, so you can write
- little bits of assembly code if you need to. It has a built-in visual line
- editor, so you can edit command lines as you type them. There are packages
- for defining structures and bit fields, similar to C. A built-in decompiler
- allows you to interactively decompile any Forth word that you have previously
- defined. Try typing see followed by the name of any Forth command, or any
- Forth word you have already defined.
-
-
- Line Editing
- ============
-
- While you are typing a Forth command line, you can move around in the line and
- edit it. Have a look at the chapter TYPING FORTH COMMAND LINES .
-
-