aboutsummaryrefslogtreecommitdiffstats
path: root/gowin
diff options
context:
space:
mode:
Diffstat (limited to 'gowin')
-rw-r--r--gowin/arch.cc53
-rw-r--r--gowin/arch.h17
-rw-r--r--gowin/constids.inc12
-rw-r--r--gowin/gfx.cc4
-rw-r--r--gowin/pack.cc102
5 files changed, 177 insertions, 11 deletions
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<ConstIds>(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<ArchRanges>
// 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<IdString> bel_ids, wire_ids, pip_ids;
@@ -459,8 +459,6 @@ struct Arch : BaseArch<ArchRanges>
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<ArchRanges>
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
#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<IdString> &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<IdString> &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<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
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);