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 <rabbit@yrabbit.cyou>
This commit is contained in:
parent
b36e8a3013
commit
20b7f760d9
@ -1696,20 +1696,19 @@ Arch::Arch(ArchArgs args) : args(args)
|
|||||||
belname = idf("R%dC%d_IOLOGIC%c", row + 1, col + 1, 'A' + z);
|
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);
|
addBel(belname, id_IOLOGIC, Loc(col, row, BelZ::iologic_z + z), false);
|
||||||
|
|
||||||
for (int i = 0; i < 10; ++i) {
|
IdString const iologic_in_ports[] = {id_TX0, id_TX1, id_TX2, id_TX3, id_RESET, id_CALIB,
|
||||||
if (i < 4) {
|
id_PCLK, id_D, id_D0, id_D1, id_D2, id_D3,
|
||||||
// TX
|
id_D4, id_D5, id_D6, id_D7, id_D8, id_D9};
|
||||||
IdString const tx[] = {id_TX0, id_TX1, id_TX2, id_TX3};
|
for (IdString port : iologic_in_ports) {
|
||||||
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, tx[i].hash())->src_id);
|
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, port.hash())->src_id);
|
||||||
addBelInput(belname, tx[i], idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
|
addBelInput(belname, port, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
|
||||||
}
|
}
|
||||||
// D
|
IdString const iologic_out_ports[] = {id_Q, id_Q0, id_Q1, id_Q2, id_Q3, id_Q4,
|
||||||
IdString const d[] = {id_D0, id_D1, id_D2, id_D3, id_D4, id_D5, id_D6, id_D7, id_D8, id_D9};
|
id_Q5, id_Q6, id_Q7, id_Q8, id_Q9};
|
||||||
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, d[i].hash())->src_id);
|
for (IdString port : iologic_out_ports) {
|
||||||
addBelInput(belname, d[i], idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
|
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);
|
auto fclk = pairLookup(bel->ports.get(), bel->num_ports, ID_FCLK);
|
||||||
// XXX as long as there is no special processing of the pins
|
// XXX as long as there is no special processing of the pins
|
||||||
if (fclk != nullptr) {
|
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)));
|
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;
|
} break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -766,6 +766,9 @@ X(TX2)
|
|||||||
X(TX3)
|
X(TX3)
|
||||||
X(FCLK)
|
X(FCLK)
|
||||||
X(PCLK)
|
X(PCLK)
|
||||||
|
X(CALIB)
|
||||||
|
X(Q8)
|
||||||
|
X(Q9)
|
||||||
X(ODDR_ALWAYS_LOW)
|
X(ODDR_ALWAYS_LOW)
|
||||||
X(ODDR_ALWAYS_HIGH)
|
X(ODDR_ALWAYS_HIGH)
|
||||||
X(GW9_ALWAYS_LOW0)
|
X(GW9_ALWAYS_LOW0)
|
||||||
@ -776,7 +779,9 @@ X(OBUF_TYPE)
|
|||||||
X(SBUF)
|
X(SBUF)
|
||||||
X(DBUF)
|
X(DBUF)
|
||||||
X(ODDR)
|
X(ODDR)
|
||||||
|
X(IDDR)
|
||||||
X(ODDRC)
|
X(ODDRC)
|
||||||
|
X(IDDRC)
|
||||||
X(ODDRA)
|
X(ODDRA)
|
||||||
X(ODDRB)
|
X(ODDRB)
|
||||||
X(ODDRCA)
|
X(ODDRCA)
|
||||||
@ -786,6 +791,11 @@ X(OSER8)
|
|||||||
X(OSER10)
|
X(OSER10)
|
||||||
X(OVIDEO)
|
X(OVIDEO)
|
||||||
X(OSER16)
|
X(OSER16)
|
||||||
|
X(IDES4)
|
||||||
|
X(IDES8)
|
||||||
|
X(IDES10)
|
||||||
|
X(IVIDEO)
|
||||||
|
X(IDES16)
|
||||||
X(IOLOGIC)
|
X(IOLOGIC)
|
||||||
X(IOLOGICA)
|
X(IOLOGICA)
|
||||||
X(IOLOGICB)
|
X(IOLOGICB)
|
||||||
|
138
gowin/pack.cc
138
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_OSER4: /* fall-through*/
|
||||||
case ID_OSER8: /* fall-through*/
|
case ID_OSER8: /* fall-through*/
|
||||||
case ID_OSER10: /* 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;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
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
|
// Pack IO logic
|
||||||
static void pack_iologic(Context *ctx)
|
static void pack_iologic(Context *ctx)
|
||||||
{
|
{
|
||||||
@ -1034,6 +1074,102 @@ static void pack_iologic(Context *ctx)
|
|||||||
ci->type = id_IOLOGIC;
|
ci->type = id_IOLOGIC;
|
||||||
}
|
}
|
||||||
} break;
|
} 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<CellInfo> 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<CellInfo> 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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user