From bdc978380c78eb8fd9d8d146aca7059bc2cb6fe7 Mon Sep 17 00:00:00 2001 From: George Rennie Date: Wed, 20 Nov 2024 13:52:25 +0100 Subject: [PATCH 01/10] kernel: define $barrier cell * This acts as an optimization barrier, limiting the rewriting allowed * For now this forbids all optimization, but attributes can be added to opt in to specific optimizations as required --- kernel/cellaigs.cc | 2 +- kernel/celledges.cc | 4 ++-- kernel/celltypes.h | 2 ++ kernel/qcsat.cc | 2 +- kernel/rtlil.cc | 9 +++++---- kernel/rtlil.h | 18 ++++++++++-------- kernel/satgen.cc | 6 +++--- techlibs/common/simlib.v | 23 +++++++++++++++++++++++ 8 files changed, 47 insertions(+), 19 deletions(-) diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc index de0a49394..f79b9642d 100644 --- a/kernel/cellaigs.cc +++ b/kernel/cellaigs.cc @@ -290,7 +290,7 @@ Aig::Aig(Cell *cell) } } - if (cell->type.in(ID($not), ID($_NOT_), ID($pos), ID($buf), ID($_BUF_))) + if (cell->type.in(ID($not), ID($_NOT_), ID($pos), ID($buf), ID($barrier), ID($_BUF_))) { for (int i = 0; i < GetSize(cell->getPort(ID::Y)); i++) { int A = mk.inport(ID::A, i); diff --git a/kernel/celledges.cc b/kernel/celledges.cc index bad7124d9..588184dd4 100644 --- a/kernel/celledges.cc +++ b/kernel/celledges.cc @@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN void bitwise_unary_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) { - bool is_signed = (cell->type != ID($buf)) && cell->getParam(ID::A_SIGNED).as_bool(); + bool is_signed = !cell->type.in(ID($buf), ID($barrier)) && cell->getParam(ID::A_SIGNED).as_bool(); int a_width = GetSize(cell->getPort(ID::A)); int y_width = GetSize(cell->getPort(ID::Y)); @@ -392,7 +392,7 @@ PRIVATE_NAMESPACE_END bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell) { - if (cell->type.in(ID($not), ID($pos), ID($buf))) { + if (cell->type.in(ID($not), ID($pos), ID($buf), ID($barrier))) { bitwise_unary_op(this, cell); return true; } diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 3167a9add..80512966e 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -87,6 +87,8 @@ struct CellTypes { setup_internals_eval(); + setup_type(ID($barrier), {ID::A}, {ID::Y}); + setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y}, true); setup_type(ID($assert), {ID::A, ID::EN}, pool(), true); diff --git a/kernel/qcsat.cc b/kernel/qcsat.cc index c9d00efa1..630368bed 100644 --- a/kernel/qcsat.cc +++ b/kernel/qcsat.cc @@ -77,7 +77,7 @@ void QuickConeSat::prepare() int QuickConeSat::cell_complexity(RTLIL::Cell *cell) { - if (cell->type.in(ID($concat), ID($slice), ID($pos), ID($buf), ID($_BUF_))) + if (cell->type.in(ID($concat), ID($slice), ID($pos), ID($buf), ID($barrier), ID($_BUF_))) return 0; if (cell->type.in(ID($not), ID($and), ID($or), ID($xor), ID($xnor), ID($reduce_and), ID($reduce_or), ID($reduce_xor), diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index e9b648b04..7104e6f1a 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1340,7 +1340,7 @@ namespace { cell->type.begins_with("$verific$") || cell->type.begins_with("$array:") || cell->type.begins_with("$extern:")) return; - if (cell->type == ID($buf)) { + if (cell->type.in(ID($buf), ID($barrier))) { port(ID::A, param(ID::WIDTH)); port(ID::Y, param(ID::WIDTH)); check_expected(); @@ -2746,7 +2746,8 @@ DEF_METHOD(LogicNot, 1, ID($logic_not)) add ## _func(name, sig_a, sig_y, is_signed, src); \ return sig_y; \ } -DEF_METHOD(Buf, sig_a.size(), ID($buf)) +DEF_METHOD(Buf, sig_a.size(), ID($buf)) +DEF_METHOD(Barrier, sig_a.size(), ID($barrier)) #undef DEF_METHOD #define DEF_METHOD(_func, _y_size, _type) \ @@ -4048,9 +4049,9 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) type.begins_with("$verific$") || type.begins_with("$array:") || type.begins_with("$extern:")) return; - if (type == ID($buf) || type == ID($mux) || type == ID($pmux) || type == ID($bmux)) { + if (type.in(ID($buf), ID($barrier), ID($mux), ID($pmux), ID($bmux))) { parameters[ID::WIDTH] = GetSize(connections_[ID::Y]); - if (type != ID($buf) && type != ID($mux)) + if (!type.in(ID($buf), ID($barrier), ID($mux))) parameters[ID::S_WIDTH] = GetSize(connections_[ID::S]); check(); return; diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 0f3984ab8..4f0afa1be 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -1371,10 +1371,11 @@ public: // The add* methods create a cell and return the created cell. All signals must exist in advance. - RTLIL::Cell* addNot (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); - RTLIL::Cell* addPos (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); - RTLIL::Cell* addBuf (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); - RTLIL::Cell* addNeg (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addNot (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addPos (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addBuf (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addBarrier (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addNeg (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); RTLIL::Cell* addAnd (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); RTLIL::Cell* addOr (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); @@ -1506,10 +1507,11 @@ public: // The methods without the add* prefix create a cell and an output signal. They return the newly created output signal. - RTLIL::SigSpec Not (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); - RTLIL::SigSpec Pos (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); - RTLIL::SigSpec Buf (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); - RTLIL::SigSpec Neg (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Not (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Pos (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Buf (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Barrier (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Neg (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); RTLIL::SigSpec And (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = ""); RTLIL::SigSpec Or (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = ""); diff --git a/kernel/satgen.cc b/kernel/satgen.cc index ba6d664db..dc41ae386 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -430,7 +430,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) return true; } - if (cell->type.in(ID($pos), ID($buf), ID($neg))) + if (cell->type.in(ID($pos), ID($buf), ID($barrier), ID($neg))) { std::vector a = importDefSigSpec(cell->getPort(ID::A), timestep); std::vector y = importDefSigSpec(cell->getPort(ID::Y), timestep); @@ -438,7 +438,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector yy = model_undef ? ez->vec_var(y.size()) : y; - if (cell->type.in(ID($pos), ID($buf))) { + if (cell->type.in(ID($pos), ID($buf), ID($barrier))) { ez->assume(ez->vec_eq(a, yy)); } else { std::vector zero(a.size(), ez->CONST_FALSE); @@ -451,7 +451,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep); extendSignalWidthUnary(undef_a, undef_y, cell); - if (cell->type.in(ID($pos), ID($buf))) { + if (cell->type.in(ID($pos), ID($buf), ID($barrier))) { ez->assume(ez->vec_eq(undef_a, undef_y)); } else { int undef_any_a = ez->expression(ezSAT::OpOr, undef_a); diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index df9e1feb1..4ad2558bc 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -108,6 +108,29 @@ endmodule // -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $barrier (A, Y) +//* group unary +//- +//- A coarse-grain buffer cell type that acts as a barrier for optimizations. +//- Optimization passes are forbidden from rewriting patterns that include +//- this cell (by merging, constant propagation etc) with the exception of +//- opt_clean that can remove it if the output is unused. +//- +module \$barrier (A, Y); + +parameter WIDTH = 0; + +input [WIDTH-1:0] A; +output [WIDTH-1:0] Y; + +assign Y = A; + +endmodule + +// -------------------------------------------------------- + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- //- $neg (A, Y) From 70646da6bbc7d13aa505de3c073d4e9520dc5871 Mon Sep 17 00:00:00 2001 From: George Rennie Date: Wed, 20 Nov 2024 13:54:54 +0100 Subject: [PATCH 02/10] opt_merge: don't merge $barrier cells --- passes/opt/opt_merge.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index eb3aa462e..03b868e4c 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -245,6 +245,7 @@ struct OptMergeWorker ct.cell_types.erase(ID($anyconst)); ct.cell_types.erase(ID($allseq)); ct.cell_types.erase(ID($allconst)); + ct.cell_types.erase(ID($barrier)); log("Finding identical cells in module `%s'.\n", module->name.c_str()); assign_map.set(module); From 88816ccc45bc15f9a1e826e07d266825699dcd9a Mon Sep 17 00:00:00 2001 From: George Rennie Date: Wed, 20 Nov 2024 13:56:46 +0100 Subject: [PATCH 03/10] write_aiger2: support $barrier --- backends/aiger2/aiger.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index a230cda4f..3a5509dd6 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -28,8 +28,8 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -#define BITWISE_OPS ID($buf), ID($not), ID($mux), ID($and), ID($or), ID($xor), ID($xnor), ID($fa), \ - ID($bwmux) +#define BITWISE_OPS ID($buf), ID($barrier), ID($not), ID($mux), ID($and), ID($or), ID($xor), ID($xnor), \ + ID($fa), ID($bwmux) #define REDUCE_OPS ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool) @@ -331,7 +331,7 @@ struct Index { a = CFALSE; } - if (cell->type.in(ID($buf), ID($pos), ID($_BUF_))) { + if (cell->type.in(ID($buf), ID($barrier), ID($pos), ID($_BUF_))) { return a; } else if (cell->type.in(ID($not), ID($_NOT_))) { return NOT(a); From 6b3901e43575df50a152093f522d6803aa9f861c Mon Sep 17 00:00:00 2001 From: George Rennie Date: Wed, 20 Nov 2024 13:57:29 +0100 Subject: [PATCH 04/10] write_btor: support $barrier, $buf and $_BUF_ --- backends/btor/btor.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index 43d189679..63efcffe7 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -508,7 +508,7 @@ struct BtorWorker goto okay; } - if (cell->type.in(ID($not), ID($neg), ID($_NOT_), ID($pos))) + if (cell->type.in(ID($not), ID($neg), ID($_NOT_), ID($pos), ID($buf), ID($_BUF_), ID($barrier))) { string btor_op; if (cell->type.in(ID($not), ID($_NOT_))) btor_op = "not"; @@ -520,9 +520,9 @@ struct BtorWorker int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed); SigSpec sig = sigmap(cell->getPort(ID::Y)); - // the $pos cell just passes through, all other cells need an actual operation applied + // buffer cells just pass through, all other cells need an actual operation applied int nid = nid_a; - if (cell->type != ID($pos)) + if (!cell->type.in(ID($pos), ID($buf), ID($_BUF_), ID($barrier))) { log_assert(!btor_op.empty()); int sid = get_bv_sid(width); From ab293d667eb46c9e560cd1adda8d8778888e46b2 Mon Sep 17 00:00:00 2001 From: George Rennie Date: Wed, 20 Nov 2024 13:58:03 +0100 Subject: [PATCH 05/10] write_firrtl: support $buf and $barrier --- backends/firrtl/firrtl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index eac0c9719..8125ff481 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -966,7 +966,7 @@ struct FirrtlWorker register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } - if (cell->type == ID($pos)) { + if (cell->type.in(ID($pos), ID($buf), ID($barrier))) { // assign y = a; // printCell(cell); string a_expr = make_expr(cell->getPort(ID::A)); From cbe86afd5c91ea98abc46959b854b51c67c2b78f Mon Sep 17 00:00:00 2001 From: George Rennie Date: Wed, 20 Nov 2024 13:58:35 +0100 Subject: [PATCH 06/10] write_smt2: support $buf and $barrier --- backends/smt2/smt2.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index a6e1965ba..b9e7bc27a 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -677,7 +677,7 @@ struct Smt2Worker if (cell->type == ID($eqx)) return export_bvop(cell, "(= A B)", 'b'); if (cell->type == ID($not)) return export_bvop(cell, "(bvnot A)"); - if (cell->type == ID($pos)) return export_bvop(cell, "A"); + if (cell->type.in(ID($pos), ID($buf), ID($barrier))) return export_bvop(cell, "A"); if (cell->type == ID($neg)) return export_bvop(cell, "(bvneg A)"); if (cell->type == ID($add)) return export_bvop(cell, "(bvadd A B)"); From 73ba5da10f9f754f6aabe0bf8cbb96f2e54df73a Mon Sep 17 00:00:00 2001 From: George Rennie Date: Wed, 20 Nov 2024 14:00:18 +0100 Subject: [PATCH 07/10] write_verilog: support $buf and $barrier --- backends/verilog/verilog_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index a8dd356bc..d2b3eba77 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1071,7 +1071,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - if (cell->type == ID($_BUF_)) { + if (cell->type.in(ID($buf), ID($_BUF_), ID($barrier))) { f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); From c352f71fc0a692df8223b282e88835cf628f1eb7 Mon Sep 17 00:00:00 2001 From: George Rennie Date: Wed, 20 Nov 2024 14:00:43 +0100 Subject: [PATCH 08/10] flatten: add -barriers flag * This uses $barrier optimization barriers to connect wires into the flattened module instead of connections --- passes/techmap/flatten.cc | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/passes/techmap/flatten.cc b/passes/techmap/flatten.cc index ea5855a09..ec768b068 100644 --- a/passes/techmap/flatten.cc +++ b/passes/techmap/flatten.cc @@ -60,6 +60,7 @@ struct FlattenWorker bool ignore_wb = false; bool create_scopeinfo = true; bool create_scopename = false; + bool barriers = false; template void map_attributes(RTLIL::Cell *cell, T *object, IdString orig_object_name) @@ -246,7 +247,27 @@ struct FlattenWorker log_error("Cell port %s.%s.%s is driving constant bits: %s <= %s\n", log_id(module), log_id(cell), log_id(port_it.first), log_signal(new_conn.first), log_signal(new_conn.second)); - module->connect(new_conn); + if (barriers) { + // Drive public output wires with barriers and the rest with + // connections + RTLIL::SigSig skip_conn, barrier_conn; + + for (int i = 0; i < GetSize(new_conn.first); i++) { + const auto lhs = new_conn.first[i], rhs = new_conn.second[i]; + auto& sigsig = !lhs.is_wire() || !lhs.wire->name.isPublic() ? skip_conn : barrier_conn; + sigsig.first.append(lhs); + sigsig.second.append(rhs); + } + + if (!skip_conn.first.empty()) + module->connect(skip_conn); + + if (!barrier_conn.first.empty()) + module->addBarrier(NEW_ID, barrier_conn.second, barrier_conn.first); + } else { + module->connect(new_conn); + } + sigmap.add(new_conn.first, new_conn.second); } @@ -345,6 +366,10 @@ struct FlattenPass : public Pass { log(" with a public name the enclosing scope can be found via their\n"); log(" 'hdlname' attribute.\n"); log("\n"); + log(" -barriers\n"); + log(" Use $barrier cells to connect flattened modules to their surrounding\n"); + log(" scope instead of connections for public wires.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { @@ -367,6 +392,10 @@ struct FlattenPass : public Pass { worker.create_scopename = true; continue; } + if (args[argidx] == "-barriers") { + worker.barriers = true; + continue; + } break; } extra_args(args, argidx, design); From c6e8fb2432da5252d510d801defc9bd6ff4a2f23 Mon Sep 17 00:00:00 2001 From: George Rennie Date: Wed, 20 Nov 2024 17:16:37 +0100 Subject: [PATCH 09/10] optbarriers: add command to add/remove optimization barriers * This can optionally ignore rewriting the outputs of cells or processes * This by default rewrites drivers of wires with public names but can also optionally rewrite drivers of wires with private names * A -remove flag allows cleaning up the design by replacing barriers with connections --- passes/cmds/Makefile.inc | 1 + passes/cmds/optbarriers.cc | 228 +++++++++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 passes/cmds/optbarriers.cc diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 95c799f8f..2cbcd8263 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -51,3 +51,4 @@ OBJS += passes/cmds/future.o OBJS += passes/cmds/box_derive.o OBJS += passes/cmds/example_dt.o OBJS += passes/cmds/portarcs.o +OBJS += passes/cmds/optbarriers.o diff --git a/passes/cmds/optbarriers.cc b/passes/cmds/optbarriers.cc new file mode 100644 index 000000000..a899bae49 --- /dev/null +++ b/passes/cmds/optbarriers.cc @@ -0,0 +1,228 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 George Rennie + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +// Standard visitor helper +template +struct overloaded : Ts... { using Ts::operator()...; }; +template +overloaded(Ts...) -> overloaded; + +struct OptBarriersPass : public Pass { + OptBarriersPass() : Pass("optbarriers", "insert optimization barriers") {} + + void help() override { + log("\n"); + log(" optbarriers [options] [selection]\n"); + log("\n"); + log("Insert optimization barriers to drivers of selected public wires.\n"); + log("\n"); + log("\n"); + log(" -nocells\n"); + log(" don't add optimization barriers to the outputs of cells\n"); + log("\n"); + log(" -noprocs\n"); + log(" don't add optimization barriers to the outputs of processes\n"); + log("\n"); + log(" -private\n"); + log(" also add optimization barriers to private wires\n"); + log("\n"); + log(" -remove\n"); + log(" replace selected optimization barriers with connections\n"); + log("\n"); + } + + void execute(std::vector args, RTLIL::Design *design) override { + log_header(design, "Executing OPTBARRIERS pass (insert optimization barriers).\n"); + + bool nocells_mode = false; + bool noprocs_mode = false; + bool private_mode = false; + bool remove_mode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-nocells") { + nocells_mode = true; + continue; + } + if (arg == "-noprocs") { + noprocs_mode = true; + continue; + } + if (arg == "-private") { + private_mode = true; + continue; + } + if (arg == "-remove") { + remove_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (remove_mode) { + log("Replacing optimization barriers with connections.\n"); + remove_barriers(design); + return; + } + + for (auto* module : design->selected_modules()) { + // We can't just sigmap and iterate through wires for rewriting as + // we want to maintain the structure in connections, and sigmap + // will just return a canonical wire which does not have to be one + // that is directly driving the wire. Therefore for each type of + // object that could be driving the wires (cells, processes, + // connections) we rewrite the sigspecs. + + // Keep track of which wires we have allocated new wires for + dict new_wires; + // Keep track of bit pairs we need to construct barriers for from + // Y to A + dict new_barriers; + + // Skip constants, unselected wires and private wires when not in + // private mode. This works for SigChunk or SigBit input. + const auto skip = [&](const auto& chunk) { + if (!chunk.is_wire()) + return true; + + if (!design->selected(module, chunk.wire)) + return true; + + if (!private_mode && !chunk.wire->name.isPublic()) + return true; + + return false; + }; + + const auto rewrite_sigspec = [&](const SigSpec& sig) { + RTLIL::SigSpec new_output; + for (const auto& chunk : sig.chunks()) { + if (skip(chunk)) { + new_output.append(chunk); + continue; + } + + // Add a wire to drive if one does not already exist + auto* new_wire = new_wires.at(chunk.wire, nullptr); + if (!new_wire) { + new_wire = module->addWire(NEW_ID, GetSize(chunk.wire)); + new_wires.emplace(chunk.wire, new_wire); + } + + RTLIL::SigChunk new_chunk = chunk; + new_chunk.wire = new_wire; + + // Rewrite output to drive new wire, and schedule adding + // barrier bits from new wire to original + new_output.append(new_chunk); + for (int i = 0; i < GetSize(chunk); i++) + new_barriers.emplace(chunk[i], new_chunk[i]); + } + + return new_output; + }; + + // Rewrite cell outputs + if (!nocells_mode) + for (auto* cell : module->cells()) + if (cell->type != ID($barrier)) + for (const auto& [name, sig] : cell->connections()) + if (cell->output(name)) + cell->setPort(name, rewrite_sigspec(sig)); + + // Rewrite connections in processes + if (!noprocs_mode) { + const auto proc_rewriter = overloaded{ + // Don't do anything for input sigspecs + [&](const SigSpec&) {}, + // Rewrite connections to drive barrier if needed + [&](SigSpec& lhs, const SigSpec&) { + lhs = rewrite_sigspec(lhs); + } + }; + + for (auto& proc : module->processes) + proc.second->rewrite_sigspecs2(proc_rewriter); + } + + // Add all the scheduled barriers. To minimize the number of cells, + // first construct a sigspec of all bits, then sort and unify before + // creating barriers + SigSpec barrier_y; + for (const auto&[y_bit, _] : new_barriers) + barrier_y.append(y_bit); + barrier_y.sort_and_unify(); + + for (const auto& sig_y : barrier_y.chunks()) { + log_assert(sig_y.is_wire()); + SigSpec sig_a; + for (int i = 0; i < GetSize(sig_y); i++) + sig_a.append(new_barriers[sig_y[i]]); + module->addBarrier(NEW_ID, sig_a, sig_y); + } + + // Rewrite connections + std::vector new_connections; + for (const auto& conn : module->connections()) { + RTLIL::SigSig skip_conn, barrier_conn; + + for (int i = 0; i < GetSize(conn.first); i++) { + auto& sigsig = skip(conn.first[i]) ? skip_conn : barrier_conn; + sigsig.first.append(conn.first[i]); + sigsig.second.append(conn.second[i]); + } + + if (!skip_conn.first.empty()) + new_connections.emplace_back(std::move(skip_conn)); + + if (!barrier_conn.first.empty()) + module->addBarrier(NEW_ID, barrier_conn.second, barrier_conn.first); + } + module->new_connections(new_connections); + } + } + + void remove_barriers(RTLIL::Design* design) { + for (auto* module : design->selected_modules()) { + std::vector barriers; + + for (auto* cell : module->selected_cells()) + if (cell->type == ID($barrier)) + barriers.emplace_back(cell); + + for (auto* cell : barriers) { + const auto lhs = cell->getPort(ID::Y), rhs = cell->getPort(ID::A); + module->connect(lhs, rhs); + module->remove(cell); + } + } + } + +} OptBarriersPass; + +PRIVATE_NAMESPACE_END From 33d5138673fe3d1aa9ea0f929c208064874373f4 Mon Sep 17 00:00:00 2001 From: George Rennie Date: Wed, 20 Nov 2024 17:40:18 +0100 Subject: [PATCH 10/10] prep: add -barriers flag * This uses optbarriers and flatten -barriers to insert barriers on public wires in the design, limiting the optimization that can affect them --- techlibs/common/prep.cc | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/techlibs/common/prep.cc b/techlibs/common/prep.cc index e9176304d..92bac2308 100644 --- a/techlibs/common/prep.cc +++ b/techlibs/common/prep.cc @@ -67,6 +67,11 @@ struct PrepPass : public ScriptPass log(" -nokeepdc\n"); log(" do not call opt_* with -keepdc\n"); log("\n"); + log(" -barriers\n"); + log(" add optimization barriers to all public wires to preserve their structure.\n"); + log(" this limits the optimizations that can be applied to the design to only\n"); + log(" those involving private wires.\n"); + log("\n"); log(" -run [:]\n"); log(" only run the commands between the labels (see below). an empty\n"); log(" from label is synonymous to 'begin', and empty to label is\n"); @@ -79,7 +84,7 @@ struct PrepPass : public ScriptPass } string top_module, fsm_opts; - bool autotop, flatten, ifxmode, memxmode, nomemmode, nokeepdc, rdff; + bool autotop, flatten, ifxmode, memxmode, nomemmode, nokeepdc, rdff, barriers; void clear_flags() override { @@ -92,6 +97,7 @@ struct PrepPass : public ScriptPass nomemmode = false; nokeepdc = false; rdff = false; + barriers = false; } void execute(std::vector args, RTLIL::Design *design) override @@ -148,6 +154,10 @@ struct PrepPass : public ScriptPass nokeepdc = true; continue; } + if (args[argidx] == "-barriers") { + barriers = true; + continue; + } break; } extra_args(args, argidx, design); @@ -183,12 +193,16 @@ struct PrepPass : public ScriptPass if (check_label("coarse")) { + if (help_mode || barriers) + run("optbarriers", "(if -barriers)"); if (help_mode) run("proc [-ifx]"); else run(ifxmode ? "proc -ifx" : "proc"); - if (help_mode || flatten) - run("flatten", "(if -flatten)"); + if (help_mode) + run("flatten [-barriers]", "(if -flatten)"); + else if (flatten) + run(barriers ? "flatten -barriers" : "flatten"); run("future"); run(nokeepdc ? "opt_expr" : "opt_expr -keepdc"); run("opt_clean");