----------------------------------------------------------------------------- -- | -- Module : ForSyDe.Deep.Backend.VHDL.Quartus -- Copyright : (c) ES Group, KTH/ICT/ES 2007-2013 -- License : BSD-style (see the file LICENSE) -- -- Maintainer : forsyde-dev@ict.kth.se -- Stability : experimental -- Portability : portable -- -- Functions to process the VHDL compilation results with Altera's Quartus II -- software. ----------------------------------------------------------------------------- module ForSyDe.Deep.Backend.VHDL.Quartus (callQuartus) where import ForSyDe.Deep.ForSyDeErr import ForSyDe.Deep.Config (getDataDir) import ForSyDe.Deep.OSharing (readURef) import ForSyDe.Deep.System.SysDef (subSys,sid,unPrimSysDef) import ForSyDe.Deep.Backend.VHDL.Traverse.VHDLM import System.IO import System.Directory import System.Process import Control.Monad.State import System.Exit (ExitCode(..)) -- | Analyze the results with Quartus -- (Note: the initial and final CWD will be /systemName/vhdl ) callQuartus :: VHDLM () callQuartus = do mQuartus <- gets (execQuartus.ops.global) -- is it necessary to call quartus? case mQuartus of Nothing -> return () -- Yes, we create the tcl script and call quartus_sh checking if it -- exists in the system Just ops -> do mPath <- liftIO $ findExecutable "quartus_sh" case mPath of Nothing -> do liftIO $ hPutStrLn stderr "Error: quartus_sh not found" throwFError QuartusFailed Just _ -> do contents <- gen_quartus_tcl ops liftIO $ writeFile "quartus.tcl" contents liftIO $ putStrLn "Running quartus_sh -t quartus.tcl" code <- liftIO $ waitForProcess =<< runCommand "quartus_sh -t quartus.tcl" case code of ExitFailure _ -> throwFError QuartusFailed _ -> return () -- | Generate the content of quartus.tcl -- Note that, even in windows, the tcl interpreter requires pathnames -- to use \"/\" instead of \"\\\" gen_quartus_tcl :: QuartusOps -> VHDLM String gen_quartus_tcl (QuartusOps act mFMax mFamDev assigs) = do sysName <- gets (sid.globalSysDef.global) dataPath <- liftIO $ getDataDir recursive <- isRecursiveSet subs <- gets (subSys.globalSysDef.global) let libDir = (changeSlashes dataPath) ++ "/lib" sysLib = sysName ++ "_lib" return $ unlines ( packages ++ [projectNew sysName] ++ mDefault mFMax fmax ++ mDefault mFamDev famDev ++ map mkAssig assigs ++ [topLevelEntity sysName, includeVHDLFile ("work/" ++ sysName ++ ".vhd") Nothing, includeVHDLFile ('"' : (libDir ++ "/forsyde.vhd") ++ "\"") (Just "forsyde"), includeVHDLFile (sysLib ++ "/" ++ sysLib ++ ".vhd") (Just sysLib)] ++ (if recursive then map (\s -> includeVHDLFile ((("work/"++).(++".vhd").sid.readURef.unPrimSysDef) s) Nothing) subs else []) ++ [actionCmd act] ) where mDefault m f = maybe [] f m actionCmd act = case act of AnalysisAndElaboration -> "execute_flow -analysis_and_elaboration" AnalysisAndSynthesis -> "execute_module -tool map" FullCompilation -> "execute_flow -compile" fmax max = ["set_global_assignment -name FMAX_REQUIREMENT \"" ++ show max ++ " MHz\""] famDev (fam, mDev) = ["set_global_assignment -name FAMILY " ++ show fam] ++ case mDev of Nothing -> [] Just dev -> ["set_global_assignment -name DEVICE " ++ show dev] mkAssig (vhdlPin, fpgaPin) = "set_location_assignment " ++ fpgaPin ++ " -to " ++ vhdlPin packages = ["load_package project", "load_package flow"] includeVHDLFile :: FilePath -- ^ system name -> Maybe String -- ^ what library to include the -- file in -> String includeVHDLFile file mLib = "set_global_assignment -name VHDL_FILE " ++ file ++ maybe "" (" -library "++) mLib topLevelEntity name = "set_global_assignment -name TOP_LEVEL_ENTITY " ++ name projectNew name = "project_new " ++ name ++ " -overwrite" changeSlashes [] = [] changeSlashes ('\\':xs) = '/' : changeSlashes xs changeSlashes (x:xs) = x : changeSlashes xs