RV32IM Venus Sim
RV32IM Venus Sim
Version 1.0
February 12, 2024
• VS Code is available for Windows, macOS, and Linux, and it can be downloaded for free
from:
https://code.visualstudio.com
• The VS Code web site automatically detects your machine type and provides a link to
download the appropriate verison of the software. Follow the provided instructions to
install VS Code on your machine.
https://code.visualstudio.com/docs
• You can also watch some introductory videos about VS Code at:
https://code.visualstudio.com/docs/getstarted/introvideos
1
2 Installing RISC-V Extensions
• Launch VS Code and select Extensions from the activity bar on the left of the screen. Alter-
natively, press: Ctrl+Shift+X1 . Extensions are useful add-ons that third-party developers
provide on the VS Code Extensions Marketplace.
• In the Extensions search bar at the top of the screen type: RISC-V. This will provide a list
of available VS Code extensions that include RISC-V support. Select the RISC-V Support
and RISC-V Venus Simulator extensions and install each separately.
• These extensions enable assembly code syntax highlighting and add the Venus RISC-V
simulator to VS Code, respectively.
3 VS Code Explorer
• In VS Code, all files are organized into folders that can be viewed using the builtin Explorer.
To open Explorer, click on the top-most icon in the activity bar on the left of the screen, or
press: Ctrl+Shift+E.
• To open a folder, press (Ctrl+K Ctrl+O). If no other folders are open in Explorer, press
the Open Folder button. Use Explorer to navigate to the desired folder location. It is
recommended that you create a single folder to save all RISC-V assembly programs you
develop in the course. You can also use sub-folders to further organize your programs.
• New files should be assigned a descriptive name and a proper extension. RISC-V assembly
files must all end with a .s extension (e.g. code.s).
1 On macOS systems, use Cmd instead of Ctrl.
2
• Create a new RISC-V assembly program and call it test.s. In the editor, type or copy the
following code:
. data
array :
. word 1 , -7 , 18 , 3 , -5 , 9 , 2 , 11 , -6 , 24
result :
. text
init :
add x1 , x0 , x0 # initialize loop iterator to zero
addi x2 , x0 , 10 # initialize upper loop bound
add x3 , x0 , x0 # initialize sum to zero
la x4 , array # initialize array pointer
loop :
lw x5 ,0( x4 ) # load next array element
add x3 , x3 , x5 # add the element to the running sum
addi x4 , x4 ,4 # increment array pointer
addi x1 , x1 ,1 # increment loop iterator
bne x1 , x2 , loop # if x1 < 10 branch back to the start of the loop
store :
la x4 , result # initialize result pointer
sw x3 , 0( x4 ) # store sum in memory
exit :
addi a0 , x0 ,10 # exit program
ecall
Note that the code is color-coded with assembler directives like .data, .text and .word
displayed in blue; assembly instructions displayed in white; and comments displayed in
green.
• When typing instructions, you can use Intellisense to generate instruction syntax templates.
To generate an instruction template, simply type a tab after the corresponding instruction
mnemonic. Intellisense uses RISC-V Application Binary Interface (ABI) register names (e.g.
t1 and s1), but you can use generic register names too (e.g. x6 and x9).
• To save your code, type Ctrl+S. You can also enable Auto Save by selecting it from the File
menu.
3
5 Running and Debugging a RISC-V Program
The RISC-V Venus Simulator VS Code extension is an RV32IM simulator that can be used to
run and debug RISC-V assembly programs. A detailed description of the simulator and how to
use it within VS Code can be found at:
https://marketplace.visualstudio.com/items?itemName=hm.riscv-venus
The following are a few highlights on using the Venus simulator to run and debug RISC-V
assembly code in VS Code:
• A program must be open in the VS Code editor before it can be run or debugged.
• To start the Venus simulator, select Run and Debug from the activity bar on the left of
the screen. Alternatively, press: Ctrl+Shift+D. Now press the Run and Debug button.
The simulator enables running a program to completion (F5), running a program to a set
breakpoint (F5), or single-stepping through a program one assembly instruction at a time
(F10).
• The Run and Debug pane on the left of the screen includes a menu of options for monitor-
ing the execution of a program and controlling the Venus simulator.
– The Variables option enables monitoring the simulated processor’s registers including
the values of the PC and integer registers (x0-x31).
– A breakpoint can be set by clicking the red dot that appears when the cursor is placed
above a given program line number. Once set, the red dot remains visible next to
the corresponding line number. A breakpoint can be removed by clicking a red dot a
second time.
– A breakpoint stops the simulator at a specific instruction. This enables examination
of the registers (or memory locations) to check if the code is executing as expected.
Execution can be resumed by repeatedly pressing F10 to single-step through the code
(i.e. execute instructions one at a time), or by pressing F5 to run the program until the
next breakpoint or until the program ends. Breakpoints are a very useful debugging
tool, and a program may have multiple active breakpoints at the same time.
– The Venus Options option includes a Views option that enables displaying a pro-
gram’s machine code, the simulated processor’s memory segments, and various vir-
tual display peripherals.
– Selecting the Assembly view under Venus Options → Views opens an assembly win-
dow that shows the memory address an instruction is stored at, the corresponding
machine code, and the RISC-V assembly code. If the source assembly code contains
pseudo-instructions, the assembly window also shows the corresponding RISC-V as-
sembly instructions they are translated to.
– Selecting the Memory view under Venus Options → Views opens an memory win-
dow that shows the various segments of the simulated processor’s memory space. You
can choose to display the Text (i.e. code), Data, Heap, or Stack sections. You can also
specify the address you would like to examine. Finally, you can display the contents
of the memory segments in different formats such as hexadecimal, signed decimal,
unsigned decimal, and ASCII.
4
– The memory view shows the contents of several memory words in a given segment.
The address and contents of each word are shown on a separate line. Because Venus
is an RV32IM simulator, a word consists of four bytes (32 bits) stored in little-endian
format. The contents of each byte are displayed in a separate column. Finally, the
Up/Down buttons can be used to navigate throughout a memory segment. Note that
the Text segment starts are address 0x00000000 and the Data segment starts at address
0x10000000.
• Use the example program to try different features of the Venus simulator.
5
6 Environment Calls
• The Venus simulator uses environment calls to emulate operating system functions. For
example, the ecall statement at the end of the example program emulates the way an
operating system exits a program.
• Venus supports a limited number of environment calls that are mainly used to display
results. You can find a complete list of supported environment calls at:
https://github.com/kvakil/venus/wiki/Environmental-Calls
• The most commonly used environment calls are: print_int, print_string, exit,
and print_character.
• Some environment calls require one or more parameters/arguments to pass data to a call.
For example, to print an integer on the terminal screen, call ID 1 should be stored in register
a0 and the integer to display should be stored in register a1. The following instruction
sequence prints the value 47 on the simulator terminal screen:
addi a0 , x0 ,1 # print_int ecall
addi a1 , x0 ,47 # integer to print
ecall
• Currently there are no standard environment calls for reading user input2 . This limitation
can be overcome by using the li and la pseudo-instructions to initialize registers to ar-
bitrary 32-bit signed and unsigned values, respectively. Alternatively, as in the example
assembly program, arrays of numeric data can be declared in a program’s .data segment
using the .byte and .word assembler directives. Null-terminated character strings also can
be declared using the .asciiz assembler directive.
2 The Venus simulator supports rudimentary environment calls for reading user input. These will be introduced
later in the course.
6
7 Virtual LED Matrix Display
The Venus simulator has a virtual 10 × 10 LED matrix display. The display can be open in
a separate window by selecting Run and Debug from the activity bar and selecting the LED
Matrix view under Venus Options → Views.
• A pixel can be turned on individually using environment call 0x100, which also requires
storing the pixel’s LED matrix coordinates in register a1 and its color in register a2.
• A pixel’s X- and Y-coordinates are specified in bits 31-16 and 15-0 of register a1, respectively.
Coordinates (0, 0) correspond to the display’s top left corner.
• A pixel’s color is set by setting its red, green, and blue components in bits 23-16, 15-8,
and 7-0 of register a2, respectively. Each color component is represented using an 8-bit
value where 0 represents no color and 255 represents full color. Each pixel can therefore be
displayed in one of 2563 = 16777216 colors.
• The following code snippet displays four pixels in white, red, green, and blue at coordinates
(3,4), (4,4), (5,4) and (6,4), respectively:
addi a0 , x0 ,0 x100
li a1 ,0 x00030004
li a2 ,0 x00FFFFFF # White
ecall
li a1 ,0 x00040004
li a2 ,0 x00FF0000 # Red
ecall
li a1 ,0 x00050004
li a2 ,0 x0000FF00 # Green
ecall
li a1 ,0 x00060004
li a2 ,0 x000000FF # Blue
ecall
• All display pixels can be set to the same color using environment call 0x101. The color is
stored in register a1. The following code snippet sets all display pixels to AUB’s Berytus
Red:
addi a0 , x0 ,0 x101
li a1 ,0 x00840132 # Berytus Red
ecall
7
8 Toy Robot Display
• The Venus simulator also has a 12-line × 16-pixel toy robot display, which can be open in
a separate window by selecting Run and Debug from the activity bar and selecting the
Robot UI view under Venus Options → Views.
• Pixels on the robot display are turned ON one line at a time using environment call 0x110.
The line number is specified in register a1 where line 0 corresponds to the bottom of the
display. Register a2 is used to store a 16-bit vector that determines which pixels on a line
should be turned ON.
• The following code snippet displays a solid line where all pixels are ON on line 6:
addi a0 , x0 ,0 x110
li a1 ,0 x0006
li a2 ,0 xFFFF
ecall
• The seven-segment displays are controlled through envrionment call 0x120. A 16-bit mask
in register a1 selects which segments to control, and a second 16-bit mask in register a2
determines whether the selected segments should be turned ON or OFF. If a bit in register
a1 is zero, the corresponding segment will be turned OFF regardless of the corresponding
bit value in register a2.
• The mapping of the 16 seven-segment elements to their corresponding mask bits is shown
below:
• For example, to display the number 25, the following segments should be selected: 0, 2, 3,
5, 6, 8, 9, 11, 12, 14. This corresponds to the bit map: 0x5B6D.
8
• The following code snippet displays the number 25 on the seven-segment displays:
addi a0 , x0 ,0 x120
li a1 ,0 x5B6D
li a2 ,0 x5B6D
ecall
• The two LEDs on the virtual board are controlled by environment call 0x121. The least-
significant two bits of register a1 are also used to control whether LED 0 (green) or LED 1
(red) are turned ON or OFF, respectively. The following code snippet turns both LEDs ON:
addi a0 , x0 ,0 x121
li a1 ,0 x3
ecall
• Two interactive push buttons on the virtual board can be pushed at any time and used as
inputs to a program. The states of the push buttons can be read using environment call
0x122. The state values (pushed/not pushed) of push buttons 0 and 1 are returned in the
least-significant two bits of register a0. A bit value of 0 indicates the corresponding button
is not pushed while a bit value of 1 indicates the corresponding button is pushed. Once
the environment call is invoked and the push button states are captured in register a0, the
states are automatically cleared to the not-pushed state. The captured states can then be
examined in register a0.
• The following loop continuously reads the states of push-buttons 0 and 1 and turns ON the
corresponding LEDs:
loop :
li a0 , 0 x122 # read button states
ecall # states captured in a0 and cleared
mv a1 , a0 # move captured states to a1
li a0 , 0 x121 # turn ON corresponding LEDs
ecall
j loop # repeat