Skip to content

Commit 05d1d56

Browse files
authored
Merge pull request YosysHQ#5704 from apullin/apullin/abc9-no-loops-fix
abc9: preserve topological-loop asserts with targeted SCC fallback
2 parents d3e297f + 5970be3 commit 05d1d56

3 files changed

Lines changed: 107 additions & 27 deletions

File tree

passes/techmap/abc9_ops.cc

Lines changed: 87 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "kernel/utils.h"
2424
#include "kernel/celltypes.h"
2525
#include "kernel/timinginfo.h"
26+
#include <optional>
2627

2728
USING_YOSYS_NAMESPACE
2829
PRIVATE_NAMESPACE_BEGIN
@@ -587,6 +588,7 @@ void break_scc(RTLIL::Module *module)
587588
auto id = it->second;
588589
auto r = ids_seen.insert(id);
589590
cell->attributes.erase(it);
591+
// Cut exactly one representative cell per SCC id.
590592
if (!r.second)
591593
continue;
592594
for (auto &c : cell->connections_) {
@@ -710,8 +712,6 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
710712

711713
SigMap sigmap(module);
712714

713-
dict<SigBit, pool<IdString>> bit_drivers, bit_users;
714-
TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
715715
dict<IdString, std::vector<IdString>> box_ports;
716716

717717
for (auto cell : module->cells()) {
@@ -750,39 +750,100 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
750750
}
751751
}
752752
}
753-
else if (!yosys_celltypes.cell_known(cell->type))
754-
continue;
755-
756-
// TODO: Speed up toposort -- we care about box ordering only
757-
for (auto conn : cell->connections()) {
758-
if (cell->input(conn.first))
759-
for (auto bit : sigmap(conn.second))
760-
bit_users[bit].insert(cell->name);
761-
762-
if (cell->output(conn.first) && !abc9_flop)
763-
for (auto bit : sigmap(conn.second))
764-
bit_drivers[bit].insert(cell->name);
765-
}
766-
toposort.node(cell->name);
767753
}
768754

769755
if (box_ports.empty())
770756
return;
771757

772-
for (auto &it : bit_users)
773-
if (bit_drivers.count(it.first))
774-
for (auto driver_cell : bit_drivers.at(it.first))
775-
for (auto user_cell : it.second)
776-
toposort.edge(driver_cell, user_cell);
758+
// Build the same topo graph for the initial pass and the optional retry.
759+
auto build_toposort = [&](TopoSort<IdString, RTLIL::sort_by_id_str> &toposort) {
760+
dict<SigBit, pool<IdString>> bit_drivers, bit_users;
777761

778-
if (ys_debug(1))
779-
toposort.analyze_loops = true;
762+
for (auto cell : module->cells()) {
763+
if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_)))
764+
continue;
765+
if (cell->has_keep_attr())
766+
continue;
780767

781-
bool no_loops = toposort.sort();
768+
auto inst_module = design->module(cell->type);
769+
bool abc9_flop = inst_module && inst_module->get_bool_attribute(ID::abc9_flop);
770+
if (abc9_flop && !dff)
771+
continue;
772+
if (!(inst_module && inst_module->get_bool_attribute(ID::abc9_box)) && !yosys_celltypes.cell_known(cell->type))
773+
continue;
774+
775+
// TODO: Speed up toposort -- we care about box ordering only
776+
for (auto conn : cell->connections()) {
777+
if (cell->input(conn.first))
778+
for (auto bit : sigmap(conn.second))
779+
bit_users[bit].insert(cell->name);
780+
781+
if (cell->output(conn.first) && !abc9_flop)
782+
for (auto bit : sigmap(conn.second))
783+
bit_drivers[bit].insert(cell->name);
784+
}
785+
toposort.node(cell->name);
786+
}
787+
788+
// Build producer -> consumer edges on sigmapped nets.
789+
for (auto &it : bit_users)
790+
if (bit_drivers.count(it.first))
791+
for (auto driver_cell : bit_drivers.at(it.first))
792+
for (auto user_cell : it.second)
793+
toposort.edge(driver_cell, user_cell);
794+
if (ys_debug(1))
795+
toposort.analyze_loops = true;
796+
return toposort.sort();
797+
};
798+
799+
// Build TopoSort in a container, as we may need to conditionally rebuild it on retry.
800+
std::optional<TopoSort<IdString, RTLIL::sort_by_id_str>> toposort;
801+
toposort.emplace();
802+
bool no_loops = build_toposort(toposort.value());
803+
804+
// Fallback for residual loops after SCC cutting: insert additional
805+
// breakers on non-box loop cells, then re-run toposort checks.
806+
if (!no_loops) {
807+
SigSpec I, O;
808+
pool<IdString> broken_cells;
809+
810+
for (auto &loop : toposort.value().loops)
811+
for (auto cell_name : loop) {
812+
// Loop reports can overlap; cut each cell at most once.
813+
if (!broken_cells.insert(cell_name).second)
814+
continue;
815+
auto cell = module->cell(cell_name);
816+
log_assert(cell);
817+
auto inst_module = design->module(cell->type);
818+
if (inst_module && inst_module->get_bool_attribute(ID::abc9_box))
819+
continue;
820+
for (auto &c : cell->connections_) {
821+
if (c.second.is_fully_const()) continue;
822+
if (cell->output(c.first)) {
823+
Wire *w = module->addWire(NEW_ID, GetSize(c.second));
824+
I.append(w);
825+
O.append(c.second);
826+
c.second = w;
827+
}
828+
}
829+
}
830+
831+
if (!I.empty()) {
832+
auto cell = module->addCell(NEW_ID, ID($__ABC9_SCC_BREAKER));
833+
log_assert(GetSize(I) == GetSize(O));
834+
cell->setParam(ID::WIDTH, GetSize(I));
835+
cell->setPort(ID::I, std::move(I));
836+
cell->setPort(ID::O, std::move(O));
837+
838+
// Rebuild topo ordering after inserting the additional breakers.
839+
toposort.emplace();
840+
no_loops = build_toposort(toposort.value());
841+
}
842+
}
782843

783844
if (ys_debug(1)) {
784845
unsigned i = 0;
785-
for (auto &it : toposort.loops) {
846+
for (auto &it : toposort.value().loops) {
786847
log(" loop %d\n", i++);
787848
for (auto cell_name : it) {
788849
auto cell = module->cell(cell_name);
@@ -806,7 +867,7 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
806867
TimingInfo timing;
807868

808869
int port_id = 1, box_count = 0;
809-
for (auto cell_name : toposort.sorted) {
870+
for (auto cell_name : toposort.value().sorted) {
810871
RTLIL::Cell *cell = module->cell(cell_name);
811872
log_assert(cell);
812873

tests/arch/ice40/fsm.ys

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ cd fsm # Constrain all select calls below inside the top module
1212

1313
select -assert-count 4 t:SB_DFF
1414
select -assert-count 2 t:SB_DFFESR
15-
select -assert-max 15 t:SB_LUT4
15+
select -assert-max 16 t:SB_LUT4
1616
select -assert-none t:SB_DFFESR t:SB_DFF t:SB_LUT4 %% t:* %D
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
read_verilog -icells -specify <<EOT
2+
(* abc9_box, blackbox *)
3+
module box1(input i, output o);
4+
specify
5+
(i => o) = 1;
6+
endspecify
7+
endmodule
8+
9+
module top(input i, output o);
10+
wire a, b, c, z;
11+
$_AND_ a0(.A(b), .B(i), .Y(a));
12+
$_AND_ b0(.A(a), .B(c), .Y(b));
13+
$_AND_ c0(.A(b), .B(i), .Y(c));
14+
box1 u_box(.i(i), .o(z));
15+
assign o = c ^ z;
16+
endmodule
17+
EOT
18+
19+
abc9 -lut 4

0 commit comments

Comments
 (0)