Skip to content
Snippets Groups Projects
generic_dpram.vhd 9.21 KiB
Newer Older
-------------------------------------------------------------------------------
-- Title      : Parametrizable dual-port synchronous RAM (Altera version)
-- Project    : Generics RAMs and FIFOs collection
-------------------------------------------------------------------------------
-- File       : generic_dpram.vhd
-- Author     : Tomasz Wlostowski
-- Company    : CERN BE-CO-HT
-- Created    : 2011-01-25
-- Platform   : 
-- Standard   : VHDL'93
-------------------------------------------------------------------------------
-- Description: True dual-port synchronous RAM for Altera FPGAs with:
-- - configurable address and data bus width
-- - byte-addressing mode (data bus width restricted to multiple of 8 bits)
-- Todo:
-- - loading initial contents from file
-------------------------------------------------------------------------------
-- Copyright (c) 2011 CERN
-------------------------------------------------------------------------------
-- Revisions  :
-- Date        Version  Author          Description
-- 2011-01-25  1.0      twlostow        Created
-------------------------------------------------------------------------------


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

library work;
use work.genram_pkg.all;
use work.memory_loader_pkg.all;

entity generic_dpram is

  generic (
    -- standard parameters
    g_data_width               : natural;
    g_size                     : natural;
    g_with_byte_enable         : boolean := false;
    g_addr_conflict_resolution : string := "read_first";
    g_init_file                : string := "";
    g_dual_clock               : boolean := true;
    g_fail_if_file_not_found : boolean := true
    );

  port (
    rst_n_i : in std_logic := '1';      -- synchronous reset, active LO

    -- Port A
    clka_i : in  std_logic;
    bwea_i : in  std_logic_vector((g_data_width+7)/8-1 downto 0);
    wea_i  : in  std_logic;
    aa_i   : in  std_logic_vector(f_log2_size(g_size)-1 downto 0);
    da_i   : in  std_logic_vector(g_data_width-1 downto 0);
    qa_o   : out std_logic_vector(g_data_width-1 downto 0);
    -- Port B

    clkb_i : in  std_logic;
    bweb_i : in  std_logic_vector((g_data_width+7)/8-1 downto 0);
    web_i  : in  std_logic;
    ab_i   : in  std_logic_vector(f_log2_size(g_size)-1 downto 0);
    db_i   : in  std_logic_vector(g_data_width-1 downto 0);
    qb_o   : out std_logic_vector(g_data_width-1 downto 0)
    );

end generic_dpram;

architecture syn of generic_dpram is

  constant c_num_bytes : integer := g_data_width/8;

  type t_ram_type is array(0 to g_size-1) of std_logic_vector(g_data_width-1 downto 0);
  type t_ram_word_bs is array (0 to 7) of std_logic_vector(7 downto 0);
  type t_ram_type_bs is array (0 to g_size - 1) of t_ram_word_bs;

  function f_memarray_to_ramtype(arr : t_meminit_array) return t_ram_type is
    variable tmp    : t_ram_type;
    variable n, pos : integer;
  begin
    pos := 0;
    while(pos < g_size)loop
      n := 0;
      -- avoid ISE loop iteration limit
      while (pos < g_size and n < 4096) loop
        for i in 0 to g_data_width-1 loop
          tmp(pos)(i) := arr(pos, i);
        end loop;  -- i
        n := n+1;
        pos := pos + 1;
      end loop;
      pos := pos + 1;
    end loop;
    return tmp;
  end f_memarray_to_ramtype;

  function f_memarray_to_ramtype_bs(arr : t_meminit_array) return t_ram_type_bs is
    variable tmp    : t_ram_type_bs;
    variable n, pos : integer;
  begin
    pos := 0;
    while(pos < g_size)loop
      n := 0;
      -- avoid ISE loop iteration limit
      while (pos < g_size and n < 4096) loop
        for i in 0 to g_data_width-1 loop
          tmp(pos)(i/8)(i mod 8) := arr(pos, i);
        end loop;  -- i
        n := n+1;
        pos := pos + 1;
      end loop;
      pos := pos + 1;
    end loop;
    return tmp;
  end f_memarray_to_ramtype_bs;


  shared variable ram : t_ram_type := f_memarray_to_ramtype(
    f_load_mem_from_file(g_init_file, g_size, g_data_width, g_fail_if_file_not_found));

  shared variable ram_bs : t_ram_type_bs:=f_memarray_to_ramtype_bs(
    f_load_mem_from_file(g_init_file, g_size, g_data_width, g_fail_if_file_not_found));
  signal q_local_a       : t_ram_word_bs;
  signal q_local_b       : t_ram_word_bs;

  signal bwe_int_a : std_logic_vector(7 downto 0);
  signal bwe_int_b : std_logic_vector(7 downto 0);

  signal clka_int : std_logic;
  signal clkb_int : std_logic;
  
begin
  assert (g_addr_conflict_resolution = "read_first")
    report "generic_dpram: Altera template supports only read-first mode." severity failure;
  assert (((g_data_width / 8) * 8) = g_data_width) or (g_with_byte_enable = false)
    report "generic_dpram: in byte-enabled mode the data width must be a multiple of 8" severity failure;
  assert(g_data_width <= 64 or g_with_byte_enable = false)
    report "generic_dpram: byte-selectable memories can be have 64-bit data width due to synthesis tool limitation" severity failure;



  --gen_single_clock : if(g_dual_clock = false) generate
  --  clka_int <= clka_i  after 1ns;
  --  clkb_int <= clka_i  after 1ns;
  --end generate gen_single_clock;

  --gen_dual_clock : if(g_dual_clock = true) generate
  --  clka_int <= clka_i  after 1ns;
  --  clkb_int <= clkb_i after 1ns;
  --end generate gen_dual_clock;



  gen_with_byte_enable : if(g_with_byte_enable = true) generate
  bwe_int_a <= std_logic_vector(to_unsigned(0, 8-bwea_i'length)) & bwea_i;
  bwe_int_b <= std_logic_vector(to_unsigned(0, 8-bweb_i'length)) & bweb_i;

    unpack : for i in 0 to c_num_bytes - 1 generate
      qa_o(8*(i+1) - 1 downto 8*i) <= q_local_a(i);
      qb_o(8*(i+1) - 1 downto 8*i) <= q_local_b(i);
    end generate unpack;

    process(clka_i)
    begin
      if(rising_edge(clka_i)) then
        if(wea_i = '1') then

-- I know the code below is stupid, but it's the only way to make Quartus
-- recongnize it as a memory block
          if(bwe_int_a(0) = '1' and g_data_width >= 8) then
            ram_bs(to_integer(unsigned(aa_i)))(0) := da_i(7 downto 0);
          end if;
          if(bwe_int_a(1) = '1' and g_data_width >= 16) then
            ram_bs(to_integer(unsigned(aa_i)))(1) := da_i(15 downto 8);
          end if;
          if(bwe_int_a(2) = '1' and g_data_width >= 24) then
            ram_bs(to_integer(unsigned(aa_i)))(2) := da_i(23 downto 16);
          end if;
          if(bwe_int_a(3) = '1' and g_data_width >= 32) then
            ram_bs(to_integer(unsigned(aa_i)))(3) := da_i(31 downto 24);
          end if;
          if(bwe_int_a(4) = '1' and g_data_width >= 40) then
            ram_bs(to_integer(unsigned(aa_i)))(4) := da_i(39 downto 32);
          end if;
          if(bwe_int_a(5) = '1' and g_data_width >= 48) then
            ram_bs(to_integer(unsigned(aa_i)))(5) := da_i(47 downto 40);
          end if;
          if(bwe_int_a(6) = '1' and g_data_width >= 56) then
            ram_bs(to_integer(unsigned(aa_i)))(6) := da_i(55 downto 48);
          end if;
          if(bwe_int_a(7) = '1' and g_data_width = 64) then
            ram_bs(to_integer(unsigned(aa_i)))(7) := da_i(64 downto 57);
          end if;
        end if;
        q_local_a <= ram_bs(to_integer(unsigned(aa_i)));
      end if;
    end process;

    process(clkb_i)
    begin
      if(rising_edge(clkb_i)) then
        if(web_i = '1') then

-- I know the code below is stupid, but it's the only way to make Quartus
-- recongnize it as a memory block
          if(bwe_int_b(0) = '1' and g_data_width >= 8) then
            ram_bs(to_integer(unsigned(ab_i)))(0) := db_i(7 downto 0);
          end if;
          if(bwe_int_b(1) = '1' and g_data_width >= 16) then
            ram_bs(to_integer(unsigned(ab_i)))(1) := db_i(15 downto 8);
          end if;
          if(bwe_int_b(2) = '1' and g_data_width >= 24) then
            ram_bs(to_integer(unsigned(ab_i)))(2) := db_i(23 downto 16);
          end if;
          if(bwe_int_b(3) = '1' and g_data_width >= 32) then
            ram_bs(to_integer(unsigned(ab_i)))(3) := db_i(31 downto 24);
          end if;
          if(bwe_int_b(4) = '1' and g_data_width >= 40) then
            ram_bs(to_integer(unsigned(ab_i)))(4) := db_i(39 downto 32);
          end if;
          if(bwe_int_b(5) = '1' and g_data_width >= 48) then
            ram_bs(to_integer(unsigned(ab_i)))(5) := db_i(47 downto 40);
          end if;
          if(bwe_int_b(6) = '1' and g_data_width >= 56) then
            ram_bs(to_integer(unsigned(ab_i)))(6) := db_i(55 downto 48);
          end if;
          if(bwe_int_b(7) = '1' and g_data_width = 64) then
            ram_bs(to_integer(unsigned(ab_i)))(7) := db_i(64 downto 57);
          end if;
        end if;
        q_local_b <= ram_bs(to_integer(unsigned(ab_i)));
      end if;
    end process;


  end generate gen_with_byte_enable;


  gen_without_byte_enable_readfirst : if(g_with_byte_enable = false) generate
    process(clka_i)
    begin
      if rising_edge(clka_i) then
        if(wea_i = '1') then
          ram(to_integer(unsigned(aa_i))) := da_i;
        end if;
        qa_o <= ram(to_integer(unsigned(aa_i)));
      end if;
    end process;

    process(clkb_i)
    begin
      if rising_edge(clkb_i) then
        if(web_i = '1') then
          ram(to_integer(unsigned(ab_i))) := db_i;
        end if;
        qb_o <= ram(to_integer(unsigned(ab_i)));
      end if;
    end process;
  end generate gen_without_byte_enable_readfirst;
  

end syn;