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.

Pre-requisites

You will need to understand

Phase 1

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.)

Interface

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

Phase 1 Testing

In order for your CPU to work seamlessly with the test suite, it must use the following interface:

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:

To run the Phase 1 tests, click on the "Actions" tab on your project's GitHub page, then select "Part 1 Workflow"

Debugging

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.

Phase 1 Hints

Phase 2

You have two main tasks to complete for Phase 2:

  1. Add additional instructions (which requires modifying the control unit).
  2. Determine the critical path for the CPU and set the clock accordingly.

Implement Additional Instructions

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:

  1. Create a new ROM unit with a larger word size in order to add extra bits, or
  2. Consolidate two or more of the existing control wires into one, and repurpose the newly "free" wire.

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.

Phase 2 Hints

To run the Phase 2 tests, click on the "Actions" tab on your project's GitHub page, then select "Part 2 Workflow"

Submission and grading

In order to get an 'M' or 'E' for this project, you must have

To submit your project:

  1. Make sure all .jls files include a comment with your name.
  2. Make sure PHSingleCycleCPU.jls includes a comment with the clock period.
  3. Make sure your repository includes all your test files / test plans.
  4. Add a commit message with [Grade Me] and push.

Updated Tuesday, 22 February 2022, 2:54 PM

W3c Validation