Logic.hs 8.96 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.Strict
29
import qualified Data.Map.Strict as Map
30 31
import qualified Data.Set as Set

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

36
type Ports = Map.Map Identifier [(Identifier, Direction)]
37 38
type Location = [Identifier]
type Locations = Set.Set Location
39
type ST = ScoperT Type (Writer Locations)
40

41
convert :: [AST] -> [AST]
42 43 44 45
convert =
    traverseFiles
        (collectDescriptionsM collectPortsM)
        (traverseDescriptions . convertDescription)
46 47
    where
        collectPortsM :: Description -> Writer Ports ()
48
        collectPortsM orig@(Part _ _ _ _ name portNames _) =
49
            tell $ Map.singleton name ports
50
            where
51 52 53 54 55 56
                ports = zip portNames (map lookupDir portNames)
                dirs = execWriter $ collectModuleItemsM collectDeclDirsM orig
                lookupDir :: Identifier -> Direction
                lookupDir portName =
                    case lookup portName dirs of
                        Just dir -> dir
57
                        Nothing -> Inout
58
        collectPortsM _ = return ()
59
        collectDeclDirsM :: ModuleItem -> Writer [(Identifier, Direction)] ()
60 61 62 63
        collectDeclDirsM (MIPackageItem (Decl (Variable dir _ ident _ _))) =
            when (dir /= Local) $ tell [(ident, dir)]
        collectDeclDirsM (MIPackageItem (Decl (Net dir _ _ _ ident _ _))) =
            when (dir /= Local) $ tell [(ident, dir)]
64
        collectDeclDirsM _ = return ()
65

66
convertDescription :: Ports -> Description -> Description
67 68
convertDescription ports description =
    evalScoper $ scopeModule conScoper description
69
    where
70
        locations = execWriter $ evalScoperT $ scopePart locScoper description
71
        -- write down which vars are procedurally assigned
72 73 74 75
        locScoper = scopeModuleItem traverseDeclM return return traverseStmtM
        -- rewrite reg continuous assignments and output port connections
        conScoper = scopeModuleItem
            (rewriteDeclM locations) (traverseModuleItemM ports) return return
76

77
traverseModuleItemM :: Ports -> ModuleItem -> Scoper Type ModuleItem
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
traverseModuleItemM ports = embedScopes $ traverseModuleItem ports

traverseModuleItem :: Ports -> Scopes Type -> ModuleItem -> ModuleItem
traverseModuleItem ports scopes =
    fixModuleItem
    where
        isReg :: LHS -> Bool
        isReg =
            or . execWriter . collectNestedLHSsM isReg'
            where
                isRegType :: Type -> Bool
                isRegType (IntegerVector TReg _ _) = True
                isRegType _ = False
                isReg' :: LHS -> Writer [Bool] ()
                isReg' lhs =
93
                    case lookupElem scopes lhs of
94 95
                        Just (_, _, t) -> tell [isRegType t]
                        _ -> tell [False]
96

97
        always_comb = AlwaysC Always . Timing (Event EventStar)
98

99
        fixModuleItem :: ModuleItem -> ModuleItem
100
        -- rewrite bad continuous assignments to use procedural assignments
101
        fixModuleItem (Assign AssignOptionNone lhs expr) =
102 103 104 105
            if not (isReg lhs)
                then Assign AssignOptionNone lhs expr
                else
                    Generate $ map GenModuleItem
106
                    [ MIPackageItem $ Decl decl
107
                    , Assign AssignOptionNone (LHSIdent x) expr
108
                    , always_comb $ Asgn AsgnOpEq Nothing lhs (Ident x)
109
                    ]
110
            where
111 112 113
                decl = Net Local TWire DefaultStrength t x [] Nil
                t = Implicit Unspecified [r]
                r = (DimsFn FnBits $ Right $ lhsToExpr lhs, RawNum 1)
114
                x = "sv2v_tmp_" ++ shortHash (lhs, expr)
115
        -- rewrite port bindings to use temporary nets where necessary
116
        fixModuleItem (Instance moduleName params instanceName rs bindings) =
117 118 119
            if null newItems
                then Instance moduleName params instanceName rs bindings
                else Generate $ map GenModuleItem $
120
                    comment : newItems ++
121 122
                    [Instance moduleName params instanceName rs bindings']
            where
123 124
                comment = MIPackageItem $ Decl $ CommentDecl
                    "rewrote reg-to-output bindings"
125
                (bindings', newItemsList) = unzip $ map fixBinding bindings
126
                newItems = concat newItemsList
127 128
                fixBinding :: PortBinding -> (PortBinding, [ModuleItem])
                fixBinding (portName, expr) =
129
                    if not outputBound || not usesReg
Zachary Snow committed
130 131
                        then ((portName, expr), [])
                        else ((portName, tmpExpr), items)
132
                    where
133 134
                        outputBound = portDir == Just Output
                        usesReg = Just True == fmap isReg (exprToLHS expr)
135
                        portDir = maybeModulePorts >>= lookup portName
136 137
                        tmp = "sv2v_tmp_" ++ instanceName ++ "_" ++ portName
                        tmpExpr = Ident tmp
138 139 140
                        decl = Net Local TWire DefaultStrength t tmp [] Nil
                        t = Implicit Unspecified [r]
                        r = (DimsFn FnBits $ Right expr, RawNum 1)
141
                        items =
142
                            [ MIPackageItem $ Decl decl
143
                            , always_comb $ Asgn AsgnOpEq Nothing lhs tmpExpr]
144
                        Just lhs = exprToLHS expr
145
                maybeModulePorts = Map.lookup moduleName ports
146 147
        fixModuleItem other = other

148
traverseDeclM :: Decl -> ST Decl
149
traverseDeclM decl@(Variable _ t x _ _) =
150
    insertElem x t >> return decl
151
traverseDeclM decl@(Net _ _ _ t x _ _) =
152
    insertElem x t >> return decl
153 154
traverseDeclM decl = return decl

155 156 157
rewriteDeclM :: Locations -> Decl -> Scoper Type Decl
rewriteDeclM locations (Variable d (IntegerVector TLogic sg rs) x a e) = do
    accesses <- localAccessesM x
158
    let location = map accessName accesses
159
    let usedAsReg = Set.member location locations
160
    blockLogic <- withinProcedureM
161
    if blockLogic || usedAsReg || e /= Nil
162 163 164
        then do
            let d' = if d == Inout then Output else d
            let t' = IntegerVector TReg sg rs
165
            insertElem accesses t'
166 167 168
            return $ Variable d' t' x a e
        else do
            let t' = Implicit sg rs
169
            insertElem accesses t'
170
            return $ Net d TWire DefaultStrength t' x a e
171 172 173 174 175 176 177 178 179 180
rewriteDeclM locations decl@(Variable d t x a e) = do
    inProcedure <- withinProcedureM
    case (d, t, inProcedure) of
        -- Reinterpret `input reg` module ports as `input logic`. We still don't
        -- treat `logic` and `reg` as the same keyword, as specifying `reg`
        -- explicitly is typically expected to flow downstream.
        (Input, IntegerVector TReg sg rs, False) ->
            rewriteDeclM locations $ Variable Input t' x a e
            where t' = IntegerVector TLogic sg rs
        _ -> insertElem x t >> return decl
181
rewriteDeclM _ (Net d n s (IntegerVector _ sg rs) x a e) =
182 183
    insertElem x t >> return (Net d n s t x a e)
    where t = Implicit sg rs
184
rewriteDeclM _ decl@(Net _ _ _ t x _ _) =
185
    insertElem x t >> return decl
186
rewriteDeclM _ (Param s (IntegerVector _ sg []) x e) =
187
    return $ Param s (Implicit sg [(zero, zero)]) x e
188
    where zero = RawNum 0
189
rewriteDeclM _ (Param s (IntegerVector _ sg rs) x e) =
190
    return $ Param s (Implicit sg rs) x e
191
rewriteDeclM _ decl = return decl
192 193

traverseStmtM :: Stmt -> ST Stmt
194 195 196
traverseStmtM (Asgn op Just{} lhs expr) =
    -- ignore the timing LHSs
    traverseStmtM $ Asgn op Nothing lhs expr
197 198 199 200 201 202
traverseStmtM stmt@(Subroutine (Ident f) (Args (_ : Ident x : _) [])) =
    when (f == "$readmemh" || f == "$readmemb") (collectLHSM $ LHSIdent x)
        >> return stmt
traverseStmtM stmt =
    collectStmtLHSsM (collectNestedLHSsM collectLHSM) stmt
        >> return stmt
203

204 205
collectLHSM :: LHS -> ST ()
collectLHSM lhs = do
206
    details <- lookupElemM lhs
207
    case details of
208 209 210
        Just (accesses, _, _) ->
            lift $ tell $ Set.singleton location
            where location = map accessName accesses
211
        Nothing -> return ()