mirror of https://github.com/zachjs/sv2v.git
213 lines
9.0 KiB
Haskell
213 lines
9.0 KiB
Haskell
{- sv2v
|
|
- Author: Zachary Snow <zach@zachjs.com>
|
|
-
|
|
- 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.
|
|
-
|
|
- Parameters and localparams with integer vector types become implicit.
|
|
-
|
|
- 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.
|
|
-}
|
|
|
|
module Convert.Logic (convert) where
|
|
|
|
import Control.Monad (when)
|
|
import Control.Monad.Writer.Strict
|
|
import qualified Data.Map.Strict as Map
|
|
import qualified Data.Set as Set
|
|
|
|
import Convert.Scoper
|
|
import Convert.Traverse
|
|
import Language.SystemVerilog.AST
|
|
|
|
type Ports = Map.Map Identifier [(Identifier, Direction)]
|
|
type Location = [Identifier]
|
|
type Locations = Set.Set Location
|
|
type ST = ScoperT Type (Writer Locations)
|
|
|
|
convert :: [AST] -> [AST]
|
|
convert =
|
|
traverseFiles
|
|
(collectDescriptionsM collectPortsM)
|
|
(traverseDescriptions . convertDescription)
|
|
where
|
|
collectPortsM :: Description -> Writer Ports ()
|
|
collectPortsM orig@(Part _ _ _ _ name portNames _) =
|
|
tell $ Map.singleton name ports
|
|
where
|
|
ports = zip portNames (map lookupDir portNames)
|
|
dirs = execWriter $ collectModuleItemsM collectDeclDirsM orig
|
|
lookupDir :: Identifier -> Direction
|
|
lookupDir portName =
|
|
case lookup portName dirs of
|
|
Just dir -> dir
|
|
Nothing -> Inout
|
|
collectPortsM _ = return ()
|
|
collectDeclDirsM :: ModuleItem -> Writer [(Identifier, Direction)] ()
|
|
collectDeclDirsM (MIPackageItem (Decl (Variable dir _ ident _ _))) =
|
|
when (dir /= Local) $ tell [(ident, dir)]
|
|
collectDeclDirsM (MIPackageItem (Decl (Net dir _ _ _ ident _ _))) =
|
|
when (dir /= Local) $ tell [(ident, dir)]
|
|
collectDeclDirsM _ = return ()
|
|
|
|
convertDescription :: Ports -> Description -> Description
|
|
convertDescription ports description =
|
|
evalScoper $ scopeModule conScoper description
|
|
where
|
|
locations = execWriter $ evalScoperT $ scopePart locScoper description
|
|
-- write down which vars are procedurally assigned
|
|
locScoper = scopeModuleItem traverseDeclM return return traverseStmtM
|
|
-- rewrite reg continuous assignments and output port connections
|
|
conScoper = scopeModuleItem
|
|
(rewriteDeclM locations) (traverseModuleItemM ports) return return
|
|
|
|
traverseModuleItemM :: Ports -> ModuleItem -> Scoper Type ModuleItem
|
|
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 =
|
|
case lookupElem scopes lhs of
|
|
Just (_, _, t) -> tell [isRegType t]
|
|
_ -> tell [False]
|
|
|
|
always_comb = AlwaysC Always . Timing (Event EventStar)
|
|
|
|
fixModuleItem :: ModuleItem -> ModuleItem
|
|
-- rewrite bad continuous assignments to use procedural assignments
|
|
fixModuleItem (Assign AssignOptionNone lhs expr) =
|
|
if not (isReg lhs)
|
|
then Assign AssignOptionNone lhs expr
|
|
else
|
|
Generate $ map GenModuleItem
|
|
[ MIPackageItem $ Decl decl
|
|
, Assign AssignOptionNone (LHSIdent x) expr
|
|
, always_comb $ Asgn AsgnOpEq Nothing lhs (Ident x)
|
|
]
|
|
where
|
|
decl = Net Local TWire DefaultStrength t x [] Nil
|
|
t = Implicit Unspecified [r]
|
|
r = (DimsFn FnBits $ Right $ lhsToExpr lhs, RawNum 1)
|
|
x = "sv2v_tmp_" ++ shortHash (lhs, expr)
|
|
-- rewrite port bindings to use temporary nets where necessary
|
|
fixModuleItem (Instance moduleName params instanceName rs bindings) =
|
|
if null newItems
|
|
then Instance moduleName params instanceName rs bindings
|
|
else Generate $ map GenModuleItem $
|
|
comment : newItems ++
|
|
[Instance moduleName params instanceName rs bindings']
|
|
where
|
|
comment = MIPackageItem $ Decl $ CommentDecl
|
|
"rewrote reg-to-output bindings"
|
|
(bindings', newItemsList) = unzip $ map fixBinding bindings
|
|
newItems = concat newItemsList
|
|
fixBinding :: PortBinding -> (PortBinding, [ModuleItem])
|
|
fixBinding (portName, expr) =
|
|
if not outputBound || not usesReg
|
|
then ((portName, expr), [])
|
|
else ((portName, tmpExpr), items)
|
|
where
|
|
outputBound = portDir == Just Output
|
|
usesReg = Just True == fmap isReg (exprToLHS expr)
|
|
portDir = maybeModulePorts >>= lookup portName
|
|
tmp = "sv2v_tmp_" ++ instanceName ++ "_" ++ portName
|
|
tmpExpr = Ident tmp
|
|
decl = Net Local TWire DefaultStrength t tmp [] Nil
|
|
t = Implicit Unspecified [r]
|
|
r = (DimsFn FnBits $ Right expr, RawNum 1)
|
|
items =
|
|
[ MIPackageItem $ Decl decl
|
|
, always_comb $ Asgn AsgnOpEq Nothing lhs tmpExpr]
|
|
Just lhs = exprToLHS expr
|
|
maybeModulePorts = Map.lookup moduleName ports
|
|
fixModuleItem other = other
|
|
|
|
traverseDeclM :: Decl -> ST Decl
|
|
traverseDeclM decl@(Variable _ t x _ _) =
|
|
insertElem x t >> return decl
|
|
traverseDeclM decl@(Net _ _ _ t x _ _) =
|
|
insertElem x t >> return decl
|
|
traverseDeclM decl = return decl
|
|
|
|
rewriteDeclM :: Locations -> Decl -> Scoper Type Decl
|
|
rewriteDeclM locations (Variable d (IntegerVector TLogic sg rs) x a e) = do
|
|
accesses <- localAccessesM x
|
|
let location = map accessName accesses
|
|
let usedAsReg = Set.member location locations
|
|
blockLogic <- withinProcedureM
|
|
if blockLogic || usedAsReg || e /= Nil
|
|
then do
|
|
let d' = if d == Inout then Output else d
|
|
let t' = IntegerVector TReg sg rs
|
|
insertElem accesses t'
|
|
return $ Variable d' t' x a e
|
|
else do
|
|
let t' = Implicit sg rs
|
|
insertElem accesses t'
|
|
return $ Net d TWire DefaultStrength t' x a e
|
|
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
|
|
rewriteDeclM _ (Net d n s (IntegerVector _ sg rs) x a e) =
|
|
insertElem x t >> return (Net d n s t x a e)
|
|
where t = Implicit sg rs
|
|
rewriteDeclM _ decl@(Net _ _ _ t x _ _) =
|
|
insertElem x t >> return decl
|
|
rewriteDeclM _ (Param s (IntegerVector _ sg []) x e) =
|
|
return $ Param s (Implicit sg [(zero, zero)]) x e
|
|
where zero = RawNum 0
|
|
rewriteDeclM _ (Param s (IntegerVector _ sg rs) x e) =
|
|
return $ Param s (Implicit sg rs) x e
|
|
rewriteDeclM _ decl = return decl
|
|
|
|
traverseStmtM :: Stmt -> ST Stmt
|
|
traverseStmtM (Asgn op Just{} lhs expr) =
|
|
-- ignore the timing LHSs
|
|
traverseStmtM $ Asgn op Nothing lhs expr
|
|
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
|
|
|
|
collectLHSM :: LHS -> ST ()
|
|
collectLHSM lhs = do
|
|
details <- lookupElemM lhs
|
|
case details of
|
|
Just (accesses, _, _) ->
|
|
lift $ tell $ Set.singleton location
|
|
where location = map accessName accesses
|
|
Nothing -> return ()
|