Merge pull request #259 from antmicro/fix-typedef
Remove move_type_to_new_typedef from process_*_typespec
diff --git a/Makefile b/Makefile
index 00e0263..1279f83 100644
--- a/Makefile
+++ b/Makefile
@@ -36,6 +36,9 @@
$(foreach plugin,$(PLUGIN_LIST),$(eval $(call install_plugin,$(plugin))))
+pmgen.py:
+ wget -nc -O $@ https://raw.githubusercontent.com/YosysHQ/yosys/master/passes/pmgen/pmgen.py
+
plugins: $(PLUGINS)
install: $(PLUGINS_INSTALL)
@@ -45,6 +48,7 @@
plugins_clean: $(PLUGINS_CLEAN)
clean:: plugins_clean
+ rm -rf pmgen.py
CLANG_FORMAT ?= clang-format-8
format:
diff --git a/Makefile_plugin.common b/Makefile_plugin.common
index 49d6ab5..6fc4492 100644
--- a/Makefile_plugin.common
+++ b/Makefile_plugin.common
@@ -56,15 +56,19 @@
EXTRA_FLAGS ?=
OBJS := $(patsubst %.cc,%.o,$(SOURCES))
+DEPS ?=
all: $(NAME).so
-$(OBJS): %.o: %.cc
- $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(EXTRA_FLAGS) -c -o $@ $^
+$(OBJS): %.o: %.cc $(DEPS)
+ $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(EXTRA_FLAGS) -c -o $@ $(filter %.cc, $^)
$(NAME).so: $(OBJS)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
+../pmgen.py:
+ @$(MAKE) -C .. pmgen.py
+
install_plugin: $(NAME).so
install -D $< $(PLUGINS_DIR)/$<
diff --git a/ql-qlf-plugin/Makefile b/ql-qlf-plugin/Makefile
index e9de5bd..00a7c4b 100644
--- a/ql-qlf-plugin/Makefile
+++ b/ql-qlf-plugin/Makefile
@@ -12,7 +12,11 @@
pp3_braminit.cc \
quicklogic_eqn.cc \
ql-edif.cc \
- ql-dsp-simd.cc
+ ql-dsp-simd.cc \
+ ql-dsp-macc.cc
+
+DEPS = pmgen/ql-dsp-pm.h \
+ pmgen/ql-dsp-macc.h
include ../Makefile_plugin.common
@@ -57,9 +61,14 @@
$(PP3_DIR)/mult_sim.v \
$(PP3_DIR)/qlal3_sim.v \
-retrieve-pmgen:=$(shell mkdir -p pmgen && wget -nc -O pmgen/pmgen.py https://raw.githubusercontent.com/SymbiFlow/yosys/master%2Bwip/passes/pmgen/pmgen.py)
+pmgen:
+ mkdir -p pmgen
-pre-build:=$(shell python3 pmgen/pmgen.py -o pmgen/ql-dsp-pm.h -p ql_dsp ql_dsp.pmg)
+pmgen/ql-dsp-pm.h: ../pmgen.py ql_dsp.pmg | pmgen
+ python3 ../pmgen.py -o $@ -p ql_dsp ql_dsp.pmg
+
+pmgen/ql-dsp-macc.h: ../pmgen.py ql-dsp-macc.pmg | pmgen
+ python3 ../pmgen.py -o $@ -p ql_dsp_macc ql-dsp-macc.pmg
install_modules: $(VERILOG_MODULES)
$(foreach f,$^,install -D $(f) $(DATA_DIR)/quicklogic/$(f);)
@@ -68,4 +77,4 @@
clean:
$(MAKE) -f ../Makefile_plugin.common $@
- rm -f *pm.h
+ rm -rf pmgen
diff --git a/ql-qlf-plugin/ql-dsp-macc.cc b/ql-qlf-plugin/ql-dsp-macc.cc
new file mode 100644
index 0000000..c422d31
--- /dev/null
+++ b/ql-qlf-plugin/ql-dsp-macc.cc
@@ -0,0 +1,237 @@
+#include "kernel/sigtools.h"
+#include "kernel/yosys.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+#include "pmgen/ql-dsp-macc.h"
+
+// ============================================================================
+
+void create_ql_macc_dsp(ql_dsp_macc_pm &pm)
+{
+ auto &st = pm.st_ql_dsp_macc;
+
+ // Reject if multiplier drives anything else than either $add or $add and
+ // $mux
+ if (st.mux == nullptr && st.mul_nusers > 2) {
+ return;
+ }
+
+ // Determine whether the output is taken from before or after the ff
+ bool out_ff;
+ if (st.ff_d_nusers == 2 && st.ff_q_nusers == 3) {
+ out_ff = true;
+ } else if (st.ff_d_nusers == 3 && st.ff_q_nusers == 2) {
+ out_ff = false;
+ } else {
+ // Illegal, cannot take the two outputs simulataneously
+ return;
+ }
+
+ // No mux, the adder can driver either the ff or the ff + output
+ if (st.mux == nullptr) {
+ if (out_ff && st.add_nusers != 2) {
+ return;
+ }
+ if (!out_ff && st.add_nusers != 3) {
+ return;
+ }
+ }
+ // Mux present, the adder cannot drive anything else
+ else {
+ if (st.add_nusers != 2) {
+ return;
+ }
+ }
+
+ // Mux can driver either the ff or the ff + output
+ if (st.mux != nullptr) {
+ if (out_ff && st.mux_nusers != 2) {
+ return;
+ }
+ if (!out_ff && st.mux_nusers != 3) {
+ return;
+ }
+ }
+
+ // Get port widths
+ size_t a_width = GetSize(st.mul->getPort(ID(A)));
+ size_t b_width = GetSize(st.mul->getPort(ID(B)));
+ size_t z_width = GetSize(st.ff->getPort(ID(Q)));
+
+ size_t min_width = std::min(a_width, b_width);
+ size_t max_width = std::max(a_width, b_width);
+
+ // Signed / unsigned
+ bool a_signed = st.mul->getParam(ID(A_SIGNED)).as_bool();
+ bool b_signed = st.mul->getParam(ID(B_SIGNED)).as_bool();
+
+ // Determine DSP type or discard if too narrow / wide
+ RTLIL::IdString type;
+ size_t tgt_a_width;
+ size_t tgt_b_width;
+ size_t tgt_z_width;
+
+ if (min_width <= 2 && max_width <= 2 && z_width <= 4) {
+ // Too narrow
+ return;
+ } else if (min_width <= 9 && max_width <= 10 && z_width <= 19) {
+ type = RTLIL::escape_id("dsp_t1_10x9x32");
+ tgt_a_width = 10;
+ tgt_b_width = 9;
+ tgt_z_width = 19;
+ } else if (min_width <= 18 && max_width <= 20 && z_width <= 38) {
+ type = RTLIL::escape_id("dsp_t1_20x18x64");
+ tgt_a_width = 20;
+ tgt_b_width = 18;
+ tgt_z_width = 38;
+ } else {
+ // Too wide
+ return;
+ }
+
+ log("Inferring MACC %zux%zu->%zu as %s from:\n", a_width, b_width, z_width, RTLIL::unescape_id(type).c_str());
+
+ for (auto cell : {st.mul, st.add, st.mux, st.ff}) {
+ if (cell != nullptr) {
+ log(" %s (%s)\n", RTLIL::unescape_id(cell->name).c_str(), RTLIL::unescape_id(cell->type).c_str());
+ }
+ }
+
+ // Build the DSP cell name
+ std::string name;
+ name += RTLIL::unescape_id(st.mul->name) + "_";
+ name += RTLIL::unescape_id(st.add->name) + "_";
+ if (st.mux != nullptr) {
+ name += RTLIL::unescape_id(st.mux->name) + "_";
+ }
+ name += RTLIL::unescape_id(st.ff->name);
+
+ // Add the DSP cell
+ RTLIL::Cell *cell = pm.module->addCell(RTLIL::escape_id(name), type);
+
+ // Get input/output data signals
+ RTLIL::SigSpec sig_a;
+ RTLIL::SigSpec sig_b;
+ RTLIL::SigSpec sig_z;
+
+ if (a_width >= b_width) {
+ sig_a = st.mul->getPort(ID(A));
+ sig_b = st.mul->getPort(ID(B));
+ } else {
+ sig_a = st.mul->getPort(ID(B));
+ sig_b = st.mul->getPort(ID(A));
+ }
+
+ sig_z = out_ff ? st.ff->getPort(ID(Q)) : st.ff->getPort(ID(D));
+
+ // Connect input data ports, sign extend / pad with zeros
+ sig_a.extend_u0(tgt_a_width, a_signed);
+ sig_b.extend_u0(tgt_b_width, b_signed);
+ cell->setPort(RTLIL::escape_id("a_i"), sig_a);
+ cell->setPort(RTLIL::escape_id("b_i"), sig_b);
+
+ // Connect output data port, pad if needed
+ if ((size_t)GetSize(sig_z) < tgt_z_width) {
+ auto *wire = pm.module->addWire(NEW_ID, tgt_z_width - GetSize(sig_z));
+ sig_z.append(wire);
+ }
+ cell->setPort(RTLIL::escape_id("z_o"), sig_z);
+
+ // Connect clock, reset and enable
+ cell->setPort(RTLIL::escape_id("clock_i"), st.ff->getPort(ID(CLK)));
+
+ RTLIL::SigSpec rst;
+ RTLIL::SigSpec ena;
+
+ if (st.ff->hasPort(ID(ARST))) {
+ rst = st.ff->getPort(ID(ARST));
+ } else {
+ rst = RTLIL::SigSpec(RTLIL::S0);
+ }
+
+ if (st.ff->hasPort(ID(EN))) {
+ ena = st.ff->getPort(ID(EN));
+ } else {
+ ena = RTLIL::SigSpec(RTLIL::S1);
+ }
+
+ cell->setPort(RTLIL::escape_id("reset_i"), rst);
+ cell->setPort(RTLIL::escape_id("load_acc_i"), ena);
+
+ // Insert feedback_i control logic used for clearing / loading the accumulator
+ if (st.mux != nullptr) {
+ RTLIL::SigSpec sig_s = st.mux->getPort(ID(S));
+
+ // Depending on the mux port ordering insert inverter if needed
+ log_assert(st.mux_ab == ID(A) || st.mux_ab == ID(B));
+ if (st.mux_ab == ID(B)) {
+ sig_s = pm.module->Not(NEW_ID, sig_s);
+ }
+
+ // Assemble the full control signal for the feedback_i port
+ RTLIL::SigSpec sig_f;
+ sig_f.append(RTLIL::S0);
+ sig_f.append(sig_s);
+ cell->setPort(RTLIL::escape_id("feedback_i"), sig_f);
+ }
+ // No acc clear/load
+ else {
+ cell->setPort(RTLIL::escape_id("feedback_i"), RTLIL::SigSpec(RTLIL::S0, 2));
+ }
+
+ // Connect control ports
+ cell->setPort(RTLIL::escape_id("unsigned_a_i"), RTLIL::SigSpec(a_signed ? RTLIL::S0 : RTLIL::S1));
+ cell->setPort(RTLIL::escape_id("unsigned_b_i"), RTLIL::SigSpec(b_signed ? RTLIL::S0 : RTLIL::S1));
+
+ // Connect config ports
+ cell->setPort(RTLIL::escape_id("saturate_enable_i"), RTLIL::SigSpec(RTLIL::S0));
+ cell->setPort(RTLIL::escape_id("shift_right_i"), RTLIL::SigSpec(RTLIL::S0, 6));
+ cell->setPort(RTLIL::escape_id("round_i"), RTLIL::SigSpec(RTLIL::S0));
+ cell->setPort(RTLIL::escape_id("register_inputs_i"), RTLIL::SigSpec(RTLIL::S0));
+
+ bool subtract = (st.add->type == RTLIL::escape_id("$sub"));
+ cell->setPort(RTLIL::escape_id("subtract_i"), RTLIL::SigSpec(subtract ? RTLIL::S1 : RTLIL::S0));
+
+ // 3 - output post acc
+ // 1 - output pre acc
+ cell->setPort(RTLIL::escape_id("output_select_i"), out_ff ? RTLIL::Const(3, 3) : RTLIL::Const(1, 3));
+
+ // Mark the cells for removal
+ pm.autoremove(st.mul);
+ pm.autoremove(st.add);
+ if (st.mux != nullptr) {
+ pm.autoremove(st.mux);
+ }
+ pm.autoremove(st.ff);
+}
+
+struct QlDspMacc : public Pass {
+
+ QlDspMacc() : Pass("ql_dsp_macc", "Does something") {}
+
+ void help() override
+ {
+ log("\n");
+ log(" ql_dsp_macc [options] [selection]\n");
+ log("\n");
+ }
+
+ void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override
+ {
+ log_header(a_Design, "Executing QL_DSP_MACC pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < a_Args.size(); argidx++) {
+ break;
+ }
+ extra_args(a_Args, argidx, a_Design);
+
+ for (auto module : a_Design->selected_modules()) {
+ ql_dsp_macc_pm(module, module->selected_cells()).run_ql_dsp_macc(create_ql_macc_dsp);
+ }
+ }
+} QlDspMacc;
+
+PRIVATE_NAMESPACE_END
diff --git a/ql-qlf-plugin/ql-dsp-macc.pmg b/ql-qlf-plugin/ql-dsp-macc.pmg
new file mode 100644
index 0000000..4cfd15a
--- /dev/null
+++ b/ql-qlf-plugin/ql-dsp-macc.pmg
@@ -0,0 +1,50 @@
+pattern ql_dsp_macc
+
+state <IdString> add_ba
+state <IdString> mux_ab
+
+state <int> mul_nusers
+state <int> add_nusers
+state <int> mux_nusers
+state <int> ff_d_nusers
+state <int> ff_q_nusers
+
+match mul
+ select mul->type.in($mul)
+ select nusers(port(mul, \Y)) <= 3
+ set mul_nusers nusers(port(mul, \Y))
+endmatch
+
+match add
+ select add->type.in($add, $sub)
+ choice <IdString> AB {\A, \B}
+ define <IdString> BA (AB == \A ? \B : \A)
+ index <SigSpec> port(add, AB) === port(mul, \Y)
+ select nusers(port(add, \Y)) <= 3
+ set add_nusers nusers(port(add, \Y))
+ set add_ba BA
+endmatch
+
+match mux
+ select mux->type.in($mux)
+ choice <IdString> AB {\A, \B}
+ define <IdString> BA (AB == \A ? \B : \A)
+ index <SigSpec> port(mux, AB) === port(mul, \Y)
+ index <SigSpec> port(mux, BA) === port(add, \Y)
+ select nusers(port(mux, \Y)) <= 3
+ set mux_nusers nusers(port(mux, \Y))
+ set mux_ab AB
+ optional
+endmatch
+
+match ff
+ select ff->type.in($dff, $adff, $dffe, $adffe)
+ index <SigSpec> port(ff, \D) === (mux == nullptr ? port(add, \Y) : port(mux, \Y))
+ index <SigSpec> port(ff, \Q) === port(add, add_ba)
+ set ff_d_nusers nusers(port(ff, \D))
+ set ff_q_nusers nusers(port(ff, \Q))
+endmatch
+
+code
+ accept;
+endcode
diff --git a/ql-qlf-plugin/ql-dsp-simd.cc b/ql-qlf-plugin/ql-dsp-simd.cc
index 1bd0850..f84c458 100644
--- a/ql-qlf-plugin/ql-dsp-simd.cc
+++ b/ql-qlf-plugin/ql-dsp-simd.cc
@@ -76,6 +76,9 @@
std::make_pair("z_o", "z"), std::make_pair("dly_b_o", "dly_b"),
};
+ // DSP parameters
+ const std::vector<std::string> m_DspParams = {"COEFF_0", "COEFF_1", "COEFF_2", "COEFF_3"};
+
// Source DSP cell type (SISD)
const RTLIL::IdString m_SisdDspType = RTLIL::escape_id("dsp_t1_10x9x32");
// Target DSP cell type for the SIMD mode
@@ -193,6 +196,17 @@
simd->setPort(dport, sigspec);
}
+ // Set parameters
+ for (const auto &it : m_DspParams) {
+ auto val_a = dsp_a->getParam(RTLIL::escape_id(it));
+ auto val_b = dsp_b->getParam(RTLIL::escape_id(it));
+
+ std::vector<RTLIL::State> bits;
+ bits.insert(bits.end(), val_a.begin(), val_a.end());
+ bits.insert(bits.end(), val_b.begin(), val_b.end());
+ simd->setParam(RTLIL::escape_id(it), RTLIL::Const(bits));
+ }
+
// Enable the fractured mode by connecting the control
// port.
simd->setPort(RTLIL::escape_id("f_mode"), RTLIL::S1);
diff --git a/ql-qlf-plugin/qlf_k6n10f/cells_sim.v b/ql-qlf-plugin/qlf_k6n10f/cells_sim.v
index 43b3ba2..296fc81 100644
--- a/ql-qlf-plugin/qlf_k6n10f/cells_sim.v
+++ b/ql-qlf-plugin/qlf_k6n10f/cells_sim.v
@@ -706,13 +706,14 @@
input [5:0] shift_right,
input round,
input subtract,
- input register_inputs,
- input [19:0] coeff_0,
- input [19:0] coeff_1,
- input [19:0] coeff_2,
- input [19:0] coeff_3
+ input register_inputs
);
+ parameter [19:0] COEFF_0 = 20'd0;
+ parameter [19:0] COEFF_1 = 20'd0;
+ parameter [19:0] COEFF_2 = 20'd0;
+ parameter [19:0] COEFF_3 = 20'd0;
+
endmodule
(* blackbox *) // TODO: add sim model
@@ -737,12 +738,14 @@
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
+ input register_inputs_i
);
+
+ parameter [19:0] COEFF_0 = 20'd0;
+ parameter [19:0] COEFF_1 = 20'd0;
+ parameter [19:0] COEFF_2 = 20'd0;
+ parameter [19:0] COEFF_3 = 20'd0;
+
endmodule
(* blackbox *) // TODO: add sim model
@@ -767,10 +770,12 @@
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
+ input register_inputs_i
);
+
+ parameter [9:0] COEFF_0 = 10'd0;
+ parameter [9:0] COEFF_1 = 10'd0;
+ parameter [9:0] COEFF_2 = 10'd0;
+ parameter [9:0] COEFF_3 = 10'd0;
+
endmodule
diff --git a/ql-qlf-plugin/qlf_k6n10f/dsp_final_map.v b/ql-qlf-plugin/qlf_k6n10f/dsp_final_map.v
index 5fe905c..b8423e5 100644
--- a/ql-qlf-plugin/qlf_k6n10f/dsp_final_map.v
+++ b/ql-qlf-plugin/qlf_k6n10f/dsp_final_map.v
@@ -26,14 +26,20 @@
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
+ input register_inputs_i
);
- QL_DSP2 _TECHMAP_REPLACE_ (
+ parameter [19:0] COEFF_0 = 20'd0;
+ parameter [19:0] COEFF_1 = 20'd0;
+ parameter [19:0] COEFF_2 = 20'd0;
+ parameter [19:0] COEFF_3 = 20'd0;
+
+ QL_DSP2 # (
+ .COEFF_0 (COEFF_0),
+ .COEFF_1 (COEFF_1),
+ .COEFF_2 (COEFF_2),
+ .COEFF_3 (COEFF_3)
+ ) _TECHMAP_REPLACE_ (
.a (a_i),
.b (b_i),
.acc_fir (acc_fir_i),
@@ -54,11 +60,7 @@
.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)
+ .register_inputs (register_inputs_i)
);
endmodule
@@ -91,10 +93,20 @@
input [ 9:0] coeff_3_i
);
+ parameter [9:0] COEFF_0 = 10'd0;
+ parameter [9:0] COEFF_1 = 10'd0;
+ parameter [9:0] COEFF_2 = 10'd0;
+ parameter [9:0] COEFF_3 = 10'd0;
+
wire [37:0] z;
wire [17:0] dly_b;
- QL_DSP2 _TECHMAP_REPLACE_ (
+ QL_DSP2 # (
+ .COEFF_0 ({10'd0, COEFF_0}),
+ .COEFF_1 ({10'd0, COEFF_1}),
+ .COEFF_2 ({10'd0, COEFF_2}),
+ .COEFF_3 ({10'd0, COEFF_3})
+ ) _TECHMAP_REPLACE_ (
.a ({10'd0, a_i}),
.b ({ 9'd0, b_i}),
.acc_fir (acc_fir_i),
@@ -115,11 +127,7 @@
.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})
+ .register_inputs (register_inputs_i)
);
assign z_o = z[18:0];
diff --git a/ql-qlf-plugin/qlf_k6n10f/dsp_map.v b/ql-qlf-plugin/qlf_k6n10f/dsp_map.v
index 054d82a..e10df2a 100644
--- a/ql-qlf-plugin/qlf_k6n10f/dsp_map.v
+++ b/ql-qlf-plugin/qlf_k6n10f/dsp_map.v
@@ -41,11 +41,7 @@
.shift_right_i (6'd0),
.round_i (1'b0),
.subtract_i (1'b0),
- .register_inputs_i (1'b0),
- .coeff_0_i (20'd0),
- .coeff_1_i (20'd0),
- .coeff_2_i (20'd0),
- .coeff_3_i (20'd0)
+ .register_inputs_i (1'b0)
);
assign Y = z;
@@ -87,11 +83,7 @@
.shift_right_i (6'd0),
.round_i (1'b0),
.subtract_i (1'b0),
- .register_inputs_i (1'b0),
- .coeff_0_i (10'd0),
- .coeff_1_i (10'd0),
- .coeff_2_i (10'd0),
- .coeff_3_i (10'd0)
+ .register_inputs_i (1'b0)
);
assign Y = z;
diff --git a/ql-qlf-plugin/synth_quicklogic.cc b/ql-qlf-plugin/synth_quicklogic.cc
index 3bb44c5..fdb23b0 100644
--- a/ql-qlf-plugin/synth_quicklogic.cc
+++ b/ql-qlf-plugin/synth_quicklogic.cc
@@ -269,6 +269,7 @@
if (help_mode) {
run("wreduce t:$mul", " (for qlf_k6n10f if not -no_dsp)");
+ run("ql_dsp_macc", " (for qlf_k6n10f if not -no_dsp)");
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)");
@@ -277,6 +278,8 @@
} else if (!nodsp) {
run("wreduce t:$mul");
+ run("ql_dsp_macc");
+
for (const auto &rule : dsp_rules) {
run(stringf("techmap -map +/mul2dsp.v "
"-D DSP_A_MAXWIDTH=%zu -D DSP_B_MAXWIDTH=%zu "
diff --git a/ql-qlf-plugin/tests/Makefile b/ql-qlf-plugin/tests/Makefile
index e54aeaf..1cc48ca 100644
--- a/ql-qlf-plugin/tests/Makefile
+++ b/ql-qlf-plugin/tests/Makefile
@@ -22,7 +22,8 @@
fsm \
pp3_bram \
qlf_k6n10f/dsp_mult \
- qlf_k6n10f/dsp_simd
+ qlf_k6n10f/dsp_simd \
+ qlf_k6n10f/dsp_macc
# qlf_k6n10_bram \
include $(shell pwd)/../../Makefile_test.common
@@ -42,4 +43,5 @@
pp3_bram_verify = true
qlf_k6n10f-dsp_mult_verify = true
qlf_k6n10f-dsp_simd_verify = true
+qlf_k6n10f-dsp_macc_verify = true
#qlf_k6n10_bram_verify = true
diff --git a/ql-qlf-plugin/tests/qlf_k6n10f/dsp_macc/dsp_macc.tcl b/ql-qlf-plugin/tests/qlf_k6n10f/dsp_macc/dsp_macc.tcl
new file mode 100644
index 0000000..2de3bdc
--- /dev/null
+++ b/ql-qlf-plugin/tests/qlf_k6n10f/dsp_macc/dsp_macc.tcl
@@ -0,0 +1,66 @@
+yosys -import
+if { [info procs quicklogic_eqn] == {} } { plugin -i ql-qlf}
+yosys -import ;# ingest plugin commands
+
+read_verilog dsp_macc.v
+design -save read
+
+set TOP "macc_simple"
+design -load read
+hierarchy -top $TOP
+synth_quicklogic -family qlf_k6n10f -top $TOP
+yosys cd $TOP
+select -assert-count 1 t:QL_DSP2
+select -assert-count 1 t:*
+
+set TOP "macc_simple_clr"
+design -load read
+hierarchy -top $TOP
+synth_quicklogic -family qlf_k6n10f -top $TOP
+yosys cd $TOP
+select -assert-count 1 t:QL_DSP2
+select -assert-count 1 t:\$lut
+select -assert-count 2 t:*
+
+set TOP "macc_simple_arst"
+design -load read
+hierarchy -top $TOP
+synth_quicklogic -family qlf_k6n10f -top $TOP
+yosys cd $TOP
+select -assert-count 1 t:QL_DSP2
+select -assert-count 1 t:*
+
+set TOP "macc_simple_ena"
+design -load read
+hierarchy -top $TOP
+synth_quicklogic -family qlf_k6n10f -top $TOP
+yosys cd $TOP
+select -assert-count 1 t:QL_DSP2
+select -assert-count 1 t:*
+
+set TOP "macc_simple_arst_clr_ena"
+design -load read
+hierarchy -top $TOP
+synth_quicklogic -family qlf_k6n10f -top $TOP
+yosys cd $TOP
+select -assert-count 1 t:QL_DSP2
+select -assert-count 1 t:\$lut
+select -assert-count 2 t:*
+
+set TOP "macc_simple_preacc"
+design -load read
+hierarchy -top $TOP
+synth_quicklogic -family qlf_k6n10f -top $TOP
+yosys cd $TOP
+select -assert-count 1 t:QL_DSP2
+select -assert-count 1 t:*
+
+set TOP "macc_simple_preacc_clr"
+design -load read
+hierarchy -top $TOP
+synth_quicklogic -family qlf_k6n10f -top $TOP
+yosys cd $TOP
+select -assert-count 1 t:QL_DSP2
+select -assert-count 1 t:\$lut
+select -assert-count 2 t:*
+
diff --git a/ql-qlf-plugin/tests/qlf_k6n10f/dsp_macc/dsp_macc.v b/ql-qlf-plugin/tests/qlf_k6n10f/dsp_macc/dsp_macc.v
new file mode 100644
index 0000000..0840210
--- /dev/null
+++ b/ql-qlf-plugin/tests/qlf_k6n10f/dsp_macc/dsp_macc.v
@@ -0,0 +1,113 @@
+// 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 macc_simple (
+ input wire clk,
+ input wire [ 7:0] A,
+ input wire [ 7:0] B,
+ output reg [15:0] Z
+);
+
+ always @(posedge clk)
+ Z <= Z + (A * B);
+
+endmodule
+
+module macc_simple_clr (
+ input wire clk,
+ input wire clr,
+ input wire [ 7:0] A,
+ input wire [ 7:0] B,
+ output reg [15:0] Z
+);
+
+ always @(posedge clk)
+ if (clr) Z <= (A * B);
+ else Z <= Z + (A * B);
+
+endmodule
+
+module macc_simple_arst (
+ input wire clk,
+ input wire rst,
+ input wire [ 7:0] A,
+ input wire [ 7:0] B,
+ output reg [15:0] Z
+);
+
+ always @(posedge clk or posedge rst)
+ if (rst) Z <= 0;
+ else Z <= Z + (A * B);
+
+endmodule
+
+module macc_simple_ena (
+ input wire clk,
+ input wire ena,
+ input wire [ 7:0] A,
+ input wire [ 7:0] B,
+ output reg [15:0] Z
+);
+
+ always @(posedge clk)
+ if (ena) Z <= Z + (A * B);
+
+endmodule
+
+module macc_simple_arst_clr_ena (
+ input wire clk,
+ input wire rst,
+ input wire clr,
+ input wire ena,
+ input wire [ 7:0] A,
+ input wire [ 7:0] B,
+ output reg [15:0] Z
+);
+
+ always @(posedge clk or posedge rst)
+ if (rst) Z <= 0;
+ else if (ena) begin
+ if (clr) Z <= (A * B);
+ else Z <= Z + (A * B);
+ end
+
+endmodule
+
+module macc_simple_preacc (
+ input wire clk,
+ input wire [ 7:0] A,
+ input wire [ 7:0] B,
+ output wire [15:0] Z
+);
+
+ reg [15:0] acc;
+
+ assign Z = acc + (A * B);
+
+ always @(posedge clk)
+ acc <= Z;
+
+endmodule
+
+module macc_simple_preacc_clr (
+ input wire clk,
+ input wire clr,
+ input wire [ 7:0] A,
+ input wire [ 7:0] B,
+ output reg [15:0] Z
+);
+
+ reg [15:0] acc;
+
+ assign Z = (clr) ? (A * B) : (acc + (A * B));
+
+ always @(posedge clk)
+ acc <= Z;
+
+endmodule
+
diff --git a/uhdm-plugin/UhdmAst.cc b/uhdm-plugin/UhdmAst.cc
index 46aa485..a08f513 100644
--- a/uhdm-plugin/UhdmAst.cc
+++ b/uhdm-plugin/UhdmAst.cc
@@ -841,30 +841,41 @@
static void simplify(AST::AstNode *current_node, AST::AstNode *parent_node)
{
+ auto dot_it =
+ std::find_if(current_node->children.begin(), current_node->children.end(), [](auto c) { return c->type == static_cast<int>(AST::AST_DOT); });
+ AST::AstNode *dot = (dot_it != current_node->children.end()) ? *dot_it : nullptr;
+
AST::AstNode *expanded = nullptr;
- AST::AstNode *dot = nullptr;
- for (auto c : current_node->children) {
- if (c->type == static_cast<int>(AST::AST_DOT) && expanded == nullptr) {
- dot = c;
- break;
- }
- }
if (dot) {
if (!AST_INTERNAL::current_scope.count(current_node->str)) {
// for accessing elements currently unsupported with AST_DOT
// fallback to "." notation
+ AST::AstNode *prefix_node = nullptr;
+ AST::AstNode *parent_node = current_node;
while (dot && !dot->str.empty()) {
- current_node->str += "." + dot->str.substr(1);
- if (!dot->children.empty()) {
- dot = dot->children[0];
+ // it is not possible for AST_RANGE to be after AST::DOT (see process_hier_path function)
+ if (parent_node->children[0]->type == AST::AST_RANGE) {
+ if (parent_node->children[1]->type == AST::AST_RANGE)
+ log_error("Multirange in AST_DOT is currently unsupported\n");
+
+ dot->type = AST::AST_IDENTIFIER;
+ simplify(dot, nullptr);
+ AST::AstNode *range_const = parent_node->children[0]->children[0];
+ prefix_node = new AST::AstNode(AST::AST_PREFIX, range_const->clone(), dot->clone());
+ break;
} else {
- dot = nullptr;
+ current_node->str += "." + dot->str.substr(1);
+ dot_it =
+ std::find_if(dot->children.begin(), dot->children.end(), [](auto c) { return c->type == static_cast<int>(AST::AST_DOT); });
+ parent_node = dot;
+ dot = (dot_it != dot->children.end()) ? *dot_it : nullptr;
}
}
- for (auto cc : current_node->children) {
- delete cc;
+ current_node->delete_children();
+ if (prefix_node != nullptr) {
+ current_node->type = AST::AST_PREFIX;
+ current_node->children = prefix_node->children;
}
- current_node->children.clear();
} else {
auto wire_node = AST_INTERNAL::current_scope[current_node->str];
// make sure wire_node is already simplified
@@ -2589,6 +2600,14 @@
current_node = nullptr;
break;
}
+ case vpiMinTypMaxOp: {
+ // ignore min and max and set only typ
+ log_assert(current_node->children.size() == 3);
+ auto tmp = current_node->children[1]->clone();
+ delete current_node;
+ current_node = tmp;
+ break;
+ }
default: {
delete current_node;
current_node = nullptr;