From e4ca7b8846b8684a30ba809369f2d2be983734dd Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Thu, 30 Jan 2025 17:26:23 +0100 Subject: [PATCH 01/26] abstract: -state MVP --- passes/cmds/Makefile.inc | 1 + passes/cmds/abstract.cc | 169 ++++++++++++++++++++++++++++++++++++++ tests/various/abstract.ys | 97 ++++++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 passes/cmds/abstract.cc create mode 100644 tests/various/abstract.ys diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 5a2af4df5..b15e91edb 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -53,3 +53,4 @@ OBJS += passes/cmds/example_dt.o OBJS += passes/cmds/portarcs.o OBJS += passes/cmds/wrapcell.o OBJS += passes/cmds/setenv.o +OBJS += passes/cmds/abstract.o diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc new file mode 100644 index 000000000..e336fad47 --- /dev/null +++ b/passes/cmds/abstract.cc @@ -0,0 +1,169 @@ +#include "kernel/yosys.h" +#include "kernel/celltypes.h" +#include "kernel/ff.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool abstract_state(Module* mod, Cell* cell, Wire* enable, bool enable_pol) { + FfData ff(nullptr, cell); + if (ff.has_sr) + log_cmd_error("SR not supported\n"); + + // Normalize to simpler FF + ff.unmap_ce(); + ff.unmap_srst(); + if (ff.has_arst) + ff.arst_to_aload(); + + // Construct abstract value + auto anyseq = mod->Anyseq(NEW_ID, ff.width); + + if (ff.has_aload) { + // ad := enable ? anyseq : ad + Wire* abstracted_ad = mod->addWire(NEW_ID, ff.sig_ad.size()); + SigSpec mux_a, mux_b; + if (enable_pol) { + mux_a = ff.sig_ad; + mux_b = anyseq; + } else { + mux_a = anyseq; + mux_b = ff.sig_ad; + } + (void)mod->addMux(NEW_ID, + mux_a, + mux_b, + enable, + abstracted_ad); + ff.sig_ad = abstracted_ad; + } + // d := enable ? anyseq : d + Wire* abstracted_d = mod->addWire(NEW_ID, ff.sig_d.size()); + SigSpec mux_a, mux_b; + if (enable_pol) { + mux_a = ff.sig_d; + mux_b = anyseq; + } else { + mux_a = anyseq; + mux_b = ff.sig_d; + } + (void)mod->addMux(NEW_ID, + mux_a, + mux_b, + enable, + abstracted_d); + ff.sig_d = abstracted_d; + (void)ff.emit(); + return true; +} + +bool abstract_value(Module* mod, Wire* wire, Wire* enable, bool enable_pol) { + // (void)mod->addMux(NEW_ID, + // mux_a, + // mux_b, + // enable, + // abstracted); + // cell->setPort(ID::D, SigSpec(abstracted)); + return false; +} + +bool abstract_init(Module* mod, Cell* cell) { + CellTypes ct; + ct.setup_internals_ff(); + if (!ct.cell_types.count(cell->type)) + return false; + // TODO figure out memory cells? + + cell->unsetParam(ID::init); + return true; +} + + +struct AbstractPass : public Pass { + AbstractPass() : Pass("abstract", "extract clock gating out of flip flops") { } + void help() override { + // TODO + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override { + log_header(design, "Executing ABSTRACT pass.\n"); + + size_t argidx; + enum Mode { + None, + State, + Initial, + Value, + }; + Mode mode; + std::string enable_name; + bool enable_pol; // true is high + for (argidx = 1; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + if (arg == "-state") { + mode = State; + continue; + } + if (arg == "-init") { + mode = Initial; + continue; + } + if (arg == "-value") { + mode = Value; + continue; + } + if (arg == "-enable") { + enable_name = args[++argidx]; + enable_pol = true; + continue; + } + if (arg == "-enablen") { + enable_name = args[++argidx]; + enable_pol = false; + continue; + } + break; + } + extra_args(args, argidx, design); + + unsigned int changed = 0; + if ((mode == State) || (mode == Value)) { + if (!enable_name.length()) + log_cmd_error("Unspecified enable wire\n"); + CellTypes ct; + if (mode == State) + ct.setup_internals_ff(); + for (auto mod : design->selected_modules()) { + log_debug("module %s\n", mod->name.c_str()); + Wire *enable_wire = mod->wire("\\" + enable_name); + if (!enable_wire) + log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); + if (mode == State) { + for (auto cell : mod->selected_cells()) + if (ct.cell_types.count(cell->type)) + changed += abstract_state(mod, cell, enable_wire, enable_pol); + } else { + // Value + for (auto wire : mod->selected_wires()) { + changed += abstract_value(mod, wire, enable_wire, enable_pol); + } + log_cmd_error("Unsupported (TODO)\n"); + } + } + log("Abstracted %d cells.\n", changed); + } else if (mode == Initial) { + for (auto mod : design->selected_modules()) { + for (auto cell : mod->selected_cells()) { + changed += abstract_init(mod, cell); + } + } + log("Abstracted %d wires.\n", changed); + } else { + log_cmd_error("No mode selected, see help message\n"); + } + } +} AbstractPass; + +PRIVATE_NAMESPACE_END diff --git a/tests/various/abstract.ys b/tests/various/abstract.ys new file mode 100644 index 000000000..8536960ca --- /dev/null +++ b/tests/various/abstract.ys @@ -0,0 +1,97 @@ +read_verilog < Date: Mon, 3 Feb 2025 22:25:09 +0100 Subject: [PATCH 02/26] abstract: -init MVP --- passes/cmds/abstract.cc | 68 ++++++++++++++++++++++++++++++++------- tests/various/abstract.ys | 13 ++------ 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index e336fad47..58fa2c513 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -67,17 +67,63 @@ bool abstract_value(Module* mod, Wire* wire, Wire* enable, bool enable_pol) { return false; } -bool abstract_init(Module* mod, Cell* cell) { - CellTypes ct; - ct.setup_internals_ff(); - if (!ct.cell_types.count(cell->type)) - return false; - // TODO figure out memory cells? +struct AbstractInitCtx { + Module* mod; + SigMap sigmap; + pool init_bits; +}; - cell->unsetParam(ID::init); - return true; +void collect_init_bits_cells(AbstractInitCtx& ctx) { + // TODO Should this discriminate between FFs and other cells? + for (auto cell : ctx.mod->selected_cells()) { + // Add all sigbits on all cell outputs to init_bits + for (auto &conn : cell->connections()) { + if (cell->output(conn.first)) { + for (auto bit : conn.second) { + log_debug("init: cell %s output %s\n", cell->name.c_str(), log_signal(bit)); + ctx.init_bits.insert(ctx.sigmap(bit)); + } + } + } + } } +void collect_init_bits_wires(AbstractInitCtx& ctx) { + for (auto wire : ctx.mod->selected_wires()) { + auto canonical = ctx.sigmap(wire); + // Find canonical drivers of all the wire bits and add them to init_bits + for (auto bit : canonical.bits()) { + log_debug("init: wire %s bit %s\n", wire->name.c_str(), log_signal(bit)); + ctx.init_bits.insert(ctx.sigmap(bit)); + } + } +} + +unsigned int abstract_init(Module* mod) { + AbstractInitCtx ctx {mod, SigMap(mod), pool()}; + pool init_bits; + collect_init_bits_cells(ctx); + collect_init_bits_wires(ctx); + unsigned int changed = 0; + + for (SigBit bit : ctx.init_bits) { +next_sigbit: + if (!bit.is_wire() || !bit.wire->has_attribute(ID::init)) + continue; + + Const init = bit.wire->attributes.at(ID::init); + std::vector& bits = init.bits(); + bits[bit.offset] = RTLIL::State::Sx; + changed++; + + for (auto bit : bits) + if (bit != RTLIL::State::Sx) + goto next_sigbit; + // All bits are Sx, erase init attribute entirely + bit.wire->attributes.erase(ID::init); + } + return changed; +} struct AbstractPass : public Pass { AbstractPass() : Pass("abstract", "extract clock gating out of flip flops") { } @@ -155,11 +201,9 @@ struct AbstractPass : public Pass { log("Abstracted %d cells.\n", changed); } else if (mode == Initial) { for (auto mod : design->selected_modules()) { - for (auto cell : mod->selected_cells()) { - changed += abstract_init(mod, cell); - } + changed += abstract_init(mod); } - log("Abstracted %d wires.\n", changed); + log("Abstracted %d bits.\n", changed); } else { log_cmd_error("No mode selected, see help message\n"); } diff --git a/tests/various/abstract.ys b/tests/various/abstract.ys index 8536960ca..fb3b3143a 100644 --- a/tests/various/abstract.ys +++ b/tests/various/abstract.ys @@ -10,12 +10,10 @@ endmodule EOT proc -# dump # show -prefix before_base abstract -state -enablen magic check # show -prefix after_base -# dump design -reset read_verilog < Date: Fri, 7 Feb 2025 10:46:50 +0100 Subject: [PATCH 03/26] abstract: -value MVP, use buffer-normalized mode --- passes/cmds/abstract.cc | 107 +++++++++++++++++++------------------- tests/various/abstract.ys | 76 +++++++++++++++++++++++---- 2 files changed, 121 insertions(+), 62 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 58fa2c513..27213a394 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -57,70 +57,70 @@ bool abstract_state(Module* mod, Cell* cell, Wire* enable, bool enable_pol) { return true; } -bool abstract_value(Module* mod, Wire* wire, Wire* enable, bool enable_pol) { - // (void)mod->addMux(NEW_ID, - // mux_a, - // mux_b, - // enable, - // abstracted); - // cell->setPort(ID::D, SigSpec(abstracted)); - return false; -} - -struct AbstractInitCtx { +struct AbstractPortCtx { Module* mod; SigMap sigmap; - pool init_bits; + pool> outs; }; -void collect_init_bits_cells(AbstractInitCtx& ctx) { - // TODO Should this discriminate between FFs and other cells? - for (auto cell : ctx.mod->selected_cells()) { - // Add all sigbits on all cell outputs to init_bits - for (auto &conn : cell->connections()) { - if (cell->output(conn.first)) { - for (auto bit : conn.second) { - log_debug("init: cell %s output %s\n", cell->name.c_str(), log_signal(bit)); - ctx.init_bits.insert(ctx.sigmap(bit)); - } - } +void collect_selected_ports(AbstractPortCtx& ctx) { + for (Cell* cell : ctx.mod->cells()) { + for (auto& conn : cell->connections()) { + // we bufnorm + log_assert(conn.second.is_wire() || conn.second.is_fully_const()); + if (conn.second.is_wire() && cell->output(conn.first)) + if (ctx.mod->selected(cell) || ctx.mod->selected(conn.second.as_wire())) + ctx.outs.insert(std::make_pair(cell, conn.first)); } } } -void collect_init_bits_wires(AbstractInitCtx& ctx) { - for (auto wire : ctx.mod->selected_wires()) { - auto canonical = ctx.sigmap(wire); - // Find canonical drivers of all the wire bits and add them to init_bits - for (auto bit : canonical.bits()) { - log_debug("init: wire %s bit %s\n", wire->name.c_str(), log_signal(bit)); - ctx.init_bits.insert(ctx.sigmap(bit)); +unsigned int abstract_value(Module* mod, Wire* enable, bool enable_pol) { + AbstractPortCtx ctx {mod, SigMap(mod), {}}; + collect_selected_ports(ctx); + unsigned int changed = 0; + for (auto [cell, port] : ctx.outs) { + SigSpec sig = cell->getPort(port); + log_assert(sig.is_wire()); + Wire* original = mod->addWire(NEW_ID, sig.size()); + cell->setPort(port, original); + auto anyseq = mod->Anyseq(NEW_ID, sig.size()); + // This code differs from abstract_state + // in that we reuse the original signal as the mux output, + // not input + SigSpec mux_a, mux_b; + if (enable_pol) { + mux_a = original; + mux_b = anyseq; + } else { + mux_a = anyseq; + mux_b = original; } + (void)mod->addMux(NEW_ID, + mux_a, + mux_b, + enable, + sig); + changed++; } + return changed; } unsigned int abstract_init(Module* mod) { - AbstractInitCtx ctx {mod, SigMap(mod), pool()}; - pool init_bits; - collect_init_bits_cells(ctx); - collect_init_bits_wires(ctx); + AbstractPortCtx ctx {mod, SigMap(mod), {}}; + collect_selected_ports(ctx); + unsigned int changed = 0; - for (SigBit bit : ctx.init_bits) { -next_sigbit: - if (!bit.is_wire() || !bit.wire->has_attribute(ID::init)) + for (auto [cell, port] : ctx.outs) { + SigSpec sig = cell->getPort(port); + log_assert(sig.is_wire()); + if (!sig.as_wire()->has_attribute(ID::init)) continue; - Const init = bit.wire->attributes.at(ID::init); - std::vector& bits = init.bits(); - bits[bit.offset] = RTLIL::State::Sx; - changed++; - - for (auto bit : bits) - if (bit != RTLIL::State::Sx) - goto next_sigbit; - // All bits are Sx, erase init attribute entirely - bit.wire->attributes.erase(ID::init); + Const init = sig.as_wire()->attributes.at(ID::init); + sig.as_wire()->attributes.erase(ID::init); + changed += sig.size(); } return changed; } @@ -174,6 +174,7 @@ struct AbstractPass : public Pass { } extra_args(args, argidx, design); + design->bufNormalize(true); unsigned int changed = 0; if ((mode == State) || (mode == Value)) { if (!enable_name.length()) @@ -191,14 +192,13 @@ struct AbstractPass : public Pass { if (ct.cell_types.count(cell->type)) changed += abstract_state(mod, cell, enable_wire, enable_pol); } else { - // Value - for (auto wire : mod->selected_wires()) { - changed += abstract_value(mod, wire, enable_wire, enable_pol); - } - log_cmd_error("Unsupported (TODO)\n"); + changed += abstract_value(mod, enable_wire, enable_pol); } } - log("Abstracted %d cells.\n", changed); + if (mode == State) + log("Abstracted %d cells.\n", changed); + else + log("Abstracted %d values.\n", changed); } else if (mode == Initial) { for (auto mod : design->selected_modules()) { changed += abstract_init(mod); @@ -207,6 +207,7 @@ struct AbstractPass : public Pass { } else { log_cmd_error("No mode selected, see help message\n"); } + design->bufNormalize(false); } } AbstractPass; diff --git a/tests/various/abstract.ys b/tests/various/abstract.ys index fb3b3143a..b122c7dc6 100644 --- a/tests/various/abstract.ys +++ b/tests/various/abstract.ys @@ -1,9 +1,9 @@ read_verilog < Date: Fri, 7 Feb 2025 13:38:50 +0100 Subject: [PATCH 04/26] abstract: -state allow partial abstraction, don't use buffer-normalized mode --- passes/cmds/abstract.cc | 136 ++++++++++++++++++++++++-------------- tests/various/abstract.ys | 21 ++++++ 2 files changed, 108 insertions(+), 49 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 27213a394..706636d59 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -5,58 +5,96 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool abstract_state(Module* mod, Cell* cell, Wire* enable, bool enable_pol) { - FfData ff(nullptr, cell); - if (ff.has_sr) - log_cmd_error("SR not supported\n"); - - // Normalize to simpler FF - ff.unmap_ce(); - ff.unmap_srst(); - if (ff.has_arst) - ff.arst_to_aload(); +struct EnableLogic { + Wire* wire; + bool pol; +}; +bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, EnableLogic enable) { // Construct abstract value - auto anyseq = mod->Anyseq(NEW_ID, ff.width); - - if (ff.has_aload) { - // ad := enable ? anyseq : ad - Wire* abstracted_ad = mod->addWire(NEW_ID, ff.sig_ad.size()); - SigSpec mux_a, mux_b; - if (enable_pol) { - mux_a = ff.sig_ad; - mux_b = anyseq; - } else { - mux_a = anyseq; - mux_b = ff.sig_ad; + auto anyseq = ff.module->Anyseq(NEW_ID, offsets.size()); + Wire* abstracted = ff.module->addWire(NEW_ID, offsets.size()); + SigSpec mux_input; + int abstracted_idx = 0; + for (int d_idx = 0; d_idx < ff.width; d_idx++) { + if (offsets.count(d_idx)) { + log_debug("bit %d: abstracted\n", d_idx); + mux_input.append(port_sig[d_idx]); + port_sig[d_idx].wire = abstracted; + port_sig[d_idx].offset = abstracted_idx; + log_assert(abstracted_idx < abstracted->width); + abstracted_idx++; } - (void)mod->addMux(NEW_ID, - mux_a, - mux_b, - enable, - abstracted_ad); - ff.sig_ad = abstracted_ad; } - // d := enable ? anyseq : d - Wire* abstracted_d = mod->addWire(NEW_ID, ff.sig_d.size()); SigSpec mux_a, mux_b; - if (enable_pol) { - mux_a = ff.sig_d; + if (enable.pol) { + mux_a = mux_input; mux_b = anyseq; } else { mux_a = anyseq; - mux_b = ff.sig_d; + mux_b = mux_input; } - (void)mod->addMux(NEW_ID, + (void)ff.module->addMux(NEW_ID, mux_a, mux_b, - enable, - abstracted_d); - ff.sig_d = abstracted_d; + enable.wire, + abstracted); (void)ff.emit(); return true; } +unsigned int abstract_state(Module* mod, EnableLogic enable) { + CellTypes ct; + ct.setup_internals_ff(); + SigMap sigmap(mod); + pool selected_representatives; + + // Collect reps for all wire bits of selected wires + for (auto wire : mod->selected_wires()) + for (auto bit : sigmap(wire)) + selected_representatives.insert(bit); + + // Collect reps for all output wire bits of selected cells + for (auto cell : mod->selected_cells()) + for (auto conn : cell->connections()) + if (cell->output(conn.first)) + for (auto bit : conn.second.bits()) + selected_representatives.insert(sigmap(bit)); + + unsigned int changed = 0; + std::vector ffs; + // Abstract flop inputs if they're driving a selected output rep + for (auto cell : mod->cells()) { + if (!ct.cell_types.count(cell->type)) + continue; + FfData ff(nullptr, cell); + if (ff.has_sr) + log_cmd_error("SR not supported\n"); + ffs.push_back(ff); + } + for (auto ff : ffs) { + // A bit inefficient + std::set offsets_to_abstract; + for (auto bit : ff.sig_q) + if (selected_representatives.count(sigmap(bit))) + offsets_to_abstract.insert(bit.offset); + + if (offsets_to_abstract.empty()) + continue; + + // Normalize to simpler FF + ff.unmap_ce(); + ff.unmap_srst(); + if (ff.has_arst) + ff.arst_to_aload(); + + if (ff.has_aload) + changed += abstract_state_port(ff, ff.sig_ad, offsets_to_abstract, enable); + changed += abstract_state_port(ff, ff.sig_d, offsets_to_abstract, enable); + } + return changed; +} + struct AbstractPortCtx { Module* mod; SigMap sigmap; @@ -75,7 +113,7 @@ void collect_selected_ports(AbstractPortCtx& ctx) { } } -unsigned int abstract_value(Module* mod, Wire* enable, bool enable_pol) { +unsigned int abstract_value(Module* mod, EnableLogic enable) { AbstractPortCtx ctx {mod, SigMap(mod), {}}; collect_selected_ports(ctx); unsigned int changed = 0; @@ -89,7 +127,7 @@ unsigned int abstract_value(Module* mod, Wire* enable, bool enable_pol) { // in that we reuse the original signal as the mux output, // not input SigSpec mux_a, mux_b; - if (enable_pol) { + if (enable.pol) { mux_a = original; mux_b = anyseq; } else { @@ -99,7 +137,7 @@ unsigned int abstract_value(Module* mod, Wire* enable, bool enable_pol) { (void)mod->addMux(NEW_ID, mux_a, mux_b, - enable, + enable.wire, sig); changed++; } @@ -174,25 +212,24 @@ struct AbstractPass : public Pass { } extra_args(args, argidx, design); - design->bufNormalize(true); + if (mode != State) + design->bufNormalize(true); + unsigned int changed = 0; if ((mode == State) || (mode == Value)) { if (!enable_name.length()) log_cmd_error("Unspecified enable wire\n"); - CellTypes ct; - if (mode == State) - ct.setup_internals_ff(); for (auto mod : design->selected_modules()) { log_debug("module %s\n", mod->name.c_str()); Wire *enable_wire = mod->wire("\\" + enable_name); if (!enable_wire) log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); if (mode == State) { - for (auto cell : mod->selected_cells()) - if (ct.cell_types.count(cell->type)) - changed += abstract_state(mod, cell, enable_wire, enable_pol); + // for (auto cell : mod->selected_cells()) + // if (ct.cell_types.count(cell->type)) + changed += abstract_state(mod, {enable_wire, enable_pol}); } else { - changed += abstract_value(mod, enable_wire, enable_pol); + changed += abstract_value(mod, {enable_wire, enable_pol}); } } if (mode == State) @@ -207,7 +244,8 @@ struct AbstractPass : public Pass { } else { log_cmd_error("No mode selected, see help message\n"); } - design->bufNormalize(false); + if (mode != State) + design->bufNormalize(false); } } AbstractPass; diff --git a/tests/various/abstract.ys b/tests/various/abstract.ys index b122c7dc6..0892ca1c4 100644 --- a/tests/various/abstract.ys +++ b/tests/various/abstract.ys @@ -15,6 +15,27 @@ abstract -state -enablen magic check -assert # show -prefix after_base +design -reset +read_verilog < Date: Mon, 10 Feb 2025 12:01:26 +0100 Subject: [PATCH 05/26] abstract: -state refactor sigbit rep pool collection --- passes/cmds/abstract.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 706636d59..743ff2dd8 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -43,10 +43,7 @@ bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, E return true; } -unsigned int abstract_state(Module* mod, EnableLogic enable) { - CellTypes ct; - ct.setup_internals_ff(); - SigMap sigmap(mod); +pool gather_selected_reps(Module* mod, SigMap& sigmap) { pool selected_representatives; // Collect reps for all wire bits of selected wires @@ -60,6 +57,14 @@ unsigned int abstract_state(Module* mod, EnableLogic enable) { if (cell->output(conn.first)) for (auto bit : conn.second.bits()) selected_representatives.insert(sigmap(bit)); + return selected_representatives; +} + +unsigned int abstract_state(Module* mod, EnableLogic enable) { + CellTypes ct; + ct.setup_internals_ff(); + SigMap sigmap(mod); + pool selected_representatives = gather_selected_reps(mod, sigmap); unsigned int changed = 0; std::vector ffs; From e9bba13a0d6537e153ed8f8d50a53ab68be0e8d2 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 13:06:40 +0100 Subject: [PATCH 06/26] abstract: no more bufnorm, -value has bit selection consistent with -state, -init temporarily gutted --- passes/cmds/abstract.cc | 119 ++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 743ff2dd8..84ec0ce14 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -100,71 +100,78 @@ unsigned int abstract_state(Module* mod, EnableLogic enable) { return changed; } -struct AbstractPortCtx { - Module* mod; - SigMap sigmap; - pool> outs; -}; - -void collect_selected_ports(AbstractPortCtx& ctx) { - for (Cell* cell : ctx.mod->cells()) { - for (auto& conn : cell->connections()) { - // we bufnorm - log_assert(conn.second.is_wire() || conn.second.is_fully_const()); - if (conn.second.is_wire() && cell->output(conn.first)) - if (ctx.mod->selected(cell) || ctx.mod->selected(conn.second.as_wire())) - ctx.outs.insert(std::make_pair(cell, conn.first)); - } - } -} - unsigned int abstract_value(Module* mod, EnableLogic enable) { - AbstractPortCtx ctx {mod, SigMap(mod), {}}; - collect_selected_ports(ctx); + SigMap sigmap(mod); + pool selected_representatives = gather_selected_reps(mod, sigmap); unsigned int changed = 0; - for (auto [cell, port] : ctx.outs) { - SigSpec sig = cell->getPort(port); - log_assert(sig.is_wire()); - Wire* original = mod->addWire(NEW_ID, sig.size()); - cell->setPort(port, original); - auto anyseq = mod->Anyseq(NEW_ID, sig.size()); - // This code differs from abstract_state - // in that we reuse the original signal as the mux output, - // not input - SigSpec mux_a, mux_b; - if (enable.pol) { - mux_a = original; - mux_b = anyseq; - } else { - mux_a = anyseq; - mux_b = original; - } - (void)mod->addMux(NEW_ID, - mux_a, - mux_b, - enable.wire, - sig); - changed++; + std::vector cells_snapshot = mod->cells(); + for (auto cell : cells_snapshot) { + for (auto conn : cell->connections()) + if (cell->output(conn.first)) { + std::set offsets_to_abstract; + for (int i = 0; i < conn.second.size(); i++) { + if (selected_representatives.count(sigmap(conn.second[i]))) { + offsets_to_abstract.insert(i); + } + } + if (offsets_to_abstract.empty()) + continue; + + auto anyseq = mod->Anyseq(NEW_ID, offsets_to_abstract.size()); + Wire* to_abstract = mod->addWire(NEW_ID, offsets_to_abstract.size()); + SigSpec mux_input; + SigSpec mux_output; + SigSpec new_port = conn.second; + int to_abstract_idx = 0; + for (int port_idx = 0; port_idx < conn.second.size(); port_idx++) { + if (offsets_to_abstract.count(port_idx)) { + log_debug("bit %d: abstracted\n", port_idx); + mux_output.append(conn.second[port_idx]); + SigBit in_bit {to_abstract, to_abstract_idx}; + new_port.replace(port_idx, in_bit); + conn.second[port_idx] = {mod->addWire(NEW_ID, 1), 0}; + mux_input.append(in_bit); + log_assert(to_abstract_idx < to_abstract->width); + to_abstract_idx++; + } + } + cell->setPort(conn.first, new_port); + SigSpec mux_a, mux_b; + if (enable.pol) { + mux_a = mux_input; + mux_b = anyseq; + } else { + mux_a = anyseq; + mux_b = mux_input; + } + (void)mod->addMux(NEW_ID, + mux_a, + mux_b, + enable.wire, + mux_output); + changed++; + } } return changed; } unsigned int abstract_init(Module* mod) { - AbstractPortCtx ctx {mod, SigMap(mod), {}}; - collect_selected_ports(ctx); + // AbstractPortCtx ctx {mod, SigMap(mod), {}}; + // collect_selected_ports(ctx); unsigned int changed = 0; - for (auto [cell, port] : ctx.outs) { - SigSpec sig = cell->getPort(port); - log_assert(sig.is_wire()); - if (!sig.as_wire()->has_attribute(ID::init)) - continue; + // for (auto [cell, port] : ctx.outs) { + // SigSpec sig = cell->getPort(port); + // log_assert(sig.is_wire()); + // if (!sig.as_wire()->has_attribute(ID::init)) + // continue; - Const init = sig.as_wire()->attributes.at(ID::init); - sig.as_wire()->attributes.erase(ID::init); - changed += sig.size(); - } + // Const init = sig.as_wire()->attributes.at(ID::init); + // sig.as_wire()->attributes.erase(ID::init); + // changed += sig.size(); + // } + log_cmd_error("Not implemented\n"); (void)mod; return changed; } @@ -217,8 +224,6 @@ struct AbstractPass : public Pass { } extra_args(args, argidx, design); - if (mode != State) - design->bufNormalize(true); unsigned int changed = 0; if ((mode == State) || (mode == Value)) { @@ -249,8 +254,6 @@ struct AbstractPass : public Pass { } else { log_cmd_error("No mode selected, see help message\n"); } - if (mode != State) - design->bufNormalize(false); } } AbstractPass; From 164699109254f04f7b5042618e8808878f7fec72 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 13:10:53 +0100 Subject: [PATCH 07/26] abstract: refactor -value --- passes/cmds/abstract.cc | 70 ++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 84ec0ce14..23817e5c1 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -100,6 +100,42 @@ unsigned int abstract_state(Module* mod, EnableLogic enable) { return changed; } +bool abstract_value_port(Module* mod, Cell* cell, std::set offsets, IdString port_name, EnableLogic enable) { + auto anyseq = mod->Anyseq(NEW_ID, offsets.size()); + Wire* to_abstract = mod->addWire(NEW_ID, offsets.size()); + SigSpec mux_input; + SigSpec mux_output; + const SigSpec& old_port = cell->getPort(port_name); + SigSpec new_port = old_port; + int to_abstract_idx = 0; + for (int port_idx = 0; port_idx < old_port.size(); port_idx++) { + if (offsets.count(port_idx)) { + log_debug("bit %d: abstracted\n", port_idx); + mux_output.append(old_port[port_idx]); + SigBit in_bit {to_abstract, to_abstract_idx}; + new_port.replace(port_idx, in_bit); + mux_input.append(in_bit); + log_assert(to_abstract_idx < to_abstract->width); + to_abstract_idx++; + } + } + cell->setPort(port_name, new_port); + SigSpec mux_a, mux_b; + if (enable.pol) { + mux_a = mux_input; + mux_b = anyseq; + } else { + mux_a = anyseq; + mux_b = mux_input; + } + (void)mod->addMux(NEW_ID, + mux_a, + mux_b, + enable.wire, + mux_output); + return true; +} + unsigned int abstract_value(Module* mod, EnableLogic enable) { SigMap sigmap(mod); pool selected_representatives = gather_selected_reps(mod, sigmap); @@ -117,39 +153,7 @@ unsigned int abstract_value(Module* mod, EnableLogic enable) { if (offsets_to_abstract.empty()) continue; - auto anyseq = mod->Anyseq(NEW_ID, offsets_to_abstract.size()); - Wire* to_abstract = mod->addWire(NEW_ID, offsets_to_abstract.size()); - SigSpec mux_input; - SigSpec mux_output; - SigSpec new_port = conn.second; - int to_abstract_idx = 0; - for (int port_idx = 0; port_idx < conn.second.size(); port_idx++) { - if (offsets_to_abstract.count(port_idx)) { - log_debug("bit %d: abstracted\n", port_idx); - mux_output.append(conn.second[port_idx]); - SigBit in_bit {to_abstract, to_abstract_idx}; - new_port.replace(port_idx, in_bit); - conn.second[port_idx] = {mod->addWire(NEW_ID, 1), 0}; - mux_input.append(in_bit); - log_assert(to_abstract_idx < to_abstract->width); - to_abstract_idx++; - } - } - cell->setPort(conn.first, new_port); - SigSpec mux_a, mux_b; - if (enable.pol) { - mux_a = mux_input; - mux_b = anyseq; - } else { - mux_a = anyseq; - mux_b = mux_input; - } - (void)mod->addMux(NEW_ID, - mux_a, - mux_b, - enable.wire, - mux_output); - changed++; + changed += abstract_value_port(mod, cell, offsets_to_abstract, conn.first, enable); } } return changed; From 9895370b329898900bab8e1fa9a02f81fc7f1c9e Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 14:56:52 +0100 Subject: [PATCH 08/26] abstract: rework -init without bufnorm, with logging --- passes/cmds/abstract.cc | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 23817e5c1..48ec09f8c 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -1,6 +1,7 @@ #include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/ff.h" +#include "kernel/ffinit.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -160,22 +161,24 @@ unsigned int abstract_value(Module* mod, EnableLogic enable) { } unsigned int abstract_init(Module* mod) { - // AbstractPortCtx ctx {mod, SigMap(mod), {}}; - // collect_selected_ports(ctx); - unsigned int changed = 0; + FfInitVals initvals; + SigMap sigmap(mod); + initvals.set(&sigmap, mod); + for (auto wire : mod->selected_wires()) + for (auto bit : SigSpec(wire)) { + // TODO these don't seem too informative + log_debug("Removing init bit on %s due to selected wire %s\n", log_signal(bit), wire->name.c_str()); + initvals.remove_init(bit); + } - // for (auto [cell, port] : ctx.outs) { - // SigSpec sig = cell->getPort(port); - // log_assert(sig.is_wire()); - // if (!sig.as_wire()->has_attribute(ID::init)) - // continue; - - // Const init = sig.as_wire()->attributes.at(ID::init); - // sig.as_wire()->attributes.erase(ID::init); - // changed += sig.size(); - // } - log_cmd_error("Not implemented\n"); (void)mod; + for (auto cell : mod->selected_cells()) + for (auto conn : cell->connections()) + if (cell->output(conn.first)) + for (auto bit : conn.second.bits()) { + log_debug("Removing init bit on %s due to selected cell %s\n", log_signal(bit), cell->name.c_str()); + initvals.remove_init(bit); + } return changed; } From cee06cecd0991bf95ffbff0a9fe769c30d973f03 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 14:59:50 +0100 Subject: [PATCH 09/26] abstract: factor out emit_mux_anyseq --- passes/cmds/abstract.cc | 48 ++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 48ec09f8c..d770a8575 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -11,9 +11,24 @@ struct EnableLogic { bool pol; }; +void emit_mux_anyseq(Module* mod, const SigSpec& mux_input, const SigSpec& mux_output, EnableLogic enable) { + auto anyseq = mod->Anyseq(NEW_ID, mux_input.size()); + SigSpec mux_a, mux_b; + if (enable.pol) { + mux_a = mux_input; + mux_b = anyseq; + } else { + mux_a = anyseq; + mux_b = mux_input; + } + (void)mod->addMux(NEW_ID, + mux_a, + mux_b, + enable.wire, + mux_output); +} + bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, EnableLogic enable) { - // Construct abstract value - auto anyseq = ff.module->Anyseq(NEW_ID, offsets.size()); Wire* abstracted = ff.module->addWire(NEW_ID, offsets.size()); SigSpec mux_input; int abstracted_idx = 0; @@ -27,19 +42,7 @@ bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, E abstracted_idx++; } } - SigSpec mux_a, mux_b; - if (enable.pol) { - mux_a = mux_input; - mux_b = anyseq; - } else { - mux_a = anyseq; - mux_b = mux_input; - } - (void)ff.module->addMux(NEW_ID, - mux_a, - mux_b, - enable.wire, - abstracted); + emit_mux_anyseq(ff.module, mux_input, abstracted, enable); (void)ff.emit(); return true; } @@ -102,7 +105,6 @@ unsigned int abstract_state(Module* mod, EnableLogic enable) { } bool abstract_value_port(Module* mod, Cell* cell, std::set offsets, IdString port_name, EnableLogic enable) { - auto anyseq = mod->Anyseq(NEW_ID, offsets.size()); Wire* to_abstract = mod->addWire(NEW_ID, offsets.size()); SigSpec mux_input; SigSpec mux_output; @@ -121,19 +123,7 @@ bool abstract_value_port(Module* mod, Cell* cell, std::set offsets, IdStrin } } cell->setPort(port_name, new_port); - SigSpec mux_a, mux_b; - if (enable.pol) { - mux_a = mux_input; - mux_b = anyseq; - } else { - mux_a = anyseq; - mux_b = mux_input; - } - (void)mod->addMux(NEW_ID, - mux_a, - mux_b, - enable.wire, - mux_output); + emit_mux_anyseq(mod, mux_input, mux_output, enable); return true; } From aca4d44a40ec8863978d02f77378817923629f58 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 16:24:42 +0100 Subject: [PATCH 10/26] abstract: improve debug logs for -state and -value --- passes/cmds/abstract.cc | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index d770a8575..55f0f4bcd 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -2,6 +2,7 @@ #include "kernel/celltypes.h" #include "kernel/ff.h" #include "kernel/ffinit.h" +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -47,28 +48,40 @@ bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, E return true; } -pool gather_selected_reps(Module* mod, SigMap& sigmap) { - pool selected_representatives; +using SelReason=std::variant; +dict> gather_selected_reps(Module* mod, SigMap& sigmap) { + dict> selected_reps; // Collect reps for all wire bits of selected wires for (auto wire : mod->selected_wires()) for (auto bit : sigmap(wire)) - selected_representatives.insert(bit); + selected_reps.insert(bit).first->second.push_back(wire); // Collect reps for all output wire bits of selected cells for (auto cell : mod->selected_cells()) for (auto conn : cell->connections()) if (cell->output(conn.first)) for (auto bit : conn.second.bits()) - selected_representatives.insert(sigmap(bit)); - return selected_representatives; + selected_reps.insert(sigmap(bit)).first->second.push_back(cell); + return selected_reps; +} + +void explain_selections(const std::vector& reasons) { + for (std::variant reason : reasons) { + if (Cell** cell_reason = std::get_if(&reason)) + log_debug("\tcell %s\n", (*cell_reason)->name.c_str()); + else if (Wire** wire_reason = std::get_if(&reason)) + log_debug("\twire %s\n", (*wire_reason)->name.c_str()); + else + log_assert(false && "insane reason variant\n"); + } } unsigned int abstract_state(Module* mod, EnableLogic enable) { CellTypes ct; ct.setup_internals_ff(); SigMap sigmap(mod); - pool selected_representatives = gather_selected_reps(mod, sigmap); + dict> selected_reps = gather_selected_reps(mod, sigmap); unsigned int changed = 0; std::vector ffs; @@ -85,8 +98,11 @@ unsigned int abstract_state(Module* mod, EnableLogic enable) { // A bit inefficient std::set offsets_to_abstract; for (auto bit : ff.sig_q) - if (selected_representatives.count(sigmap(bit))) + if (selected_reps.count(sigmap(bit))) { + log_debug("Abstracting state for bit %s due to selections:\n", log_signal(bit)); + explain_selections(selected_reps.at(sigmap(bit))); offsets_to_abstract.insert(bit.offset); + } if (offsets_to_abstract.empty()) continue; @@ -129,7 +145,7 @@ bool abstract_value_port(Module* mod, Cell* cell, std::set offsets, IdStrin unsigned int abstract_value(Module* mod, EnableLogic enable) { SigMap sigmap(mod); - pool selected_representatives = gather_selected_reps(mod, sigmap); + dict> selected_reps = gather_selected_reps(mod, sigmap); unsigned int changed = 0; std::vector cells_snapshot = mod->cells(); for (auto cell : cells_snapshot) { @@ -137,7 +153,9 @@ unsigned int abstract_value(Module* mod, EnableLogic enable) { if (cell->output(conn.first)) { std::set offsets_to_abstract; for (int i = 0; i < conn.second.size(); i++) { - if (selected_representatives.count(sigmap(conn.second[i]))) { + if (selected_reps.count(sigmap(conn.second[i]))) { + log_debug("Abstracting value for bit %s due to selections:\n", log_signal(conn.second[i])); + explain_selections(selected_reps.at(sigmap(conn.second[i]))); offsets_to_abstract.insert(i); } } From 9de890c87417cbed4dd8a93d5fc6e5a6dcb3bdbc Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 16:27:40 +0100 Subject: [PATCH 11/26] abstract: fix -init log_debug bit count, remove unnecessary log_debug --- passes/cmds/abstract.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 55f0f4bcd..ab3d215fa 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -35,7 +35,6 @@ bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, E int abstracted_idx = 0; for (int d_idx = 0; d_idx < ff.width; d_idx++) { if (offsets.count(d_idx)) { - log_debug("bit %d: abstracted\n", d_idx); mux_input.append(port_sig[d_idx]); port_sig[d_idx].wire = abstracted; port_sig[d_idx].offset = abstracted_idx; @@ -49,6 +48,7 @@ bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, E } using SelReason=std::variant; + dict> gather_selected_reps(Module* mod, SigMap& sigmap) { dict> selected_reps; @@ -129,7 +129,6 @@ bool abstract_value_port(Module* mod, Cell* cell, std::set offsets, IdStrin int to_abstract_idx = 0; for (int port_idx = 0; port_idx < old_port.size(); port_idx++) { if (offsets.count(port_idx)) { - log_debug("bit %d: abstracted\n", port_idx); mux_output.append(old_port[port_idx]); SigBit in_bit {to_abstract, to_abstract_idx}; new_port.replace(port_idx, in_bit); @@ -178,6 +177,7 @@ unsigned int abstract_init(Module* mod) { // TODO these don't seem too informative log_debug("Removing init bit on %s due to selected wire %s\n", log_signal(bit), wire->name.c_str()); initvals.remove_init(bit); + changed++; } for (auto cell : mod->selected_cells()) @@ -186,6 +186,7 @@ unsigned int abstract_init(Module* mod) { for (auto bit : conn.second.bits()) { log_debug("Removing init bit on %s due to selected cell %s\n", log_signal(bit), cell->name.c_str()); initvals.remove_init(bit); + changed++; } return changed; } @@ -245,7 +246,6 @@ struct AbstractPass : public Pass { if (!enable_name.length()) log_cmd_error("Unspecified enable wire\n"); for (auto mod : design->selected_modules()) { - log_debug("module %s\n", mod->name.c_str()); Wire *enable_wire = mod->wire("\\" + enable_name); if (!enable_wire) log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); From 3dd697fc8ad410d7f324daa3998ee8657ce7c318 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 16:33:20 +0100 Subject: [PATCH 12/26] abstract: improve -init logging --- passes/cmds/abstract.cc | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index ab3d215fa..3b0145d9a 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -171,23 +171,14 @@ unsigned int abstract_init(Module* mod) { unsigned int changed = 0; FfInitVals initvals; SigMap sigmap(mod); + dict> selected_reps = gather_selected_reps(mod, sigmap); initvals.set(&sigmap, mod); - for (auto wire : mod->selected_wires()) - for (auto bit : SigSpec(wire)) { - // TODO these don't seem too informative - log_debug("Removing init bit on %s due to selected wire %s\n", log_signal(bit), wire->name.c_str()); - initvals.remove_init(bit); - changed++; - } - - for (auto cell : mod->selected_cells()) - for (auto conn : cell->connections()) - if (cell->output(conn.first)) - for (auto bit : conn.second.bits()) { - log_debug("Removing init bit on %s due to selected cell %s\n", log_signal(bit), cell->name.c_str()); - initvals.remove_init(bit); - changed++; - } + for (auto bit : selected_reps) { + log_debug("Removing init bit on %s due to selections:\n", log_signal(bit.first)); + explain_selections(bit.second); + initvals.remove_init(bit.first); + changed++; + } return changed; } From 28c768e7b8943f37b59fe7b86281fe9a361ddf63 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Feb 2025 16:36:41 +0100 Subject: [PATCH 13/26] abstract: better present changes done --- passes/cmds/abstract.cc | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 3b0145d9a..d0e1603ce 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -240,23 +240,20 @@ struct AbstractPass : public Pass { Wire *enable_wire = mod->wire("\\" + enable_name); if (!enable_wire) log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); - if (mode == State) { - // for (auto cell : mod->selected_cells()) - // if (ct.cell_types.count(cell->type)) + if (mode == State) changed += abstract_state(mod, {enable_wire, enable_pol}); - } else { + else changed += abstract_value(mod, {enable_wire, enable_pol}); - } } if (mode == State) - log("Abstracted %d cells.\n", changed); + log("Abstracted %d stateful cells.\n", changed); else - log("Abstracted %d values.\n", changed); + log("Abstracted %d driver ports.\n", changed); } else if (mode == Initial) { for (auto mod : design->selected_modules()) { changed += abstract_init(mod); } - log("Abstracted %d bits.\n", changed); + log("Abstracted %d init bits.\n", changed); } else { log_cmd_error("No mode selected, see help message\n"); } From 7cd822b7f5ef3fb1d41cf880aa3e4f0549506bb1 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 12 Feb 2025 15:34:51 +0100 Subject: [PATCH 14/26] rtlil: Add {from,to}_hdl_index methods to Wire In the past we had the occasional bug due to some place not handling all 4 combinations of upto/downto and zero/nonzero start_offset correctly. --- kernel/rtlil.h | 13 +++++ tests/unit/kernel/rtlilTest.cc | 94 +++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/kernel/rtlil.h b/kernel/rtlil.h index f9cacd151..54735bacb 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -1673,6 +1673,19 @@ public: RTLIL::Cell *driverCell() const { log_assert(driverCell_); return driverCell_; }; RTLIL::IdString driverPort() const { log_assert(driverCell_); return driverPort_; }; + int from_hdl_index(int hdl_index) { + int zero_index = hdl_index - start_offset; + int rtlil_index = upto ? width - 1 - zero_index : zero_index; + return rtlil_index >= 0 && rtlil_index < width ? rtlil_index : INT_MIN; + } + + int to_hdl_index(int rtlil_index) { + if (rtlil_index < 0 || rtlil_index >= width) + return INT_MIN; + int zero_index = upto ? width - 1 - rtlil_index : rtlil_index; + return zero_index + start_offset; + } + #ifdef WITH_PYTHON static std::map *get_all_wires(void); #endif diff --git a/tests/unit/kernel/rtlilTest.cc b/tests/unit/kernel/rtlilTest.cc index 1d5ef3a83..cb773202d 100644 --- a/tests/unit/kernel/rtlilTest.cc +++ b/tests/unit/kernel/rtlilTest.cc @@ -5,7 +5,22 @@ YOSYS_NAMESPACE_BEGIN namespace RTLIL { - class KernelRtlilTest : public testing::Test {}; + struct SafePrintToStringParamName { + template + std::string operator()(const testing::TestParamInfo& info) const { + std::string name = testing::PrintToString(info.param); + for (auto &c : name) + if (!('0' <= c && c <= '9') && !('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z') ) c = '_'; + return name; + } + }; + + class KernelRtlilTest : public testing::Test { + protected: + KernelRtlilTest() { + if (log_files.empty()) log_files.emplace_back(stdout); + } + }; TEST_F(KernelRtlilTest, ConstAssignCompare) { @@ -74,6 +89,83 @@ namespace RTLIL { } } + + class WireRtlVsHdlIndexConversionTest : + public KernelRtlilTest, + public testing::WithParamInterface> + {}; + + INSTANTIATE_TEST_SUITE_P( + WireRtlVsHdlIndexConversionInstance, + WireRtlVsHdlIndexConversionTest, + testing::Values( + std::make_tuple(false, 0, 10), + std::make_tuple(true, 0, 10), + std::make_tuple(false, 4, 10), + std::make_tuple(true, 4, 10), + std::make_tuple(false, -4, 10), + std::make_tuple(true, -4, 10), + std::make_tuple(false, 0, 1), + std::make_tuple(true, 0, 1), + std::make_tuple(false, 4, 1), + std::make_tuple(true, 4, 1), + std::make_tuple(false, -4, 1), + std::make_tuple(true, -4, 1) + ), + SafePrintToStringParamName() + ); + + TEST_P(WireRtlVsHdlIndexConversionTest, WireRtlVsHdlIndexConversion) { + std::unique_ptr mod = std::make_unique(); + Wire *wire = mod->addWire(ID(test), 10); + + auto [upto, start_offset, width] = GetParam(); + + wire->upto = upto; + wire->start_offset = start_offset; + wire->width = width; + + int smallest = INT_MAX; + int largest = INT_MIN; + + for (int i = 0; i < wire->width; i++) { + int j = wire->to_hdl_index(i); + smallest = std::min(smallest, j); + largest = std::max(largest, j); + EXPECT_EQ( + std::make_pair(wire->from_hdl_index(j), j), + std::make_pair(i, wire->to_hdl_index(i)) + ); + } + + EXPECT_EQ(smallest, start_offset); + EXPECT_EQ(largest, start_offset + wire->width - 1); + + for (int i = 1; i < wire->width; i++) { + EXPECT_EQ( + wire->to_hdl_index(i) - wire->to_hdl_index(i - 1), + upto ? -1 : 1 + ); + } + + for (int j = smallest; j < largest; j++) { + int i = wire->from_hdl_index(j); + EXPECT_EQ( + std::make_pair(wire->from_hdl_index(j), j), + std::make_pair(i, wire->to_hdl_index(i)) + ); + } + + for (int i = -10; i < 0; i++) + EXPECT_EQ(wire->to_hdl_index(i), INT_MIN); + for (int i = wire->width; i < wire->width + 10; i++) + EXPECT_EQ(wire->to_hdl_index(i), INT_MIN); + for (int j = smallest - 10; j < smallest; j++) + EXPECT_EQ(wire->from_hdl_index(j), INT_MIN); + for (int j = largest + 1; j < largest + 11; j++) + EXPECT_EQ(wire->from_hdl_index(j), INT_MIN); + } + } YOSYS_NAMESPACE_END From 37aa2e6cd829b26acbc61a8ad7569fcc315b6251 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 12 Feb 2025 15:38:46 +0100 Subject: [PATCH 15/26] abstract: Wire vs port offset confusion bugfix This fixes the offsets_to_abstract collection in abstract_state so that it now works the same way as in abstract_value which was already correct. --- passes/cmds/abstract.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index d0e1603ce..d07f19081 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -97,12 +97,14 @@ unsigned int abstract_state(Module* mod, EnableLogic enable) { for (auto ff : ffs) { // A bit inefficient std::set offsets_to_abstract; - for (auto bit : ff.sig_q) + for (int i = 0; i < GetSize(ff.sig_q); i++) { + SigBit bit = ff.sig_q[i]; if (selected_reps.count(sigmap(bit))) { log_debug("Abstracting state for bit %s due to selections:\n", log_signal(bit)); explain_selections(selected_reps.at(sigmap(bit))); - offsets_to_abstract.insert(bit.offset); + offsets_to_abstract.insert(i); } + } if (offsets_to_abstract.empty()) continue; From 4766c92e5976d629819d4d3a03100ef43855fa58 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 12 Feb 2025 15:45:47 +0100 Subject: [PATCH 16/26] abstract: Allow unconditional value and state abstractions Also improves -enable and -enablen command line handling --- passes/cmds/abstract.cc | 54 +++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index d07f19081..da8c96af8 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -8,12 +8,15 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct EnableLogic { - Wire* wire; + SigBit bit; bool pol; }; void emit_mux_anyseq(Module* mod, const SigSpec& mux_input, const SigSpec& mux_output, EnableLogic enable) { auto anyseq = mod->Anyseq(NEW_ID, mux_input.size()); + if (enable.bit == (enable.pol ? State::S1 : State::S0)) { + mod->connect(mux_output, anyseq); + } SigSpec mux_a, mux_b; if (enable.pol) { mux_a = mux_input; @@ -25,7 +28,7 @@ void emit_mux_anyseq(Module* mod, const SigSpec& mux_input, const SigSpec& mux_o (void)mod->addMux(NEW_ID, mux_a, mux_b, - enable.wire, + enable.bit, mux_output); } @@ -201,9 +204,14 @@ struct AbstractPass : public Pass { Initial, Value, }; - Mode mode; + Mode mode = Mode::None; + enum Enable { + Always = -1, + ActiveLow = false, // ensuring we can use bool(enable) + ActiveHigh = true, + }; + Enable enable = Enable::Always; std::string enable_name; - bool enable_pol; // true is high for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; @@ -219,33 +227,49 @@ struct AbstractPass : public Pass { mode = Value; continue; } - if (arg == "-enable") { + if (arg == "-enable" && argidx + 1 < args.size()) { + if (enable != Enable::Always) + log_cmd_error("Multiple enable condition are not supported\n"); enable_name = args[++argidx]; - enable_pol = true; + enable = Enable::ActiveHigh; continue; } - if (arg == "-enablen") { + if (arg == "-enablen" && argidx + 1 < args.size()) { + if (enable != Enable::Always) + log_cmd_error("Multiple enable condition are not supported\n"); enable_name = args[++argidx]; - enable_pol = false; + enable = Enable::ActiveLow; continue; } break; } extra_args(args, argidx, design); + if (enable != Enable::Always) { + if (mode == Mode::Initial) + log_cmd_error("Conditional initial value abstraction is not supported\n"); + + if (enable_name.empty()) + log_cmd_error("Unspecified enable wire\n"); + } unsigned int changed = 0; if ((mode == State) || (mode == Value)) { - if (!enable_name.length()) - log_cmd_error("Unspecified enable wire\n"); for (auto mod : design->selected_modules()) { - Wire *enable_wire = mod->wire("\\" + enable_name); - if (!enable_wire) - log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); + EnableLogic enable_logic = { State::S1, true }; + if (enable != Enable::Always) { + Wire *enable_wire = mod->wire("\\" + enable_name); + if (!enable_wire) + log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); + if (GetSize(enable_wire) != 1) + log_cmd_error("Enable wire %s must have width 1 but has width %d in module %s\n", + enable_name.c_str(), GetSize(enable_wire), mod->name.c_str()); + enable_logic = { enable_wire, enable == Enable::ActiveHigh }; + } if (mode == State) - changed += abstract_state(mod, {enable_wire, enable_pol}); + changed += abstract_state(mod, enable_logic); else - changed += abstract_value(mod, {enable_wire, enable_pol}); + changed += abstract_value(mod, enable_logic); } if (mode == State) log("Abstracted %d stateful cells.\n", changed); From a0987195f2de7081f8bb223e9d7c541f52d44b08 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 12 Feb 2025 15:50:34 +0100 Subject: [PATCH 17/26] abstract: Support slicing of individual wires --- passes/cmds/abstract.cc | 133 ++++++++++++++++++++++++++++++++++------ 1 file changed, 113 insertions(+), 20 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index da8c96af8..43940a6ea 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -3,6 +3,7 @@ #include "kernel/ff.h" #include "kernel/ffinit.h" #include +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -12,6 +13,74 @@ struct EnableLogic { bool pol; }; +enum SliceIndices { + RtlilSlice, + HdlSlice, +}; + +struct Slice { + SliceIndices indices; + int first; + int last; + + Slice(SliceIndices indices, const std::string &slice) : + indices(indices) + { + if (slice.empty()) + syntax_error(slice); + auto sep = slice.find(':'); + const char *first_begin, *first_end, *last_begin, *last_end; + if (sep == std::string::npos) { + first_begin = last_begin = slice.c_str(); + first_end = last_end = slice.c_str() + slice.length(); + } else { + first_begin = slice.c_str(); + first_end = first_begin + sep; + + last_begin = first_end + 1; + last_end = slice.c_str() + slice.length(); + } + first = parse_index(first_begin, first_end, slice); + last = parse_index(last_begin, last_end, slice); + } + + static int parse_index(const char *begin, const char *end, const std::string &slice) { + int value; + auto result = std::from_chars(begin, end, value, 10); + if (result.ptr != end || result.ptr == begin) + syntax_error(slice); + return value; + } + + static void syntax_error(const std::string &slice) { + log_cmd_error("Invalid slice '%s', expected ':' or ''", slice.c_str()); + } + + std::string to_string() const { + const char *option = indices == RtlilSlice ? "-rawslice" : "-slice"; + if (first == last) + return stringf("%s %d", option, first); + else + return stringf("%s %d:%d", option, first, last); + } + + int wire_offset(RTLIL::Wire *wire, int index) const { + int rtl_offset = indices == RtlilSlice ? index : wire->from_hdl_index(index); + if (rtl_offset < 0 || rtl_offset >= wire->width) { + log_error("Slice %s is out of bounds for wire %s in module %s", to_string().c_str(), log_id(wire), log_id(wire->module)); + } + return rtl_offset; + } + + std::pair wire_range(RTLIL::Wire *wire) const { + int rtl_first = wire_offset(wire, first); + int rtl_last = wire_offset(wire, last); + if (rtl_first > rtl_last) + std::swap(rtl_first, rtl_last); + return {rtl_first, rtl_last + 1}; + } +}; + void emit_mux_anyseq(Module* mod, const SigSpec& mux_input, const SigSpec& mux_output, EnableLogic enable) { auto anyseq = mod->Anyseq(NEW_ID, mux_input.size()); if (enable.bit == (enable.pol ? State::S1 : State::S0)) { @@ -52,20 +121,35 @@ bool abstract_state_port(FfData& ff, SigSpec& port_sig, std::set offsets, E using SelReason=std::variant; -dict> gather_selected_reps(Module* mod, SigMap& sigmap) { +dict> gather_selected_reps(Module* mod, const std::vector &slices, SigMap& sigmap) { dict> selected_reps; - // Collect reps for all wire bits of selected wires - for (auto wire : mod->selected_wires()) - for (auto bit : sigmap(wire)) - selected_reps.insert(bit).first->second.push_back(wire); + if (slices.empty()) { + // Collect reps for all wire bits of selected wires + for (auto wire : mod->selected_wires()) + for (auto bit : sigmap(wire)) + selected_reps.insert(bit).first->second.push_back(wire); - // Collect reps for all output wire bits of selected cells - for (auto cell : mod->selected_cells()) - for (auto conn : cell->connections()) - if (cell->output(conn.first)) - for (auto bit : conn.second.bits()) - selected_reps.insert(sigmap(bit)).first->second.push_back(cell); + // Collect reps for all output wire bits of selected cells + for (auto cell : mod->selected_cells()) + for (auto conn : cell->connections()) + if (cell->output(conn.first)) + for (auto bit : conn.second.bits()) + selected_reps.insert(sigmap(bit)).first->second.push_back(cell); + } else { + if (mod->selected_wires().size() != 1 || !mod->selected_cells().empty()) + log_error("Slices are only supported for single-wire selections\n"); + + auto wire = mod->selected_wires()[0]; + + for (auto slice : slices) { + auto [begin, end] = slice.wire_range(wire); + for (int i = begin; i < end; i++) { + selected_reps.insert(sigmap(SigBit(wire, i))).first->second.push_back(wire); + } + } + + } return selected_reps; } @@ -80,11 +164,11 @@ void explain_selections(const std::vector& reasons) { } } -unsigned int abstract_state(Module* mod, EnableLogic enable) { +unsigned int abstract_state(Module* mod, EnableLogic enable, const std::vector &slices) { CellTypes ct; ct.setup_internals_ff(); SigMap sigmap(mod); - dict> selected_reps = gather_selected_reps(mod, sigmap); + dict> selected_reps = gather_selected_reps(mod, slices, sigmap); unsigned int changed = 0; std::vector ffs; @@ -147,9 +231,9 @@ bool abstract_value_port(Module* mod, Cell* cell, std::set offsets, IdStrin return true; } -unsigned int abstract_value(Module* mod, EnableLogic enable) { +unsigned int abstract_value(Module* mod, EnableLogic enable, const std::vector &slices) { SigMap sigmap(mod); - dict> selected_reps = gather_selected_reps(mod, sigmap); + dict> selected_reps = gather_selected_reps(mod, slices, sigmap); unsigned int changed = 0; std::vector cells_snapshot = mod->cells(); for (auto cell : cells_snapshot) { @@ -172,11 +256,11 @@ unsigned int abstract_value(Module* mod, EnableLogic enable) { return changed; } -unsigned int abstract_init(Module* mod) { +unsigned int abstract_init(Module* mod, const std::vector &slices) { unsigned int changed = 0; FfInitVals initvals; SigMap sigmap(mod); - dict> selected_reps = gather_selected_reps(mod, sigmap); + dict> selected_reps = gather_selected_reps(mod, slices, sigmap); initvals.set(&sigmap, mod); for (auto bit : selected_reps) { log_debug("Removing init bit on %s due to selections:\n", log_signal(bit.first)); @@ -212,6 +296,7 @@ struct AbstractPass : public Pass { }; Enable enable = Enable::Always; std::string enable_name; + std::vector slices; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; @@ -241,6 +326,14 @@ struct AbstractPass : public Pass { enable = Enable::ActiveLow; continue; } + if (arg == "-slice" && argidx + 1 < args.size()) { + slices.emplace_back(SliceIndices::HdlSlice, args[++argidx]); + continue; + } + if (arg == "-rtlilslice" && argidx + 1 < args.size()) { + slices.emplace_back(SliceIndices::RtlilSlice, args[++argidx]); + continue; + } break; } extra_args(args, argidx, design); @@ -267,9 +360,9 @@ struct AbstractPass : public Pass { enable_logic = { enable_wire, enable == Enable::ActiveHigh }; } if (mode == State) - changed += abstract_state(mod, enable_logic); + changed += abstract_state(mod, enable_logic, slices); else - changed += abstract_value(mod, enable_logic); + changed += abstract_value(mod, enable_logic, slices); } if (mode == State) log("Abstracted %d stateful cells.\n", changed); @@ -277,7 +370,7 @@ struct AbstractPass : public Pass { log("Abstracted %d driver ports.\n", changed); } else if (mode == Initial) { for (auto mod : design->selected_modules()) { - changed += abstract_init(mod); + changed += abstract_init(mod, slices); } log("Abstracted %d init bits.\n", changed); } else { From 2943c2142dd5badb57ae1d35179db98e5b25e412 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 12 Feb 2025 16:27:59 +0100 Subject: [PATCH 18/26] abstract: Improve debug logging Print the port bit instead of the arbitrary representative sigbit to identify the target of the abstraction operation. --- passes/cmds/abstract.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 43940a6ea..5639758bb 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -187,7 +187,7 @@ unsigned int abstract_state(Module* mod, EnableLogic enable, const std::vector offsets_to_abstract; for (int i = 0; i < conn.second.size(); i++) { if (selected_reps.count(sigmap(conn.second[i]))) { - log_debug("Abstracting value for bit %s due to selections:\n", log_signal(conn.second[i])); + log_debug("Abstracting value for %s.%s[%i] in module %s due to selections:\n", + log_id(cell), log_id(conn.first), i, log_id(mod)); explain_selections(selected_reps.at(sigmap(conn.second[i]))); offsets_to_abstract.insert(i); } From 212224dfe8409c77299ddeec3e315915e8e7df57 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Wed, 12 Feb 2025 17:17:06 +0100 Subject: [PATCH 19/26] abstract: Add help message --- passes/cmds/abstract.cc | 68 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 5639758bb..4a97a640c 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -276,11 +276,75 @@ unsigned int abstract_init(Module* mod, const std::vector &slices) { } struct AbstractPass : public Pass { - AbstractPass() : Pass("abstract", "extract clock gating out of flip flops") { } + AbstractPass() : Pass("abstract", "replace signals with abstract values during formal verification") { } void help() override { - // TODO // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); + log(" abstract [mode] [options] [selection]\n"); + log("\n"); + log("Perform abstraction of signals within the design. Abstraction replaces a signal\n"); + log("with an unconstrained abstract value that can take an arbitrary concrete value\n"); + log("during formal verification. The mode and options control when a signal should\n"); + log("be abstracted and how it should affect FFs present in the design.\n"); + log("\n"); + log("Modes:"); + log("\n"); + log(" -state\n"); + log(" The selected FFs will be modified to load a new abstract value on every\n"); + log(" active clock edge, async reset or async load. This is independent of any\n"); + log(" clock enable that may be present on the FF cell. Conditional abstraction\n"); + log(" is supported with the -enable/-enabeln options. If present, the condition\n"); + log(" is sampled at the same time as the FF would smaple the correspnding data\n"); + log(" or async-data input whose value will be replaced with an abstract value.\n"); + log("\n"); + log(" The selection can be used to specify which state bits to abstract. For\n"); + log(" each selected wire, any state bits that the wire is driven by will be\n"); + log(" abstracted. For a selected FF cell, all of its state is abstracted.\n"); + log(" Individual bits of a single wire can be abtracted using the -slice and\n"); + log(" -rtlilslice options.\n"); + log("\n"); + log(" -init\n"); + log(" The selected FFs will be modified to have an abstract initial value.\n"); + log(" The -enable/-enablen options are not supported in this mode.\n"); + log(" \n"); + log(" The selection is used in the same way as it is for the -state mode.\n"); + log("\n"); + log(" -value\n"); + log(" The drivers of the selected signals will be replaced with an abstract\n"); + log(" value. In this mode, the abstract value can change at any time and is\n"); + log(" not synchronized to any clock or other signal. Conditional abstraction\n"); + log(" is supported with the -enable/-enablen options. The condition will\n"); + log(" combinationally select between the original driver and the abstract\n"); + log(" value.\n"); + log("\n"); + log(" The selection can be used to specify which output bits of which drivers\n"); + log(" to abtract. For a selected cell, all its output bits will be abstracted.\n"); + log(" For a selected wire, every output bit that is driving the wire will be\n"); + log(" abstracted. Individual bits of a single wire can be abstracted using the\n"); + log(" -slice and -rtlilslice options.\n"); + log("\n"); + log(" -enable \n"); + log(" -enablen \n"); + log(" Perform conditional abstraction with a named single bit wire as\n"); + log(" condition. For -enable the wire is used as an active-high condition and\n"); + log(" for -enablen as an active-low condition. See the description of the\n"); + log(" -state and -value modes for details on how the condition affects the\n"); + log(" abstractions performed by either mode. This option is not supported in\n"); + log(" the -init mode.\n"); + log("\n"); + log(" -slice :\n"); + log(" -slice \n"); + log(" -rtlilslice :\n"); + log(" -rtlilslice \n"); + log(" Limit the abstraction to a slice of a single selected wire. The targeted\n"); + log(" bits of the wire can be given as an inclusive range of indices or as a\n"); + log(" single index. When using the -slice option, the indices are interpreted\n"); + log(" following the source level declaration of the wire. This means the\n"); + log(" -slice option will respect declarations with a non-zero-based index range\n"); + log(" or with reversed bitorder. The -rtlilslice options will always use\n"); + log(" zero-based indexing where index 0 corresponds to the least significant\n"); + log(" bit of the wire.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { log_header(design, "Executing ABSTRACT pass.\n"); From d3a90021ada18dff7dbc95fc8075fd1d1dedf14e Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 18 Feb 2025 13:50:24 +0100 Subject: [PATCH 20/26] abstract: test -state --- tests/various/abstract.ys | 213 +++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 116 deletions(-) diff --git a/tests/various/abstract.ys b/tests/various/abstract.ys index 0892ca1c4..30aa8eb39 100644 --- a/tests/various/abstract.ys +++ b/tests/various/abstract.ys @@ -10,15 +10,59 @@ endmodule EOT proc -# show -prefix before_base +design -save half_clock + +# ----------------------------------------------------------------------------- +# An empty selection causes no change +select -none + +logger -expect log "Abstracted 0 stateful cells" 1 + abstract -state -enablen magic +logger -check-expected + +logger -expect log "Abstracted 0 init bits" 1 + abstract -init +logger -check-expected + +logger -expect log "Abstracted 0 driver ports" 1 + abstract -value -enablen magic +logger -check-expected + +select -clear +# ----------------------------------------------------------------------------- +design -load half_clock +# Basic -state test abstract -state -enablen magic check -assert -# show -prefix after_base +# Connections to dff D input port +select -set conn_to_d t:$dff %x:+[D] t:$dff %d +# The D input port is fed with a mux +select -set mux @conn_to_d %ci t:$mux %i +select -assert-count 1 @mux +# The S input port is fed with the magic wire +select -assert-count 1 @mux %x:+[S] w:magic %i +# The A input port is fed with an anyseq +select -assert-count 1 @mux %x:+[A] %ci t:$anyseq %i +# The B input port is fed with the negated Q +select -set not @mux %x:+[B] %ci t:$not %i +select -assert-count 1 @not %x:+[A] o:Q %i +design -load half_clock +# Same thing, inverted polarity +abstract -state -enable magic +check -assert +select -set conn_to_d t:$dff %x:+[D] t:$dff %d +select -set mux @conn_to_d %ci t:$mux %i +select -assert-count 1 @mux +select -assert-count 1 @mux %x:+[S] w:magic %i +# so we get swapped A and B +select -assert-count 1 @mux %x:+[B] %ci t:$anyseq %i +select -set not @mux %x:+[A] %ci t:$not %i +select -assert-count 1 @not %x:+[A] o:Q %i +# ----------------------------------------------------------------------------- design -reset read_verilog < Date: Tue, 18 Feb 2025 15:20:09 +0100 Subject: [PATCH 21/26] abstract: test -value --- .../{abstract.ys => abstract_state.ys} | 0 tests/various/abstract_value.ys | 74 +++++++++++++++++++ 2 files changed, 74 insertions(+) rename tests/various/{abstract.ys => abstract_state.ys} (100%) create mode 100644 tests/various/abstract_value.ys diff --git a/tests/various/abstract.ys b/tests/various/abstract_state.ys similarity index 100% rename from tests/various/abstract.ys rename to tests/various/abstract_state.ys diff --git a/tests/various/abstract_value.ys b/tests/various/abstract_value.ys new file mode 100644 index 000000000..643d55a65 --- /dev/null +++ b/tests/various/abstract_value.ys @@ -0,0 +1,74 @@ +read_verilog < muxes on inputs and outputs +design -load split_output +select -assert-count 0 t:$mux +abstract -value -enable magic w:* w:magic %d +select -assert-count 3 t:$mux +# All cells selected -> muxes on outputs only +design -load split_output +select -assert-count 0 t:$mux +abstract -value -enable magic t:* +select -assert-count 1 t:$mux +# ----------------------------------------------------------------------------- From 925c617c52f545dfe47a7564e64755a2d5445282 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 18 Feb 2025 17:05:14 +0100 Subject: [PATCH 22/26] abstract: add module input -value abstraction --- passes/cmds/abstract.cc | 45 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 4a97a640c..918330f0f 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -212,7 +212,7 @@ unsigned int abstract_state(Module* mod, EnableLogic enable, const std::vector offsets, IdString port_name, EnableLogic enable) { +bool abstract_value_cell_port(Module* mod, Cell* cell, std::set offsets, IdString port_name, EnableLogic enable) { Wire* to_abstract = mod->addWire(NEW_ID, offsets.size()); SigSpec mux_input; SigSpec mux_output; @@ -234,6 +234,31 @@ bool abstract_value_port(Module* mod, Cell* cell, std::set offsets, IdStrin return true; } +bool abstract_value_mod_port(Module* mod, Wire* wire, std::set offsets, EnableLogic enable) { + Wire* to_abstract = mod->addWire(NEW_ID, wire); + to_abstract->port_input = true; + to_abstract->port_id = wire->port_id; + wire->port_input = false; + wire->port_id = 0; + mod->swap_names(wire, to_abstract); + SigSpec mux_input; + SigSpec mux_output; + SigSpec direct_lhs; + SigSpec direct_rhs; + for (int port_idx = 0; port_idx < wire->width; port_idx++) { + if (offsets.count(port_idx)) { + mux_output.append(SigBit(wire, port_idx)); + mux_input.append(SigBit(to_abstract, port_idx)); + } else { + direct_lhs.append(SigBit(wire, port_idx)); + direct_rhs.append(SigBit(to_abstract, port_idx)); + } + } + mod->connections_.push_back(SigSig(direct_lhs, direct_rhs)); + emit_mux_anyseq(mod, mux_input, mux_output, enable); + return true; +} + unsigned int abstract_value(Module* mod, EnableLogic enable, const std::vector &slices) { SigMap sigmap(mod); dict> selected_reps = gather_selected_reps(mod, slices, sigmap); @@ -254,9 +279,25 @@ unsigned int abstract_value(Module* mod, EnableLogic enable, const std::vector wires_snapshot = mod->wires(); + for (auto wire : wires_snapshot) + if (wire->port_input) { + std::set offsets_to_abstract; + for (auto bit : SigSpec(wire)) + if (selected_reps.count(sigmap(bit))) { + log_debug("Abstracting value for module input port bit %s in module %s due to selections:\n", + log_signal(bit), log_id(mod)); + explain_selections(selected_reps.at(sigmap(bit))); + offsets_to_abstract.insert(bit.offset); + } + if (offsets_to_abstract.empty()) + continue; + + changed += abstract_value_mod_port(mod, wire, offsets_to_abstract, enable); + } return changed; } From 5bd18613bb0fbdb1809fc40be33fa5f04df2707f Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 19 Feb 2025 23:03:43 +0100 Subject: [PATCH 23/26] abstract: test -init --- tests/various/abstract_init.ys | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 tests/various/abstract_init.ys diff --git a/tests/various/abstract_init.ys b/tests/various/abstract_init.ys new file mode 100644 index 000000000..e848ddf0a --- /dev/null +++ b/tests/various/abstract_init.ys @@ -0,0 +1,39 @@ +design -reset +read_verilog < Date: Tue, 25 Feb 2025 00:18:16 +0100 Subject: [PATCH 24/26] abstract: test -slice for all modes, -rtlilslice for -init --- tests/various/abstract_init.ys | 76 +++++++++++++++++++++++++++++++++ tests/various/abstract_state.ys | 40 ++++++++++++++--- tests/various/abstract_value.ys | 25 ++++++++++- 3 files changed, 133 insertions(+), 8 deletions(-) diff --git a/tests/various/abstract_init.ys b/tests/various/abstract_init.ys index e848ddf0a..06f29153c 100644 --- a/tests/various/abstract_init.ys +++ b/tests/various/abstract_init.ys @@ -37,3 +37,79 @@ select -assert-count 1 w:Q a:init=2'b01 %i abstract -init w:QQQ check -assert select -assert-count 1 w:Q a:init=2'b0x %i + +design -reset +read_verilog < Date: Tue, 25 Feb 2025 00:19:15 +0100 Subject: [PATCH 25/26] abstract: typo? --- passes/cmds/abstract.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 918330f0f..1e67dcd88 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -57,7 +57,7 @@ struct Slice { } std::string to_string() const { - const char *option = indices == RtlilSlice ? "-rawslice" : "-slice"; + const char *option = indices == RtlilSlice ? "-rtlilslice" : "-slice"; if (first == last) return stringf("%s %d", option, first); else From 3f60a2cc67a27db7592e6029f5acdda6749f6ed4 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 25 Feb 2025 00:22:14 +0100 Subject: [PATCH 26/26] abstract: test -slice from:to for -init --- tests/various/abstract_init.ys | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/various/abstract_init.ys b/tests/various/abstract_init.ys index 06f29153c..2e0cf2e64 100644 --- a/tests/various/abstract_init.ys +++ b/tests/various/abstract_init.ys @@ -52,10 +52,16 @@ EOT proc opt_expr opt_dff +design -save basic select -assert-count 1 w:Q a:init=2'b01 %i abstract -init -slice 0 w:Q check -assert select -assert-count 1 w:Q a:init=2'b0x %i +design -load basic +select -assert-count 1 w:Q a:init=2'b01 %i +abstract -init -slice 0:1 w:Q +check -assert +select -assert-count 0 w:Q a:init %i design -reset read_verilog <