From 379dcda1398ea7a1325f82340148359ffe2bf548 Mon Sep 17 00:00:00 2001 From: Niklas Nisbeth Date: Tue, 31 Dec 2019 02:38:10 +0100 Subject: [PATCH 01/15] ice40: Demote conflicting FF init values to a warning --- techlibs/ice40/ice40_ffinit.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/techlibs/ice40/ice40_ffinit.cc b/techlibs/ice40/ice40_ffinit.cc index 3089d8932..c098736e9 100644 --- a/techlibs/ice40/ice40_ffinit.cc +++ b/techlibs/ice40/ice40_ffinit.cc @@ -78,10 +78,12 @@ struct Ice40FfinitPass : public Pass { continue; if (initbits.count(bit)) { - if (initbits.at(bit) != val) - log_error("Conflicting init values for signal %s (%s = %s, %s = %s).\n", + if (initbits.at(bit) != val) { + log_warning("Conflicting init values for signal %s (%s = %s, %s = %s).\n", log_signal(bit), log_signal(SigBit(wire, i)), log_signal(val), log_signal(initbit_to_wire[bit]), log_signal(initbits.at(bit))); + initbits.at(bit) = State::Sx; + } continue; } @@ -114,6 +116,10 @@ struct Ice40FfinitPass : public Pass { continue; State val = initbits.at(bit_q); + + if (val == State::Sx) + continue; + handled_initbits.insert(bit_q); log("FF init value for cell %s (%s): %s = %c\n", log_id(cell), log_id(cell->type), From 3fa374a69886c3a7ef8d52a2bbb67ea740e130dd Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 9 Jan 2020 21:22:54 +0100 Subject: [PATCH 02/15] Add fminit pass Signed-off-by: Clifford Wolf --- passes/sat/Makefile.inc | 1 + passes/sat/fminit.cc | 197 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 passes/sat/fminit.cc diff --git a/passes/sat/Makefile.inc b/passes/sat/Makefile.inc index fc3ac879e..4bb4b0edc 100644 --- a/passes/sat/Makefile.inc +++ b/passes/sat/Makefile.inc @@ -12,4 +12,5 @@ OBJS += passes/sat/supercover.o OBJS += passes/sat/fmcombine.o OBJS += passes/sat/mutate.o OBJS += passes/sat/cutpoint.o +OBJS += passes/sat/fminit.o diff --git a/passes/sat/fminit.cc b/passes/sat/fminit.cc new file mode 100644 index 000000000..f3f00b382 --- /dev/null +++ b/passes/sat/fminit.cc @@ -0,0 +1,197 @@ +/* + * 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 FminitPass : public Pass { + FminitPass() : Pass("fminit", "set init values/sequences for formal") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" fminit [options] \n"); + log("\n"); + log("This pass creates init constraints (for example for reset sequences) in a formal\n"); + log("model.\n"); + log("\n"); + log(" -seq \n"); + log(" Set sequence using comma-separated list of values, use 'z for\n"); + log(" unconstrained bits. The last value is used for the remainder of the\n"); + log(" trace.\n"); + log("\n"); + log(" -set \n"); + log(" Add constant value constraint\n"); + log("\n"); + log(" -posedge \n"); + log(" -negedge \n"); + log(" Set clock for init sequences\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + vector>> initdata; + vector> setdata; + string clocksignal; + bool clockedge; + + log_header(design, "Executing FMINIT pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-o" && argidx+1 < args.size()) { + // filename = args[++argidx]; + // continue; + // } + if (args[argidx] == "-seq" && argidx+2 < args.size()) { + string lhs = args[++argidx]; + string rhs = args[++argidx]; + initdata.push_back(make_pair(lhs, split_tokens(rhs, ","))); + continue; + } + if (args[argidx] == "-set" && argidx+2 < args.size()) { + string lhs = args[++argidx]; + string rhs = args[++argidx]; + setdata.push_back(make_pair(lhs, rhs)); + continue; + } + if (args[argidx] == "-posedge" && argidx+1 < args.size()) { + clocksignal = args[++argidx]; + clockedge = true; + continue; + } + if (args[argidx] == "-negedge" && argidx+1 < args.size()) { + clocksignal = args[++argidx]; + clockedge = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + Module *module = nullptr; + + for (auto mod : design->selected_modules()) { + if (module != nullptr) + log_error("'fminit' requires exactly one module to be selected.\n"); + module = mod; + } + + if (module == nullptr) + log_error("'fminit' requires exactly one module to be selected.\n"); + + SigSpec clksig; + if (!clocksignal.empty()) { + if (!SigSpec::parse(clksig, module, clocksignal)) + log_error("Error parsing expression '%s'.\n", clocksignal.c_str()); + } + + for (auto &it : setdata) + { + SigSpec lhs, rhs; + + if (!SigSpec::parse(lhs, module, it.first)) + log_error("Error parsing expression '%s'.\n", it.first.c_str()); + + if (!SigSpec::parse_rhs(lhs, rhs, module, it.second)) + log_error("Error parsing expression '%s'.\n", it.second.c_str()); + + SigSpec final_lhs, final_rhs; + + for (int i = 0; i < GetSize(rhs); i++) + if (rhs[i] != State::Sz) { + final_lhs.append(lhs[i]); + final_rhs.append(rhs[i]); + } + + if (!final_lhs.empty()) { + SigSpec eq = module->Eq(NEW_ID, final_lhs, final_rhs); + module->addAssume(NEW_ID, eq, State::S1); + } + } + + vector ctrlsig; + vector ctrlsig_latched; + + for (auto &it : initdata) + { + SigSpec lhs, rhs; + + if (!SigSpec::parse(lhs, module, it.first)) + log_error("Error parsing expression '%s'.\n", it.first.c_str()); + + for (int i = 0; i < GetSize(it.second); i++) + { + if (i >= GetSize(ctrlsig)) + { + SigSpec insig = i > 0 ? ctrlsig.at(i-1) : State::S0; + + Wire *outwire = module->addWire(NEW_ID); + outwire->attributes[ID(init)] = i > 0 ? State::S0 : State::S1; + + if (clksig.empty()) + module->addFf(NEW_ID, insig, outwire); + else + module->addDff(NEW_ID, clksig, insig, outwire, clockedge); + + ctrlsig.push_back(outwire); + ctrlsig_latched.push_back(SigSpec()); + } + + if (i+1 == GetSize(it.second) && ctrlsig_latched[i].empty()) + { + Wire *ffwire = module->addWire(NEW_ID); + ffwire->attributes[ID(init)] = State::S0; + SigSpec outsig = module->Or(NEW_ID, ffwire, ctrlsig[i]); + + if (clksig.empty()) + module->addFf(NEW_ID, outsig, ffwire); + else + module->addDff(NEW_ID, clksig, outsig, ffwire, clockedge); + + ctrlsig_latched[i] = outsig; + } + + SigSpec ctrl = i+1 == GetSize(it.second) ? ctrlsig_latched[i] : ctrlsig[i]; + + SigSpec final_lhs, final_rhs; + + if (!SigSpec::parse_rhs(lhs, rhs, module, it.second[i])) + log_error("Error parsing expression '%s'.\n", it.second[i].c_str()); + + for (int i = 0; i < GetSize(rhs); i++) + if (rhs[i] != State::Sz) { + final_lhs.append(lhs[i]); + final_rhs.append(rhs[i]); + } + + if (!final_lhs.empty()) { + SigSpec eq = module->Eq(NEW_ID, final_lhs, final_rhs); + module->addAssume(NEW_ID, eq, ctrl); + } + } + } + } +} FminitPass; + +PRIVATE_NAMESPACE_END From 55f86eda36ed9288528c2216972d5b49acfca54f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Ko=C5=9Bcielnicki?= Date: Mon, 13 Jan 2020 14:49:31 +0100 Subject: [PATCH 03/15] edif: Just ignore connections to 'z Connecting a const 'z to a net should be equivalent to not connecting it at all, so let's just ignore such connections on output. --- backends/edif/edif.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index 6d9469538..e9beace83 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -404,6 +404,8 @@ struct EdifBackend : public Backend { for (auto &ref : it.second) log_warning("Exporting x-bit on %s as zero bit.\n", ref.c_str()); sig = RTLIL::State::S0; + } else if (sig == RTLIL::State::Sz) { + continue; } else { for (auto &ref : it.second) log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.c_str()); From a5d2358a60084361902583f4fa024d2d53ce6c2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Ko=C5=9Bcielnicki?= Date: Tue, 14 Jan 2020 22:48:40 +0100 Subject: [PATCH 04/15] fsm_detect: Add a cache to avoid excessive CPU usage for big mux networks. Fixes #1634. --- passes/fsm/fsm_detect.cc | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc index fb3896669..a1c8067b4 100644 --- a/passes/fsm/fsm_detect.cc +++ b/passes/fsm/fsm_detect.cc @@ -34,13 +34,20 @@ static SigSet sig2driver, sig2user; static std::set muxtree_cells; static SigPool sig_at_port; -static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool &recursion_monitor) +static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool &recursion_monitor, dict &mux_tree_cache) { + if (mux_tree_cache.find(sig) != mux_tree_cache.end()) + return mux_tree_cache.at(sig); + if (sig.is_fully_const() || old_sig == sig) { +ret_true: + mux_tree_cache[sig] = true; return true; } if (sig_at_port.check_any(assign_map(sig))) { +ret_false: + mux_tree_cache[sig] = false; return false; } @@ -49,13 +56,13 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, poo for (auto &cellport : cellport_list) { if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") { - return false; + goto ret_false; } if (recursion_monitor.count(cellport.first)) { log_warning("logic loop in mux tree at signal %s in module %s.\n", log_signal(sig), RTLIL::id2cstr(module->name)); - return false; + goto ret_false; } recursion_monitor.insert(cellport.first); @@ -63,22 +70,22 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, poo RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A")); RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B")); - if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) { + if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor, mux_tree_cache)) { recursion_monitor.erase(cellport.first); - return false; + goto ret_false; } for (int i = 0; i < sig_b.size(); i += sig_a.size()) - if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) { + if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor, mux_tree_cache)) { recursion_monitor.erase(cellport.first); - return false; + goto ret_false; } recursion_monitor.erase(cellport.first); muxtree_cells.insert(cellport.first); } - return true; + goto ret_true; } static bool check_state_users(RTLIL::SigSpec sig) @@ -143,11 +150,12 @@ static void detect_fsm(RTLIL::Wire *wire) pool recursion_monitor; RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q")); RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D")); + dict mux_tree_cache; if (sig_q != assign_map(wire)) continue; - looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor); + looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor, mux_tree_cache); looks_like_good_state_reg = check_state_users(sig_q); if (!looks_like_state_reg) From 1e6d56dca11c7a632b74ba51361f879dc706110a Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Fri, 17 Jan 2020 11:14:19 -0800 Subject: [PATCH 05/15] +/xilinx/arith_map.v fix $lcu rule --- techlibs/xilinx/arith_map.v | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/techlibs/xilinx/arith_map.v b/techlibs/xilinx/arith_map.v index 5c848d4e6..c345a3da3 100644 --- a/techlibs/xilinx/arith_map.v +++ b/techlibs/xilinx/arith_map.v @@ -53,9 +53,9 @@ module _80_xilinx_lcu (P, G, CI, CO); ( .CYINIT(CI), .CI (1'd0), - .DI (G [(Y_WIDTH - 1):i*4]), - .S (S [(Y_WIDTH - 1):i*4]), - .CO (CO[(Y_WIDTH - 1):i*4]), + .DI (G [(WIDTH - 1):i*4]), + .S (S [(WIDTH - 1):i*4]), + .CO (CO[(WIDTH - 1):i*4]), ); // Another one end else begin @@ -63,9 +63,9 @@ module _80_xilinx_lcu (P, G, CI, CO); ( .CYINIT(1'd0), .CI (C [i*4 - 1]), - .DI (G [(Y_WIDTH - 1):i*4]), - .S (S [(Y_WIDTH - 1):i*4]), - .CO (CO[(Y_WIDTH - 1):i*4]), + .DI (G [(WIDTH - 1):i*4]), + .S (S [(WIDTH - 1):i*4]), + .CO (CO[(WIDTH - 1):i*4]), ); end From 5c589244df2ec4fc5fde0bcdc69dee727f4b8e79 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Fri, 17 Jan 2020 12:02:46 -0800 Subject: [PATCH 06/15] Deprecate `_CLB_CARRY from +/xilinx/arith_map.v since #1623 --- techlibs/xilinx/arith_map.v | 199 +++++++++++++------------------- techlibs/xilinx/synth_xilinx.cc | 2 - 2 files changed, 82 insertions(+), 119 deletions(-) diff --git a/techlibs/xilinx/arith_map.v b/techlibs/xilinx/arith_map.v index c345a3da3..40c378d16 100644 --- a/techlibs/xilinx/arith_map.v +++ b/techlibs/xilinx/arith_map.v @@ -33,7 +33,21 @@ module _80_xilinx_lcu (P, G, CI, CO); genvar i; -`ifdef _CLB_CARRY +`ifdef _EXPLICIT_CARRY + + wire [WIDTH-1:0] C = {CO, CI}; + wire [WIDTH-1:0] S = P & ~G; + + generate for (i = 0; i < WIDTH; i = i + 1) begin:slice + MUXCY muxcy ( + .CI(C[i]), + .DI(G[i]), + .S(S[i]), + .O(CO[i]) + ); + end endgenerate + +`else localparam CARRY4_COUNT = (WIDTH + 3) / 4; localparam MAX_WIDTH = CARRY4_COUNT * 4; @@ -97,34 +111,6 @@ module _80_xilinx_lcu (P, G, CI, CO); end end endgenerate - -`elsif _EXPLICIT_CARRY - - wire [WIDTH-1:0] C = {CO, CI}; - wire [WIDTH-1:0] S = P & ~G; - - generate for (i = 0; i < WIDTH; i = i + 1) begin:slice - MUXCY muxcy ( - .CI(C[i]), - .DI(G[i]), - .S(S[i]), - .O(CO[i]) - ); - end endgenerate - -`else - - wire [WIDTH-1:0] C = {CO, CI}; - wire [WIDTH-1:0] S = P & ~G; - - generate for (i = 0; i < WIDTH; i = i + 1) begin:slice - MUXCY muxcy ( - .CI(C[i]), - .DI(G[i]), - .S(S[i]), - .O(CO[i]) - ); - end endgenerate `endif endmodule @@ -161,79 +147,7 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO); genvar i; -`ifdef _CLB_CARRY - - localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4; - localparam MAX_WIDTH = CARRY4_COUNT * 4; - localparam PAD_WIDTH = MAX_WIDTH - Y_WIDTH; - - wire [MAX_WIDTH-1:0] S = {{PAD_WIDTH{1'b0}}, AA ^ BB}; - wire [MAX_WIDTH-1:0] DI = {{PAD_WIDTH{1'b0}}, AA & BB}; - - wire [MAX_WIDTH-1:0] C = CO; - - genvar i; - generate for (i = 0; i < CARRY4_COUNT; i = i + 1) begin:slice - - // Partially occupied CARRY4 - if ((i+1)*4 > Y_WIDTH) begin - - // First one - if (i == 0) begin - CARRY4 carry4_1st_part - ( - .CYINIT(CI), - .CI (1'd0), - .DI (DI[(Y_WIDTH - 1):i*4]), - .S (S [(Y_WIDTH - 1):i*4]), - .O (Y [(Y_WIDTH - 1):i*4]), - .CO (CO[(Y_WIDTH - 1):i*4]) - ); - // Another one - end else begin - CARRY4 carry4_part - ( - .CYINIT(1'd0), - .CI (C [i*4 - 1]), - .DI (DI[(Y_WIDTH - 1):i*4]), - .S (S [(Y_WIDTH - 1):i*4]), - .O (Y [(Y_WIDTH - 1):i*4]), - .CO (CO[(Y_WIDTH - 1):i*4]) - ); - end - - // Fully occupied CARRY4 - end else begin - - // First one - if (i == 0) begin - CARRY4 carry4_1st_full - ( - .CYINIT(CI), - .CI (1'd0), - .DI (DI[((i+1)*4 - 1):i*4]), - .S (S [((i+1)*4 - 1):i*4]), - .O (Y [((i+1)*4 - 1):i*4]), - .CO (CO[((i+1)*4 - 1):i*4]) - ); - // Another one - end else begin - CARRY4 carry4_full - ( - .CYINIT(1'd0), - .CI (C [i*4 - 1]), - .DI (DI[((i+1)*4 - 1):i*4]), - .S (S [((i+1)*4 - 1):i*4]), - .O (Y [((i+1)*4 - 1):i*4]), - .CO (CO[((i+1)*4 - 1):i*4]) - ); - end - - end - - end endgenerate - -`elsif _EXPLICIT_CARRY +`ifdef _EXPLICIT_CARRY wire [Y_WIDTH-1:0] S = AA ^ BB; wire [Y_WIDTH-1:0] DI = AA & BB; @@ -333,23 +247,74 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO); `else - wire [Y_WIDTH-1:0] S = AA ^ BB; - wire [Y_WIDTH-1:0] DI = AA & BB; + localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4; + localparam MAX_WIDTH = CARRY4_COUNT * 4; + localparam PAD_WIDTH = MAX_WIDTH - Y_WIDTH; - wire [Y_WIDTH-1:0] C = {CO, CI}; + wire [MAX_WIDTH-1:0] S = {{PAD_WIDTH{1'b0}}, AA ^ BB}; + wire [MAX_WIDTH-1:0] DI = {{PAD_WIDTH{1'b0}}, AA & BB}; + + wire [MAX_WIDTH-1:0] C = CO; + + genvar i; + generate for (i = 0; i < CARRY4_COUNT; i = i + 1) begin:slice + + // Partially occupied CARRY4 + if ((i+1)*4 > Y_WIDTH) begin + + // First one + if (i == 0) begin + CARRY4 carry4_1st_part + ( + .CYINIT(CI), + .CI (1'd0), + .DI (DI[(Y_WIDTH - 1):i*4]), + .S (S [(Y_WIDTH - 1):i*4]), + .O (Y [(Y_WIDTH - 1):i*4]), + .CO (CO[(Y_WIDTH - 1):i*4]) + ); + // Another one + end else begin + CARRY4 carry4_part + ( + .CYINIT(1'd0), + .CI (C [i*4 - 1]), + .DI (DI[(Y_WIDTH - 1):i*4]), + .S (S [(Y_WIDTH - 1):i*4]), + .O (Y [(Y_WIDTH - 1):i*4]), + .CO (CO[(Y_WIDTH - 1):i*4]) + ); + end + + // Fully occupied CARRY4 + end else begin + + // First one + if (i == 0) begin + CARRY4 carry4_1st_full + ( + .CYINIT(CI), + .CI (1'd0), + .DI (DI[((i+1)*4 - 1):i*4]), + .S (S [((i+1)*4 - 1):i*4]), + .O (Y [((i+1)*4 - 1):i*4]), + .CO (CO[((i+1)*4 - 1):i*4]) + ); + // Another one + end else begin + CARRY4 carry4_full + ( + .CYINIT(1'd0), + .CI (C [i*4 - 1]), + .DI (DI[((i+1)*4 - 1):i*4]), + .S (S [((i+1)*4 - 1):i*4]), + .O (Y [((i+1)*4 - 1):i*4]), + .CO (CO[((i+1)*4 - 1):i*4]) + ); + end + + end - generate for (i = 0; i < Y_WIDTH; i = i + 1) begin:slice - MUXCY muxcy ( - .CI(C[i]), - .DI(DI[i]), - .S(S[i]), - .O(CO[i]) - ); - XORCY xorcy ( - .CI(C[i]), - .LI(S[i]), - .O(Y[i]) - ); end endgenerate `endif diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index 3dc05cd10..5c3b5179d 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -518,8 +518,6 @@ struct SynthXilinxPass : public ScriptPass techmap_args += " -map +/xilinx/arith_map.v"; if (vpr) techmap_args += " -D _EXPLICIT_CARRY"; - else - techmap_args += " -D _CLB_CARRY"; } run("techmap " + techmap_args); run("opt -fast"); From 6692e5d558e7c7277153b7a3bd1623af0e57405d Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Fri, 17 Jan 2020 15:28:02 -0800 Subject: [PATCH 07/15] ice40_dsp: tolerant of fanout-less outputs, as well as all-zero inputs --- passes/pmgen/ice40_dsp.pmg | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg index 6b6d2b56f..9514e65d9 100644 --- a/passes/pmgen/ice40_dsp.pmg +++ b/passes/pmgen/ice40_dsp.pmg @@ -57,6 +57,9 @@ code sigA sigB sigH sigH.append(O[i]); } log_assert(nusers(O.extract_end(i)) <= 1); + + if (sigH.empty()) + reject; endcode code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol @@ -328,6 +331,8 @@ arg argD argQ clock clock_pol code dff = nullptr; + if (argQ.empty()) + reject; for (auto c : argQ.chunks()) { if (!c.wire) reject; From 4985318263a8113563c9c62c60a9d4d6ee0a4f4e Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Fri, 17 Jan 2020 15:37:52 -0800 Subject: [PATCH 08/15] ice40_dsp: add default values for parameters --- passes/pmgen/ice40_dsp.cc | 8 ++++---- passes/pmgen/ice40_dsp.pmg | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc index f60e67158..202a43f0c 100644 --- a/passes/pmgen/ice40_dsp.cc +++ b/passes/pmgen/ice40_dsp.cc @@ -73,11 +73,11 @@ void create_ice40_dsp(ice40_dsp_pm &pm) // SB_MAC16 Input Interface SigSpec A = st.sigA; - A.extend_u0(16, st.mul->getParam(ID(A_SIGNED)).as_bool()); + A.extend_u0(16, st.mul->connections_.at(ID(A_SIGNED), State::S0).as_bool()); log_assert(GetSize(A) == 16); SigSpec B = st.sigB; - B.extend_u0(16, st.mul->getParam(ID(B_SIGNED)).as_bool()); + B.extend_u0(16, st.mul->connections_.at(ID(B_SIGNED), State::S0).as_bool()); log_assert(GetSize(B) == 16); SigSpec CD = st.sigCD; @@ -248,8 +248,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setParam(ID(BOTADDSUB_CARRYSELECT), Const(0, 2)); cell->setParam(ID(MODE_8x8), State::S0); - cell->setParam(ID(A_SIGNED), st.mul->getParam(ID(A_SIGNED)).as_bool()); - cell->setParam(ID(B_SIGNED), st.mul->getParam(ID(B_SIGNED)).as_bool()); + cell->setParam(ID(A_SIGNED), st.mul->parameters.at(ID(A_SIGNED), State::S0).as_bool()); + cell->setParam(ID(B_SIGNED), st.mul->parameters.at(ID(B_SIGNED), State::S0).as_bool()); if (st.ffO) { if (st.o_lo) diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg index 9514e65d9..fca307453 100644 --- a/passes/pmgen/ice40_dsp.pmg +++ b/passes/pmgen/ice40_dsp.pmg @@ -63,7 +63,7 @@ code sigA sigB sigH endcode code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol - if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) { + if (mul->type != \SB_MAC16 || !param(mul, \A_REG, State::S0).as_bool()) { argQ = sigA; subpattern(in_dffe); if (dff) { @@ -84,7 +84,7 @@ code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol endcode code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol - if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) { + if (mul->type != \SB_MAC16 || !param(mul, \B_REG, State::S0).as_bool()) { argQ = sigB; subpattern(in_dffe); if (dff) { @@ -107,7 +107,7 @@ endcode code argD ffFJKG sigH clock clock_pol if (nusers(sigH) == 2 && (mul->type != \SB_MAC16 || - (!param(mul, \TOP_8x8_MULT_REG).as_bool() && !param(mul, \BOT_8x8_MULT_REG).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool()))) { + (!param(mul, \TOP_8x8_MULT_REG, State::S0).as_bool() && !param(mul, \BOT_8x8_MULT_REG, State::S0).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1, State::S0).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1, State::S0).as_bool()))) { argD = sigH; subpattern(out_dffe); if (dff) { @@ -146,7 +146,7 @@ endcode code argD ffH sigH sigO clock clock_pol if (ffFJKG && nusers(sigH) == 2 && - (mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) { + (mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2, State::S0).as_bool())) { argD = sigH; subpattern(out_dffe); if (dff) { @@ -177,7 +177,7 @@ reject_ffH: ; endcode match add - if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT).as_int() == 3) + if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT, State::S0).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT, State::S0).as_int() == 3) select add->type.in($add) choice AB {\A, \B} @@ -203,7 +203,7 @@ code sigCD sigO cd_signed if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width)) reject; // If accumulator, check adder width and signedness - if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(add, \A_SIGNED).as_bool())) + if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED, State::S0).as_bool() != param(add, \A_SIGNED).as_bool())) reject; sigO = port(add, \Y); @@ -278,7 +278,7 @@ endcode code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol if (!sigCD.empty() && sigCD != sigO && - (mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) { + (mul->type != \SB_MAC16 || (!param(mul, \C_REG, State::S0).as_bool() && !param(mul, \D_REG, State::S0).as_bool()))) { argQ = sigCD; subpattern(in_dffe); if (dff) { From ad6c49fff12e27d99c1fc15850857e5129dd76ee Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Fri, 17 Jan 2020 15:38:26 -0800 Subject: [PATCH 09/15] ice40_dsp: add test --- tests/arch/ice40/ice40_dsp.ys | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/arch/ice40/ice40_dsp.ys diff --git a/tests/arch/ice40/ice40_dsp.ys b/tests/arch/ice40/ice40_dsp.ys new file mode 100644 index 000000000..250273859 --- /dev/null +++ b/tests/arch/ice40/ice40_dsp.ys @@ -0,0 +1,11 @@ +read_verilog < Date: Fri, 17 Jan 2020 15:41:55 -0800 Subject: [PATCH 10/15] synth_ice40: call wreduce before mul2dsp --- techlibs/ice40/synth_ice40.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index 121bcff1f..d92e40726 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -273,7 +273,8 @@ struct SynthIce40Pass : public ScriptPass run("opt_expr"); run("opt_clean"); if (help_mode || dsp) { - run("memory_dff"); + run("memory_dff"); // ice40_dsp will merge registers, reserve memory port registers first + run("wreduce t:$mul"); run("techmap -map +/mul2dsp.v -map +/ice40/dsp_map.v -D DSP_A_MAXWIDTH=16 -D DSP_B_MAXWIDTH=16 " "-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_Y_MINWIDTH=11 " "-D DSP_NAME=$__MUL16X16", "(if -dsp)"); From 5507c328fff7b534abcba4186b2e5b1e26c4ad5e Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Fri, 17 Jan 2020 15:57:52 -0800 Subject: [PATCH 11/15] Add #1644 testcase --- tests/arch/ice40/bug1644.il.gz | Bin 0 -> 25669 bytes tests/arch/ice40/bug1644.ys | 2 ++ 2 files changed, 2 insertions(+) create mode 100644 tests/arch/ice40/bug1644.il.gz create mode 100644 tests/arch/ice40/bug1644.ys diff --git a/tests/arch/ice40/bug1644.il.gz b/tests/arch/ice40/bug1644.il.gz new file mode 100644 index 0000000000000000000000000000000000000000..363c510efe8028d2593da0ec83e4caea16a30ca8 GIT binary patch literal 25669 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 Z-?M84_Ggu%{#;x6{{qE6g`vMe0{|QxOz8js literal 0 HcmV?d00001 diff --git a/tests/arch/ice40/bug1644.ys b/tests/arch/ice40/bug1644.ys new file mode 100644 index 000000000..5950f0e3c --- /dev/null +++ b/tests/arch/ice40/bug1644.ys @@ -0,0 +1,2 @@ +read_ilang bug1644.il.gz +synth_ice40 -top top -dsp -json adc_dac_pass_through.json -run :map_bram From ee500b6d8e7a6cd10a8187a3fc69b46f3d155a91 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Fri, 17 Jan 2020 16:05:10 -0800 Subject: [PATCH 12/15] xilinx_dsp: add parameter defaults --- passes/pmgen/xilinx_dsp.pmg | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/passes/pmgen/xilinx_dsp.pmg b/passes/pmgen/xilinx_dsp.pmg index 5d3b9c2eb..b9a4b0864 100644 --- a/passes/pmgen/xilinx_dsp.pmg +++ b/passes/pmgen/xilinx_dsp.pmg @@ -120,7 +120,7 @@ endcode // reset functionality, using a subpattern discussed above) // If matched, treat 'A' input as input of ADREG code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock - if (param(dsp, \ADREG).as_int() == 0) { + if (param(dsp, \ADREG, 1).as_int() == 0) { argQ = sigA; subpattern(in_dffe); if (dff) { @@ -176,7 +176,7 @@ code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cem // Only search for ffA2 if there was a pre-adder // (otherwise ffA2 would have been matched as ffAD) if (preAdd) { - if (param(dsp, \AREG).as_int() == 0) { + if (param(dsp, \AREG, 1).as_int() == 0) { argQ = sigA; subpattern(in_dffe); if (dff) { @@ -237,7 +237,7 @@ endcode // (5) Match 'B' input for B2REG // If B2REG, then match 'B' input for B1REG code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemux ffB1rstmux ffB1cepol - if (param(dsp, \BREG).as_int() == 0) { + if (param(dsp, \BREG, 1).as_int() == 0) { argQ = sigB; subpattern(in_dffe); if (dff) { @@ -287,7 +287,7 @@ endcode // (6) Match 'D' input for DREG code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock - if (param(dsp, \DREG).as_int() == 0) { + if (param(dsp, \DREG, 1).as_int() == 0) { argQ = sigD; subpattern(in_dffe); if (dff) { @@ -308,7 +308,7 @@ endcode // (7) Match 'P' output that exclusively drives an MREG code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock - if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) { + if (param(dsp, \MREG, 1).as_int() == 0 && nusers(sigM) == 2) { argD = sigM; subpattern(out_dffe); if (dff) { @@ -335,7 +335,7 @@ endcode // recognised in xilinx_dsp.cc). match postAdd // Ensure that Z mux is not already used - if port(dsp, \OPMODE, SigSpec()).extract(4,3).is_fully_zero() + if port(dsp, \OPMODE, SigSpec(0, 7)).extract(4,3).is_fully_zero() select postAdd->type.in($add) select GetSize(port(postAdd, \Y)) <= 48 @@ -363,7 +363,7 @@ endcode // (9) Match 'P' output that exclusively drives a PREG code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock - if (param(dsp, \PREG).as_int() == 0) { + if (param(dsp, \PREG, 1).as_int() == 0) { int users = 2; // If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux if (ffMcemux && !postAdd) users++; From e17f3f8c63603746ad3aa33e9900d91e9b86db39 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Fri, 17 Jan 2020 16:06:20 -0800 Subject: [PATCH 13/15] Consistency --- passes/pmgen/ice40_dsp.pmg | 8 +++++--- passes/pmgen/xilinx_dsp.pmg | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg index fca307453..9d649cb98 100644 --- a/passes/pmgen/ice40_dsp.pmg +++ b/passes/pmgen/ice40_dsp.pmg @@ -56,10 +56,12 @@ code sigA sigB sigH break; sigH.append(O[i]); } - log_assert(nusers(O.extract_end(i)) <= 1); - - if (sigH.empty()) + // This sigM could have no users if downstream sinks (e.g. $add) is + // narrower than $mul result, for example + if (i == 0) reject; + + log_assert(nusers(O.extract_end(i)) <= 1); endcode code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol diff --git a/passes/pmgen/xilinx_dsp.pmg b/passes/pmgen/xilinx_dsp.pmg index b9a4b0864..20925c0dc 100644 --- a/passes/pmgen/xilinx_dsp.pmg +++ b/passes/pmgen/xilinx_dsp.pmg @@ -460,7 +460,7 @@ arg argD argQ clock code dff = nullptr; - if (GetSize(argQ) == 0) + if (argQ.empty() == 0) reject; for (const auto &c : argQ.chunks()) { // Abandon matches when 'Q' is a constant From db68e4c2a7a39eda46863fba8b8c8313a831f606 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Fri, 17 Jan 2020 16:08:04 -0800 Subject: [PATCH 14/15] ice40_dsp: fix typo --- passes/pmgen/ice40_dsp.cc | 4 ++-- tests/arch/xilinx/xilinx_dsp.ys | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 tests/arch/xilinx/xilinx_dsp.ys diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc index 202a43f0c..c364cd91a 100644 --- a/passes/pmgen/ice40_dsp.cc +++ b/passes/pmgen/ice40_dsp.cc @@ -73,11 +73,11 @@ void create_ice40_dsp(ice40_dsp_pm &pm) // SB_MAC16 Input Interface SigSpec A = st.sigA; - A.extend_u0(16, st.mul->connections_.at(ID(A_SIGNED), State::S0).as_bool()); + A.extend_u0(16, st.mul->parameters.at(ID(A_SIGNED), State::S0).as_bool()); log_assert(GetSize(A) == 16); SigSpec B = st.sigB; - B.extend_u0(16, st.mul->connections_.at(ID(B_SIGNED), State::S0).as_bool()); + B.extend_u0(16, st.mul->parameters.at(ID(B_SIGNED), State::S0).as_bool()); log_assert(GetSize(B) == 16); SigSpec CD = st.sigCD; diff --git a/tests/arch/xilinx/xilinx_dsp.ys b/tests/arch/xilinx/xilinx_dsp.ys new file mode 100644 index 000000000..3b9f52930 --- /dev/null +++ b/tests/arch/xilinx/xilinx_dsp.ys @@ -0,0 +1,11 @@ +read_verilog < Date: Fri, 17 Jan 2020 17:07:03 -0800 Subject: [PATCH 15/15] xilinx_dsp: another typo; move xilinx specific test --- passes/pmgen/xilinx_dsp.pmg | 2 +- tests/{various => arch/xilinx}/bug1462.ys | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/{various => arch/xilinx}/bug1462.ys (100%) diff --git a/passes/pmgen/xilinx_dsp.pmg b/passes/pmgen/xilinx_dsp.pmg index 20925c0dc..af47ab111 100644 --- a/passes/pmgen/xilinx_dsp.pmg +++ b/passes/pmgen/xilinx_dsp.pmg @@ -460,7 +460,7 @@ arg argD argQ clock code dff = nullptr; - if (argQ.empty() == 0) + if (argQ.empty()) reject; for (const auto &c : argQ.chunks()) { // Abandon matches when 'Q' is a constant diff --git a/tests/various/bug1462.ys b/tests/arch/xilinx/bug1462.ys similarity index 100% rename from tests/various/bug1462.ys rename to tests/arch/xilinx/bug1462.ys