mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	sdc: collect strictly matching objects
This commit is contained in:
		
							parent
							
								
									3054f3a046
								
							
						
					
					
						commit
						583910b664
					
				
					 2 changed files with 146 additions and 101 deletions
				
			
		|  | @ -9,6 +9,117 @@ | |||
| USING_YOSYS_NAMESPACE | ||||
| PRIVATE_NAMESPACE_BEGIN | ||||
| 
 | ||||
| 
 | ||||
| struct SdcObjects { | ||||
| 	std::vector<std::string> design_ports; | ||||
| 	std::vector<std::pair<std::string, Cell*>> design_cells; | ||||
| 	std::vector<std::pair<std::string, Cell*>> design_pins; | ||||
| 	std::vector<std::pair<std::string, Wire*>> design_nets; | ||||
| 
 | ||||
| 	pool<std::string> constrained_ports; | ||||
| 	pool<std::pair<std::string, Cell*>> constrained_cells; | ||||
| 	pool<std::pair<std::string, Cell*>> constrained_pins; | ||||
| 	pool<std::pair<std::string, Wire*>> constrained_nets; | ||||
| 
 | ||||
| 	void sniff_module(std::list<std::string>& hierarchy, Module* mod) { | ||||
| 		std::string prefix; | ||||
| 		for (auto mod_name : hierarchy) { | ||||
| 			if (prefix.length()) | ||||
| 				prefix += "/"; // TODO seperator?
 | ||||
| 			prefix += mod_name; | ||||
| 		} | ||||
| 
 | ||||
| 		for (auto* wire : mod->wires()) { | ||||
| 			std::string name = wire->name.str(); | ||||
| 			log_assert(name.length()); | ||||
| 			// TODO: really skip internal wires?
 | ||||
| 			if (name[0] == '$') | ||||
| 				continue; | ||||
| 			name = name.substr(1); | ||||
| 			std::string path = prefix; | ||||
| 			if (path.length()) | ||||
| 				path += "/"; | ||||
| 			path += name; | ||||
| 			design_nets.push_back(std::make_pair(path, wire)); | ||||
| 		} | ||||
| 
 | ||||
| 		for (auto* cell : mod->cells()) { | ||||
| 			std::string name = cell->name.str(); | ||||
| 			// TODO: really skip internal cells?
 | ||||
| 			if (name[0] == '$') | ||||
| 				continue; | ||||
| 			name = name.substr(1); | ||||
| 			std::string path = prefix; | ||||
| 			if (path.length()) | ||||
| 				path += "/"; | ||||
| 			path += name; | ||||
| 			design_cells.push_back(std::make_pair(path, cell)); | ||||
| 			for (auto pin : cell->connections()) { | ||||
| 				std::string pin_name = path + "/" + pin.first.str().substr(1); | ||||
| 				design_pins.push_back(std::make_pair(pin_name, cell)); | ||||
| 			} | ||||
| 			if (auto sub_mod = mod->design->module(cell->type)) { | ||||
| 				hierarchy.push_back(name); | ||||
| 				sniff_module(hierarchy, sub_mod); | ||||
| 				hierarchy.pop_back(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	SdcObjects(Design* design) { | ||||
| 		Module* top = design->top_module(); | ||||
| 		if (!top) | ||||
| 			log_error("Top module couldn't be determined. Check 'top' attribute usage"); | ||||
| 		for (auto port : top->ports) { | ||||
| 			design_ports.push_back(port.str().substr(1)); | ||||
| 		} | ||||
| 		std::list<std::string> hierarchy{}; | ||||
| 		sniff_module(hierarchy, top); | ||||
| 	} | ||||
| 	~SdcObjects() = default; | ||||
| 	void dump() { | ||||
| 		log("Design ports:\n"); | ||||
| 		for (auto name : design_ports) { | ||||
| 			log("\t%s\n", name.c_str()); | ||||
| 		} | ||||
| 		log("Design cells:\n"); | ||||
| 		for (auto [name, cell] : design_cells) { | ||||
| 			(void)cell; | ||||
| 			log("\t%s\n", name.c_str()); | ||||
| 		} | ||||
| 		log("Design pins:\n"); | ||||
| 		for (auto [name, pin] : design_pins) { | ||||
| 			(void)pin; | ||||
| 			log("\t%s\n", name.c_str()); | ||||
| 		} | ||||
| 		log("Design nets:\n"); | ||||
| 		for (auto [name, net] : design_nets) { | ||||
| 			(void)net; | ||||
| 			log("\t%s\n", name.c_str()); | ||||
| 		} | ||||
| 		log("\n"); | ||||
| 		log("Constrained ports:\n"); | ||||
| 		for (auto name : constrained_ports) { | ||||
| 			log("\t%s\n", name.c_str()); | ||||
| 		} | ||||
| 		log("Constrained cells:\n"); | ||||
| 		for (auto [name, cell] : constrained_cells) { | ||||
| 			(void)cell; | ||||
| 			log("\t%s\n", name.c_str()); | ||||
| 		} | ||||
| 		log("Constrained pins:\n"); | ||||
| 		for (auto [name, pin] : constrained_pins) { | ||||
| 			(void)pin; | ||||
| 			log("\t%s\n", name.c_str()); | ||||
| 		} | ||||
| 		log("Constrained nets:\n"); | ||||
| 		for (auto [name, net] : constrained_nets) { | ||||
| 			(void)net; | ||||
| 			log("\t%s\n", name.c_str()); | ||||
| 		} | ||||
| 		log("\n"); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| template<typename T> | ||||
| static bool parse_flag(char* arg, const char* flag_name, T& flag_var) { | ||||
| 	std::string expected = std::string("-") + flag_name; | ||||
|  | @ -23,9 +134,10 @@ static bool parse_flag(char* arg, const char* flag_name, T& flag_var) { | |||
| // TODO vectors
 | ||||
| // TODO cell arrays?
 | ||||
| 
 | ||||
| static int sdc_get_pins_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[]) | ||||
| static int sdc_get_pins_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[]) | ||||
| { | ||||
| 	(void)interp; | ||||
| 	auto* objects = (SdcObjects*)data; | ||||
| 	// When this flag is present, the search for the pattern is made in all positions in the hierarchy.
 | ||||
| 	bool hierarchical_flag = false; | ||||
| 	bool regexp_flag = false; | ||||
|  | @ -53,9 +165,16 @@ static int sdc_get_pins_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* c | |||
| 	for (; i < objc; i++) { | ||||
| 		patterns.push_back(Tcl_GetString(objv[i])); | ||||
| 	} | ||||
| 	log("get_pins patterns:\n"); | ||||
| 	for (auto pat : patterns) { | ||||
| 		log("\t%s\n", pat.c_str()); | ||||
| 		bool found = false; | ||||
| 		for (auto [name, pin] : objects->design_pins) { | ||||
| 			if (name == pat) { | ||||
| 				found = true; | ||||
| 				objects->constrained_pins.insert(std::make_pair(name, pin)); | ||||
| 			} | ||||
| 		} | ||||
| 		if (!found) | ||||
| 			log_warning("No matches in design for pattern %s\n", pat.c_str()); | ||||
| 	} | ||||
| 	(void)hierarchical_flag; | ||||
| 	(void)regexp_flag; | ||||
|  | @ -68,9 +187,10 @@ static int sdc_get_pins_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* c | |||
| 	return TCL_OK; | ||||
| } | ||||
| 
 | ||||
| static int sdc_get_ports_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[]) | ||||
| static int sdc_get_ports_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[]) | ||||
| { | ||||
| 	(void)interp; | ||||
| 	auto* objects = (SdcObjects*)data; | ||||
| 	bool regexp_flag = false; | ||||
| 	bool nocase_flag = false; | ||||
| 	std::vector<std::string> patterns; | ||||
|  | @ -84,20 +204,29 @@ static int sdc_get_ports_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* | |||
| 	for (; i < objc; i++) { | ||||
| 		patterns.push_back(Tcl_GetString(objv[i])); | ||||
| 	} | ||||
| 	log("get_ports patterns:\n"); | ||||
| 	for (auto pat : patterns) { | ||||
| 		log("\t%s\n", pat.c_str()); | ||||
| 		bool found = false; | ||||
| 		for (auto name : objects->design_ports) { | ||||
| 			if (name == pat) { | ||||
| 				found = true; | ||||
| 				objects->constrained_ports.insert(name); | ||||
| 			} | ||||
| 		} | ||||
| 		if (!found) | ||||
| 			log_warning("No matches in design for pattern %s\n", pat.c_str()); | ||||
| 	} | ||||
| 	(void)regexp_flag; | ||||
| 	(void)nocase_flag; | ||||
| 	return TCL_OK; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| class SDCInterpreter | ||||
| { | ||||
| private: | ||||
| 	Tcl_Interp* interp = nullptr; | ||||
| public: | ||||
| 	std::unique_ptr<SdcObjects> objects; | ||||
| 	~SDCInterpreter() { | ||||
| 		if (interp) | ||||
| 			Tcl_DeleteInterp(interp); | ||||
|  | @ -106,105 +235,21 @@ public: | |||
| 		static SDCInterpreter instance; | ||||
| 		return instance; | ||||
| 	} | ||||
| 	Tcl_Interp* fresh_interp() { | ||||
| 	Tcl_Interp* fresh_interp(Design* design) { | ||||
| 		if (interp) | ||||
| 			Tcl_DeleteInterp(interp); | ||||
| 
 | ||||
| 		interp = Tcl_CreateInterp(); | ||||
| 		if (Tcl_Init(interp)!=TCL_OK) | ||||
| 			log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno())); | ||||
| 		Tcl_CreateObjCommand(interp, "get_pins", sdc_get_pins_cmd, NULL, NULL); | ||||
| 		Tcl_CreateObjCommand(interp, "get_ports", sdc_get_ports_cmd, NULL, NULL); | ||||
| 			log_error("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno())); | ||||
| 
 | ||||
| 		objects = std::make_unique<SdcObjects>(design); | ||||
| 		Tcl_CreateObjCommand(interp, "get_pins", sdc_get_pins_cmd, (ClientData) objects.get(), NULL); | ||||
| 		Tcl_CreateObjCommand(interp, "get_ports", sdc_get_ports_cmd, (ClientData) objects.get(), NULL); | ||||
| 		return interp; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct SdcObjects { | ||||
| 	std::vector<std::string> ports; | ||||
| 	std::vector<std::pair<std::string, Cell*>> cells; | ||||
| 	std::vector<std::pair<std::string, Cell*>> pins; | ||||
| 	std::vector<std::pair<std::string, Wire*>> nets; | ||||
| 
 | ||||
| 	void sniff_module(std::list<std::string>& hierarchy, Module* mod) { | ||||
| 		log_debug("sniffing module %s\n", mod->name.c_str()); | ||||
| 
 | ||||
| 		std::string prefix; | ||||
| 		for (auto mod_name : hierarchy) { | ||||
| 			if (prefix.length()) | ||||
| 				prefix += "/"; // TODO seperator?
 | ||||
| 			prefix += mod_name; | ||||
| 		} | ||||
| 
 | ||||
| 		for (auto* wire : mod->wires()) { | ||||
| 			std::string name = wire->name.str(); | ||||
| 			log_assert(name.length()); | ||||
| 			// TODO: really skip internal wires?
 | ||||
| 			if (name[0] == '$') | ||||
| 				continue; | ||||
| 			name = name.substr(1); | ||||
| 			std::string path = prefix; | ||||
| 			if (path.length()) | ||||
| 				path += "/"; | ||||
| 			path += name; | ||||
| 			nets.push_back(std::make_pair(path, wire)); | ||||
| 		} | ||||
| 
 | ||||
| 		for (auto* cell : mod->cells()) { | ||||
| 			std::string name = cell->name.str(); | ||||
| 			// TODO: really skip internal cells?
 | ||||
| 			if (name[0] == '$') | ||||
| 				continue; | ||||
| 			name = name.substr(1); | ||||
| 			std::string path = prefix; | ||||
| 			if (path.length()) | ||||
| 				path += "/"; | ||||
| 			path += name; | ||||
| 			cells.push_back(std::make_pair(path, cell)); | ||||
| 			for (auto pin : cell->connections()) { | ||||
| 				std::string pin_name = path + "/" + pin.first.str().substr(1); | ||||
| 				pins.push_back(std::make_pair(pin_name, cell)); | ||||
| 			} | ||||
| 			if (auto sub_mod = mod->design->module(cell->type)) { | ||||
| 				hierarchy.push_back(name); | ||||
| 				sniff_module(hierarchy, sub_mod); | ||||
| 				hierarchy.pop_back(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	SdcObjects(Design* design) { | ||||
| 		Module* top = design->top_module(); | ||||
| 		if (!top) | ||||
| 			log_error("Top module couldn't be determined. Check 'top' attribute usage"); | ||||
| 		for (auto port : top->ports) { | ||||
| 			ports.push_back(port.str().substr(1)); | ||||
| 		} | ||||
| 		std::list<std::string> hierarchy{}; | ||||
| 		sniff_module(hierarchy, top); | ||||
| 	} | ||||
| 	void dump() { | ||||
| 		log("Dumping detected design objects visible to SDC constraints\n"); | ||||
| 		log("Ports:\n"); | ||||
| 		for (auto name : ports) { | ||||
| 			log("\t%s\n", name.c_str()); | ||||
| 		} | ||||
| 		log("Cells:\n"); | ||||
| 		for (auto [name, cell] : cells) { | ||||
| 			(void)cell; | ||||
| 			log("\t%s\n", name.c_str()); | ||||
| 		} | ||||
| 		log("Pins:\n"); | ||||
| 		for (auto [name, pin] : pins) { | ||||
| 			(void)pin; | ||||
| 			log("\t%s\n", name.c_str()); | ||||
| 		} | ||||
| 		log("Nets:\n"); | ||||
| 		for (auto [name, net] : nets) { | ||||
| 			(void)net; | ||||
| 			log("\t%s\n", name.c_str()); | ||||
| 		} | ||||
| 		log("\n"); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| // Also see TclPass
 | ||||
| struct SdcPass : public Pass { | ||||
| 	// TODO help
 | ||||
|  | @ -213,16 +258,16 @@ struct SdcPass : public Pass { | |||
| 		if (args.size() < 2) | ||||
| 			log_cmd_error("Missing SDC file.\n"); | ||||
| 		// TODO optional extra stub file
 | ||||
| 		Tcl_Interp *interp = SDCInterpreter::get().fresh_interp(); | ||||
| 		SDCInterpreter& sdc = SDCInterpreter::get(); | ||||
| 		Tcl_Interp *interp = sdc.fresh_interp(design); | ||||
| 		Tcl_Preserve(interp); | ||||
| 		std::string stub_path = "+/sdc/stubs.sdc"; | ||||
| 		rewrite_filename(stub_path); | ||||
| 		SdcObjects objects(design); | ||||
| 		objects.dump(); | ||||
| 		if (Tcl_EvalFile(interp, stub_path.c_str()) != TCL_OK) | ||||
| 			log_cmd_error("SDC interpreter returned an error in stub file: %s\n", Tcl_GetStringResult(interp)); | ||||
| 		if (Tcl_EvalFile(interp, args[1].c_str()) != TCL_OK) | ||||
| 			log_cmd_error("SDC interpreter returned an error: %s\n", Tcl_GetStringResult(interp)); | ||||
| 		sdc.objects->dump(); | ||||
| 		Tcl_Release(interp); | ||||
| 	} | ||||
| } SdcPass; | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| puts "SDC constraints file says hello from arbitrary Tcl execution" | ||||
| set_false_path -from [get_pins s1/sa] -to [get_pins s1/sb] | ||||
| puts "This should print something:" | ||||
| puts [get_ports {slink_clk_o slink_*_o}] | ||||
| puts [get_ports a b] | ||||
| puts "Did it?" | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue