From 42fbb110fcea1ae05ba48433df86d6381715daeb Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 16 Nov 2018 22:53:53 +0100 Subject: [PATCH 01/20] ice40/bitstream: Handle IoCtrl.IE_ polarity when configuring unused SB_IO Signed-off-by: Sylvain Munaut --- ice40/bitstream.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 4e54df1d..5d138798 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -671,8 +671,13 @@ void write_asc(const Context *ctx, std::ostream &out) 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); + 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), true); + set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false); + } else { + set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), false); + set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false); + } } } else if (ctx->bel_to_cell[bel.index] == nullptr && ctx->getBelType(bel) == id_ICESTORM_RAM) { const BelInfoPOD &beli = ci.bel_data[bel.index]; From 70e1fe423f663a3a02233e4f1c1f4f917d14c23c Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sat, 17 Nov 2018 10:18:45 +0100 Subject: [PATCH 02/20] ice40/chipdb: Fix LOCKED keyword support to include all packages Signed-off-by: Sylvain Munaut --- ice40/chipdb.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ice40/chipdb.py b/ice40/chipdb.py index 5b2f3e57..2009483f 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -692,7 +692,8 @@ with open(args.filename, "r") as f: if mode[0] == "extra_cell": if line[0] == "LOCKED": - extra_cells[mode[1]].append((("LOCKED_" + line[1]), (0, 0, "LOCKED"))) + for pkg in line[1:]: + extra_cells[mode[1]].append((("LOCKED_" + pkg), (0, 0, "LOCKED"))) else: extra_cells[mode[1]].append((line[0], (int(line[1]), int(line[2]), line[3]))) continue From b29165eeba41317f160af7e386739ff5a31a8bb9 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sat, 17 Nov 2018 10:18:17 +0100 Subject: [PATCH 03/20] ice40/arch: Add helper to check if a BEL is LOCKED or not Signed-off-by: Sylvain Munaut --- ice40/arch.cc | 19 +++++++++++++++++++ ice40/arch.h | 2 ++ 2 files changed, 21 insertions(+) diff --git a/ice40/arch.cc b/ice40/arch.cc index 2a9e167b..995150ac 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -284,6 +284,25 @@ std::vector Arch::getBelPins(BelId bel) const return ret; } +bool Arch::isBelLocked(BelId bel) const +{ + const BelConfigPOD *bel_config = nullptr; + for (int i = 0; i < chip_info->num_belcfgs; i++) { + if (chip_info->bel_config[i].bel_index == bel.index) { + bel_config = &chip_info->bel_config[i]; + break; + } + } + NPNR_ASSERT(bel_config != nullptr); + for (int i = 0; i < bel_config->num_entries; i++) { + if (strcmp("LOCKED", bel_config->entries[i].cbit_name.get())) + continue; + if ("LOCKED_" + archArgs().package == bel_config->entries[i].entry_name.get()) + return true; + } + return false; +} + // ----------------------------------------------------------------------- WireId Arch::getWireByName(IdString name) const diff --git a/ice40/arch.h b/ice40/arch.h index 836dc46e..b6f10c03 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -510,6 +510,8 @@ struct Arch : BaseCtx PortType getBelPinType(BelId bel, IdString pin) const; std::vector getBelPins(BelId bel) const; + bool isBelLocked(BelId bel) const; + // ------------------------------------------------- WireId getWireByName(IdString name) const; From 8c69a3bba3d72dd445f1003d470c3ed5ed06c5d4 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sat, 17 Nov 2018 10:19:08 +0100 Subject: [PATCH 04/20] ice40/pack: Make sure we don't use a LOCKED bel when placing PLL Signed-off-by: Sylvain Munaut --- ice40/pack.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ice40/pack.cc b/ice40/pack.cc index e0a9f6ad..d0142fa3 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -838,6 +838,8 @@ static void pack_special(Context *ctx) for (auto bel : ctx->getBels()) { if (ctx->getBelType(bel) != id_ICESTORM_PLL) continue; + if (ctx->isBelLocked(bel)) + continue; // A PAD PLL must have its' PACKAGEPIN on the SB_IO that's shared // with PLLOUT_A. From 5fb3353557853178c2787ea8a80afddf5ace8a4d Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 16 Nov 2018 22:55:57 +0100 Subject: [PATCH 05/20] ice40/pack: Allow PLL to be constrained via 'BEL' attributes Signed-off-by: Sylvain Munaut --- ice40/pack.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ice40/pack.cc b/ice40/pack.cc index d0142fa3..c7614830 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -886,6 +886,16 @@ static void pack_special(Context *ctx) log_error("Could not constrain PLL '%s' to any PLL Bel (too many PLLs?)\n", packed->name.c_str(ctx)); } + } else { + pll_bel = ctx->getBelByName(ctx->id(packed->attrs[ctx->id("BEL")])); + if (ctx->getBelType(pll_bel) != id_ICESTORM_PLL) + log_error("PLL '%s' is constrained to BEL %s which isn't a ICESTORM_PLL BEL\n", + packed->name.c_str(ctx), ctx->getBelName(pll_bel).c_str(ctx)); + if (ctx->isBelLocked(pll_bel)) + log_error("PLL '%s' is constrained to locked BEL %s\n", packed->name.c_str(ctx), + ctx->getBelName(pll_bel).c_str(ctx)); + log_info(" constrained PLL '%s' to %s\n", packed->name.c_str(ctx), + ctx->getBelName(pll_bel).c_str(ctx)); } // Delete the original PACKAGEPIN net if needed. From ac5d767d4fc96a02cfcf5f06930c1aa5f41c97b4 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 16 Nov 2018 22:55:31 +0100 Subject: [PATCH 06/20] ice40/pack: Stop looking for BEL when we have one during PLL placement Ideally we should first process all the PLL that are constrained somehow (either explicitely or because they are PAD) and then free place the rest. Signed-off-by: Sylvain Munaut --- ice40/pack.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/ice40/pack.cc b/ice40/pack.cc index c7614830..c91c97be 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -881,6 +881,7 @@ static void pack_special(Context *ctx) packed->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx); pll_bel = bel; constrained = true; + break; } if (!constrained) { log_error("Could not constrain PLL '%s' to any PLL Bel (too many PLLs?)\n", From 35e9ec773776354c7a5bf2d5d2692c78111b603c Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sun, 18 Nov 2018 19:16:08 +0100 Subject: [PATCH 07/20] ice40: Minor fix in predicate checking for logic port - is_sb_pll40 covers all the PLL types - Use helper to test for gbuf Signed-off-by: Sylvain Munaut --- ice40/pack.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ice40/pack.cc b/ice40/pack.cc index c91c97be..ece352a7 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -461,8 +461,9 @@ static bool is_logic_port(BaseCtx *ctx, const PortRef &port) { if (is_clock_port(ctx, port) || is_reset_port(ctx, port) || is_enable_port(ctx, port)) return false; - return !is_sb_io(ctx, port.cell) && !is_sb_pll40(ctx, port.cell) && !is_sb_pll40_pad(ctx, port.cell) && - port.cell->type != ctx->id("SB_GB"); + return !is_sb_io(ctx, port.cell) && + !is_gbuf(ctx, port.cell) && + !is_sb_pll40(ctx, port.cell); } static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen, bool is_logic) From 78f3c2c37dbb007830704c5038c524991d171381 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 16 Nov 2018 23:35:13 +0100 Subject: [PATCH 08/20] ice40: Make PLL default FEEDBACK_MODE to SIMPLE Signed-off-by: Sylvain Munaut --- ice40/cells.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ice40/cells.cc b/ice40/cells.cc index fbb77b0c..35255580 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -219,7 +219,7 @@ std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::stri new_cell->params[ctx->id("FDA_FEEDBACK")] = "0"; new_cell->params[ctx->id("FDA_RELATIVE")] = "0"; - new_cell->params[ctx->id("FEEDBACK_PATH")] = "0"; + new_cell->params[ctx->id("FEEDBACK_PATH")] = "1"; new_cell->params[ctx->id("FILTER_RANGE")] = "0"; new_cell->params[ctx->id("PLLOUT_SELECT_A")] = "0"; From ad23caef33ed768f1e11b29e3c4c10edabd1c836 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sun, 18 Nov 2018 16:11:40 +0100 Subject: [PATCH 09/20] ice40/pll: Add proper support for PLLOUT_SELECT_xxx attributes Signed-off-by: Sylvain Munaut --- ice40/pack.cc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ice40/pack.cc b/ice40/pack.cc index ece352a7..d689fa69 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -733,6 +733,24 @@ static void pack_special(Context *ctx) for (auto param : ci->params) packed->params[param.first] = param.second; + const std::map pos_map_name = { + {ctx->id("PLLOUT_SELECT"), ctx->id("PLLOUT_SELECT_A")}, + {ctx->id("PLLOUT_SELECT_PORTA"), ctx->id("PLLOUT_SELECT_A")}, + {ctx->id("PLLOUT_SELECT_PORTB"), ctx->id("PLLOUT_SELECT_B")}, + }; + const std::map pos_map_val = { + {"GENCLK", 0}, + {"GENCLK_HALF", 1}, + {"SHIFTREG_90deg", 2}, + {"SHIFTREG_0deg", 3}, + }; + for (auto param : ci->params) + if (pos_map_name.find(param.first) != pos_map_name.end()) { + if (pos_map_val.find(param.second) == pos_map_val.end()) + log_error("Invalid PLL output selection '%s'\n", param.second.c_str()); + packed->params[pos_map_name.at(param.first)] = std::to_string(pos_map_val.at(param.second)); + } + auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")]; packed->params[ctx->id("FEEDBACK_PATH")] = feedback_path == "DELAY" From f6d60229844d95ab94666629d2f0284b2f524227 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sun, 18 Nov 2018 16:11:14 +0100 Subject: [PATCH 10/20] ice40: Fix PLLTYPE for SB_PLL40_2F_PAD Signed-off-by: Sylvain Munaut --- ice40/cells.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ice40/cells.cc b/ice40/cells.cc index 35255580..3906334f 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -362,7 +362,7 @@ uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell) if (cell->type == ctx->id("SB_PLL40_2_PAD")) return 4; if (cell->type == ctx->id("SB_PLL40_2F_PAD")) - return 5; + return 6; if (cell->type == ctx->id("SB_PLL40_CORE")) return 3; if (cell->type == ctx->id("SB_PLL40_2F_CORE")) From 9483a95a4adfe9f9715a0066770e12f8b581185e Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sun, 18 Nov 2018 19:19:07 +0100 Subject: [PATCH 11/20] ice40: Improve the is_sb_pll40_XXX predicates collection - Add a test for dual output PLL variant - Make them handle the packet version of the cell This will become useful for various tests during PLL rework Signed-off-by: Sylvain Munaut --- ice40/cells.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ice40/cells.h b/ice40/cells.h index 054388ac..7f349020 100644 --- a/ice40/cells.h +++ b/ice40/cells.h @@ -81,7 +81,19 @@ inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell) inline bool is_sb_pll40_pad(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") || - cell->type == ctx->id("SB_PLL40_2F_PAD"); + cell->type == ctx->id("SB_PLL40_2F_PAD") || + (cell->type == ctx->id("ICESTORM_PLL") && + (cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_PAD" || cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2_PAD" || + cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2F_PAD")); +} + +inline bool is_sb_pll40_dual(const BaseCtx *ctx, const CellInfo *cell) +{ + return cell->type == ctx->id("SB_PLL40_2_PAD") || cell->type == ctx->id("SB_PLL40_2F_PAD") || + cell->type == ctx->id("SB_PLL40_2F_CORE") || + (cell->type == ctx->id("ICESTORM_PLL") && (cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2_PAD" || + cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2F_PAD" || + cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2F_CORE")); } uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell); From c219d8fe4d3b8b6c79a715ca5808156287e7f642 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sun, 18 Nov 2018 19:21:41 +0100 Subject: [PATCH 12/20] ice40: Fix BEL validity check for PLL vs SB_IO Signed-off-by: Sylvain Munaut --- ice40/arch_place.cc | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc index c97b9c26..a09a5092 100644 --- a/ice40/arch_place.cc +++ b/ice40/arch_place.cc @@ -114,31 +114,30 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const // Find shared PLL by looking for driving bel siblings from D_IN_0 // that are a PLL clock output. auto wire = getBelPinWire(bel, id_D_IN_0); - IdString pll_bel_pin; - BelId pll_bel; for (auto pin : getWireBelPins(wire)) { if (pin.pin == id_PLLOUT_A || pin.pin == id_PLLOUT_B) { - pll_bel = pin.bel; - pll_bel_pin = pin.pin; - break; - } - } - // Is there a PLL that shares this IO buffer? - if (pll_bel.index != -1) { - auto pll_cell = getBoundBelCell(pll_bel); - // Is a PLL placed in this PLL bel? - if (pll_cell != nullptr) { - // Is the shared port driving a net? - auto pi = pll_cell->ports[pll_bel_pin]; - if (pi.net != nullptr) { - // Are we perhaps a PAD INPUT Bel that can be placed here? - if (pll_cell->attrs[id("BEL_PAD_INPUT")] == getBelName(bel).str(this)) { - return true; - } - return false; - } + // Is there a PLL there ? + auto pll_cell = getBoundBelCell(pin.bel); + if (pll_cell == nullptr) + break; + + // Is that port actually used ? + if ((pin.pin == id_PLLOUT_B) && !is_sb_pll40_dual(this, pll_cell)) + break; + + // Is that SB_IO used at an input ? + if ((cell->ports[id_D_IN_0].net == nullptr) && (cell->ports[id_D_IN_1].net == nullptr)) + break; + + // Are we perhaps a PAD INPUT Bel that can be placed here? + if (pll_cell->attrs[id("BEL_PAD_INPUT")] == getBelName(bel).str(this)) + return true; + + // Conflict + return false; } } + Loc ioLoc = getBelLocation(bel); Loc compLoc = ioLoc; compLoc.z = 1 - compLoc.z; From 3f4dc7c80e19b9ff404055029c3239d29d0af25f Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 16 Nov 2018 00:04:55 +0100 Subject: [PATCH 13/20] ice40: Add GlobalNetowkrInfo in the chip database Signed-off-by: Sylvain Munaut --- ice40/arch.h | 18 ++++++++++- ice40/chipdb.py | 82 +++++++++++++++++++++++++++---------------------- 2 files changed, 63 insertions(+), 37 deletions(-) diff --git a/ice40/arch.h b/ice40/arch.h index b6f10c03..e8c597c9 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -212,11 +212,26 @@ NPNR_PACKED_STRUCT(struct CellTimingPOD { RelPtr path_delays; }); +NPNR_PACKED_STRUCT(struct GlobalNetworkInfoPOD { + uint8_t gb_x; + uint8_t gb_y; + + uint8_t pi_gb_x; + uint8_t pi_gb_y; + uint8_t pi_gb_pio; + + uint8_t pi_eb_bank; + uint16_t pi_eb_x; + uint16_t pi_eb_y; + + uint16_t pad; +}); + NPNR_PACKED_STRUCT(struct ChipInfoPOD { int32_t width, height; int32_t num_bels, num_wires, num_pips; int32_t num_switches, num_belcfgs, num_packages; - int32_t num_timing_cells; + int32_t num_timing_cells, num_global_networks; RelPtr bel_data; RelPtr wire_data; RelPtr pip_data; @@ -225,6 +240,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD { RelPtr bel_config; RelPtr packages_data; RelPtr cell_timing; + RelPtr global_network_info; RelPtr> tile_wire_names; }); diff --git a/ice40/chipdb.py b/ice40/chipdb.py index 2009483f..6db6c26b 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -36,6 +36,7 @@ ierens = list() extra_cells = dict() extra_cell_config = dict() packages = list() +glbinfo = dict([(i, {}) for i in range(8)]) wire_belports = dict() @@ -640,6 +641,18 @@ with open(args.filename, "r") as f: extra_cells[mode[1]] = [] continue + if line[0] == ".gbufin": + mode = ("gbufin",) + continue + + if line[0] == ".gbufpin": + mode = ("gbufpin",) + continue + + if line[0] == ".extra_bits": + mode = ("extra_bits",) + continue + if (line[0][0] == ".") or (mode is None): mode = None continue @@ -698,6 +711,27 @@ with open(args.filename, "r") as f: extra_cells[mode[1]].append((line[0], (int(line[1]), int(line[2]), line[3]))) continue + if mode[0] == "gbufin": + idx = int(line[2]) + glbinfo[idx]['gb_x'] = int(line[0]) + glbinfo[idx]['gb_y'] = int(line[1]) + continue + + if mode[0] == "gbufpin": + idx = int(line[3]) + glbinfo[idx]['pi_gb_x'] = int(line[0]) + glbinfo[idx]['pi_gb_y'] = int(line[1]) + glbinfo[idx]['pi_gb_pio'] = int(line[2]) + continue + + if mode[0] == "extra_bits": + if line[0].startswith('padin_glb_netwk.'): + idx = int(line[0].split('.')[1]) + glbinfo[idx]['pi_eb_bank'] = int(line[1]) + glbinfo[idx]['pi_eb_x'] = int(line[2]) + glbinfo[idx]['pi_eb_y'] = int(line[3]) + continue + def add_wire(x, y, name): global num_wires wire_idx = num_wires @@ -974,42 +1008,8 @@ for tile_xy, tile_type in sorted(tiles.items()): for i in range(2): add_bel_io(tile_xy[0], tile_xy[1], i) - if dev_name == "1k": - add_bel_gb(tile_xy, 7, 0, 0) - add_bel_gb(tile_xy, 7, 17, 1) - add_bel_gb(tile_xy, 13, 9, 2) - add_bel_gb(tile_xy, 0, 9, 3) - add_bel_gb(tile_xy, 6, 17, 4) - add_bel_gb(tile_xy, 6, 0, 5) - add_bel_gb(tile_xy, 0, 8, 6) - add_bel_gb(tile_xy, 13, 8, 7) - elif dev_name == "5k": - add_bel_gb(tile_xy, 13, 0, 0) - add_bel_gb(tile_xy, 13, 31, 1) - add_bel_gb(tile_xy, 19, 31, 2) - add_bel_gb(tile_xy, 6, 31, 3) - add_bel_gb(tile_xy, 12, 31, 4) - add_bel_gb(tile_xy, 12, 0, 5) - add_bel_gb(tile_xy, 6, 0, 6) - add_bel_gb(tile_xy, 19, 0, 7) - elif dev_name == "8k": - add_bel_gb(tile_xy, 33, 16, 7) - add_bel_gb(tile_xy, 0, 16, 6) - add_bel_gb(tile_xy, 17, 33, 1) - add_bel_gb(tile_xy, 17, 0, 0) - add_bel_gb(tile_xy, 0, 17, 3) - add_bel_gb(tile_xy, 33, 17, 2) - add_bel_gb(tile_xy, 16, 0, 5) - add_bel_gb(tile_xy, 16, 33, 4) - elif dev_name == "384": - add_bel_gb(tile_xy, 7, 4, 7) - add_bel_gb(tile_xy, 0, 4, 6) - add_bel_gb(tile_xy, 4, 9, 1) - add_bel_gb(tile_xy, 4, 0, 0) - add_bel_gb(tile_xy, 0, 5, 3) - add_bel_gb(tile_xy, 7, 5, 2) - add_bel_gb(tile_xy, 3, 0, 5) - add_bel_gb(tile_xy, 3, 9, 4) + for gidx, ginfo in glbinfo.items(): + add_bel_gb(tile_xy, ginfo['gb_x'], ginfo['gb_y'], gidx) if tile_type == "ramb": add_bel_ram(tile_xy[0], tile_xy[1]) @@ -1424,6 +1424,14 @@ for cell, timings in sorted(cell_timings.items()): bba.u32(len(timings), "num_paths") bba.r("cell_paths_%d" % beltype, "path_delays") +bba.l("global_network_info_%s" % dev_name, "GlobalNetworkInfoPOD") +for i in range(len(glbinfo)): + for k in ['gb_x', 'gb_y', 'pi_gb_x', 'pi_gb_y', 'pi_gb_pio', 'pi_eb_bank']: + bba.u8(glbinfo[i][k], k) + for k in ['pi_eb_x', 'pi_eb_y']: + bba.u16(glbinfo[i][k], k) + bba.u16(0, "padding") + bba.l("chip_info_%s" % dev_name) bba.u32(dev_width, "dev_width") bba.u32(dev_height, "dev_height") @@ -1434,6 +1442,7 @@ bba.u32(len(switchinfo), "num_switches") bba.u32(len(extra_cell_config), "num_belcfgs") bba.u32(len(packageinfo), "num_packages") bba.u32(len(cell_timings), "num_timing_cells") +bba.u32(len(glbinfo), "num_global_networks") bba.r("bel_data_%s" % dev_name, "bel_data") bba.r("wire_data_%s" % dev_name, "wire_data") bba.r("pip_data_%s" % dev_name, "pip_data") @@ -1442,6 +1451,7 @@ bba.r("bits_info_%s" % dev_name, "bits_info") bba.r("bel_config_%s" % dev_name if len(extra_cell_config) > 0 else None, "bel_config") bba.r("package_info_%s" % dev_name, "packages_data") bba.r("cell_timings_%s" % dev_name, "cell_timing") +bba.r("global_network_info_%s" % dev_name, "global_network_info") bba.r("tile_wire_names", "tile_wire_names") bba.pop() From 325d46e284fd7944b99929c1482e641a1db53931 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Mon, 19 Nov 2018 01:49:52 +0100 Subject: [PATCH 14/20] ice40/chipdb: Add wires to global network for all cells that can drive it The icebox DB is a bit inconsistent in how global network connections are represented. Here we make it appear consistent by creating ports on the cells that can drive it. Signed-off-by: Sylvain Munaut --- ice40/cells.cc | 4 ++-- ice40/chipdb.py | 22 ++++++++++++++++++---- ice40/constids.inc | 2 ++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/ice40/cells.cc b/ice40/cells.cc index 3906334f..53f2e10c 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -244,8 +244,8 @@ std::unique_ptr 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); + add_port(ctx, new_cell.get(), "PLLOUT_A_GLOBAL", PORT_OUT); + add_port(ctx, new_cell.get(), "PLLOUT_B_GLOBAL", PORT_OUT); } else { log_error("unable to create iCE40 cell of type %s", type.c_str(ctx)); } diff --git a/ice40/chipdb.py b/ice40/chipdb.py index 6db6c26b..96231b26 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -863,6 +863,10 @@ def add_bel_io(x, y, z): add_bel_input(bel, wire_dout_1, "D_OUT_1") add_bel_input(bel, wire_out_en, "OUTPUT_ENABLE") + for gidx, ginfo in glbinfo.items(): + if (ginfo['pi_gb_x'], ginfo['pi_gb_y'], ginfo['pi_gb_pio']) == (x,y,z): + add_bel_output(bel, wire_names[(x, y, "glb_netwk_%d" % gidx)], "GLOBAL_BUFFER_OUTPUT") + def add_bel_ram(x, y): bel = len(bel_name) bel_name.append("X%d/Y%d/ram" % (x, y)) @@ -920,6 +924,18 @@ def is_ec_output(ec_entry): def is_ec_pll_clock_output(ec, ec_entry): return ec[0] == 'PLL' and ec_entry[0] in ('PLLOUT_A', 'PLLOUT_B') +def add_pll_clock_output(bel, ec, entry): + # Fabric output + io_x, io_y, io_z = entry[1] + io_zs = 'io_{}/D_IN_0'.format(io_z) + io_z = int(io_z) + add_bel_output(bel, wire_names[(io_x, io_y, io_zs)], entry[0]) + + # Global output + for gidx, ginfo in glbinfo.items(): + if (ginfo['pi_gb_x'], ginfo['pi_gb_y'], ginfo['pi_gb_pio']) == (io_x, io_y, io_z): + add_bel_output(bel, wire_names[(io_x, io_y, "glb_netwk_%d" % gidx)], entry[0] + '_GLOBAL') + def add_bel_ec(ec): ectype, x, y, z = ec bel = len(bel_name) @@ -929,15 +945,13 @@ def add_bel_ec(ec): bel_pos.append((x, y, z)) bel_wires.append(list()) for entry in extra_cells[ec]: - if is_ec_wire(entry) and "glb_netwk_" not in entry[1][2]: # TODO: osc glb output conflicts with GB + if is_ec_wire(entry): if is_ec_output(entry): add_bel_output(bel, wire_names[entry[1]], entry[0]) else: add_bel_input(bel, wire_names[entry[1]], entry[0]) elif is_ec_pll_clock_output(ec, entry): - x, y, z = entry[1] - z = 'io_{}/D_IN_0'.format(z) - add_bel_output(bel, wire_names[(x, y, z)], entry[0]) + add_pll_clock_output(bel, ec, entry) else: extra_cell_config[bel].append(entry) diff --git a/ice40/constids.inc b/ice40/constids.inc index dad08e59..e1c4992e 100644 --- a/ice40/constids.inc +++ b/ice40/constids.inc @@ -121,6 +121,8 @@ X(DYNAMICDELAY_7) X(LOCK) X(PLLOUT_A) X(PLLOUT_B) +X(PLLOUT_A_GLOBAL) +X(PLLOUT_B_GLOBAL) X(BYPASS) X(RESETB) X(LATCHINPUTVALUE) From bc9f2da470b96c4e2f65324a0da1ffe099b5e586 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Mon, 19 Nov 2018 01:57:47 +0100 Subject: [PATCH 15/20] ice40: Introduce the concept of forPadIn SB_GB Those are cells that are created mainly to handle the various sources a global network can be driven from other than a user net. When the flag is set, this means the global network usually driven by this BEL is in fact driven by something else and so that SB_GB BEL and matching global network can't be used. This is also what gets used to set the extra bits during bitstream generation. Signed-off-by: Sylvain Munaut --- ice40/arch.cc | 2 ++ ice40/arch_place.cc | 2 ++ ice40/archdefs.h | 4 ++++ ice40/bitstream.cc | 18 +++++++++++++++++- ice40/pack.cc | 29 ++++++++++++++++++++++++++++- 5 files changed, 53 insertions(+), 2 deletions(-) diff --git a/ice40/arch.cc b/ice40/arch.cc index 995150ac..3138b813 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -1044,6 +1044,8 @@ void Arch::assignCellInfo(CellInfo *cell) 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"; + } else if (cell->type == id_SB_GB) { + cell->gbInfo.forPadIn = bool_or_default(cell->attrs, this->id("FOR_PAD_IN")); } } diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc index a09a5092..41f9b640 100644 --- a/ice40/arch_place.cc +++ b/ice40/arch_place.cc @@ -161,6 +161,8 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const return getBelPackagePin(bel) != ""; } else if (cell->type == id_SB_GB) { + if (cell->gbInfo.forPadIn) + return true; NPNR_ASSERT(cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net != nullptr); const NetInfo *net = cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net; IdString glb_net = getWireName(getBelPinWire(bel, id_GLOBAL_BUFFER_OUTPUT)); diff --git a/ice40/archdefs.h b/ice40/archdefs.h index b9614c07..8dcf0365 100644 --- a/ice40/archdefs.h +++ b/ice40/archdefs.h @@ -152,6 +152,10 @@ struct ArchCellInfo bool lvds; // TODO: clk packing checks... } ioInfo; + struct + { + bool forPadIn; + } gbInfo; }; }; diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 5d138798..c32680ee 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -269,6 +269,9 @@ void write_asc(const Context *ctx, std::ostream &out) config.at(y).at(x).resize(rows, std::vector(cols)); } } + + std::vector> extra_bits; + out << ".comment from next-pnr" << std::endl; switch (ctx->args.type) { @@ -513,7 +516,16 @@ void write_asc(const Context *ctx, std::ostream &out) } } } else if (cell.second->type == ctx->id("SB_GB")) { - // no cell config bits + if (cell.second->gbInfo.forPadIn) { + Loc gb_loc = ctx->getBelLocation(bel); + for (int i = 0; i < ci.num_global_networks; i++) { + if ((gb_loc.x == ci.global_network_info[i].gb_x) && (gb_loc.y == ci.global_network_info[i].gb_y)) { + extra_bits.push_back(std::make_tuple(ci.global_network_info[i].pi_eb_bank, + ci.global_network_info[i].pi_eb_x, + ci.global_network_info[i].pi_eb_y)); + } + } + } } else if (cell.second->type == ctx->id("ICESTORM_RAM")) { const BelInfoPOD &beli = ci.bel_data[bel.index]; int x = beli.x, y = beli.y; @@ -795,6 +807,10 @@ void write_asc(const Context *ctx, std::ostream &out) } } + // Write extra-bits + for (auto eb : extra_bits) + out << ".extra_bit " << std::get<0>(eb) << " " << std::get<1>(eb) << " " << std::get<2>(eb) << std::endl; + // Write symbols // const bool write_symbols = 1; for (auto wire : ctx->getWires()) { diff --git a/ice40/pack.cc b/ice40/pack.cc index d689fa69..cae6ab8c 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -381,6 +381,33 @@ static void pack_constants(Context *ctx) } } +static std::unique_ptr create_padin_gbuf(Context *ctx, CellInfo *cell, IdString port_name, + std::string gbuf_name) +{ + // Find the matching SB_GB BEL connected to the same global network + BelId gb_bel; + BelId bel = ctx->getBelByName(ctx->id(cell->attrs[ctx->id("BEL")])); + auto wire = ctx->getBelPinWire(bel, port_name); + for (auto src_bel : ctx->getWireBelPins(wire)) { + if (ctx->getBelType(src_bel.bel) == id_SB_GB && src_bel.pin == id_GLOBAL_BUFFER_OUTPUT) { + gb_bel = src_bel.bel; + break; + } + } + + NPNR_ASSERT(gb_bel != BelId()); + + // Create a SB_GB Cell and lock it there + std::unique_ptr gb = create_ice_cell(ctx, ctx->id("SB_GB"), gbuf_name); + gb->attrs[ctx->id("FOR_PAD_IN")] = "1"; + gb->attrs[ctx->id("BEL")] = ctx->getBelName(gb_bel).str(ctx); + + // Reconnect the net to that port for easier identification it's a global net + replace_port(cell, port_name, gb.get(), id_GLOBAL_BUFFER_OUTPUT); + + return gb; +} + static bool is_nextpnr_iob(Context *ctx, CellInfo *cell) { return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") || @@ -1003,13 +1030,13 @@ bool Arch::pack() try { log_break(); pack_constants(ctx); - promote_globals(ctx); pack_io(ctx); pack_lut_lutffs(ctx); pack_nonlut_ffs(ctx); pack_carries(ctx); pack_ram(ctx); pack_special(ctx); + promote_globals(ctx); ctx->assignArchInfo(); constrain_chains(ctx); ctx->assignArchInfo(); From d8e4c21d96bfca7da60d0c445a1fdba48f46e9e1 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 16 Nov 2018 02:03:25 +0100 Subject: [PATCH 16/20] ice40: Add support for PLL global outputs via PADIN Signed-off-by: Sylvain Munaut --- ice40/bitstream.cc | 94 ++++++++++++++++++++++++---------------------- ice40/pack.cc | 63 ++++++++++++------------------- 2 files changed, 73 insertions(+), 84 deletions(-) diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index c32680ee..d2c2ac16 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -356,8 +356,45 @@ void write_asc(const Context *ctx, std::ostream &out) } } - std::unordered_set sb_io_used_by_pll; - std::unordered_set sb_io_used_by_io; + // Scan for PLL and collects the affected SB_IOs + std::unordered_set sb_io_used_by_pll_out; + std::unordered_set sb_io_used_by_pll_pad; + + for (auto &cell : ctx->cells) { + if (cell.second->type != ctx->id("ICESTORM_PLL")) + continue; + + // Collect all locations matching an PLL output port + // note: It doesn't matter if the port is connected or not, or if fabric/global + // is used. As long as it's a PLL type for which the port exists, the SB_IO + // is not available and must be configured for PLL mode + const std::vector ports = {id_PLLOUT_A, id_PLLOUT_B}; + for (auto &port : ports) { + // If the output is not enabled in this mode, ignore it + if (port == id_PLLOUT_B && !is_sb_pll40_dual(ctx, cell.second.get())) + continue; + + // Get IO Bel that this PLL port goes through by finding sibling + // Bel driving the same wire via PIN_D_IN_0. + auto wire = ctx->getBelPinWire(cell.second->bel, port); + BelId io_bel; + for (auto pin : ctx->getWireBelPins(wire)) { + if (pin.pin == id_D_IN_0) { + io_bel = pin.bel; + break; + } + } + NPNR_ASSERT(io_bel.index != -1); + auto io_bel_loc = ctx->getBelLocation(io_bel); + + // Mark this SB_IO as being used by a PLL output path + sb_io_used_by_pll_out.insert(io_bel_loc); + + // 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); + } + } // Set logic cell config for (auto &cell : ctx->cells) { @@ -445,14 +482,15 @@ void write_asc(const Context *ctx, std::ostream &out) } else if (cell.second->type == ctx->id("SB_IO")) { const BelInfoPOD &beli = ci.bel_data[bel.index]; int x = beli.x, y = beli.y, z = beli.z; - sb_io_used_by_io.insert(Loc(x, y, z)); const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO]; 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"; + bool used_by_pll_out = sb_io_used_by_pll_out.count(Loc(x, y, z)) > 0; + bool used_by_pll_pad = sb_io_used_by_pll_pad.count(Loc(x, y, z)) > 0; - for (int i = 0; i < 6; i++) { + for (int i = used_by_pll_out ? 2 : 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); } @@ -475,6 +513,8 @@ void write_asc(const Context *ctx, std::ostream &out) } } + input_en = (input_en & !used_by_pll_out) | used_by_pll_pad; + 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); @@ -613,47 +653,13 @@ void write_asc(const Context *ctx, std::ostream &out) 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 &port : cell.second->ports) { - // If this port is not a PLLOUT port, ignore it. - if (port.second.name != ctx->id("PLLOUT_A") && port.second.name != ctx->id("PLLOUT_B")) - continue; - - // If the output is not driving any net, ignore it. - if (port.second.net == nullptr) - continue; - - // Get IO Bel that this PLL port goes through by finding sibling - // Bel driving the same wire via PIN_D_IN_0. - auto wire = ctx->getBelPinWire(cell.second->bel, port.second.name); - BelId io_bel; - for (auto pin : ctx->getWireBelPins(wire)) { - if (pin.pin == id_D_IN_0) { - io_bel = pin.bel; - break; - } - } - NPNR_ASSERT(io_bel.index != -1); - auto io_bel_loc = ctx->getBelLocation(io_bel); - - // Check that this SB_IO is either unused or just used as an output. - if (sb_io_used_by_io.count(io_bel_loc)) { - log_error("SB_IO '%s' already in use, cannot route PLL through\n", ctx->getBelName(bel).c_str(ctx)); - } - sb_io_used_by_pll.insert(io_bel_loc); - - // Get IE/REN config location (cf. http://www.clifford.at/icestorm/io_tile.html) - auto ieren = get_ieren(bi, io_bel_loc.x, io_bel_loc.y, io_bel_loc.z); - int iex, iey, iez; - std::tie(iex, iey, iez) = ieren; - NPNR_ASSERT(iez != -1); - + for (auto &io_bel_loc : sb_io_used_by_pll_out) { // Write config. const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO]; - // Enable input buffer and disable pull-up resistor in block - // (this is used by the PLL). - 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); - // PINTYPE[0] passes the PLL through to the fabric. + + // 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); } @@ -668,7 +674,7 @@ void write_asc(const Context *ctx, std::ostream &out) const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO]; const BelInfoPOD &beli = ci.bel_data[bel.index]; int x = beli.x, y = beli.y, z = beli.z; - if (sb_io_used_by_pll.count(Loc(x, y, z))) { + if (sb_io_used_by_pll_out.count(Loc(x, y, z))) { continue; } diff --git a/ice40/pack.cc b/ice40/pack.cc index cae6ab8c..ca67baab 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -790,32 +790,6 @@ 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); @@ -823,24 +797,15 @@ static void pack_special(Context *ctx) if (bpos != std::string::npos) { newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2); } - if (pi.name == ctx->id("PLLOUTCOREA")) + + if (pi.name == ctx->id("PLLOUTCOREA") || pi.name == ctx->id("PLLOUTCORE")) newname = "PLLOUT_A"; if (pi.name == ctx->id("PLLOUTCOREB")) 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("PLLOUTGLOBALA") || pi.name == ctx->id("PLLOUTGLOBALA")) + newname = "PLLOUT_A_GLOBAL"; 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()); + newname = "PLLOUT_B_GLOBAL"; if (pi.name == ctx->id("PACKAGEPIN")) { if (!is_pad) { @@ -1011,6 +976,24 @@ static void pack_special(Context *ctx) } } + // Handle the global buffer connections + for (auto port : packed->ports) { + PortInfo &pi = port.second; + bool is_b_port; + + if (pi.name == ctx->id("PLLOUT_A_GLOBAL")) + is_b_port = false; + else if (pi.name == ctx->id("PLLOUT_B_GLOBAL")) + is_b_port = true; + else + continue; + + std::unique_ptr gb = + create_padin_gbuf(ctx, packed.get(), pi.name, + "$gbuf_" + ci->name.str(ctx) + "_pllout_" + (is_b_port ? "b" : "a")); + new_cells.push_back(std::move(gb)); + } + new_cells.push_back(std::move(packed)); } } From 519d4e2af8d8e7d8e2d1d873b9cd9681ef83cc62 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sat, 17 Nov 2018 13:04:14 +0100 Subject: [PATCH 17/20] ice40: Add support for SB_GB_IO During packing we replace them by standard SB_IO cells and create the 'fake' SB_GB that matches that IO site global buffer connection. It's done in a separate pass because we need to make sure the nextpnr iob have been dealt first so we have our final Bel location on the SB_IO. Signed-off-by: Sylvain Munaut --- ice40/arch.cc | 1 + ice40/archdefs.h | 1 + ice40/bitstream.cc | 1 + ice40/cells.h | 3 +++ ice40/pack.cc | 33 +++++++++++++++++++++++++-------- 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/ice40/arch.cc b/ice40/arch.cc index 3138b813..259eec67 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -1044,6 +1044,7 @@ void Arch::assignCellInfo(CellInfo *cell) 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"; + cell->ioInfo.global = bool_or_default(cell->attrs, this->id("GLOBAL")); } else if (cell->type == id_SB_GB) { cell->gbInfo.forPadIn = bool_or_default(cell->attrs, this->id("FOR_PAD_IN")); } diff --git a/ice40/archdefs.h b/ice40/archdefs.h index 8dcf0365..2bffe667 100644 --- a/ice40/archdefs.h +++ b/ice40/archdefs.h @@ -150,6 +150,7 @@ struct ArchCellInfo struct { bool lvds; + bool global; // TODO: clk packing checks... } ioInfo; struct diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index d2c2ac16..44d385e4 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -514,6 +514,7 @@ void write_asc(const Context *ctx, std::ostream &out) } input_en = (input_en & !used_by_pll_out) | used_by_pll_pad; + input_en |= cell.second->ioInfo.global; 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); diff --git a/ice40/cells.h b/ice40/cells.h index 7f349020..270292ed 100644 --- a/ice40/cells.h +++ b/ice40/cells.h @@ -53,6 +53,9 @@ inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type // Return true if a cell is a SB_IO inline bool is_sb_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_IO"); } +// Return true if a cell is a SB_GB_IO +inline bool is_sb_gb_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_GB_IO"); } + // Return true if a cell is a global buffer inline bool is_gbuf(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_GB"); } diff --git a/ice40/pack.cc b/ice40/pack.cc index ca67baab..a40f0878 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -414,6 +414,11 @@ static bool is_nextpnr_iob(Context *ctx, CellInfo *cell) cell->type == ctx->id("$nextpnr_iobuf"); } +static bool is_ice_iob(const Context *ctx, const CellInfo *cell) +{ + return is_sb_io(ctx, cell) || is_sb_gb_io(ctx, cell); +} + // Pack IO buffers static void pack_io(Context *ctx) { @@ -428,10 +433,10 @@ static void pack_io(Context *ctx) if (is_nextpnr_iob(ctx, ci)) { CellInfo *sb = nullptr; if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) { - sb = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_sb_io, ctx->id("PACKAGE_PIN"), true, ci); + sb = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_ice_iob, ctx->id("PACKAGE_PIN"), true, ci); } else if (ci->type == ctx->id("$nextpnr_obuf")) { - sb = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_sb_io, ctx->id("PACKAGE_PIN"), true, ci); + sb = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_ice_iob, ctx->id("PACKAGE_PIN"), true, ci); } if (sb != nullptr) { // Trivial case, SB_IO used. Just destroy the net and the @@ -442,8 +447,8 @@ static void pack_io(Context *ctx) 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)); + log_error("PACKAGE_PIN of %s '%s' connected to more than a single top level IO.\n", + sb->type.c_str(ctx), sb->name.c_str(ctx)); if (net != nullptr) { delete_nets.insert(net->name); @@ -465,13 +470,26 @@ static void pack_io(Context *ctx) } packed_cells.insert(ci->name); std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(sb->attrs, sb->attrs.begin())); - } else if (is_sb_io(ctx, ci)) { + } else if (is_sb_io(ctx, ci) || is_sb_gb_io(ctx, ci)) { NetInfo *net = ci->ports.at(ctx->id("PACKAGE_PIN")).net; if ((net != nullptr) && (net->users.size() > 1)) - log_error("PACKAGE_PIN of SB_IO '%s' connected to more than a single top level IO.\n", + log_error("PACKAGE_PIN of %s '%s' connected to more than a single top level IO.\n", ci->type.c_str(ctx), ci->name.c_str(ctx)); } } + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (is_sb_gb_io(ctx, ci)) { + // If something is connecto the GLOBAL OUTPUT, create the fake 'matching' SB_GB + std::unique_ptr gb = + create_padin_gbuf(ctx, ci, id_GLOBAL_BUFFER_OUTPUT, "$gbuf_" + ci->name.str(ctx) + "_io"); + new_cells.push_back(std::move(gb)); + + // Make it a normal SB_IO with global marker + ci->type = ctx->id("SB_IO"); + ci->attrs[ctx->id("GLOBAL")] = "1"; + } + } for (auto pcell : packed_cells) { ctx->cells.erase(pcell); } @@ -488,8 +506,7 @@ static bool is_logic_port(BaseCtx *ctx, const PortRef &port) { if (is_clock_port(ctx, port) || is_reset_port(ctx, port) || is_enable_port(ctx, port)) return false; - return !is_sb_io(ctx, port.cell) && - !is_gbuf(ctx, port.cell) && + return !is_sb_io(ctx, port.cell) && !is_sb_gb_io(ctx, port.cell) && !is_gbuf(ctx, port.cell) && !is_sb_pll40(ctx, port.cell); } From 271cc7be115fc28ffe82a1bade235e41a3b43896 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Mon, 19 Nov 2018 01:59:53 +0100 Subject: [PATCH 18/20] ice40/pack: Add helper to constain cells that are unique in the FPGA Signed-off-by: Sylvain Munaut --- ice40/pack.cc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ice40/pack.cc b/ice40/pack.cc index a40f0878..255dc75f 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -695,6 +695,22 @@ static std::unique_ptr spliceLUT(Context *ctx, CellInfo *ci, IdString return pt; } +// Force placement for cells that are unique anyway +static BelId cell_place_unique(Context *ctx, CellInfo *ci) +{ + for (auto bel : ctx->getBels()) { + if (ctx->getBelType(bel) != ci->type) + continue; + if (ctx->isBelLocked(bel)) + continue; + IdString bel_name = ctx->getBelName(bel); + ci->attrs[ctx->id("BEL")] = bel_name.str(ctx); + log_info(" constrained %s '%s' to %s\n", ci->type.c_str(ctx), ci->name.c_str(ctx), bel_name.c_str(ctx)); + return bel; + } + log_error("Unable to place cell '%s' of type '%s'\n", ci->name.c_str(ctx), ci->type.c_str(ctx)); +} + // Pack special functions static void pack_special(Context *ctx) { From de8de6304f6905525fd5774d30851c0cc9fe4e37 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Mon, 19 Nov 2018 02:00:59 +0100 Subject: [PATCH 19/20] ice40: Add global network output support for LFOSC/HFOSC Signed-off-by: Sylvain Munaut --- ice40/pack.cc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ice40/pack.cc b/ice40/pack.cc index 255dc75f..c0970735 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -725,25 +725,33 @@ static void pack_special(Context *ctx) std::unique_ptr packed = create_ice_cell(ctx, ctx->id("ICESTORM_LFOSC"), ci->name.str(ctx) + "_OSC"); packed_cells.insert(ci->name); + cell_place_unique(ctx, packed.get()); replace_port(ci, ctx->id("CLKLFEN"), packed.get(), ctx->id("CLKLFEN")); replace_port(ci, ctx->id("CLKLFPU"), packed.get(), ctx->id("CLKLFPU")); - if (/*bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))*/ true) { // FIXME + if (bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))) { replace_port(ci, ctx->id("CLKLF"), packed.get(), ctx->id("CLKLF_FABRIC")); } else { replace_port(ci, ctx->id("CLKLF"), packed.get(), ctx->id("CLKLF")); + std::unique_ptr gb = + create_padin_gbuf(ctx, packed.get(), ctx->id("CLKLF"), "$gbuf_" + ci->name.str(ctx) + "_lfosc"); + new_cells.push_back(std::move(gb)); } new_cells.push_back(std::move(packed)); } else if (is_sb_hfosc(ctx, ci)) { std::unique_ptr packed = create_ice_cell(ctx, ctx->id("ICESTORM_HFOSC"), ci->name.str(ctx) + "_OSC"); packed_cells.insert(ci->name); + cell_place_unique(ctx, packed.get()); packed->params[ctx->id("CLKHF_DIV")] = str_or_default(ci->params, ctx->id("CLKHF_DIV"), "0b00"); replace_port(ci, ctx->id("CLKHFEN"), packed.get(), ctx->id("CLKHFEN")); replace_port(ci, ctx->id("CLKHFPU"), packed.get(), ctx->id("CLKHFPU")); - if (/*bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))*/ true) { // FIXME + if (bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))) { replace_port(ci, ctx->id("CLKHF"), packed.get(), ctx->id("CLKHF_FABRIC")); } else { replace_port(ci, ctx->id("CLKHF"), packed.get(), ctx->id("CLKHF")); + std::unique_ptr gb = + create_padin_gbuf(ctx, packed.get(), ctx->id("CLKHF"), "$gbuf_" + ci->name.str(ctx) + "_hfosc"); + new_cells.push_back(std::move(gb)); } new_cells.push_back(std::move(packed)); } else if (is_sb_spram(ctx, ci)) { From e8556aff372c77c1e14a4378b43b47f8ba1e75ec Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Sat, 17 Nov 2018 18:02:31 +0100 Subject: [PATCH 20/20] ice40: Add support for SB_RGBA_DRV Signed-off-by: Sylvain Munaut --- ice40/arch.cc | 4 ++++ ice40/bitstream.cc | 5 +++++ ice40/cells.cc | 14 ++++++++++++++ ice40/cells.h | 2 ++ ice40/pack.cc | 35 +++++++++++++++++++++++++++++++++-- 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/ice40/arch.cc b/ice40/arch.cc index 259eec67..02e5515b 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -946,6 +946,10 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_COMB_INPUT; } else if (cell->type == id_SB_WARMBOOT) { return TMG_ENDPOINT; + } else if (cell->type == id_SB_RGBA_DRV) { + if (port == id_RGB0 || port == id_RGB1 || port == id_RGB2) + return TMG_IGNORE; + return TMG_ENDPOINT; } log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this)); } diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 44d385e4..ecb26753 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -586,6 +586,11 @@ void write_asc(const Context *ctx, std::ostream &out) set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_1", write_mode & 0x2); set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_2", read_mode & 0x1); set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_3", read_mode & 0x2); + } else if (cell.second->type == ctx->id("SB_RGBA_DRV")) { + const std::vector> rgba_params = { + {"CURRENT_MODE", 1}, {"RGB0_CURRENT", 6}, {"RGB1_CURRENT", 6}, {"RGB2_CURRENT", 6}}; + configure_extra_cell(config, ctx, cell.second.get(), rgba_params, true, std::string("IpConfig.")); + set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "RGBA_DRV_EN", true, "IpConfig."); } else if (cell.second->type == ctx->id("SB_WARMBOOT") || cell.second->type == ctx->id("ICESTORM_LFOSC")) { // No config needed } else if (cell.second->type == ctx->id("ICESTORM_SPRAM")) { diff --git a/ice40/cells.cc b/ice40/cells.cc index 53f2e10c..dbb75c2c 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -246,6 +246,20 @@ std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::stri add_port(ctx, new_cell.get(), "PLLOUT_B", PORT_OUT); add_port(ctx, new_cell.get(), "PLLOUT_A_GLOBAL", PORT_OUT); add_port(ctx, new_cell.get(), "PLLOUT_B_GLOBAL", PORT_OUT); + } else if (type == ctx->id("SB_RGBA_DRV")) { + new_cell->params[ctx->id("CURRENT_MODE")] = "0b0"; + new_cell->params[ctx->id("RGB0_CURRENT")] = "0b000000"; + new_cell->params[ctx->id("RGB1_CURRENT")] = "0b000000"; + new_cell->params[ctx->id("RGB2_CURRENT")] = "0b000000"; + + add_port(ctx, new_cell.get(), "CURREN", PORT_IN); + add_port(ctx, new_cell.get(), "RGBLEDEN", PORT_IN); + add_port(ctx, new_cell.get(), "RGB0PWM", PORT_IN); + add_port(ctx, new_cell.get(), "RGB1PWM", PORT_IN); + add_port(ctx, new_cell.get(), "RGB2PWM", PORT_IN); + add_port(ctx, new_cell.get(), "RGB0", PORT_OUT); + add_port(ctx, new_cell.get(), "RGB1", PORT_OUT); + add_port(ctx, new_cell.get(), "RGB2", PORT_OUT); } else { log_error("unable to create iCE40 cell of type %s", type.c_str(ctx)); } diff --git a/ice40/cells.h b/ice40/cells.h index 270292ed..1fbd9073 100644 --- a/ice40/cells.h +++ b/ice40/cells.h @@ -74,6 +74,8 @@ inline bool is_sb_spram(const BaseCtx *ctx, const CellInfo *cell) { return cell- inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_MAC16"); } +inline bool is_sb_rgba_drv(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_RGBA_DRV"); } + inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") || diff --git a/ice40/pack.cc b/ice40/pack.cc index c0970735..dae19b2d 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -431,12 +431,15 @@ static void pack_io(Context *ctx) for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; if (is_nextpnr_iob(ctx, ci)) { - CellInfo *sb = nullptr; + CellInfo *sb = nullptr, *rgb = nullptr; if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) { sb = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_ice_iob, ctx->id("PACKAGE_PIN"), true, ci); } else if (ci->type == ctx->id("$nextpnr_obuf")) { - sb = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_ice_iob, ctx->id("PACKAGE_PIN"), true, ci); + NetInfo *net = ci->ports.at(ctx->id("I")).net; + sb = net_only_drives(ctx, net, is_ice_iob, ctx->id("PACKAGE_PIN"), true, ci); + if (net && net->driver.cell && is_sb_rgba_drv(ctx, net->driver.cell)) + rgb = net->driver.cell; } if (sb != nullptr) { // Trivial case, SB_IO used. Just destroy the net and the @@ -460,6 +463,11 @@ static void pack_io(Context *ctx) delete_nets.insert(net2->name); } } + } else if (rgb != nullptr) { + log_info("%s use by SB_RGBA_DRV %s, not creating SB_IO\n", ci->name.c_str(ctx), rgb->name.c_str(ctx)); + disconnect_port(ctx, ci, ctx->id("I")); + packed_cells.insert(ci->name); + continue; } else { // Create a SB_IO buffer std::unique_ptr ice_cell = @@ -787,6 +795,29 @@ static void pack_special(Context *ctx) replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); } new_cells.push_back(std::move(packed)); + } else if (is_sb_rgba_drv(ctx, ci)) { + /* Force placement (no choices anyway) */ + cell_place_unique(ctx, ci); + + /* Disconnect all external ports and check there is no users (they should have been + * dealth with during IO packing */ + for (auto port : ci->ports) { + PortInfo &pi = port.second; + NetInfo *net = pi.net; + + if (net == nullptr) + continue; + if ((pi.name != ctx->id("RGB0")) && (pi.name != ctx->id("RGB1")) && (pi.name != ctx->id("RGB2"))) + continue; + + if (net->users.size() > 0) + log_error("SB_RGBA_DRV port connected to more than just package pin !\n"); + + ctx->nets.erase(net->name); + } + ci->ports.erase(ctx->id("RGB0")); + ci->ports.erase(ctx->id("RGB1")); + ci->ports.erase(ctx->id("RGB2")); } else if (is_sb_pll40(ctx, ci)) { bool is_pad = is_sb_pll40_pad(ctx, ci); bool is_core = !is_pad;