-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- Copyright 2025 VLSI Solution Oy
-- SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
--
-- Licensed under the Solderpad Hardware License v 2.1 (the "License");
-- you may not use this file except in compliance with the License, or,
-- at your option, the Apache License version 2.0.
-- You may obtain a copy of the License at
--
--    https://solderpad.org/licenses/SHL-2.1/
--
-- Unless required by applicable law or agreed to in writing, any work
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--                               RISCV core
--                                 V1.1
--
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

-- - - - - - - -
--  libraries
-- - - - - - - -
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.rv_pkg.all;


entity axi4_retime is
  generic ( AXIDATA_W  : natural range 32 to 64:= 32 );          -- select 32/64
  port ( clk_i              : in  std_logic;
         xrst_i             : in  std_logic;
         --
         inport_awvalid_i   : in  std_logic;
         inport_awaddr_i    : in  slv_31_00;
         inport_awid_i      : in  slv_03_00;
         inport_awlen_i     : in  slv_07_00;
         inport_awburst_i   : in  slv_01_00;
         inport_wvalid_i    : in  std_logic;
         inport_wdata_i     : in  slv_t(AXIDATA_W-1 downto 0);
         inport_wstrb_i     : in  slv_t(AXIDATA_W/8-1 downto 0); -- 64b / 32b
         inport_wlast_i     : in  std_logic;
         inport_bready_i    : in  std_logic;
         --
         inport_arvalid_i   : in  std_logic;
         inport_araddr_i    : in  slv_31_00;
         inport_arid_i      : in  slv_03_00;
         inport_arlen_i     : in  slv_07_00;
         inport_arburst_i   : in  slv_01_00;
         inport_rready_i    : in  std_logic;
         --
         inport_awready_o   : out std_logic;
         inport_wready_o    : out std_logic;
         inport_bvalid_o    : out std_logic;
         inport_bresp_o     : out slv_01_00;
         inport_bid_o       : out slv_03_00;
         --
         inport_arready_o   : out std_logic;
         inport_rvalid_o    : out std_logic;
         inport_rdata_o     : out slv_t(AXIDATA_W-1 downto 0);
         inport_rresp_o     : out slv_01_00;
         inport_rid_o       : out slv_03_00;
         inport_rlast_o     : out std_logic;
         --
         outport_awready_i  : in  std_logic;
         outport_wready_i   : in  std_logic;
         outport_bvalid_i   : in  std_logic;
         outport_bresp_i    : in  slv_01_00;
         outport_bid_i      : in  slv_03_00;
         --
         outport_arready_i  : in  std_logic;
         outport_rvalid_i   : in  std_logic;
         outport_rdata_i    : in  slv_t(AXIDATA_W-1 downto 0);
         outport_rresp_i    : in  slv_01_00;
         outport_rid_i      : in  slv_03_00;
         outport_rlast_i    : in  std_logic;
         --
         outport_awvalid_o  : out std_logic;
         outport_awaddr_o   : out slv_31_00;
         outport_awid_o     : out slv_03_00;
         outport_awlen_o    : out slv_07_00;
         outport_awburst_o  : out slv_01_00;
         outport_wvalid_o   : out std_logic;
         outport_wdata_o    : out slv_t(AXIDATA_W-1 downto 0);
         outport_wstrb_o    : out slv_t(AXIDATA_W/8-1 downto 0); -- 64b / 32b
         outport_wlast_o    : out std_logic;
         outport_bready_o   : out std_logic;
         --
         outport_arvalid_o  : out std_logic;
         outport_araddr_o   : out slv_31_00;
         outport_arid_o     : out slv_03_00;
         outport_arlen_o    : out slv_07_00;
         outport_arburst_o  : out slv_01_00;
         outport_rready_o   : out std_logic
         );
end entity axi4_retime;


architecture rtl of axi4_retime is

begin

  -- write cmd req
  b_buf44 : block
    type   buf_t is array (1 downto 0) of slv_t(43 downto 0);
    signal buf_q     : buf_t;
    signal rptr_q    : slv_00_00;
    signal wptr_q    : slv_00_00;
    signal count_q   : slv_01_00;
    signal accept_w  : std_logic;
    signal valid_w   : std_logic;
  begin
    p_fifo : process(clk_i)
    begin
      if (clk_i'event and clk_i = '1') then
        if (xrst_i = '0') then
          count_q   <= (others => '0');
          rptr_q    <= (others => '0');
          wptr_q    <= (others => '0');
          buf_q     <= (others  => (others => '0'));
        else
          --  push
          if (inport_awvalid_i = '1' and accept_w = '1') then
            buf_q(to_uint(wptr_q)) <= inport_awaddr_i &          -- 43:12
                                      inport_awid_i &            -- 11:8
                                      inport_awlen_i;            -- 7:0
            wptr_q <= inc1u(wptr_q);
          end if;
          --  pop
          if (outport_awready_i = '1' and valid_w = '1') then
            rptr_q  <= inc1u(rptr_q);
          end if;
          --  count up
          if ((inport_awvalid_i = '1' and  accept_w = '1') and
              (outport_awready_i = '0' or valid_w = '0')) then
            count_q <= inc1u(count_q);
          --  count down
          elsif ((inport_awvalid_i = '0' or  accept_w = '0') and
                 (outport_awready_i = '1' and valid_w = '1')) then
            count_q <= dec1u(count_q);
          end if;
        end if;
      end if;
    end process p_fifo;
    valid_w           <= not_zero(count_q);
    accept_w          <= not count_q(1);
    -- NC:  inport_awburst_i
    inport_awready_o  <= accept_w;                            -- accept
    outport_awvalid_o <= valid_w;                             -- valid
    outport_awaddr_o  <= buf_q(to_uint(rptr_q))(43 downto 12);
    outport_awid_o    <= buf_q(to_uint(rptr_q))(11 downto 8);
    outport_awlen_o   <= buf_q(to_uint(rptr_q))(7 downto 0);
    outport_awburst_o <= "01";                                -- INCR type only
  end block b_buf44;



  -- write data req
  b_buf33 : block
    type   buf_t is array (1 downto 0) of slv_t(AXIDATA_W-1+1 downto 0);
    signal buf_q     : buf_t;                                 -- 33 / 65
    signal rptr_q    : slv_00_00;
    signal wptr_q    : slv_00_00;
    signal count_q   : slv_01_00;
    signal accept_w  : std_logic;
    signal valid_w   : std_logic;
    signal wstrb_q   : slv_t(AXIDATA_W/8-1 downto 0);         -- 64b / 32b
  begin
    p_fifo : process(clk_i)
    begin
      if (clk_i'event and clk_i = '1') then
        if (xrst_i = '0') then
          count_q   <= (others => '0');
          rptr_q    <= (others => '0');
          wptr_q    <= (others => '0');
          buf_q     <= (others  => (others => '0'));
          wstrb_q   <= (others => '0');
        else
          --  push
          if (inport_wvalid_i = '1' and accept_w = '1') then
            wstrb_q                <= inport_wstrb_i;
            buf_q(to_uint(wptr_q)) <= inport_wlast_i &        -- 32
                                      inport_wdata_i;         -- 31:0 / 63:0
            wptr_q                 <= inc1u(wptr_q);
          end if;
          --  pop
          if (outport_wready_i = '1' and valid_w = '1') then
            rptr_q                 <= inc1u(rptr_q);
          end if;
          --  count up
          if ((inport_wvalid_i = '1' and  accept_w = '1') and
              (outport_wready_i = '0' or valid_w = '0')) then
            count_q                <= inc1u(count_q);
          --  count down
          elsif ((inport_wvalid_i = '0' or  accept_w = '0') and
                 (outport_wready_i = '1' and valid_w = '1')) then
            count_q                <= dec1u(count_q);
          end if;
        end if;
      end if;
    end process p_fifo;
    -- NC inport_wstrb_i
    valid_w           <= not_zero(count_q);
    accept_w          <= not count_q(1);
    outport_wvalid_o  <= valid_w;                             -- accept
    inport_wready_o   <= accept_w;                            -- valid
--  outport_wstrb_o   <= X"F";                                -- ! 4b always
    outport_wstrb_o   <= wstrb_q;                             -- ! 4b / 8b
    outport_wlast_o   <= buf_q(to_uint(rptr_q))(AXIDATA_W);   -- 32b / 64b
    outport_wdata_o   <= buf_q(to_uint(rptr_q))(AXIDATA_W-1 downto 0);
  end block b_buf33;



  -- write resp
  b_buf6 : block
    type   buf_t is array (1 downto 0) of slv_t(5 downto 0);
    signal buf_q     : buf_t;
    signal rptr_q    : slv_00_00;
    signal wptr_q    : slv_00_00;
    signal count_q   : slv_01_00;
    signal accept_w  : std_logic;
    signal valid_w   : std_logic;
  begin
    p_fifo : process(clk_i)
    begin
      if (clk_i'event and clk_i = '1') then
        if (xrst_i = '0') then
          count_q   <= (others => '0');
          rptr_q    <= (others => '0');
          wptr_q    <= (others => '0');
          buf_q     <= (others  => (others => '0'));
        else
          --  push
          if (outport_bvalid_i = '1' and accept_w = '1') then
            buf_q(to_uint(wptr_q)) <= outport_bresp_i &       -- 5:4
                                      outport_bid_i;          -- 3:0
            wptr_q  <= inc1u(wptr_q);
          end if;
          --  pop
          if (inport_bready_i = '1' and valid_w = '1') then
            rptr_q  <= inc1u(rptr_q);
          end if;
          --  count up
          if ((outport_bvalid_i = '1' and  accept_w = '1') and
              (inport_bready_i = '0' or valid_w = '0')) then
            count_q <= inc1u(count_q);
          --  count down
          elsif ((outport_bvalid_i = '0' or  accept_w = '0') and
                 (inport_bready_i = '1' and valid_w = '1')) then
            count_q <= dec1u(count_q);
          end if;
        end if;
      end if;
    end process p_fifo;
    --
    valid_w           <= not_zero(count_q);
    accept_w          <= not count_q(1);
    inport_bvalid_o   <= valid_w;                             -- valid
    outport_bready_o  <= accept_w;                            -- accept
    inport_bresp_o    <= buf_q(to_uint(rptr_q))(5 downto 4);
    inport_bid_o      <= buf_q(to_uint(rptr_q))(3 downto 0);
  end block b_buf6;



  --  read request
  b_buf46 : block
    type   buf_t is array (1 downto 0) of slv_t(45 downto 0);
    signal buf_q     : buf_t;
    signal rptr_q    : slv_00_00;
    signal wptr_q    : slv_00_00;
    signal count_q   : slv_01_00;
    signal accept_w  : std_logic;
    signal valid_w   : std_logic;
  begin
    p_fifo : process(clk_i)
    begin
      if (clk_i'event and clk_i = '1') then
        if (xrst_i = '0') then
          count_q   <= (others => '0');
          rptr_q    <= (others => '0');
          wptr_q    <= (others => '0');
          buf_q     <= (others  => (others => '0'));
        else
          --  push
          if (inport_arvalid_i = '1' and accept_w = '1') then
            buf_q(to_uint(wptr_q)) <= inport_araddr_i &       -- 45:14
                                      inport_arid_i   &       -- 13:10
                                      inport_arlen_i  &       -- 9:2
                                      inport_arburst_i;       -- 1:0
            wptr_q  <= inc1u(wptr_q);
          end if;
          --  pop
          if (outport_arready_i = '1' and valid_w = '1') then
            rptr_q  <= inc1u(rptr_q);
          end if;
          --  count up
          if ((inport_arvalid_i = '1' and  accept_w = '1') and
              (outport_arready_i = '0' or valid_w = '0')) then
            count_q <= inc1u(count_q);
          --  count down
          elsif ((inport_arvalid_i = '0' or  accept_w = '0') and
                 (outport_arready_i = '1' and valid_w = '1')) then
            count_q <= dec1u(count_q);
          end if;
        end if;
      end if;
    end process p_fifo;
    valid_w            <= not_zero(count_q);
    accept_w           <= not count_q(1);                     -- "00" or "01"
    outport_arvalid_o  <= valid_w;                            -- valid
    inport_arready_o   <= accept_w;                           -- accept
    outport_araddr_o   <= buf_q(to_uint(rptr_q))(45 downto 14);
    outport_arid_o     <= buf_q(to_uint(rptr_q))(13 downto 10);
    outport_arlen_o    <= buf_q(to_uint(rptr_q))(9 downto 2);
    outport_arburst_o  <= buf_q(to_uint(rptr_q))(1 downto 0);
  end block b_buf46;


  --  read response
  b_buf39 : block
    type   buf_t is array (1 downto 0) of slv_t(AXIDATA_W-1+7 downto 0);
    signal buf_q     : buf_t;                                 -- 38:0 / 70:0
    signal rptr_q    : slv_00_00;
    signal wptr_q    : slv_00_00;
    signal count_q   : slv_01_00;
    signal accept_w  : std_logic;
    signal valid_w   : std_logic;
  begin
    p_fifo : process(clk_i)
    begin
      if (clk_i'event and clk_i = '1') then
        if (xrst_i = '0') then
          count_q   <= (others => '0');
          rptr_q    <= (others => '0');
          wptr_q    <= (others => '0');
          buf_q     <= (others  => (others => '0'));
        else
          --  push
          if (outport_rvalid_i = '1' and accept_w = '1') then
            buf_q(to_uint(wptr_q)) <= outport_rdata_i &       -- 38:7
                                      outport_rresp_i &       -- 6:5
                                      outport_rid_i   &       -- 4:1
                                      outport_rlast_i;        -- 0
            wptr_q  <= inc1u(wptr_q);
          end if;
          --  pop
          if (inport_rready_i = '1' and valid_w = '1') then
            rptr_q  <= inc1u(rptr_q);
          end if;
          --  count up
          if ((outport_rvalid_i = '1' and  accept_w = '1') and
              (inport_rready_i = '0' or valid_w = '0')) then
            count_q <= inc1u(count_q);
          --  count down
          elsif ((outport_rvalid_i = '0' or  accept_w = '0') and
                 (inport_rready_i= '1' and valid_w = '1')) then
            count_q <= dec1u(count_q);
          end if;
        end if;
      end if;
    end process p_fifo;
    --
    valid_w           <= not_zero(count_q);
    accept_w          <= not count_q(1);                      -- "00" or "01"
    inport_rvalid_o   <= valid_w;                             -- valid
    outport_rready_o  <= accept_w;                            -- accept
    inport_rdata_o    <= buf_q(to_uint(rptr_q))(AXIDATA_W-1+7 downto 7);
    inport_rresp_o    <= buf_q(to_uint(rptr_q))(6 downto 5);
    inport_rid_o      <= buf_q(to_uint(rptr_q))(4 downto 1);
    inport_rlast_o    <= buf_q(to_uint(rptr_q))(0);
  end block b_buf39;

end rtl;
