From 1bf8c2b823992569830cf0d248cb12bfccd5f384 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 19 Apr 2019 14:03:05 +0200 Subject: [PATCH 01/10] Import initial pmux2shiftx from eddieh Signed-off-by: Clifford Wolf --- passes/opt/Makefile.inc | 1 + passes/opt/pmux2shiftx.cc | 81 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 passes/opt/pmux2shiftx.cc diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index c3e0a2a40..337fee9e4 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -13,5 +13,6 @@ OBJS += passes/opt/wreduce.o OBJS += passes/opt/opt_demorgan.o OBJS += passes/opt/rmports.o OBJS += passes/opt/opt_lut.o +OBJS += passes/opt/pmux2shiftx.o endif diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc new file mode 100644 index 000000000..6ffc27a4c --- /dev/null +++ b/passes/opt/pmux2shiftx.cc @@ -0,0 +1,81 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 + +struct Pmux2ShiftxPass : public Pass { + Pmux2ShiftxPass() : Pass("pmux2shiftx", "transform $pmux cells to $shiftx cells") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" pmux2shiftx [selection]\n"); + log("\n"); + log("This pass transforms $pmux cells to $shiftx cells.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing PMUX2SHIFTX pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + for (auto cell : module->selected_cells()) + { + if (cell->type != "$pmux") + continue; + + // Create a new encoder, out of a $pmux, that takes + // the existing pmux's 'S' input and transforms it + // back into a binary value + RTLIL::SigSpec shiftx_a; + RTLIL::SigSpec pmux_s; + + int s_width = cell->getParam("\\S_WIDTH").as_int(); + if (!cell->getPort("\\A").is_fully_undef()) { + ++s_width; + shiftx_a.append(cell->getPort("\\A")); + pmux_s.append(module->Not(NEW_ID, module->ReduceOr(NEW_ID, cell->getPort("\\S")))); + } + const int clog2width = ceil(log2(s_width)); + + RTLIL::SigSpec pmux_b; + for (int i = s_width-1; i >= 0; i--) + pmux_b.append(RTLIL::Const(i, clog2width)); + shiftx_a.append(cell->getPort("\\B")); + pmux_s.append(cell->getPort("\\S")); + + RTLIL::SigSpec pmux_y = module->addWire(NEW_ID, clog2width); + module->addPmux(NEW_ID, RTLIL::Const(RTLIL::Sx, clog2width), pmux_b, pmux_s, pmux_y); + module->addShiftx(NEW_ID, shiftx_a, pmux_y, cell->getPort("\\Y")); + module->remove(cell); + } + } +} Pmux2ShiftxPass; + +PRIVATE_NAMESPACE_END From 481f0015be48e7662f6fc55ca8bbb46c51829ccb Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 19 Apr 2019 18:10:12 +0200 Subject: [PATCH 02/10] Complete rewrite of pmux2shiftx Signed-off-by: Clifford Wolf --- passes/opt/pmux2shiftx.cc | 284 +++++++++++++++++++++++++++++++++++--- 1 file changed, 261 insertions(+), 23 deletions(-) diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc index 6ffc27a4c..8ea70fe84 100644 --- a/passes/opt/pmux2shiftx.cc +++ b/passes/opt/pmux2shiftx.cc @@ -45,35 +45,273 @@ struct Pmux2ShiftxPass : public Pass { extra_args(args, argidx, design); for (auto module : design->selected_modules()) - for (auto cell : module->selected_cells()) { - if (cell->type != "$pmux") - continue; + SigMap sigmap(module); - // Create a new encoder, out of a $pmux, that takes - // the existing pmux's 'S' input and transforms it - // back into a binary value - RTLIL::SigSpec shiftx_a; - RTLIL::SigSpec pmux_s; + dict> eqdb; - int s_width = cell->getParam("\\S_WIDTH").as_int(); - if (!cell->getPort("\\A").is_fully_undef()) { - ++s_width; - shiftx_a.append(cell->getPort("\\A")); - pmux_s.append(module->Not(NEW_ID, module->ReduceOr(NEW_ID, cell->getPort("\\S")))); + for (auto cell : module->selected_cells()) + { + if (cell->type == "$eq") + { + dict bits; + + SigSpec A = sigmap(cell->getPort("\\A")); + SigSpec B = sigmap(cell->getPort("\\B")); + + int a_width = cell->getParam("\\A_WIDTH").as_int(); + int b_width = cell->getParam("\\B_WIDTH").as_int(); + + if (a_width < b_width) { + bool a_signed = cell->getParam("\\A_SIGNED").as_int(); + A.extend_u0(b_width, a_signed); + } + + if (b_width < a_width) { + bool b_signed = cell->getParam("\\B_SIGNED").as_int(); + B.extend_u0(a_width, b_signed); + } + + for (int i = 0; i < GetSize(A); i++) { + SigBit a_bit = A[i], b_bit = B[i]; + if (b_bit.wire && !a_bit.wire) { + std::swap(a_bit, b_bit); + } + if (!a_bit.wire || b_bit.wire) + goto next_cell; + if (bits.count(a_bit)) + goto next_cell; + bits[a_bit] = b_bit.data; + } + + if (GetSize(bits) > 20) + goto next_cell; + + bits.sort(); + pair entry; + + for (auto it : bits) { + entry.first.append_bit(it.first); + entry.second.bits.push_back(it.second); + } + + eqdb[sigmap(cell->getPort("\\Y")[0])] = entry; + goto next_cell; + } + + if (cell->type == "$logic_not") + { + dict bits; + + SigSpec A = sigmap(cell->getPort("\\A")); + + for (int i = 0; i < GetSize(A); i++) + bits[A[i]] = State::S0; + + bits.sort(); + pair entry; + + for (auto it : bits) { + entry.first.append_bit(it.first); + entry.second.bits.push_back(it.second); + } + + eqdb[sigmap(cell->getPort("\\Y")[0])] = entry; + goto next_cell; + } + next_cell:; } - const int clog2width = ceil(log2(s_width)); - RTLIL::SigSpec pmux_b; - for (int i = s_width-1; i >= 0; i--) - pmux_b.append(RTLIL::Const(i, clog2width)); - shiftx_a.append(cell->getPort("\\B")); - pmux_s.append(cell->getPort("\\S")); + for (auto cell : module->selected_cells()) + { + if (cell->type != "$pmux") + continue; - RTLIL::SigSpec pmux_y = module->addWire(NEW_ID, clog2width); - module->addPmux(NEW_ID, RTLIL::Const(RTLIL::Sx, clog2width), pmux_b, pmux_s, pmux_y); - module->addShiftx(NEW_ID, shiftx_a, pmux_y, cell->getPort("\\Y")); - module->remove(cell); + string src = cell->get_src_attribute(); + int width = cell->getParam("\\WIDTH").as_int(); + int width_bits = ceil_log2(width); + int extwidth = width; + + while (extwidth & (extwidth-1)) + extwidth++; + + dict> seldb; + + SigSpec S = sigmap(cell->getPort("\\S")); + for (int i = 0; i < GetSize(S); i++) + { + if (!eqdb.count(S[i])) + continue; + + auto &entry = eqdb.at(S[i]); + seldb[entry.first].insert(i); + } + + if (seldb.empty()) + continue; + + log("Inspecting $pmux cell %s/%s.\n", log_id(module), log_id(cell)); + log(" data width: %d (next power-of-2 = %d, log2 = %d)\n", width, extwidth, width_bits); + + SigSpec updated_S = cell->getPort("\\S"); + SigSpec updated_B = cell->getPort("\\B"); + + #if 1 + for (auto &it : seldb) { + string msg = stringf("seldb: %s ->", log_signal(it.first)); + for (int i : it.second) + msg += stringf(" %d(%s)", i, log_signal(eqdb.at(S[i]).second)); + log(" %s\n", msg.c_str()); + } + #endif + + while (!seldb.empty()) + { + // pick the largest entry in seldb + SigSpec sig = seldb.begin()->first; + for (auto &it : seldb) { + if (GetSize(sig) < GetSize(it.first)) + sig = it.first; + else if (GetSize(seldb.at(sig)) < GetSize(it.second)) + sig = it.first; + } + + log(" checking ctrl signal %s\n", log_signal(sig)); + + // find the relevant choices + dict choices; + vector onescnt(GetSize(sig)); + for (int i : seldb.at(sig)) { + Const val = eqdb.at(S[i]).second; + choices[val] = i; + for (int k = 0; k < GetSize(val); k++) + if (val[k] == State::S1) + onescnt[k] |= 1; + else + onescnt[k] |= 2; + } + + // TBD: also find choices that are using signals that are subsets of the bits in "sig" + + // find the best permutation + vector> perm(GetSize(sig)); + for (int i = 0; i < GetSize(onescnt); i++) + perm[i] = make_pair(onescnt[i], i); + // TBD: this is not the best permutation + std::sort(perm.rbegin(), perm.rend()); + + // permutated sig + Const perm_xormask(State::S0, GetSize(sig)); + SigSpec perm_sig(State::S0, GetSize(sig)); + for (int i = 0; i < GetSize(sig); i++) { + if (perm[i].first == 1) + perm_xormask[i] = State::S1; + perm_sig[i] = sig[perm[i].second]; + } + + log(" best permutation: %s\n", log_signal(perm_sig)); + log(" best xor mask: %s\n", log_signal(perm_xormask)); + + // permutated choices + int min_choice = 1 << 30; + int max_choice = -1; + dict perm_choices; + + for (auto &it : choices) + { + Const &old_c = it.first; + Const new_c(State::S0, GetSize(old_c)); + + for (int i = 0; i < GetSize(old_c); i++) + new_c[i] = old_c[perm[i].second]; + + Const new_c_before_xor = new_c; + new_c = const_xor(new_c, perm_xormask, false, false, GetSize(new_c)); + + perm_choices[new_c] = it.second; + + min_choice = std::min(min_choice, new_c.as_int()); + max_choice = std::max(max_choice, new_c.as_int()); + + log(" %s -> %s -> %s\n", log_signal(old_c), log_signal(new_c_before_xor), log_signal(new_c)); + } + + log(" choices: %d\n", GetSize(choices)); + log(" min choice: %d\n", min_choice); + log(" max choice: %d\n", max_choice); + log(" range density: %d%%\n", 100*GetSize(choices)/(max_choice-min_choice+1)); + log(" absolute density: %d%%\n", 100*GetSize(choices)/(max_choice+1)); + + bool full_case = (min_choice == 0) && (max_choice == (1 << GetSize(sig))-1) && (max_choice+1 == GetSize(choices)); + log(" full case: %s\n", full_case ? "true" : "false"); + + // use arithmetic offset if density is less than 30% + Const offset(State::S0, GetSize(sig)); + if (3*GetSize(choices) < max_choice && 3*GetSize(choices) >= (max_choice-min_choice)) + { + log(" using offset method.\n"); + + offset = Const(min_choice, GetSize(sig)); + min_choice -= offset.as_int(); + max_choice -= offset.as_int(); + + dict new_perm_choices; + for (auto &it : perm_choices) + new_perm_choices[const_sub(it.first, offset, false, false, GetSize(sig))] = it.second; + perm_choices.swap(new_perm_choices); + } + + // ignore cases with a absolute density of less than 30% + if (3*GetSize(choices) < max_choice) { + log(" insufficient density.\n"); + seldb.erase(sig); + continue; + } + + // creat cmp signal + SigSpec cmp = perm_sig; + if (perm_xormask.as_bool()) + cmp = module->Xor(NEW_ID, cmp, perm_xormask, false, src); + if (offset.as_bool()) + cmp = module->Sub(NEW_ID, cmp, offset, false, src); + + // create enable signal + SigBit en = State::S1; + if (!full_case) { + Const enable_mask(State::S0, max_choice+1); + for (auto &it : perm_choices) + enable_mask[it.first.as_int()] = State::S1; + en = module->addWire(NEW_ID); + module->addShift(NEW_ID, enable_mask, cmp, en, false, src); + } + + // create data signal + SigSpec data(State::Sx, (max_choice+1)*extwidth); + for (auto &it : perm_choices) { + int position = it.first.as_int()*extwidth; + int data_index = it.second; + data.replace(position, cell->getPort("\\B").extract(data_index*width, width)); + updated_S[data_index] = State::S0; + updated_B.replace(data_index*width, SigSpec(State::Sx, width)); + } + + // create shiftx cell + SigSpec shifted_cmp = {cmp, SigSpec(State::S0, width_bits)}; + SigSpec outsig = module->addWire(NEW_ID, width); + Cell *c = module->addShiftx(NEW_ID, data, shifted_cmp, outsig, false, src); + updated_S.append(en); + updated_B.append(outsig); + log(" created $shiftx cell %s.\n", log_id(c)); + + // remove this sig and continue with the next block + seldb.erase(sig); + } + + // update $pmux cell + cell->setPort("\\S", updated_S); + cell->setPort("\\B", updated_B); + cell->setParam("\\S_WIDTH", GetSize(updated_S)); + } } } } Pmux2ShiftxPass; From 177878cbb091f9339ee68a904f57f0283bdcfe10 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 19 Apr 2019 20:20:08 +0200 Subject: [PATCH 03/10] Improve pmux2shift ctrl permutation finder Signed-off-by: Clifford Wolf --- passes/opt/pmux2shiftx.cc | 141 ++++++++++++++++++++++++++++++-------- 1 file changed, 114 insertions(+), 27 deletions(-) diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc index 8ea70fe84..daa73b0b8 100644 --- a/passes/opt/pmux2shiftx.cc +++ b/passes/opt/pmux2shiftx.cc @@ -29,17 +29,29 @@ struct Pmux2ShiftxPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" pmux2shiftx [selection]\n"); + log(" pmux2shiftx [options] [selection]\n"); log("\n"); log("This pass transforms $pmux cells to $shiftx cells.\n"); log("\n"); + log(" -density non_offset_percentage offset_percentage\n"); + log(" specifies the minimum density for non_offset- and for offset-mode\n"); + log(" default values are 30 (non-offset) and 50 (offset)\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { + int non_offset_percentage = 30; + int offset_percentage = 50; + log_header(design, "Executing PMUX2SHIFTX pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-density" && argidx+2 < args.size()) { + non_offset_percentage = atoi(args[++argidx].c_str()); + offset_percentage = atoi(args[++argidx].c_str()); + continue; + } break; } extra_args(args, argidx, design); @@ -180,34 +192,108 @@ struct Pmux2ShiftxPass : public Pass { // find the relevant choices dict choices; - vector onescnt(GetSize(sig)); for (int i : seldb.at(sig)) { Const val = eqdb.at(S[i]).second; choices[val] = i; - for (int k = 0; k < GetSize(val); k++) - if (val[k] == State::S1) - onescnt[k] |= 1; - else - onescnt[k] |= 2; } // TBD: also find choices that are using signals that are subsets of the bits in "sig" // find the best permutation - vector> perm(GetSize(sig)); - for (int i = 0; i < GetSize(onescnt); i++) - perm[i] = make_pair(onescnt[i], i); - // TBD: this is not the best permutation - std::sort(perm.rbegin(), perm.rend()); + vector perm_new_from_old(GetSize(sig)); + Const perm_xormask(State::S0, GetSize(sig)); + { + vector values(GetSize(choices)); + vector used_src_columns(GetSize(sig)); + vector> columns(GetSize(sig), vector(GetSize(values))); + + for (int i = 0; i < GetSize(choices); i++) { + Const val = choices.element(i)->first; + for (int k = 0; k < GetSize(val); k++) + if (val[k] == State::S1) + columns[k][i] = true; + } + + for (int dst_col = GetSize(sig)-1; dst_col >= 0; dst_col--) + { + int best_src_col = -1; + bool best_inv = false; + int best_maxval = 0; + int best_delta = 0; + + // find best src colum for this dst column + for (int src_col = 0; src_col < GetSize(sig); src_col++) + { + if (used_src_columns[src_col]) + continue; + + int this_maxval = 0; + int this_minval = 1 << 30; + + int this_inv_maxval = 0; + int this_inv_minval = 1 << 30; + + for (int i = 0; i < GetSize(values); i++) + { + int val = values[i]; + int inv_val = val; + + if (columns[src_col][i]) + val |= 1 << dst_col; + else + inv_val |= 1 << dst_col; + + this_maxval = std::max(this_maxval, val); + this_minval = std::min(this_minval, val); + + this_inv_maxval = std::max(this_inv_maxval, inv_val); + this_inv_minval = std::min(this_inv_minval, inv_val); + } + + int this_delta = this_maxval - this_minval; + int this_inv_delta = this_maxval - this_minval; + bool this_inv = false; + + if (this_delta != this_inv_delta) + this_inv = this_inv_delta < this_delta; + else if (this_maxval != this_inv_maxval) + this_inv = this_inv_maxval < this_maxval; + + if (this_inv) { + this_delta = this_inv_delta; + this_maxval = this_inv_maxval; + this_minval = this_inv_minval; + } + + bool this_is_better = false; + + if (best_src_col < 0) + this_is_better = true; + else if (this_delta != best_delta) + this_is_better = this_delta < best_delta; + else if (this_maxval != best_maxval) + this_is_better = this_maxval < best_maxval; + else + this_is_better = sig[best_src_col] < sig[src_col]; + + if (this_is_better) { + best_src_col = src_col; + best_inv = this_inv; + best_maxval = this_maxval; + best_delta = this_delta; + } + } + + used_src_columns[best_src_col] = true; + perm_new_from_old[dst_col] = best_src_col; + perm_xormask[dst_col] = best_inv ? State::S1 : State::S0; + } + } // permutated sig - Const perm_xormask(State::S0, GetSize(sig)); SigSpec perm_sig(State::S0, GetSize(sig)); - for (int i = 0; i < GetSize(sig); i++) { - if (perm[i].first == 1) - perm_xormask[i] = State::S1; - perm_sig[i] = sig[perm[i].second]; - } + for (int i = 0; i < GetSize(sig); i++) + perm_sig[i] = sig[perm_new_from_old[i]]; log(" best permutation: %s\n", log_signal(perm_sig)); log(" best xor mask: %s\n", log_signal(perm_xormask)); @@ -223,7 +309,7 @@ struct Pmux2ShiftxPass : public Pass { Const new_c(State::S0, GetSize(old_c)); for (int i = 0; i < GetSize(old_c); i++) - new_c[i] = old_c[perm[i].second]; + new_c[i] = old_c[perm_new_from_old[i]]; Const new_c_before_xor = new_c; new_c = const_xor(new_c, perm_xormask, false, false, GetSize(new_c)); @@ -236,18 +322,21 @@ struct Pmux2ShiftxPass : public Pass { log(" %s -> %s -> %s\n", log_signal(old_c), log_signal(new_c_before_xor), log_signal(new_c)); } + int range_density = 100*GetSize(choices) / (max_choice-min_choice+1); + int absolute_density = 100*GetSize(choices) / (max_choice+1); + log(" choices: %d\n", GetSize(choices)); log(" min choice: %d\n", min_choice); log(" max choice: %d\n", max_choice); - log(" range density: %d%%\n", 100*GetSize(choices)/(max_choice-min_choice+1)); - log(" absolute density: %d%%\n", 100*GetSize(choices)/(max_choice+1)); + log(" range density: %d%%\n", range_density); + log(" absolute density: %d%%\n", absolute_density); bool full_case = (min_choice == 0) && (max_choice == (1 << GetSize(sig))-1) && (max_choice+1 == GetSize(choices)); log(" full case: %s\n", full_case ? "true" : "false"); - // use arithmetic offset if density is less than 30% + // check density percentages Const offset(State::S0, GetSize(sig)); - if (3*GetSize(choices) < max_choice && 3*GetSize(choices) >= (max_choice-min_choice)) + if (absolute_density < non_offset_percentage && range_density >= offset_percentage) { log(" using offset method.\n"); @@ -259,10 +348,8 @@ struct Pmux2ShiftxPass : public Pass { for (auto &it : perm_choices) new_perm_choices[const_sub(it.first, offset, false, false, GetSize(sig))] = it.second; perm_choices.swap(new_perm_choices); - } - - // ignore cases with a absolute density of less than 30% - if (3*GetSize(choices) < max_choice) { + } else + if (absolute_density < non_offset_percentage) { log(" insufficient density.\n"); seldb.erase(sig); continue; From 4c831d72ef2d3a9f9b91d6fa27e09800ae09e869 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 19 Apr 2019 20:23:09 +0200 Subject: [PATCH 04/10] Add test for pmux2shiftx Signed-off-by: Clifford Wolf --- tests/various/pmux2shiftx.v | 28 ++++++++++++++++++++++++++++ tests/various/pmux2shiftx.ys | 24 ++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/various/pmux2shiftx.v create mode 100644 tests/various/pmux2shiftx.ys diff --git a/tests/various/pmux2shiftx.v b/tests/various/pmux2shiftx.v new file mode 100644 index 000000000..ac61c5c0e --- /dev/null +++ b/tests/various/pmux2shiftx.v @@ -0,0 +1,28 @@ +module pmux2shiftx_test ( + input [2:0] S1, + input [5:0] S2, + input [9:0] A, B, C, D, D, E, F, + input [9:0] G, H, I, J, K, L, M, N, + output reg [9:0] X +); + always @* begin + case (S1) + 3'd0: X = A; + 3'd1: X = B; + 3'd2: X = C; + 3'd3: X = D; + 3'd4: X = E; + 3'd5: X = F; + 3'd6: X = G; + 3'd7: X = H; + endcase + case (S2) + 6'd46: X = I; + 6'd47: X = J; + 6'd48: X = K; + 6'd52: X = L; + 6'd53: X = M; + 6'd54: X = N; + endcase + end +endmodule diff --git a/tests/various/pmux2shiftx.ys b/tests/various/pmux2shiftx.ys new file mode 100644 index 000000000..16618ac0a --- /dev/null +++ b/tests/various/pmux2shiftx.ys @@ -0,0 +1,24 @@ +read_verilog pmux2shiftx.v +prep +design -save gold + +pmux2shiftx +opt +# show -width +select -assert-count 1 t:$mux +select -assert-count 1 t:$shift +select -assert-count 2 t:$shiftx +select -assert-count 1 t:$sub +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + +design -load gold +stat + +design -load gate +stat From 0070184ea9dea56d1dfd8268035bc01a3e340add Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 19 Apr 2019 23:37:11 +0200 Subject: [PATCH 05/10] Improvements in pmux2shiftx Signed-off-by: Clifford Wolf --- passes/opt/pmux2shiftx.cc | 19 ++++++------------ tests/various/pmux2shiftx.v | 38 +++++++++++++++++++++--------------- tests/various/pmux2shiftx.ys | 12 ++++++++---- 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc index daa73b0b8..d0f41cb07 100644 --- a/passes/opt/pmux2shiftx.cc +++ b/passes/opt/pmux2shiftx.cc @@ -149,6 +149,7 @@ struct Pmux2ShiftxPass : public Pass { dict> seldb; + SigSpec B = cell->getPort("\\B"); SigSpec S = sigmap(cell->getPort("\\S")); for (int i = 0; i < GetSize(S); i++) { @@ -168,15 +169,6 @@ struct Pmux2ShiftxPass : public Pass { SigSpec updated_S = cell->getPort("\\S"); SigSpec updated_B = cell->getPort("\\B"); - #if 1 - for (auto &it : seldb) { - string msg = stringf("seldb: %s ->", log_signal(it.first)); - for (int i : it.second) - msg += stringf(" %d(%s)", i, log_signal(eqdb.at(S[i]).second)); - log(" %s\n", msg.c_str()); - } - #endif - while (!seldb.empty()) { // pick the largest entry in seldb @@ -319,7 +311,8 @@ struct Pmux2ShiftxPass : public Pass { min_choice = std::min(min_choice, new_c.as_int()); max_choice = std::max(max_choice, new_c.as_int()); - log(" %s -> %s -> %s\n", log_signal(old_c), log_signal(new_c_before_xor), log_signal(new_c)); + log(" %3d: %s -> %s -> %s: %s\n", it.second, log_signal(old_c), log_signal(new_c_before_xor), + log_signal(new_c), log_signal(B.extract(it.second*width, width))); } int range_density = 100*GetSize(choices) / (max_choice-min_choice+1); @@ -338,9 +331,9 @@ struct Pmux2ShiftxPass : public Pass { Const offset(State::S0, GetSize(sig)); if (absolute_density < non_offset_percentage && range_density >= offset_percentage) { - log(" using offset method.\n"); - offset = Const(min_choice, GetSize(sig)); + log(" offset: %s\n", log_signal(offset)); + min_choice -= offset.as_int(); max_choice -= offset.as_int(); @@ -377,7 +370,7 @@ struct Pmux2ShiftxPass : public Pass { for (auto &it : perm_choices) { int position = it.first.as_int()*extwidth; int data_index = it.second; - data.replace(position, cell->getPort("\\B").extract(data_index*width, width)); + data.replace(position, B.extract(data_index*width, width)); updated_S[data_index] = State::S0; updated_B.replace(data_index*width, SigSpec(State::Sx, width)); } diff --git a/tests/various/pmux2shiftx.v b/tests/various/pmux2shiftx.v index ac61c5c0e..fec84187b 100644 --- a/tests/various/pmux2shiftx.v +++ b/tests/various/pmux2shiftx.v @@ -1,28 +1,34 @@ module pmux2shiftx_test ( input [2:0] S1, input [5:0] S2, - input [9:0] A, B, C, D, D, E, F, - input [9:0] G, H, I, J, K, L, M, N, + input [1:0] S3, + input [9:0] A, B, C, D, D, E, F, G, H, + input [9:0] I, J, K, L, M, N, O, P, Q, output reg [9:0] X ); always @* begin case (S1) - 3'd0: X = A; - 3'd1: X = B; - 3'd2: X = C; - 3'd3: X = D; - 3'd4: X = E; - 3'd5: X = F; - 3'd6: X = G; - 3'd7: X = H; + 3'd 0: X = A; + 3'd 1: X = B; + 3'd 2: X = C; + 3'd 3: X = D; + 3'd 4: X = E; + 3'd 5: X = F; + 3'd 6: X = G; + 3'd 7: X = H; endcase case (S2) - 6'd46: X = I; - 6'd47: X = J; - 6'd48: X = K; - 6'd52: X = L; - 6'd53: X = M; - 6'd54: X = N; + 6'd 45: X = I; + 6'd 47: X = J; + 6'd 49: X = K; + 6'd 55: X = L; + 6'd 57: X = M; + 6'd 59: X = N; + endcase + case (S3) + 2'd 1: X = O; + 2'd 2: X = P; + 2'd 3: X = Q; endcase end endmodule diff --git a/tests/various/pmux2shiftx.ys b/tests/various/pmux2shiftx.ys index 16618ac0a..f5e83171c 100644 --- a/tests/various/pmux2shiftx.ys +++ b/tests/various/pmux2shiftx.ys @@ -2,13 +2,17 @@ read_verilog pmux2shiftx.v prep design -save gold -pmux2shiftx +pmux2shiftx -density 70 50 + opt + +stat # show -width -select -assert-count 1 t:$mux -select -assert-count 1 t:$shift -select -assert-count 2 t:$shiftx select -assert-count 1 t:$sub +select -assert-count 2 t:$mux +select -assert-count 2 t:$shift +select -assert-count 3 t:$shiftx + design -stash gate design -import gold -as gold From 37728520a6d559a9f9a0082ddb3d596e8a634d97 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 20 Apr 2019 01:15:48 +0200 Subject: [PATCH 06/10] Improvements in "pmux2shiftx" Signed-off-by: Clifford Wolf --- passes/opt/pmux2shiftx.cc | 64 +++++++++++++++++++++++++++++------- tests/various/pmux2shiftx.ys | 2 +- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc index d0f41cb07..e6a0bd06b 100644 --- a/passes/opt/pmux2shiftx.cc +++ b/passes/opt/pmux2shiftx.cc @@ -33,23 +33,41 @@ struct Pmux2ShiftxPass : public Pass { log("\n"); log("This pass transforms $pmux cells to $shiftx cells.\n"); log("\n"); - log(" -density non_offset_percentage offset_percentage\n"); + log(" -min_density \n"); log(" specifies the minimum density for non_offset- and for offset-mode\n"); log(" default values are 30 (non-offset) and 50 (offset)\n"); log("\n"); + log(" -min_choices \n"); + log(" specified the minimum number of choices for a control signal\n"); + log(" defaukt: 3\n"); + log("\n"); + log(" -allow_onehot\n"); + log(" by default, pmuxes with one-hot encoded control signals are not\n"); + log(" converted. this option disables that check.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { - int non_offset_percentage = 30; - int offset_percentage = 50; + int min_non_offset_percentage = 30; + int min_offset_percentage = 50; + int min_choices = 3; + bool allow_onehot = false; log_header(design, "Executing PMUX2SHIFTX pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-density" && argidx+2 < args.size()) { - non_offset_percentage = atoi(args[++argidx].c_str()); - offset_percentage = atoi(args[++argidx].c_str()); + if (args[argidx] == "-min_density" && argidx+2 < args.size()) { + min_non_offset_percentage = atoi(args[++argidx].c_str()); + min_offset_percentage = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-min_choices" && argidx+1 < args.size()) { + min_choices = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-allow_onehot") { + allow_onehot = true; continue; } break; @@ -163,8 +181,7 @@ struct Pmux2ShiftxPass : public Pass { if (seldb.empty()) continue; - log("Inspecting $pmux cell %s/%s.\n", log_id(module), log_id(cell)); - log(" data width: %d (next power-of-2 = %d, log2 = %d)\n", width, extwidth, width_bits); + bool printed_pmux_header = false; SigSpec updated_S = cell->getPort("\\S"); SigSpec updated_B = cell->getPort("\\B"); @@ -180,17 +197,40 @@ struct Pmux2ShiftxPass : public Pass { sig = it.first; } - log(" checking ctrl signal %s\n", log_signal(sig)); - // find the relevant choices + bool is_onehot = true; dict choices; for (int i : seldb.at(sig)) { Const val = eqdb.at(S[i]).second; + int onebits = 0; + for (auto b : val.bits) + if (b == State::S1) + onebits++; + if (onebits > 1) + is_onehot = false; choices[val] = i; } // TBD: also find choices that are using signals that are subsets of the bits in "sig" + if (is_onehot && !allow_onehot) { + seldb.erase(sig); + continue; + } + + if (GetSize(choices) < min_choices) { + seldb.erase(sig); + continue; + } + + if (!printed_pmux_header) { + printed_pmux_header = true; + log("Inspecting $pmux cell %s/%s.\n", log_id(module), log_id(cell)); + log(" data width: %d (next power-of-2 = %d, log2 = %d)\n", width, extwidth, width_bits); + } + + log(" checking ctrl signal %s\n", log_signal(sig)); + // find the best permutation vector perm_new_from_old(GetSize(sig)); Const perm_xormask(State::S0, GetSize(sig)); @@ -329,7 +369,7 @@ struct Pmux2ShiftxPass : public Pass { // check density percentages Const offset(State::S0, GetSize(sig)); - if (absolute_density < non_offset_percentage && range_density >= offset_percentage) + if (absolute_density < min_non_offset_percentage && range_density >= min_offset_percentage) { offset = Const(min_choice, GetSize(sig)); log(" offset: %s\n", log_signal(offset)); @@ -342,7 +382,7 @@ struct Pmux2ShiftxPass : public Pass { new_perm_choices[const_sub(it.first, offset, false, false, GetSize(sig))] = it.second; perm_choices.swap(new_perm_choices); } else - if (absolute_density < non_offset_percentage) { + if (absolute_density < min_non_offset_percentage) { log(" insufficient density.\n"); seldb.erase(sig); continue; diff --git a/tests/various/pmux2shiftx.ys b/tests/various/pmux2shiftx.ys index f5e83171c..f84ae3869 100644 --- a/tests/various/pmux2shiftx.ys +++ b/tests/various/pmux2shiftx.ys @@ -2,7 +2,7 @@ read_verilog pmux2shiftx.v prep design -save gold -pmux2shiftx -density 70 50 +pmux2shiftx -min_density 70 50 opt From e06d158e8a3ae3626fbf82b3a8c6764f05111513 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 20 Apr 2019 01:18:07 +0200 Subject: [PATCH 07/10] Fix some typos Signed-off-by: Clifford Wolf --- passes/opt/pmux2shiftx.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc index e6a0bd06b..dbcdff3d8 100644 --- a/passes/opt/pmux2shiftx.cc +++ b/passes/opt/pmux2shiftx.cc @@ -39,7 +39,7 @@ struct Pmux2ShiftxPass : public Pass { log("\n"); log(" -min_choices \n"); log(" specified the minimum number of choices for a control signal\n"); - log(" defaukt: 3\n"); + log(" default: 3\n"); log("\n"); log(" -allow_onehot\n"); log(" by default, pmuxes with one-hot encoded control signals are not\n"); @@ -253,7 +253,7 @@ struct Pmux2ShiftxPass : public Pass { int best_maxval = 0; int best_delta = 0; - // find best src colum for this dst column + // find best src column for this dst column for (int src_col = 0; src_col < GetSize(sig); src_col++) { if (used_src_columns[src_col]) From b3a3e08e389c8d10fbd8e0dc8b48e1e559dedf5d Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 20 Apr 2019 02:03:44 +0200 Subject: [PATCH 08/10] Improve "pmux2shiftx" Signed-off-by: Clifford Wolf --- passes/opt/pmux2shiftx.cc | 18 ++++++++---------- tests/various/pmux2shiftx.ys | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc index dbcdff3d8..4cd061c68 100644 --- a/passes/opt/pmux2shiftx.cc +++ b/passes/opt/pmux2shiftx.cc @@ -33,9 +33,9 @@ struct Pmux2ShiftxPass : public Pass { log("\n"); log("This pass transforms $pmux cells to $shiftx cells.\n"); log("\n"); - log(" -min_density \n"); - log(" specifies the minimum density for non_offset- and for offset-mode\n"); - log(" default values are 30 (non-offset) and 50 (offset)\n"); + log(" -min_density \n"); + log(" specifies the minimum density for the shifter\n"); + log(" default: 50\n"); log("\n"); log(" -min_choices \n"); log(" specified the minimum number of choices for a control signal\n"); @@ -48,8 +48,7 @@ struct Pmux2ShiftxPass : public Pass { } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { - int min_non_offset_percentage = 30; - int min_offset_percentage = 50; + int min_density = 50; int min_choices = 3; bool allow_onehot = false; @@ -57,9 +56,8 @@ struct Pmux2ShiftxPass : public Pass { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-min_density" && argidx+2 < args.size()) { - min_non_offset_percentage = atoi(args[++argidx].c_str()); - min_offset_percentage = atoi(args[++argidx].c_str()); + if (args[argidx] == "-min_density" && argidx+1 < args.size()) { + min_density = atoi(args[++argidx].c_str()); continue; } if (args[argidx] == "-min_choices" && argidx+1 < args.size()) { @@ -369,7 +367,7 @@ struct Pmux2ShiftxPass : public Pass { // check density percentages Const offset(State::S0, GetSize(sig)); - if (absolute_density < min_non_offset_percentage && range_density >= min_offset_percentage) + if (absolute_density < min_density && range_density >= min_density) { offset = Const(min_choice, GetSize(sig)); log(" offset: %s\n", log_signal(offset)); @@ -382,7 +380,7 @@ struct Pmux2ShiftxPass : public Pass { new_perm_choices[const_sub(it.first, offset, false, false, GetSize(sig))] = it.second; perm_choices.swap(new_perm_choices); } else - if (absolute_density < min_non_offset_percentage) { + if (absolute_density < min_density) { log(" insufficient density.\n"); seldb.erase(sig); continue; diff --git a/tests/various/pmux2shiftx.ys b/tests/various/pmux2shiftx.ys index f84ae3869..6bb9626eb 100644 --- a/tests/various/pmux2shiftx.ys +++ b/tests/various/pmux2shiftx.ys @@ -2,7 +2,7 @@ read_verilog pmux2shiftx.v prep design -save gold -pmux2shiftx -min_density 70 50 +pmux2shiftx -min_density 70 opt From 97e9caa4fa1f874b693a9d948f48418f22babb6c Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 20 Apr 2019 17:52:16 +0200 Subject: [PATCH 09/10] Add "onehot" pass, improve "pmux2shiftx" onehot handling Signed-off-by: Clifford Wolf --- passes/opt/pmux2shiftx.cc | 417 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 404 insertions(+), 13 deletions(-) diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc index 4cd061c68..5fd49a571 100644 --- a/passes/opt/pmux2shiftx.cc +++ b/passes/opt/pmux2shiftx.cc @@ -23,6 +23,172 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +struct OnehotDatabase +{ + Module *module; + const SigMap &sigmap; + bool verbose = false; + + pool init_ones; + dict> sig_sources_db; + dict sig_onehot_cache; + pool recursion_guard; + + OnehotDatabase(Module *module, const SigMap &sigmap) : module(module), sigmap(sigmap) + { + } + + void initialize() + { + for (auto wire : module->wires()) + { + auto it = wire->attributes.find("\\init"); + if (it == wire->attributes.end()) + continue; + + auto &val = it->second; + int width = std::max(GetSize(wire), GetSize(val)); + + for (int i = 0; i < width; i++) + if (val[i] == State::S1) + init_ones.insert(sigmap(SigBit(wire, i))); + } + + for (auto cell : module->cells()) + { + vector inputs; + SigSpec output; + + if (cell->type.in("$adff", "$dff", "$dffe", "$dlatch", "$ff")) + { + output = cell->getPort("\\Q"); + if (cell->type == "$adff") + inputs.push_back(cell->getParam("\\ARST_VALUE")); + inputs.push_back(cell->getPort("\\D")); + } + + if (cell->type.in("$mux", "$pmux")) + { + output = cell->getPort("\\Y"); + inputs.push_back(cell->getPort("\\A")); + SigSpec B = cell->getPort("\\B"); + for (int i = 0; i < GetSize(B); i += GetSize(output)) + inputs.push_back(B.extract(i, GetSize(output))); + } + + if (!output.empty()) + { + output = sigmap(output); + auto &srcs = sig_sources_db[output]; + for (auto src : inputs) { + while (!src.empty() && src[GetSize(src)-1] == State::S0) + src.remove(GetSize(src)-1); + srcs.insert(sigmap(src)); + } + } + } + } + + void query_worker(const SigSpec &sig, bool &retval, bool &cache, int indent) + { + if (verbose) + log("%*s %s\n", indent, "", log_signal(sig)); + log_assert(retval); + + if (recursion_guard.count(sig)) { + if (verbose) + log("%*s - recursion\n", indent, ""); + cache = false; + return; + } + + auto it = sig_onehot_cache.find(sig); + if (it != sig_onehot_cache.end()) { + if (verbose) + log("%*s - cached (%s)\n", indent, "", it->second ? "true" : "false"); + if (!it->second) + retval = false; + return; + } + + bool found_init_ones = false; + for (auto bit : sig) { + if (init_ones.count(bit)) { + if (found_init_ones) { + if (verbose) + log("%*s - non-onehot init value\n", indent, ""); + retval = false; + break; + } + found_init_ones = true; + } + } + + if (retval) + { + if (sig.is_fully_const()) + { + bool found_ones = false; + for (auto bit : sig) { + if (bit == State::S1) { + if (found_ones) { + if (verbose) + log("%*s - non-onehot constant\n", indent, ""); + retval = false; + break; + } + found_ones = true; + } + } + } + else + { + auto srcs = sig_sources_db.find(sig); + if (srcs == sig_sources_db.end()) { + if (verbose) + log("%*s - no sources for non-const signal\n", indent, ""); + retval = false; + } else { + for (auto &src : srcs->second) { + bool child_cache = true; + recursion_guard.insert(sig); + query_worker(src, retval, child_cache, indent+4); + recursion_guard.erase(sig); + if (!child_cache) + cache = false; + if (!retval) + break; + } + } + } + } + + // it is always safe to cache a negative result + if (cache || !retval) + sig_onehot_cache[sig] = retval; + } + + bool query(const SigSpec &sig) + { + bool retval = true; + bool cache = true; + + if (verbose) + log("** ONEHOT QUERY START (%s)\n", log_signal(sig)); + + query_worker(sig, retval, cache, 3); + + if (verbose) + log("** ONEHOT QUERY RESULT = %s\n", retval ? "true" : "false"); + + // it is always safe to cache the root result of a query + if (!cache) + sig_onehot_cache[sig] = retval; + + return retval; + } +}; + struct Pmux2ShiftxPass : public Pass { Pmux2ShiftxPass() : Pass("pmux2shiftx", "transform $pmux cells to $shiftx cells") { } void help() YS_OVERRIDE @@ -33,6 +199,9 @@ struct Pmux2ShiftxPass : public Pass { log("\n"); log("This pass transforms $pmux cells to $shiftx cells.\n"); log("\n"); + log(" -v, -vv\n"); + log(" verbose output\n"); + log("\n"); log(" -min_density \n"); log(" specifies the minimum density for the shifter\n"); log(" default: 50\n"); @@ -41,9 +210,9 @@ struct Pmux2ShiftxPass : public Pass { log(" specified the minimum number of choices for a control signal\n"); log(" default: 3\n"); log("\n"); - log(" -allow_onehot\n"); - log(" by default, pmuxes with one-hot encoded control signals are not\n"); - log(" converted. this option disables that check.\n"); + log(" -onehot ignore|pmux|shiftx\n"); + log(" select strategy for one-hot encoded control signals\n"); + log(" default: pmux\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE @@ -51,6 +220,9 @@ struct Pmux2ShiftxPass : public Pass { int min_density = 50; int min_choices = 3; bool allow_onehot = false; + bool optimize_onehot = true; + bool verbose = false; + bool verbose_onehot = false; log_header(design, "Executing PMUX2SHIFTX pass.\n"); @@ -64,8 +236,31 @@ struct Pmux2ShiftxPass : public Pass { min_choices = atoi(args[++argidx].c_str()); continue; } - if (args[argidx] == "-allow_onehot") { + if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "ignore") { + argidx++; + allow_onehot = false; + optimize_onehot = false; + continue; + } + if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "pmux") { + argidx++; + allow_onehot = false; + optimize_onehot = true; + continue; + } + if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "shiftx") { + argidx++; allow_onehot = true; + optimize_onehot = false; + continue; + } + if (args[argidx] == "-v") { + verbose = true; + continue; + } + if (args[argidx] == "-vv") { + verbose = true; + verbose_onehot = true; continue; } break; @@ -75,10 +270,15 @@ struct Pmux2ShiftxPass : public Pass { for (auto module : design->selected_modules()) { SigMap sigmap(module); + OnehotDatabase onehot_db(module, sigmap); + onehot_db.verbose = verbose_onehot; + + if (optimize_onehot) + onehot_db.initialize(); dict> eqdb; - for (auto cell : module->selected_cells()) + for (auto cell : module->cells()) { if (cell->type == "$eq") { @@ -181,6 +381,12 @@ struct Pmux2ShiftxPass : public Pass { bool printed_pmux_header = false; + if (verbose) { + printed_pmux_header = true; + log("Inspecting $pmux cell %s/%s.\n", log_id(module), log_id(cell)); + log(" data width: %d (next power-of-2 = %d, log2 = %d)\n", width, extwidth, width_bits); + } + SigSpec updated_S = cell->getPort("\\S"); SigSpec updated_B = cell->getPort("\\B"); @@ -196,7 +402,7 @@ struct Pmux2ShiftxPass : public Pass { } // find the relevant choices - bool is_onehot = true; + bool is_onehot = GetSize(sig) > 2; dict choices; for (int i : seldb.at(sig)) { Const val = eqdb.at(S[i]).second; @@ -211,14 +417,17 @@ struct Pmux2ShiftxPass : public Pass { // TBD: also find choices that are using signals that are subsets of the bits in "sig" - if (is_onehot && !allow_onehot) { - seldb.erase(sig); - continue; - } + if (!verbose) + { + if (is_onehot && !allow_onehot && !optimize_onehot) { + seldb.erase(sig); + continue; + } - if (GetSize(choices) < min_choices) { - seldb.erase(sig); - continue; + if (GetSize(choices) < min_choices) { + seldb.erase(sig); + continue; + } } if (!printed_pmux_header) { @@ -229,6 +438,65 @@ struct Pmux2ShiftxPass : public Pass { log(" checking ctrl signal %s\n", log_signal(sig)); + auto print_choices = [&]() { + log(" table of choices:\n"); + for (auto &it : choices) + log(" %3d: %s: %s\n", it.second, log_signal(it.first), + log_signal(B.extract(it.second*width, width))); + }; + + if (verbose) + { + if (is_onehot && !allow_onehot && !optimize_onehot) { + print_choices(); + log(" ignoring one-hot encoding.\n"); + seldb.erase(sig); + continue; + } + + if (GetSize(choices) < min_choices) { + print_choices(); + log(" insufficient choices.\n"); + seldb.erase(sig); + continue; + } + } + + if (is_onehot && optimize_onehot) + { + print_choices(); + if (!onehot_db.query(sig)) + { + log(" failed to detect onehot driver. do not optimize.\n"); + } + else + { + log(" optimizing one-hot encoding.\n"); + for (auto &it : choices) + { + const Const &val = it.first; + int index = -1; + + for (int i = 0; i < GetSize(val); i++) + if (val[i] == State::S1) { + log_assert(index < 0); + index = i; + } + + if (index < 0) { + log(" %3d: zero encoding.\n", it.second); + continue; + } + + SigBit new_ctrl = sig[index]; + log(" %3d: new crtl signal is %s.\n", it.second, log_signal(new_ctrl)); + updated_S[it.second] = new_ctrl; + } + } + seldb.erase(sig); + continue; + } + // find the best permutation vector perm_new_from_old(GetSize(sig)); Const perm_xormask(State::S0, GetSize(sig)); @@ -434,4 +702,127 @@ struct Pmux2ShiftxPass : public Pass { } } Pmux2ShiftxPass; +struct OnehotPass : public Pass { + OnehotPass() : Pass("onehot", "optimize $eq cells for onehot signals") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" onehot [options] [selection]\n"); + log("\n"); + log("This pass optimizes $eq cells that compare one-hot signals against constants\n"); + log("\n"); + log(" -v, -vv\n"); + log(" verbose output\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + bool verbose = false; + bool verbose_onehot = false; + + log_header(design, "Executing ONEHOT pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-v") { + verbose = true; + continue; + } + if (args[argidx] == "-vv") { + verbose = true; + verbose_onehot = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + SigMap sigmap(module); + OnehotDatabase onehot_db(module, sigmap); + onehot_db.verbose = verbose_onehot; + onehot_db.initialize(); + + for (auto cell : module->selected_cells()) + { + if (cell->type != "$eq") + continue; + + SigSpec A = sigmap(cell->getPort("\\A")); + SigSpec B = sigmap(cell->getPort("\\B")); + + int a_width = cell->getParam("\\A_WIDTH").as_int(); + int b_width = cell->getParam("\\B_WIDTH").as_int(); + + if (a_width < b_width) { + bool a_signed = cell->getParam("\\A_SIGNED").as_int(); + A.extend_u0(b_width, a_signed); + } + + if (b_width < a_width) { + bool b_signed = cell->getParam("\\B_SIGNED").as_int(); + B.extend_u0(a_width, b_signed); + } + + if (A.is_fully_const()) + std::swap(A, B); + + if (!B.is_fully_const()) + continue; + + if (verbose) + log("Checking $eq(%s, %s) cell %s/%s.\n", log_signal(A), log_signal(B), log_id(module), log_id(cell)); + + if (!onehot_db.query(A)) { + if (verbose) + log(" onehot driver test on %s failed.\n", log_signal(A)); + continue; + } + + int index = -1; + bool not_onehot = false; + + for (int i = 0; i < GetSize(B); i++) { + if (B[i] != State::S1) + continue; + if (index >= 0) + not_onehot = true; + index = i; + } + + if (index < 0) { + if (verbose) + log(" not optimizing the zero pattern.\n"); + continue; + } + + SigSpec Y = cell->getPort("\\Y"); + + if (not_onehot) + { + if (verbose) + log(" replacing with constant 0 driver.\n"); + else + log("Replacing one-hot $eq(%s, %s) cell %s/%s with constant 0 driver.\n", log_signal(A), log_signal(B), log_id(module), log_id(cell)); + module->connect(Y, SigSpec(1, GetSize(Y))); + } + else + { + SigSpec sig = A[index]; + if (verbose) + log(" replacing with signal %s.\n", log_signal(sig)); + else + log("Replacing one-hot $eq(%s, %s) cell %s/%s with signal %s.\n",log_signal(A), log_signal(B), log_id(module), log_id(cell), log_signal(sig)); + sig.extend_u0(GetSize(Y)); + module->connect(Y, sig); + } + + module->remove(cell); + } + } + } +} OnehotPass; + PRIVATE_NAMESPACE_END From fc23af170768926e645b6c94c54b4c2e6e8d0808 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 20 Apr 2019 18:13:37 +0200 Subject: [PATCH 10/10] Auto-initialize OnehotDatabase on-demand in pmux2shiftx.cc Signed-off-by: Clifford Wolf --- passes/opt/pmux2shiftx.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc index 5fd49a571..5f897b131 100644 --- a/passes/opt/pmux2shiftx.cc +++ b/passes/opt/pmux2shiftx.cc @@ -28,6 +28,7 @@ struct OnehotDatabase Module *module; const SigMap &sigmap; bool verbose = false; + bool initialized = false; pool init_ones; dict> sig_sources_db; @@ -40,6 +41,9 @@ struct OnehotDatabase void initialize() { + log_assert(!initialized); + initialized = true; + for (auto wire : module->wires()) { auto it = wire->attributes.find("\\init"); @@ -176,6 +180,9 @@ struct OnehotDatabase if (verbose) log("** ONEHOT QUERY START (%s)\n", log_signal(sig)); + if (!initialized) + initialize(); + query_worker(sig, retval, cache, 3); if (verbose) @@ -273,9 +280,6 @@ struct Pmux2ShiftxPass : public Pass { OnehotDatabase onehot_db(module, sigmap); onehot_db.verbose = verbose_onehot; - if (optimize_onehot) - onehot_db.initialize(); - dict> eqdb; for (auto cell : module->cells()) @@ -743,7 +747,6 @@ struct OnehotPass : public Pass { SigMap sigmap(module); OnehotDatabase onehot_db(module, sigmap); onehot_db.verbose = verbose_onehot; - onehot_db.initialize(); for (auto cell : module->selected_cells()) {