------------------------------------------------------------------------------- -- -- Copyright (C) 2011 -- 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. -- -- 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/>. -- ------------------------------------------------------------------------------- -- RO read only (no VHDL present to access HW in write mode) -- WO write only (no VHDL present to access HW in read mode) -- WE write event (=WO) -- WR write control, read control -- RW read status, write control -- RC read, clear on read -- FR FIFO read -- FW FIFO write -- -- wi Bits R/W Name Default Description |REG_DDR3| -- ============================================================================= -- 0 [0] WE burstbegin 0x0 -- 1 [0] WO wr_not_rd 0x0 -- 2 [0] RO done 0x1 -- 3 [1..0] RO cal_result 0x1 cal_fail & cal_ok -- 4 [0] RO waitrequest_n 0x1 -- 5 [31..0] WO address 0x0 -- 6 [31..0] WO burstsize 0x0 -- 7 [0] WR flush 0x0 -- ============================================================================= LIBRARY IEEE, common_lib, diag_lib; USE IEEE.STD_LOGIC_1164.ALL; USE common_lib.common_pkg.ALL; USE common_lib.common_mem_pkg.ALL; USE diag_lib.diag_pkg.ALL; ENTITY io_ddr_reg IS PORT ( -- Clocks and reset mm_rst : IN STD_LOGIC; -- reset synchronous with mm_clk mm_clk : IN STD_LOGIC; -- memory-mapped bus clock dp_rst : IN STD_LOGIC; -- reset synchronous with dp_clk dp_clk : IN STD_LOGIC; -- other clock domain clock -- Memory Mapped Slave in mm_clk domain sla_in : IN t_mem_mosi; -- actual ranges defined by c_mm_reg sla_out : OUT t_mem_miso; -- actual ranges defined by c_mm_reg -- MM registers in st_clk domain dvr_miso : IN t_mem_ctlr_miso; dvr_mosi : OUT t_mem_ctlr_mosi ); END io_ddr_reg; ARCHITECTURE rtl OF io_ddr_reg IS CONSTANT c_mm_reg : t_c_mem := (latency => 1, adr_w => ceil_log2(8), dat_w => c_word_w, -- Use MM bus data width = c_word_w = 32 for all MM registers nof_dat => 8, init_sl => '0'); -- Registers in mm_clk domain SIGNAL mm_dvr_miso : t_mem_ctlr_miso; SIGNAL mm_dvr_mosi : t_mem_ctlr_mosi; BEGIN ------------------------------------------------------------------------------ -- MM register access in the mm_clk domain -- . Hardcode the shared MM slave register directly in RTL instead of using -- the common_reg_r_w instance. Directly using RTL is easier when the large -- MM register has multiple different fields and with different read and -- write options per field in one MM register. ------------------------------------------------------------------------------ p_mm_reg : PROCESS (mm_rst, mm_clk) BEGIN IF mm_rst = '1' THEN sla_out <= c_mem_miso_rst; mm_dvr_mosi <= c_mem_ctlr_mosi_rst; ELSIF rising_edge(mm_clk) THEN -- Read access defaults sla_out.rdval <= '0'; mm_dvr_mosi.burstbegin <= '0'; -- Write access: set register value IF sla_in.wr = '1' THEN CASE TO_UINT(sla_in.address(c_mm_reg.adr_w-1 DOWNTO 0)) IS -- Write Block Sync WHEN 0 => mm_dvr_mosi.burstbegin <= sla_in.wrdata(0); WHEN 1 => mm_dvr_mosi.wr <= sla_in.wrdata(0); mm_dvr_mosi.rd <= NOT(sla_in.wrdata(0)); WHEN 5 => mm_dvr_mosi.address <= sla_in.wrdata(c_mem_ctlr_address_w-1 DOWNTO 0); WHEN 6 => mm_dvr_mosi.burstsize <= sla_in.wrdata(c_mem_ctlr_burstsize_w-1 DOWNTO 0); WHEN 7 => mm_dvr_mosi.flush <= sla_in.wrdata(0); WHEN OTHERS => NULL; -- unused MM addresses END CASE; -- Read access: get register value ELSIF sla_in.rd = '1' THEN sla_out <= c_mem_miso_rst; -- set unused rddata bits to '0' when read sla_out.rdval <= '1'; -- c_mm_reg.latency = 1 CASE TO_UINT(sla_in.address(c_mm_reg.adr_w-1 DOWNTO 0)) IS -- Read Block Sync WHEN 2 => sla_out.rddata(0) <= mm_dvr_miso.done; WHEN 3 => sla_out.rddata(1 DOWNTO 0) <= mm_dvr_miso.cal_fail & mm_dvr_miso.cal_ok; WHEN 4 => sla_out.rddata(0) <= mm_dvr_miso.waitrequest_n; WHEN 7 => sla_out.rddata(0) <= mm_dvr_mosi.flush; WHEN OTHERS => NULL; -- unused MM addresses END CASE; END IF; END IF; END PROCESS; ------------------------------------------------------------------------------ -- Transfer register value between mm_clk and st_clk domain. -- If the function of the register ensures that the value will not be used -- immediately when it was set, then the transfer between the clock domains -- can be done by wires only. Otherwise if the change in register value can -- have an immediate effect then the bit or word value needs to be transfered -- using: -- -- . common_async --> for single-bit level signal -- . common_spulse --> for single-bit pulse signal -- . common_reg_cross_domain --> for a multi-bit (a word) signal -- -- Typically always use a crossing component for the single bit signals (to -- be on the safe side) and only use a crossing component for the word -- signals if it is necessary (to avoid using more logic than necessary). ------------------------------------------------------------------------------ u_spulse_en_evt : ENTITY common_lib.common_spulse PORT MAP ( in_rst => mm_rst, in_clk => mm_clk, in_pulse => mm_dvr_mosi.burstbegin, in_busy => OPEN, out_rst => dp_rst, out_clk => dp_clk, out_pulse => dvr_mosi.burstbegin ); u_async_wr : ENTITY common_lib.common_async GENERIC MAP ( g_rst_level => '0' ) PORT MAP ( rst => dp_rst, clk => dp_clk, din => mm_dvr_mosi.wr, dout => dvr_mosi.wr ); u_async_rd : ENTITY common_lib.common_async GENERIC MAP ( g_rst_level => '0' ) PORT MAP ( rst => dp_rst, clk => dp_clk, din => mm_dvr_mosi.rd, dout => dvr_mosi.rd ); u_async_flush : ENTITY common_lib.common_async GENERIC MAP ( g_rst_level => '0' ) PORT MAP ( rst => dp_rst, clk => dp_clk, din => mm_dvr_mosi.flush, dout => dvr_mosi.flush ); u_async_done : ENTITY common_lib.common_async GENERIC MAP ( g_rst_level => '0' ) PORT MAP ( rst => mm_rst, clk => mm_clk, din => dvr_miso.done, dout => mm_dvr_miso.done ); u_async_waitrequest_n : ENTITY common_lib.common_async GENERIC MAP ( g_rst_level => '0' ) PORT MAP ( rst => mm_rst, clk => mm_clk, din => dvr_miso.waitrequest_n, dout => mm_dvr_miso.waitrequest_n ); mm_dvr_miso.cal_fail <= dvr_miso.cal_fail; mm_dvr_miso.cal_ok <= dvr_miso.cal_ok; u_cross_domain_addr : ENTITY common_lib.common_reg_cross_domain PORT MAP ( in_rst => mm_rst, in_clk => mm_clk, in_dat => mm_dvr_mosi.address, in_done => OPEN, out_rst => dp_rst, out_clk => dp_clk, out_dat => dvr_mosi.address, out_new => OPEN ); u_cross_domain_burstsize : ENTITY common_lib.common_reg_cross_domain PORT MAP ( in_rst => mm_rst, in_clk => mm_clk, in_dat => mm_dvr_mosi.burstsize, in_done => OPEN, out_rst => dp_rst, out_clk => dp_clk, out_dat => dvr_mosi.burstsize, out_new => OPEN ); END rtl;