diff --git a/libraries/io/ddr/src/vhdl/io_ddr.vhd b/libraries/io/ddr/src/vhdl/io_ddr.vhd index ade1d6b6432ddf2e5be3c3762fa8ebc1c5028af5..36f9ab5db6b01afa822bf00414e445e56c607e22 100644 --- a/libraries/io/ddr/src/vhdl/io_ddr.vhd +++ b/libraries/io/ddr/src/vhdl/io_ddr.vhd @@ -33,15 +33,14 @@ ENTITY io_ddr IS g_technology : NATURAL := c_tech_select_default; g_tech_ddr : t_c_tech_ddr; g_wr_data_w : NATURAL := 32; - g_wr_use_ctrl : BOOLEAN := FALSE; -- TRUE to allow filling the WR FIFO (by disabling flush) after an EOP - g_wr_fifo_depth : NATURAL := 128; -- >=16 AND >g_tech_ddr.maxburstsize , defined at read side of write FIFO. - g_rd_fifo_depth : NATURAL := 256; -- >=16 AND >g_tech_ddr.maxburstsize > c_ddr_ctlr_nof_latent_reads, defined at write side of read FIFO. + g_wr_use_ctrl : BOOLEAN := FALSE; -- TRUE to allow filling the write FIFO by disabling flush after an EOP + g_wr_fifo_depth : NATURAL := 128; -- >=16 AND >g_tech_ddr.maxburstsize, defined at DDR side of the FIFO. + g_rd_fifo_depth : NATURAL := 256; -- >=16 AND >g_tech_ddr.maxburstsize, defined at DDR side of the FIFO. g_rd_data_w : NATURAL := 32; - g_flush_wr_fifo : BOOLEAN := FALSE; -- TRUE instantiates a dp_flush + controller to flush the write fifo when the driver is not ready to write - g_flush_sop : BOOLEAN := FALSE; - g_flush_sop_channel : BOOLEAN := FALSE; - g_flush_sop_start_channel : NATURAL := 0; - g_flush_nof_channels : NATURAL := 0 + g_flush_mode : STRING := "VALID"; -- "VALID", "SOP", "SYNC" + g_flush_use_channel : BOOLEAN := FALSE; + g_flush_start_channel : NATURAL := 0; + g_flush_nof_channels : POSITIVE := 1 ); PORT ( -- DDR reference clock @@ -63,6 +62,7 @@ ENTITY io_ddr IS dvr_start_addr : IN t_tech_ddr_addr; dvr_end_addr : IN t_tech_ddr_addr; dvr_done : OUT STD_LOGIC; + dvr_flush_en : IN STD_LOGIC := '0'; -- Write FIFO clock domain wr_clk : IN STD_LOGIC; @@ -90,22 +90,20 @@ END io_ddr; ARCHITECTURE str OF io_ddr IS - CONSTANT c_ctlr_data_w : NATURAL := func_tech_ddr_ctlr_data_w( g_tech_ddr); + CONSTANT c_ctlr_data_w : NATURAL := func_tech_ddr_ctlr_data_w(g_tech_ddr); - CONSTANT c_wr_fifo_depth : NATURAL := g_wr_fifo_depth * (c_ctlr_data_w/g_wr_data_w); -- Multiply fifo depth by the fifo's rd/wr width ratio to get write side depth - - CONSTANT c_ddr_ctlr_nof_latent_reads : NATURAL := 100; -- Due to having a command cue, even after de-asserting read requests, the PHY keeps processing the cued read requests. - -- This makes sure 100 words are still available in the read FIFO after it de-asserted its siso.ready signal towards the ddr3 read side. - - CONSTANT c_latency : NATURAL := 1; + CONSTANT c_wr_fifo_depth : NATURAL := g_wr_fifo_depth * (c_ctlr_data_w/g_wr_data_w); -- get FIFO depth at write side + CONSTANT c_wr_fifo_af_margin : NATURAL := 4 + 1; -- use +1 to compensate for latency introduced by registering wr_siso.ready due to RL=0 + CONSTANT c_rd_fifo_af_margin : NATURAL := 4 + g_tech_ddr.maxburstsize; -- use ctlr_rd_src_in.ready to only start new rd access when there is sufficient space in the read FIFO + SIGNAL i_ctlr_init_done : STD_LOGIC; SIGNAL i_dvr_done : STD_LOGIC; SIGNAL ctlr_mosi : t_tech_ddr_mosi := c_tech_ddr_mosi_rst; SIGNAL ctlr_miso : t_tech_ddr_miso := c_tech_ddr_miso_rst; - SIGNAL dvr_flush : STD_LOGIC := '0'; + SIGNAL wr_flush : STD_LOGIC := '0'; SIGNAL ctlr_wr_snk_out : t_dp_siso := c_dp_siso_rdy; -- default xon='1' SIGNAL ctlr_wr_snk_in : t_dp_sosi := c_dp_sosi_rst; @@ -130,7 +128,7 @@ BEGIN g_rd_data_w => c_ctlr_data_w, g_use_ctrl => g_wr_use_ctrl, g_wr_fifo_size => c_wr_fifo_depth, - g_wr_fifo_af_margin => 4 + c_latency, --default (4) + c_latency to compensate for latency introduced by registering wr_siso.ready + g_wr_fifo_af_margin => c_wr_fifo_af_margin, g_rd_fifo_rl => 0 ) PORT MAP ( @@ -150,11 +148,11 @@ BEGIN src_out => flush_wr_sosi ); - u_dp_flush : ENTITY dp_lib.dp_flush -- Always instantiate the flusher as it also contains a RL adapter + u_dp_flush : ENTITY dp_lib.dp_flush GENERIC MAP ( g_ready_latency => 0, - g_framed_xon => g_wr_use_ctrl, -- stop flushing when dvr_flush is low and a sop has arrived - g_framed_xoff => FALSE -- immediately start flushing when dvr_flush goes high + g_framed_xon => g_wr_use_ctrl, -- stop flushing when wr_flush is low and a sop has arrived + g_framed_xoff => FALSE -- immediately start flushing when wr_flush goes high ) PORT MAP ( rst => ctlr_rst_in, @@ -166,30 +164,29 @@ BEGIN src_out => ctlr_wr_snk_in, src_in => ctlr_wr_snk_out, - flush_en => dvr_flush + flush_en => wr_flush ); - gen_io_ddr_driver_flush_ctrl : IF g_flush_wr_fifo = TRUE GENERATE - u_io_ddr_driver_flush_ctrl : ENTITY work.io_ddr_driver_flush_ctrl - GENERIC MAP ( - g_sop => g_flush_sop, - g_sop_channel => g_flush_sop_channel, - g_sop_start_channel => g_flush_sop_start_channel, - g_nof_channels => g_flush_nof_channels - ) - PORT MAP ( - rst => wr_rst, - clk => wr_clk, - - dvr_en => dvr_en, - dvr_wr_not_rd => dvr_wr_not_rd, - dvr_done => i_dvr_done, - - wr_sosi => wr_sosi, - - dvr_flush => dvr_flush - ); - END GENERATE; + u_io_ddr_driver_flush_ctrl : ENTITY work.io_ddr_driver_flush_ctrl + GENERIC MAP ( + g_mode => g_flush_mode, + g_use_channel => g_flush_use_channel, + g_start_channel => g_flush_start_channel, + g_nof_channels => g_flush_nof_channels + ) + PORT MAP ( + rst => wr_rst, + clk => wr_clk, + + dvr_flush_en => dvr_flush_en, + dvr_en => dvr_en, + dvr_wr_not_rd => dvr_wr_not_rd, + dvr_done => i_dvr_done, + + wr_sosi => wr_sosi, + + wr_flush => wr_flush + ); u_rd_fifo : ENTITY dp_lib.dp_fifo_dc_mixed_widths GENERIC MAP ( @@ -197,7 +194,7 @@ BEGIN g_rd_data_w => g_rd_data_w, g_use_ctrl => FALSE, g_wr_fifo_size => g_rd_fifo_depth, - g_wr_fifo_af_margin => c_ddr_ctlr_nof_latent_reads, -- >=4 (required by dp_fifo) + g_wr_fifo_af_margin => c_rd_fifo_af_margin, -- >=4 (required by dp_fifo) g_rd_fifo_rl => 1 ) PORT MAP ( diff --git a/libraries/io/ddr/src/vhdl/io_ddr_driver_flush_ctrl.vhd b/libraries/io/ddr/src/vhdl/io_ddr_driver_flush_ctrl.vhd index f2f9e6aac4ee8b1484ca90f68b3c4ac81cafe39d..744a446edfee9a64633da692507284086f48ab4f 100644 --- a/libraries/io/ddr/src/vhdl/io_ddr_driver_flush_ctrl.vhd +++ b/libraries/io/ddr/src/vhdl/io_ddr_driver_flush_ctrl.vhd @@ -18,7 +18,20 @@ -- You should have received a copy of the GNU General Public License -- along with this program. If not, see <http://www.gnu.org/licenses/>. -- --------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-- Purpose: Flush the write input FIFO in io_ddr when not doing a write access. +-- Description: +-- The write input FIFO is flushed by when the io_ddr is not doing a write +-- access, so when idle or also when doing a read access. +-- When the io_ddr is starting a new write access, then the write input FIFO +-- gets filled. The filling starts dependent on: +-- +-- . g_mode = "VALID" : immediately start filling on next valid data +-- . g_mode = "SOP" : start filling on next sop +-- . g_mode = "SYNC" : start filling on next sync +-- +-- . g_use_channel = TRUE : start filling when channel matches g_start_channel LIBRARY IEEE, common_lib, dp_lib; USE IEEE.STD_LOGIC_1164.ALL; @@ -28,23 +41,23 @@ USE dp_lib.dp_stream_pkg.ALL; ENTITY io_ddr_driver_flush_ctrl IS GENERIC ( - g_sop : BOOLEAN := FALSE; -- Stop flushing on SOP, otherwise stop flushing on valid data - g_sop_channel : BOOLEAN := FALSE; -- When g_sop=TRUE, also check if the channel matches g_sop_start_channel - g_sop_start_channel : NATURAL := 0; - g_nof_channels : NATURAL := 0 + g_mode : STRING := "VALID"; -- "VALID", "SOP", "SYNC" + g_use_channel : BOOLEAN := FALSE; + g_start_channel : NATURAL := 0; + g_nof_channels : POSITIVE := 1 ); PORT ( clk : IN STD_LOGIC; rst : IN STD_LOGIC; + dvr_flush_en : IN STD_LOGIC := '1'; dvr_en : IN STD_LOGIC := '1'; dvr_wr_not_rd : IN STD_LOGIC; dvr_done : IN STD_LOGIC; wr_sosi : IN t_dp_sosi; - dvr_flush : OUT STD_LOGIC - + wr_flush : OUT STD_LOGIC ); END io_ddr_driver_flush_ctrl; @@ -58,27 +71,25 @@ ARCHITECTURE str OF io_ddr_driver_flush_ctrl IS SIGNAL state : t_state; SIGNAL nxt_state : t_state; + SIGNAL channel : NATURAL RANGE 0 TO g_nof_channels-1; + SIGNAL flush_dis : STD_LOGIC; BEGIN - -- Flush ddr3 module's FIFO (keep sinking the stream but simply discard the - -- data) after reset to prevent ddr3 write fifo from filling up - which would - -- cause problems downstream (dp_mux uses two fifos that cannot be - -- allowed to fill up too much). - -- Also flush the ddr3 module's FIFO when it is reading. - - gen_sop : IF g_sop = TRUE GENERATE -- Disable flushing on arrival of SOP - gen_sop_only: IF g_sop_channel = FALSE GENERATE - flush_dis <= '1' WHEN wr_sosi.sop='1' ELSE '0'; - END GENERATE; - gen_channel : IF g_sop_channel = TRUE GENERATE -- Only disable flushing on arrival of specific channel SOP - flush_dis <= '1' WHEN wr_sosi.sop='1' AND UNSIGNED(wr_sosi.channel(c_channel_w-1 DOWNTO 0))=g_sop_start_channel ELSE '0'; - END GENERATE; + -- Flush disable control + no_channel: IF g_use_channel=FALSE GENERATE + gen_valid : IF g_mode="VALID" GENERATE flush_dis <= wr_sosi.valid; END GENERATE; + gen_sop : IF g_mode="SOP" GENERATE flush_dis <= wr_sosi.sop ; END GENERATE; + gen_sync : IF g_mode="SYNC" GENERATE flush_dis <= wr_sosi.sync ; END GENERATE; END GENERATE; - - gen_val : IF g_sop = FALSE GENERATE -- Disable flushing on arrival of 1st valid data - flush_dis <= wr_sosi.valid; + + use_channel: IF g_use_channel=TRUE GENERATE + channel <= TO_UINT(wr_sosi.channel(c_channel_w-1 DOWNTO 0)); + + gen_valid : IF g_mode="VALID" GENERATE flush_dis <= '1' WHEN wr_sosi.valid='1' AND channel=g_start_channel ELSE '0'; END GENERATE; + gen_sop : IF g_mode="SOP" GENERATE flush_dis <= '1' WHEN wr_sosi.sop ='1' AND channel=g_start_channel ELSE '0'; END GENERATE; + gen_sync : IF g_mode="SYNC" GENERATE flush_dis <= '1' WHEN wr_sosi.sync ='1' AND channel=g_start_channel ELSE '0'; END GENERATE; END GENERATE; p_reg : PROCESS(rst, clk) @@ -90,23 +101,23 @@ BEGIN END IF; END PROCESS; - p_state : PROCESS(state, dvr_done, dvr_en, dvr_wr_not_rd, flush_dis) + p_state : PROCESS(state, dvr_flush_en, dvr_done, dvr_en, dvr_wr_not_rd, flush_dis) BEGIN nxt_state <= state; - dvr_flush <= '0'; + wr_flush <= '0'; CASE state IS WHEN s_idle => - IF dvr_done='1' THEN - dvr_flush <= '1'; + IF dvr_flush_en='1' AND dvr_done='1' THEN + wr_flush <= '1'; nxt_state <= s_flush; END IF; WHEN s_flush => - dvr_flush <= '1'; + wr_flush <= '1'; IF dvr_en='1' AND dvr_wr_not_rd='1' THEN nxt_state <= s_stop; END IF; WHEN OTHERS => -- s_stop - dvr_flush <= '1'; + wr_flush <= '1'; IF flush_dis = '1' THEN nxt_state <= s_idle; END IF; diff --git a/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd b/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd index 415db0eb043d291231670c9a737ab42ac9839141..7f3534ad3db561b9ad89eb7c70d2569709f055ba 100644 --- a/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd +++ b/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd @@ -89,7 +89,8 @@ ARCHITECTURE str of tb_io_ddr IS SIGNAL dvr_en : STD_LOGIC; SIGNAL dvr_wr_not_rd : STD_LOGIC; SIGNAL dvr_done : STD_LOGIC; - + SIGNAL dvr_flush_en : STD_LOGIC := '0'; + SIGNAL wr_siso : t_dp_siso; SIGNAL wr_sosi : t_dp_sosi; @@ -135,6 +136,9 @@ BEGIN src_diag_en <= '1'; snk_diag_en <= '1'; + -- When dvr_flush_en='0' then the write FIFO is flushed until the first write access is started + proc_common_wait_some_cycles(ctlr_clk, 100); + FOR I IN c_address_lo_arr'RANGE LOOP v_addr_lo := func_tech_ddr_dq_address(TO_DDR_CTLR_ADDRESS(4*c_address_lo_arr(I)) , c_tech_ddr); v_addr_hi := func_tech_ddr_dq_address(TO_DDR_CTLR_ADDRESS(4*c_address_lo_arr(I)+4*c_nof_address_arr(I)-1), c_tech_ddr); @@ -217,7 +221,8 @@ BEGIN dvr_done => dvr_done, dvr_start_addr => dvr_start_addr, dvr_end_addr => dvr_end_addr, - + dvr_flush_en => dvr_flush_en, + wr_clk => dp_clk, wr_rst => dp_rst,