🛠 Controlling GPIOs on Sapphire RV64
Objectives
This tutorial guides you through configuring and using the General Purpose Input/Output (GPIO) interfaces of the Sapphire RV64 SoC. You will learn how to enable I/O peripherals, understand their memory addressing via the AXI4/APB3 bus, and manipulate registers to light an LED or read a button. This is the essential first step for any embedded project requiring physical interaction.
Required Hardware
- A compatible Efinix FPGA board (Titanium, Topaz, or Trion T20+ series).
- The Efinity design tool (version 2026.1 or newer).
- A RISC-V development environment (compiler, toolchain).
- Simple external components: an LED, a resistor (330 Ohm), and a push-button with a pull-up resistor.
Steps
- Configure the GPIO peripheral: In Efinity, open your Sapphire RV64 project. Access the SoC configuration and ensure the GPIO block is enabled. Select the desired number of pins (up to 32 GPIOs available). Configure the bus width (typically 32 bits for the data register).
- Generate the bitstream: Generate the FPGA bitstream. This step synthesizes the VexiiRiscv core and peripherals, including GPIOs, onto the FPGA. Upload the bitstream to your board.
- Identify memory addresses: Consult the documentation generated by Efinity to find the GPIO base address. For example, assume the base address is
0x10000000. Typical registers include: Direction (0x00), Data (0x04), and Mask (0x08).
- Write the C code: Create a
main.cfile. Define pointers to the GPIO registers.
#include <stdint.h>
#define GPIO_BASE 0x10000000
#define GPIO_DIR (*(volatile uint32_t *)(GPIO_BASE + 0x00))
#define GPIO_DATA (*(volatile uint32_t *)(GPIO_BASE + 0x04))
void main() {
// Configure pin 0 as output (bit 0 set to 1)
GPIO_DIR |= (1 << 0);
while(1) {
GPIO_DATA ^= (1 << 0); // Toggle LED
for(volatile int i=0; i<100000; i++); // Simple delay
}
}
- Compile and flash: Compile the code with the appropriate RISC-V compiler (RV64IMACFD target). Load the binary into the SoC memory via JTAG or UART depending on your configuration.
Verification
Once the program runs, the LED connected to GPIO pin 0 should blink at regular intervals. If you use a button on another pin configured as input, you can add a read of GPIO_DATA to check the logical state.
Further Reading
Explore using GPIO interrupts to react to state changes without polling loops, or configure multiple GPIO groups to control 7-segment displays.
Other tutorials for this board
- Getting Started with the Sapphire RV64 Board
- Simple UART Automation with Sapphire RV64
- Getting Started with Sapphire RV64 on Efinix FPGA
- Configuring GPIOs and Driving Peripherals on Sapphire RV64
- Deploying an IoT Node with Sapphire RV64: Network and Storage
- Getting Started with Sapphire RV64 SoC on Efinix FPGA
Code examples for this board
gpio_blink_riscv.inoC (RISC-V)uart_echo_riscv.inoC (RISC-V)spi_sensor_read.inoC (RISC-V)i2c_master_write.inoC (RISC-V)timer_interrupt_handler.inoC (RISC-V)i2c-bmp280-temperature-6.inoC