{-# LANGUAGE NoImplicitPrelude #-}
{-# OPTIONS_HADDOCK hide #-}
module Protocols.PacketStream.Base (
PacketStreamM2S (..),
PacketStreamS2M (..),
PacketStream,
nullByte,
toCSignal,
unsafeFromCSignal,
unsafeDropBackpressure,
empty,
consume,
forceResetSanity,
zeroOutInvalidBytesC,
stripTrailingEmptyC,
unsafeAbortOnBackpressureC,
truncateAbortedPackets,
void,
fanout,
registerBwd,
registerFwd,
registerBoth,
fstMeta,
sndMeta,
mapMeta,
filterMeta,
firstMeta,
secondMeta,
bimapMeta,
eitherMeta,
mapMetaS,
filterMetaS,
firstMetaS,
secondMetaS,
bimapMetaS,
eitherMetaS,
) where
import Prelude qualified as P
import Control.DeepSeq (NFData)
import Clash.Prelude hiding (empty, sample)
import Data.Bifunctor qualified as B
import Data.Coerce (coerce)
import Data.Maybe qualified as Maybe
import Data.Proxy
import Protocols
import Protocols.DfConv qualified as DfConv
import Protocols.Idle
data PacketStreamM2S (dataWidth :: Nat) (meta :: Type) = PacketStreamM2S
{ _data :: Vec dataWidth (BitVector 8)
, _last :: Maybe (Index (dataWidth + 1))
, _meta :: meta
, _abort :: Bool
}
deriving (Generic, ShowX, Show, NFData, Bundle, Functor)
deriving instance
(KnownNat dataWidth, NFDataX meta) =>
NFDataX (PacketStreamM2S dataWidth meta)
instance (KnownNat dataWidth, Eq meta) => Eq (PacketStreamM2S dataWidth meta) where
t1 == t2 = lastEq && metaEq && abortEq && dataEq
where
lastEq = _last t1 == _last t2
metaEq = _meta t1 == _meta t2
abortEq = _abort t1 == _abort t2
mask = case _last t1 of
Nothing -> repeat False
Just size -> imap (\i _ -> resize i >= size) (_data t1)
dataEq = case compareSNat (SNat @dataWidth) d0 of
SNatLE -> True
SNatGT ->
leToPlus @1 @dataWidth
$ fold (&&)
$ zipWith3 (\b1 b2 isNull -> isNull || b1 == b2) (_data t1) (_data t2) mask
instance Default (Maybe (PacketStreamM2S dataWidth meta)) where
def = Nothing
deriveAutoReg ''PacketStreamM2S
newtype PacketStreamS2M = PacketStreamS2M
{ _ready :: Bool
}
deriving stock (Generic, Show)
deriving anyclass (Bundle, ShowX)
deriving newtype (NFDataX, Eq)
instance Default PacketStreamS2M where
def = PacketStreamS2M True
deriveAutoReg ''PacketStreamS2M
data PacketStream (dom :: Domain) (dataWidth :: Nat) (meta :: Type)
instance Protocol (PacketStream dom dataWidth meta) where
type
Fwd (PacketStream dom dataWidth meta) =
Signal dom (Maybe (PacketStreamM2S dataWidth meta))
type Bwd (PacketStream dom dataWidth meta) = Signal dom PacketStreamS2M
instance IdleCircuit (PacketStream dom dataWidth meta) where
idleBwd _ = pure (PacketStreamS2M False)
idleFwd _ = pure Nothing
instance DfConv.DfConv (PacketStream dom dataWidth meta) where
type Dom (PacketStream dom dataWidth meta) = dom
type FwdPayload (PacketStream dom dataWidth meta) = PacketStreamM2S dataWidth meta
toDfCircuit _ = fromSignals go
where
go (fwdIn, bwdIn) =
(
( fmap coerce bwdIn
, pure (deepErrorX "PacketStream toDfCircuit: undefined")
)
, P.fst fwdIn
)
fromDfCircuit _ = fromSignals go
where
go (fwdIn, bwdIn) =
( coerce <$> P.fst bwdIn
,
( fwdIn
, pure (deepErrorX "PacketStream fromDfCircuit: undefined")
)
)
nullByte ::
String ->
BitVector 8
nullByte src =
deepErrorX
$ src
<> ": value of PacketStream null byte is undefined. "
<> "Data bytes that are not enabled must not be evaluated."
unsafeFromCSignal ::
forall dom dataWidth meta.
Circuit
(CSignal dom (Maybe (PacketStreamM2S dataWidth meta)))
(PacketStream dom dataWidth meta)
unsafeFromCSignal = Circuit (\(fwdInS, _) -> ((), fwdInS))
toCSignal ::
forall dom dataWidth meta.
(HiddenClockResetEnable dom) =>
Circuit
(PacketStream dom dataWidth meta)
(CSignal dom (Maybe (PacketStreamM2S dataWidth meta)))
toCSignal = forceResetSanity |> Circuit (\(fwdIn, _) -> (pure (PacketStreamS2M True), fwdIn))
unsafeDropBackpressure ::
(HiddenClockResetEnable dom) =>
Circuit
(PacketStream dom dwIn meta)
(PacketStream dom dwOut meta) ->
Circuit
(CSignal dom (Maybe (PacketStreamM2S dwIn meta)))
(CSignal dom (Maybe (PacketStreamM2S dwOut meta)))
unsafeDropBackpressure ckt = unsafeFromCSignal |> ckt |> toCSignal
unsafeAbortOnBackpressureC ::
forall (dataWidth :: Nat) (meta :: Type) (dom :: Domain).
(HiddenClockResetEnable dom) =>
Circuit
(CSignal dom (Maybe (PacketStreamM2S dataWidth meta)))
(PacketStream dom dataWidth meta)
unsafeAbortOnBackpressureC =
Circuit $ \(fwdInS, bwdInS) -> ((), go <$> bundle (fwdInS, bwdInS))
where
go (fwdIn, bwdIn) =
fmap (\pkt -> pkt{_abort = _abort pkt || not (_ready bwdIn)}) fwdIn
forceResetSanity ::
forall dom dataWidth meta.
(KnownDomain dom, HiddenReset dom) =>
Circuit (PacketStream dom dataWidth meta) (PacketStream dom dataWidth meta)
forceResetSanity = forceResetSanityGeneric
stripTrailingEmptyC ::
forall (dataWidth :: Nat) (meta :: Type) (dom :: Domain).
(HiddenClockResetEnable dom) =>
(KnownNat dataWidth) =>
(NFDataX meta) =>
Circuit
(PacketStream dom dataWidth meta)
(PacketStream dom dataWidth meta)
stripTrailingEmptyC = forceResetSanity |> fromSignals (mealyB go (False, False, Nothing))
where
go (notFirst, flush, cache) (Nothing, bwdIn) =
((notFirst, flush', cache'), (PacketStreamS2M True, fwdOut))
where
fwdOut = if flush then cache else Nothing
(flush', cache')
| flush && _ready bwdIn = (False, Nothing)
| otherwise = (flush, cache)
go (notFirst, flush, cache) (Just transferIn, bwdIn) = (nextStOut, (bwdOut, fwdOut))
where
(notFirst', flush', cache', fwdOut) = case _last transferIn of
Nothing -> (True, False, Just transferIn, cache)
Just i ->
let trailing = i == 0 && notFirst
in ( False
, not trailing
, if trailing then Nothing else Just transferIn
, if trailing
then (\x -> x{_last = Just maxBound, _abort = _abort x || _abort transferIn}) <$> cache
else cache
)
bwdOut = PacketStreamS2M (Maybe.isNothing cache || _ready bwdIn)
nextStOut
| Maybe.isNothing cache || _ready bwdIn = (notFirst', flush', cache')
| otherwise = (notFirst, flush, cache)
zeroOutInvalidBytesC ::
forall (dom :: Domain) (dataWidth :: Nat) (meta :: Type).
(KnownNat dataWidth) =>
(1 <= dataWidth) =>
Circuit
(PacketStream dom dataWidth meta)
(PacketStream dom dataWidth meta)
zeroOutInvalidBytesC = Circuit $ \(fwdIn, bwdIn) -> (bwdIn, fmap (go <$>) fwdIn)
where
go transferIn = transferIn{_data = dataOut}
where
dataOut = case _last transferIn of
Nothing -> _data transferIn
Just i ->
imap
(\(j :: Index dataWidth) byte -> if resize j < i then byte else 0x00)
(_data transferIn)
data TruncateState = Forwarding | Truncating
deriving (Show, ShowX, Eq, Generic, NFDataX)
truncateAbortedPackets ::
forall (dom :: Domain) (dataWidth :: Nat) (meta :: Type).
(HiddenClockResetEnable dom, KnownNat dataWidth, ShowX meta) =>
(1 <= dataWidth) =>
Circuit
(PacketStream dom dataWidth meta)
(PacketStream dom dataWidth meta)
truncateAbortedPackets = forceResetSanity |> Circuit (unbundle . mealy go Forwarding . bundle)
where
go state (Nothing, _) = (state, (deepErrorX "truncateAbortedPackets: undefined ack", Nothing))
go Truncating (Just m2s, _) = (nextState, (PacketStreamS2M True, Nothing))
where
nextState
| Maybe.isJust m2s._last = Forwarding
| otherwise = Truncating
go Forwarding (Just m2sLeft, PacketStreamS2M ack) = (nextState, (PacketStreamS2M ack, Just m2sRight))
where
m2sRight
| m2sLeft._abort = m2sLeft{_last = Just 0}
| otherwise = m2sLeft
nextState
| Maybe.isNothing m2sLeft._last && m2sLeft._abort && ack = Truncating
| otherwise = Forwarding
fanout ::
forall n dataWidth meta dom.
(HiddenClockResetEnable dom) =>
(KnownNat n) =>
(KnownNat dataWidth) =>
(1 <= n) =>
(NFDataX meta) =>
Circuit
(PacketStream dom dataWidth meta)
(Vec n (PacketStream dom dataWidth meta))
fanout = DfConv.fanout Proxy Proxy
registerFwd ::
forall dataWidth meta dom.
(HiddenClockResetEnable dom) =>
(KnownNat dataWidth) =>
(NFDataX meta) =>
Circuit (PacketStream dom dataWidth meta) (PacketStream dom dataWidth meta)
registerFwd = DfConv.registerFwd Proxy Proxy
registerBwd ::
forall dataWidth meta dom.
(HiddenClockResetEnable dom) =>
(KnownNat dataWidth) =>
(NFDataX meta) =>
Circuit (PacketStream dom dataWidth meta) (PacketStream dom dataWidth meta)
registerBwd = DfConv.registerBwd Proxy Proxy
registerBoth ::
forall dataWidth meta dom.
(HiddenClockResetEnable dom) =>
(KnownNat dataWidth) =>
(NFDataX meta) =>
Circuit (PacketStream dom dataWidth meta) (PacketStream dom dataWidth meta)
registerBoth = registerBwd |> registerFwd
empty :: Circuit () (PacketStream dom dataWidth meta)
empty = Circuit (const ((), pure Nothing))
consume :: (HiddenReset dom) => Circuit (PacketStream dom dataWidth meta) ()
consume = Circuit (const (pure (PacketStreamS2M True), ()))
void :: (HiddenClockResetEnable dom) => Circuit (PacketStream dom dataWidth meta) ()
void = DfConv.void Proxy
fstMeta :: Circuit (PacketStream dom dataWidth (a, b)) (PacketStream dom dataWidth a)
fstMeta = mapMeta P.fst
sndMeta :: Circuit (PacketStream dom dataWidth (a, b)) (PacketStream dom dataWidth b)
sndMeta = mapMeta P.snd
mapMeta ::
(metaIn -> metaOut) ->
Circuit (PacketStream dom dataWidth metaIn) (PacketStream dom dataWidth metaOut)
mapMeta f = mapMetaS (pure f)
mapMetaS ::
Signal dom (metaIn -> metaOut) ->
Circuit (PacketStream dom dataWidth metaIn) (PacketStream dom dataWidth metaOut)
mapMetaS fS = Circuit $ \(fwdIn, bwdIn) -> (bwdIn, go <$> bundle (fwdIn, fS))
where
go (inp, f) = (\inPkt -> inPkt{_meta = f (_meta inPkt)}) <$> inp
filterMeta ::
(meta -> Bool) ->
Circuit (PacketStream dom dataWidth meta) (PacketStream dom dataWidth meta)
filterMeta p = filterMetaS (pure p)
filterMetaS ::
Signal dom (meta -> Bool) ->
Circuit (PacketStream dom dataWidth meta) (PacketStream dom dataWidth meta)
filterMetaS pS = Circuit $ \(fwdIn, bwdIn) -> unbundle (go <$> bundle (fwdIn, bwdIn, pS))
where
go (Nothing, bwdIn, _) = (bwdIn, Nothing)
go (Just inPkt, bwdIn, predicate)
| predicate (_meta inPkt) = (bwdIn, Just inPkt)
| otherwise = (PacketStreamS2M True, Nothing)
eitherMeta ::
(a -> c) ->
(b -> c) ->
Circuit
(PacketStream dom dataWidth (Either a b))
(PacketStream dom dataWidth c)
eitherMeta f g = eitherMetaS (pure f) (pure g)
eitherMetaS ::
Signal dom (a -> c) ->
Signal dom (b -> c) ->
Circuit
(PacketStream dom dataWidth (Either a b))
(PacketStream dom dataWidth c)
eitherMetaS fS gS = mapMetaS (liftA2 P.either fS gS)
bimapMeta ::
(B.Bifunctor p) =>
(a -> b) ->
(c -> d) ->
Circuit
(PacketStream dom dataWidth (p a c))
(PacketStream dom dataWidth (p b d))
bimapMeta f g = bimapMetaS (pure f) (pure g)
bimapMetaS ::
(B.Bifunctor p) =>
Signal dom (a -> b) ->
Signal dom (c -> d) ->
Circuit
(PacketStream dom dataWidth (p a c))
(PacketStream dom dataWidth (p b d))
bimapMetaS fS gS = mapMetaS (liftA2 B.bimap fS gS)
firstMeta ::
(B.Bifunctor p) =>
(a -> b) ->
Circuit
(PacketStream dom dataWidth (p a c))
(PacketStream dom dataWidth (p b c))
firstMeta f = firstMetaS (pure f)
firstMetaS ::
(B.Bifunctor p) =>
Signal dom (a -> b) ->
Circuit
(PacketStream dom dataWidth (p a c))
(PacketStream dom dataWidth (p b c))
firstMetaS fS = mapMetaS (B.first <$> fS)
secondMeta ::
(B.Bifunctor p) =>
(b -> c) ->
Circuit
(PacketStream dom dataWidth (p a b))
(PacketStream dom dataWidth (p a c))
secondMeta f = secondMetaS (pure f)
secondMetaS ::
(B.Bifunctor p) =>
Signal dom (b -> c) ->
Circuit
(PacketStream dom dataWidth (p a b))
(PacketStream dom dataWidth (p a c))
secondMetaS fS = mapMetaS (B.second <$> fS)