Lab 3 教你如何使用 Venus 这个 RISC-V 执行模拟器。
Setup
In my cs61c directory, pull the files for this lab with:
$ git fetch starter main
$ git merge FETCH_HEAD
Exercise 1: Venus Basics
This exercise will walk you through the process of connecting your file system to Venus, which should save you a lot of trouble copy/pasting files between your local drive and the Venus editor.
Open venus.cs61c.org in your web browser (Chrome or Firefox are recommended).
Go back to the editor tab, and edit ex1_hello.s so that the output prints 2024.
- Hint: The value in
a1is printed whenecallexecutes. Whatecalldoes is out of scope for this class though.
My answer:
.text
addi a0 x0 1 # a0 = 0 + 1
addi a1 x0 2024 # a1 = 0 + 2024
# This prints out the integer stored in a1
ecall
# This exits the program
addi a0 x0 17
addi a1 x0 0
ecall
Exercise 2: Using the Venus Debugger
This exercise will ask you to write down your answers in ex2_answers.txt. The question numbers may be different from the step numbers, please be careful!
-
Open
fib.sin the Venus debugger using one of the two ways listed above.- Question 1: What is the machine code of the highlighted instruction? The answer should be a 32 bit hexadecimal number, with the
0xprefix.0b funct7 rs2 rs1 funct3 rd opcode0b 0000000 00000 00000 000 00101 01100110b 0000 0000 0000 0000 0000 0010 1011 00110x000002B3
- Question 2: What is the machine code of the instruction at address
0x34? The answer should be a 32 bit hexadecimal number, with the0xprefix.0x00000073
- Question 1: What is the machine code of the highlighted instruction? The answer should be a 32 bit hexadecimal number, with the
-
Click the "step" button to advance to the next instruction. The second instruction should now be highlighted.
-
Click the "prev" button to undo the last executed instruction. Note that undo may or may not undo operations performed by
ecall, such as exiting the program or printing to console. -
On the right side of the screen, click the "Registers" tab to view the values of all 32 registers. This tab may be already selected if it the title is highlighted in yellow. Make sure you on looking at the integer registers, not the floating point registers.
- Question 3: What is the value of the
spregister? The answer should be a 32 bit hexadecimal number, with the0xprefix.0x7FFFFFDC
- Question 3: What is the value of the
-
Continue stepping until the value in
t1changes.- Question 4: What is the new value of the
t1register? The answer should be a 32 bit hexadecimal number, with the0xprefix.0x00000001
- Question 5: What is the machine code of the current instruction? The answer should be a 32 bit hexadecimal number, with the
0xprefix.0x10000E17
- Question 4: What is the new value of the
-
Step until you are at address
0x10. At this point,t3's value has been updated.- Question 6: What is the value of the
t3register? The answer should be a 32 bit hexadecimal number, with the0xprefix.0x10000000
- Question 6: What is the value of the
-
If we look at the current instruction, we're loading from the
t3register. Use the "Memory" tab (next to the "Registers" tab), and input the answer of question 6 (the value oft3) into the "Address" box. You may need to scroll down on the memory tab before it is visible. Press "Go" to go to that memory address- Question 7: What is the byte that
t3points to? The answer should be an 8 bit (1 byte) hexadecimal number, with the0xprefix.0x0C
- Question 7: What is the byte that
-
Set a breakpoint at address
0x28by clicking the row at that address. The row should turn light red and a breakpoint symbol should appear. -
Continue until the breakpoint by pressing "Run".
- Question 8: What is the value of the
t0register? The answer should be a 32 bit hexadecimal number, with the0xprefix.0x00000001
- Question 8: What is the value of the
-
Continue 6 more times.
- Question 9: What is the new value of the
t0register? The answer should be a 32 bit hexadecimal number, with the0xprefix.0x0000000D- 0 1 1 2 3 5 8 13
- Question 9: What is the new value of the
-
Sometimes, reading hexadecimal values aren't very helpful. Set the display settings to "Decimal" by using the dropdown at the bottom of the register tab. This can also be done for the memory tab.
- Question 10: What is the value of the
t0register in decimal? The answer should be a decimal number without a prefix.13
- Question 10: What is the value of the
-
Click the instruction at address
0x28again to unset the breakpoint. -
Click run to finish running the program, since there are no more breakpoints.
- Question 11: What is the output of the program? The answer should be a decimal number without a prefix.
144
- Question 11: What is the output of the program? The answer should be a decimal number without a prefix.
Exercise 3: Using Memcheck
Similar to the previous exercise, this exercise will ask you to write down your answers in ex3_answers.txt. The question numbers may be different from the step numbers, please be careful!
-
Open
ex3_memcheck.sin the Venus editor and read through the entire program to get an idea of what it does. -
Run the program. Oh no, the program errors! Let's take a look at the error message.
- Question 1: What address did the program try to access, but caused the error? The answer should be a 32 bit hexadecimal number, with the
0xprefix.0x10008058
- Question 2: How many bytes was the program trying to access? The answer should be a decimal number without a prefix.
4
- Question 1: What address did the program try to access, but caused the error? The answer should be a 32 bit hexadecimal number, with the
-
This seems like a memory error, so let's give memcheck a shot. Enable memcheck (normal mode) and reopen
ex3_memcheck.sin VDB. -
Run the program. Look, a memcheck error with more details! Read the error carefully.
- Question 3: What address did the program try to access, but caused the error? The answer should be a 32 bit hexadecimal number, with the
0xprefix.0x10008080
- Question 4: How many bytes were allocated in the block related to the error? The answer should be a number without units.
40
- Question 5: Which line of the source file caused this error? The answer should be a number.
18
- Question 3: What address did the program try to access, but caused the error? The answer should be a 32 bit hexadecimal number, with the
-
Compare your answer to Question 2 and Question 4. Note that memcheck may change the memory address that
mallocreturns. -
Let's try to debug this error. Recall that
t1contains the loop counter.- Question 6: What is the value of
t1based on the memcheck error message? The answer should be a decimal number.10
- Question 6: What is the value of
-
Fix this error in the source code and save the file.
-
Run the program again. The process complete without any invalid access errors. However, it complains that there's some unfreed memory.
- Question 7: How many bytes were not freed when the program exited? The answer should be a decimal number without units.
40
- Question 7: How many bytes were not freed when the program exited? The answer should be a decimal number without units.
-
Rerun the program with memcheck in verbose mode. Remember to reopen the file in VDB.
- Question 8: What is the address of the block that was not freed? The answer should be a 32 bit hexadecimal number, with the
0xprefix.0x10008058
- Question 8: What is the address of the block that was not freed? The answer should be a 32 bit hexadecimal number, with the
-
Fix this error by calling
free. -
Disable memcheck for the next two exercises.
Exercise 4: Array Practice
Consider the discrete-valued function f defined on integers in the set {-3, -2, -1, 0, 1, 2, 3}. Here's the function definition:
f(-3) = 6
f(-2) = 61
f(-1) = 17
f(0) = -38
f(1) = 19
f(2) = 42
f(3) = 5
Implement the function in ex4_discrete_fn.s in RISC-V, with the condition that your code may NOT use any branch and/or jump instructions! Make sure that your code is saved locally. We have provided some hints in case you get stuck.
All output values are stored in the output array which is passed to f through register a1. You can index into that array to get the output corresponding to the input.
Make sure that you only write to the t and a registers. If you use other registers, strange things may happen (you'll learn about why soon).
My answer:
- ex4_discrete_fn.s
.globl f # this allows other files to find the function f
# f takes in two arguments:
# a0 is the value we want to evaluate f at
# a1 is the address of the "output" array (read the lab spec for more information).
# The return value should be stored in a0
f:
# Your code here
addi t0, a0, 3 # t0 = a0 + 3, a0 is the value we want to evaluate f at, t0 is the index of the array
slli t0, t0, 2 # t0 = t0 * 4, t0 is the offset of the element in the array
add t0, t0, a1 # t0 = t0 + a1, t0 is the address of the element in the array
lw a0, 0(t0) # a0 = *t0, a0 is the value of the element in the array
# This is how you return from a function. You'll learn more about this later.
# This should be the last line in your program.
jr ra
Exercise 5: Factorial
In this exercise, you will be implementing the factorial function in RISC-V. This function takes in a single integer parameter n and returns n!. A stub of this function can be found in the file ex5_factorial.s.
The argument that is passed into the function is located at the label n. You can modify n to test different factorials. To implement, you will need to add instructions under the factorial label. There is an recursive solution, but we recommend that you implement the iterative solution. You can assume that the factorial function will only be called on positive values with results that won't overflow a 32-bit two's complement integer.
At the start of the factorial call, the register a0 contains the number which we want to compute the factorial of. Then, place your return value in register a0 before returning from the function.
Make sure that you only write to the t and a registers. If you use other registers, strange things may happen (you'll learn about why soon).
Also, make sure you initialize the registers you are using! Venus might show that the registers are initially 0, but in real life they can contain garbage data. Make sure you set the register values that you will be using to some defined number before using them.
My answer:
- ex5_factorial.s
# factorial takes one argument:
# a0 contains the number which we want to compute the factorial of
# The return value should be stored in a0
factorial:
# YOUR CODE HERE
add t0, a0, x0 # t0 = a0, t0 is the counter
addi t1, x0, 1 # t1 = 1, t1 is the result
addi t2, x0, 1 # t2 = 1, t2 is the multiplier
loop:
beq t0, x0, end # if t0 == 0, jump to end
mul t1, t1, t2 # t1 = t1 * t2
addi t2, t2, 1 # t2 = t2 + 1
addi t0, t0, -1 # t0 = t0 - 1
j loop # jump to loop
end:
add a0, t1, x0 # a0 = t1