Skip to content

Lab 2: Two-Function Calculator
Part B: Multipliers and Calculator

Lab 2 will give you experience designing, implementing, testing, and prototyping more complicated combinational logic using the Verilog hardware description language. This lab will primarily leverage concepts from Topic 2: Combinational Logic, Topic 3: Boolean Algebra, and Topic 4: Combinational Building Blocks including experience with adders, multiplexors, and multipliers. This lab will also reinforce three key abstraction principles: modularity, hierarchy, and regularity.

You will be implementing a two-function calculator that takes as input two binary values and then calculates either the sum or the product of these two values. The input values and the result will be displayed on seven-segment displays using your Verilog hardware design from Lab 1. Your implementation will mostly use gate-level modeling, but you will also start to explore very simple register-transfer-level modeling. Parts of the calculator will be used in future labs. The lab includes four parts:

  • Part A: Adders and Muxes

    • Due 9/25 @ 11:59pm via GitHub
    • Students should work on Part A before, during, and after your assigned lab section during the week of 9/22
  • Part B: Multipliers and Calculator

    • Due 10/2 @ 11:59pm via GitHub
    • Plan to start on Part B during the week of 9/22
    • Even though Part B is due on 10/2 you still need the code ready to go before your lab section the week of 9/29!
  • Part C: FPGA Prototype

    • Due week of 9/29 during assigned lab section
    • Even though completed with a partner, every student must turn in their own paper check-off sheet in their lab section!
  • Part D: Report

    • Due week of 9/29, three days after lab section @ 11:59pm via Canvas
    • Post-lab survey on Canvas is due at the same time as the report

All parts of Lab 2 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. This handout assumed you have successfully completed Part A. You should have already cloned your individual remote repository, so use git pull to ensure you have any recent updates before working on your lab assignment.

% cd ${HOME}/ece2300/groupXX
% git pull
% tree

where XX should be replaced with your group number. Students must try to get Part B working before their lab section the week of 9/29. If you have to finish Part B at the beginning of your lab section the week of 9/29 then you will likely not be able to finish Part C.

Your repo includes the following files in the lab2 subdirectory for Part B. These files are for modeling real hardware using the synthesizable subset of Verilog.

  • Multiplier_1x16b_GL.v: 1-bit by 16-bit multplier
  • Multiplier_2x16b_GL.v: 2-bit by 16-bit multplier
  • Multiplier_2x16b_RTL.v: 2-bit by 16-bit register-transfer-level multiplier
  • Calculator_GL.v: Two-function calculator supporting addition and multiplication

The lab2/test subdirectory includes the following test libraries and test benches for Part B.

  • Multiplier_1x16b_GL-test.v: Tests for 1-bit by 16-bit multplier
  • Multiplier_2x16b_GL-test.v: Tests for 2-bit by 16-bit multplier
  • Multiplier_2x16b_RTL-test.v: Tests for 2-bit by 16-bit register-transfer-level multiplier
  • Calculator_GL-test.v: Tests for two-function calculator

Your repo includes the following file in the lab1/sim subdirectory. This file is for an interactive simulator for final testing of your hardware in a similar context to what you will be doing in Part C on the actual hardware in the lab.

  • caculator-sim.v: Interactive simulator for two-function calculator

Part B is divided into four steps.

  • Step 1. Implement and test Multiplier_1x16b_GL
  • Step 2. Implement and test Multiplier_2x16b_GL
  • Step 3. Implement and test Multiplier_2x16b_RTL
  • Step 4. Implement and test Calculator_GL

Consider having one partner work on the hardware implementation while the other partner works on the test cases in parallel for one module; then switch roles for the next module.

1. Interface and Implementation Specification

In Part B, you will be using the components you implemented and tested in Part A to implement a simple multiplier and ultimately a two-function calculator that takes as input two binary values and then calculates either the sum or the product of these two values. 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 combinational building block.

1.1. One-bit by 16-Bit Multiplier

A one-bit by 16-bit multiplier multiplies a 16-bit input value by a one-bit input value to determine a 16-bit product output. Review the lecture notes for more on multipliers and then implement this simple multiplier in Multiplier_1x16b_GL. Use explicit gate-level modeling.

1.2. Two-bit by 16-Bit Multiplier

A two-bit by 16-bit multiplier multiplies a 16-bit input value by a two-bit input value to determine an 16-bit product output.

We use two one-bit by 16-bit multipliers to create the two partial products, and then we use an 16-bit adder to sum these two partial products to get the final result. If the output overflows then the implementation should truncate by using the lower 16 bits of the product.

Review the lecture nodes for more on multipliers and then implement this multiplier in Multiplier_2x16b_GL by instantiating two Multiplier_1x16b_GL modules and one AdderCarrySelect_16b_GL and correctly connecting all of the ports. You will need to use the include Verilog preprocessor macro to include the appropriate child modules. You will likely need some internal wires. Note that you may also have some unused signals (i.e., the carry output from the adder and the most significant bit of the sum). These unused signals will cause verilator linting errors. You can use the ECE2300_UNUSED Verilog preprocessor macro to avoid unused signal errors.

1.3. Two-bit by 16-Bit Register-Transfer-Level Multiplier

Similar to our 16-bit RTL adder, we can also implement a two-bit by 16-bit RTL multplier. Instead of implementing the multiplier by instantiating various hardware modules, we can use RTL modeling through the * operator to implement a 16-bit multiplier in a single line of Verilog. Implement such a two-bit by 16-bit RTL multiplier in Multiplier_16b_RTL.

1.4. Two-Function Calculator

We can now put our adder and multiplier together using a 16-bit two-to-one mux to create a two-function calculator. The calculator has an op input to select which operation we want to perform. If op is zero then we perform addition; if op is one then we perform multiplication. Note that you will need to connect just the least-significant two bits of input in1 to the multiplier's input in1.

Implement the calculator in Calculator_GL.v by instantiating an AdderCarrySelect_16b_GL module, an Multiplier_2x16b_GL module, and a Mux2_16b_GL module and then correctly connecting all of the ports. You will likely need some internal wires. You will need to use the include Verilog preprocessor macro to include the appropriate child modules. You may also need to use an extra Verilog snippet to avoid unused signal errors.

2. Testing Strategy

You will use a similar strategy as in Part A to ensure all of your implementations are correct. You will need to use a combination of basic, directed, and random testing to make a compelling case for correctness for Part B. Be sure to carefully test for situations where the output of the multiplication needs to be truncated.

2.1. Simulator

We have provided you a simple calculator simulator which will emulate what you will prototype during Part C. After finishing your implementation for the complete two-function calculator, you can build and run the calculator simulator like this:

% cd ${HOME}/ece2300/groupXX/lab2-calc/build
% make calculator-sim
% ./calculator-sim +in0-switches=00100 +in1-switches=00011 +button=0
% ./calculator-sim +in0-switches=00100 +in1-switches=00011 +button=1

The switches are connected to the inputs of the calculator, and the button is connected to the op input of the calculator. The calculator simulator will show what the six seven segment displays would look like on the FPGA prototype.

3. 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 their repository, browsing the source code, and confirming the code on GitHub is the code they want to submit. 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.

3.1. Part A Revisions

If your design is failing some tests from Part A, then you should fix whatever is wrong. If your design is passing your tests but failing our tests you should browse the staff tests to see how to improve your own testing. Fixing whatever is wrong now can improve your code functionality score and also ensures that you can use your work from this lab in future labs.

To revise your submission, simply push any updates to your tests and/or your hardware designs to GiHub just like normal. You do not need to switch branches; just push your changes to the main branch like normal. Make sure your updates are passing all of your (potentially updated) tests on the main branch on GitHub Actions. After pushing your updated code, you must enter a comment on the grading pull request page that explains what was wrong and how you fixed it. The instructors will then take care of merging your changes into the grading pull request. You should not rely on the instructor merging your revision into the pull request to see if your code passes the staff tests; you should copy whatever staff tests are important into your own test benches. The only way we can increase your score after a revision is if you clearly explain what was wrong and how you fixed it. If you fix your code on the main branch and do not explain it in the grading pull request then you will not receive a revised score!

All Part A revisions need to be finalized by the due date for Part B. No revisions are allowed for Part B code.

3.2. Code and Verification Quality

Code and verification quality will be assessed in the same way as for Part A.

3.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-lab2-partA
% make check-lab2-partB