Logic.hs 7.19 KB
Newer Older
1 2 3
{- sv2v
 - Author: Zachary Snow <zach@zachjs.com>
 -
4 5 6 7 8 9
 - Conversion from `logic` to `wire` or `reg`
 -
 - We convert a module-level logic to a reg if it is assigned to in an always or
 - initial block. Other module-level logics become wires. All other logics
 - (i.e., in a function) become regs.
 -
10 11
 - Parameters and localparams with integer vector types become implicit.
 -
12 13 14 15 16 17 18 19 20 21 22 23
 - The struct conversion and Verilog-2005's lack of permissive net vs. variable
 - resolution leads to some interesting special cases for this conversion, as
 - parts of a struct may be used as a variable, while other parts may be used as
 - a net.
 -
 - 1) If a reg, or a portion thereof, is assigned by a continuous assignment
 - item, then that assignment is converted to a procedural assignment within an
 - added `always_comb` item.
 -
 - 2) If a reg, or a portion thereof, is bound to an output port, then that
 - binding is replaced by a temporary net declaration, and a procedural
 - assignment is added which updates the reg to the value of the new net.
24 25 26 27
 -}

module Convert.Logic (convert) where

28
import Control.Monad.Writer
29
import qualified Data.Map.Strict as Map
30 31
import qualified Data.Set as Set

32
import Convert.Traverse
33 34
import Language.SystemVerilog.AST

35
type Idents = Set.Set Identifier
36
type Ports = Map.Map (Identifier, Identifier) Direction
37

38
convert :: [AST] -> [AST]
39 40 41 42
convert =
    traverseFiles
        (collectDescriptionsM collectPortsM)
        (traverseDescriptions . convertDescription)
43 44
    where
        collectPortsM :: Description -> Writer Ports ()
45
        collectPortsM (orig @ (Part _ _ _ _ name portNames _)) =
46 47 48
            collectModuleItemsM collectPortDirsM orig
            where
                collectPortDirsM :: ModuleItem -> Writer Ports ()
49
                collectPortDirsM (MIPackageItem (Decl (Variable dir _ ident _ _))) =
50 51 52 53 54 55 56 57 58
                    if dir == Local then
                        return ()
                    else if elem ident portNames then
                        tell $ Map.singleton (name, ident) dir
                    else
                        error $ "encountered decl with a dir that isn't a port: "
                            ++ show (dir, ident)
                collectPortDirsM _ = return ()
        collectPortsM _ = return ()
59

60 61
convertDescription :: Ports -> Description -> Description
convertDescription ports orig =
62
    if shouldConvert
63
        then converted
64
        else orig
65
    where
66
        shouldConvert = case orig of
67 68
            Part _ _ Interface _ _ _ _ -> False
            Part _ _ Module _ _ _ _ -> True
69
            PackageItem _ -> True
70
            Package _ _ _ -> False
71 72 73 74

        origIdents = execWriter (collectModuleItemsM regIdents orig)
        fixed = traverseModuleItems fixModuleItem orig
        fixedIdents = execWriter (collectModuleItemsM regIdents fixed)
75
        conversion = traverseDecls convertDecl . convertModuleItem
76 77 78
        converted = traverseModuleItems conversion fixed

        fixModuleItem :: ModuleItem -> ModuleItem
79
        -- rewrite bad continuous assignments to use procedural assignments
80 81
        fixModuleItem (Assign Nothing lhs expr) =
            if Set.disjoint usedIdents origIdents
82
                then Assign Nothing lhs expr
83
                else AlwaysC AlwaysComb $ Asgn AsgnOpEq Nothing lhs expr
84 85
            where
                usedIdents = execWriter $ collectNestedLHSsM lhsIdents lhs
86
        -- rewrite port bindings to use temporary nets where necessary
87
        fixModuleItem (Instance moduleName params instanceName rs bindings) =
88 89 90
            if null newItems
                then Instance moduleName params instanceName rs bindings
                else Generate $ map GenModuleItem $
91
                    comment : newItems ++
92 93
                    [Instance moduleName params instanceName rs bindings']
            where
94 95
                comment = MIPackageItem $ Decl $ CommentDecl
                    "rewrote reg-to-output bindings"
96 97 98 99
                (bindings', newItemsList) = unzip $ map fixBinding bindings
                newItems = concat newItemsList
                fixBinding :: PortBinding -> (PortBinding, [ModuleItem])
                fixBinding (portName, Just expr) =
100
                    if portDir /= Just Output || Set.disjoint usedIdents origIdents
101 102 103 104 105 106 107 108
                        then ((portName, Just expr), [])
                        else ((portName, Just tmpExpr), items)
                    where
                        portDir = Map.lookup (moduleName, portName) ports
                        usedIdents = execWriter $
                            collectNestedExprsM exprIdents expr
                        tmp = "sv2v_tmp_" ++ instanceName ++ "_" ++ portName
                        tmpExpr = Ident tmp
109
                        t = Net TWire Unspecified [(DimsFn FnBits $ Right expr, Number "1")]
110
                        items =
111
                            [ MIPackageItem $ Decl $ Variable Local t tmp [] Nothing
112
                            , AlwaysC AlwaysComb $ Asgn AsgnOpEq Nothing lhs tmpExpr]
113 114 115 116 117 118 119
                        lhs = case exprToLHS expr of
                            Just l -> l
                            Nothing ->
                                error $ "bad non-lhs, non-net expr "
                                    ++ show expr ++ " connected to output port "
                                    ++ portName ++ " of " ++ instanceName
                fixBinding other = (other, [])
120 121
        fixModuleItem other = other

122
        -- rewrite variable declarations to have the correct type
123
        convertModuleItem (MIPackageItem (Decl (Variable dir (IntegerVector _ sg mr) ident a me))) =
124
            MIPackageItem $ Decl $ Variable dir (t mr) ident a me
125
            where
126
                t = if Set.member ident fixedIdents
127
                    then IntegerVector TReg sg
128
                    else Net TWire sg
129
        convertModuleItem other = other
130 131
        -- all other logics (i.e. inside of functions) become regs
        convertDecl :: Decl -> Decl
132 133
        convertDecl (Param s (IntegerVector _ sg rs) x e) =
            Param s (Implicit sg rs) x e
134 135
        convertDecl (Variable d (IntegerVector TLogic sg rs) x a me) =
            Variable d (IntegerVector TReg sg rs) x a me
136
        convertDecl other = other
137

138
regIdents :: ModuleItem -> Writer Idents ()
139 140
regIdents (AlwaysC _ stmt) = do
    collectNestedStmtsM collectReadMemsM stmt
141
    collectNestedStmtsM (collectStmtLHSsM (collectNestedLHSsM lhsIdents)) $
142
        traverseNestedStmts removeTimings stmt
143
    where
144 145 146
        removeTimings :: Stmt -> Stmt
        removeTimings (Timing _ s) = s
        removeTimings other = other
147 148 149 150 151 152
        collectReadMemsM :: Stmt -> Writer Idents ()
        collectReadMemsM (Subroutine (Ident f) (Args (_ : Just (Ident x) : _) [])) =
            if f == "$readmemh" || f == "$readmemb"
                then tell $ Set.singleton x
                else return ()
        collectReadMemsM _ = return ()
153 154
regIdents (Initial stmt) =
    regIdents $ AlwaysC Always stmt
155 156
regIdents (Final stmt) =
    regIdents $ AlwaysC Always stmt
157
regIdents _ = return ()
158 159

lhsIdents :: LHS -> Writer Idents ()
160
lhsIdents (LHSIdent x) = tell $ Set.singleton x
161
lhsIdents _ = return () -- the collector recurses for us
162 163 164 165

exprIdents :: Expr -> Writer Idents ()
exprIdents (Ident x) = tell $ Set.singleton x
exprIdents _ = return () -- the collector recurses for us