`timescale 1ns / 1ps

module novena_eim_wrapper (
    // EIM Asynchronous SRAM Interface (from i.MX6)
    input  wire        eim_bclk, // Sometimes used for sync, but Novena uses async EIM usually.
    input  wire        eim_cs0_n,
    input  wire        eim_oe_n,
    input  wire        eim_we_n,
    input  wire [15:0] eim_addr, // 16-bit word address
    inout  wire [15:0] eim_data,
    
    // Internal FPGA Clock
    input  wire        clk,
    input  wire        rst_n
);

    // ============================================
    // EIM Bus Synchronization & Demux
    // ============================================
    reg [2:0] cs_sync;
    reg [2:0] we_sync;
    reg [2:0] oe_sync;
    reg [15:0] addr_sync [0:2];
    reg [15:0] data_in_sync [0:2];

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cs_sync <= 3'b111;
            we_sync <= 3'b111;
            oe_sync <= 3'b111;
        end else begin
            cs_sync <= {cs_sync[1:0], eim_cs0_n};
            we_sync <= {we_sync[1:0], eim_we_n};
            oe_sync <= {oe_sync[1:0], eim_oe_n};
            addr_sync[2] <= addr_sync[1]; addr_sync[1] <= addr_sync[0]; addr_sync[0] <= eim_addr;
            data_in_sync[2] <= data_in_sync[1]; data_in_sync[1] <= data_in_sync[0]; data_in_sync[0] <= eim_data;
        end
    end

    wire eim_write_pulse = (cs_sync[2] == 0) && (we_sync[2] == 1 && we_sync[1] == 0);
    wire eim_read_active = (cs_sync[1] == 0) && (oe_sync[1] == 0);

    // ============================================
    // Memory Map
    // ============================================
    // TX Buffer: 2048 words (to satisfy XST index range checks)
    reg [15:0] tx_bram [0:2047];
    // RX Buffer: 2048 words
    reg [15:0] rx_bram [0:2047];

    reg control_start;
    reg status_busy;
    reg status_done;
    reg [7:0] final_error_count;
    reg [15:0] read_data_reg;

    // EIM Write Logic
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            control_start <= 0;
        end else begin
            control_start <= 0; // Auto-clear
            if (eim_write_pulse) begin
                if (addr_sync[2] < 16'h0800) begin
                    if (addr_sync[2] < 1152) begin
                        tx_bram[addr_sync[2]] <= data_in_sync[2];
                    end
                end else if (addr_sync[2] == 16'h1000) begin
                    if (data_in_sync[2][0]) control_start <= 1;
                end
            end
        end
    end

    // EIM Read Logic
    always @(posedge clk) begin
        if (eim_read_active) begin
            if (addr_sync[1] < 16'h0800) begin
                if (addr_sync[1] < 1152) read_data_reg <= tx_bram[addr_sync[1]];
                else read_data_reg <= 16'h0000;
            end else if (addr_sync[1] >= 16'h0800 && addr_sync[1] < 16'h1000) begin
                if ((addr_sync[1] - 16'h0800) < 1152) read_data_reg <= rx_bram[addr_sync[1] - 16'h0800];
                else read_data_reg <= 16'h0000;
            end else if (addr_sync[1] == 16'h1001) begin
                read_data_reg <= {final_error_count, 6'b0, status_done, status_busy};
            end else begin
                read_data_reg <= 16'hDEAD;
            end
        end
    end

    assign eim_data = eim_read_active ? read_data_reg : 16'hZ;

    // Dummy use of eim_bclk to silence XST unused input warning
    wire dummy_bclk_use = eim_bclk;

    // ============================================
    // Meggitt Decoder Instantiation & State Machine
    // ============================================
    reg dec_start;
    reg [7:0] dec_data_in;
    reg dec_data_valid;
    wire dec_done;
    wire [7:0] dec_data_out;
    wire dec_data_out_valid;
    wire [7:0] dec_error_count;

    meggitt_decoder_trap decoder_inst (
        .clk(clk),
        .rst_n(rst_n),
        .start(dec_start),
        .data_in(dec_data_in),
        .data_valid(dec_data_valid),
        .done(dec_done),
        .data_out(dec_data_out),
        .data_out_valid(dec_data_out_valid),
        .bit_counter(),
        .error_count(dec_error_count)
    );

    localparam SM_IDLE = 3'd0;
    localparam SM_LOAD = 3'd1;
    localparam SM_WAIT = 3'd2;
    localparam SM_READ = 3'd3;
    localparam SM_DONE = 3'd4;

    reg [2:0] state;
    reg [11:0] byte_counter;
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state <= SM_IDLE;
            status_busy <= 0;
            status_done <= 0;
            dec_start <= 0;
            dec_data_valid <= 0;
            byte_counter <= 0;
            final_error_count <= 0;
        end else begin
            case (state)
                SM_IDLE: begin
                    dec_start <= 0;
                    dec_data_valid <= 0;
                    if (control_start) begin
                        state <= SM_LOAD;
                        status_busy <= 1;
                        status_done <= 0;
                        byte_counter <= 0;
                        dec_start <= 1;
                    end
                end

                SM_LOAD: begin
                    dec_start <= 0;
                    if (byte_counter < 2304) begin
                        dec_data_valid <= 1;
                        // Fetch byte from TX BRAM (16-bit words)
                        // Big-endian or little-endian layout depends on host. Assume Big-Endian for now (high byte first).
                        if (byte_counter[0] == 0) begin
                            dec_data_in <= tx_bram[byte_counter[11:1]][15:8];
                        end else begin
                            dec_data_in <= tx_bram[byte_counter[11:1]][7:0];
                        end
                        byte_counter <= byte_counter + 1'b1;
                    end else begin
                        dec_data_valid <= 0;
                        state <= SM_WAIT;
                    end
                end

                SM_WAIT: begin
                    if (dec_done) begin
                        state <= SM_READ;
                        byte_counter <= 0;
                        final_error_count <= dec_error_count;
                    end
                end

                SM_READ: begin
                    if (dec_data_out_valid) begin
                        if (byte_counter[0] == 0) begin
                            rx_bram[byte_counter[11:1]][15:8] <= dec_data_out;
                        end else begin
                            rx_bram[byte_counter[11:1]][7:0] <= dec_data_out;
                        end
                        byte_counter <= byte_counter + 1'b1;
                        if (byte_counter == 2303) begin
                            state <= SM_DONE;
                        end
                    end
                end

                SM_DONE: begin
                    status_busy <= 0;
                    status_done <= 1;
                    state <= SM_IDLE;
                end
            endcase
        end
    end

endmodule
