PYNQ: PYTHON PRODUCTIVITY

Implementing Convolution beginner questions

Hello !

Am working on a little project to make a convolution core, and i have been surprised by the fact hls_video.h and hls_opencv.h have been deprecated, making the tutorials i was following much less useful.

So now i am trying to implement the Conv2D with LineBuffer that is in the examples of Vivado HLS. ( and also in this link )

Can anyone guide me a little i want to pass an image from to the program on the testbench, How can i do it without the openCV libraries ?

After i export the generated design to Vivado i get an “ap_ctrl” port but Vivado doesn’t let me connect GPIO nor anything to it,

How can i control the core ?

If i put a Constant with “1” would that mean that the core would be always on ?

When i try to validate the design Vivado tells me it lacks a “Tlast” which i understood was important for AXI_Stream, Where can i get this signal from ?
is it one of these DST_TVALID or DST_TREADY on the 11x11 filter core that i should wire to AXI2mm_tlast ?

one last question is , supposing i got ta bitstream and then ran it on the PYNQ ,
how would i be able to pass ta test image to it ?
at which address should i send it with python ?

I suspect it should be an address on the DMA that i should configure on the adress bar on vivado , but i am still a bit confused and the AXI documentation i s super vast and i got lost on there mayt times.

thank you for helping me !

Hi,
Unfortunately there are a few issues with that example for what you are trying to do.

I’d suggest you use an AXI lite interface for the control port. This is a memory mapped interface, so you can then control your IP (start/stop, status) by reading/writing registers in the IP that are generated as part of the AXI lite interface.
You can do this by adding this inside your function:
#pragma HLS INTERFACE s_axilite port=return
You can then start your IP by writing the value 0x81 to register zero of the IP.
You can also “bundle” the height/width parameters into this AXI lite interface. They will get their own register that you can use to configure these values

#pragma HLS INTERFACE s_axilite port=height

#pragma HLS INTERFACE s_axilite port=width

(For reference, the ap_ctrl port is a bundle of 4 wires. You should be able to expand it and connect GPIO to each individual wire - but I’m suggesting you switch this to an AXI lite interface)

TLAST is needed by the DMA. For the sideband signals, which includes TLAST, you can’t use the hls:stream type in that example.
[Vitis HLS aggregates xf::cv::ap_axiu - Community Forums][Vitis HLS aggregates xf::cv::ap_axiu - Community Forums)
You can also see an example I’ll link below.

To send an image you allocate some memory and do a DMA transfer to the IP. The DMA reads from memory and streams (through the AXI stream interface) to the IP. You use the DMA to read from the output stream of the IP and it writes back to memory.

You can find a DMA tutorial here:
https://github.com/Xilinx/PYNQ_Workshop/blob/master/Session_4/6_dma_tutorial.ipynb

You might find this example helpful, particularly for the TLAST:
https://github.com/Xilinx/PYNQ-HelloWorld/
Source code:
https://github.com/Xilinx/PYNQ-HelloWorld/blob/master/boards/ip/hls/resize/xf_resize_accel.cpp

1 Like

Thank you a lot for this very detailed answer !
If i understand correctly once i redesign everything, my python JupyterNotebook should talk to :
1-The AXI lite (by writing 0x81 which i guess is (10000001)b)
2-The DMA (by sending the image)

I will find the addresses i should write to in the hwh generated by Vivado HLS ?

Thanks again for your time and very complete answer !

Apologies, I should have explained that part further.

When you generate the IP with an AXI lite interface, a control register is generated at offset 0.

An example C driver will also be generated. In your HLS “solution” directory, you should be able to find the driver here:
\impl\ip\drivers\your_ip_name\src
If you check this file, you should be able to find the info below.
your_ip_name_hw.h

// control
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved

This is a convention, but by default all HLS IP will have the control register mapped here if you use the pragma I mentioned in the previous reply.
#pragma HLS INTERFACE s_axilite port=return

0x81 is setting ap_start, and also auto_restart. (auto restart is optional, you can write a 0x1 every time you want your IP to run instead).

You can also read this register to check the status of the IP (bits 1,2,3)

In the same header you should find details for any other registers in your design. For example, the offset for the heigh and width registers if you choose to include them.

Cathal

1 Like

Hello !
I still have some more questions regarding the TLAST, if am not too being too much trouble.

I have been looking at the posts you directed me to, and am indeed suffering from their same problem.

I understood that the workaround has been to add this condition that implements a ‘1’ at the end of the output array from the operation ( dst[size].last ).

and i am not able to do it this simply as on the examples you pointed me to, as you expected the hls:stream type doesn’t store an array to let me append a ‘1’ to it.

I cannot therefore keep the code in the example in Vivado HLS, are there no simple fix to refactor the code to make it generate the TLAST ?

I have been thinking of making the T type in the prototypes of the Conv Example similar to the AXIS_T in the resize tutorial examples you sent me and then add that condition.
but i am a bit confused since the vertical and horizontal Convolutions seem to be unrolled in different for loops.

Thanks again for your time and attention.

You can’t use the hls:stream type for this. You can see the workaround to use a custom struct and have a “last”.
In the code you copied, this is the axis_t type which is this (in header file):

struct axis_t {
ap_uint<24> data;
ap_int<1> last;
};

If you call the variable in the struct last it will be recognized as the AXI sideband TLAST signal.

It gets synthesized with this:
#pragma HLS INTERFACE axis port=src //depth=384*288 // Added depth for C/RTL cosimulation

There is more than one way to code this so you may see different variations in different examples.

Cathal

1 Like

Hi @cathalmccabe,

I’ve tried that workaround on another project, using the functions from the HelloWorld repo, however I am getting the following error using Vitis 2020.2,

type 'hls::stream<DATATYPE, 2>' (aka 'stream<ap_uint<8>, 2>') does not provide a subscript operator

Do you know what could be causing that?

Also, in the new Composable pipeline repo, the author stops using that pre-defined conversion functions and starts using xf::cv::AXIvideo2xfMat. Does that mean that Vitis libraries streaming functions are working know?

Another question, what would be the benefits of using an AXI streaming + DMA besides using AXI memory mapped, for example, when implementing Vitis vision library functions? What would be the differences, pros and cons about the two options?

Thank you in advance!

Pedro