diff --git a/.ci/build_docs.sh b/.ci/build_docs.sh index aebbc724..8001c25a 100755 --- a/.ci/build_docs.sh +++ b/.ci/build_docs.sh @@ -20,29 +20,36 @@ cabal haddock \ |& tee haddock_log set +e -if grep -q "Missing documentation" haddock_log; then + +suppressed_warnings=( + 'Consider exporting it together with its parent(s) for code clarity.' + ) + +grep -v -e "${suppressed_warnings[@]}" haddock_log |& tee haddock_filtered + +if grep -q "Missing documentation" haddock_filtered; then echo -e "\e[1m\e[31mMissing documentation! Scroll up for full log.\e[0m" - grep --color=always -n -C 5 "Missing documentation" haddock_log + grep --color=always -n -C 5 "Missing documentation" haddock_filtered exit 1 fi -if grep -q "If you qualify the identifier, haddock can try to link it anyway" haddock_log; then +if grep -q "If you qualify the identifier, haddock can try to link it anyway" haddock_filtered; then echo -e "\e[1m\e[31mIdentifier out of scope! Scroll up for full log.\e[0m" - grep --color=always -n -C 5 "If you qualify the identifier, haddock can try to link it anyway" haddock_log + grep --color=always -n -C 5 "If you qualify the identifier, haddock can try to link it anyway" haddock_filtered exit 1 fi -if grep -q "could not find link destinations for" haddock_log; then +if grep -q "could not find link destinations for" haddock_filtered; then echo -e "\e[1m\e[31mAn identifier could not be linked! Scroll up for full log.\e[0m" - grep --color=always -n -C 5 "could not find link destinations for" haddock_log + grep --color=always -n -C 5 "could not find link destinations for" haddock_filtered exit 1 fi -if grep -E -q "^Warning:" haddock_log; then +if grep -E -q "^Warning:" haddock_filtered; then echo -e "\e[1m\e[31mAn unknown warning occured. Scroll up for full log.\e[0m" - grep --color=always -n -C 5 -E "^Warning:" haddock_log + grep --color=always -n -C 5 -E "^Warning:" haddock_filtered exit 1 fi # Copy documention to docs/ -ln -s "$(dirname "$(tail -n1 haddock_log)")" docs +ln -s "$(dirname "$(tail -n1 haddock_filtered)")" docs diff --git a/clash-protocols.cabal b/clash-protocols.cabal index bb79b7d2..1175fe7e 100644 --- a/clash-protocols.cabal +++ b/clash-protocols.cabal @@ -155,38 +155,32 @@ library exposed-modules: Protocols - + Protocols.Avalon.MemMap + Protocols.Avalon.Stream Protocols.Axi4.Common - Protocols.Axi4.ReadAddress Protocols.Axi4.ReadData + Protocols.Axi4.Stream Protocols.Axi4.WriteAddress Protocols.Axi4.WriteData Protocols.Axi4.WriteResponse - Protocols.Axi4.Stream - - Protocols.Avalon.Stream - - Protocols.Avalon.MemMap - - Protocols.Wishbone - Protocols.Wishbone.Standard - Protocols.Wishbone.Standard.Hedgehog - Protocols.Cpp Protocols.Df Protocols.DfConv Protocols.Hedgehog Protocols.Hedgehog.Internal + Protocols.Idle Protocols.Internal Protocols.Internal.TaggedBundle Protocols.Internal.TaggedBundle.TH Protocols.Internal.TH Protocols.Internal.Units Protocols.Internal.Units.TH - Protocols.Plugin Protocols.Plugin.Internal + Protocols.Wishbone + Protocols.Wishbone.Standard + Protocols.Wishbone.Standard.Hedgehog -- 'testProperty' is broken upstream, it reports wrong test names -- TODO: test / upstream ^ @@ -197,6 +191,7 @@ library other-modules: Data.Bifunctor.Extra Paths_clash_protocols + Protocols.Internal.Classes default-language: Haskell2010 diff --git a/src/Protocols/Avalon/MemMap.hs b/src/Protocols/Avalon/MemMap.hs index c018197d..22a3ec97 100644 --- a/src/Protocols/Avalon/MemMap.hs +++ b/src/Protocols/Avalon/MemMap.hs @@ -102,6 +102,7 @@ import Clash.Prelude hiding (take, concat, length) import qualified Clash.Prelude as C -- me +import Protocols.Idle import Protocols.Internal import qualified Protocols.DfConv as DfConv @@ -1261,3 +1262,13 @@ instance -- -- Tests can still be made for Avalon MM circuits, using 'DfConv.dfConvTestBench'. -- See 'Tests.Protocols.AvalonMemMap' for examples. + +instance (KnownManagerConfig config) => + IdleCircuit (AvalonMmManager dom config) where + idleFwd _ = pure mmManagerOutNoData + idleBwd _ = pure $ boolToMmManagerAck False + +instance (KnownSubordinateConfig config) => + IdleCircuit (AvalonMmSubordinate dom fixedWaitTime config) where + idleFwd _ = pure mmSubordinateInNoData + idleBwd _ = pure $ boolToMmSubordinateAck False diff --git a/src/Protocols/Avalon/Stream.hs b/src/Protocols/Avalon/Stream.hs index bbad91dc..b3e52a9d 100644 --- a/src/Protocols/Avalon/Stream.hs +++ b/src/Protocols/Avalon/Stream.hs @@ -26,10 +26,11 @@ import Clash.Prelude hiding (take, concat, length) import qualified Clash.Prelude as C -- me +import Protocols.Hedgehog.Internal +import Protocols.Idle import Protocols.Internal import qualified Protocols.Df as Df import qualified Protocols.DfConv as DfConv -import Protocols.Hedgehog.Internal instance Hashable (C.Unsigned n) @@ -231,3 +232,8 @@ instance expectN Proxy options nExpected sampled = expectN (Proxy @(Df.Df dom _)) options nExpected $ Df.maybeToData <$> sampled + +instance IdleCircuit (AvalonStream dom conf dataType) where + idleFwd _ = pure Nothing + idleBwd _ = pure AvalonStreamS2M { _ready = False } + diff --git a/src/Protocols/Axi4/ReadAddress.hs b/src/Protocols/Axi4/ReadAddress.hs index e17d7d1c..c17ecff9 100644 --- a/src/Protocols/Axi4/ReadAddress.hs +++ b/src/Protocols/Axi4/ReadAddress.hs @@ -364,3 +364,7 @@ axi4ReadAddrMsgFromReadAddrInfo Axi4ReadAddressInfo{..} , _arqos = _ariqos , _aruser = _ariuser } + +instance IdleCircuit (Axi4ReadAddress dom conf userType) where + idleFwd _ = pure M2S_NoReadAddress + idleBwd _ = pure S2M_ReadAddress { _arready = False } diff --git a/src/Protocols/Axi4/ReadData.hs b/src/Protocols/Axi4/ReadData.hs index f52cce0a..4b582f3d 100644 --- a/src/Protocols/Axi4/ReadData.hs +++ b/src/Protocols/Axi4/ReadData.hs @@ -34,6 +34,7 @@ import qualified Clash.Prelude as C -- me import Protocols.Axi4.Common +import Protocols.Idle import Protocols.Internal -- | Configuration options for 'Axi4ReadData'. @@ -121,3 +122,7 @@ deriving instance , C.NFDataX dataType ) => C.NFDataX (S2M_ReadData conf userType dataType) + +instance IdleCircuit (Axi4ReadData dom conf userType dataType) where + idleFwd _ = C.pure S2M_NoReadData + idleBwd _ = C.pure $ M2S_ReadData False diff --git a/src/Protocols/Axi4/Stream.hs b/src/Protocols/Axi4/Stream.hs index e399f53e..d4ff05e9 100644 --- a/src/Protocols/Axi4/Stream.hs +++ b/src/Protocols/Axi4/Stream.hs @@ -24,10 +24,10 @@ import Clash.Prelude hiding (take, concat, length) import qualified Clash.Prelude as C -- me +import Protocols.Hedgehog.Internal import Protocols.Internal import qualified Protocols.Df as Df import qualified Protocols.DfConv as DfConv -import Protocols.Hedgehog.Internal instance (KnownNat n) => Hashable (Unsigned n) @@ -173,3 +173,7 @@ instance expectN Proxy options nExpected sampled = expectN (Proxy @(Df.Df dom _)) options nExpected $ Df.maybeToData <$> sampled + +instance IdleCircuit (Axi4Stream dom conf userType) where + idleFwd Proxy = C.pure Nothing + idleBwd Proxy = C.pure $ Axi4StreamS2M False diff --git a/src/Protocols/Axi4/WriteAddress.hs b/src/Protocols/Axi4/WriteAddress.hs index f6bbf8ac..afc66827 100644 --- a/src/Protocols/Axi4/WriteAddress.hs +++ b/src/Protocols/Axi4/WriteAddress.hs @@ -357,3 +357,7 @@ axi4WriteAddrMsgFromWriteAddrInfo _awlen _awburst Axi4WriteAddressInfo{..} , _awuser = _awiuser , _awlen, _awburst } + +instance IdleCircuit (Axi4WriteAddress dom conf userType) where + idleFwd _ = C.pure M2S_NoWriteAddress + idleBwd _ = C.pure $ S2M_WriteAddress False diff --git a/src/Protocols/Axi4/WriteData.hs b/src/Protocols/Axi4/WriteData.hs index 4803759b..07f285bf 100644 --- a/src/Protocols/Axi4/WriteData.hs +++ b/src/Protocols/Axi4/WriteData.hs @@ -34,6 +34,7 @@ import qualified Clash.Prelude as C -- me import Protocols.Axi4.Common +import Protocols.Idle import Protocols.Internal -- | Configuration options for 'Axi4WriteData'. @@ -113,3 +114,7 @@ deriving instance , C.NFDataX userType ) => C.NFDataX (M2S_WriteData conf userType) + +instance IdleCircuit (Axi4WriteData dom conf userType) where + idleFwd _ = C.pure M2S_NoWriteData + idleBwd _ = C.pure S2M_WriteData{ _wready = False } diff --git a/src/Protocols/Axi4/WriteResponse.hs b/src/Protocols/Axi4/WriteResponse.hs index d644b788..e65cc2ae 100644 --- a/src/Protocols/Axi4/WriteResponse.hs +++ b/src/Protocols/Axi4/WriteResponse.hs @@ -32,6 +32,7 @@ import qualified Clash.Prelude as C -- me import Protocols.Axi4.Common +import Protocols.Idle import Protocols.Internal -- | Configuration options for 'Axi4WriteResponse'. @@ -109,3 +110,7 @@ deriving instance , C.NFDataX userType ) => C.NFDataX (S2M_WriteResponse conf userType) + +instance IdleCircuit (Axi4WriteResponse dom conf userType) where + idleFwd _ = pure S2M_NoWriteResponse + idleBwd _ = pure $ M2S_WriteResponse False diff --git a/src/Protocols/Df.hs b/src/Protocols/Df.hs index 03351114..82fc0088 100644 --- a/src/Protocols/Df.hs +++ b/src/Protocols/Df.hs @@ -14,6 +14,7 @@ carries data, no metadata. For documentation see: {-# LANGUAGE NamedFieldPuns #-} {-# OPTIONS_GHC -fno-warn-orphans #-} +{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} -- TODO: Fix warnings introduced by GHC 9.2 w.r.t. incomplete lazy pattern matches {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} @@ -98,6 +99,8 @@ import qualified Clash.Explicit.Prelude as CE -- me import Protocols.Internal +{-# ANN module "HLint: ignore Use const" #-} + -- $setup -- >>> import Protocols -- >>> import Clash.Prelude (Vec(..)) @@ -148,6 +151,10 @@ instance Monad Data where NoData >>= _f = NoData Data a >>= f = f a +instance IdleCircuit (Df dom a) where + idleFwd _ = C.pure NoData + idleBwd _ = C.pure (Ack False) + -- | Convert 'Data' to 'Maybe'. Produces 'Just' on 'Data', 'Nothing' on 'NoData'. dataToMaybe :: Data a -> Maybe a dataToMaybe NoData = Nothing diff --git a/src/Protocols/Idle.hs b/src/Protocols/Idle.hs new file mode 100644 index 00000000..79e19372 --- /dev/null +++ b/src/Protocols/Idle.hs @@ -0,0 +1,30 @@ +{-# OPTIONS_GHC "-Wno-orphans" #-} + +{-| +Functionalities to easily create idle circuits for protocols. +-} +module Protocols.Idle + ( IdleCircuit(..) + , idleSource + , idleSink + ) where + +import Data.Proxy +import Protocols.Cpp (maxTupleSize) +import Protocols.Internal +import Protocols.Internal.TH (idleCircuitTupleInstances) + +instance (IdleCircuit a, IdleCircuit b) => IdleCircuit (a, b) where + idleFwd _ = (idleFwd $ Proxy @a, idleFwd $ Proxy @b) + idleBwd _ = (idleBwd $ Proxy @a, idleBwd $ Proxy @b) + +-- Derive instances for tuples up to maxTupleSize +idleCircuitTupleInstances 3 maxTupleSize + +-- | Idle state of a source, this circuit does not produce any data. +idleSource :: forall p. (IdleCircuit p) => Circuit () p +idleSource = Circuit $ const ((), idleFwd $ Proxy @p) + +-- | Idle state of a sink, this circuit does not consume any data. +idleSink :: forall p. (IdleCircuit p) => Circuit p () +idleSink = Circuit $ const (idleBwd $ Proxy @p, ()) diff --git a/src/Protocols/Internal.hs b/src/Protocols/Internal.hs index d2127b3c..15505ae8 100644 --- a/src/Protocols/Internal.hs +++ b/src/Protocols/Internal.hs @@ -9,14 +9,15 @@ Internal module to prevent hs-boot files (breaks Haddock) {-# LANGUAGE TypeFamilyDependencies #-} {-# LANGUAGE UndecidableInstances #-} -#if !MIN_VERSION_clash_prelude(1, 8, 2) -{-# OPTIONS_GHC -fno-warn-orphans #-} -- NFDataX and ShowX for Identity and Proxy -#endif +{-# OPTIONS_GHC -fno-warn-orphans #-} -- TODO: Hide internal documentation -- {-# OPTIONS_HADDOCK hide #-} -module Protocols.Internal where +module Protocols.Internal + ( module Protocols.Internal + , module Protocols.Internal.Classes + ) where import Control.DeepSeq (NFData) import Data.Hashable (Hashable) @@ -29,6 +30,7 @@ import Clash.Prelude (Signal, type (+), type (*)) import qualified Clash.Prelude as C import qualified Clash.Explicit.Prelude as CE +import Protocols.Internal.Classes import Protocols.Internal.TH (protocolTupleInstances) import Protocols.Cpp (maxTupleSize) @@ -44,119 +46,6 @@ import GHC.Generics (Generic) >>> import Protocols -} --- | A /Circuit/, in its most general form, corresponds to a component with two --- pairs of an input and output. As a diagram: --- --- @ --- Circuit a b --- --- +-----------+ --- Fwd a | | Fwd b --- +------->+ +--------> --- | | --- | | --- Bwd a | | Bwd b --- <--------+ +<-------+ --- | | --- +-----------+ --- @ --- --- The first pair, @(Fwd a, Bwd a)@ can be thought of the data sent to and from --- the component on the left hand side of this circuit. For this pair, @Fwd a@ --- is the data sent from the circuit on the left hand side (not pictured), while --- @Bwd a@ is the data sent to the left hand side from the current circuit. --- --- Similarly, the second pair, @(Fwd b, Bwd)@, can be thought of as the data --- sent to and from the right hand side of this circuit. In this case, @Fwd b@ --- is the data sent from the current circuit to the one on the right hand side, --- while @Bwd b@ is the data received from the right hand side. --- --- In Haskell terms, we would say this is simply a function taking two inputs, --- @Fwd a@ and @Bwd b@, yielding a pair of outputs @Fwd b@ and @Bwd a@. This is --- in fact exactly its definition: --- --- @ --- newtype Circuit a b = --- Circuit ( (Fwd a, Bwd b) -> (Bwd a, Fwd b) ) --- @ --- --- Note that the type parameters /a/ and /b/ don't directly correspond to the --- types of the inputs and outputs of this function. Instead, the type families --- @Fwd@ and @Bwd@ decide this. The type parameters can be thought of as --- deciders for what /protocol/ the left hand side and right hand side must --- speak. --- --- Let's make it a bit more concrete by building such a protocol. For this --- example, we'd like to build a protocol that sends data to a circuit, while --- allowing the circuit to signal whether it processed the sent data or not. Similarly, --- we'd like the sender to be able to indicate that it doesn't have any data to --- send. These kind of protocols fall under the umbrella of "dataflow" protocols, --- so lets call it /DataFlowSimple/ or /Df/ for short: --- --- @ --- data Df (dom :: Domain) (a :: Type) --- @ --- --- We're only going to use it on the type level, so we won't need any --- constructors for this datatype. The first type parameter indicates the --- synthesis domain the protocol will use. This is the same /dom/ as used in --- /Signal dom a/. The second type indicates what data the protocol needs to --- send. Again, this is similar to the /a/ in /Signal dom a/. --- --- As said previously, we'd like the sender to either send /no data/ or --- /some data/. We can capture this in a data type very similar to /Maybe/: --- --- @ --- data Data a = NoData | Data a --- @ --- --- On the way back, we'd like to either acknowledge or not acknowledge sent --- data. Similar to /Bool/ we define: --- --- @ --- newtype Ack = Ack Bool --- @ --- --- With these three definitions we're ready to make an instance for /Fwd/ and --- /Bwd/: --- --- @ --- instance Protocol (Df dom a) where --- type Fwd (Df dom a) = Signal dom (Data a) --- type Bwd (Df dom a) = Signal dom Ack --- @ --- --- Having defined all this, we can take a look at /Circuit/ once more: now --- instantiated with our types. The following: --- --- @ --- f :: Circuit (Df dom a) (Df dom b) --- @ --- --- ..now corresponds to the following protocol: --- --- @ --- +-----------+ --- Signal dom (Data a) | | Signal dom (Data b) --- +------------------------>+ +-------------------------> --- | | --- | | --- Signal dom Ack | | Signal dom Ack --- <-------------------------+ +<------------------------+ --- | | --- +-----------+ --- @ --- --- There's a number of advantages over manually writing out these function --- types: --- --- 1. It reduces syntactical noise in type signatures --- --- 2. It eliminates the need for manually routing acknowledgement lines --- -newtype Circuit a b = - Circuit ( (Fwd a, Bwd b) -> (Bwd a, Fwd b) ) - -- | Protocol-agnostic acknowledgement newtype Ack = Ack Bool deriving (Generic, C.NFDataX, Show, C.Bundle) @@ -174,16 +63,6 @@ instance Default Ack where data CSignal (dom :: CE.Domain) (a :: Type) type role CSignal nominal representational --- | A protocol describes the in- and outputs of one side of a 'Circuit'. -class Protocol a where - -- | Sender to receiver type family. See 'Circuit' for an explanation on the - -- existence of 'Fwd'. - type Fwd (a :: Type) - - -- | Receiver to sender type family. See 'Circuit' for an explanation on the - -- existence of 'Bwd'. - type Bwd (a :: Type) - instance Protocol () where type Fwd () = () type Bwd () = () @@ -198,7 +77,7 @@ instance Protocol (a, b) where -- Generate n-tuple instances, where n > 2 -protocolTupleInstances maxTupleSize +protocolTupleInstances 3 maxTupleSize instance C.KnownNat n => Protocol (C.Vec n a) where type Fwd (C.Vec n a) = C.Vec n (Fwd a) diff --git a/src/Protocols/Internal/Classes.hs b/src/Protocols/Internal/Classes.hs new file mode 100644 index 00000000..9d18ec70 --- /dev/null +++ b/src/Protocols/Internal/Classes.hs @@ -0,0 +1,142 @@ +{-| +These class definitions are needed to be able to write Template Haskell quotes +for instances. They are defined separately to avoid import loops. + +This module is not exported; the classes and their (orphan) instances are +exported elsewhere. +-} + +module Protocols.Internal.Classes where + +import Data.Kind (Type) +import Data.Proxy + +-- | A protocol describes the in- and outputs of one side of a 'Circuit'. +class Protocol a where + -- | Sender to receiver type family. See 'Circuit' for an explanation on the + -- existence of 'Fwd'. + type Fwd (a :: Type) + + -- | Receiver to sender type family. See 'Circuit' for an explanation on the + -- existence of 'Bwd'. + type Bwd (a :: Type) + +-- | A /Circuit/, in its most general form, corresponds to a component with two +-- pairs of an input and output. As a diagram: +-- +-- @ +-- Circuit a b +-- +-- +-----------+ +-- Fwd a | | Fwd b +-- +------->+ +--------> +-- | | +-- | | +-- Bwd a | | Bwd b +-- <--------+ +<-------+ +-- | | +-- +-----------+ +-- @ +-- +-- The first pair, @(Fwd a, Bwd a)@ can be thought of the data sent to and from +-- the component on the left hand side of this circuit. For this pair, @Fwd a@ +-- is the data sent from the circuit on the left hand side (not pictured), while +-- @Bwd a@ is the data sent to the left hand side from the current circuit. +-- +-- Similarly, the second pair, @(Fwd b, Bwd)@, can be thought of as the data +-- sent to and from the right hand side of this circuit. In this case, @Fwd b@ +-- is the data sent from the current circuit to the one on the right hand side, +-- while @Bwd b@ is the data received from the right hand side. +-- +-- In Haskell terms, we would say this is simply a function taking two inputs, +-- @Fwd a@ and @Bwd b@, yielding a pair of outputs @Fwd b@ and @Bwd a@. This is +-- in fact exactly its definition: +-- +-- @ +-- newtype Circuit a b = +-- Circuit ( (Fwd a, Bwd b) -> (Bwd a, Fwd b) ) +-- @ +-- +-- Note that the type parameters /a/ and /b/ don't directly correspond to the +-- types of the inputs and outputs of this function. Instead, the type families +-- @Fwd@ and @Bwd@ decide this. The type parameters can be thought of as +-- deciders for what /protocol/ the left hand side and right hand side must +-- speak. +-- +-- Let's make it a bit more concrete by building such a protocol. For this +-- example, we'd like to build a protocol that sends data to a circuit, while +-- allowing the circuit to signal whether it processed the sent data or not. Similarly, +-- we'd like the sender to be able to indicate that it doesn't have any data to +-- send. These kind of protocols fall under the umbrella of "dataflow" protocols, +-- so lets call it /DataFlowSimple/ or /Df/ for short: +-- +-- @ +-- data Df (dom :: Domain) (a :: Type) +-- @ +-- +-- We're only going to use it on the type level, so we won't need any +-- constructors for this datatype. The first type parameter indicates the +-- synthesis domain the protocol will use. This is the same /dom/ as used in +-- /Signal dom a/. The second type indicates what data the protocol needs to +-- send. Again, this is similar to the /a/ in /Signal dom a/. +-- +-- As said previously, we'd like the sender to either send /no data/ or +-- /some data/. We can capture this in a data type very similar to /Maybe/: +-- +-- @ +-- data Data a = NoData | Data a +-- @ +-- +-- On the way back, we'd like to either acknowledge or not acknowledge sent +-- data. Similar to /Bool/ we define: +-- +-- @ +-- newtype Ack = Ack Bool +-- @ +-- +-- With these three definitions we're ready to make an instance for /Fwd/ and +-- /Bwd/: +-- +-- @ +-- instance Protocol (Df dom a) where +-- type Fwd (Df dom a) = Signal dom (Data a) +-- type Bwd (Df dom a) = Signal dom Ack +-- @ +-- +-- Having defined all this, we can take a look at /Circuit/ once more: now +-- instantiated with our types. The following: +-- +-- @ +-- f :: Circuit (Df dom a) (Df dom b) +-- @ +-- +-- ..now corresponds to the following protocol: +-- +-- @ +-- +-----------+ +-- Signal dom (Data a) | | Signal dom (Data b) +-- +------------------------>+ +-------------------------> +-- | | +-- | | +-- Signal dom Ack | | Signal dom Ack +-- <-------------------------+ +<------------------------+ +-- | | +-- +-----------+ +-- @ +-- +-- There's a number of advantages over manually writing out these function +-- types: +-- +-- 1. It reduces syntactical noise in type signatures +-- +-- 2. It eliminates the need for manually routing acknowledgement lines +-- +newtype Circuit a b = + Circuit ( (Fwd a, Bwd b) -> (Bwd a, Fwd b) ) + +-- | Idle state of a Circuit. Aims to provide no data for both the forward and +-- backward direction. Transactions are not acknowledged. +class (Protocol p) => IdleCircuit p where + idleFwd :: Proxy p -> Fwd (p :: Type) + idleBwd :: Proxy p -> Bwd (p :: Type) + diff --git a/src/Protocols/Internal/TH.hs b/src/Protocols/Internal/TH.hs index a17e18be..de9330cd 100644 --- a/src/Protocols/Internal/TH.hs +++ b/src/Protocols/Internal/TH.hs @@ -1,14 +1,17 @@ {-# OPTIONS_HADDOCK hide #-} -module Protocols.Internal.TH (protocolTupleInstances) where +module Protocols.Internal.TH where +import Control.Monad.Extra (concatMapM) import Language.Haskell.TH +import Protocols.Internal.Classes + appTs :: Q Type -> [Q Type] -> Q Type appTs = foldl appT -protocolTupleInstances :: Int -> Q [Dec] -protocolTupleInstances n = mapM protocolTupleInstance [3..n] +protocolTupleInstances :: Int -> Int -> Q [Dec] +protocolTupleInstances n m = mapM protocolTupleInstance [n..m] protocolTupleInstance :: Int -> Q Dec protocolTupleInstance n = @@ -35,3 +38,26 @@ protocolTupleInstance n = lhs = conT con `appT` tup rhs = tupleT n `appTs` map (conT con `appT`) tyVars +-- | Template haskell function to generate IdleCircuit instances for the tuples +-- n through m inclusive. To see a 2-tuple version of the pattern we generate, +-- see @Protocols.IdleCircuit@. +idleCircuitTupleInstances :: Int -> Int -> DecsQ +idleCircuitTupleInstances n m = concatMapM idleCircuitTupleInstance [n..m] + +-- | Template Haskell function to generate an IdleCircuit instance for an +-- n-tuple. +idleCircuitTupleInstance :: Int -> DecsQ +idleCircuitTupleInstance n = + [d| instance $instCtx => IdleCircuit $instTy where + idleFwd _ = $fwdExpr + idleBwd _ = $bwdExpr + |] + where + circTys = map (\i -> varT $ mkName $ "c" <> show i) [1..n] + instCtx = foldl appT (tupleT n) $ map (\ty -> [t| IdleCircuit $ty |]) circTys + instTy = foldl appT (tupleT n) circTys + fwdExpr = tupE $ map mkFwdExpr circTys + mkFwdExpr ty = [e| idleFwd $ Proxy @($ty) |] + bwdExpr = tupE $ map mkBwdExpr circTys + mkBwdExpr ty = [e| idleBwd $ Proxy @($ty) |] + diff --git a/src/Protocols/Wishbone.hs b/src/Protocols/Wishbone.hs index d466eb17..6c5edbfa 100644 --- a/src/Protocols/Wishbone.hs +++ b/src/Protocols/Wishbone.hs @@ -9,11 +9,14 @@ module Protocols.Wishbone where import Clash.Prelude (DivRU, Nat, Type, (:::)) -import qualified Clash.Prelude as C +import Prelude hiding (head, not, (&&)) + import Clash.Signal.Internal (Signal (..)) import Control.DeepSeq (NFData) import Protocols -import Prelude hiding (head, not, (&&)) +import Protocols.Internal.Classes + +import qualified Clash.Prelude as C -- | Data communicated from a Wishbone Master to a Wishbone Slave data WishboneM2S addressWidth selWidth dat = WishboneM2S @@ -206,6 +209,11 @@ instance Protocol (Wishbone dom mode addressWidth dat) where type Bwd (Wishbone dom mode addressWidth dat) = Signal dom (WishboneS2M dat) +instance (C.KnownNat aw, C.KnownNat (C.BitSize dat), C.NFDataX dat) => + IdleCircuit (Wishbone dom mode aw dat) where + idleFwd _ = C.pure emptyWishboneM2S + idleBwd _ = C.pure emptyWishboneS2M + -- | Construct "default" Wishbone M2S signals emptyWishboneM2S :: (C.KnownNat addressWidth, C.KnownNat (C.BitSize dat), C.NFDataX dat) =>