mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +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)) 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
 |