diff options
Diffstat (limited to 'ice40')
-rw-r--r-- | ice40/arch.cc | 23 | ||||
-rw-r--r-- | ice40/arch.h | 2 | ||||
-rw-r--r-- | ice40/arch_place.cc | 27 | ||||
-rw-r--r-- | ice40/archdefs.h | 6 | ||||
-rw-r--r-- | ice40/bitstream.cc | 51 | ||||
-rw-r--r-- | ice40/cells.cc | 21 | ||||
-rw-r--r-- | ice40/cells.h | 2 | ||||
-rw-r--r-- | ice40/chains.cc | 43 | ||||
-rw-r--r-- | ice40/constids.inc | 1 | ||||
-rw-r--r-- | ice40/delay.cc | 2 | ||||
-rw-r--r-- | ice40/gfx.cc | 13 | ||||
-rw-r--r-- | ice40/pack.cc | 83 |
12 files changed, 189 insertions, 85 deletions
diff --git a/ice40/arch.cc b/ice40/arch.cc index 3983a24e..eb26ae5a 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -414,7 +414,6 @@ std::vector<std::pair<IdString, std::string>> Arch::getPipAttrs(PipId pip) const return ret; } - // ----------------------------------------------------------------------- BelId Arch::getPackagePinBel(const std::string &pin) const @@ -866,15 +865,22 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id return TMG_COMB_INPUT; if (port == id_COUT || port == id_LO) return TMG_COMB_OUTPUT; - if (cell->lcInfo.dffEnable) { - clockPort = id_CLK; - if (port == id_O) + if (port == id_O) { + // LCs with no inputs are constant drivers + if (cell->lcInfo.inputCount == 0) + return TMG_IGNORE; + if (cell->lcInfo.dffEnable) { + clockPort = id_CLK; return TMG_REGISTER_OUTPUT; + } else - return TMG_REGISTER_INPUT; - } else { - if (port == id_O) return TMG_COMB_OUTPUT; + } + else { + if (cell->lcInfo.dffEnable) { + clockPort = id_CLK; + return TMG_REGISTER_INPUT; + } else return TMG_COMB_INPUT; } @@ -959,7 +965,6 @@ void Arch::assignArchInfo() void Arch::assignCellInfo(CellInfo *cell) { - cell->belType = cell->type; if (cell->type == id_ICESTORM_LC) { cell->lcInfo.dffEnable = bool_or_default(cell->params, id_DFF_ENABLE); cell->lcInfo.carryEnable = bool_or_default(cell->params, id_CARRY_ENABLE); @@ -976,6 +981,8 @@ void Arch::assignCellInfo(CellInfo *cell) cell->lcInfo.inputCount++; if (get_net_or_empty(cell, id_I3)) cell->lcInfo.inputCount++; + } else if (cell->type == id_SB_IO) { + cell->ioInfo.lvds = str_or_default(cell->params, id_IO_STANDARD, "SB_LVCMOS") == "SB_LVDS_INPUT"; } } diff --git a/ice40/arch.h b/ice40/arch.h index 37f663d9..27d5db9f 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -817,7 +817,7 @@ struct Arch : BaseCtx bool isBelLocationValid(BelId bel) const; // Helper function for above - bool logicCellsCompatible(const CellInfo** it, const size_t size) const; + bool logicCellsCompatible(const CellInfo **it, const size_t size) const; // ------------------------------------------------- // Assign architecure-specific arguments to nets and cells, which must be diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc index c69fd34f..c97b9c26 100644 --- a/ice40/arch_place.cc +++ b/ice40/arch_place.cc @@ -27,14 +27,14 @@ NEXTPNR_NAMESPACE_BEGIN -bool Arch::logicCellsCompatible(const CellInfo** it, const size_t size) const +bool Arch::logicCellsCompatible(const CellInfo **it, const size_t size) const { bool dffs_exist = false, dffs_neg = false; const NetInfo *cen = nullptr, *clk = nullptr, *sr = nullptr; int locals_count = 0; - for (auto cell : boost::make_iterator_range(it, it+size)) { - NPNR_ASSERT(cell->belType == id_ICESTORM_LC); + for (auto cell : boost::make_iterator_range(it, it + size)) { + NPNR_ASSERT(cell->type == id_ICESTORM_LC); if (cell->lcInfo.dffEnable) { if (!dffs_exist) { dffs_exist = true; @@ -139,6 +139,27 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const } } } + Loc ioLoc = getBelLocation(bel); + Loc compLoc = ioLoc; + compLoc.z = 1 - compLoc.z; + + // Check LVDS pairing + if (cell->ioInfo.lvds) { + // Check correct z and complement location is free + if (ioLoc.z != 0) + return false; + BelId compBel = getBelByLocation(compLoc); + CellInfo *compCell = getBoundBelCell(compBel); + if (compCell) + return false; + } else { + // Check LVDS IO is not placed at complement location + BelId compBel = getBelByLocation(compLoc); + CellInfo *compCell = getBoundBelCell(compBel); + if (compCell && compCell->ioInfo.lvds) + return false; + } + return getBelPackagePin(bel) != ""; } else if (cell->type == id_SB_GB) { NPNR_ASSERT(cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net != nullptr); diff --git a/ice40/archdefs.h b/ice40/archdefs.h index 360617fd..c04033e7 100644 --- a/ice40/archdefs.h +++ b/ice40/archdefs.h @@ -134,7 +134,6 @@ struct NetInfo; struct ArchCellInfo { - IdString belType; union { struct @@ -145,6 +144,11 @@ struct ArchCellInfo int inputCount; const NetInfo *clk, *cen, *sr; } lcInfo; + struct + { + bool lvds; + // TODO: clk packing checks... + } ioInfo; }; }; diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 4ea91011..e56ed37d 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -447,6 +447,8 @@ void write_asc(const Context *ctx, std::ostream &out) unsigned pin_type = get_param_or_def(cell.second.get(), ctx->id("PIN_TYPE"));
bool neg_trigger = get_param_or_def(cell.second.get(), ctx->id("NEG_TRIGGER"));
bool pullup = get_param_or_def(cell.second.get(), ctx->id("PULLUP"));
+ bool lvds = get_param_str_or_def(cell.second.get(), ctx->id("IO_STANDARD")) == "SB_LVDS_INPUT";
+
for (int i = 0; i < 6; i++) {
bool val = (pin_type >> i) & 0x01;
set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val);
@@ -457,10 +459,17 @@ void write_asc(const Context *ctx, std::ostream &out) std::tie(iex, iey, iez) = ieren;
NPNR_ASSERT(iez != -1);
- bool input_en = false;
- if ((ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_0).index] != nullptr) ||
- (ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_1).index] != nullptr)) {
- input_en = true;
+ bool input_en;
+ if (lvds) {
+ input_en = false;
+ pullup = false;
+ } else {
+ if ((ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_0).index] != nullptr) ||
+ (ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_1).index] != nullptr)) {
+ input_en = true;
+ } else {
+ input_en = false;
+ }
}
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
@@ -478,6 +487,31 @@ void write_asc(const Context *ctx, std::ostream &out) set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35", !pullup);
}
}
+
+ if (lvds) {
+ NPNR_ASSERT(z == 0);
+ set_config(ti, config.at(y).at(x), "IoCtrl.LVDS", true);
+ // Set comp IO config
+ auto comp_ieren = get_ieren(bi, x, y, 1);
+ int ciex, ciey, ciez;
+ std::tie(ciex, ciey, ciez) = comp_ieren;
+
+ if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
+ set_config(ti, config.at(ciey).at(ciex), "IoCtrl.IE_" + std::to_string(ciez), !input_en);
+ set_config(ti, config.at(ciey).at(ciex), "IoCtrl.REN_" + std::to_string(ciez), !pullup);
+ } else {
+ set_config(ti, config.at(ciey).at(ciex), "IoCtrl.IE_" + std::to_string(ciez), input_en);
+ set_config(ti, config.at(ciey).at(ciex), "IoCtrl.REN_" + std::to_string(ciez), !pullup);
+ }
+
+ if (ctx->args.type == ArchArgs::UP5K) {
+ if (ciez == 0) {
+ set_config(ti, config.at(ciey).at(ciex), "IoCtrl.cf_bit_39", !pullup);
+ } else if (iez == 1) {
+ set_config(ti, config.at(ciey).at(ciex), "IoCtrl.cf_bit_35", !pullup);
+ }
+ }
+ }
} else if (cell.second->type == ctx->id("SB_GB")) {
// no cell config bits
} else if (cell.second->type == ctx->id("ICESTORM_RAM")) {
@@ -630,6 +664,13 @@ void write_asc(const Context *ctx, std::ostream &out) int iex, iey, iez;
std::tie(iex, iey, iez) = ieren;
if (iez != -1) {
+ // IO is not actually unused if part of an LVDS pair
+ if (z == 1) {
+ BelId lvds0 = ctx->getBelByLocation(Loc{x, y, 0});
+ const CellInfo *lvds0cell = ctx->getBoundBelCell(lvds0);
+ if (lvds0cell != nullptr && lvds0cell->ioInfo.lvds)
+ continue;
+ }
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
}
@@ -870,7 +911,7 @@ bool read_asc(Context *ctx, std::istream &in) }
if (isUsed) {
NetInfo *net = ctx->wire_to_net[pi.dst];
- if (net!=nullptr) {
+ if (net != nullptr) {
WireId wire;
wire.index = pi.dst;
ctx->unbindWire(wire);
diff --git a/ice40/cells.cc b/ice40/cells.cc index e79a1fda..fbb77b0c 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -230,7 +230,8 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri new_cell->params[ctx->id("TEST_MODE")] = "0"; add_port(ctx, new_cell.get(), "BYPASS", PORT_IN); - add_port(ctx, new_cell.get(), "DYNAMICDELAY", PORT_IN); + for (int i = 0; i < 8; i++) + add_port(ctx, new_cell.get(), "DYNAMICDELAY_" + std::to_string(i), PORT_IN); add_port(ctx, new_cell.get(), "EXTFEEDBACK", PORT_IN); add_port(ctx, new_cell.get(), "LATCHINPUTVALUE", PORT_IN); add_port(ctx, new_cell.get(), "REFERENCECLK", PORT_IN); @@ -243,6 +244,8 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri add_port(ctx, new_cell.get(), "LOCK", PORT_OUT); add_port(ctx, new_cell.get(), "PLLOUT_A", PORT_OUT); add_port(ctx, new_cell.get(), "PLLOUT_B", PORT_OUT); + add_port(ctx, new_cell.get(), "PLLOUTGLOBALA", PORT_OUT); + add_port(ctx, new_cell.get(), "PLLOUTGLOBALB", PORT_OUT); } else { log_error("unable to create iCE40 cell of type %s", type.c_str(ctx)); } @@ -312,7 +315,7 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l replace_port(dff, ctx->id("Q"), lc, ctx->id("O")); } -void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio) +void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, std::unordered_set<IdString> &todelete_cells) { if (nxio->type == ctx->id("$nextpnr_ibuf")) { sbio->params[ctx->id("PIN_TYPE")] = "1"; @@ -339,12 +342,16 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio) sbio->params[ctx->id("PIN_TYPE")] = "41"; replace_port(tbuf, ctx->id("A"), sbio, ctx->id("D_OUT_0")); replace_port(tbuf, ctx->id("E"), sbio, ctx->id("OUTPUT_ENABLE")); - ctx->nets.erase(donet->name); - if (!donet->users.empty()) + + if (donet->users.size() > 1) { + for (auto user : donet->users) + log_info(" remaining tristate user: %s.%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx)); log_error("unsupported tristate IO pattern for IO buffer '%s', " "instantiate SB_IO manually to ensure correct behaviour\n", nxio->name.c_str(ctx)); - ctx->cells.erase(tbuf->name); + } + ctx->nets.erase(donet->name); + todelete_cells.insert(tbuf->name); } } @@ -376,6 +383,10 @@ bool is_clock_port(const BaseCtx *ctx, const PortRef &port) port.port == ctx->id("WCLKN"); if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP")) return port.port == ctx->id("CLK"); + if (is_sb_spram(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_SPRAM")) + return port.port == id_CLOCK; + if (is_sb_io(ctx, port.cell)) + return port.port == id_INPUT_CLK || port.port == id_OUTPUT_CLK; return false; } diff --git a/ice40/cells.h b/ice40/cells.h index 16135448..054388ac 100644 --- a/ice40/cells.h +++ b/ice40/cells.h @@ -98,7 +98,7 @@ void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff = tr void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut = false); // Convert a nextpnr IO buffer to a SB_IO -void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio); +void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, std::unordered_set<IdString> &todelete_cells); // Return true if a port is a clock port bool is_clock_port(const BaseCtx *ctx, const PortRef &port); diff --git a/ice40/chains.cc b/ice40/chains.cc index bb20b60b..fb361d2d 100644 --- a/ice40/chains.cc +++ b/ice40/chains.cc @@ -21,6 +21,7 @@ #include <algorithm> #include <vector> #include "cells.h" +#include "chain_utils.h" #include "design_utils.h" #include "log.h" #include "place_common.h" @@ -28,45 +29,6 @@ NEXTPNR_NAMESPACE_BEGIN -struct CellChain -{ - std::vector<CellInfo *> cells; -}; - -// Generic chain finder -template <typename F1, typename F2, typename F3> -std::vector<CellChain> find_chains(const Context *ctx, F1 cell_type_predicate, F2 get_previous, F3 get_next, - size_t min_length = 2) -{ - std::set<IdString> chained; - std::vector<CellChain> chains; - for (auto cell : sorted(ctx->cells)) { - if (chained.find(cell.first) != chained.end()) - continue; - CellInfo *ci = cell.second; - if (cell_type_predicate(ctx, ci)) { - CellInfo *start = ci; - CellInfo *prev_start = ci; - while (prev_start != nullptr) { - start = prev_start; - prev_start = get_previous(ctx, start); - } - CellChain chain; - CellInfo *end = start; - while (end != nullptr) { - chain.cells.push_back(end); - end = get_next(ctx, end); - } - if (chain.cells.size() >= min_length) { - chains.push_back(chain); - for (auto c : chain.cells) - chained.insert(c->name); - } - } - } - return chains; -} - class ChainConstrainer { private: @@ -97,7 +59,8 @@ class ChainConstrainer } tile.push_back(cell); chains.back().cells.push_back(cell); - bool split_chain = (!ctx->logicCellsCompatible(tile.data(), tile.size())) || (int(chains.back().cells.size()) > max_length); + bool split_chain = (!ctx->logicCellsCompatible(tile.data(), tile.size())) || + (int(chains.back().cells.size()) > max_length); if (split_chain) { CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT"))); tile.pop_back(); diff --git a/ice40/constids.inc b/ice40/constids.inc index adcea7ad..dad08e59 100644 --- a/ice40/constids.inc +++ b/ice40/constids.inc @@ -435,3 +435,4 @@ X(ICESTORM_SPRAM) X(DFF_ENABLE) X(CARRY_ENABLE) X(NEG_CLK) +X(IO_STANDARD)
\ No newline at end of file diff --git a/ice40/delay.cc b/ice40/delay.cc index d76aaefb..54905551 100644 --- a/ice40/delay.cc +++ b/ice40/delay.cc @@ -121,7 +121,7 @@ struct model_params_t int delta_sp4; int delta_sp12; - static const model_params_t &get(const ArchArgs& args) + static const model_params_t &get(const ArchArgs &args) { static const model_params_t model_hx8k = {588, 129253, 8658, 118333, 23915, -73105, 57696, -86797, 89, 3706, -316, -575, -158, -296}; diff --git a/ice40/gfx.cc b/ice40/gfx.cc index 74338b8d..c41c424b 100644 --- a/ice40/gfx.cc +++ b/ice40/gfx.cc @@ -21,7 +21,8 @@ NEXTPNR_NAMESPACE_BEGIN -void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, int w, int h, GfxTileWireId id, GraphicElement::style_t style) +void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, int w, int h, GfxTileWireId id, + GraphicElement::style_t style) { GraphicElement el; el.type = GraphicElement::TYPE_LINE; @@ -462,7 +463,7 @@ void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, int w, int h, Gfx g.push_back(el); } - if (idx <= 15 && (x == 0 || x == w-1) && y == 1) { + if (idx <= 15 && (x == 0 || x == w - 1) && y == 1) { float y1 = y - (0.03 + 0.0025 * (60 - idx - 4)); el.x1 = x2; @@ -478,7 +479,7 @@ void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, int w, int h, Gfx g.push_back(el); } - if (idx >= 4 && (x == 0 || x == w-1) && y == h-2) { + if (idx >= 4 && (x == 0 || x == w - 1) && y == h - 2) { float y1 = y + 2.0 - (0.03 + 0.0025 * (60 - idx)); el.x1 = x1; @@ -901,7 +902,7 @@ static bool getWireXY_local(GfxTileWireId id, float &x, float &y) if (id >= TILE_WIRE_LUTFF_0_IN_0 && id <= TILE_WIRE_LUTFF_7_IN_3) { int idx = id - TILE_WIRE_LUTFF_0_IN_0; int z = idx / 4; - int input = idx % 4; + int input = 3 - idx % 4; x = local_swbox_x2; y = (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch; return true; @@ -971,11 +972,11 @@ void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, return; } - if (getWireXY_local(src, x1, y1) && getWireXY_local(dst, x2, y2)) { + if (getWireXY_local(src, x1, y1) && getWireXY_local(dst, x2, y2)) { pipGfx(g, x, y, x1, y1, x2, y2, local_swbox_x1, local_swbox_y1, local_swbox_x2, local_swbox_y2, style); return; } - + if (TILE_WIRE_LUTFF_0_IN_0_LUT <= src && src <= TILE_WIRE_LUTFF_7_IN_3_LUT && TILE_WIRE_LUTFF_0_OUT <= dst && dst <= TILE_WIRE_LUTFF_7_OUT) { int lut_idx = (src - TILE_WIRE_LUTFF_0_IN_0_LUT) / 4; diff --git a/ice40/pack.cc b/ice40/pack.cc index 7c853e0e..edd12f92 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -159,7 +159,7 @@ static void pack_carries(Context *ctx) exhausted_cells.find(usr.cell->name) == exhausted_cells.end()) { // This clause stops us double-packing cells i0_matches.insert(usr.cell->name); - if (!i1_net) { + if (!i1_net && !usr.cell->ports.at(ctx->id("I2")).net) { // I1 is don't care when disconnected, duplicate I0 i1_matches.insert(usr.cell->name); } @@ -174,7 +174,7 @@ static void pack_carries(Context *ctx) exhausted_cells.find(usr.cell->name) == exhausted_cells.end()) { // This clause stops us double-packing cells i1_matches.insert(usr.cell->name); - if (!i0_net) { + if (!i0_net && !usr.cell->ports.at(ctx->id("I1")).net) { // I0 is don't care when disconnected, duplicate I1 i0_matches.insert(usr.cell->name); } @@ -391,6 +391,8 @@ static bool is_nextpnr_iob(Context *ctx, CellInfo *cell) static void pack_io(Context *ctx) { std::unordered_set<IdString> packed_cells; + std::unordered_set<IdString> delete_nets; + std::vector<std::unique_ptr<CellInfo>> new_cells; log_info("Packing IOs..\n"); @@ -410,21 +412,27 @@ static void pack_io(Context *ctx) log_info("%s feeds SB_IO %s, removing %s %s.\n", ci->name.c_str(ctx), sb->name.c_str(ctx), ci->type.c_str(ctx), ci->name.c_str(ctx)); NetInfo *net = sb->ports.at(ctx->id("PACKAGE_PIN")).net; + if (((ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) && + net->users.size() > 1) || + (ci->type == ctx->id("$nextpnr_obuf") && (net->users.size() > 2 || net->driver.cell != nullptr))) + log_error("PACKAGE_PIN of SB_IO '%s' connected to more than a single top level IO.\n", + sb->name.c_str(ctx)); + if (net != nullptr) { - ctx->nets.erase(net->name); + delete_nets.insert(net->name); sb->ports.at(ctx->id("PACKAGE_PIN")).net = nullptr; } if (ci->type == ctx->id("$nextpnr_iobuf")) { NetInfo *net2 = ci->ports.at(ctx->id("I")).net; if (net2 != nullptr) { - ctx->nets.erase(net2->name); + delete_nets.insert(net2->name); } } } else { // Create a SB_IO buffer std::unique_ptr<CellInfo> ice_cell = create_ice_cell(ctx, ctx->id("SB_IO"), ci->name.str(ctx) + "$sb_io"); - nxio_to_sb(ctx, ci, ice_cell.get()); + nxio_to_sb(ctx, ci, ice_cell.get(), packed_cells); new_cells.push_back(std::move(ice_cell)); sb = new_cells.back().get(); } @@ -435,6 +443,9 @@ static void pack_io(Context *ctx) for (auto pcell : packed_cells) { ctx->cells.erase(pcell); } + for (auto dnet : delete_nets) { + ctx->nets.erase(dnet); + } for (auto &ncell : new_cells) { ctx->cells[ncell->name] = std::move(ncell); } @@ -451,6 +462,8 @@ static bool is_logic_port(BaseCtx *ctx, const PortRef &port) static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen, bool is_logic) { + log_info("promoting %s%s%s%s\n", net->name.c_str(ctx), is_reset ? " [reset]" : "", is_cen ? " [cen]" : "", is_logic ? " [logic]" : ""); + std::string glb_name = net->name.str(ctx) + std::string("_$glb_") + (is_reset ? "sr" : (is_cen ? "ce" : "clk")); std::unique_ptr<CellInfo> gb = create_ice_cell(ctx, ctx->id("SB_GB"), "$gbuf_" + glb_name); gb->ports[ctx->id("USER_SIGNAL_TO_GLOBAL_BUFFER")].net = net; @@ -717,6 +730,32 @@ static void pack_special(Context *ctx) NetInfo *pad_packagepin_net = nullptr; + int pllout_a_used = 0; + int pllout_b_used = 0; + for (auto port : ci->ports) { + PortInfo &pi = port.second; + if (pi.name == ctx->id("PLLOUTCOREA")) + pllout_a_used++; + if (pi.name == ctx->id("PLLOUTCOREB")) + pllout_b_used++; + if (pi.name == ctx->id("PLLOUTCORE")) + pllout_a_used++; + if (pi.name == ctx->id("PLLOUTGLOBALA")) + pllout_a_used++; + if (pi.name == ctx->id("PLLOUTGLOBALB")) + pllout_b_used++; + if (pi.name == ctx->id("PLLOUTGLOBAL")) + pllout_a_used++; + } + + if (pllout_a_used > 1) + log_error("PLL '%s' is using multiple ports mapping to PLLOUT_A output of the PLL\n", + ci->name.c_str(ctx)); + + if (pllout_b_used > 1) + log_error("PLL '%s' is using multiple ports mapping to PLLOUT_B output of the PLL\n", + ci->name.c_str(ctx)); + for (auto port : ci->ports) { PortInfo &pi = port.second; std::string newname = pi.name.str(ctx); @@ -730,10 +769,22 @@ static void pack_special(Context *ctx) newname = "PLLOUT_B"; if (pi.name == ctx->id("PLLOUTCORE")) newname = "PLLOUT_A"; + if (pi.name == ctx->id("PLLOUTGLOBALA")) + newname = "PLLOUT_A"; + if (pi.name == ctx->id("PLLOUTGLOBALB")) + newname = "PLLOUT_B"; + if (pi.name == ctx->id("PLLOUTGLOBAL")) + newname = "PLLOUT_A"; + + if (pi.name == ctx->id("PLLOUTGLOBALA") || pi.name == ctx->id("PLLOUTGLOBALB") || + pi.name == ctx->id("PLLOUTGLOBAL")) + log_warning("PLL '%s' is using port %s but implementation does not actually " + "use the global clock output of the PLL\n", + ci->name.c_str(ctx), pi.name.str(ctx).c_str()); if (pi.name == ctx->id("PACKAGEPIN")) { if (!is_pad) { - log_error("PLL '%s' has a PACKAGEPIN but is not a PAD PLL", ci->name.c_str(ctx)); + log_error("PLL '%s' has a PACKAGEPIN but is not a PAD PLL\n", ci->name.c_str(ctx)); } else { // We drop this port and instead place the PLL adequately below. pad_packagepin_net = port.second.net; @@ -743,18 +794,21 @@ static void pack_special(Context *ctx) } if (pi.name == ctx->id("REFERENCECLK")) { if (!is_core) - log_error("PLL '%s' has a REFERENCECLK but is not a CORE PLL", ci->name.c_str(ctx)); + log_error("PLL '%s' has a REFERENCECLK but is not a CORE PLL\n", ci->name.c_str(ctx)); } if (packed->ports.count(ctx->id(newname)) == 0) { if (ci->ports[pi.name].net == nullptr) { - log_warning("PLL '%s' has unknown unconnected port '%s' - ignoring\n", ci->name.c_str(ctx), pi.name.c_str(ctx)); + log_warning("PLL '%s' has unknown unconnected port '%s' - ignoring\n", ci->name.c_str(ctx), + pi.name.c_str(ctx)); continue; } else { if (ctx->force) { - log_error("PLL '%s' has unknown connected port '%s'\n", ci->name.c_str(ctx), pi.name.c_str(ctx)); + log_error("PLL '%s' has unknown connected port '%s'\n", ci->name.c_str(ctx), + pi.name.c_str(ctx)); } else { - log_warning("PLL '%s' has unknown connected port '%s' - ignoring\n", ci->name.c_str(ctx), pi.name.c_str(ctx)); + log_warning("PLL '%s' has unknown connected port '%s' - ignoring\n", ci->name.c_str(ctx), + pi.name.c_str(ctx)); continue; } } @@ -806,13 +860,15 @@ static void pack_special(Context *ctx) packagepin_cell->ports.erase(pll_packagepin_driver.port); } - log_info(" constrained PLL '%s' to %s\n", packed->name.c_str(ctx), ctx->getBelName(bel).c_str(ctx)); + log_info(" constrained PLL '%s' to %s\n", packed->name.c_str(ctx), + ctx->getBelName(bel).c_str(ctx)); packed->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx); pll_bel = bel; constrained = true; } if (!constrained) { - log_error("Could not constrain PLL '%s' to any PLL Bel (too many PLLs?)\n", packed->name.c_str(ctx)); + log_error("Could not constrain PLL '%s' to any PLL Bel (too many PLLs?)\n", + packed->name.c_str(ctx)); } } @@ -831,8 +887,7 @@ static void pack_special(Context *ctx) // If we have a net connected to LOCK, make sure it only drives LUTs. auto port = packed->ports[ctx->id("LOCK")]; if (port.net != nullptr) { - log_info(" PLL '%s' has LOCK output, need to pass all outputs via LUT\n", - ci->name.c_str(ctx)); + log_info(" PLL '%s' has LOCK output, need to pass all outputs via LUT\n", ci->name.c_str(ctx)); bool found_lut = false; bool all_luts = true; unsigned int lut_count = 0; |