gowin: Add support for DSP primitives.
For the following primitives: - PADD9 - PADD18 - MULT9X9 - MULT18X18 - MULT36X36 - MULTALU18X18 - MULTALU36X18 - MULTADDALU18X18 - ALU54D packing and processing of fixed wires between macro and between DSP blocks is implemented. Clusters of DSP and macro blocks are processed using custom placement of cluster elements. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
ff96fc5af1
commit
210e0fa33b
@ -917,6 +917,48 @@ X(WREB)
|
||||
X(CLKA)
|
||||
X(CLKB)
|
||||
|
||||
// DSP
|
||||
X(ALU54D)
|
||||
X(MULTADDALU18X18)
|
||||
X(MULTALU18X18)
|
||||
X(MULTALU36X18)
|
||||
X(MULT36X36)
|
||||
X(MULT18X18)
|
||||
X(MULT9X9)
|
||||
X(PADD18)
|
||||
X(PADD9)
|
||||
X(ASIGN)
|
||||
X(BSIGN)
|
||||
X(ASIGN0)
|
||||
X(BSIGN0)
|
||||
X(ASIGN1)
|
||||
X(BSIGN1)
|
||||
X(ASEL)
|
||||
X(ASEL0)
|
||||
X(ASEL1)
|
||||
X(BSEL)
|
||||
X(BSEL0)
|
||||
X(BSEL1)
|
||||
X(SOA_REG)
|
||||
X(DSIGN)
|
||||
X(ACCLOAD)
|
||||
X(ACCLOAD0)
|
||||
X(ACCLOAD1)
|
||||
X(NET_ACCLOAD)
|
||||
X(ALUSEL0)
|
||||
X(ALUSEL1)
|
||||
X(ALUSEL2)
|
||||
X(ALUSEL3)
|
||||
X(ALUSEL4)
|
||||
X(ALUSEL5)
|
||||
X(ALUSEL6)
|
||||
X(USE_CASCADE_OUT)
|
||||
X(USE_CASCADE_IN)
|
||||
X(LAST_IN_CHAIN)
|
||||
X(MULTALU18X18_MODE)
|
||||
X(MULTADDALU18X18_MODE)
|
||||
X(MULTALU36X18_MODE)
|
||||
|
||||
// IOB types
|
||||
X(IBUF)
|
||||
X(OBUF)
|
||||
|
@ -215,6 +215,7 @@ struct GowinGlobalRouter
|
||||
}
|
||||
ctx->bindWire(src, net, STRENGTH_LOCKED);
|
||||
|
||||
RouteResult routed = NOT_ROUTED;
|
||||
for (auto usr : net->users.enumerate()) {
|
||||
WireId dst = ctx->getNetinfoSinkWire(net, net->users.at(usr.index), 0);
|
||||
if (dst == WireId()) {
|
||||
@ -222,8 +223,16 @@ struct GowinGlobalRouter
|
||||
ctx->nameOf(net->users.at(usr.index).cell), ctx->nameOf(net->users.at(usr.index).port));
|
||||
}
|
||||
// log_info(" usr wire: %s\n", ctx->nameOfWire(dst));
|
||||
backwards_bfs_route(net, src, dst, 1000000, true,
|
||||
[&](PipId pip) { return (is_relaxed_sink(usr.value) || global_pip_filter(pip)); });
|
||||
if (backwards_bfs_route(net, src, dst, 1000000, false, [&](PipId pip) {
|
||||
return (is_relaxed_sink(usr.value) || global_pip_filter(pip));
|
||||
})) {
|
||||
routed = routed == ROUTED_PARTIALLY ? routed : ROUTED_ALL;
|
||||
} else {
|
||||
routed = routed == NOT_ROUTED ? routed : ROUTED_PARTIALLY;
|
||||
}
|
||||
}
|
||||
if (routed == NOT_ROUTED) {
|
||||
ctx->unbindWire(src);
|
||||
}
|
||||
|
||||
// b) route net before buf from whatever to the buf input
|
||||
|
@ -32,6 +32,7 @@ struct GowinImpl : HimbaechelAPI
|
||||
void postRoute() override;
|
||||
|
||||
bool isBelLocationValid(BelId bel, bool explain_invalid) const override;
|
||||
void notifyBelChange(BelId bel, CellInfo *cell) override;
|
||||
|
||||
// Bel bucket functions
|
||||
IdString getBelBucketForCellType(IdString cell_type) const override;
|
||||
@ -41,6 +42,11 @@ struct GowinImpl : HimbaechelAPI
|
||||
// wires
|
||||
bool checkPipAvail(PipId pip) const override;
|
||||
|
||||
// Cluster
|
||||
bool isClusterStrict(const CellInfo *cell) const { return true; }
|
||||
bool getClusterPlacement(ClusterId cluster, BelId root_bel,
|
||||
std::vector<std::pair<CellInfo *, BelId>> &placement) const;
|
||||
|
||||
private:
|
||||
HimbaechelHelpers h;
|
||||
GowinUtils gwu;
|
||||
@ -53,15 +59,37 @@ struct GowinImpl : HimbaechelAPI
|
||||
// Validity checking
|
||||
struct GowinCellInfo
|
||||
{
|
||||
// slice info
|
||||
const NetInfo *lut_f = nullptr;
|
||||
const NetInfo *ff_d = nullptr, *ff_ce = nullptr, *ff_clk = nullptr, *ff_lsr = nullptr;
|
||||
const NetInfo *alu_sum = nullptr;
|
||||
// dsp info
|
||||
const NetInfo *dsp_asign = nullptr, *dsp_bsign = nullptr, *dsp_asel = nullptr, *dsp_bsel = nullptr,
|
||||
*dsp_ce = nullptr, *dsp_clk = nullptr, *dsp_reset = nullptr;
|
||||
bool dsp_soa_reg;
|
||||
};
|
||||
std::vector<GowinCellInfo> fast_cell_info;
|
||||
void assign_cell_info();
|
||||
|
||||
// dsp control nets
|
||||
// Each DSP and each macro has a small set of control wires that are
|
||||
// allocated to internal primitives as needed. It is assumed that most
|
||||
// primitives use the same signals for CE, CLK and especially RESET, so
|
||||
// these wires are few and need to be controlled.
|
||||
struct dsp_net_counters
|
||||
{
|
||||
dict<IdString, int> ce;
|
||||
dict<IdString, int> clk;
|
||||
dict<IdString, int> reset;
|
||||
};
|
||||
dict<BelId, dsp_net_counters> dsp_net_cnt;
|
||||
dict<BelId, CellInfo *> dsp_bel2cell; // Remember the connection with cells
|
||||
// since this information is already lost during unbinding
|
||||
void adjust_dsp_pin_mapping(void);
|
||||
|
||||
// bel placement validation
|
||||
bool slice_valid(int x, int y, int z) const;
|
||||
bool dsp_valid(Loc l, IdString bel_type, bool explain_invalid) const;
|
||||
};
|
||||
|
||||
struct GowinArch : HimbaechelArch
|
||||
@ -175,6 +203,59 @@ void GowinImpl::pack()
|
||||
gowin_pack(ctx);
|
||||
}
|
||||
|
||||
// One DSP macro, in a rough approximation, consists of 5 large operating
|
||||
// blocks (pre-adders, multipliers and alu), at almost every input (blocks
|
||||
// usually have two of them) you can turn on registers, in addition, there are
|
||||
// registers on a dedicated operand shift line between DSP and registers at
|
||||
// the outputs. As we see, the number of registers is large, but the DSP has
|
||||
// only four inputs for each of the CE, CLK and RESET signals, and here we tell
|
||||
// gowin_pack which version of each signal is used by which block.
|
||||
// We also indicate to the router which Bel's pin to use.
|
||||
void GowinImpl::adjust_dsp_pin_mapping(void)
|
||||
{
|
||||
for (auto b2c : dsp_bel2cell) {
|
||||
BelId bel = b2c.first;
|
||||
Loc loc = ctx->getBelLocation(bel);
|
||||
CellInfo *ci = b2c.second;
|
||||
const auto dsp_data = fast_cell_info.at(ci->flat_index);
|
||||
|
||||
auto set_cell_bel_pin = [&](dict<IdString, int> nets, IdString pin, IdString net_name, const char *fmt,
|
||||
const char *fmt_double = nullptr) {
|
||||
int i = 0;
|
||||
for (auto net_cnt : nets) {
|
||||
if (net_cnt.first == net_name) {
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
ci->cell_bel_pins.at(pin).clear();
|
||||
if (fmt_double == nullptr) {
|
||||
ci->cell_bel_pins.at(pin).push_back(ctx->idf(fmt, i));
|
||||
} else {
|
||||
ci->cell_bel_pins.at(pin).push_back(ctx->idf(fmt_double, i, 0));
|
||||
ci->cell_bel_pins.at(pin).push_back(ctx->idf(fmt_double, i, 1));
|
||||
}
|
||||
ci->setAttr(pin, i);
|
||||
};
|
||||
|
||||
if (dsp_data.dsp_reset != nullptr) {
|
||||
BelId dsp = ctx->getBelByLocation(Loc(loc.x, loc.y, BelZ::DSP_Z));
|
||||
set_cell_bel_pin(dsp_net_cnt.at(dsp).reset, id_RESET, dsp_data.dsp_reset->name, "RESET%d",
|
||||
ci->type == id_MULT36X36 ? "RESET%d%d" : nullptr);
|
||||
}
|
||||
if (dsp_data.dsp_ce != nullptr) {
|
||||
BelId dsp = ctx->getBelByLocation(Loc(loc.x, loc.y, gwu.get_dsp_macro(loc.z)));
|
||||
set_cell_bel_pin(dsp_net_cnt.at(dsp).ce, id_CE, dsp_data.dsp_ce->name, "CE%d",
|
||||
ci->type == id_MULT36X36 ? "CE%d%d" : nullptr);
|
||||
}
|
||||
if (dsp_data.dsp_clk != nullptr) {
|
||||
BelId dsp = ctx->getBelByLocation(Loc(loc.x, loc.y, gwu.get_dsp_macro(loc.z)));
|
||||
set_cell_bel_pin(dsp_net_cnt.at(dsp).clk, id_CLK, dsp_data.dsp_clk->name, "CLK%d",
|
||||
ci->type == id_MULT36X36 ? "CLK%d%d" : nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GowinImpl::prePlace() { assign_cell_info(); }
|
||||
void GowinImpl::postPlace()
|
||||
{
|
||||
@ -190,6 +271,9 @@ void GowinImpl::postPlace()
|
||||
}
|
||||
log_break();
|
||||
}
|
||||
|
||||
// adjust cell pin to bel pin mapping for DSP cells (CE, CLK and RESET pins)
|
||||
adjust_dsp_pin_mapping();
|
||||
}
|
||||
|
||||
void GowinImpl::preRoute() { gowin_route_globals(ctx); }
|
||||
@ -238,10 +322,10 @@ void GowinImpl::postRoute()
|
||||
bool GowinImpl::isBelLocationValid(BelId bel, bool explain_invalid) const
|
||||
{
|
||||
Loc l = ctx->getBelLocation(bel);
|
||||
IdString bel_type = ctx->getBelType(bel);
|
||||
if (!ctx->getBoundBelCell(bel)) {
|
||||
return true;
|
||||
}
|
||||
IdString bel_type = ctx->getBelType(bel);
|
||||
switch (bel_type.hash()) {
|
||||
case ID_LUT4: /* fall-through */
|
||||
case ID_DFF:
|
||||
@ -251,6 +335,16 @@ bool GowinImpl::isBelLocationValid(BelId bel, bool explain_invalid) const
|
||||
case ID_RAM16SDP4:
|
||||
// only slices 4 and 5 are critical for RAM
|
||||
return slice_valid(l.x, l.y, l.z - BelZ::RAMW_Z + 5) && slice_valid(l.x, l.y, l.z - BelZ::RAMW_Z + 4);
|
||||
case ID_PADD9: /* fall-through */
|
||||
case ID_PADD18: /* fall-through */
|
||||
case ID_MULT9X9: /* fall-through */
|
||||
case ID_MULT18X18: /* fall-through */
|
||||
case ID_MULTADDALU18X18: /* fall-through */
|
||||
case ID_MULTALU18X18: /* fall-through */
|
||||
case ID_MULTALU36X18: /* fall-through */
|
||||
case ID_MULT36X36: /* fall-through */
|
||||
case ID_ALU54D:
|
||||
return dsp_valid(l, bel_type, explain_invalid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -290,6 +384,10 @@ IdString GowinImpl::getBelBucketForCellType(IdString cell_type) const
|
||||
|
||||
bool GowinImpl::isValidBelForCellType(IdString cell_type, BelId bel) const
|
||||
{
|
||||
if (cell_type == id_DUMMY_CELL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
IdString bel_type = ctx->getBelType(bel);
|
||||
if (bel_type == id_IOB) {
|
||||
return cell_type.in(id_IBUF, id_OBUF);
|
||||
@ -347,6 +445,23 @@ void GowinImpl::assign_cell_info()
|
||||
fc.alu_sum = ci->getPort(id_SUM);
|
||||
continue;
|
||||
}
|
||||
auto get_net = [&](IdString port_id) {
|
||||
NetInfo *ni = ci->getPort(port_id);
|
||||
if (ni != nullptr && ni->driver.cell == nullptr) {
|
||||
ni = nullptr;
|
||||
}
|
||||
return ni;
|
||||
};
|
||||
if (is_dsp(ci)) {
|
||||
fc.dsp_reset = get_net(id_RESET);
|
||||
fc.dsp_clk = get_net(id_CLK);
|
||||
fc.dsp_ce = get_net(id_CE);
|
||||
fc.dsp_asign = get_net(id_ASIGN);
|
||||
fc.dsp_bsign = get_net(id_BSIGN);
|
||||
fc.dsp_asel = get_net(id_ASEL);
|
||||
fc.dsp_bsel = get_net(id_BSEL);
|
||||
fc.dsp_soa_reg = ci->params.count(id_SOA_REG) && ci->params.at(id_SOA_REG).as_int64() == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -367,6 +482,59 @@ inline bool incompatible_ffs(const CellInfo *ff, const CellInfo *adj_ff)
|
||||
}
|
||||
|
||||
// placement validation
|
||||
bool GowinImpl::dsp_valid(Loc l, IdString bel_type, bool explain_invalid) const
|
||||
{
|
||||
const CellInfo *dsp = ctx->getBoundBelCell(ctx->getBelByLocation(l));
|
||||
const auto &dsp_data = fast_cell_info.at(dsp->flat_index);
|
||||
// check for shift out register - there is only one for macro
|
||||
if (dsp_data.dsp_soa_reg) {
|
||||
if (l.z == BelZ::MULT18X18_0_1_Z || l.z == BelZ::MULT18X18_1_1_Z || l.z == BelZ::MULT9X9_0_0_Z ||
|
||||
l.z == BelZ::MULT9X9_0_1_Z || l.z == BelZ::MULT9X9_1_0_Z || l.z == BelZ::MULT9X9_1_1_Z) {
|
||||
if (explain_invalid) {
|
||||
log_nonfatal_error(
|
||||
"It is not possible to place the DSP so that the SOA register is on the macro boundary.\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bel_type.in(id_MULT9X9, id_PADD9)) {
|
||||
int pair_z = gwu.get_dsp_paired_9(l.z);
|
||||
const CellInfo *adj_dsp9 = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(l.x, l.y, pair_z)));
|
||||
if (adj_dsp9 != nullptr) {
|
||||
const auto &adj_dsp9_data = fast_cell_info.at(adj_dsp9->flat_index);
|
||||
if ((dsp_data.dsp_asign != adj_dsp9_data.dsp_asign) || (dsp_data.dsp_bsign != adj_dsp9_data.dsp_bsign) ||
|
||||
(dsp_data.dsp_asel != adj_dsp9_data.dsp_asel) || (dsp_data.dsp_bsel != adj_dsp9_data.dsp_bsel) ||
|
||||
(dsp_data.dsp_reset != adj_dsp9_data.dsp_reset) || (dsp_data.dsp_ce != adj_dsp9_data.dsp_ce) ||
|
||||
(dsp_data.dsp_clk != adj_dsp9_data.dsp_clk)) {
|
||||
if (explain_invalid) {
|
||||
log_nonfatal_error("For 9bit primitives the control signals must be same.\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// check for control nets "overflow"
|
||||
BelId dsp_bel = ctx->getBelByLocation(Loc(l.x, l.y, BelZ::DSP_Z));
|
||||
if (dsp_net_cnt.at(dsp_bel).reset.size() > 4) {
|
||||
if (explain_invalid) {
|
||||
log_nonfatal_error("More than 4 different networks for RESET signals in one DSP are not allowed.\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
BelId dsp_macro_bel = ctx->getBelByLocation(Loc(l.x, l.y, gwu.get_dsp_macro(l.z)));
|
||||
if (dsp_net_cnt.count(dsp_macro_bel)) {
|
||||
if (dsp_net_cnt.at(dsp_macro_bel).ce.size() > 4 || dsp_net_cnt.at(dsp_macro_bel).clk.size() > 4) {
|
||||
if (explain_invalid) {
|
||||
log_nonfatal_error(
|
||||
"More than 4 different networks for CE or CLK signals in one DSP macro are not allowed.\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GowinImpl::slice_valid(int x, int y, int z) const
|
||||
{
|
||||
const CellInfo *lut = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2)));
|
||||
@ -433,6 +601,99 @@ bool GowinImpl::slice_valid(int x, int y, int z) const
|
||||
return true;
|
||||
}
|
||||
|
||||
// Cluster
|
||||
bool GowinImpl::getClusterPlacement(ClusterId cluster, BelId root_bel,
|
||||
std::vector<std::pair<CellInfo *, BelId>> &placement) const
|
||||
{
|
||||
CellInfo *root_ci = getClusterRootCell(cluster);
|
||||
if (!root_ci->type.in(id_PADD9, id_MULT9X9, id_PADD18, id_MULT18X18, id_MULTALU18X18, id_MULTALU36X18,
|
||||
id_MULTADDALU18X18, id_ALU54D)) {
|
||||
return HimbaechelAPI::getClusterPlacement(cluster, root_bel, placement);
|
||||
}
|
||||
|
||||
NPNR_ASSERT(root_bel != BelId());
|
||||
if (!isValidBelForCellType(root_ci->type, root_bel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IdString bel_type = ctx->getBelType(root_bel);
|
||||
// non-chain DSP
|
||||
if (root_ci->constr_children.size() == 1 && bel_type.in(id_PADD9, id_MULT9X9)) {
|
||||
return HimbaechelAPI::getClusterPlacement(cluster, root_bel, placement);
|
||||
}
|
||||
|
||||
placement.clear();
|
||||
Loc root_loc = ctx->getBelLocation(root_bel);
|
||||
placement.emplace_back(root_ci, root_bel);
|
||||
|
||||
Loc mult_loc = root_loc;
|
||||
for (auto child : root_ci->constr_children) {
|
||||
Loc child_loc;
|
||||
child_loc.y = root_loc.y;
|
||||
if (child->type == id_DUMMY_CELL) {
|
||||
child_loc.x = mult_loc.x + child->constr_x;
|
||||
child_loc.z = mult_loc.z + child->constr_z;
|
||||
} else {
|
||||
child_loc = gwu.get_dsp_next_in_chain(mult_loc, child->type);
|
||||
mult_loc = child_loc;
|
||||
}
|
||||
|
||||
BelId child_bel = ctx->getBelByLocation(child_loc);
|
||||
if (child_bel == BelId() || !isValidBelForCellType(child->type, child_bel))
|
||||
return false;
|
||||
placement.emplace_back(child, child_bel);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GowinImpl::notifyBelChange(BelId bel, CellInfo *cell)
|
||||
{
|
||||
if (cell != nullptr && !is_dsp(cell)) {
|
||||
return;
|
||||
}
|
||||
if (cell == nullptr && dsp_bel2cell.count(bel) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// trace DSP control networks
|
||||
IdString cell_type = id_DUMMY_CELL;
|
||||
if (cell != nullptr) {
|
||||
cell_type = cell->type;
|
||||
}
|
||||
Loc loc = ctx->getBelLocation(bel);
|
||||
Loc l = loc;
|
||||
l.z = gwu.get_dsp(loc.z);
|
||||
BelId dsp = ctx->getBelByLocation(l);
|
||||
l.z = gwu.get_dsp_macro(loc.z);
|
||||
BelId dsp_macro = ctx->getBelByLocation(l);
|
||||
|
||||
if (cell) {
|
||||
const auto &dsp_cell_data = fast_cell_info.at(cell->flat_index);
|
||||
if (dsp_cell_data.dsp_reset != nullptr) {
|
||||
dsp_net_cnt[dsp].reset[dsp_cell_data.dsp_reset->name]++;
|
||||
}
|
||||
if (dsp_cell_data.dsp_ce != nullptr) {
|
||||
dsp_net_cnt[dsp_macro].ce[dsp_cell_data.dsp_ce->name]++;
|
||||
}
|
||||
if (dsp_cell_data.dsp_clk != nullptr) {
|
||||
dsp_net_cnt[dsp_macro].clk[dsp_cell_data.dsp_clk->name]++;
|
||||
}
|
||||
dsp_bel2cell[bel] = cell;
|
||||
} else {
|
||||
const auto &dsp_cell_data = fast_cell_info.at(dsp_bel2cell.at(bel)->flat_index);
|
||||
if (dsp_cell_data.dsp_reset != nullptr) {
|
||||
dsp_net_cnt.at(dsp).reset.at(dsp_cell_data.dsp_reset->name)--;
|
||||
}
|
||||
if (dsp_cell_data.dsp_ce != nullptr) {
|
||||
dsp_net_cnt.at(dsp_macro).ce.at(dsp_cell_data.dsp_ce->name)--;
|
||||
}
|
||||
if (dsp_cell_data.dsp_clk != nullptr) {
|
||||
dsp_net_cnt.at(dsp_macro).clk.at(dsp_cell_data.dsp_clk->name)--;
|
||||
}
|
||||
dsp_bel2cell.erase(bel);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -54,6 +54,14 @@ inline bool type_is_bsram(IdString cell_type)
|
||||
}
|
||||
inline bool is_bsram(const CellInfo *cell) { return type_is_bsram(cell->type); }
|
||||
|
||||
// Return true if a cell is a DSP
|
||||
inline bool type_is_dsp(IdString cell_type)
|
||||
{
|
||||
return cell_type.in(id_PADD9, id_PADD18, id_MULT9X9, id_MULT18X18, id_MULT36X36, id_ALU54D, id_MULTALU18X18,
|
||||
id_MULTALU36X18, id_MULTADDALU18X18);
|
||||
}
|
||||
inline bool is_dsp(const CellInfo *cell) { return type_is_dsp(cell->type); }
|
||||
|
||||
// ==========================================
|
||||
// extra data in the chip db
|
||||
// ==========================================
|
||||
@ -113,7 +121,54 @@ enum
|
||||
PLL_Z = 275,
|
||||
GSR_Z = 276,
|
||||
VCC_Z = 277,
|
||||
VSS_Z = 278
|
||||
VSS_Z = 278,
|
||||
|
||||
// The two least significant bits encode Z for 9-bit adders and
|
||||
// multipliers, if they are equal to 0, then we get Z of their common
|
||||
// 18-bit equivalent.
|
||||
DSP_Z = 509, // DSP
|
||||
|
||||
DSP_0_Z = 511, // DSP macro 0
|
||||
PADD18_0_0_Z = 512,
|
||||
PADD9_0_0_Z = 512 + 1,
|
||||
PADD9_0_1_Z = 512 + 2,
|
||||
PADD18_0_1_Z = 516,
|
||||
PADD9_0_2_Z = 516 + 1,
|
||||
PADD9_0_3_Z = 516 + 2,
|
||||
|
||||
MULT18X18_0_0_Z = 520,
|
||||
MULT9X9_0_0_Z = 520 + 1,
|
||||
MULT9X9_0_1_Z = 520 + 2,
|
||||
MULT18X18_0_1_Z = 524,
|
||||
MULT9X9_0_2_Z = 524 + 1,
|
||||
MULT9X9_0_3_Z = 524 + 2,
|
||||
|
||||
ALU54D_0_Z = 524 + 3,
|
||||
MULTALU18X18_0_Z = 528,
|
||||
MULTALU36X18_0_Z = 528 + 1,
|
||||
MULTADDALU18X18_0_Z = 528 + 2,
|
||||
|
||||
MULT36X36_Z = 528 + 3,
|
||||
|
||||
DSP_1_Z = 543, // DSP macro 1
|
||||
PADD18_1_0_Z = 544,
|
||||
PADD9_1_0_Z = 544 + 1,
|
||||
PADD9_1_1_Z = 544 + 2,
|
||||
PADD18_1_1_Z = 548,
|
||||
PADD9_1_2_Z = 548 + 1,
|
||||
PADD9_1_3_Z = 548 + 2,
|
||||
|
||||
MULT18X18_1_0_Z = 552,
|
||||
MULT9X9_1_0_Z = 552 + 1,
|
||||
MULT9X9_1_1_Z = 552 + 2,
|
||||
MULT18X18_1_1_Z = 556,
|
||||
MULT9X9_1_2_Z = 556 + 1,
|
||||
MULT9X9_1_3_Z = 556 + 2,
|
||||
|
||||
ALU54D_1_Z = 556 + 3,
|
||||
MULTALU18X18_1_Z = 560,
|
||||
MULTALU36X18_1_Z = 560 + 1,
|
||||
MULTADDALU18X18_1_Z = 560 + 2
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,50 @@ GSR_Z = 276
|
||||
VCC_Z = 277
|
||||
GND_Z = 278
|
||||
|
||||
DSP_Z = 509
|
||||
|
||||
DSP_0_Z = 511 # DSP macro 0
|
||||
PADD18_0_0_Z = 512
|
||||
PADD9_0_0_Z = 512 + 1
|
||||
PADD9_0_1_Z = 512 + 2
|
||||
PADD18_0_1_Z = 516
|
||||
PADD9_0_2_Z = 516 + 1
|
||||
PADD9_0_3_Z = 516 + 2
|
||||
|
||||
MULT18X18_0_0_Z = 520
|
||||
MULT9X9_0_0_Z = 520 + 1
|
||||
MULT9X9_0_1_Z = 520 + 2
|
||||
MULT18X18_0_1_Z = 524
|
||||
MULT9X9_0_2_Z = 524 + 1
|
||||
MULT9X9_0_3_Z = 524 + 2
|
||||
|
||||
ALU54D_0_Z = 524 + 3
|
||||
MULTALU18X18_0_Z = 528
|
||||
MULTALU36X18_0_Z = 528 + 1
|
||||
MULTADDALU18X18_0_Z = 528 + 2
|
||||
|
||||
MULT36X36_Z = 528 + 3
|
||||
|
||||
DSP_1_Z = 543 # DSP macro 1
|
||||
PADD18_1_0_Z = 544
|
||||
PADD9_1_0_Z = 544 + 1
|
||||
PADD9_1_1_Z = 544 + 2
|
||||
PADD18_1_1_Z = 548
|
||||
PADD9_1_2_Z = 548 + 1
|
||||
PADD9_1_3_Z = 548 + 2
|
||||
|
||||
MULT18X18_1_0_Z = 552
|
||||
MULT9X9_1_0_Z = 552 + 1
|
||||
MULT9X9_1_1_Z = 552 + 2
|
||||
MULT18X18_1_1_Z = 556
|
||||
MULT9X9_1_2_Z = 556 + 1
|
||||
MULT9X9_1_3_Z = 556 + 2
|
||||
|
||||
ALU54D_1_Z = 556 + 3
|
||||
MULTALU18X18_1_Z = 560
|
||||
MULTALU36X18_1_Z = 560 + 1
|
||||
MULTADDALU18X18_1_Z = 560 + 2
|
||||
|
||||
# =======================================
|
||||
# Chipdb additional info
|
||||
# =======================================
|
||||
@ -363,6 +407,15 @@ def create_tiletype(create_func, chip: Chip, db: chipdb, x: int, y: int, ttyp: i
|
||||
create_switch_matrix(tt, db, x, y)
|
||||
chip.set_tile_type(x, y, tdesc.tiletype)
|
||||
|
||||
def add_port_wire(tt, bel, portmap, name, wire_type, port_type):
|
||||
wire = portmap[name]
|
||||
if not tt.has_wire(wire):
|
||||
if name.startswith('CLK'):
|
||||
tt.create_wire(wire, "TILE_CLK")
|
||||
else:
|
||||
tt.create_wire(wire, wire_type)
|
||||
tt.add_bel_pin(bel, name, wire, port_type)
|
||||
|
||||
def create_null_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc: TypeDesc):
|
||||
typename = "NULL"
|
||||
tiletype = f"{typename}_{ttyp}"
|
||||
@ -586,29 +639,234 @@ def create_bsram_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tde
|
||||
portmap = db.grid[y][x].bels['BSRAM'].portmap
|
||||
bsram = tt.create_bel("BSRAM", "BSRAM", z = BSRAM_Z)
|
||||
|
||||
def add_port_wire(tt, bel, name, wire_type = "BSRAM_I", port_type = PinType.INPUT):
|
||||
wire = portmap[name]
|
||||
if not tt.has_wire(wire):
|
||||
if name.startswith('CLK'):
|
||||
tt.create_wire(wire, "TILE_CLK")
|
||||
else:
|
||||
tt.create_wire(wire, wire_type)
|
||||
tt.add_bel_pin(bel, name, wire, port_type)
|
||||
|
||||
for sfx in {'', 'A', 'B'}:
|
||||
for inp in _bsram_inputs:
|
||||
add_port_wire(tt, bsram, f"{inp}{sfx}")
|
||||
add_port_wire(tt, bsram, portmap, f"{inp}{sfx}", "BSRAM_I", PinType.INPUT)
|
||||
for idx in range(3):
|
||||
add_port_wire(tt, bsram, f"BLKSEL{sfx}{idx}")
|
||||
add_port_wire(tt, bsram, portmap, f"BLKSEL{sfx}{idx}", "BSRAM_I", PinType.INPUT)
|
||||
for idx in range(14):
|
||||
add_port_wire(tt, bsram, f"AD{sfx}{idx}")
|
||||
add_port_wire(tt, bsram, portmap, f"AD{sfx}{idx}", "BSRAM_I", PinType.INPUT)
|
||||
for idx in range(18):
|
||||
add_port_wire(tt, bsram, f"DI{sfx}{idx}")
|
||||
add_port_wire(tt, bsram, f"DO{sfx}{idx}", "BSRAM_O", PinType.OUTPUT)
|
||||
add_port_wire(tt, bsram, portmap, f"DI{sfx}{idx}", "BSRAM_I", PinType.INPUT)
|
||||
add_port_wire(tt, bsram, portmap, f"DO{sfx}{idx}", "BSRAM_O", PinType.OUTPUT)
|
||||
if not sfx:
|
||||
for idx in range(18, 36):
|
||||
add_port_wire(tt, bsram, f"DI{idx}")
|
||||
add_port_wire(tt, bsram, f"DO{idx}", "BSRAM_O", PinType.OUTPUT)
|
||||
add_port_wire(tt, bsram, portmap, f"DI{idx}", "BSRAM_I", PinType.INPUT)
|
||||
add_port_wire(tt, bsram, portmap, f"DO{idx}", "BSRAM_O", PinType.OUTPUT)
|
||||
|
||||
tdesc.tiletype = tiletype
|
||||
return tt
|
||||
|
||||
# DSP
|
||||
_mult_inputs = {'ASEL', 'BSEL', 'ASIGN', 'BSIGN'}
|
||||
def create_dsp_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc: TypeDesc):
|
||||
typename = "DSP"
|
||||
tiletype = f"{typename}_{ttyp}"
|
||||
if tdesc.sfx != 0:
|
||||
tiletype += f"_{tdesc.sfx}"
|
||||
tt = chip.create_tile_type(tiletype)
|
||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||
|
||||
# create big DSP
|
||||
belname = f'DSP'
|
||||
portmap = db.grid[y][x].bels[belname].portmap
|
||||
dsp = tt.create_bel(belname, "DSP", DSP_Z)
|
||||
dsp.flags = BEL_FLAG_HIDDEN
|
||||
|
||||
# create DSP macros
|
||||
for idx in range(2):
|
||||
belname = f'DSP{idx}'
|
||||
portmap = db.grid[y][x].bels[belname].portmap
|
||||
dsp = tt.create_bel(belname, "DSP", eval(f'DSP_{idx}_Z'))
|
||||
dsp.flags = BEL_FLAG_HIDDEN
|
||||
|
||||
# create pre-adders
|
||||
for mac, idx in [(mac, idx) for mac in range(2) for idx in range(4)]:
|
||||
belname = f'PADD9{mac}{idx}'
|
||||
portmap = db.grid[y][x].bels[belname].portmap
|
||||
dsp = tt.create_bel(belname, "PADD9", eval(f'PADD9_{mac}_{idx}_Z'))
|
||||
|
||||
add_port_wire(tt, dsp, portmap, "ADDSUB", "DSP_I", PinType.INPUT)
|
||||
for sfx in {'A', 'B'}:
|
||||
for inp in range(9):
|
||||
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}", "DSP_I", PinType.INPUT)
|
||||
for inp in range(9):
|
||||
add_port_wire(tt, dsp, portmap, f"C{inp}", "DSP_I", PinType.INPUT)
|
||||
for inp in range(4):
|
||||
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, "ASEL", "DSP_I", PinType.INPUT)
|
||||
for outp in range(9):
|
||||
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
||||
|
||||
for mac, idx in [(mac, idx) for mac in range(2) for idx in range(2)]:
|
||||
belname = f'PADD18{mac}{idx}'
|
||||
portmap = db.grid[y][x].bels[belname].portmap
|
||||
dsp = tt.create_bel(belname, "PADD18", eval(f'PADD18_{mac}_{idx}_Z'))
|
||||
|
||||
add_port_wire(tt, dsp, portmap, "ADDSUB", "DSP_I", PinType.INPUT)
|
||||
for sfx in {'A', 'B'}:
|
||||
for inp in range(18):
|
||||
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}", "DSP_I", PinType.INPUT)
|
||||
for inp in range(18):
|
||||
add_port_wire(tt, dsp, portmap, f"C{inp}", "DSP_I", PinType.INPUT)
|
||||
for inp in range(4):
|
||||
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, "ASEL", "DSP_I", PinType.INPUT)
|
||||
for outp in range(18):
|
||||
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
||||
|
||||
# create multipliers
|
||||
# mult 9x9
|
||||
for mac, idx in [(mac, idx) for mac in range(2) for idx in range(4)]:
|
||||
belname = f'MULT9X9{mac}{idx}'
|
||||
portmap = db.grid[y][x].bels[belname].portmap
|
||||
dsp = tt.create_bel(belname, "MULT9X9", eval(f'MULT9X9_{mac}_{idx}_Z'))
|
||||
|
||||
for sfx in {'A', 'B'}:
|
||||
for inp in range(9):
|
||||
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}", "DSP_I", PinType.INPUT)
|
||||
for inp in _mult_inputs:
|
||||
add_port_wire(tt, dsp, portmap, inp, "DSP_I", PinType.INPUT)
|
||||
for inp in range(4):
|
||||
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
||||
for outp in range(18):
|
||||
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
||||
|
||||
# mult 18x18
|
||||
for mac, idx in [(mac, idx) for mac in range(2) for idx in range(2)]:
|
||||
belname = f'MULT18X18{mac}{idx}'
|
||||
portmap = db.grid[y][x].bels[belname].portmap
|
||||
dsp = tt.create_bel(belname, "MULT18X18", eval(f'MULT18X18_{mac}_{idx}_Z'))
|
||||
|
||||
for sfx in {'A', 'B'}:
|
||||
for inp in range(18):
|
||||
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}", "DSP_I", PinType.INPUT)
|
||||
for inp in _mult_inputs:
|
||||
add_port_wire(tt, dsp, portmap, inp, "DSP_I", PinType.INPUT)
|
||||
for inp in range(4):
|
||||
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
||||
for outp in range(36):
|
||||
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
||||
|
||||
# mult 36x36
|
||||
belname = 'MULT36X36'
|
||||
portmap = db.grid[y][x].bels[belname].portmap
|
||||
dsp = tt.create_bel(belname, "MULT36X36", MULT36X36_Z)
|
||||
|
||||
for i in range(2):
|
||||
for sfx in {'A', 'B'}:
|
||||
for inp in range(36):
|
||||
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}{i}", "DSP_I", PinType.INPUT)
|
||||
for inp in {'ASIGN', 'BSIGN'}:
|
||||
add_port_wire(tt, dsp, portmap, f"{inp}{i}", "DSP_I", PinType.INPUT)
|
||||
for inp in range(4):
|
||||
add_port_wire(tt, dsp, portmap, f"CE{inp}{i}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"CLK{inp}{i}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"RESET{inp}{i}", "DSP_I", PinType.INPUT)
|
||||
for outp in range(72):
|
||||
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
||||
|
||||
# create alus
|
||||
for mac in range(2):
|
||||
belname = f'ALU54D{mac}'
|
||||
portmap = db.grid[y][x].bels[belname].portmap
|
||||
dsp = tt.create_bel(belname, "ALU54D", eval(f'ALU54D_{mac}_Z'))
|
||||
|
||||
for sfx in {'A', 'B'}:
|
||||
for inp in range(54):
|
||||
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}", "DSP_I", PinType.INPUT)
|
||||
for inp in {'ASIGN', 'BSIGN'}:
|
||||
add_port_wire(tt, dsp, portmap, inp, "DSP_I", PinType.INPUT)
|
||||
for inp in range(4):
|
||||
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
||||
if inp < 2:
|
||||
add_port_wire(tt, dsp, portmap, f"ACCLOAD{inp}", "DSP_I", PinType.INPUT)
|
||||
for outp in range(54):
|
||||
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
||||
|
||||
# create multalus
|
||||
# MULTALU18X18
|
||||
for mac in range(2):
|
||||
belname = f'MULTALU18X18{mac}'
|
||||
portmap = db.grid[y][x].bels[belname].portmap
|
||||
dsp = tt.create_bel(belname, "MULTALU18X18", eval(f'MULTALU18X18_{mac}_Z'))
|
||||
|
||||
for i in range(2):
|
||||
for sfx in {'ASIGN', 'BSIGN'}:
|
||||
add_port_wire(tt, dsp, portmap, f"{sfx}{i}", "DSP_I", PinType.INPUT)
|
||||
for sfx in {'A', 'B'}:
|
||||
for inp in range(18):
|
||||
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}{i}", "DSP_I", PinType.INPUT)
|
||||
for sfx in {'C', 'D'}:
|
||||
for inp in range(54):
|
||||
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, "DSIGN", "DSP_I", PinType.INPUT)
|
||||
for inp in range(4):
|
||||
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
||||
if inp < 2:
|
||||
add_port_wire(tt, dsp, portmap, f"ACCLOAD{inp}", "DSP_I", PinType.INPUT)
|
||||
for outp in range(54):
|
||||
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
||||
|
||||
# MULTALU36X18
|
||||
for mac in range(2):
|
||||
belname = f'MULTALU36X18{mac}'
|
||||
portmap = db.grid[y][x].bels[belname].portmap
|
||||
dsp = tt.create_bel(belname, "MULTALU36X18", eval(f'MULTALU36X18_{mac}_Z'))
|
||||
|
||||
for i in range(2):
|
||||
for sfx in {'ASIGN', 'BSIGN'}:
|
||||
add_port_wire(tt, dsp, portmap, f"{sfx}{i}", "DSP_I", PinType.INPUT)
|
||||
for inp in range(18):
|
||||
add_port_wire(tt, dsp, portmap, f"A{inp}{i}", "DSP_I", PinType.INPUT)
|
||||
for inp in range(7):
|
||||
add_port_wire(tt, dsp, portmap, f"ALUSEL{inp}", "DSP_I", PinType.INPUT)
|
||||
for inp in range(36):
|
||||
add_port_wire(tt, dsp, portmap, f"B{inp}", "DSP_I", PinType.INPUT)
|
||||
for inp in range(54):
|
||||
add_port_wire(tt, dsp, portmap, f"C{inp}", "DSP_I", PinType.INPUT)
|
||||
for inp in range(4):
|
||||
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
||||
for outp in range(54):
|
||||
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
||||
|
||||
# MULTADDALU18X18
|
||||
for mac in range(2):
|
||||
belname = f'MULTADDALU18X18{mac}'
|
||||
portmap = db.grid[y][x].bels[belname].portmap
|
||||
dsp = tt.create_bel(belname, "MULTADDALU18X18", eval(f'MULTADDALU18X18_{mac}_Z'))
|
||||
|
||||
for i in range(2):
|
||||
for sfx in {'ASIGN', 'BSIGN', 'ASEL', 'BSEL'}:
|
||||
add_port_wire(tt, dsp, portmap, f"{sfx}{i}", "DSP_I", PinType.INPUT)
|
||||
for inp in range(18):
|
||||
add_port_wire(tt, dsp, portmap, f"A{inp}{i}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"B{inp}{i}", "DSP_I", PinType.INPUT)
|
||||
for inp in range(7):
|
||||
add_port_wire(tt, dsp, portmap, f"ALUSEL{inp}", "DSP_I", PinType.INPUT)
|
||||
for inp in range(54):
|
||||
add_port_wire(tt, dsp, portmap, f"C{inp}", "DSP_I", PinType.INPUT)
|
||||
for inp in range(4):
|
||||
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
||||
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
||||
for outp in range(54):
|
||||
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
||||
|
||||
tdesc.tiletype = tiletype
|
||||
return tt
|
||||
@ -739,6 +997,7 @@ def main():
|
||||
ssram_tiletypes = db.tile_types['M']
|
||||
pll_tiletypes = db.tile_types['P']
|
||||
bsram_tiletypes = db.tile_types.get('B', set())
|
||||
dsp_tiletypes = db.tile_types.get('D', set())
|
||||
|
||||
# Setup tile grid
|
||||
for x in range(X):
|
||||
@ -758,6 +1017,8 @@ def main():
|
||||
create_tiletype(create_pll_tiletype, ch, db, x, y, ttyp)
|
||||
elif ttyp in bsram_tiletypes:
|
||||
create_tiletype(create_bsram_tiletype, ch, db, x, y, ttyp)
|
||||
elif ttyp in dsp_tiletypes:
|
||||
create_tiletype(create_dsp_tiletype, ch, db, x, y, ttyp)
|
||||
else:
|
||||
create_tiletype(create_null_tiletype, ch, db, x, y, ttyp)
|
||||
|
||||
|
@ -105,4 +105,124 @@ std::unique_ptr<CellInfo> GowinUtils::create_cell(IdString name, IdString type)
|
||||
return std::make_unique<CellInfo>(ctx, name, type);
|
||||
}
|
||||
|
||||
// DSP
|
||||
Loc GowinUtils::get_dsp_next_9_in_chain(Loc from) const
|
||||
{
|
||||
Loc res;
|
||||
res.y = from.y;
|
||||
if (get_dsp_18_idx(from.z) == 0) {
|
||||
res.x = from.x;
|
||||
res.z = from.z + 4;
|
||||
return res;
|
||||
}
|
||||
if (get_dsp_macro_num(from.z)) {
|
||||
// next DSP
|
||||
res.x = from.x + 9;
|
||||
res.z = from.z & (~0x24);
|
||||
} else {
|
||||
// next macro
|
||||
res.x = from.x;
|
||||
res.z = get_dsp_next_macro(from.z) & (~4);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Loc GowinUtils::get_dsp_next_macro_in_chain(Loc from) const
|
||||
{
|
||||
Loc res;
|
||||
res.y = from.y;
|
||||
if (get_dsp_macro_num(from.z)) {
|
||||
// next DSP
|
||||
res.x = from.x + 9;
|
||||
res.z = from.z & (~0x20);
|
||||
} else {
|
||||
// next macro
|
||||
res.x = from.x;
|
||||
res.z = get_dsp_next_macro(from.z);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Loc GowinUtils::get_dsp_next_in_chain(Loc from, IdString dsp_type) const
|
||||
{
|
||||
if (dsp_type.in(id_PADD9, id_PADD18, id_MULT9X9, id_MULT18X18)) {
|
||||
return get_dsp_next_9_in_chain(from);
|
||||
}
|
||||
if (dsp_type.in(id_ALU54D, id_MULTALU18X18, id_MULTALU36X18, id_MULTADDALU18X18)) {
|
||||
return get_dsp_next_macro_in_chain(from);
|
||||
}
|
||||
NPNR_ASSERT_FALSE("Unknown DSP cell type.");
|
||||
}
|
||||
|
||||
CellInfo *GowinUtils::dsp_bus_src(const CellInfo *ci, const char *bus_prefix, int wire_num) const
|
||||
{
|
||||
bool connected_to_const = false; // and disconnected too
|
||||
CellInfo *connected_to_cell = nullptr;
|
||||
|
||||
for (int i = 0; i < wire_num; ++i) {
|
||||
const NetInfo *net = ci->getPort(ctx->idf("%s[%d]", bus_prefix, i));
|
||||
if (connected_to_cell == nullptr) {
|
||||
if (net == nullptr || net->driver.cell == nullptr || net->name == ctx->id("$PACKER_VCC") ||
|
||||
net->name == ctx->id("$PACKER_GND")) {
|
||||
connected_to_const = true;
|
||||
continue;
|
||||
} else {
|
||||
if (connected_to_const) {
|
||||
log_error("The %s cell %s bus is connected simultaneously to constants and to another DSP.\n",
|
||||
ctx->nameOf(ci), bus_prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (net == nullptr || !is_dsp(net->driver.cell)) {
|
||||
log_error("The %s cell %s bus is not connected to another DSP.\n", ctx->nameOf(ci), bus_prefix);
|
||||
}
|
||||
if (connected_to_cell != nullptr && net->driver.cell != connected_to_cell) {
|
||||
log_error("The %s cell %s bus is connected to different DSPs: %s and %s.\n", ctx->nameOf(ci), bus_prefix,
|
||||
ctx->nameOf(connected_to_cell), ctx->nameOf(net->driver.cell));
|
||||
}
|
||||
connected_to_cell = net->driver.cell;
|
||||
}
|
||||
if (connected_to_const) {
|
||||
return nullptr;
|
||||
}
|
||||
return connected_to_cell;
|
||||
}
|
||||
|
||||
CellInfo *GowinUtils::dsp_bus_dst(const CellInfo *ci, const char *bus_prefix, int wire_num) const
|
||||
{
|
||||
bool disconnected = false; // and disconnected too
|
||||
CellInfo *connected_to_cell = nullptr;
|
||||
|
||||
for (int i = 0; i < wire_num; ++i) {
|
||||
const NetInfo *net = ci->getPort(ctx->idf("%s[%d]", bus_prefix, i));
|
||||
if (connected_to_cell == nullptr) {
|
||||
if (net == nullptr || net->users.entries() == 0) {
|
||||
disconnected = true;
|
||||
continue;
|
||||
} else {
|
||||
if (disconnected) {
|
||||
log_error("The %s cell %s bus is partially disconnected.\n", ctx->nameOf(ci), bus_prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (net->users.entries() > 1) {
|
||||
log_error("Net %s has >1 users.\n", ctx->nameOf(net));
|
||||
}
|
||||
|
||||
CellInfo *dst = (*net->users.begin()).cell;
|
||||
if (net == nullptr || !is_dsp(dst)) {
|
||||
log_error("The %s cell %s bus is not connected to another DSP.\n", ctx->nameOf(ci), bus_prefix);
|
||||
}
|
||||
if (connected_to_cell != nullptr && dst != connected_to_cell) {
|
||||
log_error("The %s cell %s bus is connected to different DSPs: %s and %s.\n", ctx->nameOf(ci), bus_prefix,
|
||||
ctx->nameOf(connected_to_cell), ctx->nameOf(dst));
|
||||
}
|
||||
connected_to_cell = dst;
|
||||
}
|
||||
if (disconnected) {
|
||||
return nullptr;
|
||||
}
|
||||
return connected_to_cell;
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -31,6 +31,30 @@ struct GowinUtils
|
||||
Loc get_pair_iologic_bel(Loc loc);
|
||||
BelId get_io_bel_from_iologic(BelId bel);
|
||||
|
||||
// DSP
|
||||
inline int get_dsp_18_z(int z) const { return z & (~3); }
|
||||
inline int get_dsp_9_idx(int z) const { return z & 3; }
|
||||
inline int get_dsp_18_idx(int z) const { return z & 4; }
|
||||
inline int get_dsp_paired_9(int z) const { return (3 - get_dsp_9_idx(z)) | (z & (~3)); }
|
||||
inline int get_dsp_mult_from_padd(int padd_z) const { return padd_z + 8; }
|
||||
inline int get_dsp_padd_from_mult(int mult_z) const { return mult_z - 8; }
|
||||
inline int get_dsp_next_macro(int z) const { return z + 32; }
|
||||
inline int get_dsp(int z) const { return BelZ::DSP_Z; }
|
||||
inline int get_dsp_macro(int z) const { return (z & 0x20) + BelZ::DSP_0_Z; }
|
||||
inline int get_dsp_macro_num(int z) const { return (z & 0x20) >> 5; }
|
||||
Loc get_dsp_next_9_in_chain(Loc from) const;
|
||||
Loc get_dsp_next_macro_in_chain(Loc from) const;
|
||||
Loc get_dsp_next_in_chain(Loc from, IdString dsp_type) const;
|
||||
|
||||
// check bus.
|
||||
// This is necessary to find the head in the DSP chain - these buses are
|
||||
// not switched in the hardware, but in software you can leave them
|
||||
// unconnected or connect them to VCC or VSS, which is the same - as I
|
||||
// already said, they are hard-wired and we are only discovering the fact
|
||||
// that they are not connected to another DSP in the chain.
|
||||
CellInfo *dsp_bus_src(const CellInfo *ci, const char *bus_prefix, int wire_num) const;
|
||||
CellInfo *dsp_bus_dst(const CellInfo *ci, const char *bus_prefix, int wire_num) const;
|
||||
|
||||
bool is_diff_io_supported(IdString type);
|
||||
bool have_bottom_io_cnds(void);
|
||||
IdString get_bottom_io_wire_a_net(int8_t condition);
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user