From 66f9ee412ae94fa305660bdc33fa0c00fedd5500 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 9 Jun 2015 22:33:26 +0200 Subject: [PATCH] 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 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) { - aig->nodes.at(node).outports.push_back(pair(portname, portbit)); + return and_gate(left_parent, right_parent, true); + } + + void outport(int node, IdString portname, int portbit = 0) + { + if (portbit < GetSize(cell->getPort(portname))) + aig->nodes.at(node).outports.push_back(pair(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_")) { - 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); + 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 == "$_OR_" || cell->type == "$_NOR_") + if (cell->type.in("$or", "$_OR_", "$_NOR_")) { - 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); + 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"); + 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 == "$_OAI4_") + { + 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 + * + * 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 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 replaced_cells; + int not_replaced_count = 0; + dict stat_replaced; + dict 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 sigs; + dict, 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 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