mirror of
https://github.com/YosysHQ/yosys
synced 2026-07-23 23:52:32 +00:00
twine
This commit is contained in:
parent
3d27e83d0f
commit
3424c00cd0
63 changed files with 2635 additions and 503 deletions
|
|
@ -1106,7 +1106,31 @@ std::string AstNode::loc_string() const
|
|||
|
||||
void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast)
|
||||
{
|
||||
obj->attributes[ID::src] = ast->loc_string();
|
||||
// All AttrObjects in genrtlil — Cell/Wire/Module/Process AND the inner
|
||||
// types (CaseRule, SwitchRule, MemWriteAction) — share current_module's
|
||||
// design's twine pool. process_module attaches current_module->design
|
||||
// early so this is reachable.
|
||||
if (!current_module || !current_module->design)
|
||||
return;
|
||||
const auto &loc = ast->location;
|
||||
if (!loc.begin.filename || loc.begin.filename->empty()) {
|
||||
obj->set_src_attribute(¤t_module->design->src_twines, ast->loc_string());
|
||||
return;
|
||||
}
|
||||
// Split filename and per-location tail so the filename interns once
|
||||
// per file and every cell/wire src for that file gets a Suffix node
|
||||
// carrying only ":line.col-line.col". For a typical large design with
|
||||
// thousands of objects in one file this collapses N copies of a long
|
||||
// path into 1 Leaf + N short Suffix tails.
|
||||
TwinePool *pool = ¤t_module->design->src_twines;
|
||||
Twine::Id file_id = pool->intern(*loc.begin.filename);
|
||||
std::string tail = stringf(":%d.%d-%d.%d",
|
||||
loc.begin.line, loc.begin.column,
|
||||
loc.end.line, loc.end.column);
|
||||
Twine::Id suffix_id = pool->intern_suffix(file_id, tail);
|
||||
pool->release(file_id); // suffix internally holds a ref now
|
||||
obj->set_src_id(pool, suffix_id);
|
||||
pool->release(suffix_id); // set_src_id retained on obj's behalf
|
||||
}
|
||||
|
||||
static bool param_has_no_default(const AstNode* param) {
|
||||
|
|
@ -1130,6 +1154,12 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d
|
|||
|
||||
AstModule *module = new AstModule;
|
||||
current_module = module;
|
||||
// Set design backpointer early — every set_src_attr in genrtlil.cc
|
||||
// resolves the pool via current_module->design->src_twines. The
|
||||
// final design->add(current_module) at end-of-process_module hooks
|
||||
// the module into the design's modules_ dict; we just need design
|
||||
// reachable as a backpointer for src interning meanwhile.
|
||||
module->design = design;
|
||||
|
||||
module->ast = nullptr;
|
||||
module->name = ast->str;
|
||||
|
|
@ -1920,6 +1950,29 @@ RTLIL::Module *AstModule::clone() const
|
|||
return new_mod;
|
||||
}
|
||||
|
||||
RTLIL::Module *AstModule::clone(RTLIL::Design *dst, bool src_id_verbatim) const
|
||||
{
|
||||
AstModule *new_mod = new AstModule;
|
||||
new_mod->name = name;
|
||||
dst->add(new_mod);
|
||||
cloneInto(new_mod, src_id_verbatim);
|
||||
|
||||
new_mod->ast = ast->clone();
|
||||
new_mod->nolatches = nolatches;
|
||||
new_mod->nomeminit = nomeminit;
|
||||
new_mod->nomem2reg = nomem2reg;
|
||||
new_mod->mem2reg = mem2reg;
|
||||
new_mod->noblackbox = noblackbox;
|
||||
new_mod->lib = lib;
|
||||
new_mod->nowb = nowb;
|
||||
new_mod->noopt = noopt;
|
||||
new_mod->icells = icells;
|
||||
new_mod->pwires = pwires;
|
||||
new_mod->autowire = autowire;
|
||||
|
||||
return new_mod;
|
||||
}
|
||||
|
||||
void AstModule::loadconfig() const
|
||||
{
|
||||
current_ast = NULL;
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@ namespace AST
|
|||
void expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces) override;
|
||||
bool reprocess_if_necessary(RTLIL::Design *design) override;
|
||||
RTLIL::Module *clone() const override;
|
||||
RTLIL::Module *clone(RTLIL::Design *dst, bool src_id_verbatim = false) const override;
|
||||
void loadconfig() const;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -167,7 +167,9 @@ static void check_unique_id(RTLIL::Module *module, RTLIL::IdString id,
|
|||
const AstNode *node, const char *to_add_kind)
|
||||
{
|
||||
auto already_exists = [&](const RTLIL::AttrObject *existing, const char *existing_kind) {
|
||||
std::string src = existing->get_string_attribute(ID::src);
|
||||
std::string src;
|
||||
if (module->design)
|
||||
src = existing->get_src_attribute(&module->design->src_twines);
|
||||
std::string location_str = "earlier";
|
||||
if (!src.empty())
|
||||
location_str = "at " + src;
|
||||
|
|
|
|||
|
|
@ -287,6 +287,25 @@ void json_parse_attr_param(dict<IdString, Const> &results, JsonNode *node)
|
|||
}
|
||||
}
|
||||
|
||||
// AttrObject-aware overload: extracts ID::src and routes it to the typed
|
||||
// src_id_ field via set_src_attribute. Other keys still land in the
|
||||
// attributes dict via the generic path.
|
||||
void json_parse_attributes(TwinePool *pool, RTLIL::AttrObject *obj, JsonNode *node)
|
||||
{
|
||||
if (node->type != 'D')
|
||||
log_error("JSON attributes or parameters node is not a dictionary.\n");
|
||||
|
||||
for (auto it : node->data_dict)
|
||||
{
|
||||
IdString key = RTLIL::escape_id(it.first.c_str());
|
||||
Const value = json_parse_attr_param_value(it.second);
|
||||
if (key == ID::src && (value.flags & RTLIL::CONST_FLAG_STRING))
|
||||
obj->set_src_attribute(pool, value.decode_string());
|
||||
else
|
||||
obj->attributes[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void json_import(Design *design, string &modname, JsonNode *node)
|
||||
{
|
||||
log("Importing module %s from JSON tree.\n", modname);
|
||||
|
|
@ -300,7 +319,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
design->add(module);
|
||||
|
||||
if (node->data_dict.count("attributes"))
|
||||
json_parse_attr_param(module->attributes, node->data_dict.at("attributes"));
|
||||
json_parse_attributes(&design->src_twines, module, node->data_dict.at("attributes"));
|
||||
|
||||
if (node->data_dict.count("parameter_default_values"))
|
||||
json_parse_attr_param(module->parameter_default_values, node->data_dict.at("parameter_default_values"));
|
||||
|
|
@ -483,7 +502,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
}
|
||||
|
||||
if (net_node->data_dict.count("attributes"))
|
||||
json_parse_attr_param(wire->attributes, net_node->data_dict.at("attributes"));
|
||||
json_parse_attributes(&design->src_twines, wire, net_node->data_dict.at("attributes"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -564,7 +583,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
}
|
||||
|
||||
if (cell_node->data_dict.count("attributes"))
|
||||
json_parse_attr_param(cell->attributes, cell_node->data_dict.at("attributes"));
|
||||
json_parse_attributes(&design->src_twines, cell, cell_node->data_dict.at("attributes"));
|
||||
|
||||
if (cell_node->data_dict.count("parameters"))
|
||||
json_parse_attr_param(cell->parameters, cell_node->data_dict.at("parameters"));
|
||||
|
|
@ -611,7 +630,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
|
|||
}
|
||||
|
||||
if (memory_node->data_dict.count("attributes"))
|
||||
json_parse_attr_param(mem->attributes, memory_node->data_dict.at("attributes"));
|
||||
json_parse_attributes(&design->src_twines, mem, memory_node->data_dict.at("attributes"));
|
||||
|
||||
module->memories[mem->name] = mem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "kernel/register.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/utils.h"
|
||||
#include "kernel/twine.h"
|
||||
#include <charconv>
|
||||
#include <deque>
|
||||
#include <optional>
|
||||
|
|
@ -52,6 +53,14 @@ struct RTLILFrontendWorker {
|
|||
std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack;
|
||||
std::vector<RTLIL::CaseRule*> case_stack;
|
||||
|
||||
// Remap from file-local twine ids (as they appear in the `twines` block
|
||||
// and on cell/wire src attrs) to ids in design->src_twines. Filled by
|
||||
// parse_twines; consumed by parse_attribute. Parser-side ids retained
|
||||
// during parse_twines are tracked here so they can be released at
|
||||
// end-of-parse — only the cell/wire references should survive.
|
||||
dict<Twine::Id, Twine::Id> twine_remap;
|
||||
std::vector<Twine::Id> twine_parser_holds;
|
||||
|
||||
template <typename... Args>
|
||||
[[noreturn]]
|
||||
void error(FmtString<TypeIdentity<Args>...> fmt, const Args &... args)
|
||||
|
|
@ -436,9 +445,15 @@ struct RTLILFrontendWorker {
|
|||
|
||||
current_module = new RTLIL::Module;
|
||||
current_module->name = std::move(module_name);
|
||||
current_module->attributes = std::move(attrbuf);
|
||||
if (!delete_current_module)
|
||||
if (delete_current_module) {
|
||||
// Module is about to be discarded — drop its src attribute
|
||||
// rather than push it into a pool we'll never reach.
|
||||
attrbuf.erase(ID::src);
|
||||
current_module->attributes = std::move(attrbuf);
|
||||
} else {
|
||||
design->add(current_module);
|
||||
current_module->absorb_attrs(&design->src_twines, std::move(attrbuf));
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
|
@ -491,10 +506,105 @@ struct RTLILFrontendWorker {
|
|||
{
|
||||
RTLIL::IdString id = parse_id();
|
||||
RTLIL::Const c = parse_const();
|
||||
// The '|' separator inside a src attribute is a Yosys-internal
|
||||
// merge convention emitted only by the legacy strpool path or by
|
||||
// a `dump -resolve-src`; no external tool should be producing it.
|
||||
// Warn so the producer learns to emit one path:line.col per
|
||||
// attribute. We don't try to repair the value — the user's input
|
||||
// is wrong and silently interning it would hide that.
|
||||
if (id == RTLIL::ID::src && (c.flags & RTLIL::CONST_FLAG_STRING)) {
|
||||
std::string raw = c.decode_string();
|
||||
Twine::Id file_id = TwinePool::parse_ref(raw);
|
||||
if (file_id != Twine::Null) {
|
||||
// Translate the file-local twine id to the destination
|
||||
// design's pool id via twine_remap. If the file had no
|
||||
// `twines` block (legacy) the remap is empty — accept the
|
||||
// ref verbatim and let downstream code intern it.
|
||||
auto it = twine_remap.find(file_id);
|
||||
if (it != twine_remap.end())
|
||||
c = RTLIL::Const(TwinePool::format_ref(it->second));
|
||||
} else if (raw.find('|') != std::string::npos) {
|
||||
log_warning("line %d: src attribute %s contains '|' separators. "
|
||||
"That convention is Yosys-internal; the producing tool "
|
||||
"should emit a single path:line.col per attribute and "
|
||||
"let Yosys merge through the twine pool.\n",
|
||||
line_num, raw.c_str());
|
||||
}
|
||||
}
|
||||
attrbuf.insert({std::move(id), std::move(c)});
|
||||
expect_eol();
|
||||
}
|
||||
|
||||
// Parses a `twines` ... `end` block. Builds twine_remap so subsequent
|
||||
// cell/wire src "@N" references (which use file-local ids) translate
|
||||
// to ids in design->src_twines. The destination pool may already be
|
||||
// non-empty (multi-file load) — interned/concated nodes are dedup'd
|
||||
// against the existing pool by the pool itself. Each parser-side
|
||||
// retain is tracked in twine_parser_holds and released at end-of-parse
|
||||
// so only cell/wire references survive.
|
||||
void parse_twines()
|
||||
{
|
||||
expect_eol();
|
||||
while (true) {
|
||||
if (try_parse_keyword("end"))
|
||||
break;
|
||||
if (try_parse_keyword("leaf")) {
|
||||
int file_id = static_cast<int>(parse_integer());
|
||||
std::string text = parse_string();
|
||||
expect_eol();
|
||||
Twine::Id local_id = design->src_twines.intern(text);
|
||||
twine_parser_holds.push_back(local_id);
|
||||
twine_remap[static_cast<Twine::Id>(file_id)] = local_id;
|
||||
continue;
|
||||
}
|
||||
if (try_parse_keyword("suffix")) {
|
||||
int file_id = static_cast<int>(parse_integer());
|
||||
Twine::Id file_parent = static_cast<Twine::Id>(parse_integer());
|
||||
std::string tail = parse_string();
|
||||
expect_eol();
|
||||
auto it = twine_remap.find(file_parent);
|
||||
if (it == twine_remap.end())
|
||||
error("twines: suffix %d references undefined parent %u.",
|
||||
file_id, file_parent);
|
||||
Twine::Id local_id = design->src_twines.intern_suffix(it->second, tail);
|
||||
twine_parser_holds.push_back(local_id);
|
||||
twine_remap[static_cast<Twine::Id>(file_id)] = local_id;
|
||||
continue;
|
||||
}
|
||||
if (try_parse_keyword("concat")) {
|
||||
int file_id = static_cast<int>(parse_integer());
|
||||
std::vector<Twine::Id> children;
|
||||
while (!try_parse_eol()) {
|
||||
Twine::Id file_child = static_cast<Twine::Id>(parse_integer());
|
||||
auto it = twine_remap.find(file_child);
|
||||
if (it == twine_remap.end())
|
||||
error("twines: concat %d references undefined leaf/concat %u.",
|
||||
file_id, file_child);
|
||||
children.push_back(it->second);
|
||||
}
|
||||
Twine::Id local_id = design->src_twines.concat(
|
||||
std::span<const Twine::Id>{children});
|
||||
twine_parser_holds.push_back(local_id);
|
||||
twine_remap[static_cast<Twine::Id>(file_id)] = local_id;
|
||||
continue;
|
||||
}
|
||||
error("Expected `leaf`, `suffix` or `concat` inside twines block, got `%s'.",
|
||||
error_token());
|
||||
}
|
||||
expect_eol();
|
||||
}
|
||||
|
||||
// Release the per-file parser refs gathered during parse_twines. Call
|
||||
// once the entire file has been parsed and every cell/wire that ever
|
||||
// referred to a file_id has already adopted the corresponding local_id.
|
||||
void release_twine_parser_holds()
|
||||
{
|
||||
for (Twine::Id id : twine_parser_holds)
|
||||
design->src_twines.release(id);
|
||||
twine_parser_holds.clear();
|
||||
twine_remap.clear();
|
||||
}
|
||||
|
||||
void parse_parameter()
|
||||
{
|
||||
RTLIL::IdString id = parse_id();
|
||||
|
|
@ -556,7 +666,7 @@ struct RTLILFrontendWorker {
|
|||
error("Unexpected wire option: %s", error_token());
|
||||
}
|
||||
|
||||
wire->attributes = std::move(attrbuf);
|
||||
wire->absorb_attrs(&design->src_twines, std::move(attrbuf));
|
||||
wire->width = width;
|
||||
wire->upto = upto;
|
||||
wire->start_offset = start_offset;
|
||||
|
|
@ -570,7 +680,7 @@ struct RTLILFrontendWorker {
|
|||
void parse_memory()
|
||||
{
|
||||
RTLIL::Memory *memory = new RTLIL::Memory;
|
||||
memory->attributes = std::move(attrbuf);
|
||||
memory->absorb_attrs(&design->src_twines, std::move(attrbuf));
|
||||
|
||||
int width = 1;
|
||||
int start_offset = 0;
|
||||
|
|
@ -638,7 +748,7 @@ struct RTLILFrontendWorker {
|
|||
error("RTLIL error: redefinition of cell %s.", cell_name);
|
||||
}
|
||||
RTLIL::Cell *cell = current_module->addCell(cell_name, cell_type);
|
||||
cell->attributes = std::move(attrbuf);
|
||||
cell->absorb_attrs(&design->src_twines, std::move(attrbuf));
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
|
@ -728,7 +838,7 @@ struct RTLILFrontendWorker {
|
|||
{
|
||||
RTLIL::SwitchRule *rule = new RTLIL::SwitchRule;
|
||||
rule->signal = parse_sigspec();
|
||||
rule->attributes = std::move(attrbuf);
|
||||
rule->absorb_attrs(&design->src_twines, std::move(attrbuf));
|
||||
switch_stack.back()->push_back(rule);
|
||||
expect_eol();
|
||||
|
||||
|
|
@ -745,7 +855,7 @@ struct RTLILFrontendWorker {
|
|||
|
||||
expect_keyword("case");
|
||||
RTLIL::CaseRule *case_rule = new RTLIL::CaseRule;
|
||||
case_rule->attributes = std::move(attrbuf);
|
||||
case_rule->absorb_attrs(&design->src_twines, std::move(attrbuf));
|
||||
rule->cases.push_back(case_rule);
|
||||
switch_stack.push_back(&case_rule->switches);
|
||||
case_stack.push_back(case_rule);
|
||||
|
|
@ -779,7 +889,7 @@ struct RTLILFrontendWorker {
|
|||
error("RTLIL error: redefinition of process %s.", proc_name);
|
||||
}
|
||||
RTLIL::Process *proc = current_module->addProcess(std::move(proc_name));
|
||||
proc->attributes = std::move(attrbuf);
|
||||
proc->absorb_attrs(&design->src_twines, std::move(attrbuf));
|
||||
|
||||
switch_stack.clear();
|
||||
switch_stack.push_back(&proc->root_case.switches);
|
||||
|
|
@ -828,7 +938,7 @@ struct RTLILFrontendWorker {
|
|||
break;
|
||||
|
||||
RTLIL::MemWriteAction act;
|
||||
act.attributes = std::move(attrbuf);
|
||||
act.absorb_attrs(&design->src_twines, std::move(attrbuf));
|
||||
act.memid = parse_id();
|
||||
act.address = parse_sigspec();
|
||||
act.data = parse_sigspec();
|
||||
|
|
@ -869,10 +979,15 @@ struct RTLILFrontendWorker {
|
|||
expect_eol();
|
||||
continue;
|
||||
}
|
||||
if (try_parse_keyword("twines")) {
|
||||
parse_twines();
|
||||
continue;
|
||||
}
|
||||
error("Unexpected token: %s", error_token());
|
||||
}
|
||||
if (attrbuf.size() != 0)
|
||||
error("dangling attribute");
|
||||
release_twine_parser_holds();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue