The Zynq Processing System (PS), featuring an ARM hard core, offers a powerful platform for embedded applications. This article explores a fundamental method for controlling peripheral hardware from the PS: driving a chasing LED sequence through General Purpose Input/Output (GPIO) functionality. Leveraging a pre-configured GPIO IP core within the Programmable Logic (PL) and connected to the PS via an AXI interface, we can demonstrate basic hardware enteraction using C software.
Software Implementation
Controlling the GPIO peripheral from the PS involves writing a simple C application. The core logic entails sequentially illuminating individual LEDs to create a "chasing" effect, with short delays between each state change. Xilinx provides a set of drivers for its IP cores, simplifying interaction. For GPIO, the XGpio_WriteReg function is used to directly manipulate the GPIO output registers.
The following C code snippet illustrates this concept:
#include "xgpio_l.h"
#include <stdio.h>
#include "xparameters.h"
#include <stdint.h> // For uint32_t
// Define the base address for the GPIO peripheral from xparameters.h
#define GPIO_LED_BASE_ADDRESS XPAR_GPIO_0_BASEADDR
// Simple software-based delay function
void execute_delay(uint32_t outer_loops, uint32_t inner_loops) {
volatile uint32_t i, j; // Use volatile to prevent compiler optimizations
for (i = 0; i < outer_loops; ++i) {
for (j = 0; j < inner_loops; ++j) {
// Empty loop for delay
}
}
}
int main() {
uint32_t current_led_value = 0x01; // Initial pattern for the first LED
const uint32_t data_register_offset = 0x00; // Data register is typically at offset 0 for channel 1
// Infinite loop for continuous LED chasing
while (1) {
// Write the current pattern to the GPIO output register
// XGpio_WriteReg(BaseAddress, RegisterOffset, ValueToWrite)
XGpio_WriteReg(GPIO_LED_BASE_ADDRESS, data_register_offset, current_led_value);
// Wait for a short duration
execute_delay(1000, 10000);
// Shift the pattern to light up the next LED
current_led_value <<= 1;
// If all 4 LEDs have been lit, reset to the first LED (0x01 for the first bit)
if (current_led_value > 0x08) { // After 0x01, 0x02, 0x04, 0x08, the next is 0x10
current_led_value = 0x01;
}
}
return 0; // This line is typically not reached in embedded systems with infinite loops
}
This program utilizes the XGpio\_WriteReg function to directly write to the memory-mapped register of the GPIO IP, accessible at XPAR\_GPIO\_0\_BASEADDR. The pattern 0x01, 0x02, 0x04, 0x08 corresponds to lighting up successive individual LEDs (assuming a 4-bit GPIO output). The execute\_delay function provides a software-based pause between state changes, creating the visual chasing effect.
Hardware Interface Overview (AXI4-Lite)
The connection between the Zynq PS and peripherals in the PL, such as a GPIO controller, is typically managed by the Advanced eXtensible Interface 4 (AXI4) protocol. For simple, register-based access like GPIO, the AXI4-Lite variant is commonly employed. This protocol defines a set of signals for address, write data, read data, and handshake control.
The following VHDL snippet, extracted from an IP instantiation, illustrates some of the fundamental AXI4-Lite port signals used in a typical Xilinx GPIO IP core:
PORT MAP (
s_axi_aclk => s_axi_aclk, -- AXI Clock
s_axi_aresetn => s_axi_aresetn, -- AXI Reset (active low)
s_axi_awaddr => s_axi_awaddr, -- Write Address Channel: Address
s_axi_awvalid => s_axi_awvalid, -- Write Address Channel: Valid signal from master
s_axi_awready => s_axi_awready, -- Write Address Channel: Ready signal from slave
s_axi_wdata => s_axi_wdata, -- Write Data Channel: Data
s_axi_wstrb => s_axi_wstrb, -- Write Data Channel: Byte strobes
s_axi_wvalid => s_axi_wvalid, -- Write Data Channel: Valid signal from master
s_axi_wready => s_axi_wready, -- Write Data Channel: Ready signal from slave
s_axi_bresp => s_axi_bresp, -- Write Response Channel: Response
s_axi_bvalid => s_axi_bvalid, -- Write Response Channel: Valid signal from slave
s_axi_bready => s_axi_bready, -- Write Response Channel: Ready signal from master
s_axi_araddr => s_axi_araddr, -- Read Address Channel: Address
s_axi_arvalid => s_axi_arvalid, -- Read Address Channel: Valid signal from master
s_axi_arready => s_axi_arready, -- Read Address Channel: Ready signal from slave
s_axi_rdata => s_axi_rdata, -- Read Data Channel: Data
s_axi_rresp => s_axi_rresp, -- Read Data Channel: Response
s_axi_rvalid => s_axi_rvalid, -- Read Data Channel: Valid signal from slave
s_axi_rready => s_axi_rready, -- Read Data Channel: Ready signal from master
gpio_io_i => STD_LOGIC_VECTOR(TO_UNSIGNED(0, 4)), -- GPIO input channel 1 (unused for output)
gpio_io_o => gpio_io_o, -- GPIO output channel 1 (connected to LEDs)
gpio2_io_i => STD_LOGIC_VECTOR(TO_UNSIGNED(0, 32)) -- GPIO input channel 2 (unused)
);
In this port map, signals like s\_axi\_awaddr (write address), s\_axi\_wdata (write data), and the associated \_valid and \_ready handshake signals are crucial for the PS (acting as AXI master) to communicate with the GPIO IP (acting as AXI slave). The gpio\_io\_o port represents the actual physical output pins that would be connected to the LEDs. Observing these signals provides a foundational understanding of how the AXI4-Lite protocol facilitates memory-mapped register access between the ARM processor and custom hardware logic.
This demonstration reinforces the concept of memory-mapped I/O and how software running on the PS can directly control hardware peripherals implemented in the PL via the AXI interconnect. Further exploration involves designing custom AXI4-Lite compliant IP cores to create more sophisticated hardware-software interfaces.