Skip to content

Lab 4: TinyRV1 Processor
Part D: FPGA Prototype V1

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: Accumulate Accelerator

    • Due 11/25 @ 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

This handout assumes that you have read and understand the course tutorials and that you have attended the discussion sections. This handout assumes you have successfully completed Parts A and B, meaning your processor, memory bus, and SPI are all working in simulation.

What do we do if Parts A and B is not done?

If Parts A and B are not done then you cannot get started on Part D. You must focus on getting your processor, memory bus, and SPI working before you can complete anything in Part D.

Here are the steps to get started:

  • Step 1. Find your lab partner
  • Step 2. Find a free workstation
  • Step 3. Ask the TAs for a lab check-off sheet (each student needs their own check-off sheet)

For each lab report task you must take some notes, save a screenshot, and/or record some data for your lab report. The lab report is not due until the last day of classes.

For each lab check-off task you must raise your hand and have a TA come to check-off your work. The TA will ask you the questions included as part of the lab check-off task and the assess your understanding using the following rubric: mastery; accomplished; emerging; beginning. If the TA and students together feel the students have not mastered the lab check-off task, the students are encouraged to take a few minutes and try again.

What do we do if the TAs are busy?

Students must maintain a sense of urgency throughout the three hour lab section. When you get to a lab check-off task, raise your hand for 2-3 minutes. If no TA is available you must keep going; if nothing else read ahead and make a plan for the next section of the lab handout. If you do nothing and wait 20-30mins with your hand raised for a TA then you will not be able to finish all of Part C. You must be strategic. Keep moving through the handout and bring over a TA when they are free to possibly look at multiple lab check-off tasks. If you are truly stuck and no TA is available, ask your neighbors. We need to all work together to make sure every student can complete Part D. There are no extensions and students cannot complete Part D at any other time except during their assigned lab section.

Lab Check-Off Task 1: Setup Lab Kit

The TAs will pass out an ECE 2300 Lab Kit to each group. The TAs will record the kit number on your check-off sheet. For this lab, you will receive the FPGA board, a USB-B cable, a USB-C cable, and a component box with some jumper wires and a USB flash drive. Use the USB-B cable to plug the FPGA board into the workstation.

1. Simulate the Single-Cycle TinyRV1 Processor

Before starting to work on an FPGA prototype, you must make sure you have a working Verilog hardware design that has been thoroughly tested in simulation. We will be both running test simulations and also using two different interactive simulators to ensure our hardware design is fully functional. One student should start VS Code on the workstation, log into the ecelinux servers, source the setup script, and make sure their group repository is up to date.

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

Where XX is your group number.

1.1. Running Tests

Now run all of the tests from a clean build to ensure your design is fully functional.

% cd ${HOME}/ece2300/groupXX
% trash build
% mkdir build
% cd build
% ../configure
% make check-lab1
% make check-lab2
% make check-lab3
% make check-lab4-partA
% make check-lab4-partB

Lab Check-Off Task 2: Verify Tests

Show a TA that your hardware designs are passing all of your tests. The TA will ask the students to explain their testing strategy for two different instructions. Students should show the TA their tests and explain how these tests ensure correct functionality of the TinyRV1 processor.

1.1. Running Interactive ISA Simulator

We now want to assemble a test program and run it on the ISA simulator. Start by taking a look at the following very simple assembly test program that we provide you.

% cd ${HOME}/ece2300/groupXX/build
% code ../lab4/asm/test1.asm

This assembly test program simply adds 3 and 4 and displays the result on out0. You can use tinyrv1-assemble to translate this assembly program into a machine program like this:

% cd ${HOME}/ece2300/groupXX/build
% ../scripts/tinyrv1-assemble -o test1.bin ../lab4/asm/test1.asm
% cat test1.bin

The build system can also do this for us like this:

% cd ${HOME}/ece2300/groupXX/build
% make test1.bin

Now let's build the ISA simulator and then execute this test program.

% cd ${HOME}/ece2300/groupXX/build
% make proc-isa-sim
% ./proc-isa-sim +bin=test1.bin

You should be able to see that out0 is 7. You can use the +step command line option to single step through your test program.

% cd ${HOME}/ece2300/groupXX/build
% make proc-isa-sim
% ./proc-isa-sim +bin=test1.bin +step

Step through several instructions then enter r and press enter to finish the executing the test program without stepping. You can use the +tui command line option to see the program, PC, registers, physical memory, and memory mapped I/O.

% cd ${HOME}/ece2300/groupXX/build
% make proc-isa-sim
% ./proc-isa-sim +bin=test1.bin +tui

Here are the possible commands you can use in TUI mode.

  • Enter executes the current instruction
  • /quit quits the simulator
  • /run finishes executing the program without stepping
  • /N runs simulation for N cycles (where N is a decimal number)
  • /inN=M sets input N to value M where M can be a decimal number (i.e., 10), hex number (i.e., 0xa), or binary number (0b1010)

Verify that out0 is being set to 7. There is also a +tui-tall mode that shows more instructions, registers, and memory.

% cd ${HOME}/ece2300/groupXX/build
% ./proc-isa-sim +bin=test1.bin +tui-tall

Now let's experiment with a second simple assembly test program.

% cd ${HOME}/ece2300/groupXX/build
% code ../lab4/asm/test2.asm

This assembly test program reads in0, in1, and in2 and writes each one to out0, out1, and out2 respectively. Let's make the corresponding machine program and run it on the ISA simulator.

% cd ${HOME}/ece2300/groupXX/build
% make test2.bin
% ./proc-isa-sim +bin=test2.bin +in0=0x10 +in1=0x11 +in2=0x12

Confirm that out0 is 0x10, out1 is 0x11, and out2 is 0x12. Try executing this same machine program using both the +step and +tui mode.

% cd ${HOME}/ece2300/groupXX/build
% ./proc-isa-sim +bin=test2.bin +in0=0x10 +in1=0x11 +in2=0x12 +step
% ./proc-isa-sim +bin=test2.bin +in0=0x10 +in1=0x11 +in2=0x12 +tui

In TUI mode step through the program for a few cycles then try changing one of the inputs and run the simulation for 20 cycles like this:

(tui) /in0=0x13
(tui) /20

Verify that out0 changes to 0x13. We have one final assembly test program that tests in3 and out3.

% cd ${HOME}/ece2300/groupXX/build
% make test3.bin
% ./proc-isa-sim +bin=test3.bin +in3=0x03

1.3. Running Interactive Single-Cycle Processor Simulator

Now we are ready to run these three assembly tests programs on your TinyRV1 single-cycle processor integrated with your memory bus. Build the interactive single-cycle processor simulator like this:

% cd ${HOME}/ece2300/groupXX/build
% make proc-scycle-sim

What do we do if we get a 'Can't find definition of m' error?

You may see an error like this:

%Error: ../lab4/sim/proc-scycle-sim.v:226:68:
  Can't find definition of 'm' in dotted variable: 'proc.dpath.rf.m'

The interactive simulator needs to poke into your design to display the value of reach register in your register file, but this means it needs to know the name of the 2D array of signals you are using to store the register file state. For example, you might have a single like this in your register file:

logic [31:0] rf [31];

You need to rename this signal to be m like this:

logic [31:0] m [31];

Then rerun all of your tests. After this change, you should be able to build the interactive single-cycle processor simulator.

Now run the first machine program on the interactive single-cycle processor simulator including using step and TUI modes.

% cd ${HOME}/ece2300/groupXX/build
% make test1.bin
% ./proc-scycle-sim +bin=test1.bin
% ./proc-scycle-sim +bin=test1.bin +step
% ./proc-scycle-sim +bin=test1.bin +tui

You can use +dump-vcd=waves.vcd to debug any issues using waveforms.

% cd ${HOME}/ece2300/groupXX/build
% ./proc-scycle-sim +bin=test1.bin +dump-vcd=waves.vcd

Now run the second machine program on the interactive single-cycle processor simulator including using step and TUI modes.

% cd ${HOME}/ece2300/groupXX/build
% make test2.bin
% ./proc-scycle-sim +bin=test2.bin +in0=0x10 +in1=0x11 +in2=0x12
% ./proc-scycle-sim +bin=test2.bin +in0=0x10 +in1=0x11 +in2=0x12 +step
% ./proc-scycle-sim +bin=test2.bin +in0=0x10 +in1=0x11 +in2=0x12 +tui

Try changing the inputs in TUI mode and verify the outputs change.

% cd ${HOME}/ece2300/groupXX/build
% make test3.bin
% ./proc-scycle-sim +bin=test3.bin +in3=0x03
% ./proc-scycle-sim +bin=test3.bin +in3=0x03 +step
% ./proc-scycle-sim +bin=test3.bin +in3=0x03 +tui

Lab Check-Off Task 3: Verify Interactive Simulator

Show a TA your TinyRV1 single-cycle processor running test2.bin in TUI mode. The TA will ask you to change the inputs, simulate for some number of cycles, and verify that the outputs are changing to the desired values.

1.4. Copying Files to Workstation

We now need to get the files for your design from ecelinux onto the workstation. This requires multiple steps.

  • Step 1. Click Microsoft Edge on the desktop to open a web-browser on the workstation to log into GitHub and then find your repository

  • Step 2. Start PowerShell by clicking the Start menu then searching for Windows PowerShell

  • Step 3. Use the following command to change to your home directory on the workstation in the lab (where netid is your Cornell NetID)

% cd C:\Users\netid
  • Step 4. Clone your repo onto the workstation by using this command in PowerShell (where netid is your Cornell NetID, notice we are using https!):
% git clone https://github.com/cornell-ece2300/groupXX
  • Step 5. In the Connect to GitHub pop-up, click Sign in with your browser

  • Step 6. You may be asked for your GitHub username again and you may be asked to authorize the Git Credential Manager; click authorize git-ecosystem

  • Step 7. Verify that you have successfully cloned your repo by changing into your repo and using tree on the workstation:

% cd groupXX
% tree

2. Setup Quartus Project

Click Quartus (Quartus Prime 18.1) on the desktop to start Quartus. Important: Ensure that the Quartus Version is 18.1 and not 23.1. Then, click Run the Quartus Prime software. You might need to try starting Quartus twice. Setup a new Quartus project using the New Project Wizard:

  • Directory, Name, Top-Level Entity
    • You must enter the working directory as follows with your NetID!
    • Working directory: C:\Users\netid\lab4d
    • Name of this project: lab4d
    • Name of top-level design entity: lab4d
    • Click Next
  • Directory does not exist. Do you want to create it?
    • Click yes
  • Project Type
    • Choose Empty Project
    • Click Next
  • Add Files
    • Click User Libraries...
    • Click triple dots to the right of Project library name
    • Click on This PC, then navigate to your cloned repo by choosing Windows (C:) > Users > netid > groupXX where XX is your group number
    • Click Select Folder
    • Click Add
    • Click OK
    • Click triple dots to right of File name
    • Click on This PC, then navigate to your cloned repo by choosing Windows (C:) > Users > netid > groupXX > lab4 where XX is your group number
    • Shift-click on every Verilog hardware design file (do not include any files in the test or sim subdirectories)
    • Click Open
    • Click Next
  • Family, Device, and Board Settings
    • Click Board tab
    • Family: Cyclone V
    • Select DE0-CV Development Board
    • Make sure Create top-level design file is checked
    • Click Next
  • EDA Tool Settings
    • Click Next
  • Summary
    • Click Finish

As in previous labs, you must use the following steps to ensure Quartus knows your design includes RTL modeling:

  • Choose Assignments > Settings from the menu
  • Select the category Compiler Settings > Verilog HDL Input
  • Under Verilog version click SystemVerilog
  • Click OK

3. TinyRV1 Processor FPGA Prototype

We will now integrate your TinyRV1 single-cycle processor into a complete embedded system, synthesize the design, and configure the FPGA so we can run machine programs on the real prototype.

3.1. Integrate

We want to implement a TinyRV1 processor FPGA prototype with the following specification:

  • Input to clock divider is connected to the 50MHz clock on the FPGA board
  • rst (ACTIVE LOW!) is connected to the reset button on the board using a synchronizer
  • The left most push button will be the go button (see below)
  • The left five switches are in0
  • The right five switches are in1
  • The right three push buttons are the three least significant bits of in2 (ACTIVE LOW!)
  • The two seven-segment displays on the left are out0
  • The two seven-segment displays in the middle are out1
  • The two seven-segment displays on the right are out2
  • The left most LED shows the go bit
  • The right eight LEDs are for out3
  • The general-purpose pins will be used to connect to the SPI
    • SCLK connects to GPIO_1[1] which connects to D0 on USB-to-SPI adapter
    • MOSI connects to GPIO_1[3] which connects to D1 on USB-to-SPI adapter
    • MISO connects to GPIO_1[5] which connects to D2 on USB-to-SPI adapter
    • CS connects to GPIO_1[7] which connects to D3 on USB-to-SPI adapter

Here is a block diagam and annotated FPGA board and breadboard diagram illustrating the system we will be prototyping for Part D.

Executing a program on our TinyRV1 processor FPGA prototype involves the following four steps illustrated below.

  • Step 1: Press Reset Button -- We need to reset the system through a synchronizer to avoid metastability. This reset signal will reset all components in the system and will also initialize the physical memory with a simple default program. The "go" bit is reset to zero, and we connect the go bit through a NOT gate to the instruction and data memory wait inputs. This means after reset the processor will be waiting and will not execute any instructions.

  • Step 2: Load Program -- While the processor is waiting we can load a program into physical memory from the workstation. We will use a Python-based loader on the workstation which takes as input a .bin file and translates each line into a 44-bit SPI packet which then gets sent over the USB-C cable to the USB-to-SPI adapter on the breadboard. The USB-to-SPI adapter sends the SPI packets to the FPGA through four jumper wires which connect to four general-purpose I/O pins (one each for SCLK, MOSI, MISO, CS). We will also probe the SCLK and MOSI signals on the oscilloscope.

  • Step 3: Press Go Button -- Now that the program has been fully loaded into physical memory we can go ahead an tell the processor to start executing this program by pressing the go button. Notice how pressing the go button enables the DFFRE which is has its input hardwired to 1. So when we press the go button this DFFRE is enabled which then stores a 1. The DFFRE will continue to store a 1 until the system is reset.

  • Step 4: Program Starts Executing -- Now that the instructions and data memory wait signals are zero, the processor will fetch the first instruction at address zero and start executing the program.

Here is a template you can use for your top-level design.

//----------------------------------------------------------------------
// Clock Divider
//----------------------------------------------------------------------
// You need to connect the ports to the appropriate top-level ports.

logic clk;

ClockDiv_RTL clock_div
(
  .clk_in     (/* fill this in */),
  .divide_sel (1),
  .clk_out    (clk)
);

//----------------------------------------------------------------------
// Reset Synchronizer
//----------------------------------------------------------------------
// You need to connect the ports to the appropriate top-level ports.

logic rst;

Synchronizer_RTL reset_sync
(
  .clk (clk),
  .d   (/* fill this in */), // REMEMBER: Reset port is active low!
  .q   (rst)
);

//----------------------------------------------------------------------
// DFFRE for go bit
//----------------------------------------------------------------------
// You need to connect the ports to the appropriate top-level ports.

logic go;

DFFRE_RTL go_reg
(
  .clk (clk),
  .rst (rst),
  .d   (1'b1),
  .en  (/* fill this in */), // REMEMBER: Push buttons are active low!
  .q   (go)
);

assign /* fill this in */ = go;

//----------------------------------------------------------------------
// Processor
//----------------------------------------------------------------------

logic        imem_val;
logic        imem_wait;
logic [31:0] imem_addr;
logic [31:0] imem_rdata;

logic        dmem_val;
logic        dmem_wait;
logic        dmem_type;
logic [31:0] dmem_addr;
logic [31:0] dmem_wdata;
logic [31:0] dmem_rdata;

logic        trace_val;
logic [31:0] trace_addr;
logic [31:0] trace_data;
logic        trace_wen;
logic [4:0]  trace_wreg;
logic [31:0] trace_wdata;

ProcScycle proc
(
  .imem_wait (~go),
  .dmem_wait (~go),
  .*
);

//----------------------------------------------------------------------
// Memory Bus
//----------------------------------------------------------------------

logic        hmem_val;
logic [31:0] hmem_addr;
logic [31:0] hmem_wdata;

logic        mem0_val;
logic        mem0_wait;
logic        mem0_type;
logic [31:0] mem0_wdata;
logic [31:0] mem0_addr;
logic [31:0] mem0_rdata;

logic        mem1_val;
logic        mem1_type;
logic [31:0] mem1_addr;
logic [31:0] mem1_wdata;
logic [31:0] mem1_rdata;
logic        mem1_wait;

logic [31:0] in0, in1, in2, in3;
logic [31:0] out0, out1, out2, out3;

MemoryBus_RTL memory_bus
(
  .*
);

//----------------------------------------------------------------------
// Physical Memory
//----------------------------------------------------------------------

Memory physical_memory
(
  .*
);

//----------------------------------------------------------------------
// SPI
//----------------------------------------------------------------------
// You need to connect sclk, mosi, miso, and cs to the appropriate
// top-level ports.

logic sclk;
logic mosi;
logic miso;
logic cs;

assign sclk = /* fill this in */;
assign mosi = /* fill this in */;
assign miso = /* fill this in */;
assign cs   = /* fill this in */;

SPI_RTL minion
(
  .*
);

//----------------------------------------------------------------------
// External Inputs
//----------------------------------------------------------------------
// You need to connect the ports to the appropriate top-level ports.

assign in0 = { 27'b0, /* fill this in */ };
assign in1 = { 27'b0, /* fill this in */ };

// REMEMBER: The push buttons are active low!
assign in2 = { 29'b0, /* fill this in */ };

assign in3 = '0;

//----------------------------------------------------------------------
// External Outputs
//----------------------------------------------------------------------
// You need to connect the ports to the appropriate top-level ports.

DisplayOpt_GL display_out0
(
  .in       (out0[4:0]),
  .seg_tens (/* fill this in */),
  .seg_ones (/* fill this in */)
);

DisplayOpt_GL display_out1
(
  .in       (out1[4:0]),
  .seg_tens (/* fill this in */),
  .seg_ones (/* fill this in */)
);

DisplayOpt_GL display_out2
(
  .in       (out2[4:0]),
  .seg_tens (/* fill this in */),
  .seg_ones (/* fill this in */)
);

assign /* fill this in */ = out3[7:0];

You will need to also include the following modules.

`include "lab1/DisplayOpt_GL.v"
`include "lab3/DFFRE_RTL.v"
`include "lab3/DFF_RTL.v"
`include "lab3/ClockDiv_RTL.v"
`include "lab4/Synchronizer_RTL.v"
`include "lab4/ProcScycle.v"
`include "lab4/MemoryBus_RTL.v"
`include "lab4/SPI_RTL.v"
`include "lab4/Memory.v"

Use the following steps when you are ready to integrate the counter.

  • Double-click on DE0_CV_golden_top
  • Instantiate the template shown above
  • Fill in the connections to the top-level ports
  • Be sure to include all the required modules at the top
  • Choose File > Save from the menu

Then make sure to hook up the SPI interface on the breadboard as mentioned above.

  • SCLK connects to GPIO_1[1] which connects to D0 on USB-to-SPI adapter
  • MOSI connects to GPIO_1[3] which connects to D1 on USB-to-SPI adapter
  • MISO connects to GPIO_1[5] which connects to D2 on USB-to-SPI adapter
  • CS connects to GPIO_1[7] which connects to D3 on USB-to-SPI adapter

Consult the handout from Lab 2, Part C for more about using the breadboard. Connect the two oscilloscope probes to the SCLK and MOSI pins of the SPI interface.

Lab Check-Off Task 4: Discuss TinyRV1 Processor Integration

Show a TA your breadboard wiring and your top-level integration. Discuss how the connections provided in the template implement the specification. Confirm with the TA that you have correctly accounted for the fact that reset and all push-buttons are active low! Show how you have connected SCLK and MOSI to the oscilloscope. Take your time and double check all connections! Synthesizing the processor takes 10 minutes so get your connections right the first time!

3.2. Synthesize

You will need to update the timing constraint file as follows:

set_max_delay -from [all_inputs] -to [all_outputs] 20
set_min_delay -from [all_inputs] -to [all_outputs] 0

create_clock -period 20 [get_ports {CLOCK_50}]
create_clock -name clk -period 80 [get_nets {clock_div|counter_reg|q[0]}]

set_output_delay -add_delay -clock clk -max 0 [all_outputs]
set_output_delay -add_delay -clock clk -min 0 [all_outputs]

set_input_delay  -add_delay -clock clk -max 0 [all_inputs]
set_input_delay  -add_delay -clock clk -min 0 [all_inputs]

As in Lab 3, we must specify the clock constraints in two steps. First, we create specify that the top-level CLOCK_50 port has a clock period of 20ns. Then, we we must carefully constrain the output of the clock divider so the tools know that this is also a clock signal. We will be using a clock period of 80ns which should be sufficient for your processor to meet timing.

Now use the following steps to synthesize your design.

  • Choose Processing > Start Compilation from the menu
  • Wait ten minutes for synthesis to complete
  • While you are waiting go ahead and start working on your calculator assembly program in the next section!

How do I fix "can't open Verilog Design File" errors?

This probably means you did not setup the user library correctly, so Quartus cannot find the files you are including using the include Verilog preprocessor directive. You can use the following steps to fix this:

  • Choose Assignments > Settings from the menu
  • Select the category Libraries
  • Click triple dots to the right of Project library name
  • Click on This PC, then navigate to your cloned repo by choosing Windows (C:) > Users > netid > groupXX where XX is your group number
  • Click Select Folder
  • Click Add
  • Click OK
  • Choose Processing > Start Compilation from the menu to see if this fixes the issue

How do I fix "Verilog HDL syntax" errors?

This might be because you did not configure Quartus to use SystemVerilog! You can use the following steps to ensure Quartus knows your design includes RTL modeling:

  • Choose Assignments > Settings from the menu
  • Select the category Compiler Settings > Verilog HDL Input
  • Under Verilog version click SystemVerilog
  • Click OK

Now let's see how many gates our design is using.

  • Choose Processing -> Compilation Report from the menu
  • Under Table of Contents choose Fitter > Resource Section > Resource Usage Summary
  • Look through the report to determine the number of combinational ALUTs (configurable look-up tables) that are used for your design
  • Look through the report to determine the number of dedicated logic registers that are used for your design
  • Look through the report to determine what percentage of the total FPGA resources are being used (use the "Logic Utilization" row at the top of the area report)
  • Be sure to write down these area numbers

Let's also look at the area on the FPGA used by the processor FPGA prototype using the chip planner.

  • Chip Planner
    • Choose Tools > Chip Planner from the menu
    • Identify where the logic used to implement your design is located in the FPGA
    • Save a screenshot of the chip planner
    • Choose File > Close from the menu to close the chip planner

The final step is to analyze the timing (i.e., the critical path delay) of your processor. We will analyze timing for the Slow 1100mV 85C Model which is the default choice in the Timing Analyzer.

  • Choose Tools > Timing Analyzer from the menu
  • Double-click Update Timing Netlist
  • Choose Reports > Custom Reports > Report Timing from the menu
  • Report Timing
    • Clocks - From clock: clk
    • Clocks - To clock: clk
    • Targets - From: [get_registers *]
    • Targets - To: [get_registers *]
    • Report number of paths: 1
    • Enter the file name as critical-path.txt
    • Click Report Timing
  • Identify the propagation delay of the displayed path
  • Look at the actual critical path (i.e., Data Arrival Path) which shows the longest path from one of the inputs through your design to one of the outputs
  • Choose File > Close from the menu to close the timing analyzer

Lab Report Task 1: Save Data for TinyRV1 Processor

Save the area data, chip planner screenshot, and critical path report for your lab report.

Lab Check-Off Task 5: Discuss the TinyRV1 FPGA Prototype

Discusse the area, chip planner screenshot, and the critical path report with a TA. Explain why the number of logical registers is reasonble. Hint: Calculate the total number of bits in the 512B physical memory and the register file. Explain to the TA where your critical path goes using the TinyRV1 block diagrams.

3.3. Configure

Now we are finally ready to configure the FPGA for TinyRV1 single-cycle processor prototype.

  • Choose Tools > Programmer from the menu
  • Click Hardware Setup
  • Currently selected hardware: USB-Blaster [USB-0]
  • Click Close
  • Click Start

The provided physical memory has a default program which is automatically loaded into memory on reset. The default program has the following four instructions:

  addi x1, x0, 2
  addi x2, x1, 2
  sw   x2, 0x210(x0)

done:
  jal  x0, done

This program should just display 2+2=4 on out0. Go ahead and press reset then the go button (i.e., the left-most push button). You must press reset first before pressing the go button! You should see 4 displayed ont out0.

Now want to try loading in a new program from the workstation over SPI. You will need to use the USB-C cable to connect the SPI adapter on the breadboard to the top-left USB port of the workstation. You must use the top-left USB port of the workstation; any other port will not work!

Setup the oscilloscope so we can see the SPI packets being sent from the workstation to the TinyRV1 processor FPGA prototype. Revisit Lab 3, Part D for more on using the oscilloscope. Here are the steps we will need.

  • Turn on the oscilloscope
  • Press Default Setup
  • Press 2 to turn on Channel 2

Now use PowerShell on the workstation to assemble our test program and load it into the physical memory in our FPGA prototype over SPI. These commands should be done on the workstation not on ecelinux!

% cd C:\Users\netid\groupXX
% mkdir build
% cd build
% python ..\scripts\tinyrv1-assemble -o test1.bin ..\lab4\asm\test1.asm
% python ..\scripts\tinyrv1-load test1.bin

You should see the oscilloscope flicker. Using the following steps to get a screen shot of the SPI packets on the oscilloscope.

  • Rotate the Horizontal Scale knob counter clockwise to 20ms
  • Continue to load the program (i.e., press up arrow key in PowerShell and then enter to run tinyrv1-load again), see SPI packets flicker
  • Rotate the Vertical Position knob of channel 2 counter-clock wise
  • Continue to load program ... position channel 2 below channel 1 using Vertical Position knob
  • Rotate the Horizontal Position counter clockwise to move the signal to the left
  • Continue to load the program and adjust the vertical/horizontal position
  • Rotate the Trigger Level kbob clockwise until the trigger is about 1V higher than baseline voltage level
  • Press Single
  • Load program again to get a captured waveform
  • Use the USB Flash Drive to save the captured wavform for the report

Now that we are sure SPI packets are being sent to the processor prototype let's try out the simple test program.

  • Press reset on the FPGA
  • Load the program
  • Press the go button

If everything is working then you should see 7 on out0. Now experiment with the second test program. First assemble it into a machine program.

% cd C:\Users\netid\groupXX\build
% python ..\scripts\tinyrv1-assemble -o test2.bin ..\lab4\asm\test2.asm
% python ..\scripts\tinyrv1-load test2.bin

Make sure you reset the FPGA before loading the program! Then after loading the program press the go button to start executing the program.

With the second test program you should be able to change the switches and press the push buttons and see the result on the seven-segment displays.

Lab Report Task 2: Save Screenshots for SPI

Save a screenshot from the oscilloscope that clearly shows the SPI packets including both the SCLK and MOSI signals.

Lab Check-Off Task 6: Demonstrate the TinyRV1 Processor FPGA Prototype

Show a TA your SPI screenshots. Show the TA your prototype running the second test program. Experiment with different inputs and show that the outputs show the correct output.

4. TinyRV1 Calculator Assembly Program

In this part, we will implement a TinyRV1 assembly program that has the same behavior as the two-function calculator we implemented in Lab 2, and then we will extend the calculator to support subtraction. This illustrate the tremendous power of using a general-purpose processor; adding more functionality to specialized hardware requires adding more specialized hardware, while adding new functionality to a general-purpose processor simply requires adding more software.

We require taking an incremental approach:

  • Step 1: Implement and test calculator that supports addition
  • Step 2: Implement and test calculator that supports addition and multiplication
  • Step 3: Implement and test calculator that supports addition, multiplication, and subtraction

For each step, we will start by developing an assembly program on ecelinux and use the ISA and single-cycle processor simulator to verify its correctness before loading the program onto the FPGA prototype.

4.1. Step 1: Addition

Open up the calculator-step1.asm file on ecelinux using VS Code.

% cd ${HOME}/ece2300/groupXX/build
% code ../lab4/asm/calculator-step1.asm

Here is the pseudocode for step 1.

loop:

  # load inputs

  x1 = in0
  x2 = in1

  # display outputs

  out0 = x1
  out1 = x2

  # addition

  x4 = x1 + x2

  # done

  out2 = x4
  goto loop

Implement this pseudo-code in assembly in the calculator-step1.asm file. Do not use explicit target addresses for jal, jr, and branch! You must use labels for these control flow instructions! Then assemble it and verify it works on both the ISA simulator and your single-cycle processor.

% cd ${HOME}/ece2300/groupXX/build
% make calculator-step1.bin
% ./proc-isa-sim    +bin=calculator-step1.bin +in0=3 +in1=2
% ./proc-scycle-sim +bin=calculator-step1.bin +in0=3 +in1=2

Use git to commit your calculator assembly program and push to GitHub. Then pull the new program to the workstation by using PowerShell as follows:

% cd C:\Users\netid\groupXX\build
% git pull

Double check that your new assembly program has been correctly pulled onto the workstation!

% cd C:\Users\netid\groupXX\build
% cat ..\lab4\asm\calculator-step1.asm

Assemble step 1 into a machine program and load it onto the processor.

% cd C:\Users\netid\groupXX\build
% python ..\scripts\tinyrv1-assemble -o calculator-step1.bin ..\lab4\asm\calculator-step1.asm
% python ..\scripts\tinyrv1-load calculator-step1.bin

Remember to make sure the FPGA board is reset before you load the program. Then press the go button to start the program. Once your step 1 program is working then move on to step 2.

4.1. Step 2: Addition and Multiplication

Open up the calculator-step2.asm file on ecelinux using VS Code.

% cd ${HOME}/ece2300/groupXX/build
% code ../lab4/asm/calculator-step2.asm

Here is the pseudocode for step 2.

loop:

  # load inputs

  x1 = in0
  x2 = in1
  x3 = in2

  # display outputs

  out0 = x1
  out1 = x2

  # addition

  if ( x3 == 0 ):
    x4 = x1 + x2

  # multiplication

  else:
    x4 = x1 * x2

  # done

  out2 = x4
  goto loop

Implement this pseudo-code in assembly in the calculator-step2.asm file. Do not use explicit target addresses for jal, jr, and branch! You must use labels for these control flow instructions! Then assemble it and verify it works on both the ISA simulator and your single-cycle processor.

% cd ${HOME}/ece2300/groupXX/build
% make calculator-step2.bin
% ./proc-isa-sim    +bin=calculator-step2.bin +in0=3 +in1=2 +in2=0
% ./proc-isa-sim    +bin=calculator-step2.bin +in0=3 +in1=2 +in2=1
% ./proc-scycle-sim +bin=calculator-step2.bin +in0=3 +in1=2 +in2=0
% ./proc-scycle-sim +bin=calculator-step2.bin +in0=3 +in1=2 +in2=1

Use git to commit your calculator assembly program and push to GitHub. Then pull the new program to the workstation by using PowerShell as follows:

% cd C:\Users\netid\groupXX\build
% git pull

Double check that your new assembly program has been correctly pulled onto the workstation!

% cd C:\Users\netid\groupXX\build
% cat ..\lab4\asm\calculator-step2.asm

Assemble step 2 into a machine program and load it onto the processor.

% cd C:\Users\netid\groupXX\build
% python ..\scripts\tinyrv1-assemble -o calculator-step2.bin ..\lab4\asm\calculator-step2.asm
% python ..\scripts\tinyrv1-load calculator-step2.bin

Remember to make sure the FPGA board is reset before you load the program. Then press the go button to start the program. Once your step 2 program is working then move on to step 3.

4.1. Step 3: Addition, Multiplication, and Subtraction

Open up the calculator-step3.asm file on ecelinux using VS Code.

% cd ${HOME}/ece2300/groupXX/build
% code ../lab4/asm/calculator-step3.asm

Here is the pseudocode for step 3.

loop:

  # load inputs

  x1 = in0
  x2 = in1
  x3 = in2

  # display outputs

  out0 = x1
  out1 = x2

  # addition

  if ( x3 == 0 ):
    x4 = x1 + x2

  # multiplication

  elif ( x3 == 1 ):
    x4 = x1 * x2

  # subtraction

  else:
    x4 = x1 - x2

  # done

  out2 = x4
  goto loop

Implement this pseudo-code in assembly in the calculator-step3.asm file. Do not use explicit target addresses for jal, jr, and branch! You must use labels for these control flow instructions! Then assemble it and verify it works on both the ISA simulator and your single-cycle processor.

% cd ${HOME}/ece2300/groupXX/build
% make calculator-step3.bin
% ./proc-isa-sim    +bin=calculator-step3.bin +in0=3 +in1=2 +in2=0
% ./proc-isa-sim    +bin=calculator-step3.bin +in0=3 +in1=2 +in2=1
% ./proc-isa-sim    +bin=calculator-step3.bin +in0=3 +in1=2 +in2=2
% ./proc-scycle-sim +bin=calculator-step3.bin +in0=3 +in1=2 +in2=0
% ./proc-scycle-sim +bin=calculator-step3.bin +in0=3 +in1=2 +in2=1
% ./proc-scycle-sim +bin=calculator-step3.bin +in0=3 +in1=2 +in2=2

Use git to commit your calculator assembly program and push to GitHub. Then pull the new program to the workstation by using PowerShell as follows:

% cd C:\Users\netid\groupXX\build
% git pull

Double check that your new assembly program has been correctly pulled onto the workstation!

% cd C:\Users\netid\groupXX\build
% cat ..\lab4\asm\calculator-step3.asm

Assemble step 3 into a machine program and load it onto the processor.

% cd C:\Users\netid\groupXX\build
% python ..\scripts\tinyrv1-assemble -o calculator-step3.bin ..\lab4\asm\calculator-step3.asm
% python ..\scripts\tinyrv1-load calculator-step3.bin

Remember to make sure the FPGA board is reset before you load the program. Then press the go button to start the program.

Lab Check-Off Task 7: Demonstrate the TinyRV1 Calculator Assembly Program

Show a TA the final three-function calculator assembly program working in simulation on both the ISA simulator and the single-cycle RTL simulator. Show a TA your completed three-function calculator assembly program working on the FPGA prototype. Try several different input values and operations. Look through your past reports to compare the area of your TinyRV1 processor FPGA prototype to the final two-function calculator FPGA prototype from Lab 2. Discuss the trade-off between general-purpose hardware (i.e., the TinyRV1 processor FPGA prototype) and specalized hardware (i.e., the two-function calculator FPGA prototype) both in terms of area, delay, and flexibility.

Lab Check-Off Task 8: Turn in Lab Kit

When you are finished with your demo, pack up your ECE 2300 Lab Kit. Put the wires back and USB flash drive in the component box. Return the FPGA board, USB cables, and component box to a TA who will then record the kit number on your check-off sheet, initial the final check-off, and then collect your check-off sheet.

5. Optional Extension: Four-Function Function

This section is an optional extension to the main lab and is meant for students who have already completed all of the above tasks. Students can exploit the power of a truly programmable processor to add yet another function to their calculator.

  • Option 1: Add a power function which calculates in0 raised to in1. So if in0 is 2 and in1 is 3 then the result should be 8.

  • Option 2: Add a factorial function which calculates in0 factorial. Be careful though because factorials get large fast! You will only be able to caculate 4! without overflow.

Your calculator should use the fourth function when the third pushbutton is pressed.