Skip to content
Snippets Groups Projects

WIP: Resolve L2SDP-49

Closed Pieter Donker requested to merge L2SDP-49 into master
All threads resolved!
Files
9
-------------------------------------------------------------------------------
--
-- Copyright (C) 2012
-- 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
-- ====================================================================================
-- 0 [0] RW dp_on 0x0 WR '1' enables DP (on PPS when dp_on_pps=1)
-- RD '0' = DP off; RD '1' = DP on.
-- [1] WR dp_on_pps 0x0
-- 1 [31..0] WR nof_clk_per_sync 0x0
-- 2 [31..0] RW bsn[31..0] 0x0 write bsn_init, read current bsn
-- 3 [31..0] RW bsn[63..32] 0x0 write bsn_init, read current bsn
-- 4 [31..0] RW bsn_time_offset 0x0
-- ====================================================================================
LIBRARY IEEE, common_lib;
USE IEEE.STD_LOGIC_1164.ALL;
USE common_lib.common_pkg.ALL;
USE common_lib.common_mem_pkg.ALL;
ENTITY dp_bsn_source_reg_v2 IS
GENERIC (
g_cross_clock_domain : BOOLEAN := TRUE; -- use FALSE when mm_clk and st_clk are the same, else use TRUE to cross the clock domain
g_nof_clk_per_sync : NATURAL := 200 * 10**6
);
PORT (
-- Clocks and reset
mm_rst : IN STD_LOGIC; -- reset synchronous with mm_clk
mm_clk : IN STD_LOGIC; -- memory-mapped bus clock
st_rst : IN STD_LOGIC; -- reset synchronous with st_clk
st_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
st_on : OUT STD_LOGIC := '1'; -- level
st_on_pps : OUT STD_LOGIC := '0'; -- level
st_on_status : IN STD_LOGIC;
st_nof_clk_per_sync : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); -- nof block per sync
st_bsn_init : OUT STD_LOGIC_VECTOR; -- wr init BSN
st_current_bsn : IN STD_LOGIC_VECTOR; -- rd current BSN
st_bsn_time_offset : OUT STD_LOGIC_VECTOR;
st_current_bsn_time_offset : IN STD_LOGIC_VECTOR
);
END dp_bsn_source_reg_v2;
ARCHITECTURE rtl OF dp_bsn_source_reg_v2 IS
CONSTANT c_bsn_w : NATURAL := st_bsn_init'LENGTH;
CONSTANT c_bsn_time_offset_w : NATURAL := st_bsn_time_offset'LENGTH;
-- Define the actual size of the MM slave register
CONSTANT c_mm_reg : t_c_mem := (latency => 1,
adr_w => 3,
dat_w => c_word_w, -- Use MM bus data width = c_word_w = 32 for all MM registers
nof_dat => 3**2,
init_sl => '0');
-- Registers in mm_clk domain
SIGNAL mm_on : STD_LOGIC;
SIGNAL mm_on_pps : STD_LOGIC;
SIGNAL mm_on_ctrl : STD_LOGIC_VECTOR(1 DOWNTO 0); -- = mm_on_pps & mm_on
SIGNAL st_on_ctrl : STD_LOGIC_VECTOR(1 DOWNTO 0); -- = st_on_pps & st_on
SIGNAL mm_on_status : STD_LOGIC;
SIGNAL mm_nof_clk_per_sync : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
SIGNAL mm_bsn_init : STD_LOGIC_VECTOR(c_longword_w-1 DOWNTO 0);
SIGNAL mm_bsn_init_wr : STD_LOGIC;
SIGNAL mm_current_bsn : STD_LOGIC_VECTOR(c_longword_w-1 DOWNTO 0) := (OTHERS=>'0');
SIGNAL mm_current_bsn_hi : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0) := (OTHERS=>'0');
SIGNAL mm_bsn_time_offset : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0');
SIGNAL mm_bsn_time_offset_wr : STD_LOGIC;
SIGNAL mm_current_bsn_time_offset : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0');
-- Registers in st_clk domain
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
-- Read access
sla_out <= c_mem_miso_rst;
-- Access event, register values
mm_on <= '0';
mm_on_pps <= '0';
mm_nof_clk_per_sync <= TO_UVEC(g_nof_clk_per_sync, c_word_w);
mm_bsn_init <= (OTHERS=>'0');
mm_bsn_init_wr <= '0';
mm_current_bsn_hi <= (OTHERS=>'0');
mm_bsn_time_offset <= (OTHERS=>'0');
mm_bsn_time_offset_wr <= '0';
ELSIF rising_edge(mm_clk) THEN
-- Read access defaults
sla_out.rdval <= '0';
-- Access event defaults
mm_bsn_init_wr <= '0';
mm_bsn_time_offset_wr <= '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_on <= sla_in.wrdata(0);
mm_on_pps <= sla_in.wrdata(1);
WHEN 1 =>
mm_nof_clk_per_sync <= sla_in.wrdata(31 DOWNTO 0);
-- Write init BSN
WHEN 2 =>
mm_bsn_init(31 DOWNTO 0) <= sla_in.wrdata(31 DOWNTO 0);
WHEN 3 =>
mm_bsn_init(63 DOWNTO 32) <= sla_in.wrdata(31 DOWNTO 0);
mm_bsn_init_wr <= '1';
-- write bsn_time_offset
WHEN 4 =>
mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0) <= sla_in.wrdata(c_bsn_time_offset_w-1 DOWNTO 0);
mm_bsn_time_offset_wr <= '1';
WHEN OTHERS => NULL; -- not used 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 0 =>
sla_out.rddata(0) <= mm_on_status;
sla_out.rddata(1) <= mm_on_pps;
WHEN 1 =>
sla_out.rddata(31 DOWNTO 0) <= mm_nof_clk_per_sync;
-- Read current BSN
WHEN 2 =>
sla_out.rddata(31 DOWNTO 0) <= mm_current_bsn(31 DOWNTO 0);
mm_current_bsn_hi <= mm_current_bsn(63 DOWNTO 32); -- first read low part and preserve high part
WHEN 3 =>
sla_out.rddata(31 DOWNTO 0) <= mm_current_bsn_hi;
-- Read current bsn_time_offset
WHEN 4 =>
sla_out.rddata(c_bsn_time_offset_w-1 DOWNTO 0) <= mm_current_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0);
WHEN OTHERS => NULL; -- not used 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 save side) and only use a crossing component for the word
-- signals if it is necessary (to avoid using more logic than necessary).
------------------------------------------------------------------------------
no_cross : IF g_cross_clock_domain = FALSE GENERATE -- so mm_clk = st_clk
st_on <= mm_on;
st_on_pps <= mm_on_pps;
st_nof_clk_per_sync <= mm_nof_clk_per_sync;
st_bsn_init <= mm_bsn_init(c_bsn_w-1 DOWNTO 0);
st_bsn_time_offset <= mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0);
p_st_clk : PROCESS(st_rst, st_clk)
BEGIN
IF st_rst='1' THEN
st_bsn_init <= TO_UVEC(0, c_bsn_w);
st_bsn_time_offset <= TO_UVEC(0, c_bsn_time_offset_w);
ELSIF rising_edge(st_clk) THEN
IF mm_bsn_init_wr='1' THEN
st_bsn_init <= mm_bsn_init(c_bsn_w-1 DOWNTO 0); -- use wr of mm_bsn_init high part for in_new to ensure proper transfer of double word
END IF;
IF mm_bsn_time_offset_wr='1' THEN
st_bsn_time_offset <= mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0); -- use wr of mm_bsn_init high part for in_new to ensure proper transfer of double word
END IF;
END IF;
END PROCESS;
mm_current_bsn(c_bsn_w-1 DOWNTO 0) <= st_current_bsn; -- MM user may read current_bsn twice to avoid small chance that the high part of the double word changed (i.e. incremented)
mm_current_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0) <= st_current_bsn_time_offset;
END GENERATE; -- no_cross
gen_cross : IF g_cross_clock_domain = TRUE GENERATE
-- Block sync registers
u_dp_on_ctrl : ENTITY common_lib.common_reg_cross_domain
PORT MAP (
in_rst => mm_rst,
in_clk => mm_clk,
in_dat => mm_on_ctrl,
in_done => OPEN, -- pulses when no more pending in_new
out_rst => st_rst,
out_clk => st_clk,
out_dat => st_on_ctrl,
out_new => OPEN
);
mm_on_ctrl(0) <= mm_on;
mm_on_ctrl(1) <= mm_on_pps;
st_on <= st_on_ctrl(0);
st_on_pps <= st_on_ctrl(1);
u_mm_on_status : ENTITY common_lib.common_async
GENERIC MAP (
g_rst_level => '0'
)
PORT MAP (
rst => mm_rst,
clk => mm_clk,
din => st_on_status,
dout => mm_on_status
);
-- write occurs with sufficient margin before it is used, still use common_reg_cross_domain nonetheless
u_nof_block_per_sync : ENTITY common_lib.common_reg_cross_domain
PORT MAP (
in_rst => mm_rst,
in_clk => mm_clk,
in_dat => mm_nof_clk_per_sync,
in_done => OPEN, -- pulses when no more pending in_new
out_rst => st_rst,
out_clk => st_clk,
out_dat => st_nof_clk_per_sync,
out_new => OPEN
);
-- write occurs with sufficient margin before it is used, still use common_reg_cross_domain nonetheless
u_bsn_init : ENTITY common_lib.common_reg_cross_domain
PORT MAP (
in_rst => mm_rst,
in_clk => mm_clk,
in_new => mm_bsn_init_wr, -- use wr of mm_bsn_init high part for in_new to ensure proper transfer of double word
in_dat => mm_bsn_init(c_bsn_w-1 DOWNTO 0),
in_done => OPEN, -- pulses when no more pending in_new
out_rst => st_rst,
out_clk => st_clk,
out_dat => st_bsn_init,
out_new => OPEN
);
-- thanks to mm_current_bsn_hi the double word can be read reliably
u_current_bsn : ENTITY common_lib.common_reg_cross_domain
PORT MAP (
in_rst => st_rst,
in_clk => st_clk,
in_new => '1', -- could use t_dp_sosi sop here to indicate in_new, but using default '1' is fine too
in_dat => st_current_bsn,
in_done => OPEN, -- pulses when no more pending in_new
out_rst => mm_rst,
out_clk => mm_clk,
out_dat => mm_current_bsn(c_bsn_w-1 DOWNTO 0),
out_new => OPEN
);
-- write occurs with sufficient margin before it is used, still use common_reg_cross_domain nonetheless
u_bsn_time_offset : ENTITY common_lib.common_reg_cross_domain
PORT MAP (
in_rst => mm_rst,
in_clk => mm_clk,
in_new => mm_bsn_time_offset_wr, -- use wr of mm_bsn_time_offset high part for in_new to ensure proper transfer of double word
in_dat => mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0),
in_done => OPEN, -- pulses when no more pending in_new
out_rst => st_rst,
out_clk => st_clk,
out_dat => st_bsn_time_offset,
out_new => OPEN
);
-- write occurs with sufficient margin before it is used, still use common_reg_cross_domain nonetheless
u_current_bsn_offset : ENTITY common_lib.common_reg_cross_domain
PORT MAP (
in_rst => st_rst,
in_clk => st_clk,
in_new => '1',
in_dat => st_current_bsn_time_offset,
in_done => OPEN, -- pulses when no more pending in_new
out_rst => mm_rst,
out_clk => mm_clk,
out_dat => mm_current_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0),
out_new => OPEN
);
END GENERATE; -- gen_cross
END rtl;
Loading