{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Protocols.Experimental.Df (
module Protocols.Df,
module Protocols.Experimental.Simulate,
drive,
stall,
sample,
simulate,
) where
import Clash.Explicit.Prelude qualified as CE
import Clash.Prelude qualified as C
import Clash.Signal.Internal (Signal (..))
import Data.Bifunctor qualified as B
import Data.Bool (bool)
import Data.Coerce qualified as Coerce
import Data.List ((\\))
import Data.Maybe qualified as Maybe
import Data.Proxy
import GHC.Stack (HasCallStack)
import Prelude
import Prelude qualified as P
import Protocols
import Protocols.Df
import Protocols.Experimental.Simulate
instance Backpressure (Df dom a) where
boolsToBwd _ = C.fromList_lazy . Coerce.coerce
instance (C.KnownDomain dom, C.NFDataX a, C.ShowX a, Show a) => Simulate (Df dom a) where
type SimulateFwdType (Df dom a) = [Maybe a]
type SimulateBwdType (Df dom a) = [Ack]
type SimulateChannels (Df dom a) = 1
simToSigFwd _ = C.fromList_lazy
simToSigBwd _ = C.fromList_lazy
sigToSimFwd _ s = C.sample_lazy s
sigToSimBwd _ s = C.sample_lazy s
stallC conf (C.head -> (stallAck, stalls)) = stall conf stallAck stalls
instance (C.KnownDomain dom, C.NFDataX a, C.ShowX a, Show a) => Drivable (Df dom a) where
type ExpectType (Df dom a) = [a]
toSimulateType Proxy = P.map Just
fromSimulateType Proxy = Maybe.catMaybes
driveC = drive
sampleC = sample
drive ::
forall dom a.
(C.KnownDomain dom) =>
SimulationConfig ->
[Maybe a] ->
Circuit () (Df dom a)
drive SimulationConfig{resetCycles} s0 =
Circuit $
((),)
. C.fromList_lazy
. go s0 resetCycles
. CE.sample_lazy
. P.snd
where
go _ resetN ~(ack : acks)
| resetN > 0 =
Nothing : (ack `C.seqX` go s0 (resetN - 1) acks)
go [] _ ~(ack : acks) =
Nothing : (ack `C.seqX` go [] 0 acks)
go (Nothing : is) _ ~(ack : acks) =
Nothing : (ack `C.seqX` go is 0 acks)
go (Just dat : is) _ ~(Ack ack : acks) =
Just dat : go (if ack then is else Just dat : is) 0 acks
sample ::
forall dom b.
(C.KnownDomain dom) =>
SimulationConfig ->
Circuit () (Df dom b) ->
[Maybe b]
sample SimulationConfig{..} c =
CE.sampleN_lazy timeoutAfter $
ignoreWhileInReset $
P.snd $
toSignals c ((), Ack <$> rst_n)
where
ignoreWhileInReset s =
uncurry (bool Nothing)
<$> C.bundle (s, rst_n)
rst_n = C.fromList (replicate resetCycles False <> repeat True)
stall ::
forall dom a.
( C.KnownDomain dom
, HasCallStack
) =>
SimulationConfig ->
StallAck ->
[Int] ->
Circuit (Df dom a) (Df dom a)
stall SimulationConfig{..} stallAck stalls =
Circuit $
uncurry (go stallAcks stalls resetCycles)
where
stallAcks
| stallAck == StallCycle = [minBound .. maxBound] \\ [StallCycle]
| otherwise = [stallAck]
toStallAck :: Maybe a -> Ack -> StallAck -> Ack
toStallAck (Just _) ack = P.const ack
toStallAck Nothing ack = \case
StallWithNack -> Ack False
StallWithAck -> Ack True
StallWithErrorX -> C.errorX "No defined ack"
StallTransparently -> ack
StallCycle -> Ack False
go ::
[StallAck] ->
[Int] ->
Int ->
Signal dom (Maybe a) ->
Signal dom Ack ->
( Signal dom Ack
, Signal dom (Maybe a)
)
go [] ss rs fwd bwd =
go stallAcks ss rs fwd bwd
go (_ : sas) _ resetN (f :- fwd) ~(b :- bwd)
| resetN > 0 =
B.bimap (b :-) (f :-) (go sas stalls (resetN - 1) fwd bwd)
go (sa : sas) [] _ (f :- fwd) ~(b :- bwd) =
B.bimap (toStallAck f b sa :-) (f :-) (go sas [] 0 fwd bwd)
go (sa : sas) ss _ (Nothing :- fwd) ~(b :- bwd) =
B.bimap (toStallAck Nothing b sa :-) (Nothing :-) (go sas ss 0 fwd bwd)
go (_sa : sas) (s : ss) _ (f0 :- fwd) ~(Ack b0 :- bwd) =
let
(f1, b1, s1) = case compare 0 s of
LT -> (Nothing, Ack False, pred s : ss)
EQ -> (f0, Ack b0, if b0 then ss else s : ss)
GT -> error ("Unexpected negative stall: " <> show s)
in
B.bimap (b1 :-) (f1 :-) (go sas s1 0 fwd bwd)
simulate ::
forall dom a b.
(C.KnownDomain dom) =>
SimulationConfig ->
( C.Clock dom ->
C.Reset dom ->
C.Enable dom ->
Circuit (Df dom a) (Df dom b)
) ->
[Maybe a] ->
[Maybe b]
simulate conf@SimulationConfig{..} circ inputs =
sample conf (drive conf inputs |> circ clk rst ena)
where
(clk, rst, ena) = (C.clockGen, resetGen resetCycles, C.enableGen)
resetGen :: (C.KnownDomain dom) => Int -> C.Reset dom
resetGen n =
C.unsafeFromActiveHigh $
C.fromList (replicate n True <> repeat False)