synth_ice40: Use opt_dff.

The main part is converting ice40_dsp to recognize the new FF types
created in opt_dff instead of trying to recognize the mux patterns on
its own.

The fsm call has been moved upwards because the passes cannot deal with
$dffe/$sdff*, and other optimizations don't help it much anyway.
This commit is contained in:
Marcelina Kościelnicka 2020-07-22 13:34:11 +02:00
parent 8501342fc5
commit cf60699884
7 changed files with 93 additions and 390 deletions

View File

@ -31,15 +31,15 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
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("ffA: %s\n", log_id(st.ffA, "--"));
log_debug("ffB: %s\n", log_id(st.ffB, "--"));
log_debug("ffCD: %s\n", log_id(st.ffCD, "--"));
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("ffO: %s\n", log_id(st.ffO, "--"));
log_debug("\n");
if (GetSize(st.sigA) > 16) {
@ -97,16 +97,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setParam(ID(D_REG), st.ffCD ? State::S1 : 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));
if (st.ffA && st.ffA->hasPort(ID::EN))
AHOLD = st.ffA->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffA->getPort(ID::EN)) : st.ffA->getPort(ID::EN);
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));
if (st.ffB && st.ffB->hasPort(ID::EN))
BHOLD = st.ffB->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffB->getPort(ID::EN)) : st.ffB->getPort(ID::EN);
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));
if (st.ffCD && st.ffCD->hasPort(ID::EN))
CDHOLD = st.ffCD->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffCD->getPort(ID::EN)) : st.ffCD->getPort(ID::EN);
else
CDHOLD = State::S0;
cell->setPort(ID(AHOLD), AHOLD);
@ -115,12 +115,12 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort(ID(DHOLD), CDHOLD);
SigSpec IRSTTOP, IRSTBOT;
if (st.ffArstmux)
IRSTTOP = st.ffArstpol ? st.ffArstmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffArstmux->getPort(ID::S));
if (st.ffA && st.ffA->hasPort(ID::ARST))
IRSTTOP = st.ffA->getParam(ID::ARST_POLARITY).as_bool() ? st.ffA->getPort(ID::ARST) : pm.module->Not(NEW_ID, st.ffA->getPort(ID::ARST));
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));
if (st.ffB && st.ffB->hasPort(ID::ARST))
IRSTBOT = st.ffB->getParam(ID::ARST_POLARITY).as_bool() ? st.ffB->getPort(ID::ARST) : pm.module->Not(NEW_ID, st.ffB->getPort(ID::ARST));
else
IRSTBOT = State::S0;
cell->setPort(ID(IRSTTOP), IRSTTOP);
@ -207,16 +207,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
}
SigSpec OHOLD;
if (st.ffOholdmux)
OHOLD = st.ffOholdpol ? st.ffOholdmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffOholdmux->getPort(ID::S));
if (st.ffO && st.ffO->hasPort(ID::EN))
OHOLD = st.ffO->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffO->getPort(ID::EN)) : st.ffO->getPort(ID::EN);
else
OHOLD = State::S0;
cell->setPort(ID(OHOLDTOP), OHOLD);
cell->setPort(ID(OHOLDBOT), OHOLD);
SigSpec ORST;
if (st.ffOrstmux)
ORST = st.ffOrstpol ? st.ffOrstmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffOrstmux->getPort(ID::S));
if (st.ffO && st.ffO->hasPort(ID::ARST))
ORST = st.ffO->getParam(ID::ARST_POLARITY).as_bool() ? st.ffO->getPort(ID::ARST) : pm.module->Not(NEW_ID, st.ffO->getPort(ID::ARST));
else
ORST = State::S0;
cell->setPort(ID(ORSTTOP), ORST);
@ -228,6 +228,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
acc_reset = st.mux->getPort(ID::S);
else
acc_reset = pm.module->Not(NEW_ID, st.mux->getPort(ID::S));
} else if (st.ffO && st.ffO->hasPort(ID::SRST)) {
acc_reset = st.ffO->getParam(ID::SRST_POLARITY).as_bool() ? st.ffO->getPort(ID::SRST) : pm.module->Not(NEW_ID, st.ffO->getPort(ID::SRST));
}
cell->setPort(ID(OLOADTOP), acc_reset);
cell->setPort(ID(OLOADBOT), acc_reset);

View File

@ -6,20 +6,16 @@ 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
state <Cell*> ffA ffB ffCD
state <Cell*> ffFJKG ffH ffO
// subpattern
state <bool> argSdff
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
udata <Cell*> dff
udata <bool> dffclock_pol
match mul
select mul->type.in($mul, \SB_MAC16)
@ -64,7 +60,7 @@ code sigA sigB sigH
log_assert(nusers(O.extract_end(i)) <= 1);
endcode
code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
code argQ ffA sigA clock clock_pol
if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) {
argQ = sigA;
subpattern(in_dffe);
@ -72,20 +68,12 @@ code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
ffA = dff;
clock = dffclock;
clock_pol = dffclock_pol;
if (dffrstmux) {
ffArstmux = dffrstmux;
ffArstpol = dffrstpol;
}
if (dffholdmux) {
ffAholdmux = dffholdmux;
ffAholdpol = dffholdpol;
}
sigA = dffD;
}
}
endcode
code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
code argQ ffB sigB clock clock_pol
if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) {
argQ = sigB;
subpattern(in_dffe);
@ -93,47 +81,44 @@ code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
ffB = dff;
clock = dffclock;
clock_pol = dffclock_pol;
if (dffrstmux) {
ffBrstmux = dffrstmux;
ffBrstpol = dffrstpol;
}
if (dffholdmux) {
ffBholdmux = dffholdmux;
ffBholdpol = dffholdpol;
}
sigB = dffD;
}
}
endcode
code argD ffFJKG sigH clock clock_pol
code argD argSdff 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;
argSdff = false;
subpattern(out_dffe);
if (dff) {
// F/J/K/G do not have a CE-like (hold) input
if (dffholdmux)
if (dff->hasPort(\EN))
goto reject_ffFJKG;
// 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)
if (ffA) {
if (ffA->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffFJKG;
if (ffA->hasPort(\ARST)) {
if (port(ffA, \ARST) != port(dff, \ARST))
goto reject_ffFJKG;
if (param(ffA, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
goto reject_ffFJKG;
}
}
if (ffBrstmux) {
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
goto reject_ffFJKG;
if (ffBrstpol != dffrstpol)
if (ffB) {
if (ffB->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffFJKG;
if (ffB->hasPort(\ARST)) {
if (port(ffB, \ARST) != port(dff, \ARST))
goto reject_ffFJKG;
if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
goto reject_ffFJKG;
}
}
ffFJKG = dff;
@ -146,23 +131,24 @@ reject_ffFJKG: ;
}
endcode
code argD ffH sigH sigO clock clock_pol
code argD argSdff 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;
argSdff = false;
subpattern(out_dffe);
if (dff) {
// H does not have a CE-like (hold) input
if (dffholdmux)
if (dff->hasPort(\EN))
goto reject_ffH;
// Reset signal of H (IRSTBOT) shared with B
if ((ffBrstmux != NULL) != (dffrstmux != NULL))
if (ffB->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffH;
if (ffBrstmux) {
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
if (ffB->hasPort(\ARST)) {
if (port(ffB, \ARST) != port(dff, \ARST))
goto reject_ffH;
if (ffBrstpol != dffrstpol)
if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
goto reject_ffH;
}
@ -226,7 +212,7 @@ code sigO
sigO = port(mux, \Y);
endcode
code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_pol cd_signed o_lo
code argD argSdff ffO 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).as_int() != 1 && param(mul, \BOTOUTPUT_SELECT).as_int() != 1) &&
@ -238,6 +224,7 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p
// First try entire sigO
if (nusers(sigO) == 2) {
argD = sigO;
argSdff = !mux;
subpattern(out_dffe);
}
@ -245,6 +232,7 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p
if (!dff && GetSize(sigO) > 16) {
argD = sigO.extract(0, 16);
if (nusers(argD) == 2) {
argSdff = !mux;
subpattern(out_dffe);
o_lo = dff;
}
@ -254,14 +242,6 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p
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);
}
@ -273,39 +253,44 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p
reject;
sigCD = port(mux, muxAB == \B ? \A : \B);
cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool();
} else if (dff && dff->hasPort(\SRST)) {
if (sigCD != sigO)
reject;
sigCD = param(dff, \SRST_VALUE);
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
code argQ ffCD 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)
if (ffA) {
if (ffA->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffCD;
if (ffA->hasPort(\ARST)) {
if (port(ffA, \ARST) != port(dff, \ARST))
goto reject_ffCD;
if (param(ffA, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
goto reject_ffCD;
}
}
if (ffBrstmux) {
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
goto reject_ffCD;
if (ffBrstpol != dffrstpol)
if (ffB) {
if (ffB->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffCD;
if (ffB->hasPort(\ARST)) {
if (port(ffB, \ARST) != port(dff, \ARST))
goto reject_ffCD;
if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
goto reject_ffCD;
}
}
ffCD = dff;
@ -347,7 +332,7 @@ code
endcode
match ff
select ff->type.in($dff)
select ff->type.in($dff, $dffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
@ -357,8 +342,6 @@ match ff
// 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
@ -378,72 +361,13 @@ code argQ argD
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
arg argD argSdff argQ clock clock_pol
code
dff = nullptr;
@ -452,101 +376,19 @@ code
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)
select ff->type.in($dff, $dffe, $sdff, $sdffce)
// SB_MAC16 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
// Only allow sync reset if requested.
filter argSdff || ff->type.in($dff, $dffe)
// 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
@ -559,10 +401,8 @@ code argQ
}
SigSpec D = port(ff, \D);
SigSpec Q = port(ff, \Q);
if (!ffholdmux) {
argQ = argD;
argQ.replace(D, Q);
}
argQ = argD;
argQ.replace(D, Q);
for (auto c : argQ.chunks()) {
Const init = c.wire->attributes.at(\init, State::Sx);
@ -575,7 +415,4 @@ code 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

@ -1,7 +1,6 @@
OBJS += techlibs/ice40/synth_ice40.o
OBJS += techlibs/ice40/ice40_braminit.o
OBJS += techlibs/ice40/ice40_ffssr.o
OBJS += techlibs/ice40/ice40_opt.o
GENFILES += techlibs/ice40/brams_init1.vh

View File

@ -1,131 +0,0 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
*
* 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
struct Ice40FfssrPass : public Pass {
Ice40FfssrPass() : Pass("ice40_ffssr", "iCE40: merge synchronous set/reset into FF cells") { }
void help() override
{
log("\n");
log(" ice40_ffssr [options] [selection]\n");
log("\n");
log("Merge synchronous set/reset $_MUX_ cells into iCE40 FFs.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing ICE40_FFSSR pass (merge synchronous set/reset into FF cells).\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);
pool<IdString> sb_dff_types;
sb_dff_types.insert(ID(SB_DFF));
sb_dff_types.insert(ID(SB_DFFE));
sb_dff_types.insert(ID(SB_DFFN));
sb_dff_types.insert(ID(SB_DFFNE));
for (auto module : design->selected_modules())
{
log("Merging set/reset $_MUX_ cells into SB_FFs in %s.\n", log_id(module));
SigMap sigmap(module);
dict<SigBit, Cell*> sr_muxes;
vector<Cell*> ff_cells;
for (auto cell : module->selected_cells())
{
if (sb_dff_types.count(cell->type)) {
ff_cells.push_back(cell);
continue;
}
if (cell->type != ID($_MUX_))
continue;
SigBit bit_a = sigmap(cell->getPort(ID::A));
SigBit bit_b = sigmap(cell->getPort(ID::B));
if (bit_a.wire == nullptr || bit_b.wire == nullptr)
sr_muxes[sigmap(cell->getPort(ID::Y))] = cell;
}
for (auto cell : ff_cells)
{
if (cell->get_bool_attribute(ID(dont_touch)))
continue;
SigSpec sig_d = cell->getPort(ID::D);
if (GetSize(sig_d) < 1)
continue;
SigBit bit_d = sigmap(sig_d[0]);
if (sr_muxes.count(bit_d) == 0)
continue;
Cell *mux_cell = sr_muxes.at(bit_d);
SigBit bit_a = sigmap(mux_cell->getPort(ID::A));
SigBit bit_b = sigmap(mux_cell->getPort(ID::B));
SigBit bit_s = sigmap(mux_cell->getPort(ID::S));
log(" Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell),
log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type));
SigBit sr_val, sr_sig;
if (bit_a.wire == nullptr) {
bit_d = bit_b;
sr_val = bit_a;
sr_sig = module->NotGate(NEW_ID, bit_s);
} else {
log_assert(bit_b.wire == nullptr);
bit_d = bit_a;
sr_val = bit_b;
sr_sig = bit_s;
}
if (sr_val == State::S1) {
cell->type = cell->type.str() + "SS";
cell->setPort(ID::S, sr_sig);
cell->setPort(ID::D, bit_d);
} else {
cell->type = cell->type.str() + "SR";
cell->setPort(ID::R, sr_sig);
cell->setPort(ID::D, bit_d);
}
}
}
}
} Ice40FfssrPass;
PRIVATE_NAMESPACE_END

View File

@ -215,7 +215,7 @@ struct Ice40OptPass : public Pass {
log(" <ice40 specific optimizations>\n");
log(" opt_expr -mux_undef -undriven [-full]\n");
log(" opt_merge\n");
log(" opt_rmdff\n");
log(" opt_dff\n");
log(" opt_clean\n");
log(" while <changed design>\n");
log("\n");
@ -247,7 +247,7 @@ struct Ice40OptPass : public Pass {
Pass::call(design, "opt_expr " + opt_expr_args);
Pass::call(design, "opt_merge");
Pass::call(design, "opt_rmdff");
Pass::call(design, "opt_dff");
Pass::call(design, "opt_clean");
if (design->scratchpad_get_bool("opt.did_something") == false)

View File

@ -293,6 +293,10 @@ struct SynthIce40Pass : public ScriptPass
run("opt_clean");
run("check");
run("opt");
run("fsm");
run("opt");
run("opt_dff");
run("opt");
run("wreduce");
run("peepopt");
run("opt_clean");
@ -316,8 +320,6 @@ struct SynthIce40Pass : public ScriptPass
}
run("alumacc");
run("opt");
run("fsm");
run("opt -fast");
run("memory -nomap");
run("opt_clean");
}
@ -354,11 +356,6 @@ struct SynthIce40Pass : public ScriptPass
if (check_label("map_ffs"))
{
if (!nodffe)
run("dff2dffe -direct-match $_DFF_*");
if (min_ce_use >= 0) {
run("opt_merge");
}
if (nodffe)
run(stringf("dfflegalize -cell $_DFF_?_ 0 -cell $_DFF_?P?_ 0 -cell $_SDFF_?P?_ 0 -cell $_DLATCH_?_ x"));
else
@ -366,7 +363,6 @@ struct SynthIce40Pass : public ScriptPass
run("techmap -map +/ice40/ff_map.v");
run("opt_expr -mux_undef");
run("simplemap");
run("ice40_ffssr");
run("ice40_opt -full");
}

View File

@ -12,5 +12,5 @@ cd fsm # Constrain all select calls below inside the top module
select -assert-count 4 t:SB_DFF
select -assert-count 2 t:SB_DFFESR
select -assert-count 15 t:SB_LUT4
select -assert-max 15 t:SB_LUT4
select -assert-none t:SB_DFFESR t:SB_DFF t:SB_LUT4 %% t:* %D