X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=c%CE%BBash%2FCLasH%2FTranslator.hs;h=a9bb9fa4cfed041f1ba5b69d6b82fe4b75857689;hb=0223b461a043e42d3fc5442904b73ce0bd537472;hp=c1e853aad6ede4f39c5c3cdcfe788f5c0d6b4747;hpb=4495e7a13be4643f342c5b05dcb819b60dc1b108;p=matthijs%2Fmaster-project%2Fc%CE%BBash.git diff --git "a/c\316\273ash/CLasH/Translator.hs" "b/c\316\273ash/CLasH/Translator.hs" index c1e853a..a9bb9fa 100644 --- "a/c\316\273ash/CLasH/Translator.hs" +++ "b/c\316\273ash/CLasH/Translator.hs" @@ -1,176 +1,144 @@ -{-# LANGUAGE ScopedTypeVariables #-} +module CLasH.Translator + ( + makeVHDLAnnotations + ) where -module CLasH.Translator where - -import qualified Directory -import qualified System.FilePath as FilePath -import qualified List -import Debug.Trace -import qualified Control.Arrow as Arrow -import GHC hiding (loadModule, sigName) -import CoreSyn -import qualified CoreUtils -import qualified Var -import qualified Type -import qualified TyCon -import qualified DataCon -import qualified HscMain -import qualified SrcLoc -import qualified FastString +-- Standard Modules +import qualified System.Directory as Directory import qualified Maybe -import qualified Module -import qualified Data.Foldable as Foldable +import qualified Monad +import qualified System.FilePath as FilePath import qualified Control.Monad.Trans.State as State -import qualified Control.Monad as Monad -import Name +import Text.PrettyPrint.HughesPJ (render) +import Data.Accessor.Monad.Trans.State import qualified Data.Map as Map -import Data.Accessor -import Data.Generics -import NameEnv ( lookupNameEnv ) +import qualified Data.Time.Clock as Clock +import Debug.Trace + +-- GHC API +import qualified CoreSyn import qualified HscTypes -import HscTypes ( cm_binds, cm_types ) -import MonadUtils ( liftIO ) -import Outputable ( showSDoc, ppr, showSDocDebug ) -import DynFlags ( defaultDynFlags ) import qualified UniqSupply -import List ( find ) -import qualified List -import qualified Monad -import qualified Annotations -import qualified Serialized --- The following modules come from the ForSyDe project. They are really --- internal modules, so ForSyDe.cabal has to be modified prior to installing --- ForSyDe to get access to these modules. +-- VHDL Imports import qualified Language.VHDL.AST as AST -import qualified Language.VHDL.FileIO +import qualified Language.VHDL.FileIO as FileIO import qualified Language.VHDL.Ppr as Ppr --- This is needed for rendering the pretty printed VHDL -import Text.PrettyPrint.HughesPJ (render) +-- Local Imports import CLasH.Translator.TranslatorTypes import CLasH.Translator.Annotations -import CLasH.Utils.Pretty -import CLasH.Normalize -import CLasH.VHDL.VHDLTypes -import qualified CLasH.VHDL as VHDL - -makeVHDL :: FilePath -> String -> String -> Bool -> IO () -makeVHDL libdir filename name stateful = do - -- Load the module - (core, env) <- loadModule libdir filename +import CLasH.Utils +import CLasH.Utils.GhcTools +import CLasH.VHDL +import CLasH.VHDL.VHDLTools +import CLasH.VHDL.Testbench + +-- | Turn Haskell to VHDL, Usings Strings to indicate the Top Entity, Initial +-- State and Test Inputs. +-- makeVHDLStrings :: +-- FilePath -- ^ The GHC Library Dir +-- -> [FilePath] -- ^ The FileNames +-- -> String -- ^ The TopEntity +-- -> String -- ^ The InitState +-- -> String -- ^ The TestInput +-- -> IO () +-- makeVHDLStrings libdir filenames topentity initstate testinput = do +-- makeVHDL libdir filenames finder +-- where +-- finder = findSpec (hasVarName topentity) +-- (hasVarName initstate) +-- (isCLasHAnnotation isInitState) +-- (hasVarName testinput) + +-- | Turn Haskell to VHDL, Using the Annotations for Top Entity, Initial State +-- and Test Inputs found in the Files. +makeVHDLAnnotations :: + FilePath -- ^ The GHC Library Dir + -> [FilePath] -- ^ The FileNames + -> IO () +makeVHDLAnnotations libdir filenames = + makeVHDL libdir filenames finder + where + finder = findSpec (hasCLasHAnnotation isTopEntity) + (hasCLasHAnnotation isInitState) + (isCLasHAnnotation isInitState) + (hasCLasHAnnotation isTestInput) + +-- | Turn Haskell to VHDL, using the given finder functions to find the Top +-- Entity, Initial State and Test Inputs in the Haskell Files. +makeVHDL :: + FilePath -- ^ The GHC Library Dir + -> [FilePath] -- ^ The Filenames + -> Finder + -> IO () +makeVHDL libdir filenames finder = do + start <- Clock.getCurrentTime + -- Load the modules + (cores, env, specs) <- loadModules libdir filenames (Just finder) -- Translate to VHDL - vhdl <- moduleToVHDL env core [(name, stateful)] - -- Write VHDL to file - let dir = "./vhdl/" ++ name ++ "/" - prepareDir dir - mapM (writeVHDL dir) vhdl - return () - -makeVHDLAnn :: FilePath -> String -> IO () -makeVHDLAnn libdir filename = do - (core, top, init, env) <- loadModuleAnn libdir filename - let top_entity = head top - vhdl <- case init of - [] -> moduleToVHDLAnn env core [top_entity] - xs -> moduleToVHDLAnnState env core [(top_entity, (head xs))] + vhdl <- moduleToVHDL env cores specs + -- Write VHDL to file. Just use the first entity for the name + let top_entity = head $ Maybe.catMaybes $ map (\(t, _, _) -> t) specs let dir = "./vhdl/" ++ (show top_entity) ++ "/" prepareDir dir - mapM (writeVHDL dir) vhdl - return () - -listBindings :: FilePath -> String -> IO [()] -listBindings libdir filename = do - (core, env) <- loadModule libdir filename - let binds = CoreSyn.flattenBinds $ cm_binds core - mapM (listBinding) binds - -listBinding :: (CoreBndr, CoreExpr) -> IO () -listBinding (b, e) = do - putStr "\nBinder: " - putStr $ show b - putStr "\nExpression: \n" - putStr $ prettyShow e - putStr "\n\n" - putStr $ showSDoc $ ppr e - putStr "\n\n" - putStr $ showSDoc $ ppr $ CoreUtils.exprType e - putStr "\n\n" - --- | Show the core structure of the given binds in the given file. -listBind :: FilePath -> String -> String -> IO () -listBind libdir filename name = do - (core, env) <- loadModule libdir filename - let [(b, expr)] = findBinds core [name] - putStr "\n" - putStr $ prettyShow expr - putStr "\n\n" - putStr $ showSDoc $ ppr expr - putStr "\n\n" - putStr $ showSDoc $ ppr $ CoreUtils.exprType expr - putStr "\n\n" - --- | Translate the binds with the given names from the given core module to --- VHDL. The Bool in the tuple makes the function stateful (True) or --- stateless (False). -moduleToVHDL :: HscTypes.HscEnv -> HscTypes.CoreModule -> [(String, Bool)] -> IO [(AST.VHDLId, AST.DesignFile)] -moduleToVHDL env core list = do - let (names, statefuls) = unzip list - let binds = map fst $ findBinds core names - -- Generate a UniqSupply - -- Running - -- egrep -r "(initTcRnIf|mkSplitUniqSupply)" . - -- on the compiler dir of ghc suggests that 'z' is not used to generate a - -- unique supply anywhere. - uniqSupply <- UniqSupply.mkSplitUniqSupply 'z' - -- Turn bind into VHDL - let all_bindings = (CoreSyn.flattenBinds $ cm_binds core) - let (normalized_bindings, typestate) = normalizeModule env uniqSupply all_bindings binds statefuls - let vhdl = VHDL.createDesignFiles typestate normalized_bindings - mapM (putStr . render . Ppr.ppr . snd) vhdl - --putStr $ "\n\nFinal session:\n" ++ prettyShow sess ++ "\n\n" + mapM_ (writeVHDL dir) vhdl + end <- Clock.getCurrentTime + trace ("\nTotal compilation took " ++ show (Clock.diffUTCTime end start)) $ + return () + +-- | Translate the specified entities in the given modules to VHDL. +moduleToVHDL :: + HscTypes.HscEnv -- ^ The GHC Environment + -> [HscTypes.CoreModule] -- ^ The Core Modules + -> [EntitySpec] -- ^ The entities to generate + -> IO [(AST.VHDLId, AST.DesignFile)] +moduleToVHDL env cores specs = do + (vhdl, count) <- runTranslatorSession env $ do + let all_bindings = concatMap (\x -> CoreSyn.flattenBinds (HscTypes.cm_binds x)) cores + -- Store the bindings we loaded + tsBindings %= Map.fromList all_bindings + let all_initstates = concatMap (\x -> case x of (_, Nothing, _) -> []; (_, Just inits, _) -> inits) specs + tsInitStates %= Map.fromList all_initstates + test_binds <- catMaybesM $ Monad.mapM mkTest specs + let topbinds = Maybe.catMaybes $ map (\(top, _, _) -> top) specs + vhdl <- case topbinds of + [] -> error "Could not find top entity requested" + tops -> createDesignFiles (tops ++ test_binds) + count <- get tsTransformCounter + return (vhdl, count) + mapM_ (putStr . render . Ppr.ppr . snd) vhdl + putStr $ "Total number of transformations applied: " ++ (show count) ++ "\n" return vhdl - -moduleToVHDLAnn :: HscTypes.HscEnv -> HscTypes.CoreModule -> [CoreSyn.CoreBndr] -> IO [(AST.VHDLId, AST.DesignFile)] -moduleToVHDLAnn env core binds = do + where + mkTest :: EntitySpec -> TranslatorSession (Maybe CoreSyn.CoreBndr) + -- Create a testbench for any entry that has test input + mkTest (_, _, Nothing) = return Nothing + mkTest (Nothing, _, _) = return Nothing + mkTest (Just top, _, Just input) = do + bndr <- createTestbench Nothing cores input top + return $ Just bndr + +-- Run the given translator session. Generates a new UniqSupply for that +-- session. +runTranslatorSession :: HscTypes.HscEnv -> TranslatorSession a -> IO a +runTranslatorSession env session = do -- Generate a UniqSupply -- Running -- egrep -r "(initTcRnIf|mkSplitUniqSupply)" . - -- on the compiler dir of ghc suggests that 'z' is not used to generate a - -- unique supply anywhere. + -- on the compiler dir of ghc suggests that 'z' is not used to generate + -- a unique supply anywhere. uniqSupply <- UniqSupply.mkSplitUniqSupply 'z' - -- Turn bind into VHDL - let all_bindings = (CoreSyn.flattenBinds $ cm_binds core) - let (normalized_bindings, typestate) = normalizeModule env uniqSupply all_bindings binds [False] - let vhdl = VHDL.createDesignFiles typestate normalized_bindings - mapM (putStr . render . Ppr.ppr . snd) vhdl - --putStr $ "\n\nFinal session:\n" ++ prettyShow sess ++ "\n\n" - return vhdl - -moduleToVHDLAnnState :: HscTypes.HscEnv -> HscTypes.CoreModule -> [(CoreSyn.CoreBndr, CoreSyn.CoreBndr)] -> IO [(AST.VHDLId, AST.DesignFile)] -moduleToVHDLAnnState env core list = do - let (binds, init_states) = unzip list - -- Generate a UniqSupply - -- Running - -- egrep -r "(initTcRnIf|mkSplitUniqSupply)" . - -- on the compiler dir of ghc suggests that 'z' is not used to generate a - -- unique supply anywhere. - uniqSupply <- UniqSupply.mkSplitUniqSupply 'z' - -- Turn bind into VHDL - let all_bindings = (CoreSyn.flattenBinds $ cm_binds core) - let (normalized_bindings, typestate) = normalizeModule env uniqSupply all_bindings binds [True] - let vhdl = VHDL.createDesignFiles typestate normalized_bindings - mapM (putStr . render . Ppr.ppr . snd) vhdl - --putStr $ "\n\nFinal session:\n" ++ prettyShow sess ++ "\n\n" - return vhdl + let init_typestate = TypeState builtin_types [] Map.empty Map.empty env + let init_state = TranslatorState uniqSupply init_typestate Map.empty Map.empty 0 Map.empty Map.empty Map.empty 0 + return $ State.evalState session init_state -- | Prepares the directory for writing VHDL files. This means creating the -- dir if it does not exist and removing all existing .vhdl files from it. prepareDir :: String -> IO() prepareDir dir = do -- Create the dir if needed - exists <- Directory.doesDirectoryExist dir - Monad.unless exists $ Directory.createDirectory dir + Directory.createDirectoryIfMissing True dir -- Find all .vhdl files in the directory files <- Directory.getDirectoryContents dir let to_remove = filter ((==".vhdl") . FilePath.takeExtension) files @@ -186,287 +154,6 @@ writeVHDL dir (name, vhdl) = do -- Find the filename let fname = dir ++ (AST.fromVHDLId name) ++ ".vhdl" -- Write the file - Language.VHDL.FileIO.writeDesignFile vhdl fname - --- | Loads the given file and turns it into a core module. -loadModule :: FilePath -> String -> IO (HscTypes.CoreModule, HscTypes.HscEnv) -loadModule libdir filename = - defaultErrorHandler defaultDynFlags $ do - runGhc (Just libdir) $ do - dflags <- getSessionDynFlags - setSessionDynFlags dflags - --target <- guessTarget "adder.hs" Nothing - --liftIO (print (showSDoc (ppr (target)))) - --liftIO $ printTarget target - --setTargets [target] - --load LoadAllTargets - --core <- GHC.compileToCoreSimplified "Adders.hs" - core <- GHC.compileToCoreModule filename - env <- GHC.getSession - return (core, env) - --- | Loads the given file and turns it into a core module. -loadModuleAnn :: FilePath -> String -> IO (HscTypes.CoreModule, [CoreSyn.CoreBndr], [CoreSyn.CoreBndr], HscTypes.HscEnv) -loadModuleAnn libdir filename = - defaultErrorHandler defaultDynFlags $ do - runGhc (Just libdir) $ do - dflags <- getSessionDynFlags - setSessionDynFlags dflags - --target <- guessTarget "adder.hs" Nothing - --liftIO (print (showSDoc (ppr (target)))) - --liftIO $ printTarget target - --setTargets [target] - --load LoadAllTargets - --core <- GHC.compileToCoreSimplified "Adders.hs" - core <- GHC.compileToCoreModule filename - env <- GHC.getSession - top_entity <- findTopEntity core - init_state <- findInitState core - return (core, top_entity, init_state, env) - -findTopEntity :: GhcMonad m => HscTypes.CoreModule -> m [CoreSyn.CoreBndr] -findTopEntity core = do - let binds = CoreSyn.flattenBinds $ cm_binds core - topbinds <- Monad.filterM (hasTopEntityAnnotation . fst) binds - let bndrs = case topbinds of [] -> error $ "Couldn't find top entity in current module." ; xs -> fst (unzip topbinds) - return bndrs - -findInitState :: GhcMonad m => HscTypes.CoreModule -> m [CoreSyn.CoreBndr] -findInitState core = do - let binds = CoreSyn.flattenBinds $ cm_binds core - statebinds <- Monad.filterM (hasInitStateAnnotation . fst) binds - let bndrs = case statebinds of [] -> [] ; xs -> fst (unzip statebinds) - return bndrs - -hasTopEntityAnnotation :: GhcMonad m => Var.Var -> m Bool -hasTopEntityAnnotation var = do - let deserializer = Serialized.deserializeWithData - let target = Annotations.NamedTarget (Var.varName var) - (anns :: [CLasHAnn]) <- GHC.findGlobalAnns deserializer target - let top_ents = filter isTopEntity anns - case top_ents of - [] -> return False - xs -> return True - -hasInitStateAnnotation :: GhcMonad m => Var.Var -> m Bool -hasInitStateAnnotation var = do - let deserializer = Serialized.deserializeWithData - let target = Annotations.NamedTarget (Var.varName var) - (anns :: [CLasHAnn]) <- GHC.findGlobalAnns deserializer target - let top_ents = filter isInitState anns - case top_ents of - [] -> return False - xs -> return True - --- | Extracts the named binds from the given module. -findBinds :: HscTypes.CoreModule -> [String] -> [(CoreBndr, CoreExpr)] -findBinds core names = Maybe.mapMaybe (findBind (CoreSyn.flattenBinds $ cm_binds core)) names - --- | Extract a named bind from the given list of binds -findBind :: [(CoreBndr, CoreExpr)] -> String -> Maybe (CoreBndr, CoreExpr) -findBind binds lookfor = - -- This ignores Recs and compares the name of the bind with lookfor, - -- disregarding any namespaces in OccName and extra attributes in Name and - -- Var. - find (\(var, _) -> lookfor == (occNameString $ nameOccName $ getName var)) binds - --- | Flattens the given bind into the given signature and adds it to the --- session. Then (recursively) finds any functions it uses and does the same --- with them. --- flattenBind :: --- HsFunction -- The signature to flatten into --- -> (CoreBndr, CoreExpr) -- The bind to flatten --- -> TranslatorState () --- --- flattenBind hsfunc bind@(var, expr) = do --- -- Flatten the function --- let flatfunc = flattenFunction hsfunc bind --- -- Propagate state variables --- let flatfunc' = propagateState hsfunc flatfunc --- -- Store the flat function in the session --- modA tsFlatFuncs (Map.insert hsfunc flatfunc') --- -- Flatten any functions used --- let used_hsfuncs = Maybe.mapMaybe usedHsFunc (flat_defs flatfunc') --- mapM_ resolvFunc used_hsfuncs - --- | Decide which incoming state variables will become state in the --- given function, and which will be propagate to other applied --- functions. --- propagateState :: --- HsFunction --- -> FlatFunction --- -> FlatFunction --- --- propagateState hsfunc flatfunc = --- flatfunc {flat_defs = apps', flat_sigs = sigs'} --- where --- (olds, news) = unzip $ getStateSignals hsfunc flatfunc --- states' = zip olds news --- -- Find all signals used by all sigdefs --- uses = concatMap sigDefUses (flat_defs flatfunc) --- -- Find all signals that are used more than once (is there a --- -- prettier way to do this?) --- multiple_uses = uses List.\\ (List.nub uses) --- -- Find the states whose "old state" signal is used only once --- single_use_states = filter ((`notElem` multiple_uses) . fst) states' --- -- See if these single use states can be propagated --- (substate_sigss, apps') = unzip $ map (propagateState' single_use_states) (flat_defs flatfunc) --- substate_sigs = concat substate_sigss --- -- Mark any propagated state signals as SigSubState --- sigs' = map --- (\(id, info) -> (id, if id `elem` substate_sigs then info {sigUse = SigSubState} else info)) --- (flat_sigs flatfunc) - --- | Propagate the state into a single function application. --- propagateState' :: --- [(SignalId, SignalId)] --- -- ^ TODO --- -> SigDef -- ^ The SigDef to process. --- -> ([SignalId], SigDef) --- -- ^ Any signal ids that should become substates, --- -- and the resulting application. --- --- propagateState' states def = --- if (is_FApp def) then --- (our_old ++ our_new, def {appFunc = hsfunc'}) --- else --- ([], def) --- where --- hsfunc = appFunc def --- args = appArgs def --- res = appRes def --- our_states = filter our_state states --- -- A state signal belongs in this function if the old state is --- -- passed in, and the new state returned --- our_state (old, new) = --- any (old `Foldable.elem`) args --- && new `Foldable.elem` res --- (our_old, our_new) = unzip our_states --- -- Mark the result --- zipped_res = zipValueMaps res (hsFuncRes hsfunc) --- res' = fmap (mark_state (zip our_new [0..])) zipped_res --- -- Mark the args --- zipped_args = zipWith zipValueMaps args (hsFuncArgs hsfunc) --- args' = map (fmap (mark_state (zip our_old [0..]))) zipped_args --- hsfunc' = hsfunc {hsFuncArgs = args', hsFuncRes = res'} --- --- mark_state :: [(SignalId, StateId)] -> (SignalId, HsValueUse) -> HsValueUse --- mark_state states (id, use) = --- case lookup id states of --- Nothing -> use --- Just state_id -> State state_id - --- | Returns pairs of signals that should be mapped to state in this function. --- getStateSignals :: --- HsFunction -- | The function to look at --- -> FlatFunction -- | The function to look at --- -> [(SignalId, SignalId)] --- -- | TODO The state signals. The first is the state number, the second the --- -- signal to assign the current state to, the last is the signal --- -- that holds the new state. --- --- getStateSignals hsfunc flatfunc = --- [(old_id, new_id) --- | (old_num, old_id) <- args --- , (new_num, new_id) <- res --- , old_num == new_num] --- where --- sigs = flat_sigs flatfunc --- -- Translate args and res to lists of (statenum, sigid) --- args = concat $ zipWith stateList (hsFuncArgs hsfunc) (flat_args flatfunc) --- res = stateList (hsFuncRes hsfunc) (flat_res flatfunc) - --- | Find the given function, flatten it and add it to the session. Then --- (recursively) do the same for any functions used. --- resolvFunc :: --- HsFunction -- | The function to look for --- -> TranslatorState () --- --- resolvFunc hsfunc = do --- flatfuncmap <- getA tsFlatFuncs --- -- Don't do anything if there is already a flat function for this hsfunc or --- -- when it is a builtin function. --- Monad.unless (Map.member hsfunc flatfuncmap) $ do --- -- Not working with new builtins -- Monad.unless (elem hsfunc VHDL.builtin_hsfuncs) $ do --- -- New function, resolve it --- core <- getA tsCoreModule --- -- Find the named function --- let name = (hsFuncName hsfunc) --- let bind = findBind (CoreSyn.flattenBinds $ cm_binds core) name --- case bind of --- Nothing -> error $ "Couldn't find function " ++ name ++ " in current module." --- Just b -> flattenBind hsfunc b - --- | Translate a top level function declaration to a HsFunction. i.e., which --- interface will be provided by this function. This function essentially --- defines the "calling convention" for hardware models. --- mkHsFunction :: --- Var.Var -- ^ The function defined --- -> Type -- ^ The function type (including arguments!) --- -> Bool -- ^ Is this a stateful function? --- -> HsFunction -- ^ The resulting HsFunction --- --- mkHsFunction f ty stateful= --- HsFunction hsname hsargs hsres --- where --- hsname = getOccString f --- (arg_tys, res_ty) = Type.splitFunTys ty --- (hsargs, hsres) = --- if stateful --- then --- let --- -- The last argument must be state --- state_ty = last arg_tys --- state = useAsState (mkHsValueMap state_ty) --- -- All but the last argument are inports --- inports = map (useAsPort . mkHsValueMap)(init arg_tys) --- hsargs = inports ++ [state] --- hsres = case splitTupleType res_ty of --- -- Result type must be a two tuple (state, ports) --- Just [outstate_ty, outport_ty] -> if Type.coreEqType state_ty outstate_ty --- then --- Tuple [state, useAsPort (mkHsValueMap outport_ty)] --- else --- error $ "Input state type of function " ++ hsname ++ ": " ++ (showSDoc $ ppr state_ty) ++ " does not match output state type: " ++ (showSDoc $ ppr outstate_ty) --- otherwise -> error $ "Return type of top-level function " ++ hsname ++ " must be a two-tuple containing a state and output ports." --- in --- (hsargs, hsres) --- else --- -- Just use everything as a port --- (map (useAsPort . mkHsValueMap) arg_tys, useAsPort $ mkHsValueMap res_ty) - --- | Adds signal names to the given FlatFunction --- nameFlatFunction :: --- FlatFunction --- -> FlatFunction --- --- nameFlatFunction flatfunc = --- -- Name the signals --- let --- s = flat_sigs flatfunc --- s' = map nameSignal s in --- flatfunc { flat_sigs = s' } --- where --- nameSignal :: (SignalId, SignalInfo) -> (SignalId, SignalInfo) --- nameSignal (id, info) = --- let hints = nameHints info in --- let parts = ("sig" : hints) ++ [show id] in --- let name = concat $ List.intersperse "_" parts in --- (id, info {sigName = Just name}) --- --- -- | Splits a tuple type into a list of element types, or Nothing if the type --- -- is not a tuple type. --- splitTupleType :: --- Type -- ^ The type to split --- -> Maybe [Type] -- ^ The tuples element types --- --- splitTupleType ty = --- case Type.splitTyConApp_maybe ty of --- Just (tycon, args) -> if TyCon.isTupleTyCon tycon --- then --- Just args --- else --- Nothing --- Nothing -> Nothing + FileIO.writeDesignFile vhdl fname -- vim: set ts=8 sw=2 sts=2 expandtab: