aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG2
-rw-r--r--README.md13
-rw-r--r--backends/aiger/xaiger.cc63
-rw-r--r--frontends/aiger/aigerparse.cc39
-rw-r--r--passes/pmgen/Makefile.inc6
-rw-r--r--passes/pmgen/test_pmgen.cc12
-rw-r--r--passes/pmgen/xilinx_srl.cc262
-rw-r--r--passes/pmgen/xilinx_srl.pmg326
-rw-r--r--passes/techmap/abc9.cc47
-rw-r--r--passes/techmap/shregmap.cc179
-rw-r--r--techlibs/ecp5/Makefile.inc3
-rw-r--r--techlibs/ecp5/abc_5g.box18
-rw-r--r--techlibs/ecp5/abc_map.v24
-rw-r--r--techlibs/ecp5/abc_model.v5
-rw-r--r--techlibs/ecp5/abc_unmap.v5
-rw-r--r--techlibs/ecp5/cells_sim.v5
-rw-r--r--techlibs/ecp5/synth_ecp5.cc7
-rw-r--r--techlibs/xilinx/Makefile.inc3
-rw-r--r--techlibs/xilinx/abc_map.v125
-rw-r--r--techlibs/xilinx/abc_model.v34
-rw-r--r--techlibs/xilinx/abc_unmap.v28
-rw-r--r--techlibs/xilinx/abc_xc7.box31
-rw-r--r--techlibs/xilinx/cells_map.v2
-rw-r--r--techlibs/xilinx/cells_sim.v96
-rw-r--r--techlibs/xilinx/synth_xilinx.cc48
-rw-r--r--techlibs/xilinx/xc7_brams_bb.v10
-rw-r--r--tests/various/shregmap.ys33
-rw-r--r--tests/xilinx/.gitignore3
-rw-r--r--tests/xilinx/pmgen_xilinx_srl.ys57
-rwxr-xr-xtests/xilinx/run-test.sh20
-rw-r--r--tests/xilinx/xilinx_srl.v40
-rw-r--r--tests/xilinx/xilinx_srl.ys67
32 files changed, 1253 insertions, 360 deletions
diff --git a/CHANGELOG b/CHANGELOG
index f7774cb9e..c29429295 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -36,6 +36,8 @@ Yosys 0.9 .. Yosys 0.9-dev
- Added "ice40_wrapcarry" to encapsulate SB_LUT+SB_CARRY pairs for techmapping
- Removed "ice40_unlut"
- Improvements in pmgen: slices, choices, define, generate
+ - Added "xilinx_srl" for Xilinx shift register extraction
+ - Removed "shregmap -tech xilinx" (superseded by "xilinx_srl")
Yosys 0.8 .. Yosys 0.9
----------------------
diff --git a/README.md b/README.md
index 38ca77862..c6c6f24b6 100644
--- a/README.md
+++ b/README.md
@@ -427,19 +427,16 @@ Verilog Attributes and non-standard features
blackbox or whitebox definition to a corresponding entry in a `abc9`
box-file.
-- The port attribute ``abc_scc_break`` indicates a module input port that will
- be treated as a primary output during `abc9` techmapping. Doing so eliminates
- the possibility of a strongly-connected component (i.e. a combinatorial loop)
- existing. Typically, this is specified for sequential inputs on otherwise
- combinatorial boxes -- for example, applying ``abc_scc_break`` onto the `D`
- port of a LUTRAM cell prevents `abc9` from interpreting any `Q` -> `D` paths
- as a combinatorial loop.
-
- The port attribute ``abc_carry`` marks the carry-in (if an input port) and
carry-out (if output port) ports of a box. This information is necessary for
`abc9` to preserve the integrity of carry-chains. Specifying this attribute
onto a bus port will affect only its most significant bit.
+- The port attribute ``abc_arrival`` specifies an integer (for output ports
+ only) to be used as the arrival time of this sequential port. It can be used,
+ for example, to specify the clk-to-Q delay of a flip-flop for consideration
+ during techmapping.
+
Non-standard or SystemVerilog features for formal verification
==============================================================
diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc
index 77d0e94c2..b66706ffa 100644
--- a/backends/aiger/xaiger.cc
+++ b/backends/aiger/xaiger.cc
@@ -83,6 +83,7 @@ struct XAigerWriter
dict<SigBit, pair<SigBit, SigBit>> and_map;
vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int>> ci_bits;
vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int,int>> co_bits;
+ dict<SigBit, float> arrival_times;
vector<pair<int, int>> aig_gates;
vector<int> aig_outputs;
@@ -247,30 +248,32 @@ struct XAigerWriter
if (!holes_mode) {
toposort.node(cell->name);
for (const auto &conn : cell->connections()) {
- if (cell->input(conn.first)) {
+ auto port_wire = inst_module->wire(conn.first);
+ if (port_wire->port_input) {
// Ignore inout for the sake of topographical ordering
- if (cell->output(conn.first)) continue;
+ if (port_wire->port_output) continue;
for (auto bit : sigmap(conn.second))
bit_users[bit].insert(cell->name);
}
- if (cell->output(conn.first))
+ if (port_wire->port_output)
for (auto bit : sigmap(conn.second))
bit_drivers[bit].insert(cell->name);
}
}
}
else {
- bool cell_known = cell->known();
+ bool cell_known = inst_module;
for (const auto &c : cell->connections()) {
if (c.second.is_fully_const()) continue;
- auto is_input = !cell_known || cell->input(c.first);
- auto is_output = !cell_known || cell->output(c.first);
+ auto port_wire = inst_module ? inst_module->wire(c.first) : nullptr;
+ auto is_input = !cell_known || port_wire->port_input;
+ auto is_output = !cell_known || port_wire->port_output;
if (!is_input && !is_output)
log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type));
if (is_input) {
- for (auto b : c.second.bits()) {
+ for (auto b : c.second) {
Wire *w = b.wire;
if (!w) continue;
if (!w->port_output || !cell_known) {
@@ -286,7 +289,17 @@ struct XAigerWriter
}
}
if (is_output) {
- for (auto b : c.second.bits()) {
+ int arrival = 0;
+ if (port_wire) {
+ auto it = port_wire->attributes.find("\\abc_arrival");
+ if (it != port_wire->attributes.end()) {
+ if (it->second.flags != 0)
+ log_error("Attribute 'abc_arrival' on port '%s' of module '%s' is not an integer.\n", log_id(port_wire), log_id(cell->type));
+ arrival = it->second.as_int();
+ }
+ }
+
+ for (auto b : c.second) {
Wire *w = b.wire;
if (!w) continue;
input_bits.insert(b);
@@ -294,6 +307,9 @@ struct XAigerWriter
if (O != b)
alias_map[O] = b;
undriven_bits.erase(O);
+
+ if (arrival)
+ arrival_times[b] = arrival;
}
}
}
@@ -559,19 +575,30 @@ struct XAigerWriter
f << "c";
+ auto write_buffer_float = [](std::stringstream &buffer, float f32) {
+ buffer.write(reinterpret_cast<const char*>(&f32), sizeof(f32));
+ };
+ std::stringstream i_buffer;
+ auto write_i_buffer = std::bind(write_buffer_float, std::ref(i_buffer), std::placeholders::_1);
+ for (auto bit : input_bits)
+ write_i_buffer(arrival_times.at(bit, 0));
+ //std::stringstream o_buffer;
+ //auto write_o_buffer = std::bind(write_buffer_float, std::ref(o_buffer), std::placeholders::_1);
+ //for (auto bit : output_bits)
+ // write_o_buffer(0);
+
if (!box_list.empty()) {
auto write_buffer = [](std::stringstream &buffer, int i32) {
int32_t i32_be = to_big_endian(i32);
buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be));
};
-
std::stringstream h_buffer;
auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1);
write_h_buffer(1);
log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ci_bits));
write_h_buffer(input_bits.size() + ci_bits.size());
log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(co_bits));
- write_h_buffer(output_bits.size() + co_bits.size());
+ write_h_buffer(output_bits.size() + GetSize(co_bits));
log_debug("piNum = %d\n", GetSize(input_bits));
write_h_buffer(input_bits.size());
log_debug("poNum = %d\n", GetSize(output_bits));
@@ -651,7 +678,6 @@ struct XAigerWriter
std::stringstream r_buffer;
auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1);
write_r_buffer(0);
-
f << "r";
buffer_str = r_buffer.str();
buffer_size_be = to_big_endian(buffer_str.size());
@@ -707,6 +733,21 @@ struct XAigerWriter
log_pop();
}
}
+ else {
+ log_debug("piNum = %d\n", GetSize(input_bits));
+ log_debug("poNum = %d\n", GetSize(output_bits));
+ }
+
+ f << "i";
+ std::string buffer_str = i_buffer.str();
+ int32_t buffer_size_be = to_big_endian(buffer_str.size());
+ f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
+ f.write(buffer_str.data(), buffer_str.size());
+ //f << "o";
+ //buffer_str = o_buffer.str();
+ //buffer_size_be = to_big_endian(buffer_str.size());
+ //f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
+ //f.write(buffer_str.data(), buffer_str.size());
f << stringf("Generated by %s\n", yosys_version_str);
}
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc
index 06522939f..e8ee487e5 100644
--- a/frontends/aiger/aigerparse.cc
+++ b/frontends/aiger/aigerparse.cc
@@ -430,6 +430,7 @@ void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup)
else if (c == 'r') {
uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
flopNum = parse_xaiger_literal(f);
+ log_debug("flopNum: %u\n", flopNum);
log_assert(dataSize == (flopNum+1) * sizeof(uint32_t));
f.ignore(flopNum * sizeof(uint32_t));
}
@@ -496,8 +497,7 @@ void AigerReader::parse_aiger_ascii()
// Parse latches
RTLIL::Wire *clk_wire = nullptr;
- if (L > 0) {
- log_assert(clk_name != "");
+ if (L > 0 && !clk_name.empty()) {
clk_wire = module->wire(clk_name);
log_assert(!clk_wire);
log_debug2("Creating %s\n", clk_name.c_str());
@@ -513,7 +513,10 @@ void AigerReader::parse_aiger_ascii()
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
- module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire);
+ if (clk_wire)
+ module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire);
+ else
+ module->addFfGate(NEW_ID, d_wire, q_wire);
// Reset logic is optional in AIGER 1.9
if (f.peek() == ' ') {
@@ -621,8 +624,7 @@ void AigerReader::parse_aiger_binary()
// Parse latches
RTLIL::Wire *clk_wire = nullptr;
- if (L > 0) {
- log_assert(clk_name != "");
+ if (L > 0 && !clk_name.empty()) {
clk_wire = module->wire(clk_name);
log_assert(!clk_wire);
log_debug2("Creating %s\n", clk_name.c_str());
@@ -638,7 +640,10 @@ void AigerReader::parse_aiger_binary()
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
- module->addDff(NEW_ID, clk_wire, d_wire, q_wire);
+ if (clk_wire)
+ module->addDff(NEW_ID, clk_wire, d_wire, q_wire);
+ else
+ module->addFf(NEW_ID, d_wire, q_wire);
// Reset logic is optional in AIGER 1.9
if (f.peek() == ' ') {
@@ -776,19 +781,19 @@ void AigerReader::post_process()
// NB: Assume box_module->ports are sorted alphabetically
// (as RTLIL::Module::fixup_ports() would do)
for (auto port_name : box_module->ports) {
- RTLIL::Wire* w = box_module->wire(port_name);
- log_assert(w);
+ RTLIL::Wire* port = box_module->wire(port_name);
+ log_assert(port);
RTLIL::SigSpec rhs;
- RTLIL::Wire* wire = nullptr;
- for (int i = 0; i < GetSize(w); i++) {
- if (w->port_input) {
+ for (int i = 0; i < GetSize(port); i++) {
+ RTLIL::Wire* wire = nullptr;
+ if (port->port_input) {
log_assert(co_count < outputs.size());
wire = outputs[co_count++];
log_assert(wire);
log_assert(wire->port_output);
wire->port_output = false;
}
- if (w->port_output) {
+ if (port->port_output) {
log_assert((piNum + ci_count) < inputs.size());
wire = inputs[piNum + ci_count++];
log_assert(wire);
@@ -797,6 +802,7 @@ void AigerReader::post_process()
}
rhs.append(wire);
}
+
cell->setPort(port_name, rhs);
}
}
@@ -814,6 +820,7 @@ void AigerReader::post_process()
RTLIL::Wire* wire = inputs[variable];
log_assert(wire);
log_assert(wire->port_input);
+ log_debug("Renaming input %s", log_id(wire));
if (index == 0) {
// Cope with the fact that a CI might be identical
@@ -840,6 +847,7 @@ void AigerReader::post_process()
wire->port_input = false;
}
}
+ log_debug(" -> %s\n", log_id(wire));
}
else if (type == "output") {
log_assert(static_cast<unsigned>(variable + co_count) < outputs.size());
@@ -850,6 +858,7 @@ void AigerReader::post_process()
wire->port_output = false;
continue;
}
+ log_debug("Renaming output %s", log_id(wire));
if (index == 0) {
// Cope with the fact that a CO might be identical
@@ -871,6 +880,7 @@ void AigerReader::post_process()
else {
wire->port_output = false;
module->connect(wire, existing);
+ wire = existing;
}
}
else if (index > 0) {
@@ -896,6 +906,7 @@ void AigerReader::post_process()
wire->port_output = false;
}
}
+ log_debug(" -> %s\n", log_id(wire));
}
else if (type == "box") {
RTLIL::Cell* cell = module->cell(stringf("$__box%d__", variable));
@@ -1004,8 +1015,8 @@ struct AigerFrontend : public Frontend {
log(" Name of module to be created (default: <filename>)\n");
log("\n");
log(" -clk_name <wire_name>\n");
- log(" AIGER latches to be transformed into posedge DFFs clocked by wire of");
- log(" this name (default: clk)\n");
+ log(" If specified, AIGER latches to be transformed into $_DFF_P_ cells\n");
+ log(" clocked by wire of this name. Otherwise, $_FF_ cells will be used.\n");
log("\n");
log(" -map <filename>\n");
log(" read file with port and latch symbols\n");
diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc
index 8e0cbdca8..e73a7b1c9 100644
--- a/passes/pmgen/Makefile.inc
+++ b/passes/pmgen/Makefile.inc
@@ -30,3 +30,9 @@ PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^)
+
+# --------------------------------------
+
+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))
diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc
index 0ad769dfd..4f3eec935 100644
--- a/passes/pmgen/test_pmgen.cc
+++ b/passes/pmgen/test_pmgen.cc
@@ -28,6 +28,7 @@ bool did_something;
#include "passes/pmgen/test_pmgen_pm.h"
#include "passes/pmgen/ice40_dsp_pm.h"
+#include "passes/pmgen/xilinx_srl_pm.h"
#include "passes/pmgen/peepopt_pm.h"
void reduce_chain(test_pmgen_pm &pm)
@@ -180,7 +181,7 @@ void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const
while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000)
{
if (timeout++ > 10000)
- log_error("pmgen generator is stuck: 10000 iterations an no matching module generated.\n");
+ log_error("pmgen generator is stuck: 10000 iterations with no matching module generated.\n");
pm matcher(mod, mod->cells());
@@ -216,7 +217,7 @@ void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const
run(matcher, [](){});
}
- if (submodcnt)
+ if (submodcnt && maxsubcnt < (1 << 16))
maxsubcnt *= 2;
design->remove(mod);
@@ -349,13 +350,18 @@ struct TestPmgenPass : public Pass {
if (pattern == "ice40_dsp")
return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp);
+ if (pattern == "xilinx_srl.fixed")
+ return GENERATE_PATTERN(xilinx_srl_pm, fixed);
+ if (pattern == "xilinx_srl.variable")
+ return GENERATE_PATTERN(xilinx_srl_pm, variable);
+
if (pattern == "peepopt-muldiv")
return GENERATE_PATTERN(peepopt_pm, muldiv);
if (pattern == "peepopt-shiftmul")
return GENERATE_PATTERN(peepopt_pm, shiftmul);
- log_cmd_error("Unkown pattern: %s\n", pattern.c_str());
+ log_cmd_error("Unknown pattern: %s\n", pattern.c_str());
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
diff --git a/passes/pmgen/xilinx_srl.cc b/passes/pmgen/xilinx_srl.cc
new file mode 100644
index 000000000..87fcaa15a
--- /dev/null
+++ b/passes/pmgen/xilinx_srl.cc
@@ -0,0 +1,262 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * (C) 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
+
+// for peepopt_pm
+bool did_something;
+
+#include "passes/pmgen/xilinx_srl_pm.h"
+#include "passes/pmgen/peepopt_pm.h"
+
+void run_fixed(xilinx_srl_pm &pm)
+{
+ auto &st = pm.st_fixed;
+ auto &ud = pm.ud_fixed;
+ log("Found fixed chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type));
+
+ SigSpec initval;
+ for (auto cell : ud.longest_chain) {
+ log_debug(" %s\n", log_id(cell));
+ if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) {
+ SigBit Q = cell->getPort(ID(Q));
+ log_assert(Q.wire);
+ auto it = Q.wire->attributes.find(ID(init));
+ if (it != Q.wire->attributes.end()) {
+ auto &i = it->second[Q.offset];
+ initval.append(i);
+ i = State::Sx;
+ }
+ else
+ initval.append(State::Sx);
+ }
+ else if (cell->type.in(ID(FDRE), ID(FDRE_1))) {
+ if (cell->parameters.at(ID(INIT), State::S0).as_bool())
+ initval.append(State::S1);
+ else
+ initval.append(State::S0);
+ }
+ else
+ log_abort();
+ pm.autoremove(cell);
+ }
+
+ auto first_cell = ud.longest_chain.back();
+ auto last_cell = ud.longest_chain.front();
+ Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_));
+ pm.module->swap_names(c, first_cell);
+
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID(FDRE), ID(FDRE_1))) {
+ c->setParam(ID(DEPTH), GetSize(ud.longest_chain));
+ c->setParam(ID(INIT), initval.as_const());
+ if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setParam(ID(CLKPOL), 1);
+ else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_), ID(FDRE_1)))
+ c->setParam(ID(CLKPOL), 0);
+ else if (first_cell->type.in(ID(FDRE))) {
+ if (!first_cell->parameters.at(ID(IS_C_INVERTED), State::S0).as_bool())
+ c->setParam(ID(CLKPOL), 1);
+ else
+ c->setParam(ID(CLKPOL), 0);
+ }
+ else
+ log_abort();
+ if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)))
+ c->setParam(ID(ENPOL), 1);
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_)))
+ c->setParam(ID(ENPOL), 0);
+ else
+ c->setParam(ID(ENPOL), 2);
+
+ c->setPort(ID(C), first_cell->getPort(ID(C)));
+ c->setPort(ID(D), first_cell->getPort(ID(D)));
+ c->setPort(ID(Q), last_cell->getPort(ID(Q)));
+ c->setPort(ID(L), GetSize(ud.longest_chain)-1);
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_)))
+ c->setPort(ID(E), State::S1);
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setPort(ID(E), first_cell->getPort(ID(E)));
+ else if (first_cell->type.in(ID(FDRE), ID(FDRE_1)))
+ c->setPort(ID(E), first_cell->getPort(ID(CE)));
+ else
+ log_abort();
+ }
+ else
+ log_abort();
+
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
+void run_variable(xilinx_srl_pm &pm)
+{
+ auto &st = pm.st_variable;
+ auto &ud = pm.ud_variable;
+
+ log("Found variable chain of length %d (%s):\n", GetSize(ud.chain), log_id(st.first->type));
+
+ SigSpec initval;
+ for (const auto &i : ud.chain) {
+ auto cell = i.first;
+ auto slice = i.second;
+ log_debug(" %s\n", log_id(cell));
+ if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) {
+ SigBit Q = cell->getPort(ID(Q))[slice];
+ log_assert(Q.wire);
+ auto it = Q.wire->attributes.find(ID(init));
+ if (it != Q.wire->attributes.end()) {
+ auto &i = it->second[Q.offset];
+ initval.append(i);
+ i = State::Sx;
+ }
+ else
+ initval.append(State::Sx);
+ }
+ else
+ log_abort();
+ }
+ pm.autoremove(st.shiftx);
+
+ auto first_cell = ud.chain.back().first;
+ auto first_slice = ud.chain.back().second;
+
+ Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_));
+ pm.module->swap_names(c, first_cell);
+
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) {
+ c->setParam(ID(DEPTH), GetSize(ud.chain));
+ c->setParam(ID(INIT), initval.as_const());
+ Const clkpol, enpol;
+ if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ clkpol = 1;
+ else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_)))
+ clkpol = 0;
+ else if (first_cell->type.in(ID($dff), ID($dffe)))
+ clkpol = first_cell->getParam(ID(CLK_POLARITY));
+ else
+ log_abort();
+ if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)))
+ enpol = 1;
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_)))
+ enpol = 0;
+ else if (first_cell->type.in(ID($dffe)))
+ enpol = first_cell->getParam(ID(EN_POLARITY));
+ else
+ enpol = 2;
+ c->setParam(ID(CLKPOL), clkpol);
+ c->setParam(ID(ENPOL), enpol);
+
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setPort(ID(C), first_cell->getPort(ID(C)));
+ else if (first_cell->type.in(ID($dff), ID($dffe)))
+ c->setPort(ID(C), first_cell->getPort(ID(CLK)));
+ else
+ log_abort();
+ c->setPort(ID(D), first_cell->getPort(ID(D))[first_slice]);
+ c->setPort(ID(Q), st.shiftx->getPort(ID(Y)));
+ c->setPort(ID(L), st.shiftx->getPort(ID(B)));
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($dff)))
+ c->setPort(ID(E), State::S1);
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setPort(ID(E), first_cell->getPort(ID(E)));
+ else if (first_cell->type.in(ID($dffe)))
+ c->setPort(ID(E), first_cell->getPort(ID(EN)));
+ else
+ log_abort();
+ }
+ else
+ log_abort();
+
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
+struct XilinxSrlPass : public Pass {
+ XilinxSrlPass() : Pass("xilinx_srl", "Xilinx shift register extraction") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" xilinx_srl [options] [selection]\n");
+ log("\n");
+ log("This pass converts chains of built-in flops (bit-level: $_DFF_[NP]_, $_DFFE_*\n");
+ log("and word-level: $dff, $dffe) as well as Xilinx flops (FDRE, FDRE_1) into a\n");
+ log("$__XILINX_SHREG cell. Chains must be of the same cell type, clock, clock polarity,\n");
+ log("enable, and enable polarity (where relevant).\n");
+ log("Flops with resets cannot be mapped to Xilinx devices and will not be inferred.");
+ log("\n");
+ log(" -minlen N\n");
+ log(" min length of shift register (default = 3)\n");
+ log("\n");
+ log(" -fixed\n");
+ log(" infer fixed-length shift registers.\n");
+ log("\n");
+ log(" -variable\n");
+ log(" infer variable-length shift registers (i.e. fixed-length shifts where\n");
+ log(" each element also fans-out to a $shiftx cell).\n");
+ log("\n");
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing XILINX_SRL pass (Xilinx shift register extraction).\n");
+
+ bool fixed = false;
+ bool variable = false;
+ int minlen = 3;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-minlen" && argidx+1 < args.size()) {
+ minlen = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-fixed") {
+ fixed = true;
+ continue;
+ }
+ if (args[argidx] == "-variable") {
+ variable = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!fixed && !variable)
+ log_cmd_error("'-fixed' and/or '-variable' must be specified.\n");
+
+ for (auto module : design->selected_modules()) {
+ auto pm = xilinx_srl_pm(module, module->selected_cells());
+ pm.ud_fixed.minlen = minlen;
+ pm.ud_variable.minlen = minlen;
+
+ if (fixed)
+ pm.run_fixed(run_fixed);
+ if (variable)
+ pm.run_variable(run_variable);
+ }
+ }
+} XilinxSrlPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/xilinx_srl.pmg b/passes/pmgen/xilinx_srl.pmg
new file mode 100644
index 000000000..b18119b87
--- /dev/null
+++ b/passes/pmgen/xilinx_srl.pmg
@@ -0,0 +1,326 @@
+pattern fixed
+
+state <IdString> clk_port en_port
+udata <vector<Cell*>> chain longest_chain
+udata <pool<Cell*>> non_first_cells
+udata <int> minlen
+
+code
+ non_first_cells.clear();
+ subpattern(setup);
+endcode
+
+match first
+ select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !first->has_keep_attr()
+ select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero()
+ filter !non_first_cells.count(first)
+generate
+ SigSpec C = module->addWire(NEW_ID);
+ SigSpec D = module->addWire(NEW_ID);
+ SigSpec Q = module->addWire(NEW_ID);
+ auto r = rng(8);
+ Cell* cell;
+ switch (r)
+ {
+ case 0:
+ case 1:
+ cell = module->addCell(NEW_ID, \FDRE);
+ cell->setPort(\C, C);
+ cell->setPort(\D, D);
+ cell->setPort(\Q, Q);
+ cell->setPort(\CE, module->addWire(NEW_ID));
+ if (r & 1)
+ cell->setPort(\R, module->addWire(NEW_ID));
+ else {
+ if (rng(2) == 0)
+ cell->setPort(\R, State::S0);
+ }
+ break;
+ case 2:
+ case 3:
+ cell = module->addDffGate(NEW_ID, C, D, Q, r & 1);
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ cell = module->addDffeGate(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 2);
+ break;
+ default: log_abort();
+ }
+endmatch
+
+code clk_port en_port
+ if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1))
+ clk_port = \C;
+ else log_abort();
+ if (first->type.in($_DFF_N_, $_DFF_P_))
+ en_port = IdString();
+ else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ en_port = \E;
+ else if (first->type.in(\FDRE, \FDRE_1))
+ en_port = \CE;
+ else log_abort();
+
+ longest_chain.clear();
+ chain.push_back(first);
+ subpattern(tail);
+finally
+ chain.pop_back();
+ log_assert(chain.empty());
+ if (GetSize(longest_chain) >= minlen)
+ accept;
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern setup
+arg clk_port
+arg en_port
+
+match first
+ select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !first->has_keep_attr()
+ select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero()
+endmatch
+
+code clk_port en_port
+ if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1))
+ clk_port = \C;
+ else log_abort();
+ if (first->type.in($_DFF_N_, $_DFF_P_))
+ en_port = IdString();
+ else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ en_port = \E;
+ else if (first->type.in(\FDRE, \FDRE_1))
+ en_port = \CE;
+ else log_abort();
+endcode
+
+match next
+ select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !next->has_keep_attr()
+ select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep)
+ select nusers(port(next, \Q)) == 2
+ index <IdString> next->type === first->type
+ index <SigBit> port(next, \Q) === port(first, \D)
+ filter port(next, clk_port) == port(first, clk_port)
+ filter en_port == IdString() || port(next, en_port) == port(first, en_port)
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero()
+endmatch
+
+code
+ non_first_cells.insert(next);
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern tail
+arg first
+arg clk_port
+arg en_port
+
+match next
+ semioptional
+ select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !next->has_keep_attr()
+ select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep)
+ select nusers(port(next, \Q)) == 2
+ index <IdString> next->type === chain.back()->type
+ index <SigBit> port(next, \Q) === port(chain.back(), \D)
+ filter port(next, clk_port) == port(first, clk_port)
+ filter en_port == IdString() || port(next, en_port) == port(first, en_port)
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero()
+generate
+ Cell *cell = module->addCell(NEW_ID, chain.back()->type);
+ cell->setPort(\C, chain.back()->getPort(\C));
+ cell->setPort(\D, module->addWire(NEW_ID));
+ cell->setPort(\Q, chain.back()->getPort(\D));
+ if (cell->type == \FDRE) {
+ if (rng(2) == 0)
+ cell->setPort(\R, chain.back()->connections_.at(\R, State::S0));
+ cell->setPort(\CE, chain.back()->getPort(\CE));
+ }
+ else if (cell->type.begins_with("$_DFFE_"))
+ cell->setPort(\E, chain.back()->getPort(\E));
+endmatch
+
+code
+ if (next) {
+ chain.push_back(next);
+ subpattern(tail);
+ } else {
+ if (GetSize(chain) > GetSize(longest_chain))
+ longest_chain = chain;
+ }
+finally
+ if (next)
+ chain.pop_back();
+endcode
+
+// -----------
+
+pattern variable
+
+state <IdString> clk_port en_port
+state <int> shiftx_width
+state <int> slice
+udata <int> minlen
+udata <vector<pair<Cell*,int>>> chain
+udata <pool<SigBit>> chain_bits
+
+code
+ chain_bits.clear();
+endcode
+
+match shiftx
+ select shiftx->type.in($shiftx)
+ select !shiftx->has_keep_attr()
+ select param(shiftx, \Y_WIDTH).as_int() == 1
+ filter param(shiftx, \A_WIDTH).as_int() >= minlen
+generate
+ minlen = 3;
+ module->addShiftx(NEW_ID, module->addWire(NEW_ID, rng(6)+minlen), module->addWire(NEW_ID, 3), module->addWire(NEW_ID));
+endmatch
+
+code shiftx_width
+ shiftx_width = param(shiftx, \A_WIDTH).as_int();
+endcode
+
+match first
+ select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, $dff, $dffe)
+ select !first->has_keep_attr()
+ select port(first, \Q)[0].wire && !port(first, \Q)[0].wire->get_bool_attribute(\keep)
+ slice idx GetSize(port(first, \Q))
+ select nusers(port(first, \Q)[idx]) <= 2
+ index <SigBit> port(first, \Q)[idx] === port(shiftx, \A)[shiftx_width-1]
+ set slice idx
+generate
+ SigSpec C = module->addWire(NEW_ID);
+ auto WIDTH = rng(3)+1;
+ SigSpec D = module->addWire(NEW_ID, WIDTH);
+ SigSpec Q = module->addWire(NEW_ID, WIDTH);
+ auto r = rng(8);
+ Cell *cell = nullptr;
+ switch (r)
+ {
+ case 0:
+ case 1:
+ cell = module->addDff(NEW_ID, C, D, Q, r & 1);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ //cell = module->addDffe(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 4);
+ //break;
+ case 6:
+ case 7:
+ WIDTH = 1;
+ cell = module->addDffGate(NEW_ID, C, D[0], Q[0], r & 1);
+ break;
+ default: log_abort();
+ }
+ shiftx->connections_.at(\A)[shiftx_width-1] = port(cell, \Q)[rng(WIDTH)];
+endmatch
+
+code clk_port en_port
+ if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ clk_port = \C;
+ else if (first->type.in($dff, $dffe))
+ clk_port = \CLK;
+ else log_abort();
+ if (first->type.in($_DFF_N_, $_DFF_P_, $dff))
+ en_port = IdString();
+ else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ en_port = \E;
+ else if (first->type.in($dffe))
+ en_port = \EN;
+ else log_abort();
+
+ chain_bits.insert(port(first, \Q)[slice]);
+ chain.emplace_back(first, slice);
+ subpattern(tail);
+finally
+ if (GetSize(chain) == shiftx_width)
+ accept;
+ chain.clear();
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern tail
+arg first
+arg shiftx
+arg shiftx_width
+arg slice
+arg clk_port
+arg en_port
+
+match next
+ semioptional
+ select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, $dff, $dffe)
+ select !next->has_keep_attr()
+ select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep)
+ slice idx GetSize(port(next, \Q))
+ select nusers(port(next, \Q)[idx]) <= 3
+ index <IdString> next->type === chain.back().first->type
+ index <SigBit> port(next, \Q)[idx] === port(chain.back().first, \D)[chain.back().second]
+ index <SigBit> port(next, \Q)[idx] === port(shiftx, \A)[shiftx_width-1-GetSize(chain)]
+ filter port(next, clk_port) == port(first, clk_port)
+ filter en_port == IdString() || port(next, en_port) == port(first, en_port)
+ filter !next->type.in($dff, $dffe) || param(next, \CLK_POLARITY).as_bool() == param(first, \CLK_POLARITY).as_bool()
+ filter !next->type.in($dffe) || param(next, \EN_POLARITY).as_bool() == param(first, \EN_POLARITY).as_bool()
+ filter !chain_bits.count(port(next, \D)[idx])
+ set slice idx
+generate
+ if (GetSize(chain) < shiftx_width) {
+ auto back = chain.back().first;
+ auto slice = chain.back().second;
+ if (back->type.in($dff, $dffe)) {
+ auto WIDTH = GetSize(port(back, \D));
+ if (rng(2) == 0 && slice < WIDTH-1) {
+ auto new_slice = slice + rng(WIDTH-1-slice);
+ back->connections_.at(\D)[slice] = port(back, \Q)[new_slice];
+ }
+ else {
+ auto D = module->addWire(NEW_ID, WIDTH);
+ if (back->type == $dff)
+ module->addDff(NEW_ID, port(back, \CLK), D, port(back, \D), param(back, \CLK_POLARITY).as_bool());
+ else if (back->type == $dffe)
+ module->addDffe(NEW_ID, port(back, \CLK), port(back, \EN), D, port(back, \D), param(back, \CLK_POLARITY).as_bool(), param(back, \EN_POLARITY).as_bool());
+ else
+ log_abort();
+ }
+ }
+ else if (back->type.begins_with("$_DFF_")) {
+ Cell *cell = module->addCell(NEW_ID, back->type);
+ cell->setPort(\C, back->getPort(\C));
+ cell->setPort(\D, module->addWire(NEW_ID));
+ cell->setPort(\Q, back->getPort(\D));
+ }
+ else
+ log_abort();
+ shiftx->connections_.at(\A)[shiftx_width-1-GetSize(chain)] = port(back, \D)[slice];
+ }
+endmatch
+
+code
+ if (next) {
+ chain_bits.insert(port(next, \Q)[slice]);
+ chain.emplace_back(next, slice);
+ if (GetSize(chain) < shiftx_width)
+ subpattern(tail);
+ }
+endcode
diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc
index 84cb2c04f..968c68b70 100644
--- a/passes/techmap/abc9.cc
+++ b/passes/techmap/abc9.cc
@@ -76,8 +76,7 @@ inline std::string remap_name(RTLIL::IdString abc_name)
return stringf("$abc$%d$%s", map_autoidx, abc_name.c_str()+1);
}
-void handle_loops(RTLIL::Design *design,
- const dict<IdString,pool<IdString>> &scc_break_inputs)
+void handle_loops(RTLIL::Design *design)
{
Pass::call(design, "scc -set_attr abc_scc_id {}");
@@ -114,30 +113,6 @@ void handle_loops(RTLIL::Design *design,
}
cell->attributes.erase(it);
}
-
- auto jt = scc_break_inputs.find(cell->type);
- if (jt != scc_break_inputs.end())
- for (auto port_name : jt->second) {
- RTLIL::SigSpec sig;
- auto &rhs = cell->connections_.at(port_name);
- for (auto b : rhs) {
- Wire *w = b.wire;
- if (!w) continue;
- w->port_output = true;
- w->set_bool_attribute(ID(abc_scc_break));
- w = module->wire(stringf("%s.abci", w->name.c_str()));
- if (!w) {
- w = module->addWire(stringf("%s.abci", b.wire->name.c_str()), GetSize(b.wire));
- w->port_input = true;
- }
- else {
- log_assert(b.offset < GetSize(w));
- log_assert(w->port_input);
- }
- sig.append(RTLIL::SigBit(w, b.offset));
- }
- rhs = sig;
- }
}
module->fixup_ports();
@@ -272,8 +247,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
bool /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
bool show_tempdir, std::string box_file, std::string lut_file,
- std::string wire_delay, const dict<int,IdString> &box_lookup,
- const dict<IdString,pool<IdString>> &scc_break_inputs
+ std::string wire_delay, const dict<int,IdString> &box_lookup
)
{
module = current_module;
@@ -413,7 +387,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
RTLIL::Selection& sel = design->selection_stack.back();
sel.select(module);
- handle_loops(design, scc_break_inputs);
+ handle_loops(design);
Pass::call(design, "aigmap");
@@ -1067,7 +1041,6 @@ struct Abc9Pass : public Pass {
extra_args(args, argidx, design);
dict<int,IdString> box_lookup;
- dict<IdString,pool<IdString>> scc_break_inputs;
for (auto m : design->modules()) {
auto it = m->attributes.find(ID(abc_box_id));
if (it == m->attributes.end())
@@ -1085,17 +1058,13 @@ struct Abc9Pass : public Pass {
for (auto p : m->ports) {
auto w = m->wire(p);
log_assert(w);
- if (w->port_input) {
- if (w->attributes.count(ID(abc_scc_break)))
- scc_break_inputs[m->name].insert(p);
- if (w->attributes.count(ID(abc_carry))) {
+ if (w->attributes.count(ID(abc_carry))) {
+ if (w->port_input) {
if (carry_in)
log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));
carry_in = w;
}
- }
- if (w->port_output) {
- if (w->attributes.count(ID(abc_carry))) {
+ else if (w->port_output) {
if (carry_out)
log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));
carry_out = w;
@@ -1147,7 +1116,7 @@ struct Abc9Pass : public Pass {
if (!dff_mode || !clk_str.empty()) {
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, dff_mode, clk_str, keepff,
delay_target, lutin_shared, fast_mode, show_tempdir,
- box_file, lut_file, wire_delay, box_lookup, scc_break_inputs);
+ box_file, lut_file, wire_delay, box_lookup);
continue;
}
@@ -1293,7 +1262,7 @@ struct Abc9Pass : public Pass {
en_sig = assign_map(std::get<3>(it.first));
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$",
keepff, delay_target, lutin_shared, fast_mode, show_tempdir,
- box_file, lut_file, wire_delay, box_lookup, scc_break_inputs);
+ box_file, lut_file, wire_delay, box_lookup);
assign_map.set(mod);
}
}
diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc
index 5e298d8dd..9da69e8ba 100644
--- a/passes/techmap/shregmap.cc
+++ b/passes/techmap/shregmap.cc
@@ -26,9 +26,7 @@ PRIVATE_NAMESPACE_BEGIN
struct ShregmapTech
{
virtual ~ShregmapTech() { }
- 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 analyze(vector<int> &taps) = 0;
virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
};
@@ -56,7 +54,7 @@ struct ShregmapOptions
struct ShregmapTechGreenpak4 : ShregmapTech
{
- bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/)
+ bool analyze(vector<int> &taps)
{
if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
taps.clear();
@@ -93,155 +91,6 @@ 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 == ID($shiftx)) {
- if (cell->getParam(ID(Y_WIDTH)) != 1) continue;
- int j = 0;
- for (auto bit : sigmap(cell->getPort(ID::A)))
- sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j++, 0);
- log_assert(j == cell->getParam(ID(A_WIDTH)).as_int());
- }
- else if (cell->type == ID($mux)) {
- int j = 0;
- for (auto bit : sigmap(cell->getPort(ID::A)))
- sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++);
- j = 0;
- for (auto bit : sigmap(cell->getPort(ID::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 == ID($shiftx) && port == ID::A)
- return;
- if (cell->type == ID($mux) && port.in(ID::A, ID::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 == ID($shiftx)) {
- if (GetSize(taps) > shiftx->getParam(ID(A_WIDTH)).as_int())
- return false;
- // Due to padding the most significant bits of A may be 1'bx,
- // and if so, discount them
- if (GetSize(taps) < shiftx->getParam(ID(A_WIDTH)).as_int()) {
- const SigSpec A = shiftx->getPort(ID::A);
- const int A_width = shiftx->getParam(ID(A_WIDTH)).as_int();
- for (int i = GetSize(taps); i < A_width; ++i)
- if (A[i] != RTLIL::Sx) return false;
- }
- else if (GetSize(taps) != shiftx->getParam(ID(A_WIDTH)).as_int())
- return false;
- }
- else if (shiftx->type == ID($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, ID($__XILINX_SHREG_));
- newcell->set_src_attribute(cell->get_src_attribute());
- newcell->setParam(ID(DEPTH), cell->getParam(ID(DEPTH)));
- newcell->setParam(ID(INIT), cell->getParam(ID(INIT)));
- newcell->setParam(ID(CLKPOL), cell->getParam(ID(CLKPOL)));
- newcell->setParam(ID(ENPOL), cell->getParam(ID(ENPOL)));
-
- newcell->setPort(ID(C), cell->getPort(ID(C)));
- newcell->setPort(ID(D), cell->getPort(ID(D)));
- if (cell->hasPort(ID(E)))
- newcell->setPort(ID(E), cell->getPort(ID(E)));
-
- Cell* shiftx = std::get<0>(it->second);
- RTLIL::SigSpec l_wire, q_wire;
- if (shiftx->type == ID($shiftx)) {
- l_wire = shiftx->getPort(ID::B);
- q_wire = shiftx->getPort(ID::Y);
- shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID));
- }
- else if (shiftx->type == ID($mux)) {
- l_wire = shiftx->getPort(ID(S));
- q_wire = shiftx->getPort(ID::Y);
- shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID));
- }
- else log_abort();
-
- newcell->setPort(ID(Q), q_wire);
- newcell->setPort(ID(L), l_wire);
-
- return false;
- }
-};
-
-
struct ShregmapWorker
{
Module *module;
@@ -264,10 +113,8 @@ struct ShregmapWorker
for (auto wire : module->wires())
{
if (wire->port_output || wire->get_bool_attribute(ID::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(ID(init))) {
@@ -317,10 +164,8 @@ 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);
- }
}
}
@@ -425,7 +270,7 @@ struct ShregmapWorker
if (taps.empty() || taps.back() < depth-1)
taps.push_back(depth-1);
- if (opts.tech->analyze(taps, qbits))
+ if (opts.tech->analyze(taps))
break;
taps.pop_back();
@@ -544,9 +389,6 @@ 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();
@@ -617,11 +459,6 @@ struct ShregmapPass : public Pass {
log("\n");
log(" -tech greenpak4\n");
log(" map to greenpak4 shift registers.\n");
- log(" this option also implies -clkpol pos -zinit\n");
- log("\n");
- log(" -tech xilinx\n");
- log(" map to xilinx dynamic-length shift registers.\n");
- log(" this option also implies -params -init\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -676,12 +513,6 @@ 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/ecp5/Makefile.inc b/techlibs/ecp5/Makefile.inc
index c41d16076..c02ee677f 100644
--- a/techlibs/ecp5/Makefile.inc
+++ b/techlibs/ecp5/Makefile.inc
@@ -12,6 +12,9 @@ $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram.txt))
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/arith_map.v))
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v))
+$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_map.v))
+$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_unmap.v))
+$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_model.v))
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.box))
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.lut))
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g_nowide.lut))
diff --git a/techlibs/ecp5/abc_5g.box b/techlibs/ecp5/abc_5g.box
index c757d137d..66ebe196a 100644
--- a/techlibs/ecp5/abc_5g.box
+++ b/techlibs/ecp5/abc_5g.box
@@ -15,16 +15,16 @@ CCU2C 1 1 9 3
630 379 630 379 526 275 392 141 273
516 516 516 516 412 412 278 278 43
-# Box 2 : TRELLIS_DPR16X4 (16x4 dist ram)
+# Box 2 : TRELLIS_DPR16X4_COMB (16x4 dist ram)
# Outputs: DO0, DO1, DO2, DO3
-# name ID w/b ins outs
-TRELLIS_DPR16X4 2 0 14 4
-
-#DI0 DI1 DI2 DI3 RAD0 RAD1 RAD2 RAD3 WAD0 WAD1 WAD2 WAD3 WCK WRE
-- - - - 141 379 275 379 - - - - - -
-- - - - 141 379 275 379 - - - - - -
-- - - - 141 379 275 379 - - - - - -
-- - - - 141 379 275 379 - - - - - -
+# name ID w/b ins outs
+$__ABC_RAM16X2_COMB 2 0 8 4
+
+#A0 A1 A2 A3 RAD0 RAD1 RAD2 RAD3
+0 0 0 0 141 379 275 379
+0 0 0 0 141 379 275 379
+0 0 0 0 141 379 275 379
+0 0 0 0 141 379 275 379
# Box 3 : PFUMX (MUX2)
# Outputs: Z
diff --git a/techlibs/ecp5/abc_map.v b/techlibs/ecp5/abc_map.v
new file mode 100644
index 000000000..ffd25f06d
--- /dev/null
+++ b/techlibs/ecp5/abc_map.v
@@ -0,0 +1,24 @@
+// ---------------------------------------
+
+module TRELLIS_DPR16X4 (
+ input [3:0] DI,
+ input [3:0] WAD,
+ input WRE,
+ input WCK,
+ input [3:0] RAD,
+ output [3:0] DO
+);
+ parameter WCKMUX = "WCK";
+ parameter WREMUX = "WRE";
+ parameter [63:0] INITVAL = 64'h0000000000000000;
+ wire [3:0] \$DO ;
+
+ TRELLIS_DPR16X4 #(
+ .WCKMUX(WCKMUX), .WREMUX(WREMUX), .INITVAL(INITVAL)
+ ) _TECHMAP_REPLACE_ (
+ .DI(DI), .WAD(WAD), .WRE(WRE), .WCK(WCK),
+ .RAD(RAD), .DO(\$DO )
+ );
+
+ \$__ABC_DPR16X4_COMB do (.A(\$DO ), .S(RAD), .Y(DO));
+endmodule
diff --git a/techlibs/ecp5/abc_model.v b/techlibs/ecp5/abc_model.v
new file mode 100644
index 000000000..56a733b75
--- /dev/null
+++ b/techlibs/ecp5/abc_model.v
@@ -0,0 +1,5 @@
+// ---------------------------------------
+
+(* abc_box_id=2 *)
+module \$__ABC_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y);
+endmodule
diff --git a/techlibs/ecp5/abc_unmap.v b/techlibs/ecp5/abc_unmap.v
new file mode 100644
index 000000000..d43cdd93f
--- /dev/null
+++ b/techlibs/ecp5/abc_unmap.v
@@ -0,0 +1,5 @@
+// ---------------------------------------
+
+module \$__ABC_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y);
+ assign Y = A;
+endmodule
diff --git a/techlibs/ecp5/cells_sim.v b/techlibs/ecp5/cells_sim.v
index dc8334acb..01b10f392 100644
--- a/techlibs/ecp5/cells_sim.v
+++ b/techlibs/ecp5/cells_sim.v
@@ -109,16 +109,13 @@ module PFUMX (input ALUT, BLUT, C0, output Z);
endmodule
// ---------------------------------------
-//(* abc_box_id=2 *)
module TRELLIS_DPR16X4 (
- (* abc_scc_break *)
input [3:0] DI,
- (* abc_scc_break *)
input [3:0] WAD,
- (* abc_scc_break *)
input WRE,
input WCK,
input [3:0] RAD,
+ /* (* abc_arrival=<TODO> *) */
output [3:0] DO
);
parameter WCKMUX = "WCK";
diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc
index a8075e86e..2593546e0 100644
--- a/techlibs/ecp5/synth_ecp5.cc
+++ b/techlibs/ecp5/synth_ecp5.cc
@@ -280,12 +280,17 @@ struct SynthEcp5Pass : public ScriptPass
if (abc2 || help_mode) {
run("abc", " (only if -abc2)");
}
- run("techmap -map +/ecp5/latches_map.v");
+ std::string techmap_args = "-map +/ecp5/latches_map.v";
+ if (abc9)
+ techmap_args += " -map +/ecp5/abc_map.v -max_iter 1";
+ run("techmap " + techmap_args);
+
if (abc9) {
if (nowidelut)
run("abc9 -lut +/ecp5/abc_5g_nowide.lut -box +/ecp5/abc_5g.box -W 200");
else
run("abc9 -lut +/ecp5/abc_5g.lut -box +/ecp5/abc_5g.box -W 200");
+ run("techmap -map +/ecp5/abc_unmap.v");
} else {
if (nowidelut)
run("abc -lut 4 -dress");
diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc
index 2b1af289c..2efcf7d90 100644
--- a/techlibs/xilinx/Makefile.inc
+++ b/techlibs/xilinx/Makefile.inc
@@ -39,6 +39,9 @@ $(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/mux_map.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_map.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_unmap.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_model.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7.box))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7.lut))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7_nowide.lut))
diff --git a/techlibs/xilinx/abc_map.v b/techlibs/xilinx/abc_map.v
new file mode 100644
index 000000000..c3701b1a8
--- /dev/null
+++ b/techlibs/xilinx/abc_map.v
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ *
+ */
+
+// ============================================================================
+
+module RAM32X1D (
+ output DPO, SPO,
+ input D,
+ input WCLK,
+ input WE,
+ input A0, A1, A2, A3, A4,
+ input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4
+);
+ parameter INIT = 32'h0;
+ parameter IS_WCLK_INVERTED = 1'b0;
+ wire \$DPO , \$SPO ;
+ RAM32X1D #(
+ .INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED)
+ ) _TECHMAP_REPLACE_ (
+ .DPO(\$DPO ), .SPO(\$SPO ),
+ .D(D), .WCLK(WCLK), .WE(WE),
+ .A0(A0), .A1(A1), .A2(A2), .A3(A3), .A4(A4),
+ .DPRA0(DPRA0), .DPRA1(DPRA1), .DPRA2(DPRA2), .DPRA3(DPRA3), .DPRA4(DPRA4)
+ );
+ \$__ABC_LUT6 dpo (.A(\$DPO ), .S({1'b0, A0, A1, A2, A3, A4}), .Y(DPO));
+ \$__ABC_LUT6 spo (.A(\$SPO ), .S({1'b0, A0, A1, A2, A3, A4}), .Y(SPO));
+endmodule
+
+module RAM64X1D (
+ output DPO, SPO,
+ input D,
+ input WCLK,
+ input WE,
+ input A0, A1, A2, A3, A4, A5,
+ input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5
+);
+ parameter INIT = 64'h0;
+ parameter IS_WCLK_INVERTED = 1'b0;
+ wire \$DPO , \$SPO ;
+ RAM64X1D #(
+ .INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED)
+ ) _TECHMAP_REPLACE_ (
+ .DPO(\$DPO ), .SPO(\$SPO ),
+ .D(D), .WCLK(WCLK), .WE(WE),
+ .A0(A0), .A1(A1), .A2(A2), .A3(A3), .A4(A4), .A5(A5),
+ .DPRA0(DPRA0), .DPRA1(DPRA1), .DPRA2(DPRA2), .DPRA3(DPRA3), .DPRA4(DPRA4), .DPRA5(DPRA5)
+ );
+ \$__ABC_LUT6 dpo (.A(\$DPO ), .S({A0, A1, A2, A3, A4, A5}), .Y(DPO));
+ \$__ABC_LUT6 spo (.A(\$SPO ), .S({A0, A1, A2, A3, A4, A5}), .Y(SPO));
+endmodule
+
+module RAM128X1D (
+ output DPO, SPO,
+ input D,
+ input WCLK,
+ input WE,
+ input [6:0] A, DPRA
+);
+ parameter INIT = 128'h0;
+ parameter IS_WCLK_INVERTED = 1'b0;
+ wire \$DPO , \$SPO ;
+ RAM128X1D #(
+ .INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED)
+ ) _TECHMAP_REPLACE_ (
+ .DPO(\$DPO ), .SPO(\$SPO ),
+ .D(D), .WCLK(WCLK), .WE(WE),
+ .A(A),
+ .DPRA(DPRA)
+ );
+ \$__ABC_LUT7 dpo (.A(\$DPO ), .S(A), .Y(DPO));
+ \$__ABC_LUT7 spo (.A(\$SPO ), .S(A), .Y(SPO));
+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;
+ wire \$Q ;
+ SRL16E #(
+ .INIT(INIT), .IS_CLK_INVERTED(IS_CLK_INVERTED)
+ ) _TECHMAP_REPLACE_ (
+ .Q(\$Q ),
+ .A0(A0), .A1(A1), .A2(A2), .A3(A3), .CE(CE), .CLK(CLK), .D(D)
+ );
+ // TODO: Check if SRL uses fast inputs or slow inputs
+ \$__ABC_LUT6 q (.A(\$Q ), .S({A0, A1, A2, A3, 1'b0, 1'b0}), .Y(Q));
+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;
+ wire \$Q ;
+ SRLC32E #(
+ .INIT(INIT), .IS_CLK_INVERTED(IS_CLK_INVERTED)
+ ) _TECHMAP_REPLACE_ (
+ .Q(\$Q ), .Q31(Q31),
+ .A(A), .CE(CE), .CLK(CLK), .D(D)
+ );
+ // TODO: Check if SRL uses fast inputs or slow inputs
+ \$__ABC_LUT6 q (.A(\$Q ), .S({A, 1'b0}), .Y(Q));
+endmodule
diff --git a/techlibs/xilinx/abc_model.v b/techlibs/xilinx/abc_model.v
new file mode 100644
index 000000000..655b993f6
--- /dev/null
+++ b/techlibs/xilinx/abc_model.v
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ *
+ */
+
+// ============================================================================
+
+(* abc_box_id = 3, lib_whitebox *)
+module \$__XILINX_MUXF78 (output O, input I0, I1, I2, I3, S0, S1);
+ assign O = S1 ? (S0 ? I3 : I2)
+ : (S0 ? I1 : I0);
+endmodule
+
+(* abc_box_id=2000 *)
+module \$__ABC_LUT6 (input A, input [5:0] S, output Y);
+endmodule
+(* abc_box_id=2001 *)
+module \$__ABC_LUT7 (input A, input [6:0] S, output Y);
+endmodule
diff --git a/techlibs/xilinx/abc_unmap.v b/techlibs/xilinx/abc_unmap.v
new file mode 100644
index 000000000..f101a22d0
--- /dev/null
+++ b/techlibs/xilinx/abc_unmap.v
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ *
+ */
+
+// ============================================================================
+
+module \$__ABC_LUT6 (input A, input [5:0] S, output Y);
+ assign Y = A;
+endmodule
+module \$__ABC_LUT7 (input A, input [6:0] S, output Y);
+ assign Y = A;
+endmodule
diff --git a/techlibs/xilinx/abc_xc7.box b/techlibs/xilinx/abc_xc7.box
index 3789ff350..554cc0cf0 100644
--- a/techlibs/xilinx/abc_xc7.box
+++ b/techlibs/xilinx/abc_xc7.box
@@ -1,4 +1,5 @@
# Max delays from https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf
+# https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/slicel.sdf
# NB: Inputs/Outputs must be ordered alphabetically
# (with exceptions for carry in/out)
@@ -14,6 +15,7 @@ F7MUX 1 1 3 1
MUXF8 2 1 3 1
104 94 273
+# Box containing MUXF7.[AB] + MUXF8
# Inputs: I0 I1 I2 I3 S0 S1
# Outputs: O
$__MUXF78 3 1 6 1
@@ -37,22 +39,15 @@ CARRY4 4 1 10 8
580 526 507 398 385 508 528 378 380 114
# SLICEM/A6LUT
-# Inputs: A0 A1 A2 A3 A4 D DPRA0 DPRA1 DPRA2 DPRA3 DPRA4 WCLK WE
+# Box to emulate comb/seq behaviour of RAMD{32,64} and SRL{16,32}
+# Inputs: A S0 S1 S2 S3 S4 S5
+# Outputs: Y
+$__ABC_LUT6 2000 0 7 1
+0 642 631 472 407 238 127
+
+# SLICEM/A6LUT + F7BMUX
+# Box to emulate comb/seq behaviour of RAMD128
+# Inputs: A S0 S1 S2 S3 S4 S5 S6
# Outputs: DPO SPO
-RAM32X1D 5 0 13 2
-- - - - - - 631 472 407 238 127 - -
-631 472 407 238 127 - - - - - - - -
-
-# SLICEM/A6LUT
-# Inputs: A0 A1 A2 A3 A4 A5 D DPRA0 DPRA1 DPRA2 DPRA3 DPRA4 DPRA5 WCLK WE
-# Outputs: DPO SPO
-RAM64X1D 6 0 15 2
-- - - - - - - 642 631 472 407 238 127 - -
-642 631 472 407 238 127 - - - - - - - - -
-
-# SLICEM/A6LUT + F7[AB]MUX
-# Inputs: A0 A1 A2 A3 A4 A5 A6 D DPRA0 DPRA1 DPRA2 DPRA3 DPRA4 DPRA5 DPRA6 WCLK WE
-# Outputs: DPO SPO
-RAM128X1D 7 0 17 2
-- - - - - - - - 1009 998 839 774 605 494 450 - -
-1047 1036 877 812 643 532 478 - - - - - - - - - -
+$__ABC_LUT7 2001 0 8 1
+0 1047 1036 877 812 643 532 478
diff --git a/techlibs/xilinx/cells_map.v b/techlibs/xilinx/cells_map.v
index b8e5bafc7..a15884ec4 100644
--- a/techlibs/xilinx/cells_map.v
+++ b/techlibs/xilinx/cells_map.v
@@ -331,7 +331,6 @@ module \$_MUX16_ (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V, Y)
endmodule
`endif
-`ifndef _ABC
module \$__XILINX_MUXF78 (O, I0, I1, I2, I3, S0, S1);
output O;
input I0, I1, I2, I3, S0, S1;
@@ -364,4 +363,3 @@ module \$__XILINX_MUXF78 (O, I0, I1, I2, I3, S0, S1);
else
MUXF8 mux8 (.I0(T0), .I1(T1), .S(S1), .O(O));
endmodule
-`endif
diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v
index e12b77c02..b4657daca 100644
--- a/techlibs/xilinx/cells_sim.v
+++ b/techlibs/xilinx/cells_sim.v
@@ -184,14 +184,6 @@ module MUXF8(output O, input I0, I1, S);
assign O = S ? I1 : I0;
endmodule
-`ifdef _ABC
-(* abc_box_id = 3, lib_whitebox *)
-module \$__XILINX_MUXF78 (output O, input I0, I1, I2, I3, S0, S1);
- assign O = S1 ? (S0 ? I3 : I2)
- : (S0 ? I1 : I0);
-endmodule
-`endif
-
module XORCY(output O, input CI, LI);
assign O = CI ^ LI;
endmodule
@@ -236,7 +228,15 @@ endmodule
`endif
-module FDRE (output reg Q, (* clkbuf_sink *) input C, input CE, D, R);
+// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf#L238-L250
+
+module FDRE (
+ (* abc_arrival=303 *)
+ output reg Q,
+ (* clkbuf_sink *)
+ input C,
+ input CE, D, R
+);
parameter [0:0] INIT = 1'b0;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
@@ -248,7 +248,13 @@ module FDRE (output reg Q, (* clkbuf_sink *) input C, input CE, D, R);
endcase endgenerate
endmodule
-module FDSE (output reg Q, (* clkbuf_sink *) input C, input CE, D, S);
+module FDSE (
+ (* abc_arrival=303 *)
+ output reg Q,
+ (* clkbuf_sink *)
+ input C,
+ input CE, D, S
+);
parameter [0:0] INIT = 1'b1;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
@@ -260,7 +266,13 @@ module FDSE (output reg Q, (* clkbuf_sink *) input C, input CE, D, S);
endcase endgenerate
endmodule
-module FDCE (output reg Q, (* clkbuf_sink *) input C, input CE, D, CLR);
+module FDCE (
+ (* abc_arrival=303 *)
+ output reg Q,
+ (* clkbuf_sink *)
+ input C,
+ input CE, D, CLR
+);
parameter [0:0] INIT = 1'b0;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
@@ -274,7 +286,13 @@ module FDCE (output reg Q, (* clkbuf_sink *) input C, input CE, D, CLR);
endcase endgenerate
endmodule
-module FDPE (output reg Q, (* clkbuf_sink *) input C, input CE, D, PRE);
+module FDPE (
+ (* abc_arrival=303 *)
+ output reg Q,
+ (* clkbuf_sink *)
+ input C,
+ input CE, D, PRE
+);
parameter [0:0] INIT = 1'b1;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
@@ -288,38 +306,61 @@ module FDPE (output reg Q, (* clkbuf_sink *) input C, input CE, D, PRE);
endcase endgenerate
endmodule
-module FDRE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, R);
+module FDRE_1 (
+ (* abc_arrival=303 *)
+ output reg Q,
+ (* clkbuf_sink *)
+ input C,
+ input CE, D, R
+);
parameter [0:0] INIT = 1'b0;
initial Q <= INIT;
always @(negedge C) if (R) Q <= 1'b0; else if(CE) Q <= D;
endmodule
-module FDSE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, S);
+module FDSE_1 (
+ (* abc_arrival=303 *)
+ output reg Q,
+ (* clkbuf_sink *)
+ input C,
+ input CE, D, S
+);
parameter [0:0] INIT = 1'b1;
initial Q <= INIT;
always @(negedge C) if (S) Q <= 1'b1; else if(CE) Q <= D;
endmodule
-module FDCE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, CLR);
+module FDCE_1 (
+ (* abc_arrival=303 *)
+ output reg Q,
+ (* clkbuf_sink *)
+ input C,
+ input CE, D, CLR
+);
parameter [0:0] INIT = 1'b0;
initial Q <= INIT;
always @(negedge C, posedge CLR) if (CLR) Q <= 1'b0; else if (CE) Q <= D;
endmodule
-module FDPE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, PRE);
+module FDPE_1 (
+ (* abc_arrival=303 *)
+ output reg Q,
+ (* clkbuf_sink *)
+ input C,
+ input CE, D, PRE
+);
parameter [0:0] INIT = 1'b1;
initial Q <= INIT;
always @(negedge C, posedge PRE) if (PRE) Q <= 1'b1; else if (CE) Q <= D;
endmodule
-(* abc_box_id = 5 *)
module RAM32X1D (
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L957
+ (* abc_arrival=1153 *)
output DPO, SPO,
- (* abc_scc_break *)
input D,
(* clkbuf_sink *)
input WCLK,
- (* abc_scc_break *)
input WE,
input A0, A1, A2, A3, A4,
input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4
@@ -335,14 +376,13 @@ module RAM32X1D (
always @(posedge clk) if (WE) mem[a] <= D;
endmodule
-(* abc_box_id = 6 *)
module RAM64X1D (
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L957
+ (* abc_arrival=1153 *)
output DPO, SPO,
- (* abc_scc_break *)
input D,
(* clkbuf_sink *)
input WCLK,
- (* abc_scc_break *)
input WE,
input A0, A1, A2, A3, A4, A5,
input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5
@@ -358,14 +398,13 @@ module RAM64X1D (
always @(posedge clk) if (WE) mem[a] <= D;
endmodule
-(* abc_box_id = 7 *)
module RAM128X1D (
- output DPO, SPO,
- (* abc_scc_break *)
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L957
+ (* abc_arrival=1153 *)
+ output DPO, SPO,
input D,
(* clkbuf_sink *)
input WCLK,
- (* abc_scc_break *)
input WE,
input [6:0] A, DPRA
);
@@ -379,6 +418,8 @@ module RAM128X1D (
endmodule
module SRL16E (
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L904-L905
+ (* abc_arrival=1472 *)
output Q,
input A0, A1, A2, A3, CE,
(* clkbuf_sink *)
@@ -423,7 +464,10 @@ module SRLC16E (
endmodule
module SRLC32E (
+ // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L904-L905
+ (* abc_arrival=1472 *)
output Q,
+ (* abc_arrival=1114 *)
output Q31,
input [4:0] A,
input CE,
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc
index f058da83d..bfc0ac2bf 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -268,9 +268,9 @@ struct SynthXilinxPass : public ScriptPass
{
if (check_label("begin")) {
if (vpr)
- run("read_verilog -lib -icells -D _ABC -D_EXPLICIT_CARRY +/xilinx/cells_sim.v");
+ run("read_verilog -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v");
else
- run("read_verilog -lib -icells -D _ABC +/xilinx/cells_sim.v");
+ run("read_verilog -lib +/xilinx/cells_sim.v");
run("read_verilog -lib +/xilinx/cells_xtra.v");
@@ -303,9 +303,8 @@ struct SynthXilinxPass : public ScriptPass
if (widemux > 0 || help_mode)
run("muxpack", " ('-widemux' only)");
- // shregmap -tech xilinx can cope with $shiftx and $mux
- // cells for identifying variable-length shift registers,
- // so attempt to convert $pmux-es to the former
+ // xilinx_srl looks for $shiftx cells for identifying variable-length
+ // shift registers, so attempt to convert $pmux-es to this
// Also: wide multiplexer inference benefits from this too
if (!(nosrl && widemux == 0) || help_mode) {
run("pmux2shiftx", "(skip if '-nosrl' and '-widemux=0')");
@@ -387,13 +386,8 @@ struct SynthXilinxPass : public ScriptPass
}
run("opt -full");
- if (!nosrl || help_mode) {
- // shregmap operates on bit-level flops, not word-level,
- // so break those down here
- run("simplemap t:$dff t:$dffe", " (skip if '-nosrl')");
- // shregmap with '-tech xilinx' infers variable length shift regs
- run("shregmap -tech xilinx -minlen 3", "(skip if '-nosrl')");
- }
+ if (!nosrl || help_mode)
+ run("xilinx_srl -variable -minlen 3", "(skip if '-nosrl')");
std::string techmap_args = " -map +/techmap.v";
if (help_mode)
@@ -414,22 +408,32 @@ struct SynthXilinxPass : public ScriptPass
}
if (check_label("map_cells")) {
- std::string techmap_args = "-map +/techmap.v -D _ABC -map +/xilinx/cells_map.v";
+ std::string techmap_args = "-map +/techmap.v -map +/xilinx/cells_map.v";
if (widemux > 0)
techmap_args += stringf(" -D MIN_MUX_INPUTS=%d", widemux);
run("techmap " + techmap_args);
run("clean");
}
+ if (check_label("map_ffs")) {
+ if (abc9 || help_mode) {
+ run("techmap -map +/xilinx/ff_map.v", "('-abc9' only)");
+ run("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", "('-abc9' only)");
+ }
+ }
+
if (check_label("map_luts")) {
run("opt_expr -mux_undef");
if (flatten_before_abc)
run("flatten");
if (help_mode)
- run("abc -luts 2:2,3,6:5[,10,20] [-dff]", "(option for 'nowidelut', option for '-retime')");
+ run("abc -luts 2:2,3,6:5[,10,20] [-dff]", "(option for 'nowidelut'; option for '-retime')");
else if (abc9) {
if (family != "xc7")
log_warning("'synth_xilinx -abc9' currently supports '-family xc7' only.\n");
+ run("techmap -map +/xilinx/abc_map.v -max_iter 1");
+ run("read_verilog -icells -lib +/xilinx/abc_model.v");
if (nowidelut)
run("abc9 -lut +/xilinx/abc_xc7_nowide.lut -box +/xilinx/abc_xc7.box -W " + std::to_string(XC7_WIRE_DELAY));
else
@@ -446,10 +450,18 @@ struct SynthXilinxPass : public ScriptPass
// This shregmap call infers fixed length shift registers after abc
// has performed any necessary retiming
if (!nosrl || help_mode)
- run("shregmap -minlen 3 -init -params -enpol any_or_none", "(skip if '-nosrl')");
- run("techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v");
- run("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");
+ run("xilinx_srl -fixed -minlen 3", "(skip if '-nosrl')");
+ std::string techmap_args = "-map +/xilinx/lut_map.v -map +/xilinx/cells_map.v";
+ if (help_mode)
+ techmap_args += " [-map +/xilinx/ff_map.v]";
+ else if (abc9)
+ techmap_args += " -map +/xilinx/abc_unmap.v";
+ else
+ techmap_args += " -map +/xilinx/ff_map.v";
+ run("techmap " + techmap_args);
+ if (!abc9 || help_mode)
+ run("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", "(without '-abc9' only)");
run("clean");
}
diff --git a/techlibs/xilinx/xc7_brams_bb.v b/techlibs/xilinx/xc7_brams_bb.v
index a43b4b5a1..5b40a457d 100644
--- a/techlibs/xilinx/xc7_brams_bb.v
+++ b/techlibs/xilinx/xc7_brams_bb.v
@@ -1,3 +1,5 @@
+// Max delays from https://github.com/SymbiFlow/prjxray-db/blob/f8e0364116b2983ac72a3dc8c509ea1cc79e2e3d/artix7/timings/BRAM_L.sdf#L138-L147
+
module RAMB18E1 (
(* clkbuf_sink *)
input CLKARDCLK,
@@ -21,9 +23,13 @@ module RAMB18E1 (
input [1:0] WEA,
input [3:0] WEBWE,
+ (* abc_arrival=2454 *)
output [15:0] DOADO,
+ (* abc_arrival=2454 *)
output [15:0] DOBDO,
+ (* abc_arrival=2454 *)
output [1:0] DOPADOP,
+ (* abc_arrival=2454 *)
output [1:0] DOPBDOP
);
parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
@@ -147,9 +153,13 @@ module RAMB36E1 (
input [3:0] WEA,
input [7:0] WEBWE,
+ (* abc_arrival=2454 *)
output [31:0] DOADO,
+ (* abc_arrival=2454 *)
output [31:0] DOBDO,
+ (* abc_arrival=2454 *)
output [3:0] DOPADOP,
+ (* abc_arrival=2454 *)
output [3:0] DOPBDOP
);
parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
diff --git a/tests/various/shregmap.ys b/tests/various/shregmap.ys
index 0e5fe882b..16e5f40e1 100644
--- a/tests/various/shregmap.ys
+++ b/tests/various/shregmap.ys
@@ -31,36 +31,3 @@ sat -verify -prove-asserts -show-ports -seq 5 miter
#design -load gate
#stat
-
-##########
-
-design -load read
-design -copy-to model $__XILINX_SHREG_
-hierarchy -top shregmap_variable_test
-prep
-design -save gold
-
-simplemap t:$dff t:$dffe
-shregmap -tech xilinx
-
-#stat
-# show -width
-# write_verilog -noexpr -norename
-select -assert-count 1 t:$_DFF_P_
-select -assert-count 2 t:$__XILINX_SHREG_
-
-design -stash gate
-
-design -import gold -as gold
-design -import gate -as gate
-design -copy-from model -as $__XILINX_SHREG_ \$__XILINX_SHREG_
-prep
-
-miter -equiv -flatten -make_assert -make_outputs gold gate miter
-sat -verify -prove-asserts -show-ports -seq 5 miter
-
-# design -load gold
-# stat
-
-# design -load gate
-# stat
diff --git a/tests/xilinx/.gitignore b/tests/xilinx/.gitignore
new file mode 100644
index 000000000..b48f808a1
--- /dev/null
+++ b/tests/xilinx/.gitignore
@@ -0,0 +1,3 @@
+/*.log
+/*.out
+/run-test.mk
diff --git a/tests/xilinx/pmgen_xilinx_srl.ys b/tests/xilinx/pmgen_xilinx_srl.ys
new file mode 100644
index 000000000..ea2f20487
--- /dev/null
+++ b/tests/xilinx/pmgen_xilinx_srl.ys
@@ -0,0 +1,57 @@
+read_verilog -icells <<EOT
+module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, output SO);
+ parameter DEPTH = 1;
+ parameter [DEPTH-1:0] INIT = 0;
+ parameter CLKPOL = 1;
+ parameter ENPOL = 2;
+
+ wire pos_clk = C == CLKPOL;
+ reg pos_en;
+ always @(E)
+ if (ENPOL == 2) pos_en = 1'b1;
+ else pos_en = (E == ENPOL[0]);
+
+ reg [DEPTH-1:0] r;
+ always @(posedge pos_clk)
+ if (pos_en)
+ r <= {r[DEPTH-2:0], D};
+
+ assign Q = r[L];
+ assign SO = r[DEPTH-1];
+endmodule
+EOT
+read_verilog +/xilinx/cells_sim.v
+proc
+design -save model
+
+test_pmgen -generate xilinx_srl.fixed
+hierarchy -top pmtest_xilinx_srl_pm_fixed
+flatten; opt_clean
+
+design -save gold
+xilinx_srl -fixed
+techmap -autoproc -map %model
+design -stash gate
+
+design -copy-from gold -as gold pmtest_xilinx_srl_pm_fixed
+design -copy-from gate -as gate pmtest_xilinx_srl_pm_fixed
+dff2dffe -unmap # sat does not support flops-with-enable yet
+miter -equiv -flatten -make_assert gold gate miter
+sat -set-init-zero -seq 5 -verify -prove-asserts miter
+
+design -load model
+
+test_pmgen -generate xilinx_srl.variable
+hierarchy -top pmtest_xilinx_srl_pm_variable
+flatten; opt_clean
+
+design -save gold
+xilinx_srl -variable
+techmap -autoproc -map %model
+design -stash gate
+
+design -copy-from gold -as gold pmtest_xilinx_srl_pm_variable
+design -copy-from gate -as gate pmtest_xilinx_srl_pm_variable
+dff2dffe -unmap # sat does not support flops-with-enable yet
+miter -equiv -flatten -make_assert gold gate miter
+sat -set-init-zero -seq 5 -verify -prove-asserts miter
diff --git a/tests/xilinx/run-test.sh b/tests/xilinx/run-test.sh
new file mode 100755
index 000000000..ea56b70f0
--- /dev/null
+++ b/tests/xilinx/run-test.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+set -e
+{
+echo "all::"
+for x in *.ys; do
+ echo "all:: run-$x"
+ echo "run-$x:"
+ echo " @echo 'Running $x..'"
+ echo " @../../yosys -ql ${x%.ys}.log $x"
+done
+for s in *.sh; do
+ if [ "$s" != "run-test.sh" ]; then
+ echo "all:: run-$s"
+ echo "run-$s:"
+ echo " @echo 'Running $s..'"
+ echo " @bash $s"
+ fi
+done
+} > run-test.mk
+exec ${MAKE:-make} -f run-test.mk
diff --git a/tests/xilinx/xilinx_srl.v b/tests/xilinx/xilinx_srl.v
new file mode 100644
index 000000000..bc2a15ab2
--- /dev/null
+++ b/tests/xilinx/xilinx_srl.v
@@ -0,0 +1,40 @@
+module xilinx_srl_static_test(input i, clk, output [1:0] q);
+reg head = 1'b0;
+reg [3:0] shift1 = 4'b0000;
+reg [3:0] shift2 = 4'b0000;
+
+always @(posedge clk) begin
+ head <= i;
+ shift1 <= {shift1[2:0], head};
+ shift2 <= {shift2[2:0], head};
+end
+
+assign q = {shift2[3], shift1[3]};
+endmodule
+
+module xilinx_srl_variable_test(input i, clk, input [1:0] l1, l2, output [1:0] q);
+reg head = 1'b0;
+reg [3:0] shift1 = 4'b0000;
+reg [3:0] shift2 = 4'b0000;
+
+always @(posedge clk) begin
+ head <= i;
+ shift1 <= {shift1[2:0], head};
+ shift2 <= {shift2[2:0], head};
+end
+
+assign q = {shift2[l2], shift1[l1]};
+endmodule
+
+module $__XILINX_SHREG_(input C, D, E, input [1:0] L, output Q);
+parameter CLKPOL = 1;
+parameter ENPOL = 1;
+parameter DEPTH = 1;
+parameter [DEPTH-1:0] INIT = {DEPTH{1'b0}};
+reg [DEPTH-1:0] r = INIT;
+wire clk = C ^ CLKPOL;
+always @(posedge C)
+ if (E)
+ r <= { r[DEPTH-2:0], D };
+assign Q = r[L];
+endmodule
diff --git a/tests/xilinx/xilinx_srl.ys b/tests/xilinx/xilinx_srl.ys
new file mode 100644
index 000000000..b8df0e55a
--- /dev/null
+++ b/tests/xilinx/xilinx_srl.ys
@@ -0,0 +1,67 @@
+read_verilog xilinx_srl.v
+design -save read
+
+design -copy-to model $__XILINX_SHREG_
+hierarchy -top xilinx_srl_static_test
+prep
+design -save gold
+
+techmap
+xilinx_srl -fixed
+opt
+
+# stat
+# show -width
+select -assert-count 1 t:$_DFF_P_
+select -assert-count 2 t:$__XILINX_SHREG_
+
+design -stash gate
+
+design -import gold -as gold
+design -import gate -as gate
+design -copy-from model -as $__XILINX_SHREG_ \$__XILINX_SHREG_
+prep
+
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+dump gate
+sat -verify -prove-asserts -show-ports -seq 5 miter
+
+#design -load gold
+#stat
+
+#design -load gate
+#stat
+
+##########
+
+design -load read
+design -copy-to model $__XILINX_SHREG_
+hierarchy -top xilinx_srl_variable_test
+prep
+design -save gold
+
+xilinx_srl -variable
+opt
+
+#stat
+# show -width
+# write_verilog -noexpr -norename
+select -assert-count 1 t:$dff
+select -assert-count 1 t:$dff r:WIDTH=1 %i
+select -assert-count 2 t:$__XILINX_SHREG_
+
+design -stash gate
+
+design -import gold -as gold
+design -import gate -as gate
+design -copy-from model -as $__XILINX_SHREG_ \$__XILINX_SHREG_
+prep
+
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports -seq 5 miter
+
+# design -load gold
+# stat
+
+# design -load gate
+# stat