-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- 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 rv_tcm is
    generic (
      RAM_WORDS     : natural range 2**10 to 2**20 := 2**16;  -- RAM in words
      --
      BOOT_ADDR     : slv_31_00 := X"80000000";
      CPU_ID        : slv_31_00 := X"00000000";
      CACHE_ADDR_L  : slv_31_00 := X"80000000";
      CACHE_ADDR_H  : slv_31_00 := X"8FFFFFFF" );
  port ( clk_i          : in  std_logic;
         xrst_i         : in  std_logic;     -- syncr reset
         xrst_cpu_i     : in  std_logic;     -- riscv core only
         -- AXI slave
         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;
         --  vsbus for peripherals
         pp_irq_i       : in  std_logic;     -- perip interrupt
         pp_addr_o      : out slv_31_00;
         pp_rd_o        : out std_logic;
         pp_rd_data_i   : in  slv_31_00;
         pp_wr_o        : out slv_03_00;
         pp_wr_data_o   : out slv_31_00
         );
end entity rv_tcm;


architecture rtl of rv_tcm is

  component rv_cpu
    generic (
      BOOT_ADDR       : slv_31_00 := X"80000000";
      CPU_ID          : slv_31_00 := X"00000000";
      CACHE_ADDR_L    : slv_31_00 := X"80000000";
      CACHE_ADDR_H    : slv_31_00 := X"8FFFFFFF";
      --
      RISCV_M         : std_logic := '1';   -- RV mul / div extension
      RISCV_E         : std_logic := '0';   -- RV rf embedded extension
      RISCV_S_U       : std_logic := '1';   -- supervisor/user privilege levels
  --  RISCV_I         : std_logic := '1';   -- always enabled when no RISCV_E !
  --  RISCV_Zicsr     : std_logic := '1';   -- always enabled !
  --  RISCV_Zifencei  : std_logic := '1';   -- always enabled !
      RISCV_A         : std_logic := '1';  -- amo extension
      RISCV_Zba       : std_logic := '1';  -- address generation (shXadd)
      RISCV_ZB        : std_logic := '1';  -- ZB-extension + Zicond
      --
      SUPPORT_MMU         : std_logic := '1';
      BYPASS_LOAD         : std_logic := '1';
      BYPASS_MULT         : std_logic := '1';
      EXTRA_DECODE_STAGE  : std_logic := '1' );
    port ( clk_i              : in  std_logic;
           xrst_i             : in  std_logic;    -- syncr reset
           --   interrupt request from irq ctrl
           irq_i              : in  std_logic;    -- external inter
           tmrirq_i           : in  std_logic;    -- riscv timer inter
           --  icache inputs
           imem_accept_i      : in  std_logic;
           imem_valid_i       : in  std_logic;
           imem_error_i       : in  std_logic;
           imem_rdata_i       : in  slv_31_00;
           --  icache outputs
           imem_rd_o          : out std_logic;
           imem_pc_o          : out slv_31_00;
           imem_flush_o       : out std_logic;
           imem_invalidate_o  : out std_logic;
           --  dcache/peripheral inputs
           dmem_rdata_i       : in  slv_31_00;
           dmem_accept_i      : in  std_logic;
           dmem_ack_i         : in  std_logic;
           dmem_error_i       : in  std_logic;
           dmem_resp_tag_i    : in  slv_10_00;
           --  dcache/peripheral outputs
           dmem_rd_o          : out std_logic;
           dmem_wr_o          : out slv_03_00;
           dmem_addr_o        : out slv_31_00;
           dmem_wdata_o       : out slv_31_00;
           dmem_cacheable_o   : out std_logic;
           dmem_req_tag_o     : out slv_10_00;
           dmem_flush_o       : out std_logic;
           dmem_invalidate_o  : out std_logic;
           dmem_wb_o          : out std_logic );
  end component;


  component tcm_mem
    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_addr_i        : in  slv_31_00;
           dmem_data_wr_i     : in  slv_31_00;
           dmem_rd_i          : in  std_logic;
           dmem_wr_i          : in  slv_03_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 component;



  signal icache_rd_w          : std_logic;
  signal icache_pc_w          : slv_31_00;
  signal icache_inst_x01_w    : slv_31_00;
  signal icache_inst_w        : slv_31_00;
  signal icache_accept_w      : std_logic;
  signal icache_valid_w       : std_logic;
  signal icache_flush_w       : std_logic;
  signal icache_error_w       : std_logic;
  signal icache_invalidate_w  : std_logic;

  signal core_d_cacheable_w   : std_logic;
  signal core_d_accept_w      : std_logic;
  signal core_d_ack_w         : std_logic;
  signal core_d_rd_w          : std_logic;                    -- core_d rd ena
  signal core_d_wr_w          : slv_03_00;                    -- core_d wr ena
  signal core_d_addr_w        : slv_31_00;
  signal core_d_data_rd_w     : slv_31_00;
  signal core_d_data_wr_w     : slv_31_00;
  signal core_d_resp_tag_w    : slv_10_00;
  signal core_d_req_tag_w     : slv_10_00;
  signal core_d_wb_w          : std_logic;
  signal core_d_error_w       : std_logic;
  signal core_d_flush_w       : std_logic;
  signal core_d_invalidate_w  : std_logic;

  signal dcache_accept_w      : std_logic;
  signal dcache_ack_w         : std_logic;
  signal dcache_cached_rd_w   : std_logic;                    -- dcache rd ena
  signal dcache_cached_wr_w   : slv_03_00;                    -- dcache wr ena
  signal dcache_data_rd_w     : slv_31_00;
  signal dcache_resp_tag_w    : slv_10_00;
  signal dcache_error_w       : std_logic;

  --  perip control
  signal core_rd_q            : std_logic;
  signal core_wr_q            : slv_03_00;
  signal core_addr_q          : slv_31_00;
  signal core_wr_data_q       : slv_31_00;
  signal core_d_cacheable_q   : std_logic;
  --  use register in dcache

  signal pp_access_w          : std_logic;
  signal pp_ack_w             : std_logic;

begin




  p_pp_retime : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        core_rd_q          <= '0';
        core_wr_q          <= X"0";                     -- 3:0   --! 3:1 NC
        core_addr_q        <= X"00000000";              -- 31:0
        core_wr_data_q     <= X"00000000";              -- 31:0
        core_d_cacheable_q <= '0';
      else
        if (core_d_accept_w = '1') then
          core_rd_q          <= core_d_rd_w;
          core_wr_q          <= core_d_wr_w;            -- 3:0   --! 3:1 NC
          core_addr_q        <= core_d_addr_w;          -- 31:0
          core_wr_data_q     <= core_d_data_wr_w;       -- 31:0
          core_d_cacheable_q <= core_d_cacheable_w;
        end if;
      end if;
    end if;
  end process p_pp_retime;


  --
  --  NOTE: peripheral bus has a one clock cycle delay in all operations
  --
  pp_access_w  <= not core_d_cacheable_q;
  pp_addr_o    <= core_addr_q;
  pp_rd_o      <= core_rd_q and pp_access_w;
  pp_wr_o      <= core_wr_q and (pp_access_w & pp_access_w &
                                 pp_access_w & pp_access_w);
  pp_wr_data_o <= core_wr_data_q;
  --  always ack after one clock
  pp_ack_w     <= (not core_d_cacheable_q) and
                  (core_rd_q or ne( core_wr_q,"0000"));



  --  dcache read/write enebles
  dcache_cached_rd_w <= core_d_rd_w and core_d_cacheable_w;
  dcache_cached_wr_w <= core_d_wr_w and ( core_d_cacheable_w &
                                          core_d_cacheable_w &
                                          core_d_cacheable_w &
                                          core_d_cacheable_w );



  --
  --  MUXED dcache/perip inputs to core
  --
  core_d_data_rd_w <= dcache_data_rd_w when (core_d_cacheable_q = '1') else
                      pp_rd_data_i;

  core_d_ack_w     <= pp_ack_w when (core_d_cacheable_q = '0') else
                      dcache_ack_w;

  core_d_resp_tag_w <= dcache_resp_tag_w;


  core_d_accept_w <= dcache_accept_w  and  '1';    -- perips always ready
  core_d_error_w  <= dcache_error_w   or   '0';    -- perips never error



  u_cpu : rv_cpu
    generic map ( BOOT_ADDR       => BOOT_ADDR,
                  CPU_ID          => CPU_ID,
                  CACHE_ADDR_L    => CACHE_ADDR_L,
                  CACHE_ADDR_H    => CACHE_ADDR_H,
                  --
                  RISCV_M         => '1',   -- RV mul / div extension
                  RISCV_E         => '0',   -- RV rf embedded extension
                  RISCV_S_U       => '1',   -- supervisor/user privilege levels
               -- RISCV_I         => '1',   -- always enabled when no RISCV_E !
               -- RISCV_Zicsr     => '1',   -- always enabled !
               -- RISCV_Zifencei  => '1',   -- always enabled !
                  RISCV_A         => '1',   -- amo extension
                  RISCV_Zba       => '1',   -- address generation (shXadd)
                  RISCV_ZB        => '1',   -- ZB-extension + Zicond
                  --
                  SUPPORT_MMU         => '1',
                  BYPASS_LOAD         => '1', -- support data load bypass
                  BYPASS_MULT         => '1', -- support multiplier bypass
                  EXTRA_DECODE_STAGE  => '1' )
    port map ( clk_i              => clk_i,
               xrst_i             => xrst_cpu_i,
               --  interrupt request from irq ctrl
               irq_i              => pp_irq_i,   -- external inter from irq_ctrl
               tmrirq_i           => '0',        -- riscv timer inter
               -- icache inputs
               imem_accept_i      => icache_accept_w,
               imem_valid_i       => icache_valid_w,
               imem_error_i       => icache_error_w,
               imem_rdata_i       => icache_inst_w,
               --  icache outputs
               imem_rd_o          => icache_rd_w,
               imem_pc_o          => icache_pc_w,
               imem_flush_o       => icache_flush_w,
               imem_invalidate_o  => icache_invalidate_w,
               --  dcache/peripheral inputs
               dmem_rdata_i       => core_d_data_rd_w,   -- MUXED dcache/perip
               dmem_accept_i      => core_d_accept_w,
               dmem_ack_i         => core_d_ack_w,       -- MUXED dcache/perip
               dmem_error_i       => core_d_error_w,
               dmem_resp_tag_i    => core_d_resp_tag_w,
               --  dcache/peripheral outputs
               dmem_rd_o          => core_d_rd_w,        -- dcache/perip rd ena
               dmem_wr_o          => core_d_wr_w,        -- dcache/perip wr ena
               dmem_addr_o        => core_d_addr_w,
               dmem_wdata_o       => core_d_data_wr_w,
               dmem_cacheable_o   => core_d_cacheable_w,
               dmem_req_tag_o     => core_d_req_tag_w,
               dmem_flush_o       => core_d_flush_w,
               dmem_invalidate_o  => core_d_invalidate_w,
               dmem_wb_o          => core_d_wb_w );


  icache_inst_w <=
    -- pragma translate_off
    to_stdlogicvector(to_bitvector(
      -- pragma translate_on
      icache_inst_x01_w
      -- pragma translate_off
      ))
    -- pragma translate_on
    ;



  u_tcm_mem : tcm_mem
    generic map ( RAM_WORDS  => RAM_WORDS )              -- 1K - 1M
    port map ( clk_i              => clk_i,
               xrst_i             => xrst_i,             -- syncr reset
               --
               imem_rd_i          => icache_rd_w,
               imem_flush_i       => icache_flush_w,
               imem_invalidate_i  => icache_invalidate_w,
               imem_pc_i          => icache_pc_w,
               imem_accept_o      => icache_accept_w,
               imem_valid_o       => icache_valid_w,
               imem_error_o       => icache_error_w,
               imem_inst_o        => icache_inst_x01_w ,
               --
               dmem_addr_i        => core_d_addr_w,
               dmem_data_wr_i     => core_d_data_wr_w,
               dmem_rd_i          => dcache_cached_rd_w,
               dmem_wr_i          => dcache_cached_wr_w,
               dmem_req_tag_i     => core_d_req_tag_w,
               dmem_invalidate_i  => core_d_invalidate_w,
               dmem_writeback_i   => core_d_wb_w,
               dmem_flush_i       => core_d_flush_w,
               dmem_data_rd_o     => dcache_data_rd_w,
               dmem_accept_o      => dcache_accept_w,
               dmem_ack_o         => dcache_ack_w,
               dmem_error_o       => dcache_error_w,
               dmem_resp_tag_o    => dcache_resp_tag_w,
               --
               axi_arready_o      => axi_arready_o,
               axi_rvalid_o       => axi_rvalid_o,
               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,
               axi_wready_o       => axi_wready_o,
               axi_bvalid_o       => axi_bvalid_o,
               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 );

end rtl;
