mirror of https://github.com/YosysHQ/nextpnr.git
1129 lines
47 KiB
C++
1129 lines
47 KiB
C++
#include "design_utils.h"
|
|
#include "log.h"
|
|
#include "nextpnr.h"
|
|
|
|
#define HIMBAECHEL_CONSTIDS "uarch/gowin/constids.inc"
|
|
#include "himbaechel_constids.h"
|
|
#include "himbaechel_helpers.h"
|
|
|
|
#include "gowin.h"
|
|
#include "gowin_utils.h"
|
|
#include "pack.h"
|
|
|
|
#include <cinttypes>
|
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
// ===================================
|
|
// DSP
|
|
// ===================================
|
|
void GowinPacker::pass_net_type(CellInfo *ci, IdString port)
|
|
{
|
|
const NetInfo *net = ci->getPort(port);
|
|
std::string connected_net = "NET";
|
|
if (net != nullptr) {
|
|
if (net->name == ctx->id("$PACKER_VCC")) {
|
|
connected_net = "VCC";
|
|
} else if (net->name == ctx->id("$PACKER_GND")) {
|
|
connected_net = "GND";
|
|
}
|
|
ci->setAttr(ctx->idf("NET_%s", port.c_str(ctx)), connected_net);
|
|
} else {
|
|
ci->setAttr(ctx->idf("NET_%s", port.c_str(ctx)), std::string(""));
|
|
}
|
|
}
|
|
|
|
void GowinPacker::pack_dsp(void)
|
|
{
|
|
std::vector<std::unique_ptr<CellInfo>> new_cells;
|
|
log_info("Pack DSP...\n");
|
|
|
|
std::vector<std::unique_ptr<CellInfo>> new_dsps;
|
|
std::vector<CellInfo *> dsp_heads;
|
|
std::vector<IdString> cells_to_remove;
|
|
|
|
auto process_dsp_cell = [&](CellInfo *ci) {
|
|
if (ctx->verbose) {
|
|
log_info(" pack %s %s\n", ci->type.c_str(ctx), ctx->nameOf(ci));
|
|
}
|
|
switch (ci->type.hash()) {
|
|
case ID_PADD9: {
|
|
gwu.remove_brackets(ci);
|
|
pass_net_type(ci, id_ASEL);
|
|
|
|
// ADD_SUB wire
|
|
IdString add_sub_net = ctx->id("$PACKER_GND");
|
|
if (ci->params.count(ctx->id("ADD_SUB"))) {
|
|
if (ci->params.at(ctx->id("ADD_SUB")).as_int64() == 1) {
|
|
add_sub_net = ctx->id("$PACKER_VCC");
|
|
}
|
|
}
|
|
ci->addInput(ctx->id("ADDSUB"));
|
|
ci->connectPort(ctx->id("ADDSUB"), ctx->nets.at(add_sub_net).get());
|
|
|
|
// PADD does not have outputs to the outside of the DSP -
|
|
// it is always connected to the inputs of the multiplier;
|
|
// to emulate a separate PADD primitive, we use
|
|
// multiplication by input C, equal to 1. We can switch the
|
|
// multiplier to multiplication mode by C in gowin_pack,
|
|
// but we will have to generate the value 1 at input C
|
|
// here.
|
|
ci->addInput(ctx->id("C0"));
|
|
ci->connectPort(ctx->id("C0"), ctx->nets.at(ctx->id("$PACKER_VCC")).get());
|
|
for (int i = 1; i < 9; ++i) {
|
|
ci->addInput(ctx->idf("C%d", i));
|
|
ci->connectPort(ctx->idf("C%d", i), ctx->nets.at(ctx->id("$PACKER_GND")).get());
|
|
}
|
|
// mark mult9x9 as used by making cluster
|
|
ci->cluster = ci->name;
|
|
ci->constr_abs_z = false;
|
|
ci->constr_x = 0;
|
|
ci->constr_y = 0;
|
|
ci->constr_y = 0;
|
|
|
|
IdString mult_name = gwu.create_aux_name(ci->name);
|
|
std::unique_ptr<CellInfo> mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(mult_cell));
|
|
CellInfo *mult_ci = new_cells.back().get();
|
|
|
|
mult_ci->cluster = ci->name;
|
|
mult_ci->constr_x = 0;
|
|
mult_ci->constr_y = 0;
|
|
mult_ci->constr_z = gwu.get_dsp_mult_from_padd(0);
|
|
|
|
// DSP head?
|
|
if (gwu.dsp_bus_src(ci, "SI", 9) == nullptr && gwu.dsp_bus_dst(ci, "SBO", 9) == nullptr) {
|
|
for (int i = 0; i < 9; ++i) {
|
|
ci->disconnectPort(ctx->idf("SI%d", i));
|
|
ci->disconnectPort(ctx->idf("SBO%d", i));
|
|
}
|
|
dsp_heads.push_back(ci);
|
|
if (ctx->verbose) {
|
|
log_info(" found a DSP head: %s\n", ctx->nameOf(ci));
|
|
}
|
|
}
|
|
} break;
|
|
case ID_PADD18: {
|
|
pass_net_type(ci, id_ASEL);
|
|
gwu.remove_brackets(ci);
|
|
|
|
// ADD_SUB wire
|
|
IdString add_sub_net = ctx->id("$PACKER_GND");
|
|
if (ci->params.count(ctx->id("ADD_SUB"))) {
|
|
if (ci->params.at(ctx->id("ADD_SUB")).as_int64() == 1) {
|
|
add_sub_net = ctx->id("$PACKER_VCC");
|
|
}
|
|
}
|
|
ci->addInput(ctx->id("ADDSUB"));
|
|
ci->connectPort(ctx->id("ADDSUB"), ctx->nets.at(add_sub_net).get());
|
|
|
|
// XXX form C as 1
|
|
ci->addInput(ctx->id("C0"));
|
|
ci->connectPort(ctx->id("C0"), ctx->nets.at(ctx->id("$PACKER_VCC")).get());
|
|
for (int i = 1; i < 18; ++i) {
|
|
ci->addInput(ctx->idf("C%d", i));
|
|
ci->connectPort(ctx->idf("C%d", i), ctx->nets.at(ctx->id("$PACKER_GND")).get());
|
|
}
|
|
//
|
|
// add padd9s and mult9s as a children
|
|
ci->cluster = ci->name;
|
|
ci->constr_abs_z = false;
|
|
ci->constr_x = 0;
|
|
ci->constr_y = 0;
|
|
ci->constr_z = 0;
|
|
ci->constr_children.clear();
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
IdString padd_name = gwu.create_aux_name(ci->name, i * 2);
|
|
std::unique_ptr<CellInfo> padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(padd_cell));
|
|
CellInfo *padd_ci = new_cells.back().get();
|
|
|
|
padd_ci->cluster = ci->name;
|
|
padd_ci->constr_abs_z = false;
|
|
padd_ci->constr_x = 0;
|
|
padd_ci->constr_y = 0;
|
|
padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::PADD18_0_0_Z + i;
|
|
|
|
IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1);
|
|
std::unique_ptr<CellInfo> mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(mult_cell));
|
|
CellInfo *mult_ci = new_cells.back().get();
|
|
|
|
mult_ci->cluster = ci->name;
|
|
mult_ci->constr_abs_z = false;
|
|
mult_ci->constr_x = 0;
|
|
mult_ci->constr_y = 0;
|
|
mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::PADD18_0_0_Z + i;
|
|
}
|
|
// DSP head?
|
|
if (gwu.dsp_bus_src(ci, "SI", 18) == nullptr && gwu.dsp_bus_dst(ci, "SBO", 18) == nullptr) {
|
|
for (int i = 0; i < 18; ++i) {
|
|
ci->disconnectPort(ctx->idf("SI%d", i));
|
|
ci->disconnectPort(ctx->idf("SBO%d", i));
|
|
}
|
|
dsp_heads.push_back(ci);
|
|
if (ctx->verbose) {
|
|
log_info(" found a DSP head: %s\n", ctx->nameOf(ci));
|
|
}
|
|
}
|
|
} break;
|
|
case ID_MULT9X9: {
|
|
pass_net_type(ci, id_ASEL);
|
|
pass_net_type(ci, id_BSEL);
|
|
gwu.remove_brackets(ci);
|
|
|
|
// add padd9 as a child
|
|
ci->cluster = ci->name;
|
|
ci->constr_abs_z = false;
|
|
ci->constr_x = 0;
|
|
ci->constr_y = 0;
|
|
ci->constr_z = 0;
|
|
ci->constr_children.clear();
|
|
|
|
IdString padd_name = gwu.create_aux_name(ci->name);
|
|
std::unique_ptr<CellInfo> padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(padd_cell));
|
|
CellInfo *padd_ci = new_cells.back().get();
|
|
|
|
padd_ci->cluster = ci->name;
|
|
padd_ci->constr_abs_z = false;
|
|
padd_ci->constr_x = 0;
|
|
padd_ci->constr_y = 0;
|
|
padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULT9X9_0_0_Z;
|
|
|
|
// DSP head?
|
|
if (gwu.dsp_bus_src(ci, "SIA", 9) == nullptr && gwu.dsp_bus_src(ci, "SIB", 9) == nullptr) {
|
|
for (int i = 0; i < 9; ++i) {
|
|
ci->disconnectPort(ctx->idf("SIA%d", i));
|
|
ci->disconnectPort(ctx->idf("SIB%d", i));
|
|
}
|
|
dsp_heads.push_back(ci);
|
|
if (ctx->verbose) {
|
|
log_info(" found a DSP head: %s\n", ctx->nameOf(ci));
|
|
}
|
|
}
|
|
} break;
|
|
case ID_MULT12X12: {
|
|
gwu.remove_brackets(ci);
|
|
} break;
|
|
case ID_MULT18X18: {
|
|
pass_net_type(ci, id_ASEL);
|
|
pass_net_type(ci, id_BSEL);
|
|
gwu.remove_brackets(ci);
|
|
|
|
// add padd9s and mult9s as a children
|
|
ci->cluster = ci->name;
|
|
ci->constr_abs_z = false;
|
|
ci->constr_x = 0;
|
|
ci->constr_y = 0;
|
|
ci->constr_z = 0;
|
|
ci->constr_children.clear();
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
IdString padd_name = gwu.create_aux_name(ci->name, i * 2);
|
|
std::unique_ptr<CellInfo> padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(padd_cell));
|
|
CellInfo *padd_ci = new_cells.back().get();
|
|
|
|
padd_ci->cluster = ci->name;
|
|
padd_ci->constr_abs_z = false;
|
|
padd_ci->constr_x = 0;
|
|
padd_ci->constr_y = 0;
|
|
padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULT18X18_0_0_Z + i;
|
|
|
|
IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1);
|
|
std::unique_ptr<CellInfo> mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(mult_cell));
|
|
CellInfo *mult_ci = new_cells.back().get();
|
|
|
|
mult_ci->cluster = ci->name;
|
|
mult_ci->constr_abs_z = false;
|
|
mult_ci->constr_x = 0;
|
|
mult_ci->constr_y = 0;
|
|
mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::MULT18X18_0_0_Z + i;
|
|
}
|
|
|
|
// DSP head?
|
|
if (gwu.dsp_bus_src(ci, "SIA", 18) == nullptr && gwu.dsp_bus_src(ci, "SIB", 18) == nullptr) {
|
|
for (int i = 0; i < 18; ++i) {
|
|
ci->disconnectPort(ctx->idf("SIA%d", i));
|
|
ci->disconnectPort(ctx->idf("SIB%d", i));
|
|
}
|
|
dsp_heads.push_back(ci);
|
|
if (ctx->verbose) {
|
|
log_info(" found a DSP head: %s\n", ctx->nameOf(ci));
|
|
}
|
|
}
|
|
} break;
|
|
case ID_ALU54D: {
|
|
pass_net_type(ci, id_ACCLOAD);
|
|
gwu.remove_brackets(ci);
|
|
|
|
// ACCLOAD - It looks like these wires are always connected to each other.
|
|
ci->cell_bel_pins.at(id_ACCLOAD).clear();
|
|
ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ACCLOAD0);
|
|
ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ACCLOAD1);
|
|
|
|
// add padd9s and mult9s as a children
|
|
ci->cluster = ci->name;
|
|
ci->constr_abs_z = false;
|
|
ci->constr_x = 0;
|
|
ci->constr_y = 0;
|
|
ci->constr_z = 0;
|
|
ci->constr_children.clear();
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
IdString padd_name = gwu.create_aux_name(ci->name, i * 2);
|
|
std::unique_ptr<CellInfo> padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(padd_cell));
|
|
CellInfo *padd_ci = new_cells.back().get();
|
|
|
|
padd_ci->cluster = ci->name;
|
|
padd_ci->constr_abs_z = false;
|
|
padd_ci->constr_x = 0;
|
|
padd_ci->constr_y = 0;
|
|
padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::ALU54D_0_Z + 4 * (i / 2) + (i % 2);
|
|
|
|
IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1);
|
|
std::unique_ptr<CellInfo> mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(mult_cell));
|
|
CellInfo *mult_ci = new_cells.back().get();
|
|
|
|
mult_ci->cluster = ci->name;
|
|
mult_ci->constr_abs_z = false;
|
|
mult_ci->constr_x = 0;
|
|
mult_ci->constr_y = 0;
|
|
mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::ALU54D_0_Z + 4 * (i / 2) + (i % 2);
|
|
}
|
|
|
|
// DSP head?
|
|
if (gwu.dsp_bus_src(ci, "CASI", 55) == nullptr) {
|
|
for (int i = 0; i < 55; ++i) {
|
|
ci->disconnectPort(ctx->idf("CASI%d", i));
|
|
}
|
|
dsp_heads.push_back(ci);
|
|
if (ctx->verbose) {
|
|
log_info(" found a DSP head: %s\n", ctx->nameOf(ci));
|
|
}
|
|
}
|
|
} break;
|
|
case ID_MULTALU18X18: {
|
|
// Ports C and D conflict so we need to know the operating mode here.
|
|
if (ci->params.count(id_MULTALU18X18_MODE) == 0) {
|
|
ci->setParam(id_MULTALU18X18_MODE, 0);
|
|
}
|
|
int multalu18x18_mode = ci->params.at(id_MULTALU18X18_MODE).as_int64();
|
|
if (multalu18x18_mode < 0 || multalu18x18_mode > 2) {
|
|
log_error("%s MULTALU18X18_MODE is not in {0, 1, 2}.\n", ctx->nameOf(ci));
|
|
}
|
|
NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
|
|
|
|
for (int i = 0; i < 54; ++i) {
|
|
if (i < 18) {
|
|
if (multalu18x18_mode != 2) {
|
|
ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d1", i));
|
|
ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d1", i));
|
|
} else {
|
|
ci->renamePort(ctx->idf("A[%d]", i), ctx->idf("A%d0", i));
|
|
ci->renamePort(ctx->idf("B[%d]", i), ctx->idf("B%d0", i));
|
|
}
|
|
}
|
|
}
|
|
|
|
gwu.remove_brackets(ci);
|
|
for (int i = 0; i < 54; ++i) {
|
|
switch (multalu18x18_mode) {
|
|
case 0:
|
|
ci->disconnectPort(ctx->idf("D%d", i));
|
|
break;
|
|
case 1:
|
|
ci->disconnectPort(ctx->idf("C%d", i));
|
|
ci->disconnectPort(ctx->idf("D%d", i));
|
|
break;
|
|
case 2:
|
|
ci->disconnectPort(ctx->idf("C%d", i));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (multalu18x18_mode != 2) {
|
|
ci->renamePort(id_ASIGN, id_ASIGN1);
|
|
ci->renamePort(id_BSIGN, id_BSIGN1);
|
|
ci->addInput(id_ASIGN0);
|
|
ci->addInput(id_BSIGN0);
|
|
ci->connectPort(id_ASIGN0, vss_net);
|
|
ci->connectPort(id_BSIGN0, vss_net);
|
|
ci->disconnectPort(id_DSIGN);
|
|
} else { // BSIGN0 and DSIGN are the same wire
|
|
ci->renamePort(id_ASIGN, id_ASIGN0);
|
|
ci->addInput(id_ASIGN1);
|
|
ci->connectPort(id_ASIGN1, vss_net);
|
|
ci->renamePort(id_BSIGN, id_BSIGN0);
|
|
}
|
|
|
|
// ACCLOAD - It looks like these wires are always connected to each other.
|
|
pass_net_type(ci, id_ACCLOAD);
|
|
ci->cell_bel_pins.at(id_ACCLOAD).clear();
|
|
ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ACCLOAD0);
|
|
ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ACCLOAD1);
|
|
|
|
// add padd9s and mult9s as a children
|
|
ci->cluster = ci->name;
|
|
ci->constr_abs_z = false;
|
|
ci->constr_x = 0;
|
|
ci->constr_y = 0;
|
|
ci->constr_z = 0;
|
|
ci->constr_children.clear();
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
IdString padd_name = gwu.create_aux_name(ci->name, i * 2);
|
|
std::unique_ptr<CellInfo> padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(padd_cell));
|
|
CellInfo *padd_ci = new_cells.back().get();
|
|
|
|
padd_ci->cluster = ci->name;
|
|
padd_ci->constr_abs_z = false;
|
|
padd_ci->constr_x = 0;
|
|
padd_ci->constr_y = 0;
|
|
padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULTALU18X18_0_Z + i;
|
|
|
|
IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1);
|
|
std::unique_ptr<CellInfo> mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(mult_cell));
|
|
CellInfo *mult_ci = new_cells.back().get();
|
|
|
|
mult_ci->cluster = ci->name;
|
|
mult_ci->constr_abs_z = false;
|
|
mult_ci->constr_x = 0;
|
|
mult_ci->constr_y = 0;
|
|
mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::MULTALU18X18_0_Z + i;
|
|
}
|
|
|
|
// DSP head?
|
|
if (gwu.dsp_bus_src(ci, "CASI", 55) == nullptr) {
|
|
for (int i = 0; i < 55; ++i) {
|
|
ci->disconnectPort(ctx->idf("CASI%d", i));
|
|
}
|
|
dsp_heads.push_back(ci);
|
|
if (ctx->verbose) {
|
|
log_info(" found a DSP head: %s\n", ctx->nameOf(ci));
|
|
}
|
|
}
|
|
} break;
|
|
case ID_MULT27X36: {
|
|
// We assemble that primitive from two MultAlu27x18 connected via CASO->CASI.
|
|
IdString l_mult_name = gwu.create_aux_name(ci->name, 1);
|
|
std::unique_ptr<CellInfo> l_mult_cell = gwu.create_cell(l_mult_name, id_MULTALU27X18);
|
|
new_dsps.push_back(std::move(l_mult_cell));
|
|
CellInfo *l_mult = new_dsps.back().get();
|
|
|
|
IdString h_mult_name = gwu.create_aux_name(ci->name, 2);
|
|
std::unique_ptr<CellInfo> h_mult_cell = gwu.create_cell(h_mult_name, id_MULTALU27X18);
|
|
new_dsps.push_back(std::move(h_mult_cell));
|
|
CellInfo *h_mult = new_dsps.back().get();
|
|
|
|
// SIAs are disconnected
|
|
for (int i = 0; i < 27; ++i) {
|
|
l_mult->addInput(ctx->idf("SIA[%d]", i));
|
|
h_mult->addInput(ctx->idf("SIA[%d]", i));
|
|
l_mult->addOutput(ctx->idf("SOA[%d]", i));
|
|
h_mult->addOutput(ctx->idf("SOA[%d]", i));
|
|
}
|
|
|
|
// unused
|
|
l_mult->addInput(id_CSEL);
|
|
h_mult->addInput(id_CSEL);
|
|
l_mult->addInput(id_ASEL);
|
|
h_mult->addInput(id_ASEL);
|
|
l_mult->addInput(id_ACCSEL);
|
|
h_mult->addInput(id_ACCSEL);
|
|
|
|
for (int i = 0; i < 48; ++i) {
|
|
// C is disconnected
|
|
l_mult->addInput(ctx->idf("C[%d]", i));
|
|
h_mult->addInput(ctx->idf("C[%d]", i));
|
|
// low mult CASI is disconnected
|
|
l_mult->addInput(ctx->idf("CASI[%d]", i));
|
|
// high mult CASO is disconnected
|
|
h_mult->addOutput(ctx->idf("CASO[%d]", i));
|
|
// low CASO -> high CASI
|
|
l_mult->addOutput(ctx->idf("CASO[%d]", i));
|
|
h_mult->addInput(ctx->idf("CASI[%d]", i));
|
|
l_mult->connectPorts(ctx->idf("CASO[%d]", i), h_mult, ctx->idf("CASI[%d]", i));
|
|
}
|
|
|
|
// input A is shared
|
|
ci->movePortBusTo(id_A, 0, 1, l_mult, id_A, 0, 1, 27);
|
|
l_mult->copyPortBusTo(id_A, 0, 1, h_mult, id_A, 0, 1, 27);
|
|
|
|
// input D is shared
|
|
ci->movePortBusTo(id_D, 0, 1, l_mult, id_D, 0, 1, 26);
|
|
l_mult->copyPortBusTo(id_D, 0, 1, h_mult, id_D, 0, 1, 26);
|
|
|
|
// input PSEL is shared
|
|
ci->movePortTo(id_PSEL, l_mult, id_PSEL);
|
|
l_mult->copyPortBusTo(id_PSEL, 0, 1, h_mult, id_PSEL, 0, 1, 26);
|
|
|
|
// input PADDSUB is shared
|
|
ci->movePortTo(id_PADDSUB, l_mult, id_PADDSUB);
|
|
l_mult->copyPortTo(id_PADDSUB, h_mult, id_PADDSUB);
|
|
|
|
// input B is divided
|
|
ci->movePortBusTo(id_B, 0, 1, l_mult, id_B, 0, 1, 18);
|
|
ci->movePortBusTo(id_B, 18, 1, h_mult, id_B, 0, 1, 18);
|
|
|
|
// output DOUT is divided
|
|
ci->movePortBusTo(id_DOUT, 0, 1, l_mult, id_DOUT, 0, 1, 18);
|
|
ci->movePortBusTo(id_DOUT, 18, 1, h_mult, id_DOUT, 0, 1, 47);
|
|
|
|
// Control inputs are shared
|
|
ci->movePortBusTo(id_CLK, 0, 1, l_mult, id_CLK, 0, 1, 2);
|
|
l_mult->copyPortBusTo(id_CLK, 0, 1, h_mult, id_CLK, 0, 1, 2);
|
|
ci->movePortBusTo(id_CE, 0, 1, l_mult, id_CE, 0, 1, 2);
|
|
l_mult->copyPortBusTo(id_CE, 0, 1, h_mult, id_CE, 0, 1, 2);
|
|
ci->movePortBusTo(id_RESET, 0, 1, l_mult, id_RESET, 0, 1, 2);
|
|
l_mult->copyPortBusTo(id_RESET, 0, 1, h_mult, id_RESET, 0, 1, 2);
|
|
|
|
// copy params
|
|
for (auto param : ci->params) {
|
|
l_mult->setParam(param.first, param.second);
|
|
h_mult->setParam(param.first, param.second);
|
|
}
|
|
// mark as MULT27X36
|
|
// we will catch these attributes during packing and add the missing parameters
|
|
l_mult->setAttr(id_MULT27X36_MAIN, 1);
|
|
h_mult->setAttr(id_MULT27X36_AUX, 1);
|
|
|
|
// remove former Mult27x36
|
|
cells_to_remove.push_back(ci->name);
|
|
} break;
|
|
case ID_MULTALU27X18: {
|
|
gwu.remove_brackets(ci);
|
|
pass_net_type(ci, id_ACCSEL);
|
|
ci->cell_bel_pins[id_ACCSEL].clear();
|
|
ci->cell_bel_pins.at(id_ACCSEL).push_back(id_ACCSEL0);
|
|
ci->cell_bel_pins.at(id_ACCSEL).push_back(id_ACCSEL1);
|
|
|
|
// mark 2 mult12x12 as parts of the cluster to prevent
|
|
// other multipliers from being placed there
|
|
ci->cluster = ci->name;
|
|
ci->constr_abs_z = false;
|
|
ci->constr_x = 0;
|
|
ci->constr_y = 0;
|
|
ci->constr_z = 0;
|
|
ci->constr_children.clear();
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
IdString mult12x12_name = gwu.create_aux_name(ci->name, i * 2);
|
|
std::unique_ptr<CellInfo> mult12x12_cell = gwu.create_cell(mult12x12_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(mult12x12_cell));
|
|
CellInfo *mult12x12_ci = new_cells.back().get();
|
|
|
|
mult12x12_ci->cluster = ci->name;
|
|
mult12x12_ci->constr_abs_z = false;
|
|
mult12x12_ci->constr_x = 0;
|
|
mult12x12_ci->constr_y = 0;
|
|
mult12x12_ci->constr_z = BelZ::MULT12X12_0_Z - BelZ::MULTALU27X18_Z + i;
|
|
if (ctx->verbose) {
|
|
log_info(" mark %s multiplier as used by %s\n", mult12x12_name.c_str(ctx), ctx->nameOf(ci));
|
|
}
|
|
}
|
|
|
|
// DSP head? This primitive has the ability to form chains using both SO[AB] -> SI[AB] and
|
|
// CASO->CASI
|
|
bool cas_head = false;
|
|
if (gwu.dsp_bus_src(ci, "CASI", 48) == nullptr) {
|
|
for (int i = 0; i < 48; ++i) {
|
|
ci->disconnectPort(ctx->idf("CASI%d", i));
|
|
}
|
|
cas_head = true;
|
|
}
|
|
bool so_head = false;
|
|
if (gwu.dsp_bus_src(ci, "SIA", 27) == nullptr) {
|
|
for (int i = 0; i < 27; ++i) {
|
|
ci->disconnectPort(ctx->idf("SIA%d", i));
|
|
}
|
|
so_head = true;
|
|
}
|
|
if (cas_head && so_head) {
|
|
dsp_heads.push_back(ci);
|
|
if (ctx->verbose) {
|
|
log_info(" found a DSP head: %s\n", ctx->nameOf(ci));
|
|
}
|
|
}
|
|
} break;
|
|
case ID_MULTALU36X18: {
|
|
if (ci->params.count(id_MULTALU18X18_MODE) == 0) {
|
|
ci->setParam(id_MULTALU18X18_MODE, 0);
|
|
}
|
|
int multalu36x18_mode = ci->params.at(id_MULTALU36X18_MODE).as_int64();
|
|
if (multalu36x18_mode < 0 || multalu36x18_mode > 2) {
|
|
log_error("%s MULTALU36X18_MODE is not in {0, 1, 2}.\n", ctx->nameOf(ci));
|
|
}
|
|
NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
|
|
|
|
gwu.remove_brackets(ci);
|
|
for (int i = 0; i < 36; ++i) {
|
|
if (i < 18) {
|
|
ci->cell_bel_pins.at(ctx->idf("A[%d]", i)).clear();
|
|
ci->cell_bel_pins[ctx->idf("A%d", i)].push_back(ctx->idf("A%d0", i));
|
|
ci->cell_bel_pins.at(ctx->idf("A%d", i)).push_back(ctx->idf("A%d1", i));
|
|
}
|
|
}
|
|
for (int i = 0; i < 54; ++i) {
|
|
switch (multalu36x18_mode) {
|
|
case 1: /* fallthrough */
|
|
case 2:
|
|
ci->disconnectPort(ctx->idf("C%d", i));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// both A have sign bit
|
|
// only MSB part of B has sign bit
|
|
ci->cell_bel_pins.at(id_ASIGN).clear();
|
|
ci->cell_bel_pins.at(id_ASIGN).push_back(id_ASIGN0);
|
|
ci->cell_bel_pins.at(id_ASIGN).push_back(id_ASIGN1);
|
|
ci->renamePort(id_BSIGN, id_BSIGN1);
|
|
ci->addInput(id_BSIGN0);
|
|
ci->connectPort(id_BSIGN0, vss_net);
|
|
|
|
pass_net_type(ci, id_ACCLOAD);
|
|
if (multalu36x18_mode == 1) {
|
|
if (ci->attrs.at(id_NET_ACCLOAD).as_string() == "GND" ||
|
|
ci->attrs.at(id_NET_ACCLOAD).as_string() == "VCC") {
|
|
ci->disconnectPort(id_ACCLOAD);
|
|
} else {
|
|
ci->addInput(id_ALUSEL4);
|
|
ci->addInput(id_ALUSEL6);
|
|
ci->cell_bel_pins.at(id_ACCLOAD).clear();
|
|
ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ALUSEL4);
|
|
ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ALUSEL6);
|
|
}
|
|
} else {
|
|
ci->disconnectPort(id_ACCLOAD);
|
|
}
|
|
|
|
// add padd9s and mult9s as a children
|
|
ci->cluster = ci->name;
|
|
ci->constr_abs_z = false;
|
|
ci->constr_x = 0;
|
|
ci->constr_y = 0;
|
|
ci->constr_z = 0;
|
|
ci->constr_children.clear();
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
IdString padd_name = gwu.create_aux_name(ci->name, i * 2);
|
|
std::unique_ptr<CellInfo> padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(padd_cell));
|
|
CellInfo *padd_ci = new_cells.back().get();
|
|
|
|
padd_ci->cluster = ci->name;
|
|
padd_ci->constr_abs_z = false;
|
|
padd_ci->constr_x = 0;
|
|
padd_ci->constr_y = 0;
|
|
padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULTALU36X18_0_Z + i;
|
|
|
|
IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1);
|
|
std::unique_ptr<CellInfo> mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(mult_cell));
|
|
CellInfo *mult_ci = new_cells.back().get();
|
|
|
|
mult_ci->cluster = ci->name;
|
|
mult_ci->constr_abs_z = false;
|
|
mult_ci->constr_x = 0;
|
|
mult_ci->constr_y = 0;
|
|
mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::MULTALU36X18_0_Z + i;
|
|
}
|
|
|
|
// DSP head?
|
|
if (gwu.dsp_bus_src(ci, "CASI", 55) == nullptr) {
|
|
for (int i = 0; i < 55; ++i) {
|
|
ci->disconnectPort(ctx->idf("CASI%d", i));
|
|
}
|
|
dsp_heads.push_back(ci);
|
|
if (ctx->verbose) {
|
|
log_info(" found a DSP head: %s\n", ctx->nameOf(ci));
|
|
}
|
|
}
|
|
} break;
|
|
case ID_MULTADDALU12X12: {
|
|
gwu.remove_brackets(ci);
|
|
pass_net_type(ci, id_ACCSEL);
|
|
|
|
ci->cell_bel_pins.at(id_ACCSEL).clear();
|
|
ci->cell_bel_pins.at(id_ACCSEL).push_back(id_ACCSEL0);
|
|
ci->cell_bel_pins.at(id_ACCSEL).push_back(id_ACCSEL1);
|
|
|
|
// mark 2 mult12x12 as parts of the cluster to prevent
|
|
// other multipliers from being placed there
|
|
ci->cluster = ci->name;
|
|
ci->constr_abs_z = false;
|
|
ci->constr_x = 0;
|
|
ci->constr_y = 0;
|
|
ci->constr_z = 0;
|
|
ci->constr_children.clear();
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
IdString mult12x12_name = gwu.create_aux_name(ci->name, i * 2);
|
|
std::unique_ptr<CellInfo> mult12x12_cell = gwu.create_cell(mult12x12_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(mult12x12_cell));
|
|
CellInfo *mult12x12_ci = new_cells.back().get();
|
|
|
|
mult12x12_ci->cluster = ci->name;
|
|
mult12x12_ci->constr_abs_z = false;
|
|
mult12x12_ci->constr_x = 0;
|
|
mult12x12_ci->constr_y = 0;
|
|
mult12x12_ci->constr_z = BelZ::MULT12X12_0_Z - BelZ::MULTADDALU12X12_Z + i;
|
|
}
|
|
|
|
// DSP head?
|
|
if (gwu.dsp_bus_src(ci, "CASI", 48) == nullptr) {
|
|
for (int i = 0; i < 48; ++i) {
|
|
ci->disconnectPort(ctx->idf("CASI%d", i));
|
|
}
|
|
dsp_heads.push_back(ci);
|
|
if (ctx->verbose) {
|
|
log_info(" found a DSP head: %s\n", ctx->nameOf(ci));
|
|
}
|
|
}
|
|
|
|
} break;
|
|
case ID_MULTADDALU18X18: {
|
|
if (ci->params.count(id_MULTADDALU18X18_MODE) == 0) {
|
|
ci->setParam(id_MULTADDALU18X18_MODE, 0);
|
|
}
|
|
int multaddalu18x18_mode = ci->params.at(id_MULTADDALU18X18_MODE).as_int64();
|
|
if (multaddalu18x18_mode < 0 || multaddalu18x18_mode > 2) {
|
|
log_error("%s MULTADDALU18X18_MODE is not in {0, 1, 2}.\n", ctx->nameOf(ci));
|
|
}
|
|
for (int i = 0; i < 54; ++i) {
|
|
if (i < 18) {
|
|
ci->renamePort(ctx->idf("A0[%d]", i), ctx->idf("A%d0", i));
|
|
ci->renamePort(ctx->idf("B0[%d]", i), ctx->idf("B%d0", i));
|
|
ci->renamePort(ctx->idf("A1[%d]", i), ctx->idf("A%d1", i));
|
|
ci->renamePort(ctx->idf("B1[%d]", i), ctx->idf("B%d1", i));
|
|
}
|
|
if (multaddalu18x18_mode == 0) {
|
|
ci->renamePort(ctx->idf("C[%d]", i), ctx->idf("C%d", i));
|
|
} else {
|
|
ci->disconnectPort(ctx->idf("C[%d]", i));
|
|
}
|
|
}
|
|
gwu.remove_brackets(ci);
|
|
|
|
pass_net_type(ci, id_ASEL0);
|
|
pass_net_type(ci, id_ASEL1);
|
|
pass_net_type(ci, id_BSEL0);
|
|
pass_net_type(ci, id_BSEL1);
|
|
pass_net_type(ci, id_ACCLOAD);
|
|
if (multaddalu18x18_mode == 1) {
|
|
if (ci->attrs.at(id_NET_ACCLOAD).as_string() == "GND" ||
|
|
ci->attrs.at(id_NET_ACCLOAD).as_string() == "VCC") {
|
|
ci->disconnectPort(id_ACCLOAD);
|
|
} else {
|
|
ci->addInput(id_ALUSEL4);
|
|
ci->addInput(id_ALUSEL6);
|
|
ci->cell_bel_pins.at(id_ACCLOAD).clear();
|
|
ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ALUSEL4);
|
|
ci->cell_bel_pins.at(id_ACCLOAD).push_back(id_ALUSEL6);
|
|
}
|
|
} else {
|
|
ci->disconnectPort(id_ACCLOAD);
|
|
}
|
|
|
|
// add padd9s and mult9s as a children
|
|
ci->cluster = ci->name;
|
|
ci->constr_abs_z = false;
|
|
ci->constr_x = 0;
|
|
ci->constr_y = 0;
|
|
ci->constr_z = 0;
|
|
ci->constr_children.clear();
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
IdString padd_name = gwu.create_aux_name(ci->name, i * 2);
|
|
std::unique_ptr<CellInfo> padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(padd_cell));
|
|
CellInfo *padd_ci = new_cells.back().get();
|
|
|
|
padd_ci->cluster = ci->name;
|
|
padd_ci->constr_abs_z = false;
|
|
padd_ci->constr_x = 0;
|
|
padd_ci->constr_y = 0;
|
|
padd_ci->constr_z = BelZ::PADD9_0_0_Z - BelZ::MULTADDALU18X18_0_Z + i;
|
|
|
|
IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1);
|
|
std::unique_ptr<CellInfo> mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(mult_cell));
|
|
CellInfo *mult_ci = new_cells.back().get();
|
|
|
|
mult_ci->cluster = ci->name;
|
|
mult_ci->constr_abs_z = false;
|
|
mult_ci->constr_x = 0;
|
|
mult_ci->constr_y = 0;
|
|
mult_ci->constr_z = BelZ::MULT9X9_0_0_Z - BelZ::MULTADDALU18X18_0_Z + i;
|
|
}
|
|
|
|
// DSP head? This primitive has the ability to form chains using both SO[AB] -> SI[AB] and
|
|
// CASO->CASI
|
|
bool cas_head = false;
|
|
if (gwu.dsp_bus_src(ci, "CASI", 55) == nullptr) {
|
|
for (int i = 0; i < 55; ++i) {
|
|
ci->disconnectPort(ctx->idf("CASI%d", i));
|
|
}
|
|
cas_head = true;
|
|
}
|
|
bool so_head = false;
|
|
if (gwu.dsp_bus_src(ci, "SIA", 18) == nullptr && gwu.dsp_bus_src(ci, "SIB", 18) == nullptr) {
|
|
for (int i = 0; i < 18; ++i) {
|
|
ci->disconnectPort(ctx->idf("SIA%d", i));
|
|
ci->disconnectPort(ctx->idf("SIB%d", i));
|
|
}
|
|
so_head = true;
|
|
}
|
|
if (cas_head && so_head) {
|
|
dsp_heads.push_back(ci);
|
|
if (ctx->verbose) {
|
|
log_info(" found a DSP head: %s\n", ctx->nameOf(ci));
|
|
}
|
|
}
|
|
} break;
|
|
case ID_MULT36X36: {
|
|
for (int i = 0; i < 36; ++i) {
|
|
ci->cell_bel_pins.at(ctx->idf("A[%d]", i)).clear();
|
|
ci->cell_bel_pins.at(ctx->idf("A[%d]", i)).push_back(ctx->idf("A%d0", i));
|
|
ci->cell_bel_pins.at(ctx->idf("A[%d]", i)).push_back(ctx->idf("A%d1", i));
|
|
ci->cell_bel_pins.at(ctx->idf("B[%d]", i)).clear();
|
|
ci->cell_bel_pins.at(ctx->idf("B[%d]", i)).push_back(ctx->idf("B%d0", i));
|
|
ci->cell_bel_pins.at(ctx->idf("B[%d]", i)).push_back(ctx->idf("B%d1", i));
|
|
}
|
|
// only MSB sign bits
|
|
ci->cell_bel_pins.at(id_ASIGN).clear();
|
|
ci->cell_bel_pins.at(id_ASIGN).push_back(id_ASIGN0);
|
|
ci->cell_bel_pins.at(id_ASIGN).push_back(id_ASIGN1);
|
|
ci->cell_bel_pins.at(id_BSIGN).clear();
|
|
ci->cell_bel_pins.at(id_BSIGN).push_back(id_BSIGN0);
|
|
ci->cell_bel_pins.at(id_BSIGN).push_back(id_BSIGN1);
|
|
|
|
// LSB sign bits = 0
|
|
NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
|
|
ci->addInput(id_ZERO_SIGN);
|
|
ci->cell_bel_pins[id_ZERO_SIGN].push_back(id_ZERO_ASIGN0);
|
|
ci->cell_bel_pins.at(id_ZERO_SIGN).push_back(id_ZERO_BSIGN0);
|
|
ci->cell_bel_pins.at(id_ZERO_SIGN).push_back(id_ZERO_BSIGN1);
|
|
ci->cell_bel_pins.at(id_ZERO_SIGN).push_back(id_ZERO_ASIGN1);
|
|
ci->connectPort(id_ZERO_SIGN, vss_net);
|
|
|
|
for (int i = 0; i < 72; ++i) {
|
|
ci->renamePort(ctx->idf("DOUT[%d]", i), ctx->idf("DOUT%d", i));
|
|
}
|
|
|
|
// add padd9s and mult9s as a children
|
|
ci->cluster = ci->name;
|
|
ci->constr_abs_z = false;
|
|
ci->constr_x = 0;
|
|
ci->constr_y = 0;
|
|
ci->constr_z = 0;
|
|
ci->constr_children.clear();
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
IdString padd_name = gwu.create_aux_name(ci->name, i * 2);
|
|
std::unique_ptr<CellInfo> padd_cell = gwu.create_cell(padd_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(padd_cell));
|
|
CellInfo *padd_ci = new_cells.back().get();
|
|
|
|
static int padd_z[] = {BelZ::PADD9_0_0_Z, BelZ::PADD9_0_2_Z, BelZ::PADD9_1_0_Z, BelZ::PADD9_1_2_Z};
|
|
padd_ci->cluster = ci->name;
|
|
padd_ci->constr_abs_z = false;
|
|
padd_ci->constr_x = 0;
|
|
padd_ci->constr_y = 0;
|
|
padd_ci->constr_z = padd_z[i / 2] - BelZ::MULT36X36_Z + i % 2;
|
|
|
|
IdString mult_name = gwu.create_aux_name(ci->name, i * 2 + 1);
|
|
std::unique_ptr<CellInfo> mult_cell = gwu.create_cell(mult_name, id_DUMMY_CELL);
|
|
new_cells.push_back(std::move(mult_cell));
|
|
CellInfo *mult_ci = new_cells.back().get();
|
|
|
|
static int mult_z[] = {BelZ::MULT9X9_0_0_Z, BelZ::MULT9X9_0_2_Z, BelZ::MULT9X9_1_0_Z,
|
|
BelZ::MULT9X9_1_2_Z};
|
|
mult_ci->cluster = ci->name;
|
|
mult_ci->constr_abs_z = false;
|
|
mult_ci->constr_x = 0;
|
|
mult_ci->constr_y = 0;
|
|
mult_ci->constr_z = mult_z[i / 2] - BelZ::MULT36X36_Z + i % 2;
|
|
}
|
|
} break;
|
|
default:
|
|
log_error("Unsupported DSP type '%s'\n", ci->type.c_str(ctx));
|
|
}
|
|
};
|
|
|
|
for (auto &cell : ctx->cells) {
|
|
auto ci = cell.second.get();
|
|
if (is_dsp(ci)) {
|
|
process_dsp_cell(ci);
|
|
}
|
|
}
|
|
|
|
// Process new DSPs if any are generated
|
|
for (auto &cell : new_dsps) {
|
|
IdString name = cell->name;
|
|
ctx->cells[name] = std::move(cell);
|
|
auto ci = ctx->cells.at(name).get();
|
|
process_dsp_cell(ci);
|
|
}
|
|
|
|
for (auto cell : cells_to_remove) {
|
|
ctx->cells.erase(cell);
|
|
}
|
|
|
|
// add new cells
|
|
for (auto &cell : new_cells) {
|
|
if (cell->cluster != ClusterId()) {
|
|
IdString cluster_root = cell->cluster;
|
|
IdString cell_name = cell->name;
|
|
ctx->cells[cell_name] = std::move(cell);
|
|
ctx->cells.at(cluster_root).get()->constr_children.push_back(ctx->cells.at(cell_name).get());
|
|
} else {
|
|
ctx->cells[cell->name] = std::move(cell);
|
|
}
|
|
}
|
|
|
|
// make CASO->CASI only chain from DSPs
|
|
auto make_CAS_chain = [&](CellInfo *head, int wire_num) {
|
|
CellInfo *cur_dsp = head;
|
|
while (1) {
|
|
CellInfo *next_dsp = gwu.dsp_bus_dst(cur_dsp, "CASO", wire_num);
|
|
if (next_dsp == nullptr) {
|
|
// End of chain
|
|
for (int i = 0; i < wire_num; ++i) {
|
|
cur_dsp->disconnectPort(ctx->idf("CASO%d", i));
|
|
}
|
|
break;
|
|
}
|
|
for (int i = 0; i < wire_num; ++i) {
|
|
cur_dsp->disconnectPort(ctx->idf("CASO%d", i));
|
|
next_dsp->disconnectPort(ctx->idf("CASI%d", i));
|
|
}
|
|
cur_dsp->setAttr(id_USE_CASCADE_OUT, 1);
|
|
cur_dsp = next_dsp;
|
|
cur_dsp->setAttr(id_USE_CASCADE_IN, 1);
|
|
if (ctx->verbose) {
|
|
log_info(" add %s to the chain.\n", ctx->nameOf(cur_dsp));
|
|
}
|
|
if (head->cluster == ClusterId()) {
|
|
head->cluster = head->name;
|
|
}
|
|
cur_dsp->cluster = head->name;
|
|
head->constr_children.push_back(cur_dsp);
|
|
for (auto child : cur_dsp->constr_children) {
|
|
child->cluster = head->name;
|
|
head->constr_children.push_back(child);
|
|
}
|
|
cur_dsp->constr_children.clear();
|
|
}
|
|
};
|
|
|
|
// make combined CASO->CASI and SOA->SIA chain
|
|
auto make_SIA_CAS_chain = [&](CellInfo *head, int cas_wire_num, int sia_wire_num, bool use_sib = true) {
|
|
CellInfo *cur_dsp = head;
|
|
while (1) {
|
|
bool end_of_cas_chain = false;
|
|
int wire_num = cas_wire_num;
|
|
CellInfo *next_dsp_a = gwu.dsp_bus_dst(cur_dsp, "CASO", wire_num);
|
|
if (next_dsp_a == nullptr) {
|
|
// End of CASO chain
|
|
for (int i = 0; i < wire_num; ++i) {
|
|
cur_dsp->disconnectPort(ctx->idf("CASO%d", i));
|
|
}
|
|
end_of_cas_chain = true;
|
|
} else {
|
|
for (int i = 0; i < wire_num; ++i) {
|
|
cur_dsp->disconnectPort(ctx->idf("CASO%d", i));
|
|
next_dsp_a->disconnectPort(ctx->idf("CASI%d", i));
|
|
}
|
|
}
|
|
|
|
bool end_of_so_chain = false;
|
|
wire_num = sia_wire_num;
|
|
CellInfo *next_so_dsp_a = gwu.dsp_bus_dst(cur_dsp, "SOA", wire_num);
|
|
CellInfo *next_so_dsp_b = use_sib ? gwu.dsp_bus_dst(cur_dsp, "SOB", wire_num) : nullptr;
|
|
if (next_so_dsp_a != nullptr && next_so_dsp_b != nullptr && next_so_dsp_a != next_so_dsp_b) {
|
|
log_error("%s is the source for two different DSPs (%s and %s) in the chain.", ctx->nameOf(cur_dsp),
|
|
ctx->nameOf(next_so_dsp_a), ctx->nameOf(next_so_dsp_b));
|
|
}
|
|
if (next_so_dsp_a == nullptr && (!use_sib || (use_sib && next_so_dsp_b == nullptr))) {
|
|
// End of SO chain
|
|
for (int i = 0; i < wire_num; ++i) {
|
|
cur_dsp->disconnectPort(ctx->idf("SOA%d", i));
|
|
if (use_sib) {
|
|
cur_dsp->disconnectPort(ctx->idf("SOB%d", i));
|
|
}
|
|
}
|
|
end_of_so_chain = true;
|
|
} else {
|
|
next_so_dsp_a = next_so_dsp_a != nullptr ? next_so_dsp_a : next_so_dsp_b;
|
|
for (int i = 0; i < wire_num; ++i) {
|
|
cur_dsp->disconnectPort(ctx->idf("SOA%d", i));
|
|
next_so_dsp_a->disconnectPort(ctx->idf("SIA%d", i));
|
|
if (use_sib) {
|
|
cur_dsp->disconnectPort(ctx->idf("SOB%d", i));
|
|
next_so_dsp_a->disconnectPort(ctx->idf("SIB%d", i));
|
|
}
|
|
}
|
|
}
|
|
if (end_of_cas_chain && end_of_so_chain) {
|
|
break;
|
|
}
|
|
|
|
// to the next
|
|
if (!end_of_cas_chain) {
|
|
cur_dsp->setAttr(id_USE_CASCADE_OUT, 1);
|
|
}
|
|
cur_dsp = next_dsp_a != nullptr ? next_dsp_a : next_so_dsp_a;
|
|
if (!end_of_cas_chain) {
|
|
cur_dsp->setAttr(id_USE_CASCADE_IN, 1);
|
|
}
|
|
if (ctx->verbose) {
|
|
log_info(" add %s to the chain. End of the SO chain:%d, end of the CAS chain:%d\n",
|
|
ctx->nameOf(cur_dsp), end_of_so_chain, end_of_cas_chain);
|
|
}
|
|
if (head->cluster == ClusterId()) {
|
|
head->cluster = head->name;
|
|
}
|
|
cur_dsp->cluster = head->name;
|
|
head->constr_children.push_back(cur_dsp);
|
|
for (auto child : cur_dsp->constr_children) {
|
|
child->cluster = head->name;
|
|
head->constr_children.push_back(child);
|
|
}
|
|
cur_dsp->constr_children.clear();
|
|
}
|
|
};
|
|
|
|
// DSP chains
|
|
for (CellInfo *head : dsp_heads) {
|
|
if (ctx->verbose) {
|
|
log_info("Process a DSP head: %s\n", ctx->nameOf(head));
|
|
}
|
|
switch (head->type.hash()) {
|
|
case ID_PADD9: /* fallthrough */
|
|
case ID_PADD18: {
|
|
int wire_num = 9;
|
|
if (head->type == id_PADD18) {
|
|
wire_num = 18;
|
|
}
|
|
|
|
CellInfo *cur_dsp = head;
|
|
while (1) {
|
|
CellInfo *next_dsp_a = gwu.dsp_bus_dst(cur_dsp, "SO", wire_num);
|
|
CellInfo *next_dsp_b = gwu.dsp_bus_src(cur_dsp, "SBI", wire_num);
|
|
if (next_dsp_a != nullptr && next_dsp_b != nullptr && next_dsp_a != next_dsp_b) {
|
|
log_error("%s is the next for two different DSPs (%s and %s) in the chain.", ctx->nameOf(cur_dsp),
|
|
ctx->nameOf(next_dsp_a), ctx->nameOf(next_dsp_b));
|
|
}
|
|
if (next_dsp_a == nullptr && next_dsp_b == nullptr) {
|
|
// End of chain
|
|
cur_dsp->setAttr(id_LAST_IN_CHAIN, 1);
|
|
for (int i = 0; i < wire_num; ++i) {
|
|
cur_dsp->disconnectPort(ctx->idf("SO%d", i));
|
|
cur_dsp->disconnectPort(ctx->idf("SBI%d", i));
|
|
}
|
|
break;
|
|
}
|
|
|
|
next_dsp_a = next_dsp_a != nullptr ? next_dsp_a : next_dsp_b;
|
|
for (int i = 0; i < wire_num; ++i) {
|
|
cur_dsp->disconnectPort(ctx->idf("SO%d", i));
|
|
cur_dsp->disconnectPort(ctx->idf("SBI%d", i));
|
|
next_dsp_a->disconnectPort(ctx->idf("SI%d", i));
|
|
next_dsp_a->disconnectPort(ctx->idf("SBO%d", i));
|
|
}
|
|
cur_dsp = next_dsp_a;
|
|
if (ctx->verbose) {
|
|
log_info(" add %s to the chain.\n", ctx->nameOf(cur_dsp));
|
|
}
|
|
if (head->cluster == ClusterId()) {
|
|
head->cluster = head->name;
|
|
}
|
|
cur_dsp->cluster = head->name;
|
|
head->constr_children.push_back(cur_dsp);
|
|
for (auto child : cur_dsp->constr_children) {
|
|
child->cluster = head->name;
|
|
head->constr_children.push_back(child);
|
|
}
|
|
cur_dsp->constr_children.clear();
|
|
}
|
|
} break;
|
|
case ID_MULT9X9: /* fallthrough */
|
|
case ID_MULT18X18: {
|
|
int wire_num = 9;
|
|
if (head->type == id_MULT18X18) {
|
|
wire_num = 18;
|
|
}
|
|
|
|
CellInfo *cur_dsp = head;
|
|
while (1) {
|
|
CellInfo *next_dsp_a = gwu.dsp_bus_dst(cur_dsp, "SOA", wire_num);
|
|
CellInfo *next_dsp_b = gwu.dsp_bus_dst(cur_dsp, "SOB", wire_num);
|
|
if (next_dsp_a != nullptr && next_dsp_b != nullptr && next_dsp_a != next_dsp_b) {
|
|
log_error("%s is the source for two different DSPs (%s and %s) in the chain.", ctx->nameOf(cur_dsp),
|
|
ctx->nameOf(next_dsp_a), ctx->nameOf(next_dsp_b));
|
|
}
|
|
if (next_dsp_a == nullptr && next_dsp_b == nullptr) {
|
|
// End of chain
|
|
for (int i = 0; i < wire_num; ++i) {
|
|
cur_dsp->disconnectPort(ctx->idf("SOA%d", i));
|
|
cur_dsp->disconnectPort(ctx->idf("SOB%d", i));
|
|
}
|
|
break;
|
|
}
|
|
|
|
next_dsp_a = next_dsp_a != nullptr ? next_dsp_a : next_dsp_b;
|
|
for (int i = 0; i < wire_num; ++i) {
|
|
cur_dsp->disconnectPort(ctx->idf("SOA%d", i));
|
|
cur_dsp->disconnectPort(ctx->idf("SOB%d", i));
|
|
next_dsp_a->disconnectPort(ctx->idf("SIA%d", i));
|
|
next_dsp_a->disconnectPort(ctx->idf("SIB%d", i));
|
|
}
|
|
cur_dsp = next_dsp_a;
|
|
if (ctx->verbose) {
|
|
log_info(" add %s to the chain.\n", ctx->nameOf(cur_dsp));
|
|
}
|
|
if (head->cluster == ClusterId()) {
|
|
head->cluster = head->name;
|
|
}
|
|
cur_dsp->cluster = head->name;
|
|
head->constr_children.push_back(cur_dsp);
|
|
for (auto child : cur_dsp->constr_children) {
|
|
child->cluster = head->name;
|
|
head->constr_children.push_back(child);
|
|
}
|
|
cur_dsp->constr_children.clear();
|
|
}
|
|
} break;
|
|
case ID_MULTALU18X18: /* fallthrough */
|
|
case ID_MULTALU36X18: /* fallthrough */
|
|
case ID_ALU54D: {
|
|
make_CAS_chain(head, 55);
|
|
} break;
|
|
case ID_MULTADDALU12X12: {
|
|
make_CAS_chain(head, 48);
|
|
} break;
|
|
case ID_MULTALU27X18: {
|
|
// This primitive has the ability to form chains using both SO[AB] -> SI[AB] and CASO->CASI
|
|
make_SIA_CAS_chain(head, 48, 27, false);
|
|
} break;
|
|
case ID_MULTADDALU18X18: {
|
|
// This primitive has the ability to form chains using both SO[AB] -> SI[AB] and CASO->CASI
|
|
make_SIA_CAS_chain(head, 55, 18);
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
NEXTPNR_NAMESPACE_END
|