/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2017 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/sigtools.h" #include "kernel/modtools.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN void demorgan_worker( ModIndex& index, Cell *cell, unsigned int& cells_changed) { SigMap& sigmap = index.sigmap; auto m = cell->module; //TODO: Add support for reduce_xor //DeMorgan of XOR is either XOR (if even number of inputs) or XNOR (if odd number) if( (cell->type != ID($reduce_and)) && (cell->type != ID($reduce_or)) ) return; auto insig = sigmap(cell->getPort(ID::A)); log("Inspecting %s cell %s (%d inputs)\n", log_id(cell->type), log_id(cell->name), GetSize(insig)); int num_inverted = 0; for(int i=0; i ports = index.query_ports(b); bool inverted = false; for(auto x : ports) { if(x.port == ID::Y && x.cell->type == ID($_NOT_)) { inverted = true; break; } } if(inverted) num_inverted ++; } //Stop if less than half of the inputs are inverted if(num_inverted*2 < GetSize(insig)) { log(" %d / %d inputs are inverted, not pushing\n", num_inverted, GetSize(insig)); return; } //More than half of the inputs are inverted! Push through cells_changed ++; log(" %d / %d inputs are inverted, pushing inverter through reduction\n", num_inverted, GetSize(insig)); //For each input, either add or remove the inverter as needed //TODO: this duplicates the loop up above, can we refactor it? for(int i=0; i ports = index.query_ports(b); RTLIL::Cell* srcinv = NULL; for(auto x : ports) { if(x.port == ID::Y && x.cell->type == ID($_NOT_)) { srcinv = x.cell; break; } } //We are NOT inverted! Add an inverter if(!srcinv) { auto inverted_b = m->addWire(NEW_ID); m->addNot(NEW_ID, RTLIL::SigSpec(b), RTLIL::SigSpec(inverted_b)); insig[i] = inverted_b; } //We ARE inverted - bypass it //Don't automatically delete the inverter since other stuff might still use it else insig[i] = srcinv->getPort(ID::A); } //Cosmetic fixup: If our input is just a scrambled version of one bus, rearrange it //Reductions are all commutative, so there's no point in having them in a weird order bool same_signal = true; RTLIL::Wire* srcwire = insig[0].wire; dict seen_bits; for(int i=0; iwidth) ) { log("Rearranging bits\n"); RTLIL::SigSpec newsig; for(int i=0; isetPort(ID::A, insig); //Change the cell type if(cell->type == ID($reduce_and)) cell->type = ID($reduce_or); else if(cell->type == ID($reduce_or)) cell->type = ID($reduce_and); //don't change XOR //Add an inverter to the output auto inverted_output = cell->getPort(ID::Y); auto uninverted_output = m->addWire(NEW_ID); m->addNot(NEW_ID, RTLIL::SigSpec(uninverted_output), inverted_output); cell->setPort(ID::Y, uninverted_output); } struct OptDemorganPass : public Pass { OptDemorganPass() : Pass("opt_demorgan", "Optimize reductions with DeMorgan equivalents") { } void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" opt_demorgan [selection]\n"); log("\n"); log("This pass pushes inverters through $reduce_* cells if this will reduce the\n"); log("overall gate count of the circuit\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing OPT_DEMORGAN pass (push inverters through $reduce_* cells).\n"); int argidx = 0; extra_args(args, argidx, design); unsigned int cells_changed = 0; for (auto module : design->selected_modules()) { ModIndex index(module); for (auto cell : module->selected_cells()) demorgan_worker(index, cell, cells_changed); } if(cells_changed) log("Pushed inverters through %u reductions\n", cells_changed); } } OptDemorganPass; PRIVATE_NAMESPACE_END