mirror of https://github.com/zachjs/sv2v.git
support and convert jumps: break, continue, and return
This commit is contained in:
parent
08c38e6193
commit
8ae89a7bd4
|
|
@ -21,6 +21,7 @@ import qualified Convert.Foreach
|
|||
import qualified Convert.FuncRet
|
||||
import qualified Convert.Interface
|
||||
import qualified Convert.IntTypes
|
||||
import qualified Convert.Jump
|
||||
import qualified Convert.KWArgs
|
||||
import qualified Convert.Logic
|
||||
import qualified Convert.LogOp
|
||||
|
|
@ -30,7 +31,6 @@ import qualified Convert.NestPI
|
|||
import qualified Convert.Package
|
||||
import qualified Convert.ParamType
|
||||
import qualified Convert.RemoveComments
|
||||
import qualified Convert.Return
|
||||
import qualified Convert.Simplify
|
||||
import qualified Convert.SizeCast
|
||||
import qualified Convert.StarPort
|
||||
|
|
@ -75,7 +75,7 @@ phases excludes =
|
|||
, Convert.Package.convert
|
||||
, Convert.Enum.convert
|
||||
, Convert.NestPI.convert
|
||||
, Convert.Return.convert
|
||||
, Convert.Jump.convert
|
||||
, Convert.Foreach.convert
|
||||
, selectExclude (Job.Interface, Convert.Interface.convert)
|
||||
, selectExclude (Job.Always , Convert.AlwaysKW.convert)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,245 @@
|
|||
{- sv2v
|
||||
- Author: Zachary Snow <zach@zachjs.com>
|
||||
-
|
||||
- Conversion for `return`, `break`, and `continue`
|
||||
-
|
||||
- Because Verilog-2005 has no jumping statements, this conversion ends up
|
||||
- producing significantly more verbose code to acheive the same control flow.
|
||||
-}
|
||||
|
||||
module Convert.Jump (convert) where
|
||||
|
||||
import Control.Monad.State
|
||||
|
||||
import Convert.Traverse
|
||||
import Language.SystemVerilog.AST
|
||||
|
||||
data JumpType
|
||||
= JTNone
|
||||
| JTContinue
|
||||
| JTBreak
|
||||
| JTReturn
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
data Info = Info
|
||||
{ sJumpType :: JumpType
|
||||
, sLoopID :: Identifier
|
||||
}
|
||||
|
||||
convert :: [AST] -> [AST]
|
||||
convert = map $ traverseDescriptions $ traverseModuleItems convertModuleItem
|
||||
|
||||
convertModuleItem :: ModuleItem -> ModuleItem
|
||||
convertModuleItem (MIPackageItem (Function ml t f decls stmtsOrig)) =
|
||||
if sJumpType finalState == JTNone || sJumpType finalState == JTReturn
|
||||
then MIPackageItem $ Function ml t f decls stmts'
|
||||
else error "illegal jump statement within task"
|
||||
where
|
||||
stmts = map (traverseNestedStmts convertReturn) stmtsOrig
|
||||
convertReturn :: Stmt -> Stmt
|
||||
convertReturn (Return Nil) = Return Nil
|
||||
convertReturn (Return e) =
|
||||
Block Seq "" []
|
||||
[ asgn f e
|
||||
, Return Nil
|
||||
]
|
||||
convertReturn other = other
|
||||
initialState = Info { sJumpType = JTNone, sLoopID = "" }
|
||||
(stmts', finalState) = runState (convertStmts stmts) initialState
|
||||
convertModuleItem (MIPackageItem (Task ml f decls stmts)) =
|
||||
if sJumpType finalState == JTNone || sJumpType finalState == JTReturn
|
||||
then MIPackageItem $ Task ml f decls $ stmts'
|
||||
else error "illegal jump statement within task"
|
||||
where
|
||||
initialState = Info { sJumpType = JTNone, sLoopID = "" }
|
||||
(stmts', finalState) = runState (convertStmts stmts) initialState
|
||||
convertModuleItem (Initial stmt) =
|
||||
if sJumpType finalState == JTNone
|
||||
then Initial stmt'
|
||||
else error "illegal jump statement within initial construct"
|
||||
where
|
||||
initialState = Info { sJumpType = JTNone, sLoopID = "" }
|
||||
(stmt', finalState) = runState (convertStmt stmt) initialState
|
||||
convertModuleItem (AlwaysC kw stmt) =
|
||||
if sJumpType finalState == JTNone
|
||||
then AlwaysC kw stmt'
|
||||
else error "illegal jump statement within always construct"
|
||||
where
|
||||
initialState = Info { sJumpType = JTNone, sLoopID = "" }
|
||||
(stmt', finalState) = runState (convertStmt stmt) initialState
|
||||
convertModuleItem other = other
|
||||
|
||||
convertStmts :: [Stmt] -> State Info [Stmt]
|
||||
convertStmts stmts = do
|
||||
res <- convertStmt $ Block Seq "" [] stmts
|
||||
let Block Seq "" [] stmts' = res
|
||||
return stmts'
|
||||
|
||||
|
||||
-- rewrites the given statement, and returns the type of any unfinished jump
|
||||
convertStmt :: Stmt -> State Info Stmt
|
||||
|
||||
convertStmt (Block Par x decls stmts) = do
|
||||
-- break, continue, and return disallowed in fork-join
|
||||
modify $ \s -> s { sLoopID = "" }
|
||||
loopID <- gets sLoopID
|
||||
stmts' <- mapM convertStmt stmts
|
||||
modify $ \s -> s { sLoopID = loopID }
|
||||
return $ Block Par x decls stmts'
|
||||
|
||||
convertStmt (Block Seq x decls stmts) = do
|
||||
stmts' <- step stmts
|
||||
return $ Block Seq x decls $ filter (/= Null) stmts'
|
||||
where
|
||||
step :: [Stmt] -> State Info [Stmt]
|
||||
step [] = return []
|
||||
step (s : ss) = do
|
||||
jt <- gets sJumpType
|
||||
loopID <- gets sLoopID
|
||||
if jt == JTNone then do
|
||||
s' <- convertStmt s
|
||||
jt' <- gets sJumpType
|
||||
if jt' == JTNone || not (isBranch s) || null loopID then do
|
||||
ss' <- step ss
|
||||
return $ s' : ss'
|
||||
else do
|
||||
modify $ \t -> t { sJumpType = JTNone }
|
||||
ss' <- step ss
|
||||
let comp = BinOp Eq (Ident loopID) runLoop
|
||||
let stmt = Block Seq "" [] ss'
|
||||
modify $ \t -> t { sJumpType = jt' }
|
||||
return [s', If Nothing comp stmt Null]
|
||||
else do
|
||||
return [Null]
|
||||
isBranch :: Stmt -> Bool
|
||||
isBranch (If{}) = True
|
||||
isBranch (Case{}) = True
|
||||
isBranch _ = False
|
||||
|
||||
convertStmt (If unique expr thenStmt elseStmt) = do
|
||||
(thenStmt', thenJT) <- convertSubStmt thenStmt
|
||||
(elseStmt', elseJT) <- convertSubStmt elseStmt
|
||||
let newJT = max thenJT elseJT
|
||||
modify $ \s -> s { sJumpType = newJT }
|
||||
return $ If unique expr thenStmt' elseStmt'
|
||||
|
||||
convertStmt (Case unique kw expr cases mdef) = do
|
||||
(mdef', mdefJT) <-
|
||||
case mdef of
|
||||
Nothing -> return (Nothing, JTNone)
|
||||
Just stmt -> do
|
||||
(stmt', jt) <- convertSubStmt stmt
|
||||
return (Just stmt', jt)
|
||||
results <- mapM convertSubStmt $ map snd cases
|
||||
let (stmts', jts) = unzip results
|
||||
let cases' = zip (map fst cases) stmts'
|
||||
let newJT = foldl max mdefJT jts
|
||||
modify $ \s -> s { sJumpType = newJT }
|
||||
return $ Case unique kw expr cases' mdef'
|
||||
|
||||
convertStmt (For inits comp incr stmt) =
|
||||
convertLoop loop comp stmt
|
||||
where loop c s = For inits c incr s
|
||||
convertStmt (While comp stmt) =
|
||||
convertLoop While comp stmt
|
||||
convertStmt (DoWhile comp stmt) =
|
||||
convertLoop DoWhile comp stmt
|
||||
|
||||
convertStmt (Continue) = do
|
||||
loopID <- gets sLoopID
|
||||
modify $ \s -> s { sJumpType = JTContinue }
|
||||
assertMsg (not $ null loopID) "encountered continue outside of loop"
|
||||
return $ asgn loopID continueLoop
|
||||
convertStmt (Break) = do
|
||||
loopID <- gets sLoopID
|
||||
modify $ \s -> s { sJumpType = JTBreak }
|
||||
assertMsg (not $ null loopID) "encountered break outside of loop"
|
||||
return $ asgn loopID exitLoop
|
||||
convertStmt (Return Nil) = do
|
||||
loopID <- gets sLoopID
|
||||
modify $ \s -> s { sJumpType = JTReturn }
|
||||
if null loopID
|
||||
then return Null
|
||||
else return $ asgn loopID exitLoop
|
||||
|
||||
convertStmt (RepeatL expr stmt) = do
|
||||
modify $ \s -> s { sLoopID = "repeat" }
|
||||
stmt' <- convertStmt stmt
|
||||
jt <- gets sJumpType
|
||||
assertMsg (jt == JTNone) "jumps not supported within repeat loops"
|
||||
return $ RepeatL expr stmt'
|
||||
convertStmt (Forever stmt) = do
|
||||
modify $ \s -> s { sLoopID = "forever" }
|
||||
stmt' <- convertStmt stmt
|
||||
jt <- gets sJumpType
|
||||
assertMsg (jt == JTNone) "jumps not supported within forever loops"
|
||||
return $ Forever stmt'
|
||||
|
||||
convertStmt (Timing timing stmt) =
|
||||
convertStmt stmt >>= return . Timing timing
|
||||
convertStmt (StmtAttr attr stmt) =
|
||||
convertStmt stmt >>= return . StmtAttr attr
|
||||
|
||||
convertStmt (Return{}) = return $
|
||||
error "non-void return should have been elaborated already"
|
||||
convertStmt (Foreach{}) = return $
|
||||
error "foreach should have been elaborated already"
|
||||
|
||||
convertStmt other = return other
|
||||
|
||||
|
||||
-- convert a statement on its own without changing the state, but returning the
|
||||
-- resulting jump type; used to reconcile across branching statements
|
||||
convertSubStmt :: Stmt -> State Info (Stmt, JumpType)
|
||||
convertSubStmt stmt = do
|
||||
origState <- get
|
||||
stmt' <- convertStmt stmt
|
||||
jt <- gets sJumpType
|
||||
put origState
|
||||
if sJumpType origState == JTNone
|
||||
then return (stmt', jt)
|
||||
else error $ "convertStmt invariant failed on: " ++ show stmt
|
||||
|
||||
convertLoop :: (Expr -> Stmt -> Stmt) -> Expr -> Stmt -> State Info Stmt
|
||||
convertLoop loop comp stmt = do
|
||||
Info { sJumpType = origJT, sLoopID = origLoopID } <- get
|
||||
let loopID = (++) "_sv2v_loop_" $ shortHash $ loop comp stmt
|
||||
modify $ \s -> s { sLoopID = loopID }
|
||||
stmt' <- convertStmt stmt
|
||||
jt <- gets sJumpType
|
||||
let afterJT = if jt == JTReturn then jt else origJT
|
||||
put $ Info { sJumpType = afterJT, sLoopID = origLoopID }
|
||||
let comp' = BinOp LogAnd (BinOp Ne (Ident loopID) exitLoop) comp
|
||||
return $ if jt == JTNone
|
||||
then loop comp stmt'
|
||||
else Block Seq ""
|
||||
[ Variable Local loopStateType loopID [] (Just runLoop)
|
||||
]
|
||||
[ loop comp' $ Block Seq "" []
|
||||
[ asgn loopID runLoop
|
||||
, stmt'
|
||||
]
|
||||
, if afterJT == JTReturn && origLoopID /= ""
|
||||
then asgn origLoopID exitLoop
|
||||
else Null
|
||||
]
|
||||
where loopStateType = IntegerVector TBit Unspecified [(Number "0", Number "1")]
|
||||
|
||||
|
||||
-- stop running the loop immediately (break or return)
|
||||
exitLoop :: Expr
|
||||
exitLoop = Number "0"
|
||||
-- keep running the loop normally
|
||||
runLoop :: Expr
|
||||
runLoop = Number "1"
|
||||
-- skip to the next iteration of the loop (continue)
|
||||
continueLoop :: Expr
|
||||
continueLoop = Number "2"
|
||||
|
||||
|
||||
assertMsg :: Bool -> String -> State Info ()
|
||||
assertMsg True _ = return ()
|
||||
assertMsg False msg = error msg
|
||||
|
||||
asgn :: Identifier -> Expr -> Stmt
|
||||
asgn x e = AsgnBlk AsgnOpEq (LHSIdent x) e
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
{- sv2v
|
||||
- Author: Zachary Snow <zach@zachjs.com>
|
||||
-
|
||||
- Conversion for `return`
|
||||
-}
|
||||
|
||||
module Convert.Return (convert) where
|
||||
|
||||
import Convert.Traverse
|
||||
import Language.SystemVerilog.AST
|
||||
|
||||
convert :: [AST] -> [AST]
|
||||
convert = map $ traverseDescriptions $ traverseModuleItems convertFunction
|
||||
|
||||
convertFunction :: ModuleItem -> ModuleItem
|
||||
convertFunction (MIPackageItem (Function ml t f decls stmts)) =
|
||||
MIPackageItem $ Function ml t f decls $
|
||||
map (traverseNestedStmts convertStmt) stmts
|
||||
where
|
||||
convertStmt :: Stmt -> Stmt
|
||||
convertStmt (Return e) = AsgnBlk AsgnOpEq (LHSIdent f) e
|
||||
convertStmt other = other
|
||||
convertFunction other = other
|
||||
|
|
@ -229,6 +229,13 @@ traverseSinglyNestedStmtsM fullMapper = cs
|
|||
where
|
||||
cs (StmtAttr a stmt) = fullMapper stmt >>= return . StmtAttr a
|
||||
cs (Block _ "" [] []) = return Null
|
||||
cs (Block Seq name decls stmts) = do
|
||||
stmts' <- mapM fullMapper stmts
|
||||
return $ Block Seq name decls $ concatMap explode stmts'
|
||||
where
|
||||
explode :: Stmt -> [Stmt]
|
||||
explode (Block Seq "" [] ss) = ss
|
||||
explode other = [other]
|
||||
cs (Block kw name decls stmts) =
|
||||
mapM fullMapper stmts >>= return . Block kw name decls
|
||||
cs (Case u kw expr cases def) = do
|
||||
|
|
@ -254,6 +261,8 @@ traverseSinglyNestedStmtsM fullMapper = cs
|
|||
cs (Trigger blocks x) = return $ Trigger blocks x
|
||||
cs (Assertion a) =
|
||||
traverseAssertionStmtsM fullMapper a >>= return . Assertion
|
||||
cs (Continue) = return Continue
|
||||
cs (Break) = return Break
|
||||
cs (Null) = return Null
|
||||
|
||||
traverseAssertionStmtsM :: Monad m => MapperM m Stmt -> MapperM m Assertion
|
||||
|
|
@ -714,6 +723,8 @@ traverseStmtExprsM exprMapper = flatStmtMapper
|
|||
a' <- traverseAssertionStmtsM stmtMapper a
|
||||
a'' <- traverseAssertionExprsM exprMapper a'
|
||||
return $ Assertion a''
|
||||
flatStmtMapper (Continue) = return Continue
|
||||
flatStmtMapper (Break) = return Break
|
||||
flatStmtMapper (Null) = return Null
|
||||
|
||||
initsMapper (Left decls) = mapM declMapper decls >>= return . Left
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ data Stmt
|
|||
| Subroutine (Maybe Identifier) Identifier Args
|
||||
| Trigger Bool Identifier
|
||||
| Assertion Assertion
|
||||
| Continue
|
||||
| Break
|
||||
| Null
|
||||
deriving Eq
|
||||
|
||||
|
|
@ -96,6 +98,8 @@ instance Show Stmt where
|
|||
show (Timing t s ) = printf "%s %s" (show t) (show s)
|
||||
show (Trigger b x) = printf "->%s %s;" (if b then "" else ">") x
|
||||
show (Assertion a) = show a
|
||||
show (Continue ) = "continue;"
|
||||
show (Break ) = "break;"
|
||||
show (Null ) = ";"
|
||||
|
||||
data CaseKW
|
||||
|
|
|
|||
|
|
@ -911,6 +911,9 @@ StmtNonBlock :: { Stmt }
|
|||
| Identifier "::" Identifier CallArgs ";" { Subroutine (Just $1) $3 $4 }
|
||||
| TimingControl Stmt { Timing $1 $2 }
|
||||
| "return" Expr ";" { Return $2 }
|
||||
| "return" ";" { Return Nil }
|
||||
| "break" ";" { Break }
|
||||
| "continue" ";" { Continue }
|
||||
| "while" "(" Expr ")" Stmt { While $3 $5 }
|
||||
| "repeat" "(" Expr ")" Stmt { RepeatL $3 $5 }
|
||||
| "do" Stmt "while" "(" Expr ")" ";" { DoWhile $5 $2 }
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ executable sv2v
|
|||
Convert.FuncRet
|
||||
Convert.Interface
|
||||
Convert.IntTypes
|
||||
Convert.Jump
|
||||
Convert.KWArgs
|
||||
Convert.Logic
|
||||
Convert.LogOp
|
||||
|
|
@ -75,7 +76,6 @@ executable sv2v
|
|||
Convert.Package
|
||||
Convert.ParamType
|
||||
Convert.RemoveComments
|
||||
Convert.Return
|
||||
Convert.Simplify
|
||||
Convert.SizeCast
|
||||
Convert.StarPort
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
module top;
|
||||
|
||||
task skip1;
|
||||
$display("HELLO skip1");
|
||||
return;
|
||||
$display("UNREACHABLE");
|
||||
endtask
|
||||
function void skip2;
|
||||
$display("HELLO skip2");
|
||||
return;
|
||||
$display("UNREACHABLE");
|
||||
endfunction
|
||||
function int skip3;
|
||||
$display("HELLO skip3");
|
||||
return 1;
|
||||
$display("UNREACHABLE");
|
||||
endfunction
|
||||
task skip4;
|
||||
for (int i = 0; i < 10; ++i) begin
|
||||
$display("HELLO skip4");
|
||||
return;
|
||||
$display("UNREACHABLE");
|
||||
end
|
||||
$display("UNREACHABLE");
|
||||
endtask
|
||||
task skip5;
|
||||
for (int i = 0; i < 10; ++i) begin
|
||||
$display("HELLO skip5-1");
|
||||
for (int j = 0; j < 10; ++j) begin
|
||||
$display("HELLO skip5-2");
|
||||
return;
|
||||
$display("UNREACHABLE");
|
||||
end
|
||||
$display("UNREACHABLE");
|
||||
end
|
||||
$display("UNREACHABLE");
|
||||
endtask
|
||||
initial begin
|
||||
skip1;
|
||||
skip2;
|
||||
$display(skip3());
|
||||
skip4;
|
||||
skip5;
|
||||
end
|
||||
|
||||
initial
|
||||
for (int i = 0; i < 10; ++i) begin
|
||||
$display("Loop Y:", i);
|
||||
continue;
|
||||
$display("UNREACHABLE");
|
||||
end
|
||||
|
||||
initial
|
||||
for (int i = 0; i < 10; ++i) begin
|
||||
$display("Loop Z:", i);
|
||||
break;
|
||||
$display("UNREACHABLE");
|
||||
end
|
||||
|
||||
initial
|
||||
for (int i = 0; i < 10; ++i)
|
||||
if (i < 5)
|
||||
$display("Loop A:", i);
|
||||
else
|
||||
break;
|
||||
|
||||
initial
|
||||
for (int i = 0; i < 10; ++i) begin
|
||||
if (i < 3)
|
||||
$display("Loop B-1:", i);
|
||||
else if (i < 7)
|
||||
$display("Loop B-2:", i);
|
||||
else begin
|
||||
$display("Loop B-3:", i);
|
||||
continue;
|
||||
$display("UNREACHABLE");
|
||||
end
|
||||
$display("Loop B:", i);
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
module top;
|
||||
|
||||
task skip1;
|
||||
$display("HELLO skip1");
|
||||
endtask
|
||||
task skip2;
|
||||
$display("HELLO skip2");
|
||||
endtask
|
||||
function integer skip3;
|
||||
input x;
|
||||
begin
|
||||
$display("HELLO skip3");
|
||||
skip3 = 1;
|
||||
end
|
||||
endfunction
|
||||
task skip4;
|
||||
$display("HELLO skip4");
|
||||
endtask
|
||||
task skip5;
|
||||
begin
|
||||
$display("HELLO skip5-1");
|
||||
$display("HELLO skip5-2");
|
||||
end
|
||||
endtask
|
||||
initial begin
|
||||
skip1;
|
||||
skip2;
|
||||
$display(skip3(0));
|
||||
skip4;
|
||||
skip5;
|
||||
end
|
||||
|
||||
initial begin : loop_y
|
||||
integer i;
|
||||
for (i = 0; i < 10; ++i)
|
||||
$display("Loop Y:", i);
|
||||
end
|
||||
|
||||
initial begin : loop_z
|
||||
integer i;
|
||||
i = 0;
|
||||
$display("Loop Z:", i);
|
||||
end
|
||||
|
||||
initial begin : loop_a
|
||||
integer i;
|
||||
for (i = 0; i < 5; ++i)
|
||||
$display("Loop A:", i);
|
||||
end
|
||||
|
||||
initial begin : loop_b
|
||||
integer i;
|
||||
for (i = 0; i < 10; ++i) begin
|
||||
if (i < 3) begin
|
||||
$display("Loop B-1:", i);
|
||||
$display("Loop B:", i);
|
||||
end
|
||||
else if (i < 7) begin
|
||||
$display("Loop B-2:", i);
|
||||
$display("Loop B:", i);
|
||||
end
|
||||
else
|
||||
$display("Loop B-3:", i);
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue