Skip to content

Lab 4: TinyRV1 Processor
Part A: Processor Components

Lab 4 will give you experience designing, implementing, testing, and prototyping a single-cycle processor microarchitecture and a specialized accelerator. The processor will implement the TinyRV1 instruction set. The instruction set manual is located here:

The lab will continue to leverage concepts from Topic 2: Combinational Logic, Topic 3: Boolean Algebra, Topic 4: Combinational Building Blocks, Topic 6: Sequential Logic, Topic 7: Finite-State Machines, and Topic 8: Sequential Building Blocks. The lab will also leverage concepts from Topic 9: Instruction Set Architecture and Topic 10: Single-Cycle Processors The lab will continue to provide opportunities to leverage the three key abstraction principles: modularity, hierarchy, and regularity.

The lab includes seven parts:

  • Part A: Processor Components

    • Due 11/6 @ 11:59pm via GitHub
    • Students should work on Part A before, during, and after your assigned lab section during the week of 11/3
    • Pre-lab survey on Canvas is (roughly) due by end of lab section during the week of 11/3
  • Part B: TinyRV1 Processor

    • Due 11/13 @ 11:59pm via GitHub
    • Students should work on Part B before, during, and after your assigned lab section during the week of 11/10
  • Part C: Accumulator Accelerator

    • Due 10/24 @ 11:59pm via GitHub
    • Students should plan to submit Part C before they leave for
    • Thanksgiving Break
  • Part D: FPGA Prototype v1

    • Due week of 11/17 during assigned lab section
    • This part will focus on prototyping the code developed in Part A+B
    • Even though completed with a partner, every student must turn in their own paper check-off sheet in their lab section!
  • Part E: FPGA Prototype v2

    • Due week of 12/1 during assigned lab section
    • This part will focus on prototyping the code developed in Part A+B+C
    • Even though completed with a partner, every student must turn in their own paper check-off sheet in their lab section!
  • Part F: TinyRV1 Assembly

    • Due 12/4 @ 11:59pm via GitHub
    • This part will include all of the assembly developed during Part D+E
  • Part G: Report

    • Due on 12/8 at 11:59pm for all groups!
    • Post-lab survey on Canvas is due at the same time as the report

All parts of Lab 4 must be done with a partner. You can confirm your partner on Canvas (Click on People, then Groups, then search for your name to find your lab group).

Both students must contribute to all parts!

It is not acceptable for one student to exclusively work on the code while the other student exclusively works on the report. It is not acceptable for one student to exclusively work on hardware design while the other student exclusively works on testing. Both students must contribute to all parts. Student understanding of Verilog design and testing will be assessed on the prelim exams, final exam, and Verilog coding exam. The instructors will also survey the Git commit log on GitHub to confirm that both students are contributing equally. If you are using pair programming, then both students must take turns using their own account so both students have representative Git commits. Students should create commits after finishing each step of the lab, so their contribution is clear in the Git commit log. A student's whose contribution is limited as represented by the Git commit log will receive a significant deduction to their lab score.

This handout assumes that you have read and understand the course tutorials and that you have attended the discussion sections. To get started, use VS Code to log into a specific ecelinux server, source the setup script, and use git pull to ensure you have any recent updates before working on your lab assignment.

 % source setup-ece2300.sh
 % cd ${HOME}/ece2300/groupXX
 % git pull
 % tree

where XX should be replaced with your group number. Go ahead and create a build directory, run configure to generate a Makefile, and run all of the tests.

% cd ${HOME}/ece2300/groupXX
% mkdir -p build
% cd build
% ../configure
% make check

Your repo contains the following files which are part of the automated build system:

  • Makefile.in: Makefile for the build system
  • configure: Configure script for the build system
  • configure.ac: Used to generate the configure script
  • scripts: Scripts used by the build system

The following table shows all of the hardware modules you will be developing in Lab 3.

Lab 4 requires implementing and verifying 22 hardware modules. While this might seem like a daunting task, many of these hardware modules require less than five lines of Verilog code. Some modules will not be unit tested and for other modules we provide the test cases.

This lab will almost exclusively use RTL modeling. RTL implementations can use the following Verilog constructs.

  • logic, assign
  • +, -, *
  • >>, <<
  • ==, !=, <, >, <=, >=
  • &&, ||, !
  • &, ~&, |, ~|, ^, ^~ (reduction operators)
  • ?: (ternary operator)
  • always_comb, always_ff @(posedge clk)
  • if, else if, endif
  • case, default, endcase

Note that some hardware modules have more specific restrictions; see the source comments for more details. Using unallowed Verilog constructs will result in significant penalties for code functionality and code quality. If you have any questions on what Verilog constructs can and cannot be used, please ask an instructor. There are no restrictions on Verilog constructs in test benches or interactive simulators.

1. Pre-Lab Survey

Take some time to meet with your partner to discuss the pre-lab survey which is on Canvas. The pre-lab survey includes questions on your learning outcomes, workload distribution, workload roadmap, communication, and collaboration. The survey is due (roughly) by the end of your assigned lab section the week of 11/6. Students with an early lab section on Monday might want to complete the pre-lab survey right after their lab section, while students with a late lab section on Wednesday should meet earlier in the week and complete the pre-lab survey then. A student will not receive a grade for the lab unless the pre-lab survey is completed.

2. Interface and Implementation Specification

This section describe the required interface (i.e., the ports for the module and the module's functional behavior) before describing the required implementation (i.e., what goes inside the module) for each hardware module. You will need to implement multiplexors, a register, four arithmetic units, immediate generation unit, a register file, edge detector, shift register, and synchronizer.

1.1. Multiplexors and Registers

Implement 2-to-1 and 4-to-1 32-bit multiplexors using RTL modeling. Be sure to properly propagate Xs to avoid X-optimism.

Implement a 32-bit register which supports reset and enable using RTL modeling. Be sure to properly propagate Xs to avoid X-optimism.

1.2. Arithmetic Units

Implement four arithmetic units.

  • Implement a 32-bit adder by instantiating 2 16-bit carry select adders from Lab 2. The 32-bit adder will be the only module implemented at the gate-level in your final processor implementation.

  • Implement a 32-bit equality comparator using RTL modeling.

  • Compose the adder, the equality comparator, and a 2-to-1 multiplexor to create a simple ALU. The ALU takes as op input port which specifies whether the ALU should do an add (op is zero) or an equality comparison (op is one).

  • Implement a 32-bit by 32-bit multiplier using RTL modeling.

1.3. Immediate Generation Unit

Implement an immediate generation unit suitable for use in generating immediates from TinyRV1 instructions. The immediate generation unit uses the following encoding for the imm_type input:

  • imm_type == 0: I-type (ADDI)
  • imm_type == 1: S-type (SW)
  • imm_type == 2: J-type (JAL)
  • imm_type == 3: B-type (BNE)

See the TinyRV1 ISA manual for more details.

1.4. Register File

Implement a register files with 32 32-bit registers. Reading register 0 should always return the value zero. Writing and reading the same register results in reading the old value. The register file provides two read ports and one write port.

1.5. Edge Detector

Implement a simple positive edge detector with a clk input, d input, and pos_edge output. The edge detector should output a 1 whenever the d input is zero on the previous cycle and one on the current cycle. Here is a trace showing the exact desired behavior:

d  pos_edge
0  x # output undefined on first cycle
1  1
0  0
1  1
1  0
1  0
1  0
0  0
0  0
1  1
0  0

The edge detector should use one DFF_RTL module from Lab 3. Since the edge detector does not have a reset, your test cases will need to ignore the output on the first cycle.

1.6. ShiftRegister_44b

Implement a 44-bit serial-in, parallel-out shift register with enable. Whenever en is one, the shift register should shift in a new least significant bit from the serial input (sin). The parallel output (pout) should be directly connected to the bits of the shift register. Here is a trace showing the exact desired behavior.

en sin pout
0  0   44'b0000_0000
1  1   44'b0000_0000
1  1   44'b0000_0001
1  0   44'b0000_0011
1  1   44'b0000_0110
1  0   44'b0000_1101
1  0   44'b0001_1010
1  1   44'b0011_0100
1  1   44'b0110_1001
0  0   44'b1101_0011  # en=0, do not shift in new input
0  0   44'b1101_0011  # en=0, do not shift in new input
0  0   44'b1101_0011  # en=0, do not shift in new input
0  0   44'b1101_0011  # en=0, do not shift in new input
1  1   44'b1101_0011
1  1   44'b1010_0111

The shift register should be implemented using a single always_ff and be reset to all zeros.

1.7. Synchronizer

Implement a synchronizer to avoid metastability on asynchronous external inputs. The synchronizer should use two DFF_RTL modules from Lab 3. Since the synchronizer does not have a reset, your test cases will need to ignore the output on the first two cycles.

3. Testing Strategy

You are responsible for developing an effective testing strategy to ensure all implementations are correct. Writing tests is one of the most important and challenging aspects of designing hardware. Hardware engineers often spend far more time implementing tests than they do implementing the actual hardware design.

3.1. Basic Testing

We will be using the same lightweight testing framework from the previous labs. For each hardware module, we provide a test bench for you to use along with one basic test case. You can run the basic tests for all hardware modules using the generated Makefile.

% cd ${HOME}/ece2300/groupXX/build
% make check-lab4-partA
% make check-lab4
% make check

You can also build and run a single test simulator.

% cd ${HOME}/ece2300/groupXX/build
% make Mux2_32b_RTL-test
% ./Mux2_32b_RTL-test

You can specify which specific test case to run on the command line and also dump waveforms that can be viewed using Surfer.

% cd ${HOME}/ece2300/groupXX/build
% make Mux2_32b_RTL-test
% ./Mux2_32b_RTL-test +test-case=1
% ./Mux2_32b_RTL-test +test-case=1 +dump-vcd=waves.vcd

3.2. Directed Testing

Remember, each directed test case should focus on testing a very specific kind of functionality and they should contain 2-10 checks. Be sure to add your tests cases to the list in the initial block and to check the output of the test simulator to confirm that your directed test cases are running and testing what you expect them to. Consider purposefully inserting a bug in your designs to see if your directed test cases will catch the bug.

3.3. Random Testing

Directed testing is useful for testing the known unknowns, but what about the unknown unknowns? How should we test for corner cases we have not even thought of yet? Random testing can help increase our testing coverage and increase our confident that our hardware design is functionally correct.

Random test cases should include a for loop. Each iteration should: (1) generate random input values; (2) use Verilog test code to programmatically determine the correct output values; and (3) use the check task to ensure the design-under-test produces the correct outputs give the corresponding random inputs.

3.4. X-Propagation Testing

You must also include one X-propagation test case for some modules. If nothing else, check that if all of the inputs are X then all of the outputs are X.

4. Lab Code Submission

To submit your code you simply push your code to GitHub. You can push your code as many times as you like before the deadline. Students are responsible for going to the GitHub website for your repository, browsing the source code, and confirming the code on GitHub is the code they want to submit is on GitHub Be sure to verify your code is passing your tests both on ecelinux and on GitHub Actions. Your design code will be assessed both in terms of code quality, verification quality, and functionality.

5.1. Code Quality

Your code quality score will be based on how well you follow the course coding conventions posted here:

5.2. Verification Quality

Verification quality is based on how well your testing enables making a compelling case for correctness. You will need to write compelling directed test cases, use reasonable randomg testing, and include a simple X-propgation test case. Use comments appropriately to describe your test cases.

5.3. Functionality

Your functionality score will be determined by running your code against a series of tests developed by the instructors to test its correctness. Note that we will be using the automated build system to test your final code submission as shown below.

% mkdir -p ${HOME}/ece2300
% cd ${HOME}/ece2300
% git clone git@github.com:cornell-ece2300/groupXX
% cd groupXX

% mkdir -p build
% cd build
% ../configure
% make check-lab4-partA