liberty: add filtering for things we need

This commit is contained in:
Emil J. Tywoniak
2026-08-26 13:44:51 +02:00
parent 25b06f428e
commit 5e1c851ebc
6 changed files with 190 additions and 33 deletions
+1 -1
View File
@@ -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<std::string, std::tuple<int, int, bool>> global_type_map;
+1 -1
View File
@@ -801,7 +801,7 @@ void read_liberty_cellarea(dict<IdString, cell_area_t> &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) {
+1 -1
View File
@@ -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;
}
+1 -1
View File
@@ -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;
}
+45 -19
View File
@@ -49,17 +49,20 @@ using namespace Yosys;
LibertyAstCache LibertyAstCache::instance;
std::shared_ptr<const LibertyAst> LibertyAstCache::cached_ast(const std::string &fname)
std::shared_ptr<const LibertyAst> 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<const LibertyAst> &ast)
void LibertyAstCache::parsed_ast(const std::string &fname, const LibertyFilter &filter,
const std::shared_ptr<const LibertyAst> &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<std::string> args;
std::vector<LibertyAst *> 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
+141 -10
View File
@@ -22,7 +22,11 @@
#include "kernel/yosys.h"
#include <stdio.h>
#include <algorithm>
#include <array>
#include <span>
#include <string>
#include <string_view>
#include <vector>
#include <set>
@@ -33,11 +37,119 @@
namespace Yosys
{
class LibertyFilter
{
std::span<const std::string_view> allowed;
bool unrestricted;
public:
constexpr LibertyFilter() : allowed{}, unrestricted(true) {}
constexpr explicit LibertyFilter(std::span<const std::string_view> 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 <std::size_t N>
consteval std::array<std::string_view, N> liberty_names(const char *const (&ids)[N])
{
std::array<std::string_view, N> 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 <std::size_t A, std::size_t B>
consteval std::array<std::string_view, A + B> merge_names(
const std::array<std::string_view, A> &a, const std::array<std::string_view, B> &b)
{
std::array<std::string_view, A + B> merged{};
std::merge(a.begin(), a.end(), b.begin(), b.end(), merged.begin());
return merged;
}
template <std::size_t A, std::size_t B, typename... Rest>
consteval auto merge_names(const std::array<std::string_view, A> &a,
const std::array<std::string_view, B> &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<std::string> args;
std::vector<LibertyAst*> children;
LibertyFilter filter;
~LibertyAst();
const LibertyAst *find(std::string name) const;
@@ -147,14 +259,20 @@ namespace Yosys
LibertyAstCache() {};
~LibertyAstCache() {};
public:
dict<std::string, std::shared_ptr<const LibertyAst>> cached;
struct CacheEntry {
LibertyFilter filter;
std::shared_ptr<const LibertyAst> ast;
};
dict<std::string, CacheEntry> cached;
bool cache_by_default = false;
bool verbose = false;
dict<std::string, bool> cache_path;
std::shared_ptr<const LibertyAst> cached_ast(const std::string &fname);
void parsed_ast(const std::string &fname, const std::shared_ptr<const LibertyAst> &ast);
std::shared_ptr<const LibertyAst> cached_ast(const std::string &fname, const LibertyFilter &filter);
void parsed_ast(const std::string &fname, const LibertyFilter &filter,
const std::shared_ptr<const LibertyAst> &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<const LibertyAst> 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) {