aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/n3xx/dboards/common/sync/CrossTrigger.vhd
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2020-01-23 16:10:22 -0800
committerMartin Braun <martin.braun@ettus.com>2020-01-28 09:35:36 -0800
commitbafa9d95453387814ef25e6b6256ba8db2df612f (patch)
tree39ba24b5b67072d354775272e687796bb511848d /fpga/usrp3/top/n3xx/dboards/common/sync/CrossTrigger.vhd
parent3075b981503002df3115d5f1d0b97d2619ba30f2 (diff)
downloaduhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.gz
uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.bz2
uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.zip
Merge FPGA repository back into UHD repository
The FPGA codebase was removed from the UHD repository in 2014 to reduce the size of the repository. However, over the last half-decade, the split between the repositories has proven more burdensome than it has been helpful. By merging the FPGA code back, it will be possible to create atomic commits that touch both FPGA and UHD codebases. Continuous integration testing is also simplified by merging the repositories, because it was previously difficult to automatically derive the correct UHD branch when testing a feature branch on the FPGA repository. This commit also updates the license files and paths therein. We are therefore merging the repositories again. Future development for FPGA code will happen in the same repository as the UHD host code and MPM code. == Original Codebase and Rebasing == The original FPGA repository will be hosted for the foreseeable future at its original local location: https://github.com/EttusResearch/fpga/ It can be used for bisecting, reference, and a more detailed history. The final commit from said repository to be merged here is 05003794e2da61cabf64dd278c45685a7abad7ec. This commit is tagged as v4.0.0.0-pre-uhd-merge. If you have changes in the FPGA repository that you want to rebase onto the UHD repository, simply run the following commands: - Create a directory to store patches (this should be an empty directory): mkdir ~/patches - Now make sure that your FPGA codebase is based on the same state as the code that was merged: cd src/fpga # Or wherever your FPGA code is stored git rebase v4.0.0.0-pre-uhd-merge Note: The rebase command may look slightly different depending on what exactly you're trying to rebase. - Create a patch set for your changes versus v4.0.0.0-pre-uhd-merge: git format-patch v4.0.0.0-pre-uhd-merge -o ~/patches Note: Make sure that only patches are stored in your output directory. It should otherwise be empty. Make sure that you picked the correct range of commits, and only commits you wanted to rebase were exported as patch files. - Go to the UHD repository and apply the patches: cd src/uhd # Or wherever your UHD repository is stored git am --directory fpga ~/patches/* rm -rf ~/patches # This is for cleanup == Contributors == The following people have contributed mainly to these files (this list is not complete): Co-authored-by: Alex Williams <alex.williams@ni.com> Co-authored-by: Andrej Rode <andrej.rode@ettus.com> Co-authored-by: Ashish Chaudhari <ashish@ettus.com> Co-authored-by: Ben Hilburn <ben.hilburn@ettus.com> Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com> Co-authored-by: Daniel Jepson <daniel.jepson@ni.com> Co-authored-by: Derek Kozel <derek.kozel@ettus.com> Co-authored-by: EJ Kreinar <ej@he360.com> Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com> Co-authored-by: Ian Buckley <ian.buckley@gmail.com> Co-authored-by: Jörg Hofrichter <joerg.hofrichter@ni.com> Co-authored-by: Jon Kiser <jon.kiser@ni.com> Co-authored-by: Josh Blum <josh@joshknows.com> Co-authored-by: Jonathon Pendlum <jonathan.pendlum@ettus.com> Co-authored-by: Martin Braun <martin.braun@ettus.com> Co-authored-by: Matt Ettus <matt@ettus.com> Co-authored-by: Michael West <michael.west@ettus.com> Co-authored-by: Moritz Fischer <moritz.fischer@ettus.com> Co-authored-by: Nick Foster <nick@ettus.com> Co-authored-by: Nicolas Cuervo <nicolas.cuervo@ettus.com> Co-authored-by: Paul Butler <paul.butler@ni.com> Co-authored-by: Paul David <paul.david@ettus.com> Co-authored-by: Ryan Marlow <ryan.marlow@ettus.com> Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com> Co-authored-by: Sylvain Munaut <tnt@246tNt.com> Co-authored-by: Trung Tran <trung.tran@ettus.com> Co-authored-by: Vidush Vishwanath <vidush.vishwanath@ettus.com> Co-authored-by: Wade Fife <wade.fife@ettus.com>
Diffstat (limited to 'fpga/usrp3/top/n3xx/dboards/common/sync/CrossTrigger.vhd')
-rw-r--r--fpga/usrp3/top/n3xx/dboards/common/sync/CrossTrigger.vhd414
1 files changed, 414 insertions, 0 deletions
diff --git a/fpga/usrp3/top/n3xx/dboards/common/sync/CrossTrigger.vhd b/fpga/usrp3/top/n3xx/dboards/common/sync/CrossTrigger.vhd
new file mode 100644
index 000000000..70a873501
--- /dev/null
+++ b/fpga/usrp3/top/n3xx/dboards/common/sync/CrossTrigger.vhd
@@ -0,0 +1,414 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright 2018 Ettus Research, a National Instruments Company
+--
+-- SPDX-License-Identifier: LGPL-3.0-or-later
+--
+--
+-- Purpose:
+--
+-- Uses the RP and SP edges to cross a trigger from the RefClk domain to
+-- the SampleClk domain. The RP FE captures the input trigger and sends it to
+-- the SampleClk domain. There, it is double-synchronized but only allowed to pass
+-- when the SP RE occurs. The trigger (now in the SampleClk domain) is then passed
+-- through an elastic buffer before being sent on it's merry way.
+--
+-- Below is the latency through this module. If you assert rTriggerIn before or after
+-- the rRP RE, then you need to add/subtract the distance to the rRP RE.
+--
+-- Deterministic latency through this module is (starting at the rRP RE):
+-- Measured difference between rRP and sSP rising edges (using a TDC, positive value
+-- if rRP rises before sSP).
+-- + One period of sSP
+-- + Two periods of SampleClk (Double Sync)
+-- + (sElasticBufferPtr value + 1) * SampleClk Period
+-- + One period of SampleClk
+--
+-- How much skew between RP and SP can we allow and still safely pass triggers?
+-- Our "launch" edge is essentially the RP FE, and our "latch" edge is the SP RE.
+-- Consider the no skew (RP and SP edges align) case first. Our setup and hold budget
+-- is balanced at T/2. Based on this, it seems we can tolerate almost T/2 skew in either
+-- direction (ignoring a few Reference and Sample Clock cycles here and there).
+-- My recommendation is to keep the skew to a minimum, like less than T/4.
+-- In the context of the FTDC project for N310, this should be a no-brainer since
+-- the SP pulses are started only a few RefClk cycles after RP. The skew is
+-- easily verified by taking a FTDC measurement. If the skew is less than T/4, you can
+-- sleep easy. If not, then I recommend doing a comprehensive analysis of how much
+-- settling time you have between the trigger being launched from the RefClk domain
+-- and latched in the SampleClk domain.
+--
+-- vreview_group Tdc
+-------------------------------------------------------------------------------
+
+library ieee;
+ use ieee.std_logic_1164.all;
+ use ieee.numeric_std.all;
+
+library unisim;
+ use unisim.vcomponents.all;
+
+
+entity CrossTrigger is
+ port (
+ aReset : in boolean;
+
+ RefClk : in std_logic;
+ -- For convenience while writing this, I have only considered the N3x0 case where
+ -- rRP is slightly ahead of sSP in phase.
+ rRP : in boolean;
+ -- De-asserts the clock cycle after rTriggerIn asserts. Re-asserts after the
+ -- second falling edge of rRP, indicating new triggers can be accepted.
+ rReadyForInput : out boolean;
+ -- Only one pulse will be output for each rising edge of rTriggerIn. rTriggerIn is
+ -- ignored when rReadyForInput is de-asserted. All levels are ignored when Enable
+ -- is de-asserted.
+ rEnableTrigger : in boolean;
+ rTriggerIn : in boolean;
+
+ SampleClk : in std_logic;
+ sSP : in boolean;
+ -- An elastic buffer just before the output is used to compensate for skew
+ -- in sSP pulses across boards. Default should be in the middle of the 4 bit
+ -- range at 7.
+ sElasticBufferPtr : in unsigned(3 downto 0);
+ -- Single-cycle pulse output.
+ sTriggerOut : out boolean
+ );
+end CrossTrigger;
+
+
+architecture rtl of CrossTrigger is
+
+ --vhook_sigstart
+ --vhook_sigend
+
+ signal rRpFE,
+ rRpDly,
+ rTriggerToSClk,
+ rTriggerCaptured,
+ sSpRE,
+ sSpDly : boolean;
+
+ signal sTriggerBuffer : unsigned(2**sElasticBufferPtr'length-1 downto 0);
+ signal sTriggerInSClk, sTriggerInSClk_ms : boolean;
+
+ function to_StdLogic(b : boolean) return std_ulogic is
+ begin
+ if b then
+ return '1';
+ else
+ return '0';
+ end if;
+ end to_StdLogic;
+
+ function to_Boolean (s : std_ulogic) return boolean is
+ begin
+ return (To_X01(s)='1');
+ end to_Boolean;
+
+ attribute async_reg : string;
+ attribute async_reg of sTriggerInSClk : signal is "TRUE";
+ attribute async_reg of sTriggerInSClk_ms : signal is "TRUE";
+
+begin
+
+ -- Reference Clock Domain Trigger Capture : -------------------------------------------
+ -- The trigger input is captured whenever it is high. The captured value is reset
+ -- by the falling edge of rRP.
+ -- ------------------------------------------------------------------------------------
+
+ rRpFE <= rRpDly and not rRP;
+
+ CaptureTrigger : process(aReset, RefClk)
+ begin
+ if aReset then
+ rTriggerCaptured <= false;
+ rRpDly <= false;
+ elsif rising_edge(RefClk) then
+ rRpDly <= rRP;
+ if not rEnableTrigger then
+ rTriggerCaptured <= false;
+ elsif rTriggerIn then
+ -- Capture trigger whenever the input is asserted (so this will work with single
+ -- cycle and multi-cycle pulses).
+ rTriggerCaptured <= true;
+ elsif rRpFE then
+ -- Reset the captured trigger one cycle after the rRP FE.
+ rTriggerCaptured <= false;
+ end if;
+ end if;
+ end process;
+
+
+ -- Send Trigger To Sample Clock Domain : ----------------------------------------------
+ -- Send the captured trigger on the falling edge of rRP.
+ -- ------------------------------------------------------------------------------------
+ SendTrigger : process(aReset, RefClk)
+ begin
+ if aReset then
+ rTriggerToSClk <= false;
+ elsif rising_edge(RefClk) then
+ if not rEnableTrigger then
+ rTriggerToSClk <= false;
+ elsif rRpFE then
+ rTriggerToSClk <= rTriggerCaptured;
+ end if;
+ end if;
+ end process;
+
+ rReadyForInput <= not (rTriggerToSClk or rTriggerCaptured);
+
+ -- Capture Trigger in Sample Clock Domain : -------------------------------------------
+ -- On the rising edge of sSP, capture the trigger. To keep things free of
+ -- metastability, we double-sync the trigger into the SampleClk domain first.
+ -- ------------------------------------------------------------------------------------
+
+ ReceiveAndProcessTrigger : process(aReset, SampleClk)
+ begin
+ if aReset then
+ sSpDly <= false;
+ sTriggerBuffer <= (others => '0');
+ sTriggerOut <= false;
+ sTriggerInSClk_ms <= false;
+ sTriggerInSClk <= false;
+ elsif rising_edge(SampleClk) then
+ -- Edge detector delays.
+ sSpDly <= sSP;
+
+ -- Double-synchronizer for trigger.
+ sTriggerInSClk_ms <= rTriggerToSClk;
+ sTriggerInSClk <= sTriggerInSClk_ms;
+
+ -- Delay chain for the elastic buffer. Move to the left people! Note that this
+ -- operation incurs at least one cycle of delay. Also note the trigger input is
+ -- gated with the SP RE.
+ sTriggerBuffer <= sTriggerBuffer(sTriggerBuffer'high-1 downto 0) &
+ to_stdlogic(sTriggerInSClk and sSpRE);
+
+ -- Based on the buffer pointer value select and flop the output one more time.
+ sTriggerOut <= to_boolean(sTriggerBuffer(to_integer(sElasticBufferPtr)));
+ end if;
+ end process;
+
+ -- Rising edge detectors.
+ sSpRE <= sSP and not sSpDly;
+
+
+end rtl;
+
+
+
+
+
+--------------------------------------------------------------------------------
+-- Testbench for CrossTrigger
+--
+-- Meh coverage on the triggers so far... but this tests general operation
+-- and latency.
+--------------------------------------------------------------------------------
+
+--synopsys translate_off
+library ieee;
+ use ieee.std_logic_1164.all;
+ use ieee.numeric_std.all;
+
+entity tb_CrossTrigger is end tb_CrossTrigger;
+
+architecture test of tb_CrossTrigger is
+
+ -- Sets up a 1.25 MHz period.
+ constant kClksPerPulseMaxBits: integer := 10;
+ constant kRpPeriodInRClks : integer := 8;
+ constant kRpHighTimeInRClks : integer := 4;
+ constant kSpPeriodInRClks : integer := 100;
+ constant kSpHighTimeInRClks : integer := 50;
+
+ --vhook_sigstart
+ signal aReset: boolean;
+ signal RefClk: std_logic := '0';
+ signal rEnablePulser: boolean;
+ signal rEnableTrigger: boolean;
+ signal rReadyForInput: boolean;
+ signal rRP: boolean;
+ signal rTriggerIn: boolean;
+ signal SampleClk: std_logic := '0';
+ signal sElasticBufferPtr: unsigned(3 downto 0);
+ signal sEnablePulser: boolean;
+ signal sSP: boolean;
+ signal sTriggerOut: boolean;
+ --vhook_sigend
+
+ signal StopSim : boolean;
+ -- shared variable Rand : Random_t;
+ constant kSPer : time := 8.000 ns; -- 125.00 MHz
+ constant kRPer : time := 100.000 ns; -- 10.00 MHz
+
+ signal rRfiExpected: boolean:= true;
+ signal sTriggerOutExpected: boolean:= false;
+
+ procedure ClkWait(
+ signal Clk : in std_logic;
+ X : positive := 1) is
+ begin
+ for i in 1 to X loop
+ wait until rising_edge(Clk);
+ end loop;
+ end procedure ClkWait;
+
+begin
+
+ SampleClk <= not SampleClk after kSPer/2 when not StopSim else '0';
+ RefClk <= not RefClk after kRPer/2 when not StopSim else '0';
+
+ --vhook_e Pulser RpPulser
+ --vhook_a Clk RefClk
+ --vhook_a cLoadLimits true
+ --vhook_a cPeriod to_unsigned(kRpPeriodInRClks,kClksPerPulseMaxBits)
+ --vhook_a cHighTime to_unsigned(kRpHighTimeInRClks,kClksPerPulseMaxBits)
+ --vhook_a cEnablePulse rEnablePulser
+ --vhook_a cPulse rRP
+ RpPulser: entity work.Pulser (rtl)
+ generic map (kClksPerPulseMaxBits => kClksPerPulseMaxBits) --integer range 3:32 :=16
+ port map (
+ aReset => aReset, --in boolean
+ Clk => RefClk, --in std_logic
+ cLoadLimits => true, --in boolean
+ cPeriod => to_unsigned(kRpPeriodInRClks,kClksPerPulseMaxBits), --in unsigned(kClksPerPulseMaxBits-1:0)
+ cHighTime => to_unsigned(kRpHighTimeInRClks,kClksPerPulseMaxBits), --in unsigned(kClksPerPulseMaxBits-1:0)
+ cEnablePulse => rEnablePulser, --in boolean
+ cPulse => rRP); --out boolean
+
+ --vhook_e Pulser SpPulser
+ --vhook_a Clk SampleClk
+ --vhook_a cLoadLimits true
+ --vhook_a cPeriod to_unsigned(kSpPeriodInRClks,kClksPerPulseMaxBits)
+ --vhook_a cHighTime to_unsigned(kSpHighTimeInRClks,kClksPerPulseMaxBits)
+ --vhook_a cEnablePulse sEnablePulser
+ --vhook_a cPulse sSP
+ SpPulser: entity work.Pulser (rtl)
+ generic map (kClksPerPulseMaxBits => kClksPerPulseMaxBits) --integer range 3:32 :=16
+ port map (
+ aReset => aReset, --in boolean
+ Clk => SampleClk, --in std_logic
+ cLoadLimits => true, --in boolean
+ cPeriod => to_unsigned(kSpPeriodInRClks,kClksPerPulseMaxBits), --in unsigned(kClksPerPulseMaxBits-1:0)
+ cHighTime => to_unsigned(kSpHighTimeInRClks,kClksPerPulseMaxBits), --in unsigned(kClksPerPulseMaxBits-1:0)
+ cEnablePulse => sEnablePulser, --in boolean
+ cPulse => sSP); --out boolean
+
+
+ main: process
+ procedure SendTrigger is
+ begin
+ assert rReadyForInput
+ report "RFI isn't high, so we can't issue a trigger" severity error;
+
+ -- Give it some action. We need to ideally test this for every phase offset of
+ -- rTriggerIn with respect to the rising edge of rRP, but let's get to that later.
+ -- For now, wait until a rising edge on rRP and then wait for most of the period
+ -- to issue the trigger.
+ wait until rRP and not rRP'delayed;
+ wait for (kRpPeriodInRClks-3)*kRPer;
+ rTriggerIn <= true;
+ ClkWait(RefClk);
+ rTriggerIn <= false;
+ rRfiExpected <= false;
+
+ -- At this point, we wait until a sSP RE, plus two SampleClks, plus sElasticBufferPtr
+ -- plus 1 worth of SampleClks, plus one more SampleClk, and then the trigger
+ -- should appear.
+ wait until not rRP and rRP'delayed;
+ wait until sSP and not sSP'delayed;
+ ClkWait(SampleClk,1);
+ ClkWait(SampleClk, to_integer(sElasticBufferPtr)+1);
+ sTriggerOutExpected <= true;
+ ClkWait(SampleClk,1);
+ sTriggerOutExpected <= false;
+ wait until not rRP and rRP'delayed;
+ ClkWait(RefClk,1);
+ rRfiExpected <= true;
+ end procedure SendTrigger;
+
+ begin
+ rEnablePulser <= false;
+ sEnablePulser <= false;
+ rEnableTrigger <= true;
+ sElasticBufferPtr <= to_unsigned(7, sElasticBufferPtr'length);
+
+ aReset <= true, false after 10 ns;
+ ClkWait(RefClk,5);
+
+ -- Start up the pulsers and ensure nothing comes out of the trigger for a while.
+ rEnablePulser <= true;
+ ClkWait(RefClk, 3);
+ ClkWait(SampleClk, 2);
+ sEnablePulser <= true;
+
+ ClkWait(RefClk, kRpPeriodInRClks*5);
+ assert (not sTriggerOut) and sTriggerOut'stable(kRpPeriodInRClks*5*kRPer)
+ report "Rogue activity on sTriggerOut before rTriggerIn asserted!" severity error;
+ assert (rReadyForInput) and rReadyForInput'stable(kRpPeriodInRClks*5*kRPer)
+ report "Ready for Input was not high before trigger!" severity error;
+
+
+ SendTrigger;
+
+ ClkWait(RefClk, kRpPeriodInRClks*5);
+
+ SendTrigger;
+
+ ClkWait(RefClk, kRpPeriodInRClks*5);
+
+ -- Turn off the trigger enable and send a trigger.
+ rEnableTrigger <= false;
+ ClkWait(RefClk);
+ rTriggerIn <= true;
+ ClkWait(RefClk);
+ rTriggerIn <= false;
+
+ -- And nothing should happen.
+ ClkWait(RefClk, kRpPeriodInRClks*5);
+ assert (not sTriggerOut) and sTriggerOut'stable(kRpPeriodInRClks*5*kRPer)
+ report "Rogue activity on sTriggerOut before rTriggerIn asserted!" severity error;
+
+
+ ClkWait(RefClk, kRpPeriodInRClks*5);
+ StopSim <= true;
+ wait;
+ end process;
+
+
+ CheckRfi : process(RefClk)
+ begin
+ if falling_edge(RefClk) then
+ assert rReadyForInput = rRfiExpected
+ report "RFI didn't match expected" severity error;
+ end if;
+ end process;
+
+ CheckTrigOut : process(SampleClk)
+ begin
+ if falling_edge(SampleClk) then
+ assert sTriggerOut = sTriggerOutExpected
+ report "Trigger Out didn't match expected" severity error;
+ end if;
+ end process;
+
+
+ --vhook_e CrossTrigger dutx
+ dutx: entity work.CrossTrigger (rtl)
+ port map (
+ aReset => aReset, --in boolean
+ RefClk => RefClk, --in std_logic
+ rRP => rRP, --in boolean
+ rReadyForInput => rReadyForInput, --out boolean
+ rEnableTrigger => rEnableTrigger, --in boolean
+ rTriggerIn => rTriggerIn, --in boolean
+ SampleClk => SampleClk, --in std_logic
+ sSP => sSP, --in boolean
+ sElasticBufferPtr => sElasticBufferPtr, --in unsigned(3:0)
+ sTriggerOut => sTriggerOut); --out boolean
+
+
+end test;
+--synopsys translate_on