mirror of https://github.com/YosysHQ/yosys.git
Merge pull request #4128 from whitequark/check-cell
Add `$check` cell to represent assertions with messages
This commit is contained in:
commit
3caac53827
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
|
|
@ -163,6 +163,28 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const
|
|||
return wire;
|
||||
}
|
||||
|
||||
static void check_unique_id(RTLIL::Module *module, RTLIL::IdString id,
|
||||
const AstNode *node, const char *to_add_kind)
|
||||
{
|
||||
auto already_exists = [&](const RTLIL::AttrObject *existing, const char *existing_kind) {
|
||||
std::string src = existing->get_string_attribute(ID::src);
|
||||
std::string location_str = "earlier";
|
||||
if (!src.empty())
|
||||
location_str = "at " + src;
|
||||
node->input_error("Cannot add %s `%s' because a %s with the same name was already created %s!\n",
|
||||
to_add_kind, id.c_str(), existing_kind, location_str.c_str());
|
||||
};
|
||||
|
||||
if (const RTLIL::Wire *wire = module->wire(id))
|
||||
already_exists(wire, "signal");
|
||||
if (const RTLIL::Cell *cell = module->cell(id))
|
||||
already_exists(cell, "cell");
|
||||
if (module->processes.count(id))
|
||||
already_exists(module->processes.at(id), "process");
|
||||
if (module->memories.count(id))
|
||||
already_exists(module->memories.at(id), "memory");
|
||||
}
|
||||
|
||||
// helper class for rewriting simple lookahead references in AST always blocks
|
||||
struct AST_INTERNAL::LookaheadRewriter
|
||||
{
|
||||
|
@ -316,10 +338,10 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
// Buffer for generating the init action
|
||||
RTLIL::SigSpec init_lvalue, init_rvalue;
|
||||
|
||||
// The most recently assigned $print cell \PRIORITY.
|
||||
int last_print_priority;
|
||||
// The most recently assigned $print or $check cell \PRIORITY.
|
||||
int last_effect_priority;
|
||||
|
||||
ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg), last_print_priority(0)
|
||||
ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg), last_effect_priority(0)
|
||||
{
|
||||
// rewrite lookahead references
|
||||
LookaheadRewriter la_rewriter(always);
|
||||
|
@ -703,8 +725,10 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
std::stringstream sstr;
|
||||
sstr << ast->str << "$" << ast->filename << ":" << ast->location.first_line << "$" << (autoidx++);
|
||||
|
||||
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($print));
|
||||
set_src_attr(cell, ast);
|
||||
Wire *en = current_module->addWire(sstr.str() + "_EN", 1);
|
||||
set_src_attr(en, ast);
|
||||
proc->root_case.actions.push_back(SigSig(en, false));
|
||||
current_case->actions.push_back(SigSig(en, true));
|
||||
|
||||
RTLIL::SigSpec triggers;
|
||||
RTLIL::Const polarity;
|
||||
|
@ -717,18 +741,15 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
polarity.bits.push_back(RTLIL::S0);
|
||||
}
|
||||
}
|
||||
cell->parameters[ID::TRG_WIDTH] = triggers.size();
|
||||
cell->parameters[ID::TRG_ENABLE] = (always->type == AST_INITIAL) || !triggers.empty();
|
||||
cell->parameters[ID::TRG_POLARITY] = polarity;
|
||||
cell->parameters[ID::PRIORITY] = --last_print_priority;
|
||||
|
||||
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($print));
|
||||
set_src_attr(cell, ast);
|
||||
cell->setParam(ID::TRG_WIDTH, triggers.size());
|
||||
cell->setParam(ID::TRG_ENABLE, (always->type == AST_INITIAL) || !triggers.empty());
|
||||
cell->setParam(ID::TRG_POLARITY, polarity);
|
||||
cell->setParam(ID::PRIORITY, --last_effect_priority);
|
||||
cell->setPort(ID::TRG, triggers);
|
||||
|
||||
Wire *wire = current_module->addWire(sstr.str() + "_EN", 1);
|
||||
set_src_attr(wire, ast);
|
||||
cell->setPort(ID::EN, wire);
|
||||
|
||||
proc->root_case.actions.push_back(SigSig(wire, false));
|
||||
current_case->actions.push_back(SigSig(wire, true));
|
||||
cell->setPort(ID::EN, en);
|
||||
|
||||
int default_base = 10;
|
||||
if (ast->str.back() == 'b')
|
||||
|
@ -766,7 +787,7 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
args.push_back(arg);
|
||||
}
|
||||
|
||||
Fmt fmt = {};
|
||||
Fmt fmt;
|
||||
fmt.parse_verilog(args, /*sformat_like=*/false, default_base, /*task_name=*/ast->str, current_module->name);
|
||||
if (ast->str.substr(0, 8) == "$display")
|
||||
fmt.append_string("\n");
|
||||
|
@ -776,6 +797,70 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
}
|
||||
break;
|
||||
|
||||
// generate $check cells
|
||||
case AST_ASSERT:
|
||||
case AST_ASSUME:
|
||||
case AST_LIVE:
|
||||
case AST_FAIR:
|
||||
case AST_COVER:
|
||||
{
|
||||
std::string flavor, desc;
|
||||
if (ast->type == AST_ASSERT) { flavor = "assert"; desc = "assert ()"; }
|
||||
if (ast->type == AST_ASSUME) { flavor = "assume"; desc = "assume ()"; }
|
||||
if (ast->type == AST_LIVE) { flavor = "live"; desc = "assert (eventually)"; }
|
||||
if (ast->type == AST_FAIR) { flavor = "fair"; desc = "assume (eventually)"; }
|
||||
if (ast->type == AST_COVER) { flavor = "cover"; desc = "cover ()"; }
|
||||
|
||||
IdString cellname;
|
||||
if (ast->str.empty())
|
||||
cellname = stringf("$%s$%s:%d$%d", flavor.c_str(), RTLIL::encode_filename(ast->filename).c_str(), ast->location.first_line, autoidx++);
|
||||
else
|
||||
cellname = ast->str;
|
||||
check_unique_id(current_module, cellname, ast, "procedural assertion");
|
||||
|
||||
RTLIL::SigSpec check = ast->children[0]->genWidthRTLIL(-1, false, &subst_rvalue_map.stdmap());
|
||||
if (GetSize(check) != 1)
|
||||
check = current_module->ReduceBool(NEW_ID, check);
|
||||
|
||||
Wire *en = current_module->addWire(cellname.str() + "_EN", 1);
|
||||
set_src_attr(en, ast);
|
||||
proc->root_case.actions.push_back(SigSig(en, false));
|
||||
current_case->actions.push_back(SigSig(en, true));
|
||||
|
||||
RTLIL::SigSpec triggers;
|
||||
RTLIL::Const polarity;
|
||||
for (auto sync : proc->syncs) {
|
||||
if (sync->type == RTLIL::STp) {
|
||||
triggers.append(sync->signal);
|
||||
polarity.bits.push_back(RTLIL::S1);
|
||||
} else if (sync->type == RTLIL::STn) {
|
||||
triggers.append(sync->signal);
|
||||
polarity.bits.push_back(RTLIL::S0);
|
||||
}
|
||||
}
|
||||
|
||||
RTLIL::Cell *cell = current_module->addCell(cellname, ID($check));
|
||||
set_src_attr(cell, ast);
|
||||
for (auto &attr : ast->attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
log_file_error(ast->filename, ast->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
cell->setParam(ID::FLAVOR, flavor);
|
||||
cell->setParam(ID::TRG_WIDTH, triggers.size());
|
||||
cell->setParam(ID::TRG_ENABLE, (always->type == AST_INITIAL) || !triggers.empty());
|
||||
cell->setParam(ID::TRG_POLARITY, polarity);
|
||||
cell->setParam(ID::PRIORITY, --last_effect_priority);
|
||||
cell->setPort(ID::TRG, triggers);
|
||||
cell->setPort(ID::EN, en);
|
||||
cell->setPort(ID::A, check);
|
||||
|
||||
// No message is emitted to ensure Verilog code roundtrips correctly.
|
||||
Fmt fmt;
|
||||
fmt.emit_rtlil(cell);
|
||||
break;
|
||||
}
|
||||
|
||||
case AST_NONE:
|
||||
case AST_FOR:
|
||||
break;
|
||||
|
@ -1242,28 +1327,6 @@ void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real
|
|||
width_hint, kWidthLimit);
|
||||
}
|
||||
|
||||
static void check_unique_id(RTLIL::Module *module, RTLIL::IdString id,
|
||||
const AstNode *node, const char *to_add_kind)
|
||||
{
|
||||
auto already_exists = [&](const RTLIL::AttrObject *existing, const char *existing_kind) {
|
||||
std::string src = existing->get_string_attribute(ID::src);
|
||||
std::string location_str = "earlier";
|
||||
if (!src.empty())
|
||||
location_str = "at " + src;
|
||||
node->input_error("Cannot add %s `%s' because a %s with the same name was already created %s!\n",
|
||||
to_add_kind, id.c_str(), existing_kind, location_str.c_str());
|
||||
};
|
||||
|
||||
if (const RTLIL::Wire *wire = module->wire(id))
|
||||
already_exists(wire, "signal");
|
||||
if (const RTLIL::Cell *cell = module->cell(id))
|
||||
already_exists(cell, "cell");
|
||||
if (module->processes.count(id))
|
||||
already_exists(module->processes.at(id), "process");
|
||||
if (module->memories.count(id))
|
||||
already_exists(module->memories.at(id), "memory");
|
||||
}
|
||||
|
||||
// create RTLIL from an AST node
|
||||
// all generated cells, wires and processes are added to the module pointed to by 'current_module'
|
||||
// when the AST node is an expression (AST_ADD, AST_BIT_XOR, etc.), the result signal is returned.
|
||||
|
@ -1945,48 +2008,50 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
}
|
||||
break;
|
||||
|
||||
// generate $assert cells
|
||||
// generate $check cells
|
||||
case AST_ASSERT:
|
||||
case AST_ASSUME:
|
||||
case AST_LIVE:
|
||||
case AST_FAIR:
|
||||
case AST_COVER:
|
||||
{
|
||||
IdString celltype;
|
||||
if (type == AST_ASSERT) celltype = ID($assert);
|
||||
if (type == AST_ASSUME) celltype = ID($assume);
|
||||
if (type == AST_LIVE) celltype = ID($live);
|
||||
if (type == AST_FAIR) celltype = ID($fair);
|
||||
if (type == AST_COVER) celltype = ID($cover);
|
||||
std::string flavor, desc;
|
||||
if (type == AST_ASSERT) { flavor = "assert"; desc = "assert property ()"; }
|
||||
if (type == AST_ASSUME) { flavor = "assume"; desc = "assume property ()"; }
|
||||
if (type == AST_LIVE) { flavor = "live"; desc = "assert property (eventually)"; }
|
||||
if (type == AST_FAIR) { flavor = "fair"; desc = "assume property (eventually)"; }
|
||||
if (type == AST_COVER) { flavor = "cover"; desc = "cover property ()"; }
|
||||
|
||||
log_assert(children.size() == 2);
|
||||
IdString cellname;
|
||||
if (str.empty())
|
||||
cellname = stringf("$%s$%s:%d$%d", flavor.c_str(), RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
|
||||
else
|
||||
cellname = str;
|
||||
check_unique_id(current_module, cellname, this, "procedural assertion");
|
||||
|
||||
RTLIL::SigSpec check = children[0]->genRTLIL();
|
||||
if (GetSize(check) != 1)
|
||||
check = current_module->ReduceBool(NEW_ID, check);
|
||||
|
||||
RTLIL::SigSpec en = children[1]->genRTLIL();
|
||||
if (GetSize(en) != 1)
|
||||
en = current_module->ReduceBool(NEW_ID, en);
|
||||
|
||||
IdString cellname;
|
||||
if (str.empty())
|
||||
cellname = stringf("%s$%s:%d$%d", celltype.c_str(), RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
|
||||
else
|
||||
cellname = str;
|
||||
|
||||
check_unique_id(current_module, cellname, this, "procedural assertion");
|
||||
RTLIL::Cell *cell = current_module->addCell(cellname, celltype);
|
||||
RTLIL::Cell *cell = current_module->addCell(cellname, ID($check));
|
||||
set_src_attr(cell, this);
|
||||
|
||||
for (auto &attr : attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
|
||||
cell->setParam(ID(FLAVOR), flavor);
|
||||
cell->parameters[ID::TRG_WIDTH] = 0;
|
||||
cell->parameters[ID::TRG_ENABLE] = 0;
|
||||
cell->parameters[ID::TRG_POLARITY] = 0;
|
||||
cell->parameters[ID::PRIORITY] = 0;
|
||||
cell->setPort(ID::TRG, RTLIL::SigSpec());
|
||||
cell->setPort(ID::EN, RTLIL::S1);
|
||||
cell->setPort(ID::A, check);
|
||||
cell->setPort(ID::EN, en);
|
||||
|
||||
// No message is emitted to ensure Verilog code roundtrips correctly.
|
||||
Fmt fmt;
|
||||
fmt.emit_rtlil(cell);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -178,7 +178,7 @@ Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_
|
|||
args.push_back(arg);
|
||||
}
|
||||
|
||||
Fmt fmt = {};
|
||||
Fmt fmt;
|
||||
fmt.parse_verilog(args, sformat_like, default_base, /*task_name=*/str, current_module->name);
|
||||
return fmt;
|
||||
}
|
||||
|
@ -784,7 +784,7 @@ AstNode *AstNode::clone_at_zero()
|
|||
pointee->type != AST_MEMORY)
|
||||
break;
|
||||
|
||||
YS_FALLTHROUGH;
|
||||
YS_FALLTHROUGH
|
||||
case AST_MEMRD:
|
||||
detectSignWidth(width_hint, sign_hint);
|
||||
return mkconst_int(0, sign_hint, width_hint);
|
||||
|
@ -3039,97 +3039,6 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
|||
}
|
||||
skip_dynamic_range_lvalue_expansion:;
|
||||
|
||||
if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME || type == AST_LIVE || type == AST_FAIR || type == AST_COVER) && current_block != NULL)
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << "$formal$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++);
|
||||
std::string id_check = sstr.str() + "_CHECK", id_en = sstr.str() + "_EN";
|
||||
|
||||
AstNode *wire_check = new AstNode(AST_WIRE);
|
||||
wire_check->str = id_check;
|
||||
wire_check->was_checked = true;
|
||||
current_ast_mod->children.push_back(wire_check);
|
||||
current_scope[wire_check->str] = wire_check;
|
||||
while (wire_check->simplify(true, 1, -1, false)) { }
|
||||
|
||||
AstNode *wire_en = new AstNode(AST_WIRE);
|
||||
wire_en->str = id_en;
|
||||
wire_en->was_checked = true;
|
||||
current_ast_mod->children.push_back(wire_en);
|
||||
if (current_always_clocked) {
|
||||
current_ast_mod->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1)))));
|
||||
current_ast_mod->children.back()->children[0]->children[0]->children[0]->str = id_en;
|
||||
current_ast_mod->children.back()->children[0]->children[0]->children[0]->was_checked = true;
|
||||
}
|
||||
current_scope[wire_en->str] = wire_en;
|
||||
while (wire_en->simplify(true, 1, -1, false)) { }
|
||||
|
||||
AstNode *check_defval;
|
||||
if (type == AST_LIVE || type == AST_FAIR) {
|
||||
check_defval = new AstNode(AST_REDUCE_BOOL, children[0]->clone());
|
||||
} else {
|
||||
std::vector<RTLIL::State> x_bit;
|
||||
x_bit.push_back(RTLIL::State::Sx);
|
||||
check_defval = mkconst_bits(x_bit, false);
|
||||
}
|
||||
|
||||
AstNode *assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), check_defval);
|
||||
assign_check->children[0]->str = id_check;
|
||||
assign_check->children[0]->was_checked = true;
|
||||
|
||||
AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, 1));
|
||||
assign_en->children[0]->str = id_en;
|
||||
assign_en->children[0]->was_checked = true;
|
||||
|
||||
AstNode *default_signals = new AstNode(AST_BLOCK);
|
||||
default_signals->children.push_back(assign_check);
|
||||
default_signals->children.push_back(assign_en);
|
||||
current_top_block->children.insert(current_top_block->children.begin(), default_signals);
|
||||
|
||||
if (type == AST_LIVE || type == AST_FAIR) {
|
||||
assign_check = nullptr;
|
||||
} else {
|
||||
assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_REDUCE_BOOL, children[0]->clone()));
|
||||
assign_check->children[0]->str = id_check;
|
||||
assign_check->children[0]->was_checked = true;
|
||||
assign_check->fixup_hierarchy_flags();
|
||||
}
|
||||
|
||||
if (current_always == nullptr || current_always->type != AST_INITIAL) {
|
||||
assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1));
|
||||
} else {
|
||||
assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_FCALL));
|
||||
assign_en->children[1]->str = "\\$initstate";
|
||||
}
|
||||
assign_en->children[0]->str = id_en;
|
||||
assign_en->children[0]->was_checked = true;
|
||||
assign_en->fixup_hierarchy_flags();
|
||||
|
||||
newNode = new AstNode(AST_BLOCK);
|
||||
if (assign_check != nullptr)
|
||||
newNode->children.push_back(assign_check);
|
||||
newNode->children.push_back(assign_en);
|
||||
|
||||
AstNode *assertnode = new AstNode(type);
|
||||
assertnode->location = location;
|
||||
assertnode->str = str;
|
||||
assertnode->children.push_back(new AstNode(AST_IDENTIFIER));
|
||||
assertnode->children.push_back(new AstNode(AST_IDENTIFIER));
|
||||
assertnode->children[0]->str = id_check;
|
||||
assertnode->children[1]->str = id_en;
|
||||
assertnode->attributes.swap(attributes);
|
||||
current_ast_mod->children.push_back(assertnode);
|
||||
|
||||
goto apply_newNode;
|
||||
}
|
||||
|
||||
if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME || type == AST_LIVE || type == AST_FAIR || type == AST_COVER) && children.size() == 1)
|
||||
{
|
||||
children.push_back(mkconst_int(1, false, 1));
|
||||
fixup_hierarchy_flags();
|
||||
did_something = true;
|
||||
}
|
||||
|
||||
// found right-hand side identifier for memory -> replace with memory read port
|
||||
if (stage > 1 && type == AST_IDENTIFIER && id2ast != NULL && id2ast->type == AST_MEMORY && !in_lvalue &&
|
||||
children.size() == 1 && children[0]->type == AST_RANGE && children[0]->children.size() == 1) {
|
||||
|
|
|
@ -102,6 +102,7 @@ struct CellTypes
|
|||
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool<RTLIL::IdString>(), true);
|
||||
setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool<RTLIL::IdString>(), true);
|
||||
setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y});
|
||||
setup_type(ID($get_tag), {ID::A}, {ID::Y});
|
||||
setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, pool<RTLIL::IdString>());
|
||||
|
|
|
@ -88,6 +88,7 @@ X(equiv_merged)
|
|||
X(equiv_region)
|
||||
X(extract_order)
|
||||
X(F)
|
||||
X(FLAVOR)
|
||||
X(FORMAT)
|
||||
X(force_downto)
|
||||
X(force_upto)
|
||||
|
|
|
@ -1068,6 +1068,12 @@ namespace {
|
|||
error(__LINE__);
|
||||
}
|
||||
|
||||
std::string param_string(const RTLIL::IdString &name)
|
||||
{
|
||||
param(name);
|
||||
return cell->parameters.at(name).decode_string();
|
||||
}
|
||||
|
||||
void port(const RTLIL::IdString& name, int width)
|
||||
{
|
||||
auto it = cell->connections_.find(name);
|
||||
|
@ -1747,6 +1753,22 @@ namespace {
|
|||
return;
|
||||
}
|
||||
|
||||
if (cell->type == ID($check)) {
|
||||
std::string flavor = param_string(ID(FLAVOR));
|
||||
if (!(flavor == "assert" || flavor == "assume" || flavor == "live" || flavor == "fair" || flavor == "cover"))
|
||||
error(__LINE__);
|
||||
param(ID(FORMAT));
|
||||
param_bool(ID::TRG_ENABLE);
|
||||
param(ID::TRG_POLARITY);
|
||||
param(ID::PRIORITY);
|
||||
port(ID::A, 1);
|
||||
port(ID::EN, 1);
|
||||
port(ID::TRG, param(ID::TRG_WIDTH));
|
||||
port(ID::ARGS, param(ID::ARGS_WIDTH));
|
||||
check_expected();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type == ID($_BUF_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; }
|
||||
if (cell->type == ID($_NOT_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; }
|
||||
if (cell->type == ID($_AND_)) { port(ID::A,1); port(ID::B,1); port(ID::Y,1); check_expected(); return; }
|
||||
|
|
|
@ -23,6 +23,52 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
static RTLIL::IdString formal_flavor(RTLIL::Cell *cell)
|
||||
{
|
||||
if (cell->type != ID($check))
|
||||
return cell->type;
|
||||
|
||||
std::string flavor_param = cell->getParam(ID(FLAVOR)).decode_string();
|
||||
if (flavor_param == "assert")
|
||||
return ID($assert);
|
||||
else if (flavor_param == "assume")
|
||||
return ID($assume);
|
||||
else if (flavor_param == "cover")
|
||||
return ID($cover);
|
||||
else if (flavor_param == "live")
|
||||
return ID($live);
|
||||
else if (flavor_param == "fair")
|
||||
return ID($fair);
|
||||
else
|
||||
log_abort();
|
||||
}
|
||||
|
||||
static void set_formal_flavor(RTLIL::Cell *cell, RTLIL::IdString flavor)
|
||||
{
|
||||
if (cell->type != ID($check)) {
|
||||
cell->type = flavor;
|
||||
return;
|
||||
}
|
||||
|
||||
if (flavor == ID($assert))
|
||||
cell->setParam(ID(FLAVOR), std::string("assert"));
|
||||
else if (flavor == ID($assume))
|
||||
cell->setParam(ID(FLAVOR), std::string("assume"));
|
||||
else if (flavor == ID($cover))
|
||||
cell->setParam(ID(FLAVOR), std::string("cover"));
|
||||
else if (flavor == ID($live))
|
||||
cell->setParam(ID(FLAVOR), std::string("live"));
|
||||
else if (flavor == ID($fair))
|
||||
cell->setParam(ID(FLAVOR), std::string("fair"));
|
||||
else
|
||||
log_abort();
|
||||
}
|
||||
|
||||
static bool is_triggered_check_cell(RTLIL::Cell * cell)
|
||||
{
|
||||
return cell->type == ID($check) && cell->getParam(ID(TRG_ENABLE)).as_bool();
|
||||
}
|
||||
|
||||
struct ChformalPass : public Pass {
|
||||
ChformalPass() : Pass("chformal", "change formal constraints of the design") { }
|
||||
void help() override
|
||||
|
@ -41,13 +87,18 @@ struct ChformalPass : public Pass {
|
|||
log(" -fair $fair cells, representing assume(s_eventually ...)\n");
|
||||
log(" -cover $cover cells, representing cover() statements\n");
|
||||
log("\n");
|
||||
log(" Additionally chformal will operate on $check cells corresponding to the\n");
|
||||
log(" selected constraint types.\n");
|
||||
log("\n");
|
||||
log("Exactly one of the following modes must be specified:\n");
|
||||
log("\n");
|
||||
log(" -remove\n");
|
||||
log(" remove the cells and thus constraints from the design\n");
|
||||
log("\n");
|
||||
log(" -early\n");
|
||||
log(" bypass FFs that only delay the activation of a constraint\n");
|
||||
log(" bypass FFs that only delay the activation of a constraint. When inputs\n");
|
||||
log(" of the bypassed FFs do not remain stable between clock edges, this may\n");
|
||||
log(" result in unexpected behavior.\n");
|
||||
log("\n");
|
||||
log(" -delay <N>\n");
|
||||
log(" delay activation of the constraint by <N> clock cycles\n");
|
||||
|
@ -69,6 +120,11 @@ struct ChformalPass : public Pass {
|
|||
log(" -fair2live\n");
|
||||
log(" change the roles of cells as indicated. these options can be combined\n");
|
||||
log("\n");
|
||||
log(" -lower\n");
|
||||
log(" convert each $check cell into an $assert, $assume, $live, $fair or\n");
|
||||
log(" $cover cell. If the $check cell contains a message, also produce a\n");
|
||||
log(" $print cell.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
|
@ -146,6 +202,10 @@ struct ChformalPass : public Pass {
|
|||
mode = 'c';
|
||||
continue;
|
||||
}
|
||||
if (mode == 0 && args[argidx] == "-lower") {
|
||||
mode = 'l';
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
@ -166,7 +226,7 @@ struct ChformalPass : public Pass {
|
|||
vector<Cell*> constr_cells;
|
||||
|
||||
for (auto cell : module->selected_cells())
|
||||
if (constr_types.count(cell->type))
|
||||
if (constr_types.count(formal_flavor(cell)))
|
||||
constr_cells.push_back(cell);
|
||||
|
||||
if (mode == 'r')
|
||||
|
@ -216,6 +276,18 @@ struct ChformalPass : public Pass {
|
|||
}
|
||||
|
||||
for (auto cell : constr_cells)
|
||||
{
|
||||
if (is_triggered_check_cell(cell)) {
|
||||
if (cell->getParam(ID::TRG_WIDTH).as_int() != 1)
|
||||
continue;
|
||||
cell->setPort(ID::TRG, SigSpec());
|
||||
cell->setParam(ID::TRG_ENABLE, false);
|
||||
cell->setParam(ID::TRG_WIDTH, 0);
|
||||
cell->setParam(ID::TRG_POLARITY, false);
|
||||
}
|
||||
|
||||
IdString flavor = formal_flavor(cell);
|
||||
|
||||
while (true)
|
||||
{
|
||||
SigSpec A = sigmap(cell->getPort(ID::A));
|
||||
|
@ -225,8 +297,8 @@ struct ChformalPass : public Pass {
|
|||
break;
|
||||
|
||||
if (!init_zero.count(EN)) {
|
||||
if (cell->type == ID($cover)) break;
|
||||
if (cell->type.in(ID($assert), ID($assume)) && !init_one.count(A)) break;
|
||||
if (flavor == ID($cover)) break;
|
||||
if (flavor.in(ID($assert), ID($assume)) && !init_one.count(A)) break;
|
||||
}
|
||||
|
||||
const auto &A_map = ffmap.at(A);
|
||||
|
@ -238,25 +310,31 @@ struct ChformalPass : public Pass {
|
|||
cell->setPort(ID::A, A_map.first);
|
||||
cell->setPort(ID::EN, EN_map.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (mode == 'd')
|
||||
{
|
||||
for (auto cell : constr_cells)
|
||||
for (int i = 0; i < mode_arg; i++)
|
||||
{
|
||||
SigSpec orig_a = cell->getPort(ID::A);
|
||||
SigSpec orig_en = cell->getPort(ID::EN);
|
||||
if (is_triggered_check_cell(cell))
|
||||
log_error("Cannot delay edge triggered $check cell %s, run async2sync or clk2fflogic first.\n", log_id(cell));
|
||||
|
||||
Wire *new_a = module->addWire(NEW_ID);
|
||||
Wire *new_en = module->addWire(NEW_ID);
|
||||
new_en->attributes[ID::init] = State::S0;
|
||||
for (int i = 0; i < mode_arg; i++)
|
||||
{
|
||||
SigSpec orig_a = cell->getPort(ID::A);
|
||||
SigSpec orig_en = cell->getPort(ID::EN);
|
||||
|
||||
module->addFf(NEW_ID, orig_a, new_a);
|
||||
module->addFf(NEW_ID, orig_en, new_en);
|
||||
Wire *new_a = module->addWire(NEW_ID);
|
||||
Wire *new_en = module->addWire(NEW_ID);
|
||||
new_en->attributes[ID::init] = State::S0;
|
||||
|
||||
cell->setPort(ID::A, new_a);
|
||||
cell->setPort(ID::EN, new_en);
|
||||
module->addFf(NEW_ID, orig_a, new_a);
|
||||
module->addFf(NEW_ID, orig_en, new_en);
|
||||
|
||||
cell->setPort(ID::A, new_a);
|
||||
cell->setPort(ID::EN, new_en);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -278,21 +356,76 @@ struct ChformalPass : public Pass {
|
|||
if (mode =='p')
|
||||
{
|
||||
for (auto cell : constr_cells)
|
||||
module->addCover(NEW_ID_SUFFIX("coverenable"),
|
||||
cell->getPort(ID::EN), State::S1, cell->get_src_attribute());
|
||||
{
|
||||
if (cell->type == ID($check)) {
|
||||
Cell *cover = module->addCell(NEW_ID_SUFFIX("coverenable"), ID($check));
|
||||
cover->attributes = cell->attributes;
|
||||
cover->parameters = cell->parameters;
|
||||
cover->setParam(ID(FLAVOR), Const("cover"));
|
||||
|
||||
for (auto const &conn : cell->connections())
|
||||
if (!conn.first.in(ID::A, ID::EN))
|
||||
cover->setPort(conn.first, conn.second);
|
||||
cover->setPort(ID::A, cell->getPort(ID::EN));
|
||||
cover->setPort(ID::EN, State::S1);
|
||||
} else {
|
||||
module->addCover(NEW_ID_SUFFIX("coverenable"),
|
||||
cell->getPort(ID::EN), State::S1, cell->get_src_attribute());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (mode == 'c')
|
||||
{
|
||||
for (auto cell : constr_cells)
|
||||
if (assert2assume && cell->type == ID($assert))
|
||||
cell->type = ID($assume);
|
||||
else if (assume2assert && cell->type == ID($assume))
|
||||
cell->type = ID($assert);
|
||||
else if (live2fair && cell->type == ID($live))
|
||||
cell->type = ID($fair);
|
||||
else if (fair2live && cell->type == ID($fair))
|
||||
cell->type = ID($live);
|
||||
for (auto cell : constr_cells) {
|
||||
IdString flavor = formal_flavor(cell);
|
||||
if (assert2assume && flavor == ID($assert))
|
||||
set_formal_flavor(cell, ID($assume));
|
||||
else if (assume2assert && flavor == ID($assume))
|
||||
set_formal_flavor(cell, ID($assert));
|
||||
else if (live2fair && flavor == ID($live))
|
||||
set_formal_flavor(cell, ID($fair));
|
||||
else if (fair2live && flavor == ID($fair))
|
||||
set_formal_flavor(cell, ID($live));
|
||||
}
|
||||
}
|
||||
else
|
||||
if (mode == 'l')
|
||||
{
|
||||
for (auto cell : constr_cells) {
|
||||
if (cell->type != ID($check))
|
||||
continue;
|
||||
|
||||
if (is_triggered_check_cell(cell))
|
||||
log_error("Cannot lower edge triggered $check cell %s, run async2sync or clk2fflogic first.\n", log_id(cell));
|
||||
|
||||
|
||||
Cell *plain_cell = module->addCell(NEW_ID, formal_flavor(cell));
|
||||
|
||||
plain_cell->attributes = cell->attributes;
|
||||
|
||||
SigBit sig_a = cell->getPort(ID::A);
|
||||
SigBit sig_en = cell->getPort(ID::EN);
|
||||
|
||||
plain_cell->setPort(ID::A, sig_a);
|
||||
plain_cell->setPort(ID::EN, sig_en);
|
||||
|
||||
if (plain_cell->type.in(ID($assert), ID($assume)))
|
||||
sig_a = module->Not(NEW_ID, sig_a);
|
||||
|
||||
SigBit combined_en = module->And(NEW_ID, sig_a, sig_en);
|
||||
|
||||
module->swap_names(cell, plain_cell);
|
||||
|
||||
if (cell->getPort(ID::ARGS).empty()) {
|
||||
module->remove(cell);
|
||||
} else {
|
||||
cell->type = ID($print);
|
||||
cell->setPort(ID::EN, combined_en);
|
||||
cell->unsetPort(ID::A);
|
||||
cell->unsetParam(ID(FLAVOR));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -671,7 +671,7 @@ bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod)
|
|||
if (cache.count(mod) == 0)
|
||||
for (auto c : mod->cells()) {
|
||||
RTLIL::Module *m = mod->design->module(c->type);
|
||||
if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
|
||||
if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in(ID($check), ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
|
||||
return cache[mod] = true;
|
||||
}
|
||||
return cache[mod];
|
||||
|
|
|
@ -82,7 +82,7 @@ struct keep_cache_t
|
|||
if (!ignore_specify && cell->type.in(ID($specify2), ID($specify3), ID($specrule)))
|
||||
return true;
|
||||
|
||||
if (cell->type == ID($print))
|
||||
if (cell->type == ID($print) || cell->type == ID($check))
|
||||
return true;
|
||||
|
||||
if (cell->has_keep_attr())
|
||||
|
|
|
@ -41,31 +41,88 @@ struct Async2syncPass : public Pass {
|
|||
log("reset value in the next cycle regardless of the data-in value at the time of\n");
|
||||
log("the clock edge.\n");
|
||||
log("\n");
|
||||
log(" -nolower\n");
|
||||
log(" Do not automatically run 'chformal -lower' to lower $check cells.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
// bool flag_noinit = false;
|
||||
bool flag_nolower = false;
|
||||
|
||||
log_header(design, "Executing ASYNC2SYNC pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-noinit") {
|
||||
// flag_noinit = true;
|
||||
// continue;
|
||||
// }
|
||||
if (args[argidx] == "-nolower") {
|
||||
flag_nolower = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
bool have_check_cells = false;
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
SigMap sigmap(module);
|
||||
FfInitVals initvals(&sigmap, module);
|
||||
|
||||
SigBit initstate;
|
||||
|
||||
for (auto cell : vector<Cell*>(module->selected_cells()))
|
||||
{
|
||||
if (cell->type.in(ID($print), ID($check)))
|
||||
{
|
||||
if (cell->type == ID($check))
|
||||
have_check_cells = true;
|
||||
|
||||
bool trg_enable = cell->getParam(ID(TRG_ENABLE)).as_bool();
|
||||
if (!trg_enable)
|
||||
continue;
|
||||
|
||||
int trg_width = cell->getParam(ID(TRG_WIDTH)).as_int();
|
||||
|
||||
if (trg_width > 1)
|
||||
log_error("$check cell %s with TRG_WIDTH > 1 is not support by async2sync, use clk2fflogic.\n", log_id(cell));
|
||||
|
||||
if (trg_width == 0) {
|
||||
if (initstate == State::S0)
|
||||
initstate = module->Initstate(NEW_ID);
|
||||
|
||||
SigBit sig_en = cell->getPort(ID::EN);
|
||||
cell->setPort(ID::EN, module->And(NEW_ID, sig_en, initstate));
|
||||
} else {
|
||||
SigBit sig_en = cell->getPort(ID::EN);
|
||||
SigSpec sig_args = cell->getPort(ID::ARGS);
|
||||
bool trg_polarity = cell->getParam(ID(TRG_POLARITY)).as_bool();
|
||||
SigBit sig_trg = cell->getPort(ID::TRG);
|
||||
Wire *sig_en_q = module->addWire(NEW_ID);
|
||||
Wire *sig_args_q = module->addWire(NEW_ID, GetSize(sig_args));
|
||||
sig_en_q->attributes.emplace(ID::init, State::S0);
|
||||
module->addDff(NEW_ID, sig_trg, sig_en, sig_en_q, trg_polarity, cell->get_src_attribute());
|
||||
module->addDff(NEW_ID, sig_trg, sig_args, sig_args_q, trg_polarity, cell->get_src_attribute());
|
||||
cell->setPort(ID::EN, sig_en_q);
|
||||
cell->setPort(ID::ARGS, sig_args_q);
|
||||
if (cell->type == ID($check)) {
|
||||
SigBit sig_a = cell->getPort(ID::A);
|
||||
Wire *sig_a_q = module->addWire(NEW_ID);
|
||||
sig_a_q->attributes.emplace(ID::init, State::S1);
|
||||
module->addDff(NEW_ID, sig_trg, sig_a, sig_a_q, trg_polarity, cell->get_src_attribute());
|
||||
cell->setPort(ID::A, sig_a_q);
|
||||
}
|
||||
}
|
||||
|
||||
cell->setPort(ID::TRG, SigSpec());
|
||||
|
||||
cell->setParam(ID::TRG_ENABLE, false);
|
||||
cell->setParam(ID::TRG_WIDTH, 0);
|
||||
cell->setParam(ID::TRG_POLARITY, false);
|
||||
cell->set_bool_attribute(ID(trg_on_gclk));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!RTLIL::builtin_ff_cell_types().count(cell->type))
|
||||
continue;
|
||||
|
||||
|
@ -273,6 +330,12 @@ struct Async2syncPass : public Pass {
|
|||
ff.emit();
|
||||
}
|
||||
}
|
||||
|
||||
if (have_check_cells && !flag_nolower) {
|
||||
log_push();
|
||||
Pass::call(design, "chformal -lower");
|
||||
log_pop();
|
||||
}
|
||||
}
|
||||
} Async2syncPass;
|
||||
|
||||
|
|
|
@ -48,6 +48,9 @@ struct Clk2fflogicPass : public Pass {
|
|||
log("reset value in the next cycle regardless of the data-in value at the time of\n");
|
||||
log("the clock edge.\n");
|
||||
log("\n");
|
||||
log(" -nolower\n");
|
||||
log(" Do not automatically run 'chformal -lower' to lower $check cells.\n");
|
||||
log("\n");
|
||||
}
|
||||
// Active-high sampled and current value of a level-triggered control signal. Initial sampled values is low/non-asserted.
|
||||
SampledSig sample_control(Module *module, SigSpec sig, bool polarity, bool is_fine) {
|
||||
|
@ -117,21 +120,23 @@ struct Clk2fflogicPass : public Pass {
|
|||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
// bool flag_noinit = false;
|
||||
bool flag_nolower = false;
|
||||
|
||||
log_header(design, "Executing CLK2FFLOGIC pass (convert clocked FFs to generic $ff cells).\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-noinit") {
|
||||
// flag_noinit = true;
|
||||
// continue;
|
||||
// }
|
||||
if (args[argidx] == "-nolower") {
|
||||
flag_nolower = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
bool have_check_cells = false;
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
SigMap sigmap(module);
|
||||
|
@ -194,79 +199,137 @@ struct Clk2fflogicPass : public Pass {
|
|||
mem.emit();
|
||||
}
|
||||
|
||||
SigBit initstate;
|
||||
|
||||
for (auto cell : vector<Cell*>(module->selected_cells()))
|
||||
{
|
||||
SigSpec qval;
|
||||
if (RTLIL::builtin_ff_cell_types().count(cell->type)) {
|
||||
FfData ff(&initvals, cell);
|
||||
if (cell->type.in(ID($print), ID($check)))
|
||||
{
|
||||
if (cell->type == ID($check))
|
||||
have_check_cells = true;
|
||||
|
||||
if (ff.has_gclk) {
|
||||
// Already a $ff or $_FF_ cell.
|
||||
bool trg_enable = cell->getParam(ID(TRG_ENABLE)).as_bool();
|
||||
if (!trg_enable)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ff.has_clk) {
|
||||
log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
|
||||
log_id(module), log_id(cell), log_id(cell->type),
|
||||
log_signal(ff.sig_clk), log_signal(ff.sig_d), log_signal(ff.sig_q));
|
||||
} else if (ff.has_aload) {
|
||||
log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
|
||||
log_id(module), log_id(cell), log_id(cell->type),
|
||||
log_signal(ff.sig_aload), log_signal(ff.sig_ad), log_signal(ff.sig_q));
|
||||
int trg_width = cell->getParam(ID(TRG_WIDTH)).as_int();
|
||||
|
||||
if (trg_width == 0) {
|
||||
if (initstate == State::S0)
|
||||
initstate = module->Initstate(NEW_ID);
|
||||
|
||||
SigBit sig_en = cell->getPort(ID::EN);
|
||||
cell->setPort(ID::EN, module->And(NEW_ID, sig_en, initstate));
|
||||
} else {
|
||||
// $sr.
|
||||
log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n",
|
||||
log_id(module), log_id(cell), log_id(cell->type),
|
||||
log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_q));
|
||||
SigBit sig_en = cell->getPort(ID::EN);
|
||||
SigSpec sig_args = cell->getPort(ID::ARGS);
|
||||
Const trg_polarity = cell->getParam(ID(TRG_POLARITY));
|
||||
SigSpec sig_trg = cell->getPort(ID::TRG);
|
||||
|
||||
SigSpec sig_trg_sampled;
|
||||
|
||||
for (auto const &bit : sig_trg)
|
||||
sig_trg_sampled.append(sample_control_edge(module, bit, trg_polarity[GetSize(sig_trg_sampled)] == State::S1, false));
|
||||
SigSpec sig_args_sampled = sample_data(module, sig_args, Const(State::S0, GetSize(sig_args)), false, false).sampled;
|
||||
SigBit sig_en_sampled = sample_data(module, sig_en, State::S0, false, false).sampled;
|
||||
|
||||
SigBit sig_trg_combined = module->ReduceOr(NEW_ID, sig_trg_sampled);
|
||||
|
||||
cell->setPort(ID::EN, module->And(NEW_ID, sig_en_sampled, sig_trg_combined));
|
||||
cell->setPort(ID::ARGS, sig_args_sampled);
|
||||
if (cell->type == ID($check)) {
|
||||
SigBit sig_a_sampled = sample_data(module, sig_en, State::S1, false, false).sampled;
|
||||
cell->setPort(ID::A, sig_a_sampled);
|
||||
}
|
||||
}
|
||||
|
||||
ff.remove();
|
||||
cell->setPort(ID::TRG, SigSpec());
|
||||
|
||||
if (ff.has_clk)
|
||||
ff.unmap_ce_srst();
|
||||
cell->setParam(ID::TRG_ENABLE, false);
|
||||
cell->setParam(ID::TRG_WIDTH, 0);
|
||||
cell->setParam(ID::TRG_POLARITY, false);
|
||||
cell->set_bool_attribute(ID(trg_on_gclk));
|
||||
|
||||
auto next_q = sample_data(module, ff.sig_q, ff.val_init, ff.is_fine, true).sampled;
|
||||
|
||||
if (ff.has_clk) {
|
||||
// The init value for the sampled d is never used, so we can set it to fixed zero, reducing uninit'd FFs
|
||||
auto sampled_d = sample_data(module, ff.sig_d, RTLIL::Const(State::S0, ff.width), ff.is_fine);
|
||||
auto clk_edge = sample_control_edge(module, ff.sig_clk, ff.pol_clk, ff.is_fine);
|
||||
next_q = mux(module, next_q, sampled_d.sampled, clk_edge, ff.is_fine);
|
||||
}
|
||||
|
||||
SampledSig sampled_aload, sampled_ad, sampled_set, sampled_clr, sampled_arst;
|
||||
// The check for a constant sig_aload is also done by opt_dff, but when using verific and running
|
||||
// clk2fflogic before opt_dff (which does more and possibly unwanted optimizations) this check avoids
|
||||
// generating a lot of extra logic.
|
||||
bool has_nonconst_aload = ff.has_aload && ff.sig_aload != (ff.pol_aload ? State::S0 : State::S1);
|
||||
if (has_nonconst_aload) {
|
||||
sampled_aload = sample_control(module, ff.sig_aload, ff.pol_aload, ff.is_fine);
|
||||
// The init value for the sampled ad is never used, so we can set it to fixed zero, reducing uninit'd FFs
|
||||
sampled_ad = sample_data(module, ff.sig_ad, RTLIL::Const(State::S0, ff.width), ff.is_fine);
|
||||
}
|
||||
if (ff.has_sr) {
|
||||
sampled_set = sample_control(module, ff.sig_set, ff.pol_set, ff.is_fine);
|
||||
sampled_clr = sample_control(module, ff.sig_clr, ff.pol_clr, ff.is_fine);
|
||||
}
|
||||
if (ff.has_arst)
|
||||
sampled_arst = sample_control(module, ff.sig_arst, ff.pol_arst, ff.is_fine);
|
||||
|
||||
// First perform updates using _only_ sampled values, then again using _only_ current values. Unlike the previous
|
||||
// implementation, this approach correctly handles all the cases of multiple signals changing simultaneously.
|
||||
for (int current = 0; current < 2; current++) {
|
||||
if (has_nonconst_aload)
|
||||
next_q = mux(module, next_q, sampled_ad[current], sampled_aload[current], ff.is_fine);
|
||||
if (ff.has_sr)
|
||||
next_q = bitwise_sr(module, next_q, sampled_set[current], sampled_clr[current], ff.is_fine);
|
||||
if (ff.has_arst)
|
||||
next_q = mux(module, next_q, ff.val_arst, sampled_arst[current], ff.is_fine);
|
||||
}
|
||||
|
||||
module->connect(ff.sig_q, next_q);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!RTLIL::builtin_ff_cell_types().count(cell->type))
|
||||
continue;
|
||||
|
||||
FfData ff(&initvals, cell);
|
||||
|
||||
if (ff.has_gclk) {
|
||||
// Already a $ff or $_FF_ cell.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ff.has_clk) {
|
||||
log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
|
||||
log_id(module), log_id(cell), log_id(cell->type),
|
||||
log_signal(ff.sig_clk), log_signal(ff.sig_d), log_signal(ff.sig_q));
|
||||
} else if (ff.has_aload) {
|
||||
log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
|
||||
log_id(module), log_id(cell), log_id(cell->type),
|
||||
log_signal(ff.sig_aload), log_signal(ff.sig_ad), log_signal(ff.sig_q));
|
||||
} else {
|
||||
// $sr.
|
||||
log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n",
|
||||
log_id(module), log_id(cell), log_id(cell->type),
|
||||
log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_q));
|
||||
}
|
||||
|
||||
ff.remove();
|
||||
|
||||
if (ff.has_clk)
|
||||
ff.unmap_ce_srst();
|
||||
|
||||
auto next_q = sample_data(module, ff.sig_q, ff.val_init, ff.is_fine, true).sampled;
|
||||
|
||||
if (ff.has_clk) {
|
||||
// The init value for the sampled d is never used, so we can set it to fixed zero, reducing uninit'd FFs
|
||||
auto sampled_d = sample_data(module, ff.sig_d, RTLIL::Const(State::S0, ff.width), ff.is_fine);
|
||||
auto clk_edge = sample_control_edge(module, ff.sig_clk, ff.pol_clk, ff.is_fine);
|
||||
next_q = mux(module, next_q, sampled_d.sampled, clk_edge, ff.is_fine);
|
||||
}
|
||||
|
||||
SampledSig sampled_aload, sampled_ad, sampled_set, sampled_clr, sampled_arst;
|
||||
// The check for a constant sig_aload is also done by opt_dff, but when using verific and running
|
||||
// clk2fflogic before opt_dff (which does more and possibly unwanted optimizations) this check avoids
|
||||
// generating a lot of extra logic.
|
||||
bool has_nonconst_aload = ff.has_aload && ff.sig_aload != (ff.pol_aload ? State::S0 : State::S1);
|
||||
if (has_nonconst_aload) {
|
||||
sampled_aload = sample_control(module, ff.sig_aload, ff.pol_aload, ff.is_fine);
|
||||
// The init value for the sampled ad is never used, so we can set it to fixed zero, reducing uninit'd FFs
|
||||
sampled_ad = sample_data(module, ff.sig_ad, RTLIL::Const(State::S0, ff.width), ff.is_fine);
|
||||
}
|
||||
if (ff.has_sr) {
|
||||
sampled_set = sample_control(module, ff.sig_set, ff.pol_set, ff.is_fine);
|
||||
sampled_clr = sample_control(module, ff.sig_clr, ff.pol_clr, ff.is_fine);
|
||||
}
|
||||
if (ff.has_arst)
|
||||
sampled_arst = sample_control(module, ff.sig_arst, ff.pol_arst, ff.is_fine);
|
||||
|
||||
// First perform updates using _only_ sampled values, then again using _only_ current values. Unlike the previous
|
||||
// implementation, this approach correctly handles all the cases of multiple signals changing simultaneously.
|
||||
for (int current = 0; current < 2; current++) {
|
||||
if (has_nonconst_aload)
|
||||
next_q = mux(module, next_q, sampled_ad[current], sampled_aload[current], ff.is_fine);
|
||||
if (ff.has_sr)
|
||||
next_q = bitwise_sr(module, next_q, sampled_set[current], sampled_clr[current], ff.is_fine);
|
||||
if (ff.has_arst)
|
||||
next_q = mux(module, next_q, ff.val_arst, sampled_arst[current], ff.is_fine);
|
||||
}
|
||||
|
||||
module->connect(ff.sig_q, next_q);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (have_check_cells && !flag_nolower) {
|
||||
log_push();
|
||||
Pass::call(design, "chformal -lower");
|
||||
log_pop();
|
||||
}
|
||||
}
|
||||
} Clk2fflogicPass;
|
||||
|
||||
|
|
|
@ -1803,11 +1803,12 @@ endmodule
|
|||
|
||||
module \$print (EN, TRG, ARGS);
|
||||
|
||||
parameter PRIORITY = 0;
|
||||
|
||||
parameter FORMAT = "";
|
||||
parameter ARGS_WIDTH = 0;
|
||||
parameter PRIORITY = 0;
|
||||
parameter TRG_ENABLE = 1;
|
||||
|
||||
parameter TRG_ENABLE = 1;
|
||||
parameter TRG_WIDTH = 0;
|
||||
parameter TRG_POLARITY = 0;
|
||||
|
||||
|
@ -1817,6 +1818,27 @@ input [ARGS_WIDTH-1:0] ARGS;
|
|||
|
||||
endmodule
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
module \$check (A, EN, TRG, ARGS);
|
||||
|
||||
parameter FLAVOR = "";
|
||||
parameter PRIORITY = 0;
|
||||
|
||||
parameter FORMAT = "";
|
||||
parameter ARGS_WIDTH = 0;
|
||||
|
||||
parameter TRG_ENABLE = 1;
|
||||
parameter TRG_WIDTH = 0;
|
||||
parameter TRG_POLARITY = 0;
|
||||
|
||||
input A;
|
||||
input EN;
|
||||
input [TRG_WIDTH-1:0] TRG;
|
||||
input [ARGS_WIDTH-1:0] ARGS;
|
||||
|
||||
endmodule
|
||||
|
||||
// --------------------------------------------------------
|
||||
`ifndef SIMLIB_NOSR
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ reg [7:0] i = 0;
|
|||
always @(posedge clk) begin
|
||||
if (i < VECTORLEN) begin
|
||||
// FIXME: for some reason the first assert fails (despite comparing zero to zero)
|
||||
if (i > 0)
|
||||
if (i > 0)
|
||||
assert(y == y_expected);
|
||||
i <= i + 1;
|
||||
end
|
||||
|
@ -117,4 +117,5 @@ EOF
|
|||
read_verilog +/quicklogic/qlf_k6n10f/dsp_sim.v
|
||||
hierarchy -top testbench
|
||||
proc
|
||||
async2sync
|
||||
sim -assert -q -clock clk -n 20
|
||||
|
|
|
@ -36,7 +36,7 @@ blockram_tests: "list[tuple[list[tuple[str, int]], str, list[str]]]" = [
|
|||
([("ADDRESS_WIDTH", 14), ("DATA_WIDTH", 2)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=2 %i"]),
|
||||
([("ADDRESS_WIDTH", 15), ("DATA_WIDTH", 1)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=1 %i"]),
|
||||
|
||||
# 2x asymmetric (1024x36bit write / 2048x18bit read or vice versa = 1TDP36K)
|
||||
# 2x asymmetric (1024x36bit write / 2048x18bit read or vice versa = 1TDP36K)
|
||||
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 18), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
|
||||
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 16), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
|
||||
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 9), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
|
||||
|
@ -131,6 +131,7 @@ read_verilog -defer -formal mem_tb.v
|
|||
chparam{param_str} -set VECTORLEN {vectorlen} TB
|
||||
hierarchy -top TB -check
|
||||
prep
|
||||
async2sync
|
||||
log ** CHECKING SIMULATION FOR TEST {top} WITH PARAMS{param_str}
|
||||
sim -clock clk -n {vectorlen} -assert
|
||||
"""
|
||||
|
@ -254,16 +255,16 @@ sim_tests: list[TestClass] = [
|
|||
{"rq_a": 0x5678},
|
||||
]
|
||||
),
|
||||
TestClass( # basic TDP test
|
||||
TestClass( # basic TDP test
|
||||
# note that the testbench uses ra and wa, while the common TDP model
|
||||
# uses a shared address
|
||||
params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 36},
|
||||
top="sync_ram_tdp",
|
||||
assertions=[],
|
||||
test_steps=[
|
||||
{"wce_a": 1, "ra_a": 0x0A, "wce_b": 1, "ra_b": 0xBA,
|
||||
{"wce_a": 1, "ra_a": 0x0A, "wce_b": 1, "ra_b": 0xBA,
|
||||
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
|
||||
{"wce_a": 1, "ra_a": 0xFF,
|
||||
{"wce_a": 1, "ra_a": 0xFF,
|
||||
"wd_a": 0},
|
||||
{"rce_a": 1, "ra_a": 0x0A, "rce_b": 1, "ra_b": 0x0A},
|
||||
{"rq_a": 0xdeadbeef, "rq_b": 0xdeadbeef},
|
||||
|
@ -276,9 +277,9 @@ sim_tests: list[TestClass] = [
|
|||
top="sync_ram_tdp",
|
||||
assertions=[],
|
||||
test_steps=[
|
||||
{"wce_a": 1, "ra_a": 0x0F, "wce_b": 1, "ra_b": 0xBA,
|
||||
{"wce_a": 1, "ra_a": 0x0F, "wce_b": 1, "ra_b": 0xBA,
|
||||
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
|
||||
{"wce_a": 1, "ra_a": 0xFF,
|
||||
{"wce_a": 1, "ra_a": 0xFF,
|
||||
"wd_a": 0},
|
||||
{"rce_a": 1, "ra_a": 0x0F, "rce_b": 1, "ra_b": 0x0A},
|
||||
{"rq_a": 0, "rq_b": 0x00005a5a},
|
||||
|
@ -291,7 +292,7 @@ sim_tests: list[TestClass] = [
|
|||
top="sync_ram_tdp",
|
||||
assertions=[],
|
||||
test_steps=[
|
||||
{"wce_a": 1, "ra_a": 0x0A, "wce_b": 1, "ra_b": 0xBA,
|
||||
{"wce_a": 1, "ra_a": 0x0A, "wce_b": 1, "ra_b": 0xBA,
|
||||
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
|
||||
{"wce_a": 1, "ra_a": 0xBA, "rce_b": 1, "ra_b": 0xBA,
|
||||
"wd_a": 0xa5a5a5a5},
|
||||
|
@ -409,7 +410,7 @@ for sim_test in sim_tests:
|
|||
fn = f"t_mem{i}.ys"
|
||||
f = open(fn, mode="w")
|
||||
j = 0
|
||||
|
||||
|
||||
# output yosys script test file
|
||||
print(
|
||||
blockram_template.format(param_str=param_str, top=top),
|
||||
|
|
|
@ -10,5 +10,6 @@ select -assert-count 1 t:TDP36K a:is_split=0 %i
|
|||
select -assert-count 1 t:TDP36K a:was_split_candidate=0 %i
|
||||
read_verilog +/quicklogic/common/cells_sim.v +/quicklogic/qlf_k6n10f/cells_sim.v +/quicklogic/qlf_k6n10f/brams_sim.v +/quicklogic/qlf_k6n10f/sram1024x18_mem.v +/quicklogic/qlf_k6n10f/ufifo_ctl.v +/quicklogic/qlf_k6n10f/TDP18K_FIFO.v
|
||||
prep
|
||||
async2sync
|
||||
hierarchy -top top
|
||||
sim -assert -q -n 12 -clock clk
|
||||
|
|
|
@ -30,6 +30,7 @@ module top(output [42:0] P);
|
|||
assert property (P == 42*42);
|
||||
endmodule
|
||||
EOT
|
||||
async2sync
|
||||
techmap -map +/xilinx/xc7_dsp_map.v
|
||||
verilog_defaults -add -D ALLOW_WHITEBOX_DSP48E1
|
||||
synth_xilinx -abc9
|
||||
|
|
|
@ -75,7 +75,7 @@ generate_tests() {
|
|||
if [[ $do_sv = true ]]; then
|
||||
for x in *.sv; do
|
||||
if [ ! -f "${x%.sv}.ys" ]; then
|
||||
generate_ys_test "$x" "-p \"prep -top top; sat -enable_undef -verify -prove-asserts\" $yosys_args"
|
||||
generate_ys_test "$x" "-p \"prep -top top; async2sync; sat -enable_undef -verify -prove-asserts\" $yosys_args"
|
||||
fi;
|
||||
done
|
||||
fi;
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
read_verilog -sv asserts.v
|
||||
hierarchy; proc; opt
|
||||
hierarchy; proc; opt; async2sync
|
||||
sat -verify -seq 1 -set-at 1 rst 1 -tempinduct -prove-asserts
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
read_verilog -sv asserts_seq.v
|
||||
hierarchy; proc; opt
|
||||
hierarchy; proc; opt; async2sync
|
||||
|
||||
sat -verify -prove-asserts -tempinduct -seq 1 test_001
|
||||
sat -falsify -prove-asserts -tempinduct -seq 1 test_002
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
read_verilog -sv initval.v
|
||||
proc;;
|
||||
proc; async2sync;;
|
||||
|
||||
sat -seq 10 -prove-asserts
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
read_verilog -sv sizebits.sv
|
||||
prep; sat -verify -prove-asserts
|
||||
prep; async2sync; sat -verify -prove-asserts
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
read_verilog -sv enum_simple.sv
|
||||
hierarchy; proc; opt
|
||||
hierarchy; proc; opt; async2sync
|
||||
sat -verify -seq 1 -set-at 1 rst 1 -tempinduct -prove-asserts -show-all
|
||||
|
||||
|
|
|
@ -4,4 +4,5 @@ select -assert-count 2 t:$shift
|
|||
select -assert-count 2 t:$shiftx
|
||||
prep -top top
|
||||
flatten
|
||||
async2sync
|
||||
sat -enable_undef -verify -prove-asserts
|
||||
|
|
|
@ -9,6 +9,6 @@ logger -expect warning "reg '\\var_18' is assigned in a continuous assignment" 1
|
|||
logger -expect warning "reg '\\var_19' is assigned in a continuous assignment" 1
|
||||
|
||||
read_verilog -sv typedef_initial_and_assign.sv
|
||||
hierarchy; proc; opt
|
||||
hierarchy; proc; opt; async2sync
|
||||
select -module top
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
read_verilog -sv typedef_struct_port.sv
|
||||
hierarchy; proc; opt
|
||||
hierarchy; proc; opt; async2sync
|
||||
select -module top
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
select -module test_parser
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
read_verilog -formal <<EOT
|
||||
|
||||
module top(input clk, a, en);
|
||||
wire a_q = '0;
|
||||
wire en_q = '0;
|
||||
|
||||
always @(posedge clk) begin
|
||||
a_q <= a;
|
||||
en_q <= en;
|
||||
end
|
||||
|
||||
always @(posedge clk)
|
||||
if (en_q)
|
||||
assert(a_q);
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
prep
|
||||
|
||||
design -save prep
|
||||
|
||||
select -assert-count 1 t:$check r:FLAVOR=assert %i
|
||||
|
||||
chformal -assert2assume
|
||||
|
||||
select -assert-count 1 t:$check r:FLAVOR=assume %i
|
||||
|
||||
chformal -assume2assert
|
||||
|
||||
select -assert-count 1 t:$check r:FLAVOR=assert %i
|
||||
|
||||
async2sync
|
||||
|
||||
chformal -lower
|
||||
select -assert-count 1 t:$assert
|
||||
|
||||
design -load prep
|
||||
|
||||
chformal -assert2assume
|
||||
async2sync
|
||||
chformal -lower
|
||||
chformal -assume -early
|
||||
|
||||
rename -enumerate -pattern assume_% t:$assume
|
||||
expose -evert t:$assume
|
||||
|
||||
design -save gold
|
||||
|
||||
design -load prep
|
||||
|
||||
chformal -assert2assume
|
||||
chformal -assume -early
|
||||
async2sync
|
||||
chformal -lower
|
||||
|
||||
rename -enumerate -pattern assume_% t:$assume
|
||||
expose -evert t:$assume
|
||||
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
|
||||
design -copy-from gold -as gold top
|
||||
design -copy-from gate -as gate top
|
||||
|
||||
miter -equiv -flatten -make_assert gold gate miter
|
||||
|
||||
sat -verify -prove-asserts -tempinduct miter
|
|
@ -2,10 +2,10 @@ read_verilog -formal <<EOT
|
|||
module top(input a, b, c, d);
|
||||
|
||||
always @* begin
|
||||
if (a) assert (b == c);
|
||||
if (!a) assert (b != c);
|
||||
if (b) assume (c);
|
||||
if (c) cover (d);
|
||||
if (a) c0: assert (b == c);
|
||||
if (!a) c1: assert (b != c);
|
||||
if (b) c2: assume (c);
|
||||
if (c) c3: cover (d);
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
@ -13,6 +13,10 @@ EOT
|
|||
|
||||
prep -top top
|
||||
|
||||
design -save prep
|
||||
|
||||
async2sync
|
||||
|
||||
select -assert-count 1 t:$cover
|
||||
|
||||
chformal -cover -coverenable
|
||||
|
@ -23,3 +27,36 @@ select -assert-count 4 t:$cover
|
|||
|
||||
chformal -assume -coverenable
|
||||
select -assert-count 5 t:$cover
|
||||
|
||||
autoname */t:$cover
|
||||
expose -evert */c? */c?_EN_$cover_*
|
||||
|
||||
design -save a2s_first
|
||||
|
||||
design -load prep
|
||||
select -assert-count 1 r:FLAVOR=cover
|
||||
|
||||
chformal -cover -coverenable
|
||||
select -assert-count 2 r:FLAVOR=cover
|
||||
|
||||
chformal -assert -coverenable
|
||||
select -assert-count 4 r:FLAVOR=cover
|
||||
|
||||
chformal -assume -coverenable
|
||||
select -assert-count 5 r:FLAVOR=cover
|
||||
|
||||
async2sync
|
||||
|
||||
autoname */t:$cover
|
||||
expose -evert */c? */c?_EN_$cover_*
|
||||
|
||||
design -save a2s_last
|
||||
|
||||
design -reset
|
||||
|
||||
design -copy-from a2s_first -as gold top
|
||||
design -copy-from a2s_last -as gate top
|
||||
|
||||
miter -equiv -flatten -make_assert gold gate miter
|
||||
|
||||
sat -verify -prove-asserts -tempinduct miter
|
||||
|
|
|
@ -37,14 +37,17 @@ EOT
|
|||
|
||||
if ../../yosys -q -p 'verific -sv chparam1.sv'; then
|
||||
../../yosys -q -p 'verific -sv chparam1.sv; hierarchy -chparam X 123123123 -top top; prep -flatten' \
|
||||
-p 'async2sync' \
|
||||
-p 'sat -verify -prove-asserts -show-ports -set din[0] 1' \
|
||||
-p 'sat -falsify -prove-asserts -show-ports -set din[0] 0'
|
||||
|
||||
../../yosys -q -p 'verific -sv chparam2.sv; hierarchy -chparam X 123123123 -top top; prep -flatten' \
|
||||
-p 'async2sync' \
|
||||
-p 'sat -verify -prove-asserts -show-ports -set din[0] 1' \
|
||||
-p 'sat -falsify -prove-asserts -show-ports -set din[0] 0'
|
||||
fi
|
||||
../../yosys -q -p 'read_verilog -sv chparam2.sv; hierarchy -chparam X 123123123 -top top; prep -flatten' \
|
||||
-p 'async2sync' \
|
||||
-p 'sat -verify -prove-asserts -show-ports -set din[0] 1' \
|
||||
-p 'sat -falsify -prove-asserts -show-ports -set din[0] 0'
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
|
||||
../../yosys -p "
|
||||
read_verilog -formal -DFAST clk2fflogic_effects.sv
|
||||
hierarchy -top top; proc;;
|
||||
tee -o clk2fflogic_effects.sim.log sim -fst /tmp/sim.fst -q -n 16
|
||||
"
|
||||
|
||||
../../yosys -p "
|
||||
read_verilog -formal -DFAST clk2fflogic_effects.sv
|
||||
hierarchy -top top; proc;;
|
||||
clk2fflogic;;
|
||||
|
||||
tee -o clk2fflogic_effects.clk2fflogic.log sim -fst /tmp/sim.fst -q -n 16
|
||||
"
|
||||
|
||||
iverilog -g2012 -o clk2fflogic_effects.iv.out clk2fflogic_effects.sv
|
||||
|
||||
./clk2fflogic_effects.iv.out > clk2fflogic_effects.iv.log
|
||||
|
||||
sort clk2fflogic_effects.iv.log > clk2fflogic_effects.iv.sorted.log
|
||||
tail +3 clk2fflogic_effects.sim.log | sort > clk2fflogic_effects.sim.sorted.log
|
||||
tail +3 clk2fflogic_effects.clk2fflogic.log | sort > clk2fflogic_effects.clk2fflogic.sorted.log
|
||||
|
||||
cmp clk2fflogic_effects.iv.sorted.log clk2fflogic_effects.sim.sorted.log
|
||||
cmp clk2fflogic_effects.iv.sorted.log clk2fflogic_effects.clk2fflogic.sorted.log
|
|
@ -0,0 +1,82 @@
|
|||
module top;
|
||||
|
||||
(* gclk *)
|
||||
reg gclk;
|
||||
|
||||
reg clk = 0;
|
||||
always @(posedge gclk)
|
||||
clk <= !clk;
|
||||
|
||||
reg [4:0] counter = 0;
|
||||
|
||||
reg eff_0_trg = '0;
|
||||
reg eff_0_en = '0;
|
||||
|
||||
reg eff_1_trgA = '0;
|
||||
reg eff_1_trgB = '0;
|
||||
reg eff_1_en = '0;
|
||||
|
||||
reg eff_2_trgA = '0;
|
||||
reg eff_2_trgB = '0;
|
||||
reg eff_2_en = '0;
|
||||
|
||||
`ifdef FAST
|
||||
always @(posedge gclk) begin
|
||||
`else
|
||||
always @(posedge clk) begin
|
||||
`endif
|
||||
counter <= counter + 1;
|
||||
|
||||
eff_0_trg = 32'b00000000000000110011001100101010 >> counter;
|
||||
eff_0_en <= 32'b00000000000001100000110110110110 >> counter;
|
||||
|
||||
eff_1_trgA = 32'b00000000000000000011110000011110 >> counter;
|
||||
eff_1_trgB = 32'b00000000000000001111000001111000 >> counter;
|
||||
eff_1_en <= 32'b00000000000000001010101010101010 >> counter;
|
||||
|
||||
eff_2_trgA = counter[0];
|
||||
eff_2_trgB = !counter[0];
|
||||
eff_2_en <= 32'b00000000000000000000001111111100 >> counter;
|
||||
end
|
||||
|
||||
always @(posedge eff_0_trg)
|
||||
if (eff_0_en)
|
||||
$display("%02d: eff0 +", counter);
|
||||
|
||||
always @(negedge eff_0_trg)
|
||||
if (eff_0_en)
|
||||
$display("%02d: eff0 -", counter);
|
||||
|
||||
always @(posedge eff_0_trg, negedge eff_0_trg)
|
||||
if (eff_0_en)
|
||||
$display("%02d: eff0 *", counter);
|
||||
|
||||
always @(posedge eff_1_trgA, posedge eff_1_trgB)
|
||||
if (eff_1_en)
|
||||
$display("%02d: eff1 ++", counter);
|
||||
|
||||
always @(posedge eff_1_trgA, negedge eff_1_trgB)
|
||||
if (eff_1_en)
|
||||
$display("%02d: eff1 +-", counter);
|
||||
|
||||
always @(negedge eff_1_trgA, posedge eff_1_trgB)
|
||||
if (eff_1_en)
|
||||
$display("%02d: eff1 -+", counter);
|
||||
|
||||
always @(negedge eff_1_trgA, negedge eff_1_trgB)
|
||||
if (eff_1_en)
|
||||
$display("%02d: eff1 --", counter);
|
||||
|
||||
always @(posedge eff_2_trgA, posedge eff_2_trgB)
|
||||
if (eff_2_en)
|
||||
$display("repeated");
|
||||
|
||||
`ifdef __ICARUS__
|
||||
initial gclk = 0;
|
||||
always @(gclk) gclk <= #5 !gclk;
|
||||
always @(posedge gclk)
|
||||
if (counter == 31)
|
||||
$finish(0);
|
||||
`endif
|
||||
|
||||
endmodule
|
|
@ -3,4 +3,5 @@ hierarchy
|
|||
proc
|
||||
opt -full
|
||||
select -module top
|
||||
async2sync
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
|
|
|
@ -4,4 +4,5 @@ proc
|
|||
flatten
|
||||
opt -full
|
||||
select -module top
|
||||
async2sync
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
|
|
|
@ -4,4 +4,5 @@ proc
|
|||
flatten
|
||||
opt -full
|
||||
select -module top
|
||||
async2sync
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
|
|
|
@ -47,4 +47,5 @@ end
|
|||
endmodule
|
||||
EOF
|
||||
hierarchy; proc; opt
|
||||
async2sync
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
; SMT-LIBv2 description generated by Yosys $VERSION
|
||||
; yosys-smt2-module smtlib2
|
||||
(declare-sort |smtlib2_s| 0)
|
||||
(declare-fun |smtlib2_is| (|smtlib2_s|) Bool)
|
||||
(declare-fun |smtlib2#0| (|smtlib2_s|) (_ BitVec 8)) ; \a
|
||||
; yosys-smt2-input a 8
|
||||
; yosys-smt2-witness {"offset": 0, "path": ["\\a"], "smtname": "a", "smtoffset": 0, "type": "input", "width": 8}
|
||||
(define-fun |smtlib2_n a| ((state |smtlib2_s|)) (_ BitVec 8) (|smtlib2#0| state))
|
||||
(declare-fun |smtlib2#1| (|smtlib2_s|) (_ BitVec 8)) ; \b
|
||||
; yosys-smt2-input b 8
|
||||
; yosys-smt2-witness {"offset": 0, "path": ["\\b"], "smtname": "b", "smtoffset": 0, "type": "input", "width": 8}
|
||||
(define-fun |smtlib2_n b| ((state |smtlib2_s|)) (_ BitVec 8) (|smtlib2#1| state))
|
||||
; yosys-smt2-output add 8
|
||||
; yosys-smt2-witness {"offset": 0, "path": ["\\add"], "smtname": "add", "smtoffset": 0, "type": "blackbox", "width": 8}
|
||||
(define-fun |smtlib2_n add| ((state |smtlib2_s|)) (_ BitVec 8) (let (
|
||||
(|a| (|smtlib2_n a| state))
|
||||
(|b| (|smtlib2_n b| state))
|
||||
)
|
||||
(bvadd a b)
|
||||
))
|
||||
; yosys-smt2-output eq 1
|
||||
; yosys-smt2-witness {"offset": 0, "path": ["\\eq"], "smtname": "eq", "smtoffset": 0, "type": "blackbox", "width": 1}
|
||||
(define-fun |smtlib2_n eq| ((state |smtlib2_s|)) Bool (let (
|
||||
(|a| (|smtlib2_n a| state))
|
||||
(|b| (|smtlib2_n b| state))
|
||||
)
|
||||
(= a b)
|
||||
))
|
||||
; yosys-smt2-output sub 8
|
||||
; yosys-smt2-witness {"offset": 0, "path": ["\\sub"], "smtname": "sub", "smtoffset": 0, "type": "blackbox", "width": 8}
|
||||
(define-fun |smtlib2_n sub| ((state |smtlib2_s|)) (_ BitVec 8) (let (
|
||||
(|a| (|smtlib2_n a| state))
|
||||
(|b| (|smtlib2_n b| state))
|
||||
)
|
||||
(bvadd a (bvneg b))
|
||||
))
|
||||
(define-fun |smtlib2_a| ((state |smtlib2_s|)) Bool true)
|
||||
(define-fun |smtlib2_u| ((state |smtlib2_s|)) Bool true)
|
||||
(define-fun |smtlib2_i| ((state |smtlib2_s|)) Bool true)
|
||||
(define-fun |smtlib2_h| ((state |smtlib2_s|)) Bool true)
|
||||
(define-fun |smtlib2_t| ((state |smtlib2_s|) (next_state |smtlib2_s|)) Bool true) ; end of module smtlib2
|
||||
; yosys-smt2-module uut
|
||||
(declare-sort |uut_s| 0)
|
||||
(declare-fun |uut_is| (|uut_s|) Bool)
|
||||
; yosys-smt2-cell smtlib2 s
|
||||
; yosys-smt2-witness {"path": ["\\s"], "smtname": "s", "type": "cell"}
|
||||
(declare-fun |uut#0| (|uut_s|) (_ BitVec 8)) ; \add
|
||||
(declare-fun |uut#1| (|uut_s|) Bool) ; \eq
|
||||
(declare-fun |uut#2| (|uut_s|) (_ BitVec 8)) ; \sub
|
||||
(declare-fun |uut_h s| (|uut_s|) |smtlib2_s|)
|
||||
; yosys-smt2-anyconst uut#3 8 smtlib2_module.v:14.17-14.26
|
||||
; yosys-smt2-witness {"offset": 0, "path": ["\\a"], "smtname": 3, "smtoffset": 0, "type": "init", "width": 8}
|
||||
(declare-fun |uut#3| (|uut_s|) (_ BitVec 8)) ; \a
|
||||
; yosys-smt2-anyconst uut#4 8 smtlib2_module.v:14.32-14.41
|
||||
; yosys-smt2-witness {"offset": 0, "path": ["\\b"], "smtname": 4, "smtoffset": 0, "type": "init", "width": 8}
|
||||
(declare-fun |uut#4| (|uut_s|) (_ BitVec 8)) ; \b
|
||||
(define-fun |uut#5| ((state |uut_s|)) (_ BitVec 8) (bvadd (|uut#3| state) (|uut#4| state))) ; \add2
|
||||
(define-fun |uut#6| ((state |uut_s|)) Bool (= (|uut#0| state) (|uut#5| state))) ; $0$formal$smtlib2_module.v:28$1_CHECK[0:0]$9
|
||||
; yosys-smt2-assert 0 $assert$smtlib2_module.v:28$19 smtlib2_module.v:28.17-29.22
|
||||
(define-fun |uut_a 0| ((state |uut_s|)) Bool (or (|uut#6| state) (not true))) ; $assert$smtlib2_module.v:28$19
|
||||
(define-fun |uut#7| ((state |uut_s|)) (_ BitVec 8) (bvsub (|uut#3| state) (|uut#4| state))) ; \sub2
|
||||
(define-fun |uut#8| ((state |uut_s|)) Bool (= (|uut#2| state) (|uut#7| state))) ; $0$formal$smtlib2_module.v:29$2_CHECK[0:0]$11
|
||||
; yosys-smt2-assert 1 $assert$smtlib2_module.v:29$20 smtlib2_module.v:29.23-30.22
|
||||
(define-fun |uut_a 1| ((state |uut_s|)) Bool (or (|uut#8| state) (not true))) ; $assert$smtlib2_module.v:29$20
|
||||
(define-fun |uut#9| ((state |uut_s|)) Bool (= (|uut#3| state) (|uut#4| state))) ; $eq$smtlib2_module.v:31$17_Y
|
||||
(define-fun |uut#10| ((state |uut_s|)) Bool (= (ite (|uut#1| state) #b1 #b0) (ite (|uut#9| state) #b1 #b0))) ; $0$formal$smtlib2_module.v:30$3_CHECK[0:0]$13
|
||||
; yosys-smt2-assert 2 $assert$smtlib2_module.v:30$21 smtlib2_module.v:30.23-31.25
|
||||
(define-fun |uut_a 2| ((state |uut_s|)) Bool (or (|uut#10| state) (not true))) ; $assert$smtlib2_module.v:30$21
|
||||
(define-fun |uut_a| ((state |uut_s|)) Bool (and
|
||||
(|uut_a 0| state)
|
||||
(|uut_a 1| state)
|
||||
(|uut_a 2| state)
|
||||
(|smtlib2_a| (|uut_h s| state))
|
||||
))
|
||||
(define-fun |uut_u| ((state |uut_s|)) Bool
|
||||
(|smtlib2_u| (|uut_h s| state))
|
||||
)
|
||||
(define-fun |uut_i| ((state |uut_s|)) Bool
|
||||
(|smtlib2_i| (|uut_h s| state))
|
||||
)
|
||||
(define-fun |uut_h| ((state |uut_s|)) Bool (and
|
||||
(= (|uut_is| state) (|smtlib2_is| (|uut_h s| state)))
|
||||
(= (|uut#3| state) (|smtlib2_n a| (|uut_h s| state))) ; smtlib2.a
|
||||
(= (|uut#0| state) (|smtlib2_n add| (|uut_h s| state))) ; smtlib2.add
|
||||
(= (|uut#4| state) (|smtlib2_n b| (|uut_h s| state))) ; smtlib2.b
|
||||
(= (|uut#1| state) (|smtlib2_n eq| (|uut_h s| state))) ; smtlib2.eq
|
||||
(= (|uut#2| state) (|smtlib2_n sub| (|uut_h s| state))) ; smtlib2.sub
|
||||
(|smtlib2_h| (|uut_h s| state))
|
||||
))
|
||||
(define-fun |uut_t| ((state |uut_s|) (next_state |uut_s|)) Bool (and
|
||||
(= (|uut#4| state) (|uut#4| next_state)) ; $anyconst$5 \b
|
||||
(= (|uut#3| state) (|uut#3| next_state)) ; $anyconst$4 \a
|
||||
(|smtlib2_t| (|uut_h s| state) (|uut_h s| next_state))
|
||||
)) ; end of module uut
|
||||
; yosys-smt2-topmod uut
|
||||
; end of yosys output
|
|
@ -1,5 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
../../yosys -q -p 'read_verilog -formal smtlib2_module.v; prep; write_smt2 smtlib2_module.smt2'
|
||||
sed 's/; SMT-LIBv2 description generated by Yosys .*/; SMT-LIBv2 description generated by Yosys $VERSION/;s/ *$//' smtlib2_module.smt2 > smtlib2_module-filtered.smt2
|
||||
diff -au smtlib2_module-expected.smt2 smtlib2_module-filtered.smt2
|
|
@ -1,33 +0,0 @@
|
|||
(* smtlib2_module *)
|
||||
module smtlib2(a, b, add, sub, eq);
|
||||
input [7:0] a, b;
|
||||
(* smtlib2_comb_expr = "(bvadd a b)" *)
|
||||
output [7:0] add;
|
||||
(* smtlib2_comb_expr = "(bvadd a (bvneg b))" *)
|
||||
output [7:0] sub;
|
||||
(* smtlib2_comb_expr = "(= a b)" *)
|
||||
output eq;
|
||||
endmodule
|
||||
|
||||
(* top *)
|
||||
module uut;
|
||||
wire [7:0] a = $anyconst, b = $anyconst, add, sub, add2, sub2;
|
||||
wire eq;
|
||||
|
||||
assign add2 = a + b;
|
||||
assign sub2 = a - b;
|
||||
|
||||
smtlib2 s (
|
||||
.a(a),
|
||||
.b(b),
|
||||
.add(add),
|
||||
.sub(sub),
|
||||
.eq(eq)
|
||||
);
|
||||
|
||||
always @* begin
|
||||
assert(add == add2);
|
||||
assert(sub == sub2);
|
||||
assert(eq == (a == b));
|
||||
end
|
||||
endmodule
|
|
@ -2,4 +2,5 @@ read_verilog -sv struct_access.sv
|
|||
hierarchy
|
||||
proc
|
||||
opt
|
||||
async2sync
|
||||
sat -verify -seq 1 -prove-asserts -show-all
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
read_verilog -sv asgn_expr.sv
|
||||
proc
|
||||
async2sync
|
||||
sat -verify -prove-asserts -show-all
|
||||
|
|
|
@ -14,6 +14,6 @@ always_comb begin
|
|||
end
|
||||
endmodule
|
||||
EOT
|
||||
hierarchy; proc; opt
|
||||
hierarchy; proc; opt; async2sync
|
||||
select -module dut
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
|
|
|
@ -17,6 +17,7 @@ output reg b
|
|||
endmodule
|
||||
EOT
|
||||
proc
|
||||
async2sync
|
||||
sat -verify -prove-asserts
|
||||
|
||||
|
||||
|
@ -42,6 +43,7 @@ output b, c
|
|||
endmodule
|
||||
EOT
|
||||
proc
|
||||
async2sync
|
||||
sat -verify -prove-asserts
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
read_verilog -sv func_tern_hint.sv
|
||||
proc
|
||||
opt
|
||||
async2sync
|
||||
sat -verify -seq 1 -prove-asserts -show-all
|
||||
|
|
|
@ -3,5 +3,6 @@ hierarchy -top top
|
|||
proc
|
||||
flatten
|
||||
opt -full
|
||||
async2sync
|
||||
select -module top
|
||||
sat -verify -seq 1 -prove-asserts -enable_undef
|
||||
|
|
|
@ -3,5 +3,6 @@ hierarchy
|
|||
proc
|
||||
flatten
|
||||
opt -full
|
||||
async2sync
|
||||
select -module top
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
|
|
|
@ -3,4 +3,5 @@ proc
|
|||
flatten
|
||||
opt -full
|
||||
select -module top
|
||||
async2sync
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all -enable_undef
|
||||
|
|
|
@ -2,4 +2,5 @@ read_verilog -sv net_types.sv
|
|||
hierarchy
|
||||
proc
|
||||
opt -full
|
||||
async2sync
|
||||
sat -verify -prove-asserts -show-all
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
read_verilog -sv package_task_func.sv
|
||||
proc
|
||||
opt -full
|
||||
async2sync
|
||||
sat -verify -seq 1 -prove-asserts -show-all
|
||||
|
|
|
@ -3,5 +3,6 @@ hierarchy
|
|||
proc
|
||||
flatten
|
||||
opt -full
|
||||
async2sync
|
||||
select -module top
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
|
|
|
@ -16,5 +16,6 @@ hierarchy
|
|||
proc
|
||||
flatten
|
||||
opt -full
|
||||
async2sync
|
||||
select -module top
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
|
|
|
@ -14,6 +14,7 @@ endmodule
|
|||
EOT
|
||||
|
||||
prep -top top
|
||||
async2sync
|
||||
sim -n 3 -clock clk
|
||||
|
||||
design -reset
|
||||
|
@ -32,4 +33,5 @@ endmodule
|
|||
EOT
|
||||
|
||||
prep -top top
|
||||
async2sync
|
||||
sim -n 3 -clock clk
|
||||
|
|
|
@ -2,4 +2,5 @@ read_verilog -sv prefix.sv
|
|||
hierarchy
|
||||
proc
|
||||
select -module top
|
||||
async2sync
|
||||
sat -verify -seq 1 -prove-asserts -show-all
|
||||
|
|
|
@ -14,6 +14,7 @@ endmodule
|
|||
EOT
|
||||
|
||||
prep -top top
|
||||
async2sync
|
||||
sim -n 3 -clock clk
|
||||
|
||||
design -reset
|
||||
|
@ -32,6 +33,7 @@ endmodule
|
|||
EOT
|
||||
|
||||
prep -top top
|
||||
async2sync
|
||||
sim -n 3 -clock clk
|
||||
|
||||
design -reset
|
||||
|
@ -49,4 +51,5 @@ endmodule
|
|||
EOT
|
||||
|
||||
prep -top top
|
||||
async2sync
|
||||
sim -n 3 -clock clk
|
||||
|
|
|
@ -2,4 +2,5 @@ read_verilog -sv size_cast.sv
|
|||
proc
|
||||
opt -full
|
||||
select -module top
|
||||
async2sync
|
||||
sat -verify -prove-asserts -show-all
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
read_verilog -formal -sv struct_access.sv
|
||||
proc
|
||||
opt -full
|
||||
async2sync
|
||||
sat -verify -seq 1 -prove-asserts -show-all
|
||||
|
|
|
@ -19,5 +19,6 @@ EOF
|
|||
|
||||
proc
|
||||
opt -full
|
||||
async2sync
|
||||
select -module top
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
|
|
|
@ -33,5 +33,6 @@ hierarchy
|
|||
proc
|
||||
flatten
|
||||
opt -full
|
||||
async2sync
|
||||
select -module top
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
|
|
|
@ -3,5 +3,6 @@ hierarchy
|
|||
proc
|
||||
flatten
|
||||
opt -full
|
||||
async2sync
|
||||
select -module top
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
|
|
|
@ -3,5 +3,6 @@ hierarchy
|
|||
proc
|
||||
flatten
|
||||
opt -full
|
||||
async2sync
|
||||
select -module top
|
||||
sat -verify -seq 1 -tempinduct -prove-asserts -show-all
|
||||
|
|
|
@ -12,6 +12,7 @@ endmodule
|
|||
EOT
|
||||
|
||||
prep -top top
|
||||
async2sync
|
||||
sim -n 3 -clock clk
|
||||
|
||||
design -reset
|
||||
|
@ -29,5 +30,6 @@ endmodule
|
|||
EOT
|
||||
|
||||
prep -top top
|
||||
async2sync
|
||||
sim -n 3 -clock clk
|
||||
|
||||
|
|
Loading…
Reference in New Issue