diff --git a/modules/wishbone/wb_clock_crossing/Manifest.py b/modules/wishbone/wb_clock_crossing/Manifest.py index ae25624eaa24e10cdda3f655c7ccb0d1885acf7b..5128dcf61101f2489e2616971ec21b7cd359badb 100644 --- a/modules/wishbone/wb_clock_crossing/Manifest.py +++ b/modules/wishbone/wb_clock_crossing/Manifest.py @@ -1 +1,4 @@ -files = [ "xwb_clock_crossing.vhd" ]; +files = [ + "xwb_clock_crossing.vhd", + "xwb_clock_bridge.vhd", +] diff --git a/modules/wishbone/wb_clock_crossing/xwb_clock_bridge.vhd b/modules/wishbone/wb_clock_crossing/xwb_clock_bridge.vhd new file mode 100644 index 0000000000000000000000000000000000000000..0ab8c7d1d1b3efe71716a868a98da63d498964c5 --- /dev/null +++ b/modules/wishbone/wb_clock_crossing/xwb_clock_bridge.vhd @@ -0,0 +1,297 @@ +-------------------------------------------------------------------------------- +-- CERN BE-CO-HT +-- General Cores Library +-- https://www.ohwr.org/projects/general-cores +-------------------------------------------------------------------------------- +-- +-- unit name: xwb_clock_bridge +-- +-- description: Cross clock-domain wishbone adapter +-- +-------------------------------------------------------------------------------- +-- Copyright CERN 2018 +-------------------------------------------------------------------------------- +-- Copyright and related rights are licensed under the Solderpad Hardware +-- 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://solderpad.org/licenses/SHL-2.0. +-- Unless required by applicable law or agreed to in writing, software, +-- hardware and materials distributed under this 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. +-------------------------------------------------------------------------------- + +-- IMPORTANT: If you reset one clock domain, you must reset BOTH! +-- Release of the reset lines may be arbitrarily out-of-phase + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; +use work.wishbone_pkg.all; +use work.genram_pkg.all; + +entity xwb_clock_bridge is + generic ( + -- Slave port (from WB master) mode + g_SLAVE_PORT_WB_MODE : t_wishbone_interface_mode := PIPELINED; + -- Master port (to WB slave) mode + g_MASTER_PORT_WB_MODE : t_wishbone_interface_mode := PIPELINED; + -- Depth of sync FIFOs. Increase it to improve performance + -- in case of longer pipelined bursts + g_SIZE : natural := 16); + port ( + -- Slave port (from WB master) + slave_clk_i : in std_logic; + slave_rst_n_i : in std_logic; + slave_i : in t_wishbone_slave_in; + slave_o : out t_wishbone_slave_out; + -- Master port (to WB slave) + master_clk_i : in std_logic; + master_rst_n_i : in std_logic; + master_i : in t_wishbone_master_in; + master_o : out t_wishbone_master_out); +end xwb_clock_bridge; + +architecture arch of xwb_clock_bridge is + + -- size of counter for keeping track of pending WB transactions. + constant c_PEND_WB_CNT_LEN : positive := 16; + + constant c_S2M_FIFO_WIDTH : natural := + c_WISHBONE_ADDRESS_WIDTH + + C_WISHBONE_DATA_WIDTH + + 4 + -- SEL + 1; -- WE + + constant c_S2M_DATA_OFFSET : natural := c_S2M_FIFO_WIDTH - c_WISHBONE_ADDRESS_WIDTH - 1; + + subtype t_s2m_addr_range is + natural range c_S2M_FIFO_WIDTH - 1 downto c_S2M_DATA_OFFSET + 1; + + subtype t_s2m_data_range is + natural range c_S2M_DATA_OFFSET downto 5; + + subtype t_s2m_fifo_data is + std_logic_vector(c_S2M_FIFO_WIDTH - 1 downto 0); + + constant c_M2S_FIFO_WIDTH : natural := + C_WISHBONE_DATA_WIDTH + + 1 + -- ACK + 1 + -- ERR + 1; -- RTY + + subtype t_m2s_data_range is + natural range c_M2S_FIFO_WIDTH - 1 downto 3; + + subtype t_m2s_fifo_data is + std_logic_vector(c_M2S_FIFO_WIDTH - 1 downto 0); + + -- signals in the slave port clock domain (SDM). + signal sdm_fifo_in : t_s2m_fifo_data; + signal sdm_s2m_full : std_logic; + signal sdm_wr_en : std_logic; + signal sdm_fifo_out : t_m2s_fifo_data; + signal sdm_m2s_empty : std_logic; + signal sdm_rd_en : std_logic; + signal sdm_rd_en_d1 : std_logic; + signal sdm_wb_in : t_wishbone_slave_in; + signal sdm_wb_out : t_wishbone_slave_out; + + -- signals in the master port clock domain (MDM). + signal mdm_fifo_in : t_m2s_fifo_data; + signal mdm_wr_en : std_logic; + signal mdm_fifo_out : t_s2m_fifo_data; + signal mdm_s2m_empty : std_logic; + signal mdm_rd_en : std_logic; + signal mdm_rd_en_d1 : std_logic; + signal mdm_wb_in : t_wishbone_master_in; + signal mdm_wb_out : t_wishbone_master_out; + signal mdm_m2s_count : std_logic_vector(f_log2_size(g_SIZE)-1 downto 0); + signal mdm_pend_tr : std_logic; + signal mdm_pend_cnt : unsigned(f_log2_size(g_SIZE)-1 downto 0); + signal mdm_block_en : std_logic; + signal mdm_block_lim : unsigned(f_log2_size(g_SIZE)-1 downto 0); + signal mdm_wb_new_tr : std_logic; + +begin -- architecture arch + + ------------------------------------------------------------------------------ + -- Slave and Master port adapters to convert from user's interface to + -- PIPELINED mode because all the sync logic is based on the pipelined + -- protocol. + -- Address granularity is irrelevant for the clock bridge, we assume it is + -- BYTE and just pass it over. + ------------------------------------------------------------------------------ + + cmp_wb_slave_port_adapter : wb_slave_adapter + generic map ( + g_slave_use_struct => TRUE, + g_slave_mode => g_SLAVE_PORT_WB_MODE, + g_slave_granularity => BYTE, + g_master_use_struct => TRUE, + g_master_mode => PIPELINED, + g_master_granularity => BYTE) + port map ( + clk_sys_i => slave_clk_i, + rst_n_i => slave_rst_n_i, + slave_i => slave_i, + slave_o => slave_o, + master_i => sdm_wb_out, + master_o => sdm_wb_in); + + cmp_wb_master_port_adapter : wb_slave_adapter + generic map ( + g_slave_use_struct => TRUE, + g_slave_mode => PIPELINED, + g_slave_granularity => BYTE, + g_master_use_struct => TRUE, + g_master_mode => g_MASTER_PORT_WB_MODE, + g_master_granularity => BYTE) + port map ( + clk_sys_i => master_clk_i, + rst_n_i => master_rst_n_i, + slave_i => mdm_wb_out, + slave_o => mdm_wb_in, + master_i => master_i, + master_o => master_o); + + ------------------------------------------------------------------------------ + -- S2M FIFO: from slave port to master port (transmits transcactions to WB slaves). + -- Including all signal assignments in the slave port clock domain (SDM). + ------------------------------------------------------------------------------ + + cmp_s2m_fifo : generic_async_fifo_dual_rst + generic map ( + g_DATA_WIDTH => c_S2M_FIFO_WIDTH, + g_SIZE => g_SIZE) + port map ( + rst_wr_n_i => slave_rst_n_i, + clk_wr_i => slave_clk_i, + d_i => sdm_fifo_in, + we_i => sdm_wr_en, + wr_full_o => sdm_s2m_full, + rst_rd_n_i => master_rst_n_i, + clk_rd_i => master_clk_i, + q_o => mdm_fifo_out, + rd_i => mdm_rd_en, + rd_empty_o => mdm_s2m_empty); + + -- Write to S2M FIFO whenever the WB master has asserted CYC and STB + -- and the S2M FIFO is not full (otherwise, we stall). + sdm_wr_en <= not sdm_s2m_full and sdm_wb_in.cyc and sdm_wb_in.stb; + + -- Map sdm_wb_in to sdm_fifo_in + sdm_fifo_in(t_s2m_addr_range) <= sdm_wb_in.adr; + sdm_fifo_in(t_s2m_data_range) <= sdm_wb_in.dat; + sdm_fifo_in(4 downto 1) <= sdm_wb_in.sel; + sdm_fifo_in(0) <= sdm_wb_in.we; + + -- Read from M2S FIFO (in other words, update state of ACK, ERR, RTY and + -- DAT signals) whenever it is not empty. + sdm_rd_en <= not sdm_m2s_empty; + + -- Always stall when the S2M FIFO is full. + sdm_wb_out.stall <= sdm_s2m_full; + + -- DAT is always output, since it is validated by ACK/ERR/RTY. + sdm_wb_out.dat <= sdm_fifo_out(t_m2s_data_range); + + -- Delay sdm_rd_en by one cycle to align it with the data on sdm_fifo_out. + p_sdm_rd_en_d1 : process (slave_clk_i) is + begin + if rising_edge(slave_clk_i) then + sdm_rd_en_d1 <= sdm_rd_en; + end if; + end process p_sdm_rd_en_d1; + + -- Acknowledge with a one clock cycle wide pulse per entry in M2S FIFO + -- (M2S FIFO is read whenever it is not empty). Data is made available one + -- clock cycle after sdm_rd_en is asserted. + sdm_wb_out.ack <= sdm_wb_in.cyc and sdm_fifo_out(2) and sdm_rd_en_d1; + sdm_wb_out.err <= sdm_wb_in.cyc and sdm_fifo_out(1) and sdm_rd_en_d1; + sdm_wb_out.rty <= sdm_wb_in.cyc and sdm_fifo_out(0) and sdm_rd_en_d1; + + ------------------------------------------------------------------------------ + -- M2S FIFO: from master port to slave port (receives replies from WB slaves) + ------------------------------------------------------------------------------ + + cmp_m2s_fifo : generic_async_fifo_dual_rst + generic map ( + g_DATA_WIDTH => c_M2S_FIFO_WIDTH, + g_WITH_WR_FULL => FALSE, + g_WITH_WR_COUNT => TRUE, + g_SIZE => g_SIZE) + port map ( + rst_wr_n_i => master_rst_n_i, + clk_wr_i => master_clk_i, + d_i => mdm_fifo_in, + we_i => mdm_wr_en, + wr_count_o => mdm_m2s_count, + rst_rd_n_i => slave_rst_n_i, + clk_rd_i => slave_clk_i, + q_o => sdm_fifo_out, + rd_i => sdm_rd_en, + rd_empty_o => sdm_m2s_empty); + + -- Read from S2M FIFO (in other words, update state of ADR, DAT, SEL and WE + -- signals) whenever the S2M FIFO is not empty and there is enough space in + -- the M2S_FIFO to push the new reply, taking into account any pending + -- transactions (not yet ack'ed by the WB slave). + mdm_rd_en <= not (mdm_s2m_empty or mdm_block_en) and + not (mdm_pend_tr and mdm_wb_in.stall); + + -- Assert CYC whenever there are pending WB transactions. + mdm_wb_out.cyc <= mdm_pend_tr; + + -- Delay mdm_rd_en by one cycle to align it with the data on mdm_fifo_out. + -- Delay STB and STALL by one cycle to properly detect new transactions. + p_mdm_d1 : process (master_clk_i) is + begin + if rising_edge(master_clk_i) then + mdm_rd_en_d1 <= mdm_rd_en; + mdm_wb_new_tr <= mdm_wb_out.stb and mdm_wb_in.stall; + end if; + end process p_mdm_d1; + + -- Strobe with a one clock cycle wide pulse per entry in S2M FIFO, unless + -- the WB slave stalls, in which case the STB is extended. + mdm_wb_out.stb <= mdm_rd_en_d1 or mdm_wb_new_tr; + + -- ADR, DAT, SEL and WE are always output, since they are validated by STB. + mdm_wb_out.adr <= mdm_fifo_out(t_s2m_addr_range); + mdm_wb_out.dat <= mdm_fifo_out(t_s2m_data_range); + mdm_wb_out.sel <= mdm_fifo_out(4 downto 1); + mdm_wb_out.we <= mdm_fifo_out(0); + + -- Map mdm_wb_in to mdm_fifo_in + mdm_fifo_in(t_m2s_data_range) <= mdm_wb_in.dat; + mdm_fifo_in(2) <= mdm_wb_in.ack; + mdm_fifo_in(1) <= mdm_wb_in.err; + mdm_fifo_in(0) <= mdm_wb_in.rty; + + -- Write to M2S FIFO whenever the WB slave terminates a cycle in any way. + mdm_wr_en <= mdm_wb_out.cyc and (mdm_wb_in.ack or mdm_wb_in.err or mdm_wb_in.rty); + + -- Keep a count of pending WB transactions. + p_wb_pending_cnt : process (master_clk_i) is + begin + if rising_edge(master_clk_i) then + if master_rst_n_i = '0' then + mdm_pend_cnt <= (others => '0'); + elsif mdm_rd_en = '1' and mdm_wr_en = '0' then + mdm_pend_cnt <= mdm_pend_cnt + 1; + elsif mdm_rd_en = '0' and mdm_wr_en = '1' then + mdm_pend_cnt <= mdm_pend_cnt - 1; + end if; + end if; + end process p_wb_pending_cnt; + + mdm_pend_tr <= '0' when mdm_pend_cnt = 0 else '1'; + + mdm_block_lim <= to_unsigned(g_SIZE-1, mdm_m2s_count'length) - unsigned(mdm_m2s_count); + mdm_block_en <= '0' when mdm_pend_cnt < mdm_block_lim else '1'; + +end architecture arch; diff --git a/modules/wishbone/wishbone_pkg.vhd b/modules/wishbone/wishbone_pkg.vhd index 14cdb2aabca2155083061858137e360cfb391817..9800a003b3c63a47692b61960c53e9bb1e8becec 100644 --- a/modules/wishbone/wishbone_pkg.vhd +++ b/modules/wishbone/wishbone_pkg.vhd @@ -559,6 +559,22 @@ package wishbone_pkg is slave_stall_i : in std_logic := '0'); end component; + component xwb_clock_bridge is + generic ( + g_SLAVE_PORT_WB_MODE : t_wishbone_interface_mode := PIPELINED; + g_MASTER_PORT_WB_MODE : t_wishbone_interface_mode := PIPELINED; + g_SIZE : natural := 16); + port ( + slave_clk_i : in std_logic; + slave_rst_n_i : in std_logic; + slave_i : in t_wishbone_slave_in; + slave_o : out t_wishbone_slave_out; + master_clk_i : in std_logic; + master_rst_n_i : in std_logic; + master_i : in t_wishbone_master_in; + master_o : out t_wishbone_master_out); + end component xwb_clock_bridge; + -- g_size is in words function f_xwb_dpram(g_size : natural) return t_sdb_device; component xwb_dpram