Skip to content

Commit 41b2cf5

Browse files
committed
Add new $check cell to represent assertions with a message.
1 parent 1b9fdcf commit 41b2cf5

File tree

12 files changed

+515
-305
lines changed

12 files changed

+515
-305
lines changed

backends/cxxrtl/cxxrtl_backend.cc

Lines changed: 229 additions & 134 deletions
Large diffs are not rendered by default.

backends/cxxrtl/runtime/cxxrtl/cxxrtl.h

Lines changed: 26 additions & 1 deletion
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

Lines changed: 79 additions & 9 deletions
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_)) {
@@ -1805,6 +1822,39 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
18051822
return true;
18061823
}
18071824

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

18101860
return false;
@@ -1894,7 +1944,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
18941944
}
18951945
}
18961946

1897-
void dump_sync_print(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector<const RTLIL::Cell*> &cells)
1947+
void dump_sync_effect(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector<const RTLIL::Cell*> &cells)
18981948
{
18991949
if (trg.size() == 0) {
19001950
f << stringf("%s" "initial begin\n", indent.c_str());
@@ -1918,9 +1968,29 @@ void dump_sync_print(std::ostream &f, std::string indent, const RTLIL::SigSpec &
19181968
for (auto cell : cells) {
19191969
f << stringf("%s" " if (", indent.c_str());
19201970
dump_sigspec(f, cell->getPort(ID::EN));
1921-
f << stringf(")\n");
1971+
f << stringf(") begin\n");
1972+
1973+
if (cell->type == ID($print)) {
1974+
dump_cell_expr_print(f, indent + " ", cell);
1975+
} else if (cell->type == ID($check)) {
1976+
std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
1977+
if (flavor == "assert" || flavor == "assume") {
1978+
Fmt fmt;
1979+
fmt.parse_rtlil(cell);
1980+
if (!fmt.parts.empty()) {
1981+
f << stringf("%s" " if (!", indent.c_str());
1982+
dump_sigspec(f, cell->getPort(ID::A));
1983+
f << stringf(")\n");
1984+
dump_cell_expr_print(f, indent + " ", cell);
1985+
}
1986+
} else {
1987+
f << stringf("%s" " /* message omitted */\n", indent.c_str());
1988+
}
19221989

1923-
dump_cell_expr_print(f, indent + " ", cell);
1990+
dump_cell_expr_check(f, indent + " ", cell);
1991+
}
1992+
1993+
f << stringf("%s" " end\n", indent.c_str());
19241994
}
19251995

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

21722242
void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
21732243
{
2174-
std::map<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> sync_print_cells;
2244+
std::map<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> sync_effect_cells;
21752245

21762246
reg_wires.clear();
21772247
reset_auto_counter(module);
@@ -2203,8 +2273,8 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
22032273
std::set<std::pair<RTLIL::Wire*,int>> reg_bits;
22042274
for (auto cell : module->cells())
22052275
{
2206-
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
2207-
sync_print_cells[make_pair(cell->getPort(ID::TRG), cell->getParam(ID::TRG_POLARITY))].push_back(cell);
2276+
if (cell->type.in(ID($print), ID($check)) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
2277+
sync_effect_cells[make_pair(cell->getPort(ID::TRG), cell->getParam(ID::TRG_POLARITY))].push_back(cell);
22082278
continue;
22092279
}
22102280

@@ -2263,8 +2333,8 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
22632333
for (auto cell : module->cells())
22642334
dump_cell(f, indent + " ", cell);
22652335

2266-
for (auto &it : sync_print_cells)
2267-
dump_sync_print(f, indent + " ", it.first.first, it.first.second, it.second);
2336+
for (auto &it : sync_effect_cells)
2337+
dump_sync_effect(f, indent + " ", it.first.first, it.first.second, it.second);
22682338

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

docs/source/CHAPTER_CellLib.rst

Lines changed: 3 additions & 3 deletions
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)