diff --git a/CHANGELOG.md b/CHANGELOG.md index a791401..6e7252a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ ## Unreleased +* Explicitly-sized number literals with non-zero bits exceeding the given width + (e.g., `1'b11`, `3'sd8`, `2'o7`) are truncated and produce a warning, rather + than yielding a cryptic error +* Unsized number literals exceeding the maximum width of 32 bits (e.g., + `'h1_ffff_ffff`, `4294967296`) are truncated and produce a warning, rather + than being silently extended + * Support for unsized number literals exceeding the standard-imposed 32-bit + limit can be re-enabled with `--oversized-numbers` +* Number literals with leading zeroes which extend beyond the width of the + literal (e.g., `1'b01`, `'h0_FFFF_FFFF`) now produce a warning * Non-positive integer size casts are now detected and forbidden * Negative indices in struct pattern literals are now detected and forbidden diff --git a/README.md b/README.md index b7a1bf5..88038bd 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,8 @@ Conversion: 'adjacent' to create a .v file next to each input; use a path ending in .v to write to a file Other: + --oversized-numbers Disable standard-imposed 32-bit limit on unsized + number literals (e.g., 'h1_ffff_ffff, 4294967296) --help Display help message --version Print version information --numeric-version Print just the version number diff --git a/src/Job.hs b/src/Job.hs index 21af0e9..50e05c0 100644 --- a/src/Job.hs +++ b/src/Job.hs @@ -43,6 +43,7 @@ data Job = Job , verbose :: Bool , write :: Write , writeRaw :: String + , oversizedNumbers :: Bool } deriving (Typeable, Data) version :: String @@ -71,13 +72,17 @@ defaultJob = Job &= help ("How to write output; default is 'stdout'; use 'adjacent' to" ++ " create a .v file next to each input; use a path ending in .v" ++ " to write to a file") + , oversizedNumbers = nam_ "oversized-numbers" + &= help ("Disable standard-imposed 32-bit limit on unsized number" + ++ " literals (e.g., 'h1_ffff_ffff, 4294967296)") + &= groupname "Other" } &= program "sv2v" &= summary ("sv2v " ++ version) &= details [ "sv2v converts SystemVerilog to Verilog." , "More info: https://github.com/zachjs/sv2v" , "(C) 2019-2021 Zachary Snow, 2011-2015 Tom Hawkins" ] - &= helpArg [explicit, name "help", groupname "Other"] + &= helpArg [explicit, name "help"] &= versionArg [explicit, name "version"] &= verbosityArgs [ignore] [ignore] where diff --git a/src/Language/SystemVerilog/AST/Number.hs b/src/Language/SystemVerilog/AST/Number.hs index 6b4748e..51904a6 100644 --- a/src/Language/SystemVerilog/AST/Number.hs +++ b/src/Language/SystemVerilog/AST/Number.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE TupleSections #-} {- sv2v - Author: Zachary Snow - @@ -22,36 +23,127 @@ import Data.Char (digitToInt, intToDigit, toLower) import Data.List (elemIndex) import Text.Read (readMaybe) +{-# NOINLINE parseNumber #-} +parseNumber :: Bool -> String -> (Number, String) +parseNumber oversizedNumbers = + (parseNormalized oversizedNumbers) . normalizeNumber + -- normalize the number first, making everything lowercase and removing -- visual niceties like spaces and underscores -parseNumber :: String -> Number -parseNumber = parseNumber' . map toLower . filter (not . isPad) +normalizeNumber :: String -> String +normalizeNumber = map toLower . filter (not . isPad) where isPad = flip elem "_ \n\t" -parseNumber' :: String -> Number -parseNumber' "'0" = UnbasedUnsized Bit0 -parseNumber' "'1" = UnbasedUnsized Bit1 -parseNumber' "'x" = UnbasedUnsized BitX -parseNumber' "'z" = UnbasedUnsized BitZ -parseNumber' str = +-- truncate the given decimal number literal, if necessary +validateDecimal :: Bool -> String -> Int -> Bool -> Integer -> (Number, String) +validateDecimal oversizedNumbers str sz sg v + | sz < -32 && oversizedNumbers = + valid $ Decimal sz sg v + | sz < -32 = + addTruncateMessage str $ + if sg && v' > widthMask 31 + -- avoid zero-pad on signed decimals + then Based (-32) sg Hex v' 0 + else Decimal (-32) sg v' + | sz > 0 && v > widthMask sz = + addTruncateMessage str $ + Decimal sz sg v' + | otherwise = + valid $ Decimal sz sg v + where + v' = v .&. widthMask truncWidth + truncWidth = if sz < -32 then -32 else sz + +-- produce a warning message describing the applied truncation +addTruncateMessage :: String -> Number -> (Number, String) +addTruncateMessage orig trunc = (trunc, msg) + where + width = show $ numberBitLength trunc + bit = if width == "1" then "bit" else "bits" + msg = "Number literal " ++ orig ++ " exceeds " ++ width ++ " " ++ bit + ++ "; truncating to " ++ show trunc ++ "." + +-- when extending a wide unsized number, we use the bit width if the digits +-- cover exactly that many bits, and add an extra 0 padding bit otherwise +extendUnsizedBased :: Int -> Number -> Number +extendUnsizedBased sizeByDigits n + | size > -32 || sizeByBits < 32 = n + | sizeByBits == sizeByDigits = useWidth sizeByBits + | otherwise = useWidth $ sizeByBits + 1 + where + Based size sg base vals knds = n + sizeByBits = fromIntegral $ max (bits vals) (bits knds) + useWidth sz = Based (negate sz) sg base vals knds + +-- truncate the given based number literal, if necessary +validateBased :: String -> Int -> Int -> Number -> (Number, String) +validateBased orig sizeIfPadded sizeByDigits n + -- more digits than the size would allow for, regardless of their values + | sizeIfPadded < sizeByDigits = truncated + -- unsized literal with fewer than 32 bits + | 0 > size && size > -32 = validated + -- no padding bits are present + | abs size >= sizeIfPadded = validated + -- check the padding bits in the leading digit, if there are any + | all (isLegalPad sizethBit) paddingBits = validated + -- some of the padding bits aren't legal + | otherwise = truncated + where + Based size sg base vals knds = n + n' = Based size sg base' vals' knds' + + validated = valid n + truncated = addTruncateMessage orig n' + + -- checking padding bits + sizethBit = getBit $ abs size - 1 + paddingBits = map getBit [abs size..sizeIfPadded - 1] + getBit = getVKBit vals knds + + -- truncated the number, and selected a valid new base + vals' = vals .&. widthMask size + knds' = knds .&. widthMask size + base' = if size == -32 && baseSelect == Octal + then Binary + else baseSelect + baseSelect = selectBase base vals' knds' + +-- if the MSB post-truncation is X or Z, then any padding bits must match; if +-- the MSB post-truncation is 0 or 1, then non-zero padding bits are forbidden +isLegalPad :: Bit -> Bit -> Bool +isLegalPad Bit1 = (== Bit0) +isLegalPad bit = (== bit) + +parseNormalized :: Bool -> String -> (Number, String) +parseNormalized _ "'0" = valid $ UnbasedUnsized Bit0 +parseNormalized _ "'1" = valid $ UnbasedUnsized Bit1 +parseNormalized _ "'x" = valid $ UnbasedUnsized BitX +parseNormalized _ "'z" = valid $ UnbasedUnsized BitZ +parseNormalized oversizedNumbers str = -- simple decimal number if maybeIdx == Nothing then - let n = readDecimal str - in Decimal (negate $ decimalSize True n) True n + let num = readDecimal str + sz = negate (decimalSize True num) + in decimal sz True num -- non-decimal based integral number else if maybeBase /= Nothing then let (values, kinds) = parseBasedDigits (baseSize base) digitsExtended - in Based size signed base values kinds + number = Based size signed base values kinds + sizeIfPadded = sizeDigits * bitsPerDigit + sizeByDigits = length digitsExtended * bitsPerDigit + in if oversizedNumbers && size < 0 + then valid $ extendUnsizedBased sizeByDigits number + else validateBased str sizeIfPadded sizeByDigits number -- decimal X or Z literal - else if numDigits == 1 && elem leadDigit xzDigits then - let (vals, knds) = parseBasedDigits 2 $ replicate (abs size) leadDigit - in Based size signed Binary vals knds + else if numDigits == 1 && leadDigitIsXZ then + let vals = if elem leadDigit zDigits then widthMask size else 0 + knds = widthMask size + in valid $ Based size signed Binary vals knds -- explicitly-based decimal number else let num = readDecimal digits - in if rawSize == 0 - then Decimal (negate $ decimalSize signed num) signed num - else Decimal size signed num + sz = if rawSize == 0 then negate (decimalSize signed num) else size + in decimal sz signed num where -- pull out the components of the literals maybeIdx = elemIndex '\'' str @@ -63,8 +155,9 @@ parseNumber' str = -- high-order X or Z is extended up to the size of the literal leadDigit = head digits numDigits = length digits + leadDigitIsXZ = elem leadDigit xzDigits digitsExtended = - if elem leadDigit xzDigits + if leadDigitIsXZ then replicate (sizeDigits - numDigits) leadDigit ++ digits else digits @@ -84,12 +177,24 @@ parseNumber' str = size = if rawSize /= 0 then rawSize - else if maybeBase /= Nothing then - negate $ bitsPerDigit * numDigits - else + else if maybeBase == Nothing || leadDigitIsXZ then -32 + else + negate $ min 32 $ bitsPerDigit * numDigits bitsPerDigit = bits $ baseSize base - 1 + -- shortcut for decimal outputs + decimal :: Int -> Bool -> Integer -> (Number, String) + decimal = validateDecimal oversizedNumbers str + +-- shorthand denoting a valid number literal +valid :: Number -> (Number, String) +valid = (, "") + +-- mask with the lowest N bits set +widthMask :: Int -> Integer +widthMask pow = 2 ^ (abs pow) - 1 + -- read a simple unsigned decimal number readDecimal :: Read a => String -> a readDecimal str = @@ -171,6 +276,16 @@ bitToVK Bit1 = (1, 0) bitToVK BitX = (0, 1) bitToVK BitZ = (1, 1) +-- get the logical bit value at the given index in a (values, kinds) pair +getVKBit :: Integer -> Integer -> Int -> Bit +getVKBit v k i = + case (v .&. select, k .&. select) of + (0, 0) -> Bit0 + (_, 0) -> Bit1 + (0, _) -> BitX + (_, _) -> BitZ + where select = 2 ^ i + data Base = Binary | Octal diff --git a/src/Language/SystemVerilog/Parser.hs b/src/Language/SystemVerilog/Parser.hs index 7ba0091..6d608bb 100644 --- a/src/Language/SystemVerilog/Parser.hs +++ b/src/Language/SystemVerilog/Parser.hs @@ -22,6 +22,7 @@ data Config = Config , cfIncludePaths :: [FilePath] , cfSiloed :: Bool , cfSkipPreprocessor :: Bool + , cfOversizedNumbers :: Bool } -- parse CLI macro definitions into the internal macro environment format @@ -48,7 +49,7 @@ parseFile :: Config -> FilePath -> ExceptT String IO (Config, AST) parseFile config path = do (config', contents) <- preprocessFile config path tokens <- liftEither $ runExcept $ lexStr contents - ast <- parse tokens + ast <- parse (cfOversizedNumbers config) tokens return (config', ast) -- preprocess an individual file, potentially updating the configuration diff --git a/src/Language/SystemVerilog/Parser/Parse.y b/src/Language/SystemVerilog/Parser/Parse.y index ea2e80c..d2c028b 100644 --- a/src/Language/SystemVerilog/Parser/Parse.y +++ b/src/Language/SystemVerilog/Parser/Parse.y @@ -19,6 +19,7 @@ module Language.SystemVerilog.Parser.Parse (parse) where import Control.Monad.Except import Control.Monad.State.Strict import Data.Maybe (catMaybes, fromMaybe) +import System.IO (hPutStrLn, stderr) import Language.SystemVerilog.AST import Language.SystemVerilog.Parser.ParseDecl import Language.SystemVerilog.Parser.Tokens @@ -1206,7 +1207,7 @@ Real :: { String } : real { tokenString $1 } Number :: { Number } - : number { parseNumber $ tokenString $1 } + : number {% readNumber (tokenPosition $1) (tokenString $1) } String :: { String } : string { tail $ init $ tokenString $1 } @@ -1463,25 +1464,26 @@ join : "join" {} | error {% missingToken "join" } data ParseData = ParseData { pPosition :: Position , pTokens :: [Token] + , pOversizedNumbers :: Bool } type ParseState = StateT ParseData (ExceptT String IO) -parse :: [Token] -> ExceptT String IO AST -parse [] = return [] -parse tokens = +parse :: Bool -> [Token] -> ExceptT String IO AST +parse _ [] = return [] +parse oversizedNumbers tokens = evalStateT parseMain initialState where position = tokenPosition $ head tokens - initialState = ParseData position tokens + initialState = ParseData position tokens oversizedNumbers positionKeep :: (Token -> ParseState a) -> ParseState a positionKeep cont = do - tokens <- gets pTokens + ParseData _ tokens oversizedNumbers <- get case tokens of [] -> cont TokenEOF tok : toks -> do - put $ ParseData (tokenPosition tok) toks + put $ ParseData (tokenPosition tok) toks oversizedNumbers cont tok parseErrorTok :: Token -> ParseState a @@ -1672,4 +1674,12 @@ splitInit decl = (Variable d t ident a Nil, Just (LHSIdent ident, e)) where Variable d t ident a e = decl +readNumber :: Position -> String -> ParseState Number +readNumber pos str = do + oversizedNumbers <- gets pOversizedNumbers + let (num, msg) = parseNumber oversizedNumbers str + when (not $ null msg) $ lift $ lift $ + hPutStrLn stderr $ show pos ++ ": Warning: " ++ msg + return num + } diff --git a/src/sv2v.hs b/src/sv2v.hs index b231509..427e992 100644 --- a/src/sv2v.hs +++ b/src/sv2v.hs @@ -80,6 +80,7 @@ main = do , cfIncludePaths = incdir job , cfSiloed = siloed job , cfSkipPreprocessor = skipPreprocessor job + , cfOversizedNumbers = oversizedNumbers job } result <- runExceptT $ parseFiles config (files job) case result of diff --git a/test/core/duplicate_genvar_shadow.sv b/test/basic/duplicate_genvar_shadow.sv similarity index 100% rename from test/core/duplicate_genvar_shadow.sv rename to test/basic/duplicate_genvar_shadow.sv diff --git a/test/basic/shift.sv b/test/basic/shift.sv index 53b891d..f747719 100644 --- a/test/basic/shift.sv +++ b/test/basic/shift.sv @@ -26,25 +26,5 @@ module top; `TEST(2, 1); `TEST(2, 2); `TEST(2, 3); - - `TEST(-8589934592, 0); - `TEST(-8589934592, 1); - `TEST(-8589934592, 2); - `TEST(-8589934592, 3); - - `TEST(-8589934593, 0); - `TEST(-8589934593, 1); - `TEST(-8589934593, 2); - `TEST(-8589934593, 3); - - `TEST(8589934592, 0); - `TEST(8589934592, 1); - `TEST(8589934592, 2); - `TEST(8589934592, 3); - - `TEST(8589934593, 0); - `TEST(8589934593, 1); - `TEST(8589934593, 2); - `TEST(8589934593, 3); end endmodule diff --git a/test/core/cast.sv b/test/core/cast.sv index 68c98cd..eff9a0f 100644 --- a/test/core/cast.sv +++ b/test/core/cast.sv @@ -29,10 +29,6 @@ module top; $display("%b", 32'(4)); $display("%b", 33'(4)); $display("%b", 33'(64'hFFFF_FFFF_FFFF_FFFF)); - $display("%b", 32'(4294967296)); - $display("%b", 33'(4294967296)); - $display("%b", 32'(4294967297)); - $display("%b", 33'(4294967297)); end localparam bit foo = '0; diff --git a/test/core/cast.v b/test/core/cast.v index f79fdf2..1b0c64b 100644 --- a/test/core/cast.v +++ b/test/core/cast.v @@ -32,10 +32,6 @@ module top; $display("%b", 32'd4); $display("%b", 33'd4); $display("%b", 33'h1_FFFF_FFFF); - $display("%b", 32'd0); - $display("%b", 33'd4294967296); - $display("%b", 32'd1); - $display("%b", 33'd4294967297); end localparam [0:0] foo = 0; diff --git a/test/core/duplicate_genvar_shadow.v b/test/core/duplicate_genvar_shadow.v deleted file mode 100644 index 780b2a6..0000000 --- a/test/core/duplicate_genvar_shadow.v +++ /dev/null @@ -1,9 +0,0 @@ -module top; - initial begin - $display("A %0d", 32); - $display("B %0d", 10); - $display("C %0d", 32); - $display("D %0d", 6); - $display("E %0d", 32); - end -endmodule diff --git a/test/core/sign_cast.sv b/test/core/sign_cast.sv index cf4c373..72f2f7c 100644 --- a/test/core/sign_cast.sv +++ b/test/core/sign_cast.sv @@ -1,7 +1,9 @@ module top; initial begin - $display(signed'(4294967295)); - $display(unsigned'(4294967295)); + $display(signed'(1'b1)); + $display(unsigned'(1'sb1)); + $display(signed'(1)); + $display(unsigned'(1)); $display(signed'(-1)); $display(unsigned'(-1)); end diff --git a/test/core/sign_cast.v b/test/core/sign_cast.v index 8a64b7a..78aaa05 100644 --- a/test/core/sign_cast.v +++ b/test/core/sign_cast.v @@ -1,7 +1,9 @@ module top; initial begin - $display($signed(4294967295)); - $display($unsigned(4294967295)); + $display($signed(1'b1)); + $display($unsigned(1'sb1)); + $display($signed(1)); + $display($unsigned(1)); $display($signed(-1)); $display($unsigned(-1)); end diff --git a/test/core/struct_array_tb.v b/test/core/struct_array_tb.v index 747b80d..06fddbc 100644 --- a/test/core/struct_array_tb.v +++ b/test/core/struct_array_tb.v @@ -10,7 +10,7 @@ module top; initial begin $monitor("%d: %01b %04b %02b", select, a, b, c); - in = 'b01111011011011101111100111110111001010001011100110101000; + in = 56'b01111011011011101111100111110111001010001011100110101000; select = 0; #1; select = 1; #1; select = 2; #1; diff --git a/test/lex/number.sv b/test/lex/number.sv index 6b900a4..1cff24b 100644 --- a/test/lex/number.sv +++ b/test/lex/number.sv @@ -15,13 +15,8 @@ module top; `TEST(1'SOZ) `TEST(2'SOZ) `TEST(3'SOZ) `TEST(7'SOZ) `TEST(8'SOZ) `TEST(9'SOZ) `TEST(9'SOZZ) `TEST(10'SOZZ) `TEST(1234_5678) `TEST('h1234_5678) `TEST('o1234_5677) `TEST('b0101_1100) - `TEST('d4294967295) `TEST('d4294967296) `TEST('d4294967297) `TEST('d4294967298) `TEST('d4294967299) - `TEST('d004294967295) `TEST('d004294967296) `TEST('d004294967297) `TEST('d004294967298) `TEST('d004294967299) - - `TEST(4294967295) `TEST(4294967296) `TEST(4294967297) `TEST(4294967298) `TEST(4294967299) - `TEST(-4294967295) `TEST(-4294967297) `TEST(-4294967298) `TEST(-4294967299) - `TEST(-8589934593) `TEST(8589934592) `TEST(8589934593) - // iverlog does weird things with these: `TEST(-4294967296) `TEST(-8589934592) + `TEST('d4294967294) `TEST('d4294967295) + `TEST('d004294967294) `TEST('d004294967295) `TEST(659) `TEST('h 837FF) `TEST('o7460) `TEST(4'b1001) `TEST(5 'D 3) `TEST(3'b01x) `TEST(12'hx) `TEST(16'hz) @@ -31,10 +26,9 @@ module top; `TEST(3'bx) `TEST(3'b1x) `TEST(3'bx1) `TEST('b1x) `TEST('bx1) `TEST(3'b0x1) `TEST(3'b0z1) `TEST('hf & 10'hf) `TEST(7'hf & 10'hf) - `TEST('b01xz01xz01xz01xz01xz01xz01xz01xz01xz) `TEST('b101xz01xz01xz01xz01xz01xz01xz01xz01xz) `TEST(36'b01xz01xz01xz01xz01xz01xz01xz01xz01xz) `TEST(37'b01xz01xz01xz01xz01xz01xz01xz01xz01xz) `TEST(36'sb01xz01xz01xz01xz01xz01xz01xz01xz01xz) `TEST(37'sb01xz01xz01xz01xz01xz01xz01xz01xz01xz) - `TEST('h01xz01xz) `TEST('h101xz01xz) + `TEST('h01xz01xz) `TEST(36'h01xz01xz) `TEST(37'h01xz01xz) `TEST(36'hb01xz01xz) `TEST(37'hb01xz01xz) diff --git a/test/lib/functions.sh b/test/lib/functions.sh index 0b4c9cd..ef5458b 100644 --- a/test/lib/functions.sh +++ b/test/lib/functions.sh @@ -24,12 +24,20 @@ simulate() { -Wno-portbind \ -o $sim_prog \ -g2005 \ + -gstrict-expr-width \ -DTEST_VCD="\"$sim_vcd_tmp\"" \ -DTEST_TOP=$sim_top \ + "$@" \ $SCRIPT_DIR/tb_dumper.v \ - "$@" 2>&1` - assertTrue "iverilog on $1 failed" $? - assertNull "iverilog emitted warnings:\n$iv_output" "$iv_output" + 2>&1` + if [ $? -ne 0 ]; then + fail "iverilog on $1 failed:\n$iv_output" + return + elif [ "$EXPECT_IVERILOG_WARNINGS" != "0" ]; then + assertNull "iverilog on $1 emitted warnings:\n$iv_output" "$iv_output" + else + assertNotNull "iverilog on $1 did not emit any warnings" "$iv_output" + fi # run the simulation $sim_prog > $sim_log assertTrue "simulating $1 failed" $? diff --git a/test/truncate/no_trunc.txt b/test/truncate/no_trunc.txt new file mode 100644 index 0000000..11886c7 --- /dev/null +++ b/test/truncate/no_trunc.txt @@ -0,0 +1,54 @@ +'dX +'dZ +'sdX +'sdZ + +'b0 +'b1 +'bx +'bz + +'sb0 +'sb1 +'sbx +'sbz + +1'b0 +1'b1 +1'bx +1'bz + +1'sb0 +1'sb1 +1'sbx +1'sbz + +1'hX +1'hZ +2'hX +2'hZ +3'hX +3'hZ +4'hX +4'hZ +5'hX +5'hZ + +1'oX +1'oZ +2'oX +2'oZ +3'oX +3'oZ +4'oX +4'oZ + +1'sd1 +2'sd2 +2'sd3 +32'sd4294967294 + +'o1xz01xz01xz +'o2xz01xz01xz +32'o1xz01xz01xz +32'o2xz01xz01xz diff --git a/test/truncate/run.sh b/test/truncate/run.sh new file mode 100755 index 0000000..362f050 --- /dev/null +++ b/test/truncate/run.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +testNumber() { + mode=$1 + number=$2 + + ve=$SHUNIT_TMPDIR/ve.v + cs=$SHUNIT_TMPDIR/cs.v + + ve_log=$ve.log + cs_log=$cs.log + + # substitute the current number literal into a copy of the test template + sed -e "s/NUM/$number/" < template.v > $ve + + # convert in strict mode + runAndCapture $ve + assertTrue "conversion should succeed" $result + assertNotNull "stdout should not be empty" "$stdout" + if [ $mode = no_trunc ]; then + assertNull "stderr should be empty:\n$stderr" "$stderr" + else + assertNotNull "stderr should not be empty" "$stderr" + fi + + # convert result in strict mode + mv -f $SHUNIT_TMPDIR/stdout $cs + runAndCapture $cs + assertTrue "conversion should succeed" $result + assertNotNull "stdout should not be empty" "$stdout" + assertNull "stderr should be empty:\n$stderr" "$stderr" + + # simulate and compare in strict mode + EXPECT_IVERILOG_WARNINGS=`[ $mode = trunc_ivl_warns ]; echo $?` \ + simulate /dev/null $ve_log top $ve + simulate /dev/null $cs_log top $cs + output=`diff $ve_log $cs_log` + assertTrue "number literals differ:\n$output" $? + + # convert in lax mode + runAndCapture --oversized-numbers $ve + assertTrue "conversion should succeed" $result + assertNotNull "stdout should not be empty" "$stdout" + if [ $mode = no_trunc ] || [[ ! "$number" =~ .\' ]]; then + assertNull "stderr should be empty:\n$stderr" "$stderr" + else + assertNotNull "stderr should not be empty" "$stderr" + fi + + # convert result in lax mode + mv -f $SHUNIT_TMPDIR/stdout $cs + runAndCapture --oversized-numbers $cs + assertTrue "conversion should succeed" $result + assertNotNull "stdout should not be empty" "$stdout" + assertNull "stderr should be empty:\n$stderr" "$stderr" + + # simulate and compare in lax mode + EXPECT_IVERILOG_WARNINGS=`[[ "$number" =~ .\' ]] && [ $mode = trunc_ivl_warns ]; echo $?` \ + simulate /dev/null $ve_log top -gno-strict-expr-width $ve + simulate /dev/null $cs_log top -gno-strict-expr-width $cs + output=`diff $ve_log $cs_log` + assertTrue "number literals differ:\n$output" $? +} + +addTest() { + mode=$1 + number=$2 + test="${mode}_${number//\'/_}" + eval "$test() { testNumber $mode \"$number\"; }" + suite_addTest $test +} + +suite() { + modes=(no_trunc trunc_ivl_warns trunc_ivl_silent) + for mode in ${modes[@]}; do + while read number; do + [ -n "$number" ] && \ + addTest $mode "$number" + done < $mode.txt + done +} + +source ../lib/functions.sh + +. shunit2 diff --git a/test/truncate/template.v b/test/truncate/template.v new file mode 100644 index 0000000..15c6708 --- /dev/null +++ b/test/truncate/template.v @@ -0,0 +1,5 @@ +module top; + localparam X = NUM; + localparam [63:0] Y = X; + initial $display("%0d %b %b", $bits(X), X, Y); +endmodule diff --git a/test/truncate/trunc_ivl_silent.txt b/test/truncate/trunc_ivl_silent.txt new file mode 100644 index 0000000..6181077 --- /dev/null +++ b/test/truncate/trunc_ivl_silent.txt @@ -0,0 +1,8 @@ +'h0_FFFF_FFFF +'o00xz01xz01xz +'o01xz01xz01xz +'bz01xz01xz01xz01xz01xz01xz01xz01xz +'hZ_ZZZZ_ZZZZ +'hz01xz01xz +4294967294 +4294967295 diff --git a/test/truncate/trunc_ivl_warns.txt b/test/truncate/trunc_ivl_warns.txt new file mode 100644 index 0000000..ac0ca67 --- /dev/null +++ b/test/truncate/trunc_ivl_warns.txt @@ -0,0 +1,59 @@ +1'b01 +1'b001 +1'bxx +1'b0x + +1'd2 +1'sd2 +1'd3 +1'sd3 +3'sd8 + +'b101xz01xz01xz01xz01xz01xz01xz01xz +'bxz01xz01xz01xz01xz01xz01xz01xz01xz +'b1xz01xz01xz01xz01xz01xz01xz01xz01xz + +32'bz01xz01xz01xz01xz01xz01xz01xz01xz +32'bxz01xz01xz01xz01xz01xz01xz01xz01xz +32'b1xz01xz01xz01xz01xz01xz01xz01xz01xz + +2'o4 +2'o7 + +'h1_FFFF_FFFF +'hF_FFFF_FFFF + +'o4xz01xz01xz +'oz1xz01xz01xz + +'h101xz01xz +'hxz01xz01xz +'h0xz01xz01xz + +'d4294967296 +'d4294967297 +'d04294967296 +'d04294967297 + +'d8589934590 +'d8589934591 +'d8589934592 +'d8589934593 + +4294967296 +4294967297 + +8589934590 +8589934591 +8589934592 +8589934593 + +1'd111 +1'b111 +1'o111 +1'h111 + +2'd111 +2'b111 +2'o111 +2'h111