aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rw-r--r--ecp5/lpf.cc9
-rw-r--r--ecp5/pack.cc178
-rw-r--r--generic/viaduct/fabulous/fabric_parsing.h14
-rw-r--r--generic/viaduct/fabulous/fabulous.cc17
-rw-r--r--gowin/arch.cc120
-rw-r--r--gowin/arch.h7
-rw-r--r--gowin/constids.inc2
-rw-r--r--gowin/pack.cc3
-rw-r--r--ice40/bitstream.cc33
-rw-r--r--ice40/cells.cc3
-rw-r--r--ice40/constids.inc2
-rw-r--r--ice40/pack.cc6
-rw-r--r--machxo2/arch_pybindings.cc4
14 files changed, 316 insertions, 88 deletions
diff --git a/README.md b/README.md
index 957be889..47201dc5 100644
--- a/README.md
+++ b/README.md
@@ -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;