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.


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.


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.


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.


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.


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.


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.


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.


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