{-# LANGUAGE PatternSynonyms #-} {- sv2v - Author: Zachary Snow <zach@zachjs.com> - - Conversion for unbased, unsized literals ('0, '1, 'z, 'x) - - The literals are given a binary base, a size of 1, and are made signed to - allow sign extension. For context-determined expressions, the converted - literals are explicitly cast to the appropriate context-determined size. - - As a special case, unbased, unsized literals which take on the size of a - module port binding are replaced with a hierarchical reference to an - appropriately sized constant which is injected into the instantiated module's - definition. This allows these literals to be used for parameterized ports - without further complicating other conversions, as hierarchical references - are not allowed within constant expressions. -} module Convert.UnbasedUnsized (convert) where import Control.Monad.Writer import Convert.Traverse import Language.SystemVerilog.AST data ExprContext = SelfDetermined | ContextDetermined Expr deriving (Eq, Show) type Port = Either Identifier Int data Bind = Bind { bModule :: Identifier , bBit :: Char , bPort :: Port } deriving (Eq, Show) type Binds = [Bind] convert :: [AST] -> [AST] convert files = map (traverseDescriptions $ convertDescription binds) files' where (files', binds) = runWriter $ mapM (mapM $ traverseModuleItemsM convertModuleItemM) files convertDescription :: Binds -> Description -> Description convertDescription [] other = other convertDescription binds (Part attrs extern kw lifetime name ports items) = Part attrs extern kw lifetime name ports items' where binds' = filter ((== name) . bModule) binds items' = removeDupes [] $ items ++ map (bindItem ports) binds' removeDupes :: [Identifier] -> [ModuleItem] -> [ModuleItem] removeDupes _ [] = [] removeDupes existing (item @ (MIPackageItem (Decl decl)) : is) = case decl of Param Localparam _ x _ -> if elem x existing then removeDupes existing is else item : removeDupes (x : existing) is _ -> item : removeDupes existing is removeDupes existing (item : is) = item : removeDupes existing is convertDescription _ other = other bindName :: Bind -> Identifier bindName (Bind _ ch (Left x)) = "sv2v_uub_" ++ ch : '_' : x bindName (Bind m ch (Right i)) = bindName $ Bind m ch (Left $ show i) bindItem :: [Identifier] -> Bind -> ModuleItem bindItem ports bind = MIPackageItem $ Decl $ Param Localparam typ name expr where portName = lookupPort ports (bPort bind) size = DimsFn FnBits $ Right $ Ident portName rng = (BinOp Sub size (RawNum 1), RawNum 0) typ = Implicit Unspecified [rng] name = bindName bind expr = literalFor $ bBit bind lookupPort :: [Identifier] -> Port -> Identifier lookupPort _ (Left x) = x lookupPort ports (Right i) = if i < length ports then ports !! i else error $ "out of bounds bort binding " ++ show (ports, i) convertModuleItemM :: ModuleItem -> Writer Binds ModuleItem convertModuleItemM (Instance moduleName params instanceName [] bindings) = do bindings' <- mapM (uncurry convertBinding) $ zip bindings [0..] let item = Instance moduleName params instanceName [] bindings' return $ convertModuleItem item where tag = Ident ":uub:" convertBinding :: PortBinding -> Int -> Writer Binds PortBinding convertBinding (portName, expr) idx = do let port = if null portName then Right idx else Left portName let expr' = convertExpr (ContextDetermined tag) expr expr'' <- traverseNestedExprsM (replaceBindingExpr port) expr' return (portName, expr'') replaceBindingExpr :: Port -> Expr -> Writer Binds Expr replaceBindingExpr port (orig @ (Cast Right{} (ConvertedUU a b))) = do let ch = charForBit a b if orig == sizedLiteralFor tag ch then do let bind = Bind moduleName ch port tell [bind] let expr = Dot (Ident instanceName) (bindName bind) return expr else return orig replaceBindingExpr _ other = return other convertModuleItemM other = return $ convertModuleItem other convertModuleItem :: ModuleItem -> ModuleItem convertModuleItem = traverseExprs (convertExpr SelfDetermined) . traverseTypes (traverseNestedTypes convertType) literalFor :: Char -> Expr literalFor 'Z' = literalFor 'z' literalFor 'X' = literalFor 'x' literalFor '0' = Number $ Based 1 True Binary 0 0 literalFor '1' = Number $ Based 1 True Binary 1 0 literalFor 'x' = Number $ Based 1 True Binary 0 1 literalFor 'z' = Number $ Based 1 True Binary 1 1 literalFor ch = error $ "unexpected unbased-unsized digit: " ++ [ch] pattern ConvertedUU :: Integer -> Integer -> Expr pattern ConvertedUU a b = Number (Based 1 True Binary a b) charForBit :: Integer -> Integer -> Char charForBit 0 0 = '0' charForBit 1 0 = '1' charForBit 0 1 = 'x' charForBit 1 1 = 'z' charForBit _ _ = error "charForBit invariant violated" sizedLiteralFor :: Expr -> Char -> Expr sizedLiteralFor expr ch = Cast (Right size) (literalFor ch) where size = DimsFn FnBits $ Right expr convertExpr :: ExprContext -> Expr -> Expr convertExpr _ (DimsFn fn (Right e)) = DimsFn fn $ Right $ convertExpr SelfDetermined e convertExpr _ (Cast te e) = Cast te $ convertExpr SelfDetermined e convertExpr _ (Concat exprs) = Concat $ map (convertExpr SelfDetermined) exprs convertExpr _ (Pattern items) = Pattern $ zip (map fst items) (map (convertExpr SelfDetermined . snd) items) convertExpr _ (Call expr (Args pnArgs kwArgs)) = Call expr $ Args pnArgs' kwArgs' where pnArgs' = map (convertExpr SelfDetermined) pnArgs Pattern kwArgs' = convertExpr SelfDetermined $ Pattern kwArgs convertExpr _ (Repeat count exprs) = Repeat count $ map (convertExpr SelfDetermined) exprs convertExpr SelfDetermined (Mux cond (e1 @ UU{}) (e2 @ UU{})) = Mux (convertExpr SelfDetermined cond) (convertExpr SelfDetermined e1) (convertExpr SelfDetermined e2) convertExpr SelfDetermined (Mux cond e1 e2) = Mux (convertExpr SelfDetermined cond) (convertExpr (ContextDetermined e2) e1) (convertExpr (ContextDetermined e1) e2) convertExpr (ContextDetermined expr) (Mux cond e1 e2) = Mux (convertExpr SelfDetermined cond) (convertExpr context e1) (convertExpr context e2) where context = ContextDetermined expr convertExpr SelfDetermined (BinOp op e1 e2) = if isPeerSizedBinOp op || isParentSizedBinOp op then BinOp op (convertExpr (ContextDetermined e2) e1) (convertExpr (ContextDetermined e1) e2) else BinOp op (convertExpr SelfDetermined e1) (convertExpr SelfDetermined e2) convertExpr (ContextDetermined expr) (BinOp op e1 e2) = if isPeerSizedBinOp op then BinOp op (convertExpr (ContextDetermined e2) e1) (convertExpr (ContextDetermined e1) e2) else if isParentSizedBinOp op then BinOp op (convertExpr context e1) (convertExpr context e2) else BinOp op (convertExpr SelfDetermined e1) (convertExpr SelfDetermined e2) where context = ContextDetermined expr convertExpr context (UniOp op expr) = if isSizedUniOp op then UniOp op (convertExpr context expr) else UniOp op (convertExpr SelfDetermined expr) convertExpr SelfDetermined (UU ch) = literalFor ch convertExpr (ContextDetermined expr) (UU ch) = sizedLiteralFor expr ch convertExpr _ other = other pattern UU :: Char -> Expr pattern UU ch = Number (UnbasedUnsized ch) convertType :: Type -> Type convertType (TypeOf e) = TypeOf $ convertExpr SelfDetermined e convertType other = other isParentSizedBinOp :: BinOp -> Bool isParentSizedBinOp BitAnd = True isParentSizedBinOp BitXor = True isParentSizedBinOp BitXnor = True isParentSizedBinOp BitOr = True isParentSizedBinOp Mul = True isParentSizedBinOp Div = True isParentSizedBinOp Mod = True isParentSizedBinOp Add = True isParentSizedBinOp Sub = True isParentSizedBinOp _ = False isPeerSizedBinOp :: BinOp -> Bool isPeerSizedBinOp Eq = True isPeerSizedBinOp Ne = True isPeerSizedBinOp TEq = True isPeerSizedBinOp TNe = True isPeerSizedBinOp WEq = True isPeerSizedBinOp WNe = True isPeerSizedBinOp Lt = True isPeerSizedBinOp Le = True isPeerSizedBinOp Gt = True isPeerSizedBinOp Ge = True isPeerSizedBinOp _ = False isSizedUniOp :: UniOp -> Bool isSizedUniOp = (/= LogNot)