Deadline: EOD Tuesday, February 26th
So far, we have been dealing with C program files (.c file extension), and have been using the gcc
compiler to execute these higher-level language programs. Now, we are learning about the RISC-V assembly language, which is a lower-level language much closer to machine code. For context, gcc
takes the C code we write, first compiles this down to assembly code (gcc uses a more complex assembly language than RISC-V), and then assembles this down to machine code/binary.
In this lab, we will deal with several RISC-V assembly program files, each of which have a .s file extension. To run these, we will need to use a RISC-V simulator. The simulator we will use was developed by Keyhan Vakil (now a CS161 TA) and improved by Stephan Kaminsky (one of your CS61C TAs). The simulator is called Venus and can be found online here.
main
function must be put first.ecall
with argument value 10. This signals for the program to exit. The ecall instructions are analogous to System Calls and allow us to do things such as print to the console or request chunks of memory from the heap.:
).#
).For the following exercises, please save your completed code in a file on your local machine. This is crucial for the checkoff portion to work.
Getting started:
lab3_ex1.s
into the editor, or click this magic linkRecord your answers to the following questions in a text file. Some of the questions will require you to run the RISC-V code using Venus’ simulator tab.
.data
, .word
, .text
directives mean (i.e. what do you use them for)? Hint: think about the 4 sections of memory.n
stored in memory? Hint: Look at the contents of the registers.At this point, make sure that you are comfortable with the following. Note that these will not be part of the lab checkoff, but are meant to benchmark how comfortable you are with the material in the exercise.
Open the files lab3_ex2_c.c
and lab3_ex2_assembly.s
. The assembly code provided (.s file) is a translation of the given C program into RISC-V. You can use this magic link to open it in Venus.
Find/explain the following components of the assembly file and put your answers in a text file.
k
.source
and dest
arrays.After you’ve answered explained the above components, edit lab3_ex2_assembly.s
so that it dest
satisfies the following conditions.
dest[i] = 2 * source[i]
for even i
dest[i] = 1
for odd i
Hint: This can be done by adding one line of code and modifying another (in other words, you only need to make 2 changes). Look at the initial values of dest; how does this help you implement this modification?
Verify that your changes work for the given source
and dest
arrays by running your code in a new Venus tab and check that the output looks like:
3 1 4 1 5 9
6 1 8 1 10 1
Make sure you are comfortable with the following.
In this exercise, you will be implementing a function factorial
in RISC-V that has a single integer parameter n
and returns n!
. A stub of this function can be found in the file factorial.s
. You can use this magic link to open it in Venus. You will only need to add instructions under the factorial
label, and the arguments that are passed into the function are defined at the top of the file. You may solve this problem using either recursion or iteration.
Implement factorial
and make sure that the program correctly outputs 3!
, 6!
, 7!
, and 8!
. In other words, the output should be
6 720 5040 40320
Make sure you are comfortable with the following.
map
This exercise uses the file list_map.s
. You can open it in Venus with this magic link
In this exercise, you will complete an implementation of map
on linked-lists in RISC-V. Our function will be simplified to mutate the list in-place, rather than creating and returning a new list with the modified values.
You will find it helpful to refer to the RISC-V green card to complete this exercise. If you encounter any instructions or pseudo-instructions you are unfamiliar with, use this as a resource.
Our map
procedure will take two parameters; the first parameter will be the address of the head node of a singly-linked list whose values are 32-bit integers. So, in C, the structure would be defined as:
struct node {
int value;
struct node *next;
};
Our second parameter will be the address of a function that takes one int as an argument and returns an int. We’ll use the jalr
RISC-V instruction to call this function on the list node values.
Our map
function will recursively go down the list, applying the function to each value of the list and storing the value returned in that corresponding node. In C, the function would be something like this:
void map(struct node *head, int (*f)(int))
{
if(!head) { return; }
head->value = f(head->value);
map(head->next,f);
}
If you haven’t seen the int (*f)(int)
kind of declaration before, don’t worry too much about it. Basically it means that f
is a pointer to a function, which, in C, can then be used exactly like any other function.
There are exactly nine (9) comments (8 in map
and 1 in main
) in the provided code where it says YOUR CODE HERE
.
Complete the implementation of map
by filling out each of these nine markers with the appropriate code. For the lab, provide a sample call to map
with square
as the function argument. There are comments in the code that explain what should be accomplished at each marker. When you’ve filled in these instructions, running the code should provide you with the following output:
9 8 7 6 5 4 3 2 1 0
81 64 49 36 25 16 9 4 1 0
The first line is the original list, and the second line is the modified list after the mapped function (in this case square
) is applied.
At this point, you should be comfortable with the following.
Make sure your assembly code is saved to the corresponding files in your lab03
folder. Run make
in your lab03 folder on the Hive machine and show your TA/AI that it passes all 3 checks (you should see three Success!
lines in your output).