--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- Mock Turtle
-- https://gitlab.cern.ch/coht/mockturtle
--------------------------------------------------------------------------------
--
-- unit name:   mt_urv_wrapper
--
-- description: A small wrapper for the URV encompassing the internal RAM and
-- access to the RAM through CPU CSR register block.
--
--------------------------------------------------------------------------------
-- Copyright CERN 2014-2018
--------------------------------------------------------------------------------
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 2.0 (the "License"); you may not use this file except
-- in compliance with the License. You may obtain a copy of the License at
-- http://solderpad.org/licenses/SHL-2.0.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this 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.
--------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

use work.genram_pkg.all;
use work.memory_loader_pkg.all;
use work.wishbone_pkg.all;
use work.urv_pkg.all;

entity plc_urv is
  generic(
    g_IRAM_LOG_SIZE : natural := 10);
  port(
    clk_sys_i : in  std_logic;
    rst_n_i   : in  std_logic;

    --  WB port to write to iram.
    ram_wb_i : in  t_wishbone_slave_in;
    ram_wb_o : out t_wishbone_slave_out;

    --  Peripheral.
    dwb_o     : out t_wishbone_master_out;
    dwb_i     : in  t_wishbone_master_in);
end plc_urv;

architecture arch of plc_urv is
  signal cpu_rst        : std_logic;
  signal cpu_rst_d      : std_logic;

  signal im_addr  : std_logic_vector(31 downto 0);
  signal im_data  : std_logic_vector(31 downto 0);
  signal im_valid : std_logic;

  signal dm_addr, dm_data_s, dm_data_l                  : std_logic_vector(31 downto 0);
  signal dm_data_select                                 : std_logic_vector(3 downto 0);
  signal dm_load, dm_store, dm_load_done, dm_store_done : std_logic;

  signal dwb_out         : t_wishbone_master_out;
begin

  dwb_o <= dwb_out;

  U_cpu_core : urv_cpu
    generic map (
      g_timer_frequency => 0,
      g_with_hw_debug => 0,
      g_with_hw_mul => 0,
      g_with_hw_div => 0
    )
    port map (
      clk_i            => clk_sys_i,
      rst_i            => cpu_rst,
      irq_i            => '0',
      im_addr_o        => im_addr,
      im_data_i        => im_data,
      im_valid_i       => im_valid,
      dm_addr_o        => dm_addr,
      dm_data_s_o      => dm_data_s,
      dm_data_l_i      => dm_data_l,
      dm_data_select_o => dm_data_select,
      dm_store_o       => dm_store,
      dm_load_o        => dm_load,
      dm_load_done_i   => dm_load_done,
      dm_store_done_i  => dm_store_done,
      dbg_force_i      => '0',
      dbg_enabled_o    => open,
      dbg_insn_i       => x"0000_0000",
      dbg_insn_set_i   => '0',
      dbg_insn_ready_o => open,
      dbg_mbx_data_i   => x"0000_0000",
      dbg_mbx_write_i  => '0',
      dbg_mbx_data_o   => open);

  process (clk_sys_i)
  begin
    if rising_edge(clk_sys_i) then
      if rst_n_i = '0' then
        dwb_out.cyc          <= '0';
        dwb_out.stb          <= '0';
      else
        if dm_load = '1' or dm_store = '1' then
          dwb_out.cyc  <= '1';
          dwb_out.stb  <= '1';
          dwb_out.adr  <= dm_addr;
          dwb_out.sel  <= dm_data_select;
          dwb_out.we   <= dm_store;
          dwb_out.dat  <= dm_data_s;
        elsif dwb_i.ack = '1' then
          dwb_out.cyc <= '0';
          dwb_out.stb <= '0';
        end if;
      end if;
    end if;
  end process;

  dm_data_l <= dwb_i.dat;
  dm_store_done <= dwb_i.ack and dwb_out.we;
  dm_load_done <= dwb_i.ack and not dwb_out.we;

  p_iram: process (clk_sys_i)
  is
    constant IRAM_WSIZE : natural := 2 ** (g_IRAM_LOG_SIZE - 2);
    variable mem : t_ram32_type(0 to IRAM_WSIZE - 1);
  begin
    if rising_edge(clk_sys_i) then
      im_data <= mem (to_integer(unsigned(im_addr(g_IRAM_LOG_SIZE - 1 downto 2))));
      ram_wb_o.ack <= '0';
      if ram_wb_i.stb = '1' and ram_wb_i.cyc = '1' and ram_wb_i.we = '1' then
        mem (to_integer(unsigned(ram_wb_i.adr(g_IRAM_LOG_SIZE - 1 downto 2)))) := ram_wb_i.dat;
        ram_wb_o.ack <= '1';
      end if;
    end if;
  end process;

  ram_wb_o.dat <= (others => 'X');
  ram_wb_o.err <= '0';
  ram_wb_o.rty <= '0';
  ram_wb_o.stall <= '0';

  cpu_rst <= not rst_n_i;

  p_im_valid : process(clk_sys_i)
  begin
    if rising_edge(clk_sys_i) then
      if cpu_rst = '1' then
        im_valid  <= '0';
        cpu_rst_d <= '1';
      else
        cpu_rst_d <= cpu_rst;
        im_valid  <= (not cpu_rst_d);
      end if;
    end if;
  end process p_im_valid;
end arch;
