mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-03 21:09:12 +00:00 
			
		
		
		
	s/((Claire|Xen|Xenia|Clifford)\s+)+(Wolf|Xen)\s+<(claire|clifford)@(symbioticeda.com|clifford.at|yosyshq.com)>/Claire Xenia Wolf <claire@yosyshq.com>/gi; s/((Nina|Nak|N\.)\s+)+Engelhardt\s+<nak@(symbioticeda.com|yosyshq.com)>/N. Engelhardt <nak@yosyshq.com>/gi; s/((David)\s+)+Shah\s+<(dave|david)@(symbioticeda.com|yosyshq.com|ds0.me)>/David Shah <dave@ds0.me>/gi; s/((Miodrag)\s+)+Milanovic\s+<(miodrag|micko)@(symbioticeda.com|yosyshq.com)>/Miodrag Milanovic <micko@yosyshq.com>/gi; s,https?://www.clifford.at/yosys/,http://yosyshq.net/yosys/,g;
		
			
				
	
	
		
			202 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			202 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 *  yosys -- Yosys Open SYnthesis Suite
 | 
						|
 *
 | 
						|
 *  Copyright (C) 2017  Claire Xenia Wolf <claire@yosyshq.com>
 | 
						|
 *
 | 
						|
 *  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() 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) 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
 |