From c6401413a4511de7faf56254ea27a85b5dc55ca0 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 9 Oct 2019 14:23:35 +0100 Subject: ecp5: Add support for IO registers Signed-off-by: David Shah --- common/util.h | 11 ++++++- ecp5/bitstream.cc | 6 ++++ ecp5/pack.cc | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/common/util.h b/common/util.h index 81d7e47d..2ccfe5d2 100644 --- a/common/util.h +++ b/common/util.h @@ -119,7 +119,16 @@ inline const NetInfo *get_net_or_empty(const CellInfo *cell, const IdString port return found->second.net; else 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 diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 1d153720..acab95dd 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -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"); if (datamux_oddr != "PADDO") 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"); if (datamux_mddr != "PADDO") 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")) { const NetInfo *cen = get_net_or_empty(ci, ctx->id("CE")); if (cen != nullptr) { diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 15c02e86..0aa69952 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -1844,6 +1844,8 @@ class Ecp5Packer auto set_iologic_mode = [&](CellInfo *iol, std::string mode) { 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) log_error("IOLOGIC '%s' has conflicting modes '%s' and '%s'\n", iol->name.c_str(ctx), curr_mode.c_str(), mode.c_str()); @@ -2240,6 +2242,101 @@ class Ecp5Packer std::string(ci->type == ctx->id("TSHX2DQSA") ? "DQSW" : "DQSW270"); iol->params[ctx->id("IOLTOMUX")] = std::string("TDDR"); 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"); + replace_port(ci, ctx->id("CE"), iol, 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_I, 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"); + replace_port(ci, ctx->id("CE"), iol, 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(); -- cgit v1.2.3