home *** CD-ROM | disk | FTP | other *** search
- <!-- Forthmacs Formatter generated HTML V.2 output -->
- <html>
- <head>
- <title>Hardware Diagnostics in Forth</title>
- </head>
- <body>
- <h1>Hardware Diagnostics in Forth</h1>
- <hr>
- <p>
- <p>
- <h2>fine tools ...</h2>
- <p>
- 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.
- <p>
- 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.
- <p>
- 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.
- <p>
- <p>
- <h2>Warning</h2>
- <p>
- 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.
- <p>
- 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.
- <p>
- <p>
- <h2>Getting started</h2>
- <p>
- 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 <em>ok</em> .
- <p>
- <p>
- <h2>Poking at registers</h2>
- <p>
- 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:
- <p><pre> 3340000 @ .</pre><p>
- <strong>@</strong> says to read a 32-bit word from the preceding address. <strong>.</strong>
- says to print the result. <strong>@</strong> is pronounced "fetch". In
- general, the <strong>@</strong> symbol is pronounced <em>fetch</em> in Forth
- terminology. There must be one or more spaces separating symbols!
- <p>
- Note: The Forth parser is really simple; it just grabs the next sequence of
- non-blank characters (called a <em>word</em> 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.
- <p>
- Note: <strong>@</strong> accesses a 32-bit location, the address must be
- long-aligned. If your device is 8 bits wide, use <code><A href="_smal_BE#16C">c@</A></code>
- in this and future examples; also use <code><A href="_smal_BB#169">c!</A></code>
- instead of <strong>!</strong> .
- <p>
- 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
- <p>
- 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.
- <p>
- On a working device, instead of getting an error, Forth will display the
- contents of the register you accessed.
- <p>
- <p>
- <h2>Scope Loops</h2>
- <p>
- 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:
- <p>
- <p><pre>
- 3340000 constant reg-addr
- : test begin reg-addr @ drop key? until ;
- </pre><p>
- <p>
- This creates a loop which will repeatedly read a register at location 3340000. <code><A href="_smal_AF#155">begin</A></code>
- ... <code><A href="_smal_BS#23A">key?</A></code> <code><A href="_smal_BC#31A">until</A></code>
- means to keep doing everything between the <code><A href="_smal_AF#155">begin</A></code>
- and the <code><A href="_smal_BS#23A">key?</A></code> until a key is typed on the
- keyboard. The <code><A href="_smal_BG#1CE">drop</A></code> 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 <code><A href="_smal_BG#1CE">drop</A>.</code>
- The loop is called <strong>test</strong> , and it is a new command which you
- have just created, you could have called it anything you wanted, instead of <strong>test</strong>
- .
- <p>
- 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.
- <p><pre> test</pre><p>
- <p>
- In general, the way to execute a Forth command is by typing its name.
- <p>
- 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.
- <p>
- It wasn't actually necessary to have given the loop a name. You could have just
- typed:
- <p><pre> begin reg-addr @ drop again</pre><p>
- <p>
- This is different from almost all Forth dialects, RISC OS Forthmacs knows about
- temporary compilation and forgets about the compiled code afterwards.
- <p>
- 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
- <p>
- <p>
- <h2>Writing to registers</h2>
- <p>
- Now that you can read your register, no doubt you want to write to it too.
- <p><pre> 1234 reg-addr !</pre><p>
- writes the 32-bit word 1234 (hex) to the address left by the word <strong>reg-addr</strong>
- (which we defined earlier). If you want to write a byte instead of a 32-bit
- word, use <code><A href="_smal_BB#169">c!</A>.</code>
- <p><pre> reg-addr @ .</pre><p>
- reads back the register and prints the value, so you can verify that the write
- actually worked.
- <p>
- <p>
- <h2>Do Loops</h2>
- <p>
- An obvious thing to do now is to write a bunch of different values to the
- register and see if they all work.
- <p>
- <p><pre>
- : 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 ;
- </pre><p>
- <p>
- 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.
- <p>
- How does this test-loop work? Let's go over it line-by-line. <em>ffff 0</em>
- are the arguments to the <code><A href="_smal_AP#1BF">do</A></code> ... <code><A href="_smal_AW#256">loop</A></code>
- 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 <em>i reg-addr !</em> . Previously we used the
- literal number <em>1234</em> as the value to store into location reg-addr. This
- time we use the loop index <strong>i</strong> . The loop index is <em>always</em>
- called <strong>i</strong> . If you use nested loops, the index of the next
- outer loop is called <strong>j</strong> .
- <p>
- 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 <strong>i</strong> . The operator <code><A href="_smal_BI#110"><></A></code>
- (not-equal) compares the top two things on the stack and leaves true if they are
- not equal or false if they are equal.
- <p>
- If the numbers are equal, all is well. If they are different, we need to print
- an error message. That is where the <code><A href="_smal_AW#226">if</A></code>
- ... <code><A href="_smal_BK#2F2">then</A></code> construct comes in. Here is
- the strange part: The stuff you want to do if the condition is true goes <code><A href="_smal_AH#157">between</A></code>
- <code><A href="_smal_AW#226">if</A></code> and <code><A href="_smal_BK#2F2">then</A>,</code>
- not after <code><A href="_smal_BK#2F2">then</A></code> as one would expect.
- This is unfortunate, but it is not the end of the world. The condition that is
- tested comes BEFORE the <code><A href="_smal_AW#226">if</A>;</code> in this case
- the condition is the true/false value left on the stack by the <code><A href="_smal_BI#110"><></A></code>
- operator. If this seems strange to you, consider that it is very simple, yet
- completely general. It is also possible to specify an <code><A href="_smal_BP#1D7">else</A></code>
- clause (details later).
- <p>
- The only thing remaining for this test-loop is to describe how the error message
- is printed. The construct <em>." ... "</em> , 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 <strong>i .</strong>
- . As you have probably guessed, <strong>.</strong> just means print whatever
- number is on the stack. Next we print another string, followed by the value
- read back from the register. Finally, <code><A href="_smal_AV#195">cr</A></code>
- prints a carriage-return and linefeed.
- <p>
- <p>
- <h2>Extensibility</h2>
- <p>
- Earlier we saw how to make a word called <em>test</em> 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:
- <p><pre> : dma! 3340804 ! ;</pre><p>
- This defines a new word called <strong>dma!</strong> which takes an argument and
- stores it into the prescribed location. This word can be used as:
- <p><pre> f00000 dma!</pre><p>
- 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.
- <p><pre> : init-dma f00000 dma! ;</pre><p>
- 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).
- <p>
- <p>
- <h2>Variables</h2>
- <p>
- Define a variable with
- <p><pre> variable foo</pre><p>
- The new variable <strong>foo</strong> has space for a 32-bit word. Put a number
- in the variable with:
- <p><pre> 129876 foo !</pre><p>
- and get it back with
- <p><pre> foo @</pre><p>
- 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 <strong>foo !</strong> picked it up and put in the variable foo. <strong>foo @</strong>
- retrieved it from the variable and returned it to the stack.
- <p>
- <p>
- <h2>Constants</h2>
- <p>
- 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:
- <p><pre> : mem-base 100000 ;</pre><p>
- Now the word mem-base is equivalent to the number 100000. A slightly more
- efficient form of this is:
- <p><pre> 100000 constant mem-base</pre><p>
- A word defined with <code><A href="_smal_AP#18F">constant</A></code> will
- execute somewhat faster than one defined the other way (but you would probably
- never notice the difference).
- <p>
- <p>
- <h2>C Language Analogies</h2>
- <p>
- <p><pre>
- 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
- </pre><p>
- <p>
- Forth Notes:
- <p>
- "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.
- <p>
- Within a do loop, the word <strong>i</strong> will put the loop index on the
- stack.
- <p>
- <p>
- <h2>One More Thing ...</h2>
- <p>
- 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:
- <p><pre> : scope-loop begin 1234 reg-addr ! key? until ;</pre><p>
- This word will continuously write 1234 to location <em>reg-addr</em> until you
- type any key. The word <code><A href="_smal_BS#23A">key?</A></code> returns
- true (which happens to be equal to -1) if a key has been depressed, and false
- (0) if not.
- <p>
- <p>
- <h2>Other Wonderful Features</h2>
- <p>
- 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 <code><A href="_smal_BR#2C9">see</A></code> followed by the name of
- any Forth command, or any Forth word you have already defined.
- <p>
- <p>
- <h2>Line Editing</h2>
- <p>
- While you are typing a Forth command line, you can move around in the line and
- edit it. Have a look at the chapter <strong>Typing Forth Command Lines</strong>
- .
- <p>
- </body>
- </html>
-