Co-authored-by: Andrew Moch <Andrew.Moch@ni.com> Co-authored-by: Daniel Jepson <daniel.jepson@ni.com> Co-authored-by: Javier Valenzuela <javier.valenzuela@ni.com> Co-authored-by: Joerg Hofrichter <joerg.hofrichter@ni.com> Co-authored-by: Kumaran Subramoniam <kumaran.subramoniam@ni.com> Co-authored-by: Max Köhler <max.koehler@ni.com> Co-authored-by: Michael Auchter <michael.auchter@ni.com> Co-authored-by: Paul Butler <paul.butler@ni.com> Co-authored-by: Wade Fife <wade.fife@ettus.com> Co-authored-by: Hector Rubio <hrubio@ni.com> Original-commit: 6d3765605262016a80f71e36357f749ea35cbe5a
144 lines
4.0 KiB
VHDL
144 lines
4.0 KiB
VHDL
--
|
|
-- Copyright 2021 Ettus Research, a National Instruments Brand
|
|
--
|
|
-- SPDX-License-Identifier: LGPL-3.0-or-later
|
|
--
|
|
-- Module: dac_1_3_clk_converter
|
|
--
|
|
-- Description:
|
|
--
|
|
-- This module transfers data from s_axis_aclk to m_axis_aclk. m_axis_aclk
|
|
-- must be three times the frequency of s_axis_aclk, and the two clocks must
|
|
-- be related (this module requires timing closure across the clock domain
|
|
-- boundary).
|
|
--
|
|
|
|
library IEEE;
|
|
use IEEE.std_logic_1164.all;
|
|
|
|
entity dac_1_3_clk_converter is
|
|
port(
|
|
s_axis_aclk : in std_logic;
|
|
s_axis_aresetn : in std_logic;
|
|
s_axis_tvalid : in std_logic;
|
|
s_axis_tdata : in std_logic_vector(31 downto 0);
|
|
s_axis_tready : out std_logic := '1';
|
|
|
|
m_axis_aclk : in std_logic;
|
|
m_axis_aresetn : in std_logic;
|
|
m_axis_tready : in std_logic;
|
|
m_axis_tdata : out std_logic_vector(31 downto 0);
|
|
m_axis_tvalid : out std_logic
|
|
);
|
|
end entity dac_1_3_clk_converter;
|
|
|
|
architecture RTL of dac_1_3_clk_converter is
|
|
|
|
-- I was unable to think of a simple implementation that implements a correct
|
|
-- AXIS handshake on both ports. All my ideas became equivalent to a two
|
|
-- clock FIFO (although the clocks are synchronous, so the write-to-read
|
|
-- latency would have been certain).
|
|
--
|
|
-- We don't expect the DAC to ever hold off incoming data, and dac_100m_bd
|
|
-- already has the AXIS handshake disconnected: the FIR is configured to
|
|
-- disallow back pressure - it has no m_axis_data_tready pin.
|
|
--
|
|
-- I'm going with the simple, but not strictly correct, implementation.
|
|
-- s_axis_tready will be constantly true, even when it shouldn't be. The
|
|
-- bottom line is this component is likely useless for any application but
|
|
-- dac_100m_bd.
|
|
|
|
type output_fsm is (
|
|
idle,
|
|
got_data,
|
|
-- The recovery state of delay ensures that we don't re-use an old input
|
|
-- valid signal (remember the output clock is 3x the frequency of the input
|
|
-- clock)
|
|
recovery
|
|
);
|
|
|
|
subtype word is std_logic_vector(s_axis_tdata'range);
|
|
signal output_state_mclk : output_fsm;
|
|
signal axis_tdata_sclk : word;
|
|
signal axis_tvalid_sclk : std_logic;
|
|
|
|
signal axis_tdata_mclk : word;
|
|
signal axis_tvalid_mclk : std_logic;
|
|
|
|
begin
|
|
|
|
s_axis_tready <= '1';
|
|
|
|
input_valid_register:
|
|
process(s_axis_aclk, s_axis_aresetn) is
|
|
begin
|
|
if s_axis_aresetn='0' then
|
|
axis_tvalid_sclk <= '0';
|
|
elsif rising_edge(s_axis_aclk) then
|
|
axis_tvalid_sclk <= s_axis_tvalid;
|
|
end if;
|
|
end process;
|
|
|
|
input_data_register:
|
|
process (s_axis_aclk) is
|
|
begin
|
|
if rising_edge(s_axis_aclk) then
|
|
axis_tdata_sclk <= s_axis_tdata;
|
|
end if;
|
|
end process input_data_register;
|
|
|
|
-- These CDC registers will not become metastable because the two clock
|
|
-- domains are related.
|
|
cdc_input_valid_register:
|
|
process (m_axis_aclk, m_axis_aresetn) is
|
|
begin
|
|
if m_axis_aresetn='0' then
|
|
axis_tvalid_mclk <= '0';
|
|
elsif rising_edge(m_axis_aclk) then
|
|
axis_tvalid_mclk <= axis_tvalid_sclk;
|
|
end if;
|
|
end process cdc_input_valid_register;
|
|
|
|
cdc_input_data_register:
|
|
process (m_axis_aclk) is
|
|
begin
|
|
if rising_edge(m_axis_aclk) then
|
|
axis_tdata_mclk <= axis_tdata_sclk;
|
|
end if;
|
|
end process cdc_input_data_register;
|
|
|
|
output_data_register:
|
|
process (m_axis_aclk) is
|
|
begin
|
|
if rising_edge(m_axis_aclk) then
|
|
if output_state_mclk=idle then
|
|
m_axis_tdata <= axis_tdata_mclk;
|
|
end if;
|
|
end if;
|
|
end process output_data_register;
|
|
|
|
fsm: process(m_axis_aresetn, m_axis_aclk) is
|
|
begin
|
|
if m_axis_aresetn='0' then
|
|
output_state_mclk <= idle;
|
|
m_axis_tvalid <= '0';
|
|
elsif rising_edge(m_axis_aclk) then
|
|
m_axis_tvalid <= '0';
|
|
case output_state_mclk is
|
|
when idle =>
|
|
if axis_tvalid_mclk='1' then
|
|
output_state_mclk <= got_data;
|
|
end if;
|
|
|
|
when got_data =>
|
|
m_axis_tvalid <= '1';
|
|
output_state_mclk <= recovery;
|
|
|
|
when recovery =>
|
|
output_state_mclk <= idle;
|
|
end case;
|
|
end if;
|
|
end process fsm;
|
|
|
|
end RTL;
|