From 9834b68041f8bc015d0195303b81636047d86b1c Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 24 Sep 2018 14:27:50 +0100 Subject: [PATCH 1/4] ice40: Remove obsolete belType member Signed-off-by: David Shah --- ice40/arch.cc | 1 - ice40/arch_place.cc | 2 +- ice40/archdefs.h | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ice40/arch.cc b/ice40/arch.cc index 3983a24e..7ce967ec 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); diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc index c69fd34f..a050e39f 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; diff --git a/ice40/archdefs.h b/ice40/archdefs.h index 360617fd..8e20de23 100644 --- a/ice40/archdefs.h +++ b/ice40/archdefs.h @@ -134,7 +134,6 @@ struct NetInfo; struct ArchCellInfo { - IdString belType; union { struct From d5d9fb27a66604c89b7a2746e7ca9749b62aec91 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 24 Sep 2018 15:14:28 +0100 Subject: [PATCH 2/4] ice40: Validity check for LVDS IO Signed-off-by: David Shah --- ice40/arch.cc | 2 ++ ice40/arch_place.cc | 21 +++++++++++++++++++++ ice40/archdefs.h | 5 +++++ ice40/constids.inc | 1 + 4 files changed, 29 insertions(+) diff --git a/ice40/arch.cc b/ice40/arch.cc index 7ce967ec..43a3dec2 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -975,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 a050e39f..b436f7d7 100644 --- a/ice40/arch_place.cc +++ b/ice40/arch_place.cc @@ -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 8e20de23..c04033e7 100644 --- a/ice40/archdefs.h +++ b/ice40/archdefs.h @@ -144,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/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 From 2ee86ab5a80fdc2c49c6c6e34a2c0d31ae291c63 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 24 Sep 2018 15:25:37 +0100 Subject: [PATCH 3/4] ice40: Tristate IO support fixes Signed-off-by: David Shah --- ice40/cells.cc | 12 ++++++++---- ice40/cells.h | 2 +- ice40/pack.cc | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ice40/cells.cc b/ice40/cells.cc index e79a1fda..70b89631 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -312,7 +312,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 &todelete_cells) { if (nxio->type == ctx->id("$nextpnr_ibuf")) { sbio->params[ctx->id("PIN_TYPE")] = "1"; @@ -339,12 +339,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 &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/pack.cc b/ice40/pack.cc index 7c853e0e..feca8a77 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 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(); } From dea87e46c4f316a950425504cadda56aaeeab280 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 24 Sep 2018 17:58:55 +0100 Subject: [PATCH 4/4] ice40: LVDS input bitstream support Signed-off-by: David Shah --- ice40/bitstream.cc | 52 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-) 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); }