From 66f9ee412ae94fa305660bdc33fa0c00fedd5500 Mon Sep 17 00:00:00 2001
From: Clifford Wolf <clifford@clifford.at>
Date: Tue, 9 Jun 2015 22:33:26 +0200
Subject: Added "aig" pass

---
 kernel/cellaigs.cc          | 158 +++++++++++++++++++++++++++++++++++++++-----
 passes/techmap/Makefile.inc |   1 +
 passes/techmap/aig.cc       | 148 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 291 insertions(+), 16 deletions(-)
 create mode 100644 passes/techmap/aig.cc

diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc
index 735c86654..b738b9dba 100644
--- a/kernel/cellaigs.cc
+++ b/kernel/cellaigs.cc
@@ -44,12 +44,42 @@ unsigned int AigNode::hash() const
 struct AigMaker
 {
 	Aig *aig;
+	Cell *cell;
 	idict<AigNode> aig_indices;
 
-	AigMaker(Aig *aig) : aig(aig) { }
+	int the_true_node;
+	int the_false_node;
 
-	int inport(IdString portname, int portbit, bool inverter = false)
+	AigMaker(Aig *aig, Cell *cell) : aig(aig), cell(cell)
+	{
+		the_true_node = -1;
+		the_false_node = -1;
+	}
+
+	int bool_node(bool value)
 	{
+		AigNode node;
+		node.portbit = -1;
+		node.inverter = !value;
+		node.left_parent = -1;
+		node.right_parent = -1;
+
+		if (!aig_indices.count(node)) {
+			aig_indices.expect(node, GetSize(aig->nodes));
+			aig->nodes.push_back(node);
+		}
+
+		return aig_indices.at(node);
+	}
+
+	int inport(IdString portname, int portbit = 0, bool inverter = false)
+	{
+		if (portbit >= GetSize(cell->getPort(portname))) {
+			if (cell->parameters.count(portname.str() + "_SIGNED") && cell->getParam(portname.str() + "_SIGNED").as_bool())
+				return inport(portname, GetSize(cell->getPort(portname))-1, inverter);
+			return bool_node(!inverter);
+		}
+
 		AigNode node;
 		node.portname = portname;
 		node.portbit = portbit;
@@ -65,8 +95,16 @@ struct AigMaker
 		return aig_indices.at(node);
 	}
 
-	int gate(int left_parent, int right_parent, bool inverter = false)
+	int not_inport(IdString portname, int portbit = 0)
 	{
+		return inport(portname, portbit, true);
+	}
+
+	int and_gate(int left_parent, int right_parent, bool inverter = false)
+	{
+		if (left_parent > right_parent)
+			std::swap(left_parent, right_parent);
+
 		AigNode node;
 		node.portbit = -1;
 		node.inverter = inverter;
@@ -81,9 +119,15 @@ struct AigMaker
 		return aig_indices.at(node);
 	}
 
-	void outport(int node, IdString portname, int portbit)
+	int nand_gate(int left_parent, int right_parent)
+	{
+		return and_gate(left_parent, right_parent, true);
+	}
+
+	void outport(int node, IdString portname, int portbit = 0)
 	{
-		aig->nodes.at(node).outports.push_back(pair<IdString, int>(portname, portbit));
+		if (portbit < GetSize(cell->getPort(portname)))
+			aig->nodes.at(node).outports.push_back(pair<IdString, int>(portname, portbit));
 	}
 };
 
@@ -92,28 +136,110 @@ Aig::Aig(Cell *cell)
 	if (cell->type[0] != '$')
 		return;
 
-	AigMaker mk(this);
+	AigMaker mk(this, cell);
 	name = cell->type.str();
 
 	cell->parameters.sort();
 	for (auto p : cell->parameters)
 		name += stringf(":%d", p.second.as_int());
 
-	if (cell->type == "$_AND_" || cell->type == "$_NAND_")
+	if (cell->type.in("$and", "$_AND_", "$_NAND_"))
+	{
+		for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) {
+			int A = mk.inport("\\A", i);
+			int B = mk.inport("\\B", i);
+			int Y = mk.and_gate(A, B, cell->type == "$_NAND_");
+			mk.outport(Y, "\\Y", i);
+		}
+		return;
+	}
+
+	if (cell->type.in("$or", "$_OR_", "$_NOR_"))
+	{
+		for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) {
+			int A = mk.not_inport("\\A", i);
+			int B = mk.not_inport("\\B", i);
+			int Y = mk.and_gate(A, B, cell->type != "$_NOR_");
+			mk.outport(Y, "\\Y", i);
+		}
+		return;
+	}
+
+	if (cell->type.in("$xor", "$xnor", "$_XOR_", "$_XNOR_"))
+	{
+		for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) {
+			int A = mk.inport("\\A", i);
+			int B = mk.inport("\\B", i);
+			int NA = mk.not_inport("\\A", i);
+			int NB = mk.not_inport("\\B", i);
+			int NOT_AB = mk.nand_gate(A, B);
+			int NOT_NAB = mk.nand_gate(NA, NB);
+			int Y = mk.and_gate(NOT_AB, NOT_NAB, !cell->type.in("$xor", "$_XOR_"));
+			mk.outport(Y, "\\Y", i);
+		}
+		return;
+	}
+
+	if (cell->type.in("$mux", "$_MUX_"))
+	{
+		int S = mk.inport("\\S");
+		int NS = mk.not_inport("\\S");
+		for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) {
+			int A = mk.inport("\\A", i);
+			int B = mk.inport("\\B", i);
+			int NOT_SB = mk.nand_gate(S, B);
+			int NOT_NSA = mk.nand_gate(NS, A);
+			int Y = mk.nand_gate(NOT_SB, NOT_NSA);
+			mk.outport(Y, "\\Y", i);
+		}
+		return;
+	}
+
+	if (cell->type == "$_AOI3_")
+	{
+		int A = mk.inport("\\A");
+		int B = mk.inport("\\B");
+		int NC = mk.not_inport("\\C");
+		int NOT_AB = mk.nand_gate(A, B);
+		int Y = mk.and_gate(NOT_AB, NC);
+		mk.outport(Y, "\\Y");
+		return;
+	}
+
+	if (cell->type == "$_OAI3_")
+	{
+		int NA = mk.not_inport("\\A");
+		int NB = mk.not_inport("\\B");
+		int C = mk.inport("\\C");
+		int NOT_NAB = mk.nand_gate(NA, NB);
+		int Y = mk.nand_gate(NOT_NAB, C);
+		mk.outport(Y, "\\Y");
+		return;
+	}
+
+	if (cell->type == "$_AOI4_")
 	{
-		int A = mk.inport("A", 0);
-		int B = mk.inport("B", 0);
-		int Y = mk.gate(A, B, cell->type == "$_NAND_");
-		mk.outport(Y, "Y", 0);
+		int A = mk.inport("\\A");
+		int B = mk.inport("\\B");
+		int C = mk.inport("\\C");
+		int D = mk.inport("\\D");
+		int NOT_AB = mk.nand_gate(A, B);
+		int NOT_CD = mk.nand_gate(C, D);
+		int Y = mk.and_gate(NOT_AB, NOT_CD);
+		mk.outport(Y, "\\Y");
 		return;
 	}
 
-	if (cell->type == "$_OR_" || cell->type == "$_NOR_")
+	if (cell->type == "$_OAI4_")
 	{
-		int A = mk.inport("A", 0, true);
-		int B = mk.inport("B", 0, true);
-		int Y = mk.gate(A, B, cell->type == "$_OR_");
-		mk.outport(Y, "Y", 0);
+		int NA = mk.not_inport("\\A");
+		int NB = mk.not_inport("\\B");
+		int NC = mk.not_inport("\\C");
+		int ND = mk.not_inport("\\D");
+		int NOT_NAB = mk.nand_gate(NA, NB);
+		int NOT_NCD = mk.nand_gate(NC, ND);
+		int Y = mk.nand_gate(NOT_NAB, NOT_NCD);
+		mk.outport(Y, "\\Y");
 		return;
 	}
 
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index 6ef4fbc26..3cbee5b85 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -18,6 +18,7 @@ OBJS += passes/techmap/dff2dffe.o
 OBJS += passes/techmap/dffinit.o
 OBJS += passes/techmap/pmuxtree.o
 OBJS += passes/techmap/muxcover.o
+OBJS += passes/techmap/aig.o
 endif
 
 GENFILES += passes/techmap/techmap.inc
diff --git a/passes/techmap/aig.cc b/passes/techmap/aig.cc
new file mode 100644
index 000000000..0c0f0b130
--- /dev/null
+++ b/passes/techmap/aig.cc
@@ -0,0 +1,148 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/cellaigs.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct AigPass : public Pass {
+	AigPass() : Pass("aig", "convert logic to and-inverter circuit") { }
+	virtual void help()
+	{
+		log("\n");
+		log("    aig [options] [selection]\n");
+		log("\n");
+		log("Replace all logic cells with circuits made of only $_AND_ and\n");
+		log("$_NOT_ cells.\n");
+		log("\n");
+		log("    -nand\n");
+		log("        Enable creation of $_NAND_ cells\n");
+		log("\n");
+	}
+	virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+	{
+		bool nand_mode = false;
+
+		log_header("Executing AIG pass (converting logic to AIG).\n");
+
+		size_t argidx;
+		for (argidx = 1; argidx < args.size(); argidx++)
+		{
+			if (args[argidx] == "-nand") {
+				nand_mode = true;
+				continue;
+			}
+			break;
+		}
+		extra_args(args, argidx, design);
+
+		for (auto module : design->selected_modules())
+		{
+			vector<Cell*> replaced_cells;
+			int not_replaced_count = 0;
+			dict<IdString, int> stat_replaced;
+			dict<IdString, int> stat_not_replaced;
+			int orig_num_cells = GetSize(module->cells());
+
+			for (auto cell : module->selected_cells())
+			{
+				Aig aig(cell);
+
+				if (cell->type == "$_AND_" || cell->type == "$_NOT_")
+					aig.name.clear();
+
+				if (nand_mode && cell->type == "$_NAND_")
+					aig.name.clear();
+
+				if (aig.name.empty()) {
+					not_replaced_count++;
+					stat_not_replaced[cell->type]++;
+					continue;
+				}
+
+				vector<SigBit> sigs;
+				dict<pair<int, int>, SigBit> and_cache;
+
+				for (int node_idx = 0; node_idx < GetSize(aig.nodes); node_idx++)
+				{
+					SigBit bit;
+					auto &node = aig.nodes[node_idx];
+
+					if (node.portbit >= 0) {
+						bit = cell->getPort(node.portname)[node.portbit];
+					} else if (node.left_parent < 0 && node.right_parent < 0) {
+						bit = node.inverter ? State::S0 : State::S1;
+					} else {
+						SigBit A = sigs.at(node.left_parent);
+						SigBit B = sigs.at(node.right_parent);
+						if (nand_mode && node.inverter) {
+							bit = module->NandGate(NEW_ID, A, B);
+							goto nand_inverter;
+						} else {
+							pair<int, int> key(node.left_parent, node.right_parent);
+							if (and_cache.count(key))
+								bit = and_cache.at(key);
+							else
+								bit = module->AndGate(NEW_ID, A, B);
+						}
+					}
+
+					if (node.inverter)
+						bit = module->NotGate(NEW_ID, bit);
+				
+				nand_inverter:
+					for (auto &op : node.outports)
+						module->connect(cell->getPort(op.first)[op.second], bit);
+
+					sigs.push_back(bit);
+				}
+
+				replaced_cells.push_back(cell);
+				stat_replaced[cell->type]++;
+			}
+
+			if (not_replaced_count == 0 && replaced_cells.empty())
+				continue;
+
+			log("Module %s: replaced %d cells with %d new cells, skipped %d cells.\n", log_id(module),
+					GetSize(replaced_cells), GetSize(module->cells()) - orig_num_cells, not_replaced_count);
+
+			if (!stat_replaced.empty()) {
+				stat_replaced.sort();
+				log("  replaced %d cell types:\n", GetSize(stat_replaced));
+				for (auto &it : stat_replaced)
+					log("%8d %s\n", it.second, log_id(it.first));
+			}
+
+			if (!stat_not_replaced.empty()) {
+				stat_not_replaced.sort();
+				log("  not replaced %d cell types:\n", GetSize(stat_not_replaced));
+				for (auto &it : stat_not_replaced)
+					log("%8d %s\n", it.second, log_id(it.first));
+			}
+
+			for (auto cell : replaced_cells)
+				module->remove(cell);
+		}
+	}
+} AigPass;
+ 
+PRIVATE_NAMESPACE_END
-- 
cgit v1.2.3