383 lines
12 KiB
Verilog
383 lines
12 KiB
Verilog
`define FFT_CSR_ADDR 16'hf000
|
||
`define FFT_IN_RE_BASE 16'h1000
|
||
`define FFT_IN_IM_BASE 16'h2000
|
||
`define FFT_OUT_RE_BASE 16'h3000
|
||
`define FFT_OUT_IM_BASE 16'h4000
|
||
|
||
module fft_controller #(
|
||
parameter FFT_LEN = 1024, // FFT处理长度
|
||
parameter BRAM_RD_LATENCY = 2 // 适配 BRAM 的读延迟 (通常 Block RAM 开启输出寄存器为 2)
|
||
)(
|
||
input aclk,
|
||
input aresetn,
|
||
|
||
// AXI Lite / AXI4 Wrapper ports
|
||
input [4 :0] s_awid,
|
||
input [31:0] s_awaddr,
|
||
input [7 :0] s_awlen,
|
||
input [2 :0] s_awsize,
|
||
input [1 :0] s_awburst,
|
||
input s_awlock,
|
||
input [3 :0] s_awcache,
|
||
input [2 :0] s_awprot,
|
||
input s_awvalid,
|
||
output reg s_awready,
|
||
input [4 :0] s_wid,
|
||
input [31:0] s_wdata,
|
||
input [3 :0] s_wstrb,
|
||
input s_wlast,
|
||
input s_wvalid,
|
||
output reg s_wready,
|
||
output [4 :0] s_bid,
|
||
output [1 :0] s_bresp,
|
||
output reg s_bvalid,
|
||
input s_bready,
|
||
|
||
input [4 :0] s_arid,
|
||
input [31:0] s_araddr,
|
||
input [7 :0] s_arlen,
|
||
input [2 :0] s_arsize,
|
||
input [1 :0] s_arburst,
|
||
input s_arlock,
|
||
input [3 :0] s_arcache,
|
||
input [2 :0] s_arprot,
|
||
input s_arvalid,
|
||
output reg s_arready,
|
||
output [4 :0] s_rid,
|
||
output reg [31:0] s_rdata,
|
||
output [1 :0] s_rresp,
|
||
output reg s_rlast,
|
||
output reg s_rvalid,
|
||
input s_rready,
|
||
|
||
output fft_finish
|
||
);
|
||
|
||
// AXI Burst 内部寄存器
|
||
reg [31:0] aw_addr_reg;
|
||
reg [1 :0] aw_burst_reg;
|
||
reg [4 :0] aw_id_reg;
|
||
|
||
reg [31:0] ar_addr_reg;
|
||
reg [7 :0] ar_len_reg;
|
||
reg [1 :0] ar_burst_reg;
|
||
reg [4 :0] ar_id_reg;
|
||
reg [7 :0] ar_cnt;
|
||
|
||
// 写通道状态机 (AW, W, B)
|
||
localparam W_IDLE = 2'd0;
|
||
localparam W_DATA = 2'd1;
|
||
localparam W_RESP = 2'd2;
|
||
reg [1:0] w_state;
|
||
|
||
always @(posedge aclk) begin
|
||
if (~aresetn) begin
|
||
w_state <= W_IDLE;
|
||
s_awready <= 1'b1;
|
||
s_wready <= 1'b0;
|
||
s_bvalid <= 1'b0;
|
||
aw_addr_reg <= 32'h0;
|
||
aw_burst_reg <= 2'h0;
|
||
aw_id_reg <= 5'h0;
|
||
end else begin
|
||
case (w_state)
|
||
W_IDLE: begin
|
||
s_awready <= 1'b1;
|
||
if (s_awvalid && s_awready) begin
|
||
s_awready <= 1'b0;
|
||
s_wready <= 1'b1;
|
||
aw_addr_reg <= s_awaddr;
|
||
aw_burst_reg <= s_awburst;
|
||
aw_id_reg <= s_awid;
|
||
w_state <= W_DATA;
|
||
end
|
||
end
|
||
W_DATA: begin
|
||
// 接收到有效写数据
|
||
if (s_wvalid && s_wready) begin
|
||
// INCR 模式下地址递增
|
||
if (aw_burst_reg == 2'b01) begin
|
||
aw_addr_reg <= aw_addr_reg + 4;
|
||
end
|
||
// 若是最后一拍,跳转到发送响应
|
||
if (s_wlast) begin
|
||
s_wready <= 1'b0;
|
||
s_bvalid <= 1'b1;
|
||
w_state <= W_RESP;
|
||
end
|
||
end
|
||
end
|
||
W_RESP: begin
|
||
if (s_bvalid && s_bready) begin
|
||
s_bvalid <= 1'b0;
|
||
s_awready <= 1'b1; // 准备接收下一次写请求
|
||
w_state <= W_IDLE;
|
||
end
|
||
end
|
||
default: w_state <= W_IDLE;
|
||
endcase
|
||
end
|
||
end
|
||
|
||
// 产生有效的写动作脉冲,用于触发 BRAM 写使能
|
||
wire w_active = (w_state == W_DATA) && s_wvalid && s_wready;
|
||
assign s_bid = aw_id_reg;
|
||
assign s_bresp = 2'b00;
|
||
|
||
// 读通道状态机(AR, R)
|
||
localparam R_IDLE = 2'd0;
|
||
localparam R_WAIT = 2'd1;
|
||
localparam R_DATA = 2'd2;
|
||
reg [1:0] r_state;
|
||
reg [3:0] r_wait_cnt;
|
||
|
||
always @(posedge aclk) begin
|
||
if (~aresetn) begin
|
||
r_state <= R_IDLE;
|
||
s_arready <= 1'b1;
|
||
s_rvalid <= 1'b0;
|
||
s_rlast <= 1'b0;
|
||
ar_addr_reg <= 32'h0;
|
||
ar_len_reg <= 8'h0;
|
||
ar_burst_reg <= 2'h0;
|
||
ar_id_reg <= 5'h0;
|
||
ar_cnt <= 8'h0;
|
||
r_wait_cnt <= 4'h0;
|
||
end else begin
|
||
case (r_state)
|
||
R_IDLE: begin
|
||
s_arready <= 1'b1;
|
||
if (s_arvalid && s_arready) begin
|
||
s_arready <= 1'b0;
|
||
ar_addr_reg <= s_araddr;
|
||
ar_len_reg <= s_arlen;
|
||
ar_burst_reg <= s_arburst;
|
||
ar_id_reg <= s_arid;
|
||
ar_cnt <= 8'h0;
|
||
r_wait_cnt <= BRAM_RD_LATENCY;
|
||
r_state <= R_WAIT; // 进入等待以拉取第一笔 BRAM 数据
|
||
end
|
||
end
|
||
R_WAIT: begin
|
||
// 等待 BRAM 固定潜伏期结束
|
||
if (r_wait_cnt <= 1) begin
|
||
s_rvalid <= 1'b1;
|
||
s_rlast <= (ar_cnt == ar_len_reg);
|
||
r_state <= R_DATA;
|
||
r_wait_cnt <= 4'h0;
|
||
end else begin
|
||
r_wait_cnt <= r_wait_cnt - 1;
|
||
end
|
||
end
|
||
R_DATA: begin
|
||
// 数据被总线成功读取
|
||
if (s_rvalid && s_rready) begin
|
||
if (ar_cnt == ar_len_reg) begin
|
||
// Burst 结束
|
||
s_rvalid <= 1'b0;
|
||
s_rlast <= 1'b0;
|
||
s_arready <= 1'b1;
|
||
r_state <= R_IDLE;
|
||
end else begin
|
||
// 准备读取下一拍数据
|
||
ar_cnt <= ar_cnt + 1;
|
||
// INCR 模式下地址递增
|
||
if (ar_burst_reg == 2'b01) begin
|
||
ar_addr_reg <= ar_addr_reg + 4;
|
||
end
|
||
s_rvalid <= 1'b0;
|
||
s_rlast <= 1'b0;
|
||
r_wait_cnt <= BRAM_RD_LATENCY;
|
||
r_state <= R_WAIT; // 再次进入等待状态
|
||
end
|
||
end
|
||
end
|
||
default: r_state <= R_IDLE;
|
||
endcase
|
||
end
|
||
end
|
||
|
||
assign s_rid = ar_id_reg;
|
||
assign s_rresp = 2'b00;
|
||
|
||
// 地址译码
|
||
wire hit_in_re = (aw_addr_reg[15:12] == 4'h1);
|
||
wire hit_in_im = (aw_addr_reg[15:12] == 4'h2);
|
||
wire hit_out_re = (ar_addr_reg[15:12] == 4'h3);
|
||
wire hit_out_im = (ar_addr_reg[15:12] == 4'h4);
|
||
|
||
// 寄存器控制
|
||
reg [3:0] fft_ctrl;
|
||
reg [3:0] fft_status;
|
||
|
||
wire [31:0] fft_csr = {24'h0, fft_ctrl, fft_status};
|
||
// 只有发生有效的写握手且地址对应 CSR 时,才写入控制寄存器
|
||
wire write_fft_csr = w_active && (aw_addr_reg[15:0] == `FFT_CSR_ADDR);
|
||
|
||
// 状态机
|
||
localparam IDLE = 2'd0;
|
||
localparam LOAD = 2'd1;
|
||
localparam WAIT_OUT = 2'd2;
|
||
localparam DONE = 2'd3;
|
||
|
||
reg [1:0] state, next_state;
|
||
reg [10:0] in_cnt;
|
||
reg [10:0] out_cnt;
|
||
|
||
wire do_en;
|
||
wire [31:0] do_re;
|
||
wire [31:0] do_im;
|
||
|
||
always @(posedge aclk) begin
|
||
if (~aresetn) state <= IDLE;
|
||
else state <= next_state;
|
||
end
|
||
|
||
always @(*) begin
|
||
case (state)
|
||
IDLE: if (fft_ctrl[0]) next_state = LOAD; else next_state = IDLE;
|
||
LOAD: if (in_cnt == FFT_LEN - 1) next_state = WAIT_OUT; else next_state = LOAD;
|
||
WAIT_OUT: if (do_en && out_cnt == FFT_LEN - 1) next_state = DONE; else next_state = WAIT_OUT;
|
||
DONE: next_state = IDLE;
|
||
default: next_state = IDLE;
|
||
endcase
|
||
end
|
||
|
||
always @(posedge aclk) begin
|
||
if (~aresetn) begin
|
||
in_cnt <= 0;
|
||
out_cnt <= 0;
|
||
end else if (state == IDLE) begin
|
||
in_cnt <= 0;
|
||
out_cnt <= 0;
|
||
end else begin
|
||
if (state == LOAD) in_cnt <= in_cnt + 1;
|
||
if (do_en) out_cnt <= out_cnt + 1;
|
||
end
|
||
end
|
||
|
||
// 更新寄存器与状态
|
||
always @(posedge aclk) begin
|
||
if (~aresetn) begin
|
||
fft_ctrl <= 4'h0;
|
||
fft_status <= 4'h0;
|
||
end else begin
|
||
if (write_fft_csr) begin
|
||
fft_ctrl <= s_wdata[7:4];
|
||
end else if (state != IDLE) begin
|
||
fft_ctrl[0] <= 1'b0;
|
||
end
|
||
|
||
fft_status[0] <= (state != IDLE && state != DONE);
|
||
|
||
if (write_fft_csr && s_wdata[4]) begin
|
||
fft_status[1] <= 1'b0;
|
||
end else if (state == DONE) begin
|
||
fft_status[1] <= 1'b1;
|
||
end
|
||
end
|
||
end
|
||
|
||
assign fft_finish = fft_status[1];
|
||
|
||
// BRAM 接口连线
|
||
wire [31:0] in_re_dout;
|
||
wire [31:0] in_im_dout;
|
||
wire [31:0] out_re_dout;
|
||
wire [31:0] out_im_dout;
|
||
|
||
bram0 u_in0(
|
||
.clka (aclk),
|
||
.ena (w_active && hit_in_re), // 由 burst写逻辑产生
|
||
.wea (1'b1),
|
||
.addra (aw_addr_reg[11:2]), // 取当前递增的突发写地址
|
||
.dina (s_wdata),
|
||
|
||
.clkb (aclk),
|
||
.enb (state == LOAD),
|
||
.addrb (in_cnt[9:0]),
|
||
.doutb (in_re_dout)
|
||
);
|
||
|
||
bram0 u_in1(
|
||
.clka (aclk),
|
||
.ena (w_active && hit_in_im),
|
||
.wea (1'b1),
|
||
.addra (aw_addr_reg[11:2]),
|
||
.dina (s_wdata),
|
||
|
||
.clkb (aclk),
|
||
.enb (state == LOAD),
|
||
.addrb (in_cnt[9:0]),
|
||
.doutb (in_im_dout)
|
||
);
|
||
|
||
// 对于输出 BRAM,采用原先的位反转(Bit-reverse)读取逻辑
|
||
wire [9:0] b_rd_addr = ar_addr_reg[11:2];
|
||
wire [9:0] bit_reversed_index = {
|
||
b_rd_addr[0], b_rd_addr[1], b_rd_addr[2], b_rd_addr[3], b_rd_addr[4],
|
||
b_rd_addr[5], b_rd_addr[6], b_rd_addr[7], b_rd_addr[8], b_rd_addr[9]
|
||
};
|
||
|
||
bram0 u_out0(
|
||
.clka (aclk),
|
||
.ena (do_en),
|
||
.wea (1'b1),
|
||
.addra (out_cnt[9:0]),
|
||
.dina (do_re),
|
||
|
||
.clkb (aclk),
|
||
.enb (r_state != R_IDLE), // 当处于读突发事务时始终使能 BRAM
|
||
.addrb (bit_reversed_index),
|
||
.doutb (out_re_dout)
|
||
);
|
||
|
||
bram0 u_out1(
|
||
.clka (aclk),
|
||
.ena (do_en),
|
||
.wea (1'b1),
|
||
.addra (out_cnt[9:0]),
|
||
.dina (do_im),
|
||
|
||
.clkb (aclk),
|
||
.enb (r_state != R_IDLE),
|
||
.addrb (bit_reversed_index),
|
||
.doutb (out_im_dout)
|
||
);
|
||
|
||
// 支持 1 到 N 拍的读延迟,让 di_en 严格对齐 BRAM 的吐出数据
|
||
reg [3:0] di_en_shift;
|
||
always @(posedge aclk) begin
|
||
if (~aresetn) di_en_shift <= 0;
|
||
else di_en_shift <= {di_en_shift[2:0], (state == LOAD)};
|
||
end
|
||
|
||
wire di_en_r = (BRAM_RD_LATENCY == 1) ? di_en_shift[0] :
|
||
(BRAM_RD_LATENCY == 2) ? di_en_shift[1] :
|
||
(BRAM_RD_LATENCY == 3) ? di_en_shift[2] : di_en_shift[3];
|
||
|
||
FFT #(
|
||
.WIDTH(32)
|
||
) u_FFT (
|
||
.clock (aclk),
|
||
.reset (~aresetn),
|
||
.di_en (di_en_r),
|
||
.di_re (in_re_dout),
|
||
.di_im (in_im_dout),
|
||
.do_en (do_en),
|
||
.do_re (do_re),
|
||
.do_im (do_im)
|
||
);
|
||
|
||
// 读取返回数据选择
|
||
wire [31:0] rdata_d =
|
||
(ar_addr_reg[15:0] == `FFT_CSR_ADDR) ? fft_csr :
|
||
hit_out_re ? out_re_dout :
|
||
hit_out_im ? out_im_dout :
|
||
32'h0;
|
||
|
||
always @(*) begin
|
||
s_rdata = rdata_d;
|
||
end
|
||
|
||
endmodule |