Hello all,
I was wondering if I could ask for some help on the following issue.
I have a trivial topEntity
function, using clash-protocols
, that plugs into an Axi 4 Stream:
roundTripTopEntity
:: Clock System
-> Reset System
-> (Signal System (Maybe (Axi4StreamM2S AxisConfig Bool)), Signal System Axi4StreamS2M)
-> (Signal System Axi4StreamS2M, Signal System (Maybe (Axi4StreamM2S AxisConfig Bool)))
roundTripTopEntity clk rst = withClockResetEnable clk rst enableGen $ toSignals circuitRoundTrip
circuitRoundTrip
:: forall dom cfg. (AxiCfg cfg, HiddenClockResetEnable dom)
=> Circuit
(Axi4Stream dom cfg Bool)
(Axi4Stream dom cfg Bool)
circuitRoundTrip =
let
proxy = Proxy @(Axi4Stream dom cfg Bool)
fromDf = DfConv.dfToDfConvInp proxy
toDf = DfConv.dfToDfConvOtp proxy
in fromDf |> toDf
And I have a means of testing this, which feeds in Axi4StreamM2S values, only moving on to the next one if they are ack-ed via a tready
signal:
topEntityRunner
:: forall dom cfg n.
( HiddenClockResetEnable dom
, NFDataX (Axi4StreamM2S cfg Bool)
, KnownNat n
)
=> AxiSTopEntity dom cfg
-> Vec n (Axi4StreamM2S cfg Bool) -- Data to input
-> Signal dom (Maybe (Axi4StreamM2S cfg Bool))
topEntityRunner theTopEntity inputVec =
let
m2sGenerator :: Signal dom (Maybe (Axi4StreamM2S cfg Bool))
m2sGenerator = axiM2SGenerator inputVec outS2M
s2MInput :: Signal dom Axi4StreamS2M
s2MInput = pure $ Axi4StreamS2M True
(outS2M, outM2S) = theTopEntity clockGen resetGen (m2sGenerator, s2MInput)
in outM2S
axiM2SGenerator
:: (HiddenClockResetEnable dom, NFDataX (Axi4StreamM2S cfg Bool), KnownNat n)
=> Vec n (Axi4StreamM2S cfg Bool) -- Data to input
-> Signal dom Axi4StreamS2M -- the acks we get back
-> Signal dom (Maybe (Axi4StreamM2S cfg Bool))
axiM2SGenerator initialQueue =
mealy axiStateMachine (initialQueue, 0)
axiStateMachine
:: (KnownNat n)
=> (Vec n (Axi4StreamM2S cfg Bool), Index (n + 1))
-> Axi4StreamS2M
-> ((Vec n (Axi4StreamM2S cfg Bool), Index (n + 1)), Maybe (Axi4StreamM2S cfg Bool))
axiStateMachine state@(queue, n) !s2mVal =
let out = case (n == maxBound, _tready s2mVal) of
(True, _) -> (state, Nothing)
(False, True) -> ((queue, n+1), Just $ queue !! n )
(False, False) -> (state, Just $ queue !! n)
in out
If I then write a test which uses the latter to test the former:
it "topEntityRunner" $ do
let clk = systemClockGen
rst = systemResetGen
en = enableGen
axiChunk isLast =
Axi4StreamM2S
{ _tdata = 0x01 :> 0x02 :> 0x03 :> 0x04 :> 0x05 :> 0x06 :> 0x07 :> 0x08 :> Nil
, _tkeep = True :> True :> True :> True :> True :> True :> True :> True :> Nil
, _tstrb = True :> True :> True :> True :> True :> True :> True :> True :> Nil
, _tid = 0
, _tdest = 0
, _tuser = False
, _tlast = isLast
}
axiChunks :: Vec 20 (Axi4StreamM2S AxiCfg64 Bool) =
replicate d19 (axiChunk False) ++ axiChunk True :> Nil
outSignal = withClockResetEnable clk rst en $
topEntityRunner roundTripTopEntity axiChunks
sampledOutput = withClockResetEnable clk rst en $
sampleWithResetN d1 20 outSignal
toList axiChunks `shouldBe` catMaybes sampledOutput
For some reason, though, this hangs during testing. There’s two notable things about this:
- it doesn’t hang if the code isn’t in any sense recursive, ie if the data I’m feeding in doesn’t depend at all on the
tready
signals coming out. - it doesn’t hang if I use an even more trivial (combinatorial)
topEntity
function, below:
trivialTopEntity clk rst (m2sIn, s2mIn) =
let s2mOut = s2mIn
m2sOut = m2sIn
in (s2mOut, m2sOut)
Any help gratefully appreciated.