cse141L Lab 2: Single-Cycle MIPS Datapath


Changelog

January 19 Updated the PC reset value from 32'h3FFFFFFC to 32'h003FFFFC.
January 17

Added a section about loading blank.memh at the end of the lab to allow you to run synthesis without errors.


Due: January 23

Overview

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.

Getting Started

General Notes

Single-Cycle MIPS Datapath Implementation


The datapath schematic for Lab 2.

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.

The processor module:
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
);

Provided Component Interfaces

The instruction rom provides a read-only memory which your processor will get its instructions from. Our version is slightly different than what you will find in the textbook, because we require that the address input be connected to PC_next (the input of the PC register), instead of PC (the output of the PC register). We will use the parameter in a later lab so you can specify what application you want to run.
The inst_rom module:
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.

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;
The final module we provide is an ALU. The alu is responsible for doing all of the calculations in your processor. This one supports more operations that the ALU described in the book and has slightly different inputs and outputs.
The alu module:
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
);
Because our ALU has different inputs and outputs, here's a list of operations it can perform and what value of Func_in each corresponds to. You don't need this information now, but it will be helpful in later labs. Its included here for completeness.
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

Additional Components

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 Register File

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:

Question 1: Code up your register file. Include the source in your report. Write a test-bench to make sure your register file behaves as expected. Check that register zero behaves as expected by the MIPS ISA. Include a waveform of your register file in action, showing all of the inputs and outputs of your 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:

Question 2: Code up the remaining components. Include the source in your report. You should also test these extensively with test benches. Include some examples from each module showing your testing.

Putting it All Together

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.

Question 3: Finish wiring up your schematic. Include the source in your report. Draw out the schematic for processor.v. Include the names and bit-widths of the wires, and the names of the modules.

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

  • Submit your lab report at ted.ucsd.edu. The link to upload the file is available under "Assignments"->"Lab 2: Single-Cycle MIPS Datapath". The deadline for submitting your report is Monday, Jan 23rd by 3PM . Do not send softcopies to TAs email ids.
  • Submit a pdf file that contains answers to all of the questions (3) found in the lab descriptioni - including questions, verilog source code, graphs, screen-shots, etc. Name the pdf file as cse141L-lab2-LastName1-FirstName1-LastName2-FirstName2.pdf with your team members last name and first names substitude for LastName(i) and FirstName(i). There are many tools out there capable of integrating text and graphics and producing PDF files (OpenOffice does a pretty good job).
  • Also submit your quartus project files as a single zip file. Be sure to name the zip file as cse141L-lab2-LastName-FirstName.zip. We should be able to build your project without errors from these files. (Except for path problems to *.memh files).
  • Remember you need to upload two files to TED .pdf and .zip

Due: January 23