Commit 2eee536f by Zachary Snow

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
parent bfd0cee0
...@@ -3,15 +3,12 @@ ...@@ -3,15 +3,12 @@
- -
- Conversion for `enum` - Conversion for `enum`
- -
- This conversion replaces the enum items with localparams. The localparams are - This conversion replaces references to enum items with their values. The
- explicitly sized to match the size of the converted enum type. For packages - values are explicitly cast to the enum's base type.
- 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.
- -
- SystemVerilog allows for enums to have any number of the items' values - 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 - 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, - 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 - whether implicitly or explicitly. We catch try to catch "obvious" instances
...@@ -20,86 +17,91 @@ ...@@ -20,86 +17,91 @@
module Convert.Enum (convert) where module Convert.Enum (convert) where
import Control.Monad.Writer.Strict import Control.Monad (zipWithM_, (>=>))
import Data.List (elemIndices) import Data.List (elemIndices)
import qualified Data.Set as Set
import Convert.ExprUtils import Convert.ExprUtils
import Convert.Package (inject) import Convert.Scoper
import Convert.Traverse import Convert.Traverse
import Language.SystemVerilog.AST import Language.SystemVerilog.AST
type EnumInfo = (Type, [(Identifier, Expr)]) type SC = Scoper Expr
type Enums = Set.Set EnumInfo
convert :: [AST] -> [AST] convert :: [AST] -> [AST]
convert = map $ concatMap convertDescription convert = map $ traverseDescriptions $ partScoper
traverseDeclM traverseModuleItemM traverseGenItemM traverseStmtM
convertDescription :: Description -> [Description] traverseDeclM :: Decl -> SC Decl
convertDescription (description @ Part{}) = traverseDeclM decl = do
[Part attrs extern kw lifetime name ports items'] case decl of
where Variable _ _ x _ _ -> insertElem x Nil
items' = -- only keep what's used Net _ _ _ _ x _ _ -> insertElem x Nil
if null enumItems Param _ _ x _ -> insertElem x Nil
then items ParamType _ x _ -> insertElem x Nil
else inject enumItems items CommentDecl{} -> return ()
Part attrs extern kw lifetime name ports items = description' traverseDeclTypesM traverseTypeM decl >>=
(description', enumItems) = convertDescription' description traverseDeclExprsM traverseExprM
convertDescription other = [other]
-- replace and collect the enum types in a description traverseModuleItemM :: ModuleItem -> SC ModuleItem
convertDescription' :: Description -> (Description, [PackageItem]) traverseModuleItemM (Genvar x) =
convertDescription' description = insertElem x Nil >> return (Genvar x)
(description', enumItems) traverseModuleItemM item =
where traverseNodesM traverseExprM return traverseTypeM traverseLHSM return item
-- replace and collect the enum types in this description where traverseLHSM = traverseLHSExprsM traverseExprM
(description', enums) = runWriter $
traverseModuleItemsM traverseModuleItemM description traverseGenItemM :: GenItem -> SC GenItem
traverseModuleItemM = traverseTypesM $ traverseNestedTypesM traverseType traverseGenItemM = traverseGenItemExprsM traverseExprM
-- convert the collected enums into their corresponding localparams
enumItems = concatMap makeEnumItems $ Set.toList enums 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, but write down, enum types -- replace enum types and insert enum items
traverseType :: Type -> Writer Enums Type replaceEnum :: Type -> SC Type
traverseType (Enum (t @ Alias{}) v rs) = replaceEnum t@(Enum Alias{} v _) = -- not ready
return $ Enum t v rs -- not ready mapM_ (flip insertElem Nil . fst) v >> return t
traverseType (Enum (t @ PSAlias{}) v rs) = replaceEnum (Enum (Implicit sg rl) v rs) =
return $ Enum t v rs -- not ready replaceEnum $ Enum t' v rs
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
where where
-- default to a 32 bit logic -- default to a 32 bit logic
t' = IntegerVector TLogic sg rl' t' = IntegerVector TLogic sg rl'
rl' = if null rl rl' = if null rl
then [(RawNum 31, RawNum 0)] then [(RawNum 31, RawNum 0)]
else rl else rl
traverseType (Enum t v rs) = do replaceEnum (Enum t v rs) =
let (tf, rl) = typeRanges t insertEnumItems t v >> return (tf $ rl ++ rs)
rlParam <- case rl of where (tf, rl) = typeRanges t
[ ] -> return [(RawNum 0, RawNum 0)] replaceEnum other = return other
[_] -> 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
makeEnumItems :: EnumInfo -> [PackageItem] insertEnumItems :: Type -> [(Identifier, Expr)] -> SC ()
makeEnumItems (itemType, l) = insertEnumItems itemType items =
-- check for obviously duplicate values -- check for obviously duplicate values
if noDuplicates if noDuplicates
then zipWith toPackageItem keys vals then zipWithM_ insertEnumItem keys vals
else error $ "enum conversion has duplicate vals: " else error $ "enum conversion has duplicate vals: "
++ show (zip keys vals) ++ show (zip keys vals)
where where
keys = map fst l insertEnumItem :: Identifier -> Expr -> SC ()
vals = tail $ scanl step (UniOp UniSub $ RawNum 1) (map snd l) 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 noDuplicates = all (null . tail . flip elemIndices vals) vals
step :: Expr -> Expr -> Expr step :: Expr -> Expr -> Expr
step expr Nil = simplify $ BinOp Add expr (RawNum 1) step expr Nil = simplify $ BinOp Add expr (RawNum 1)
step _ expr = expr step _ expr = expr
toPackageItem :: Identifier -> Expr -> PackageItem
toPackageItem x v =
Decl $ Param Localparam itemType x v'
where v' = simplify v
...@@ -18,10 +18,7 @@ ...@@ -18,10 +18,7 @@
- into modules and interfaces as needed. - into modules and interfaces as needed.
-} -}
module Convert.Package module Convert.Package (convert) where
( convert
, inject
) where
import Control.Monad.State.Strict import Control.Monad.State.Strict
import Control.Monad.Writer.Strict import Control.Monad.Writer.Strict
...@@ -60,15 +57,6 @@ makeLocal (Decl (Param _ t x e)) = Decl $ Param Localparam t x e ...@@ -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 (Decl (ParamType _ x t)) = Decl $ ParamType Localparam x t
makeLocal other = other 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 -- collect packages and global package items
collectPackageM :: Description -> Writer (Packages, Classes, [PackageItem]) () collectPackageM :: Description -> Writer (Packages, Classes, [PackageItem]) ()
collectPackageM (PackageItem item) = collectPackageM (PackageItem item) =
......
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
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
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