Skip to content

Commit 3caac53

Browse files
authored
Merge pull request #4128 from whitequark/check-cell
Add `$check` cell to represent assertions with messages
2 parents 9f27923 + ffb82df commit 3caac53

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1140
-553
lines changed

backends/cxxrtl/cxxrtl_backend.cc

+229-134
Large diffs are not rendered by default.

backends/cxxrtl/runtime/cxxrtl/cxxrtl.h

+26-1
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,23 @@ struct lazy_fmt {
952952
virtual std::string operator() () const = 0;
953953
};
954954

955-
// An object that can be passed to a `eval()` method in order to act on side effects.
955+
// Flavor of a `$check` cell.
956+
enum class flavor {
957+
// Corresponds to a `$assert` cell in other flows, and a Verilog `assert ()` statement.
958+
ASSERT,
959+
// Corresponds to a `$assume` cell in other flows, and a Verilog `assume ()` statement.
960+
ASSUME,
961+
// Corresponds to a `$live` cell in other flows, and a Verilog `assert (eventually)` statement.
962+
ASSERT_EVENTUALLY,
963+
// Corresponds to a `$fair` cell in other flows, and a Verilog `assume (eventually)` statement.
964+
ASSUME_EVENTUALLY,
965+
// Corresponds to a `$cover` cell in other flows, and a Verilog `cover ()` statement.
966+
COVER,
967+
};
968+
969+
// An object that can be passed to a `eval()` method in order to act on side effects. The default behavior implemented
970+
// below is the same as the behavior of `eval(nullptr)`, except that `-print-output` option of `write_cxxrtl` is not
971+
// taken into account.
956972
struct performer {
957973
// Called by generated formatting code to evaluate a Verilog `$time` expression.
958974
virtual int64_t vlog_time() const { return 0; }
@@ -964,6 +980,15 @@ struct performer {
964980
virtual void on_print(const lazy_fmt &formatter, const metadata_map &attributes) {
965981
std::cout << formatter();
966982
}
983+
984+
// Called when a `$check` cell is triggered.
985+
virtual void on_check(flavor type, bool condition, const lazy_fmt &formatter, const metadata_map &attributes) {
986+
if (type == flavor::ASSERT || type == flavor::ASSUME) {
987+
if (!condition)
988+
std::cerr << formatter();
989+
CXXRTL_ASSERT(condition && "Check failed");
990+
}
991+
}
967992
};
968993

969994
// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in

backends/verilog/verilog_backend.cc

+79-9
Original file line numberDiff line numberDiff line change
@@ -1008,7 +1008,7 @@ void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell
10081008

10091009
void dump_cell_expr_print(std::ostream &f, std::string indent, const RTLIL::Cell *cell)
10101010
{
1011-
Fmt fmt = {};
1011+
Fmt fmt;
10121012
fmt.parse_rtlil(cell);
10131013
std::vector<VerilogFmtArg> args = fmt.emit_verilog();
10141014

@@ -1041,6 +1041,23 @@ void dump_cell_expr_print(std::ostream &f, std::string indent, const RTLIL::Cell
10411041
f << stringf(");\n");
10421042
}
10431043

1044+
void dump_cell_expr_check(std::ostream &f, std::string indent, const RTLIL::Cell *cell)
1045+
{
1046+
std::string flavor = cell->getParam(ID(FLAVOR)).decode_string();
1047+
if (flavor == "assert")
1048+
f << stringf("%s" "assert (", indent.c_str());
1049+
else if (flavor == "assume")
1050+
f << stringf("%s" "assume (", indent.c_str());
1051+
else if (flavor == "live")
1052+
f << stringf("%s" "assert (eventually ", indent.c_str());
1053+
else if (flavor == "fair")
1054+
f << stringf("%s" "assume (eventually ", indent.c_str());
1055+
else if (flavor == "cover")
1056+
f << stringf("%s" "cover (", indent.c_str());
1057+
dump_sigspec(f, cell->getPort(ID::A));
1058+
f << stringf(");\n");
1059+
}
1060+
10441061
bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
10451062
{
10461063
if (cell->type == ID($_NOT_)) {
@@ -1814,6 +1831,39 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
18141831
return true;
18151832
}
18161833

1834+
if (cell->type == ID($check))
1835+
{
1836+
// Sync $check cells are accumulated and handled in dump_module.
1837+
if (cell->getParam(ID::TRG_ENABLE).as_bool())
1838+
return true;
1839+
1840+
f << stringf("%s" "always @*\n", indent.c_str());
1841+
1842+
f << stringf("%s" " if (", indent.c_str());
1843+
dump_sigspec(f, cell->getPort(ID::EN));
1844+
f << stringf(") begin\n");
1845+
1846+
std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
1847+
if (flavor == "assert" || flavor == "assume") {
1848+
Fmt fmt;
1849+
fmt.parse_rtlil(cell);
1850+
if (!fmt.parts.empty()) {
1851+
f << stringf("%s" " if (!", indent.c_str());
1852+
dump_sigspec(f, cell->getPort(ID::A));
1853+
f << stringf(")\n");
1854+
dump_cell_expr_print(f, indent + " ", cell);
1855+
}
1856+
} else {
1857+
f << stringf("%s" " /* message omitted */\n", indent.c_str());
1858+
}
1859+
1860+
dump_cell_expr_check(f, indent + " ", cell);
1861+
1862+
f << stringf("%s" " end\n", indent.c_str());
1863+
1864+
return true;
1865+
}
1866+
18171867
// FIXME: $fsm
18181868

18191869
return false;
@@ -1903,7 +1953,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
19031953
}
19041954
}
19051955

1906-
void dump_sync_print(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector<const RTLIL::Cell*> &cells)
1956+
void dump_sync_effect(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector<const RTLIL::Cell*> &cells)
19071957
{
19081958
if (trg.size() == 0) {
19091959
f << stringf("%s" "initial begin\n", indent.c_str());
@@ -1927,9 +1977,29 @@ void dump_sync_print(std::ostream &f, std::string indent, const RTLIL::SigSpec &
19271977
for (auto cell : cells) {
19281978
f << stringf("%s" " if (", indent.c_str());
19291979
dump_sigspec(f, cell->getPort(ID::EN));
1930-
f << stringf(")\n");
1980+
f << stringf(") begin\n");
1981+
1982+
if (cell->type == ID($print)) {
1983+
dump_cell_expr_print(f, indent + " ", cell);
1984+
} else if (cell->type == ID($check)) {
1985+
std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
1986+
if (flavor == "assert" || flavor == "assume") {
1987+
Fmt fmt;
1988+
fmt.parse_rtlil(cell);
1989+
if (!fmt.parts.empty()) {
1990+
f << stringf("%s" " if (!", indent.c_str());
1991+
dump_sigspec(f, cell->getPort(ID::A));
1992+
f << stringf(")\n");
1993+
dump_cell_expr_print(f, indent + " ", cell);
1994+
}
1995+
} else {
1996+
f << stringf("%s" " /* message omitted */\n", indent.c_str());
1997+
}
19311998

1932-
dump_cell_expr_print(f, indent + " ", cell);
1999+
dump_cell_expr_check(f, indent + " ", cell);
2000+
}
2001+
2002+
f << stringf("%s" " end\n", indent.c_str());
19332003
}
19342004

19352005
f << stringf("%s" "end\n", indent.c_str());
@@ -2182,7 +2252,7 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo
21822252

21832253
void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
21842254
{
2185-
std::map<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> sync_print_cells;
2255+
std::map<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> sync_effect_cells;
21862256

21872257
reg_wires.clear();
21882258
reset_auto_counter(module);
@@ -2214,8 +2284,8 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
22142284
std::set<std::pair<RTLIL::Wire*,int>> reg_bits;
22152285
for (auto cell : module->cells())
22162286
{
2217-
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
2218-
sync_print_cells[make_pair(cell->getPort(ID::TRG), cell->getParam(ID::TRG_POLARITY))].push_back(cell);
2287+
if (cell->type.in(ID($print), ID($check)) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
2288+
sync_effect_cells[make_pair(cell->getPort(ID::TRG), cell->getParam(ID::TRG_POLARITY))].push_back(cell);
22192289
continue;
22202290
}
22212291

@@ -2274,8 +2344,8 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
22742344
for (auto cell : module->cells())
22752345
dump_cell(f, indent + " ", cell);
22762346

2277-
for (auto &it : sync_print_cells)
2278-
dump_sync_print(f, indent + " ", it.first.first, it.first.second, it.second);
2347+
for (auto &it : sync_effect_cells)
2348+
dump_sync_effect(f, indent + " ", it.first.first, it.first.second, it.second);
22792349

22802350
for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
22812351
dump_process(f, indent + " ", it->second);

docs/source/CHAPTER_CellLib.rst

+3-3
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ Add information about ``$specify2``, ``$specify3``, and ``$specrule`` cells.
621621
Formal verification cells
622622
~~~~~~~~~~~~~~~~~~~~~~~~~
623623

624-
Add information about ``$assert``, ``$assume``, ``$live``, ``$fair``,
624+
Add information about ``$check``, ``$assert``, ``$assume``, ``$live``, ``$fair``,
625625
``$cover``, ``$equiv``, ``$initstate``, ``$anyconst``, ``$anyseq``,
626626
``$anyinit``, ``$allconst``, ``$allseq`` cells.
627627

@@ -654,8 +654,8 @@ If ``\TRG_ENABLE`` is true, the following parameters also apply:
654654
negative-edge triggered.
655655

656656
``\PRIORITY``
657-
When multiple ``$print`` cells fire on the same trigger, they execute in
658-
descending priority order.
657+
When multiple ``$print`` or ``$$check`` cells fire on the same trigger, they\
658+
execute in descending priority order.
659659

660660
Ports:
661661

0 commit comments

Comments
 (0)