PYNQ: PYTHON PRODUCTIVITY

Properly interfacing with IP from Pynq

Hi all, i have the following code just as a playground to the Pynq and Vivado environment.
foo.cpp

#include "complex.h"

complex_t complex_add(const complex_t a, const complex_t b)
{
    return complex_init(a.real + b.real, a.imaj + b.imaj);
}

complex_t complex_substract(const complex_t a, const complex_t b) {
    return complex_init(a.real - b.real, a.imaj - b.imaj);
}

complex_t complex_multiply(const complex_t a, const complex_t b) {
    return complex_init(a.real * b.real - a.imaj * b.imaj, a.real * b.imaj + a.imaj * b.real);
}


void foo(double re1[50], double im1[50],const double re,const double im, double& I, double& Q){
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS INTERFACE s_axilite port=re1
#pragma HLS INTERFACE s_axilite port=im1
#pragma HLS INTERFACE s_axilite port=re
#pragma HLS INTERFACE s_axilite port=im
#pragma HLS INTERFACE s_axilite port=I
#pragma HLS INTERFACE s_axilite port=Q

	complex_t tmp = complex_init(0, 0);

	for (int i = 0; i < 50; i++) {
		tmp = complex_add(tmp, complex_init(re1[i], im1[i]));
	}
	complex_t out = complex_multiply(tmp, complex_init(re, im));
	I = out.real;
	Q = out.imaj;
}

complex.h

#pragma once

#if !defined(M_PI)
#   define M_PI 3.14159265358979323846
#endif

struct complex {
    double real;
    double imaj;
};
typedef struct complex complex_t;

complex_t complex_init(const double real, const double imaj) {
    complex_t temp;
    temp.real = real;
    temp.imaj = imaj;
    return temp;
}

I want to learn how to give an array to an IP and get some values back but so far i have had no success. This is my Python code,

import numpy as np
foo.write(0x010, 1) # Write to 're'
foo.write(0x01c, 1)  # Write to 'im'
print(foo.read(0x010)) # Prints 1
print(foo.read(0x01c)) # Prints 1

from struct import pack, unpack
print(foo.read(0x200)) # Prints 0
print(foo.read(0x204)) # Prints 0
# First pack value in bytes as double
aux = np.ones((50,1))
aux_double = np.array([pack('d', x) for x in aux])
aux_double
foo.write(0x200, aux_double.tobytes())
foo.write(0x400, aux_double.tobytes())

When i read the memory address where I and Q are stored i always get 0 no matter which way i use to input variables into the two re1 and im1 arrays (see c++ code)

What am i missing?

I’m new to Pynq so this might have an answer in another post i simply don’t know how it’s called (i called it interfacing). In this post he does not uses arrays but a pointer. Is that the way to approach it?

My final goal is to be able to use the cv::Mat object from Vitis Vision library but i’m stuck in this simpler example. Any help, advice or even tutorial will be appreciated.

You need to start your IP by writing 1 to bit 0 of the control register (at offset 0x0).

Check this example for your end goal: https://github.com/Xilinx/PYNQ-HelloWorld/ (uses xf::cv::resize)

Cathal

Hey Cath, How should i do that?

resizer.write(0x00,0x81) # start

As used in the resizer example does not change anything. Neither does writing a packed 1.

Yes, exactly. 0x81 also “auto restarts” so if you keep feeding data, the IP will keep processing it. If you use 0x1, you have to write the start value every time you want the IP to run.

What do you mean by this?:

As used in the resizer example does not change anything. Neither does writing a packed 1.

Cathal

I meant that command didn’t change anything.
Here is the memory layout,

0x010 : Data signal of re

    bit 31~0 - re[31:0] (Read/Write)
0x014 : Data signal of re

    bit 31~0 - re[63:32] (Read/Write)
0x018 : reserved

0x01c : Data signal of im bit 31~0 - im[31:0] (Read/Write)

0x020 : Data signal of im bit 31~0 - im[63:32] (Read/Write)

0x024 : reserved

0x028 : Data signal of I

    bit 31~0 - I[31:0] (Read)
0x02c : Data signal of I

    bit 31~0 - I[63:32] (Read)
0x030 : Control signal of I

    bit 0  - I_ap_vld (Read/COR)
    others - reserved
0x040 : Data signal of Q bit 31~0 - Q[31:0] (Read)

0x044 : Data signal of Q

    bit 31~0 - Q[63:32] (Read)
0x048 : Control signal of Q

    bit 0  - Q_ap_vld (Read/COR)
    others - reserved
0x200 ~ 0x3ff : Memory 're1' (50 * 64b)

    Word 2n   : bit [31:0] - re1[n][31: 0]
    Word 2n+1 : bit [31:0] - re1[n][63:32]
0x400 ~ 0x5ff : Memory 'im1' (50 * 64b)

    Word 2n   : bit [31:0] - im1[n][31: 0]
    Word 2n+1 : bit [31:0] - im1[n][63:32]

and here a snippet of what i tried. I don’t see an error in the C++ code for the variables I and Q to still be 0.


The final foo.read(0x02c) should be the lower part of the double variable I.

Hi @cathalmccabe, this is precisely my issue when i refer to interfacing with OpenCV constructs.
I would like to know (as in read and learn more) what ways exist to interface an IP with the outside world (PYNQ). Is it with the stream construct, or the vector construct, or also simply passing a pointer like in here and writting it to a cv::Mat object (which has raised issues as in the first link)?.
Please consider that i’m asking from a novice point of view, so some things might be obvious to you that are not to me.
Thanks in advance.