This tutorial contains information on how to create a module in PL that can access to DDR memory without AXI DMA. The module does a black and white image invert operation.
Source Code
This repository contains all of the code required in order to follow this tutorial.
This RTL module does a simple image invert operation for black and white image. It has one 8-bit input and one 8-bit output. Every input will be subtracted from 255 to produce the output.
1.2. Controller to Access DDR Memory
To access DDR memory from PL, we are going to use AXI Data Mover IP. This IP handles the AXI-Full protocol to provide access to DDR memory.
To use the AXI Data Mover IP, we need a simple state machine that generated instruction to control the AXI Data Mover IP.
The state machine consists of core (img_inv_ctrl.v) and AXI-Lite wrapper (axi_img_inv_ctrl.v).
This is the code for img_inv_ctrl.v.
This is the code for axi_img_inv_ctrl.v.
1.3. System Design
This diagram shows our system. It consists of an ARM CPU, DRAM, AXI Image Invert Control (), AXI Data Mover, and our AXI-Stream image invert module. Our AXI-Stream image invert module is connected to the AXI Data Mover. Between them, we also add AXI-Stream FIFO IP.
The following figure shows the Zynq IP high-performance port configuration. There are two pots enabled, which are AXI HP0 and AXI HP2.
The following figure shows the AXI Data Mover configuration.
This is the final block design diagram as shown in Vivado.
2. Software Design
First, we need to create access to the state machine module for the AXI Data Mover. Because it is just a memory map IP, so we use MMIO.
Prepare the input image. Declare several black and white image as arrays. The image is MNIST digit image 28x28 pixels.
This is the input image plotted by pyplot.
Process the image bytes with the image invert module.
Read and check the result.
This is the output image plotted by pyplot.
3. Full Step-by-Step Tutorial
This video contains detailed steps for making this project.
4. Conclusion
In this tutorial, we covered a tutorial on how to create a module in PL that can access to DDR memory without AXI DMA, but using AXI Data Mover. This method is suitable if the main control in in PL.
# Access to memory map of the axi_inv_img_ctrl
ADDR_BASE = 0xA0000000
ADDR_RANGE = 0x80
ctrl_obj = MMIO(ADDR_BASE, ADDR_RANGE)
# MNIST image as text
img_txt_1 = b'0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,124,253,255,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,244,251,253,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,251,251,253,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,236,251,211,31,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,228,251,251,94,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,253,253,189,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,253,251,235,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,205,253,251,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104,251,253,184,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,240,251,193,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,253,253,253,159,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,151,251,251,251,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,221,251,251,172,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,234,251,251,196,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,251,251,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,159,255,253,253,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,228,253,247,140,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,251,253,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,251,253,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,193,253,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0'
img_txt_6 = b'0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,222,225,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,147,234,252,176,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,197,253,252,208,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,178,252,253,117,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,252,252,253,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,222,253,253,79,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,131,252,179,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,198,246,220,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,79,253,252,135,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,140,253,252,118,0,0,0,0,111,140,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,191,255,253,56,0,0,114,113,222,253,253,255,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,76,252,253,223,37,0,48,174,252,252,242,214,253,199,31,0,0,0,0,0,0,0,0,0,0,0,0,13,109,252,228,130,0,38,165,253,233,164,49,63,253,214,31,0,0,0,0,0,0,0,0,0,0,0,0,73,252,252,126,0,23,178,252,240,148,7,44,215,240,148,0,0,0,0,0,0,0,0,0,0,0,0,0,119,252,252,0,0,197,252,252,63,0,57,252,252,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,253,174,0,48,229,253,112,0,38,222,253,112,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,252,173,0,48,227,252,158,226,234,201,27,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,252,252,57,104,240,252,252,253,233,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,242,252,253,252,252,252,252,240,148,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75,189,253,252,252,157,112,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0'
img_txt_8 = b'0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,203,229,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,47,47,30,95,254,215,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,154,185,185,223,253,253,133,175,255,188,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,253,253,253,246,161,228,253,253,254,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,245,253,158,137,21,0,48,233,253,233,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,254,223,25,0,0,36,170,254,244,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,212,253,161,11,26,178,253,236,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,155,253,228,80,223,253,253,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,253,253,253,254,253,154,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,253,253,253,254,179,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,171,254,254,254,179,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,171,253,253,253,253,178,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,123,254,253,203,156,253,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,253,254,121,13,93,253,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,239,253,76,8,32,219,253,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,133,254,191,0,5,108,234,254,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,253,190,5,85,253,236,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,253,169,192,253,253,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,253,253,254,236,129,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,118,243,191,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0'
# Convert MNIST image from text to numpy array
img_bytes = np.array([int(i) for i in img_txt_8.decode('utf-8').split(',')], dtype=np.uint8)
img_bytes.shape
# Convert image array to 2D for plotting
img_bytes_2d = np.reshape(img_bytes, (28, 28))
img_bytes_2d.shape
# Plot original image before inversion
pyplot.imshow(img_bytes_2d, cmap=pyplot.get_cmap('gray'))
# Allocate physical memory for AXI data mover MM2S and S2MM
input_buffer = allocate(shape=img_bytes.shape, dtype=np.uint8)
output_buffer = allocate(shape=img_bytes.shape, dtype=np.uint8)
# Copy original image from numpy array to physical memory
np.copyto(input_buffer, img_bytes)
print("Input buffer address :", hex(input_buffer.physical_address))
# Setup AXI data mover instruction
ctrl_obj.write(0x0, input_buffer.physical_address)
ctrl_obj.write(0x8, output_buffer.physical_address)
ctrl_obj.write(0x10, len(img_bytes))
# Start main controller
ctrl_obj.write(0x18, 1)
# Wait until ready flag is 1
while ((ctrl_obj.read(0x18) & (1 << 1)) == 0):
pass
# Convert inverted image array to 2D for plotting
img_bytes_2d_inv = np.reshape(output_buffer, (28, 28))
img_bytes_2d_inv.shape
# Plot image after inversion
pyplot.imshow(img_bytes_2d_inv, cmap=pyplot.get_cmap('gray'))
# Delete buffer to prevent memory leak
del input_buffer, output_buffer