mirror of
https://github.com/YosysHQ/yosys
synced 2025-08-17 08:42:16 +00:00
Merge upstream
This commit is contained in:
parent
12137c7ac4
commit
881080a827
43 changed files with 510 additions and 94 deletions
|
@ -14,3 +14,13 @@ $(eval $(call add_share_file,share/ice40,techlibs/ice40/spram.txt))
|
|||
$(eval $(call add_share_file,share/ice40,techlibs/ice40/spram_map.v))
|
||||
$(eval $(call add_share_file,share/ice40,techlibs/ice40/dsp_map.v))
|
||||
$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc9_model.v))
|
||||
|
||||
OBJS += techlibs/ice40/ice40_dsp.o
|
||||
GENFILES += techlibs/ice40/ice40_dsp_pm.h
|
||||
techlibs/ice40/ice40_dsp.o: techlibs/ice40/ice40_dsp_pm.h
|
||||
$(eval $(call add_extra_objs,techlibs/ice40/ice40_dsp_pm.h))
|
||||
|
||||
OBJS += techlibs/ice40/ice40_wrapcarry.o
|
||||
GENFILES += techlibs/ice40/ice40_wrapcarry_pm.h
|
||||
techlibs/ice40/ice40_wrapcarry.o: techlibs/ice40/ice40_wrapcarry_pm.h
|
||||
$(eval $(call add_extra_objs,techlibs/ice40/ice40_wrapcarry_pm.h))
|
||||
|
|
319
techlibs/ice40/ice40_dsp.cc
Normal file
319
techlibs/ice40/ice40_dsp.cc
Normal file
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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 "techlibs/ice40/ice40_dsp_pm.h"
|
||||
|
||||
void create_ice40_dsp(ice40_dsp_pm &pm)
|
||||
{
|
||||
auto &st = pm.st_ice40_dsp;
|
||||
|
||||
log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul));
|
||||
|
||||
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\n", log_id(st.ffO, "--"));
|
||||
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;
|
||||
}
|
||||
|
||||
if (GetSize(st.sigB) > 16) {
|
||||
log(" input B (%s) is too large (%d > 16).\n", log_signal(st.sigB), GetSize(st.sigB));
|
||||
return;
|
||||
}
|
||||
|
||||
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.sigH) > 32) {
|
||||
log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigH), GetSize(st.sigH));
|
||||
return;
|
||||
}
|
||||
|
||||
Cell *cell = st.mul;
|
||||
if (cell->type == ID($mul)) {
|
||||
log(" replacing %s with SB_MAC16 cell.\n", log_id(st.mul->type));
|
||||
|
||||
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, st.mul->getParam(ID::A_SIGNED).as_bool());
|
||||
log_assert(GetSize(A) == 16);
|
||||
|
||||
SigSpec B = st.sigB;
|
||||
B.extend_u0(16, st.mul->getParam(ID::B_SIGNED).as_bool());
|
||||
log_assert(GetSize(B) == 16);
|
||||
|
||||
SigSpec CD = st.sigCD;
|
||||
if (CD.empty())
|
||||
CD = RTLIL::Const(0, 32);
|
||||
else
|
||||
log_assert(GetSize(CD) == 32);
|
||||
|
||||
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(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);
|
||||
|
||||
SigSpec AHOLD, BHOLD, CDHOLD;
|
||||
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.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.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);
|
||||
cell->setPort(ID(BHOLD), BHOLD);
|
||||
cell->setPort(ID(CHOLD), CDHOLD);
|
||||
cell->setPort(ID(DHOLD), CDHOLD);
|
||||
|
||||
SigSpec IRSTTOP, IRSTBOT;
|
||||
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.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);
|
||||
cell->setPort(ID(IRSTBOT), IRSTBOT);
|
||||
|
||||
if (st.clock != SigBit())
|
||||
{
|
||||
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");
|
||||
|
||||
if (st.ffA)
|
||||
log(" ffA:%s", log_id(st.ffA));
|
||||
|
||||
if (st.ffB)
|
||||
log(" ffB:%s", log_id(st.ffB));
|
||||
|
||||
if (st.ffCD)
|
||||
log(" ffCD:%s", log_id(st.ffCD));
|
||||
|
||||
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(ID::CLK, State::S0);
|
||||
cell->setPort(ID(CE), State::S0);
|
||||
cell->setParam(ID(NEG_TRIGGER), State::S0);
|
||||
}
|
||||
|
||||
// SB_MAC16 Cascade Interface
|
||||
|
||||
cell->setPort(ID(SIGNEXTIN), State::Sx);
|
||||
cell->setPort(ID(SIGNEXTOUT), pm.module->addWire(NEW_ID));
|
||||
|
||||
cell->setPort(ID::CI, State::Sx);
|
||||
|
||||
cell->setPort(ID(ACCUMCI), State::Sx);
|
||||
cell->setPort(ID(ACCUMCO), pm.module->addWire(NEW_ID));
|
||||
|
||||
// SB_MAC16 Output Interface
|
||||
|
||||
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(ID::O, O);
|
||||
|
||||
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(ID(ADDSUBTOP), State::S0);
|
||||
cell->setPort(ID(ADDSUBBOT), State::S0);
|
||||
}
|
||||
|
||||
SigSpec OHOLD;
|
||||
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.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);
|
||||
cell->setPort(ID(ORSTBOT), ORST);
|
||||
|
||||
SigSpec acc_reset = State::S0;
|
||||
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));
|
||||
} 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);
|
||||
|
||||
// SB_MAC16 Remaining Parameters
|
||||
|
||||
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(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(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(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());
|
||||
|
||||
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));
|
||||
|
||||
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 {
|
||||
Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { }
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" ice40_dsp [options] [selection]\n");
|
||||
log("\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 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) override
|
||||
{
|
||||
log_header(design, "Executing ICE40_DSP pass (map multipliers).\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())
|
||||
ice40_dsp_pm(module, module->selected_cells()).run_ice40_dsp(create_ice40_dsp);
|
||||
}
|
||||
} Ice40DspPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
417
techlibs/ice40/ice40_dsp.pmg
Normal file
417
techlibs/ice40/ice40_dsp.pmg
Normal file
|
@ -0,0 +1,417 @@
|
|||
pattern ice40_dsp
|
||||
|
||||
state <SigBit> clock
|
||||
state <bool> clock_pol cd_signed o_lo
|
||||
state <SigSpec> sigA sigB sigCD sigH sigO
|
||||
state <Cell*> add mux
|
||||
state <IdString> addAB muxAB
|
||||
|
||||
state <Cell*> ffA ffB ffCD
|
||||
state <Cell*> ffFJKG ffH ffO
|
||||
|
||||
// subpattern
|
||||
state <bool> argSdff
|
||||
state <SigSpec> argQ argD
|
||||
udata <SigSpec> dffD dffQ
|
||||
udata <SigBit> dffclock
|
||||
udata <Cell*> dff
|
||||
udata <bool> dffclock_pol
|
||||
|
||||
match mul
|
||||
select mul->type.in($mul, \SB_MAC16)
|
||||
select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
|
||||
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 sign bit
|
||||
++i;
|
||||
return sig.extract(0, i);
|
||||
};
|
||||
sigA = unextend(port(mul, \A));
|
||||
sigB = unextend(port(mul, \B));
|
||||
|
||||
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;
|
||||
|
||||
// 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]);
|
||||
}
|
||||
// This sigM could have no users if downstream sinks (e.g. $add) is
|
||||
// narrower than $mul result, for example
|
||||
if (i == 0)
|
||||
reject;
|
||||
|
||||
log_assert(nusers(O.extract_end(i)) <= 1);
|
||||
endcode
|
||||
|
||||
code argQ ffA 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;
|
||||
sigA = dffD;
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
code argQ ffB 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;
|
||||
sigB = dffD;
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
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 (dff->hasPort(\EN))
|
||||
goto reject_ffFJKG;
|
||||
|
||||
// Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT)
|
||||
// shared with A and B
|
||||
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 (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;
|
||||
clock = dffclock;
|
||||
clock_pol = dffclock_pol;
|
||||
sigH = dffQ;
|
||||
|
||||
reject_ffFJKG: ;
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
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 (dff->hasPort(\EN))
|
||||
goto reject_ffH;
|
||||
|
||||
// Reset signal of H (IRSTBOT) shared with B
|
||||
if (ffB->hasPort(\ARST) != dff->hasPort(\ARST))
|
||||
goto reject_ffH;
|
||||
if (ffB->hasPort(\ARST)) {
|
||||
if (port(ffB, \ARST) != port(dff, \ARST))
|
||||
goto reject_ffH;
|
||||
if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
|
||||
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
|
||||
|
||||
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();
|
||||
|
||||
int natural_mul_width = GetSize(sigA) + GetSize(sigB);
|
||||
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 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 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
|
||||
|
||||
code sigO
|
||||
if (mux)
|
||||
sigO = port(mux, \Y);
|
||||
endcode
|
||||
|
||||
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) &&
|
||||
// Ensure that OLOADTOP/OLOADBOT is unused or zero
|
||||
(port(mul, \OLOADTOP, State::S0).is_fully_zero() && port(mul, \OLOADBOT, State::S0).is_fully_zero()))) {
|
||||
|
||||
dff = nullptr;
|
||||
|
||||
// First try entire sigO
|
||||
if (nusers(sigO) == 2) {
|
||||
argD = sigO;
|
||||
argSdff = !mux;
|
||||
subpattern(out_dffe);
|
||||
}
|
||||
|
||||
// Otherwise try just its least significant 16 bits
|
||||
if (!dff && GetSize(sigO) > 16) {
|
||||
argD = sigO.extract(0, 16);
|
||||
if (nusers(argD) == 2) {
|
||||
argSdff = !mux;
|
||||
subpattern(out_dffe);
|
||||
o_lo = dff;
|
||||
}
|
||||
}
|
||||
|
||||
if (dff) {
|
||||
ffO = dff;
|
||||
clock = dffclock;
|
||||
clock_pol = dffclock_pol;
|
||||
|
||||
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();
|
||||
} 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 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) {
|
||||
// Reset signal of C (IRSTTOP) and D (IRSTBOT)
|
||||
// shared with A and B
|
||||
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 (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;
|
||||
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;
|
||||
if (argQ.empty())
|
||||
reject;
|
||||
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, $dffe)
|
||||
// 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
|
||||
endmatch
|
||||
|
||||
code argQ argD
|
||||
{
|
||||
if (clock != SigBit()) {
|
||||
if (port(ff, \CLK)[0] != 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);
|
||||
}
|
||||
endcode
|
||||
|
||||
// #######################
|
||||
|
||||
subpattern out_dffe
|
||||
arg argD argSdff argQ clock clock_pol
|
||||
|
||||
code
|
||||
dff = nullptr;
|
||||
for (auto c : argD.chunks())
|
||||
if (c.wire->get_bool_attribute(\keep))
|
||||
reject;
|
||||
endcode
|
||||
|
||||
match ff
|
||||
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]
|
||||
|
||||
// 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
|
||||
endmatch
|
||||
|
||||
code argQ
|
||||
if (ff) {
|
||||
if (clock != SigBit()) {
|
||||
if (port(ff, \CLK)[0] != clock)
|
||||
reject;
|
||||
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
|
||||
reject;
|
||||
}
|
||||
SigSpec D = port(ff, \D);
|
||||
SigSpec Q = port(ff, \Q);
|
||||
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();
|
||||
}
|
||||
endcode
|
158
techlibs/ice40/ice40_wrapcarry.cc
Normal file
158
techlibs/ice40/ice40_wrapcarry.cc
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.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 "techlibs/ice40/ice40_wrapcarry_pm.h"
|
||||
|
||||
void create_ice40_wrapcarry(ice40_wrapcarry_pm &pm)
|
||||
{
|
||||
auto &st = pm.st_ice40_wrapcarry;
|
||||
|
||||
#if 0
|
||||
log("\n");
|
||||
log("carry: %s\n", log_id(st.carry, "--"));
|
||||
log("lut: %s\n", log_id(st.lut, "--"));
|
||||
#endif
|
||||
|
||||
log(" replacing SB_LUT + SB_CARRY with $__ICE40_CARRY_WRAPPER cell.\n");
|
||||
|
||||
Cell *cell = pm.module->addCell(NEW_ID, ID($__ICE40_CARRY_WRAPPER));
|
||||
pm.module->swap_names(cell, st.carry);
|
||||
|
||||
cell->setPort(ID::A, st.carry->getPort(ID(I0)));
|
||||
cell->setPort(ID::B, st.carry->getPort(ID(I1)));
|
||||
auto CI = st.carry->getPort(ID::CI);
|
||||
cell->setPort(ID::CI, CI);
|
||||
cell->setPort(ID::CO, st.carry->getPort(ID::CO));
|
||||
|
||||
cell->setPort(ID(I0), st.lut->getPort(ID(I0)));
|
||||
auto I3 = st.lut->getPort(ID(I3));
|
||||
if (pm.sigmap(CI) == pm.sigmap(I3)) {
|
||||
cell->setParam(ID(I3_IS_CI), State::S1);
|
||||
I3 = State::Sx;
|
||||
}
|
||||
else
|
||||
cell->setParam(ID(I3_IS_CI), State::S0);
|
||||
cell->setPort(ID(I3), I3);
|
||||
cell->setPort(ID::O, st.lut->getPort(ID::O));
|
||||
cell->setParam(ID::LUT, st.lut->getParam(ID(LUT_INIT)));
|
||||
|
||||
for (const auto &a : st.carry->attributes)
|
||||
cell->attributes[stringf("\\SB_CARRY.%s", a.first.c_str())] = a.second;
|
||||
for (const auto &a : st.lut->attributes)
|
||||
cell->attributes[stringf("\\SB_LUT4.%s", a.first.c_str())] = a.second;
|
||||
cell->attributes[ID(SB_LUT4.name)] = Const(st.lut->name.str());
|
||||
if (st.carry->get_bool_attribute(ID::keep) || st.lut->get_bool_attribute(ID::keep))
|
||||
cell->attributes[ID::keep] = true;
|
||||
|
||||
pm.autoremove(st.carry);
|
||||
pm.autoremove(st.lut);
|
||||
}
|
||||
|
||||
struct Ice40WrapCarryPass : public Pass {
|
||||
Ice40WrapCarryPass() : Pass("ice40_wrapcarry", "iCE40: wrap carries") { }
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" ice40_wrapcarry [selection]\n");
|
||||
log("\n");
|
||||
log("Wrap manually instantiated SB_CARRY cells, along with their associated SB_LUT4s,\n");
|
||||
log("into an internal $__ICE40_CARRY_WRAPPER cell for preservation across technology\n");
|
||||
log("mapping.\n");
|
||||
log("\n");
|
||||
log("Attributes on both cells will have their names prefixed with 'SB_CARRY.' or\n");
|
||||
log("'SB_LUT4.' and attached to the wrapping cell.\n");
|
||||
log("A (* keep *) attribute on either cell will be logically OR-ed together.\n");
|
||||
log("\n");
|
||||
log(" -unwrap\n");
|
||||
log(" unwrap $__ICE40_CARRY_WRAPPER cells back into SB_CARRYs and SB_LUT4s,\n");
|
||||
log(" including restoring their attributes.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
bool unwrap = false;
|
||||
|
||||
log_header(design, "Executing ICE40_WRAPCARRY pass (wrap carries).\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-unwrap") {
|
||||
unwrap = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules()) {
|
||||
if (!unwrap)
|
||||
ice40_wrapcarry_pm(module, module->selected_cells()).run_ice40_wrapcarry(create_ice40_wrapcarry);
|
||||
else {
|
||||
for (auto cell : module->selected_cells()) {
|
||||
if (cell->type != ID($__ICE40_CARRY_WRAPPER))
|
||||
continue;
|
||||
|
||||
auto carry = module->addCell(NEW_ID, ID(SB_CARRY));
|
||||
carry->setPort(ID(I0), cell->getPort(ID::A));
|
||||
carry->setPort(ID(I1), cell->getPort(ID::B));
|
||||
carry->setPort(ID::CI, cell->getPort(ID::CI));
|
||||
carry->setPort(ID::CO, cell->getPort(ID::CO));
|
||||
module->swap_names(carry, cell);
|
||||
auto lut_name = cell->attributes.at(ID(SB_LUT4.name), Const(NEW_ID.str())).decode_string();
|
||||
auto lut = module->addCell(lut_name, ID($lut));
|
||||
lut->setParam(ID::WIDTH, 4);
|
||||
lut->setParam(ID::LUT, cell->getParam(ID::LUT));
|
||||
auto I3 = cell->getPort(cell->getParam(ID(I3_IS_CI)).as_bool() ? ID::CI : ID(I3));
|
||||
lut->setPort(ID::A, { I3, cell->getPort(ID::B), cell->getPort(ID::A), cell->getPort(ID(I0)) });
|
||||
lut->setPort(ID::Y, cell->getPort(ID::O));
|
||||
|
||||
Const src;
|
||||
for (const auto &a : cell->attributes)
|
||||
if (a.first.begins_with("\\SB_CARRY.\\"))
|
||||
carry->attributes[a.first.c_str() + strlen("\\SB_CARRY.")] = a.second;
|
||||
else if (a.first.begins_with("\\SB_LUT4.\\"))
|
||||
lut->attributes[a.first.c_str() + strlen("\\SB_LUT4.")] = a.second;
|
||||
else if (a.first == ID::src)
|
||||
src = a.second;
|
||||
else if (a.first.in(ID(SB_LUT4.name), ID::keep, ID::module_not_derived))
|
||||
continue;
|
||||
else
|
||||
log_abort();
|
||||
|
||||
if (!src.empty()) {
|
||||
carry->attributes.insert(std::make_pair(ID::src, src));
|
||||
lut->attributes.insert(std::make_pair(ID::src, src));
|
||||
}
|
||||
|
||||
module->remove(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} Ice40WrapCarryPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
15
techlibs/ice40/ice40_wrapcarry.pmg
Normal file
15
techlibs/ice40/ice40_wrapcarry.pmg
Normal file
|
@ -0,0 +1,15 @@
|
|||
pattern ice40_wrapcarry
|
||||
|
||||
match carry
|
||||
select carry->type.in(\SB_CARRY)
|
||||
endmatch
|
||||
|
||||
match lut
|
||||
select lut->type.in(\SB_LUT4)
|
||||
index <SigSpec> port(lut, \I1) === port(carry, \I0)
|
||||
index <SigSpec> port(lut, \I2) === port(carry, \I1)
|
||||
endmatch
|
||||
|
||||
code
|
||||
accept;
|
||||
endcode
|
Loading…
Add table
Add a link
Reference in a new issue