Lab: CHUMP
In this lab, you will learn about different parts of a simple 4-bit computer processor: The CHUMP (Cheap Homebrew Understandable Minimal Processor). You will get to know each part by using it, but you will not build any circuits of your own.
Exercise 1: Carrying Multiple Bits
Carrying a 4-bit value from one place in a circuit to another requires 4 wires, each carrying 1 bit. A 64-bit computer processor adds, subtracts, stores, and retrieves 64-bit values. Every time a value must be moved from one place in such a processor to another, 64 wires are required.
All those wires can make it difficult to follow a circuit diagram. Therefore, CircuitVerse lets you draw a single 4-bit wire to connect 4-bit input elements, 4-bit outputs, 4-bit selectors, 4-bit flip-flops, etc., as shorthands for 4 separate wires connecting 4 separate inputs, 4 separate outputs, etc.
Open the Multiple Bits project in CircuitVerse.
Click on Fullscreen near the top-right to see a larger version of the circuit.
Enter the number 14 using the 4 separate inputs.
Now enter the number 14 using the single 4-bit input.
Exercise 2: Register
A register is an essential part of any computer processor. It is the hardware equivalent of a variable. It can store a binary value, retrieve that value, and replace that value.
Open the Register project in CircuitVerse.
You are already familiar with all the parts inside the register (a selector and a flip-flop). You should have no trouble figuring out how this simple finite state machine works.
Use the In, Load, and Clock inputs to perform the following sequence of operations.
Output the number 3.
Set In to 5 and set Load appropriately so that when you tick the Clock, the output does not change (remains 3).
Output the number 5.
Output the number 0.
Exercise 3: ALU
The Arithmetic Logic Unit is the heart of the computer processor. It is usually just referred to as the ALU (pronounced: AY ehl YOO). You already know how to build combinational logic to add binary values. The ALU is simply a more complicated combinational logic device that performs a variety of simple functions.
Open the ALU project in CircuitVerse.
Copy the following table into your notes.
Function Description
000
001
010
011
100
101
110
111
In the Description column, you'll describe what each function does. Specifically, for each function, you'll write down how the output depends on the values of A and B. For example, if, for some value of Function, the output is always the sum of A and B, then you might write A + B. If, for some other value of Function, the output is always the value of B (no matter what A is), then you might simply write B. If, for some other value of Function, the output is always 1101 (no matter what A or B is), then you might simply write 13.
Use the A, B, and Function inputs to determine what each value of Function does, and write down your descriptions. You will need this information throughout the lab.
Notice there is also a 1-bit output marked 1s?. What does it tell you?
Exercise 4: Datapath
Now we'll look at a datapath that uses an ALU and a register.
Open the Datapath project in CircuitVerse.
The output of the ALU goes into the register, and the output of the register loops back into the A input of the ALU. This lets the register accumulate the results of calculations from the ALU, and so we call this register the accumulator. The B input of the ALU is a constant that can be added to or subtracted from the accumulator.
Use the Constant, ALU Function, Load Accum, and Clock inputs to perform the following sequence of operations.
LOAD 3 (store 3 in the accumulator)
ADD 2 (add 2 to the accumulator, so that it now contains 5)
SUBTRACT 1 (subtract 1 from the accumulator, so that it now contains 4)
LOAD 0 (store 0 in the accumulator, without using addition or subtraction)
Exercise 5: RAM
Now we'll look at RAM, which stands for Random Access Memory. RAM is the hardware equivalent of an array. You can access (retrieve) the value stored at any (random) address of memory, and you can replace any value. (More correctly, an array is an abstraction that programming languages use to interact with RAM.)
Suppose our RAM stores 8 data values, each consisting of 8 bits, as illustrated below.
Address Data Value
0 (8 bits)
1 (8 bits)
2 (8 bits)
3 (8 bits)
4 (8 bits)
5 (8 bits)
6 (8 bits)
7 (8 bits)
Question: For this RAM, how many bits are needed to specify which address to read from or write to?
Question: For this RAM, how many bits are needed to indicate if we wish to read or overwrite the value at the specified address?
Question: For this RAM, how many bits are needed for the data value we wish to store at the specified address?
Question: For this RAM, how many bits are needed to output the value from the specified address?
Typically, values stored in RAM are lost when the machine is powered down. Inside RAM, each value may be stored in its own register. For example, the following diagram shows a RAM with just 2 addresses. (In practice, building RAM out of registers is often prohibitively expensive.)
Open the RAM project in CircuitVerse.
Question: How many addresses does this RAM have?
Question: How many bits are stored at each address of this RAM?
Question: What is the total number of bits stored in this RAM?
Use the Address, Data In, and Write RAM inputs to perform the following sequence of operations.
At address 3, store the number 5.
At address 6, store the number 8.
Retrieve the value 5 from address 3 (without changing Data In and without overwriting address 3).
Exercise 6: Datapath with RAM
Open the Datapath with RAM project in CircuitVerse.
This is the same datapath as before, but now we have added RAM. Notice that we can now save the value in the accumulator to RAM. We can also now choose whether to use the Constant or the value retrieved from RAM ("it") as the input to the ALU. We can also use the Constant or value retrieved from RAM as the next address to read from or write to. Notice that flip-flops have been added so that no value is read or written until the clock ticks.
Use the Constant, Argument, ALU Function, Write RAM, Load Accum, and Clock inputs to perform the following sequence of operations.
LOAD 3 (store 3 in the accumulator)
ADD 2 (add 2 to the accumulator, so that it now contains 5)
SUBTRACT 1 (subtract 1 from the accumulator, so that it now contains 4)
STORETO 7 (store the 4 in the accumulator at address 7 of RAM--without changing the accumulator)
LOAD 2 (store 2 in the accumulator, without using addition or subtraction--without writing anything to RAM)
ADD 1 (add 1 to the accumulator, so that it now contains 3)
READ 7 (read from address 7 of RAM, so that 4 now appears at the output labelled "it"--without changing the accumulator)
LOAD IT (store the 4 retrieved from RAM in the accumulator)
ADD 1 (add 1 to the accumulator, so that it now contains 5)
READ 7 (read from address 7 of RAM, so that 4 now appears at the output labelled "it")
ADD IT (add the 4 retrieved from RAM to the accumulator, so that it now contains 9)
Exercise 7: Counter
Open the Counter project in CircuitVerse.
This 4-bit counter is essentially a fancy version of the register we saw earlier. Like the register, this counter is a finite state machine that lets you enter and store a 4-bit value. However, when the counter isn't storing a new 4-bit value, it adds 1 to the stored value each time the clock ticks. Also, this counter can be reset to 0.
Use the In, Load, Reset, and Clock inputs to perform the following sequence of operations.
Tick the clock until the counter outputs the number 9.
Reset the counter to output the number 0 (without first outputting 10, 11, 12, 13, 14, 15).
Tick the clock until the counter outputs the number 9 again.
Load the number 3 into the counter.
Tick the clock once so that the counter outputs the number 4.
Exercise 8: Datapath with Program Counter
Counters are great for keeping track of what program line number is currently being executed. Why? Suppose you've just executed line 7 of a program. Most likely, you're going to move on to execute line 8, then line 9, which is accomplished by incrementing the counter. But occasionally you'll reach an instruction that requires you to jump to an unrelated line number, which is accomplished by loading that new line number into the counter. And you can reset your counter to start over from line 0. The counter that keeps track of a program line number is traditionally called the program counter, and is often abbreviated as PC.
Open the Datapath with Program Counter project in CircuitVerse.
This is the same datapath as before, but now we have added a counter that will eventually serve to keep track of the current program line number.
Use the Constant, Argument, ALU Function, Write RAM, Load Accum, Reset, Jump, and Clock inputs to perform the following sequence of operations.
Tick the clock until the program counter (PC) reaches 9.
Reset the program counter back to 0.
Tick the clock until the program counter reaches 9 again.
With a single tick of the clock, set the program counter to 3. Which inputs do you need to set in order to accomplish this?
Exercise 9: ROM
Now we'll look at ROM, which stands for Read Only Memory. A ROM is basically a truth table. The input is an address, and the output is the value at that address, just like for RAM. Unlike RAM, a ROM typically retains its data when the power is turned off, and its data values cannot (easily) be changed.
Open the ROM project in CircuitVerse.
This particular ROM stores an 8-bit value at each of 16 addresses. Each of the 16 boxes you see inside the ROM element represents the value stored at one of those 16 addresses.
Use the ROM to perform the following tasks.
Change the address to 1, 2, 3, 4, etc., and you should see the dark green cursor highlight the appropriate box in dark green.
Set the address to 3. How does the binary output value correspond to the value in the highlighted box? (Hint: Look at the first 4 bits separately from the last 4 bits.)
Set the address to 7. How does the binary output value correspond to the value in the highlighted box?
Set the address to 5. What is the value of the hexadecimal digit a in binary?
Set the address to 15. What is the value of the hexadecimal digit e in binary?
Try clicking on a box and literally typing in a value. Yes, you can type to change the values in the boxes. Really.
Can you type a value in a box that will make the output 11111111?
Exercise 10: Control ROM
The machine language executed by our CHUMP processor is called Chumpanese. We are now ready to set up our circuit to execute Chumpanese instructions. Here are the instructions we need to support.
OpCode Instruction Summary
0000 LOAD const accum = const; pc++;
0001 LOAD IT accum = ram[addr]; pc++;
0010 ADD const accum += const; pc++;
0011 ADD IT accum += ram[addr]; pc++;
0100 SUBTRACT const accum -= const; pc++;
0101 SUBTRACT IT accum -= ram[addr]; pc++;
0110 STORETO const ram[const] = accum; pc++;
0111 STORETO IT ram[ram[addr]] = accum; pc++;
1000 READ const addr = const; pc++;
1001 READ IT addr = ram[addr]; pc++;
1010 GOTO const pc = const;
1011 GOTO IT pc = ram[addr];
1100 IFZERO const if (accum == 0) pc = const; else pc++;
1101 IFZERO IT if (accum == 0) pc = ram[addr]; else pc++;
Up until now, we've used the Argument, ALU Function, Load Accum, Write RAM, and Jump inputs to set up our datapath to perform the operation we want. For example, when we want to load the constant into the accumulator, we set Argument to 0 (to select the constant), ALU Function to 011 (to output B), Load Accum to 1 (to overwrite the accumulator), Write RAM to 0 (so that we don't write to RAM), and Jump to 0 (so that we go on to the next line number, intead of jumping). These input values are summarized in the table below.
Instruction Arg ALU Acc RAM Jmp
LOAD const 0 011 1 0 0
But we shouldn't need to use 7 bits just to choose one of 14 Chumpanese operations. We will therefore use a 4-bit OpCode (operation code) to choose which of the 14 instructions to execute. You can see from the table of Chumpanese instructions that the OpCode for the LOAD const instruction is 0000.
Now, we need a circuit that takes in a 4-bit OpCode as input, and outputs the 7 bits that control the circuit for that particular instruction. We could accomplish this with a mess of combinational logic. But, instead, we're going to use a ROM. The OpCode will act as the 4-bit address to the ROM, and the ROM's output will control the datapath. The ROM we'll be using stores 8 bits at each address. Since we only need 7 bits to control the datapath, we'll insert a 0 between the ALU Function and Load Accumulator values as follows.
OpCode Instruction Arg ALU Acc RAM Jmp Binary Hex
0000 LOAD const 0 011 0 1 0 0 00110100 34
In summary, we need 7 bits to control the datapath to execute a LOAD const instruction. We inserted a 0 to arrive at the 8-bit value 00110100, which is 34 in hexadecimal. Since the OpCode for the LOAD const instruction is 0000, we stored 34 at address 0000 in the ROM.
Open the Control ROM project in CircuitVerse.
Notice that 34 has already been stored at address 0000 in the ROM. Your job is to fill in the rest of the ROM values, so that each 4-bit OpCode results in the correct behavior for that instruction.
Make a spreadsheet to keep track of the values corresponding to each OpCode.
You may use THIS SPREADSHEET PROVIDED TO YOU.
It is recommended that you work through these in order, starting by storing the hexadecimal value for the LOAD IT command at address 0001. (Hint: It should be exactly the same as the LOAD const instruction, except for the Arg value.)
Make sure to keep a list of your final hexadecimal values, because you will need to re-enter them in the next part of the lab.
Exercise 11: Program ROM
Open the Program ROM project in CircuitVerse.
Click the Fork link to save your own copy of this circuit. (You will need to be logged in to do this.)
This is the complete CHUMP. Notice there are now two ROMs.
First, fill in the Control ROM with the hexadecimal codes you worked out in the previous exercise.
Each program instruction has two parts: a 4-bit OpCode and a 4-bit constant. Together, there are 8 bits in each instruction. For example, the OpCode for ADD const is 0010, and therefore the ADD 3 instruction is represented as 00100011. Likewise, the OpCode for ADD IT is 0011, and therefore a complete ADD IT instruction is represented as 00110000. The constant part of an "IT" instruction is ignored, so we simply use 0000. A CHUMP program is entered in the Program ROM using hexadecimal codes. Thus, an ADD 3 instruction is entered as 23, and an ADD IT instruction is entered as 30.
Consider the following simple Java program.
max = 10;
count = 0;
while (count != max)
count++;
We might write this program in Chumpanese as follows.
load 10
storeto max
load 0
storeto count
loop: read count
load it
read max
subtract it
ifzero end
read count
load it
add 1
storeto count
goto loop
end: goto end
Enter this program into the Program ROM. Then show your teacher that it works.
Notes on Terminology: Chumpanese is called an assembly language. When you translate assembly code into binary/hexadecimal, you are converting your program into machine code.
Additional Credit Suggestions
Enter a more useful/interesting program into the Program ROM and show your teacher that it works.
Invent a new instruction for one of the remaining OpCodes (1110 and 1111) to perform.
Improve upon the design of the CHUMP circuit. For example, can you make it run longer programs, or operate on larger numbers, or allow a user to input a number, or support new instructions like ifpos/ifneg.