mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-24 01:25:33 +00:00
Add new $check
cell to represent assertions with a message.
This commit is contained in:
parent
e1a59ba80b
commit
c7bf0e3b8f
12 changed files with 516 additions and 306 deletions
|
@ -218,7 +218,7 @@ bool is_internal_cell(RTLIL::IdString type)
|
|||
|
||||
bool is_effectful_cell(RTLIL::IdString type)
|
||||
{
|
||||
return type.isPublic() || type == ID($print);
|
||||
return type.in(ID($print), ID($check));
|
||||
}
|
||||
|
||||
bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell)
|
||||
|
@ -282,7 +282,7 @@ struct FlowGraph {
|
|||
CONNECT,
|
||||
CELL_SYNC,
|
||||
CELL_EVAL,
|
||||
PRINT_SYNC,
|
||||
EFFECT_SYNC,
|
||||
PROCESS_SYNC,
|
||||
PROCESS_CASE,
|
||||
MEM_RDPORT,
|
||||
|
@ -292,7 +292,7 @@ struct FlowGraph {
|
|||
Type type;
|
||||
RTLIL::SigSig connect = {};
|
||||
const RTLIL::Cell *cell = nullptr;
|
||||
std::vector<const RTLIL::Cell*> print_sync_cells;
|
||||
std::vector<const RTLIL::Cell*> cells;
|
||||
const RTLIL::Process *process = nullptr;
|
||||
const Mem *mem = nullptr;
|
||||
int portidx;
|
||||
|
@ -480,11 +480,11 @@ struct FlowGraph {
|
|||
return node;
|
||||
}
|
||||
|
||||
Node *add_print_sync_node(std::vector<const RTLIL::Cell*> cells)
|
||||
Node *add_effect_sync_node(std::vector<const RTLIL::Cell*> cells)
|
||||
{
|
||||
Node *node = new Node;
|
||||
node->type = Node::Type::PRINT_SYNC;
|
||||
node->print_sync_cells = cells;
|
||||
node->type = Node::Type::EFFECT_SYNC;
|
||||
node->cells = cells;
|
||||
nodes.push_back(node);
|
||||
return node;
|
||||
}
|
||||
|
@ -1063,99 +1063,6 @@ struct CxxrtlWorker {
|
|||
f << ".val()";
|
||||
}
|
||||
|
||||
void dump_print(const RTLIL::Cell *cell)
|
||||
{
|
||||
Fmt fmt = {};
|
||||
fmt.parse_rtlil(cell);
|
||||
|
||||
f << indent << "if (";
|
||||
dump_sigspec_rhs(cell->getPort(ID::EN));
|
||||
f << " == value<1>{1u}) {\n";
|
||||
inc_indent();
|
||||
dict<std::string, RTLIL::SigSpec> fmt_args;
|
||||
f << indent << "struct : public lazy_fmt {\n";
|
||||
inc_indent();
|
||||
f << indent << "std::string operator() () const override {\n";
|
||||
inc_indent();
|
||||
fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) {
|
||||
if (sig.size() == 0)
|
||||
f << "value<0>()";
|
||||
else {
|
||||
std::string arg_name = "arg" + std::to_string(fmt_args.size());
|
||||
fmt_args[arg_name] = sig;
|
||||
f << arg_name;
|
||||
}
|
||||
}, "performer");
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
f << indent << "struct performer *performer;\n";
|
||||
for (auto arg : fmt_args)
|
||||
f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n";
|
||||
dec_indent();
|
||||
f << indent << "} formatter;\n";
|
||||
f << indent << "formatter.performer = performer;\n";
|
||||
for (auto arg : fmt_args) {
|
||||
f << indent << "formatter." << arg.first << " = ";
|
||||
dump_sigspec_rhs(arg.second);
|
||||
f << ";\n";
|
||||
}
|
||||
f << indent << "if (performer) {\n";
|
||||
inc_indent();
|
||||
f << indent << "static const metadata_map attributes = ";
|
||||
dump_metadata_map(cell->attributes);
|
||||
f << ";\n";
|
||||
f << indent << "performer->on_print(formatter, attributes);\n";
|
||||
dec_indent();
|
||||
f << indent << "} else {\n";
|
||||
inc_indent();
|
||||
f << indent << print_output << " << formatter();\n";
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
}
|
||||
|
||||
void dump_sync_print(std::vector<const RTLIL::Cell*> &cells)
|
||||
{
|
||||
log_assert(!cells.empty());
|
||||
const auto &trg = cells[0]->getPort(ID::TRG);
|
||||
const auto &trg_polarity = cells[0]->getParam(ID::TRG_POLARITY);
|
||||
|
||||
f << indent << "if (";
|
||||
for (int i = 0; i < trg.size(); i++) {
|
||||
RTLIL::SigBit trg_bit = trg[i];
|
||||
trg_bit = sigmaps[trg_bit.wire->module](trg_bit);
|
||||
log_assert(trg_bit.wire);
|
||||
|
||||
if (i != 0)
|
||||
f << " || ";
|
||||
|
||||
if (trg_polarity[i] == State::S1)
|
||||
f << "posedge_";
|
||||
else
|
||||
f << "negedge_";
|
||||
f << mangle(trg_bit);
|
||||
}
|
||||
f << ") {\n";
|
||||
inc_indent();
|
||||
std::sort(cells.begin(), cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) {
|
||||
return a->getParam(ID::PRIORITY).as_int() > b->getParam(ID::PRIORITY).as_int();
|
||||
});
|
||||
for (auto cell : cells) {
|
||||
log_assert(cell->getParam(ID::TRG_ENABLE).as_bool());
|
||||
log_assert(cell->getPort(ID::TRG) == trg);
|
||||
log_assert(cell->getParam(ID::TRG_POLARITY) == trg_polarity);
|
||||
|
||||
std::vector<const RTLIL::Cell*> inlined_cells;
|
||||
collect_cell_eval(cell, /*for_debug=*/false, inlined_cells);
|
||||
dump_inlined_cells(inlined_cells);
|
||||
dump_print(cell);
|
||||
}
|
||||
dec_indent();
|
||||
|
||||
f << indent << "}\n";
|
||||
}
|
||||
|
||||
void dump_inlined_cells(const std::vector<const RTLIL::Cell*> &cells)
|
||||
{
|
||||
if (cells.empty()) {
|
||||
|
@ -1309,6 +1216,144 @@ struct CxxrtlWorker {
|
|||
}
|
||||
}
|
||||
|
||||
void dump_print(const RTLIL::Cell *cell)
|
||||
{
|
||||
Fmt fmt;
|
||||
fmt.parse_rtlil(cell);
|
||||
|
||||
f << indent << "if (";
|
||||
dump_sigspec_rhs(cell->getPort(ID::EN));
|
||||
f << " == value<1>{1u}) {\n";
|
||||
inc_indent();
|
||||
dict<std::string, RTLIL::SigSpec> fmt_args;
|
||||
f << indent << "struct : public lazy_fmt {\n";
|
||||
inc_indent();
|
||||
f << indent << "std::string operator() () const override {\n";
|
||||
inc_indent();
|
||||
fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) {
|
||||
if (sig.size() == 0)
|
||||
f << "value<0>()";
|
||||
else {
|
||||
std::string arg_name = "arg" + std::to_string(fmt_args.size());
|
||||
fmt_args[arg_name] = sig;
|
||||
f << arg_name;
|
||||
}
|
||||
}, "performer");
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
f << indent << "struct performer *performer;\n";
|
||||
for (auto arg : fmt_args)
|
||||
f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n";
|
||||
dec_indent();
|
||||
f << indent << "} formatter;\n";
|
||||
f << indent << "formatter.performer = performer;\n";
|
||||
for (auto arg : fmt_args) {
|
||||
f << indent << "formatter." << arg.first << " = ";
|
||||
dump_sigspec_rhs(arg.second);
|
||||
f << ";\n";
|
||||
}
|
||||
f << indent << "if (performer) {\n";
|
||||
inc_indent();
|
||||
f << indent << "static const metadata_map attributes = ";
|
||||
dump_metadata_map(cell->attributes);
|
||||
f << ";\n";
|
||||
f << indent << "performer->on_print(formatter, attributes);\n";
|
||||
dec_indent();
|
||||
f << indent << "} else {\n";
|
||||
inc_indent();
|
||||
f << indent << print_output << " << formatter();\n";
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
}
|
||||
|
||||
void dump_effect(const RTLIL::Cell *cell)
|
||||
{
|
||||
Fmt fmt;
|
||||
fmt.parse_rtlil(cell);
|
||||
|
||||
f << indent << "if (";
|
||||
dump_sigspec_rhs(cell->getPort(ID::EN));
|
||||
f << ") {\n";
|
||||
inc_indent();
|
||||
dict<std::string, RTLIL::SigSpec> fmt_args;
|
||||
f << indent << "struct : public lazy_fmt {\n";
|
||||
inc_indent();
|
||||
f << indent << "std::string operator() () const override {\n";
|
||||
inc_indent();
|
||||
fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) {
|
||||
if (sig.size() == 0)
|
||||
f << "value<0>()";
|
||||
else {
|
||||
std::string arg_name = "arg" + std::to_string(fmt_args.size());
|
||||
fmt_args[arg_name] = sig;
|
||||
f << arg_name;
|
||||
}
|
||||
}, "performer");
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
f << indent << "struct performer *performer;\n";
|
||||
for (auto arg : fmt_args)
|
||||
f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n";
|
||||
dec_indent();
|
||||
f << indent << "} formatter;\n";
|
||||
f << indent << "formatter.performer = performer;\n";
|
||||
for (auto arg : fmt_args) {
|
||||
f << indent << "formatter." << arg.first << " = ";
|
||||
dump_sigspec_rhs(arg.second);
|
||||
f << ";\n";
|
||||
}
|
||||
if (cell->hasPort(ID::A)) {
|
||||
f << indent << "bool condition = (bool)";
|
||||
dump_sigspec_rhs(cell->getPort(ID::A));
|
||||
f << ";\n";
|
||||
}
|
||||
f << indent << "if (performer) {\n";
|
||||
inc_indent();
|
||||
f << indent << "static const metadata_map attributes = ";
|
||||
dump_metadata_map(cell->attributes);
|
||||
f << ";\n";
|
||||
if (cell->type == ID($print)) {
|
||||
f << indent << "performer->on_print(formatter, attributes);\n";
|
||||
} else if (cell->type == ID($check)) {
|
||||
std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
|
||||
f << indent << "performer->on_check(";
|
||||
if (flavor == "assert")
|
||||
f << "flavor::ASSERT";
|
||||
else if (flavor == "assume")
|
||||
f << "flavor::ASSUME";
|
||||
else if (flavor == "live")
|
||||
f << "flavor::ASSERT_EVENTUALLY";
|
||||
else if (flavor == "fair")
|
||||
f << "flavor::ASSUME_EVENTUALLY";
|
||||
else if (flavor == "cover")
|
||||
f << "flavor::COVER";
|
||||
else log_assert(false);
|
||||
f << ", condition, formatter, attributes);\n";
|
||||
} else log_assert(false);
|
||||
dec_indent();
|
||||
f << indent << "} else {\n";
|
||||
inc_indent();
|
||||
if (cell->type == ID($print)) {
|
||||
f << indent << print_output << " << formatter();\n";
|
||||
} else if (cell->type == ID($check)) {
|
||||
std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
|
||||
if (flavor == "assert" || flavor == "assume") {
|
||||
f << indent << "if (!condition) {\n";
|
||||
inc_indent();
|
||||
f << indent << "std::cerr << formatter();\n";
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
f << indent << "CXXRTL_ASSERT(condition && \"Check failed\");\n";
|
||||
}
|
||||
} else log_assert(false);
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
}
|
||||
|
||||
void dump_cell_eval(const RTLIL::Cell *cell, bool for_debug = false)
|
||||
{
|
||||
std::vector<const RTLIL::Cell*> inlined_cells;
|
||||
|
@ -1322,30 +1367,34 @@ struct CxxrtlWorker {
|
|||
f << " = ";
|
||||
dump_cell_expr(cell, for_debug);
|
||||
f << ";\n";
|
||||
// $print cell
|
||||
} else if (cell->type == ID($print)) {
|
||||
// Effectful cells
|
||||
} else if (is_effectful_cell(cell->type)) {
|
||||
log_assert(!for_debug);
|
||||
|
||||
// Sync $print cells are grouped into PRINT_SYNC nodes in the FlowGraph.
|
||||
// Sync effectful cells are grouped into EFFECT_SYNC nodes in the FlowGraph.
|
||||
log_assert(!cell->getParam(ID::TRG_ENABLE).as_bool() || (cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0));
|
||||
|
||||
if (!cell->getParam(ID::TRG_ENABLE).as_bool()) { // async $print cell
|
||||
f << indent << "auto " << mangle(cell) << "_curr = ";
|
||||
if (!cell->getParam(ID::TRG_ENABLE).as_bool()) { // async effectful cell
|
||||
f << indent << "auto " << mangle(cell) << "_next = ";
|
||||
dump_sigspec_rhs(cell->getPort(ID::EN));
|
||||
f << ".concat(";
|
||||
dump_sigspec_rhs(cell->getPort(ID::ARGS));
|
||||
if (cell->type == ID($print))
|
||||
dump_sigspec_rhs(cell->getPort(ID::ARGS));
|
||||
else if (cell->type == ID($check))
|
||||
dump_sigspec_rhs(cell->getPort(ID::A));
|
||||
else log_assert(false);
|
||||
f << ").val();\n";
|
||||
|
||||
f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_curr) {\n";
|
||||
f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_next) {\n";
|
||||
inc_indent();
|
||||
dump_print(cell);
|
||||
f << indent << mangle(cell) << " = " << mangle(cell) << "_curr;\n";
|
||||
dump_effect(cell);
|
||||
f << indent << mangle(cell) << " = " << mangle(cell) << "_next;\n";
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
} else { // initial $print cell
|
||||
} else { // initial effectful cell
|
||||
f << indent << "if (!" << mangle(cell) << ") {\n";
|
||||
inc_indent();
|
||||
dump_print(cell);
|
||||
dump_effect(cell);
|
||||
f << indent << mangle(cell) << " = value<1>{1u};\n";
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
|
@ -1728,6 +1777,47 @@ struct CxxrtlWorker {
|
|||
}
|
||||
}
|
||||
|
||||
void dump_cell_effect_sync(std::vector<const RTLIL::Cell*> &cells)
|
||||
{
|
||||
log_assert(!cells.empty());
|
||||
const auto &trg = cells[0]->getPort(ID::TRG);
|
||||
const auto &trg_polarity = cells[0]->getParam(ID::TRG_POLARITY);
|
||||
|
||||
f << indent << "if (";
|
||||
for (int i = 0; i < trg.size(); i++) {
|
||||
RTLIL::SigBit trg_bit = trg[i];
|
||||
trg_bit = sigmaps[trg_bit.wire->module](trg_bit);
|
||||
log_assert(trg_bit.wire);
|
||||
|
||||
if (i != 0)
|
||||
f << " || ";
|
||||
|
||||
if (trg_polarity[i] == State::S1)
|
||||
f << "posedge_";
|
||||
else
|
||||
f << "negedge_";
|
||||
f << mangle(trg_bit);
|
||||
}
|
||||
f << ") {\n";
|
||||
inc_indent();
|
||||
std::sort(cells.begin(), cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) {
|
||||
return a->getParam(ID::PRIORITY).as_int() > b->getParam(ID::PRIORITY).as_int();
|
||||
});
|
||||
for (auto cell : cells) {
|
||||
log_assert(cell->getParam(ID::TRG_ENABLE).as_bool());
|
||||
log_assert(cell->getPort(ID::TRG) == trg);
|
||||
log_assert(cell->getParam(ID::TRG_POLARITY) == trg_polarity);
|
||||
|
||||
std::vector<const RTLIL::Cell*> inlined_cells;
|
||||
collect_cell_eval(cell, /*for_debug=*/false, inlined_cells);
|
||||
dump_inlined_cells(inlined_cells);
|
||||
dump_effect(cell);
|
||||
}
|
||||
dec_indent();
|
||||
|
||||
f << indent << "}\n";
|
||||
}
|
||||
|
||||
void dump_mem_rdport(const Mem *mem, int portidx, bool for_debug = false)
|
||||
{
|
||||
auto &port = mem->rd_ports[portidx];
|
||||
|
@ -2047,11 +2137,10 @@ struct CxxrtlWorker {
|
|||
}
|
||||
}
|
||||
for (auto cell : module->cells()) {
|
||||
// Certain $print cells have additional state, which must be reset as well.
|
||||
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool())
|
||||
f << indent << mangle(cell) << " = value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << ">();\n";
|
||||
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)
|
||||
f << indent << mangle(cell) << " = value<1>();\n";
|
||||
// Async and initial effectful cells have additional state, which must be reset as well.
|
||||
if (is_effectful_cell(cell->type))
|
||||
if (!cell->getParam(ID::TRG_ENABLE).as_bool() || cell->getParam(ID::TRG_WIDTH).as_int() == 0)
|
||||
f << indent << mangle(cell) << " = {};\n";
|
||||
if (is_internal_cell(cell->type))
|
||||
continue;
|
||||
f << indent << mangle(cell);
|
||||
|
@ -2099,8 +2188,8 @@ struct CxxrtlWorker {
|
|||
case FlowGraph::Node::Type::CELL_EVAL:
|
||||
dump_cell_eval(node.cell);
|
||||
break;
|
||||
case FlowGraph::Node::Type::PRINT_SYNC:
|
||||
dump_sync_print(node.print_sync_cells);
|
||||
case FlowGraph::Node::Type::EFFECT_SYNC:
|
||||
dump_cell_effect_sync(node.cells);
|
||||
break;
|
||||
case FlowGraph::Node::Type::PROCESS_CASE:
|
||||
dump_process_case(node.process);
|
||||
|
@ -2481,11 +2570,15 @@ struct CxxrtlWorker {
|
|||
f << "\n";
|
||||
bool has_cells = false;
|
||||
for (auto cell : module->cells()) {
|
||||
// Certain $print cells have additional state, which requires storage.
|
||||
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool())
|
||||
f << indent << "value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << "> " << mangle(cell) << ";\n";
|
||||
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)
|
||||
f << indent << "value<1> " << mangle(cell) << ";\n";
|
||||
// Async and initial effectful cells have additional state, which requires storage.
|
||||
if (is_effectful_cell(cell->type)) {
|
||||
if (cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)
|
||||
f << indent << "value<1> " << mangle(cell) << ";\n"; // async initial cell
|
||||
if (!cell->getParam(ID::TRG_ENABLE).as_bool() && cell->type == ID($print))
|
||||
f << indent << "value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << "> " << mangle(cell) << ";\n"; // {EN, ARGS}
|
||||
if (!cell->getParam(ID::TRG_ENABLE).as_bool() && cell->type == ID($check))
|
||||
f << indent << "value<2> " << mangle(cell) << ";\n"; // {EN, A}
|
||||
}
|
||||
if (is_internal_cell(cell->type))
|
||||
continue;
|
||||
dump_attrs(cell);
|
||||
|
@ -2803,8 +2896,8 @@ struct CxxrtlWorker {
|
|||
cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn);
|
||||
}
|
||||
|
||||
// $print cells may be triggered on posedge/negedge events.
|
||||
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
|
||||
// Effectful cells may be triggered on posedge/negedge events.
|
||||
if (is_effectful_cell(cell->type) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
|
||||
for (size_t i = 0; i < (size_t)cell->getParam(ID::TRG_WIDTH).as_int(); i++) {
|
||||
RTLIL::SigBit trg = cell->getPort(ID::TRG).extract(i, 1);
|
||||
if (is_valid_clock(trg))
|
||||
|
@ -2945,10 +3038,12 @@ struct CxxrtlWorker {
|
|||
// Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users.
|
||||
pool<FlowGraph::Node*, hash_ptr_ops> worklist;
|
||||
for (auto node : flow.nodes) {
|
||||
if (node->type == FlowGraph::Node::Type::CELL_EVAL && is_effectful_cell(node->cell->type))
|
||||
worklist.insert(node); // node has effects
|
||||
else if (node->type == FlowGraph::Node::Type::PRINT_SYNC)
|
||||
worklist.insert(node); // node is sync $print
|
||||
if (node->type == FlowGraph::Node::Type::CELL_EVAL && !is_internal_cell(node->cell->type))
|
||||
worklist.insert(node); // node evaluates a submodule
|
||||
else if (node->type == FlowGraph::Node::Type::CELL_EVAL && is_effectful_cell(node->cell->type))
|
||||
worklist.insert(node); // node has async effects
|
||||
else if (node->type == FlowGraph::Node::Type::EFFECT_SYNC)
|
||||
worklist.insert(node); // node has sync effects
|
||||
else if (node->type == FlowGraph::Node::Type::MEM_WRPORTS)
|
||||
worklist.insert(node); // node is memory write
|
||||
else if (node->type == FlowGraph::Node::Type::PROCESS_SYNC && is_memwr_process(node->process))
|
||||
|
@ -3005,21 +3100,21 @@ struct CxxrtlWorker {
|
|||
}
|
||||
|
||||
// Emit reachable nodes in eval().
|
||||
// Accumulate sync $print cells per trigger condition.
|
||||
dict<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> sync_print_cells;
|
||||
// Accumulate sync effectful cells per trigger condition.
|
||||
dict<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> effect_sync_cells;
|
||||
for (auto node : node_order)
|
||||
if (live_nodes[node]) {
|
||||
if (node->type == FlowGraph::Node::Type::CELL_EVAL &&
|
||||
node->cell->type == ID($print) &&
|
||||
is_effectful_cell(node->cell->type) &&
|
||||
node->cell->getParam(ID::TRG_ENABLE).as_bool() &&
|
||||
node->cell->getParam(ID::TRG_WIDTH).as_int() != 0)
|
||||
sync_print_cells[make_pair(node->cell->getPort(ID::TRG), node->cell->getParam(ID::TRG_POLARITY))].push_back(node->cell);
|
||||
effect_sync_cells[make_pair(node->cell->getPort(ID::TRG), node->cell->getParam(ID::TRG_POLARITY))].push_back(node->cell);
|
||||
else
|
||||
schedule[module].push_back(*node);
|
||||
}
|
||||
|
||||
for (auto &it : sync_print_cells) {
|
||||
auto node = flow.add_print_sync_node(it.second);
|
||||
for (auto &it : effect_sync_cells) {
|
||||
auto node = flow.add_effect_sync_node(it.second);
|
||||
schedule[module].push_back(*node);
|
||||
}
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
@ -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_)) {
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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");
|
||||
|
||||
dump_cell_expr_print(f, indent + " ", cell);
|
||||
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_check(f, indent + " ", cell);
|
||||
}
|
||||
|
||||
f << stringf("%s" " end\n", indent.c_str());
|
||||
}
|
||||
|
||||
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
|
|||
|
||||
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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue