diff --git a/libraries/base/common/src/vhdl/common_paged_ram_rw_rw.vhd b/libraries/base/common/src/vhdl/common_paged_ram_rw_rw.vhd index 22dc9992af4ac0225fe7caa13e1f6f108312a1b3..4e4405a92ab196a72c53327c42d766a1e572ca85 100644 --- a/libraries/base/common/src/vhdl/common_paged_ram_rw_rw.vhd +++ b/libraries/base/common/src/vhdl/common_paged_ram_rw_rw.vhd @@ -1,48 +1,70 @@ -------------------------------------------------------------------------------- +-- ----------------------------------------------------------------------------- -- --- Copyright (C) 2011 +-- Copyright 2011-2023 -- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> -- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands -- --- This program is free software: you can redistribute it and/or modify --- it under the terms of the GNU General Public License as published by --- the Free Software Foundation, either version 3 of the License, or --- (at your option) any later version. +-- 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 -- --- This program is distributed in the hope that it will be useful, --- but WITHOUT ANY WARRANTY; without even the implied warranty of --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --- GNU General Public License for more details. +-- http://www.apache.org/licenses/LICENSE-2.0 -- --- You should have received a copy of the GNU General Public License --- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- 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. -- -------------------------------------------------------------------------------- - --- Purpose: Multi page memory +-- ----------------------------------------------------------------------------- +-- +-- Author: +-- - +-- Changed by: +-- D.F. Brouwer +-- Purpose: +-- Multi page memory -- Description: -- When next_page_* pulses then the next access will occur in the next page. -- Remarks: --- . See common_paged_ram_crw_crw for details. +-- . 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 rw_rw IP should be used. +-- As a result, this file has been modified. [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_rw_rw 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_start_page_a : natural := 0; - g_start_page_b : natural := 0; - g_rd_latency : natural := 1; - g_true_dual_port : boolean := true + 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_start_page_a : natural := 0; + g_start_page_b : natural := 0; + g_rd_latency : natural := 1; + g_true_dual_port : boolean := true ); port ( rst : in std_logic; @@ -65,40 +87,254 @@ entity common_paged_ram_rw_rw is ); end common_paged_ram_rw_rw; -architecture str of common_paged_ram_rw_rw is +architecture rtl of common_paged_ram_rw_rw 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_a : natural range 0 to g_nof_pages - 1; + signal nxt_page_sel_a : natural; + signal page_sel_b : natural range 0 to g_nof_pages - 1; + signal nxt_page_sel_b : natural; + + -- . use page_sel_dly to adjust for g_rd_latency of rd_dat and rd_val + signal page_sel_a_dly : t_page_sel_arr(0 to g_rd_latency - 1); + signal nxt_page_sel_a_dly : t_page_sel_arr(0 to g_rd_latency - 1); + signal page_sel_b_dly : t_page_sel_arr(0 to g_rd_latency - 1); + signal nxt_page_sel_b_dly : t_page_sel_arr(0 to g_rd_latency - 1); + + -- g_str = "use_ofs" : + signal page_ofs_a : natural range 0 to c_buf_nof_words - 1; + signal nxt_page_ofs_a : natural; + signal page_ofs_b : natural range 0 to c_buf_nof_words - 1; + signal nxt_page_ofs_b : natural; + + -- >>> Access control + + -- g_str = "use_mux" : + signal page_wr_en_a : std_logic_vector(0 to g_nof_pages - 1); + signal page_wr_dat_a : t_data_arr(0 to g_nof_pages - 1); + signal page_rd_en_a : std_logic_vector(0 to g_nof_pages - 1); + signal page_rd_dat_a : t_data_arr(0 to g_nof_pages - 1); + signal page_rd_val_a : std_logic_vector(0 to g_nof_pages - 1); + + signal page_wr_en_b : std_logic_vector(0 to g_nof_pages - 1); + signal page_wr_dat_b : t_data_arr(0 to g_nof_pages - 1); + signal page_rd_en_b : std_logic_vector(0 to g_nof_pages - 1); + signal page_rd_dat_b : t_data_arr(0 to g_nof_pages - 1); + signal page_rd_val_b : std_logic_vector(0 to g_nof_pages - 1); + + -- g_str = "use_adr" : + signal mem_adr_a : std_logic_vector(c_mem_addr_w - 1 downto 0); + signal mem_adr_b : std_logic_vector(c_mem_addr_w - 1 downto 0); + + -- g_str = "use_ofs" : + signal buf_adr_a : std_logic_vector(c_buf_addr_w - 1 downto 0); + signal buf_adr_b : std_logic_vector(c_buf_addr_w - 1 downto 0); begin - u_crw_crw : entity work.common_paged_ram_crw_crw - generic map ( - g_technology => g_technology, - g_str => g_str, - g_data_w => g_data_w, - g_nof_pages => g_nof_pages, - g_page_sz => g_page_sz, - g_start_page_a => g_start_page_a, - g_start_page_b => g_start_page_b, - g_rd_latency => g_rd_latency, - g_true_dual_port => g_true_dual_port - ) - port map ( - rst_a => rst, - rst_b => rst, - clk_a => clk, - clk_b => clk, - clken_a => clken, - clken_b => clken, - next_page_a => next_page_a, - adr_a => adr_a, - wr_en_a => wr_en_a, - wr_dat_a => wr_dat_a, - rd_en_a => rd_en_a, - rd_dat_a => rd_dat_a, - rd_val_a => rd_val_a, - next_page_b => next_page_b, - adr_b => adr_b, - wr_en_b => wr_en_b, - wr_dat_b => wr_dat_b, - rd_en_b => rd_en_b, - rd_dat_b => rd_dat_b, - rd_val_b => rd_val_b - ); -end str; + -- page select (for all) and page address offset (for use_ofs) + p_reg_a : process (rst, clk) + begin + if rst = '1' then + page_sel_a <= g_start_page_a; + page_sel_a_dly <= (others => g_start_page_a); + page_ofs_a <= g_start_page_a * g_page_sz; + elsif rising_edge(clk) then + page_sel_a <= nxt_page_sel_a; + page_sel_a_dly <= nxt_page_sel_a_dly; + page_ofs_a <= nxt_page_ofs_a; + end if; + end process; + + p_reg_b : process (rst, clk) + begin + if rst = '1' then + page_sel_b <= g_start_page_b; + page_sel_b_dly <= (others => g_start_page_b); + page_ofs_b <= g_start_page_b * g_page_sz; + elsif rising_edge(clk) then + page_sel_b <= nxt_page_sel_b; + page_sel_b_dly <= nxt_page_sel_b_dly; + page_ofs_b <= nxt_page_ofs_b; + end if; + end process; + + nxt_page_sel_a_dly(0) <= page_sel_a; + nxt_page_sel_a_dly(1 to g_rd_latency - 1) <= page_sel_a_dly(0 to g_rd_latency - 2); + nxt_page_sel_b_dly(0) <= page_sel_b; + nxt_page_sel_b_dly(1 to g_rd_latency - 1) <= page_sel_b_dly(0 to g_rd_latency - 2); + + p_next_page_a : process(next_page_a, page_sel_a, page_ofs_a) + begin + nxt_page_sel_a <= page_sel_a; + nxt_page_ofs_a <= page_ofs_a; + if next_page_a = '1' then + if page_sel_a < g_nof_pages - 1 then + nxt_page_sel_a <= page_sel_a + 1; + nxt_page_ofs_a <= page_ofs_a + g_page_sz; + else + nxt_page_sel_a <= 0; + nxt_page_ofs_a <= 0; + end if; + end if; + end process; + + p_next_page_b : process(next_page_b, page_sel_b, page_ofs_b) + begin + nxt_page_sel_b <= page_sel_b; + nxt_page_ofs_b <= page_ofs_b; + if next_page_b = '1' then + if page_sel_b < g_nof_pages - 1 then + nxt_page_sel_b <= page_sel_b + 1; + nxt_page_ofs_b <= page_ofs_b + g_page_sz; + else + nxt_page_sel_b <= 0; + nxt_page_ofs_b <= 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_rw_rw + generic map ( + g_technology => g_technology, + g_ram => c_page_ram, + g_init_file => "UNUSED", + g_true_dual_port => g_true_dual_port + ) + port map ( + rst => rst, + clk => clk, + clken => clken, + adr_a => adr_a, + wr_en_a => page_wr_en_a(I), + wr_dat_a => wr_dat_a, + rd_en_a => page_rd_en_a(I), + rd_dat_a => page_rd_dat_a(I), + rd_val_a => page_rd_val_a(I), + adr_b => adr_b, + wr_en_b => page_wr_en_b(I), + wr_dat_b => wr_dat_b, + rd_en_b => page_rd_en_b(I), + rd_dat_b => page_rd_dat_b(I), + rd_val_b => page_rd_val_b(I) + ); + end generate; + + p_mux : process(page_sel_a, wr_en_a, rd_en_a, page_sel_a_dly, page_rd_dat_a, page_rd_val_a, + page_sel_b, wr_en_b, rd_en_b, page_sel_b_dly, page_rd_dat_b, page_rd_val_b) + begin + -- use page_sel direct for control + page_wr_en_a <= (others => '0'); + page_wr_en_b <= (others => '0'); + page_rd_en_a <= (others => '0'); + page_rd_en_b <= (others => '0'); + page_wr_en_a(page_sel_a) <= wr_en_a; + page_wr_en_b(page_sel_b) <= wr_en_b; + page_rd_en_a(page_sel_a) <= rd_en_a; + page_rd_en_b(page_sel_b) <= rd_en_b; + + -- use page_sel_dly to account for the RAM read latency + rd_dat_a <= page_rd_dat_a(page_sel_a_dly(g_rd_latency - 1)); + rd_dat_b <= page_rd_dat_b(page_sel_b_dly(g_rd_latency - 1)); + rd_val_a <= page_rd_val_a(page_sel_a_dly(g_rd_latency - 1)); + rd_val_b <= page_rd_val_b(page_sel_b_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_rw_rw + generic map ( + g_technology => g_technology, + g_ram => c_mem_ram, + g_init_file => "UNUSED", + g_true_dual_port => g_true_dual_port + ) + port map ( + rst => rst, + clk => clk, + clken => clken, + adr_a => mem_adr_a, + wr_en_a => wr_en_a, + wr_dat_a => wr_dat_a, + rd_en_a => rd_en_a, + rd_dat_a => rd_dat_a, + rd_val_a => rd_val_a, + adr_b => mem_adr_b, + wr_en_b => wr_en_b, + wr_dat_b => wr_dat_b, + rd_en_b => rd_en_b, + rd_dat_b => rd_dat_b, + rd_val_b => rd_val_b + ); + + mem_adr_a <= TO_UVEC(page_sel_a, c_mem_nof_pages_w) & adr_a; + mem_adr_b <= TO_UVEC(page_sel_b, c_mem_nof_pages_w) & adr_b; + end generate; -- gen_adr + + gen_ofs : if g_str = "use_ofs" generate + u_buf : entity work.common_ram_rw_rw + generic map ( + g_technology => g_technology, + g_ram => c_buf_ram, + g_init_file => "UNUSED", + g_true_dual_port => g_true_dual_port + ) + port map ( + rst => rst, + clk => clk, + clken => clken, + adr_a => buf_adr_a, + wr_en_a => wr_en_a, + wr_dat_a => wr_dat_a, + rd_en_a => rd_en_a, + rd_dat_a => rd_dat_a, + rd_val_a => rd_val_a, + adr_b => buf_adr_b, + wr_en_b => wr_en_b, + wr_dat_b => wr_dat_b, + rd_en_b => rd_en_b, + rd_dat_b => rd_dat_b, + rd_val_b => rd_val_b + ); + + buf_adr_a <= INCR_UVEC(RESIZE_UVEC(adr_a, c_buf_addr_w), page_ofs_a); + buf_adr_b <= INCR_UVEC(RESIZE_UVEC(adr_b, c_buf_addr_w), page_ofs_b); + end generate; -- gen_ofs + +end rtl;