diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index 76c1ecca3..080c86d29 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -488,7 +488,7 @@ struct LibertyFrontend : public Frontend { log_header(design, "Executing Liberty frontend: %s\n", filename); - LibertyParser parser(*f, filename); + LibertyParser parser(*f, filename, liberty_synth_filter); int cell_count = 0; std::map> global_type_map; diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index de767b96a..4526bc1f6 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -801,7 +801,7 @@ void read_liberty_cellarea(dict &cell_area, string libert { std::istream *f = uncompressed(liberty_file.c_str()); yosys_input_files.insert(liberty_file); - LibertyParser libparser(*f, liberty_file); + LibertyParser libparser(*f, liberty_file, liberty_synth_filter); delete f; for (auto cell : libparser.ast->children) { diff --git a/passes/techmap/clockgate.cc b/passes/techmap/clockgate.cc index 650719cd5..e012e278b 100644 --- a/passes/techmap/clockgate.cc +++ b/passes/techmap/clockgate.cc @@ -308,7 +308,7 @@ struct ClockgatePass : public Pass { LibertyMergedCells merged; for (auto path : liberty_files) { std::istream* f = uncompressed(path); - LibertyParser p(*f, path); + LibertyParser p(*f, path, liberty_synth_filter); merged.merge(p); delete f; } diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index df1be9ecf..a17398498 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -662,7 +662,7 @@ struct DfflibmapPass : public Pass { LibertyMergedCells merged; for (auto path : liberty_files) { std::istream* f = uncompressed(path); - LibertyParser p(*f, path); + LibertyParser p(*f, path, liberty_synth_filter); merged.merge(p); delete f; } diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index fe3815cd1..041920b5c 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -49,17 +49,20 @@ using namespace Yosys; LibertyAstCache LibertyAstCache::instance; -std::shared_ptr LibertyAstCache::cached_ast(const std::string &fname) +std::shared_ptr LibertyAstCache::cached_ast(const std::string &fname, const LibertyFilter &filter) { auto it = cached.find(fname); if (it == cached.end()) return nullptr; + if (!it->second.filter.covers(filter)) + return nullptr; if (verbose) log("Using cached data for liberty file `%s'\n", fname); - return it->second; + return it->second.ast; } -void LibertyAstCache::parsed_ast(const std::string &fname, const std::shared_ptr &ast) +void LibertyAstCache::parsed_ast(const std::string &fname, const LibertyFilter &filter, + const std::shared_ptr &ast) { auto it = cache_path.find(fname); bool should_cache = it == cache_path.end() ? cache_by_default : it->second; @@ -67,7 +70,7 @@ void LibertyAstCache::parsed_ast(const std::string &fname, const std::shared_ptr return; if (verbose) log("Caching data for liberty file `%s'\n", fname); - cached.emplace(fname, ast); + cached.emplace(fname, CacheEntry{filter, ast}); } #endif @@ -138,6 +141,7 @@ LibertyAst::~LibertyAst() const LibertyAst *LibertyAst::find(std::string name) const { + log_assert(filter.allows(name)); for (auto child : children) if (child->id == name) return child; @@ -628,7 +632,7 @@ int LibertyParser::consume_wrecked_str(int tok, std::string& out_str) { return tok; } -LibertyAst *LibertyParser::parse(bool top_level) +LibertyParser::ParseResult LibertyParser::try_parse(bool top_level, bool skip_all) { std::string str; @@ -644,19 +648,23 @@ LibertyAst *LibertyParser::parse(bool top_level) if (tok == EOF) { if (top_level) - return NULL; + return ParseResult::closed(); report_unexpected_token(tok); } if (tok == '}') - return NULL; + return ParseResult::closed(); if (tok != 'v') { report_unexpected_token(tok); } - LibertyAst *ast = new LibertyAst; - ast->id = str; + std::string id = std::move(str); + bool skip = skip_all || !filter.allows(id); + + std::string value; + std::vector args; + std::vector children; while (1) { @@ -667,8 +675,8 @@ LibertyAst *LibertyParser::parse(bool top_level) if ((tok == ';') || (tok == 'n')) break; - if (tok == ':' && ast->value.empty()) { - tok = lexer(ast->value); + if (tok == ':' && value.empty()) { + tok = lexer(value); if (tok == 'v') { tok = lexer(str); if (tok == '[') { @@ -676,19 +684,19 @@ LibertyAst *LibertyParser::parse(bool top_level) tok = lexer(str); } else { // Hack for when an expression string is unquoted - tok = consume_wrecked_str(tok, ast->value); + tok = consume_wrecked_str(tok, value); } } else if (tok == '(') { // Hack for when an expression string is unquoted and starts with // parentheses - tok = consume_wrecked_str(tok, ast->value); + tok = consume_wrecked_str(tok, value); } while (tok == '+' || tok == '-' || tok == '*' || tok == '/' || tok == '!') { - ast->value += tok; + value += tok; tok = lexer(str); if (tok != 'v') error(); - ast->value += str; + value += str; tok = lexer(str); } @@ -722,7 +730,7 @@ LibertyAst *LibertyParser::parse(bool top_level) if (tok != 'v') { report_unexpected_token(tok); } - ast->args.push_back(arg); + args.push_back(std::move(arg)); } continue; } @@ -730,12 +738,12 @@ LibertyAst *LibertyParser::parse(bool top_level) if (tok == '{') { bool terminated = false; while (1) { - LibertyAst *child = parse(false); + LibertyAst *child = parse(false, skip); if (child == NULL) { terminated = true; break; } - ast->children.push_back(child); + children.push_back(child); } if (!terminated) { report_unexpected_token(EOF); @@ -746,7 +754,25 @@ LibertyAst *LibertyParser::parse(bool top_level) report_unexpected_token(tok); } - return ast; + if (skip) + return ParseResult::skipped(); + + return ParseResult::node(new LibertyAst{std::move(id), std::move(value), std::move(args), + std::move(children), filter}); +} + +LibertyAst *LibertyParser::parse(bool top_level, bool skip_all) +{ + while (1) { + ParseResult result = try_parse(top_level, skip_all); + // We got our statement, return it + if (result.kind == ParseResult::Node) + return result.ast; + // We didn't parse a statement, and there aren't any more, so we're done + if (result.kind == ParseResult::Closed) + return NULL; + // Otherwise, we skipped a node due to filtering, keep going + } } #ifndef FILTERLIB diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 674484dad..fd0e1d1c5 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -22,7 +22,11 @@ #include "kernel/yosys.h" #include +#include +#include +#include #include +#include #include #include @@ -33,11 +37,119 @@ namespace Yosys { + class LibertyFilter + { + std::span allowed; + bool unrestricted; + public: + constexpr LibertyFilter() : allowed{}, unrestricted(true) {} + constexpr explicit LibertyFilter(std::span sorted_ids) + : allowed(sorted_ids), unrestricted(false) {} + + constexpr bool allows(std::string_view id) const { + return unrestricted || std::binary_search(allowed.begin(), allowed.end(), id); + } + + constexpr bool covers(const LibertyFilter &other) const { + if (unrestricted) + return true; + if (other.unrestricted) + return false; + return std::includes(allowed.begin(), allowed.end(), + other.allowed.begin(), other.allowed.end()); + } + + static constexpr LibertyFilter all() { return {}; } + }; + + template + consteval std::array liberty_names(const char *const (&ids)[N]) + { + std::array sorted{}; + for (std::size_t i = 0; i < N; i++) + sorted[i] = ids[i]; + std::sort(sorted.begin(), sorted.end()); + if (std::adjacent_find(sorted.begin(), sorted.end()) != sorted.end()) + throw "duplicate id in liberty name list"; + return sorted; + } + + template + consteval std::array merge_names( + const std::array &a, const std::array &b) + { + std::array merged{}; + std::merge(a.begin(), a.end(), b.begin(), b.end(), merged.begin()); + return merged; + } + + template + consteval auto merge_names(const std::array &a, + const std::array &b, const Rest &... rest) + { + return merge_names(merge_names(a, b), rest...); + } + + inline constexpr auto liberty_common_names = liberty_names({ + "library", "cell", "area", + }); + + inline constexpr auto liberty_pin_names = liberty_names({ + "pin", "direction", + }); + + inline constexpr auto liberty_ff_names = liberty_names({ + "ff", "clocked_on", "next_state", "clear", "preset", + }); + + inline constexpr auto read_liberty_names = merge_names( + liberty_common_names, liberty_pin_names, liberty_ff_names, + liberty_names({ + "bus", "type", "statetable", "ff_bank", "latch", "latch_bank", + "bus_type", "capacitance", "function", "three_state", + "base_type", "data_type", "bit_width", "bit_from", "bit_to", "downto", + "data_in", "enable", "clear_preset_var1", "clear_preset_var2", + })); + + inline constexpr auto dfflibmap_names = merge_names( + liberty_common_names, liberty_pin_names, liberty_ff_names, + liberty_names({ + "dont_use", "function", + })); + + inline constexpr auto clockgate_names = merge_names( + liberty_common_names, liberty_pin_names, + liberty_names({ + "dont_use", "clock_gating_integrated_cell", + "clock_gate_clock_pin", "clock_gate_enable_pin", + "clock_gate_out_pin", "clock_gate_test_pin", + })); + + inline constexpr auto stat_liberty_names = merge_names( + liberty_common_names, + liberty_names({ + "ff", "port_names", + "single_area_parameterised", "double_area_parameterised", + })); + + inline constexpr auto liberty_synthesis_names = + merge_names(read_liberty_names, dfflibmap_names, clockgate_names, stat_liberty_names); + + inline constexpr LibertyFilter liberty_synth_filter{liberty_synthesis_names}; + + static_assert(liberty_synth_filter.covers(LibertyFilter{read_liberty_names})); + static_assert(liberty_synth_filter.covers(LibertyFilter{dfflibmap_names})); + static_assert(liberty_synth_filter.covers(LibertyFilter{clockgate_names})); + static_assert(liberty_synth_filter.covers(LibertyFilter{stat_liberty_names})); + static_assert(!liberty_synth_filter.allows("timing")); + static_assert(!liberty_synth_filter.allows("internal_power")); + struct LibertyAst { std::string id, value; std::vector args; std::vector children; + LibertyFilter filter; ~LibertyAst(); const LibertyAst *find(std::string name) const; @@ -147,14 +259,20 @@ namespace Yosys LibertyAstCache() {}; ~LibertyAstCache() {}; public: - dict> cached; + struct CacheEntry { + LibertyFilter filter; + std::shared_ptr ast; + }; + + dict cached; bool cache_by_default = false; bool verbose = 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); + std::shared_ptr cached_ast(const std::string &fname, const LibertyFilter &filter); + void parsed_ast(const std::string &fname, const LibertyFilter &filter, + const std::shared_ptr &ast); static LibertyAstCache instance; }; #endif @@ -166,6 +284,16 @@ namespace Yosys private: LibertyInputStream f; int line; + LibertyFilter filter; + + struct ParseResult { + enum Kind { Node, Skipped, Closed } kind; + LibertyAst *ast; + + static ParseResult node(LibertyAst *ast) { return {Node, ast}; } + static ParseResult skipped() { return {Skipped, nullptr}; } + static ParseResult closed() { return {Closed, nullptr}; } + }; /* lexer return values: 'v': identifier, string, array range [...] -> str holds the token string @@ -178,7 +306,8 @@ namespace Yosys void report_unexpected_token(int tok); void parse_vector_range(int tok); int consume_wrecked_str(int tok, std::string& out_str); - LibertyAst *parse(bool top_level); + ParseResult try_parse(bool top_level, bool skipping); + LibertyAst *parse(bool top_level, bool skipping); void error() const; void error(const std::string &str) const; @@ -186,8 +315,9 @@ namespace Yosys std::shared_ptr shared_ast; const LibertyAst *ast = nullptr; - LibertyParser(std::istream &f) : f(f), line(1) { - shared_ast.reset(parse(true)); + LibertyParser(std::istream &f, LibertyFilter filter = LibertyFilter::all()) + : f(f), line(1), filter(filter) { + shared_ast.reset(parse(true, false)); ast = shared_ast.get(); if (!ast) { #ifdef FILTERLIB @@ -200,11 +330,12 @@ namespace Yosys } #ifndef FILTERLIB - LibertyParser(std::istream &f, const std::string &fname) : f(f), line(1) { - shared_ast = LibertyAstCache::instance.cached_ast(fname); + LibertyParser(std::istream &f, const std::string &fname, + LibertyFilter filter = LibertyFilter::all()) : f(f), line(1), filter(filter) { + shared_ast = LibertyAstCache::instance.cached_ast(fname, filter); if (!shared_ast) { - shared_ast.reset(parse(true)); - LibertyAstCache::instance.parsed_ast(fname, shared_ast); + shared_ast.reset(parse(true, false)); + LibertyAstCache::instance.parsed_ast(fname, filter, shared_ast); } ast = shared_ast.get(); if (!ast) {