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)