Commit e778a671 by Zachary Snow

generate explicit sensitivity lists when necessary

parent 9eceb556
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
intentional width-extending operations such as `+ 0` and `* 1` intentional width-extending operations such as `+ 0` and `* 1`
* Fixed forced conversion to `reg` of data sensed in an edge-controlled * Fixed forced conversion to `reg` of data sensed in an edge-controlled
procedural assignment procedural assignment
* `always_comb` and `always_latch` now generate explicit sensitivity lists where
necessary because of calls to functions which reference non-local data
## v0.0.9 ## v0.0.9
......
...@@ -3,24 +3,224 @@ ...@@ -3,24 +3,224 @@
- -
- Conversion for `always_latch`, `always_comb`, and `always_ff` - Conversion for `always_latch`, `always_comb`, and `always_ff`
- -
- `always_latch` -> `always @*` - `always_comb` and `always_latch` become `always @*`, or produce an explicit
- `always_comb` -> `always @*` - sensitivity list if they need to pick up sensitivities from the functions
- `always_ff` -> `always` - they call. `always_ff` simply becomes `always`.
-
- TODO: `always_comb` blocks do not run at time zero
-} -}
module Convert.AlwaysKW (convert) where module Convert.AlwaysKW (convert) where
import Control.Monad.State.Strict
import Control.Monad.Writer.Strict
import Data.Maybe (fromMaybe, mapMaybe)
import Convert.Scoper
import Convert.Traverse import Convert.Traverse
import Language.SystemVerilog.AST import Language.SystemVerilog.AST
convert :: [AST] -> [AST] convert :: [AST] -> [AST]
convert = map $ traverseDescriptions $ traverseModuleItems replaceAlwaysKW convert = map $ traverseDescriptions traverseDescription
replaceAlwaysKW :: ModuleItem -> ModuleItem traverseDescription :: Description -> Description
replaceAlwaysKW (AlwaysC AlwaysLatch stmt) = traverseDescription description@Part{} =
AlwaysC Always $ Timing (Event EventStar) stmt evalState (evalScoperT $ scopePart op description) mempty
replaceAlwaysKW (AlwaysC AlwaysComb stmt) = where op = traverseModuleItem >=> scoper
AlwaysC Always $ Timing (Event EventStar) stmt traverseDescription description = description
replaceAlwaysKW (AlwaysC AlwaysFF stmt) =
AlwaysC Always stmt type SC = ScoperT Kind (State (Any, [Expr]))
replaceAlwaysKW other = other
type PortDir = (Identifier, Direction)
data Kind
= Const Expr
| Var
| Proc [Expr] [PortDir]
deriving Eq
scoper :: ModuleItem -> SC ModuleItem
scoper = scopeModuleItem traverseDecl return traverseGenItem traverseStmt
-- track declarations and visit expressions they contain
traverseDecl :: Decl -> SC Decl
traverseDecl decl = do
case decl of
Param s _ x e -> do
-- handle references to local constants
e' <- if s == Localparam
then scopeExpr e
else return Nil
insertElem x $ Const e'
ParamType _ x _ -> insertElem x $ Const Nil
Variable _ _ x _ _ -> do
-- don't let the second visit of a function or task overwrite the
-- Proc entry that was just generated
details <- lookupLocalIdentM x
case details of
Just (_, _, Proc{}) -> return ()
_ -> insertElem x Var
Net _ _ _ _ x _ _ -> insertElem x Var
CommentDecl{} -> return ()
traverseDeclExprsM traverseExpr decl
-- track expressions and subroutines in a statement
traverseStmt :: Stmt -> SC Stmt
traverseStmt (Subroutine expr args) =
traverseCall Subroutine expr args
traverseStmt stmt = traverseStmtExprsM traverseExpr stmt
-- visit tasks, functions, and always blocks in generate scopes
traverseGenItem :: GenItem -> SC GenItem
traverseGenItem (GenModuleItem item) =
traverseModuleItem item >>= return . GenModuleItem
traverseGenItem other = return other
-- identify variables referenced within an expression
traverseExpr :: Expr -> SC Expr
traverseExpr (Call expr args) =
traverseCall Call expr args
traverseExpr expr = do
prefix <- embedScopes longestStaticPrefix expr
case prefix of
Just expr' -> push (Any False, [expr']) >> return expr
_ -> traverseSinglyNestedExprsM traverseExpr expr
-- turn a reference to a variable into a canonicalized longest static prefix, if
-- possible, per IEEE 1800-2017 Section 11.5.3
longestStaticPrefix :: Scopes Kind -> Expr -> Maybe Expr
longestStaticPrefix scopes expr@Ident{} =
asVar scopes expr
longestStaticPrefix scopes (Range expr mode (l, r)) = do
expr' <- longestStaticPrefix scopes expr
l' <- asConst scopes l
r' <- asConst scopes r
Just $ Range expr' mode (l', r')
longestStaticPrefix scopes (Bit expr idx) = do
expr' <- longestStaticPrefix scopes expr
idx' <- asConst scopes idx
Just $ Bit expr' idx'
longestStaticPrefix scopes orig@(Dot expr field) =
case asVar scopes orig of
Just orig' -> Just orig'
_ -> do
expr' <- longestStaticPrefix scopes expr
Just $ Dot expr' field
longestStaticPrefix _ _ =
Nothing
-- lookup an expression as an outwardly-visible variable
asVar :: Scopes Kind -> Expr -> Maybe Expr
asVar scopes expr = do
(accesses, _, Var) <- lookupElem scopes expr
if visible accesses
then Just $ accessesToExpr accesses
else Nothing
visible :: [Access] -> Bool
visible = not . elem (Access "" Nil)
-- lookup an expression as a hoist-able constant
asConst :: Scopes Kind -> Expr -> Maybe Expr
asConst scopes expr =
case runWriter $ asConstRaw scopes expr of
(expr', Any False) -> Just expr'
_ -> Nothing
asConstRaw :: Scopes Kind -> Expr -> Writer Any Expr
asConstRaw scopes expr =
case lookupElem scopes expr of
Just (_, _, Const Nil) -> recurse
Just (_, _, Const expr') -> asConstRaw scopes expr'
Just{} -> tell (Any True) >> return Nil
Nothing -> recurse
where
recurse = traverseSinglyNestedExprsM (asConstRaw scopes) expr
-- special handling for subroutine invocations and function calls
traverseCall :: (Expr -> Args -> a) -> Expr -> Args -> SC a
traverseCall constructor expr args = do
details <- lookupElemM expr
expr' <- traverseExpr expr
args' <- case details of
Just (_, _, Proc exprs ps) -> do
when (not $ null exprs) $
push (Any True, exprs)
traverseArgs ps args
_ -> traverseArgs [] args
return $ constructor expr' args'
-- treats output ports as assignment-like contexts
traverseArgs :: [PortDir] -> Args -> SC Args
traverseArgs ps (Args pnArgs kwArgs) = do
pnArgs' <- zipWithM usingPN [0..] pnArgs
kwArgs' <- mapM usingKW kwArgs
return (Args pnArgs' kwArgs')
where
usingPN :: Int -> Expr -> SC Expr
usingPN key val = do
if dir == Output
then return val
else traverseExpr val
where dir = if key < length ps
then snd $ ps !! key
else Input
usingKW :: (Identifier, Expr) -> SC (Identifier, Expr)
usingKW (key, val) = do
val' <- if dir == Output
then return val
else traverseExpr val
return (key, val')
where dir = fromMaybe Input $ lookup key ps
-- append to the non-local expression state
push :: (Any, [Expr]) -> SC ()
push x = lift $ modify' (x <>)
-- custom traversal which converts SystemVerilog `always` keywords and tracks
-- information about task and functions
traverseModuleItem :: ModuleItem -> SC ModuleItem
traverseModuleItem (AlwaysC AlwaysLatch stmt) = do
e <- fmap toEvent $ findNonLocals $ Initial stmt
return $ AlwaysC Always $ Timing (Event e) stmt
traverseModuleItem (AlwaysC AlwaysComb stmt) = do
e <- fmap toEvent $ findNonLocals $ Initial stmt
return $ AlwaysC Always $ Timing (Event e) stmt
traverseModuleItem (AlwaysC AlwaysFF stmt) =
return $ AlwaysC Always stmt
traverseModuleItem item@(MIPackageItem (Function _ _ x decls _)) = do
(_, s) <- findNonLocals item
insertElem x $ Proc s (ports decls)
return item
traverseModuleItem item@(MIPackageItem (Task _ x decls _)) = do
insertElem x $ Proc [] (ports decls)
return item
traverseModuleItem other = return other
toEvent :: (Bool, [Expr]) -> Event
toEvent (False, _) = EventStar
toEvent (True, exprs) =
EventExpr $ foldl1 EventExprOr $ map (EventExprEdge NoEdge) exprs
-- turn a list of port declarations into a port direction map
ports :: [Decl] -> [PortDir]
ports = filter ((/= Local) . snd) . map port
port :: Decl -> PortDir
port (Variable d _ x _ _) = (x, d)
port (Net d _ _ _ x _ _) = (x, d)
port _ = ("", Local)
-- get a list of non-local variables referenced within a module item, and
-- whether or not this module item references any functions which themselves
-- reference non-local variables
findNonLocals :: ModuleItem -> SC (Bool, [Expr])
findNonLocals item = do
scopes <- get
lift $ put mempty
_ <- scoper item
(anys, exprs) <- lift get
let nonLocals = mapMaybe (longestStaticPrefix scopes) exprs
return (getAny anys, nonLocals)
`define ALWAYS(trigger) always_comb
`include "always_prefix.vh"
`define ALWAYS(trigger) always @(trigger)
`include "always_prefix.vh"
module mod(
input wire [3:0] idx,
input wire [14:0] data
);
localparam Y = 2;
localparam X = 10000;
`define TEST(expr, trigger, extra) \
if (1) begin \
function automatic f; \
input reg ignored; \
localparam X = Y + 1; \
localparam THREE = X; \
f = expr; \
endfunction \
`ALWAYS(trigger) begin : blk \
localparam ZERO = 0; \
$display(`"%2d %b expr trigger`", \
$time, f(ZERO) extra); \
end \
end
`define TEST_SIMPLE(expr) `TEST(expr, expr, )
`TEST_SIMPLE(data)
`TEST_SIMPLE(data[1])
`TEST_SIMPLE(data[4])
`TEST_SIMPLE(data[4:1])
`TEST_SIMPLE(data[10:1])
localparam ONE = 1;
parameter FOUR = 4;
`TEST_SIMPLE(data[ONE])
`TEST_SIMPLE(data[FOUR])
`TEST_SIMPLE(data[FOUR:ONE])
`TEST(data[idx], data or idx, )
`TEST(data[idx+:2], data or idx, )
`TEST(data[THREE], data[3], )
`TEST(data[ignored], data, )
`TEST(data[THREE], data[0] or data[3], & data[0])
endmodule
module top;
reg [3:0] idx;
reg [14:0] data;
mod m(idx, data);
initial begin
#1 data = 0;
#1 idx = 0;
#1 data[0] = 1;
#1 data[4] = 1;
#1 data[5] = 1;
#1 data[3] = 1;
#1 data[8] = 1;
#1 idx = 0;
#1 idx = 1;
#1 data[0] = 0;
#1 data[0] = 1;
end
endmodule
module mod(
input wire inp1, inp2,
output reg out1, out2, out3, out4, out5, out6, out7, out8, out9, outA, outB
);
localparam ZERO = 0;
task automatic t;
output reg o;
o = inp1;
endtask
function automatic flop;
input reg i;
flop = i;
endfunction
function automatic flip;
input reg i;
flip = flop(~i);
endfunction
function automatic f;
input reg i; // ignored
f = inp2;
endfunction
function automatic g;
input reg inp1; // ignored
g = f(ZERO) & mod.inp1;
endfunction
function void u;
output reg o;
o = inp1;
endfunction
task automatic asgn;
output reg o;
input reg i;
o = i;
endtask
always_comb
t(out1);
always_comb
out2 = f(ZERO);
always_comb
out3 = f(ZERO) & inp1;
always_comb
out4 = g(ZERO);
always_comb
out5 = flip(inp1);
always_comb begin
reg x;
x = g(ZERO);
out6 = x;
end
always_comb
u(out7);
parameter ONE = 1;
if (ONE)
always_comb begin
asgn(out8, flip(inp1));
out9 = f(ZERO);
end
always_latch
if (inp1)
outA = f(ZERO);
struct packed {
logic x, y;
} s;
assign s = {inp1, inp2};
function automatic h;
input reg i; // ignored
h = s.y;
endfunction
always_comb
outB = h(ZERO);
endmodule
module mod(
input wire inp1, inp2,
output reg out1, out2, out3, out4, out5, out6, out7, out8, out9, outA, outB
);
localparam ZERO = 0;
task automatic t;
output reg o;
o = inp1;
endtask
function automatic flop;
input reg i;
flop = i;
endfunction
function automatic flip;
input reg i;
flip = flop(~i);
endfunction
function automatic f;
input reg i; // ignored
f = inp2;
endfunction
function automatic g;
input reg i; // ignored
g = f(ZERO) & inp1;
endfunction
task automatic u;
output reg o;
o = inp1;
endtask
task automatic asgn;
output reg o;
input reg i;
o = i;
endtask
always @*
t(out1);
always @(inp2)
out2 = f(ZERO);
always @(inp1, inp2)
out3 = f(ZERO) & inp1;
always @(inp1, inp2)
out4 = g(ZERO);
always @*
out5 = flip(inp1);
always @(inp1, inp2) begin : blk
reg x;
x = g(ZERO);
out6 = x;
end
always @(inp1)
u(out7);
parameter ONE = 1;
if (ONE)
always @(inp1, inp2) begin
asgn(out8, flip(inp1));
out9 = f(ZERO);
end
always @(inp1, inp2)
if (inp1)
outA = f(ZERO);
wire [1:0] s;
assign s = {inp1, inp2};
function automatic h;
input reg i; // ignored
h = s[0];
endfunction
always @(s[0])
outB = h(ZERO);
endmodule
module top;
reg inp1, inp2;
wire out1, out2, out3, out4, out5, out6, out7, out8, out9, outA, outB;
mod m(inp1, inp2, out1, out2, out3, out4, out5, out6, out7, out8, out9, outA, outB);
initial begin
$monitor(inp1, inp2,
out1, out2, out3, out4, out5, out6, out7, out8, out9, outA, outB);
repeat (2) begin
#1 inp1 = 0;
#1 inp2 = 0;
#1 inp2 = 1;
#1 inp1 = 1;
#1 inp2 = 0;
#1 inp2 = 1;
#1 inp2 = 0;
#1 inp1 = 0;
#1 inp1 = 1;
#1 inp2 = 1;
#1 inp1 = 0;
#1 inp1 = 1;
end
end
endmodule
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment