Vivado HLS2019.2版本,进行C++开发遇到的一些问题

一、Run C Simulation遇到报错

1.原因 

在初次接触 Vivado HLS 这款软件时,笔者仅知晓其能够借助 C 或 C++ 编程语言实现相关算法。基于过往经验,笔者默认认为其编程范式与 Visual Studio 完全一致。为快速入门,笔者在 优快云 搜索并浏览了若干关于 HLS 的基础教程帖子,初步感觉实现难度不高,遂决定动手编写小型示例代码以加深对该技术的理解。然而,在实际编写与调试过程中,却频繁遭遇各类问题,这使得笔者深刻意识到 Vivado HLS 与 Visual Studio 在编程实践上存在着诸多差异,也由此开启了对 Vivado HLS 编程特性的深入探索之旅。

2.笔者遇到的情况导致报错

笔者将主函数int main直接写入到了signalConvolve.cpp,因为在任意的C或者C++程序中,顶层函数都称为main(),但是在Vivado HLS的设计流程中,工程师们可以将main()下的任意子函数指定为综合的顶层函数,无法综合顶层函数main()

Vivado HLS 对可以被综合成顶层函数的规则如下:

(1)仅允许指定1个函数作为综合的顶层函数

(2)层级中综合的顶层函数下的任意子函数也会被同样进行综合

(3)如果要综合的函数不包含在综合的顶层函数下的层级内,则必须将此类函数合并到综合的单一顶层函数中

signalConvolve.h

#ifndef FIR_FILTER_H
#define FIR_FILTER_H

#define FILTER_LENGTH 10       // 滤波器阶数
#define SIGNAL_LENGTH 1024    // 输入信号长度

// 计算汉明窗
void hammingWindow(int N, double* window);

// 设计低通FIR滤波器系数
void designLowPassFIR(int N, double cutoffFrequency, double* filterCoefficients);

// 卷积运算函数
void convolve(double* inputSignal, double* filter, double* outputSignal);

#endif

signalConvolve.cpp

#include <iostream>
#include <cmath>
#include "signalConvolve.h"



#include <hls_stream.h>

// 计算汉明窗
void hammingWindow(int N, double* window) {
    for (int n = 0; n < N; ++n) {
        window[n] = 0.54 - 0.46 * cos(2 * M_PI * n / (N - 1));   
    }
}

// 设计低通FIR滤波器系数
void designLowPassFIR(int N, double cutoffFrequency, double* filterCoefficients) {
    int M = N / 2;  // 滤波器的中心对称点
    double normCutoff = 2 * cutoffFrequency;  // 标准化截止频率

    // 计算理想低通滤波器的系数 
    for (int n = 0; n < N; ++n) {
        if (n == M) {
            filterCoefficients[n] = normCutoff / M;
        } else {
            filterCoefficients[n] = sin(normCutoff * (n - M)) / (M * (n - M));
        }
    }

    // 应用汉明窗
    double window[N];
    hammingWindow(N, window);
    for (int n = 0; n < N; ++n) {
        filterCoefficients[n] *= window[n];
    }
}

// 卷积运算函数
void convolve(double* inputSignal, double* filter, double* outputSignal) {
    #pragma HLS PIPELINE
    for (int n = 0; n < SIGNAL_LENGTH + FILTER_LENGTH - 1; ++n) {
        outputSignal[n] = 0;
        for (int k = 0; k < FILTER_LENGTH; ++k) {
            int idx = n - k;
            if (idx >= 0 && idx < SIGNAL_LENGTH) {
                outputSignal[n] += inputSignal[idx] * filter[k];
            }
        }
    }
}





// 生成输入信号
void generateInputSignal(double* inputSignal, int length) {
    for (int i = 0; i < length; ++i) {
        inputSignal[i] = sin(2 * M_PI * i / 50.0);  // 输入信号:正弦波
    }
}

int main() {
    // 分配内存
    double inputSignal[SIGNAL_LENGTH] = {0};
    double filterCoefficients[FILTER_LENGTH] = {0};
    double outputSignal[SIGNAL_LENGTH + FILTER_LENGTH - 1] = {0};

    // 生成输入信号
    generateInputSignal(inputSignal, SIGNAL_LENGTH);

    // 设计低通滤波器
    double cutoffFrequency = 0.2;  // 截止频率
    designLowPassFIR(FILTER_LENGTH, cutoffFrequency, filterCoefficients);

    // 执行卷积
    convolve(inputSignal, filterCoefficients, outputSignal);

    // 打印卷积结果
    std::cout << "卷积结果:" << std::endl;
    for (int i = 0; i < SIGNAL_LENGTH + FILTER_LENGTH - 1; ++i) {
        std::cout << outputSignal[i] << " ";
    }
    std::cout << std::endl;

    return 0;
}

3.解决方法

signalConvolve.h

#ifndef FIR_FILTER_H
#define FIR_FILTER_H

#define FILTER_LENGTH 10       // 滤波器阶数
#define SIGNAL_LENGTH 1024    // 输入信号长度

// 计算汉明窗
void hammingWindow(int N, double* window);

// 设计低通FIR滤波器系数
void designLowPassFIR(int N, double cutoffFrequency, double* filterCoefficients);

// 卷积运算函数
void convolve(double* inputSignal, double* filter, double* outputSignal);

#endif

signalConvolve.cpp

#include "signalConvolve.h"
#include <cmath>
#include <hls_stream.h>

// 计算汉明窗
void hammingWindow(int N, double* window) {
    for (int n = 0; n < N; ++n) {
        window[n] = 0.54 - 0.46 * cos(2 * M_PI * n / (N - 1));   
    }
}

// 设计低通FIR滤波器系数
void designLowPassFIR(int N, double cutoffFrequency, double* filterCoefficients) {
    int M = N / 2;  // 滤波器的中心对称点
    double normCutoff = 2 * cutoffFrequency;  // 标准化截止频率

    // 计算理想低通滤波器的系数 
    for (int n = 0; n < N; ++n) {
        if (n == M) {
            filterCoefficients[n] = normCutoff / M;
        } else {
            filterCoefficients[n] = sin(normCutoff * (n - M)) / (M * (n - M));
        }
    }

    // 应用汉明窗
    double window[N];
    hammingWindow(N, window);
    for (int n = 0; n < N; ++n) {
        filterCoefficients[n] *= window[n];
    }
}

// 卷积运算函数
void convolve(double* inputSignal, double* filter, double* outputSignal) {
    #pragma HLS PIPELINE
    for (int n = 0; n < SIGNAL_LENGTH + FILTER_LENGTH - 1; ++n) {
        outputSignal[n] = 0;
        for (int k = 0; k < FILTER_LENGTH; ++k) {
            int idx = n - k;
            if (idx >= 0 && idx < SIGNAL_LENGTH) {
                outputSignal[n] += inputSignal[idx] * filter[k];
            }
        }
    }
}

仿真测试文件

signalConvolve_tb.cpp

#include <iostream>
#include <cmath>
#include "signalConvolve.h"

// 生成输入信号
void generateInputSignal(double* inputSignal, int length) {
    for (int i = 0; i < length; ++i) {
        inputSignal[i] = sin(2 * M_PI * i / 50.0);  // 输入信号:正弦波
    }
}

int main() {
    // 分配内存
    double inputSignal[SIGNAL_LENGTH] = {0};
    double filterCoefficients[FILTER_LENGTH] = {0};
    double outputSignal[SIGNAL_LENGTH + FILTER_LENGTH - 1] = {0};

    // 生成输入信号
    generateInputSignal(inputSignal, SIGNAL_LENGTH);

    // 设计低通滤波器
    double cutoffFrequency = 0.2;  // 截止频率
    designLowPassFIR(FILTER_LENGTH, cutoffFrequency, filterCoefficients);

    // 执行卷积
    convolve(inputSignal, filterCoefficients, outputSignal);

    // 打印卷积结果
    std::cout << "卷积结果:" << std::endl;
    for (int i = 0; i < SIGNAL_LENGTH + FILTER_LENGTH - 1; ++i) {
        std::cout << outputSignal[i] << " ";
    }
    std::cout << std::endl;

    return 0;
}

二、C Synthesis遇到报错

1.出现报错现象

ERROR: [SYNCHK 200-61] HLS_SignalConvolve/src/signalconvolve.cpp:38: unsupported memory access on variable 'outputSignal' which is (or contains) an array with unknown size at compile time.

导致C Synthesis报错的代码,如下图红色框标记

2.原因和解决方法

原因:这个错误是由于在 Vivado HLS 中,数组的大小必须在编译时已知(即是一个常量),但是你在代码中使用了动态大小的数组 outputSignal,这是 Vivado HLS 不支持的。

解决方法:

在 Vivado HLS 中,必须确保 outputSignal 数组的大小在编译过程中已明确确定。这是由于 Vivado HLS 无法在程序执行时进行动态内存分配,所以设计中所有的数组容量都需要通过常量或基于常量的表达式来预先设定。

举个例子,将 outputSignal 的容量设置为 SIGNAL_LENGTH + FILTER_LENGTH - 1(这里的 SIGNAL_LENGTH 和 FILTER_LENGTH 应均为常量),其目的在于使整个数组在编译时就能被工具识别并正确处理。请特别注意,若未能满足这一条件,后续代码的综合与仿真环节可能会中断,并且生成的硬件描述语言(HDL)代码也可能存在潜在错误,进而影响开发项目中基于 HLS 部分的功能实现与系统整体性能。

所以,在开始详细编写 HLS 代码或进行相关逻辑设计前,需仔细判断所有数组、指针及相关内存操作单元所涉及的容量参数,确保没有包含无法在初期已知的变长问题,如果存在动态需求,可能需要重构算法逻辑、利用 Vivado HLS 的流接口以及其他帧控制机制,将其转化为可在编译时刻得知的足够多的硬件资源富裕情况下的静态数据流操作。

3.更新代码如下

signalConvolve.h

#ifndef FIR_FILTER_H
#define FIR_FILTER_H

#define FILTER_LENGTH 10       // 滤波器阶数
#define SIGNAL_LENGTH 1024    // 输入信号长度
#define OUTPUT_LENGTH (SIGNAL_LENGTH + FILTER_LENGTH - 1) // 输出信号长度

// 计算汉明窗
void hammingWindow(int N, double window[]);

// 设计低通FIR滤波器系数
void designLowPassFIR(int N, double cutoffFrequency, double filterCoefficients[]);

// 卷积运算函数
void convolve(double inputSignal[SIGNAL_LENGTH], double filter[FILTER_LENGTH], double outputSignal[OUTPUT_LENGTH]);

#endif

signalConvolve.cpp

#include "signalConvolve.h"
#include <cmath>
#include <hls_stream.h>



// 计算汉明窗
void hammingWindow(int N, double window[]) {
    for (int n = 0; n < N; ++n) {
        window[n] = 0.54 - 0.46 * cos(2 * M_PI * n / (N - 1));
    }
}

// 设计低通FIR滤波器系数
void designLowPassFIR(int N, double cutoffFrequency, double filterCoefficients[]) {
    int M = N / 2;  // 滤波器的中心对称点
    double normCutoff = 2 * cutoffFrequency;  // 标准化截止频率

    // 计算理想低通滤波器的系数
    for (int n = 0; n < N; ++n) {
        if (n == M) {
            filterCoefficients[n] = normCutoff / M;
        } else {
            filterCoefficients[n] = sin(normCutoff * (n - M)) / (M * (n - M));
        }
    }

    // 应用汉明窗
    double window[N];
    hammingWindow(N, window);
    for (int n = 0; n < N; ++n) {
        filterCoefficients[n] *= window[n];
    }
}

// 卷积运算函数
void convolve(double inputSignal[SIGNAL_LENGTH], double filter[FILTER_LENGTH], double outputSignal[OUTPUT_LENGTH]) {
    #pragma HLS PIPELINE
    for (int n = 0; n < OUTPUT_LENGTH; ++n) {
        outputSignal[n] = 0;
        for (int k = 0; k < FILTER_LENGTH; ++k) {
            int idx = n - k;
            if (idx >= 0 && idx < SIGNAL_LENGTH) {
                outputSignal[n] += inputSignal[idx] * filter[k];
            }
        }
    }
}

测试仿真代码signalConvolve_tb.cpp

#include <iostream>
#include <cmath>
#include "signalConvolve.h"
using namespace std;




// 生成输入信号
void generateInputSignal(double inputSignal[], int length) {
    for (int i = 0; i < length; ++i) {
        inputSignal[i] = sin(2 * M_PI * i / 50.0);  // 输入信号:正弦波
    }
}

int main() {
    // 分配内存
    double inputSignal[SIGNAL_LENGTH] = {0};
    double filterCoefficients[FILTER_LENGTH] = {0};
    double outputSignal[OUTPUT_LENGTH] = {0};

    // 生成输入信号
    generateInputSignal(inputSignal, SIGNAL_LENGTH);

    // 设计低通滤波器
    double cutoffFrequency = 0.2;  // 截止频率
    designLowPassFIR(FILTER_LENGTH, cutoffFrequency, filterCoefficients);

    // 执行卷积
    convolve(inputSignal, filterCoefficients, outputSignal);

    // 打印卷积结果
    std::cout << "卷积结果:" << std::endl;
    for (int i = 0; i < OUTPUT_LENGTH; ++i) {
        std::cout << outputSignal[i] << " ";
    }
    std::cout << std::endl;

    return 0;
}

三、使用STL vector容器可会导致 C Synthesis报错

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
# define   M_PI   3.1415926




// 计算汉明窗
std::vector<double> hammingWindow(int N) {
    std::vector<double> window(N);
    for (int n = 0; n < N; ++n) {
        window[n] = 0.54 - 0.46 * cos(2 * M_PI * n / (N - 1));   
    }
    return window;
}

// 设计低通滤波器的系数(10阶低通,截止频率0.2)
std::vector<double> designLowPassFIR(int N, double cutoffFrequency) {
    std::vector<double> filterCoefficients(N);
    int M = N / 2;  // 滤波器的中心对称点
    double normCutoff = 2 * cutoffFrequency;  // 标准化截止频率

    // 计算理想低通滤波器的系数 
    for (int n = 0; n < N; ++n) {
        if (n == M) {
            // 中心对称点的系数
            filterCoefficients[n] = normCutoff / M;
        }
        else {
            // 其他点使用 sinc 函数
            filterCoefficients[n] = sin(normCutoff * (n - M)) / (M * (n - M));
        }
    }

    // 应用汉明窗
    std::vector<double> window = hammingWindow(N);
    for (int n = 0; n < N; ++n) {
        filterCoefficients[n] *= window[n];
    }

    return filterCoefficients;
}

// 卷积运算函数
std::vector<double> convolve(const std::vector<double>& inputSignal, const std::vector<double>& filter) {
    int inputLength = inputSignal.size();
    int filterLength = filter.size();
    int outputLength = inputLength + filterLength - 1;  // 卷积结果长度

    std::vector<double> output(outputLength, 0.0);  // 初始化输出信号为 0

    // 执行卷积运算
    for (int n = 0; n < outputLength; ++n) {
        for (int k = 0; k < filterLength; ++k) {
            int idx = n - k;
            if (idx >= 0 && idx < inputLength) {
                output[n] += inputSignal[idx] * filter[k];
            }
        }
    }

    return output;
}

int main() {
    int signalLength = 1024;  // 输入信号的长度
    int filterLength = 10;    // 滤波器的阶数

    // 生成一个输入信号 
    std::vector<double> inputSignal(signalLength, 0.0);
    for (int i = 0; i < signalLength; ++i) {
        inputSignal[i] = sin(2 * M_PI * i / 50.0);  
        //inputSignal[i] = 1;
    }

    // 设计低通滤波器系数
    double cutoffFrequency = 0.2;  // 归一化截止频率
    std::vector<double> lowPassFilter = designLowPassFIR(filterLength, cutoffFrequency);


    //测试filter
   /* std::vector<double> filter(filterLength,0.0);
    for (int i = 0; i < filterLength; ++i) {
        filter[i] = 1;
    
    }*/

    // 执行卷积运算
     std::vector<double> outputSignal = convolve(inputSignal, lowPassFilter);
   // std::vector<double> outputSignal = convolve(inputSignal, filter);
   
   
    // 打印输出信号 
    std::cout << "卷积结果:" << std::endl;
    for (int i = 0; i < (signalLength + filterLength -1 ); ++i) {   
        std::cout << outputSignal[i] << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

四、附带Xilinx 官方学习文档

《ug902-vivado-high-level-synthesis(中文版)- 高层次综合学习指南》,项目下载地址如下

项目首页 - ug902-vivado-high-level-synthesis中文版-高层次综合学习指南分享:本资源提供了ug902-vivado-high-level-synthesis的中文版本PDF文档,它是专为那些希望深入理解和掌握Xilinx Vivado设计套件中的高层次综合(High-Level Synthesis, HLS)技术的学习者和开发者准备的宝贵资料。高层次综合允许工程师直接从C/C++或SystemC代码生成硬件描述语言(如Verilog或VHDL),极大地简化了复杂算法到FPGA实现的过程 - GitCode

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值