PYNQ: PYTHON PRODUCTIVITY FOR ZYNQ

How to write and read image using ACP port, Jupyter

Hello,

I am following the belows tutorial about creating a custom filter with Vivado HLS:
Part 1
Part 2

I have generated the bitstream file and copied to my Pynq board. Could you help me to write the code in Jupyter to send an image and receive the processed image?
I think that I can´t use MMIO to read ACP interface. Do you have another example similar to this?
I am almost starting with FPGAs, any help will be appreciated.

Thanks!

Check this guide to the Zynq interfaces:
https://pynq.readthedocs.io/en/v2.4/overlay_design_methodology/pspl_interface.html

MMIO reads/writes salve registers over the AXI GP ports. The Master side of the port is on the PS side - i.e. the PS is reading/writing registers in the PL.

The ACP and the HP ports are AXI masters on the PL. i.e. the PL can read/write into the PS memory system. In the case of the ACP, this goes through the PS cache controller.

If you are new to Zynq, you almost definitely don’t want to use the ACP. The cache is shared with the CPU. If the data the PL needs is not in the cache, you can cause cache thrashing which won’t help the performance of the PL IP, and will cause the CPUs running your OS and potentially other software to experience cache misses and slow down the whole system.

If you want the PL to access PS memory directly, use the HP ports. There is no difference in the IP you create. It will have the same AXI interface to connect to either HP or ACP.

Your IP can access DRAM directly, and this memory is shared with the PS (i.e. where your OS is running) so you need to make sure to allocate a memory buffer in the OS, and pass it to the PL.

One example of doing this with PYNQ is with the DMA example. See example here:
https://github.com/Xilinx/PYNQ_Workshop/blob/master/Session_4/6_pynqtutorial_dma_updated.ipynb

Cathal

Hi Cathal,

I have been following your advice and I have changed it to HP port. I thought that just the ACP port could communicate PL and PS without the DMA, after reading the tutorial. I would like to try without DMA first, and later with DMA and streaming. Below I have pasted the code from Jupyter

import pynq
import pynq.lib.dma
import numpy as np

from PIL import Image
from IPython.display import display
from pynq import Overlay
from pynq import DefaultIP
from pynq import DefaultHierarchy
from pynq import Xlnk
from pynq import MMIO
base = Overlay("/home/xilinx/pynq/overlays/tuto_conv/top.bit")
image_path = "/home/xilinx/jupyter_notebooks/TUTORIALS/Tuto_conv/test.jpg"
original_image = Image.open(image_path)
original_image.load()
input_array = np.array(original_image)
input_image = Image.fromarray(input_array)
display(input_image)
xlnk=Xlnk()
in_buffer = xlnk.cma_array(shape=(360,640,3), dtype=np.uint8, cacheable=1)
out_buffer = xlnk.cma_array(shape=(360,640,3), dtype=np.uint8, cacheable=1)
hex(in_buffer.physical_address)
hex(out_buffer.physical_address)
in_buffer[0:360*640*3] = input_array
buf_image = Image.fromarray(in_buffer)
display(buf_image)
IP_BASE_ADDRESS = 0x43C00000
ADDRESS_RANGE = 0x10000
mmio = MMIO(IP_BASE_ADDRESS, ADDRESS_RANGE)

# 0x10 : Data signal of image_in      bit 31~0 - image_in[31:0] (Read/Write)
mmio.write(0x10,0x18100000)
# 0x18 : Data signal of image_out     bit 31~0 - image_out[31:0] (Read/Write)
mmio.write(0x18,0x18200000)
buf_image_out = Image.fromarray(out_buffer)
display(buf_image_out)

As I expected the output image is black. Could you tell me if this is the right way?

THank you so much

What is your IP supposed to be doing?
It would help if you could post the code for it, and show how your system is connected.

The ACP and HP ports are both AXI masters. They don’t need a DMA, but an IP in the PL needs to initiate read and write transactions. i.e. it needs to be the AXI Master.
The ACP affects cache. The HP doesn’t. Unless you really understand the implications of this, just use the HP ports, and ignore the ACP.

Cathal

The code of the IP is the same that shown in the tutorial, is a 2D concolution filter:
#include <hls_video.h>
#include <stdint.h>

// Get 1080x1920 HD grey image as input (from “image_in” array)
// and do 2D convolution on it with a hard coded 3x3 kernel.
// Processed image is stored in “image_out” array.

void conv(uint8_t image_in[360640],uint8_t image_out[360640]){
#pragma HLS INTERFACE s_axilite port=return bundle=CRTL_BUS
#pragma HLS INTERFACE m_axi depth=691200 port=image_in offset=slave bundle=CRTL_BUS
#pragma HLS INTERFACE m_axi depth=691200 port=image_out offset=slave bundle=CRTL_BUS
#pragma HLS DATAFLOW
const char coefficients[3][3] = { {-1,-2,-1},
{ 0, 0, 0},
{ 1, 2, 1} };

hls::Mat<360,640,HLS_8UC1> src; // Mat format variables to store images
hls::Mat<360,640,HLS_8UC1> dst;
// Convert from axi format to Mat the input image
hls::AXIM2Mat<640,uint8_t,360,640,HLS_8UC1>(image_in,src);
// Declare a variable kind window for the coefficients
hls::Window<3,3,char> kernel;
// Convert the coefficients to hls format
for (int i=0;i<3;i++){
for (int j=0;j<3;j++){
kernel.val[i][j]=coefficients[i][j];
}
}
// indicates the relative position of a filtered point within the kernel
hls::Point_ anchor = hls::Point_(-1,-1); // means the center of the kernel
hls::Filter2D(src,dst,kernel,anchor);
// Convert to Axi format
hls::Mat2AXIM<640,uint8_t,360,640,HLS_8UC1>(dst,image_out);
}

Thank you!