ECE/CS 314: Computer Organization
Fall 2004
Project 2: Simulating A MIPS Processor
 
  Basics
 

Please read this document completely before starting.

  • The template files for this project are in /usr/class/ececs314/proj2.tar.gz. You can extract them into your current directory by executing
    tar xzfv /usr/class/ececs314/proj2.tar.gz
    This creates a directory named ececs314/proj2 that contains the template files for this project.  You'll probably want to do this from you home directory, so type 'cd' or 'cd ~' before executing this command.
  • You will turn in this project electronically. See the end of this document for specific details.
  • The project will be graded out of 100 points.
  • The consultants can help you when you encounter problems. The consulting schedule is posted on the course web page. If you still have problems after talking to the consultants, you can try the course newsgroup, e-mail your questions to one of the TAs, or schedule an appointment.
  Project Goal
 

The goal of this programming project is to write a functional simulator for a subset of the MIPS instructions. This project will help you understand how each instruction in the MIPS processor behaves. To successfully complete this project, you will need to understand the following items:

  • Project 1
  • Simple C programming
  • The precise definition of the MIPS instruction set (lectures 2, 3 and the corresponding reading assignments from the textbook, particularly Section A.10)
  • The working environment (assembling, simulating, and debugging assembly code;)
The first project involved writing assembly-language programs. This project involves writing C code that implements a MIPS processor simulator that can execute assembly language programs.

  Part 1: Executing An Instruction
 

In this part, you will write C code that executes one instruction on the MIPS processor. The function has the following prototype:

int OneStep (unsigned long ins,
                unsigned long Regs[],
                unsigned long PC,
                void *mem,
                int *jmpBranchInfo,
                unsigned long *jmpBranchTarget
                );

The arguments to this function are as follows:

  • Inputs:
    • ins: instruction to be executed
    • PC: program counter for instruction
  • Inputs/Outputs:
    • Regs: an array of 32 unsigned integers, containing the values of the registers.
    • mem: a pointer to the simulated machine memory. You can read or write memory only by using the FetchWord and StoreWord procedures that we provide; these are discussed below.
  • Outputs:
    • jmpBranchInfo: set to 0 if the instruction is not a branch or jump instruction; set to 1 if the instruction is a branch that is not taken, and set to 2 if the instruction is a jump or a branch that is taken. (A jump is always taken; a branch is taken when its condition evaluates to true.) You must assign a value to this output.
    • jmpBranchTarget: set to the target address of a jump or branch instruction that is taken. You must assign a value to this output if (*jmpBranchInfo) is set to 2.
  • Return value (these values are all non-negative, though they may not look that way in some browsers):
    • 0 if the instruction was recognized and executed without error,
    • 1 if the instruction was unrecognized (or not implemented by your simulator),
    • 2 if the instruction generated a memory alignment error,
    • 3 if the instruction generated an integer overflow (overflow detection is not required, but is Extra Credit, see below),
    • 4 for any other error condition you might detect.
You are given opcodes.h, containing a list of the opcodes for the instructions you are supposed to implement, as well as some helpful #define s for the output parameters.

A good way to implement this function is to split the instructions into three categories:

  • Those that can be decoded by simply looking at the "op" field of the instruction (top 6 bits);
  • Those where the "op" field is zero (SPECIAL), where the instruction can be decoded by looking at the "func" field (bottom 6 bits);
  • Those were the "op" field is one (REGIMM), where the instruction can be decoded by looking at the "rt" field of the instruction.
The values of these different fields (op, func, rt) for different instructions are given in the opcodes.h file. The file also names all the instructions you have to implement.

exec.c contains the template C code for this function. Modify it so that the function correctly executes one instruction.

Memory can be accessed only by reading or writing an entire word at a time. There are two functions provided:

  • unsigned long fetchWord (void * mem, unsigned long addr); which returns the word stored at address addr. NOTE: addr MUST be word-aligned.
  • void StoreWord (void *mem, unsigned long addr, unsigned long value); which sets the word located at address addr to the word value. NOTE: addr MUST be word-aligned.
Prototypes for these functions are contained in exec.c . Always check address alignment before performing a load or store operation.

Generate an executable program by running

gmake
This creates a proj2 executable, which is similar to gmipc except it uses your function to execute code. You might want to use gmipc as a "reference implementation" to find errors in your own implementation.

Remarks.

  • It may be helpful to "decode" the instruction (by extracting various fields into C variables) before you start writing code to implement individual instructions.
  • Don't forget, register 0 is always zero.
  • Byte and half-word store operations will require a read and a write to memory.
  • There is a template file, exec.template.c, which you can start from.  This file already has the switch statement setup.
  • ONLY CHANGE THE EXEC.C FILE!!!
  Part 2: Testing
 

In this part, you will write functional tests for your processor simulator. The purpose of these tests is to check that you have correctly implemented individual instructions.

There are three classes of instructions:

  • arithmetic and logical
  • memory (load and store)
  • control (jumps and branches)
For each instruction in each class, devise a test case that checks that the instruction is correctly executed. Run these through your simulator and check that instructions are correctly executed.

Create your test cases in the test subdirectory of your proj2/ directory. Modify the sample program testpgm.s to contain your test cases. Be sure to save all versions of your test files, because we want to see your test suite for grading purposes. Describe your testing strategy and the files that were used to test your processor in your README file (discussed in the SUBMITTING section below).

You should also run the sample msort.image program through your simulator. The program sorts one array, leaving the result in memory. Make sure your simulator correctly executes this program by checking the contents of the memory before the simulator exits! (set a breakpoint after the call to msort from main returns).

Note that your MIPS code from Project 1 may not run on your simulator if it uses multiply or divide instructions and you haven't implemented them.

  Extra Credit
 

Overflow Detection (5 pts)

This extra credit feature is simple in principle: your OneStep function should return the appropriate error code (3) under those conditions where the ISA specifies that “an integer overflow exception occurs.”

Using the interface we have provided, it is not possible for you to faithfully simulate an overflow exception. So how do you test your code? There are many possibilities. For example, you might temporarily modify OneStep so it clears the target register to 0 on overflow, then run appropriate test programs to show the correct behavior. Describe your testing strategy in your README file.

Multiply and Divide (5 pts)

The MIPS multiply and divide operations manipulate 64-bit quantities. If you are C-literate, you can declare 64-bit integers in C and do these operations directly. This solution will enable you to run the puts and puti routines, but to get the full extra credit you should implement the operations using 32-bit C operations. As above, describe your testing strategy in your README file.
  Submitting Your Project
 

Submission will be done using CMS. Remember you must be logged in to Sidecar to access CMS. (From a CSL machine, type kinit to create a Sidecar ticket, and kdestroy to destroy it before you logout from the machine.)

In your ececs314/proj2 directory, create a file named README. The first four lines must be:

NAME: username1 username2
Name of person1
Name of person2
PROJECT 2
username1 and username2 should be the CSL login ids of you and your partner – your netids. Your README file must contain information about how you tested your simulator, and which test files you used. If you have any special instructions or anything you'd like us to know about your project, please let us know by typing it into the README file.

For this project, the only file that you will edit is "exec.c".  You will also add the test files(.s) in the /test directory, nothing else will get submitted.  There is a script available that will tar these up for you, and check that all the files are present, so that you can submit them to CMS.  Once you have finish the project, simply type:

proj2tar.314

from your proj2 directory. This will create the file proj2_valid_sub.tar.gz, a compressed archive of all the files in your project. Upload this file to CMS, following the instructions in the CMS documentation.

There is no written report (other than your README file) required for this project.