ecp5: Add DELAYF/DELAYG support
Signed-off-by: David Shah <davey1576@gmail.com>
This commit is contained in:
parent
fd52db813f
commit
817ba5a4b9
@ -107,6 +107,8 @@ void disconnect_port(const Context *ctx, CellInfo *cell, IdString port_name)
|
||||
return user.cell == cell && user.port == port_name;
|
||||
}),
|
||||
port.net->users.end());
|
||||
if (port.net->driver.cell == cell && port.net->driver.port == port_name)
|
||||
port.net->driver.cell = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -790,18 +790,22 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
|
||||
cc.tiles[pio_tile].add_enum(pio + ".PULLMODE", str_or_default(ci->attrs, ctx->id("PULLMODE"), "NONE"));
|
||||
if (ci->attrs.count(ctx->id("TERMINATION"))) {
|
||||
auto vccio = get_vccio(ioType_from_str(iotype));
|
||||
switch(vccio) {
|
||||
case IOVoltage::VCC_1V8:
|
||||
cc.tiles[pio_tile].add_enum(pio + ".TERMINATION_1V8", str_or_default(ci->attrs, ctx->id("TERMINATION"), "OFF"));
|
||||
break;
|
||||
case IOVoltage::VCC_1V5:
|
||||
cc.tiles[pio_tile].add_enum(pio + ".TERMINATION_1V5", str_or_default(ci->attrs, ctx->id("TERMINATION"), "OFF"));
|
||||
break;
|
||||
case IOVoltage::VCC_1V35:
|
||||
cc.tiles[pio_tile].add_enum(pio + ".TERMINATION_1V35", str_or_default(ci->attrs, ctx->id("TERMINATION"), "OFF"));
|
||||
break;
|
||||
default:
|
||||
log_error("TERMINATION is not supported with Vcc = %s (on PIO %s)\n", iovoltage_to_str(vccio).c_str(), ci->name.c_str(ctx));
|
||||
switch (vccio) {
|
||||
case IOVoltage::VCC_1V8:
|
||||
cc.tiles[pio_tile].add_enum(pio + ".TERMINATION_1V8",
|
||||
str_or_default(ci->attrs, ctx->id("TERMINATION"), "OFF"));
|
||||
break;
|
||||
case IOVoltage::VCC_1V5:
|
||||
cc.tiles[pio_tile].add_enum(pio + ".TERMINATION_1V5",
|
||||
str_or_default(ci->attrs, ctx->id("TERMINATION"), "OFF"));
|
||||
break;
|
||||
case IOVoltage::VCC_1V35:
|
||||
cc.tiles[pio_tile].add_enum(pio + ".TERMINATION_1V35",
|
||||
str_or_default(ci->attrs, ctx->id("TERMINATION"), "OFF"));
|
||||
break;
|
||||
default:
|
||||
log_error("TERMINATION is not supported with Vcc = %s (on PIO %s)\n",
|
||||
iovoltage_to_str(vccio).c_str(), ci->name.c_str(ctx));
|
||||
}
|
||||
}
|
||||
std::string datamux_oddr = str_or_default(ci->params, ctx->id("DATAMUX_ODDR"), "PADDO");
|
||||
|
11
ecp5/cells.h
11
ecp5/cells.h
@ -46,6 +46,17 @@ inline bool is_pfumx(const BaseCtx *ctx, const CellInfo *cell) { return cell->ty
|
||||
|
||||
inline bool is_l6mux(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("L6MUX21"); }
|
||||
|
||||
inline bool is_iologic_input_cell(const BaseCtx *ctx, const CellInfo *cell)
|
||||
{
|
||||
return cell->type == ctx->id("IDDRX1F") || cell->type == ctx->id("IDDRX2F") || cell->type == ctx->id("IDDR71B") ||
|
||||
cell->type == ctx->id("IDDRX2DQA");
|
||||
}
|
||||
inline bool is_iologic_output_cell(const BaseCtx *ctx, const CellInfo *cell)
|
||||
{
|
||||
return cell->type == ctx->id("ODDRX1F") || cell->type == ctx->id("ODDRX2F") || cell->type == ctx->id("ODDR71B") ||
|
||||
cell->type == ctx->id("ODDRX2DQA") || cell->type == ctx->id("ODDRX2DQSB") || cell->type == ctx->id("OSHX2A");
|
||||
}
|
||||
|
||||
void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut);
|
||||
void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index);
|
||||
void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc);
|
||||
|
123
ecp5/pack.cc
123
ecp5/pack.cc
@ -1569,6 +1569,32 @@ class Ecp5Packer
|
||||
}
|
||||
}
|
||||
|
||||
int lookup_delay(const std::string &del_mode)
|
||||
{
|
||||
if (del_mode == "USER_DEFINED")
|
||||
return 0;
|
||||
else if (del_mode == "DQS_ALIGNED_X2")
|
||||
return 6;
|
||||
else if (del_mode == "DQS_CMD_CLK")
|
||||
return 9;
|
||||
else if (del_mode == "ECLK_ALIGNED")
|
||||
return 21;
|
||||
else if (del_mode == "ECLK_CENTERED")
|
||||
return 11;
|
||||
else if (del_mode == "ECLKBRIDGE_ALIGNED")
|
||||
return 39;
|
||||
else if (del_mode == "ECLKBRIDGE_CENTERED")
|
||||
return 29;
|
||||
else if (del_mode == "SCLK_ALIGNED")
|
||||
return 50;
|
||||
else if (del_mode == "SCLK_CENTERED")
|
||||
return 39;
|
||||
else if (del_mode == "SCLK_ZEROHOLD")
|
||||
return 59;
|
||||
else
|
||||
log_error("Unsupported DEL_MODE '%s'\n", del_mode.c_str());
|
||||
}
|
||||
|
||||
// Pack IOLOGIC
|
||||
void pack_iologic()
|
||||
{
|
||||
@ -1634,7 +1660,7 @@ class Ecp5Packer
|
||||
|
||||
auto set_iologic_mode = [&](CellInfo *iol, std::string mode) {
|
||||
auto &curr_mode = iol->params[ctx->id("MODE")];
|
||||
if (curr_mode != "NONE" && 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(),
|
||||
mode.c_str());
|
||||
if (iol->type == id_SIOLOGIC && mode != "IREG_OREG" && mode != "IDDRX1_ODDRX1" && mode != "NONE")
|
||||
@ -1707,6 +1733,101 @@ class Ecp5Packer
|
||||
}
|
||||
};
|
||||
|
||||
auto tie_zero = [&](CellInfo *ci, IdString port) {
|
||||
std::unique_ptr<CellInfo> zero_cell{new CellInfo};
|
||||
std::unique_ptr<NetInfo> zero_net{new NetInfo};
|
||||
IdString name = ctx->id(ci->name.str(ctx) + "$zero$" + port.str(ctx));
|
||||
zero_cell->type = ctx->id("GND");
|
||||
zero_cell->name = name;
|
||||
zero_net->name = name;
|
||||
zero_cell->ports[ctx->id("GND")].type = PORT_OUT;
|
||||
connect_port(ctx, zero_net.get(), zero_cell.get(), ctx->id("GND"));
|
||||
ctx->nets[name] = std::move(zero_net);
|
||||
new_cells.push_back(std::move(zero_cell));
|
||||
};
|
||||
|
||||
for (auto cell : sorted(ctx->cells)) {
|
||||
CellInfo *ci = cell.second;
|
||||
if (ci->type == ctx->id("DELAYF") || ci->type == ctx->id("DELAYG")) {
|
||||
CellInfo *i_pio = net_driven_by(ctx, ci->ports.at(ctx->id("A")).net, is_trellis_io, id_O);
|
||||
CellInfo *o_pio = net_only_drives(ctx, ci->ports.at(ctx->id("Z")).net, is_trellis_io, id_I, true);
|
||||
CellInfo *iol = nullptr;
|
||||
if (i_pio != nullptr && ci->ports.at(ctx->id("A")).net->users.size() == 1) {
|
||||
iol = create_pio_iologic(i_pio, ci);
|
||||
set_iologic_mode(iol, "IREG_OREG");
|
||||
bool drives_iologic = false;
|
||||
for (auto user : ci->ports.at(ctx->id("Z")).net->users)
|
||||
if (is_iologic_input_cell(ctx, user.cell) && user.port == ctx->id("D"))
|
||||
drives_iologic = true;
|
||||
if (drives_iologic) {
|
||||
// Reconnect to PIO which the packer expects later on
|
||||
NetInfo *input_net = ci->ports.at(ctx->id("A")).net, *dly_net = ci->ports.at(ctx->id("Z")).net;
|
||||
disconnect_port(ctx, i_pio, id_O);
|
||||
i_pio->ports.at(id_O).net = nullptr;
|
||||
disconnect_port(ctx, ci, id_A);
|
||||
ci->ports.at(id_A).net = nullptr;
|
||||
disconnect_port(ctx, ci, id_Z);
|
||||
ci->ports.at(id_Z).net = nullptr;
|
||||
connect_port(ctx, dly_net, i_pio, id_O);
|
||||
connect_port(ctx, input_net, iol, id_INDD);
|
||||
connect_port(ctx, input_net, iol, id_DI);
|
||||
} else {
|
||||
replace_port(ci, id_A, iol, id_PADDI);
|
||||
replace_port(ci, id_Z, iol, id_INDD);
|
||||
}
|
||||
packed_cells.insert(cell.first);
|
||||
} else if (o_pio != nullptr) {
|
||||
iol = create_pio_iologic(o_pio, ci);
|
||||
iol->params[ctx->id("DELAY.OUTDEL")] = "ENABLED";
|
||||
bool driven_by_iol = false;
|
||||
NetInfo *input_net = ci->ports.at(ctx->id("A")).net, *dly_net = ci->ports.at(ctx->id("Z")).net;
|
||||
if (input_net->driver.cell != nullptr && is_iologic_output_cell(ctx, input_net->driver.cell) &&
|
||||
input_net->driver.port == ctx->id("Q"))
|
||||
driven_by_iol = true;
|
||||
if (driven_by_iol) {
|
||||
disconnect_port(ctx, o_pio, id_I);
|
||||
i_pio->ports.at(id_I).net = nullptr;
|
||||
disconnect_port(ctx, ci, id_A);
|
||||
ci->ports.at(id_A).net = nullptr;
|
||||
disconnect_port(ctx, ci, id_Z);
|
||||
ci->ports.at(id_Z).net = nullptr;
|
||||
connect_port(ctx, input_net, o_pio, id_I);
|
||||
ctx->nets.erase(dly_net->name);
|
||||
} else {
|
||||
replace_port(ci, ctx->id("A"), iol, id_TXDATA0);
|
||||
replace_port(ci, ctx->id("Z"), iol, id_IOLDO);
|
||||
if (!o_pio->ports.count(id_IOLDO)) {
|
||||
o_pio->ports[id_IOLDO].name = id_IOLDO;
|
||||
o_pio->ports[id_IOLDO].type = PORT_IN;
|
||||
}
|
||||
replace_port(o_pio, id_I, o_pio, id_IOLDO);
|
||||
}
|
||||
packed_cells.insert(cell.first);
|
||||
} else {
|
||||
log_error("%s '%s' must be connected directly to top level input or output\n", ci->type.c_str(ctx),
|
||||
ci->name.c_str(ctx));
|
||||
}
|
||||
iol->params[ctx->id("DELAY.DEL_VALUE")] =
|
||||
std::to_string(lookup_delay(str_or_default(ci->params, ctx->id("DEL_MODE"), "USER_DEFINED")));
|
||||
if (ci->params.count(ctx->id("DEL_VALUE")))
|
||||
iol->params[ctx->id("DELAY.DEL_VALUE")] = ci->params.at(ctx->id("DEL_VALUE"));
|
||||
if (ci->ports.count(id_LOADN))
|
||||
replace_port(ci, id_LOADN, iol, id_LOADN);
|
||||
else
|
||||
tie_zero(ci, id_LOADN);
|
||||
if (ci->ports.count(id_MOVE))
|
||||
replace_port(ci, id_MOVE, iol, id_MOVE);
|
||||
else
|
||||
tie_zero(ci, id_MOVE);
|
||||
if (ci->ports.count(id_DIRECTION))
|
||||
replace_port(ci, id_DIRECTION, iol, id_DIRECTION);
|
||||
else
|
||||
tie_zero(ci, id_DIRECTION);
|
||||
if (ci->ports.count(id_CFLAG))
|
||||
replace_port(ci, id_CFLAG, iol, id_CFLAG);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto cell : sorted(ctx->cells)) {
|
||||
CellInfo *ci = cell.second;
|
||||
if (ci->type == ctx->id("IDDRX1F")) {
|
||||
|
Loading…
Reference in New Issue
Block a user