-------------------------------------------------------------------------------
-- data_path.vhd - entity/architecture pair
-------------------------------------------------------------------------------
--
--  ***************************************************************************
--  **  Copyright(C) 2003 by Xilinx, Inc. All rights reserved.               **
--  **                                                                       **
--  **  This text contains proprietary, confidential                         **
--  **  information of Xilinx, Inc. , is distributed by                      **
--  **  under license from Xilinx, Inc., and may be used,                    **
--  **  copied and/or disclosed only pursuant to the terms                   **
--  **  of a valid license agreement with Xilinx, Inc.                       **
--  **                                                                       **
--  **  Unmodified source code is guaranteed to place and route,             **
--  **  function and run at speed according to the datasheet                 **
--  **  specification. Source code is provided "as-is", with no              **
--  **  obligation on the part of Xilinx to provide support.                 **
--  **                                                                       **
--  **  Xilinx Hotline support of source code IP shall only include          **
--  **  standard level Xilinx Hotline support, and will only address         **
--  **  issues and questions related to the standard released Netlist        **
--  **  version of the core (and thus indirectly, the original core source). **
--  **                                                                       **
--  **  The Xilinx Support Hotline does not have access to source            **
--  **  code and therefore cannot answer specific questions related          **
--  **  to source HDL. The Xilinx Support Hotline will only be able          **
--  **  to confirm the problem in the Netlist version of the core.           **
--  **                                                                       **
--  **  This copyright and support notice must be retained as part           **
--  **  of this text at all times.                                           **
--  ***************************************************************************
--
-------------------------------------------------------------------------------
-- Filename:        data_path.vhd
-- Version:         v1.10b
-- Description:     This file contains the read data path and write data path
--                  for data bus/device width matching.
--                  
-- VHDL-Standard:   VHDL'93
-------------------------------------------------------------------------------
-- Structure:   
--                  emc.vhd
--                      -- addr_counter_mux.vhd
--                      -- data_path.vhd
--                      -- io_registers.vhd
--                      -- mem_state_machine.vhd
--                      -- select_param.vhd
--                      -- wait_states.vhd
-------------------------------------------------------------------------------
-- Author:          ALS
-- History:
--   ALS           12/06/02    First Version
--   ALS           03/10/03    
-- ^^^^^^
--  Version 1.10.b created to include a generic to include/exclude negedge 
--  io registers
-- ~~~~~~
-------------------------------------------------------------------------------
-- Naming Conventions:
--      active low signals:                     "*_n"
--      clock signals:                          "clk", "clk_div#", "clk_#x" 
--      reset signals:                          "rst", "rst_n" 
--      generics:                               "C_*" 
--      user defined types:                     "*_TYPE" 
--      state machine next state:               "*_ns" 
--      state machine current state:            "*_cs" 
--      combinatorial signals:                  "*_com" 
--      pipelined or register delay signals:    "*_d#" 
--      counter signals:                        "*cnt*"
--      clock enable signals:                   "*_ce" 
--      internal version of output port         "*_i"
--      device pins:                            "*_pin" 
--      ports:                                  - Names begin with Uppercase 
--      processes:                              "*_PROCESS" 
--      component instantiations:               "<ENTITY_>I_<#|FUNC>
-------------------------------------------------------------------------------

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;

library proc_common_v1_00_b;
use proc_common_v1_00_b.proc_common_pkg.all;
use proc_common_v1_00_b.all;

library unisim;
use unisim.all;

-------------------------------------------------------------------------------
-- Definition of Generics:
--      C_MAX_MEM_WIDTH             -- maximum memory width of all memory banks   
--      C_MIN_MEM_WIDTH             -- minimum memory width (set to 8 bits)    
--      C_IPIF_DWIDTH               -- width of IPIF data bus           
--      C_ADDR_CNTR_WIDTH           -- width of address counter     
--      C_GLOBAL_DATAWIDTH_MATCH    -- indicates if datawidth matching is implemented
--                                  -- in any memory bank
--      C_GLOBAL_SYNC_MEM           -- indicates if any memory bank is synchronous      
--
-- Definition of Ports:
--  -- inputs
--     Valid_Memory_Access          -- current transaction is a memory access   
--     Mem_DQ_I                     -- data input from memory banks          
--     Bus2IP_Data                  -- data to be output to memory banks         
--     Bus2IP_BE                    -- byte enables to be output to memory banks                       
--     Addr_cnt                     -- address count (used in data-width matching)      
--     Mem_Wrcycle_Done             -- memory write cycle is complete (from state machine)  
--     Mem_Rdcycle_Done             -- memory read cycle is complete (from state machine)   
--     Mem_OEN                      -- output enable to memory banks
--     Mem_width_bytes              -- width of memory bank currently being accessed (in bytes)     
--     Datawidth_match              -- data-width matching for current memory bank
--     Synch_mem                    -- synchronous setting for current memory bank
--     Two_pipe_delay               -- pipe delay for current memory bank
--
--  -- outputs
--     Mem_RdAck                    -- memory read ack to IPIF
--     Mem_ReadData                 -- data read from memory
--     Mem_WriteData                -- data written to memory
--     Mem_BE                       -- byte enables to memory

--
-- --- Clocks and reset
--      Clk                 
--      Rst 
--      Input_datareg_rst           -- reset of data input register
---------------------------------------------------------------------------

-----------------------------------------------------------------------------
-- Entity section
-----------------------------------------------------------------------------

entity data_path is
  generic ( C_MAX_MEM_WIDTH             : integer;
            C_MIN_MEM_WIDTH             : integer;
            C_IPIF_DWIDTH               : integer;
            C_ADDR_CNTR_WIDTH           : integer range 1 to 5;
            C_GLOBAL_DATAWIDTH_MATCH    : integer range 0 to 1;
            C_GLOBAL_SYNC_MEM           : integer range 0 to 1);
  port (
    -- inputs
    Valid_Memory_Access : in  std_logic;
    Mem_DQ_I            : in  std_logic_vector(0 to C_MAX_MEM_WIDTH-1);
    Bus2IP_Data         : in  std_logic_vector(0 to C_IPIF_DWIDTH-1);
    Bus2IP_BE           : in  std_logic_vector(0 to C_IPIF_DWIDTH/8-1);
    
    Addr_cnt            : in  std_logic_vector(0 to C_ADDR_CNTR_WIDTH-1);
    
    Mem_Wrcycle_Done    : in  std_logic;
    Mem_Rdcycle_Done    : in  std_logic;
    Mem_OEN             : in  std_logic;
    
    Mem_width_bytes     : in  integer range 0 to 8;
    Datawidth_match     : in  std_logic;
       
    Synch_mem           : in  std_logic;
    Two_pipe_delay      : in  std_logic;
    
    -- outputs
    Mem_RdAck           : out std_logic;
    Mem_ReadData        : out std_logic_vector(0 to C_IPIF_DWIDTH-1);
    Mem_WriteData       : out std_logic_vector(0 to C_MAX_MEM_WIDTH-1);
    Mem_BE              : out std_logic_vector(0 to C_MAX_MEM_WIDTH/8-1);
    
    -- Clocks and reset
    Clk                 : in  std_logic;
    Rst                 : in  std_logic;
    Input_datareg_rst   : in  std_logic
    );
end entity data_path;


-----------------------------------------------------------------------------
-- Architecture section
-----------------------------------------------------------------------------

architecture imp of data_path is
-----------------------------------------------------------------------------
-- Constant declarations
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Signal declarations
-----------------------------------------------------------------------------
signal read_data_pipe       : std_logic_vector(0 to C_IPIF_DWIDTH-1);
signal mem_readdata_reg     : std_logic_vector(0 to C_IPIF_DWIDTH-1);
signal write_data           : std_logic_vector(0 to C_IPIF_DWIDTH-1);
signal bus2ip_data_d1       : std_logic_vector(0 to C_IPIF_DWIDTH-1);
signal bus2ip_data_d2       : std_logic_vector(0 to C_IPIF_DWIDTH-1);
signal read_ce              : std_logic_vector(0 to C_IPIF_DWIDTH/8-1);
signal read_ce0_d1          : std_logic;
signal read_ce0_re          : std_logic;
signal read_ce_cmb          : std_logic_vector(0 to C_IPIF_DWIDTH/8-1);
signal read_ce_reg1         : std_logic_vector(0 to C_IPIF_DWIDTH/8-1);
signal mem_data_in          : std_logic_vector(0 to C_IPIF_DWIDTH-1);
signal mem_oen_d1           : std_logic;
signal mem_oen_d2           : std_logic;
signal mem_rdcycle_done_d1  : std_logic;
signal mem_rdcycle_done_d2  : std_logic;
signal read_done            : std_logic;

signal final_read_ce        : std_logic;
signal final_read_ce_d1     : std_logic;
signal final_read_ce_re     : std_logic;
signal final_readce_rst     : std_logic;
signal rdack_din            : std_logic;
signal rdack_rst            : std_logic;

signal addr_cnt_d1          : std_logic_vector(0 to C_ADDR_CNTR_WIDTH-1);
signal addr_cnt_d2          : std_logic_vector(0 to C_ADDR_CNTR_WIDTH-1);
signal addr_cnt_sel         : std_logic_vector(0 to C_ADDR_CNTR_WIDTH-1);

-----------------------------------------------------------------------------
-- Component declarations
-----------------------------------------------------------------------------
component FDRE
  port (
    Q : out std_logic;
    C : in std_logic;
    CE : in std_logic;
    D : in std_logic;
    R : in std_logic
  );
end component;

component FDR
  port (
    Q : out std_logic;
    C : in std_logic;
    D : in std_logic;
    R : in std_logic
  );
end component;

-----------------------------------------------------------------------------
-- Begin architecture
-----------------------------------------------------------------------------

begin  -- architecture imp

---------------------------- Synchronous Memories-----------------------------
-- Pipelining of MEM_OEN and BUS2IP_DATA as well as the selection of 
-- whether to use MEM_OEN or MEM_RDCYCLE_DONE as the read_done signal is 
-- needed if any synchronous memories are in the design
------------------------------------------------------------------------------
SYNCMEM_GEN: if C_GLOBAL_SYNC_MEM = 1 generate
    -- if synchronous memory, have to delay before generating the enable
    -- pipe Mem_OEN by synchronous memory pipe-1 to generate read done
    -- if not data width matching, read_done is Mem_rdcycle_done.
    MEM_OEN_PIPE: process (Clk)
    begin 
        if Clk'event and Clk = '1' then
            if Rst = '1' or Valid_Memory_Access = '0' then
                mem_oen_d1 <= '1';
                mem_oen_d2 <= '1';
            else
                mem_oen_d1 <= Mem_OEN;
                mem_oen_d2 <= mem_oen_d1;
            end if;
        end if;
    end process MEM_OEN_PIPE;


    -- select the signal to use as the read_ce
    READ_DONE_SELECT: process(mem_oen_d1, mem_oen_d2, Mem_Rdcycle_Done, 
                                Two_pipe_delay, Synch_mem)
    begin
        if Synch_mem = '1' then
            if Two_pipe_delay = '1' then
                read_done <= not(mem_oen_d2);
            else
                read_done <= not(mem_oen_d1);
            end if;
        else
            read_done <= Mem_Rdcycle_done;
        end if;
    end process READ_DONE_SELECT;

    -- If the memory is synchronous, the BEs assert at the same time as for synchronous memories
    -- however, the write data goes out one or two clocks later (depending on Two_pipe_delay)
    WRITE_DATA_PIPE_PROCESS: process(Clk)
    begin
        if Clk'event and Clk = '1' then
            if Rst = '1' then
                bus2ip_data_d1 <= (others => '0');
                bus2ip_data_d2 <= (others => '0');
            else
                bus2ip_data_d1 <= Bus2IP_Data;
                bus2ip_data_d2 <= bus2ip_data_d1;
            end if;
        end if;
    end process WRITE_DATA_PIPE_PROCESS;
end generate SYNCMEM_GEN;

---------------------------- No Synchronous Memories---------------------------
-- Pipelining of MEM_OEN and Bus2IP_data is not necessary
-------------------------------------------------------------------------------
NOSYNC_MEM_GEN: if C_GLOBAL_SYNC_MEM = 0 generate
    read_done <= Mem_Rdcycle_done;
    bus2ip_data_d1 <= Bus2IP_Data;
    bus2ip_data_d2 <= Bus2IP_Data;
end generate NOSYNC_MEM_GEN;


---------------------------- NO DATAWIDTH MATCHING ----------------------------
-- if datawidth matching has not been turned on for any memory banks, simplify
-- the logic
--
NO_DATAWIDTH_MATCH_GEN: if C_GLOBAL_DATAWIDTH_MATCH = 0 generate
begin
    ------------------------- READ -------------------------
    -- Register READ_CE's to allow for output register on memory address & control
    -- signals
    READ_CE_REG_PROCESS: process(Clk)
    begin
        if Clk'event and Clk = '1' then
            if Rst = '1' or Valid_Memory_Access = '0' then
               -- read_ce <= (others => '0');
               read_ce(0) <= '0';
               read_ce0_d1 <= '0';
            else
                read_ce(0) <= read_done;
                read_ce0_d1 <= read_ce(0);
            end if;
        end if;
    end process;
        
    -- Generate input register structure
    -- This creates registers of MIN_MEM_WIDTH size to create a register of 
    -- MAX_MEM_WIDTH size. 
    READ_REGISTER_MEM_BANK_GEN: for j in 0 to C_MAX_MEM_WIDTH-1 generate
            RDDATA_BIT: FDRE
                port map (
                    Q   => read_data_pipe(j),
                    C   => Clk, 
                    CE  => read_ce(0), 
                    D   => Mem_DQ_I(j), 
                    R   => Input_datareg_rst 
                    );
    end generate READ_REGISTER_MEM_BANK_GEN;

    Mem_ReadData(0 to C_MAX_MEM_WIDTH-1) <= read_data_pipe(0 to C_MAX_MEM_WIDTH-1);
    
    MAXMEM_WIDTH_LT_IPIF_DWIDTH_GEN: if C_MAX_MEM_WIDTH < C_IPIF_DWIDTH generate    
        Mem_ReadData(C_MAX_MEM_WIDTH to C_IPIF_DWIDTH-1) <= (others => '0');
    end generate MAXMEM_WIDTH_LT_IPIF_DWIDTH_GEN;
    
    read_ce0_re <= read_ce(0) and not(read_ce0_d1);
    rdack_din <= read_ce0_re;
    rdack_rst <= not(Valid_Memory_Access);
    RDACK: FDR
             port map (
               Q => Mem_RdAck, 
               C => Clk, 
               D => rdack_din, 
               R => rdack_rst
             );

    -------------------------------------------------------------------------------
    -- Write Data Path
    -------------------------------------------------------------------------------
    WRITE_DATA_PROCESS: process(Bus2IP_Data, Synch_mem, 
                                    Two_pipe_delay, bus2ip_data_d1, bus2ip_data_d2)
    begin
        if Synch_mem = '1' then
            if Two_pipe_delay = '1' then
                write_data <= bus2ip_data_d2;
            else
                write_data <= bus2ip_data_d1;
            end if;
        else
            write_data <= Bus2IP_Data;
        end if;
    end process WRITE_DATA_PROCESS;



    Mem_WriteData <= write_data(0 to C_MAX_MEM_WIDTH-1);
    Mem_BE <= Bus2IP_BE(0 to C_MAX_MEM_WIDTH/8-1);
    
end generate NO_DATAWIDTH_MATCH_GEN;

---------------------------- DATAWIDTH MATCHING ----------------------------
-- if datawidth matching has been turned on at least 1 memory bank, implement
-- the data width matching logic
-- Note that an individual bank with datawidth matching turned off will still
-- use this logic
--
DATAWIDTH_MATCH_GEN: if C_GLOBAL_DATAWIDTH_MATCH = 1 generate

    -------------------------------------------------------------------------------
    -- Synchronous memories have a pipe delay of one or two clocks
    -- need to delay the address counter by the same amount to generate the proper
    -- CEs and mux selects
    -- If selected memory bank is not using datawidth matching, addr_cnt_sel=0
    -------------------------------------------------------------------------------
    ADDR_CNT_PIPE_PROCESS: process(Clk)
    begin
        if Clk'event and Clk = '1' then
            if Rst = '1' then
                addr_cnt_d1 <= (others => '0');
                addr_cnt_d2 <= (others => '0');
            else
                addr_cnt_d1 <= Addr_cnt;
                addr_cnt_d2 <= addr_cnt_d1;
            end if;
        end if;
    end process ADDR_CNT_PIPE_PROCESS;

    SYNC_ADDR_CNT_PROCESS: process(Datawidth_match, Addr_cnt, Synch_mem, 
                                    Two_pipe_delay, addr_cnt_d1, addr_cnt_d2)
    begin
        if Datawidth_match = '0' then
            addr_cnt_sel <= (others => '0');
        else
            if Synch_mem = '1' then
                if Two_pipe_delay = '1' then
                    addr_cnt_sel <= addr_cnt_d2;
                else
                    addr_cnt_sel <= addr_cnt_d1;
                end if;
            else
                addr_cnt_sel <= Addr_cnt;
            end if;
        end if;
    end process SYNC_ADDR_CNT_PROCESS;

    -------------------------------------------------------------------------------
    -- Read Data Path
    -------------------------------------------------------------------------------
    -- Generate input register structure
    -- This creates registers of MIN_MEM_WIDTH size to create a register of 
    -- MAX_MEM_WIDTH size. This register is then replicated as necessary to match the 
    -- IPIF data width. CEs for the input registers are based on MIN_MEM_WIDTH registers

    READ_REGISTER_DATAMATCH_GEN: for i in 0 to C_IPIF_DWIDTH/C_MAX_MEM_WIDTH - 1 generate
        READ_REGISTER_MEM_BANK_GEN: for j in 0 to C_MAX_MEM_WIDTH/C_MIN_MEM_WIDTH-1 generate
            READ_REGISTER_BIT_GEN: for k in 0 to C_MIN_MEM_WIDTH-1 generate
                RDDATA_BIT: FDRE
                    port map (
                        Q   => read_data_pipe(i*C_MAX_MEM_WIDTH+j*C_MIN_MEM_WIDTH+k),
                        C   => Clk, 
                        CE  => read_ce(i*C_MAX_MEM_WIDTH/C_MIN_MEM_WIDTH+j), 
                        D   => mem_data_in(j*C_MIN_MEM_WIDTH+k), 
                        R   => Input_datareg_rst 
                        );
            end generate READ_REGISTER_BIT_GEN;
        end generate READ_REGISTER_MEM_BANK_GEN;
    end generate READ_REGISTER_DATAMATCH_GEN;

    -- Final read register - this register holds the data pieces for presentation to the IPIF
    FINAL_READDATA_REG: process(Clk)
    begin
        if Clk'event and Clk = '1' then
            if Input_datareg_rst = '1' then
                mem_readdata_reg <= (others => '0');
            else
                if final_read_ce = '1' then
                    mem_readdata_reg <= read_data_pipe;
                end if;
            end if;
        end if;
    end process FINAL_READDATA_REG;

    -- If mem_width = IPIF_DWIDTH or data matching is turned off for this bank, the final read
    -- register is not necessary
    Mem_readdata <= read_data_pipe when 
                    ((Mem_width_bytes = C_IPIF_DWIDTH/8) or (Datawidth_match='0'))
                    else    mem_readdata_reg;

    -- Generate the READ_CEs and read data for the input register structure 
    -- MEM_DQ_I has to be routed to the right portion of the input register, create
    -- this muxing

    -- this process varies if the IPIF dwidth is 32 or 64, so generate separate 
    -- process based on this
    READ_DATAEN_64_GEN: if C_IPIF_DWIDTH=64 generate
        READ_DATAEN_PROCESS: process(Mem_width_bytes, read_done, addr_cnt_sel, Mem_DQ_I)
        begin
            read_ce_cmb     <= (others => '0');
            mem_data_in     <= (others => '0');
            case Mem_width_bytes is
                when 1  =>
                        -- create the read ces
                        for i in 0 to C_IPIF_DWIDTH/C_MIN_MEM_WIDTH -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                read_ce_cmb(i) <= read_done;
                            else
                                read_ce_cmb(i) <= '0';
                            end if;
                        end loop;
                        -- create the input data
                        for i in 0 to C_MAX_MEM_WIDTH/C_MIN_MEM_WIDTH-1 loop
                            mem_data_in(i*C_MIN_MEM_WIDTH to i*C_MIN_MEM_WIDTH+C_MIN_MEM_WIDTH-1)
                                <= Mem_DQ_I(0 to C_MIN_MEM_WIDTH-1);
                        end loop;
                when 2 =>
                        -- create the read ces
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*2) -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                read_ce_cmb(i*2)    <= read_done;
                                read_ce_cmb(i*2+1)  <= read_done;
                            end if;
                        end loop;
                        -- create the input data
                        for i in 0 to C_MAX_MEM_WIDTH/(C_MIN_MEM_WIDTH*2)-1 loop
                            mem_data_in(i*C_MIN_MEM_WIDTH*2 to i*C_MIN_MEM_WIDTH*2+C_MIN_MEM_WIDTH*2-1)
                                <= Mem_DQ_I(0 to C_MIN_MEM_WIDTH*2-1);
                        end loop;
                when 4 =>
                        -- create the read ces
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*4) -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                read_ce_cmb(i*4)        <= read_done;
                                read_ce_cmb(i*4+1)  <= read_done;
                                read_ce_cmb(i*4+2)  <= read_done;
                                read_ce_cmb(i*4+3)  <= read_done;
                            end if;
                        end loop;
                        -- create the input data
                        for i in 0 to C_MAX_MEM_WIDTH/(C_MIN_MEM_WIDTH*4)-1 loop
                            mem_data_in(i*C_MIN_MEM_WIDTH*4 to i*C_MIN_MEM_WIDTH*4+C_MIN_MEM_WIDTH*4-1)
                                <= Mem_DQ_I(0 to C_MIN_MEM_WIDTH*4-1);
                        end loop;
                when 8 =>
                        -- create the read ces
                        -- data width of selected memory bank is max memory width and matches IPIF Dwidth
                        -- no muxing is necessary,just make assignments
                        for i in 0 to C_MAX_MEM_WIDTH/8-1 loop
                            read_ce_cmb(i) <= read_done;
                        end loop;
                        mem_data_in <= Mem_DQ_I;
                        
                when others =>
                        read_ce_cmb <= (others => '0');
                        mem_data_in <= (others => '0');
            end case;
        end process READ_DATAEN_PROCESS;
    end generate READ_DATAEN_64_GEN;

    READ_DATAEN_32_GEN: if C_IPIF_DWIDTH=32 generate
        READ_DATAEN_PROCESS: process(Mem_width_bytes, read_done, addr_cnt_sel,Mem_DQ_I)
        begin
            read_ce_cmb <= (others => '0');
            mem_data_in <= (others => '0');
            case Mem_width_bytes is
                when 1  =>
                        -- create the read ces
                        for i in 0 to C_IPIF_DWIDTH/C_MIN_MEM_WIDTH -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                read_ce_cmb(i) <= read_done;
                            end if;
                        end loop;
                        -- create the input data
                        for i in 0 to C_MAX_MEM_WIDTH/C_MIN_MEM_WIDTH-1 loop
                            mem_data_in(i*C_MIN_MEM_WIDTH to i*C_MIN_MEM_WIDTH+C_MIN_MEM_WIDTH-1)
                                <= Mem_DQ_I(0 to C_MIN_MEM_WIDTH-1);
                        end loop;
                when 2 =>
                        -- create the read ces
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*2) -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                read_ce_cmb(i*2)        <= read_done;
                                read_ce_cmb(i*2+1)  <= read_done;
                            end if;
                        end loop;
                        -- create the input data
                        for i in 0 to C_MAX_MEM_WIDTH/(C_MIN_MEM_WIDTH*2)-1 loop
                            mem_data_in(i*C_MIN_MEM_WIDTH*2 to i*C_MIN_MEM_WIDTH*2+C_MIN_MEM_WIDTH*2-1)
                                <= Mem_DQ_I(0 to C_MIN_MEM_WIDTH*2-1);
                        end loop;
                when 4 =>
                        -- create the read ces
                        -- data width of selected memory bank is max memory width and matches IPIF Dwidth
                        -- no muxing is necessary,just make assignments
                        for i in 0 to C_MAX_MEM_WIDTH/8-1 loop
                            read_ce_cmb(i) <= read_done;
                        end loop;
                        mem_data_in <= Mem_DQ_I;                                                

                when others =>
                        read_ce_cmb <= (others => '0');
                        mem_data_in <= (others => '0');
            end case;
        end process READ_DATAEN_PROCESS;
    end generate READ_DATAEN_32_GEN;

    -- Register READ_CE's to allow for output register on memory address & control
    -- signals and another clock to allow for pipeline input registers
    READ_CE_REG_PROCESS: process(Clk)
    begin
        if Clk'event and Clk = '1' then
            if Valid_Memory_Access = '0' then
                read_ce_reg1 <= (others => '0');
                read_ce      <= (others => '0');
                read_ce0_d1 <= '0';
            else
                read_ce_reg1 <= read_ce_cmb;
                read_ce      <= read_ce_reg1;
                read_ce0_d1 <= read_ce(0);
            end if;
        end if;
    end process;
    
    final_readce_rst <= not(Valid_Memory_Access);
    -- final read ce is registered from last ce
    FINAL_READCE: FDR
             port map (
               Q => final_read_ce, 
               C => Clk, 
               D => read_ce(C_IPIF_DWIDTH/C_MIN_MEM_WIDTH-1), 
              -- R => Rst
               R => final_readce_rst
             );
    FINAL_READCE_D1: FDR
             port map (
               Q => final_read_ce_d1, 
               C => Clk, 
               D => final_read_ce, 
              -- R => Rst
               R => final_readce_rst
             );

    -- If mem_width = IPIF_DWIDTH or data matching is turned off for this bank, read ack is
    -- a single pulse one clock after read_ce(0) rising edge
    -- otherwise read ACK is registered from final ce rising edge
    read_ce0_re <= read_ce(0) and not(read_ce0_d1);
    final_read_ce_re <= final_read_ce and not(final_read_ce_d1);
    rdack_din <= read_ce0_re when
                    ((Mem_width_bytes = C_IPIF_DWIDTH/8) or (Datawidth_match='0'))
                    else final_read_ce_re;

    rdack_rst <= not(Valid_Memory_Access);

    RDACK: FDR
             port map (
               Q => Mem_RdAck, 
               C => Clk, 
               D => rdack_din, 
               R => rdack_rst
             );


    -------------------------------------------------------------------------------
    -- Write Data Path
    -------------------------------------------------------------------------------
    -- Create write data mux to mux Bus2IP_data out to memories
    -- this will vary on whether the max memory data width is 8, 16,32,or 64, so use separate
    -- generate statements
    -- If the memory is synchronous, the BEs assert at the same time as for synchronous memories
    -- however, the write data goes out one or two clocks later (depending on Two_pipe_delay)
    -- Therefore, separate processes are used for the write data and the byte enables. If the
    -- memory is synchronous, the address count is delayed by the pipe delay of the synchronous
    -- memory which will delay the changing of the Mem_writedata value. The Write data mux process
    -- therefore uses addr_cnt_sel instead of addr_cnt to denote the difference in it and addr cnt

    WRITE_DATA_PROCESS: process(Datawidth_match, Bus2IP_Data, Synch_mem, 
                                    Two_pipe_delay, bus2ip_data_d1, bus2ip_data_d2)
    begin
        if Datawidth_match = '0' then
            write_data <= Bus2IP_Data;
        else
            if Synch_mem = '1' then
                if Two_pipe_delay = '1' then
                    write_data <= bus2ip_data_d2;
                else
                    write_data <= bus2ip_data_d1;
                end if;
            else
                write_data <= Bus2IP_Data;
            end if;
        end if;
    end process WRITE_DATA_PROCESS;


    WRITE_DATABE_MUX_64_GEN: if C_MAX_MEM_WIDTH=64 generate
        WRITE_DATA_MUX_PROCESS: process(Mem_width_bytes, Mem_Wrcycle_Done, addr_cnt_sel, 
                                        write_data)
        begin
            Mem_WriteData   <= (others => '0');
            case Mem_width_bytes is
                when 1  =>
                        for i in 0 to C_IPIF_DWIDTH/C_MIN_MEM_WIDTH -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_WriteData(0 to C_MIN_MEM_WIDTH-1) <=
                                    write_data(i*C_MIN_MEM_WIDTH to i*C_MIN_MEM_WIDTH + C_MIN_MEM_WIDTH-1);
                            end if;
                        end loop;
                when 2 =>
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*2) -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_WriteData(0 to 2*C_MIN_MEM_WIDTH-1) <=
                                    write_data(i*2*C_MIN_MEM_WIDTH to i*2*C_MIN_MEM_WIDTH + 2*C_MIN_MEM_WIDTH-1);
                            end if;
                        end loop;
                when 4 =>
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*4) -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_WriteData(0 to 4*C_MIN_MEM_WIDTH-1) <=
                                    write_data(i*4*C_MIN_MEM_WIDTH to i*4*C_MIN_MEM_WIDTH + 4*C_MIN_MEM_WIDTH-1);
                            end if;
                        end loop;
                  when 8 =>
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*8) -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_WriteData(0 to 8*C_MIN_MEM_WIDTH-1) <=
                                    write_data(i*8*C_MIN_MEM_WIDTH to i*8*C_MIN_MEM_WIDTH + 8*C_MIN_MEM_WIDTH-1);
                            end if;
                        end loop;
                when others =>
                        Mem_WriteData <= (others => '0');
            end case;
        end process WRITE_DATA_MUX_PROCESS;
        WRITE_BE_MUX_PROCESS: process(Mem_width_bytes, Mem_Wrcycle_Done, Addr_cnt, 
                                        Bus2IP_BE)
        begin
            Mem_BE      <= (others => '0');
            case Mem_width_bytes is
                when 1  =>
                        for i in 0 to C_IPIF_DWIDTH/C_MIN_MEM_WIDTH -1 loop
                            if Addr_cnt = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_BE(0 to C_MIN_MEM_WIDTH/8-1) <=
                                    Bus2IP_BE(i*C_MIN_MEM_WIDTH/8 to i*C_MIN_MEM_WIDTH/8 + C_MIN_MEM_WIDTH/8-1);
                            end if;
                        end loop;
                when 2 =>
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*2) -1 loop
                            if Addr_cnt = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_BE(0 to 2*C_MIN_MEM_WIDTH/8-1) <=
                                    Bus2IP_BE(i*2*C_MIN_MEM_WIDTH/8 to i*2*C_MIN_MEM_WIDTH/8 + 2*C_MIN_MEM_WIDTH/8-1);
                            end if;
                        end loop;
                when 4 =>
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*4) -1 loop
                            if Addr_cnt = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_BE(0 to 4*C_MIN_MEM_WIDTH/8-1) <=
                                    Bus2IP_BE(i*4*C_MIN_MEM_WIDTH/8 to i*4*C_MIN_MEM_WIDTH/8 + 4*C_MIN_MEM_WIDTH/8-1);
                            end if;
                        end loop;
                  when 8 =>
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*8) -1 loop
                            if Addr_cnt = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_BE(0 to 8*C_MIN_MEM_WIDTH/8-1) <=
                                    Bus2IP_BE(i*8*C_MIN_MEM_WIDTH/8 to i*8*C_MIN_MEM_WIDTH/8 + 8*C_MIN_MEM_WIDTH/8-1);
                            end if;
                        end loop;
                when others =>
                        Mem_BE   <= (others => '0');
            end case;
        end process WRITE_BE_MUX_PROCESS;
    end generate WRITE_DATABE_MUX_64_GEN;

    WRITE_DATABE_MUX_32_GEN: if C_MAX_MEM_WIDTH=32 generate
        WRITE_DATA_MUX_PROCESS: process(Mem_width_bytes, Mem_Wrcycle_Done, addr_cnt_sel, 
                                        write_data)
        begin
            Mem_WriteData   <= (others => '0');
            case Mem_width_bytes is
                when 1  =>
                        for i in 0 to C_IPIF_DWIDTH/C_MIN_MEM_WIDTH -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_WriteData(0 to C_MIN_MEM_WIDTH-1) <=
                                    write_data(i*C_MIN_MEM_WIDTH to i*C_MIN_MEM_WIDTH + C_MIN_MEM_WIDTH-1);
                            end if;
                        end loop;
                when 2 =>
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*2) -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_WriteData(0 to 2*C_MIN_MEM_WIDTH-1) <=
                                    write_data(i*2*C_MIN_MEM_WIDTH to i*2*C_MIN_MEM_WIDTH + 2*C_MIN_MEM_WIDTH-1);
                            end if;
                        end loop;
                when 4 =>
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*4) -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_WriteData(0 to 4*C_MIN_MEM_WIDTH-1) <=
                                    write_data(i*4*C_MIN_MEM_WIDTH to i*4*C_MIN_MEM_WIDTH + 4*C_MIN_MEM_WIDTH-1);
                            end if;
                        end loop;
                when others =>
                        Mem_WriteData <= (others => '0');
            end case;
        end process WRITE_DATA_MUX_PROCESS;
        WRITE_BE_MUX_PROCESS: process(Mem_width_bytes, Mem_Wrcycle_Done, Addr_cnt, 
                                        Bus2IP_BE)
        begin
            Mem_BE      <= (others => '0');
            case Mem_width_bytes is
                when 1  =>
                        for i in 0 to C_IPIF_DWIDTH/C_MIN_MEM_WIDTH -1 loop
                            if Addr_cnt = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_BE(0 to C_MIN_MEM_WIDTH/8-1) <=
                                    Bus2IP_BE(i*C_MIN_MEM_WIDTH/8 to i*C_MIN_MEM_WIDTH/8 + C_MIN_MEM_WIDTH/8-1);
                            end if;
                        end loop;
                when 2 =>
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*2) -1 loop
                            if Addr_cnt = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_BE(0 to 2*C_MIN_MEM_WIDTH/8-1) <=
                                    Bus2IP_BE(i*2*C_MIN_MEM_WIDTH/8 to i*2*C_MIN_MEM_WIDTH/8 + 2*C_MIN_MEM_WIDTH/8-1);
                            end if;
                        end loop;
                when 4 =>
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*4) -1 loop
                            if Addr_cnt = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_BE(0 to 4*C_MIN_MEM_WIDTH/8-1) <=
                                    Bus2IP_BE(i*4*C_MIN_MEM_WIDTH/8 to i*4*C_MIN_MEM_WIDTH/8 + 4*C_MIN_MEM_WIDTH/8-1);
                            end if;
                        end loop;
                when others =>
                        Mem_BE   <= (others => '0');
            end case;
        end process WRITE_BE_MUX_PROCESS;
    end generate WRITE_DATABE_MUX_32_GEN;

    WRITE_DATABE_MUX_16_GEN: if C_MAX_MEM_WIDTH=16 generate
        WRITE_DATA_MUX_PROCESS: process(Mem_width_bytes, Mem_Wrcycle_Done, addr_cnt_sel, 
                                        write_data)
        begin
            Mem_WriteData   <= (others => '0');
            case Mem_width_bytes is
                when 1  =>
                        for i in 0 to C_IPIF_DWIDTH/C_MIN_MEM_WIDTH -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_WriteData(0 to C_MIN_MEM_WIDTH-1) <=
                                    write_data(i*C_MIN_MEM_WIDTH to i*C_MIN_MEM_WIDTH + C_MIN_MEM_WIDTH-1);
                            end if;
                        end loop;
                when 2 =>
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*2) -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_WriteData(0 to 2*C_MIN_MEM_WIDTH-1) <=
                                    write_data(i*2*C_MIN_MEM_WIDTH to i*2*C_MIN_MEM_WIDTH + 2*C_MIN_MEM_WIDTH-1);
                            end if;
                        end loop;
                when others =>
                        Mem_WriteData <= (others => '0');
            end case;
        end process WRITE_DATA_MUX_PROCESS;
        WRITE_BE_MUX_PROCESS: process(Mem_width_bytes, Mem_Wrcycle_Done, Addr_cnt, 
                                        Bus2IP_BE)
        begin
            Mem_BE      <= (others => '0');
            case Mem_width_bytes is
                when 1  =>
                        for i in 0 to C_IPIF_DWIDTH/C_MIN_MEM_WIDTH -1 loop
                            if Addr_cnt = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_BE(0 to C_MIN_MEM_WIDTH/8-1) <=
                                    Bus2IP_BE(i*C_MIN_MEM_WIDTH/8 to i*C_MIN_MEM_WIDTH/8 + C_MIN_MEM_WIDTH/8-1);
                            end if;
                        end loop;
                when 2 =>
                        for i in 0 to C_IPIF_DWIDTH/(C_MIN_MEM_WIDTH*2) -1 loop
                            if Addr_cnt = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_BE(0 to 2*C_MIN_MEM_WIDTH/8-1) <=
                                    Bus2IP_BE(i*2*C_MIN_MEM_WIDTH/8 to i*2*C_MIN_MEM_WIDTH/8 + 2*C_MIN_MEM_WIDTH/8-1);
                            end if;
                        end loop;
                when others =>
                        Mem_BE   <= (others => '0');
            end case;
        end process WRITE_BE_MUX_PROCESS;
    end generate WRITE_DATABE_MUX_16_GEN;

    WRITE_DATABE_MUX_8_GEN: if C_MAX_MEM_WIDTH=8 generate
        WRITE_DATA_MUX_PROCESS: process(Mem_width_bytes, Mem_Wrcycle_Done, addr_cnt_sel, 
                                        write_data)
        begin
            Mem_WriteData   <= (others => '0');
            case Mem_width_bytes is
                when 1  =>
                        for i in 0 to C_IPIF_DWIDTH/C_MIN_MEM_WIDTH -1 loop
                            if addr_cnt_sel = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_WriteData(0 to C_MIN_MEM_WIDTH-1) <=
                                    write_data(i*C_MIN_MEM_WIDTH to i*C_MIN_MEM_WIDTH + C_MIN_MEM_WIDTH-1);
                            end if;
                        end loop;
                when others =>
                        Mem_WriteData <= (others => '0');
            end case;
        end process WRITE_DATA_MUX_PROCESS;
        WRITE_BE_MUX_PROCESS: process(Mem_width_bytes, Mem_Wrcycle_Done, Addr_cnt, 
                                        Bus2IP_BE)
        begin
            Mem_BE      <= (others => '0');
            case Mem_width_bytes is
                when 1  =>
                        for i in 0 to C_IPIF_DWIDTH/C_MIN_MEM_WIDTH -1 loop
                            if Addr_cnt = conv_std_logic_vector(i, C_ADDR_CNTR_WIDTH) then
                                Mem_BE(0 to C_MIN_MEM_WIDTH/8-1) <=
                                    Bus2IP_BE(i*C_MIN_MEM_WIDTH/8 to i*C_MIN_MEM_WIDTH/8 + C_MIN_MEM_WIDTH/8-1);
                            end if;
                        end loop;
                when others =>
                        Mem_BE   <= (others => '0');
            end case;
        end process WRITE_BE_MUX_PROCESS;
    end generate WRITE_DATABE_MUX_8_GEN;
    
end generate DATAWIDTH_MATCH_GEN;
end imp;

