From 2b1f7875bb8c3a761dfb9db21706f918b58be9c3 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Sun, 22 Jul 2018 13:42:07 +0100 Subject: [PATCH 01/33] ice40: Implement emitting PLLs --- common/nextpnr.h | 3 ++ common/router1.cc | 1 + gui/fpgaviewwidget.cc | 3 +- ice40/arch.cc | 3 +- ice40/bitstream.cc | 118 +++++++++++++++++++++++++++++++++++++----- ice40/cells.cc | 49 ++++++++++++++++++ ice40/cells.h | 9 ++++ ice40/chipdb.py | 41 +++++++++++++++ ice40/gfx.h | 6 ++- ice40/pack.cc | 32 ++++++++++++ ice40/pcf.cc | 25 +++++++++ ice40/portpins.inc | 2 + 12 files changed, 275 insertions(+), 17 deletions(-) diff --git a/common/nextpnr.h b/common/nextpnr.h index 021772fe..9ee00a50 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -173,6 +173,9 @@ struct Loc bool operator==(const Loc &other) const { return (x == other.x) && (y == other.y) && (z == other.z); } bool operator!=(const Loc &other) const { return (x != other.x) || (y != other.y) || (z == other.z); } + + Loc(int x, int y, int z) : x(x), y(y), z(z) {} + Loc() {} }; NEXTPNR_NAMESPACE_END diff --git a/common/router1.cc b/common/router1.cc index fbf3c467..21c54e4c 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -210,6 +210,7 @@ struct Router else overtimeRevisitCnt++; } + QueuedWire next_qw; next_qw.wire = next_wire; diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index e21af678..cd042af5 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -400,6 +400,7 @@ void FPGAViewWidget::paintGL() // Calculate world thickness to achieve a screen 1px/1.1px line. float thick1Px = mouseToWorldCoordinates(1, 0).x(); float thick11Px = mouseToWorldCoordinates(1.1, 0).x(); + float thick2Px = mouseToWorldCoordinates(2, 0).x(); // Draw grid. auto grid = LineShaderData(); @@ -418,7 +419,7 @@ void FPGAViewWidget::paintGL() for (int i = 0; i < 8; i++) lineShader_.draw(rendererData_->highlighted[i], colors_.highlight[i], thick11Px, matrix); - lineShader_.draw(rendererData_->selected, colors_.selected, thick11Px, matrix); + lineShader_.draw(rendererData_->selected, colors_.selected, thick2Px, matrix); rendererDataLock_.unlock(); } diff --git a/ice40/arch.cc b/ice40/arch.cc index 65b21afd..d6ef800d 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -331,11 +331,12 @@ WireId Arch::getBelPinWire(BelId bel, PortPin pin) const int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires; const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get(); - for (int i = 0; i < num_bel_wires; i++) + for (int i = 0; i < num_bel_wires; i++) { if (bel_wires[i].port == pin) { ret.index = bel_wires[i].wire_index; break; } + } return ret; } diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 9ac8e857..77d92a89 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -74,23 +74,33 @@ void set_config(const TileInfoPOD &ti, std::vector> &tile_cf for (int i = 0; i < cfg.num_bits; i++) { int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col); if (cbit && !value) - log_error("clearing already set config bit %s", name.c_str()); + log_error("clearing already set config bit %s\n", name.c_str()); cbit = value; } } else { int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col); cbit = value; if (cbit && !value) - log_error("clearing already set config bit %s[%d]", name.c_str(), index); + log_error("clearing already set config bit %s[%d]\n", name.c_str(), index); + } +} + +// Set an IE_{EN,REN} logical bit in a tile config. Logical means enabled. +// On {HX,LP}1K devices these bits are active low, so we need to inver them. +void set_ie_bit_logical(const Context *ctx, const TileInfoPOD &ti, std::vector> &tile_cfg, const std::string &name, bool value) { + if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) { + set_config(ti, tile_cfg, name, !value); + } else { + set_config(ti, tile_cfg, name, value); } } int get_param_or_def(const CellInfo *cell, const IdString param, int defval = 0) { auto found = cell->params.find(param); - if (found != cell->params.end()) + if (found != cell->params.end()) { return std::stoi(found->second); - else + } else return defval; } @@ -117,7 +127,7 @@ static const BelConfigPOD &get_ec_config(const ChipInfoPOD *chip, BelId bel) typedef std::vector>>> chipconfig_t; static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfigPOD &cell_cbits, std::string name, - bool value) + bool value, std::string prefix) { const ChipInfoPOD *chip = ctx->chip_info; @@ -125,7 +135,7 @@ static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfi const auto &cbit = cell_cbits.entries[i]; if (cbit.entry_name.get() == name) { const auto &ti = chip->bits_info->tiles_nonrouting[tile_at(ctx, cbit.x, cbit.y)]; - set_config(ti, config.at(cbit.y).at(cbit.x), std::string("IpConfig.") + cbit.cbit_name.get(), value); + set_config(ti, config.at(cbit.y).at(cbit.x), prefix + cbit.cbit_name.get(), value); return; } } @@ -133,7 +143,7 @@ static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfi } void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell, - const std::vector> ¶ms, bool string_style) + const std::vector> ¶ms, bool string_style, std::string prefix) { const ChipInfoPOD *chip = ctx->chip_info; const auto &bc = get_ec_config(chip, cell->bel); @@ -163,10 +173,10 @@ void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *ce value.resize(p.second); if (p.second == 1) { - set_ec_cbit(config, ctx, bc, p.first, value.at(0)); + set_ec_cbit(config, ctx, bc, p.first, value.at(0), prefix); } else { for (int i = 0; i < p.second; i++) { - set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i)); + set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i), prefix); } } } @@ -258,8 +268,13 @@ void write_asc(const Context *ctx, std::ostream &out) } } } + + std::unordered_set sb_io_used_by_pll; + std::unordered_set sb_io_used_by_user; + // Set logic cell config for (auto &cell : ctx->cells) { + BelId bel = cell.second.get()->bel; if (bel == BelId()) { std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl; @@ -304,6 +319,7 @@ 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_user.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")); @@ -405,7 +421,79 @@ void write_asc(const Context *ctx, std::ostream &out) {"MODE_8x8", 1}, {"A_SIGNED", 1}, {"B_SIGNED", 1}}; - configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false); + configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false, std::string("IpConfig.")); + } else if (cell.second->type == ctx->id("ICESTORM_PLL")) { + const std::vector> pll_params = { + {"DELAY_ADJMODE_FB", 1}, {"DELAY_ADJMODE_REL", 1}, + {"DIVF", 7}, {"DIVQ", 3}, {"DIVR", 4}, + {"FDA_FEEDBACK", 4}, {"FDA_RELATIVE", 4}, + {"FEEDBACK_PATH", 3}, {"FILTER_RANGE", 3}, + {"PLLOUT_SELECT_A", 2}, {"PLLOUT_SELECT_B", 2}, + {"PLLTYPE", 3}, {"SHIFTREG_DIV_MODE", 1}, {"TEST_MODE", 1} + }; + 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; + + // Find SB_IO Bel in net that's driving a wire. That's the one + // we're routing the signal through. + // TODO(q3k): Is there a nicer way to do this? + bool found = false; + + // For every wire in the PLLOUT net... + for (auto wp : port.second.net->wires) { + // ... get its' uphill bel ... + auto bel = ctx->getBelPinUphill(wp.first).bel; + if (bel == BelId()) { + continue; + } + // ... and check if it's an SB_IO. + if (ctx->getBelType(bel) != TYPE_SB_IO) + continue; + + // Check that this SB_IO is either unused or just used as an output. + const BelInfoPOD &io_beli = ci.bel_data[bel.index]; + auto io_loc = Loc(io_beli.x, io_beli.y, io_beli.z); + + if (sb_io_used_by_user.count(io_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(Loc(io_beli.x, io_beli.y, io_beli.z)); + + // Get IE/REN config location (cf. http://www.clifford.at/icestorm/io_tile.html) + auto ieren = get_ieren(bi, io_beli.x, io_beli.y, io_beli.z); + int iex, iey, iez; + std::tie(iex, iey, iez) = ieren; + NPNR_ASSERT(iez != -1); + + // 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. + set_config(ti, config.at(io_beli.y).at(io_beli.x), "IOB_" + std::to_string(io_beli.z) + ".PINTYPE_0", true); + + found = true; + break; + } + + if (!found) { + log_error("Could not find SB_IO forwarding PLL '%s' %s signal\n", + cell.second->name.c_str(ctx), port.second.name.c_str(ctx)); + } + } + } else { NPNR_ASSERT(false); } @@ -416,14 +504,16 @@ 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))) { + continue; + } + auto ieren = get_ieren(bi, x, y, z); int iex, iey, iez; std::tie(iex, iey, iez) = ieren; if (iez != -1) { - 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); - } + 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); } } else if (ctx->bel_to_cell[bel.index] == IdString() && ctx->getBelType(bel) == TYPE_ICESTORM_RAM) { const BelInfoPOD &beli = ci.bel_data[bel.index]; diff --git a/ice40/cells.cc b/ice40/cells.cc index 71a65d44..3215a38c 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -207,6 +207,40 @@ std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::stri add_port(ctx, new_cell.get(), "ACCUMCO", PORT_OUT); add_port(ctx, new_cell.get(), "SIGNEXTOUT", PORT_OUT); + } else if (type == ctx->id("ICESTORM_PLL")) { + new_cell->params[ctx->id("DELAY_ADJMODE_FB")] = "0"; + new_cell->params[ctx->id("DELAY_ADJMODE_REL")] = "0"; + + new_cell->params[ctx->id("DIVF")] = "0"; + new_cell->params[ctx->id("DIVQ")] = "0"; + new_cell->params[ctx->id("DIVR")] = "0"; + + 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("FILTER_RANGE")] = "0"; + + new_cell->params[ctx->id("PLLOUT_SELECT_A")] = "0"; + new_cell->params[ctx->id("PLLOUT_SELECT_B")] = "0"; + + new_cell->params[ctx->id("PLLTYPE")] = "0"; + new_cell->params[ctx->id("SHIFTREG_DIVMODE")] = "0"; + 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); + 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); + add_port(ctx, new_cell.get(), "RESETB", PORT_IN); + + add_port(ctx, new_cell.get(), "SCLK", PORT_IN); + add_port(ctx, new_cell.get(), "SDI", PORT_IN); + add_port(ctx, new_cell.get(), "SDI", PORT_OUT); + + 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); } else { log_error("unable to create iCE40 cell of type %s", type.c_str(ctx)); } @@ -312,6 +346,21 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio) } } +uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell) +{ + if (cell->type == ctx->id("SB_PLL40_PAD")) + return 2; + if (cell->type == ctx->id("SB_PLL40_2_PAD")) + return 4; + if (cell->type == ctx->id("SB_PLL40_2F_PAD")) + return 5; + if (cell->type == ctx->id("SB_PLL40_CORE")) + return 3; + if (cell->type == ctx->id("SB_PLL40_2F_CORE")) + return 7; + NPNR_ASSERT(0); +} + bool is_clock_port(const BaseCtx *ctx, const PortRef &port) { if (port.cell == nullptr) diff --git a/ice40/cells.h b/ice40/cells.h index 2f9c77e8..404f401c 100644 --- a/ice40/cells.h +++ b/ice40/cells.h @@ -71,6 +71,15 @@ 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_pll40(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_CORE") || + cell->type == ctx->id("SB_PLL40_2F_CORE"); +} + +uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell); + // Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports // as needed. Set no_dff if a DFF is not being used, so that the output // can be reconnected diff --git a/ice40/chipdb.py b/ice40/chipdb.py index 0e8e3ba7..63f08e36 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -184,6 +184,8 @@ def wire_type(name): wt = "LOCAL" elif name in ("WCLK", "WCLKE", "WE", "RCLK", "RCLKE", "RE"): wt = "LOCAL" + elif name in ("PLLOUT_A", "PLLOUT_B"): + wt = "LOCAL" if wt is None: print("No type for wire: %s (%s)" % (longname, name), file=sys.stderr) @@ -591,6 +593,43 @@ def is_ec_output(ec_entry): if "glb_netwk_" in wirename: return True return False +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, ec_entry): + #print('add_pll_clock_output', ec, ec_entry) + pll_x, pll_y, pll_z = ec[1], ec[2], ec[3] + port = ec_entry[0] + io_x, io_y, io_z = ec_entry[1] + io_z = int(io_z) + + global num_wires + wire_idx = num_wires + num_wires = num_wires + 1 + + wire_xy[wire_idx] = [(pll_x, pll_y)] + + wire_names_r[wire_idx] = (pll_x, pll_y, port) + wire_names[(pll_x, pll_y, port)] = wire_idx + wire_segments[wire_idx] = { + (pll_x, pll_y): port, + (io_x, io_y): 'PLLIN', + } + + wire_downhill_belports[wire_idx] = {(bel, port),} + bel_wires[bel].append((wire_idx, port)) + + io_wire = wire_names[(io_x, io_y, 'io_{}/D_IN_0'.format(io_z))] + wire_downhill[wire_idx] = {io_wire,} + if io_wire not in wire_uphill: + wire_uphill[io_wire] = set() + wire_uphill[io_wire].add(wire_idx) + + switches.append((io_x, io_y, 0, [])) + switchnum = len(switches) - 1 + pip_xy[(wire_idx, io_wire)] = (io_x, io_y, 0, switchnum) + + def add_bel_ec(ec): ectype, x, y, z = ec bel = len(bel_name) @@ -605,6 +644,8 @@ def add_bel_ec(ec): 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): + add_pll_clock_output(bel, ec, entry) else: extra_cell_config[bel].append(entry) diff --git a/ice40/gfx.h b/ice40/gfx.h index 8a55407d..7eeaccf1 100644 --- a/ice40/gfx.h +++ b/ice40/gfx.h @@ -464,7 +464,11 @@ enum GfxTileWireId TILE_WIRE_SP12_H_R_23, TILE_WIRE_SP12_H_L_22, - TILE_WIRE_SP12_H_L_23 + TILE_WIRE_SP12_H_L_23, + + TILE_WIRE_PLLIN, + TILE_WIRE_PLLOUT_A, + TILE_WIRE_PLLOUT_B }; void gfxTileWire(std::vector &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style); diff --git a/ice40/pack.cc b/ice40/pack.cc index 7e2e389c..f63e14f6 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -591,6 +591,38 @@ 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_pll40(ctx, ci)) { + std::unique_ptr packed = + create_ice_cell(ctx, ctx->id("ICESTORM_PLL"), ci->name.str(ctx) + "_PLL"); + packed_cells.insert(ci->name); + for (auto attr : ci->attrs) + packed->attrs[attr.first] = attr.second; + for (auto param : ci->params) + packed->params[param.first] = param.second; + + auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")]; + packed->params[ctx->id("FEEDBACK_PATH")] = feedback_path == "DELAY" ? "0" : + feedback_path == "SIMPLE" ? "1" : + feedback_path == "PHASE_AND_DELAY" ? "2" : + feedback_path == "EXTERNAL" ? "6" : feedback_path; + packed->params[ctx->id("PLLTYPE")] = std::to_string(sb_pll40_type(ctx, ci)); + + for (auto port : ci->ports) { + PortInfo &pi = port.second; + std::string newname = pi.name.str(ctx); + size_t bpos = newname.find('['); + if (bpos != std::string::npos) { + newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2); + } + if (pi.name == ctx->id("PLLOUTCOREA")) + newname = "PLLOUT_A"; + if (pi.name == ctx->id("PLLOUTCOREB")) + newname = "PLLOUT_B"; + if (pi.name == ctx->id("PLLOUTCORE")) + newname = "PLLOUT_A"; + replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); + } + new_cells.push_back(std::move(packed)); } } diff --git a/ice40/pcf.cc b/ice40/pcf.cc index ac1c8598..3d0d0d84 100644 --- a/ice40/pcf.cc +++ b/ice40/pcf.cc @@ -62,6 +62,31 @@ bool apply_pcf(Context *ctx, std::istream &in) log_info("constrained '%s' to bel '%s'\n", cell.c_str(), fnd_cell->second->attrs[ctx->id("BEL")].c_str()); } + } else if (cmd == "set_loc") { + size_t args_end = 1; + while (args_end < words.size() && words.at(args_end).at(0) == '-') + args_end++; + std::string cell = words.at(args_end); + std::string x = words.at(args_end + 1); + std::string y = words.at(args_end + 2); + std::string z = words.at(args_end + 3); + auto fnd_cell = ctx->cells.find(ctx->id(cell)); + if (fnd_cell == ctx->cells.end()) { + log_error("unmatched pcf constraint %s\n", cell.c_str()); + } + + Loc loc; + loc.x = std::stoi(x); + loc.y = std::stoi(y); + loc.z = std::stoi(z); + auto bel = ctx->getBelByLocation(loc); + if (bel == BelId()) { + log_error("constrain '%s': unknown bel\n", line.c_str()); + } + + fnd_cell->second->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx); + log_info("constrained '%s' to bel '%s'\n", cell.c_str(), ctx->getBelName(bel).c_str(ctx)); + } else { log_error("unsupported pcf command '%s'\n", cmd.c_str()); } diff --git a/ice40/portpins.inc b/ice40/portpins.inc index d78625d1..f9dac887 100644 --- a/ice40/portpins.inc +++ b/ice40/portpins.inc @@ -118,6 +118,8 @@ X(DYNAMICDELAY_5) X(DYNAMICDELAY_6) X(DYNAMICDELAY_7) X(LOCK) +X(PLLOUT_A) +X(PLLOUT_B) X(BYPASS) X(RESETB) X(LATCHINPUTVALUE) From db31c0625bb722dd9c42fead97d3988659656648 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Mon, 23 Jul 2018 16:58:11 +0100 Subject: [PATCH 02/33] ice40: Fail early on SB_PLL40_*_PAD cells --- ice40/cells.h | 7 +++++++ ice40/pack.cc | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/ice40/cells.h b/ice40/cells.h index 404f401c..4bc50e8a 100644 --- a/ice40/cells.h +++ b/ice40/cells.h @@ -78,6 +78,13 @@ inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell) cell->type == ctx->id("SB_PLL40_2F_CORE"); } +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"); +} + + uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell); // Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports diff --git a/ice40/pack.cc b/ice40/pack.cc index f63e14f6..c4d47bf4 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -595,6 +595,13 @@ static void pack_special(Context *ctx) std::unique_ptr packed = create_ice_cell(ctx, ctx->id("ICESTORM_PLL"), ci->name.str(ctx) + "_PLL"); packed_cells.insert(ci->name); + + if (is_sb_pll40_pad(ctx, ci)) { + // TODO(q3k): Implement these after checking their behaviour on + // a board with exposed 'clock pads'. + log_error("SB_PLL40_*_PAD cells are not supported yet.\n"); + } + for (auto attr : ci->attrs) packed->attrs[attr.first] = attr.second; for (auto param : ci->params) From 69233385f875d01a36e36924d3099175e7544b4e Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Mon, 23 Jul 2018 21:49:02 +0100 Subject: [PATCH 03/33] ice40: Emit feed-through LUTs for PLL/LOCK --- common/nextpnr.h | 3 - ice40/chipdb.py | 2 +- ice40/pack.cc | 159 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 159 insertions(+), 5 deletions(-) diff --git a/common/nextpnr.h b/common/nextpnr.h index 9ee00a50..021772fe 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -173,9 +173,6 @@ struct Loc bool operator==(const Loc &other) const { return (x == other.x) && (y == other.y) && (z == other.z); } bool operator!=(const Loc &other) const { return (x != other.x) || (y != other.y) || (z == other.z); } - - Loc(int x, int y, int z) : x(x), y(y), z(z) {} - Loc() {} }; NEXTPNR_NAMESPACE_END diff --git a/ice40/chipdb.py b/ice40/chipdb.py index 63f08e36..602477a0 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -617,7 +617,7 @@ def add_pll_clock_output(bel, ec, ec_entry): } wire_downhill_belports[wire_idx] = {(bel, port),} - bel_wires[bel].append((wire_idx, port)) + bel_wires[bel].append((wire_idx, port, beltypes['PLL'])) io_wire = wire_names[(io_x, io_y, 'io_{}/D_IN_0'.format(io_z))] wire_downhill[wire_idx] = {io_wire,} diff --git a/ice40/pack.cc b/ice40/pack.cc index c4d47bf4..6e56c49b 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -613,7 +613,7 @@ static void pack_special(Context *ctx) feedback_path == "PHASE_AND_DELAY" ? "2" : feedback_path == "EXTERNAL" ? "6" : feedback_path; packed->params[ctx->id("PLLTYPE")] = std::to_string(sb_pll40_type(ctx, ci)); - + for (auto port : ci->ports) { PortInfo &pi = port.second; std::string newname = pi.name.str(ctx); @@ -629,6 +629,162 @@ static void pack_special(Context *ctx) newname = "PLLOUT_A"; replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); } + + // If PLL is not constrained already, do that - we need this + // information to then constrain the LOCK LUT. + BelId pll_bel; + bool constrained = false; + if (packed->attrs.find(ctx->id("BEL")) == packed->attrs.end()) { + //FIXME replace by getBelsByType when implemented + for (auto bel : ctx->getBels()) { + if (ctx->getBelType(bel) != TYPE_ICESTORM_PLL) + continue; + log_info(" constrained '%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 '%s' to any PLL Bel\n", packed->name.c_str(ctx)); + } + } + + // The LOCK signal on iCE40 PLLs goes through the neigh_op_bnl_1 wire. + // In practice, this means the LOCK signal can only directly reach LUT + // inputs. + // 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) { + bool found_lut = false; + bool all_luts = true; + unsigned int lut_count = 0; + for (const auto &user : port.net->users) { + NPNR_ASSERT(user.cell != nullptr); + if (user.cell->type == ctx->id("ICESTORM_LC")) { + found_lut = true; + lut_count++; + } else { + all_luts = false; + } + } + + if (found_lut && all_luts) { + // Every user is a LUT, carry on now. + } else if (found_lut && !all_luts && lut_count < 8) { + // Strategy: create a pass-through LUT, move all non-LUT users behind it. + log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx)); + // Create pass-through LUT. + std::unique_ptr pt = + create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "$nextpnr_ice40_pack_pll_lc"); + packed_cells.insert(pt->name); + pt->params[ctx->id("LUT_INIT")] = "255"; // all lower bits lit, so output is always I3 + + // Create net to which all non-LUTs will be moved. + std::unique_ptr out_net = std::unique_ptr(new NetInfo); + out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_ice40_pack_pll_net"); + out_net->driver.cell = pt.get(); + out_net->driver.port = ctx->id("O"); + pt->ports.at(ctx->id("O")).net = out_net.get(); + + // Move all non-LUTs to new net. + std::vector new_users; + for (const auto &user : port.net->users) { + if (user.cell->type == ctx->id("ICESTORM_LC")) { + new_users.push_back(user); + } + // Rewrite pointer into net in user. + user.cell->ports[user.port].net = out_net.get(); + // Add user to net. + PortRef pr; + pr.cell = user.cell; + pr.port = user.port; + out_net->users.push_back(pr); + } + // Add LUT to new users. + PortRef pr; + pr.cell = pt.get(); + pr.port = ctx->id("I3"); + new_users.push_back(pr); + pt->ports.at(ctx->id("I3")).net = port.net; + + // Replace users of the original net. + port.net->users = new_users; + + ctx->nets[out_net->name] = std::move(out_net); + new_cells.push_back(std::move(pt)); + } else { + // Strategy: create a pass-through LUT, move every user behind it. + log_info(" LUT strategy for %s: move all users to new LUT\n", port.name.c_str(ctx)); + // Create pass-through LUT. + std::unique_ptr pt = + create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "$nextpnr_ice40_pack_pll_lc"); + packed_cells.insert(pt->name); + pt->params[ctx->id("LUT_INIT")] = "255"; // all lower bits lit, so output is always I3 + + // Create net to which all current users will be moved. + std::unique_ptr out_net = std::unique_ptr(new NetInfo); + out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_ice40_pack_pll_net"); + out_net->driver.cell = pt.get(); + out_net->driver.port = ctx->id("O"); + pt->ports.at(ctx->id("O")).net = out_net.get(); + + // Move all users to new net. + for (const auto &user : port.net->users) { + // Rewrite pointer into net in user. + user.cell->ports[user.port].net = out_net.get(); + // Add user to net. + PortRef pr; + pr.cell = user.cell; + pr.port = user.port; + out_net->users.push_back(pr); + } + + // Add LUT to new users. + std::vector new_users; + PortRef pr; + pr.cell = pt.get(); + pr.port = ctx->id("I3"); + new_users.push_back(pr); + pt->ports.at(ctx->id("I3")).net = port.net; + + // Replace users of the original net. + port.net->users = new_users; + + ctx->nets[out_net->name] = std::move(out_net); + new_cells.push_back(std::move(pt)); + } + + // Find wire driven by this port. + const auto &pll_beli = ctx->chip_info->bel_data[pll_bel.index]; + const WireInfoPOD *wirei = nullptr; + for (int i = 0; i < pll_beli.num_bel_wires; i++) { + auto bel_port = ctx->portPinToId(pll_beli.bel_wires[i].port); + if (port.name != bel_port) + continue; + wirei = &ctx->chip_info->wire_data[pll_beli.bel_wires[i].wire_index]; + break; + } + NPNR_ASSERT(wirei != nullptr); + + // Now, constrain all LUTs on the output of the signal to be at + // the correct Bel relative to the PLL Bel. + int x = wirei->x; + int y = wirei->y; + int z = 0; + for (const auto &user : port.net->users) { + NPNR_ASSERT(user.cell != nullptr); + NPNR_ASSERT(user.cell->type == ctx->id("ICESTORM_LC")); + + // TODO(q3k): handle when the Bel might be already the + // target of another constraint. + NPNR_ASSERT(z < 8); + auto target_bel = ctx->getBelByLocation(Loc(x, y, z++)); + auto target_bel_name = ctx->getBelName(target_bel).str(ctx); + user.cell->attrs[ctx->id("BEL")] = target_bel_name; + log_info(" constrained '%s' to %s\n", user.cell->name.c_str(ctx), target_bel_name.c_str()); + } + } + new_cells.push_back(std::move(packed)); } } @@ -649,6 +805,7 @@ bool Arch::pack() log_break(); pack_constants(ctx); promote_globals(ctx); + pack_io(ctx); pack_lut_lutffs(ctx); pack_nonlut_ffs(ctx); From e6c7b1446580c92e9581593c78dd21753b409606 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Mon, 23 Jul 2018 22:13:10 +0100 Subject: [PATCH 04/33] ice40: Refactor PLL/LOCK LUT splicing out into Arch:: --- ice40/arch.cc | 50 ++++++++++++++++++++++++++++++ ice40/arch.h | 5 +++ ice40/bitstream.cc | 1 + ice40/pack.cc | 77 ++-------------------------------------------- 4 files changed, 59 insertions(+), 74 deletions(-) diff --git a/ice40/arch.cc b/ice40/arch.cc index d6ef800d..6679217a 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -2,6 +2,7 @@ * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 Serge Bazanski * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -805,4 +806,53 @@ void Arch::assignCellInfo(CellInfo *cell) } } +std::unique_ptr Arch::spliceLUT(CellInfo *ci, IdString portId, bool onlyNonLUTs) +{ + auto port = ci->ports[portId]; + + NPNR_ASSERT(port.net != nullptr); + + + // Create pass-through LUT. + std::unique_ptr pt = + create_ice_cell(getCtx(), id("ICESTORM_LC"), ci->name.str(this) + "$nextpnr_ice40_pack_pll_lc"); + pt->params[id("LUT_INIT")] = "255"; // output is always I3 + + // Create LUT output net. + std::unique_ptr out_net = std::unique_ptr(new NetInfo); + out_net->name = id(ci->name.str(this) + "$nextnr_ice40_pack_pll_net"); + out_net->driver.cell = pt.get(); + out_net->driver.port = id("O"); + pt->ports.at(id("O")).net = out_net.get(); + + // New users of the original cell's port + std::vector new_users; + for (const auto &user : port.net->users) { + if (onlyNonLUTs && user.cell->type == id("ICESTORM_LC")) { + new_users.push_back(user); + continue; + } + // Rewrite pointer into net in user. + user.cell->ports[user.port].net = out_net.get(); + // Add user to net. + PortRef pr; + pr.cell = user.cell; + pr.port = user.port; + out_net->users.push_back(pr); + } + + // Add LUT to new users. + PortRef pr; + pr.cell = pt.get(); + pr.port = id("I3"); + new_users.push_back(pr); + pt->ports.at(id("I3")).net = port.net; + + // Replace users of the original net. + port.net->users = new_users; + + nets[out_net->name] = std::move(out_net); + return pt; +} + NEXTPNR_NAMESPACE_END diff --git a/ice40/arch.h b/ice40/arch.h index d4d71cfc..15422aa7 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -746,6 +746,11 @@ struct Arch : BaseCtx IdString id_cen, id_clk, id_sr; IdString id_i0, id_i1, id_i2, id_i3; IdString id_dff_en, id_neg_clk; + + + // ------------------------------------------------- + // Insert a LUT/LC in between a given output port of a cell and all its' users. + std::unique_ptr spliceLUT(CellInfo *ci, IdString portId, bool onlyNonLUTs); }; NEXTPNR_NAMESPACE_END diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 77d92a89..2ee8a294 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -3,6 +3,7 @@ * * Copyright (C) 2018 Clifford Wolf * Copyright (C) 2018 David Shah + * Copyright (C) 2018 Serge Bazanski * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/ice40/pack.cc b/ice40/pack.cc index 6e56c49b..dc0db578 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -3,6 +3,7 @@ * * Copyright (C) 2018 Clifford Wolf * Copyright (C) 2018 David Shah + * Copyright (C) 2018 Serge Bazanski * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -673,84 +674,12 @@ static void pack_special(Context *ctx) } else if (found_lut && !all_luts && lut_count < 8) { // Strategy: create a pass-through LUT, move all non-LUT users behind it. log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx)); - // Create pass-through LUT. - std::unique_ptr pt = - create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "$nextpnr_ice40_pack_pll_lc"); - packed_cells.insert(pt->name); - pt->params[ctx->id("LUT_INIT")] = "255"; // all lower bits lit, so output is always I3 - - // Create net to which all non-LUTs will be moved. - std::unique_ptr out_net = std::unique_ptr(new NetInfo); - out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_ice40_pack_pll_net"); - out_net->driver.cell = pt.get(); - out_net->driver.port = ctx->id("O"); - pt->ports.at(ctx->id("O")).net = out_net.get(); - - // Move all non-LUTs to new net. - std::vector new_users; - for (const auto &user : port.net->users) { - if (user.cell->type == ctx->id("ICESTORM_LC")) { - new_users.push_back(user); - } - // Rewrite pointer into net in user. - user.cell->ports[user.port].net = out_net.get(); - // Add user to net. - PortRef pr; - pr.cell = user.cell; - pr.port = user.port; - out_net->users.push_back(pr); - } - // Add LUT to new users. - PortRef pr; - pr.cell = pt.get(); - pr.port = ctx->id("I3"); - new_users.push_back(pr); - pt->ports.at(ctx->id("I3")).net = port.net; - - // Replace users of the original net. - port.net->users = new_users; - - ctx->nets[out_net->name] = std::move(out_net); + auto pt = ctx->spliceLUT(packed.get(), port.name, true); new_cells.push_back(std::move(pt)); } else { // Strategy: create a pass-through LUT, move every user behind it. log_info(" LUT strategy for %s: move all users to new LUT\n", port.name.c_str(ctx)); - // Create pass-through LUT. - std::unique_ptr pt = - create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "$nextpnr_ice40_pack_pll_lc"); - packed_cells.insert(pt->name); - pt->params[ctx->id("LUT_INIT")] = "255"; // all lower bits lit, so output is always I3 - - // Create net to which all current users will be moved. - std::unique_ptr out_net = std::unique_ptr(new NetInfo); - out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_ice40_pack_pll_net"); - out_net->driver.cell = pt.get(); - out_net->driver.port = ctx->id("O"); - pt->ports.at(ctx->id("O")).net = out_net.get(); - - // Move all users to new net. - for (const auto &user : port.net->users) { - // Rewrite pointer into net in user. - user.cell->ports[user.port].net = out_net.get(); - // Add user to net. - PortRef pr; - pr.cell = user.cell; - pr.port = user.port; - out_net->users.push_back(pr); - } - - // Add LUT to new users. - std::vector new_users; - PortRef pr; - pr.cell = pt.get(); - pr.port = ctx->id("I3"); - new_users.push_back(pr); - pt->ports.at(ctx->id("I3")).net = port.net; - - // Replace users of the original net. - port.net->users = new_users; - - ctx->nets[out_net->name] = std::move(out_net); + auto pt = ctx->spliceLUT(packed.get(), port.name, false); new_cells.push_back(std::move(pt)); } From 1d3147e26a2d57085199b645c12df5ab4836850e Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Tue, 24 Jul 2018 01:18:49 +0100 Subject: [PATCH 05/33] ice40: Prevent placement of SB_IOs in IO blocks used by PLL outputs --- ice40/arch_place.cc | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc index cf1276a7..9c76851b 100644 --- a/ice40/arch_place.cc +++ b/ice40/arch_place.cc @@ -106,6 +106,30 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const bel_cells.push_back(cell); return logicCellsCompatible(bel_cells); } else if (cell->type == id_sb_io) { + // Do not allow placement of input SB_IOs on blocks where there a PLL is outputting to. + for (auto iter_bel : getBels()) { + if (getBelType(iter_bel) != TYPE_ICESTORM_PLL) + continue; + if (checkBelAvail(iter_bel)) + continue; + + auto bel_cell = cells.at(getBoundBelCell(iter_bel)).get(); + for (auto type : {id("PLLOUT_A"), id("PLLOUT_B")}) { + auto it = bel_cell->ports.find(type); + if (it == bel_cell->ports.end()) + continue; + if (it->second.net == nullptr) + continue; + auto wire = getBelPinWire(iter_bel, portPinFromId(type)); + for (auto pip : getPipsDownhill(wire)) { + auto driven_wire = getPipDstWire(pip); + auto io_bel = chip_info->wire_data[driven_wire.index].bel_uphill.bel_index; + if (io_bel == bel.index) { + return false; + } + } + } + } return getBelPackagePin(bel) != ""; } else if (cell->type == id_sb_gb) { NPNR_ASSERT(cell->ports.at(id_glb_buf_out).net != nullptr); From 065ea95eaba3e0d2389695a3bddde774e5251ee9 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Tue, 24 Jul 2018 01:24:07 +0100 Subject: [PATCH 06/33] ice40: Move spliceLUT back to pack.cc --- ice40/arch.cc | 49 --------------------------------------------- ice40/arch.h | 5 ----- ice40/pack.cc | 55 +++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 53 insertions(+), 56 deletions(-) diff --git a/ice40/arch.cc b/ice40/arch.cc index 6679217a..5c632faa 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -806,53 +806,4 @@ void Arch::assignCellInfo(CellInfo *cell) } } -std::unique_ptr Arch::spliceLUT(CellInfo *ci, IdString portId, bool onlyNonLUTs) -{ - auto port = ci->ports[portId]; - - NPNR_ASSERT(port.net != nullptr); - - - // Create pass-through LUT. - std::unique_ptr pt = - create_ice_cell(getCtx(), id("ICESTORM_LC"), ci->name.str(this) + "$nextpnr_ice40_pack_pll_lc"); - pt->params[id("LUT_INIT")] = "255"; // output is always I3 - - // Create LUT output net. - std::unique_ptr out_net = std::unique_ptr(new NetInfo); - out_net->name = id(ci->name.str(this) + "$nextnr_ice40_pack_pll_net"); - out_net->driver.cell = pt.get(); - out_net->driver.port = id("O"); - pt->ports.at(id("O")).net = out_net.get(); - - // New users of the original cell's port - std::vector new_users; - for (const auto &user : port.net->users) { - if (onlyNonLUTs && user.cell->type == id("ICESTORM_LC")) { - new_users.push_back(user); - continue; - } - // Rewrite pointer into net in user. - user.cell->ports[user.port].net = out_net.get(); - // Add user to net. - PortRef pr; - pr.cell = user.cell; - pr.port = user.port; - out_net->users.push_back(pr); - } - - // Add LUT to new users. - PortRef pr; - pr.cell = pt.get(); - pr.port = id("I3"); - new_users.push_back(pr); - pt->ports.at(id("I3")).net = port.net; - - // Replace users of the original net. - port.net->users = new_users; - - nets[out_net->name] = std::move(out_net); - return pt; -} - NEXTPNR_NAMESPACE_END diff --git a/ice40/arch.h b/ice40/arch.h index 15422aa7..d4d71cfc 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -746,11 +746,6 @@ struct Arch : BaseCtx IdString id_cen, id_clk, id_sr; IdString id_i0, id_i1, id_i2, id_i3; IdString id_dff_en, id_neg_clk; - - - // ------------------------------------------------- - // Insert a LUT/LC in between a given output port of a cell and all its' users. - std::unique_ptr spliceLUT(CellInfo *ci, IdString portId, bool onlyNonLUTs); }; NEXTPNR_NAMESPACE_END diff --git a/ice40/pack.cc b/ice40/pack.cc index dc0db578..14abc1d0 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -537,6 +537,57 @@ static void promote_globals(Context *ctx) } } +// spliceLUT adds a pass-through LUT LC between the given cell's output port +// and either all users or only non_LUT users. +static std::unique_ptr spliceLUT(Context *ctx, CellInfo *ci, IdString portId, bool onlyNonLUTs) +{ + auto port = ci->ports[portId]; + + NPNR_ASSERT(port.net != nullptr); + + + // Create pass-through LUT. + std::unique_ptr pt = + create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "$nextpnr_ice40_pack_pll_lc"); + pt->params[ctx->id("LUT_INIT")] = "255"; // output is always I3 + + // Create LUT output net. + std::unique_ptr out_net = std::unique_ptr(new NetInfo); + out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_ice40_pack_pll_net"); + out_net->driver.cell = pt.get(); + out_net->driver.port = ctx->id("O"); + pt->ports.at(ctx->id("O")).net = out_net.get(); + + // New users of the original cell's port + std::vector new_users; + for (const auto &user : port.net->users) { + if (onlyNonLUTs && user.cell->type == ctx->id("ICESTORM_LC")) { + new_users.push_back(user); + continue; + } + // Rewrite pointer into net in user. + user.cell->ports[user.port].net = out_net.get(); + // Add user to net. + PortRef pr; + pr.cell = user.cell; + pr.port = user.port; + out_net->users.push_back(pr); + } + + // Add LUT to new users. + PortRef pr; + pr.cell = pt.get(); + pr.port = ctx->id("I3"); + new_users.push_back(pr); + pt->ports.at(ctx->id("I3")).net = port.net; + + // Replace users of the original net. + port.net->users = new_users; + + ctx->nets[out_net->name] = std::move(out_net); + return pt; +} + // Pack special functions static void pack_special(Context *ctx) { @@ -674,12 +725,12 @@ static void pack_special(Context *ctx) } else if (found_lut && !all_luts && lut_count < 8) { // Strategy: create a pass-through LUT, move all non-LUT users behind it. log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx)); - auto pt = ctx->spliceLUT(packed.get(), port.name, true); + auto pt = spliceLUT(ctx, packed.get(), port.name, true); new_cells.push_back(std::move(pt)); } else { // Strategy: create a pass-through LUT, move every user behind it. log_info(" LUT strategy for %s: move all users to new LUT\n", port.name.c_str(ctx)); - auto pt = ctx->spliceLUT(packed.get(), port.name, false); + auto pt = spliceLUT(ctx, packed.get(), port.name, false); new_cells.push_back(std::move(pt)); } From dbf79d78bbae387817fc442cfda06b5b17c32c40 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Tue, 24 Jul 2018 01:36:55 +0100 Subject: [PATCH 07/33] ice40: A slightly nicer way to do this. --- ice40/bitstream.cc | 77 +++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 46 deletions(-) diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 2ee8a294..c388fe2b 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -444,55 +444,40 @@ void write_asc(const Context *ctx, std::ostream &out) if (port.second.net == nullptr) continue; - // Find SB_IO Bel in net that's driving a wire. That's the one - // we're routing the signal through. - // TODO(q3k): Is there a nicer way to do this? - bool found = false; + // Get IO Bel that this PLL port goes through. + // We navigate one pip downhill to the next wire (there should + // be only one). Then, the bel that drives that wire should be + // the SB_IO that we're looking for. + auto wire = ctx->getBelPinWire(cell.second->bel, ctx->portPinFromId(port.second.name)); + auto pips = ctx->getPipsDownhill(wire).begin(); + auto driven_wire = ctx->getPipDstWire(*pips); + auto io_bel = ctx->chip_info->wire_data[driven_wire.index].bel_uphill.bel_index; + auto io_beli = ctx->chip_info->bel_data[io_bel]; + NPNR_ASSERT(io_beli.type == TYPE_SB_IO); - // For every wire in the PLLOUT net... - for (auto wp : port.second.net->wires) { - // ... get its' uphill bel ... - auto bel = ctx->getBelPinUphill(wp.first).bel; - if (bel == BelId()) { - continue; - } - // ... and check if it's an SB_IO. - if (ctx->getBelType(bel) != TYPE_SB_IO) - continue; - - // Check that this SB_IO is either unused or just used as an output. - const BelInfoPOD &io_beli = ci.bel_data[bel.index]; - auto io_loc = Loc(io_beli.x, io_beli.y, io_beli.z); - - if (sb_io_used_by_user.count(io_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(Loc(io_beli.x, io_beli.y, io_beli.z)); - - // Get IE/REN config location (cf. http://www.clifford.at/icestorm/io_tile.html) - auto ieren = get_ieren(bi, io_beli.x, io_beli.y, io_beli.z); - int iex, iey, iez; - std::tie(iex, iey, iez) = ieren; - NPNR_ASSERT(iez != -1); - - // 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. - set_config(ti, config.at(io_beli.y).at(io_beli.x), "IOB_" + std::to_string(io_beli.z) + ".PINTYPE_0", true); - - found = true; - break; + // Check that this SB_IO is either unused or just used as an output. + auto io_loc = Loc(io_beli.x, io_beli.y, io_beli.z); + if (sb_io_used_by_user.count(io_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(Loc(io_beli.x, io_beli.y, io_beli.z)); + + // Get IE/REN config location (cf. http://www.clifford.at/icestorm/io_tile.html) + auto ieren = get_ieren(bi, io_beli.x, io_beli.y, io_beli.z); + int iex, iey, iez; + std::tie(iex, iey, iez) = ieren; + NPNR_ASSERT(iez != -1); + + // 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. + set_config(ti, config.at(io_beli.y).at(io_beli.x), "IOB_" + std::to_string(io_beli.z) + ".PINTYPE_0", true); - if (!found) { - log_error("Could not find SB_IO forwarding PLL '%s' %s signal\n", - cell.second->name.c_str(ctx), port.second.name.c_str(ctx)); - } } } else { From fae7994bc34b302dbd35c0793a9ce9f81234dbc1 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Tue, 24 Jul 2018 01:38:20 +0100 Subject: [PATCH 08/33] clang-format --- common/router1.cc | 1 - ice40/bitstream.cc | 35 ++++++++++-------- ice40/cells.h | 1 - ice40/pack.cc | 90 +++++++++++++++++++++++----------------------- 4 files changed, 67 insertions(+), 60 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index 21c54e4c..fbf3c467 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -210,7 +210,6 @@ struct Router else overtimeRevisitCnt++; } - QueuedWire next_qw; next_qw.wire = next_wire; diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index c388fe2b..1d799307 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -88,7 +88,9 @@ void set_config(const TileInfoPOD &ti, std::vector> &tile_cf // Set an IE_{EN,REN} logical bit in a tile config. Logical means enabled. // On {HX,LP}1K devices these bits are active low, so we need to inver them. -void set_ie_bit_logical(const Context *ctx, const TileInfoPOD &ti, std::vector> &tile_cfg, const std::string &name, bool value) { +void set_ie_bit_logical(const Context *ctx, const TileInfoPOD &ti, std::vector> &tile_cfg, + const std::string &name, bool value) +{ if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) { set_config(ti, tile_cfg, name, !value); } else { @@ -320,7 +322,7 @@ 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_user.insert(Loc(x,y,z)); + sb_io_used_by_user.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")); @@ -424,14 +426,20 @@ void write_asc(const Context *ctx, std::ostream &out) {"B_SIGNED", 1}}; configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false, std::string("IpConfig.")); } else if (cell.second->type == ctx->id("ICESTORM_PLL")) { - const std::vector> pll_params = { - {"DELAY_ADJMODE_FB", 1}, {"DELAY_ADJMODE_REL", 1}, - {"DIVF", 7}, {"DIVQ", 3}, {"DIVR", 4}, - {"FDA_FEEDBACK", 4}, {"FDA_RELATIVE", 4}, - {"FEEDBACK_PATH", 3}, {"FILTER_RANGE", 3}, - {"PLLOUT_SELECT_A", 2}, {"PLLOUT_SELECT_B", 2}, - {"PLLTYPE", 3}, {"SHIFTREG_DIV_MODE", 1}, {"TEST_MODE", 1} - }; + const std::vector> pll_params = {{"DELAY_ADJMODE_FB", 1}, + {"DELAY_ADJMODE_REL", 1}, + {"DIVF", 7}, + {"DIVQ", 3}, + {"DIVR", 4}, + {"FDA_FEEDBACK", 4}, + {"FDA_RELATIVE", 4}, + {"FEEDBACK_PATH", 3}, + {"FILTER_RANGE", 3}, + {"PLLOUT_SELECT_A", 2}, + {"PLLOUT_SELECT_B", 2}, + {"PLLTYPE", 3}, + {"SHIFTREG_DIV_MODE", 1}, + {"TEST_MODE", 1}}; 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. @@ -458,8 +466,7 @@ void write_asc(const Context *ctx, std::ostream &out) // Check that this SB_IO is either unused or just used as an output. auto io_loc = Loc(io_beli.x, io_beli.y, io_beli.z); if (sb_io_used_by_user.count(io_loc)) { - log_error("SB_IO '%s' already in use, cannot route PLL through\n", - ctx->getBelName(bel).c_str(ctx)); + 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(Loc(io_beli.x, io_beli.y, io_beli.z)); @@ -476,8 +483,8 @@ void write_asc(const Context *ctx, std::ostream &out) 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. - set_config(ti, config.at(io_beli.y).at(io_beli.x), "IOB_" + std::to_string(io_beli.z) + ".PINTYPE_0", true); - + set_config(ti, config.at(io_beli.y).at(io_beli.x), "IOB_" + std::to_string(io_beli.z) + ".PINTYPE_0", + true); } } else { diff --git a/ice40/cells.h b/ice40/cells.h index 4bc50e8a..16135448 100644 --- a/ice40/cells.h +++ b/ice40/cells.h @@ -84,7 +84,6 @@ inline bool is_sb_pll40_pad(const BaseCtx *ctx, const CellInfo *cell) cell->type == ctx->id("SB_PLL40_2F_PAD"); } - uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell); // Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports diff --git a/ice40/pack.cc b/ice40/pack.cc index 14abc1d0..8552e381 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -541,51 +541,50 @@ static void promote_globals(Context *ctx) // and either all users or only non_LUT users. static std::unique_ptr spliceLUT(Context *ctx, CellInfo *ci, IdString portId, bool onlyNonLUTs) { - auto port = ci->ports[portId]; + auto port = ci->ports[portId]; - NPNR_ASSERT(port.net != nullptr); + NPNR_ASSERT(port.net != nullptr); + // Create pass-through LUT. + std::unique_ptr pt = + create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "$nextpnr_ice40_pack_pll_lc"); + pt->params[ctx->id("LUT_INIT")] = "255"; // output is always I3 - // Create pass-through LUT. - std::unique_ptr pt = - create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "$nextpnr_ice40_pack_pll_lc"); - pt->params[ctx->id("LUT_INIT")] = "255"; // output is always I3 + // Create LUT output net. + std::unique_ptr out_net = std::unique_ptr(new NetInfo); + out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_ice40_pack_pll_net"); + out_net->driver.cell = pt.get(); + out_net->driver.port = ctx->id("O"); + pt->ports.at(ctx->id("O")).net = out_net.get(); - // Create LUT output net. - std::unique_ptr out_net = std::unique_ptr(new NetInfo); - out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_ice40_pack_pll_net"); - out_net->driver.cell = pt.get(); - out_net->driver.port = ctx->id("O"); - pt->ports.at(ctx->id("O")).net = out_net.get(); + // New users of the original cell's port + std::vector new_users; + for (const auto &user : port.net->users) { + if (onlyNonLUTs && user.cell->type == ctx->id("ICESTORM_LC")) { + new_users.push_back(user); + continue; + } + // Rewrite pointer into net in user. + user.cell->ports[user.port].net = out_net.get(); + // Add user to net. + PortRef pr; + pr.cell = user.cell; + pr.port = user.port; + out_net->users.push_back(pr); + } - // New users of the original cell's port - std::vector new_users; - for (const auto &user : port.net->users) { - if (onlyNonLUTs && user.cell->type == ctx->id("ICESTORM_LC")) { - new_users.push_back(user); - continue; - } - // Rewrite pointer into net in user. - user.cell->ports[user.port].net = out_net.get(); - // Add user to net. - PortRef pr; - pr.cell = user.cell; - pr.port = user.port; - out_net->users.push_back(pr); - } + // Add LUT to new users. + PortRef pr; + pr.cell = pt.get(); + pr.port = ctx->id("I3"); + new_users.push_back(pr); + pt->ports.at(ctx->id("I3")).net = port.net; - // Add LUT to new users. - PortRef pr; - pr.cell = pt.get(); - pr.port = ctx->id("I3"); - new_users.push_back(pr); - pt->ports.at(ctx->id("I3")).net = port.net; + // Replace users of the original net. + port.net->users = new_users; - // Replace users of the original net. - port.net->users = new_users; - - ctx->nets[out_net->name] = std::move(out_net); - return pt; + ctx->nets[out_net->name] = std::move(out_net); + return pt; } // Pack special functions @@ -660,10 +659,13 @@ static void pack_special(Context *ctx) packed->params[param.first] = param.second; auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")]; - packed->params[ctx->id("FEEDBACK_PATH")] = feedback_path == "DELAY" ? "0" : - feedback_path == "SIMPLE" ? "1" : - feedback_path == "PHASE_AND_DELAY" ? "2" : - feedback_path == "EXTERNAL" ? "6" : feedback_path; + packed->params[ctx->id("FEEDBACK_PATH")] = + feedback_path == "DELAY" + ? "0" + : feedback_path == "SIMPLE" ? "1" + : feedback_path == "PHASE_AND_DELAY" + ? "2" + : feedback_path == "EXTERNAL" ? "6" : feedback_path; packed->params[ctx->id("PLLTYPE")] = std::to_string(sb_pll40_type(ctx, ci)); for (auto port : ci->ports) { @@ -687,7 +689,7 @@ static void pack_special(Context *ctx) BelId pll_bel; bool constrained = false; if (packed->attrs.find(ctx->id("BEL")) == packed->attrs.end()) { - //FIXME replace by getBelsByType when implemented + // FIXME replace by getBelsByType when implemented for (auto bel : ctx->getBels()) { if (ctx->getBelType(bel) != TYPE_ICESTORM_PLL) continue; @@ -759,7 +761,7 @@ static void pack_special(Context *ctx) // target of another constraint. NPNR_ASSERT(z < 8); auto target_bel = ctx->getBelByLocation(Loc(x, y, z++)); - auto target_bel_name = ctx->getBelName(target_bel).str(ctx); + auto target_bel_name = ctx->getBelName(target_bel).str(ctx); user.cell->attrs[ctx->id("BEL")] = target_bel_name; log_info(" constrained '%s' to %s\n", user.cell->name.c_str(ctx), target_bel_name.c_str()); } From 65ceb20784ccd0e2be71c733dbc23dc61d83d653 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Tue, 24 Jul 2018 02:05:30 +0100 Subject: [PATCH 09/33] ice40: emit list of upbels in chipdb --- ice40/arch.h | 4 ++-- ice40/arch_place.cc | 2 +- ice40/bitstream.cc | 2 +- ice40/chipdb.py | 30 ++++++++++++++++++------------ 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/ice40/arch.h b/ice40/arch.h index d4d71cfc..f0060d48 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -83,8 +83,8 @@ NPNR_PACKED_STRUCT(struct WireInfoPOD { int32_t num_uphill, num_downhill; RelPtr pips_uphill, pips_downhill; - int32_t num_bels_downhill; - BelPortPOD bel_uphill; + int32_t num_bels_uphill, num_bels_downhill; + RelPtr bels_uphill; RelPtr bels_downhill; int32_t num_bel_pins; diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc index 9c76851b..95cebb48 100644 --- a/ice40/arch_place.cc +++ b/ice40/arch_place.cc @@ -123,7 +123,7 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const auto wire = getBelPinWire(iter_bel, portPinFromId(type)); for (auto pip : getPipsDownhill(wire)) { auto driven_wire = getPipDstWire(pip); - auto io_bel = chip_info->wire_data[driven_wire.index].bel_uphill.bel_index; + auto io_bel = chip_info->wire_data[driven_wire.index].bels_uphill[0].bel_index; if (io_bel == bel.index) { return false; } diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 1d799307..934cca0c 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -459,7 +459,7 @@ void write_asc(const Context *ctx, std::ostream &out) auto wire = ctx->getBelPinWire(cell.second->bel, ctx->portPinFromId(port.second.name)); auto pips = ctx->getPipsDownhill(wire).begin(); auto driven_wire = ctx->getPipDstWire(*pips); - auto io_bel = ctx->chip_info->wire_data[driven_wire.index].bel_uphill.bel_index; + auto io_bel = ctx->chip_info->wire_data[driven_wire.index].bels_uphill[0].bel_index; auto io_beli = ctx->chip_info->bel_data[io_bel]; NPNR_ASSERT(io_beli.type == TYPE_SB_IO); diff --git a/ice40/chipdb.py b/ice40/chipdb.py index 602477a0..38989a0b 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -41,7 +41,7 @@ extra_cells = dict() extra_cell_config = dict() packages = list() -wire_uphill_belport = dict() +wire_uphill_belports = dict() wire_downhill_belports = dict() wire_belports = dict() @@ -462,8 +462,9 @@ def add_bel_input(bel, wire, port): bel_wires[bel].append((wire, port, 0)) def add_bel_output(bel, wire, port): - assert wire not in wire_uphill_belport - wire_uphill_belport[wire] = (bel, port) + if wire not in wire_uphill_belports: + wire_uphill_belports[wire] = set() + wire_uphill_belports[wire].add((bel, port)) if wire not in wire_belports: wire_belports[wire] = set() wire_belports[wire].add((bel, port)) @@ -1042,6 +1043,15 @@ for wire in range(num_wires): num_downhill = 0 list_downhill = None + if wire in wire_uphill_belports: + num_bels_uphill = len(wire_uphill_belports[wire]) + bba.l("wire%d_upbels" % wire, "BelPortPOD") + for belport in sorted(wire_uphill_belports[wire]): + bba.u32(belport[0], "bel_index") + bba.u32(portpins[belport[1]], "port") + else: + num_bels_uphill = 0 + if wire in wire_downhill_belports: num_bels_downhill = len(wire_downhill_belports[wire]) bba.l("wire%d_downbels" % wire, "BelPortPOD") @@ -1072,16 +1082,12 @@ for wire in range(num_wires): info["num_bels_downhill"] = num_bels_downhill info["list_bels_downhill"] = ("wire%d_downbels" % wire) if num_bels_downhill > 0 else None + info["num_bels_uphill"] = num_bels_uphill + info["list_bels_uphill"] = ("wire%d_upbels" % wire) if num_bels_uphill > 0 else None + info["num_bel_pins"] = num_bel_pins info["list_bel_pins"] = ("wire%d_bels" % wire) if num_bel_pins > 0 else None - if wire in wire_uphill_belport: - info["uphill_bel"] = wire_uphill_belport[wire][0] - info["uphill_pin"] = portpins[wire_uphill_belport[wire][1]] - else: - info["uphill_bel"] = -1 - info["uphill_pin"] = 0 - avg_x, avg_y = 0, 0 if wire in wire_xy: for x, y in wire_xy[wire]: @@ -1156,9 +1162,9 @@ for wire, info in enumerate(wireinfo): bba.u32(info["num_downhill"], "num_downhill") bba.r(info["list_uphill"], "pips_uphill") bba.r(info["list_downhill"], "pips_downhill") + bba.u32(info["num_bels_uphill"], "num_bels_uphill") bba.u32(info["num_bels_downhill"], "num_bels_downhill") - bba.u32(info["uphill_bel"], "bel_uphill.bel_index") - bba.u32(info["uphill_pin"], "bel_uphill.port") + bba.r(info["list_bels_uphill"], "bels_uphill") bba.r(info["list_bels_downhill"], "bels_downhill") bba.u32(info["num_bel_pins"], "num_bel_pins") bba.r(info["list_bel_pins"], "bel_pins") From eaae1d299c030be85aa9eb3a45ce2c02afe919f1 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Tue, 24 Jul 2018 02:35:16 +0100 Subject: [PATCH 10/33] ice40: move PLL->IO from pseudo pip to second uphill bel --- ice40/arch_place.cc | 40 +++++++++++++++++++++------------------- ice40/bitstream.cc | 31 ++++++++++++++++--------------- ice40/chipdb.py | 38 +++----------------------------------- 3 files changed, 40 insertions(+), 69 deletions(-) diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc index 95cebb48..00c960b6 100644 --- a/ice40/arch_place.cc +++ b/ice40/arch_place.cc @@ -107,26 +107,28 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const return logicCellsCompatible(bel_cells); } else if (cell->type == id_sb_io) { // Do not allow placement of input SB_IOs on blocks where there a PLL is outputting to. - for (auto iter_bel : getBels()) { - if (getBelType(iter_bel) != TYPE_ICESTORM_PLL) - continue; - if (checkBelAvail(iter_bel)) - continue; - auto bel_cell = cells.at(getBoundBelCell(iter_bel)).get(); - for (auto type : {id("PLLOUT_A"), id("PLLOUT_B")}) { - auto it = bel_cell->ports.find(type); - if (it == bel_cell->ports.end()) - continue; - if (it->second.net == nullptr) - continue; - auto wire = getBelPinWire(iter_bel, portPinFromId(type)); - for (auto pip : getPipsDownhill(wire)) { - auto driven_wire = getPipDstWire(pip); - auto io_bel = chip_info->wire_data[driven_wire.index].bels_uphill[0].bel_index; - if (io_bel == bel.index) { - return false; - } + // Find shared PLL by looking for driving bel siblings from D_IN_0 + // that are a PLL clock output. + auto wire = getBelPinWire(bel, PIN_D_IN_0); + PortPin pll_bel_pin; + BelId pll_bel; + for (auto pin : getWireBelPins(wire)) { + if (pin.pin == PIN_PLLOUT_A || pin.pin == PIN_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) { + // Is a PLL placed in this PLL bel? + if (!checkBelAvail(pll_bel)) { + // Is the shared port driving a net? + auto pll_cell = getBoundBelCell(pll_bel); + auto pi = cells.at(pll_cell)->ports[portPinToId(pll_bel_pin)]; + if (pi.net != nullptr) { + return false; } } } diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 934cca0c..7c3a26ca 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -452,26 +452,27 @@ void write_asc(const Context *ctx, std::ostream &out) if (port.second.net == nullptr) continue; - // Get IO Bel that this PLL port goes through. - // We navigate one pip downhill to the next wire (there should - // be only one). Then, the bel that drives that wire should be - // the SB_IO that we're looking for. + // 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, ctx->portPinFromId(port.second.name)); - auto pips = ctx->getPipsDownhill(wire).begin(); - auto driven_wire = ctx->getPipDstWire(*pips); - auto io_bel = ctx->chip_info->wire_data[driven_wire.index].bels_uphill[0].bel_index; - auto io_beli = ctx->chip_info->bel_data[io_bel]; - NPNR_ASSERT(io_beli.type == TYPE_SB_IO); + BelId io_bel; + for (auto pin : ctx->getWireBelPins(wire)) { + if (pin.pin == PIN_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. - auto io_loc = Loc(io_beli.x, io_beli.y, io_beli.z); - if (sb_io_used_by_user.count(io_loc)) { + if (sb_io_used_by_user.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(Loc(io_beli.x, io_beli.y, io_beli.z)); + 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_beli.x, io_beli.y, io_beli.z); + 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); @@ -483,8 +484,8 @@ void write_asc(const Context *ctx, std::ostream &out) 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. - set_config(ti, config.at(io_beli.y).at(io_beli.x), "IOB_" + std::to_string(io_beli.z) + ".PINTYPE_0", - true); + 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); } } else { diff --git a/ice40/chipdb.py b/ice40/chipdb.py index 38989a0b..63d90b45 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -597,40 +597,6 @@ 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, ec_entry): - #print('add_pll_clock_output', ec, ec_entry) - pll_x, pll_y, pll_z = ec[1], ec[2], ec[3] - port = ec_entry[0] - io_x, io_y, io_z = ec_entry[1] - io_z = int(io_z) - - global num_wires - wire_idx = num_wires - num_wires = num_wires + 1 - - wire_xy[wire_idx] = [(pll_x, pll_y)] - - wire_names_r[wire_idx] = (pll_x, pll_y, port) - wire_names[(pll_x, pll_y, port)] = wire_idx - wire_segments[wire_idx] = { - (pll_x, pll_y): port, - (io_x, io_y): 'PLLIN', - } - - wire_downhill_belports[wire_idx] = {(bel, port),} - bel_wires[bel].append((wire_idx, port, beltypes['PLL'])) - - io_wire = wire_names[(io_x, io_y, 'io_{}/D_IN_0'.format(io_z))] - wire_downhill[wire_idx] = {io_wire,} - if io_wire not in wire_uphill: - wire_uphill[io_wire] = set() - wire_uphill[io_wire].add(wire_idx) - - switches.append((io_x, io_y, 0, [])) - switchnum = len(switches) - 1 - pip_xy[(wire_idx, io_wire)] = (io_x, io_y, 0, switchnum) - - def add_bel_ec(ec): ectype, x, y, z = ec bel = len(bel_name) @@ -646,7 +612,9 @@ def add_bel_ec(ec): else: add_bel_input(bel, wire_names[entry[1]], entry[0]) elif is_ec_pll_clock_output(ec, entry): - add_pll_clock_output(bel, 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]) else: extra_cell_config[bel].append(entry) From 90ba958abe85ced03f16888644dd026b133cab36 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Tue, 24 Jul 2018 03:03:31 +0100 Subject: [PATCH 11/33] ice40: fixes before review --- gui/fpgaviewwidget.cc | 3 +-- ice40/arch_place.cc | 1 + ice40/bitstream.cc | 12 ++++++------ ice40/cells.cc | 1 + ice40/pack.cc | 19 +++++-------------- ice40/pcf.cc | 25 ------------------------- 6 files changed, 14 insertions(+), 47 deletions(-) diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index cd042af5..e21af678 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -400,7 +400,6 @@ void FPGAViewWidget::paintGL() // Calculate world thickness to achieve a screen 1px/1.1px line. float thick1Px = mouseToWorldCoordinates(1, 0).x(); float thick11Px = mouseToWorldCoordinates(1.1, 0).x(); - float thick2Px = mouseToWorldCoordinates(2, 0).x(); // Draw grid. auto grid = LineShaderData(); @@ -419,7 +418,7 @@ void FPGAViewWidget::paintGL() for (int i = 0; i < 8; i++) lineShader_.draw(rendererData_->highlighted[i], colors_.highlight[i], thick11Px, matrix); - lineShader_.draw(rendererData_->selected, colors_.selected, thick2Px, matrix); + lineShader_.draw(rendererData_->selected, colors_.selected, thick11Px, matrix); rendererDataLock_.unlock(); } diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc index 00c960b6..16cc757e 100644 --- a/ice40/arch_place.cc +++ b/ice40/arch_place.cc @@ -3,6 +3,7 @@ * * Copyright (C) 2018 Clifford Wolf * Copyright (C) 2018 David Shah + * Copyright (C) 2018 Serge Bazanski * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 7c3a26ca..e9851a83 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -87,7 +87,7 @@ void set_config(const TileInfoPOD &ti, std::vector> &tile_cf } // Set an IE_{EN,REN} logical bit in a tile config. Logical means enabled. -// On {HX,LP}1K devices these bits are active low, so we need to inver them. +// On {HX,LP}1K devices these bits are active low, so we need to invert them. void set_ie_bit_logical(const Context *ctx, const TileInfoPOD &ti, std::vector> &tile_cfg, const std::string &name, bool value) { @@ -101,9 +101,9 @@ void set_ie_bit_logical(const Context *ctx, const TileInfoPOD &ti, std::vectorparams.find(param); - if (found != cell->params.end()) { + if (found != cell->params.end()) return std::stoi(found->second); - } else + else return defval; } @@ -273,7 +273,7 @@ void write_asc(const Context *ctx, std::ostream &out) } std::unordered_set sb_io_used_by_pll; - std::unordered_set sb_io_used_by_user; + std::unordered_set sb_io_used_by_io; // Set logic cell config for (auto &cell : ctx->cells) { @@ -322,7 +322,7 @@ 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_user.insert(Loc(x, y, 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")); @@ -466,7 +466,7 @@ void write_asc(const Context *ctx, std::ostream &out) 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_user.count(io_bel_loc)) { + 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); diff --git a/ice40/cells.cc b/ice40/cells.cc index 3215a38c..610bf85e 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -3,6 +3,7 @@ * * Copyright (C) 2018 Clifford Wolf * Copyright (C) 2018 David Shah + * Copyright (C) 2018 Serge Bazanski * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/ice40/pack.cc b/ice40/pack.cc index 8552e381..03b33190 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -736,22 +736,14 @@ static void pack_special(Context *ctx) new_cells.push_back(std::move(pt)); } - // Find wire driven by this port. - const auto &pll_beli = ctx->chip_info->bel_data[pll_bel.index]; - const WireInfoPOD *wirei = nullptr; - for (int i = 0; i < pll_beli.num_bel_wires; i++) { - auto bel_port = ctx->portPinToId(pll_beli.bel_wires[i].port); - if (port.name != bel_port) - continue; - wirei = &ctx->chip_info->wire_data[pll_beli.bel_wires[i].wire_index]; - break; - } - NPNR_ASSERT(wirei != nullptr); + // Find wire that will be driven by this port. + const auto pll_out_wire = ctx->getBelPinWire(pll_bel, ctx->portPinFromId(port.name)); + NPNR_ASSERT(pll_out_wire.index != -1); // Now, constrain all LUTs on the output of the signal to be at // the correct Bel relative to the PLL Bel. - int x = wirei->x; - int y = wirei->y; + int x = ctx->chip_info->wire_data[pll_out_wire.index].x; + int y = ctx->chip_info->wire_data[pll_out_wire.index].y; int z = 0; for (const auto &user : port.net->users) { NPNR_ASSERT(user.cell != nullptr); @@ -787,7 +779,6 @@ bool Arch::pack() log_break(); pack_constants(ctx); promote_globals(ctx); - pack_io(ctx); pack_lut_lutffs(ctx); pack_nonlut_ffs(ctx); diff --git a/ice40/pcf.cc b/ice40/pcf.cc index 3d0d0d84..ac1c8598 100644 --- a/ice40/pcf.cc +++ b/ice40/pcf.cc @@ -62,31 +62,6 @@ bool apply_pcf(Context *ctx, std::istream &in) log_info("constrained '%s' to bel '%s'\n", cell.c_str(), fnd_cell->second->attrs[ctx->id("BEL")].c_str()); } - } else if (cmd == "set_loc") { - size_t args_end = 1; - while (args_end < words.size() && words.at(args_end).at(0) == '-') - args_end++; - std::string cell = words.at(args_end); - std::string x = words.at(args_end + 1); - std::string y = words.at(args_end + 2); - std::string z = words.at(args_end + 3); - auto fnd_cell = ctx->cells.find(ctx->id(cell)); - if (fnd_cell == ctx->cells.end()) { - log_error("unmatched pcf constraint %s\n", cell.c_str()); - } - - Loc loc; - loc.x = std::stoi(x); - loc.y = std::stoi(y); - loc.z = std::stoi(z); - auto bel = ctx->getBelByLocation(loc); - if (bel == BelId()) { - log_error("constrain '%s': unknown bel\n", line.c_str()); - } - - fnd_cell->second->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx); - log_info("constrained '%s' to bel '%s'\n", cell.c_str(), ctx->getBelName(bel).c_str(ctx)); - } else { log_error("unsupported pcf command '%s'\n", cmd.c_str()); } From a09f95bb06025afecd5753c969443928f1f0894a Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 24 Jul 2018 11:16:33 +0200 Subject: [PATCH 12/33] ice40: Fix SPRAM and other primitives in corners other than (0, 0) Signed-off-by: David Shah --- ice40/chipdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ice40/chipdb.py b/ice40/chipdb.py index 0e8e3ba7..4dcddbed 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -662,7 +662,7 @@ for tile_xy, tile_type in sorted(tiles.items()): add_bel_ec(ec) for ec in sorted(extra_cells.keys()): - if ec[1] == 0 and ec[2] == 0: + if ec[1] in (0, dev_width - 1) and ec[2] in (0, dev_height - 1): add_bel_ec(ec) class BinaryBlobAssembler: From 4359197dfe49f981966222679d8270b0d4dc311d Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 24 Jul 2018 11:21:10 +0200 Subject: [PATCH 13/33] ice40: Trim BRAM constant inputs, reduces routing congestion around BRAM Signed-off-by: David Shah --- ice40/pack.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ice40/pack.cc b/ice40/pack.cc index 7e2e389c..164fa756 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -311,6 +311,9 @@ static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constne (user.port != ctx->id("CLK") && ((constval && user.port == ctx->id("CE")) || (!constval && user.port != ctx->id("CE"))))) { uc->ports[user.port].net = nullptr; + } else if (is_ram(ctx, uc) && !constval && user.port != ctx->id("RCLK") && user.port != ctx->id("RCLKN") && user.port != ctx->id("WCLK") && user.port != ctx->id("WCLKN") + && user.port != ctx->id("RCLKE") && user.port != ctx->id("WCLKE")) { + uc->ports[user.port].net = nullptr; } else { uc->ports[user.port].net = constnet; constnet->users.push_back(user); From 7858663aa7f211cebde2d543f7d0094d84ca11b1 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 24 Jul 2018 11:46:14 +0200 Subject: [PATCH 14/33] timing: Model clock to Q times Signed-off-by: David Shah --- common/timing.cc | 3 +++ ice40/arch.cc | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/common/timing.cc b/common/timing.cc index 3a48935f..d91dea20 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -88,6 +88,9 @@ void assign_budget(Context *ctx) IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first); if (clock_domain != IdString()) { delay_t slack = delay_t(1.0e12 / ctx->target_freq); // TODO: clock constraints + delay_t clkToQ; + if (ctx->getCellDelay(cell.second.get(), clock_domain, port.first, clkToQ)) + slack -= clkToQ; if (port.second.net) follow_net(ctx, port.second.net, 0, slack); } diff --git a/ice40/arch.cc b/ice40/arch.cc index 65b21afd..c29673cc 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -733,6 +733,14 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort } else if (fromPort == id("I2") && toPort == id("COUT")) { delay = 230; return true; + } else if (fromPort == id("CLK") && toPort == id("O")) { + delay = 540; + return true; + } + } else if (cell->type == id("ICESTORM_RAM")) { + if (fromPort == id("RCLK")) { + delay = 2140; + return true; } } return false; @@ -743,6 +751,11 @@ IdString Arch::getPortClock(const CellInfo *cell, IdString port) const if (cell->type == id("ICESTORM_LC") && bool_or_default(cell->params, id("DFF_ENABLE"))) { if (port != id("LO") && port != id("CIN") && port != id("COUT")) return id("CLK"); + } else if (cell->type == id("ICESTORM_RAM")) { + if (port.str(this)[0] == 'R') + return id("RCLK"); + else + return id("WCLK"); } return IdString(); } @@ -751,6 +764,8 @@ bool Arch::isClockPort(const CellInfo *cell, IdString port) const { if (cell->type == id("ICESTORM_LC") && port == id("CLK")) return true; + if (cell->type == id("ICESTORM_RAM") && (port == id("RCLK") || (port == id("WCLK")))) + return true; return false; } From 9d38907e95dac76a6b9754562752c36e3f65b9ec Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 24 Jul 2018 12:18:01 +0200 Subject: [PATCH 15/33] Add G_ARROW (for now same look as G_LINE) Signed-off-by: Clifford Wolf --- common/nextpnr.h | 1 + gui/fpgaviewwidget.cc | 4 ++-- ice40/gfx.cc | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/common/nextpnr.h b/common/nextpnr.h index 021772fe..3ba4f3b3 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -147,6 +147,7 @@ struct GraphicElement { G_NONE, G_LINE, + G_ARROW, G_BOX, G_CIRCLE, G_LABEL diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index e21af678..cd288deb 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -327,7 +327,7 @@ void FPGAViewWidget::drawDecal(LineShaderData &out, const DecalXY &decal) line.build(out); } - if (el.type == GraphicElement::G_LINE) { + if (el.type == GraphicElement::G_LINE || el.type == GraphicElement::G_ARROW) { PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2, offsetY + scale * el.y2) .build(out); } @@ -360,7 +360,7 @@ void FPGAViewWidget::drawDecal(LineShaderData out[], const DecalXY &decal) } } - if (el.type == GraphicElement::G_LINE) { + if (el.type == GraphicElement::G_LINE || el.type == GraphicElement::G_ARROW) { auto line = PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2, offsetY + scale * el.y2); switch (el.style) { diff --git a/ice40/gfx.cc b/ice40/gfx.cc index f6ed789f..1b01cbd8 100644 --- a/ice40/gfx.cc +++ b/ice40/gfx.cc @@ -647,7 +647,7 @@ void pipGfx(std::vector &g, int x, int y, float x1, float y1, fl float ty = 0.5 * (y1 + y2); GraphicElement el; - el.type = GraphicElement::G_LINE; + el.type = GraphicElement::G_ARROW; el.style = style; if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) { From f61e9e56094946492bdd364ab272c19919a9faca Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 24 Jul 2018 12:22:57 +0200 Subject: [PATCH 16/33] ecp5: Set BANKREF to correct VccIO Signed-off-by: David Shah --- ecp5/bitstream.cc | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 19ddb9f9..1efee5fc 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -30,6 +30,7 @@ #include #include +#include "io.h" #include "log.h" #include "util.h" @@ -182,12 +183,34 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } } } + // Find bank voltages + std::unordered_map bankVcc; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->bel != BelId() && ci->type == ctx->id("TRELLIS_IO")) { + int bank = ctx->getPioBelBank(ci->bel); + std::string iotype = str_or_default(ci->attrs, ctx->id("IO_TYPE"), "LVCMOS33"); + IOVoltage vcc = get_vccio(ioType_from_str(iotype)); + if (bankVcc.find(bank) != bankVcc.end()) { + // TODO: strong and weak constraints + if (bankVcc[bank] != vcc) { + log_error("Error processing '%s': incompatible IO voltages %s and %s on bank %d.", + cell.first.c_str(ctx), iovoltage_to_str(bankVcc[bank]).c_str(), + iovoltage_to_str(vcc).c_str(), bank); + } + } else { + bankVcc[bank] = vcc; + } + } + } - // Set all bankref tiles to 3.3V (TODO) + // Set all bankref tiles to appropriate VccIO for (const auto &tile : empty_chip.tiles) { std::string type = tile.second->info.type; if (type.find("BANKREF") != std::string::npos && type != "BANKREF8") { - cc.tiles[tile.first].add_enum("BANK.VCCIO", "3V3"); + int bank = std::stoi(type.substr(7)); + if (bankVcc.find(bank) != bankVcc.end()) + cc.tiles[tile.first].add_enum("BANK.VCCIO", iovoltage_to_str(bankVcc[bank])); } } From c3cbc274ac6395b083bea465404ed58f62136194 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 24 Jul 2018 12:24:14 +0200 Subject: [PATCH 17/33] Change G_FRAME color to be significantly darker than G_ACTIVE Signed-off-by: Clifford Wolf --- gui/fpgaviewwidget.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index cd288deb..7ccb3445 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -246,7 +246,7 @@ FPGAViewWidget::FPGAViewWidget(QWidget *parent) { colors_.background = QColor("#000000"); colors_.grid = QColor("#333"); - colors_.frame = QColor("#d0d0d0"); + colors_.frame = QColor("#808080"); colors_.hidden = QColor("#606060"); colors_.inactive = QColor("#303030"); colors_.active = QColor("#f0f0f0"); From c06bca07134b8b52733b025d19fd26910cd9a4e3 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 24 Jul 2018 12:27:41 +0200 Subject: [PATCH 18/33] Add dummy bba main Signed-off-by: Clifford Wolf --- bba/main.cc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 bba/main.cc diff --git a/bba/main.cc b/bba/main.cc new file mode 100644 index 00000000..42b79504 --- /dev/null +++ b/bba/main.cc @@ -0,0 +1,15 @@ +#include +#include +int main() +{ + char buffer[512]; + int i, j; + while (1) { + i = read(0, buffer, 512); + if (i == 0) break; + assert(i > 0); + j = write(1, buffer, i); + assert(i == j); + } + return 0; +} From 1538d26073911e6969bca8cbcff3da09891a846b Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 24 Jul 2018 12:39:33 +0200 Subject: [PATCH 19/33] Add CellInfo data for placement constraints Signed-off-by: Clifford Wolf --- common/nextpnr.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/common/nextpnr.h b/common/nextpnr.h index 3ba4f3b3..ca0854e5 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -273,6 +273,16 @@ struct CellInfo : ArchCellInfo // cell_port -> bel_pin std::unordered_map pins; + + // placement constraints + CellInfo *constr_parent; + stdL::vector constr_children; + const int UNCONSTR = INT_MIN; + int constr_x = UNCONSTR; // this.x - parent.x + int constr_y = UNCONSTR; // this.y - parent.y + int constr_z = UNCONSTR; // this.z - parent.z + bool constr_abs_z = false; // parent.z := 0 + // parent.[xyz] := 0 when (constr_parent == nullptr) }; struct DeterministicRNG From f86fc6e7fdf91341a1bd44415e578767fa0b13fb Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 24 Jul 2018 12:41:08 +0200 Subject: [PATCH 20/33] Typo fix Signed-off-by: Clifford Wolf --- common/nextpnr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/nextpnr.h b/common/nextpnr.h index ca0854e5..4a1aaac1 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -276,7 +276,7 @@ struct CellInfo : ArchCellInfo // placement constraints CellInfo *constr_parent; - stdL::vector constr_children; + std::vector constr_children; const int UNCONSTR = INT_MIN; int constr_x = UNCONSTR; // this.x - parent.x int constr_y = UNCONSTR; // this.y - parent.y From 35a6bc496eb28f26c42e25a5e746987329e96f3e Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 24 Jul 2018 12:57:54 +0200 Subject: [PATCH 21/33] ecp5: Support for differential IO Signed-off-by: David Shah --- ecp5/bitstream.cc | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 1efee5fc..c2218762 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -258,6 +258,20 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex std::string pic_tile = get_pic_tile(ctx, empty_chip, bel); cc.tiles[pio_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype); cc.tiles[pic_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype); + if (is_differential(ioType_from_str(iotype))) { + // Explicitly disable other pair + std::string other; + if (pio == "PIOA") + other = "PIOB"; + else if (pio == "PIOC") + other = "PIOD"; + else + log_error("cannot place differential IO at location %s\n", pio.c_str()); + cc.tiles[pio_tile].add_enum(other + ".BASE_TYPE", "_NONE_"); + cc.tiles[pic_tile].add_enum(other + ".BASE_TYPE", "_NONE_"); + cc.tiles[pio_tile].add_enum(other + ".PULLMODE", "NONE"); + cc.tiles[pio_tile].add_enum(pio + ".PULLMODE", "NONE"); + } if (dir != "INPUT" && (ci->ports.find(ctx->id("T")) == ci->ports.end() || ci->ports.at(ctx->id("T")).net == nullptr)) { // Tie tristate low if unconnected for outputs or bidir @@ -270,7 +284,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex std::string cib_wirename = ctx->locInfo(cib_wire)->wire_data[cib_wire.index].name.get(); cc.tiles[cib_tile].add_enum("CIB." + cib_wirename + "MUX", "0"); } - if (dir == "INPUT") { + if (dir == "INPUT" && !is_differential(ioType_from_str(iotype))) { cc.tiles[pio_tile].add_enum(pio + ".HYSTERESIS", "ON"); } } else { From 942c552e079abef96d0d87fa367329fbdc0bc053 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 24 Jul 2018 15:31:00 +0200 Subject: [PATCH 22/33] Add bbasm target, use as passthru in iCE40 builder Signed-off-by: David Shah --- .gitignore | 2 ++ CMakeLists.txt | 2 ++ bba/bba.cmake | 12 ++++++++++++ ice40/family.cmake | 23 +++++++++++++++++------ 4 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 bba/bba.cmake diff --git a/.gitignore b/.gitignore index 6ca9e19f..f3be78e5 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ build/ /Testing/* CTestTestfile.cmake install_manifest.txt +/bbasm +/ImportExecutables.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index c8ee6805..55e57763 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,6 +167,8 @@ if(MINGW) add_definitions("-Wa,-mbig-obj") endif(MINGW) +include(bba/bba.cmake) + foreach (family ${ARCH}) message(STATUS "Configuring architecture : ${family}") string(TOUPPER ${family} ufamily) diff --git a/bba/bba.cmake b/bba/bba.cmake new file mode 100644 index 00000000..4bb9ea0e --- /dev/null +++ b/bba/bba.cmake @@ -0,0 +1,12 @@ +IF(CMAKE_CROSSCOMPILING) + SET(IMPORT_EXECUTABLES "IMPORTFILE-NOTFOUND" CACHE FILEPATH "Point it to the export file from a native build") + INCLUDE(${IMPORT_EXECUTABLES}) +ENDIF(CMAKE_CROSSCOMPILING) + +IF(NOT CMAKE_CROSSCOMPILING) + ADD_EXECUTABLE(bbasm bba/main.cc) +ENDIF(NOT CMAKE_CROSSCOMPILING) + +IF(NOT CMAKE_CROSSCOMPILING) + EXPORT(TARGETS bbasm FILE ${CMAKE_BINARY_DIR}/ImportExecutables.cmake ) +ENDIF(NOT CMAKE_CROSSCOMPILING) diff --git a/ice40/family.cmake b/ice40/family.cmake index 9af06f82..95cdf331 100644 --- a/ice40/family.cmake +++ b/ice40/family.cmake @@ -19,13 +19,18 @@ if (MSVC) set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ice40/resources/chipdb.rc PROPERTIES LANGUAGE RC) foreach (dev ${devices}) set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt) + set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bba) set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bin) set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc) set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h) - add_custom_command(OUTPUT ${DEV_CC_DB} - COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -b -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_DB} + add_custom_command(OUTPUT ${DEV_CC_BBA_DB} + COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -b -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB} DEPENDS ${DEV_TXT_DB} ${DB_PY} ) + add_custom_command(OUTPUT ${DEV_CC_DB} + COMMAND bbasm < ${DEV_CC_BBA_DB} > ${DEV_CC_DB} + DEPENDS bbasm ${DEV_CC_BBA_DB} + ) target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB}) set_source_files_properties(${DEV_CC_DB} PROPERTIES HEADER_FILE_ONLY TRUE) foreach (target ${family_targets}) @@ -36,14 +41,20 @@ else() target_compile_options(ice40_chipdb PRIVATE -g0 -O0 -w) foreach (dev ${devices}) set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt) + set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bba) set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.cc) set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc) set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h) - add_custom_command(OUTPUT ${DEV_CC_DB} - COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -c -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_DB}.new - COMMAND mv ${DEV_CC_DB}.new ${DEV_CC_DB} + add_custom_command(OUTPUT ${DEV_CC_BBA_DB} + COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -c -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}.new + COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB} DEPENDS ${DEV_TXT_DB} ${DB_PY} - ) + ) + add_custom_command(OUTPUT ${DEV_CC_DB} + COMMAND bbasm < ${DEV_CC_BBA_DB} > ${DEV_CC_DB}.new + COMMAND mv ${DEV_CC_DB}.new ${DEV_CC_DB} + DEPENDS bbasm ${DEV_CC_BBA_DB} + ) target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB}) foreach (target ${family_targets}) target_sources(${target} PRIVATE $) From c0c8dc760290cb78611f3764219a7604f6f73c44 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 24 Jul 2018 15:44:39 +0200 Subject: [PATCH 23/33] Remove uphill/downhill bel pins from ice40 db Signed-off-by: Clifford Wolf --- ice40/arch.h | 4 ---- ice40/chipdb.py | 30 ------------------------------ 2 files changed, 34 deletions(-) diff --git a/ice40/arch.h b/ice40/arch.h index d4d71cfc..3f6a5324 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -83,10 +83,6 @@ NPNR_PACKED_STRUCT(struct WireInfoPOD { int32_t num_uphill, num_downhill; RelPtr pips_uphill, pips_downhill; - int32_t num_bels_downhill; - BelPortPOD bel_uphill; - RelPtr bels_downhill; - int32_t num_bel_pins; RelPtr bel_pins; diff --git a/ice40/chipdb.py b/ice40/chipdb.py index 4dcddbed..1127767d 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -41,8 +41,6 @@ extra_cells = dict() extra_cell_config = dict() packages = list() -wire_uphill_belport = dict() -wire_downhill_belports = dict() wire_belports = dict() wire_names = dict() @@ -451,17 +449,12 @@ for i in range(8): add_wire(0, 0, "padin_%d" % i) def add_bel_input(bel, wire, port): - if wire not in wire_downhill_belports: - wire_downhill_belports[wire] = set() - wire_downhill_belports[wire].add((bel, port)) if wire not in wire_belports: wire_belports[wire] = set() wire_belports[wire].add((bel, port)) bel_wires[bel].append((wire, port, 0)) def add_bel_output(bel, wire, port): - assert wire not in wire_uphill_belport - wire_uphill_belport[wire] = (bel, port) if wire not in wire_belports: wire_belports[wire] = set() wire_belports[wire].add((bel, port)) @@ -1001,15 +994,6 @@ for wire in range(num_wires): num_downhill = 0 list_downhill = None - if wire in wire_downhill_belports: - num_bels_downhill = len(wire_downhill_belports[wire]) - bba.l("wire%d_downbels" % wire, "BelPortPOD") - for belport in sorted(wire_downhill_belports[wire]): - bba.u32(belport[0], "bel_index") - bba.u32(portpins[belport[1]], "port") - else: - num_bels_downhill = 0 - if wire in wire_belports: num_bel_pins = len(wire_belports[wire]) bba.l("wire%d_bels" % wire, "BelPortPOD") @@ -1028,19 +1012,9 @@ for wire in range(num_wires): info["num_downhill"] = num_downhill info["list_downhill"] = list_downhill - info["num_bels_downhill"] = num_bels_downhill - info["list_bels_downhill"] = ("wire%d_downbels" % wire) if num_bels_downhill > 0 else None - info["num_bel_pins"] = num_bel_pins info["list_bel_pins"] = ("wire%d_bels" % wire) if num_bel_pins > 0 else None - if wire in wire_uphill_belport: - info["uphill_bel"] = wire_uphill_belport[wire][0] - info["uphill_pin"] = portpins[wire_uphill_belport[wire][1]] - else: - info["uphill_bel"] = -1 - info["uphill_pin"] = 0 - avg_x, avg_y = 0, 0 if wire in wire_xy: for x, y in wire_xy[wire]: @@ -1115,10 +1089,6 @@ for wire, info in enumerate(wireinfo): bba.u32(info["num_downhill"], "num_downhill") bba.r(info["list_uphill"], "pips_uphill") bba.r(info["list_downhill"], "pips_downhill") - bba.u32(info["num_bels_downhill"], "num_bels_downhill") - bba.u32(info["uphill_bel"], "bel_uphill.bel_index") - bba.u32(info["uphill_pin"], "bel_uphill.port") - bba.r(info["list_bels_downhill"], "bels_downhill") bba.u32(info["num_bel_pins"], "num_bel_pins") bba.r(info["list_bel_pins"], "bel_pins") bba.u32(len(wire_segments[wire]), "num_segments") From e200c281d988b30ee39bd4d5934d6e6b2d3d4b6d Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 24 Jul 2018 15:45:49 +0200 Subject: [PATCH 24/33] common: Remove use of deprecated APIs Signed-off-by: David Shah --- common/archcheck.cc | 5 ++--- common/place_common.cc | 22 +++++++++++----------- common/placer1.cc | 26 +++++++++++--------------- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/common/archcheck.cc b/common/archcheck.cc index 5c4ef26c..5059066d 100644 --- a/common/archcheck.cc +++ b/common/archcheck.cc @@ -17,8 +17,8 @@ * */ -#include "nextpnr.h" #include "log.h" +#include "nextpnr.h" #if 0 #define dbg(...) log(__VA_ARGS__) @@ -84,8 +84,7 @@ void archcheck_locs(const Context *ctx) log_info("Checking all locations..\n"); for (int x = 0; x < ctx->getGridDimX(); x++) - for (int y = 0; y < ctx->getGridDimY(); y++) - { + for (int y = 0; y < ctx->getGridDimY(); y++) { dbg("> %d %d\n", x, y); std::unordered_set usedz; diff --git a/common/place_common.cc b/common/place_common.cc index 95b7b2aa..5673c847 100644 --- a/common/place_common.cc +++ b/common/place_common.cc @@ -28,19 +28,20 @@ NEXTPNR_NAMESPACE_BEGIN wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type, float &tns) { wirelen_t wirelength = 0; - int driver_x, driver_y; + Loc driver_loc; bool driver_gb; CellInfo *driver_cell = net->driver.cell; if (!driver_cell) return 0; if (driver_cell->bel == BelId()) return 0; - ctx->estimatePosition(driver_cell->bel, driver_x, driver_y, driver_gb); + driver_gb = ctx->getBelGlobalBuf(driver_cell->bel); + driver_loc = ctx->getBelLocation(driver_cell->bel); WireId drv_wire = ctx->getBelPinWire(driver_cell->bel, ctx->portPinFromId(net->driver.port)); if (driver_gb) return 0; float worst_slack = 1000; - int xmin = driver_x, xmax = driver_x, ymin = driver_y, ymax = driver_y; + int xmin = driver_loc.x, xmax = driver_loc.x, ymin = driver_loc.y, ymax = driver_loc.y; for (auto load : net->users) { if (load.cell == nullptr) continue; @@ -56,15 +57,14 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type worst_slack = std::min(slack, worst_slack); } - int load_x, load_y; - bool load_gb; - ctx->estimatePosition(load_cell->bel, load_x, load_y, load_gb); - if (load_gb) + if (ctx->getBelGlobalBuf(load_cell->bel)) continue; - xmin = std::min(xmin, load_x); - ymin = std::min(ymin, load_y); - xmax = std::max(xmax, load_x); - ymax = std::max(ymax, load_y); + Loc load_loc = ctx->getBelLocation(load_cell->bel); + + xmin = std::min(xmin, load_loc.x); + ymin = std::min(ymin, load_loc.y); + xmax = std::max(xmax, load_loc.x); + ymax = std::max(ymax, load_loc.y); } if (ctx->timing_driven && type == MetricType::COST) { wirelength = wirelen_t((((ymax - ymin) + (xmax - xmin)) * std::min(5.0, (1.0 + std::exp(-worst_slack / 5))))); diff --git a/common/placer1.cc b/common/placer1.cc index 025c7c15..f713fb88 100644 --- a/common/placer1.cc +++ b/common/placer1.cc @@ -50,9 +50,7 @@ class SAPlacer { int num_bel_types = 0; for (auto bel : ctx->getBels()) { - int x, y; - bool gb; - ctx->estimatePosition(bel, x, y, gb); + Loc loc = ctx->getBelLocation(bel); BelType type = ctx->getBelType(bel); int type_idx; if (bel_types.find(type) == bel_types.end()) { @@ -63,13 +61,13 @@ class SAPlacer } if (int(fast_bels.size()) < type_idx + 1) fast_bels.resize(type_idx + 1); - if (int(fast_bels.at(type_idx).size()) < (x + 1)) - fast_bels.at(type_idx).resize(x + 1); - if (int(fast_bels.at(type_idx).at(x).size()) < (y + 1)) - fast_bels.at(type_idx).at(x).resize(y + 1); - max_x = std::max(max_x, x); - max_y = std::max(max_y, y); - fast_bels.at(type_idx).at(x).at(y).push_back(bel); + if (int(fast_bels.at(type_idx).size()) < (loc.x + 1)) + fast_bels.at(type_idx).resize(loc.x + 1); + if (int(fast_bels.at(type_idx).at(loc.x).size()) < (loc.y + 1)) + fast_bels.at(type_idx).at(loc.x).resize(loc.y + 1); + max_x = std::max(max_x, loc.x); + max_y = std::max(max_y, loc.y); + fast_bels.at(type_idx).at(loc.x).at(loc.y).push_back(bel); } diameter = std::max(max_x, max_y) + 1; } @@ -404,12 +402,10 @@ class SAPlacer BelId random_bel_for_cell(CellInfo *cell) { BelType targetType = ctx->belTypeFromId(cell->type); - int x, y; - bool gb; - ctx->estimatePosition(cell->bel, x, y, gb); + Loc curr_loc = ctx->getBelLocation(cell->bel); while (true) { - int nx = ctx->rng(2 * diameter + 1) + std::max(x - diameter, 0); - int ny = ctx->rng(2 * diameter + 1) + std::max(y - diameter, 0); + int nx = ctx->rng(2 * diameter + 1) + std::max(curr_loc.x - diameter, 0); + int ny = ctx->rng(2 * diameter + 1) + std::max(curr_loc.y - diameter, 0); int beltype_idx = bel_types.at(targetType); if (nx >= int(fast_bels.at(beltype_idx).size())) continue; From 5a170f286c2868375d93477ad3bd08ccc30a15a1 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 24 Jul 2018 15:52:56 +0200 Subject: [PATCH 25/33] ice40: Remove use of deprecated APIs Signed-off-by: David Shah --- ice40/arch_place.cc | 7 ++++--- ice40/pack.cc | 5 +++-- ice40/place_legaliser.cc | 8 +++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc index cf1276a7..d32ebe98 100644 --- a/ice40/arch_place.cc +++ b/ice40/arch_place.cc @@ -71,7 +71,8 @@ bool Arch::isBelLocationValid(BelId bel) const { if (getBelType(bel) == TYPE_ICESTORM_LC) { std::vector bel_cells; - for (auto bel_other : getBelsAtSameTile(bel)) { + Loc bel_loc = getBelLocation(bel); + for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) { IdString cell_other = getBoundBelCell(bel_other); if (cell_other != IdString()) { const CellInfo *ci_other = cells.at(cell_other).get(); @@ -94,8 +95,8 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const NPNR_ASSERT(getBelType(bel) == TYPE_ICESTORM_LC); std::vector bel_cells; - - for (auto bel_other : getBelsAtSameTile(bel)) { + Loc bel_loc = getBelLocation(bel); + for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) { IdString cell_other = getBoundBelCell(bel_other); if (cell_other != IdString() && bel_other != bel) { const CellInfo *ci_other = cells.at(cell_other).get(); diff --git a/ice40/pack.cc b/ice40/pack.cc index 164fa756..f054be6b 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -311,8 +311,9 @@ static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constne (user.port != ctx->id("CLK") && ((constval && user.port == ctx->id("CE")) || (!constval && user.port != ctx->id("CE"))))) { uc->ports[user.port].net = nullptr; - } else if (is_ram(ctx, uc) && !constval && user.port != ctx->id("RCLK") && user.port != ctx->id("RCLKN") && user.port != ctx->id("WCLK") && user.port != ctx->id("WCLKN") - && user.port != ctx->id("RCLKE") && user.port != ctx->id("WCLKE")) { + } else if (is_ram(ctx, uc) && !constval && user.port != ctx->id("RCLK") && user.port != ctx->id("RCLKN") && + user.port != ctx->id("WCLK") && user.port != ctx->id("WCLKN") && user.port != ctx->id("RCLKE") && + user.port != ctx->id("WCLKE")) { uc->ports[user.port].net = nullptr; } else { uc->ports[user.port].net = constnet; diff --git a/ice40/place_legaliser.cc b/ice40/place_legaliser.cc index 2aefb839..ebc2b865 100644 --- a/ice40/place_legaliser.cc +++ b/ice40/place_legaliser.cc @@ -75,11 +75,9 @@ static void get_chain_midpoint(const Context *ctx, const CellChain &chain, float for (auto cell : chain.cells) { if (cell->bel == BelId()) continue; - int bel_x, bel_y; - bool bel_gb; - ctx->estimatePosition(cell->bel, bel_x, bel_y, bel_gb); - total_x += bel_x; - total_y += bel_y; + Loc bel_loc = ctx->getBelLocation(cell->bel); + total_x += bel_loc.x; + total_y += bel_loc.y; N++; } NPNR_ASSERT(N > 0); From 974ca143e80ac48b0e87054001a48b0d6597c6fa Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 24 Jul 2018 16:09:29 +0200 Subject: [PATCH 26/33] Remove implementations of deprecated APIs Signed-off-by: David Shah --- ecp5/arch.cc | 38 +++++--------------------------------- ecp5/arch.h | 3 --- ecp5/arch_place.cc | 7 ++++--- generic/arch.cc | 7 ------- generic/arch.h | 1 - ice40/arch.cc | 25 ------------------------- ice40/arch.h | 3 --- ice40/arch_pybindings.cc | 2 -- 8 files changed, 9 insertions(+), 77 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 7d67dd0c..ea8abc0e 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -192,14 +192,14 @@ BelId Arch::getBelByName(IdString name) const return ret; } -BelRange Arch::getBelsAtSameTile(BelId bel) const +BelRange Arch::getBelsByTile(int x, int y) const { BelRange br; - NPNR_ASSERT(bel != BelId()); - br.b.cursor_tile = bel.location.y * chip_info->width + bel.location.x; - br.e.cursor_tile = bel.location.y * chip_info->width + bel.location.x; + + br.b.cursor_tile = y * chip_info->width + x; + br.e.cursor_tile = y * chip_info->width + x; br.b.cursor_index = 0; - br.e.cursor_index = locInfo(bel)->num_bels - 1; + br.e.cursor_index = chip_info->locations[chip_info->location_type[br.b.cursor_tile]].num_bels - 1; br.b.chip = chip_info; br.e.chip = chip_info; ++br.e; @@ -399,36 +399,8 @@ BelId Arch::getBelByLocation(Loc loc) const return BelId(); } -BelRange Arch::getBelsByTile(int x, int y) const -{ - BelRange br; - - int num_bels = 0; - - if (x < chip_info->width && y < chip_info->height) { - const LocationTypePOD &locI = chip_info->locations[chip_info->location_type[y * chip_info->width + x]]; - num_bels = locI.num_bels; - } - - br.b.cursor_tile = y * chip_info->width + x; - br.e.cursor_tile = y * chip_info->width + x; - br.b.cursor_index = 0; - br.e.cursor_index = num_bels - 1; - br.b.chip = chip_info; - br.e.chip = chip_info; - ++br.e; - return br; -} - // ----------------------------------------------------------------------- -void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const -{ - x = bel.location.x; - y = bel.location.y; - gb = false; -} - delay_t Arch::estimateDelay(WireId src, WireId dst) const { return 200 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y)); diff --git a/ecp5/arch.h b/ecp5/arch.h index ce2b90c3..4400db5e 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -482,8 +482,6 @@ struct Arch : BaseCtx return range; } - BelRange getBelsAtSameTile(BelId bel) const; - BelType getBelType(BelId bel) const { NPNR_ASSERT(bel != BelId()); @@ -752,7 +750,6 @@ struct Arch : BaseCtx // ------------------------------------------------- - void estimatePosition(BelId bel, int &x, int &y, bool &gb) const; delay_t estimateDelay(WireId src, WireId dst) const; delay_t getDelayEpsilon() const { return 20; } delay_t getRipupDelayPenalty() const { return 200; } diff --git a/ecp5/arch_place.cc b/ecp5/arch_place.cc index 22ebab67..84432043 100644 --- a/ecp5/arch_place.cc +++ b/ecp5/arch_place.cc @@ -66,7 +66,8 @@ bool Arch::isBelLocationValid(BelId bel) const { if (getBelType(bel) == TYPE_TRELLIS_SLICE) { std::vector bel_cells; - for (auto bel_other : getBelsAtSameTile(bel)) { + Loc bel_loc = getBelLocation(bel); + for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) { IdString cell_other = getBoundBelCell(bel_other); if (cell_other != IdString()) { const CellInfo *ci_other = cells.at(cell_other).get(); @@ -89,8 +90,8 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const NPNR_ASSERT(getBelType(bel) == TYPE_TRELLIS_SLICE); std::vector bel_cells; - - for (auto bel_other : getBelsAtSameTile(bel)) { + Loc bel_loc = getBelLocation(bel); + for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) { IdString cell_other = getBoundBelCell(bel_other); if (cell_other != IdString() && bel_other != bel) { const CellInfo *ci_other = cells.at(cell_other).get(); diff --git a/generic/arch.cc b/generic/arch.cc index 0fff2e4c..af245a4b 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -368,13 +368,6 @@ const std::vector &Arch::getGroupGroups(GroupId group) const { return g // --------------------------------------------------------------- -void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const -{ - x = bels.at(bel).x; - y = bels.at(bel).y; - gb = bels.at(bel).gb; -} - delay_t Arch::estimateDelay(WireId src, WireId dst) const { const WireInfo &s = wires.at(src); diff --git a/generic/arch.h b/generic/arch.h index 61e6b033..3025bda0 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -191,7 +191,6 @@ struct Arch : BaseCtx const std::vector &getGroupPips(GroupId group) const; const std::vector &getGroupGroups(GroupId group) const; - void estimatePosition(BelId bel, int &x, int &y, bool &gb) const; delay_t estimateDelay(WireId src, WireId dst) const; delay_t getDelayEpsilon() const { return 0.01; } delay_t getRipupDelayPenalty() const { return 1.0; } diff --git a/ice40/arch.cc b/ice40/arch.cc index c29673cc..2270fdc1 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -291,23 +291,6 @@ BelRange Arch::getBelsByTile(int x, int y) const return br; } -BelRange Arch::getBelsAtSameTile(BelId bel) const -{ - BelRange br; - NPNR_ASSERT(bel != BelId()); - int x = chip_info->bel_data[bel.index].x; - int y = chip_info->bel_data[bel.index].y; - int start = bel.index, end = bel.index; - while (start >= 0 && chip_info->bel_data[start].x == x && chip_info->bel_data[start].y == y) - start--; - start++; - br.b.cursor = start; - while (end < chip_info->num_bels && chip_info->bel_data[end].x == x && chip_info->bel_data[end].y == y) - end++; - br.e.cursor = end; - return br; -} - PortType Arch::getBelPinType(BelId bel, PortPin pin) const { NPNR_ASSERT(bel != BelId()); @@ -482,14 +465,6 @@ std::vector Arch::getGroupGroups(GroupId group) const // ----------------------------------------------------------------------- -void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const -{ - NPNR_ASSERT(bel != BelId()); - x = chip_info->bel_data[bel.index].x; - y = chip_info->bel_data[bel.index].y; - gb = chip_info->bel_data[bel.index].type == TYPE_SB_GB; -} - delay_t Arch::estimateDelay(WireId src, WireId dst) const { NPNR_ASSERT(src != WireId()); diff --git a/ice40/arch.h b/ice40/arch.h index 3f6a5324..3aec25a2 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -449,8 +449,6 @@ struct Arch : BaseCtx bool getBelGlobalBuf(BelId bel) const { return chip_info->bel_data[bel.index].type == TYPE_SB_GB; } - BelRange getBelsAtSameTile(BelId bel) const NPNR_DEPRECATED; - BelType getBelType(BelId bel) const { NPNR_ASSERT(bel != BelId()); @@ -681,7 +679,6 @@ struct Arch : BaseCtx // ------------------------------------------------- - void estimatePosition(BelId bel, int &x, int &y, bool &gb) const NPNR_DEPRECATED; delay_t estimateDelay(WireId src, WireId dst) const; delay_t getDelayEpsilon() const { return 20; } delay_t getRipupDelayPenalty() const { return 200; } diff --git a/ice40/arch_pybindings.cc b/ice40/arch_pybindings.cc index 246d0f57..98164e87 100644 --- a/ice40/arch_pybindings.cc +++ b/ice40/arch_pybindings.cc @@ -79,8 +79,6 @@ void arch_wrap_python() conv_to_str, conv_from_str>::def_wrap(ctx_cls, "getConflictingBelCell"); fn_wrapper_0a>::def_wrap(ctx_cls, "getBels"); - fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "getBelsAtSameTile"); fn_wrapper_2a, conv_from_str, conv_from_str>::def_wrap(ctx_cls, "getBelPinWire"); From a82a8840d2c3995a1b746020a54315acd7fba7b8 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 24 Jul 2018 16:28:06 +0200 Subject: [PATCH 27/33] Add missing implementations of generic Arch methods Signed-off-by: Clifford Wolf --- generic/arch.cc | 33 +++++++++++++++++++++++++++++---- generic/arch.h | 2 +- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/generic/arch.cc b/generic/arch.cc index af245a4b..ac79ebea 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -77,18 +77,18 @@ void Arch::addBel(IdString name, IdString type, Loc loc, bool gb) bel_ids.push_back(name); bel_by_loc[loc] = name; - if (bels_by_tile.size() <= loc.x) + if (int(bels_by_tile.size()) <= loc.x) bels_by_tile.resize(loc.x + 1); - if (bels_by_tile[loc.x].size() <= loc.y) + if (int(bels_by_tile[loc.x].size()) <= loc.y) bels_by_tile[loc.x].resize(loc.y + 1); bels_by_tile[loc.x][loc.y].push_back(name); - if (tileDimZ.size() <= loc.x) + if (int(tileDimZ.size()) <= loc.x) tileDimZ.resize(loc.x + 1); - if (tileDimZ[loc.x].size() <= loc.y) + if (int(tileDimZ[loc.x].size()) <= loc.y) tileDimZ[loc.x].resize(loc.y + 1); gridDimX = std::max(gridDimX, loc.x + 1); @@ -193,6 +193,30 @@ BelId Arch::getBelByName(IdString name) const IdString Arch::getBelName(BelId bel) const { return bel; } +Loc Arch::getBelLocation(BelId bel) const +{ + auto &info = bels.at(bel); + return Loc(info.x, info.y, info.z); +} + +BelId Arch::getBelByLocation(Loc loc) const +{ + auto it = bel_by_loc.find(loc); + if (it != bel_by_loc.end()) + return it->second; + return BelId(); +} + +const std::vector &Arch::getBelsByTile(int x, int y) const +{ + return bels_by_tile.at(x).at(y); +} + +bool Arch::getBelGlobalBuf(BelId bel) const +{ + return bels.at(bel).gb; +} + uint32_t Arch::getBelChecksum(BelId bel) const { // FIXME @@ -234,6 +258,7 @@ std::vector Arch::getBelPins(BelId bel) const std::vector ret; for (auto &it : bels.at(bel).pins) ret.push_back(it.first); + return ret; } // --------------------------------------------------------------- diff --git a/generic/arch.h b/generic/arch.h index 3025bda0..97ed1ac2 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -141,7 +141,7 @@ struct Arch : BaseCtx IdString getBelName(BelId bel) const; Loc getBelLocation(BelId bel) const; BelId getBelByLocation(Loc loc) const; - std::vector getBelsByTile(int x, int y) const; + const std::vector &getBelsByTile(int x, int y) const; bool getBelGlobalBuf(BelId bel) const; uint32_t getBelChecksum(BelId bel) const; void bindBel(BelId bel, IdString cell, PlaceStrength strength); From 3931c84fedc568e24c7e7e98132ec5757cdb29ab Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 24 Jul 2018 16:38:35 +0200 Subject: [PATCH 28/33] ecp5: Architecture testing and fixing Signed-off-by: David Shah --- ecp5/arch.cc | 8 +++++++- ecp5/main.cc | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index ea8abc0e..5507d1e3 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -202,7 +202,10 @@ BelRange Arch::getBelsByTile(int x, int y) const br.e.cursor_index = chip_info->locations[chip_info->location_type[br.b.cursor_tile]].num_bels - 1; br.b.chip = chip_info; br.e.chip = chip_info; - ++br.e; + if (br.e.cursor_index == -1) + ++br.e.cursor_index; + else + ++br.e; return br; } @@ -278,6 +281,7 @@ PipId Arch::getPipByName(IdString name) const Location loc; std::string basename; std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this)); + ret.location = loc; const LocationTypePOD *loci = locInfo(ret); for (int i = 0; i < loci->num_pips; i++) { PipId curr; @@ -285,6 +289,8 @@ PipId Arch::getPipByName(IdString name) const curr.index = i; pip_by_name[getPipName(curr)] = curr; } + if (pip_by_name.find(name) == pip_by_name.end()) + NPNR_ASSERT_FALSE_STR("no pip named " + name.str(this)); return pip_by_name[name]; } diff --git a/ecp5/main.cc b/ecp5/main.cc index 5a4a900a..90096855 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -63,6 +63,7 @@ int main(int argc, char *argv[]) #ifndef NO_GUI options.add_options()("gui", "start gui"); #endif + options.add_options()("test", "check architecture database integrity"); options.add_options()("25k", "set device type to LFE5U-25F"); options.add_options()("45k", "set device type to LFE5U-45F"); @@ -148,6 +149,9 @@ int main(int argc, char *argv[]) if (vm.count("no-tmdriv")) ctx->timing_driven = false; + if (vm.count("test")) + ctx->archcheck(); + #ifndef NO_GUI if (vm.count("gui")) { Application a(argc, argv); From 2039112a472fade4b161502dc683461640386304 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Tue, 24 Jul 2018 15:59:18 +0100 Subject: [PATCH 29/33] ice40: after review --- ice40/pack.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/ice40/pack.cc b/ice40/pack.cc index b1e7380e..b4f711f3 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -693,7 +693,6 @@ static void pack_special(Context *ctx) BelId pll_bel; bool constrained = false; if (packed->attrs.find(ctx->id("BEL")) == packed->attrs.end()) { - // FIXME replace by getBelsByType when implemented for (auto bel : ctx->getBels()) { if (ctx->getBelType(bel) != TYPE_ICESTORM_PLL) continue; From c9c3d970c918f9c91d9886467d7957cbc49bd6b1 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 24 Jul 2018 20:21:31 +0200 Subject: [PATCH 30/33] Fixed pybiding so generic can work and ecp5 expose all needed --- ecp5/arch_pybindings.cc | 125 ++++++++++++++++++++++++++++++++++++- generic/arch_pybindings.cc | 12 +++- 2 files changed, 133 insertions(+), 4 deletions(-) diff --git a/ecp5/arch_pybindings.cc b/ecp5/arch_pybindings.cc index 8310c3a1..c261c3ec 100644 --- a/ecp5/arch_pybindings.cc +++ b/ecp5/arch_pybindings.cc @@ -2,7 +2,7 @@ * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Clifford Wolf - * Copyright (C) 2018 David Shah + * Copyright (C) 2018 David Shah * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,13 +20,132 @@ #ifndef NO_PYTHON +#include "arch_pybindings.h" #include "nextpnr.h" #include "pybindings.h" NEXTPNR_NAMESPACE_BEGIN -void arch_wrap_python() {} +void arch_wrap_python() +{ + using namespace PythonConversion; + class_("ArchArgs").def_readwrite("type", &ArchArgs::type); + + class_("BelId").def_readwrite("index", &BelId::index); + + class_("WireId").def_readwrite("index", &WireId::index); + + class_("PipId").def_readwrite("index", &PipId::index); + + class_("BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin); + + enum_("PortPin") +#define X(t) .value("PIN_" #t, PIN_##t) + +#include "portpins.inc" + ; +#undef X + + auto arch_cls = class_, boost::noncopyable>("Arch", init()); + auto ctx_cls = class_, boost::noncopyable>("Context", no_init) + .def("checksum", &Context::checksum) + .def("pack", &Context::pack) + .def("place", &Context::place) + .def("route", &Context::route); + + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBelType"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "checkBelAvail"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBelChecksum"); + fn_wrapper_3a_v, + conv_from_str, pass_through>::def_wrap(ctx_cls, "bindBel"); + fn_wrapper_1a_v>::def_wrap( + ctx_cls, "unbindBel"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBoundBelCell"); + fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingBelCell"); + fn_wrapper_0a>::def_wrap(ctx_cls, + "getBels"); + + fn_wrapper_2a, + conv_from_str, conv_from_str>::def_wrap(ctx_cls, "getBelPinWire"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getWireBelPins"); + + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getWireChecksum"); + fn_wrapper_3a_v, + conv_from_str, pass_through>::def_wrap(ctx_cls, "bindWire"); + fn_wrapper_1a_v>::def_wrap( + ctx_cls, "unbindWire"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "checkWireAvail"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBoundWireNet"); + fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingWireNet"); + + fn_wrapper_0a>::def_wrap( + ctx_cls, "getWires"); + + fn_wrapper_0a>::def_wrap( + ctx_cls, "getPips"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipChecksum"); + fn_wrapper_3a_v, + conv_from_str, pass_through>::def_wrap(ctx_cls, "bindPip"); + fn_wrapper_1a_v>::def_wrap( + ctx_cls, "unbindPip"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "checkPipAvail"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBoundPipNet"); + fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingPipNet"); + + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipsDownhill"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipsUphill"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getWireAliases"); + + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipSrcWire"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipDstWire"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipDelay"); + + fn_wrapper_1a, + pass_through>::def_wrap(ctx_cls, "getPackagePinBel"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBelPackagePin"); + + fn_wrapper_0a>::def_wrap( + ctx_cls, "getChipName"); + fn_wrapper_0a>::def_wrap(ctx_cls, + "archId"); + + typedef std::unordered_map> CellMap; + typedef std::unordered_map> NetMap; + + readonly_wrapper>::def_wrap(ctx_cls, + "cells"); + readonly_wrapper>::def_wrap(ctx_cls, + "nets"); + WRAP_RANGE(Bel, conv_to_str); + WRAP_RANGE(Wire, conv_to_str); + WRAP_RANGE(AllPip, conv_to_str); + WRAP_RANGE(Pip, conv_to_str); + + WRAP_MAP_UPTR(CellMap, "IdCellMap"); + WRAP_MAP_UPTR(NetMap, "IdNetMap"); +} NEXTPNR_NAMESPACE_END -#endif +#endif // NO_PYTHON diff --git a/generic/arch_pybindings.cc b/generic/arch_pybindings.cc index a99723f2..186b2c13 100644 --- a/generic/arch_pybindings.cc +++ b/generic/arch_pybindings.cc @@ -20,12 +20,22 @@ #ifndef NO_PYTHON +#include "arch_pybindings.h" #include "nextpnr.h" #include "pybindings.h" NEXTPNR_NAMESPACE_BEGIN -void arch_wrap_python() { class_("ArchArgs"); } +void arch_wrap_python() +{ + using namespace PythonConversion; + auto arch_cls = class_, boost::noncopyable>("Arch", init()); + auto ctx_cls = class_, boost::noncopyable>("Context", no_init) + .def("checksum", &Context::checksum) + .def("pack", &Context::pack) + .def("place", &Context::place) + .def("route", &Context::route); +} NEXTPNR_NAMESPACE_END From ede3cc1459c3d0fd9501358ae91ca74bb7ee0047 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 24 Jul 2018 20:30:18 +0200 Subject: [PATCH 31/33] Disable pips for now on ECP5 just to be able to work on other parts --- gui/designwidget.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gui/designwidget.cc b/gui/designwidget.cc index e63ee937..7e8e2840 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -312,6 +312,7 @@ void DesignWidget::newContext(Context *ctx) QMap pip_items; pip_root->setText(0, "Pips"); treeWidget->insertTopLevelItem(0, pip_root); +#ifndef ARCH_ECP5 if (ctx) { for (auto pip : ctx->getPips()) { auto id = ctx->getPipName(pip); @@ -338,6 +339,7 @@ void DesignWidget::newContext(Context *ctx) for (auto pip : nameToItem[2].toStdMap()) { pip_root->addChild(pip.second); } +#endif nets_root = new QTreeWidgetItem(treeWidget); nets_root->setText(0, "Nets"); From 6a7f3cd336ebb35389d0c4ee4f8b457d2a2e026a Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 24 Jul 2018 21:25:33 +0200 Subject: [PATCH 32/33] ecp5: Working on LVDS inputs for Versa support Signed-off-by: David Shah --- ecp5/bitstream.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index c2218762..9a925d63 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -185,6 +185,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } // Find bank voltages std::unordered_map bankVcc; + std::unordered_map bankLvds; + for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (ci->bel != BelId() && ci->type == ctx->id("TRELLIS_IO")) { @@ -201,6 +203,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } else { bankVcc[bank] = vcc; } + if (iotype == "LVDS") + bankLvds[bank] = true; } } @@ -211,6 +215,10 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex int bank = std::stoi(type.substr(7)); if (bankVcc.find(bank) != bankVcc.end()) cc.tiles[tile.first].add_enum("BANK.VCCIO", iovoltage_to_str(bankVcc[bank])); + if (bankLvds[bank]) { + cc.tiles[tile.first].add_enum("BANK.DIFF_REF", "ON"); + cc.tiles[tile.first].add_enum("BANK.LVDSO", "ON"); + } } } From 32c7247785f48b2307e559a0af50d9387bda8b49 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 24 Jul 2018 22:26:26 +0200 Subject: [PATCH 33/33] ecp5: Bitsream gen tuning Signed-off-by: David Shah --- ecp5/bitstream.cc | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 9a925d63..f1feba24 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -191,18 +191,23 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex CellInfo *ci = cell.second.get(); if (ci->bel != BelId() && ci->type == ctx->id("TRELLIS_IO")) { int bank = ctx->getPioBelBank(ci->bel); + std::string dir = str_or_default(ci->params, ctx->id("DIR"), "INPUT"); std::string iotype = str_or_default(ci->attrs, ctx->id("IO_TYPE"), "LVCMOS33"); - IOVoltage vcc = get_vccio(ioType_from_str(iotype)); - if (bankVcc.find(bank) != bankVcc.end()) { - // TODO: strong and weak constraints - if (bankVcc[bank] != vcc) { - log_error("Error processing '%s': incompatible IO voltages %s and %s on bank %d.", - cell.first.c_str(ctx), iovoltage_to_str(bankVcc[bank]).c_str(), - iovoltage_to_str(vcc).c_str(), bank); + + if (dir != "INPUT") { + IOVoltage vcc = get_vccio(ioType_from_str(iotype)); + if (bankVcc.find(bank) != bankVcc.end()) { + // TODO: strong and weak constraints + if (bankVcc[bank] != vcc) { + log_error("Error processing '%s': incompatible IO voltages %s and %s on bank %d.", + cell.first.c_str(ctx), iovoltage_to_str(bankVcc[bank]).c_str(), + iovoltage_to_str(vcc).c_str(), bank); + } + } else { + bankVcc[bank] = vcc; } - } else { - bankVcc[bank] = vcc; } + if (iotype == "LVDS") bankLvds[bank] = true; } @@ -275,8 +280,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex other = "PIOD"; else log_error("cannot place differential IO at location %s\n", pio.c_str()); - cc.tiles[pio_tile].add_enum(other + ".BASE_TYPE", "_NONE_"); - cc.tiles[pic_tile].add_enum(other + ".BASE_TYPE", "_NONE_"); + //cc.tiles[pio_tile].add_enum(other + ".BASE_TYPE", "_NONE_"); + //cc.tiles[pic_tile].add_enum(other + ".BASE_TYPE", "_NONE_"); cc.tiles[pio_tile].add_enum(other + ".PULLMODE", "NONE"); cc.tiles[pio_tile].add_enum(pio + ".PULLMODE", "NONE"); }