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 @@
-
- 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
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, 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
-- 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) =
......
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