aboutsummaryrefslogtreecommitdiffstats
path: root/gowin/pack.cc
diff options
context:
space:
mode:
authorYRabbit <rabbit@yrabbit.cyou>2022-03-15 11:02:37 +1000
committerYRabbit <rabbit@yrabbit.cyou>2022-03-15 11:02:37 +1000
commitbadef293ebdf15567ddbf060f65c2eb7c1faa4ed (patch)
treee2c7abb567088b5287455ef240a5786c2137b2db /gowin/pack.cc
parentb852df3260a19aad40607c7520e3ed19895e636f (diff)
downloadnextpnr-badef293ebdf15567ddbf060f65c2eb7c1faa4ed.tar.gz
nextpnr-badef293ebdf15567ddbf060f65c2eb7c1faa4ed.tar.bz2
nextpnr-badef293ebdf15567ddbf060f65c2eb7c1faa4ed.zip
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>
Diffstat (limited to 'gowin/pack.cc')
-rw-r--r--gowin/pack.cc102
1 files changed, 98 insertions, 4 deletions
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);