gowin: add support for ODDR primitive

Compatible with older versions of apicula bases.
Also small fixes and as the number of virtual Bels grows it is necessary
to assign them Z coordinate in a centralized way to avoid conflicts and
for this purpose introduced the BelZ enum.

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
YRabbit 2022-03-15 11:02:37 +10:00
parent b852df3260
commit badef293eb
5 changed files with 177 additions and 11 deletions

View File

@ -789,7 +789,7 @@ void Arch::addMuxBels(const DatabasePOD *db, int row, int col)
// 4 MUX2_LUT5, 2 MUX2_LUT6, 1 MUX2_LUT7, 1 MUX2_LUT8 // 4 MUX2_LUT5, 2 MUX2_LUT6, 1 MUX2_LUT7, 1 MUX2_LUT8
for (int j = 0; j < 8; ++j) { for (int j = 0; j < 8; ++j) {
z = j + mux_0_z; z = j + BelZ::mux_0_z;
int grow = row + 1; int grow = row + 1;
int gcol = col + 1; int gcol = col + 1;
@ -990,6 +990,7 @@ Arch::Arch(ArchArgs args) : args(args)
IdString portname; IdString portname;
int z = 0; int z = 0;
bool dff = true; bool dff = true;
bool oddrc = false;
switch (static_cast<ConstIds>(bel->type_id)) { switch (static_cast<ConstIds>(bel->type_id)) {
case ID_GSR0: case ID_GSR0:
snprintf(buf, 32, "R%dC%d_GSR0", row + 1, col + 1); snprintf(buf, 32, "R%dC%d_GSR0", row + 1, col + 1);
@ -1119,6 +1120,56 @@ Arch::Arch(ArchArgs args) : args(args)
addBelInput(belname, id_OEN, id(buf)); addBelInput(belname, id_OEN, id(buf));
break; break;
// IO logic
case ID_ODDRCB:
z++; /* fall-through*/
case ID_ODDRCA:
oddrc = true;
z++; /* fall-through*/
case ID_ODDRB:
z++; /* fall-through*/
case ID_ODDRA: {
snprintf(buf, 32, "R%dC%d_ODDR%s%c", row + 1, col + 1, oddrc ? "C" : "", 'A' + z - (oddrc ? 2 : 0));
belname = id(buf);
addBel(belname, id_ODDR, Loc(col, row, BelZ::iologic_0_z + z), false);
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_D0)->src_id);
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
addBelInput(belname, id_D0, id(buf));
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_D1)->src_id);
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
addBelInput(belname, id_D1, id(buf));
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_TX)->src_id);
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
addBelInput(belname, id_TX, id(buf));
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_CLK)->src_id);
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
addBelInput(belname, id_CLK, id(buf));
if (oddrc) {
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_CE)->src_id);
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
addBelInput(belname, id_CE, id(buf));
}
// dummy wires
snprintf(buf, 32, "ODDR%s%c_Q0", oddrc ? "C" : "", 'A' + z - (oddrc ? 2 : 0));
IdString id_q0 = id(buf);
IdString q0_name = wireToGlobal(row, col, db, id_q0);
if (wires.count(q0_name) == 0)
addWire(q0_name, id_q0, row, col);
addBelOutput(belname, id_Q0, q0_name);
snprintf(buf, 32, "ODDR%s%c_Q1", oddrc ? "C" : "", 'A' + z - (oddrc ? 2 : 0));
IdString id_q1 = id(buf);
IdString q1_name = wireToGlobal(row, col, db, id_q1);
if (wires.count(q1_name) == 0)
addWire(q1_name, id_q1, row, col);
addBelOutput(belname, id_Q1, q1_name);
} break;
default: default:
break; break;
} }

View File

@ -287,8 +287,8 @@ struct Arch : BaseArch<ArchRanges>
// These functions include useful errors if not found // These functions include useful errors if not found
WireInfo &wire_info(IdString wire); WireInfo &wire_info(IdString wire);
PipInfo &pip_info(IdString wire); PipInfo &pip_info(IdString pip);
BelInfo &bel_info(IdString wire); BelInfo &bel_info(IdString bel);
std::vector<IdString> bel_ids, wire_ids, pip_ids; std::vector<IdString> bel_ids, wire_ids, pip_ids;
@ -459,8 +459,6 @@ struct Arch : BaseArch<ArchRanges>
void assignArchInfo() override; void assignArchInfo() override;
bool cellsCompatible(const CellInfo **cells, int count) const; bool cellsCompatible(const CellInfo **cells, int count) const;
bool haveBelType(int x, int y, IdString bel_type); bool haveBelType(int x, int y, IdString bel_type);
// start Z for the MUX2LUT5 bels
int const mux_0_z = 10;
// chip db version // chip db version
unsigned int const chipdb_version = 1; unsigned int const chipdb_version = 1;
@ -476,6 +474,17 @@ struct Arch : BaseArch<ArchRanges>
std::map<const IdString, IdString> dff_comp_mode; std::map<const IdString, IdString> dff_comp_mode;
}; };
// Bels Z range
namespace BelZ {
enum
{
mux_0_z = 10, // start Z for the MUX2LUT5 bels
iologic_0_z = 20, // start Z for the IOLOGIC bels
vcc_0_z = 277, // virtual VCC bel Z
gnd_0_z = 278 // virtual VSS bel Z
};
}
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END
#endif /* GOWIN_ARCH_H */ #endif /* GOWIN_ARCH_H */

View File

@ -679,6 +679,18 @@ X(IOBHS)
X(IOBIS) X(IOBIS)
X(IOBJS) X(IOBJS)
// IOLOGIC
X(TX)
X(OBUF_TYPE)
X(SBUF)
X(DBUF)
X(ODDR)
X(ODDRC)
X(ODDRA)
X(ODDRB)
X(ODDRCA)
X(ODDRCB)
// Wide LUTs // Wide LUTs
X(MUX2_LUT5) X(MUX2_LUT5)
X(MUX2_LUT6) X(MUX2_LUT6)

View File

@ -5639,14 +5639,14 @@ void gfxSetBelDefaultDecal(Arch *arch, BelInfo &bel)
break; break;
case ID_GW_MUX2_LUT5: case ID_GW_MUX2_LUT5:
active.x = inactive.x = bel.x + mux2lut5_x; active.x = inactive.x = bel.x + mux2lut5_x;
active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut5_y[(bel.z - arch->mux_0_z) >> 1]; active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut5_y[(bel.z - BelZ::mux_0_z) >> 1];
active.decal = id_DECAL_MUXUPPER_ACTIVE; active.decal = id_DECAL_MUXUPPER_ACTIVE;
inactive.decal = id_DECAL_MUXUPPER_INACTIVE; inactive.decal = id_DECAL_MUXUPPER_INACTIVE;
arch->setBelDecal(bel.name, active, inactive); arch->setBelDecal(bel.name, active, inactive);
break; break;
case ID_GW_MUX2_LUT6: case ID_GW_MUX2_LUT6:
active.x = inactive.x = bel.x + mux2lut6_x; active.x = inactive.x = bel.x + mux2lut6_x;
active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut6_y[(bel.z - arch->mux_0_z) / 5]; active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut6_y[(bel.z - BelZ::mux_0_z) / 5];
active.decal = id_DECAL_MUXLOWER_ACTIVE; active.decal = id_DECAL_MUXLOWER_ACTIVE;
inactive.decal = id_DECAL_MUXLOWER_INACTIVE; inactive.decal = id_DECAL_MUXLOWER_INACTIVE;
arch->setBelDecal(bel.name, active, inactive); arch->setBelDecal(bel.name, active, inactive);

View File

@ -271,7 +271,7 @@ static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool<IdString> &packed_ce
// mux is the cluster root // mux is the cluster root
packed->cluster = packed->name; packed->cluster = packed->name;
lut1->cluster = packed->name; lut1->cluster = packed->name;
lut1->constr_z = -ctx->mux_0_z + 1; lut1->constr_z = -BelZ::mux_0_z + 1;
packed->constr_children.clear(); packed->constr_children.clear();
// reconnect MUX ports // reconnect MUX ports
@ -314,9 +314,9 @@ static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool<IdString> &packed_ce
// mux is the cluster root // mux is the cluster root
packed->cluster = packed->name; packed->cluster = packed->name;
lut0->cluster = packed->name; lut0->cluster = packed->name;
lut0->constr_z = -ctx->mux_0_z; lut0->constr_z = -BelZ::mux_0_z;
lut1->cluster = packed->name; lut1->cluster = packed->name;
lut1->constr_z = -ctx->mux_0_z + 1; lut1->constr_z = -BelZ::mux_0_z + 1;
packed->constr_children.clear(); packed->constr_children.clear();
// reconnect MUX ports // reconnect MUX ports
@ -724,8 +724,100 @@ static bool is_gowin_diff_iob(const Context *ctx, const CellInfo *cell)
} }
} }
static bool is_gowin_iologic(const Context *ctx, const CellInfo *cell)
{
switch (cell->type.index) {
case ID_ODDR: /* fall-through*/
case ID_ODDRC:
return true;
default:
return false;
}
}
static bool is_iob(const Context *ctx, const CellInfo *cell) { return (cell->type.index == ID_IOB); } static bool is_iob(const Context *ctx, const CellInfo *cell) { return (cell->type.index == ID_IOB); }
// Pack IO logic
static void pack_iologic(Context *ctx)
{
pool<IdString> packed_cells;
pool<IdString> delete_nets;
std::vector<std::unique_ptr<CellInfo>> new_cells;
log_info("Packing IO logic..\n");
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
if (ctx->verbose)
log_info("cell '%s' is of type '%s'\n", ctx->nameOf(ci), ci->type.c_str(ctx));
if (is_gowin_iologic(ctx, ci)) {
CellInfo *q0_dst = nullptr;
CellInfo *q1_dst = nullptr;
switch (ci->type.index) {
case ID_ODDRC: /* fall-through*/
case ID_ODDR: {
q0_dst = net_only_drives(ctx, ci->ports.at(id_Q0).net, is_iob, id_I);
NPNR_ASSERT(q0_dst != nullptr);
auto iob_bel = q0_dst->attrs.find(id_BEL);
if (q0_dst->attrs.count(id_DIFF_TYPE)) {
ci->attrs[id_OBUF_TYPE] = std::string("DBUF");
} else {
ci->attrs[id_OBUF_TYPE] = std::string("SBUF");
}
if (iob_bel != q0_dst->attrs.end()) {
// already know there to place, no need of any cluster stuff
Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(iob_bel->second.as_string()));
loc.z += BelZ::iologic_0_z;
ci->attrs[id_BEL] = ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx);
} else {
// make cluster from ODDR and OBUF
ci->cluster = ci->name;
ci->constr_x = 0;
ci->constr_y = 0;
ci->constr_z = 0;
ci->constr_abs_z = false;
ci->constr_children.push_back(q0_dst);
q0_dst->cluster = ci->name;
q0_dst->constr_x = 0;
q0_dst->constr_y = 0;
q0_dst->constr_z = -BelZ::iologic_0_z;
q0_dst->constr_abs_z = false;
}
// disconnect Q0 output: it is wired internally
delete_nets.insert(ci->ports.at(id_Q0).net->name);
q0_dst->disconnectPort(id_I);
ci->disconnectPort(id_Q0);
ci->attrs[id_IOBUF] = 0;
// if Q1 is conected then disconnet it too
if (port_used(ci, id_Q1)) {
q1_dst = net_only_drives(ctx, ci->ports.at(id_Q1).net, is_iob, id_OEN);
if (q1_dst != nullptr) {
delete_nets.insert(ci->ports.at(id_Q1).net->name);
q0_dst->disconnectPort(id_OEN);
ci->disconnectPort(id_Q1);
ci->attrs[id_IOBUF] = 1;
}
}
} break;
default:
break;
}
}
}
for (auto pcell : packed_cells) {
ctx->cells.erase(pcell);
}
for (auto dnet : delete_nets) {
ctx->nets.erase(dnet);
}
for (auto &ncell : new_cells) {
ctx->cells[ncell->name] = std::move(ncell);
}
}
// Pack differential IO buffers // Pack differential IO buffers
static void pack_diff_io(Context *ctx) static void pack_diff_io(Context *ctx)
{ {
@ -749,7 +841,7 @@ static void pack_diff_io(Context *ctx)
NPNR_ASSERT(iob_p != nullptr); NPNR_ASSERT(iob_p != nullptr);
NPNR_ASSERT(iob_n != nullptr); NPNR_ASSERT(iob_n != nullptr);
auto iob_p_bel_a = iob_p->attrs.find(id_BEL); auto iob_p_bel_a = iob_p->attrs.find(id_BEL);
if (iob_p_bel_a == ci->attrs.end()) { if (iob_p_bel_a == iob_p->attrs.end()) {
log_error("LVDS '%s' must be restricted.\n", ctx->nameOf(ci)); log_error("LVDS '%s' must be restricted.\n", ctx->nameOf(ci));
continue; continue;
} }
@ -793,6 +885,7 @@ static void pack_diff_io(Context *ctx)
ctx->cells[ncell->name] = std::move(ncell); ctx->cells[ncell->name] = std::move(ncell);
} }
} }
// Pack IO buffers // Pack IO buffers
static void pack_io(Context *ctx) static void pack_io(Context *ctx)
{ {
@ -895,6 +988,7 @@ bool Arch::pack()
pack_gsr(ctx); pack_gsr(ctx);
pack_io(ctx); pack_io(ctx);
pack_diff_io(ctx); pack_diff_io(ctx);
pack_iologic(ctx);
pack_wideluts(ctx); pack_wideluts(ctx);
pack_alus(ctx); pack_alus(ctx);
pack_lut_lutffs(ctx); pack_lut_lutffs(ctx);