twine: fix replayability, reduce TwineSearch usage

This commit is contained in:
Emil J. Tywoniak 2026-06-22 17:53:19 +02:00
parent e9eb3889b7
commit 7c73fd62e4
41 changed files with 273 additions and 272 deletions

View File

@ -79,6 +79,7 @@ struct BtorWorker
dict<SigBit, bool> initbits;
pool<Wire*> statewires;
pool<string> srcsymbols;
TwineSearch src_search;
vector<Mem> memories;
dict<Cell*, Mem*> mem_cells;
@ -125,11 +126,11 @@ struct BtorWorker
string src = module && module->design ? module->design->get_src_attribute(obj) : std::string();
if (srcsym && infostr[0] == '$') {
std::replace(src.begin(), src.end(), ' ', '_');
TwineRef src_ref = TwineSearch(&module->design->twines).find(src);
TwineRef src_ref = src_search.find(src);
if (srcsymbols.count(src) || src_ref != Twine::Null) {
for (int i = 1;; i++) {
string s = stringf("%s-%d", src, i);
TwineRef s_ref = TwineSearch(&module->design->twines).find(s);
TwineRef s_ref = src_search.find(s);
if (!srcsymbols.count(s) && s_ref == Twine::Null) {
src = s;
break;
@ -153,11 +154,11 @@ struct BtorWorker
string src = module && module->design ? module->design->get_src_attribute(mem) : std::string();
if (srcsym && infostr[0] == '$') {
std::replace(src.begin(), src.end(), ' ', '_');
TwineRef src_ref = TwineSearch(&module->design->twines).find(src);
TwineRef src_ref = src_search.find(src);
if (srcsymbols.count(src) || src_ref != Twine::Null) {
for (int i = 1;; i++) {
string s = stringf("%s-%d", src, i);
TwineRef s_ref = TwineSearch(&module->design->twines).find(s);
TwineRef s_ref = src_search.find(s);
if (!srcsymbols.count(s) && s_ref == Twine::Null) {
src = s;
break;
@ -1193,7 +1194,7 @@ struct BtorWorker
}
BtorWorker(std::ostream &f, RTLIL::Module *module, bool verbose, bool single_bad, bool cover_mode, bool print_internal_names, string info_filename, string ywmap_filename) :
f(f), sigmap(module), module(module), verbose(verbose), single_bad(single_bad), cover_mode(cover_mode), print_internal_names(print_internal_names), info_filename(info_filename)
f(f), sigmap(module), module(module), verbose(verbose), single_bad(single_bad), cover_mode(cover_mode), print_internal_names(print_internal_names), src_search(&module->design->twines), info_filename(info_filename)
{
if (!info_filename.empty())
infof("name %s\n", module);

View File

@ -138,8 +138,6 @@ struct JsonWriter
void write_parameters(const dict<IdString, Const> &parameters, bool for_module=false, const RTLIL::AttrObject *src_obj=nullptr)
{
bool first = true;
// Emit the typed src field first if present — it lives outside the
// attribute dict after the typed-src migration.
if (src_obj && design && design->obj_src_id(src_obj) != Twine::Null) {
f << stringf("\n %s%s: ", for_module ? "" : " ", get_name(RTLIL::ID::src));
write_parameter_value(RTLIL::Const(design->get_src_attribute(src_obj)));

View File

@ -34,9 +34,6 @@ YOSYS_NAMESPACE_BEGIN
void RTLIL_BACKEND::dump_attributes(std::ostream &f, std::string indent, const RTLIL::AttrObject *obj, const RTLIL::Design *design, bool stringify)
{
// Emit the typed src field first. It is not stored in obj->attributes
// — the dict no longer holds ID::src under any circumstance. Backends
// that want to materialize the pipe-joined literal pass stringify.
if (design && design->obj_src_id(obj) != Twine::Null) {
TwineRef id = design->obj_src_id(obj);
f << stringf("%s" "attribute \\src ", indent);

View File

@ -269,13 +269,14 @@ end_of_header:
else
log_abort();
RTLIL::Wire* n0 = module->wire(TwineSearch(&design->twines).find(stringf("$aiger%d$0", aiger_autoidx)));
RTLIL::Wire* n0 = aiger_wires.count(0) ? aiger_wires.at(0) : nullptr;
if (n0)
module->connect(n0, State::S0);
// Parse footer (symbol table, comments, etc.)
unsigned l1;
std::string s;
TwineSearch search(&design->twines);
for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) {
if (c == 'i' || c == 'l' || c == 'o' || c == 'b') {
f.ignore(1);
@ -294,7 +295,7 @@ end_of_header:
log_assert(l1 < latches.size());
wire = latches[l1];
} else if (c == 'o') {
wire = module->wire(TwineSearch(&design->twines).find(escaped_s.str()));
wire = module->wire(search.find(escaped_s.str()));
log_assert(l1 < outputs.size());
if (wire) {
// Could have been renamed by a latch
@ -307,7 +308,7 @@ end_of_header:
wire = bad_properties[l1];
} else log_abort();
module->rename(wire, design->twines.add(std::string{escaped_s.str()}));
module->rename(wire, intern_name(escaped_s.str(), search));
}
else if (c == 'j' || c == 'f') {
// TODO
@ -346,25 +347,28 @@ RTLIL::Wire* AigerReader::createWireIfNotExists(RTLIL::Module *module, unsigned
{
const unsigned variable = literal >> 1;
const bool invert = literal & 1;
if (auto it = aiger_wires.find(literal); it != aiger_wires.end())
return it->second;
RTLIL::IdString wire_name(stringf("$aiger%d$%d%s", aiger_autoidx, variable, invert ? "b" : ""));
RTLIL::Wire *wire = module->wire(TwineSearch(&design->twines).find(wire_name.str()));
if (wire) return wire;
log_debug2("Creating %s\n", wire_name.c_str());
wire = module->addWire(design->twines.add(std::string{wire_name.str()}));
RTLIL::Wire *wire = module->addWire(design->twines.add(std::string{wire_name.str()}));
wire->port_input = wire->port_output = false;
aiger_wires[literal] = wire;
if (!invert) return wire;
RTLIL::IdString wire_inv_name(stringf("$aiger%d$%d", aiger_autoidx, variable));
RTLIL::Wire *wire_inv = module->wire(TwineSearch(&design->twines).find(wire_inv_name.str()));
if (wire_inv) {
if (module->cell(TwineSearch(&design->twines).find(wire_inv_name.str()))) return wire;
const unsigned base_literal = variable << 1;
RTLIL::Wire *wire_inv;
if (auto it = aiger_wires.find(base_literal); it != aiger_wires.end()) {
wire_inv = it->second;
}
else {
RTLIL::IdString wire_inv_name(stringf("$aiger%d$%d", aiger_autoidx, variable));
log_debug2("Creating %s\n", wire_inv_name.c_str());
wire_inv = module->addWire(design->twines.add(std::string{wire_inv_name.str()}));
wire_inv->port_input = wire_inv->port_output = false;
aiger_wires[base_literal] = wire_inv;
}
log_debug2("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str());
log_debug2("Creating %s = ~$aiger%d$%d\n", wire_name.c_str(), aiger_autoidx, variable);
module->addNotGate(Twine{stringf("$not$aiger%d$%d", aiger_autoidx, variable)}, wire_inv, wire);
return wire;
@ -402,7 +406,7 @@ void AigerReader::parse_xaiger()
else
log_abort();
RTLIL::Wire* n0 = module->wire(TwineSearch(&design->twines).find(stringf("$aiger%d$0", aiger_autoidx)));
RTLIL::Wire* n0 = aiger_wires.count(0) ? aiger_wires.at(0) : nullptr;
if (n0)
module->connect(n0, State::S0);
@ -422,11 +426,12 @@ void AigerReader::parse_xaiger()
uint32_t lutSize = parse_xaiger_literal(f);
log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize);
ConstEvalAig ce(module);
TwineSearch search(&design->twines);
for (unsigned i = 0; i < lutNum; ++i) {
uint32_t rootNodeID = parse_xaiger_literal(f);
uint32_t cutLeavesM = parse_xaiger_literal(f);
log_debug2("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM);
RTLIL::Wire *output_sig = module->wire(TwineSearch(&design->twines).find(stringf("$aiger%d$%d", aiger_autoidx, rootNodeID)));
RTLIL::Wire *output_sig = module->wire(search.find(stringf("$aiger%d$%d", aiger_autoidx, rootNodeID)));
log_assert(output_sig);
uint32_t nodeID;
RTLIL::SigSpec input_sig;
@ -437,7 +442,7 @@ void AigerReader::parse_xaiger()
log_debug("\tLUT '$lut$aiger%d$%d' input %d is constant!\n", aiger_autoidx, rootNodeID, cutLeavesM);
continue;
}
RTLIL::Wire *wire = module->wire(TwineSearch(&design->twines).find(stringf("$aiger%d$%d", aiger_autoidx, nodeID)));
RTLIL::Wire *wire = module->wire(search.find(stringf("$aiger%d$%d", aiger_autoidx, nodeID)));
log_assert(wire);
input_sig.append(wire);
}
@ -456,7 +461,7 @@ void AigerReader::parse_xaiger()
log_assert(o.wire == nullptr);
lut_mask.set(gray, o.data);
}
RTLIL::Cell *output_cell = module->cell(TwineSearch(&design->twines).find(stringf("$and$aiger%d$%d", aiger_autoidx, rootNodeID)));
RTLIL::Cell *output_cell = module->cell(search.find(stringf("$and$aiger%d$%d", aiger_autoidx, rootNodeID)));
log_assert(output_cell);
module->remove(output_cell);
module->addLut(Twine{stringf("$lut$aiger%d$%d", aiger_autoidx, rootNodeID)}, input_sig, output_sig, std::move(lut_mask));
@ -772,6 +777,21 @@ void AigerReader::parse_aiger_binary()
}
}
// add(std::string) always interns a fresh leaf, but an equal-content name may
// already exist in the pool as a NEW_TWINE suffix (auto-generated names share a
// prefix leaf). Reuse that ref so every wire/cell with the same name string is
// keyed by a single canonical ref; otherwise leaf-vs-suffix refs diverge and
// module->wire()/cell() lookups miss.
TwineRef AigerReader::intern_name(const std::string &escaped, TwineSearch &search)
{
TwineRef existing = search.find(escaped);
if (existing != Twine::Null)
return existing;
TwineRef ref = design->twines.add(std::string{escaped});
search.insert(ref);
return ref;
}
void AigerReader::post_process()
{
unsigned ci_count = 0, co_count = 0;
@ -816,6 +836,7 @@ void AigerReader::post_process()
std::ifstream mf(map_filename);
std::string type, symbol;
int variable, index;
TwineSearch search(&design->twines);
while (mf >> type >> variable >> index >> symbol) {
RTLIL::IdString escaped_s = RTLIL::escape_id(symbol);
if (type == "input") {
@ -830,9 +851,9 @@ void AigerReader::post_process()
// Cope with the fact that a CI might be identical
// to a PI (necessary due to ABC); in those cases
// simply connect the latter to the former
existing = module->wire(TwineSearch(&design->twines).find(escaped_s.str()));
existing = module->wire(search.find(escaped_s.str()));
if (!existing)
module->rename(wire, design->twines.add(std::string{escaped_s.str()}));
module->rename(wire, intern_name(escaped_s.str(), search));
else {
wire->port_input = false;
module->connect(wire, existing);
@ -841,9 +862,9 @@ void AigerReader::post_process()
}
else {
RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s, index);
existing = module->wire(TwineSearch(&design->twines).find(indexed_name.str()));
existing = module->wire(search.find(indexed_name.str()));
if (!existing)
module->rename(wire, design->twines.add(std::string{indexed_name.str()}));
module->rename(wire, intern_name(indexed_name.str(), search));
else {
module->connect(wire, existing);
wire->port_input = false;
@ -875,9 +896,9 @@ void AigerReader::post_process()
// Cope with the fact that a CO might be identical
// to a PO (necessary due to ABC); in those cases
// simply connect the latter to the former
existing = module->wire(TwineSearch(&design->twines).find(escaped_s.str()));
existing = module->wire(search.find(escaped_s.str()));
if (!existing)
module->rename(wire, design->twines.add(std::string{escaped_s.str()}));
module->rename(wire, intern_name(escaped_s.str(), search));
else {
wire->port_output = false;
existing->port_output = true;
@ -888,9 +909,9 @@ void AigerReader::post_process()
}
else {
RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s, index);
existing = module->wire(TwineSearch(&design->twines).find(indexed_name.str()));
existing = module->wire(search.find(indexed_name.str()));
if (!existing)
module->rename(wire, design->twines.add(std::string{indexed_name.str()}));
module->rename(wire, intern_name(indexed_name.str(), search));
else {
wire->port_output = false;
existing->port_output = true;
@ -912,17 +933,18 @@ void AigerReader::post_process()
}
}
else if (type == "box") {
RTLIL::Cell* cell = module->cell(TwineSearch(&design->twines).find(stringf("$box%d", variable)));
RTLIL::Cell* cell = module->cell(search.find(stringf("$box%d", variable)));
if (!cell)
log_debug("Box %d (%s) no longer exists.\n", variable, log_id(escaped_s));
else
module->rename(cell, design->twines.add(std::string{escaped_s.str()}));
module->rename(cell, intern_name(escaped_s.str(), search));
}
else
log_error("Symbol type '%s' not recognised.\n", type);
}
}
TwineSearch wp_search(&design->twines);
for (auto &wp : wideports_cache) {
auto name = wp.first;
int min = wp.second.first;
@ -930,30 +952,33 @@ void AigerReader::post_process()
if (min == 0 && max == 0)
continue;
RTLIL::Wire *wire = module->wire(TwineSearch(&design->twines).find(name.str()));
if (wire)
module->rename(wire, design->twines.add(std::string{RTLIL::escape_id(stringf("%s[%d]", name.str(), 0))}));
RTLIL::Wire *wire = module->wire(wp_search.find(name.str()));
if (wire) {
TwineRef renamed = design->twines.add(std::string{RTLIL::escape_id(stringf("%s[%d]", name.str(), 0))});
wp_search.insert(renamed);
module->rename(wire, renamed);
}
// Do not make ports with a mix of input/output into
// wide ports
bool port_input = false, port_output = false;
for (int i = min; i <= max; i++) {
RTLIL::IdString other_name = name.str() + stringf("[%d]", i);
RTLIL::Wire *other_wire = module->wire(TwineSearch(&design->twines).find(other_name.str()));
RTLIL::Wire *other_wire = module->wire(wp_search.find(other_name.str()));
if (other_wire) {
port_input = port_input || other_wire->port_input;
port_output = port_output || other_wire->port_output;
}
}
wire = module->addWire(design->twines.add(std::string{name.str()}), max-min+1);
wire = module->addWire(intern_name(name.str(), wp_search), max-min+1);
wire->start_offset = min;
wire->port_input = port_input;
wire->port_output = port_output;
for (int i = min; i <= max; i++) {
RTLIL::IdString other_name = stringf("%s[%d]", name, i);
RTLIL::Wire *other_wire = module->wire(TwineSearch(&design->twines).find(other_name.str()));
RTLIL::Wire *other_wire = module->wire(wp_search.find(other_name.str()));
if (other_wire) {
other_wire->port_input = false;
other_wire->port_output = false;

View File

@ -46,6 +46,7 @@ struct AigerReader
std::vector<RTLIL::Wire*> bad_properties;
std::vector<RTLIL::Cell*> boxes;
std::vector<int> mergeability, initial_state;
dict<unsigned, RTLIL::Wire*> aiger_wires;
AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports);
void parse_aiger();
@ -54,6 +55,7 @@ struct AigerReader
void parse_aiger_binary();
void post_process();
TwineRef intern_name(const std::string &escaped, TwineSearch &search);
RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal);
};

View File

@ -414,7 +414,7 @@ struct Xaiger2Frontend : public Frontend {
log_error("Bad map file: primary output literal out of range\n");
if (bits[lit] == RTLIL::Sm)
log_error("Bad map file: primary output literal is a marker\n");
Wire *w = module->wire(TwineSearch(&design->twines).find(name));
Wire *w = module->wire(search.find(name));
if (!w || woffset < 0 || woffset >= w->width)
log_error("Map file references non-existent signal bit %s[%d]\n",
name.c_str(), woffset);
@ -434,8 +434,8 @@ struct Xaiger2Frontend : public Frontend {
log_error("Bad map file: pseudo primary output literal out of range\n");
if (bits[lit] == RTLIL::Sm)
log_error("Bad map file: pseudo primary output literal is a marker\n");
Cell *cell = module->cell(TwineSearch(&design->twines).find(box_name));
auto box_port_ref = TwineSearch(&design->twines).find(box_port);
Cell *cell = module->cell(search.find(box_name));
auto box_port_ref = search.find(box_port);
if (!cell || !cell->hasPort(box_port_ref))
log_error("Map file references non-existent box port %s/%s\n",
box_name.c_str(), box_port.c_str());

View File

@ -1106,10 +1106,6 @@ std::string AstNode::loc_string() const
void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast)
{
// 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;
@ -1152,11 +1148,6 @@ 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->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;

View File

@ -312,7 +312,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
Module *module = new RTLIL::Module;
module->design = design;
module->meta_->name = design->twines.add(Twine{RTLIL::escape_id(modname)});
module->meta_->name = design->twines.add(RTLIL::escape_id(modname));
if (design->module(module->meta_->name))
log_error("Re-definition of module %s.\n", design->twines.str(module->meta_->name));
@ -327,6 +327,8 @@ void json_import(Design *design, string &modname, JsonNode *node)
dict<int, SigBit> signal_bits;
dict<IdString, Wire*> wire_cache;
if (node->data_dict.count("ports"))
{
JsonNode *ports_node = node->data_dict.at("ports");
@ -357,10 +359,12 @@ void json_import(Design *design, string &modname, JsonNode *node)
if (port_bits_node->type != 'A')
log_error("JSON port node '%s' has non-array bits attribute.\n", port_name.unescape());
Wire *port_wire = module->wire(TwineSearch(&design->twines).find(port_name.str()));
Wire *port_wire = wire_cache.count(port_name) ? wire_cache.at(port_name) : nullptr;
if (port_wire == nullptr)
port_wire = module->addWire(Twine{port_name.str()}, GetSize(port_bits_node->data_array));
if (port_wire == nullptr) {
port_wire = module->addWire(design->twines.add(port_name.str()), GetSize(port_bits_node->data_array));
wire_cache[port_name] = port_wire;
}
if (port_node->data_dict.count("upto") != 0) {
JsonNode *val = port_node->data_dict.at("upto");
@ -455,10 +459,12 @@ void json_import(Design *design, string &modname, JsonNode *node)
if (bits_node->type != 'A')
log_error("JSON netname node '%s' has non-array bits attribute.\n", net_name.unescape());
Wire *wire = module->wire(TwineSearch(&design->twines).find(net_name.str()));
Wire *wire = wire_cache.count(net_name) ? wire_cache.at(net_name) : nullptr;
if (wire == nullptr)
wire = module->addWire(Twine{net_name.str()}, GetSize(bits_node->data_array));
if (wire == nullptr) {
wire = module->addWire(design->twines.add(net_name.str()), GetSize(bits_node->data_array));
wire_cache[net_name] = wire;
}
if (net_node->data_dict.count("upto") != 0) {
JsonNode *val = net_node->data_dict.at("upto");
@ -532,7 +538,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
IdString cell_type = RTLIL::escape_id(type_node->data_string.c_str());
Cell *cell = module->addCell(Twine{cell_name.str()}, Twine{cell_type.str()});
Cell *cell = module->addCell(design->twines.add(cell_name.str()), design->twines.add(cell_type.str()));
if (cell_node->data_dict.count("connections") == 0)
log_error("JSON cells node '%s' has no connections attribute.\n", cell_name.unescape());
@ -580,7 +586,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
}
cell->setPort(design->twines.add(Twine{conn_name.str()}), sig);
cell->setPort(design->twines.add(conn_name.str()), sig);
}
if (cell_node->data_dict.count("attributes"))
@ -604,7 +610,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
JsonNode *memory_node = memory_node_it.second;
RTLIL::Memory *mem = new RTLIL::Memory;
mem->meta_->name = design->twines.add(Twine{memory_name.str()});
mem->meta_->name = design->twines.add(memory_name.str());
mem->module = module;
if (memory_node->type != 'D')

View File

@ -50,26 +50,14 @@ struct RTLILFrontendWorker {
RTLIL::Module *current_module;
dict<RTLIL::IdString, RTLIL::Const> attrbuf;
// A resolved "@N" src reference, carried from parse_attribute to the
// object's absorb_attrs site so it lands on the object without being
// flattened into a fresh leaf.
TwineRef pending_src = Twine::Null;
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->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<size_t, TwineRef> twine_remap;
std::vector<TwineRef> twine_parser_holds;
// Raw twines-block node descriptors keyed by file-local id. The block may
// list a node before its children (the writer emits in pool-index order,
// and free-list slot reuse can place a parent at a lower index than a
// child), so descriptors are collected first and materialized on demand in
// dependency order via materialize_file_twine.
struct TwineDesc {
enum Kind { Leaf, Suffix, Concat } kind;
std::string text;
@ -481,8 +469,6 @@ struct RTLILFrontendWorker {
current_module->design = design;
current_module->meta_->name = module_name;
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);
pending_src = Twine::Null;
current_module->attributes = std::move(attrbuf);
@ -543,18 +529,9 @@ 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();
if (!raw.empty() && raw[0] == '@') {
// Compact "@N" reference into the `twines` block: carry the
// resolved twine straight to the object (set after its
// absorb_attrs) so its structure is preserved, not flattened.
size_t file_id = 0;
auto [ptr, ec] = std::from_chars(raw.data() + 1, raw.data() + raw.size(), file_id);
if (ec != std::errc() || ptr != raw.data() + raw.size())
@ -591,10 +568,7 @@ struct RTLILFrontendWorker {
base = TwineRef(id);
} else {
auto it = twine_remap.find(id);
if (it == twine_remap.end())
base = materialize_file_twine(id);
else
base = it->second;
base = (it == twine_remap.end()) ? materialize_file_twine(id) : it->second;
}
return twine_tag(base, is_public);
}
@ -708,21 +682,17 @@ struct RTLILFrontendWorker {
error("Expected `leaf`, `suffix` or `concat` inside twines block, got `%s'.",
error_token());
}
std::vector<size_t> ordered_ids;
ordered_ids.reserve(twine_descs.size());
for (auto &it : twine_descs)
materialize_file_twine(it.first);
ordered_ids.push_back(it.first);
std::sort(ordered_ids.begin(), ordered_ids.end());
for (size_t id : ordered_ids)
materialize_file_twine(id);
twine_descs.clear();
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()
{
twine_parser_holds.clear();
twine_remap.clear();
}
void parse_parameter()
{
RTLIL::IdString id = parse_id();
@ -1080,9 +1050,6 @@ struct RTLILFrontendWorker {
act.enable = parse_sigspec();
act.priority_mask = parse_const();
rule->mem_write_actions.push_back(act);
// meta_idx_ is a weak ref — drop ours so the pushed copy
// in the vector is the sole holder. Process::~Process
// walks the tree to actually release.
act.meta_ = nullptr;
expect_eol();
}
@ -1126,7 +1093,9 @@ struct RTLILFrontendWorker {
}
if (attrbuf.size() != 0)
error("dangling attribute");
release_twine_parser_holds();
twine_parser_holds.clear();
twine_remap.clear();
}
};

View File

@ -39,12 +39,6 @@ void manufacture_info(InputType flop, OutputType& info, FfInitVals *initvals) {
info.sig_q = cell->getPort(TW::Q);
info.width = GetSize(info.sig_q);
info.attributes = cell->attributes;
// Carry src across construction → emit() as an owning Twine
// reference. Retaining a slot on the source pool keeps it
// alive even if the source cell gets removed between
// manufacture_info() and emit(); emit() then transfers the
// id verbatim into the new cell — no flatten/re-intern, no
// pipe-leaf risk for cells whose src is a Concat.
if (cell->src_id() != Twine::Null && cell->module && cell->module->design)
info.src_twine = cell->src_id();
if (initvals)
@ -762,7 +756,6 @@ Cell *FfData::emit() {
}
}
// src is carried in info.src_twine (an OwnedTwine retaining the
// source slot). Transfer the id verbatim to the new cell — same
// pool, no flatten. The OwnedTwine still holds its own ref until
// FfData is destroyed; set_src_id retains on the cell's behalf.
cell->attributes = attributes;

View File

@ -172,8 +172,9 @@ struct FfData : FfTypeData {
dict<IdString, Const> attributes;
// Stashed src across construction → emit. Refcount-managed so the
// source cell's pool slot survives if the cell itself is removed
// before emit() runs. Empty when the source cell had no src.
TwineRef src_twine;
// before emit() runs. Null when the source cell had no src (default
// TwineRef() is index 0, a valid constid, so it must be Null here).
TwineRef src_twine = Twine::Null;
FfData(Module *module = nullptr, FfInitVals *initvals = nullptr, IdString name = IdString()) : module(module), initvals(initvals), cell(nullptr), name(name) {
width = 0;

View File

@ -887,7 +887,6 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
return nullptr;
// Keep src as a "@N" reference into the design's twine pool throughout
// — never flatten to a literal path string. That way every addX call
// below adopts the same slot as Mem itself (via set_src_attribute's
// "@N" parse_ref path), and there's no flatten → re-intern → pipe-
// leaf round-trip on cells whose src is a Concat node.
@ -998,7 +997,6 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
IdString name = stringf("$%s$rdreg[%d]", memid, idx);
FfData ff(module, initvals, name);
// Carry mem's src into the ff via the OwnedTwine handle — same
// pool, direct id retain. emit() transfers verbatim.
ff.src_twine = mem_src;
ff.width = GetSize(port.data);

View File

@ -953,7 +953,6 @@ bool RTLIL::AttrObject::get_bool_attribute(IdString id) const
void RTLIL::AttrObject::set_string_attribute(IdString id, string value)
{
// ID::src on the base AttrObject is not routable here because the base
// has no Design context — callers needing string-form src must go
// through the subtype helper (Cell::set_src_attribute / Wire::… / …)
// which derives the design from context.
log_assert(id != ID::src && "set_string_attribute(ID::src,...) on AttrObject base; use the subtype helper");
@ -965,7 +964,6 @@ void RTLIL::AttrObject::set_string_attribute(IdString id, string value)
string RTLIL::AttrObject::get_string_attribute(IdString id) const
{
// ID::src is not in the dict — callers must use the subtype helper.
log_assert(id != ID::src && "get_string_attribute(ID::src) on AttrObject base; use the subtype helper");
std::string value;
const auto it = attributes.find(id);
@ -1821,22 +1819,22 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_modules(RTLIL::SelectPartial
switch (boxes)
{
case RTLIL::SB_UNBOXED_WARN:
log_warning("Ignoring boxed module %s.\n", twines.str(it.first).c_str());
log_warning("Ignoring boxed module %s.\n", log_id(it.second));
break;
case RTLIL::SB_EXCL_BB_WARN:
log_warning("Ignoring blackbox module %s.\n", twines.str(it.first).c_str());
log_warning("Ignoring blackbox module %s.\n", log_id(it.second));
break;
case RTLIL::SB_UNBOXED_ERR:
log_error("Unsupported boxed module %s.\n", twines.str(it.first).c_str());
log_error("Unsupported boxed module %s.\n", log_id(it.second));
break;
case RTLIL::SB_EXCL_BB_ERR:
log_error("Unsupported blackbox module %s.\n", twines.str(it.first).c_str());
log_error("Unsupported blackbox module %s.\n", log_id(it.second));
break;
case RTLIL::SB_UNBOXED_CMDERR:
log_cmd_error("Unsupported boxed module %s.\n", twines.str(it.first).c_str());
log_cmd_error("Unsupported boxed module %s.\n", log_id(it.second));
break;
case RTLIL::SB_EXCL_BB_CMDERR:
log_cmd_error("Unsupported blackbox module %s.\n", twines.str(it.first).c_str());
log_cmd_error("Unsupported blackbox module %s.\n", log_id(it.second));
break;
default:
break;
@ -1845,13 +1843,13 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_modules(RTLIL::SelectPartial
switch(partials)
{
case RTLIL::SELECT_WHOLE_WARN:
log_warning("Ignoring partially selected module %s.\n", twines.str(it.first).c_str());
log_warning("Ignoring partially selected module %s.\n", log_id(it.second));
break;
case RTLIL::SELECT_WHOLE_ERR:
log_error("Unsupported partially selected module %s.\n", twines.str(it.first).c_str());
log_error("Unsupported partially selected module %s.\n", log_id(it.second));
break;
case RTLIL::SELECT_WHOLE_CMDERR:
log_cmd_error("Unsupported partially selected module %s.\n", twines.str(it.first).c_str());
log_cmd_error("Unsupported partially selected module %s.\n", log_id(it.second));
break;
default:
break;
@ -1893,7 +1891,6 @@ RTLIL::Module::~Module()
delete pr.second;
for (auto binding : bindings_)
delete binding;
// Module's own src — release last so the pool stays valid for
// inner releases above.
if (design)
design->obj_release_src(this);
@ -3172,7 +3169,6 @@ void RTLIL::Module::cloneInto(RTLIL::Module *new_mod, bool src_id_verbatim) cons
// Transfer src across designs. Both modules must be attached
// to a design for the migration to happen; in the
// detached-clone() scratch flow (equiv_make, etc.) src is
// dropped here — those callers don't preserve src across the
// temp clone by design.
if (this->design && new_mod->design)
copy_src_into(this, this->design, new_mod, new_mod->design);
@ -3189,7 +3185,6 @@ void RTLIL::Module::cloneInto(RTLIL::Module *new_mod, bool src_id_verbatim) cons
return;
// Preserve name already set by addWire/addCell (in dst's pool).
TwineRef saved_name = dst_obj->meta_ ? dst_obj->meta_->name : Twine::Null;
// Recycle old meta slot (no field releases — name's retain lives on).
if (dst_obj->meta_) {
dst_obj->meta_->name = Twine::Null;
dst_obj->meta_->src = Twine::Null;
@ -3274,7 +3269,6 @@ void RTLIL::Module::cloneInto(RTLIL::Module *new_mod, bool src_id_verbatim) cons
// TwinePool allocates slots sequentially as copy_src_into →
// copy_from interns each wire's src, so the destination pool
// ends up with leaves in the same order the frontend
// originally interned them — that lets write_rtlil emit
// byte-equal "@N" refs across single-module clones into an
// existing destination design.
// Re-intern each wire/cell name from the source design's pool into
@ -3472,7 +3466,6 @@ void RTLIL::Module::add(RTLIL::Process *process)
processes[process->meta_->name] = process;
process->module = this;
// Propagate module back-pointer to every CaseRule/SwitchRule in the
// root case tree and every MemWriteAction in the sync rules — so the
// per-Design src meta vector can be resolved from any inner-process
// AttrObject via `module->design` after attach.
process->root_case.setModuleRecursive(this);
@ -3853,7 +3846,6 @@ RTLIL::Memory *RTLIL::Module::addMemory(TwineRef name, const RTLIL::Memory *othe
mem->size = other->size;
mem->attributes = other->attributes;
{
// Clone path drops src for now — caller responsible for migrating
// src across the design boundary if needed. addMemory(name) is the
// common case.
(void)other;
@ -6783,7 +6775,6 @@ RTLIL::CaseRule *RTLIL::CaseRule::clone() const
new_caserule->compare = compare;
new_caserule->actions = actions;
new_caserule->attributes = attributes;
// clone() drops src — CaseRule has no pool backpointer, so we can't
// retain. The caller (Module::addProcess(name, other)) is responsible
// for walking the cloned tree and migrating src via context.
for (auto &it : switches)
@ -6807,7 +6798,6 @@ RTLIL::SwitchRule *RTLIL::SwitchRule::clone() const
RTLIL::SwitchRule *new_switchrule = new RTLIL::SwitchRule;
new_switchrule->signal = signal;
new_switchrule->attributes = attributes;
// clone() drops src — see CaseRule::clone for rationale.
for (auto &it : cases)
new_switchrule->cases.push_back(it->clone());
return new_switchrule;
@ -6821,7 +6811,6 @@ RTLIL::SyncRule *RTLIL::SyncRule::clone() const
new_syncrule->signal = signal;
new_syncrule->actions = actions;
new_syncrule->mem_write_actions = mem_write_actions;
// Drop meta_idx_ on the cloned MemWriteActions — the integer was
// copied by the vector assignment above without registering with
// any pool; the caller is responsible for migrating src across the
// clone via context (see Process::clone).
@ -6974,7 +6963,6 @@ void RTLIL::Memory::absorb_attrs(dict<IdString, RTLIL::Const> &&buf)
module->design->absorb_attrs(this, std::move(buf));
}
// CaseRule / SwitchRule / MemWriteAction src helpers — all delegate to
// module->design->obj_* via the back-pointer added in the earlier commit.
TwineRef RTLIL::CaseRule::src_id() const
{

View File

@ -555,12 +555,16 @@ struct RTLIL::IdString
// often one needs to check if a given IdString is part of a list (for example a list
// of cell types). the following functions helps with that.
// Constrained to 2+ args so a single argument always resolves to a concrete
// overload below; otherwise a single argument matching none of them (e.g. a
// TwineRef) would re-match this template and recurse infinitely.
template<typename... Args>
bool in(const Args &... args) const {
bool in(const Args &... args) const requires (sizeof...(Args) != 1) {
return (... || in(args));
}
bool in(IdString rhs) const { return *this == rhs; }
inline bool in(TwineRef rhs) const;
bool in(const char *rhs) const { return *this == rhs; }
bool in(const std::string &rhs) const { return *this == rhs; }
inline bool in(const pool<IdString> &rhs) const;
@ -600,6 +604,8 @@ inline bool operator==(TwineRef a, RTLIL::IdString b) {
}
inline bool operator==(RTLIL::IdString a, TwineRef b) { return b == a; }
inline bool RTLIL::IdString::in(TwineRef rhs) const { return *this == rhs; }
struct RTLIL::OwningIdString : public RTLIL::IdString {
inline OwningIdString() { }
inline OwningIdString(const OwningIdString &str) : IdString(str) { get_reference(); }
@ -1402,7 +1408,6 @@ inline bool operator!=(RTLIL::IdString lhs, const RTLIL::NameMasqBase<Derived> &
// Read-only masquerade for Wire::name. Reads materialise the TwineRef in
// the owning Design's twines pool into a temporary IdString. Writes are
// intentionally unsupported — use Module::rename(wire, new_name) instead.
// Defined before Wire so it can be used as a [[no_unique_address]] member.
struct RTLIL::WireNameMasq : RTLIL::NameMasqBase<RTLIL::WireNameMasq> {
WireNameMasq() = default;
@ -2194,7 +2199,6 @@ struct RTLIL::Design
// Wholesale-copy this design into `dst`. `dst` must be empty (no
// modules). Copies twines verbatim and clones each module
// preserving src_id_ values directly — avoids the per-module
// copy_from pool rebuild and yields byte-identical RTLIL output
// across design -push/-pop, -save/-load, etc.
void clone_into(RTLIL::Design *dst) const;
@ -2325,7 +2329,6 @@ private:
friend struct RTLIL::Patch;
public:
// Shadows NamedObject::name. Reads materialise via twines; writes
// are a compile error — use Module::rename(wire, new_name) instead.
[[no_unique_address]] RTLIL::WireNameMasq name;
Hasher::hash_t hashidx_;
@ -2395,7 +2398,6 @@ struct RTLIL::Memory : public RTLIL::AttrObject
Memory();
~Memory();
// Back-pointer to the owning module — same role as Cell::module /
// Wire::module. Set by Module::addMemory / the frontends that
// construct Memory free-standing before attaching to a module.
// Lets Memory's src access resolve uniformly via module->design.
@ -2436,7 +2438,6 @@ private:
bool bufnorm_handle_setPort(TwineRef portname, RTLIL::SigSpec &signal, dict<TwineRef, RTLIL::SigSpec>::iterator conn_it);
public:
// Shadows NamedObject::name. Reads materialise via twines; writes
// are a compile error — use Module::rename(cell, new_name) instead.
[[no_unique_address]] RTLIL::CellNameMasq name;
Hasher::hash_t hashidx_;
@ -2525,7 +2526,6 @@ struct RTLIL::CaseRule : public RTLIL::AttrObject
// Walk the whole CaseRule subtree (this case, every switch, every
// nested case, every MemWriteAction inside this process's sync rules
// — those are reached through Process, not here) and set `module` on
// each. Idempotent.
void setModuleRecursive(RTLIL::Module *m);
@ -2706,7 +2706,6 @@ inline Hasher RTLIL::SigBit::hash_into(Hasher h) const {
inline Hasher RTLIL::SigBit::hash_top() const {
Hasher h;
if (wire) {
// Use the wire's name (TwineRef) directly — avoids IdString materialisation.
TwineRef name = wire->meta_ ? wire->meta_->name : Twine::Null;
h.eat(name);
h.eat(offset);
@ -3075,7 +3074,6 @@ public:
virtual RTLIL::Module *clone() const;
// Clone variant that attaches the new module to `dst` BEFORE cloneInto
// runs. This is the right pattern when the destination design is known
// up front — it avoids the "detached module, attach later" flow and
// the pending-literal src stashing it entails. Subtypes override to
// preserve their type (AstModule). `src_id_verbatim` is forwarded to
// cloneInto.
@ -3104,7 +3102,6 @@ public:
return design->selected_member(meta_->name, member->meta_->name);
}
// Primary (fast) overloads — key directly into the dict.
RTLIL::Wire* wire(TwineRef id) {
auto it = wires_.find(id);
return it == wires_.end() ? nullptr : it->second;

View File

@ -1172,7 +1172,6 @@ bool RTLIL::Cell::bufnorm_handle_setPort(TwineRef portname, SigSpec &signal, dic
}
auto dir = port_dir(portname);
// Fast path: connecting a full driverless wire to an output port — everything else
// goes through the bufnorm queues and is handled during the next bufNormalize call
if ((dir == RTLIL::PD_OUTPUT || dir == RTLIL::PD_INOUT) && signal.is_wire()) {
Wire *w = signal.as_wire();

View File

@ -145,7 +145,6 @@ void twine_prepopulate() {
// log_assert(parent->is_flat() && "Suffix parent must be a flat node (Leaf or Suffix)");
// if (tail.empty()) {
// // No tail means "the same string as parent". Hand back a fresh
// // owning ref on parent — semantically equivalent to a degenerate
// // suffix node, but we avoid allocating a slot for it.
// retain(parent);
// return parent;

View File

@ -109,7 +109,6 @@ struct TW {
};
#define TW(id) ((size_t)std::integral_constant<int, lookup_well_known_id(#id)>::value)
// #define TW(name) TW::lookup(#name)
struct Twine {
static constexpr TwineRef Null = std::numeric_limits<size_t>::max();
@ -120,19 +119,31 @@ struct Twine {
auto operator<=>(const Suffix&) const = default;
};
struct AutoPrefix {
struct AutoSuffix {
const std::string *prefix;
std::string tail;
auto operator<=>(const AutoPrefix&) const = default;
auto operator<=>(const AutoSuffix&) const = default;
};
std::variant<std::monostate, std::string, std::vector<TwineRef>, Suffix, AutoPrefix> data;
std::variant<
// Unused slot
std::monostate,
// "leaf", regular deduplicated string
std::string,
// "concat", for src only, requires concatenating character convention - '|' for src attributes, others for others in the future
std::vector<TwineRef>,
// "suffix", deduplicates shared prefixes
Suffix,
// transient suffix constructed with NEW_TWINE and NEW_TWINE_SUFFIX
// turned into a regular Suffix when added to a TwinePool
AutoSuffix> data;
bool is_dead() const { return std::holds_alternative<std::monostate>(data); }
bool is_leaf() const { return std::holds_alternative<std::string>(data); }
bool is_concat() const { return std::holds_alternative<std::vector<TwineRef>>(data); }
bool is_suffix() const { return std::holds_alternative<Suffix>(data); }
bool is_auto_prefix() const { return std::holds_alternative<AutoPrefix>(data); }
bool is_auto_prefix() const { return std::holds_alternative<AutoSuffix>(data); }
bool is_flat() const { return is_leaf() || is_suffix(); }
const std::string &leaf() const { return std::get<std::string>(data); }
const std::vector<TwineRef> &children() const { return std::get<std::vector<TwineRef>>(data); }
@ -150,7 +161,6 @@ struct TwineHash {
size_t operator()(const Twine& t) const noexcept;
size_t operator()(TwineRef ref) const noexcept;
// size_t operator()(std::string_view v) const noexcept;
};
struct TwineEq {
@ -161,8 +171,6 @@ struct TwineEq {
bool operator()(TwineRef a, TwineRef b) const noexcept;
bool operator()(TwineRef a, const Twine& b) const noexcept;
bool operator()(const Twine& a, TwineRef b) const noexcept;
// bool operator()(TwineRef a, std::string_view b) const noexcept;
// bool operator()(std::string_view a, TwineRef b) const noexcept;
};
@ -365,12 +373,8 @@ struct TwinePool {
return ref;
}
// Interns a structural Twine verbatim and returns the handle. Leaf content
// is stored as-is — callers holding an escaped string must strip the '\'
// and tag publicity themselves (or use the add(std::string) overload).
// Suffix names inherit the prefix handle's publicity.
TwineRef add(Twine t) {
if (auto *ap = std::get_if<Twine::AutoPrefix>(&t.data)) {
if (auto *ap = std::get_if<Twine::AutoSuffix>(&t.data)) {
TwineRef pref = add_inner(Twine{*ap->prefix});
return add_inner(Twine{Twine::Suffix{pref, std::move(ap->tail)}});
}
@ -612,7 +616,7 @@ struct DeepTwineEq {
// Required by unordered_set to handle hash collisions between two TwineRefs.
bool operator()(TwineRef a, TwineRef b) const {
if (a == b) return true; // Index or structural equality shortcut
std::string fb = flatten(b);
std::string fb = pool->unescaped_str(b);
return (*this)(a, std::string_view(fb));
}
@ -660,7 +664,7 @@ struct TwineChildPool {
// Local analog of TwinePool::add; see there for the convention.
TwineRef add(Twine t) {
if (auto *ap = std::get_if<Twine::AutoPrefix>(&t.data)) {
if (auto *ap = std::get_if<Twine::AutoSuffix>(&t.data)) {
TwineRef pref = add_inner(Twine{*ap->prefix});
return add_inner(Twine{Twine::Suffix{pref, std::move(ap->tail)}});
}
@ -731,6 +735,11 @@ struct TwineSearch {
index.insert(STATIC_TWINE_END + idx);
}
}
// Keep a hoisted search current after adding a ref to the pool, so the
// search need not be rebuilt (O(pool)) between finds in a loop.
void insert(TwineRef ref) {
index.insert(twine_untag(ref));
}
// Escaped-name aware. Resolves both statics and locals by content.
TwineRef find(std::string_view sv) const {
bool is_public = !sv.empty() && sv[0] == '\\';

View File

@ -134,7 +134,6 @@ namespace {
void apply_src(Module* mod, Cell* root, const std::vector<Cell*>& extras,
const std::vector<Cell*>& targets, Cell* merge_src_into)
{
// Without a design there's no pool — the cells can't carry typed
// src, so silently drop merge-of-src in that path.
if (!mod || !mod->design)
return;
@ -205,7 +204,6 @@ void Patch::patch(Cell* root_cell, TwineRef old_port, SigSpec new_sig,
apply_src(mod, root_cell, extras, committed, merge_src_into);
// Drop root_cell's driver on the output port BEFORE wiring old_sig to
// new_sig — otherwise old_sig would briefly have two drivers (root_cell
// and new_sig) which signorm flags as conflicting.
root_cell->unsetPort(old_port);
@ -213,7 +211,6 @@ void Patch::patch(Cell* root_cell, TwineRef old_port, SigSpec new_sig,
map->add(old_sig, new_sig);
mod->connect_incremental(old_sig, new_sig);
// Remove root cell only — no input-cone walk.
mod->remove(root_cell);
}
@ -277,7 +274,6 @@ void Patch::commit_inheriting_src(Cell* src_source) {
Cell *committed = commit_cell(std::move(cell));
// commit_cell attaches the cell to mod, so adopt_src_from can
// now resolve the pool via committed->module->design. Direct
// id transfer — no flatten/re-intern detour.
if (src_source)
committed->adopt_src_from(src_source);
}

View File

@ -6,7 +6,6 @@
YOSYS_NAMESPACE_BEGIN
// No virtual methods — subclasses cannot be dispatched through a Patch pointer.
struct RTLIL::Patch : public CellAdderMixin<RTLIL::Patch>
{
protected:

View File

@ -127,7 +127,6 @@ public:
// ---------------------------------------------------
// BitGrouper — partition output bits by a per-bit key
// ---------------------------------------------------
//
// Many passes that split a multi-bit cell or word-level FF into smaller

View File

@ -305,12 +305,12 @@ RTLIL::IdString new_id_suffix(std::string_view file, int line, std::string_view
#define NEW_ID_SUFFIX(suffix) \
YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix)
#define NEW_TWINE \
YOSYS_NAMESPACE_PREFIX Twine{YOSYS_NAMESPACE_PREFIX Twine::AutoPrefix{[](std::string_view func) -> const std::string * { \
YOSYS_NAMESPACE_PREFIX Twine{YOSYS_NAMESPACE_PREFIX Twine::AutoSuffix{[](std::string_view func) -> const std::string * { \
static std::unique_ptr<const std::string> prefix(YOSYS_NAMESPACE_PREFIX create_id_prefix(__FILE__, __LINE__, func)); \
return prefix.get(); \
}(__FUNCTION__), std::to_string(YOSYS_NAMESPACE_PREFIX autoidx++)}}
#define NEW_TWINE_SUFFIX(suffix) \
YOSYS_NAMESPACE_PREFIX Twine{YOSYS_NAMESPACE_PREFIX Twine::AutoPrefix{[](std::string_view func) -> const std::string * { \
YOSYS_NAMESPACE_PREFIX Twine{YOSYS_NAMESPACE_PREFIX Twine::AutoSuffix{[](std::string_view func) -> const std::string * { \
static std::unique_ptr<const std::string> prefix(YOSYS_NAMESPACE_PREFIX create_id_prefix(__FILE__, __LINE__, func)); \
return prefix.get(); \
}(__FUNCTION__), std::string(suffix) + "$" + std::to_string(YOSYS_NAMESPACE_PREFIX autoidx++)}}

View File

@ -5,6 +5,7 @@ endif
OBJS += passes/cmds/add.o
OBJS += passes/cmds/delete.o
OBJS += passes/cmds/design.o
OBJS += passes/cmds/dump_twines.o
OBJS += passes/cmds/design_equal.o
OBJS += passes/cmds/select.o
OBJS += passes/cmds/show.o

View File

@ -55,7 +55,6 @@ struct PrintAttrsPass : public Pass {
}
static void log_src(const RTLIL::Design *design, const RTLIL::AttrObject *obj, const unsigned int indent) {
// Emit src outside the attributes loop — it lives on the typed
// meta-vector slot, not in obj->attributes.
if (design && design->obj_src_id(obj) != Twine::Null)
log("%s(* src=\"%s\" *)\n", get_indent_str(indent),

View File

@ -145,7 +145,6 @@ static bool match_attr(const dict<RTLIL::IdString, RTLIL::Const> &attributes, co
// them after the migration of src out of the attribute dict by synthesizing
// a one-element dict view containing the flattened ID::src and feeding it
// through the normal match_attr path. Other ID::src-shaped patterns like
// wildcards still flow through the (empty) dict and won't match src — those
// uses were rare and the dict-path migration is a separate concern.
static bool match_attr(const RTLIL::Design *design, const RTLIL::AttrObject *obj, const std::string &match_expr)
{

View File

@ -98,7 +98,7 @@ struct SplitcellsWorker
std::string s = cell->name.str() + (slice_msb == slice_lsb ?
stringf("%c%d%c", format[0], slice_lsb, format[1]) :
stringf("%c%d%c%d%c", format[0], slice_msb, format[2], slice_lsb, format[1]));
TwineRef slice_name = module->uniquify(module->design->twines.add(Twine{s}));
TwineRef slice_name = module->uniquify(module->design->twines.add(std::move(s)));
Cell *slice = module->addCell(slice_name, cell);
@ -127,7 +127,7 @@ struct SplitcellsWorker
if (slice->hasParam(ID::WIDTH))
slice->setParam(ID::WIDTH, GetSize(slice->getPort(TW::Y)));
log(" slice %d: %s => %s\n", i, slice_name, log_signal(slice->getPort(TW::Y)));
log(" slice %d: %s => %s\n", i, module->design->twines.str(slice_name).c_str(), log_signal(slice->getPort(TW::Y)));
}
module->remove(cell);
@ -162,11 +162,11 @@ struct SplitcellsWorker
int slice_msb = slices[i]-1;
int slice_lsb = slices[i-1];
TwinePool twines = module->design->twines;
TwinePool &twines = module->design->twines;
std::string s = cell->name.str() + (slice_msb == slice_lsb ?
stringf("%c%d%c", format[0], slice_lsb, format[1]) :
stringf("%c%d%c%d%c", format[0], slice_msb, format[2], slice_lsb, format[1]));
TwineRef slice_name = module->uniquify(twines.add(Twine{s}));
TwineRef slice_name = module->uniquify(twines.add(std::move(s)));
Cell *slice = module->addCell(slice_name, cell);

View File

@ -55,7 +55,7 @@ struct SplitnetsWorker
if (format.size() > 1)
new_wire_name += format.substr(1, 1);
RTLIL::Wire *new_wire = module->addWire(module->uniquify(module->design->twines.add(Twine{new_wire_name})), width);
RTLIL::Wire *new_wire = module->addWire(module->uniquify(module->design->twines.add(std::move(new_wire_name))), width);
new_wire->port_id = wire->port_id ? wire->port_id + offset : 0;
new_wire->port_input = wire->port_input;
new_wire->port_output = wire->port_output;

View File

@ -56,8 +56,9 @@ struct EquivAddPass : public Pass {
if (GetSize(args) == 4 && args[1] == "-cell")
{
Cell *gold_cell = module->cell(TwineSearch(&design->twines).find(RTLIL::escape_id(args[2])));
Cell *gate_cell = module->cell(TwineSearch(&design->twines).find(RTLIL::escape_id(args[3])));
TwineSearch search(&design->twines);
Cell *gold_cell = module->cell(search.find(RTLIL::escape_id(args[2])));
Cell *gate_cell = module->cell(search.find(RTLIL::escape_id(args[3])));
if (gold_cell == nullptr) {
if (try_mode) {

View File

@ -154,10 +154,11 @@ struct EquivMakeWorker
// list of cells without added $equiv cells
auto cells_list = equiv_mod->cells().to_vector();
TwineSearch search(&equiv_mod->design->twines);
for (auto id : wire_names)
{
TwineRef gold_id = TwineSearch(&equiv_mod->design->twines).find(id.str() + "_gold");
TwineRef gate_id = TwineSearch(&equiv_mod->design->twines).find(id.str() + "_gate");
TwineRef gold_id = search.find(id.str() + "_gold");
TwineRef gate_id = search.find(id.str() + "_gate");
Wire *gold_wire = equiv_mod->wire(gold_id);
Wire *gate_wire = equiv_mod->wire(gate_id);
@ -331,10 +332,11 @@ struct EquivMakeWorker
{
SigMap assign_map(equiv_mod);
TwineSearch search(&equiv_mod->design->twines);
for (auto id : cell_names)
{
TwineRef gold_id = TwineSearch(&equiv_mod->design->twines).find(id.str() + "_gold");
TwineRef gate_id = TwineSearch(&equiv_mod->design->twines).find(id.str() + "_gate");
TwineRef gold_id = search.find(id.str() + "_gold");
TwineRef gate_id = search.find(id.str() + "_gate");
Cell *gold_cell = equiv_mod->cell(gold_id);
Cell *gate_cell = equiv_mod->cell(gate_id);
@ -380,7 +382,7 @@ struct EquivMakeWorker
}
equiv_mod->remove(gate_cell);
equiv_mod->rename(gold_cell, TwineSearch(&equiv_mod->design->twines).find(id.str()));
equiv_mod->rename(gold_cell, search.find(id.str()));
}
}
@ -492,9 +494,10 @@ struct EquivMakePass : public Pass {
if (argidx+3 != args.size())
log_cmd_error("Invalid number of arguments.\n");
worker.gold_mod = design->module(TwineSearch(&design->twines).find(RTLIL::escape_id(args[argidx])));
worker.gate_mod = design->module(TwineSearch(&design->twines).find(RTLIL::escape_id(args[argidx+1])));
worker.equiv_mod = design->module(TwineSearch(&design->twines).find(RTLIL::escape_id(args[argidx+2])));
TwineSearch search(&design->twines);
worker.gold_mod = design->module(search.find(RTLIL::escape_id(args[argidx])));
worker.gate_mod = design->module(search.find(RTLIL::escape_id(args[argidx+1])));
worker.equiv_mod = design->module(search.find(RTLIL::escape_id(args[argidx+2])));
if (worker.gold_mod == nullptr)
log_cmd_error("Can't find gold module %s.\n", args[argidx]);

View File

@ -156,17 +156,20 @@ struct EquivMiterWorker
struct RewriteSigSpecWorker {
RTLIL::Module * mod;
TwineSearch *search;
void operator()(SigSpec &sig) {
vector<SigChunk> chunks = sig.chunks();
for (auto &c : chunks)
if (c.wire != NULL)
c.wire = mod->wire(TwineSearch(&mod->design->twines).find(c.wire->name.str()));
c.wire = mod->wire(search->find(c.wire->name.str()));
sig = chunks;
}
};
RewriteSigSpecWorker rewriteSigSpecWorker;
TwineSearch rewrite_search(&miter_module->design->twines);
rewriteSigSpecWorker.mod = miter_module;
rewriteSigSpecWorker.search = &rewrite_search;
miter_module->rewrite_sigspecs(rewriteSigSpecWorker);
// find undriven or unused wires

View File

@ -48,46 +48,61 @@ struct module_ptr_compare {
}
};
IdString concat_name(RTLIL::Cell *cell, IdString const &object_name, const std::string &separator = ".")
// Split an object's escaped name into the shared per-instance prefix and the
// remaining tail; prefix + tail is the flattened escaped name. flatten_cell
// interns the prefix once and shares it via a Suffix node (like techmap's
// apply_prefix_ref) instead of storing a full leaf string per object.
std::pair<std::string, std::string> hier_name_parts(RTLIL::Cell *cell, std::string_view object_name_view, const std::string &separator)
{
std::string_view object_name_view(object_name.c_str());
if (object_name_view[0] == '\\'){
return concat_views(cell->name.str(), separator, object_name_view.substr(1));
}
if (!object_name_view.empty() && object_name_view[0] == '\\')
return {cell->name.str() + separator, std::string(object_name_view.substr(1))};
constexpr std::string_view prefix = "$flatten";
if (object_name_view.substr(0, prefix.size()) == prefix){
if (object_name_view.substr(0, prefix.size()) == prefix)
object_name_view.remove_prefix(prefix.size());
}
return concat_views(prefix, cell->name.str(), separator, object_name_view);
return {"$flatten" + cell->name.str() + separator, std::string(object_name_view)};
}
template<class T>
TwineRef map_name(RTLIL::Cell *cell, T *object, const std::string &separator = ".")
std::string concat_name(RTLIL::Cell *cell, std::string_view object_name_view, const std::string &separator = ".")
{
return cell->module->uniquify(cell->module->design->twines.add(Twine{concat_name(cell, object->name, separator).str()}));
auto [prefix, tail] = hier_name_parts(cell, object_name_view, separator);
return prefix + tail;
}
// Specialization for Memory
template<>
TwineRef map_name<RTLIL::Memory>(RTLIL::Cell *cell, RTLIL::Memory *object, const std::string &separator)
// Build the flattened name of a template object, given the instance's already
// interned public/private prefix leaves. A name like "\a.b.c.w" is stored as
// nested Suffix nodes Suffix{Suffix{Suffix{"\a.", "b."}, "c."}, "w"}. When the
// template was itself flattened earlier its names are already such Suffixes, so
// flattening instance "u" re-prefixes only the innermost leaf ("\a." -> "\u.a.")
// and reuses the rest, yielding "\u.a.b.c.w" while keeping "b.", "c.", "w"
// shared -- rather than materialising "a.b.c.w" as one fresh tail per object.
TwineRef remap_flattened_name(RTLIL::Design *design, TwineRef obj_ref,
TwineRef pub_prefix_ref, TwineRef priv_prefix_ref, dict<TwineRef, TwineRef> &memo)
{
auto design = cell->module->design;
std::string obj_name(design->twines.str(object->meta_->name));
IdString obj_name_id(obj_name);
std::string mapped_name = concat_name(cell, obj_name_id, separator).str();
return cell->module->uniquify(design->twines.add(Twine{mapped_name}));
}
if (auto it = memo.find(obj_ref); it != memo.end())
return it->second;
// Specialization for Process
template<>
TwineRef map_name<RTLIL::Process>(RTLIL::Cell *cell, RTLIL::Process *object, const std::string &separator)
{
auto design = cell->module->design;
std::string obj_name(design->twines.str(object->meta_->name));
IdString obj_name_id(obj_name);
std::string mapped_name = concat_name(cell, obj_name_id, separator).str();
return cell->module->uniquify(design->twines.add(Twine{mapped_name}));
const Twine &node = design->twines[obj_ref];
TwineRef result;
if (node.is_suffix()) {
const Twine::Suffix &sfx = node.suffix();
TwineRef prefix = remap_flattened_name(design, twine_tag(sfx.prefix, obj_ref.is_public()),
pub_prefix_ref, priv_prefix_ref, memo);
result = design->twines.add(Twine{Twine::Suffix{prefix, sfx.tail}});
} else {
std::string escaped = design->twines.str(obj_ref);
std::string_view obj = escaped;
if (!obj.empty() && obj[0] == '\\') {
result = design->twines.add(Twine{Twine::Suffix{pub_prefix_ref, std::string(obj.substr(1))}});
} else {
constexpr std::string_view flatten_prefix = "$flatten";
if (obj.substr(0, flatten_prefix.size()) == flatten_prefix)
obj.remove_prefix(flatten_prefix.size());
result = design->twines.add(Twine{Twine::Suffix{priv_prefix_ref, std::string(obj)}});
}
}
memo[obj_ref] = result;
return result;
}
void map_sigspec(const dict<RTLIL::Wire*, RTLIL::Wire*> &map, RTLIL::SigSpec &sig, RTLIL::Module *into = nullptr)
@ -152,15 +167,22 @@ struct FlattenWorker
}
}
void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector<RTLIL::Cell*> &new_cells, const std::string &separator)
void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector<RTLIL::Cell*> &new_cells, const std::string &separator, const dict<std::string, RTLIL::Wire*> &hier_wires)
{
// Copy the contents of the flattened cell
dict<TwineRef, TwineRef> memory_map;
TwineRef pub_prefix_ref = design->twines.add(cell->name.str() + separator);
TwineRef priv_prefix_ref = design->twines.add("$flatten" + cell->name.str() + separator);
dict<TwineRef, TwineRef> remap_memo;
auto make_name = [&](TwineRef obj_ref) -> TwineRef {
return module->uniquify(remap_flattened_name(design, obj_ref, pub_prefix_ref, priv_prefix_ref, remap_memo));
};
dict<std::string, TwineRef> memory_map;
for (auto &tpl_memory_it : tpl->memories) {
RTLIL::Memory *new_memory = module->addMemory(map_name(cell, tpl_memory_it.second, separator), tpl_memory_it.second);
RTLIL::Memory *new_memory = module->addMemory(make_name(tpl_memory_it.second->meta_->name), tpl_memory_it.second);
map_attributes(cell, new_memory, design->twines.str(tpl_memory_it.second->meta_->name));
memory_map[tpl_memory_it.first] = new_memory->meta_->name;
memory_map[design->twines.str(tpl_memory_it.first)] = new_memory->meta_->name;
design->select(module, new_memory);
}
@ -172,8 +194,9 @@ struct FlattenWorker
RTLIL::Wire *new_wire = nullptr;
if (tpl_wire->name[0] == '\\') {
std::string wire_name = concat_name(cell, tpl_wire->name, separator).str();
RTLIL::Wire *hier_wire = module->wire(TwineSearch(&design->twines).find(wire_name));
std::string wire_name = concat_name(cell, tpl_wire->name.str(), separator);
auto hwit = hier_wires.find(wire_name);
RTLIL::Wire *hier_wire = (hwit != hier_wires.end()) ? hwit->second : nullptr;
if (hier_wire != nullptr && hier_wire->get_bool_attribute(ID::hierconn)) {
hier_wire->attributes.erase(ID::hierconn);
if (GetSize(hier_wire) < GetSize(tpl_wire)) {
@ -185,7 +208,7 @@ struct FlattenWorker
}
}
if (new_wire == nullptr) {
new_wire = module->addWire(map_name(cell, tpl_wire, separator), tpl_wire);
new_wire = module->addWire(make_name(tpl_wire->name.ref()), tpl_wire);
new_wire->port_input = new_wire->port_output = false;
new_wire->port_id = false;
}
@ -196,12 +219,11 @@ struct FlattenWorker
}
for (auto &tpl_proc_it : tpl->processes) {
RTLIL::Process *new_proc = module->addProcess(map_name(cell, tpl_proc_it.second, separator), tpl_proc_it.second);
RTLIL::Process *new_proc = module->addProcess(make_name(tpl_proc_it.second->meta_->name), tpl_proc_it.second);
map_attributes(cell, new_proc, design->twines.str(tpl_proc_it.second->meta_->name));
for (auto new_proc_sync : new_proc->syncs)
for (auto &memwr_action : new_proc_sync->mem_write_actions) {
TwineRef old_memid_ref = TwineSearch(&design->twines).find(memwr_action.memid.str());
memwr_action.memid = design->twines.str(memory_map.at(old_memid_ref));
memwr_action.memid = design->twines.str(memory_map.at(memwr_action.memid.str()));
}
auto rewriter = [&](RTLIL::SigSpec &sig) { map_sigspec(wire_map, sig); };
new_proc->rewrite_sigspecs(rewriter);
@ -211,15 +233,14 @@ struct FlattenWorker
for (auto tpl_cell : tpl->cells()) {
if (tpl_cell->type.in(TW($input_port), TW($output_port), TW($public)))
continue;
RTLIL::Cell *new_cell = module->addCell(map_name(cell, tpl_cell, separator), tpl_cell);
RTLIL::Cell *new_cell = module->addCell(make_name(tpl_cell->name.ref()), tpl_cell);
map_attributes(cell, new_cell, tpl_cell->name);
if (new_cell->has_memid()) {
IdString memid = new_cell->getParam(ID::MEMID).decode_string();
TwineRef memid_ref = TwineSearch(&design->twines).find(memid.str());
new_cell->setParam(ID::MEMID, Const(design->twines.str(memory_map.at(memid_ref))));
new_cell->setParam(ID::MEMID, Const(design->twines.str(memory_map.at(memid.str()))));
} else if (new_cell->is_mem_cell()) {
IdString memid = new_cell->getParam(ID::MEMID).decode_string();
new_cell->setParam(ID::MEMID, Const(concat_name(cell, memid, separator).str()));
new_cell->setParam(ID::MEMID, Const(concat_name(cell, memid.str(), separator)));
}
auto rewriter = [&](RTLIL::SigSpec &sig) { map_sigspec(wire_map, sig); };
new_cell->rewrite_sigspecs(rewriter);
@ -319,7 +340,6 @@ struct FlattenWorker
scopeinfo->attributes.emplace(stringf("\\cell_%s", RTLIL::unescape_id(attr.first).c_str()), attr.second);
}
// src lives outside cell->attributes after the typed-src
// migration — fold it into the renamed-attribute view by
// hand so `a:cell_src` selectors keep working.
if (cell->src_id() != Twine::Null)
scopeinfo->attributes.emplace(ID(cell_src), RTLIL::Const(cell->get_src_attribute()));
@ -335,7 +355,7 @@ struct FlattenWorker
module->remove(cell);
if (scopeinfo != nullptr)
module->rename(scopeinfo, design->twines.add(Twine{cell_name.str()}));
module->rename(scopeinfo, design->twines.add(cell_name.str()));
}
void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool<RTLIL::Module*> &used_modules, const std::string &separator)
@ -344,6 +364,15 @@ struct FlattenWorker
return;
SigMap sigmap(module);
// hierconn wires are connection points pre-created in `module`; flatten
// reuses them by hierarchical name. Index them once instead of doing a
// pool-wide content search per template wire.
dict<std::string, RTLIL::Wire*> hier_wires;
for (auto wire : module->wires())
if (wire->get_bool_attribute(ID::hierconn))
hier_wires[wire->name.str()] = wire;
std::vector<RTLIL::Cell*> worklist = module->selected_cells();
while (!worklist.empty())
{
@ -368,7 +397,7 @@ struct FlattenWorker
// If a design is fully selected and has a top module defined, topological sorting ensures that all cells
// added during flattening are black boxes, and flattening is finished in one pass. However, when flattening
// individual modules, this isn't the case, and the newly added cells might have to be flattened further.
flatten_cell(design, module, cell, tpl, sigmap, worklist, separator);
flatten_cell(design, module, cell, tpl, sigmap, worklist, separator, hier_wires);
}
}
};

View File

@ -113,7 +113,6 @@ struct MemoryMapWorker
std::set<int> static_ports;
std::map<int, RTLIL::SigSpec> static_cells_map;
// "@N" ref, not a flattened literal — avoids re-interning a
// possibly-Concat src as a single pipe-joined leaf on every
// new cell. set_src_attribute's parse_ref path retains the
// pool slot directly.

View File

@ -406,7 +406,6 @@ struct OptMergePass : public Pass {
ct.cell_types.erase(TW($anyconst));
ct.cell_types.erase(TW($allseq));
ct.cell_types.erase(TW($allconst));
// Synthetic driver cells signorm creates for module ports — must
// never be folded into one another, otherwise distinct ports collapse.
ct.cell_types.erase(TW($input_port));
ct.cell_types.erase(TW($output_port));

View File

@ -461,7 +461,6 @@ struct WreduceWorker
static int count_nontrivial_wire_attrs(RTLIL::Wire *w)
{
// w->attributes no longer holds ID::src — typed src field, not counted.
int count = w->attributes.size();
count -= w->attributes.count(ID::unused_bits);
return count;

View File

@ -316,7 +316,7 @@ struct SimInstance
Module *mod = module->design->module(cell->type_impl);
if (mod != nullptr) {
dirty_children.insert(new SimInstance(shared, scope + "." + cell->module->design->twines.str(cell->meta_->name), mod, cell, this));
dirty_children.insert(new SimInstance(shared, scope + "." + cell->module->design->twines.unescaped_str(cell->meta_->name), mod, cell, this));
}
for (auto &port : cell->connections()) {

View File

@ -294,14 +294,17 @@ RTLIL::Cell *replace(RTLIL::Module *needle, RTLIL::Module *haystack, SubCircuit:
auto &tw = needle->design->twines;
// create new cell
RTLIL::Cell *cell = haystack->addCell(Twine{stringf("$extract$%s$%d", needle->name, autoidx++)}, Twine{needle->name.str()});
RTLIL::Cell *cell = haystack->addCell(haystack->design->twines.add(stringf("$extract$%s$%d", needle->name, autoidx++)), haystack->design->twines.add(needle->name.str()));
// create cell ports
// create cell ports. Port names come from the needle (map) pool; translate
// them into the haystack pool so the new cell's ports are keyed by the same
// refs as the referenced module in the haystack design.
for (auto wire : needle->wires()) {
if (wire->port_id > 0) {
TwineRef portname = haystack->design->twines.add(tw.str(wire->meta_->name));
for (int i = 0; i < wire->width; i++)
sig2port.insert(sigmap(RTLIL::SigSpec(wire, i)), std::pair<TwineRef, int>(wire->meta_->name, i));
cell->setPort(wire->meta_->name, RTLIL::SigSpec(RTLIL::State::Sz, wire->width));
sig2port.insert(sigmap(RTLIL::SigSpec(wire, i)), std::pair<TwineRef, int>(portname, i));
cell->setPort(portname, RTLIL::SigSpec(RTLIL::State::Sz, wire->width));
}
}
@ -628,7 +631,7 @@ struct ExtractPass : public Pass {
if (!mine_mode)
for (auto module : map->modules()) {
SubCircuit::Graph mod_graph;
std::string graph_name = "needle_" + design->twines.unescaped_str(module->name);
std::string graph_name = "needle_" + map->twines.unescaped_str(module->name);
log("Creating needle graph %s.\n", graph_name);
if (module2graph(mod_graph, module, constports)) {
solver.addGraph(graph_name, mod_graph);
@ -656,8 +659,8 @@ struct ExtractPass : public Pass {
for (auto needle : needle_list)
for (auto &haystack_it : haystack_map) {
log("Solving for %s in %s.\n", ("needle_" + design->twines.unescaped_str(needle->name)), haystack_it.first);
solver.solve(results, "needle_" + design->twines.unescaped_str(needle->name), haystack_it.first, false);
log("Solving for %s in %s.\n", ("needle_" + map->twines.unescaped_str(needle->name)), haystack_it.first);
solver.solve(results, "needle_" + map->twines.unescaped_str(needle->name), haystack_it.first, false);
}
log("Found %d matches.\n", GetSize(results));

View File

@ -170,7 +170,7 @@ struct MuxcoverWorker
return true;
}
char port_name[3] = {'\\', *path, 0};
return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(tree.muxes.at(bit)->module->design->twines.add(Twine{std::string(port_name)}))), path+1, false);
return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(tree.muxes.at(bit)->module->design->twines.add(std::string(port_name)))), path+1, false);
} else {
ret_bit = bit;
return true;

View File

@ -535,7 +535,6 @@ struct TechmapWorker
extmapper_module = extmapper_design->addModule(extmapper_design->twines.add(std::string{m_name}));
RTLIL::Cell *extmapper_cell = extmapper_module->addCell(cell->type.ref(), cell);
// addCell(name, cell) already migrated src across
// designs via copy_src_into — no need for an
// explicit set_src_attribute round-trip here.
int port_counter = 1;

View File

@ -62,7 +62,6 @@ void create_ice40_wrapcarry(ice40_wrapcarry_pm &pm)
cell->attributes[stringf("\\SB_CARRY.%s", a.first)] = a.second;
for (const auto &a : st.lut->attributes)
cell->attributes[stringf("\\SB_LUT4.%s", a.first)] = a.second;
// src now lives in src_id_, not the attributes dict — propagate it
// via prefixed flat-literal attributes so the unwrap pass can restore.
if (st.carry->src_id() != Twine::Null)
cell->attributes[IdString("\\SB_CARRY.\\src")] = Const(st.carry->get_src_attribute());

View File

@ -7,6 +7,6 @@ import gen_tests_makefile
gen_tests_makefile.generate_autotest("*.v", "",
"""if grep -Eq 'expect-(wr-ports|rd-ports|rd-clk)' $@; then \\
$(YOSYS) -f verilog -qp "proc; opt; memory -nomap; dump -outfile $(@:.v=).dmp t:\\$$mem_v2" $@; \\
$(YOSYS) -f verilog -qp "proc; opt; memory -nomap; dump -readable -outfile $(@:.v=).dmp t:\\$$mem_v2" $@; \\
python3 validate.py $@ $(@:.v=).dmp; \\
fi""")

View File

@ -1,15 +1,15 @@
autoidx 1
twines
leaf 0 "/home/emil/repo/"
suffix 1 0 "foo/"
suffix 2 1 "bar.v"
suffix 3 2 ":10.1-10.5"
suffix 4 2 ":11.1-11.5"
leaf 1009 "/home/emil/repo/"
suffix 1010 1009 "foo/"
suffix 1011 1010 "bar.v"
suffix 1012 1011 ":10.1-10.5"
suffix 1013 1011 ":11.1-11.5"
end
module \chain
attribute \src "@3"
attribute \src "@1012" # /home/emil/repo/foo/bar.v:10.1-10.5
wire input 1 \a
attribute \src "@4"
attribute \src "@1013" # /home/emil/repo/foo/bar.v:11.1-11.5
wire output 2 \b
connect \b \a
end

View File

@ -1,15 +1,16 @@
autoidx 1
twines
leaf 0 "everything.v"
suffix 1 0 ":1.1-1.10"
suffix 2 0 ":2.5-2.8"
concat 3 1 2
leaf 1009 "everything.v"
suffix 1010 1009 ":1.1-1.10"
suffix 1011 1009 ":2.5-2.8"
concat 1012 1010 1011
leaf 1013 "tiny"
end
attribute \src "@3"
module \tiny
attribute \src "@1"
attribute \src "@1012" # everything.v:1.1-1.10|everything.v:2.5-2.8
module $pub@1013 # \tiny
attribute \src "@1010" # everything.v:1.1-1.10
wire input 1 \a
attribute \src "@2"
attribute \src "@1011" # everything.v:2.5-2.8
wire output 2 \b
connect \b \a
end