`timescale 1ns/1ps
// GPIO control: bit31 toggles a new frame, bit30 selects the case, bits19:16 select
// a result register, bits8:0 select a spectrum bin. No external ADC yet.
module fft_demo #(parameter SIM_OUTPUT_STALLS = 0) (
    input wire clk,
    input wire resetn,
    input wire [31:0] control,
    output reg [31:0] report_data,
    output reg led_n
);
    localparam CONFIGURE=0, FETCH=1, SEND=2, DRAIN=3, DONE=4;
    reg [2:0] state;
    reg [8:0] sample_index;
    reg case_id;
    reg [31:0] source_rom [0:1023];
    reg [31:0] source_data;
    initial $readmemh("tone_q15.mem", source_rom);
    always @(posedge clk)
        if (state == FETCH) source_data <= source_rom[{case_id, sample_index}];

    wire config_ready, input_ready, output_valid, output_last;
    wire [31:0] output_data;
    wire [23:0] output_user;
    wire [7:0] fft_status;
    wire fft_status_valid, event_started, event_unexpected, event_missing;
    wire event_overflow, event_status_halt, event_input_halt, event_output_halt;
    reg [7:0] stall_counter;
    wire output_ready = !SIM_OUTPUT_STALLS || (stall_counter[2:0] != 0 && stall_counter[2:0] != 1);
    wire output_fire = output_valid && output_ready;

    // Radix-2: first stage shifts by two, eight others by one => 1/1024.
    // PG109 v9.0: FWD_INV is bit0; SCALE_SCH follows immediately in bits18:1.
    xfft_512 fft (
        .aclk(clk), .aresetn(resetn),
        .s_axis_config_tdata(24'h02aaad),
        .s_axis_config_tvalid(state == CONFIGURE), .s_axis_config_tready(config_ready),
        .s_axis_data_tdata(source_data), .s_axis_data_tvalid(state == SEND),
        .s_axis_data_tready(input_ready), .s_axis_data_tlast(sample_index == 511),
        .m_axis_data_tdata(output_data), .m_axis_data_tuser(output_user),
        .m_axis_data_tvalid(output_valid), .m_axis_data_tready(output_ready),
        .m_axis_data_tlast(output_last),
        .m_axis_status_tdata(fft_status), .m_axis_status_tvalid(fft_status_valid),
        .m_axis_status_tready(1'b1),
        .event_frame_started(event_started), .event_tlast_unexpected(event_unexpected),
        .event_tlast_missing(event_missing), .event_fft_overflow(event_overflow),
        .event_status_channel_halt(event_status_halt),
        .event_data_in_channel_halt(event_input_halt),
        .event_data_out_channel_halt(event_output_halt)
    );

    wire signed [15:0] fft_i = output_data[15:0];
    wire signed [15:0] fft_q = output_data[31:16];
    reg [31:0] i_square, q_square, power_value;
    reg square_valid, power_valid, square_last, power_last;
    reg [8:0] square_index, power_index;
    reg [31:0] spectrum_iq [0:511];
    reg [31:0] spectrum_power [0:511];
    reg [31:0] read_iq, read_power;
    reg [31:0] peak_power;
    reg [8:0] peak_index;
    reg [9:0] input_count, output_count;
    reg [7:0] errors;
    reg [31:0] frame_count;
    reg start_seen;
    reg [24:0] led_count;

    // Synchronous read ports infer on-chip memories; ARM waits before reading.
    always @(posedge clk) begin
        read_iq <= spectrum_iq[control[8:0]];
        read_power <= spectrum_power[control[8:0]];
        case (control[19:16])
            0: report_data <= {29'b0, |errors, state != DONE, state == DONE};
            1: report_data <= {23'b0, peak_index};
            2: report_data <= peak_power;
            3: report_data <= {22'b0, input_count};
            4: report_data <= {22'b0, output_count};
            5: report_data <= {24'b0, errors};
            6: report_data <= read_iq;
            7: report_data <= read_power;
            8: report_data <= 32'h524E4731;
            9: report_data <= frame_count;
            10: report_data <= {31'b0, case_id};
            default: report_data <= 0;
        endcase
    end

    always @(posedge clk) begin
        if (!resetn) begin
            state <= CONFIGURE; sample_index <= 0; start_seen <= 0; case_id <= 0;
            input_count <= 0; output_count <= 0; errors <= 0;
            peak_power <= 0; peak_index <= 0; frame_count <= 0;
            square_valid <= 0; power_valid <= 0;
            i_square <= 0; q_square <= 0; power_value <= 0;
            square_index <= 0; power_index <= 0; square_last <= 0; power_last <= 0;
            stall_counter <= 0; led_count <= 0; led_n <= 1;
        end else begin
            stall_counter <= stall_counter + 1'b1;
            if (led_count == 24999999) begin
                led_count <= 0; led_n <= ~led_n;
            end else led_count <= led_count + 1'b1;

            if (event_unexpected) errors[0] <= 1;
            if (event_missing) errors[1] <= 1;
            if (event_overflow || (fft_status_valid && fft_status[0])) errors[2] <= 1;

            case (state)
                CONFIGURE: if (config_ready) state <= FETCH;
                FETCH: state <= SEND;
                SEND: if (input_ready) begin
                    input_count <= input_count + 1'b1;
                    if (sample_index == 511) state <= DRAIN;
                    else begin sample_index <= sample_index + 1'b1; state <= FETCH; end
                end
                DONE: if (control[31] != start_seen) begin
                    start_seen <= control[31]; state <= CONFIGURE; sample_index <= 0;
                    case_id <= control[30];
                    input_count <= 0; output_count <= 0; errors <= 0;
                    peak_power <= 0; peak_index <= 0;
                end
            endcase

            square_valid <= output_fire;
            if (output_fire) begin
                i_square <= fft_i * fft_i;
                q_square <= fft_q * fft_q;
                square_index <= output_user[8:0]; square_last <= output_last;
                spectrum_iq[output_user[8:0]] <= output_data;
                output_count <= output_count + 1'b1;
                if (output_user[8:0] != output_count[8:0]) errors[3] <= 1;
                if (output_last != (output_count == 511)) errors[4] <= 1;
            end
            power_valid <= square_valid;
            if (square_valid) begin
                power_value <= i_square + q_square;
                power_index <= square_index; power_last <= square_last;
            end
            if (power_valid) begin
                spectrum_power[power_index] <= power_value;
                if (power_index >= 1 && power_index <= 255 && power_value > peak_power) begin
                    peak_power <= power_value; peak_index <= power_index;
                end
                if (power_last) begin state <= DONE; frame_count <= frame_count + 1'b1; end
            end
        end
    end
endmodule
