diff options
Diffstat (limited to 'passes/techmap')
| -rw-r--r-- | passes/techmap/Makefile.inc | 2 | ||||
| -rw-r--r-- | passes/techmap/abc9.cc | 152 | ||||
| -rw-r--r-- | passes/techmap/alumacc.cc | 19 | ||||
| -rw-r--r-- | passes/techmap/attrmap.cc | 187 | ||||
| -rw-r--r-- | passes/techmap/clkbufmap.cc | 298 | ||||
| -rw-r--r-- | passes/techmap/dff2dffs.cc | 29 | ||||
| -rw-r--r-- | passes/techmap/extractinv.cc | 123 | ||||
| -rw-r--r-- | passes/techmap/iopadmap.cc | 58 | ||||
| -rw-r--r-- | passes/techmap/shregmap.cc | 181 | ||||
| -rw-r--r-- | passes/techmap/techmap.cc | 189 | 
10 files changed, 841 insertions, 397 deletions
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 56f05eca4..cd357d72a 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -16,6 +16,7 @@ endif  ifneq ($(SMALL),1)  OBJS += passes/techmap/iopadmap.o +OBJS += passes/techmap/clkbufmap.o  OBJS += passes/techmap/hilomap.o  OBJS += passes/techmap/extract.o  OBJS += passes/techmap/extract_fa.o @@ -39,6 +40,7 @@ OBJS += passes/techmap/attrmap.o  OBJS += passes/techmap/zinit.o  OBJS += passes/techmap/dff2dffs.o  OBJS += passes/techmap/flowmap.o +OBJS += passes/techmap/extractinv.o  endif  GENFILES += passes/techmap/techmap.inc diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index 29929f80b..97d4c5ef3 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -76,8 +76,7 @@ inline std::string remap_name(RTLIL::IdString abc_name)  	return stringf("$abc$%d$%s", map_autoidx, abc_name.c_str()+1);  } -void handle_loops(RTLIL::Design *design, -		const dict<IdString,pool<IdString>> &scc_break_inputs) +void handle_loops(RTLIL::Design *design)  {  	Pass::call(design, "scc -set_attr abc_scc_id {}"); @@ -85,7 +84,7 @@ void handle_loops(RTLIL::Design *design,  	// cell in the component, and select (and mark) all its output  	// wires  	pool<RTLIL::Const> ids_seen; -	for (auto cell : module->selected_cells()) { +	for (auto cell : module->cells()) {  		auto it = cell->attributes.find(ID(abc_scc_id));  		if (it != cell->attributes.end()) {  			auto r = ids_seen.insert(it->second); @@ -114,30 +113,6 @@ void handle_loops(RTLIL::Design *design,  			}  			cell->attributes.erase(it);  		} - -		auto jt = scc_break_inputs.find(cell->type); -		if (jt != scc_break_inputs.end()) -			for (auto port_name : jt->second) { -				RTLIL::SigSpec sig; -				auto &rhs = cell->connections_.at(port_name); -				for (auto b : rhs) { -					Wire *w = b.wire; -					if (!w) continue; -					w->port_output = true; -					w->set_bool_attribute(ID(abc_scc_break)); -					w = module->wire(stringf("%s.abci", w->name.c_str())); -					if (!w) { -						w = module->addWire(stringf("%s.abci", b.wire->name.c_str()), GetSize(b.wire)); -						w->port_input = true; -					} -					else { -						log_assert(b.offset < GetSize(w)); -						log_assert(w->port_input); -					} -					sig.append(RTLIL::SigBit(w, b.offset)); -				} -				rhs = sig; -			}  	}  	module->fixup_ports(); @@ -269,11 +244,10 @@ struct abc_output_filter  };  void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, -		bool cleanup, vector<int> lut_costs, bool /*dff_mode*/, std::string clk_str, +		bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,  		bool /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,  		bool show_tempdir, std::string box_file, std::string lut_file, -		std::string wire_delay, const dict<int,IdString> &box_lookup, -		const dict<IdString,pool<IdString>> &scc_break_inputs +		std::string wire_delay, const dict<int,IdString> &box_lookup  )  {  	module = current_module; @@ -309,8 +283,8 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri  			clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0));  	} -	//if (dff_mode && clk_sig.empty()) -	//	log_cmd_error("Clock domain %s not found.\n", clk_str.c_str()); +	if (dff_mode && clk_sig.empty()) +		log_cmd_error("Clock domain %s not found.\n", clk_str.c_str());  	std::string tempdir_name = "/tmp/yosys-abc-XXXXXX";  	if (!cleanup) @@ -383,7 +357,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri  	fprintf(f, "%s\n", abc_script.c_str());  	fclose(f); -	if (/*dff_mode ||*/ !clk_str.empty()) +	if (dff_mode || !clk_str.empty())  	{  		if (clk_sig.size() == 0)  			log("No%s clock domain found. Not extracting any FF cells.\n", clk_str.empty() ? "" : " matching"); @@ -413,16 +387,13 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri  		RTLIL::Selection& sel = design->selection_stack.back();  		sel.select(module); -		handle_loops(design, scc_break_inputs); +		handle_loops(design);  		Pass::call(design, "aigmap");  		//log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n",  		//		count_gates, GetSize(signal_list), count_input, count_output); -#if 0 -		Pass::call(design, stringf("write_verilog -noexpr -norename %s/before.v", tempdir_name.c_str())); -#endif  		Pass::call(design, stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str()));  		std::string buffer; @@ -531,12 +502,6 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri  				for (int i = 0; i < GetSize(w); i++)  					output_bits.insert({wire, i});  			} - -			auto jt = w->attributes.find("\\init"); -			if (jt != w->attributes.end()) { -				auto r = remap_wire->attributes.insert(std::make_pair("\\init", jt->second)); -				log_assert(r.second); -			}  		}  		for (auto &it : module->connections_) { @@ -578,6 +543,8 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri  			if (mapped_cell->type == ID($_NOT_)) {  				RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A);  				RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y); +				bit_users[a_bit].insert(mapped_cell->name); +				bit_drivers[y_bit].insert(mapped_cell->name);  				if (!a_bit.wire) {  					mapped_cell->setPort(ID::Y, module->addWire(NEW_ID)); @@ -585,8 +552,8 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri  					log_assert(wire);  					module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1);  				} -				else { -					RTLIL::Cell* driving_lut = nullptr; +				else if (!lut_costs.empty() || !lut_file.empty()) { +					RTLIL::Cell* driver_lut = nullptr;  					// ABC can return NOT gates that drive POs  					if (!a_bit.wire->port_input) {  						// If it's not a NOT gate that that comes from a PI directly, @@ -598,10 +565,10 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri  							driver_name = stringf("%s$lut", a_bit.wire->name.c_str());  						else  							driver_name = stringf("%s[%d]$lut", a_bit.wire->name.c_str(), a_bit.offset); -						driving_lut = mapped_mod->cell(driver_name); +						driver_lut = mapped_mod->cell(driver_name);  					} -					if (!driving_lut) { +					if (!driver_lut) {  						// If a driver couldn't be found (could be from PI or box CI)  						// then implement using a LUT  						cell = module->addLut(remap_name(stringf("%s$lut", mapped_cell->name.c_str())), @@ -610,13 +577,13 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri  								RTLIL::Const::from_string("01"));  						bit2sinks[cell->getPort(ID::A)].push_back(cell);  						cell_stats[ID($lut)]++; -						bit_users[a_bit].insert(mapped_cell->name); -						bit_drivers[y_bit].insert(mapped_cell->name);  					}  					else -						not2drivers[mapped_cell] = driving_lut; +						not2drivers[mapped_cell] = driver_lut;  					continue;  				} +				else +					log_abort();  				if (cell && markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;  				continue;  			} @@ -700,32 +667,31 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri  		}  		for (auto &it : cell_stats) -			log("ABC RESULTS:   %15s cells: %8d\n", log_id(it.first), it.second); +			log("ABC RESULTS:   %15s cells: %8d\n", it.first.c_str(), it.second);  		int in_wires = 0, out_wires = 0;  		// Stitch in mapped_mod's inputs/outputs into module -		for (auto port_name : mapped_mod->ports) { -			RTLIL::Wire *port = mapped_mod->wire(port_name); -			log_assert(port); -			RTLIL::Wire *wire = module->wire(port->name); +		for (auto port : mapped_mod->ports) { +			RTLIL::Wire *w = mapped_mod->wire(port); +			RTLIL::Wire *wire = module->wire(port);  			log_assert(wire); -			RTLIL::Wire *remap_wire = module->wire(remap_name(port->name)); +			RTLIL::Wire *remap_wire = module->wire(remap_name(port));  			RTLIL::SigSpec signal = RTLIL::SigSpec(wire, 0, GetSize(remap_wire));  			log_assert(GetSize(signal) >= GetSize(remap_wire));  			RTLIL::SigSig conn; -			if (port->port_input) { -				conn.first = remap_wire; -				conn.second = signal; -				in_wires++; -				module->connect(conn); -			} -			if (port->port_output) { +			if (w->port_output) {  				conn.first = signal;  				conn.second = remap_wire;  				out_wires++;  				module->connect(conn);  			} +			else if (w->port_input) { +				conn.first = remap_wire; +				conn.second = signal; +				in_wires++; +				module->connect(conn); +			}  		}  		for (auto &it : bit_users) @@ -733,21 +699,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri  				for (auto driver_cell : bit_drivers.at(it.first))  				for (auto user_cell : it.second)  					toposort.edge(driver_cell, user_cell); -#if 0 -		toposort.analyze_loops = true; -#endif  		bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); -#if 0 -		unsigned i = 0; -		for (auto &it : toposort.loops) { -			log("  loop %d\n", i++); -			for (auto cell_name : it) { -				auto cell = mapped_mod->cell(cell_name); -				log_assert(cell); -				log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str()); -			} -		} -#endif  		log_assert(no_loops);  		for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) { @@ -1048,7 +1000,7 @@ struct Abc9Pass : public Pass {  				fast_mode = true;  				continue;  			} -			//if (arg == "-retime") { +			//if (arg == "-dff") {  			//	dff_mode = true;  			//	continue;  			//} @@ -1075,9 +1027,6 @@ struct Abc9Pass : public Pass {  			}  			if (arg == "-box" && argidx+1 < args.size()) {  				box_file = args[++argidx]; -				rewrite_filename(box_file); -				if (!box_file.empty() && !is_absolute_path(box_file)) -					box_file = std::string(pwd) + "/" + box_file;  				continue;  			}  			if (arg == "-W" && argidx+1 < args.size()) { @@ -1088,11 +1037,15 @@ struct Abc9Pass : public Pass {  		}  		extra_args(args, argidx, design); -		if (lut_costs.empty() && lut_file.empty()) -			log_cmd_error("abc9 must be called with '-lut' or '-luts'\n"); +		// ABC expects a box file for XAIG +		if (box_file.empty()) +		    box_file = "+/dummy.box"; + +		rewrite_filename(box_file); +		if (!box_file.empty() && !is_absolute_path(box_file)) +		    box_file = std::string(pwd) + "/" + box_file;  		dict<int,IdString> box_lookup; -		dict<IdString,pool<IdString>> scc_break_inputs;  		for (auto m : design->modules()) {  			auto it = m->attributes.find(ID(abc_box_id));  			if (it == m->attributes.end()) @@ -1110,17 +1063,13 @@ struct Abc9Pass : public Pass {  			for (auto p : m->ports) {  				auto w = m->wire(p);  				log_assert(w); -				if (w->port_input) { -					if (w->attributes.count(ID(abc_scc_break))) -						scc_break_inputs[m->name].insert(p); -					if (w->attributes.count(ID(abc_carry))) { +				if (w->attributes.count(ID(abc_carry))) { +					if (w->port_input) {  						if (carry_in)  							log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));  						carry_in = w;  					} -				} -				if (w->port_output) { -					if (w->attributes.count(ID(abc_carry))) { +					else if (w->port_output) {  						if (carry_out)  							log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));  						carry_out = w; @@ -1177,7 +1126,8 @@ struct Abc9Pass : public Pass {  				abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, false, clk_str, keepff,  						delay_target, lutin_shared, fast_mode, show_tempdir, -						box_file, lut_file, wire_delay, box_lookup, scc_break_inputs); +						box_file, lut_file, wire_delay, box_lookup); +  				design->selection_stack.pop_back();  				continue;  			} @@ -1361,36 +1311,20 @@ struct Abc9Pass : public Pass {  						std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)),  						std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first))); -			design->selection_stack.emplace_back(false); -  			for (auto &it : assigned_cells) { -				// FIXME: abc9_module calls below can delete cells, -				//        leaving a dangling pointer here...  				clk_polarity = std::get<0>(it.first);  				clk_sig = assign_map(std::get<1>(it.first));  				en_polarity = std::get<2>(it.first);  				en_sig = assign_map(std::get<3>(it.first)); - -				pool<RTLIL::IdString> assigned_names; -				for (auto i : it.second) -					assigned_names.insert(i->name); -				RTLIL::Selection& sel = design->selection_stack.back(); -				sel.selected_members[mod->name] = std::move(assigned_names); -  				abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$",  						keepff, delay_target, lutin_shared, fast_mode, show_tempdir, -						box_file, lut_file, wire_delay, box_lookup, scc_break_inputs); +						box_file, lut_file, wire_delay, box_lookup);  				assign_map.set(mod);  			} - -			design->selection_stack.pop_back();  		}  		assign_map.clear(); -		// The "clean" pass also contains a design->check() call -		Pass::call(design, "clean"); -  		log_pop();  	}  } Abc9Pass; diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index 5b168d524..034731b87 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -48,14 +48,25 @@ struct AlumaccWorker  		RTLIL::SigSpec cached_cf, cached_of, cached_sf;  		RTLIL::SigSpec get_lt() { -			if (GetSize(cached_lt) == 0) -				cached_lt = is_signed ? alu_cell->module->Xor(NEW_ID, get_of(), get_sf()) : get_cf(); +			if (GetSize(cached_lt) == 0) { +				if (is_signed) { +					get_of(); +					get_sf(); +					cached_lt = alu_cell->module->Xor(NEW_ID, cached_of, cached_sf); +				} +				else +					cached_lt = get_cf(); +			}  			return cached_lt;  		}  		RTLIL::SigSpec get_gt() { -			if (GetSize(cached_gt) == 0) -				cached_gt = alu_cell->module->Not(NEW_ID, alu_cell->module->Or(NEW_ID, get_lt(), get_eq()), false, alu_cell->get_src_attribute()); +			if (GetSize(cached_gt) == 0) { +				get_lt(); +				get_eq(); +				SigSpec Or = alu_cell->module->Or(NEW_ID, cached_lt, cached_eq); +				cached_gt = alu_cell->module->Not(NEW_ID, Or, false, alu_cell->get_src_attribute()); +			}  			return cached_gt;  		} diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc index a38638e0b..5f30817d4 100644 --- a/passes/techmap/attrmap.cc +++ b/passes/techmap/attrmap.cc @@ -143,6 +143,82 @@ void attrmap_apply(string objname, vector<std::unique_ptr<AttrmapAction>> &actio  	attributes.swap(new_attributes);  } +void log_attrmap_paramap_options() +{ +	log("    -tocase <name>\n"); +	log("        Match attribute names case-insensitively and set it to the specified\n"); +	log("        name.\n"); +	log("\n"); +	log("    -rename <old_name> <new_name>\n"); +	log("        Rename attributes as specified\n"); +	log("\n"); +	log("    -map <old_name>=<old_value> <new_name>=<new_value>\n"); +	log("        Map key/value pairs as indicated.\n"); +	log("\n"); +	log("    -imap <old_name>=<old_value> <new_name>=<new_value>\n"); +	log("        Like -map, but use case-insensitive match for <old_value> when\n"); +	log("        it is a string value.\n"); +	log("\n"); +	log("    -remove <name>=<value>\n"); +	log("        Remove attributes matching this pattern.\n"); +} + +bool parse_attrmap_paramap_options(size_t &argidx, std::vector<std::string> &args, vector<std::unique_ptr<AttrmapAction>> &actions) +{ +	std::string arg = args[argidx]; +	if (arg == "-tocase" && argidx+1 < args.size()) { +		auto action = new AttrmapTocase; +		action->name = args[++argidx]; +		actions.push_back(std::unique_ptr<AttrmapAction>(action)); +		return true; +	} +	if (arg == "-rename" && argidx+2 < args.size()) { +		auto action = new AttrmapRename; +		action->old_name = args[++argidx]; +		action->new_name = args[++argidx]; +		actions.push_back(std::unique_ptr<AttrmapAction>(action)); +		return true; +	} +	if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) { +		string arg1 = args[++argidx]; +		string arg2 = args[++argidx]; +		string val1, val2; +		size_t p = arg1.find("="); +		if (p != string::npos) { +			val1 = arg1.substr(p+1); +			arg1 = arg1.substr(0, p); +		} +		p = arg2.find("="); +		if (p != string::npos) { +			val2 = arg2.substr(p+1); +			arg2 = arg2.substr(0, p); +		} +		auto action = new AttrmapMap; +		action->imap = (arg == "-map"); +		action->old_name = arg1; +		action->new_name = arg2; +		action->old_value = val1; +		action->new_value = val2; +		actions.push_back(std::unique_ptr<AttrmapAction>(action)); +		return true; +	} +	if (arg == "-remove" && argidx+1 < args.size()) { +		string arg1 = args[++argidx], val1; +		size_t p = arg1.find("="); +		if (p != string::npos) { +			val1 = arg1.substr(p+1); +			arg1 = arg1.substr(0, p); +		} +		auto action = new AttrmapRemove; +		action->name = arg1; +		action->has_value = (p != string::npos); +		action->value = val1; +		actions.push_back(std::unique_ptr<AttrmapAction>(action)); +		return true; +	} +	return false; +} +  struct AttrmapPass : public Pass {  	AttrmapPass() : Pass("attrmap", "renaming attributes") { }  	void help() YS_OVERRIDE @@ -151,25 +227,10 @@ struct AttrmapPass : public Pass {  		log("\n");  		log("    attrmap [options] [selection]\n");  		log("\n"); -		log("This command renames attributes and/or mapps key/value pairs to\n"); +		log("This command renames attributes and/or maps key/value pairs to\n");  		log("other key/value pairs.\n");  		log("\n"); -		log("    -tocase <name>\n"); -		log("        Match attribute names case-insensitively and set it to the specified\n"); -		log("        name.\n"); -		log("\n"); -		log("    -rename <old_name> <new_name>\n"); -		log("        Rename attributes as specified\n"); -		log("\n"); -		log("    -map <old_name>=<old_value> <new_name>=<new_value>\n"); -		log("        Map key/value pairs as indicated.\n"); -		log("\n"); -		log("    -imap <old_name>=<old_value> <new_name>=<new_value>\n"); -		log("        Like -map, but use case-insensitive match for <old_value> when\n"); -		log("        it is a string value.\n"); -		log("\n"); -		log("    -remove <name>=<value>\n"); -		log("        Remove attributes matching this pattern.\n"); +		log_attrmap_paramap_options();  		log("\n");  		log("    -modattr\n");  		log("        Operate on module attributes instead of attributes on wires and cells.\n"); @@ -190,58 +251,9 @@ struct AttrmapPass : public Pass {  		size_t argidx;  		for (argidx = 1; argidx < args.size(); argidx++)  		{ -			std::string arg = args[argidx]; -			if (arg == "-tocase" && argidx+1 < args.size()) { -				auto action = new AttrmapTocase; -				action->name = args[++argidx]; -				actions.push_back(std::unique_ptr<AttrmapAction>(action)); -				continue; -			} -			if (arg == "-rename" && argidx+2 < args.size()) { -				auto action = new AttrmapRename; -				action->old_name = args[++argidx]; -				action->new_name = args[++argidx]; -				actions.push_back(std::unique_ptr<AttrmapAction>(action)); +			if (parse_attrmap_paramap_options(argidx, args, actions))  				continue; -			} -			if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) { -				string arg1 = args[++argidx]; -				string arg2 = args[++argidx]; -				string val1, val2; -				size_t p = arg1.find("="); -				if (p != string::npos) { -					val1 = arg1.substr(p+1); -					arg1 = arg1.substr(0, p); -				} -				p = arg2.find("="); -				if (p != string::npos) { -					val2 = arg2.substr(p+1); -					arg2 = arg2.substr(0, p); -				} -				auto action = new AttrmapMap; -				action->imap = (arg == "-map"); -				action->old_name = arg1; -				action->new_name = arg2; -				action->old_value = val1; -				action->new_value = val2; -				actions.push_back(std::unique_ptr<AttrmapAction>(action)); -				continue; -			} -			if (arg == "-remove" && argidx+1 < args.size()) { -				string arg1 = args[++argidx], val1; -				size_t p = arg1.find("="); -				if (p != string::npos) { -					val1 = arg1.substr(p+1); -					arg1 = arg1.substr(0, p); -				} -				auto action = new AttrmapRemove; -				action->name = arg1; -				action->has_value = (p != string::npos); -				action->value = val1; -				actions.push_back(std::unique_ptr<AttrmapAction>(action)); -				continue; -			} -			if (arg == "-modattr") { +			if (args[argidx] == "-modattr") {  				modattr_mode = true;  				continue;  			} @@ -287,4 +299,43 @@ struct AttrmapPass : public Pass {  	}  } AttrmapPass; +struct ParamapPass : public Pass { +	ParamapPass() : Pass("paramap", "renaming cell parameters") { } +	void help() YS_OVERRIDE +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    paramap [options] [selection]\n"); +		log("\n"); +		log("This command renames cell parameters and/or maps key/value pairs to\n"); +		log("other key/value pairs.\n"); +		log("\n"); +		log_attrmap_paramap_options(); +		log("\n"); +		log("For example, mapping Diamond-style ECP5 \"init\" attributes to Yosys-style:\n"); +		log("\n"); +		log("    paramap -tocase INIT t:LUT4\n"); +		log("\n"); +	} +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		log_header(design, "Executing PARAMAP pass (move or copy cell parameters).\n"); + +		vector<std::unique_ptr<AttrmapAction>> actions; + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) +		{ +			if (parse_attrmap_paramap_options(argidx, args, actions)) +				continue; +			break; +		} +		extra_args(args, argidx, design); + +		for (auto module : design->selected_modules()) +		for (auto cell : module->selected_cells()) +			attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->parameters); +	} +} ParamapPass; +  PRIVATE_NAMESPACE_END diff --git a/passes/techmap/clkbufmap.cc b/passes/techmap/clkbufmap.cc new file mode 100644 index 000000000..246932d81 --- /dev/null +++ b/passes/techmap/clkbufmap.cc @@ -0,0 +1,298 @@ +/* + *  yosys -- Yosys Open SYnthesis Suite + * + *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> + *  Copyright (C) 2019  Marcin KoĆcielnicki <mwk@0x04.net> + * + *  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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void split_portname_pair(std::string &port1, std::string &port2) +{ +	size_t pos = port1.find_first_of(':'); +	if (pos != std::string::npos) { +		port2 = port1.substr(pos+1); +		port1 = port1.substr(0, pos); +	} +} + +struct ClkbufmapPass : public Pass { +	ClkbufmapPass() : Pass("clkbufmap", "insert global buffers on clock networks") { } +	void help() YS_OVERRIDE +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    clkbufmap [options] [selection]\n"); +		log("\n"); +		log("Inserts global buffers between nets connected to clock inputs and their drivers.\n"); +		log("\n"); +		log("In the absence of any selection, all wires without the 'clkbuf_inhibit'\n"); +		log("attribute will be considered for global buffer insertion.\n"); +		log("Alternatively, to consider all wires without the 'buffer_type' attribute set to\n"); +		log("'none' or 'bufr' one would specify:\n"); +		log("  'w:* a:buffer_type=none a:buffer_type=bufr %%u %%d'\n"); +		log("as the selection.\n"); +		log("\n"); +		log("    -buf <celltype> <portname_out>:<portname_in>\n"); +		log("        Specifies the cell type to use for the global buffers\n"); +		log("        and its port names.  The first port will be connected to\n"); +		log("        the clock network sinks, and the second will be connected\n"); +		log("        to the actual clock source.  This option is required.\n"); +		log("\n"); +		log("    -inpad <celltype> <portname_out>:<portname_in>\n"); +		log("        If specified, a PAD cell of the given type is inserted on\n"); +		log("        clock nets that are also top module's inputs (in addition\n"); +		log("        to the global buffer).\n"); +		log("\n"); +	} + +	void module_queue(Design *design, Module *module, std::vector<Module *> &modules_sorted, pool<Module *> &modules_processed) { +		if (modules_processed.count(module)) +			return; +		for (auto cell : module->cells()) { +			Module *submodule = design->module(cell->type); +			if (!submodule) +				continue; +			module_queue(design, submodule, modules_sorted, modules_processed); +		} +		modules_sorted.push_back(module); +		modules_processed.insert(module); +	} + +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		log_header(design, "Executing CLKBUFMAP pass (inserting global clock buffers).\n"); + +		std::string buf_celltype, buf_portname, buf_portname2; +		std::string inpad_celltype, inpad_portname, inpad_portname2; + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) +		{ +			std::string arg = args[argidx]; +			if (arg == "-buf" && argidx+2 < args.size()) { +				buf_celltype = args[++argidx]; +				buf_portname = args[++argidx]; +				split_portname_pair(buf_portname, buf_portname2); +				continue; +			} +			if (arg == "-inpad" && argidx+2 < args.size()) { +				inpad_celltype = args[++argidx]; +				inpad_portname = args[++argidx]; +				split_portname_pair(inpad_portname, inpad_portname2); +				continue; +			} +			break; +		} + +		bool select = false; +		if (argidx < args.size()) { +			if (args[argidx].compare(0, 1, "-") != 0) +				select = true; +			extra_args(args, argidx, design); +		} + +		if (buf_celltype.empty()) +			log_error("The -buf option is required.\n"); + +		// Cell type, port name, bit index. +		pool<pair<IdString, pair<IdString, int>>> sink_ports; +		pool<pair<IdString, pair<IdString, int>>> buf_ports; + +		// Process submodules before module using them. +		std::vector<Module *> modules_sorted; +		pool<Module *> modules_processed; +		for (auto module : design->selected_modules()) +			module_queue(design, module, modules_sorted, modules_processed); + +		for (auto module : modules_sorted) +		{ +			if (module->get_blackbox_attribute()) { +				for (auto port : module->ports) { +					auto wire = module->wire(port); +					if (wire->get_bool_attribute("\\clkbuf_driver")) +						for (int i = 0; i < GetSize(wire); i++) +							buf_ports.insert(make_pair(module->name, make_pair(wire->name, i))); +					if (wire->get_bool_attribute("\\clkbuf_sink")) +						for (int i = 0; i < GetSize(wire); i++) +							sink_ports.insert(make_pair(module->name, make_pair(wire->name, i))); +				} +				continue; +			} +			pool<SigBit> sink_wire_bits; +			pool<SigBit> buf_wire_bits; +			pool<SigBit> driven_wire_bits; +			SigMap sigmap(module); +			// bit -> (buffer, buffer's input) +			dict<SigBit, pair<Cell *, Wire *>> buffered_bits; + +			// First, collect nets that could use a clock buffer. +			for (auto cell : module->cells()) +			for (auto port : cell->connections()) +			for (int i = 0; i < port.second.size(); i++) +				if (sink_ports.count(make_pair(cell->type, make_pair(port.first, i)))) +					sink_wire_bits.insert(sigmap(port.second[i])); + +			// Second, collect ones that already have a clock buffer. +			for (auto cell : module->cells()) +			for (auto port : cell->connections()) +			for (int i = 0; i < port.second.size(); i++) +				if (buf_ports.count(make_pair(cell->type, make_pair(port.first, i)))) +					buf_wire_bits.insert(sigmap(port.second[i])); + +			// Collect all driven bits. +			for (auto cell : module->cells()) +			for (auto port : cell->connections()) +				if (cell->output(port.first)) +					for (int i = 0; i < port.second.size(); i++) +						driven_wire_bits.insert(port.second[i]); + +			// Insert buffers. +			std::vector<pair<Wire *, Wire *>> input_queue; +			// Copy current wire list, as we will be adding new ones during iteration. +			std::vector<Wire *> wires(module->wires()); +			for (auto wire : wires) +			{ +				// Should not happen. +				if (wire->port_input && wire->port_output) +					continue; +				bool process_wire = module->selected(wire); +				if (!select && wire->get_bool_attribute("\\clkbuf_inhibit")) +					process_wire = false; +				if (!process_wire) { +					// This wire is supposed to be bypassed, so make sure we don't buffer it in +					// some buffer higher up in the hierarchy. +					if (wire->port_output) +						for (int i = 0; i < GetSize(wire); i++) +							buf_ports.insert(make_pair(module->name, make_pair(wire->name, i))); +					continue; +				} + +				pool<int> input_bits; + +				for (int i = 0; i < GetSize(wire); i++) +				{ +					SigBit wire_bit(wire, i); +					SigBit mapped_wire_bit = sigmap(wire_bit); +					if (buf_wire_bits.count(mapped_wire_bit)) { +						// Already buffered downstream.  If this is an output, mark it. +						if (wire->port_output) +							buf_ports.insert(make_pair(module->name, make_pair(wire->name, i))); +					} else if (!sink_wire_bits.count(mapped_wire_bit)) { +						// Nothing to do. +					} else if (driven_wire_bits.count(wire_bit) || (wire->port_input && module->get_bool_attribute("\\top"))) { +						// Clock network not yet buffered, driven by one of +						// our cells or a top-level input -- buffer it. + +						log("Inserting %s on %s.%s[%d].\n", buf_celltype.c_str(), log_id(module), log_id(wire), i); +						RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(buf_celltype)); +						Wire *iwire = module->addWire(NEW_ID); +						cell->setPort(RTLIL::escape_id(buf_portname), mapped_wire_bit); +						cell->setPort(RTLIL::escape_id(buf_portname2), iwire); +						if (wire->port_input && !inpad_celltype.empty() && module->get_bool_attribute("\\top")) { +							log("Inserting %s on %s.%s[%d].\n", inpad_celltype.c_str(), log_id(module), log_id(wire), i); +							RTLIL::Cell *cell2 = module->addCell(NEW_ID, RTLIL::escape_id(inpad_celltype)); +							cell2->setPort(RTLIL::escape_id(inpad_portname), iwire); +							iwire = module->addWire(NEW_ID); +							cell2->setPort(RTLIL::escape_id(inpad_portname2), iwire); +						} +						buffered_bits[mapped_wire_bit] = make_pair(cell, iwire); + +						if (wire->port_input) { +							input_bits.insert(i); +						} +					} else if (wire->port_input) { +						// A clock input in a submodule -- mark it, let higher level +						// worry about it. +						if (wire->port_input) +							sink_ports.insert(make_pair(module->name, make_pair(wire->name, i))); +					} +				} +				if (!input_bits.empty()) { +					// This is an input port and some buffers were inserted -- we need +					// to create a new input wire and transfer attributes. +					Wire *new_wire = module->addWire(NEW_ID, wire); + +					for (int i = 0; i < wire->width; i++) { +						SigBit wire_bit(wire, i); +						SigBit mapped_wire_bit = sigmap(wire_bit); +						auto it = buffered_bits.find(mapped_wire_bit); +						if (it != buffered_bits.end()) { + +							module->connect(it->second.second, SigSpec(new_wire, i)); +						} else { +							module->connect(SigSpec(wire, i), SigSpec(new_wire, i)); +						} +					} +					input_queue.push_back(make_pair(wire, new_wire)); +				} +			} + +			// Mark any newly-buffered output ports as such. +			for (auto wire : module->selected_wires()) { +				if (wire->port_input || !wire->port_output) +					continue; +				for (int i = 0; i < GetSize(wire); i++) +				{ +					SigBit wire_bit(wire, i); +					SigBit mapped_wire_bit = sigmap(wire_bit); +					if (buffered_bits.count(mapped_wire_bit)) +						buf_ports.insert(make_pair(module->name, make_pair(wire->name, i))); +				} +			} + +			// Reconnect the drivers to buffer inputs. +			for (auto cell : module->cells()) +			for (auto port : cell->connections()) { +				if (!cell->output(port.first)) +					continue; +				SigSpec sig = port.second; +				bool newsig = false; +				for (auto &bit : sig) { +					const auto it = buffered_bits.find(sigmap(bit)); +					if (it == buffered_bits.end()) +						continue; +					// Avoid substituting buffer's own output pin. +					if (cell == it->second.first) +						continue; +					bit = it->second.second; +					newsig = true; +				} +				if (newsig) +					cell->setPort(port.first, sig); +			} + +			// This has to be done last, to avoid upsetting sigmap before the port reconnections. +			for (auto &it : input_queue) { +				Wire *wire = it.first; +				Wire *new_wire = it.second; +				module->swap_names(new_wire, wire); +				wire->attributes.clear(); +				wire->port_id = 0; +				wire->port_input = false; +				wire->port_output = false; +			} + +			module->fixup_ports(); +		} +	} +} ClkbufmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dff2dffs.cc b/passes/techmap/dff2dffs.cc index 0ea033513..3fa1ed5cf 100644 --- a/passes/techmap/dff2dffs.cc +++ b/passes/techmap/dff2dffs.cc @@ -34,11 +34,16 @@ struct Dff2dffsPass : public Pass {  		log("Merge synchronous set/reset $_MUX_ cells to create $__DFFS_[NP][NP][01], to be run before\n");  		log("dff2dffe for SR over CE priority.\n");  		log("\n"); +		log("    -match-init\n"); +		log("        Disallow merging synchronous set/reset that has polarity opposite of the\n"); +		log("        output wire's init attribute (if any).\n"); +		log("\n");  	}  	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE  	{  		log_header(design, "Executing dff2dffs pass (merge synchronous set/reset into FF cells).\n"); +		bool match_init = false;  		size_t argidx;  		for (argidx = 1; argidx < args.size(); argidx++)  		{ @@ -46,6 +51,10 @@ struct Dff2dffsPass : public Pass {  			// 	singleton_mode = true;  			// 	continue;  			// } +			if (args[argidx] == "-match-init") { +				match_init = true; +				continue; +			}  			break;  		}  		extra_args(args, argidx, design); @@ -96,9 +105,6 @@ struct Dff2dffsPass : public Pass {  				SigBit bit_b = sigmap(mux_cell->getPort(ID::B));  				SigBit bit_s = sigmap(mux_cell->getPort(ID(S))); -				log("  Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell), -						log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type)); -  				SigBit sr_val, sr_sig;  				bool invert_sr;  				sr_sig = bit_s; @@ -113,6 +119,23 @@ struct Dff2dffsPass : public Pass {  					invert_sr = false;  				} +				if (match_init) { +					SigBit bit_q = cell->getPort(ID(Q)); +					if (bit_q.wire) { +						auto it = bit_q.wire->attributes.find(ID(init)); +						if (it != bit_q.wire->attributes.end()) { +							auto init_val = it->second[bit_q.offset]; +							if (init_val == State::S1 && sr_val != State::S1) +								continue; +							if (init_val == State::S0 && sr_val != State::S0) +								continue; +						} +					} +				} + +				log("  Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell), +						log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type)); +  				if (sr_val == State::S1) {  					if (cell->type == ID($_DFF_N_)) {  						if (invert_sr) cell->type = ID($__DFFS_NN1_); diff --git a/passes/techmap/extractinv.cc b/passes/techmap/extractinv.cc new file mode 100644 index 000000000..dda71f12a --- /dev/null +++ b/passes/techmap/extractinv.cc @@ -0,0 +1,123 @@ +/* + *  yosys -- Yosys Open SYnthesis Suite + * + *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> + *  Copyright (C) 2019  Marcin KoĆcielnicki <mwk@0x04.net> + * + *  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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void split_portname_pair(std::string &port1, std::string &port2) +{ +	size_t pos = port1.find_first_of(':'); +	if (pos != std::string::npos) { +		port2 = port1.substr(pos+1); +		port1 = port1.substr(0, pos); +	} +} + +struct ExtractinvPass : public Pass { +	ExtractinvPass() : Pass("extractinv", "extract explicit inverter cells for invertible cell pins") { } +	void help() YS_OVERRIDE +	{ +		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +		log("\n"); +		log("    extractinv [options] [selection]\n"); +		log("\n"); +		log("Searches the design for all cells with invertible pins controlled by a cell\n"); +		log("parameter (eg. IS_CLK_INVERTED on many Xilinx cells) and removes the parameter.\n"); +		log("If the parameter was set to 1, inserts an explicit inverter cell in front of\n"); +		log("the pin instead.  Normally used for output to ISE, which does not support the\n"); +		log("inversion parameters.\n"); +		log("\n"); +		log("To mark a cell port as invertible, use (* invertible_pin = \"param_name\" *)\n"); +		log("on the wire in the blackbox module.  The parameter value should have\n"); +		log("the same width as the port, and will be effectively XORed with it.\n"); +		log("\n"); +		log("    -inv <celltype> <portname_out>:<portname_in>\n"); +		log("        Specifies the cell type to use for the inverters and its port names.\n"); +		log("        This option is required.\n"); +		log("\n"); +	} + +	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE +	{ +		log_header(design, "Executing EXTRACTINV pass (extracting pin inverters).\n"); + +		std::string inv_celltype, inv_portname, inv_portname2; + +		size_t argidx; +		for (argidx = 1; argidx < args.size(); argidx++) +		{ +			std::string arg = args[argidx]; +			if (arg == "-inv" && argidx+2 < args.size()) { +				inv_celltype = args[++argidx]; +				inv_portname = args[++argidx]; +				split_portname_pair(inv_portname, inv_portname2); +				continue; +			} +			break; +		} +		extra_args(args, argidx, design); + +		if (inv_celltype.empty()) +			log_error("The -inv option is required.\n"); + +		for (auto module : design->selected_modules()) +		{ +			for (auto cell : module->selected_cells()) +			for (auto port : cell->connections()) { +				auto cell_module = design->module(cell->type); +				if (!cell_module) +					continue; +				auto cell_wire = cell_module->wire(port.first); +				if (!cell_wire) +					continue; +				auto it = cell_wire->attributes.find("\\invertible_pin"); +				if (it == cell_wire->attributes.end()) +					continue; +				IdString param_name = RTLIL::escape_id(it->second.decode_string()); +				auto it2 = cell->parameters.find(param_name); +				// Inversion not used -- skip. +				if (it2 == cell->parameters.end()) +					continue; +				SigSpec sig = port.second; +				if (it2->second.size() != sig.size()) +					log_error("The inversion parameter needs to be the same width as the port (%s.%s port %s parameter %s)", log_id(module->name), log_id(cell->type), log_id(port.first), log_id(param_name)); +				RTLIL::Const invmask = it2->second; +				cell->parameters.erase(param_name); +				if (invmask.is_fully_zero()) +					continue; +				Wire *iwire = module->addWire(NEW_ID, sig.size()); +				for (int i = 0; i < sig.size(); i++) +					if (invmask[i] == State::S1) { +						RTLIL::Cell *icell = module->addCell(NEW_ID, RTLIL::escape_id(inv_celltype)); +						icell->setPort(RTLIL::escape_id(inv_portname), SigSpec(iwire, i)); +						icell->setPort(RTLIL::escape_id(inv_portname2), sig[i]); +						log("Inserting %s on %s.%s.%s[%d].\n", inv_celltype.c_str(), log_id(module), log_id(cell->type), log_id(port.first), i); +						sig[i] = SigBit(iwire, i); +					} +				cell->setPort(port.first, sig); +			} +		} +	} +} ExtractinvPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index a2551316f..c868b9a87 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -64,6 +64,11 @@ struct IopadmapPass : public Pass {  		log("        of the tristate driver and the 2nd portname is the internal output\n");  		log("        buffering the external signal.\n");  		log("\n"); +		log("    -ignore <celltype> <portname>[:<portname>]*\n"); +		log("        Skips mapping inputs/outputs that are already connected to given\n"); +		log("        ports of the given cell.  Can be used multiple times.  This is in\n"); +		log("        addition to the cells specified as mapping targets.\n"); +		log("\n");  		log("    -widthparam <param_name>\n");  		log("        Use the specified parameter name to set the port width.\n");  		log("\n"); @@ -88,6 +93,7 @@ struct IopadmapPass : public Pass {  		std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3;  		std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4;  		std::string widthparam, nameparam; +		pool<pair<IdString, IdString>> ignore;  		bool flag_bits = false;  		size_t argidx; @@ -127,6 +133,18 @@ struct IopadmapPass : public Pass {  				split_portname_pair(tinoutpad_portname3, tinoutpad_portname4);  				continue;  			} +			if (arg == "-ignore" && argidx+2 < args.size()) { +				std::string ignore_celltype = args[++argidx]; +				std::string ignore_portname = args[++argidx]; +				std::string ignore_portname2; +				while (!ignore_portname.empty()) { +					split_portname_pair(ignore_portname, ignore_portname2); +					ignore.insert(make_pair(RTLIL::escape_id(ignore_celltype), RTLIL::escape_id(ignore_portname))); + +					ignore_portname = ignore_portname2; +				} +				continue; +			}  			if (arg == "-widthparam" && argidx+1 < args.size()) {  				widthparam = args[++argidx];  				continue; @@ -143,6 +161,23 @@ struct IopadmapPass : public Pass {  		}  		extra_args(args, argidx, design); +		if (!inpad_portname2.empty()) +			ignore.insert(make_pair(RTLIL::escape_id(inpad_celltype), RTLIL::escape_id(inpad_portname2))); +		if (!outpad_portname2.empty()) +			ignore.insert(make_pair(RTLIL::escape_id(outpad_celltype), RTLIL::escape_id(outpad_portname2))); +		if (!inoutpad_portname2.empty()) +			ignore.insert(make_pair(RTLIL::escape_id(inoutpad_celltype), RTLIL::escape_id(inoutpad_portname2))); +		if (!toutpad_portname3.empty()) +			ignore.insert(make_pair(RTLIL::escape_id(toutpad_celltype), RTLIL::escape_id(toutpad_portname3))); +		if (!tinoutpad_portname4.empty()) +			ignore.insert(make_pair(RTLIL::escape_id(tinoutpad_celltype), RTLIL::escape_id(tinoutpad_portname4))); + +		for (auto module : design->modules()) +			if (module->get_blackbox_attribute()) +				for (auto wire : module->wires()) +					if (wire->get_bool_attribute("\\iopad_external_pin")) +						ignore.insert(make_pair(module->name, wire->name)); +  		for (auto module : design->selected_modules())  		{  			dict<IdString, pool<int>> skip_wires; @@ -150,28 +185,11 @@ struct IopadmapPass : public Pass {  			SigMap sigmap(module);  			for (auto cell : module->cells()) -			{ -				if (cell->type == RTLIL::escape_id(inpad_celltype) && cell->hasPort(RTLIL::escape_id(inpad_portname2))) -					for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(inpad_portname2)))) -						skip_wire_bits.insert(bit); - -				if (cell->type == RTLIL::escape_id(outpad_celltype) && cell->hasPort(RTLIL::escape_id(outpad_portname2))) -					for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(outpad_portname2)))) +			for (auto port : cell->connections()) +				if (ignore.count(make_pair(cell->type, port.first))) +					for (auto bit : sigmap(port.second))  						skip_wire_bits.insert(bit); -				if (cell->type == RTLIL::escape_id(inoutpad_celltype) && cell->hasPort(RTLIL::escape_id(inoutpad_portname2))) -					for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(inoutpad_portname2)))) -						skip_wire_bits.insert(bit); - -				if (cell->type == RTLIL::escape_id(toutpad_celltype) && cell->hasPort(RTLIL::escape_id(toutpad_portname3))) -					for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(toutpad_portname3)))) -						skip_wire_bits.insert(bit); - -				if (cell->type == RTLIL::escape_id(tinoutpad_celltype) && cell->hasPort(RTLIL::escape_id(tinoutpad_portname4))) -					for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(tinoutpad_portname4)))) -						skip_wire_bits.insert(bit); -			} -  			if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty())  			{  				dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits; diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc index 5e298d8dd..be00e5030 100644 --- a/passes/techmap/shregmap.cc +++ b/passes/techmap/shregmap.cc @@ -26,9 +26,7 @@ PRIVATE_NAMESPACE_BEGIN  struct ShregmapTech  {  	virtual ~ShregmapTech() { } -	virtual void init(const Module * /*module*/, const SigMap &/*sigmap*/) {} -	virtual void non_chain_user(const SigBit &/*bit*/, const Cell* /*cell*/, IdString /*port*/) {} -	virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) = 0; +	virtual bool analyze(vector<int> &taps) = 0;  	virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;  }; @@ -56,7 +54,7 @@ struct ShregmapOptions  struct ShregmapTechGreenpak4 : ShregmapTech  { -	bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/) +	bool analyze(vector<int> &taps)  	{  		if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {  			taps.clear(); @@ -93,155 +91,6 @@ struct ShregmapTechGreenpak4 : ShregmapTech  	}  }; -struct ShregmapTechXilinx7 : ShregmapTech -{ -	dict<SigBit, std::tuple<Cell*,int,int>> sigbit_to_shiftx_offset; -	const ShregmapOptions &opts; - -	ShregmapTechXilinx7(const ShregmapOptions &opts) : opts(opts) {} - -	virtual void init(const Module* module, const SigMap &sigmap) override -	{ -		for (const auto &i : module->cells_) { -			auto cell = i.second; -			if (cell->type == ID($shiftx)) { -				if (cell->getParam(ID(Y_WIDTH)) != 1) continue; -				int j = 0; -				for (auto bit : sigmap(cell->getPort(ID::A))) -					sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j++, 0); -				log_assert(j == cell->getParam(ID(A_WIDTH)).as_int()); -			} -			else if (cell->type == ID($mux)) { -				int j = 0; -				for (auto bit : sigmap(cell->getPort(ID::A))) -					sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++); -				j = 0; -				for (auto bit : sigmap(cell->getPort(ID::B))) -					sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 1, j++); -			} -		} -	} - -	virtual void non_chain_user(const SigBit &bit, const Cell *cell, IdString port) override -	{ -		auto it = sigbit_to_shiftx_offset.find(bit); -		if (it == sigbit_to_shiftx_offset.end()) -			return; -		if (cell) { -			if (cell->type == ID($shiftx) && port == ID::A) -				return; -			if (cell->type == ID($mux) && port.in(ID::A, ID::B)) -				return; -		} -		sigbit_to_shiftx_offset.erase(it); -	} - -	virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) override -	{ -		if (GetSize(taps) == 1) -			return taps[0] >= opts.minlen-1 && sigbit_to_shiftx_offset.count(qbits[0]); - -		if (taps.back() < opts.minlen-1) -			return false; - -		Cell *shiftx = nullptr; -		int group = 0; -		for (int i = 0; i < GetSize(taps); ++i) { -			auto it = sigbit_to_shiftx_offset.find(qbits[i]); -			if (it == sigbit_to_shiftx_offset.end()) -				return false; - -			// Check taps are sequential -			if (i != taps[i]) -				return false; -			// Check taps are not connected to a shift register, -			// or sequential to the same shift register -			if (i == 0) { -				int offset; -				std::tie(shiftx,offset,group) = it->second; -				if (offset != i) -					return false; -			} -			else { -				Cell *shiftx_ = std::get<0>(it->second); -				if (shiftx_ != shiftx) -					return false; -				int offset = std::get<1>(it->second); -				if (offset != i) -					return false; -				int group_ = std::get<2>(it->second); -				if (group_ != group) -					return false; -			} -		} -		log_assert(shiftx); - -		// Only map if $shiftx exclusively covers the shift register -		if (shiftx->type == ID($shiftx)) { -			if (GetSize(taps) > shiftx->getParam(ID(A_WIDTH)).as_int()) -				return false; -			// Due to padding the most significant bits of A may be 1'bx, -			//   and if so, discount them -			if (GetSize(taps) < shiftx->getParam(ID(A_WIDTH)).as_int()) { -				const SigSpec A = shiftx->getPort(ID::A); -				const int A_width = shiftx->getParam(ID(A_WIDTH)).as_int(); -				for (int i = GetSize(taps); i < A_width; ++i) -					if (A[i] != RTLIL::Sx) return false; -			} -			else if (GetSize(taps) != shiftx->getParam(ID(A_WIDTH)).as_int()) -				return false; -		} -		else if (shiftx->type == ID($mux)) { -			if (GetSize(taps) != 2) -				return false; -		} -		else log_abort(); - -		return true; -	} - -	virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) override -	{ -		const auto &tap = *taps.begin(); -		auto bit = tap.second; - -		auto it = sigbit_to_shiftx_offset.find(bit); -		log_assert(it != sigbit_to_shiftx_offset.end()); - -		auto newcell = cell->module->addCell(NEW_ID, ID($__XILINX_SHREG_)); -		newcell->set_src_attribute(cell->get_src_attribute()); -		newcell->setParam(ID(DEPTH), cell->getParam(ID(DEPTH))); -		newcell->setParam(ID(INIT), cell->getParam(ID(INIT))); -		newcell->setParam(ID(CLKPOL), cell->getParam(ID(CLKPOL))); -		newcell->setParam(ID(ENPOL), cell->getParam(ID(ENPOL))); - -		newcell->setPort(ID(C), cell->getPort(ID(C))); -		newcell->setPort(ID(D), cell->getPort(ID(D))); -		if (cell->hasPort(ID(E))) -			newcell->setPort(ID(E), cell->getPort(ID(E))); - -		Cell* shiftx = std::get<0>(it->second); -		RTLIL::SigSpec l_wire, q_wire; -		if (shiftx->type == ID($shiftx)) { -			l_wire = shiftx->getPort(ID::B); -			q_wire = shiftx->getPort(ID::Y); -			shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID)); -		} -		else if (shiftx->type == ID($mux)) { -			l_wire = shiftx->getPort(ID(S)); -			q_wire = shiftx->getPort(ID::Y); -			shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID)); -		} -		else log_abort(); - -		newcell->setPort(ID(Q), q_wire); -		newcell->setPort(ID(L), l_wire); - -		return false; -	} -}; - -  struct ShregmapWorker  {  	Module *module; @@ -264,10 +113,8 @@ struct ShregmapWorker  		for (auto wire : module->wires())  		{  			if (wire->port_output || wire->get_bool_attribute(ID::keep)) { -				for (auto bit : sigmap(wire)) { +				for (auto bit : sigmap(wire))  					sigbit_with_non_chain_users.insert(bit); -					if (opts.tech) opts.tech->non_chain_user(bit, nullptr, {}); -				}  			}  			if (wire->attributes.count(ID(init))) { @@ -317,10 +164,8 @@ struct ShregmapWorker  			for (auto conn : cell->connections())  				if (cell->input(conn.first)) -					for (auto bit : sigmap(conn.second)) { +					for (auto bit : sigmap(conn.second))  						sigbit_with_non_chain_users.insert(bit); -						if (opts.tech) opts.tech->non_chain_user(bit, cell, conn.first); -					}  		}  	} @@ -346,7 +191,7 @@ struct ShregmapWorker  				IdString q_port = opts.ffcells.at(c1->type).second;  				auto c1_conn = c1->connections(); -				auto c2_conn = c1->connections(); +				auto c2_conn = c2->connections();  				c1_conn.erase(d_port);  				c1_conn.erase(q_port); @@ -425,7 +270,7 @@ struct ShregmapWorker  					if (taps.empty() || taps.back() < depth-1)  						taps.push_back(depth-1); -					if (opts.tech->analyze(taps, qbits)) +					if (opts.tech->analyze(taps))  						break;  					taps.pop_back(); @@ -544,9 +389,6 @@ struct ShregmapWorker  	ShregmapWorker(Module *module, const ShregmapOptions &opts) :  			module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)  	{ -		if (opts.tech) -			opts.tech->init(module, sigmap); -  		make_sigbit_chain_next_prev();  		find_chain_start_cells(); @@ -617,11 +459,6 @@ struct ShregmapPass : public Pass {  		log("\n");  		log("    -tech greenpak4\n");  		log("        map to greenpak4 shift registers.\n"); -		log("        this option also implies -clkpol pos -zinit\n"); -		log("\n"); -		log("    -tech xilinx\n"); -		log("        map to xilinx dynamic-length shift registers.\n"); -		log("        this option also implies -params -init\n");  		log("\n");  	}  	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -676,12 +513,6 @@ struct ShregmapPass : public Pass {  					clkpol = "pos";  					opts.zinit = true;  					opts.tech = new ShregmapTechGreenpak4; -				} -				else if (tech == "xilinx") { -					opts.init = true; -					opts.params = true; -					enpol = "any_or_none"; -					opts.tech = new ShregmapTechXilinx7(opts);  				} else {  					argidx--;  					break; diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index b271c8781..08a1af2d5 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -205,20 +205,57 @@ struct TechmapWorker  		}  		std::map<RTLIL::IdString, RTLIL::IdString> positional_ports; +		dict<Wire*, IdString> temp_renamed_wires; +		pool<SigBit> autopurge_tpl_bits; -		for (auto &it : tpl->wires_) { +		for (auto &it : tpl->wires_) +		{  			if (it.second->port_id > 0) -				positional_ports[stringf("$%d", it.second->port_id)] = it.first; +			{ +				IdString posportname = stringf("$%d", it.second->port_id); +				positional_ports[posportname] = it.first; + +				if (!flatten_mode && it.second->get_bool_attribute(ID(techmap_autopurge)) && +						(!cell->hasPort(it.second->name) || !GetSize(cell->getPort(it.second->name))) && +						(!cell->hasPort(posportname) || !GetSize(cell->getPort(posportname)))) +				{ +					if (sigmaps.count(tpl) == 0) +						sigmaps[tpl].set(tpl); + +					for (auto bit : sigmaps.at(tpl)(it.second)) +						if (bit.wire != nullptr) +							autopurge_tpl_bits.insert(bit); +				} +			}  			IdString w_name = it.second->name;  			apply_prefix(cell->name, w_name); -			RTLIL::Wire *w = module->addWire(w_name, it.second); -			w->port_input = false; -			w->port_output = false; -			w->port_id = 0; -			if (it.second->get_bool_attribute(ID(_techmap_special_))) -				w->attributes.clear(); -			if (w->attributes.count(ID(src))) -				w->add_strpool_attribute(ID(src), extra_src_attrs); +			RTLIL::Wire *w = module->wire(w_name); +			if (w != nullptr) { +				if (!flatten_mode || !w->get_bool_attribute(ID(hierconn))) { +					temp_renamed_wires[w] = w->name; +					module->rename(w, NEW_ID); +					w = nullptr; +				} else { +					w->attributes.erase(ID(hierconn)); +					if (GetSize(w) < GetSize(it.second)) { +						log_warning("Widening signal %s.%s to match size of %s.%s (via %s.%s).\n", log_id(module), log_id(w), +								log_id(tpl), log_id(it.second), log_id(module), log_id(cell)); +						w->width = GetSize(it.second); +					} +				} +			} +			if (w == nullptr) { +				w = module->addWire(w_name, it.second); +				w->port_input = false; +				w->port_output = false; +				w->port_id = 0; +				if (!flatten_mode) +					w->attributes.erase(ID(techmap_autopurge)); +				if (it.second->get_bool_attribute(ID(_techmap_special_))) +					w->attributes.clear(); +				if (w->attributes.count(ID(src))) +					w->add_strpool_attribute(ID(src), extra_src_attrs); +			}  			design->select(module, w);  		} @@ -322,6 +359,12 @@ struct TechmapWorker  				for (auto &attr : w->attributes) {  					if (attr.first == ID(src))  						continue; +					auto lhs = GetSize(extra_connect.first); +					auto rhs = GetSize(extra_connect.second); +					if (lhs > rhs) +						extra_connect.first.remove(rhs, lhs-rhs); +					else if (rhs > lhs) +						extra_connect.second.remove(lhs, rhs-lhs);  					module->connect(extra_connect);  					break;  				} @@ -344,11 +387,31 @@ struct TechmapWorker  			if (!flatten_mode && c->type.begins_with("\\$"))  				c->type = c->type.substr(1); -			for (auto &it2 : c->connections_) { -				apply_prefix(cell->name, it2.second, module); -				port_signal_map.apply(it2.second); +			vector<IdString> autopurge_ports; + +			for (auto &it2 : c->connections_) +			{ +				bool autopurge = false; +				if (!autopurge_tpl_bits.empty()) { +					autopurge = GetSize(it2.second) != 0; +					for (auto &bit : sigmaps.at(tpl)(it2.second)) +						if (!autopurge_tpl_bits.count(bit)) { +							autopurge = false; +							break; +						} +				} + +				if (autopurge) { +					autopurge_ports.push_back(it2.first); +				} else { +					apply_prefix(cell->name, it2.second, module); +					port_signal_map.apply(it2.second); +				}  			} +			for (auto &it2 : autopurge_ports) +				c->unsetPort(it2); +  			if (c->type.in(ID($memrd), ID($memwr), ID($meminit))) {  				IdString memid = c->getParam(ID(MEMID)).decode_string();  				log_assert(memory_renames.count(memid) != 0); @@ -380,6 +443,16 @@ struct TechmapWorker  		}  		module->remove(cell); + +		for (auto &it : temp_renamed_wires) +		{ +			Wire *w = it.first; +			IdString name = it.second; +			IdString altname = module->uniquify(name); +			Wire *other_w = module->wire(name); +			module->rename(other_w, altname); +			module->rename(w, name); +		}  	}  	bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells, @@ -396,6 +469,18 @@ struct TechmapWorker  		SigMap sigmap(module); +		dict<SigBit, State> init_bits; +		pool<SigBit> remove_init_bits; + +		for (auto wire : module->wires()) { +			if (wire->attributes.count("\\init")) { +				Const value = wire->attributes.at("\\init"); +				for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) +					if (value[i] != State::Sx) +						init_bits[sigmap(SigBit(wire, i))] = value[i]; +			} +		} +  		TopoSort<RTLIL::Cell*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell>> cells;  		std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit;  		std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell; @@ -633,6 +718,17 @@ struct TechmapWorker  									bit = RTLIL::SigBit(RTLIL::State::Sx);  							parameters[stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const();  						} +						if (tpl->avail_parameters.count(stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))) != 0) { +							auto sig = sigmap(conn.second); +							RTLIL::Const value(State::Sx, sig.size()); +							for (int i = 0; i < sig.size(); i++) { +								auto it = init_bits.find(sig[i]); +								if (it != init_bits.end()) { +									value[i] = it->second; +								} +							} +							parameters[stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))] = value; +						}  					}  					int unique_bit_id_counter = 0; @@ -833,7 +929,7 @@ struct TechmapWorker  					TechmapWires twd = techmap_find_special_wires(tpl);  					for (auto &it : twd) { -						if (it.first != "_TECHMAP_FAIL_" && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_") +						if (it.first != "_TECHMAP_FAIL_" && (it.first.substr(0, 20) != "_TECHMAP_REMOVEINIT_" || it.first[it.first.size()-1] != '_') && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_")  							log_error("Techmap yielded unknown config wire %s.\n", it.first.c_str());  						if (techmap_do_cache[tpl])  							for (auto &it2 : it.second) @@ -864,6 +960,23 @@ struct TechmapWorker  					mkdebug.off();  				} +				TechmapWires twd = techmap_find_special_wires(tpl); +				for (auto &it : twd) { +					if (it.first.substr(0, 20) == "_TECHMAP_REMOVEINIT_") { +						for (auto &it2 : it.second) { +							auto val = it2.value.as_const(); +							auto wirename = RTLIL::escape_id(it.first.substr(20, it.first.size() - 20 - 1)); +							auto it = cell->connections().find(wirename); +							if (it != cell->connections().end()) { +								auto sig = sigmap(it->second); +								for (int i = 0; i < sig.size(); i++) +									if (val[i] == State::S1) +										remove_init_bits.insert(sig[i]); +							} +						} +					} +				} +  				if (extern_mode && !in_recursion)  				{  					std::string m_name = stringf("$extern:%s", log_id(tpl)); @@ -907,6 +1020,25 @@ struct TechmapWorker  			handled_cells.insert(cell);  		} +		if (!remove_init_bits.empty()) { +			for (auto wire : module->wires()) +				if (wire->attributes.count("\\init")) { +					Const &value = wire->attributes.at("\\init"); +					bool do_cleanup = true; +					for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) { +						SigBit bit = sigmap(SigBit(wire, i)); +						if (remove_init_bits.count(bit)) +							value[i] = State::Sx; +						else if (value[i] != State::Sx) +							do_cleanup = false; +					} +					if (do_cleanup) { +						log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire)); +						wire->attributes.erase("\\init"); +					} +				} +		} +  		if (log_continue) {  			log_header(design, "Continuing TECHMAP pass.\n");  			log_continue = false; @@ -943,7 +1075,8 @@ struct TechmapPass : public Pass {  		log("        instead of inlining them.\n");  		log("\n");  		log("    -max_iter <number>\n"); -		log("        only run the specified number of iterations.\n"); +		log("        only run the specified number of iterations on each module.\n"); +		log("        default: unlimited\n");  		log("\n");  		log("    -recursive\n");  		log("        instead of the iterative breadth-first algorithm use a recursive\n"); @@ -980,6 +1113,11 @@ struct TechmapPass : public Pass {  		log("will create a wrapper for the cell and then run the command string that the\n");  		log("attribute is set to on the wrapper module.\n");  		log("\n"); +		log("When a port on a module in the map file has the 'techmap_autopurge' attribute\n"); +		log("set, and that port is not connected in the instantiation that is mapped, then\n"); +		log("then a cell port connected only to such wires will be omitted in the mapped\n"); +		log("version of the circuit.\n"); +		log("\n");  		log("All wires in the modules from the map file matching the pattern _TECHMAP_*\n");  		log("or *._TECHMAP_* are special wires that are used to pass instructions from\n");  		log("the mapping module to the techmap command. At the moment the following special\n"); @@ -1018,6 +1156,13 @@ struct TechmapPass : public Pass {  		log("\n");  		log("        It is possible to combine both prefixes to 'RECURSION; CONSTMAP; '.\n");  		log("\n"); +		log("    _TECHMAP_REMOVEINIT_<port-name>_\n"); +		log("        When this wire is set to a constant value, the init attribute of the wire(s)\n"); +		log("        connected to this port will be consumed.  This wire must have the same\n"); +		log("        width as the given port, and for every bit that is set to 1 in the value,\n"); +		log("        the corresponding init attribute bit will be changed to 1'bx.  If all\n"); +		log("        bits of an init attribute are left as x, it will be removed.\n"); +		log("\n");  		log("In addition to this special wires, techmap also supports special parameters in\n");  		log("modules in the map file:\n");  		log("\n"); @@ -1031,6 +1176,13 @@ struct TechmapPass : public Pass {  		log("        former has a 1-bit for each constant input bit and the latter has the\n");  		log("        value for this bit. The unused bits of the latter are set to undef (x).\n");  		log("\n"); +		log("    _TECHMAP_WIREINIT_<port-name>_\n"); +		log("        When a parameter with this name exists, it will be set to the initial\n"); +		log("        value of the wire(s) connected to the given port, as specified by the init\n"); +		log("        attribute. If the attribute doesn't exist, x will be filled for the\n"); +		log("        missing bits.  To remove the init attribute bits used, use the\n"); +		log("        _TECHMAP_REMOVEINIT_*_ wires.\n"); +		log("\n");  		log("    _TECHMAP_BITS_CONNMAP_\n");  		log("    _TECHMAP_CONNMAP_<port-name>_\n");  		log("        For an N-bit port, the _TECHMAP_CONNMAP_<port-name>_ parameter, if it\n"); @@ -1157,15 +1309,16 @@ struct TechmapPass : public Pass {  			RTLIL::Module *module = *worker.module_queue.begin();  			worker.module_queue.erase(module); +			int module_max_iter = max_iter;  			bool did_something = true;  			std::set<RTLIL::Cell*> handled_cells;  			while (did_something) {  				did_something = false; -					if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false)) -						did_something = true; +				if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false)) +					did_something = true;  				if (did_something)  					module->check(); -				if (max_iter > 0 && --max_iter == 0) +				if (module_max_iter > 0 && --module_max_iter == 0)  					break;  			}  		}  | 
