SimIt-MIPS

Users' Guide

  1. Installation
  2. Quick Start
  3. Interpreter
  4. Static-compiled Simulator
  5. Dynamic-compiled Simulator
  6. Reference

Installation

To install, first untar the source by

tar xzvf SimIt-MIPS-1.0.tar.gz

Then build the source code by

cd SimIt-MIPS-1.0
./configure
make
make install

After these steps, the ./build/bin directory contains the following programs:

emips
A MIPS32 interpreter.
emips_decomp
A Decompiler that translates mips binary to C++.
build_scs
A shell script that builds static-compiled simulator using emips_decomp
emips_jit
A dynamic-compiled simulator.
emips_jit_server
A optional translation server that can be used to acceperate dynamic-compiled simulation.

To test the installation was successful type

./build/bin/emips test/wc configure

The above command simulates the UNIX utility wc. If the simulator is installed correctly, you should see the line count, word count, and character count of the file configure.

If one prefers other installation directory than the default build, one can specify the prefix configuration flag.

./configure --prefix=<PREFIX path>

For convenience of referencing the simulator commands, one can include the bin/ directory into the environment variable PATH by typing in bash

export PATH=$PATH:<PREFIX PATH>/bin

or in csh

setenv PATH ${PATH}:<PREFIX PATH>/bin


BACK TO INDEX

Quick Start

SimIt-MIPS reads ELF32 little-endian MIPS binaries. The binaries must be statically linked. See the crosstool page for more information on building a cross MIPS compiler. The mipsel configuration with gcc-3.3.4 and gibc-2.3.2 should work fine.

This section uses a simple C program to demonstrate the essential steps involved in using the simulator. Below shows the example program:

#include <stdio.h>
int main()
{
  printf("Hello World!\n");
  return 0;
}

Now compile the program using the ELF32 MIPS cross-compiler. Remember to statically link the binary. The command should be something like:

mipsel-linux-gnu-gcc -static -o hello_world hello_world.c 

The compiler generates the hello_world MIPS32 binary. Assuming that the <PREFIX>/bin directory has been added to the environment variable PATH, one can simulate the binary by typing

emips hello_world

The interpreter emips will output "Hello World!" in the standard output. Usage of the interpreter is described in more detail in Section Interpreter. One can also build a static-compiled simulator for the program (more accurately, binary-translate the program) by typing

build_scs hello_world

The above command will take several minutes to finish since building the static-compiled simulator involves compiling dozens of C++ functions. The building process will create a folder named hello_world.scs under the current working directory. In the folder, there is a script hello_world. To run the translated program, simply run the script.

./hello_world.scs/hello_world

Section Static compiled simulation gives a complete description on building static-compiled simulators.

Lastly, the dynamic-compiled simulator can be invoked by

emips_jit hello_world

In this example, the dynamic-compiled simulator interprets the whole program since hello_world is too short to trigger run-time compilation. A full description of the dynamic-compiled simulator is provided in Section Dynamic compiled simulation.


BACK TO INDEX

Interpreter

Commandline Options

The default syntax for running the interpreter is:

./emips <program name> <program arguments>

By default, the interpreter will print out running time information and instruction counts. If one wants SimIt-MIPS to print out error messages and system call information, run it in verbose mode by typing

./emips -v <program name> <program arguments>

Often simulation can take a very long period of time. To avoid waiting indefinitely, one can truncate simulation by setting an upper bound of the number of instructions to simulate.

./emips -m [number of instructions] <program name> <program arguments>

Built-in Debugger

SimIt-MIPS also has the ability to trace the target programs. To start SimIt-MIPS in debugging mode type:

./emips -d <program name>

The following instructions are available at the debugging prompt:

h
print help message
u [addr]
disassemble 10 instructions from [addr]
addr can be either a hex address or 'pc',
which will disassemble from the current program counter
d [addr]
dump 256 bytes from memory address [addr]
g [addr]
run until pc = [addr]
t [num]
step [num] instruction(s)
r
dump gpr and fpr value(s)
rf
same as 'r' but fpr shown as float
rd
same as 'r' but fpr shown as double
pc
dump program counter
hi
dump HI register
lo
dump LO register
fcsr
dump FCSR register
ra
dump return address register ra(r31)
q
quit

We again use the program hello_world as an example. Although the debugger performs run-time disassembly, it is sometimes useful to create a dump of the assembly code for the target program before debugging. The MIPS cross objdump can be used to create the dump. It is normally built along with the cross-gcc.

mipsel-linux-gnu-objdump -d hello_world >hello_world.dump

Now start the debugger with the command:

./emips -d hello_world

One should now see the debugger prompt ">". At the prompt, one can type any of the commands above. We assume that, from our dump file, we learn that the main function starts at address 0x004003e0. We can instruct the debugger to run until the main function by typing:

>g 4003e0

At this point, if we want to see the first 10 instructions in main, we type:

>u pc

Suppose we want to inspect certain memory accesses and we notice that the 5th instruction is a memory store.

0x004003f0 : 0xafbc0010    sw $gp, 16($sp)

We can step through the first 4 instructions with:

>t 4

To inspect the contents of the registers, we can simply type:

>r

From the screen printout, we can learn value of the stack pointer, $sp, which is used in the sw instruction. Assume its value is 0xbfffbda8. We can observe the memory contents from this address by typing:

>d bfffbda8

The command will display 256 bytes of memory. So we can find the contents of memory that the program is accessing. If we decide that the contents of memory is incorrect, we may want to go back and check previous memory accesses. We can now terminate the debugger by typing:

>q

and restart it to trace back to the first memory access. Obviously there are more complicated debugging scenarios than this. Hopefully the above example will get you started.


GDB Interface

A more powerful debugging option is the GDB remote debugging interface. This is currently in an experimental phase bus has already proven useful. To enable this debugging approach, one must first build a GDB client that supports the MIPS target. The GDB client receives user commands, sends control instructions to the MIPS interpreter, modifies/retrieves register and memory values, and displays debugging information to the user. We built a GDB 6.3 client using the following configuration options

"--host=i686-pc-linux-gnu --target=mipsel-linux-gnu"

To enable the gdb interface of the interpreter, type:

./emips -gdb 127.0.0.1:1234 <program name> <program arguments> &

The "-gdb" option specifies an IP address and a port. Here we use port 1234 on the local machine (127.0.0.1) to communicate between GDB and the interpreter. We then start GDB by typing

mipsel-linux-gnu-gdb <program name>
(gdb)target remote 127.0.0.1:1234
(gdb)break main
(gdb)cont

The above commands sets a break point at the main function and starts simulation. Most common GDB commands are supported. The approach also works with DDD for graphical debugging.


BACK TO INDEX

Static compiled simulation

The static-compiled simulator breaks up the target program into small pages, and translates each page into a C++ function. The C++ functions are then compiled and linked together to form the static-compiled simulator. The process is often called binary translation.

In SimIt-MIPS, the program emips_decomp is used to perform translation from MIPS binary to C++. However, it is not recommended that users invoke it directly. Users should use the script build_scs instead. The script not only invokes emips_decomp to do translation, but also compiles the translated code and link them with support routines into a full simulator. The command line format of the script is

build_scs [-c] [-v] [-l num] <program>

The meaning of the optional arguments are listed below:

-c
To count the number instructions during simulation. By default, the simulator will not count since it affects speed.
-v
To turn on the verbose mode. The simulator will print a message during each system call.
-l <floating-point number>
To specify the size of a page, in K instructions. Default page size is 1K=1024 instructions.

The arguments must precede the path/name of the MIPS32 binary program to translate. The page size will be rounded up to the nearest power of 2. Additionally, if the page size is less than 512, it will be hard limited to 512. A larger page size means less but larger functions. A overly large page/function size may cause significant slowdown of G++ since many modern compiler optimizers have super-linear complexity.

The build_scs script will generate a directory named <program<.scs under the current working directory. The directory contains another script with the same name as the original program. The script can be invoked in the same way as the original MIPS program. For example, for the MIPS32 program wc, the script will generate the directory wc.scs and another script named wc under the new directory. One can then invoke wc.scs/wc in exactly the same way as one would invoke wc on a MIPS platform — the same program arguments to wc should be supplied to wc.scs/wc.


BACK TO INDEX

Dynamic compiled simulation

The dynamic-compiled simulator combines the functionality of the interpreter and the static-compiled simulator. It interprets the program at the beginning of the simulation. Meanwhile it profiles the simulated program by keeping a counter for each page of the code. The counter records the cumulative number of simulated instructions in the page. If the number is beyond a predefined threshold, then this page is deemed hot and is translated. Translation is performed in two steps. First the page is translated to a C++ function. Next the C++ function is compiled into a shared library and linked to the simulator itself. Compilation of the C++ function can be done by the simulator itself, or can be distributed to a remote translation server. Obviously, the use of more translation servers means faster simulation speed.

The dynamic-compiled simulator can be invoked by the command emips_jit. The command has the following format:

emips_jit [-t num] [-l num] [-f fname] [-c] [-m] [-v] [-w] [-h] <program> <program arguments>

The meaning of the command line arguments are explained below:

-t <floating-point number>
The threshold to trigger translation, in million. The default threshold is 64 million.
-l <floating-point number>
The page size in thousand instructions. This parameter is the same as that of the static-compiled simulator.
-f <file name>
The configuration file name. A configuration file specifies the IP/port information of the translation servers. If this file is given, the simulator will distribute C++ compilation tasks to the translation servers. Otherwise, the simulator will perform C++ itself.
-c
To count the number of instructions during simulation. This has the same effect as that of the static-compiled simulator.
-m
To enable simulating self-modifying code.
-v
To turn on the verbose mode. This has the same effect as that of the static-compiled simulator.
-w
To turn on verbose communication between the simulator and the translation servers. This is useful only when -f is specified.
-h
To print the usage information and exit.

Translation of code pages into dynamic libraries is performed by GCC. Due to the slow speed of GCC in compiling C++ code, For small programs it may take emips_jit longer to simulate than emips. But for large programs, the translation overhead will be sufficiently absorbed and emips_jit will outperform emips.

The dynamic-compiled simulator will create a folder $HOME/.emips to cache translation results. If a program has been simulated once, subsequent simulation may be much faster because shared libraries in the cache can be directly loaded on simulation start.

The translation server program emips_jit_server has the following usage format.

 emips_jit_server [-v] [-p portnum] [-h]

The commandline options are listed below.

-v
To turn on verbose mode. Will print all communication activities with the simulator.
-p <port number>
The socket port number. The translation server will listen on the port to await connections from the simulator.
-n
To turn off linking. This server will simply compile the C++ page and send the .o object code to the simulator. The simulator will do the linking to produce the .so shared library. This is useful when the server platform cannot generate compatible .so files.
-h
To print the usage message and exit.

To perform dynamic-compiled simulation using multiple workstations, one need to first ensure that they can communicate with each other using UNIX socket. Firewalls may need to be partially turned off. Then the translation servers can be started first, followed by the simulator emips_jit. The -f option of emips_jit must be used to specify the address of the translation servers. The configuration file should one or more lines. Each line specifes the address of one translation server in the following format.

IP_address port_number

More technical details of the dynamic-compiled simulator have been published in conference proceedings. See References for information.


BACK TO INDEX

Reference

  • W. Qin, J. D'Errico, X. Zhu, A Multiprocessing Approach to Accelerate Retargetable and Portable Dynamic-compiled Instruction-set Simulation, International Conference on Hardware/Software Codesign and System Synthesis, November 2006

  • J. D'Errico, W. Qin, Constructing Portable Compiled Instruction-set Simulators — An ADL-driven Approach, IEEE/ACM Design Automation and Test in Europe, March 2006.

BACK TO INDEX

If you have further questions, please contact the authors at weiqin@gmail.com or jderrico3@gmail.com. Check out Simit-ARM too!