This is the VMIPS Programmer's Manual, Zeroth Edition, for version 1.0-alpha.
Warning: This is an unfinished prerelease version of the documentation, dated 9 May 2001. This documentation is entirely preliminary, and doesn't cover everything that the final documentation will have to. Eventually it should grow into a more comprehensive user- and programmer-level document.
Copyright © 2001 Brian R. Gaeke.
VMIPS is a simulator for a machine compatible with the MIPS R3000 RISC architecture. VMIPS consists entirely of software; no special hardware is required to run programs on VMIPS--that is, VMIPS is a virtual machine.
Since VMIPS is based on an already-existing architecture, it is relatively easy to find tools to build programs that will run on VMIPS.
VMIPS is easily extended by programmers to include more virtual devices, such as frame buffers, disk drives, etc. VMIPS is written in C++ and uses a fairly simple class structure. Furthermore, VMIPS is intended to be a "concrete" virtual machine which its users can modify at will--"concrete" meaning that it maintains a tight correspondence between its structures and structures which actually appear in modern physical computer hardware. For example, a programmer who wished to modify the CPU simulation could easily extract the "CPU" class from the VMIPS source code, and replace it with one which was more to his/her liking.
VMIPS is also designed with debugging and testing in mind, offering an interface to the GNU debugger GDB by which programs can be debugged while they run on the simulator. As such, it is intended to be a practical simulator target for compilers and assembly language/hardware-software interface courses.
VMIPS is free software. This means that we encourage you to share it with everyone, but we do not give you the freedom to restrict others from sharing it with everyone. For a comprehensive explanation please read the GNU General Public License.
MIPS and R3000 are registered trademarks of MIPS Technologies Inc.
Step 0. If VMIPS is installed on your system, you can start building programs with it right away. Otherwise, you (or your system administrator) will have to compile VMIPS first; see the appendix on Installation.
Step 1. First, compile your program. You should have a MIPS cross-compiler available. VMIPS supports the GNU C Compiler; most installations of VMIPS will also have an installation of the GNU C Compiler targetting the MIPS architecture. Your easiest interface to the C compiler will probably be through the vmipstool program; to run the MIPS compiler that VMIPS was installed with, use the "vmipstool -compile" command.
Step 2. Link your program with any support code necessary. VMIPS comes with some canned support code, in the share/setup directory, or you can write your own support code. VMIPS comes with a linker script for simple standalone programs, which you can run with "vmipstool -link", or you can write your own linker script.
Step 3. Build a ROM image. This is necessary because the current version of VMIPS does not read in executables. Most real machines don't; they have an embedded program on a piece of flash ROM that reads in the first executable and runs it. This makes development a little more realistic, but not quite so convenient; this may change in the future, but for now it's necessary. To build a ROM image, use the script that comes with VMIPS, by running "vmipstool -make-rom".
Step 4. Start the simulator using "vmips ROMFILE", where ROMFILE is the name of your ROM image. Your program should run to completion, and if you are using the canned setup code that comes with VMIPS, the simulator should halt when it hits the first "break" instruction, which should happen right after your "entry" function returns.
Let's assume you have vmips already compiled, and that you have some setup code in "setup.s", and a standalone program (i.e., not one meant to run under an operating system) in "hello.c".
First assemble the setup code.
vmipstool --assemble -o setup.o setup.s
Compile your program:
vmipstool --compile -c hello.c
Then, link your program and the setup code together to produce an executable:
vmipstool --link -o hello setup.o hello.o
Build a ROM image from the executable:
vmipstool --make-rom hello hello.rom
Run the program.
vmips hello.rom
The program will terminate, by default, when your setup code generates a breakpoint exception (using the "break" instruction, for example). This termination condition can be changed by adding one of the "halt" options to the file ".vmipsrc" in your home directory.
Programs for VMIPS are generally built out of C or assembly-language source code. It is theoretically possible to use C++ or other languages, but the infrastructure required has not yet been investigated or documented.
The easiest way to get VMIPS to run a program is to install that program as the VMIPS ROM. Building a C program as a ROM requires that you link it with some setup code.
This section describes the default VMIPS setup code. It also describes the minimal set of things you need to do before you can run C code from the ROM, since that is the intended purpose of the default VMIPS setup code.
Start by clearing out registers and TLB entries.
Set yourself up a stack pointer ($sp). Usually this can just be some number of megabytes above the end of your code's data segment. You can get the address of the end of your code's data segment from your linker script.
Set up your globals pointer ($gp), if your code uses global data. You can get the right address from your linker script.
If you have writable data in ROM, your C code probably doesn't realize that it's in ROM, and it will want to write to it. You should copy the writable data to RAM. There is code to do this in the canned setup code provided with VMIPS.
Finally, your setup code should finish by calling the entry point of your C code. Usually this will have a name like "entry"; using the name "main" is not recommended, because many versions of GCC assume that they can call standard C runtime setup functions (crt0.o) from the beginning of "main". You may or may not want this.
When the C code returns, you will probably want to halt the machine; the default way to do this is by executing a break instruction.
One last thing to include in your setup code is a boot-time exception handler of some sort. An absolutely minimal exception handler is a break instruction at address 0xbfc00180, which will halt the machine on any exception.
You want the text section of your program to start with the setup code, so link in the setup code first.
You want the setup code to start at 0xbfc00000, which is the MIPS reset exception vector. In practical terms, when VMIPS starts up, it will reset. When VMIPS resets, it jumps to 0xbfc00000, which is the beginning of your setup code.
If the linker complains about not being able to find the symbol _gp_disp, you should turn on the GCC option -mno-abicalls. _gp_disp is used by the SGI N32 ABI for MIPS ELF. One reliable reference source claims, "_gp_disp is a reserved symbol defined by the linker to be the distance between the lui instruction and the context pointer." The GNU linkers currently in use do not appear to support this function.
The VMIPS simulator gets runtime options from four different sources,
in this order: first, it checks its compile-time defaults, which are
set by the site administrator in the source file optiontbl.h
. Then,
the system-wide configuration file is read; usually this is in
/usr/local/share/vmipsrc
, but it may have been moved by the site
administrator. (This is configurable in the source file options.h
, and
by specifying the -prefix and -sharedir options to the GNU configure
script when building VMIPS.) Next, it checks the user's own configuration
file, usually the file .vmipsrc
in your home directory. Last, it
reads the command line, and gets any options listed there.
The configuration file may contain as many options per line as you want, provided no line exceeds BUFSIZ (usually 1,024) characters. Whitespace separates options from one another. A string or number option named NAME can appear as NAME=VALUE, where VALUE is the string or number in question. If the number begins with 0x, it will be interpreted as a 32-bit hexadecimal number, and if it begins with 0, it will be interpreted as octal. Otherwise, it will be interpreted as a decimal number. Numbers are always unsigned. A Boolean option named NAME can appear as either NAME (to set it to TRUE) or noNAME (to set it to FALSE).
The following is a list of the configuration options present in this version of VMIPS.
haltdumpcpu
(type: Boolean)
Controls whether the CPU registers will be dumped on halt. The default value is FALSE.
haltdumpcp0
(type: Boolean)
Controls whether the system control coprocessor (CP0) registers will be dumped on halt. The default value is FALSE.
excpriomsg
(type: Boolean)
Controls whether exception prioritizing messages will be printed. These messages attempt to explain which of a number of exceptions caused by the same instruction will be reported. The default value is FALSE.
excmsg
(type: Boolean)
Controls whether every exception will cause a message to be printed. The message gives the exception code, a short explanation of the exception code, its priority, the delay slot state of the virtual CPU, and states what type of memory access the exception was caused by, if applicable. The default value is FALSE.
bootmsg
(type: Boolean)
Controls whether boot-time and halt-time messages will be printed. These include ROM image size, self test messages, reset and halt announcements, and possibly other messages. The default value is TRUE.
instdump
(type: Boolean)
Controls whether every instruction executed will be disassembled and printed. The default value is TRUE.
dumpcpu
(type: Boolean)
Controls whether the CPU registers will be dumped after every instruction. The default value is FALSE.
dumpcp0
(type: Boolean)
Controls whether the system control coprocessor (CP0) registers will be dumped after every instruction. The default value is FALSE.
haltibe
(type: Boolean)
If haltibe is set to TRUE, the virtual machine will halt when an instruction fetch causes a bus error (exception code 6, Instruction bus error). This is useful if you are expecting execution to jump into unmapped areas of memory, and you want it to stop instead of calling the exception handler. The default value is TRUE.
haltjrra
(type: Boolean)
If haltjrra is set to TRUE, the virtual machine will halt when the instruction "jr $31" (also written "jr $ra") is encountered. Since this is the instruction for a procedure call to return, this is useful if you have a simple procedure to run and you want execution to terminate when it finishes. The default value is FALSE.
haltbreak
(type: Boolean)
If haltbreak is set to TRUE, the virtual machine will halt when a breakpoint exception is encountered (exception code 9). This is equivalent to halting when a "break" instruction is encountered. The default value is TRUE.
instcounts
(type: Boolean)
Set instcounts to TRUE if you want to see instruction counts, a rough estimate of total runtime, and execution speed in instructions per second when the virtual machine halts. The default value is FALSE.
romfile
(type: string)
This is the name of the file which will be initially loaded into memory (at the address given in "loadaddr", typically 0xbfc00000) and executed when the virtual machine is reset. The default value is "romfile.rom".
configfile
(type: string)
This is the name of the user configuration file. It will be ~username-expanded and checked for configuration options before the virtual machine boots. The default value is "~/.vmipsrc".
loadaddr
(type: number)
This is the virtual address where the ROM will be loaded. Note that the MIPS reset exception vector is always 0xbfc00000 so unless you're doing something incredibly clever you should plan to have some executable code at that address. Since the caches and TLB are in an indeterminate state at the time of reset, the load address must be in uncacheable memory which is not mapped through the TLB (kernel segment "kseg1"). This effectively constrains the valid range of load addresses to between 0xa0000000 and 0xc0000000. The default value is 0xbfc00000.
memsize
(type: number)
This variable controls the size of the virtual CPU's "physical" memory in bytes. You might want to round this off to the nearest page; you can determine the pagesize using utils/getpagesize.cc. The default value is 0x100000.
memdump
(type: Boolean)
If memdump is set, then the virtual machine will dump its RAM into a file named "memdump.bin" at the end of processing. The default value is FALSE.
reportirq
(type: Boolean)
If reportirq is set, then any change in the interrupt inputs from a device will be reported on stderr. The default value is FALSE.
usetty
(type: Boolean)
If usetty is set, then the SPIM-compatible console device will be configured. If it is not set, then no console device will be available to the virtual machine. The default value is TRUE.
ttydev
(type: string)
This pathname will be used as the device from which reads from the console device will take their data, and to which writes to the console device will send their data. The default value is "/dev/tty".
debug
(type: Boolean)
If debug is set, then the gdb remote serial protocol backend will be enabled in the virtual machine. This will cause the machine to wait for gdb to attach and "continue" before booting the ROM file. If debug is not set, then the machine will boot the ROM file without pausing. The default value is FALSE.