mirror of
https://github.com/YosysHQ/yosys
synced 2026-08-02 12:23:25 +00:00
Merge pull request #5929 from YosysHQ/nella/fuse-2
Add register absorption for MULT* for Nexus (fix #5917).
This commit is contained in:
commit
4821ed17b4
7 changed files with 721 additions and 16 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<std::string> 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;
|
||||
|
|
|
|||
|
|
@ -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 <SigBit> clk
|
||||
state <SigSpec> argQ argD
|
||||
state <Cell*> ffA ffB ffY
|
||||
|
||||
udata <Cell*> dff
|
||||
udata <SigBit> dffclk
|
||||
udata <SigSpec> 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 <SigSpec> 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 <SigSpec> 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
|
||||
|
|
|
|||
45
tests/arch/nexus/dsp_equiv.sv
Normal file
45
tests/arch/nexus/dsp_equiv.sv
Normal file
|
|
@ -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
|
||||
154
tests/arch/nexus/dsp_equiv.ys
Normal file
154
tests/arch/nexus/dsp_equiv.ys
Normal file
|
|
@ -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
|
||||
156
tests/arch/nexus/pipe_mul.sv
Normal file
156
tests/arch/nexus/pipe_mul.sv
Normal file
|
|
@ -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
|
||||
74
tests/arch/nexus/pipe_mul.ys
Normal file
74
tests/arch/nexus/pipe_mul.ys
Normal file
|
|
@ -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*
|
||||
Loading…
Add table
Add a link
Reference in a new issue