----------------------------------------------------------------------------- -- | -- Module : ForSyDe.Deep.Backend.VHDL.Ghdl -- 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 GHDL. ----------------------------------------------------------------------------- module ForSyDe.Deep.Backend.VHDL.Ghdl (executeTestBenchGhdl) where import ForSyDe.Deep.Backend.VHDL.Traverse.VHDLM import ForSyDe.Deep.Backend.VHDL.TestBench import ForSyDe.Deep.System.SysDef import ForSyDe.Deep.OSharing import ForSyDe.Deep.ForSyDeErr import ForSyDe.Deep.Config (getDataDir) import Data.Maybe (isJust) import Control.Monad.State (gets) import System.Directory (findExecutable, setCurrentDirectory, getTemporaryDirectory, createDirectoryIfMissing) import System.Process (readProcessWithExitCode, readProcess, runProcess, waitForProcess) import System.Exit (ExitCode(..)) import System.IO import System.FilePath (()) import qualified Language.Haskell.TH as TH (Exp) -- -- This tool driver needs a Ghdl version containing the following commit, -- otherwise it will fail with an obscure pattern match failure from within TH -- generated code -- -- commit f6d8e786a1ca3165b41cea7de05b8f2151ac31ff -- Author: Tristan Gingold -- Date: Sat May 30 14:05:20 2015 +0200 -- -- write: do not implicitely append LF. -- -- The oldest release containing this was v0.33 -- data GhdlCommand = Analyze | Elaborate | Compile | Import | Run deriving Eq instance Show GhdlCommand where show Analyze = "-a" show Elaborate = "-e" show Compile = "-c" show Import = "-i" show Run = "-r" data GhdlEnv = GhdlEnv { sysId :: String , sysTb :: String , syslib :: String , tbFile :: FilePath , tbExecutable :: FilePath , libFile :: FilePath , workFiles :: [FilePath] , forsydeLibFile :: FilePath , forsydeLibDir :: FilePath , systemLibDir :: FilePath , workDir :: FilePath , paths :: [FilePath] } mkGhdlEnv :: SysDefVal -> FilePath -> GhdlEnv mkGhdlEnv sys osDataPath = GhdlEnv { sysId = sysId , sysTb = sysTb , syslib = syslib , libFile = syslib (syslib ++ ".vhd") , tbFile = "test" (sysTb ++ ".vhd") , forsydeLibFile = osDataPath"lib""forsyde.vhd" , workFiles = ("work" (sysId ++ ".vhd")) : map (("work").(++".vhd").sid.readURef.unPrimSysDef) (subSys sys) , forsydeLibDir = forsydeLibDir , systemLibDir = systemLibDir , workDir = workDir , tbExecutable = workDir sysTb , paths = [forsydeLibDir, systemLibDir, workDir] } where sysId = sid sys syslib = sysId ++ "_lib" sysTb = sysId ++ "_tb" workDir = "work" "ghdl" forsydeLibDir = "forsyde" "ghdl" systemLibDir = syslib "ghdl" -- | Generate a testbench and execute it with GHDL -- (Note: the initial and final CWD will be / ) executeTestBenchGhdl :: Maybe Int -- ^ Number of cycles to simulate -> [[TH.Exp]] -- ^ input stimuli, each signal value -- is expressed as a template haskell -- expression -> VHDLM [[String]] -- ^ results, each signal value -- is expressed as a string executeTestBenchGhdl mCycles stimuli = do -- Check if GHDL is installed installed <- liftIO isGhdlInstalled unless installed (throwFError GhdlFailed) -- compile testbench cycles <- writeVHDLTestBench mCycles stimuli -- assemble all the file paths for compilation sysid <- gets (sid.globalSysDef.global) sys <- gets (globalSysDef.global) dataPath <- liftIO getDataDir let env = mkGhdlEnv sys dataPath -- set up directory structure and tmp files file <- liftIO $ do setCurrentDirectory (sysid "vhdl") tmpdir <- getTemporaryDirectory (file, handle) <- openTempFile tmpdir "tb_out.txt" hClose handle -- close handle to avoid opening problems in windows mapM_ (createDirectoryIfMissing True) $ paths env return file -- analyze the installed forsyde library runGhdlCommand Analyze "forsyde" -- library (toplevel) (forsydeLibDir env) -- workdir [] -- include paths [forsydeLibFile env] -- files -- analyze the generated system library runGhdlCommand Analyze (syslib env) -- library (toplevel) (systemLibDir env) -- workdir [forsydeLibDir env] -- include paths [libFile env] -- files -- compile test bench hierarchy runGhdlCompile (sysTb env) -- toplevel (workDir env) -- workdir [forsydeLibDir env, systemLibDir env] -- include paths (tbFile env:workFiles env) -- files -- Run simulation and capture output testOutput <- runGhdlSim (sysTb env) cycles liftIO $ setCurrentDirectory (".." "..") --liftIO $ print testOutput -- show what we actually get from Ghdl parseTestBenchOut testOutput runGhdlCompile :: String -> FilePath -> [FilePath] -> [FilePath] -> VHDLM () runGhdlCompile toplevel workdir libPaths files = runGhdlCommandInWorkdir Compile "work" workdir libPaths files extra where extra = [show Elaborate, toplevel] runGhdlCommand :: GhdlCommand -> String -> FilePath -> [FilePath] -> [FilePath] -> VHDLM () runGhdlCommand cmd lib work paths files = runGhdlCommandInWorkdir cmd lib work paths files [] runGhdlCommandInWorkdir :: GhdlCommand -> String -> FilePath -> [FilePath] -> [FilePath] -> [String] -> VHDLM () runGhdlCommandInWorkdir command libName workdir libPaths files extraOpts = runCommand "ghdl" $ cmd ++ paths ++ opts ++ files ++ extraOpts where cmd = [show command] paths = map ("-P"++) libPaths opts = ["--work="++libName, "--workdir="++workdir] runGhdlSim :: String -> Int -> VHDLM String runGhdlSim toplevel cycles = do (output,success) <- liftIO $ do putStrLn $ "Running: ghdl " ++ unwords args (exitcode,stdout,stderr) <- readProcessWithExitCode "ghdl" args stdin let success = exitcode == ExitSuccess return (stdout,success) unless success (throwFError GhdlFailed) return output where stdin = "" args = [show Run, toplevel, "--stop-time="++show (cycles*10)++"ns"] -- | run a shell command runCommand :: String -- ^ Command to execute -> [String] -- ^ Command arguments -> VHDLM () runCommand command args = do success <- liftIO $ do putStrLn msg h <- runProcess command args Nothing Nothing Nothing Nothing Nothing code <- waitForProcess h return $ code == ExitSuccess unless success (throwFError GhdlFailed) where msg = "Running: " ++ command ++ " " ++ unwords args -- Look for GHDL executables isGhdlInstalled :: IO Bool isGhdlInstalled = executablePresent "ghdl" where executablePresent = liftM isJust .findExecutable