Skip to content

Commit 2aa02b9

Browse files
committed
Preserve \src attributes through ABC9 via XAIGER "y" extension
Use ABC's &verify -y mechanism to compute output-to-input object equivalence mappings during optimization. The XAIGER writer emits a "y" identity extension and src map entries in the map file. After ABC optimizes and rewrites the AIG, the XAIGER readers use the "y" mapping to trace output objects back to input objects, recovering the original \src attributes for mapped LUT cells. Changes: - xaiger writer: emit "y" identity extension + "src" map file entries - abc9_exe: use &verify -y (combinational) + second &write - aiger/aiger2 readers: parse "y" extension + src map, apply \src - aigmap: propagate \src to AND/NOT/NAND gates during AIG mapping Enabled via: scratchpad -set abc9.verify true Co-developed-by: Claude Code v2.1.44 (claude-opus-4-6)
1 parent 5f8489d commit 2aa02b9

7 files changed

Lines changed: 186 additions & 6 deletions

File tree

backends/aiger/xaiger.cc

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ struct XAigerWriter
6363

6464
vector<Cell*> box_list;
6565

66+
// Track which cell produced each AND-map or CI entry (for \src propagation)
67+
dict<SigBit, Cell*> and_cell;
68+
dict<SigBit, Cell*> ci_cell;
69+
// Track \src strings for AIG objects (by object ID)
70+
dict<int, std::string> aig_obj_src;
71+
6672
int mkgate(int a0, int a1)
6773
{
6874
aig_m++, aig_a++;
@@ -108,6 +114,13 @@ struct XAigerWriter
108114
int a0 = bit2aig(args.first);
109115
int a1 = bit2aig(args.second);
110116
a = mkgate(a0, a1);
117+
// Record \src for this AIG object if the source cell has one
118+
auto cell_it = and_cell.find(bit);
119+
if (cell_it != and_cell.end()) {
120+
auto src = cell_it->second->get_string_attribute(ID::src);
121+
if (!src.empty())
122+
aig_obj_src[a >> 1] = src;
123+
}
111124
} else
112125
if (alias_map.count(bit)) {
113126
a = bit2aig(alias_map.at(bit));
@@ -207,6 +220,7 @@ struct XAigerWriter
207220
unused_bits.erase(B);
208221
undriven_bits.erase(Y);
209222
and_map[Y] = make_pair(A, B);
223+
and_cell[Y] = cell;
210224
continue;
211225
}
212226

@@ -373,6 +387,7 @@ struct XAigerWriter
373387
if (O != b)
374388
alias_map[O] = b;
375389
ci_bits.emplace_back(b);
390+
ci_cell[b] = cell;
376391
undriven_bits.erase(O);
377392
}
378393
}
@@ -425,6 +440,13 @@ struct XAigerWriter
425440
if (aig_map.count(bit))
426441
log_error("Visited AIG node more than once; this could be a combinatorial loop that has not been broken\n");
427442
aig_map[bit] = 2*aig_m;
443+
// Record \src for box CI objects
444+
auto ci_it = ci_cell.find(bit);
445+
if (ci_it != ci_cell.end()) {
446+
auto src = ci_it->second->get_string_attribute(ID::src);
447+
if (!src.empty())
448+
aig_obj_src[aig_m] = src;
449+
}
428450
}
429451

430452
for (auto bit : co_bits) {
@@ -671,6 +693,20 @@ struct XAigerWriter
671693
//f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
672694
//f.write(buffer_str.data(), buffer_str.size());
673695

696+
// Write "y" extension: identity mapping for \src preservation
697+
// ABC's &verify -y will use this to map output objects back to input objects
698+
{
699+
int n_objs = aig_m + 1; // objects 0 through aig_m
700+
f << "y";
701+
write_buffer(f, 4 * n_objs); // length in bytes (BE32)
702+
for (int i = 0; i < n_objs; i++) {
703+
// Identity mapping: each object maps to itself (literal = 2*i)
704+
// Written as native-endian 32-bit int (matches ABC's fwrite of Vec_IntArray)
705+
uint32_t lit = 2 * i;
706+
f.write(reinterpret_cast<const char*>(&lit), sizeof(lit));
707+
}
708+
}
709+
674710
f << stringf("Generated by %s\n", yosys_maybe_version());
675711

676712
design->scratchpad_set_int("write_xaiger.num_ands", and_map.size());
@@ -715,6 +751,10 @@ struct XAigerWriter
715751
for (auto &it : output_lines)
716752
f << it.second;
717753
log_assert(output_lines.size() == output_bits.size());
754+
755+
// Write src lines mapping AIG object IDs to \src attribute values
756+
for (auto &it : aig_obj_src)
757+
f << stringf("src %d %s\n", it.first, it.second.c_str());
718758
}
719759
};
720760

frontends/aiger/aigerparse.cc

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,9 @@ void AigerReader::parse_xaiger()
458458
RTLIL::Cell *output_cell = module->cell(stringf("$and$aiger%d$%d", aiger_autoidx, rootNodeID));
459459
log_assert(output_cell);
460460
module->remove(output_cell);
461-
module->addLut(stringf("$lut$aiger%d$%d", aiger_autoidx, rootNodeID), input_sig, output_sig, std::move(lut_mask));
461+
auto *lut = module->addLut(stringf("$lut$aiger%d$%d", aiger_autoidx, rootNodeID), input_sig, output_sig, std::move(lut_mask));
462+
// Track for \src application via "y" mapping
463+
lut_by_obj[rootNodeID] = lut;
462464
}
463465
}
464466
else if (c == 'r') {
@@ -510,6 +512,14 @@ void AigerReader::parse_xaiger()
510512
boxes.emplace_back(cell);
511513
}
512514
}
515+
else if (c == 'y') {
516+
uint32_t dataSize = parse_xaiger_literal(f);
517+
uint32_t n_entries = dataSize / 4;
518+
log_debug("y: dataSize=%u n_entries=%u\n", dataSize, n_entries);
519+
equiv_lit_ids.resize(n_entries);
520+
// Data is written as native-endian 32-bit ints by ABC
521+
f.read(reinterpret_cast<char*>(equiv_lit_ids.data()), dataSize);
522+
}
513523
else if (c == 'a' || c == 'i' || c == 'o' || c == 's') {
514524
uint32_t dataSize = parse_xaiger_literal(f);
515525
f.ignore(dataSize);
@@ -810,11 +820,29 @@ void AigerReader::post_process()
810820

811821
dict<RTLIL::IdString, std::pair<int,int>> wideports_cache;
812822

823+
// Map from input AIG object ID to \src attribute value
824+
dict<int, std::string> obj_src;
825+
813826
if (!map_filename.empty()) {
814827
std::ifstream mf(map_filename);
815828
std::string type, symbol;
816829
int variable, index;
817-
while (mf >> type >> variable >> index >> symbol) {
830+
while (mf >> type) {
831+
if (type == "src") {
832+
// Parse src lines: "src <obj_id> <src_value>"
833+
int obj_id;
834+
std::string src_value;
835+
if (!(mf >> obj_id))
836+
log_error("Bad map file: malformed src line\n");
837+
std::getline(mf, src_value);
838+
// Trim leading whitespace
839+
size_t start = src_value.find_first_not_of(" \t");
840+
if (start != std::string::npos)
841+
obj_src[obj_id] = src_value.substr(start);
842+
continue;
843+
}
844+
if (!(mf >> variable >> index >> symbol))
845+
break;
818846
RTLIL::IdString escaped_s = RTLIL::escape_id(symbol);
819847
if (type == "input") {
820848
log_assert(static_cast<unsigned>(variable) < inputs.size());
@@ -983,6 +1011,28 @@ void AigerReader::post_process()
9831011
else
9841012
module->rename(cell, stringf("$lut%s[%d]", y_port.wire->name, y_port.offset));
9851013
}
1014+
1015+
// Apply \src attributes using "y" extension equivalence mapping
1016+
if (!equiv_lit_ids.empty() && !obj_src.empty()) {
1017+
int applied = 0;
1018+
for (auto &[obj_id, lut] : lut_by_obj) {
1019+
if (obj_id < 0 || obj_id >= (int) equiv_lit_ids.size())
1020+
continue;
1021+
int32_t equiv_lit = equiv_lit_ids[obj_id];
1022+
if (equiv_lit < 0)
1023+
continue;
1024+
int input_obj = equiv_lit >> 1;
1025+
auto src_it = obj_src.find(input_obj);
1026+
if (src_it != obj_src.end()) {
1027+
lut->set_string_attribute(ID::src, src_it->second);
1028+
applied++;
1029+
log_debug("Applied \\src '%s' to cell %s (obj %d -> input obj %d)\n",
1030+
src_it->second.c_str(), log_id(lut), obj_id, input_obj);
1031+
}
1032+
}
1033+
if (applied > 0)
1034+
log("Applied \\src attributes to %d cells via equivalence mapping.\n", applied);
1035+
}
9861036
}
9871037

9881038
struct AigerFrontend : public Frontend {

frontends/aiger/aigerparse.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ struct AigerReader
4646
std::vector<RTLIL::Wire*> bad_properties;
4747
std::vector<RTLIL::Cell*> boxes;
4848
std::vector<int> mergeability, initial_state;
49+
std::vector<int32_t> equiv_lit_ids;
50+
dict<int, RTLIL::Cell*> lut_by_obj;
4951

5052
AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports);
5153
void parse_aiger();

frontends/aiger2/xaiger.cc

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ struct Xaiger2Frontend : public Frontend {
121121
bits[0] = RTLIL::S0;
122122
bits[1] = RTLIL::S1;
123123

124+
// Map from input AIG object ID to \src attribute value
125+
dict<int, std::string> obj_src;
126+
124127
std::string type;
125128
while (map_file >> type) {
126129
if (type == "pi") {
@@ -165,6 +168,16 @@ struct Xaiger2Frontend : public Frontend {
165168
retained_boxes.resize(box_seq + 1);
166169
}
167170
boxes[box_seq] = std::make_pair(box, def);
171+
} else if (type == "src") {
172+
int obj_id;
173+
std::string src_value;
174+
if (!(map_file >> obj_id))
175+
log_error("Bad map file (30)\n");
176+
std::getline(map_file, src_value);
177+
// Trim leading whitespace
178+
size_t start = src_value.find_first_not_of(" \t");
179+
if (start != std::string::npos)
180+
obj_src[obj_id] = src_value.substr(start);
168181
} else {
169182
std::string scratch;
170183
std::getline(map_file, scratch);
@@ -248,6 +261,9 @@ struct Xaiger2Frontend : public Frontend {
248261

249262
log_debug("reading 'M' (second pass)\n");
250263

264+
// Track output literal → Cell* for \src application
265+
dict<uint32_t, Cell*> lit_to_instance;
266+
251267
f->seekg(extensions_start);
252268
bool read_mapping = false;
253269
uint32_t no_cells, no_instances;
@@ -294,6 +310,7 @@ struct Xaiger2Frontend : public Frontend {
294310
auto out_w = module->addWire(module->uniquify(stringf("$lit%d", out_lit)));
295311
instance->setPort(cell.out, out_w);
296312
bits[out_lit] = out_w;
313+
lit_to_instance[out_lit] = instance;
297314
for (auto in : cell.ins) {
298315
uint32_t in_lit = read_be32(*f);
299316
log_assert(out_lit < bits.size());
@@ -318,6 +335,49 @@ struct Xaiger2Frontend : public Frontend {
318335
log("Read %d instances with cell library of size %d.\n",
319336
no_instances, no_cells);
320337

338+
// Read 'y' extension (equivalence literal IDs from &verify -y)
339+
// and apply \src attributes to mapped cells
340+
f->seekg(extensions_start);
341+
log_debug("reading 'y' (third pass)\n");
342+
for (int c = f->get(); c != EOF; c = f->get()) {
343+
if (c == 'y') {
344+
uint32_t len = read_be32(*f);
345+
uint32_t n_entries = len / 4;
346+
log_debug("y: len=%u n_entries=%u\n", len, n_entries);
347+
348+
std::vector<int32_t> equiv_lit_ids(n_entries);
349+
// Data is written as native-endian 32-bit ints by ABC
350+
f->read(reinterpret_cast<char*>(equiv_lit_ids.data()), len);
351+
352+
// Apply \src: for each mapped cell, look up its equivalent
353+
// input object via the "y" mapping, then look up the \src
354+
// from the input map file
355+
for (auto &[out_lit, instance] : lit_to_instance) {
356+
uint32_t out_obj = out_lit >> 1;
357+
if (out_obj >= n_entries)
358+
continue;
359+
int32_t equiv_lit = equiv_lit_ids[out_obj];
360+
if (equiv_lit < 0)
361+
continue; // no mapping for this object
362+
int input_obj = equiv_lit >> 1;
363+
auto src_it = obj_src.find(input_obj);
364+
if (src_it != obj_src.end()) {
365+
instance->set_string_attribute(ID::src, src_it->second);
366+
log_debug(" applied \\src '%s' to cell %s (out_obj=%d -> input_obj=%d)\n",
367+
src_it->second.c_str(), log_id(instance), out_obj, input_obj);
368+
}
369+
}
370+
break;
371+
} else if (c == '\n') {
372+
break;
373+
} else if (c == 'c') {
374+
break;
375+
} else {
376+
uint32_t len = read_be32(*f);
377+
f->ignore(len);
378+
}
379+
}
380+
321381
f->seekg(extensions_start);
322382
log_debug("reading 'h' (second pass)\n");
323383
int co_counter = 0;

passes/techmap/abc9_exe.cc

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,14 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe
248248
}
249249

250250
abc9_script += stringf("; &ps -l; &write -n %s/output.aig", tempdir_name);
251-
if (design->scratchpad_get_bool("abc9.verify", true)) {
252-
if (dff_mode)
251+
if (design->scratchpad_get_bool("abc9.verify")) {
252+
if (dff_mode) {
253253
abc9_script += "; &verify -s";
254-
else
255-
abc9_script += "; &verify";
254+
} else {
255+
abc9_script += "; &verify -y";
256+
// Rewrite output to include "y" extension from verification
257+
abc9_script += stringf("; &write -n %s/output.aig", tempdir_name);
258+
}
256259
}
257260
abc9_script += "; time";
258261
abc9_script = add_echos_to_abc9_cmd(abc9_script);

passes/techmap/aigmap.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ struct AigmapPass : public Pass {
110110
if (nand_mode && node.inverter) {
111111
bit = module->addWire(NEW_ID);
112112
auto gate = module->addNandGate(NEW_ID, A, B, bit);
113+
gate->set_src_attribute(cell->get_src_attribute());
113114
if (select_mode)
114115
new_sel.insert(gate->name);
115116

@@ -121,6 +122,7 @@ struct AigmapPass : public Pass {
121122
else {
122123
bit = module->addWire(NEW_ID);
123124
auto gate = module->addAndGate(NEW_ID, A, B, bit);
125+
gate->set_src_attribute(cell->get_src_attribute());
124126
if (select_mode)
125127
new_sel.insert(gate->name);
126128
}
@@ -130,6 +132,7 @@ struct AigmapPass : public Pass {
130132
if (node.inverter) {
131133
SigBit new_bit = module->addWire(NEW_ID);
132134
auto gate = module->addNotGate(NEW_ID, bit, new_bit);
135+
gate->set_src_attribute(cell->get_src_attribute());
133136
bit = new_bit;
134137
if (select_mode)
135138
new_sel.insert(gate->name);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
read_verilog <<EOT
2+
module top(input a, b, c, output o);
3+
assign o = (a & b) | c;
4+
endmodule
5+
EOT
6+
7+
# src attributes are set by the Verilog frontend based on source location
8+
# After simplemap, the $_AND_/$_OR_ cells should have \src attributes
9+
simplemap
10+
11+
# Check that cells have \src attributes before abc9
12+
select -assert-any a:src
13+
14+
# Enable verification with -y flag for src attribute propagation
15+
scratchpad -set abc9.verify true
16+
17+
# Run abc9
18+
abc9 -lut 4
19+
20+
# After abc9, the mapped LUT cells should have \src attributes
21+
# propagated via the "y" extension equivalence mapping
22+
select -assert-any t:$lut a:src

0 commit comments

Comments
 (0)