diff --git a/README.md b/README.md index 5dd4293..8c0ca34 100644 --- a/README.md +++ b/README.md @@ -101,8 +101,8 @@ Other: ## Supported Features sv2v supports most synthesizable SystemVerilog features. Current notable -exceptions include `export` and complex (non-identifier) `modport` expressions. -Assertions are also supported, but are simply dropped during conversion. +exceptions include `export` and interface arrays. Assertions are also supported, +but are simply dropped during conversion. If you find a bug or have a feature request, please create an issue. Preference will be given to issues which include examples or test cases. diff --git a/src/Convert/Interface.hs b/src/Convert/Interface.hs index 9c74cd7..cc15434 100644 --- a/src/Convert/Interface.hs +++ b/src/Convert/Interface.hs @@ -38,8 +38,10 @@ convert = -- we can only collect/map non-extern interfaces collectDesc :: Description -> Writer (Interfaces, Modules) () collectDesc (orig @ (Part _ False kw _ name ports items)) = do - if kw == Interface - then tell (Map.singleton name (ports, items), Map.empty) + if kw == Interface then + if all fullyResolved items + then tell (Map.singleton name (ports, items), Map.empty) + else return () else tell (Map.empty, Map.singleton name (params, decls)) where params = map fst $ parameters items @@ -51,8 +53,19 @@ convert = collectDecl _ = return () collectDesc _ = return () isInterface :: Description -> Bool - isInterface (Part _ False Interface _ _ _ _) = True + isInterface (Part _ False Interface _ _ _ items) = + all fullyResolved items isInterface _ = False + -- returns whether a ModuleItem still contains TypeOf + fullyResolved :: ModuleItem -> Bool + fullyResolved = + not . any isTypeOf . execWriter . + collectNestedModuleItemsM (collectTypesM collectType) + where + collectType :: Type -> Writer [Type] () + collectType t = tell [t] + isTypeOf TypeOf{} = True + isTypeOf _ = False convertDescription :: Interfaces -> Modules -> Description -> Description convertDescription interfaces modules (Part attrs extern Module lifetime name ports items) = @@ -72,7 +85,9 @@ convertDescription interfaces modules (Part attrs extern Module lifetime name po collectInterface (MIPackageItem (Decl (Variable _ t ident _ _))) = case t of InterfaceT interfaceName (Just modportName) [] -> - tell (Map.empty, Map.singleton ident (interfaceName, modportDecls)) + if Map.member interfaceName interfaces + then tell (Map.empty, Map.singleton ident (interfaceName, modportDecls)) + else return () where Just modportDecls = lookupModport interfaceName modportName Alias Nothing interfaceName [] -> case impliedModport interfaceName of @@ -92,7 +107,8 @@ convertDescription interfaces modules (Part attrs extern Module lifetime name po -- expand instantiation of a modport case Map.lookup ident modports of Just (_, modportDecls) -> Generate $ map GenModuleItem $ - filter shouldKeep interfaceItems ++ map makePortDecl modportDecls + filter shouldKeep interfaceItems ++ map makePortDecl + (prefixModportDecls ident modportDecls) Nothing -> orig where interfaceName = case t of @@ -108,10 +124,10 @@ convertDescription interfaces modules (Part attrs extern Module lifetime name po shouldKeep (MIPackageItem Function{}) = True shouldKeep _ = False makePortDecl :: ModportDecl -> ModuleItem - makePortDecl (dir, port, expr) = + makePortDecl (dir, port, typ, _) = MIPackageItem $ Decl $ Variable dir mpt (ident ++ "_" ++ port) mprs Nil - where (mpt, mprs) = lookupType interfaceItems ident expr + where (mpt, mprs) = (typ, []) mapInterface (Instance part params ident [] instancePorts) = -- expand modport port bindings case Map.lookup part interfaces of @@ -201,7 +217,7 @@ convertDescription interfaces modules (Part attrs extern Module lifetime name po -- modport directly bound to a modport expandPortBinding' interfaceName portName ident (map redirect modportDecls) - where redirect (d, x, _) = (d, x, Ident x) + where redirect (d, x, t, _) = (d, x, t, Ident x) expandPortBinding _ other _ = ([], [other]) expandPortBinding' :: Identifier -> Identifier -> Identifier -> @@ -214,7 +230,7 @@ convertDescription interfaces modules (Part attrs extern Module lifetime name po interfaceParamNames = map fst $ parameters interfaceItems toParamBinding x = (portName ++ '_' : x, Right $ Ident $ instanceName ++ '_' : x) portBindings = map toPortBinding modportDecls - toPortBinding (_, x, e) = (x', e') + toPortBinding (_, x, _, e) = (x', e') where x' = portName ++ '_' : x e' = traverseNestedExprs prefixExpr e @@ -247,8 +263,8 @@ convertDescription interfaces modules (Part attrs extern Module lifetime name po mapM (collectNestedModuleItemsM collectModportDecls) $ interfaceItems collectModportDecls :: ModuleItem -> Writer [ModportDecl] () - collectModportDecls (MIPackageItem (Decl (Variable d _ x _ _))) = - tell [(d', x, Ident x)] + collectModportDecls (MIPackageItem (Decl (Variable d t x _ _))) = + tell [(d', x, t, Ident x)] where d' = if d == Local then Inout else d collectModportDecls _ = return () @@ -268,7 +284,8 @@ convertDescription interfaces modules (Part attrs extern Module lifetime name po convertPort ident = case Map.lookup ident modports of Nothing -> [ident] - Just (_, decls) -> map (\(_, x, _) -> ident ++ "_" ++ x) decls + Just (_, decls) -> map (\(_, x, _, _) -> + ident ++ "_" ++ x) decls convertDescription _ _ other = other @@ -324,20 +341,23 @@ collectIdentsM item = collectDeclsM collectDecl item collectDecl (ParamType _ x _) = tell $ Set.singleton x collectDecl (CommentDecl _) = return () -lookupType :: [ModuleItem] -> Identifier -> Expr -> (Type, [Range]) -lookupType items prefix (Ident ident) = - case mapMaybe findType items of - [] -> error $ "unable to locate type of " ++ ident - ts -> head ts +-- add a prefix to the expressions in a modport definition +prefixModportDecls :: Identifier -> [ModportDecl] -> [ModportDecl] +prefixModportDecls name modportDecls = + map mapper modportDecls where - findType :: ModuleItem -> Maybe (Type, [Range]) - findType (MIPackageItem (Decl (Variable _ t x rs _))) = - if x == prefix ++ "_" ++ ident then Just (t, rs) else Nothing - findType _ = Nothing -lookupType _ _ expr = - -- TODO: Add support for non-Ident modport expressions. - error $ "interface conversion does not support modport expressions that " - ++ " are not identifiers: " ++ show expr + mapper :: ModportDecl -> ModportDecl + mapper (d, x, t, e) = (d, x, t', e') + where + exprMapper = traverseNestedExprs prefixExpr + t' = traverseNestedTypes (traverseTypeExprs exprMapper) t + e' = exprMapper e + prefix :: Identifier -> Identifier + prefix = (++) $ name ++ "_" + prefixExpr :: Expr -> Expr + prefixExpr (Ident ('$' : x)) = Ident $ '$' : x + prefixExpr (Ident x) = Ident (prefix x) + prefixExpr other = other -- convert an interface instantiation into a series of equivalent module items inlineInterface :: Interface -> (Identifier, [ParamBinding], [PortBinding]) -> [ModuleItem] @@ -367,7 +387,11 @@ inlineInterface (ports, items) (instanceName, instanceParams, instancePorts) = removeDeclDir :: ModuleItem -> ModuleItem removeDeclDir (MIPackageItem (Decl (Variable _ t x a e))) = - MIPackageItem $ Decl $ Variable Local t x a e + MIPackageItem $ Decl $ Variable Local t' x a e + where t' = case t of + Implicit Unspecified rs -> + IntegerVector TLogic Unspecified rs + _ -> t removeDeclDir other = other removeModport :: ModuleItem -> ModuleItem removeModport (Modport x _) = diff --git a/src/Convert/Logic.hs b/src/Convert/Logic.hs index 7296c33..13b4dfe 100644 --- a/src/Convert/Logic.hs +++ b/src/Convert/Logic.hs @@ -56,10 +56,11 @@ convert = portName ++ " in module " ++ name collectPortsM _ = return () collectDeclDirsM :: ModuleItem -> Writer [(Identifier, Direction)] () - collectDeclDirsM (MIPackageItem (Decl (Variable dir _ ident _ _))) = - if dir == Local - then return () - else tell [(ident, dir)] + collectDeclDirsM (MIPackageItem (Decl (Variable dir t ident _ _))) = + case (dir, t) of + (_, InterfaceT{}) -> tell [(ident, Local)] + (Local, _) -> return () + _ -> tell [(ident, dir)] collectDeclDirsM _ = return () convertDescription :: Ports -> Description -> Description diff --git a/src/Convert/Traverse.hs b/src/Convert/Traverse.hs index 0710fc8..784362a 100644 --- a/src/Convert/Traverse.hs +++ b/src/Convert/Traverse.hs @@ -648,9 +648,10 @@ traverseExprsM' strat exprMapper = moduleItemMapper return $ GenCase e' cases' genItemMapper other = return other - modportDeclMapper (dir, ident, e) = do + modportDeclMapper (dir, ident, t, e) = do + t' <- typeMapper t e' <- exprMapper e - return (dir, ident, e') + return (dir, ident, t', e') traverseExprs' :: TFStrategy -> Mapper Expr -> Mapper ModuleItem traverseExprs' strat = unmonad $ traverseExprsM' strat @@ -933,6 +934,11 @@ traverseTypesM' strategy mapper item = then fullMapper t >>= \t' -> return (i, Left t') else return (i, Left t) mapParam (i, Right e) = return $ (i, Right e) + miMapper (Modport name decls) = + mapM mapModportDecl decls >>= return . Modport name + where + mapModportDecl (d, x, t, e) = + fullMapper t >>= \t' -> return (d, x, t', e) miMapper other = return other traverseTypes' :: TypeStrategy -> Mapper Type -> Mapper ModuleItem diff --git a/src/Language/SystemVerilog/AST/ModuleItem.hs b/src/Language/SystemVerilog/AST/ModuleItem.hs index 40a2b28..e261955 100644 --- a/src/Language/SystemVerilog/AST/ModuleItem.hs +++ b/src/Language/SystemVerilog/AST/ModuleItem.hs @@ -28,7 +28,7 @@ import Language.SystemVerilog.AST.Expr (Expr(Ident, Nil), Range, TypeOrExpr, sho import Language.SystemVerilog.AST.GenItem (GenItem) import Language.SystemVerilog.AST.LHS (LHS) import Language.SystemVerilog.AST.Stmt (Stmt, AssertionItem, Timing(Delay)) -import Language.SystemVerilog.AST.Type (Identifier, DriveStrength) +import Language.SystemVerilog.AST.Type (Type, Identifier, DriveStrength) data ModuleItem = MIAttr Attr ModuleItem @@ -99,16 +99,16 @@ showParam (i, arg) = where fmt = if i == "" then "%s%s" else ".%s(%s)" showModportDecl :: ModportDecl -> String -showModportDecl (dir, ident, e) = +showModportDecl (dir, ident, t, e) = if e == Ident ident then printf "%s %s" (show dir) ident - else printf "%s .%s(%s)" (show dir) ident (show e) + else printf "%s .%s(/* type: %s */ %s)" (show dir) ident (show t) (show e) type PortBinding = (Identifier, Expr) type ParamBinding = (Identifier, TypeOrExpr) -type ModportDecl = (Direction, Identifier, Expr) +type ModportDecl = (Direction, Identifier, Type, Expr) data AlwaysKW = Always diff --git a/src/Language/SystemVerilog/Parser/Parse.y b/src/Language/SystemVerilog/Parser/Parse.y index a87111a..5a9d74b 100644 --- a/src/Language/SystemVerilog/Parser/Parse.y +++ b/src/Language/SystemVerilog/Parser/Parse.y @@ -588,7 +588,7 @@ ModportPortsDeclarations :: { [ModportDecl] } ModportPortsDeclaration(delim) :: { [ModportDecl] } : ModportSimplePortsDeclaration(delim) { $1 } ModportSimplePortsDeclaration(delim) :: { [ModportDecl] } - : Direction ModportSimplePorts delim { map (\(a, b) -> ($1, a, b)) $2 } + : Direction ModportSimplePorts delim { map (\(a, b) -> ($1, a, TypeOf b, b)) $2 } ModportSimplePorts :: { [(Identifier, Expr)] } : ModportSimplePort { [$1] } | ModportSimplePorts "," ModportSimplePort { $1 ++ [$3] } diff --git a/test/basic/interface_infer.sv b/test/basic/interface_infer.sv index ffd6aa7..4287918 100644 --- a/test/basic/interface_infer.sv +++ b/test/basic/interface_infer.sv @@ -1,6 +1,6 @@ localparam SOME_VAL = 3; interface Interface; - logic x; + logic x = 0; modport Modport( input x ); @@ -15,11 +15,11 @@ module Module(Interface.Modport foo); initial $display("Module %d", foo.x); endmodule module top; + Interface i(); + Module m(i); generate for (genvar g = 0; g < 5; ++g) begin initial $display(g); end endgenerate - Interface i(); - Module m(i); endmodule diff --git a/test/basic/interface_infer.v b/test/basic/interface_infer.v index ccbb11b..f5e79f0 100644 --- a/test/basic/interface_infer.v +++ b/test/basic/interface_infer.v @@ -2,16 +2,16 @@ module Module(input wire x); initial $display("Module %d", x); endmodule module top; - wire i_x; + wire i_x = 0; localparam SOME_VAL = 3; initial $display("Interface %d %d", i_x, SOME_VAL); Module m(.x(i_x)); generate genvar g; - for (g = 0; g < 5; g = g + 1) begin + for (g = 10; g < 15; g = g + 1) begin initial $display(g); end - for (g = 10; g < 15; g = g + 1) begin + for (g = 0; g < 5; g = g + 1) begin initial $display(g); end endgenerate diff --git a/test/basic/interface_modport.sv b/test/basic/interface_modport.sv new file mode 100644 index 0000000..6d35432 --- /dev/null +++ b/test/basic/interface_modport.sv @@ -0,0 +1,51 @@ +interface Interface #(parameter WIDTH = 4) ( + input clock, + output [$clog2(WIDTH) - 1:0] indices [2] +); + logic [2*WIDTH-1:0] x; + modport ModportA( + input clock, + output indices, + input .x(x[2*WIDTH-1:WIDTH]), .y(x[WIDTH-1:0]) + ); + modport ModportB( + input clock, + output .x(x) + ); +endinterface + +module ModuleA(Interface.ModportA m); + assign m.indices[0] = $clog2(m.x); + assign m.indices[1] = $clog2(m.y); +endmodule + +module ModuleB(Interface.ModportB m); + initial m.x = 1; + localparam WIDTH = 2 * m.WIDTH; + always @(posedge m.clock) begin + logic temp; + temp = m.x[WIDTH-1]; + for (integer i = WIDTH-1; i > 0; --i) begin + m.x[i] = m.x[i-1]; + end + m.x[0] = temp; + end +endmodule + +module ModuleBWrapper(Interface.ModportB m); + ModuleB b(m); +endmodule + +module ModuleAWrapper(Interface.ModportA m); + ModuleA a(m); +endmodule + +module Tester(input clock); + parameter WIDTH = 1; + logic [WIDTH-1:0] idx1, idx2; + Interface #(2 ** WIDTH) i(clock, '{idx1, idx2}); + ModuleAWrapper a(i); + ModuleBWrapper b(i); + always @(negedge clock) + $display("%d %0d %2d %2d %b", $time, WIDTH, idx1, idx2, i.x); +endmodule diff --git a/test/basic/interface_modport.v b/test/basic/interface_modport.v new file mode 100644 index 0000000..44e4280 --- /dev/null +++ b/test/basic/interface_modport.v @@ -0,0 +1,26 @@ +module Tester(input clock); + parameter WIDTH = 1; + + localparam DATA_WIDTH = 2 ** WIDTH; + + reg [2*DATA_WIDTH-1:0] x; + initial x = 1; + + wire [WIDTH-1:0] idx1, idx2; + assign idx1 = $clog2(x[2*DATA_WIDTH-1:DATA_WIDTH]); + assign idx2 = $clog2(x[DATA_WIDTH-1:0]); + + always @(posedge clock) begin : block + localparam SIZE = 2 * DATA_WIDTH; + integer i; + reg temp; + temp = x[SIZE-1]; + for (i = SIZE-1; i > 0; i = i - 1) begin + x[i] = x[i-1]; + end + x[0] = temp; + end + + always @(negedge clock) + $display("%d %0d %2d %2d %b", $time, WIDTH, idx1, idx2, x); +endmodule diff --git a/test/basic/interface_modport_tb.v b/test/basic/interface_modport_tb.v new file mode 100644 index 0000000..a3b4742 --- /dev/null +++ b/test/basic/interface_modport_tb.v @@ -0,0 +1,18 @@ +module top; + reg clock; + initial begin + clock = 1; + forever #5 clock = ~clock; + end + + initial begin + repeat(30) + @(posedge clock); + $finish; + end + + Tester #(1) t1(clock); + Tester #(2) t2(clock); + Tester #(3) t3(clock); + Tester #(4) t4(clock); +endmodule