From 5ad9e4cacc2d2f882571ecb37c3ea3ac4dd13886 Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Thu, 2 Oct 2025 23:13:08 +0300 Subject: [PATCH] gowin: dsp: Add basic DSP block inferencing for various MULT cells --- techlibs/gowin/Makefile.inc | 1 + techlibs/gowin/dsp_map.v | 65 +++++++++++++++++++++++++++++++++++ techlibs/gowin/synth_gowin.cc | 45 +++++++++++++++++++++++- tests/arch/gowin/mul_gw1n.ys | 47 +++++++++++++++++++++++++ tests/arch/gowin/mul_gw2a.ys | 47 +++++++++++++++++++++++++ 5 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 techlibs/gowin/dsp_map.v create mode 100644 tests/arch/gowin/mul_gw1n.ys create mode 100644 tests/arch/gowin/mul_gw2a.ys diff --git a/techlibs/gowin/Makefile.inc b/techlibs/gowin/Makefile.inc index 9ec7dce4d..4cb664b42 100644 --- a/techlibs/gowin/Makefile.inc +++ b/techlibs/gowin/Makefile.inc @@ -11,3 +11,4 @@ $(eval $(call add_share_file,share/gowin,techlibs/gowin/brams_map.v)) $(eval $(call add_share_file,share/gowin,techlibs/gowin/brams.txt)) $(eval $(call add_share_file,share/gowin,techlibs/gowin/lutrams_map.v)) $(eval $(call add_share_file,share/gowin,techlibs/gowin/lutrams.txt)) +$(eval $(call add_share_file,share/gowin,techlibs/gowin/dsp_map.v)) diff --git a/techlibs/gowin/dsp_map.v b/techlibs/gowin/dsp_map.v new file mode 100644 index 000000000..5f53813bc --- /dev/null +++ b/techlibs/gowin/dsp_map.v @@ -0,0 +1,65 @@ +module \$__MUL9X9 (input [8:0] A, input [8:0] B, output [17:0] Y); + parameter A_WIDTH = 9; + parameter B_WIDTH = 9; + parameter Y_WIDTH = 18; + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + + wire [8:0] soa; + wire [8:0] sob; + + MULT9X9 _TECHMAP_REPLACE_ ( + .A(A), + .B(B), + .SIA(8'b0), + .SIB(8'b0), + .ASIGN(A_SIGNED), + .BSIGN(B_SIGNED), + .ASEL(1'b0), + .BSEL(1'b0), + .SOA(soa), + .SOB(sob), + .DOUT(Y) + ); +endmodule + +module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y); + parameter A_WIDTH = 18; + parameter B_WIDTH = 18; + parameter Y_WIDTH = 36; + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + + wire [17:0] soa; + wire [17:0] sob; + + MULT18X18 _TECHMAP_REPLACE_ ( + .A(A), + .B(B), + .SIA(18'b0), + .SIB(18'b0), + .ASIGN(A_SIGNED), + .BSIGN(B_SIGNED), + .ASEL(1'b0), + .BSEL(1'b0), + .SOA(soa), + .SOB(sob), + .DOUT(Y) + ); +endmodule + +module \$__MUL36X36 (input [35:0] A, input [35:0] B, output [72:0] Y); + parameter A_WIDTH = 36; + parameter B_WIDTH = 36; + parameter Y_WIDTH = 72; + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + + MULT36X36 _TECHMAP_REPLACE_ ( + .A(A), + .B(B), + .ASIGN(A_SIGNED), + .BSIGN(B_SIGNED), + .DOUT(Y) + ); +endmodule diff --git a/techlibs/gowin/synth_gowin.cc b/techlibs/gowin/synth_gowin.cc index 8f3553df6..45677cb55 100644 --- a/techlibs/gowin/synth_gowin.cc +++ b/techlibs/gowin/synth_gowin.cc @@ -91,13 +91,16 @@ struct SynthGowinPass : public ScriptPass log(" The following families are supported:\n"); log(" 'gw1n', 'gw2a', 'gw5a'.\n"); log("\n"); + log(" -nodsp\n"); + log(" do not infer DSP multipliers.\n"); + log("\n"); log("The following commands are executed by this synthesis command:\n"); help_script(); log("\n"); } string top_opt, vout_file, json_file, family; - bool retime, nobram, nolutram, flatten, nodffe, nowidelut, abc9, noiopads, noalu, no_rw_check; + bool retime, nobram, nolutram, flatten, nodffe, nowidelut, abc9, noiopads, noalu, no_rw_check, nodsp; void clear_flags() override { @@ -115,6 +118,7 @@ struct SynthGowinPass : public ScriptPass noiopads = false; noalu = false; no_rw_check = false; + nodsp = false; } void execute(std::vector args, RTLIL::Design *design) override @@ -193,6 +197,10 @@ struct SynthGowinPass : public ScriptPass no_rw_check = true; continue; } + if (args[argidx] == "-nodsp") { + nodsp = true; + continue; + } break; } extra_args(args, argidx, design); @@ -208,6 +216,24 @@ struct SynthGowinPass : public ScriptPass log_pop(); } + // DSP mapping rules for mul2dsp + struct DSPRule { + int a_maxwidth; + int b_maxwidth; + int a_minwidth; + int b_minwidth; + std::string prim; + }; + + // gw1n and gw2a + const std::vector dsp_rules = { + {36, 36, 18, 18, "$__MUL36X36"}, + {18, 18, 10, 4, "$__MUL18X18"}, + {18, 18, 4, 10, "$__MUL18X18"}, + { 9, 9, 4, 4, "$__MUL9X9"}, + }; + // TODO: gw5a (MULT12X12, MULT27x36) + void script() override { std::string no_rw_check_opt = ""; @@ -233,6 +259,23 @@ struct SynthGowinPass : public ScriptPass if (check_label("coarse")) { + if (help_mode) + { + run("techmap -map +/mul2dsp.v [...]", "(unless -nodsp)"); + run("techmap -map +/gowin/dsp_map.v [...]", "(unless -nodsp)"); + } else if (!nodsp) { + if (family == "gw1n" || family == "gw2a") + { + for (const auto &rule : dsp_rules) + { + run(stringf("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=%d -D DSP_B_MAXWIDTH=%d -D DSP_A_MINWIDTH=%d -D DSP_B_MINWIDTH=%d -D DSP_NAME=%s", + rule.a_maxwidth, rule.b_maxwidth, rule.a_minwidth, rule.b_minwidth, rule.prim.c_str())); + run("chtype -set $mul t:$__soft_mul"); + } + run("techmap -map +/gowin/dsp_map.v"); + } + } + run("synth -run coarse" + no_rw_check_opt); } diff --git a/tests/arch/gowin/mul_gw1n.ys b/tests/arch/gowin/mul_gw1n.ys new file mode 100644 index 000000000..62ea6e6ba --- /dev/null +++ b/tests/arch/gowin/mul_gw1n.ys @@ -0,0 +1,47 @@ +read_verilog ../common/mul.v +chparam -set X_WIDTH 8 -set Y_WIDTH 8 -set A_WIDTH 16 +hierarchy -top top +proc +# equivalence checking is somewhat slow (and missing simulation models) +synth_gowin -family gw1n + +cd top # Constrain all select calls below inside the top module +select -assert-count 1 t:MULT9X9 +# XXX: Whats's `top/x_IBUF_I_O_MULT9X9_A_A_GND_G` ?? +# select -assert-none t:IBUF t:OBUF t:MULT9X9 %% t:* %D + +design -reset +read_verilog ../common/mul.v +chparam -set X_WIDTH 16 -set Y_WIDTH 16 -set A_WIDTH 32 +hierarchy -top top +proc +synth_gowin -family gw1n +cd top # Constrain all select calls below inside the top module +select -assert-count 1 t:MULT18X18 +# XXX: top/x_IBUF_I_O_MULT18X18_A_A_GND_G ??? +# select -assert-none t:IBUF t:OBUF t:MULT18X18 %% t:* %D + + +design -reset +read_verilog ../common/mul.v +chparam -set X_WIDTH 32 -set Y_WIDTH 32 -set A_WIDTH 64 +hierarchy -top top +proc +# equivalence checking is too slow here +synth_gowin +cd top # Constrain all select calls below inside the top module +select -assert-count 1 t:MULT36X36 +# XXX: top/x_IBUF_I_O_MULT36X36_A_A_GND_G +# select -assert-none t:IBUF t:OBUF t:MULT36X36 %% t:* %D + +# TODO: We end up with two 18x18 multipliers +# design -reset +# read_verilog ../common/mul.v +# chparam -set X_WIDTH 32 -set Y_WIDTH 16 -set A_WIDTH 48 +# hierarchy -top top +# proc +# # equivalence checking is too slow here +# synth_gowin +# cd top # Constrain all select calls below inside the top module +# select -assert-count 2 t:MULT18X18 +# select -assert-none t:IBUF t:OBUF t:MULT36X36 %% t:* %D diff --git a/tests/arch/gowin/mul_gw2a.ys b/tests/arch/gowin/mul_gw2a.ys new file mode 100644 index 000000000..8a1db8c6e --- /dev/null +++ b/tests/arch/gowin/mul_gw2a.ys @@ -0,0 +1,47 @@ +read_verilog ../common/mul.v +chparam -set X_WIDTH 8 -set Y_WIDTH 8 -set A_WIDTH 16 +hierarchy -top top +proc +# equivalence checking is somewhat slow (and missing simulation models) +synth_gowin -family gw2a + +cd top # Constrain all select calls below inside the top module +select -assert-count 1 t:MULT9X9 +# XXX: Whats's `top/x_IBUF_I_O_MULT9X9_A_A_GND_G` ?? +# select -assert-none t:IBUF t:OBUF t:MULT9X9 %% t:* %D + +design -reset +read_verilog ../common/mul.v +chparam -set X_WIDTH 16 -set Y_WIDTH 16 -set A_WIDTH 32 +hierarchy -top top +proc +synth_gowin -family gw2a +cd top # Constrain all select calls below inside the top module +select -assert-count 1 t:MULT18X18 +# XXX: top/x_IBUF_I_O_MULT18X18_A_A_GND_G ??? +# select -assert-none t:IBUF t:OBUF t:MULT18X18 %% t:* %D + + +design -reset +read_verilog ../common/mul.v +chparam -set X_WIDTH 32 -set Y_WIDTH 32 -set A_WIDTH 64 +hierarchy -top top +proc +# equivalence checking is too slow here +synth_gowin -family gw2a +cd top # Constrain all select calls below inside the top module +select -assert-count 1 t:MULT36X36 +# XXX: top/x_IBUF_I_O_MULT36X36_A_A_GND_G +# select -assert-none t:IBUF t:OBUF t:MULT36X36 %% t:* %D + +# TODO: We end up with two 18x18 multipliers +# design -reset +# read_verilog ../common/mul.v +# chparam -set X_WIDTH 32 -set Y_WIDTH 16 -set A_WIDTH 48 +# hierarchy -top top +# proc +# # equivalence checking is too slow here +# synth_gowin -family gw2a +# cd top # Constrain all select calls below inside the top module +# select -assert-count 2 t:MULT18X18 +# select -assert-none t:IBUF t:OBUF t:MULT36X36 %% t:* %D