| /* |
| * yosys -- Yosys Open SYnthesis Suite |
| * |
| * Copyright (C) 2017 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/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<GetSize(insig); i++) |
| { |
| auto b = insig[i]; |
| |
| //See if this bit is driven by a $not cell |
| //TODO: do other stuff like nor/nand? |
| pool<ModIndex::PortInfo> 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<GetSize(insig); i++) |
| { |
| auto b = insig[i]; |
| |
| //See if this bit is driven by a $not cell |
| //TODO: do other stuff like nor/nand? |
| pool<ModIndex::PortInfo> 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<int, int> seen_bits; |
| for(int i=0; i<GetSize(insig); i++) |
| seen_bits[i] = 0; |
| for(int i=0; i<GetSize(insig); i++) |
| { |
| seen_bits[insig[i].offset] ++; |
| if(insig[i].wire != srcwire) |
| { |
| same_signal = false; |
| break; |
| } |
| } |
| if(same_signal) |
| { |
| //Make sure we've seen every bit exactly once |
| bool every_bit_once = true; |
| for(int i=0; i<GetSize(insig); i++) |
| { |
| if(seen_bits[i] != 1) |
| { |
| every_bit_once = false; |
| break; |
| } |
| } |
| |
| //All good? Just use the whole wire as-is without any reordering |
| //We do have to swap MSB to LSB b/c that's the way the reduction cells seem to work? |
| //Unclear on why this isn't sorting properly |
| //TODO: can we do SigChunks instead of single bits if we have subsets of a bus? |
| if(every_bit_once && (GetSize(insig) == srcwire->width) ) |
| { |
| log("Rearranging bits\n"); |
| RTLIL::SigSpec newsig; |
| for(int i=0; i<GetSize(insig); i++) |
| newsig.append(RTLIL::SigBit(srcwire, GetSize(insig) - i - 1)); |
| insig = newsig; |
| insig.sort(); |
| } |
| } |
| |
| //Push the new input signal back to the reduction (after bypassing/adding inverters) |
| cell->setPort(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<std::string> 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 |