0% found this document useful (0 votes)
56 views14 pages

EE 471 LAB1 RipesIntroduction

Ripes is a graphical processor simulator and assembly code editor designed for the RISC-V instruction set, providing features for writing and simulating assembly and C programs. It includes various tabs for editing code, viewing processor states, managing memory, and interacting with memory-mapped I/O devices. The simulator supports multiple processor models and allows users to visualize the execution of programs and the behavior of the processor's datapath.

Uploaded by

Võ Thiện
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
56 views14 pages

EE 471 LAB1 RipesIntroduction

Ripes is a graphical processor simulator and assembly code editor designed for the RISC-V instruction set, providing features for writing and simulating assembly and C programs. It includes various tabs for editing code, viewing processor states, managing memory, and interacting with memory-mapped I/O devices. The simulator supports multiple processor models and allows users to visualize the execution of programs and the behavior of the processor's datapath.

Uploaded by

Võ Thiện
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

EE 471 - COMPUTER ORGANIZATION AND DESIGN

LAB 1 – RIPES INTRODUCTION

Ripes is a graphical processor simulator and assembly code editor built for the RISC-V
instruction set architecture, suitable for teaching how assembly level code is executed
on various microarchitectures.

The following sections serve as an introduction to the main features of Ripes.

The Editor Tab

The editor tab shows two code segments. On the left hand side, it is possible to write an
assembly program written using the RISC-V RV32(I/M) instruction sets. Whenever any
edits are performed in this assembly program - and no syntax errors are found - the
assembly code will automatically be assembled and inserted into the simulator. If a C
compiler has been registered, the `input type` may be set to `C`. It is then possible to
write, compile and execute C-language programs within Ripes, see
this wiki page for more detail.

Next, on the right hand side a second code view is displayed. This is a non-interactive
view of the current program in its assembled state, denoted as the program viewer. We
may view the assembled program as either disassembled RISC-V instructions, or as the
raw binary code. The blue sidebar of the right-hand view may be clicked on to set a

breakpoint at the desired address. Pressing the icon will bring up a list of all
symbols in the current program. Through this, it is possible to navigate the program
viewer to any of these symbols.
Ripes is bundled with various examples of RISC-V assembly programs, which can be
found under the File->Load Examples menu.
An example program could be the following: loading a value from memory into a
register and incrementing the value.

.data
w: .word 0x1234

.text
lw a0 w
addi a0 a0 1
With a program ready to be simulated, we may now move to the Processor tab.

The Processor Tab

The processor tab is where Ripes displays its view of the currently selected processor, as
well as any additional information relevant to the execution. Apart from the processor
view, the processor tab contains the following views:

• 1: Registers: A list of all registers of the processor. Register values may


be edited through clicking on the value of the given register. Editing a register
value is immediately reflected in the processor circuit. The most recently modified
register is highlighted with a yellow background.
• 2: Instruction memory: A view into the current program loaded in the simulator.
o BP: Breakpoints, click to toggle. Any breakpoint set in the editor tab will be
reflected here.
o PC: The address of the given instruction
o Stage: Lists the stage(s) that is currently executing the given instruction
o Instruction: Disassembled instruction
• 3: Statistics: Various statistics based on the cycle count and current number of
retired instructions.
• 4: Output: Any output through an ecall print function will be displayed here.

The Processor View


Processor models in Ripes communicate the current state of the datapath through
various visual means, such as

• Multiplexers indicate the currently selected input signal by highlighting an input


port with a green dot.
• Various components contains indicators to communicate whenever i.e. a register
is clocked, a branch is taken, etc.
• Port value changes are reflected through signal wires:
o Boolean (1-bit signals): Boolean signals will always indicate whether a
signal is high (1) when a wire is green, and low (0) when a wire is grey.
o Other signals, when modified, will briefly flash green to indicate that the
value was modified.

The processor view may be zoomed by performing a ctrl+scroll operation (cmd+scroll


on OSX).

Clicking a wire highlights the entirety of the wire. This is useful when trying to deduce
how a signal is routed through the datapath in some of the more complex
layouts.
Given that Ripes simulates the entire datapath of a processor, it is possible to
investigate the value of any signal, at any point in time.

1. Hover over any port in the processor view. This will display the name of the port,
as well as the current value of the port.

2. Press the Display signal values button. This will display the output values of all
output ports in the design. Alternatively, right click on any port and press "show
value" to display its label. If a port's value label has been made visible, it is
possible to change the radix of the displayed value through right-clicking the
port label.

Controlling the Simulator


The toolbar within Ripes contains all of the relevant actions for controlling the simulator.

Select Auto- Show stage


Reset Reverse Clock Run
Processor clock table

• Select Processor: Opens the processor selection dialog (for details, refer to
section below).
• Reset: Resets the processor, setting the program counter to the entry point of
the current program, and resets the simulator memory.
• Reverse: Undo's a clock-cycle.
• Clock: Clocks all memory elements in the circuit and updates the state of the
circuit.
• Auto-clock: Clocks the circuit with the given frequency specified by the auto-
clock interval. Auto-clocking will stop once a breakpoint is hit.
• Run: Executes the simulator without performing GUI updates, to be as fast as
possible. Any print ecall functions will still be printed to the output console.
Running will stop once a breakpoint is hit or an exit ecall has been performed.
• Show stage table: Displays a chart showing which instructions resided in which
pipeline stage(s) for each cycle. Stalled stages are indicated with a '-' value. Note:
Stage information is not recorded while executing the processor through
the Run option.
• Select View->Show processor signal values to display all output port values of the
processor.

While executing the program loaded earlier, we may observe that, in cycle 4, a load-use
dependency arises between the 2nd and 3rd instruction. This results in the ID stage
being stalled for one clock cycle, whilst the load is being performed. Pipeline stalls (due
to hazards) and flushes (due to control flow) will be indicated above a pipeline stage
as nop instructions highlighted in red.
Selecting Processor Models

Through providing multiple processor models, Ripes provides the ability to investigate
how different microarchitectures affect program execution. The set of processor models
shipping in version 2.0 (described below) aims to address each level of added
complexity when going from a single cycle processor to a fully functioning, in-order
pipelined processor. Ripes provides the following processor models:

• RISC-V Single Cycle Processor


• RISC-V 5-Stage Processor w/o Forwarding or Hazard Detection
• RISC-V 5-Stage Processor w/o Hazard Detection
• RISC-V 5-Stage Processor

Furthermore, each processor provides multiple layouts of the processor. By default, the
following two layouts are provided:

• Standard: A simplified view of the processor. Control components and signals


are omitted.
• Extended: An extended view of the processor. Control components and signals
are visible as well as wire bit-widths.
Opening the processor selection dialog, one may choose and configure the current
processor:

On the left hand side, each available processor is listed. As for configuration, a layout
may be selected from the list of layouts. Note that the layout does **not** affect the
inner workings of the processor model, only the displayed components. Finally, it is
possible to specify register initializations. These initialization values will be applied to the
registers each time the processor is reset.
As an example processor selection, the following image shows the extended layout of
the RISC-V 5-stage Processor:
The Memory Tab

The memory tab provides a view into the entire addressable address space of the
processor. Navigating the memory may be done as follows:
• Scrolling the memory view
• Go to register will scroll the memory view to the value currently present in the
selected register
• Go to section will scroll the memory view to the address of the given section
value in memory (i.e. the instruction memory .text segment, the static
data .data segment etc). Furthermore, a custom address may be specified
through the "Address..." option.

Whenever the processor is reset, all memory written during program execution will be
reset to its initial state.

The Cache tab

Ripes includes a cache simulator, which you can read more about
here.

I/O tab
Memory-mapped I/O devices
As of version 2.2, Ripes includes various memory-mapped I/O devices. Through these, it
is possible to quickly realize a small embedded system. The following page gives an
overview of the usage of devices, as well as how to create your own.
Usage
Navigating to the I/O tab, the set of available devices are available at (1). Double-
clicking any of these will create the device.
When a device is created, it is automatically assigned a place in the memory map of the
system, and from this, available to be read or written from the processor.

All devices are shown in the (2) area. Every device may be popped-out of being fixed to
this area, if the button (3) is clicked. This allows for navigating to other tabs of the
program, if it is needed to view a device and i.e., the executing program, at the same
time.

On the right-hand side, an overview of the inner details of each device is shown (4).
Some devices may have configuration parameters, such as the number of switches in
the switches device, or the dimensions of an LED matrix. (5) shows the register map of
the device. Here, each entry lists the registers' address (relative to the base offset of the
device), whether the register is read or write only, as well as the bit-width of the
register. (6) shows the symbols exported by the selected device. At minimum, this
contains the base address and size (in bytes) of the device, in the memory map.

The collection of all definitions exported by the currently instantiated devices is shown
in (7). These definitions are available for reference in assembly or C-language programs.

To use access the devices:

• In assembly: the definitions can be used anywhere where an immediate value is


provided.
• In C: #include "ripes_system.h" in the program, and reference the names like any
other #define

Example
The following example is written in C (see Building and Executing C programs with
Ripes for instructions on how to set up the C compiler). For an assembly example, please
see File->Load Example...->Assembly->leds.s.
First, navigate to the I/O tab and instantiate two devices; an LED matrix device
and Switches device. Select the instantiated LED matrix device and, in the right-hand
side Parameters list, adjust the Height parameter to 1, Width parameter to 8, and increase
the LED size. In doing so, notice how the register map as well as the exported symbols
from the LED matrix changes.
Next, navigate to the editor tab and load the File->Load Example...->C-
>switchesAndLeds.c example. In this program, we assign the base addresses of the LED
matrix and switches component to variables, which we will use to read- and write from.
A bitmask is continuously applied to test each bit in the switches register. If set, the LED
at the offset of the toggled switch is written to, in the LED matrix device. Next, press
the build button.
A note on simulation speed:
When accessing devices, we often want to be able to execute the program as fast as
possible, to make device access as interactive as possible. It is therefore recommended
to switch to i.e., the single-cycle processor, as well as to reduce the Max. cache plot
cycles setting, to reduce the overhead from other parts of the simulator.

Navigate to the I/O tab, and press the Run button ( ), to run the program. Now,
when toggling the switches you should see that the corresponding LED lights up in the
LED matrix. Try also to just step through the program (F6). Toggle a switch, and you'll
notice that the latency between your click and the LED lighting up is substantially larger,
due to the reduced clock frequency of the processor.

Adding new devices


Adding a new device consists mainly of defining the behavior of the device, as well as
the visualization for the device. The second part is strictly Qt UI programming, and so
will not be explained here.

Any new devices must inherit from the IOBase class. Please read this header as it
describes each of the functions made available to you when implementing a new device.
Each device must provide a precise description of its interface, namely, its
programmable registers, exported symbols, name, and so forth. For reference, please
see the implementation of a current component, i.e., the IOSwitches class.

The four most important functions for integrating a device into Ripes are:

• uint32_t ioRead(uint32_t offset, unsigned size): Read access from the


processor to a device, requesting to read the value at address offset 0,
reading size bytes. I.e., if a device is placed at base address 0xffff0000 and the
processor reads a word (4 bytes) from memory position 0xffff0004, this function will be
called as ioRead(0x4, 4), and the device must return the value corresponding to the
given offset.
• void ioWrite(uint32_t offset, uint32_t value, unsigned size): Similar as above,
but a write access from the processor to the device.
• void memWrite(uint32_t address, uint32_t value, uint32_t size): A device can
itself call this function during execution to write into simulator memory. address is
an absolute address.
• uint32_t memRead(uint32_t address, uint32_t value): Similar as above, but allows a
device to read a value from simulator memory.

If your peripheral requires to call the Qt widget update function, you must use emit
scheduleUpdate() if updating from within an ioRead or ioWrite call. This is because these
functions are called from within the simulator, which runs on another thread, and
modifications to the Qt UI must be called from the main thread. By calling emit
scheduleUpdate(), we ensure that there is a cross-thread signal emitted, for scheduling
an update of the component in the Qt event loop.

You might also like