diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index cda13ff8b..3228f02fb 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -539,7 +539,7 @@ struct LibertyFrontend : public Frontend { log_header(design, "Executing Liberty frontend: %s\n", filename.c_str()); - LibertyParser parser(*f); + LibertyParser parser(*f, filename); int cell_count = 0; std::map> global_type_map; diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 7e51b6cb1..23583a8a8 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -352,7 +352,7 @@ void read_liberty_cellarea(dict &cell_area, string libert yosys_input_files.insert(liberty_file); if (f->fail()) log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno)); - LibertyParser libparser(*f); + LibertyParser libparser(*f, liberty_file); delete f; for (auto cell : libparser.ast->children) diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 4e1d16744..e6a910a0d 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -6,6 +6,7 @@ OBJS += passes/techmap/dfflibmap.o OBJS += passes/techmap/maccmap.o OBJS += passes/techmap/booth.o OBJS += passes/techmap/libparse.o +OBJS += passes/techmap/libcache.o ifeq ($(ENABLE_ABC),1) OBJS += passes/techmap/abc.o diff --git a/passes/techmap/clockgate.cc b/passes/techmap/clockgate.cc index 363f998db..b34ad1769 100644 --- a/passes/techmap/clockgate.cc +++ b/passes/techmap/clockgate.cc @@ -312,7 +312,7 @@ struct ClockgatePass : public Pass { std::istream* f = uncompressed(path); if (f->fail()) log_cmd_error("Can't open liberty file `%s': %s\n", path.c_str(), strerror(errno)); - LibertyParser p(*f); + LibertyParser p(*f, path); merged.merge(p); delete f; } diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index fe4f8aada..84db7f157 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -634,7 +634,7 @@ struct DfflibmapPass : public Pass { std::istream* f = uncompressed(path); if (f->fail()) log_cmd_error("Can't open liberty file `%s': %s\n", path.c_str(), strerror(errno)); - LibertyParser p(*f); + LibertyParser p(*f, path); merged.merge(p); delete f; } diff --git a/passes/techmap/libcache.cc b/passes/techmap/libcache.cc new file mode 100644 index 000000000..19f1fa87d --- /dev/null +++ b/passes/techmap/libcache.cc @@ -0,0 +1,130 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2025 Jannis Harder + * + * 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 "passes/techmap/libparse.h" + + USING_YOSYS_NAMESPACE + PRIVATE_NAMESPACE_BEGIN + + struct LibcachePass : public Pass { + LibcachePass() : Pass("libcache", "control caching of technology library data parsed from liberty files") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" libcache {-enable|-disable|-purge} { -all | [path]... }\n"); + log("\n"); + log("Controls the default and per path caching of liberty file data.\n"); + log("\n"); + log(" -enable Enable caching.\n"); + log(" -disable Disable caching.\n"); + log(" -purge Reset cache setting and forget cached data.\n"); + log("\n"); + log("This mode takes a list of paths as argument. If no paths are provided, this\n"); + log("command does nothing. The -all option can be used to change the default cache\n"); + log("setting for -enable/-disable or to reset and forget about all paths.\n"); + log("\n"); + log("By default caching is disabled.\n"); + log("\n"); + log(" libcache -list\n"); + log("\n"); + log("Displays the current cache settings and cached paths.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *) override + { + bool enable = false; + bool disable = false; + bool purge = false; + bool all = false; + bool list = false; + std::vector paths; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-enable") { + enable = true; + continue; + } + if (args[argidx] == "-disable") { + enable = true; + continue; + } + if (args[argidx] == "-purge") { + purge = true; + continue; + } + if (args[argidx] == "-all") { + all = true; + continue; + } + if (args[argidx] == "-list") { + list = true; + continue; + } + std::string fname = args[argidx]; + rewrite_filename(fname); + paths.push_back(fname); + break; + } + int modes = enable + disable + purge + list; + if (modes == 0) + log_cmd_error("At least one of -enable, -disable, -purge or -list is required.\n"); + if (modes > 1) + log_cmd_error("Only one of -enable, -disable, -purge or -list may be present.\n"); + + if (all && !paths.empty()) + log_cmd_error("The -all option cannot be combined with a list of paths.\n"); + if (list && (all || !paths.empty())) + log_cmd_error("The -list mode takes no further options.\n"); + if (!list && !all && paths.empty()) + log("No paths specified, use -all to %s\n", purge ? "purge all paths" : "change the default setting"); + + if (list) { + log("Caching is %s by default.\n", LibertyAstCache::instance.cache_by_default ? "enabled" : "disabled"); + for (auto const &entry : LibertyAstCache::instance.cache_path) + log("Caching is %s for `%s'.\n", entry.second ? "enabled" : "disabled", entry.first.c_str()); + for (auto const &entry : LibertyAstCache::instance.cached) + log("Data for `%s' is currently cached.\n", entry.first.c_str()); + } else if (enable || disable) { + if (all) { + LibertyAstCache::instance.cache_by_default = enable; + } else { + for (auto const &path : paths) { + LibertyAstCache::instance.cache_path[path] = enable; + } + } + } else if (purge) { + if (all) { + LibertyAstCache::instance.cached.clear(); + LibertyAstCache::instance.cache_path.clear(); + } else { + for (auto const &path : paths) { + LibertyAstCache::instance.cached.erase(path); + LibertyAstCache::instance.cache_path.erase(path); + } + } + } else { + log_assert(false); + } + } +} LibcachePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index d3d5b7d57..d7a952603 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -32,6 +32,31 @@ using namespace Yosys; +#ifndef FILTERLIB + +LibertyAstCache LibertyAstCache::instance; + +std::shared_ptr LibertyAstCache::cached_ast(const std::string &fname) +{ + auto it = cached.find(fname); + if (it == cached.end()) + return nullptr; + log("Using cached data for liberty file `%s'\n", fname.c_str()); + return it->second; +} + +void LibertyAstCache::parsed_ast(const std::string &fname, const std::shared_ptr &ast) +{ + auto it = cache_path.find(fname); + bool should_cache = it == cache_path.end() ? cache_by_default : it->second; + if (!should_cache) + return; + log("Caching data for liberty file `%s'\n", fname.c_str()); + cached.emplace(fname, ast); +} + +#endif + bool LibertyInputStream::extend_buffer_once() { if (eof) diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 1fcaaebee..9332afebd 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -132,6 +132,22 @@ namespace Yosys } }; +#ifndef FILTERLIB + class LibertyAstCache { + LibertyAstCache() {}; + ~LibertyAstCache() {}; + public: + dict> cached; + + bool cache_by_default = false; + dict cache_path; + + std::shared_ptr cached_ast(const std::string &fname); + void parsed_ast(const std::string &fname, const std::shared_ptr &ast); + static LibertyAstCache instance; + }; +#endif + class LibertyMergedCells; class LibertyParser { @@ -152,15 +168,29 @@ namespace Yosys void error(const std::string &str) const; public: - const LibertyAst *ast; + std::shared_ptr shared_ast; + const LibertyAst *ast = nullptr; - LibertyParser(std::istream &f) : f(f), line(1), ast(parse()) {} - ~LibertyParser() { if (ast) delete ast; } + LibertyParser(std::istream &f) : f(f), line(1) { + shared_ast.reset(parse()); + ast = shared_ast.get(); + } + +#ifndef FILTERLIB + LibertyParser(std::istream &f, const std::string &fname) : f(f), line(1) { + shared_ast = LibertyAstCache::instance.cached_ast(fname); + if (!shared_ast) { + shared_ast.reset(parse()); + LibertyAstCache::instance.parsed_ast(fname, shared_ast); + } + ast = shared_ast.get(); + } +#endif }; class LibertyMergedCells { - std::vector asts; + std::vector> asts; public: std::vector cells; @@ -168,10 +198,7 @@ namespace Yosys { if (parser.ast) { const LibertyAst *ast = parser.ast; - asts.push_back(ast); - // The parser no longer owns its top level ast, but we do. - // sketchy zone - parser.ast = nullptr; + asts.push_back(parser.shared_ast); if (ast->id != "library") parser.error("Top level entity isn't \"library\".\n"); for (const LibertyAst *cell : ast->children) @@ -179,11 +206,6 @@ namespace Yosys cells.push_back(cell); } } - ~LibertyMergedCells() - { - for (auto ast : asts) - delete ast; - } }; } diff --git a/tests/liberty/libcache.ys b/tests/liberty/libcache.ys new file mode 100644 index 000000000..a741a9df1 --- /dev/null +++ b/tests/liberty/libcache.ys @@ -0,0 +1,58 @@ +libcache -enable busdef.lib + +logger -expect log "Caching is disabled by default." 1 +logger -expect log "Caching is enabled for `busdef.lib'." 1 +libcache -list +logger -check-expected + +logger -expect log "Caching data" 1 +log Caching data +read_liberty normal.lib; design -reset +logger -check-expected + +logger -expect log "Caching data" 1 +read_liberty -lib busdef.lib; design -reset +logger -check-expected + +logger -expect log "Using caching data" 1 +log Using caching data +read_liberty normal.lib; design -reset +logger -check-expected + +logger -expect log "Using cached data" 1 +read_liberty -lib busdef.lib; design -reset +logger -check-expected + +libcache -purge busdef.lib + +logger -expect log "Caching is disabled by default." 1 +logger -expect log "Caching is enabled for `busdef.lib'." 1 +log Caching is enabled for `busdef.lib'. +libcache -list +logger -check-expected + +libcache -enable -all + +logger -expect log "Caching is enabled by default." 1 +libcache -list +logger -check-expected + +logger -expect log "Caching data" 1 +read_liberty normal.lib; design -reset +logger -check-expected + +logger -expect log "Caching data" 1 +read_liberty -lib busdef.lib; design -reset +logger -check-expected + +logger -expect log "Using cached data" 1 +read_liberty -lib busdef.lib; design -reset +logger -check-expected + +logger -expect log "Using cached data" 1 +read_liberty normal.lib; design -reset +logger -check-expected + +logger -expect log "Using cached data" 1 +dfflibmap -liberty normal.lib +logger -check-expected