Merge pull request #232 from antmicro/k6n10f_dsp_model

k6n10f DSP behavioral model
diff --git a/Makefile_test.common b/Makefile_test.common
index 16e016e..84af520 100644
--- a/Makefile_test.common
+++ b/Makefile_test.common
@@ -62,6 +62,7 @@
 		fi \
 	else \
 		if [ $$$$RETVAL -ne 0 ]; then \
+			echo "Unexpected runtime error"; \
 		    printf "Test %-20s \e[31;1mFAILED\e[0m @ %s\n" $(1) $(CURDIR); \
 			false; \
 		fi \
@@ -69,6 +70,29 @@
 
 endef
 
+define test_sim_tpl =
+$(1): $(1)/ok
+	@printf "Test %-18s \e[32mPASSED\e[0m @ %s\n" $(1) $(CURDIR);
+
+$(1)/$$(notdir $(1).vvp): $(1)/$$(notdir $(1).v)
+	@iverilog -vvvv -g2005 -o $$@ $$< -I../ -DVCD_FILE=\"$(1)/$$(notdir $(1).vcd)\" >$(1)/$$(notdir $(1).vvp.log) 2>&1; \
+	if [ $$$$? -ne 0 ]; then \
+		printf "Test %-18s \e[31;1mFAILED\e[0m @ %s\n" $(1) $(CURDIR); \
+		false; \
+	fi
+
+$(1)/ok: $(1)/$$(notdir $(1).vvp) $(1)/$$(notdir $(1).v)
+	@vvp -vvvv $$< >$(1)/$$(notdir $(1).log) 2>&1; \
+	if [ $$$$? -ne 0 ]; then \
+		printf "Test %-18s \e[31;1mFAILED\e[0m @ %s\n" $(1) $(CURDIR); \
+		false; \
+	else \
+		touch $$@; \
+		true; \
+	fi
+
+endef
+
 define unit_test_tpl =
 $(1): $(1)/$(1).test
 	@$$<
@@ -83,7 +107,7 @@
 
 diff_test = diff $(1)/$(1).golden.$(2) $(1)/$(1).$(2)
 
-all: $(TESTS) $(UNIT_TESTS)
+all: $(TESTS) $(SIM_TESTS) $(UNIT_TESTS)
 
 $(GTEST_DIR)/build/lib/libgtest.a $(GTEST_DIR)/build/lib/libgtest_main.a:
 	@mkdir -p $(GTEST_DIR)/build
@@ -91,12 +115,14 @@
 	cmake ..; \
 	make
 
-.PHONY: all clean $(TESTS) $(UNIT_TESTS)
+.PHONY: all clean $(TESTS) $(SIM_TESTS) $(UNIT_TESTS)
 
 $(foreach test,$(TESTS),$(eval $(call test_tpl,$(test))))
+$(foreach test,$(SIM_TESTS),$(eval $(call test_sim_tpl,$(test))))
 $(foreach test,$(UNIT_TESTS),$(eval $(call unit_test_tpl,$(test))))
 
 clean:
 	@rm -rf $(foreach test,$(TESTS),$(test)/$(test).sdc $(test)/$(test)_[0-9].sdc $(test)/$(test).txt $(test)/$(test).eblif $(test)/$(test).json)
+	@rm -rf $(foreach test,$(SIM_TESTS),$(test)/*.vvp $(test)/*.vcd)
 	@rm -rf $(foreach test,$(UNIT_TESTS),$(test)/$(test).test.o $(test)/$(test).test.d $(test)/$(test).test)
 	@find . -name "ok" -or -name "*.log" | xargs rm -rf
diff --git a/environment.yml b/environment.yml
index cc60e2b..d6f473d 100644
--- a/environment.yml
+++ b/environment.yml
@@ -13,3 +13,4 @@
 dependencies:
   - litex-hub::yosys
   - litex-hub::surelog
+  - litex-hub::iverilog
diff --git a/ql-qlf-plugin/qlf_k6n10f/cells_sim.v b/ql-qlf-plugin/qlf_k6n10f/cells_sim.v
index 296fc81..6270f49 100644
--- a/ql-qlf-plugin/qlf_k6n10f/cells_sim.v
+++ b/ql-qlf-plugin/qlf_k6n10f/cells_sim.v
@@ -685,17 +685,17 @@
 
 (* blackbox *)
 module QL_DSP2 ( // TODO: Name subject to change
-    input  [19:0] a,
-    input  [17:0] b,
-    input  [ 3:0] acc_fir,
-    output [37:0] z,
-    output [17:0] dly_b,
+    input  [NBITS_A-1:0] a,
+    input  [NBITS_B-1:0] b,
+    input  [NBITS_AF-1:0] acc_fir,
+    output [NBITS_Z-1:0] z,
+    output [NBITS_B-1:0] dly_b,
 
     (* clkbuf_sink *)
     input         clk,
     input         reset,
 
-    input  [1:0]  feedback,
+    input  [2:0]  feedback,
     input         load_acc,
     input         unsigned_a,
     input         unsigned_b,
@@ -709,14 +709,359 @@
     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;
+    parameter [NBITS_COEF-1:0] COEFF_0 = 20'd0;
+    parameter [NBITS_COEF-1:0] COEFF_1 = 20'd0;
+    parameter [NBITS_COEF-1:0] COEFF_2 = 20'd0;
+    parameter [NBITS_COEF-1:0] COEFF_3 = 20'd0;
+
+      localparam NBITS_ACC = 64;
+      localparam NBITS_A = 20;
+      localparam NBITS_B = 18;
+      localparam NBITS_Z = 38;
+      localparam NBITS_COEF = 20;
+      localparam NBITS_AF = 4;
+
+      wire [NBITS_Z-1:0] dsp_full_z;
+      wire [(NBITS_Z/2)-1:0] dsp_frac0_z;
+      wire [(NBITS_Z/2)-1:0] dsp_frac1_z;
+
+      wire [NBITS_B-1:0] dsp_full_dly_b;
+      wire [(NBITS_B/2)-1:0] dsp_frac0_dly_b;
+      wire [(NBITS_B/2)-1:0] dsp_frac1_dly_b;
+
+      assign z = f_mode ? {dsp_frac1_z, dsp_frac0_z} : dsp_full_z;
+      assign dly_b = f_mode ? {dsp_frac1_dly_b, dsp_frac0_dly_b} : dsp_full_dly_b;
+
+	// Output used when fmode == 1
+        dsp_t1_sim #(
+	    .NBITS_A(NBITS_A/2),
+            .NBITS_B(NBITS_B/2),
+            .NBITS_ACC(NBITS_ACC/2),
+            .NBITS_Z(NBITS_Z/2),
+            .NBITS_COEF(NBITS_COEF/2),
+            .NBITS_AF(NBITS_AF/2)
+        ) dsp_frac0 (
+            .a_i(a[(NBITS_A/2)-1:0]),
+            .b_i(b[(NBITS_B/2)-1:0]),
+            .z_o(dsp_frac0_z),
+            .dly_b_o(dsp_frac0_dly_b),
+
+            .acc_fir_i(acc_fir[(NBITS_AF/2)-1:0]),
+            .feedback_i(feedback),
+            .load_acc_i(load_acc),
+
+            .unsigned_a_i(unsigned_a),
+            .unsigned_b_i(unsigned_b),
+
+            .clock_i(clk),
+            .reset_n_i(~reset),
+
+            .saturate_enable_i(saturate_enable),
+            .output_select_i(output_select),
+            .round_i(round),
+            .shift_right_i(shift_right),
+            .subtract_i(subtract),
+            .register_inputs_i(register_inputs),
+            .coef_0_i(COEFF_0[(NBITS_COEF/2)-1:0]),
+            .coef_1_i(COEFF_1[(NBITS_COEF/2)-1:0]),
+            .coef_2_i(COEFF_2[(NBITS_COEF/2)-1:0]),
+            .coef_3_i(COEFF_3[(NBITS_COEF/2)-1:0])
+        );
+
+	// Output used when fmode == 1
+        dsp_t1_sim #(
+	    .NBITS_A(NBITS_A/2),
+            .NBITS_B(NBITS_B/2),
+            .NBITS_ACC(NBITS_ACC/2),
+            .NBITS_Z(NBITS_Z/2),
+            .NBITS_COEF(NBITS_COEF/2),
+            .NBITS_AF(NBITS_AF/2)
+        ) dsp_frac1 (
+            .a_i(a[NBITS_A-1:NBITS_A/2]),
+            .b_i(b[NBITS_B-1:NBITS_B/2]),
+            .z_o(dsp_frac1_z),
+            .dly_b_o(dsp_frac1_dly_b),
+
+            .acc_fir_i(acc_fir[NBITS_AF-1:NBITS_AF/2]),
+            .feedback_i(feedback),
+            .load_acc_i(load_acc),
+
+            .unsigned_a_i(unsigned_a),
+            .unsigned_b_i(unsigned_b),
+
+            .clock_i(clk),
+            .reset_n_i(~reset),
+
+            .saturate_enable_i(saturate_enable),
+            .output_select_i(output_select),
+            .round_i(round),
+            .shift_right_i(shift_right),
+            .subtract_i(subtract),
+            .register_inputs_i(register_inputs),
+            .coef_0_i(COEFF_0[NBITS_COEF-1:NBITS_COEF/2]),
+            .coef_1_i(COEFF_1[NBITS_COEF-1:NBITS_COEF/2]),
+            .coef_2_i(COEFF_2[NBITS_COEF-1:NBITS_COEF/2]),
+            .coef_3_i(COEFF_3[NBITS_COEF-1:NBITS_COEF/2])
+        );
+
+	// Output used when fmode == 0
+        dsp_t1_sim #(
+             .NBITS_A(NBITS_A),
+             .NBITS_B(NBITS_B),
+             .NBITS_ACC(NBITS_ACC),
+             .NBITS_Z(NBITS_Z),
+             .NBITS_COEF(NBITS_COEF),
+             .NBITS_AF(NBITS_AF)
+        ) dsp_full (
+            .a_i(a),
+            .b_i(b),
+            .z_o(dsp_full_z),
+            .dly_b_o(dsp_full_dly_b),
+
+            .acc_fir_i(acc_fir),
+            .feedback_i(feedback),
+            .load_acc_i(load_acc),
+
+            .unsigned_a_i(unsigned_a),
+            .unsigned_b_i(unsigned_b),
+
+            .clock_i(clk),
+            .reset_n_i(~reset),
+
+            .saturate_enable_i(saturate_enable),
+            .output_select_i(output_select),
+            .round_i(round),
+            .shift_right_i(shift_right),
+            .subtract_i(subtract),
+            .register_inputs_i(register_inputs),
+            .coef_0_i(COEFF_0),
+            .coef_1_i(COEFF_1),
+            .coef_2_i(COEFF_2),
+            .coef_3_i(COEFF_3)
+        );
+endmodule
+
+module dsp_t1_sim # (
+    parameter NBITS_ACC  = 64,
+    parameter NBITS_A    = 20,
+    parameter NBITS_B    = 18,
+    parameter NBITS_Z	 = 38,
+    parameter NBITS_COEF = 20,
+    parameter NBITS_AF   = 4
+)(
+    input [NBITS_A-1:0] a_i,
+    input [NBITS_B-1:0] b_i,
+    output [NBITS_Z-1:0] z_o,
+    output [NBITS_B-1:0] dly_b_o,
+
+    input [NBITS_AF-1:0] acc_fir_i,
+    input [2:0] feedback_i,
+    input load_acc_i,
+
+    input unsigned_a_i,
+    input unsigned_b_i,
+
+    input clock_i,
+    input reset_n_i,
+
+    input saturate_enable_i,
+    input [2:0] output_select_i,
+    input round_i,
+    input [5:0] shift_right_i,
+    input subtract_i,
+    input register_inputs_i,
+    input [NBITS_COEF-1:0] coef_0_i,
+    input [NBITS_COEF-1:0] coef_1_i,
+    input [NBITS_COEF-1:0] coef_2_i,
+    input [NBITS_COEF-1:0] coef_3_i
+);
+
+// FIXME: The version of Icarus Verilog from Conda seems not to recognize the
+// $error macro. Disable this sanity check for now because of that.
+`ifndef __ICARUS__
+    if (NBITS_ACC < NBITS_A + NBITS_B)
+        $error("NBITS_ACC must be > NBITS_A + NBITS_B");
+`endif
+
+    // Input registers
+    reg  [NBITS_A-1:0]  r_a;
+    reg  [NBITS_B-1:0]  r_b;
+    reg  [NBITS_AF-1:0] r_acc_fir;
+    reg                 r_unsigned_a;
+    reg                 r_unsigned_b;
+    reg                 r_load_acc;
+    reg  [2:0]          r_feedback;
+    reg  [5:0]          r_shift_d1;
+    reg  [5:0]          r_shift_d2;
+    reg			r_subtract;
+    reg			r_sat;
+    reg			r_rnd;
+
+    always @(posedge clock_i or negedge reset_n_i) begin
+        if (~reset_n_i) begin
+
+            r_a <= 'h0;
+            r_b <= 'h0;
+
+	    r_acc_fir	 <= 0;
+            r_unsigned_a <= 0;
+            r_unsigned_b <= 0;
+            r_feedback   <= 0;
+            r_shift_d1   <= 0;
+            r_shift_d2   <= 0;
+	    r_subtract   <= 0;
+            r_load_acc   <= 0;
+            r_sat	 <= 0;
+            r_rnd	 <= 0;
+
+        end else begin
+
+            r_a <= a_i;
+            r_b <= b_i;
+
+	    r_acc_fir	 <= acc_fir_i;
+            r_unsigned_a <= unsigned_a_i;
+            r_unsigned_b <= unsigned_b_i;
+            r_feedback   <= feedback_i;
+            r_shift_d1   <= shift_right_i;
+            r_shift_d2   <= r_shift_d1;
+	    r_subtract   <= subtract_i;
+            r_load_acc   <= load_acc_i;
+            r_sat	 <= r_sat;
+            r_rnd	 <= r_rnd;
+
+        end
+    end
+
+    // Registered / non-registered input path select
+    wire [NBITS_A-1:0]  a = register_inputs_i ? r_a : a_i;
+    wire [NBITS_B-1:0]  b = register_inputs_i ? r_b : b_i;
+
+    wire [NBITS_AF-1:0] acc_fir = register_inputs_i ? r_acc_fir : acc_fir_i;
+    wire       unsigned_a = register_inputs_i ? r_unsigned_a : unsigned_a_i;
+    wire       unsigned_b = register_inputs_i ? r_unsigned_b : unsigned_b_i;
+    wire [2:0] feedback   = register_inputs_i ? r_feedback   : feedback_i;
+    wire       load_acc   = register_inputs_i ? r_load_acc   : load_acc_i;
+    wire       subtract   = register_inputs_i ? r_subtract   : subtract_i;
+    wire       sat	  = register_inputs_i ? r_sat : saturate_enable_i;
+    wire       rnd	  = register_inputs_i ? r_rnd : round_i;
+
+    // Shift right control
+    wire [5:0] shift_d1 = register_inputs_i ? r_shift_d1 : shift_right_i;
+    wire [5:0] shift_d2 = output_select_i[1] ? shift_d1 : r_shift_d2;
+    //localparam SHIFT_SEL = {register_inputs_i, output_select_i[1]};
+    //wire [5:0] shift_right = (SHIFT_SEL == 2'b00) ?   shift_right_i :
+                             //(SHIFT_SEL == 2'b01) ?   r_shift_d1 :
+                             //(SHIFT_SEL == 2'b10) ?   r_shift_d1 :
+                           //[>(SHIFT_SEL == 2'b11) ?<] r_shift_d2;
+
+    // Multiplier
+    wire unsigned_mode = unsigned_a & unsigned_b;
+    wire [NBITS_A-1:0] mult_a;
+    assign mult_a = (feedback == 3'h0) ?   a :
+                    (feedback == 3'h1) ?   a :
+                    (feedback == 3'h2) ?   a :
+                    (feedback == 3'h3) ?   acc[NBITS_A-1:0] :
+                    (feedback == 3'h4) ?   coef_0_i :
+                    (feedback == 3'h5) ?   coef_1_i :
+                    (feedback == 3'h6) ?   coef_2_i :
+					   coef_3_i;	// if feedback == 3'h7
+
+    wire [NBITS_B-1:0] mult_b = (feedback == 2'h2) ? {NBITS_B{1'b0}}  : b;
+
+    wire [NBITS_A-1:0] mult_sgn_a = mult_a[NBITS_A-1];
+    wire [NBITS_A-1:0] mult_mag_a = (mult_sgn_a) ? (~mult_a + 1) : mult_a;
+    wire [NBITS_B-1:0] mult_sgn_b = mult_b[NBITS_B-1];
+    wire [NBITS_B-1:0] mult_mag_b = (mult_sgn_b) ? (~mult_b + 1) : mult_b;
+
+    wire [NBITS_A+NBITS_B-1:0] mult_mag = mult_mag_a * mult_mag_b;
+    wire mult_sgn = mult_sgn_a ^ mult_sgn_b;
+
+    wire [NBITS_A+NBITS_B-1:0] mult = (unsigned_a && unsigned_b) ?
+        (mult_a * mult_b) : (mult_sgn ? (~mult_mag + 1) : mult_mag);
+
+    // Sign extension
+    wire [NBITS_ACC-1:0] mult_xtnd = unsigned_mode ?
+        {{(NBITS_ACC-NBITS_A-NBITS_B){1'b0}},                    mult[NBITS_A+NBITS_B-1:0]} :
+        {{(NBITS_ACC-NBITS_A-NBITS_B){mult[NBITS_A+NBITS_B-1]}}, mult[NBITS_A+NBITS_B-1:0]};
+
+    wire [NBITS_ACC-1:0] a_xtnd = (unsigned_a) ?
+			      { {(NBITS_ACC - NBITS_A - NBITS_AF){1'b0}}, acc_fir, {a} } :
+			      { {(NBITS_ACC - NBITS_A - NBITS_AF){acc_fir[NBITS_AF-1]}}, acc_fir, {a[NBITS_A-1:0]} };
+
+    // Adder
+    wire [NBITS_ACC-1:0] add_a = (subtract_i) ? (~mult_xtnd + 1) : mult_xtnd;
+    wire [NBITS_ACC-1:0] add_b = (feedback_i == 3'h0) ? acc :
+                                 (feedback_i == 3'h1) ? {{NBITS_ACC}{1'b0}} : a_xtnd;
+
+    wire [NBITS_ACC-1:0] add_o = add_a + add_b;
+
+    // Accumulator
+    reg [NBITS_ACC-1:0] acc;
+    always @(posedge clock_i or negedge reset_n_i)
+        if (~reset_n_i) acc <= 'h0;
+        else begin
+            if (load_acc)
+                acc <= add_o;
+            else
+                acc <= acc;
+        end
+
+    // Adder/accumulator output selection
+    wire [NBITS_ACC-1:0] acc_out = (output_select_i[1]) ? add_o : acc;
+
+    // Round, shift, saturate
+    wire [NBITS_ACC-1:0] acc_rnd = (rnd && (shift_right_i != 0)) ? (acc_out + ({{(NBITS_ACC-1){1'b0}}, 1'b1} << (shift_right_i - 1))) :
+                                                                    acc_out;
+
+    wire [NBITS_ACC-1:0] acc_shr = (unsigned_mode) ? (acc_rnd  >> shift_right_i) :
+                                                     (acc_rnd >>> shift_right_i);
+
+    wire [NBITS_ACC-1:0] acc_sat_u = (acc_shr[NBITS_ACC-1:NBITS_Z] != 0) ? {{(NBITS_ACC-NBITS_Z){1'b0}},{NBITS_Z{1'b1}}} :
+                                                                           {{(NBITS_ACC-NBITS_Z){1'b0}},{acc_shr[NBITS_Z-1:0]}};
+
+    wire [NBITS_ACC-1:0] acc_sat_s = ((|acc_shr[NBITS_ACC-1:NBITS_Z-1] == 1'b0) ||
+                                      (&acc_shr[NBITS_ACC-1:NBITS_Z-1] == 1'b1)) ? {{(NBITS_ACC-NBITS_Z){1'b0}},{acc_shr[NBITS_Z-1:0]}} :
+                                                                                   {{(NBITS_ACC-NBITS_Z){1'b0}},{acc_shr[NBITS_ACC-1],{NBITS_Z-1{~acc_shr[NBITS_ACC-1]}}}};
+
+    wire [NBITS_ACC-1:0] acc_sat = (sat) ? ((unsigned_mode) ? acc_sat_u : acc_sat_s) : acc_shr;
+
+    // Output signals
+    wire [NBITS_Z-1:0]  z0;
+    reg  [NBITS_Z-1:0]  z1;
+    wire [NBITS_Z-1:0]  z2;
+
+    assign z0 = mult_xtnd[NBITS_Z-1:0];
+    assign z2 = acc_sat[NBITS_Z-1:0];
+
+    always @(posedge clock_i or negedge reset_n_i)
+        if (!reset_n_i)
+            z1 <= 0;
+        else begin
+            z1 <= (output_select_i == 3'b100) ? z0 : z2;
+        end
+
+    // Output mux
+    assign z_o = (output_select_i == 3'h0) ?   z0 :
+                 (output_select_i == 3'h1) ?   z2 :
+                 (output_select_i == 3'h2) ?   z2 :
+                 (output_select_i == 3'h3) ?   z2 :
+                 (output_select_i == 3'h4) ?   z1 :
+                 (output_select_i == 3'h5) ?   z1 :
+                 (output_select_i == 3'h6) ?   z1 :
+					       z1;	// if output_select_i == 3'h7
+
+    // B input delayed passthrough
+    reg [NBITS_B-1:0] dly_b_o;
+
+    always @(posedge clock_i or negedge reset_n_i)
+        if (!reset_n_i)
+            dly_b_o <= 0;
+        else
+            dly_b_o <= b_i;
 
 endmodule
 
-(* blackbox *) // TODO: add sim model
 module dsp_t1_20x18x64 (
     input  [19:0] a_i,
     input  [17:0] b_i,
@@ -728,7 +1073,7 @@
     input         clock_i,
     input         reset_i,
 
-    input  [1:0]  feedback_i,
+    input  [2:0]  feedback_i,
     input         load_acc_i,
     input         unsigned_a_i,
     input         unsigned_b_i,
@@ -746,9 +1091,38 @@
     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)
+   ) dsp (
+	.a(a_i),
+	.b(b_i),
+	.z(z_o),
+	.dly_b(dly_b_o),
+
+	.f_mode(1'b0),	// 20x18x64 DSP
+
+	.acc_fir(acc_fir_i),
+	.feedback(feedback_i),
+	.load_acc(load_acc_i),
+
+	.unsigned_a(unsigned_a_i),
+	.unsigned_b(unsigned_b_i),
+
+	.clk(clock_i),
+	.reset(reset_i),
+
+	.saturate_enable(saturate_enable_i),
+	.output_select(output_select_i),
+	.round(round_i),
+	.shift_right(shift_right_i),
+	.subtract(subtract_i),
+	.register_inputs(register_inputs_i)
+    );
 endmodule
 
-(* blackbox *) // TODO: add sim model
 module dsp_t1_10x9x32 (
     input  [ 9:0] a_i,
     input  [ 8:0] b_i,
@@ -760,7 +1134,7 @@
     input         clock_i,
     input         reset_i,
 
-    input  [1:0]  feedback_i,
+    input  [2:0]  feedback_i,
     input         load_acc_i,
     input         unsigned_a_i,
     input         unsigned_b_i,
@@ -778,4 +1152,37 @@
     parameter [9:0] COEFF_2 = 10'd0;
     parameter [9:0] COEFF_3 = 10'd0;
 
+    wire [18:0] z_rem;
+    wire [8:0] dly_b_rem;
+
+    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})
+   ) dsp (
+	.a({10'd0, a_i}),
+	.b({9'd0, b_i}),
+	.z({z_rem, z_o}),
+	.dly_b({dly_b_rem, dly_b_o}),
+
+	.f_mode(1'b1),	// 10x9x32 DSP
+
+	.acc_fir({2'd0, acc_fir_i}),
+	.feedback(feedback_i),
+	.load_acc(load_acc_i),
+
+	.unsigned_a(unsigned_a_i),
+	.unsigned_b(unsigned_b_i),
+
+	.clk(clock_i),
+	.reset(reset_i),
+
+	.saturate_enable(saturate_enable_i),
+	.output_select(output_select_i),
+	.round(round_i),
+	.shift_right(shift_right_i),
+	.subtract(subtract_i),
+	.register_inputs(register_inputs_i)
+    );
 endmodule
diff --git a/ql-qlf-plugin/tests/Makefile b/ql-qlf-plugin/tests/Makefile
index 1cc48ca..77938e3 100644
--- a/ql-qlf-plugin/tests/Makefile
+++ b/ql-qlf-plugin/tests/Makefile
@@ -26,6 +26,11 @@
     qlf_k6n10f/dsp_macc
 #	qlf_k6n10_bram \
 
+SIM_TESTS = \
+    qlf_k6n10f/sim_dsp_mult \
+    qlf_k6n10f/sim_dsp_mult_r \
+    qlf_k6n10f/sim_dsp_fir
+
 include $(shell pwd)/../../Makefile_test.common
 
 consts_verify = true
diff --git a/ql-qlf-plugin/tests/qlf_k6n10f/sim_dsp_fir/sim_dsp_fir.v b/ql-qlf-plugin/tests/qlf_k6n10f/sim_dsp_fir/sim_dsp_fir.v
new file mode 100644
index 0000000..2f953c0
--- /dev/null
+++ b/ql-qlf-plugin/tests/qlf_k6n10f/sim_dsp_fir/sim_dsp_fir.v
@@ -0,0 +1,143 @@
+// 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
+
+`include "qlf_k6n10f/cells_sim.v"
+
+module tb();
+
+    // Clock
+    reg clk;
+    initial clk <= 1'b0;
+    always #0.5 clk <= ~clk;
+
+    // Reset
+    reg rst;
+    initial begin
+            rst <= 1'b0;
+        #2  rst <= 1'b1;
+        #2  rst <= 1'b0;
+    end
+
+    // Filter control
+    reg [2:0] fcnt;
+    reg [3:0] dcnt;
+
+    initial begin
+        fcnt <= 0;
+        dcnt <= 0;
+    end
+
+    // MAC cycle counter
+    always @(posedge clk)
+        if (rst) fcnt <= 0;
+        else begin
+            if (fcnt == 4)
+                fcnt <= 0;
+            else
+                fcnt <= fcnt + 1;
+        end
+
+    wire stb = (fcnt == 4);
+
+    // Data address counter
+    always @(posedge clk)
+        if (rst)      dcnt <= 0;
+        else if (stb) dcnt <= dcnt + 1;
+
+    // Filter coeffs (S0.19)
+    reg signed [19:0] coeff;
+    always @(*) case (fcnt)
+        2'd0: coeff <= 20'h0000B;
+        2'd1: coeff <= 20'h0000E;
+        2'd2: coeff <= 20'h0000E;
+        2'd3: coeff <= 20'h0000F;
+
+        default: coeff <= 20'h00000;
+    endcase
+
+    // Input data (S0.17)
+    reg signed [17:0] data;
+    always @(*) case (dcnt)
+        'd0:  data  <= 18'h00400;
+        'd1:  data  <= 18'h00000;
+        'd2:  data  <= 18'h00000;
+        'd3:  data  <= 18'h00000;
+        'd4:  data  <= 18'h00000;
+        'd5:  data  <= 18'h00000;
+        'd6:  data  <= 18'h00000;
+        'd7:  data  <= 18'h00000;
+        'd8:  data  <= 18'h00800;
+        default data <= 18'h00000;
+    endcase
+
+    // UUT
+    wire signed [3:0] acc_fir_i = 4'h0;
+    wire signed [19:0] A = coeff;
+    wire signed [17:0] B = data;
+    wire signed [37:0] Z;
+
+    dsp_t1_sim # (
+    ) uut (
+        .clock_i		(clk),
+        .reset_n_i		(~rst),
+        .a_i			((!stb) ? A : 20'h0),
+        .b_i			((!stb) ? B : 18'h0),
+        .acc_fir_i		((!stb) ? acc_fir_i : 4'h0),
+        .unsigned_a_i		(1'b0),
+        .unsigned_b_i		(1'b0),
+        .feedback_i		(stb),
+        .load_acc_i		(1'b1),
+        .shift_right_i		(6'd10),
+	.register_inputs_i	(1'b0),
+	.output_select_i	(3'h1),
+	.round_i		(1'b1),
+	.saturate_enable_i	(1'b1),
+	.subtract_i		(1'b0),
+        .z_o			(Z)
+    );
+
+    // Output counter
+    integer ocnt;
+    initial ocnt <= 0;
+
+    always @(posedge clk)
+        if (stb) ocnt <= ocnt + 1;
+
+    // Expected output data
+    reg signed [31:0] odata;
+    always @(*) case (ocnt)
+    'd0: odata <= 32'h000036;
+    'd1: odata <= 32'h000000;
+    'd2: odata <= 32'h000000;
+    'd3: odata <= 32'h000000;
+    'd4: odata <= 32'h000000;
+    'd5: odata <= 32'h000000;
+    'd6: odata <= 32'h000000;
+    'd7: odata <= 32'h000000;
+    'd8: odata <= 32'h00006C;
+    default: odata <= 32'h000000;
+    endcase
+
+    // Error detection
+    wire error = stb && (odata !== Z[31:0]);
+
+    // Error counting
+    integer error_count;
+    initial error_count <= 0;
+    always @(posedge clk) begin
+        if (error) error_count <= error_count + 1;
+    end
+
+    // Simulation control / data dump
+    initial begin
+        $dumpfile(`VCD_FILE);
+        $dumpvars(0, tb);
+        #150 $finish_and_return( (error_count == 0) ? 0 : -1 );
+    end
+
+endmodule
diff --git a/ql-qlf-plugin/tests/qlf_k6n10f/sim_dsp_mult/sim_dsp_mult.v b/ql-qlf-plugin/tests/qlf_k6n10f/sim_dsp_mult/sim_dsp_mult.v
new file mode 100644
index 0000000..f162756
--- /dev/null
+++ b/ql-qlf-plugin/tests/qlf_k6n10f/sim_dsp_mult/sim_dsp_mult.v
@@ -0,0 +1,71 @@
+// 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
+
+`include "qlf_k6n10f/cells_sim.v"
+`timescale 1ns/1ps
+
+module tb();
+
+    // Clock
+    reg clk;
+    initial clk <= 1'b0;
+    always #0.5 clk <= ~clk;
+
+    // Reset
+    reg rst;
+    initial begin
+            rst <= 1'b0;
+        #1  rst <= 1'b1;
+        #2  rst <= 1'b0;
+    end
+
+    // Input data / reference
+    reg signed [19:0] A;
+    reg signed [17:0] B;
+    reg signed [37:0] C;
+
+    always @(posedge clk) begin
+        A = $random;
+        B = $random;
+
+        C <= A * B;
+    end
+
+    // UUT
+    wire signed [37:0] Z;
+
+    dsp_t1_sim # (
+    ) uut (
+        .a_i            	(A),
+        .b_i            	(B),
+        .unsigned_a_i   	(1'h0),
+        .unsigned_b_i   	(1'h0),
+        .feedback_i     	(3'h0),
+	.register_inputs_i	(1'h0),
+	.output_select_i	(3'h0),
+        .z_o            	(Z)
+    );
+
+    // Error detection
+    wire error = (Z !== C);
+
+    // Error counting
+    integer error_count;
+    initial error_count <= 0;
+    always @(posedge clk) begin
+        if (error) error_count <= error_count + 1;
+    end
+
+    // Simulation control / data dump
+    initial begin
+        $dumpfile(`VCD_FILE);
+        $dumpvars(0, tb);
+        #10000 $finish_and_return( (error_count == 0) ? 0 : -1 );
+    end
+
+endmodule
diff --git a/ql-qlf-plugin/tests/qlf_k6n10f/sim_dsp_mult_r/sim_dsp_mult_r.v b/ql-qlf-plugin/tests/qlf_k6n10f/sim_dsp_mult_r/sim_dsp_mult_r.v
new file mode 100644
index 0000000..6187e1a
--- /dev/null
+++ b/ql-qlf-plugin/tests/qlf_k6n10f/sim_dsp_mult_r/sim_dsp_mult_r.v
@@ -0,0 +1,80 @@
+// 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
+
+`include "qlf_k6n10f/cells_sim.v"
+
+module tb();
+
+    // Clock
+    reg clk;
+    initial clk <= 1'b0;
+    always #0.5 clk <= ~clk;
+
+    // Reset
+    reg rst;
+    initial begin
+            rst <= 1'b0;
+        #1  rst <= 1'b1;
+        #2  rst <= 1'b0;
+    end
+
+    // Input data / reference
+    reg signed [19:0] A;
+    reg signed [17:0] B;
+    reg signed [37:0] C;
+
+    // Shift data change half a clock cycle
+    // to make registered inputs apparent
+    initial begin
+	forever begin
+        A = $random;
+        B = $random;
+
+        C <= A * B;
+	#1.5;
+	end
+    end
+
+    // UUT
+    wire signed [37:0] Z;
+
+    dsp_t1_sim # (
+    ) uut (
+        .a_i			(A),
+        .b_i			(B),
+        .unsigned_a_i		(1'h0),
+        .unsigned_b_i		(1'h0),
+        .feedback_i		(3'h0),
+	.register_inputs_i	(1'h1),
+	.output_select_i	(3'h0),
+	.clock_i		(clk),
+        .z_o			(Z)
+    );
+
+    // Error detection
+    reg [37:0] r_C;
+    always @(posedge clk)
+        r_C <= C;
+
+    wire error = (Z !== r_C);
+
+    // Error counting
+    integer error_count;
+    initial error_count <= 0;
+    always @(posedge clk) begin
+        if (error) error_count <= error_count + 1;
+    end
+
+    // Simulation control / data dump
+    initial begin
+        $dumpfile(`VCD_FILE);
+        $dumpvars(0, tb);
+        #100 $finish_and_return( (error_count == 0) ? 0 : -1 );
+    end
+
+endmodule