diff --git a/src/Convert/Interface.hs b/src/Convert/Interface.hs index 7f25c99..52afffa 100644 --- a/src/Convert/Interface.hs +++ b/src/Convert/Interface.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE PatternSynonyms #-} {- sv2v - Author: Zachary Snow - @@ -11,6 +12,7 @@ import Data.Maybe (mapMaybe) import Control.Monad.Writer.Strict import qualified Data.Map.Strict as Map +import Convert.ExprUtils (endianCondExpr) import Convert.Scoper import Convert.Traverse import Language.SystemVerilog.AST @@ -23,18 +25,34 @@ data PartInfo = PartInfo type PartInfos = Map.Map Identifier PartInfo type ModportInstances = [(Identifier, (Identifier, Identifier))] -type ModportBinding = (Identifier, (Expr, Expr)) +type ModportBinding = (Identifier, (Substitutions, Expr)) +type Substitutions = [(Expr, Expr)] convert :: [AST] -> [AST] -convert = - traverseFiles (collectDescriptionsM collectPart) - (map . convertDescription) +convert files = + if needsFlattening + then files + else traverseFiles + (collectDescriptionsM collectPart) + (map . convertDescription) + files where -- we can only collect/map non-extern interfaces and modules collectPart :: Description -> Writer PartInfos () collectPart (Part _ False kw _ name ports items) = tell $ Map.singleton name $ PartInfo kw ports items collectPart _ = return () + -- multidimensional instances need to be flattened before this + -- conversion can proceed + needsFlattening = + getAny $ execWriter $ mapM (collectDescriptionsM checkPart) files + checkPart :: Description -> Writer Any () + checkPart (Part _ _ _ _ _ _ items) = + mapM (collectNestedModuleItemsM checkItem) items >> return () + checkPart _ = return () + checkItem :: ModuleItem -> Writer Any () + checkItem (Instance _ _ _ rs _) = when (length rs > 1) $ tell $ Any True + checkItem _ = return () convertDescription :: PartInfos -> Description -> Description convertDescription _ (Part _ _ Interface _ name _ _) = @@ -54,26 +72,25 @@ convertDescription parts (Part attrs extern Module lifetime name ports items) = traverseModuleItemM :: ModuleItem -> Scoper [ModportDecl] ModuleItem traverseModuleItemM (Modport modportName modportDecls) = insertElem modportName modportDecls >> return (Generate []) - traverseModuleItemM (instanceItem @ (Instance _ _ _ [] _)) = + traverseModuleItemM (instanceItem @ Instance{}) = if maybePartInfo == Nothing then return instanceItem else if partKind == Interface then -- inline instantiation of an interface convertNested $ Generate $ map GenModuleItem $ - inlineInstance [] [] + inlineInstance rs [] partItems instanceName paramBindings portBindings else if not $ null (extractModportInstances partInfo) then do modports <- embedScopes (\l () -> l) () -- inline instantiation of a module convertNested $ Generate $ map GenModuleItem $ - inlineInstance + inlineInstance rs (modportBindings modports) - (modportSubstitutions modports) partItems instanceName paramBindings portBindings else return instanceItem where - Instance part rawParamBindings instanceName [] rawPortBindings = + Instance part rawParamBindings instanceName rs rawPortBindings = instanceItem maybePartInfo = Map.lookup part parts Just partInfo = maybePartInfo @@ -85,19 +102,73 @@ convertDescription parts (Part attrs extern Module lifetime name ports items) = modportInstances = extractModportInstances partInfo modportBindings modports = mapMaybe - (inferModportBinding modports modportInstances) portBindings - modportSubstitutions modports = concatMap - (expandModportBinding modports) (modportBindings modports) + (inferModportBinding modports modportInstances) $ + map (second $ addImpliedSlice modports) portBindings + second f = \(a, b) -> (a, f b) traverseModuleItemM other = return other - -- determines the underlying modport and interface instances associated - -- with the given port binding, if it is a modport binding + -- add explicit slices for bindings of entire modport instance arrays + addImpliedSlice :: Scopes [ModportDecl] -> Expr -> Expr + addImpliedSlice modports (orig @ (Dot expr modportName)) = + case lookupElem modports (InstArrKey expr) of + Just (_, _, InstArrVal l r) -> + Dot (Range expr NonIndexed (l, r)) modportName + _ -> orig + addImpliedSlice modports expr = + case lookupElem modports (InstArrKey expr) of + Just (_, _, InstArrVal l r) -> + Range expr NonIndexed (l, r) + _ -> expr + + -- elaborates and resolves provided modport bindings inferModportBinding :: Scopes [ModportDecl] -> ModportInstances -> PortBinding -> Maybe ModportBinding - inferModportBinding _ _ ("", _) = - error "internal inferModportBinding invariant violated" inferModportBinding modports modportInstances (portName, expr) = + if maybeInfo == Nothing + then Nothing + else Just (portName, modportBinding) + where + modportBinding = (substitutions, replaceBit modportE) + substitutions = + genSubstitutions modports base instanceE modportE + maybeInfo = + lookupModportBinding modports modportInstances portName bitd + Just (instanceE, modportE) = maybeInfo + + (exprUndot, bitd) = case expr of + Dot subExpr x -> (subExpr, Dot bitdUndot x) + _ -> (expr, bitdUndot) + bitdUndot = case exprUndot of + Range subExpr _ _ -> Bit subExpr taggedOffset + Bit subExpr _ -> Bit subExpr untaggedOffset + _ -> exprUndot + bitReplacement = case exprUndot of + Range _ mode range -> \e -> Range e mode range + Bit _ idx -> flip Bit idx + _ -> id + base = case exprUndot of + Range{} -> Bit (Ident portName) Tag + _ -> Ident portName + + untaggedOffset = Ident $ modportBaseName portName + taggedOffset = BinOp Add Tag untaggedOffset + + replaceBit :: Expr -> Expr + replaceBit (Bit subExpr idx) = + if idx == untaggedOffset || idx == taggedOffset + then bitReplacement subExpr + else Bit subExpr idx + replaceBit (Dot subExpr x) = + Dot (replaceBit subExpr) x + replaceBit (Ident x) = Ident x + replaceBit _ = error "replaceBit invariant violated" + + -- determines the underlying modport and interface instances associated + -- with the given port binding, if it is a modport binding + lookupModportBinding :: Scopes [ModportDecl] -> ModportInstances + -> Identifier -> Expr -> Maybe (Expr, Expr) + lookupModportBinding modports modportInstances portName expr = if bindingIsModport then -- provided specific instance modport foundModport expr @@ -116,11 +187,11 @@ convertDescription parts (Part attrs extern Module lifetime name ports items) = modportName = case lookup portName modportInstances of Just (_, x) -> x Nothing -> error $ "can't deduce modport for interface " - ++ " bound to port " ++ portName + ++ show expr ++ " bound to port " ++ portName foundModport modportE = - Just (portName, (instanceE, modportE)) - where instanceE = findInstance modportE + Just (findInstance modportE, qualifyModport modportE) + findInstance :: Expr -> Expr findInstance e = case lookupElem modports (Dot e "") of @@ -128,18 +199,27 @@ convertDescription parts (Part attrs extern Module lifetime name ports items) = Bit e' _ -> findInstance e' Dot e' _ -> findInstance e' _ -> error "internal invariant violated" - Just (accesses, _, _) -> - foldl accessToExpr (Ident topName) rest - where Access topName Nil : rest = init accesses + Just (accesses, _, _) -> accessesToExpr $ init accesses + qualifyModport :: Expr -> Expr + qualifyModport e = + case lookupElem modports e of + Just (accesses, _, _) -> accessesToExpr accesses + Nothing -> accessesToExpr $ init accesses + where Just (accesses, _, _) = + lookupElem modports (Dot e "") + accessesToExpr :: [Access] -> Expr + accessesToExpr accesses = + foldl accessToExpr (Ident topName) rest + where Access topName Nil : rest = accesses accessToExpr :: Expr -> Access -> Expr accessToExpr e (Access x Nil) = Dot e x accessToExpr e (Access x i) = Bit (Dot e x) i -- expand a modport binding into a series of expression substitutions - expandModportBinding :: Scopes [ModportDecl] - -> ModportBinding -> [(Expr, Expr)] - expandModportBinding modports (portName, (instanceE, modportE)) = - (Ident portName, instanceE) : + genSubstitutions :: Scopes [ModportDecl] -> Expr -> Expr -> Expr + -> [(Expr, Expr)] + genSubstitutions modports baseE instanceE modportE = + (baseE, instanceE) : map toPortBinding modportDecls where a = lookupElem modports modportE @@ -148,7 +228,7 @@ convertDescription parts (Part attrs extern Module lifetime name ports items) = if a == Nothing then b else a toPortBinding (_, x, e) = (x', e') where - x' = Dot (Ident portName) x + x' = Dot baseE x e' = prefixExpr e prefixExpr :: Expr -> Expr prefixExpr (Ident x) = @@ -212,22 +292,29 @@ impliedModport = -- convert an interface-bound module instantiation or an interface instantiation -- into a series of equivalent inlined module items -inlineInstance :: [ModportBinding] -> [(Expr, Expr)] -> [ModuleItem] +inlineInstance :: [Range] -> [ModportBinding] -> [ModuleItem] -> Identifier -> [ParamBinding] -> [PortBinding] -> [ModuleItem] -inlineInstance modportBindings modportSubstitutions items +inlineInstance ranges modportBindings items instanceName instanceParams instancePorts = comment : + map (MIPackageItem . Decl) bindingBaseParams ++ map (MIPackageItem . Decl) parameterBinds ++ - Generate [GenBlock instanceName $ map GenModuleItem items'] + (wrapInstance $ GenBlock instanceName $ map GenModuleItem items') : portBindings where items' = evalScoper traverseDeclM traverseModuleItemM traverseGenItemM traverseStmtM "" $ map (traverseNestedModuleItems rewriteItem) $ if null modportBindings - then Modport "" (impliedModport items) : items + then dimensionModport : bundleModport : items else items + -- synthetic modports to be collected and removed after inlining + bundleModport = Modport "" (impliedModport items) + dimensionModport = if not isArray + then MIPackageItem $ Decl $ CommentDecl "not an instance array" + else InstArrEncoded arrayLeft arrayRight + inlineKind = if null modportBindings then "interface" @@ -239,7 +326,7 @@ inlineInstance modportBindings modportSubstitutions items filter notSubstituted instancePorts notSubstituted :: PortBinding -> Bool notSubstituted (portName, _) = - lookup (portName) modportBindings == Nothing + lookup portName modportBindings == Nothing rewriteItem :: ModuleItem -> ModuleItem rewriteItem = @@ -273,6 +360,7 @@ inlineInstance modportBindings modportSubstitutions items traverseStmtLHSsM traverseLHSM -- used for replacing usages of modports in the module being inlined + modportSubstitutions = concatMap (fst . snd) modportBindings lhsReplacements = map (\(x, y) -> (toLHS x, toLHS y)) exprReplacements exprReplacements = filter ((/= Nil) . snd) modportSubstitutions -- LHSs are replaced using simple substitutions @@ -287,10 +375,21 @@ inlineInstance modportBindings modportSubstitutions items else traverseSinglyNestedLHSs (tagLHS scopes) lhs replaceLHS :: LHS -> LHS replaceLHS (LHSDot lhs "@") = lhs + replaceLHS (LHSDot (LHSBit lhs elt) field) = + case lookup (LHSDot (LHSBit lhs Tag) field) lhsReplacements of + Just resolved -> replaceLHSArrTag elt resolved + Nothing -> LHSDot (replaceLHS $ LHSBit lhs elt) field + replaceLHS (LHSBit lhs elt) = + case lookup (LHSBit lhs Tag) lhsReplacements of + Just resolved -> replaceLHSArrTag elt resolved + Nothing -> LHSBit (replaceLHS lhs) elt replaceLHS lhs = case lookup lhs lhsReplacements of Just lhs' -> lhs' Nothing -> traverseSinglyNestedLHSs replaceLHS lhs + replaceLHSArrTag :: Expr -> LHS -> LHS + replaceLHSArrTag = + traverseNestedLHSs . (traverseLHSExprs . replaceArrTag) -- top-level expressions may be modports bound to other modports traverseExprM :: Expr -> Scoper Expr Expr traverseExprM expr = do @@ -321,17 +420,40 @@ inlineInstance modportBindings modportSubstitutions items replaceExpr' expr replaceExpr' :: Expr -> Expr replaceExpr' (Dot expr "@") = expr + replaceExpr' (Dot (Bit expr elt) field) = + case lookup (Dot (Bit expr Tag) field) exprReplacements of + Just resolved -> replaceArrTag (replaceExpr' elt) resolved + Nothing -> Dot (replaceExpr' $ Bit expr elt) field + replaceExpr' (Bit expr elt) = + case lookup (Bit expr Tag) exprReplacements of + Just resolved -> replaceArrTag (replaceExpr' elt) resolved + Nothing -> Bit (replaceExpr' expr) (replaceExpr' elt) replaceExpr' expr = case lookup expr exprReplacements of Just expr' -> expr' Nothing -> traverseSinglyNestedExprs replaceExpr' expr + replaceArrTag :: Expr -> Expr -> Expr + replaceArrTag replacement Tag = replacement + replaceArrTag replacement expr = + traverseSinglyNestedExprs (replaceArrTag replacement) expr removeModportInstance :: ModuleItem -> ModuleItem removeModportInstance (MIPackageItem (Decl (Variable d t x a e))) = MIPackageItem $ Decl $ - if lookup x modportBindings /= Nothing - then CommentDecl $ "removed modport instance " ++ x - else Variable d t x a e + if maybeModportBinding == Nothing then + Variable d t x a e + else if makeBindingBaseExpr modportE == Nothing then + CommentDecl $ "removed modport instance " ++ x + else if null a then + localparam (modportBaseName x) bindingBaseExpr + else + localparam (modportBaseName x) $ + BinOp Sub bindingBaseExpr (sliceLo NonIndexed $ head a) + where + maybeModportBinding = lookup x modportBindings + Just (_, modportE) = maybeModportBinding + bindingBaseExpr = Ident $ bindingBaseName ++ x + removeModportInstance other = other removeDeclDir :: ModuleItem -> ModuleItem @@ -343,6 +465,26 @@ inlineInstance modportBindings modportSubstitutions items _ -> t removeDeclDir other = other + -- capture the lower bound for each modport array binding + bindingBaseParams = map makeBindingBaseParam modportBindings + makeBindingBaseParam :: ModportBinding -> Decl + makeBindingBaseParam (portName, (_, modportE)) = + case makeBindingBaseExpr modportE of + Just expr -> localparam (bindingBaseName ++ portName) expr + Nothing -> CommentDecl "no-op" + bindingBaseName = "_sv2v_bind_base_" ++ shortHash instanceName ++ "_" + makeBindingBaseExpr :: Expr -> Maybe Expr + makeBindingBaseExpr modportE = + case modportE of + Dot (Range _ mode range) _ -> Just $ sliceLo mode range + Range _ mode range -> Just $ sliceLo mode range + Dot (Bit _ idx) _ -> Just idx + Bit _ idx -> Just idx + _ -> Nothing + + localparam :: Identifier -> Expr -> Decl + localparam = Param Localparam (Implicit Unspecified []) + paramTmp = "_tmp_" ++ (shortHash (items, instanceName)) ++ "_" parameterBinds = map makeParameterBind instanceParams @@ -371,10 +513,15 @@ inlineInstance modportBindings modportSubstitutions items portBindingItem :: PortBinding -> Maybe ModuleItem portBindingItem (_, Nil) = Nothing portBindingItem (ident, expr) = + Just $ wrapInstance $ GenModuleItem $ if findDeclDir ident == Input - then bind (LHSDot (LHSIdent instanceName) ident) expr - else bind (toLHS expr) (Dot (Ident instanceName) ident) - where bind a b = Just $ Assign AssignOptionNone a b + then bind (LHSDot (inj LHSBit LHSIdent) ident) expr + else bind (toLHS expr) (Dot (inj Bit Ident) ident) + where + bind = Assign AssignOptionNone + inj bit idn = if null ranges + then idn instanceName + else bit (idn instanceName) (Ident loopVar) declDirs = execWriter $ mapM (collectDeclsM collectDeclDir) items @@ -397,6 +544,50 @@ inlineInstance modportBindings modportSubstitutions items Nothing -> error $ "trying to bind an " ++ inlineKind ++ " output to " ++ show expr ++ " but that can't be an LHS" + -- for instance arrays, a unique identifier to be used as a genvar + loopVar = "_sv2v_arr_" ++ shortHash (instanceName, ranges) + + isArray = not $ null ranges + [arrayRange @ (arrayLeft, arrayRight)] = ranges + + -- wrap the given item in a generate loop if necessary + wrapInstance :: GenItem -> ModuleItem + wrapInstance item = + Generate $ + if not isArray then + [item] + else + [ GenModuleItem (Genvar loopVar) + , GenFor inits cond incr item + ] + where + inits = (loopVar, arrayLeft) + cond = endianCondExpr arrayRange + (BinOp Ge (Ident loopVar) arrayRight) + (BinOp Le (Ident loopVar) arrayRight) + incr = (loopVar, AsgnOp Add, step) + step = endianCondExpr arrayRange + (UniOp UniSub $ RawNum 1) (RawNum 1) + +-- used for modport array binding offset placeholders +pattern Tag :: Expr +pattern Tag = Ident "%" + +modportBaseName :: Identifier -> Identifier +modportBaseName = (++) "_sv2v_base_" + +-- the dimensions of interface instance arrays are encoded as synthetic modports +-- during inlining, enabling subsequent modport bindings to implicitly use the +-- bounds of the interface instance array when the bounds are unspecified +pattern InstArrName :: Identifier +pattern InstArrName = "~instance_array_dimensions~" +pattern InstArrVal :: Expr -> Expr -> [ModportDecl] +pattern InstArrVal l r = [(Local, "l", l), (Local, "r", r)] +pattern InstArrKey :: Expr -> Expr +pattern InstArrKey expr = Dot (Bit expr (RawNum 0)) InstArrName +pattern InstArrEncoded :: Expr -> Expr -> ModuleItem +pattern InstArrEncoded l r = Modport InstArrName (InstArrVal l r) + type Binding t = (Identifier, t) -- give a set of bindings explicit names resolveBindings :: Show t => [Identifier] -> [Binding t] -> [Binding t] @@ -419,3 +610,9 @@ parameterNames = collectDeclM (Param Parameter _ x _) = tell [x] collectDeclM (ParamType Parameter x _) = tell [x] collectDeclM _ = return () + +-- determines the lower bound for the given slice +sliceLo :: PartSelectMode -> Range -> Expr +sliceLo NonIndexed (l, r) = endianCondExpr (l, r) r l +sliceLo IndexedPlus (base, _) = base +sliceLo IndexedMinus (base, len) = BinOp Add (BinOp Sub base len) (RawNum 1) diff --git a/test/basic/interface_array.sv b/test/basic/interface_array.sv new file mode 100644 index 0000000..86b3fc3 --- /dev/null +++ b/test/basic/interface_array.sv @@ -0,0 +1,173 @@ +interface Interface(i); + input i; + logic v; + logic o; + task tick; + $display("I i = %b, v = %b, o = %b", i, v, o); + endtask + initial $display("Hello I'm Interface!"); + modport ModportA( + input .i(i ^ 1'b1), + output v + ); + modport ModportB( + input .i(i), + output .v(o) + ); +endinterface + +module ModuleA(i); + parameter flip = 0; + Interface i; + assign i.v = i.i ^ 1'(flip); + task tick; + $display("A i.v = %b", i.v); + endtask + initial $display("Hello I'm ModuleA %0d!", flip); +endmodule + +module ModuleASet(is); + parameter flip2 = 0; + parameter flip1 = 0; + parameter flip0 = 0; + Interface is [2:0]; + assign is[2].v = is[2].i ^ 1'(flip2); + assign is[1].v = is[1].i ^ 1'(flip1); + assign is[0].v = is[0].i ^ 1'(flip0); + task tick; + $display("AS i.v = %b", is[2].v); + $display("AS i.v = %b", is[1].v); + $display("AS i.v = %b", is[0].v); + endtask + initial begin + $display("Hello I'm ModuleASet %0d %0d %0d!", flip2, flip1, flip0); + end +endmodule + +module ModuleCSet(is); + parameter flip2 = 0; + parameter flip1 = 0; + parameter flip0 = 0; + Interface.ModportB is [2:0]; + assign is[2].v = is[2].i ^ 1'(flip2); + assign is[1].v = is[1].i ^ 1'(flip1); + assign is[0].v = is[0].i ^ 1'(flip0); + task tick; + $display("CS i.v = %b", is[2].v); + $display("CS i.v = %b", is[1].v); + $display("CS i.v = %b", is[0].v); + endtask + initial begin + $display("Hello I'm ModuleCSet %0d %0d %0d!", flip2, flip1, flip0); + end +endmodule + +module ModuleB(is); + parameter WIDTH = 1; + Interface is [WIDTH-1:0]; + logic [WIDTH-1:0] i_concat; + logic [WIDTH-1:0] v_concat; + for (genvar i = WIDTH - 1; i >= 0; i = i - 1) begin + assign i_concat[i] = is[i].i; + assign v_concat[i] = is[i].v; + end + task tick; + $display("B i_concat = %b, v_concat = %b", i_concat, v_concat); + bn.tick; + endtask + initial $display("Hello I'm ModuleB %0d!", WIDTH); + ModuleBNested #(WIDTH) bn(is); +endmodule + +module ModuleBNested(is); + parameter WIDTH = 1; + Interface is [WIDTH-1:0]; + logic [WIDTH-1:0] i_concat; + logic [WIDTH-1:0] v_concat; + for (genvar i = WIDTH - 1; i >= 0; i = i - 1) begin + assign i_concat[i] = is[i].i; + assign v_concat[i] = is[i].v; + end + task tick; + $display("BN i_concat = %b, v_concat = %b", i_concat, v_concat); + endtask +endmodule + +module top; + logic inp; + + Interface intfX[2:0](inp); + + ModuleA #(0) xa2(intfX[2]); + ModuleA #(1) xa1(intfX[1]); + ModuleA #(1) xa0(intfX[0]); + + ModuleB #(3) xb20(intfX[2:0]); + ModuleB #(2) xb21(intfX[2:1]); + ModuleB #(1) xb22(intfX[2:2]); + ModuleB #(1) xb11(intfX[1:1]); + ModuleB #(1) xb00(intfX[0:0]); + ModuleB #(3) xbf(intfX); + + ModuleASet #(1, 1, 0) xs(intfX[2:0].ModportB); + + Interface intfY[2:0](inp); + + ModuleA #(0) ya2(intfY[2].ModportA); + ModuleA #(1) ya1(intfY[1].ModportA); + ModuleA #(1) ya0(intfY[0].ModportA); + + ModuleB #(3) yb20(intfY[2:0].ModportA); + ModuleB #(2) yb21(intfY[2:1].ModportA); + ModuleB #(1) yb22(intfY[2:2].ModportA); + ModuleB #(1) yb11(intfY[1:1].ModportA); + ModuleB #(1) yb00(intfY[0:0].ModportA); + ModuleB #(3) ybf(intfY.ModportA); + + ModuleCSet #(0, 0, 1) ys(intfY[2:0]); + + initial begin + inp = 0; tick; + inp = 1; tick; + inp = 0; tick; + inp = 1; tick; + end + + task tick; + #1; + + intfX[2].tick; + intfX[1].tick; + intfX[0].tick; + + xa2.tick; + xa1.tick; + xa0.tick; + + xb20.tick; + xb21.tick; + xb22.tick; + xb11.tick; + xb00.tick; + xbf.tick; + + xs.tick; + + intfY[2].tick; + intfY[1].tick; + intfY[0].tick; + + ya2.tick; + ya1.tick; + ya0.tick; + + yb20.tick; + yb21.tick; + yb22.tick; + yb11.tick; + yb00.tick; + ybf.tick; + + ys.tick; + endtask +endmodule diff --git a/test/basic/interface_array.v b/test/basic/interface_array.v new file mode 100644 index 0000000..05f571d --- /dev/null +++ b/test/basic/interface_array.v @@ -0,0 +1,99 @@ +module top; + reg inp; + + initial begin + $display("Hello I'm Interface!"); + $display("Hello I'm Interface!"); + $display("Hello I'm Interface!"); + + $display("Hello I'm ModuleA 0!"); + $display("Hello I'm ModuleA 1!"); + $display("Hello I'm ModuleA 1!"); + + $display("Hello I'm ModuleB 3!"); + $display("Hello I'm ModuleB 2!"); + $display("Hello I'm ModuleB 1!"); + $display("Hello I'm ModuleB 1!"); + $display("Hello I'm ModuleB 1!"); + $display("Hello I'm ModuleB 3!"); + + $display("Hello I'm ModuleASet 1 1 0!"); + + $display("Hello I'm Interface!"); + $display("Hello I'm Interface!"); + $display("Hello I'm Interface!"); + + $display("Hello I'm ModuleA 0!"); + $display("Hello I'm ModuleA 1!"); + $display("Hello I'm ModuleA 1!"); + + $display("Hello I'm ModuleB 3!"); + $display("Hello I'm ModuleB 2!"); + $display("Hello I'm ModuleB 1!"); + $display("Hello I'm ModuleB 1!"); + $display("Hello I'm ModuleB 1!"); + $display("Hello I'm ModuleB 3!"); + + $display("Hello I'm ModuleCSet 0 0 1!"); + + inp = 0; tick; + inp = 1; tick; + inp = 0; tick; + inp = 1; tick; + end + + task tick; begin + #1; + + $display("I i = %b, v = %b, o = %b", inp, inp, inp ^ 1'b1); + $display("I i = %b, v = %b, o = %b", inp, inp ^ 1'b1, inp ^ 1'b1); + $display("I i = %b, v = %b, o = %b", inp, inp ^ 1'b1, inp); + + $display("A i.v = %b", inp); + $display("A i.v = %b", inp ^ 1'b1); + $display("A i.v = %b", inp ^ 1'b1); + + $display("B i_concat = %b, v_concat = %b", {3 {inp}}, {inp, inp ^ 1'b1, inp ^ 1'b1}); + $display("BN i_concat = %b, v_concat = %b", {3 {inp}}, {inp, inp ^ 1'b1, inp ^ 1'b1}); + $display("B i_concat = %b, v_concat = %b", {2 {inp}}, {inp, inp ^ 1'b1}); + $display("BN i_concat = %b, v_concat = %b", {2 {inp}}, {inp, inp ^ 1'b1}); + $display("B i_concat = %b, v_concat = %b", {1 {inp}}, inp); + $display("BN i_concat = %b, v_concat = %b", {1 {inp}}, inp); + $display("B i_concat = %b, v_concat = %b", {1 {inp}}, inp ^ 1'b1); + $display("BN i_concat = %b, v_concat = %b", {1 {inp}}, inp ^ 1'b1); + $display("B i_concat = %b, v_concat = %b", {1 {inp}}, inp ^ 1'b1); + $display("BN i_concat = %b, v_concat = %b", {1 {inp}}, inp ^ 1'b1); + $display("B i_concat = %b, v_concat = %b", {3 {inp}}, {inp, inp ^ 1'b1, inp ^ 1'b1}); + $display("BN i_concat = %b, v_concat = %b", {3 {inp}}, {inp, inp ^ 1'b1, inp ^ 1'b1}); + + $display("AS i.v = %b", inp ^ 1'b1); + $display("AS i.v = %b", inp ^ 1'b1); + $display("AS i.v = %b", inp); + + $display("I i = %b, v = %b, o = %b", inp, inp ^ 1'b1, inp); + $display("I i = %b, v = %b, o = %b", inp, inp, inp); + $display("I i = %b, v = %b, o = %b", inp, inp, inp ^ 1'b1); + + $display("A i.v = %b", inp ^ 1'b1); + $display("A i.v = %b", inp); + $display("A i.v = %b", inp); + + $display("B i_concat = %b, v_concat = %b", {3 {~inp}}, {inp ^ 1'b1, inp, inp}); + $display("BN i_concat = %b, v_concat = %b", {3 {~inp}}, {inp ^ 1'b1, inp, inp}); + $display("B i_concat = %b, v_concat = %b", {2 {~inp}}, {inp ^ 1'b1, inp}); + $display("BN i_concat = %b, v_concat = %b", {2 {~inp}}, {inp ^ 1'b1, inp}); + $display("B i_concat = %b, v_concat = %b", {1 {~inp}}, inp ^ 1'b1); + $display("BN i_concat = %b, v_concat = %b", {1 {~inp}}, inp ^ 1'b1); + $display("B i_concat = %b, v_concat = %b", {1 {~inp}}, inp); + $display("BN i_concat = %b, v_concat = %b", {1 {~inp}}, inp); + $display("B i_concat = %b, v_concat = %b", {1 {~inp}}, inp); + $display("BN i_concat = %b, v_concat = %b", {1 {~inp}}, inp); + $display("B i_concat = %b, v_concat = %b", {3 {~inp}}, {inp ^ 1'b1, inp, inp}); + $display("BN i_concat = %b, v_concat = %b", {3 {~inp}}, {inp ^ 1'b1, inp, inp}); + + $display("CS i.v = %b", inp); + $display("CS i.v = %b", inp); + $display("CS i.v = %b", inp ^ 1'b1); + end + endtask +endmodule diff --git a/test/basic/interface_array_multi.sv b/test/basic/interface_array_multi.sv new file mode 100644 index 0000000..312a6bd --- /dev/null +++ b/test/basic/interface_array_multi.sv @@ -0,0 +1,10 @@ +interface Interface; + logic x; +endinterface + +module top; + Interface intfs[3:2][8:5](); + for (genvar x = 2; x <= 3; x = x + 1) + for (genvar y = 5; y <= 8; y = y + 1) + assign intfs[x][y].x = '1; +endmodule diff --git a/test/basic/interface_array_multi.v b/test/basic/interface_array_multi.v new file mode 100644 index 0000000..7233acd --- /dev/null +++ b/test/basic/interface_array_multi.v @@ -0,0 +1,11 @@ +module top; + generate + if (1) begin : block + wire [3:2][8:5] xs; + genvar x, y; + for (x = 2; x <= 3; x = x + 1) + for (y = 5; y <= 8; y = y + 1) + assign xs[x][y] = 1'b1; + end + endgenerate +endmodule diff --git a/test/basic/interface_array_single.sv b/test/basic/interface_array_single.sv new file mode 100644 index 0000000..a47e303 --- /dev/null +++ b/test/basic/interface_array_single.sv @@ -0,0 +1,49 @@ +`define SHADOW \ + integer i; \ + Interface intfs[1:0](); + +interface Interface; + integer x; + modport ModportA(input .x(x + 1)); + modport ModportB(input .x(x + 2)); +endinterface + +module ModuleA(intf); + Interface intf; + `SHADOW + initial #1 $display("ModuleA got %0d", intf.x); + ModuleN n(intf); +endmodule + +module ModuleB(intf); + Interface.ModportA intf; + `SHADOW + initial #1 $display("ModuleB got %0d", intf.x); + ModuleN n(intf); +endmodule + +module ModuleC(intf); + Interface.ModportB intf; + `SHADOW + initial #1 $display("ModuleC got %0d", intf.x); + ModuleN n(intf); +endmodule + +module ModuleN(intf); + Interface intf; + `SHADOW + initial #1 $display("ModuleN got %0d", intf.x); +endmodule + +module top; + Interface intfs[4:8](); + `define LOOP for (genvar i = 4; i <= 8; ++i) + `LOOP initial intfs[i].x = i ** 2; + `LOOP ModuleA a1(intfs[i]); + `LOOP ModuleA a2(intfs[i].ModportA); + `LOOP ModuleA a3(intfs[i].ModportB); + `LOOP ModuleB b1(intfs[i]); + `LOOP ModuleB b2(intfs[i].ModportA); + `LOOP ModuleC c1(intfs[i]); + `LOOP ModuleC c2(intfs[i].ModportB); +endmodule diff --git a/test/basic/interface_array_single.v b/test/basic/interface_array_single.v new file mode 100644 index 0000000..6dccd45 --- /dev/null +++ b/test/basic/interface_array_single.v @@ -0,0 +1,25 @@ +module top; + genvar i; + generate + + initial #1; + + `define PRINT(X, offset) \ + for (i = 4; i <= 8; i = i + 1) \ + initial begin \ + $display(`"Module``X got %0d`", i ** 2 + offset); \ + $display("ModuleN got %0d", i ** 2 + offset); \ + end + + `PRINT(A, 0) + `PRINT(A, 1) + `PRINT(A, 2) + + `PRINT(B, 1) + `PRINT(B, 1) + + `PRINT(C, 2) + `PRINT(C, 2) + + endgenerate +endmodule diff --git a/test/basic/interface_array_slice.sv b/test/basic/interface_array_slice.sv new file mode 100644 index 0000000..deac8cb --- /dev/null +++ b/test/basic/interface_array_slice.sv @@ -0,0 +1,49 @@ +interface Interface; + logic x; +endinterface + +module Module(intfs); + parameter LEFT = 0; + parameter RIGHT = 0; + Interface intfs[LEFT:RIGHT]; + logic [LEFT:RIGHT] xs; + localparam LO = LEFT > RIGHT ? RIGHT : LEFT; + localparam HI = LEFT > RIGHT ? LEFT : RIGHT; + for (genvar i = LO; i <= HI; i = i + 1) begin + // intentional shadowing of dimension constants + localparam LEFT = 0; + localparam RIGHT = 0; + assign xs[i] = intfs[i].x; + end +endmodule + +module Instance(); + parameter LEFT = 0; + parameter RIGHT = 0; + + parameter INNER_LEFT = 0; + parameter INNER_RIGHT = 0; + parameter INNER_OFFSET = 0; + + reg [LEFT:RIGHT] xs; + + localparam DIR = LEFT >= RIGHT ? -1 : 1; + Interface intfs[LEFT:RIGHT](); + generate + genvar i; + for (i = LEFT; i <= RIGHT; i = i + DIR) + assign intfs[i].x = xs[i]; + endgenerate + + // intentional name collision with localparams in the module + localparam LO = INNER_LEFT >= INNER_RIGHT ? INNER_RIGHT : INNER_LEFT; + localparam HI = INNER_LEFT >= INNER_RIGHT ? INNER_LEFT : INNER_RIGHT; + localparam LEN = HI - LO + 1; + + Module #(INNER_LEFT + INNER_OFFSET, INNER_RIGHT + INNER_OFFSET) + l(intfs[INNER_LEFT:INNER_RIGHT]); + Module #(INNER_LEFT + INNER_OFFSET, INNER_RIGHT + INNER_OFFSET) + m(intfs[LO+:LEN]); + Module #(INNER_LEFT + INNER_OFFSET, INNER_RIGHT + INNER_OFFSET) + n(intfs[HI-:LEN]); +endmodule diff --git a/test/basic/interface_array_slice.v b/test/basic/interface_array_slice.v new file mode 100644 index 0000000..2d43d92 --- /dev/null +++ b/test/basic/interface_array_slice.v @@ -0,0 +1,27 @@ +module Module(xs); + parameter LEFT = 0; + parameter RIGHT = 0; + input wire [LEFT:RIGHT] xs; +endmodule + +module Instance(); + parameter LEFT = 0; + parameter RIGHT = 0; + + parameter INNER_LEFT = 0; + parameter INNER_RIGHT = 0; + parameter INNER_OFFSET = 0; + + reg [LEFT:RIGHT] xs; + + localparam LO = INNER_LEFT >= INNER_RIGHT ? INNER_RIGHT : INNER_LEFT; + localparam HI = INNER_LEFT >= INNER_RIGHT ? INNER_LEFT : INNER_RIGHT; + localparam LEN = HI - LO + 1; + + Module #(INNER_LEFT + INNER_OFFSET, INNER_RIGHT + INNER_OFFSET) + l(xs[INNER_LEFT:INNER_RIGHT]); + Module #(INNER_LEFT + INNER_OFFSET, INNER_RIGHT + INNER_OFFSET) + m(xs[LO+:LEN]); + Module #(INNER_LEFT + INNER_OFFSET, INNER_RIGHT + INNER_OFFSET) + n(xs[HI-:LEN]); +endmodule diff --git a/test/basic/interface_array_slice_tb.v b/test/basic/interface_array_slice_tb.v new file mode 100644 index 0000000..2cfcb7f --- /dev/null +++ b/test/basic/interface_array_slice_tb.v @@ -0,0 +1,53 @@ +module Test(); + parameter BASE = 0; + parameter SIZE = 0; + parameter DIR = 0; + + localparam LEFT = BASE; + localparam RIGHT = BASE + DIR * (SIZE - 1); + + genvar left, right, offset; + generate + for (left = LEFT + SIZE; left <= RIGHT + SIZE; left = left + 1) + for (right = LEFT + SIZE; right <= RIGHT + SIZE; right = right + 1) + if ((left - right) * DIR <= 0) + for (offset = -2 + SIZE; offset <= 2 + SIZE; offset = offset + 1) + begin + + Instance #( + LEFT, RIGHT, + left - SIZE, right - SIZE, offset - SIZE + ) i(); + + initial begin + i.xs = 1; + while (i.xs != 0) begin + #1; + $display("LEFT=%2d RIGHT=%2d INNER_LEFT=%2d INNER_RIGHT=%2d INNER_OFFSET=%2d i.xs=%b i.l.xs=%b i.m.xs=%b i.n.xs=%b", + LEFT, RIGHT, + left - SIZE, right - SIZE, offset - SIZE, + i.xs, i.l.xs, i.m.xs, i.n.xs); + i.xs = i.xs + 1; + end + end + + end + endgenerate +endmodule + +module Suite(); + parameter SIZE = 0; + genvar base; + generate + for (base = -2 + SIZE; base <= 2 + SIZE; base = base + 1) begin + Test #(base - SIZE, SIZE, -1) b(); + Test #(base - SIZE, SIZE, 1) f(); + end + endgenerate +endmodule + +module top; + Suite #(2) s2(); + Suite #(3) s3(); + Suite #(4) s4(); +endmodule