{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# OPTIONS_GHC -fconstraint-solver-iterations=5 #-}
{-# OPTIONS_GHC -fplugin Protocols.Plugin #-}
{-# OPTIONS_HADDOCK hide #-}
module Protocols.PacketStream.Depacketizers (
depacketizerC,
depacketizeToDfC,
) where
import Clash.Prelude
import Clash.Sized.Vector.Extra
import Protocols
import Protocols.PacketStream.Base
import Data.Constraint (Dict (Dict))
import Data.Constraint.Nat.Extra
import Data.Data ((:~:) (Refl))
import Data.Maybe
type BufSize (headerBytes :: Nat) (dataWidth :: Nat) =
dataWidth * headerBytes `DivRU` dataWidth
type ForwardBytes (headerBytes :: Nat) (dataWidth :: Nat) =
(dataWidth - (headerBytes `Mod` dataWidth)) `Mod` dataWidth
type DepacketizerCt (headerBytes :: Nat) (dataWidth :: Nat) =
( KnownNat headerBytes
, KnownNat dataWidth
, 1 <= headerBytes
, 1 <= dataWidth
, BufSize headerBytes dataWidth ~ headerBytes + ForwardBytes headerBytes dataWidth
, headerBytes `Mod` dataWidth <= dataWidth
, ForwardBytes headerBytes dataWidth <= dataWidth
)
data DepacketizerState (headerBytes :: Nat) (dataWidth :: Nat)
= Parse
{ _aborted :: Bool
, _buf :: Vec (BufSize headerBytes dataWidth) (BitVector 8)
, _counter :: Index (headerBytes `DivRU` dataWidth)
}
| Forward
{ _aborted :: Bool
, _buf :: Vec (BufSize headerBytes dataWidth) (BitVector 8)
, _counter :: Index (headerBytes `DivRU` dataWidth)
, _lastFwd :: Bool
}
deriving (Show, ShowX, Generic)
deriving instance
(DepacketizerCt headerBytes dataWidth) =>
NFDataX (DepacketizerState headerBytes dataWidth)
instance
(DepacketizerCt headerBytes dataWidth) =>
Default (DepacketizerState headerBytes dataWidth)
where
def :: DepacketizerState headerBytes dataWidth
def = Parse False (deepErrorX "depacketizerT: undefined initial buffer") maxBound
depacketizerT ::
forall
(headerBytes :: Nat)
(header :: Type)
(metaIn :: Type)
(metaOut :: Type)
(dataWidth :: Nat).
(BitPack header) =>
(BitSize header ~ headerBytes * 8) =>
(NFDataX metaIn) =>
(DepacketizerCt headerBytes dataWidth) =>
(header -> metaIn -> metaOut) ->
DepacketizerState headerBytes dataWidth ->
(Maybe (PacketStreamM2S dataWidth metaIn), PacketStreamS2M) ->
( DepacketizerState headerBytes dataWidth
, (PacketStreamS2M, Maybe (PacketStreamM2S dataWidth metaOut))
)
depacketizerT _ Parse{..} (Just PacketStreamM2S{..}, _) = (nextStOut, (PacketStreamS2M outReady, Nothing))
where
nextAborted = _aborted || _abort
nextCounter = pred _counter
nextParseBuf = fst (shiftInAtN _buf _data)
prematureEnd idx = case sameNat d0 (SNat @(headerBytes `Mod` dataWidth)) of
Just Refl -> idx /= maxBound
_ -> idx < natToNum @(headerBytes `Mod` dataWidth)
nextStOut = case (_counter == 0, _last) of
(False, Nothing) ->
Parse nextAborted nextParseBuf nextCounter
(False, Just _) ->
def
(True, Just idx)
| prematureEnd idx ->
def
(True, _) ->
Forward nextAborted nextParseBuf nextCounter (isJust _last)
outReady
| Forward{_lastFwd = True} <- nextStOut = False
| otherwise = True
depacketizerT toMetaOut st@Forward{..} (Just pkt@PacketStreamM2S{..}, bwdIn) = (nextStOut, (PacketStreamS2M outReady, Just outPkt))
where
nextAborted = _aborted || _abort
nextBuf = header ++ nextFwdBytes
newLast = adjustLast <$> _last
(header, fwdBytes) = splitAt (SNat @headerBytes) _buf
(dataOut, nextFwdBytes) = splitAt (SNat @dataWidth) (fwdBytes ++ _data)
adjustLast ::
Index (dataWidth + 1) -> Either (Index (dataWidth + 1)) (Index (dataWidth + 1))
adjustLast idx
| _lastFwd = Right (idx - x)
| idx <= x = Left (idx + y)
| otherwise = Right (idx - x)
where
x = natToNum @(headerBytes `Mod` dataWidth)
y = natToNum @(ForwardBytes headerBytes dataWidth)
outPkt = case sameNat d0 (SNat @(headerBytes `Mod` dataWidth)) of
Just Refl ->
pkt
{ _data = _data
, _last = if _lastFwd then Just 0 else _last
, _meta = toMetaOut (bitCoerce header) _meta
, _abort = nextAborted
}
Nothing ->
pkt
{ _data = dataOut
, _last =
if _lastFwd
then either Just Just =<< newLast
else either Just (const Nothing) =<< newLast
, _meta = toMetaOut (bitCoerce header) _meta
, _abort = nextAborted
}
nextForwardSt = Forward nextAborted nextBuf maxBound
nextSt = case sameNat d0 (SNat @(headerBytes `Mod` dataWidth)) of
Just Refl
| isJust _last -> def
| otherwise -> nextForwardSt False
Nothing ->
case (_lastFwd, newLast) of
(False, Nothing) -> nextForwardSt False
(False, Just (Right _)) -> nextForwardSt True
_ -> def
nextStOut = if _ready bwdIn then nextSt else st
outReady
| Forward{_lastFwd = True} <- nextStOut = False
| otherwise = _ready bwdIn
depacketizerT _ st (Nothing, _) = (st, (deepErrorX "undefined ack", Nothing))
depacketizerC ::
forall
(headerBytes :: Nat)
(header :: Type)
(metaIn :: Type)
(metaOut :: Type)
(dataWidth :: Nat)
(dom :: Domain).
(HiddenClockResetEnable dom) =>
(BitPack header) =>
(BitSize header ~ headerBytes * 8) =>
(NFDataX metaIn) =>
(KnownNat headerBytes) =>
(KnownNat dataWidth) =>
(1 <= headerBytes) =>
(1 <= dataWidth) =>
(header -> metaIn -> metaOut) ->
Circuit (PacketStream dom dataWidth metaIn) (PacketStream dom dataWidth metaOut)
depacketizerC toMetaOut = forceResetSanity |> fromSignals ckt
where
ckt = case ( eqTimesDivRu @dataWidth @headerBytes
, leModulusDivisor @headerBytes @dataWidth
, leModulusDivisor @(dataWidth - (headerBytes `Mod` dataWidth)) @dataWidth
) of
(Dict, Dict, Dict) -> mealyB (depacketizerT toMetaOut) def
type DepacketizeToDfCt (headerBytes :: Nat) (dataWidth :: Nat) =
( KnownNat headerBytes
, KnownNat dataWidth
, 1 <= headerBytes
, 1 <= dataWidth
, headerBytes <= headerBytes `DivRU` dataWidth * dataWidth
)
data DfDepacketizerState (headerBytes :: Nat) (dataWidth :: Nat)
= DfParse
{ _dfAborted :: Bool
, _dfParseBuf :: Vec (BufSize headerBytes dataWidth) (BitVector 8)
, _dfCounter :: Index (headerBytes `DivRU` dataWidth)
}
| DfConsumePadding
{ _dfAborted :: Bool
, _dfParseBuf :: Vec (BufSize headerBytes dataWidth) (BitVector 8)
}
deriving (Generic, Show, ShowX)
deriving instance
(DepacketizeToDfCt headerBytes dataWidth) =>
(NFDataX (DfDepacketizerState headerBytes dataWidth))
instance
(DepacketizeToDfCt headerBytes dataWidth) =>
Default (DfDepacketizerState headerBytes dataWidth)
where
def :: DfDepacketizerState headerBytes dataWidth
def = DfParse False (deepErrorX "depacketizeToDfT: undefined initial buffer") maxBound
depacketizeToDfT ::
forall
(headerBytes :: Nat)
(header :: Type)
(meta :: Type)
(a :: Type)
(dataWidth :: Nat).
(NFDataX meta) =>
(BitPack header) =>
(BitSize header ~ headerBytes * 8) =>
(DepacketizeToDfCt headerBytes dataWidth) =>
(header -> meta -> a) ->
DfDepacketizerState headerBytes dataWidth ->
(Maybe (PacketStreamM2S dataWidth meta), Ack) ->
(DfDepacketizerState headerBytes dataWidth, (PacketStreamS2M, Maybe a))
depacketizeToDfT _ DfParse{..} (Just (PacketStreamM2S{..}), _) = (nextStOut, (PacketStreamS2M readyOut, Nothing))
where
nextAborted = _dfAborted || _abort
nextParseBuf = fst (shiftInAtN _dfParseBuf _data)
prematureEnd idx =
case compareSNat (SNat @(headerBytes `Mod` dataWidth)) d0 of
SNatGT -> idx < (natToNum @(headerBytes `Mod` dataWidth))
_ -> idx < (natToNum @dataWidth)
(nextStOut, readyOut) =
case (_dfCounter == 0, _last) of
(False, Nothing) ->
(DfParse nextAborted nextParseBuf (pred _dfCounter), True)
(False, Just _) ->
(def, True)
(True, Just idx) ->
if nextAborted || prematureEnd idx
then (def, True)
else (DfConsumePadding nextAborted nextParseBuf, False)
(True, Nothing) ->
(DfConsumePadding nextAborted nextParseBuf, True)
depacketizeToDfT toOut st@DfConsumePadding{..} (Just (PacketStreamM2S{..}), Ack readyIn) = (nextStOut, (PacketStreamS2M readyOut, fwdOut))
where
nextAborted = _dfAborted || _abort
outDf = toOut (bitCoerce (takeLe (SNat @headerBytes) _dfParseBuf)) _meta
(nextSt, fwdOut) =
if isJust _last
then (def, if nextAborted then Nothing else Just outDf)
else (st{_dfAborted = nextAborted}, Nothing)
readyOut = isNothing fwdOut || readyIn
nextStOut = if readyOut then nextSt else st
depacketizeToDfT _ st (Nothing, _) = (st, (deepErrorX "undefined ack", Nothing))
depacketizeToDfC ::
forall
(headerBytes :: Nat)
(header :: Type)
(meta :: Type)
(a :: Type)
(dataWidth :: Nat)
(dom :: Domain).
(HiddenClockResetEnable dom) =>
(NFDataX meta) =>
(NFDataX a) =>
(BitPack header) =>
(KnownNat headerBytes) =>
(KnownNat dataWidth) =>
(1 <= headerBytes) =>
(1 <= dataWidth) =>
(BitSize header ~ headerBytes * 8) =>
(header -> meta -> a) ->
Circuit (PacketStream dom dataWidth meta) (Df dom a)
depacketizeToDfC toOut = forceResetSanity |> fromSignals ckt
where
ckt = case leTimesDivRu @dataWidth @headerBytes of
Dict -> mealyB (depacketizeToDfT toOut) def