This page covers a bunch of stuff on how to use both Venus on both the web and CLI interfaces. It’s still a work in progress, and will be updated as the semester goes on.
Venus’s code is open source and under active development (mostly by longtime head TA Stephan Kaminsky). You can find the code on Github at this link.
The web interface for Venus can be accessed at venus.cs61c.org.
mainfunction is marked with
.globl(see below). That means that the
mainfunction must be put first if it is not declared
ecallwith argument value 10. This signals for the program to exit. The
ecallinstructions 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.
ecallexit rule is if your
mainis marked global, in which case you can either exit with an
ecallor return the program’s exit code in
a0(like the return value of the
mainfunction in C).
ebreakinstruction in your code to automatically force Venus to pause.
ebreakwithin a branch, like so: ```
You can ignore this tab. Chocopy is a variant of Python used in CS 164 (Programming Languages and Compilers), so it’s not really relevant to this course. The long and short of it is that you can actually compile a subset of Python into RISC-V assembly that you can then execute.
Most of the basic functionality you need for writing assembly programs exists in the “Editor” and “Simulator” tabs. The “Venus” tab exposes a terminal-like interface that lets us work with multiple files to build more complex programs.
helpin the terminal will show the list of commands (you may need to scroll the terminal to the right to see all of them), and
help <command name>will give you information about the command.
ls: Lists the contents of the specified directory (or the current directory if no arguments are provided.
cd: Changes the directory your terminal is in. Like with a normal terminal, you can use Tab to autocomplete the names of subfolders.
touch: Creates a new empty file that we can modify later.
xxd: Prints the contents of a file in hexadecimal.
edit: Opens up the specified file in the “Editor” tab (for example,
edit file.s). Note that the Cmd + S and Ctrl + S shortcuts will work to save the file in the virtual filesystem if you choose to edit a different file later. But, to ensure that your work is saved, we recommend that you periodically save local copies of every file. If you’ve mounted your local filesystem, then Cmd + S/ Ctrl + S will reflect the changes in your local filesystem automatically.
run: Runs the provided assembly file(s) to completion in the terminal. Output will be displayed in the terminal as well.
vdb: Links and assembles the provided assembly file(s) and opens the result in the “Simulator” tab for debugging.
upload: Opens a prompt to upload files from our local computer to the virtual filesystem. You can hold down the Ctrl or Cmd keys to select multiple files to upload. Once a file is uploaded, we can edit it in the Venus editor using the
download: Downloads the specified files locally. You can specify multiple files to download, e.g.
download file1.s file2.s, and each one will have a corresponding prompt to ask you where to download the files.
mount: “Mounts” your local file system to the Venus virtual file system. This command takes in the URL at which the Venus jar webserver is running, followed by the name you want to assign to the mounted folder in the virtual file system. For example, if you ran the Venus jar with
java -jar tools/venus.jar . -dm --port 6162on your local machine, and wanted the mounted folder to be accessible at
vmfsin the virtual file system, you would run
mount http://localhost:6162 vmfsin the Venus terminal.
--portflag, the port defaults to 6161. Running
mount localwill default to this port.
mountcommand in the Venus terminal, it will place it at
mount local vmfs <encryption key>.
umount: “Unmounts” a folder that was monted via the
Use the “Simulator Default Args” field to set the arguments to your program. These will then
be reflected when you run your program in the “Simulator” tab:
a0 will be initialized to the equivalent of
a1 will be initialized to the equivalent of
Configuration options for Venus can be found in the “Settings” pane under the “Venus” tab.
mainto terminate the program via
ecall, as opposed to terminating when the PC exceeds the end of the text section
gpto the start of the static section of memory,
spto the top of the stack, and
rato the return address of the
mainfunction if a global
mainlabel is found; if off, all registers are initialized to 0
Toggles the Calling Convention Checker (see Tools for explanation). The checker’s output will be dumped into the simulator console when enabled.
Allows register and memory values to be printed during program execution. See traces for more details.
The latest version of the Venus CLI can be downloaded at https://venus.cs61c.org/jvm/venus-jvm-latest.jar. You need to be able to run Java to run it.
Everything passed after the file name will be passed to your program as command line arguments.
For example, if you wanted to pass arguments
test.s, you would run
$ java -jar venus.jar test.s arg1 arg2 arg3
a0 will be initialized to the equivalent of
a1 will be initialized to the equivalent
To provide simulator or assembler options to Venus, place your flags between the JAR and the name
of the assembly file. For example, if you wanted to run
test.s with immutable text (
-it) and the
calling convention checker (
-cc), you would run this command:
$ java -jar venus.jar -it -cc test.s arg1 arg2 arg3
Note: Flags to configure Venus assembly or simulation behavior can be passed after the name of
the file. These arguments will be consumed by the simulator and not passed to the program; this
behavior may change in the future with improvements to Venus’s argument parser.
If you wish to pass an argument to your program that shares a name with a Venus flag, then add
-- before your program arguments. For example, to pass the string “-it” as the first argument
test.s, you would run
$ java -jar venus.jar test.s -- -it
Ran for more than max allowed steps!: Venus will automatically terminate if your program runs for too many steps. This is expected for large MNIST sized inputs, and you can workaround it with the
-msflag. If you’re getting this for small inputs, you might have an infinite loop.
Attempting to access uninitialized memory between the stack and heap.: Your code is trying to read or write to a memory address between the stack and heap pointers, which is causing a segmentation fault. Check that you are allocating enough memory, and that you are accessing the correct addresses.
The magic value for this malloc node is incorrect! This means you are overriding malloc metadata OR have specified the address of an incorrect malloc node!: Your code is modifying the metadata of a malloc node. This error can come from any of the alloc or free commands as venus implements a linked list form of malloc. The metadata is right below (lower address) the pointer to that location so if you write to the wrong location, you may corrupt that data. Venus has a method to detect some corruptions which is why you get this error. Check that you are correctly indexing any malloced data and that you are not writing out of bounds of what you allocated.
The RISC-V calling convention specifies which registers are saved by the caller of a function,
and which are saved by the callee. Bugs resulting from a failure to comply with the calling
convention checker can be difficult to track down on their own, so Venus provides the
--callingConvention) flag to automatically detect certain kinds of calling convention
Before you use the calling convention checker, you should be aware of two things:
.globldirective. You may sometimes see errors reported within functions that aren’t exported, but this is usually because they are being called by a function that’s declared
There are currently three types of error messages that the CC checker can produce, each explained below.
In each example,
func refers to the function where the error message was reported.
You can run these examples yourself in Venus by modifying the following code snippet:
.globl func main: jal func li a0, 10 # Code for exit ecall ecall func: # Paste the example definition of func
func overwrote a callee-saved register (any of s0 through s11).
To fix this, make sure you
func stores the values of any registers it overwrites by clearing
space on the stack and storing them in memory in the prologue, and then restoring their values
and restoring the stack pointer in the epilogue.
Example with error:
func: li s0, 100 # === Error reported here === li s1, 128 # === Error reported here === ret
func: # BEGIN PROLOGUE # Each clobbered register (4 bytes each) needs to be stored addi sp, sp, -8 sw s0, 0(sp) sw s1, 4(sp) # END PROLOGUE li s0, 100 li s1, 128 # BEGIN EPILOGUE lw s1, 4(sp) lw s0, 0(sp) addi sp, sp, 8 # END EPILOGUE ret
The value of
s0 in the function that called
func was different before and after
called. This error message complements the previous one, but will instead be reported on the line
jalr ra) instruction.
Example with error:
func: # BEGIN PROLOGUE addi sp, sp, -8 sw s0, 0(sp) sw s1, 4(sp) # END PROLOGUE li s0, 100 li s1, 128 # BEGIN EPILOGUE # Forgot to restore the values of s0 and s1! addi sp, sp, 8 # END EPILOGUE ret # === Error reported twice here ===
Fixed example: Same as for the previous error. Just make sure to include both the prologue and epilogue.
func attempted to read from a register that wasn’t set by the caller, and wasn’t yet initialized within the body of
func. Even if the function that calls
func set values in a register like
s0, attempting to use their values within
func would be violating an abstraction barrier:
func shouldn’t need to know anything about the values of registers in its caller, with the
exception of the argument, stack pointer, and return address.
Example with error:
func: mv a0, t0 # === Error reported here === ret
This sort of error usually originates due to a typo (the instruction is reading from the wrong register, or the order of arguments to the instruction was flipped accidentally), or a violation of abstraction barriers as described above. Fixes will vary depending on what the function was intended to do.
You can set “traces” in both the web and CLI versions of Venus to print values in registers
and memory during the execution of your program. In the web interface, you can enable traces in
the “Traces” part of the Settings pane; in the command line you can enable and customize traces
The trace patterns support a few special symbols, somewhat like a C format string. They are as follows:
\t: tab indent
%31%: the value in the corresponding register
%line%: the line number of the code being executed
%pc%: the program counter of the current instruciton
%inst%: the code of the current instruction
%output%: the output of an ecall message
%decode%: the decoded machine code of the current instruction
Once we begin to write more complex RISC-V programs, it will become necessary to split our code into multiple files and debug/test them individually. The “Venus” tab of the web editor provides access to a terminal (and corresponding virtual filesyste) that lets us edit and test programs built from multiple files.
Note: To ensure that you don’t lose any of your work, make sure to save local copies of your files frequently. To be doubly sure, also save your local work with git, and push those saves frequently!
.globl directive identifies functions that we want to export to other files,
similar to including a function in a header file in C.
Venus supports the
.import directive (not technically part of the RISC-V spec)
to access other assembly files, similar to the C
include directive. It will
only make labels marked
Unlike most real-world programs (and the RISC-V spec), dereferencing the null pointer
DOES NOT segfault. This is because by default, the start of the text segment is set to
0x0000_0000, whereas a real system would likely set it to something like
0x10000_0000. This value can be configured in the settings pane of the web interface.
ecall instruction is used to perform system calls or request other privileged
operations such as accessing the file system or writing output to console.
To perform a syscall, load the appropriate syscall number (listed below) into the
a0 register, then place the syscall’s arguments in order in the remaining argument
a2, etc.) as needed. For example, the following assembly
snippet would print the number
1024 to stdout:
li a0, 1 # syscall number for printing integer li a1, 1024 # the integer we're printing ecall # issue system call
Syscalls that return values will place the value in
a0, as with any other function.
Note: The calling convention for RISC-V on Linux (described
under “Architecture calling conventions”) actually specifies that the syscall number be
passed through the
a7 register. Venus uses
a0 for simplicity, and follows in the
tradition of the SPIM simulator.
See the Venus wiki for the full list of available calls.