Project 1: RISC-V Assembly Language
UW–Madison ECE 552: Introduction to Computer Architecture · Spring 2026
Project Introduction
In this project phase, you will refresh your memory on assembly programming and gain exposure to the 32-bit RISC-V (RV32I) instruction set architecture (ISA) which we will use in this course. You are expected to have prior experience writing and debugging C and assembly languages at the level of ECE/CS 354 and to have familiarity with digital logic at the level of ECE/CS 252 for this assignment.
Solve each coding problem in the specified language and answer the free-response questions associated with each problem (provided near the end of this document). This assignment should be completed by yourself. You may discuss the problems with other students but must work on your own answers.
You may not use generative artificial intelligence (e.g. ChatGPT). We have reviewed the answers given by AI by copying and pasting the questions into various LLMs.
You are encouraged to use the Internet to answer the free response questions provided, but please do not share answers online.
This project will be worth 2.5% of your final course grade. All material is available in the Project 1 Repository on Gitlab. You can clone the repository locally or in a CSL machine. If you choose to use a local machine, you can either install the required packages or use the Docker image provided.
EDIT 1/28/26: The Docker image can be obtained by running the following commands:
cd path/to/project1/code # replace with the actual path to the project1 folder
docker run --rm -v $(pwd):/project1 -it coderkalyan/ece552-tools:latest bash
Problem 1: Unsigned Multiplication in RV32I Assembly
Using the RISC-V RV32I ISA reference sheet provided in class and on the Reference Materials page and Canvas, write an assembly routine to perform multiplication between two unsigned 32-bit numbers and output the lower 32 least significant bits of the product as a 32-bit unsigned result.
You are not allowed to use any of the RISC-V multiplication instructions (mul[h][s][u]), which are not present in RV32I and not described in the reference sheet. You may use any of the instructions that do appear in the reference sheet (e.g. add). Do not use any pseudo-instructions such as mv or ret. There is no maximum number of instructions you may use, but you are strongly encouraged to write the most compact (and fast) implementation you can.
Note: your implementation should not be linear in complexity with respect to the actual numbers provided. Therefore, an implementation that multiplies a × b by adding a to itself b times is not acceptable, as it is much too slow.
See the starter code provided below. This code can also be found in the Project 1 repository on GitLab. You can clone the repository and edit umul.s locally – the other files in the repository are for building and grading, and need not be modified.
## Starter Code
## Author: Your Name
## [Description]
## Multiplies two 32-bit *unsigned* numbers and provides a 32-bit *unsigned* result
## consisting of the lower 32 bits of the product.
##
## [Arguments]
## a0 = multiplicand
## a1 = multiplier
##
## [Returns]
## a0 = 32-bit product
.text
.global umul
umul:
# This dummy code adds the two operands and returns the result.
# Replace with your implementation.
add a0, a0, a1
jalr zero, 0(ra)
You can debug your code using a simulator such as Venus. Use “step” to execute assembly code one line at a time, or use “run” to run all lines of code at once. Some simulators use breakpoints to allow for more flexibility in debugging.
If you would like to test your code, you may do so locally, or on a CSL machine. First, make sure that you have cloned the Project 1 repository from GitLab in whichever environment you will be testing in. Use the provided Makefile to build and run the tests.
make test-umul
After cloning, if you are using a local machine, you can either install the required packages locally or use the Docker image provided. You can also use a CSL machine which should have the required packages. The commands necessary for using the Docker image are provided below:
$ cd path/to/project1/code # replace with the actual path to the project1 folder
$ docker run --rm -v $(pwd):/project1 -it coderkalyan/ece552-tools:latest bash
root@530481afd799:/# cd /project1
root@530481afd799:/project1 make test-umul
Answer the free response questions related to this problem (near the bottom of this page) in project1.txt.
Problem 2: Dot Product in C
Write C code for a function that takes in two arrays as inputs and returns their dot product as an integer. That is, given two input arrays A[N] and B[N], the program should compute output = sum(A[i] * B[i]) for i in [0, N). Your code should utilize the multiply routine you developed in problem 1; this can be achieved by calling the extern function provided in the starter code.
See the starter code below. This code is also provided in the Project 1 repository on GitLab.
#include <inttypes.h>
#include <stdio.h>
extern uint32_t umul(const uint32_t x, const uint32_t y);
// Performs a vector dot product between the 32 bit unsigned integer
// arrays A and B of length len. The arrays are guaranteed to be the
// same length (len items).
uint32_t dot(const uint32_t *const A, const uint32_t *const B, const size_t len)
{
// Replace with your implementation of dot product.
return 0;
}
The compiler toolchain in the Docker image provided is also capable of compiling and linking this C program with your previous assembly. Again, you can use a CSL machine or install the packages locally if you prefer. Try building and running the tests:
make test-dot
The steps to use the Docker image are the same as the previous problem:
$ cd path/to/project1/code # replace with the actual path to the project1 folder
$ docker run --rm -v $(pwd):/project1 -it coderkalyan/ece552-tools:latest bash
root@530481afd799:/# cd /project1
root@530481afd799:/project1 make test-dot
Answer the free response questions related to this problem (near the bottom of this page) in project1.txt.
Problem 3: Mystery Assembly Program
Read the following mystery routine implemented in RISC-V assembly and answer the following questions about what it does.
Try stepping through this code using a simulator such as Venus and passing in function arguments using register a0 (per the standard RISC-V calling convention) to store the first function argument and obtain the return value. The register ra is used for the return address.
The code is also available under project1/mystery.s on the git repo – if you want, you can compile it and try running it on different values. While the process is the same, we haven’t provided a make target for mystery – we encourage you to look at the file and run gcc appropriately inside the Docker image.
main:
addi t5, zero, 8
slli t5, t5, 2
sub t0, a0, t5
slli t1, t0, 2
add t1, t1, t0
addi t2, zero, 0
addi t3, t1, 0
addi t4, zero, 9
loop:
blt t3, t4, done
sub t3, t3, t4
addi t2, t2, 1
beq zero, zero, loop
done:
addi a0, t2, 0
jalr zero, 0(ra)
Answer the free response questions related to this problem (near the bottom of this page) in project1.txt.
Free Response Questions
Answer the following free-response questions in a text file named project1.txt. Use the template below to format your answers. Replace your_name with your actual name and replace answers[0], answers[1], ..., answers[N] with your actual answers to each question.
Submission Template
Author: your_name
[Q0]: answers[0]
[Q1]: answers[1]
...
[QN]: answers[N]
Problem 1
[Q0] Reflect: Is the result in a0 always the correct and complete product of the two 32-bit input numbers? Why or why not?
[Q1] Think: Can you think of any other arithmetic operations not listed on the reference sheet (on the Reference Materials page and Canvas) that could be implemented using RISC-V assembly code? Multiplication and division are not allowed as answers.
[Q2] Think: Multiplication can also be implemented in hardware by creating a mul instruction. What might be the benefits and costs of introducing a hardware multiplication instruction? Describe at least one benefit and one cost/downside.
[Q3] Research: Recall the IA-32 (32-bit x86) assembly language you learned in ECE 354 (Wikipedia). Describe 3 differences between RV32I and IA-32. Do not simply list terminology (like CISC or RISC) without describing what it means or providing an example.
Problem 2
[Q4] Think: Provide a brief description of the differences between a high-level language such as C and a low-level language such as RISC-V. In what situations would you use one or the other?
[Q5] Research: What software components allow the C code you write to be realized as machine instructions that can be executed by a processor? How might their design impact overall performance?
[Q6] Research: Adding a hardware multiplication instruction would improve performance for certain programs that utilize this heavily, such as dot product. What would be the effects, if any, of adding this optimization to a CPU on other programs that do not require multiplication as extensively?
Problem 3
[Q7] Reflect: What is this program doing (be specific as to the equation this implements)?
[Q8] Think: Explain how you would modify this assembly code to produce the opposite effect.
[Q9] Research: A simple arithmetic program such as this can still run faster by means of hardware improvement. Name one hardware improvement you could make that would make this particular program run faster.
Submission Instructions
Submit the following files to the appropriate Gradescope assignment:
| Filename | Points | Notes |
|---|---|---|
umul.s |
10 | See problem 1. |
dot.c |
10 | See problem 2. |
project1.txt |
20 | See template above. |
Filenames must match exactly. Please double check to ensure that you have submitted all of the required files.
This project will be worth 2.5% of your final course grade. The results of any manually graded content will be made available after the deadline.