From 74dad5afe72ff1afa777352e9ca078cd80878a52 Mon Sep 17 00:00:00 2001
From: Dan Ravensloft <dan.ravensloft@gmail.com>
Date: Mon, 11 Jan 2021 18:37:27 +0000
Subject: [PATCH] scc: Add -specify option to find loops in boxes

---
 passes/cmds/scc.cc     | 77 ++++++++++++++++++++++++++++++++----------
 passes/techmap/abc9.cc |  2 +-
 2 files changed, 61 insertions(+), 18 deletions(-)

diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc
index 8e7f3f990..7aa9a484f 100644
--- a/passes/cmds/scc.cc
+++ b/passes/cmds/scc.cc
@@ -37,7 +37,7 @@ struct SccWorker
 	RTLIL::Design *design;
 	RTLIL::Module *module;
 	SigMap sigmap;
-	CellTypes ct;
+	CellTypes ct, specifyCells;
 
 	std::set<RTLIL::Cell*> workQueue;
 	std::map<RTLIL::Cell*, std::set<RTLIL::Cell*>> cellToNextCell;
@@ -100,7 +100,7 @@ struct SccWorker
 		}
 	}
 
-	SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool nofeedbackMode, bool allCellTypes, int maxDepth) :
+	SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool nofeedbackMode, bool allCellTypes, bool specifyMode, int maxDepth) :
 			design(design), module(module), sigmap(module)
 	{
 		if (module->processes.size() > 0) {
@@ -115,6 +115,18 @@ struct SccWorker
 			ct.setup_stdcells();
 		}
 
+		// Discover boxes with specify rules in them, for special handling.
+		if (specifyMode) {
+			for (auto mod : design->modules())
+				if (mod->get_blackbox_attribute(false))
+					for (auto cell : mod->cells())
+						if (cell->type == ID($specify2))
+						{
+							specifyCells.setup_module(mod);
+							break;
+						}
+		}
+
 		SigPool selectedSignals;
 		SigSet<RTLIL::Cell*> sigToNextCells;
 
@@ -129,29 +141,52 @@ struct SccWorker
 			if (!design->selected(module, cell))
 				continue;
 
-			if (!allCellTypes && !ct.cell_known(cell->type))
+			if (!allCellTypes && !ct.cell_known(cell->type) && !specifyCells.cell_known(cell->type))
 				continue;
 
 			workQueue.insert(cell);
 
 			RTLIL::SigSpec inputSignals, outputSignals;
 
-			for (auto &conn : cell->connections())
-			{
-				bool isInput = true, isOutput = true;
+			if (specifyCells.cell_known(cell->type)) {
+				// Use specify rules of the type `(X => Y) = NN` to look for asynchronous paths in boxes.
+				for (auto subcell : design->module(cell->type)->cells())
+				{
+					if (subcell->type != ID($specify2))
+						continue;
 
-				if (ct.cell_known(cell->type)) {
-					isInput = ct.cell_input(cell->type, conn.first);
-					isOutput = ct.cell_output(cell->type, conn.first);
+					for (auto bit : subcell->getPort(ID::SRC))
+					{
+						if (!bit.wire || !cell->hasPort(bit.wire->name))
+							continue;
+						inputSignals.append(sigmap(cell->getPort(bit.wire->name)));
+					}
+
+					for (auto bit : subcell->getPort(ID::DST))
+					{
+						if (!bit.wire || !cell->hasPort(bit.wire->name))
+							continue;
+						outputSignals.append(sigmap(cell->getPort(bit.wire->name)));
+					}
 				}
+			} else {
+				for (auto &conn : cell->connections())
+				{
+					bool isInput = true, isOutput = true;
 
-				RTLIL::SigSpec sig = selectedSignals.extract(sigmap(conn.second));
-				sig.sort_and_unify();
+					if (ct.cell_known(cell->type)) {
+						isInput = ct.cell_input(cell->type, conn.first);
+						isOutput = ct.cell_output(cell->type, conn.first);
+					}
 
-				if (isInput)
-					inputSignals.append(sig);
-				if (isOutput)
-					outputSignals.append(sig);
+					RTLIL::SigSpec sig = selectedSignals.extract(sigmap(conn.second));
+					sig.sort_and_unify();
+
+					if (isInput)
+						inputSignals.append(sig);
+					if (isOutput)
+						outputSignals.append(sig);
+				}
 			}
 
 			inputSignals.sort_and_unify();
@@ -228,7 +263,7 @@ struct SccPass : public Pass {
 		log("design.\n");
 		log("\n");
 		log("    -expect <num>\n");
-		log("        expect to find exactly <num> SSCs. A different number of SSCs will\n");
+		log("        expect to find exactly <num> SCCs. A different number of SCCs will\n");
 		log("        produce an error.\n");
 		log("\n");
 		log("    -max_depth <num>\n");
@@ -254,6 +289,9 @@ struct SccPass : public Pass {
 		log("        replace the current selection with a selection of all cells and wires\n");
 		log("        that are part of a found logic loop\n");
 		log("\n");
+		log("    -specify\n");
+		log("        examine specify rules to detect logic loops in whitebox/blackbox cells\n");
+		log("\n");
 	}
 	void execute(std::vector<std::string> args, RTLIL::Design *design) override
 	{
@@ -261,6 +299,7 @@ struct SccPass : public Pass {
 		bool allCellTypes = false;
 		bool selectMode = false;
 		bool nofeedbackMode = false;
+		bool specifyMode = false;
 		int maxDepth = -1;
 		int expect = -1;
 
@@ -293,6 +332,10 @@ struct SccPass : public Pass {
 				selectMode = true;
 				continue;
 			}
+			if (args[argidx] == "-specify") {
+				specifyMode = true;
+				continue;
+			}
 			break;
 		}
 		int origSelectPos = design->selection_stack.size() - 1;
@@ -303,7 +346,7 @@ struct SccPass : public Pass {
 
 		for (auto mod : design->selected_modules())
 		{
-			SccWorker worker(design, mod, nofeedbackMode, allCellTypes, maxDepth);
+			SccWorker worker(design, mod, nofeedbackMode, allCellTypes, specifyMode, maxDepth);
 
 			if (!setAttr.empty())
 			{
diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc
index 7d017ac40..56bb15495 100644
--- a/passes/techmap/abc9.cc
+++ b/passes/techmap/abc9.cc
@@ -339,7 +339,7 @@ struct Abc9Pass : public ScriptPass
 
 		if (check_label("pre")) {
 			run("read_verilog -icells -lib -specify +/abc9_model.v");
-			run("scc -set_attr abc9_scc_id {}");
+			run("scc -specify -set_attr abc9_scc_id {}");
 			if (help_mode)
 				run("abc9_ops -mark_scc -prep_delays -prep_xaiger [-dff]", "(option for -dff)");
 			else