Skip to main content
Department of Information Technology

Simics Introduction Lab

The purpose of this lab is to get you started with Simics, and show you some of the basic debug functionality. We assume that you start by installing Simics as detailed in the installation guide.

Run Simics with Linux on a MIPS machine

Now that you're Simics installation is completed, let's take the simulator for a spin. We will do this by running a Montavista Linux on Simics/MIPS.

  • Change directory to $SIMICS/home/mips32-test-machine/:

host$ cd ~/simics/home/mips32-test-machine

  • Start simics, using the simics script present in all home directories. As the argument, give the name of the startup script (.simics file) that you will use to start Simics. In our case, the startup file is called linux.simics.

host$ ./simics linux.simics

  • If this fails to work, please see the clarifications and FAQ page for more information.
  • When Simics starts, two windows should open on the side (one text terminal and one small window representing the LCD display of the simulated Malta board). In the main window, you will be greeted by a Simics startup message, and finally you will arrive at the Simics command prompt:

Checking out a license... done: academic license.
Looking for additional Simics modules in /home/jakob/simics/v9-sol8-64/lib
                         
  +----------------+    Copyright 1998-2004 by Virtutech, All Rights Reserved
  |   Virtutech    |    Version: simics-2.0.16
  |     Simics     |    Compiled: Sun Jul 11 01:54:46 MEST 2004
  +----------------+
    www.simics.com      "Virtutech" and "Simics" are trademarks of Virtutech AB

Type 'copyright' for details on copyright.
Type 'license' for details on warranty, copying, etc.
Type 'readme' for further information about this version.
Type 'help help' for info on the on-line documentation.

simics> 

  • Start booting Linux by typing the command c, for "continue":

simics> c

  • Now, you should be getting output in the text terminal and a scrolling text in the LCD window (and a bunch of warnings about memory accesses in the main Simics window). After a short while, the simulated Montavista Linux will arrive at its command-prompt (in the text terminal that has green text on a black background).
  • Try som Linux commands at this prompt, such as ls, cd, pwd. Use cat to look at the contents of files. To see what the Linux thinks that it is running from, try cat /proc/cpuinfo:

sh-2.04# cat /proc/cpuinfo
cpu                     : MIPS
cpu model               : R4310 V0.0
system type             : unknown unknown
BogoMIPS                : 9.93
byteorder               : little endian
unaligned accesses      : 0
wait instruction        : no
microsecond timers      : no
extra interrupt vector  : yes
hardware watchpoint     : yes
VCED exceptions         : not available
VCEI exceptions         : not available

  • To get back to the Simics prompt, we need to stop the simulation. Bring the Simics console to the front and press ctrl-C. This should get you back to the Simics prompt. When we stop simulation in this manner, Simics will show you the instruction where execution was stopped, with both its virtual (v:) and physical address shown (p:):

Ctrl-C pressed. Interrupting simulation, please wait.
[cpu0] <v:0x80108710> <p:0x00108710> nop
simics>

Using the Simics command prompt

From the Simics command prompt, you have a lot of powerful commands available. Here we will walk you through the most basic information; for more commands and tips on debugging, look at the debug tips page.

  • Simics is a object-oriented environment: the entire simulated machine is built up from a number of objects, typically one for each device, processor, or memory in the system. Plus some for the basic functionality provided by Simics. All objects have commands defined for them, some more and some less. To see all objects in the current simulation, use the command list-objects:

simics> list-objects 
Class           Object                | Class           Object               
--------------------------------------+--------------------------------------
<DS12887>       rtc0                  | <memory-space>  pci_bus0_memory_space
<GT64120-pci>   gt64120_0_pci_0_0     | <memory-space>  pci_bus1_conf_space  
<GT64120-pci>   gt64120_0_pci_0_1     | <memory-space>  pci_bus1_io_space    
<GT64120-pci>   gt64120_0_pci_1_0     | <memory-space>  pci_bus1_memory_space
<GT64120-pci>   gt64120_0_pci_1_1     | <memory-space>  phys_mem0            
<GT64120>       gt64120_0             | <mips-4kc>      cpu0                 
<NS16550>       tty0                  | <pci-bus>       pci_bus0             
<context>       primary_context       | <pci-bus>       pci_bus1             
<hostfs>        hfs0                  | <python>        python               
<i8259x2>       pic0                  | <ram>           memory0              
<image>         memory0_image         | <recorder>      rec0                 
<image>         rom0_image            | <rom>           rom0                 
<malta>         malta0                | <sim>           sim                  
<memory-space>  cbus_space            | <text-console>  con0                 
<memory-space>  pci_bus0_conf_space   | <text-console>  display0             
<memory-space>  pci_bus0_io_space     |                                

  • Noteworthy objects are cpu0, the processor, and tty0, the serial port connected to the con0 console. Try the command help on these objects to see the command available for each.

simics> help con0
The text-console class provides a text user interface to the simulated 
machine's console.

The following commands are available for the 'con0' object:

 <text-console>.break                    - set a string to break on
 <text-console>.capture-start            - capture output to file
 <text-console>.capture-stop             - stop output capture to file
 <text-console>.info                     - print information about the device
 <text-console>.input                    - send string to a console
 <text-console>.input-file               - input a file into a console
 <text-console>.kbd-abort                - send a keyboard abort signal
 <text-console>.list-break-strings       - list all strings the console will
                                           break on
 <text-console>.no-window                - show/hide console window
 <text-console>.quiet                    - toggle console output redirection
 <text-console>.read-only                - toggle read-only mode
 <text-console>.status                   - print status of the device
 <text-console>.switch-to-server-console - replace text console with server
                                           console
 <text-console>.unbreak                  - stop breaking on string

  • To look at the code we are about execute, use the da (disassemble) command. To look at the location from the current program counter and on, we will use the short form "%pc":

simics> da %pc
[cpu0] <v:0x80108704> <p:0x00108704> lui        v0,0x8025

  • Looking a single instruction is not very useful, however. There are better ways of doing things, and to find out about them, use the Simics help command:

simics> help da
[... output from Simics ...]

  • Note the "count" parameter that can be given to the command. By specifying this, we can disassemble more instructions:

simics> da %pc 10
v:0x80100604 p:0x00100604  lui gp,0x8010
v:0x80100608 p:0x00100608  addiu gp,gp,24576
v:0x8010060c p:0x0010060c  addiu t0,gp,8160
v:0x80100610 p:0x00100610  lui at,0x8025
v:0x80100614 p:0x00100614  sw t0,-23752(at)
v:0x80100618 p:0x00100618  addiu sp,t0,-16
v:0x8010061c p:0x0010061c  lui t0,0x80a6
v:0x80100620 p:0x00100620  addiu t0,t0,-12288
v:0x80100624 p:0x00100624  sw zero,0(t0)
v:0x80100628 p:0x00100628  lui t1,0x80a8

  • It is often quite useful to know how many instructions have been executed. Since Simics is deterministic, a bug in the execution will appear at the same time each time a program is executed (typically, each time you start a certain build of your operating system from scratch). To see the current time, use the command ptime:

simics> ptime
Number of steps executed (CPU cpu0) : 10000123

  • To get some more detailed information on the execution up to this point, use the print-statistics command for cpu0 (note that you can use tab-completion to quickly type commands):

simics> cpu0.print-statistics 

Statistics for cpu cpu0
       User  Supervisor       Total  Description
          0    10000123    10000123  instructions executed
          0     1763947     1763947  memory read operations
          0      967364      967364  memory write operations
          0         200         200  I/O read operations
          0         213         213  I/O write operations

  • We can use the Simics command-line to provide input to the simulated machine. This is useful to script startup and demo sequences, and test input and output. If, for example, we want to do an ls command in the simulated Linux, we can do it the following way from the Simics command-line (you need to continue the simulation for anything to happen, as the simulated machine needs to accept the input):

simics> con0.input "ls\n"
simics> c

  • That's the end of this part of the lab. To quit Simics, press ctrl-C to get back to prompt (unless you are already at prompt), and give the command quit to quit Simics:

simics> quit
Simics license checked in!
host$

Simics breakpoints during OS boot

The previous step will have taught you how to start and stop Simics, and given a flavor of how it is working with Simics. Now, we'll look more into how Simics can be used to investigate the boot process of an operating system.

  • Start Simics again just like you did before (with the Linux setup).
  • Set a breakpoint on accesses to the malta0 device, which handles the LCD display (the 8-letter window with red letters). This is done using the break-io command. Note that you do not need to know where the malta0 device is mapped in memory, which is quite convenient:

simics> break-io malta0

  • Check that we have a breakpoint by listing all breakpoints, with break-io -list:

simics> break-io -list
breaking enabled for these devices:
  malta0

  • See the debug tips page for more on I/O breakpoint and other Simics breakpoints. You can also read Chapter 8 of the User's Guide for UNIX (or Chapter 7 in the User's Guide for Windows) for more on Simics breakpoints.
  • Start simulation with the continue command. Simics will stop at the first access to the malta0 device, showing you the type and values of the access. Note that the instruction displayed is the one after the accessing instruction, as the stop takes place after we execute the instruction.

simics> c
[malta0] Write: 0x1f000418 4 0x4c
[cpu0] v:0x80216a14 p:0x00216a14  j 0x80216a20

  • This tells us that a value of "0x4c" (character code for "L") was written to address 0x1f000418, and that the size of the write was 4 bytes. This can be verified by looking at the access history of the malta0 device:

simics> malta0.log
     0  Timestamp: obj = malta0 cycle = 109107 cpu = cpu0  pc = 0x80216a10
        Write access from cpu0. PA = 0x1f000418 size = 4  data = 0x4c

Only 1 entries listed (no more in buffer)

  • Note the information about the time of the access, who did it (which processor), and which instruction did the access. The accessing instruction was not the jump instruction at 0x80216a14, but the previous instruction at 0x80216a10. Also note that the address of the access is consistent with the memory map of the MIPS machine.
  • To see the accessing instruction, use the da (disassemble) command:

simics> da 0x80216a10
v:0x80216a10 p:0x00216a10  sw v0,0(v1)

  • Do c a few times to see the display of the word "LINUX" build up on the LCD. Each breakpoint will correspond to an additional letter being set.
  • Look at the access history for malta0 again:

simics> malta0.log
     4  Timestamp: obj = malta0 cycle = 109143 cpu = cpu0  pc = 0x80216a10
        Write access from cpu0. PA = 0x1f000438 size = 4  data = 0x58

     3  Timestamp: obj = malta0 cycle = 109134 cpu = cpu0  pc = 0x80216a10
        Write access from cpu0. PA = 0x1f000430 size = 4  data = 0x55

     2  Timestamp: obj = malta0 cycle = 109125 cpu = cpu0  pc = 0x80216a10
        Write access from cpu0. PA = 0x1f000428 size = 4  data = 0x4e

     1  Timestamp: obj = malta0 cycle = 109116 cpu = cpu0  pc = 0x80216a10
        Write access from cpu0. PA = 0x1f000420 size = 4  data = 0x49

     0  Timestamp: obj = malta0 cycle = 109107 cpu = cpu0  pc = 0x80216a10
        Write access from cpu0. PA = 0x1f000418 size = 4  data = 0x4c

Only 5 entries listed (no more in buffer)

  • That's enough with malta0. Remove the breakpoint using the unbreak-io command, and check that it is gone:

simics> unbreak-io malta0 
simics> break-io -list
breaking enabled for these devices:

  • The next step is to let the boot run until it displays "LINUX" on the serial console (the regular text window, not the little LCD display). This is done using the con0.break command. We continue immediately to see the effect:

simics> con0.break "LINUX"
simics> c
[text-console - con0] Breaking on string 'LINUX'
[text-console] Break on string
[cpu0] v:0x80216d68 p:0x00216d68  jr ra

  • The con0.break command stops as soon as the given string is displayed on the console. This is very handy when you want script a program or an OS boot, for example to automatically log in each time the machine is started. Just add a sequence of commands at the end of a .simics file.
  • We can also break when interrupts happen in the processor. We use the break-exception command, telling Simics to stop when the MIPS processor gets an interrupt (note that interrupts are just one of the types of exceptions that we can detect):

simics> break-exception Interrupt 
simics> c
[cpu0] (@ cycle 20890861) Exception 0: Interrupt
[cpu0] v:0x80000200 p:0x00000200  j 0x80216460

  • The instruction shown is the one that will be executed as a result of the interrupt. To find the interrupt handler, we will need to single-step from here, using the si command (note that by pressing return in Simics, we simply reexecute the last command). The result looks like this, note that we jump from the interrupt vector into the interrupt handler (more on that below):

simics> si
[cpu0] v:0x80000204 p:0x00000204  nop
[cpu0] v:0x80216460 p:0x00216460  mfc0 k0,12 sel = 0 (status)
[cpu0] v:0x80216464 p:0x00216464  nop
[cpu0] v:0x80216468 p:0x00216468  sll k0,k0,3
[cpu0] v:0x8021646c p:0x0021646c  bltz k0,0x8021647c
[cpu0] v:0x80216470 p:0x00216470  move k1,sp
[cpu0] v:0x8021647c p:0x0021647c  move k0,sp
...

  • Run until you hit the next interrupt. Then look at the contents of the cause register to determine the type of interrupt that hit us (look in the MIPS Processor Manual for the details on the cause register). Do cpu0.pregs -all to see all the CPU registers and their values (this is a really long output):

simics> c
[cpu0] (@ cycle 21007098) Exception 0: Interrupt
[cpu0] v:0x80000200 p:0x00000200  j 0x80216460
simics> cpu0.pregs -all
zero  [r0] = 0x00000000      s0 [r16] = 0x00000000    
  at  [r1] = 0x80270000      s1 [r17] = 0x00000000    
  v0  [r2] = 0x80a90000      s2 [r18] = 0x00000000    
  v1  [r3] = 0x00002000      s3 [r19] = 0x00000000    
  a0  [r4] = 0x00000009      s4 [r20] = 0x00000000    
  a1  [r5] = 0x00000001      s5 [r21] = 0x00000000    
  a2  [r6] = 0x00000001      s6 [r22] = 0x00000000    
  a3  [r7] = 0x00000007      s7 [r23] = 0x00000000    
  t0  [r8] = 0x80a7a930      t8 [r24] = 0x00000003    
  t1  [r9] = 0x0000003c      t9 [r25] = 0x0000000a    
  t2 [r10] = 0x00000018      k0 [r26] = 0x80107f38    
  t3 [r11] = 0xffffffe0      k1 [r27] = 0x80107f38    
  t4 [r12] = 0x80a7a546      gp [r28] = 0x80106000    
  t5 [r13] = 0xfffffffe      sp [r29] = 0x80107f80    
  t6 [r14] = 0xffffffff      fp [r30] = 0x00000000    
  t7 [r15] = 0x80107e91      ra [r31] = 0x8024e388    

...

Cause    0x00808000
    0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 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    
...

  • Single-stepping from an interrupt will bring us through the MIPS exception vectors (which contain code) into the actual interrupt handler. Note the purpose of the jump instruction at address 0x00000200 is simply to jump to real interrupt handler elsewhere in memory. The result is similar to this:

simics> si
[cpu0] <v:0x80000200> <p:0x00000200> j  0x208100
simics> si
[cpu0] <v:0x80000204> <p:0x00000204> nop
simics> si
[cpu0] v:0x80216460 p:0x00216460  mfc0 k0,12 sel = 0 (status)
simics> si
[cpu0] v:0x80216464 p:0x00216464  nop

  • To see what happens to the control registers of the MIPS during the exception handling, we can let Simics use its tracing facilities while the code is running. Try the trace-cr command to start tracing control register changes. Then run some number of instructions using the c command with an argument:

simics> trace-cr -all
simics> c 1000
[cpu0] status <- 0x1000fc00
[cpu0] status <- 0x1000fc00
[cpu0] status <- 0x1000fc00
[cpu0] compare <- 0x4ddfb7
[cpu0] compare <- 0x4ea307
[cpu0] compare <- 0x4f6657
[cpu0] compare <- 0x5029a7
[cpu0] compare <- 0x50ecf7

  • To stop tracing, use untrace-cr -all. Note that you can trace only a particular control register by giving its name to the trace-cr command.
  • To stop when a control register changes, use break-cr with the name of a control register or -all, as with all other breakpoint commands (note that as always, your output might be different depending on when in the boot process you execute the Simics commands):

simics> break-cr -all
simics> c
[cpu0] status <- 0x1000fc03
[cpu0] v:0x8010a910 p:0x0010a910  lw v1,160(sp)
simics> c
[cpu0] epc <- 0x8024e844
[cpu0] v:0x8010a918 p:0x0010a918  lw ra,148(sp)

  • That's it for showing off Simics features as an OS debugger.

Checkpointing

A very powerful feature of Simics is the ability to take a checkpoint of an execution and later continue from that same point. Basically, you write a snapshot of the system state to disk and bring it back into the machine, letting it continue from the point where you left off. We will demonstrate this using the Linux system.

  • Start Simics running Linux as shown previously.
  • Issue the command c 100_000_000, i.e. run one hundred million instructions.
  • When Simics gets back to prompt, look at the state of the machine with commands:
    • How does the text console look?
    • The register contents: cpu0.pregs
    • The execution stats to date: cpu0.print-statistics
  • Now save a checkpoint using the command write-configuration:

simics> write-configuration example-checkpoint
simics>

  • Quit Simics.
  • Next, look in the filesystem for the files generated by the checkpoint:

host$ ls
captured-file.txt                   linux.simics*
example-checkpoint                  mips32-test-machine-common.simics*
example-checkpoint.memory0_image    mips32-test-machine.config*
example-checkpoint.raw              simics*
gsimics*                            strace.py*
linux.conf*

  • Note all the files named example-checkpoint + something else: they are all part of the checkpoint.
  • Now start Simics using the checkpoint, to get back to where we were. Use the -c argument to tell Simics to open the checkpoint:

host$ simics -c example-checkpoint

  • Inspect the state of the machine using the same commands as above. Note that the statistics have been zeroed: when a checkpoint is taken, only persistent machine state is saved, and not the state of statistics gathering.

Testing Simics with an example program

Next, let us try running a small example program on the compsys machine. There is a ready-compiled example timer program included in the installation of the compsys machine, called example_timer.elf which we will use here. This step will also check that your installation of compsys was correct and complete.

  • Move back to the compsys directory:

host$ cd ~/simics/home/compsys

  • Look at the script compsys.simics. This is the script used to start simics. It is quite short; basically all that is done is to set a variable and then hand over to the compsys-common.simics script that does the machine setup.

host$ cat compsys.simics 
#
# Machine start file for the computer systems DV1 course, ht2004
# Intended for use with Simics 2.0.16 or later
#
# Uses the compsys-common.simics file for most of the actual
# work. 
#

#
# Set name of your binary to load here!
#
@binary_to_load = "example_timer.elf"

#
# Run the common startup script
#
run-command-file simics-common.simics

  • Start Simics using this script (we ignore most of the Simics output in the example below):

host$ simics compsys.simics 

Loading binary: 'example_timer.elf' 
  PC set to: 8002029c 
  Loading OK. 

Welcome to Simics for the computer systems course! 

simics> 

  • Start simulating using c. Note that we loaded this program into the memory of the machine without any operating system being part of the process; we are running it directly on the hardware, just like you will with your own operating systems.
  • Stop the program and experiment with the debugging features of Simics. Which devices and interrupts does the program use?
  • Note that you will be using some convenient scripts to run your own programs in Simics, as described in the compilation introduction lab.

Scripting Simics

Optionally, you might want to investigate the possibilities of extending the Simics command line using scripting. As example here, we will add a command that stops execution as soon as an eret instruction is found.

To start, we will set a breakpoint on eret instructions using the command line interactively. This process relies on a breakpoint filtering feature in Simics (to find more information on the commands, do help break and help set-prefix).

  • Set a breakpoint on the entire memory containing instructions (in virtual addresses for the Linux example, the memory is much smaller for the compsys machine):

simics> break -x 0x8000_0000 0x0800_0000
Breakpoint 1 set on address 0x80000000, length 134217728 with access mode 'x'

  • If you try executing, you will stop on every instruction. Which is not very practical.
  • We can modify this breakpoint to match just specific instructions. This is done using one of the commands

set-prefix, set-substr, or set-pattern. For the eret instuction, prefix is the most appropriate. Note that we need to specify the ID number of the breakpoint to modify:

simics> set-prefix 1 "eret"
simics> list-breakpoints
 Id Type     Enb Start              Stop                 Hits Space
  1 virt-x   yes 0x0000000080000000 0x0000000087ffffff      5 primary-context
    Prefix: eret

  • Let the simulation run. After a while, it should stop when it finds an eret instruction:

simics> c
Code breakpoint 1 reached.
[cpu0] <v:0x8010a99c> <p:0x0010a99c> eret

  • This is quite a lot of typing to do each time such a breakpoint is desired. Instead, we can create a small script-file that does the job for us. Open up an emacs or other text editor, and enter the commands used above on consecutive lines:

break -x 0x80000000 0x08000000
set-prefix 1 "eret"

  • Save this under a name (like "setbp.simics") in the Simics directory you are currently using (likely home/mips32-test-machine or home/compsys_machine).
  • Quit Simics and restart it, so you have a new fresh machine.
  • Load the command-file you saved using the command run-command-file:

simics> run-command-file setbp.simics

  • Check that the breakpoint triggers as before when your program executes.

As you might see, the above solution only works for a single breakpoint, since we have hardcoded the breakpoint ID number in the argument to the set-prefix command. The only way to fix this limitation is to move to Python scripting and use the Simics API (as described in the Simics User's Guide. Feel free to explore this aspect of Simics on your own.

Anyway, putting common command sequences into script files is handy for repeating tasks with Simics, or to automate testing. When doing the labs, the run.simics file and the run.sh script will be used. These files can be extended with new commands during project development.

Updated  2004-10-25 20:27:16 by Jakob Engblom.