mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	Merge branch 'YosysHQ:main' into main
This commit is contained in:
		
						commit
						caaef5ac14
					
				
					 21 changed files with 432 additions and 137 deletions
				
			
		
							
								
								
									
										29
									
								
								CHANGELOG
									
										
									
									
									
								
							
							
						
						
									
										29
									
								
								CHANGELOG
									
										
									
									
									
								
							|  | @ -2,9 +2,36 @@ | ||||||
| List of major changes and improvements between releases | List of major changes and improvements between releases | ||||||
| ======================================================= | ======================================================= | ||||||
| 
 | 
 | ||||||
| Yosys 0.47 .. Yosys 0.48-dev | Yosys 0.48 .. Yosys 0.49-dev | ||||||
| -------------------------- | -------------------------- | ||||||
| 
 | 
 | ||||||
|  | Yosys 0.47 .. Yosys 0.48 | ||||||
|  | -------------------------- | ||||||
|  |  * Various | ||||||
|  |     - Removed "read_ilang" deprecated pass. | ||||||
|  |     - Enhanced boxing features in the experimental "abc_new" command. | ||||||
|  |     - Added new Tcl methods for design inspection. | ||||||
|  |     - Added clock enable inference to "dfflibmap". | ||||||
|  |     - Added a Han-Carlson and Sklansky option for $lcu mapping. | ||||||
|  | 
 | ||||||
|  |  * New commands and options | ||||||
|  |     - Added "-nopeepopt" option to "clk2fflogic" pass. | ||||||
|  |     - Added "-liberty" and "-dont_use" options to "clockgate" pass. | ||||||
|  |     - Added "-ignore_buses" option to "read_liberty" pass. | ||||||
|  |     - Added "-dont_map" option to "techmap" pass. | ||||||
|  |     - Added "-selected" option to "write_json" pass. | ||||||
|  |     - Added "wrapcell" command for creating wrapper modules | ||||||
|  |       around selected cells. | ||||||
|  |     - Added "portarcs" command for deriving propagation timing arcs. | ||||||
|  |     - Added "setenv" command for setting environment variables. | ||||||
|  | 
 | ||||||
|  |  * Gowin support | ||||||
|  |     - Added "-family" option to "synth_gowin" pass. | ||||||
|  |     - Cell definitions split by family.  | ||||||
|  | 
 | ||||||
|  |  * Verific support | ||||||
|  |     - Improved blackbox support. | ||||||
|  | 
 | ||||||
| Yosys 0.46 .. Yosys 0.47 | Yosys 0.46 .. Yosys 0.47 | ||||||
| -------------------------- | -------------------------- | ||||||
|  * Various |  * Various | ||||||
|  |  | ||||||
							
								
								
									
										6
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -168,7 +168,7 @@ ifeq ($(OS), Haiku) | ||||||
| CXXFLAGS += -D_DEFAULT_SOURCE | CXXFLAGS += -D_DEFAULT_SOURCE | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| YOSYS_VER := 0.47+211 | YOSYS_VER := 0.48+0 | ||||||
| 
 | 
 | ||||||
| # Note: We arrange for .gitcommit to contain the (short) commit hash in
 | # Note: We arrange for .gitcommit to contain the (short) commit hash in
 | ||||||
| # tarballs generated with git-archive(1) using .gitattributes. The git repo
 | # tarballs generated with git-archive(1) using .gitattributes. The git repo
 | ||||||
|  | @ -184,7 +184,7 @@ endif | ||||||
| OBJS = kernel/version_$(GIT_REV).o | OBJS = kernel/version_$(GIT_REV).o | ||||||
| 
 | 
 | ||||||
| bumpversion: | bumpversion: | ||||||
| 	sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 647d61d.. | wc -l`/;" Makefile | 	sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline aaa5347.. | wc -l`/;" Makefile | ||||||
| 
 | 
 | ||||||
| ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) | ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) | ||||||
| 
 | 
 | ||||||
|  | @ -318,7 +318,7 @@ endif | ||||||
| else ifeq ($(CONFIG),mxe) | else ifeq ($(CONFIG),mxe) | ||||||
| PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config | PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config | ||||||
| CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++ | CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++ | ||||||
| CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) -D_POSIX_SOURCE -DYOSYS_MXE_HACKS -Wno-attributes | CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) -D_POSIX_SOURCE -Wno-attributes | ||||||
| CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) | CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) | ||||||
| LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s | LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s | ||||||
| LIBS := $(filter-out -lrt,$(LIBS)) | LIBS := $(filter-out -lrt,$(LIBS)) | ||||||
|  |  | ||||||
|  | @ -832,12 +832,8 @@ struct XAigerAnalysis : Index<XAigerAnalysis, int, 0, 0> { | ||||||
| 			return false; | 			return false; | ||||||
| 
 | 
 | ||||||
| 		Cell *driver = bit.wire->driverCell(); | 		Cell *driver = bit.wire->driverCell(); | ||||||
| 		if (!driver->type.isPublic()) |  | ||||||
| 			return false; |  | ||||||
| 
 |  | ||||||
| 		Module *mod = design->module(driver->type); | 		Module *mod = design->module(driver->type); | ||||||
| 		log_assert(mod); | 		if (!mod || !mod->has_attribute(ID::abc9_box_id)) | ||||||
| 		if (!mod->has_attribute(ID::abc9_box_id)) |  | ||||||
| 			return false; | 			return false; | ||||||
| 
 | 
 | ||||||
| 		int max = 1; | 		int max = 1; | ||||||
|  | @ -870,7 +866,7 @@ struct XAigerAnalysis : Index<XAigerAnalysis, int, 0, 0> { | ||||||
| 		HierCursor cursor; | 		HierCursor cursor; | ||||||
| 		for (auto box : top_minfo->found_blackboxes) { | 		for (auto box : top_minfo->found_blackboxes) { | ||||||
| 			Module *def = design->module(box->type); | 			Module *def = design->module(box->type); | ||||||
| 			if (!box->type.isPublic() || (def && !def->has_attribute(ID::abc9_box_id))) | 			if (!(def && def->has_attribute(ID::abc9_box_id))) | ||||||
| 			for (auto &conn : box->connections_) | 			for (auto &conn : box->connections_) | ||||||
| 			if (box->output(conn.first)) | 			if (box->output(conn.first)) | ||||||
| 			for (auto bit : conn.second) | 			for (auto bit : conn.second) | ||||||
|  | @ -885,7 +881,7 @@ struct XAigerAnalysis : Index<XAigerAnalysis, int, 0, 0> { | ||||||
| 
 | 
 | ||||||
| 		for (auto box : top_minfo->found_blackboxes) { | 		for (auto box : top_minfo->found_blackboxes) { | ||||||
| 			Module *def = design->module(box->type); | 			Module *def = design->module(box->type); | ||||||
| 			if (!box->type.isPublic() || (def && !def->has_attribute(ID::abc9_box_id))) | 			if (!(def && def->has_attribute(ID::abc9_box_id))) | ||||||
| 			for (auto &conn : box->connections_) | 			for (auto &conn : box->connections_) | ||||||
| 			if (box->input(conn.first)) | 			if (box->input(conn.first)) | ||||||
| 			for (auto bit : conn.second) | 			for (auto bit : conn.second) | ||||||
|  | @ -1106,7 +1102,7 @@ struct XAigerWriter : AigerWriter { | ||||||
| 							holes_module->ports.push_back(w->name); | 							holes_module->ports.push_back(w->name); | ||||||
| 							holes_pis.push_back(w); | 							holes_pis.push_back(w); | ||||||
| 						} | 						} | ||||||
| 						in_conn.append(holes_pis[i]); | 						in_conn.append(holes_pis[holes_pi_idx]); | ||||||
| 						holes_pi_idx++; | 						holes_pi_idx++; | ||||||
| 					} | 					} | ||||||
| 					holes_wb->setPort(port_id, in_conn); | 					holes_wb->setPort(port_id, in_conn); | ||||||
|  |  | ||||||
|  | @ -1087,7 +1087,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) | ||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (cell->type == ID($_BUF_)) { | 	if (cell->type.in(ID($_BUF_), ID($buf))) { | ||||||
| 		f << stringf("%s" "assign ", indent.c_str()); | 		f << stringf("%s" "assign ", indent.c_str()); | ||||||
| 		dump_sigspec(f, cell->getPort(ID::Y)); | 		dump_sigspec(f, cell->getPort(ID::Y)); | ||||||
| 		f << stringf(" = "); | 		f << stringf(" = "); | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ import os | ||||||
| project = 'YosysHQ Yosys' | project = 'YosysHQ Yosys' | ||||||
| author = 'YosysHQ GmbH' | author = 'YosysHQ GmbH' | ||||||
| copyright ='2024 YosysHQ GmbH' | copyright ='2024 YosysHQ GmbH' | ||||||
| yosys_ver = "0.47" | yosys_ver = "0.48" | ||||||
| 
 | 
 | ||||||
| # select HTML theme | # select HTML theme | ||||||
| html_theme = 'furo-ys' | html_theme = 'furo-ys' | ||||||
|  |  | ||||||
|  | @ -203,7 +203,6 @@ struct Xaiger2Frontend : public Frontend { | ||||||
| 					/* unused box_id = */ read_be32(*f); | 					/* unused box_id = */ read_be32(*f); | ||||||
| 					auto box_seq = read_be32(*f); | 					auto box_seq = read_be32(*f); | ||||||
| 
 | 
 | ||||||
| 					log("box_seq=%d boxes.size=%d\n", box_seq, (int) boxes.size()); |  | ||||||
| 					log_assert(box_seq < boxes.size()); | 					log_assert(box_seq < boxes.size()); | ||||||
| 
 | 
 | ||||||
| 					auto [cell, def] = boxes[box_seq]; | 					auto [cell, def] = boxes[box_seq]; | ||||||
|  |  | ||||||
|  | @ -572,6 +572,8 @@ struct LibertyFrontend : public Frontend { | ||||||
| 			for (auto &attr : attributes) | 			for (auto &attr : attributes) | ||||||
| 				module->attributes[attr] = 1; | 				module->attributes[attr] = 1; | ||||||
| 
 | 
 | ||||||
|  | 			bool simple_comb_cell = true, has_outputs = false; | ||||||
|  | 
 | ||||||
| 			for (auto node : cell->children) | 			for (auto node : cell->children) | ||||||
| 			{ | 			{ | ||||||
| 				if (node->id == "pin" && node->args.size() == 1) { | 				if (node->id == "pin" && node->args.size() == 1) { | ||||||
|  | @ -613,6 +615,8 @@ struct LibertyFrontend : public Frontend { | ||||||
| 					if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal")) | 					if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal")) | ||||||
| 						log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0).c_str(), log_id(module->name)); | 						log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0).c_str(), log_id(module->name)); | ||||||
| 
 | 
 | ||||||
|  | 					simple_comb_cell = false; | ||||||
|  | 
 | ||||||
| 					if (dir->value == "internal") | 					if (dir->value == "internal") | ||||||
| 						continue; | 						continue; | ||||||
| 
 | 
 | ||||||
|  | @ -660,6 +664,9 @@ struct LibertyFrontend : public Frontend { | ||||||
| 				{ | 				{ | ||||||
| 					const LibertyAst *dir = node->find("direction"); | 					const LibertyAst *dir = node->find("direction"); | ||||||
| 
 | 
 | ||||||
|  | 					if (dir->value == "internal" || dir->value == "inout") | ||||||
|  | 						simple_comb_cell = false; | ||||||
|  | 
 | ||||||
| 					if (flag_lib && dir->value == "internal") | 					if (flag_lib && dir->value == "internal") | ||||||
| 						continue; | 						continue; | ||||||
| 
 | 
 | ||||||
|  | @ -680,8 +687,10 @@ struct LibertyFrontend : public Frontend { | ||||||
| 						continue; | 						continue; | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					if (dir && dir->value == "output") | 					if (dir && dir->value == "output") { | ||||||
|  | 						has_outputs = true; | ||||||
| 						wire->port_output = true; | 						wire->port_output = true; | ||||||
|  | 					} | ||||||
| 
 | 
 | ||||||
| 					if (flag_lib) | 					if (flag_lib) | ||||||
| 						continue; | 						continue; | ||||||
|  | @ -699,36 +708,35 @@ struct LibertyFrontend : public Frontend { | ||||||
| 								goto skip_cell; | 								goto skip_cell; | ||||||
| 							} | 							} | ||||||
| 						} | 						} | ||||||
|  | 						simple_comb_cell = false; | ||||||
| 					} else { | 					} else { | ||||||
| 						RTLIL::SigSpec out_sig = parse_func_expr(module, func->value.c_str()); | 						RTLIL::SigSpec out_sig = parse_func_expr(module, func->value.c_str()); | ||||||
| 						const LibertyAst *three_state = node->find("three_state"); | 						const LibertyAst *three_state = node->find("three_state"); | ||||||
| 						if (three_state) { | 						if (three_state) { | ||||||
| 							out_sig = create_tristate(module, out_sig, three_state->value.c_str()); | 							out_sig = create_tristate(module, out_sig, three_state->value.c_str()); | ||||||
|  | 							simple_comb_cell = false; | ||||||
| 						} | 						} | ||||||
| 						module->connect(RTLIL::SigSig(wire, out_sig)); | 						module->connect(RTLIL::SigSig(wire, out_sig)); | ||||||
| 					} | 					} | ||||||
|  | 				} | ||||||
| 
 | 
 | ||||||
| 					if (flag_unit_delay) { | 				if (node->id == "ff" || node->id == "ff_bank" || | ||||||
| 						pool<Wire *> done; | 						node->id == "latch" || node->id == "latch_bank" || | ||||||
|  | 						node->id == "statetable") | ||||||
|  | 					simple_comb_cell = false; | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 						for (auto timing : node->children) | 			if (simple_comb_cell && has_outputs) { | ||||||
| 						if (timing->id == "timing" && timing->args.empty()) { | 				module->set_bool_attribute(ID::abc9_box); | ||||||
| 							auto type = timing->find("timing_type"); |  | ||||||
| 							auto related_pin = timing->find("related_pin"); |  | ||||||
| 							if (!type || type->value != "combinational" || !related_pin) |  | ||||||
| 								continue; |  | ||||||
| 
 |  | ||||||
| 							Wire *related = module->wire(RTLIL::escape_id(related_pin->value)); |  | ||||||
| 							if (!related) |  | ||||||
| 								log_error("Failed to find related pin %s for timing of pin %s on %s\n", |  | ||||||
| 										  related_pin->value.c_str(), log_id(wire), log_id(module)); |  | ||||||
| 
 |  | ||||||
| 							if (done.count(related)) |  | ||||||
| 								continue; |  | ||||||
| 
 | 
 | ||||||
|  | 				if (flag_unit_delay) { | ||||||
|  | 					for (auto wi : module->wires()) | ||||||
|  | 					if (wi->port_input) { | ||||||
|  | 						for (auto wo : module->wires()) | ||||||
|  | 						if (wo->port_output) { | ||||||
| 							RTLIL::Cell *spec = module->addCell(NEW_ID, ID($specify2)); | 							RTLIL::Cell *spec = module->addCell(NEW_ID, ID($specify2)); | ||||||
| 							spec->setParam(ID::SRC_WIDTH, 1); | 							spec->setParam(ID::SRC_WIDTH, wi->width); | ||||||
| 							spec->setParam(ID::DST_WIDTH, 1); | 							spec->setParam(ID::DST_WIDTH, wo->width); | ||||||
| 							spec->setParam(ID::T_FALL_MAX, 1000); | 							spec->setParam(ID::T_FALL_MAX, 1000); | ||||||
| 							spec->setParam(ID::T_FALL_TYP, 1000); | 							spec->setParam(ID::T_FALL_TYP, 1000); | ||||||
| 							spec->setParam(ID::T_FALL_MIN, 1000); | 							spec->setParam(ID::T_FALL_MIN, 1000); | ||||||
|  | @ -737,11 +745,10 @@ struct LibertyFrontend : public Frontend { | ||||||
| 							spec->setParam(ID::T_RISE_MIN, 1000); | 							spec->setParam(ID::T_RISE_MIN, 1000); | ||||||
| 							spec->setParam(ID::SRC_DST_POL, false); | 							spec->setParam(ID::SRC_DST_POL, false); | ||||||
| 							spec->setParam(ID::SRC_DST_PEN, false); | 							spec->setParam(ID::SRC_DST_PEN, false); | ||||||
| 							spec->setParam(ID::FULL, false); | 							spec->setParam(ID::FULL, true); | ||||||
| 							spec->setPort(ID::EN, Const(1, 1)); | 							spec->setPort(ID::EN, Const(1, 1)); | ||||||
| 							spec->setPort(ID::SRC, related); | 							spec->setPort(ID::SRC, wi); | ||||||
| 							spec->setPort(ID::DST, wire); | 							spec->setPort(ID::DST, wo); | ||||||
| 							done.insert(related); |  | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | @ -31,6 +31,10 @@ | ||||||
| #  include <editline/readline.h> | #  include <editline/readline.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifdef YOSYS_ENABLE_TCL | ||||||
|  | #  include <tcl.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <limits.h> | #include <limits.h> | ||||||
|  |  | ||||||
|  | @ -18,8 +18,9 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #ifdef YOSYS_ENABLE_TCL | #ifdef YOSYS_ENABLE_TCL | ||||||
| #include "tclTomMath.h" | #include <tcl.h> | ||||||
| #include "tclTomMathDecls.h" | #include <tclTomMath.h> | ||||||
|  | #include <tclTomMathDecls.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include "kernel/yosys.h" | #include "kernel/yosys.h" | ||||||
|  |  | ||||||
|  | @ -31,6 +31,10 @@ | ||||||
| #  include <editline/readline.h> | #  include <editline/readline.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifdef YOSYS_ENABLE_TCL | ||||||
|  | #  include <tcl.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef YOSYS_ENABLE_PLUGINS | #ifdef YOSYS_ENABLE_PLUGINS | ||||||
| #  include <dlfcn.h> | #  include <dlfcn.h> | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -43,6 +43,10 @@ | ||||||
| #include "kernel/rtlil.h" | #include "kernel/rtlil.h" | ||||||
| #include "kernel/register.h" | #include "kernel/register.h" | ||||||
| 
 | 
 | ||||||
|  | #ifdef YOSYS_ENABLE_TCL | ||||||
|  | struct Tcl_Interp; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| YOSYS_NAMESPACE_BEGIN | YOSYS_NAMESPACE_BEGIN | ||||||
| 
 | 
 | ||||||
| void yosys_setup(); | void yosys_setup(); | ||||||
|  |  | ||||||
|  | @ -65,29 +65,6 @@ | ||||||
| #define FRIEND_TEST(test_case_name, test_name) \ | #define FRIEND_TEST(test_case_name, test_name) \ | ||||||
|   friend class test_case_name##_##test_name##_Test |   friend class test_case_name##_##test_name##_Test | ||||||
| 
 | 
 | ||||||
| #ifdef YOSYS_ENABLE_TCL |  | ||||||
| #  include <tcl.h> |  | ||||||
| #  ifdef YOSYS_MXE_HACKS |  | ||||||
| extern Tcl_Command Tcl_CreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); |  | ||||||
| extern Tcl_Interp *Tcl_CreateInterp(void); |  | ||||||
| extern void Tcl_Preserve(ClientData data); |  | ||||||
| extern void Tcl_Release(ClientData clientData); |  | ||||||
| extern int Tcl_InterpDeleted(Tcl_Interp *interp); |  | ||||||
| extern void Tcl_DeleteInterp(Tcl_Interp *interp); |  | ||||||
| extern int Tcl_Eval(Tcl_Interp *interp, const char *script); |  | ||||||
| extern int Tcl_EvalFile(Tcl_Interp *interp, const char *fileName); |  | ||||||
| extern void Tcl_Finalize(void); |  | ||||||
| extern int Tcl_GetCommandInfo(Tcl_Interp *interp, const char *cmdName, Tcl_CmdInfo *infoPtr); |  | ||||||
| extern const char *Tcl_GetStringResult(Tcl_Interp *interp); |  | ||||||
| extern Tcl_Obj *Tcl_NewStringObj(const char *bytes, int length); |  | ||||||
| extern Tcl_Obj *Tcl_NewIntObj(int intValue); |  | ||||||
| extern Tcl_Obj *Tcl_NewListObj(int objc, Tcl_Obj *const objv[]); |  | ||||||
| extern Tcl_Obj *Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags); |  | ||||||
| #  endif |  | ||||||
| #  undef CONST |  | ||||||
| #  undef INLINE |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| #  undef NOMINMAX | #  undef NOMINMAX | ||||||
| #  define NOMINMAX 1 | #  define NOMINMAX 1 | ||||||
|  |  | ||||||
|  | @ -18,12 +18,37 @@ | ||||||
|  */ |  */ | ||||||
| #include "kernel/yosys.h" | #include "kernel/yosys.h" | ||||||
| #include "kernel/celltypes.h" | #include "kernel/celltypes.h" | ||||||
|  | #include "kernel/sigtools.h" | ||||||
| #include "backends/rtlil/rtlil_backend.h" | #include "backends/rtlil/rtlil_backend.h" | ||||||
| 
 | 
 | ||||||
| USING_YOSYS_NAMESPACE | USING_YOSYS_NAMESPACE | ||||||
| PRIVATE_NAMESPACE_BEGIN | PRIVATE_NAMESPACE_BEGIN | ||||||
| 
 | 
 | ||||||
| std::optional<std::string> format(std::string fmt, const dict<IdString, Const> ¶meters) | bool has_fmt_field(std::string fmt, std::string field_name) | ||||||
|  | { | ||||||
|  | 	auto it = fmt.begin(); | ||||||
|  | 	while (it != fmt.end()) { | ||||||
|  | 		if (*it == '{') { | ||||||
|  | 			it++; | ||||||
|  | 			auto beg = it; | ||||||
|  | 			while (it != fmt.end() && *it != '}') it++; | ||||||
|  | 			if (it == fmt.end()) | ||||||
|  | 				return false; | ||||||
|  | 
 | ||||||
|  | 			if (std::string(beg, it) == field_name) | ||||||
|  | 				return true; | ||||||
|  | 		} | ||||||
|  | 		it++; | ||||||
|  | 	} | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct ContextData { | ||||||
|  | 	std::string unused_outputs; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | std::optional<std::string> format(std::string fmt, const dict<IdString, Const> ¶meters, | ||||||
|  | 								  const ContextData &context) | ||||||
| { | { | ||||||
| 	std::stringstream result; | 	std::stringstream result; | ||||||
| 
 | 
 | ||||||
|  | @ -38,13 +63,19 @@ std::optional<std::string> format(std::string fmt, const dict<IdString, Const> & | ||||||
| 				return {}; | 				return {}; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			auto id = RTLIL::escape_id(std::string(beg, it)); | 			std::string param_name = {beg, it}; | ||||||
| 			if (!parameters.count(id)) { |  | ||||||
| 				log("Parameter %s referenced in format string '%s' not found\n", log_id(id), fmt.c_str()); |  | ||||||
| 				return {}; |  | ||||||
| 			} |  | ||||||
| 
 | 
 | ||||||
| 			RTLIL_BACKEND::dump_const(result, parameters.at(id)); | 			if (param_name == "%unused") { | ||||||
|  | 				result << context.unused_outputs; | ||||||
|  | 			} else { | ||||||
|  | 				auto id = RTLIL::escape_id(std::string(beg, it)); | ||||||
|  | 				if (!parameters.count(id)) { | ||||||
|  | 					log("Parameter %s referenced in format string '%s' not found\n", log_id(id), fmt.c_str()); | ||||||
|  | 					return {}; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				RTLIL_BACKEND::dump_const(result, parameters.at(id)); | ||||||
|  | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			result << *it; | 			result << *it; | ||||||
| 		} | 		} | ||||||
|  | @ -54,6 +85,45 @@ std::optional<std::string> format(std::string fmt, const dict<IdString, Const> & | ||||||
| 	return {result.str()}; | 	return {result.str()}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | struct Chunk { | ||||||
|  | 	IdString port; | ||||||
|  | 	int base, len; | ||||||
|  | 
 | ||||||
|  | 	Chunk(IdString id, int base, int len) | ||||||
|  | 		: port(id), base(base), len(len) {} | ||||||
|  | 
 | ||||||
|  | 	IdString format(Cell *cell) | ||||||
|  | 	{ | ||||||
|  | 		if (len == cell->getPort(port).size()) | ||||||
|  | 			return port; | ||||||
|  | 		else if (len == 1) | ||||||
|  | 			return stringf("%s[%d]", port.c_str(), base); | ||||||
|  | 		else | ||||||
|  | 			return stringf("%s[%d:%d]", port.c_str(), base + len - 1, base); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	SigSpec sample(Cell *cell) | ||||||
|  | 	{ | ||||||
|  | 		return cell->getPort(port).extract(base, len); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Joins contiguous runs of bits into a 'Chunk'
 | ||||||
|  | std::vector<Chunk> collect_chunks(std::vector<std::pair<IdString, int>> bits) | ||||||
|  | { | ||||||
|  | 	std::vector<Chunk> ret; | ||||||
|  | 	std::sort(bits.begin(), bits.end()); | ||||||
|  | 	for (auto it = bits.begin(); it != bits.end();) { | ||||||
|  | 		auto sep = it + 1; | ||||||
|  | 		for (; sep != bits.end() && | ||||||
|  | 				sep->first == it->first && | ||||||
|  | 				sep->second == (sep - 1)->second + 1; sep++); | ||||||
|  | 		ret.emplace_back(it->first, it->second, sep - it); | ||||||
|  | 		it = sep; | ||||||
|  | 	} | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| struct WrapcellPass : Pass { | struct WrapcellPass : Pass { | ||||||
| 	WrapcellPass() : Pass("wrapcell", "wrap individual cells into new modules") {} | 	WrapcellPass() : Pass("wrapcell", "wrap individual cells into new modules") {} | ||||||
| 
 | 
 | ||||||
|  | @ -68,6 +138,10 @@ struct WrapcellPass : Pass { | ||||||
| 		log("parameter values as specified in curly brackets. If the named module already\n"); | 		log("parameter values as specified in curly brackets. If the named module already\n"); | ||||||
| 		log("exists, it is reused.\n"); | 		log("exists, it is reused.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("If the template contains the special string '{%%unused}', the command tracks\n"); | ||||||
|  | 		log("unused output ports -- specialized wrapper modules will be generated per every\n"); | ||||||
|  | 		log("distinct set of unused port bits as appearing on any selected cell.\n"); | ||||||
|  | 		log("\n"); | ||||||
| 		log("    -setattr <attribute-name>\n"); | 		log("    -setattr <attribute-name>\n"); | ||||||
| 		log("        set the given boolean attribute on each created wrapper module\n"); | 		log("        set the given boolean attribute on each created wrapper module\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | @ -114,35 +188,81 @@ struct WrapcellPass : Pass { | ||||||
| 		CellTypes ct; | 		CellTypes ct; | ||||||
| 		ct.setup(); | 		ct.setup(); | ||||||
| 
 | 
 | ||||||
|  | 		bool tracking_unused = has_fmt_field(name_fmt, "%unused"); | ||||||
|  | 
 | ||||||
| 		for (auto module : d->selected_modules()) { | 		for (auto module : d->selected_modules()) { | ||||||
| 			for (auto cell : module->selected_cells()) { | 			SigPool unused; | ||||||
| 				std::optional<std::string> unescaped_name = format(name_fmt, cell->parameters); |  | ||||||
| 				if (!unescaped_name) |  | ||||||
| 					log_error("Formatting error when processing cell '%s' in module '%s'\n", |  | ||||||
| 							  log_id(cell), log_id(module)); |  | ||||||
| 
 | 
 | ||||||
| 				IdString name = RTLIL::escape_id(unescaped_name.value()); | 			for (auto wire : module->wires()) | ||||||
| 
 | 			if (wire->has_attribute(ID::unused_bits)) { | ||||||
| 				if (d->module(name)) { | 				std::string str = wire->get_string_attribute(ID::unused_bits); | ||||||
| 					cell->type = name; | 				for (auto it = str.begin(); it != str.end();) { | ||||||
| 					cell->parameters.clear(); | 					auto sep = it; | ||||||
| 					continue; | 					for (; sep != str.end() && *sep != ' '; sep++); | ||||||
|  | 					unused.add(SigBit(wire, std::stoi(std::string(it, sep)))); | ||||||
|  | 					for (it = sep; it != str.end() && *it == ' '; it++); | ||||||
| 				} | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			for (auto cell : module->selected_cells()) { | ||||||
|  | 				Module *subm; | ||||||
|  | 				Cell *subcell; | ||||||
| 
 | 
 | ||||||
| 				if (!ct.cell_known(cell->type)) | 				if (!ct.cell_known(cell->type)) | ||||||
| 					log_error("Non-internal cell type '%s' on cell '%s' in module '%s' unsupported\n", | 					log_error("Non-internal cell type '%s' on cell '%s' in module '%s' unsupported\n", | ||||||
| 							  log_id(cell->type), log_id(cell), log_id(module)); | 							  log_id(cell->type), log_id(cell), log_id(module)); | ||||||
| 
 | 
 | ||||||
| 				Module *subm = d->addModule(name); | 				std::vector<std::pair<IdString, int>> unused_outputs, used_outputs; | ||||||
| 				Cell *subcell = subm->addCell("$1", cell->type); |  | ||||||
| 				for (auto conn : cell->connections()) { | 				for (auto conn : cell->connections()) { | ||||||
| 					Wire *w = subm->addWire(conn.first, conn.second.size()); | 					if (ct.cell_output(cell->type, conn.first)) | ||||||
| 					if (ct.cell_output(cell->type, w->name)) | 					for (int i = 0; i < conn.second.size(); i++) { | ||||||
| 						w->port_output = true; | 						if (tracking_unused && unused.check(conn.second[i])) | ||||||
| 					else | 							unused_outputs.emplace_back(conn.first, i); | ||||||
| 						w->port_input = true; | 						else | ||||||
| 					subcell->setPort(conn.first, w); | 							used_outputs.emplace_back(conn.first, i); | ||||||
|  | 					} | ||||||
| 				} | 				} | ||||||
|  | 
 | ||||||
|  | 				ContextData context; | ||||||
|  | 				if (!unused_outputs.empty()) { | ||||||
|  | 					context.unused_outputs += "_unused"; | ||||||
|  | 					for (auto chunk : collect_chunks(unused_outputs)) | ||||||
|  | 						context.unused_outputs += "_" + RTLIL::unescape_id(chunk.format(cell)); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				std::optional<std::string> unescaped_name = format(name_fmt, cell->parameters, context); | ||||||
|  | 				if (!unescaped_name) | ||||||
|  | 					log_error("Formatting error when processing cell '%s' in module '%s'\n", | ||||||
|  | 							  log_id(cell), log_id(module)); | ||||||
|  | 
 | ||||||
|  | 				IdString name = RTLIL::escape_id(unescaped_name.value()); | ||||||
|  | 				if (d->module(name)) | ||||||
|  | 					goto replace_cell; | ||||||
|  | 
 | ||||||
|  | 				subm = d->addModule(name); | ||||||
|  | 				subcell = subm->addCell("$1", cell->type); | ||||||
|  | 				for (auto conn : cell->connections()) { | ||||||
|  | 					if (ct.cell_output(cell->type, conn.first)) { | ||||||
|  | 						// Insert marker bits as placehodlers which need to be replaced
 | ||||||
|  | 						subcell->setPort(conn.first, SigSpec(RTLIL::Sm, conn.second.size())); | ||||||
|  | 					} else { | ||||||
|  | 						Wire *w = subm->addWire(conn.first, conn.second.size()); | ||||||
|  | 						w->port_input = true; | ||||||
|  | 						subcell->setPort(conn.first, w); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				for (auto chunk : collect_chunks(used_outputs)) { | ||||||
|  | 					Wire *w = subm->addWire(chunk.format(cell), chunk.len); | ||||||
|  | 					w->port_output = true; | ||||||
|  | 					subcell->connections_[chunk.port].replace(chunk.base, w); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				for (auto chunk : collect_chunks(unused_outputs)) { | ||||||
|  | 					Wire *w = subm->addWire(chunk.format(cell), chunk.len); | ||||||
|  | 					subcell->connections_[chunk.port].replace(chunk.base, w); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
| 				subcell->parameters = cell->parameters; | 				subcell->parameters = cell->parameters; | ||||||
| 				subm->fixup_ports(); | 				subm->fixup_ports(); | ||||||
| 
 | 
 | ||||||
|  | @ -150,7 +270,7 @@ struct WrapcellPass : Pass { | ||||||
| 					if (rule.value_fmt.empty()) { | 					if (rule.value_fmt.empty()) { | ||||||
| 						subm->set_bool_attribute(rule.name); | 						subm->set_bool_attribute(rule.name); | ||||||
| 					} else { | 					} else { | ||||||
| 						std::optional<std::string> value = format(rule.value_fmt, cell->parameters); | 						std::optional<std::string> value = format(rule.value_fmt, cell->parameters, context); | ||||||
| 
 | 
 | ||||||
| 						if (!value) | 						if (!value) | ||||||
| 							log_error("Formatting error when processing cell '%s' in module '%s'\n", | 							log_error("Formatting error when processing cell '%s' in module '%s'\n", | ||||||
|  | @ -160,8 +280,20 @@ struct WrapcellPass : Pass { | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				cell->type = name; | 			replace_cell: | ||||||
| 				cell->parameters.clear(); | 				cell->parameters.clear(); | ||||||
|  | 
 | ||||||
|  | 				dict<IdString, SigSpec> new_connections; | ||||||
|  | 
 | ||||||
|  | 				for (auto conn : cell->connections()) | ||||||
|  | 				if (!ct.cell_output(cell->type, conn.first)) | ||||||
|  | 					new_connections[conn.first] = conn.second; | ||||||
|  | 
 | ||||||
|  | 				for (auto chunk : collect_chunks(used_outputs)) | ||||||
|  | 					new_connections[chunk.format(cell)] = chunk.sample(cell); | ||||||
|  | 
 | ||||||
|  | 				cell->type = name; | ||||||
|  | 				cell->connections_ = new_connections; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -969,13 +969,10 @@ void prep_box(RTLIL::Design *design) | ||||||
| 		if (it == module->attributes.end()) | 		if (it == module->attributes.end()) | ||||||
| 			continue; | 			continue; | ||||||
| 		bool box = it->second.as_bool(); | 		bool box = it->second.as_bool(); | ||||||
| 		module->attributes.erase(it); |  | ||||||
| 		if (!box) | 		if (!box) | ||||||
| 			continue; | 			continue; | ||||||
| 
 | 
 | ||||||
| 		auto r = module->attributes.insert(ID::abc9_box_id); | 		auto r = module->attributes.insert(ID::abc9_box_id); | ||||||
| 		if (!r.second) |  | ||||||
| 			continue; |  | ||||||
| 		r.first->second = abc9_box_id++; | 		r.first->second = abc9_box_id++; | ||||||
| 
 | 
 | ||||||
| 		if (module->get_bool_attribute(ID::abc9_flop)) { | 		if (module->get_bool_attribute(ID::abc9_flop)) { | ||||||
|  | @ -1078,7 +1075,8 @@ void prep_box(RTLIL::Design *design) | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			ss << log_id(module) << " " << module->attributes.at(ID::abc9_box_id).as_int(); | 			ss << log_id(module) << " " << module->attributes.at(ID::abc9_box_id).as_int(); | ||||||
| 			ss << " " << (module->get_bool_attribute(ID::whitebox) ? "1" : "0"); | 			bool has_model = module->get_bool_attribute(ID::whitebox) || !module->get_bool_attribute(ID::blackbox); | ||||||
|  | 			ss << " " << (has_model ? "1" : "0"); | ||||||
| 			ss << " " << GetSize(inputs) << " " << GetSize(outputs) << std::endl; | 			ss << " " << GetSize(inputs) << " " << GetSize(outputs) << std::endl; | ||||||
| 
 | 
 | ||||||
| 			bool first = true; | 			bool first = true; | ||||||
|  | @ -1096,8 +1094,9 @@ void prep_box(RTLIL::Design *design) | ||||||
| 			ss << std::endl; | 			ss << std::endl; | ||||||
| 
 | 
 | ||||||
| 			auto &t = timing.setup_module(module); | 			auto &t = timing.setup_module(module); | ||||||
| 			if (t.comb.empty()) | 			if (t.comb.empty() && !outputs.empty() && !inputs.empty()) { | ||||||
| 				log_error("Module '%s' with (* abc9_box *) has no timing (and thus no connectivity) information.\n", log_id(module)); | 				log_error("Module '%s' with (* abc9_box *) has no timing (and thus no connectivity) information.\n", log_id(module)); | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			for (const auto &o : outputs) { | 			for (const auto &o : outputs) { | ||||||
| 				first = true; | 				first = true; | ||||||
|  |  | ||||||
|  | @ -19,10 +19,29 @@ | ||||||
| 
 | 
 | ||||||
| #include "kernel/register.h" | #include "kernel/register.h" | ||||||
| #include "kernel/rtlil.h" | #include "kernel/rtlil.h" | ||||||
|  | #include "kernel/utils.h" | ||||||
| 
 | 
 | ||||||
| USING_YOSYS_NAMESPACE | USING_YOSYS_NAMESPACE | ||||||
| PRIVATE_NAMESPACE_BEGIN | PRIVATE_NAMESPACE_BEGIN | ||||||
| 
 | 
 | ||||||
|  | std::vector<Module*> order_modules(Design *design, std::vector<Module *> modules) | ||||||
|  | { | ||||||
|  | 	std::set<Module *> modules_set(modules.begin(), modules.end()); | ||||||
|  | 	TopoSort<Module*> sort; | ||||||
|  | 
 | ||||||
|  | 	for (auto m : modules) { | ||||||
|  | 		sort.node(m); | ||||||
|  | 
 | ||||||
|  | 		for (auto cell : m->cells()) { | ||||||
|  | 			Module *submodule = design->module(cell->type); | ||||||
|  | 			if (modules_set.count(submodule)) | ||||||
|  | 				sort.edge(submodule, m); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	log_assert(sort.sort()); | ||||||
|  | 	return sort.sorted; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| struct AbcNewPass : public ScriptPass { | struct AbcNewPass : public ScriptPass { | ||||||
| 	AbcNewPass() : ScriptPass("abc_new", "(experimental) use ABC for SC technology mapping (new)") | 	AbcNewPass() : ScriptPass("abc_new", "(experimental) use ABC for SC technology mapping (new)") | ||||||
| 	{ | 	{ | ||||||
|  | @ -101,6 +120,15 @@ struct AbcNewPass : public ScriptPass { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (check_label("prep_boxes")) { | 		if (check_label("prep_boxes")) { | ||||||
|  | 			if (!help_mode) { | ||||||
|  | 				for (auto mod : active_design->selected_whole_modules_warn()) { | ||||||
|  | 					if (mod->get_bool_attribute(ID::abc9_box)) { | ||||||
|  | 						mod->set_bool_attribute(ID::abc9_box, false); | ||||||
|  | 						mod->set_bool_attribute(ID(abc9_deferred_box), true); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			run("box_derive"); | 			run("box_derive"); | ||||||
| 			run("abc9_ops -prep_box"); | 			run("abc9_ops -prep_box"); | ||||||
| 		} | 		} | ||||||
|  | @ -109,7 +137,8 @@ struct AbcNewPass : public ScriptPass { | ||||||
| 			std::vector<Module *> selected_modules; | 			std::vector<Module *> selected_modules; | ||||||
| 
 | 
 | ||||||
| 			if (!help_mode) { | 			if (!help_mode) { | ||||||
| 				selected_modules = active_design->selected_whole_modules_warn(); | 				selected_modules = order_modules(active_design, | ||||||
|  | 												 active_design->selected_whole_modules_warn()); | ||||||
| 				active_design->selection_stack.emplace_back(false); | 				active_design->selection_stack.emplace_back(false); | ||||||
| 			} else { | 			} else { | ||||||
| 				selected_modules = {nullptr}; | 				selected_modules = {nullptr}; | ||||||
|  | @ -131,15 +160,36 @@ struct AbcNewPass : public ScriptPass { | ||||||
| 					active_design->selection().select(mod); | 					active_design->selection().select(mod); | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
|  | 				std::string script_save; | ||||||
|  | 				if (!help_mode && mod->has_attribute(ID(abc9_script))) { | ||||||
|  | 					script_save = active_design->scratchpad_get_string("abc9.script"); | ||||||
|  | 					active_design->scratchpad_set_string("abc9.script", | ||||||
|  | 						mod->get_string_attribute(ID(abc9_script))); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
| 				run(stringf("  abc9_ops -write_box %s/input.box", tmpdir.c_str())); | 				run(stringf("  abc9_ops -write_box %s/input.box", tmpdir.c_str())); | ||||||
| 				run(stringf("  write_xaiger2 -mapping_prep -map2 %s/input.map2 %s/input.xaig", tmpdir.c_str(), tmpdir.c_str())); | 				run(stringf("  write_xaiger2 -mapping_prep -map2 %s/input.map2 %s/input.xaig", tmpdir.c_str(), tmpdir.c_str())); | ||||||
| 				run(stringf("  abc9_exe %s -cwd %s -box %s/input.box", exe_options.c_str(), tmpdir.c_str(), tmpdir.c_str())); | 				run(stringf("  abc9_exe %s -cwd %s -box %s/input.box", exe_options.c_str(), tmpdir.c_str(), tmpdir.c_str())); | ||||||
| 				run(stringf("  read_xaiger2 -sc_mapping -module_name %s -map2 %s/input.map2 %s/output.aig", | 				run(stringf("  read_xaiger2 -sc_mapping -module_name %s -map2 %s/input.map2 %s/output.aig", | ||||||
| 							modname.c_str(), tmpdir.c_str(), tmpdir.c_str())); | 							modname.c_str(), tmpdir.c_str(), tmpdir.c_str())); | ||||||
| 
 | 
 | ||||||
|  | 				if (!help_mode && mod->has_attribute(ID(abc9_script))) { | ||||||
|  | 					if (script_save.empty()) | ||||||
|  | 						active_design->scratchpad_unset("abc9.script"); | ||||||
|  | 					else | ||||||
|  | 						active_design->scratchpad_set_string("abc9.script", script_save); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
| 				if (!help_mode) { | 				if (!help_mode) { | ||||||
| 					active_design->selection().selected_modules.clear(); | 					active_design->selection().selected_modules.clear(); | ||||||
| 					log_pop(); | 					log_pop(); | ||||||
|  | 
 | ||||||
|  | 					if (mod->get_bool_attribute(ID(abc9_deferred_box))) { | ||||||
|  | 						mod->set_bool_attribute(ID(abc9_deferred_box), false); | ||||||
|  | 						mod->set_bool_attribute(ID::abc9_box, true); | ||||||
|  | 						Pass::call_on_module(active_design, mod, "portarcs -draw -write"); | ||||||
|  | 						run("abc9_ops -prep_box"); | ||||||
|  | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -20,3 +20,12 @@ always @(posedge CLK, posedge CLEAR, posedge PRESET) | ||||||
| assign QN = ~Q; | assign QN = ~Q; | ||||||
| 
 | 
 | ||||||
| endmodule | endmodule | ||||||
|  | 
 | ||||||
|  | module dffe(input CLK, EN, D, output reg Q, output QN); | ||||||
|  | 
 | ||||||
|  | always @(negedge CLK) | ||||||
|  | 	if (EN) Q <= D; | ||||||
|  | 
 | ||||||
|  | assign QN = ~Q; | ||||||
|  | 
 | ||||||
|  | endmodule | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ library(test) { | ||||||
|     ff("IQ", "IQN") { |     ff("IQ", "IQN") { | ||||||
|       next_state : "D"; |       next_state : "D"; | ||||||
|       clocked_on : "!CLK"; |       clocked_on : "!CLK"; | ||||||
|     }  |     } | ||||||
|     pin(D) { |     pin(D) { | ||||||
|       direction : input; |       direction : input; | ||||||
|     } |     } | ||||||
|  | @ -19,7 +19,7 @@ library(test) { | ||||||
|     pin(QN) { |     pin(QN) { | ||||||
|       direction: output; |       direction: output; | ||||||
|       function : "IQN"; |       function : "IQN"; | ||||||
|     }  |     } | ||||||
|   } |   } | ||||||
|   cell (dffsr) { |   cell (dffsr) { | ||||||
|     area : 6; |     area : 6; | ||||||
|  | @ -30,7 +30,7 @@ library(test) { | ||||||
|       preset     : "PRESET"; |       preset     : "PRESET"; | ||||||
|       clear_preset_var1 : L; |       clear_preset_var1 : L; | ||||||
|       clear_preset_var2 : L; |       clear_preset_var2 : L; | ||||||
|     }  |     } | ||||||
|     pin(D) { |     pin(D) { | ||||||
|       direction : input; |       direction : input; | ||||||
|     } |     } | ||||||
|  | @ -50,6 +50,30 @@ library(test) { | ||||||
|     pin(QN) { |     pin(QN) { | ||||||
|       direction: output; |       direction: output; | ||||||
|       function : "IQN"; |       function : "IQN"; | ||||||
|     }  |     } | ||||||
|  |   } | ||||||
|  |   cell (dffe) { | ||||||
|  |     area : 6; | ||||||
|  |     ff("IQ", "IQN") { | ||||||
|  |       next_state : "(D&EN) | (IQ&!EN)"; | ||||||
|  |       clocked_on : "!CLK"; | ||||||
|  |     } | ||||||
|  |     pin(D) { | ||||||
|  |       direction : input; | ||||||
|  |     } | ||||||
|  |     pin(EN) { | ||||||
|  |       direction : input; | ||||||
|  |     } | ||||||
|  |     pin(CLK) { | ||||||
|  |       direction : input; | ||||||
|  |     } | ||||||
|  |     pin(Q) { | ||||||
|  |       direction: output; | ||||||
|  |       function : "IQ"; | ||||||
|  |     } | ||||||
|  |     pin(QN) { | ||||||
|  |       direction: output; | ||||||
|  |       function : "IQN"; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,14 +1,15 @@ | ||||||
| read_verilog -icells <<EOT | read_verilog -icells <<EOT | ||||||
| 
 | 
 | ||||||
| module top(input C, D, S, R, output [9:0] Q); | module top(input C, D, E, S, R, output [11:0] Q); | ||||||
| 
 | 
 | ||||||
| $_DFF_P_ ff0 (.C(C), .D(D), .Q(Q[0])); | $_DFF_P_ ff0 (.C(C), .D(D), .Q(Q[0])); | ||||||
| $_DFF_PP0_ ff1 (.C(C), .D(D), .R(R), .Q(Q[1])); | $_DFF_PP0_ ff1 (.C(C), .D(D), .R(R), .Q(Q[1])); | ||||||
| $_DFF_PP1_ ff2 (.C(C), .D(D), .R(R), .Q(Q[2])); | $_DFF_PP1_ ff2 (.C(C), .D(D), .R(R), .Q(Q[2])); | ||||||
| $_DFFSR_PPP_ ff3 (.C(C), .D(D), .R(R), .S(S), .Q(Q[3])); | $_DFFSR_PPP_ ff3 (.C(C), .D(D), .R(R), .S(S), .Q(Q[3])); | ||||||
| $_DFFSR_NNN_ ff4 (.C(C), .D(D), .R(R), .S(S), .Q(Q[4])); | $_DFFSR_NNN_ ff4 (.C(C), .D(D), .R(R), .S(S), .Q(Q[4])); | ||||||
|  | $_DFFE_PP_ ff5 (.C(C), .D(D), .E(E), .Q(Q[5])); | ||||||
| 
 | 
 | ||||||
| assign Q[9:5] = ~Q[4:0]; | assign Q[11:6] = ~Q[5:0]; | ||||||
| 
 | 
 | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
|  | @ -29,23 +30,25 @@ design -load orig | ||||||
| dfflibmap -liberty dfflibmap.lib | dfflibmap -liberty dfflibmap.lib | ||||||
| clean | clean | ||||||
| 
 | 
 | ||||||
| select -assert-count 4 t:$_NOT_ | select -assert-count 5 t:$_NOT_ | ||||||
| select -assert-count 1 t:dffn | select -assert-count 1 t:dffn | ||||||
| select -assert-count 4 t:dffsr | select -assert-count 4 t:dffsr | ||||||
| select -assert-none t:dffn t:dffsr t:$_NOT_ %% %n t:* %i | select -assert-count 1 t:dffe | ||||||
|  | select -assert-none t:dffn t:dffsr t:dffe t:$_NOT_ %% %n t:* %i | ||||||
| 
 | 
 | ||||||
| design -load orig | design -load orig | ||||||
| dfflibmap -prepare -liberty dfflibmap.lib | dfflibmap -prepare -liberty dfflibmap.lib | ||||||
| 
 | 
 | ||||||
| select -assert-count 9 t:$_NOT_ | select -assert-count 11 t:$_NOT_ | ||||||
| select -assert-count 1 t:$_DFF_N_ | select -assert-count 1 t:$_DFF_N_ | ||||||
| select -assert-count 4 t:$_DFFSR_PPP_ | select -assert-count 4 t:$_DFFSR_PPP_ | ||||||
| select -assert-none t:$_DFF_N_ t:$_DFFSR_PPP_ t:$_NOT_ %% %n t:* %i | select -assert-count 1 t:$_DFFE_NP_ | ||||||
|  | select -assert-none t:$_DFF_N_ t:$_DFFSR_PPP_ t:$_DFFE_NP_ t:$_NOT_ %% %n t:* %i | ||||||
| 
 | 
 | ||||||
| design -load orig | design -load orig | ||||||
| dfflibmap -map-only -liberty dfflibmap.lib | dfflibmap -map-only -liberty dfflibmap.lib | ||||||
| 
 | 
 | ||||||
| select -assert-count 5 t:$_NOT_ | select -assert-count 6 t:$_NOT_ | ||||||
| select -assert-count 0 t:dffn | select -assert-count 0 t:dffn | ||||||
| select -assert-count 1 t:dffsr | select -assert-count 1 t:dffsr | ||||||
| 
 | 
 | ||||||
|  | @ -54,20 +57,22 @@ dfflibmap -prepare -liberty dfflibmap.lib | ||||||
| dfflibmap -map-only -liberty dfflibmap.lib | dfflibmap -map-only -liberty dfflibmap.lib | ||||||
| clean | clean | ||||||
| 
 | 
 | ||||||
| select -assert-count 4 t:$_NOT_ | select -assert-count 5 t:$_NOT_ | ||||||
| select -assert-count 1 t:dffn | select -assert-count 1 t:dffn | ||||||
| select -assert-count 4 t:dffsr | select -assert-count 4 t:dffsr | ||||||
| select -assert-none t:dffn t:dffsr t:$_NOT_ %% %n t:* %i | select -assert-count 1 t:dffe | ||||||
|  | select -assert-none t:dffn t:dffsr t:dffe t:$_NOT_ %% %n t:* %i | ||||||
| 
 | 
 | ||||||
| design -load orig | design -load orig | ||||||
| dfflibmap -prepare -liberty dfflibmap_dffn.lib -liberty dfflibmap_dffsr.lib | dfflibmap -prepare -liberty dfflibmap_dffn_dffe.lib -liberty dfflibmap_dffsr.lib | ||||||
| dfflibmap -map-only -liberty dfflibmap_dffn.lib -liberty dfflibmap_dffsr.lib | dfflibmap -map-only -liberty dfflibmap_dffn_dffe.lib -liberty dfflibmap_dffsr.lib | ||||||
| clean | clean | ||||||
| 
 | 
 | ||||||
| select -assert-count 4 t:$_NOT_ | select -assert-count 5 t:$_NOT_ | ||||||
| select -assert-count 1 t:dffn | select -assert-count 1 t:dffn | ||||||
|  | select -assert-count 1 t:dffe | ||||||
| select -assert-count 4 t:dffsr | select -assert-count 4 t:dffsr | ||||||
| select -assert-none t:dffn t:dffsr t:$_NOT_ %% %n t:* %i | select -assert-none t:dffn t:dffsr t:dffe t:$_NOT_ %% %n t:* %i | ||||||
| 
 | 
 | ||||||
| design -load orig | design -load orig | ||||||
| dfflibmap -liberty dfflibmap.lib -dont_use *ffn | dfflibmap -liberty dfflibmap.lib -dont_use *ffn | ||||||
|  | @ -75,3 +80,4 @@ clean | ||||||
| 
 | 
 | ||||||
| select -assert-count 0 t:dffn | select -assert-count 0 t:dffn | ||||||
| select -assert-count 5 t:dffsr | select -assert-count 5 t:dffsr | ||||||
|  | select -assert-count 1 t:dffe | ||||||
|  |  | ||||||
|  | @ -1,23 +0,0 @@ | ||||||
| library(test) { |  | ||||||
|   cell (dffn) { |  | ||||||
|     area : 6; |  | ||||||
|     ff("IQ", "IQN") { |  | ||||||
|       next_state : "D"; |  | ||||||
|       clocked_on : "!CLK"; |  | ||||||
|     }  |  | ||||||
|     pin(D) { |  | ||||||
|       direction : input; |  | ||||||
|     } |  | ||||||
|     pin(CLK) { |  | ||||||
|       direction : input; |  | ||||||
|     } |  | ||||||
|     pin(Q) { |  | ||||||
|       direction: output; |  | ||||||
|       function : "IQ"; |  | ||||||
|     } |  | ||||||
|     pin(QN) { |  | ||||||
|       direction: output; |  | ||||||
|       function : "IQN"; |  | ||||||
|     }  |  | ||||||
|   } |  | ||||||
| } |  | ||||||
							
								
								
									
										47
									
								
								tests/techmap/dfflibmap_dffn_dffe.lib
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								tests/techmap/dfflibmap_dffn_dffe.lib
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | ||||||
|  | library(test) { | ||||||
|  |   cell (dffn) { | ||||||
|  |     area : 6; | ||||||
|  |     ff("IQ", "IQN") { | ||||||
|  |       next_state : "D"; | ||||||
|  |       clocked_on : "!CLK"; | ||||||
|  |     }  | ||||||
|  |     pin(D) { | ||||||
|  |       direction : input; | ||||||
|  |     } | ||||||
|  |     pin(CLK) { | ||||||
|  |       direction : input; | ||||||
|  |     } | ||||||
|  |     pin(Q) { | ||||||
|  |       direction: output; | ||||||
|  |       function : "IQ"; | ||||||
|  |     } | ||||||
|  |     pin(QN) { | ||||||
|  |       direction: output; | ||||||
|  |       function : "IQN"; | ||||||
|  |     }  | ||||||
|  |   } | ||||||
|  |   cell (dffe) { | ||||||
|  |     area : 6; | ||||||
|  |     ff("IQ", "IQN") { | ||||||
|  |       next_state : "(D&EN) | (IQ&!EN)"; | ||||||
|  |       clocked_on : "!CLK"; | ||||||
|  |     } | ||||||
|  |     pin(D) { | ||||||
|  |       direction : input; | ||||||
|  |     } | ||||||
|  |     pin(EN) { | ||||||
|  |       direction : input; | ||||||
|  |     } | ||||||
|  |     pin(CLK) { | ||||||
|  |       direction : input; | ||||||
|  |     } | ||||||
|  |     pin(Q) { | ||||||
|  |       direction: output; | ||||||
|  |       function : "IQ"; | ||||||
|  |     } | ||||||
|  |     pin(QN) { | ||||||
|  |       direction: output; | ||||||
|  |       function : "IQN"; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -18,8 +18,40 @@ EOF | ||||||
| 
 | 
 | ||||||
| wreduce | wreduce | ||||||
| wrapcell -setattr foo -formatattr bar w{Y_WIDTH} -name OR_{A_WIDTH}_{B_WIDTH}_{Y_WIDTH} | wrapcell -setattr foo -formatattr bar w{Y_WIDTH} -name OR_{A_WIDTH}_{B_WIDTH}_{Y_WIDTH} | ||||||
|  | check -assert | ||||||
| select -assert-count 2 top/t:OR_2_3_3 | select -assert-count 2 top/t:OR_2_3_3 | ||||||
| select -assert-count 1 top/t:OR_3_4_4 | select -assert-count 1 top/t:OR_3_4_4 | ||||||
| select -assert-none top/t:OR_2_3_3 top/t:OR_3_4_4 %% top/t:* %D | select -assert-none top/t:OR_2_3_3 top/t:OR_3_4_4 %% top/t:* %D | ||||||
| select -assert-mod-count 2 OR_2_3_3 OR_3_4_4 | select -assert-mod-count 2 OR_2_3_3 OR_3_4_4 | ||||||
| select -assert-mod-count 2 A:bar=w3 A:bar=w4 | select -assert-mod-count 2 A:bar=w3 A:bar=w4 | ||||||
|  | 
 | ||||||
|  | design -reset | ||||||
|  | read_verilog <<EOF | ||||||
|  | module top( | ||||||
|  | 	input [1:0] a, | ||||||
|  | 	input [2:0] b, | ||||||
|  | 	output [2:0] y, | ||||||
|  | 	input [2:0] a2, | ||||||
|  | 	input [3:0] b2, | ||||||
|  | 	output [3:0] y2, | ||||||
|  |  	input [1:0] a3, | ||||||
|  |  	input [2:0] b3, | ||||||
|  |  	output [2:0] y3 | ||||||
|  | ); | ||||||
|  | 	assign y = a | (*keep*) b; | ||||||
|  | 	assign y2 = a2 | (*keep*) b2; | ||||||
|  | 	wire [2:0] y3_ = a3 | (*keep*) b3; | ||||||
|  | 	assign y3 = {y3_[2], y3_[0]}; | ||||||
|  | endmodule | ||||||
|  | EOF | ||||||
|  | 
 | ||||||
|  | opt_clean | ||||||
|  | wreduce | ||||||
|  | wrapcell -setattr foo -formatattr bar w{Y_WIDTH} -name OR_{A_WIDTH}_{B_WIDTH}_{Y_WIDTH}{%unused} | ||||||
|  | check -assert | ||||||
|  | select -assert-count 1 top/t:OR_2_3_3 | ||||||
|  | select -assert-count 1 top/t:OR_2_3_3_unused_Y[1] | ||||||
|  | select -assert-count 1 top/t:OR_3_4_4 | ||||||
|  | select -assert-none top/t:OR_2_3_3 top/t:OR_3_4_4 top/t:OR_2_3_3_unused_Y[1] %% top/t:* %D | ||||||
|  | select -assert-mod-count 2 OR_2_3_3 OR_3_4_4 | ||||||
|  | select -assert-mod-count 3 A:bar=w3 A:bar=w4 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue