From f9917d94da51a09812cb1356ab2cd4971b86b88a Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Tue, 26 Dec 2023 09:21:19 -0700 Subject: [PATCH] execute always_comb/latch at time zero --- CHANGELOG.md | 1 + src/Convert/AlwaysKW.hs | 49 +++++++++++++++++++++++++++++--------- test/core/always_comb.sv | 14 +++++++++++ test/core/always_comb.v | 14 +++++++++++ test/core/always_comb.vh | 12 ++++++++++ test/core/always_prefix.v | 2 +- test/core/always_prefix.vh | 3 +++ test/core/always_sense.sv | 4 ++++ test/core/always_sense.v | 14 +++++++---- test/core/logic_cond.v | 7 ++++-- test/lib/clean_vcd.py | 2 +- test/lib/functions.sh | 2 +- 12 files changed, 103 insertions(+), 21 deletions(-) create mode 100644 test/core/always_comb.sv create mode 100644 test/core/always_comb.v create mode 100644 test/core/always_comb.vh diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b52f97..9db7bc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ ### Other Enhancements +* `always_comb` and `always_latch` now reliably execute at time zero * Added error checking for unresolved typenames * Added constant folding for `||` and `&&` * `input reg` module ports are now converted to `input wire` diff --git a/src/Convert/AlwaysKW.hs b/src/Convert/AlwaysKW.hs index b93c178..d70aa9e 100644 --- a/src/Convert/AlwaysKW.hs +++ b/src/Convert/AlwaysKW.hs @@ -5,9 +5,9 @@ - - `always_comb` and `always_latch` become `always @*`, or produce an explicit - sensitivity list if they need to pick up sensitivities from the functions - - they call. `always_ff` simply becomes `always`. - - - - TODO: `always_comb` blocks do not run at time zero + - they call. These blocks are triggered at time zero by adding a no-op + - statement reading from `_sv2v_0`, which is injected and updated in an + - `initial` block. `always_ff` simply becomes `always`. -} module Convert.AlwaysKW (convert) where @@ -24,9 +24,15 @@ convert :: [AST] -> [AST] convert = map $ traverseDescriptions traverseDescription traverseDescription :: Description -> Description -traverseDescription description@Part{} = - evalState (evalScoperT $ scopePart op description) mempty - where op = traverseModuleItem >=> scoper +traverseDescription (Part att ext kw lif name pts items) = + Part att ext kw lif name pts $ + if getAny anys && not (elem triggerDecl items') + then triggerDecl : items' ++ [triggerFire] + else items' + where + op = traverseModuleItem >=> scoper + (items', (anys, _)) = flip runState mempty $ evalScoperT $ + insertElem triggerIdent Var >> scopeModuleItems op name items traverseDescription description = description type SC = ScoperT Kind (State (Any, [Expr])) @@ -182,12 +188,13 @@ 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 AlwaysLatch stmt) = + traverseModuleItem $ AlwaysC AlwaysComb stmt traverseModuleItem (AlwaysC AlwaysComb stmt) = do - e <- fmap toEvent $ findNonLocals $ Initial stmt - return $ AlwaysC Always $ Timing (Event e) stmt + push (Any True, []) + e <- fmap toEvent $ findNonLocals $ Initial stmt' + return $ AlwaysC Always $ Timing (Event e) stmt' + where stmt' = addTriggerStmt stmt traverseModuleItem (AlwaysC AlwaysFF stmt) = return $ AlwaysC Always stmt traverseModuleItem item@(MIPackageItem (Function _ _ x decls _)) = do @@ -220,8 +227,28 @@ port _ = ("", Local) findNonLocals :: ModuleItem -> SC (Bool, [Expr]) findNonLocals item = do scopes <- get + prev <- lift get lift $ put mempty _ <- scoper item (anys, exprs) <- lift get + lift $ put prev let nonLocals = mapMaybe (longestStaticPrefix scopes) exprs return (getAny anys, nonLocals) + +triggerIdent :: Identifier +triggerIdent = "_sv2v_0" + +triggerDecl :: ModuleItem +triggerDecl = MIPackageItem $ Decl $ Variable Local t triggerIdent [] Nil + where t = IntegerVector TReg Unspecified [] + +triggerFire :: ModuleItem +triggerFire = Initial $ Asgn AsgnOpEq Nothing (LHSIdent triggerIdent) (RawNum 0) + +triggerStmt :: Stmt +triggerStmt = If NoCheck (Ident triggerIdent) Null Null + +addTriggerStmt :: Stmt -> Stmt +addTriggerStmt (Block Seq name decls stmts) = + Block Seq name decls $ triggerStmt : stmts +addTriggerStmt stmt = Block Seq "" [] [triggerStmt, stmt] diff --git a/test/core/always_comb.sv b/test/core/always_comb.sv new file mode 100644 index 0000000..f32807c --- /dev/null +++ b/test/core/always_comb.sv @@ -0,0 +1,14 @@ +module top; + logic x; + always_comb + x = 0; + + `include "always_comb.vh" + `TEST(_comb, 1) + `TEST(_comb, 2) + `TEST(@*, 3) + `TEST(@*, 4) + + initial x1 = 0; + initial x3 = 0; +endmodule diff --git a/test/core/always_comb.v b/test/core/always_comb.v new file mode 100644 index 0000000..8d7b04b --- /dev/null +++ b/test/core/always_comb.v @@ -0,0 +1,14 @@ +module top; + reg x; + initial + x = 0; + + `include "always_comb.vh" + `TEST(@(x1), 1) + `TEST(@(x1), 2) + `TEST(@*, 3) + `TEST(@*, 4) + + initial x1 = 0; + initial x3 = 0; +endmodule diff --git a/test/core/always_comb.vh b/test/core/always_comb.vh new file mode 100644 index 0000000..f425253 --- /dev/null +++ b/test/core/always_comb.vh @@ -0,0 +1,12 @@ +reg never; +`define TEST(sense, num) \ + reg x``num, y``num, z``num; \ + function automatic t``num; \ + input inp; \ + t``num = x``num; \ + endfunction \ + always``sense begin \ + y``num = 0; \ + z``num = t``num(0); \ + if (never) ; \ + end diff --git a/test/core/always_prefix.v b/test/core/always_prefix.v index f2a2c1f..01712e9 100644 --- a/test/core/always_prefix.v +++ b/test/core/always_prefix.v @@ -1,2 +1,2 @@ -`define ALWAYS(trigger) always @(trigger) +`define ALWAYS(trigger) always @(trigger, start) `include "always_prefix.vh" diff --git a/test/core/always_prefix.vh b/test/core/always_prefix.vh index 28bb2b5..ffdd82d 100644 --- a/test/core/always_prefix.vh +++ b/test/core/always_prefix.vh @@ -4,6 +4,7 @@ module mod( ); localparam Y = 2; localparam X = 10000; + reg start; `define TEST(expr, trigger, extra) \ if (1) begin \ @@ -40,4 +41,6 @@ parameter FOUR = 4; `TEST(data[THREE], data[3], ) `TEST(data[ignored], data, ) `TEST(data[THREE], data[0] or data[3], & data[0]) + + initial start = 0; endmodule diff --git a/test/core/always_sense.sv b/test/core/always_sense.sv index c75e1c8..fcb1375 100644 --- a/test/core/always_sense.sv +++ b/test/core/always_sense.sv @@ -39,6 +39,8 @@ module mod( o = i; endtask + logic start; + always_comb t(out1); always_comb @@ -82,4 +84,6 @@ module mod( endfunction always_comb asgn(.i(i(ZERO)), .o(outC)); + + initial start = 0; endmodule diff --git a/test/core/always_sense.v b/test/core/always_sense.v index ad8b027..1623795 100644 --- a/test/core/always_sense.v +++ b/test/core/always_sense.v @@ -39,17 +39,19 @@ module mod( o = i; endtask + reg start; + always @* t(out1); always @(inp2) out2 = f(ZERO); - always @(inp1, inp2) + always @(start, inp1, inp2) out3 = f(ZERO) & inp1; - always @(inp1, inp2) + always @(start, inp1, inp2) out4 = g(ZERO); always @* out5 = flip(inp1); - always @(inp1, inp2) begin : blk + always @(start, inp1, inp2) begin : blk reg x; x = g(ZERO); out6 = x; @@ -58,11 +60,11 @@ module mod( u(out7); parameter ONE = 1; if (ONE) - always @(inp1, inp2) begin + always @(start, inp1, inp2) begin asgn(out8, flip(inp1)); out9 = f(ZERO); end - always @(inp1, inp2) + always @(start, inp1, inp2) if (inp1) outA = f(ZERO); @@ -80,4 +82,6 @@ module mod( endfunction always @(s[1]) asgn(outC, i(ZERO)); + + initial start = 0; endmodule diff --git a/test/core/logic_cond.v b/test/core/logic_cond.v index 449e494..71f6cd9 100644 --- a/test/core/logic_cond.v +++ b/test/core/logic_cond.v @@ -4,8 +4,11 @@ module Example(inp, out); input wire inp; output reg out; generate - if (ENABLED) - always @* out = inp; + if (ENABLED) begin + reg start; + always @(inp, start) out = inp; + initial start = 0; + end else initial out = DEFAULT; endgenerate diff --git a/test/lib/clean_vcd.py b/test/lib/clean_vcd.py index c23b7c3..1da026c 100755 --- a/test/lib/clean_vcd.py +++ b/test/lib/clean_vcd.py @@ -13,7 +13,7 @@ if __name__ == "__main__": # Find and drop dumped parameters. if line.startswith("$var "): parts = line.split() - should_drop = parts[1] == "parameter" + should_drop = parts[1] == "parameter" or parts[4] == "_sv2v_0" if not should_drop and not pending: print(line, end="") continue diff --git a/test/lib/functions.sh b/test/lib/functions.sh index 5505324..58570c1 100644 --- a/test/lib/functions.sh +++ b/test/lib/functions.sh @@ -34,7 +34,7 @@ simulate() { $sim_prog -no-date > $sim_log assertTrue "simulating $1 failed" $? # remove parameters from the VCD if present - if grep "var parameter" $sim_vcd_tmp > /dev/null; then + if grep -E "var parameter| _sv2v_0 " $sim_vcd_tmp > /dev/null; then $SCRIPT_DIR/clean_vcd.py < $sim_vcd_tmp > $sim_vcd elif [ $sim_vcd != "/dev/null" ]; then mv -f $sim_vcd_tmp $sim_vcd