3 Testing FCode Programs





FCode Source

An FCode source file is essentially a Forth language source code file. The basic Forth words available to the programmer are listed in Chapter 11, "FCode Dictionary".

FCode programs have the following format:

--------------------------------------------------
\ Title comment describing the program that follows fcode-version1 < body of the FCode program '> end0 --------------------------------------------------

fcode-version1 is a macro which directs the tokenizer to create an FCode header. For a description of the FCode header see "FCode Binary Format" on page 19. fcode-version1 produces a header including the version1 FCode. The macro fcode-version2 is similar except that it produces a header containing the start1 FCode. This macro may also be used to begin the FCode source. However since OpenBoot version 1 systems only recognize version1, plug-in device FCode that must run in OpenBoot version 1 systems must use fcode-version1.

end0 is an FCode that marks the end of an FCode program. It must be at the end of the program or erroneous results may occur. end1 is an alternative but end0 is recommended.

The comment in the first line is not necessary in many cases but it is recommended since it allows some OpenBoot tools to recognize the file as a Forth source file.

Tokenizing FCode Source

The process of converting FCode source to FCode binary is referred to as tokenizing. A tokenizer program converts FCode source words to their corresponding byte-codes, as indicated in Chapter 11, "FCode Dictionary". A tokenizer program with instructions describing its use is available from SunExpress®. It is part of the SBus/SCSI Developer's Kit mentioned in the Preface.

An FCode program's source can reside in multiple files. The fload tokenizer directive directs the tokenizer input stream to load another file. fload acts like an #include statement in C. When fload is encountered, the tokenizer begins processing the file named by the fload directive. When the named file is completed, tokenizing continues with the file that issued the fload. fload directives may be nested.

Typically, the tokenizer produces a file in the following format:

The header has the following format:

You can use this file to load either an FCode PROM or system memory for debugging as described in "Using the Forth Monitor to Test FCode Programs" on page 23.

The load point of the file is not used when burning an FCode PROM, but is used by Forth Monitor commands that load FCode files into system memory. The tokenizer available from SunExpress sets the load point to be the recommended 0x4000 address.

FCode Binary Format

The format of FCode binary that is required by the OpenBoot FCode evaluator is as follows:

    Table 3-1 FCode Binary Format

------------------------------------------------
Element Structure ------------------------------------------------
               
FCode header   Eight bytes
               
Body           0 or more bytes
               
End byte-code  1 byte, the end0 byte-code

------------------------------------------------

The format of the FCode header is:

    Table 3-2 FCode Header Format

------------------------------------------------------------------------------------
Byte(s) Content ------------------------------------------------------------------------------------
             
0            One of the FCodes: version1, start0, start1, start2, start4
             
1            reserved
             
2 and 3      16-bit checksum of the FCode body
             
4 through 7  count of bytes in the FCode binary image including the header

------------------------------------------------------------------------------------

Testing FCode Programs on the Target Machine

Once you have created the FCode binary, you can test it using the OpenBoot Forth Monitor. The Forth Monitor provides facilities to allow you to load your program into system memory and direct the FCode evaluator to interpret it from there. This allows you to debug your FCode without having to create a PROM and attach it to your plug-in board for each FCode revision during the debug process. See the OpenBoot 3.x Command Reference for complete documentation of the use of the Forth Monitor.

The FCode testing process generally involves the following steps:

    1. Configuring the target machine. This includes installing the hardware associated with the FCode program in the target machine and powering-up the machine to the Forth Monitor.
    2. Loading the FCode program into memory from a serial line, a network, a hard disk, or a floppy disk.
    3. Interpreting the FCode program to create a device node(s) on the OpenBoot device tree.
    4. Browsing the device node(s) to verify proper FCode interpretation.
    5. Exercising the FCode program's device driver methods compiled into the device node, if any.

If the FCode program does not include any methods which involve using the actual hardware (e.g. a driver which only publishes properties) then the program can be tested without installing the hardware.

Configuring the Target Machine

Setting Appropriate Configuration Parameters

Before powering-down the target machine to install the target hardware, a few NVRAM configuration variables should be set to appropriate values. You can set them from the Forth Monitor as follows:

--------------------------------
ok setenv auto-boot? false ok setenv fcode-debug? true --------------------------------

Setting auto-boot? to false tells OpenBoot not to boot the OS on a machine reset but rather to enter the Forth Monitor at the ok prompt.

Setting fcode-debug? to true tells the OpenBoot FCode evaluator to save the names of words created by interpreting FCode words which were tokenized with headers on. This is in addition to words defined after the tokenizer processed an external directive (i.e. words whose names are always saved). fcode-debug? defaults to false to conserve RAM space in normal machine operation. With the names saved, the debugging methods described in later sections will be easier since it will be easier to read decompiled FCode.

Modifying the Expansion Bus Probe Sequence

The start-up sequence in the machine's OpenBoot implementation normally examines all expansion buses for the presence of plug-in devices and their on- board FCode PROM programs. It then invokes the FCode evaluator to interpret any programs found. This process is called probing.

When using the Forth Monitor to load and interpret an FCode program in system memory, it is better to configure OpenBoot to avoid probing that device automatically. The probing can then be done manually (as explained later) from the Forth Monitor.

Configuring an OpenBoot implementation to avoid probing a given slot on a given expansion bus can be done in various implementation-dependent ways. That is, they will be different for different systems and different expansion buses.

Many machines with a SBus have an NVRAM configuration variable named sbus-probe-list. It defines which SBus card slots will be probed during start up and the order in which they will be probed.

For example, a machine with four SBus slots might have the sbus-probe-list configuration variable set to a default value of 0123. Setting sbus-probe-list to 013 directs OpenBoot during start-up to probe first SBus slots 0, 1, and 3. This leaves SBus slot 2 un-probed, free for use by the device under development.

Methods to prevent probing a given slot for other types of expansion buses can involve using the NVRAMRC script. Among other uses, an NVRAMRC script can:

After the FCode program is debugged and programmed in PROM on the device, you can do a full system test (including automatic probing of the new device), by restoring the expansion bus probing configuration to the default.

Getting to the Forth Monitor

After completing the configuration described above, power the machine down and install the device. Then power the system up. The display should stop scrolling at the ok prompt, ready to accept Forth Monitor commands.

Note - On the SPARCstation(TM) 1 and SPARCstation 1+, SBus slot 3 may be used only for SBus slave devices, such as frame buffers. Unlike slots 1 and 2, it may not be used for SBus master devices, such as disk drive or network interfaces.

Using the Command Line Editor of the Forth Monitor

Refer to the OpenBoot 3.x Command Reference for a list and description of the line-editing commands available with the Forth Monitor.

Using the Forth Monitor to Test FCode Programs

Complete directions for using the Forth Monitor to download files to system memory are provided in the OpenBoot 3.x Command Reference. A synopsis of FCode words for executing FCode source files is shown below.

    Table 3-3 File Execute-related Commands

--------------------------------------------------------------------------------------------------------------------
FCode Stack Notation Function --------------------------------------------------------------------------------------------------------------------
                                                                                      
begin-package          ( arg-addr arg-len reg-addr reg-len path-addr path-len -- )    Initializes device tree for 
                                                                                      executing FCode.
                                                                                      
end-package            ( -- )                                                         Completes a device tree 
                                                                                      entry and returns to the 
                                                                                      Forth Monitor 
                                                                                      environment.
                                                                                      
open-dev               ( path-addr path-len -- )                                      Opens the specified device 
                                                                                      node and all of its parents.
                                                                                      
device-end             ( -- )                                                         Closes the current node 
                                                                                      and returns to the Forth 
                                                                                      Monitor environment.
                                                                                      
select-dev             ( path-addr path-len -- )                                      Opens the specified device 
                                                                                      node and all of its parents, 
                                                                                      and makes the device the 
                                                                                      current instance.
                                                                                      
unselect-dev           ( -- )                                                         Closes the specified device 
                                                                                      node and all of its parents, 
                                                                                      and unselects the active 
                                                                                      package and current 
                                                                                      instance leaving none 
                                                                                      selected.
                                                                                      
set-args               ( arg-addr arg-len reg-addr reg-len -- )                       Sets values returned by 
                                                                                      my-args, my-space and 
                                                                                      my-address for the 
                                                                                      current node.
                                                                                      
execute-device-method  ( ... path-addr path-len cmd-addr cmd-len -- ... ok? )         Executes the named 
                                                                                      command in the specified 
                                                                                      device tree node.

--------------------------------------------------------------------------------------------------------------------

Using dload to Load from Ethernet

dload loads files over Ethernet at a specified address, as shown below.

-----------------------------
ok 4000 dload filename -----------------------------

In the above example, filename must be relative to the server's root. Use 4000 (hex) as the address for dload input.

FCode programs loaded with dload must be in the format described in "Tokenizing FCode Source". The tokenizer provided by SunExpress can output these files.

dload uses the trivial file transfer protocol (TFTP), so the server may need to have its permissions adjusted for this to work.

Using dlbin to Load From Serial Port A

dlbin may be used to load files over serial line A. Connect the target system's serial port A to a machine that is able to transfer a file on request. The following example assumes a tip window setup on a Sun system which will provide the FCode file. (See the OpenBoot 3.x Command Reference for information on setting tip connections.)

    1. At the ok prompt, type:
-------------
ok dlbin -------------
    2. In the tip window of the other system, send the file:
---------------------------------------
~C (local command) cat filename (Away two seconds) ---------------------------------------

The ok prompt will reappear on the screen of the target system.

FCode programs loaded with dlbin must be in the format described in "Tokenizing FCode Source". dlbin loads the files at the entry point indicated in the file header. It is recommended that this address be 0x4000.

Using boot to Load From Hard Disk, Floppy Disk, or Ethernet

You can also load an FCode program with boot, the command normally used to boot the operating system. Use the following format:

-------------------------------------------------------
ok boot [device-specifier] [filename] -h -------------------------------------------------------

device-specifier is either a full device path name or a device alias. See the OpenBoot 3.x Command Reference for information on device path names and aliases.

For a hard disk or floppy partition, filename is relative to the resident file system. See the OpenBoot 3.x Command Reference for information on creating a bootable floppy disk. For a network, filename is relative to the system's root partition on its root server. In both cases, the leading / must be omitted from the file path.

The -h flag specifies that the program should be loaded, but not executed. This flag must be included since otherwise boot will attempt to automatically execute the file assuming it is executable binary.

boot uses intermediate booters to accomplish its task. When loading from a hard disk or floppy disk, the OpenBoot firmware first loads the disk's boot block, which in turn loads a second-level booter. When loading over a network, the firmware uses TFTP to load the second-level booter. In both cases, filename and -h are passed to these intermediate booters.

The output file produced by a tokenizer may need to be converted to the format required by the secondary boot program. For example, Solaris 2.x intermediate booters require ELF format. fakeboot, a program available from SunExpress, may be useful in this process.

The location in memory where the FCode program is loaded depends on the secondary boot program and the fakeboot program.

Using dl to Load Forth Over Serial Port A

Forth programs loaded with dl must be ASCII files.

To load the file over the serial line, connect the system's serial port A to a machine that is able to transfer a file on request. One method is to set up a TIP window on another Sun system. (See OpenBoot 3.x Command Reference for information on this procedure.) The following example assumes a TIP window setup.

    1. At the ok prompt, type:
-------------
ok dl -------------
    2. In the TIP window of the other system, send the file, and follow it with a Control-D to signal the end of the file.
-------------------------------------
~C (local command) cat filename (Away two seconds) ^-D -------------------------------------

The ok prompt appears on the screen of the system to which the file is loaded.

dl normally loads the file at 4000 (hex). The file is automatically interpreted after it is loaded.

Using the Forth Monitor to Interpret an FCode Program

FCode program interpretation involves creating a device node on the device tree. Device nodes are also known as packages. Creating a device node from downloaded FCode involves the following steps:

    1. Setting up the environment with begin-package.

    For example, a begin-package call for creating a device node for a SBus card installed in SBus Slot 3 of a SPARCstation 2 looks like:

-------------------------------------------
ok 0 0 " 3,0" " /sbus" begin-package -------------------------------------------

    In the example, the string, /sbus, indicates that the device node which will be created by the FCode program is to be a child node of the /sbus node in the device tree.

    In general, parent nodes, which support child nodes, can be used as this argument to begin-package. The device node defined by the FCode program will be created as a child of that node. Give the full device pathname from the root node. Other types of parent nodes define different address spaces. Another example of an SBus parent node is on a SPARCstation 10 where its device pathname is /iommu/sbus.

    In the example, the string, " 3,0" indicates the SBus slot number, 3 and byte-offset 0 in the slot's address space where the device node is to be based.

    In general, this string is a pair of values separated by a comma which identify the physical address associated with the expansion slot. The form of this physical address depends on the physical address space defined by the parent node. For children of an SBus node, the form is slot-number, byte-offset. Other parent nodes will define different address spaces.

    The physical address pair value is retrieved in the FCode program with both the my-address and my-space FCodes. The slot ID string is converted to a binary form consisting of three values. Those values can be retrieved with the FCode program by using my-address for the phys.lo and phys.mid components and my-space for the phys.hi component.

    In the preceding example, the initial 0 0 represents a null argument string passed to the FCode program.

    This argument string is retrieved in the FCode program with the my-args FCode. Generally, FCode programs do not take arguments at interpretation time so this will usually be the null string. (For the SPARCstation 2, when the FCode PROM on an SBus card is automatically interpreted during system power-on, this is set to a null string.)

    begin-package is defined as:

----------------------------------------------------------------------------
: begin-package ( arg-addr arg-len reg-addr reg-len dev-addr dev-len -- ) select-dev new-device set-args ; ----------------------------------------------------------------------------

    select-dev ( parent-dev-addr parent-dev-len -- ) opens the input device node (the parent node) and makes it the current instance.

    new-device ( -- ) initializes a new device node as a child of the currently active node and makes it the current instance.

    set-args ( arg-addr arg-len reg-addr reg-len -- ) sets the values returned by my-args, my-space, and my-address for the current instance.

    2. Interpreting the loaded FCode with byte-load

    byte-load is the Forth Monitor command that invokes the FCode evaluator to compile the FCode program into the current instance.

    For FCode programs downloaded with byte-load use:

----------------------------
ok load-base ' c@ byte-load ----------------------------

    load-base is the system default load address. The argument, ' c@ , tells byte-load to use c@ as the access routine for reading the FCode.

    3. Closing the environment with end-package

    end-package finishes up the creation of the device tree node.

-----------------
ok end-package -----------------

    It is defined as:

-------------------------------------------------------
: end-package ( -- ) finish-device unselect-dev ; -------------------------------------------------------

    finish-device ( -- ) Completes the device tree node initialized by new-device and changes the current instance to the parent node.

    unselect-dev ( -- ) Closes the parent device tree node and returns to the normal Forth Monitor environment. That is, there is no longer a current instance or active package.

Using the Forth Monitor to Browse a Device Node

The Forth Monitor has many built-in commands to navigate the device tree. Table 3-4 lists the Forth Monitor commands supporting device node browsing:

    Table 3-4 Commands for Browsing the Device Tree

----------------------------------------------------------------------------------------------------------------
Command Description ----------------------------------------------------------------------------------------------------------------
                              
.properties                   Display the names and values of the current node's properties.
                              
dev device-path               Choose the indicated device node, making it the current node.
                              
dev node-name                 Search for a node with the given name in the sub-tree below the current node, 
                              and choose the first such node found.
                              
dev ..                        Choose the device node that is the parent of the current node.
                              
dev /                         Choose the root machine node.
                              
device-end                    De-select the current device node, leaving no node selected.
                              
" device-path" find-device    Choose device node, similar to dev.
                              
get-inherited-property        ( name-addr name-len -- true | value-addr value-len false ) 
                              Return property value of current instance or its parents
                              
get-my-property               ( name-addr name-len -- true | value-addr value-len false ) 
                              Return property value of current instance.
                              
ls                            Display the names of the current node's children.
                              
pwd                           Display the device path name that names the current node.
                              
see wordname                  Decompile the specified word.
                              
show-devs [device-path]       Display all the devices known to the system directly beneath a given level in 
                              the device hierarchy. show-devs used by itself shows the entire device tree. 
                              
words                         Display the names of the current node's methods.

----------------------------------------------------------------------------------------------------------------

Once a device node has been created, you can use the Forth Monitor to browse the node. See the OpenBoot 3.x Command Reference for a more complete discussion. Here is a brief synopsis of the available commands:

-----------------------------
ok dev /sbus/ACME,widget -----------------------------
----------------------------------------
ok " /sbus/ACME,widget" find-device ----------------------------------------

Using the Forth Monitor to Test a Device Driver

The Forth Monitor provides the capability to test the methods of an FCode program by allowing you to execute individual methods from the Forth Monitor prompt.

Device Node Methods

Using select-dev

select-dev initializes an execution environment for the methods of the package specified by its stack arguments. It allows the user to subsequently execute the device node's methods directly by name. For example:

---------------------------------------
ok " /sbus/ACME,widget" select-dev ---------------------------------------

select-dev performs the following steps:

    1. Effectively calls "dev /sbus/ACME,widget" to make the named device the active package. This enables the recognition of the device methods by the Forth Monitor.
    2. Establishes a chained set of package instances for each node in the path. In particular, this makes the package's instance-specific data items available to its methods.
    3. Opens all device nodes in the path by calling the open method of each. select-dev assumes open (and close) methods in each node in the path, so the device node under test must have one.

Once these steps are performed you can execute the methods of the current device node by typing their names at the prompt. For example:

-----------------------------
ok clear-widget-register ok fetch-widget-register . 0 -----------------------------

As is generally true of the Forth language, if execution of a method exposes an error in the code, the error can be isolated by executing the component words of the method step-by-step. Use see to decompile the method, and then type the component words individually until the error is evident. For example:

-------------------------------
ok see clear-widget-register : clear-widget-register enable-register-write 0 widget-register rl! disable-register-write ; ok enable-register-write ok 0 widget-register rl! ok disable-register-write -------------------------------

This process can be performed recursively by decompiling the component words and then individually executing their component words. This is much easier if most of the words were defined with the headers directive since see can then display the names of the component words instead of hexadecimal codes.

This process is also enhanced by executing showstack. showstack causes the stack's contents to be displayed prior to every ok prompt. For example:

---------------------
ok 1 2 ok showstack 1 2 ok . clear 3 4 2 3 4 ok ---------------------

Device nodes can also be modified as needed with any of the following techniques:

---------------------------------------------------
ok : open open initialize-widget-register-2 ; ---------------------------------------------------

    In general, such redefinitions affect only external uses of the named method (i.e. calls from other packages via $call-method and the like) and interactive use via the Forth Monitor. Previously compiled calls to the method in the same package are unaffected unless the method is called by name (e.g. with $call-self).

unselect-dev reverses the effects of select-dev by calling the close method of each device in the path of the current active node, destroying the package instance of each node, and returning to the normal Forth Monitor environment. Execute unselect-dev as follows:

--------------------
ok unselect-dev --------------------

Using begin-select-dev

Sometimes, select-dev will not work because the open method of a newly- written package does not work correctly. In this case, begin-select-dev can be used since it does everything that select-dev does except for opening the last child node. For example:

---------------------------------------------
ok " /sbus/ACME,widget" begin-select-dev ---------------------------------------------

Using execute-device-method

execute-device-method executes a method directly from the normal Forth Monitor environment. That is, it is not necessary to manually make the device node the current instance before executing the method. For example:

-------------------------------------------------------------
ok " /sbus/ACME,widget" " test-it" execute-device-method -------------------------------------------------------------

execute-device-method returns false if the method could not be executed; otherwise it returns true on top of whatever results were placed on the stack by the successful execution of the method.

execute-device-method performs the following steps:

    1. Establishes a chained set of package instances for each node in the path. In particular, this makes an instance of all data items of the device node available to its methods.
    2. Opens all device nodes in the named device path except the last device node in the pathname.
    3. Invokes the named method.
    4. Closes all the device nodes in the path (except the last one) destroying their package instances.
    5. Restores the current instance to the one that was current prior to beginning this process.
    6. Restores the active package to the one that was active prior to beginning this process.
    7. Returns the results.

Note that, in contrast to select-dev, execute-device-method does not call the open method of the last device node in the path. Consequently, any method invoked in this manner must not require any pre-established state which normally is created by open.

In summary, execute-device-method is provided to allow execution of device node methods designed to provide their own state initialization, and therefore to execute without previous execution of the open method. A typical example is a selftest method.

Using apply

apply provides an alternative manner of invoking execute-device- method in that it takes its arguments from the input stream instead of from the stack. The above example would be invoked with apply as follows:

---------------------------------------
ok apply test-it /sbus/ACME,widget ---------------------------------------

Since apply invokes execute-device-method, all of the restrictions listed above for execute-device-method must be followed.

Testing FCode Programs in Source Form

The Forth Monitor enables you to skip the tokenizer and download FCode program source directly. This practice is not recommended since the only advantage is to save a small amount of time tokenizing the program. There are also some disadvantages:

To load an ASCII Forth source file over serial line A, you use the command dl. In addition to loading the file over the serial line, dl compiles the Forth source while it is loading, without requiring an extra command. Therefore, you must execute begin-package before downloading. See "Using dl to Load Forth Over Serial Port A" on page 26 for details.

Producing an FCode PROM

The output of the tokenizer program is used to make an actual FCode PROM. If your PROM burning tools do not accept the raw binary format of the tokenizer, you may need to develop a format conversion utility.

Exercising an Installed FCode PROM

You can either let OpenBoot automatically evaluate the FCode program from the PROM or you can remove the device from the OpenBoot probing as discussed earlier in "Configuring the Target Machine" on page 21.

The same process discussed for testing FCode programs loaded to system memory can be used to test FCode programs already loaded into PROM on the device.

If you take the device out of the probing sequence, a device node can be built manually as in the following example for a device installed in SBus slot 1:

-----------------------------------------------------------
ok 10000 constant rom-size ok " /sbus" select-dev ok " 1" decode-unit ( phys.lo phys.mid phys.hi ) ok rom-size map-in ( virt ) ok new-device ( virt ) ok " " " 1,0" set-args ( virt ) ok dup 1 byte-load ( virt ) ok finish-device ( virt ) ok rom-size map-out ok unselect-dev -----------------------------------------------------------

This is essentially the same sequence as outlined for evaluating FCode loaded into system memory, except that you must map in and map out the FCode PROM by using the decode-unit, map-in, and map-out methods of the parent device node. For more information about these methods, see Chapter 8, "Memory-Mapped Buses".

You can browse the device node and exercise the device methods in the same way as described earlier. You can also define new methods and patch existing ones. Of course, these modifications will only remain until a system reset.