mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-11 03:33:36 +00:00
Merge c68fd85b9c
into f03b44959b
This commit is contained in:
commit
54d84cbc39
|
@ -11,9 +11,12 @@ OBJS += techlibs/quicklogic/ql_ioff.o
|
|||
# --------------------------------------
|
||||
|
||||
OBJS += techlibs/quicklogic/ql_dsp_macc.o
|
||||
GENFILES += techlibs/quicklogic/ql_dsp_macc_pm.h techlibs/quicklogic/qlf_k6n10f/bram_types_sim.v
|
||||
OBJS += techlibs/quicklogic/ql_dsp.o
|
||||
GENFILES += techlibs/quicklogic/ql_dsp_macc_pm.h techlibs/quicklogic/ql_dsp_pm.h techlibs/quicklogic/qlf_k6n10f/bram_types_sim.v
|
||||
techlibs/quicklogic/ql_dsp_macc.o: techlibs/quicklogic/ql_dsp_macc_pm.h
|
||||
techlibs/quicklogic/ql_dsp.o: techlibs/quicklogic/ql_dsp_pm.h
|
||||
$(eval $(call add_extra_objs,techlibs/quicklogic/ql_dsp_macc_pm.h))
|
||||
$(eval $(call add_extra_objs,techlibs/quicklogic/ql_dsp_pm.h))
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
|
@ -36,9 +39,13 @@ $(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf
|
|||
$(eval $(call add_gen_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/bram_types_sim.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/cells_sim.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/ffs_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dsp_sim.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dsp_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dsp_final_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dspv1_sim.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dspv1_sim_extra.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dspv1_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dspv1_final_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dspv2_sim.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dspv2_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dspv2_final_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/TDP18K_FIFO.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/ufifo_ctl.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/sram1024x18_mem.v))
|
||||
|
|
134
techlibs/quicklogic/ql_dsp.cc
Normal file
134
techlibs/quicklogic/ql_dsp.cc
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* 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/rtlil.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
||||
// promote dspv2_16x9x32_cfg_ports to dspv2_32x18x64_cfg_ports if need be
|
||||
bool promote(Module *m, Cell *cell) {
|
||||
if (cell->type == ID(dspv2_32x18x64_cfg_ports)) {
|
||||
return false;
|
||||
} else {
|
||||
log_assert(cell->type == ID(dspv2_16x9x32_cfg_ports));
|
||||
}
|
||||
|
||||
auto widen_output = [&](IdString port_name, int new_width) {
|
||||
if (!cell->hasPort(port_name))
|
||||
return;
|
||||
SigSpec port = cell->getPort(port_name);
|
||||
if (port.size() < new_width) {
|
||||
port = {m->addWire(NEW_ID, new_width - port.size()), port};
|
||||
cell->setPort(port_name, port);
|
||||
}
|
||||
};
|
||||
|
||||
auto widen_input = [&](IdString port_name, int new_width) {
|
||||
if (!cell->hasPort(port_name))
|
||||
return;
|
||||
SigSpec port = cell->getPort(port_name);
|
||||
if (port.size() < new_width) {
|
||||
port.extend_u0(new_width, /* is_signed= */ true);
|
||||
cell->setPort(port_name, port);
|
||||
}
|
||||
};
|
||||
|
||||
widen_output(ID(z_o), 50);
|
||||
widen_output(ID(a_cout_o), 32);
|
||||
widen_output(ID(b_cout_o), 18);
|
||||
widen_output(ID(z_cout_o), 50);
|
||||
|
||||
auto uses_port = [&](IdString port_name) {
|
||||
return cell->hasPort(port_name) && !cell->getPort(port_name).is_fully_undef();
|
||||
};
|
||||
|
||||
if (uses_port(ID(a_cin_i)) || uses_port(ID(b_cin_i)) || uses_port(ID(z_cin_i))) {
|
||||
log_error("Cannot promote %s (type %s) with cascading paths\n", log_id(cell), log_id(cell->type));
|
||||
}
|
||||
|
||||
widen_input(ID(a_i), 32);
|
||||
widen_input(ID(b_i), 18);
|
||||
widen_input(ID(c_i), 18);
|
||||
cell->type = ID(dspv2_32x18x64_cfg_ports);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool did_something;
|
||||
|
||||
#include "techlibs/quicklogic/ql_dsp_pm.h"
|
||||
|
||||
struct QlDspPass : Pass {
|
||||
QlDspPass() : Pass("ql_dsp", "pack into QuickLogic DSPs") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" ql_dsp [selection]\n");
|
||||
log("\n");
|
||||
log("This pass packs input and output path registers into QuickLogic DSP blocks,\n");
|
||||
log("additionally it supports Z path cascading and post-adder packing.\n");
|
||||
log("\n");
|
||||
log(" -nocascade\n");
|
||||
log(" forbid cascading\n");
|
||||
log("\n");
|
||||
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *d) override
|
||||
{
|
||||
log_header(d, "Executing QL_DSP pass. (pack into QuickLogic DSPs)\n");
|
||||
|
||||
bool nocascade = false;
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-nocascade") {
|
||||
nocascade = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, d);
|
||||
|
||||
for (auto module : d->selected_modules()) {
|
||||
did_something = true;
|
||||
|
||||
while (did_something)
|
||||
{
|
||||
// TODO: could be optimized by more reuse of the pmgen object
|
||||
did_something = false;
|
||||
{
|
||||
ql_dsp_pm pm(module, module->selected_cells());
|
||||
pm.run_ql_dsp_pack_regs();
|
||||
}
|
||||
if (!nocascade) {
|
||||
ql_dsp_pm pm(module, module->selected_cells());
|
||||
pm.run_ql_dsp_cascade();
|
||||
}
|
||||
{
|
||||
ql_dsp_pm pm(module, module->selected_cells());
|
||||
pm.run_ql_dsp_pack_regs();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} QlDspPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
272
techlibs/quicklogic/ql_dsp.pmg
Normal file
272
techlibs/quicklogic/ql_dsp.pmg
Normal file
|
@ -0,0 +1,272 @@
|
|||
// derived from passes/pmgen/xilinx_dsp.pmg
|
||||
pattern ql_dsp_pack_regs
|
||||
|
||||
state <SigBit> clock reset
|
||||
state <bool> clock_inferred
|
||||
|
||||
// Variables used for subpatterns
|
||||
state <SigSpec> argQ argD
|
||||
udata <SigSpec> dffD dffQ
|
||||
udata <SigBit> dffclock dffreset
|
||||
udata <Cell*> dff
|
||||
|
||||
match dsp
|
||||
select dsp->type.in(\dspv2_32x18x64_cfg_ports, \dspv2_16x9x32_cfg_ports)
|
||||
endmatch
|
||||
|
||||
code clock_inferred clock reset
|
||||
clock_inferred = false;
|
||||
clock = port(dsp, \clock_i);
|
||||
reset = port(dsp, \reset_i);
|
||||
endcode
|
||||
|
||||
// try packing on Z output
|
||||
code argD clock_inferred clock reset
|
||||
if (port(dsp, \output_select_i)[2] == RTLIL::S0 &&
|
||||
(!dsp->hasPort(\z_cout_o) || nusers(port(dsp, \z_cout_o)) == 1) &&
|
||||
nusers(port(dsp, \z_o)) == 2) {
|
||||
argD = port(dsp, \z_o);
|
||||
subpattern(out_dffe);
|
||||
if (dff) {
|
||||
clock_inferred = true;
|
||||
clock = dffclock;
|
||||
reset = dffreset;
|
||||
log("%s: inferring Z path register from flip-flop %s\n", log_id(dsp), log_id(dff));
|
||||
dsp->connections_[\output_select_i][2] = RTLIL::S1;
|
||||
dsp->setPort(\z_o, dffQ);
|
||||
did_something = true;
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
// try packing on B input
|
||||
code argQ clock_inferred clock reset
|
||||
if ((!dsp->hasPort(\b_cout_o) || nusers(port(dsp, \b_cout_o)) == 1) &&
|
||||
!param(dsp, \B_REG).as_bool()) {
|
||||
argQ = port(dsp, \b_i);
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
clock_inferred = true;
|
||||
clock = dffclock;
|
||||
reset = dffreset;
|
||||
log("%s: inferring B path register from flip-flop %s\n", log_id(dsp), log_id(dff));
|
||||
dsp->parameters[\B_REG] = Const(1, 1);
|
||||
dsp->setPort(\b_i, dffD);
|
||||
did_something = true;
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
// try packing on A input
|
||||
code argQ clock_inferred clock reset
|
||||
if ((!dsp->hasPort(\a_cout_o) || nusers(port(dsp, \a_cout_o)) == 1) &&
|
||||
!param(dsp, \A_REG).as_bool()) {
|
||||
argQ = port(dsp, \a_i);
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
clock_inferred = true;
|
||||
clock = dffclock;
|
||||
reset = dffreset;
|
||||
log("%s: inferring A path register from flip-flop %s\n", log_id(dsp), log_id(dff));
|
||||
dsp->parameters[\A_REG] = Const(1, 1);
|
||||
dsp->setPort(\a_i, dffD);
|
||||
did_something = true;
|
||||
}
|
||||
}
|
||||
endcode
|
||||
|
||||
code
|
||||
if (clock_inferred) {
|
||||
dsp->setPort(\clock_i, clock);
|
||||
dsp->setPort(\reset_i, reset);
|
||||
}
|
||||
endcode
|
||||
|
||||
// #######################
|
||||
// Subpattern for matching against input registers, based on knowledge of the
|
||||
// 'Q' output.
|
||||
subpattern in_dffe
|
||||
arg argQ clock reset
|
||||
|
||||
code
|
||||
dff = nullptr;
|
||||
if (argQ.empty())
|
||||
reject;
|
||||
for (const auto &c : argQ.chunks()) {
|
||||
if (!c.wire) {
|
||||
// Abandon matches when constant Q bits are non-zero
|
||||
// (doesn't match DSPv2 init/reset behavior)
|
||||
if (!SigSpec(c).is_fully_zero())
|
||||
reject;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Abandon matches when 'Q' has the keep attribute set
|
||||
if (c.wire->get_bool_attribute(\keep))
|
||||
reject;
|
||||
// Abandon matches when 'Q' has a non-zero init attribute set (not supported by DSPv2)
|
||||
Const init = c.wire->attributes.at(\init, Const());
|
||||
if (!init.empty())
|
||||
for (auto b : init.extract(c.offset, c.width))
|
||||
if (b != State::Sx && b != State::S0)
|
||||
reject;
|
||||
}
|
||||
endcode
|
||||
|
||||
match ff
|
||||
select ff->type.in($dff, $dffe, $adff, $adffe)
|
||||
// DSPv2 does not support polarity inversion
|
||||
select param(ff, \CLK_POLARITY).as_bool()
|
||||
|
||||
// Check that reset value, if present, is fully 0.
|
||||
filter ff->type.in($dff, $dffe) || param(ff, \ARST_VALUE).is_fully_zero()
|
||||
|
||||
// Check reset polarity, if present
|
||||
filter ff->type.in($dff, $dffe) || param(ff, \ARST_POLARITY).as_bool()
|
||||
|
||||
// Check that the LSB argQ bit is present (the rest follow by the nusers(...)=2 condition)
|
||||
slice offset GetSize(port(ff, \D))
|
||||
index <SigBit> port(ff, \Q)[offset] === argQ[0]
|
||||
|
||||
define <SigBit> ff_reset (ff->type.in($dff, $dffe) ? RTLIL::S0 : port(ff, \ARST))
|
||||
filter clock == RTLIL::Sx || port(ff, \CLK)[0] == clock
|
||||
filter clock == RTLIL::Sx || ff_reset == reset
|
||||
endmatch
|
||||
|
||||
code argD
|
||||
dff = ff;
|
||||
dffclock = port(ff, \CLK);
|
||||
dffreset = (ff->type.in($dff, $dffe) ? RTLIL::S0 : port(ff, \ARST));
|
||||
dffD = argQ;
|
||||
dffD.replace(port(ff, \Q), port(ff, \D));
|
||||
endcode
|
||||
|
||||
|
||||
// #######################
|
||||
// Subpattern for matching against output registers, based on knowledge of the
|
||||
// 'D' input.
|
||||
|
||||
subpattern out_dffe
|
||||
arg argD clock reset
|
||||
|
||||
code
|
||||
dff = nullptr;
|
||||
if (argD.empty())
|
||||
reject;
|
||||
for (const auto &c : argD.chunks()) {
|
||||
// Abandon matches when 'D' has the keep attribute set
|
||||
if (!c.wire || c.wire->get_bool_attribute(\keep))
|
||||
reject;
|
||||
}
|
||||
endcode
|
||||
|
||||
match ff
|
||||
select ff->type.in($dff, $dffe, $adff, $adffe)
|
||||
// DSPv2 does not support polarity inversion
|
||||
select param(ff, \CLK_POLARITY).as_bool()
|
||||
|
||||
// Check that reset value, if present, is fully 0.
|
||||
filter ff->type.in($dff, $dffe) || param(ff, \ARST_VALUE).is_fully_zero()
|
||||
|
||||
// Check reset polarity, if present
|
||||
filter ff->type.in($dff, $dffe) || param(ff, \ARST_POLARITY).as_bool()
|
||||
|
||||
slice offset GetSize(port(ff, \D))
|
||||
index <SigBit> port(ff, \D)[offset] === argD[0]
|
||||
|
||||
define <SigBit> ff_reset (ff->type.in($dff, $dffe) ? RTLIL::S0 : port(ff, \ARST))
|
||||
filter clock == RTLIL::Sx || port(ff, \CLK)[0] == clock
|
||||
filter clock == RTLIL::Sx || ff_reset == reset
|
||||
endmatch
|
||||
|
||||
code
|
||||
dff = ff;
|
||||
dffclock = port(ff, \CLK);
|
||||
dffreset = (ff->type.in($dff, $dffe) ? RTLIL::S0 : port(ff, \ARST));
|
||||
dffQ = argD;
|
||||
dffQ.replace(port(ff, \D), port(ff, \Q));
|
||||
|
||||
// Abandon matches when 'Q' has a defined init attribute set
|
||||
// (not supported by DSPv2)
|
||||
for (auto c : dffQ.chunks()) {
|
||||
Const init = c.wire->attributes.at(\init, Const());
|
||||
if (!init.empty())
|
||||
for (auto b : init.extract(c.offset, c.width))
|
||||
if (b != State::Sx)
|
||||
reject;
|
||||
}
|
||||
|
||||
{
|
||||
// Rewire retired flip-flop slice
|
||||
SigSpec D = port(ff, \D);
|
||||
SigSpec Q = port(ff, \Q);
|
||||
D.replace(argD, module->addWire(NEW_ID, argD.size()), &Q);
|
||||
D.replace(argD, Const(RTLIL::Sx, argD.size()));
|
||||
ff->setPort(\D, D);
|
||||
ff->setPort(\Q, Q);
|
||||
}
|
||||
endcode
|
||||
|
||||
pattern ql_dsp_cascade
|
||||
|
||||
match dsp1
|
||||
select dsp1->type.in(\dspv2_32x18x64_cfg_ports, \dspv2_16x9x32_cfg_ports)
|
||||
filter !dsp1->hasPort(\z_cout_o) || nusers(port(dsp1, \z_cout_o)) == 1
|
||||
endmatch
|
||||
|
||||
match dsp2
|
||||
select dsp2->type.in(\dspv2_32x18x64_cfg_ports, \dspv2_16x9x32_cfg_ports)
|
||||
filter port(dsp2, \output_select_i).is_fully_const()
|
||||
define <int> output_sel port(dsp2, \output_select_i).as_int()
|
||||
filter output_sel == 0 || (output_sel == 4 && !param(dsp2, \M_REG).as_bool())
|
||||
// expect `dsp2` and `add` for exclusive users
|
||||
filter nusers(port(dsp2, \z_o)) == 2
|
||||
filter !dsp2->hasPort(\z_cout_o) || nusers(port(dsp2, \z_cout_o)) == 1
|
||||
filter dsp1 != dsp2
|
||||
endmatch
|
||||
|
||||
match add
|
||||
select add->type.in($add, $sub)
|
||||
define <int> width param(add, \Y_WIDTH).as_int()
|
||||
|
||||
index <SigBit> port(add, \A)[0] === port(dsp1, \z_o)[0]
|
||||
filter port(add, \A).size() >= width && port(dsp1, \z_o).size() >= width
|
||||
filter port(add, \A).extract(0, width) == port(dsp1, \z_o).extract(0, width)
|
||||
|
||||
index <SigBit> port(add, \B)[0] === port(dsp2, \z_o)[0]
|
||||
filter port(add, \B).size() >= width && port(dsp2, \z_o).size() >= width
|
||||
filter port(add, \B).extract(0, width) == port(dsp2, \z_o).extract(0, width)
|
||||
endmatch
|
||||
|
||||
code
|
||||
const int z_width = 50;
|
||||
|
||||
log("%s: inferring post-adder from %s (type %s)\n", log_id(dsp2), log_id(add), log_id(add->type));
|
||||
if (promote(module, dsp1))
|
||||
log(" - promoting %s to non-fractured DSP block\n", log_id(dsp1));
|
||||
if (promote(module, dsp2))
|
||||
log(" - promoting %s to non-fractured DSP block\n", log_id(dsp2));
|
||||
|
||||
// link up z_cout_o of dsp1 to z_cin_i of dsp2
|
||||
Wire *link = module->addWire(NEW_ID, z_width);
|
||||
dsp1->setPort(\z_cout_o, link);
|
||||
dsp2->setPort(\z_cin_i, link);
|
||||
|
||||
// configure the path inside dsp2
|
||||
if (port(dsp2, \output_select_i).as_int() == 4) {
|
||||
log("%s: inferring M register\n", log_id(dsp2));
|
||||
dsp2->setParam(\M_REG, Const(1, 1));
|
||||
}
|
||||
dsp2->setParam(\SUBTRACT, Const(add->type == $sub, 1));
|
||||
dsp2->setPort(\feedback_i, Const(3, 3));
|
||||
dsp2->setPort(\output_select_i, Const(3, 3));
|
||||
dsp2->setParam(\ROUND, Const(0, 3));
|
||||
dsp2->setParam(\SHIFT_REG, Const(0, 6));
|
||||
dsp2->setParam(\SATURATE, Const(0, 1));
|
||||
dsp2->setParam(\ZCIN_REG, Const(1, 1));
|
||||
dsp2->setPort(\z_o, {port(dsp2, \z_o).extract_end(port(add, \Y).size()), port(add, \Y)});
|
||||
|
||||
did_something = true;
|
||||
autoremove(add);
|
||||
accept;
|
||||
endcode
|
|
@ -44,20 +44,32 @@ struct QlDspIORegs : public Pass {
|
|||
log("\n");
|
||||
log("This pass looks for QL_DSP2 cells and changes their cell type depending on their\n");
|
||||
log("configuration.\n");
|
||||
log("\n");
|
||||
log(" -dspv2\n");
|
||||
log(" target DSPv2.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override
|
||||
{
|
||||
log_header(a_Design, "Executing QL_DSP_IO_REGS pass.\n");
|
||||
|
||||
bool target_dspv2 = false;
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < a_Args.size(); argidx++) {
|
||||
if (a_Args[argidx] == "-dspv2") {
|
||||
target_dspv2 = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(a_Args, argidx, a_Design);
|
||||
|
||||
for (auto module : a_Design->selected_modules()) {
|
||||
ql_dsp_io_regs_pass(module);
|
||||
if (target_dspv2)
|
||||
ql_dsp_io_regs_pass_v2(module);
|
||||
else
|
||||
ql_dsp_io_regs_pass(module);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,6 +139,7 @@ struct QlDspIORegs : public Pass {
|
|||
}
|
||||
|
||||
// Set new type name
|
||||
log_debug("Converted %s to %s\n", log_id(cell->type), new_type.c_str());
|
||||
cell->type = RTLIL::IdString(new_type);
|
||||
|
||||
std::vector<std::string> ports2del;
|
||||
|
@ -152,6 +165,146 @@ struct QlDspIORegs : public Pass {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ql_dsp_io_regs_pass_v2(Module *module)
|
||||
{
|
||||
sigmap.set(module);
|
||||
|
||||
for (auto cell : module->cells()) {
|
||||
if (cell->type != ID(QL_DSPV2))
|
||||
continue;
|
||||
|
||||
// If the cell does not have the "is_inferred" attribute set
|
||||
// then don't touch it.
|
||||
if (!cell->get_bool_attribute(ID(is_inferred)))
|
||||
continue;
|
||||
|
||||
if (!cell->hasPort(ID(output_select)) ||
|
||||
!sigmap(cell->getPort(ID(output_select))).is_fully_def() ||
|
||||
!cell->hasParam(ID(MODE_BITS)) ||
|
||||
cell->getParam(ID(MODE_BITS)).size() != 72) {
|
||||
log_error("Missing configuration tie-offs or parameters on DSP cell %s\n",
|
||||
log_id(cell));
|
||||
}
|
||||
int out_sel_i = sigmap(cell->getPort(ID(output_select))).as_int();
|
||||
Const mode = cell->getParam(ID(MODE_BITS));
|
||||
|
||||
// Get the feedback port
|
||||
if (!cell->hasPort(ID(feedback)))
|
||||
log_error("Missing 'feedback' port on %s", log_id(cell));
|
||||
SigSpec feedback = sigmap(cell->getPort(ID(feedback)));
|
||||
|
||||
bool a_reg = mode[61] != RTLIL::S0;
|
||||
bool b_reg = mode[63] != RTLIL::S0;
|
||||
|
||||
// Build new type name
|
||||
std::string new_type = "\\QL_DSPV2_MULT";
|
||||
|
||||
// Decide if we should be deleting the clock port
|
||||
bool del_clk = true;
|
||||
|
||||
if (a_reg != b_reg) {
|
||||
// no specialized type for mixed scenario
|
||||
continue;
|
||||
}
|
||||
|
||||
enum {
|
||||
MULT,
|
||||
MULTADD,
|
||||
MULTACC,
|
||||
Unrecognized
|
||||
} base_function = Unrecognized;
|
||||
|
||||
switch (out_sel_i) {
|
||||
case 0:
|
||||
case 4:
|
||||
base_function = MULT;
|
||||
break;
|
||||
case 1:
|
||||
case 5:
|
||||
case 2:
|
||||
case 3:
|
||||
case 6:
|
||||
case 7:
|
||||
if (feedback.is_fully_def() && (feedback.as_int() == 2 || feedback.as_int() == 3)) {
|
||||
del_clk = false;
|
||||
new_type += "ADD";
|
||||
base_function = MULTADD;
|
||||
break;
|
||||
} else if (feedback.extract(1, 2).is_fully_zero()) {
|
||||
del_clk = false;
|
||||
new_type += "ACC";
|
||||
base_function = MULTACC;
|
||||
break;
|
||||
} else {
|
||||
base_function = Unrecognized;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (base_function == Unrecognized) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a_reg && b_reg) {
|
||||
del_clk = false;
|
||||
new_type += "_REGIN";
|
||||
}
|
||||
|
||||
if (out_sel_i > 3) {
|
||||
del_clk = false;
|
||||
new_type += "_REGOUT";
|
||||
}
|
||||
|
||||
// Set new type name
|
||||
log_debug("Converted %s to %s\n", log_id(cell->type), new_type.c_str());
|
||||
cell->type = RTLIL::IdString(new_type);
|
||||
|
||||
std::vector<std::string> ports2del;
|
||||
|
||||
if (del_clk) {
|
||||
cell->unsetPort(ID(clk));
|
||||
cell->unsetPort(ID(reset));
|
||||
}
|
||||
|
||||
switch (base_function) {
|
||||
case MULTACC: {
|
||||
static const std::vector<IdString> to_del = {
|
||||
ID(c), ID(a_cin), ID(b_cin), ID(z_cin),
|
||||
ID(a_cout), ID(b_cout)
|
||||
};
|
||||
|
||||
for (auto port : to_del)
|
||||
cell->unsetPort(port);
|
||||
break;
|
||||
}
|
||||
case MULTADD: {
|
||||
static const std::vector<IdString> to_del = {
|
||||
ID(c), ID(a_cin), ID(b_cin), ID(a_cout), ID(b_cout)
|
||||
};
|
||||
|
||||
for (auto port : to_del)
|
||||
cell->unsetPort(port);
|
||||
break;
|
||||
}
|
||||
case MULT: {
|
||||
static const std::vector<IdString> to_del = {
|
||||
ID(c), ID(load_acc), ID(acc_reset),
|
||||
ID(a_cin), ID(b_cin), ID(z_cin), ID(a_cout), ID(b_cout)
|
||||
};
|
||||
|
||||
for (auto port : to_del)
|
||||
cell->unsetPort(port);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
} QlDspIORegs;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
|
@ -27,158 +27,303 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
// ============================================================================
|
||||
|
||||
static void create_ql_macc_dsp(ql_dsp_macc_pm &pm)
|
||||
static void create_ql_macc_dsp_v1(ql_dsp_macc_pm &pm)
|
||||
{
|
||||
auto &st = pm.st_ql_dsp_macc;
|
||||
auto &st = pm.st_ql_dsp_macc;
|
||||
|
||||
// Get port widths
|
||||
size_t a_width = GetSize(st.mul->getPort(ID(A)));
|
||||
size_t b_width = GetSize(st.mul->getPort(ID(B)));
|
||||
size_t z_width = GetSize(st.ff->getPort(ID(Q)));
|
||||
// Get port widths
|
||||
size_t a_width = GetSize(st.mul->getPort(ID::A));
|
||||
size_t b_width = GetSize(st.mul->getPort(ID::B));
|
||||
size_t z_width = GetSize(st.ff->getPort(ID::Q));
|
||||
|
||||
size_t min_width = std::min(a_width, b_width);
|
||||
size_t max_width = std::max(a_width, b_width);
|
||||
size_t min_width = std::min(a_width, b_width);
|
||||
size_t max_width = std::max(a_width, b_width);
|
||||
|
||||
// Signed / unsigned
|
||||
bool ab_signed = st.mul->getParam(ID(A_SIGNED)).as_bool();
|
||||
log_assert(ab_signed == st.mul->getParam(ID(B_SIGNED)).as_bool());
|
||||
// Signed / unsigned
|
||||
bool ab_signed = st.mul->getParam(ID::A_SIGNED).as_bool();
|
||||
log_assert(ab_signed == st.mul->getParam(ID::B_SIGNED).as_bool());
|
||||
|
||||
// Determine DSP type or discard if too narrow / wide
|
||||
RTLIL::IdString type;
|
||||
size_t tgt_a_width;
|
||||
size_t tgt_b_width;
|
||||
size_t tgt_z_width;
|
||||
// Determine DSP type or discard if too narrow / wide
|
||||
RTLIL::IdString type;
|
||||
size_t tgt_a_width;
|
||||
size_t tgt_b_width;
|
||||
size_t tgt_z_width;
|
||||
|
||||
string cell_base_name = "dsp_t1";
|
||||
string cell_size_name = "";
|
||||
string cell_cfg_name = "";
|
||||
string cell_full_name = "";
|
||||
string cell_base_name = "dsp_t1";
|
||||
string cell_size_name = "";
|
||||
string cell_cfg_name = "";
|
||||
string cell_full_name = "";
|
||||
|
||||
if (min_width <= 2 && max_width <= 2 && z_width <= 4) {
|
||||
log_debug("\trejected: too narrow (%zd %zd %zd)\n", min_width, max_width, z_width);
|
||||
return;
|
||||
} else if (min_width <= 9 && max_width <= 10 && z_width <= 19) {
|
||||
cell_size_name = "_10x9x32";
|
||||
tgt_a_width = 10;
|
||||
tgt_b_width = 9;
|
||||
tgt_z_width = 19;
|
||||
} else if (min_width <= 18 && max_width <= 20 && z_width <= 38) {
|
||||
cell_size_name = "_20x18x64";
|
||||
tgt_a_width = 20;
|
||||
tgt_b_width = 18;
|
||||
tgt_z_width = 38;
|
||||
} else {
|
||||
log_debug("\trejected: too wide (%zd %zd %zd)\n", min_width, max_width, z_width);
|
||||
return;
|
||||
}
|
||||
if (min_width <= 2 && max_width <= 2 && z_width <= 4) {
|
||||
log_debug("\trejected: too narrow (%zd %zd %zd)\n", min_width, max_width, z_width);
|
||||
return;
|
||||
} else if (min_width <= 9 && max_width <= 10 && z_width <= 19) {
|
||||
cell_size_name = "_10x9x32";
|
||||
tgt_a_width = 10;
|
||||
tgt_b_width = 9;
|
||||
tgt_z_width = 19;
|
||||
} else if (min_width <= 18 && max_width <= 20 && z_width <= 38) {
|
||||
cell_size_name = "_20x18x64";
|
||||
tgt_a_width = 20;
|
||||
tgt_b_width = 18;
|
||||
tgt_z_width = 38;
|
||||
} else {
|
||||
log_debug("\trejected: too wide (%zd %zd %zd)\n", min_width, max_width, z_width);
|
||||
return;
|
||||
}
|
||||
|
||||
type = RTLIL::escape_id(cell_base_name + cell_size_name + "_cfg_ports");
|
||||
log("Inferring MACC %zux%zu->%zu as %s from:\n", a_width, b_width, z_width, log_id(type));
|
||||
type = RTLIL::escape_id(cell_base_name + cell_size_name + "_cfg_ports");
|
||||
log("Inferring MACC %zux%zu->%zu as %s from:\n", a_width, b_width, z_width, log_id(type));
|
||||
|
||||
for (auto cell : {st.mul, st.add, st.mux, st.ff})
|
||||
if (cell)
|
||||
log(" %s (%s)\n", log_id(cell), log_id(cell->type));
|
||||
for (auto cell : {st.mul, st.add, st.mux, st.ff})
|
||||
if (cell)
|
||||
log(" %s (%s)\n", log_id(cell), log_id(cell->type));
|
||||
|
||||
// Add the DSP cell
|
||||
RTLIL::Cell *cell = pm.module->addCell(NEW_ID, type);
|
||||
// Add the DSP cell
|
||||
RTLIL::Cell *cell = pm.module->addCell(NEW_ID, type);
|
||||
|
||||
// Set attributes
|
||||
cell->set_bool_attribute(ID(is_inferred), true);
|
||||
// Set attributes
|
||||
cell->set_bool_attribute(ID(is_inferred), true);
|
||||
|
||||
// Get input/output data signals
|
||||
RTLIL::SigSpec sig_a, sig_b, sig_z;
|
||||
sig_a = st.mul->getPort(ID(A));
|
||||
sig_b = st.mul->getPort(ID(B));
|
||||
sig_z = st.output_registered ? st.ff->getPort(ID(Q)) : st.ff->getPort(ID(D));
|
||||
// Get input/output data signals
|
||||
RTLIL::SigSpec sig_a, sig_b, sig_z;
|
||||
sig_a = st.mul->getPort(ID::A);
|
||||
sig_b = st.mul->getPort(ID::B);
|
||||
sig_z = st.output_registered ? st.ff->getPort(ID::Q) : st.ff->getPort(ID::D);
|
||||
|
||||
if (a_width < b_width)
|
||||
std::swap(sig_a, sig_b);
|
||||
if (a_width < b_width)
|
||||
std::swap(sig_a, sig_b);
|
||||
|
||||
// Connect input data ports, sign extend / pad with zeros
|
||||
sig_a.extend_u0(tgt_a_width, ab_signed);
|
||||
sig_b.extend_u0(tgt_b_width, ab_signed);
|
||||
cell->setPort(ID(a_i), sig_a);
|
||||
cell->setPort(ID(b_i), sig_b);
|
||||
// Connect input data ports, sign extend / pad with zeros
|
||||
sig_a.extend_u0(tgt_a_width, ab_signed);
|
||||
sig_b.extend_u0(tgt_b_width, ab_signed);
|
||||
cell->setPort(ID(a_i), sig_a);
|
||||
cell->setPort(ID(b_i), sig_b);
|
||||
|
||||
// Connect output data port, pad if needed
|
||||
if ((size_t) GetSize(sig_z) < tgt_z_width) {
|
||||
auto *wire = pm.module->addWire(NEW_ID, tgt_z_width - GetSize(sig_z));
|
||||
sig_z.append(wire);
|
||||
}
|
||||
cell->setPort(ID(z_o), sig_z);
|
||||
// Connect output data port, pad if needed
|
||||
if ((size_t) GetSize(sig_z) < tgt_z_width) {
|
||||
auto *wire = pm.module->addWire(NEW_ID, tgt_z_width - GetSize(sig_z));
|
||||
sig_z.append(wire);
|
||||
}
|
||||
cell->setPort(ID(z_o), sig_z);
|
||||
|
||||
// Connect clock, reset and enable
|
||||
cell->setPort(ID(clock_i), st.ff->getPort(ID(CLK)));
|
||||
// Connect clock, reset and enable
|
||||
cell->setPort(ID(clock_i), st.ff->getPort(ID::CLK));
|
||||
|
||||
RTLIL::SigSpec rst;
|
||||
RTLIL::SigSpec ena;
|
||||
RTLIL::SigSpec rst;
|
||||
RTLIL::SigSpec ena;
|
||||
|
||||
if (st.ff->hasPort(ID(ARST))) {
|
||||
if (st.ff->getParam(ID(ARST_POLARITY)).as_int() != 1) {
|
||||
rst = pm.module->Not(NEW_ID, st.ff->getPort(ID(ARST)));
|
||||
} else {
|
||||
rst = st.ff->getPort(ID(ARST));
|
||||
}
|
||||
} else {
|
||||
rst = RTLIL::SigSpec(RTLIL::S0);
|
||||
}
|
||||
if (st.ff->hasPort(ID::ARST)) {
|
||||
if (st.ff->getParam(ID::ARST_POLARITY).as_int() != 1) {
|
||||
rst = pm.module->Not(NEW_ID, st.ff->getPort(ID::ARST));
|
||||
} else {
|
||||
rst = st.ff->getPort(ID::ARST);
|
||||
}
|
||||
} else {
|
||||
rst = RTLIL::SigSpec(RTLIL::S0);
|
||||
}
|
||||
|
||||
if (st.ff->hasPort(ID(EN))) {
|
||||
if (st.ff->getParam(ID(EN_POLARITY)).as_int() != 1) {
|
||||
ena = pm.module->Not(NEW_ID, st.ff->getPort(ID(EN)));
|
||||
} else {
|
||||
ena = st.ff->getPort(ID(EN));
|
||||
}
|
||||
} else {
|
||||
ena = RTLIL::SigSpec(RTLIL::S1);
|
||||
}
|
||||
if (st.ff->hasPort(ID::EN)) {
|
||||
if (st.ff->getParam(ID::EN_POLARITY).as_int() != 1) {
|
||||
ena = pm.module->Not(NEW_ID, st.ff->getPort(ID::EN));
|
||||
} else {
|
||||
ena = st.ff->getPort(ID::EN);
|
||||
}
|
||||
} else {
|
||||
ena = RTLIL::SigSpec(RTLIL::S1);
|
||||
}
|
||||
|
||||
cell->setPort(ID(reset_i), rst);
|
||||
cell->setPort(ID(load_acc_i), ena);
|
||||
cell->setPort(ID(reset_i), rst);
|
||||
cell->setPort(ID(load_acc_i), ena);
|
||||
|
||||
// Insert feedback_i control logic used for clearing / loading the accumulator
|
||||
if (st.mux_in_pattern) {
|
||||
RTLIL::SigSpec sig_s = st.mux->getPort(ID(S));
|
||||
// Insert feedback_i control logic used for clearing / loading the accumulator
|
||||
if (st.mux_in_pattern) {
|
||||
RTLIL::SigSpec sig_s = st.mux->getPort(ID::S);
|
||||
|
||||
// Depending on the mux port ordering insert inverter if needed
|
||||
log_assert(st.mux_ab.in(ID(A), ID(B)));
|
||||
if (st.mux_ab == ID(A))
|
||||
sig_s = pm.module->Not(NEW_ID, sig_s);
|
||||
// Depending on the mux port ordering insert inverter if needed
|
||||
log_assert(st.mux_ab.in(ID::A, ID::B));
|
||||
if (st.mux_ab == ID::A)
|
||||
sig_s = pm.module->Not(NEW_ID, sig_s);
|
||||
|
||||
// Assemble the full control signal for the feedback_i port
|
||||
RTLIL::SigSpec sig_f;
|
||||
sig_f.append(sig_s);
|
||||
sig_f.append(RTLIL::S0);
|
||||
sig_f.append(RTLIL::S0);
|
||||
cell->setPort(ID(feedback_i), sig_f);
|
||||
}
|
||||
// No acc clear/load
|
||||
else {
|
||||
cell->setPort(ID(feedback_i), RTLIL::SigSpec(RTLIL::S0, 3));
|
||||
}
|
||||
// Assemble the full control signal for the feedback_i port
|
||||
RTLIL::SigSpec sig_f;
|
||||
sig_f.append(sig_s);
|
||||
sig_f.append(RTLIL::S0);
|
||||
sig_f.append(RTLIL::S0);
|
||||
cell->setPort(ID(feedback_i), sig_f);
|
||||
}
|
||||
// No acc clear/load
|
||||
else {
|
||||
cell->setPort(ID(feedback_i), RTLIL::SigSpec(RTLIL::S0, 3));
|
||||
}
|
||||
|
||||
// Connect control ports
|
||||
cell->setPort(ID(unsigned_a_i), RTLIL::SigSpec(ab_signed ? RTLIL::S0 : RTLIL::S1));
|
||||
cell->setPort(ID(unsigned_b_i), RTLIL::SigSpec(ab_signed ? RTLIL::S0 : RTLIL::S1));
|
||||
// Connect control ports
|
||||
cell->setPort(ID(unsigned_a_i), RTLIL::SigSpec(ab_signed ? RTLIL::S0 : RTLIL::S1));
|
||||
cell->setPort(ID(unsigned_b_i), RTLIL::SigSpec(ab_signed ? RTLIL::S0 : RTLIL::S1));
|
||||
|
||||
// Connect config bits
|
||||
cell->setPort(ID(saturate_enable_i), RTLIL::SigSpec(RTLIL::S0));
|
||||
cell->setPort(ID(shift_right_i), RTLIL::SigSpec(RTLIL::S0, 6));
|
||||
cell->setPort(ID(round_i), RTLIL::SigSpec(RTLIL::S0));
|
||||
cell->setPort(ID(register_inputs_i), RTLIL::SigSpec(RTLIL::S0));
|
||||
// 3 - output post acc; 1 - output pre acc
|
||||
cell->setPort(ID(output_select_i), RTLIL::Const(st.output_registered ? 1 : 3, 3));
|
||||
// Connect config bits
|
||||
cell->setPort(ID(saturate_enable_i), RTLIL::SigSpec(RTLIL::S0));
|
||||
cell->setPort(ID(shift_right_i), RTLIL::SigSpec(RTLIL::S0, 6));
|
||||
cell->setPort(ID(round_i), RTLIL::SigSpec(RTLIL::S0));
|
||||
cell->setPort(ID(register_inputs_i), RTLIL::SigSpec(RTLIL::S0));
|
||||
// 3 - output post acc; 1 - output pre acc
|
||||
cell->setPort(ID(output_select_i), RTLIL::Const(st.output_registered ? 1 : 3, 3));
|
||||
|
||||
bool subtract = (st.add->type == ID($sub));
|
||||
cell->setPort(ID(subtract_i), RTLIL::SigSpec(subtract ? RTLIL::S1 : RTLIL::S0));
|
||||
bool subtract = (st.add->type == ID($sub));
|
||||
cell->setPort(ID(subtract_i), RTLIL::SigSpec(subtract ? RTLIL::S1 : RTLIL::S0));
|
||||
|
||||
// Mark the cells for removal
|
||||
pm.autoremove(st.mul);
|
||||
pm.autoremove(st.add);
|
||||
if (st.mux != nullptr) {
|
||||
pm.autoremove(st.mux);
|
||||
}
|
||||
pm.autoremove(st.ff);
|
||||
// Mark the cells for removal
|
||||
pm.autoremove(st.mul);
|
||||
pm.autoremove(st.add);
|
||||
if (st.mux != nullptr) {
|
||||
pm.autoremove(st.mux);
|
||||
}
|
||||
pm.autoremove(st.ff);
|
||||
}
|
||||
|
||||
void create_ql_macc_dsp_v2(ql_dsp_macc_pm &pm)
|
||||
{
|
||||
auto &st = pm.st_ql_dsp_macc;
|
||||
|
||||
SigSpec sig_a = st.mul->getPort(ID::A);
|
||||
SigSpec sig_b = st.mul->getPort(ID::B);
|
||||
|
||||
if (sig_a.size() < sig_b.size())
|
||||
std::swap(sig_a, sig_b);
|
||||
|
||||
// Signed / unsigned
|
||||
bool ab_signed = st.mul->getParam(ID::A_SIGNED).as_bool();
|
||||
log_assert(ab_signed == st.mul->getParam(ID::B_SIGNED).as_bool());
|
||||
|
||||
int z_width = GetSize(st.ff->getPort(ID::Q));
|
||||
if (!ab_signed) {
|
||||
if (sig_a.msb() != RTLIL::S0 && sig_a.size() < z_width)
|
||||
sig_a.append(RTLIL::S0);
|
||||
if (sig_b.msb() != RTLIL::S0 && sig_b.size() < z_width)
|
||||
sig_b.append(RTLIL::S0);
|
||||
}
|
||||
int a_width = GetSize(sig_a);
|
||||
int b_width = GetSize(sig_b);
|
||||
|
||||
// Determine DSP type or discard if too narrow / wide
|
||||
RTLIL::IdString type;
|
||||
size_t tgt_a_width;
|
||||
size_t tgt_b_width;
|
||||
size_t tgt_z_width;
|
||||
|
||||
string cell_base_name = "dspv2";
|
||||
string cell_size_name = "";
|
||||
string cell_cfg_name = "";
|
||||
string cell_full_name = "";
|
||||
|
||||
if (a_width <= 2 && b_width <= 2 && z_width <= 4) {
|
||||
log_debug("\trejected: too narrow (%d %d %d)\n", a_width, b_width, z_width);
|
||||
return;
|
||||
} else if (a_width <= 16 && b_width <= 9 && z_width <= 25) {
|
||||
cell_size_name = "_16x9x32";
|
||||
tgt_a_width = 16;
|
||||
tgt_b_width = 9;
|
||||
tgt_z_width = 25; // TODO
|
||||
} else if (a_width <= 32 && b_width <= 18 && z_width <= 50) {
|
||||
cell_size_name = "_32x18x64";
|
||||
tgt_a_width = 32;
|
||||
tgt_b_width = 18;
|
||||
tgt_z_width = 50;
|
||||
} else {
|
||||
log_debug("\trejected: too wide (%d %d %d)\n", a_width, b_width, z_width);
|
||||
return;
|
||||
}
|
||||
|
||||
type = RTLIL::escape_id(cell_base_name + cell_size_name + "_cfg_ports");
|
||||
log("Inferring MACC %dx%d->%d as %s from:\n", a_width, b_width, z_width, log_id(type));
|
||||
|
||||
for (auto cell : {st.mul, st.add, st.mux, st.ff})
|
||||
if (cell)
|
||||
log(" %s (%s)\n", log_id(cell), log_id(cell->type));
|
||||
|
||||
// Add the DSP cell
|
||||
RTLIL::Cell *cell = pm.module->addCell(NEW_ID, type);
|
||||
|
||||
// Set attributes
|
||||
cell->set_bool_attribute(ID(is_inferred), true);
|
||||
|
||||
// Get input/output data signals
|
||||
SigSpec sig_z;
|
||||
sig_z = st.output_registered ? st.ff->getPort(ID::Q) : st.ff->getPort(ID::D);
|
||||
|
||||
// Connect input data ports, sign extend / pad with zeros
|
||||
sig_a.extend_u0(tgt_a_width, true);
|
||||
sig_b.extend_u0(tgt_b_width, true);
|
||||
cell->setPort(ID(a_i), sig_a);
|
||||
cell->setPort(ID(b_i), sig_b);
|
||||
cell->setPort(ID(c_i), SigSpec(RTLIL::S0, tgt_b_width));
|
||||
|
||||
// Connect output data port, pad if needed
|
||||
if ((size_t) GetSize(sig_z) < tgt_z_width) {
|
||||
auto *wire = pm.module->addWire(NEW_ID, tgt_z_width - GetSize(sig_z));
|
||||
sig_z.append(wire);
|
||||
}
|
||||
cell->setPort(ID(z_o), sig_z);
|
||||
|
||||
// Connect clock, reset and enable
|
||||
cell->setPort(ID(clock_i), st.ff->getPort(ID::CLK));
|
||||
|
||||
RTLIL::SigSpec rst;
|
||||
RTLIL::SigSpec ena;
|
||||
|
||||
if (st.ff->hasPort(ID::ARST)) {
|
||||
if (st.ff->getParam(ID::ARST_POLARITY).as_int() != 1) {
|
||||
rst = pm.module->Not(NEW_ID, st.ff->getPort(ID::ARST));
|
||||
} else {
|
||||
rst = st.ff->getPort(ID::ARST);
|
||||
}
|
||||
} else {
|
||||
rst = RTLIL::SigSpec(RTLIL::S0);
|
||||
}
|
||||
|
||||
if (st.ff->hasPort(ID::EN)) {
|
||||
if (st.ff->getParam(ID::EN_POLARITY).as_int() != 1) {
|
||||
ena = pm.module->Not(NEW_ID, st.ff->getPort(ID::EN));
|
||||
} else {
|
||||
ena = st.ff->getPort(ID::EN);
|
||||
}
|
||||
} else {
|
||||
ena = RTLIL::SigSpec(RTLIL::S1);
|
||||
}
|
||||
|
||||
cell->setPort(ID(reset_i), rst);
|
||||
cell->setPort(ID(load_acc_i), ena);
|
||||
|
||||
// Insert feedback_i control logic used for clearing / loading the accumulator
|
||||
if (st.mux_in_pattern) {
|
||||
RTLIL::SigSpec sig_s = st.mux->getPort(ID::S);
|
||||
|
||||
// Depending on the mux port ordering insert inverter if needed
|
||||
log_assert(st.mux_ab.in(ID::A, ID::B));
|
||||
if (st.mux_ab == ID::A)
|
||||
sig_s = pm.module->Not(NEW_ID, sig_s);
|
||||
|
||||
cell->setPort(ID(feedback_i), {RTLIL::S0, RTLIL::S0, sig_s});
|
||||
}
|
||||
// No acc clear/load
|
||||
else {
|
||||
cell->setPort(ID(feedback_i), RTLIL::SigSpec(RTLIL::S0, 3));
|
||||
}
|
||||
|
||||
cell->setPort(ID(acc_reset_i), RTLIL::SigSpec(RTLIL::S0));
|
||||
// 3 - output post acc; 1 - output pre acc
|
||||
cell->setPort(ID(output_select_i), RTLIL::Const(st.output_registered ? 1 : 3, 3));
|
||||
|
||||
bool subtract = (st.add->type == ID($sub));
|
||||
cell->setParam(ID(SUBTRACT), RTLIL::Const(subtract ? RTLIL::S1 : RTLIL::S0));
|
||||
|
||||
// Mark the cells for removal
|
||||
pm.autoremove(st.mul);
|
||||
pm.autoremove(st.add);
|
||||
if (st.mux != nullptr) {
|
||||
pm.autoremove(st.mux);
|
||||
}
|
||||
pm.autoremove(st.ff);
|
||||
}
|
||||
|
||||
struct QlDspMacc : public Pass {
|
||||
|
@ -186,27 +331,41 @@ struct QlDspMacc : public Pass {
|
|||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" ql_dsp_macc [selection]\n");
|
||||
log("\n");
|
||||
log("This pass looks for a multiply-accumulate pattern based on which it infers a\n");
|
||||
log("QuickLogic DSP cell.\n");
|
||||
log("\n");
|
||||
log("This pass looks for a multiply-accumulate pattern based on which it infers a\n");
|
||||
log("QuickLogic DSP cell.\n");
|
||||
log("\n");
|
||||
log(" -dspv2\n");
|
||||
log(" target DSPv2.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(a_Design, "Executing QL_DSP_MACC pass.\n");
|
||||
log_header(design, "Executing QL_DSP_MACC pass.\n");
|
||||
|
||||
bool target_dspv2 = false;
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < a_Args.size(); argidx++) {
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-dspv2") {
|
||||
target_dspv2 = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(a_Args, argidx, a_Design);
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : a_Design->selected_modules())
|
||||
ql_dsp_macc_pm(module, module->selected_cells()).run_ql_dsp_macc(create_ql_macc_dsp);
|
||||
for (auto module : design->selected_modules()) {
|
||||
ql_dsp_macc_pm pm(module, module->selected_cells());
|
||||
|
||||
if (target_dspv2)
|
||||
pm.run_ql_dsp_macc(create_ql_macc_dsp_v2);
|
||||
else
|
||||
pm.run_ql_dsp_macc(create_ql_macc_dsp_v1);
|
||||
}
|
||||
}
|
||||
|
||||
} QlDspMacc;
|
||||
|
|
|
@ -29,7 +29,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct QlDspSimdPass : public Pass {
|
||||
|
||||
QlDspSimdPass() : Pass("ql_dsp_simd", "merge QuickLogic K6N10f DSP pairs to operate in SIMD mode") {}
|
||||
QlDspSimdPass() : Pass("ql_dsp_simd", "merge QuickLogic K6N10f DSP pairs to operate in fractured mode") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
|
@ -37,16 +37,17 @@ struct QlDspSimdPass : public Pass {
|
|||
log("\n");
|
||||
log(" ql_dsp_simd [selection]\n");
|
||||
log("\n");
|
||||
log("This pass identifies K6N10f DSP cells with identical configuration and pack pairs\n");
|
||||
log("of them together into other DSP cells that can perform SIMD operation.\n");
|
||||
log("This pass identifies K6N10f DSP cells with identical configuration and merges\n");
|
||||
log("pairs of them, enabling fractured mode.\n");
|
||||
}
|
||||
|
||||
// ..........................................
|
||||
|
||||
/// Describes DSP config unique to a whole DSP cell
|
||||
/// Describes DSP config unique to a DSP cell
|
||||
struct DspConfig {
|
||||
// Port connections
|
||||
dict<RTLIL::IdString, RTLIL::SigSpec> connections;
|
||||
dict<RTLIL::IdString, RTLIL::Const> parameters;
|
||||
|
||||
DspConfig() = default;
|
||||
|
||||
|
@ -55,60 +56,136 @@ struct QlDspSimdPass : public Pass {
|
|||
|
||||
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(connections); return h; }
|
||||
|
||||
bool operator==(const DspConfig &ref) const { return connections == ref.connections; }
|
||||
bool operator==(const DspConfig &ref) const { return connections == ref.connections && parameters == ref.parameters; }
|
||||
};
|
||||
|
||||
// ..........................................
|
||||
|
||||
const int m_ModeBitsSize = 80;
|
||||
|
||||
// DSP parameters
|
||||
const std::vector<std::string> m_DspParams = {"COEFF_3", "COEFF_2", "COEFF_1", "COEFF_0"};
|
||||
|
||||
/// Temporary SigBit to SigBit helper map.
|
||||
SigMap sigmap;
|
||||
|
||||
static bool is_cascade(const Cell* cell)
|
||||
{
|
||||
static const std::vector<IdString> cascade_ports = {
|
||||
ID(a_cout_o),
|
||||
ID(b_cout_o),
|
||||
ID(z_cout_o),
|
||||
ID(a_cin_i),
|
||||
ID(b_cin_i),
|
||||
ID(z_cin_i)
|
||||
};
|
||||
for (auto p : cascade_ports) {
|
||||
if (cell->hasPort(p) && !cell->getPort(p).is_fully_undef())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// ..........................................
|
||||
|
||||
void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override
|
||||
{
|
||||
log_header(a_Design, "Executing QL_DSP_SIMD pass.\n");
|
||||
|
||||
// DSP control and config ports to consider and how to map them to ports
|
||||
// of the target DSP cell
|
||||
static const std::vector<std::pair<IdString, IdString>> m_DspCfgPorts = {
|
||||
std::make_pair(ID(clock_i), ID(clk)),
|
||||
std::make_pair(ID(reset_i), ID(reset)),
|
||||
std::make_pair(ID(feedback_i), ID(feedback)),
|
||||
std::make_pair(ID(load_acc_i), ID(load_acc)),
|
||||
std::make_pair(ID(unsigned_a_i), ID(unsigned_a)),
|
||||
std::make_pair(ID(unsigned_b_i), ID(unsigned_b)),
|
||||
std::make_pair(ID(subtract_i), ID(subtract)),
|
||||
std::make_pair(ID(output_select_i), ID(output_select)),
|
||||
std::make_pair(ID(saturate_enable_i), ID(saturate_enable)),
|
||||
std::make_pair(ID(shift_right_i), ID(shift_right)),
|
||||
std::make_pair(ID(round_i), ID(round)),
|
||||
std::make_pair(ID(register_inputs_i), ID(register_inputs))
|
||||
// The following lists have to match simulation model interfaces.
|
||||
|
||||
// DSP control and config ports that must be equal between
|
||||
// merged half-blocks
|
||||
// In addition to functional differences,
|
||||
// v1 and v2 have different balance between shared functionality
|
||||
// in ports vs params.
|
||||
static const std::vector<IdString> m_Dspv1CfgPorts = {
|
||||
ID(acc_fir_i),
|
||||
ID(feedback_i),
|
||||
ID(load_acc_i),
|
||||
ID(unsigned_a_i),
|
||||
ID(unsigned_b_i),
|
||||
ID(clock_i),
|
||||
ID(s_reset),
|
||||
ID(saturate_enable_i),
|
||||
ID(output_select_i),
|
||||
ID(round_i),
|
||||
ID(shift_right_i),
|
||||
ID(subtract_i),
|
||||
ID(register_inputs_i),
|
||||
};
|
||||
static const std::vector<IdString> m_Dspv1CfgParams = {
|
||||
ID(COEFF_0),
|
||||
ID(COEFF_1),
|
||||
ID(COEFF_2),
|
||||
ID(COEFF_3),
|
||||
};
|
||||
static const std::vector<IdString> m_Dspv2CfgPorts = {
|
||||
ID(clock_i),
|
||||
ID(reset_i),
|
||||
ID(acc_reset_i),
|
||||
ID(feedback_i),
|
||||
ID(load_acc_i),
|
||||
ID(output_select_i),
|
||||
};
|
||||
static const std::vector<IdString> m_Dspv2CfgParams = {
|
||||
ID(COEFF_0),
|
||||
ID(ACC_FIR),
|
||||
ID(ROUND),
|
||||
ID(ZC_SHIFT),
|
||||
ID(ZREG_SHIFT),
|
||||
ID(SHIFT_REG),
|
||||
ID(SATURATE),
|
||||
ID(SUBTRACT),
|
||||
ID(PRE_ADD),
|
||||
ID(A_SEL),
|
||||
ID(A_REG),
|
||||
ID(B_SEL),
|
||||
ID(B_REG),
|
||||
ID(C_REG),
|
||||
ID(BC_REG),
|
||||
ID(M_REG),
|
||||
ID(FRAC_MODE),
|
||||
};
|
||||
|
||||
// DSP data ports and how to map them to ports of the target DSP cell
|
||||
static const std::vector<std::pair<IdString, IdString>> m_DspDataPorts = {
|
||||
std::make_pair(ID(a_i), ID(a)),
|
||||
std::make_pair(ID(b_i), ID(b)),
|
||||
std::make_pair(ID(acc_fir_i), ID(acc_fir)),
|
||||
std::make_pair(ID(z_o), ID(z)),
|
||||
std::make_pair(ID(dly_b_o), ID(dly_b))
|
||||
|
||||
// Data ports to be concatenated into merged cell
|
||||
static const std::vector<IdString> m_Dspv1DataPorts = {
|
||||
ID(a_i),
|
||||
ID(b_i),
|
||||
ID(z_o),
|
||||
ID(dly_b_o),
|
||||
};
|
||||
static const std::vector<IdString> m_Dspv2DataPorts = {
|
||||
ID(a_i),
|
||||
ID(b_i),
|
||||
ID(c_i),
|
||||
ID(z_o),
|
||||
};
|
||||
|
||||
// Source DSP cell type (SISD)
|
||||
static const IdString m_SisdDspType = ID(dsp_t1_10x9x32);
|
||||
// Source DSP cell type (half-block)
|
||||
static const IdString m_Dspv1SisdType = ID(dsp_t1_10x9x32_cfg_ports);
|
||||
static const IdString m_Dspv2SisdType = ID(dspv2_16x9x32_cfg_ports);
|
||||
|
||||
// Target DSP cell types for the SIMD mode
|
||||
static const IdString m_SimdDspType = ID(QL_DSP2);
|
||||
// Target DSP cell types (full-block)
|
||||
static const IdString m_Dspv1SimdType = ID(dsp_t1_20x18x64_cfg_ports_fracturable);
|
||||
static const IdString m_Dspv2SimdType = ID(dspv2_32x18x64_cfg_ports);
|
||||
|
||||
// Parse args
|
||||
extra_args(a_Args, 1, a_Design);
|
||||
int dsp_version = 1;
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < a_Args.size(); argidx++) {
|
||||
if (a_Args[argidx] == "-dspv2") {
|
||||
dsp_version = 2;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(a_Args, argidx, a_Design);
|
||||
|
||||
log_assert(dsp_version < 3);
|
||||
log_assert(dsp_version > 0);
|
||||
const auto& cfg_ports = (dsp_version == 1) ? m_Dspv1CfgPorts : m_Dspv2CfgPorts;
|
||||
const auto& cfg_params = (dsp_version == 1) ? m_Dspv1CfgParams : m_Dspv2CfgParams;
|
||||
const auto& data_ports = (dsp_version == 1) ? m_Dspv1DataPorts : m_Dspv2DataPorts;
|
||||
auto half_dsp = (dsp_version == 1) ? m_Dspv1SisdType : m_Dspv2SisdType;
|
||||
auto full_dsp = (dsp_version == 1) ? m_Dspv1SimdType : m_Dspv2SimdType;
|
||||
|
||||
int cellsMerged = 0;
|
||||
// Process modules
|
||||
for (auto module : a_Design->selected_modules()) {
|
||||
// Setup the SigMap
|
||||
|
@ -118,25 +195,33 @@ struct QlDspSimdPass : public Pass {
|
|||
dict<DspConfig, std::vector<RTLIL::Cell *>> groups;
|
||||
for (auto cell : module->selected_cells()) {
|
||||
// Check if this is a DSP cell we are looking for (type starts with m_SisdDspType)
|
||||
if (cell->type != m_SisdDspType)
|
||||
if (cell->type != half_dsp)
|
||||
continue;
|
||||
|
||||
// Skip if it has the (* keep *) attribute set
|
||||
if (cell->has_keep_attr())
|
||||
if (cell->has_keep_attr()) {
|
||||
log_debug("skip %s because it's marked keep\n", log_id(cell));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if it has cascading
|
||||
if (is_cascade(cell)) {
|
||||
log_debug("skip %s because it's cascading\n", log_id(cell));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add to a group
|
||||
const auto key = getDspConfig(cell, m_DspCfgPorts);
|
||||
const auto key = getDspConfig(cell, cfg_ports, cfg_params);
|
||||
groups[key].push_back(cell);
|
||||
}
|
||||
|
||||
log_debug("Checking %zu detected mode-equivalent DSP cell classes\n", groups.size());
|
||||
std::vector<Cell *> cellsToRemove;
|
||||
|
||||
// Map cell pairs to the target DSP SIMD cell
|
||||
for (const auto &it : groups) {
|
||||
const auto &group = it.second;
|
||||
const auto &config = it.first;
|
||||
|
||||
log_debug("Checking %zu half-blocks\n", group.size());
|
||||
// Ensure an even number
|
||||
size_t count = group.size();
|
||||
if (count & 1)
|
||||
|
@ -148,7 +233,7 @@ struct QlDspSimdPass : public Pass {
|
|||
Cell *dsp_b = group[i + 1];
|
||||
|
||||
// Create the new cell
|
||||
Cell *simd = module->addCell(NEW_ID, m_SimdDspType);
|
||||
Cell *simd = module->addCell(NEW_ID, full_dsp);
|
||||
|
||||
log(" SIMD: %s (%s) + %s (%s) => %s (%s)\n", log_id(dsp_a), log_id(dsp_a->type),
|
||||
log_id(dsp_b), log_id(dsp_b->type), log_id(simd), log_id(simd->type));
|
||||
|
@ -156,27 +241,36 @@ struct QlDspSimdPass : public Pass {
|
|||
// Check if the target cell is known (important to know
|
||||
// its port widths)
|
||||
if (!simd->known())
|
||||
log_error(" The target cell type '%s' is not known!", log_id(simd));
|
||||
|
||||
log_error(" The target cell type '%s' is not known!", log_id(simd->type));
|
||||
// Connect common ports
|
||||
for (const auto &it : m_DspCfgPorts)
|
||||
simd->setPort(it.first, config.connections.at(it.second));
|
||||
|
||||
for (auto port : cfg_ports) {
|
||||
if (config.connections.count(port))
|
||||
simd->setPort(port, config.connections.at(port));
|
||||
}
|
||||
for (auto param : cfg_params) {
|
||||
if (config.parameters.count(param))
|
||||
simd->setParam(param, config.parameters.at(param));
|
||||
}
|
||||
|
||||
// Connect data ports
|
||||
for (const auto &it : m_DspDataPorts) {
|
||||
for (auto port : data_ports) {
|
||||
size_t width;
|
||||
bool isOutput;
|
||||
|
||||
std::tie(width, isOutput) = getPortInfo(simd, it.second);
|
||||
std::tie(width, isOutput) = getPortInfo(simd, port);
|
||||
if (!width)
|
||||
log_error("Can't determine portinfo for %s\n", log_id(port));
|
||||
|
||||
auto getConnection = [&](const RTLIL::Cell *cell) {
|
||||
RTLIL::SigSpec sigspec;
|
||||
if (cell->hasPort(it.first)) {
|
||||
const auto &sig = cell->getPort(it.first);
|
||||
if (cell->hasPort(port)) {
|
||||
const auto &sig = cell->getPort(port);
|
||||
sigspec.append(sig);
|
||||
}
|
||||
|
||||
int padding = width / 2 - sigspec.size();
|
||||
log_assert(padding >= 0);
|
||||
|
||||
if (padding) {
|
||||
if (!isOutput)
|
||||
|
@ -190,27 +284,14 @@ struct QlDspSimdPass : public Pass {
|
|||
RTLIL::SigSpec sigspec;
|
||||
sigspec.append(getConnection(dsp_a));
|
||||
sigspec.append(getConnection(dsp_b));
|
||||
simd->setPort(it.second, sigspec);
|
||||
simd->setPort(port, sigspec);
|
||||
}
|
||||
|
||||
// Concatenate FIR coefficient parameters into the single
|
||||
// MODE_BITS parameter
|
||||
Const mode_bits;
|
||||
for (const auto &it : m_DspParams) {
|
||||
auto val_a = dsp_a->getParam(it);
|
||||
auto val_b = dsp_b->getParam(it);
|
||||
|
||||
mode_bits.bits().insert(mode_bits.bits().end(),
|
||||
val_a.begin(), val_a.end());
|
||||
mode_bits.bits().insert(mode_bits.bits().end(),
|
||||
val_b.begin(), val_b.end());
|
||||
}
|
||||
|
||||
// Enable the fractured mode by connecting the control
|
||||
// port.
|
||||
simd->setPort(ID(f_mode), State::S1);
|
||||
simd->setParam(ID(MODE_BITS), mode_bits);
|
||||
log_assert(mode_bits.size() == m_ModeBitsSize);
|
||||
// Enable the fractured mode
|
||||
if (dsp_version == 1)
|
||||
simd->setPort(ID(f_mode_i), State::S1);
|
||||
else
|
||||
simd->setParam(ID(FRAC_MODE), State::S1);
|
||||
|
||||
// Handle the "is_inferred" attribute. If one of the fragments
|
||||
// is not inferred mark the whole DSP as not inferred
|
||||
|
@ -223,11 +304,12 @@ struct QlDspSimdPass : public Pass {
|
|||
cellsToRemove.push_back(dsp_b);
|
||||
}
|
||||
}
|
||||
|
||||
cellsMerged += cellsToRemove.size();
|
||||
// Remove old cells
|
||||
for (auto cell : cellsToRemove)
|
||||
module->remove(cell);
|
||||
}
|
||||
log("Merged %d half-block cells\n", cellsMerged);
|
||||
}
|
||||
|
||||
// ..........................................
|
||||
|
@ -257,19 +339,24 @@ struct QlDspSimdPass : public Pass {
|
|||
}
|
||||
|
||||
/// Given a DSP cell populates and returns a DspConfig struct for it.
|
||||
DspConfig getDspConfig(RTLIL::Cell *a_Cell, const std::vector<std::pair<IdString, IdString>> &dspCfgPorts)
|
||||
DspConfig getDspConfig(RTLIL::Cell *a_Cell, const std::vector<IdString> &dspCfgPorts, const std::vector<IdString> &dspCfgParams)
|
||||
{
|
||||
DspConfig config;
|
||||
|
||||
for (const auto &it : dspCfgPorts) {
|
||||
auto port = it.first;
|
||||
|
||||
for (auto port : dspCfgPorts) {
|
||||
// Port unconnected
|
||||
if (!a_Cell->hasPort(port))
|
||||
continue;
|
||||
|
||||
config.connections[port] = sigmap(a_Cell->getPort(port));
|
||||
}
|
||||
for (auto param : dspCfgParams) {
|
||||
// Param unset?
|
||||
if (!a_Cell->hasParam(param))
|
||||
continue;
|
||||
|
||||
config.parameters[param] = a_Cell->getParam(param);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,63 @@
|
|||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
module dsp_t1_20x18x64_cfg_ports_fracturable (
|
||||
input [19:0] a_i,
|
||||
input [17:0] b_i,
|
||||
input [ 5:0] acc_fir_i,
|
||||
output [37:0] z_o,
|
||||
output [17:0] dly_b_o,
|
||||
|
||||
input clock_i,
|
||||
input reset_i,
|
||||
|
||||
input [2:0] feedback_i,
|
||||
input load_acc_i,
|
||||
input unsigned_a_i,
|
||||
input unsigned_b_i,
|
||||
|
||||
input [2:0] output_select_i,
|
||||
input saturate_enable_i,
|
||||
input [5:0] shift_right_i,
|
||||
input round_i,
|
||||
input subtract_i,
|
||||
input register_inputs_i,
|
||||
input f_mode_i
|
||||
);
|
||||
|
||||
parameter [19:0] COEFF_0 = 20'd0;
|
||||
parameter [19:0] COEFF_1 = 20'd0;
|
||||
parameter [19:0] COEFF_2 = 20'd0;
|
||||
parameter [19:0] COEFF_3 = 20'd0;
|
||||
|
||||
QL_DSP2 # (
|
||||
.MODE_BITS ({COEFF_3, COEFF_2, COEFF_1, COEFF_0})
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.a (a_i),
|
||||
.b (b_i),
|
||||
.acc_fir (acc_fir_i),
|
||||
.z (z_o),
|
||||
.dly_b (dly_b_o),
|
||||
|
||||
.clk (clock_i),
|
||||
.reset (reset_i),
|
||||
|
||||
.feedback (feedback_i),
|
||||
.load_acc (load_acc_i),
|
||||
.unsigned_a (unsigned_a_i),
|
||||
.unsigned_b (unsigned_b_i),
|
||||
|
||||
.f_mode (f_mode_i),
|
||||
.output_select (output_select_i),
|
||||
.saturate_enable (saturate_enable_i),
|
||||
.shift_right (shift_right_i),
|
||||
.round (round_i),
|
||||
.subtract (subtract_i),
|
||||
.register_inputs (register_inputs_i)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
module dsp_t1_20x18x64_cfg_ports (
|
||||
input [19:0] a_i,
|
||||
input [17:0] b_i,
|
||||
|
@ -42,30 +99,30 @@ module dsp_t1_20x18x64_cfg_ports (
|
|||
parameter [19:0] COEFF_2 = 20'd0;
|
||||
parameter [19:0] COEFF_3 = 20'd0;
|
||||
|
||||
QL_DSP2 # (
|
||||
.MODE_BITS ({COEFF_3, COEFF_2, COEFF_1, COEFF_0})
|
||||
dsp_t1_20x18x64_cfg_ports_fracturable # (
|
||||
.COEFF_0 (COEFF_0),
|
||||
.COEFF_1 (COEFF_1),
|
||||
.COEFF_2 (COEFF_2),
|
||||
.COEFF_3 (COEFF_3)
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.a (a_i),
|
||||
.b (b_i),
|
||||
.acc_fir (acc_fir_i),
|
||||
.z (z_o),
|
||||
.dly_b (dly_b_o),
|
||||
|
||||
.clk (clock_i),
|
||||
.reset (reset_i),
|
||||
|
||||
.feedback (feedback_i),
|
||||
.load_acc (load_acc_i),
|
||||
.unsigned_a (unsigned_a_i),
|
||||
.unsigned_b (unsigned_b_i),
|
||||
|
||||
.f_mode (1'b0), // No fracturation
|
||||
.output_select (output_select_i),
|
||||
.saturate_enable (saturate_enable_i),
|
||||
.shift_right (shift_right_i),
|
||||
.round (round_i),
|
||||
.subtract (subtract_i),
|
||||
.register_inputs (register_inputs_i)
|
||||
.a_i (a_i),
|
||||
.b_i (b_i),
|
||||
.acc_fir_i (acc_fir_i),
|
||||
.z_o (z_o),
|
||||
.dly_b_o (dly_b_o),
|
||||
.clock_i (clock_i),
|
||||
.reset_i (reset_i),
|
||||
.feedback_i (feedback_i),
|
||||
.load_acc_i (load_acc_i),
|
||||
.unsigned_a_i (unsigned_a_i),
|
||||
.unsigned_b_i (unsigned_b_i),
|
||||
.output_select_i (output_select_i),
|
||||
.saturate_enable_i (saturate_enable_i),
|
||||
.shift_right_i (shift_right_i),
|
||||
.round_i (round_i),
|
||||
.subtract_i (subtract_i),
|
||||
.register_inputs_i (register_inputs_i),
|
||||
.f_mode_i (1'b0)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
@ -122,7 +179,7 @@ module dsp_t1_10x9x32_cfg_ports (
|
|||
.unsigned_a (unsigned_a_i),
|
||||
.unsigned_b (unsigned_b_i),
|
||||
|
||||
.f_mode (1'b1), // Enable fractuation, Use the lower half
|
||||
.f_mode (1'b0),
|
||||
.output_select (output_select_i),
|
||||
.saturate_enable (saturate_enable_i),
|
||||
.shift_right (shift_right_i),
|
80
techlibs/quicklogic/qlf_k6n10f/dspv1_sim_extra.v
Normal file
80
techlibs/quicklogic/qlf_k6n10f/dspv1_sim_extra.v
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2020-2022 F4PGA Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
`timescale 1ps/1ps
|
||||
|
||||
`default_nettype none
|
||||
|
||||
// dsp_t1_20x18x64_cfg_ports but with input wire f_mode_i
|
||||
// This is a yosys-specific extension beyond the vendor-provided model
|
||||
module dsp_t1_20x18x64_cfg_ports_fracturable (
|
||||
input wire [19:0] a_i,
|
||||
input wire [17:0] b_i,
|
||||
input wire [ 5:0] acc_fir_i,
|
||||
output wire [37:0] z_o,
|
||||
output wire [17:0] dly_b_o,
|
||||
|
||||
(* clkbuf_sink *)
|
||||
input wire clock_i,
|
||||
input wire reset_i,
|
||||
|
||||
input wire [ 2:0] feedback_i,
|
||||
input wire load_acc_i,
|
||||
input wire unsigned_a_i,
|
||||
input wire unsigned_b_i,
|
||||
|
||||
input wire [ 2:0] output_select_i,
|
||||
input wire saturate_enable_i,
|
||||
input wire [ 5:0] shift_right_i,
|
||||
input wire round_i,
|
||||
input wire subtract_i,
|
||||
input wire register_inputs_i,
|
||||
input wire f_mode_i
|
||||
);
|
||||
|
||||
parameter [19:0] COEFF_0 = 20'd0;
|
||||
parameter [19:0] COEFF_1 = 20'd0;
|
||||
parameter [19:0] COEFF_2 = 20'd0;
|
||||
parameter [19:0] COEFF_3 = 20'd0;
|
||||
|
||||
QL_DSP2 #(
|
||||
.MODE_BITS({COEFF_3, COEFF_2, COEFF_1, COEFF_0})
|
||||
) dsp (
|
||||
.a(a_i),
|
||||
.b(b_i),
|
||||
.z(z_o),
|
||||
.dly_b(dly_b_o),
|
||||
|
||||
.f_mode(f_mode_i), // 20x18x64 DSP
|
||||
|
||||
.acc_fir(acc_fir_i),
|
||||
.feedback(feedback_i),
|
||||
.load_acc(load_acc_i),
|
||||
|
||||
.unsigned_a(unsigned_a_i),
|
||||
.unsigned_b(unsigned_b_i),
|
||||
|
||||
.clk(clock_i),
|
||||
.reset(reset_i),
|
||||
|
||||
.saturate_enable(saturate_enable_i),
|
||||
.output_select(output_select_i),
|
||||
.round(round_i),
|
||||
.shift_right(shift_right_i),
|
||||
.subtract(subtract_i),
|
||||
.register_inputs(register_inputs_i)
|
||||
);
|
||||
endmodule
|
142
techlibs/quicklogic/qlf_k6n10f/dspv2_final_map.v
Normal file
142
techlibs/quicklogic/qlf_k6n10f/dspv2_final_map.v
Normal file
|
@ -0,0 +1,142 @@
|
|||
// Derived from dspv2_sim.v
|
||||
|
||||
module dspv2_32x18x64_cfg_ports (
|
||||
input wire [31:0] a_i,
|
||||
input wire [17:0] b_i,
|
||||
input wire [17:0] c_i,
|
||||
output wire [49:0] z_o,
|
||||
|
||||
input wire clock_i,
|
||||
input wire reset_i,
|
||||
input wire acc_reset_i,
|
||||
|
||||
input wire [ 2:0] feedback_i,
|
||||
input wire load_acc_i,
|
||||
input wire [ 2:0] output_select_i,
|
||||
|
||||
input wire [31:0] a_cin_i,
|
||||
input wire [17:0] b_cin_i,
|
||||
input wire [49:0] z_cin_i,
|
||||
|
||||
output wire [31:0] a_cout_o,
|
||||
output wire [17:0] b_cout_o,
|
||||
output wire [49:0] z_cout_o
|
||||
|
||||
);
|
||||
|
||||
parameter [31:0] COEFF_0 = 32'h0;
|
||||
parameter [5:0] ACC_FIR = 6'h0;
|
||||
parameter [2:0] ROUND = 3'h0;
|
||||
parameter [4:0] ZC_SHIFT = 5'h0;
|
||||
parameter [4:0] ZREG_SHIFT = 5'h0;
|
||||
parameter [5:0] SHIFT_REG = 6'h0;
|
||||
parameter SATURATE = 1'b0;
|
||||
parameter SUBTRACT = 1'b0;
|
||||
parameter PRE_ADD = 1'b0;
|
||||
parameter A_SEL = 1'b0;
|
||||
parameter A_REG = 1'b0;
|
||||
parameter B_SEL = 1'b0;
|
||||
parameter B_REG = 1'b0;
|
||||
parameter C_REG = 1'b0;
|
||||
parameter BC_REG = 1'b0;
|
||||
parameter M_REG = 1'b0;
|
||||
parameter ZCIN_REG = 1'b0;
|
||||
parameter FRAC_MODE = 1'b0; // 32x18x64 DSP
|
||||
|
||||
(* is_inferred *)
|
||||
QL_DSPV2 #(
|
||||
.MODE_BITS({FRAC_MODE,3'b000,ZCIN_REG,M_REG,BC_REG,C_REG,B_REG,B_SEL,A_REG,A_SEL,PRE_ADD,SUBTRACT,SATURATE,SHIFT_REG,ZREG_SHIFT,ZC_SHIFT,ROUND,ACC_FIR,COEFF_0})
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.a(a_i),
|
||||
.b(b_i),
|
||||
.c(c_i),
|
||||
.load_acc(load_acc_i),
|
||||
.feedback(feedback_i),
|
||||
.output_select(output_select_i),
|
||||
.z(z_o),
|
||||
|
||||
.clk(clock_i),
|
||||
.reset(reset_i),
|
||||
.acc_reset(acc_reset_i),
|
||||
|
||||
.a_cin(a_cin_i),
|
||||
.b_cin(b_cin_i),
|
||||
.z_cin(z_cin_i),
|
||||
|
||||
.z_cout(z_cout_o),
|
||||
.a_cout(a_cout_o),
|
||||
.b_cout(b_cout_o)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
module dspv2_16x9x32_cfg_ports (
|
||||
input wire [15:0] a_i,
|
||||
input wire [8:0] b_i,
|
||||
input wire [8:0] c_i,
|
||||
output wire [24:0] z_o,
|
||||
|
||||
input wire clock_i,
|
||||
input wire reset_i,
|
||||
input wire acc_reset_i,
|
||||
|
||||
input wire [ 2:0] feedback_i,
|
||||
input wire load_acc_i,
|
||||
input wire [ 2:0] output_select_i,
|
||||
|
||||
input wire [15:0] a_cin_i,
|
||||
input wire [8:0] b_cin_i,
|
||||
input wire [24:0] z_cin_i,
|
||||
|
||||
output wire [15:0] a_cout_o,
|
||||
output wire [8:0] b_cout_o,
|
||||
output wire [24:0] z_cout_o
|
||||
|
||||
);
|
||||
|
||||
parameter [15:0] COEFF_0 = 16'h0;
|
||||
parameter [5:0] ACC_FIR = 6'h0;
|
||||
parameter [2:0] ROUND = 3'h0;
|
||||
parameter [4:0] ZC_SHIFT = 5'h0;
|
||||
parameter [4:0] ZREG_SHIFT = 5'h0;
|
||||
parameter [5:0] SHIFT_REG = 6'h0;
|
||||
parameter SATURATE = 1'b0;
|
||||
parameter SUBTRACT = 1'b0;
|
||||
parameter PRE_ADD = 1'b0;
|
||||
parameter A_SEL = 1'b0;
|
||||
parameter A_REG = 1'b0;
|
||||
parameter B_SEL = 1'b0;
|
||||
parameter B_REG = 1'b0;
|
||||
parameter C_REG = 1'b0;
|
||||
parameter BC_REG = 1'b0;
|
||||
parameter M_REG = 1'b0;
|
||||
parameter ZCIN_REG = 1'b0;
|
||||
parameter FRAC_MODE = 1'b1; // 16x9x32 DSP
|
||||
|
||||
(* is_inferred *)
|
||||
QL_DSPV2 #(
|
||||
.MODE_BITS({FRAC_MODE,3'b000,ZCIN_REG,M_REG,BC_REG,C_REG,B_REG,B_SEL,A_REG,A_SEL,PRE_ADD,SUBTRACT,SATURATE,SHIFT_REG,ZREG_SHIFT,ZC_SHIFT,ROUND,ACC_FIR,16'h0,COEFF_0})
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.a(a_i),
|
||||
.b(b_i),
|
||||
.c(c_i),
|
||||
.load_acc(load_acc_i),
|
||||
.feedback(feedback_i),
|
||||
.output_select(output_select_i),
|
||||
.z(z_o),
|
||||
|
||||
.clk(clock_i),
|
||||
.reset(reset_i),
|
||||
.acc_reset(acc_reset_i),
|
||||
|
||||
.a_cin(a_cin_i),
|
||||
.b_cin(b_cin_i),
|
||||
.z_cin(z_cin_i),
|
||||
|
||||
.z_cout(z_cout_o),
|
||||
.a_cout(a_cout_o),
|
||||
.b_cout(b_cout_o)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
59
techlibs/quicklogic/qlf_k6n10f/dspv2_map.v
Normal file
59
techlibs/quicklogic/qlf_k6n10f/dspv2_map.v
Normal file
|
@ -0,0 +1,59 @@
|
|||
module \$__MUL32X18 (input [31:0] A, input [17:0] B, output [49:0] Y);
|
||||
parameter A_SIGNED = 0;
|
||||
parameter B_SIGNED = 0;
|
||||
parameter A_WIDTH = 32;
|
||||
parameter B_WIDTH = 18;
|
||||
parameter Y_WIDTH = 50;
|
||||
|
||||
dspv2_32x18x64_cfg_ports _TECHMAP_REPLACE_ (
|
||||
.a_i(A),
|
||||
.b_i(B),
|
||||
.c_i(18'd0),
|
||||
.z_o(Y),
|
||||
|
||||
.clock_i(1'bx),
|
||||
.reset_i(1'bx),
|
||||
.acc_reset_i(1'b0),
|
||||
.feedback_i(3'd0),
|
||||
.load_acc_i(1'b0),
|
||||
.output_select_i(3'd0),
|
||||
.a_cin_i(32'dx),
|
||||
.b_cin_i(18'dx),
|
||||
.z_cin_i(50'dx),
|
||||
/* TODO: connect to dummy wires?
|
||||
.a_cout_o(),
|
||||
.b_cout_o(),
|
||||
.z_cout_o(),
|
||||
*/
|
||||
);
|
||||
endmodule
|
||||
|
||||
module \$__MUL16X9 (input [15:0] A, input [8:0] B, output [24:0] Y);
|
||||
parameter A_SIGNED = 0;
|
||||
parameter B_SIGNED = 0;
|
||||
parameter A_WIDTH = 16;
|
||||
parameter B_WIDTH = 9;
|
||||
parameter Y_WIDTH = 25;
|
||||
|
||||
dspv2_16x9x32_cfg_ports _TECHMAP_REPLACE_ (
|
||||
.a_i(A),
|
||||
.b_i(B),
|
||||
.c_i(9'd0),
|
||||
.z_o(Y),
|
||||
|
||||
.clock_i(1'bx),
|
||||
.reset_i(1'bx),
|
||||
.acc_reset_i(1'b0),
|
||||
.feedback_i(3'd0),
|
||||
.load_acc_i(1'b0),
|
||||
.output_select_i(3'd0),
|
||||
.a_cin_i(32'dx),
|
||||
.b_cin_i(18'dx),
|
||||
.z_cin_i(50'dx),
|
||||
/* TODO: connect to dummy wires?
|
||||
.a_cout_o(),
|
||||
.b_cout_o(),
|
||||
.z_cout_o(),
|
||||
*/
|
||||
);
|
||||
endmodule
|
1437
techlibs/quicklogic/qlf_k6n10f/dspv2_sim.v
Normal file
1437
techlibs/quicklogic/qlf_k6n10f/dspv2_sim.v
Normal file
File diff suppressed because it is too large
Load diff
|
@ -27,6 +27,12 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct SynthQuickLogicPass : public ScriptPass {
|
||||
|
||||
enum DSPKind {
|
||||
None,
|
||||
V1,
|
||||
V2,
|
||||
};
|
||||
|
||||
SynthQuickLogicPass() : ScriptPass("synth_quicklogic", "Synthesis for QuickLogic FPGAs") {}
|
||||
|
||||
void help() override
|
||||
|
@ -50,6 +56,10 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
log(" do not use dsp_t1_* to implement multipliers and associated logic\n");
|
||||
log(" (qlf_k6n10f only).\n");
|
||||
log("\n");
|
||||
log(" -dspv2\n");
|
||||
log(" synthesize for the v2 DSP block model instead of v1\n");
|
||||
log(" (qlf_k6n10f only).\n");
|
||||
log("\n");
|
||||
log(" -nocarry\n");
|
||||
log(" do not use adder_carry cells in output netlist.\n");
|
||||
log("\n");
|
||||
|
@ -78,7 +88,8 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
}
|
||||
|
||||
string top_opt, blif_file, edif_file, family, currmodule, verilog_file, lib_path;
|
||||
bool abc9, inferAdder, nobram, bramTypes, dsp, ioff;
|
||||
bool abc9, inferAdder, nobram, bramTypes, ioff;
|
||||
DSPKind dsp;
|
||||
|
||||
void clear_flags() override
|
||||
{
|
||||
|
@ -93,7 +104,7 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
nobram = false;
|
||||
bramTypes = false;
|
||||
lib_path = "+/quicklogic/";
|
||||
dsp = true;
|
||||
dsp = V1;
|
||||
ioff = true;
|
||||
}
|
||||
|
||||
|
@ -156,7 +167,11 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nodsp" || args[argidx] == "-no_dsp") {
|
||||
dsp = false;
|
||||
dsp = None;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-dspv2") {
|
||||
dsp = V2;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-noioff") {
|
||||
|
@ -198,8 +213,11 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
read_simlibs += stringf(" %sqlf_k6n10f/brams_sim.v", lib_path.c_str());
|
||||
if (bramTypes)
|
||||
read_simlibs += stringf(" %sqlf_k6n10f/bram_types_sim.v", lib_path.c_str());
|
||||
if (dsp)
|
||||
read_simlibs += stringf(" %sqlf_k6n10f/dsp_sim.v", lib_path.c_str());
|
||||
if (dsp == V1) {
|
||||
read_simlibs += stringf(" %sqlf_k6n10f/dspv1_sim.v", lib_path.c_str());
|
||||
read_simlibs += stringf(" %sqlf_k6n10f/dspv1_sim_extra.v", lib_path.c_str());
|
||||
} else if (dsp == V2)
|
||||
read_simlibs += stringf(" %sqlf_k6n10f/dspv2_sim.v", lib_path.c_str());
|
||||
}
|
||||
run(read_simlibs);
|
||||
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
|
||||
|
@ -225,18 +243,35 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
}
|
||||
|
||||
if (check_label("map_dsp", "(for qlf_k6n10f, skip if -nodsp)")
|
||||
&& ((dsp && family == "qlf_k6n10f") || help_mode)) {
|
||||
&& (((dsp != None) && family == "qlf_k6n10f") || help_mode)) {
|
||||
run("wreduce t:$mul");
|
||||
run("ql_dsp_macc");
|
||||
|
||||
run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=20 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=11 -D DSP_B_MINWIDTH=10 -D DSP_NAME=$__QL_MUL20X18");
|
||||
run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=10 -D DSP_B_MAXWIDTH=9 -D DSP_A_MINWIDTH=4 -D DSP_B_MINWIDTH=4 -D DSP_NAME=$__QL_MUL10X9");
|
||||
run("chtype -set $mul t:$__soft_mul");
|
||||
if (dsp == V1) {
|
||||
run("ql_dsp_macc");
|
||||
|
||||
run("techmap -map " + lib_path + family + "/dsp_map.v -D USE_DSP_CFG_PARAMS=0");
|
||||
run("ql_dsp_simd");
|
||||
run("techmap -map " + lib_path + family + "/dsp_final_map.v");
|
||||
run("ql_dsp_io_regs");
|
||||
run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=20 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=11 -D DSP_B_MINWIDTH=10 -D DSP_NAME=$__QL_MUL20X18");
|
||||
run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=10 -D DSP_B_MAXWIDTH=9 -D DSP_A_MINWIDTH=4 -D DSP_B_MINWIDTH=4 -D DSP_NAME=$__QL_MUL10X9");
|
||||
run("chtype -set $mul t:$__soft_mul");
|
||||
|
||||
run("techmap -map " + lib_path + family + "/dspv1_map.v -D USE_DSP_CFG_PARAMS=0");
|
||||
run("ql_dsp_simd");
|
||||
run("techmap -map " + lib_path + family + "/dspv1_final_map.v");
|
||||
run("ql_dsp_io_regs");
|
||||
} else if (dsp == V2) {
|
||||
run("ql_dsp_macc -dspv2");
|
||||
run("techmap -map +/mul2dsp.v -map " + lib_path + family + "/dspv2_map.v -D USE_DSP_CFG_PARAMS=0 -D DSP_SIGNEDONLY "
|
||||
"-D DSP_A_MAXWIDTH=32 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=10 -D DSP_B_MINWIDTH=10 -D DSP_NAME=$__MUL32X18");
|
||||
run("chtype -set $mul t:$__soft_mul");
|
||||
run("techmap -map +/mul2dsp.v -map " + lib_path + family + "/dspv2_map.v -D USE_DSP_CFG_PARAMS=0 -D DSP_SIGNEDONLY "
|
||||
"-D DSP_A_MAXWIDTH=16 -D DSP_B_MAXWIDTH=9 -D DSP_A_MINWIDTH=4 -D DSP_B_MINWIDTH=4 -D DSP_NAME=$__MUL16X9");
|
||||
run("chtype -set $mul t:$__soft_mul");
|
||||
run("ql_dsp");
|
||||
run("ql_dsp_simd -dspv2");
|
||||
run("techmap -map " + lib_path + family + "/dspv2_final_map.v");
|
||||
run("ql_dsp_io_regs -dspv2");
|
||||
} else {
|
||||
log_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("coarse")) {
|
||||
|
|
|
@ -21,7 +21,7 @@ EOF
|
|||
design -save ast
|
||||
proc
|
||||
wreduce
|
||||
#equiv_opt -async2sync -map +/quicklogic/qlf_k6n10f/dsp_sim.v synth_quicklogic -family qlf_k6n10f
|
||||
#equiv_opt -async2sync -map +/quicklogic/qlf_k6n10f/dspv1_sim.v synth_quicklogic -family qlf_k6n10f
|
||||
#design -load postopt
|
||||
synth_quicklogic -family qlf_k6n10f
|
||||
cd top
|
||||
|
@ -114,8 +114,8 @@ always @(posedge clk) begin
|
|||
end
|
||||
endmodule
|
||||
EOF
|
||||
read_verilog +/quicklogic/qlf_k6n10f/dsp_sim.v
|
||||
read_verilog +/quicklogic/qlf_k6n10f/dspv1_sim.v
|
||||
hierarchy -top testbench
|
||||
proc
|
||||
async2sync
|
||||
sim -assert -q -clock clk -n 20
|
||||
sim -q -clock clk -n 20 -assert
|
65
tests/arch/quicklogic/qlf_k6n10f/dspv1_simd.ys
Normal file
65
tests/arch/quicklogic/qlf_k6n10f/dspv1_simd.ys
Normal file
|
@ -0,0 +1,65 @@
|
|||
read_verilog <<EOT
|
||||
module foo(
|
||||
input [7:0] A,
|
||||
input [7:0] B,
|
||||
input [7:0] C,
|
||||
input [7:0] D,
|
||||
output reg [7:0] X,
|
||||
output reg [7:0] Y);
|
||||
assign X = A * B;
|
||||
assign Y = C * D;
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
synth_quicklogic -run :map_dsp
|
||||
|
||||
# this is just taken from map_dsp step
|
||||
wreduce t:$mul
|
||||
ql_dsp_macc
|
||||
techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=20 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=11 -D DSP_B_MINWIDTH=10 -D DSP_NAME=$__QL_MUL20X18
|
||||
chtype -set $mul t:$__soft_mul
|
||||
techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=10 -D DSP_B_MAXWIDTH=9 -D DSP_A_MINWIDTH=4 -D DSP_B_MINWIDTH=4 -D DSP_NAME=$__QL_MUL10X9
|
||||
techmap -map +/quicklogic/qlf_k6n10f/dspv1_map.v -D USE_DSP_CFG_PARAMS=0
|
||||
read_verilog -lib +/quicklogic/qlf_k6n10f/dspv1_sim.v
|
||||
read_verilog -lib +/quicklogic/qlf_k6n10f/dspv1_sim_extra.v
|
||||
|
||||
select -assert-count 2 t:dsp_t1_10x9x32_cfg_ports
|
||||
select -assert-count 0 t:dsp_t1_20x18x64_cfg_ports_fracturable
|
||||
equiv_opt -assert -async2sync -map +/quicklogic/qlf_k6n10f/dspv1_sim.v -map +/quicklogic/qlf_k6n10f/dspv1_sim_extra.v ql_dsp_simd
|
||||
design -load postopt
|
||||
select -assert-count 0 t:dsp_t1_10x9x32_cfg_ports
|
||||
select -assert-count 1 t:dsp_t1_20x18x64_cfg_ports_fracturable
|
||||
|
||||
design -reset
|
||||
|
||||
read_verilog <<EOT
|
||||
module foo(
|
||||
input [7:0] A,
|
||||
input [7:0] B,
|
||||
input [7:0] C,
|
||||
input [7:0] D,
|
||||
input [7:0] E,
|
||||
output reg [7:0] X,
|
||||
output reg [7:0] Y);
|
||||
assign X = A * B;
|
||||
assign Y = C * (D + E); // <-- look here
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
synth_quicklogic -run :map_dsp
|
||||
|
||||
wreduce t:$mul
|
||||
ql_dsp_macc
|
||||
techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=20 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=11 -D DSP_B_MINWIDTH=10 -D DSP_NAME=$__QL_MUL20X18
|
||||
chtype -set $mul t:$__soft_mul
|
||||
techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=10 -D DSP_B_MAXWIDTH=9 -D DSP_A_MINWIDTH=4 -D DSP_B_MINWIDTH=4 -D DSP_NAME=$__QL_MUL10X9
|
||||
techmap -map +/quicklogic/qlf_k6n10f/dspv1_map.v -D USE_DSP_CFG_PARAMS=0
|
||||
read_verilog -lib +/quicklogic/qlf_k6n10f/dspv1_sim.v
|
||||
read_verilog -lib +/quicklogic/qlf_k6n10f/dspv1_sim_extra.v
|
||||
|
||||
select -assert-count 2 t:dsp_t1_10x9x32_cfg_ports
|
||||
select -assert-count 0 t:dsp_t1_20x18x64_cfg_ports_fracturable
|
||||
equiv_opt -assert -async2sync -map +/quicklogic/qlf_k6n10f/dspv1_sim.v -map +/quicklogic/qlf_k6n10f/dspv1_sim_extra.v ql_dsp_simd
|
||||
design -load postopt
|
||||
select -assert-count 0 t:dsp_t1_10x9x32_cfg_ports
|
||||
select -assert-count 1 t:dsp_t1_20x18x64_cfg_ports_fracturable
|
174
tests/arch/quicklogic/qlf_k6n10f/dspv2_full_dsp_flow.ys
Normal file
174
tests/arch/quicklogic/qlf_k6n10f/dspv2_full_dsp_flow.ys
Normal file
|
@ -0,0 +1,174 @@
|
|||
read_verilog <<EOF
|
||||
module top(input [6:0] a, input [4:0] b, input [4:0] c, input [4:0] d, input [4:0] e, input [4:0] f, output reg [7:0] o);
|
||||
assign o = (a * b) + (c * d) + (e * f);
|
||||
endmodule
|
||||
EOF
|
||||
design -save gold
|
||||
|
||||
synth_quicklogic -family qlf_k6n10f -dspv2 -run :coarse
|
||||
check -assert
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:QL_DSPV2_MULT
|
||||
select -assert-count 2 top/t:QL_DSPV2_MULTADD
|
||||
read_verilog +/quicklogic/qlf_k6n10f/dspv2_sim.v
|
||||
prep -flatten -top top
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gate -as gate top
|
||||
design -copy-from gold -as gold top
|
||||
async2sync
|
||||
equiv_make gold gate equiv
|
||||
opt -fast equiv
|
||||
equiv_induct equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(input signed [3:0] a, input signed [3:0] b, input signed [3:0] c, input signed [3:0] d, output signed [4:0] o);
|
||||
assign o = (a * b) + (c * d);
|
||||
endmodule
|
||||
EOF
|
||||
design -save gold
|
||||
|
||||
synth_quicklogic -family qlf_k6n10f -dspv2 -run :coarse
|
||||
check -assert
|
||||
opt_clean
|
||||
stat
|
||||
select -assert-count 1 top/t:QL_DSPV2_MULT
|
||||
select -assert-count 1 top/t:QL_DSPV2_MULTADD
|
||||
read_verilog +/quicklogic/qlf_k6n10f/dspv2_sim.v
|
||||
prep -flatten -top top
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gate -as gate top
|
||||
design -copy-from gold -as gold top
|
||||
async2sync
|
||||
equiv_make gold gate equiv
|
||||
opt -fast equiv
|
||||
equiv_induct equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(input signed [16:0] ar, input signed [16:0] ai, input signed [16:0] br, input signed [16:0] bi, output reg signed [33:0] qr, output reg signed [33:0] qi, input clk);
|
||||
reg signed [33:0] rr, ri, ir, ii;
|
||||
always @(posedge clk) begin
|
||||
rr <= ar * br;
|
||||
ri <= ar * bi;
|
||||
ir <= ai * br;
|
||||
ii <= ai * bi;
|
||||
qr <= rr - ii;
|
||||
qi <= ir + ri;
|
||||
end
|
||||
endmodule
|
||||
EOF
|
||||
|
||||
synth_quicklogic -family qlf_k6n10f -dspv2 -run :coarse
|
||||
check -assert
|
||||
opt_clean
|
||||
select -assert-count 2 top/t:QL_DSPV2_MULT_REGOUT
|
||||
select -assert-count 2 top/t:QL_DSPV2_MULTADD_REGOUT
|
||||
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(input signed [16:0] ar, input signed [16:0] ai, input signed [16:0] br, input signed [16:0] bi, output reg signed [33:0] qr, output reg signed [33:0] qi, input clk);
|
||||
reg signed [16:0] ar_, ai_, br_, bi_;
|
||||
always @(posedge clk) begin
|
||||
ar_ <= ar;
|
||||
ai_ <= ai;
|
||||
br_ <= br;
|
||||
bi_ <= bi;
|
||||
end
|
||||
|
||||
reg signed [33:0] rr, ri, ir, ii;
|
||||
always @(posedge clk) begin
|
||||
rr <= ar_ * br_;
|
||||
ri <= ar_ * bi_;
|
||||
ir <= ai_ * br_;
|
||||
ii <= ai_ * bi_;
|
||||
qr <= rr - ii;
|
||||
qi <= ir + ri;
|
||||
end
|
||||
endmodule
|
||||
EOF
|
||||
|
||||
synth_quicklogic -family qlf_k6n10f -dspv2 -run :coarse
|
||||
check -assert
|
||||
opt_clean -purge
|
||||
select -assert-count 2 top/t:QL_DSPV2_MULT_REGIN_REGOUT
|
||||
select -assert-count 2 top/t:QL_DSPV2_MULTADD_REGIN_REGOUT
|
||||
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(input [6:0] a, input [4:0] b, input [4:0] c, input [4:0] d, output [7:0] e, output [7:0] f);
|
||||
assign e = a * b;
|
||||
assign f = c * d;
|
||||
endmodule
|
||||
EOF
|
||||
design -save gold
|
||||
|
||||
synth_quicklogic -family qlf_k6n10f -dspv2 -run :coarse
|
||||
check -assert
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:QL_DSPV2_MULT
|
||||
select -assert-count 1 top/c:*
|
||||
read_verilog +/quicklogic/qlf_k6n10f/dspv2_sim.v
|
||||
prep -flatten -top top
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gate -as gate top
|
||||
design -copy-from gold -as gold top
|
||||
async2sync
|
||||
equiv_make gold gate equiv
|
||||
opt -fast equiv
|
||||
equiv_induct equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(input signed [6:0] a, input signed [4:0] b, input [4:0] c, input [4:0] d, output signed [7:0] e, output [7:0] f);
|
||||
assign e = a * b;
|
||||
assign f = c * d;
|
||||
endmodule
|
||||
EOF
|
||||
design -save gold
|
||||
|
||||
synth_quicklogic -family qlf_k6n10f -dspv2 -run :coarse
|
||||
check -assert
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:QL_DSPV2_MULT
|
||||
select -assert-count 1 top/c:*
|
||||
read_verilog +/quicklogic/qlf_k6n10f/dspv2_sim.v
|
||||
prep -flatten -top top
|
||||
design -save gate
|
||||
|
||||
design -reset
|
||||
design -copy-from gate -as gate top
|
||||
design -copy-from gold -as gold top
|
||||
async2sync
|
||||
equiv_make gold gate equiv
|
||||
opt -fast equiv
|
||||
equiv_induct equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(input [16:0] a, input [16:0] b, output reg [33:0] o, input clk, input [2:0] j);
|
||||
reg [16:0] ar;
|
||||
reg [16:0] br;
|
||||
|
||||
always @(posedge clk) begin
|
||||
ar <= a;
|
||||
br <= b;
|
||||
o <= {ar * br, j};
|
||||
end
|
||||
endmodule
|
||||
EOF
|
||||
|
||||
synth_quicklogic -family qlf_k6n10f -dspv2 -run :coarse
|
||||
check -assert
|
||||
opt_clean
|
||||
select -assert-count 1 top/t:QL_DSPV2_MULT_REGIN_REGOUT
|
45
tests/arch/quicklogic/qlf_k6n10f/dspv2_macc.tcl
Normal file
45
tests/arch/quicklogic/qlf_k6n10f/dspv2_macc.tcl
Normal file
|
@ -0,0 +1,45 @@
|
|||
yosys -import
|
||||
|
||||
proc testcase {top} {
|
||||
log -header "Testcase $top"
|
||||
log -push
|
||||
|
||||
design -load ast
|
||||
prep -top $top
|
||||
design -save gold
|
||||
|
||||
design -load ast
|
||||
hierarchy -top $top
|
||||
synth_quicklogic -family qlf_k6n10f -dspv2 -run :coarse
|
||||
opt_clean
|
||||
select -assert-none t:\$mul
|
||||
stat
|
||||
dump $top
|
||||
select -assert-count 1 t:QL_DSPV2_MULTACC
|
||||
read_verilog +/quicklogic/qlf_k6n10f/dspv2_sim.v
|
||||
prep -flatten -top $top
|
||||
design -save gate
|
||||
|
||||
design -clear
|
||||
design -copy-from gate -as gate A:top
|
||||
design -copy-from gold -as gold A:top
|
||||
async2sync
|
||||
equiv_make gold gate equiv
|
||||
opt -fast equiv
|
||||
equiv_induct equiv
|
||||
equiv_status -assert equiv
|
||||
|
||||
log -pop
|
||||
}
|
||||
|
||||
read_verilog dspv2_macc.v
|
||||
design -save ast
|
||||
|
||||
testcase "macc_simple"
|
||||
testcase "macc_simple_clr"
|
||||
testcase "macc_simple_arst"
|
||||
testcase "macc_simple_ena"
|
||||
testcase "macc_simple_arst_clr_ena"
|
||||
testcase "macc_simple_preacc_clr"
|
||||
testcase "macc_simple_signed"
|
||||
testcase "macc_simple_signed_subtract"
|
145
tests/arch/quicklogic/qlf_k6n10f/dspv2_macc.v
Normal file
145
tests/arch/quicklogic/qlf_k6n10f/dspv2_macc.v
Normal file
|
@ -0,0 +1,145 @@
|
|||
// Copyright 2020-2022 F4PGA Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
module macc_simple (
|
||||
input wire clk,
|
||||
input wire [5:0] A,
|
||||
input wire [5:0] B,
|
||||
output reg [8:0] Z
|
||||
);
|
||||
|
||||
always @(posedge clk)
|
||||
Z <= Z + (A * B);
|
||||
|
||||
endmodule
|
||||
|
||||
module macc_simple_clr (
|
||||
input wire clk,
|
||||
input wire clr,
|
||||
input wire [5:0] A,
|
||||
input wire [5:0] B,
|
||||
output reg [6:0] Z
|
||||
);
|
||||
|
||||
always @(posedge clk)
|
||||
if (clr) Z <= (A * B);
|
||||
else Z <= Z + (A * B);
|
||||
|
||||
endmodule
|
||||
|
||||
module macc_simple_arst (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
input wire [5:0] A,
|
||||
input wire [5:0] B,
|
||||
output reg [8:0] Z
|
||||
);
|
||||
|
||||
always @(posedge clk or posedge rst)
|
||||
if (rst) Z <= 0;
|
||||
else Z <= Z + (A * B);
|
||||
|
||||
endmodule
|
||||
|
||||
module macc_simple_ena (
|
||||
input wire clk,
|
||||
input wire ena,
|
||||
input wire [5:0] A,
|
||||
input wire [5:0] B,
|
||||
output reg [8:0] Z
|
||||
);
|
||||
|
||||
always @(posedge clk)
|
||||
if (ena) Z <= Z + (A * B);
|
||||
|
||||
endmodule
|
||||
|
||||
module macc_simple_arst_clr_ena (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
input wire clr,
|
||||
input wire ena,
|
||||
input wire [5:0] A,
|
||||
input wire [5:0] B,
|
||||
output reg [7:0] Z
|
||||
);
|
||||
|
||||
always @(posedge clk or posedge rst)
|
||||
if (rst) Z <= 0;
|
||||
else if (ena) begin
|
||||
if (clr) Z <= (A * B);
|
||||
else Z <= Z + (A * B);
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module macc_simple_preacc (
|
||||
input wire clk,
|
||||
input wire [4:0] A,
|
||||
input wire [4:0] B,
|
||||
output wire [7:0] Z
|
||||
);
|
||||
|
||||
reg [7:0] acc;
|
||||
|
||||
assign Z = acc + (A * B);
|
||||
|
||||
always @(posedge clk)
|
||||
acc <= Z;
|
||||
|
||||
endmodule
|
||||
|
||||
module macc_simple_preacc_clr (
|
||||
input wire clk,
|
||||
input wire clr,
|
||||
input wire [5:0] A,
|
||||
input wire [5:0] B,
|
||||
output reg [7:0] Z
|
||||
);
|
||||
|
||||
reg [7:0] acc;
|
||||
|
||||
assign Z = (clr) ? (A * B) : (acc + (A * B));
|
||||
|
||||
always @(posedge clk)
|
||||
acc <= Z;
|
||||
|
||||
endmodule
|
||||
|
||||
module macc_simple_signed (
|
||||
input wire clk,
|
||||
input wire signed [4:0] A,
|
||||
input wire signed [4:0] B,
|
||||
output reg signed [7:0] Z
|
||||
);
|
||||
|
||||
always @(posedge clk)
|
||||
Z <= Z + (A * B);
|
||||
|
||||
endmodule
|
||||
|
||||
module macc_simple_signed_subtract (
|
||||
input wire clk,
|
||||
input wire signed [4:0] A,
|
||||
input wire signed [4:0] B,
|
||||
output reg signed [7:0] Z
|
||||
);
|
||||
|
||||
always @(posedge clk)
|
||||
Z <= Z - (A * B);
|
||||
|
||||
endmodule
|
||||
|
59
tests/arch/quicklogic/qlf_k6n10f/dspv2_simd.ys
Normal file
59
tests/arch/quicklogic/qlf_k6n10f/dspv2_simd.ys
Normal file
|
@ -0,0 +1,59 @@
|
|||
read_verilog <<EOT
|
||||
module foo(
|
||||
input [7:0] A,
|
||||
input [7:0] B,
|
||||
input [7:0] C,
|
||||
input [7:0] D,
|
||||
output reg [7:0] X,
|
||||
output reg [7:0] Y);
|
||||
assign X = A * B;
|
||||
assign Y = C * D;
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
synth_quicklogic -dspv2 -run :map_dsp
|
||||
|
||||
# this is just taken from map_dsp step
|
||||
wreduce t:$mul
|
||||
ql_dsp_macc -dspv2
|
||||
techmap -map +/mul2dsp.v -map +/quicklogic/qlf_k6n10f/dspv2_map.v -D USE_DSP_CFG_PARAMS=0 -D DSP_SIGNEDONLY -D DSP_A_MAXWIDTH=32 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=10 -D DSP_B_MINWIDTH=10 -D DSP_NAME=$__MUL32X18
|
||||
chtype -set $mul t:$__soft_mul
|
||||
techmap -map +/mul2dsp.v -map +/quicklogic/qlf_k6n10f/dspv2_map.v -D USE_DSP_CFG_PARAMS=0 -D DSP_SIGNEDONLY -D DSP_A_MAXWIDTH=16 -D DSP_B_MAXWIDTH=9 -D DSP_A_MINWIDTH=4 -D DSP_B_MINWIDTH=4 -D DSP_NAME=$__MUL16X9
|
||||
read_verilog -lib +/quicklogic/qlf_k6n10f/dspv2_sim.v
|
||||
select -assert-count 2 t:dspv2_16x9x32_cfg_ports
|
||||
select -assert-count 0 t:dspv2_32x18x64_cfg_ports
|
||||
equiv_opt -assert -async2sync -map +/quicklogic/qlf_k6n10f/dspv2_sim.v ql_dsp_simd -dspv2
|
||||
design -load postopt
|
||||
select -assert-count 0 t:dspv2_16x9x32_cfg_ports
|
||||
select -assert-count 1 t:dspv2_32x18x64_cfg_ports
|
||||
|
||||
design -reset
|
||||
|
||||
read_verilog <<EOT
|
||||
module foo(
|
||||
input [7:0] A,
|
||||
input [7:0] B,
|
||||
input [7:0] C,
|
||||
input [7:0] D,
|
||||
input [7:0] E,
|
||||
output reg [7:0] X,
|
||||
output reg [7:0] Y);
|
||||
assign X = A * B;
|
||||
assign Y = C * (D + E); // <-- look here
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
synth_quicklogic -dspv2 -run :map_dsp
|
||||
|
||||
wreduce t:$mul
|
||||
ql_dsp_macc -dspv2
|
||||
techmap -map +/mul2dsp.v -map +/quicklogic/qlf_k6n10f/dspv2_map.v -D USE_DSP_CFG_PARAMS=0 -D DSP_SIGNEDONLY -D DSP_A_MAXWIDTH=32 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=10 -D DSP_B_MINWIDTH=10 -D DSP_NAME=$__MUL32X18
|
||||
chtype -set $mul t:$__soft_mul
|
||||
techmap -map +/mul2dsp.v -map +/quicklogic/qlf_k6n10f/dspv2_map.v -D USE_DSP_CFG_PARAMS=0 -D DSP_SIGNEDONLY -D DSP_A_MAXWIDTH=16 -D DSP_B_MAXWIDTH=9 -D DSP_A_MINWIDTH=4 -D DSP_B_MINWIDTH=4 -D DSP_NAME=$__MUL16X9
|
||||
read_verilog -lib +/quicklogic/qlf_k6n10f/dspv2_sim.v
|
||||
select -assert-count 2 t:dspv2_16x9x32_cfg_ports
|
||||
select -assert-count 0 t:dspv2_32x18x64_cfg_ports
|
||||
equiv_opt -assert -async2sync -map +/quicklogic/qlf_k6n10f/dspv2_sim.v ql_dsp_simd -dspv2
|
||||
design -load postopt
|
||||
select -assert-count 0 t:dspv2_16x9x32_cfg_ports
|
||||
select -assert-count 1 t:dspv2_32x18x64_cfg_ports
|
|
@ -1,4 +1,4 @@
|
|||
verific -sv -lib +/quicklogic/qlf_k6n10f/dsp_sim.v
|
||||
verific -sv -lib +/quicklogic/qlf_k6n10f/dspv1_sim.v
|
||||
|
||||
verific -sv <<EOF
|
||||
module top (
|
||||
|
|
Loading…
Reference in a new issue