当前位置: 首页 > news >正文

verilog实现STFT

  短时傅里叶变换(STFT, Short Time Fourier Transform),是处理采样数据、获取信号时频特征的一种常用方法。然而其消耗的计算资源较为庞大,在数据采集设备全速运行时,若在上位机进行 STFT 的计算,则很难做到实时性。因此尝试将 STFT 步骤迁移到 FPGA 端进行,从而减轻上位机的计算压力。

  STFT 的操作流程上,就是对滑动窗口中的数据做 FFT,这一工作可以交给 FFT IP 核进行,因此我们实际只需要实现对采样数据流的控制,以实现滑动窗口

采样数据存储容器设计

FIFO 设计

  由于采样数据由 ADC 源源不断地采集而来,因此需要类似于 FIFO 这样的数据存储模块。然而在 STFT 过程中,需要对数据进行重叠读取,用常规的 FIFO 是无法实现这一目标的,因此需要自己用 RAM 搭建一个数据存储模块,其采样数据以 FIFO 接口的形式顺序传入,而以 RAM 的接口形式随机读取。

  因此利用真双口 Block RAM 搭建同步 FIFO,并利用真双口 RAM 的两个输入输出接口,实现 FIFO 接口和 RAM 接口;通过真双口 RAM,可以同时进行两个操作,这就为实时 STFT 过程中,采样数据的顺序写入以及 STFT 随机读取的同时进行提供了基础。同时,为了模拟 STFT 窗口的移动,因此也设计了 FIFO 首尾指针的配置功能,从而可以通过直接修改头指针来滑动数据窗口:

/* * file         : FIFO_BRAM_sync.v* author       : 今朝无言* lab		    : WHU-EIS-LMSWE* date		    : 2024-07-20* version      : v3.0* description  : 利用真双口 Block RAM 搭建同步 FIFO,从而同时支持随机存取功能*/
`default_nettype none
module FIFO_BRAM_sync(
input	wire					clk,
input	wire					rst_n,//FIFO Interface
input	wire	[DataWidth-1:0]	FIFO_data_in,
input	wire					FIFO_wren,
output	reg		[DataWidth-1:0]	FIFO_data_out,
input	wire					FIFO_rden,
output	wire					FIFO_out_vaild,
output	wire					FIFO_empty,				//空标志
output	wire					FIFO_full,				//满标志
output	wire	[AddrWidth-1:0]	FIFO_element_cnt,		//FIFO中元素个数
output	wire	[AddrWidth-1:0]	FIFO_head,				//当前首指针,指向下一个被读取的元素
output	wire	[AddrWidth-1:0]	FIFO_tail,				//当前尾指针,指向下一个被写入的位置//RAM Interface
input	wire	[AddrWidth-1:0]	RAM_addr_w,
input	wire	[DataWidth-1:0]	RAM_data_in,
input	wire					RAM_wren,
input	wire	[AddrWidth-1:0]	RAM_addr_r,
output	reg		[DataWidth-1:0]	RAM_data_out,
input	wire					RAM_rden,
output	wire					RAM_out_vaild,//Config Interface
input	wire	[AddrWidth-1:0]	config_set_Head,		//设置首指针
input	wire	[AddrWidth-1:0]	config_set_Tail,		//设置尾指针
input	wire	[1:0]			config_vaild,			//bit0控制修改Head,bit1控制修改Tail
output	reg						config_ready,//异常告警
output	reg						event_wr_collision,		//发生写入冲突
output	reg						event_FIFO_wr_failed,	//写FIFO失败
output	reg						event_FIFO_rd_failed,	//读FIFO失败
output	reg						event_RAM_rd_failed		//读RAM失败
);parameter	DataWidth		= 32;		//这两个 param 注意与 BRAM IP 的配置相一致
parameter	FIFO_Depth		= 65536;localparam	AddrWidth		= (FIFO_Depth>1)? clogb2(FIFO_Depth - 1) : 1;//------------------------log2---------------------------------
function integer clogb2 (input integer depth);beginfor (clogb2 = 0; depth > 0; clogb2 = clogb2 + 1) begindepth	= depth >> 1;endend
endfunction//----------------------FIFO interface--------------------------
reg						FIFO_empty_r;
reg						FIFO_full_r;
reg		[AddrWidth-1:0]	FIFO_element_cnt_r	= 'd0;
reg		[AddrWidth-1:0]	FIFO_head_r			= 'd0;	//当前首指针,指向下一个被读取的元素
reg		[AddrWidth-1:0]	FIFO_tail_r			= 'd0;	//当前尾指针,指向下一个被写入的位置assign	FIFO_empty			= FIFO_empty_r;
assign	FIFO_full			= FIFO_full_r;
assign	FIFO_element_cnt	= FIFO_element_cnt_r;
assign	FIFO_head			= FIFO_head_r;
assign	FIFO_tail			= FIFO_tail_r;assign	FIFO_out_vaild		= FIFO_out_vaild_r[3];
assign	RAM_out_vaild		= RAM_out_vaild_r[3];//----------------------BRAM IP Core----------------------------
reg						BRAM_wea	= 1'b0;
reg		[AddrWidth-1:0]	BRAM_addra	= 'd0;
reg		[DataWidth-1:0]	BRAM_dina	= 'd0;
wire	[DataWidth-1:0]	BRAM_douta;reg						BRAM_web	= 1'b0;
reg		[AddrWidth-1:0]	BRAM_addrb	= 'd0;
reg		[DataWidth-1:0]	BRAM_dinb	= 'd0;
wire	[DataWidth-1:0]	BRAM_doutb;//利用真双口RAM(True Dual RAM)进行功能实现
RAM_TrueDual_0 RAM_inst(.clka		(clk),.ena		(1'b1),.wea		(BRAM_wea),.addra		(BRAM_addra),.dina		(BRAM_dina),.douta		(BRAM_douta),.clkb		(clk),.enb		(1'b1),.web		(BRAM_web),.addrb		(BRAM_addrb),.dinb		(BRAM_dinb),.doutb		(BRAM_doutb)
);
//对于两个独立的输入输出端口,要满足 FIFO 读写、RAM 读写四种操作,因此需要合理设计优先级,
//本设计优先满足 FIFO 要求、优先满足写入要求,即:FIFO写 > RAM 写 > FIFO读 > RAM读,
//并根据优先级依次分配 port A/B。//注意,由于RAM的特性,在addr改变后的第二个时钟上升沿才输出响应的数据;而写则是发生在同一时钟沿。
//这就导致本模块进行FIFO读时,出现3个clk的延迟(本模块里还额外打一拍)//-------------------------CTRL---------------------------------
//BRAM_wea
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginBRAM_wea	<= 1'b0;endelse begincasex({FIFO_wren, RAM_wren, FIFO_rden, RAM_rden})4'b1xxx: beginBRAM_wea	<= 1'b1;end4'b01xx: beginBRAM_wea	<= 1'b1;enddefault: beginBRAM_wea	<= 1'b0;endendcaseend
end//BRAM_addra
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginBRAM_addra	<= 'd0;endelse begincasex({FIFO_wren, RAM_wren, FIFO_rden, RAM_rden})4'b1xxx: beginBRAM_addra	<= FIFO_tail;end4'b01xx: beginBRAM_addra	<= RAM_addr_w;end4'b001x: beginBRAM_addra	<= FIFO_head;end4'b0001: beginBRAM_addra	<= RAM_addr_r;enddefault: beginBRAM_addra	<= 'd0;endendcaseend
end//BRAM_dina
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginBRAM_dina	<= 'd0;endelse begincasex({FIFO_wren, RAM_wren, FIFO_rden, RAM_rden})4'b1xxx: beginBRAM_dina	<= FIFO_data_in;end4'b01xx: beginBRAM_dina	<= RAM_data_in;enddefault: beginBRAM_dina	<= 'd0;endendcaseend
end//BRAM_web
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginBRAM_web	<= 1'b0;endelse begincasex({FIFO_wren, RAM_wren, FIFO_rden, RAM_rden})4'b11xx: beginBRAM_web	<= 1'b1;enddefault: beginBRAM_web	<= 1'b0;endendcaseend
end//BRAM_addrb
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginBRAM_addrb	<= 'd0;endelse begincasex({FIFO_wren, RAM_wren, FIFO_rden, RAM_rden})4'b11xx: beginBRAM_addrb	<= RAM_addr_w;end4'b101x, 4'b011x: beginBRAM_addrb	<= FIFO_head;end4'b1001, 4'b0101, 4'b0011: beginBRAM_addrb	<= RAM_addr_r;enddefault: beginBRAM_addrb	<= 'd0;endendcaseend
end//BRAM_dinb
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginBRAM_dinb	<= 'd0;endelse begincasex({FIFO_wren, RAM_wren, FIFO_rden, RAM_rden})4'b11xx: beginBRAM_dinb	<= RAM_data_in;enddefault: beginBRAM_dinb	<= 'd0;endendcaseend
end//FIFO_data_out
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginFIFO_data_out	<= 'd0;endelse begincasex(mod_buf_d2)4'b001x: beginFIFO_data_out	<= BRAM_douta;end4'b101x, 4'b011x: beginFIFO_data_out	<= BRAM_doutb;enddefault: beginFIFO_data_out	<= 'd0;endendcaseend
end//RAM_data_out
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginRAM_data_out	<= 'd0;endelse begincasex(mod_buf_d2)4'b0001: beginRAM_data_out	<= BRAM_douta;end4'b1001, 4'b0101, 4'b0011: beginRAM_data_out	<= BRAM_doutb;enddefault: beginRAM_data_out	<= 'd0;endendcaseend
end//FIFO_head_r
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginFIFO_head_r		<= 'd0;endelse beginif(config_ready & config_vaild[0]) beginFIFO_head_r		<= config_set_Head;		//配置head、tail时,应暂停读写操作,以避免出现错误endelse begincasex({FIFO_wren, RAM_wren, FIFO_rden, RAM_rden})4'b101x, 4'b011x, 4'b001x: beginif(~FIFO_empty) beginif(FIFO_head_r < FIFO_Depth - 1'b1) beginFIFO_head_r		<= FIFO_head_r + 1'b1;endelse beginFIFO_head_r		<= 16'd0;endendelse beginFIFO_head_r		<= FIFO_head_r;endenddefault: beginFIFO_head_r		<= FIFO_head_r;endendcaseendend
end//FIFO_tail_r
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginFIFO_tail_r		<= 'd0;endelse beginif(config_ready & config_vaild[1]) beginFIFO_tail_r		<= config_set_Tail;endelse begincasex({FIFO_wren, RAM_wren, FIFO_rden, RAM_rden})4'b1xxx: beginif(~FIFO_full) beginif(FIFO_tail_r < FIFO_Depth - 1'b1) beginFIFO_tail_r		<= FIFO_tail_r + 1'b1;endelse beginFIFO_tail_r		<= 16'd0;endendelse beginFIFO_tail_r		<= FIFO_tail_r;endenddefault: beginFIFO_tail_r		<= FIFO_tail_r;endendcaseendend
end//FIFO_empty_r
always @(*) beginFIFO_empty_r	<= (FIFO_head==FIFO_tail)? 1'b1 : 1'b0;
end//FIFO_full_r
always @(*) beginFIFO_full_r		<= ((FIFO_tail + 1'b1 == FIFO_head) || ((FIFO_head == 16'd0) && (FIFO_tail == FIFO_Depth - 1'b1)))? 1'b1 : 1'b0;
end//FIFO_element_cnt
always @(*) beginif(FIFO_tail >= FIFO_head) beginFIFO_element_cnt_r	<= FIFO_tail - FIFO_head;endelse beginFIFO_element_cnt_r	<= FIFO_Depth + FIFO_tail - FIFO_head;end
end//config_ready
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginconfig_ready	<= 1'b0;endelse beginconfig_ready	<= 1'b1;end
end//event_wr_collision
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginevent_wr_collision	<= 1'b0;endelse begincasex({FIFO_wren, RAM_wren, FIFO_rden, RAM_rden})4'b11xx: beginif(FIFO_tail == RAM_addr_w) begin	//发生写入地址冲突,将无法确定写入对应地址的数据时是来自于哪个Portevent_wr_collision	<= 1'b1;endelse beginevent_wr_collision	<= 1'b0;endenddefault: beginevent_wr_collision	<= 1'b0;endendcaseend
end//event_FIFO_wr_failed
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginevent_FIFO_wr_failed	<= 1'b0;endelse begincasex({FIFO_wren, RAM_wren, FIFO_rden, RAM_rden})4'b1xxx: beginif(FIFO_full) beginevent_FIFO_wr_failed	<= 1'b1;endelse beginevent_FIFO_wr_failed	<= 1'b0;endenddefault: beginevent_FIFO_wr_failed	<= 1'b0;endendcaseend
end//event_FIFO_rd_failed
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginevent_FIFO_rd_failed	<= 1'b0;endelse begincasex({FIFO_wren, RAM_wren, FIFO_rden, RAM_rden})4'b111x: beginevent_FIFO_rd_failed	<= 1'b1;end4'b001x, 4'b101x, 4'b011x: beginif(FIFO_empty) beginevent_FIFO_rd_failed	<= 1'b1;endelse beginevent_FIFO_rd_failed	<= 1'b0;endenddefault: beginevent_FIFO_rd_failed	<= 1'b0;endendcaseend
end//event_RAM_rd_failed
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginevent_RAM_rd_failed		<= 1'b0;endelse begincasex({FIFO_wren, RAM_wren, FIFO_rden, RAM_rden})4'b1101, 4'b1011, 4'b1111, 4'b0111: beginevent_RAM_rd_failed		<= 1'b1;enddefault: beginevent_RAM_rd_failed		<= 1'b0;endendcaseend
end//FIFO_out_vaild_r
reg		[3:0]	FIFO_out_vaild_r	= 4'b0;
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginFIFO_out_vaild_r	<= 4'b0000;endelse begincasex({FIFO_wren, RAM_wren, FIFO_rden, RAM_rden})4'b101x, 4'b011x, 4'b001x: beginFIFO_out_vaild_r	<= {FIFO_out_vaild_r[2:0], ~FIFO_empty};enddefault: beginFIFO_out_vaild_r	<= {FIFO_out_vaild_r[2:0], 1'b0};endendcaseend
end//RAM_out_vaild_r
reg		[3:0]	RAM_out_vaild_r	= 4'b0;
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginRAM_out_vaild_r		<= 4'b0000;endelse begincasex({FIFO_wren, RAM_wren, FIFO_rden, RAM_rden})4'b0001, 4'b1001, 4'b0101, 4'b0011: beginRAM_out_vaild_r		<= {RAM_out_vaild_r[2:0], 1'b1};enddefault: beginRAM_out_vaild_r		<= {RAM_out_vaild_r[2:0], 1'b0};endendcaseend
end//mod_buf
reg		[3:0]	mod_buf_d0	= 4'b0000;
reg		[3:0]	mod_buf_d1	= 4'b0000;
reg		[3:0]	mod_buf_d2	= 4'b0000;
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginmod_buf_d0		<= 4'b0000;mod_buf_d1		<= 4'b0000;mod_buf_d2		<= 4'b0000;endelse beginmod_buf_d0		<= {FIFO_wren, RAM_wren, FIFO_rden, RAM_rden};mod_buf_d1		<= mod_buf_d0;mod_buf_d2		<= mod_buf_d1;end
endendmodule

testbench

  编写 Testbench 如下,进行本 FIFO_RAM 的测试

`timescale 1ns/100ps
`default_nettype none
module FIFO_BRAM_sync_tb();reg		clk_100M	= 1'b1;
reg		rst_n		= 1'b1;always #5 beginclk_100M	<= ~clk_100M;
end//FIFO Interface
reg		[15:0]	FIFO_data_in	= 16'd0;
reg				FIFO_wren		= 1'b0;
wire	[15:0]	FIFO_data_out;
reg				FIFO_rden		= 1'b0;
wire			FIFO_out_vaild;
wire			FIFO_empty;
wire			FIFO_full;
wire	[6:0]	FIFO_element_cnt;
wire	[6:0]	FIFO_head;
wire	[6:0]	FIFO_tail;//RAM Interface
reg		[15:0]	RAM_addr_w		= 16'd0;
reg		[15:0]	RAM_data_in		= 16'd0;
reg				RAM_wren		= 1'b0;
reg		[15:0]	RAM_addr_r		= 16'd0;
wire	[15:0]	RAM_data_out;
reg				RAM_rden		= 1'b0;
wire			RAM_out_vaild;//Config Interface
reg		[15:0]	config_set_Head	= 16'd0;
reg		[15:0]	config_set_Tail	= 16'd0;
reg		[1:0]	config_vaild	= 2'b00;
wire			config_ready;//异常告警
wire			event_wr_collision;
wire			event_FIFO_wr_failed;
wire			event_FIFO_rd_failed;
wire			event_RAM_rd_failed;FIFO_BRAM_sync #(.DataWidth				(16),.FIFO_Depth				(16'd128)		//参数设置注意和 BRAM IP 的配置一致
)
FIFO_BRAM_sync_inst(.clk					(clk_100M),.rst_n					(rst_n),//FIFO Interface.FIFO_data_in			(FIFO_data_in),.FIFO_wren				(FIFO_wren),.FIFO_data_out			(FIFO_data_out),.FIFO_rden				(FIFO_rden),.FIFO_out_vaild			(FIFO_out_vaild),.FIFO_empty				(FIFO_empty),.FIFO_full				(FIFO_full),.FIFO_element_cnt		(FIFO_element_cnt),.FIFO_head				(FIFO_head),.FIFO_tail				(FIFO_tail),//RAM Interface.RAM_addr_w				(RAM_addr_w),.RAM_data_in			(RAM_data_in),.RAM_wren				(RAM_wren),.RAM_addr_r				(RAM_addr_r),.RAM_data_out			(RAM_data_out),.RAM_rden				(RAM_rden),.RAM_out_vaild			(RAM_out_vaild),//Config Interface.config_set_Head		(config_set_Head),.config_set_Tail		(config_set_Tail),.config_vaild			(config_vaild),.config_ready			(config_ready),//异常告警.event_wr_collision		(event_wr_collision),		//发生写入冲突.event_FIFO_wr_failed	(event_FIFO_wr_failed),		//写FIFO失败.event_FIFO_rd_failed	(event_FIFO_rd_failed),		//读FIFO失败.event_RAM_rd_failed	(event_RAM_rd_failed)		//读RAM失败
);//---------------------------Ctrl-------------------------------
//FIFO_data_in
always @(posedge clk_100M) beginif(~rst_n) beginFIFO_data_in	<= 16'd0;endelse beginif(FIFO_wren) beginFIFO_data_in	<= FIFO_data_in + 1'b1;endelse beginFIFO_data_in	<= FIFO_data_in;endend
end//RAM_data_in
always @(posedge clk_100M) beginif(~rst_n) beginRAM_data_in		<= 16'd66;endelse beginif(RAM_wren) beginRAM_data_in		<= RAM_data_in + 1'b1;endelse beginRAM_data_in		<= RAM_data_in;endend
end//FIFO Write
task FIFO_write;input	[15:0]	Len;integer i;beginfor(i=0; i<Len; i=i+1) beginwait(clk_100M);FIFO_wren	<= 1'b1;wait(~clk_100M);endwait(clk_100M);FIFO_wren	<= 1'b0;end
endtask//FIFO Read
task FIFO_read;input	[15:0]	Len;integer i;beginfor(i=0; i<Len; i=i+1) beginwait(clk_100M);FIFO_rden	<= 1'b1;wait(~clk_100M);endwait(clk_100M);FIFO_rden	<= 1'b0;end
endtask//RAM wr
task RAM_write;input	[15:0]	addr;beginwait(clk_100M);RAM_wren	<= 1'b1;RAM_addr_w		<= addr;wait(~clk_100M);wait(clk_100M);RAM_wren	<= 1'b0;end
endtask//RAM rd
task RAM_read;input	[15:0]	addr;beginwait(clk_100M);RAM_rden	<= 1'b1;RAM_addr_r		<= addr;wait(~clk_100M);wait(clk_100M);RAM_rden	<= 1'b0;end
endtask//set FIFO head & tail
task set_FIFO_head_tail;input	[15:0]	head;input	[15:0]	tail;beginwait(clk_100M);config_vaild	<= 2'b11;config_set_Head	<= head;config_set_Tail	<= tail;wait(~clk_100M);wait(clk_100M);config_vaild	<= 2'b00;config_set_Head	<= 16'd0;config_set_Tail	<= 16'd0;end
endtask//main
integer i;
initial beginrst_n	<= 1'b0;#100;rst_n	<= 1'b1;#100;//分别进行FIFO的读写FIFO_write(16'd64);FIFO_read(16'd72);		//测试读空FIFO_write(16'd150);	//测试写满FIFO_read(16'd150);//同时进行FIFO读写#200;forkbegin: join_FIFO_wrFIFO_write(16'd64);endbegin: join_FIFO_rdFIFO_read(16'd72);endjoin//测试RAM端口RAM_write(16'd12);RAM_read(16'd12);//FIFO/RAM依次读写FIFO_write(16'd64);for(i=0; i<10; i=i+1) beginRAM_read(i);endfor(i=15; i<20; i=i+1) beginRAM_write(i);endFIFO_read(16'd64);//设置FIFO head & tailset_FIFO_head_tail(16'd10, 16'd20);FIFO_read(16'd20);//测试读写异常处理机制#200;forkbegin: join_FIFO_wr2FIFO_write(16'd64);endbegin: join_RAM_wr2#0;for(i=32; i<40; i=i+1) beginRAM_write(i);endendbegin: join_FIFO_rd2#0;FIFO_read(16'd32);endbegin: join_RAM_rd2#160;for(i=0; i<32; i=i+1) beginRAM_read(i);endendjoin//测试FIFO/RAM同时写#200;forkbegin: join_FIFO_wr3FIFO_write(16'd64);endbegin: join_RAM_wr3#200;for(i=32; i<40; i=i+1) beginRAM_write(i);endendjoin//测试FIFO/RAM同时读#200;forkbegin: join_FIFO_rd3FIFO_read(16'd127);endbegin: join_RAM_rd3#200;for(i=0; i<32; i=i+1) beginRAM_read(i);endendjoin//测试写地址冲突#200;set_FIFO_head_tail(16'd0, 16'd0);forkbegin: join_FIFO_wr4FIFO_write(16'd64);endbegin: join_RAM_wr4#0;for(i=0; i<32; i=i+1) beginRAM_write(i);endendjoin//查看写入冲突下写入了什么数据#100;FIFO_read(16'd64);		//其实也没啥必要看,BRAM手册上说这种情况是无法确定写入数据的//仿真结果是,在发生写入冲突时,内容为 port B 写入的数据//finish#1000;$stop;
endendmodule

在这里插入图片描述

STFT顶层模块实现

设计实现

  进一步地,利用我们上面实现的 FIFO 模块,实现 STFT 模块。该模块用到了 FFT IP 核,65536 point,启用 Config Transform Length 功能,数据位宽 16bit,Pipelined Stream I/O,Natural Order,固定缩放,具体如何使用 FFT IP 可以参考我之前写的这篇博客。

/* * file         : STFT.v* author       : 今朝无言* lab		    : WHU-EIS-LMSWE* date		    : 2024-07-20* version      : v1.0* description  : Short Time Fourier Transform (STFT)*/
`default_nettype none
module STFT(
input	wire					clk_100M,
input	wire					rst_n,//data in
input	wire	signed	[15:0]	data_in_Real,
input	wire	signed	[15:0]	data_in_Img,
input	wire					data_in_vaild,
output	reg						data_in_ready,//data out
output	reg		signed	[15:0]	data_out_Real,
output	reg		signed	[15:0]	data_out_Img,
output	reg				[15:0]	data_out_Idx,
output	reg						data_out_vaild,
input	wire					data_out_ready,//Config
input	wire			[4:0]	config_NFFT,		//STFT点数(以二为底的指数,最大16)
input	wire			[15:0]	config_overlap,		//STFT步进
input	wire			[15:0]	config_SCALE_SCH,	//缩放系数,每2bit控制一个蝶形单元(Radix-4)的缩放
input	wire					config_FWD_INV,		//FFT(1) or IFFT(0)
input	wire					config_vaild,
output	reg						config_ready
);
// 利用 Block RAM 构成同步循环队列(从而也支持随机读取),构成 data_in 的存储空间,并根据步进及 NFFT 读取数据供入 FFT IP//--------------------------FFT IP------------------------------------
//config
reg				[15:0]	SCALE_SCH	= {2'd2, 2'd2, 2'd2, 2'd2,2'd2, 2'd2, 2'd2, 2'd2}; //16'haaaa;
reg						FWD_INV		= 1'b1;		//1:FFT, 0:IFFT
reg				[4:0]	NFFT		= 5'd10;	//默认 N=1024wire			[31:0]	s_axis_config_tdata;
wire					s_axis_config_tvalid;
wire					s_axis_config_tready;assign	s_axis_config_tdata		= {7'b0, SCALE_SCH, FWD_INV, 3'b0, NFFT};
assign	s_axis_config_tvalid	= config_ready & config_vaild; //event_frame_started;//data in
reg		signed	[15:0]	XN_Real					= 16'sd0;
reg		signed	[15:0]	XN_Img					= 16'sd0;
wire			[31:0]	s_axis_data_tdata;
reg						s_axis_data_tvalid		= 1'b0;
wire					s_axis_data_tready;
reg						s_axis_data_tlast		= 1'b0;assign	s_axis_data_tdata	= {XN_Img, XN_Real};//data out
wire	signed	[15:0]	XK_Real;
wire	signed	[15:0]	XK_Img;
wire			[15:0]	XK_INDEX;
wire			[31:0]	m_axis_data_tdata;
wire			[15:0]	m_axis_data_tuser;
wire					m_axis_data_tvalid;
reg						m_axis_data_tready		= 1'b0;
wire					m_axis_data_tlast;assign	XK_Real		= m_axis_data_tdata[15:0];
assign	XK_Img		= m_axis_data_tdata[31:16];
assign	XK_INDEX	= m_axis_data_tuser[15:0];//events
wire					event_frame_started;
wire					event_tlast_unexpected;
wire					event_tlast_missing;
wire					event_status_channel_halt;
wire					event_data_in_channel_halt;
wire					event_data_out_channel_halt;//FFT IP,65536 point,启用 Config Transform Length 功能,数据位宽 16bit,Pipelined Stream I/O,Natural Order,固定缩放
FFT_1 FFT_inst(.aclk							(clk_100M),.aresetn						(rst_n),//Config.s_axis_config_tdata			(s_axis_config_tdata),.s_axis_config_tvalid			(s_axis_config_tvalid),.s_axis_config_tready			(s_axis_config_tready),//DATA INPUT.s_axis_data_tdata				(s_axis_data_tdata),.s_axis_data_tvalid				(s_axis_data_tvalid),.s_axis_data_tready				(s_axis_data_tready),.s_axis_data_tlast				(s_axis_data_tlast),//DATA OUTPUT.m_axis_data_tdata				(m_axis_data_tdata),.m_axis_data_tuser				(m_axis_data_tuser),.m_axis_data_tvalid				(m_axis_data_tvalid),.m_axis_data_tready				(m_axis_data_tready),.m_axis_data_tlast				(m_axis_data_tlast),//event signal.event_frame_started			(event_frame_started),.event_tlast_unexpected			(event_tlast_unexpected),.event_tlast_missing			(event_tlast_missing),.event_status_channel_halt		(event_status_channel_halt),.event_data_in_channel_halt		(event_data_in_channel_halt),.event_data_out_channel_halt	(event_data_out_channel_halt)
);//----------------------FIFO_RAM_sync---------------------------------
//FIFO Interface
reg		[31:0]	FIFO_data_in			= 32'd0;
reg				FIFO_wren				= 1'b0;
wire	[31:0]	FIFO_data_out;
reg				FIFO_rden				= 1'b0;
wire			FIFO_out_vaild;
wire			FIFO_empty;
wire			FIFO_full;
wire	[15:0]	FIFO_element_cnt;
wire	[15:0]	FIFO_head;
wire	[15:0]	FIFO_tail;//RAM Interface
reg		[15:0]	RAM_addr_w				= 16'd0;
reg		[31:0]	RAM_data_in				= 32'd0;
reg				RAM_wren				= 1'b0;
reg		[15:0]	RAM_addr_r				= 16'd0;
wire	[31:0]	RAM_data_out;
reg				RAM_rden				= 1'b0;
wire			RAM_out_vaild;//Config Interface
reg		[15:0]	FIFO_config_set_Head	= 16'd0;
reg		[15:0]	FIFO_config_set_Tail	= 16'd0;
reg		[1:0]	FIFO_config_vaild		= 2'b00;
wire			FIFO_config_ready;//异常告警
wire			event_wr_collision;
wire			event_FIFO_wr_failed;
wire			event_FIFO_rd_failed;
wire			event_RAM_rd_failed;localparam	FIFO_Depth	= 65536;FIFO_BRAM_sync #(.DataWidth				(32),		//16bit Real + 16bit Img.FIFO_Depth				(FIFO_Depth)
)
FIFO_BRAM_sync_inst(.clk					(clk_100M),.rst_n					(rst_n),//FIFO Interface.FIFO_data_in			(FIFO_data_in),.FIFO_wren				(FIFO_wren),.FIFO_data_out			(FIFO_data_out),.FIFO_rden				(FIFO_rden),.FIFO_out_vaild			(FIFO_out_vaild),.FIFO_empty				(FIFO_empty),.FIFO_full				(FIFO_full),.FIFO_element_cnt		(FIFO_element_cnt),.FIFO_head				(FIFO_head),.FIFO_tail				(FIFO_tail),//RAM Interface.RAM_addr_w				(RAM_addr_w),.RAM_data_in			(RAM_data_in),.RAM_wren				(RAM_wren),.RAM_addr_r				(RAM_addr_r),.RAM_data_out			(RAM_data_out),.RAM_rden				(RAM_rden),.RAM_out_vaild			(RAM_out_vaild),//Config Interface.config_set_Head		(FIFO_config_set_Head),.config_set_Tail		(FIFO_config_set_Tail),.config_vaild			(FIFO_config_vaild),.config_ready			(FIFO_config_ready),//异常告警.event_wr_collision		(event_wr_collision),		//发生写入冲突.event_FIFO_wr_failed	(event_FIFO_wr_failed),		//写FIFO失败.event_FIFO_rd_failed	(event_FIFO_rd_failed),		//读FIFO失败.event_RAM_rd_failed	(event_RAM_rd_failed)		//读RAM失败
);//-------------------------------参数配置-------------------------------------
reg		[15:0]	STFT_overlap	= 16'd1024;		//STFT的步进点数,步进点数 = FFT点数 - 重叠点数//NFFT
always @(posedge clk_100M or negedge rst_n) beginif(~rst_n) beginNFFT	<= 5'd10;endelse beginif(config_ready & config_vaild) beginNFFT	<= config_NFFT;endelse beginNFFT	<= NFFT;endend
end//STFT_overlap
always @(posedge clk_100M or negedge rst_n) beginif(~rst_n) beginSTFT_overlap	<= 16'd1024;endelse beginif(config_ready & config_vaild) beginSTFT_overlap	<= config_overlap;endelse beginSTFT_overlap	<= STFT_overlap;endend
end//config_ready
always @(posedge clk_100M or negedge rst_n) beginif(~rst_n) beginconfig_ready	<= 1'b0;endelse beginconfig_ready	<= 1'b1;end
end//SCALE_SCH
always @(posedge clk_100M or negedge rst_n) beginif(~rst_n) beginSCALE_SCH	<= {2'd2, 2'd2, 2'd2, 2'd2,2'd2, 2'd2, 2'd2, 2'd2};endelse beginif(config_ready & config_vaild) beginSCALE_SCH	<= config_SCALE_SCH;endelse beginSCALE_SCH	<= SCALE_SCH;endend
end//FWD_INV
always @(posedge clk_100M or negedge rst_n) beginif(~rst_n) beginFWD_INV		<= 1'b1;endelse beginif(config_ready & config_vaild) beginFWD_INV		<= config_FWD_INV;endelse beginFWD_INV		<= FWD_INV;endend
end//----------------------------数据输入流控制-------------------------------------
//RAM_addr_w
always @(*) beginRAM_addr_w	<= 16'd0;
end//RAM_data_in
always @(*) beginRAM_data_in		<= 16'd0;
end//RAM_wren
always @(*) beginRAM_wren	<= 1'b0;
end//FIFO_data_in
always @(posedge clk_100M or negedge rst_n) beginif(~rst_n) beginFIFO_data_in	<= 32'd0;endelse beginif(data_in_vaild & data_in_ready) beginFIFO_data_in	<= {data_in_Img, data_in_Real};endelse beginFIFO_data_in	<= FIFO_data_in;endend
end//FIFO_wren
always @(*) beginFIFO_wren	<= data_in_vaild & data_in_ready;
end//data_in_ready
always @(*) begindata_in_ready	<= ~FIFO_full;
end//----------------------------数据输出流控制-------------------------------------
//data_out_Real
always @(*) begindata_out_Real	<= XK_Real;
end//data_out_Img
always @(*) begindata_out_Img	<= XK_Img;
end//data_out_Idx
always @(*) begindata_out_Idx	<= XK_INDEX;
end//data_out_vaild
always @(*) begindata_out_vaild	<= m_axis_data_tvalid;
end//m_axis_data_tready
always @(*) beginm_axis_data_tready	<= data_out_ready;
end//-----------------------------STFT 流程控制-------------------------------------
localparam	S_IDLE		= 8'h01;	//等待FIFO中数据足够一次FFT
localparam	S_PUSH_DATA	= 8'h02;	//向FFT IP推送数据
localparam	S_OVERLAP	= 8'h04;	//滑动STFT窗口
localparam	S_END		= 8'h08;reg		[7:0]	state	= S_IDLE;
reg		[7:0]	next_state;always @(posedge clk_100M or negedge rst_n) beginif(~rst_n) beginstate	<= S_IDLE;endelse beginstate	<= next_state;end
endalways @(*) begincase(state)S_IDLE: beginif(FIFO_element_cnt >= (1 << NFFT)) beginnext_state	<= S_PUSH_DATA;endelse beginnext_state	<= S_IDLE;endendS_PUSH_DATA: beginif(cnt >= (1 << NFFT) + 4) begin		//NFFT个clk使能读RAM数据,以及4个clk的读数据输出延迟next_state	<= S_OVERLAP;endelse beginnext_state	<= S_PUSH_DATA;endendS_OVERLAP: beginnext_state	<= S_END;endS_END: beginnext_state	<= S_IDLE;enddefault: beginnext_state	<= S_IDLE;endendcase
end//cnt
reg		[15:0]	cnt		= 16'd0;	//S_PUSH_DATA阶段进行计数,以控制正确的数据流输入FFT_IP
always @(posedge clk_100M) begincase(state)S_PUSH_DATA: begincnt		<= cnt + 1'b1;enddefault: begincnt		<= 16'd0;endendcase
end//FIFO_rden
always @(*) beginFIFO_rden	<= 1'b0;
end//RAM_addr_r
always @(posedge clk_100M) begincase(state)S_IDLE: beginRAM_addr_r	<= FIFO_head;endS_PUSH_DATA: beginif(cnt <= (1 << NFFT) - 1'b1) beginRAM_addr_r	<= (RAM_addr_r == FIFO_Depth - 1'b1)? 16'd0 : RAM_addr_r + 1'b1;endelse beginRAM_addr_r	<= RAM_addr_r;endenddefault: beginRAM_addr_r	<= RAM_addr_r;endendcase
end//RAM_rden
always @(posedge clk_100M) begincase(state)S_PUSH_DATA: beginif(cnt <= (1 << NFFT) - 1'b1) beginRAM_rden	<= 1'b1;endelse beginRAM_rden	<= 1'b0;endenddefault: beginRAM_rden	<= 1'b0;endendcase
end//XN_Real & XN_Img
always @(posedge clk_100M) begincase(state)S_PUSH_DATA: beginif(RAM_out_vaild) beginXN_Real		<= RAM_data_out[15:0];XN_Img		<= RAM_data_out[31:16];endelse beginXN_Real		<= XN_Real;XN_Img		<= XN_Img;endenddefault: beginXN_Real		<= 16'd0;XN_Img		<= 16'd0;endendcase
end//s_axis_data_tvalid
always @(posedge clk_100M) begincase(state)S_PUSH_DATA: beginif(RAM_out_vaild) begins_axis_data_tvalid	<= 1'b1;endelse begins_axis_data_tvalid	<= 1'b0;endenddefault: begins_axis_data_tvalid	<= 1'b0;endendcase
end//s_axis_data_tlast
always @(posedge clk_100M) begincase(state)S_PUSH_DATA: beginif(RAM_out_vaild && (cnt == (1 << NFFT) + 4)) begins_axis_data_tlast	<= 1'b1;endelse begins_axis_data_tlast	<= 1'b0;endenddefault: begins_axis_data_tlast	<= 1'b0;endendcase
end//FIFO_config_set_Head
always @(posedge clk_100M) begincase(state)S_OVERLAP: beginif(FIFO_config_ready) beginFIFO_config_set_Head	<= FIFO_head + STFT_overlap;endelse beginFIFO_config_set_Head	<= FIFO_head;endenddefault: beginFIFO_config_set_Head	<= FIFO_head;endendcase
end//FIFO_config_set_Tail
always @(*) beginFIFO_config_set_Tail	<= FIFO_tail;
end//FIFO_config_vaild
always @(posedge clk_100M) begincase(state)S_OVERLAP: beginFIFO_config_vaild	<= 2'b01;enddefault: beginFIFO_config_vaild	<= 2'b00;endendcase
endendmodule

testbench

//测试STFT
`timescale 1ns/1ns
`default_nettype nonemodule STFT_tb();reg		clk_100M	= 1'b1;
reg		rst_n		= 1'b1;always #5 beginclk_100M	<= ~clk_100M;
end//------------------------STFT-------------------------------
//data in
reg		signed	[15:0]	data_in_Real		= 16'sd0;
reg		signed	[15:0]	data_in_Img			= 16'sd0;
reg						data_in_vaild		= 1'b0;
wire					data_in_ready;//data out
wire	signed	[15:0]	data_out_Real;
wire	signed	[15:0]	data_out_Img;
wire			[15:0]	data_out_Idx;
wire					data_out_vaild;
reg						data_out_ready		= 1'b0;//Config
reg				[4:0]	config_NFFT			= 5'd10;		//STFT点数(以二为底的指数,最大16)
reg				[15:0]	config_overlap		= 16'd128;		//STFT步进
reg				[15:0]	config_SCALE_SCH	= 16'haaaa;		//缩放系数,每2bit控制一个蝶形单元(Radix-4)的缩放,2'b2
reg						config_FWD_INV		= 1'b1;			//FFT(1) or IFFT(0)
reg						config_vaild		= 1'b0;
wire					config_ready;STFT STFT_inst(.clk_100M			(clk_100M),.rst_n				(rst_n),//data in.data_in_Real		(data_in_Real),.data_in_Img		(data_in_Img),.data_in_vaild		(data_in_vaild),.data_in_ready		(data_in_ready),//data out.data_out_Real		(data_out_Real),.data_out_Img		(data_out_Img),.data_out_Idx		(data_out_Idx),.data_out_vaild		(data_out_vaild),.data_out_ready		(data_out_ready),//Config.config_NFFT		(config_NFFT),			//STFT点数(以二为底的指数,最大16).config_overlap		(config_overlap),		//STFT步进.config_SCALE_SCH	(config_SCALE_SCH),		//缩放系数,每2bit控制一个蝶形单元(Radix-4)的缩放,2'b2.config_FWD_INV		(config_FWD_INV),		//FFT(1) or IFFT(0).config_vaild		(config_vaild),.config_ready		(config_ready)
);//------------------------------------------------------------
//data in
always @(posedge clk_100M) beginif(data_in_ready & rst_n) begindata_in_Real	<= data_in_Real + 16'sd1;data_in_Img		<= data_in_Img + 16'sd0;data_in_vaild	<= 1'b1;endelse begindata_in_Real	<= data_in_Real;data_in_Img		<= data_in_Img;data_in_vaild	<= 1'b0;end
end//data out
always @(posedge clk_100M) begindata_out_ready	<= 1'b1;
end//Config
always @(posedge clk_100M) beginif(config_ready) beginconfig_NFFT			<= 5'd10;		//1024点config_overlap		<= 16'd128;		//noverlap 128config_SCALE_SCH	<= 16'haaaa;	//缩放因子 1/NFFTconfig_FWD_INV		<= 1'b1;		//执行FFTconfig_vaild		<= 1'b1;endelse beginconfig_NFFT			<= config_NFFT;config_overlap		<= config_overlap;config_SCALE_SCH	<= config_SCALE_SCH;config_FWD_INV		<= config_FWD_INV;config_vaild		<= 1'b0;end
end//main
initial beginrst_n	<= 1'b0;#100;rst_n	<= 1'b1;#100;#1000000;$stop;
endendmodule

在这里插入图片描述

与 MATLAB 结果比对

%--------------------验证STFT结果---------------------------
clc,clear,close all%% data
datas = 1:65535;	%testbench里STFT的数据流%% STFT
fs=1;
nfft=1024;
window_len=nfft;
noverlap=128;
overlap=window_len-noverlap;[s,f,t] = stft(datas,fs,'window',ones(window_len,1),'OverlapLength',overlap,'FFTLength',nfft);
s = s/nfft;%% plot
figure('color','w')
mesh(t,f,real(s))
xlabel('$t/s$','interpreter','latex')
ylabel('$f/Hz$','interpreter','latex')figure('color','w')
mesh(t,f,imag(s))
xlabel('$t/s$','interpreter','latex')
ylabel('$f/Hz$','interpreter','latex')

  可以对比 MATLAB 计算结果与 FPGA 计算结果,误差在 ± 3 \pm3 ±3 以内,读者也可自行运行上述程序验证。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【功能自动化】WebTours实现订票流程并设置检查点
  • 小程序学习day11-生命周期函数、组件所在页面的生命周期、自定义组件的插槽、自定义组件的父子通信
  • VS2022 - 制作自己的C#类库dll,并输出Unity识别的pdb调试信息文件
  • 7. 数据结构—二叉树(链式存储)
  • 改编版猜数字小游戏,猜错了就黑屏(整蛊版本)
  • PhotoZoom Pro 9:AI加持让图像放大革命性飞跃 PhotoZoom下载
  • mkv怎么转换mp4格式?格式转换秘籍大揭底!
  • 《黑神话:悟空》发售后快手游戏笔记本电脑GMV日环比增长40%
  • haproxy编译安装
  • 闲置物品交易平台网站商城-计算机毕设Java|springboot实战项目
  • 泛微eteams OA对接金蝶云星空写入数据
  • 火语言RPA流程组件介绍--打开文件/运行进程命令
  • 通过Qt Creator Plugin开发Qt Creator插件-【金丹篇】
  • 视频项目开发,EasyCVR视频融合平台为何成为关键驱动力
  • jenkins最佳实践(一):jenkins安装与部署
  • (ckeditor+ckfinder用法)Jquery,js获取ckeditor值
  • 【附node操作实例】redis简明入门系列—字符串类型
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • 3.7、@ResponseBody 和 @RestController
  • Angular数据绑定机制
  • HTML中设置input等文本框为不可操作
  • JSDuck 与 AngularJS 融合技巧
  • LeetCode29.两数相除 JavaScript
  • mysql innodb 索引使用指南
  • PAT A1120
  • Python 基础起步 (十) 什么叫函数?
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • vue-loader 源码解析系列之 selector
  • Web Storage相关
  • 不发不行!Netty集成文字图片聊天室外加TCP/IP软硬件通信
  • 代理模式
  • 聚簇索引和非聚簇索引
  • 前端路由实现-history
  • 什么是Javascript函数节流?
  • 小程序测试方案初探
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 【云吞铺子】性能抖动剖析(二)
  • 正则表达式-基础知识Review
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • #07【面试问题整理】嵌入式软件工程师
  • (MATLAB)第五章-矩阵运算
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (附源码)计算机毕业设计ssm基于Internet快递柜管理系统
  • (力扣记录)235. 二叉搜索树的最近公共祖先
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (转)EOS中账户、钱包和密钥的关系
  • (转)VC++中ondraw在什么时候调用的
  • **PHP二维数组遍历时同时赋值
  • .htaccess配置常用技巧
  • .mp4格式的视频为何不能通过video标签在chrome浏览器中播放?
  • .NET COER+CONSUL微服务项目在CENTOS环境下的部署实践
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .NET Micro Framework初体验
  • .NET MVC 验证码