aboutsummaryrefslogtreecommitdiffstats
path: root/ice40
diff options
context:
space:
mode:
Diffstat (limited to 'ice40')
-rw-r--r--ice40/arch.cc23
-rw-r--r--ice40/arch.h2
-rw-r--r--ice40/arch_place.cc27
-rw-r--r--ice40/archdefs.h6
-rw-r--r--ice40/bitstream.cc51
-rw-r--r--ice40/cells.cc21
-rw-r--r--ice40/cells.h2
-rw-r--r--ice40/chains.cc43
-rw-r--r--ice40/constids.inc1
-rw-r--r--ice40/delay.cc2
-rw-r--r--ice40/gfx.cc13
-rw-r--r--ice40/pack.cc83
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;