Lab 2: Taking Control - SW
Due: 6:00PM, 2/14/2007
The first lab focused on the the logic needed for the datapath whilst
providing all necessary control signals. This lab will add support for
three new instructions that are essential for subroutines. Once that logic
is present the remainder of the lab will be constructing a control unit that
supports all the normal operations. At the end the controller and datapath will
be joined together and tested.
Phase 0: Administration
This lab will take place in the same workspace as lab1, but the design will
now be lab2. The files for this lab are provided as an archived design. You will
need to restore the design, add it to the workspace, and copy the design files
from lab1 into the new design. Download the archived design lab2.zip and follow
- Download lab2.zip.
- Start Active-HDL and Open the cse378 workspace used in lab1.
- Select Design > Restore Design from the menu
- Browse to the downloaded lab2.zip file
- Set the Restore To: directory to the cse378 directory that contains the
folders lab1 and lib378 and click Finish.
At this point the files are all available. Now we need to tell Active-HDL
about the new design.
- Right-Click the yellow workspace icon name cse378 from the design browser
and choose "Add Existing Design to Workspace".
- Find and open the file "lab2.adf" in the newly restored lab2
- Set lab2 as the active design
- Click and Drag datapath.bde and pcaddresscomputer.v from
lab1 to lab2.
Phase 1: Subroutine Operations (
Function calls rely on the ability to jump to a location, run a number of
instructions and then return to the point after the call.
However, with the instructions implemented thus far, it is impossible to store PC or any value
explicitly derived from it in a register. Similarly, it's (in
general) impossible to jump or branch to a location specified in a register.
These requirements are satisfied by three new instructions.
The jump-register (
JR), jump-and-link (
JAL), and jump-and-link-register (
JALR) instructions remedy this situation.
The jump-register instruction (
JR) is an unconditional jump like the
instruction from Lab 1. The difference is that the target address for
from a register specified in the instruction. The jump-and-link instruction (
JAL) behaves like the
simple jump instruction (
J), but also stores a return address in register 31 ($ra).
This stored value is useful because the
JR or Jump-Register instruction sets the
PC to the value stored in a register. The jump-and-link-register
JALR) is the union of
meaning that it transfers control to the address in a specified register, and
stores the return address in the register file. However, unlike
JALR allows the programmer
to specify the destination register of the return address.
Part A. Using a register as a jump Target (
These two instructions set the PC to be the value in the
register specified by the rs field of the instruction. This is mainly to allow returns from subroutines, but has additional
advantages (see book for more). The PCAddressComputer from Lab 1 has an RS
input that was previously unimportant.
Note: RS refers to the value
coming from the output named "read data 1" on the register file.
- Check that the RS input port of the PCAddressComputer is connected properly.
- Modify PCAddressComputer Unit so PC =
Jump control signals are set.
Part B. Computing Return Addresses (
The return address for these instructions is the address of the next
instruction in the program. This address needs to be stored in the register
file, so we need some way to present it as the Write input to the register file.
The best way to do this is to simply pass the return address through the ALU by
adding the address to 0.
- Add a multiplexer between the Register file and the rs/immed multiplexer
that chooses between the value from the register file and the return
- Add a multiplexer between the Register file and the rt/immed multiplexer
that chooses between the value from the register file and a constant
- Note: Control signals are added in part D
Part C. Destination register for
The destination register is specified by a field in all previously
implemented instructions. The Jump-and-Link (
changes this trend by forcing the destination to be register 31, commonly called
the return address register. This behavior is unique to
and should not happen the
- Create a 5-bit constant containing
the value 31
- add a constant component
- Right-Click the new constant and select "Properties"
- Switch to the Parameters tab and set VALUE = 31 and WIDTH = 5
- Use an m5w_2_1 5-bit mux to
set register write destination for the
Part D. Control for
JAL operation requires the choice of register 31 as target and the next
instruction address as value to write to the register file. It also sets
the PC to the jump target stored in the instruction. The
sets the PC to value in the register specified by the RS field of the
instruction. Finally the
JALR instruction sets the PC the way
JR does, and
stores the next instruction the way
JAL does, but stores the result to a
register specified by the RD field of the instruction.
- Use control signal
JAL to perform
- Modify PCAddressComputer Unit to compute proper PC based on
Part E. Testing
Test the updated Datapath.bde with test fixture phase1_tf.v,
and macro phase1_runtest.do from the
Tests folder. If there are
any errors, you may want to see the source code for the test (phase1.s).
Phase 2: Building the Controller
The earlier tasks focused on building the datapath and connecting the proper
control signals. Now that work is complete, and the task of generating the
control signals must be addressed.
Fortunately, the MIPS instruction set makes instruction decoding easy by
grouping the instructions into 3 separate formats: R-format, I-format, and
J-format instructions. These formats describe the meanings of the bits in the
instruction and also help to group similar instructions.
R-format instructions are instructions that generally take two operand registers and store the result of the operation in a third register. Exceptions to this rule are instructions like
JR. For all R-format instructions, the opcode field (top 6 bits) will always be 0. This is followed by the rs, rt, and rd fields, which are each 5 bits in size. In most R-format instructions, the rs and rt fields indicate which registers are used as operands and the result is stored in the register indicated by the rd field. These fields are followed by the shamt field, which is also 5 bits in size and contains the amount to shift by in certain operations. Finally, there is a 6-bit funct field, which is used to differentiate between the different R-format instructions.
I-format instructions are instructions that generally take one register and an immediate value as operands, then store the result of the operation in the other specified register. Exceptions to this rule include branches and stores, which use the immediate field as an offset to determine the location to branch or store to. In all I-format instructions, the top 6 bits are used for a unique opcode which distinguishes the functions from each other and other types of instructions. This is followed by the rs and rt fields, each of which are 5 bits in size. In general, if a register is involved as a destination, the rt field is used to determine which register is the destination, while the rs field is used as the first operand. The last 16 bits of the instruction are used as an immediate value, which may or may not be sign extended depending on the instruction being executed.
J-format instructions are the used when jumps larger than can be handled by branches are needed. In a J-format instruction, the top 6 bits are used as an opcode to distinguish the instruction from other instructions, and this is followed by a 26-bit address to jump to. When a J-format instruction is executed, the 26-bit address is shifted left by 2, and the top four bits of PC+4 are prepended onto the resulting 28-bit address. Be aware that some jump instructions, like JR, are NOT J-format instructions, but are R-format instructions instead.
Part A. Putting it all in
A skeleton for the controller you need to implement is included in the design.
It is highly suggested that you treat each output wire as the result of a
boolean expression. The individual variables in the expression should be either
tests for a specific instruction, or the result of a different boolean
expression. For an example, assume that there was a control signal STOP
that was only asserted during instructions XACT and WAIT. Then, the following
Verilog would ensure that STOP was set properly.
wire op_x = (op == XACT);
wire op_w = (op == WAIT);
assign STOP = op_x | op_w;
The advantage of this scheme is that it minimizes repetitious code, which is
one key failing of using a large case statement to explicitly set the value of
every control signal for every instruction. The
ALUControl component is implemented in this style, so feel free to look at its
source for a larger example.
The expected behavior of the controller is detailed in a separate page. lab2_controller.html
Be sure to implement all of the following instructions. Take advantage of
similarity between groups of instructions
- Branch Instructions: BEQ, BNE, BLEZ, BGTZ
- Jump Instructions: J, JR, JAL, JALR (note that JR, JALR are R-Format
- Shift Instructions: SRL, SLL, SRA, SRLV, SLLV, SRAV
- Logical Immediates: XORI, ORI, ANDI
- Arithmetic Immediates: ADDI, ADDIU, SLTI, SLTIU
- Arithmetic: ADD, SUB, ADDU, SUBU, SLT, SLTU
- Logical: AND, OR, XOR, NOR
- Loads: LW, LH, LB, LHU, LBU
- Stores: SW, SH, SB
- Multiplies: MULT, MULTU
- HI-LO: MTHI, MFHI, MTLO, MFLO (MULT and MULTU put results in [HI,LO])
- Misc: LUI - load upper immediate. (see COD:3e 95-96)
Part B. Testing in Isolation
UPDATE: Before you start this part, open up the file phase2_tf.v and find a line with ".func(func));". Change this line to ".Func(func));".
The controller will be tested in isolation to verify that all instructions are implemented properly.
Test the controller with the test fixture phase2_tf.v,
and macro phase2_runtest.do. Be aware that the generated waveform may have regions where the signal is green.
These correspond to signals that are not required to have a specific value for
the current instruction. This does not mean that you may leave these signals
unassigned! They must have a value of 1 or 0. If there are
any errors, you may want to see the source code for the test (phase2.s). DO NOT continue until all operations pass
Phase 3: Putting it together
The controller needs to be integrated with the datapath now that both have
been successfully tested in isolation. To make things easier in later labs
we will want to add the controller and logic from the datapath into a single
block diagram. This diagram will represent the full single-cycle processor
and will only have input ports for things like CLK, Reset, Instruction, and
CPUDataIn and output ports for PCOut and CPUDataOut.
Part A: Combining Control and Datapath
- Open the block diagram cpu.bde.
- Copy the datapath logic from datapath.bde into cpu.bde
- Add the controller component to cpu.bde
- Connect things up properly
Part B: Testing the integration
Verify that the integration was successful with test fixture phase3_tf.v,
and macro phase3_runtest.do. If there
are errors, you may want to see the source code for the test (phase3.s).
To turn in your lab, complete all phases and use the Design -> Archive Design command in ActiveHDL on the final product to produce a .zip file containing all the essential files. Put this somewhere accessible via attu and use
"turnin -c cse378 'your file'" to submit it for grading.