-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- 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 tcm_mem_pmem_fifo2 is
  generic ( WIDTH  : natural := 8;
            DEPTH  : natural := 4;
            ADDR_W : natural := 2 );
  port ( clk_i       : in     std_logic;
         xrst_i      : in     std_logic;                  -- syncr reset
         data_in_i   : in     slv_t(WIDTH-1 downto 0);
         push_i      : in     std_logic;  
         pop_i       : in     std_logic;   
         --
         data_out_o  : out    slv_t(WIDTH-1 downto 0);
         accept_o    : buffer std_logic; 
         valid_o     : buffer std_logic
         );
end entity tcm_mem_pmem_fifo2;


architecture rtl of tcm_mem_pmem_fifo2 is

  constant COUNT_W : natural := ADDR_W + 1;

  -- - - - - - -
  --  registers
  -- - - - - - -
  type rfile_t is array (DEPTH-1 downto 0) of slv_t(WIDTH-1 downto 0);  
  signal rfile_q   : rfile_t;
  signal rd_ptr_q  : slv_t(ADDR_W-1  downto 0);
  signal wr_ptr_q  : slv_t(ADDR_W-1  downto 0);
  signal count_q   : slv_t(COUNT_W-1 downto 0);

begin
 
  process(clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        count_q   <= (others => '0');
        rd_ptr_q  <= (others => '0');
        wr_ptr_q  <= (others => '0');
        rfile_q   <= (others => (others => '0'));
      else
        --  push
        if (push_i = '1' and accept_o = '1') then
          rfile_q(to_uint(wr_ptr_q))  <= data_in_i;
          wr_ptr_q      <= inc1u(wr_ptr_q);
        end if;
        --  pop
        if (pop_i  = '1' and valid_o = '1') then
          rd_ptr_q      <= inc1u(rd_ptr_q);
        end if;
        --  count up
        if ( (push_i = '1' and accept_o = '1') and
             (pop_i = '1' or valid_o = '0') ) then
          count_q <= inc1u(count_q);
        --  count down
        elsif ( (push_i = '0' or accept_o = '0') and
                (pop_i = '1' and valid_o = '1') ) then
          count_q <= dec1u(count_q);
        end if;
      end if;
    end if;
  end process;


  accept_o   <= ne(count_q, DEPTH);
  valid_o    <= not_zero(count_q);

  data_out_o <= rfile_q(to_uint(rd_ptr_q));

end rtl;



-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--                               RISCV core
--                                 V1.1
--
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

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

entity tcm_axi is
  port ( clk_i          : in     std_logic;
         xrst_i         : in     std_logic;              -- syncr reset
         --
         ram_accept_i   : in     std_logic;
         ram_ack_i      : in     std_logic;
         ram_error_i    : in     std_logic;
         ram_rdata_i    : in     slv_31_00;
         ram_wr_o       : buffer slv_03_00;
         ram_rd_o       : buffer std_logic;
         ram_len_o      : out    slv_07_00;
         ram_addr_o     : out    slv_31_00;
         ram_wdata_o    : out    slv_31_00;
         --
         axi_arready_o  : buffer std_logic;
         axi_rvalid_o   : buffer std_logic;
         axi_rdata_o    : out    slv_31_00;
         axi_rresp_o    : out    slv_01_00;
         axi_rid_o      : out    slv_03_00;
         axi_rlast_o    : out    std_logic;
         axi_arvalid_i  : in     std_logic;
         axi_araddr_i   : in     slv_31_00;
         axi_arid_i     : in     slv_03_00;
         axi_arlen_i    : in     slv_07_00;
         axi_arburst_i  : in     slv_01_00;
         axi_rready_i   : in     std_logic;
         --
         axi_awready_o  : buffer std_logic;
         axi_wready_o   : buffer std_logic;
         axi_bvalid_o   : buffer std_logic;
         axi_bresp_o    : out    slv_01_00;
         axi_bid_o      : out    slv_03_00;
         axi_awvalid_i  : in     std_logic;
         axi_awaddr_i   : in     slv_31_00;
         axi_awid_i     : in     slv_03_00;
         axi_awlen_i    : in     slv_07_00;
         axi_awburst_i  : in     slv_01_00;
         axi_wvalid_i   : in     std_logic;
         axi_wdata_i    : in     slv_31_00;
         axi_wstrb_i    : in     slv_03_00;
         axi_wlast_i    : in     std_logic;
         axi_bready_i   : in     std_logic
         );
end entity tcm_axi;


architecture rtl of tcm_axi is

  component tcm_mem_pmem_fifo2
    generic ( WIDTH  : natural := 8;
              DEPTH  : natural := 4;
              ADDR_W : natural := 2 );
    port ( clk_i       : in     std_logic;
           xrst_i      : in     std_logic;                  -- syncr reset
           data_in_i   : in     slv_t(WIDTH-1 downto 0);
           push_i      : in     std_logic;  
           pop_i       : in     std_logic;   
           --
           data_out_o  : out    slv_t(WIDTH-1 downto 0);
           accept_o    : buffer std_logic; 
           valid_o     : buffer std_logic );
  end component;

  constant AXI4_FIXED_BURST    : slv_01_00 := "00";
  constant AXI4_INCR_BURST     : slv_01_00 := "01";
  constant AXI4_WRAP_BURST     : slv_01_00 := "10";         -- 2,4,8 or 16 

  constant SUPPORT_FIXED_BURST : boolean := false;
  constant SUPPORT_INCR_BURST  : boolean := true;           -- default
  constant SUPPORT_WRAP_BURST  : boolean := false;


  -- - - - - - - - - - - -
  --  calculate next addr
  -- - - - - - - - - - - -
  function nxt_addr ( addr   : slv_31_00;
                      axtype : slv_01_00;
                      axlen  : slv_07_00 ) return slv_31_00 is
    variable result : slv_31_00;
    variable mask   : slv_31_00;
  begin
    mask   := (others => '0');
    --
    result := addu(addr, 4);                        -- default AXI4_BURST_INCR
    --
    case axtype is
      when "00" =>                                  -- AXI4_BURST_FIXED
        if (SUPPORT_FIXED_BURST) then
          result := addr;
        end if;
      when "10" =>                                  -- AXI4_BURST_WRAP
        if (SUPPORT_WRAP_BURST) then
          case axlen is
            when  X"00"   => mask := X"00000003";
            when  X"01"   => mask := X"00000007";
            when  X"03"   => mask := X"0000000F";
            when  X"07"   => mask := X"0000001F";
            when  X"0F"   => mask := X"0000003F";
            when  others  => mask := X"0000003F";
          end case;
          result := (addr and not mask) or (addu(addr, 4) and mask);
        end if;
      when others =>                                -- AXI4_BURST_INCR
        result := addu(addr, 4);
    end case;
    return result;
  end function nxt_addr;

  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  --  registers / wires
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  signal req_len_q          : slv_07_00;
  signal req_addr_q         : slv_31_00;
  signal req_rd_q           : std_logic;
  signal req_wr_q           : std_logic;
  signal req_id_q           : slv_03_00;
  signal req_axburst_q      : slv_01_00;
  signal req_axlen_q        : slv_07_00;
  signal req_prio_q         : std_logic;
  signal req_hold_rd_q      : std_logic;
  signal req_hold_wr_q      : std_logic;

  signal req_fifo_accept_w  : std_logic;

  signal req_push_w         : std_logic;
  signal req_in_w           : slv_05_00;

  signal req_out_valid_w    : std_logic;
  signal req_out_w          : slv_05_00;
  signal resp_accept_w      : std_logic;

  signal resp_is_write_w    : std_logic;
  signal resp_is_read_w     : std_logic;
  signal resp_is_last_w     : std_logic;
  signal resp_id_w          : slv_03_00;

  signal resp_valid_w       : std_logic;

  signal write_prio_w       : std_logic;
  signal read_prio_w        : std_logic;

  signal write_active_w     : std_logic;
  signal read_active_w      : std_logic;
  signal addr_w             : slv_31_00;
  signal wr_w               : std_logic;
  signal rd_w               : std_logic;
  
begin

  -- - - - - - - -
  --  sequential
  -- - - - - - - -
  process(clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        req_len_q     <= X"00";
        req_addr_q    <= X"00000000";
        req_wr_q      <= '0';
        req_rd_q      <= '0';
        req_id_q      <= X"0";
        req_axburst_q <= "00";
        req_axlen_q   <= X"00";
        req_prio_q    <= '0';
      else
        --  burst continuation
        if ((ram_wr_o /= X"0" or ram_rd_o = '1') and ram_accept_i = '1') then
          if (req_len_q = X"00") then
            req_rd_q   <= '0';
            req_wr_q   <= '0';
          else
            req_addr_q <= nxt_addr(req_addr_q, req_axburst_q, req_axlen_q);
            req_len_q  <= dec1u(req_len_q);
          end if;
        end if;
        --
        -- Write command accepted
        if (axi_awvalid_i = '1' and axi_awready_o = '1') then
          -- Data ready?
          if (axi_wvalid_i = '1' and axi_wready_o = '1') then
            req_wr_q      <= not axi_wlast_i;
            req_len_q     <= dec1u(axi_awlen_i);
            req_id_q      <= axi_awid_i;
            req_axburst_q <= axi_awburst_i;
            req_axlen_q   <= axi_awlen_i;
            req_addr_q    <= nxt_addr(axi_awaddr_i,axi_awburst_i,axi_awlen_i);
            -- Data not ready
          else
            req_wr_q      <= '1';
            req_len_q     <= axi_awlen_i;
            req_id_q      <= axi_awid_i;
            req_axburst_q <= axi_awburst_i;
            req_axlen_q   <= axi_awlen_i;
            req_addr_q    <= axi_awaddr_i;
          end if;
          req_prio_q    <= not req_prio_q;
          -- Read command accepted
        elsif (axi_arvalid_i = '1' and axi_arready_o = '1') then
          req_rd_q      <= not_zero(axi_arlen_i);
          req_len_q     <= dec1u(axi_arlen_i);
          req_addr_q    <= nxt_addr(axi_araddr_i, axi_arburst_i, axi_arlen_i);
          req_id_q      <= axi_arid_i;
          req_axburst_q <= axi_arburst_i;
          req_axlen_q   <= axi_arlen_i;
          req_prio_q    <= not req_prio_q;
        end if;
      end if;
    end if;
  end process;


      
  process(clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        req_hold_rd_q   <= '0';
        req_hold_wr_q   <= '0';
      else
        if (ram_rd_o = '1' and ram_accept_i = '0') then
          req_hold_rd_q   <= '1';
        elsif (ram_accept_i = '1') then
          req_hold_rd_q   <= '0';
        end if;
        --
        if (not_zero(ram_wr_o) and ram_accept_i = '0') then
          req_hold_wr_q   <= '1';
        elsif (ram_accept_i = '1') then
          req_hold_wr_q   <= '0';
        end if;
      end if;
    end if;
  end process;


  -- - - - - - - - - -
  --  request tracking
  -- - - - - - - - - -
  req_push_w <= (ram_rd_o or not_zero(ram_wr_o)) and ram_accept_i;



  process (axi_arvalid_i,axi_arready_o, axi_arlen_i, axi_arid_i,
           axi_awvalid_i, axi_awready_o, axi_awlen_i, axi_awid_i,
           ram_rd_o, req_len_q, req_id_q)
  begin
    req_in_w <= "000000";
    --  first cycle of read burst
    if (axi_arvalid_i = '1' and axi_arready_o = '1') then
      req_in_w <= ('1' & is_zero(axi_arlen_i) & axi_arid_i);
      --  first cycle of write burst
    elsif (axi_awvalid_i = '1' and axi_awready_o = '1') then
      req_in_w <= ('0' & is_zero(axi_awlen_i) &  axi_awid_i);
      --  in burst
    else
      req_in_w <= (ram_rd_o & is_zero(req_len_q) & req_id_q);
    end if;
  end process;



  u_requests : tcm_mem_pmem_fifo2
    generic map ( WIDTH  => (1 + 1 + 4),
                  DEPTH  => 4,
                  ADDR_W => 2 )
    port map ( clk_i       => clk_i,
               xrst_i      => xrst_i,                  -- syncr reset
               data_in_i   => req_in_w,                -- WIDTH-1:0
               push_i      => req_push_w,  
               accept_o    => req_fifo_accept_w, 
               --
               pop_i       => resp_accept_w,   
               data_out_o  => req_out_w,               -- WIDTH-1:0
               valid_o     => req_out_valid_w );




  resp_is_write_w <= not req_out_w(5) when (req_out_valid_w = '1') else '0';
  resp_is_read_w  <= req_out_w(5)     when (req_out_valid_w = '1') else '0';
  resp_is_last_w  <= req_out_w(4);
  resp_id_w       <= req_out_w(3 downto 0);

  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  --  response buffering
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  u_response : tcm_mem_pmem_fifo2
    generic map ( WIDTH  => 32,
                  DEPTH  => 4,
                  ADDR_W => 2 )
    port map ( clk_i       => clk_i,
               xrst_i      => xrst_i,                  -- syncr reset
               data_in_i   => ram_rdata_i,         -- WIDTH-1:0
               push_i      => ram_ack_i ,  
               accept_o    => OPEN, 
               --
               pop_i       => resp_accept_w,   
               data_out_o  => axi_rdata_o,             -- WIDTH-1:0
               valid_o     => resp_valid_w );



  -- - - - - - - -
  --  RAM request
  -- - - - - - - -
  --  round robin priority between read and write
  write_prio_w    <= ((req_prio_q and (not req_hold_rd_q)) or req_hold_wr_q);
  read_prio_w     <= (((not req_prio_q) and (not req_hold_wr_q)) or
                      req_hold_rd_q);

  write_active_w  <= (axi_awvalid_i or req_wr_q) and (not req_rd_q) and
                     req_fifo_accept_w and
                     (write_prio_w or req_wr_q or (not axi_arvalid_i));
  read_active_w   <= (axi_arvalid_i or req_rd_q) and (not req_wr_q) and
                     req_fifo_accept_w and
                     (read_prio_w or req_rd_q or (not axi_awvalid_i));


  
  axi_awready_o   <= write_active_w and (not req_wr_q) and ram_accept_i and
                     req_fifo_accept_w;
  axi_wready_o    <= write_active_w and ram_accept_i and req_fifo_accept_w;
  axi_arready_o   <= read_active_w and (not req_rd_q) and ram_accept_i and
                     req_fifo_accept_w;

  addr_w          <= req_addr_q when (req_wr_q = '1' or req_rd_q = '1') else
                     axi_awaddr_i when (write_active_w = '1') else
                     axi_araddr_i;

  wr_w            <= write_active_w and axi_wvalid_i;
  rd_w            <= read_active_w;

  --  RAM if
  ram_addr_o      <= addr_w;
  ram_wdata_o     <= axi_wdata_i;
  ram_rd_o        <= rd_w;
  ram_wr_o        <= axi_wstrb_i when (wr_w = '1') else "0000";
  ram_len_o       <= axi_awlen_i when (axi_awvalid_i = '1') else
                     axi_arlen_i when (axi_arvalid_i = '1') else
                     X"00";

  -- - - - - - -
  --  response
  -- - - - - - -
  axi_bvalid_o    <= resp_valid_w and resp_is_write_w and resp_is_last_w;
  axi_bresp_o     <= "00";
  axi_bid_o       <= resp_id_w;

  axi_rvalid_o    <= resp_valid_w and resp_is_read_w;
  axi_rresp_o     <= "00";
  axi_rid_o       <= resp_id_w;
  axi_rlast_o     <= resp_is_last_w;

  resp_accept_w   <= (axi_rvalid_o and axi_rready_i) or 
                     (axi_bvalid_o and axi_bready_i) or
                     ( resp_valid_w and resp_is_write_w and
                       (not resp_is_last_w) ); -- ignore write resps mid burst

end rtl;
