mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-30 19:22:31 +00:00 
			
		
		
		
	Merge branch 'master' of github.com:YosysHQ/yosys into clifford/fix968
This commit is contained in:
		
						commit
						d187be39d6
					
				
					 35 changed files with 788 additions and 291 deletions
				
			
		|  | @ -163,31 +163,61 @@ struct FirrtlWorker | |||
| 		} | ||||
| 	}; | ||||
| 	/* Memories defined within this module. */ | ||||
| 	 struct memory { | ||||
| 		 string name;					// memory name
 | ||||
| 		 int abits;						// number of address bits
 | ||||
| 		 int size;						// size (in units) of the memory
 | ||||
| 		 int width;						// size (in bits) of each element
 | ||||
| 		 int read_latency; | ||||
| 		 int write_latency; | ||||
| 		 vector<read_port> read_ports; | ||||
| 		 vector<write_port> write_ports; | ||||
| 		 std::string init_file; | ||||
| 		 std::string init_file_srcFileSpec; | ||||
| 		 memory(string name, int abits, int size, int width) : name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") {} | ||||
| 		 memory() : read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){} | ||||
| 		 void add_memory_read_port(read_port &rp) { | ||||
| 			 read_ports.push_back(rp); | ||||
| 		 } | ||||
| 		 void add_memory_write_port(write_port &wp) { | ||||
| 			 write_ports.push_back(wp); | ||||
| 		 } | ||||
| 		 void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) { | ||||
| 			 this->init_file = init_file; | ||||
| 			 this->init_file_srcFileSpec = init_file_srcFileSpec; | ||||
| 	struct memory { | ||||
| 		Cell *pCell;					// for error reporting
 | ||||
| 		string name;					// memory name
 | ||||
| 		int abits;						// number of address bits
 | ||||
| 		int size;						// size (in units) of the memory
 | ||||
| 		int width;						// size (in bits) of each element
 | ||||
| 		int read_latency; | ||||
| 		int write_latency; | ||||
| 		vector<read_port> read_ports; | ||||
| 		vector<write_port> write_ports; | ||||
| 		std::string init_file; | ||||
| 		std::string init_file_srcFileSpec; | ||||
| 		string srcLine; | ||||
| 		memory(Cell *pCell, string name, int abits, int size, int width) : pCell(pCell), name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") { | ||||
| 			// Provide defaults for abits or size if one (but not the other) is specified.
 | ||||
| 			if (this->abits == 0 && this->size != 0) { | ||||
| 				this->abits = ceil_log2(this->size); | ||||
| 			} else if (this->abits != 0 && this->size == 0) { | ||||
| 				this->size = 1 << this->abits; | ||||
| 			} | ||||
| 			// Sanity-check this construction.
 | ||||
| 			if (this->name == "") { | ||||
| 				log_error("Nameless memory%s\n", this->atLine()); | ||||
| 			} | ||||
| 			if (this->abits == 0 && this->size == 0) { | ||||
| 				log_error("Memory %s has zero address bits and size%s\n", this->name.c_str(), this->atLine()); | ||||
| 			} | ||||
| 			if (this->width == 0) { | ||||
| 				log_error("Memory %s has zero width%s\n", this->name.c_str(), this->atLine()); | ||||
| 			} | ||||
| 		 } | ||||
| 		// We need a default constructor for the dict insert.
 | ||||
| 	   memory() : pCell(0), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){} | ||||
| 
 | ||||
| 	 }; | ||||
| 		const char *atLine() { | ||||
| 			if (srcLine == "") { | ||||
| 				if (pCell) { | ||||
| 					auto p = pCell->attributes.find("\\src"); | ||||
| 					srcLine = " at " + p->second.decode_string(); | ||||
| 				} | ||||
| 			} | ||||
| 			return srcLine.c_str(); | ||||
| 		} | ||||
| 		void add_memory_read_port(read_port &rp) { | ||||
| 			read_ports.push_back(rp); | ||||
| 		} | ||||
| 		void add_memory_write_port(write_port &wp) { | ||||
| 			write_ports.push_back(wp); | ||||
| 		} | ||||
| 		void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) { | ||||
| 			this->init_file = init_file; | ||||
| 			this->init_file_srcFileSpec = init_file_srcFileSpec; | ||||
| 		} | ||||
| 
 | ||||
| 	}; | ||||
| 	dict<string, memory> memories; | ||||
| 
 | ||||
| 	void register_memory(memory &m) | ||||
|  | @ -604,7 +634,7 @@ struct FirrtlWorker | |||
| 				int abits = cell->parameters.at("\\ABITS").as_int(); | ||||
| 				int width = cell->parameters.at("\\WIDTH").as_int(); | ||||
| 				int size = cell->parameters.at("\\SIZE").as_int(); | ||||
| 				memory m(mem_id, abits, size, width); | ||||
| 				memory m(cell, mem_id, abits, size, width); | ||||
| 				int rd_ports = cell->parameters.at("\\RD_PORTS").as_int(); | ||||
| 				int wr_ports = cell->parameters.at("\\WR_PORTS").as_int(); | ||||
| 
 | ||||
|  | @ -681,6 +711,8 @@ struct FirrtlWorker | |||
| 			{ | ||||
| 				std::string cell_type = fid(cell->type); | ||||
| 				std::string mem_id = make_id(cell->parameters["\\MEMID"].decode_string()); | ||||
| 				int abits = cell->parameters.at("\\ABITS").as_int(); | ||||
| 				int width = cell->parameters.at("\\WIDTH").as_int(); | ||||
| 				memory *mp = nullptr; | ||||
| 				if (cell->type == "$meminit" ) { | ||||
| 					log_error("$meminit (%s.%s.%s) currently unsupported\n", log_id(module), log_id(cell), mem_id.c_str()); | ||||
|  | @ -693,6 +725,11 @@ struct FirrtlWorker | |||
| 					Const clk_enable = cell->parameters.at("\\CLK_ENABLE"); | ||||
| 					Const clk_polarity = cell->parameters.at("\\CLK_POLARITY"); | ||||
| 
 | ||||
| 					// Do we already have an entry for this memory?
 | ||||
| 					if (memories.count(mem_id) == 0) { | ||||
| 						memory m(cell, mem_id, abits, 0, width); | ||||
| 						register_memory(m); | ||||
| 					} | ||||
| 					mp = &memories.at(mem_id); | ||||
| 					int portNum = 0; | ||||
| 					bool transparency = false; | ||||
|  | @ -890,7 +927,7 @@ struct FirrtlWorker | |||
| 
 | ||||
| 		// If we have any memory definitions, output them.
 | ||||
| 		for (auto kv : memories) { | ||||
| 			memory m = kv.second; | ||||
| 			memory &m = kv.second; | ||||
| 			f << stringf("    mem %s:\n", m.name.c_str()); | ||||
| 			f << stringf("      data-type => UInt<%d>\n", m.width); | ||||
| 			f << stringf("      depth => %d\n", m.size); | ||||
|  |  | |||
|  | @ -645,6 +645,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun | |||
| 			if (!id_ast->children[0]->range_valid) | ||||
| 				log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", str.c_str()); | ||||
| 			this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1; | ||||
| 			if (children.size() > 1) | ||||
| 				range = children[1]; | ||||
| 		} else | ||||
| 			log_file_error(filename, linenum, "Failed to detect width for identifier %s!\n", str.c_str()); | ||||
| 		if (range) { | ||||
|  |  | |||
|  | @ -1605,6 +1605,7 @@ skip_dynamic_range_lvalue_expansion:; | |||
| 			current_scope[wire_tmp->str] = wire_tmp; | ||||
| 			wire_tmp->attributes["\\nosync"] = AstNode::mkconst_int(1, false); | ||||
| 			while (wire_tmp->simplify(true, false, false, 1, -1, false, false)) { } | ||||
| 			wire_tmp->is_logic = true; | ||||
| 
 | ||||
| 			AstNode *wire_tmp_id = new AstNode(AST_IDENTIFIER); | ||||
| 			wire_tmp_id->str = wire_tmp->str; | ||||
|  |  | |||
|  | @ -45,7 +45,7 @@ YOSYS_NAMESPACE_END | |||
| USING_YOSYS_NAMESPACE | ||||
| %} | ||||
| 
 | ||||
| %name-prefix "rtlil_frontend_ilang_yy" | ||||
| %define api.prefix {rtlil_frontend_ilang_yy} | ||||
| 
 | ||||
| %union { | ||||
| 	char *string; | ||||
|  |  | |||
|  | @ -206,7 +206,9 @@ YOSYS_NAMESPACE_END | |||
| "const"      { if (formal_mode) return TOK_CONST; SV_KEYWORD(TOK_CONST); } | ||||
| "checker"    { if (formal_mode) return TOK_CHECKER; SV_KEYWORD(TOK_CHECKER); } | ||||
| "endchecker" { if (formal_mode) return TOK_ENDCHECKER; SV_KEYWORD(TOK_ENDCHECKER); } | ||||
| "final"      { SV_KEYWORD(TOK_FINAL); } | ||||
| "logic"      { SV_KEYWORD(TOK_LOGIC); } | ||||
| "var"        { SV_KEYWORD(TOK_VAR); } | ||||
| "bit"        { SV_KEYWORD(TOK_REG); } | ||||
| 
 | ||||
| "eventually"   { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } | ||||
|  |  | |||
|  | @ -96,7 +96,7 @@ static void free_attr(std::map<std::string, AstNode*> *al) | |||
| 
 | ||||
| %} | ||||
| 
 | ||||
| %name-prefix "frontend_verilog_yy" | ||||
| %define api.prefix {frontend_verilog_yy} | ||||
| 
 | ||||
| %union { | ||||
| 	std::string *string; | ||||
|  | @ -106,11 +106,11 @@ static void free_attr(std::map<std::string, AstNode*> *al) | |||
| } | ||||
| 
 | ||||
| %token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE TOK_SVA_LABEL | ||||
| %token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER | ||||
| %token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER TOK_FINAL | ||||
| %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END | ||||
| %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM | ||||
| %token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP | ||||
| %token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT | ||||
| %token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR | ||||
| %token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG TOK_LOGIC | ||||
| %token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL | ||||
| %token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT | ||||
|  | @ -456,6 +456,9 @@ wire_type_token: | |||
| 	TOK_LOGIC { | ||||
| 		astbuf3->is_logic = true; | ||||
| 	} | | ||||
| 	TOK_VAR { | ||||
| 		astbuf3->is_logic = true; | ||||
| 	} | | ||||
| 	TOK_INTEGER { | ||||
| 		astbuf3->is_reg = true; | ||||
| 		astbuf3->range_left = 31; | ||||
|  | @ -1341,6 +1344,9 @@ opt_property: | |||
| 	TOK_PROPERTY { | ||||
| 		$$ = true; | ||||
| 	} | | ||||
| 	TOK_FINAL { | ||||
| 		$$ = false; | ||||
| 	} | | ||||
| 	/* empty */ { | ||||
| 		$$ = false; | ||||
| 	}; | ||||
|  |  | |||
|  | @ -291,7 +291,7 @@ struct QwpWorker | |||
| 		// gaussian elimination
 | ||||
| 		for (int i = 0; i < N; i++) | ||||
| 		{ | ||||
| 			if (config.verbose && ((i+1) % (N/15)) == 0) | ||||
| 			if (config.verbose && N > 15 && ((i+1) % (N/15)) == 0) | ||||
| 				log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N); | ||||
| 
 | ||||
| 			// find best row
 | ||||
|  |  | |||
|  | @ -272,6 +272,10 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	SigPool raw_used_signals_noaliases; | ||||
| 	for (auto &it : module->connections_) | ||||
| 		raw_used_signals_noaliases.add(it.second); | ||||
| 
 | ||||
| 	module->connections_.clear(); | ||||
| 
 | ||||
| 	SigPool used_signals; | ||||
|  | @ -281,6 +285,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos | |||
| 		for (auto &it2 : cell->connections_) { | ||||
| 			assign_map.apply(it2.second); | ||||
| 			used_signals.add(it2.second); | ||||
| 			raw_used_signals_noaliases.add(it2.second); | ||||
| 			if (!ct_all.cell_output(cell->type, it2.first)) | ||||
| 				used_signals_nodrivers.add(it2.second); | ||||
| 		} | ||||
|  | @ -301,53 +306,63 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	std::vector<RTLIL::Wire*> maybe_del_wires; | ||||
| 	pool<RTLIL::Wire*> del_wires_queue; | ||||
| 	for (auto wire : module->wires()) | ||||
| 	{ | ||||
| 		if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep") || wire->attributes.count("\\init")) { | ||||
| 			RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1; | ||||
| 			assign_map.apply(s2); | ||||
| 			if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) { | ||||
| 				maybe_del_wires.push_back(wire); | ||||
| 			} else { | ||||
| 				log_assert(GetSize(s1) == GetSize(s2)); | ||||
| 				Const initval; | ||||
| 				if (wire->attributes.count("\\init")) | ||||
| 					initval = wire->attributes.at("\\init"); | ||||
| 				if (GetSize(initval) != GetSize(wire)) | ||||
| 					initval.bits.resize(GetSize(wire), State::Sx); | ||||
| 				RTLIL::SigSig new_conn; | ||||
| 				for (int i = 0; i < GetSize(s1); i++) | ||||
| 					if (s1[i] != s2[i]) { | ||||
| 						if (s2[i] == State::Sx && (initval[i] == State::S0 || initval[i] == State::S1)) { | ||||
| 							s2[i] = initval[i]; | ||||
| 							initval[i] = State::Sx; | ||||
| 						} | ||||
| 						new_conn.first.append_bit(s1[i]); | ||||
| 						new_conn.second.append_bit(s2[i]); | ||||
| 					} | ||||
| 				if (new_conn.first.size() > 0) { | ||||
| 					if (initval.is_fully_undef()) | ||||
| 						wire->attributes.erase("\\init"); | ||||
| 					else | ||||
| 						wire->attributes.at("\\init") = initval; | ||||
| 					used_signals.add(new_conn.first); | ||||
| 					used_signals.add(new_conn.second); | ||||
| 					module->connect(new_conn); | ||||
| 				} | ||||
| 			} | ||||
| 		SigSpec s1 = SigSpec(wire), s2 = assign_map(s1); | ||||
| 		log_assert(GetSize(s1) == GetSize(s2)); | ||||
| 
 | ||||
| 		Const initval; | ||||
| 		if (wire->attributes.count("\\init")) | ||||
| 			initval = wire->attributes.at("\\init"); | ||||
| 		if (GetSize(initval) != GetSize(wire)) | ||||
| 			initval.bits.resize(GetSize(wire), State::Sx); | ||||
| 		if (initval.is_fully_undef()) | ||||
| 			wire->attributes.erase("\\init"); | ||||
| 
 | ||||
| 		bool delete_this_wire = false; | ||||
| 		if (wire->port_id != 0 || wire->get_bool_attribute("\\keep") || !initval.is_fully_undef()) { | ||||
| 			/* do not delete anything with "keep" or module ports or initialized wires */ | ||||
| 		} else | ||||
| 		if (!purge_mode && check_public_name(wire->name)) { | ||||
| 			/* do not get rid of public names unless in purge mode */ | ||||
| 		} else { | ||||
| 			if (!used_signals.check_any(RTLIL::SigSpec(wire))) | ||||
| 				maybe_del_wires.push_back(wire); | ||||
| 			if (!raw_used_signals_noaliases.check_any(s1)) | ||||
| 				delete_this_wire = true; | ||||
| 			if (!used_signals_nodrivers.check_any(s2)) | ||||
| 				delete_this_wire = true; | ||||
| 		} | ||||
| 
 | ||||
| 		RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire)); | ||||
| 		if (!used_signals_nodrivers.check_any(sig)) { | ||||
| 		if (delete_this_wire) { | ||||
| 			del_wires_queue.insert(wire); | ||||
| 		} else { | ||||
| 			RTLIL::SigSig new_conn; | ||||
| 			for (int i = 0; i < GetSize(s1); i++) | ||||
| 				if (s1[i] != s2[i]) { | ||||
| 					if (s2[i] == State::Sx && (initval[i] == State::S0 || initval[i] == State::S1)) { | ||||
| 						s2[i] = initval[i]; | ||||
| 						initval[i] = State::Sx; | ||||
| 					} | ||||
| 					new_conn.first.append_bit(s1[i]); | ||||
| 					new_conn.second.append_bit(s2[i]); | ||||
| 				} | ||||
| 			if (new_conn.first.size() > 0) { | ||||
| 				if (initval.is_fully_undef()) | ||||
| 					wire->attributes.erase("\\init"); | ||||
| 				else | ||||
| 					wire->attributes.at("\\init") = initval; | ||||
| 				used_signals.add(new_conn.first); | ||||
| 				used_signals.add(new_conn.second); | ||||
| 				module->connect(new_conn); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (!used_signals_nodrivers.check_all(s2)) { | ||||
| 			std::string unused_bits; | ||||
| 			for (int i = 0; i < GetSize(sig); i++) { | ||||
| 				if (sig[i].wire == NULL) | ||||
| 			for (int i = 0; i < GetSize(s2); i++) { | ||||
| 				if (s2[i].wire == NULL) | ||||
| 					continue; | ||||
| 				if (!used_signals_nodrivers.check(sig[i])) { | ||||
| 				if (!used_signals_nodrivers.check(s2[i])) { | ||||
| 					if (!unused_bits.empty()) | ||||
| 						unused_bits += " "; | ||||
| 					unused_bits += stringf("%d", i); | ||||
|  | @ -362,24 +377,19 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	int del_temp_wires_count = 0; | ||||
| 	for (auto wire : del_wires_queue) { | ||||
| 		if (ys_debug() || (check_public_name(wire->name) && verbose)) | ||||
| 			log_debug("  removing unused non-port wire %s.\n", wire->name.c_str()); | ||||
| 		else | ||||
| 			del_temp_wires_count++; | ||||
| 	} | ||||
| 
 | ||||
| 	pool<RTLIL::Wire*> del_wires; | ||||
| 	module->remove(del_wires_queue); | ||||
| 	count_rm_wires += GetSize(del_wires_queue); | ||||
| 
 | ||||
| 	int del_wires_count = 0; | ||||
| 	for (auto wire : maybe_del_wires) | ||||
| 		if (!used_signals.check_any(RTLIL::SigSpec(wire))) { | ||||
| 			if (check_public_name(wire->name) && verbose) { | ||||
| 				log_debug("  removing unused non-port wire %s.\n", wire->name.c_str()); | ||||
| 			} | ||||
| 			del_wires.insert(wire); | ||||
| 			del_wires_count++; | ||||
| 		} | ||||
| 
 | ||||
| 	module->remove(del_wires); | ||||
| 	count_rm_wires += del_wires.size(); | ||||
| 
 | ||||
| 	if (verbose && del_wires_count > 0) | ||||
| 		log_debug("  removed %d unused temporary wires.\n", del_wires_count); | ||||
| 	if (verbose && del_temp_wires_count) | ||||
| 		log_debug("  removed %d unused temporary wires.\n", del_temp_wires_count); | ||||
| } | ||||
| 
 | ||||
| bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose) | ||||
|  | @ -526,6 +536,9 @@ struct OptCleanPass : public Pass { | |||
| 
 | ||||
| 		ct_all.setup(design); | ||||
| 
 | ||||
| 		count_rm_cells = 0; | ||||
| 		count_rm_wires = 0; | ||||
| 
 | ||||
| 		for (auto module : design->selected_whole_modules_warn()) { | ||||
| 			if (module->has_processes_warn()) | ||||
| 				continue; | ||||
|  | @ -591,7 +604,7 @@ struct CleanPass : public Pass { | |||
| 		for (auto module : design->selected_whole_modules()) { | ||||
| 			if (module->has_processes()) | ||||
| 				continue; | ||||
| 			rmunused_module(module, purge_mode, false, false); | ||||
| 			rmunused_module(module, purge_mode, ys_debug(), false); | ||||
| 		} | ||||
| 
 | ||||
| 		log_suppressed(); | ||||
|  |  | |||
|  | @ -61,7 +61,7 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) | |||
| 		} | ||||
| 		if (wire->port_input) | ||||
| 			driven_signals.add(sigmap(wire)); | ||||
| 		if (wire->port_output) | ||||
| 		if (wire->port_output || wire->get_bool_attribute("\\keep")) | ||||
| 			used_signals.add(sigmap(wire)); | ||||
| 		all_signals.add(sigmap(wire)); | ||||
| 	} | ||||
|  | @ -88,7 +88,7 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		log_debug("Setting undriven signal in %s to constant: %s = %s\n", RTLIL::id2cstr(module->name), log_signal(sig), log_signal(val)); | ||||
| 		log_debug("Setting undriven signal in %s to constant: %s = %s\n", log_id(module), log_signal(sig), log_signal(val)); | ||||
| 		module->connect(sig, val); | ||||
| 		did_something = true; | ||||
| 	} | ||||
|  | @ -104,10 +104,15 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) | |||
| 				if (SigBit(initval[i]) == sig[i]) | ||||
| 					initval[i] = State::Sx; | ||||
| 			} | ||||
| 			if (initval.is_fully_undef()) | ||||
| 			if (initval.is_fully_undef()) { | ||||
| 				log_debug("Removing init attribute from %s/%s.\n", log_id(module), log_id(wire)); | ||||
| 				wire->attributes.erase("\\init"); | ||||
| 			else | ||||
| 				did_something = true; | ||||
| 			} else if (initval != wire->attributes.at("\\init")) { | ||||
| 				log_debug("Updating init attribute on %s/%s: %s\n", log_id(module), log_id(wire), log_signal(initval)); | ||||
| 				wire->attributes["\\init"] = initval; | ||||
| 				did_something = true; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -531,6 +531,42 @@ struct WreducePass : public Pass { | |||
| 						module->connect(sig, Const(0, GetSize(sig))); | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				if (c->type.in("$div", "$mod", "$pow")) | ||||
| 				{ | ||||
| 					SigSpec A = c->getPort("\\A"); | ||||
| 					int original_a_width = GetSize(A); | ||||
| 					if (c->getParam("\\A_SIGNED").as_bool()) { | ||||
| 						while (GetSize(A) > 1 && A[GetSize(A)-1] == State::S0 && A[GetSize(A)-2] == State::S0) | ||||
| 							A.remove(GetSize(A)-1, 1); | ||||
| 					} else { | ||||
| 						while (GetSize(A) > 0 && A[GetSize(A)-1] == State::S0) | ||||
| 							A.remove(GetSize(A)-1, 1); | ||||
| 					} | ||||
| 					if (original_a_width != GetSize(A)) { | ||||
| 						log("Removed top %d bits (of %d) from port A of cell %s.%s (%s).\n", | ||||
| 								original_a_width-GetSize(A), original_a_width, log_id(module), log_id(c), log_id(c->type)); | ||||
| 						c->setPort("\\A", A); | ||||
| 						c->setParam("\\A_WIDTH", GetSize(A)); | ||||
| 					} | ||||
| 
 | ||||
| 					SigSpec B = c->getPort("\\B"); | ||||
| 					int original_b_width = GetSize(B); | ||||
| 					if (c->getParam("\\B_SIGNED").as_bool()) { | ||||
| 						while (GetSize(B) > 1 && B[GetSize(B)-1] == State::S0 && B[GetSize(B)-2] == State::S0) | ||||
| 							B.remove(GetSize(B)-1, 1); | ||||
| 					} else { | ||||
| 						while (GetSize(B) > 0 && B[GetSize(B)-1] == State::S0) | ||||
| 							B.remove(GetSize(B)-1, 1); | ||||
| 					} | ||||
| 					if (original_b_width != GetSize(B)) { | ||||
| 						log("Removed top %d bits (of %d) from port B of cell %s.%s (%s).\n", | ||||
| 								original_b_width-GetSize(B), original_b_width, log_id(module), log_id(c), log_id(c->type)); | ||||
| 						c->setPort("\\B", B); | ||||
| 						c->setParam("\\B_WIDTH", GetSize(B)); | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				if (!opt_memx && c->type.in("$memrd", "$memwr", "$meminit")) { | ||||
| 					IdString memid = c->getParam("\\MEMID").decode_string(); | ||||
| 					RTLIL::Memory *mem = module->memories.at(memid); | ||||
|  |  | |||
							
								
								
									
										1
									
								
								passes/pmgen/.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								passes/pmgen/.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1 +1,2 @@ | |||
| /ice40_dsp_pm.h | ||||
| /peepopt_pm.h | ||||
|  |  | |||
|  | @ -1,8 +1,23 @@ | |||
| OBJS += passes/pmgen/ice40_dsp.o | ||||
| OBJS += passes/pmgen/peepopt.o | ||||
| 
 | ||||
| # --------------------------------------
 | ||||
| 
 | ||||
| passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h | ||||
| EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h | ||||
| .SECONDARY: passes/pmgen/ice40_dsp_pm.h | ||||
| 
 | ||||
| passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg | ||||
| 	$(P) mkdir -p passes/pmgen && python3 $^ $@ | ||||
| 	$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p ice40_dsp $(filter-out $<,$^) | ||||
| 
 | ||||
| # --------------------------------------
 | ||||
| 
 | ||||
| passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h | ||||
| EXTRA_OBJS += passes/pmgen/peepopt_pm.h | ||||
| .SECONDARY: passes/pmgen/peepopt_pm.h | ||||
| 
 | ||||
| PEEPOPT_PATTERN  = passes/pmgen/peepopt_shiftmul.pmg | ||||
| PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg | ||||
| 
 | ||||
| passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) | ||||
| 	$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^) | ||||
|  |  | |||
|  | @ -29,19 +29,25 @@ up in any future matches: | |||
| 
 | ||||
|     pm.blacklist(some_cell); | ||||
| 
 | ||||
| The `.run(callback_function)` method searches for all matches and calls the | ||||
| callback function for each found match: | ||||
| The `.run_<pattern_name>(callback_function)` method searches for all matches | ||||
| for the pattern`<pattern_name>` and calls the callback function for each found | ||||
| match: | ||||
| 
 | ||||
|     pm.run([&](){ | ||||
|     pm.run_foobar([&](){ | ||||
|         log("found matching 'foo' cell: %s\n", log_id(pm.st.foo)); | ||||
|         log("          with 'bar' cell: %s\n", log_id(pm.st.bar)); | ||||
|     }); | ||||
| 
 | ||||
| The `.pmg` file declares matcher state variables that are accessible via the | ||||
| `.st.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.) | ||||
| `.st_<pattern_name>.<state_name>` members. (The `.st_<pattern_name>` member is | ||||
| of type `foobar_pm::state_<pattern_name>_t`.) | ||||
| 
 | ||||
| Similarly the `.pmg` file declares user data variables that become members of | ||||
| `.ud`, a struct of type `foobar_pm::udata_t`. | ||||
| `.ud_<pattern_name>`, a struct of type `foobar_pm::udata_<pattern_name>_t`. | ||||
| 
 | ||||
| There are four versions of the `run_<pattern_name>()` method: Without callback, | ||||
| callback without arguments, callback with reference to `pm`, and callback with | ||||
| reference to `pm.st_<pattern_name>`. | ||||
| 
 | ||||
| 
 | ||||
| The .pmg File Format | ||||
|  | @ -52,6 +58,12 @@ lines consist of whitespace-separated tokens. | |||
| 
 | ||||
| Lines in `.pmg` files starting with `//` are comments. | ||||
| 
 | ||||
| Declaring a pattern | ||||
| ------------------- | ||||
| 
 | ||||
| A `.pmg` file contains one or more patterns. Each pattern starts with a line | ||||
| with the `pattern` keyword followed by the name of the pattern. | ||||
| 
 | ||||
| Declaring state variables | ||||
| ------------------------- | ||||
| 
 | ||||
|  | @ -66,7 +78,7 @@ State variables are automatically managed by the generated backtracking algorith | |||
| and saved and restored as needed. | ||||
| 
 | ||||
| They are automatically initialized to the default constructed value of their type | ||||
| when `.run(callback_function)` is called. | ||||
| when `.run_<pattern_name>(callback_function)` is called. | ||||
| 
 | ||||
| Declaring udata variables | ||||
| ------------------------- | ||||
|  |  | |||
|  | @ -19,47 +19,50 @@ | |||
| 
 | ||||
| #include "kernel/yosys.h" | ||||
| #include "kernel/sigtools.h" | ||||
| #include "passes/pmgen/ice40_dsp_pm.h" | ||||
| 
 | ||||
| USING_YOSYS_NAMESPACE | ||||
| PRIVATE_NAMESPACE_BEGIN | ||||
| 
 | ||||
| #include "passes/pmgen/ice40_dsp_pm.h" | ||||
| 
 | ||||
| void create_ice40_dsp(ice40_dsp_pm &pm) | ||||
| { | ||||
| 	auto &st = pm.st_ice40_dsp; | ||||
| 
 | ||||
| #if 0 | ||||
| 	log("\n"); | ||||
| 	log("ffA:   %s\n", log_id(pm.st.ffA, "--")); | ||||
| 	log("ffB:   %s\n", log_id(pm.st.ffB, "--")); | ||||
| 	log("mul:   %s\n", log_id(pm.st.mul, "--")); | ||||
| 	log("ffY:   %s\n", log_id(pm.st.ffY, "--")); | ||||
| 	log("addAB: %s\n", log_id(pm.st.addAB, "--")); | ||||
| 	log("muxAB: %s\n", log_id(pm.st.muxAB, "--")); | ||||
| 	log("ffS:   %s\n", log_id(pm.st.ffS, "--")); | ||||
| 	log("ffA:   %s\n", log_id(st.ffA, "--")); | ||||
| 	log("ffB:   %s\n", log_id(st.ffB, "--")); | ||||
| 	log("mul:   %s\n", log_id(st.mul, "--")); | ||||
| 	log("ffY:   %s\n", log_id(st.ffY, "--")); | ||||
| 	log("addAB: %s\n", log_id(st.addAB, "--")); | ||||
| 	log("muxAB: %s\n", log_id(st.muxAB, "--")); | ||||
| 	log("ffS:   %s\n", log_id(st.ffS, "--")); | ||||
| #endif | ||||
| 
 | ||||
| 	log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul)); | ||||
| 	log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul)); | ||||
| 
 | ||||
| 	if (GetSize(pm.st.sigA) > 16) { | ||||
| 		log("  input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA)); | ||||
| 	if (GetSize(st.sigA) > 16) { | ||||
| 		log("  input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA)); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (GetSize(pm.st.sigB) > 16) { | ||||
| 		log("  input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB)); | ||||
| 	if (GetSize(st.sigB) > 16) { | ||||
| 		log("  input B (%s) is too large (%d > 16).\n", log_signal(st.sigB), GetSize(st.sigB)); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (GetSize(pm.st.sigS) > 32) { | ||||
| 		log("  accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS)); | ||||
| 	if (GetSize(st.sigS) > 32) { | ||||
| 		log("  accumulator (%s) is too large (%d > 32).\n", log_signal(st.sigS), GetSize(st.sigS)); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (GetSize(pm.st.sigY) > 32) { | ||||
| 		log("  output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY)); | ||||
| 	if (GetSize(st.sigY) > 32) { | ||||
| 		log("  output (%s) is too large (%d > 32).\n", log_signal(st.sigY), GetSize(st.sigY)); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool(); | ||||
| 	bool mul_signed = st.mul->getParam("\\A_SIGNED").as_bool(); | ||||
| 
 | ||||
| 	if (mul_signed) { | ||||
| 		log("  inference of signed iCE40 DSP arithmetic is currently not supported.\n"); | ||||
|  | @ -69,21 +72,21 @@ void create_ice40_dsp(ice40_dsp_pm &pm) | |||
| 	log("  replacing $mul with SB_MAC16 cell.\n"); | ||||
| 
 | ||||
| 	Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16"); | ||||
| 	pm.module->swap_names(cell, pm.st.mul); | ||||
| 	pm.module->swap_names(cell, st.mul); | ||||
| 
 | ||||
| 	// SB_MAC16 Input Interface
 | ||||
| 
 | ||||
| 	SigSpec A = pm.st.sigA; | ||||
| 	SigSpec A = st.sigA; | ||||
| 	A.extend_u0(16, mul_signed); | ||||
| 
 | ||||
| 	SigSpec B = pm.st.sigB; | ||||
| 	SigSpec B = st.sigB; | ||||
| 	B.extend_u0(16, mul_signed); | ||||
| 
 | ||||
| 	SigSpec CD; | ||||
| 	if (pm.st.muxA) | ||||
| 		CD = pm.st.muxA->getPort("\\B"); | ||||
| 	if (pm.st.muxB) | ||||
| 		CD = pm.st.muxB->getPort("\\A"); | ||||
| 	if (st.muxA) | ||||
| 		CD = st.muxA->getPort("\\B"); | ||||
| 	if (st.muxB) | ||||
| 		CD = st.muxB->getPort("\\A"); | ||||
| 	CD.extend_u0(32, mul_signed); | ||||
| 
 | ||||
| 	cell->setPort("\\A", A); | ||||
|  | @ -91,8 +94,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm) | |||
| 	cell->setPort("\\C", CD.extract(0, 16)); | ||||
| 	cell->setPort("\\D", CD.extract(16, 16)); | ||||
| 
 | ||||
| 	cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\A_REG", st.ffA ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\B_REG", st.ffB ? State::S1 : State::S0); | ||||
| 
 | ||||
| 	cell->setPort("\\AHOLD", State::S0); | ||||
| 	cell->setPort("\\BHOLD", State::S0); | ||||
|  | @ -102,25 +105,25 @@ void create_ice40_dsp(ice40_dsp_pm &pm) | |||
| 	cell->setPort("\\IRSTTOP", State::S0); | ||||
| 	cell->setPort("\\IRSTBOT", State::S0); | ||||
| 
 | ||||
| 	if (pm.st.clock_vld) | ||||
| 	if (st.clock_vld) | ||||
| 	{ | ||||
| 		cell->setPort("\\CLK", pm.st.clock); | ||||
| 		cell->setPort("\\CLK", st.clock); | ||||
| 		cell->setPort("\\CE", State::S1); | ||||
| 		cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1); | ||||
| 		cell->setParam("\\NEG_TRIGGER", st.clock_pol ? State::S0 : State::S1); | ||||
| 
 | ||||
| 		log("  clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge"); | ||||
| 		log("  clock: %s (%s)", log_signal(st.clock), st.clock_pol ? "posedge" : "negedge"); | ||||
| 
 | ||||
| 		if (pm.st.ffA) | ||||
| 			log(" ffA:%s", log_id(pm.st.ffA)); | ||||
| 		if (st.ffA) | ||||
| 			log(" ffA:%s", log_id(st.ffA)); | ||||
| 
 | ||||
| 		if (pm.st.ffB) | ||||
| 			log(" ffB:%s", log_id(pm.st.ffB)); | ||||
| 		if (st.ffB) | ||||
| 			log(" ffB:%s", log_id(st.ffB)); | ||||
| 
 | ||||
| 		if (pm.st.ffY) | ||||
| 			log(" ffY:%s", log_id(pm.st.ffY)); | ||||
| 		if (st.ffY) | ||||
| 			log(" ffY:%s", log_id(st.ffY)); | ||||
| 
 | ||||
| 		if (pm.st.ffS) | ||||
| 			log(" ffS:%s", log_id(pm.st.ffS)); | ||||
| 		if (st.ffS) | ||||
| 			log(" ffS:%s", log_id(st.ffS)); | ||||
| 
 | ||||
| 		log("\n"); | ||||
| 	} | ||||
|  | @ -144,16 +147,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm) | |||
| 
 | ||||
| 	// SB_MAC16 Output Interface
 | ||||
| 
 | ||||
| 	SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY; | ||||
| 	SigSpec O = st.ffS ? st.sigS : st.sigY; | ||||
| 	if (GetSize(O) < 32) | ||||
| 		O.append(pm.module->addWire(NEW_ID, 32-GetSize(O))); | ||||
| 
 | ||||
| 	cell->setPort("\\O", O); | ||||
| 
 | ||||
| 	if (pm.st.addAB) { | ||||
| 		log("  accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type)); | ||||
| 		cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1); | ||||
| 		cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1); | ||||
| 	if (st.addAB) { | ||||
| 		log("  accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type)); | ||||
| 		cell->setPort("\\ADDSUBTOP", st.addAB->type == "$add" ? State::S0 : State::S1); | ||||
| 		cell->setPort("\\ADDSUBBOT", st.addAB->type == "$add" ? State::S0 : State::S1); | ||||
| 	} else { | ||||
| 		cell->setPort("\\ADDSUBTOP", State::S0); | ||||
| 		cell->setPort("\\ADDSUBBOT", State::S0); | ||||
|  | @ -166,10 +169,10 @@ void create_ice40_dsp(ice40_dsp_pm &pm) | |||
| 	cell->setPort("\\OHOLDBOT", State::S0); | ||||
| 
 | ||||
| 	SigSpec acc_reset = State::S0; | ||||
| 	if (pm.st.muxA) | ||||
| 		acc_reset = pm.st.muxA->getPort("\\S"); | ||||
| 	if (pm.st.muxB) | ||||
| 		acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S")); | ||||
| 	if (st.muxA) | ||||
| 		acc_reset = st.muxA->getPort("\\S"); | ||||
| 	if (st.muxB) | ||||
| 		acc_reset = pm.module->Not(NEW_ID, st.muxB->getPort("\\S")); | ||||
| 
 | ||||
| 	cell->setPort("\\OLOADTOP", acc_reset); | ||||
| 	cell->setPort("\\OLOADBOT", acc_reset); | ||||
|  | @ -179,17 +182,17 @@ void create_ice40_dsp(ice40_dsp_pm &pm) | |||
| 	cell->setParam("\\C_REG", State::S0); | ||||
| 	cell->setParam("\\D_REG", State::S0); | ||||
| 
 | ||||
| 	cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\TOP_8x8_MULT_REG", st.ffY ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\BOT_8x8_MULT_REG", st.ffY ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\PIPELINE_16x16_MULT_REG1", st.ffY ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0); | ||||
| 
 | ||||
| 	cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2)); | ||||
| 	cell->setParam("\\TOPOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2)); | ||||
| 	cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2)); | ||||
| 	cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0); | ||||
| 	cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2)); | ||||
| 
 | ||||
| 	cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2)); | ||||
| 	cell->setParam("\\BOTOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2)); | ||||
| 	cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2)); | ||||
| 	cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0); | ||||
| 	cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2)); | ||||
|  | @ -198,9 +201,9 @@ void create_ice40_dsp(ice40_dsp_pm &pm) | |||
| 	cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0); | ||||
| 	cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0); | ||||
| 
 | ||||
| 	pm.autoremove(pm.st.mul); | ||||
| 	pm.autoremove(pm.st.ffY); | ||||
| 	pm.autoremove(pm.st.ffS); | ||||
| 	pm.autoremove(st.mul); | ||||
| 	pm.autoremove(st.ffY); | ||||
| 	pm.autoremove(st.ffS); | ||||
| } | ||||
| 
 | ||||
| struct Ice40DspPass : public Pass { | ||||
|  | @ -230,7 +233,7 @@ struct Ice40DspPass : public Pass { | |||
| 		extra_args(args, argidx, design); | ||||
| 
 | ||||
| 		for (auto module : design->selected_modules()) | ||||
| 			ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp); | ||||
| 			ice40_dsp_pm(module, module->selected_cells()).run_ice40_dsp(create_ice40_dsp); | ||||
| 	} | ||||
| } Ice40DspPass; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| pattern ice40_dsp | ||||
| 
 | ||||
| state <SigBit> clock | ||||
| state <bool> clock_pol clock_vld | ||||
| state <SigSpec> sigA sigB sigY sigS | ||||
|  |  | |||
							
								
								
									
										68
									
								
								passes/pmgen/peepopt.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								passes/pmgen/peepopt.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,68 @@ | |||
| /*
 | ||||
|  *  yosys -- Yosys Open SYnthesis Suite | ||||
|  * | ||||
|  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> | ||||
|  * | ||||
|  *  Permission to use, copy, modify, and/or distribute this software for any | ||||
|  *  purpose with or without fee is hereby granted, provided that the above | ||||
|  *  copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
|  *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
|  *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "kernel/yosys.h" | ||||
| #include "kernel/sigtools.h" | ||||
| 
 | ||||
| USING_YOSYS_NAMESPACE | ||||
| PRIVATE_NAMESPACE_BEGIN | ||||
| 
 | ||||
| bool did_something; | ||||
| 
 | ||||
| #include "passes/pmgen/peepopt_pm.h" | ||||
| 
 | ||||
| struct PeepoptPass : public Pass { | ||||
| 	PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { } | ||||
| 	void help() YS_OVERRIDE | ||||
| 	{ | ||||
| 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | ||||
| 		log("\n"); | ||||
| 		log("    peepopt [options] [selection]\n"); | ||||
| 		log("\n"); | ||||
| 		log("This pass applies a collection of peephole optimizers to the current design.\n"); | ||||
| 		log("\n"); | ||||
| 	} | ||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||
| 	{ | ||||
| 		log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n"); | ||||
| 
 | ||||
| 		size_t argidx; | ||||
| 		for (argidx = 1; argidx < args.size(); argidx++) | ||||
| 		{ | ||||
| 			// if (args[argidx] == "-singleton") {
 | ||||
| 			// 	singleton_mode = true;
 | ||||
| 			// 	continue;
 | ||||
| 			// }
 | ||||
| 			break; | ||||
| 		} | ||||
| 		extra_args(args, argidx, design); | ||||
| 
 | ||||
| 		for (auto module : design->selected_modules()) { | ||||
| 			did_something = true; | ||||
| 			while (did_something) { | ||||
| 				did_something = false; | ||||
| 				peepopt_pm pm(module, module->selected_cells()); | ||||
| 				pm.run_shiftmul(); | ||||
| 				pm.run_muldiv(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } PeepoptPass; | ||||
| 
 | ||||
| PRIVATE_NAMESPACE_END | ||||
							
								
								
									
										36
									
								
								passes/pmgen/peepopt_muldiv.pmg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								passes/pmgen/peepopt_muldiv.pmg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | |||
| pattern muldiv | ||||
| 
 | ||||
| state <SigSpec> t x y | ||||
| 
 | ||||
| match mul | ||||
| 	select mul->type == $mul | ||||
| 	select GetSize(port(mul, \A)) + GetSize(port(mul, \B)) <= GetSize(port(mul, \Y)) | ||||
| endmatch | ||||
| 
 | ||||
| code t x y | ||||
| 	t = port(mul, \Y); | ||||
| 	x = port(mul, \A); | ||||
| 	y = port(mul, \B); | ||||
| 	branch; | ||||
| 	std::swap(x, y); | ||||
| endcode | ||||
| 
 | ||||
| match div | ||||
| 	select div->type.in($div) | ||||
| 	index <SigSpec> port(div, \A) === t | ||||
| 	index <SigSpec> port(div, \B) === x | ||||
| endmatch | ||||
| 
 | ||||
| code | ||||
| 	SigSpec div_y = port(div, \Y); | ||||
| 	SigSpec val_y = y; | ||||
| 
 | ||||
| 	if (GetSize(div_y) != GetSize(val_y)) | ||||
| 		val_y.extend_u0(GetSize(div_y), param(div, \A_SIGNED).as_bool()); | ||||
| 
 | ||||
| 	did_something = true; | ||||
| 	log("muldiv pattern in %s: mul=%s, div=%s\n", log_id(module), log_id(mul), log_id(div)); | ||||
| 	module->connect(div_y, val_y); | ||||
| 	autoremove(div); | ||||
| 	reject; | ||||
| endcode | ||||
							
								
								
									
										87
									
								
								passes/pmgen/peepopt_shiftmul.pmg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								passes/pmgen/peepopt_shiftmul.pmg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,87 @@ | |||
| pattern shiftmul | ||||
| 
 | ||||
| state <SigSpec> shamt | ||||
| 
 | ||||
| match shift | ||||
| 	select shift->type.in($shift, $shiftx, $shr) | ||||
| endmatch | ||||
| 
 | ||||
| code shamt | ||||
| 	shamt = port(shift, \B); | ||||
| 	if (shamt.empty()) | ||||
| 		reject; | ||||
| 	if (shamt[GetSize(shamt)-1] == State::S0) { | ||||
| 		do { | ||||
| 			shamt.remove(GetSize(shamt)-1); | ||||
| 			if (shamt.empty()) | ||||
| 				reject; | ||||
| 		} while (shamt[GetSize(shamt)-1] == State::S0); | ||||
| 	} else | ||||
| 	if (shift->type.in($shift, $shiftx) && param(shift, \B_SIGNED).as_bool()) { | ||||
| 		reject; | ||||
| 	} | ||||
| 	if (GetSize(shamt) > 20) | ||||
| 		reject; | ||||
| endcode | ||||
| 
 | ||||
| match mul | ||||
| 	select mul->type.in($mul) | ||||
| 	select port(mul, \A).is_fully_const() || port(mul, \B).is_fully_const() | ||||
| 	index <SigSpec> port(mul, \Y) === shamt | ||||
| endmatch | ||||
| 
 | ||||
| code | ||||
| 	IdString const_factor_port = port(mul, \A).is_fully_const() ? \A : \B; | ||||
| 	IdString const_factor_signed = const_factor_port == \A ? \A_SIGNED : \B_SIGNED; | ||||
| 	Const const_factor_cnst = port(mul, const_factor_port).as_const(); | ||||
| 	int const_factor = const_factor_cnst.as_int(); | ||||
| 
 | ||||
| 	if (GetSize(const_factor_cnst) == 0) | ||||
| 		reject; | ||||
| 
 | ||||
| 	if (const_factor_cnst.bits[GetSize(const_factor_cnst)-1] != State::S0 && | ||||
| 			param(mul, const_factor_signed).as_bool()) | ||||
| 		reject; | ||||
| 
 | ||||
| 	if (GetSize(const_factor_cnst) > 20) | ||||
| 		reject; | ||||
| 
 | ||||
| 	if (GetSize(port(shift, \Y)) > const_factor) | ||||
| 		reject; | ||||
| 
 | ||||
| 	did_something = true; | ||||
| 	log("shiftmul pattern in %s: shift=%s, mul=%s\n", log_id(module), log_id(shift), log_id(mul)); | ||||
| 
 | ||||
| 	int new_const_factor_log2 = ceil_log2(const_factor); | ||||
| 	int new_const_factor = 1 << new_const_factor_log2; | ||||
| 
 | ||||
| 	SigSpec padding(State::Sx, new_const_factor-const_factor); | ||||
| 	SigSpec old_a = port(shift, \A), new_a; | ||||
| 	int trunc = 0; | ||||
| 
 | ||||
| 	if (GetSize(old_a) % const_factor != 0) { | ||||
| 		trunc = const_factor - GetSize(old_a) % const_factor; | ||||
| 		old_a.append(SigSpec(State::Sx, trunc)); | ||||
| 	} | ||||
| 
 | ||||
| 	for (int i = 0; i*const_factor < GetSize(old_a); i++) { | ||||
| 		SigSpec slice = old_a.extract(i*const_factor, const_factor); | ||||
| 		new_a.append(slice); | ||||
| 		new_a.append(padding); | ||||
| 	} | ||||
| 
 | ||||
| 	if (trunc > 0) | ||||
| 		new_a.remove(GetSize(new_a)-trunc, trunc); | ||||
| 
 | ||||
| 	SigSpec new_b = {port(mul, const_factor_port == \A ? \B : \A), SigSpec(State::S0, new_const_factor_log2)}; | ||||
| 	if (param(shift, \B_SIGNED).as_bool()) | ||||
| 		new_b.append(State::S0); | ||||
| 
 | ||||
| 	shift->setPort(\A, new_a); | ||||
| 	shift->setParam(\A_WIDTH, GetSize(new_a)); | ||||
| 	shift->setPort(\B, new_b); | ||||
| 	shift->setParam(\B_WIDTH, GetSize(new_b)); | ||||
| 
 | ||||
| 	blacklist(shift); | ||||
| 	reject; | ||||
| endcode | ||||
|  | @ -3,15 +3,42 @@ | |||
| import re | ||||
| import sys | ||||
| import pprint | ||||
| import getopt | ||||
| 
 | ||||
| pp = pprint.PrettyPrinter(indent=4) | ||||
| 
 | ||||
| pmgfile = sys.argv[1] | ||||
| assert pmgfile.endswith(".pmg") | ||||
| prefix = pmgfile[0:-4] | ||||
| prefix = prefix.split('/')[-1] | ||||
| outfile = sys.argv[2] | ||||
| prefix = None | ||||
| pmgfiles = list() | ||||
| outfile = None | ||||
| debug = False | ||||
| genhdr = False | ||||
| 
 | ||||
| opts, args = getopt.getopt(sys.argv[1:], "p:o:dg") | ||||
| 
 | ||||
| for o, a in opts: | ||||
|     if o == "-p": | ||||
|         prefix = a | ||||
|     elif o == "-o": | ||||
|         outfile = a | ||||
|     elif o == "-d": | ||||
|         debug = True | ||||
|     elif o == "-g": | ||||
|         genhdr = True | ||||
| 
 | ||||
| if outfile is None: | ||||
|     outfile = "/dev/stdout" | ||||
| 
 | ||||
| for a in args: | ||||
|     assert a.endswith(".pmg") | ||||
|     if prefix is None and len(args) == 1: | ||||
|         prefix = a[0:-4] | ||||
|         prefix = prefix.split('/')[-1] | ||||
|     pmgfiles.append(a) | ||||
| 
 | ||||
| assert prefix is not None | ||||
| 
 | ||||
| current_pattern = None | ||||
| patterns = dict() | ||||
| state_types = dict() | ||||
| udata_types = dict() | ||||
| blocks = list() | ||||
|  | @ -77,7 +104,8 @@ def rewrite_cpp(s): | |||
| 
 | ||||
|     return "".join(t) | ||||
| 
 | ||||
| with open(pmgfile, "r") as f: | ||||
| def process_pmgfile(f): | ||||
|     global current_pattern | ||||
|     while True: | ||||
|         line = f.readline() | ||||
|         if line == "": break | ||||
|  | @ -87,14 +115,31 @@ with open(pmgfile, "r") as f: | |||
|         if len(cmd) == 0 or cmd[0].startswith("//"): continue | ||||
|         cmd = cmd[0] | ||||
| 
 | ||||
|         if cmd == "pattern": | ||||
|             if current_pattern is not None: | ||||
|                 block = dict() | ||||
|                 block["type"] = "final" | ||||
|                 block["pattern"] = current_pattern | ||||
|                 blocks.append(block) | ||||
|             line = line.split() | ||||
|             assert len(line) == 2 | ||||
|             assert line[1] not in patterns | ||||
|             current_pattern = line[1] | ||||
|             patterns[current_pattern] = len(blocks) | ||||
|             state_types[current_pattern] = dict() | ||||
|             udata_types[current_pattern] = dict() | ||||
|             continue | ||||
| 
 | ||||
|         assert current_pattern is not None | ||||
| 
 | ||||
|         if cmd == "state": | ||||
|             m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line) | ||||
|             assert m | ||||
|             type_str = m.group(1) | ||||
|             states_str = m.group(2) | ||||
|             for s in re.split(r"\s+", states_str): | ||||
|                 assert s not in state_types | ||||
|                 state_types[s] = type_str | ||||
|                 assert s not in state_types[current_pattern] | ||||
|                 state_types[current_pattern][s] = type_str | ||||
|             continue | ||||
| 
 | ||||
|         if cmd == "udata": | ||||
|  | @ -103,19 +148,20 @@ with open(pmgfile, "r") as f: | |||
|             type_str = m.group(1) | ||||
|             udatas_str = m.group(2) | ||||
|             for s in re.split(r"\s+", udatas_str): | ||||
|                 assert s not in udata_types | ||||
|                 udata_types[s] = type_str | ||||
|                 assert s not in udata_types[current_pattern] | ||||
|                 udata_types[current_pattern][s] = type_str | ||||
|             continue | ||||
| 
 | ||||
|         if cmd == "match": | ||||
|             block = dict() | ||||
|             block["type"] = "match" | ||||
|             block["pattern"] = current_pattern | ||||
| 
 | ||||
|             line = line.split() | ||||
|             assert len(line) == 2 | ||||
|             assert line[1] not in state_types | ||||
|             assert line[1] not in state_types[current_pattern] | ||||
|             block["cell"] = line[1] | ||||
|             state_types[line[1]] = "Cell*"; | ||||
|             state_types[current_pattern][line[1]] = "Cell*"; | ||||
| 
 | ||||
|             block["if"] = list() | ||||
|             block["select"] = list() | ||||
|  | @ -158,15 +204,18 @@ with open(pmgfile, "r") as f: | |||
|                 assert False | ||||
| 
 | ||||
|             blocks.append(block) | ||||
|             continue | ||||
| 
 | ||||
|         if cmd == "code": | ||||
|             block = dict() | ||||
|             block["type"] = "code" | ||||
|             block["pattern"] = current_pattern | ||||
| 
 | ||||
|             block["code"] = list() | ||||
|             block["states"] = set() | ||||
| 
 | ||||
|             for s in line.split()[1:]: | ||||
|                 assert s in state_types | ||||
|                 assert s in state_types[current_pattern] | ||||
|                 block["states"].add(s) | ||||
| 
 | ||||
|             while True: | ||||
|  | @ -179,17 +228,36 @@ with open(pmgfile, "r") as f: | |||
|                 block["code"].append(rewrite_cpp(l.rstrip())) | ||||
| 
 | ||||
|             blocks.append(block) | ||||
|             continue | ||||
| 
 | ||||
|         assert False | ||||
| 
 | ||||
| for fn in pmgfiles: | ||||
|     with open(fn, "r") as f: | ||||
|         process_pmgfile(f) | ||||
| 
 | ||||
| if current_pattern is not None: | ||||
|     block = dict() | ||||
|     block["type"] = "final" | ||||
|     block["pattern"] = current_pattern | ||||
|     blocks.append(block) | ||||
| 
 | ||||
| current_pattern = None | ||||
| 
 | ||||
| if debug: | ||||
|     pp.pprint(blocks) | ||||
| 
 | ||||
| with open(outfile, "w") as f: | ||||
|     print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f) | ||||
|     for fn in pmgfiles: | ||||
|         print("// Generated by pmgen.py from {}".format(fn), file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("#include \"kernel/yosys.h\"", file=f) | ||||
|     print("#include \"kernel/sigtools.h\"", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("YOSYS_NAMESPACE_BEGIN", file=f) | ||||
|     print("", file=f) | ||||
|     if genhdr: | ||||
|         print("#include \"kernel/yosys.h\"", file=f) | ||||
|         print("#include \"kernel/sigtools.h\"", file=f) | ||||
|         print("", file=f) | ||||
|         print("YOSYS_NAMESPACE_BEGIN", file=f) | ||||
|         print("", file=f) | ||||
| 
 | ||||
|     print("struct {}_pm {{".format(prefix), file=f) | ||||
|     print("  Module *module;", file=f) | ||||
|  | @ -212,17 +280,19 @@ with open(outfile, "w") as f: | |||
|     print("  int rollback;", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  struct state_t {", file=f) | ||||
|     for s, t in sorted(state_types.items()): | ||||
|         print("    {} {};".format(t, s), file=f) | ||||
|     print("  } st;", file=f) | ||||
|     print("", file=f) | ||||
|     for current_pattern in sorted(patterns.keys()): | ||||
|         print("  struct state_{}_t {{".format(current_pattern), file=f) | ||||
|         for s, t in sorted(state_types[current_pattern].items()): | ||||
|             print("    {} {};".format(t, s), file=f) | ||||
|         print("  }} st_{};".format(current_pattern), file=f) | ||||
|         print("", file=f) | ||||
| 
 | ||||
|     print("  struct udata_t {", file=f) | ||||
|     for s, t in sorted(udata_types.items()): | ||||
|         print("    {} {};".format(t, s), file=f) | ||||
|     print("  } ud;", file=f) | ||||
|     print("", file=f) | ||||
|         print("  struct udata_{}_t {{".format(current_pattern), file=f) | ||||
|         for s, t in sorted(udata_types[current_pattern].items()): | ||||
|             print("    {} {};".format(t, s), file=f) | ||||
|         print("  }} ud_{};".format(current_pattern), file=f) | ||||
|         print("", file=f) | ||||
|     current_pattern = None | ||||
| 
 | ||||
|     for v, n in sorted(ids.items()): | ||||
|         if n[0] == "\\": | ||||
|  | @ -258,20 +328,24 @@ with open(outfile, "w") as f: | |||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  void check_blacklist() {", file=f) | ||||
|     print("    if (!blacklist_dirty)", file=f) | ||||
|     print("      return;", file=f) | ||||
|     print("    blacklist_dirty = false;", file=f) | ||||
|     for index in range(len(blocks)): | ||||
|         block = blocks[index] | ||||
|         if block["type"] == "match": | ||||
|             print("    if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f) | ||||
|             print("      rollback = {};".format(index+1), file=f) | ||||
|             print("      return;", file=f) | ||||
|             print("    }", file=f) | ||||
|     print("    rollback = 0;", file=f) | ||||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
|     for current_pattern in sorted(patterns.keys()): | ||||
|         print("  void check_blacklist_{}() {{".format(current_pattern), file=f) | ||||
|         print("    if (!blacklist_dirty)", file=f) | ||||
|         print("      return;", file=f) | ||||
|         print("    blacklist_dirty = false;", file=f) | ||||
|         for index in range(len(blocks)): | ||||
|             block = blocks[index] | ||||
|             if block["pattern"] != current_pattern: | ||||
|                 continue | ||||
|             if block["type"] == "match": | ||||
|                 print("    if (st_{}.{} != nullptr && blacklist_cells.count(st_{}.{})) {{".format(current_pattern, block["cell"], current_pattern, block["cell"]), file=f) | ||||
|                 print("      rollback = {};".format(index+1), file=f) | ||||
|                 print("      return;", file=f) | ||||
|                 print("    }", file=f) | ||||
|         print("    rollback = 0;", file=f) | ||||
|         print("  }", file=f) | ||||
|         print("", file=f) | ||||
|     current_pattern = None | ||||
| 
 | ||||
|     print("  SigSpec port(Cell *cell, IdString portname) {", file=f) | ||||
|     print("    return sigmap(cell->getPort(portname));", file=f) | ||||
|  | @ -294,11 +368,13 @@ with open(outfile, "w") as f: | |||
| 
 | ||||
|     print("  {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f) | ||||
|     print("      module(module), sigmap(module) {", file=f) | ||||
|     for s, t in sorted(udata_types.items()): | ||||
|         if t.endswith("*"): | ||||
|             print("    ud.{} = nullptr;".format(s), file=f) | ||||
|         else: | ||||
|             print("    ud.{} = {}();".format(s, t), file=f) | ||||
|     for current_pattern in sorted(patterns.keys()): | ||||
|         for s, t in sorted(udata_types[current_pattern].items()): | ||||
|             if t.endswith("*"): | ||||
|                 print("    ud_{}.{} = nullptr;".format(current_pattern,s), file=f) | ||||
|             else: | ||||
|                 print("    ud_{}.{} = {}();".format(current_pattern, s, t), file=f) | ||||
|     current_pattern = None | ||||
|     print("    for (auto cell : module->cells()) {", file=f) | ||||
|     print("      for (auto &conn : cell->connections())", file=f) | ||||
|     print("        add_siguser(conn.second, cell);", file=f) | ||||
|  | @ -328,34 +404,52 @@ with open(outfile, "w") as f: | |||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  void run(std::function<void()> on_accept_f) {", file=f) | ||||
|     print("    on_accept = on_accept_f;", file=f) | ||||
|     print("    rollback = 0;", file=f) | ||||
|     print("    blacklist_dirty = false;", file=f) | ||||
|     for s, t in sorted(state_types.items()): | ||||
|         if t.endswith("*"): | ||||
|             print("    st.{} = nullptr;".format(s), file=f) | ||||
|         else: | ||||
|             print("    st.{} = {}();".format(s, t), file=f) | ||||
|     print("    block_0();", file=f) | ||||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
| 
 | ||||
|     print("  void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f) | ||||
|     print("    run([&](){on_accept_f(*this);});", file=f) | ||||
|     print("  }", file=f) | ||||
|     print("", file=f) | ||||
|     for current_pattern in sorted(patterns.keys()): | ||||
|         print("  void run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f) | ||||
|         print("    on_accept = on_accept_f;", file=f) | ||||
|         print("    rollback = 0;", file=f) | ||||
|         print("    blacklist_dirty = false;", file=f) | ||||
|         for s, t in sorted(state_types[current_pattern].items()): | ||||
|             if t.endswith("*"): | ||||
|                 print("    st_{}.{} = nullptr;".format(current_pattern, s), file=f) | ||||
|             else: | ||||
|                 print("    st_{}.{} = {}();".format(current_pattern, s, t), file=f) | ||||
|         print("    block_{}();".format(patterns[current_pattern]), file=f) | ||||
|         print("  }", file=f) | ||||
|         print("", file=f) | ||||
|         print("  void run_{}(std::function<void({}_pm&)> on_accept_f) {{".format(current_pattern, prefix), file=f) | ||||
|         print("    run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f) | ||||
|         print("  }", file=f) | ||||
|         print("", file=f) | ||||
|         print("  void run_{}(std::function<void(state_{}_t&)> on_accept_f) {{".format(current_pattern, current_pattern), file=f) | ||||
|         print("    run_{}([&](){{on_accept_f(st_{});}});".format(current_pattern, current_pattern), file=f) | ||||
|         print("  }", file=f) | ||||
|         print("", file=f) | ||||
|         print("  void run_{}() {{".format(current_pattern), file=f) | ||||
|         print("    run_{}([](){{}});".format(current_pattern, current_pattern), file=f) | ||||
|         print("  }", file=f) | ||||
|         print("", file=f) | ||||
|     current_pattern = None | ||||
| 
 | ||||
|     for index in range(len(blocks)): | ||||
|         block = blocks[index] | ||||
| 
 | ||||
|         print("  void block_{}() {{".format(index), file=f) | ||||
|         current_pattern = block["pattern"] | ||||
| 
 | ||||
|         if block["type"] == "final": | ||||
|             print("    on_accept();", file=f) | ||||
|             print("    check_blacklist_{}();".format(current_pattern), file=f) | ||||
|             print("  }", file=f) | ||||
|             if index+1 != len(blocks): | ||||
|                 print("", file=f) | ||||
|             continue | ||||
| 
 | ||||
|         const_st = set() | ||||
|         nonconst_st = set() | ||||
|         restore_st = set() | ||||
| 
 | ||||
|         for i in range(index): | ||||
|         for i in range(patterns[current_pattern], index): | ||||
|             if blocks[i]["type"] == "code": | ||||
|                 for s in blocks[i]["states"]: | ||||
|                     const_st.add(s) | ||||
|  | @ -378,27 +472,27 @@ with open(outfile, "w") as f: | |||
|             assert False | ||||
| 
 | ||||
|         for s in sorted(const_st): | ||||
|             t = state_types[s] | ||||
|             t = state_types[current_pattern][s] | ||||
|             if t.endswith("*"): | ||||
|                 print("    {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) | ||||
|                 print("    {} const &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f) | ||||
|             else: | ||||
|                 print("    const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) | ||||
|                 print("    const {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f) | ||||
| 
 | ||||
|         for s in sorted(nonconst_st): | ||||
|             t = state_types[s] | ||||
|             print("    {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) | ||||
|             t = state_types[current_pattern][s] | ||||
|             print("    {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f) | ||||
| 
 | ||||
|         if len(restore_st): | ||||
|             print("", file=f) | ||||
|             for s in sorted(restore_st): | ||||
|                 t = state_types[s] | ||||
|                 t = state_types[current_pattern][s] | ||||
|                 print("    {} backup_{} = {};".format(t, s, s), file=f) | ||||
| 
 | ||||
|         if block["type"] == "code": | ||||
|             print("", file=f) | ||||
|             print("    do {", file=f) | ||||
|             print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f) | ||||
|             print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f) | ||||
|             print("#define reject do {{ check_blacklist_{}(); goto rollback_label; }} while(0)".format(current_pattern), file=f) | ||||
|             print("#define accept do {{ on_accept(); check_blacklist_{}(); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f) | ||||
|             print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) | ||||
| 
 | ||||
|             for line in block["code"]: | ||||
|  | @ -417,11 +511,11 @@ with open(outfile, "w") as f: | |||
|             if len(restore_st) or len(nonconst_st): | ||||
|                 print("", file=f) | ||||
|                 for s in sorted(restore_st): | ||||
|                     t = state_types[s] | ||||
|                     t = state_types[current_pattern][s] | ||||
|                     print("    {} = backup_{};".format(s, s), file=f) | ||||
|                 for s in sorted(nonconst_st): | ||||
|                     if s not in restore_st: | ||||
|                         t = state_types[s] | ||||
|                         t = state_types[current_pattern][s] | ||||
|                         if t.endswith("*"): | ||||
|                             print("    {} = nullptr;".format(s), file=f) | ||||
|                         else: | ||||
|  | @ -470,17 +564,12 @@ with open(outfile, "w") as f: | |||
|         else: | ||||
|             assert False | ||||
| 
 | ||||
| 
 | ||||
|         current_pattern = None | ||||
|         print("  }", file=f) | ||||
|         print("", file=f) | ||||
| 
 | ||||
|     print("  void block_{}() {{".format(len(blocks)), file=f) | ||||
|     print("    on_accept();", file=f) | ||||
|     print("    check_blacklist();", file=f) | ||||
|     print("  }", file=f) | ||||
|     print("};", file=f) | ||||
| 
 | ||||
|     print("", file=f) | ||||
|     print("YOSYS_NAMESPACE_END", file=f) | ||||
| 
 | ||||
| # pp.pprint(blocks) | ||||
|     if genhdr: | ||||
|         print("", file=f) | ||||
|         print("YOSYS_NAMESPACE_END", file=f) | ||||
|  |  | |||
|  | @ -508,7 +508,7 @@ struct ExposePass : public Pass { | |||
| 				} | ||||
| 
 | ||||
| 				for (auto &conn : module->connections_) | ||||
| 					conn.first = out_to_in_map(sigmap(conn.first)); | ||||
| 					conn.first = out_to_in_map(conn.first); | ||||
| 			} | ||||
| 
 | ||||
| 			if (flag_cut) | ||||
|  |  | |||
|  | @ -1169,6 +1169,7 @@ struct SatPass : public Pass { | |||
| 			if (args[argidx] == "-tempinduct-def") { | ||||
| 				tempinduct = true; | ||||
| 				tempinduct_def = true; | ||||
| 				enable_undef = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-tempinduct-baseonly") { | ||||
|  |  | |||
|  | @ -330,20 +330,33 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) | |||
| std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr) | ||||
| { | ||||
| 	std::string abc_sname = abc_name.substr(1); | ||||
| 	if (abc_sname.substr(0, 5) == "ys__n") { | ||||
| 		bool inv = abc_sname.back() == 'v'; | ||||
| 		if (inv) abc_sname.pop_back(); | ||||
| 	bool isnew = false; | ||||
| 	if (abc_sname.substr(0, 4) == "new_") | ||||
| 	{ | ||||
| 		abc_sname.erase(0, 4); | ||||
| 		isnew = true; | ||||
| 	} | ||||
| 	if (abc_sname.substr(0, 5) == "ys__n") | ||||
| 	{ | ||||
| 		abc_sname.erase(0, 5); | ||||
| 		if (abc_sname.find_last_not_of("012345689") == std::string::npos) { | ||||
| 		if (std::isdigit(abc_sname.at(0))) | ||||
| 		{ | ||||
| 			int sid = std::stoi(abc_sname); | ||||
| 			for (auto sig : signal_list) { | ||||
| 				if (sig.id == sid && sig.bit.wire != nullptr) { | ||||
| 			size_t postfix_start = abc_sname.find_first_not_of("0123456789"); | ||||
| 			std::string postfix = postfix_start != std::string::npos ? abc_sname.substr(postfix_start) : ""; | ||||
| 
 | ||||
| 			if (sid < GetSize(signal_list)) | ||||
| 			{ | ||||
| 				auto sig = signal_list.at(sid); | ||||
| 				if (sig.bit.wire != nullptr) | ||||
| 				{ | ||||
| 					std::stringstream sstr; | ||||
| 					sstr << "$abc$" << map_autoidx << "$" << sig.bit.wire->name.substr(1); | ||||
| 					if (sig.bit.wire->width != 1) | ||||
| 						sstr << "[" << sig.bit.offset << "]"; | ||||
| 					if (inv) | ||||
| 						sstr << "_inv"; | ||||
| 					if (isnew) | ||||
| 						sstr << "_new"; | ||||
| 					sstr << postfix; | ||||
| 					if (orig_wire != nullptr) | ||||
| 						*orig_wire = sig.bit.wire; | ||||
| 					return sstr.str(); | ||||
|  |  | |||
|  | @ -102,7 +102,8 @@ struct DffinitPass : public Pass { | |||
| 				if (wire->attributes.count("\\init")) { | ||||
| 					Const value = wire->attributes.at("\\init"); | ||||
| 					for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) | ||||
| 						init_bits[sigmap(SigBit(wire, i))] = value[i]; | ||||
| 						if (value[i] != State::Sx) | ||||
| 							init_bits[sigmap(SigBit(wire, i))] = value[i]; | ||||
| 				} | ||||
| 				if (wire->port_output) | ||||
| 					for (auto bit : sigmap(wire)) | ||||
|  |  | |||
|  | @ -178,7 +178,17 @@ struct ShregmapTechXilinx7 : ShregmapTech | |||
| 
 | ||||
| 		// Only map if $shiftx exclusively covers the shift register
 | ||||
| 		if (shiftx->type == "$shiftx") { | ||||
| 			if (GetSize(taps) != shiftx->getParam("\\A_WIDTH").as_int()) | ||||
| 			if (GetSize(taps) > shiftx->getParam("\\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("\\A_WIDTH").as_int()) { | ||||
| 				const SigSpec A = shiftx->getPort("\\A"); | ||||
| 				const int A_width = shiftx->getParam("\\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("\\A_WIDTH").as_int()) | ||||
| 				return false; | ||||
| 		} | ||||
| 		else if (shiftx->type == "$mux") { | ||||
|  |  | |||
|  | @ -201,6 +201,8 @@ struct SynthPass : public ScriptPass | |||
| 			run("check"); | ||||
| 			run("opt"); | ||||
| 			run("wreduce"); | ||||
| 			run("peepopt"); | ||||
| 			run("opt_clean"); | ||||
| 			if (help_mode) | ||||
| 				run("techmap -map +/cmp2lut.v", " (if -lut)"); | ||||
| 			else | ||||
|  |  | |||
|  | @ -241,6 +241,8 @@ struct SynthIce40Pass : public ScriptPass | |||
| 			run("check"); | ||||
| 			run("opt"); | ||||
| 			run("wreduce"); | ||||
| 			run("peepopt"); | ||||
| 			run("opt_clean"); | ||||
| 			run("share"); | ||||
| 			run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); | ||||
| 			run("opt_expr"); | ||||
|  |  | |||
|  | @ -17,6 +17,16 @@ | |||
|  * | ||||
|  */ | ||||
| 
 | ||||
| // Convert negative-polarity reset to positive-polarity | ||||
| (* techmap_celltype = "$_DFF_NN0_" *) | ||||
| module _90_dff_nn0_to_np0 (input D, C, R, output Q); \$_DFF_NP0_  _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule | ||||
| (* techmap_celltype = "$_DFF_PN0_" *) | ||||
| module _90_dff_pn0_to_pp0 (input D, C, R, output Q); \$_DFF_PP0_  _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule | ||||
| (* techmap_celltype = "$_DFF_NN1_" *) | ||||
| module _90_dff_nn1_to_np1 (input D, C, R, output Q); \$_DFF_NP1   _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule | ||||
| (* techmap_celltype = "$_DFF_PN1_" *) | ||||
| module _90_dff_pn1_to_pp1 (input D, C, R, output Q); \$_DFF_PP1   _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule | ||||
| 
 | ||||
| module \$__SHREG_ (input C, input D, input E, output Q); | ||||
|   parameter DEPTH = 0; | ||||
|   parameter [DEPTH-1:0] INIT = 0; | ||||
|  |  | |||
|  | @ -22,26 +22,21 @@ | |||
| 
 | ||||
| `ifndef _NO_FFS | ||||
| 
 | ||||
| `ifndef _NO_POS_SR | ||||
| module  \$_DFF_N_   (input D, C, output Q);    FDRE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule | ||||
| module  \$_DFF_P_   (input D, C, output Q);    FDRE   #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule | ||||
| 
 | ||||
| module  \$_DFFE_NP_ (input D, C, E, output Q); FDRE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E),    .R(1'b0)); endmodule | ||||
| module  \$_DFFE_PP_ (input D, C, E, output Q); FDRE   #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E),    .R(1'b0)); endmodule | ||||
| 
 | ||||
| module  \$_DFF_NN0_ (input D, C, R, output Q); FDCE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R)); endmodule | ||||
| module  \$_DFF_NP0_ (input D, C, R, output Q); FDCE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule | ||||
| module  \$_DFF_PN0_ (input D, C, R, output Q); FDCE   #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R)); endmodule | ||||
| module  \$_DFF_PP0_ (input D, C, R, output Q); FDCE   #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule | ||||
| 
 | ||||
| module  \$_DFF_NN1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R)); endmodule | ||||
| module  \$_DFF_NP1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule | ||||
| module  \$_DFF_PN1_ (input D, C, R, output Q); FDPE   #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R)); endmodule | ||||
| module  \$_DFF_PP1_ (input D, C, R, output Q); FDPE   #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule | ||||
| `endif | ||||
| 
 | ||||
| module  \$_DFF_NN0_ (input D, C, R, output Q); \$_DFF_NP0_         _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C),              .R(~R)); endmodule | ||||
| module  \$_DFF_PN0_ (input D, C, R, output Q); \$_DFF_PP0_         _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C),              .R(~R)); endmodule | ||||
| 
 | ||||
| module  \$_DFF_NN1_ (input D, C, R, output Q); \$_DFF_NP1          _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C),              .R(~R)); endmodule | ||||
| module  \$_DFF_PN1_ (input D, C, R, output Q); \$_DFF_PP1          _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C),              .R(~R)); endmodule | ||||
| `endif | ||||
| 
 | ||||
| `endif | ||||
| 
 | ||||
|  |  | |||
|  | @ -205,45 +205,41 @@ struct SynthXilinxPass : public ScriptPass | |||
| 		} | ||||
| 
 | ||||
| 		if (check_label("fine")) { | ||||
| 			run("opt -fast"); | ||||
| 			// shregmap -tech xilinx can cope with $shiftx and $mux
 | ||||
| 			//   cells for identifiying variable-length shift registers,
 | ||||
| 			//   so attempt to convert $pmux-es to the former
 | ||||
| 			if (!nosrl || help_mode) | ||||
| 				run("pmux2shiftx", "(skip if '-nosrl')"); | ||||
| 
 | ||||
| 			run("opt -fast -full"); | ||||
| 			run("memory_map"); | ||||
| 			run("dffsr2dff"); | ||||
| 			run("dff2dffe"); | ||||
| 			run("opt -full"); | ||||
| 
 | ||||
| 			if (!vpr || help_mode) | ||||
| 				run("techmap -map +/xilinx/arith_map.v"); | ||||
| 			else | ||||
| 				run("techmap -map +/xilinx/arith_map.v -D _EXPLICIT_CARRY"); | ||||
| 
 | ||||
| 			run("hierarchy -check"); | ||||
| 			run("opt -fast"); | ||||
| 		} | ||||
| 
 | ||||
| 		if (check_label("map_cells")) | ||||
| 		{ | ||||
| 			if (!nosrl || help_mode) { | ||||
| 				// shregmap operates on bit-level flops, not word-level,
 | ||||
| 				//   so break those down here
 | ||||
| 				run("simplemap t:$dff t:$dffe", "(skip if '-nosrl')"); | ||||
| 				// shregmap -tech xilinx can cope with $shiftx and $mux
 | ||||
| 				//   cells for identifiying variable-length shift registers,
 | ||||
| 				//   so attempt to convert $pmux-es to the former
 | ||||
| 				run("pmux2shiftx", "(skip if '-nosrl')"); | ||||
| 				// pmux2shiftx can leave behind a $pmux with a single entry
 | ||||
| 				//   -- need this to clean that up before shregmap
 | ||||
| 				run("opt_expr -mux_undef", "(skip if '-nosrl')"); | ||||
| 				// shregmap with '-tech xilinx' infers variable length shift regs
 | ||||
| 				run("shregmap -tech xilinx -minlen 3", "(skip if '-nosrl')"); | ||||
| 			} | ||||
| 
 | ||||
| 			run("techmap -map +/xilinx/cells_map.v"); | ||||
| 			run("techmap"); | ||||
| 			run("opt -fast"); | ||||
| 		} | ||||
| 
 | ||||
| 		if (check_label("map_cells")) { | ||||
| 			run("techmap -map +/techmap.v -map +/xilinx/cells_map.v"); | ||||
| 			run("clean"); | ||||
| 		} | ||||
| 
 | ||||
| 		if (check_label("map_luts")) | ||||
| 		{ | ||||
| 			run("opt -full"); | ||||
| 			run("techmap -map +/techmap.v -D _NO_POS_SR -map +/xilinx/ff_map.v"); | ||||
| 		if (check_label("map_luts")) { | ||||
| 			if (help_mode) | ||||
| 				run("abc -luts 2:2,3,6:5,10,20 [-dff]"); | ||||
| 			else | ||||
|  | @ -259,21 +255,18 @@ struct SynthXilinxPass : public ScriptPass | |||
| 			run("clean"); | ||||
| 		} | ||||
| 
 | ||||
| 		if (check_label("check")) | ||||
| 		{ | ||||
| 		if (check_label("check")) { | ||||
| 			run("hierarchy -check"); | ||||
| 			run("stat"); | ||||
| 			run("check -noinit"); | ||||
| 		} | ||||
| 
 | ||||
| 		if (check_label("edif")) | ||||
| 		{ | ||||
| 		if (check_label("edif")) { | ||||
| 			if (!edif_file.empty() || help_mode) | ||||
| 				run(stringf("write_edif -pvector bra %s", edif_file.c_str())); | ||||
| 		} | ||||
| 
 | ||||
| 		if (check_label("blif")) | ||||
| 		{ | ||||
| 		if (check_label("blif")) { | ||||
| 			if (!blif_file.empty() || help_mode) | ||||
| 				run(stringf("write_blif %s", edif_file.c_str())); | ||||
| 		} | ||||
|  |  | |||
							
								
								
									
										22
									
								
								tests/memories/firrtl_938.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								tests/memories/firrtl_938.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| module top | ||||
| ( | ||||
| 	input [7:0] data_a, | ||||
| 	input [6:1] addr_a, | ||||
| 	input we_a, clk, | ||||
| 	output reg [7:0] q_a | ||||
| ); | ||||
| 	// Declare the RAM variable | ||||
| 	reg [7:0] ram[63:0]; | ||||
| 
 | ||||
| 	// Port A | ||||
| 	always @ (posedge clk) | ||||
| 	begin | ||||
| 		if (we_a) | ||||
| 		begin | ||||
| 			ram[addr_a] <= data_a; | ||||
| 			q_a <= data_a; | ||||
| 		end | ||||
| 			q_a <= ram[addr_a]; | ||||
| 	end | ||||
| 
 | ||||
| endmodule | ||||
|  | @ -92,3 +92,25 @@ module mem2reg_test5(input ctrl, output out); | |||
| 	assign out = bar[foo[0]]; | ||||
| endmodule | ||||
| 
 | ||||
| // ------------------------------------------------------ | ||||
| 
 | ||||
| module mem2reg_test6 (din, dout); | ||||
|         input   wire    [3:0] din; | ||||
|         output  reg     [3:0] dout; | ||||
| 
 | ||||
|         reg [1:0] din_array  [1:0]; | ||||
|         reg [1:0] dout_array [1:0]; | ||||
| 
 | ||||
|         always @* begin | ||||
| 		din_array[0] = din[0 +: 2]; | ||||
| 		din_array[1] = din[2 +: 2]; | ||||
| 
 | ||||
| 		dout_array[0] = din_array[0]; | ||||
| 		dout_array[1] = din_array[1]; | ||||
| 
 | ||||
| 		{dout_array[0][1], dout_array[0][0]} = dout_array[0][0] + dout_array[1][0]; | ||||
| 
 | ||||
| 		dout[0 +: 2] = dout_array[0]; | ||||
| 		dout[2 +: 2] = dout_array[1]; | ||||
|         end | ||||
| endmodule | ||||
|  |  | |||
							
								
								
									
										9
									
								
								tests/simple/peepopt.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/simple/peepopt.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| module peepopt_shiftmul_0 #(parameter N=3, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output [W-1:0] o); | ||||
| assign o = i[s*W+:W]; | ||||
| endmodule | ||||
| 
 | ||||
| module peepopt_muldiv_0(input [1:0] i, output [1:0] o); | ||||
| wire [3:0] t; | ||||
| assign t = i * 3; | ||||
| assign o = t / 3; | ||||
| endmodule | ||||
|  | @ -16,6 +16,7 @@ operators.v	$pow | |||
| partsel.v	drops modules | ||||
| process.v	drops modules | ||||
| realexpr.v	drops modules | ||||
| retime.v	Initial value (11110101) for (retime_test.ff) not supported | ||||
| scopes.v	original verilog issues ( -x where x isn't declared signed) | ||||
| sincos.v	$adff | ||||
| specify.v	no code (empty module generates error | ||||
|  |  | |||
|  | @ -11,13 +11,13 @@ echo "" > $STDERRFILE | |||
| 
 | ||||
| echo -n "Test: ${TESTNAME} -> " | ||||
| 
 | ||||
| $PWD/../../yosys -p "read_verilog -sv ${TESTNAME}.sv ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_syn.v" >> $STDOUTFILE >> $STDERRFILE | ||||
| $PWD/../../yosys -p "read_verilog -sv ${TESTNAME}_ref.v ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_ref_syn.v" >> $STDOUTFILE >> $STDERRFILE | ||||
| set -e | ||||
| 
 | ||||
| $PWD/../../yosys -p "read_verilog -sv ${TESTNAME}.sv ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_syn.v" >> $STDOUTFILE 2>> $STDERRFILE | ||||
| $PWD/../../yosys -p "read_verilog -sv ${TESTNAME}_ref.v ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_ref_syn.v" >> $STDOUTFILE 2>> $STDERRFILE | ||||
| 
 | ||||
| rm -f a.out reference_result.txt dut_result.txt | ||||
| 
 | ||||
| set -e | ||||
| 
 | ||||
| iverilog -g2012 ${TESTNAME}_syn.v | ||||
| iverilog -g2012 ${TESTNAME}_ref_syn.v | ||||
| 
 | ||||
|  |  | |||
|  | @ -147,7 +147,8 @@ do | |||
| 		fi | ||||
| 		if $genvcd; then sed -i 's,// \$dump,$dump,g' ${bn}_tb.v; fi | ||||
| 		compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.v $libs \ | ||||
| 					"$toolsdir"/../../techlibs/common/simlib.v | ||||
| 					"$toolsdir"/../../techlibs/common/simlib.v \ | ||||
| 					"$toolsdir"/../../techlibs/common/simcells.v | ||||
| 		if $genvcd; then mv testbench.vcd ${bn}_ref.vcd; fi | ||||
| 
 | ||||
| 		test_count=0 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue