-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- 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
--
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

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

--
-- ! ! This is a dummy model to avoid compile warning of missing entity ! !
-- ! ! Functional model is used in simulation                           ! !
--
entity ram_2p is
  port ( clock      : in  std_logic   := 'X';               -- clock
         data_a     : in  slv_31_00   := (others => 'X');   -- datain _a
         data_b     : in  slv_31_00   := (others => 'X');   -- datain_b
         address_a  : in  slv_15_00   := (others => 'X');   -- address_a
         address_b  : in  slv_15_00   := (others => 'X');   -- address_b
         wren_a     : in  std_logic   := 'X';               -- wren_a
         wren_b     : in  std_logic   := 'X';               -- wren_b
         byteena_b  : in  slv_03_00   := (others => 'X');   -- byte_enable_b
         q_a        : out slv_31_00;                        -- dataout_a
         q_b        : out slv_31_00 );                      -- dataout_b
end entity ram_2p;
architecture rtl of ram_2p is
begin
end rtl;
-- pragma translate_on



-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--                               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_ram is
  generic ( RAM_WORDS  : natural range 2**10 to 2**20 := 2**16 ); -- 1K - 1M
  port ( clk_i       : in  std_logic;
         xrst_i      : in  std_logic;                             -- syncr reset
         --
         addr0_i     : in  slv_31_00;
         data0_o     : out slv_31_00;
         --
         wr1_i       : in  slv_03_00;                             -- byte enable
         addr1_i     : in  slv_31_00;
         data1_i     : in  slv_31_00;
         data1_o     : out slv_31_00
         );
end entity tcm_ram;


architecture rtl of tcm_ram is

  constant RAM_ADDR_W      : natural range 10 to 20 := log2(RAM_WORDS);

  --  Arria 10 dual port ram, 64k
  component ram_2p
    port ( clock      : in  std_logic   := 'X';              -- clock
           data_a     : in  slv_31_00   := (others => 'X');  -- datain _a
           data_b     : in  slv_31_00   := (others => 'X');  -- datain_b
           address_a  : in  slv_15_00   := (others => 'X');  -- address_a
           address_b  : in  slv_15_00   := (others => 'X');  -- address_b
           wren_a     : in  std_logic   := 'X';              -- wren_a
           wren_b     : in  std_logic   := 'X';              -- wren_b
           byteena_b  : in  slv_03_00   := (others => 'X');  -- byte_enable_b
           q_a        : out slv_31_00;                       -- dataout_a
           q_b        : out slv_31_00 );                     -- dataout_b
  end component ram_2p;

  signal wr_w : std_logic;

  --  selects simulation model when compiled
  constant FPGA_SYNTHESIZABLE  : std_logic := '1'
                                              -- pragma translate_off
                                              and '0'
                                              -- pragma translate_on
                                              ;
begin

  wr_w <= wr1_i(3) or wr1_i(2) or wr1_i(1) or wr1_i(0);



  g_fpga : if ( FPGA_SYNTHESIZABLE = '1' and RAM_WORDS = 2**16 ) generate

    --  Arria 10 dual port ram, 64k
    u_ram_2p : ram_2p
      port map ( clock     => clk_i,                         -- clock
                 data_a    => X"00000000",                   -- datain _a   NC
                 data_b    => data1_i,                       -- datain_b
                 address_a => addr0_i(15+2 downto 0+2),      -- address_a
                 address_b => addr1_i(15+2 downto 0+2),      -- address_b
                 wren_a    => '0',                           -- wren_a      NC
                 wren_b    => wr_w,                          -- wren_b
                 byteena_b => wr1_i,                         -- byte_enable_b
                 q_a       => data0_o,                       -- dataout_a
                 q_b       => data1_o );                     -- dataout_b
  end generate g_fpga;



  g_simulation : if ( FPGA_SYNTHESIZABLE /= '1' or RAM_WORDS /= 2**16) generate
    type ram_t is array (0 to RAM_WORDS-1) of slv_31_00;
    shared variable ram  : ram_t;
    --
    signal ram_read0_q  : slv_31_00;
    signal ram_read1_q  : slv_31_00;
  begin
    --
    p_ram : process (clk_i)
    begin
      if ( clk_i'event and clk_i = '1' ) then
        --  i-bus read only
        --  no read enable here
        ram_read0_q <= ram(to_uint( addr0_i(RAM_ADDR_W-1+2 downto 2)));
        --
        --
        --  d-bus write / read
        if (wr_w = '1') then
          if (wr1_i(0) = '1') then
            ram(to_uint(addr1_i(RAM_ADDR_W-1+2 downto 2)))(7 downto 0)  :=
              data1_i(7 downto 0);
          end if;
          if (wr1_i(1) = '1') then
            ram(to_uint(addr1_i(RAM_ADDR_W-1+2 downto 2)))(15 downto 8) :=
              data1_i(15 downto 8);
          end if;
          if (wr1_i(2) = '1') then
            ram(to_uint(addr1_i(RAM_ADDR_W-1+2 downto 2)))(23 downto 16):=
              data1_i(23 downto 16);
          end if;
          if (wr1_i(3) = '1') then
            ram(to_uint(addr1_i(RAM_ADDR_W-1+2 downto 2)))(31 downto 24):=
              data1_i(31 downto 24);
          end if;
        end if;
        --
        --  no read enable here
        ram_read1_q <= ram(to_uint(addr1_i(RAM_ADDR_W-1+2 downto 2)));
        --
      end if;
    end process p_ram;
    --
    data0_o  <= ram_read0_q;                                 -- i-bus read
    data1_o  <= ram_read1_q;                                 -- d-bus read
    --
  end generate g_simulation;

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_mem is
  generic ( RAM_WORDS  : natural range 2**10 to 2**20 := 2**16 ); -- 1K - 1M
  port ( clk_i              : in  std_logic;
         xrst_i             : in  std_logic;                      -- syncr reset
         --
         imem_rd_i          : in  std_logic;
         imem_flush_i       : in  std_logic;
         imem_invalidate_i  : in  std_logic;
         imem_pc_i          : in  slv_31_00;
         imem_accept_o      : out std_logic;
         imem_valid_o       : out std_logic;
         imem_error_o       : out std_logic;
         imem_inst_o        : out slv_31_00;
         --
         dmem_rd_i          : in  std_logic;
         dmem_wr_i          : in  slv_03_00;
         dmem_addr_i        : in  slv_31_00;
         dmem_data_wr_i     : in  slv_31_00;
         dmem_req_tag_i     : in  slv_10_00;
         dmem_invalidate_i  : in  std_logic;
         dmem_writeback_i   : in  std_logic;
         dmem_flush_i       : in  std_logic;
         dmem_data_rd_o     : out slv_31_00;
         dmem_accept_o      : out std_logic;
         dmem_ack_o         : out std_logic;
         dmem_error_o       : out std_logic;
         dmem_resp_tag_o    : out slv_10_00;
         --
         axi_arready_o      : out std_logic;
         axi_rvalid_o       : out 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      : out std_logic;
         axi_wready_o       : out std_logic;
         axi_bvalid_o       : out 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_mem;


architecture rtl of tcm_mem is

  --  AXI-RAM if
  component tcm_axi
    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_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_rlast_o    : out    std_logic;
           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 component;

  --  RAM
  component tcm_ram
    generic ( RAM_WORDS  : natural range 2**10 to 2**20 := 2**16 ); -- 1K - 1M
    port ( clk_i    : in  std_logic;
           xrst_i   : in  std_logic;
           --
           addr0_i  : in  slv_31_00;
           data0_o  : out slv_31_00;
           --
           wr1_i    : in  slv_03_00;
           addr1_i  : in  slv_31_00;
           data1_i  : in  slv_31_00;
           data1_o  : out slv_31_00 );
  end component;


  -- - - - - - - - - - - - -
  -- AXI -> RAM interface
  -- - - - - - - - - - - - -
  constant RAM_ADDR_W     : natural range 10 to 20 := log2(RAM_WORDS);

  signal ext_accept_w     : std_logic;
  signal ext_ack_w        : std_logic;
  signal ext_rdata_w      : slv_31_00;
  signal ext_wr_w         : slv_03_00;
  signal ext_rd_w         : std_logic;
  signal ext_addr_w       : slv_31_00;
  signal ext_wdata_w      : slv_31_00;

  signal muxed_addr_w     : slv_31_00;
  signal muxed_data_w     : slv_31_00;
  signal muxed_wr_w       : slv_03_00;
  signal data_w           : slv_31_00;

  signal imem_valid_q     : std_logic;                     -- reg

  signal dmem_accept_q    : std_logic;                     -- reg
  signal dmem_tag_q       : slv_10_00;                     -- reg
  signal dmem_ack_q       : std_logic;                     -- reg
  signal ext_ack_q        : std_logic;                     -- reg

  signal axi_awready_o_w  : std_logic;
  signal axi_wready_o_w   : std_logic;
  signal axi_bvalid_o_w   : std_logic;
  signal axi_arready_o_w  : std_logic;
  signal axi_rvalid_o_w   : std_logic;

  signal dmem_accept_o_w  : std_logic;

begin

  u_conv: tcm_axi
    port map ( clk_i          => clk_i,                   -- AXI mem if
               xrst_i         => xrst_i,
               --
               axi_arready_o  => axi_arready_o_w,
               axi_rvalid_o   => axi_rvalid_o_w,
               axi_rdata_o    => axi_rdata_o,
               axi_rresp_o    => axi_rresp_o,
               axi_rid_o      => axi_rid_o,
               axi_rlast_o    => axi_rlast_o,
               axi_arvalid_i  => axi_arvalid_i,
               axi_araddr_i   => axi_araddr_i,
               axi_arid_i     => axi_arid_i,
               axi_arlen_i    => axi_arlen_i,
               axi_arburst_i  => axi_arburst_i,
               axi_rready_i   => axi_rready_i,
               --
               axi_awready_o  => axi_awready_o_w,
               axi_wready_o   => axi_wready_o_w,
               axi_bvalid_o   => axi_bvalid_o_w,
               axi_bresp_o    => axi_bresp_o,
               axi_bid_o      => axi_bid_o,
               axi_awvalid_i  => axi_awvalid_i,
               axi_awaddr_i   => axi_awaddr_i,
               axi_awid_i     => axi_awid_i,
               axi_awlen_i    => axi_awlen_i,
               axi_awburst_i  => axi_awburst_i,
               axi_wvalid_i   => axi_wvalid_i,
               axi_wdata_i    => axi_wdata_i,
               axi_wstrb_i    => axi_wstrb_i,
               axi_wlast_i    => axi_wlast_i,
               axi_bready_i   => axi_bready_i,
               --
               ram_accept_i   => ext_accept_w,            -- RAM if
               ram_ack_i      => ext_ack_w,
               ram_error_i    => '0',
               ram_rdata_i    => ext_rdata_w,
               ram_wr_o       => ext_wr_w,
               ram_rd_o       => ext_rd_w,
               ram_len_o      => OPEN,                    -- NC
               ram_addr_o     => ext_addr_w,
               ram_wdata_o    => ext_wdata_w );


  axi_awready_o  <= axi_awready_o_w;
  axi_wready_o   <= axi_wready_o_w;
  axi_bvalid_o   <= axi_bvalid_o_w;
  axi_arready_o  <= axi_arready_o_w;
  axi_rvalid_o   <= axi_rvalid_o_w;


  -- - - - - - - - - - - - - - - - - - - - - - - - - -
  --  dual port RAM  (one write port, two read ports)
  --  mode: read first
  -- - - - - - - - - - - - - - - - - - - - - - - - - -

  --  mux access to the 2nd port between external access and CPU data access
  muxed_addr_w <= ext_addr_w when (ext_accept_w = '1') else dmem_addr_i;
  muxed_data_w <= ext_wdata_w when (ext_accept_w = '1') else
                  dmem_data_wr_i;
  muxed_wr_w   <= ext_wr_w when (ext_accept_w = '1') else dmem_wr_i;

 
  ext_rdata_w <= data_w;


  u_tcm_ram : tcm_ram
    generic map ( RAM_WORDS  => RAM_WORDS )  -- 1K - 1M
    port map ( clk_i    => clk_i,
               xrst_i   => xrst_i,
               --
               addr0_i  => imem_pc_i,
               data0_o  => imem_inst_o,
               --
               wr1_i    => muxed_wr_w,
               addr1_i  => muxed_addr_w,
               data1_i  => muxed_data_w,
               data1_o  => data_w );


  -- - - - - - - - - - - -
  --  instruction fetch
  -- - - - - - - - - - - -
  process(clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        imem_valid_q <= '0';
      else
        imem_valid_q <= imem_rd_i;
      end if;
    end if;
  end process;


  imem_accept_o  <= '1';
  imem_valid_o   <= imem_valid_q;
  imem_error_o   <= '0';



  -- - - - - - - - - - - - - - - - - - - - - -
  --  data access / incoming external access
  -- - - - - - - - - - - - - - - - - - - - - -
  process(clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        dmem_accept_q <= '1';
        --  external request, do not accept internal requests in next cycle
      elsif (ext_rd_w = '1' or not_zero(ext_wr_w)) then
        dmem_accept_q <= '0';
      else
        dmem_accept_q <= '1';
      end if;
    end if;
  end process;


  process(clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        dmem_ack_q    <= '0';
        dmem_tag_q    <= "00000000000";
      elsif ((dmem_rd_i = '1' or not_zero(dmem_wr_i) or
              dmem_flush_i = '1' or dmem_invalidate_i = '1' or
              dmem_writeback_i = '1') and dmem_accept_o_w = '1') then
        dmem_ack_q    <= '1';
        dmem_tag_q    <= dmem_req_tag_i;
      else
        dmem_ack_q    <= '0';
      end if;
    end if;
  end process;



  process(clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        ext_ack_q <= '0';
        --  external request accepted
      elsif ((ext_rd_w = '1' or not_zero(ext_wr_w)) and
             ext_accept_w = '1') then
        ext_ack_q <= '1';
      else
        ext_ack_q <= '0';
      end if;
    end if;
  end process;


  dmem_ack_o       <= dmem_ack_q;
  dmem_resp_tag_o  <= dmem_tag_q;
  dmem_data_rd_o   <= data_w;
  dmem_error_o     <= '0';

  dmem_accept_o_w  <= dmem_accept_q;
  dmem_accept_o    <= dmem_accept_o_w;

  ext_accept_w     <= not dmem_accept_q;
  ext_ack_w        <= ext_ack_q;

end rtl;
