Hi, I’m Carrie Anne, this is Crash Course Computer Science, and today, we’re talking about processors.
Just a warning though - this is probably the most complicated episode in the series.
So once you get this, you’re golden.
We’ve already made a Arithmetic and Logic Unit, which takes in binary numbers and performs
calculations, and we’ve made two types of computer memory: Registers -- small, linear
chunks of memory, useful for storing a single value -- and then we scaled up, and made some
RAM, a larger bank of memory that can store a lot of numbers located at different addresses.
Now it’s time to put it all together and build ourselves the heart of any computer,
but without any of the emotional baggage that comes with human hearts.
For computers, this is the Central Processing Unit, most commonly called the CPU.
INTRO
A CPU’s job is to execute programs.
Programs, like Microsoft Office, Safari, or your beloved copy of Half Life: 2, are made
up of a series of individual operations, called instructions, because they “instruct”
the computer what to do.
If these are mathematical instructions, like add or subtract, the CPU will configure its
ALU to do the mathematical operation.
Or it might be a memory instruction, in which case the CPU will talk with memory
to read and write values.
There are a lot of parts in a CPU, so we’re going to lay it out piece by piece, building
up as we go.
We’ll focus on functional blocks, rather than showing every single wire.
When we do connect two components with a line, this is an abstraction for all of the necessary wires.
This high level view is called the microarchitecture.
OK, first, we’re going to need some memory.
Lets drop in the RAM module we created last episode.
To keep things simple, we’ll assume it only has 16 memory locations, each containing 8 bits.
Let’s also give our processor four, 8-bit memory registers, labeled A, B, C and D which
will be used to temporarily store and manipulate values.
We already know that data can be stored in memory as binary values
and programs can be stored in memory too.
We can assign an ID to each instruction supported by our CPU.
In our hypothetical example, we use the first four bits to store the “operation code”,
or opcode for short.
The final four bits specify where the data for that operation should come from -
this could be registers or an address in memory.
We also need two more registers to complete our CPU.
First, we need a register to keep track of where we are in a program.
For this, we use an instruction address register, which as the name suggests, stores the memory
address of the current instruction.
And then we need the other register to store the current instruction, which we’ll call the instruction register.
When we first boot up our computer, all of our registers start at 0.
As an example, we’ve initialized our RAM with a simple computer program that we’ll to through today.
The first phase of a CPU’s operation is called the fetch phase.
This is where we retrieve our first instruction.
First, we wire our Instruction Address Register to our RAM module.
The register’s value is 0, so the RAM returns whatever value is stored in address 0.
In this case, 0010 1110.
Then this value is copied into our instruction register.
Now that we’ve fetched an instruction from memory, we need to figure out what that instruction is
so we can execute it.
That is run it.
Not kill it.
This is called the decode phase.
In this case the opcode, which is the first four bits, is: 0010.
This opcode corresponds to the “LOAD A” instruction, which loads a value from RAM
into Register A.
The RAM address is the last four bits of our instruction which are 1110, or 14 in decimal.
Next, instructions are decoded and interpreted by a Control Unit.
Like everything else we’ve built, it too is made out of logic gates.
For example, to recognize a LOAD A instruction, we need a circuit that checks if the opcode
matches 0010 which we can do with a handful of logic gates.
Now that we know what instruction we’re dealing with, we can go ahead and perform
that instruction which is the beginning of the execute phase!
Using the output of our LOAD_A checking circuit, we can turn on the RAM’s read enable line
and send in address 14.
The RAM retrieves the value at that address, which is 00000011, or 3 in decimal.
Now, because this is a LOAD_A instruction, we want that value to only be saved into Register A
and not any of the other registers.
So if we connect the RAM’s data wires to our four data registers, we can use our LOAD_A
check circuit to enable the write enable only for Register A.
And there you have it -- we’ve successfully loaded the value at RAM address 14 into Register A.
We’ve completed the instruction, so we can turn all of our wires off, and we’’re
ready to fetch the next instruction in memory.
To do this, we increment the Instruction Address Register by 1 which completes the execute phase.
LOAD_A is just one of several possible instructions that our CPU can execute.
Different instructions are decoded by different logic circuits, which configure the CPU’s
components to perform that action.
Looking at all those individual decode circuits is too much detail, so since we looked at one example,
we’re going to go head and package them all up as a single Control Unit to keep things simple.
That’s right a new level of abstraction.
The Control Unit is comparable to the conductor of an orchestra, directing all of the different
parts of the CPU.
Having completed one full fetch/decode/execute cycle, we’re ready to start all over again,
beginning with the fetch phase.
The Instruction Address Register now has the value 1 in it, so the RAM gives us the value
stored at address 1, which is 0001 1111.
On to the decode phase!
0001 is the “LOAD B” instruction, which moves a value from RAM into Register B.
The memory location this time is 1111, which is 15 in decimal.
Now to the execute phase!
The Control Unit configures the RAM to read address 15 and configures Register B to receive the data.
Bingo, we just saved the value 00001110, or the number 14 in decimal, into Register B.
Last thing to do is increment our instruction address register by 1, and we’re done with another cycle.
Our next instruction is a bit different.
Let’s fetch it.
1000 01 00.
That opcode 1000 is an ADD instruction.
Instead of an 4-bit RAM address, this instruction uses two sets of 2 bits.
Remember that 2 bits can encode 4 values, so 2 bits is enough to select any one of our 4 registers.
The first set of 2 bits is 01, which in this case corresponds to Register B, and 00, which is Register A.
So “1000 01 00” is the instruction for adding the value in Register B into the value in register A.
So to execute this instruction, we need to integrate the ALU we made in Episode 5 into our CPU.
The Control Unit is responsible for selecting the right registers to pass in as inputs,
and configuring the ALU to perform the right operation.
For this ADD instruction, the Control Unit enables Register B and feeds its value into
the first input of the ALU.
It also enables Register A and feeds it into the second ALU input.
As we already discussed, the ALU itself can perform several different operations, so the
Control Unit must configure it to perform an ADD operation by passing in the ADD opcode.
Finally, the output should be saved into Register A. But it can’t be written directly
because the new value would ripple back into the ALU and then keep adding to itself.
So the Control Unit uses an internal register to temporarily save the output, turn off the
ALU, and then write the value into the proper destination register.
In this case, our inputs were 3 and 14, and so the sum is 17, or 00010001 in binary,
which is now sitting in Register A. As before, the last thing to do is increment our instruction
address by 1, and another cycle is complete.
Okay, so let’s fetch one last instruction: 01001101.
When we decode it we see that 0100 is a STORE_A instruction, with a RAM address of 13.
As usual, we pass the address to the RAM module, but instead of read-enabling the memory, we write-enable it.
At the same time, we read-enable Register A. This allows us to use the data line to
pass in the value stored in register A.
Congrats, we just ran our first computer program!
It loaded two values from memory, added them together, and then saved that sum back into memory.
Of course, by me talking you through the individual steps, I was manually transitioning the CPU
through its fetch, decode and execute phases.
But there isn’t a mini Carrie Anne inside of every computer.
So the responsibility of keeping the CPU ticking along falls to a component called the clock.
As it’s name suggests, the clock triggers an electrical signal at a precise and regular interval.
Its signal is used by the Control Unit to advance the internal operation of the CPU,
keeping everything in lock-step - like the dude on a Roman galley drumming rhythmically
at the front, keeping all the rowers synchronized... or a metronome.
Of course you can’t go too fast, because even electricity takes some time to travel
down wires and for the signal to settle.
The speed at which a CPU can carry out each step of the fetch-decode-execute cycle is called its Clock Speed.
This speed is measured in Hertz - a unit of frequency.
One Hertz means one cycle per second.
Given that it took me about 6 minutes to talk you through 4 instructions -- LOAD, LOAD,
ADD and STORE -- that means I have an effective clock speed of roughly .03 Hertz.
Admittedly, I’m not a great computer but even someone handy with math
might only be able to do one calculation in their head every second or 1 Hertz.
The very first, single-chip CPU was the Intel 4004, a 4-bit CPU released in 1971.
It’s microarchitecture is actually pretty similar to our example CPU.
Despite being the first processor of its kind, it had a mind-blowing clock speed of 740 Kilohertz
-- that’s 740 thousand cycles per second.
You might think that’s fast, but it’s nothing compared to the processors that we use today.
One megahertz is one million clock cycles per second, and the computer or even phone
that you are watching this video on right now is no doubt a few gigahertz -- that's
BILLIONs of CPU cycles every… single... second.
Also, you may have heard of people overclocking their computers.
This is when you modify the clock to speed up the tempo of the CPU -- like when the drummer
speeds up when the Roman Galley needs to ram another ship.
Chip makers often design CPUs with enough tolerance to handle a little bit of overclocking,
but too much can either overheat the CPU, or produce gobbledygook as the signals fall behind the clock.
And although you don’t hear very much about underclocking, it’s actually super useful.
Sometimes it’s not necessary to run the processor at full speed...
maybe the user has stepped away, or just not running a particularly demanding program.
By slowing the CPU down, you can save a lot of power, which is important for computers
that run on batteries, like laptops and smartphones.
To meet these needs, many modern processors can increase or decrease their clock speed
based on demand, which is called dynamic frequency scaling.
So, with the addition of a clock, our CPU is complete.
We can now put a box around it, and make it its own component.
Yup.
A new level of abstraction!
RAM, as I showed you last episode, lies outside the CPU as its own component, and they communicate
with each other using address, data and enable wires.
Although the CPU we designed today is a simplified example, many of the basic mechanics we discussed
are still found in modern processors.
Next episode, we’re going to beef up our CPU, extending it with more instructions as
we take our first baby steps into software.
I’ll see you next week.