diff options
-rw-r--r-- | ice40/bitstream.cc | 94 | ||||
-rw-r--r-- | ice40/pack.cc | 63 |
2 files changed, 73 insertions, 84 deletions
diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index c32680ee..d2c2ac16 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -356,8 +356,45 @@ void write_asc(const Context *ctx, std::ostream &out) } } - std::unordered_set<Loc> sb_io_used_by_pll; - std::unordered_set<Loc> sb_io_used_by_io; + // Scan for PLL and collects the affected SB_IOs + std::unordered_set<Loc> sb_io_used_by_pll_out; + std::unordered_set<Loc> sb_io_used_by_pll_pad; + + for (auto &cell : ctx->cells) { + if (cell.second->type != ctx->id("ICESTORM_PLL")) + continue; + + // Collect all locations matching an PLL output port + // note: It doesn't matter if the port is connected or not, or if fabric/global + // is used. As long as it's a PLL type for which the port exists, the SB_IO + // is not available and must be configured for PLL mode + const std::vector<IdString> ports = {id_PLLOUT_A, id_PLLOUT_B}; + for (auto &port : ports) { + // If the output is not enabled in this mode, ignore it + if (port == id_PLLOUT_B && !is_sb_pll40_dual(ctx, cell.second.get())) + continue; + + // Get IO Bel that this PLL port goes through by finding sibling + // Bel driving the same wire via PIN_D_IN_0. + auto wire = ctx->getBelPinWire(cell.second->bel, port); + BelId io_bel; + for (auto pin : ctx->getWireBelPins(wire)) { + if (pin.pin == id_D_IN_0) { + io_bel = pin.bel; + break; + } + } + NPNR_ASSERT(io_bel.index != -1); + auto io_bel_loc = ctx->getBelLocation(io_bel); + + // Mark this SB_IO as being used by a PLL output path + sb_io_used_by_pll_out.insert(io_bel_loc); + + // If this is a PAD PLL, and this is the 'PLLOUT_A' port, then the same SB_IO is also PAD + if (port == id_PLLOUT_A && is_sb_pll40_pad(ctx, cell.second.get())) + sb_io_used_by_pll_pad.insert(io_bel_loc); + } + } // Set logic cell config for (auto &cell : ctx->cells) { @@ -445,14 +482,15 @@ void write_asc(const Context *ctx, std::ostream &out) } else if (cell.second->type == ctx->id("SB_IO")) { const BelInfoPOD &beli = ci.bel_data[bel.index]; int x = beli.x, y = beli.y, z = beli.z; - sb_io_used_by_io.insert(Loc(x, y, z)); const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO]; unsigned pin_type = get_param_or_def(cell.second.get(), ctx->id("PIN_TYPE")); bool neg_trigger = get_param_or_def(cell.second.get(), ctx->id("NEG_TRIGGER")); bool pullup = get_param_or_def(cell.second.get(), ctx->id("PULLUP")); bool lvds = get_param_str_or_def(cell.second.get(), ctx->id("IO_STANDARD")) == "SB_LVDS_INPUT"; + bool used_by_pll_out = sb_io_used_by_pll_out.count(Loc(x, y, z)) > 0; + bool used_by_pll_pad = sb_io_used_by_pll_pad.count(Loc(x, y, z)) > 0; - for (int i = 0; i < 6; i++) { + for (int i = used_by_pll_out ? 2 : 0; i < 6; i++) { bool val = (pin_type >> i) & 0x01; set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val); } @@ -475,6 +513,8 @@ void write_asc(const Context *ctx, std::ostream &out) } } + input_en = (input_en & !used_by_pll_out) | used_by_pll_pad; + if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) { set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), !input_en); set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup); @@ -613,47 +653,13 @@ void write_asc(const Context *ctx, std::ostream &out) configure_extra_cell(config, ctx, cell.second.get(), pll_params, false, std::string("PLL.")); // Configure the SB_IOs that the clock outputs are going through. - for (auto &port : cell.second->ports) { - // If this port is not a PLLOUT port, ignore it. - if (port.second.name != ctx->id("PLLOUT_A") && port.second.name != ctx->id("PLLOUT_B")) - continue; - - // If the output is not driving any net, ignore it. - if (port.second.net == nullptr) - continue; - - // Get IO Bel that this PLL port goes through by finding sibling - // Bel driving the same wire via PIN_D_IN_0. - auto wire = ctx->getBelPinWire(cell.second->bel, port.second.name); - BelId io_bel; - for (auto pin : ctx->getWireBelPins(wire)) { - if (pin.pin == id_D_IN_0) { - io_bel = pin.bel; - break; - } - } - NPNR_ASSERT(io_bel.index != -1); - auto io_bel_loc = ctx->getBelLocation(io_bel); - - // Check that this SB_IO is either unused or just used as an output. - if (sb_io_used_by_io.count(io_bel_loc)) { - log_error("SB_IO '%s' already in use, cannot route PLL through\n", ctx->getBelName(bel).c_str(ctx)); - } - sb_io_used_by_pll.insert(io_bel_loc); - - // Get IE/REN config location (cf. http://www.clifford.at/icestorm/io_tile.html) - auto ieren = get_ieren(bi, io_bel_loc.x, io_bel_loc.y, io_bel_loc.z); - int iex, iey, iez; - std::tie(iex, iey, iez) = ieren; - NPNR_ASSERT(iez != -1); - + for (auto &io_bel_loc : sb_io_used_by_pll_out) { // Write config. const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO]; - // Enable input buffer and disable pull-up resistor in block - // (this is used by the PLL). - set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true); - set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false); - // PINTYPE[0] passes the PLL through to the fabric. + + // PINTYPE[1:0] == "01" passes the PLL through to the fabric. + set_config(ti, config.at(io_bel_loc.y).at(io_bel_loc.x), + "IOB_" + std::to_string(io_bel_loc.z) + ".PINTYPE_1", false); set_config(ti, config.at(io_bel_loc.y).at(io_bel_loc.x), "IOB_" + std::to_string(io_bel_loc.z) + ".PINTYPE_0", true); } @@ -668,7 +674,7 @@ void write_asc(const Context *ctx, std::ostream &out) const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO]; const BelInfoPOD &beli = ci.bel_data[bel.index]; int x = beli.x, y = beli.y, z = beli.z; - if (sb_io_used_by_pll.count(Loc(x, y, z))) { + if (sb_io_used_by_pll_out.count(Loc(x, y, z))) { continue; } diff --git a/ice40/pack.cc b/ice40/pack.cc index cae6ab8c..ca67baab 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -790,32 +790,6 @@ static void pack_special(Context *ctx) NetInfo *pad_packagepin_net = nullptr; - int pllout_a_used = 0; - int pllout_b_used = 0; - for (auto port : ci->ports) { - PortInfo &pi = port.second; - if (pi.name == ctx->id("PLLOUTCOREA")) - pllout_a_used++; - if (pi.name == ctx->id("PLLOUTCOREB")) - pllout_b_used++; - if (pi.name == ctx->id("PLLOUTCORE")) - pllout_a_used++; - if (pi.name == ctx->id("PLLOUTGLOBALA")) - pllout_a_used++; - if (pi.name == ctx->id("PLLOUTGLOBALB")) - pllout_b_used++; - if (pi.name == ctx->id("PLLOUTGLOBAL")) - pllout_a_used++; - } - - if (pllout_a_used > 1) - log_error("PLL '%s' is using multiple ports mapping to PLLOUT_A output of the PLL\n", - ci->name.c_str(ctx)); - - if (pllout_b_used > 1) - log_error("PLL '%s' is using multiple ports mapping to PLLOUT_B output of the PLL\n", - ci->name.c_str(ctx)); - for (auto port : ci->ports) { PortInfo &pi = port.second; std::string newname = pi.name.str(ctx); @@ -823,24 +797,15 @@ static void pack_special(Context *ctx) if (bpos != std::string::npos) { newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2); } - if (pi.name == ctx->id("PLLOUTCOREA")) + + if (pi.name == ctx->id("PLLOUTCOREA") || pi.name == ctx->id("PLLOUTCORE")) newname = "PLLOUT_A"; if (pi.name == ctx->id("PLLOUTCOREB")) newname = "PLLOUT_B"; - if (pi.name == ctx->id("PLLOUTCORE")) - newname = "PLLOUT_A"; - if (pi.name == ctx->id("PLLOUTGLOBALA")) - newname = "PLLOUT_A"; + if (pi.name == ctx->id("PLLOUTGLOBALA") || pi.name == ctx->id("PLLOUTGLOBALA")) + newname = "PLLOUT_A_GLOBAL"; if (pi.name == ctx->id("PLLOUTGLOBALB")) - newname = "PLLOUT_B"; - if (pi.name == ctx->id("PLLOUTGLOBAL")) - newname = "PLLOUT_A"; - - if (pi.name == ctx->id("PLLOUTGLOBALA") || pi.name == ctx->id("PLLOUTGLOBALB") || - pi.name == ctx->id("PLLOUTGLOBAL")) - log_warning("PLL '%s' is using port %s but implementation does not actually " - "use the global clock output of the PLL\n", - ci->name.c_str(ctx), pi.name.str(ctx).c_str()); + newname = "PLLOUT_B_GLOBAL"; if (pi.name == ctx->id("PACKAGEPIN")) { if (!is_pad) { @@ -1011,6 +976,24 @@ static void pack_special(Context *ctx) } } + // Handle the global buffer connections + for (auto port : packed->ports) { + PortInfo &pi = port.second; + bool is_b_port; + + if (pi.name == ctx->id("PLLOUT_A_GLOBAL")) + is_b_port = false; + else if (pi.name == ctx->id("PLLOUT_B_GLOBAL")) + is_b_port = true; + else + continue; + + std::unique_ptr<CellInfo> gb = + create_padin_gbuf(ctx, packed.get(), pi.name, + "$gbuf_" + ci->name.str(ctx) + "_pllout_" + (is_b_port ? "b" : "a")); + new_cells.push_back(std::move(gb)); + } + new_cells.push_back(std::move(packed)); } } |