Merge remote-tracking branch 'origin/xaig_dff' into eddie/exp
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index aa8f941..52e7ac7 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc
@@ -134,6 +134,8 @@ dict<string, int> min_limits, max_limits; bool or_next_if_better, make_transp, make_outreg; char shuffle_enable; + IdString attr; + Const value; }; dict<IdString, vector<bram_t>> brams; @@ -327,6 +329,13 @@ continue; } + if (GetSize(tokens) >= 2 && tokens[0] == "attribute") { + data.attr = RTLIL::escape_id(tokens[1]); + if (GetSize(tokens) > 2) + data.value = tokens[2]; + continue; + } + syntax_error(); } } @@ -788,7 +797,6 @@ match_properties["dcells"] = ((mem_width + bram.dbits - 1) / bram.dbits); match_properties["acells"] = ((mem_size + (1 << bram.abits) - 1) / (1 << bram.abits)); match_properties["cells"] = match_properties["dcells"] * match_properties["acells"] * match_properties["dups"]; - log(" Updated properties: dups=%d waste=%d efficiency=%d\n", match_properties["dups"], match_properties["waste"], match_properties["efficiency"]); @@ -806,13 +814,28 @@ if (!match_properties.count(it.first)) log_error("Unknown property '%s' in match rule for bram type %s.\n", it.first.c_str(), log_id(match.name)); - if (match_properties[it.first] <= it.second) - continue; log(" Rule for bram type %s rejected: requirement 'max %s %d' not met.\n", log_id(match.name), it.first.c_str(), it.second); return false; } + if (!match.attr.empty()) { + auto it = cell->attributes.find(match.attr); + if (it == cell->attributes.end()) { + if (!match.value.empty()) + log(" Rule for bram type %s rejected: requirement 'attribute %s=\"%s\"' not met.\n", + log_id(match.name), log_id(match.attr), match.value.decode_string().c_str()); + return false; + } + else { + if (it->second != match.value) { + log(" Rule for bram type %s rejected: requirement 'attribute %s=\"%s\"' not met.\n", + log_id(match.name), log_id(match.attr), match.value.decode_string().c_str()); + return false; + } + } + } + if (mode == 1) return true; } @@ -1100,6 +1123,24 @@ goto next_match_rule; } + if (!match.attr.empty()) { + auto it = cell->attributes.find(match.attr); + if (it == cell->attributes.end()) { + if (!match.value.empty()) { + log(" Rule for bram type %s rejected: requirement 'attribute %s=\"%s\"' not met.\n", + log_id(match.name), log_id(match.attr), match.value.decode_string().c_str()); + goto next_match_rule; + } + } + else { + if (it->second != match.value) { + log(" Rule for bram type %s rejected: requirement 'attribute %s=\"%s\"' not met.\n", + log_id(match.name), log_id(match.attr), match.value.decode_string().c_str()); + goto next_match_rule; + } + } + } + log(" Rule #%d for bram type %s (variant %d) accepted.\n", i+1, log_id(bram.name), bram.variant); if (or_next_if_better || !best_rule_cache.empty()) @@ -1242,6 +1283,9 @@ log("A match containing the command 'shuffle_enable A' will re-organize\n"); log("the data bits to accommodate the enable pattern of port A.\n"); log("\n"); + log("A match containing the command 'attribute' will bypass min bits/efficiency\n"); + log("to select the type of memory.\n"); + log("\n"); } void execute(vector<string> args, Design *design) YS_OVERRIDE {
diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc index 6acbce6..9dcb3f0 100644 --- a/passes/memory/memory_collect.cc +++ b/passes/memory/memory_collect.cc
@@ -218,6 +218,10 @@ mem->setPort("\\RD_DATA", sig_rd_data); mem->setPort("\\RD_EN", sig_rd_en); + // Copy attributes from RTLIL memory to $mem + for (auto attr : memory->attributes) + mem->attributes[attr.first] = attr.second; + for (auto c : memcells) module->remove(c);
diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc index 145d2eb..a4d9d42 100644 --- a/passes/pmgen/Makefile.inc +++ b/passes/pmgen/Makefile.inc
@@ -45,3 +45,9 @@ OBJS += passes/pmgen/xilinx_srl.o passes/pmgen/xilinx_srl.o: passes/pmgen/xilinx_srl_pm.h $(eval $(call add_extra_objs,passes/pmgen/xilinx_srl_pm.h)) + +# -------------------------------------- + +OBJS += passes/pmgen/xilinx_dff.o +passes/pmgen/xilinx_dff.o: passes/pmgen/xilinx_dff_pm.h +$(eval $(call add_extra_objs,passes/pmgen/xilinx_dff_pm.h))
diff --git a/passes/pmgen/xilinx_dff.cc b/passes/pmgen/xilinx_dff.cc new file mode 100644 index 0000000..bf0c735 --- /dev/null +++ b/passes/pmgen/xilinx_dff.cc
@@ -0,0 +1,62 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 2019 Eddie Hung <eddie@fpgeh.com> + * + * 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 + +#include "passes/pmgen/xilinx_dff_pm.h" + +struct XilinxDffPass : public Pass { + XilinxDffPass() : Pass("xilinx_dff", "Xilinx: TODO") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" xilinx_dff [options] [selection]\n"); + log("\n"); + log("TODO\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing XILINX_DFF pass (TODO).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-singleton") { + // singleton_mode = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) { + xilinx_dff_pm pm(module, module->selected_cells()); + pm.run_xilinx_dff(); + } + } +} XilinxDffPass; + +PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/xilinx_dff.pmg b/passes/pmgen/xilinx_dff.pmg new file mode 100644 index 0000000..f5637d4 --- /dev/null +++ b/passes/pmgen/xilinx_dff.pmg
@@ -0,0 +1,26 @@ +pattern xilinx_dff + +match fd + select fd->type.in(\FDRE) + select port(fd, \R).is_fully_zero() + select port(fd, \CE).is_fully_ones() +endmatch + +match lut + select lut->type.in(\LUT2) + index <SigSpec> port(lut, \O) === port(fd, \D) +endmatch + +code + if (lut->type == \LUT2) { + if (param(lut, \INIT) == Const::from_string("0100")) { + fd->setPort(\D, port(lut, \I0)); + fd->setPort(\R, port(lut, \I1)); + } + else if (param(lut, \INIT) == Const::from_string("0010")) { + fd->setPort(\R, port(lut, \I0)); + fd->setPort(\D, port(lut, \I1)); + } + } + else log_abort(); +endcode
diff --git a/passes/pmgen/xilinx_dsp.pmg b/passes/pmgen/xilinx_dsp.pmg index 0ba5290..5d3b9c2 100644 --- a/passes/pmgen/xilinx_dsp.pmg +++ b/passes/pmgen/xilinx_dsp.pmg
@@ -347,9 +347,9 @@ index <SigBit> port(postAdd, AB)[0] === sigP[0] filter GetSize(port(postAdd, AB)) >= GetSize(sigP) filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP - // Check that remainder of AB is a sign-extension - define <bool> AB_SIGNED (param(postAdd, AB == \A ? \A_SIGNED : \B_SIGNED).as_bool()) - filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(AB_SIGNED ? sigP[GetSize(sigP)-1] : State::S0, GetSize(port(postAdd, AB))-GetSize(sigP)) + // Check that remainder of AB is a sign- or zero-extension + filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(State::S0, GetSize(port(postAdd, AB))-GetSize(sigP)) + set postAddAB AB optional endmatch
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index 5bc5538..239937f 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc
@@ -561,6 +561,7 @@ else techmap_args += " -map " + ff_map_file; run("techmap " + techmap_args); + run("xilinx_dff"); } if (check_label("finalize")) {
diff --git a/techlibs/xilinx/xc7_xcu_brams.txt b/techlibs/xilinx/xc7_xcu_brams.txt index f116111..7d3d997 100644 --- a/techlibs/xilinx/xc7_xcu_brams.txt +++ b/techlibs/xilinx/xc7_xcu_brams.txt
@@ -81,10 +81,18 @@ endmatch match $__XILINX_RAMB18_SDP - min bits 4096 + min bits 1024 min efficiency 5 shuffle_enable B make_transp + attribute ram_style + or_next_if_better +endmatch + +match $__XILINX_RAMB18_SDP + shuffle_enable B + make_transp + attribute ram_style block or_next_if_better endmatch @@ -97,7 +105,7 @@ endmatch match $__XILINX_RAMB18_TDP - min bits 4096 + min bits 1024 min efficiency 5 shuffle_enable B make_transp
diff --git a/tests/arch/xilinx/dsp_fastfir.ys b/tests/arch/xilinx/dsp_fastfir.ys new file mode 100644 index 0000000..0067a82 --- /dev/null +++ b/tests/arch/xilinx/dsp_fastfir.ys
@@ -0,0 +1,69 @@ +read_verilog <<EOT +// Citation https://github.com/ZipCPU/dspfilters/blob/master/rtl/fastfir.v +module fastfir_dynamictaps(i_clk, i_reset, i_tap_wr, i_tap, i_ce, i_sample, o_result); + wire [30:0] _00_; + wire [23:0] _01_; + wire [11:0] _02_; + wire [30:0] _03_; + wire [23:0] _04_; + wire [30:0] _05_; + wire [23:0] _06_; + wire [30:0] _07_; + wire [23:0] _08_; + wire [11:0] _09_; + wire [30:0] _10_; + wire [23:0] _11_; + wire [30:0] _12_; + wire [23:0] _13_; + wire [11:0] \fir.FILTER[0].tapk.delayed_sample ; + reg [30:0] \fir.FILTER[0].tapk.o_acc = 31'h00000000; + wire [11:0] \fir.FILTER[0].tapk.o_sample ; + reg [23:0] \fir.FILTER[0].tapk.product ; + reg [11:0] \fir.FILTER[0].tapk.tap = 12'h000; + wire [11:0] \fir.FILTER[1].tapk.delayed_sample ; + wire [30:0] \fir.FILTER[1].tapk.o_acc ; + wire [11:0] \fir.FILTER[1].tapk.o_sample ; + reg [23:0] \fir.FILTER[1].tapk.product ; + reg [11:0] \fir.FILTER[1].tapk.tap = 12'h000; + input i_ce; + input i_clk; + input i_reset; + input [11:0] i_sample; + input [11:0] i_tap; + input i_tap_wr; + output [30:0] o_result; + reg [30:0] o_result; + assign _03_ = 31'h00000000 + { \fir.FILTER[0].tapk.product [23], \fir.FILTER[0].tapk.product [23], \fir.FILTER[0].tapk.product [23], \fir.FILTER[0].tapk.product [23], \fir.FILTER[0].tapk.product [23], \fir.FILTER[0].tapk.product [23], \fir.FILTER[0].tapk.product [23], \fir.FILTER[0].tapk.product }; + assign _04_ = $signed(\fir.FILTER[0].tapk.tap ) * $signed(i_sample); + always @(posedge i_clk) + \fir.FILTER[0].tapk.tap <= _02_; + always @(posedge i_clk) + \fir.FILTER[0].tapk.o_acc <= _00_; + always @(posedge i_clk) + \fir.FILTER[0].tapk.product <= _01_; + assign _02_ = i_tap_wr ? i_tap : \fir.FILTER[0].tapk.tap ; + assign _05_ = i_ce ? _03_ : \fir.FILTER[0].tapk.o_acc ; + assign _00_ = i_reset ? 31'h00000000 : _05_; + assign _06_ = i_ce ? _04_ : \fir.FILTER[0].tapk.product ; + assign _01_ = i_reset ? 24'h000000 : _06_; + assign _10_ = \fir.FILTER[0].tapk.o_acc + { \fir.FILTER[1].tapk.product [23], \fir.FILTER[1].tapk.product [23], \fir.FILTER[1].tapk.product [23], \fir.FILTER[1].tapk.product [23], \fir.FILTER[1].tapk.product [23], \fir.FILTER[1].tapk.product [23], \fir.FILTER[1].tapk.product [23], \fir.FILTER[1].tapk.product }; + assign _11_ = $signed(\fir.FILTER[1].tapk.tap ) * $signed(i_sample); + always @(posedge i_clk) + \fir.FILTER[1].tapk.tap <= _09_; + always @(posedge i_clk) + o_result <= _07_; + always @(posedge i_clk) + \fir.FILTER[1].tapk.product <= _08_; + assign _09_ = i_tap_wr ? \fir.FILTER[0].tapk.tap : \fir.FILTER[1].tapk.tap ; + assign _12_ = i_ce ? _10_ : o_result; + assign _07_ = i_reset ? 31'h00000000 : _12_; + assign _13_ = i_ce ? _11_ : \fir.FILTER[1].tapk.product ; + assign _08_ = i_reset ? 24'h000000 : _13_; + assign \fir.FILTER[1].tapk.o_acc = o_result; +endmodule +EOT + +synth_xilinx +cd fastfir_dynamictaps +select -assert-count 2 t:DSP48E1 +select -assert-none t:* t:DSP48E1 %d t:BUFG %d