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: -- -- /BLOCKS[]..B[].BRAM -- -- Higher data bits -> higher -- Higher address -> higher -- 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 -- /BLOCKS[0].RAMB16_S2.B[3].BRAM [7:6]; -- /BLOCKS[0].RAMB16_S2.B[2].BRAM [5:4]; -- /BLOCKS[0].RAMB16_S2.B[1].BRAM [3:2]; -- /BLOCKS[0].RAMB16_S2.B[0].BRAM [1:0]; -- END_BUS_BLOCK; -- BUS_BLOCK -- /BLOCKS[1].RAMB16_S2.B[3].BRAM [7:6]; -- /BLOCKS[1].RAMB16_S2.B[2].BRAM [5:4]; -- /BLOCKS[1].RAMB16_S2.B[1].BRAM [3:2]; -- /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;