From c01bb8850942ed690670ff5ded8eaaea0068e11e Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 14 Dec 2018 16:40:38 +0000 Subject: ecp5: Add IOLOGIC timing and bitstream; ODDR working Signed-off-by: David Shah --- ecp5/arch.cc | 20 ++++++++++++++++++++ ecp5/bitstream.cc | 18 ++++++++++++++++++ ecp5/globals.cc | 2 ++ ecp5/pack.cc | 55 +++++++++++++++++++++++++++++++------------------------ 4 files changed, 71 insertions(+), 24 deletions(-) (limited to 'ecp5') diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 719426ab..380c0d7d 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -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; } diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index df16946d..5031dab8 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -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 ¶m : 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); diff --git a/ecp5/globals.cc b/ecp5/globals.cc index ddaae5e5..49947b20 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -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; } diff --git a/ecp5/pack.cc b/ecp5/pack.cc index a09480c2..f5fd7b6e 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -1378,8 +1378,9 @@ class Ecp5Packer } // Pack IOLOGIC - void pack_iologic() { - std::unordered_map pio_iologic; + void pack_iologic() + { + std::unordered_map 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 iol = create_ecp5_cell(ctx, s ? id_SIOLOGIC : id_IOLOGIC, pio->name.str(ctx) + "$IOL"); + std::unique_ptr 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(); -- cgit v1.2.3