diff --git a/clash-prelude/clash-prelude.cabal b/clash-prelude/clash-prelude.cabal index 05c60917c1..2cc89b6248 100644 --- a/clash-prelude/clash-prelude.cabal +++ b/clash-prelude/clash-prelude.cabal @@ -126,6 +126,7 @@ Library Clash.Annotations.BitRepresentation.Internal Clash.Annotations.BitRepresentation.Util Clash.Annotations.SynthesisAttributes + Clash.Annotations.TH Clash.Class.BitPack Clash.Class.Exp @@ -269,6 +270,7 @@ Library hashable >= 1.2.1.0 && < 1.4, half >= 0.2.2.3 && < 1.0, lens >= 4.9 && < 4.19, + recursion-schemes >= 5.1 && < 5.2, QuickCheck >= 2.7 && < 2.14, reflection >= 2 && < 2.2, singletons >= 1.0 && < 3.0, @@ -317,7 +319,8 @@ test-suite unittests base, hint >= 0.7 && < 0.10, tasty >= 1.2 && < 1.3, - tasty-hunit + tasty-hunit, + template-haskell Other-Modules: Clash.Tests.BitPack Clash.Tests.BitVector @@ -325,6 +328,7 @@ test-suite unittests Clash.Tests.DerivingDataReprTypes Clash.Tests.Signal Clash.Tests.NFDataX + Clash.Tests.TopEntityGeneration benchmark benchmark-clash-prelude diff --git a/clash-prelude/src/Clash/Annotations/TH.hs b/clash-prelude/src/Clash/Annotations/TH.hs new file mode 100644 index 0000000000..a935e102f7 --- /dev/null +++ b/clash-prelude/src/Clash/Annotations/TH.hs @@ -0,0 +1,404 @@ +{-| + +This module can automatically generate TopEntity definitions from 'Clash.NamedTypes' +annotations. Type/data families will resolve a single step (non-recusive). + +See "Clash.Tests.TopEntityGeneration" for more examples. + +@ +import Clash.Annotations.TopEntityGen + +data Named + = Named + { name1 :: "named1" ::: BitVector 3 + , name2 :: "named2" ::: BitVector 5 + } + +topEntity :: "tup1" ::: Signal System (Int, Bool) + -> "tup2" ::: (Signal System Int, Signal System Bool) + -> "tup3" ::: Signal System ("int":::Int, "bool":::Bool) + -> "tup4" ::: ("int":::Signal System Int, "bool":::Signal System Bool) + -> "custom" ::: Signal System Named + -> "outTup" ::: Signal System ("outint":::Int, "outbool":::Bool) +topEntity = undefined +makeTopEntity 'topEntity +-- ===> +-- {-# ANN topEntity Synthesize "topEntity3" +-- [ PortName "tup1" +-- , PortName "tup2" +-- , PortProduct "tup3" [PortName "int",PortName "bool"] +-- , PortProduct "tup4" [PortName "int",PortName "bool"] +-- , PortProduct "custom" [PortName "named1",PortName "named2"] +-- ] +-- (PortProduct "outTup" [PortName "outint",PortName "outbool"]) +-- #-} +@ + +-} + +{-# OPTIONS_GHC -fno-warn-orphans #-} +-- Required to 'makeBaseFunctor' of 'Language.Haskell.TH.Syntax.Type' + +{-# LANGUAGE CPP #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveFoldable #-} +{-# LANGUAGE DeriveFunctor #-} +{-# LANGUAGE DeriveLift #-} +{-# LANGUAGE DeriveTraversable #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE ViewPatterns #-} + +module Clash.Annotations.TH + ( -- * To create a Synthesize annotation pragma + makeTopEntity + , makeTopEntityWithName + , makeTopEntityWithName' + -- * To create a TopEntity value + , buildTopEntity + , buildTopEntity' + ) +where + +import Data.Foldable ( fold + , find + ) +import qualified Data.Set as Set +#if !(MIN_VERSION_base(4,11,0)) +import Data.Semigroup as Semigroup +#endif +import Language.Haskell.TH + +import Data.Functor.Foldable ( cata, para, embed ) +import Data.Functor.Foldable.TH +import Control.Lens ( view + , _3 + ) +import Language.Haskell.TH.Instances ( ) + +import Clash.Annotations.TopEntity ( PortName(..) + , TopEntity(..) + ) +import Clash.NamedTypes + +$(makeBaseFunctor ''Type) + +-- | A datatype to track failing naming in a subtree. +data Naming a = Complete [a] | HasFail String + +instance Semigroup (Naming a) where + Complete a <> Complete b = Complete $ a ++ b + HasFail e1 <> HasFail e2 = HasFail $ e1 ++ e2 + _ <> HasFail e = HasFail e + HasFail e <> _ = HasFail e + +instance Monoid (Naming a) where + mempty = Complete [] +#if !(MIN_VERSION_base(4,11,0)) + mappend = (Semigroup.<>) +#endif + +-- | A template haskell helper function. Get the 'Type's from a 'Con'. +getTypes :: Con -> [Type] +getTypes (NormalC _ xs) = map snd xs +getTypes (RecC _ xs) = map (view _3) xs +getTypes (InfixC x _ y) = [snd x, snd y] +getTypes (ForallC _ _ c) = getTypes c +getTypes (GadtC _ xs _) = map snd xs +getTypes (RecGadtC _ xs _) = map (view _3) xs + +-- | A template haskell helper function. Get the 'Name' of a 'Con'. These names +-- are only used in type/data family resolution which is best effort, +-- so for Gadts we just take the first name. +getName :: Con -> Name +getName (NormalC n _) = n +getName (RecC n _) = n +getName (InfixC _ n _) = n +getName (ForallC _ _ c) = getName c +getName (GadtC (n : _) _ _) = n +getName (RecGadtC (n : _) _ _) = n +getName (GadtC [] _ _) = error "Encountered GADT with no constructors?" +getName (RecGadtC [] _ _) = error "Encountered GADT with no constructors?" + +-- | Flag constructors with partially named fields as failing. +namesFromConstructor + :: Set.Set Name + -> Type + -> Info + -> Con + -> Q (Naming PortName) +namesFromConstructor seen split info (getTypes -> xs) = + (mconcat <$> mapM (expandFamiliesAndGatherNames seen split) xs) + >>= \case + Complete names | length names > 0 && length names /= length xs -> + return $ HasFail $ "Partially named constructor arguments in " + ++ pprint info ++ "\n" + x -> return x + +-- | Flag sum types as failing if they have any constructors with names. +possibleNamedSum + :: Set.Set Name + -> Type + -> Info + -> [Con] + -> Q (Naming PortName) +possibleNamedSum seen split info xs = + (fold <$> mapM (namesFromConstructor seen split info) xs) >>= \case + Complete [] -> return $ Complete [] + x -> return $ x <> HasFail ("Sum types not supported!" + ++ "\n at " ++ pprint info ++ "\n") + +-- | Build a list of 'PortName's from a template haskell 'Name' +nameToPorts + :: Set.Set Name + -- ^ Locally track seen names to handle recursive datatypes + -> Type + -- ^ Type to split at + -> Name + -- ^ Name to investigate + -> Q (Naming PortName) +nameToPorts seen split name = do + if Set.member name seen + then return $ Complete [] + else do + let seen' = Set.insert name seen + -- Recur on types by reifying the name. + -- This allows us to find splits defined in data types. + info <- reify name + + case info of + TyConI (getConstructors -> [x]) -> + namesFromConstructor seen' split info x + TyConI (getConstructors -> xs) -> + possibleNamedSum seen split info xs + DataConI _ (AppT (AppT ArrowT ty) _) _ -> + expandFamiliesAndGatherNames seen split ty + PrimTyConI _ _ _ -> return $ Complete [] + _ -> return $ HasFail $ "Unhandled " ++ pprint info ++ "\n" + where + getConstructors (DataD _ _ _ _ x _) = x + getConstructors (NewtypeD _ _ _ _ x _) = [x] + getConstructors (DataInstD _ _ _ _ x _) = x + getConstructors (NewtypeInstD _ _ _ _ x _) = [x] + getConstructors _ = [] + +-- | A helper function for recursively walking a 'Type' tree and +-- building a list of 'PortName's. +gatherNames + :: Set.Set Name + -- ^ Locally track seen names to handle recursive datatypes + -> Type + -- ^ Type to split at + -> TypeF (Type, Q (Naming PortName)) + -- ^ Case under scrutiny, paramorphism style + -> Q (Naming PortName) +gatherNames _ split (AppTF (AppT split' (LitT (StrTyLit name)), _) (_,c)) + -- Is there a ' ::: ' annotation? + | split' == split + -- We found our split. If: + -- - We only have no names from children: use split name as PortName + -- - We have children reporting names: use split name as name to PortProduct + = c >>= \case + Complete [] -> return $ Complete [PortName name] + Complete xs -> return $ Complete [PortProduct name xs] + HasFail err -> return $ HasFail err +gatherNames seen split (ConTF name) = nameToPorts seen split name +gatherNames _ _ f = do + -- Just collect names + f' <- mapM snd f + return $ fold f' + +-- | Helper algebra to expand first level (non-recursive) type/data family +-- instances in a best effort manner. Data family instances with sum type +-- constructors are ignored. +expandFamilies :: TypeF (Q Type) -> Q (Type) +expandFamilies (AppTF a b) = do + a' <- a + b' <- b + case unApp (AppT a' b') [] of + (ConT x : xs) -> do + info <- reify x + case info of + -- Closed type families have to be handled separately + FamilyI (ClosedTypeFamilyD (TypeFamilyHead _ bds _ _) eqs) _ -> + if length bds == length xs then + case find ((==) xs . tySynArgs) eqs of +#if MIN_VERSION_template_haskell(2,15,0) + Just (TySynEqn _ _ r) -> return r +#else + Just (TySynEqn _ r) -> return r +#endif + _ -> return (AppT a' b') + -- We didn't find a matching instance so give up. + else return (AppT a' b') + -- We don't yet have all the arguments. + -- Now for open type families and data families + _ -> + case familyArity info of + Just i | i == length xs -> do + inst <- reifyInstances x xs + case inst of +#if MIN_VERSION_template_haskell(2,15,0) + [TySynInstD (TySynEqn _ _ t)] -> return t +#else + [TySynInstD _ (TySynEqn _ t)] -> return t +#endif + [NewtypeInstD _ _ _ _ c _] -> return $ ConT (getName c) + [DataInstD _ _ _ _ cs _] -> do + case cs of + [c] -> return $ ConT (getName c) + _ -> return $ PromotedTupleT 0 + -- Ignore sum type in a data family by replacing with + -- empty tuple. We don't want to fail because this subtree + -- might not be relevant to naming. + z -> fail + $ "Encountered\n" + ++ pprint z + ++ "\nduring family application! Perhaps a missing instance?" + _ -> return (AppT a' b') + _ -> return (AppT a' b') + where + unApp (AppT l r) = unApp l . (r :) + unApp t = (t :) + +#if MIN_VERSION_template_haskell(2,15,0) + tySynArgs (TySynEqn _ args _) = tail (unApp args []) +#else + tySynArgs (TySynEqn args _) = args +#endif + + familyArity (FamilyI (OpenTypeFamilyD (TypeFamilyHead _ xs _ _)) _) = + Just (length xs) + familyArity (FamilyI (DataFamilyD _ xs _) _) = Just (length xs) + familyArity _ = Nothing +expandFamilies t = embed <$> sequence t + +-- | Runs 'expandFamilies' and then 'gatherNames' +expandFamiliesAndGatherNames + :: Set.Set Name + -- ^ Locally track seen names to handle recursive datatypes + -> Type + -- ^ Type to split at + -> Type + -- ^ Type to investigate + -> Q (Naming PortName) +expandFamiliesAndGatherNames seen split x = + cata expandFamilies x >>= para (gatherNames seen split) + +-- | Build a possible failing 'PortName' tree and unwrap the 'Naming' result. +buildPorts + :: Type + -- ^ Type to split at + -> Type + -- ^ Type to investigate + -> Q [PortName] +buildPorts split x = do + expandFamiliesAndGatherNames Set.empty split x >>= \case + Complete xs -> return xs + HasFail err -> fail err + +-- | Matches a type `a -> b` +pattern ArrowTy :: Type -> Type -> Type +pattern ArrowTy a b = AppT (AppT ArrowT a) b + +-- | Get the result 'PortName' from a function type +toReturnName :: Type -> Type -> Q PortName +toReturnName split (ArrowTy _ b) = toReturnName split b +toReturnName split b = + buildPorts split b + >>= \case + [] -> fail "No return name specified!" + [x] -> return x + xs -> return $ PortProduct "" xs + +-- | Get the argument 'PortName's from a function type +toArgNames :: Type -> Type -> Q [PortName] +toArgNames split ty = go (0::Int) ty [] + where + go i (ArrowTy x y) acc = do + v <- go (i+1) y acc + name <- buildPorts split x + -- Fail if we didn't get a single PortName + case name of + [a] -> return (a:v) + f -> fail $ "Failed to get a single name for argument " ++ show i + ++ "\n a.k.a. " ++ pprint x + ++ "\n got name " ++ show f + go _ _ acc = return acc + +-- | Remove constraints from a type. Fail if there are free type variables. +removeConstraints :: Type -> Q Type +removeConstraints (ForallT [] _ x) = removeConstraints x +removeConstraints (ForallT xs _ x) = + fail $ "Found free type variables on TopEntity!\n" + ++ pprint xs + ++ "\nattached to\n" + ++ pprint x +removeConstraints x = return x + +-- | Return a typed expression for a 'TopEntity' of a given @('Name', 'Type')@. +buildTopEntity' :: Maybe String -> (Name, Type) -> TExpQ TopEntity +buildTopEntity' topName (name, ty) = do + -- get a Name for this type operator so we can check it + -- in the ArrowTy case + split <- [t| (:::) |] + + ty' <- removeConstraints ty + + ins <- toArgNames split ty' + out <- toReturnName split ty' + + let outName = case topName of + Just name' -> name' -- user specified name + Nothing -> nameBase name -- auto-generated from haskell name + + [|| Synthesize + { t_name = outName + , t_inputs = ins + , t_output = out + } ||] + +-- | Return an untyped expression for a 'TopEntity' of a given 'Name'. +buildTopEntity :: Maybe String -> Name -> ExpQ +buildTopEntity topName name = + fmap unType $ getNameBinding name >>= buildTopEntity' topName + +-- | Turn the 'Name' of a value to a @('Name', 'Type')@ +getNameBinding :: Name -> Q (Name, Type) +getNameBinding n = reify n >>= \case + VarI name ty _ -> return (name, ty) + _ -> fail "getNameBinding: Invalid Name, must be a top-level binding!" + +-- | Wrap a 'TopEntity' expression in an annotation pragma +makeTopEntityWithName' :: Name -> Maybe String -> DecQ +makeTopEntityWithName' n topName = do + (name,ty) <- getNameBinding n + topEntity <- buildTopEntity' topName (name,ty) + let prag t = PragmaD (AnnP (valueAnnotation name) t) + return $ prag $ unType topEntity + +-- | Automatically create a @'TopEntity'@ for a given @'Name'@, using the given +-- @'String'@ to specify the name of the generated RTL entity. +-- +-- The function arguments and return values of the function specified by the +-- given @'Name'@ must be annotated with @'(:::)'@. This annotation provides the +-- given name of the port. +makeTopEntityWithName :: Name -> String -> DecsQ +makeTopEntityWithName nam top = pure <$> makeTopEntityWithName' nam (Just top) + +-- | Automatically create a @'TopEntity'@ for a given @'Name'@. The name of the +-- generated RTL entity will be the name of the function that has been +-- specified; e.g. @'makeTopEntity' 'foobar@ will generate a @foobar@ module. +-- +-- The function arguments and return values of the function specified by the +-- given @'Name'@ must be annotated with @'(:::)'@. This annotation provides the +-- given name of the port. +makeTopEntity :: Name -> DecsQ +makeTopEntity nam = pure <$> makeTopEntityWithName' nam Nothing diff --git a/clash-prelude/src/Clash/Annotations/TopEntity.hs b/clash-prelude/src/Clash/Annotations/TopEntity.hs index 9944f8f124..5ed6722b5a 100644 --- a/clash-prelude/src/Clash/Annotations/TopEntity.hs +++ b/clash-prelude/src/Clash/Annotations/TopEntity.hs @@ -253,7 +253,7 @@ data TopEntity -- g = ... -- @ | TestBench TH.Name - deriving (Data,Show,Generic) + deriving (Eq,Data,Show,Generic) instance Lift TopEntity where lift (Synthesize name inputs output) = @@ -355,7 +355,7 @@ data PortName -- 2. The prefix for any unnamed ports below the 'PortProduct' -- -- You can use an empty String ,\"\" , in case you want an auto-generated name. - deriving (Data,Show,Generic,Lift) + deriving (Eq,Data,Show,Generic,Lift) -- | Default 'Synthesize' annotation which has no specified names for the input -- and output ports. diff --git a/clash-prelude/tests/Clash/Tests/TopEntityGeneration.hs b/clash-prelude/tests/Clash/Tests/TopEntityGeneration.hs new file mode 100644 index 0000000000..ebb70d9a17 --- /dev/null +++ b/clash-prelude/tests/Clash/Tests/TopEntityGeneration.hs @@ -0,0 +1,227 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE GADTs #-} + +module Clash.Tests.TopEntityGeneration where + +import Language.Haskell.TH.Syntax (recover) + +import Test.Tasty +import Test.Tasty.HUnit + +import Clash.Prelude hiding (undefined) +import Clash.Annotations.TH + +data Unnamed = Unnamed Int +data Simple = Simple ("simple1" ::: Int) ("simple2" ::: Bool) +data Named + = Named + { name1 :: "named1" ::: BitVector 3 + , name2 :: "named2" ::: BitVector 5 + } +data Embedded + = Embedded + { name3 :: "embedded1" ::: Simple + , name4 :: "embedded2" ::: Bool + } +newtype Single = Single ("s" ::: Int) + +data Gadt x where + Gadt :: ("gadt" ::: Int) -> Gadt Int + +type family CF x y z where + CF Int Int Int = ("cfiii" ::: Single) + CF Bool Int Int = ("cfbii" ::: Single) +type family OF x y z +type instance OF Int Int Int = ("ofiii" ::: Single) +type instance OF Bool Int Int = ("ofbii" ::: Single) + +data family X x y z +data instance X Int Int Int = X1 ("xiii" ::: Int) +newtype instance X Bool Int Int = X2 ("xbii" ::: Int) + +data Impossible = L ("left" ::: Int) | R ("right" ::: Bool) + +data FailureTy1 = TwoF1 ("one" ::: Int) Int +data FailureTy2 = TwoF2 ("one" ::: Int) Simple +data SuccessTy = TwoS ("one" ::: Int) Single + +topEntity1 :: "in1" ::: Signal System Int + -> "in2" ::: Signal System Bool + -> "out" ::: Signal System Int +topEntity1 = undefined +makeTopEntity 'topEntity1 + +expectedTopEntity1 :: TopEntity +expectedTopEntity1 = + Synthesize "topEntity1" + [PortName "in1", PortName "in2"] + (PortName "out") + +topEntity2 :: "int" ::: Signal System Int + -> "tuple" ::: ( "tup1" ::: Signal System (BitVector 7) + , "tup2" ::: Signal System (BitVector 9)) + -> "simple" ::: Signal System Simple + -> "named" ::: Signal System Named + -> "embedded" ::: Signal System Embedded + -> "out" ::: Signal System Bool +topEntity2 = undefined +makeTopEntity 'topEntity2 + +expectedTopEntity2 :: TopEntity +expectedTopEntity2 = + Synthesize "topEntity2" + [ PortName "int" + , PortProduct "tuple" [PortName "tup1",PortName "tup2"] + , PortProduct "simple" [PortName "simple1",PortName "simple2"] + , PortProduct "named" [PortName "named1",PortName "named2"] + , PortProduct "embedded" + [ PortProduct "embedded1" + [ PortName "simple1" + , PortName "simple2"] + , PortName "embedded2"] + ] + (PortName "out") + +topEntity3 :: "tup1" ::: Signal System (Int, Bool) + -> "tup2" ::: (Signal System Int, Signal System Bool) + -> "tup3" ::: Signal System ("int":::Int, "bool":::Bool) + -> "tup4" ::: ("int":::Signal System Int, "bool":::Signal System Bool) + -> "custom" ::: Signal System Named + -> "outTup" ::: Signal System ("outint":::Int, "outbool":::Bool) +topEntity3 = undefined +makeTopEntity 'topEntity3 + +expectedTopEntity3 :: TopEntity +expectedTopEntity3 = + Synthesize "topEntity3" + [ PortName "tup1" + , PortName "tup2" + , PortProduct "tup3" [PortName "int",PortName "bool"] + , PortProduct "tup4" [PortName "int",PortName "bool"] + , PortProduct "custom" [PortName "named1",PortName "named2"] + ] + (PortProduct "outTup" [PortName "outint",PortName "outbool"]) + +topEntity4 :: Signal System (Gadt Int) + -> Signal System Single + -> Signal System (CF Int Int Int) + -> Signal System (CF Bool Int Int) + -> Signal System (OF Int Int Int) + -> Signal System (OF Bool Int Int) + -> Signal System (X Int Int Int) + -> Signal System (X Bool Int Int) + -> Signal System Single +topEntity4 = undefined +makeTopEntity 'topEntity4 + +expectedTopEntity4 :: TopEntity +expectedTopEntity4 = + Synthesize "topEntity4" + [ PortName "gadt" + , PortName "s" + , PortProduct "cfiii" [PortName "s"] + , PortProduct "cfbii" [PortName "s"] + , PortProduct "ofiii" [PortName "s"] + , PortProduct "ofbii" [PortName "s"] + , PortName "xiii" + , PortName "xbii" + ] + (PortName "s") + +topEntity5 :: "in1" ::: Signal System SuccessTy + -> "out" ::: Signal System Int +topEntity5 = undefined +makeTopEntity 'topEntity5 + +expectedTopEntity5 :: TopEntity +expectedTopEntity5 = + Synthesize "topEntity5" + [PortProduct "in1" [PortName "one", PortName "s"]] + (PortName "out") + +topEntity6 :: (HiddenClockResetEnable System) + => "in1" ::: Signal System SuccessTy + -> "out" ::: Signal System Int +topEntity6 = undefined +makeTopEntity 'topEntity6 + +expectedTopEntity6 :: TopEntity +expectedTopEntity6 = + Synthesize "topEntity6" + [PortProduct "in1" [PortName "one", PortName "s"]] + (PortName "out") + + +topEntityFailure1 + :: "int" ::: Signal System Int + -> "tuple" ::: ("tup1" ::: Signal System (BitVector 7), "tup2" ::: Signal System (BitVector 9)) + -> "simple" ::: Signal System Simple + -> "named" ::: Signal System Named + -> Signal System Embedded + -> "out" ::: Signal System Bool +topEntityFailure1 = undefined + +topEntityFailure2 + :: "int" ::: Signal System Int + -> "tuple" ::: ("tup1" ::: Signal System (BitVector 7), "tup2" ::: Signal System (BitVector 9)) + -> "simple" ::: Signal System Simple + -> "named" ::: Signal System Named + -> Signal System Int + -> "out" ::: Signal System Bool +topEntityFailure2 = undefined + +topEntityFailure3 + :: "int" ::: Signal System Impossible + -> "out" ::: Signal System Bool +topEntityFailure3 = undefined + +topEntityFailure4 + :: "int" ::: Signal System FailureTy1 + -> "out" ::: Signal System Bool +topEntityFailure4 = undefined + +topEntityFailure5 + :: "int" ::: Signal System FailureTy2 + -> "out" ::: Signal System Bool +topEntityFailure5 = undefined + +topEntityFailure6 + :: "int" ::: Signal System a + -> "out" ::: Signal System Bool +topEntityFailure6 = undefined + +tests :: TestTree +tests = + testGroup + "TopEntityGeneration" + [ testCase "topEntity1" $ + $(recover ([| () |]) (buildTopEntity Nothing 'topEntity1)) @?= expectedTopEntity1 + , testCase "topEntity2" $ + $(recover ([| () |]) (buildTopEntity Nothing 'topEntity2)) @?= expectedTopEntity2 + , testCase "topEntity3" $ + $(recover ([| () |]) (buildTopEntity Nothing 'topEntity3)) @?= expectedTopEntity3 + , testCase "topEntity4" $ + $(recover ([| () |]) (buildTopEntity Nothing 'topEntity4)) @?= expectedTopEntity4 + , testCase "topEntity5" $ + $(recover ([| () |]) (buildTopEntity Nothing 'topEntity5)) @?= expectedTopEntity5 + , testCase "topEntity6" $ + $(recover ([| () |]) (buildTopEntity Nothing 'topEntity6)) @?= expectedTopEntity6 + + , testCase "topEntityFailure1" $ + $(recover ([| () |]) (buildTopEntity Nothing 'topEntityFailure1)) @?= () + , testCase "topEntityFailure2" $ + $(recover ([| () |]) (buildTopEntity Nothing 'topEntityFailure2)) @?= () + , testCase "topEntityFailure3" $ + $(recover ([| () |]) (buildTopEntity Nothing 'topEntityFailure3)) @?= () + , testCase "topEntityFailure4" $ + $(recover ([| () |]) (buildTopEntity Nothing 'topEntityFailure4)) @?= () + , testCase "topEntityFailure5" $ + $(recover ([| () |]) (buildTopEntity Nothing 'topEntityFailure5)) @?= () + , testCase "topEntityFailure6" $ + $(recover ([| () |]) (buildTopEntity Nothing 'topEntityFailure6)) @?= () + ] + diff --git a/clash-prelude/tests/unittests.hs b/clash-prelude/tests/unittests.hs index 1631507a59..4b7d98a76b 100644 --- a/clash-prelude/tests/unittests.hs +++ b/clash-prelude/tests/unittests.hs @@ -7,6 +7,7 @@ import qualified Clash.Tests.BitVector import qualified Clash.Tests.DerivingDataRepr import qualified Clash.Tests.Signal import qualified Clash.Tests.NFDataX +import qualified Clash.Tests.TopEntityGeneration tests :: TestTree tests = testGroup "Unittests" @@ -15,6 +16,7 @@ tests = testGroup "Unittests" , Clash.Tests.DerivingDataRepr.tests , Clash.Tests.Signal.tests , Clash.Tests.NFDataX.tests + , Clash.Tests.TopEntityGeneration.tests ] main :: IO ()