mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			216 lines
		
	
	
	
		
			6.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			216 lines
		
	
	
	
		
			6.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 *  yosys -- Yosys Open SYnthesis Suite
 | 
						|
 *
 | 
						|
 *  Copyright (C) 2023  N. Engelhardt <nak@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/log.h"
 | 
						|
#include "kernel/register.h"
 | 
						|
#include "kernel/rtlil.h"
 | 
						|
#include "kernel/sigtools.h"
 | 
						|
 | 
						|
USING_YOSYS_NAMESPACE
 | 
						|
PRIVATE_NAMESPACE_BEGIN
 | 
						|
 | 
						|
// ============================================================================
 | 
						|
 | 
						|
 | 
						|
 | 
						|
struct QlBramMergeWorker {
 | 
						|
 | 
						|
	// can be used to record parameter values that have to match on both sides
 | 
						|
	typedef dict<RTLIL::IdString, RTLIL::Const> MergeableGroupKeyType;
 | 
						|
 | 
						|
	RTLIL::Module *module;
 | 
						|
	dict<MergeableGroupKeyType, pool<RTLIL::Cell*>> mergeable_groups;
 | 
						|
 | 
						|
	QlBramMergeWorker(RTLIL::Module* module) : module(module)
 | 
						|
	{
 | 
						|
		const RTLIL::IdString split_cell_type = ID($__QLF_TDP36K);
 | 
						|
 | 
						|
		for (RTLIL::Cell* cell : module->selected_cells())
 | 
						|
		{
 | 
						|
			if(cell->type != split_cell_type) continue;
 | 
						|
			if(!cell->hasParam(ID(OPTION_SPLIT))) continue;
 | 
						|
			if(cell->getParam(ID(OPTION_SPLIT)) != RTLIL::Const(1, 32)) continue;
 | 
						|
			mergeable_groups[get_key(cell)].insert(cell);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	static MergeableGroupKeyType get_key(RTLIL::Cell* cell)
 | 
						|
	{
 | 
						|
		MergeableGroupKeyType key;
 | 
						|
		// For now, there are no restrictions on which cells can be merged
 | 
						|
		(void) cell;
 | 
						|
		return key;
 | 
						|
	}
 | 
						|
 | 
						|
	const dict<RTLIL::IdString, RTLIL::IdString>& param_map(bool second)
 | 
						|
	{
 | 
						|
		static const dict<RTLIL::IdString, RTLIL::IdString> bram1_map = {
 | 
						|
			{ ID(INIT),                     ID(INIT1) },
 | 
						|
			{ ID(PORT_A_WIDTH),             ID(PORT_A1_WIDTH) },
 | 
						|
			{ ID(PORT_B_WIDTH),             ID(PORT_B1_WIDTH) },
 | 
						|
			{ ID(PORT_A_WR_BE_WIDTH),       ID(PORT_A1_WR_BE_WIDTH) },
 | 
						|
			{ ID(PORT_B_WR_BE_WIDTH),       ID(PORT_B1_WR_BE_WIDTH) }
 | 
						|
		};
 | 
						|
		static const dict<RTLIL::IdString, RTLIL::IdString> bram2_map = {
 | 
						|
			{ ID(INIT),                     ID(INIT2) },
 | 
						|
			{ ID(PORT_A_WIDTH),             ID(PORT_A2_WIDTH) },
 | 
						|
			{ ID(PORT_B_WIDTH),             ID(PORT_B2_WIDTH) },
 | 
						|
			{ ID(PORT_A_WR_BE_WIDTH),       ID(PORT_A2_WR_BE_WIDTH) },
 | 
						|
			{ ID(PORT_B_WR_BE_WIDTH),       ID(PORT_B2_WR_BE_WIDTH) }
 | 
						|
		};
 | 
						|
 | 
						|
		if(second)
 | 
						|
			return bram2_map;
 | 
						|
		else
 | 
						|
			return bram1_map;
 | 
						|
	}
 | 
						|
 | 
						|
	const dict<RTLIL::IdString, RTLIL::IdString>& port_map(bool second)
 | 
						|
	{
 | 
						|
		static const dict<RTLIL::IdString, RTLIL::IdString> bram1_map = {
 | 
						|
			{ ID(PORT_A_CLK),       ID(PORT_A1_CLK) },
 | 
						|
			{ ID(PORT_B_CLK),       ID(PORT_B1_CLK) },
 | 
						|
			{ ID(PORT_A_CLK_EN),    ID(PORT_A1_CLK_EN) },
 | 
						|
			{ ID(PORT_B_CLK_EN),    ID(PORT_B1_CLK_EN) },
 | 
						|
			{ ID(PORT_A_ADDR),      ID(PORT_A1_ADDR) },
 | 
						|
			{ ID(PORT_B_ADDR),      ID(PORT_B1_ADDR) },
 | 
						|
			{ ID(PORT_A_WR_DATA),   ID(PORT_A1_WR_DATA) },
 | 
						|
			{ ID(PORT_B_WR_DATA),   ID(PORT_B1_WR_DATA) },
 | 
						|
			{ ID(PORT_A_WR_EN),     ID(PORT_A1_WR_EN) },
 | 
						|
			{ ID(PORT_B_WR_EN),     ID(PORT_B1_WR_EN) },
 | 
						|
			{ ID(PORT_A_WR_BE),     ID(PORT_A1_WR_BE) },
 | 
						|
			{ ID(PORT_B_WR_BE),     ID(PORT_B1_WR_BE) },
 | 
						|
			{ ID(PORT_A_RD_DATA),   ID(PORT_A1_RD_DATA) },
 | 
						|
			{ ID(PORT_B_RD_DATA),   ID(PORT_B1_RD_DATA) }
 | 
						|
		};
 | 
						|
		static const dict<RTLIL::IdString, RTLIL::IdString> bram2_map = {
 | 
						|
			{ ID(PORT_A_CLK),       ID(PORT_A2_CLK) },
 | 
						|
			{ ID(PORT_B_CLK),       ID(PORT_B2_CLK) },
 | 
						|
			{ ID(PORT_A_CLK_EN),    ID(PORT_A2_CLK_EN) },
 | 
						|
			{ ID(PORT_B_CLK_EN),    ID(PORT_B2_CLK_EN) },
 | 
						|
			{ ID(PORT_A_ADDR),      ID(PORT_A2_ADDR) },
 | 
						|
			{ ID(PORT_B_ADDR),      ID(PORT_B2_ADDR) },
 | 
						|
			{ ID(PORT_A_WR_DATA),   ID(PORT_A2_WR_DATA) },
 | 
						|
			{ ID(PORT_B_WR_DATA),   ID(PORT_B2_WR_DATA) },
 | 
						|
			{ ID(PORT_A_WR_EN),     ID(PORT_A2_WR_EN) },
 | 
						|
			{ ID(PORT_B_WR_EN),     ID(PORT_B2_WR_EN) },
 | 
						|
			{ ID(PORT_A_WR_BE),     ID(PORT_A2_WR_BE) },
 | 
						|
			{ ID(PORT_B_WR_BE),     ID(PORT_B2_WR_BE) },
 | 
						|
			{ ID(PORT_A_RD_DATA),   ID(PORT_A2_RD_DATA) },
 | 
						|
			{ ID(PORT_B_RD_DATA),   ID(PORT_B2_RD_DATA) }
 | 
						|
		};
 | 
						|
 | 
						|
		if(second)
 | 
						|
			return bram2_map;
 | 
						|
		else
 | 
						|
			return bram1_map;
 | 
						|
	}
 | 
						|
 | 
						|
	void merge_brams(RTLIL::Cell* bram1, RTLIL::Cell* bram2)
 | 
						|
	{
 | 
						|
		const RTLIL::IdString merged_cell_type = ID($__QLF_TDP36K_MERGED);
 | 
						|
 | 
						|
		// Create the new cell
 | 
						|
		RTLIL::Cell* merged = module->addCell(NEW_ID, merged_cell_type);
 | 
						|
		log_debug("Merging split BRAM cells %s and %s -> %s\n", log_id(bram1->name), log_id(bram2->name), log_id(merged->name));
 | 
						|
 | 
						|
		for (auto &it : param_map(false))
 | 
						|
		{
 | 
						|
			if(bram1->hasParam(it.first))
 | 
						|
				merged->setParam(it.second, bram1->getParam(it.first));
 | 
						|
		}
 | 
						|
		for (auto &it : param_map(true))
 | 
						|
		{
 | 
						|
			if(bram2->hasParam(it.first))
 | 
						|
				merged->setParam(it.second, bram2->getParam(it.first));
 | 
						|
		}
 | 
						|
 | 
						|
		for (auto &it : port_map(false))
 | 
						|
		{
 | 
						|
			if (bram1->hasPort(it.first))
 | 
						|
				merged->setPort(it.second, bram1->getPort(it.first));
 | 
						|
			else
 | 
						|
				log_error("Can't find port %s on cell %s!\n", log_id(it.first), log_id(bram1->name));
 | 
						|
		}
 | 
						|
		for (auto &it : port_map(true))
 | 
						|
		{
 | 
						|
			if (bram2->hasPort(it.first))
 | 
						|
				merged->setPort(it.second, bram2->getPort(it.first));
 | 
						|
			else
 | 
						|
				log_error("Can't find port %s on cell %s!\n", log_id(it.first), log_id(bram2->name));
 | 
						|
		}
 | 
						|
		merged->attributes = bram1->attributes;
 | 
						|
		for (auto attr: bram2->attributes)
 | 
						|
			if (!merged->has_attribute(attr.first))
 | 
						|
				merged->attributes.insert(attr);
 | 
						|
 | 
						|
		// Remove the old cells
 | 
						|
		module->remove(bram1);
 | 
						|
		module->remove(bram2);
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	void merge_bram_groups()
 | 
						|
	{
 | 
						|
		for (auto &it : mergeable_groups)
 | 
						|
		{
 | 
						|
			while (it.second.size() > 1)
 | 
						|
			{
 | 
						|
				merge_brams(it.second.pop(), it.second.pop());
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
struct QlBramMergePass : public Pass {
 | 
						|
	
 | 
						|
	QlBramMergePass() : Pass("ql_bram_merge", "Infers QuickLogic k6n10f BRAM pairs that can operate independently") {}
 | 
						|
 | 
						|
	void help() override
 | 
						|
	{
 | 
						|
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | 
						|
		log("\n");
 | 
						|
		log("    ql_bram_merge [selection]\n");
 | 
						|
		log("\n");
 | 
						|
		log("    This pass identifies k6n10f 18K BRAM cells and packs pairs of them together\n");
 | 
						|
		log("    into a TDP36K cell operating in split mode\n");
 | 
						|
		log("\n");
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
	void execute(std::vector<std::string> args, RTLIL::Design *design) override
 | 
						|
	{
 | 
						|
		log_header(design, "Executing QL_BRAM_MERGE pass.\n");
 | 
						|
 | 
						|
		size_t argidx = 1;
 | 
						|
		extra_args(args, argidx, design);
 | 
						|
 | 
						|
		for (RTLIL::Module* module : design->selected_modules())
 | 
						|
		{
 | 
						|
			QlBramMergeWorker worker(module);
 | 
						|
			worker.merge_bram_groups();
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
} QlBramMergePass;
 | 
						|
 | 
						|
PRIVATE_NAMESPACE_END
 |