`timescale 1ns/1ps
module up2_mem (
	input				            clk,
	input				            nRst,

    // Mem side
    input                           i_read_ack,
    output                          o_read_req,

    input                           i_write_ack,
    output                          o_write_req,
    
    input   [DATA_WIDTH-1:0]        i_data,
    output  [ADDR_WIDTH-1:0]        o_addr,
    output  [DATA_WIDTH-1:0]        o_data,

    // Regs side
    input                           i_shift,
    input   [3:0]                   i_shift_data,
    output  [3:0]                   o_shift_data,
    input                           i_swap_req,
    output                          o_swap_ack
);

    parameter   ADDR_NIBBLES    = 1;
    parameter   DATA_NIBBLES    = 1;
    parameter   SHIFT_WIDTH     = 4 * (ADDR_NIBBLES+DATA_NIBBLES);
    parameter   ADDR_WIDTH      = 4 * ADDR_NIBBLES;
    parameter   DATA_WIDTH      = 4 * DATA_NIBBLES;
    parameter   IDLE            = 2'b00;
    parameter   READ_REQ_1      = 2'b01;
    parameter   WRITE_REQ       = 2'b10;
    parameter   READ_REQ_2      = 2'b11;

    reg     [SHIFT_WIDTH-1:0]   shift;
    reg     [1:0]               state;
    wire    [DATA_WIDTH-1:0]    shift_update;

    assign  o_data              =   shift[DATA_WIDTH-1:0] ^ i_data;
    assign  o_addr              =   shift[SHIFT_WIDTH-1:DATA_WIDTH];
    assign  o_shift_data        =   shift[3:0];
    assign  shift_update        =   o_data ^ i_data;
    assign  o_read_req          =   (   ((state == IDLE)        && (i_swap_req))    ||
                                        ((state == READ_REQ_1)  && (~i_read_ack))   ||
                                        ((state == WRITE_REQ)   && (i_write_ack))   ||
                                        ((state == READ_REQ_2)  && (~i_read_ack)));
    assign  o_write_req         =   (   ((state == READ_REQ_1)  && (i_read_ack))    ||
                                        ((state == WRITE_REQ)   && (~i_write_ack)));
    assign  o_swap_ack          =   (   ( state == READ_REQ_2)  && (i_read_ack));


	always@(posedge clk or negedge nRst) begin
		if(!nRst) begin
		    shift       <= 'd0;
            state       <= IDLE;
        end else begin
            if(i_shift) begin
                shift   <= {i_shift_data,shift[SHIFT_WIDTH-1:4]};
            end 
            case(state)
                IDLE:       if(i_swap_req) begin
                                state                   <= READ_REQ_1;
                            end
                READ_REQ_1: if(i_read_ack) begin
                                shift[DATA_WIDTH-1:0]   <= shift_update;
                                state                   <= WRITE_REQ;
                            end 
                WRITE_REQ:  if(i_write_ack) begin
                                state                   <= READ_REQ_2;
                            end 
                READ_REQ_2: if(i_read_ack) begin
                                shift[DATA_WIDTH-1:0]   <= shift_update; 
                                state                   <= IDLE;
                            end 
            endcase
		end
	end
endmodule