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:
YRabbit 2024-03-18 22:08:52 +10:00 committed by myrtle
parent ff96fc5af1
commit 210e0fa33b
8 changed files with 1725 additions and 30 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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
};
}

View File

@ -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)

View File

@ -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

View File

@ -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