--- /dev/null
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.std_logic_arith.all;
+use ieee.std_logic_unsigned.all;
+
+library UNISIM;
+use UNISIM.vcomponents.all;
+
+--------------------------------------------------------------------------------
+-- Generator of synchronous memory with generic size and width of address and
+-- data buses made up of RAMB primitives.
+--
+-- There is no special relation between SIZE and ADDR_WIDTH parameters. Both
+-- values are independent of each other.
+--
+-- Currently basic building element are: RAMB16_S9, RAMB_S4 and RAMB_S2
+-- primitives (which are preset at least in Virtex-II/II-Pro and Spartan-3/3E
+-- devices). If your device doesn't contain one of these memory primitives, the
+-- only thing you have to do is to add simple generate statement of your
+-- primitive into the BLOCKS statement and add record about this new memory
+-- primitive to the BRAM_TYPE_ARRAY.
+--
+-- Resulting memory is composed of regular array of one memory primitive type
+-- which must be specified by BRAM_TYPE generic parameter. One iteration of
+-- BLOCKS generate statement produces one row in memory array which is called
+-- BLOCK. One iteration of generate statement in BLOCKS (named after memory
+-- primitive) produces one memory primitive in memory array.
+--
+-- Input ports "we" and "din" have default zero values. So it's not required
+-- to connect them.
+--
+--
+-- Memory primitives naming convention is following:
+--
+-- <path to component>/BLOCKS[<row num>].<mem primitive name>.B[<col num>].BRAM
+--
+-- Higher data bits -> higher <col num>
+-- Higher address -> higher <row num>
+-- <mem primitive name> is name specified in BRAM_TYPE parameter.
+--
+--
+-- Example of Block RAM Memory Map (*.bmm) file:
+--
+-- // ram_generic: SIZE=16kB, WIDTH=8b, BRAM_TYPE=RAMB16_S2
+-- ADDRESS_SPACE blockrom RAMB16 [0x0000:0x3fff]
+-- BUS_BLOCK
+-- <path>/BLOCKS[0].RAMB16_S2.B[3].BRAM [7:6];
+-- <path>/BLOCKS[0].RAMB16_S2.B[2].BRAM [5:4];
+-- <path>/BLOCKS[0].RAMB16_S2.B[1].BRAM [3:2];
+-- <path>/BLOCKS[0].RAMB16_S2.B[0].BRAM [1:0];
+-- END_BUS_BLOCK;
+-- BUS_BLOCK
+-- <path>/BLOCKS[1].RAMB16_S2.B[3].BRAM [7:6];
+-- <path>/BLOCKS[1].RAMB16_S2.B[2].BRAM [5:4];
+-- <path>/BLOCKS[1].RAMB16_S2.B[1].BRAM [3:2];
+-- <path>/BLOCKS[1].RAMB16_S2.B[0].BRAM [1:0];
+-- END_BUS_BLOCK;
+-- END_ADDRESS_SPACE;
+--------------------------------------------------------------------------------
+
+entity ram_generic is
+ generic (
+ BRAM_TYPE : string := "RAMB16_S9"; -- Memory primitive used
+ SIZE : integer := 8*1024; -- Physical size in bytes
+ ADDR_WIDTH : integer := 16; -- Width of address port
+ DATA_WIDTH : integer := 8; -- Width of data port
+ NEG_EN : boolean := false; -- Low active "enable"
+ NEG_WE : boolean := false); -- Low active "write enable"
+ port (
+ clk : in std_logic;
+ addr : in std_logic_vector (ADDR_WIDTH-1 downto 0);
+ en : in std_logic;
+ we : in std_logic := '0';
+ din : in std_logic_vector (DATA_WIDTH-1 downto 0) := conv_std_logic_vector(0, DATA_WIDTH);
+ dout : out std_logic_vector (DATA_WIDTH-1 downto 0));
+end entity ram_generic;
+
+--------------------------------------------------------------------------------
+
+architecture behavioral of ram_generic is
+
+ type bram_type_t is record
+ name : string (1 to 15);
+ name_width : integer;
+ addr_width : integer;
+ data_width : integer;
+ end record bram_type_t;
+
+ type bram_type_array_t is array (natural range <>) of bram_type_t;
+
+ constant BRAM_TYPE_ARRAY : bram_type_array_t := (
+ ("RAMB16_S9 ", 9,11, 8),
+ ("RAMB16_S4 ", 9,12, 4),
+ ("RAMB16_S2 ", 9,13, 2));
+
+ impure function select_bram_type (constant name : string) return bram_type_t is
+ variable result_exists : boolean;
+ variable result_index : integer;
+ begin
+ for i in BRAM_TYPE_ARRAY'range loop
+ if BRAM_TYPE_ARRAY(i).name(1 to BRAM_TYPE_ARRAY(i).name_width) = name then
+ result_exists := true;
+ result_index := i;
+ end if;
+ end loop;
+
+ if not result_exists then
+ assert false
+ report "Block RAM type """ & BRAM_TYPE & """ is unsupported!"
+ severity ERROR;
+ end if;
+
+ return bram_type_array(result_index);
+ end function select_bram_type;
+
+
+ constant BRAM_USED : bram_type_t := select_bram_type(BRAM_TYPE);
+ constant BRAM_DATA_WIDTH : integer := BRAM_USED.data_width;
+ constant BRAM_PER_BLOCK : integer := ((DATA_WIDTH - 1) / BRAM_DATA_WIDTH) + 1;
+
+ constant BLOCK_ADDR_WIDTH : integer := BRAM_USED.addr_width;
+ constant BLOCK_SIZE : integer := 2**BLOCK_ADDR_WIDTH;
+ constant BLOCK_COUNT : integer := ((SIZE - 1) / BLOCK_SIZE) + 1;
+
+
+ subtype BLOCK_RANGE is natural range BLOCK_COUNT-1 downto 0;
+ subtype BRAM_RANGE is natural range BRAM_PER_BLOCK-1 downto 0;
+
+ type block_data_t is array (BRAM_RANGE) of
+ std_logic_vector (BRAM_DATA_WIDTH-1 downto 0);
+
+
+ type data_array_t is array (BLOCK_RANGE) of block_data_t;
+ type ctrl_array_t is array (BLOCK_RANGE) of std_logic;
+
+
+ signal dout_array : data_array_t;
+ signal din_array : block_data_t;
+ signal en_array : ctrl_array_t;
+ signal we_array : ctrl_array_t;
+
+ signal block_addr : std_logic_vector (BLOCK_ADDR_WIDTH-1 downto 0);
+ signal block_index : integer;
+ signal block_index_reg : integer;
+
+ signal inter_en : std_logic;
+ signal inter_we : std_logic;
+
+--------------------------------------------------------------------------------
+
+begin
+
+ -- Adjust input control signals according to NEG_EN and NEG_WE generic
+ -- parameters.
+ SIGNALS_LEVEL : process (en, we) is
+ begin
+ if NEG_EN then
+ inter_en <= not en;
+ else
+ inter_en <= en;
+ end if;
+
+ if NEG_WE then
+ inter_we <= not we;
+ else
+ inter_we <= we;
+ end if;
+ end process;
+
+
+ -- Decode and latch address of BLOCK (i.e. RAM row).
+ BLOCK_INDEX_DECODER : process (clk, addr) is
+ begin
+ block_index <= conv_integer(addr) / BLOCK_SIZE;
+
+ if rising_edge(clk) and inter_en = '1' then
+ block_index_reg <= block_index;
+ end if;
+ end process;
+
+
+ -- Address decoder which produces control signals and data inputs for all
+ -- blocks and RAM columns and multiplexes data outputs from all RAMs.
+ DECODER : process (addr, din, inter_en, inter_we) is
+ variable dout_var : std_logic_vector (BRAM_PER_BLOCK*BRAM_DATA_WIDTH-1 downto 0);
+ variable din_var : std_logic_vector (BRAM_PER_BLOCK*BRAM_DATA_WIDTH-1 downto 0);
+ begin
+ -- decode address to single block RAM
+ if addr'HIGH <= block_addr'HIGH then
+ -- when logic address space is smaller then or equal to 1 BLOCK size
+ block_addr <= (others => '0');
+ block_addr (addr'RANGE) <= addr;
+ else
+ -- when logic address space is bigger then 1 BLOCK size
+ block_addr <= addr (block_addr'RANGE);
+ end if;
+
+ -- split "din" to data inputs of appropriate RAM columns
+ din_var := (others => '0');
+ din_var (din'RANGE) := din;
+
+ for i in BRAM_RANGE loop
+ din_array (i) <= din_var ((i+1)*BRAM_DATA_WIDTH-1 downto i*BRAM_DATA_WIDTH);
+ end loop;
+
+ -- concatenate data outputs of appropriate RAMs into "dout".
+ dout <= (others => '0');
+
+ if block_index_reg < BLOCK_COUNT then
+ for i in BRAM_RANGE loop
+ dout_var := dout_var & dout_array (block_index_reg)(i);
+ end loop;
+
+ dout <= dout_var (dout'RANGE);
+ end if;
+
+ -- produce "en" and "we" signals for all RAMs.
+ en_array <= (others => inter_en);
+
+ we_array <= (others => '0');
+
+ if block_index < BLOCK_COUNT then
+ we_array (block_index) <= inter_we;
+ end if;
+ end process;
+
+
+ -- Generation of RAMB array. Each iteration produces memory block with full
+ -- width and length dependent on used memory primitive. Length of one block
+ -- is computed from BLOCK_ADDR_WIDTH constant.
+ BLOCKS : for i in 0 to BLOCK_COUNT-1 generate
+ RAMB16_S9 : if BRAM_TYPE = "RAMB16_S9" generate
+ B : for j in BRAM_RANGE generate
+ BRAM : RAMB16_S9
+ port map (
+ clk => clk,
+ addr => block_addr,
+ en => en_array (i),
+ we => we_array (i),
+ di => din_array (j),
+ do => dout_array (i)(j),
+ dip => "0",
+ dop => open,
+ ssr => '0');
+ end generate;
+ end generate;
+
+ RAMB16_S4 : if BRAM_TYPE = "RAMB16_S4" generate
+ B : for j in BRAM_RANGE generate
+ BRAM : RAMB16_S4
+ port map (
+ clk => clk,
+ addr => block_addr,
+ en => en_array (i),
+ we => we_array (i),
+ di => din_array (j),
+ do => dout_array (i)(j),
+ ssr => '0');
+ end generate;
+ end generate;
+
+ RAMB16_S2 : if BRAM_TYPE = "RAMB16_S2" generate
+ B : for j in BRAM_RANGE generate
+ BRAM : RAMB16_S2
+ port map (
+ clk => clk,
+ addr => block_addr,
+ en => en_array (i),
+ we => we_array (i),
+ di => din_array (j),
+ do => dout_array (i)(j),
+ ssr => '0');
+ end generate;
+ end generate;
+ end generate;
+
+end architecture behavioral;
+