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


-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- ARRIA 10 GENERIC ROM MODEL
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

-- pragma translate_off
-- - - - - - - -
--  libraries
-- - - - - - - -
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.rv_pkg.all;
--
entity altera_syncram is
  generic ( intended_device_family : string  := "Arria 10";
            lpm_type               : string  := "altera_syncram";
            address_aclr_a         : string  := "CLEAR0";
            clock_enable_input_a   : string  := "BYPASS";
            clock_enable_output_a  : string  := "BYPASS";
            init_file              : string  := "rom.mif";
            numwords_a             : natural := 2**14;             -- 2**ADDR_W
            operation_mode         : string  := "ROM";
            outdata_aclr_a         : string  := "NONE";
            outdata_sclr_a         : string  := "NONE";
            outdata_reg_a          : string  := "UNREGISTERED";
            enable_force_to_zero   : string  := "FALSE";
            widthad_a              : natural := 14;                -- ADDR_W
            width_a                : natural := 32;                -- DATA_W
            width_byteena_a        : natural := 1 );
  port ( clock0          : in  std_logic;
         aclr0           : in  std_logic;
         address_a       : in  std_logic_vector;
         rden_a          : in  std_logic;
         q_a             : out std_logic_vector;
         aclr1           : in  std_logic;
         address2_a      : in  std_logic;
         address2_b      : in  std_logic;
         address_b       : in  std_logic;
         addressstall_a  : in  std_logic;
         addressstall_b  : in  std_logic;
         byteena_a       : in  std_logic;
         byteena_b       : in  std_logic;
         clock1          : in  std_logic;
         clocken0        : in  std_logic;
         clocken1        : in  std_logic;
         clocken2        : in  std_logic;
         clocken3        : in  std_logic;
         data_a          : in  std_logic_vector;
         data_b          : in  std_logic;
         eccencbypass    : in  std_logic;
         eccencparity    : in  std_logic_vector;
         eccstatus       : out std_logic;  -- ? w ?
         q_b             : out std_logic_vector;
         rden_b          : in  std_logic;
         sclr            : in  std_logic;
         wren_a          : in  std_logic;
         wren_b          : in  std_logic );
end entity altera_syncram;
-- pragma translate_on



-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.rv_pkg.all;

entity fpgarom is      -- dummy entity to avoid compilation warning
  generic ( DATA_W    : natural range 8 to 32 := 32;
            ADDR_W    : natural range 4 to 32 := 14;    -- default 16k
            MIF_FILE  : string    := "rom.mif" );       -- for asic sim / fpga
  port ( clk     : in  std_logic  := 'X';
         xreset  : in  std_logic  := 'X';
         rden    : in  std_logic  := 'X';
         address : in  slv_t(ADDR_W-1 downto 0);        -- ! WORD !
         dout    : out slv_t(DATA_W-1 downto 0) );      -- dataout
end entity fpgarom;


architecture struct of fpgarom is

  component altera_syncram
    generic ( intended_device_family : string  := "Arria 10";
              lpm_type               : string  := "altera_syncram";
              address_aclr_a         : string  := "CLEAR0";
              clock_enable_input_a   : string  := "BYPASS";
              clock_enable_output_a  : string  := "BYPASS";
              init_file              : string  := MIF_FILE;
              numwords_a             : natural := 2**ADDR_W;
              operation_mode         : string  := "ROM";
              outdata_aclr_a         : string  := "NONE";
              outdata_sclr_a         : string  := "NONE";
              outdata_reg_a          : string  := "UNREGISTERED";
              enable_force_to_zero   : string  := "FALSE";
              widthad_a              : natural := ADDR_W;
              width_a                : natural := DATA_W;
              width_byteena_a        : natural := 1 );
    port ( clock0          : in  std_logic;
           aclr0           : in  std_logic;
           address_a       : in  std_logic_vector(ADDR_W-1 downto 0);
           rden_a          : in  std_logic;
           q_a             : out std_logic_vector(DATA_W-1 downto 0);
           aclr1           : in  std_logic;
           address2_a      : in  std_logic;
           address2_b      : in  std_logic;
           address_b       : in  std_logic;
           addressstall_a  : in  std_logic;
           addressstall_b  : in  std_logic;
           byteena_a       : in  std_logic;
           byteena_b       : in  std_logic;
           clock1          : in  std_logic;
           clocken0        : in  std_logic;
           clocken1        : in  std_logic;
           clocken2        : in  std_logic;
           clocken3        : in  std_logic;
           data_a          : in  std_logic_vector(DATA_W-1 downto 0);
           data_b          : in  std_logic;
           eccencbypass    : in  std_logic;
           eccencparity    : in  std_logic_vector(7 downto 0);
           eccstatus       : out std_logic;  -- ? w ?
           q_b             : out std_logic_vector(DATA_W-1 downto 0);
           rden_b          : in  std_logic;
           sclr            : in  std_logic;
           wren_a          : in  std_logic;
           wren_b          : in  std_logic );
  end component;

  signal aclr : std_logic;

begin

  aclr <= not xreset;

  u_altera_syncram : altera_syncram
    generic map ( intended_device_family => "Arria 10",
                  lpm_type               => "altera_syncram",
                  address_aclr_a         => "CLEAR0",
                  clock_enable_input_a   => "BYPASS",
                  clock_enable_output_a  => "BYPASS",
                  init_file              => MIF_FILE,
                  numwords_a             => 2**ADDR_W,
                  operation_mode         => "ROM",
                  outdata_aclr_a         => "NONE",
                  outdata_sclr_a         => "NONE",
                  outdata_reg_a          => "UNREGISTERED",
                  enable_force_to_zero   => "FALSE",
                  widthad_a              => ADDR_W,
                  width_a                => DATA_W,
                  width_byteena_a        => 1 )
    port map ( clock0          => clk,
               aclr0           => aclr,
               address_a       => address,
               rden_a          => rden,
               q_a             => dout,
               aclr1           => '0',
               address2_a      => '1',
               address2_b      => '1',
               address_b       => '1',
               addressstall_a  => '0',
               addressstall_b  => '0',
               byteena_a       => '1',
               byteena_b       => '1',
               clock1          => '1',
               clocken0        => '1',
               clocken1        => '1',
               clocken2        => '1',
               clocken3        => '1',
               data_a          => (others => '1'),
               data_b          => '1',
               eccencbypass    => '0',
               eccencparity    => (others => '0'),
               eccstatus       => OPEN,
               q_b             => OPEN,
               rden_b          => '1',
               sclr            => '0',
               wren_a          => '0',
               wren_b          => '0' );
end struct;



-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- GENERIC SIMULATION ROM MODEL
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

-- pragma translate_off
-- Mem model for simulation and FPGA synthesis (USE lib model in asic synthesis)

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


use ieee.std_logic_textio.all;
use std.textio.all;
library std_developerskit;
use std_developerskit.std_iopak.all;

entity rom_ss_1p is
  generic ( DATA_W      : natural range 8 to 32 := 32;
            ADDR_W      : natural range 4 to 32 := 14;  -- default 16k
            MIF_FILE    : string    := "rom.mif";       -- for asic sim / fpga
            HOLD_DELAY  : time      := 0.5 ns;          -- dout hold delay
            ODELAY      : time      := 4.5 ns;          -- clk to dout delay
            LOG_FILE    : string    := "rom.out";       -- log file
            DEBUG       : std_logic := '0' );           -- log accesses
  port ( clk       : in  std_logic;
         xreset    : in  std_logic;
         --  inputs
         rd        : in  std_logic;
         addr      : in  slv_t(ADDR_W-1 downto 0);
         --  outputs
         dout      : out slv_t(DATA_W-1 downto 0) );
end entity rom_ss_1p;


architecture behav of rom_ss_1p is

  type rom_t is array((2**ADDR_W)-1 downto 0) of slv_t(DATA_W-1 downto 0);
  shared variable rom : rom_t;

  signal init_done_w     : std_logic := '0';
  signal rom_read_q      : slv_t(DATA_W-1 downto 0);

begin


  p_rom : process (clk) is
    -- - - - - - - - - - - - - - - - - - - - - - - - - - - -
    --  for checking .mif file contents (some sanity checks)
    -- - - - - - - - - - - - - - - - - - - - - - - - - - - -
    procedure check_syntax (string_in  : string;
                            string_ref : string;
                            int_in     : integer;
                            int_ref    : integer;
                            err_code   : string;
                            err_line   : integer) is
    begin
      assert ( (string_in = string_ref) and (int_in = int_ref) )
        report "   Mif file format error: " & err_code & " : " & string_in
        & ", line: " & To_String(err_line)
        severity FAILURE;
    end check_syntax;

    -- - - - - - - - - - - - - - - - -
    --  read in .mif file
    -- - - - - - - - - - - - - - - - -
    procedure mif_read ( file_name : in  string;
                         memory    : out rom_t) is
      file mif_data         : text;
      variable status       : File_Open_Status;
      variable ln           : line;
      variable char1        : string (1 to 1);
      variable ok           : boolean;
      --
      variable depth_c      : string (1 to 8);  -- i.e. "DEPTH = "
      variable width_c      : string (1 to 8);  -- i.e. "WIDTH = "
      variable addr_radix_c : string (1 to 20); -- i.e. "ADDRESS_RADIX = dec;"
      variable data_radix_c : string (1 to 17); -- i.e. "DATA_RADIX = bin;"
      variable content_c    : string (1 to 7);
      variable begin_c      : string (1 to 5);
      variable separator_c  : string (1 to 3);  -- i.e. " : " or " = "
      variable semicolon_c  : string (1 to 1);  -- i.e. ";"
      variable end_c        : string (1 to 4);  -- i.e. "END;"
      --
      variable line_cnt_v   : integer := 0;
      variable data_cnt_v   : integer := 0;
      variable tmp_addr_v   : integer;
      variable rom_depth_v  : integer;
      variable rom_word_v   : integer;
      variable tmp_val_v    : slv_t(rom(0)'range);

    begin
      -- Mif file format is like:
      --    DEPTH = 16384;
      --    WIDTH = 16;
      --    ADDRESS_RADIX = dec;
      --    DATA_RADIX = bin;
      --    CONTENT
      --    BEGIN
      --    0 : 0000000000000000;
      --    ...
      --    16383 : 0000000000000000;
      --    END;
      File_Open(status, mif_data, MIF_FILE, read_mode);
      assert (status = Open_Ok)
        report "   ERROR : MIF file does not exist."
        severity FAILURE;
      readline(mif_data,ln);            -- DEPTH = x;
      line_cnt_v := line_cnt_v + 1;
      read(ln, depth_c);
      read(ln, tmp_addr_v);
      check_syntax (depth_c, "DEPTH = ", 0, 0, "DEPTH ERROR", line_cnt_v);
      rom_depth_v := tmp_addr_v;
      --
      readline(mif_data,ln);            -- WIDTH = y;
      line_cnt_v := line_cnt_v + 1;
      read(ln, width_c);
      read(ln, tmp_addr_v);
      check_syntax (width_c, "WIDTH = ", tmp_addr_v, rom(0)'length,
                    "WIDTH ERROR", line_cnt_v);
      rom_word_v := tmp_addr_v;
      --
      readline(mif_data,ln);            -- ADDRESS_RADIX = dec;
      line_cnt_v := line_cnt_v + 1;
      read(ln,addr_radix_c);
      check_syntax (addr_radix_c, "ADDRESS_RADIX = dec;", 0, 0,
                    "ADDRESS RADIX ERROR", line_cnt_v);
      --
      readline(mif_data,ln);            -- DATA_RADIX = bin;
      line_cnt_v := line_cnt_v + 1;
      read(ln,data_radix_c);
      check_syntax (data_radix_c, "DATA_RADIX = bin;",0, 0,
                    "DATA RADIX ERROR", line_cnt_v);
      --
      readline(mif_data,ln);            -- CONTENT
      line_cnt_v := line_cnt_v + 1;
      read(ln,content_c);
      check_syntax (content_c, "CONTENT",0, 0, "CONTENT ERROR", line_cnt_v);
      --
      readline(mif_data,ln);            -- BEGIN
      line_cnt_v := line_cnt_v + 1;
      read(ln,begin_c);
      check_syntax (begin_c, "BEGIN",0, 0, "BEGIN ERROR", line_cnt_v);
      --
      while not endfile(mif_data) loop   -- NOTE: Not all words included
        readline(mif_data,ln);
        line_cnt_v := line_cnt_v + 1;
        --
        if (line_cnt_v mod 100000 = 0) then
          report "NOTE: read : " & To_String(line_cnt_v) & " lines. . .";
        end if;
        --
        read(ln, tmp_addr_v, ok);     --
        if ( ok ) then                -- continue data reading
          read(ln, separator_c);
          read(ln, tmp_val_v);      -- NOTE: "use ieee.std_logic_textio.all;"
          read(ln, semicolon_c);      --                    _______
          --
          -- checking only last character is ";"
          check_syntax (semicolon_c, ";",0, 0, "DATA ERROR", line_cnt_v);
          --
          rom(tmp_addr_v) := tmp_val_v;
          data_cnt_v      := data_cnt_v + 1;
        else
          read(ln, end_c, ok);        -- reached "END;"
          if ( ok and end_c = "END;" ) then
            report "> > > NOTE: Reached mif file end: " & file_name;
            report "> > > NOTE: Found " & To_String(data_cnt_v) &
              " words (size expected: " & To_String(rom_depth_v) &
              " x " & To_String(rom_word_v) & ")";
          else
            report "ERROR: Bad line (" & To_String(line_cnt_v) & "): " &
              file_name;
          end if;
        end if;
      end loop;
      --
    end procedure mif_read;

  begin
    --
    if (init_done_w = '0') then                      -- < < <  MEM INIT
      --
      mif_read(MIF_FILE, rom);
      --
      report "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - ";
      report "- - - BOOTROM  - MEM INIT : "  &  MIF_FILE &
        " -  MEM SIZE : " & To_String(rom'length, "%d");
      report "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - ";
      init_done_w <= '1';
    end if;                                          -- < < <  MEM INIT
    --
    if (clk'event and clk = '1') then
      if (xreset = '0') then
        null;
      elsif (rd = '1') then
        rom_read_q <= (others => 'X') after HOLD_DELAY,
                      rom(to_uint(addr)) after ODELAY;
      end if;
    end if;
  end process p_rom;
  --
  dout <= rom_read_q;
  --
  -- NOTE: data delayed by one cycle
  --
  g_log : if ( DEBUG = '1' ) generate
    file out_file       : ASCII_TEXT open WRITE_MODE is LOG_FILE;
  begin
    p_log : process (clk)
      variable tmp_str_v    : string (1 to 16);
    begin
      if (clk'event and clk = '1') then
        if (xreset = '1' and rd = '1') then
          tmp_str_v := To_String(to_bitvector(addr),"%04X") & " : " &
                       To_String(to_bitvector(rom(to_uint(addr))),"%06X") ; --&
                       fputs( (tmp_str_v & To_String(now)),  out_file);

        end if;
      end if;
    end process;
  end generate g_log;
  --
end behav;
-- pragma translate_on




-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- 32b x 16KW ROM MODEL FOR SIMULATION AND FPGA SYNTHESIS
-- (use lib model in ASIC synthesis)
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- Mem model for simulation and FPGA synthesis (USE lib model in asic synthesis)

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

-- pragma translate_off
use ieee.std_logic_textio.all;
use std.textio.all;
library std_developerskit;
use std_developerskit.std_iopak.all;
-- pragma translate_on

entity rom32x16k is
  generic ( DATA_W      : natural range 8 to 32 := 32;
            ADDR_W      : natural range 4 to 32 := 14;  -- default 16k
            MIF_FILE    : string    := "rom.mif";       -- for asic sim / fpga
            ROM_TYPE    : string    := "AUTO";          -- MLAB/M20K,  M4K/M512
            -- pragma translate_off
            HOLD_DELAY  : time      := 0.5 ns;          -- dout hold delay
            ODELAY      : time      := 4.5 ns;          -- clk to dout delay
            LOG_FILE    : string    := "rom.out";       -- log file
            -- pragma translate_on
            DEBUG       : std_logic := '1' );           -- log accesses
  port ( clk       : in  std_logic;
         xreset    : in  std_logic;
         --  inputs
         rd        : in  std_logic;
         addr      : in  slv_t(ADDR_W-1 downto 0);
         --  outputs
         dout      : out slv_t(DATA_W-1 downto 0) );
end entity rom32x16k;


architecture struct of rom32x16k is

  -- asic sim/syn : '0', FPGA: '1'
  constant USE_FPGA : std_logic := '1'
                                   -- pragma translate_off
                                   and '0'
                                   -- pragma translate_on
                                   ;

begin

  g_FPGA : if ( USE_FPGA = '1' ) generate
    --
    component fpgarom       -- dummy entity to avoid simulation warning
      generic ( DATA_W    : natural range 8 to 32 := 32;
                ADDR_W    : natural range 4 to 32 := 13;  -- default 16k
                MIF_FILE  : string  := "rom.mif" );       -- for sim / fpga
      port ( clk     : in  std_logic  := 'X';
             xreset  : in  std_logic  := 'X';
             rden    : in  std_logic  := 'X';
             address : in  slv_t(ADDR_W-1 downto 0) := (others => 'X'); -- WORD
             dout    : out slv_t(DATA_W-1 downto 0) );      -- dataout
    end component;
    --
  begin
    --
    u0 : component fpgarom
      generic map ( DATA_W    => DATA_W,
                    ADDR_W    => ADDR_W,              -- default 16k
                    MIF_FILE  => MIF_FILE )           -- for sim / fpga
       port map ( clk      => clk,                    --           .clk
                  xreset   => xreset,                 --           .aclr
                  rden     => rd,                     --           .rden
                  address  => addr,                   --  rom_input.address
                  dout     => dout );                 -- rom_output.dataout
  end generate g_FPGA;


  -- pragma translate_off
  g_ASIC : if ( USE_FPGA /= '1' ) generate
    --
    component rom_ss_1p
      generic ( DATA_W      : natural range 8 to 32 := 32;
                ADDR_W      : natural range 4 to 32 := 14;  -- default 16k
                MIF_FILE    : string  := "rom.mif";         -- for sim / fpga
                HOLD_DELAY  : time      := 0.5 ns;          -- dout hold delay
                ODELAY      : time      := 4.5 ns;          -- clk to dout delay
                LOG_FILE    : string    := "rom.out";       -- log file
                DEBUG       : std_logic := '0' );           -- log accesses
      port ( clk       : in  std_logic;
             xreset    : in  std_logic;
             --  inputs
             rd        : in  std_logic;
             addr      : in  slv_t(ADDR_W-1 downto 0);
             --  outputs
             dout      : out slv_t(DATA_W-1 downto 0) );
    end component;
    --
  begin
    --
    u_rom_ss_1p : rom_ss_1p
      generic map ( DATA_W      => DATA_W,      -- data width
                    ADDR_W      => ADDR_W,      -- rom size : 2** ADDR_W
                    MIF_FILE    => MIF_FILE,    -- for sim / fpga
                    HOLD_DELAY  => HOLD_DELAY,  -- dout hold delay
                    ODELAY      => ODELAY,      -- clk to dout delay
                    LOG_FILE    => LOG_FILE,    -- log file
                    DEBUG       => DEBUG )      -- log accesses

      port map ( clk       => clk,
                 xreset    => xreset,
                 --  inputs
                 rd        => rd,
                 addr      => addr,
                 --  outputs
                 dout      => dout );

  end generate g_ASIC;
  -- pragma translate_on

end struct;





-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- tag ram, NO byte enables
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

-- Mem model for simulation and FPGA synthesis (USE lib model in asic synthesis)

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

entity tag_ram is
  generic ( DATA_W             : natural range 8 to 32 := 24;
            ADDR_W             : natural range 4 to 32 := 9;
            ODELAY             : time := 3.5 ns;    -- clk to dout delay
            INITIALIZE_TO_ZERO : std_logic := '0' );
  port ( clk      : in  std_logic;
         xreset   : in  std_logic;                  -- NC now in FPGA synthesis
         --  inputs
         rd       : in  std_logic;
         wr       : in  std_logic;
         addr     : in  slv_t(ADDR_W-1 downto 0);
         din      : in  slv_t(DATA_W-1 downto 0);
         --  outputs
         dout     : out slv_t(DATA_W-1 downto 0) );
end entity tag_ram;


architecture behav of tag_ram is

  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- single port RAM
  -- mode: read OR write
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  type cachet_t is array (0 to 2**ADDR_W-1) of slv_t(DATA_W-1 downto 0);
  signal ram           : cachet_t;
  signal ram_read_q    : slv_t(DATA_W-1 downto 0);

  -- pragma translate_off
  signal initialized_w : std_logic := '0';
  -- pragma translate_on

begin

  -- synchronous write
  p_ram : process (clk)
  begin
    -- pragma translate_off
    if (initialized_w = '0' and INITIALIZE_TO_ZERO = '1') then
      ram <= (others =>(others => '0'));
      initialized_w <= '1';
    end if;
    -- pragma translate_on
    if ( clk'event and clk = '1' ) then
      if ( wr = '1' ) then
        ram(to_uint(addr)) <= din;
      end if;
      if ( rd = '1' ) then
        ram_read_q <=
          -- pragma translate_off
          (others => 'U') after 0.2 ns,  -- some hold time
          -- pragma translate_on
          ram(to_uint(addr))
          -- pragma translate_off
          after ODELAY
          -- pragma translate_on
          ;
        --
      end if;
    end if;
    -- pragma translate_off
    assert (not (rd = '1' and wr = '1' ))
      report "ERROR: Read and Write are not allowed at the same cycle"
      severity FAILURE;
    -- pragma translate_on
  end process p_ram;

  dout <= ram_read_q;

end behav;





-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- data_ram, data RAM with byte enables
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

-- Mem model for simulation and FPGA synthesis (USE lib model in asic synthesis)
-- - - - - - - -
--  libraries
-- - - - - - - -
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.rv_pkg.all;

entity data_ram is
  generic ( DATA_W             : natural range 8 to 64 := 64;
            ADDR_W             : natural range 4 to 32 := 12;
            ODELAY             : time := 4.5 ns;     -- clk to dout delay
            INITIALIZE_TO_ZERO : std_logic := '0' );
  port ( clk      : in  std_logic;
         xreset   : in  std_logic;                   -- NC now in FPGA synthesis
         --  inputs
         rd       : in  std_logic;
         wr       : in  slv_t(DATA_W/8-1 downto 0);  -- 64b / 32b
         addr     : in  slv_t(ADDR_W-1 downto 0);
         din      : in  slv_t(DATA_W-1 downto 0);
         --  outputs
         dout     : out slv_t(DATA_W-1 downto 0) );
end entity data_ram;


architecture behav of data_ram is

  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- single port RAM
  -- mode: read OR write
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  type cachet_t is array (0 to 2**ADDR_W-1) of slv_t(DATA_W-1 downto 0);
  signal ram           : cachet_t;
  signal ram_read_q    : slv_t(DATA_W-1 downto 0);

  -- pragma translate_off
  signal initialized_w : std_logic := '0';
  -- pragma translate_on

begin

  -- synchronous write
  p_ram : process (clk)
  begin
    -- pragma translate_off
    if (initialized_w = '0' and INITIALIZE_TO_ZERO = '1') then
      ram <= (others =>(others => '0'));
      initialized_w <= '1';
    end if;
    -- pragma translate_on
    if ( clk'event and clk = '1' ) then
      for ii in 0 to DATA_W/8-1 loop
        if ( wr(ii) = '1' ) then
          ram(to_uint(addr))(ii*8+7 downto ii*8) <= din(ii*8+7 downto ii*8);
        end if;
      end loop;
      --
      --
      if ( rd = '1' ) then -- and is_zero(wr)) then
        ram_read_q <=
          -- pragma translate_off
          (others => 'L') after 0.2 ns,             -- some hold time
          -- pragma translate_on
          ram(to_uint(addr))
          -- pragma translate_off
          after ODELAY
          -- pragma translate_on
          ;
        --
      end if;
    end if;
    -- pragma translate_off
    assert (not (rd = '1' and not_zero(wr) ))
      report "ERROR: Read and Write are not allowed at the same cycle"
      severity FAILURE;
    -- pragma translate_on
  end process p_ram;

  dout <= ram_read_q;

end behav;
