diff options
-rw-r--r-- | README.md | 6 | ||||
-rw-r--r-- | ecp5/lpf.cc | 9 | ||||
-rw-r--r-- | ecp5/pack.cc | 178 | ||||
-rw-r--r-- | generic/viaduct/fabulous/fabric_parsing.h | 14 | ||||
-rw-r--r-- | generic/viaduct/fabulous/fabulous.cc | 17 | ||||
-rw-r--r-- | gowin/arch.cc | 120 | ||||
-rw-r--r-- | gowin/arch.h | 7 | ||||
-rw-r--r-- | gowin/constids.inc | 2 | ||||
-rw-r--r-- | gowin/pack.cc | 3 | ||||
-rw-r--r-- | ice40/bitstream.cc | 33 | ||||
-rw-r--r-- | ice40/cells.cc | 3 | ||||
-rw-r--r-- | ice40/constids.inc | 2 | ||||
-rw-r--r-- | ice40/pack.cc | 6 | ||||
-rw-r--r-- | machxo2/arch_pybindings.cc | 4 |
14 files changed, 316 insertions, 88 deletions
@@ -5,7 +5,7 @@ nextpnr aims to be a vendor neutral, timing driven, FOSS FPGA place and route tool. Currently nextpnr supports: - * Lattice iCE40 devices supported by [Project IceStorm](http://bygone.clairexen.net/icestorm/) + * Lattice iCE40 devices supported by [Project IceStorm](https://github.com/YosysHQ/icestorm) * Lattice ECP5 devices supported by [Project Trellis](https://github.com/YosysHQ/prjtrellis) * Lattice Nexus devices supported by [Project Oxide](https://github.com/gatecat/prjoxide) * Gowin LittleBee devices supported by [Project Apicula](https://github.com/YosysHQ/apicula) @@ -54,7 +54,7 @@ Getting started ### nextpnr-ice40 -For iCE40 support, install [Project IceStorm](http://bygone.clairexen.net/icestorm/) to `/usr/local` or another location, which should be passed as `-DICESTORM_INSTALL_PREFIX=/usr` to CMake. Then build and install `nextpnr-ice40` using the following commands: +For iCE40 support, install [Project IceStorm](https://github.com/YosysHQ/icestorm) to `/usr/local` or another location, which should be passed as `-DICESTORM_INSTALL_PREFIX=/usr` to CMake. Then build and install `nextpnr-ice40` using the following commands: ``` cmake . -DARCH=ice40 @@ -252,7 +252,7 @@ Links and references ### FPGA bitstream documentation (and tools) projects -- [Project IceStorm (Lattice iCE40)](http://bygone.clairexen.net/icestorm/) +- [Project IceStorm (Lattice iCE40)](https://github.com/YosysHQ/icestorm) - [Project Trellis (Lattice ECP5)](https://yosyshq.github.io/prjtrellis-db/) - [Project X-Ray (Xilinx 7-Series)](https://symbiflow.github.io/prjxray-db/) - [Project Chibi (Intel MAX-V)](https://github.com/rqou/project-chibi) 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)); } diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 79d1688f..0c95b66c 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -36,6 +36,20 @@ static bool is_nextpnr_iob(Context *ctx, CellInfo *cell) cell->type == ctx->id("$nextpnr_iobuf"); } +static bool net_is_constant(const Context *ctx, NetInfo *net, bool &value) +{ + auto gnd = ctx->id("$PACKER_GND_NET"); + auto vcc = ctx->id("$PACKER_VCC_NET"); + if (net == nullptr) + return false; + if (net->name.in(gnd, vcc)) { + value = (net->name == vcc); + return true; + } else { + return false; + } +} + class Ecp5Packer { public: @@ -1508,6 +1522,10 @@ class Ecp5Packer // Check if two nets have identical constant drivers bool equal_constant(NetInfo *a, NetInfo *b) { + if (a == nullptr && b == nullptr) + return true; + if ((a == nullptr) != (b == nullptr)) + return false; if (a->driver.cell == nullptr || b->driver.cell == nullptr) return (a->driver.cell == nullptr && b->driver.cell == nullptr); if (a->driver.cell->type != id_GND && a->driver.cell->type != id_VCC) @@ -2237,7 +2255,8 @@ class Ecp5Packer iol->params[id_CEMUX] = str_or_default(ci->params, id_CEMUX, "CE"); ci->movePortTo(id_CE, iol, id_CE); } else { - if (iol->getPort(id_CE) != ci->getPort(id_CE) || + if ((iol->getPort(id_CE) != ci->getPort(id_CE) && + !equal_constant(iol->getPort(id_CE), ci->getPort(id_CE))) || str_or_default(ci->params, id_CEMUX, "CE") != str_or_default(iol->params, id_CEMUX, "CE")) log_error("CE signal or polarity mismatch for IO flipflop %s with other IOFFs at " @@ -2303,7 +2322,8 @@ class Ecp5Packer iol->params[id_CEMUX] = str_or_default(ci->params, id_CEMUX, "CE"); ci->movePortTo(id_CE, iol, id_CE); } else { - if (iol->getPort(id_CE) != ci->getPort(id_CE) || + if ((iol->getPort(id_CE) != ci->getPort(id_CE) && + !equal_constant(iol->getPort(id_CE), ci->getPort(id_CE))) || str_or_default(ci->params, id_CEMUX, "CE") != str_or_default(iol->params, id_CEMUX, "CE")) log_error("CE signal or polarity mismatch for IO flipflop %s with other IOFFs at " @@ -2602,6 +2622,15 @@ class Ecp5Packer auto MHz = [&](delay_t a) { return 1000.0 / ctx->getDelayNS(a); }; auto equals_epsilon = [](delay_t a, delay_t b) { return (std::abs(a - b) / std::max(double(b), 1.0)) < 1e-3; }; + auto equals_epsilon_pair = [&](DelayPair& a, DelayPair& b) { + return equals_epsilon(a.min_delay, b.min_delay) + && equals_epsilon(a.max_delay, b.max_delay); + }; + auto equals_epsilon_constr = [&](ClockConstraint& a, ClockConstraint& b) { + return equals_epsilon_pair(a.high, b.high) + && equals_epsilon_pair(a.low, b.low) + && equals_epsilon_pair(a.period, b.period); + }; pool<IdString> user_constrained, changed_nets; for (auto &net : ctx->nets) { @@ -2619,24 +2648,30 @@ class Ecp5Packer return true; }; - auto set_period = [&](CellInfo *ci, IdString port, delay_t period) { + auto simple_clk_contraint = [&](delay_t period) { + auto constr = std::unique_ptr<ClockConstraint>(new ClockConstraint()); + constr->low = DelayPair(period / 2); + constr->high = DelayPair(period / 2); + constr->period = DelayPair(period); + + return constr; + }; + + auto set_constraint = [&](CellInfo *ci, IdString port, std::unique_ptr<ClockConstraint> constr) { if (!ci->ports.count(port)) return; NetInfo *to = ci->ports.at(port).net; if (to == nullptr) return; if (to->clkconstr != nullptr) { - if (!equals_epsilon(to->clkconstr->period.minDelay(), period) && user_constrained.count(to->name)) + if (!equals_epsilon_constr(*to->clkconstr, *constr) && user_constrained.count(to->name)) log_warning( " Overriding derived constraint of %.1f MHz on net %s with user-specified constraint of " "%.1f MHz.\n", - MHz(to->clkconstr->period.min_delay), to->name.c_str(ctx), MHz(period)); + MHz(to->clkconstr->period.min_delay), to->name.c_str(ctx), MHz(constr->period.min_delay)); return; } - to->clkconstr = std::unique_ptr<ClockConstraint>(new ClockConstraint()); - to->clkconstr->low = DelayPair(period / 2); - to->clkconstr->high = DelayPair(period / 2); - to->clkconstr->period = DelayPair(period); + to->clkconstr = std::move(constr); log_info(" Derived frequency constraint of %.1f MHz for net %s\n", MHz(to->clkconstr->period.minDelay()), to->name.c_str(ctx)); changed_nets.insert(to->name); @@ -2706,6 +2741,121 @@ class Ecp5Packer copy_constraint(ci, id_CLK1, id_ECSOUT, 1); } else if (ci->type == id_DCCA) { copy_constraint(ci, id_CLKI, id_CLKO, 1); + } else if (ci->type == id_DCSC) { + if ((!ci->ports.count(id_CLK0) && !ci->ports.count(id_CLK1)) || !ci->ports.count(id_DCSOUT)) + continue; + + auto mode = str_or_default(ci->params, id_DCSMODE, "POS"); + bool mode_constant = false; + auto mode_is_constant = net_is_constant(ctx, ci->ports.at(id_MODESEL).net, mode_constant); + + if (mode_is_constant && mode_constant == false) { + if (mode == "CLK0_LOW" || mode == "CLK0_HIGH" || mode == "CLK0") { + copy_constraint(ci, id_CLK0, id_DCSOUT, 1.0); + continue; + } else if (mode == "CLK1_LOW" || mode == "CLK1_HIGH" || mode == "CLK1") { + copy_constraint(ci, id_CLK1, id_DCSOUT, 1.0); + continue; + } else if (mode == "LOW" || mode == "HIGH") { + continue; + } + } + + std::unique_ptr<ClockConstraint> derived_constr = nullptr; + std::vector<NetInfo*> in_ports = { + ci->ports.at(id_CLK0).net, + ci->ports.at(id_CLK1).net, + }; + + // Generate all unique clock pairs find the worst + // constraint from switching between them and merge them + // into the final output constraint. + for (size_t i = 0; i < in_ports.size(); ++i) { + auto p1 = in_ports[i]; + if (p1 == nullptr || p1->clkconstr == nullptr) { + derived_constr = nullptr; + break; + } + for (size_t j = i + 1; j < in_ports.size(); ++j) { + auto p2 = in_ports[j]; + if (p2 == nullptr || p2->clkconstr == nullptr) { + break; + } + auto& c1 = p1->clkconstr; + auto& c2 = p2->clkconstr; + + auto merged_constr = std::unique_ptr<ClockConstraint>(new ClockConstraint()); + + if (mode == "NEG") { + merged_constr->low = DelayPair( + std::min(c1->low.min_delay, c2->low.min_delay), + std::max( + c1->low.max_delay + c2->period.max_delay, + c2->low.max_delay + c1->period.max_delay + ) + ); + } else { + merged_constr->low = DelayPair( + std::min(c1->low.min_delay, c2->low.min_delay), + std::max(c1->low.max_delay, c2->low.max_delay) + ); + } + + if (mode == "POS") { + merged_constr->high = DelayPair( + std::min(c1->high.min_delay, c2->high.min_delay), + std::max( + c1->high.max_delay + c2->period.max_delay, + c2->high.max_delay + c1->period.max_delay + ) + ); + } else { + merged_constr->high = DelayPair( + std::min(c1->high.min_delay, c2->high.min_delay), + std::max(c1->high.max_delay, c2->high.max_delay) + ); + } + + merged_constr->period = DelayPair( + std::min(c1->period.min_delay, c2->period.min_delay), + std::max(c1->period.max_delay, c2->period.max_delay) + ); + + if (derived_constr == nullptr) { + derived_constr = std::move(merged_constr); + continue; + } + + derived_constr->period.min_delay = std::min( + derived_constr->period.min_delay, + merged_constr->period.min_delay + ); + derived_constr->period.max_delay = std::max( + derived_constr->period.max_delay, + merged_constr->period.max_delay + ); + derived_constr->low.min_delay = std::min( + derived_constr->low.min_delay, + merged_constr->low.min_delay + ); + derived_constr->low.max_delay = std::max( + derived_constr->low.max_delay, + merged_constr->low.max_delay + ); + derived_constr->high.min_delay = std::min( + derived_constr->high.min_delay, + merged_constr->high.min_delay + ); + derived_constr->high.max_delay = std::max( + derived_constr->high.max_delay, + merged_constr->high.max_delay + ); + } + } + + if (derived_constr != nullptr) { + set_constraint(ci, id_DCSOUT, std::move(derived_constr)); + } } else if (ci->type == id_EHXPLLL) { delay_t period_in; if (!get_period(ci, id_CLKI, period_in)) @@ -2734,13 +2884,13 @@ class Ecp5Packer log_info(" Derived VCO frequency %.1f MHz of PLL '%s' is out of legal range [400MHz, " "800MHz]\n", vco_freq, ci->name.c_str(ctx)); - set_period(ci, id_CLKOP, vco_period * int_or_default(ci->params, id_CLKOP_DIV, 1)); - set_period(ci, id_CLKOS, vco_period * int_or_default(ci->params, id_CLKOS_DIV, 1)); - set_period(ci, id_CLKOS2, vco_period * int_or_default(ci->params, id_CLKOS2_DIV, 1)); - set_period(ci, id_CLKOS3, vco_period * int_or_default(ci->params, id_CLKOS3_DIV, 1)); + set_constraint(ci, id_CLKOP, simple_clk_contraint(vco_period * int_or_default(ci->params, id_CLKOP_DIV, 1))); + set_constraint(ci, id_CLKOS, simple_clk_contraint(vco_period * int_or_default(ci->params, id_CLKOS_DIV, 1))); + set_constraint(ci, id_CLKOS2, simple_clk_contraint(vco_period * int_or_default(ci->params, id_CLKOS2_DIV, 1))); + set_constraint(ci, id_CLKOS3, simple_clk_contraint(vco_period * int_or_default(ci->params, id_CLKOS3_DIV, 1))); } else if (ci->type == id_OSCG) { int div = int_or_default(ci->params, id_DIV, 128); - set_period(ci, id_OSC, delay_t((1.0e6 / (2.0 * 155)) * div)); + set_constraint(ci, id_OSC, simple_clk_contraint(delay_t((1.0e6 / (2.0 * 155)) * div))); } } } diff --git a/generic/viaduct/fabulous/fabric_parsing.h b/generic/viaduct/fabulous/fabric_parsing.h index 3fa263ca..841b2465 100644 --- a/generic/viaduct/fabulous/fabric_parsing.h +++ b/generic/viaduct/fabulous/fabric_parsing.h @@ -68,6 +68,14 @@ struct parser_view return npos; } + size_t rfind(char tok) const + { + for (size_t i = m_length; i > 0; i--) + if (m_ptr[i - 1] == tok) + return i - 1; + return npos; + } + IdString to_id(const BaseCtx *ctx) { // This isn't really ideal, let's hope one day we can move to C++20 and have proper string_views instead :3 @@ -133,6 +141,12 @@ struct parser_view NPNR_ASSERT(pos != npos); return std::make_pair(parser_view(m_ptr, pos), parser_view(m_ptr + pos + 1, m_length - (pos + 1))); } + std::pair<parser_view, parser_view> rsplit(char delim) const + { + size_t pos = rfind(delim); + NPNR_ASSERT(pos != npos); + return std::make_pair(parser_view(m_ptr, pos), parser_view(m_ptr + pos + 1, m_length - (pos + 1))); + } }; struct CsvParser diff --git a/generic/viaduct/fabulous/fabulous.cc b/generic/viaduct/fabulous/fabulous.cc index e2fe1b74..7dad0c66 100644 --- a/generic/viaduct/fabulous/fabulous.cc +++ b/generic/viaduct/fabulous/fabulous.cc @@ -237,6 +237,16 @@ struct FabulousImpl : ViaductAPI NPNR_ASSERT(bel_idx.size() == 1); int bel_z = bel_idx[0] - 'A'; NPNR_ASSERT(bel_z >= 0 && bel_z < 26); + std::vector<parser_view> ports; + parser_view port; + while (!(port = csv.next_field()).empty()) { + ports.push_back(port); + } + IdString bel_name = bel_idx.to_id(ctx); + if (bel_type.in(id_InPass4_frame_config, id_OutPass4_frame_config)) { + // Assign BRAM IO a nicer name than just a letter + bel_name = ports.front().rsplit('_').first.to_id(ctx); + } /* In the future we will need to handle optionally splitting SLICEs into separate LUT/COMB and FF bels This is the preferred approach in nextpnr for arches where the LUT and FF can be used separately of @@ -245,12 +255,7 @@ struct FabulousImpl : ViaductAPI While this isn't yet the standard fabulous SLICE, it should be considered as a future option in fabulous. */ Loc loc(bel_x, bel_y, bel_z); - BelId bel = ctx->addBel(IdStringList::concat(tile, bel_idx.to_id(ctx)), bel_type, loc, false, false); - std::vector<parser_view> ports; - parser_view port; - while (!(port = csv.next_field()).empty()) { - ports.push_back(port); - } + BelId bel = ctx->addBel(IdStringList::concat(tile, bel_name), bel_type, loc, false, false); handle_bel_ports(bel, tile, bel_type, ports); } postprocess_bels(); diff --git a/gowin/arch.cc b/gowin/arch.cc index 88b9f42f..1629d653 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -1430,6 +1430,25 @@ Arch::Arch(ArchArgs args) : args(args) snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); addBelInput(belname, id_OSCEN, id(buf)); break; + case ID_OSCW: + snprintf(buf, 32, "R%dC%d_OSCW", row + 1, col + 1); + belname = id(buf); + addBel(belname, id_OSCW, Loc(col, row, BelZ::osc_z), false); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCOUT)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelOutput(belname, id_OSCOUT, id(buf)); + break; + case ID_OSCO: + snprintf(buf, 32, "R%dC%d_OSCO", row + 1, col + 1); + belname = id(buf); + addBel(belname, id_OSCO, Loc(col, row, BelZ::osc_z), false); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCOUT)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelOutput(belname, id_OSCOUT, id(buf)); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCEN)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_OSCEN, id(buf)); + break; case ID_RAM16: snprintf(buf, 32, "R%dC%d_RAMW", row + 1, col + 1); belname = id(buf); @@ -2108,6 +2127,58 @@ bool Arch::is_GCLKT_iob(const CellInfo *cell) return false; } +void Arch::bind_pll_to_bel(CellInfo *ci, PLL loc) +{ + BelId bel; + switch (ci->type.hash()) { + case ID_PLLVR: + bel = loc == PLL::left ? id("R1C28_PLLVR") : id("R1C37_PLLVR"); + break; + case ID_rPLL: + if (family == "GW1N-1" || family == "GW1NZ-1") { + if (loc == PLL::left) { + return; + } + bel = id("R1C18_rPLL"); + break; + } + if (family == "GW1NS-2") { + if (loc == PLL::left) { + return; + } + bel = id("R10C20_rPLL"); + break; + } + if (family == "GW1N-4") { + bel = loc == PLL::left ? id("R1C10_rPLL") : id("R1C28_rPLL"); + break; + } + if (family == "GW1NR-9C" || family == "GW1NR-9") { + bel = loc == PLL::left ? id("R10C1_rPLL") : id("R10C47_rPLL"); + break; + } + return; + default: + return; + } + if (checkBelAvail(bel) || ci->belStrength != STRENGTH_LOCKED) { + if (ci->bel == bel) { + unbindBel(bel); + } else { + if (!checkBelAvail(bel) && ci->belStrength != STRENGTH_LOCKED) { + CellInfo *other_ci = getBoundBelCell(bel); + unbindBel(bel); + BelId our_bel = ci->bel; + unbindBel(our_bel); + bindBel(our_bel, other_ci, STRENGTH_LOCKED); + } + } + ci->disconnectPort(id_CLKIN); + ci->setParam(id_INSEL, Property("CLKIN0")); + bindBel(bel, ci, STRENGTH_LOCKED); + } +} + // If the PLL input can be connected using a direct wire, then do so, // bypassing conventional routing. void Arch::fix_pll_nets(Context *ctx) @@ -2129,53 +2200,12 @@ void Arch::fix_pll_nets(Context *ctx) break; } if (net_driven_by(ctx, net, is_RPLL_T_IN_iob, id_O) != nullptr) { - if (ci->type == id_rPLL) { - ci->disconnectPort(id_CLKIN); - ci->setParam(id_INSEL, Property("CLKIN0")); - break; - } - BelId bel = id("R1C37_PLLVR"); - if (ci->type == id_PLLVR) { - if (checkBelAvail(bel) || ci->belStrength != STRENGTH_LOCKED) { - if (ci->bel == bel) { - unbindBel(bel); - } else { - if (!checkBelAvail(bel) && ci->belStrength != STRENGTH_LOCKED) { - CellInfo *other_ci = getBoundBelCell(bel); - unbindBel(bel); - BelId our_bel = ci->bel; - unbindBel(our_bel); - bindBel(our_bel, other_ci, STRENGTH_LOCKED); - } - } - ci->disconnectPort(id_CLKIN); - ci->setParam(id_INSEL, Property("CLKIN0")); - bindBel(bel, ci, STRENGTH_LOCKED); - break; - } - } + bind_pll_to_bel(ci, PLL::right); + break; } if (net_driven_by(ctx, net, is_LPLL_T_IN_iob, id_O) != nullptr) { - BelId bel = id("R1C28_PLLVR"); - if (ci->type == id_PLLVR) { - if (checkBelAvail(bel) || ci->belStrength != STRENGTH_LOCKED) { - if (ci->bel == bel) { - unbindBel(bel); - } else { - if (!checkBelAvail(bel) && ci->belStrength != STRENGTH_LOCKED) { - CellInfo *other_ci = getBoundBelCell(bel); - unbindBel(bel); - BelId our_bel = ci->bel; - unbindBel(our_bel); - bindBel(our_bel, other_ci, STRENGTH_LOCKED); - } - } - ci->disconnectPort(id_CLKIN); - ci->setParam(id_INSEL, Property("CLKIN0")); - bindBel(bel, ci, STRENGTH_LOCKED); - break; - } - } + bind_pll_to_bel(ci, PLL::left); + break; } // XXX do special bels (HCLK etc) // This is general routing through CLK0 pip diff --git a/gowin/arch.h b/gowin/arch.h index f060165a..822cdfc1 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -283,6 +283,12 @@ struct ArchRanges : BaseArchRanges using GroupGroupsRangeT = const std::vector<GroupId> &; }; +enum class PLL // fixed PLL locations +{ + left, + right +}; + struct Arch : BaseArch<ArchRanges> { std::string family; @@ -482,6 +488,7 @@ struct Arch : BaseArch<ArchRanges> void add_rpll_ports(DatabasePOD const *db, BelsPOD const *bel, IdString belname, int row, int col); void fix_pll_nets(Context *ctx); bool is_GCLKT_iob(const CellInfo *cell); + void bind_pll_to_bel(CellInfo *ci, PLL loc); GowinGlobalRouter globals_router; void mark_gowin_globals(Context *ctx); diff --git a/gowin/constids.inc b/gowin/constids.inc index dac84a1e..b678cc77 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -851,6 +851,8 @@ X(OSC) X(OSCZ) X(OSCH) X(OSCF) +X(OSCW) +X(OSCO) // PLLs X(rPLL) diff --git a/gowin/pack.cc b/gowin/pack.cc index cb24ac02..fc870890 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -1043,7 +1043,8 @@ static void pack_plls(Context *ctx) switch (ci->type.hash()) { case ID_rPLL: { - if (parm_device == "GW1N-1" || parm_device == "GW1NZ-1" || parm_device == "GW1NR-9C") { + if (parm_device == "GW1N-1" || parm_device == "GW1NZ-1" || parm_device == "GW1NR-9C" || + parm_device == "GW1NR-9" || parm_device == "GW1N-4" || parm_device == "GW1NS-2C") { pll_disable_unused_ports(ctx, ci); // A cell std::unique_ptr<CellInfo> cell = create_generic_cell(ctx, id_rPLL, ci->name.str(ctx) + "$rpll"); diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index c8ad93d8..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, @@ -423,6 +427,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 +740,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 +752,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<CellInfo> 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) diff --git a/ice40/pack.cc b/ice40/pack.cc index c9811721..f8944685 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -132,10 +132,12 @@ static void pack_nonlut_ffs(Context *ctx) static bool net_is_constant(const Context *ctx, NetInfo *net, bool &value) { + auto gnd = ctx->id("$PACKER_GND_NET"); + auto vcc = ctx->id("$PACKER_VCC_NET"); if (net == nullptr) return false; - if (net->name == ctx->id("$PACKER_GND_NET") || net->name == ctx->id("$PACKER_VCC_NET")) { - value = (net->name == ctx->id("$PACKER_VCC_NET")); + if (net->name.in(gnd, vcc)) { + value = (net->name == vcc); return true; } else { return false; diff --git a/machxo2/arch_pybindings.cc b/machxo2/arch_pybindings.cc index 2ed68697..3fcf721d 100644 --- a/machxo2/arch_pybindings.cc +++ b/machxo2/arch_pybindings.cc @@ -54,8 +54,8 @@ void arch_wrap_python(py::module &m) readonly_wrapper<BelPin, decltype(&BelPin::bel), &BelPin::bel, conv_to_str<BelId>>::def_wrap(belpin_cls, "bel"); readonly_wrapper<BelPin, decltype(&BelPin::pin), &BelPin::pin, conv_to_str<IdString>>::def_wrap(belpin_cls, "pin"); - typedef const PipRange UphillPipRange; - typedef const PipRange DownhillPipRange; + typedef PipRange UphillPipRange; + typedef PipRange DownhillPipRange; typedef const std::vector<BelBucketId> &BelBucketRange; typedef const std::vector<BelId> &BelRangeForBelBucket; |