| /* | |
| * 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/cellaigs.h" | |
| YOSYS_NAMESPACE_BEGIN | |
| AigNode::AigNode() | |
| { | |
| portbit = -1; | |
| inverter = false; | |
| left_parent = -1; | |
| right_parent = -1; | |
| } | |
| bool AigNode::operator==(const AigNode &other) const | |
| { | |
| if (portname != other.portname) return false; | |
| if (portbit != other.portbit) return false; | |
| if (inverter != other.inverter) return false; | |
| if (left_parent != other.left_parent) return false; | |
| if (right_parent != other.right_parent) return false; | |
| return true; | |
| } | |
| unsigned int AigNode::hash() const | |
| { | |
| unsigned int h = mkhash_init; | |
| h = mkhash(portname.hash(), portbit); | |
| h = mkhash(h, inverter); | |
| h = mkhash(h, left_parent); | |
| h = mkhash(h, right_parent); | |
| return h; | |
| } | |
| bool Aig::operator==(const Aig &other) const | |
| { | |
| return name == other.name; | |
| } | |
| unsigned int Aig::hash() const | |
| { | |
| return hash_ops<std::string>::hash(name); | |
| } | |
| struct AigMaker | |
| { | |
| Aig *aig; | |
| Cell *cell; | |
| idict<AigNode> aig_indices; | |
| int the_true_node; | |
| int the_false_node; | |
| AigMaker(Aig *aig, Cell *cell) : aig(aig), cell(cell) | |
| { | |
| the_true_node = -1; | |
| the_false_node = -1; | |
| } | |
| int node2index(const AigNode &node) | |
| { | |
| if (node.left_parent > node.right_parent) { | |
| AigNode n(node); | |
| std::swap(n.left_parent, n.right_parent); | |
| return node2index(n); | |
| } | |
| if (!aig_indices.count(node)) { | |
| aig_indices.expect(node, GetSize(aig->nodes)); | |
| aig->nodes.push_back(node); | |
| } | |
| return aig_indices.at(node); | |
| } | |
| int bool_node(bool value) | |
| { | |
| AigNode node; | |
| node.inverter = value; | |
| return node2index(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; | |
| node.inverter = inverter; | |
| return node2index(node); | |
| } | |
| vector<int> inport_vec(IdString portname, int width) | |
| { | |
| vector<int> vec; | |
| for (int i = 0; i < width; i++) | |
| vec.push_back(inport(portname, i)); | |
| return vec; | |
| } | |
| int not_inport(IdString portname, int portbit = 0) | |
| { | |
| return inport(portname, portbit, true); | |
| } | |
| int not_gate(int A) | |
| { | |
| AigNode node(aig_indices[A]); | |
| node.outports.clear(); | |
| node.inverter = !node.inverter; | |
| return node2index(node); | |
| } | |
| int and_gate(int A, int B, bool inverter = false) | |
| { | |
| if (A == B) | |
| return inverter ? not_gate(A) : A; | |
| const AigNode &nA = aig_indices[A]; | |
| const AigNode &nB = aig_indices[B]; | |
| AigNode nB_inv(nB); | |
| nB_inv.inverter = !nB_inv.inverter; | |
| if (nA == nB_inv) | |
| return bool_node(inverter); | |
| bool nA_bool = nA.portbit < 0 && nA.left_parent < 0 && nA.right_parent < 0; | |
| bool nB_bool = nB.portbit < 0 && nB.left_parent < 0 && nB.right_parent < 0; | |
| if (nA_bool && nB_bool) { | |
| bool bA = nA.inverter; | |
| bool bB = nB.inverter; | |
| return bool_node(inverter != (bA && bB)); | |
| } | |
| if (nA_bool) { | |
| bool bA = nA.inverter; | |
| if (inverter) | |
| return bA ? not_gate(B) : bool_node(true); | |
| return bA ? B : bool_node(false); | |
| } | |
| if (nB_bool) { | |
| bool bB = nB.inverter; | |
| if (inverter) | |
| return bB ? not_gate(A) : bool_node(true); | |
| return bB ? A : bool_node(false); | |
| } | |
| AigNode node; | |
| node.inverter = inverter; | |
| node.left_parent = A; | |
| node.right_parent = B; | |
| return node2index(node); | |
| } | |
| int nand_gate(int A, int B) | |
| { | |
| return and_gate(A, B, true); | |
| } | |
| int or_gate(int A, int B) | |
| { | |
| return nand_gate(not_gate(A), not_gate(B)); | |
| } | |
| int nor_gate(int A, int B) | |
| { | |
| return and_gate(not_gate(A), not_gate(B)); | |
| } | |
| int xor_gate(int A, int B) | |
| { | |
| return nor_gate(and_gate(A, B), nor_gate(A, B)); | |
| } | |
| int xnor_gate(int A, int B) | |
| { | |
| return or_gate(and_gate(A, B), nor_gate(A, B)); | |
| } | |
| int andnot_gate(int A, int B) | |
| { | |
| return and_gate(A, not_gate(B)); | |
| } | |
| int ornot_gate(int A, int B) | |
| { | |
| return or_gate(A, not_gate(B)); | |
| } | |
| int mux_gate(int A, int B, int S) | |
| { | |
| return or_gate(and_gate(A, not_gate(S)), and_gate(B, S)); | |
| } | |
| vector<int> adder(const vector<int> &A, const vector<int> &B, int carry, vector<int> *X = nullptr, vector<int> *CO = nullptr) | |
| { | |
| vector<int> Y(GetSize(A)); | |
| log_assert(GetSize(A) == GetSize(B)); | |
| for (int i = 0; i < GetSize(A); i++) { | |
| Y[i] = xor_gate(xor_gate(A[i], B[i]), carry); | |
| carry = or_gate(and_gate(A[i], B[i]), and_gate(or_gate(A[i], B[i]), carry)); | |
| if (X != nullptr) | |
| X->at(i) = xor_gate(A[i], B[i]); | |
| if (CO != nullptr) | |
| CO->at(i) = carry; | |
| } | |
| return Y; | |
| } | |
| void outport(int node, IdString portname, int portbit = 0) | |
| { | |
| if (portbit < GetSize(cell->getPort(portname))) | |
| aig->nodes.at(node).outports.push_back(pair<IdString, int>(portname, portbit)); | |
| } | |
| void outport_bool(int node, IdString portname) | |
| { | |
| outport(node, portname); | |
| for (int i = 1; i < GetSize(cell->getPort(portname)); i++) | |
| outport(bool_node(false), portname, i); | |
| } | |
| void outport_vec(const vector<int> &vec, IdString portname) | |
| { | |
| for (int i = 0; i < GetSize(vec); i++) | |
| outport(vec.at(i), portname, i); | |
| } | |
| }; | |
| Aig::Aig(Cell *cell) | |
| { | |
| if (cell->type[0] != '$') | |
| return; | |
| AigMaker mk(this, cell); | |
| name = cell->type.str(); | |
| string mkname_last; | |
| bool mkname_a_signed = false; | |
| bool mkname_b_signed = false; | |
| bool mkname_is_signed = false; | |
| cell->parameters.sort(); | |
| for (auto p : cell->parameters) | |
| { | |
| if (p.first == "\\A_WIDTH" && mkname_a_signed) { | |
| name = mkname_last + stringf(":%d%c", p.second.as_int(), mkname_is_signed ? 'S' : 'U'); | |
| } else if (p.first == "\\B_WIDTH" && mkname_b_signed) { | |
| name = mkname_last + stringf(":%d%c", p.second.as_int(), mkname_is_signed ? 'S' : 'U'); | |
| } else { | |
| mkname_last = name; | |
| name += stringf(":%d", p.second.as_int()); | |
| } | |
| mkname_a_signed = false; | |
| mkname_b_signed = false; | |
| mkname_is_signed = false; | |
| if (p.first == "\\A_SIGNED") { | |
| mkname_a_signed = true; | |
| mkname_is_signed = p.second.as_bool(); | |
| } | |
| if (p.first == "\\B_SIGNED") { | |
| mkname_b_signed = true; | |
| mkname_is_signed = p.second.as_bool(); | |
| } | |
| } | |
| if (cell->type.in("$not", "$_NOT_", "$pos", "$_BUF_")) | |
| { | |
| for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) { | |
| int A = mk.inport("\\A", i); | |
| int Y = cell->type.in("$not", "$_NOT_") ? mk.not_gate(A) : A; | |
| mk.outport(Y, "\\Y", i); | |
| } | |
| goto optimize; | |
| } | |
| if (cell->type.in("$and", "$_AND_", "$_NAND_", "$or", "$_OR_", "$_NOR_", "$xor", "$xnor", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_")) | |
| { | |
| for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) { | |
| int A = mk.inport("\\A", i); | |
| int B = mk.inport("\\B", i); | |
| int Y = cell->type.in("$and", "$_AND_") ? mk.and_gate(A, B) : | |
| cell->type.in("$_NAND_") ? mk.nand_gate(A, B) : | |
| cell->type.in("$or", "$_OR_") ? mk.or_gate(A, B) : | |
| cell->type.in("$_NOR_") ? mk.nor_gate(A, B) : | |
| cell->type.in("$xor", "$_XOR_") ? mk.xor_gate(A, B) : | |
| cell->type.in("$xnor", "$_XNOR_") ? mk.xnor_gate(A, B) : | |
| cell->type.in("$_ANDNOT_") ? mk.andnot_gate(A, B) : | |
| cell->type.in("$_ORNOT_") ? mk.ornot_gate(A, B) : -1; | |
| mk.outport(Y, "\\Y", i); | |
| } | |
| goto optimize; | |
| } | |
| if (cell->type.in("$mux", "$_MUX_")) | |
| { | |
| int S = mk.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 Y = mk.mux_gate(A, B, S); | |
| mk.outport(Y, "\\Y", i); | |
| } | |
| goto optimize; | |
| } | |
| if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool")) | |
| { | |
| int Y = mk.inport("\\A", 0); | |
| for (int i = 1; i < GetSize(cell->getPort("\\A")); i++) { | |
| int A = mk.inport("\\A", i); | |
| if (cell->type == "$reduce_and") Y = mk.and_gate(A, Y); | |
| if (cell->type == "$reduce_or") Y = mk.or_gate(A, Y); | |
| if (cell->type == "$reduce_bool") Y = mk.or_gate(A, Y); | |
| if (cell->type == "$reduce_xor") Y = mk.xor_gate(A, Y); | |
| if (cell->type == "$reduce_xnor") Y = mk.xor_gate(A, Y); | |
| } | |
| if (cell->type == "$reduce_xnor") | |
| Y = mk.not_gate(Y); | |
| mk.outport(Y, "\\Y", 0); | |
| for (int i = 1; i < GetSize(cell->getPort("\\Y")); i++) | |
| mk.outport(mk.bool_node(false), "\\Y", i); | |
| goto optimize; | |
| } | |
| if (cell->type.in("$logic_not", "$logic_and", "$logic_or")) | |
| { | |
| int A = mk.inport("\\A", 0), Y = -1; | |
| for (int i = 1; i < GetSize(cell->getPort("\\A")); i++) | |
| A = mk.or_gate(mk.inport("\\A", i), A); | |
| if (cell->type.in("$logic_and", "$logic_or")) { | |
| int B = mk.inport("\\B", 0); | |
| for (int i = 1; i < GetSize(cell->getPort("\\B")); i++) | |
| B = mk.or_gate(mk.inport("\\B", i), B); | |
| if (cell->type == "$logic_and") Y = mk.and_gate(A, B); | |
| if (cell->type == "$logic_or") Y = mk.or_gate(A, B); | |
| } else { | |
| if (cell->type == "$logic_not") Y = mk.not_gate(A); | |
| } | |
| mk.outport_bool(Y, "\\Y"); | |
| goto optimize; | |
| } | |
| if (cell->type.in("$add", "$sub")) | |
| { | |
| int width = GetSize(cell->getPort("\\Y")); | |
| vector<int> A = mk.inport_vec("\\A", width); | |
| vector<int> B = mk.inport_vec("\\B", width); | |
| int carry = mk.bool_node(false); | |
| if (cell->type == "$sub") { | |
| for (auto &n : B) | |
| n = mk.not_gate(n); | |
| carry = mk.not_gate(carry); | |
| } | |
| vector<int> Y = mk.adder(A, B, carry); | |
| mk.outport_vec(Y, "\\Y"); | |
| goto optimize; | |
| } | |
| if (cell->type == "$alu") | |
| { | |
| int width = GetSize(cell->getPort("\\Y")); | |
| vector<int> A = mk.inport_vec("\\A", width); | |
| vector<int> B = mk.inport_vec("\\B", width); | |
| int carry = mk.inport("\\CI"); | |
| int binv = mk.inport("\\BI"); | |
| for (auto &n : B) | |
| n = mk.xor_gate(n, binv); | |
| vector<int> X(width), CO(width); | |
| vector<int> Y = mk.adder(A, B, carry, &X, &CO); | |
| for (int i = 0; i < width; i++) | |
| X[i] = mk.xor_gate(A[i], B[i]); | |
| mk.outport_vec(Y, "\\Y"); | |
| mk.outport_vec(X, "\\X"); | |
| mk.outport_vec(CO, "\\CO"); | |
| goto optimize; | |
| } | |
| if (cell->type.in("$eq", "$ne")) | |
| { | |
| int width = max(GetSize(cell->getPort("\\A")), GetSize(cell->getPort("\\B"))); | |
| vector<int> A = mk.inport_vec("\\A", width); | |
| vector<int> B = mk.inport_vec("\\B", width); | |
| int Y = mk.bool_node(false); | |
| for (int i = 0; i < width; i++) | |
| Y = mk.or_gate(Y, mk.xor_gate(A[i], B[i])); | |
| if (cell->type == "$eq") | |
| Y = mk.not_gate(Y); | |
| mk.outport_bool(Y, "\\Y"); | |
| goto optimize; | |
| } | |
| if (cell->type == "$_AOI3_") | |
| { | |
| int A = mk.inport("\\A"); | |
| int B = mk.inport("\\B"); | |
| int C = mk.inport("\\C"); | |
| int Y = mk.nor_gate(mk.and_gate(A, B), C); | |
| mk.outport(Y, "\\Y"); | |
| goto optimize; | |
| } | |
| if (cell->type == "$_OAI3_") | |
| { | |
| int A = mk.inport("\\A"); | |
| int B = mk.inport("\\B"); | |
| int C = mk.inport("\\C"); | |
| int Y = mk.nand_gate(mk.or_gate(A, B), C); | |
| mk.outport(Y, "\\Y"); | |
| goto optimize; | |
| } | |
| 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 Y = mk.nor_gate(mk.and_gate(A, B), mk.and_gate(C, D)); | |
| mk.outport(Y, "\\Y"); | |
| goto optimize; | |
| } | |
| if (cell->type == "$_OAI4_") | |
| { | |
| int A = mk.inport("\\A"); | |
| int B = mk.inport("\\B"); | |
| int C = mk.inport("\\C"); | |
| int D = mk.inport("\\D"); | |
| int Y = mk.nand_gate(mk.nor_gate(A, B), mk.nor_gate(C, D)); | |
| mk.outport(Y, "\\Y"); | |
| goto optimize; | |
| } | |
| name.clear(); | |
| return; | |
| optimize:; | |
| pool<int> used_old_ids; | |
| vector<AigNode> new_nodes; | |
| dict<int, int> old_to_new_ids; | |
| old_to_new_ids[-1] = -1; | |
| for (int i = GetSize(nodes)-1; i >= 0; i--) { | |
| if (!nodes[i].outports.empty()) | |
| used_old_ids.insert(i); | |
| if (!used_old_ids.count(i)) | |
| continue; | |
| if (nodes[i].left_parent >= 0) | |
| used_old_ids.insert(nodes[i].left_parent); | |
| if (nodes[i].right_parent >= 0) | |
| used_old_ids.insert(nodes[i].right_parent); | |
| } | |
| for (int i = 0; i < GetSize(nodes); i++) { | |
| if (!used_old_ids.count(i)) | |
| continue; | |
| nodes[i].left_parent = old_to_new_ids.at(nodes[i].left_parent); | |
| nodes[i].right_parent = old_to_new_ids.at(nodes[i].right_parent); | |
| old_to_new_ids[i] = GetSize(new_nodes); | |
| new_nodes.push_back(nodes[i]); | |
| } | |
| new_nodes.swap(nodes); | |
| } | |
| YOSYS_NAMESPACE_END |