2

I'm currently trying to do some project on Vivado HLS. However, I got an error as shown in the title during synthesis. However, this error appears:

error:** invalid operands to binary expression ('double' and 'datau32' (aka 'ap_axiu<32, 2, 5, 6>')) imgOut= (0.2126*Imgin[coord] + 0.7152*Imgin[coord+1] + 0.0722*Imgin[coord+2])

This is my HLS code:

#include "core.h"

void imgreading(hls::stream<datau32> &inStream, datau32 Imgin[imagesize])
{
    for(int i=0;i<imagesize;i++)
    {
        Imgin[i]=(datau32)inStream.read();
    }
}

void resize_half(hls::stream<datau32> &inStream, hls::stream<datau32> &outStream)
{
#pragma HLS INTERFACE axis port=inStream
#pragma HLS INTERFACE axis port=outStream
#pragma HLS INTERFACE s_axilite port=return bundle=CRTL_BUS
    datau32 Imgin[imagesize];
    imgreading (inStream,Imgin);
    datau32 imgOut;
    int coord=0;


#pragma HLS DATAFLOW
    for (int a=0; a<240; a++) {
        for(int b=0; b<320; b++){
#pragma HLS PIPELINE II=1
            coord=6*(a*640+b);
            imgOut=  (0.2126*Imgin[coord] + 0.7152*Imgin[coord+1] + 0.0722*Imgin[coord+2]) ;
            datau32 dataOutSideChannel;
            dataOutSideChannel.data = imgOut;
            outStream.write (dataOutSideChannel);
        }
    }
}
Pradeep
  • 9,667
  • 13
  • 27
  • 34

1 Answers1

1

The tools complains that it cannot deal with the binary operators in imgOut= (0.2126*Imgin[coord] + 0.7152*Imgin[coord+1] + 0.0722*Imgin[coord+2]). Binary operators are operators with two operands, here * and +. As mentioned in the error message, datau32 is of type ap_axiu<32, 2, 5, 6>. Imgin and imgOut have datau32 as basetype. Hence, the message appears to be referring to the multiplications of Imgin[...] with the floating-point constants (0.2126, etc.)

Anyway, ap_axiu is used to declare AXI buses. It is a structure with the following format (See page 110 of UG902 for Vivado HLS 2017.1.):

template<int D,int U,int TI,int TD>
struct ap_axiu{
  ap_uint<D> data;
  ap_uint<D/8> keep;
  ap_uint<D/8> strb;
  ap_uint<U> user;
  ap_uint<1> last;
  ap_uint<TI> id;
  ap_uint<TD> dest;
};

So what you are trying to do is multiply a floating-point constant with a structure. That is not allowed in C++.

If you did intent to use an AXI bus, you will have to use the data field of the structure to convey the data. The result of multiplying the integer data field with a double is another double. double is 64-bit wide, so you will have to handle that discrepancy too. You could use constants of type float, which is likely enough precision. Or you could make your AXI bus wider. Or you could reduce the precision after the computation by casting to float. Or you could use 2 bus cycles to transfer a single element. Note that if you want to convert a double or float to an integer, you will have to use reinterpret_cast to avoid losing precision. Note that you will also have to assign values to all other fields of the ap_axiu structure. Note that you will also have to assign values to all other fields of the ap_axiu structure (keep, strb, etc.).

An easier way to use the AXI bus is by declaring inStream and outStream as arrays, e.g. ap_uint<32> inStream[320*240]. The handshaking (TREADY and TVALID) is automatically taken care of. If you need the so-called side-channel (the remaining signals like TLAST or TUSER), you cannot use this method. That may be the case, for example, if you want to transmit packets of data instead of a continuous stream (can be done with TLAST), or if your data size is not a multiple of the bus size such that you need a byte enable signal (TKEEP).

I can also imagine that you never intended to use an AXI bus. There are types such as ap_uint and ap_fixed that can be used to submit data on a plain bus.

Finally, I want to emphasize that you should always debug your code in software first. There are many bugs that are hard to solve based on the output of the synthesis alone. Some messages tend to point people in the wrong direction. I recommend that you debug your code using the C simulation functionality first. Alternatively, you can compile the code outside Vivado HLS with a regular C compiler such as gcc. I also recommend using a memory checker such as valgrind to ensure that your code does not write outside array bounds, etc. The tool does not always find these issues, but it does result in unusable hardware.

I think this is the solution that you are looking for:

void resize_half(ap_uint<32> inAXI[640 * 480 * 3], ap_uint<32> outAXI[320 * 240])
{
#pragma HLS INTERFACE axis port=inAXI
#pragma HLS INTERFACE axis port=outAXI
#pragma HLS INTERFACE s_axilite port=return bundle=CRTL_BUS

#pragma HLS dataflow
  hls::stream<ap_uint<32> > Stream[3];
  for (int i = 0; i < 480; i++)
    for (int j = 0; j < 640; j++)
      for (int k = 0; k < 3; k++)
      {
#pragma HLS PIPELINE II=1
        ap_uint<32> value = inAXI[3 * (640 * i + j) + k];
        if (i % 2 == 0 && j % 2 == 0)
          Stream[k].write(value);
      }

  for (int a = 0; a < 240; a++)
  {
    for (int b = 0; b < 320; b++)
    {
#pragma HLS PIPELINE II=1
      ap_uint<32> x = Stream[0].read();
      ap_uint<32> y = Stream[1].read();
      ap_uint<32> z = Stream[2].read();
      outAXI[320 * a + b] = 0.2126 * x + 0.7152 * y + 0.0722 * z;
    }
  }
}
Gyzuh
  • 171
  • 1
  • 1
  • 5
  • Thanks for the suggestion. I changed the line of code into Imgin.data and now it works. Also, I declare the floating point using ap_fixed. Do you mean to use hls::stream> inStream[640*480] ? – Ong Sie SIong May 11 '18 at 14:05
  • Most of my suggestions are different ways to achieve the same thing, so implementing all of them at the same time will probably not work. If you use `Imgin.data`, you should also set the other fields. Using `hls::stream> inStream[640*480]` makes no sense. That way, you are declaring 640*480 streams. In combination with the `axis` interface, I suggest you use `ap_uint<32> inStream[640*480]` and stop using the `ap_axiu` structure. – Gyzuh May 11 '18 at 14:56
  • I got what you mean, another question: I thought I must use stream interface if I want to use the DMA in Vivado IP Integrator later? – Ong Sie SIong May 11 '18 at 15:14
  • You can use an array or a `hls::stream` and set it as `axis` interface, but not both. Anyway, upon examining your code more closely, I noticed that you are trying to downconvert an image. You are essentially skipping samples, something the tool easily complains about. To help you a bit, I made an implementation that synthesizes, and I believe it should work, but I admit I haven't tested it properly. I put it at the end of my answer. – Gyzuh May 11 '18 at 15:48
  • Actually I'm trying to resize and do grayscale conversion at the same time for an input image of 640*480. The output will be a grayscale image of 320*240. Thanks for the code. I'll try it out. – Ong Sie SIong May 11 '18 at 16:24