ecp5: Add IOLOGIC timing and bitstream; ODDR working

Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
David Shah 2018-12-14 16:40:38 +00:00
parent 9dc845b20d
commit c01bb88509
4 changed files with 71 additions and 24 deletions

View File

@ -579,6 +579,8 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
return false;
} else if (cell->type == id_DP16KD) {
return false;
} else if (cell->type == id_IOLOGIC || cell->type == id_SIOLOGIC) {
return false;
} else {
return false;
}
@ -669,6 +671,16 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT;
}
return TMG_IGNORE;
} else if (cell->type == id_IOLOGIC || cell->type == id_SIOLOGIC) {
if (port == id_CLK || port == id_ECLK) {
return TMG_CLOCK_INPUT;
} else if (port == id_IOLDO || port == id_IOLDOI || port == id_IOLDOD || port == id_IOLTO || port == id_PADDI ||
port == id_DQSR90 || port == id_DQSW || port == id_DQSW270) {
return TMG_IGNORE;
} else {
clockInfoCount = 1;
return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT;
}
} else {
log_error("cell type '%s' is unsupported (instantiated as '%s')\n", cell->type.c_str(this),
cell->name.c_str(this));
@ -744,6 +756,14 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
info.setup = getDelayFromNS(1);
info.hold = getDelayFromNS(0);
}
} else if (cell->type == id_IOLOGIC || cell->type == id_SIOLOGIC) {
info.clock_port = id_CLK;
if (cell->ports.at(port).type == PORT_OUT) {
info.clockToQ = getDelayFromNS(0.5);
} else {
info.setup = getDelayFromNS(0.1);
info.hold = getDelayFromNS(0);
}
}
return info;
}

View File

@ -745,6 +745,12 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
}
if (ci->attrs.count(ctx->id("SLEWRATE")))
cc.tiles[pio_tile].add_enum(pio + ".SLEWRATE", str_or_default(ci->attrs, ctx->id("SLEWRATE"), "SLOW"));
std::string datamux_oddr = str_or_default(ci->params, ctx->id("DATAMUX_ODDR"), "PADDO");
if (datamux_oddr != "PADDO")
cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_ODDR", datamux_oddr);
std::string datamux_mddr = str_or_default(ci->params, ctx->id("DATAMUX_MDDR"), "PADDO");
if (datamux_mddr != "PADDO")
cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_MDDR", datamux_mddr);
} else if (ci->type == ctx->id("DCCA")) {
// Nothing to do
} else if (ci->type == ctx->id("DP16KD")) {
@ -1078,6 +1084,18 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_ENABLE_FILTEROPAMP"), 0), 1));
cc.tilegroups.push_back(tg);
} else if (ci->type == id_IOLOGIC || ci->type == id_SIOLOGIC) {
Loc pio_loc = ctx->getBelLocation(ci->bel);
pio_loc.z -= ci->type == id_SIOLOGIC ? 2 : 4;
std::string pic_tile = get_pic_tile(ctx, ctx->getBelByLocation(pio_loc));
std::string prim = std::string("IOLOGIC") + "ABCD"[pio_loc.z];
for (auto &param : ci->params) {
if (param.first == ctx->id("DELAY.DEL_VALUE"))
cc.tiles[pic_tile].add_word(prim + "." + param.first.str(ctx),
int_to_bitvector(std::stoi(param.second), 7));
else
cc.tiles[pic_tile].add_enum(prim + "." + param.first.str(ctx), param.second);
}
} else if (ci->type == id_DCUA) {
TileGroup tg;
tg.tiles = get_dcu_tiles(ctx, ci->bel);

View File

@ -58,6 +58,8 @@ class Ecp5GlobalRouter
if (user.cell->type == id_DCUA && (user.port == id_CH0_FF_RXI_CLK || user.port == id_CH1_FF_RXI_CLK ||
user.port == id_CH0_FF_TXI_CLK || user.port == id_CH1_FF_TXI_CLK))
return true;
if ((user.cell->type == id_IOLOGIC || user.cell->type == id_SIOLOGIC) && user.port == id_CLK)
return true;
return false;
}

View File

@ -1378,8 +1378,9 @@ class Ecp5Packer
}
// Pack IOLOGIC
void pack_iologic() {
std::unordered_map<IdString, CellInfo*> pio_iologic;
void pack_iologic()
{
std::unordered_map<IdString, CellInfo *> pio_iologic;
auto set_iologic_sclk = [&](CellInfo *iol, CellInfo *prim, IdString port, bool input) {
NetInfo *sclk = nullptr;
@ -1388,12 +1389,13 @@ class Ecp5Packer
if (sclk == nullptr) {
iol->params[input ? ctx->id("CLKIMUX") : ctx->id("CLKOMUX")] = "0";
} else {
iol->params[input ? ctx->id("CLKIMUX") : ctx->id("CLKOMUX")] = "CLK";
if (iol->ports[id_CLK].net != nullptr) {
if (iol->ports[id_CLK].net != sclk)
log_error("IOLOGIC '%s' has conflicting clocks '%s' and '%s'\n",
iol->name.c_str(ctx), iol->ports[id_CLK].net->name.c_str(ctx), sclk->name.c_str(ctx));
log_error("IOLOGIC '%s' has conflicting clocks '%s' and '%s'\n", iol->name.c_str(ctx),
iol->ports[id_CLK].net->name.c_str(ctx), sclk->name.c_str(ctx));
} else {
iol->ports[id_CLK].net = sclk;
connect_port(ctx, sclk, iol, id_CLK);
}
}
if (prim->ports.count(port))
@ -1407,12 +1409,13 @@ class Ecp5Packer
if (lsr == nullptr) {
iol->params[input ? ctx->id("LSRIMUX") : ctx->id("LSROMUX")] = "0";
} else {
iol->params[input ? ctx->id("LSRIMUX") : ctx->id("LSROMUX")] = "LSRMUX";
if (iol->ports[id_LSR].net != nullptr) {
if (iol->ports[id_LSR].net != lsr)
log_error("IOLOGIC '%s' has conflicting LSR signals '%s' and '%s'\n",
iol->name.c_str(ctx), iol->ports[id_LSR].net->name.c_str(ctx), lsr->name.c_str(ctx));
log_error("IOLOGIC '%s' has conflicting LSR signals '%s' and '%s'\n", iol->name.c_str(ctx),
iol->ports[id_LSR].net->name.c_str(ctx), lsr->name.c_str(ctx));
} else {
iol->ports[id_LSR].net = lsr;
connect_port(ctx, lsr, iol, id_LSR);
}
}
if (prim->ports.count(port))
@ -1422,27 +1425,29 @@ 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)
log_error("IOLOGIC '%s' has conflicting modes '%s' and '%s'\n", iol->name.c_str(ctx), curr_mode.c_str(), 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());
curr_mode = mode;
};
auto create_pio_iologic = [&](CellInfo *pio, CellInfo *curr) {
if (!pio->attrs.count(ctx->id("BEL")))
log_error("IOLOGIC functionality (DDR, DELAY, DQS, etc) can only be used with pin-constrained PIO (while processing '%s').\n",
curr->name.c_str(ctx));
log_error("IOLOGIC functionality (DDR, DELAY, DQS, etc) can only be used with pin-constrained PIO "
"(while processing '%s').\n",
curr->name.c_str(ctx));
BelId bel = ctx->getBelByName(ctx->id(pio->attrs.at(ctx->id("BEL"))));
NPNR_ASSERT(bel != BelId());
Loc loc = ctx->getBelLocation(pio->bel);
log_info("IOLOGIC component %s connected to PIO Bel %s\n", curr->name.c_str(ctx),
ctx->getBelName(bel).c_str(ctx));
Loc loc = ctx->getBelLocation(bel);
bool s = false;
if (loc.y == 0 || loc.y == (ctx->chip_info->height - 1))
s = true;
std::unique_ptr<CellInfo> iol = create_ecp5_cell(ctx, s ? id_SIOLOGIC : id_IOLOGIC, pio->name.str(ctx) + "$IOL");
std::unique_ptr<CellInfo> iol =
create_ecp5_cell(ctx, s ? id_SIOLOGIC : id_IOLOGIC, pio->name.str(ctx) + "$IOL");
iol->constr_parent = pio;
iol->constr_x = 0;
iol->constr_y = 0;
iol->constr_z = 4;
pio->constr_children.push_back(iol.get());
loc.z += s ? 2 : 4;
iol->attrs[ctx->id("BEL")] = ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx);
CellInfo *iol_ptr = iol.get();
pio_iologic[pio->name] = iol_ptr;
@ -1455,7 +1460,8 @@ class Ecp5Packer
if (ci->type == ctx->id("IDDRX1F")) {
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)
log_error("IDDRX1F '%s' D input must be connected only to a top level input\n", ci->name.c_str(ctx));
log_error("IDDRX1F '%s' D input must be connected only to a top level input\n",
ci->name.c_str(ctx));
CellInfo *iol;
if (pio_iologic.count(pio->name))
iol = pio_iologic.at(pio->name);
@ -1472,7 +1478,8 @@ class Ecp5Packer
} else if (ci->type == ctx->id("ODDRX1F")) {
CellInfo *pio = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_I, true);
if (pio == nullptr)
log_error("ODDRX1F '%s' Q output must be connected only to a top level output\n", ci->name.c_str(ctx));
log_error("ODDRX1F '%s' Q output must be connected only to a top level output\n",
ci->name.c_str(ctx));
CellInfo *iol;
if (pio_iologic.count(pio->name))
iol = pio_iologic.at(pio->name);
@ -1480,10 +1487,10 @@ class Ecp5Packer
iol = create_pio_iologic(pio, ci);
set_iologic_mode(iol, "IDDRX1_ODDRX1");
replace_port(ci, ctx->id("Q"), iol, id_IOLDO);
replace_port(pio, id_PADDO, pio, id_IOLDO);
replace_port(pio, id_I, pio, id_IOLDO);
pio->params[ctx->id("DATAMUX_ODDR")] = "IOLDO";
set_iologic_sclk(iol, ci, ctx->id("SCLK"), true);
set_iologic_lsr(iol, ci, ctx->id("RST"), true);
set_iologic_sclk(iol, ci, ctx->id("SCLK"), false);
set_iologic_lsr(iol, ci, ctx->id("RST"), false);
replace_port(ci, ctx->id("D0"), iol, id_TXDATA0);
replace_port(ci, ctx->id("D1"), iol, id_TXDATA1);
iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED");
@ -1491,13 +1498,13 @@ class Ecp5Packer
}
}
flush_cells();
};
public:
void pack()
{
pack_io();
pack_iologic();
pack_ebr();
pack_dsps();
pack_dcus();