mirror of https://github.com/zachjs/sv2v.git
enum conversion to handles additional scope conflicts
- substitute enum item values directly into usage sites rather than creating synthetic localparams - substitution handles shadowing of expressions in enum item values - disconnected scopes can reference conflicting enum items
This commit is contained in:
parent
bfd0cee0dc
commit
2eee536f62
|
|
@ -3,15 +3,12 @@
|
|||
-
|
||||
- Conversion for `enum`
|
||||
-
|
||||
- This conversion replaces the enum items with localparams. The localparams are
|
||||
- explicitly sized to match the size of the converted enum type. For packages
|
||||
- and enums used in the global scope, these localparams are inserted in place.
|
||||
- For enums used within a module or interface, the localparams are injected as
|
||||
- needed using a nesting procedure from the package conversion.
|
||||
- This conversion replaces references to enum items with their values. The
|
||||
- values are explicitly cast to the enum's base type.
|
||||
-
|
||||
- SystemVerilog allows for enums to have any number of the items' values
|
||||
- specified or unspecified. If the first one is unspecified, it is 0. All other
|
||||
- values take on the value of the previous item, plus 1.
|
||||
- unspecified values take on the value of the previous item, plus 1.
|
||||
-
|
||||
- It is an error for multiple items of the same enum to take on the same value,
|
||||
- whether implicitly or explicitly. We catch try to catch "obvious" instances
|
||||
|
|
@ -20,86 +17,91 @@
|
|||
|
||||
module Convert.Enum (convert) where
|
||||
|
||||
import Control.Monad.Writer.Strict
|
||||
import Control.Monad (zipWithM_, (>=>))
|
||||
import Data.List (elemIndices)
|
||||
import qualified Data.Set as Set
|
||||
|
||||
import Convert.ExprUtils
|
||||
import Convert.Package (inject)
|
||||
import Convert.Scoper
|
||||
import Convert.Traverse
|
||||
import Language.SystemVerilog.AST
|
||||
|
||||
type EnumInfo = (Type, [(Identifier, Expr)])
|
||||
type Enums = Set.Set EnumInfo
|
||||
type SC = Scoper Expr
|
||||
|
||||
convert :: [AST] -> [AST]
|
||||
convert = map $ concatMap convertDescription
|
||||
convert = map $ traverseDescriptions $ partScoper
|
||||
traverseDeclM traverseModuleItemM traverseGenItemM traverseStmtM
|
||||
|
||||
convertDescription :: Description -> [Description]
|
||||
convertDescription (description @ Part{}) =
|
||||
[Part attrs extern kw lifetime name ports items']
|
||||
where
|
||||
items' = -- only keep what's used
|
||||
if null enumItems
|
||||
then items
|
||||
else inject enumItems items
|
||||
Part attrs extern kw lifetime name ports items = description'
|
||||
(description', enumItems) = convertDescription' description
|
||||
convertDescription other = [other]
|
||||
traverseDeclM :: Decl -> SC Decl
|
||||
traverseDeclM decl = do
|
||||
case decl of
|
||||
Variable _ _ x _ _ -> insertElem x Nil
|
||||
Net _ _ _ _ x _ _ -> insertElem x Nil
|
||||
Param _ _ x _ -> insertElem x Nil
|
||||
ParamType _ x _ -> insertElem x Nil
|
||||
CommentDecl{} -> return ()
|
||||
traverseDeclTypesM traverseTypeM decl >>=
|
||||
traverseDeclExprsM traverseExprM
|
||||
|
||||
-- replace and collect the enum types in a description
|
||||
convertDescription' :: Description -> (Description, [PackageItem])
|
||||
convertDescription' description =
|
||||
(description', enumItems)
|
||||
where
|
||||
-- replace and collect the enum types in this description
|
||||
(description', enums) = runWriter $
|
||||
traverseModuleItemsM traverseModuleItemM description
|
||||
traverseModuleItemM = traverseTypesM $ traverseNestedTypesM traverseType
|
||||
-- convert the collected enums into their corresponding localparams
|
||||
enumItems = concatMap makeEnumItems $ Set.toList enums
|
||||
traverseModuleItemM :: ModuleItem -> SC ModuleItem
|
||||
traverseModuleItemM (Genvar x) =
|
||||
insertElem x Nil >> return (Genvar x)
|
||||
traverseModuleItemM item =
|
||||
traverseNodesM traverseExprM return traverseTypeM traverseLHSM return item
|
||||
where traverseLHSM = traverseLHSExprsM traverseExprM
|
||||
|
||||
-- replace, but write down, enum types
|
||||
traverseType :: Type -> Writer Enums Type
|
||||
traverseType (Enum (t @ Alias{}) v rs) =
|
||||
return $ Enum t v rs -- not ready
|
||||
traverseType (Enum (t @ PSAlias{}) v rs) =
|
||||
return $ Enum t v rs -- not ready
|
||||
traverseType (Enum (t @ CSAlias{}) v rs) =
|
||||
return $ Enum t v rs -- not ready
|
||||
traverseType (Enum (Implicit sg rl) v rs) =
|
||||
traverseType $ Enum t' v rs
|
||||
traverseGenItemM :: GenItem -> SC GenItem
|
||||
traverseGenItemM = traverseGenItemExprsM traverseExprM
|
||||
|
||||
traverseStmtM :: Stmt -> SC Stmt
|
||||
traverseStmtM = traverseStmtExprsM traverseExprM
|
||||
|
||||
traverseTypeM :: Type -> SC Type
|
||||
traverseTypeM =
|
||||
traverseSinglyNestedTypesM traverseTypeM >=>
|
||||
traverseTypeExprsM traverseExprM >=>
|
||||
replaceEnum
|
||||
|
||||
traverseExprM :: Expr -> SC Expr
|
||||
traverseExprM (Ident x) = do
|
||||
details <- lookupElemM x
|
||||
return $ case details of
|
||||
Just (_, _, Nil) -> Ident x
|
||||
Just (_, _, e) -> e
|
||||
Nothing -> Ident x
|
||||
traverseExprM expr =
|
||||
traverseSinglyNestedExprsM traverseExprM expr
|
||||
>>= traverseExprTypesM traverseTypeM
|
||||
|
||||
-- replace enum types and insert enum items
|
||||
replaceEnum :: Type -> SC Type
|
||||
replaceEnum t@(Enum Alias{} v _) = -- not ready
|
||||
mapM_ (flip insertElem Nil . fst) v >> return t
|
||||
replaceEnum (Enum (Implicit sg rl) v rs) =
|
||||
replaceEnum $ Enum t' v rs
|
||||
where
|
||||
-- default to a 32 bit logic
|
||||
t' = IntegerVector TLogic sg rl'
|
||||
rl' = if null rl
|
||||
then [(RawNum 31, RawNum 0)]
|
||||
else rl
|
||||
traverseType (Enum t v rs) = do
|
||||
let (tf, rl) = typeRanges t
|
||||
rlParam <- case rl of
|
||||
[ ] -> return [(RawNum 0, RawNum 0)]
|
||||
[_] -> return rl
|
||||
_ -> error $ "unexpected multi-dim enum type: " ++ show (Enum t v rs)
|
||||
tell $ Set.singleton (tf rlParam, v) -- type of localparams
|
||||
return $ tf (rl ++ rs) -- type of variables
|
||||
traverseType other = return other
|
||||
replaceEnum (Enum t v rs) =
|
||||
insertEnumItems t v >> return (tf $ rl ++ rs)
|
||||
where (tf, rl) = typeRanges t
|
||||
replaceEnum other = return other
|
||||
|
||||
makeEnumItems :: EnumInfo -> [PackageItem]
|
||||
makeEnumItems (itemType, l) =
|
||||
insertEnumItems :: Type -> [(Identifier, Expr)] -> SC ()
|
||||
insertEnumItems itemType items =
|
||||
-- check for obviously duplicate values
|
||||
if noDuplicates
|
||||
then zipWith toPackageItem keys vals
|
||||
then zipWithM_ insertEnumItem keys vals
|
||||
else error $ "enum conversion has duplicate vals: "
|
||||
++ show (zip keys vals)
|
||||
where
|
||||
keys = map fst l
|
||||
vals = tail $ scanl step (UniOp UniSub $ RawNum 1) (map snd l)
|
||||
insertEnumItem :: Identifier -> Expr -> SC ()
|
||||
insertEnumItem x = scopeExpr . Cast (Left itemType) >=> insertElem x
|
||||
(keys, valsRaw) = unzip items
|
||||
vals = tail $ scanl step (UniOp UniSub $ RawNum 1) valsRaw
|
||||
noDuplicates = all (null . tail . flip elemIndices vals) vals
|
||||
step :: Expr -> Expr -> Expr
|
||||
step expr Nil = simplify $ BinOp Add expr (RawNum 1)
|
||||
step _ expr = expr
|
||||
toPackageItem :: Identifier -> Expr -> PackageItem
|
||||
toPackageItem x v =
|
||||
Decl $ Param Localparam itemType x v'
|
||||
where v' = simplify v
|
||||
|
|
|
|||
|
|
@ -18,10 +18,7 @@
|
|||
- into modules and interfaces as needed.
|
||||
-}
|
||||
|
||||
module Convert.Package
|
||||
( convert
|
||||
, inject
|
||||
) where
|
||||
module Convert.Package (convert) where
|
||||
|
||||
import Control.Monad.State.Strict
|
||||
import Control.Monad.Writer.Strict
|
||||
|
|
@ -60,15 +57,6 @@ makeLocal (Decl (Param _ t x e)) = Decl $ Param Localparam t x e
|
|||
makeLocal (Decl (ParamType _ x t)) = Decl $ ParamType Localparam x t
|
||||
makeLocal other = other
|
||||
|
||||
-- utility for inserting package items into a set of module items as needed
|
||||
inject :: [PackageItem] -> [ModuleItem] -> [ModuleItem]
|
||||
inject packageItems items =
|
||||
addItems localPIs Set.empty (map addUsedPIs items)
|
||||
where
|
||||
localPIs = Map.fromList $ concatMap toPIElem packageItems
|
||||
toPIElem :: PackageItem -> [(Identifier, PackageItem)]
|
||||
toPIElem item = map (, item) (piNames item)
|
||||
|
||||
-- collect packages and global package items
|
||||
collectPackageM :: Description -> Writer (Packages, Classes, [PackageItem]) ()
|
||||
collectPackageM (PackageItem item) =
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
module top;
|
||||
if (1) begin : blkA
|
||||
typedef enum {
|
||||
X = 2, Y
|
||||
} E;
|
||||
initial $display("blkA X=%0d Y=%0d", X, Y);
|
||||
end
|
||||
if (1) begin : blkB
|
||||
typedef enum {
|
||||
X, Y
|
||||
} E;
|
||||
initial $display("blkB X=%0d Y=%0d", X, Y);
|
||||
initial begin : blk1
|
||||
localparam Y = 100;
|
||||
$display("blkB.blk1 X=%0d Y=%0d", X, Y);
|
||||
end
|
||||
initial begin : blk2
|
||||
integer Y;
|
||||
Y = 101;
|
||||
Y++;
|
||||
$display("blkB.blk2 X=%0d Y=%0d", X, Y);
|
||||
end
|
||||
initial begin : blk3
|
||||
localparam type Y = byte;
|
||||
$display("blkB.blk3 $bits(X)=%0d $bits(Y)=%0d", $bits(X), $bits(Y));
|
||||
end
|
||||
if (1) begin : blk4
|
||||
genvar X;
|
||||
for (X = 0; X < 2; X = X + 1)
|
||||
initial $display("blkB.blk4 X=%0d Y=%0d", X, Y);
|
||||
end
|
||||
if (1) begin : blk5
|
||||
wire [3:0] X = 7;
|
||||
initial $display("blkB.blk5 X=%0d Y=%0d", X, Y);
|
||||
end
|
||||
end
|
||||
typedef enum [5:0] {
|
||||
X = 22, Y
|
||||
} E;
|
||||
initial $display("X=%b Y=%b", X, Y);
|
||||
if (1) begin : blkC
|
||||
typedef byte T;
|
||||
localparam A = 1;
|
||||
localparam B = 2;
|
||||
typedef enum T {
|
||||
X = A, Y = B
|
||||
} E;
|
||||
initial $display("blkC X=%b Y=%b", X, Y);
|
||||
initial begin : blk1
|
||||
localparam type T = shortint;
|
||||
localparam A = 2;
|
||||
localparam B = 3;
|
||||
$display("blkC.blk1 X=%b Y=%b", X, Y);
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
module top;
|
||||
generate
|
||||
if (1) begin : blkA
|
||||
localparam X = 2;
|
||||
localparam Y = X + 1;
|
||||
initial $display("blkA X=%0d Y=%0d", X, Y);
|
||||
end
|
||||
if (1) begin : blkB
|
||||
localparam X = 0;
|
||||
localparam Y = X + 1;
|
||||
initial $display("blkB X=%0d Y=%0d", X, Y);
|
||||
initial begin : blk1
|
||||
localparam Y = 100;
|
||||
$display("blkB.blk1 X=%0d Y=%0d", X, Y);
|
||||
end
|
||||
initial begin : blk2
|
||||
integer Y;
|
||||
Y = 102;
|
||||
$display("blkB.blk2 X=%0d Y=%0d", X, Y);
|
||||
end
|
||||
initial begin : blk3
|
||||
reg [7:0] Y;
|
||||
$display("blkB.blk3 $bits(X)=%0d $bits(Y)=%0d", $bits(X), $bits(Y));
|
||||
end
|
||||
if (1) begin : blk4
|
||||
genvar X;
|
||||
for (X = 0; X < 2; X = X + 1)
|
||||
initial $display("blkB.blk4 X=%0d Y=%0d", X, Y);
|
||||
end
|
||||
if (1) begin : blk5
|
||||
wire [3:0] X = 7;
|
||||
initial $display("blkB.blk5 X=%0d Y=%0d", X, Y);
|
||||
end
|
||||
end
|
||||
localparam [5:0] X = 22;
|
||||
localparam [5:0] Y = X + 1;
|
||||
initial $display("X=%b Y=%b", X, Y);
|
||||
if (1) begin : blkC
|
||||
localparam signed [7:0] X = 1;
|
||||
localparam signed [7:0] Y = 2;
|
||||
initial $display("blkC X=%b Y=%b", X, Y);
|
||||
initial begin : blk1
|
||||
$display("blkC.blk1 X=%b Y=%b", X, Y);
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
Loading…
Reference in New Issue