Commit 5aea0ee9 by Zachary Snow

add write adjacent mode

parent 8eb9523d
...@@ -73,6 +73,9 @@ running `stack install`, or copy over the executable manually. ...@@ -73,6 +73,9 @@ running `stack install`, or copy over the executable manually.
## Usage ## Usage
sv2v takes in a list of files and prints the converted Verilog to `stdout`. sv2v takes in a list of files and prints the converted Verilog to `stdout`.
Using `--write=adjacent` will create a converted `.v` for every `.sv` input file
rather than printing to `stdout`.
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
from `stdin`. from `stdin`.
...@@ -92,6 +95,8 @@ Conversion: ...@@ -92,6 +95,8 @@ Conversion:
-E --exclude=CONV Exclude a particular conversion (always, assert, -E --exclude=CONV Exclude a particular conversion (always, assert,
interface, or logic) interface, or logic)
-v --verbose Retain certain conversion artifacts -v --verbose Retain certain conversion artifacts
-w --write=MODE How to write output; default is 'stdout'; use
'adjacent' to create a .v file next to each input
Other: Other:
--help Display help message --help Display help message
--version Print version information --version Print version information
......
...@@ -21,6 +21,14 @@ data Exclude ...@@ -21,6 +21,14 @@ data Exclude
| Succinct | Succinct
deriving (Show, Typeable, Data, Eq) deriving (Show, Typeable, Data, Eq)
data Write
= Stdout
| Adjacent
deriving (Show, Typeable, Data, Eq)
instance Default Write where
def = Stdout
data Job = Job data Job = Job
{ files :: [FilePath] { files :: [FilePath]
, incdir :: [FilePath] , incdir :: [FilePath]
...@@ -29,6 +37,7 @@ data Job = Job ...@@ -29,6 +37,7 @@ data Job = Job
, skipPreprocessor :: Bool , skipPreprocessor :: Bool
, exclude :: [Exclude] , exclude :: [Exclude]
, verbose :: Bool , verbose :: Bool
, write :: Write
} deriving (Show, Typeable, Data) } deriving (Show, Typeable, Data)
defaultJob :: Job defaultJob :: Job
...@@ -47,6 +56,9 @@ defaultJob = Job ...@@ -47,6 +56,9 @@ defaultJob = Job
++ " or logic)") ++ " or logic)")
&= groupname "Conversion" &= groupname "Conversion"
, verbose = nam "verbose" &= help "Retain certain conversion artifacts" , verbose = nam "verbose" &= help "Retain certain conversion artifacts"
, write = nam_ "write" &= name "w" &= typ "MODE"
&= help ("How to write output; default is 'stdout'; use 'adjacent' to"
++ " create a .v file next to each input")
} }
&= program "sv2v" &= program "sv2v"
&= summary ("sv2v " ++ giDescribe $$tGitInfoCwd) &= summary ("sv2v " ++ giDescribe $$tGitInfoCwd)
......
...@@ -4,12 +4,15 @@ ...@@ -4,12 +4,15 @@
- conversion entry point - conversion entry point
-} -}
import System.IO import System.Directory (doesFileExist)
import System.Exit import System.IO (hPrint, hPutStrLn, stderr, stdout)
import System.Exit (exitFailure, exitSuccess)
import Control.Monad (filterM, when, zipWithM_)
import Data.List (elemIndex, intercalate)
import Data.List (elemIndex)
import Job (readJob, files, exclude, incdir, define, siloed, skipPreprocessor)
import Convert (convert) import Convert (convert)
import Job (readJob, Job(..), Write(..))
import Language.SystemVerilog.AST import Language.SystemVerilog.AST
import Language.SystemVerilog.Parser (parseFiles) import Language.SystemVerilog.Parser (parseFiles)
...@@ -32,15 +35,39 @@ emptyWarnings before after = ...@@ -32,15 +35,39 @@ emptyWarnings before after =
if all null before || any (not . null) after then if all null before || any (not . null) after then
return () return ()
else if any (any isInterface) before then else if any (any isInterface) before then
hPutStr stderr $ "Warning: Source includes an interface but output is " hPutStrLn stderr $ "Warning: Source includes an interface but output is"
++ "empty because there is no top-level module which has no ports " ++ " empty because there is no top-level module which has no ports"
++ "which are interfaces." ++ " which are interfaces."
else if any (any isPackage) before then else if any (any isPackage) before then
hPutStr stderr $ "Warning: Source includes packages but no modules. " hPutStrLn stderr $ "Warning: Source includes packages but no modules."
++ "Please convert packages alongside the modules that use them." ++ " Please convert packages alongside the modules that use them."
else else
return () return ()
rewritePath :: FilePath -> IO FilePath
rewritePath path = do
when (end /= ext) $ do
hPutStrLn stderr $ "Refusing to write adjacent to " ++ show path
++ " because that path does not end in " ++ show ext
exitFailure
return $ base ++ ".v"
where
ext = ".sv"
(base, end) = splitAt (length path - length ext) path
writeOutput :: Write -> [FilePath] -> [AST] -> IO ()
writeOutput Stdout _ asts =
hPrint stdout $ concat asts
writeOutput Adjacent inPaths asts = do
outPaths <- mapM rewritePath inPaths
badPaths <- filterM doesFileExist outPaths
when (not $ null badPaths) $ do
hPutStrLn stderr $ "Refusing to write output because the following"
++ " files would be overwritten: " ++ intercalate ", " badPaths
exitFailure
let results = map (++ "\n") $ map show asts
zipWithM_ writeFile outPaths results
main :: IO () main :: IO ()
main = do main = do
job <- readJob job <- readJob
...@@ -50,12 +77,12 @@ main = do ...@@ -50,12 +77,12 @@ main = do
(skipPreprocessor job) (files job) (skipPreprocessor job) (files job)
case result of case result of
Left msg -> do Left msg -> do
hPutStr stderr $ msg ++ "\n" hPutStrLn stderr msg
exitFailure exitFailure
Right asts -> do Right asts -> do
-- convert the files -- convert the files
let asts' = convert (exclude job) asts let asts' = convert (exclude job) asts
emptyWarnings asts asts' emptyWarnings asts asts'
-- print the converted files out -- write the converted files out
hPrint stdout $ concat asts' writeOutput (write job) (files job) asts'
exitSuccess exitSuccess
#!/bin/bash #!/bin/bash
validateOutput() { runErrorTest() {
stdout_len=`wc -l < $SHUNIT_TMPDIR/stdout` runAndCapture $1.sv
assertEquals "stdout should be empty" 0 $stdout_len assertFalse "conversion should have failed" $result
stderr=`cat $SHUNIT_TMPDIR/stderr` assertNull "stdout should be empty" "$stdout"
assertNotNull "stderr should not be empty" "$stderr" assertNotNull "stderr should not be empty" "$stderr"
line=`head -n1 $1` line=`head -n1 $1.sv`
if [[ "$line" =~ \/\/\ pattern:\ .* ]]; then if [[ "$line" =~ \/\/\ pattern:\ .* ]]; then
pattern=${line:12} pattern=${line:12}
if [[ ! "$stderr" =~ $pattern ]]; then if [[ ! "$stderr" =~ $pattern ]]; then
...@@ -16,11 +16,7 @@ validateOutput() { ...@@ -16,11 +16,7 @@ validateOutput() {
addTest() { addTest() {
test=$1 test=$1
eval "test_$test() { \ eval "test_$test() { runErrorTest $test; }"
$SV2V $test.sv 2> $SHUNIT_TMPDIR/stderr > $SHUNIT_TMPDIR/stdout; \
assertFalse \"conversion should have failed\" \$?; \
validateOutput $test.sv; \
}"
suite_addTest test_$test suite_addTest test_$test
} }
......
...@@ -131,3 +131,10 @@ runTest() { ...@@ -131,3 +131,10 @@ runTest() {
test=$1 test=$1
simpleTest "${test}.sv" "${test}.v" "${test}_tb.v" simpleTest "${test}.sv" "${test}.v" "${test}_tb.v"
} }
runAndCapture() {
$SV2V "$@" > "$SHUNIT_TMPDIR/stdout" 2> "$SHUNIT_TMPDIR/stderr"
result=$?
stdout=`cat $SHUNIT_TMPDIR/stdout`
stderr=`cat $SHUNIT_TMPDIR/stderr`
}
module one;
logic x;
endmodule
#!/bin/bash
clearArtifacts() {
rm -f one.v two.v
}
createArtifacts() {
touch one.v two.v
}
test_prereq() {
for file in `ls *.sv`; do
assertConverts "$file"
done
}
test_default() {
runAndCapture *.sv
assertTrue "default conversion should succeed" $result
assertNotNull "stdout should not be empty" "$stdout"
assertNull "stderr should be empty" "$stderr"
}
test_stdout() {
runAndCapture --write=stdout *.sv
assertTrue "default conversion should succeed" $result
assertNotNull "stdout should not be empty" "$stdout"
assertNull "stderr should be empty" "$stderr"
}
test_adjacent() {
runAndCapture --write=stdout *.sv
expected="$stdout"
runAndCapture --write=adjacent *.sv
assertTrue "adjacent conversion should succeed" $result
assertNull "stdout should be empty" "$stdout"
assertNull "stderr should be empty" "$stderr"
actual=`cat one.v two.v`
assertEquals "adjacent output should match combined" "$expected" "$actual"
clearArtifacts
}
test_adjacent_exist() {
createArtifacts
runAndCapture --write=adjacent *.sv
clearArtifacts
assertFalse "adjacent conversion should fail" $result
assertNull "stdout should be empty" "$stdout"
assertEquals "stderr should list existing files" \
"Refusing to write output because the following files would be overwritten: one.v, two.v" \
"$stderr"
}
test_adjacent_extension() {
createArtifacts
runAndCapture --write=adjacent *.v
clearArtifacts
assertFalse "adjacent conversion should fail" $result
assertNull "stdout should be empty" "$stdout"
assertEquals "stderr should list existing files" \
"Refusing to write adjacent to \"one.v\" because that path does not end in \".sv\"" \
"$stderr"
}
source ../lib/functions.sh
. shunit2
module two;
logic x;
endmodule
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment