yosys/techlibs/rapidflex/src/rf_dsp_mad.cc

193 lines
5.2 KiB
C++

#include "kernel/sigtools.h"
#include "kernel/yosys.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
#include "rf_dsp_mad_pm.h"
static void create_rf_mad_dsp(rf_dsp_mad_pm &pm) {
auto &st = pm.st_rf_dsp_mad;
// Reject if multiplier drives anything else than $add
if (st.mul_nusers > 2) {
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 c_width = GetSize(st.add->getPort(ID(A)));
if (st.add_ba == ID(B)) {
c_width = GetSize(st.add->getPort(ID(B)));
}
size_t z_width = GetSize(st.add->getPort(ID(Y)));
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();
bool c_signed = st.add->getParam(ID(A_SIGNED)).as_bool();
if (st.add_ba == ID(B)) {
c_signed = st.add->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_c_width;
size_t tgt_z_width;
string cell_base_name = "mad";
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 <= 12 && max_width <= 10 && z_width <= 22) {
cell_size_name = "12x10x22";
tgt_a_width = 12;
tgt_b_width = 10;
tgt_c_width = 22;
tgt_z_width = 22;
} else if (min_width <= 24 && max_width <= 20 && z_width <= 44) {
cell_size_name = "24x20x44";
tgt_a_width = 24;
tgt_b_width = 20;
tgt_c_width = 44;
tgt_z_width = 44;
} else {
// Too wide
return;
}
cell_full_name = cell_base_name + cell_size_name + cell_cfg_name;
type = RTLIL::escape_id(cell_full_name);
log("Inferring MAD %zux%zu+%zu->%zu as %s from:\n", a_width, b_width, c_width,
z_width, RTLIL::unescape_id(type).c_str());
for (auto cell : {st.mul, st.add}) {
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) + "_";
// 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_c;
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_c = st.add->getPort(ID(A));
if (st.add_ba == ID(B)) {
sig_c = st.add->getPort(ID(B));
}
sig_z = st.add->getPort(ID(Y));
// 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);
sig_c.extend_u0(tgt_c_width, c_signed);
cell->setPort(RTLIL::escape_id("A0"), sig_a);
cell->setPort(RTLIL::escape_id("B0"), sig_b);
// Connect input data port, pad if needed
if ((size_t)GetSize(sig_c) < tgt_c_width) {
auto *wire = pm.module->addWire(NEW_ID, tgt_c_width - GetSize(sig_c));
sig_c.append(wire);
}
cell->setPort(RTLIL::escape_id("C0"), sig_c);
// 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("Y"), sig_z);
bool subtract = (st.add->type == RTLIL::escape_id("$sub"));
if (subtract) {
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);
}
struct RfDspMacc : public Pass {
// Local variables
bool show_help;
RfDspMacc()
: Pass("rf_dsp_mad", "Extract multiply-add and multiply-subtract "
"operators and map to dedicated DSPs") {}
void help() override {
log("\n");
log(" rf_dsp_mad [options] [selection]\n");
log("\n");
log(" Extract multiply-add and multiply-subtract operators and map to "
"dedicated DSPs\n");
log("\n");
log(" -help: show help desk\n");
log("\n");
}
void clear_flags() override { show_help = false; }
void execute(std::vector<std::string> a_Args,
RTLIL::Design *a_Design) override {
log_header(a_Design, "Executing RF_DSP_MAD pass.\n");
size_t argidx;
for (argidx = 1; argidx < a_Args.size(); argidx++) {
if (a_Args[argidx] == "-help") {
show_help = true;
continue;
}
break;
}
extra_args(a_Args, argidx, a_Design);
if (show_help) {
help();
return;
}
for (auto module : a_Design->selected_modules()) {
rf_dsp_mad_pm(module, module->selected_cells())
.run_rf_dsp_mad(create_rf_mad_dsp);
}
}
} RfDspMad;
PRIVATE_NAMESPACE_END