Skip to content

CS 21 25.2 Laboratory Exercise 7

Pipelined Processor

Overview

You will be using Logisim to modify an existing pipelined RISC-V processor implementation to support additional functionalities.

General Instructions

For this laboratory activity, you are to work on one HOPELEx Checkpoint Item that will aid in your understanding of the RISC-V pipelined processor.

At the end of the HOPELEx Checkpoints are take-home exercises called TakeHOPE items. These are not graded and will not be submitted, but will help you prepare for the HOPE.

Logisim Template

Overview

A Logisim-Evolution template for a working RISC-V pipelined processor can be downloaded via the link in the previous section. This processor implementation supports the following instructions:

  • lw
  • sw
  • add
  • sub
  • and
  • or
  • slt
  • addi
  • beq
  • jal

Recommended zoom level

The given template is meant to be viewed at a zoom level of x0.9.

The bottom part of the template consists of the pipelined datapath and control while the top part provides a view of important values arranged by pipeline stage.

Basic execution

Before moving forward, select Simulate > Reset Simulation (or hit Ctrl/Cmd-r) to restart the simulation state.

Observe that PC4F (i.e., the PC + 4 value in the Fetch stage) is currently at 0x4 and that NextPC is also at 0x4. Ensure you are able to follow how the implementation is able to compute this.

Notice as well that PC4D, PC4E, PC4M, and PC4W are currently at 0x0.

Select Simulate > Manual Tick Half Cycle twice or Simulate > Manual Tick Full Cycle once (preferably using their keyboard shortcuts) to cause the next triggering edge to occur. Notice that the 0x4 value of PC4F has now moved to PC4E, and that PC4F is now 0x8.

Tick the clock once more to see the said values move one stage. Before moving forward, ensure that PC4F is 0xc, PC4D is 0x8, and PC4E is 0x4.

Manual hazard resolution

The template does not have any forwarding or hazard resolution implemented. To perform manual hazard resolution, the top-left corner of the template provides input pins for StallF, StallD, FlushD, and FlushE that can be manually set with the Poke tool.

Use the Poke tool to set StallF to 1, then tick the clock. Verify that PC4F is still 0xc, but PC4D is also 0xc. Ensure you understand why that is the case before proceeding.

Set StallF to 0, then tick the clock once; PC4F should now be 0x10 while PC4D should be 0xc. Then, set both StallF and FlushD to 1. Verify that PC4F and PC4D are unaffected, then tick the clock. What happened to PC4F and PC4D?

Set both StallF and FlushD to 0, then tick the clock thrice; PC4F should now be 0x1c while PC4D should be 0x18. Verify as well that PC4E is 0x14 and PC4M is 0x10.

Finally, think about what setting StallF, StallD, and FlushE to 1 would do, then verify your hypothesis by setting them to 1, ticking the clock, and checking the values of PC4F, PC4D, PC4E, PC4M, and PC4W.

HOPELEx Checkpoint

Checkpoint Task: Forwarding + Hazard Unit

Modify the given Logisim circuit such that:

  • A subcircuit named hazardunit that detects and resolves hazards is present
  • hazardunit is placed in the main circuit to serve as its hazard unit
  • The datapath accommodates forwarding as discussed in class

For your convenience, the complete hazard unit logic is as follows:

Output Logic
ForwardAE = 10 (priority) (Rs1E == RdM) & RegWriteM & (Rs1E != 0)
ForwardAE = 01 (Rs1E == RdW) & RegWriteW & (Rs1E != 0)
ForwardAE = 00 Otherwise
ForwardBE = 10 (priority) (Rs2E == RdM) & RegWriteM & (Rs2E != 0)
ForwardBE = 01 (Rs2E == RdW) & RegWriteW & (Rs2E != 0)
ForwardBE = 00 Otherwise
lwStall (not used outside the hazard unit) ResultSrcE_0 & ((Rs1D == RdE ) | (Rs2D == RdE))
StallF lwStall
StallD lwStall
FlushD PCSrcE
FlushE lwStall | PCSrcE

Flushing behavior implication

Note that the way FlushD and FlushE are computed implies that the processor employs static branch prediction with a branch-not-taken default.

The complete datapath forwarding logic is as follows:

Control signal Effect
ForwardAE == 00 RD1E is first ALU operand
ForwardAE == 01 ResultW is first ALU operand
ForwardAE == 10 ALUResultM is first ALU operand
ForwardBE == 00 RD2E is both WriteDataE value and port 0 input of ALUSrcE multiplexer
ForwardBE == 01 ResultW is both WriteDataE value port 0 input of ALUSrcE multiplexer
ForwardBE == 10 ALUResultM is both WriteDataE value port 0 input of ALUSrcE multiplexer

The complete pipelined processor diagram may be found below for your convenience:

RISC-V pipelined processor diagram

Ensure that:

  • All other instructions that were supported before by the template still work as intended
  • The input pins for StallF, StallD, FlushD, and FlushE in the top-right corner of the main circuit are deleted

You are encouraged to use tunnels and comparators.

Save the resulting circuit as lab07.circ.

Hint: Priority encoders

You may find priority encoders more useful in deriving an implementation of a conditional statement over a implementing with a truth table.

Demo program

Tip: Quick conversion shell script

Supposing the output of the Ripes assembly is in assembly.txt, the following command automatically transforms the said output into its Logisim-ready form in logisim.txt:

printf "v3.0 hex words\n%s" "$(cat assembly.txt | grep "^\s" | tr -s " " | cut -d " " -f 3)" > logisim.txt

You may test your work using the following RISC-V program and compare the resulting register values in the State tab after 37 clock cycles with those of Ripes:

main:
    addi s0, zero, 100    # s0 = 100

    addi t0, zero, 5      # t0 = 5
    addi t1, zero, 10     # t1 = 10
    add  t2, t0, t1       # t2 = 15
    add  t3, t2, t0       # t3 = 20 (forwarding)

    addi t4, zero, 7      # t4 = 7
    sw   t4, 0(s0)        # MEM[100] = 7
    lw   t5, 0(s0)        # t5 = 7
    addi t6, zero, 3
    add  t1, t5, t6       # t1 = 10 (forwarding)

    lw   t2, 0(s0)        # t2 = 7
    add  t3, t2, t0       # t3 = 12 (load-use stall)

    addi t0, zero, 1      # t0 = 1
    addi t1, zero, 2      # t1 = 2
    beq  t0, t1, skip1    # Not taken (forwarding; should not flush)

    addi t2, zero, 99     # t2 = 99

skip1:
    addi t0, zero, 3      # t0 = 3
    addi t1, zero, 3      # t1 = 3
    beq  t0, t1, taken1   # Taken (forwarding; should flush)

    addi t2, zero, 111    # Should be flushed

taken1:
    jal  t0, func         # t0 = PC + 4 (initial fetch should be flushed)
    addi t3, zero, 222    # Should be flushed

after:
    beq  zero, zero, end  # Taken (should not trigger forwarding)

func:
    addi t4, zero, 42     # t4 = 42
    jal  zero, after      # return (no ra needed)

end:
    beq zero, zero, end   # Infinite loop

TakeHOPE Problems

Item 1: 1-bit dynamic branch predictor

Create a 1-bit dynamic branch predictor with a branch-taken default.

Ensure that at least instruction addresses 0x0 to 0x78 have their own entry in the branch history table. You may use an 8-bit RAM component in Logisim for this.

Item 2: 2-bit dynamic branch predictor

Create a 2-bit dynamic branch predictor with a weakly branch-not-taken default.

Ensure that at least instruction addresses 0x0 to 0x78 have their own entry in the branch history table. You may use an 8-bit RAM component in Logisim for this.