Ripes

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:

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:

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.

If you create your own device, do not hesitate to submit a pull request to have it included in the next release of Ripes!