gowin: Add support for true differential output

The new primitive appears as an amalgamation of two existing OBUF
primitives.  Compatible with older versions of apicula, although, of
course, using TLVDS_OBUF with old databases will not bring the desired
result, but no crash.

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
YRabbit 2022-02-23 15:53:04 +10:00
parent 4666ea7051
commit ad49b7c78d
2 changed files with 87 additions and 0 deletions

View File

@ -636,6 +636,8 @@ X(I)
X(O) X(O)
X(IO) X(IO)
X(OE) X(OE)
X(OB)
// bels // bels
X(DFF0) X(DFF0)
X(DFF1) X(DFF1)
@ -735,6 +737,7 @@ X(IBUF)
X(OBUF) X(OBUF)
X(IOBUF) X(IOBUF)
X(TBUF) X(TBUF)
X(TLVDS_OBUF)
// primitive attributes // primitive attributes
X(INIT) X(INIT)
@ -744,6 +747,8 @@ X(INPUT_USED)
X(OUTPUT_USED) X(OUTPUT_USED)
X(ENABLE_USED) X(ENABLE_USED)
X(BEL) X(BEL)
X(DIFF)
X(DIFF_TYPE)
// ports // ports
X(EN) X(EN)

View File

@ -678,6 +678,85 @@ static bool is_gowin_iob(const Context *ctx, const CellInfo *cell)
} }
} }
static bool is_gowin_diff_iob(const Context *ctx, const CellInfo *cell)
{
switch (cell->type.index) {
case ID_TLVDS_OBUF:
return true;
default:
return false;
}
}
static bool is_iob(const Context *ctx, const CellInfo *cell) { return (cell->type.index == ID_IOB); }
// Pack differential IO buffers
static void pack_diff_io(Context *ctx)
{
pool<IdString> packed_cells;
pool<IdString> delete_nets;
std::vector<std::unique_ptr<CellInfo>> new_cells;
log_info("Packing diff IOs..\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_diff_iob(ctx, ci)) {
CellInfo *iob_p = nullptr;
CellInfo *iob_n = nullptr;
switch (ci->type.index) {
case ID_TLVDS_OBUF: {
iob_p = net_only_drives(ctx, ci->ports.at(id_O).net, is_iob, id_I);
iob_n = net_only_drives(ctx, ci->ports.at(id_OB).net, is_iob, id_I);
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()) {
log_error("LVDS '%s' must be restricted.\n", ctx->nameOf(ci));
continue;
}
BelId iob_p_bel = ctx->getBelByNameStr(iob_p_bel_a->second.as_string());
Loc loc_p = ctx->getBelLocation(iob_p_bel);
if (loc_p.z != 0) {
log_error("LVDS '%s' positive pin is not A.\n", ctx->nameOf(ci));
continue;
}
// restrict the N buffer
loc_p.z = 1;
iob_n->attrs[id_BEL] = ctx->getBelName(ctx->getBelByLocation(loc_p)).str(ctx);
// mark IOBs as part of DS pair
iob_n->attrs[id_DIFF] = std::string("N");
iob_n->attrs[id_DIFF_TYPE] = std::string("TLVDS_OBUF");
iob_p->attrs[id_DIFF] = std::string("P");
iob_p->attrs[id_DIFF_TYPE] = std::string("TLVDS_OBUF");
// disconnect N input: it is wired internally
delete_nets.insert(iob_n->ports.at(id_I).net->name);
iob_n->disconnectPort(id_I);
ci->disconnectPort(id_OB);
// disconnect P output
delete_nets.insert(ci->ports.at(id_O).net->name);
ci->disconnectPort(id_O);
// connect TLVDS input to P input
ci->movePortTo(id_I, iob_p, id_I);
packed_cells.insert(ci->name);
} 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 IO buffers // Pack IO buffers
static void pack_io(Context *ctx) static void pack_io(Context *ctx)
{ {
@ -689,6 +768,8 @@ static void pack_io(Context *ctx)
for (auto &cell : ctx->cells) { for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get(); 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_iob(ctx, ci)) { if (is_gowin_iob(ctx, ci)) {
CellInfo *iob = nullptr; CellInfo *iob = nullptr;
switch (ci->type.index) { switch (ci->type.index) {
@ -776,6 +857,7 @@ bool Arch::pack()
log_break(); log_break();
pack_constants(ctx); pack_constants(ctx);
pack_io(ctx); pack_io(ctx);
pack_diff_io(ctx);
pack_wideluts(ctx); pack_wideluts(ctx);
pack_alus(ctx); pack_alus(ctx);
pack_lut_lutffs(ctx); pack_lut_lutffs(ctx);