Copyright 1997, 1998, 1999, 2000, 2001 Providenza & Boekelheide, Inc.
July 25, 2001
March 23, 2000
Oct 28,1999
Sep 9, 1999
April 26, 1999
February 24, 1999
August 26, 1998
What's New
Environment variables can now be used to select
the PCI device.
Check out Extending DBG for a new method
for adding your own customized DBG commands.
A Forth Cheat Sheet has
been added.
Introduction
DBG is a hardware debugger designed for use on a x86 based PCI system.
It includes an extensive set of debug commands as well as a Forth interpreter
to allow flexible scripting and interactive diagnostic development.
DBG is a DOS based application designed to run in a variety of OS configurations. We’ve successfully used DBG in:
DBG is constantly in development - your
feedback
is welcome. The revision history
section can show you what's new.
Terminology
Throughout this document, hex numbers are used unless otherwise noted.
Note that DBG and its Forth interpreter are case insensitive. We’ll frequently
use the style $reg to emphasize that the parameter us a hex value.
Bytes are 8 bits, Words are 16 bits, Dwords are 32 bits.
Note that when specifying a memory address...
Acknowledgments
Thanks to Phil Burk for the Pforth
interpreter source code.
Also thanks to the folks who created the PMODE/W DOS extender used by
DBG.
Starting DBG
DBG is started from the DOS prompt by typing:
dbg [options]If no options are specified, DBG finds all the devices on the PCI bus and prompts the user to select one for debugging. DBG then uses the selected device to initialize internal Base Address Registers (BARs) that are used to access the selected device. You can skip this interactive prompt by using both the -d and -v options to manually specify a PCI device or bye setting a couple of environment variables. Multiple command line options can be used.
Flag | Description |
-d$pci_device_id | specify the target PCI device ID |
-v$pci_vendor_id | specify the target PCI vendor ID |
-nfunction | specify the target PCI function |
-t$slot | specify a PCI device via slot number |
-ccommand | run a debugger command |
-f | start in Forth mode |
-ffilename | load the specified Forth file, and start in Forth mode |
-sfilename | specify a script to run |
-q | quit the debugger |
dbg -d1234 -v5678 | Start the debugger and use the PCI device with vendor ID 5678 (hex) and device ID 1234 (hex). |
dbg "-c 1dmem 0" -cbye | Start the debugger, display dword 0 in BAR 1, then stop the debugger. Note the quotes. |
dbg -d1234 -v5678 -f | Start the debugger and use the PCI device with vendor ID 5678 (hex) and device ID 1234 (hex). Start in Forth mode. |
PCI Vendor/Device ID
Command line options can be used to specify the desired target PCI
device. The two methods are:
The vendor and device IDs can also be specified using the environment variables DBG_VENDOR and DBG_DEVICE. These would typically be set using the DOS 'set' command:
set DBG_VENDOR=1131If you're using a PCI slot number to specify the PCI board, you can use the DBG_SLOT environment variable.
set DBG_DEVICE=8500
If neither valid command line options or environment variables have
been used to specify the PCI device/vendor values, DBG reverts to a simple
interactive prompt to force the user to select an appropriate device.
Using DBG
DBG can either operate in native mode or in forth mode.
Native
mode presents a traditional user interface with a limited command set.
Forth
mode provides a direct interface to Forth - a interpreted, stack based
programming language.
Core to DBG is the concept of Base Address Registers (BARs).
DBG uses BARs to access the different memory spaces that many PCI devices
utilize. For example, many graphics devices will use one BAR for registers
and another BAR to access the video memory. DBG’s BAR registers allow you
to specify offsets into each BAR rather than having to specify an actual
physical address for each command. This is very useful since the PCI BIOS
is allowed to map these two BARs in different memory locations each time
the system is booted. Thus, if you know that the FOO register is at offset
$14 in BAR 1, you can access FOO by simply using the number $14 rather
than an absolute physical address. DBG treats BARs 7 and 9 specially. BAR
9 is pre-initialized to allow access to the bottom 1MB of PC address space
- this allows easy access to peripherals (such as VGA) that use portions
of the old DOS memory area. BAR 7 is used to allow access to other areas
of physical memory as specified by the user - see the BAR7
command below.
'Native' DBG Commands
DBG has numerous 'native' commands in the non-Forth mode of operation.
These commands allow you to examine memory, IO, and configuration spaces,
test memory, etc. The commands can be broken into the following categories:
[0]cmd>The [0] in this case indicates that, by default, BAR 0 will be used for all memory oriented commands. You can change this default by using the BAR command.
Sometimes, you want to access a memory location in a different BAR than the default for only one or two commands. DBG allows you to do this by prepending the desired BAR to the command. For example:
[0]cmd> 1dmem 0will use BAR 1 for the dmem command.
The pci, io, and mem commands are used to access a single memory, I/O, or PCI configuration location. Each command is followed by an address and, optionally, a data value. If no data value is specified, the value at the specified address is displayed. If a data value is specified, it is written to the location.
Command | Action |
bpci $reg <$data> | byte read or write a PCI register |
wpci $reg <$data> | word read or write a PCI register |
dpci $reg <$data> | dword read or write a PCI register |
bio $port <$data> | byte read/write a I/O port |
wio $port <$data> | word read/write a I/O port |
dio $port <$data> | dword read/write a I/O port |
bmem $addr <$data> | byte read/write a memory location |
wmem $addr <$data> | word read/write a memory location |
dmem $addr <$data> | dword read/write a memory location |
The compare commands are used to compare memory to an
expected value. If the value does not match, an error message is
printed. If the halt flag is set, repeat_loops and scripts
will also be aborted on an error.
Command | Action |
bcmp $addr $data | compare byte memory location to an expected value |
wcmp $addr $data | compare word memory location to an expected value |
wcmp $addr $data | compare dword memory location to an expected value |
Command | Action |
bwait $addr $value $mask | wait until ( (byte_memory & $mask) == $value || kbhit() ) |
wwait $addr $value $mask | wait until ( (word_memory & $mask) == $value || kbhit() ) |
dwait $addr $value $mask | wait until ( (dword_memory & $mask) == $value || kbhit() ) |
Command | Action |
bx $addr | examine bytes in memory |
wx $addr | examine words in memory |
dx $addr | examine dwords in memory |
After entering the command, DBG displays the current value in the memory location, then waits for user input:
The dump command allows you to dump a range of memory locations. Again, bytes, words, and dwords variants are supported.
Command | Action |
bdmp $addr1 [$addr2] | dump bytes of memory |
wdmp $addr1 [$addr2] | dump words of memory |
ddmp $addr1 [$addr2] | dump dwords of memory |
If no second address is specified, it is assumed to be 3Fh greater than
addr1.
The fill command is used to fill a range of memory locations with a constant value. Again, bytes, words, and dwords variants are supported.
Command | Action |
bfil $addr1 $addr2 $data | fill byte memory range with data |
wfil $addr1 $addr2 $data | fill word memory range with data |
dfil $addr1 $addr2 $data | fill dword memory range with data |
The test commands (byte, word, dword) provide rudimentary memory tests.
Command | Action |
btst [$addr1 $addr2] | run byte oriented memory tests |
wtst [$addr1 $addr2] | run word oriented memory tests |
bdst [$addr1 $addr2] | run dword oriented memory tests |
The memory tests operate in several phases:
The crc command is provided to compute a 32 bit CRC of the specified memory range.
Command | Action |
bcrc $addr $addr [$value] | compute byte CRC, optionally compare to expected value |
wcrc $addr $addr [$value] | compute word CRC, optionally compare to expected value |
dcrc $addr $addr [$value] | compute dword CRC, optionally compare to expected value |
The csum command is used to compute a 16 bit checksum of the specified memory range.
Command | Action |
csum $addr1 $addr2 | compute 16 bit csum on the range of bytes |
Command | Action |
loop | rewind the current script and run it again |
loop $value $mask | loop script if ((last_read & mask) == value) This is not well supported since Forth is far more flexible for looping, etc. |
s file_name | run the script |
p <name> | run the DOS program. If no name, shell to a DOS prompt |
Several of these commands deserve a little extra description.
BAR7 is used to access physical memory - it is very dangerous if you’re not careful. When working on devices that perform bus mastering, you frequently need to obtain access to a chunk of physical memory. BAR7 does not allocate memory - it simply lets you access it. You can do bad things to the system memory with this. The command takes two parameters: a physical address to access, and the size of the area to access. Once you have initialized BAR7, you can use normal DBG commands to examine and change values in the physical memory. You cannot use BAR7 a second time to change the memory aperture - get it right the first time!
One trick we’ve used when working with bus mastering devices is to use a PC with a reasonable amount of memory, say 32MB. We can guess that DOS will probably not be using the upper 16MB, so, big_gulp, we assume that we can use this area as a scratch-pad for bus mastering experiments using BAR 7 to peek and poke the locations. You can get in big trouble doing this, so good luck! Remember, BAR7 just provides a way to access physical memory, it doesn’t allocate it.It’s easy to forget that BAR7 is a different command from BAR 7. The former creates an aperture to access physical memory, the latter specifies a default BAR for subsequent commands.
ALIAS is a way to create text short-cuts. For example, if you are repeatedly typing:
ddmp 13478 134ffYou could create an alias to allow less typing:
alias foo ddmp 13478 134ffTyping ‘foo’ will now execute the memory dump command.
HALT allows a ‘halt on error’ flag to be set or cleared. This
will terminate commands that have been invoked with the ‘!’ prefix.
LAST_READ is a variable set during the execution of several of the commands. It is useful in aborting scripts, but has been largely supplanted by the flexibility provided by the Forth interpreter. The following commands set last_read:
Command | Value set in last_read |
bio, wio, dio | value read from IO port |
bpci, wpci, dpci | value read from PCI cfg register |
bmem, wmem, dmem | value read from memory |
bcrc, wcrc, dcrc | crc computed |
csum | checksum computed |
btst, wtst, dtst | number of memory test errors |
bcmp, wcmp, dcmp | value read from memory |
bwait, wwait, dwait | value read from memory |
Prefix | Action |
! | repeat command until keyboard hit or error |
0..9 | over-ride default PCI BAR for this command |
/ | start of non-printing comment (useful in scripts) |
# | start of printing comment (useful in scripts) |
Forth is a stack based language using Reverse Polish Notation (RPN) similar to an HP calculator. Many tutorials are available describing the forth language - it is beyond the scope of this document to teach forth. The Pforth web page maintained by Phil Burk contains a simple tutorial that can help get you started. Forth operations are usually called words. The simple program
1 2 +consists of 3 words.
When forth is active, the debugger prompt changes to:
Stack<16>
ok:
The <16> indicates that forth is using hexadecimal for numbers.
If any data is on the stack, it is printed after the Stack <16> portion
of the prompt.
Forth has frequently been described as a write only language
- easy to write, impossible to read. To some extent, this is true.
You can minimize this by (gasp!) liberally using comments. What a
concept! Since Forth is a stack based language, a commenting style
has evolved to match the architecture. You will frequently see Forth
comments in the style:
( in_value -- out_value
)
This indicates that in_value is on the stack before the Forth word is invoked and that out_value is on the stack after the word finishes. It is a very handy way of describing input and output parameters to Forth words.
The following words have been added as extensions to Forth to aid in accessing hardware.
Forth Word | Description | ||||||||||||||||||
dbg | return to normal debugger | ||||||||||||||||||
dbg" cmd_string" | ask the normal debugger to execute cmd_string. Note that dbg" is one word followed by whitespace. The quote marks are required for this Forth word. | ||||||||||||||||||
inb | ( port -- data ) read byte IO port | ||||||||||||||||||
inw | ( port -- data ) read word IO port | ||||||||||||||||||
ind | ( port -- data ) read dword IO port | ||||||||||||||||||
outb | ( data port -- ) write byte IO port | ||||||||||||||||||
outw | ( data port -- ) write word IO port | ||||||||||||||||||
outd | ( data port -- ) write dword IO port | ||||||||||||||||||
cfgrdb | ( port -- data ) read byte pci cfg reg | ||||||||||||||||||
cfgrdw | ( port -- data ) read word pci cfg reg | ||||||||||||||||||
cfgrdd | ( port -- data ) read dword pci cfg reg | ||||||||||||||||||
cfgwrb | ( data reg -- ) write byte pci cfg reg | ||||||||||||||||||
cfgwrw | ( data reg -- ) write word pci cfg reg | ||||||||||||||||||
cfgwrd | ( data reg -- ) write dword pci cfg reg | ||||||||||||||||||
bar | ( bar_number -- bar_value) retrieve specified PCI linear base address value. This is not a physical address -- it is the virtual address corresponding to the specified PCI BAR. This address can be used by Forth primitives such as "!" and "@" to access memory. | ||||||||||||||||||
time | ( -- current_time) retrieve the current time. The time is in 1/100 of a second. Note that most DOS system clocks run at about 18Hz, hence the granularity is between 5 and 6 hundreths of a second. | ||||||||||||||||||
int386 | ( in_array out_array -- ) perform a DOS int. Each array
is 32 bytes long (8 dwords) arranged as follows:
|
||||||||||||||||||
:: (double colon) | Same as the standard : (colon) word used to define new words EXCEPT that it doesn't complain if you are redefining a pre-exisiting word. Use ;; (double semi-c) to end the word definition! | ||||||||||||||||||
;; (double semi-c) | Same as the standard ; (semi-colon) word EXCEPT it is usually used to terminate a :: defined word. |
The Forth Cheat Sheet gives some
examples of standard popular/useful Forth words.
Forth Example
The following example gives a flavor of using Forth to aid in debug.
\ Forth comments start with a back-slash
\ make sure we’re using hex
\ define a constant to allow us to access memory in BAR 1
\ define a word to display the 1st 16 (decimal) bytes in BAR 1
\ define a word to change the 100th byte to a 0
\ define a word to loop until the dword at 0x1234 is non-0 or a
key? if \ check if we need to get & drop a key
\ Routine to Clr the screen via int386 call \ create two 8 dword arrays for passing int386 parameters
: my_clr
\ perform the INT command to clear the screen
|
Stack |
|
|||||||||||||||||||||
Program Flow |
|
|||||||||||||||||||||
and Comparison |
|
|||||||||||||||||||||
|
|
|||||||||||||||||||||
|
|
|||||||||||||||||||||
|
|
|||||||||||||||||||||
SingleStep |
|
As of V2.11, if the 'native' debugger doesn't understand a command that
you've entered, it tries to find a corresponding Forth word that is prefixed
by 'dbg_'. If it finds the word, it passes the command to Forth to
execute. For example
You Type | DBG tells Forth |
|
|
To assist this process, DBG automatically tries to read the file dbg_xtnd.f into the Forth interpreter when starting up. DBG first looks for the extension file in the current directory in which DBG was invoked. If the file is not found the current directory, the directory holding the DBG executable is then checked.
Some things to note...
|
|
Changes |
|
Initial release to web | |
|
|
Changed PCI code to scan all busses |
|
|
Deleted forth word 'foo'.
Turned of floating point support |
|
|
Upgrade to PForth 2.1
Changed 'bus' to 'slot' for initial PCI prompt Revised forth hashing algorithm Added 'hash' command to dbg core cmds |
|
|
Changed the 'bar8' cmd to be 'bar7' since the rombase address reg is
actually bar 8!
Fixed the forth 'key?' to map to 'kbhit' - This got broken when we ported to PForth 2.1 |
|
|
Added rudimentary support for multi-function devices
Added 'time' word to forth |
|
|
Added bcmp/wcmp/dcmp commands
Enhanced halt flag to kill scripts on error. Added bwait/wwait/dwait commands Added kwait command Enabled 'f' command to forward a single line into the forth interpreter |
|
10-28-99 | Added debug extensions and dbg_xtnd.f auto reading
Misc Forth bug fixes: fixed bugs in forth trace code (stack corruption) fixed bug in (quit) - didn't clean stack properly fixed bug in forth local variables - { -- lout } caused errors fixed RI to remember the outmost name for nested includes include statistics are now in decimal Added int386 word to forth |
|
1-14-00 | More Forth cleanups -
Removed extra CR after a dbg_extension is executed Re-enabled calling native dbg cmds from compiled 4th words REALLY fixed forth RI to remember outermost file name Fixed nested CASE statements Added warning for long (non 8.3) script names Increased forth dictionary size Added :: and ;; words to allow word redfinition without complaints |
|
4-14-00 | Added -t command line option to specify a PCI slot
Fixed bug in identifying multi-function PCI devices Forth cleanups Allow \ comment inside local variable Allow Carriage Return inside local variable declaration Removed word 'test1' |
V2.14 | 7-25-00 | Added support for specifying pci_vendor/pci_device or pci_slot from env. variables |
Please email bug reports or comments to johnp (@t) probo dot com