/*
* OFDM deinterleaver
*/
module deinterleave
(
    input clock,
    input reset,
    input enable,

    input [7:0] rate,
    input [5:0] in_bits,
    input [5:0] soft_in_bits,
    input [3:0] soft_in_bits_pos,
    input input_strobe,
    input soft_decoding,

    output reg [5:0] out_bits,
    output [1:0] erase,
    output output_strobe
);

wire ht = rate[7];

wire [5:0] num_data_carrier = ht? 52: 48;
wire [5:0] half_data_carrier = ht? 26: 24;

reg [5:0] addra;
reg [5:0] addrb;

reg [11:0] lut_key;
wire [21:0] lut_out;
wire [21:0] lut_out_delayed;

reg lut_valid;
wire lut_valid_delayed;

assign erase[0] = lut_out_delayed[21];
assign erase[1] = lut_out_delayed[20];

wire [2:0] lut_bita = lut_out_delayed[7:5];
wire [2:0] lut_bitb = lut_out_delayed[4:2];

wire [5:0] bit_outa;
wire [5:0] bit_outb;
wire [5:0] soft_bit_outa;
wire [5:0] soft_bit_outb;
wire [3:0] soft_bit_outa_pos;
wire [3:0] soft_bit_outb_pos;

// Soft and hard decoding
wire [4:0] MOD_TYPE = {rate[7], rate[3:0]};
wire BPSK   = MOD_TYPE == 5'b01011 || MOD_TYPE == 5'b01111 || MOD_TYPE == 5'b10000;
wire QPSK   = MOD_TYPE == 5'b01010 || MOD_TYPE == 5'b01110 || MOD_TYPE == 5'b10001 || MOD_TYPE == 5'b10010;
wire QAM_16 = MOD_TYPE == 5'b01001 || MOD_TYPE == 5'b01101 || MOD_TYPE == 5'b10011 || MOD_TYPE == 5'b10100;
wire QAM_64 = MOD_TYPE == 5'b01000 || MOD_TYPE == 5'b01100 || MOD_TYPE == 5'b10101 || MOD_TYPE == 5'b10110 || MOD_TYPE == 5'b10111;

wire [2:0] N_BPSC_DIV_2 = BPSK ? 3'b000 : (QPSK ? 3'b001 : (QAM_16 ? 3'b010: (QAM_64 ? 3'b011 : 3'b111)));

always @* begin
    if(lut_valid_delayed == 1'b1) begin

        // Soft decoding
        if(soft_decoding && (BPSK || QPSK || QAM_16 || QAM_64)) begin
            if(BPSK || lut_bita < N_BPSC_DIV_2) begin
                if(lut_bita[1:0] == soft_bit_outa_pos[1:0]) begin
                    out_bits[2:0] = soft_bit_outa[2:0];
                end else begin
                    if(bit_outa[lut_bita] == 1'b1)
                        out_bits[2:0] = 3'b111;
                    else
                        out_bits[2:0] = 3'b011;
                end
            end else begin
                if(lut_bita == ({1'b0,soft_bit_outa_pos[3:2]} + N_BPSC_DIV_2)) begin
                    out_bits[2:0] = soft_bit_outa[5:3];
                end else begin
                    if(bit_outa[lut_bita] == 1'b1)
                        out_bits[2:0] = 3'b111;
                    else
                        out_bits[2:0] = 3'b011;
                end
            end

            if(BPSK || lut_bitb < N_BPSC_DIV_2) begin
                if(lut_bitb[1:0] == soft_bit_outb_pos[1:0]) begin
                    out_bits[5:3] = soft_bit_outb[2:0];
                end else begin
                    if(bit_outb[lut_bitb] == 1'b1)
                        out_bits[5:3] = 3'b111;
                    else
                        out_bits[5:3] = 3'b011;
                end
            end else begin
                if(lut_bitb == ({1'b0,soft_bit_outb_pos[3:2]} + N_BPSC_DIV_2)) begin
                    out_bits[5:3] = soft_bit_outb[5:3];
                end else begin
                    if(bit_outb[lut_bitb] == 1'b1)
                        out_bits[5:3] = 3'b111;
                    else
                        out_bits[5:3] = 3'b011;
                end
            end

        // Hard decoding
        end else begin
            if(bit_outa[lut_bita] == 1'b1)
                out_bits[2:0] = 3'b111;
            else
                out_bits[2:0] = 3'b011;

            if(bit_outb[lut_bitb] == 1'b1)
                out_bits[5:3] = 3'b111;
            else
                out_bits[5:3] = 3'b011;
        end
    end else begin
        out_bits[2:0] = 0;
        out_bits[5:3] = 0;
    end
end

//assign out_bits[0] = lut_valid_delayed? bit_outa[lut_bita]: 0;
//assign out_bits[1] = lut_valid_delayed? bit_outb[lut_bitb]: 0;
assign output_strobe = enable & lut_valid_delayed & lut_out_delayed[1];

wire [5:0] lut_addra = lut_out[19:14];
wire [5:0] lut_addrb = lut_out[13:8];
wire lut_done = lut_out[0];

reg ram_delay;
reg ht_delayed;

ram_2port #(.DWIDTH(16), .AWIDTH(6)) ram_inst (
    .clka(clock),
    .ena(1),
    .wea(input_strobe),
    .addra(addra),
    .dia({in_bits, soft_in_bits, soft_in_bits_pos}),
    .doa({bit_outa,soft_bit_outa,soft_bit_outa_pos}),
    .clkb(clock),
    .enb(1),
    .web(0),
    .addrb(addrb),
    .dib(32'hFFFF),
    .dob({bit_outb,soft_bit_outb,soft_bit_outb_pos})
);

deinter_lut lut_inst (
    .clka(clock),
    .addra(lut_key),
    .douta(lut_out)
);

delayT #(.DATA_WIDTH(23), .DELAY(2)) delay_inst (
    .clock(clock),
    .reset(reset),

    .data_in({lut_valid, lut_out}),
    .data_out({lut_valid_delayed, lut_out_delayed})
);


localparam S_INPUT = 0;
localparam S_GET_BASE = 1;
localparam S_OUTPUT = 2;

reg [1:0] state;

always @(posedge clock) begin
    if (reset) begin
        addra <= num_data_carrier>>1;
        addrb <= 0;

        lut_key <= 0;
        lut_valid <= 0;
        ht_delayed <= 0;

        ram_delay <= 0;
        state <= S_INPUT;
    end else if (enable) begin
        ht_delayed <= ht;
        if (ht != ht_delayed) begin
            addra <= num_data_carrier>>1;
        end

        case(state)
            S_INPUT: begin
                if (input_strobe) begin
                    if (addra == half_data_carrier-1) begin
                        lut_key <= {7'b0, ht, rate[3:0]};
                        ram_delay <= 0;
                        lut_valid <= 0;
                        state <= S_GET_BASE;
                    end else begin
                        if (addra == num_data_carrier-1) begin
                            addra <= 0;
                        end else begin
                            addra <= addra + 1;
                        end
                    end
                end
            end

            S_GET_BASE: begin
                if (ram_delay) begin
                    lut_key <= lut_out;
                    ram_delay <= 0;
                    state <= S_OUTPUT;
                end else begin
                    ram_delay <= 1;
                end
            end

            S_OUTPUT: begin
                if (ram_delay) begin
                    addra <= lut_addra;
                    addrb <= lut_addrb;
                    if (lut_done) begin
                        lut_key <= 0;
                        lut_valid <= 0;
                        state <= S_INPUT;
                    end else begin
                        lut_valid <= 1;
                        lut_key <= lut_key + 1;
                    end
                end else begin
                    ram_delay <= 1;
                    lut_valid <= 1;
                    lut_key <= lut_key + 1;
                end
            end
            default: begin
            end
        endcase
    end
end

endmodule
