Patch Level Update: PICC-18 STD
Patch Level Update: HI-TECH C PRO for the PIC10/12/16 MCU Family
NEW - PRO Compiler supporting Microchip PIC32 microcontrollers
Omniscient Code Generation: as featured in EDN Hot 100 Products of 2007.
The XA Single Board Computer from HI-TECH Software is designed to allow a potential user of the XA microcontroller to evaluate the chip, or implement a first prototype without the time and expense of producing a custom PCB.
XA Single Board Computer
Lucifer Source Debugger
Pin Connections and Memory Map
Entire Manual (PDF)*
Entire Manual (HTML)
*Acrobat files require the Acrobat Reader from Adobe.
Lucifer is a source level remote debugger for use with the HI-TECH C compilers. It consists of a program which runs on a host machine (usually MS-DOS or UNIX) and communicates with an XA- based microcontroller system via a serial line. The host program provides the user interface, including source code display, disassembly, displaying memory etc. The target system must have logic to read and write memory and registers, and implement single stepping. With each version of Lucifer a small program is provided which can be compiled and placed in a ROM in a target system to implement these features. The standard host program is set up to communicate with this ROM program via a serial line.
To use Lucifer you will need to have the compiler generate a symbol file, with symbol name, line number and file name symbols included. This can be produced using the XAC -G option. If you use the -H option you will get a symbol file which can be used by Lucifer, but which does not contain any source code level information.
You can also use HPDXA to produce HEX and symbol files suitable for use with Lucifer. The LUCIFER directory contains luctest.prj, a sample project file which you can use as a guide to compiling your own programs with HPDXA. See the Using HPDXA chapter for more details. The basic option needed is Options/Source level debug info. Lucifer can be invoked from the Run/Download ... menu item. Otherwise once you have produced HEX and symbol files, invoke Lucifer as follows:
If you are using an MS-DOS system, COMn should be COM1 or COM2. Lucifer will access a standard serial port addressed as either port. For UNIX simply specify the name of the serial port connected to your target, for example -p/dev/tty006 .
The default baud rate is 38400 for both MS-DOS and UNIX. The default serial port is COM1 for MS-DOS and /dev/ttya for UNIX. The -s (speed) and -p (port) options may be used to access ports other than the default. For UNIX /dev may be left off the device name and will automatically added, thus -ptty0 and -p/dev/tty0 will access the same device. For example, under MS-DOS, lucxa -s9600 -pcom2 will access COM2 at 9600 baud.
New default speed and port values may be set using the environment variable LUCXA_ARGS . LUCXA_ARGS may specify any mixture of valid Lucifer ` - ' options except filename options. For example, if you want the default options to be 4800 baud on port COM2, add the following line to your AUTOEXEC.BAT file:
In addition to the speed and port options Lucifer takes two optional arguments which are, in order of appearance, the name of the symbol file to use and the name of the .hex or .bin file to download. If no download file is specified, Lucifer will automatically search for .sym , .hex and .bin files which the same base name as the symbol name given. Thus the command:
would automatically locate and use test.sym and test.hex or test.bin . If you do not want to autoload your HEX or BIN file, give your symbol file a different base name to your HEX file.
When downloading binary files, Lucifer normally prompts for the download address. When downloading directly from the command line you can override this prompting by adding the option -Baddr[:end] to the LUCXA command line. For example, if you want to download a file test.bin at address $2000, you could use the command:
The optional :end value is the address at which the download should be terminated. For example, if you want to load the first $2000 bytes of test.bin from address $4000 to address $6000, use the command:
Lucifer should announce itself, then attempt to communicate with the target. If successful it prints a message sent by the target, identifying itself, e.g:
XA Lucifer Monitor (+ diagnostics) V4.3
Lucifer will then display a prompt : and wait for commands. For a list of commands, type ? and press return. Note that all commands should be in lower case.
2.2 Symbol Names In Expressions
Where Lucifer commands take numeric values or addresses as arguments; symbol names, register names and line numbers may be used. Symbols should be entered in exactly the same case as they were defined in the source code. Note that Lucifer cannot access auto variables or parameters by name. Where an expression is required, it may be of the forms in See Lucifer expression forms.
Lucifer expression forms
By default, in the b
(breakpoint) command any decimal number will be interpreted, as a line
number. However, in the u (unassemble) command
any number will be interpreted, by default, as a hex number representing
an address. These assumptions can always be overridden by using the
colon or dollar prefixes. When entering a symbol, it is not necessary
to type the underscore prepended by the C compiler. However, when printing
out symbols the debugger will always print the underscore. Any register
name may also be used where a symbol is expected.
2.2.1
Auto Variables and Parameters
Auto variables and parameters cannot be accessed by name with Lucifer. To examine the contents of an auto variable or parameter, the best approach is to disassemble (with the u command) a line of code referencing the variable, and dump the corresponding memory location (e.g. sp+4 ).
Lucifer recognizes the commands listed in See Lucifer Command Set.
Lucifer Command Set
2.3.1 The B command: set or display breakpoints
The b command is used to set and display breakpoints. If no expression is supplied after the b command, a list of all currently set breakpoints will be displayed. If an expression is supplied, a breakpoint will be set at the line or address specified. If you attempt to set a breakpoint which already exists, or enter an expression which Lucifer cannot understand, an appropriate error message will be displayed. Note: by default, any decimal number specified will be interpreted as a line number. If you want to specify an absolute address, prefix it with a dollar sign. For example:
: b 10
Set breakpoint at _main+$28
:
Breakpoints can also include a semicolon separated list of Lucifer commands, which will be executed when the breakpoint is encountered. This makes it possible to create breakpoints which stop, display a value and then restart execution. For example, the command:
creates a breakpoint which stops, displays the value of global variable pi and then continues execution.
2.3.2 The C command: display instruction at PC
The c command is used to display the assembler instruction and C source line addressed by the current value of the program counter. This is useful if you have been using other Lucifer commands and aren't quite sure where in the program the program counter is pointing. For example:
2.3.3 The D command: display memory contents
The d command is used to display a hex dump of the contents of memory on the target system. If no expressions are specified, 16 bytes are dumped from the address reached by the last d command. If one address is specified, 16 bytes are dumped from the address given. If two addresses are specified, the contents of memory from the first address to the second address are displayed. Dump addresses given can be symbols, line numbers, register names or absolute memory addresses.
2.3.4 The E command: examine
C source code
The e command is used to examine the C source code of a function or file. If a function name is given, Lucifer will locate the source file containing the function requested and display from just above the start of the function. If a file name is given, Lucifer will display from line 1 of the requested file. For example:
: e
main
2:
3:int value, result;
4:
5:main()
6:{
7: scanf("%d",&value);
8: result = (value << 1) + 6;
9: printf("result = %d\n",result);
10:}
:
2.3.5 The G command: commence execution
The g command is used to commence execution of code on the target system. If no expression is supplied after the g command, execution will commence from the current value of PC (the program counter). If an expression is supplied, execution will commence from the address given. Execution will continue until a breakpoint is reached, or the user interrupts with control -C . After a breakpoint has been reached, execution can be continued from the same place using the g , s and t commands.
2.3.6 The I command: toggle instruction trace mode
The i command is used to toggle instruction trace mode. If instruction trace mode is enabled, each instruction is displayed before it is executed while stepping by entire C lines with the s command. For example, with instruction trace disabled, step behaves like this:
: s
result = 20
Stepped to
10:}
:
With instruction trace enabled, step will instead behave like this:
: s
_memtest+30H push r4
_memtest+32H mov r0,#04A2H
_memtest+36H push r0
_memtest+38H fcall _printf
_memtest+3CH adds r7,#4
result = 20
Stepped to
10:}
:
Note that the library function printf() was not traced and thus operated properly and at full speed.
2.3.7 The L command: load a hex file
The l command is used to load object files into the target system. Lucifer correctly handles Motorola S-record format object files, Intel HEX files and binary images.
2.3.8 The M command: modify memory
The m command is used to write one or more values or ascii strings into memory at a specified address. This command takes the form:
where addr is the address to write to and all following arguments are values or strings to write to memory. Strings may use either single or double quotes. For example:
2.3.9 The Q command: exit to operating system
The q command is used to exit from Lucifer to the operating system. Note: the q command does not stop the target system (that is, the Lucifer monitor running on the target system), so it is possible to re-enter Lucifer without re-initializing the target.
2.3.10 The R command: remove breakpoints
The r command is used to remove breakpoints which have been set with the b command. If no arguments are given the user is prompted for each breakpoint in turn. For example:
: r
Remove _main+$28 ? y
Remove _main+$44 ? n
Remove _test ? n
: r main+$44
Removed breakpoint _main+$44
:
2.3.11 The S command: step one line
The s command is used to step by one line of C or assembler code. For example:
: s
Stepped to
7: scanf("%d", &value);
: s
Target wants input: 7
Stepped to
8: result = (value << 1) + 6;
: s
Stepped to
9: printf("result = %d\n",result);
: s
result = 20
Stepped to
10:}
:
This is normally implemented by executing several machine instruction single steps, and therefore can be quite slow. If Lucifer can determine that there are no function calls or control structures (break, continue, etc.) in the line, it will set a temporary breakpoint on the next line and execute the line at full speed. When single stepping by machine instructions, the step command will execute subroutine calls to external and library functions at full speed. This avoids the slow process of single stepping through complex library routines like printf(). Normal library console I/O works correctly during single stepping using the s command. Where no line number information is available, such as inside library routines, the s command becomes an assembler step like the t command.
2.3.12 The T command: trace one instruction
The t command is used to trace one machine instruction on the target. The current value of PC (the program counter) is used as the address of the instruction to be executed. After the instruction has been executed, the next instruction and the contents of all registers will be displayed.
2.3.13 The U command: disassemble machine instructions
The u command disassembles object code from the target system's memory. For example:
: u
9: printf("result = %d\n", result);
_memtest+30H push r4,r5
_memtest+32H mov r0,#04A2H
_memtest+36H push r0
_memtest+38H fcall _printf
_memtest+3CH adds r7,#6
If an expression is supplied, the disassembly commences from the address supplied. If an address is not supplied, the disassembly commences from the instruction where the last disassembly ended. The disassembler automatically converts addresses in the object code to symbols if the symbol table for the program being disassembled is available. If the source code for a C program being disassembled is available, the C lines corresponding to each group of instructions are also displayed. Note: by default, any values specified will be interpreted as absolute addresses. If you want to specify a line number, prefix it with a colon.
2.3.14 The W command: upload binary
The w command is used to upload and write a chunk of target memory as a binary file. This command takes three arguments: filename, start address and length. The start address and length values are in hex. For example, if the Lucifer monitor ROM were at $7000 to $7FFF in the target system, it could be uploaded to a binary file with the command:
: w
lucrom.bin 7000 1000
........
Uploaded 4096 (0x1000) bytes to lucrom.bin
:
2.3.15 The X command: examine or change registers
The x command is used to examine and change the contents of the target CPU registers. If no parameters are given, the registers are displayed without change. To change the contents of a register, two parameters must be supplied, a valid register name and the new value of the register. After setting a new register value, the contents of the registers are displayed. For example:
Any valid XA register name may be used. The PSW may be accessed as PSW for the entire 16 bit value, or as PSWL and PSWH for the low and high bytes respectively.
2.3.16 The @
command: display C variables
The @ command is used to examine the contents of memory interpreted as one of the standard C types. The form of the @ command is:
where t is the type of the variable to be displayed, * consists of zero or more indirection operators (" * " or " n* "), and expr is the address of the variable to be displayed. See Lucifer @ command variants , shows the available @ command variants. For example, to display a long variable longvar in hex:
To display a character, pointed to by a pointer cptr :
To de-reference ihandle : a pointer to a pointer to an unsigned int:
After displaying the variable, the current address is advanced by the size of the type displayed. This, makes it possible to step through arrays by repeatedly pressing return. On-line help for the @ command may be obtained by entering ?@ at the " : " prompt.
Lucifer @ command variants
2.3.17 The . command: set a breakpoint and go
The . command is used to set a temporary breakpoint and resume execution from the current value of PC (the program counter). Execution continues until any breakpoint is reached or the user interrupts with control -C , then the temporary breakpoint is removed. Note: the temporary breakpoint is removed even if execution stops at a different breakpoint or is interrupted. If no breakpoint address is specified, the . command will display a list of active breakpoints.
: .
10
Target wants input: 7
result = 20
Breakpoint
10:}
main+$28 RET
:
2.3.18 The ; command: display from a source line
The ; command is used to display 10 lines of source code from a specified position in a source file. If the line number is omitted, the last page of source code displayed will be re-displayed. For example:
: ;
4
4:
5: main()
6: {
7: scanf("%d",&value);
8: result = (value << 1) + 6;
9: printf("result = %d\n",result);
10:}
2.3.19 The = command: display next page of source
The = command is used to display the next 10 lines of source code from the current file. For example, if the last source line displayed was line 7, = will display 10 lines starting from line 8.
2.3.20 The - command: display previous page of source
The - command is used to display the previous 10 lines of source code from the current file. For example, if the last page displayed started at line 15, - will display 10 lines starting from line 5.
2.3.21 The / command: search source file for a string
The / command is used to search the current source file for occurrences of a sequence of characters. Any text typed after the / is used to search the source file. The first source line containing the string specified is displayed. If no text is typed after the / , the previous search string will be used. Each string search starts from the point where the previous one finished, allowing the user to step through a source file finding all occurrences of a string.
: /printf
10: printf("Enter a number:");
: /
14: printf("Result = %d\n",answer);
: /
Can't find printf
:
2.3.22 The ! command: execute a DOS command
The ! command is used to execute an operating system shell command line without exiting from Lucifer. Any text typed after the ! is passed through to the shell without modification.
In addition to the commands listed above, Lucifer will interpret any valid decimal number typed as a source line number and attempt to display the C source code for that line.
Pressing return without entering a command will result in re-execution of the previous command. In most cases the command resumes where the previous one left off. For example, if the previous command was d 2000 , pressing return will have the same effect as the command d 2010 .
If return is pressed after a breakpoint or . command has executed, it is equivalent to disassembling from the breakpoint address.
2.4 User Input and Output with Lucifer
The standard versions of the console I/O routines putch(), getch(), getche() and init_uart() are configured to work automatically with LUCIFER. Code which is downloaded under LUCIFER may use the standard I/O routines like printf() without any library modifications. Once you have finished debugging your code, you will need to insert into the library console, I/O routines suitable for your hardware. You can use the file sources/getch.c as a starting point.
2.5 Installing Lucifer on a Target
In order to use Lucifer on your target system, you will need to compile the Lucifer monitor and place it in a ROM. If your XA system already has a monitor in ROM, it is also possible to download the Lucifer target code into RAM. In most cases you will be able to use the Lucifer monitor program supplied without much modification.
Normally the only changes required will be the baud rate initialization in init_uart(). This requires modification for different baud rates or different clock frequencies. There are self-explanatory comments, in the code, at that point. If you do not wish to use one of the XA internal serial ports you will need to modify the target code to access a different serial port.
2.5.1 Modifying the Target Code
Most modifications to target.c will be made to the serial I/O functions, putch(), getch() and init_uart(). For Lucifer to work correctly, you will need to have a system with common code and external RAM space as it is not possible to write to code memory on the XA. This can be achieved by mapping 32K of ROM from 0000h to 7FFFh and 32K of static RAM from 8000h to FFFFh and using PSEN and RD NOR'ed together for chip select.
Since the XA has separate, overlapping address spaces for code and data, special memory mapping is required to allow code to be downloaded and executed. It is important to map RAM into the first 64K of the data space, since the stack pointer in system mode is only 16 bits. Therefore a suggested arrangement is as follows. A ROM (size not important) is mapped into code space at 0. This will hold the Lucifer target program. A RAM of adequate size (say 128K bytes for example purposes) is mapped into data space at two locations; at 0, and at a higher address, say 80000h. It is also mapped into code space at the higher address, i.e. at 80000h it appears as both code and data. This now allows Lucifer to download code into the ROM at the high address, and execute it from there. A portion of the RAM is reserved for data. The target code as supplied reserves 32K bytes of RAM for data, so user programs are downloaded at 88000h. The monitor program uses RAM from 7F00h to 7FFFh, so RAM from 20h (above the registers) to 7EFFh is available for the user program. With this arrangement, you would compile the Lucifer target program with the following addresses:
ROM address 0
RAM address 7F00
RAM size 100
and when compiling your application program you will compile with these addresses:
ROM address 88000
RAM address 20
RAM size 7EE0
Target.c reflects all unused interrupt vectors to the start of the user program area (88000 in the example above). This address is selected by the macro RAMBASE in target.c . All interrupts will be reflected to RAMBASE +vector, for example interrupt vector 84h will be reflected to 88084h. This means you do not have to change your program whether it is compiled to run under Lucifer or stand-alone in ROM, since the vector addresses are interpreted as being relative to the ROM start address.
Copyright © 2008 HI-TECH Software • Trademarks • Forum
Site Map