From f52fc96d8b9ccac2d7023e13c6e4b225cac41c09 Mon Sep 17 00:00:00 2001
From: Erik Kooistra <kooistra@astron.nl>
Date: Mon, 5 Jan 2015 14:22:44 +0000
Subject: [PATCH] Added io_ddr_cross_domain.vhd to handle dvr_clk and ctlr_clk
 domains in case they are different clock domains.

---
 libraries/io/ddr/hdllib.cfg                   |   1 +
 libraries/io/ddr/src/vhdl/io_ddr.vhd          | 233 ++++++++++++------
 .../io/ddr/src/vhdl/io_ddr_cross_domain.vhd   | 149 +++++++++++
 libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd        |  58 +++--
 libraries/io/ddr/tb/vhdl/tb_tb_io_ddr.vhd     |  20 +-
 5 files changed, 349 insertions(+), 112 deletions(-)
 create mode 100644 libraries/io/ddr/src/vhdl/io_ddr_cross_domain.vhd

diff --git a/libraries/io/ddr/hdllib.cfg b/libraries/io/ddr/hdllib.cfg
index a033ca3ac9..41a4c6ca9c 100644
--- a/libraries/io/ddr/hdllib.cfg
+++ b/libraries/io/ddr/hdllib.cfg
@@ -12,6 +12,7 @@ modelsim_compile_ip_files =
 synth_files =
     src/vhdl/io_ddr_driver_flush_ctrl.vhd
     src/vhdl/io_ddr_driver.vhd
+    src/vhdl/io_ddr_cross_domain.vhd
     src/vhdl/io_ddr.vhd
 
 test_bench_files = 
diff --git a/libraries/io/ddr/src/vhdl/io_ddr.vhd b/libraries/io/ddr/src/vhdl/io_ddr.vhd
index bdf19a2b66..b804ce46e2 100644
--- a/libraries/io/ddr/src/vhdl/io_ddr.vhd
+++ b/libraries/io/ddr/src/vhdl/io_ddr.vhd
@@ -18,47 +18,81 @@
 -- 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: Provide streaming interface to DDR memory
 -- Description:
 --
---   dvr_wr_fifo_usedw <----------------\
---                                      |
---            wr_sosi     wr_fifo_src   |   ctlr_wr_snk   ctlr_mosi
---             .                .       |     .             .
---             .     ________   .       |     .   _______   .   ______
---             .    |        |--.-------+------->|       |  .  |      |
---             .    |        |  .    ______   .  |       |  .  |      |
---             .    |dp_fifo |  .   |      |  .  |       |  .  |      |
---            ----->|dc_mixed|-+--->|dp    |---->|  io   |  .  | tech |
---                  |widths  | |    |flush |     | ddr   |  .  | ddr  |
---                  |________| |    |______|<-\  | driver|  .  |      |
---                             |              |  |       |  .  |      |
---                             |   wr_flush_en|  |       |  .  |      |
---                             |     ______   |  |       |  .  |      |
---                             \--->|io_ddr|--/  |       |  .  |      |
---                                  |driver|     |       |  .  |      |
---                             /--->|flush |     |       |  .  |      |
---                             |/-->|ctrl  |<-\  |       |  .  |      |
---                             ||/->|______|  |  |       |  .  |      |
---                             |||            |  |       |  .  |      |<--- phy_in
---   dvr_flush_en   -----------+||------------|->|       |---->|      |---> phy_out
---   dvr_en         ------------+|------------|->|       |<----|      |<--> phy_io
---   dvr_wr_not_rd  -------------+------------|->|       |  .  |      |
---   dvr_done       <-------------------------+--|       |  .  |      |
---   dvr_start_addr ---------------------------->|       |  .  |      |
---   dvr_end_addr   ---------------------------->|       |  .  |      |
---                   ________                    |       |  .  |      |
---                  |dp_fifo |                   |       |  .  |      |
---            <-----|dc_mixed|<------------------|       |  .  |      |
---             .    |widths  |                .  |_______|  .  |______|
---             .    |________|                .             .           
---            rd_sosi                       ctlr_rd_src   ctlr_miso
---            rd_fifo_usedw                               ctlr_init_done
---         
+--   The write FIFO and read FIFO take care of the clock domain crossing that
+--   is needed if the ctlr_clk is not used for the data path. Furthermore the
+--   FIFO takes care of repacking the user write and read data into the
+--   typically wider data width c_ctlr_data_w = 256 for DDR3.
 --
-
+--   The DDR access starts after a dvr_en pulse. The access can be write or
+--   read as defined by dvr_wr_not_rd. The size of the access depends the
+--   address range given by dvr_start_addr and dvr_end_addr. The dvr_done goes
+--   high when the access has finished and it goes low when a new access
+--   starts.
+--
+--   The dvr_wr_flush_en pulse is recognized only between DDR accesses so when
+--   the dvr_done is active, otherwise the dvr_wr_flush_en is ignored. The
+--   dvr_wr_flush_en pulse can be used to flush the write FIFO to avoid that is
+--   runs full in case a next write access will not happen soon enough. A next
+--   DDR write access can start on the next valid, sop or sync dependent on
+--   g_wr_flush_mode.
+--
+-- Block diagram:
+--
+--   dvr_wr_fifo_usedw <-*-------------------\
+--                                           |
+--                        ctlr_wr_fifo_src   |    ctlr_wr_snk   ctlr_mosi
+--                                    .      |      .             .
+--                      ________      .      |      .   _______   .   ______
+--                     |        |-----.------+------.->|       |  .  |      |
+--                     |        |     .   ______    .  |       |  .  |      |
+--                     |dp_fifo |     .  |      |   .  |       |  .  |      |
+--   wr_sosi --------->|dc_mixed|-+----->|dp    |----->| io    |  .  | tech |
+--   wr_clk  --------->|widths  | |      |flush |      | ddr   |  .  | ddr  |
+--                     |________| |      |______|<--\  | driver|  .  |      |
+--                                |                 |  |       |  .  |      |
+--                                | ctlr_wr_flush_en|  |       |  .  |      |
+--                                |       ______    |  |       |  .  |      |
+--                                \----->|io_ddr|---/  |       |  .  |      |
+--   dvr_clk ------------>               |driver|      |       |  .  |      |<--- phy_in
+--   dvr_wr_flush_en ----*-------------->|flush |      |       |---->|      |---> phy_ou
+--                                 /---->|ctrl  |<--\  |       |<----|      |<--> phy_io
+--                                 |/--->|______|   |  |       |  .  |      |
+--                                 ||               |  |       |  .  |      |
+--   dvr_en          ----*---------+|---------------|->|       |  .  |      |
+--   dvr_wr_not_rd   ----*----------+---------------|->|       |  .  |      |
+--   dvr_done        <---*--------------------------+--|       |  .  |      |
+--   dvr_start_addr  ----*---------------------------->|       |  .  |      |
+--   dvr_end_addr    ----*---------------------------->|       |  .  |      |
+--                      ________                       |       |  .  |      |
+--   rd_clk  --------->|dp_fifo |                      |       |  .  |      |
+--   rd_sosi <---------|dc_mixed|<---------------------|       |  .  |      |
+--   rd_fifo_usedw     |widths  |                   .  |_______|  .  |______|---\
+--                     |________|                   .             .             |
+--                                                ctlr_rd_src   ctlr_miso       |
+--                                                              ctlr_init_done  |
+--                                                                              |
+--   ctlr_clk /------ctlr_clk_in ------->                                       |
+--            \------ctlr_clk_out-----------------------------------------------/
+--
+--     * = clock domain crossing between dvr_clk and ctlr_clk clock domains.
+--
+-- Remarks:
+-- . If the dvr_clk=ctlr_clk then the clock domain crossing logic defaults
+--   to wires. However dvr_clk could also be the dp_clk or the mm_clk and then 
+--   the clock domain crossing logic is needed.
+--   No need to cross dvr_start_addr, because the address is stable when the
+--   dvr_en is stable. No need to cross dvr_wr_fifo_usedw, because it is only
+--   used for monitoring purposes.
+-- . Externally connect ctlr_clk = ctlr_clk_in = ctlr_clk_out
+-- . Typically wr_clk = rd_clk = dp_clk.
+-- . The main PHY signals are carried by phy_ou and phy_io. The phy_in signals
+--   are typically not needed.
+        
 LIBRARY IEEE, technology_lib, tech_ddr_lib, common_lib, dp_lib;
 USE IEEE.STD_LOGIC_1164.ALL;
 USE common_lib.common_pkg.ALL;
@@ -71,6 +105,7 @@ ENTITY io_ddr IS
   GENERIC(
     g_technology              : NATURAL := c_tech_select_default;
     g_tech_ddr                : t_c_tech_ddr;
+    g_cross_domain_dvr_ctlr   : BOOLEAN := TRUE;
     g_wr_data_w               : NATURAL := 32;  
     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. 
@@ -95,12 +130,15 @@ ENTITY io_ddr IS
     ctlr_init_done     : OUT   STD_LOGIC;
     ctlr_rdy           : OUT   STD_LOGIC;
 
+    -- Driver clock domain
+    dvr_clk            : IN    STD_LOGIC;
+    dvr_rst            : IN    STD_LOGIC;
     dvr_en             : IN    STD_LOGIC;
+    dvr_done           : OUT   STD_LOGIC;
     dvr_wr_not_rd      : IN    STD_LOGIC;
     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';
+    dvr_wr_flush_en    : IN    STD_LOGIC := '0';
     dvr_wr_fifo_usedw  : OUT   STD_LOGIC_VECTOR(ceil_log2(g_wr_fifo_depth * (func_tech_ddr_ctlr_data_w(g_tech_ddr)/g_wr_data_w) )-1 DOWNTO 0);  -- for monitoring purposes
 
     -- Write FIFO clock domain
@@ -134,42 +172,75 @@ ARCHITECTURE str OF io_ddr IS
   CONSTANT c_wr_fifo_use_ctrl  : BOOLEAN := c_wr_use_sync OR c_wr_use_ctrl;
   
   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);  -- 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_dvr_en           : STD_LOGIC;
+  SIGNAL ctlr_dvr_done         : STD_LOGIC;
+  SIGNAL ctlr_dvr_wr_not_rd    : STD_LOGIC;
+  SIGNAL ctlr_dvr_start_addr   : t_tech_ddr_addr;
+  SIGNAL ctlr_dvr_end_addr     : t_tech_ddr_addr;
+  SIGNAL ctlr_dvr_wr_flush_en  : STD_LOGIC := '0';
+    
+  SIGNAL i_ctlr_init_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 ctlr_mosi             : t_tech_ddr_mosi := c_tech_ddr_mosi_rst;
+  SIGNAL ctlr_miso             : t_tech_ddr_miso := c_tech_ddr_miso_rst;
 
-  SIGNAL wr_flush_en       : STD_LOGIC := '0';
+  SIGNAL ctlr_wr_flush_en      : STD_LOGIC := '0';
  
-  SIGNAL wr_fifo_snk_in    : t_dp_sosi;
+  SIGNAL wr_fifo_snk_in        : t_dp_sosi;
   
-  SIGNAL wr_fifo_src_in    : t_dp_siso;
-  SIGNAL wr_fifo_src_out   : t_dp_sosi := c_dp_sosi_rst;
+  SIGNAL ctlr_wr_fifo_src_in   : t_dp_siso;
+  SIGNAL ctlr_wr_fifo_src_out  : t_dp_sosi := c_dp_sosi_rst;
 
-  SIGNAL wr_flush_snk_in   : t_dp_sosi := c_dp_sosi_rst;
+  SIGNAL ctlr_wr_flush_snk_in  : t_dp_sosi := c_dp_sosi_rst;
   
-  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;
+  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;
 
-  SIGNAL ctlr_rd_src_in    : t_dp_siso;
-  SIGNAL ctlr_rd_src_out   : t_dp_sosi := c_dp_sosi_rst;
+  SIGNAL ctlr_rd_src_in        : t_dp_siso;
+  SIGNAL ctlr_rd_src_out       : t_dp_sosi := c_dp_sosi_rst;
   
-  SIGNAL wr_fifo_usedw     : STD_LOGIC_VECTOR(ceil_log2(g_wr_fifo_depth)-1 DOWNTO 0);  -- read side depth of the write FIFO
+  SIGNAL ctlr_wr_fifo_usedw    : STD_LOGIC_VECTOR(ceil_log2(g_wr_fifo_depth)-1 DOWNTO 0);  -- read side depth of the write FIFO
   
 BEGIN 
 
   ctlr_init_done <= i_ctlr_init_done;
   ctlr_rdy <= ctlr_miso.waitrequest_n;
-  dvr_done <= i_dvr_done;
-  dvr_wr_fifo_usedw <= wr_fifo_usedw;
   
+  u_io_ddr_cross_domain : ENTITY work.io_ddr_cross_domain
+  GENERIC MAP (
+    g_cross_domain => g_cross_domain_dvr_ctlr
+  )
+  PORT MAP(
+    -- Driver clock domain
+    dvr_clk                => dvr_clk,
+    dvr_rst                => dvr_rst,
+    
+    dvr_en                 => dvr_en,
+    dvr_done               => dvr_done,
+    dvr_wr_not_rd          => dvr_wr_not_rd,
+    dvr_start_addr         => dvr_start_addr,
+    dvr_end_addr           => dvr_end_addr,
+    dvr_wr_flush_en        => dvr_wr_flush_en,
+    dvr_wr_fifo_usedw      => dvr_wr_fifo_usedw,
+    
+    -- DDR controller clock domain
+    ctlr_clk               => ctlr_clk_in,
+    ctlr_rst               => ctlr_rst_in,
+    
+    ctlr_dvr_en            => ctlr_dvr_en,
+    ctlr_dvr_done          => ctlr_dvr_done,
+    ctlr_dvr_wr_not_rd     => ctlr_dvr_wr_not_rd,
+    ctlr_dvr_start_addr    => ctlr_dvr_start_addr,
+    ctlr_dvr_end_addr      => ctlr_dvr_end_addr,
+    ctlr_dvr_wr_flush_en   => ctlr_dvr_wr_flush_en,
+    ctlr_dvr_wr_fifo_usedw => ctlr_wr_fifo_usedw
+  );
+   
   p_wr_fifo_snk_in : PROCESS (wr_sosi)
   BEGIN
     wr_fifo_snk_in <= wr_sosi;
@@ -199,39 +270,39 @@ BEGIN
     snk_in         => wr_fifo_snk_in,
   
     wr_usedw       => OPEN,
-    rd_usedw       => wr_fifo_usedw,
+    rd_usedw       => ctlr_wr_fifo_usedw,
     rd_emp         => OPEN,
 
-    src_in         => wr_fifo_src_in,
-    src_out        => wr_fifo_src_out
+    src_in         => ctlr_wr_fifo_src_in,
+    src_out        => ctlr_wr_fifo_src_out
   );
   
   u_dp_flush : ENTITY dp_lib.dp_flush
   GENERIC MAP (
     g_ready_latency => 0,
-    g_framed_xon    => c_wr_fifo_use_ctrl,  -- stop flushing when wr_flush_en is low and a sop (or sync via sop) has arrived 
-    g_framed_xoff   => FALSE                -- immediately start flushing when wr_flush_en goes high
+    g_framed_xon    => c_wr_fifo_use_ctrl,  -- stop flushing when flush_en is low and a sop (or sync via sop) has arrived 
+    g_framed_xoff   => FALSE                -- immediately start flushing when flush_en goes high
   )
   PORT MAP (
     rst      => ctlr_rst_in,
     clk      => ctlr_clk_in,
    
-    snk_in   => wr_fifo_src_out,
-    snk_out  => wr_fifo_src_in,
+    snk_in   => ctlr_wr_fifo_src_out,
+    snk_out  => ctlr_wr_fifo_src_in,
 
     src_out  => ctlr_wr_snk_in,
     src_in   => ctlr_wr_snk_out,
 
-    flush_en => wr_flush_en
+    flush_en => ctlr_wr_flush_en
   );
 
-  p_wr_flush_snk_in : PROCESS (wr_fifo_src_out)
+  p_ctlr_wr_flush_snk_in : PROCESS (ctlr_wr_fifo_src_out)
   BEGIN
-    wr_flush_snk_in <= wr_fifo_src_out;
+    ctlr_wr_flush_snk_in <= ctlr_wr_fifo_src_out;
     IF c_wr_use_sync=TRUE THEN
       -- Work around : Transport sync via sop through the dp_fifo_dc_mixed_widths
-      wr_flush_snk_in.sync <= wr_fifo_src_out.sop;
-      wr_flush_snk_in.sop  <= '0';
+      ctlr_wr_flush_snk_in.sync <= ctlr_wr_fifo_src_out.sop;
+      ctlr_wr_flush_snk_in.sop  <= '0';
     END IF;
   END PROCESS;
   
@@ -243,17 +314,17 @@ BEGIN
     g_nof_channels  => g_wr_flush_nof_channels
   )
   PORT MAP (
-    rst           => ctlr_rst_in,
-    clk           => ctlr_clk_in,
+    rst              => ctlr_rst_in,
+    clk              => ctlr_clk_in,
 
-    dvr_flush_en  => dvr_flush_en,
-    dvr_en        => dvr_en,
-    dvr_wr_not_rd => dvr_wr_not_rd,
-    dvr_done      => i_dvr_done,
+    dvr_en           => ctlr_dvr_en,
+    dvr_done         => ctlr_dvr_done,
+    dvr_wr_not_rd    => ctlr_dvr_wr_not_rd,
+    dvr_wr_flush_en  => ctlr_dvr_wr_flush_en,
 
-    wr_sosi       => wr_flush_snk_in,
+    wr_sosi          => ctlr_wr_flush_snk_in,
 
-    wr_flush_en   => wr_flush_en
+    ctlr_wr_flush_en => ctlr_wr_flush_en
   );
 
   u_rd_fifo : ENTITY dp_lib.dp_fifo_dc_mixed_widths
@@ -290,13 +361,13 @@ BEGIN
     rst             => ctlr_rst_in,  
     clk             => ctlr_clk_in,        
 
-    dvr_en          => dvr_en,
-    dvr_wr_not_rd   => dvr_wr_not_rd,
-    dvr_start_addr  => dvr_start_addr,
-    dvr_end_addr    => dvr_end_addr, 
-    dvr_done        => i_dvr_done,
+    dvr_en          => ctlr_dvr_en,
+    dvr_wr_not_rd   => ctlr_dvr_wr_not_rd,
+    dvr_start_addr  => ctlr_dvr_start_addr,
+    dvr_end_addr    => ctlr_dvr_end_addr, 
+    dvr_done        => ctlr_dvr_done,
     
-    wr_fifo_usedw   => wr_fifo_usedw,
+    wr_fifo_usedw   => ctlr_wr_fifo_usedw,
     wr_snk_in       => ctlr_wr_snk_in, 
     wr_snk_out      => ctlr_wr_snk_out,
     
diff --git a/libraries/io/ddr/src/vhdl/io_ddr_cross_domain.vhd b/libraries/io/ddr/src/vhdl/io_ddr_cross_domain.vhd
new file mode 100644
index 0000000000..a5420a39ed
--- /dev/null
+++ b/libraries/io/ddr/src/vhdl/io_ddr_cross_domain.vhd
@@ -0,0 +1,149 @@
+--------------------------------------------------------------------------------
+--
+-- Copyright (C) 2014
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.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.
+--
+-- 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.
+--
+-- 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: Cross clock domain between dvr_clk and ctlr_clk
+-- Description:
+--
+-- Remarks:
+-- . If the dvr_clk=ctlr_clk then the clock domain crossing logic defaults
+--   to wires. However dvr_clk could also be the dp_clk or the mm_clk and then 
+--   the clock domain crossing logic is needed.
+--   No need to cross dvr_start_addr, because the address is stable when the
+--   dvr_en is stable. No need to cross dvr_wr_fifo_usedw, because it is only
+--   used for monitoring purposes.
+        
+LIBRARY IEEE, technology_lib, tech_ddr_lib, common_lib, dp_lib;
+USE IEEE.STD_LOGIC_1164.ALL;
+USE common_lib.common_pkg.ALL;
+USE technology_lib.technology_select_pkg.ALL;
+USE technology_lib.technology_pkg.ALL;
+USE tech_ddr_lib.tech_ddr_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+
+ENTITY io_ddr_cross_domain IS
+  GENERIC (
+    g_cross_domain : BOOLEAN := TRUE
+  );                      
+  PORT (
+    -- Driver clock domain
+    dvr_clk                : IN  STD_LOGIC;
+    dvr_rst                : IN  STD_LOGIC;
+    
+    dvr_en                 : IN  STD_LOGIC;
+    dvr_done               : OUT STD_LOGIC;
+    dvr_wr_not_rd          : IN  STD_LOGIC;
+    dvr_start_addr         : IN  t_tech_ddr_addr;
+    dvr_end_addr           : IN  t_tech_ddr_addr;
+    dvr_wr_flush_en        : IN  STD_LOGIC := '0';
+    dvr_wr_fifo_usedw      : OUT STD_LOGIC_VECTOR;
+    
+    -- DDR controller clock domain
+    ctlr_clk               : IN  STD_LOGIC;
+    ctlr_rst               : IN  STD_LOGIC;
+    
+    ctlr_dvr_en            : OUT STD_LOGIC;
+    ctlr_dvr_done          : IN  STD_LOGIC;
+    ctlr_dvr_wr_not_rd     : OUT STD_LOGIC;
+    ctlr_dvr_start_addr    : OUT t_tech_ddr_addr;
+    ctlr_dvr_end_addr      : OUT t_tech_ddr_addr;
+    ctlr_dvr_wr_flush_en   : OUT STD_LOGIC := '0';
+    ctlr_dvr_wr_fifo_usedw : IN  STD_LOGIC_VECTOR
+   );
+END io_ddr_cross_domain;
+
+
+ARCHITECTURE str OF io_ddr_cross_domain IS
+
+  SIGNAL dvr_en_busy  : STD_LOGIC;
+  SIGNAL new_dvr_done : STD_LOGIC;
+  
+BEGIN 
+
+  no_cross : IF g_cross_domain=FALSE GENERATE
+    -- dvr_clk --> ctlr_clk
+    ctlr_dvr_en            <= dvr_en;
+    ctlr_dvr_wr_not_rd     <= dvr_wr_not_rd;
+    ctlr_dvr_start_addr    <= dvr_start_addr;
+    ctlr_dvr_end_addr      <= dvr_end_addr;
+    ctlr_dvr_wr_flush_en   <= dvr_wr_flush_en;
+    
+    -- ctlr_clk --> dvr_clk
+    dvr_done               <= ctlr_dvr_done;
+    dvr_wr_fifo_usedw      <= ctlr_dvr_wr_fifo_usedw;
+  END GENERATE;
+  
+  gen_cross : IF g_cross_domain=TRUE GENERATE
+    -- dvr_clk --> ctlr_clk
+    u_common_spulse_ctlr_dvr_en : ENTITY common_lib.common_spulse
+    GENERIC MAP (
+      g_delay_len => c_meta_delay_len
+    )
+    PORT MAP (
+      in_rst    => dvr_rst,
+      in_clk    => dvr_clk,
+      in_pulse  => dvr_en,
+      in_busy   => dvr_en_busy,
+      out_rst   => ctlr_rst,
+      out_clk   => ctlr_clk,
+      out_pulse => ctlr_dvr_en
+    );
+    
+    -- Only register into the other clock domain
+    ctlr_dvr_wr_not_rd  <= dvr_wr_not_rd  WHEN rising_edge(ctlr_clk);
+    ctlr_dvr_start_addr <= dvr_start_addr WHEN rising_edge(ctlr_clk);
+    ctlr_dvr_end_addr   <= dvr_end_addr   WHEN rising_edge(ctlr_clk);
+    
+    u_common_spulse_ctlr_dvr_wr_flush_en : ENTITY common_lib.common_spulse
+    GENERIC MAP (
+      g_delay_len => c_meta_delay_len
+    )
+    PORT MAP (
+      in_rst    => dvr_rst,
+      in_clk    => dvr_clk,
+      in_pulse  => dvr_wr_flush_en,
+      out_rst   => ctlr_rst,
+      out_clk   => ctlr_clk,
+      out_pulse => ctlr_dvr_wr_flush_en
+    );
+    
+    -- ctlr_clk --> dvr_clk
+    u_common_async_dvr_done : ENTITY common_lib.common_async
+    GENERIC MAP (
+      g_rst_level => '0',
+      g_delay_len => c_meta_delay_len
+    )
+    PORT MAP (
+      rst  => dvr_rst,
+      clk  => dvr_clk,
+      din  => ctlr_dvr_done,
+      dout => new_dvr_done
+    );
+    
+    -- Ensure previous dvr_done goes low after new dvr_en
+    dvr_done <= new_dvr_done AND NOT dvr_en_busy;
+      
+    -- Only register the word in the other clock domai 
+    dvr_wr_fifo_usedw <= ctlr_dvr_wr_fifo_usedw WHEN rising_edge(ctlr_clk);
+    
+  END GENERATE;
+  
+END str;
diff --git a/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd b/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd
index b8fd498594..11ab25b024 100644
--- a/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd
+++ b/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd
@@ -46,17 +46,18 @@ USE tech_ddr_lib.tech_ddr_pkg.ALL;
 
 ENTITY tb_io_ddr IS
   GENERIC (
-    g_technology       : NATURAL := c_tech_select_default;
-    g_nof_repeat       : NATURAL := 2;
-    g_wr_flush_mode    : STRING := "SYN"  -- "VAL", "SOP", "SYN"
+    g_technology            : NATURAL := c_tech_select_default;
+    g_ctlr_ref_clk_period   : TIME := 5 ns;     -- 200 MHz
+    g_dvr_clk_period        : TIME := 5 ns;    -- 50 ns
+    g_dp_clk_period         : TIME := 5000 ps;  -- 200 MHz
+    g_nof_repeat            : NATURAL := 2;
+    g_wr_flush_mode         : STRING := "SYN"  -- "VAL", "SOP", "SYN"
   );
 END ENTITY tb_io_ddr;
 
 ARCHITECTURE str of tb_io_ddr IS
 
-
-  CONSTANT c_ctlr_ref_clk_period : TIME := 5 ns;  -- 200 MHz
-  CONSTANT c_dp_clk_period       : TIME := 5000 ps;  -- 200 MHz
+  CONSTANT c_cross_domain_dvr_ctlr : BOOLEAN := g_ctlr_ref_clk_period/=g_dvr_clk_period;
 
   CONSTANT c_tech_ddr         : t_c_tech_ddr := c_tech_ddr3_4g_800m_master;
   
@@ -91,6 +92,8 @@ ARCHITECTURE str of tb_io_ddr IS
   SIGNAL ctlr_ref_rst         : STD_LOGIC;
   SIGNAL ctlr_clk             : STD_LOGIC;
   SIGNAL ctlr_rst             : STD_LOGIC;
+  SIGNAL dvr_clk              : STD_LOGIC := '0';
+  SIGNAL dvr_rst              : STD_LOGIC;
   SIGNAL dp_clk               : STD_LOGIC := '0';
   SIGNAL dp_rst               : STD_LOGIC;
 
@@ -101,9 +104,9 @@ ARCHITECTURE str of tb_io_ddr IS
   SIGNAL dvr_end_addr         : t_tech_ddr_addr;
 
   SIGNAL dvr_en               : STD_LOGIC;
-  SIGNAL dvr_wr_not_rd        : STD_LOGIC;
   SIGNAL dvr_done             : STD_LOGIC;
-  SIGNAL dvr_flush_en         : STD_LOGIC;
+  SIGNAL dvr_wr_not_rd        : STD_LOGIC;
+  SIGNAL dvr_wr_flush_en      : STD_LOGIC;
   SIGNAL dvr_wr_fifo_usedw    : STD_LOGIC_VECTOR(ceil_log2(c_wr_fifo_depth * c_dp_factor)-1 DOWNTO 0);    
   
   SIGNAL diag_wr_src_in       : t_dp_siso;
@@ -130,23 +133,26 @@ ARCHITECTURE str of tb_io_ddr IS
   
 BEGIN
  
-  ctlr_ref_clk <= NOT ctlr_ref_clk OR tb_end AFTER c_ctlr_ref_clk_period/2; 
+  ctlr_ref_clk <= NOT ctlr_ref_clk OR tb_end AFTER g_ctlr_ref_clk_period/2; 
   ctlr_ref_rst <= '1', '0' AFTER 100 ns;
   
-  dp_clk   <= NOT dp_clk OR tb_end AFTER c_dp_clk_period/2; 
+  dvr_clk  <= NOT dvr_clk OR tb_end AFTER g_dvr_clk_period/2; 
+  dvr_rst  <= '1', '0' AFTER 100 ns;
+  
+  dp_clk   <= NOT dp_clk OR tb_end AFTER g_dp_clk_period/2; 
   dp_rst   <= '1', '0' AFTER 100 ns;
 
   p_stimuli : PROCESS
     VARIABLE v_addr_lo : t_tech_ddr_addr;
     VARIABLE v_addr_hi : t_tech_ddr_addr;
   BEGIN
-    tb_end        <= '0';
-    dvr_flush_en  <= '0';
-    dvr_en        <= '0';
-    src_diag_en   <= '0';
-    dvr_wr_not_rd <= '0';
-    snk_diag_en   <= '0';
-    expected_cnt  <= 0;
+    tb_end          <= '0';
+    dvr_en          <= '0';
+    dvr_wr_flush_en <= '0';
+    dvr_wr_not_rd   <= '0';
+    src_diag_en     <= '0';
+    snk_diag_en     <= '0';
+    expected_cnt    <= 0;
 
     proc_common_wait_until_high(ctlr_clk, ctlr_init_done);
     proc_common_wait_some_cycles(ctlr_clk, 2);  -- Give the driver FSM a cycle to go into idle mode
@@ -155,7 +161,7 @@ BEGIN
     src_diag_en <= '1';
     snk_diag_en <= '1';
     
-    -- After reset even when dvr_flush_en='0' then the write FIFO is flushed until the first write access is started
+    -- After reset even when dvr_wr_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 R IN 0 TO g_nof_repeat-1 LOOP
@@ -169,11 +175,11 @@ BEGIN
         -- START ACCESS
         dvr_wr_not_rd <= c_wr_not_rd_arr(I);
         dvr_en        <= '1';
-        proc_common_wait_some_cycles(ctlr_clk, 1);
+        proc_common_wait_some_cycles(dvr_clk, 1);
         dvr_en        <= '0'; 
         
         -- ACCESS DONE  
-        proc_common_wait_until_lo_hi(ctlr_clk, dvr_done);
+        proc_common_wait_until_lo_hi(dvr_clk, dvr_done);
         
         IF c_wr_not_rd_arr(I)='0' THEN
           expected_cnt <= expected_cnt + c_nof_address_arr(I)*c_dp_factor;
@@ -188,9 +194,9 @@ BEGIN
       snk_diag_en <= '0';
       
       -- Flush the wr fifo
-      dvr_flush_en <= '1';
-      proc_common_wait_some_cycles(ctlr_clk, 1);
-      dvr_flush_en <= '0';
+      dvr_wr_flush_en <= '1';
+      proc_common_wait_some_cycles(dvr_clk, 1);
+      dvr_wr_flush_en <= '0';
       
       proc_common_wait_some_cycles(ctlr_clk, 500);
       ASSERT UNSIGNED(dvr_wr_fifo_usedw) = 0      REPORT "[ERROR] Write FIFO is flushed but not empty!" SEVERITY FAILURE;
@@ -260,6 +266,7 @@ BEGIN
   GENERIC MAP(
     g_technology             => g_technology,
     g_tech_ddr               => c_tech_ddr,
+    g_cross_domain_dvr_ctlr  => c_cross_domain_dvr_ctlr,
     g_wr_data_w              => c_dp_data_w,
     g_wr_fifo_depth          => c_wr_fifo_depth,  -- >=16 AND >g_tech_ddr.maxburstsize, defined at DDR side of the FIFO.
     g_rd_fifo_depth          => c_rd_fifo_depth,  -- >=16 AND >g_tech_ddr.maxburstsize, defined at DDR side of the FIFO. 
@@ -284,12 +291,15 @@ BEGIN
     ctlr_init_done     => ctlr_init_done,
     ctlr_rdy           => ctlr_rdy,
 
+    -- Driver clock domain
+    dvr_clk            => dvr_clk,
+    dvr_rst            => dvr_rst,    
     dvr_en             => dvr_en,
     dvr_wr_not_rd      => dvr_wr_not_rd,
     dvr_done           => dvr_done,
     dvr_start_addr     => dvr_start_addr,
     dvr_end_addr       => dvr_end_addr,
-    dvr_flush_en       => dvr_flush_en,
+    dvr_wr_flush_en    => dvr_wr_flush_en,
     dvr_wr_fifo_usedw  => dvr_wr_fifo_usedw,
     
     -- Write FIFO clock domain
diff --git a/libraries/io/ddr/tb/vhdl/tb_tb_io_ddr.vhd b/libraries/io/ddr/tb/vhdl/tb_tb_io_ddr.vhd
index 080b876a5e..ac4f8ff367 100644
--- a/libraries/io/ddr/tb/vhdl/tb_tb_io_ddr.vhd
+++ b/libraries/io/ddr/tb/vhdl/tb_tb_io_ddr.vhd
@@ -39,12 +39,18 @@ ARCHITECTURE tb OF tb_tb_io_ddr IS
 
 BEGIN
 
-  -- g_technology       : NATURAL := c_tech_select_default;
-  -- g_nof_repeat       : NATURAL := 2;
-  -- g_wr_flush_mode    : STRING := "SYN"  -- "VAL", "SOP", "SYN"
-
-  u_fill_wrfifo_on_next_valid     : ENTITY work.tb_io_ddr GENERIC MAP (c_tech_select_default, 2, "VAL");
-  u_fill_wrfifo_on_next_sop       : ENTITY work.tb_io_ddr GENERIC MAP (c_tech_select_default, 2, "SOP");
-  u_fill_wrfifo_on_next_sync      : ENTITY work.tb_io_ddr GENERIC MAP (c_tech_select_default, 2, "SYN");
+  -- g_technology           : NATURAL := c_tech_select_default;
+  -- g_ctlr_ref_clk_period  : TIME := 5 ns;     -- 200 MHz
+  -- g_dvr_clk_period       : TIME := 20 ns;    -- 50 ns
+  -- g_dp_clk_period        : TIME := 5000 ps;  -- 200 MHz
+  -- g_nof_repeat           : NATURAL := 2;
+  -- g_wr_flush_mode        : STRING := "SYN"  -- "VAL", "SOP", "SYN"
+
+  u_fill_wrfifo_on_next_valid     : ENTITY work.tb_io_ddr GENERIC MAP (c_tech_select_default, 5 ns, 5 ns, 5 ns, 2, "VAL");
+  u_fill_wrfifo_on_next_sop       : ENTITY work.tb_io_ddr GENERIC MAP (c_tech_select_default, 5 ns, 5 ns, 5 ns, 2, "SOP");
+  u_fill_wrfifo_on_next_sync      : ENTITY work.tb_io_ddr GENERIC MAP (c_tech_select_default, 5 ns, 5 ns, 5 ns, 2, "SYN");
+  
+  u_cross_dvr_to_faster_ctlr      : ENTITY work.tb_io_ddr GENERIC MAP (c_tech_select_default, 5 ns, 20 ns, 5 ns, 1, "VAL");
+  u_cross_dvr_to_slower_ctlr      : ENTITY work.tb_io_ddr GENERIC MAP (c_tech_select_default, 5 ns,  1 ns, 5 ns, 1, "VAL");
   
 END tb;
-- 
GitLab