Skip to content

Commit

Permalink
Merge pull request #4128 from whitequark/check-cell
Browse files Browse the repository at this point in the history
Add `$check` cell to represent assertions with messages
  • Loading branch information
whitequark authored Feb 3, 2024
2 parents 9f27923 + ffb82df commit 3caac53
Show file tree
Hide file tree
Showing 62 changed files with 1,140 additions and 553 deletions.
363 changes: 229 additions & 134 deletions backends/cxxrtl/cxxrtl_backend.cc

Large diffs are not rendered by default.

27 changes: 26 additions & 1 deletion backends/cxxrtl/runtime/cxxrtl/cxxrtl.h
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,23 @@ struct lazy_fmt {
virtual std::string operator() () const = 0;
};

// An object that can be passed to a `eval()` method in order to act on side effects.
// Flavor of a `$check` cell.
enum class flavor {
// Corresponds to a `$assert` cell in other flows, and a Verilog `assert ()` statement.
ASSERT,
// Corresponds to a `$assume` cell in other flows, and a Verilog `assume ()` statement.
ASSUME,
// Corresponds to a `$live` cell in other flows, and a Verilog `assert (eventually)` statement.
ASSERT_EVENTUALLY,
// Corresponds to a `$fair` cell in other flows, and a Verilog `assume (eventually)` statement.
ASSUME_EVENTUALLY,
// Corresponds to a `$cover` cell in other flows, and a Verilog `cover ()` statement.
COVER,
};

// An object that can be passed to a `eval()` method in order to act on side effects. The default behavior implemented
// below is the same as the behavior of `eval(nullptr)`, except that `-print-output` option of `write_cxxrtl` is not
// taken into account.
struct performer {
// Called by generated formatting code to evaluate a Verilog `$time` expression.
virtual int64_t vlog_time() const { return 0; }
Expand All @@ -964,6 +980,15 @@ struct performer {
virtual void on_print(const lazy_fmt &formatter, const metadata_map &attributes) {
std::cout << formatter();
}

// Called when a `$check` cell is triggered.
virtual void on_check(flavor type, bool condition, const lazy_fmt &formatter, const metadata_map &attributes) {
if (type == flavor::ASSERT || type == flavor::ASSUME) {
if (!condition)
std::cerr << formatter();
CXXRTL_ASSERT(condition && "Check failed");
}
}
};

// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in
Expand Down
88 changes: 79 additions & 9 deletions backends/verilog/verilog_backend.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1008,7 +1008,7 @@ void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell

void dump_cell_expr_print(std::ostream &f, std::string indent, const RTLIL::Cell *cell)
{
Fmt fmt = {};
Fmt fmt;
fmt.parse_rtlil(cell);
std::vector<VerilogFmtArg> args = fmt.emit_verilog();

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

void dump_cell_expr_check(std::ostream &f, std::string indent, const RTLIL::Cell *cell)
{
std::string flavor = cell->getParam(ID(FLAVOR)).decode_string();
if (flavor == "assert")
f << stringf("%s" "assert (", indent.c_str());
else if (flavor == "assume")
f << stringf("%s" "assume (", indent.c_str());
else if (flavor == "live")
f << stringf("%s" "assert (eventually ", indent.c_str());
else if (flavor == "fair")
f << stringf("%s" "assume (eventually ", indent.c_str());
else if (flavor == "cover")
f << stringf("%s" "cover (", indent.c_str());
dump_sigspec(f, cell->getPort(ID::A));
f << stringf(");\n");
}

bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
{
if (cell->type == ID($_NOT_)) {
Expand Down Expand Up @@ -1814,6 +1831,39 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}

if (cell->type == ID($check))
{
// Sync $check cells are accumulated and handled in dump_module.
if (cell->getParam(ID::TRG_ENABLE).as_bool())
return true;

f << stringf("%s" "always @*\n", indent.c_str());

f << stringf("%s" " if (", indent.c_str());
dump_sigspec(f, cell->getPort(ID::EN));
f << stringf(") begin\n");

std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
if (flavor == "assert" || flavor == "assume") {
Fmt fmt;
fmt.parse_rtlil(cell);
if (!fmt.parts.empty()) {
f << stringf("%s" " if (!", indent.c_str());
dump_sigspec(f, cell->getPort(ID::A));
f << stringf(")\n");
dump_cell_expr_print(f, indent + " ", cell);
}
} else {
f << stringf("%s" " /* message omitted */\n", indent.c_str());
}

dump_cell_expr_check(f, indent + " ", cell);

f << stringf("%s" " end\n", indent.c_str());

return true;
}

// FIXME: $fsm

return false;
Expand Down Expand Up @@ -1903,7 +1953,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
}
}

void dump_sync_print(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector<const RTLIL::Cell*> &cells)
void dump_sync_effect(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector<const RTLIL::Cell*> &cells)
{
if (trg.size() == 0) {
f << stringf("%s" "initial begin\n", indent.c_str());
Expand All @@ -1927,9 +1977,29 @@ void dump_sync_print(std::ostream &f, std::string indent, const RTLIL::SigSpec &
for (auto cell : cells) {
f << stringf("%s" " if (", indent.c_str());
dump_sigspec(f, cell->getPort(ID::EN));
f << stringf(")\n");
f << stringf(") begin\n");

if (cell->type == ID($print)) {
dump_cell_expr_print(f, indent + " ", cell);
} else if (cell->type == ID($check)) {
std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
if (flavor == "assert" || flavor == "assume") {
Fmt fmt;
fmt.parse_rtlil(cell);
if (!fmt.parts.empty()) {
f << stringf("%s" " if (!", indent.c_str());
dump_sigspec(f, cell->getPort(ID::A));
f << stringf(")\n");
dump_cell_expr_print(f, indent + " ", cell);
}
} else {
f << stringf("%s" " /* message omitted */\n", indent.c_str());
}

dump_cell_expr_print(f, indent + " ", cell);
dump_cell_expr_check(f, indent + " ", cell);
}

f << stringf("%s" " end\n", indent.c_str());
}

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

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

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

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

for (auto &it : sync_print_cells)
dump_sync_print(f, indent + " ", it.first.first, it.first.second, it.second);
for (auto &it : sync_effect_cells)
dump_sync_effect(f, indent + " ", it.first.first, it.first.second, it.second);

for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
dump_process(f, indent + " ", it->second);
Expand Down
6 changes: 3 additions & 3 deletions docs/source/CHAPTER_CellLib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ Add information about ``$specify2``, ``$specify3``, and ``$specrule`` cells.
Formal verification cells
~~~~~~~~~~~~~~~~~~~~~~~~~

Add information about ``$assert``, ``$assume``, ``$live``, ``$fair``,
Add information about ``$check``, ``$assert``, ``$assume``, ``$live``, ``$fair``,
``$cover``, ``$equiv``, ``$initstate``, ``$anyconst``, ``$anyseq``,
``$anyinit``, ``$allconst``, ``$allseq`` cells.

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

``\PRIORITY``
When multiple ``$print`` cells fire on the same trigger, they execute in
descending priority order.
When multiple ``$print`` or ``$$check`` cells fire on the same trigger, they\
execute in descending priority order.

Ports:

Expand Down
Loading

0 comments on commit 3caac53

Please sign in to comment.