我是靠谱客的博主 坦率小松鼠,最近开发中收集的这篇文章主要介绍c++ hough变换代码_Chisel实践 —— 短时傅里叶变换模块的实现与测试,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

62d56652936c5e6704c2f439839a72fb.png

一、STFT基本介绍

本专栏之前的三篇文章中,详细介绍了如何使用Chisel快速搭建FFT流水线电路。同时在这几篇文章中,所提到的对音频信号进行预处理的过程,其中有一步是对数据进行短时傅里叶变换(short-time Fourier transformSTFT)。因此,我们可以考虑在该FFT电路上对其进行拓展开发,以实现STFT功能。

STFT思想是:选择一个时频局部化的窗函数,假定分析窗函数g(t)在一个短时间间隔内是平稳(伪平稳)的,移动窗函数,使f(t)g(t)在不同的有限时间宽度内是平稳信号,从而计算出各个不同时刻的功率谱。短时傅里叶变换使用一个固定的窗函数,窗函数一旦确定了以后,其形状就不再发生改变,短时傅里叶变换的分辨率也就确定了。如果要改变分辨率,则需要重新选择窗函数。

在本实验中,结合KWS-SoC开发中实际的应用。我们选择对单帧音频数据进行短时傅里叶变换,其中单帧为320个数据点,其中数据点数是可以配置的,只需要用户在生成IP核的时候设置对应的参数,并且重新生成窗函数对应的权值点即可。

在之前的两篇文章([1](格斯:用Chisel快速搭建FFT流水线电路),[2](格斯:用Chisel快速搭建FFT流水线电路(续篇)—— 开源工程和使用教程))中,我们对如何使用Chisel快速搭建FFT流水线电路的基本方法进行了简要介绍,且在开源地址

https://github.com/IA-C-Lab-Fudan/Chisel-FFT

对相应的代码和工程进行了开源。

本实验最终的工程同样开源于以上工程的STFT分支中。

二、设计流程

简单分析下音频预处理流程中的HLS源码,我们可以将STFT划分成以下三个大步骤,如图所示:

f18c889935bdcfbc5590fd58ff168b2f.png
短时傅里叶变换步骤

为了提高数据处理的效率,我们把三个模块划分为三级流水线,模块之间通过valid/ready进行握手传输。

需要注意的是,在原FFT工程中,FFT顶层模块的输入握手信号是突发式握手的,也就是说data_in每次握手一次,需要连续传输FFT_LENGTH个信号。因此,我们需要对第一级Hanning模块的输出握手信号进行修订,使其满足FFT突发握手的需求。

1. Hanning block

STFT的第一步是对信号加窗,在KWS_SoC中所用到的是汉宁窗,其窗函数如下所示:

2a41bf9b01a868c62ce6fee5776d99e1.png
Hanning窗函数

为了减少计算量,我们提前计算好cos的值,将Frame_Length个离散的权值点存入IP核中,运算时直接相乘。为了实现这一步骤,需要单独定义一个传输cos值的接口,此外,需要定义Signal_in信号的输入和Hanning窗的输出。

值得注意的是,KWS_SoC中单帧的数据有320个,我们需要对其进行补0到512个点。因此整个流程可以用三个状态表示:接受数据加窗,补零,空闲

通过Chisel的Enum非常方便定义状态机状态,并且用switch或者when语句实现三段式状态机:

//FSM transfer signal

对于握手信号的处理,由于FFT模块使用的是突发握手,所以我们在本模块之后添加一个深度为2 * FFTLength的 FIFO,每次FIFO的当前数据大于FFT_length,将o_valid拉高

然后等待与FFT的i_ready握手一次,将o_valid拉低,并且从FIFO中连续读出FFT_length个数据,然后再等待FIFO数据量大于FFT_length,以此重复。

2. FFT block

该部分我们直接调用的FFT工程中的FFT package,FFT_Length配置为512。在输入512个时域实部数据以后,将在512个周期后输出频域变换的结果,返回类型是复数定点类型,握手方式为单次握手。

3. Spectrogram block

这部分我们先在Butterfly.scala中定义了一个求复数能量的函数:

class 

由于最终我们只需要输出FFT_LENGTH/2 + 1个点,通过设置一个counter,每次握手向该模块的FIFO内写入单个数据,将对应前FFT_LENGTH/2 + 1能量发送出去。对于该模块的ready信号,除了

取决于下一级的i_ready信号,还需要该级输出队列FIFO中有足够的容量可以写入,具体逻辑可以参考开源工程中的源码。

三、测试流程

1. 单独测试

在src/test/scala/STFT下,提供了单独测试每个模块的脚本样例。在测试中,我们用到了chisel的tester2特性。生成好的波形在./test_run_dir下,读者可以通过运行每个测试脚本,然后用gtkwave生成波形进行调试与测试。

2. 联合测试

对顶层STFT模块进行测试,可以看到,首先传输720个cos权重值,这里我们只会用到前320个点,然后通过数据端口连续发送多组数据。再经过一段时间后,spectrogram模块输出最终257个结果:

aaae9293f037ae9d7abe4d241a7254d7.png
总体输入输出

突发传输握手的波形如下所示

96236055932792d82258db115e9b2043.png
FFT block的突发握手

三个模块流水线运行的波形如下图所示

e6b0bcc14375e762702c138e2ff6d921.png
流水线运行

四、FPGA评估

通过修改vivado的tcl脚本fft.tcl中的参数,包括文件路径等,我们按照之前帖子的方法,用verilog emmiter脚本生成好STFT顶层模块的代码后,创建Vivado工程,添加时钟IP,对该模块进行评估( FPGA元件为v7xc7vx485tffg1761-2,频率为50MHz)

1. 时序报告

此模块在50Mhz的工作频率下,S/H时序满足要求

cfbc0e2ab3048bfc353ac4659e9350b9.png

2. 资源报告

此模块具体的资源消耗如下表所示

ResourceUtilizationAvailableUtilization %
LUT104153036003.43
LUTRAM53921308004.12
FF26746072000.44
DSP14028005.00
BUFG1323.13
MMCM1147.14

3. 功耗报告

此模块的功耗分析如下所示

d85e5ae8507d43528a8a055042fc27d7.png

五、 结语

本文基于IA-C-Lab-Fudan中的Chisel-FFT进行拓展开发,实现了短时傅里叶变换的功能。此功能在KWS-SoC中已经提及到,作为预处理过程很重要的一个流程,并且在VIVADO HLS中难以优化且费时的一环,这里通过简单地对FFT IP核进行拓展,实现了其功能,并且有流水线执行的特点。除了加快了处理速度,其生成的verilog代码的可读性以及用户对具体硬件的控制性相对HLS生成 的方式有着很大的优势。

另外,我们在本实验的探索过程中,也体验到Chisel相对与verilog的优势: 举个例子,比如在每个模块的输出口,我们需要定义一个FIFO来暂存输出数据,在HLS中,我们的写法是添加HLS STREAM参数变成流对象:

#pragma HLS STREAM variable=output depth=2*FFTLength

对于verilog而言,要实现这个功能,需要添加冗长的verilog代码来例化这个FIFO,同时需要考虑读写信号的连线问题

FIFO 

同时需要添加FIFO的实现代码,容易出错且复杂。

而对于Chisel而言,其提供了Queue类

val 

仅需要提供该队列的输入信号类型和深度,就可以生成我们需要的FIFO,这种FIFO的输出输出端口具备Chisel的DecoupledIO特性(这种特性将信号包装成valid,ready,bits的握手组信号,相对于 verilog,用户也可以直接定义这种类型的型号,将握手信号封装,代码更加简洁)。可以看到,这种类型相对于verilog已经大大简化,只需要一句就可以实现FIFO的定义。

总而言之,Chisel作为硬件设计语言的先进形态,既继承了verilog/system verilog的优势,又有自己独特的优势,比如高度封装、软硬协同测试便捷等特点,也是未来硬件设计语言的发展方向。

最后

以上就是坦率小松鼠为你收集整理的c++ hough变换代码_Chisel实践 —— 短时傅里叶变换模块的实现与测试的全部内容,希望文章能够帮你解决c++ hough变换代码_Chisel实践 —— 短时傅里叶变换模块的实现与测试所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(76)

评论列表共有 0 条评论

立即
投稿
返回
顶部