mirror of https://github.com/zachjs/sv2v.git
support for loading from library directories
This commit is contained in:
parent
aa429204ea
commit
51c90baf5e
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -1,5 +1,13 @@
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
|
||||||
|
* Added `-y`/`--libdir` for specifying library directories from which to
|
||||||
|
automatically load modules and interfaces used in the design that are not
|
||||||
|
found in the provided input files
|
||||||
|
* The `string` data type is now dropped from parameters and localparams
|
||||||
|
* Added support for passing through `sequence` and `property` declarations
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* Fixed crash when converting multi-dimensional arrays or arrays of structs or
|
* Fixed crash when converting multi-dimensional arrays or arrays of structs or
|
||||||
|
|
@ -18,11 +26,6 @@
|
||||||
* Fixed keywords included in the "1364-2001" and "1364-2001-noconfig"
|
* Fixed keywords included in the "1364-2001" and "1364-2001-noconfig"
|
||||||
`begin_keywords` version specifiers
|
`begin_keywords` version specifiers
|
||||||
|
|
||||||
### New Features
|
|
||||||
|
|
||||||
* `string` data type is now dropped from parameters and localparams
|
|
||||||
* Added support for passing through `sequence` and `property` declarations
|
|
||||||
|
|
||||||
### Other Enhancements
|
### Other Enhancements
|
||||||
|
|
||||||
* Added elaboration for accesses to fields of struct constants, which can
|
* Added elaboration for accesses to fields of struct constants, which can
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,9 @@ default. Users should typically pass all of their SystemVerilog source files to
|
||||||
sv2v at once so it can properly resolve packages, interfaces, type parameters,
|
sv2v at once so it can properly resolve packages, interfaces, type parameters,
|
||||||
etc., across files. Using `--write=adjacent` will create a converted `.v` for
|
etc., across files. Using `--write=adjacent` will create a converted `.v` for
|
||||||
every `.sv` input file rather than printing to `stdout`. `--write`/`-w` can also
|
every `.sv` input file rather than printing to `stdout`. `--write`/`-w` can also
|
||||||
be used to specify a path to a `.v` output file.
|
be used to specify a path to a `.v` output file. Undefined modules and
|
||||||
|
interfaces can be automatically loaded from library directories using
|
||||||
|
`--libdir`/`-y`.
|
||||||
|
|
||||||
Users may specify `include` search paths, define macros during preprocessing,
|
Users may specify `include` search paths, define macros during preprocessing,
|
||||||
and exclude some of the conversions. Specifying `-` as an input file will read
|
and exclude some of the conversions. Specifying `-` as an input file will read
|
||||||
|
|
@ -91,6 +93,8 @@ sv2v [OPTIONS] [FILES]
|
||||||
|
|
||||||
Preprocessing:
|
Preprocessing:
|
||||||
-I --incdir=DIR Add directory to include search path
|
-I --incdir=DIR Add directory to include search path
|
||||||
|
-y --libdir=DIR Add a directory to the library search path used
|
||||||
|
when looking for undefined modules and interfaces
|
||||||
-D --define=NAME[=VALUE] Define a macro for preprocessing
|
-D --define=NAME[=VALUE] Define a macro for preprocessing
|
||||||
--siloed Lex input files separately, so macros from
|
--siloed Lex input files separately, so macros from
|
||||||
earlier files are not defined in later files
|
earlier files are not defined in later files
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ data Write
|
||||||
data Job = Job
|
data Job = Job
|
||||||
{ files :: [FilePath]
|
{ files :: [FilePath]
|
||||||
, incdir :: [FilePath]
|
, incdir :: [FilePath]
|
||||||
|
, libdir :: [FilePath]
|
||||||
, define :: [String]
|
, define :: [String]
|
||||||
, siloed :: Bool
|
, siloed :: Bool
|
||||||
, skipPreprocessor :: Bool
|
, skipPreprocessor :: Bool
|
||||||
|
|
@ -58,6 +59,9 @@ defaultJob = Job
|
||||||
, incdir = nam_ "I" &= name "incdir" &= typDir
|
, incdir = nam_ "I" &= name "incdir" &= typDir
|
||||||
&= help "Add directory to include search path"
|
&= help "Add directory to include search path"
|
||||||
&= groupname "Preprocessing"
|
&= groupname "Preprocessing"
|
||||||
|
, libdir = nam_ "y" &= name "libdir" &= typDir
|
||||||
|
&= help ("Add a directory to the library search path used when looking"
|
||||||
|
++ " for undefined modules and interfaces")
|
||||||
, define = nam_ "D" &= name "define" &= typ "NAME[=VALUE]"
|
, define = nam_ "D" &= name "define" &= typ "NAME[=VALUE]"
|
||||||
&= help "Define a macro for preprocessing"
|
&= help "Define a macro for preprocessing"
|
||||||
, siloed = nam_ "siloed" &= help ("Lex input files separately, so"
|
, siloed = nam_ "siloed" &= help ("Lex input files separately, so"
|
||||||
|
|
|
||||||
|
|
@ -3,28 +3,41 @@
|
||||||
- Author: Zachary Snow <zach@zachjs.com>
|
- Author: Zachary Snow <zach@zachjs.com>
|
||||||
-}
|
-}
|
||||||
module Language.SystemVerilog.Parser
|
module Language.SystemVerilog.Parser
|
||||||
( initialEnv
|
( parseFiles
|
||||||
, parseFiles
|
|
||||||
, Config(..)
|
, Config(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad.Except
|
import Control.Monad.Except
|
||||||
import Data.List (elemIndex)
|
import Data.List (elemIndex)
|
||||||
|
import Data.Maybe (catMaybes)
|
||||||
|
import System.Directory (findFile)
|
||||||
import qualified Data.Map.Strict as Map
|
import qualified Data.Map.Strict as Map
|
||||||
|
import qualified Data.Set as Set
|
||||||
|
|
||||||
import Language.SystemVerilog.AST (AST)
|
import Language.SystemVerilog.AST (AST)
|
||||||
import Language.SystemVerilog.Parser.Lex (lexStr)
|
import Language.SystemVerilog.Parser.Lex (lexStr)
|
||||||
import Language.SystemVerilog.Parser.Parse (parse)
|
import Language.SystemVerilog.Parser.Parse (parse)
|
||||||
import Language.SystemVerilog.Parser.Preprocess (preprocess, annotate, Env, Contents)
|
import Language.SystemVerilog.Parser.Preprocess (preprocess, annotate, Env, Contents)
|
||||||
|
|
||||||
|
type Output = (FilePath, AST)
|
||||||
|
type Strings = Set.Set String
|
||||||
|
|
||||||
data Config = Config
|
data Config = Config
|
||||||
{ cfEnv :: Env
|
{ cfDefines :: [String]
|
||||||
, cfIncludePaths :: [FilePath]
|
, cfIncludePaths :: [FilePath]
|
||||||
|
, cfLibraryPaths :: [FilePath]
|
||||||
, cfSiloed :: Bool
|
, cfSiloed :: Bool
|
||||||
, cfSkipPreprocessor :: Bool
|
, cfSkipPreprocessor :: Bool
|
||||||
, cfOversizedNumbers :: Bool
|
, cfOversizedNumbers :: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data Context = Context
|
||||||
|
{ ctConfig :: Config
|
||||||
|
, ctEnv :: Env
|
||||||
|
, ctUsed :: Strings
|
||||||
|
, ctHave :: Strings
|
||||||
|
}
|
||||||
|
|
||||||
-- parse CLI macro definitions into the internal macro environment format
|
-- parse CLI macro definitions into the internal macro environment format
|
||||||
initialEnv :: [String] -> Env
|
initialEnv :: [String] -> Env
|
||||||
initialEnv = Map.map (, []) . Map.fromList . map splitDefine
|
initialEnv = Map.map (, []) . Map.fromList . map splitDefine
|
||||||
|
|
@ -38,26 +51,58 @@ splitDefine str =
|
||||||
where (name, rest) = splitAt idx str
|
where (name, rest) = splitAt idx str
|
||||||
|
|
||||||
-- parse a list of files according to the given configuration
|
-- parse a list of files according to the given configuration
|
||||||
parseFiles :: Config -> [FilePath] -> ExceptT String IO [AST]
|
parseFiles :: Config -> [FilePath] -> ExceptT String IO [Output]
|
||||||
parseFiles _ [] = return []
|
parseFiles config = parseFiles' context . zip (repeat "")
|
||||||
parseFiles config (path : paths) = do
|
where
|
||||||
(config', ast) <- parseFile config path
|
context = Context config env mempty mempty
|
||||||
fmap (ast :) $ parseFiles config' paths
|
env = initialEnv $ cfDefines config
|
||||||
|
|
||||||
-- parse an individual file, potentially updating the configuration
|
-- parse files, keeping track of which parts are defined and used
|
||||||
parseFile :: Config -> FilePath -> ExceptT String IO (Config, AST)
|
parseFiles' :: Context -> [(String, FilePath)] -> ExceptT String IO [Output]
|
||||||
parseFile config path = do
|
|
||||||
(config', contents) <- preprocessFile config path
|
-- look for missing parts in libraries if any library paths were provided
|
||||||
|
parseFiles' context []
|
||||||
|
| null libdirs = return []
|
||||||
|
| otherwise = do
|
||||||
|
possibleFiles <- catMaybes <$> mapM lookupLibrary missingParts
|
||||||
|
if null possibleFiles
|
||||||
|
then return []
|
||||||
|
else parseFiles' context possibleFiles
|
||||||
|
where
|
||||||
|
missingParts = Set.toList $ ctUsed context Set.\\ ctHave context
|
||||||
|
libdirs = cfLibraryPaths $ ctConfig context
|
||||||
|
lookupLibrary partName = ((partName, ) <$>) <$> lookupLibFile partName
|
||||||
|
lookupLibFile = liftIO . findFile libdirs . (++ ".sv")
|
||||||
|
|
||||||
|
-- load the files, but complain if an expected part is missing
|
||||||
|
parseFiles' context ((part, path) : files) = do
|
||||||
|
(context', ast) <- parseFile context path
|
||||||
|
let misdirected = not $ null part || Set.member part (ctHave context')
|
||||||
|
when misdirected $ throwError $
|
||||||
|
"Expected to find module or interface " ++ show part ++ " in file "
|
||||||
|
++ show path ++ " selected from the library path."
|
||||||
|
((path, ast) :) <$> parseFiles' context' files
|
||||||
|
|
||||||
|
-- parse an individual file, updating the context
|
||||||
|
parseFile :: Context -> FilePath -> ExceptT String IO (Context, AST)
|
||||||
|
parseFile context path = do
|
||||||
|
(context', contents) <- preprocessFile context path
|
||||||
tokens <- liftEither $ runExcept $ lexStr contents
|
tokens <- liftEither $ runExcept $ lexStr contents
|
||||||
ast <- parse (cfOversizedNumbers config) tokens
|
(ast, used, have) <- parse (cfOversizedNumbers config) tokens
|
||||||
return (config', ast)
|
let context'' = context' { ctUsed = used <> ctUsed context
|
||||||
|
, ctHave = have <> ctHave context }
|
||||||
|
return (context'', ast)
|
||||||
|
where config = ctConfig context
|
||||||
|
|
||||||
-- preprocess an individual file, potentially updating the configuration
|
-- preprocess an individual file, potentially updating the environment
|
||||||
preprocessFile :: Config -> FilePath -> ExceptT String IO (Config, Contents)
|
preprocessFile :: Context -> FilePath -> ExceptT String IO (Context, Contents)
|
||||||
preprocessFile config path | cfSkipPreprocessor config =
|
preprocessFile context path
|
||||||
fmap (config, ) $ annotate path
|
| cfSkipPreprocessor config =
|
||||||
preprocessFile config path = do
|
(context, ) <$> annotate path
|
||||||
(env', contents) <- preprocess (cfIncludePaths config) env path
|
| otherwise = do
|
||||||
let config' = config { cfEnv = if cfSiloed config then env else env' }
|
(env', contents) <- preprocess (cfIncludePaths config) env path
|
||||||
return (config', contents)
|
let context' = context { ctEnv = if cfSiloed config then env else env' }
|
||||||
where env = cfEnv config
|
return (context', contents)
|
||||||
|
where
|
||||||
|
config = ctConfig context
|
||||||
|
env = ctEnv context
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import Control.Monad.Except
|
||||||
import Control.Monad.State.Strict
|
import Control.Monad.State.Strict
|
||||||
import Data.Maybe (catMaybes, fromMaybe)
|
import Data.Maybe (catMaybes, fromMaybe)
|
||||||
import System.IO (hPutStrLn, stderr)
|
import System.IO (hPutStrLn, stderr)
|
||||||
|
import qualified Data.Set as Set
|
||||||
import Language.SystemVerilog.AST
|
import Language.SystemVerilog.AST
|
||||||
import Language.SystemVerilog.Parser.ParseDecl
|
import Language.SystemVerilog.Parser.ParseDecl
|
||||||
import Language.SystemVerilog.Parser.Tokens
|
import Language.SystemVerilog.Parser.Tokens
|
||||||
|
|
@ -554,7 +555,7 @@ Part(begin, end) :: { Description }
|
||||||
|
|
||||||
PartHeader :: { [Attr] -> Bool -> PartKW -> [ModuleItem] -> Identifier -> ParseState Description }
|
PartHeader :: { [Attr] -> Bool -> PartKW -> [ModuleItem] -> Identifier -> ParseState Description }
|
||||||
: Lifetime Identifier PackageImportDeclarations Params PortDecls ";"
|
: Lifetime Identifier PackageImportDeclarations Params PortDecls ";"
|
||||||
{ \attrs extern kw items label -> checkTag $2 label $ Part attrs extern kw $1 $2 (fst $5) ($3 ++ $4 ++ (snd $5) ++ items) }
|
{% recordPartHave $2 >> return \attrs extern kw items label -> checkTag $2 label $ Part attrs extern kw $1 $2 (fst $5) ($3 ++ $4 ++ (snd $5) ++ items) }
|
||||||
|
|
||||||
ModuleKW :: { PartKW }
|
ModuleKW :: { PartKW }
|
||||||
: "module" { Module }
|
: "module" { Module }
|
||||||
|
|
@ -701,7 +702,7 @@ ModuleItem :: { [ModuleItem] }
|
||||||
| "generate" GenItems endgenerate { [Generate $2] }
|
| "generate" GenItems endgenerate { [Generate $2] }
|
||||||
NonGenerateModuleItem :: { [ModuleItem] }
|
NonGenerateModuleItem :: { [ModuleItem] }
|
||||||
-- This item covers module instantiations and all declarations
|
-- This item covers module instantiations and all declarations
|
||||||
: ModuleDeclTokens(";") { parseDTsAsModuleItems $1 }
|
: ModuleDeclTokens(";") {% mapM recordPartUsed $ parseDTsAsModuleItems $1 }
|
||||||
| ParameterDecl(";") { map (MIPackageItem . Decl) $1 }
|
| ParameterDecl(";") { map (MIPackageItem . Decl) $1 }
|
||||||
| "defparam" LHSAsgns ";" { map (uncurry Defparam) $2 }
|
| "defparam" LHSAsgns ";" { map (uncurry Defparam) $2 }
|
||||||
| "assign" AssignOption LHSAsgns ";" { map (uncurry $ Assign $2) $3 }
|
| "assign" AssignOption LHSAsgns ";" { map (uncurry $ Assign $2) $3 }
|
||||||
|
|
@ -1549,25 +1550,29 @@ data ParseData = ParseData
|
||||||
{ pPosition :: Position
|
{ pPosition :: Position
|
||||||
, pTokens :: [Token]
|
, pTokens :: [Token]
|
||||||
, pOversizedNumbers :: Bool
|
, pOversizedNumbers :: Bool
|
||||||
|
, pPartsUsed :: Strings
|
||||||
|
, pPartsHave :: Strings
|
||||||
}
|
}
|
||||||
|
|
||||||
type ParseState = StateT ParseData (ExceptT String IO)
|
type ParseState = StateT ParseData (ExceptT String IO)
|
||||||
|
type Strings = Set.Set String
|
||||||
|
|
||||||
parse :: Bool -> [Token] -> ExceptT String IO AST
|
parse :: Bool -> [Token] -> ExceptT String IO (AST, Strings, Strings)
|
||||||
parse _ [] = return []
|
parse _ [] = return mempty
|
||||||
parse oversizedNumbers tokens =
|
parse oversizedNumbers tokens = do
|
||||||
evalStateT parseMain initialState
|
(ast, finalState) <- runStateT parseMain initialState
|
||||||
|
return (ast, pPartsUsed finalState, pPartsHave finalState)
|
||||||
where
|
where
|
||||||
position = tokenPosition $ head tokens
|
position = tokenPosition $ head tokens
|
||||||
initialState = ParseData position tokens oversizedNumbers
|
initialState = ParseData position tokens oversizedNumbers mempty mempty
|
||||||
|
|
||||||
positionKeep :: (Token -> ParseState a) -> ParseState a
|
positionKeep :: (Token -> ParseState a) -> ParseState a
|
||||||
positionKeep cont = do
|
positionKeep cont = do
|
||||||
ParseData _ tokens oversizedNumbers <- get
|
tokens <- gets pTokens
|
||||||
case tokens of
|
case tokens of
|
||||||
[] -> cont TokenEOF
|
[] -> cont TokenEOF
|
||||||
tok : toks -> do
|
tok : toks -> do
|
||||||
put $ ParseData (tokenPosition tok) toks oversizedNumbers
|
modify' $ \s -> s { pPosition = tokenPosition tok, pTokens = toks }
|
||||||
cont tok
|
cont tok
|
||||||
|
|
||||||
parseErrorTok :: Token -> ParseState a
|
parseErrorTok :: Token -> ParseState a
|
||||||
|
|
@ -1842,4 +1847,19 @@ portBindingAttrs (pos, attrs) = parseWarning pos msg
|
||||||
where msg = "Ignored port connection attributes "
|
where msg = "Ignored port connection attributes "
|
||||||
++ concatMap show attrs ++ "."
|
++ concatMap show attrs ++ "."
|
||||||
|
|
||||||
|
recordPartUsed :: ModuleItem -> ParseState ModuleItem
|
||||||
|
recordPartUsed item@(Instance partName _ _ _ _) = do
|
||||||
|
partsUsed <- gets pPartsUsed
|
||||||
|
when (Set.notMember partName partsUsed) $ do
|
||||||
|
let partsUsed' = Set.insert partName partsUsed
|
||||||
|
modify' $ \s -> s { pPartsUsed = partsUsed' }
|
||||||
|
return item
|
||||||
|
recordPartUsed item = return item
|
||||||
|
|
||||||
|
recordPartHave :: Identifier -> ParseState ()
|
||||||
|
recordPartHave partName = do
|
||||||
|
partsHave <- gets pPartsHave
|
||||||
|
let partsHave' = Set.insert partName partsHave
|
||||||
|
modify' $ \s -> s { pPartsHave = partsHave' }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
src/sv2v.hs
10
src/sv2v.hs
|
|
@ -13,7 +13,7 @@ import Control.Monad.Except (runExceptT)
|
||||||
import Convert (convert)
|
import Convert (convert)
|
||||||
import Job (readJob, Job(..), Write(..))
|
import Job (readJob, Job(..), Write(..))
|
||||||
import Language.SystemVerilog.AST
|
import Language.SystemVerilog.AST
|
||||||
import Language.SystemVerilog.Parser (initialEnv, parseFiles, Config(..))
|
import Language.SystemVerilog.Parser (parseFiles, Config(..))
|
||||||
|
|
||||||
isInterface :: Description -> Bool
|
isInterface :: Description -> Bool
|
||||||
isInterface (Part _ _ Interface _ _ _ _ ) = True
|
isInterface (Part _ _ Interface _ _ _ _ ) = True
|
||||||
|
|
@ -69,8 +69,9 @@ main = do
|
||||||
job <- readJob
|
job <- readJob
|
||||||
-- parse the input files
|
-- parse the input files
|
||||||
let config = Config
|
let config = Config
|
||||||
{ cfEnv = initialEnv (define job)
|
{ cfDefines = define job
|
||||||
, cfIncludePaths = incdir job
|
, cfIncludePaths = incdir job
|
||||||
|
, cfLibraryPaths = libdir job
|
||||||
, cfSiloed = siloed job
|
, cfSiloed = siloed job
|
||||||
, cfSkipPreprocessor = skipPreprocessor job
|
, cfSkipPreprocessor = skipPreprocessor job
|
||||||
, cfOversizedNumbers = oversizedNumbers job
|
, cfOversizedNumbers = oversizedNumbers job
|
||||||
|
|
@ -80,12 +81,13 @@ main = do
|
||||||
Left msg -> do
|
Left msg -> do
|
||||||
hPutStrLn stderr msg
|
hPutStrLn stderr msg
|
||||||
exitFailure
|
exitFailure
|
||||||
Right asts -> do
|
Right inputs -> do
|
||||||
|
let (inPaths, asts) = unzip inputs
|
||||||
-- convert the files if requested
|
-- convert the files if requested
|
||||||
asts' <- if passThrough job
|
asts' <- if passThrough job
|
||||||
then return asts
|
then return asts
|
||||||
else convert (dumpPrefix job) (exclude job) asts
|
else convert (dumpPrefix job) (exclude job) asts
|
||||||
emptyWarnings (concat asts) (concat asts')
|
emptyWarnings (concat asts) (concat asts')
|
||||||
-- write the converted files out
|
-- write the converted files out
|
||||||
writeOutput (write job) (files job) asts'
|
writeOutput (write job) inPaths asts'
|
||||||
exitSuccess
|
exitSuccess
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ Many of these suites test a particular feature of the sv2v CLI.
|
||||||
* `help` ensures the `--help` output in the README is up to date
|
* `help` ensures the `--help` output in the README is up to date
|
||||||
* `keyword` tests `begin_keywords` version specifiers
|
* `keyword` tests `begin_keywords` version specifiers
|
||||||
* `number` generates and tests short number literals
|
* `number` generates and tests short number literals
|
||||||
|
* `search` tests `-y`/`--libdir`
|
||||||
* `siloed` tests `--siloed` and default compilation unit behavior
|
* `siloed` tests `--siloed` and default compilation unit behavior
|
||||||
* `truncate` tests number literal truncation and `--oversized-numbers`
|
* `truncate` tests number literal truncation and `--oversized-numbers`
|
||||||
* `warning` tests conversion warnings
|
* `warning` tests conversion warnings
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
*.v
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
module apple;
|
||||||
|
initial $display("apple");
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
module surprise;
|
||||||
|
initial $display("This isn't what you're looking for!");
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
interface orange;
|
||||||
|
initial $display("orange");
|
||||||
|
endinterface
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
evaluate() {
|
||||||
|
design_v=$SHUNIT_TMPDIR/search_design.v
|
||||||
|
output_log=$SHUNIT_TMPDIR/search.log
|
||||||
|
touch $output_log
|
||||||
|
simulate /dev/null $output_log top <(echo "$1") /dev/null
|
||||||
|
tail -n1 $output_log
|
||||||
|
}
|
||||||
|
|
||||||
|
search() {
|
||||||
|
top_sv=$SHUNIT_TMPDIR/search_top.sv
|
||||||
|
echo "module top; $mod m(); endmodule" > $top_sv
|
||||||
|
runAndCapture "$@" $top_sv
|
||||||
|
}
|
||||||
|
|
||||||
|
searchAndEvaluate() {
|
||||||
|
search "$@"
|
||||||
|
assertTrue "$mod conversion should succeed" $result
|
||||||
|
assertNotNull "$mod stdout should not be empty" "$stdout"
|
||||||
|
assertNull "$mod stderr should be empty" "$stderr"
|
||||||
|
output=`evaluate "$stdout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFound() {
|
||||||
|
searchAndEvaluate "$@"
|
||||||
|
assertEquals "simulation output should match" $mod "$output"
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNotFound() {
|
||||||
|
searchAndEvaluate "$@"
|
||||||
|
assertContains "iverilog should fail" "$output" "$mod referenced 1 times"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_found() {
|
||||||
|
for mod in apple orange; do
|
||||||
|
checkFound -y.
|
||||||
|
checkFound -y../base -y.
|
||||||
|
checkFound -y. -y../base
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
test_not_found_default() {
|
||||||
|
for mod in apple orange; do
|
||||||
|
checkNotFound
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
test_not_found_missing() {
|
||||||
|
for mod in apple orange doesnt_exist; do
|
||||||
|
checkNotFound -y../base
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
test_misdirect() {
|
||||||
|
mod=misdirect
|
||||||
|
runAndCapture -y. <(echo "module top; $mod m(); endmodule")
|
||||||
|
assertFalse "conversion should not succeed" $result
|
||||||
|
assertNull "stdout should be empty" "$stdout"
|
||||||
|
assertContains "stderr should match expected error" "$stderr" \
|
||||||
|
'Expected to find module or interface "misdirect" in file "./misdirect.sv" selected from the library path.'
|
||||||
|
}
|
||||||
|
|
||||||
|
test_found_write_adjacent() {
|
||||||
|
files=(apple.v orange.v top.v)
|
||||||
|
for file in "${files[@]}"; do
|
||||||
|
assertTrue "$file should not exist" "[ ! -f $file ]"
|
||||||
|
done
|
||||||
|
|
||||||
|
runAndCapture -y. -wadj top.sv
|
||||||
|
assertTrue "conversion should succeed" $result
|
||||||
|
assertNull "stdout should be empty" "$stdout"
|
||||||
|
assertNull "stderr should be empty" "$stderr"
|
||||||
|
|
||||||
|
for file in "${files[@]}"; do
|
||||||
|
assertTrue "$file should exist" "[ -f $file ]"
|
||||||
|
rm -f $file
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
source ../lib/functions.sh
|
||||||
|
|
||||||
|
. shunit2
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
module top;
|
||||||
|
apple a();
|
||||||
|
orange o();
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue