CS 21 25.2 Laboratory Exercise 8-9
Memory-Mapped I/O: Software and Hardware
Overview
You will be using both Ripes and Logisim to implement a simple graphical game using a graphical display and hardware-based player input.
Individual or pair activity
This activity is intended to be done either alone or with a partner. You are allowed to choose a partner from any CS 21 25.2 lab section.
To confirm your group (either solo or pair), create a group via https://classroom.github.com/a/Vp1gzQ_y (once per pair). For pairs, have the other member join the group created.
This activity involves two subtasks:
- Simple graphical game in RISC-V
- Logisim-based single-cycle processor with I/O devices
Relevant Links
- Ripes Fork: https://ripes.upd-dcs.work
- Google Classroom (slides, submission bin): https://classroom.google.com/u/2/c/ODQwNDkwODYxODg3
- RISC-V Green Card: https://drive.google.com/file/d/1xSll1ON5cSaOQhoGxpkvr4fKe7lGQFy3/view
- Lab 8-9 Logisim Template: https://drive.google.com/file/d/1addpP44gEbUUp1pAwubxlu0PBFl1fMH6/view?usp=sharing
Part 1: Simple Graphical Game in RISC-V
Overview
For this subtask, you are to implement a simple RISC-V-based game that employs the LED matrix and directional pad devices provided by Ripes.
The game is set in a 10 x 10 grid in which the player controls a single entity that occupies one cell and starts at the top-left-most cell of the grid.
Ripes setup
The program is to assume that Ripes is set up as follows:
- LED matrix:
- Base address of LED matrix (
LED_MATRIX_0_BASE):0xf0000000 - Size of LED matrix (
LED_MATRIX_0_SIZE):0x190 - Width of LED matrix (
LED_MATRIX_0_WIDTH):0xa - Height of LED matrix (
LED_MATRIX_0_HEIGHT):0xa
- Base address of LED matrix (
- Directional pad:
- Base address of directional pad (
D_PAD_0_BASE):0xf0000190 - Size of directional pad (
D_PAD_0_SIZE):0x10 - Address of up register (
D_PAD_0_UP_OFFSET):0xf0000190 - Offset of address of up register (
D_PAD_0_UP_OFFSET):0x0 - Address of down register (
D_PAD_0_DOWN_OFFSET):0xf0000194 - Offset of address of down register (
D_PAD_0_DOWN_OFFSET):0x4 - Address of left register (
D_PAD_0_LEFT_OFFSET):0xf0000198 - Offset of address of left register (
D_PAD_0_LEFT_OFFSET):0x8 - Address of right register (
D_PAD_0_RIGHT_OFFSET):0xf000019c - Offset of address of right register (
D_PAD_0_RIGHT_OFFSET):0xc
- Base address of directional pad (
To ensure that Ripes is set up as described above, do the following:
- Open the I/O tab of Ripes (see panel on left side of Ripes window)
- Close all I/O device windows (e.g., subwindows with titles such as LED Matrix 0 and D-Pad 0); if done correctly, the I/O exports section should show only the following text:
- Double-click the LED Matrix item under the Devices section; an LED matrix window should appear together with information about the LED matrix at the right-hand side of the screen
- In the said information panel, do the following:
- Double-click the value
25to the right of Height, replace it with10, then hitEnterto confirm; the LED matrix displayed should adjust its height accordingly - Double-click the value
25to the right of Width, replace it with10, then hitEnterto confirm; the LED matrix displayed should adjust its width accordingly - Double-click the value
8to the right of LED size, replace it with20, then hitEnterto confirm; the LEDs in the LED matrix should appear much larger
- Double-click the value
- Double-click the D-Pad item under the Devices section; a directional pad window should appear together with information about the directional pad at the right-hand side of the screen
- If done correctly, the I/O exports window should show exactly the following:
#ifndef RIPES_IO_HEADER #define RIPES_IO_HEADER // ***************************************************************************** // * LED_MATRIX_0 // ***************************************************************************** #define LED_MATRIX_0_BASE (0xf0000000) #define LED_MATRIX_0_SIZE (0x190) #define LED_MATRIX_0_WIDTH (0xa) #define LED_MATRIX_0_HEIGHT (0xa) // ***************************************************************************** // * D_PAD_0 // ***************************************************************************** #define D_PAD_0_BASE (0xf0000190) #define D_PAD_0_SIZE (0x10) #define D_PAD_0_UP_OFFSET (0x0) #define D_PAD_0_UP (0xf0000190) #define D_PAD_0_DOWN_OFFSET (0x4) #define D_PAD_0_DOWN (0xf0000194) #define D_PAD_0_LEFT_OFFSET (0x8) #define D_PAD_0_LEFT (0xf0000198) #define D_PAD_0_RIGHT_OFFSET (0xc) #define D_PAD_0_RIGHT (0xf000019c) #endif // RIPES_IO_HEADER
Tip: Automatic Ripes assembler constants
All definitions above (e.g., D_PAD_0_BASE) can be used in your program as though they were defined via .equ (i.e., Ripes automatically has .equ D_PAD_0_BASE 0xf0000190 set).
As an example, li t0, D_PAD_0_BASE will work automatically in Ripes without any having any .equ for D_PAD_0_BASE as long as the I/O panel has the appropriate #define.
Note that you may press the button inside each I/O device subwindow to make them appear even if the current Ripes tab is changed to anything other than the I/O tab.
Movement
Pressing a directional pad button determines the movement of the player entity across the grid. Pressing multiple buttons at the same time has undefined behavior.
The player entity is not allowed to move past the edge of the grid (i.e., attempting to go past any edge will result in the player entity not moving).
Directional pad-based movement should be implemented via polling. While there is no expected speed at which the player entity moves across the grid, it must be possible to realistically finish the game.
Food objects
At the start of the game, seven food objects must be in the grid at the following locations (rows and columns are zero-indexed):
- Row 0, Column 9
- Row 9, Column 0
- Row 9, Column 9
- Row 2, Column 2
- Row 6, Column 7
- Row 1, Column 8
- Row 6, Column 3
Each food object must appear as a white dot (i.e., with color 0x00ffffff) in their specified location.
Consuming food objects
When the player entity collides with a food object (i.e., they share the same grid location), the food object must disappear. Additionally, the player entity must change color based on the number of consumed food objects:
- 1 consumed food object:
0x00ff0000(red) - 2 consumed food objects:
0x00ff7f00(orange) - 3 consumed food objects:
0x00ffff00(yellow) - 4 consumed food objects:
0x0000ff00(green) - 5 consumed food objects:
0x000000ff(blue) - 6 consumed food objects:
0x007f00ff(violet) - 7 consumed food objects:
0x00ff00ff(pink)
Initially, the player entity must have the color 0x00cccccc (gray).
When all the food objects have been consumed, the game must still continue (i.e., there is no indication that all food objects have been consumed).
Data segment constraints
Your implementation must not initialize anything via .data. You are to use the address range starting 0x10000 as a replacement for the data segment. If there are initial values needed, they must be manually initialized by the program.
Instruction constraints
Your implementation must use only the following instructions:
lwswaddsubandorsltaddibeqjalluijalr
While you are allowed to use pseudoinstructions such as li, you must ensure that their equivalent basic instructions fall within the allowed subset above.
Multiplication via repeated addition
You are expected to perform multiplication via repeated addition (ideally via a loop).
Deliverable
Create a RISC-V program Lab 8-9.s that implements the game described above, subject to the listed constraints.
Ensure that this is properly pushed to your Github Classroom repository.
Part 2: Logisim-Based Single-Cycle Processor with I/O Devices
Overview
A Logisim-Evolution template for a working RISC-V single-cycle processor can be downloaded via the link in the previous section. This processor implementation supports the following instructions:
lwswaddsubandorsltaddibeqjallui
Unlike the single-cycle processor in Lab 5, the single-cycle processor in this implementation has its instruction memory and data memory factored out.
In other words, the single-cycle processor is simply a subcircuit called processor that takes in CLK, ReadData, and Instr as inputs, and produces PCCurrent, MemWrite, Address, and WriteData as outputs. The main circuit connects a processor instance with the factored-out instruction and data memories.
main includes two I/O devices: a directional pad and an RGB video component (taken to be an LED matrix).
Address decoder
To provide access to I/O devices (which includes data memory), memory-mapped I/O is employed by using addressdecoder to partition the address space into the following mappings:
0x00000000-0xefffffff: Data memory0xf0000000-0xf000018f: RGB video component0xf0000190-0xf000019f: Directional pad0xf00001a0-0xffffffff: Data memory
Consistency with Ripes
Note that the memory-mapped address ranges above are consistent with the Ripes setup described in the previous section.
The ReadDataSel output of the address decoder is responsible for determining the I/O device source of ReadData which is to be fed to the processor. Of the three I/O devices in main, only data memory and the directional pad are capable of being read from.
For example, Address = 0xf0000194 falls under the segment for the directional pad, so ReadData will be whatever dpadcontroller emits (more on this later).
The MemWrite signal, if set to 1, translates to one of the writeable I/O devices being enabled for writing. The outputs WEMem and WERGB correspond to the write enable signals for data memory and the RGB video component, respectively.
If MemWrite is 1, which write enable signal will be set depends on where Address falls in the memory map stated earlier. As an example, Address = 0xf00000004 falls under the RGB video component, so WERGB must be set to 1 (assuming MemWrite is also 1).
RGB video component
The RGB video component in main is a 10x10 matrix of RGB pixels. Each pixel is described by a 24-bit value 0xRRGGBB where 0xRR, 0xGG, and 0xBB correspond to the intensities of the red, green, and blue components of the resulting color, each as an 8-bit unsigned value (i.e., 0 to 255).
Data persistence outside controller
Note that unlike the LED matrix described in the lecture, the expected controller of the RGB vide omponent should not have registers to store the current color of each pixel.
Instead, the RGB video component itself is able to store the current colors of all pixels.
The rgbcontroller subcircuit is in charge of two things:
- Translating
Addressinto the unsigned 4-bit valuesXRGBandYRGBwhich correspond to zero-indexed column (X) and row (Y) indices, respectively - Taking the lower 24 bits of the 32-bit
WriteDataand emitting them as theWriteDataRGBoutput
Assuming WERGB is set to 1, exactly one pixel can be set to the value of WriteDataRGB during each positive edge of CLK. Which pixel is set is determined by the XRGB and YRGB values set by rgbcontroller.
Each pixel occupies four byte addresses (despite being only a 24-bit value). The first pixel (i.e., row 0, column 0) corresponds to address 0xf0000000. The next pixel (i.e., row 0, column 1) corresponds to address 0xf0000004. The last pixel (i.e., row 9, column 9) corresponds to address 0xf000018c.
Supposing Address is set to 0xf0000188, WriteData is 0x00014421, and WERGB is 1, the pixel in row 9, column 8 will be set to the color with 0x01 for red, 0x44 for green, and 0x21 for blue (i.e., UP Forest Green) during the next positive edge.
Directional pad
The directional pad in main is comprised of four input Pin components (one for each cardinal direction) and switches from 0 to 1 (or vice versa) with the Poke tool.
For each pin, a value of 1 represents the state in which the user is pressing on the corresponding button, while 0 means it is not being pressed.
Each direction corresponds to an address in the segment mapped to the directional pad:
- Up:
0xf0000190 - Down:
0xf0000194 - Left:
0xf0000198 - Right:
0xf000019c
The dpadcontroller subcircuit is in charge of two things:
- Stores the latest value of each direction (i.e., either
0or1) during each positive edge ofCLK - Sets
ReadDataDPadto the 32-bit value corresponding to which directionAddresscorresponds to
The least significant bit of ReadDataDPad must be equal to the stored value of the direction Address corresponds to. All other bits of ReadDataDPad must be set to 0.
As ReadDataDPad depends on Address to select which direction value to read, only one direction value can be retrieved during each tick.
Input pin instead of button
While the button component seems to be more appropriate for the directional pad, there is no way to simultaneously actuate several buttons (which is needed to reset the game).
The input pin component allows this, hence its use over buttons.
jalr support
As the given circuit does not currently support jalr, you are also expected to add support for it.
Deliverable
Provide implementations for the addressdecoder, dpadcontroller, and rgbcontroller subcircuits as described above. Ensure your implementation also supports jalr.
Save your work as Lab 8-9.circ. If done correctly, your RISC-V code from Part 1 should run properly on the Logisim circuit.
Ensure that this is properly pushed to your Github Classroom repository.
Submission Details
- Github Classroom link: https://classroom.github.com/a/Vp1gzQ_y
- Deadline: May 19 (T), 11:59 PM
Two-week deadline and Lab 10
Kindly note that despite the two-week deadline, the timeline for Lab Exercise 10 will be overlapping with that of Lab Exercise 8-9.