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.