From 6e5f40e36497ebcbf1769122e39278508fc13e84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 1 Feb 2024 10:37:30 +0100 Subject: [PATCH 01/14] utils: Save detected loops with their nodes in-order --- kernel/utils.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/utils.h b/kernel/utils.h index 8fa223824da..3216c5eb5f0 100644 --- a/kernel/utils.h +++ b/kernel/utils.h @@ -149,7 +149,7 @@ template , typename OPS = hash_ops> cla std::map node_to_index; std::vector> edges; std::vector sorted; - std::set> loops; + std::set> loops; TopoSort() : indirect_cmp(nodes) { @@ -220,10 +220,10 @@ template , typename OPS = hash_ops> cla if (active_cells[root_index]) { found_loops = true; if (analyze_loops) { - std::set loop; + std::vector loop; for (int i = GetSize(active_stack) - 1; i >= 0; i--) { const int index = active_stack[i]; - loop.insert(nodes[index]); + loop.push_back(nodes[index]); if (index == root_index) break; } From c5ae74af34e23ab126ca9a8781e1211fabd34c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 1 Feb 2024 10:40:45 +0100 Subject: [PATCH 02/14] check: Improve found loop logging Print the detected loop in-order, and include source location for each node, if available. --- passes/cmds/check.cc | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index d1c83f04d88..c3e775a387a 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -104,8 +104,7 @@ struct CheckPass : public Pass { dict> wire_drivers; dict wire_drivers_count; pool used_wires; - TopoSort topo; - + TopoSort> topo; for (auto &proc_it : module->processes) { std::vector all_cases = {&proc_it.second->root_case}; @@ -164,16 +163,16 @@ struct CheckPass : public Pass { if (cell->input(conn.first)) for (auto bit : sig) if (bit.wire) { - if (logic_cell) - topo.edge(stringf("wire %s", log_signal(bit)), - stringf("cell %s (%s)", log_id(cell), log_id(cell->type))); + if (logic_cell && bit.wire) + topo.edge(std::make_pair(bit.wire->name, bit.offset), + std::make_pair(cell->name, -1)); used_wires.insert(bit); } if (cell->output(conn.first)) for (int i = 0; i < GetSize(sig); i++) { - if (logic_cell) - topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)), - stringf("wire %s", log_signal(sig[i]))); + if (logic_cell && sig[i].wire) + topo.edge(std::make_pair(cell->name, -1), + std::make_pair(sig[i].wire->name, sig[i].offset)); if (sig[i].wire || !cell->input(conn.first)) wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)", @@ -239,8 +238,28 @@ struct CheckPass : public Pass { topo.sort(); for (auto &loop : topo.loops) { string message = stringf("found logic loop in module %s:\n", log_id(module)); - for (auto &str : loop) - message += stringf(" %s\n", str.c_str()); + for (auto &pair : loop) { + RTLIL::AttrObject *obj; + if (pair.second == -1) + obj = module->cell(pair.first); + else + obj = module->wire(pair.first); + log_assert(obj); + std::string src; + if (obj->has_attribute(ID::src)) { + std::string src_attr = obj->get_src_attribute(); + src = stringf(" source: %s", src_attr.c_str()); + } + if (pair.second == -1) { + Cell *cell = module->cell(pair.first); + log_assert(cell); + message += stringf(" cell %s (%s)%s\n", log_id(cell), log_id(cell->type), src.c_str()); + } else { + Wire *wire = module->wire(pair.first); + log_assert(wire); + message += stringf(" wire %s%s\n", log_signal(SigBit(wire, pair.second)), src.c_str()); + } + } log_warning("%s", message.c_str()); counter++; } From fa74d0bd1a070a007292cc260b49f183f00ebfd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 5 Feb 2024 14:44:55 +0100 Subject: [PATCH 03/14] check: Use cell edges data in detecting combinational loops --- passes/cmds/check.cc | 159 ++++++++++++++++++++++++++++++++----------- 1 file changed, 120 insertions(+), 39 deletions(-) diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index c3e775a387a..9f70dbdc7c8 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/celledges.h" #include "kernel/celltypes.h" #include "kernel/utils.h" @@ -102,6 +103,7 @@ struct CheckPass : public Pass { SigMap sigmap(module); dict> wire_drivers; + dict driver_cells; dict wire_drivers_count; pool used_wires; TopoSort> topo; @@ -149,6 +151,46 @@ struct CheckPass : public Pass { } } + struct CircuitEdgesDatabase : AbstractCellEdgesDatabase { + TopoSort> &topo; + SigMap sigmap; + + CircuitEdgesDatabase(TopoSort> &topo, SigMap &sigmap) + : topo(topo), sigmap(sigmap) {} + + void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, + RTLIL::IdString to_port, int to_bit, int) override { + SigBit from = sigmap(cell->getPort(from_port))[from_bit]; + SigBit to = sigmap(cell->getPort(to_port))[to_bit]; + + if (from.wire && to.wire) + topo.edge(std::make_pair(from.wire->name, from.offset), std::make_pair(to.wire->name, to.offset)); + } + + bool add_edges_from_cell(Cell *cell) { + if (AbstractCellEdgesDatabase::add_edges_from_cell(cell)) + return true; + + // We don't have accurate cell edges, do the fallback of all input-output pairs + for (auto &conn : cell->connections()) { + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + if (bit.wire) + topo.edge(std::make_pair(bit.wire->name, bit.offset), + std::make_pair(cell->name, -1)); + + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + if (bit.wire) + topo.edge(std::make_pair(cell->name, -1), + std::make_pair(bit.wire->name, bit.offset)); + } + return true; + } + }; + + CircuitEdgesDatabase edges_db(topo, sigmap); + for (auto cell : module->cells()) { if (mapped && cell->type.begins_with("$") && design->module(cell->type) == nullptr) { @@ -157,31 +199,29 @@ struct CheckPass : public Pass { counter++; cell_allowed:; } + for (auto &conn : cell->connections()) { + bool input = cell->input(conn.first); + bool output = cell->output(conn.first); + SigSpec sig = sigmap(conn.second); - bool logic_cell = yosys_celltypes.cell_evaluable(cell->type); - if (cell->input(conn.first)) - for (auto bit : sig) - if (bit.wire) { - if (logic_cell && bit.wire) - topo.edge(std::make_pair(bit.wire->name, bit.offset), - std::make_pair(cell->name, -1)); - used_wires.insert(bit); - } - if (cell->output(conn.first)) - for (int i = 0; i < GetSize(sig); i++) { - if (logic_cell && sig[i].wire) - topo.edge(std::make_pair(cell->name, -1), - std::make_pair(sig[i].wire->name, sig[i].offset)); - - if (sig[i].wire || !cell->input(conn.first)) - wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)", - log_id(conn.first), i, log_id(cell), log_id(cell->type))); - } - if (!cell->input(conn.first) && cell->output(conn.first)) - for (auto bit : sig) - if (bit.wire) wire_drivers_count[bit]++; + for (int i = 0; i < sig.size(); i++) { + SigBit bit = sig[i]; + + if (input && bit.wire) + used_wires.insert(bit); + if (output && !input && bit.wire) + wire_drivers_count[bit]++; + if (output && (bit.wire || !input)) + wire_drivers[bit].push_back(stringf("port %s[%d] of cell %s (%s)", log_id(conn.first), i, + log_id(cell), log_id(cell->type))); + if (output) + driver_cells[bit] = cell; + } } + + if (yosys_celltypes.cell_evaluable(cell->type)) + edges_db.add_edges_from_cell(cell); } pool init_bits; @@ -238,27 +278,68 @@ struct CheckPass : public Pass { topo.sort(); for (auto &loop : topo.loops) { string message = stringf("found logic loop in module %s:\n", log_id(module)); + + // `loop` only contains wire bits, or an occassional special helper node for cells for + // which we have done the edges fallback. The cell and its ports that led to an edge is + // an information we need to recover now. For that we need to have the previous wire bit + // of the loop at hand. + SigBit prev; + for (auto it = loop.rbegin(); it != loop.rend(); it++) + if (it->second != -1) { // skip the fallback helper nodes + prev = SigBit(module->wire(it->first), it->second); + break; + } + log_assert(prev != SigBit()); + for (auto &pair : loop) { - RTLIL::AttrObject *obj; if (pair.second == -1) - obj = module->cell(pair.first); - else - obj = module->wire(pair.first); - log_assert(obj); - std::string src; - if (obj->has_attribute(ID::src)) { - std::string src_attr = obj->get_src_attribute(); - src = stringf(" source: %s", src_attr.c_str()); + continue; // helper node for edges fallback, we can ignore it + + struct MatchingEdgePrinter : AbstractCellEdgesDatabase { + std::string &message; + SigMap &sigmap; + SigBit from, to; + int nhits; + const int HITS_LIMIT = 3; + + MatchingEdgePrinter(std::string &message, SigMap &sigmap, SigBit from, SigBit to) + : message(message), sigmap(sigmap), from(from), to(to), nhits(0) {} + + void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, + RTLIL::IdString to_port, int to_bit, int) override { + SigBit edge_from = sigmap(cell->getPort(from_port))[from_bit]; + SigBit edge_to = sigmap(cell->getPort(to_port))[to_bit]; + + if (edge_from == from && edge_to == to && nhits++ < HITS_LIMIT) + message += stringf(" %s[%d] --> %s[%d]\n", log_id(from_port), from_bit, + log_id(to_port), to_bit); + if (nhits == HITS_LIMIT) + message += " ...\n"; + } + }; + + Wire *wire = module->wire(pair.first); + log_assert(wire); + SigBit bit(module->wire(pair.first), pair.second); + log_assert(driver_cells.count(bit)); + Cell *driver = driver_cells.at(bit); + + std::string driver_src; + if (driver->has_attribute(ID::src)) { + std::string src_attr = driver->get_src_attribute(); + driver_src = stringf(" source: %s", src_attr.c_str()); } - if (pair.second == -1) { - Cell *cell = module->cell(pair.first); - log_assert(cell); - message += stringf(" cell %s (%s)%s\n", log_id(cell), log_id(cell->type), src.c_str()); - } else { - Wire *wire = module->wire(pair.first); - log_assert(wire); - message += stringf(" wire %s%s\n", log_signal(SigBit(wire, pair.second)), src.c_str()); + message += stringf(" cell %s (%s)%s\n", log_id(driver), log_id(driver->type), driver_src.c_str()); + MatchingEdgePrinter printer(message, sigmap, prev, bit); + printer.add_edges_from_cell(driver); + + std::string wire_src; + if (wire->has_attribute(ID::src)) { + std::string src_attr = wire->get_src_attribute(); + wire_src = stringf(" source: %s", src_attr.c_str()); } + message += stringf(" wire %s%s\n", log_signal(SigBit(wire, pair.second)), wire_src.c_str()); + prev = bit; } log_warning("%s", message.c_str()); counter++; From 3eef6450f10b672c95864b1e35409febfd501ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 5 Feb 2024 14:46:12 +0100 Subject: [PATCH 04/14] check: Add coarse-grain false positive test --- tests/various/check.ys | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/various/check.ys diff --git a/tests/various/check.ys b/tests/various/check.ys new file mode 100644 index 00000000000..cdc944ed371 --- /dev/null +++ b/tests/various/check.ys @@ -0,0 +1,12 @@ +design -reset +read_verilog < Date: Mon, 12 Feb 2024 11:09:24 +0100 Subject: [PATCH 05/14] celledges: Describe asynchronous read ports --- kernel/celledges.cc | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/kernel/celledges.cc b/kernel/celledges.cc index 2ed0d503605..598d7d8060e 100644 --- a/kernel/celledges.cc +++ b/kernel/celledges.cc @@ -307,6 +307,40 @@ void shift_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) } } +void packed_mem_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) +{ + log_assert(cell->type == ID($mem_v2)); + Const rd_clk_enable = cell->getParam(ID::RD_CLK_ENABLE); + int n_rd_ports = cell->getParam(ID::RD_PORTS).as_int(); + int abits = cell->getParam(ID::ABITS).as_int(); + int width = cell->getParam(ID::WIDTH).as_int(); + + for (int i = 0; i < n_rd_ports; i++) { + if (rd_clk_enable[i] != State::S0) + continue; + + for (int j = 0; j < abits; j++) + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::RD_ADDR, i * abits + j, + ID::RD_DATA, i * width + k, -1); + } +} + +void memrd_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) +{ + log_assert(cell->type.in(ID($memrd), ID($memrd_v2))); + + if (cell->getParam(ID::CLK_ENABLE).as_bool()) + return; + + int abits = cell->getParam(ID::ABITS).as_int(); + int width = cell->getParam(ID::WIDTH).as_int(); + + for (int j = 0; j < abits; j++) + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::ADDR, j, ID::DATA, k, -1); +} + PRIVATE_NAMESPACE_END bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell) @@ -361,6 +395,16 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL return true; } + if (cell->type == ID($mem_v2)) { + packed_mem_op(this, cell); + return true; + } + + if (cell->type.in(ID($memrd), ID($memrd_v2))) { + memrd_op(this, cell); + return true; + } + // FIXME: $mul $div $mod $divfloor $modfloor $pow $slice $concat $bweqx // FIXME: $lut $sop $alu $lcu $macc $fa $logic_and $logic_or $bwmux From b6112b3551d75f87da8a61e7ee482e6f4b840d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 12 Feb 2024 11:10:41 +0100 Subject: [PATCH 06/14] check: Consider read ports in loop detection --- passes/cmds/check.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index 9f70dbdc7c8..070a3fb9d73 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -220,7 +220,7 @@ struct CheckPass : public Pass { } } - if (yosys_celltypes.cell_evaluable(cell->type)) + if (yosys_celltypes.cell_evaluable(cell->type) || cell->type.in(ID($mem_v2), ID($memrd), ID($memrd_v2))) edges_db.add_edges_from_cell(cell); } From e1e77a7fa9cad776975b4f00e94823e078a83824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 12 Feb 2024 12:32:15 +0100 Subject: [PATCH 07/14] check: Extend testing --- tests/various/check.ys | 33 ++++++++++++++++++++++++++++++++- tests/various/check_2.ys | 17 +++++++++++++++++ tests/various/check_3.ys | 20 ++++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 tests/various/check_2.ys create mode 100644 tests/various/check_3.ys diff --git a/tests/various/check.ys b/tests/various/check.ys index cdc944ed371..41b9960911f 100644 --- a/tests/various/check.ys +++ b/tests/various/check.ys @@ -1,5 +1,5 @@ design -reset -read_verilog < Date: Mon, 12 Feb 2024 12:32:50 +0100 Subject: [PATCH 08/14] check: Assert edges data is not out-of-bounds --- passes/cmds/check.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index 070a3fb9d73..bb169b4f965 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -160,8 +160,12 @@ struct CheckPass : public Pass { void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, RTLIL::IdString to_port, int to_bit, int) override { - SigBit from = sigmap(cell->getPort(from_port))[from_bit]; - SigBit to = sigmap(cell->getPort(to_port))[to_bit]; + SigSpec from_portsig = cell->getPort(from_port); + SigSpec to_portsig = cell->getPort(to_port); + log_assert(from_bit >= 0 && from_bit < from_portsig.size()); + log_assert(to_bit >= 0 && to_bit < to_portsig.size()); + SigBit from = sigmap(from_portsig[from_bit]); + SigBit to = sigmap(to_portsig[to_bit]); if (from.wire && to.wire) topo.edge(std::make_pair(from.wire->name, from.offset), std::make_pair(to.wire->name, to.offset)); From e4296072c42f3812588f78f76aa9aa035ad08f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 12 Feb 2024 15:09:52 +0100 Subject: [PATCH 09/14] check: Rephrase regex for portability --- tests/various/check_3.ys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/various/check_3.ys b/tests/various/check_3.ys index 53c6fbab2ba..14915ab8d4d 100644 --- a/tests/various/check_3.ys +++ b/tests/various/check_3.ys @@ -16,5 +16,5 @@ EOF hierarchy -top pingpong prep logger -nowarn "found logic loop in module pingpong:" -logger -expect error "Found \d+ problems in 'check -assert'" 1 +logger -expect error "Found [0-9]+ problems in 'check -assert'" 1 check -assert From 4a10e78777a65c3fc18ffcba6e01bf5a62df81c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 23 Feb 2024 10:44:41 +0100 Subject: [PATCH 10/14] celledges: Emit empty edges for write/init ports --- kernel/celledges.cc | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/kernel/celledges.cc b/kernel/celledges.cc index 598d7d8060e..48475cfdd7a 100644 --- a/kernel/celledges.cc +++ b/kernel/celledges.cc @@ -341,6 +341,18 @@ void memrd_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) db->add_edge(cell, ID::ADDR, j, ID::DATA, k, -1); } +void mem_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) +{ + if (cell->type == ID($mem_v2)) + packed_mem_op(db, cell); + else if (cell->type.in(ID($memrd), ID($memrd_v2))) + memrd_op(db, cell); + else if (cell->type.in(ID($memwr), ID($memwr_v2), ID($meminit))) + return; /* no edges here */ + else + log_abort(); +} + PRIVATE_NAMESPACE_END bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell) @@ -395,13 +407,8 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL return true; } - if (cell->type == ID($mem_v2)) { - packed_mem_op(this, cell); - return true; - } - - if (cell->type.in(ID($memrd), ID($memrd_v2))) { - memrd_op(this, cell); + if (cell->type.in(ID($mem_v2), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2), ID($meminit))) { + mem_op(this, cell); return true; } From 87e72ef86fcd40865b944b5ea65575be1602faf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 23 Feb 2024 10:58:32 +0100 Subject: [PATCH 11/14] celledges: Add read ports arst paths --- kernel/celledges.cc | 17 ++++++++++++----- tests/various/check_4.ys | 29 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 tests/various/check_4.ys diff --git a/kernel/celledges.cc b/kernel/celledges.cc index 48475cfdd7a..1dbe3fd42bc 100644 --- a/kernel/celledges.cc +++ b/kernel/celledges.cc @@ -316,8 +316,11 @@ void packed_mem_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) int width = cell->getParam(ID::WIDTH).as_int(); for (int i = 0; i < n_rd_ports; i++) { - if (rd_clk_enable[i] != State::S0) + if (rd_clk_enable[i] != State::S0) { + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::RD_ARST, i, ID::RD_DATA, i * width + k, -1); continue; + } for (int j = 0; j < abits; j++) for (int k = 0; k < width; k++) @@ -329,13 +332,17 @@ void packed_mem_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) void memrd_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) { log_assert(cell->type.in(ID($memrd), ID($memrd_v2))); - - if (cell->getParam(ID::CLK_ENABLE).as_bool()) - return; - int abits = cell->getParam(ID::ABITS).as_int(); int width = cell->getParam(ID::WIDTH).as_int(); + if (cell->getParam(ID::CLK_ENABLE).as_bool()) { + if (cell->type == ID($memrd_v2)) { + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::ARST, 0, ID::DATA, k, -1); + } + return; + } + for (int j = 0; j < abits; j++) for (int k = 0; k < width; k++) db->add_edge(cell, ID::ADDR, j, ID::DATA, k, -1); diff --git a/tests/various/check_4.ys b/tests/various/check_4.ys new file mode 100644 index 00000000000..010cd01fd53 --- /dev/null +++ b/tests/various/check_4.ys @@ -0,0 +1,29 @@ +# loop involving the asynchronous reset on a memory port +design -reset +read -vlog2k < Date: Fri, 23 Feb 2024 11:41:03 +0100 Subject: [PATCH 12/14] celledges: Register async FF paths --- kernel/celledges.cc | 35 +++++++++++++++++++++++++++++++++++ passes/cmds/check.cc | 3 ++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/kernel/celledges.cc b/kernel/celledges.cc index 1dbe3fd42bc..09cdfd55b96 100644 --- a/kernel/celledges.cc +++ b/kernel/celledges.cc @@ -360,6 +360,34 @@ void mem_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) log_abort(); } +void ff_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) +{ + int width = cell->getPort(ID::Q).size(); + + if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) { + for (int k = 0; k < width; k++) { + db->add_edge(cell, ID::D, k, ID::Q, k, -1); + db->add_edge(cell, ID::EN, 0, ID::Q, k, -1); + } + } + + if (cell->hasPort(ID::CLR)) + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::CLR, 0, ID::Q, k, -1); + if (cell->hasPort(ID::SET)) + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::SET, 0, ID::Q, k, -1); + if (cell->hasPort(ID::ALOAD)) + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::ALOAD, 0, ID::Q, k, -1); + if (cell->hasPort(ID::AD)) + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::AD, k, ID::Q, k, -1); + if (cell->hasPort(ID::ARST)) + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::ARST, 0, ID::Q, k, -1); +} + PRIVATE_NAMESPACE_END bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell) @@ -419,6 +447,13 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL return true; } + if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + ff_op(this, cell); + return true; + } + + // FIXME: $mul $div $mod $divfloor $modfloor $slice $concat + // FIXME: $lut $sop $alu $lcu $macc $fa // FIXME: $mul $div $mod $divfloor $modfloor $pow $slice $concat $bweqx // FIXME: $lut $sop $alu $lcu $macc $fa $logic_and $logic_or $bwmux diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index bb169b4f965..23e4e7c927c 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -224,7 +224,8 @@ struct CheckPass : public Pass { } } - if (yosys_celltypes.cell_evaluable(cell->type) || cell->type.in(ID($mem_v2), ID($memrd), ID($memrd_v2))) + if (yosys_celltypes.cell_evaluable(cell->type) || cell->type.in(ID($mem_v2), ID($memrd), ID($memrd_v2)) \ + || RTLIL::builtin_ff_cell_types().count(cell->type)) edges_db.add_edges_from_cell(cell); } From 5924d97381c95b6ed94ab2767504edb32c870c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 27 Feb 2024 12:37:46 +0100 Subject: [PATCH 13/14] tests: Remove part of test involving combinational loops --- tests/opt/opt_dff_qd.ys | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/tests/opt/opt_dff_qd.ys b/tests/opt/opt_dff_qd.ys index 7b0b4c2242a..c6232643f1f 100644 --- a/tests/opt/opt_dff_qd.ys +++ b/tests/opt/opt_dff_qd.ys @@ -7,7 +7,7 @@ module top(...); input CLK; input EN; (* init = 24'h555555 *) -output [19:0] Q; +output [17:0] Q; input SRST; input ARST; input [1:0] CLR; @@ -23,26 +23,20 @@ $sdffce #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_V $dffsr #(.CLK_POLARITY(1'b1), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff7 (.CLK(CLK), .SET(SET), .CLR(CLR), .D(Q[15:14]), .Q(Q[15:14])); $dffsre #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff8 (.CLK(CLK), .EN(EN), .SET(SET), .CLR(CLR), .D(Q[17:16]), .Q(Q[17:16])); -$dlatch #(.EN_POLARITY(1'b1), .WIDTH(2)) ff9 (.EN(EN), .D(Q[19:18]), .Q(Q[19:18])); -$adlatch #(.EN_POLARITY(1'b0), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff10 (.EN(EN), .ARST(ARST), .D(Q[21:20]), .Q(Q[21:20])); -$dlatchsr #(.EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff11 (.EN(EN), .SET(SET), .CLR(CLR), .D(Q[23:22]), .Q(Q[23:22])); - endmodule EOT design -save orig -# Equivalence check will fail for unmapped adlatch and dlatchsr due to negative hold hack. -delete top/ff10 top/ff11 equiv_opt -undef -assert -multiclock opt_dff -keepdc design -load orig opt_dff -keepdc select -assert-count 1 t:$and select -assert-count 3 t:$dffe -select -assert-count 3 t:$dlatch -select -assert-count 3 t:$sr +select -assert-count 2 t:$dlatch +select -assert-count 2 t:$sr select -assert-none t:$and t:$dffe t:$dlatch t:$sr %% %n t:* %i design -load orig @@ -50,7 +44,7 @@ simplemap opt_dff -keepdc select -assert-count 2 t:$_AND_ select -assert-count 6 t:$_DFFE_??_ -select -assert-count 6 t:$_DLATCH_?_ -select -assert-count 6 t:$_SR_??_ +select -assert-count 4 t:$_DLATCH_?_ +select -assert-count 4 t:$_SR_??_ select -assert-none t:$_AND_ t:$_DFFE_??_ t:$_DLATCH_?_ t:$_SR_??_ %% %n t:* %i From 206d894c56620948670201ec6ff5848b73dfd927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 4 Mar 2024 11:47:01 +0100 Subject: [PATCH 14/14] check: Omit private wires in loop report --- passes/cmds/check.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index 23e4e7c927c..255c32fbad9 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -338,12 +338,15 @@ struct CheckPass : public Pass { MatchingEdgePrinter printer(message, sigmap, prev, bit); printer.add_edges_from_cell(driver); - std::string wire_src; - if (wire->has_attribute(ID::src)) { - std::string src_attr = wire->get_src_attribute(); - wire_src = stringf(" source: %s", src_attr.c_str()); + if (wire->name.isPublic()) { + std::string wire_src; + if (wire->has_attribute(ID::src)) { + std::string src_attr = wire->get_src_attribute(); + wire_src = stringf(" source: %s", src_attr.c_str()); + } + message += stringf(" wire %s%s\n", log_signal(SigBit(wire, pair.second)), wire_src.c_str()); } - message += stringf(" wire %s%s\n", log_signal(SigBit(wire, pair.second)), wire_src.c_str()); + prev = bit; } log_warning("%s", message.c_str());