CS61C Spring 2012

Lab 12: Advanced Logisim and Basic Pipelining

Background

Goals

There are two parts to this lab. In the first part, you will be getting practice building more complicated things in logism. In the second part, you will gain some hands-on experience with pipelining.

Reading

P&H 4.5, 4.6
Refer to the Logisim Website or to lab 10 for a refresher on Logisim.

Setup

For part A, start all Logisim circuits from scratch. Feel free to do each exercise as separate sub-circuits in the same Logisim file. For part B, you can find a starter circuit and data file in ~cs61c/labs/12 or using the links provided in the problem description.

Part (A): Advanced Logisim

The following exercises will introduce you to more advanced techniques/concepts in Logisim.

Exercise A.1: Rotate Right

A helpful tool that you may have encountered briefly is the splitter. It is found in the Wiring folder. When you select it, you can use the toolbar on the bottom half of the right side to adjust a few parameters. The "Bit Width In" is the number of bits of the wire leading into it (bus width). The "Fan Out" is the number of branches. You can then set which bits are sent out to which part of the fan. For each bit you can go to the "Bit X" entry and edit which fan it comes out of or none. Lastly, you can edit which direction the fan faces. If it faces East it is often useful for splitting a wire into subwires, but if you flip it to face West it is good for concatenating subwires into one larger wire.

With your knowledge of splitters and your knowledge and experience with multiplexers from the last lab, you are ready to implement a non-trivial combinational logic block: rotr, which stands for "Rotate Right". The idea is that rotr A,B will "rotate" the bit pattern of input A to the right by B bits. So, if A were 0b1011010101110011 and B were 0b0101 (5 in decimal), the output of the block would be 0b1001110110101011. Notice that the rightmost 5 bits were rotated off the right end of the value and back onto the left end. In RTL, the operation would be something like "R = A >> B | A << (16 - B)".

You must implement a subcircuit named "rotr" with the following inputs:

The output should be A rotated right by B bit positions, as outlined above. You are NOT allowed to use Logisim shifters in your solution, though all other combinational logic (MUXes, constants, gates, adders, etc.) is allowed. Show off your rotr subcircuit in the main subcircuit.

Hint: Before you start wiring, you should think veeeery carefully about how you might decompose this problem into smaller ones and join them together. You should feel very free to use subcircuits when implementing rotr. If you don't, expect to regret it.

Tip: If your wiring from a large splitter is getting messy, sometimes chaining splitters can keep things more localized and cleaner. For example, a 1 to 16 split can be achieved by a fan out of 4 connected to 4 more splitters of fan out 4. (this goes for your homework, too)

Checkoff

Show your TA your rotr circuit and verify that it works.  

Exercise A.2: Fibonacci Numbers

So far we have built circuits that are either 1) purely combinational and require no clock or 2) have memory but run infinitely. In this exercise, we want to build a circuit that will compute the Nth Fibonacci number. As a quick review the Fibonacci numbers are defined by F0 = 0, F1 = 1, and Fi = Fi-1 + Fi-2.

  1. Start with just the infinite Fibonacci computation. Hopefully this should be pretty intuitive based on previous exercises that you have done before. How many registers do you need? What arithmetic blocks do you need? Where should the output be attached? Make sure you figure out a way to properly initialize the registers for the computation to work.
  2. Your sub-circuit will compute up to the 15th Fibonacci number and we will assume our input N > 0 is an unsigned number. How many input bits do you need and how many output bits do you need to represent the 15th Fibonacci number?
  3. Now to prevent the circuit from computing the Fibonacci numbers to infinity, we will make use of the Enable bit (lower left) on registers. When unset or pulled high, the registers will continue to load their inputs on the rising edge of the CLK. If Enabled is pulled low, then the registers will NOT load their inputs. We need to create a signal that will properly go low when we want the circuit to stop computing Fibonacci numbers.
  4. Create an additional part of the circuit that halts the original circuit after N computations. For this exercise you may find the following blocks useful: Counter (Memory), Comparator (Arithmetic), and Probe (Wiring). Make sure that it properly stops on the Nth Fibonacci number (watch for off-by-one errors) and that it actually stays there (run it for at least 20 clock cycles).
Your output should be in binary, but you can use a probe to display the value in decimal. Make sure you check the attributes of any counter and comparator you use.

Bonus: With proper register initialization, you can get this circuit fib15 to work properly for N > 0. But register initialization is annoying and must be repeated every time you reset your circuit. With a clever addition, you can get this fib15 to work for N >= 0 without the need for register initialization (Hint: it involves a MUX).

Checkoff

Show your TA your fib15 circuit and verify that for N = 15 after some time it will return Out = 610 indefinitely.  

Part (B): Pipelining

This next exercise will get you some hands-on practice with pipelining. Assume that on power-on, registers initially contain zeros.

Consider the following 2-input FSM. Its next state and output is computed by multiplying the inputs and adding it to the current state.

Say the propogation delay of a adder block is 50ns, the propogation delay of a multiplication block is 50 ns, and the clk-to-q delay of a register is 5ns. Calculate the maximum clock rate at which this circuit can operate. Assume that the register setup time is negligible, and that both inputs come from clocked registers that receive their data from an outside source.

Checkoff

Show your TA the calculations you performed to find the maximum clock rate (non-pipelined).  

We want to improve the performance of this circuit, and let it operate at a higher clock rate. To do so, we will divide up the multiplication and addition into two different pipeline stages; in the first pipeline stage, we will perform the multiplication of the two inputs. In the second pipeline stage, we will add the product to the state.

Our definition of "correctness" will be simple: we will consider the sequence of outputs from this circuit "correct" iff it corresponds to the sequence of outputs the non-pipelined version would emit, potentially with some leading zeros. For example, if for some sequence of inputs the non-pipelined version emits [3,5,1,2,4, ...], a correct circuit might emit the sequence of outputs [0,3,5,1,2,4, ...] for that same sequence of inputs.

For your convenience and to help standardize check-offs, we are providing a starting point in the files pipeline.circ and ROMdata (use the links or copy from ~cs61c/labs/12/). In pipeline.circ, the sub-circuit Non-pipelined is set up exactly as the figure above. The main circuit is set up to produce the output sequence [3,5,1,2,4,-1,0,0,...] on the non-pipelined version of this circuit. It is also a handy example of how to use memory from a file. The ROM block should be initialized to the proper data, but if it is zero-ed out, right-click it and choose "Load image..." and select ROMdata.

Note that we need a register to hold the intermediate value of the computation between pipeline stages. This is a general theme with pipelines.

  1. Complete the sub-circuit Pipelined. You will need to add a register to divide the multiplication and addition into separate pipeline stages.
  2. Calculate the maximum clock rate for the pipelined version of the circuit, and explain how the pipelining increases performance.

Checkoff

Show your TA the completed, pipelined circuit.  
Show your TA the calculations you performed to find the maximum clock rate (pipelined).