一、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(中文版)- 高层次综合学习指南》,项目下载地址如下: