{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fconstraint-solver-iterations=20 #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Protocols.Experimental.Simulate (
module Protocols.Experimental.Simulate.Types,
simulateC,
simulateCS,
simulateCSE,
simulateCircuit,
def,
) where
import Clash.Explicit.Prelude qualified as CE
import Clash.Prelude (type (*), type (+))
import Clash.Prelude qualified as C
import Data.Default (Default (def))
import Data.Proxy
import Data.Tuple (swap)
import Protocols.Experimental.Simulate.Types
import Protocols.Internal
import Protocols.Internal.TH (
backPressureTupleInstances,
drivableTupleInstances,
simulateTupleInstances,
)
import Protocols.Plugin.Cpp (maxTupleSize)
instance Backpressure () where
boolsToBwd _ _ = ()
instance (Backpressure a, Backpressure b) => Backpressure (a, b) where
boolsToBwd _ bs = (boolsToBwd (Proxy @a) bs, boolsToBwd (Proxy @b) bs)
backPressureTupleInstances 3 maxTupleSize
instance (C.KnownNat n, Backpressure a) => Backpressure (C.Vec n a) where
boolsToBwd _ bs = C.repeat (boolsToBwd (Proxy @a) bs)
instance Backpressure (CSignal dom a) where
boolsToBwd _ _ = ()
instance Simulate () where
type SimulateFwdType () = ()
type SimulateBwdType () = ()
type SimulateChannels () = 0
simToSigFwd _ = id
simToSigBwd _ = id
sigToSimFwd _ = id
sigToSimBwd _ = id
stallC _ _ = idC
instance Drivable () where
type ExpectType () = ()
toSimulateType Proxy () = ()
fromSimulateType Proxy () = ()
driveC _ _ = idC
sampleC _ _ = ()
instance (Simulate a, Simulate b) => Simulate (a, b) where
type SimulateFwdType (a, b) = (SimulateFwdType a, SimulateFwdType b)
type SimulateBwdType (a, b) = (SimulateBwdType a, SimulateBwdType b)
type SimulateChannels (a, b) = SimulateChannels a + SimulateChannels b
simToSigFwd Proxy ~(fwdsA, fwdsB) = (simToSigFwd (Proxy @a) fwdsA, simToSigFwd (Proxy @b) fwdsB)
simToSigBwd Proxy ~(bwdsA, bwdsB) = (simToSigBwd (Proxy @a) bwdsA, simToSigBwd (Proxy @b) bwdsB)
sigToSimFwd Proxy ~(fwdSigA, fwdSigB) = (sigToSimFwd (Proxy @a) fwdSigA, sigToSimFwd (Proxy @b) fwdSigB)
sigToSimBwd Proxy ~(bwdSigA, bwdSigB) = (sigToSimBwd (Proxy @a) bwdSigA, sigToSimBwd (Proxy @b) bwdSigB)
stallC conf stalls =
let
(stallsL, stallsR) = C.splitAtI @(SimulateChannels a) @(SimulateChannels b) stalls
Circuit stalledL = stallC @a conf stallsL
Circuit stalledR = stallC @b conf stallsR
in
Circuit $ \(~((fwdL0, fwdR0), (bwdL0, bwdR0))) ->
let
(fwdL1, bwdL1) = stalledL (fwdL0, bwdL0)
(fwdR1, bwdR1) = stalledR (fwdR0, bwdR0)
in
((fwdL1, fwdR1), (bwdL1, bwdR1))
simulateTupleInstances 3 maxTupleSize
instance (Drivable a, Drivable b) => Drivable (a, b) where
type ExpectType (a, b) = (ExpectType a, ExpectType b)
toSimulateType Proxy (t1, t2) =
( toSimulateType (Proxy @a) t1
, toSimulateType (Proxy @b) t2
)
fromSimulateType Proxy (t1, t2) =
( fromSimulateType (Proxy @a) t1
, fromSimulateType (Proxy @b) t2
)
driveC conf (fwd1, fwd2) =
let (Circuit f1, Circuit f2) = (driveC @a conf fwd1, driveC @b conf fwd2)
in Circuit (\(_, ~(bwd1, bwd2)) -> ((), (snd (f1 ((), bwd1)), snd (f2 ((), bwd2)))))
sampleC conf (Circuit f) =
let
bools = replicate (resetCycles conf) False <> repeat True
(_, (fwd1, fwd2)) = f ((), (boolsToBwd (Proxy @a) bools, boolsToBwd (Proxy @b) bools))
in
( sampleC @a conf (Circuit $ \_ -> ((), fwd1))
, sampleC @b conf (Circuit $ \_ -> ((), fwd2))
)
drivableTupleInstances 3 maxTupleSize
instance (Simulate a) => Simulate (Reverse a) where
type SimulateFwdType (Reverse a) = SimulateBwdType a
type SimulateBwdType (Reverse a) = SimulateFwdType a
type SimulateChannels (Reverse a) = SimulateChannels a
simToSigFwd Proxy = simToSigBwd (Proxy @a)
simToSigBwd Proxy = simToSigFwd (Proxy @a)
sigToSimFwd Proxy = sigToSimBwd (Proxy @a)
sigToSimBwd Proxy = sigToSimFwd (Proxy @a)
stallC conf stalls =
let Circuit stalled = stallC @a conf stalls
in Circuit $ \(fwd, bwd) -> swap (stalled (bwd, fwd))
instance (CE.KnownNat n, Simulate a) => Simulate (C.Vec n a) where
type SimulateFwdType (C.Vec n a) = C.Vec n (SimulateFwdType a)
type SimulateBwdType (C.Vec n a) = C.Vec n (SimulateBwdType a)
type SimulateChannels (C.Vec n a) = n * SimulateChannels a
simToSigFwd Proxy = C.map (simToSigFwd (Proxy @a))
simToSigBwd Proxy = C.map (simToSigBwd (Proxy @a))
sigToSimFwd Proxy = C.map (sigToSimFwd (Proxy @a))
sigToSimBwd Proxy = C.map (sigToSimBwd (Proxy @a))
stallC conf stalls0 =
let
stalls1 = C.unconcatI @n @(SimulateChannels a) stalls0
stalled = C.map (toSignals . stallC @a conf) stalls1
in
Circuit $ \(fwds, bwds) -> C.unzip (C.zipWith ($) stalled (C.zip fwds bwds))
instance (C.KnownNat n, Drivable a) => Drivable (C.Vec n a) where
type ExpectType (C.Vec n a) = C.Vec n (ExpectType a)
toSimulateType Proxy = C.map (toSimulateType (Proxy @a))
fromSimulateType Proxy = C.map (fromSimulateType (Proxy @a))
driveC conf fwds =
let circuits = C.map (($ ()) . curry . (toSignals @_ @a) . driveC conf) fwds
in Circuit (\(_, bwds) -> ((), C.map snd (C.zipWith ($) circuits bwds)))
sampleC conf (Circuit f) =
let
bools = replicate (resetCycles conf) False <> repeat True
(_, fwds) = f ((), (C.repeat (boolsToBwd (Proxy @a) bools)))
in
C.map (\fwd -> sampleC @a conf (Circuit $ \_ -> ((), fwd))) fwds
instance (C.KnownDomain dom) => Simulate (CSignal dom a) where
type SimulateFwdType (CSignal dom a) = [a]
type SimulateBwdType (CSignal dom a) = ()
type SimulateChannels (CSignal dom a) = 1
simToSigFwd Proxy list = C.fromList_lazy list
simToSigBwd Proxy () = ()
sigToSimFwd Proxy sig = C.sample_lazy sig
sigToSimBwd Proxy _ = ()
stallC _ _ = idC
instance (C.NFDataX a, C.ShowX a, Show a, C.KnownDomain dom) => Drivable (CSignal dom a) where
type ExpectType (CSignal dom a) = [a]
toSimulateType Proxy = id
fromSimulateType Proxy = id
driveC _conf [] = error "CSignal.driveC: Can't drive with empty list"
driveC SimulationConfig{resetCycles} fwd0@(f : _) =
let fwd1 = C.fromList_lazy (replicate resetCycles f <> fwd0 <> repeat f)
in Circuit (\_ -> ((), fwd1))
sampleC SimulationConfig{resetCycles, ignoreReset, timeoutAfter} (Circuit f) =
let sampled = CE.sampleN_lazy timeoutAfter (snd (f ((), ())))
in if ignoreReset then drop resetCycles sampled else sampled
simulateC ::
forall a b.
(Drivable a, Drivable b) =>
Circuit a b ->
SimulationConfig ->
SimulateFwdType a ->
SimulateFwdType b
simulateC c conf as =
sampleC conf (driveC conf as |> c)
simulateCS ::
forall a b.
(Drivable a, Drivable b) =>
Circuit a b ->
ExpectType a ->
ExpectType b
simulateCS c =
fromSimulateType (Proxy @b)
. simulateC c def{ignoreReset = True}
. toSimulateType (Proxy @a)
simulateCSE ::
forall dom a b.
(Drivable a, Drivable b, C.KnownDomain dom) =>
(C.Clock dom -> C.Reset dom -> C.Enable dom -> Circuit a b) ->
ExpectType a ->
ExpectType b
simulateCSE c = simulateCS (c clk rst ena)
where
clk = C.clockGen
rst = resetGen (resetCycles def)
ena = C.enableGen
resetGen n =
C.unsafeFromActiveHigh $
C.fromList (replicate n True <> repeat False)
simulateCircuit ::
forall a b.
(Simulate a, Simulate b) =>
SimulateFwdType a ->
SimulateBwdType b ->
Circuit a b ->
(SimulateBwdType a, SimulateFwdType b)
simulateCircuit fwds bwds circ =
(sigToSimBwd (Proxy @a) bwdSig, sigToSimFwd (Proxy @b) fwdSig)
where
(bwdSig, fwdSig) =
toSignals circ $
(simToSigFwd (Proxy @a) fwds, simToSigBwd (Proxy @b) bwds)