mirror of https://github.com/YosysHQ/yosys.git
add dsp inference
This commit is contained in:
parent
6682693888
commit
20d864bbde
|
@ -0,0 +1 @@
|
||||||
|
/*_pm.h
|
|
@ -1,6 +1,20 @@
|
||||||
|
%_pm.h: passes/pmgen/pmgen.py %.pmg
|
||||||
|
$(P) mkdir -p pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p $(subst _pm.h,,$(notdir $@)) $(filter-out $<,$^)
|
||||||
|
|
||||||
OBJS += techlibs/quicklogic/synth_quicklogic.o
|
OBJS += techlibs/quicklogic/synth_quicklogic.o
|
||||||
OBJS += techlibs/quicklogic/ql-bram-merge.o
|
OBJS += techlibs/quicklogic/ql_bram_merge.o
|
||||||
OBJS += techlibs/quicklogic/ql-bram-types.o
|
OBJS += techlibs/quicklogic/ql_bram_types.o
|
||||||
|
OBJS += techlibs/quicklogic/ql_dsp_simd.o
|
||||||
|
OBJS += techlibs/quicklogic/ql_dsp_io_regs.o
|
||||||
|
|
||||||
|
# --------------------------------------
|
||||||
|
|
||||||
|
OBJS += techlibs/quicklogic/ql_dsp_macc.o
|
||||||
|
GENFILES += techlibs/quicklogic/ql_dsp_macc_pm.h
|
||||||
|
techlibs/quicklogic/ql_dsp_macc.o: techlibs/quicklogic/ql_dsp_macc_pm.h
|
||||||
|
$(eval $(call add_extra_objs,techlibs/quicklogic/ql_dsp_macc_pm.h))
|
||||||
|
|
||||||
|
# --------------------------------------
|
||||||
|
|
||||||
$(eval $(call add_share_file,share/quicklogic/common,techlibs/quicklogic/common/cells_sim.v))
|
$(eval $(call add_share_file,share/quicklogic/common,techlibs/quicklogic/common/cells_sim.v))
|
||||||
|
|
||||||
|
@ -22,3 +36,6 @@ $(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf
|
||||||
$(eval $(call add_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/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/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/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))
|
|
@ -0,0 +1,244 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel/sigtools.h"
|
||||||
|
#include "kernel/yosys.h"
|
||||||
|
|
||||||
|
USING_YOSYS_NAMESPACE
|
||||||
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
#define MODE_BITS_REGISTER_INPUTS_ID 92
|
||||||
|
#define MODE_BITS_OUTPUT_SELECT_START_ID 81
|
||||||
|
#define MODE_BITS_OUTPUT_SELECT_WIDTH 3
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
struct QlDspIORegs : public Pass {
|
||||||
|
|
||||||
|
const std::vector<std::string> ports2del_mult = {"load_acc", "subtract", "acc_fir", "dly_b"};
|
||||||
|
const std::vector<std::string> ports2del_mult_acc = {"acc_fir", "dly_b"};
|
||||||
|
const std::vector<std::string> ports2del_mult_add = {"dly_b"};
|
||||||
|
const std::vector<std::string> ports2del_extension = {"saturate_enable", "shift_right", "round"};
|
||||||
|
|
||||||
|
/// Temporary SigBit to SigBit helper map.
|
||||||
|
SigMap m_SigMap;
|
||||||
|
|
||||||
|
// ..........................................
|
||||||
|
|
||||||
|
QlDspIORegs() : Pass("ql_dsp_io_regs", "Changes types of QL_DSP2/QL_DSP3 depending on their configuration.") {}
|
||||||
|
|
||||||
|
void help() override
|
||||||
|
{
|
||||||
|
log("\n");
|
||||||
|
log(" ql_dsp_io_regs [options] [selection]\n");
|
||||||
|
log("\n");
|
||||||
|
log("Looks for QL_DSP2/QL_DSP3 cells and changes their types depending\n");
|
||||||
|
log("on their configuration.\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");
|
||||||
|
|
||||||
|
size_t argidx;
|
||||||
|
for (argidx = 1; argidx < a_Args.size(); argidx++) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
extra_args(a_Args, argidx, a_Design);
|
||||||
|
|
||||||
|
for (auto module : a_Design->selected_modules()) {
|
||||||
|
ql_dsp_io_regs_pass(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a pair of mask and value describing constant bit connections of
|
||||||
|
// a SigSpec
|
||||||
|
std::pair<uint32_t, uint32_t> get_constant_mask_value(const RTLIL::SigSpec *sigspec)
|
||||||
|
{
|
||||||
|
uint32_t mask = 0L;
|
||||||
|
uint32_t value = 0L;
|
||||||
|
|
||||||
|
auto sigbits = sigspec->bits();
|
||||||
|
for (ssize_t i = (sigbits.size() - 1); i >= 0; --i) {
|
||||||
|
auto other = m_SigMap(sigbits[i]);
|
||||||
|
|
||||||
|
mask <<= 1;
|
||||||
|
value <<= 1;
|
||||||
|
|
||||||
|
// A known constant
|
||||||
|
if (!other.is_wire() && other.data != RTLIL::Sx) {
|
||||||
|
mask |= 0x1;
|
||||||
|
value |= (other.data == RTLIL::S1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(mask, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ql_dsp_io_regs_pass(RTLIL::Module *module)
|
||||||
|
{
|
||||||
|
// Setup the SigMap
|
||||||
|
m_SigMap.clear();
|
||||||
|
m_SigMap.set(module);
|
||||||
|
|
||||||
|
for (auto cell : module->cells_) {
|
||||||
|
std::string cell_type = cell.second->type.str();
|
||||||
|
if (cell_type == RTLIL::escape_id("QL_DSP2") || cell_type == RTLIL::escape_id("QL_DSP3")) {
|
||||||
|
auto dsp = cell.second;
|
||||||
|
|
||||||
|
// If the cell does not have the "is_inferred" attribute set
|
||||||
|
// then don't touch it.
|
||||||
|
if (!dsp->has_attribute(RTLIL::escape_id("is_inferred")) || dsp->get_bool_attribute(RTLIL::escape_id("is_inferred")) == false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool del_clk = true;
|
||||||
|
bool use_dsp_cfg_params = (cell_type == RTLIL::escape_id("QL_DSP3"));
|
||||||
|
|
||||||
|
int reg_in_i;
|
||||||
|
int out_sel_i;
|
||||||
|
|
||||||
|
// Get DSP configuration
|
||||||
|
if (use_dsp_cfg_params) {
|
||||||
|
// Read MODE_BITS at correct indexes
|
||||||
|
auto mode_bits = &dsp->getParam(RTLIL::escape_id("MODE_BITS"));
|
||||||
|
RTLIL::Const register_inputs;
|
||||||
|
register_inputs = mode_bits->bits.at(MODE_BITS_REGISTER_INPUTS_ID);
|
||||||
|
reg_in_i = register_inputs.as_int();
|
||||||
|
|
||||||
|
RTLIL::Const output_select;
|
||||||
|
output_select = mode_bits->extract(MODE_BITS_OUTPUT_SELECT_START_ID, MODE_BITS_OUTPUT_SELECT_WIDTH);
|
||||||
|
out_sel_i = output_select.as_int();
|
||||||
|
} else {
|
||||||
|
// Read dedicated configuration ports
|
||||||
|
const RTLIL::SigSpec *register_inputs;
|
||||||
|
register_inputs = &dsp->getPort(RTLIL::escape_id("register_inputs"));
|
||||||
|
if (!register_inputs)
|
||||||
|
log_error("register_inputs port not found!");
|
||||||
|
auto reg_in_c = register_inputs->as_const();
|
||||||
|
reg_in_i = reg_in_c.as_int();
|
||||||
|
|
||||||
|
const RTLIL::SigSpec *output_select;
|
||||||
|
output_select = &dsp->getPort(RTLIL::escape_id("output_select"));
|
||||||
|
if (!output_select)
|
||||||
|
log_error("output_select port not found!");
|
||||||
|
auto out_sel_c = output_select->as_const();
|
||||||
|
out_sel_i = out_sel_c.as_int();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the feedback port
|
||||||
|
const RTLIL::SigSpec *feedback;
|
||||||
|
feedback = &dsp->getPort(RTLIL::escape_id("feedback"));
|
||||||
|
if (!feedback)
|
||||||
|
log_error("feedback port not found!");
|
||||||
|
|
||||||
|
// Check if feedback is or can be set to 0 which implies MACC
|
||||||
|
auto feedback_con = get_constant_mask_value(feedback);
|
||||||
|
bool have_macc = (feedback_con.second == 0x0);
|
||||||
|
// log("mask=0x%08X value=0x%08X\n", consts.first, consts.second);
|
||||||
|
// log_error("=== END HERE ===\n");
|
||||||
|
|
||||||
|
// Build new type name
|
||||||
|
std::string new_type = cell_type;
|
||||||
|
new_type += "_MULT";
|
||||||
|
|
||||||
|
if (have_macc) {
|
||||||
|
switch (out_sel_i) {
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
case 5:
|
||||||
|
case 7:
|
||||||
|
del_clk = false;
|
||||||
|
new_type += "ACC";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (out_sel_i) {
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
case 5:
|
||||||
|
case 7:
|
||||||
|
new_type += "ADD";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reg_in_i) {
|
||||||
|
del_clk = false;
|
||||||
|
new_type += "_REGIN";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_sel_i > 3) {
|
||||||
|
del_clk = false;
|
||||||
|
new_type += "_REGOUT";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new type name
|
||||||
|
dsp->type = RTLIL::IdString(new_type);
|
||||||
|
|
||||||
|
std::vector<std::string> ports2del;
|
||||||
|
|
||||||
|
if (del_clk)
|
||||||
|
ports2del.push_back("clk");
|
||||||
|
|
||||||
|
switch (out_sel_i) {
|
||||||
|
case 0:
|
||||||
|
case 4:
|
||||||
|
case 6:
|
||||||
|
ports2del.insert(ports2del.end(), ports2del_mult.begin(), ports2del_mult.end());
|
||||||
|
// Mark for deleton additional configuration ports
|
||||||
|
if (!use_dsp_cfg_params) {
|
||||||
|
ports2del.insert(ports2del.end(), ports2del_extension.begin(), ports2del_extension.end());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
case 5:
|
||||||
|
case 7:
|
||||||
|
if (have_macc) {
|
||||||
|
ports2del.insert(ports2del.end(), ports2del_mult_acc.begin(), ports2del_mult_acc.end());
|
||||||
|
} else {
|
||||||
|
ports2del.insert(ports2del.end(), ports2del_mult_add.begin(), ports2del_mult_add.end());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto portname : ports2del) {
|
||||||
|
const RTLIL::SigSpec *port = &dsp->getPort(RTLIL::escape_id(portname));
|
||||||
|
if (!port)
|
||||||
|
log_error("%s port not found!", portname.c_str());
|
||||||
|
dsp->connections_.erase(RTLIL::escape_id(portname));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the sigmap
|
||||||
|
m_SigMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
} QlDspIORegs;
|
||||||
|
|
||||||
|
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,307 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel/sigtools.h"
|
||||||
|
#include "kernel/yosys.h"
|
||||||
|
|
||||||
|
USING_YOSYS_NAMESPACE
|
||||||
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
#include "ql_dsp_macc_pm.h"
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
bool use_dsp_cfg_params;
|
||||||
|
|
||||||
|
static void create_ql_macc_dsp(ql_dsp_macc_pm &pm)
|
||||||
|
{
|
||||||
|
auto &st = pm.st_ql_dsp_macc;
|
||||||
|
|
||||||
|
// Reject if multiplier drives anything else than either $add or $add and
|
||||||
|
// $mux
|
||||||
|
if (st.mux == nullptr && st.mul_nusers > 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine whether the output is taken from before or after the ff
|
||||||
|
bool out_ff;
|
||||||
|
if (st.ff_d_nusers == 2 && st.ff_q_nusers == 3) {
|
||||||
|
out_ff = true;
|
||||||
|
} else if (st.ff_d_nusers == 3 && st.ff_q_nusers == 2) {
|
||||||
|
out_ff = false;
|
||||||
|
} else {
|
||||||
|
// Illegal, cannot take the two outputs simulataneously
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No mux, the adder can driver either the ff or the ff + output
|
||||||
|
if (st.mux == nullptr) {
|
||||||
|
if (out_ff && st.add_nusers != 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!out_ff && st.add_nusers != 3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Mux present, the adder cannot drive anything else
|
||||||
|
else {
|
||||||
|
if (st.add_nusers != 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mux can driver either the ff or the ff + output
|
||||||
|
if (st.mux != nullptr) {
|
||||||
|
if (out_ff && st.mux_nusers != 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!out_ff && st.mux_nusers != 3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept only posedge clocked FFs
|
||||||
|
if (st.ff->getParam(ID(CLK_POLARITY)).as_int() != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Signed / unsigned
|
||||||
|
bool a_signed = st.mul->getParam(ID(A_SIGNED)).as_bool();
|
||||||
|
bool b_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;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// Too narrow
|
||||||
|
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 {
|
||||||
|
// Too wide
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (use_dsp_cfg_params)
|
||||||
|
cell_cfg_name = "_cfg_params";
|
||||||
|
else
|
||||||
|
cell_cfg_name = "_cfg_ports";
|
||||||
|
|
||||||
|
cell_full_name = cell_base_name + cell_size_name + cell_cfg_name;
|
||||||
|
|
||||||
|
type = RTLIL::escape_id(cell_full_name);
|
||||||
|
log("Inferring MACC %zux%zu->%zu as %s from:\n", a_width, b_width, z_width, RTLIL::unescape_id(type).c_str());
|
||||||
|
|
||||||
|
for (auto cell : {st.mul, st.add, st.mux, st.ff}) {
|
||||||
|
if (cell != nullptr) {
|
||||||
|
log(" %s (%s)\n", RTLIL::unescape_id(cell->name).c_str(), RTLIL::unescape_id(cell->type).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the DSP cell name
|
||||||
|
std::string name;
|
||||||
|
name += RTLIL::unescape_id(st.mul->name) + "_";
|
||||||
|
name += RTLIL::unescape_id(st.add->name) + "_";
|
||||||
|
if (st.mux != nullptr) {
|
||||||
|
name += RTLIL::unescape_id(st.mux->name) + "_";
|
||||||
|
}
|
||||||
|
name += RTLIL::unescape_id(st.ff->name);
|
||||||
|
|
||||||
|
// Add the DSP cell
|
||||||
|
RTLIL::Cell *cell = pm.module->addCell(RTLIL::escape_id(name), type);
|
||||||
|
|
||||||
|
// Set attributes
|
||||||
|
cell->set_bool_attribute(RTLIL::escape_id("is_inferred"), true);
|
||||||
|
|
||||||
|
// Get input/output data signals
|
||||||
|
RTLIL::SigSpec sig_a;
|
||||||
|
RTLIL::SigSpec sig_b;
|
||||||
|
RTLIL::SigSpec sig_z;
|
||||||
|
|
||||||
|
if (a_width >= b_width) {
|
||||||
|
sig_a = st.mul->getPort(ID(A));
|
||||||
|
sig_b = st.mul->getPort(ID(B));
|
||||||
|
} else {
|
||||||
|
sig_a = st.mul->getPort(ID(B));
|
||||||
|
sig_b = st.mul->getPort(ID(A));
|
||||||
|
}
|
||||||
|
|
||||||
|
sig_z = out_ff ? 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, a_signed);
|
||||||
|
sig_b.extend_u0(tgt_b_width, b_signed);
|
||||||
|
cell->setPort(RTLIL::escape_id("a_i"), sig_a);
|
||||||
|
cell->setPort(RTLIL::escape_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(RTLIL::escape_id("z_o"), sig_z);
|
||||||
|
|
||||||
|
// Connect clock, reset and enable
|
||||||
|
cell->setPort(RTLIL::escape_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(RTLIL::escape_id("reset_i"), rst);
|
||||||
|
cell->setPort(RTLIL::escape_id("load_acc_i"), ena);
|
||||||
|
|
||||||
|
// Insert feedback_i control logic used for clearing / loading the accumulator
|
||||||
|
if (st.mux != nullptr) {
|
||||||
|
RTLIL::SigSpec sig_s = st.mux->getPort(ID(S));
|
||||||
|
|
||||||
|
// Depending on the mux port ordering insert inverter if needed
|
||||||
|
log_assert(st.mux_ab == ID(A) || st.mux_ab == 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(RTLIL::escape_id("feedback_i"), sig_f);
|
||||||
|
}
|
||||||
|
// No acc clear/load
|
||||||
|
else {
|
||||||
|
cell->setPort(RTLIL::escape_id("feedback_i"), RTLIL::SigSpec(RTLIL::S0, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect control ports
|
||||||
|
cell->setPort(RTLIL::escape_id("unsigned_a_i"), RTLIL::SigSpec(a_signed ? RTLIL::S0 : RTLIL::S1));
|
||||||
|
cell->setPort(RTLIL::escape_id("unsigned_b_i"), RTLIL::SigSpec(b_signed ? RTLIL::S0 : RTLIL::S1));
|
||||||
|
|
||||||
|
// Connect config bits
|
||||||
|
if (use_dsp_cfg_params) {
|
||||||
|
cell->setParam(RTLIL::escape_id("SATURATE_ENABLE"), RTLIL::Const(RTLIL::S0));
|
||||||
|
cell->setParam(RTLIL::escape_id("SHIFT_RIGHT"), RTLIL::Const(RTLIL::S0, 6));
|
||||||
|
cell->setParam(RTLIL::escape_id("ROUND"), RTLIL::Const(RTLIL::S0));
|
||||||
|
cell->setParam(RTLIL::escape_id("REGISTER_INPUTS"), RTLIL::Const(RTLIL::S0));
|
||||||
|
// 3 - output post acc; 1 - output pre acc
|
||||||
|
cell->setParam(RTLIL::escape_id("OUTPUT_SELECT"), out_ff ? RTLIL::Const(1, 3) : RTLIL::Const(3, 3));
|
||||||
|
} else {
|
||||||
|
cell->setPort(RTLIL::escape_id("saturate_enable_i"), RTLIL::SigSpec(RTLIL::S0));
|
||||||
|
cell->setPort(RTLIL::escape_id("shift_right_i"), RTLIL::SigSpec(RTLIL::S0, 6));
|
||||||
|
cell->setPort(RTLIL::escape_id("round_i"), RTLIL::SigSpec(RTLIL::S0));
|
||||||
|
cell->setPort(RTLIL::escape_id("register_inputs_i"), RTLIL::SigSpec(RTLIL::S0));
|
||||||
|
// 3 - output post acc; 1 - output pre acc
|
||||||
|
cell->setPort(RTLIL::escape_id("output_select_i"), out_ff ? RTLIL::Const(1, 3) : RTLIL::Const(3, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subtract = (st.add->type == RTLIL::escape_id("$sub"));
|
||||||
|
cell->setPort(RTLIL::escape_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct QlDspMacc : public Pass {
|
||||||
|
|
||||||
|
QlDspMacc() : Pass("ql_dsp_macc", "Does something") {}
|
||||||
|
|
||||||
|
void help() override
|
||||||
|
{
|
||||||
|
log("\n");
|
||||||
|
log(" ql_dsp_macc [options] [selection]\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -use_dsp_cfg_params\n");
|
||||||
|
log(" By default use DSP blocks with configuration bits available at module ports.\n");
|
||||||
|
log(" Specifying this forces usage of DSP block with configuration bits available as module parameters\n");
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_flags() override { use_dsp_cfg_params = false; }
|
||||||
|
|
||||||
|
void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override
|
||||||
|
{
|
||||||
|
log_header(a_Design, "Executing QL_DSP_MACC pass.\n");
|
||||||
|
|
||||||
|
size_t argidx;
|
||||||
|
for (argidx = 1; argidx < a_Args.size(); argidx++) {
|
||||||
|
if (a_Args[argidx] == "-use_dsp_cfg_params") {
|
||||||
|
use_dsp_cfg_params = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
extra_args(a_Args, argidx, a_Design);
|
||||||
|
|
||||||
|
for (auto module : a_Design->selected_modules()) {
|
||||||
|
ql_dsp_macc_pm(module, module->selected_cells()).run_ql_dsp_macc(create_ql_macc_dsp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} QlDspMacc;
|
||||||
|
|
||||||
|
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,50 @@
|
||||||
|
pattern ql_dsp_macc
|
||||||
|
|
||||||
|
state <IdString> add_ba
|
||||||
|
state <IdString> mux_ab
|
||||||
|
|
||||||
|
state <int> mul_nusers
|
||||||
|
state <int> add_nusers
|
||||||
|
state <int> mux_nusers
|
||||||
|
state <int> ff_d_nusers
|
||||||
|
state <int> ff_q_nusers
|
||||||
|
|
||||||
|
match mul
|
||||||
|
select mul->type.in($mul)
|
||||||
|
select nusers(port(mul, \Y)) <= 3
|
||||||
|
set mul_nusers nusers(port(mul, \Y))
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
match add
|
||||||
|
select add->type.in($add, $sub)
|
||||||
|
choice <IdString> AB {\A, \B}
|
||||||
|
define <IdString> BA (AB == \A ? \B : \A)
|
||||||
|
index <SigSpec> port(add, AB) === port(mul, \Y)
|
||||||
|
select nusers(port(add, \Y)) <= 3
|
||||||
|
set add_nusers nusers(port(add, \Y))
|
||||||
|
set add_ba BA
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
match mux
|
||||||
|
select mux->type.in($mux)
|
||||||
|
choice <IdString> AB {\A, \B}
|
||||||
|
define <IdString> BA (AB == \A ? \B : \A)
|
||||||
|
index <SigSpec> port(mux, AB) === port(mul, \Y)
|
||||||
|
index <SigSpec> port(mux, BA) === port(add, \Y)
|
||||||
|
select nusers(port(mux, \Y)) <= 3
|
||||||
|
set mux_nusers nusers(port(mux, \Y))
|
||||||
|
set mux_ab AB
|
||||||
|
optional
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
match ff
|
||||||
|
select ff->type.in($dff, $adff, $dffe, $adffe)
|
||||||
|
index <SigSpec> port(ff, \D) === (mux == nullptr ? port(add, \Y) : port(mux, \Y))
|
||||||
|
index <SigSpec> port(ff, \Q) === port(add, add_ba)
|
||||||
|
set ff_d_nusers nusers(port(ff, \D))
|
||||||
|
set ff_q_nusers nusers(port(ff, \Q))
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code
|
||||||
|
accept;
|
||||||
|
endcode
|
|
@ -0,0 +1,359 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kernel/log.h"
|
||||||
|
#include "kernel/register.h"
|
||||||
|
#include "kernel/rtlil.h"
|
||||||
|
#include "kernel/sigtools.h"
|
||||||
|
|
||||||
|
USING_YOSYS_NAMESPACE
|
||||||
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
#define MODE_BITS_BASE_SIZE 80
|
||||||
|
#define MODE_BITS_EXTENSION_SIZE 13
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
struct QlDspSimdPass : public Pass {
|
||||||
|
|
||||||
|
QlDspSimdPass() : Pass("ql_dsp_simd", "Infers QuickLogic k6n10f DSP pairs that can operate in SIMD mode") {}
|
||||||
|
|
||||||
|
void help() override
|
||||||
|
{
|
||||||
|
log("\n");
|
||||||
|
log(" ql_dsp_simd [selection]\n");
|
||||||
|
log("\n");
|
||||||
|
log(" This pass identifies k6n10f DSP cells with identical configuration\n");
|
||||||
|
log(" and packs pairs of them together into other DSP cells that can\n");
|
||||||
|
log(" perform SIMD operation.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ..........................................
|
||||||
|
|
||||||
|
/// Describes DSP config unique to a whole DSP cell
|
||||||
|
struct DspConfig {
|
||||||
|
|
||||||
|
// Port connections
|
||||||
|
dict<RTLIL::IdString, RTLIL::SigSpec> connections;
|
||||||
|
|
||||||
|
// Whether DSPs pass configuration bits through ports of parameters
|
||||||
|
bool use_cfg_params;
|
||||||
|
|
||||||
|
// TODO: Possibly include parameters here. For now we have just
|
||||||
|
// connections.
|
||||||
|
|
||||||
|
DspConfig() = default;
|
||||||
|
|
||||||
|
DspConfig(const DspConfig &ref) = default;
|
||||||
|
DspConfig(DspConfig &&ref) = default;
|
||||||
|
|
||||||
|
unsigned int hash() const { return connections.hash(); }
|
||||||
|
|
||||||
|
bool operator==(const DspConfig &ref) const { return connections == ref.connections && use_cfg_params == ref.use_cfg_params; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ..........................................
|
||||||
|
|
||||||
|
// DSP control and config ports to consider and how to map them to ports
|
||||||
|
// of the target DSP cell
|
||||||
|
const std::vector<std::pair<std::string, std::string>> m_DspCfgPorts = {std::make_pair("clock_i", "clk"),
|
||||||
|
std::make_pair("reset_i", "reset"),
|
||||||
|
|
||||||
|
std::make_pair("feedback_i", "feedback"),
|
||||||
|
std::make_pair("load_acc_i", "load_acc"),
|
||||||
|
std::make_pair("unsigned_a_i", "unsigned_a"),
|
||||||
|
std::make_pair("unsigned_b_i", "unsigned_b"),
|
||||||
|
|
||||||
|
std::make_pair("subtract_i", "subtract")};
|
||||||
|
// For QL_DSP2 expand with configuration ports
|
||||||
|
const std::vector<std::pair<std::string, std::string>> m_DspCfgPorts_expand = {
|
||||||
|
std::make_pair("output_select_i", "output_select"), std::make_pair("saturate_enable_i", "saturate_enable"),
|
||||||
|
std::make_pair("shift_right_i", "shift_right"), std::make_pair("round_i", "round"), std::make_pair("register_inputs_i", "register_inputs")};
|
||||||
|
|
||||||
|
// For QL_DSP3 use parameters instead
|
||||||
|
const std::vector<std::string> m_DspParams2Mode = {"OUTPUT_SELECT", "SATURATE_ENABLE", "SHIFT_RIGHT", "ROUND", "REGISTER_INPUTS"};
|
||||||
|
|
||||||
|
// DSP data ports and how to map them to ports of the target DSP cell
|
||||||
|
const std::vector<std::pair<std::string, std::string>> m_DspDataPorts = {
|
||||||
|
std::make_pair("a_i", "a"), std::make_pair("b_i", "b"), std::make_pair("acc_fir_i", "acc_fir"),
|
||||||
|
std::make_pair("z_o", "z"), std::make_pair("dly_b_o", "dly_b"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// DSP parameters
|
||||||
|
const std::vector<std::string> m_DspParams = {"COEFF_3", "COEFF_2", "COEFF_1", "COEFF_0"};
|
||||||
|
|
||||||
|
// Source DSP cell type (SISD)
|
||||||
|
const std::string m_SisdDspType = "dsp_t1_10x9x32";
|
||||||
|
// Suffix for DSP cell with configuration parameters
|
||||||
|
const std::string m_SisdDspType_cfg_params_suffix = "_cfg_params";
|
||||||
|
|
||||||
|
// Target DSP cell types for the SIMD mode
|
||||||
|
const std::string m_SimdDspType_cfg_ports = "QL_DSP2";
|
||||||
|
const std::string m_SimdDspType_cfg_params = "QL_DSP3";
|
||||||
|
|
||||||
|
/// Temporary SigBit to SigBit helper map.
|
||||||
|
SigMap m_SigMap;
|
||||||
|
|
||||||
|
// ..........................................
|
||||||
|
|
||||||
|
void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override
|
||||||
|
{
|
||||||
|
log_header(a_Design, "Executing QL_DSP_SIMD pass.\n");
|
||||||
|
|
||||||
|
// Parse args
|
||||||
|
extra_args(a_Args, 1, a_Design);
|
||||||
|
|
||||||
|
// Process modules
|
||||||
|
for (auto module : a_Design->selected_modules()) {
|
||||||
|
|
||||||
|
// Setup the SigMap
|
||||||
|
m_SigMap.clear();
|
||||||
|
m_SigMap.set(module);
|
||||||
|
|
||||||
|
// Assemble DSP cell groups
|
||||||
|
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 (strncmp(cell->type.c_str(), RTLIL::escape_id(m_SisdDspType).c_str(), RTLIL::escape_id(m_SisdDspType).size()) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if it has the (* keep *) attribute set
|
||||||
|
if (cell->has_keep_attr()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to a group
|
||||||
|
const auto key = getDspConfig(cell);
|
||||||
|
groups[key].push_back(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const RTLIL::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;
|
||||||
|
|
||||||
|
bool use_cfg_params = config.use_cfg_params;
|
||||||
|
// Ensure an even number
|
||||||
|
size_t count = group.size();
|
||||||
|
if (count & 1)
|
||||||
|
count--;
|
||||||
|
|
||||||
|
// Map SIMD pairs
|
||||||
|
for (size_t i = 0; i < count; i += 2) {
|
||||||
|
const RTLIL::Cell *dsp_a = group[i];
|
||||||
|
const RTLIL::Cell *dsp_b = group[i + 1];
|
||||||
|
|
||||||
|
std::string name = stringf("simd%ld", i / 2);
|
||||||
|
std::string SimdDspType;
|
||||||
|
|
||||||
|
if (use_cfg_params)
|
||||||
|
SimdDspType = m_SimdDspType_cfg_params;
|
||||||
|
else
|
||||||
|
SimdDspType = m_SimdDspType_cfg_ports;
|
||||||
|
|
||||||
|
log(" SIMD: %s (%s) + %s (%s) => %s (%s)\n", RTLIL::unescape_id(dsp_a->name).c_str(), RTLIL::unescape_id(dsp_a->type).c_str(),
|
||||||
|
RTLIL::unescape_id(dsp_b->name).c_str(), RTLIL::unescape_id(dsp_b->type).c_str(), RTLIL::unescape_id(name).c_str(),
|
||||||
|
SimdDspType.c_str());
|
||||||
|
|
||||||
|
// Create the new cell
|
||||||
|
RTLIL::Cell *simd = module->addCell(RTLIL::escape_id(name), RTLIL::escape_id(SimdDspType));
|
||||||
|
|
||||||
|
// 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!", SimdDspType.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, std::string>> DspCfgPorts = m_DspCfgPorts;
|
||||||
|
if (!use_cfg_params)
|
||||||
|
DspCfgPorts.insert(DspCfgPorts.end(), m_DspCfgPorts_expand.begin(), m_DspCfgPorts_expand.end());
|
||||||
|
|
||||||
|
// Connect common ports
|
||||||
|
for (const auto &it : DspCfgPorts) {
|
||||||
|
auto sport = RTLIL::escape_id(it.first);
|
||||||
|
auto dport = RTLIL::escape_id(it.second);
|
||||||
|
|
||||||
|
simd->setPort(dport, config.connections.at(sport));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect data ports
|
||||||
|
for (const auto &it : m_DspDataPorts) {
|
||||||
|
auto sport = RTLIL::escape_id(it.first);
|
||||||
|
auto dport = RTLIL::escape_id(it.second);
|
||||||
|
|
||||||
|
size_t width;
|
||||||
|
bool isOutput;
|
||||||
|
|
||||||
|
std::tie(width, isOutput) = getPortInfo(simd, dport);
|
||||||
|
|
||||||
|
auto getConnection = [&](const RTLIL::Cell *cell) {
|
||||||
|
RTLIL::SigSpec sigspec;
|
||||||
|
if (cell->hasPort(sport)) {
|
||||||
|
const auto &sig = cell->getPort(sport);
|
||||||
|
sigspec.append(sig);
|
||||||
|
}
|
||||||
|
if (sigspec.bits().size() < width / 2) {
|
||||||
|
if (isOutput) {
|
||||||
|
for (size_t i = 0; i < width / 2 - sigspec.bits().size(); ++i) {
|
||||||
|
sigspec.append(RTLIL::SigSpec());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sigspec.append(RTLIL::SigSpec(RTLIL::Sx, width / 2 - sigspec.bits().size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sigspec;
|
||||||
|
};
|
||||||
|
|
||||||
|
RTLIL::SigSpec sigspec;
|
||||||
|
sigspec.append(getConnection(dsp_a));
|
||||||
|
sigspec.append(getConnection(dsp_b));
|
||||||
|
simd->setPort(dport, sigspec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concatenate FIR coefficient parameters into the single
|
||||||
|
// MODE_BITS parameter
|
||||||
|
std::vector<RTLIL::State> mode_bits;
|
||||||
|
for (const auto &it : m_DspParams) {
|
||||||
|
auto val_a = dsp_a->getParam(RTLIL::escape_id(it));
|
||||||
|
auto val_b = dsp_b->getParam(RTLIL::escape_id(it));
|
||||||
|
|
||||||
|
mode_bits.insert(mode_bits.end(), val_a.begin(), val_a.end());
|
||||||
|
mode_bits.insert(mode_bits.end(), val_b.begin(), val_b.end());
|
||||||
|
}
|
||||||
|
long unsigned int mode_bits_size = MODE_BITS_BASE_SIZE;
|
||||||
|
if (use_cfg_params) {
|
||||||
|
// Add additional config parameters if necessary
|
||||||
|
mode_bits.push_back(RTLIL::S1); // MODE_BITS[80] == F_MODE : Enable fractured mode
|
||||||
|
for (const auto &it : m_DspParams2Mode) {
|
||||||
|
log_assert(dsp_a->getParam(RTLIL::escape_id(it)) == dsp_b->getParam(RTLIL::escape_id(it)));
|
||||||
|
auto param = dsp_a->getParam(RTLIL::escape_id(it));
|
||||||
|
if (param.size() > 1) {
|
||||||
|
mode_bits.insert(mode_bits.end(), param.bits.begin(), param.bits.end());
|
||||||
|
} else {
|
||||||
|
mode_bits.push_back(param.bits[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mode_bits_size += MODE_BITS_EXTENSION_SIZE;
|
||||||
|
} else {
|
||||||
|
// Enable the fractured mode by connecting the control
|
||||||
|
// port.
|
||||||
|
simd->setPort(RTLIL::escape_id("f_mode"), RTLIL::S1);
|
||||||
|
}
|
||||||
|
simd->setParam(RTLIL::escape_id("MODE_BITS"), RTLIL::Const(mode_bits));
|
||||||
|
log_assert(mode_bits.size() == mode_bits_size);
|
||||||
|
|
||||||
|
// Handle the "is_inferred" attribute. If one of the fragments
|
||||||
|
// is not inferred mark the whole DSP as not inferred
|
||||||
|
bool is_inferred_a =
|
||||||
|
dsp_a->has_attribute(RTLIL::escape_id("is_inferred")) ? dsp_a->get_bool_attribute(RTLIL::escape_id("is_inferred")) : false;
|
||||||
|
bool is_inferred_b =
|
||||||
|
dsp_b->has_attribute(RTLIL::escape_id("is_inferred")) ? dsp_b->get_bool_attribute(RTLIL::escape_id("is_inferred")) : false;
|
||||||
|
|
||||||
|
simd->set_bool_attribute(RTLIL::escape_id("is_inferred"), is_inferred_a && is_inferred_b);
|
||||||
|
|
||||||
|
// Mark DSP parts for removal
|
||||||
|
cellsToRemove.push_back(dsp_a);
|
||||||
|
cellsToRemove.push_back(dsp_b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove old cells
|
||||||
|
for (const auto &cell : cellsToRemove) {
|
||||||
|
module->remove(const_cast<RTLIL::Cell *>(cell));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear
|
||||||
|
m_SigMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ..........................................
|
||||||
|
|
||||||
|
/// Looks up port width and direction in the cell definition and returns it.
|
||||||
|
/// Returns (0, false) if it cannot be determined.
|
||||||
|
std::pair<size_t, bool> getPortInfo(RTLIL::Cell *a_Cell, RTLIL::IdString a_Port)
|
||||||
|
{
|
||||||
|
if (!a_Cell->known()) {
|
||||||
|
return std::make_pair(0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the module defining the cell (the previous condition ensures
|
||||||
|
// that the pointers are valid)
|
||||||
|
RTLIL::Module *mod = a_Cell->module->design->module(a_Cell->type);
|
||||||
|
if (mod == nullptr) {
|
||||||
|
return std::make_pair(0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the wire representing the port
|
||||||
|
RTLIL::Wire *wire = mod->wire(a_Port);
|
||||||
|
if (wire == nullptr) {
|
||||||
|
return std::make_pair(0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(wire->width, wire->port_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a DSP cell populates and returns a DspConfig struct for it.
|
||||||
|
DspConfig getDspConfig(RTLIL::Cell *a_Cell)
|
||||||
|
{
|
||||||
|
DspConfig config;
|
||||||
|
|
||||||
|
string cell_type = a_Cell->type.str();
|
||||||
|
string suffix = m_SisdDspType_cfg_params_suffix;
|
||||||
|
|
||||||
|
bool use_cfg_params = cell_type.size() >= suffix.size() && 0 == cell_type.compare(cell_type.size() - suffix.size(), suffix.size(), suffix);
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, std::string>> DspCfgPorts = m_DspCfgPorts;
|
||||||
|
if (!use_cfg_params)
|
||||||
|
DspCfgPorts.insert(DspCfgPorts.end(), m_DspCfgPorts_expand.begin(), m_DspCfgPorts_expand.end());
|
||||||
|
|
||||||
|
config.use_cfg_params = use_cfg_params;
|
||||||
|
|
||||||
|
for (const auto &it : DspCfgPorts) {
|
||||||
|
auto port = RTLIL::escape_id(it.first);
|
||||||
|
|
||||||
|
// Port unconnected
|
||||||
|
if (!a_Cell->hasPort(port)) {
|
||||||
|
config.connections[port] = RTLIL::SigSpec(RTLIL::Sx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the port connection and map it to unique SigBits
|
||||||
|
const auto &orgSigSpec = a_Cell->getPort(port);
|
||||||
|
const auto &orgSigBits = orgSigSpec.bits();
|
||||||
|
|
||||||
|
RTLIL::SigSpec newSigSpec;
|
||||||
|
for (size_t i = 0; i < orgSigBits.size(); ++i) {
|
||||||
|
auto newSigBit = m_SigMap(orgSigBits[i]);
|
||||||
|
newSigSpec.append(newSigBit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store
|
||||||
|
config.connections[port] = newSigSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
} QlDspSimdPass;
|
||||||
|
|
||||||
|
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,265 @@
|
||||||
|
// 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 dsp_t1_20x18x64_cfg_ports (
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
|
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 (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)
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module dsp_t1_10x9x32_cfg_ports (
|
||||||
|
input [ 9:0] a_i,
|
||||||
|
input [ 8:0] b_i,
|
||||||
|
input [ 5:0] acc_fir_i,
|
||||||
|
output [18:0] z_o,
|
||||||
|
output [ 8:0] dly_b_o,
|
||||||
|
|
||||||
|
(* clkbuf_sink *)
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
|
parameter [9:0] COEFF_0 = 10'd0;
|
||||||
|
parameter [9:0] COEFF_1 = 10'd0;
|
||||||
|
parameter [9:0] COEFF_2 = 10'd0;
|
||||||
|
parameter [9:0] COEFF_3 = 10'd0;
|
||||||
|
|
||||||
|
wire [37:0] z;
|
||||||
|
wire [17:0] dly_b;
|
||||||
|
|
||||||
|
QL_DSP2 # (
|
||||||
|
.MODE_BITS ({10'd0, COEFF_3,
|
||||||
|
10'd0, COEFF_2,
|
||||||
|
10'd0, COEFF_1,
|
||||||
|
10'd0, COEFF_0})
|
||||||
|
) _TECHMAP_REPLACE_ (
|
||||||
|
.a ({10'd0, a_i}),
|
||||||
|
.b ({ 9'd0, b_i}),
|
||||||
|
.acc_fir (acc_fir_i),
|
||||||
|
.z (z),
|
||||||
|
.dly_b (dly_b),
|
||||||
|
|
||||||
|
.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'b1), // Enable fractuation, Use the lower half
|
||||||
|
.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)
|
||||||
|
);
|
||||||
|
|
||||||
|
assign z_o = z[18:0];
|
||||||
|
assign dly_b_o = dly_b_o[8:0];
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module dsp_t1_20x18x64_cfg_params (
|
||||||
|
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 subtract_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;
|
||||||
|
|
||||||
|
parameter [2:0] OUTPUT_SELECT = 3'd0;
|
||||||
|
parameter [0:0] SATURATE_ENABLE = 1'd0;
|
||||||
|
parameter [5:0] SHIFT_RIGHT = 6'd0;
|
||||||
|
parameter [0:0] ROUND = 1'd0;
|
||||||
|
parameter [0:0] REGISTER_INPUTS = 1'd0;
|
||||||
|
|
||||||
|
QL_DSP3 # (
|
||||||
|
.MODE_BITS ({
|
||||||
|
REGISTER_INPUTS,
|
||||||
|
ROUND,
|
||||||
|
SHIFT_RIGHT,
|
||||||
|
SATURATE_ENABLE,
|
||||||
|
OUTPUT_SELECT,
|
||||||
|
1'b0, // Not fractured
|
||||||
|
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),
|
||||||
|
.subtract (subtract_i)
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module dsp_t1_10x9x32_cfg_params (
|
||||||
|
input [ 9:0] a_i,
|
||||||
|
input [ 8:0] b_i,
|
||||||
|
input [ 5:0] acc_fir_i,
|
||||||
|
output [18:0] z_o,
|
||||||
|
output [ 8:0] dly_b_o,
|
||||||
|
|
||||||
|
(* clkbuf_sink *)
|
||||||
|
input clock_i,
|
||||||
|
input reset_i,
|
||||||
|
|
||||||
|
input [2:0] feedback_i,
|
||||||
|
input load_acc_i,
|
||||||
|
input unsigned_a_i,
|
||||||
|
input unsigned_b_i,
|
||||||
|
input subtract_i
|
||||||
|
);
|
||||||
|
|
||||||
|
parameter [9:0] COEFF_0 = 10'd0;
|
||||||
|
parameter [9:0] COEFF_1 = 10'd0;
|
||||||
|
parameter [9:0] COEFF_2 = 10'd0;
|
||||||
|
parameter [9:0] COEFF_3 = 10'd0;
|
||||||
|
|
||||||
|
parameter [2:0] OUTPUT_SELECT = 3'd0;
|
||||||
|
parameter [0:0] SATURATE_ENABLE = 1'd0;
|
||||||
|
parameter [5:0] SHIFT_RIGHT = 6'd0;
|
||||||
|
parameter [0:0] ROUND = 1'd0;
|
||||||
|
parameter [0:0] REGISTER_INPUTS = 1'd0;
|
||||||
|
|
||||||
|
wire [37:0] z;
|
||||||
|
wire [17:0] dly_b;
|
||||||
|
|
||||||
|
QL_DSP3 # (
|
||||||
|
.MODE_BITS ({
|
||||||
|
REGISTER_INPUTS,
|
||||||
|
ROUND,
|
||||||
|
SHIFT_RIGHT,
|
||||||
|
SATURATE_ENABLE,
|
||||||
|
OUTPUT_SELECT,
|
||||||
|
1'b1, // Fractured
|
||||||
|
10'd0, COEFF_3,
|
||||||
|
10'd0, COEFF_2,
|
||||||
|
10'd0, COEFF_1,
|
||||||
|
10'd0, COEFF_0
|
||||||
|
})
|
||||||
|
) _TECHMAP_REPLACE_ (
|
||||||
|
.a ({10'd0, a_i}),
|
||||||
|
.b ({ 9'd0, b_i}),
|
||||||
|
.acc_fir (acc_fir_i),
|
||||||
|
.z (z),
|
||||||
|
.dly_b (dly_b),
|
||||||
|
|
||||||
|
.clk (clock_i),
|
||||||
|
.reset (reset_i),
|
||||||
|
|
||||||
|
.feedback (feedback_i),
|
||||||
|
.load_acc (load_acc_i),
|
||||||
|
.unsigned_a (unsigned_a_i),
|
||||||
|
.unsigned_b (unsigned_b_i),
|
||||||
|
.subtract (subtract_i)
|
||||||
|
);
|
||||||
|
|
||||||
|
assign z_o = z[18:0];
|
||||||
|
assign dly_b_o = dly_b_o[8:0];
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
// 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 \$__QL_MUL20X18 (input [19:0] A, input [17:0] B, output [37:0] Y);
|
||||||
|
parameter A_SIGNED = 0;
|
||||||
|
parameter B_SIGNED = 0;
|
||||||
|
parameter A_WIDTH = 0;
|
||||||
|
parameter B_WIDTH = 0;
|
||||||
|
parameter Y_WIDTH = 0;
|
||||||
|
|
||||||
|
wire [19:0] a;
|
||||||
|
wire [17:0] b;
|
||||||
|
wire [37:0] z;
|
||||||
|
|
||||||
|
assign a = (A_WIDTH == 20) ? A :
|
||||||
|
(A_SIGNED) ? {{(20 - A_WIDTH){A[A_WIDTH-1]}}, A} :
|
||||||
|
{{(20 - A_WIDTH){1'b0}}, A};
|
||||||
|
|
||||||
|
assign b = (B_WIDTH == 18) ? B :
|
||||||
|
(B_SIGNED) ? {{(18 - B_WIDTH){B[B_WIDTH-1]}}, B} :
|
||||||
|
{{(18 - B_WIDTH){1'b0}}, B};
|
||||||
|
|
||||||
|
generate if (`USE_DSP_CFG_PARAMS == 0) begin
|
||||||
|
(* is_inferred=1 *)
|
||||||
|
dsp_t1_20x18x64_cfg_ports _TECHMAP_REPLACE_ (
|
||||||
|
.a_i (a),
|
||||||
|
.b_i (b),
|
||||||
|
.acc_fir_i (6'd0),
|
||||||
|
.z_o (z),
|
||||||
|
|
||||||
|
.feedback_i (3'd0),
|
||||||
|
.load_acc_i (1'b0),
|
||||||
|
.unsigned_a_i (!A_SIGNED),
|
||||||
|
.unsigned_b_i (!B_SIGNED),
|
||||||
|
|
||||||
|
.output_select_i (3'd0),
|
||||||
|
.saturate_enable_i (1'b0),
|
||||||
|
.shift_right_i (6'd0),
|
||||||
|
.round_i (1'b0),
|
||||||
|
.subtract_i (1'b0),
|
||||||
|
.register_inputs_i (1'b0)
|
||||||
|
);
|
||||||
|
end else begin
|
||||||
|
(* is_inferred=1 *)
|
||||||
|
dsp_t1_20x18x64_cfg_params #(
|
||||||
|
.OUTPUT_SELECT (3'd0),
|
||||||
|
.SATURATE_ENABLE (1'b0),
|
||||||
|
.SHIFT_RIGHT (6'd0),
|
||||||
|
.ROUND (1'b0),
|
||||||
|
.REGISTER_INPUTS (1'b0)
|
||||||
|
) TECHMAP_REPLACE_ (
|
||||||
|
.a_i (a),
|
||||||
|
.b_i (b),
|
||||||
|
.acc_fir_i (6'd0),
|
||||||
|
.z_o (z),
|
||||||
|
|
||||||
|
.feedback_i (3'd0),
|
||||||
|
.load_acc_i (1'b0),
|
||||||
|
.unsigned_a_i (!A_SIGNED),
|
||||||
|
.unsigned_b_i (!B_SIGNED),
|
||||||
|
|
||||||
|
.subtract_i (1'b0)
|
||||||
|
);
|
||||||
|
end endgenerate
|
||||||
|
|
||||||
|
assign Y = z;
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module \$__QL_MUL10X9 (input [9:0] A, input [8:0] B, output [18:0] Y);
|
||||||
|
parameter A_SIGNED = 0;
|
||||||
|
parameter B_SIGNED = 0;
|
||||||
|
parameter A_WIDTH = 0;
|
||||||
|
parameter B_WIDTH = 0;
|
||||||
|
parameter Y_WIDTH = 0;
|
||||||
|
|
||||||
|
wire [ 9:0] a;
|
||||||
|
wire [ 8:0] b;
|
||||||
|
wire [18:0] z;
|
||||||
|
|
||||||
|
assign a = (A_WIDTH == 10) ? A :
|
||||||
|
(A_SIGNED) ? {{(10 - A_WIDTH){A[A_WIDTH-1]}}, A} :
|
||||||
|
{{(10 - A_WIDTH){1'b0}}, A};
|
||||||
|
|
||||||
|
assign b = (B_WIDTH == 9) ? B :
|
||||||
|
(B_SIGNED) ? {{( 9 - B_WIDTH){B[B_WIDTH-1]}}, B} :
|
||||||
|
{{( 9 - B_WIDTH){1'b0}}, B};
|
||||||
|
|
||||||
|
generate if (`USE_DSP_CFG_PARAMS == 0) begin
|
||||||
|
(* is_inferred=1 *)
|
||||||
|
dsp_t1_10x9x32_cfg_ports _TECHMAP_REPLACE_ (
|
||||||
|
.a_i (a),
|
||||||
|
.b_i (b),
|
||||||
|
.acc_fir_i (6'd0),
|
||||||
|
.z_o (z),
|
||||||
|
|
||||||
|
.feedback_i (3'd0),
|
||||||
|
.load_acc_i (1'b0),
|
||||||
|
.unsigned_a_i (!A_SIGNED),
|
||||||
|
.unsigned_b_i (!B_SIGNED),
|
||||||
|
|
||||||
|
.output_select_i (3'd0),
|
||||||
|
.saturate_enable_i (1'b0),
|
||||||
|
.shift_right_i (6'd0),
|
||||||
|
.round_i (1'b0),
|
||||||
|
.subtract_i (1'b0),
|
||||||
|
.register_inputs_i (1'b0)
|
||||||
|
);
|
||||||
|
end else begin
|
||||||
|
(* is_inferred=1 *)
|
||||||
|
dsp_t1_10x9x32_cfg_params #(
|
||||||
|
.OUTPUT_SELECT (3'd0),
|
||||||
|
.SATURATE_ENABLE (1'b0),
|
||||||
|
.SHIFT_RIGHT (6'd0),
|
||||||
|
.ROUND (1'b0),
|
||||||
|
.REGISTER_INPUTS (1'b0)
|
||||||
|
) TECHMAP_REPLACE_ (
|
||||||
|
.a_i (a),
|
||||||
|
.b_i (b),
|
||||||
|
.acc_fir_i (6'd0),
|
||||||
|
.z_o (z),
|
||||||
|
|
||||||
|
.feedback_i (3'd0),
|
||||||
|
.load_acc_i (1'b0),
|
||||||
|
.unsigned_a_i (!A_SIGNED),
|
||||||
|
.unsigned_b_i (!B_SIGNED),
|
||||||
|
|
||||||
|
.subtract_i (1'b0)
|
||||||
|
);
|
||||||
|
end endgenerate
|
||||||
|
|
||||||
|
assign Y = z;
|
||||||
|
|
||||||
|
endmodule
|
File diff suppressed because it is too large
Load Diff
|
@ -46,6 +46,15 @@ struct SynthQuickLogicPass : public ScriptPass {
|
||||||
log(" - pp3: PolarPro 3 \n");
|
log(" - pp3: PolarPro 3 \n");
|
||||||
log(" - qlf_k6n10f: K6N10f\n");
|
log(" - qlf_k6n10f: K6N10f\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -nodsp\n");
|
||||||
|
log(" do not use dsp_t1_* to implement multipliers and associated logic\n");
|
||||||
|
log(" (qlf_k6n10f only).\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -use_dsp_cfg_params\n");
|
||||||
|
log(" By default use DSP blocks with configuration bits available at module\n");
|
||||||
|
log(" ports. Specifying this forces usage of DSP block with configuration\n");
|
||||||
|
log(" bits available as module parameters.\n");
|
||||||
|
log("\n");
|
||||||
log(" -nocarry\n");
|
log(" -nocarry\n");
|
||||||
log(" do not use adder_carry cells in output netlist.\n");
|
log(" do not use adder_carry cells in output netlist.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -74,7 +83,7 @@ struct SynthQuickLogicPass : public ScriptPass {
|
||||||
}
|
}
|
||||||
|
|
||||||
string top_opt, blif_file, edif_file, family, currmodule, verilog_file, lib_path;
|
string top_opt, blif_file, edif_file, family, currmodule, verilog_file, lib_path;
|
||||||
bool abc9, inferAdder, nobram, bramTypes;
|
bool abc9, inferAdder, nobram, bramTypes, dsp;
|
||||||
|
|
||||||
void clear_flags() override
|
void clear_flags() override
|
||||||
{
|
{
|
||||||
|
@ -89,6 +98,7 @@ struct SynthQuickLogicPass : public ScriptPass {
|
||||||
nobram = false;
|
nobram = false;
|
||||||
bramTypes = false;
|
bramTypes = false;
|
||||||
lib_path = "+/quicklogic/";
|
lib_path = "+/quicklogic/";
|
||||||
|
dsp = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_scratchpad_defaults(RTLIL::Design *design) {
|
void set_scratchpad_defaults(RTLIL::Design *design) {
|
||||||
|
@ -149,6 +159,14 @@ struct SynthQuickLogicPass : public ScriptPass {
|
||||||
bramTypes = true;
|
bramTypes = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-nodsp" || args[argidx] == "-no_dsp") {
|
||||||
|
dsp = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-use_dsp_cfg_params") {
|
||||||
|
use_dsp_cfg_params = " -use_dsp_cfg_params";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
@ -184,6 +202,8 @@ struct SynthQuickLogicPass : public ScriptPass {
|
||||||
read_simlibs += stringf(" %sqlf_k6n10f/brams_sim.v", lib_path.c_str());
|
read_simlibs += stringf(" %sqlf_k6n10f/brams_sim.v", lib_path.c_str());
|
||||||
if (bramTypes)
|
if (bramTypes)
|
||||||
read_simlibs += stringf(" %sqlf_k6n10f/bram_types_sim.v", lib_path.c_str());
|
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());
|
||||||
}
|
}
|
||||||
run(read_simlibs);
|
run(read_simlibs);
|
||||||
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
|
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
|
||||||
|
@ -208,6 +228,24 @@ struct SynthQuickLogicPass : public ScriptPass {
|
||||||
run("share");
|
run("share");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (check_label("map_dsp", "(for qlf_k6n10f, skip if -nodsp)")
|
||||||
|
&& ((dsp && family == "qlf_k6n10f") || help_mode)) {
|
||||||
|
run("wreduce t:$mul");
|
||||||
|
run("ql_dsp_macc" + use_dsp_cfg_params);
|
||||||
|
|
||||||
|
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 (use_dsp_cfg_params.empty())
|
||||||
|
run("techmap -map " + lib_path + family + "/dsp_map.v -D USE_DSP_CFG_PARAMS=0");
|
||||||
|
else
|
||||||
|
run("techmap -map " + lib_path + family + "/dsp_map.v -D USE_DSP_CFG_PARAMS=1");
|
||||||
|
run("ql_dsp_simd");
|
||||||
|
run("techmap -map " + lib_path + family + "/dsp_final_map.v");
|
||||||
|
run("ql_dsp_io_regs");
|
||||||
|
}
|
||||||
|
|
||||||
if (check_label("coarse")) {
|
if (check_label("coarse")) {
|
||||||
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
|
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
|
||||||
run("opt_expr");
|
run("opt_expr");
|
||||||
|
@ -219,15 +257,16 @@ struct SynthQuickLogicPass : public ScriptPass {
|
||||||
run("opt_clean");
|
run("opt_clean");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_label("map_bram", "(for qlf_k6n10f, skip if -no_bram)") && (help_mode || family == "qlf_k6n10f")) {
|
if (check_label("map_bram", "(for qlf_k6n10f, skip if -no_bram)")) {
|
||||||
|
if(family == "qlf_k6n10f" || help_mode)
|
||||||
run("memory_libmap -lib " + lib_path + family + "/libmap_brams.txt");
|
run("memory_libmap -lib " + lib_path + family + "/libmap_brams.txt");
|
||||||
run("ql_bram_merge");
|
run("ql_bram_merge");
|
||||||
run("techmap -map " + lib_path + family + "/libmap_brams_map.v");
|
run("techmap -map " + lib_path + family + "/libmap_brams_map.v");
|
||||||
run("techmap -autoproc -map " + lib_path + family + "/brams_map.v");
|
run("techmap -autoproc -map " + lib_path + family + "/brams_map.v");
|
||||||
run("techmap -map " + lib_path + family + "/brams_final_map.v");
|
run("techmap -map " + lib_path + family + "/brams_final_map.v");
|
||||||
|
|
||||||
if (help_mode || bramTypes) {
|
if (bramTypes || help_mode) {
|
||||||
run("ql_bram_types");
|
run("ql_bram_types", "(if -bramtypes)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue