mirror of https://github.com/zachjs/sv2v.git
165 lines
7.1 KiB
Haskell
165 lines
7.1 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.Writer
|
|
import qualified Data.Map.Strict as Map
|
|
import qualified Data.Set as Set
|
|
|
|
import Convert.Traverse
|
|
import Language.SystemVerilog.AST
|
|
|
|
type Idents = Set.Set Identifier
|
|
type Ports = Map.Map (Identifier, Identifier) Direction
|
|
|
|
convert :: [AST] -> [AST]
|
|
convert =
|
|
traverseFiles
|
|
(collectDescriptionsM collectPortsM)
|
|
(traverseDescriptions . convertDescription)
|
|
where
|
|
collectPortsM :: Description -> Writer Ports ()
|
|
collectPortsM (orig @ (Part _ _ _ _ name portNames _)) =
|
|
collectModuleItemsM collectPortDirsM orig
|
|
where
|
|
collectPortDirsM :: ModuleItem -> Writer Ports ()
|
|
collectPortDirsM (MIPackageItem (Decl (Variable dir _ ident _ _))) =
|
|
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 ()
|
|
|
|
convertDescription :: Ports -> Description -> Description
|
|
convertDescription ports orig =
|
|
if shouldConvert
|
|
then converted
|
|
else orig
|
|
where
|
|
shouldConvert = case orig of
|
|
Part _ _ Interface _ _ _ _ -> False
|
|
Part _ _ Module _ _ _ _ -> True
|
|
PackageItem _ -> True
|
|
Package _ _ _ -> False
|
|
|
|
origIdents = execWriter (collectModuleItemsM regIdents orig)
|
|
fixed = traverseModuleItems fixModuleItem orig
|
|
fixedIdents = execWriter (collectModuleItemsM regIdents fixed)
|
|
conversion = traverseDecls convertDecl . convertModuleItem
|
|
converted = traverseModuleItems conversion fixed
|
|
|
|
fixModuleItem :: ModuleItem -> ModuleItem
|
|
-- rewrite bad continuous assignments to use procedural assignments
|
|
fixModuleItem (Assign Nothing lhs expr) =
|
|
if Set.disjoint usedIdents origIdents
|
|
then Assign Nothing lhs expr
|
|
else AlwaysC AlwaysComb $ AsgnBlk AsgnOpEq lhs expr
|
|
where
|
|
usedIdents = execWriter $ collectNestedLHSsM lhsIdents lhs
|
|
-- 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 $
|
|
(MIPackageItem $ Comment "rewrote reg-to-output bindings") :
|
|
newItems ++
|
|
[Instance moduleName params instanceName rs bindings']
|
|
where
|
|
(bindings', newItemsList) = unzip $ map fixBinding bindings
|
|
newItems = concat newItemsList
|
|
fixBinding :: PortBinding -> (PortBinding, [ModuleItem])
|
|
fixBinding (portName, Just expr) =
|
|
if portDir /= Just Output || Set.disjoint usedIdents origIdents
|
|
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
|
|
t = Net TWire Unspecified [(DimsFn FnBits $ Right expr, Number "1")]
|
|
items =
|
|
[ MIPackageItem $ Decl $ Variable Local t tmp [] Nothing
|
|
, AlwaysC AlwaysComb $ AsgnBlk AsgnOpEq lhs tmpExpr]
|
|
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, [])
|
|
fixModuleItem other = other
|
|
|
|
-- rewrite variable declarations to have the correct type
|
|
convertModuleItem (MIPackageItem (Decl (Variable dir (IntegerVector _ sg mr) ident a me))) =
|
|
MIPackageItem $ Decl $ Variable dir (t mr) ident a me
|
|
where
|
|
t = if Set.member ident fixedIdents
|
|
then IntegerVector TReg sg
|
|
else Net TWire sg
|
|
convertModuleItem other = other
|
|
-- all other logics (i.e. inside of functions) become regs
|
|
convertDecl :: Decl -> Decl
|
|
convertDecl (Param s (IntegerVector _ sg rs) x e) =
|
|
Param s (Implicit sg rs) x e
|
|
convertDecl (Variable d (IntegerVector TLogic sg rs) x a me) =
|
|
Variable d (IntegerVector TReg sg rs) x a me
|
|
convertDecl other = other
|
|
|
|
regIdents :: ModuleItem -> Writer Idents ()
|
|
regIdents (AlwaysC _ stmt) = do
|
|
collectNestedStmtsM collectReadMemsM stmt
|
|
collectNestedStmtsM (collectStmtLHSsM (collectNestedLHSsM lhsIdents)) $
|
|
traverseNestedStmts removeTimings stmt
|
|
where
|
|
removeTimings :: Stmt -> Stmt
|
|
removeTimings (Timing _ s) = s
|
|
removeTimings other = other
|
|
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 ()
|
|
regIdents (Initial stmt) =
|
|
regIdents $ AlwaysC Always stmt
|
|
regIdents (Final stmt) =
|
|
regIdents $ AlwaysC Always stmt
|
|
regIdents _ = return ()
|
|
|
|
lhsIdents :: LHS -> Writer Idents ()
|
|
lhsIdents (LHSIdent x) = tell $ Set.singleton x
|
|
lhsIdents _ = return () -- the collector recurses for us
|
|
|
|
exprIdents :: Expr -> Writer Idents ()
|
|
exprIdents (Ident x) = tell $ Set.singleton x
|
|
exprIdents _ = return () -- the collector recurses for us
|