From 20b7f760d925c0c102f5a4d03adeb3f3af4ca6f6 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 4 Apr 2023 09:50:48 +1000 Subject: gowin: Add support for IDES primitives * placement of IDES4, IVIDEO, IDES8 and IDES10 primitives is supported; * primitives are implemented for the GW1N-1, GW1NZ-1, GW1NSR-4C, GW1NR-9, GW1NR-9C chips; * tricks required for IOLOGIC to work on one side of the -9 and -9C chips are taken into account; Compatible with old apicula bases. Signed-off-by: YRabbit --- gowin/arch.cc | 27 +++++------ gowin/constids.inc | 10 ++++ gowin/pack.cc | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 159 insertions(+), 16 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 9f091a5d..8292e322 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -1696,20 +1696,19 @@ Arch::Arch(ArchArgs args) : args(args) belname = idf("R%dC%d_IOLOGIC%c", row + 1, col + 1, 'A' + z); addBel(belname, id_IOLOGIC, Loc(col, row, BelZ::iologic_z + z), false); - for (int i = 0; i < 10; ++i) { - if (i < 4) { - // TX - IdString const tx[] = {id_TX0, id_TX1, id_TX2, id_TX3}; - portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, tx[i].hash())->src_id); - addBelInput(belname, tx[i], idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this))); - } - // D - IdString const d[] = {id_D0, id_D1, id_D2, id_D3, id_D4, id_D5, id_D6, id_D7, id_D8, id_D9}; - portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, d[i].hash())->src_id); - addBelInput(belname, d[i], idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this))); + IdString const iologic_in_ports[] = {id_TX0, id_TX1, id_TX2, id_TX3, id_RESET, id_CALIB, + id_PCLK, id_D, id_D0, id_D1, id_D2, id_D3, + id_D4, id_D5, id_D6, id_D7, id_D8, id_D9}; + for (IdString port : iologic_in_ports) { + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, port.hash())->src_id); + addBelInput(belname, port, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this))); + } + IdString const iologic_out_ports[] = {id_Q, id_Q0, id_Q1, id_Q2, id_Q3, id_Q4, + id_Q5, id_Q6, id_Q7, id_Q8, id_Q9}; + for (IdString port : iologic_out_ports) { + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, port.hash())->src_id); + addBelOutput(belname, port, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this))); } - portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_PCLK)->src_id); - addBelInput(belname, id_PCLK, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this))); auto fclk = pairLookup(bel->ports.get(), bel->num_ports, ID_FCLK); // XXX as long as there is no special processing of the pins if (fclk != nullptr) { @@ -1745,8 +1744,6 @@ Arch::Arch(ArchArgs args) : args(args) addBelInput(belname, id_FCLK, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this))); } } - portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_RESET)->src_id); - addBelInput(belname, id_RESET, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this))); } break; default: break; diff --git a/gowin/constids.inc b/gowin/constids.inc index e861275f..5bd2aa46 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -766,6 +766,9 @@ X(TX2) X(TX3) X(FCLK) X(PCLK) +X(CALIB) +X(Q8) +X(Q9) X(ODDR_ALWAYS_LOW) X(ODDR_ALWAYS_HIGH) X(GW9_ALWAYS_LOW0) @@ -776,7 +779,9 @@ X(OBUF_TYPE) X(SBUF) X(DBUF) X(ODDR) +X(IDDR) X(ODDRC) +X(IDDRC) X(ODDRA) X(ODDRB) X(ODDRCA) @@ -786,6 +791,11 @@ X(OSER8) X(OSER10) X(OVIDEO) X(OSER16) +X(IDES4) +X(IDES8) +X(IDES10) +X(IVIDEO) +X(IDES16) X(IOLOGIC) X(IOLOGICA) X(IOLOGICB) diff --git a/gowin/pack.cc b/gowin/pack.cc index 49d428a8..c7da985f 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -817,13 +817,53 @@ static bool is_gowin_iologic(const Context *ctx, const CellInfo *cell) case ID_OSER4: /* fall-through*/ case ID_OSER8: /* fall-through*/ case ID_OSER10: /* fall-through*/ - case ID_OVIDEO: + case ID_OVIDEO: /* fall-through*/ + case ID_IDDR: /* fall-through*/ + case ID_IDDRC: /* fall-through*/ + case ID_IDES4: /* fall-through*/ + case ID_IDES8: /* fall-through*/ + case ID_IDES10: /* fall-through*/ + case ID_IVIDEO: return true; default: return false; } } +// IDES has different outputs +static void reconnect_ides_outs(CellInfo *ci) +{ + switch (ci->type.hash()) { + case ID_IDES4: + ci->renamePort(id_Q3, id_Q9); + ci->renamePort(id_Q2, id_Q8); + ci->renamePort(id_Q1, id_Q7); + ci->renamePort(id_Q0, id_Q6); + break; + case ID_IVIDEO: + ci->renamePort(id_Q6, id_Q9); + ci->renamePort(id_Q5, id_Q8); + ci->renamePort(id_Q4, id_Q7); + ci->renamePort(id_Q3, id_Q6); + ci->renamePort(id_Q2, id_Q5); + ci->renamePort(id_Q1, id_Q4); + ci->renamePort(id_Q0, id_Q3); + break; + case ID_IDES8: + ci->renamePort(id_Q7, id_Q9); + ci->renamePort(id_Q6, id_Q8); + ci->renamePort(id_Q5, id_Q7); + ci->renamePort(id_Q4, id_Q6); + ci->renamePort(id_Q3, id_Q5); + ci->renamePort(id_Q2, id_Q4); + ci->renamePort(id_Q1, id_Q3); + ci->renamePort(id_Q0, id_Q2); + break; + default: + break; + } +} + // Pack IO logic static void pack_iologic(Context *ctx) { @@ -1034,6 +1074,102 @@ static void pack_iologic(Context *ctx) ci->type = id_IOLOGIC; } } break; + case ID_IDDR: /* fall-through */ + case ID_IDES4: /* fall-through */ + case ID_IDES8: /* fall-through */ + case ID_IDES10: /* fall-through */ + case ID_IVIDEO: { + CellInfo *d_src = net_driven_by(ctx, ci->getPort(id_D), is_iob, id_O); + NPNR_ASSERT(d_src != nullptr); + + auto iob_bel = d_src->attrs.find(id_BEL); + if (iob_bel == d_src->attrs.end()) { + log_error("No constraints for %s. The pins for IDES/OSER must be specified explicitly.\n", + ctx->nameOf(d_src)); + } + + Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(iob_bel->second.as_string())); + loc.z += BelZ::iologic_z; + ci->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx)); + BelId bel = ctx->getBelByLocation(loc); + if (bel == BelId()) { + log_info("No bel for %s at %s. Can't place IDES/OSER here\n", ctx->nameOf(ci), + iob_bel->second.as_string().c_str()); + } + std::string in_mode; + switch (ci->type.hash()) { + case ID_IDES4: + in_mode = "IDDRX2"; + break; + case ID_IDES8: + in_mode = "IDDRX4"; + break; + case ID_IDES10: + in_mode = "IDDRX5"; + break; + case ID_IVIDEO: + in_mode = "VIDEORX"; + break; + } + ci->setParam(ctx->id("INMODE"), in_mode); + bool use_diff_io = false; + if (d_src->attrs.count(id_DIFF_TYPE)) { + ci->setAttr(id_OBUF_TYPE, std::string("DBUF")); + use_diff_io = true; + } else { + ci->setAttr(id_OBUF_TYPE, std::string("SBUF")); + } + + // disconnect D input: it is wired internally + delete_nets.insert(ci->getPort(id_D)->name); + d_src->disconnectPort(id_O); + ci->disconnectPort(id_D); + + // XXX place for -9 and -9C oddity + + ci->setAttr(id_IOLOGIC_TYPE, ci->type.str(ctx)); + reconnect_ides_outs(ci); + + // common clock inputs + if (ci->type == id_IDES4) { + ci->type = id_IOLOGIC; + // two IDER4 share FCLK, check it + Loc other_loc = loc; + other_loc.z = 1 - loc.z + 2 * BelZ::iologic_z; + BelId other_bel = ctx->getBelByLocation(other_loc); + CellInfo *other_cell = ctx->getBoundBelCell(other_bel); + if (other_cell != nullptr) { + NPNR_ASSERT(other_cell->type == id_IDES4); + if (ci->ports.at(id_FCLK).net != other_cell->ports.at(id_FCLK).net) { + log_error("%s and %s have differnet FCLK nets\n", ctx->nameOf(ci), ctx->nameOf(other_cell)); + } + } + } else { + std::unique_ptr dummy = + create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_DUMMY_IOLOGIC_IO"); + loc.z = 1 - loc.z + BelZ::iologic_z; + if (!use_diff_io) { + dummy->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx)); + new_cells.push_back(std::move(dummy)); + } + loc.z += BelZ::iologic_z; + + std::unique_ptr aux_cell = + create_generic_cell(ctx, id_IOLOGIC, ci->name.str(ctx) + "_AUX"); + ci->setAttr(ctx->id("IOLOGIC_AUX_CELL"), ci->name.str(ctx) + "_AUX"); + aux_cell->setParam(ctx->id("INMODE"), std::string("DDRENABLE")); + aux_cell->setAttr(ctx->id("IOLOGIC_MASTER_CELL"), ci->name.str(ctx)); + aux_cell->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx)); + if (port_used(ci, id_RESET)) { + aux_cell->connectPort(id_RESET, ci->ports.at(id_RESET).net); + } + if (port_used(ci, id_PCLK)) { + aux_cell->connectPort(id_PCLK, ci->ports.at(id_PCLK).net); + } + new_cells.push_back(std::move(aux_cell)); + ci->type = id_IOLOGIC; + } + } break; default: break; } -- cgit v1.2.3