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>
This commit is contained in:
parent
b852df3260
commit
badef293eb
@ -789,7 +789,7 @@ void Arch::addMuxBels(const DatabasePOD *db, int row, int col)
|
||||
|
||||
// 4 MUX2_LUT5, 2 MUX2_LUT6, 1 MUX2_LUT7, 1 MUX2_LUT8
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
z = j + mux_0_z;
|
||||
z = j + BelZ::mux_0_z;
|
||||
|
||||
int grow = row + 1;
|
||||
int gcol = col + 1;
|
||||
@ -990,6 +990,7 @@ Arch::Arch(ArchArgs args) : args(args)
|
||||
IdString portname;
|
||||
int z = 0;
|
||||
bool dff = true;
|
||||
bool oddrc = false;
|
||||
switch (static_cast<ConstIds>(bel->type_id)) {
|
||||
case ID_GSR0:
|
||||
snprintf(buf, 32, "R%dC%d_GSR0", row + 1, col + 1);
|
||||
@ -1119,6 +1120,56 @@ Arch::Arch(ArchArgs args) : args(args)
|
||||
addBelInput(belname, id_OEN, id(buf));
|
||||
break;
|
||||
|
||||
// IO logic
|
||||
case ID_ODDRCB:
|
||||
z++; /* fall-through*/
|
||||
case ID_ODDRCA:
|
||||
oddrc = true;
|
||||
z++; /* fall-through*/
|
||||
case ID_ODDRB:
|
||||
z++; /* fall-through*/
|
||||
case ID_ODDRA: {
|
||||
snprintf(buf, 32, "R%dC%d_ODDR%s%c", row + 1, col + 1, oddrc ? "C" : "", 'A' + z - (oddrc ? 2 : 0));
|
||||
belname = id(buf);
|
||||
addBel(belname, id_ODDR, Loc(col, row, BelZ::iologic_0_z + z), false);
|
||||
|
||||
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_D0)->src_id);
|
||||
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
|
||||
addBelInput(belname, id_D0, id(buf));
|
||||
|
||||
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_D1)->src_id);
|
||||
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
|
||||
addBelInput(belname, id_D1, id(buf));
|
||||
|
||||
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_TX)->src_id);
|
||||
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
|
||||
addBelInput(belname, id_TX, id(buf));
|
||||
|
||||
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_CLK)->src_id);
|
||||
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
|
||||
addBelInput(belname, id_CLK, id(buf));
|
||||
|
||||
if (oddrc) {
|
||||
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_CE)->src_id);
|
||||
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
|
||||
addBelInput(belname, id_CE, id(buf));
|
||||
}
|
||||
|
||||
// dummy wires
|
||||
snprintf(buf, 32, "ODDR%s%c_Q0", oddrc ? "C" : "", 'A' + z - (oddrc ? 2 : 0));
|
||||
IdString id_q0 = id(buf);
|
||||
IdString q0_name = wireToGlobal(row, col, db, id_q0);
|
||||
if (wires.count(q0_name) == 0)
|
||||
addWire(q0_name, id_q0, row, col);
|
||||
addBelOutput(belname, id_Q0, q0_name);
|
||||
|
||||
snprintf(buf, 32, "ODDR%s%c_Q1", oddrc ? "C" : "", 'A' + z - (oddrc ? 2 : 0));
|
||||
IdString id_q1 = id(buf);
|
||||
IdString q1_name = wireToGlobal(row, col, db, id_q1);
|
||||
if (wires.count(q1_name) == 0)
|
||||
addWire(q1_name, id_q1, row, col);
|
||||
addBelOutput(belname, id_Q1, q1_name);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
17
gowin/arch.h
17
gowin/arch.h
@ -287,8 +287,8 @@ struct Arch : BaseArch<ArchRanges>
|
||||
|
||||
// These functions include useful errors if not found
|
||||
WireInfo &wire_info(IdString wire);
|
||||
PipInfo &pip_info(IdString wire);
|
||||
BelInfo &bel_info(IdString wire);
|
||||
PipInfo &pip_info(IdString pip);
|
||||
BelInfo &bel_info(IdString bel);
|
||||
|
||||
std::vector<IdString> bel_ids, wire_ids, pip_ids;
|
||||
|
||||
@ -459,8 +459,6 @@ struct Arch : BaseArch<ArchRanges>
|
||||
void assignArchInfo() override;
|
||||
bool cellsCompatible(const CellInfo **cells, int count) const;
|
||||
bool haveBelType(int x, int y, IdString bel_type);
|
||||
// start Z for the MUX2LUT5 bels
|
||||
int const mux_0_z = 10;
|
||||
// chip db version
|
||||
unsigned int const chipdb_version = 1;
|
||||
|
||||
@ -476,6 +474,17 @@ struct Arch : BaseArch<ArchRanges>
|
||||
std::map<const IdString, IdString> dff_comp_mode;
|
||||
};
|
||||
|
||||
// Bels Z range
|
||||
namespace BelZ {
|
||||
enum
|
||||
{
|
||||
mux_0_z = 10, // start Z for the MUX2LUT5 bels
|
||||
iologic_0_z = 20, // start Z for the IOLOGIC bels
|
||||
vcc_0_z = 277, // virtual VCC bel Z
|
||||
gnd_0_z = 278 // virtual VSS bel Z
|
||||
};
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif /* GOWIN_ARCH_H */
|
||||
|
@ -679,6 +679,18 @@ X(IOBHS)
|
||||
X(IOBIS)
|
||||
X(IOBJS)
|
||||
|
||||
// IOLOGIC
|
||||
X(TX)
|
||||
X(OBUF_TYPE)
|
||||
X(SBUF)
|
||||
X(DBUF)
|
||||
X(ODDR)
|
||||
X(ODDRC)
|
||||
X(ODDRA)
|
||||
X(ODDRB)
|
||||
X(ODDRCA)
|
||||
X(ODDRCB)
|
||||
|
||||
// Wide LUTs
|
||||
X(MUX2_LUT5)
|
||||
X(MUX2_LUT6)
|
||||
|
@ -5639,14 +5639,14 @@ void gfxSetBelDefaultDecal(Arch *arch, BelInfo &bel)
|
||||
break;
|
||||
case ID_GW_MUX2_LUT5:
|
||||
active.x = inactive.x = bel.x + mux2lut5_x;
|
||||
active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut5_y[(bel.z - arch->mux_0_z) >> 1];
|
||||
active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut5_y[(bel.z - BelZ::mux_0_z) >> 1];
|
||||
active.decal = id_DECAL_MUXUPPER_ACTIVE;
|
||||
inactive.decal = id_DECAL_MUXUPPER_INACTIVE;
|
||||
arch->setBelDecal(bel.name, active, inactive);
|
||||
break;
|
||||
case ID_GW_MUX2_LUT6:
|
||||
active.x = inactive.x = bel.x + mux2lut6_x;
|
||||
active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut6_y[(bel.z - arch->mux_0_z) / 5];
|
||||
active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut6_y[(bel.z - BelZ::mux_0_z) / 5];
|
||||
active.decal = id_DECAL_MUXLOWER_ACTIVE;
|
||||
inactive.decal = id_DECAL_MUXLOWER_INACTIVE;
|
||||
arch->setBelDecal(bel.name, active, inactive);
|
||||
|
102
gowin/pack.cc
102
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);
|
||||
|
Loading…
Reference in New Issue
Block a user