diff --git a/bittide-instances/src/Bittide/Instances/Domains.hs b/bittide-instances/src/Bittide/Instances/Domains.hs index 8ea5ddfdab..c0ec90e59b 100644 --- a/bittide-instances/src/Bittide/Instances/Domains.hs +++ b/bittide-instances/src/Bittide/Instances/Domains.hs @@ -26,6 +26,8 @@ createDomain vXilinxSystem{vName="Ext200", vPeriod=hzToPeriod 200e6, vResetKind= createDomain vXilinxSystem{vName="GthRx", vPeriod=hzToPeriod 125e6} createDomain vXilinxSystem{vName="GthTx", vPeriod= hzToPeriod 125e6} +createDomain vXilinxSystem{vName="GthRx1", vPeriod= hzToPeriod 250e6} +createDomain vXilinxSystem{vName="GthTx1", vPeriod= hzToPeriod 250e6} createDomain vXilinxSystem{vName="GthRxS", vPeriod=hzToPeriod 10e9} createDomain vXilinxSystem{vName="GthTxS", vPeriod= hzToPeriod 10e9} {- ORMOLU_ENABLE -} diff --git a/bittide-instances/src/Bittide/Instances/Hitl/Transceivers.hs b/bittide-instances/src/Bittide/Instances/Hitl/Transceivers.hs index b449587b68..f2dc9fdd33 100644 --- a/bittide-instances/src/Bittide/Instances/Hitl/Transceivers.hs +++ b/bittide-instances/src/Bittide/Instances/Hitl/Transceivers.hs @@ -157,7 +157,7 @@ goTransceiversUpTest fpgaIndex refClk sysClk rst rxNs rxPs miso = @Basic125 @GthTxS @GthRxS - defConfig{debugIla = True, debugFpgaIndex = bitCoerce <$> fpgaIndex} + defConfig{debugFpgaIndex = bitCoerce <$> fpgaIndex} Inputs { clock = sysClk , reset = gthAllReset diff --git a/bittide/src/Bittide/Transceiver.hs b/bittide/src/Bittide/Transceiver.hs index 1429c4a225..97983d2d34 100644 --- a/bittide/src/Bittide/Transceiver.hs +++ b/bittide/src/Bittide/Transceiver.hs @@ -89,7 +89,6 @@ import Clash.Explicit.Prelude import Bittide.Arithmetic.Time (trueForSteps) import Bittide.ElasticBuffer (sticky) -import Clash.Cores.Xilinx.GTH (GthCore) import Clash.Cores.Xilinx.Ila ( Depth (D1024), IlaConfig (advancedTriggers, depth, stages), @@ -100,7 +99,6 @@ import Clash.Cores.Xilinx.Xpm.Cdc.ArraySingle (xpmCdcArraySingle) import Clash.Cores.Xilinx.Xpm.Cdc.Single (xpmCdcSingle) import Clash.Explicit.Reset.Extra (Asserted (Asserted), delayReset, xpmResetSynchronizer) import Clash.Prelude (withClock) -import Clash.Sized.Vector.Extra (zipWith8) import Control.Monad (when) import Data.Maybe (fromMaybe, isNothing) import Data.Proxy (Proxy (Proxy)) @@ -198,9 +196,9 @@ data Outputs n tx rx txS free = Outputs -- ^ See 'Output.stats' } -data Output tx rx txS free serializedData = Output - { txClock :: Clock tx - -- ^ Transmit clock. See 'txReset'. +data Output tx rx tx1 rx1 txS free serializedData = Output + { txOutClock :: Clock tx1 + -- ^ TODO , txReset :: Reset tx -- ^ Reset signal for the transmit side. Clock can be unstable until this reset -- is deasserted. @@ -213,8 +211,8 @@ data Output tx rx txS free serializedData = Output -- ^ Transmit data (and implicitly a clock), positive , txN :: Signal txS serializedData -- ^ Transmit data (and implicitly a clock), negative - , rxClock :: Clock rx - -- ^ Receive clock, recovered from the incoming data stream. See 'rxReset'. + , rxOutClock :: Clock rx1 + -- ^ TODO , rxReset :: Reset rx -- ^ Reset signal for the receive side. Clock can be unstable until this reset -- is deasserted. @@ -229,13 +227,19 @@ data Output tx rx txS free serializedData = Output -- ^ Statistics exported by 'ResetManager.resetManager'. Useful for debugging. } -data Input tx rx ref free rxS serializedData = Input +data Input tx rx tx1 rx1 ref free rxS serializedData = Input { clock :: Clock free -- ^ Any "always on" clock , reset :: Reset free -- ^ Reset signal for the entire transceiver , refClock :: Clock ref -- ^ Reference clock. Used to synthesize transmit clock. + , clockTx1 :: Clock tx1 + , clockTx2 :: Clock tx + , txActive :: Signal tx (BitVector 1) + , clockRx1 :: Clock rx1 + , clockRx2 :: Clock rx + , rxActive :: Signal rx (BitVector 1) , transceiverIndex :: Unsigned 3 -- ^ Index of this transceiver, used for debugging. Can be set to 0 if not used. , channelName :: String @@ -282,9 +286,23 @@ data Inputs tx rx ref free rxS n = Inputs -- ^ See 'Input.rxReady' } +{- +[NOTE: duplicate tx/rx domain] +'gthCore' and the inside of 'transceiverPrbsN' have two extra domains, tx1 and rx1, +that aren't visible outside of transceiverPrbsN. +To do this completely clean/safe transceiverPrbsN should have two extra +forall arguments, two extra KnownDomain constrainsts. +And either some Proxy arguments or we would have to enable AllowAmbiguousTypes. + +Instead I choose to sidestep that and pretend tx1/rx1 and tx/rx are the same. +This disables the typechecking safety we'd normally get from clash, +but vivado should call us out when we make a mistake. +-} + transceiverPrbsN :: - forall tx rx ref free txS rxS n. + forall tx rx ref free txS rxS n m. ( KnownNat n + , n ~ m + 1 , HasSynchronousReset tx , HasDefinedInitialValues tx , HasSynchronousReset rx @@ -302,12 +320,12 @@ transceiverPrbsN :: transceiverPrbsN opts inputs@Inputs{clock, reset, refClock} = Outputs { -- tx - txClocks = map (.txClock) outputs + txClocks = txClocks , txResets = map (.txReset) outputs , txReadys = map (.txReady) outputs , txSamplings = map (.txSampling) outputs , -- rx - rxClocks = map (.rxClock) outputs + rxClocks = rxClocks , rxResets = map (.rxReset) outputs , rxDatas = map (.rxData) outputs , -- transceiver @@ -319,27 +337,47 @@ transceiverPrbsN opts inputs@Inputs{clock, reset, refClock} = , stats = map (.stats) outputs } where - -- XXX: Replacing 'zipWithN' with '<$>' and '<*>' triggers a combination of: - -- + -- XXX: 'outputs' used to be written with zipWithN, to workaround bugs: -- * https://github.com/clash-lang/clash-compiler/issues/2723 -- * https://github.com/clash-lang/clash-compiler/issues/2722 + -- That breaks the instantiation of the the debug ILAs inside 'transceiverPrbs'. -- - -- Note that these bugs break the instantiation of multiple ILAs. + -- But when the GTHs were changed to use external "user clock networks", + -- this zipWithN became unusably slow when using more then ~4 transceivers. + -- Unfortunately this means debugIla is broken now, when using more then 1 transceiver. outputs = - zipWith8 - go - (iterateI (+ 1) 0) -- Note that the target type is only 3 bits, so this will + (go txClockNw) + <$> (iterateI (+ 1) 0) -- Note that the target type is only 3 bits, so this will -- wrap around after 8 transceivers. This is fine, as we -- only use this for debugging. - inputs.channelNames - inputs.clockPaths - (unbundle (unpack <$> inputs.rxNs)) - (unbundle (unpack <$> inputs.rxPs)) - inputs.txDatas - inputs.txReadys - inputs.rxReadys - - go transceiverIndex channelName clockPath rxN rxP txData txReady rxReady = + <*> inputs.channelNames + <*> inputs.clockPaths + <*> (unbundle (unpack <$> inputs.rxNs)) + <*> (unbundle (unpack <$> inputs.rxPs)) + <*> inputs.txDatas + <*> inputs.txReadys + <*> inputs.rxReadys + <*> rxClockNws + + -- NOTE: The example project generated by gtwizard_ultrascale suggests tying tx/rxUsrClkRst + -- to tx/rxpmaresetdone, but that doesn't seem to work. + -- And also it's not what the gtwizard_ultrascale does when configured with internal + -- "user clock network". + txUsrClkRst = noReset @tx + rxUsrClkRst = noReset @rx + + txOutClk = (head outputs).txOutClock + -- see [NOTE: duplicate tx/rx domain] + txClockNw = Gth.gthUserClockNetwork @tx @tx txOutClk txUsrClkRst + (_txClk1s, txClock, _txClkActives) = txClockNw + txClocks = repeat txClock + + rxOutClks = map (.rxOutClock) outputs + -- see [NOTE: duplicate tx/rx domain] + rxClockNws = map (flip (Gth.gthUserClockNetwork @rx @rx) rxUsrClkRst) rxOutClks + (_rxClk1s, rxClocks, _rxClkActives) = unzip3 rxClockNws + + go (clockTx1, clockTx2, txActive) transceiverIndex channelName clockPath rxN rxP txData txReady rxReady (clockRx1, clockRx2, rxActive) = transceiverPrbs opts Input @@ -354,43 +392,53 @@ transceiverPrbsN opts inputs@Inputs{clock, reset, refClock} = , clock , reset , refClock + , clockTx1 + , clockTx2 + , txActive + , clockRx1 + , clockRx2 + , rxActive } transceiverPrbs :: - forall tx rx ref free txS rxS. + forall tx rx tx1 rx1 ref free txS rxS. ( HasSynchronousReset tx , HasDefinedInitialValues tx , HasSynchronousReset rx , HasDefinedInitialValues rx , HasSynchronousReset free , HasDefinedInitialValues free + , KnownDomain rx1 + , KnownDomain tx1 , KnownDomain rxS , KnownDomain txS , KnownDomain ref , KnownDomain free ) => Config free -> - Input tx rx ref free rxS (BitVector 1) -> - Output tx rx txS free (BitVector 1) + Input tx rx tx1 rx1 ref free rxS (BitVector 1) -> + Output tx rx tx1 rx1 txS free (BitVector 1) transceiverPrbs = transceiverPrbsWith Gth.gthCore transceiverPrbsWith :: - forall tx rx ref free txS rxS serializedData. + forall tx rx tx1 rx1 ref free txS rxS serializedData. ( HasSynchronousReset tx , HasDefinedInitialValues tx , HasSynchronousReset rx , HasDefinedInitialValues rx , HasSynchronousReset free , HasDefinedInitialValues free + , KnownDomain rx1 + , KnownDomain tx1 , KnownDomain rxS , KnownDomain txS , KnownDomain ref , KnownDomain free ) => - GthCore tx rx ref free txS rxS serializedData -> + Gth.GthCore tx1 tx rx1 rx ref free txS rxS serializedData -> Config free -> - Input tx rx ref free rxS serializedData -> - Output tx rx txS free serializedData + Input tx rx tx1 rx1 ref free rxS serializedData -> + Output tx rx tx1 rx1 txS free serializedData transceiverPrbsWith gthCore opts args@Input{clock, reset} = when opts.debugIla debugIla `hwSeqX` result where @@ -469,6 +517,8 @@ transceiverPrbsWith gthCore opts args@Input{clock, reset} = txLastFree (pure True :: Signal free Bool) -- capture txLastFree -- trigger + tx_active = args.txActive + result = Output { txSampling = txUserData @@ -476,9 +526,9 @@ transceiverPrbsWith gthCore opts args@Input{clock, reset} = , txReady , txN , txP - , txClock + , txOutClock , txReset - , rxClock + , rxOutClock , rxReset , linkUp , linkReady @@ -493,16 +543,17 @@ transceiverPrbsWith gthCore opts args@Input{clock, reset} = ( txN , txP - , txClock - , rxClock + , txOutClock + , rxOutClock , rx_data0 , reset_tx_done , reset_rx_done - , tx_active - , rxCtrl0 - , rxCtrl1 - , rxCtrl2 - , rxCtrl3 + , _txpmaresetdone_out + , _rxpmaresetdone_out + , (rxCtrl0 :: Signal rx (BitVector 16)) + , (rxCtrl1 :: Signal rx (BitVector 16)) + , (rxCtrl2 :: Signal rx (BitVector 8)) + , (rxCtrl3 :: Signal rx (BitVector 8)) ) = gthCore args.channelName @@ -518,8 +569,18 @@ transceiverPrbsWith gthCore opts args@Input{clock, reset} = gtwiz_userdata_tx_in txctrl args.refClock -- gtrefclk0_in + args.clockTx1 + args.clockTx2 + args.txActive + args.clockRx1 + args.clockRx2 + args.rxActive + prbsConfig = Prbs.conf31 @48 + txClock = args.clockTx2 + rxClock = args.clockRx2 + (commas, txctrl) = Comma.generator d1 txClock txReset commasDone = isNothing <$> commas prbs = Prbs.generator txClock (unsafeFromActiveLow commasDone) enableGen prbsConfig diff --git a/bittide/src/Clash/Cores/Xilinx/GTH/BlackBoxes.hs b/bittide/src/Clash/Cores/Xilinx/GTH/BlackBoxes.hs index b62403bbe0..fa071eabc1 100644 --- a/bittide/src/Clash/Cores/Xilinx/GTH/BlackBoxes.hs +++ b/bittide/src/Clash/Cores/Xilinx/GTH/BlackBoxes.hs @@ -2,6 +2,7 @@ -- -- SPDX-License-Identifier: Apache-2.0 {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE ViewPatterns #-} module Clash.Cores.Xilinx.GTH.BlackBoxes where @@ -10,6 +11,7 @@ import Prelude import Control.Monad.State (State) import Data.String (fromString) +import Data.String.Interpolate (i) import Data.Text (Text) import Data.Text.Prettyprint.Doc.Extra (Doc) import GHC.Stack (HasCallStack) @@ -52,11 +54,25 @@ gthCoreBBF _isD _primName _args _resTys = pure $ Right (bbMeta, bb) bb = BBFunction (show 'gthCoreTF) 0 gthCoreTF nConstraints :: Int -nConstraints = 6 +nConstraints = 8 nNameArgs :: Int nNameArgs = 2 +checkHwTy :: (Show a) => (a, HWType) -> (b, HWType) -> (a, b) +checkHwTy (nm, ty1) (e, ty2) + | eqHwTyModuloDomain ty1 ty2 = (nm, e) + | otherwise = + error [i|Port ${nm} with type {ty1} can't be assigned a expression of type {ty2}|] + +eqHwTyModuloDomain :: N.HWType -> N.HWType -> Bool +eqHwTyModuloDomain x y = case (x, y) of + (N.Clock _, N.Clock _) -> True + (N.ClockN _, N.ClockN _) -> True + (N.Reset _, N.Reset _) -> True + (N.Enable _, N.Enable _) -> True + _ -> x == y + -- | Instantiate IP generated with 'gthCoreTclTF' gthCoreTF :: (HasCallStack) => TemplateFunction gthCoreTF = @@ -72,14 +88,20 @@ gthCoreBBTF :: gthCoreBBTF bbCtx | args@[ _gthrxn_in -- " ::: Signal rxS (BitVector ChansUsed) , _gthrxp_in -- " ::: Signal rxS (BitVector ChansUsed) - , gtwiz_reset_clk_freerun_in -- " ::: Clock freerun + , (gtwiz_reset_clk_freerun_in, _) -- " ::: Clock freerun , _gtwiz_reset_all_in -- " ::: Reset freerun , _gtwiz_reset_rx_datapath_in -- " ::: Reset freerun , _gtwiz_userdata_tx_in -- " ::: Signal txUser2 (BitVector (ChansUsed*TX_DATA_WIDTH)) , _txctrl2_in -- " ::: Signal txUser2 (BitVector (ChansUsed*TX_DATA_WIDTH/8)) , _gtrefclk0_in -- " ::: Clock refclk0 + , _txusrclk_in + , _txusrclk2_in + , _gtwiz_userclk_tx_active_in + , _rxusrclk_in + , _rxusrclk2_in + , _gtwiz_userclk_rx_active_in ] <- - drop (nConstraints + nNameArgs) $ map fst (DSL.tInputs bbCtx) + drop (nConstraints + nNameArgs) (DSL.tInputs bbCtx) , [tResult] <- map DSL.ety (DSL.tResults bbCtx) , [gthCoreName] <- N.bbQsysIncName bbCtx = do @@ -99,6 +121,12 @@ gthCoreBBTF bbCtx , ("txctrl2_in", N.BitVector (chansUsed * (tX_DATA_WIDTH `div` 8))) , -- , ("gtrefclk00_in", N.Clock "refclk00" ) ("gtrefclk0_in", N.Clock "refclk0") + , ("txusrclk_in", N.Clock "txUser") + , ("txusrclk2_in", N.Clock "txUser2") + , ("gtwiz_userclk_tx_active_in", N.BitVector 1) + , ("rxusrclk_in", N.Clock "rxUser") + , ("rxusrclk2_in", N.Clock "rxUser2") + , ("gtwiz_userclk_rx_active_in", N.BitVector 1) ] <> map (fmap DSL.ety) otherInps @@ -111,8 +139,6 @@ gthCoreBBTF bbCtx , ("gtwiz_reset_rx_pll_and_datapath_in", DSL.bvLit 1 0) , ("tx8b10ben_in", DSL.bvLit 1 1) , ("rx8b10ben_in", DSL.bvLit 1 1) - , ("gtwiz_userclk_tx_reset_in", DSL.bvLit 1 0) - , ("gtwiz_userclk_rx_reset_in", DSL.bvLit 1 0) , ("rxcommadeten_in", DSL.bvLit 1 1) , ("rxmcommaalignen_in", DSL.bvLit 1 1) , ("rxpcommaalignen_in", DSL.bvLit 1 1) @@ -120,12 +146,13 @@ gthCoreBBTF bbCtx compOuts = [ ("gthtxn_out", N.BitVector chansUsed) , ("gthtxp_out", N.BitVector chansUsed) - , ("gtwiz_userclk_tx_usrclk2_out", N.Clock "txUser2") - , ("gtwiz_userclk_rx_usrclk2_out", N.Clock "rxUser2") + , ("txoutclk_out", N.Clock "txUser") + , ("rxoutclk_out", N.Clock "rxUser") , ("gtwiz_userdata_rx_out", N.BitVector (chansUsed * rX_DATA_WIDTH)) , ("gtwiz_reset_tx_done_out", N.BitVector 1) , ("gtwiz_reset_rx_done_out", N.BitVector 1) - , ("gtwiz_userclk_tx_active_out", N.BitVector 1) + , ("txpmaresetdone_out", N.BitVector 1) + , ("rxpmaresetdone_out", N.BitVector 1) , ("rxctrl0_out", N.BitVector 16) , ("rxctrl1_out", N.BitVector 16) , ("rxctrl2_out", N.BitVector 8) @@ -135,7 +162,8 @@ gthCoreBBTF bbCtx DSL.declarationReturn bbCtx "gthCore_inst_block" $ do DSL.compInBlock gthCoreName compInps compOuts - let inps = zip (fst <$> compInps) args <> otherInps + let inps = zipWith checkHwTy compInps args <> otherInps + outs <- mapM (uncurry DSL.declare) compOuts DSL.instDecl N.Empty @@ -178,9 +206,9 @@ gthCoreTclBBTF bbCtx , property @Text "LOCATE_IN_SYSTEM_IBERT_CORE" "NONE" , property @Text "LOCATE_RESET_CONTROLLER" "CORE" , property @Text "LOCATE_RX_BUFFER_BYPASS_CONTROLLER" "CORE" - , property @Text "LOCATE_RX_USER_CLOCKING" "CORE" + , property @Text "LOCATE_RX_USER_CLOCKING" "EXAMPLE_DESIGN" , property @Text "LOCATE_TX_BUFFER_BYPASS_CONTROLLER" "CORE" - , property @Text "LOCATE_TX_USER_CLOCKING" "CORE" + , property @Text "LOCATE_TX_USER_CLOCKING" "EXAMPLE_DESIGN" , property @Text "LOCATE_USER_DATA_WIDTH_SIZING" "CORE" , property @Text "FREERUN_FREQUENCY" "125.0" , property @Text "RX_REFCLK_FREQUENCY" "200" diff --git a/bittide/src/Clash/Cores/Xilinx/GTH/Internal.hs b/bittide/src/Clash/Cores/Xilinx/GTH/Internal.hs index d310bc2445..0e5d4231dd 100644 --- a/bittide/src/Clash/Cores/Xilinx/GTH/Internal.hs +++ b/bittide/src/Clash/Cores/Xilinx/GTH/Internal.hs @@ -6,22 +6,34 @@ module Clash.Cores.Xilinx.GTH.Internal where -import Clash.Prelude +import Clash.Explicit.Prelude import Clash.Annotations.Primitive ( HDL (Verilog), Primitive (InlineYamlPrimitive), hasBlackBox, ) +import Clash.Annotations.SynthesisAttributes import Data.String.Interpolate (__i) import Clash.Cores.Xilinx.GTH.BlackBoxes +import Clash.Cores.Xilinx.Xpm.Cdc.Internal ( + ClockPort (..), + Param (..), + Port (..), + ResetPort (..), + inst, + instConfig, + unPort, + ) type TX_DATA_WIDTH = 64 type RX_DATA_WIDTH = 64 -type GthCore txUser2 rxUser2 refclk0 freerun txS rxS serializedData = - ( KnownDomain txUser2 +type GthCore txUser txUser2 rxUser rxUser2 refclk0 freerun txS rxS serializedData = + ( KnownDomain txUser + , KnownDomain txUser2 + , KnownDomain rxUser , KnownDomain rxUser2 , KnownDomain refclk0 , KnownDomain freerun @@ -40,21 +52,28 @@ type GthCore txUser2 rxUser2 refclk0 freerun txS rxS serializedData = "gtwiz_userdata_tx_in" ::: Signal txUser2 (BitVector TX_DATA_WIDTH) -> "txctrl2_in" ::: Signal txUser2 (BitVector (DivRU TX_DATA_WIDTH 8)) -> "gtrefclk0_in" ::: Clock refclk0 -> + "txusrclk_in" ::: Clock txUser -> + "txusrclk2_in" ::: Clock txUser2 -> + "gtwiz_userclk_tx_active_in" ::: Signal txUser2 (BitVector 1) -> + "rxusrclk_in" ::: Clock rxUser -> + "rxusrclk2_in" ::: Clock rxUser2 -> + "gtwiz_userclk_rx_active_in" ::: Signal rxUser2 (BitVector 1) -> ( "gthtxn_out" ::: Signal txS serializedData , "gthtxp_out" ::: Signal txS serializedData - , "gtwiz_userclk_tx_usrclk2_out" ::: Clock txUser2 - , "gtwiz_userclk_rx_usrclk2_out" ::: Clock rxUser2 + , "txoutclk_out" ::: Clock txUser + , "rxoutclk_out" ::: Clock rxUser , "gtwiz_userdata_rx_out" ::: Signal rxUser2 (BitVector RX_DATA_WIDTH) , "gtwiz_reset_tx_done_out" ::: Signal txUser2 (BitVector 1) , "gtwiz_reset_rx_done_out" ::: Signal rxUser2 (BitVector 1) - , "gtwiz_userclk_tx_active_out" ::: Signal txUser2 (BitVector 1) + , "txpmaresetdone_out" ::: Signal txUser (BitVector 1) + , "rxpmaresetdone_out" ::: Signal rxUser (BitVector 1) , "rxctrl0_out" ::: Signal rxUser2 (BitVector 16) , "rxctrl1_out" ::: Signal rxUser2 (BitVector 16) , "rxctrl2_out" ::: Signal rxUser2 (BitVector 8) , "rxctrl3_out" ::: Signal rxUser2 (BitVector 8) ) -gthCore :: GthCore txUser2 rxUser2 refclk0 freerun txS rxS (BitVector 1) +gthCore :: GthCore txUser txUser2 rxUser rxUser2 refclk0 freerun txS rxS (BitVector 1) gthCore !_channel !_refClkSpec @@ -65,7 +84,13 @@ gthCore !_gtwiz_reset_rx_datapath_in !_gtwiz_userdata_tx_in !_txctrl2_in - !_gtrefclk0_in = + !_gtrefclk0_in + !_ + !_ + !_ + !_ + !_ + !_ = ( undefined , undefined , undefined @@ -78,6 +103,7 @@ gthCore , undefined , undefined , undefined + , undefined ) {-# OPAQUE gthCore #-} {-# ANN gthCore hasBlackBox #-} @@ -96,6 +122,79 @@ gthCore ) #-} +{- | This mimics what PG182 calls the "[RX,TX] User Clocking Network Helper Block" + +It has a hardcoded to do no division for @usrclk@ and divide by 2 for @usrclk2@. +So it'll only work when the external RX/TX GTH interfaces uses twice the width of the internal width. +See: https://docs.amd.com/r/en-US/pg182-gtwizard-ultrascale/Transmitter-User-Clocking-Network-Helper-Block-Ports +-} +gthUserClockNetwork :: + forall user user2. + (KnownDomain user, KnownDomain user2) => + Clock user -> + Reset user2 -> + (Clock user, Clock user2, Signal user2 (BitVector 1)) +gthUserClockNetwork clkIn rstIn = + (clk1, clk2, active) + where + rstIn1 :: Reset user + rstIn1 = unsafeSynchronizerReset clk2 clk1 rstIn + clk1 = bufgGt d0 clkIn rstIn1 + clk2 :: Clock user2 + clk2 = bufgGt d1 clkIn rstIn1 + -- TODO: Use XPM syncer. Alternatively, instantiate Xilinx IP for this whole function + reg = annotate (StringAttr "ASYNC_REG" "TRUE" :> Nil) . register clk2 rstIn enableGen 0 + active = reg $ reg (pure 1) +{-# NOINLINE gthUserClockNetwork #-} + +unsafeSynchronizerReset :: + (KnownDomain dom1, KnownDomain dom2) => + Clock dom1 -> + Clock dom2 -> + Reset dom1 -> + Reset dom2 +unsafeSynchronizerReset clkIn clkOut rstIn = unsafeFromActiveHigh $ unsafeSynchronizer clkIn clkOut (unsafeToActiveHigh rstIn) + +xilinxGthUserClockNetworkTx :: + forall user user2. + (KnownDomain user, KnownDomain user2) => + Clock user -> + Reset user2 -> + (Clock user, Clock user2, Signal user2 (BitVector 1)) +xilinxGthUserClockNetworkTx clkIn rstIn = (unPort usrclk_out, unPort usrclk2_out, pack <$> unPort tx_active_out) + where + (usrclk_out, usrclk2_out, tx_active_out) = go (Param 2) (ClockPort clkIn) (ResetPort rstIn) + go :: + Param "P_FREQ_RATIO_USRCLK_TO_USRCLK2" Integer -> + ClockPort "gtwiz_userclk_tx_srcclk_in" user -> + ResetPort "gtwiz_userclk_tx_reset_in" ActiveHigh user2 -> + ( ClockPort "gtwiz_userclk_tx_usrclk_out" user + , ClockPort "gtwiz_userclk_tx_usrclk2_out" user2 + , Port "gtwiz_userclk_tx_active_out" user2 Bit + ) + go = inst (instConfig "gtwizard_ultrascale_v1_7_13_gtwiz_userclk_tx") +{-# NOINLINE xilinxGthUserClockNetworkTx #-} + +xilinxGthUserClockNetworkRx :: + forall user user2. + (KnownDomain user, KnownDomain user2) => + Clock user -> + Reset user2 -> + (Clock user, Clock user2, Signal user2 (BitVector 1)) +xilinxGthUserClockNetworkRx clkIn rstIn = (unPort usrclk_out, unPort usrclk2_out, pack <$> unPort rx_active_out) + where + (usrclk_out, usrclk2_out, rx_active_out) = go (Param 2) (ClockPort clkIn) (ResetPort rstIn) + go :: + Param "P_FREQ_RATIO_USRCLK_TO_USRCLK2" Integer -> + ClockPort "gtwiz_userclk_rx_srcclk_in" user -> + ResetPort "gtwiz_userclk_rx_reset_in" ActiveHigh user2 -> + ( ClockPort "gtwiz_userclk_rx_usrclk_out" user + , ClockPort "gtwiz_userclk_rx_usrclk2_out" user2 + , Port "gtwiz_userclk_rx_active_out" user2 Bit + ) + go = inst (instConfig "gtwizard_ultrascale_v1_7_13_gtwiz_userclk_rx") +{-# NOINLINE xilinxGthUserClockNetworkRx #-} + ibufds_gte3 :: (KnownDomain dom) => DiffClock dom -> Clock dom ibufds_gte3 !_clk = clockGen {-# OPAQUE ibufds_gte3 #-} diff --git a/bittide/tests/Tests/Transceiver.hs b/bittide/tests/Tests/Transceiver.hs index 044b4863db..b4a1501f59 100644 --- a/bittide/tests/Tests/Transceiver.hs +++ b/bittide/tests/Tests/Transceiver.hs @@ -89,7 +89,7 @@ gthCoreMock :: Natural -> -- Offset Index 8 -> - GthCore tx rx ref free tx rx (Maybe (BitVector 64)) + GthCore tx tx rx rx ref free tx rx (Maybe (BitVector 64)) gthCoreMock _name nRxResetCycles @@ -105,15 +105,23 @@ gthCoreMock rstRx txWord _txCtrl - _refClk = + _refClk + _tx1Clk + tx2Clk + _txActive + _rx1Clk + rx2Clk + _rxActive = ( txSerial , txSerial - , txClk - , rxClk + , txOutClk + , rxOutClk , rxWord , pack <$> txDone , pack <$> rxDone - , pack <$> txActive + + , error "txpmaresetdone_out unused in test" + , error "rxpmaresetdone_out unused in test" , 0 , 0 , 0 @@ -133,15 +141,16 @@ gthCoreMock registerTx = register txClk txRstAll enableGen rxResetCounter = registerRx nRxResetCycles (predSatZeroNatural <$> rxResetCounter) - txResetCounter = registerTx nTxResetCycles (predSatZeroNatural <$> txResetCounter) txDoneCounter = registerTx (nTxResetCycles + nTxDoneCycles) (predSatZeroNatural <$> txDoneCounter) rxDone = rxResetCounter .==. 0 - txActive = txResetCounter .==. 0 txDone = txDoneCounter .==. 0 - txClk = clockGen - rxClk = clockGen + txOutClk = clockGen + rxOutClk = clockGen + + txClk = tx2Clk + rxClk = rx2Clk txRstAll = unsafeFromActiveHigh (unsafeSynchronizer freeClk txClk (unsafeToActiveHigh rstAll)) rxRstAll = unsafeFromActiveHigh (unsafeSynchronizer freeClk rxClk (unsafeToActiveHigh rstAll)) @@ -177,16 +186,16 @@ dut :: -- | Number of word clock cycles delay from B -> A Natural -> ResetManager.Config -> - GthCore txA txB ref freeA txA txB (Maybe (Bytes n)) -> - GthCore txB txA ref freeB txB txA (Maybe (Bytes n)) -> + GthCore txA txA txB txB ref freeA txA txB (Maybe (Bytes n)) -> + GthCore txB txB txA txA ref freeB txB txA (Maybe (Bytes n)) -> Clock freeA -> Reset freeA -> Clock freeB -> Reset freeB -> Input txA txB -> Input txB txA -> - ( Transceiver.Output txA txB txA freeA (Maybe (Bytes n)) - , Transceiver.Output txB txA txB freeB (Maybe (Bytes n)) + ( Transceiver.Output txA txB txA txB txA freeA (Maybe (Bytes n)) + , Transceiver.Output txB txA txB txA txB freeB (Maybe (Bytes n)) ) dut abDelay @@ -209,6 +218,12 @@ dut { clock = freeClkA , reset = freeRstA , refClock = error "A: refClock not used in simulation" + , clockTx1 = clockGen + , clockTx2 = clockGen + , txActive = pure 1 -- TODO: a better simulation of this + , clockRx1 = clockGen + , clockRx2 = clockGen + , rxActive = pure 1 -- TODO: a better simulation of this , transceiverIndex = 0 , channelName = "A" , clockPath = "clkA" @@ -227,6 +242,12 @@ dut { clock = freeClkB , reset = freeRstB , refClock = error "B: refClock not used in simulation" + , clockTx1 = clockGen + , clockTx2 = clockGen + , txActive = pure 1 + , clockRx1 = clockGen + , clockRx2 = clockGen + , rxActive = pure 1 , transceiverIndex = 1 , channelName = "B" , clockPath = "clkB" @@ -238,12 +259,12 @@ dut } type DutTestFunc txA txB free = - Transceiver.Output txA txB txA free (Maybe (BitVector 64)) -> - Transceiver.Output txB txA txB free (Maybe (BitVector 64)) -> + Transceiver.Output txA txB txA txB txA free (Maybe (BitVector 64)) -> + Transceiver.Output txB txA txB txA txB free (Maybe (BitVector 64)) -> PropertyT IO () type InputFunc txA txB free = - Transceiver.Output txA txB txA free (Maybe (BitVector 64)) -> + Transceiver.Output txA txB txA txB txA free (Maybe (BitVector 64)) -> Input txA txB dutRandomized ::