Studio 6A
Names
Open questions.md
and list the names of everyone in your group.
Instruction Encoding
Binary Encoding
Using Appendix B identify the 32-bit binary representation of the instructions (show work and answers in questions.md
). Write out the final answer in hexadecimal (8 digits of hex = 32 bits). Note that the last three have a lot of similarity. Show your final results in questions.md
ori s0, s1, 32
or s0, s1, s2
and s0, s1, s2
add s0, s1, s2
Encoding Conveniences
ALU operation selection: Review the description of the ALU from homework 5a. There is a way to select bits from instructions to make the ALU do the corresponding operation. Briefly describe what bits may be candidates to identify when the ALU should do add
, or
, and and
operations.
Immediates: In addition to being organized to facilitate selecting operations, instructions are organized to facilitate identification of the “immediate” instructions, like ori
vs. the register-only variations, like or
. Which bit(s) may be being used for this purpose? Quickly compare some pairs of instructions, like ori
and or
, andi
and and
, addi
and add
, etc. to see if there’s a distinguishing feature.
Assembly Math
Complete code in math.s
that will compute 3*a + b - c
using only the instructions from table B.1 (compute use addition rather than multiplication). Test your work with different values.
As you’re working:
- Use the “Run & Debug” button on the left activity panel (Play button with a bug) to open the Venus assembly language simulator. It will try to start the simulator on whatever
.s
file is currently open. Actually running it will require using the “Launch with all views” button that will appear at the top of the window. - A floating panel should appear with buttons to run the code, step through line-by-line, and reload to the start. Step through the program a few times. Practice using this and the registers view to observe how the program modifies the registers as it runs.
- Examine the memory in the “Memory” pane. Currently it should show only instructions. You can use the Up and Down buttons to move to different regions of memory (or enter an address in hexadecimal)
- Stop the program, deliberately make a typo (syntax error / invalid line or instructions) and try to run it again. Where is the error message displayed? (Answer in
questions.md
)
Stop the simulator
Be sure to use the “Stop” button to stop the simulator before moving on to a new file. Close the tab for math.s
.
More Assembly Math
Open and complete moremath.s
, which asks you to approximate the computation of 110% of an integer without using multiplication or division. Come up with an approach using ~2-8 instructions and re-run your program with a few different test cases.
Assembly Loops
Complete the loop problem described in loops.s
. You can use the formula $ \frac{n \cdot (n+1)}{2} $ to check your work. Test your work with different values.
Registering Challenges
The entirety of large programs running on a RISC-V processor have to share the 31 registers for almost all operations. Compilers translate programs into a form that does all work by use and re-use of just these 31 variables! This is actually quite challenging and the type of thing that is error prone in humans. In fact, most modern programming languages and style guides discourage reusing variables for different meaning/data over time!
The “register conventions” (rules of use) are:
Register | ABI Name | Description | Saver |
---|---|---|---|
x0 | zero | Hard-wired zero | — |
x1 | ra | Return address | Caller |
x2 | sp | Stack pointer | Callee |
x3 | gp | Global pointer | — |
x4 | tp | Thread pointer | — |
x5–7 | t0–2 | Temporaries | Caller |
x8 | s0/fp | Saved register/frame pointer | Callee |
x9 | s1 | Saved register | Callee |
x10–11 | a0–1 | Function arguments/return values | Caller |
x12–17 | a2–7 | Function arguments | Caller |
x18–27 | s2–11 | Saved registers | Callee |
x28–31 | t3–6 | Temporaries | Caller |
From the perspective of someone writing a function (you):
- If you use any registers where the Saver is Callee, you must ensure they are their original value before you return.
- You can freely use anything where the Saver is Caller.
From the perspective of someone writing code that is calling a function (might apply to you too):
- If you use any registers where the Saver is Caller, you must assume that any information in them could be destroyed by a
jal
to any function. If you need the contents, you should move it someplace else. - You can freely use the items where the Saver is Callee if you are the topmost function, which is rare. In most cases the code you’re writing is both called and will call something else, so you have to follow both sets of policies.
Turn the code you wrote for the prior part (sum) into a valid RISC-V function that follows the register conventions in the TODO
location of functionfun.s
. Your function will be called near the top of the file:
# Call the function
jal checkpoint_regs # This function helps check that you are using registers correctly
li a0, 100
jal sum
jal check_regs # This function helps check that you are using registers correctly
The jal sum
calls your code. This code includes two additional function calls, jal checkpoint_regs
and jal check_regs
, to help ensure you are following the register use rules. They are indented extra to highlight they are extra, artificial pieces of code and not part of the program’s intended function.
- Complete the
sum
function and confirm that it works on a few test cases. Be sure to follow the register conventions! - Add an instruction that breaks the register conventions for an s-register. Re-run your program and note the error message printed by
jal check_regs
- Change a value in an s-register and then change it back to the original value. For example, move the original value someplace you can use, change the register, then move the original value back before the function returns. What happens?
- Add in more instructions that change other s-registers and check the error(s).
- The stack pointer register,
sp
is also “Callee Preserved”. What happens when you change its value? - The “stack” is the normal place to accomplish “move the original value someplace you can use” and is vital to the sharing process. The normal approach to using the stack is something like:
functionstart: addi sp, sp, -20 # Set aside space in a multiple of 4. This would be space for 5 words (4*5=20) # "Save" any of the callee-saved registers we need to use in consecutive places on the stack # starting with 0, increasing by 4s, and not exceeding the value set aside-3 sw s0, 0(sp) # my_stack[0] = s0 (indices are word indices. Each word is 4 bytes) sw s1, 4(sp) # my_stack[1] = s1 (so "word index[1]" is 4 bytes into the stack) sw s2, 8(sp) sw s3, 12(sp) sw s4, 16(sp) # Function body ... # Restore everything: Get back all the saved values first lw s0, 0(sp) # s0 = my_stack[0] (indices are word indices. Each word is 4 bytes) lw s1, 4(sp) # s1 = my_stack[1] (so "word index[1]" is 4 bytes into the stack) lw s2, 8(sp) lw s3, 12(sp) lw s4, 16(sp) addi sp,sp, 20 # Restore the stack's original value too (by undoing the initial subtract) jr ra # Return
- Apply this approach to “storing / restoring” values and update your function to use s-registers for its computation. (You can used a condensed form since you probably don’t need 5 registers)
- Answer the following questions in
questions.md
- Considering the pattern above. Why is it beneficial for most functions to only have a single return point in their assembly language? (Only one place that has a
jr ra
. Even if the function itself has several points that sayreturn
, they typically all result in code that goes to a singlejr ra
for the function) - Briefly summarize how the above should ensure that any changes to
sp
or any of thes
registers being used should not be noticed by any functions that call this function. - Note that the
jal
instruction itself changesra
. How should the above be modified for any function that calls another function?
- Considering the pattern above. Why is it beneficial for most functions to only have a single return point in their assembly language? (Only one place that has a
Submission / End-of-class: Commit And Push
1. First, be sure to commit and push files to GitHub (as shown in studio)
1.1
1.2
Caution!
Failure to type in a commit message will cause VSCode to open a window to enter the message (in the editor area) and the Source Control
pane will appear to be stuck (a waiting animation) until you type in a message and close the message pane.
1.3
2. Then go to GitHub.com and confirm the updates are on GitHub
End of Studio: Stop the Codespace
Caution!
Be sure to “stop” your Codespace. You have approximately 60 hours of Codespace time per month. Codespaces often run for ~!5 minutes extra if tabs are just closed.