gowin: implement IDES16 and OSER16 primitives
These are very cumbersome primitives that take up two cells and consequently 4 IOLOGIC bels. The primitives are implemented for the chips that contain them and are supported by apicula GW1NSR-4C, GW1NR-9 and GW1NR-9C. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
7557d33dc6
commit
fddacb3dc1
162
gowin/arch.cc
162
gowin/arch.cc
@ -1194,6 +1194,70 @@ void Arch::add_rpll_ports(DatabasePOD const *db, BelsPOD const *bel, IdString be
|
||||
}
|
||||
}
|
||||
|
||||
static bool skip_aux_oser16(std::string device, int row, int col)
|
||||
{
|
||||
if (device == "GW1NSR-4C") {
|
||||
switch (col) {
|
||||
case 2: /* fall-through*/
|
||||
case 4: /* fall-through*/
|
||||
case 6: /* fall-through*/
|
||||
case 8: /* fall-through*/
|
||||
case 9: /* fall-through*/
|
||||
case 11: /* fall-through*/
|
||||
case 13: /* fall-through*/
|
||||
case 15: /* fall-through*/
|
||||
case 17: /* fall-through*/
|
||||
case 18: /* fall-through*/
|
||||
case 20: /* fall-through*/
|
||||
case 22: /* fall-through*/
|
||||
case 24: /* fall-through*/
|
||||
case 26: /* fall-through*/
|
||||
case 27: /* fall-through*/
|
||||
case 29: /* fall-through*/
|
||||
case 31: /* fall-through*/
|
||||
case 33: /* fall-through*/
|
||||
case 35:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (device == "GW1NR-9" || device == "GW1NR-9C") {
|
||||
switch (col) {
|
||||
case 2: /* fall-through*/
|
||||
case 4: /* fall-through*/
|
||||
case 6: /* fall-through*/
|
||||
case 8: /* fall-through*/
|
||||
case 9: /* fall-through*/
|
||||
case 11: /* fall-through*/
|
||||
case 13: /* fall-through*/
|
||||
case 15: /* fall-through*/
|
||||
case 17: /* fall-through*/
|
||||
case 18: /* fall-through*/
|
||||
case 19: /* fall-through*/
|
||||
case 21: /* fall-through*/
|
||||
case 23: /* fall-through*/
|
||||
case 25: /* fall-through*/
|
||||
case 27: /* fall-through*/
|
||||
case 28: /* fall-through*/
|
||||
case 29: /* fall-through*/
|
||||
case 31: /* fall-through*/
|
||||
case 33: /* fall-through*/
|
||||
case 35: /* fall-through*/
|
||||
case 36: /* fall-through*/
|
||||
case 37: /* fall-through*/
|
||||
case 39: /* fall-through*/
|
||||
case 41: /* fall-through*/
|
||||
case 43: /* fall-through*/
|
||||
case 45:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Arch::Arch(ArchArgs args) : args(args)
|
||||
{
|
||||
family = args.family;
|
||||
@ -1745,6 +1809,92 @@ Arch::Arch(ArchArgs args) : args(args)
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case ID_OSER16: {
|
||||
if (skip_aux_oser16(device, row, col)) {
|
||||
break;
|
||||
}
|
||||
belname = idf("R%dC%d_OSER16", row + 1, col + 1);
|
||||
addBel(belname, id_OSER16, Loc(col, row, BelZ::oser16_z), false);
|
||||
|
||||
IdString const oser16_in_ports[] = {id_RESET, id_PCLK, id_D0, id_D1, id_D2, id_D3,
|
||||
id_D4, id_D5, id_D6, id_D7, id_D8, id_D9,
|
||||
id_D10, id_D11, id_D12, id_D13, id_D14, id_D15};
|
||||
for (IdString port : oser16_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)));
|
||||
}
|
||||
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, id_Q0.hash())->src_id);
|
||||
addBelOutput(belname, id_Q, 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) {
|
||||
portname = IdString(fclk->src_id);
|
||||
IdString wire = idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
|
||||
if (wires.count(wire) == 0) {
|
||||
GlobalAliasPOD alias;
|
||||
alias.dest_col = col;
|
||||
alias.dest_row = row;
|
||||
alias.dest_id = portname.hash();
|
||||
auto alias_src = genericLookup(db->aliases.get(), db->num_aliases, alias, aliasCompare);
|
||||
if (alias_src != nullptr) {
|
||||
int srcrow = alias_src->src_row;
|
||||
int srccol = alias_src->src_col;
|
||||
IdString srcid = IdString(alias_src->src_id);
|
||||
wire = wireToGlobal(srcrow, srccol, db, srcid);
|
||||
if (wires.count(wire) == 0) {
|
||||
addWire(wire, srcid, srccol, srcrow);
|
||||
}
|
||||
addBelInput(belname, id_FCLK, wire);
|
||||
}
|
||||
} else {
|
||||
addBelInput(belname, id_FCLK, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case ID_IDES16: {
|
||||
if (skip_aux_oser16(device, row, col)) {
|
||||
break;
|
||||
}
|
||||
belname = idf("R%dC%d_IDES16", row + 1, col + 1);
|
||||
addBel(belname, id_IDES16, Loc(col, row, BelZ::ides16_z), false);
|
||||
|
||||
IdString const ides16_in_ports[] = {id_RESET, id_PCLK, id_CALIB, id_D};
|
||||
for (IdString port : ides16_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 ides16_out_ports[] = {id_Q0, id_Q1, id_Q2, id_Q3, id_Q4, id_Q5, id_Q6, id_Q7,
|
||||
id_Q8, id_Q9, id_Q10, id_Q11, id_Q12, id_Q13, id_Q14, id_Q15};
|
||||
for (IdString port : ides16_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)));
|
||||
}
|
||||
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) {
|
||||
portname = IdString(fclk->src_id);
|
||||
IdString wire = idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
|
||||
if (wires.count(wire) == 0) {
|
||||
GlobalAliasPOD alias;
|
||||
alias.dest_col = col;
|
||||
alias.dest_row = row;
|
||||
alias.dest_id = portname.hash();
|
||||
auto alias_src = genericLookup(db->aliases.get(), db->num_aliases, alias, aliasCompare);
|
||||
if (alias_src != nullptr) {
|
||||
int srcrow = alias_src->src_row;
|
||||
int srccol = alias_src->src_col;
|
||||
IdString srcid = IdString(alias_src->src_id);
|
||||
wire = wireToGlobal(srcrow, srccol, db, srcid);
|
||||
if (wires.count(wire) == 0) {
|
||||
addWire(wire, srcid, srccol, srcrow);
|
||||
}
|
||||
addBelInput(belname, id_FCLK, wire);
|
||||
}
|
||||
} else {
|
||||
addBelInput(belname, id_FCLK, idf("R%dC%d_%s", row + 1, col + 1, portname.c_str(this)));
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -2312,6 +2462,7 @@ void Arch::fix_pll_nets(Context *ctx)
|
||||
// mark with hclk is used
|
||||
void Arch::mark_used_hclk(Context *ctx)
|
||||
{
|
||||
pool<IdString> aux_cells;
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo *ci = cell.second.get();
|
||||
if (ci->type != id_IOLOGIC) {
|
||||
@ -2322,6 +2473,7 @@ void Arch::mark_used_hclk(Context *ctx)
|
||||
}
|
||||
// if it's an aux cell
|
||||
if (ci->attrs.count(id_IOLOGIC_MASTER_CELL)) {
|
||||
aux_cells.insert(ci->name);
|
||||
continue;
|
||||
}
|
||||
ci->setAttr(id_IOLOGIC_FCLK, Property("UNKNOWN"));
|
||||
@ -2345,16 +2497,16 @@ void Arch::mark_used_hclk(Context *ctx)
|
||||
if (!checkPipAvail(pip)) {
|
||||
WireId src_wire = getPipSrcWire(pip);
|
||||
ci->setAttr(id_IOLOGIC_FCLK, Property(wire_info(src_wire).type.str(this)));
|
||||
if (ci->attrs.count(id_IOLOGIC_AUX_CELL)) {
|
||||
IdString aux_cell_name = ctx->id(ci->attrs[id_IOLOGIC_AUX_CELL].as_string());
|
||||
ctx->cells[aux_cell_name]->setAttr(id_IOLOGIC_FCLK,
|
||||
Property(wire_info(src_wire).type.str(this)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto acell : aux_cells) {
|
||||
IdString main_cell = ctx->id(ctx->cells.at(acell)->attrs.at(id_IOLOGIC_MASTER_CELL).as_string());
|
||||
Property &fclk = ctx->cells.at(main_cell)->attrs.at(id_IOLOGIC_FCLK);
|
||||
ctx->cells.at(acell)->setAttr(id_IOLOGIC_FCLK, fclk);
|
||||
}
|
||||
}
|
||||
|
||||
void Arch::pre_route(Context *ctx)
|
||||
|
@ -532,6 +532,8 @@ struct Arch : BaseArch<ArchRanges>
|
||||
namespace BelZ {
|
||||
enum
|
||||
{
|
||||
ioba_z = 0, // IOBA
|
||||
iobb_z = 1, // IOBB
|
||||
mux_0_z = 10, // start Z for the MUX2LUT5 bels
|
||||
oddr_0_z = 20, // XXX start Z for the ODDR bels
|
||||
lutram_0_z = 30, // start Z for the LUTRAM bels
|
||||
@ -542,7 +544,9 @@ enum
|
||||
pll_z = 289, // PLL
|
||||
pllvr_z = 290, // PLLVR
|
||||
iologic_z = 291, // IOLOGIC
|
||||
free_z = 293 // Must be the last, one can use z starting from this value, adjust accordingly.
|
||||
oser16_z = 293, // OSER16
|
||||
ides16_z = 294, // IDES16
|
||||
free_z = 295 // Must be the last, one can use z starting from this value, adjust accordingly.
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -106,6 +106,7 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
|
||||
new_cell->addOutput(id_CLKOUTD3);
|
||||
new_cell->addOutput(id_LOCK);
|
||||
} else if (type == id_IOLOGIC) {
|
||||
new_cell->addInput(id_FCLK);
|
||||
new_cell->addInput(id_PCLK);
|
||||
new_cell->addInput(id_RESET);
|
||||
} else if (type == id_DUMMY_CELL) {
|
||||
|
@ -767,8 +767,6 @@ 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,6 +774,7 @@ X(GW9_ALWAYS_LOW1)
|
||||
X(GW9C_ALWAYS_LOW0)
|
||||
X(GW9C_ALWAYS_LOW1)
|
||||
X(OBUF_TYPE)
|
||||
X(IBUF_TYPE)
|
||||
X(SBUF)
|
||||
X(DBUF)
|
||||
X(ODDR)
|
||||
@ -805,6 +804,20 @@ X(IOLOGIC_MASTER_CELL)
|
||||
X(IOLOGIC_AUX_CELL)
|
||||
X(D8)
|
||||
X(D9)
|
||||
X(D10)
|
||||
X(D11)
|
||||
X(D12)
|
||||
X(D13)
|
||||
X(D14)
|
||||
X(D15)
|
||||
X(Q8)
|
||||
X(Q9)
|
||||
X(Q10)
|
||||
X(Q11)
|
||||
X(Q12)
|
||||
X(Q13)
|
||||
X(Q14)
|
||||
X(Q15)
|
||||
|
||||
// Wide LUTs
|
||||
X(MUX2_LUT5)
|
||||
|
287
gowin/pack.cc
287
gowin/pack.cc
@ -817,12 +817,14 @@ 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_OSER16: /* fall-through*/
|
||||
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_IDES16: /* fall-through*/
|
||||
case ID_IVIDEO:
|
||||
return true;
|
||||
default:
|
||||
@ -869,6 +871,21 @@ static void reconnect_ides_outs(CellInfo *ci)
|
||||
}
|
||||
}
|
||||
|
||||
static void get_next_oser16_loc(std::string device, Loc &loc)
|
||||
{
|
||||
if (device == "GW1NSR-4C") {
|
||||
if (loc.y == 0) {
|
||||
++loc.x;
|
||||
} else {
|
||||
++loc.y;
|
||||
}
|
||||
} else {
|
||||
if (device == "GW1NR-9" || device == "GW1NR-9C") {
|
||||
++loc.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pack IO logic
|
||||
static void pack_iologic(Context *ctx)
|
||||
{
|
||||
@ -1054,7 +1071,7 @@ static void pack_iologic(Context *ctx)
|
||||
}
|
||||
} else {
|
||||
std::unique_ptr<CellInfo> dummy =
|
||||
create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_DUMMY_IOLOGIC_IO");
|
||||
create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_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));
|
||||
@ -1064,7 +1081,6 @@ static void pack_iologic(Context *ctx)
|
||||
|
||||
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->setAttr(id_IOLOGIC_TYPE, std::string("DUMMY"));
|
||||
aux_cell->setParam(ctx->id("OUTMODE"), std::string("DDRENABLE"));
|
||||
aux_cell->setAttr(ctx->id("IOLOGIC_MASTER_CELL"), ci->name.str(ctx));
|
||||
@ -1099,8 +1115,8 @@ static void pack_iologic(Context *ctx)
|
||||
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());
|
||||
log_error("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()) {
|
||||
@ -1124,19 +1140,16 @@ static void pack_iologic(Context *ctx)
|
||||
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"));
|
||||
ci->setAttr(id_IBUF_TYPE, std::string("DBUF"));
|
||||
use_diff_io = true;
|
||||
} else {
|
||||
ci->setAttr(id_OBUF_TYPE, std::string("SBUF"));
|
||||
ci->setAttr(id_IBUF_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);
|
||||
|
||||
@ -1158,7 +1171,7 @@ static void pack_iologic(Context *ctx)
|
||||
}
|
||||
} else {
|
||||
std::unique_ptr<CellInfo> dummy =
|
||||
create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_DUMMY_IOLOGIC_IO");
|
||||
create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_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));
|
||||
@ -1168,7 +1181,6 @@ static void pack_iologic(Context *ctx)
|
||||
|
||||
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->setAttr(id_IOLOGIC_TYPE, std::string("DUMMY"));
|
||||
aux_cell->setParam(ctx->id("INMODE"), std::string("DDRENABLE"));
|
||||
aux_cell->setAttr(ctx->id("IOLOGIC_MASTER_CELL"), ci->name.str(ctx));
|
||||
@ -1183,6 +1195,259 @@ static void pack_iologic(Context *ctx)
|
||||
}
|
||||
ci->type = id_IOLOGIC;
|
||||
} break;
|
||||
case ID_OSER16: {
|
||||
IdString output = id_Q;
|
||||
q0_dst = net_only_drives(ctx, ci->ports.at(output).net, is_iob, id_I);
|
||||
NPNR_ASSERT(q0_dst != nullptr);
|
||||
|
||||
auto iob_bel = q0_dst->attrs.find(id_BEL);
|
||||
if (iob_bel == q0_dst->attrs.end()) {
|
||||
log_error("No constraints for %s. The pins for IDES/OSER must be specified explicitly.\n",
|
||||
ctx->nameOf(q0_dst));
|
||||
}
|
||||
|
||||
Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(iob_bel->second.as_string()));
|
||||
if (loc.z != BelZ::ioba_z) {
|
||||
log_error("IDES16/OSER16 %s must be an A pin.\n", ctx->nameOf(ci));
|
||||
}
|
||||
|
||||
loc.z = BelZ::oser16_z;
|
||||
ci->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
|
||||
BelId bel = ctx->getBelByLocation(loc);
|
||||
if (bel == BelId()) {
|
||||
log_error("No bel for %s at %s. Can't place IDES/OSER here\n", ctx->nameOf(ci),
|
||||
iob_bel->second.as_string().c_str());
|
||||
}
|
||||
|
||||
bool use_diff_io = false;
|
||||
if (q0_dst->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 Q output: it is wired internally
|
||||
delete_nets.insert(ci->ports.at(output).net->name);
|
||||
q0_dst->disconnectPort(id_I);
|
||||
ci->disconnectPort(output);
|
||||
loc.z = BelZ::ioba_z;
|
||||
if (ctx->bels.at(ctx->getBelByLocation(loc)).pins.count(id_GW9C_ALWAYS_LOW1)) {
|
||||
q0_dst->disconnectPort(id_GW9C_ALWAYS_LOW1);
|
||||
q0_dst->connectPort(id_GW9C_ALWAYS_LOW1, ctx->nets[ctx->id("$PACKER_VCC_NET")].get());
|
||||
}
|
||||
if (ctx->bels.at(ctx->getBelByLocation(loc)).pins.count(id_GW9_ALWAYS_LOW0)) {
|
||||
q0_dst->disconnectPort(id_GW9_ALWAYS_LOW0);
|
||||
q0_dst->connectPort(id_GW9_ALWAYS_LOW0, ctx->nets[ctx->id("$PACKER_VCC_NET")].get());
|
||||
}
|
||||
|
||||
// make aux cells
|
||||
std::unique_ptr<CellInfo> dummy =
|
||||
create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_IOLOGIC_IO");
|
||||
loc.z = BelZ::iobb_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;
|
||||
|
||||
// main iologic cell
|
||||
std::string master_name = ci->name.str(ctx) + "_MAIN";
|
||||
|
||||
// aux cells
|
||||
std::unique_ptr<CellInfo> aux_cell = create_generic_cell(ctx, id_IOLOGIC, ci->name.str(ctx) + "_AUX0");
|
||||
aux_cell->setAttr(id_IOLOGIC_TYPE, std::string("OSER16"));
|
||||
aux_cell->setAttr(ctx->id("IOLOGIC_MASTER_CELL"), master_name);
|
||||
aux_cell->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
|
||||
aux_cell->setParam(ctx->id("OUTMODE"), std::string("ODDRX8"));
|
||||
aux_cell->setParam(ctx->id("UPDATE"), std::string("SAME"));
|
||||
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));
|
||||
|
||||
// aux iologic cells
|
||||
loc.z = BelZ::iologic_z + 1;
|
||||
aux_cell = create_generic_cell(ctx, id_IOLOGIC, ci->name.str(ctx) + "_AUX1");
|
||||
aux_cell->setAttr(id_IOLOGIC_TYPE, std::string("DUMMY"));
|
||||
aux_cell->setAttr(ctx->id("IOLOGIC_MASTER_CELL"), master_name);
|
||||
aux_cell->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
|
||||
aux_cell->setParam(ctx->id("OUTMODE"), std::string("DDRENABLE16"));
|
||||
aux_cell->setParam(ctx->id("UPDATE"), std::string("SAME"));
|
||||
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));
|
||||
|
||||
// master
|
||||
get_next_oser16_loc(ctx->device, loc);
|
||||
loc.z = BelZ::iologic_z;
|
||||
aux_cell = create_generic_cell(ctx, id_IOLOGIC, master_name);
|
||||
aux_cell->setAttr(id_IOLOGIC_TYPE, std::string("DUMMY"));
|
||||
aux_cell->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
|
||||
aux_cell->setParam(ctx->id("OUTMODE"), std::string("DDRENABLE16"));
|
||||
aux_cell->setParam(ctx->id("UPDATE"), std::string("SAME"));
|
||||
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);
|
||||
}
|
||||
ci->movePortTo(id_FCLK, aux_cell.get(), id_FCLK);
|
||||
ci->movePortTo(id_D12, aux_cell.get(), id_D0);
|
||||
ci->movePortTo(id_D13, aux_cell.get(), id_D1);
|
||||
ci->movePortTo(id_D14, aux_cell.get(), id_D2);
|
||||
ci->movePortTo(id_D15, aux_cell.get(), id_D3);
|
||||
new_cells.push_back(std::move(aux_cell));
|
||||
|
||||
// bottom row is special and may need two additional ports
|
||||
loc.z = BelZ::ioba_z;
|
||||
if (ctx->getBelByLocation(loc) != BelId()) {
|
||||
dummy = create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_IOLOGIC_IO0");
|
||||
dummy->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
|
||||
new_cells.push_back(std::move(dummy));
|
||||
}
|
||||
|
||||
// XXX Prohibit the use of 4th IO and IOLOGIC
|
||||
loc.z = BelZ::iobb_z;
|
||||
if (ctx->getBelByLocation(loc) != BelId()) {
|
||||
dummy = create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_IOLOGIC_IO1");
|
||||
dummy->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
|
||||
new_cells.push_back(std::move(dummy));
|
||||
}
|
||||
master_name = ci->name.str(ctx) + "_AUX2";
|
||||
loc.z = BelZ::iologic_z + 1;
|
||||
dummy = create_generic_cell(ctx, id_DUMMY_CELL, master_name);
|
||||
dummy->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
|
||||
new_cells.push_back(std::move(dummy));
|
||||
} break;
|
||||
case ID_IDES16: {
|
||||
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()));
|
||||
if (loc.z != BelZ::ioba_z) {
|
||||
log_error("IDES16/OSER16 %s must be an A pin.\n", ctx->nameOf(ci));
|
||||
}
|
||||
|
||||
loc.z += BelZ::ides16_z;
|
||||
ci->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
|
||||
BelId bel = ctx->getBelByLocation(loc);
|
||||
if (bel == BelId()) {
|
||||
log_error("No bel for %s at %s. Can't place IDES/OSER here\n", ctx->nameOf(ci),
|
||||
iob_bel->second.as_string().c_str());
|
||||
}
|
||||
|
||||
bool use_diff_io = false;
|
||||
if (d_src->attrs.count(id_DIFF_TYPE)) {
|
||||
ci->setAttr(id_IBUF_TYPE, std::string("DBUF"));
|
||||
use_diff_io = true;
|
||||
} else {
|
||||
ci->setAttr(id_IBUF_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);
|
||||
ci->setAttr(id_IOLOGIC_TYPE, ci->type.str(ctx));
|
||||
|
||||
// make aux cells
|
||||
std::unique_ptr<CellInfo> dummy =
|
||||
create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_IOLOGIC_IO");
|
||||
loc.z = BelZ::iobb_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;
|
||||
|
||||
// main iologic cell
|
||||
std::string master_name = ci->name.str(ctx) + "_MAIN";
|
||||
|
||||
// aux cells
|
||||
std::unique_ptr<CellInfo> aux_cell = create_generic_cell(ctx, id_IOLOGIC, ci->name.str(ctx) + "_AUX0");
|
||||
aux_cell->setAttr(id_IOLOGIC_TYPE, std::string("IDES16"));
|
||||
aux_cell->setAttr(ctx->id("IOLOGIC_MASTER_CELL"), master_name);
|
||||
aux_cell->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
|
||||
aux_cell->setParam(ctx->id("INMODE"), std::string("IDDRX8"));
|
||||
aux_cell->setParam(ctx->id("UPDATE"), std::string("SAME"));
|
||||
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));
|
||||
|
||||
// aux iologic cells
|
||||
loc.z = BelZ::iologic_z + 1;
|
||||
aux_cell = create_generic_cell(ctx, id_IOLOGIC, ci->name.str(ctx) + "_AUX1");
|
||||
aux_cell->setAttr(id_IOLOGIC_TYPE, std::string("DUMMY"));
|
||||
aux_cell->setAttr(ctx->id("IOLOGIC_MASTER_CELL"), master_name);
|
||||
aux_cell->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
|
||||
aux_cell->setParam(ctx->id("INMODE"), std::string("DDRENABLE16"));
|
||||
aux_cell->setParam(ctx->id("UPDATE"), std::string("SAME"));
|
||||
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));
|
||||
|
||||
// master
|
||||
get_next_oser16_loc(ctx->device, loc);
|
||||
loc.z = BelZ::iologic_z;
|
||||
aux_cell = create_generic_cell(ctx, id_IOLOGIC, master_name);
|
||||
aux_cell->setAttr(id_IOLOGIC_TYPE, std::string("DUMMY"));
|
||||
aux_cell->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
|
||||
aux_cell->setParam(ctx->id("INMODE"), std::string("DDRENABLE16"));
|
||||
aux_cell->setParam(ctx->id("UPDATE"), std::string("SAME"));
|
||||
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);
|
||||
}
|
||||
ci->movePortTo(id_FCLK, aux_cell.get(), id_FCLK);
|
||||
ci->movePortTo(id_Q0, aux_cell.get(), id_Q6);
|
||||
ci->movePortTo(id_Q1, aux_cell.get(), id_Q7);
|
||||
ci->movePortTo(id_Q2, aux_cell.get(), id_Q8);
|
||||
ci->movePortTo(id_Q3, aux_cell.get(), id_Q9);
|
||||
new_cells.push_back(std::move(aux_cell));
|
||||
|
||||
// bottom row is special and may need two additional ports
|
||||
loc.z = BelZ::ioba_z;
|
||||
if (ctx->getBelByLocation(loc) != BelId()) {
|
||||
dummy = create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_IOLOGIC_IO0");
|
||||
dummy->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
|
||||
new_cells.push_back(std::move(dummy));
|
||||
}
|
||||
|
||||
// XXX Prohibit the use of 4th IO and IOLOGIC
|
||||
loc.z = BelZ::iobb_z;
|
||||
if (ctx->getBelByLocation(loc) != BelId()) {
|
||||
dummy = create_generic_cell(ctx, id_DUMMY_CELL, ci->name.str(ctx) + "_IOLOGIC_IO1");
|
||||
dummy->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
|
||||
new_cells.push_back(std::move(dummy));
|
||||
}
|
||||
master_name = ci->name.str(ctx) + "_AUX2";
|
||||
loc.z = BelZ::iologic_z + 1;
|
||||
dummy = create_generic_cell(ctx, id_DUMMY_CELL, master_name);
|
||||
dummy->setAttr(id_BEL, ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx));
|
||||
new_cells.push_back(std::move(dummy));
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user