7. The Interconnect, Control, and Instruction Decoding
Instructin Execution
We have all of the pieces for a CPU. So, let's put them together.
First the phases of instruction execution: FDXMW
- Fetch (IF or F)
- use PC to get the next instruction from memory
- Decode (ID or D)
- look at the fetched instruction and set control signals.
- Execute (EX or X)
- wait for data to flow through the datapath.
- Memory access (MEM or M)
- if it's a load or store, do that
- write-back (WB or W)
- if there's a destination register, write the result to it.
Often we can do multiple phases "at the same time."
Memory
So, how many RAMs does a computer have? one or two??? Well, if we try lw t0, (s0)
with one memory in a single cycle... It doesn't really work.
One way to do it is with the Harvard Architecture where 1 word for memories.
Von Neumann has 2 words for 1 memory and uses multiple clock cycles to execute each instruction.
The Interconnect
We have pieces of each of the CPU (see the past like 5 pages), but they do not operate in isolation. So, let's hook them together and we again rely on the ISA to tell use what needs to go where.
Let's look at different instructions from the ISA, these will tell us what components are connected.
Ignore the 4.
The interconnect lets the CPU parts combine in many ways. It's like the CPU's "circulatory system" - it moves data around.
Ok, now we have an idea of what instructions do what and how they do it. Let's complete this idea.
An interconnect matrix helps you keep track of what is going on. Consider all the instructions your CPU should support and mark the cells accordingly.
Any component (column) with multiple things coming into it will need a MUX. The interconnect makes choices about which things go where.
For MIPS, let's create a simple version. We can connnect the pieces together into a datapath.
For addi, lw, sw, the dest and source 2 are connected which makes things easier to handle. li, is a peusdo-instruction so we need to do an addi with source is zero and ori.
Notes About the Simplifed Version
This version doesn't support jal.
To implement add, sub, addi, lw, sw, li, we need to control the control signals.
The Forgotten Phase: Operand Fetch
The operand fetch is a phase of the instruction excecution you might see in some architectures. It fetches the values to be operated on.
It happens somewhere between the DX. In MIPS, operand fetch is super simple.
This is by design with load-store architectures. They have simple operand fetch phases.
With CISCy, like x86 has some crazy instructions. inc [eax + ecx*4 + 12]
The brackets contain the effective address calculation.
Here's what the CPU has to do for this instruction:
- multiply ecx by 4
- add eax to that
- add 12 to that
- load a word from that address
- add 1 to that value
- store that value back into the address
Now, between 1 and 4, that is the operand fetch.
The Control
The control is what set the write enables and selects to the appropriate values to make each instruction happen. It's like the CPU's brain and nervous system. It looks at the instruction and determines what needs to happen.
Well, there are two kinds of control signals.
- First there are the selects, these go into the select pins of the muxes, demuxes, or decoders. They can be any number of bits.
- Then there are the write enables, theses tell the registers and memory when to store data.
NEVER PUT WRITE ENABLE AS DON'T CARE
They often come in pairs, like RegWrite and RegDataSrc. They decide what to write and when to write it.
Write enables are kind of the basis of things happening in a CPU. Almost every instruction writes something somewhere. If an instruction doesn't write anything, it's a no-op (nop).
For the control hardware itself:
Instruction Decoding
The first step is to split the encoded instruction up. But which instruction format is it? Well, it doesn't matter. All we have to do, is to just use a spliter.
As the famous line my roommate and I use, "don't worry about it!" All you have to do is to just ignore the other outputs.
Let's make the control work. The control is just a big boolean function that takes instructions opcode as its input and outputs the control signals.
However, this is not the only way of making your control unit. It is very time-consuming, confusing, hard to debug, and hard to change.
Why not just use a decoder?
Let's come up with the MemWrite control signal, for instance. This is quite straightforward.
Wait, what about multi-bit control signals like ALU operations?
We can use a MUXtipede, but it's hard.
Instead, why not just use a priority encoder.
Priority Encoder Review
This is an example of how it works.