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:
parent
4666ea7051
commit
ad49b7c78d
@ -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)
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user