CIS 351 |
Project 4 |
Winter 2022 |
GitHub Classroom link to the starter code. Make sure you have the latest version of DLUnit
. You can download it here.
For this project, you will use JLS to build the Single Cycle CPU described in Chapter 7 of the Harris and Harris textbook (Chapter 4 in the Patterson and Hennessy textbook).
You will complete this project in two phases:
It is critically important that you thoroughly test Phase 1 before moving onto Phase 2. Find me immediately if you have any trouble getting the testing software to work. If you don't have the testing software working, you really have no idea whether your CPU is functioning correctly.
Be sure to read this entire document (especially the hints) before you begin. Overlooking any of the instructions or hints can lead you down a dead-end path that will waste hours of your valuable time.
Build the Single-Cycle CPU shown in in Figure 7.14. To help you get
started, the starter code contains StarterCPU_w22.jls
which includes a
Before you begin, rename the starter CPU file PHSingleCycleCPU.jls
. You aren't required to use the provided components;
but, building your own can be tedious. The purpose of the "starter" control unit is so
that you can debug your CPU's datapath separately from the control unit. Once you have the core instructions
working, then modify my control unit to support additional instructions. (This is phase 2.)
At the end of Phase 1, your CPU should support the following instructions:
Name | Mnemonic | Format | OpCode (in hex) |
Func Code (in hex) |
---|---|---|---|---|
Add | add | R | 0 | 20 |
Add Immediate | addi | I | 8 | |
And | and | R | 0 | 24 |
And Immediate | andi | I | c | |
Branch if Equal | beq | I | 4 | |
Halt | halt |   | 20 | |
Jump | j | J | 2 | |
Load Word | lw | I | 23 | |
Load Upper Immediate | lui | I | F | |
Nor | nor | R | 0 | 27 |
Or | or | R | 0 | 25 |
Or Immediate | ori | I | d | |
Set Less Than | slt | R | 0 | 2a |
Set Less Than Immediate | slti | I | a | |
Store Word | sw | I | 2b | |
Subtract | sub | R | 0 | 22 |
PHSingleCycleCPU.jls
. (Did you remember to rename the starter file?)InstMemory
.RegisterFile
.MainMemory
and configure it with 220
words.
DLUnit
has a built-in test class that can test this CPU. In addition to the .jls
file, you also
provide an assembly file as input. DLUnit
will (1) simulate the assembly code using MARS
,
(2) simulate the assembly code using your CPU, then (3) compare the final state of the registers from each simulation. The test will pass if your CPU ends in the same state as
the CPU simulated by MARS
.
IMPORTANT: DLUnit
only checks the final state of the CPU. If you write to a register more than once, the intermediate values of that register won't get checked.
The syntax for running DLUnit
to test a CPU is java -jar DLUnit.jar jls_file.jls builtin.SingleCycleCPUTest --param assembly_file.a
To test your code, you will need to write several example assembly programs. The starter code includes a few example testing programs;
however, these programs alone won't test your CPU well.
Avoid the following cases when writing your phase 1 tests:
slt
/slti
. It will give the wrong answer if the inputs overflow when subtracting.andi
and ori
will give incorrect results if the immediate value is negative (or has bit 15 set to 1). You'll fix this in Phase 2.To run the Phase 1 tests, click on the "Actions" tab on your project's GitHub page, then select "Part 1 Workflow"
The best way to debug a circuit that is failing tests is to use JLS to step through each instruction. The starter code includes a
bash script named marsAssembler
that takes an assembly file as input and generates machine code. To debug your circuit, run
marsAssembler
and redirect its output to a file named instructions
. Your circuit's Instruction Memory element
is configured to load data from this file. So, if you launch JLS in a directory containing instructions
you will be able to
step through your CPU instruction by instruction. Important: marsAssembler
is a bash script. It won't run on the
Windows command line. You will need to use Cygwin, WSL, or something similar to run it. Another option is to look inside the bash script
and just run the Windows version of that command "by hand".
Note: marsAssembler
only works with DLUnit
version 1.1.1 or newer (built 3 November 2020). You may need to
download the most recent version (linked here). Also, make sure marsAssembler
and DLUnit.jar
are in the same directory.
WE
, OE
, and
CS
inputs to RAM and ROM are active low.
(WE stands for Write Enable; OE is Output Enable; and CS is Chip Select. )
memWrite
and memRead
wires are 1 when a
memory write or memory read is desired.
PC + 4
, not
PC
.
beq
.
lw $16, val1
is a pseudo instruction. It won't
work unless both lui
and lw
work.
You have two main tasks to complete for Phase 2:
Implement these additional instructions:
Name | Mnemonic | Format | OpCode (in hex) |
Func Code (in hex) |
---|---|---|---|---|
Add Unsigned | addu | R | 0 | 21 |
Add Immediate Unsigned | addi | I | 9 | |
Branch if not Equal | bne | I | 5 | |
Jump and Link | jal | J | 3 | |
Jump Register | jr | R | 0 | 08 |
Set Less Than Unsigned | sltu | R | 0 | 2b |
Set Less Than Immediate Unsigned | sltiu | I | b | |
Subtract Unsigned | sub | R | 0 | 23 |
Xor | xor | R | 0 | 26 |
Xor Immediate | xori | I | e |
To implement these instructions, you will need to both (1) add additional data paths in the CPU, and (2) add support for these instruction in the control unit. In fact, you will need to add additional control wires.
The provided control unit is a very simple microcode. If you look inside the control unit, you will see that it is primarily just a ROM (Read Only Memory). Each op code is just an index into the ROM; and the bits stored at that index are the values to be placed on the control wires. In other words, the control unit is just a lookup table. If you right-click on the ROM inside the control unit and select "Modify", then select "Built-in", you can see this microcode. The text field contains two columns. The first is the opcode (in hex), and the second is the bits returned (in hex). To add new instructions, you need only add the hex specifying the control wire values for that op code.
Notice however, that you will need additional control wires for some of the instructions. This control unit has three unused wires; but, you will need at least 4. Thus, you have two choices:
This microcode handles function codes in a special way. Notice that the ROM contains 27=128
lines of data,
not 26=64
.
Lines 1-63 in the ROM contain the control values for each op code except 0. Lines 64-127 contain the control values for instructions that use
an op-code/function pair. Specifically, line 64+x
contains the control values for the instruction with a function code of x
.
(Remember, only instructions with an op code of 0 use a function code.) For example, add
has an op code of 0
and a function code of
0x20 = 32
. Therefore, the control values for add
are stored on line 64+32 = 96
which is 0x60
.
StarterControl.jls
. You may want to make your changes to this file, then
import it into the CPU when you are done. using a separate .jls
file will both you and me to test the
control unit separately from the CPU. (I won't be able to help you debug your CPU if I can't test your control unit.)
slt
and sltu
. You may add hardware
to fix them. (You built this hardware for Project 1.) This is optional.
0x20
for halt
and function code of 0x20
for add
). Be sure your tests account for this.To run the Phase 2 tests, click on the "Actions" tab on your project's GitHub page, then select "Part 2 Workflow"
In order to get an 'M' or 'E' for this project, you must have
To submit your project:
.jls
files include a comment with your name.PHSingleCycleCPU.jls
includes a comment with the clock period.[Grade Me]
and push.Updated Tuesday, 22 February 2022, 2:54 PM