aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClifford Wolf <clifford@clifford.at>2018-09-25 18:21:56 +0200
committerGitHub <noreply@github.com>2018-09-25 18:21:56 +0200
commit07cf349ee46a8c8a3163d2f8a146beab02e6b487 (patch)
treea43f9f922d2af2ecb5ab59c1981acf5b2ab47405
parent1eb7411fb0b814c524b83dc3c16715a814db8f5d (diff)
parentdea87e46c4f316a950425504cadda56aaeeab280 (diff)
downloadnextpnr-07cf349ee46a8c8a3163d2f8a146beab02e6b487.tar.gz
nextpnr-07cf349ee46a8c8a3163d2f8a146beab02e6b487.tar.bz2
nextpnr-07cf349ee46a8c8a3163d2f8a146beab02e6b487.zip
Merge pull request #79 from YosysHQ/ice40lvds
ice40: Adding LVDS input support
-rw-r--r--ice40/arch.cc3
-rw-r--r--ice40/arch_place.cc23
-rw-r--r--ice40/archdefs.h6
-rw-r--r--ice40/bitstream.cc52
-rw-r--r--ice40/cells.cc12
-rw-r--r--ice40/cells.h2
-rw-r--r--ice40/constids.inc1
-rw-r--r--ice40/pack.cc2
8 files changed, 88 insertions, 13 deletions
diff --git a/ice40/arch.cc b/ice40/arch.cc
index 3983a24e..43a3dec2 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -959,7 +959,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 +975,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_place.cc b/ice40/arch_place.cc
index c69fd34f..b436f7d7 100644
--- a/ice40/arch_place.cc
+++ b/ice40/arch_place.cc
@@ -34,7 +34,7 @@ bool Arch::logicCellsCompatible(const CellInfo** it, const size_t size) const
int locals_count = 0;
for (auto cell : boost::make_iterator_range(it, it+size)) {
- NPNR_ASSERT(cell->belType == id_ICESTORM_LC);
+ 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..124be092 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,12 +459,20 @@ 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) {
set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), !input_en);
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
@@ -478,6 +488,33 @@ 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 +667,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);
}
diff --git a/ice40/cells.cc b/ice40/cells.cc
index 1c1e7a05..886dae2a 100644
--- a/ice40/cells.cc
+++ b/ice40/cells.cc
@@ -314,7 +314,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";
@@ -341,12 +341,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);
}
}
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/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/pack.cc b/ice40/pack.cc
index 01cb3855..07c003d1 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -424,7 +424,7 @@ static void pack_io(Context *ctx)
// 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();
}