From 4d215665a12b18d49979b733034a80cdaf28e94b Mon Sep 17 00:00:00 2001 From: Johan Olby Date: Mon, 13 Jul 2026 20:57:08 +0200 Subject: [PATCH] gowin: infer DSP multiply-accumulate for the GW5A family Map $macc_v2 cells (one signed <=27x18 product + one <=48-bit addend) to MULTALU27X18 with the C addend enabled, so a*b+c maps to a single DSP block instead of a multiply + fabric adder. The C addend requires DYN_C_SEL("TRUE") + CSEL=1 (gowin_pack reads the CSEL port, not the C_SEL parameter). alumacc + macc techmap run before wreduce for gw5a, so $mul and $add ports still have matching widths when alumacc tries to merge them. Running after wreduce breaks the merge: wreduce narrows $mul Y (48->45) but not $add A (stays 48), and alumacc can't merge mismatched widths. --- techlibs/gowin/CMakeLists.txt | 1 + techlibs/gowin/macc_map_gw5a.v | 59 +++++++++++++++++++++++ techlibs/gowin/synth_gowin.cc | 7 +++ tests/arch/gowin/macc_gw5a.v | 44 +++++++++++++++++ tests/arch/gowin/macc_gw5a.ys | 87 ++++++++++++++++++++++++++++++++++ 5 files changed, 198 insertions(+) create mode 100644 techlibs/gowin/macc_map_gw5a.v create mode 100644 tests/arch/gowin/macc_gw5a.v create mode 100644 tests/arch/gowin/macc_gw5a.ys diff --git a/techlibs/gowin/CMakeLists.txt b/techlibs/gowin/CMakeLists.txt index f74305818..4b6043830 100644 --- a/techlibs/gowin/CMakeLists.txt +++ b/techlibs/gowin/CMakeLists.txt @@ -52,4 +52,5 @@ yosys_pass(synth_gowin lutrams_map.v lutrams.txt dsp_map.v + macc_map_gw5a.v ) diff --git a/techlibs/gowin/macc_map_gw5a.v b/techlibs/gowin/macc_map_gw5a.v new file mode 100644 index 000000000..bc9728d74 --- /dev/null +++ b/techlibs/gowin/macc_map_gw5a.v @@ -0,0 +1,59 @@ +module \$macc_v2 (A, B, C, Y); + + parameter NPRODUCTS = 0; + parameter PRODUCT_NEGATED = 1'b0; + parameter NADDENDS = 0; + parameter ADDEND_NEGATED = 1'b0; + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter C_SIGNED = 0; + parameter A_WIDTHS = 1; + parameter B_WIDTHS = 1; + parameter C_WIDTHS = 1; + parameter Y_WIDTH = 1; + + input [A_WIDTHS-1:0] A; + input [B_WIDTHS-1:0] B; + input [C_WIDTHS-1:0] C; + output [Y_WIDTH-1:0] Y; + + wire _TECHMAP_FAIL_ = !(NPRODUCTS == 1 && NADDENDS == 1 && + PRODUCT_NEGATED == 1'b0 && ADDEND_NEGATED == 1'b0 && + A_SIGNED && B_SIGNED && C_SIGNED && + A_WIDTHS <= 27 && B_WIDTHS <= 18 && C_WIDTHS <= 48); + + wire [47:0] dout_w; + + MULTALU27X18 #( + .MULT12X12_EN("FALSE"), + .MULT_RESET_MODE("SYNC"), + .AREG_CLK("BYPASS"), + .BREG_CLK("BYPASS"), + .PREG_CLK("BYPASS"), + .OREG_CLK("BYPASS"), + .DYN_C_SEL("TRUE") + ) __TECHMAP_REPLACE__ ( + .DOUT(dout_w), + .CASO(), + .SOA(), + .A(A), + .SIA(27'd0), + .B(B), + .C(C), + .D(26'd0), + .CASI(48'd0), + .ACCSEL(1'b0), + .PSEL(1'b0), + .ASEL(1'b0), + .PADDSUB(1'b0), + .CSEL(1'b1), + .CASISEL(1'b0), + .ADDSUB(2'b00), + .CLK(2'b00), + .CE(2'b00), + .RESET(2'b00) + ); + + assign Y = dout_w; + +endmodule diff --git a/techlibs/gowin/synth_gowin.cc b/techlibs/gowin/synth_gowin.cc index d5ebdafea..25d78fc7b 100644 --- a/techlibs/gowin/synth_gowin.cc +++ b/techlibs/gowin/synth_gowin.cc @@ -277,6 +277,13 @@ struct SynthGowinPass : public ScriptPass run("opt -nodffe -nosdff"); run("fsm"); run("opt"); + // GW5A MAC: run alumacc + macc techmap BEFORE wreduce. wreduce + // narrows $mul Y but not the connected $add A, leaving a width + // mismatch that prevents alumacc from merging $mul into $add. + if (!nodsp && family == "gw5a") { + run("alumacc"); + run("techmap -map +/gowin/macc_map_gw5a.v"); + } run("wreduce"); run("peepopt"); run("opt_clean"); diff --git a/tests/arch/gowin/macc_gw5a.v b/tests/arch/gowin/macc_gw5a.v new file mode 100644 index 000000000..629e25e1d --- /dev/null +++ b/tests/arch/gowin/macc_gw5a.v @@ -0,0 +1,44 @@ +module top +#(parameter X_WIDTH=8, Y_WIDTH=8, C_WIDTH=16, A_WIDTH=16) +( + input signed [X_WIDTH-1:0] x, + input signed [Y_WIDTH-1:0] y, + input signed [C_WIDTH-1:0] c, + output signed [A_WIDTH-1:0] A +); + assign A = x * y + c; +endmodule + +module sub_top +#(parameter X_WIDTH=8, Y_WIDTH=8, C_WIDTH=16, A_WIDTH=16) +( + input signed [X_WIDTH-1:0] x, + input signed [Y_WIDTH-1:0] y, + input signed [C_WIDTH-1:0] c, + output signed [A_WIDTH-1:0] A +); + assign A = x * y - c; +endmodule + +module twoproduct_top +#(parameter X_WIDTH=8, Y_WIDTH=8, C_WIDTH=16, A_WIDTH=16) +( + input signed [X_WIDTH-1:0] x, + input signed [Y_WIDTH-1:0] y, + input signed [C_WIDTH-1:0] c, + output signed [A_WIDTH-1:0] A +); + wire signed [X_WIDTH+Y_WIDTH-1:0] p = x * y; + assign A = p + p; +endmodule + +module unsigned_top +#(parameter X_WIDTH=8, Y_WIDTH=8, C_WIDTH=16, A_WIDTH=16) +( + input [X_WIDTH-1:0] x, + input [Y_WIDTH-1:0] y, + input [C_WIDTH-1:0] c, + output [A_WIDTH-1:0] A +); + assign A = x * y + c; +endmodule diff --git a/tests/arch/gowin/macc_gw5a.ys b/tests/arch/gowin/macc_gw5a.ys new file mode 100644 index 000000000..314c9e226 --- /dev/null +++ b/tests/arch/gowin/macc_gw5a.ys @@ -0,0 +1,87 @@ +# GW5A DSP MAC ($macc_v2) inference tests. + +# 27x18 + 48-bit addend -> 1 MULTALU27X18 (MAC) +read_verilog macc_gw5a.v +chparam -set X_WIDTH 27 -set Y_WIDTH 18 -set C_WIDTH 48 -set A_WIDTH 48 +hierarchy -top top +proc +synth_gowin -family gw5a +cd top +select -assert-count 1 t:MULTALU27X18 + + +# Make sure that DSPs are not inferred with -nodsp option +design -reset +read_verilog macc_gw5a.v +chparam -set X_WIDTH 27 -set Y_WIDTH 18 -set C_WIDTH 48 -set A_WIDTH 48 +hierarchy -top top +proc +synth_gowin -family gw5a -nodsp +cd top +select -assert-none t:MULTALU27X18 + + +# A operand exceeds 27 -> _TECHMAP_FAIL_ (A_WIDTHS > 27) +design -reset +read_verilog macc_gw5a.v +chparam -set X_WIDTH 28 -set Y_WIDTH 18 -set C_WIDTH 48 -set A_WIDTH 48 +hierarchy -top top +proc +synth_gowin -family gw5a +cd top +select -assert-none t:MULTALU27X18 + + +# B operand exceeds 18 -> _TECHMAP_FAIL_ (B_WIDTHS > 18) +design -reset +read_verilog macc_gw5a.v +chparam -set X_WIDTH 27 -set Y_WIDTH 19 -set C_WIDTH 48 -set A_WIDTH 48 +hierarchy -top top +proc +synth_gowin -family gw5a +cd top +select -assert-none t:MULTALU27X18 + + +# C addend exceeds 48 -> _TECHMAP_FAIL_ (C_WIDTHS > 48) +design -reset +read_verilog macc_gw5a.v +chparam -set X_WIDTH 27 -set Y_WIDTH 18 -set C_WIDTH 49 -set A_WIDTH 49 +hierarchy -top top +proc +synth_gowin -family gw5a +cd top +select -assert-none t:MULTALU27X18 + + +# Addend negated (a*b - c) -> _TECHMAP_FAIL_ (ADDEND_NEGATED) +design -reset +read_verilog macc_gw5a.v +chparam -set X_WIDTH 27 -set Y_WIDTH 18 -set C_WIDTH 48 -set A_WIDTH 48 +hierarchy -top sub_top +proc +synth_gowin -family gw5a +cd sub_top +select -assert-none t:MULTALU27X18 + + +# Two products (a*b + a*b) -> _TECHMAP_FAIL_ (NPRODUCTS != 1) +design -reset +read_verilog macc_gw5a.v +chparam -set X_WIDTH 27 -set Y_WIDTH 18 -set C_WIDTH 48 -set A_WIDTH 48 +hierarchy -top twoproduct_top +proc +synth_gowin -family gw5a +cd twoproduct_top +select -assert-none t:MULTALU27X18 + + +# Unsigned operands -> _TECHMAP_FAIL_ (!A_SIGNED) +design -reset +read_verilog macc_gw5a.v +chparam -set X_WIDTH 27 -set Y_WIDTH 18 -set C_WIDTH 48 -set A_WIDTH 48 +hierarchy -top unsigned_top +proc +synth_gowin -family gw5a +cd unsigned_top +select -assert-none t:MULTALU27X18