# SDCC Compiler User Guide

## Debugging

There are several approaches to debugging your code. This chapter is meant to show your options and to give detail on some of them:

When writing your code:

- write your code with debugging in mind (avoid duplicating code, put conceptually similar variables into structs, use structured code, have strategic points within your code where all variables are consistent,...)
- run a syntax-checking tool like splint range none pageformat default splint (syntax checking tool) range none pageformat default lint (syntax checking tool) (see --more-pedantic Other Options) over the code.
- for the high level code use a C-compiler (like f.e. GCC) to compile run and debug the code on your host. See (see --more-pedantic Other Options) on how to handle syntax extensions like __xdata, __at(),...
- use another C-compiler to compile code for your target. Always an option but not recommended:) And not very likely to help you. If you seriously consider walking this path you should at least occasionally check portability of your code. Most commercial compiler vendors will offer an evaluation version so you can test compile your code or snippets of your code.
Debugging on a simulator:

- there is a separate section about SDCDB (section Debugging with SDCDB) below.
- or (8051 specific) use a free open source/commercial simulator which interfaces to the AOMF range none pageformat default AOMF, AOMF51 file (see Single Source File Projects) optionally generated by SDCC.
Debugging On-target:

- use a MCU port pin to serially output debug data to the RS232 port of your host. You'll probably want some level shifting device typically involving a MAX232 or similar IC. If the hardware serial port of the MCU is not available search for 'Software UART' in your favourite search machine.
- use an on-target monitor. In this context a monitor is a small program which usually accepts commands via a serial line and allows to set program counter, to single step through a program and read/write memory locations. For the 8051 good examples of monitors are paulmon and cmon51 (see section Related open source tools).
- toggle MCU port pins at strategic points within your code and use an oscilloscope. A *digital oscilloscope* range none pageformat default Oscilloscope with deep trace memory is really helpful especially if you have to debug a realtime application. If you need to monitor more pins than your oscilloscope provides you can sometimes get away with a small R-2R network. On a single channel oscilloscope you could for example monitor 2 push-pull driven pins by connecting one via a 10 k $\Omega$ resistor and the other one by a 5 k $\Omega$ resistor to the oscilloscope probe (check output drive capability of the pins you want to monitor). If you need to monitor many more pins a *logic analyzer* will be handy.
- use an ICE (*i* n *c* ircuit *e* mulator range none pageformat default ICE (in circuit emulator)). Usually very expensive. And very nice to have too. And usually locks you (for years...) to the devices the ICE can emulate.
- use a remote debugger. In most 8-bit systems the symbol information is not available on the target, and a complete debugger is too bulky for the target system. Therefore usually a debugger on the host system connects to an on-target debugging stub which accepts only primitive commands.
Terms to enter into your favourite search engine could be 'remote debugging', 'gdb stub' or 'inferior debugger'. (is there one?)
- use an on target hardware debugger. Some of the more modern MCUs include hardware support for setting break points and monitoring/changing variables by using dedicated hardware pins. This facility doesn't require additional code to run on the target and *usually* doesn't affect runtime behaviour until a breakpoint is hit. For the mcs51 most hardware debuggers use the AOMF range none pageformat default AOMF, AOMF51 file (see Single Source File Projects) as input file.
Last not least:

- if you are not familiar with any of the following terms you're likely to run into problems rather sooner than later: *volatile*, *atomic*, *memory map*, *overlay*. As an embedded programmer you *have* to know them so why not look them up *before* you have problems?)
- tell someone else about your problem (actually this is a surprisingly effective means to hunt down the bug even if the listener is not familiar with your environment). As 'failure to communicate' is probably one of the job-induced deformations of an embedded programmer this is highly encouraged.

### Debugging with SDCDB range none pageformat default SDCDB (debugger)

SDCC is distributed with a source level debugger range none pageformat default Debugger. The debugger uses a command line interface, the command repertoire of the debugger has been kept as close to gdb range none pageformat default gdb (the GNU debugger) as possible. The configuration and build process is part of the standard compiler installation, which also builds and installs the debugger in the target directory specified during configuration. The debugger allows you debug BOTH at the C source and at the ASM source level.

#### Compiling for Debugging

The --debug range none pageformat default --debug option must be specified for all files for which debug information is to be generated. The compiler generates a.adb file for each of these files. The linker creates the.cdb range none pageformat default file from the.adb range none pageformat default files and the address information. This.cdb is used by the debugger.

#### How the Debugger Works

When the --debug option is specified the compiler generates extra symbol information some of which are put into the assembler source and some are put into the.adb file. Then the linker creates the.cdb file from the individual.adb files with the address information for the symbols. The debugger reads the symbolic information generated by the compiler & the address information generated by the linker. It uses the SIMULATOR (Daniel's S51) to execute the program, the program execution is controlled by the debugger. When a command is issued for the debugger, it translates it into appropriate commands for the simulator. (Currently SDCDM only connects to the simulator but *newcdb* at http://ec2drv.sourceforge.net/ is an effort to connect directly to the hardware.)

#### Starting the Debugger SDCDB

The debugger can be started using the following command line. (Assume the file you are debugging has the file name foo).

**sdcdb foo
**
The debugger will look for the following files.

- foo.c - the source file.
- foo.cdb - the debugger symbol information file.
- foo.ihx - the Intel hex format range none pageformat default Intel hex format object file.

#### SDCDB Command Line Options

- --directory=<source file directory> this option can used to specify the directory search list. The debugger will look into the directory list specified for source, cdb & ihx files. The items in the directory list must be separated by ':', e.g. if the source files can be in the directories /home/src1 and /home/src2, the --directory option should be --directory=/home/src1:/home/src2. Note there can be no spaces in the option.
- -cd <directory> - change to the <directory>.
- -fullname - used by GUI front ends.
- -cpu <cpu-type> - this argument is passed to the simulator please see the simulator docs for details.
- -X <Clock frequency > this options is passed to the simulator please see the simulator docs for details.
- -s <serial port file> passed to simulator see the simulator docs for details.
- -S <serial in,out> passed to simulator see the simulator docs for details.
- -k <port number> passed to simulator see the simulator docs for details.

#### SDCDB Debugger Commands

As mentioned earlier the command interface for the debugger has been deliberately kept as close the GNU debugger gdb, as possible. This will help the integration with existing graphical user interfaces (like ddd, xxgdb or xemacs) existing for the GNU debugger. If you use a graphical user interface for the debugger you can skip this section.

break [line | file:line | function | file:function]

Set breakpoint at specified line or function:

**sdcdb>break 100
sdcdb>break foo.c:100
sdcdb>break funcfoo
sdcdb>break foo.c:funcfoo

clear [line | file:line | function | file:function]

Clear breakpoint at specified line or function:

**sdcdb>clear 100
sdcdb>clear foo.c:100
sdcdb>clear funcfoo
sdcdb>clear foo.c:funcfoo

continue

Continue program being debugged, after breakpoint.

finish

Execute till the end of the current function.

delete [n]

Delete breakpoint number 'n'. If used without any option clear ALL user defined break points.

info [break | stack | frame | registers]

- info break - list all breakpoints
- info stack - show the function call stack.
- info frame - show information about the current execution frame.
- info registers - show content of all registers.
step

Step program until it reaches a different source line. Note: pressing <return> repeats the last command.

next

Step program, proceeding through subroutine calls.

run

Start debugged program.

ptype variable

Print type information of the variable.

print variable

print value of variable.

file filename

load the given file name. Note this is an alternate method of loading file for debugging.

frame

print information about current frame.

set srcmode

Toggle between C source & assembly source.

! simulator command

Send the string following '!' to the simulator, the simulator response is displayed. Note the debugger does not interpret the command being sent to the simulator, so if a command like 'go' is sent the debugger can loose its execution context and may display incorrect values.

quit

"Watch me now. Iam going Down. My name is Bobby Brown"

#### Interfacing SDCDB with DDD

The screenshot was converted from png to eps with:" bmeps -c -e8f -p3 ddd_example.png >ddd_example.eps" which produces a pretty compact eps file which is free from compression artifacts. The screenshot was included in sdccman.lyx cvs version 1.120 but later removed as this broke the build system on Sourceforge (pdf-file was broken. pdflatex does not accept eps files).

The *p* ortable *n* etwork *g* raphics File http://sdcc.sourceforge.net/wiki_images/ddd_example.png shows a screenshot of a debugging session with DDD range none pageformat default DDD (debugger) (Unix only) on a simulated 8032. The debugging session might not run as smoothly as the screenshot suggests. The debugger allows setting of breakpoints, displaying and changing variables, single stepping through C and assembler code.
The source was compiled with **

sdcc --debug ddd_example.c
**
and DDD was invoked with **

ddd -debugger "sdcdb -cpu 8032 ddd_example"

Check that the double quotes or an apostrophe within the command line survive the \SpecialChar LyX tool chain. Previously the apostrophes got slanted in the PDF output so a cut and paste did not work.

#### Interfacing SDCDB with XEmacs range none pageformat default XEmacs range none pageformat default Emacs

Two files (in emacs lisp) are provided for the interfacing with XEmacs, sdcdb.el and sdcdbsrc.el. These two files can be found in the $(prefix)/bin directory after the installation is complete. These files need to be loaded into XEmacs for the interface to work. This can be done at XEmacs startup time by inserting the following into your '.xemacs' file (which can be found in your HOME directory):

(load-file sdcdbsrc.el)

.xemacs is a lisp file so the () around the command is REQUIRED. The files can also be loaded dynamically while XEmacs is running, set the environment variable 'EMACSLOADPATH' to the installation bin directory (<installdir>/bin), then enter the following command ESC-x load-file sdcdbsrc. To start the interface enter the following command:

**ESC-x sdcdbsrc**

You will prompted to enter the file name to be debugged.

The command line options that are passed to the simulator directly are bound to default values in the file sdcdbsrc.el. The variables are listed below, these values maybe changed as required.

- sdcdbsrc-cpu-type '51
- sdcdbsrc-frequency '11059200
- sdcdbsrc-serial nil
The following is a list of key mapping for the debugger interface.

;; Current Listing::
;;key binding Comment
;;-----------------
;;
;; n sdcdb-next-from-src SDCDB next command
;; b sdcdb-back-from-src SDCDB back command
;; c sdcdb-cont-from-src SDCDB continue command
;; s sdcdb-step-from-src SDCDB step command
;;? sdcdb-whatis-c-sexp SDCDB ptypecommand for data at
;; buffer point
;; x sdcdbsrc-delete SDCDB Delete all breakpoints if no arg
;; given or delete arg (C-u arg x)
;; m sdcdbsrc-frame SDCDB Display current frame if no arg,
;; given or display frame arg
;; buffer point
;;! sdcdbsrc-goto-sdcdb Goto the SDCDB output buffer
;; p sdcdb-print-c-sexp SDCDB print command for data at
;; buffer point
;; g sdcdbsrc-goto-sdcdb Goto the SDCDB output buffer
;; t sdcdbsrc-mode Toggles Sdcdbsrc mode (turns it off)
;;
;; C-c C-f sdcdb-finish-from-src SDCDB finish command
;;
;; C-x SPC sdcdb-break Set break for line with point
;; ESC t sdcdbsrc-mode Toggle Sdcdbsrc mode
;; ESC m sdcdbsrc-srcmode Toggle list mode
;;

### Debugging with other debuggers (e.g. GDB): ELF / DWARF

For some ports, SDCC can create ELF binaries with DWARF debug information. This can e.g. be used for on-target debugging on STM8 using OpenOCD and GDB. To do so, compile with --debug --out-fmt-elf. Note that --out-fmt-elf needs to be specified both when compiling (to generate the debug info in DWARF rather than CDB format) and linking (to get an ELF binary instead of Intel Hex).
