module hbmc_dru
(
    input   wire            clk,
    input   wire            arst,
    input   wire    [5:0]   rwds_oversampled,
    input   wire    [47:0]  data_oversampled,
    output  wire            recov_valid,
    output  wire    [15:0]  recov_data,
    output  reg             align_error,
    output  reg             rwds_error
);

/*----------------------------------------------------------------------------------------------------------------------------*/

    reg             prev_last_bit;
    reg     [5:0]   rwds_pair_xor;
    reg     [47:0]  data_pipeline;
    
    
    always @(posedge clk or posedge arst) begin
        if (arst) begin
            prev_last_bit <= 1'b0;
            rwds_pair_xor <=  {6{1'b0}};
            data_pipeline <= {48{1'b0}};
        end else begin
            /* Single bit pipeline delay for input data
             * to compensate edge detection stage delay */
            data_pipeline <= data_oversampled;
            
            /* Saving previous sample last bit to detect
             * an edge at the transition of two samples */
            prev_last_bit <= rwds_oversampled[5];
            
            /* RWDS edge detection vector */
            rwds_pair_xor <= {
                                 ^ rwds_oversampled[5:4],
                                 ^ rwds_oversampled[4:3],
                                 ^ rwds_oversampled[3:2],
                                 ^ rwds_oversampled[2:1],
                                 ^ rwds_oversampled[1:0],
                                   rwds_oversampled[0] ^ prev_last_bit
                             };
        end
    end
    
/*----------------------------------------------------------------------------------------------------------------------------*/
    
    genvar i;
    
    wire    [7:0]   data_mux_0;
    wire    [7:0]   data_mux_1;
    wire    [7:0]   data_mux_2;
    wire    [7:0]   data_mux_3;
    wire    [7:0]   data_mux_4;
    wire    [7:0]   data_mux_5;
    
    
    /* Data bus states at different sample positions */
    generate
        for (i = 0; i < 8; i = i + 1) begin : data_sample_mux
            assign data_mux_0[i] = data_pipeline[6 * i + 0];
            assign data_mux_1[i] = data_pipeline[6 * i + 1];
            assign data_mux_2[i] = data_pipeline[6 * i + 2];
            assign data_mux_3[i] = data_pipeline[6 * i + 3];
            assign data_mux_4[i] = data_pipeline[6 * i + 4];
            assign data_mux_5[i] = data_pipeline[6 * i + 5];
        end
    endgenerate
    
/*----------------------------------------------------------------------------------------------------------------------------*/
    
    reg             carry;
    reg     [7:0]   data_0;
    reg     [7:0]   data_1;
    reg     [7:0]   data_2;
    reg     [1:0]   data_cnt;
    
    
    /* Current process performs data bytes recovery,
     * based on RWDS strobe state. At every cycle
     * FSM can recover and output up to 3 bytes max */
    always @(posedge clk or posedge arst) begin
        if (arst) begin
            carry      <= 1'b0;
            data_0     <= {8{1'b0}};
            data_1     <= {8{1'b0}};
            data_2     <= {8{1'b0}};
            data_cnt   <= {2{1'b0}};
            rwds_error <= 1'b0;
        end else begin
            
            /* This carry flag is used to indicate that
             * RWDS edge was detected at the last bit of
             * the previous 6-bit sample and valid data
             * should be captured at the 0-bit position
             * of the current data sample */
            carry <= rwds_pair_xor[5];
            
            case (rwds_pair_xor[4:0])
                
                5'b00000: begin
                    data_0   <= (carry)? data_mux_0 : data_0;
                    data_1   <= data_1;
                    data_2   <= data_2;
                    data_cnt <= (carry)? 2'd1 : 2'd0;
                end
                
                5'b00010: begin
                    data_0   <= (carry)? data_mux_0 : data_mux_2;
                    data_1   <= (carry)? data_mux_2 : data_1;
                    data_2   <= data_2;
                    data_cnt <= (carry)? 2'd2 : 2'd1;
                end
                
                5'b00100: begin
                    data_0   <= (carry)? data_mux_0 : data_mux_3;
                    data_1   <= (carry)? data_mux_3 : data_1;
                    data_2   <= data_2;
                    data_cnt <= (carry)? 2'd2 : 2'd1;
                end
                
                5'b01000: begin
                    data_0   <= (carry)? data_mux_0 : data_mux_4;
                    data_1   <= (carry)? data_mux_4 : data_1;
                    data_2   <= data_2;
                    data_cnt <= (carry)? 2'd2 : 2'd1;
                end
                
                5'b01010: begin
                    data_0   <= (carry)? data_mux_0 : data_mux_2;
                    data_1   <= (carry)? data_mux_2 : data_mux_4;
                    data_2   <= (carry)? data_mux_4 : data_2;
                    data_cnt <= (carry)? 2'd3 : 2'd2;
                end
                
                5'b10010: begin
                    data_0   <= (carry)? data_mux_0 : data_mux_2;
                    data_1   <= (carry)? data_mux_2 : data_mux_5;
                    data_2   <= (carry)? data_mux_5 : data_2;
                    data_cnt <= (carry)? 2'd3 : 2'd2;
                end
                
                5'b10000: begin
                    data_0   <= (carry)? data_mux_0 : data_mux_5;
                    data_1   <= (carry)? data_mux_5 : data_1;
                    data_2   <= data_2;
                    data_cnt <= (carry)? 2'd2 : 2'd1;
                end
                
                5'b10100: begin
                    data_0   <= (carry)? data_mux_0 : data_mux_3;
                    data_1   <= (carry)? data_mux_3 : data_mux_5;
                    data_2   <= (carry)? data_mux_5 : data_2;
                    data_cnt <= (carry)? 2'd3 : 2'd2;
                end
                
                5'b00001: begin
                    data_0   <= data_mux_1;
                    data_1   <= data_1;
                    data_2   <= data_2;
                    data_cnt <= 2'd1;
                end
                
                5'b00101: begin
                    data_0   <= data_mux_1;
                    data_1   <= data_mux_3;
                    data_2   <= data_2;
                    data_cnt <= 2'd2;
                end
                
                5'b01001: begin
                    data_0   <= data_mux_1;
                    data_1   <= data_mux_4;
                    data_2   <= data_2;
                    data_cnt <= 2'd2;
                end
                
                5'b10001: begin
                    data_0   <= data_mux_1;
                    data_1   <= data_mux_5;
                    data_2   <= data_2;
                    data_cnt <= 2'd2;
                end
                
                default: begin
                    data_0     <= data_0;
                    data_1     <= data_1;
                    data_2     <= data_2;
                    data_cnt   <= 2'd0;
                    rwds_error <= 1'b1;
                end
                
            endcase
        end
    end
    
/*----------------------------------------------------------------------------------------------------------------------------*/
    
    reg     [15:0]  temp;
    reg     [15:0]  data;
    reg     [1:0]   temp_valid;
    reg             data_valid;
    
    
    /* As data bytes recovery FSM produces unaligned
     * data stream, this FSM performs data realignment
     * by repacking incoming 1-3 byte width stream into
     * fixed 16-bit stream with valid strobe */
    always @(posedge clk or posedge arst) begin
        if (arst) begin
            temp        <= {16{1'b0}};
            data        <= {16{1'b0}};
            temp_valid  <= 2'b00;
            data_valid  <= 1'b0;
            align_error <= 1'b0;
        end else begin
            case (data_cnt)
                2'd0: begin
                    if (temp_valid == 2'b11) begin
                        data       <= temp;
                        data_valid <= 1'b1;
                        temp_valid <= 2'b00;
                    end else begin
                        data_valid <= 1'b0;
                    end
                end
                
                2'd1: begin
                    case (temp_valid)
                        2'b00: begin
                            data_valid <= 1'b0;
                            temp[7:0]  <= data_0;
                            temp_valid <= 2'b01;
                        end
                        
                        2'b01: begin
                            data       <= {data_0, temp[7:0]};
                            data_valid <= 1'b1;
                            temp_valid <= 2'b00;
                        end
                        
                        2'b11: begin
                            data       <= temp;
                            data_valid <= 1'b1;
                            temp[7:0]  <= data_0;
                            temp_valid <= 2'b01;
                        end
                        
                        default: begin
                            align_error <= 1'b1;
                        end
                    endcase
                end
                
                2'd2: begin
                    case (temp_valid)
                        2'b00: begin
                            data       <= {data_1, data_0};
                            data_valid <= 1'b1;
                            temp_valid <= 2'b00;
                        end
                        
                        2'b01: begin
                            data       <= {data_0, temp[7:0]};
                            data_valid <= 1'b1;
                            temp[7:0]  <= data_1;
                            temp_valid <= 2'b01;
                        end
                        
                        2'b11: begin
                            data       <= temp;
                            data_valid <= 1'b1;
                            temp       <= {data_1, data_0};
                            temp_valid <= 2'b11;
                        end
                        
                        default: begin
                            align_error <= 1'b1;
                        end
                    endcase
                end
                
                2'd3: begin
                    case (temp_valid)
                        2'b00: begin
                            data       <= {data_1, data_0};
                            data_valid <= 1'b1;
                            temp[7:0]  <= data_2;
                            temp_valid <= 2'b01;
                        end
                        
                        2'b01: begin
                            data       <= {data_0, temp[7:0]};
                            data_valid <= 1'b1;
                            temp       <= {data_2, data_1};
                            temp_valid <= 2'b11;
                        end
                        
                        default: begin
                            align_error <= 1'b1;
                        end
                    endcase
                end
            endcase
        end
    end
    
    
    assign recov_valid = data_valid;
    assign recov_data  = {data[7:0], data[15:8]};


endmodule