diff --git a/README b/README index 50105ed2d..2a1dde435 100644 --- a/README +++ b/README @@ -384,9 +384,16 @@ from SystemVerilog: form. In module context: "assert property ();" and within an always block: "assert();". It is transformed to a $assert cell. +- The "assume" and "expect" statements from SystemVerilog are also + supported. The same limitations as with the "assert" statement apply. + - The keywords "always_comb", "always_ff" and "always_latch", "logic" and "bit" are supported. +- SystemVerilog packages are supported. Once a SystemVerilog file is read + into a design with "read_verilog", all its packages are available to + SystemVerilog files being read into the same design afterwards. + Building the documentation ========================== diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 57de725d8..c298d5a98 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -82,6 +82,7 @@ std::string AST::type2str(AstNodeType type) X(AST_PREFIX) X(AST_ASSERT) X(AST_ASSUME) + X(AST_EXPECT) X(AST_FCALL) X(AST_TO_BITS) X(AST_TO_SIGNED) diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 3dcd32bd4..5c2c51b8e 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -65,6 +65,7 @@ namespace AST AST_PREFIX, AST_ASSERT, AST_ASSUME, + AST_EXPECT, AST_FCALL, AST_TO_BITS, diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 3e359170b..31367b87e 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -1296,7 +1296,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // generate $assert cells case AST_ASSERT: case AST_ASSUME: + case AST_EXPECT: { + const char *celltype = "$assert"; + if (type == AST_ASSUME) celltype = "$assume"; + if (type == AST_EXPECT) celltype = "$expect"; + log_assert(children.size() == 2); RTLIL::SigSpec check = children[0]->genRTLIL(); @@ -1308,9 +1313,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) en = current_module->ReduceBool(NEW_ID, en); std::stringstream sstr; - sstr << (type == AST_ASSERT ? "$assert$" : "$assume$") << filename << ":" << linenum << "$" << (autoidx++); + sstr << celltype << "$" << filename << ":" << linenum << "$" << (autoidx++); - RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_ASSERT ? "$assert" : "$assume"); + RTLIL::Cell *cell = current_module->addCell(sstr.str(), celltype); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); for (auto &attr : attributes) { diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 25039a4fb..cf84a399c 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -1348,10 +1348,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } skip_dynamic_range_lvalue_expansion:; - if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && current_block != NULL) + if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME || type == AST_EXPECT) && current_block != NULL) { std::stringstream sstr; - sstr << "$assert$" << filename << ":" << linenum << "$" << (autoidx++); + sstr << "$formal$" << filename << ":" << linenum << "$" << (autoidx++); std::string id_check = sstr.str() + "_CHECK", id_en = sstr.str() + "_EN"; AstNode *wire_check = new AstNode(AST_WIRE); @@ -1363,8 +1363,10 @@ skip_dynamic_range_lvalue_expansion:; AstNode *wire_en = new AstNode(AST_WIRE); wire_en->str = id_en; current_ast_mod->children.push_back(wire_en); - current_ast_mod->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1))))); - current_ast_mod->children.back()->children[0]->children[0]->children[0]->str = id_en; + if (current_always == nullptr || current_always->type != AST_INITIAL) { + current_ast_mod->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1))))); + current_ast_mod->children.back()->children[0]->children[0]->children[0]->str = id_en; + } current_scope[wire_en->str] = wire_en; while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } @@ -1403,7 +1405,7 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } - if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && children.size() == 1) + if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME || type == AST_EXPECT) && children.size() == 1) { children.push_back(mkconst_int(1, false, 1)); did_something = true; diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index 107a2dfdd..c9a59d665 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -173,6 +173,7 @@ YOSYS_NAMESPACE_END "assert" { if (formal_mode) return TOK_ASSERT; SV_KEYWORD(TOK_ASSERT); } "assume" { if (formal_mode) return TOK_ASSUME; SV_KEYWORD(TOK_ASSUME); } +"expect" { if (formal_mode) return TOK_EXPECT; SV_KEYWORD(TOK_EXPECT); } "property" { if (formal_mode) return TOK_PROPERTY; SV_KEYWORD(TOK_PROPERTY); } "logic" { SV_KEYWORD(TOK_REG); } "bit" { SV_KEYWORD(TOK_REG); } diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index e7c3578c7..bfb4990b2 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -112,7 +112,8 @@ static void free_attr(std::map *al) %token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR TOK_REAL %token TOK_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE %token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED -%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_ASSERT TOK_ASSUME TOK_PROPERTY +%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_ASSERT TOK_ASSUME +%token TOK_EXPECT TOK_PROPERTY %type range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int %type wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list @@ -965,6 +966,9 @@ assert: } | TOK_ASSUME '(' expr ')' ';' { ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $3)); + } | + TOK_EXPECT '(' expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(AST_EXPECT, $3)); }; assert_property: @@ -973,6 +977,9 @@ assert_property: } | TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' { ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $4)); + } | + TOK_EXPECT TOK_PROPERTY '(' expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(AST_EXPECT, $4)); }; simple_behavioral_stmt: diff --git a/kernel/celltypes.h b/kernel/celltypes.h index cf7bc2dcf..78403fcd3 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -116,6 +116,7 @@ struct CellTypes setup_type("$assert", {A, EN}, pool(), true); setup_type("$assume", {A, EN}, pool(), true); + setup_type("$expect", {A, EN}, pool(), true); setup_type("$equiv", {A, B}, {Y}, true); } diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 9da6d2816..644a83a76 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1017,14 +1017,7 @@ namespace { return; } - if (cell->type == "$assert") { - port("\\A", 1); - port("\\EN", 1); - check_expected(); - return; - } - - if (cell->type == "$assume") { + if (cell->type.in("$assert", "$assume", "$expect")) { port("\\A", 1); port("\\EN", 1); check_expected(); @@ -1795,6 +1788,22 @@ RTLIL::Cell* RTLIL::Module::addAssert(RTLIL::IdString name, RTLIL::SigSpec sig_a return cell; } +RTLIL::Cell* RTLIL::Module::addAssume(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en) +{ + RTLIL::Cell *cell = addCell(name, "$assume"); + cell->setPort("\\A", sig_a); + cell->setPort("\\EN", sig_en); + return cell; +} + +RTLIL::Cell* RTLIL::Module::addExpect(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en) +{ + RTLIL::Cell *cell = addCell(name, "$expect"); + cell->setPort("\\A", sig_a); + cell->setPort("\\EN", sig_en); + return cell; +} + RTLIL::Cell* RTLIL::Module::addEquiv(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y) { RTLIL::Cell *cell = addCell(name, "$equiv"); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 274f97023..627835a9c 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -1004,6 +1004,8 @@ public: RTLIL::Cell* addLut (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const lut); RTLIL::Cell* addTribuf (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_y); RTLIL::Cell* addAssert (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en); + RTLIL::Cell* addAssume (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en); + RTLIL::Cell* addExpect (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en); RTLIL::Cell* addEquiv (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y); RTLIL::Cell* addSr (RTLIL::IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, RTLIL::SigSpec sig_q, bool set_polarity = true, bool clr_polarity = true); diff --git a/kernel/satgen.h b/kernel/satgen.h index e118c1569..da892c710 100644 --- a/kernel/satgen.h +++ b/kernel/satgen.h @@ -69,6 +69,7 @@ struct SatGen SigPool initial_state; std::map asserts_a, asserts_en; std::map assumes_a, assumes_en; + std::map expects_a, expects_en; std::map> imported_signals; bool ignore_div_by_zero; bool model_undef; @@ -1346,6 +1347,14 @@ struct SatGen return true; } + if (cell->type == "$expect") + { + std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep)); + expects_a[pf].append((*sigmap)(cell->getPort("\\A"))); + expects_en[pf].append((*sigmap)(cell->getPort("\\EN"))); + return true; + } + // Unsupported internal cell types: $pow $lut // .. and all sequential cells except $dff and $_DFF_[NP]_ return false; diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex index f97d4ffa5..759a5bb6a 100644 --- a/manual/CHAPTER_CellLib.tex +++ b/manual/CHAPTER_CellLib.tex @@ -421,7 +421,7 @@ pass. The combinatorial logic cells can be mapped to physical cells from a Liber using the {\tt abc} pass. \begin{fixme} -Add information about {\tt \$assert}, {\tt \$assume}, and {\tt \$equiv} cells. +Add information about {\tt \$assert}, {\tt \$assume}, {\tt \$expect}, and {\tt \$equiv} cells. \end{fixme} \begin{fixme} diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 94b93de5d..14d67884e 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -313,7 +313,7 @@ bool set_keep_assert(std::map &cache, RTLIL::Module *mod) if (cache.count(mod) == 0) for (auto c : mod->cells()) { RTLIL::Module *m = mod->design->module(c->type); - if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in("$assert", "$assume")) + if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in("$assert", "$assume", "$expect")) return cache[mod] = true; } return cache[mod]; diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 6600ffa25..d905a30bf 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -64,7 +64,7 @@ struct keep_cache_t bool query(Cell *cell) { - if (cell->type.in("$memwr", "$meminit", "$assert", "$assume")) + if (cell->type.in("$memwr", "$meminit", "$assert", "$assume", "$expect")) return true; if (cell->has_keep_attr()) diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index 8b800d414..1bff02a21 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -730,6 +730,8 @@ struct TestCellPass : public Pass { // cell_types["$slice"] = "A"; // cell_types["$concat"] = "A"; // cell_types["$assert"] = "A"; + // cell_types["$assume"] = "A"; + // cell_types["$expect"] = "A"; cell_types["$lut"] = "*"; cell_types["$sop"] = "*"; diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index 342555024..ea36e2922 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -1305,6 +1305,22 @@ endmodule // -------------------------------------------------------- +module \$expect (A, EN); + +input A, EN; + +`ifndef SIMLIB_NOCHECKS +always @* begin + if (A === 1'b1 && EN === 1'b1) begin + $display("Expectation %m passed."); + end +end +`endif + +endmodule + +// -------------------------------------------------------- + module \$equiv (A, B, Y); input A, B;