mirror of https://github.com/zachjs/sv2v.git
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
This commit is contained in:
parent
5e5ddca444
commit
306d71334b
10
CHANGELOG.md
10
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
{-# LANGUAGE TupleSections #-}
|
||||
{- sv2v
|
||||
- Author: Zachary Snow <zach@zachjs.com>
|
||||
-
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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" $?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
module top;
|
||||
localparam X = NUM;
|
||||
localparam [63:0] Y = X;
|
||||
initial $display("%0d %b %b", $bits(X), X, Y);
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
'h0_FFFF_FFFF
|
||||
'o00xz01xz01xz
|
||||
'o01xz01xz01xz
|
||||
'bz01xz01xz01xz01xz01xz01xz01xz01xz
|
||||
'hZ_ZZZZ_ZZZZ
|
||||
'hz01xz01xz
|
||||
4294967294
|
||||
4294967295
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue