diff --git a/techlibs/lattice/dsp_map_nexus.v b/techlibs/lattice/dsp_map_nexus.v index a2ee71a21..0489345cc 100644 --- a/techlibs/lattice/dsp_map_nexus.v +++ b/techlibs/lattice/dsp_map_nexus.v @@ -1,57 +1,84 @@ -module \$__NX_MUL36X36 (input [35:0] A, input [35:0] B, output [71:0] Y); +module \$__NX_MUL36X36 (input [35:0] A, input [35:0] B, input CLK, input CEA, RSTA, CEB, RSTB, CEOUT, RSTOUT, output [71:0] Y); parameter A_WIDTH = 36; parameter B_WIDTH = 36; parameter Y_WIDTH = 72; parameter A_SIGNED = 0; parameter B_SIGNED = 0; + parameter REGINPUTA = "BYPASS"; + parameter REGINPUTB = "BYPASS"; + parameter REGOUTPUT = "BYPASS"; + parameter RESETMODE = "SYNC"; MULT36X36 #( - .REGINPUTA("BYPASS"), - .REGINPUTB("BYPASS"), - .REGOUTPUT("BYPASS") + .REGINPUTA(REGINPUTA), + .REGINPUTB(REGINPUTB), + .REGOUTPUT(REGOUTPUT), + .RESETMODE(RESETMODE) ) _TECHMAP_REPLACE_ ( .A(A), .B(B), + .CLK(CLK), + .CEA(CEA), .RSTA(RSTA), + .CEB(CEB), .RSTB(RSTB), + .CEOUT(CEOUT), .RSTOUT(RSTOUT), .SIGNEDA(A_SIGNED ? 1'b1 : 1'b0), .SIGNEDB(B_SIGNED ? 1'b1 : 1'b0), .Z(Y) ); endmodule -module \$__NX_MUL36X18 (input [35:0] A, input [17:0] B, output [53:0] Y); +module \$__NX_MUL36X18 (input [35:0] A, input [17:0] B, input CLK, input CEA, RSTA, CEB, RSTB, CEOUT, RSTOUT, output [53:0] Y); parameter A_WIDTH = 36; parameter B_WIDTH = 18; parameter Y_WIDTH = 54; parameter A_SIGNED = 0; parameter B_SIGNED = 0; + parameter REGINPUTA = "BYPASS"; + parameter REGINPUTB = "BYPASS"; + parameter REGOUTPUT = "BYPASS"; + parameter RESETMODE = "SYNC"; MULT18X36 #( - .REGINPUTA("BYPASS"), - .REGINPUTB("BYPASS"), - .REGOUTPUT("BYPASS") + .REGINPUTA(REGINPUTB), + .REGINPUTB(REGINPUTA), + .REGOUTPUT(REGOUTPUT), + .RESETMODE(RESETMODE) ) _TECHMAP_REPLACE_ ( .A(B), .B(A), + .CLK(CLK), + .CEA(CEB), .RSTA(RSTB), + .CEB(CEA), .RSTB(RSTA), + .CEOUT(CEOUT), .RSTOUT(RSTOUT), .SIGNEDA(B_SIGNED ? 1'b1 : 1'b0), .SIGNEDB(A_SIGNED ? 1'b1 : 1'b0), .Z(Y) ); endmodule -module \$__NX_MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y); +module \$__NX_MUL18X18 (input [17:0] A, input [17:0] B, input CLK, input CEA, RSTA, CEB, RSTB, CEOUT, RSTOUT, output [35:0] Y); parameter A_WIDTH = 18; parameter B_WIDTH = 18; parameter Y_WIDTH = 36; parameter A_SIGNED = 0; parameter B_SIGNED = 0; + parameter REGINPUTA = "BYPASS"; + parameter REGINPUTB = "BYPASS"; + parameter REGOUTPUT = "BYPASS"; + parameter RESETMODE = "SYNC"; MULT18X18 #( - .REGINPUTA("BYPASS"), - .REGINPUTB("BYPASS"), - .REGOUTPUT("BYPASS") + .REGINPUTA(REGINPUTA), + .REGINPUTB(REGINPUTB), + .REGOUTPUT(REGOUTPUT), + .RESETMODE(RESETMODE) ) _TECHMAP_REPLACE_ ( .A(A), .B(B), + .CLK(CLK), + .CEA(CEA), .RSTA(RSTA), + .CEB(CEB), .RSTB(RSTB), + .CEOUT(CEOUT), .RSTOUT(RSTOUT), .SIGNEDA(A_SIGNED ? 1'b1 : 1'b0), .SIGNEDB(B_SIGNED ? 1'b1 : 1'b0), .Z(Y) @@ -92,6 +119,11 @@ module \$__NX_MAC18X18 (input [17:0] A, input [17:0] B, input [47:0] C, output [ .REGINPUTA("BYPASS"), .REGINPUTB("BYPASS"), .REGINPUTC("BYPASS"), + .REGADDSUB("BYPASS"), + .REGLOADC("BYPASS"), + .REGLOADC2("BYPASS"), + .REGCIN("BYPASS"), + .REGPIPELINE("BYPASS"), .REGOUTPUT("BYPASS") ) _TECHMAP_REPLACE_ ( .A(A), @@ -99,6 +131,8 @@ module \$__NX_MAC18X18 (input [17:0] A, input [17:0] B, input [47:0] C, output [ .C({6'b0, C}), .SIGNED(A_SIGNED ? 1'b1 : 1'b0), .ADDSUB(SUBTRACT ? 1'b1 : 1'b0), + .LOADC(1'b1), + .CIN(1'b0), .Z(Y) ); endmodule diff --git a/techlibs/lattice/lattice_dsp_nexus.cc b/techlibs/lattice/lattice_dsp_nexus.cc index 442182f22..8ce71276d 100644 --- a/techlibs/lattice/lattice_dsp_nexus.cc +++ b/techlibs/lattice/lattice_dsp_nexus.cc @@ -15,7 +15,9 @@ struct LatticeDspNexusPass : public Pass { log(" lattice_dsp_nexus [options] [selection]\n"); log("\n"); log("Infer Lattice Nexus sysDSP macrocells (MULTADDSUB18X18, MULTPREADD18X18,\n"); - log("MULTADDSUB9X9WIDE) from MAC and dot-product patterns.\n"); + log("MULTADDSUB9X9WIDE) from MAC and dot-product patterns, and absorb the\n"); + log("pipeline flip-flops around bare MULT18X18 / MULT36X36 multipliers into\n"); + log("the hardened DSP input and output registers.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override @@ -29,6 +31,7 @@ struct LatticeDspNexusPass : public Pass { pm.run_nexus_mac9_4lane(); pm.run_nexus_mac18(); pm.run_nexus_preadd18(); + pm.run_nexus_mul_reg(); } } } LatticeDspNexusPass; diff --git a/techlibs/lattice/lattice_dsp_nexus.pmg b/techlibs/lattice/lattice_dsp_nexus.pmg index 2ad38840b..59b0d961b 100644 --- a/techlibs/lattice/lattice_dsp_nexus.pmg +++ b/techlibs/lattice/lattice_dsp_nexus.pmg @@ -31,12 +31,23 @@ code reject; } + if (add->type == $sub && add_AB == \A) + reject; + Cell *mac = module->addCell(NEW_ID, "$__NX_MAC18X18"); IdString add_C = (add_AB == \A) ? \B : \A; - mac->setPort(\A, port(mul, \A)); - mac->setPort(\B, port(mul, \B)); - mac->setPort(\C, port(add, add_C)); + bool ab_signed = mul->getParam(\A_SIGNED).as_bool(); + SigSpec sigA = port(mul, \A); + SigSpec sigB = port(mul, \B); + SigSpec sigC = port(add, add_C); + sigA.extend_u0(18, ab_signed); + sigB.extend_u0(18, ab_signed); + sigC.extend_u0(48, ab_signed); + + mac->setPort(\A, sigA); + mac->setPort(\B, sigB); + mac->setPort(\C, sigC); mac->setPort(\Y, port(add, \Y)); mac->setParam(\A_SIGNED, mul->getParam(\A_SIGNED)); mac->setParam(\SUBTRACT, add->type == $sub ? State::S1 : State::S0); @@ -206,3 +217,231 @@ code accept; endcode + +pattern nexus_mul_reg + +state clk +state argQ argD +state ffA ffB ffY + +udata dff +udata dffclk +udata dffD dffQ + +match mul + select mul->type.in($mul) + select GetSize(port(mul, \A)) <= 36 + select GetSize(port(mul, \B)) <= 36 + select GetSize(port(mul, \Y)) <= 72 +endmatch + +// Absorb the pipeline FF on each mul input +code argQ ffA clk + argQ = port(mul, \A); + subpattern(in_ff); + if (dff) { + ffA = dff; + clk = dffclk; + } +endcode + +code argQ ffB clk + argQ = port(mul, \B); + subpattern(in_ff); + if (dff) { + ffB = dff; + clk = dffclk; + } +endcode + +// Absorb the FF on the mul output +code argD ffY clk + argD = port(mul, \Y); + subpattern(out_ff); + if (dff) { + ffY = dff; + clk = dffclk; + } +endcode + +code + if (!ffA && !ffB && !ffY) + reject; + + { + int aw = GetSize(port(mul, \A)); + int bw = GetSize(port(mul, \B)); + IdString prim; + int A_PINS; + int B_PINS; + + if (aw <= 18 && bw <= 18) { + prim = "$__NX_MUL18X18"; + A_PINS = 18; + B_PINS = 18; + } else if (aw <= 36 && bw <= 18) { + prim = "$__NX_MUL36X18"; + A_PINS = 36; + B_PINS = 18; + } else if (aw <= 18 && bw <= 36) { + prim = "$__NX_MUL36X18"; + A_PINS = 36; + B_PINS = 18; + } else { + prim = "$__NX_MUL36X36"; + A_PINS = 36; + B_PINS = 36; + } + + bool a_signed = mul->getParam(\A_SIGNED).as_bool(); + bool b_signed = mul->getParam(\B_SIGNED).as_bool(); + // Drive the DSP from the FF's D + SigSpec sigA = ffA ? port(ffA, \D) : port(mul, \A); + SigSpec sigB = ffB ? port(ffB, \D) : port(mul, \B); + SigSpec sigY = ffY ? port(ffY, \Q) : port(mul, \Y); + + // 36X18 -> wide operand goes to A + Cell *ffa = ffA; + Cell *ffb = ffB; + if (prim == "$__NX_MUL36X18" && aw <= 18 && bw <= 36) { + std::swap(sigA, sigB); + std::swap(a_signed, b_signed); + std::swap(ffa, ffb); + } + + sigA.extend_u0(A_PINS, a_signed); + sigB.extend_u0(B_PINS, b_signed); + + Cell *cell = module->addCell(NEW_ID, prim); + cell->setPort(\A, sigA); + cell->setPort(\B, sigB); + cell->setPort(\Y, sigY); + cell->setParam(\A_WIDTH, A_PINS); + cell->setParam(\B_WIDTH, B_PINS); + cell->setParam(\Y_WIDTH, GetSize(sigY)); + cell->setParam(\A_SIGNED, a_signed ? State::S1 : State::S0); + cell->setParam(\B_SIGNED, b_signed ? State::S1 : State::S0); + cell->setParam(\RESETMODE, std::string("SYNC")); + + auto ce_of = [&](Cell *ff) -> SigBit { + if (!ff) return State::S0; + + if (ff->type.in($dffe, $sdffe)) { + SigBit e = port(ff, \EN)[0]; + if (!ff->getParam(\EN_POLARITY).as_bool()) + e = module->NotGate(NEW_ID, e); + + return e; + } + + return State::S1; // Always enabled + }; + + auto rst_of = [&](Cell *ff) -> SigBit { + if (!ff) return State::S0; + + if (ff->type.in($sdff, $sdffe)) { + SigBit r = port(ff, \SRST)[0]; + if (!ff->getParam(\SRST_POLARITY).as_bool()) + r = module->NotGate(NEW_ID, r); + + return r; + } + + return State::S0; // No reset + }; + + cell->setPort(\CLK, clk != SigBit() ? clk : State::S0); + cell->setPort(\CEA, ce_of(ffa)); + cell->setPort(\RSTA, rst_of(ffa)); + cell->setPort(\CEB, ce_of(ffb)); + cell->setPort(\RSTB, rst_of(ffb)); + cell->setPort(\CEOUT, ce_of(ffY)); + cell->setPort(\RSTOUT, rst_of(ffY)); + cell->setParam(\REGINPUTA, ffa ? std::string("REGISTER") : std::string("BYPASS")); + cell->setParam(\REGINPUTB, ffb ? std::string("REGISTER") : std::string("BYPASS")); + cell->setParam(\REGOUTPUT, ffY ? std::string("REGISTER") : std::string("BYPASS")); + + if (ffa) autoremove(ffa); + if (ffb && ffb != ffa) autoremove(ffb); + if (ffY) autoremove(ffY); + autoremove(mul); + + accept; + } +endcode + +// Find the input pipeline FF whose Q feeds argQ +subpattern in_ff +arg argQ clk + +code + dff = nullptr; +endcode + +match ff + select ff->type.in($dff, $dffe, $sdff, $sdffe) + select param(ff, \CLK_POLARITY).as_bool() + filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero() + filter GetSize(port(ff, \Q)) == GetSize(argQ) + index port(ff, \Q) === argQ +endmatch + +code + if (clk != SigBit() && sigmap(port(ff, \CLK)[0]) != clk) + reject; + + for (auto bit : sigmap(port(ff, \Q))) { + if (!bit.wire) + continue; + auto it = bit.wire->attributes.find(\init); + if (it == bit.wire->attributes.end()) + continue; + if (bit.offset < GetSize(it->second)) { + State s = it->second[bit.offset]; + if (s != State::Sx && s != State::S0) + reject; + } + } + + dff = ff; + dffD = port(ff, \D); + dffclk = sigmap(port(ff, \CLK)[0]); +endcode + +subpattern out_ff +arg argD clk + +code + dff = nullptr; +endcode + +match ff + select ff->type.in($dff, $dffe, $sdff, $sdffe) + select param(ff, \CLK_POLARITY).as_bool() + filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero() + filter GetSize(port(ff, \D)) == GetSize(argD) + index port(ff, \D) === argD +endmatch + +code + if (clk != SigBit() && sigmap(port(ff, \CLK)[0]) != clk) + reject; + + for (auto bit : sigmap(port(ff, \Q))) { + if (!bit.wire) + continue; + auto it = bit.wire->attributes.find(\init); + if (it == bit.wire->attributes.end()) + continue; + if (bit.offset < GetSize(it->second)) { + State s = it->second[bit.offset]; + if (s != State::Sx && s != State::S0) + reject; + } + } + + dff = ff; + dffQ = port(ff, \Q); + dffclk = sigmap(port(ff, \CLK)[0]); +endcode diff --git a/tests/arch/nexus/dsp_equiv.sv b/tests/arch/nexus/dsp_equiv.sv new file mode 100644 index 000000000..88eb8bb21 --- /dev/null +++ b/tests/arch/nexus/dsp_equiv.sv @@ -0,0 +1,45 @@ +// https://github.com/YosysHQ/yosys/issues/5906 +module mac_add_u (input [8:0] a, b, input [17:0] c, output [17:0] y); + assign y = a*b + c; +endmodule + +module mac_add_s (input signed [8:0] a, b, input signed [17:0] c, output signed [17:0] y); + assign y = a*b + c; +endmodule + +module mac_sub_u (input [8:0] a, b, input [17:0] c, output [17:0] y); + assign y = c - a*b; +endmodule + +module mac_sub_s (input signed [8:0] a, b, input signed [17:0] c, output signed [17:0] y); + assign y = c - a*b; +endmodule + +module mac_subrev_u (input [8:0] a, b, input [17:0] c, output [17:0] y); + assign y = a*b - c; +endmodule + +module mac_wide_s (input signed [8:0] a, b, input signed [17:0] c, output signed [29:0] y); + assign y = c - a*b; +endmodule + +// https://github.com/YosysHQ/yosys/issues/5929 +module mul_pipe_u (input clk, input [8:0] a, b, output reg [17:0] y); + reg [8:0] a_r; + reg [8:0] b_r; + always @(posedge clk) begin + a_r <= a; + b_r <= b; + y <= a_r * b_r; + end +endmodule + +module mul_pipe_s (input clk, input signed [8:0] a, b, output reg signed [17:0] y); + reg signed [8:0] a_r; + reg signed [8:0] b_r; + always @(posedge clk) begin + a_r <= a; + b_r <= b; + y <= a_r * b_r; + end +endmodule diff --git a/tests/arch/nexus/dsp_equiv.ys b/tests/arch/nexus/dsp_equiv.ys new file mode 100644 index 000000000..a79ae2cf4 --- /dev/null +++ b/tests/arch/nexus/dsp_equiv.ys @@ -0,0 +1,154 @@ +read_verilog -sv dsp_equiv.sv +design -save src + +# y = a*b + c (unsigned) +design -load src +hierarchy -top mac_add_u +proc +design -stash gold +design -load src +hierarchy -top mac_add_u +synth_nexus -family lifcl -noiopad +select -assert-count 1 t:MULTADDSUB18X18 +read_verilog +/lattice/cells_sim_nexus.v +hierarchy -top mac_add_u +flatten +proc +design -stash gate +design -copy-from gold -as gold mac_add_u +design -copy-from gate -as gate mac_add_u +miter -equiv -make_assert -flatten gold gate equiv +sat -seq 1 -set-init-zero -prove-asserts -verify equiv + +# y = a*b + c (signed) +design -load src +hierarchy -top mac_add_s +proc +design -stash gold +design -load src +hierarchy -top mac_add_s +synth_nexus -family lifcl -noiopad +select -assert-count 1 t:MULTADDSUB18X18 +read_verilog +/lattice/cells_sim_nexus.v +hierarchy -top mac_add_s +flatten +proc +design -stash gate +design -copy-from gold -as gold mac_add_s +design -copy-from gate -as gate mac_add_s +miter -equiv -make_assert -flatten gold gate equiv +sat -seq 1 -set-init-zero -prove-asserts -verify equiv + +# y = c - a*b (unsigned) +design -load src +hierarchy -top mac_sub_u +proc +design -stash gold +design -load src +hierarchy -top mac_sub_u +synth_nexus -family lifcl -noiopad +select -assert-count 1 t:MULTADDSUB18X18 +read_verilog +/lattice/cells_sim_nexus.v +hierarchy -top mac_sub_u +flatten +proc +design -stash gate +design -copy-from gold -as gold mac_sub_u +design -copy-from gate -as gate mac_sub_u +miter -equiv -make_assert -flatten gold gate equiv +sat -seq 1 -set-init-zero -prove-asserts -verify equiv + +# y = c - a*b (signed) +design -load src +hierarchy -top mac_sub_s +proc +design -stash gold +design -load src +hierarchy -top mac_sub_s +synth_nexus -family lifcl -noiopad +select -assert-count 1 t:MULTADDSUB18X18 +read_verilog +/lattice/cells_sim_nexus.v +hierarchy -top mac_sub_s +flatten +proc +design -stash gate +design -copy-from gold -as gold mac_sub_s +design -copy-from gate -as gate mac_sub_s +miter -equiv -make_assert -flatten gold gate equiv +sat -seq 1 -set-init-zero -prove-asserts -verify equiv + +# y = c - a*b (signed, wide out) +design -load src +hierarchy -top mac_wide_s +proc +design -stash gold +design -load src +hierarchy -top mac_wide_s +synth_nexus -family lifcl -noiopad +select -assert-count 1 t:MULTADDSUB18X18 +read_verilog +/lattice/cells_sim_nexus.v +hierarchy -top mac_wide_s +flatten +proc +design -stash gate +design -copy-from gold -as gold mac_wide_s +design -copy-from gate -as gate mac_wide_s +miter -equiv -make_assert -flatten gold gate equiv +sat -seq 1 -set-init-zero -prove-asserts -verify equiv + +# y = a*b - c (product is the minuend, cannot map to C - A*B, must not fuse) +design -load src +hierarchy -top mac_subrev_u +proc +design -stash gold +design -load src +hierarchy -top mac_subrev_u +synth_nexus -family lifcl -noiopad +select -assert-count 0 t:MULTADDSUB18X18 +read_verilog +/lattice/cells_sim_nexus.v +hierarchy -top mac_subrev_u +flatten +proc +design -stash gate +design -copy-from gold -as gold mac_subrev_u +design -copy-from gate -as gate mac_subrev_u +miter -equiv -make_assert -flatten gold gate equiv +sat -seq 1 -set-init-zero -prove-asserts -verify equiv + +# pipelined mul (unsigned) +design -load src +hierarchy -top mul_pipe_u +proc +design -stash gold +design -load src +hierarchy -top mul_pipe_u +synth_nexus -family lifcl -noiopad +select -assert-count 1 t:MULT18X18 r:REGINPUTA=REGISTER r:REGINPUTB=REGISTER r:REGOUTPUT=REGISTER +read_verilog +/lattice/cells_sim_nexus.v +hierarchy -top mul_pipe_u +flatten +proc +design -stash gate +design -copy-from gold -as gold mul_pipe_u +design -copy-from gate -as gate mul_pipe_u +miter -equiv -make_assert -flatten gold gate equiv +sat -seq 6 -set-init-zero -prove-asserts -verify equiv + +# pipelined mul (signed) +design -load src +hierarchy -top mul_pipe_s +proc +design -stash gold +design -load src +hierarchy -top mul_pipe_s +synth_nexus -family lifcl -noiopad +select -assert-count 1 t:MULT18X18 r:REGINPUTA=REGISTER r:REGINPUTB=REGISTER r:REGOUTPUT=REGISTER +read_verilog +/lattice/cells_sim_nexus.v +hierarchy -top mul_pipe_s +flatten +proc +design -stash gate +design -copy-from gold -as gold mul_pipe_s +design -copy-from gate -as gate mul_pipe_s +miter -equiv -make_assert -flatten gold gate equiv +sat -seq 6 -set-init-zero -prove-asserts -verify equiv diff --git a/tests/arch/nexus/pipe_mul.sv b/tests/arch/nexus/pipe_mul.sv new file mode 100644 index 000000000..75769a29c --- /dev/null +++ b/tests/arch/nexus/pipe_mul.sv @@ -0,0 +1,156 @@ +// https://github.com/YosysHQ/yosys/issues/5917 + +module mul18_pipe( + input logic clk, + input logic [17:0] a, + input logic [17:0] b, + output logic [35:0] y +); + logic [17:0] a_r; + logic [17:0] b_r; + + always_ff @(posedge clk) begin + a_r <= a; + b_r <= b; + y <= a_r * b_r; + end +endmodule + +module mul18_pipe_signed ( + input logic clk, + input logic signed [17:0] a, + input logic signed [17:0] b, + output logic signed [35:0] y +); + logic signed [17:0] a_r; + logic signed [17:0] b_r; + + always_ff @(posedge clk) begin + a_r <= a; + b_r <= b; + y <= a_r * b_r; + end +endmodule + + +module mul18_pipe_in_only ( + input logic clk, + input logic [17:0] a, + input logic [17:0] b, + output logic [35:0] y +); + logic [17:0] a_r; + logic [17:0] b_r; + + always_ff @(posedge clk) begin + a_r <= a; + b_r <= b; + end + + assign y = a_r * b_r; +endmodule + +module mul18_pipe_out_only ( + input logic clk, + input logic [17:0] a, + input logic [17:0] b, + output logic [35:0] y +); + always_ff @(posedge clk) + y <= a * b; +endmodule + +module mul18_pipe_io_rst ( + input logic clk, + input logic rst, + input logic [17:0] a, + input logic [17:0] b, + output logic [35:0] y +); + logic [17:0] a_r; + logic [17:0] b_r; + + always_ff @(posedge clk) + if (rst) begin a_r <= 0; b_r <= 0; y <= 0; end + else begin a_r <= a; b_r <= b; y <= a_r * b_r; end +endmodule + +module mul24_io ( + input logic clk, + input logic [23:0] a, + input logic [23:0] b, + output logic [47:0] y +); + logic [23:0] a_r; + logic [23:0] b_r; + + always_ff @(posedge clk) begin + a_r <= a; + b_r <= b; + y <= a_r * b_r; + end +endmodule + +module mul32_io ( + input logic clk, + input logic [31:0] a, + input logic [31:0] b, + output logic [63:0] y +); + logic [31:0] a_r; + logic [31:0] b_r; + + always_ff @(posedge clk) begin + a_r <= a; + b_r <= b; + y <= a_r * b_r; + end +endmodule + +module mul18_negedge ( + input logic clk, + input logic [17:0] a, + input logic [17:0] b, + output logic [35:0] y +); + logic [17:0] a_r; + logic [17:0] b_r; + + always_ff @(negedge clk) begin + a_r <= a; + b_r <= b; + y <= a_r * b_r; + end +endmodule + +module mul18_rst_nonzero ( + input logic clk, + input logic rst, + input logic [17:0] a, + input logic [17:0] b, + output logic [35:0] y +); + logic [17:0] a_r; + logic [17:0] b_r; + + always_ff @(posedge clk) + if (rst) begin a_r <= 18'h3; b_r <= 18'h7; end + else begin a_r <= a; b_r <= b; end + always_ff @(posedge clk) + y <= a_r * b_r; +endmodule + +module mul18_two_clock ( + input logic clk0, + input logic clk1, + input logic [17:0] a, + input logic [17:0] b, + output logic [35:0] y +); + logic [17:0] a_r; + logic [17:0] b_r; + + always_ff @(posedge clk0) a_r <= a; + always_ff @(posedge clk1) b_r <= b; + assign y = a_r * b_r; +endmodule \ No newline at end of file diff --git a/tests/arch/nexus/pipe_mul.ys b/tests/arch/nexus/pipe_mul.ys new file mode 100644 index 000000000..c5e40b96e --- /dev/null +++ b/tests/arch/nexus/pipe_mul.ys @@ -0,0 +1,74 @@ +read_verilog -sv pipe_mul.sv + +design -save pristine + +# 18x18 MULT +design -load pristine +hierarchy -top mul18_pipe +synth_nexus -family lifcl -top mul18_pipe +select -assert-count 1 t:MULT18X18 r:REGINPUTA=REGISTER r:REGINPUTB=REGISTER r:REGOUTPUT=REGISTER +select -assert-count 0 t:FD1P3* + +# 18x18 MULT (signed) +design -load pristine +hierarchy -top mul18_pipe_signed +synth_nexus -family lifcl -top mul18_pipe_signed +select -assert-count 1 t:MULT18X18 r:REGINPUTA=REGISTER r:REGINPUTB=REGISTER r:REGOUTPUT=REGISTER +select -assert-count 0 t:FD1P3* + +# 18x18 MULT (input only) +design -load pristine +hierarchy -top mul18_pipe_in_only +synth_nexus -family lifcl -top mul18_pipe_in_only +select -assert-count 1 t:MULT18X18 r:REGINPUTA=REGISTER r:REGINPUTB=REGISTER r:REGOUTPUT=BYPASS +select -assert-count 0 t:FD1P3* + +# 18x18 MULT (output only) +design -load pristine +hierarchy -top mul18_pipe_out_only +synth_nexus -family lifcl -top mul18_pipe_out_only +select -assert-count 1 t:MULT18X18 r:REGINPUTA=BYPASS r:REGINPUTB=BYPASS r:REGOUTPUT=REGISTER +select -assert-count 0 t:FD1P3* + +# 18x18 MULT (reset) +design -load pristine +hierarchy -top mul18_pipe_io_rst +synth_nexus -family lifcl -top mul18_pipe_io_rst +select -assert-count 1 t:MULT18X18 r:REGINPUTA=REGISTER r:REGINPUTB=REGISTER r:REGOUTPUT=REGISTER +select -assert-count 0 t:FD1P3* + +# 24x24 MUL -> pipelined 36X36 MULT +design -load pristine +hierarchy -top mul24_io +synth_nexus -family lifcl -top mul24_io +select -assert-count 1 t:MULT36X36 r:REGINPUTA=REGISTER r:REGINPUTB=REGISTER r:REGOUTPUT=REGISTER +select -assert-count 0 t:FD1P3* + +# 32x32 MUL -> pipelined 36X36 MULT +design -load pristine +hierarchy -top mul32_io +synth_nexus -family lifcl -top mul32_io +select -assert-count 1 t:MULT36X36 r:REGINPUTA=REGISTER r:REGINPUTB=REGISTER r:REGOUTPUT=REGISTER +select -assert-count 0 t:FD1P3* + +# reject +# DSP reg is rising-edge +design -load pristine +hierarchy -top mul18_negedge +synth_nexus -family lifcl -top mul18_negedge +select -assert-count 1 t:MULT18X18 r:REGINPUTA=BYPASS r:REGINPUTB=BYPASS r:REGOUTPUT=BYPASS +select -assert-min 1 t:FD1P3* + +# DSP reg only resets to 0 +design -load pristine +hierarchy -top mul18_rst_nonzero +synth_nexus -family lifcl -top mul18_rst_nonzero +select -assert-count 1 t:MULT18X18 r:REGINPUTA=BYPASS r:REGINPUTB=BYPASS r:REGOUTPUT=BYPASS +select -assert-min 1 t:FD1P3* + +# two clocks feeding input regs -> can't share one CLK pin +design -load pristine +hierarchy -top mul18_two_clock +synth_nexus -family lifcl -top mul18_two_clock +select -assert-count 1 t:MULT18X18 r:REGINPUTA=BYPASS r:REGINPUTB=BYPASS +select -assert-min 1 t:FD1P3* \ No newline at end of file