Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add $check cell to represent assertions with messages #4128

Merged
merged 6 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading