Skip to main content
Department of Information Technology

This page has been updated by Jakob to contain more tips on how to use Simics to trace and diagnose interrupt problems. So it is better than the 2004 edition.

Debug Tips

Tracing Activity in Simics

The nice thing about a simulator like Simics compared to real hardware is that it is a much more controlled and inspectable environment. Simics can see everything that goes on in the simulated system, and can stop at any point. There are several ways to observe the activity of the system and the kernel code in Simics. All of these commands are given at the Simics command prompt, not in gbd or in the target system console.

Another important factor to notice is that Simics is perfectly deterministic. This means that there are no "glitches" or "flukes" in the system. If something does not work when you try to boot your operating system, it will not work in precisely the same way the next time you try to start it.

Inspecting the State

  • To inspect the values of the CPU registers, use the command pregs.
  • To inspect the states of all registers in the CPU, including the status of the counter registers and status flags, use pregs -all.
  • Use the command da to disassemble code at the current PC. Use da ADDRESS to disassemble a certain address.
  • To see the history of accesses to a device, use the command DEV.log. Where DEV is a device name like rtc0 or pic0. To see the current size or change the size of the history buffer, use the command io-buffer DEV.
  • To trace all accesses to a device, use the command trace-io DEV. You can trace all devices accesses by the command trace-io -all.
  • Some devices have commands like DEV.info or DEV.status that give information about its current state. Try help DEV for a list of the available commands. For example, the rtc0 device has some interesting commands available.
  • In case trace-io does not give enough information, you can try looking at the debug information from a device. To enable debug printouts, set the debug level of the device using the command DEV.debug-level N. Use N=0 to turn output off, and N=3 to get the maximum amount of information.
  • To list the contents of the TLBs in the processor, use cpu0.print-tlb.
  • To see the effects of the virtual memory system (i.e. the contents of the processor TLBs), you can use cpu0.x ADDR to read the memory at virtual address ADDR. To translate an address via the TLBs, use cpu0.l2p ADDR.
  • To look at data as binary or hexadecimal, use the print (shortform p) command with the -x and -b options. This is useful to inspect flag fields, for instance. Examples:

simics> p -x %pc
0x802080d0
simics> p -x %count
0x438be0
simics> p %count
4426720
simics> p -b 45
0b101101

Controlling Execution

  • To stop Simics so that you can give commands to it, press ctrl-c at the Simics command-line.
  • To single-step code (at the assembly level), use si.
  • To run a certain number of instructions, use c N, where N is the number of instructions to execute.

  • To stop Simics when a certain device is accessed, use the command break-io DEV. For a list of available devices, do tab completion on break-io device =. Also see Chapter 12 of the user's guide (Unix, Chapter 10 on Windows) for more information on Simics breakpointing.
  • To stop Simics when a control register changes its value, use break-cr REG. Use tab-completion on the command (in the form break-cr register =) to find out which registers are considered to be control registers.

  • To stop Simics when an exception, interrupt, or trap happens, use break-exception EVENT. Use tab-completion on the command (in the form break-exception name =) to find out the available events.
  • To stop Simics when memory accesses hit certain locations, use the break command. The arguments to break are the STARTING ADDRESS to watch, the LENGTH of the area to watch, and some flags for the conditions we want to break at: read, write, or instruction execute. For example, to stop when we read any address in the range 0xFF00 to 0xFFFF (which is an area 256 bytes long), give the command:

break 0xFF00 256 -r 

To trigger on a write as well, add the -w flag to the command.
  • You can use a facility known as magic instructions to make the program running in the simulator tell the simulator when it has reached certain points. A magic instruction is an instruction that has no effect on real hardware, but which is caught by the simulator. To stop the simulation when a magic instruction is reached, use the command magic-break-enable. See the Simics manual for more on how magic instructions are defined and used.
  • To send a text string to the simulated machine console, you can either type it directly in simulated machine console, or use the command con0.input TEXT. The text will be input once the simulation is started again. This makes it possible to write Simics scripts that control the simulated machine.

Chasing Interrupt Problems

Simics is a very powerful tool for diagnosing interrupt-related problems. To see what happens when interrupts occur or do not occur, the following commands are handy:

  • trace-exception -all will print all exceptions that occur
  • break-exception will break when an exception occurs
  • trace-cr -all will trace all changes to control registers. For interrupt handling, cause, count, status, epc are of special interest.
  • break-cr will break when control registers change
  • pregs -all will print all register, including the status register bits and count and compare.

Note that when using tracing, you should only run Simics for limited amounts of time, or you will overwhelm the console with output. Running a few million instructions at a time is useful.

Example output from using trace:

simics> c 2000000
[cpu0] (@ cycle 4995962) Exception 8: Syscall
[cpu0] status <- 0x10008000
[cpu0] status <- 0x10008000
[cpu0] cause <- 0
[cpu0] epc <- 0x80020464

Example output from using pregs:

Status   0x10008003
    0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 
    C C C C R - R - - B T S N - - - I I I I I I I I - - - U - E E I
    U U U U P   E     E S R M       M M M M M M M M       M   R X E
    3 2 1 0           V     I       7 6 5 4 3 2 1 0           L L  

Cause    0x00000020
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 
    B - C C - - - - I W - - - - - - I I I I I I I I - E E E E E - -
    D   E E         V P             P P P P P P P P   X X X X X    
        1 0                         7 6 5 4 3 2 1 0   C C C C C    

Example from using con0.input to send text to the console and then tracing what happens, also using a log on the tty0 device to see the characters being entered.

simics> trace-exception -all
simics> trace-cr -all
simics> tty0.log-level 3
[tty0] Changing log level: 1 -> 3
simics> con0.input "abc"
simics> c 100000
[tty0 info] Issue IRQ irqs=0x03 (issued)
[tty0 info] Raising interrupt 2
[cpu0] (@ cycle 6109318) Exception 0: Interrupt
[cpu0] status <- 0
[tty0 info] Clearing interrupt 2
[tty0 info] Receive character 'a' (0x61)
[tty0 info] Transmit character 'a' (0x61)
[cpu0] cause <- 0
[cpu0] epc <- 0x80028b9c
[cpu0] status <- 0x10008403
[tty0 info] Issue IRQ irqs=0x02 (not issued)
[tty0 info] Issue IRQ irqs=0x03 (issued)
[tty0 info] Raising interrupt 2
[cpu0] (@ cycle 6111316) Exception 0: Interrupt
[cpu0] status <- 0
[tty0 info] Clearing interrupt 2
[tty0 info] Receive character 'b' (0x62)
[tty0 info] Transmit character 'b' (0x62)
[cpu0] cause <- 0
[cpu0] epc <- 0x80028b9c
[cpu0] status <- 0x10008403
[tty0 info] Issue IRQ irqs=0x02 (not issued)
[tty0 info] Issue IRQ irqs=0x03 (issued)
[tty0 info] Raising interrupt 2
[cpu0] (@ cycle 6113314) Exception 0: Interrupt
[cpu0] status <- 0
[tty0 info] Clearing interrupt 2
[tty0 info] Receive character 'c' (0x63)
[tty0 info] Transmit character 'c' (0x63)
[cpu0] cause <- 0
[cpu0] epc <- 0x80028b9c
[cpu0] status <- 0x10008403
[tty0 info] Issue IRQ irqs=0x02 (not issued)
[cpu0] v:0x80028b9c p:0x00028b9c  j 0x80028b9c

As you can see, a wealth of information on what is happening in the system.

Help on Simics

Simics provides a very nice online help system. Type help to get help on how to use it. In particular, help CMD provides help on the command CMD.

Using gbd Remote

Use the gdb debugger in remote mode to debug C code running on the simulated computer in Simics. To start a remote debug section, do the following steps (see the gdb introduction lab for a hands-on example on how to do this):

  • Start Simics the usual way and load your program binaries.
  • Run Simics for a few instructions to get everything set ut in a way acceptable to gdb. Typically, c 10 should suffice.
  • Give the command gdb-remote port=NNNN at the Simics command line. This will start the gdb remote deamon in Simics, and have it listen to port NNNN. Try to use a different port from other groups running on the same computer!
  • Move over to a fresh shell, and start the MIPS cross-gdb. The gbd program to use can be found at /it/kurs/compsys/mips-devel/bin/mips-idt-elf-gdb. Start it in the directory containing the binary of the program to debug. For example:

host$ /it/kurs/compsys/mips-devel/bin/mips-idt-elf-gdb example_timer
GNU gdb 5.2.1
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "--host=sparc-sun-solaris2.7 --target=mips-idt-elf"...
(gdb)

  • When gdb has started, give the command target remote HOST:NNNN to make it connect to Simics. HOST is the name of the computer where Simics is running, and NNNN is the port number you gave in Simics. Simics should give the following output:

[gdb0] New GDB connection established

  • If this operation succeeds, you will now be able to debug from within gdb. Do not try to control the execution from Simics, since that will give strange results. You can however use the Simics state inspection commands as discussed above while gdb is controlling the execution. Note that the Simics command-line can behave strangely, printing a new prompt each time gdb stops.

Output from the Simulated Machine

On real embedded hardware, you do not have the luxury of a simulator that can tell you where your program is at and what it is doing. In such cases, you have to use whatever other output channels are available in the hardware to provide information over the state and events in the system.

In our case, we have one such easy output option available: the Malta LCD display, which can display eight characters. This can be used to print out simple messages like booting or idle or printing.

Updated  2005-03-06 11:03:45 by Jakob Engblom.