aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG1
-rw-r--r--passes/cmds/stat.cc3
-rw-r--r--passes/techmap/abc9.cc3
-rw-r--r--passes/techmap/shregmap.cc164
-rw-r--r--techlibs/xilinx/Makefile.inc3
-rw-r--r--techlibs/xilinx/cells.box19
-rw-r--r--techlibs/xilinx/cells.lut12
-rw-r--r--techlibs/xilinx/cells_box.v19
-rw-r--r--techlibs/xilinx/cells_map.v203
-rw-r--r--techlibs/xilinx/cells_sim.v39
-rw-r--r--techlibs/xilinx/cells_xtra.sh4
-rw-r--r--techlibs/xilinx/cells_xtra.v16
-rw-r--r--techlibs/xilinx/ff_map.v13
-rw-r--r--techlibs/xilinx/synth_xilinx.cc66
14 files changed, 521 insertions, 44 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 42e01645e..36b64e111 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -16,6 +16,7 @@ Yosys 0.8 .. Yosys 0.8-dev
- Added "gate2lut.v" techmap rule
- Added "rename -src"
- Added "equiv_opt" pass
+ - "synth_xilinx" to now infer hard shift registers, using new "shregmap -tech xilinx"
Yosys 0.7 .. Yosys 0.8
diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc
index 54f4ea817..3909c4c8c 100644
--- a/passes/cmds/stat.cc
+++ b/passes/cmds/stat.cc
@@ -269,6 +269,9 @@ struct StatPass : public Pass {
if (mod->get_bool_attribute("\\top"))
top_mod = mod;
+ if (mod->attributes.count("\\abc_box_id"))
+ continue;
+
statdata_t data(design, mod, width_mode, cell_area);
mod_stat[mod->name] = data;
diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc
index 18f860e36..3cee5a586 100644
--- a/passes/techmap/abc9.cc
+++ b/passes/techmap/abc9.cc
@@ -1410,6 +1410,9 @@ struct Abc9Pass : public Pass {
continue;
}
+ if (mod->attributes.count("\\abc_box_id"))
+ continue;
+
assign_map.set(mod);
signal_init.clear();
diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc
index f20863ba0..a541b33be 100644
--- a/passes/techmap/shregmap.cc
+++ b/passes/techmap/shregmap.cc
@@ -26,7 +26,9 @@ PRIVATE_NAMESPACE_BEGIN
struct ShregmapTech
{
virtual ~ShregmapTech() { }
- virtual bool analyze(vector<int> &taps) = 0;
+ virtual void init(const Module * /*module*/, const SigMap &/*sigmap*/) {}
+ virtual void non_chain_user(const SigBit &/*bit*/, const Cell* /*cell*/, IdString /*port*/) {}
+ virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) = 0;
virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
};
@@ -54,7 +56,7 @@ struct ShregmapOptions
struct ShregmapTechGreenpak4 : ShregmapTech
{
- bool analyze(vector<int> &taps)
+ bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/)
{
if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
taps.clear();
@@ -91,6 +93,145 @@ struct ShregmapTechGreenpak4 : ShregmapTech
}
};
+struct ShregmapTechXilinx7 : ShregmapTech
+{
+ dict<SigBit, std::tuple<Cell*,int,int>> sigbit_to_shiftx_offset;
+ const ShregmapOptions &opts;
+
+ ShregmapTechXilinx7(const ShregmapOptions &opts) : opts(opts) {}
+
+ virtual void init(const Module* module, const SigMap &sigmap) override
+ {
+ for (const auto &i : module->cells_) {
+ auto cell = i.second;
+ if (cell->type == "$shiftx") {
+ if (cell->getParam("\\Y_WIDTH") != 1) continue;
+ int j = 0;
+ for (auto bit : sigmap(cell->getPort("\\A")))
+ sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j++, 0);
+ log_assert(j == cell->getParam("\\A_WIDTH").as_int());
+ }
+ else if (cell->type == "$mux") {
+ int j = 0;
+ for (auto bit : sigmap(cell->getPort("\\A")))
+ sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++);
+ j = 0;
+ for (auto bit : sigmap(cell->getPort("\\B")))
+ sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 1, j++);
+ }
+ }
+ }
+
+ virtual void non_chain_user(const SigBit &bit, const Cell *cell, IdString port) override
+ {
+ auto it = sigbit_to_shiftx_offset.find(bit);
+ if (it == sigbit_to_shiftx_offset.end())
+ return;
+ if (cell) {
+ if (cell->type == "$shiftx" && port == "\\A")
+ return;
+ if (cell->type == "$mux" && (port == "\\A" || port == "\\B"))
+ return;
+ }
+ sigbit_to_shiftx_offset.erase(it);
+ }
+
+ virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) override
+ {
+ if (GetSize(taps) == 1)
+ return taps[0] >= opts.minlen-1 && sigbit_to_shiftx_offset.count(qbits[0]);
+
+ if (taps.back() < opts.minlen-1)
+ return false;
+
+ Cell *shiftx = nullptr;
+ int group = 0;
+ for (int i = 0; i < GetSize(taps); ++i) {
+ auto it = sigbit_to_shiftx_offset.find(qbits[i]);
+ if (it == sigbit_to_shiftx_offset.end())
+ return false;
+
+ // Check taps are sequential
+ if (i != taps[i])
+ return false;
+ // Check taps are not connected to a shift register,
+ // or sequential to the same shift register
+ if (i == 0) {
+ int offset;
+ std::tie(shiftx,offset,group) = it->second;
+ if (offset != i)
+ return false;
+ }
+ else {
+ Cell *shiftx_ = std::get<0>(it->second);
+ if (shiftx_ != shiftx)
+ return false;
+ int offset = std::get<1>(it->second);
+ if (offset != i)
+ return false;
+ int group_ = std::get<2>(it->second);
+ if (group_ != group)
+ return false;
+ }
+ }
+ log_assert(shiftx);
+
+ // Only map if $shiftx exclusively covers the shift register
+ if (shiftx->type == "$shiftx") {
+ if (GetSize(taps) != shiftx->getParam("\\A_WIDTH").as_int())
+ return false;
+ }
+ else if (shiftx->type == "$mux") {
+ if (GetSize(taps) != 2)
+ return false;
+ }
+ else log_abort();
+
+ return true;
+ }
+
+ virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) override
+ {
+ const auto &tap = *taps.begin();
+ auto bit = tap.second;
+
+ auto it = sigbit_to_shiftx_offset.find(bit);
+ log_assert(it != sigbit_to_shiftx_offset.end());
+
+ auto newcell = cell->module->addCell(NEW_ID, "$__XILINX_SHREG_");
+ newcell->set_src_attribute(cell->get_src_attribute());
+ newcell->setParam("\\DEPTH", cell->getParam("\\DEPTH"));
+ newcell->setParam("\\INIT", cell->getParam("\\INIT"));
+ newcell->setParam("\\CLKPOL", cell->getParam("\\CLKPOL"));
+ newcell->setParam("\\ENPOL", cell->getParam("\\ENPOL"));
+
+ newcell->setPort("\\C", cell->getPort("\\C"));
+ newcell->setPort("\\D", cell->getPort("\\D"));
+ if (cell->hasPort("\\E"))
+ newcell->setPort("\\E", cell->getPort("\\E"));
+
+ Cell* shiftx = std::get<0>(it->second);
+ RTLIL::SigSpec l_wire, q_wire;
+ if (shiftx->type == "$shiftx") {
+ l_wire = shiftx->getPort("\\B");
+ q_wire = shiftx->getPort("\\Y");
+ shiftx->setPort("\\Y", cell->module->addWire(NEW_ID));
+ }
+ else if (shiftx->type == "$mux") {
+ l_wire = shiftx->getPort("\\S");
+ q_wire = shiftx->getPort("\\Y");
+ shiftx->setPort("\\Y", cell->module->addWire(NEW_ID));
+ }
+ else log_abort();
+
+ newcell->setPort("\\Q", q_wire);
+ newcell->setPort("\\L", l_wire);
+
+ return false;
+ }
+};
+
+
struct ShregmapWorker
{
Module *module;
@@ -113,8 +254,10 @@ struct ShregmapWorker
for (auto wire : module->wires())
{
if (wire->port_output || wire->get_bool_attribute("\\keep")) {
- for (auto bit : sigmap(wire))
+ for (auto bit : sigmap(wire)) {
sigbit_with_non_chain_users.insert(bit);
+ if (opts.tech) opts.tech->non_chain_user(bit, nullptr, {});
+ }
}
if (wire->attributes.count("\\init")) {
@@ -152,8 +295,10 @@ struct ShregmapWorker
for (auto conn : cell->connections())
if (cell->input(conn.first))
- for (auto bit : sigmap(conn.second))
+ for (auto bit : sigmap(conn.second)) {
sigbit_with_non_chain_users.insert(bit);
+ if (opts.tech) opts.tech->non_chain_user(bit, cell, conn.first);
+ }
}
}
@@ -258,7 +403,7 @@ struct ShregmapWorker
if (taps.empty() || taps.back() < depth-1)
taps.push_back(depth-1);
- if (opts.tech->analyze(taps))
+ if (opts.tech->analyze(taps, qbits))
break;
taps.pop_back();
@@ -377,6 +522,9 @@ struct ShregmapWorker
ShregmapWorker(Module *module, const ShregmapOptions &opts) :
module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
{
+ if (opts.tech)
+ opts.tech->init(module, sigmap);
+
make_sigbit_chain_next_prev();
find_chain_start_cells();
@@ -501,6 +649,12 @@ struct ShregmapPass : public Pass {
clkpol = "pos";
opts.zinit = true;
opts.tech = new ShregmapTechGreenpak4;
+ }
+ else if (tech == "xilinx") {
+ opts.init = true;
+ opts.params = true;
+ enpol = "any_or_none";
+ opts.tech = new ShregmapTechXilinx7(opts);
} else {
argidx--;
break;
diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc
index d68f03bb4..43be55d51 100644
--- a/techlibs/xilinx/Makefile.inc
+++ b/techlibs/xilinx/Makefile.inc
@@ -30,6 +30,9 @@ $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/drams_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/arith_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/ff_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lut_map.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells.box))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_box.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells.lut))
$(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_36.vh))
$(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_32.vh))
diff --git a/techlibs/xilinx/cells.box b/techlibs/xilinx/cells.box
new file mode 100644
index 000000000..5ad284f47
--- /dev/null
+++ b/techlibs/xilinx/cells.box
@@ -0,0 +1,19 @@
+# Max delays from https://pastebin.com/v2hrcksd
+# from https://github.com/SymbiFlow/prjxray/pull/706#issuecomment-479380321
+
+# F7BMUX slower than F7AMUX
+# Inputs: 0 1 S0
+# Outputs: OUT
+F7BMUX 1 1 3 1
+217 223 296
+
+# Inputs: 0 1 S0
+# Outputs: OUT
+MUXF8 2 1 3 1
+104 94 273
+
+MUXCY 3 1 3 1
+1 1 1
+
+XORCY 4 1 2 1
+1 1
diff --git a/techlibs/xilinx/cells.lut b/techlibs/xilinx/cells.lut
new file mode 100644
index 000000000..c6bc7b1f7
--- /dev/null
+++ b/techlibs/xilinx/cells.lut
@@ -0,0 +1,12 @@
+# Max delays from https://pastebin.com/v2hrcksd
+# from https://github.com/SymbiFlow/prjxray/pull/706#issuecomment-479380321
+
+# K area delay
+1 11 624
+2 12 624
+3 13 624
+4 14 624
+5 15 624
+6 20 724
+7 40 1020
+8 80 1293
diff --git a/techlibs/xilinx/cells_box.v b/techlibs/xilinx/cells_box.v
new file mode 100644
index 000000000..ef6f81d27
--- /dev/null
+++ b/techlibs/xilinx/cells_box.v
@@ -0,0 +1,19 @@
+(* abc_box_id = 1 *)
+module MUXF7(output O, input I0, I1, S);
+ assign O = S ? I1 : I0;
+endmodule
+
+(* abc_box_id = 2 *)
+module MUXF8(output O, input I0, I1, S);
+ assign O = S ? I1 : I0;
+endmodule
+
+(* abc_box_id = 3 *)
+module MUXCY(output O, input CI, DI, S);
+ assign O = S ? CI : DI;
+endmodule
+
+(* abc_box_id = 4 *)
+module XORCY(output O, input CI, LI);
+ assign O = CI ^ LI;
+endmodule
diff --git a/techlibs/xilinx/cells_map.v b/techlibs/xilinx/cells_map.v
index d5801c0fc..3c4d8f4cd 100644
--- a/techlibs/xilinx/cells_map.v
+++ b/techlibs/xilinx/cells_map.v
@@ -17,4 +17,205 @@
*
*/
-// Empty for now
+module \$__SHREG_ (input C, input D, input E, output Q);
+ parameter DEPTH = 0;
+ parameter [DEPTH-1:0] INIT = 0;
+ parameter CLKPOL = 1;
+ parameter ENPOL = 2;
+
+ \$__XILINX_SHREG_ #(.DEPTH(DEPTH), .INIT(INIT), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) _TECHMAP_REPLACE_ (.C(C), .D(D), .L(DEPTH-1), .E(E), .Q(Q));
+endmodule
+
+module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, output SO);
+ parameter DEPTH = 0;
+ parameter [DEPTH-1:0] INIT = 0;
+ parameter CLKPOL = 1;
+ parameter ENPOL = 2;
+
+ // shregmap's INIT parameter shifts out LSB first;
+ // however Xilinx expects MSB first
+ function [DEPTH-1:0] brev;
+ input [DEPTH-1:0] din;
+ integer i;
+ begin
+ for (i = 0; i < DEPTH; i=i+1)
+ brev[i] = din[DEPTH-1-i];
+ end
+ endfunction
+ localparam [DEPTH-1:0] INIT_R = brev(INIT);
+
+ parameter _TECHMAP_CONSTMSK_L_ = 0;
+ parameter _TECHMAP_CONSTVAL_L_ = 0;
+
+ wire CE;
+ generate
+ if (ENPOL == 0)
+ assign CE = ~E;
+ else if (ENPOL == 1)
+ assign CE = E;
+ else
+ assign CE = 1'b1;
+ if (DEPTH == 1) begin
+ if (CLKPOL)
+ FDRE #(.INIT(INIT_R)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(CE), .R(1'b0));
+ else
+ FDRE_1 #(.INIT(INIT_R)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(CE), .R(1'b0));
+ end else
+ if (DEPTH <= 16) begin
+ SRL16E #(.INIT(INIT_R), .IS_CLK_INVERTED(~CLKPOL[0])) _TECHMAP_REPLACE_ (.A0(L[0]), .A1(L[1]), .A2(L[2]), .A3(L[3]), .CE(CE), .CLK(C), .D(D), .Q(Q));
+ end else
+ if (DEPTH > 17 && DEPTH <= 32) begin
+ SRLC32E #(.INIT(INIT_R), .IS_CLK_INVERTED(~CLKPOL[0])) _TECHMAP_REPLACE_ (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(Q));
+ end else
+ if (DEPTH > 33 && DEPTH <= 64) begin
+ wire T0, T1, T2;
+ SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1));
+ \$__XILINX_SHREG_ #(.DEPTH(DEPTH-32), .INIT(INIT[DEPTH-32-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_1 (.C(C), .D(T1), .L(L), .E(E), .Q(T2));
+ if (&_TECHMAP_CONSTMSK_L_)
+ assign Q = T2;
+ else
+ MUXF7 fpga_mux_0 (.O(Q), .I0(T0), .I1(T2), .S(L[5]));
+ end else
+ if (DEPTH > 65 && DEPTH <= 96) begin
+ wire T0, T1, T2, T3, T4, T5, T6;
+ SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1));
+ SRLC32E #(.INIT(INIT_R[64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3));
+ \$__XILINX_SHREG_ #(.DEPTH(DEPTH-64), .INIT(INIT[DEPTH-64-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_2 (.C(C), .D(T3), .L(L[4:0]), .E(E), .Q(T4));
+ if (&_TECHMAP_CONSTMSK_L_)
+ assign Q = T4;
+ else begin
+ MUXF7 fpga_mux_0 (.O(T5), .I0(T0), .I1(T2), .S(L[5]));
+ MUXF7 fpga_mux_1 (.O(T6), .I0(T4), .I1(1'b0 /* unused */), .S(L[5]));
+ MUXF8 fpga_mux_2 (.O(Q), .I0(T5), .I1(T6), .S(L[6]));
+ end
+ end else
+ if (DEPTH > 97 && DEPTH < 128) begin
+ wire T0, T1, T2, T3, T4, T5, T6, T7, T8;
+ SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1));
+ SRLC32E #(.INIT(INIT_R[64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3));
+ SRLC32E #(.INIT(INIT_R[96-1:64]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_2 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T3), .Q(T4), .Q31(T5));
+ \$__XILINX_SHREG_ #(.DEPTH(DEPTH-96), .INIT(INIT[DEPTH-96-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_3 (.C(C), .D(T5), .L(L[4:0]), .E(E), .Q(T6));
+ if (&_TECHMAP_CONSTMSK_L_)
+ assign Q = T6;
+ else begin
+ MUXF7 fpga_mux_0 (.O(T7), .I0(T0), .I1(T2), .S(L[5]));
+ MUXF7 fpga_mux_1 (.O(T8), .I0(T4), .I1(T6), .S(L[5]));
+ MUXF8 fpga_mux_2 (.O(Q), .I0(T7), .I1(T8), .S(L[6]));
+ end
+ end
+ else if (DEPTH == 128) begin
+ wire T0, T1, T2, T3, T4, T5, T6;
+ SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1));
+ SRLC32E #(.INIT(INIT_R[64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3));
+ SRLC32E #(.INIT(INIT_R[96-1:64]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_2 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T3), .Q(T4), .Q31(T5));
+ SRLC32E #(.INIT(INIT_R[128-1:96]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_3 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T5), .Q(T6), .Q31(SO));
+ if (&_TECHMAP_CONSTMSK_L_)
+ assign Q = T6;
+ else begin
+ wire T7, T8;
+ MUXF7 fpga_mux_0 (.O(T7), .I0(T0), .I1(T2), .S(L[5]));
+ MUXF7 fpga_mux_1 (.O(T8), .I0(T4), .I1(T6), .S(L[5]));
+ MUXF8 fpga_mux_2 (.O(Q), .I0(T7), .I1(T8), .S(L[6]));
+ end
+ end
+ else if (DEPTH <= 129 && ~&_TECHMAP_CONSTMSK_L_) begin
+ // Handle cases where fixed-length depth is
+ // just 1 over a convenient value
+ \$__XILINX_SHREG_ #(.DEPTH(DEPTH+1), .INIT({INIT,1'b0}), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) _TECHMAP_REPLACE_ (.C(C), .D(D), .L(L), .E(E), .Q(Q));
+ end
+ else begin
+ localparam lower_clog2 = $clog2((DEPTH+1)/2);
+ localparam lower_depth = 2 ** lower_clog2;
+ wire T0, T1, T2, T3;
+ if (&_TECHMAP_CONSTMSK_L_) begin
+ \$__XILINX_SHREG_ #(.DEPTH(lower_depth), .INIT(INIT[DEPTH-1:DEPTH-lower_depth]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_0 (.C(C), .D(D), .L(lower_depth-1), .E(E), .Q(T0));
+ \$__XILINX_SHREG_ #(.DEPTH(DEPTH-lower_depth), .INIT(INIT[DEPTH-lower_depth-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_1 (.C(C), .D(T0), .L(DEPTH-lower_depth-1), .E(E), .Q(Q), .SO(T3));
+ end
+ else begin
+ \$__XILINX_SHREG_ #(.DEPTH(lower_depth), .INIT(INIT[DEPTH-1:DEPTH-lower_depth]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_0 (.C(C), .D(D), .L(L[lower_clog2-1:0]), .E(E), .Q(T0), .SO(T1));
+ \$__XILINX_SHREG_ #(.DEPTH(DEPTH-lower_depth), .INIT(INIT[DEPTH-lower_depth-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_1 (.C(C), .D(T1), .L(L[lower_clog2-1:0]), .E(E), .Q(T2), .SO(T3));
+ assign Q = L[lower_clog2] ? T2 : T0;
+ end
+ if (DEPTH == 2 * lower_depth)
+ assign SO = T3;
+ end
+ endgenerate
+endmodule
+
+module \$shiftx (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ parameter [B_WIDTH-1:0] _TECHMAP_CONSTMSK_B_ = 0;
+ parameter [B_WIDTH-1:0] _TECHMAP_CONSTVAL_B_ = 0;
+
+ generate
+ genvar i, j;
+ if (B_SIGNED) begin
+ if (_TECHMAP_CONSTMSK_B_[B_WIDTH-1] && _TECHMAP_CONSTVAL_B_[B_WIDTH-1] == 1'b0)
+ // Optimisation to remove B_SIGNED if sign bit of B is constant-0
+ \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(0), .A_WIDTH(A_WIDTH), .B_WIDTH(B_WIDTH-1), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(A), .B(B[B_WIDTH-2:0]), .Y(Y));
+ else
+ wire _TECHMAP_FAIL_ = 1;
+ end
+ else if (Y_WIDTH > 1) begin
+ wire [$clog2(A_WIDTH/Y_WIDTH)-1:0] B_bitty = B/Y_WIDTH;
+ for (i = 0; i < Y_WIDTH; i++) begin
+ wire [A_WIDTH/Y_WIDTH-1:0] A_i;
+ for (j = 0; j < A_WIDTH/Y_WIDTH; j++)
+ assign A_i[j] = A[j*Y_WIDTH+i];
+ \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(A_WIDTH/Y_WIDTH), .B_WIDTH($clog2(A_WIDTH/Y_WIDTH)), .Y_WIDTH(1)) bitblast (.A(A_i), .B(B_bitty), .Y(Y[i]));
+ end
+ end
+ else if (B_WIDTH < 3) begin
+ wire _TECHMAP_FAIL_ = 1;
+ end
+ else if (B_WIDTH == 3) begin
+ localparam a_width0 = 2 ** 2;
+ localparam a_widthN = A_WIDTH - a_width0;
+ wire T0, T1;
+ \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_width0), .B_WIDTH(2), .Y_WIDTH(Y_WIDTH)) fpga_shiftx (.A(A[a_width0-1:0]), .B(B[2-1:0]), .Y(T0));
+ \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_widthN), .B_WIDTH($clog2(a_widthN)), .Y_WIDTH(Y_WIDTH)) fpga_shiftx_last (.A(A[A_WIDTH-1:a_width0]), .B(B[$clog2(a_widthN)-1:0]), .Y(T1));
+ MUXF7 fpga_mux (.I0(T0), .I1(T1), .S(B[B_WIDTH-1]), .O(Y));
+ end
+ else if (B_WIDTH == 4) begin
+ localparam a_width0 = 2 ** 2;
+ localparam num_mux8 = A_WIDTH / a_width0;
+ localparam a_widthN = A_WIDTH - num_mux8*a_width0;
+ wire [4-1:0] T;
+ wire T0, T1;
+ for (i = 0; i < 4; i++)
+ if (i < num_mux8)
+ \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_width0), .B_WIDTH(2), .Y_WIDTH(Y_WIDTH)) fpga_shiftx (.A(A[i*a_width0+:a_width0]), .B(B[2-1:0]), .Y(T[i]));
+ else if (i == num_mux8 && a_widthN > 0)
+ \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_widthN), .B_WIDTH($clog2(a_widthN)), .Y_WIDTH(Y_WIDTH)) fpga_shiftx_last (.A(A[A_WIDTH-1:i*a_width0]), .B(B[$clog2(a_widthN)-1:0]), .Y(T[i]));
+ else
+ assign T[i] = 1'bx;
+ MUXF7 fpga_mux_0 (.I0(T[0]), .I1(T[1]), .S(B[2]), .O(T0));
+ MUXF7 fpga_mux_1 (.I0(T[2]), .I1(T[3]), .S(B[2]), .O(T1));
+ MUXF8 fpga_mux_2 (.I0(T0), .I1(T1), .S(B[3]), .O(Y));
+ end
+ else begin
+ localparam a_width0 = 2 ** 4;
+ localparam num_mux16 = A_WIDTH / a_width0;
+ localparam a_widthN = A_WIDTH - num_mux16*a_width0;
+ wire [(2**(B_WIDTH-4))-1:0] T;
+ for (i = 0; i < 2 ** (B_WIDTH-4); i++)
+ if (i < num_mux16)
+ \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_width0), .B_WIDTH(4), .Y_WIDTH(Y_WIDTH)) fpga_shiftx (.A(A[i*a_width0+:a_width0]), .B(B[4-1:0]), .Y(T[i]));
+ else if (i == num_mux16 && a_widthN > 0) begin
+ \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_widthN), .B_WIDTH($clog2(a_widthN)), .Y_WIDTH(Y_WIDTH)) fpga_shiftx_last (.A(A[A_WIDTH-1:i*a_width0]), .B(B[$clog2(a_widthN)-1:0]), .Y(T[i]));
+ end
+ else
+ assign T[i] = 1'bx;
+ \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(2**(B_WIDTH-4)), .B_WIDTH(B_WIDTH-4), .Y_WIDTH(Y_WIDTH)) fpga_shiftx (.A(T), .B(B[B_WIDTH-1:4]), .Y(Y));
+ end
+ endgenerate
+endmodule
diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v
index 0c8f282a4..3a4540b83 100644
--- a/techlibs/xilinx/cells_sim.v
+++ b/techlibs/xilinx/cells_sim.v
@@ -308,3 +308,42 @@ module RAM128X1D (
wire clk = WCLK ^ IS_WCLK_INVERTED;
always @(posedge clk) if (WE) mem[A] <= D;
endmodule
+
+module SRL16E (
+ output Q,
+ input A0, A1, A2, A3, CE, CLK, D
+);
+ parameter [15:0] INIT = 16'h0000;
+ parameter [0:0] IS_CLK_INVERTED = 1'b0;
+
+ reg [15:0] r = INIT;
+ assign Q = r[{A3,A2,A1,A0}];
+ generate
+ if (IS_CLK_INVERTED) begin
+ always @(negedge CLK) if (CE) r <= { r[14:0], D };
+ end
+ else
+ always @(posedge CLK) if (CE) r <= { r[14:0], D };
+ endgenerate
+endmodule
+
+module SRLC32E (
+ output Q,
+ output Q31,
+ input [4:0] A,
+ input CE, CLK, D
+);
+ parameter [31:0] INIT = 32'h00000000;
+ parameter [0:0] IS_CLK_INVERTED = 1'b0;
+
+ reg [31:0] r = INIT;
+ assign Q31 = r[31];
+ assign Q = r[A];
+ generate
+ if (IS_CLK_INVERTED) begin
+ always @(negedge CLK) if (CE) r <= { r[30:0], D };
+ end
+ else
+ always @(posedge CLK) if (CE) r <= { r[30:0], D };
+ endgenerate
+endmodule
diff --git a/techlibs/xilinx/cells_xtra.sh b/techlibs/xilinx/cells_xtra.sh
index 8b06c3155..8e39b440d 100644
--- a/techlibs/xilinx/cells_xtra.sh
+++ b/techlibs/xilinx/cells_xtra.sh
@@ -135,8 +135,8 @@ function xtract_cell_decl()
xtract_cell_decl ROM256X1
xtract_cell_decl ROM32X1
xtract_cell_decl ROM64X1
- xtract_cell_decl SRL16E
- xtract_cell_decl SRLC32E
+ #xtract_cell_decl SRL16E
+ #xtract_cell_decl SRLC32E
xtract_cell_decl STARTUPE2 "(* keep *)"
xtract_cell_decl USR_ACCESSE2
xtract_cell_decl XADC
diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v
index 4fb6798be..fbcc74682 100644
--- a/techlibs/xilinx/cells_xtra.v
+++ b/techlibs/xilinx/cells_xtra.v
@@ -3809,22 +3809,6 @@ module ROM64X1 (...);
input A0, A1, A2, A3, A4, A5;
endmodule
-module SRL16E (...);
- parameter [15:0] INIT = 16'h0000;
- parameter [0:0] IS_CLK_INVERTED = 1'b0;
- output Q;
- input A0, A1, A2, A3, CE, CLK, D;
-endmodule
-
-module SRLC32E (...);
- parameter [31:0] INIT = 32'h00000000;
- parameter [0:0] IS_CLK_INVERTED = 1'b0;
- output Q;
- output Q31;
- input [4:0] A;
- input CE, CLK, D;
-endmodule
-
(* keep *)
module STARTUPE2 (...);
parameter PROG_USR = "FALSE";
diff --git a/techlibs/xilinx/ff_map.v b/techlibs/xilinx/ff_map.v
index 3d5f78770..c61fd7070 100644
--- a/techlibs/xilinx/ff_map.v
+++ b/techlibs/xilinx/ff_map.v
@@ -22,21 +22,26 @@
`ifndef _NO_FFS
+`ifndef _NO_POS_SR
module \$_DFF_N_ (input D, C, output Q); FDRE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule
module \$_DFF_P_ (input D, C, output Q); FDRE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule
module \$_DFFE_NP_ (input D, C, E, output Q); FDRE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0)); endmodule
module \$_DFFE_PP_ (input D, C, E, output Q); FDRE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0)); endmodule
-module \$_DFF_NN0_ (input D, C, R, output Q); \$_DFF_NP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
module \$_DFF_NP0_ (input D, C, R, output Q); FDCE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule
-module \$_DFF_PN0_ (input D, C, R, output Q); \$_DFF_PP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
module \$_DFF_PP0_ (input D, C, R, output Q); FDCE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule
-module \$_DFF_NN1_ (input D, C, R, output Q); \$_DFF_NP1 _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
module \$_DFF_NP1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule
-module \$_DFF_PN1_ (input D, C, R, output Q); \$_DFF_PP1 _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
module \$_DFF_PP1_ (input D, C, R, output Q); FDPE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule
+`endif
+
+module \$_DFF_NN0_ (input D, C, R, output Q); \$_DFF_NP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
+module \$_DFF_PN0_ (input D, C, R, output Q); \$_DFF_PP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
+
+module \$_DFF_NN1_ (input D, C, R, output Q); \$_DFF_NP1 _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
+module \$_DFF_PN1_ (input D, C, R, output Q); \$_DFF_PP1 _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
+`endif
`endif
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc
index 08d74cd3b..fa87a3ad6 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -64,10 +64,13 @@ struct SynthXilinxPass : public Pass
log(" (this feature is experimental and incomplete)\n");
log("\n");
log(" -nobram\n");
- log(" disable infering of block rams\n");
+ log(" disable inference of block rams\n");
log("\n");
log(" -nodram\n");
- log(" disable infering of distributed rams\n");
+ log(" disable inference of distributed rams\n");
+ log("\n");
+ log(" -nosrl\n");
+ log(" disable inference of shift registers\n");
log("\n");
log(" -run <from_label>:<to_label>\n");
log(" only run the commands between the labels (see below). an empty\n");
@@ -108,26 +111,30 @@ struct SynthXilinxPass : public Pass
log(" techmap -map +/xilinx/drams_map.v\n");
log("\n");
log(" fine:\n");
- log(" opt -fast -full\n");
+ log(" opt -fast\n");
log(" memory_map\n");
log(" dffsr2dff\n");
log(" dff2dffe\n");
- log(" opt -full\n");
- log(" techmap -map +/techmap.v -map +/xilinx/arith_map.v\n");
+ log(" techmap -map +/xilinx/arith_map.v\n");
log(" opt -fast\n");
log("\n");
log(" map_cells:\n");
+ log(" simplemap t:$dff t:$dffe (without '-nosrl' only)\n");
+ log(" pmux2shiftx (without '-nosrl' only)\n");
+ log(" opt_expr -mux_undef (without '-nosrl' only)\n");
+ log(" shregmap -tech xilinx -minlen 3 (without '-nosrl' only)\n");
log(" techmap -map +/xilinx/cells_map.v\n");
log(" clean\n");
log("\n");
log(" map_luts:\n");
- log(" techmap -map +/techmap.v -map +/xilinx/ff_map.v t:$_DFF_?N?\n");
+ log(" opt -full\n");
+ log(" techmap -map +/techmap.v -D _NO_POS_SR -map +/xilinx/ff_map.v\n");
log(" abc -luts 2:2,3,6:5,10,20 [-dff]\n");
log(" clean\n");
- log(" techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v");
+ log(" shregmap -minlen 3 -init -params -enpol any_or_none (without '-nosrl' only)\n");
+ log(" techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v");
log(" dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT \\\n");
log(" -ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT\n");
- log(" clean\n");
log("\n");
log(" check:\n");
log(" hierarchy -check\n");
@@ -153,6 +160,7 @@ struct SynthXilinxPass : public Pass
bool vpr = false;
bool nobram = false;
bool nodram = false;
+ bool nosrl = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@@ -197,6 +205,10 @@ struct SynthXilinxPass : public Pass
nodram = true;
continue;
}
+ if (args[argidx] == "-nosrl") {
+ nosrl = true;
+ continue;
+ }
if (args[argidx] == "-abc9") {
abc = "abc9";
continue;
@@ -259,16 +271,15 @@ struct SynthXilinxPass : public Pass
if (check_label(active, run_from, run_to, "fine"))
{
- Pass::call(design, "opt -fast -full");
+ Pass::call(design, "opt -fast");
Pass::call(design, "memory_map");
Pass::call(design, "dffsr2dff");
Pass::call(design, "dff2dffe");
- Pass::call(design, "opt -full");
if (vpr) {
- Pass::call(design, "techmap -map +/techmap.v -map +/xilinx/arith_map.v -D _EXPLICIT_CARRY");
+ Pass::call(design, "techmap -map +/xilinx/arith_map.v -D _EXPLICIT_CARRY");
} else {
- Pass::call(design, "techmap -map +/techmap.v -map +/xilinx/arith_map.v");
+ Pass::call(design, "techmap -map +/xilinx/arith_map.v");
}
Pass::call(design, "hierarchy -check");
@@ -277,19 +288,42 @@ struct SynthXilinxPass : public Pass
if (check_label(active, run_from, run_to, "map_cells"))
{
+ if (!nosrl) {
+ // shregmap operates on bit-level flops, not word-level,
+ // so break those down here
+ Pass::call(design, "simplemap t:$dff t:$dffe");
+ // shregmap -tech xilinx can cope with $shiftx and $mux
+ // cells for identifiying variable-length shift registers,
+ // so attempt to convert $pmux-es to the former
+ Pass::call(design, "pmux2shiftx");
+ // pmux2shiftx can leave behind a $pmux with a single entry
+ // -- need this to clean that up before shregmap
+ Pass::call(design, "opt_expr -mux_undef");
+ // shregmap with '-tech xilinx' infers variable length shift regs
+ Pass::call(design, "shregmap -tech xilinx -minlen 3");
+ }
+
Pass::call(design, "techmap -map +/xilinx/cells_map.v");
Pass::call(design, "clean");
}
if (check_label(active, run_from, run_to, "map_luts"))
{
- Pass::call(design, "techmap -map +/techmap.v -map +/xilinx/ff_map.v t:$_DFF_?N?");
- Pass::call(design, abc + " -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : ""));
+ Pass::call(design, "opt -full");
+ Pass::call(design, "techmap -map +/techmap.v -D _NO_POS_SR -map +/xilinx/ff_map.v");
+ Pass::call(design, "read_verilog +/xilinx/cells_box.v");
+ if (abc == "abc9")
+ Pass::call(design, abc + " -lut +/xilinx/cells.lut -box +/xilinx/cells.box" + string(retime ? " -dff" : ""));
+ else
+ Pass::call(design, abc + " -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : ""));
Pass::call(design, "clean");
- Pass::call(design, "techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v");
+ // This shregmap call infers fixed length shift registers after abc
+ // has performed any necessary retiming
+ if (!nosrl)
+ Pass::call(design, "shregmap -minlen 3 -init -params -enpol any_or_none");
+ Pass::call(design, "techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v");
Pass::call(design, "dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT "
"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT");
- Pass::call(design, "clean");
}
if (check_label(active, run_from, run_to, "check"))