This first lab will give you a chance to (re)learn Active-HDL while constructing the datapath and some of the control components your processor will need. We will provide a minimal datapath and necessary components. Your task is to augment the datapath to support a wider range of capabilities.
In CSE 370, the labs focused on the design and implementation of special-purpose circuits, which are designed to fulfill one specific role, e.g. a universal shift register or a magnetic card reader. In contrast to special-purpose circuits, there are general-purpose circuits that can be put to a variety of uses. In CSE 378, you will be designing a relatively simple version of possibly the best-known general-purpose circuit, the microprocessor. As discussed in lecture, one logical and common breakdown of a processor is the datapath vs. the control. The datapath is, along with state elements such as the register file and the program counter, simply the route by which data makes its way through the processor, whereas the control modifies various aspects of the processor's behavior based on its state. In short, the control may be considered the "brains" and the datapath the "brawns" of a processor.
In this writeup, there are several formatting conventions that are being used to help you distinguish between types of items in your design more easily. Code font indicates wire and input/output port names while italic font indicates a component that you will need to add from lib378. Addtionally, should you require basic components to use in your processor, such as muxes, you should get them from lib378 if possible.
One important convention that will be important in this section is the naming of the mux components. Each mux component has a name in the format mXw_Y_1_D where X represents the width of the mux (32, 6, 5, or variable - indicated by V - bits), Y represents the number of inputs that the mux can select between (2 or 4), and D represents where the selection port is on the mux (top or bottom). As an example, the component "m32_4_1_top" represents a 32-bit wide mux with 4 inputs that it can select between and a selection port on the bottom.
It is also VERY important that you follow the instructions when it comes to naming your wires. They must be named exactly as specified in this writeup with the correct case for characters. If you do not follow the naming conventions, the test fixtures will not function properly.
The original datapath contains only a register file for storage, and an ALU or arithmetic logic unit which performs all the computation. With only these components, a number of instructions can be computed. The test fixture will demonstrate this by running through a large number of operations.
- Download the archived workspace file lab1workspace.zip
- Start Active-HDL 7.1 and cancel out of the "Open Workspace" wizard
- Use Workspace->Restore Workspace to restore the workspace named cse378.
- Open Workspace cse378
- Right-click on Tests/phase1_runtest.do and select "Execute"
The simulation should display a series of messages in the console window that list and operation followed by the success or failure of that operation. Verify that all of the operations complete successfully before continuing.
Small constant values are used frequently in programs for initializing values, address calculations, iteration and other purposes. To save space in the register file, the MIPS instruction set allows small constants to be included in the instruction itself. A constant specified this way is called an immediate because its value is consumed immediately, and isn't available to later instructions in the general case. Consequently, for many simple register operations such as
ADD
, there are immediate versions. Your next task is to extend datapath.bde to implement the MIPS immediate instructions.Part A. ALUCTL
The first test fixture directly controlled the ALU by means of the
ALUCtl
signal. The ALUControl component generates these control signals for the ALU based on the instruction. The decision is made in two parts: first the ALUControl takes a two-bit ALUOp that determines the mode of ALUCtl: Add, Subtract, R-Format, or I-Format. The first two are self explanatory, while the last two tell the ALUControl whether its 6-bitfunc
oropcode
input determines the operation. In R-Format mode, thefunc
field of the instruction (bits 5:0) determines the operation. In the I-Format mode, theopcode
field of the instruction (bits 31:26) is used.
- Replace the ALUCtl input port with an ALUOp(1:0) input port
- Add an ALUControl symbol to the datapath
- Connect up the
opcode
andfunc
ports of the ALUControlPart B. Extensions
The immediate field in the MIPS immediate instructions is 16 bits wide, whereas the ALU performs its operations on 32-bit values. Therefore, the 16-bit immediate field must be widened to 32 bits while maintaining the same value in order for immediate instructions to produce correct results. For twos-complement binary values, the correct method for accomplishing this task is known as sign extension, which consists of filling in the new high-order bits with the msb of the original narrower value. For example, sign-extending 10 to 4 bits would result in 1110, and sign-extending 01 to 4 bits would result in 0001. This is the case for the arithmetic I-format instructions. However, the logical I-format instructions require zero-extended immediates, and
LUI
requires left-aligned immediates.
- Add a 2-bit input bus port named
ExtOp
- Add and appropriately wire up an Extender. Name the output wire
ExtImmed.
Part C. Computing with Immediates and Shifting
The extender processes the 16-bit immediate according to the
ExtOp
control signal but it still needs to be passed to the ALU. To make sure that immediate and shift instructions work, we need to pass the new immediate into the A and B inputs of the ALU. So we need to add multiplexers to choose between the values from the register file and the output from the extender.
- Add a multiplexer to choose between
ExtImmed
andRS
for ALU input A.- Add a multiplexer to choose between
ExtImmed
andRT
for ALU input B.- Add input ports
SrcASel
,SrcBSel
that control the appropriate multiplexers.- Wire up the new parts so that a select input of 0 selects the register -- the test fixture will assume this convention.
Part D. Testing the changes
Test the updated Datapath.bde with test fixture phase2_tf.v, and macro phase2_runtest.do. If there are any errors, you may want to see the source code for the test (phase2.s).
So far, the datapath supports register and immediate operations. However, a processor whose storage is limited to the on-board registers is a very sad processor indeed. To remedy this situation, the MIPS instruction set specifies two basic classes of operations that interface with main memory, loads and stores. Load instructions cause the value in a memory location to be transferred to a register, and stores transfer the value in a register to a memory location. You will next add hardware to implement loads and stores.
Part A. Adding Memory Ports
For the duration of Lab 1, the memories will be separate from the datapath and will be accessible by ports instead. Interfacing with the data memory will require an input and an output for the data. The instruction memory will require an output for the address and will continue using the instruction input. Note that the instruction memory input and data memory address outputs are already present. All ports to be added in this part will be 32 bits wide. Name the data memory input port
CPUDataIn
and the data memory output portCPUDataOut
.Part B. Storing to Memory (SW, SB, SH )
There are three main store instructions: SW,SB, and SH. The S stands for store, and the other letter represents the size of the data to store: W - word (4 bytes), H - halfword (2 bytes), and B - byte (1 byte).
Connect the appropriate ports. In the case of SB and SH, the controller and memory system will take care of selecting the correct bits, so do not attempt to select or otherwise alter the memory-bound bits. In short, always send a full unaltered word to data memory.
Part C. Reading from Memory ( LW, LB, LH )
- Add a multiplexer to select whether to write the ALU output or data memory output to register.
- Add a input port for the multiplexer's select input named
Load
. An input of 0 should cause the ALU output to be chosen.- Wire things up appropriately and connect ports as necessary.
- Note: As with stores, the controller and memory system will take care of choosing the correct bits, so you are encouraged not to alter the incoming data from memory.
Part D. Testing your Changes
Test the updated Datapath.bde with test fixture phase3_tf.v, and macro phase3_runtest.do. If there are any errors, you may want to see the source code for the test (phase3.s).
The program counter (PC) is a register whose value corresponds to the address of the current instruction in the instruction memory. In addition to the PC itself, the processor requires hardware to calculate the next value of PC, which is simply PC+4 unless otherwise specified in an instruction. The next task is to implement the PC as well as the PC-updating hardware.
Part A. Standard Path (PC += 4)
- Add a PC register
- Add an Adder
- Add a constant and set to 4
- Connect it all up
- Add an input port named
RESET
and connect it to a global wire (Just likeCLK
)- Connect the output from the PC register to a 32-bit output port named
PCOut
Part B. Jumps and Branches
There are two basic classes of instructions that cause program execution to deviate from the "normal" sequential pattern. Jumps cause the processor to execute an instruction at an absolute location, whereas branches cause the processor to execute an instruction relative to the current value of PC+4. In the MIPS instruction set, jumps are unconditional, where as branches are conditional.
- Add the following 1-bit inputs:
Branch
,JAL
,JR
, andJump
.- You must now fill in the PCAddressComputer. Please refrain from using an always block. Rather, use assign statements and the ternary operator (<condition>?<true case>:<false case>) to generate the proper values. Complete the following in PCAddressComputer:
- Add code to compute Jump Addresses
- Add code to compute branches:
- BEQ: Branch if RS == RT
- BNE: Branch if RS != RT
- BGTZ: Branch if RS > 0 (Be aware that ActiveHDL has no concept of negative numbers, so you won't be able to use the less than/greater than operators for this)
- BLEZ: Branch if RS <= 0
- Wire up your PCAddressComputer
Part C. Testing
Test the updated Datapath.bde with test fixture phase4_tf.v, and macro phase4_runtest.do. If there are any errors, you may want to see the source code for the test (phase4.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. You should not need to change anything on this screen, just tell it to archive what it selects by default. Put this somewhere accessible via attu and use "turnin -c cse378 -p lab1 'your file'" to submit it for grading.
If you are using a late day, use "turnin -c cse378 -p lab1_late 'your file'" to turn in your program for grading. You have 24 hours after the original turnin time to submit your lab for grading.