From badef293ebdf15567ddbf060f65c2eb7c1faa4ed Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 15 Mar 2022 11:02:37 +1000 Subject: 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 --- gowin/arch.cc | 53 +++++++++++++++++++++++++++- gowin/arch.h | 17 ++++++--- gowin/constids.inc | 12 +++++++ gowin/gfx.cc | 4 +-- gowin/pack.cc | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 177 insertions(+), 11 deletions(-) (limited to 'gowin') diff --git a/gowin/arch.cc b/gowin/arch.cc index 22874810..6213124f 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -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 for (int j = 0; j < 8; ++j) { - z = j + mux_0_z; + z = j + BelZ::mux_0_z; int grow = row + 1; int gcol = col + 1; @@ -990,6 +990,7 @@ Arch::Arch(ArchArgs args) : args(args) IdString portname; int z = 0; bool dff = true; + bool oddrc = false; switch (static_cast(bel->type_id)) { case ID_GSR0: 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)); 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: break; } diff --git a/gowin/arch.h b/gowin/arch.h index 7a5dfc46..c8392e7e 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -287,8 +287,8 @@ struct Arch : BaseArch // These functions include useful errors if not found WireInfo &wire_info(IdString wire); - PipInfo &pip_info(IdString wire); - BelInfo &bel_info(IdString wire); + PipInfo &pip_info(IdString pip); + BelInfo &bel_info(IdString bel); std::vector bel_ids, wire_ids, pip_ids; @@ -459,8 +459,6 @@ struct Arch : BaseArch void assignArchInfo() override; bool cellsCompatible(const CellInfo **cells, int count) const; bool haveBelType(int x, int y, IdString bel_type); - // start Z for the MUX2LUT5 bels - int const mux_0_z = 10; // chip db version unsigned int const chipdb_version = 1; @@ -476,6 +474,17 @@ struct Arch : BaseArch std::map 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 #endif /* GOWIN_ARCH_H */ diff --git a/gowin/constids.inc b/gowin/constids.inc index d32a987d..125fdc74 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -679,6 +679,18 @@ X(IOBHS) X(IOBIS) 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 X(MUX2_LUT5) X(MUX2_LUT6) diff --git a/gowin/gfx.cc b/gowin/gfx.cc index daae5c71..fcd42c7c 100644 --- a/gowin/gfx.cc +++ b/gowin/gfx.cc @@ -5639,14 +5639,14 @@ void gfxSetBelDefaultDecal(Arch *arch, BelInfo &bel) break; case ID_GW_MUX2_LUT5: 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; inactive.decal = id_DECAL_MUXUPPER_INACTIVE; arch->setBelDecal(bel.name, active, inactive); break; case ID_GW_MUX2_LUT6: 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; inactive.decal = id_DECAL_MUXLOWER_INACTIVE; arch->setBelDecal(bel.name, active, inactive); diff --git a/gowin/pack.cc b/gowin/pack.cc index 24daee31..9f0a2478 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -271,7 +271,7 @@ static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool &packed_ce // mux is the cluster root packed->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(); // reconnect MUX ports @@ -314,9 +314,9 @@ static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool &packed_ce // mux is the cluster root packed->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->constr_z = -ctx->mux_0_z + 1; + lut1->constr_z = -BelZ::mux_0_z + 1; packed->constr_children.clear(); // 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); } +// Pack IO logic +static void pack_iologic(Context *ctx) +{ + pool packed_cells; + pool delete_nets; + + std::vector> 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 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_n != nullptr); 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)); continue; } @@ -793,6 +885,7 @@ static void pack_diff_io(Context *ctx) ctx->cells[ncell->name] = std::move(ncell); } } + // Pack IO buffers static void pack_io(Context *ctx) { @@ -895,6 +988,7 @@ bool Arch::pack() pack_gsr(ctx); pack_io(ctx); pack_diff_io(ctx); + pack_iologic(ctx); pack_wideluts(ctx); pack_alus(ctx); pack_lut_lutffs(ctx); -- cgit v1.2.3