Merge pull request #340 from YosysHQ/dave/ecp5_io
ecp5: IOLOGIC improvements
This commit is contained in:
commit
ee769420e3
@ -119,7 +119,16 @@ inline const NetInfo *get_net_or_empty(const CellInfo *cell, const IdString port
|
|||||||
return found->second.net;
|
return found->second.net;
|
||||||
else
|
else
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
inline NetInfo *get_net_or_empty(CellInfo *cell, const IdString port)
|
||||||
|
{
|
||||||
|
auto found = cell->ports.find(port);
|
||||||
|
if (found != cell->ports.end())
|
||||||
|
return found->second.net;
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
@ -889,6 +889,10 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
|
|||||||
if (cell->ports.at(port).name == id_STOP)
|
if (cell->ports.at(port).name == id_STOP)
|
||||||
return TMG_ENDPOINT;
|
return TMG_ENDPOINT;
|
||||||
return (cell->ports.at(port).type == PORT_OUT) ? TMG_COMB_OUTPUT : TMG_COMB_INPUT;
|
return (cell->ports.at(port).type == PORT_OUT) ? TMG_COMB_OUTPUT : TMG_COMB_INPUT;
|
||||||
|
} else if (cell->type == id_ECLKBRIDGECS) {
|
||||||
|
if (cell->ports.at(port).name == id_SEL)
|
||||||
|
return TMG_ENDPOINT;
|
||||||
|
return (cell->ports.at(port).type == PORT_OUT) ? TMG_COMB_OUTPUT : TMG_COMB_INPUT;
|
||||||
} else {
|
} else {
|
||||||
log_error("cell type '%s' is unsupported (instantiated as '%s')\n", cell->type.c_str(this),
|
log_error("cell type '%s' is unsupported (instantiated as '%s')\n", cell->type.c_str(this),
|
||||||
cell->name.c_str(this));
|
cell->name.c_str(this));
|
||||||
|
@ -888,9 +888,15 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
|
|||||||
std::string datamux_oddr = str_or_default(ci->params, ctx->id("DATAMUX_ODDR"), "PADDO");
|
std::string datamux_oddr = str_or_default(ci->params, ctx->id("DATAMUX_ODDR"), "PADDO");
|
||||||
if (datamux_oddr != "PADDO")
|
if (datamux_oddr != "PADDO")
|
||||||
cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_ODDR", datamux_oddr);
|
cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_ODDR", datamux_oddr);
|
||||||
|
std::string datamux_oreg = str_or_default(ci->params, ctx->id("DATAMUX_OREG"), "PADDO");
|
||||||
|
if (datamux_oreg != "PADDO")
|
||||||
|
cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_OREG", datamux_oreg);
|
||||||
std::string datamux_mddr = str_or_default(ci->params, ctx->id("DATAMUX_MDDR"), "PADDO");
|
std::string datamux_mddr = str_or_default(ci->params, ctx->id("DATAMUX_MDDR"), "PADDO");
|
||||||
if (datamux_mddr != "PADDO")
|
if (datamux_mddr != "PADDO")
|
||||||
cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_MDDR", datamux_mddr);
|
cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_MDDR", datamux_mddr);
|
||||||
|
std::string trimux_tsreg = str_or_default(ci->params, ctx->id("TRIMUX_TSREG"), "PADDT");
|
||||||
|
if (trimux_tsreg != "PADDT")
|
||||||
|
cc.tiles[pic_tile].add_enum(pio + ".TRIMUX_TSREG", trimux_tsreg);
|
||||||
} else if (ci->type == ctx->id("DCCA")) {
|
} else if (ci->type == ctx->id("DCCA")) {
|
||||||
const NetInfo *cen = get_net_or_empty(ci, ctx->id("CE"));
|
const NetInfo *cen = get_net_or_empty(ci, ctx->id("CE"));
|
||||||
if (cen != nullptr) {
|
if (cen != nullptr) {
|
||||||
@ -1367,6 +1373,13 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
|
|||||||
std::string tile = ctx->getTileByType(std::string("ECLK_") + (r ? "R" : "L"));
|
std::string tile = ctx->getTileByType(std::string("ECLK_") + (r ? "R" : "L"));
|
||||||
if (get_net_or_empty(ci, id_STOP) != nullptr)
|
if (get_net_or_empty(ci, id_STOP) != nullptr)
|
||||||
cc.tiles[tile].add_enum(eclksync + ".MODE", "ECLKSYNCB");
|
cc.tiles[tile].add_enum(eclksync + ".MODE", "ECLKSYNCB");
|
||||||
|
} else if (ci->type == id_ECLKBRIDGECS) {
|
||||||
|
Loc loc = ctx->getBelLocation(ci->bel);
|
||||||
|
bool r = loc.x > 5;
|
||||||
|
std::string eclkb = ctx->locInfo(bel)->bel_data[bel.index].name.get();
|
||||||
|
std::string tile = ctx->getTileByType(std::string("ECLK_") + (r ? "R" : "L"));
|
||||||
|
if (get_net_or_empty(ci, id_STOP) != nullptr)
|
||||||
|
cc.tiles[tile].add_enum(eclkb + ".MODE", "ECLKBRIDGECS");
|
||||||
} else if (ci->type == id_DDRDLL) {
|
} else if (ci->type == id_DDRDLL) {
|
||||||
Loc loc = ctx->getBelLocation(ci->bel);
|
Loc loc = ctx->getBelLocation(ci->bel);
|
||||||
bool u = loc.y<15, r = loc.x> 15;
|
bool u = loc.y<15, r = loc.x> 15;
|
||||||
|
@ -1181,6 +1181,7 @@ X(RDPNTR2)
|
|||||||
X(WRPNTR0)
|
X(WRPNTR0)
|
||||||
X(WRPNTR1)
|
X(WRPNTR1)
|
||||||
X(WRPNTR2)
|
X(WRPNTR2)
|
||||||
|
X(SLIP)
|
||||||
|
|
||||||
X(GSR)
|
X(GSR)
|
||||||
|
|
||||||
@ -1289,3 +1290,7 @@ X(MULT18X18D_REGS_NONE)
|
|||||||
X(MULT18X18D_REGS_OUTPUT)
|
X(MULT18X18D_REGS_OUTPUT)
|
||||||
X(MULT18X18D_REGS_PIPELINE)
|
X(MULT18X18D_REGS_PIPELINE)
|
||||||
X(P)
|
X(P)
|
||||||
|
|
||||||
|
X(ECLKBRIDGECS)
|
||||||
|
X(SEL)
|
||||||
|
X(ECSOUT)
|
||||||
|
191
ecp5/pack.cc
191
ecp5/pack.cc
@ -1560,6 +1560,7 @@ class Ecp5Packer
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::map<std::pair<int, int>, EdgeClockInfo> eclks;
|
std::map<std::pair<int, int>, EdgeClockInfo> eclks;
|
||||||
|
std::map<NetInfo *, int> bridge_side_hint;
|
||||||
|
|
||||||
void make_eclk(PortInfo &usr_port, CellInfo *usr_cell, BelId usr_bel, int bank)
|
void make_eclk(PortInfo &usr_port, CellInfo *usr_cell, BelId usr_bel, int bank)
|
||||||
{
|
{
|
||||||
@ -1575,6 +1576,8 @@ class Ecp5Packer
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (free_eclk == -1) {
|
} else if (free_eclk == -1) {
|
||||||
|
if (bridge_side_hint.count(ecknet) && bridge_side_hint.at(ecknet) != i)
|
||||||
|
continue;
|
||||||
free_eclk = i;
|
free_eclk = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1844,6 +1847,8 @@ class Ecp5Packer
|
|||||||
|
|
||||||
auto set_iologic_mode = [&](CellInfo *iol, std::string mode) {
|
auto set_iologic_mode = [&](CellInfo *iol, std::string mode) {
|
||||||
auto &curr_mode = iol->params[ctx->id("MODE")].str;
|
auto &curr_mode = iol->params[ctx->id("MODE")].str;
|
||||||
|
if (curr_mode != "NONE" && mode == "IREG_OREG")
|
||||||
|
return;
|
||||||
if (curr_mode != "NONE" && curr_mode != "IREG_OREG" && curr_mode != mode)
|
if (curr_mode != "NONE" && curr_mode != "IREG_OREG" && curr_mode != mode)
|
||||||
log_error("IOLOGIC '%s' has conflicting modes '%s' and '%s'\n", iol->name.c_str(ctx), curr_mode.c_str(),
|
log_error("IOLOGIC '%s' has conflicting modes '%s' and '%s'\n", iol->name.c_str(ctx), curr_mode.c_str(),
|
||||||
mode.c_str());
|
mode.c_str());
|
||||||
@ -2044,10 +2049,10 @@ class Ecp5Packer
|
|||||||
replace_port(ci, ctx->id("D1"), iol, id_TXDATA1);
|
replace_port(ci, ctx->id("D1"), iol, id_TXDATA1);
|
||||||
iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED");
|
iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED");
|
||||||
packed_cells.insert(cell.first);
|
packed_cells.insert(cell.first);
|
||||||
} else if (ci->type == ctx->id("ODDRX2F")) {
|
} else if (ci->type == ctx->id("ODDRX2F") || ci->type == ctx->id("ODDR71B")) {
|
||||||
CellInfo *pio = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_I, true);
|
CellInfo *pio = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_I, true);
|
||||||
if (pio == nullptr)
|
if (pio == nullptr)
|
||||||
log_error("ODDRX2F '%s' Q output must be connected only to a top level output\n",
|
log_error("%s '%s' Q output must be connected only to a top level output\n", ci->type.c_str(ctx),
|
||||||
ci->name.c_str(ctx));
|
ci->name.c_str(ctx));
|
||||||
CellInfo *iol;
|
CellInfo *iol;
|
||||||
if (pio_iologic.count(pio->name))
|
if (pio_iologic.count(pio->name))
|
||||||
@ -2070,14 +2075,25 @@ class Ecp5Packer
|
|||||||
replace_port(ci, ctx->id("D1"), iol, id_TXDATA1);
|
replace_port(ci, ctx->id("D1"), iol, id_TXDATA1);
|
||||||
replace_port(ci, ctx->id("D2"), iol, id_TXDATA2);
|
replace_port(ci, ctx->id("D2"), iol, id_TXDATA2);
|
||||||
replace_port(ci, ctx->id("D3"), iol, id_TXDATA3);
|
replace_port(ci, ctx->id("D3"), iol, id_TXDATA3);
|
||||||
iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED");
|
if (ci->type == ctx->id("ODDR71B")) {
|
||||||
|
Loc loc =
|
||||||
|
ctx->getBelLocation(ctx->getBelByName(ctx->id(pio->attrs.at(ctx->id("BEL")).as_string())));
|
||||||
|
if (loc.z % 2 == 1)
|
||||||
|
log_error("ODDR71B '%s' can only be used at 'A' or 'C' locations\n", ci->name.c_str(ctx));
|
||||||
|
replace_port(ci, ctx->id("D4"), iol, id_TXDATA4);
|
||||||
|
replace_port(ci, ctx->id("D5"), iol, id_TXDATA5);
|
||||||
|
replace_port(ci, ctx->id("D6"), iol, id_TXDATA6);
|
||||||
|
iol->params[ctx->id("ODDRXN.MODE")] = std::string("ODDR71");
|
||||||
|
} else {
|
||||||
iol->params[ctx->id("ODDRXN.MODE")] = std::string("ODDRX2");
|
iol->params[ctx->id("ODDRXN.MODE")] = std::string("ODDRX2");
|
||||||
|
}
|
||||||
|
iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED");
|
||||||
pio->params[ctx->id("DATAMUX_ODDR")] = std::string("IOLDO");
|
pio->params[ctx->id("DATAMUX_ODDR")] = std::string("IOLDO");
|
||||||
packed_cells.insert(cell.first);
|
packed_cells.insert(cell.first);
|
||||||
} else if (ci->type == ctx->id("IDDRX2F")) {
|
} else if (ci->type == ctx->id("IDDRX2F") || ci->type == ctx->id("IDDR71B")) {
|
||||||
CellInfo *pio = net_driven_by(ctx, ci->ports.at(ctx->id("D")).net, is_trellis_io, id_O);
|
CellInfo *pio = net_driven_by(ctx, ci->ports.at(ctx->id("D")).net, is_trellis_io, id_O);
|
||||||
if (pio == nullptr || ci->ports.at(ctx->id("D")).net->users.size() > 1)
|
if (pio == nullptr || ci->ports.at(ctx->id("D")).net->users.size() > 1)
|
||||||
log_error("IDDRX2F '%s' D input must be connected only to a top level input\n",
|
log_error("%s '%s' D input must be connected only to a top level input\n", ci->type.c_str(ctx),
|
||||||
ci->name.c_str(ctx));
|
ci->name.c_str(ctx));
|
||||||
CellInfo *iol;
|
CellInfo *iol;
|
||||||
if (pio_iologic.count(pio->name))
|
if (pio_iologic.count(pio->name))
|
||||||
@ -2093,8 +2109,20 @@ class Ecp5Packer
|
|||||||
replace_port(ci, ctx->id("Q1"), iol, id_RXDATA1);
|
replace_port(ci, ctx->id("Q1"), iol, id_RXDATA1);
|
||||||
replace_port(ci, ctx->id("Q2"), iol, id_RXDATA2);
|
replace_port(ci, ctx->id("Q2"), iol, id_RXDATA2);
|
||||||
replace_port(ci, ctx->id("Q3"), iol, id_RXDATA3);
|
replace_port(ci, ctx->id("Q3"), iol, id_RXDATA3);
|
||||||
iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED");
|
if (ci->type == ctx->id("IDDR71B")) {
|
||||||
|
Loc loc =
|
||||||
|
ctx->getBelLocation(ctx->getBelByName(ctx->id(pio->attrs.at(ctx->id("BEL")).as_string())));
|
||||||
|
if (loc.z % 2 == 1)
|
||||||
|
log_error("IDDR71B '%s' can only be used at 'A' or 'C' locations\n", ci->name.c_str(ctx));
|
||||||
|
replace_port(ci, ctx->id("Q4"), iol, id_RXDATA4);
|
||||||
|
replace_port(ci, ctx->id("Q5"), iol, id_RXDATA5);
|
||||||
|
replace_port(ci, ctx->id("Q6"), iol, id_RXDATA6);
|
||||||
|
replace_port(ci, ctx->id("ALIGNWD"), iol, id_SLIP);
|
||||||
|
iol->params[ctx->id("IDDRXN.MODE")] = std::string("IDDR71");
|
||||||
|
} else {
|
||||||
iol->params[ctx->id("IDDRXN.MODE")] = std::string("IDDRX2");
|
iol->params[ctx->id("IDDRXN.MODE")] = std::string("IDDRX2");
|
||||||
|
}
|
||||||
|
iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED");
|
||||||
packed_cells.insert(cell.first);
|
packed_cells.insert(cell.first);
|
||||||
} else if (ci->type == ctx->id("OSHX2A")) {
|
} else if (ci->type == ctx->id("OSHX2A")) {
|
||||||
CellInfo *pio = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_I, true);
|
CellInfo *pio = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_I, true);
|
||||||
@ -2217,9 +2245,148 @@ class Ecp5Packer
|
|||||||
std::string(ci->type == ctx->id("TSHX2DQSA") ? "DQSW" : "DQSW270");
|
std::string(ci->type == ctx->id("TSHX2DQSA") ? "DQSW" : "DQSW270");
|
||||||
iol->params[ctx->id("IOLTOMUX")] = std::string("TDDR");
|
iol->params[ctx->id("IOLTOMUX")] = std::string("TDDR");
|
||||||
packed_cells.insert(cell.first);
|
packed_cells.insert(cell.first);
|
||||||
|
} else if (ci->type == ctx->id("TRELLIS_FF") && bool_or_default(ci->attrs, ctx->id("syn_useioff"))) {
|
||||||
|
// Pack IO flipflop into IOLOGIC
|
||||||
|
std::string mode = str_or_default(ci->attrs, ctx->id("ioff_dir"), "");
|
||||||
|
if (mode != "output") {
|
||||||
|
// See if it can be packed as an input ff
|
||||||
|
NetInfo *d = get_net_or_empty(ci, ctx->id("DI"));
|
||||||
|
CellInfo *pio = net_driven_by(ctx, d, is_trellis_io, id_O);
|
||||||
|
if (pio != nullptr && d->users.size() == 1) {
|
||||||
|
// Input FF
|
||||||
|
CellInfo *iol;
|
||||||
|
if (pio_iologic.count(pio->name))
|
||||||
|
iol = pio_iologic.at(pio->name);
|
||||||
|
else
|
||||||
|
iol = create_pio_iologic(pio, ci);
|
||||||
|
set_iologic_mode(iol, "IREG_OREG");
|
||||||
|
set_iologic_sclk(iol, ci, ctx->id("CLK"), true);
|
||||||
|
set_iologic_lsr(iol, ci, ctx->id("LSR"), true);
|
||||||
|
// Handle CLK and CE muxes
|
||||||
|
if (str_or_default(ci->params, ctx->id("CLKMUX")) == "INV")
|
||||||
|
iol->params[ctx->id("CLKIMUX")] = std::string("INV");
|
||||||
|
if (str_or_default(ci->params, ctx->id("CEMUX"), "CE") == "CE") {
|
||||||
|
iol->params[ctx->id("CEIMUX")] = std::string("CEMUX");
|
||||||
|
iol->params[ctx->id("CEMUX")] = std::string("CE");
|
||||||
|
if (get_net_or_empty(ci, ctx->id("CE")) == nullptr)
|
||||||
|
replace_port(ci, ctx->id("CE"), iol, ctx->id("CE"));
|
||||||
|
else
|
||||||
|
disconnect_port(ctx, ci, ctx->id("CE"));
|
||||||
|
} else {
|
||||||
|
iol->params[ctx->id("CEIMUX")] = std::string("1");
|
||||||
|
}
|
||||||
|
// Set IOLOGIC params from FF params
|
||||||
|
iol->params[ctx->id("FF.INREGMODE")] = std::string("FF");
|
||||||
|
iol->params[ctx->id("FF.REGSET")] = str_or_default(ci->params, ctx->id("REGSET"), "RESET");
|
||||||
|
iol->params[ctx->id("SRMODE")] = str_or_default(ci->params, ctx->id("SRMODE"), "ASYNC");
|
||||||
|
iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED");
|
||||||
|
replace_port(ci, ctx->id("DI"), iol, id_PADDI);
|
||||||
|
replace_port(ci, ctx->id("Q"), iol, id_INFF);
|
||||||
|
packed_cells.insert(cell.first);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mode != "input") {
|
||||||
|
CellInfo *pio_t = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_T, true);
|
||||||
|
CellInfo *pio_i = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_I, true);
|
||||||
|
if (pio_t != nullptr || pio_i != nullptr) {
|
||||||
|
// Output or tristate FF
|
||||||
|
bool tri = (pio_t != nullptr);
|
||||||
|
CellInfo *pio = tri ? pio_t : pio_i;
|
||||||
|
CellInfo *iol;
|
||||||
|
if (pio_iologic.count(pio->name))
|
||||||
|
iol = pio_iologic.at(pio->name);
|
||||||
|
else
|
||||||
|
iol = create_pio_iologic(pio, ci);
|
||||||
|
set_iologic_mode(iol, "IREG_OREG");
|
||||||
|
// Connection between FF and PIO
|
||||||
|
replace_port(ci, ctx->id("Q"), iol, tri ? id_IOLTO : id_IOLDO);
|
||||||
|
if (tri) {
|
||||||
|
if (!pio->ports.count(id_IOLTO)) {
|
||||||
|
pio->ports[id_IOLTO].name = id_IOLTO;
|
||||||
|
pio->ports[id_IOLTO].type = PORT_IN;
|
||||||
|
}
|
||||||
|
pio->params[ctx->id("TRIMUX_TSREG")] = std::string("IOLTO");
|
||||||
|
replace_port(pio, id_T, pio, id_IOLTO);
|
||||||
|
} else {
|
||||||
|
if (!pio->ports.count(id_IOLDO)) {
|
||||||
|
pio->ports[id_IOLDO].name = id_IOLDO;
|
||||||
|
pio->ports[id_IOLDO].type = PORT_IN;
|
||||||
|
}
|
||||||
|
pio->params[ctx->id("DATAMUX_OREG")] = std::string("IOLDO");
|
||||||
|
replace_port(pio, id_I, pio, id_IOLDO);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_iologic_sclk(iol, ci, ctx->id("CLK"), false);
|
||||||
|
set_iologic_lsr(iol, ci, ctx->id("LSR"), false);
|
||||||
|
|
||||||
|
// Handle CLK and CE muxes
|
||||||
|
if (str_or_default(ci->params, ctx->id("CLKMUX")) == "INV")
|
||||||
|
iol->params[ctx->id("CLKOMUX")] = std::string("INV");
|
||||||
|
if (str_or_default(ci->params, ctx->id("CEMUX"), "CE") == "CE") {
|
||||||
|
iol->params[ctx->id("CEOMUX")] = std::string("CEMUX");
|
||||||
|
iol->params[ctx->id("CEMUX")] = std::string("CE");
|
||||||
|
if (get_net_or_empty(ci, ctx->id("CE")) == nullptr)
|
||||||
|
replace_port(ci, ctx->id("CE"), iol, ctx->id("CE"));
|
||||||
|
else
|
||||||
|
disconnect_port(ctx, ci, ctx->id("CE"));
|
||||||
|
} else {
|
||||||
|
iol->params[ctx->id("CEOMUX")] = std::string("1");
|
||||||
|
}
|
||||||
|
// FF params
|
||||||
|
iol->params[ctx->id(tri ? "TSREG.OUTREGMODE" : "OUTREG.OUTREGMODE")] = std::string("FF");
|
||||||
|
iol->params[ctx->id(tri ? "TSREG.REGSET" : "OUTREG.REGSET")] =
|
||||||
|
str_or_default(ci->params, ctx->id("REGSET"), "RESET");
|
||||||
|
iol->params[ctx->id("SRMODE")] = str_or_default(ci->params, ctx->id("SRMODE"), "ASYNC");
|
||||||
|
// Data input
|
||||||
|
replace_port(ci, ctx->id("DI"), iol, tri ? id_TSDATA0 : id_TXDATA0);
|
||||||
|
iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED");
|
||||||
|
packed_cells.insert(cell.first);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_error("Failed to pack flipflop '%s' with 'syn_useioff' set into IOLOGIC.\n", ci->name.c_str(ctx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flush_cells();
|
flush_cells();
|
||||||
|
// Constrain ECLK-related cells
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (ci->type == id_ECLKBRIDGECS) {
|
||||||
|
NetInfo *i0 = get_net_or_empty(ci, id_CLK0), *i1 = get_net_or_empty(ci, id_CLK1),
|
||||||
|
*o = get_net_or_empty(ci, id_ECSOUT);
|
||||||
|
for (NetInfo *input : {i0, i1}) {
|
||||||
|
if (input == nullptr)
|
||||||
|
continue;
|
||||||
|
for (auto user : input->users) {
|
||||||
|
if (!user.cell->attrs.count(ctx->id("BEL")))
|
||||||
|
continue;
|
||||||
|
Loc user_loc = ctx->getBelLocation(
|
||||||
|
ctx->getBelByName(ctx->id(user.cell->attrs.at(ctx->id("BEL")).as_string())));
|
||||||
|
for (auto bel : ctx->getBels()) {
|
||||||
|
if (ctx->getBelType(bel) != id_ECLKBRIDGECS)
|
||||||
|
continue;
|
||||||
|
Loc loc = ctx->getBelLocation(bel);
|
||||||
|
if (loc.x == user_loc.x) {
|
||||||
|
ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
|
||||||
|
if (o != nullptr)
|
||||||
|
for (auto user2 : o->users) {
|
||||||
|
// Set side hint to ensure edge clock choice is routeable
|
||||||
|
if (user2.cell->type == id_ECLKSYNCB && user2.port == id_ECLKI) {
|
||||||
|
NetInfo *synco = get_net_or_empty(user2.cell, id_ECLKO);
|
||||||
|
if (synco != nullptr)
|
||||||
|
bridge_side_hint[synco] = (loc.x > 1) ? 0 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
goto eclkbridge_done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eclkbridge_done:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Promote/route edge clocks
|
// Promote/route edge clocks
|
||||||
for (auto cell : sorted(ctx->cells)) {
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
CellInfo *ci = cell.second;
|
CellInfo *ci = cell.second;
|
||||||
@ -2240,7 +2407,6 @@ class Ecp5Packer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
flush_cells();
|
flush_cells();
|
||||||
// Constrain ECLK-related cells
|
|
||||||
for (auto cell : sorted(ctx->cells)) {
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
CellInfo *ci = cell.second;
|
CellInfo *ci = cell.second;
|
||||||
if (ci->type == id_CLKDIVF) {
|
if (ci->type == id_CLKDIVF) {
|
||||||
@ -2266,7 +2432,18 @@ class Ecp5Packer
|
|||||||
clkdiv_done:
|
clkdiv_done:
|
||||||
continue;
|
continue;
|
||||||
} else if (ci->type == id_ECLKSYNCB) {
|
} else if (ci->type == id_ECLKSYNCB) {
|
||||||
|
const NetInfo *eclki = net_or_nullptr(ci, id_ECLKI);
|
||||||
const NetInfo *eclko = net_or_nullptr(ci, id_ECLKO);
|
const NetInfo *eclko = net_or_nullptr(ci, id_ECLKO);
|
||||||
|
if (eclki != nullptr && eclki->driver.cell != nullptr) {
|
||||||
|
if (eclki->driver.cell->type == id_ECLKBRIDGECS) {
|
||||||
|
BelId bel =
|
||||||
|
ctx->getBelByName(ctx->id(eclki->driver.cell->attrs.at(ctx->id("BEL")).as_string()));
|
||||||
|
Loc loc = ctx->getBelLocation(bel);
|
||||||
|
ci->attrs[ctx->id("BEL")] =
|
||||||
|
ctx->getBelName(ctx->getBelByLocation(Loc(loc.x, loc.y, 15))).str(ctx);
|
||||||
|
goto eclksync_done;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (eclko == nullptr)
|
if (eclko == nullptr)
|
||||||
log_error("ECLKSYNCB '%s' has disconnected port ECLKO\n", ci->name.c_str(ctx));
|
log_error("ECLKSYNCB '%s' has disconnected port ECLKO\n", ci->name.c_str(ctx));
|
||||||
for (auto user : eclko->users) {
|
for (auto user : eclko->users) {
|
||||||
|
Loading…
Reference in New Issue
Block a user