Commit 306d7133 by Zachary Snow

refactor sizing and truncation of integer literals

- use iverilog's -gstrict-expr-width throughout test suite
- add warnings for excess bits or padding zeroes in number literals
- add new --oversized-numbers parameter to retain support for unsized
  numbers wider than 32 bits
- localized use of oversized numbers to new truncate test suite which
  verifies the behavior of both modes, and compares against the known
  behavior of iverilog
parent 5e5ddca4
## Unreleased ## 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 * Non-positive integer size casts are now detected and forbidden
* Negative indices in struct pattern literals are now detected and forbidden * Negative indices in struct pattern literals are now detected and forbidden
......
...@@ -101,6 +101,8 @@ Conversion: ...@@ -101,6 +101,8 @@ Conversion:
'adjacent' to create a .v file next to each input; 'adjacent' to create a .v file next to each input;
use a path ending in .v to write to a file use a path ending in .v to write to a file
Other: Other:
--oversized-numbers Disable standard-imposed 32-bit limit on unsized
number literals (e.g., 'h1_ffff_ffff, 4294967296)
--help Display help message --help Display help message
--version Print version information --version Print version information
--numeric-version Print just the version number --numeric-version Print just the version number
......
...@@ -43,6 +43,7 @@ data Job = Job ...@@ -43,6 +43,7 @@ data Job = Job
, verbose :: Bool , verbose :: Bool
, write :: Write , write :: Write
, writeRaw :: String , writeRaw :: String
, oversizedNumbers :: Bool
} deriving (Typeable, Data) } deriving (Typeable, Data)
version :: String version :: String
...@@ -71,13 +72,17 @@ defaultJob = Job ...@@ -71,13 +72,17 @@ defaultJob = Job
&= help ("How to write output; default is 'stdout'; use 'adjacent' to" &= 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" ++ " create a .v file next to each input; use a path ending in .v"
++ " to write to a file") ++ " 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" &= program "sv2v"
&= summary ("sv2v " ++ version) &= summary ("sv2v " ++ version)
&= details [ "sv2v converts SystemVerilog to Verilog." &= details [ "sv2v converts SystemVerilog to Verilog."
, "More info: https://github.com/zachjs/sv2v" , "More info: https://github.com/zachjs/sv2v"
, "(C) 2019-2021 Zachary Snow, 2011-2015 Tom Hawkins" ] , "(C) 2019-2021 Zachary Snow, 2011-2015 Tom Hawkins" ]
&= helpArg [explicit, name "help", groupname "Other"] &= helpArg [explicit, name "help"]
&= versionArg [explicit, name "version"] &= versionArg [explicit, name "version"]
&= verbosityArgs [ignore] [ignore] &= verbosityArgs [ignore] [ignore]
where where
......
{-# LANGUAGE TupleSections #-}
{- sv2v {- sv2v
- Author: Zachary Snow <zach@zachjs.com> - Author: Zachary Snow <zach@zachjs.com>
- -
...@@ -22,36 +23,127 @@ import Data.Char (digitToInt, intToDigit, toLower) ...@@ -22,36 +23,127 @@ import Data.Char (digitToInt, intToDigit, toLower)
import Data.List (elemIndex) import Data.List (elemIndex)
import Text.Read (readMaybe) 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 -- normalize the number first, making everything lowercase and removing
-- visual niceties like spaces and underscores -- visual niceties like spaces and underscores
parseNumber :: String -> Number normalizeNumber :: String -> String
parseNumber = parseNumber' . map toLower . filter (not . isPad) normalizeNumber = map toLower . filter (not . isPad)
where isPad = flip elem "_ \n\t" where isPad = flip elem "_ \n\t"
parseNumber' :: String -> Number -- truncate the given decimal number literal, if necessary
parseNumber' "'0" = UnbasedUnsized Bit0 validateDecimal :: Bool -> String -> Int -> Bool -> Integer -> (Number, String)
parseNumber' "'1" = UnbasedUnsized Bit1 validateDecimal oversizedNumbers str sz sg v
parseNumber' "'x" = UnbasedUnsized BitX | sz < -32 && oversizedNumbers =
parseNumber' "'z" = UnbasedUnsized BitZ valid $ Decimal sz sg v
parseNumber' str = | 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 -- simple decimal number
if maybeIdx == Nothing then if maybeIdx == Nothing then
let n = readDecimal str let num = readDecimal str
in Decimal (negate $ decimalSize True n) True n sz = negate (decimalSize True num)
in decimal sz True num
-- non-decimal based integral number -- non-decimal based integral number
else if maybeBase /= Nothing then else if maybeBase /= Nothing then
let (values, kinds) = parseBasedDigits (baseSize base) digitsExtended 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 -- decimal X or Z literal
else if numDigits == 1 && elem leadDigit xzDigits then else if numDigits == 1 && leadDigitIsXZ then
let (vals, knds) = parseBasedDigits 2 $ replicate (abs size) leadDigit let vals = if elem leadDigit zDigits then widthMask size else 0
in Based size signed Binary vals knds knds = widthMask size
in valid $ Based size signed Binary vals knds
-- explicitly-based decimal number -- explicitly-based decimal number
else else
let num = readDecimal digits let num = readDecimal digits
in if rawSize == 0 sz = if rawSize == 0 then negate (decimalSize signed num) else size
then Decimal (negate $ decimalSize signed num) signed num in decimal sz signed num
else Decimal size signed num
where where
-- pull out the components of the literals -- pull out the components of the literals
maybeIdx = elemIndex '\'' str maybeIdx = elemIndex '\'' str
...@@ -63,8 +155,9 @@ parseNumber' str = ...@@ -63,8 +155,9 @@ parseNumber' str =
-- high-order X or Z is extended up to the size of the literal -- high-order X or Z is extended up to the size of the literal
leadDigit = head digits leadDigit = head digits
numDigits = length digits numDigits = length digits
leadDigitIsXZ = elem leadDigit xzDigits
digitsExtended = digitsExtended =
if elem leadDigit xzDigits if leadDigitIsXZ
then replicate (sizeDigits - numDigits) leadDigit ++ digits then replicate (sizeDigits - numDigits) leadDigit ++ digits
else digits else digits
...@@ -84,12 +177,24 @@ parseNumber' str = ...@@ -84,12 +177,24 @@ parseNumber' str =
size = size =
if rawSize /= 0 then if rawSize /= 0 then
rawSize rawSize
else if maybeBase /= Nothing then else if maybeBase == Nothing || leadDigitIsXZ then
negate $ bitsPerDigit * numDigits
else
-32 -32
else
negate $ min 32 $ bitsPerDigit * numDigits
bitsPerDigit = bits $ baseSize base - 1 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 -- read a simple unsigned decimal number
readDecimal :: Read a => String -> a readDecimal :: Read a => String -> a
readDecimal str = readDecimal str =
...@@ -171,6 +276,16 @@ bitToVK Bit1 = (1, 0) ...@@ -171,6 +276,16 @@ bitToVK Bit1 = (1, 0)
bitToVK BitX = (0, 1) bitToVK BitX = (0, 1)
bitToVK BitZ = (1, 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 data Base
= Binary = Binary
| Octal | Octal
......
...@@ -22,6 +22,7 @@ data Config = Config ...@@ -22,6 +22,7 @@ data Config = Config
, cfIncludePaths :: [FilePath] , cfIncludePaths :: [FilePath]
, cfSiloed :: Bool , cfSiloed :: Bool
, cfSkipPreprocessor :: Bool , cfSkipPreprocessor :: Bool
, cfOversizedNumbers :: Bool
} }
-- parse CLI macro definitions into the internal macro environment format -- parse CLI macro definitions into the internal macro environment format
...@@ -48,7 +49,7 @@ parseFile :: Config -> FilePath -> ExceptT String IO (Config, AST) ...@@ -48,7 +49,7 @@ parseFile :: Config -> FilePath -> ExceptT String IO (Config, AST)
parseFile config path = do parseFile config path = do
(config', contents) <- preprocessFile config path (config', contents) <- preprocessFile config path
tokens <- liftEither $ runExcept $ lexStr contents tokens <- liftEither $ runExcept $ lexStr contents
ast <- parse tokens ast <- parse (cfOversizedNumbers config) tokens
return (config', ast) return (config', ast)
-- preprocess an individual file, potentially updating the configuration -- preprocess an individual file, potentially updating the configuration
......
...@@ -19,6 +19,7 @@ module Language.SystemVerilog.Parser.Parse (parse) where ...@@ -19,6 +19,7 @@ module Language.SystemVerilog.Parser.Parse (parse) where
import Control.Monad.Except 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 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
...@@ -1206,7 +1207,7 @@ Real :: { String } ...@@ -1206,7 +1207,7 @@ Real :: { String }
: real { tokenString $1 } : real { tokenString $1 }
Number :: { Number } Number :: { Number }
: number { parseNumber $ tokenString $1 } : number {% readNumber (tokenPosition $1) (tokenString $1) }
String :: { String } String :: { String }
: string { tail $ init $ tokenString $1 } : string { tail $ init $ tokenString $1 }
...@@ -1463,25 +1464,26 @@ join : "join" {} | error {% missingToken "join" } ...@@ -1463,25 +1464,26 @@ join : "join" {} | error {% missingToken "join" }
data ParseData = ParseData data ParseData = ParseData
{ pPosition :: Position { pPosition :: Position
, pTokens :: [Token] , pTokens :: [Token]
, pOversizedNumbers :: Bool
} }
type ParseState = StateT ParseData (ExceptT String IO) type ParseState = StateT ParseData (ExceptT String IO)
parse :: [Token] -> ExceptT String IO AST parse :: Bool -> [Token] -> ExceptT String IO AST
parse [] = return [] parse _ [] = return []
parse tokens = parse oversizedNumbers tokens =
evalStateT parseMain initialState evalStateT parseMain initialState
where where
position = tokenPosition $ head tokens position = tokenPosition $ head tokens
initialState = ParseData position tokens initialState = ParseData position tokens oversizedNumbers
positionKeep :: (Token -> ParseState a) -> ParseState a positionKeep :: (Token -> ParseState a) -> ParseState a
positionKeep cont = do positionKeep cont = do
tokens <- gets pTokens ParseData _ tokens oversizedNumbers <- get
case tokens of case tokens of
[] -> cont TokenEOF [] -> cont TokenEOF
tok : toks -> do tok : toks -> do
put $ ParseData (tokenPosition tok) toks put $ ParseData (tokenPosition tok) toks oversizedNumbers
cont tok cont tok
parseErrorTok :: Token -> ParseState a parseErrorTok :: Token -> ParseState a
...@@ -1672,4 +1674,12 @@ splitInit decl = ...@@ -1672,4 +1674,12 @@ splitInit decl =
(Variable d t ident a Nil, Just (LHSIdent ident, e)) (Variable d t ident a Nil, Just (LHSIdent ident, e))
where Variable d t ident a e = decl 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
} }
...@@ -80,6 +80,7 @@ main = do ...@@ -80,6 +80,7 @@ main = do
, cfIncludePaths = incdir job , cfIncludePaths = incdir job
, cfSiloed = siloed job , cfSiloed = siloed job
, cfSkipPreprocessor = skipPreprocessor job , cfSkipPreprocessor = skipPreprocessor job
, cfOversizedNumbers = oversizedNumbers job
} }
result <- runExceptT $ parseFiles config (files job) result <- runExceptT $ parseFiles config (files job)
case result of case result of
......
...@@ -26,25 +26,5 @@ module top; ...@@ -26,25 +26,5 @@ module top;
`TEST(2, 1); `TEST(2, 1);
`TEST(2, 2); `TEST(2, 2);
`TEST(2, 3); `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 end
endmodule endmodule
...@@ -29,10 +29,6 @@ module top; ...@@ -29,10 +29,6 @@ module top;
$display("%b", 32'(4)); $display("%b", 32'(4));
$display("%b", 33'(4)); $display("%b", 33'(4));
$display("%b", 33'(64'hFFFF_FFFF_FFFF_FFFF)); $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 end
localparam bit foo = '0; localparam bit foo = '0;
......
...@@ -32,10 +32,6 @@ module top; ...@@ -32,10 +32,6 @@ module top;
$display("%b", 32'd4); $display("%b", 32'd4);
$display("%b", 33'd4); $display("%b", 33'd4);
$display("%b", 33'h1_FFFF_FFFF); $display("%b", 33'h1_FFFF_FFFF);
$display("%b", 32'd0);
$display("%b", 33'd4294967296);
$display("%b", 32'd1);
$display("%b", 33'd4294967297);
end end
localparam [0:0] foo = 0; localparam [0:0] foo = 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
module top; module top;
initial begin initial begin
$display(signed'(4294967295)); $display(signed'(1'b1));
$display(unsigned'(4294967295)); $display(unsigned'(1'sb1));
$display(signed'(1));
$display(unsigned'(1));
$display(signed'(-1)); $display(signed'(-1));
$display(unsigned'(-1)); $display(unsigned'(-1));
end end
......
module top; module top;
initial begin initial begin
$display($signed(4294967295)); $display($signed(1'b1));
$display($unsigned(4294967295)); $display($unsigned(1'sb1));
$display($signed(1));
$display($unsigned(1));
$display($signed(-1)); $display($signed(-1));
$display($unsigned(-1)); $display($unsigned(-1));
end end
......
...@@ -10,7 +10,7 @@ module top; ...@@ -10,7 +10,7 @@ module top;
initial begin initial begin
$monitor("%d: %01b %04b %02b", select, a, b, c); $monitor("%d: %01b %04b %02b", select, a, b, c);
in = 'b01111011011011101111100111110111001010001011100110101000; in = 56'b01111011011011101111100111110111001010001011100110101000;
select = 0; #1; select = 0; #1;
select = 1; #1; select = 1; #1;
select = 2; #1; select = 2; #1;
......
...@@ -15,13 +15,8 @@ module top; ...@@ -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(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(1234_5678) `TEST('h1234_5678) `TEST('o1234_5677) `TEST('b0101_1100)
`TEST('d4294967295) `TEST('d4294967296) `TEST('d4294967297) `TEST('d4294967298) `TEST('d4294967299) `TEST('d4294967294) `TEST('d4294967295)
`TEST('d004294967295) `TEST('d004294967296) `TEST('d004294967297) `TEST('d004294967298) `TEST('d004294967299) `TEST('d004294967294) `TEST('d004294967295)
`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(659) `TEST('h 837FF) `TEST('o7460) `TEST(659) `TEST('h 837FF) `TEST('o7460)
`TEST(4'b1001) `TEST(5 'D 3) `TEST(3'b01x) `TEST(12'hx) `TEST(16'hz) `TEST(4'b1001) `TEST(5 'D 3) `TEST(3'b01x) `TEST(12'hx) `TEST(16'hz)
...@@ -31,10 +26,9 @@ module top; ...@@ -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(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('hf & 10'hf) `TEST(7'hf & 10'hf)
`TEST('b01xz01xz01xz01xz01xz01xz01xz01xz01xz) `TEST('b101xz01xz01xz01xz01xz01xz01xz01xz01xz)
`TEST(36'b01xz01xz01xz01xz01xz01xz01xz01xz01xz) `TEST(37'b01xz01xz01xz01xz01xz01xz01xz01xz01xz) `TEST(36'b01xz01xz01xz01xz01xz01xz01xz01xz01xz) `TEST(37'b01xz01xz01xz01xz01xz01xz01xz01xz01xz)
`TEST(36'sb01xz01xz01xz01xz01xz01xz01xz01xz01xz) `TEST(37'sb01xz01xz01xz01xz01xz01xz01xz01xz01xz) `TEST(36'sb01xz01xz01xz01xz01xz01xz01xz01xz01xz) `TEST(37'sb01xz01xz01xz01xz01xz01xz01xz01xz01xz)
`TEST('h01xz01xz) `TEST('h101xz01xz) `TEST('h01xz01xz)
`TEST(36'h01xz01xz) `TEST(37'h01xz01xz) `TEST(36'h01xz01xz) `TEST(37'h01xz01xz)
`TEST(36'hb01xz01xz) `TEST(37'hb01xz01xz) `TEST(36'hb01xz01xz) `TEST(37'hb01xz01xz)
......
...@@ -24,12 +24,20 @@ simulate() { ...@@ -24,12 +24,20 @@ simulate() {
-Wno-portbind \ -Wno-portbind \
-o $sim_prog \ -o $sim_prog \
-g2005 \ -g2005 \
-gstrict-expr-width \
-DTEST_VCD="\"$sim_vcd_tmp\"" \ -DTEST_VCD="\"$sim_vcd_tmp\"" \
-DTEST_TOP=$sim_top \ -DTEST_TOP=$sim_top \
"$@" \
$SCRIPT_DIR/tb_dumper.v \ $SCRIPT_DIR/tb_dumper.v \
"$@" 2>&1` 2>&1`
assertTrue "iverilog on $1 failed" $? if [ $? -ne 0 ]; then
assertNull "iverilog emitted warnings:\n$iv_output" "$iv_output" 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 # run the simulation
$sim_prog > $sim_log $sim_prog > $sim_log
assertTrue "simulating $1 failed" $? assertTrue "simulating $1 failed" $?
......
'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
#!/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
module top;
localparam X = NUM;
localparam [63:0] Y = X;
initial $display("%0d %b %b", $bits(X), X, Y);
endmodule
'h0_FFFF_FFFF
'o00xz01xz01xz
'o01xz01xz01xz
'bz01xz01xz01xz01xz01xz01xz01xz01xz
'hZ_ZZZZ_ZZZZ
'hz01xz01xz
4294967294
4294967295
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
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