From df1ca59d7fb07786067b8b7934109634dfb97cd4 Mon Sep 17 00:00:00 2001 From: Vladimir Burian Date: Thu, 10 Mar 2011 13:29:28 +0100 Subject: [PATCH] Add custom generics RAM. This generics entity produces array of block RAM primitives with appropriate address and data decoders. The naming convention of block RAM primitives is defined and it's simply to write correct *.bmm file. --- memory/ram_generic.vhd | 278 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 memory/ram_generic.vhd diff --git a/memory/ram_generic.vhd b/memory/ram_generic.vhd new file mode 100644 index 0000000..f1570d2 --- /dev/null +++ b/memory/ram_generic.vhd @@ -0,0 +1,278 @@ +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; + -- 2.39.2