3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-23 09:05:32 +00:00

Merge remote-tracking branch 'origin/master' into xaig_dff

This commit is contained in:
Eddie Hung 2019-09-29 19:39:12 -07:00
commit 1123c09588
45 changed files with 6255 additions and 294 deletions

View file

@ -1 +1 @@
/*_pm.h
/*_pm.h

View file

@ -21,6 +21,14 @@ $(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h))
# --------------------------------------
OBJS += passes/pmgen/xilinx_dsp.o
passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h))
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_CREG_pm.h))
$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h))
# --------------------------------------
OBJS += passes/pmgen/peepopt.o
passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h
$(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h))

View file

@ -29,19 +29,19 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
{
auto &st = pm.st_ice40_dsp;
#if 0
log("\n");
log("ffA: %s\n", log_id(st.ffA, "--"));
log("ffB: %s\n", log_id(st.ffB, "--"));
log("mul: %s\n", log_id(st.mul, "--"));
log("ffY: %s\n", log_id(st.ffY, "--"));
log("addAB: %s\n", log_id(st.addAB, "--"));
log("muxAB: %s\n", log_id(st.muxAB, "--"));
log("ffS: %s\n", log_id(st.ffS, "--"));
#endif
log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul));
log_debug("ffA: %s %s %s\n", log_id(st.ffA, "--"), log_id(st.ffAholdmux, "--"), log_id(st.ffArstmux, "--"));
log_debug("ffB: %s %s %s\n", log_id(st.ffB, "--"), log_id(st.ffBholdmux, "--"), log_id(st.ffBrstmux, "--"));
log_debug("ffCD: %s %s\n", log_id(st.ffCD, "--"), log_id(st.ffCDholdmux, "--"));
log_debug("mul: %s\n", log_id(st.mul, "--"));
log_debug("ffFJKG: %s\n", log_id(st.ffFJKG, "--"));
log_debug("ffH: %s\n", log_id(st.ffH, "--"));
log_debug("add: %s\n", log_id(st.add, "--"));
log_debug("mux: %s\n", log_id(st.mux, "--"));
log_debug("ffO: %s %s %s\n", log_id(st.ffO, "--"), log_id(st.ffOholdmux, "--"), log_id(st.ffOrstmux, "--"));
log_debug("\n");
if (GetSize(st.sigA) > 16) {
log(" input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA));
return;
@ -52,59 +52,85 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
return;
}
if (GetSize(st.sigS) > 32) {
log(" accumulator (%s) is too large (%d > 32).\n", log_signal(st.sigS), GetSize(st.sigS));
if (GetSize(st.sigO) > 33) {
log(" adder/accumulator (%s) is too large (%d > 33).\n", log_signal(st.sigO), GetSize(st.sigO));
return;
}
if (GetSize(st.sigY) > 32) {
log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigY), GetSize(st.sigY));
if (GetSize(st.sigH) > 32) {
log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigH), GetSize(st.sigH));
return;
}
bool mul_signed = st.mul->getParam("\\A_SIGNED").as_bool();
Cell *cell = st.mul;
if (cell->type == ID($mul)) {
log(" replacing %s with SB_MAC16 cell.\n", log_id(st.mul->type));
log(" replacing $mul with SB_MAC16 cell.\n");
Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
pm.module->swap_names(cell, st.mul);
cell = pm.module->addCell(NEW_ID, ID(SB_MAC16));
pm.module->swap_names(cell, st.mul);
}
else log_assert(cell->type == ID(SB_MAC16));
// SB_MAC16 Input Interface
SigSpec A = st.sigA;
A.extend_u0(16, mul_signed);
A.extend_u0(16, st.mul->getParam(ID(A_SIGNED)).as_bool());
log_assert(GetSize(A) == 16);
SigSpec B = st.sigB;
B.extend_u0(16, mul_signed);
B.extend_u0(16, st.mul->getParam(ID(B_SIGNED)).as_bool());
log_assert(GetSize(B) == 16);
SigSpec CD;
if (st.muxA)
CD = st.muxA->getPort("\\B");
if (st.muxB)
CD = st.muxB->getPort("\\A");
CD.extend_u0(32, mul_signed);
SigSpec CD = st.sigCD;
if (CD.empty())
CD = RTLIL::Const(0, 32);
else
log_assert(GetSize(CD) == 32);
cell->setPort("\\A", A);
cell->setPort("\\B", B);
cell->setPort("\\C", CD.extract(0, 16));
cell->setPort("\\D", CD.extract(16, 16));
cell->setPort(ID::A, A);
cell->setPort(ID::B, B);
cell->setPort(ID(C), CD.extract(16, 16));
cell->setPort(ID(D), CD.extract(0, 16));
cell->setParam("\\A_REG", st.ffA ? State::S1 : State::S0);
cell->setParam("\\B_REG", st.ffB ? State::S1 : State::S0);
cell->setParam(ID(A_REG), st.ffA ? State::S1 : State::S0);
cell->setParam(ID(B_REG), st.ffB ? State::S1 : State::S0);
cell->setParam(ID(C_REG), st.ffCD ? State::S1 : State::S0);
cell->setParam(ID(D_REG), st.ffCD ? State::S1 : State::S0);
cell->setPort("\\AHOLD", State::S0);
cell->setPort("\\BHOLD", State::S0);
cell->setPort("\\CHOLD", State::S0);
cell->setPort("\\DHOLD", State::S0);
SigSpec AHOLD, BHOLD, CDHOLD;
if (st.ffAholdmux)
AHOLD = st.ffAholdpol ? st.ffAholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffAholdmux->getPort(ID(S)));
else
AHOLD = State::S0;
if (st.ffBholdmux)
BHOLD = st.ffBholdpol ? st.ffBholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffBholdmux->getPort(ID(S)));
else
BHOLD = State::S0;
if (st.ffCDholdmux)
CDHOLD = st.ffCDholdpol ? st.ffCDholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffCDholdmux->getPort(ID(S)));
else
CDHOLD = State::S0;
cell->setPort(ID(AHOLD), AHOLD);
cell->setPort(ID(BHOLD), BHOLD);
cell->setPort(ID(CHOLD), CDHOLD);
cell->setPort(ID(DHOLD), CDHOLD);
cell->setPort("\\IRSTTOP", State::S0);
cell->setPort("\\IRSTBOT", State::S0);
SigSpec IRSTTOP, IRSTBOT;
if (st.ffArstmux)
IRSTTOP = st.ffArstpol ? st.ffArstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffArstmux->getPort(ID(S)));
else
IRSTTOP = State::S0;
if (st.ffBrstmux)
IRSTBOT = st.ffBrstpol ? st.ffBrstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffBrstmux->getPort(ID(S)));
else
IRSTBOT = State::S0;
cell->setPort(ID(IRSTTOP), IRSTTOP);
cell->setPort(ID(IRSTBOT), IRSTBOT);
if (st.clock_vld)
if (st.clock != SigBit())
{
cell->setPort("\\CLK", st.clock);
cell->setPort("\\CE", State::S1);
cell->setParam("\\NEG_TRIGGER", st.clock_pol ? State::S0 : State::S1);
cell->setPort(ID(CLK), st.clock);
cell->setPort(ID(CE), State::S1);
cell->setParam(ID(NEG_TRIGGER), st.clock_pol ? State::S0 : State::S1);
log(" clock: %s (%s)", log_signal(st.clock), st.clock_pol ? "posedge" : "negedge");
@ -114,91 +140,137 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
if (st.ffB)
log(" ffB:%s", log_id(st.ffB));
if (st.ffY)
log(" ffY:%s", log_id(st.ffY));
if (st.ffCD)
log(" ffCD:%s", log_id(st.ffCD));
if (st.ffS)
log(" ffS:%s", log_id(st.ffS));
if (st.ffFJKG)
log(" ffFJKG:%s", log_id(st.ffFJKG));
if (st.ffH)
log(" ffH:%s", log_id(st.ffH));
if (st.ffO)
log(" ffO:%s", log_id(st.ffO));
log("\n");
}
else
{
cell->setPort("\\CLK", State::S0);
cell->setPort("\\CE", State::S0);
cell->setParam("\\NEG_TRIGGER", State::S0);
cell->setPort(ID(CLK), State::S0);
cell->setPort(ID(CE), State::S0);
cell->setParam(ID(NEG_TRIGGER), State::S0);
}
// SB_MAC16 Cascade Interface
cell->setPort("\\SIGNEXTIN", State::Sx);
cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID));
cell->setPort(ID(SIGNEXTIN), State::Sx);
cell->setPort(ID(SIGNEXTOUT), pm.module->addWire(NEW_ID));
cell->setPort("\\CI", State::Sx);
cell->setPort("\\CO", pm.module->addWire(NEW_ID));
cell->setPort(ID(CI), State::Sx);
cell->setPort("\\ACCUMCI", State::Sx);
cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID));
cell->setPort(ID(ACCUMCI), State::Sx);
cell->setPort(ID(ACCUMCO), pm.module->addWire(NEW_ID));
// SB_MAC16 Output Interface
SigSpec O = st.ffS ? st.sigS : st.sigY;
SigSpec O = st.sigO;
int O_width = GetSize(O);
if (O_width == 33) {
log_assert(st.add);
// If we have a signed multiply-add, then perform sign extension
if (st.add->getParam(ID(A_SIGNED)).as_bool() && st.add->getParam(ID(B_SIGNED)).as_bool())
pm.module->connect(O[32], O[31]);
else
cell->setPort(ID(CO), O[32]);
O.remove(O_width-1);
}
else
cell->setPort(ID(CO), pm.module->addWire(NEW_ID));
log_assert(GetSize(O) <= 32);
if (GetSize(O) < 32)
O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
cell->setPort("\\O", O);
cell->setPort(ID(O), O);
if (st.addAB) {
log(" accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type));
cell->setPort("\\ADDSUBTOP", st.addAB->type == "$add" ? State::S0 : State::S1);
cell->setPort("\\ADDSUBBOT", st.addAB->type == "$add" ? State::S0 : State::S1);
bool accum = false;
if (st.add) {
accum = (st.ffO && st.add->getPort(st.addAB == ID::A ? ID::B : ID::A) == st.sigO);
if (accum)
log(" accumulator %s (%s)\n", log_id(st.add), log_id(st.add->type));
else
log(" adder %s (%s)\n", log_id(st.add), log_id(st.add->type));
cell->setPort(ID(ADDSUBTOP), st.add->type == ID($add) ? State::S0 : State::S1);
cell->setPort(ID(ADDSUBBOT), st.add->type == ID($add) ? State::S0 : State::S1);
} else {
cell->setPort("\\ADDSUBTOP", State::S0);
cell->setPort("\\ADDSUBBOT", State::S0);
cell->setPort(ID(ADDSUBTOP), State::S0);
cell->setPort(ID(ADDSUBBOT), State::S0);
}
cell->setPort("\\ORSTTOP", State::S0);
cell->setPort("\\ORSTBOT", State::S0);
SigSpec OHOLD;
if (st.ffOholdmux)
OHOLD = st.ffOholdpol ? st.ffOholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffOholdmux->getPort(ID(S)));
else
OHOLD = State::S0;
cell->setPort(ID(OHOLDTOP), OHOLD);
cell->setPort(ID(OHOLDBOT), OHOLD);
cell->setPort("\\OHOLDTOP", State::S0);
cell->setPort("\\OHOLDBOT", State::S0);
SigSpec ORST;
if (st.ffOrstmux)
ORST = st.ffOrstpol ? st.ffOrstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffOrstmux->getPort(ID(S)));
else
ORST = State::S0;
cell->setPort(ID(ORSTTOP), ORST);
cell->setPort(ID(ORSTBOT), ORST);
SigSpec acc_reset = State::S0;
if (st.muxA)
acc_reset = st.muxA->getPort("\\S");
if (st.muxB)
acc_reset = pm.module->Not(NEW_ID, st.muxB->getPort("\\S"));
cell->setPort("\\OLOADTOP", acc_reset);
cell->setPort("\\OLOADBOT", acc_reset);
if (st.mux) {
if (st.muxAB == ID::A)
acc_reset = st.mux->getPort(ID(S));
else
acc_reset = pm.module->Not(NEW_ID, st.mux->getPort(ID(S)));
}
cell->setPort(ID(OLOADTOP), acc_reset);
cell->setPort(ID(OLOADBOT), acc_reset);
// SB_MAC16 Remaining Parameters
cell->setParam("\\C_REG", State::S0);
cell->setParam("\\D_REG", State::S0);
cell->setParam(ID(TOP_8x8_MULT_REG), st.ffFJKG ? State::S1 : State::S0);
cell->setParam(ID(BOT_8x8_MULT_REG), st.ffFJKG ? State::S1 : State::S0);
cell->setParam(ID(PIPELINE_16x16_MULT_REG1), st.ffFJKG ? State::S1 : State::S0);
cell->setParam(ID(PIPELINE_16x16_MULT_REG2), st.ffH ? State::S1 : State::S0);
cell->setParam("\\TOP_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
cell->setParam("\\BOT_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
cell->setParam("\\PIPELINE_16x16_MULT_REG1", st.ffY ? State::S1 : State::S0);
cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
cell->setParam(ID(TOPADDSUB_LOWERINPUT), Const(2, 2));
cell->setParam(ID(TOPADDSUB_UPPERINPUT), accum ? State::S0 : State::S1);
cell->setParam(ID(TOPADDSUB_CARRYSELECT), Const(3, 2));
cell->setParam("\\TOPOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
cell->setParam(ID(BOTADDSUB_LOWERINPUT), Const(2, 2));
cell->setParam(ID(BOTADDSUB_UPPERINPUT), accum ? State::S0 : State::S1);
cell->setParam(ID(BOTADDSUB_CARRYSELECT), Const(0, 2));
cell->setParam("\\BOTOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
cell->setParam(ID(MODE_8x8), State::S0);
cell->setParam(ID(A_SIGNED), st.mul->getParam(ID(A_SIGNED)).as_bool());
cell->setParam(ID(B_SIGNED), st.mul->getParam(ID(B_SIGNED)).as_bool());
cell->setParam("\\MODE_8x8", State::S0);
cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
if (st.ffO) {
if (st.o_lo)
cell->setParam(ID(TOPOUTPUT_SELECT), Const(st.add ? 0 : 3, 2));
else
cell->setParam(ID(TOPOUTPUT_SELECT), Const(1, 2));
pm.autoremove(st.mul);
pm.autoremove(st.ffY);
pm.autoremove(st.ffS);
st.ffO->connections_.at(ID(Q)).replace(O, pm.module->addWire(NEW_ID, GetSize(O)));
cell->setParam(ID(BOTOUTPUT_SELECT), Const(1, 2));
}
else {
cell->setParam(ID(TOPOUTPUT_SELECT), Const(st.add ? 0 : 3, 2));
cell->setParam(ID(BOTOUTPUT_SELECT), Const(st.add ? 0 : 3, 2));
}
if (cell != st.mul)
pm.autoremove(st.mul);
else
pm.blacklist(st.mul);
pm.autoremove(st.ffFJKG);
pm.autoremove(st.add);
}
struct Ice40DspPass : public Pass {
@ -209,7 +281,17 @@ struct Ice40DspPass : public Pass {
log("\n");
log(" ice40_dsp [options] [selection]\n");
log("\n");
log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n");
log("Map multipliers ($mul/SB_MAC16) and multiply-accumulate ($mul/SB_MAC16 + $add)\n");
log("cells into iCE40 DSP resources.\n");
log("Currently, only the 16x16 multiply mode is supported and not the 2 x 8x8 mode.\n");
log("\n");
log("Pack input registers (A, B, {C,D}; with optional hold), pipeline registers\n");
log("({F,J,K,G}, H), output registers (O -- full 32-bits or lower 16-bits only; with\n");
log("optional hold), and post-adder into into the SB_MAC16 resource.\n");
log("\n");
log("Multiply-accumulate operations using the post-adder with feedback on the {C,D}\n");
log("input will be folded into the DSP. In this scenario only, resetting the\n");
log("the accumulator to an arbitrary value can be inferred to use the {C,D} input.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE

View file

@ -1,163 +1,574 @@
pattern ice40_dsp
state <SigBit> clock
state <bool> clock_pol clock_vld
state <SigSpec> sigA sigB sigY sigS
state <Cell*> addAB muxAB
state <bool> clock_pol cd_signed o_lo
state <SigSpec> sigA sigB sigCD sigH sigO
state <Cell*> add mux
state <IdString> addAB muxAB
state <bool> ffAholdpol ffBholdpol ffCDholdpol ffOholdpol
state <bool> ffArstpol ffBrstpol ffCDrstpol ffOrstpol
state <Cell*> ffA ffAholdmux ffArstmux ffB ffBholdmux ffBrstmux ffCD ffCDholdmux
state <Cell*> ffFJKG ffH ffO ffOholdmux ffOrstmux
// subpattern
state <SigSpec> argQ argD
state <bool> ffholdpol ffrstpol
state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
udata <Cell*> dff dffholdmux dffrstmux
udata <bool> dffholdpol dffrstpol dffclock_pol
match mul
select mul->type.in($mul)
select mul->type.in($mul, \SB_MAC16)
select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
select GetSize(mul->getPort(\Y)) > 10
endmatch
match ffA
select ffA->type.in($dff)
// select nusers(port(ffA, \Q)) == 2
index <SigSpec> port(ffA, \Q) === port(mul, \A)
optional
endmatch
code sigA sigB sigH
auto unextend = [](const SigSpec &sig) {
int i;
for (i = GetSize(sig)-1; i > 0; i--)
if (sig[i] != sig[i-1])
break;
// Do not remove non-const sign bit
if (sig[i].wire)
++i;
return sig.extract(0, i);
};
sigA = unextend(port(mul, \A));
sigB = unextend(port(mul, \B));
code sigA clock clock_pol clock_vld
sigA = port(mul, \A);
SigSpec O;
if (mul->type == $mul)
O = mul->getPort(\Y);
else if (mul->type == \SB_MAC16)
O = mul->getPort(\O);
else log_abort();
if (GetSize(O) <= 10)
reject;
if (ffA) {
sigA = port(ffA, \D);
// Only care about those bits that are used
int i;
for (i = 0; i < GetSize(O); i++) {
if (nusers(O[i]) <= 1)
break;
sigH.append(O[i]);
}
log_assert(nusers(O.extract_end(i)) <= 1);
endcode
clock = port(ffA, \CLK).as_bit();
clock_pol = param(ffA, \CLK_POLARITY).as_bool();
clock_vld = true;
code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) {
argQ = sigA;
subpattern(in_dffe);
if (dff) {
ffA = dff;
clock = dffclock;
clock_pol = dffclock_pol;
if (dffrstmux) {
ffArstmux = dffrstmux;
ffArstpol = dffrstpol;
}
if (dffholdmux) {
ffAholdmux = dffholdmux;
ffAholdpol = dffholdpol;
}
sigA = dffD;
}
}
endcode
match ffB
select ffB->type.in($dff)
// select nusers(port(ffB, \Q)) == 2
index <SigSpec> port(ffB, \Q) === port(mul, \B)
optional
endmatch
code sigB clock clock_pol clock_vld
sigB = port(mul, \B);
if (ffB) {
sigB = port(ffB, \D);
SigBit c = port(ffB, \CLK).as_bit();
bool cp = param(ffB, \CLK_POLARITY).as_bool();
if (clock_vld && (c != clock || cp != clock_pol))
reject;
clock = c;
clock_pol = cp;
clock_vld = true;
code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) {
argQ = sigB;
subpattern(in_dffe);
if (dff) {
ffB = dff;
clock = dffclock;
clock_pol = dffclock_pol;
if (dffrstmux) {
ffBrstmux = dffrstmux;
ffBrstpol = dffrstpol;
}
if (dffholdmux) {
ffBholdmux = dffholdmux;
ffBholdpol = dffholdpol;
}
sigB = dffD;
}
}
endcode
match ffY
select ffY->type.in($dff)
select nusers(port(ffY, \D)) == 2
index <SigSpec> port(ffY, \D) === port(mul, \Y)
optional
endmatch
code argD ffFJKG sigH clock clock_pol
if (nusers(sigH) == 2 &&
(mul->type != \SB_MAC16 ||
(!param(mul, \TOP_8x8_MULT_REG).as_bool() && !param(mul, \BOT_8x8_MULT_REG).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool()))) {
argD = sigH;
subpattern(out_dffe);
if (dff) {
// F/J/K/G do not have a CE-like (hold) input
if (dffholdmux)
goto reject_ffFJKG;
code sigY clock clock_pol clock_vld
sigY = port(mul, \Y);
// Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT)
// shared with A and B
if ((ffArstmux != NULL) != (dffrstmux != NULL))
goto reject_ffFJKG;
if ((ffBrstmux != NULL) != (dffrstmux != NULL))
goto reject_ffFJKG;
if (ffArstmux) {
if (port(ffArstmux, \S) != port(dffrstmux, \S))
goto reject_ffFJKG;
if (ffArstpol != dffrstpol)
goto reject_ffFJKG;
}
if (ffBrstmux) {
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
goto reject_ffFJKG;
if (ffBrstpol != dffrstpol)
goto reject_ffFJKG;
}
if (ffY) {
sigY = port(ffY, \Q);
SigBit c = port(ffY, \CLK).as_bit();
bool cp = param(ffY, \CLK_POLARITY).as_bool();
ffFJKG = dff;
clock = dffclock;
clock_pol = dffclock_pol;
sigH = dffQ;
if (clock_vld && (c != clock || cp != clock_pol))
reject;
clock = c;
clock_pol = cp;
clock_vld = true;
reject_ffFJKG: ;
}
}
endcode
match addA
select addA->type.in($add)
select nusers(port(addA, \A)) == 2
index <SigSpec> port(addA, \A) === sigY
code argD ffH sigH sigO clock clock_pol
if (ffFJKG && nusers(sigH) == 2 &&
(mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) {
argD = sigH;
subpattern(out_dffe);
if (dff) {
// H does not have a CE-like (hold) input
if (dffholdmux)
goto reject_ffH;
// Reset signal of H (IRSTBOT) shared with B
if ((ffBrstmux != NULL) != (dffrstmux != NULL))
goto reject_ffH;
if (ffBrstmux) {
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
goto reject_ffH;
if (ffBrstpol != dffrstpol)
goto reject_ffH;
}
ffH = dff;
clock = dffclock;
clock_pol = dffclock_pol;
sigH = dffQ;
reject_ffH: ;
}
}
sigO = sigH;
endcode
match add
if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT).as_int() == 3)
select add->type.in($add)
choice <IdString> AB {\A, \B}
select nusers(port(add, AB)) == 2
index <SigBit> port(add, AB)[0] === sigH[0]
filter GetSize(port(add, AB)) <= GetSize(sigH)
filter port(add, AB) == sigH.extract(0, GetSize(port(add, AB)))
filter nusers(sigH.extract_end(GetSize(port(add, AB)))) <= 1
set addAB AB
optional
endmatch
match addB
if !addA
select addB->type.in($add, $sub)
select nusers(port(addB, \B)) == 2
index <SigSpec> port(addB, \B) === sigY
optional
endmatch
code sigCD sigO cd_signed
if (add) {
sigCD = port(add, addAB == \A ? \B : \A);
cd_signed = param(add, addAB == \A ? \B_SIGNED : \A_SIGNED).as_bool();
code addAB sigS
if (addA) {
addAB = addA;
sigS = port(addA, \B);
}
if (addB) {
addAB = addB;
sigS = port(addB, \A);
}
if (addAB) {
int natural_mul_width = GetSize(sigA) + GetSize(sigB);
int actual_mul_width = GetSize(sigY);
int actual_acc_width = GetSize(sigS);
int actual_mul_width = GetSize(sigH);
int actual_acc_width = GetSize(sigCD);
if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
reject;
if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool()))
// If accumulator, check adder width and signedness
if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(add, \A_SIGNED).as_bool()))
reject;
sigO = port(add, \Y);
}
endcode
match muxA
if addAB
select muxA->type.in($mux)
select nusers(port(muxA, \A)) == 2
index <SigSpec> port(muxA, \A) === port(addAB, \Y)
match mux
select mux->type == $mux
choice <IdString> AB {\A, \B}
select nusers(port(mux, AB)) == 2
index <SigSpec> port(mux, AB) === sigO
set muxAB AB
optional
endmatch
match muxB
if addAB
if !muxA
select muxB->type.in($mux)
select nusers(port(muxB, \B)) == 2
index <SigSpec> port(muxB, \B) === port(addAB, \Y)
optional
endmatch
code muxAB
muxAB = addAB;
if (muxA)
muxAB = muxA;
if (muxB)
muxAB = muxB;
code sigO
if (mux)
sigO = port(mux, \Y);
endcode
match ffS
if muxAB
select ffS->type.in($dff)
select nusers(port(ffS, \D)) == 2
index <SigSpec> port(ffS, \D) === port(muxAB, \Y)
index <SigSpec> port(ffS, \Q) === sigS
endmatch
code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_pol cd_signed o_lo
if (mul->type != \SB_MAC16 ||
// Ensure that register is not already used
((param(mul, \TOPOUTPUT_SELECT, 0).as_int() != 1 && param(mul, \BOTOUTPUT_SELECT, 0).as_int() != 1) &&
// Ensure that OLOADTOP/OLOADBOT is unused or zero
(port(mul, \OLOADTOP, State::S0).is_fully_zero() && port(mul, \OLOADBOT, State::S0).is_fully_zero()))) {
code clock clock_pol clock_vld
if (ffS) {
SigBit c = port(ffS, \CLK).as_bit();
bool cp = param(ffS, \CLK_POLARITY).as_bool();
dff = nullptr;
if (clock_vld && (c != clock || cp != clock_pol))
reject;
// First try entire sigO
if (nusers(sigO) == 2) {
argD = sigO;
subpattern(out_dffe);
}
clock = c;
clock_pol = cp;
clock_vld = true;
// Otherwise try just its least significant 16 bits
if (!dff && GetSize(sigO) > 16) {
argD = sigO.extract(0, 16);
if (nusers(argD) == 2) {
subpattern(out_dffe);
o_lo = dff;
}
}
if (dff) {
ffO = dff;
clock = dffclock;
clock_pol = dffclock_pol;
if (dffrstmux) {
ffOrstmux = dffrstmux;
ffOrstpol = dffrstpol;
}
if (dffholdmux) {
ffOholdmux = dffholdmux;
ffOholdpol = dffholdpol;
}
sigO.replace(sigO.extract(0, GetSize(dffQ)), dffQ);
}
// Loading value into output register is not
// supported unless using accumulator
if (mux) {
if (sigCD != sigO)
reject;
sigCD = port(mux, muxAB == \B ? \A : \B);
cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool();
}
}
endcode
code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol
if (!sigCD.empty() && sigCD != sigO &&
(mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) {
argQ = sigCD;
subpattern(in_dffe);
if (dff) {
if (dffholdmux) {
ffCDholdmux = dffholdmux;
ffCDholdpol = dffholdpol;
}
// Reset signal of C (IRSTTOP) and D (IRSTBOT)
// shared with A and B
if ((ffArstmux != NULL) != (dffrstmux != NULL))
goto reject_ffCD;
if ((ffBrstmux != NULL) != (dffrstmux != NULL))
goto reject_ffCD;
if (ffArstmux) {
if (port(ffArstmux, \S) != port(dffrstmux, \S))
goto reject_ffCD;
if (ffArstpol != dffrstpol)
goto reject_ffCD;
}
if (ffBrstmux) {
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
goto reject_ffCD;
if (ffBrstpol != dffrstpol)
goto reject_ffCD;
}
ffCD = dff;
clock = dffclock;
clock_pol = dffclock_pol;
sigCD = dffD;
reject_ffCD: ;
}
}
endcode
code sigCD
sigCD.extend_u0(32, cd_signed);
endcode
code
accept;
endcode
// #######################
subpattern in_dffe
arg argD argQ clock clock_pol
code
dff = nullptr;
for (auto c : argQ.chunks()) {
if (!c.wire)
reject;
if (c.wire->get_bool_attribute(\keep))
reject;
Const init = c.wire->attributes.at(\init, State::Sx);
if (!init.is_fully_undef() && !init.is_fully_zero())
reject;
}
endcode
match ff
select ff->type.in($dff)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
// Check that the rest of argQ is present
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
set ffoffset offset
endmatch
code argQ argD
{
if (clock != SigBit()) {
if (port(ff, \CLK) != clock)
reject;
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
reject;
}
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
dffD = argQ;
argD = port(ff, \D);
argQ = Q;
dffD.replace(argQ, argD);
// Only search for ffrstmux if dffD only
// has two (ff, ffrstmux) users
if (nusers(dffD) > 2)
argD = SigSpec();
}
endcode
match ffrstmux
if false /* TODO: ice40 resets are actually async */
if !argD.empty()
select ffrstmux->type.in($mux)
index <SigSpec> port(ffrstmux, \Y) === argD
choice <IdString> BA {\B, \A}
// DSP48E1 only supports reset to zero
select port(ffrstmux, BA).is_fully_zero()
define <bool> pol (BA == \B)
set ffrstpol pol
semioptional
endmatch
code argD
if (ffrstmux) {
dffrstmux = ffrstmux;
dffrstpol = ffrstpol;
argD = port(ffrstmux, ffrstpol ? \A : \B);
dffD.replace(port(ffrstmux, \Y), argD);
// Only search for ffholdmux if argQ has at
// least 3 users (ff, <upstream>, ffrstmux) and
// dffD only has two (ff, ffrstmux)
if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
argD = SigSpec();
}
else
dffrstmux = nullptr;
endcode
match ffholdmux
if !argD.empty()
select ffholdmux->type.in($mux)
index <SigSpec> port(ffholdmux, \Y) === argD
choice <IdString> BA {\B, \A}
index <SigSpec> port(ffholdmux, BA) === argQ
define <bool> pol (BA == \B)
set ffholdpol pol
semioptional
endmatch
code argD
if (ffholdmux) {
dffholdmux = ffholdmux;
dffholdpol = ffholdpol;
argD = port(ffholdmux, ffholdpol ? \A : \B);
dffD.replace(port(ffholdmux, \Y), argD);
}
else
dffholdmux = nullptr;
endcode
// #######################
subpattern out_dffe
arg argD argQ clock clock_pol
code
dff = nullptr;
for (auto c : argD.chunks())
if (c.wire->get_bool_attribute(\keep))
reject;
endcode
match ffholdmux
select ffholdmux->type.in($mux)
// ffholdmux output must have two users: ffholdmux and ff.D
select nusers(port(ffholdmux, \Y)) == 2
choice <IdString> BA {\B, \A}
// keep-last-value net must have at least three users: ffholdmux, ff, downstream sink(s)
select nusers(port(ffholdmux, BA)) >= 3
slice offset GetSize(port(ffholdmux, \Y))
define <IdString> AB (BA == \B ? \A : \B)
index <SigBit> port(ffholdmux, AB)[offset] === argD[0]
// Check that the rest of argD is present
filter GetSize(port(ffholdmux, AB)) >= offset + GetSize(argD)
filter port(ffholdmux, AB).extract(offset, GetSize(argD)) == argD
set ffoffset offset
define <bool> pol (BA == \B)
set ffholdpol pol
semioptional
endmatch
code argD argQ
dffholdmux = ffholdmux;
if (ffholdmux) {
SigSpec AB = port(ffholdmux, ffholdpol ? \A : \B);
SigSpec Y = port(ffholdmux, \Y);
argQ = argD;
argD.replace(AB, Y);
argQ.replace(AB, port(ffholdmux, ffholdpol ? \B : \A));
dffholdmux = ffholdmux;
dffholdpol = ffholdpol;
}
endcode
match ffrstmux
if false /* TODO: ice40 resets are actually async */
select ffrstmux->type.in($mux)
// ffrstmux output must have two users: ffrstmux and ff.D
select nusers(port(ffrstmux, \Y)) == 2
choice <IdString> BA {\B, \A}
// DSP48E1 only supports reset to zero
select port(ffrstmux, BA).is_fully_zero()
slice offset GetSize(port(ffrstmux, \Y))
define <IdString> AB (BA == \B ? \A : \B)
index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
// Check that offset is consistent
filter !ffholdmux || ffoffset == offset
// Check that the rest of argD is present
filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
set ffoffset offset
define <bool> pol (AB == \A)
set ffrstpol pol
semioptional
endmatch
code argD argQ
dffrstmux = ffrstmux;
if (ffrstmux) {
SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
SigSpec Y = port(ffrstmux, \Y);
argD.replace(AB, Y);
dffrstmux = ffrstmux;
dffrstpol = ffrstpol;
}
endcode
match ff
select ff->type.in($dff)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \D)[offset] === argD[0]
// Check that offset is consistent
filter (!ffholdmux && !ffrstmux) || ffoffset == offset
// Check that the rest of argD is present
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
// Check that FF.Q is connected to CE-mux
filter !ffholdmux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
set ffoffset offset
endmatch
code argQ
if (ff) {
if (clock != SigBit()) {
if (port(ff, \CLK) != clock)
reject;
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
reject;
}
SigSpec D = port(ff, \D);
SigSpec Q = port(ff, \Q);
if (!ffholdmux) {
argQ = argD;
argQ.replace(D, Q);
}
for (auto c : argQ.chunks()) {
Const init = c.wire->attributes.at(\init, State::Sx);
if (!init.is_fully_undef() && !init.is_fully_zero())
reject;
}
dff = ff;
dffQ = argQ;
dffclock = port(ff, \CLK);
dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
}
// No enable/reset mux possible without flop
else if (dffholdmux || dffrstmux)
reject;
endcode

View file

@ -286,7 +286,7 @@ def process_pmgfile(f, filename):
block["gencode"].append(rewrite_cpp(l.rstrip()))
break
assert False
raise RuntimeError("'%s' statement not recognised on line %d" % (a[0], linenr))
if block["optional"]:
assert not block["semioptional"]
@ -305,7 +305,8 @@ def process_pmgfile(f, filename):
block["states"] = set()
for s in line.split()[1:]:
assert s in state_types[current_pattern]
if s not in state_types[current_pattern]:
raise RuntimeError("'%s' not in state_types" % s)
block["states"].add(s)
codetype = "code"
@ -327,7 +328,7 @@ def process_pmgfile(f, filename):
blocks.append(block)
continue
assert False
raise RuntimeError("'%s' command not recognised" % cmd)
for fn in pmgfiles:
with open(fn, "r") as f:
@ -452,11 +453,19 @@ with open(outfile, "w") as f:
print(" return sigmap(cell->getPort(portname));", file=f)
print(" }", file=f)
print("", file=f)
print(" SigSpec port(Cell *cell, IdString portname, const SigSpec& defval) {", file=f)
print(" return sigmap(cell->connections_.at(portname, defval));", file=f)
print(" }", file=f)
print("", file=f)
print(" Const param(Cell *cell, IdString paramname) {", file=f)
print(" return cell->getParam(paramname);", file=f)
print(" }", file=f)
print("", file=f)
print(" Const param(Cell *cell, IdString paramname, const Const& defval) {", file=f)
print(" return cell->parameters.at(paramname, defval);", file=f)
print(" }", file=f)
print("", file=f)
print(" int nusers(const SigSpec &sig) {", file=f)
print(" pool<Cell*> users;", file=f)

637
passes/pmgen/xilinx_dsp.cc Normal file
View file

@ -0,0 +1,637 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* 2019 Eddie Hung <eddie@fpgeh.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
#include "passes/pmgen/xilinx_dsp_pm.h"
#include "passes/pmgen/xilinx_dsp_CREG_pm.h"
#include "passes/pmgen/xilinx_dsp_cascade_pm.h"
static Cell* addDsp(Module *module) {
Cell *cell = module->addCell(NEW_ID, ID(DSP48E1));
cell->setParam(ID(ACASCREG), 0);
cell->setParam(ID(ADREG), 0);
cell->setParam(ID(A_INPUT), Const("DIRECT"));
cell->setParam(ID(ALUMODEREG), 0);
cell->setParam(ID(AREG), 0);
cell->setParam(ID(BCASCREG), 0);
cell->setParam(ID(B_INPUT), Const("DIRECT"));
cell->setParam(ID(BREG), 0);
cell->setParam(ID(CARRYINREG), 0);
cell->setParam(ID(CARRYINSELREG), 0);
cell->setParam(ID(CREG), 0);
cell->setParam(ID(DREG), 0);
cell->setParam(ID(INMODEREG), 0);
cell->setParam(ID(MREG), 0);
cell->setParam(ID(OPMODEREG), 0);
cell->setParam(ID(PREG), 0);
cell->setParam(ID(USE_MULT), Const("NONE"));
cell->setParam(ID(USE_SIMD), Const("ONE48"));
cell->setParam(ID(USE_DPORT), Const("FALSE"));
cell->setPort(ID(D), Const(0, 25));
cell->setPort(ID(INMODE), Const(0, 5));
cell->setPort(ID(ALUMODE), Const(0, 4));
cell->setPort(ID(OPMODE), Const(0, 7));
cell->setPort(ID(CARRYINSEL), Const(0, 3));
cell->setPort(ID(ACIN), Const(0, 30));
cell->setPort(ID(BCIN), Const(0, 18));
cell->setPort(ID(PCIN), Const(0, 48));
cell->setPort(ID(CARRYIN), Const(0, 1));
return cell;
}
void xilinx_simd_pack(Module *module, const std::vector<Cell*> &selected_cells)
{
std::deque<Cell*> simd12_add, simd12_sub;
std::deque<Cell*> simd24_add, simd24_sub;
for (auto cell : selected_cells) {
if (!cell->type.in(ID($add), ID($sub)))
continue;
SigSpec Y = cell->getPort(ID(Y));
if (!Y.is_chunk())
continue;
if (!Y.as_chunk().wire->get_strpool_attribute(ID(use_dsp)).count("simd"))
continue;
if (GetSize(Y) > 25)
continue;
SigSpec A = cell->getPort(ID(A));
SigSpec B = cell->getPort(ID(B));
if (GetSize(Y) <= 13) {
if (GetSize(A) > 12)
continue;
if (GetSize(B) > 12)
continue;
if (cell->type == ID($add))
simd12_add.push_back(cell);
else if (cell->type == ID($sub))
simd12_sub.push_back(cell);
}
else if (GetSize(Y) <= 25) {
if (GetSize(A) > 24)
continue;
if (GetSize(B) > 24)
continue;
if (cell->type == ID($add))
simd24_add.push_back(cell);
else if (cell->type == ID($sub))
simd24_sub.push_back(cell);
}
else
log_abort();
}
auto f12 = [module](SigSpec &AB, SigSpec &C, SigSpec &P, SigSpec &CARRYOUT, Cell *lane) {
SigSpec A = lane->getPort(ID(A));
SigSpec B = lane->getPort(ID(B));
SigSpec Y = lane->getPort(ID(Y));
A.extend_u0(12, lane->getParam(ID(A_SIGNED)).as_bool());
B.extend_u0(12, lane->getParam(ID(B_SIGNED)).as_bool());
AB.append(A);
C.append(B);
if (GetSize(Y) < 13)
Y.append(module->addWire(NEW_ID, 13-GetSize(Y)));
else
log_assert(GetSize(Y) == 13);
P.append(Y.extract(0, 12));
CARRYOUT.append(Y[12]);
};
auto g12 = [&f12,module](std::deque<Cell*> &simd12) {
while (simd12.size() > 1) {
SigSpec AB, C, P, CARRYOUT;
Cell *lane1 = simd12.front();
simd12.pop_front();
Cell *lane2 = simd12.front();
simd12.pop_front();
Cell *lane3 = nullptr;
Cell *lane4 = nullptr;
if (!simd12.empty()) {
lane3 = simd12.front();
simd12.pop_front();
if (!simd12.empty()) {
lane4 = simd12.front();
simd12.pop_front();
}
}
log("Analysing %s.%s for Xilinx DSP SIMD12 packing.\n", log_id(module), log_id(lane1));
Cell *cell = addDsp(module);
cell->setParam(ID(USE_SIMD), Const("FOUR12"));
// X = A:B
// Y = 0
// Z = C
cell->setPort(ID(OPMODE), Const::from_string("0110011"));
log_assert(lane1);
log_assert(lane2);
f12(AB, C, P, CARRYOUT, lane1);
f12(AB, C, P, CARRYOUT, lane2);
if (lane3) {
f12(AB, C, P, CARRYOUT, lane3);
if (lane4)
f12(AB, C, P, CARRYOUT, lane4);
else {
AB.append(Const(0, 12));
C.append(Const(0, 12));
P.append(module->addWire(NEW_ID, 12));
CARRYOUT.append(module->addWire(NEW_ID, 1));
}
}
else {
AB.append(Const(0, 24));
C.append(Const(0, 24));
P.append(module->addWire(NEW_ID, 24));
CARRYOUT.append(module->addWire(NEW_ID, 2));
}
log_assert(GetSize(AB) == 48);
log_assert(GetSize(C) == 48);
log_assert(GetSize(P) == 48);
log_assert(GetSize(CARRYOUT) == 4);
cell->setPort(ID(A), AB.extract(18, 30));
cell->setPort(ID(B), AB.extract(0, 18));
cell->setPort(ID(C), C);
cell->setPort(ID(P), P);
cell->setPort(ID(CARRYOUT), CARRYOUT);
if (lane1->type == ID($sub))
cell->setPort(ID(ALUMODE), Const::from_string("0011"));
module->remove(lane1);
module->remove(lane2);
if (lane3) module->remove(lane3);
if (lane4) module->remove(lane4);
module->design->select(module, cell);
}
};
g12(simd12_add);
g12(simd12_sub);
auto f24 = [module](SigSpec &AB, SigSpec &C, SigSpec &P, SigSpec &CARRYOUT, Cell *lane) {
SigSpec A = lane->getPort(ID(A));
SigSpec B = lane->getPort(ID(B));
SigSpec Y = lane->getPort(ID(Y));
A.extend_u0(24, lane->getParam(ID(A_SIGNED)).as_bool());
B.extend_u0(24, lane->getParam(ID(B_SIGNED)).as_bool());
C.append(A);
AB.append(B);
if (GetSize(Y) < 25)
Y.append(module->addWire(NEW_ID, 25-GetSize(Y)));
else
log_assert(GetSize(Y) == 25);
P.append(Y.extract(0, 24));
CARRYOUT.append(module->addWire(NEW_ID)); // TWO24 uses every other bit
CARRYOUT.append(Y[24]);
};
auto g24 = [&f24,module](std::deque<Cell*> &simd24) {
while (simd24.size() > 1) {
SigSpec AB;
SigSpec C;
SigSpec P;
SigSpec CARRYOUT;
Cell *lane1 = simd24.front();
simd24.pop_front();
Cell *lane2 = simd24.front();
simd24.pop_front();
log("Analysing %s.%s for Xilinx DSP SIMD24 packing.\n", log_id(module), log_id(lane1));
Cell *cell = addDsp(module);
cell->setParam(ID(USE_SIMD), Const("TWO24"));
// X = A:B
// Y = 0
// Z = C
cell->setPort(ID(OPMODE), Const::from_string("0110011"));
log_assert(lane1);
log_assert(lane2);
f24(AB, C, P, CARRYOUT, lane1);
f24(AB, C, P, CARRYOUT, lane2);
log_assert(GetSize(AB) == 48);
log_assert(GetSize(C) == 48);
log_assert(GetSize(P) == 48);
log_assert(GetSize(CARRYOUT) == 4);
cell->setPort(ID(A), AB.extract(18, 30));
cell->setPort(ID(B), AB.extract(0, 18));
cell->setPort(ID(C), C);
cell->setPort(ID(P), P);
cell->setPort(ID(CARRYOUT), CARRYOUT);
if (lane1->type == ID($sub))
cell->setPort(ID(ALUMODE), Const::from_string("0011"));
module->remove(lane1);
module->remove(lane2);
module->design->select(module, cell);
}
};
g24(simd24_add);
g24(simd24_sub);
}
void xilinx_dsp_pack(xilinx_dsp_pm &pm)
{
auto &st = pm.st_xilinx_dsp_pack;
log("Analysing %s.%s for Xilinx DSP packing.\n", log_id(pm.module), log_id(st.dsp));
log_debug("preAdd: %s\n", log_id(st.preAdd, "--"));
log_debug("ffAD: %s %s %s\n", log_id(st.ffAD, "--"), log_id(st.ffADcemux, "--"), log_id(st.ffADrstmux, "--"));
log_debug("ffA2: %s %s %s\n", log_id(st.ffA2, "--"), log_id(st.ffA2cemux, "--"), log_id(st.ffA2rstmux, "--"));
log_debug("ffA1: %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--"));
log_debug("ffB2: %s %s %s\n", log_id(st.ffB2, "--"), log_id(st.ffB2cemux, "--"), log_id(st.ffB2rstmux, "--"));
log_debug("ffB1: %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--"));
log_debug("ffD: %s %s %s\n", log_id(st.ffD, "--"), log_id(st.ffDcemux, "--"), log_id(st.ffDrstmux, "--"));
log_debug("dsp: %s\n", log_id(st.dsp, "--"));
log_debug("ffM: %s %s %s\n", log_id(st.ffM, "--"), log_id(st.ffMcemux, "--"), log_id(st.ffMrstmux, "--"));
log_debug("postAdd: %s\n", log_id(st.postAdd, "--"));
log_debug("postAddMux: %s\n", log_id(st.postAddMux, "--"));
log_debug("ffP: %s %s %s\n", log_id(st.ffP, "--"), log_id(st.ffPcemux, "--"), log_id(st.ffPrstmux, "--"));
log_debug("overflow: %s\n", log_id(st.overflow, "--"));
Cell *cell = st.dsp;
if (st.preAdd) {
log(" preadder %s (%s)\n", log_id(st.preAdd), log_id(st.preAdd->type));
bool A_SIGNED = st.preAdd->getParam(ID(A_SIGNED)).as_bool();
bool D_SIGNED = st.preAdd->getParam(ID(B_SIGNED)).as_bool();
if (st.sigA == st.preAdd->getPort(ID(B)))
std::swap(A_SIGNED, D_SIGNED);
st.sigA.extend_u0(30, A_SIGNED);
st.sigD.extend_u0(25, D_SIGNED);
cell->setPort(ID(A), st.sigA);
cell->setPort(ID(D), st.sigD);
cell->setPort(ID(INMODE), Const::from_string("00100"));
if (st.ffAD) {
if (st.ffADcemux) {
SigSpec S = st.ffADcemux->getPort(ID(S));
cell->setPort(ID(CEAD), st.ffADcepol ? S : pm.module->Not(NEW_ID, S));
}
else
cell->setPort(ID(CEAD), State::S1);
cell->setParam(ID(ADREG), 1);
}
cell->setParam(ID(USE_DPORT), Const("TRUE"));
pm.autoremove(st.preAdd);
}
if (st.postAdd) {
log(" postadder %s (%s)\n", log_id(st.postAdd), log_id(st.postAdd->type));
SigSpec &opmode = cell->connections_.at(ID(OPMODE));
if (st.postAddMux) {
log_assert(st.ffP);
opmode[4] = st.postAddMux->getPort(ID(S));
pm.autoremove(st.postAddMux);
}
else if (st.ffP && st.sigC == st.sigP)
opmode[4] = State::S0;
else
opmode[4] = State::S1;
opmode[6] = State::S0;
opmode[5] = State::S1;
if (opmode[4] != State::S0) {
if (st.postAddMuxAB == ID(A))
st.sigC.extend_u0(48, st.postAdd->getParam(ID(B_SIGNED)).as_bool());
else
st.sigC.extend_u0(48, st.postAdd->getParam(ID(A_SIGNED)).as_bool());
cell->setPort(ID(C), st.sigC);
}
pm.autoremove(st.postAdd);
}
if (st.overflow) {
log(" overflow %s (%s)\n", log_id(st.overflow), log_id(st.overflow->type));
cell->setParam(ID(USE_PATTERN_DETECT), Const("PATDET"));
cell->setParam(ID(SEL_PATTERN), Const("PATTERN"));
cell->setParam(ID(SEL_MASK), Const("MASK"));
if (st.overflow->type == ID($ge)) {
Const B = st.overflow->getPort(ID(B)).as_const();
log_assert(std::count(B.bits.begin(), B.bits.end(), State::S1) == 1);
// Since B is an exact power of 2, subtract 1
// by inverting all bits up until hitting
// that one hi bit
for (auto &b : B.bits)
if (b == State::S0) b = State::S1;
else if (b == State::S1) {
b = State::S0;
break;
}
B.extu(48);
cell->setParam(ID(MASK), B);
cell->setParam(ID(PATTERN), Const(0, 48));
cell->setPort(ID(OVERFLOW), st.overflow->getPort(ID(Y)));
}
else log_abort();
pm.autoremove(st.overflow);
}
if (st.clock != SigBit())
{
cell->setPort(ID(CLK), st.clock);
auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
SigSpec D = ff->getPort(ID(D));
SigSpec Q = pm.sigmap(ff->getPort(ID(Q)));
if (!A.empty())
A.replace(Q, D);
if (rstmux) {
SigSpec Y = rstmux->getPort(ID(Y));
SigSpec AB = rstmux->getPort(rstpol ? ID(A) : ID(B));
if (!A.empty())
A.replace(Y, AB);
if (rstport != IdString()) {
SigSpec S = rstmux->getPort(ID(S));
cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
}
}
else if (rstport != IdString())
cell->setPort(rstport, State::S0);
if (cemux) {
SigSpec Y = cemux->getPort(ID(Y));
SigSpec BA = cemux->getPort(cepol ? ID(B) : ID(A));
SigSpec S = cemux->getPort(ID(S));
if (!A.empty())
A.replace(Y, BA);
cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
}
else
cell->setPort(ceport, State::S1);
for (auto c : Q.chunks()) {
auto it = c.wire->attributes.find(ID(init));
if (it == c.wire->attributes.end())
continue;
for (int i = c.offset; i < c.offset+c.width; i++) {
log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx);
it->second[i] = State::Sx;
}
}
};
if (st.ffA2) {
SigSpec A = cell->getPort(ID(A));
f(A, st.ffA2, st.ffA2cemux, st.ffA2cepol, ID(CEA2), st.ffA2rstmux, st.ffArstpol, ID(RSTA));
if (st.ffA1) {
f(A, st.ffA1, st.ffA1cemux, st.ffA1cepol, ID(CEA1), st.ffA1rstmux, st.ffArstpol, IdString());
cell->setParam(ID(AREG), 2);
cell->setParam(ID(ACASCREG), 2);
}
else {
cell->setParam(ID(AREG), 1);
cell->setParam(ID(ACASCREG), 1);
}
pm.add_siguser(A, cell);
cell->setPort(ID(A), A);
}
if (st.ffB2) {
SigSpec B = cell->getPort(ID(B));
f(B, st.ffB2, st.ffB2cemux, st.ffB2cepol, ID(CEB2), st.ffB2rstmux, st.ffBrstpol, ID(RSTB));
if (st.ffB1) {
f(B, st.ffB1, st.ffB1cemux, st.ffB1cepol, ID(CEB1), st.ffB1rstmux, st.ffBrstpol, IdString());
cell->setParam(ID(BREG), 2);
cell->setParam(ID(BCASCREG), 2);
}
else {
cell->setParam(ID(BREG), 1);
cell->setParam(ID(BCASCREG), 1);
}
pm.add_siguser(B, cell);
cell->setPort(ID(B), B);
}
if (st.ffD) {
SigSpec D = cell->getPort(ID(D));
f(D, st.ffD, st.ffDcemux, st.ffDcepol, ID(CED), st.ffDrstmux, st.ffDrstpol, ID(RSTD));
pm.add_siguser(D, cell);
cell->setPort(ID(D), D);
cell->setParam(ID(DREG), 1);
}
if (st.ffM) {
SigSpec M; // unused
f(M, st.ffM, st.ffMcemux, st.ffMcepol, ID(CEM), st.ffMrstmux, st.ffMrstpol, ID(RSTM));
st.ffM->connections_.at(ID(Q)).replace(st.sigM, pm.module->addWire(NEW_ID, GetSize(st.sigM)));
cell->setParam(ID(MREG), State::S1);
}
if (st.ffP) {
SigSpec P; // unused
f(P, st.ffP, st.ffPcemux, st.ffPcepol, ID(CEP), st.ffPrstmux, st.ffPrstpol, ID(RSTP));
st.ffP->connections_.at(ID(Q)).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP)));
cell->setParam(ID(PREG), State::S1);
}
log(" clock: %s (%s)", log_signal(st.clock), "posedge");
if (st.ffA2) {
log(" ffA2:%s", log_id(st.ffA2));
if (st.ffA1)
log(" ffA1:%s", log_id(st.ffA1));
}
if (st.ffAD)
log(" ffAD:%s", log_id(st.ffAD));
if (st.ffB2) {
log(" ffB2:%s", log_id(st.ffB2));
if (st.ffB1)
log(" ffB1:%s", log_id(st.ffB1));
}
if (st.ffD)
log(" ffD:%s", log_id(st.ffD));
if (st.ffM)
log(" ffM:%s", log_id(st.ffM));
if (st.ffP)
log(" ffP:%s", log_id(st.ffP));
}
log("\n");
SigSpec P = st.sigP;
if (GetSize(P) < 48)
P.append(pm.module->addWire(NEW_ID, 48-GetSize(P)));
cell->setPort(ID(P), P);
pm.blacklist(cell);
}
void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm)
{
auto &st = pm.st_xilinx_dsp_packC;
log_debug("Analysing %s.%s for Xilinx DSP packing (CREG).\n", log_id(pm.module), log_id(st.dsp));
log_debug("ffC: %s %s %s\n", log_id(st.ffC, "--"), log_id(st.ffCcemux, "--"), log_id(st.ffCrstmux, "--"));
Cell *cell = st.dsp;
if (st.clock != SigBit())
{
cell->setPort(ID(CLK), st.clock);
auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
SigSpec D = ff->getPort(ID(D));
SigSpec Q = pm.sigmap(ff->getPort(ID(Q)));
if (!A.empty())
A.replace(Q, D);
if (rstmux) {
SigSpec Y = rstmux->getPort(ID(Y));
SigSpec AB = rstmux->getPort(rstpol ? ID(A) : ID(B));
if (!A.empty())
A.replace(Y, AB);
if (rstport != IdString()) {
SigSpec S = rstmux->getPort(ID(S));
cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
}
}
else if (rstport != IdString())
cell->setPort(rstport, State::S0);
if (cemux) {
SigSpec Y = cemux->getPort(ID(Y));
SigSpec BA = cemux->getPort(cepol ? ID(B) : ID(A));
SigSpec S = cemux->getPort(ID(S));
if (!A.empty())
A.replace(Y, BA);
cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
}
else
cell->setPort(ceport, State::S1);
for (auto c : Q.chunks()) {
auto it = c.wire->attributes.find(ID(init));
if (it == c.wire->attributes.end())
continue;
for (int i = c.offset; i < c.offset+c.width; i++) {
log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx);
it->second[i] = State::Sx;
}
}
};
if (st.ffC) {
SigSpec C = cell->getPort(ID(C));
f(C, st.ffC, st.ffCcemux, st.ffCcepol, ID(CEC), st.ffCrstmux, st.ffCrstpol, ID(RSTC));
pm.add_siguser(C, cell);
cell->setPort(ID(C), C);
cell->setParam(ID(CREG), 1);
}
log(" clock: %s (%s)", log_signal(st.clock), "posedge");
if (st.ffC)
log(" ffC:%s", log_id(st.ffC));
log("\n");
}
pm.blacklist(cell);
}
struct XilinxDspPass : public Pass {
XilinxDspPass() : Pass("xilinx_dsp", "Xilinx: pack resources into DSPs") { }
void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" xilinx_dsp [options] [selection]\n");
log("\n");
log("Pack input registers (A2, A1, B2, B1, C, D, AD; with optional enable/reset),\n");
log("pipeline registers (M; with optional enable/reset), output registers (P; with\n");
log("optional enable/reset), pre-adder and/or post-adder into Xilinx DSP resources.\n");
log("\n");
log("Multiply-accumulate operations using the post-adder with feedback on the 'C'\n");
log("input will be folded into the DSP. In this scenario only, the 'C' input can be\n");
log("used to override the current accumulation result with a new value, which will\n");
log("be added to the multiplier result to form the next accumulation result.\n");
log("\n");
log("Use of the dedicated 'PCOUT' -> 'PCIN' cascade path is detected for 'P' -> 'C'\n");
log("connections (optionally, where 'P' is right-shifted by 17-bits and used as an\n");
log("input to the post-adder -- a pattern common for summing partial products to\n");
log("implement wide multipliers). Limited support also exists for similar cascading\n");
log("for A and B using '[AB]COUT' -> '[AB]CIN'. Currently, cascade chains are limited\n");
log("to a maximum length of 20 cells, corresponding to the smallest Xilinx 7 Series\n");
log("device.\n");
log("\n");
log("\n");
log("Experimental feature: addition/subtractions less than 12 or 24 bits with the\n");
log("'(* use_dsp=\"simd\" *)' attribute attached to the output wire or attached to\n");
log("the add/subtract operator will cause those operations to be implemented using\n");
log("the 'SIMD' feature of DSPs.\n");
log("\n");
log("Experimental feature: the presence of a `$ge' cell attached to the registered\n");
log("P output implementing the operation \"(P >= <power-of-2>)\" will be transformed\n");
log("into using the DSP48E1's pattern detector feature for overflow detection.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing XILINX_DSP pass (pack resources into DSPs).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
// if (args[argidx] == "-singleton") {
// singleton_mode = true;
// continue;
// }
break;
}
extra_args(args, argidx, design);
for (auto module : design->selected_modules()) {
xilinx_simd_pack(module, module->selected_cells());
{
xilinx_dsp_pm pm(module, module->selected_cells());
pm.run_xilinx_dsp_pack(xilinx_dsp_pack);
}
// Separating out CREG packing is necessary since there
// is no guarantee that the cell ordering corresponds
// to the "expected" case (i.e. the order in which
// they appear in the source) thus the possiblity
// existed that a register got packed as CREG into a
// downstream DSP that should have otherwise been a
// PREG of an upstream DSP that had not been pattern
// matched yet
{
xilinx_dsp_CREG_pm pm(module, module->selected_cells());
pm.run_xilinx_dsp_packC(xilinx_dsp_packC);
}
{
xilinx_dsp_cascade_pm pm(module, module->selected_cells());
pm.run_xilinx_dsp_cascade();
}
}
}
} XilinxDspPass;
PRIVATE_NAMESPACE_END

587
passes/pmgen/xilinx_dsp.pmg Normal file
View file

@ -0,0 +1,587 @@
pattern xilinx_dsp_pack
state <SigBit> clock
state <SigSpec> sigA sigB sigC sigD sigM sigP
state <IdString> postAddAB postAddMuxAB
state <bool> ffA1cepol ffA2cepol ffADcepol ffB1cepol ffB2cepol ffDcepol ffMcepol ffPcepol
state <bool> ffArstpol ffADrstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol
state <Cell*> ffAD ffADcemux ffADrstmux ffA1 ffA1cemux ffA1rstmux ffA2 ffA2cemux ffA2rstmux
state <Cell*> ffB1 ffB1cemux ffB1rstmux ffB2 ffB2cemux ffB2rstmux
state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux
// subpattern
state <SigSpec> argQ argD
state <bool> ffcepol ffrstpol
state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
udata <Cell*> dff dffcemux dffrstmux
udata <bool> dffcepol dffrstpol
match dsp
select dsp->type.in(\DSP48E1)
endmatch
code sigA sigB sigC sigD sigM clock
auto unextend = [](const SigSpec &sig) {
int i;
for (i = GetSize(sig)-1; i > 0; i--)
if (sig[i] != sig[i-1])
break;
// Do not remove non-const sign bit
if (sig[i].wire)
++i;
return sig.extract(0, i);
};
sigA = unextend(port(dsp, \A));
sigB = unextend(port(dsp, \B));
sigC = port(dsp, \C, SigSpec());
sigD = port(dsp, \D, SigSpec());
SigSpec P = port(dsp, \P);
if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
// Only care about those bits that are used
int i;
for (i = 0; i < GetSize(P); i++) {
if (nusers(P[i]) <= 1)
break;
sigM.append(P[i]);
}
log_assert(nusers(P.extract_end(i)) <= 1);
}
else
sigM = P;
// This sigM could have no users if downstream $add
// is narrower than $mul result, for example
if (sigM.empty())
reject;
clock = port(dsp, \CLK, SigBit());
endcode
code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock
if (param(dsp, \ADREG).as_int() == 0) {
argQ = sigA;
subpattern(in_dffe);
if (dff) {
ffAD = dff;
clock = dffclock;
if (dffrstmux) {
ffADrstmux = dffrstmux;
ffADrstpol = dffrstpol;
}
if (dffcemux) {
ffADcemux = dffcemux;
ffADcepol = dffcepol;
}
sigA = dffD;
}
}
endcode
match preAdd
if sigD.empty() || sigD.is_fully_zero()
// Ensure that preAdder not already used
if param(dsp, \USE_DPORT, Const("FALSE")).decode_string() == "FALSE"
if port(dsp, \INMODE, Const(0, 5)).is_fully_zero()
select preAdd->type.in($add)
// Output has to be 25 bits or less
select GetSize(port(preAdd, \Y)) <= 25
select nusers(port(preAdd, \Y)) == 2
choice <IdString> AB {\A, \B}
// A port has to be 30 bits or less
select GetSize(port(preAdd, AB)) <= 30
define <IdString> BA (AB == \A ? \B : \A)
// D port has to be 25 bits or less
select GetSize(port(preAdd, BA)) <= 25
index <SigSpec> port(preAdd, \Y) === sigA
optional
endmatch
code sigA sigD
if (preAdd) {
sigA = port(preAdd, \A);
sigD = port(preAdd, \B);
if (GetSize(sigA) < GetSize(sigD))
std::swap(sigA, sigD);
}
endcode
code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cemux ffA2rstmux ffA2cepol ffArstpol ffA1 ffA1cemux ffA1rstmux ffA1cepol
// Only search for ffA2 if there was a pre-adder
// (otherwise ffA2 would have been matched as ffAD)
if (preAdd) {
if (param(dsp, \AREG).as_int() == 0) {
argQ = sigA;
subpattern(in_dffe);
if (dff) {
ffA2 = dff;
clock = dffclock;
if (dffrstmux) {
ffA2rstmux = dffrstmux;
ffArstpol = dffrstpol;
}
if (dffcemux) {
ffA2cepol = dffcepol;
ffA2cemux = dffcemux;
}
sigA = dffD;
}
}
}
// And if there wasn't a pre-adder,
// move AD register to A
else if (ffAD) {
log_assert(!ffA2 && !ffA2cemux && !ffA2rstmux);
std::swap(ffA2, ffAD);
std::swap(ffA2cemux, ffADcemux);
std::swap(ffA2rstmux, ffADrstmux);
ffA2cepol = ffADcepol;
ffArstpol = ffADrstpol;
}
// Now attempt to match A1
if (ffA2) {
argQ = sigA;
subpattern(in_dffe);
if (dff) {
if ((ffA2rstmux != nullptr) ^ (dffrstmux != nullptr))
goto ffA1_end;
if (dffrstmux) {
if (ffArstpol != dffrstpol)
goto ffA1_end;
if (port(ffA2rstmux, \S) != port(dffrstmux, \S))
goto ffA1_end;
ffA1rstmux = dffrstmux;
}
ffA1 = dff;
clock = dffclock;
if (dffcemux) {
ffA1cemux = dffcemux;
ffA1cepol = dffcepol;
}
sigA = dffD;
ffA1_end: ;
}
}
endcode
code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemux ffB1rstmux ffB1cepol
if (param(dsp, \BREG).as_int() == 0) {
argQ = sigB;
subpattern(in_dffe);
if (dff) {
ffB2 = dff;
clock = dffclock;
if (dffrstmux) {
ffB2rstmux = dffrstmux;
ffBrstpol = dffrstpol;
}
if (dffcemux) {
ffB2cemux = dffcemux;
ffB2cepol = dffcepol;
}
sigB = dffD;
// Now attempt to match B1
if (ffB2) {
argQ = sigB;
subpattern(in_dffe);
if (dff) {
if ((ffB2rstmux != nullptr) ^ (dffrstmux != nullptr))
goto ffB1_end;
if (dffrstmux) {
if (ffBrstpol != dffrstpol)
goto ffB1_end;
if (port(ffB2rstmux, \S) != port(dffrstmux, \S))
goto ffB1_end;
ffB1rstmux = dffrstmux;
}
ffB1 = dff;
clock = dffclock;
if (dffcemux) {
ffB1cemux = dffcemux;
ffB1cepol = dffcepol;
}
sigB = dffD;
ffB1_end: ;
}
}
}
}
endcode
code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
if (param(dsp, \DREG).as_int() == 0) {
argQ = sigD;
subpattern(in_dffe);
if (dff) {
ffD = dff;
clock = dffclock;
if (dffrstmux) {
ffDrstmux = dffrstmux;
ffDrstpol = dffrstpol;
}
if (dffcemux) {
ffDcemux = dffcemux;
ffDcepol = dffcepol;
}
sigD = dffD;
}
}
endcode
code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) {
argD = sigM;
subpattern(out_dffe);
if (dff) {
ffM = dff;
clock = dffclock;
if (dffrstmux) {
ffMrstmux = dffrstmux;
ffMrstpol = dffrstpol;
}
if (dffcemux) {
ffMcemux = dffcemux;
ffMcepol = dffcepol;
}
sigM = dffQ;
}
}
sigP = sigM;
endcode
match postAdd
// Ensure that Z mux is not already used
if port(dsp, \OPMODE, SigSpec()).extract(4,3).is_fully_zero()
select postAdd->type.in($add)
select GetSize(port(postAdd, \Y)) <= 48
choice <IdString> AB {\A, \B}
select nusers(port(postAdd, AB)) <= 3
filter ffMcemux || nusers(port(postAdd, AB)) == 2
filter !ffMcemux || nusers(port(postAdd, AB)) == 3
index <SigBit> port(postAdd, AB)[0] === sigP[0]
filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP
filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP))
set postAddAB AB
optional
endmatch
code sigC sigP
if (postAdd) {
sigC = port(postAdd, postAddAB == \A ? \B : \A);
sigP = port(postAdd, \Y);
}
endcode
code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
if (param(dsp, \PREG).as_int() == 0) {
int users = 2;
// If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
if (ffMcemux && !postAdd) users++;
if (nusers(sigP) == users) {
argD = sigP;
subpattern(out_dffe);
if (dff) {
ffP = dff;
clock = dffclock;
if (dffrstmux) {
ffPrstmux = dffrstmux;
ffPrstpol = dffrstpol;
}
if (dffcemux) {
ffPcemux = dffcemux;
ffPcepol = dffcepol;
}
sigP = dffQ;
}
}
}
endcode
match postAddMux
if postAdd
if ffP
select postAddMux->type.in($mux)
select nusers(port(postAddMux, \Y)) == 2
choice <IdString> AB {\A, \B}
index <SigSpec> port(postAddMux, AB) === sigP
index <SigSpec> port(postAddMux, \Y) === sigC
set postAddMuxAB AB
optional
endmatch
code sigC
if (postAddMux)
sigC = port(postAddMux, postAddMuxAB == \A ? \B : \A);
endcode
match overflow
if ffP
if param(dsp, \USE_PATTERN_DETECT, Const("NO_PATDET")).decode_string() == "NO_PATDET"
select overflow->type.in($ge)
select GetSize(port(overflow, \Y)) <= 48
select port(overflow, \B).is_fully_const()
define <Const> B port(overflow, \B).as_const()
select std::count(B.bits.begin(), B.bits.end(), State::S1) == 1
index <SigSpec> port(overflow, \A) === sigP
optional
endmatch
code
accept;
endcode
// #######################
subpattern in_dffe
arg argD argQ clock
code
dff = nullptr;
for (auto c : argQ.chunks()) {
if (!c.wire)
reject;
if (c.wire->get_bool_attribute(\keep))
reject;
Const init = c.wire->attributes.at(\init, State::Sx);
if (!init.is_fully_undef() && !init.is_fully_zero())
reject;
}
endcode
match ff
select ff->type.in($dff)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
// Check that the rest of argQ is present
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
set ffoffset offset
endmatch
code argQ argD
{
if (clock != SigBit() && port(ff, \CLK) != clock)
reject;
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffD = argQ;
argD = port(ff, \D);
argQ = Q;
dffD.replace(argQ, argD);
// Only search for ffrstmux if dffD only
// has two (ff, ffrstmux) users
if (nusers(dffD) > 2)
argD = SigSpec();
}
endcode
match ffrstmux
if !argD.empty()
select ffrstmux->type.in($mux)
index <SigSpec> port(ffrstmux, \Y) === argD
choice <IdString> BA {\B, \A}
// DSP48E1 only supports reset to zero
select port(ffrstmux, BA).is_fully_zero()
define <bool> pol (BA == \B)
set ffrstpol pol
semioptional
endmatch
code argD
if (ffrstmux) {
dffrstmux = ffrstmux;
dffrstpol = ffrstpol;
argD = port(ffrstmux, ffrstpol ? \A : \B);
dffD.replace(port(ffrstmux, \Y), argD);
// Only search for ffcemux if argQ has at
// least 3 users (ff, <upstream>, ffrstmux) and
// dffD only has two (ff, ffrstmux)
if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
argD = SigSpec();
}
else
dffrstmux = nullptr;
endcode
match ffcemux
if !argD.empty()
select ffcemux->type.in($mux)
index <SigSpec> port(ffcemux, \Y) === argD
choice <IdString> AB {\A, \B}
index <SigSpec> port(ffcemux, AB) === argQ
define <bool> pol (AB == \A)
set ffcepol pol
semioptional
endmatch
code argD
if (ffcemux) {
dffcemux = ffcemux;
dffcepol = ffcepol;
argD = port(ffcemux, ffcepol ? \B : \A);
dffD.replace(port(ffcemux, \Y), argD);
}
else
dffcemux = nullptr;
endcode
// #######################
subpattern out_dffe
arg argD argQ clock
code
dff = nullptr;
for (auto c : argD.chunks())
if (c.wire->get_bool_attribute(\keep))
reject;
endcode
match ffcemux
select ffcemux->type.in($mux)
// ffcemux output must have two users: ffcemux and ff.D
select nusers(port(ffcemux, \Y)) == 2
choice <IdString> AB {\A, \B}
// keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s)
select nusers(port(ffcemux, AB)) >= 3
slice offset GetSize(port(ffcemux, \Y))
define <IdString> BA (AB == \A ? \B : \A)
index <SigBit> port(ffcemux, BA)[offset] === argD[0]
// Check that the rest of argD is present
filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD)
filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD
set ffoffset offset
define <bool> pol (AB == \A)
set ffcepol pol
semioptional
endmatch
code argD argQ
dffcemux = ffcemux;
if (ffcemux) {
SigSpec BA = port(ffcemux, ffcepol ? \B : \A);
SigSpec Y = port(ffcemux, \Y);
argQ = argD;
argD.replace(BA, Y);
argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B));
dffcemux = ffcemux;
dffcepol = ffcepol;
}
endcode
match ffrstmux
select ffrstmux->type.in($mux)
// ffrstmux output must have two users: ffrstmux and ff.D
select nusers(port(ffrstmux, \Y)) == 2
choice <IdString> BA {\B, \A}
// DSP48E1 only supports reset to zero
select port(ffrstmux, BA).is_fully_zero()
slice offset GetSize(port(ffrstmux, \Y))
define <IdString> AB (BA == \B ? \A : \B)
index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
// Check that offset is consistent
filter !ffcemux || ffoffset == offset
// Check that the rest of argD is present
filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
set ffoffset offset
define <bool> pol (AB == \A)
set ffrstpol pol
semioptional
endmatch
code argD argQ
dffrstmux = ffrstmux;
if (ffrstmux) {
SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
SigSpec Y = port(ffrstmux, \Y);
argD.replace(AB, Y);
dffrstmux = ffrstmux;
dffrstpol = ffrstpol;
}
endcode
match ff
select ff->type.in($dff)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \D)[offset] === argD[0]
// Check that offset is consistent
filter (!ffcemux && !ffrstmux) || ffoffset == offset
// Check that the rest of argD is present
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
// Check that FF.Q is connected to CE-mux
filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
set ffoffset offset
endmatch
code argQ
if (ff) {
if (clock != SigBit() && port(ff, \CLK) != clock)
reject;
SigSpec D = port(ff, \D);
SigSpec Q = port(ff, \Q);
if (!ffcemux) {
argQ = argD;
argQ.replace(D, Q);
}
for (auto c : argQ.chunks()) {
Const init = c.wire->attributes.at(\init, State::Sx);
if (!init.is_fully_undef() && !init.is_fully_zero())
reject;
}
dff = ff;
dffQ = argQ;
dffclock = port(ff, \CLK);
}
// No enable/reset mux possible without flop
else if (dffcemux || dffrstmux)
reject;
endcode

View file

@ -0,0 +1,181 @@
pattern xilinx_dsp_packC
udata <std::function<SigSpec(const SigSpec&)>> unextend
state <SigBit> clock
state <SigSpec> sigC sigP
state <bool> ffCcepol ffCrstpol
state <Cell*> ffC ffCcemux ffCrstmux
// subpattern
state <SigSpec> argQ argD
state <bool> ffcepol ffrstpol
state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
udata <Cell*> dff dffcemux dffrstmux
udata <bool> dffcepol dffrstpol
match dsp
select dsp->type.in(\DSP48E1)
select param(dsp, \CREG, 1).as_int() == 0
select nusers(port(dsp, \C, SigSpec())) > 1
endmatch
code argQ ffC ffCcemux ffCrstmux ffCcepol ffCrstpol sigC sigP clock
unextend = [](const SigSpec &sig) {
int i;
for (i = GetSize(sig)-1; i > 0; i--)
if (sig[i] != sig[i-1])
break;
// Do not remove non-const sign bit
if (sig[i].wire)
++i;
return sig.extract(0, i);
};
sigC = unextend(port(dsp, \C, SigSpec()));
SigSpec P = port(dsp, \P);
if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
// Only care about those bits that are used
int i;
for (i = 0; i < GetSize(P); i++) {
if (nusers(P[i]) <= 1)
break;
sigP.append(P[i]);
}
log_assert(nusers(P.extract_end(i)) <= 1);
}
else
sigP = P;
if (sigC == sigP)
reject;
clock = port(dsp, \CLK, SigBit());
argQ = sigC;
subpattern(in_dffe);
if (dff) {
ffC = dff;
clock = dffclock;
if (dffrstmux) {
ffCrstmux = dffrstmux;
ffCrstpol = dffrstpol;
}
if (dffcemux) {
ffCcemux = dffcemux;
ffCcepol = dffcepol;
}
sigC = dffD;
}
endcode
code
if (ffC)
accept;
endcode
// #######################
subpattern in_dffe
arg argD argQ clock
code
dff = nullptr;
for (auto c : argQ.chunks()) {
if (!c.wire)
reject;
if (c.wire->get_bool_attribute(\keep))
reject;
Const init = c.wire->attributes.at(\init, State::Sx);
if (!init.is_fully_undef() && !init.is_fully_zero())
reject;
}
endcode
match ff
select ff->type.in($dff)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
// Check that the rest of argQ is present
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
set ffoffset offset
endmatch
code argQ argD
{
if (clock != SigBit() && port(ff, \CLK) != clock)
reject;
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffD = argQ;
argD = port(ff, \D);
argQ = Q;
dffD.replace(argQ, argD);
// Only search for ffrstmux if dffD only
// has two (ff, ffrstmux) users
if (nusers(dffD) > 2)
argD = SigSpec();
}
endcode
match ffrstmux
if !argD.empty()
select ffrstmux->type.in($mux)
index <SigSpec> port(ffrstmux, \Y) === argD
choice <IdString> BA {\B, \A}
// DSP48E1 only supports reset to zero
select port(ffrstmux, BA).is_fully_zero()
define <bool> pol (BA == \B)
set ffrstpol pol
semioptional
endmatch
code argD
if (ffrstmux) {
dffrstmux = ffrstmux;
dffrstpol = ffrstpol;
argD = port(ffrstmux, ffrstpol ? \A : \B);
dffD.replace(port(ffrstmux, \Y), argD);
// Only search for ffcemux if argQ has at
// least 3 users (ff, <upstream>, ffrstmux) and
// dffD only has two (ff, ffrstmux)
if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
argD = SigSpec();
}
else
dffrstmux = nullptr;
endcode
match ffcemux
if !argD.empty()
select ffcemux->type.in($mux)
index <SigSpec> port(ffcemux, \Y) === argD
choice <IdString> AB {\A, \B}
index <SigSpec> port(ffcemux, AB) === argQ
define <bool> pol (AB == \A)
set ffcepol pol
semioptional
endmatch
code argD
if (ffcemux) {
dffcemux = ffcemux;
dffcepol = ffcepol;
argD = port(ffcemux, ffcepol ? \B : \A);
dffD.replace(port(ffcemux, \Y), argD);
}
else
dffcemux = nullptr;
endcode

View file

@ -0,0 +1,336 @@
pattern xilinx_dsp_cascade
udata <std::function<SigSpec(const SigSpec&)>> unextend
udata <vector<std::tuple<Cell*,int,int,int>>> chain longest_chain
state <Cell*> next
state <SigSpec> clock
state <int> AREG BREG
// subpattern
state <SigSpec> argQ argD
state <bool> ffcepol ffrstpol
state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
udata <Cell*> dff dffcemux dffrstmux
udata <bool> dffcepol dffrstpol
code
#define MAX_DSP_CASCADE 20
endcode
match first
select first->type.in(\DSP48E1)
select port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000")
select nusers(port(first, \PCOUT, SigSpec())) <= 1
endmatch
code
longest_chain.clear();
chain.emplace_back(first, -1, -1, -1);
subpattern(tail);
finally
chain.pop_back();
log_assert(chain.empty());
if (GetSize(longest_chain) > 1) {
Cell *dsp = std::get<0>(longest_chain.front());
Cell *dsp_pcin;
int P, AREG, BREG;
for (int i = 1; i < GetSize(longest_chain); i++) {
std::tie(dsp_pcin,P,AREG,BREG) = longest_chain[i];
if (i % MAX_DSP_CASCADE > 0) {
if (P >= 0) {
Wire *cascade = module->addWire(NEW_ID, 48);
dsp_pcin->setPort(ID(C), Const(0, 48));
dsp_pcin->setPort(ID(PCIN), cascade);
dsp->setPort(ID(PCOUT), cascade);
add_siguser(cascade, dsp_pcin);
add_siguser(cascade, dsp);
SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7));
if (P == 17)
opmode[6] = State::S1;
else if (P == 0)
opmode[6] = State::S0;
else log_abort();
opmode[5] = State::S0;
opmode[4] = State::S1;
dsp_pcin->setPort(\OPMODE, opmode);
log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
}
if (AREG >= 0) {
Wire *cascade = module->addWire(NEW_ID, 30);
dsp_pcin->setPort(ID(A), Const(0, 30));
dsp_pcin->setPort(ID(ACIN), cascade);
dsp->setPort(ID(ACOUT), cascade);
add_siguser(cascade, dsp_pcin);
add_siguser(cascade, dsp);
dsp->setParam(ID(ACASCREG), AREG);
dsp_pcin->setParam(ID(A_INPUT), Const("CASCADE"));
log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
}
if (BREG >= 0) {
Wire *cascade = module->addWire(NEW_ID, 18);
dsp_pcin->setPort(ID(B), Const(0, 18));
dsp_pcin->setPort(ID(BCIN), cascade);
dsp->setPort(ID(BCOUT), cascade);
add_siguser(cascade, dsp_pcin);
add_siguser(cascade, dsp);
dsp->setParam(ID(BCASCREG), BREG);
dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE"));
log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
}
}
else {
log_debug(" Blocking %s -> %s cascade (exceeds max: %d)\n", log_id(dsp), log_id(dsp_pcin), MAX_DSP_CASCADE);
}
dsp = dsp_pcin;
}
accept;
}
endcode
// ------------------------------------------------------------------
subpattern tail
arg first
arg next
match nextP
select nextP->type.in(\DSP48E1)
select !param(nextP, \CREG, State::S1).as_bool()
select port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011")
select nusers(port(nextP, \C, SigSpec())) > 1
select nusers(port(nextP, \PCIN, SigSpec())) == 0
index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0]
semioptional
endmatch
match nextP_shift17
if !nextP
select nextP_shift17->type.in(\DSP48E1)
select !param(nextP_shift17, \CREG, State::S1).as_bool()
select port(nextP_shift17, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011")
select nusers(port(nextP_shift17, \C, SigSpec())) > 1
select nusers(port(nextP_shift17, \PCIN, SigSpec())) == 0
index <SigBit> port(nextP_shift17, \C)[0] === port(std::get<0>(chain.back()), \P)[17]
semioptional
endmatch
code next
next = nextP;
if (!nextP)
next = nextP_shift17;
if (next) {
unextend = [](const SigSpec &sig) {
int i;
for (i = GetSize(sig)-1; i > 0; i--)
if (sig[i] != sig[i-1])
break;
// Do not remove non-const sign bit
if (sig[i].wire)
++i;
return sig.extract(0, i);
};
}
endcode
code argQ clock AREG
AREG = -1;
if (next) {
Cell *prev = std::get<0>(chain.back());
if (param(prev, \AREG, 2).as_int() > 0 &&
param(next, \AREG, 2).as_int() > 0 &&
param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
port(next, \ACIN, SigSpec()).is_fully_zero() &&
nusers(port(prev, \ACOUT, SigSpec())) <= 1) {
argQ = unextend(port(next, \A));
clock = port(prev, \CLK);
subpattern(in_dffe);
if (dff) {
if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0)
goto reject_AREG;
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0))
goto reject_AREG;
if (!dffcemux && port(prev, \CEA2, State::S0) != State::S0)
goto reject_AREG;
if (dffcemux && port(dffcemux, \S) != port(prev, \CEA2, State::S0))
goto reject_AREG;
if (dffD == unextend(port(prev, \A)))
AREG = 1;
reject_AREG: ;
}
}
}
endcode
code argQ clock BREG
BREG = -1;
if (next) {
Cell *prev = std::get<0>(chain.back());
if (param(prev, \BREG, 2).as_int() > 0 &&
param(next, \BREG, 2).as_int() > 0 &&
param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
port(next, \BCIN, SigSpec()).is_fully_zero() &&
nusers(port(prev, \BCOUT, SigSpec())) <= 1) {
argQ = unextend(port(next, \B));
clock = port(prev, \CLK);
subpattern(in_dffe);
if (dff) {
if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0)
goto reject_BREG;
if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0))
goto reject_BREG;
if (!dffcemux && port(prev, \CEB2, State::S0) != State::S0)
goto reject_BREG;
if (dffcemux && port(dffcemux, \S) != port(prev, \CEB2, State::S0))
goto reject_BREG;
if (dffD == unextend(port(prev, \B)))
BREG = 1;
reject_BREG: ;
}
}
}
endcode
code
if (next) {
chain.emplace_back(next, nextP_shift17 ? 17 : nextP ? 0 : -1, AREG, BREG);
SigSpec sigC = unextend(port(next, \C));
// TODO: Cannot use 'reject' since semioptional
if (nextP_shift17) {
if (GetSize(sigC)+17 <= GetSize(port(std::get<0>(chain.back()), \P)) &&
port(std::get<0>(chain.back()), \P).extract(17, GetSize(sigC)) != sigC)
subpattern(tail);
}
else {
if (GetSize(sigC) <= GetSize(port(std::get<0>(chain.back()), \P)) &&
port(std::get<0>(chain.back()), \P).extract(0, GetSize(sigC)) != sigC)
subpattern(tail);
}
} else {
if (GetSize(chain) > GetSize(longest_chain))
longest_chain = chain;
}
finally
if (next)
chain.pop_back();
endcode
// #######################
subpattern in_dffe
arg argD argQ clock
code
dff = nullptr;
for (auto c : argQ.chunks()) {
if (!c.wire)
reject;
if (c.wire->get_bool_attribute(\keep))
reject;
Const init = c.wire->attributes.at(\init, State::Sx);
if (!init.is_fully_undef() && !init.is_fully_zero())
reject;
}
endcode
match ff
select ff->type.in($dff)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
// Check that the rest of argQ is present
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
set ffoffset offset
endmatch
code argQ argD
{
if (clock != SigBit() && port(ff, \CLK) != clock)
reject;
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffD = argQ;
argD = port(ff, \D);
argQ = Q;
dffD.replace(argQ, argD);
// Only search for ffrstmux if dffD only
// has two (ff, ffrstmux) users
if (nusers(dffD) > 2)
argD = SigSpec();
}
endcode
match ffrstmux
if !argD.empty()
select ffrstmux->type.in($mux)
index <SigSpec> port(ffrstmux, \Y) === argD
choice <IdString> BA {\B, \A}
// DSP48E1 only supports reset to zero
select port(ffrstmux, BA).is_fully_zero()
define <bool> pol (BA == \B)
set ffrstpol pol
semioptional
endmatch
code argD
if (ffrstmux) {
dffrstmux = ffrstmux;
dffrstpol = ffrstpol;
argD = port(ffrstmux, ffrstpol ? \A : \B);
dffD.replace(port(ffrstmux, \Y), argD);
// Only search for ffcemux if argQ has at
// least 3 users (ff, <upstream>, ffrstmux) and
// dffD only has two (ff, ffrstmux)
if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
argD = SigSpec();
}
else
dffrstmux = nullptr;
endcode
match ffcemux
if !argD.empty()
select ffcemux->type.in($mux)
index <SigSpec> port(ffcemux, \Y) === argD
choice <IdString> AB {\A, \B}
index <SigSpec> port(ffcemux, AB) === argQ
define <bool> pol (AB == \A)
set ffcepol pol
semioptional
endmatch
code argD
if (ffcemux) {
dffcemux = ffcemux;
dffcepol = ffcepol;
argD = port(ffcemux, ffcepol ? \B : \A);
dffD.replace(port(ffcemux, \Y), argD);
}
else
dffcemux = nullptr;
endcode

View file

@ -13,9 +13,9 @@ endcode
match first
select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
select !first->has_keep_attr()
select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero()
select !first->type.in(\FDRE) || !param(first, \IS_R_INVERTED, State::S0).as_bool()
select !first->type.in(\FDRE) || !param(first, \IS_D_INVERTED, State::S0).as_bool()
select !first->type.in(\FDRE, \FDRE_1) || port(first, \R, State::S0).is_fully_zero()
filter !non_first_cells.count(first)
generate
SigSpec C = module->addWire(NEW_ID);
@ -84,9 +84,9 @@ arg en_port
match first
select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
select !first->has_keep_attr()
select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero()
select !first->type.in(\FDRE) || !param(first, \IS_R_INVERTED, State::S0).as_bool()
select !first->type.in(\FDRE) || !param(first, \IS_D_INVERTED, State::S0).as_bool()
select !first->type.in(\FDRE, \FDRE_1) || port(first, \R, State::S0).is_fully_zero()
endmatch
code clk_port en_port
@ -111,10 +111,10 @@ match next
index <SigBit> port(next, \Q) === port(first, \D)
filter port(next, clk_port) == port(first, clk_port)
filter en_port == IdString() || port(next, en_port) == port(first, en_port)
filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool()
filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero()
filter !first->type.in(\FDRE) || param(next, \IS_C_INVERTED, State::S0).as_bool() == param(first, \IS_C_INVERTED, State::S0).as_bool()
filter !first->type.in(\FDRE) || param(next, \IS_D_INVERTED, State::S0).as_bool() == param(first, \IS_D_INVERTED, State::S0).as_bool()
filter !first->type.in(\FDRE) || param(next, \IS_R_INVERTED, State::S0).as_bool() == param(first, \IS_R_INVERTED, State::S0).as_bool()
filter !first->type.in(\FDRE, \FDRE_1) || port(next, \R, State::S0).is_fully_zero()
endmatch
code
@ -138,10 +138,10 @@ match next
index <SigBit> port(next, \Q) === port(chain.back(), \D)
filter port(next, clk_port) == port(first, clk_port)
filter en_port == IdString() || port(next, en_port) == port(first, en_port)
filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool()
filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero()
filter !first->type.in(\FDRE) || param(next, \IS_C_INVERTED, State::S0).as_bool() == param(first, \IS_C_INVERTED, State::S0).as_bool()
filter !first->type.in(\FDRE) || param(next, \IS_D_INVERTED, State::S0).as_bool() == param(first, \IS_D_INVERTED, State::S0).as_bool()
filter !first->type.in(\FDRE) || param(next, \IS_R_INVERTED, State::S0).as_bool() == param(first, \IS_R_INVERTED, State::S0).as_bool()
filter !first->type.in(\FDRE, \FDRE_1) || port(next, \R, State::S0).is_fully_zero()
generate
Cell *cell = module->addCell(NEW_ID, chain.back()->type);
cell->setPort(\C, chain.back()->getPort(\C));
@ -149,7 +149,7 @@ generate
cell->setPort(\Q, chain.back()->getPort(\D));
if (cell->type == \FDRE) {
if (rng(2) == 0)
cell->setPort(\R, chain.back()->connections_.at(\R, State::S0));
cell->setPort(\R, port(chain.back(), \R, State::S0));
cell->setPort(\CE, chain.back()->getPort(\CE));
}
else if (cell->type.begins_with("$_DFFE_"))

View file

@ -553,7 +553,6 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
existing_cell = module->cell(mapped_cell->name);
log_assert(existing_cell);
cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
module->swap_names(cell, existing_cell);
}
if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
@ -594,8 +593,22 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
}
}
for (auto cell : boxes)
module->remove(cell);
for (auto existing_cell : boxes) {
Cell *cell = module->cell(remap_name(existing_cell->name));
if (cell) {
for (auto &conn : existing_cell->connections()) {
if (!conn.second.is_wire())
continue;
Wire *wire = conn.second.as_wire();
if (!wire->get_bool_attribute(ID(abc_padding)))
continue;
cell->unsetPort(conn.first);
log_debug("Dropping padded port connection for %s (%s) .%s (%s )\n", log_id(cell), cell->type.c_str(), log_id(conn.first), log_signal(conn.second));
}
module->swap_names(cell, existing_cell);
}
module->remove(existing_cell);
}
// Copy connections (and rename) from mapped_mod to module
for (auto conn : mapped_mod->connections()) {

View file

@ -351,6 +351,11 @@ struct TestAutotbBackend : public Backend {
log(" -n <int>\n");
log(" number of iterations the test bench should run (default = 1000)\n");
log("\n");
log(" -seed <int>\n");
log(" seed used for pseudo-random number generation (default = 0).\n");
log(" a value of 0 will cause an arbitrary seed to be chosen, based on\n");
log(" the current system time.\n");
log("\n");
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{