Merge pull request #251 from antmicro/fix-parameter

Fix default signed value for parameters
diff --git a/ql-qlf-plugin/Makefile b/ql-qlf-plugin/Makefile
index 2819055..e9de5bd 100644
--- a/ql-qlf-plugin/Makefile
+++ b/ql-qlf-plugin/Makefile
@@ -11,14 +11,15 @@
           ql-dsp.cc \
           pp3_braminit.cc \
           quicklogic_eqn.cc \
-          ql-edif.cc
+          ql-edif.cc \
+          ql-dsp-simd.cc
 
 include ../Makefile_plugin.common
 
 COMMON          = common
 QLF_K4N8_DIR    = qlf_k4n8
 QLF_K6N10_DIR   = qlf_k6n10
-QLF_K6N10F_DIR   = qlf_k6n10f
+QLF_K6N10F_DIR  = qlf_k6n10f
 PP3_DIR         = pp3
 VERILOG_MODULES = $(COMMON)/cells_sim.v         \
                   $(QLF_K4N8_DIR)/arith_map.v   \
@@ -37,6 +38,7 @@
                   $(QLF_K6N10F_DIR)/cells_sim.v \
                   $(QLF_K6N10F_DIR)/ffs_map.v   \
                   $(QLF_K6N10F_DIR)/dsp_map.v   \
+                  $(QLF_K6N10F_DIR)/dsp_final_map.v \
                   $(PP3_DIR)/abc9_map.v    \
                   $(PP3_DIR)/abc9_model.v  \
                   $(PP3_DIR)/abc9_unmap.v  \
diff --git a/ql-qlf-plugin/ql-dsp-simd.cc b/ql-qlf-plugin/ql-dsp-simd.cc
new file mode 100644
index 0000000..1bd0850
--- /dev/null
+++ b/ql-qlf-plugin/ql-dsp-simd.cc
@@ -0,0 +1,275 @@
+// Copyright (C) 2020-2022  The SymbiFlow Authors.
+//
+// Use of this source code is governed by a ISC-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/ISC
+//
+// SPDX-License-Identifier:ISC
+
+#include "kernel/log.h"
+#include "kernel/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+// ============================================================================
+
+struct QlDspSimdPass : public Pass {
+
+    QlDspSimdPass() : Pass("ql_dsp_simd", "Infers QuickLogic k6n10f DSP pairs that can operate in SIMD mode") {}
+
+    void help() override
+    {
+        log("\n");
+        log("    ql_dsp_simd [selection]\n");
+        log("\n");
+        log("    This pass identifies k6n10f DSP cells with identical configuration\n");
+        log("    and packs pairs of them together into other DSP cells that can\n");
+        log("    perform SIMD operation.\n");
+    }
+
+    // ..........................................
+
+    /// Describes DSP config unique to a whole DSP cell
+    struct DspConfig {
+
+        // Port connections
+        dict<RTLIL::IdString, RTLIL::SigSpec> connections;
+
+        // TODO: Possibly include parameters here. For now we have just
+        // connections.
+
+        DspConfig() = default;
+
+        DspConfig(const DspConfig &ref) = default;
+        DspConfig(DspConfig &&ref) = default;
+
+        unsigned int hash() const { return connections.hash(); }
+
+        bool operator==(const DspConfig &ref) const { return connections == ref.connections; }
+    };
+
+    // ..........................................
+
+    // DSP control and config ports to consider and how to map them to ports
+    // of the target DSP cell
+    const std::vector<std::pair<std::string, std::string>> m_DspCfgPorts = {std::make_pair("clock_i", "clk"),
+                                                                            std::make_pair("reset_i", "reset"),
+
+                                                                            std::make_pair("feedback_i", "feedback"),
+                                                                            std::make_pair("load_acc_i", "load_acc"),
+                                                                            std::make_pair("unsigned_a_i", "unsigned_a"),
+                                                                            std::make_pair("unsigned_b_i", "unsigned_b"),
+
+                                                                            std::make_pair("output_select_i", "output_select"),
+                                                                            std::make_pair("saturate_enable_i", "saturate_enable"),
+                                                                            std::make_pair("shift_right_i", "shift_right"),
+                                                                            std::make_pair("round_i", "round"),
+                                                                            std::make_pair("subtract_i", "subtract"),
+                                                                            std::make_pair("register_inputs_i", "register_inputs")};
+
+    // DSP data ports and how to map them to ports of the target DSP cell
+    const std::vector<std::pair<std::string, std::string>> m_DspDataPorts = {
+      std::make_pair("a_i", "a"), std::make_pair("b_i", "b"),         std::make_pair("acc_fir_i", "acc_fir"),
+      std::make_pair("z_o", "z"), std::make_pair("dly_b_o", "dly_b"),
+    };
+
+    // Source DSP cell type (SISD)
+    const RTLIL::IdString m_SisdDspType = RTLIL::escape_id("dsp_t1_10x9x32");
+    // Target DSP cell type for the SIMD mode
+    const RTLIL::IdString m_SimdDspType = RTLIL::escape_id("QL_DSP2");
+
+    /// Temporary SigBit to SigBit helper map.
+    SigMap m_SigMap;
+
+    // ..........................................
+
+    void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override
+    {
+        log_header(a_Design, "Executing QL_DSP_SIMD pass.\n");
+
+        // Parse args
+        extra_args(a_Args, 1, a_Design);
+
+        // Process modules
+        for (auto module : a_Design->selected_modules()) {
+
+            // Setup the SigMap
+            m_SigMap.clear();
+            m_SigMap.set(module);
+
+            // Assemble DSP cell groups
+            dict<DspConfig, std::vector<RTLIL::Cell *>> groups;
+            for (auto cell : module->selected_cells()) {
+
+                // Check if this is a DSP cell
+                if (cell->type != m_SisdDspType) {
+                    continue;
+                }
+
+                // Skip if it has the (* keep *) attribute set
+                if (cell->has_keep_attr()) {
+                    continue;
+                }
+
+                // Add to a group
+                const auto key = getDspConfig(cell);
+                groups[key].push_back(cell);
+            }
+
+            std::vector<const RTLIL::Cell *> cellsToRemove;
+
+            // Map cell pairs to the target DSP SIMD cell
+            for (const auto &it : groups) {
+                const auto &group = it.second;
+                const auto &config = it.first;
+
+                // Ensure an even number
+                size_t count = group.size();
+                if (count & 1)
+                    count--;
+
+                // Map SIMD pairs
+                for (size_t i = 0; i < count; i += 2) {
+                    const RTLIL::Cell *dsp_a = group[i];
+                    const RTLIL::Cell *dsp_b = group[i + 1];
+
+                    std::string name = stringf("simd_%s_%s", RTLIL::unescape_id(dsp_a->name).c_str(), RTLIL::unescape_id(dsp_b->name).c_str());
+
+                    log(" SIMD: %s (%s) + %s (%s) => %s (%s)\n", RTLIL::unescape_id(dsp_a->name).c_str(), RTLIL::unescape_id(dsp_a->type).c_str(),
+                        RTLIL::unescape_id(dsp_b->name).c_str(), RTLIL::unescape_id(dsp_b->type).c_str(), RTLIL::unescape_id(name).c_str(),
+                        RTLIL::unescape_id(m_SimdDspType).c_str());
+
+                    // Create the new cell
+                    RTLIL::Cell *simd = module->addCell(RTLIL::escape_id(name), m_SimdDspType);
+
+                    // Check if the target cell is known (important to know
+                    // its port widths)
+                    if (!simd->known()) {
+                        log_error(" The target cell type '%s' is not known!", RTLIL::unescape_id(m_SimdDspType).c_str());
+                    }
+
+                    // Connect common ports
+                    for (const auto &it : m_DspCfgPorts) {
+                        auto sport = RTLIL::escape_id(it.first);
+                        auto dport = RTLIL::escape_id(it.second);
+
+                        simd->setPort(dport, config.connections.at(sport));
+                    }
+
+                    // Connect data ports
+                    for (const auto &it : m_DspDataPorts) {
+                        auto sport = RTLIL::escape_id(it.first);
+                        auto dport = RTLIL::escape_id(it.second);
+
+                        size_t width;
+                        bool isOutput;
+
+                        std::tie(width, isOutput) = getPortInfo(simd, dport);
+
+                        auto getConnection = [&](const RTLIL::Cell *cell) {
+                            RTLIL::SigSpec sigspec;
+                            if (cell->hasPort(sport)) {
+                                const auto &sig = cell->getPort(sport);
+                                sigspec.append(sig);
+                            }
+                            if (sigspec.bits().size() < width / 2) {
+                                if (isOutput) {
+                                    for (size_t i = 0; i < width / 2 - sigspec.bits().size(); ++i) {
+                                        sigspec.append(RTLIL::SigSpec());
+                                    }
+                                } else {
+                                    sigspec.append(RTLIL::SigSpec(RTLIL::Sx, width / 2 - sigspec.bits().size()));
+                                }
+                            }
+                            return sigspec;
+                        };
+
+                        RTLIL::SigSpec sigspec;
+                        sigspec.append(getConnection(dsp_a));
+                        sigspec.append(getConnection(dsp_b));
+                        simd->setPort(dport, sigspec);
+                    }
+
+                    // Enable the fractured mode by connecting the control
+                    // port.
+                    simd->setPort(RTLIL::escape_id("f_mode"), RTLIL::S1);
+
+                    // Mark DSP parts for removal
+                    cellsToRemove.push_back(dsp_a);
+                    cellsToRemove.push_back(dsp_b);
+                }
+            }
+
+            // Remove old cells
+            for (const auto &cell : cellsToRemove) {
+                module->remove(const_cast<RTLIL::Cell *>(cell));
+            }
+        }
+
+        // Clear
+        m_SigMap.clear();
+    }
+
+    // ..........................................
+
+    /// Looks up port width and direction in the cell definition and returns it.
+    /// Returns (0, false) if it cannot be determined.
+    std::pair<size_t, bool> getPortInfo(RTLIL::Cell *a_Cell, RTLIL::IdString a_Port)
+    {
+        if (!a_Cell->known()) {
+            return std::make_pair(0, false);
+        }
+
+        // Get the module defining the cell (the previous condition ensures
+        // that the pointers are valid)
+        RTLIL::Module *mod = a_Cell->module->design->module(a_Cell->type);
+        if (mod == nullptr) {
+            return std::make_pair(0, false);
+        }
+
+        // Get the wire representing the port
+        RTLIL::Wire *wire = mod->wire(a_Port);
+        if (wire == nullptr) {
+            return std::make_pair(0, false);
+        }
+
+        return std::make_pair(wire->width, wire->port_output);
+    }
+
+    /// Given a DSP cell populates and returns a DspConfig struct for it.
+    DspConfig getDspConfig(RTLIL::Cell *a_Cell)
+    {
+        DspConfig config;
+
+        for (const auto &it : m_DspCfgPorts) {
+            auto port = RTLIL::escape_id(it.first);
+
+            // Port unconnected
+            if (!a_Cell->hasPort(port)) {
+                config.connections[port] = RTLIL::SigSpec(RTLIL::Sx);
+                continue;
+            }
+
+            // Get the port connection and map it to unique SigBits
+            const auto &orgSigSpec = a_Cell->getPort(port);
+            const auto &orgSigBits = orgSigSpec.bits();
+
+            RTLIL::SigSpec newSigSpec;
+            for (size_t i = 0; i < orgSigBits.size(); ++i) {
+                auto newSigBit = m_SigMap(orgSigBits[i]);
+                newSigSpec.append(newSigBit);
+            }
+
+            // Store
+            config.connections[port] = newSigSpec;
+        }
+
+        return config;
+    }
+
+} QlDspSimdPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/ql-qlf-plugin/qlf_k6n10f/arith_map.v b/ql-qlf-plugin/qlf_k6n10f/arith_map.v
index c2323d6..25c6901 100644
--- a/ql-qlf-plugin/qlf_k6n10f/arith_map.v
+++ b/ql-qlf-plugin/qlf_k6n10f/arith_map.v
@@ -26,6 +26,7 @@
 	(* force_downto *)
 	output [Y_WIDTH-1:0] CO;
 
+
 	wire _TECHMAP_FAIL_ = Y_WIDTH <= 2;
 
 	(* force_downto *)
@@ -45,6 +46,8 @@
 	wire [Y_WIDTH:0] C;
 	(* force_downto *)
 	wire [Y_WIDTH-1:0] S  = {AA ^ BB};
+	
+	assign CO[Y_WIDTH-1:0] = C[Y_WIDTH:1];
 
 	generate
 	     adder_carry intermediate_adder (
diff --git a/ql-qlf-plugin/qlf_k6n10f/cells_sim.v b/ql-qlf-plugin/qlf_k6n10f/cells_sim.v
index b8c5b38..43b3ba2 100644
--- a/ql-qlf-plugin/qlf_k6n10f/cells_sim.v
+++ b/ql-qlf-plugin/qlf_k6n10f/cells_sim.v
@@ -109,7 +109,7 @@
     assign lut4_out[2] = s4[2];
     assign lut4_out[3] = s4[3];
 
-    assign lut5_out[0] = s0[0];
+    assign lut5_out[0] = s5[0];
     assign lut5_out[1] = s5[1];
 
     assign lut6_out = li[5] ? s5[0] : s5[1];
@@ -228,7 +228,7 @@
     (* invertible_pin = "IS_C_INVERTED" *)
     input C,
     input S,
-    input E,
+    input E
 );
     parameter [0:0] INIT = 1'b0;
     parameter [0:0] IS_C_INVERTED = 1'b0;
@@ -749,7 +749,7 @@
 module dsp_t1_10x9x32 (
     input  [ 9:0] a_i,
     input  [ 8:0] b_i,
-    input  [ 3:0] acc_fir_i,
+    input  [ 1:0] acc_fir_i,
     output [18:0] z_o,
     output [ 8:0] dly_b_o,
 
diff --git a/ql-qlf-plugin/qlf_k6n10f/dsp_final_map.v b/ql-qlf-plugin/qlf_k6n10f/dsp_final_map.v
new file mode 100644
index 0000000..5fe905c
--- /dev/null
+++ b/ql-qlf-plugin/qlf_k6n10f/dsp_final_map.v
@@ -0,0 +1,129 @@
+// Copyright (C) 2020-2021  The SymbiFlow Authors.
+//
+// Use of this source code is governed by a ISC-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/ISC
+//
+// SPDX-License-Identifier:ISC
+
+module dsp_t1_20x18x64 (
+    input  [19:0] a_i,
+    input  [17:0] b_i,
+    input  [ 3:0] acc_fir_i,
+    output [37:0] z_o,
+    output [17:0] dly_b_o,
+
+    input         clock_i,
+    input         reset_i,
+
+    input  [2:0]  feedback_i,
+    input         load_acc_i,
+    input         unsigned_a_i,
+    input         unsigned_b_i,
+
+    input  [2:0]  output_select_i,
+    input         saturate_enable_i,
+    input  [5:0]  shift_right_i,
+    input         round_i,
+    input         subtract_i,
+    input         register_inputs_i,
+    input  [19:0] coeff_0_i,
+    input  [19:0] coeff_1_i,
+    input  [19:0] coeff_2_i,
+    input  [19:0] coeff_3_i
+);
+
+    QL_DSP2 _TECHMAP_REPLACE_ (
+        .a                  (a_i),
+        .b                  (b_i),
+        .acc_fir            (acc_fir_i),
+        .z                  (z_o),
+        .dly_b              (dly_b_o),
+
+        .clk                (clk_i),
+        .reset              (reset_i),
+
+        .feedback           (feedback_i),
+        .load_acc           (load_acc_i),
+        .unsigned_a         (unsigned_a_i),
+        .unsigned_b         (unsigned_b_i),
+
+        .f_mode             (1'b0), // No fracturation
+        .output_select      (output_select_i),
+        .saturate_enable    (saturate_enable_i),
+        .shift_right        (shift_right_i),
+        .round              (round_i),
+        .subtract           (subtract_i),
+        .register_inputs    (register_inputs_i),
+        .coeff_0            (coeff_0_i),
+        .coeff_1            (coeff_1_i),
+        .coeff_2            (coeff_2_i),
+        .coeff_3            (coeff_3_i)
+    );
+
+endmodule
+
+module dsp_t1_10x9x32 (
+    input  [ 9:0] a_i,
+    input  [ 8:0] b_i,
+    input  [ 1:0] acc_fir_i,
+    output [18:0] z_o,
+    output [ 8:0] dly_b_o,
+
+    (* clkbuf_sink *)
+    input         clock_i,
+    input         reset_i,
+
+    input  [2:0]  feedback_i,
+    input         load_acc_i,
+    input         unsigned_a_i,
+    input         unsigned_b_i,
+
+    input  [2:0]  output_select_i,
+    input         saturate_enable_i,
+    input  [5:0]  shift_right_i,
+    input         round_i,
+    input         subtract_i,
+    input         register_inputs_i,
+    input  [ 9:0] coeff_0_i,
+    input  [ 9:0] coeff_1_i,
+    input  [ 9:0] coeff_2_i,
+    input  [ 9:0] coeff_3_i
+);
+
+    wire [37:0] z;
+    wire [17:0] dly_b;
+
+    QL_DSP2 _TECHMAP_REPLACE_ (
+        .a                  ({10'd0, a_i}),
+        .b                  ({ 9'd0, b_i}),
+        .acc_fir            (acc_fir_i),
+        .z                  (z),
+        .dly_b              (dly_b),
+
+        .clk                (clk_i),
+        .reset              (reset_i),
+
+        .feedback           (feedback_i),
+        .load_acc           (load_acc_i),
+        .unsigned_a         (unsigned_a_i),
+        .unsigned_b         (unsigned_b_i),
+
+        .f_mode             (1'b1), // Enable fractuation, Use the lower half
+        .output_select      (output_select_i),
+        .saturate_enable    (saturate_enable_i),
+        .shift_right        (shift_right_i),
+        .round              (round_i),
+        .subtract           (subtract_i),
+        .register_inputs    (register_inputs_i),
+        .coeff_0            ({10'd0, coeff_0_i}),
+        .coeff_1            ({10'd0, coeff_1_i}),
+        .coeff_2            ({10'd0, coeff_2_i}),
+        .coeff_3            ({10'd0, coeff_3_i})
+    );
+
+    assign z_o = z[18:0];
+    assign dly_b_o = dly_b_o[8:0];
+
+endmodule
+
diff --git a/ql-qlf-plugin/qlf_k6n10f/dsp_map.v b/ql-qlf-plugin/qlf_k6n10f/dsp_map.v
index f0c5a8b..054d82a 100644
--- a/ql-qlf-plugin/qlf_k6n10f/dsp_map.v
+++ b/ql-qlf-plugin/qlf_k6n10f/dsp_map.v
@@ -31,7 +31,7 @@
         .acc_fir_i          (4'd0),
         .z_o                (z),
 
-        .feedback_i         (2'd0),
+        .feedback_i         (3'd0),
         .load_acc_i         (1'b0),
         .unsigned_a_i       (!A_SIGNED),
         .unsigned_b_i       (!B_SIGNED),
@@ -74,7 +74,7 @@
     dsp_t1_10x9x32 _TECHMAP_REPLACE_ (
         .a_i                (a),
         .b_i                (b),
-        .acc_fir_i          (4'd0),
+        .acc_fir_i          (3'd0),
         .z_o                (z),
 
         .feedback_i         (2'd0),
diff --git a/ql-qlf-plugin/synth_quicklogic.cc b/ql-qlf-plugin/synth_quicklogic.cc
index 01ce6ef..3bb44c5 100644
--- a/ql-qlf-plugin/synth_quicklogic.cc
+++ b/ql-qlf-plugin/synth_quicklogic.cc
@@ -272,6 +272,8 @@
                     run("techmap -map +/mul2dsp.v [...]", "(for qlf_k6n10f if not -no_dsp)");
                     run("chtype -set $mul t:$__soft_mul", "(for qlf_k6n10f if not -no_dsp)");
                     run("techmap -map +/quicklogic/" + family + "/dsp_map.v", "(for qlf_k6n10f if not -no_dsp)");
+                    run("ql_dsp_simd                   ", "(for qlf_k6n10f if not -no_dsp)");
+                    run("techmap -map +/quicklogic/" + family + "/dsp_final_map.v", "(for qlf_k6n10f if not -no_dsp)");
                 } else if (!nodsp) {
 
                     run("wreduce t:$mul");
@@ -284,6 +286,8 @@
                         run("chtype -set $mul t:$__soft_mul");
                     }
                     run("techmap -map +/quicklogic/" + family + "/dsp_map.v");
+                    run("ql_dsp_simd");
+                    run("techmap -map +/quicklogic/" + family + "/dsp_final_map.v");
                 }
             }
 
diff --git a/ql-qlf-plugin/tests/Makefile b/ql-qlf-plugin/tests/Makefile
index 4fc598c..e54aeaf 100644
--- a/ql-qlf-plugin/tests/Makefile
+++ b/ql-qlf-plugin/tests/Makefile
@@ -21,7 +21,8 @@
 	tribuf \
 	fsm \
 	pp3_bram \
-    qlf_k6n10f/dsp_mult
+    qlf_k6n10f/dsp_mult \
+    qlf_k6n10f/dsp_simd
 #	qlf_k6n10_bram \
 
 include $(shell pwd)/../../Makefile_test.common
@@ -40,4 +41,5 @@
 fsm_verify = true
 pp3_bram_verify = true
 qlf_k6n10f-dsp_mult_verify = true
+qlf_k6n10f-dsp_simd_verify = true
 #qlf_k6n10_bram_verify = true
diff --git a/ql-qlf-plugin/tests/full_adder/full_adder.tcl b/ql-qlf-plugin/tests/full_adder/full_adder.tcl
index 05ced41..41443f4 100644
--- a/ql-qlf-plugin/tests/full_adder/full_adder.tcl
+++ b/ql-qlf-plugin/tests/full_adder/full_adder.tcl
@@ -64,7 +64,8 @@
 read_verilog -icells -DWIDTH=4 $::env(DESIGN_TOP).v
 hierarchy -check -top full_adder
 yosys proc
-synth_quicklogic -family qlf_k6n10f
+equiv_opt -assert  -map +/quicklogic/qlf_k6n10f/cells_sim.v synth_quicklogic -family qlf_k6n10f
+design -load postopt
 yosys cd full_adder
 stat
 select -assert-count 6 t:adder_carry
@@ -75,13 +76,26 @@
 read_verilog -icells -DWIDTH=4 $::env(DESIGN_TOP).v
 hierarchy -check -top subtractor
 yosys proc
-synth_quicklogic -family qlf_k6n10f
+equiv_opt -assert  -map +/quicklogic/qlf_k6n10f/cells_sim.v synth_quicklogic -family qlf_k6n10f
+design -load postopt
 yosys cd subtractor
 stat
 select -assert-count 6 t:adder_carry
 
 design -reset
 
+# Equivalence check for comparator synthesis for qlf-k6n10
+read_verilog -icells -DWIDTH=4 $::env(DESIGN_TOP).v
+hierarchy -check -top comparator
+yosys proc
+equiv_opt -assert  -map +/quicklogic/qlf_k6n10f/cells_sim.v synth_quicklogic -family qlf_k6n10f
+design -load postopt
+yosys cd comparator
+stat
+select -assert-count 5 t:adder_carry
+
+design -reset
+
 # Equivalence check for adder synthesis for pp3
 read_verilog -icells -DWIDTH=4 $::env(DESIGN_TOP).v
 hierarchy -check -top full_adder
diff --git a/ql-qlf-plugin/tests/qlf_k6n10f/dsp_mult/dsp_mult.tcl b/ql-qlf-plugin/tests/qlf_k6n10f/dsp_mult/dsp_mult.tcl
index 90f591d..75acb10 100644
--- a/ql-qlf-plugin/tests/qlf_k6n10f/dsp_mult/dsp_mult.tcl
+++ b/ql-qlf-plugin/tests/qlf_k6n10f/dsp_mult/dsp_mult.tcl
@@ -10,26 +10,26 @@
 hierarchy -top $TOP
 synth_quicklogic -family qlf_k6n10f -top $TOP
 yosys cd $TOP
-select -assert-count 1 t:dsp_t1_20x18x64
+select -assert-count 1 t:QL_DSP2
 
 set TOP "mult_20x18"
 design -load read
 hierarchy -top $TOP
 synth_quicklogic -family qlf_k6n10f -top $TOP
 yosys cd $TOP
-select -assert-count 1 t:dsp_t1_20x18x64
+select -assert-count 1 t:QL_DSP2
 
 set TOP "mult_8x8"
 design -load read
 hierarchy -top $TOP
 synth_quicklogic -family qlf_k6n10f -top $TOP
 yosys cd $TOP
-select -assert-count 1 t:dsp_t1_10x9x32
+select -assert-count 1 t:QL_DSP2
 
 set TOP "mult_10x9"
 design -load read
 hierarchy -top $TOP
 synth_quicklogic -family qlf_k6n10f -top $TOP
 yosys cd $TOP
-select -assert-count 1 t:dsp_t1_10x9x32
+select -assert-count 1 t:QL_DSP2
 
diff --git a/ql-qlf-plugin/tests/qlf_k6n10f/dsp_simd/dsp_simd.tcl b/ql-qlf-plugin/tests/qlf_k6n10f/dsp_simd/dsp_simd.tcl
new file mode 100644
index 0000000..dd06114
--- /dev/null
+++ b/ql-qlf-plugin/tests/qlf_k6n10f/dsp_simd/dsp_simd.tcl
@@ -0,0 +1,43 @@
+yosys -import
+if { [info procs quicklogic_eqn] == {} } { plugin -i ql-qlf}
+yosys -import  ;# ingest plugin commands
+
+read_verilog dsp_simd.v
+design -save read
+
+set TOP "simd_mult"
+design -load read
+hierarchy -top $TOP
+synth_quicklogic -family qlf_k6n10f -top $TOP
+yosys cd $TOP
+select -assert-count 0 t:dsp_t1_20x18x64
+select -assert-count 0 t:dsp_t1_10x9x32
+select -assert-count 1 t:QL_DSP2
+
+set TOP "simd_mult_inferred"
+design -load read
+hierarchy -top $TOP
+synth_quicklogic -family qlf_k6n10f -top $TOP
+yosys cd $TOP
+select -assert-count 0 t:dsp_t1_20x18x64
+select -assert-count 0 t:dsp_t1_10x9x32
+select -assert-count 1 t:QL_DSP2
+
+set TOP "simd_mult_odd"
+design -load read
+hierarchy -top $TOP
+synth_quicklogic -family qlf_k6n10f -top $TOP
+yosys cd $TOP
+select -assert-count 0 t:dsp_t1_20x18x64
+select -assert-count 0 t:dsp_t1_10x9x32
+select -assert-count 2 t:QL_DSP2
+
+set TOP "simd_mult_conflict"
+design -load read
+hierarchy -top $TOP
+synth_quicklogic -family qlf_k6n10f -top $TOP
+yosys cd $TOP
+select -assert-count 0 t:dsp_t1_20x18x64
+select -assert-count 0 t:dsp_t1_10x9x32
+select -assert-count 2 t:QL_DSP2
+
diff --git a/ql-qlf-plugin/tests/qlf_k6n10f/dsp_simd/dsp_simd.v b/ql-qlf-plugin/tests/qlf_k6n10f/dsp_simd/dsp_simd.v
new file mode 100644
index 0000000..b871eb8
--- /dev/null
+++ b/ql-qlf-plugin/tests/qlf_k6n10f/dsp_simd/dsp_simd.v
@@ -0,0 +1,215 @@
+// Copyright (C) 2020-2021  The SymbiFlow Authors.
+//
+// Use of this source code is governed by a ISC-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/ISC
+//
+// SPDX-License-Identifier:ISC
+
+module simd_mult (
+    input  wire         clk,
+                
+    input  wire [ 7:0]  a0,
+    input  wire [ 7:0]  b0,
+    output wire [15:0]  z0,
+                
+    input  wire [ 7:0]  a1,
+    input  wire [ 7:0]  b1,
+    output wire [15:0]  z1
+);
+
+    dsp_t1_10x9x32 dsp_0 (
+        .a_i    (a0),
+        .b_i    (b0),
+        .z_o    (z0),
+
+        .clock_i            (clk),
+
+        .feedback_i         (3'd0),
+        .load_acc_i         (1'b0),
+        .unsigned_a_i       (1'b1),
+        .unsigned_b_i       (1'b1),
+
+        .output_select_i    (3'd0),
+        .saturate_enable_i  (1'b0),
+        .shift_right_i      (6'd0),
+        .round_i            (1'b0),
+        .subtract_i         (1'b0),
+        .register_inputs_i  (1'b1)
+    );    
+
+    dsp_t1_10x9x32 dsp_1 (
+        .a_i    (a1),
+        .b_i    (b1),
+        .z_o    (z1),
+
+        .clock_i            (clk),
+
+        .feedback_i         (3'd0),
+        .load_acc_i         (1'b0),
+        .unsigned_a_i       (1'b1),
+        .unsigned_b_i       (1'b1),
+
+        .output_select_i    (3'd0),
+        .saturate_enable_i  (1'b0),
+        .shift_right_i      (6'd0),
+        .round_i            (1'b0),
+        .subtract_i         (1'b0),
+        .register_inputs_i  (1'b1)
+    );    
+
+endmodule
+
+module simd_mult_inferred (
+    input  wire         clk,
+                
+    input  wire [ 7:0]  a0,
+    input  wire [ 7:0]  b0,
+    output reg  [15:0]  z0,
+                
+    input  wire [ 7:0]  a1,
+    input  wire [ 7:0]  b1,
+    output reg  [15:0]  z1
+);
+
+    always @(posedge clk)
+        z0 <= a0 * b0;
+
+    always @(posedge clk)
+        z1 <= a1 * b1;
+
+endmodule
+
+module simd_mult_odd (
+    input  wire         clk,
+                
+    input  wire [ 7:0]  a0,
+    input  wire [ 7:0]  b0,
+    output wire [15:0]  z0,
+                
+    input  wire [ 7:0]  a1,
+    input  wire [ 7:0]  b1,
+    output wire [15:0]  z1,
+
+    input  wire [ 7:0]  a2,
+    input  wire [ 7:0]  b2,
+    output wire [15:0]  z2
+);
+
+    dsp_t1_10x9x32 dsp_0 (
+        .a_i    (a0),
+        .b_i    (b0),
+        .z_o    (z0),
+
+        .clock_i            (clk),
+
+        .feedback_i         (3'd0),
+        .load_acc_i         (1'b0),
+        .unsigned_a_i       (1'b1),
+        .unsigned_b_i       (1'b1),
+
+        .output_select_i    (3'd0),
+        .saturate_enable_i  (1'b0),
+        .shift_right_i      (6'd0),
+        .round_i            (1'b0),
+        .subtract_i         (1'b0),
+        .register_inputs_i  (1'b1)
+    );    
+
+    dsp_t1_10x9x32 dsp_1 (
+        .a_i    (a1),
+        .b_i    (b1),
+        .z_o    (z1),
+
+        .clock_i            (clk),
+
+        .feedback_i         (3'd0),
+        .load_acc_i         (1'b0),
+        .unsigned_a_i       (1'b1),
+        .unsigned_b_i       (1'b1),
+
+        .output_select_i    (3'd0),
+        .saturate_enable_i  (1'b0),
+        .shift_right_i      (6'd0),
+        .round_i            (1'b0),
+        .subtract_i         (1'b0),
+        .register_inputs_i  (1'b1)
+    );    
+
+    dsp_t1_10x9x32 dsp_2 (
+        .a_i    (a2),
+        .b_i    (b2),
+        .z_o    (z2),
+
+        .clock_i            (clk),
+
+        .feedback_i         (3'd0),
+        .load_acc_i         (1'b0),
+        .unsigned_a_i       (1'b1),
+        .unsigned_b_i       (1'b1),
+
+        .output_select_i    (3'd0),
+        .saturate_enable_i  (1'b0),
+        .shift_right_i      (6'd0),
+        .round_i            (1'b0),
+        .subtract_i         (1'b0),
+        .register_inputs_i  (1'b1)
+    );    
+
+endmodule
+
+module simd_mult_conflict (
+    input  wire         clk0,
+    input  wire         clk1,
+                
+    input  wire [ 7:0]  a0,
+    input  wire [ 7:0]  b0,
+    output wire [15:0]  z0,
+                
+    input  wire [ 7:0]  a1,
+    input  wire [ 7:0]  b1,
+    output wire [15:0]  z1
+);
+
+    dsp_t1_10x9x32 dsp_0 (
+        .a_i    (a0),
+        .b_i    (b0),
+        .z_o    (z0),
+
+        .clock_i            (clk0),
+
+        .feedback_i         (3'd0),
+        .load_acc_i         (1'b0),
+        .unsigned_a_i       (1'b1),
+        .unsigned_b_i       (1'b1),
+
+        .output_select_i    (3'd0),
+        .saturate_enable_i  (1'b0),
+        .shift_right_i      (6'd0),
+        .round_i            (1'b0),
+        .subtract_i         (1'b0),
+        .register_inputs_i  (1'b1)
+    );    
+
+    dsp_t1_10x9x32 dsp_1 (
+        .a_i    (a1),
+        .b_i    (b1),
+        .z_o    (z1),
+
+        .clock_i            (clk1),
+
+        .feedback_i         (3'd0),
+        .load_acc_i         (1'b0),
+        .unsigned_a_i       (1'b1),
+        .unsigned_b_i       (1'b1),
+
+        .output_select_i    (3'd0),
+        .saturate_enable_i  (1'b0),
+        .shift_right_i      (6'd0),
+        .round_i            (1'b0),
+        .subtract_i         (1'b0),
+        .register_inputs_i  (1'b1)
+    );    
+
+endmodule
+