From b12a8c1a3086b5896f81b330bbabb8249514d496 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 12 Dec 2018 19:08:48 +0000 Subject: ecp5: Add {S}IOLOGIC constids and cell Signed-off-by: David Shah --- ecp5/cells.cc | 46 ++++++++++++++++++++++++++++++++++++++++++++++ ecp5/constids.inc | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) (limited to 'ecp5') diff --git a/ecp5/cells.cc b/ecp5/cells.cc index 31839ee4..9621b35c 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -41,6 +41,22 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str new_cell->name = ctx->id(name); } new_cell->type = type; + + auto copy_bel_ports = [&]() { + // First find a Bel of the target type + BelId tgt; + for (auto bel : ctx->getBels()) { + if (ctx->getBelType(bel) == type) { + tgt = bel; + break; + } + } + NPNR_ASSERT(tgt != BelId()); + for (auto port : ctx->getBelPins(tgt)) { + add_port(ctx, new_cell.get(), port.str(ctx), ctx->getBelPinType(tgt, port)); + } + }; + if (type == ctx->id("TRELLIS_SLICE")) { new_cell->params[ctx->id("MODE")] = "LOGIC"; new_cell->params[ctx->id("GSR")] = "DISABLED"; @@ -150,6 +166,36 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str add_port(ctx, new_cell.get(), "CLKI", PORT_IN); add_port(ctx, new_cell.get(), "CLKO", PORT_OUT); add_port(ctx, new_cell.get(), "CE", PORT_IN); + } else if (type == id_IOLOGIC || type == id_SIOLOGIC) { + new_cell->params[ctx->id("MODE")] = "NONE"; + new_cell->params[ctx->id("GSR")] = "DISABLED"; + new_cell->params[ctx->id("CLKIMUX")] = "CLK"; + new_cell->params[ctx->id("CLKOMUX")] = "CLK"; + new_cell->params[ctx->id("LSRIMUX")] = "0"; + new_cell->params[ctx->id("LSROMUX")] = "0"; + new_cell->params[ctx->id("LSRMUX")] = "LSR"; + + new_cell->params[ctx->id("OUTDEL")] = "DISABLED"; + new_cell->params[ctx->id("DEL_VALUE")] = "0"; + new_cell->params[ctx->id("WAIT_FOR_EDGE")] = "DISABLED"; + + new_cell->params[ctx->id("DATAMUX_ODDR")] = "PADDO"; + if (type == id_IOLOGIC) { + new_cell->params[ctx->id("IDDRXN_MODE")] = "NONE"; + new_cell->params[ctx->id("ODDRXN_MODE")] = "NONE"; + + new_cell->params[ctx->id("MIDDRX_MODE")] = "NONE"; + new_cell->params[ctx->id("MODDRX_MODE")] = "NONE"; + new_cell->params[ctx->id("MTDDRX_MODE")] = "NONE"; + + new_cell->params[ctx->id("IOLTOMUX")] = "NONE"; + new_cell->params[ctx->id("MTDDRX_DQSW_INVERT")] = "DISABLED"; + new_cell->params[ctx->id("MTDDRX_REGSET")] = "RESET"; + + new_cell->params[ctx->id("MIDDRX_MODDRX_WRCLKMUX")] = "NONE"; + } + // Just copy ports from the Bel + copy_bel_ports(); } else { log_error("unable to create ECP5 cell of type %s", type.c_str(ctx)); } diff --git a/ecp5/constids.inc b/ecp5/constids.inc index 250bc3fc..202a7eb2 100644 --- a/ecp5/constids.inc +++ b/ecp5/constids.inc @@ -1142,3 +1142,43 @@ X(PAD) X(PADDI) X(PADDO) X(PADDT) + +X(IOLOGIC) +X(SIOLOGIC) +X(DI) +X(IOLDO) +X(IOLDOD) +X(IOLDOI) +X(IOLTO) +X(INDD) +X(LOADN) +X(MOVE) +X(DIRECTION) +X(TSDATA0) +X(TXDATA0) +X(TXDATA1) +X(RXDATA0) +X(RXDATA1) +X(INFF) +X(CFLAG) +X(ECLK) +X(TSDATA1) +X(TXDATA2) +X(TXDATA3) +X(RXDATA2) +X(RXDATA3) +X(TXDATA4) +X(TXDATA5) +X(TXDATA6) +X(RXDATA4) +X(RXDATA5) +X(RXDATA6) +X(DQSR90) +X(DQSW270) +X(DQSW) +X(RDPNTR0) +X(RDPNTR1) +X(RDPNTR2) +X(WRPNTR0) +X(WRPNTR1) +X(WRPNTR2) -- cgit v1.2.3 From 36b1650df71f750df06e07c3af737bb118bced22 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 14 Dec 2018 09:55:04 +0000 Subject: ecp5: Adding IOLOGIC packing Signed-off-by: David Shah --- ecp5/cells.cc | 22 +++++++------- ecp5/pack.cc | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 11 deletions(-) (limited to 'ecp5') diff --git a/ecp5/cells.cc b/ecp5/cells.cc index 9621b35c..fee1d982 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -175,24 +175,24 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str new_cell->params[ctx->id("LSROMUX")] = "0"; new_cell->params[ctx->id("LSRMUX")] = "LSR"; - new_cell->params[ctx->id("OUTDEL")] = "DISABLED"; - new_cell->params[ctx->id("DEL_VALUE")] = "0"; - new_cell->params[ctx->id("WAIT_FOR_EDGE")] = "DISABLED"; + new_cell->params[ctx->id("DELAY.OUTDEL")] = "DISABLED"; + new_cell->params[ctx->id("DELAY.DEL_VALUE")] = "0"; + new_cell->params[ctx->id("DELAY.WAIT_FOR_EDGE")] = "DISABLED"; new_cell->params[ctx->id("DATAMUX_ODDR")] = "PADDO"; if (type == id_IOLOGIC) { - new_cell->params[ctx->id("IDDRXN_MODE")] = "NONE"; - new_cell->params[ctx->id("ODDRXN_MODE")] = "NONE"; + new_cell->params[ctx->id("IDDRXN.MODE")] = "NONE"; + new_cell->params[ctx->id("ODDRXN.MODE")] = "NONE"; - new_cell->params[ctx->id("MIDDRX_MODE")] = "NONE"; - new_cell->params[ctx->id("MODDRX_MODE")] = "NONE"; - new_cell->params[ctx->id("MTDDRX_MODE")] = "NONE"; + new_cell->params[ctx->id("MIDDRX.MODE")] = "NONE"; + new_cell->params[ctx->id("MODDRX.MODE")] = "NONE"; + new_cell->params[ctx->id("MTDDRX.MODE")] = "NONE"; new_cell->params[ctx->id("IOLTOMUX")] = "NONE"; - new_cell->params[ctx->id("MTDDRX_DQSW_INVERT")] = "DISABLED"; - new_cell->params[ctx->id("MTDDRX_REGSET")] = "RESET"; + new_cell->params[ctx->id("MTDDRX.DQSW_INVERT")] = "DISABLED"; + new_cell->params[ctx->id("MTDDRX.REGSET")] = "RESET"; - new_cell->params[ctx->id("MIDDRX_MODDRX_WRCLKMUX")] = "NONE"; + new_cell->params[ctx->id("MIDDRX_MODDRX.WRCLKMUX")] = "NONE"; } // Just copy ports from the Bel copy_bel_ports(); diff --git a/ecp5/pack.cc b/ecp5/pack.cc index ca329530..504ee784 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -1377,6 +1377,104 @@ class Ecp5Packer } } + // Pack IOLOGIC + void pack_iologic() { + std::unordered_map pio_iologic; + + auto set_iologic_sclk = [&](CellInfo *iol, CellInfo *prim, IdString port, bool input) { + NetInfo *sclk = nullptr; + if (prim->ports.count(port)) + sclk = prim->ports[port].net; + if (sclk == nullptr) { + iol->params[input ? ctx->id("CLKIMUX") : ctx->id("CLKOMUX")] = "0"; + } else { + 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)); + } else { + iol->ports[id_CLK].net = sclk; + } + } + if (prim->ports.count(port)) + disconnect_port(ctx, prim, port); + }; + + auto set_iologic_lsr = [&](CellInfo *iol, CellInfo *prim, IdString port, bool input) { + NetInfo *lsr = nullptr; + if (prim->ports.count(port)) + lsr = prim->ports[port].net; + if (lsr == nullptr) { + iol->params[input ? ctx->id("LSRIMUX") : ctx->id("LSROMUX")] = "0"; + } else { + 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)); + } else { + iol->ports[id_LSR].net = lsr; + } + } + if (prim->ports.count(port)) + disconnect_port(ctx, prim, port); + }; + + 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()); + 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)); + BelId bel = ctx->getBelByName(ctx->id(pio->attrs.at(ctx->id("BEL")))); + NPNR_ASSERT(bel != BelId()); + Loc loc = ctx->getBelLocation(pio->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"); + + iol->constr_parent = pio; + iol->constr_x = 0; + iol->constr_y = 0; + iol->constr_z = 4; + pio->constr_children.push_back(iol.get()); + + CellInfo *iol_ptr = iol.get(); + pio_iologic[pio->name] = iol_ptr; + new_cells.push_back(std::move(iol)); + return iol_ptr; + }; + + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + 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)); + 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, "IDDRX1_ODDRX1"); + replace_port(ci, ctx->id("D"), iol, id_PADDI); + set_iologic_sclk(iol, ci, ctx->id("SCLK"), true); + set_iologic_lsr(iol, ci, ctx->id("RST"), true); + replace_port(ci, ctx->id("Q0"), iol, id_RXDATA0); + replace_port(ci, ctx->id("Q1"), iol, id_RXDATA1); + iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED"); + packed_cells.insert(cell.first); + } + } + flush_cells(); + + }; + public: void pack() { -- cgit v1.2.3 From 9dc845b20d74031cd7bb4a520fc241d086befe77 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 14 Dec 2018 14:59:14 +0000 Subject: ecp5: Add ODDR packing Signed-off-by: David Shah --- ecp5/cells.cc | 7 ++++++- ecp5/pack.cc | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'ecp5') diff --git a/ecp5/cells.cc b/ecp5/cells.cc index fee1d982..58d4797c 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -127,11 +127,17 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str } else if (type == ctx->id("TRELLIS_IO")) { new_cell->params[ctx->id("DIR")] = "INPUT"; new_cell->attrs[ctx->id("IO_TYPE")] = "LVCMOS33"; + new_cell->params[ctx->id("DATAMUX_ODDR")] = "PADDO"; + new_cell->params[ctx->id("DATAMUX_MDDR")] = "PADDO"; add_port(ctx, new_cell.get(), "B", PORT_INOUT); add_port(ctx, new_cell.get(), "I", PORT_IN); add_port(ctx, new_cell.get(), "T", PORT_IN); add_port(ctx, new_cell.get(), "O", PORT_OUT); + + add_port(ctx, new_cell.get(), "IOLDO", PORT_IN); + add_port(ctx, new_cell.get(), "IOLTO", PORT_IN); + } else if (type == ctx->id("LUT4")) { new_cell->params[ctx->id("INIT")] = "0"; @@ -179,7 +185,6 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str new_cell->params[ctx->id("DELAY.DEL_VALUE")] = "0"; new_cell->params[ctx->id("DELAY.WAIT_FOR_EDGE")] = "DISABLED"; - new_cell->params[ctx->id("DATAMUX_ODDR")] = "PADDO"; if (type == id_IOLOGIC) { new_cell->params[ctx->id("IDDRXN.MODE")] = "NONE"; new_cell->params[ctx->id("ODDRXN.MODE")] = "NONE"; diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 504ee784..a09480c2 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -1469,6 +1469,25 @@ class Ecp5Packer replace_port(ci, ctx->id("Q1"), iol, id_RXDATA1); iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED"); packed_cells.insert(cell.first); + } 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)); + 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, "IDDRX1_ODDRX1"); + replace_port(ci, ctx->id("Q"), iol, id_IOLDO); + replace_port(pio, id_PADDO, 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); + 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"); + packed_cells.insert(cell.first); } } flush_cells(); -- cgit v1.2.3 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 From d75075e15c9140c768cc5b5693a284ff35a648be Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 15 Dec 2018 13:52:18 +0000 Subject: ecp5: Fix IOLOGIC ports at the same constant value Signed-off-by: David Shah --- ecp5/pack.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'ecp5') diff --git a/ecp5/pack.cc b/ecp5/pack.cc index f5fd7b6e..6ae169c9 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -1377,6 +1377,16 @@ class Ecp5Packer } } + // Check if two nets have identical constant drivers + bool equal_constant(NetInfo *a, NetInfo *b) + { + if (a->driver.cell == nullptr || b->driver.cell == nullptr) + return (a->driver.cell == nullptr && b->driver.cell == nullptr); + if (a->driver.cell->type != ctx->id("GND") && a->driver.cell->type != ctx->id("VCC")) + return false; + return a->driver.cell->type == b->driver.cell->type; + } + // Pack IOLOGIC void pack_iologic() { @@ -1391,7 +1401,7 @@ class Ecp5Packer } 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) + if (iol->ports[id_CLK].net != sclk && !equal_constant(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)); } else { @@ -1410,7 +1420,7 @@ class Ecp5Packer 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 != nullptr && !equal_constant(iol->ports[id_LSR].net, lsr)) { 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)); -- cgit v1.2.3