Venus Reference
This page (WIP) covers usage of the Venus CLI and web interfaces.
Venus Web Interface
The Venus web interface is available at https://venus.cs61c.org.
Mounting a Local Directory
You can "mount" a folder from your local device onto Venus's web frontend, so that edits you make within the browser Venus editor are reflected in your local file system, and vice versa. If you don't do this step, files created and edited in Venus will be lost each time you close the tab, unless you copy/paste them to a local file.
cd
into the folder you'd like to mount, and runjava -jar tools/venus.jar . -dm
. This will expose your directory to Venus on a network port.- You should see a big "Javalin" logo.
- If you see a message along the lines of "port unable to be bound", then you
can specify another port number explicitly by appending
--port PORT_NUM
to the command (for example,java -jar tools/venus.jar . -dm --port 6162
will expose the file system on port 6162).
- Open https://venus.cs61c.org in your web browser (Chrome or Firefox are recommended). In the Venus web terminal, run
mount local vmfs
(if you chose a different port, replace "local" with the full URL, such ashttp://localhost:6162
). This connects Venus to your file system.- In your browser, you may see a prompt saying
Key has been shown in the Venus mount server! Please copy and paste it into here.
. You should be able to see a key in the most recent line of your local terminal output; just copy and paste it into the dialog.
- In your browser, you may see a prompt saying
- Go to the "Files" tab. You should now be able to see your files under the
vmfs
folder.
The "Editor" Tab
- Enter your code in the "Editor" tab
- Programs start at the first line of assembly code regardless of the label, unless the
main
function is marked with.globl
(see below). That means that themain
function must be put first if it is not declared.globl
.- Note: Sometimes, we want to pre-allocate some items in memory before the
program starts executing. Since this allocation isn't actual code, we can
place it before the
main
function.
- Note: Sometimes, we want to pre-allocate some items in memory before the
program starts executing. Since this allocation isn't actual code, we can
place it before the
- Programs end with an
ecall
with argument value 10. This signals for the program to exit. Theecall
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.- The exception to the
ecall
exit rule is if yourmain
is marked global, in which case you can either exit with anecall
or return the program's exit code ina0
(like the return value of themain
function in C).
- The exception to the
- Labels end with a colon (
:
). - Comments start with a pound sign (
#
). - You CANNOT put more than one instruction per line.
- You can use the save keyboard shortcut (Cmd + s / Ctrl + s depending on your platform, it will save the file). You can check the name of the active file at the bottom of the editor: if it is unnamed, it will prompt you to download the file, and if you hit save rapidly, you will receive this prompt as well.
- When you are done editing, click the "Simulator" tab to prepare for execution.
The "Simulator" Tab
- When you first reach the "Simulator" tab from the "Editor" tab, you'll see an "Assemble and Simulate from Editor" button that will report errors in your code or reassemble it.
- If you've assembled the program before, you'll see a "Re-assemble from Editor" button. Click it to reassemble the code from your editor tab.
- Breakpoints can be set by clicking on the desired line of code before you hit "Run".
You can also insert an
ebreak
instruction in your code to automatically force Venus to pause.- You can simulate conditional breakpoints by placing an
ebreak
within a branch, like so:# ...code li t0, 3 # Stops execution if a0 == 3 bne a0, t0, after_breakpoint ebreak after_breakpoint: # ...code
- You can simulate conditional breakpoints by placing an
- The "Run" button runs your program until you reach a breakpoint, much like GDB's "run" command.
- The "Step" button will go to the next assembly instruction.
- The "Prev" button reverts a single assembly instruction.
- The "Reset" button ends the current run, clears all breakpoints, and returns to the beginning of execution.
- "Dump" gives you a dump of the hexadecimal representation of every instruction in your program.
- You can see the contents of registers, memory, and caches through the sidebar on the right.
- You can manually poke values in registers to affect the execution of your program.
- In the "Memory" tab, use the "Jump to" or "Address" boxes to quickly navigate to a certain address in your program.
- For all menus, use "Display Settings" at the bottom of the pane to toggle between hex, ASCII, two's complement decimal, and unsigned decimal interpretations of your values.
- The "Trace" button will dump values in registers and memory based off a specified format, described in more detail in the traces section.
The "Chocopy" Tab
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.
The "Venus" Tab
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.
Terminal Commands
- Typing
help
in the terminal will show the list of commands (you may need to scroll the terminal to the right to see all of them), andhelp COMMAND
will give you information aboutCOMMAND
. - A few common commands are listed here:
- File manipulation:
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.- Other useful commands (similar to what you'd find in a standard shell):
cat
,clear
,cp
,mkdir
,mv
,rm
,pwd
,zip
,unzip
- Venus editor and simulator:
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 saving 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.
- Connecting to a local file system:
upload
: Opens a prompt to upload files from our local computer to the virtual filesystem. You can hold down the Cmd or Ctrl keys to select multiple files to upload. Once a file is uploaded, we can edit it in the Venus editor using theedit
command.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 withjava -jar tools/venus.jar . -dm --port 6162
on your local machine, and wanted the mounted folder to be accessible atvmfs
in the virtual file system, you would runmount http://localhost:6162 vmfs
in the Venus terminal.- If you omit the
--port
flag, the port defaults to 6161. Runningmount local
will default to this port. - If you omit the path argument to the
mount
command in the Venus terminal, it will place it atdrive
by default. - You may be prompted to provide an encryption key in the browser. The key should
be shown in the last line of the Java server's terminal output, which you should
then copy/paste. You can also provide the key within the Venus web terminal, with
something like
mount local vmfs <encryption key>
.
- If you omit the
umount
: "Unmounts" a folder that was mounted via themount
command.
- File manipulation:
Passing arguments to your program
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 argc
and a1
will be initialized to the equivalent of argv
.
Settings
Configuration options for Venus can be found in the "Settings" pane under the "Venus" tab.
General
- "Simulator Default Args" - passes arguments to your program
- "Text Start" - identifies the start of the text segment
- "Max History" - specifies the maximum number of execution steps that can be reverted; leave negative for no limit
- "Aligned Addressing" - if on, forces all memory accesses to be aligned to the size of the data type, and will raise a runtime error if a memory access is not
- "Mutable Text" - if on, allows the text segment (where instructions are stored) to be overwritten during the execution of your program
- "Only Ecall Exit" - if on, requires
main
to terminate the program viaecall
, as opposed to terminating when the PC exceeds the end of the text section - "Default Reg States" - if on, initializes
a0
to argc,a1
to argv,gp
to the start of the static section of memory,sp
to the top of the stack, andra
to the return address of themain
function if a globalmain
label is found; if off, all registers are initialized to 0 - "Allow Access" - if on, allows memory accesses to occur at addresses between the stack and heap
- "Max number of steps" - sets the limit for the number of steps your program can execute; leave negative for no limit
- "Dark Mode" - activates dark mode
Calling Convention
Toggles the Calling Convention Checker (see Tools for explanation). The checker's output will be dumped into the simulator console when enabled.
Tracer
Allows register and memory values to be printed during program execution. See traces for more details.
The Venus CLI
The latest version of the Venus CLI can be downloaded at https://venus.cs61c.org/jvm/venus-jvm-latest.jar. It requires a working Java install to run.
Passing arguments to your program
Everything passed after the file name will be passed to your program as command line arguments.
For example, if you wanted to pass arguments arg1
, arg2
, and arg3
to test.s
, you would run
the following:
a0
will be initialized to the equivalent of argv
and a1
will be initialized to the equivalent
of argc
.
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:
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
to test.s
, you would run
Common Venus Errors
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-ms
flag. 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 thealloc
orfree
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 anymalloc
ed data and that you are not writing out of bounds of what you allocated.
Tools
Calling Convention Checker
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 -cc
(or --callingConvention
) flag to automatically detect certain kinds of calling convention
errors.
Before you use the calling convention checker, you should be aware of two things:
- Venus cannot detect all calling convention violations - the fact that function calls in assembly are essentially just jumps to labels means Venus has to be very conservative in choosing what to detect as a function call. The CC checker serves as an excellent sanity check (much like Valgrind does for C memory bugs), but there will be some bugs that you ultimately need to figure out manually.
- Related to the above, the CC checker will only reliably examine calling convention violations
within the body of functions exported with the
.globl
directive. 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.globl
.
Violation Messages
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
"Setting of a saved register (s0) which has not been saved!"
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
Fixed example:
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
"Save register s0 not correctly restored before return! Expected <hex value>, Actual <hex value>"
The value of s0
in the function that called func
was different before and after func
was
called. This error message complements the previous one, but will instead be reported on the line
of the ret
(or 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.
"Usage of unset register t0"
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 t0
or 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
Fixed example:
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.
Traces
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
with the --trace
and --tracepattern
flags.
The trace patterns support a few special symbols, somewhat like a C format string. They are as follows:
\t
: tab indent\n
: newline%0%
through%31%
: the value in the corresponding register%line%
: the line number of the code being executed%pc%
: the program counter of the current instruction%inst%
: the code of the current instruction%output%
: the output of an ecall message%decode%
: the decoded machine code of the current instruction
Memcheck
Memcheck is a new feature added in Fall 2022. This feature aims to be the equivalent
of valgrind
but for Venus. It is capable of finding invalid memory accesses and unfree'd
blocks in your code. The feature has two flags: --memcheck
and --memcheckVerbose
.
The --memcheck
flag prints out invalid memory accesses as well as a dump of all registers
at the time of the invalid memory access. It also lists the number of blocks that are unfree'd
at the end of the program, similar to valgrind
. The --memcheckVerbose
flag prints out every
memory access, malloc
/calloc
/realloc
/free
call and its arguments, as well as more
details about unfree'd blocks at the end of program execution.
Writing larger RISC-V programs
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 filesystem) 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!
Working with Multiple Files
The .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 .globl
available.
Behavior not defined in the RISC-V spec
Null Pointers
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.
System Calls
The 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 into the
a0
register, then place the syscall's arguments in order in the remaining argument
registers (a1
, 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 in the Linux
manpages
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.
List of System Calls
See the Venus wiki for the full list of available calls.