Vivado HLS 综合错误

Vivado HLS Synthesize error

我目前正在尝试在 Vivado HLS 上做一些项目。但是,我在合成过程中出现了如标题所示的错误。但是,出现此错误:

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])

这是我的 HLS 代码:

#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);
        }
    }
}

工具抱怨无法处理 imgOut= (0.2126*Imgin[coord] + 0.7152*Imgin[coord+1] + 0.0722*Imgin[coord+2]) 中的二元运算符。二元运算符是具有两个操作数的运算符,此处为 *+。如错误消息中所述,datau32 的类型为 ap_axiu<32, 2, 5, 6>ImginimgOut 具有 datau32 作为基类型。因此,该消息似乎指的是 Imgin[...] 与浮点常数(0.2126 等)

的乘法

总之,ap_axiu是用来声明AXI总线的。它是具有以下格式的结构(参见 UG902 第 110 页,适用于 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;
};

因此,您要做的是将浮点常量与结构相乘。这在 C++ 中是不允许的。

如果您确实打算使用 AXI 总线,则必须使用结构的 data 字段来传送数据。将整数 data 字段与 double 相乘的结果是另一个 doubledouble 是 64 位宽,因此您也必须处理这种差异。您可以使用 float 类型的常量,这可能足够精确。或者你可以让你的 AXI 总线更宽。或者您可以通过强制转换为 float 来降低计算后的精度。或者您可以使用 2 个总线周期来传输单个元素。请注意,如果要将 doublefloat 转换为整数,则必须使用 reinterpret_cast 以避免丢失精度。请注意,您还必须为 ap_axiu 结构的所有其他字段赋值。请注意,您还必须为 ap_axiu 结构(keepstrb 等)的所有其他字段赋值。

使用 AXI 总线的一种更简单的方法是将 inStreamoutStream 声明为数组,例如ap_uint<32> inStream[320*240]。握手(TREADYTVALID)会自动处理。如果你需要所谓的side-channel(剩下的信号如TLASTTUSER),你不能使用这种方法。例如,如果您想要传输数据包而不是连续流(可以使用 TLAST 来完成),或者如果您的数据大小不是总线大小的倍数,那么您可能就是这种情况需要一个字节使能信号(TKEEP)。

我还可以想象您从未打算使用 AXI 总线。有ap_uintap_fixed等类型可以用来在普通总线上提交数据

最后,我想强调的是你应该总是先在软件中调试你的代码。有很多bug,单靠综合的输出是很难解决的。有些消息往往会将人们引向错误的方向。我建议您首先使用 C 语言模拟功能调试您的代码。或者,您可以使用 gcc 等常规 C 编译器在 Vivado HLS 外部编译代码。我还建议使用内存检查器,例如 valgrind 以确保您的代码不会写入数组边界等。该工具并不总能发现这些问题,但它确实会导致硬件无法使用。

我认为这是您正在寻找的解决方案:

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;
    }
  }
}