MIPS CPU with a single clock cycle
The MIPS architecture is of type RISC and originated in 1981 from a research project by Prof. Hennessy at Stanford. This architecture is characterized by instructions of the same length, and is geared toward simplifying pipeline implementation.
In this post we will see and comment the schematic of the single clock cycle MIPS CPU, to understand what certain functional units are used for.
Here is the simplified schematic of a one-shot-clock MIPS CPU.
It may seem complex at a first glance, but after some exploration, it gets better.
Clock cycle phases
As a start, it is useful to keep in mind the steps in the execution of an instruction:
- Fetch: the instruction is taken from instruction memory;
- Decode: the instruction is unpacked, and the individual pieces take on their meaning;
- Execute: the ALU performs a calculation;
- Memory: eventually the ALU’s result or read from a register is written to memory, or memory is read;
- Write-Back: possibly the result of the ALU or the read from memory is written to a register.
Now it’s pretty quick to see similarities with the pattern:
- Fetch: includes the PC (Program Counter) and the nearby adder;
- Decode: includes unpacking the instruction, the control unit, and reading from registers;
- Execute: covers the part where the ALU operates;
- Memory: access to data memory;
- Write-Back: the output of the lower-right mux.
In the MIPS architecture we have 3 types of instructions:
J. I elaborate on this in the MIPS assembly post, but for the purpose of this explanation we only need the instruction format in general.
|R||oc (6)||rs (5)||rt (5)||rd (5)||shamt (5)||funct (6)|
|I||oc (6)||rs (5)||rt (5)||imm (16)|
|J||oc (6)||dest (26)|
Some arguments are common between
I, while others vary depending on the format. Let’s clarify:
oc: is the opcode of the instruction, that is, a code that identifies it (and thus determines its format);
rs: source register;
rt: target register;
rd: destination register;
shamt: shift amount*; if the operation is a shift, it represents the number of positions to be shifted;
func: together with the control signal
AluOpdetermines the function to be passed to the ALU;
imm: immediate part, i.e., a constant;
dest: constant indicating the address to jump to.
After reading the
opcode, the control unit activates appropriate control signals so that the CPU behaves well. On the schematic, the control signals are highlighted in blue, and are:
RegDst: chooses the target register between
rt. It is useful because it allows the two encodings
Ito be supported without adding too much circuitry. Simply, depending on the format one of the two arguments is chosen as the destination;
Jump: active if the instruction is from the
Branch: active if the instruction is from the
MemRead: active if the instruction reads from memory;
MemToReg: active if the data read from memory is to be slapped into a register;
AluOp: identifies the operation to be performed by the ALU (I carry over instructions):
1x: depends on the
MemWrite: active if the instruction writes to memory;
AluSrc: active if the second operand of the ALU is to be the
RegWrite: active if the instruction writes to a register.
Very nice. Now all the control signals make sense and don’t scare us anymore; so we can move on.
Let’s break the pattern into blocks, and comment on it.
In this step we need to fetch the next instruction from instruction memory. We know that all instructions are one word (4 bytes) long, so we have a nice adder that just does the
current_address + 4 calculation;
The handling of jumps or branches happens in the next step, that is, when we have the control signals well set up.
Here we have to pack the instruction and based on the
oc activate the appropriate control signals. Let’s take a closer look at how the CPU behaves.
Branch and Jump
Here it is convenient to analyze these two control signals together to see how the
Branchis active, it means we have to jump if the condition is true. The condition check is done by the ALU with a difference between the values of the two registers to be checked. If the result is
0it means that the condition is verified, and therefore we have to skip. For this we have
Branch AND 0which activates the mux that chooses between the result of the adder seen before, and the relative address of the jump;
Jumpis active, we have to jump to the address contained in the
destfield, so the
PCis updated with the jump address.
Quick question: *what happens if
Branch are both active?
Now based on the values of the instruction fields, data is read from the registers.
We have the arguments of the ALU and the operation to execute. The ALU calculates, and the result can represent:
- The address to read from in RAM;
- The jump enable of a branch;
- A value to be written to a register.
Based on what was said just now and the control signals, it can either read or write.
MemToRegis active, the read data is thrown directly into the destination register;
- Otherwise, the result of the ALU is written.
To conclude, today we found out:
- What a MIPS CPU with a single clock cycle looks like.
- What the execution stages are.
- What control signals exist, and how they are activated.
- How control signals affect the execution flow.
- That control signals are important.