From 0cf5006e3bbad476616f09bf1baf8a2953e1c616 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sun, 29 Jan 2023 21:11:54 +0100 Subject: [PATCH 1/5] ice40: Rework pull-up attribute copy to SB_IO blocks We try to copy the attribute only when there is a chance for the output driver to not be active. Note that this can _also_ happen when a port is specified as output but has a TBUF, which the previous code wasn't handling. We could copy the attribute "all-the-time" but this would mean if a user specified a `-pullup yes` in the PCF for a permanently driven output pin, we'd be burning power for nothing. Signed-off-by: Sylvain Munaut --- ice40/cells.cc | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/ice40/cells.cc b/ice40/cells.cc index fc24caaf..5e3bd139 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -404,25 +404,23 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &todelete_cells) { + bool pull_up_attr = false; + if (nxio->type == ctx->id("$nextpnr_ibuf")) { sbio->params[id_PIN_TYPE] = 1; - auto pu_attr = nxio->attrs.find(id_PULLUP); - if (pu_attr != nxio->attrs.end()) - sbio->params[id_PULLUP] = pu_attr->second; nxio->movePortTo(id_O, sbio, id_D_IN_0); + pull_up_attr = true; } else if (nxio->type == ctx->id("$nextpnr_obuf")) { sbio->params[id_PIN_TYPE] = 25; nxio->movePortTo(id_I, sbio, id_D_OUT_0); } else if (nxio->type == ctx->id("$nextpnr_iobuf")) { // N.B. tristate will be dealt with below NetInfo *i = nxio->getPort(id_I); - if (i == nullptr || i->driver.cell == nullptr) + if (i == nullptr || i->driver.cell == nullptr) { sbio->params[id_PIN_TYPE] = 1; - else + pull_up_attr = true; + } else sbio->params[id_PIN_TYPE] = 25; - auto pu_attr = nxio->attrs.find(id_PULLUP); - if (pu_attr != nxio->attrs.end()) - sbio->params[id_PULLUP] = pu_attr->second; nxio->movePortTo(id_I, sbio, id_D_OUT_0); nxio->movePortTo(id_O, sbio, id_D_IN_0); } else { @@ -465,6 +463,7 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &to sbio->params[id_PIN_TYPE] = 41; tbuf->movePortTo(id_A, sbio, id_D_OUT_0); tbuf->movePortTo(id_E, sbio, id_OUTPUT_ENABLE); + pull_up_attr = true; if (donet->users.entries() > 1) { for (auto user : donet->users) @@ -476,6 +475,13 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &to ctx->nets.erase(donet->name); todelete_cells.insert(tbuf->name); } + + // Copy pull-up attribute if there's any chance output driver isn't active + if (pull_up_attr) { + auto pu_attr = nxio->attrs.find(id_PULLUP); + if (pu_attr != nxio->attrs.end()) + sbio->params[id_PULLUP] = pu_attr->second; + } } uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell) From f50d4c1ed13142471505e0233e7985052d868ac2 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sun, 29 Jan 2023 22:04:13 +0100 Subject: [PATCH 2/5] ice40: Support for undriven / unconnected output ports If a port specified as output (and thus had a $nextpnr_obuf inserted) is undriven (const `z` or const `x`), we make sure to not enable the output driver. Also enable pull-ups if it was requested by the user. Signed-off-by: Sylvain Munaut --- ice40/cells.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ice40/cells.cc b/ice40/cells.cc index 5e3bd139..10964f3c 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -411,7 +411,12 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &to nxio->movePortTo(id_O, sbio, id_D_IN_0); pull_up_attr = true; } else if (nxio->type == ctx->id("$nextpnr_obuf")) { - sbio->params[id_PIN_TYPE] = 25; + NetInfo *i = nxio->getPort(id_I); + if (i == nullptr || i->driver.cell == nullptr) { + sbio->params[id_PIN_TYPE] = 1; + pull_up_attr = true; + } else + sbio->params[id_PIN_TYPE] = 25; nxio->movePortTo(id_I, sbio, id_D_OUT_0); } else if (nxio->type == ctx->id("$nextpnr_iobuf")) { // N.B. tristate will be dealt with below From 803c57d0529089bab71e203e7bc7e3e5be15285a Mon Sep 17 00:00:00 2001 From: rowanG077 Date: Tue, 31 Jan 2023 21:00:21 +0100 Subject: [PATCH 3/5] ecp5: LOCATE in LPF works on singleton vector --- ecp5/lpf.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ecp5/lpf.cc b/ecp5/lpf.cc index 4f02d22d..2e34f54a 100644 --- a/ecp5/lpf.cc +++ b/ecp5/lpf.cc @@ -131,9 +131,16 @@ bool Arch::apply_lpf(std::string filename, std::istream &in) std::string cell = strip_quotes(words.at(2)); if (words.at(3) != "SITE") log_error("expected 'SITE' after 'LOCATE COMP %s' (on line %d)\n", cell.c_str(), lineno); - auto fnd_cell = cells.find(id(cell)); if (words.size() > 5) log_error("unexpected input following LOCATE clause (on line %d)\n", lineno); + auto fnd_cell = cells.find(id(cell)); + // 1-bit wires are treated as scalar by nextpnr. + // In HDL they might have been a singleton vector. + if (fnd_cell == cells.end() && cell.size() >= 3 && cell.substr(cell.size() - 3) == "[0]") { + cell = cell.substr(0, cell.size() - 3); + fnd_cell = cells.find(id(cell)); + } + if (fnd_cell != cells.end()) { fnd_cell->second->attrs[id_LOC] = strip_quotes(words.at(4)); } From 49ae4953443951bcd9679689e76c56411a3c9385 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Wed, 1 Feb 2023 11:41:16 +0100 Subject: [PATCH 4/5] ice40: Add support for PLL ICEGATE function Technically you can enable it independently on CORE and GLOBAL output, but this is not exposed in the classic primitive, so we do the same as icecube2 and enable/disable it for both output path depending on the argument Signed-off-by: Sylvain Munaut --- ice40/bitstream.cc | 27 ++++++++++++++------------- ice40/cells.cc | 3 +++ ice40/constids.inc | 2 ++ 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index c8ad93d8..6794ecca 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -423,6 +423,18 @@ void write_asc(const Context *ctx, std::ostream &out) // If this is a PAD PLL, and this is the 'PLLOUT_A' port, then the same SB_IO is also PAD if (port == id_PLLOUT_A && is_sb_pll40_pad(ctx, cell.second.get())) sb_io_used_by_pll_pad.insert(io_bel_loc); + + // Configure the SB_IO that the clock outputs are going through. + // note: PINTYPE[1:0] must be set property to passes the PLL through to the fabric. + // "01" if ICEGATE is disabed for that port and "11" if it's enabled + const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO]; + bool icegate_ena = get_param_or_def( + ctx, cell.second.get(), (port == id_PLLOUT_A) ? id_ENABLE_ICEGATE_PORTA : id_ENABLE_ICEGATE_PORTB); + + set_config(ti, config.at(io_bel_loc.y).at(io_bel_loc.x), + "IOB_" + std::to_string(io_bel_loc.z) + ".PINTYPE_1", icegate_ena); + set_config(ti, config.at(io_bel_loc.y).at(io_bel_loc.x), + "IOB_" + std::to_string(io_bel_loc.z) + ".PINTYPE_0", true); } } @@ -724,6 +736,8 @@ void write_asc(const Context *ctx, std::ostream &out) {"DIVF", 7}, {"DIVQ", 3}, {"DIVR", 4}, + {"ENABLE_ICEGATE_PORTA", 1}, + {"ENABLE_ICEGATE_PORTB", 1}, {"FDA_FEEDBACK", 4}, {"FDA_RELATIVE", 4}, {"FEEDBACK_PATH", 3}, @@ -734,19 +748,6 @@ void write_asc(const Context *ctx, std::ostream &out) {"SHIFTREG_DIV_MODE", 2}, {"TEST_MODE", 1}}; configure_extra_cell(config, ctx, cell.second.get(), pll_params, false, std::string("PLL.")); - - // Configure the SB_IOs that the clock outputs are going through. - for (auto &io_bel_loc : sb_io_used_by_pll_out) { - // Write config. - const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO]; - - // PINTYPE[1:0] == "01" passes the PLL through to the fabric. - set_config(ti, config.at(io_bel_loc.y).at(io_bel_loc.x), - "IOB_" + std::to_string(io_bel_loc.z) + ".PINTYPE_1", false); - set_config(ti, config.at(io_bel_loc.y).at(io_bel_loc.x), - "IOB_" + std::to_string(io_bel_loc.z) + ".PINTYPE_0", true); - } - } else { NPNR_ASSERT(false); } diff --git a/ice40/cells.cc b/ice40/cells.cc index 10964f3c..d304a68f 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -216,6 +216,9 @@ std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::stri new_cell->params[id_PLLOUT_SELECT_A] = Property(0, 2); new_cell->params[id_PLLOUT_SELECT_B] = Property(0, 2); + new_cell->params[id_ENABLE_ICEGATE_PORTA] = Property::State::S0; + new_cell->params[id_ENABLE_ICEGATE_PORTB] = Property::State::S0; + new_cell->params[id_PLLTYPE] = Property(0, 3); new_cell->params[id_SHIFTREG_DIVMODE] = Property::State::S0; new_cell->params[id_TEST_MODE] = Property::State::S0; diff --git a/ice40/constids.inc b/ice40/constids.inc index 8bad9e25..5575053d 100644 --- a/ice40/constids.inc +++ b/ice40/constids.inc @@ -480,6 +480,8 @@ X(DIVQ) X(DIVR) X(D_REG) X(E) +X(ENABLE_ICEGATE_PORTA) +X(ENABLE_ICEGATE_PORTB) X(FDA_FEEDBACK) X(FDA_RELATIVE) X(FEEDBACK_PATH) From 582410629bb45a55aa1a3b8258bd466e71b3a972 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Wed, 1 Feb 2023 15:00:44 +0100 Subject: [PATCH 5/5] ice40: Don't assert on unknown extra_config bits if they are 0 Bits are 0 by default anyway, so if they are unknown (because icestorm is too od) but we want them at 0 ... it's not much of an issue. Signed-off-by: Sylvain Munaut --- ice40/bitstream.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 6794ecca..72343d75 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -153,7 +153,11 @@ static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfi return; } } - NPNR_ASSERT_FALSE_STR("failed to config extra cell config bit " + name); + if (value) + NPNR_ASSERT_FALSE_STR("failed to config extra cell config bit " + name); + else + log_warning("failed to config extra cell config bit %s to zero (ignored, maybe update icestorm ?)\n", + name.c_str()); } void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell,