mirror of
https://github.com/YosysHQ/yosys.git
synced 2026-09-05 11:35:20 +02:00
liberty: add filtering for things we need
This commit is contained in:
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user