diff --git a/libraries/base/common/src/vhdl/common_paged_ram_cr_cw.vhd b/libraries/base/common/src/vhdl/common_paged_ram_cr_cw.vhd new file mode 100644 index 0000000000000000000000000000000000000000..6deefedd86c140de86d38e5014d945099e4078c9 --- /dev/null +++ b/libraries/base/common/src/vhdl/common_paged_ram_cr_cw.vhd @@ -0,0 +1,317 @@ +-- ----------------------------------------------------------------------------- +-- +-- Copyright 2023 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- 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. +-- +-- ----------------------------------------------------------------------------- +-- +-- Author: +-- D.F. Brouwer +-- Purpose: +-- Multi page memory with seperate clock and address per port with single wr +-- and single rd +-- Description: +-- When *_next_page pulses then the next access will occur in the next page. +-- Remarks: +-- . There are three architecture variants (default use "use_adr"): +-- . use_mux : Use multiplexer logic and one RAM per page +-- . use_adr : Use MSbit address lines and one buf RAM for all pages +-- . use_ofs : Use address offset adders and one buf RAM for all pages +-- . The "use_mux" variant requires the multiplexer logic but can be more +-- efficient regarding RAM usage than the "use_adr" variant. +-- The "use_ofs" variant requires address adder logic, but is optimal +-- regarding RAM usage in case the page size is not a power of 2, because the +-- pages are then mapped at subsequent addresses in the buf RAM. +-- . The "use_adr" variant is optimal for speed, so that is set as default. +-- +-- . The crw_crw RAM covers all other variants, which were utilized by other +-- common RAM variant files. However, because the crw_crw IP is no longer +-- supported as it was previously used for previous FPGA technology identifiers +-- (device types) by the Agilex 7 (agi027_xxxx), the individual IPs should be +-- used. As a result, this file has been created. [1] +-- Reference: +-- [1] Based on the architecture of common_paged_ram_crw_crw.vhd. + +library IEEE, technology_lib; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +library common_lib; +use work.common_pkg.all; +use work.common_mem_pkg.all; +use technology_lib.technology_select_pkg.all; + +entity common_paged_ram_cr_cw is + generic ( + g_technology : natural := c_tech_select_default; + g_str : string := "use_adr"; + g_data_w : natural; + g_nof_pages : natural := 2; -- >= 2 + g_page_sz : natural; + g_wr_start_page : natural := 0; + g_rd_start_page : natural := 0; + g_rd_latency : natural := 1 + ); + port ( + -- Write port clock domain + wr_rst : in std_logic; + wr_clk : in std_logic; + wr_clken : in std_logic := '1'; + wr_next_page : in std_logic; + wr_adr : in std_logic_vector(ceil_log2(g_page_sz) - 1 downto 0) := (others => '0'); + wr_en : in std_logic := '0'; + wr_dat : in std_logic_vector(g_data_w - 1 downto 0) := (others => '0'); + -- Read port clock domain + rd_rst : in std_logic; + rd_clk : in std_logic; + rd_clken : in std_logic := '1'; + rd_next_page : in std_logic; + rd_adr : in std_logic_vector(ceil_log2(g_page_sz) - 1 downto 0) := (others => '0'); + rd_en : in std_logic := '1'; + rd_dat : out std_logic_vector(g_data_w - 1 downto 0); + rd_val : out std_logic + ); +end common_paged_ram_cr_cw; + +architecture rtl of common_paged_ram_cr_cw is + type t_page_sel_arr is array (integer range <>) of natural range 0 to g_nof_pages - 1; + + constant c_page_addr_w : natural := ceil_log2(g_page_sz); + + -- g_str = "use_mux" : + constant c_page_ram : t_c_mem := (latency => g_rd_latency, + adr_w => c_page_addr_w, + dat_w => g_data_w, + nof_dat => g_page_sz, + init_sl => '0'); + + type t_data_arr is array (integer range <>) of std_logic_vector(g_data_w - 1 downto 0); + + -- g_str = "use_adr" : + constant c_mem_nof_pages_w : natural := true_log2(g_nof_pages); + constant c_mem_addr_w : natural := c_mem_nof_pages_w + c_page_addr_w; + constant c_mem_nof_words : natural := g_nof_pages * 2**c_page_addr_w; -- <= 2**c_mem_addr_w + + constant c_mem_ram : t_c_mem := (latency => g_rd_latency, + adr_w => c_mem_addr_w, + dat_w => g_data_w, + nof_dat => c_mem_nof_words, + init_sl => '0'); + + -- g_str = "use_ofs" : + constant c_buf_addr_w : natural := ceil_log2(g_nof_pages * g_page_sz); + constant c_buf_nof_words : natural := g_nof_pages * g_page_sz; + + constant c_buf_ram : t_c_mem := (latency => g_rd_latency, + adr_w => c_buf_addr_w, + dat_w => g_data_w, + nof_dat => c_buf_nof_words, + init_sl => '0'); + + -- >>> Page control + + -- g_str = "use_mux" and g_str = "use_adr" : + -- . use page_sel direct for wr_en, rd_en, and address + signal page_sel_wr : natural range 0 to g_nof_pages - 1; + signal nxt_page_sel_wr : natural; + signal page_sel_rd : natural range 0 to g_nof_pages - 1; + signal nxt_page_sel_rd : natural; + + -- . use page_sel_dly to adjust for g_rd_latency of rd_dat and rd_val + signal page_sel_wr_dly : t_page_sel_arr(0 to g_rd_latency - 1); + signal nxt_page_sel_wr_dly : t_page_sel_arr(0 to g_rd_latency - 1); + signal page_sel_rd_dly : t_page_sel_arr(0 to g_rd_latency - 1); + signal nxt_page_sel_rd_dly : t_page_sel_arr(0 to g_rd_latency - 1); + + -- g_str = "use_ofs" : + signal page_ofs_wr : natural range 0 to c_buf_nof_words - 1; + signal nxt_page_ofs_wr : natural; + signal page_ofs_rd : natural range 0 to c_buf_nof_words - 1; + signal nxt_page_ofs_rd : natural; + + -- >>> Access control + + -- g_str = "use_mux" : + signal page_wr_en : std_logic_vector(0 to g_nof_pages - 1); + signal page_wr_dat : t_data_arr(0 to g_nof_pages - 1); + signal page_rd_en : std_logic_vector(0 to g_nof_pages - 1); + signal page_rd_dat : t_data_arr(0 to g_nof_pages - 1); + signal page_rd_val : std_logic_vector(0 to g_nof_pages - 1); + + -- g_str = "use_adr" : + signal mem_wr_adr : std_logic_vector(c_mem_addr_w - 1 downto 0); + signal mem_rd_adr : std_logic_vector(c_mem_addr_w - 1 downto 0); + + -- g_str = "use_ofs" : + signal buf_wr_adr : std_logic_vector(c_buf_addr_w - 1 downto 0); + signal buf_rd_adr : std_logic_vector(c_buf_addr_w - 1 downto 0); +begin + -- page select (for all) and page address offset (for use_ofs) + p_reg_wr : process (wr_rst, wr_clk) + begin + if wr_rst = '1' then + page_sel_wr <= g_wr_start_page; + page_sel_wr_dly <= (others => g_wr_start_page); + page_ofs_wr <= g_wr_start_page * g_page_sz; + elsif rising_edge(wr_clk) then + page_sel_wr <= nxt_page_sel_wr; + page_sel_wr_dly <= nxt_page_sel_wr_dly; + page_ofs_wr <= nxt_page_ofs_wr; + end if; + end process; + + p_reg_rd : process (rd_rst, rd_clk) + begin + if rd_rst = '1' then + page_sel_rd <= g_rd_start_page; + page_sel_rd_dly <= (others => g_rd_start_page); + page_ofs_rd <= g_rd_start_page * g_page_sz; + elsif rising_edge(rd_clk) then + page_sel_rd <= nxt_page_sel_rd; + page_sel_rd_dly <= nxt_page_sel_rd_dly; + page_ofs_rd <= nxt_page_ofs_rd; + end if; + end process; + + nxt_page_sel_wr_dly(0) <= page_sel_wr; + nxt_page_sel_wr_dly(1 to g_rd_latency - 1) <= page_sel_wr_dly(0 to g_rd_latency - 2); + nxt_page_sel_rd_dly(0) <= page_sel_rd; + nxt_page_sel_rd_dly(1 to g_rd_latency - 1) <= page_sel_rd_dly(0 to g_rd_latency - 2); + + p_wr_next_page : process(wr_next_page, page_sel_wr, page_ofs_wr) + begin + nxt_page_sel_wr <= page_sel_wr; + nxt_page_ofs_wr <= page_ofs_wr; + if wr_next_page = '1' then + if page_sel_wr < g_nof_pages - 1 then + nxt_page_sel_wr <= page_sel_wr + 1; + nxt_page_ofs_wr <= page_ofs_wr + g_page_sz; + else + nxt_page_sel_wr <= 0; + nxt_page_ofs_wr <= 0; + end if; + end if; + end process; + + p_rd_next_page : process(rd_next_page, page_sel_rd, page_ofs_rd) + begin + nxt_page_sel_rd <= page_sel_rd; + nxt_page_ofs_rd <= page_ofs_rd; + if rd_next_page = '1' then + if page_sel_rd < g_nof_pages - 1 then + nxt_page_sel_rd <= page_sel_rd + 1; + nxt_page_ofs_rd <= page_ofs_rd + g_page_sz; + else + nxt_page_sel_rd <= 0; + nxt_page_ofs_rd <= 0; + end if; + end if; + end process; + + gen_mux : if g_str = "use_mux" generate + gen_pages : for I in 0 to g_nof_pages - 1 generate + u_ram : entity work.common_ram_cr_cw + generic map ( + g_technology => g_technology, + g_ram => c_page_ram, + g_init_file => "UNUSED" + ) + port map ( + wr_rst => wr_rst, + wr_clk => wr_clk, + wr_clken => wr_clken, + rd_rst => rd_rst, + rd_clk => rd_clk, + rd_clken => rd_clken, + wr_adr => wr_adr, + wr_en => page_wr_en(I), + wr_dat => wr_dat, + rd_adr => rd_adr, + rd_en => page_rd_en(I), + rd_dat => page_rd_dat(I), + rd_val => page_rd_val(I) + ); + end generate; + + p_mux : process(page_sel_wr, wr_en, page_sel_wr_dly, page_sel_rd, + rd_en, page_sel_rd_dly, page_rd_dat, page_rd_val) + begin + -- use page_sel direct for control + page_wr_en <= (others => '0'); + page_rd_en <= (others => '0'); + page_wr_en(page_sel_wr) <= wr_en; + page_rd_en(page_sel_rd) <= rd_en; + + -- use page_sel_dly to account for the RAM read latency + rd_dat <= page_rd_dat(page_sel_rd_dly(g_rd_latency - 1)); + rd_val <= page_rd_val(page_sel_rd_dly(g_rd_latency - 1)); + end process; + end generate; -- gen_mux + + gen_adr : if g_str = "use_adr" generate + u_mem : entity work.common_ram_cr_cw + generic map ( + g_technology => g_technology, + g_ram => c_mem_ram, + g_init_file => "UNUSED" + ) + port map ( + wr_rst => wr_rst, + wr_clk => wr_clk, + wr_clken => wr_clken, + rd_rst => rd_rst, + rd_clk => rd_clk, + rd_clken => rd_clken, + wr_adr => mem_wr_adr, + wr_en => wr_en, + wr_dat => wr_dat, + rd_adr => mem_rd_adr, + rd_en => rd_en, + rd_dat => rd_dat, + rd_val => rd_val + ); + + mem_wr_adr <= TO_UVEC(page_sel_wr, c_mem_nof_pages_w) & wr_adr; + mem_rd_adr <= TO_UVEC(page_sel_rd, c_mem_nof_pages_w) & rd_adr; + end generate; -- gen_adr + + gen_ofs : if g_str = "use_ofs" generate + u_buf : entity work.common_ram_cr_cw + generic map ( + g_technology => g_technology, + g_ram => c_buf_ram, + g_init_file => "UNUSED" + ) + port map ( + wr_rst => wr_rst, + wr_clk => wr_clk, + wr_clken => wr_clken, + rd_rst => rd_rst, + rd_clk => rd_clk, + rd_clken => rd_clken, + wr_adr => buf_wr_adr, + wr_en => wr_en, + wr_dat => wr_dat, + rd_adr => buf_rd_adr, + rd_en => rd_en, + rd_dat => rd_dat, + rd_val => rd_val + ); + + buf_wr_adr <= INCR_UVEC(RESIZE_UVEC(wr_adr, c_buf_addr_w), page_ofs_wr); + buf_rd_adr <= INCR_UVEC(RESIZE_UVEC(rd_adr, c_buf_addr_w), page_ofs_rd); + end generate; -- gen_ofs + +end rtl;