Due: April 15
In this lab, you will implement the first half of a basic processor that runs a subset of the MIPS ISA. It is so simple, in fact, that it does not even have a branch instruction, so it cannot execute most programs. The processor does, however, have all the parts that a real processor has: A fetch unit, decode logic, functional units, a register file, IO support and access to memory.
All of you will be implementing the same datapath, so you will all run into similar problems. Learn from your classmates, and then go apply what you learn to your own design.
NOTE: This lab can be performed in groups of two or three. On that same note, we encourage groups to help each other out (especially with tool problems).
Please remember that you must conform to the class coding standards. They are here. If you find a bug in them (e.g., they are causing you to do something horribly ugly), please let the professor or the TA know.

Above is a schematic of what you will be building in this lab. You will implement some of these modules and wire up all of the components to match this schematic. Start by making a new project in Quartus II (targeting the Cyclone II EP2C20F484C7, as we did in the previous lab). To get you started we have written the Instruction Rom, Data Memory and ALU. We have also provided a basic test bench which you will use later to run some simple programs. You can get these files here: processor components. Download the files and add inst_rom.v, alu.v, data_mem.v to your project. Also add async_memory.v and serial_buf.v to your project - these are modules that data_mem.v depends on.
Now we'll create the top-level file to hold our datapath for this lab: processor.v.
Here are the port definitions for processor.v. NOTE: Your processor needs to use the exact same module name and port names as specified here or your processor may not work in our testing framework.
![]() |
module processor( input clock, input reset, //these ports are used for serial IO and //must be wired up to the data_memory module input [7:0] serial_in, input serial_valid_in, input serial_ready_in, output [7:0] serial_out, output serial_rden_out, output serial_wren_out ); |
![]() |
module inst_rom( input clock, input reset, input [31:0] addr_in, //Connect to PC_next output [31:0] data_out //Fetched instruction ); parameter INIT_PROGRAM; //we will use this parameter to specify a file to preload into the instruction rom. |
The data memory module provides a small amount of RAM for your processor to read and write to (4KB for the stack, and 4KB for everything else). The data_memory module also allows your processor to interface with I/O devices. It currently connects to a serial port so you can get some output from programs that you execute. If you're interested, take a look at the module source code and have a look in the textbook about interfacing I/O devices with processors.
Our data_memory module has more ports than the textbook, specifically we have a size_in input and the serial_* inputs and outputs. The size_in input will be used in a later lab and should be tied to a value of 2'b11 (3 in decimal) instead of attaching a wire to it. The serial_* wires need to be wired to the ports on the processor module, so that the testbench has access to them. As on the inst_rom module, the parameters will be used to specify a program to run. We'll cover that more when the time comes.
Why does the data memory have ports for a serial port? An excellent question, and one that we'll discuss later in 141. If your curious now, take a look ate Section B.8, and then take a look at the insides of the data_memory module.
![]() |
module data_memory( input clock, input reset, input [31:0] addr_in, //Read/Write address input [31:0] writedata_in, //Data to write to memory input re_in, //Read Enable - set high when reading from memory input we_in, //Write Enable - set high when writing to memory output [31:0] readdata_out, //Data output for reads from memory input [1:0] size_in, //Not used yet - hardwire to 2'b11 //these are used to let your processor print things to a serial port (e.g. "Hello World") //wire them up to the same ports in your processor module so they can talk to the test bench input [7:0] serial_in, input serial_ready_in, input serial_valid_in, output [7:0] serial_out, output serial_rden_out, output serial_wren_out ); parameter INIT_PROGRAM0; //we will use these 4 parameters to preload data into RAM parameter INIT_PROGRAM1; parameter INIT_PROGRAM2; parameter INIT_PROGRAM3; |
![]() |
module alu( input [5:0] Func_in, input [31:0] A_in, input [31:0] B_in, output [31:0] O_out, output Branch_out, output Jump_out ); |
| Func_in (X=don't care) | Operation | O_out value | Branch_out | Jump_out |
| 10000X | ADD | A+B | 0 | 0 |
| 10001X | SUB | A-B | 0 | 0 |
| 100100 | AND | A AND B | 0 | 0 |
| 100101 | OR | A OR B | 0 | 0 |
| 100110 | XOR | A XOR B | 0 | 0 |
| 100111 | NOR | A NOR B | 0 | 0 |
| 101XX0 | Set-Less-Than Signed | (signed(A) < signed(B)) | 0 | 0 |
| 101XX1 | Set-Less-Than Unsigned | (A < B) | 0 | 0 |
| 111000 | Branch Less Than Zero | A | (A < 0) | 0 |
| 111001 | Branch Greater Than or Equal to Zero | A | (A >= 0) | 0 |
| 11101X | Jump | A | 0 | 1 |
| 111100 | Branch Equal | A | (A == B) | 0 |
| 111101 | Branch Not Equal | A | (A != B) | 0 |
| 111110 | Branch Less Than or Equal to Zero | A | (A <= 0) | 0 |
| 111111 | Branch Greater Than Zero | A | (A > 0) | 0 |
Now its your turn to code up the remaining components that you need to complete the schematic above.
You will need the following components:
The MIPS ISA defines 32 32-bit general purpose registers (GPR) that most intructions read and write data from and to. The first component you need to build is that collection of registers, called the register file. As you can see from the datapath schematic, the register file has two Read Address ports and two Read Data ports. This means we need to be able to read two values at once. There is also a Write Address port, a Write Data port, and a Write Enable port. These are used for writing the result from completed instructions back into the register file.
Here is a list of requirements for the register file:
Now we will code up the remaining modules. These are pretty basic and you can get an idea of what each piece does from the schematic and the textbook. Here are a few notes about some of the modules to help you out:
Now that we have all of the components we need to create the datapath, go ahead and instantiate all of the modules inside your processor.v top-level file. Declare all of the wires that you need (use descriptive names to help your Make sure you connect the serial_* ports of the data_memory module to the same ports on your processor module. Make sure you get the correct bit-widths for each wire. Since we don't have the control signals yet you won't be able to test your processor. After the next step you should be able to run the Quartus sythensis without any errors and without any of the "worrisome" warnings listed on the wiki. We'll count on your component test benches catching the logic bugs for now. The rest of the bugs will show up in Lab 3.
To make quartus happy, we need to specify a program to load into the instruction rom. This file: blank.memh (yes, it should be an empty, zero-length file) will be used as the "program" and "data" files for this lab and loads all "NOP" instructions into the instruction rom.
To "load" this program into your processor we'll change some parameters on the provided init_rom and data_memory modules. First, download the file somewhere where there are no spaces in the file name and remember the full path to the files. On the inst_rom module add the following parameter: INIT_PROGRAM. You should set the value of this parameter to the full path for the blank.memh file. For example:
inst_rom #(
.INIT_PROGRAM("c:/myfiles/blank.memh")
) myInstructionRom (
...
);
Similarly, set the INIT_PROGRAM0, INIT_PROGRAM1, INIT_PROGRAM2, INIT_PROGRAM3 parameters on the data_memory module. All of them should be set to point to the blank.memh file.
For the next lab we'll be adding in all of the control signals and the additional modules that they will need. If you want to get a head start, create a control module and wire it up to the remaining ports on the modules you just created. We'll go over what goes in that module, and the additional muxes and modules you'll need for the control paths in the next lab.
| Deliverable |
|
Due: April 15 |