ice40: move PLL->IO from pseudo pip to second uphill bel
This commit is contained in:
parent
65ceb20784
commit
eaae1d299c
@ -107,27 +107,29 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
|||||||
return logicCellsCompatible(bel_cells);
|
return logicCellsCompatible(bel_cells);
|
||||||
} else if (cell->type == id_sb_io) {
|
} else if (cell->type == id_sb_io) {
|
||||||
// Do not allow placement of input SB_IOs on blocks where there a PLL is outputting to.
|
// 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();
|
// Find shared PLL by looking for driving bel siblings from D_IN_0
|
||||||
for (auto type : {id("PLLOUT_A"), id("PLLOUT_B")}) {
|
// that are a PLL clock output.
|
||||||
auto it = bel_cell->ports.find(type);
|
auto wire = getBelPinWire(bel, PIN_D_IN_0);
|
||||||
if (it == bel_cell->ports.end())
|
PortPin pll_bel_pin;
|
||||||
continue;
|
BelId pll_bel;
|
||||||
if (it->second.net == nullptr)
|
for (auto pin : getWireBelPins(wire)) {
|
||||||
continue;
|
if (pin.pin == PIN_PLLOUT_A || pin.pin == PIN_PLLOUT_B) {
|
||||||
auto wire = getBelPinWire(iter_bel, portPinFromId(type));
|
pll_bel = pin.bel;
|
||||||
for (auto pip : getPipsDownhill(wire)) {
|
pll_bel_pin = pin.pin;
|
||||||
auto driven_wire = getPipDstWire(pip);
|
break;
|
||||||
auto io_bel = chip_info->wire_data[driven_wire.index].bels_uphill[0].bel_index;
|
|
||||||
if (io_bel == bel.index) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getBelPackagePin(bel) != "";
|
return getBelPackagePin(bel) != "";
|
||||||
|
@ -452,26 +452,27 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
if (port.second.net == nullptr)
|
if (port.second.net == nullptr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Get IO Bel that this PLL port goes through.
|
// Get IO Bel that this PLL port goes through by finding sibling
|
||||||
// We navigate one pip downhill to the next wire (there should
|
// Bel driving the same wire via PIN_D_IN_0.
|
||||||
// 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 wire = ctx->getBelPinWire(cell.second->bel, ctx->portPinFromId(port.second.name));
|
||||||
auto pips = ctx->getPipsDownhill(wire).begin();
|
BelId io_bel;
|
||||||
auto driven_wire = ctx->getPipDstWire(*pips);
|
for (auto pin : ctx->getWireBelPins(wire)) {
|
||||||
auto io_bel = ctx->chip_info->wire_data[driven_wire.index].bels_uphill[0].bel_index;
|
if (pin.pin == PIN_D_IN_0) {
|
||||||
auto io_beli = ctx->chip_info->bel_data[io_bel];
|
io_bel = pin.bel;
|
||||||
NPNR_ASSERT(io_beli.type == TYPE_SB_IO);
|
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.
|
// 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_bel_loc)) {
|
||||||
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));
|
sb_io_used_by_pll.insert(io_bel_loc);
|
||||||
|
|
||||||
// Get IE/REN config location (cf. http://www.clifford.at/icestorm/io_tile.html)
|
// 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;
|
int iex, iey, iez;
|
||||||
std::tie(iex, iey, iez) = ieren;
|
std::tie(iex, iey, iez) = ieren;
|
||||||
NPNR_ASSERT(iez != -1);
|
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.IE_" + std::to_string(iez), true);
|
||||||
set_ie_bit_logical(ctx, 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.REN_" + std::to_string(iez), false);
|
||||||
// PINTYPE[0] passes the PLL through to the fabric.
|
// 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",
|
set_config(ti, config.at(io_bel_loc.y).at(io_bel_loc.x),
|
||||||
true);
|
"IOB_" + std::to_string(io_bel_loc.z) + ".PINTYPE_0", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -597,40 +597,6 @@ def is_ec_output(ec_entry):
|
|||||||
def is_ec_pll_clock_output(ec, ec_entry):
|
def is_ec_pll_clock_output(ec, ec_entry):
|
||||||
return ec[0] == 'PLL' and ec_entry[0] in ('PLLOUT_A', 'PLLOUT_B')
|
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):
|
def add_bel_ec(ec):
|
||||||
ectype, x, y, z = ec
|
ectype, x, y, z = ec
|
||||||
bel = len(bel_name)
|
bel = len(bel_name)
|
||||||
@ -646,7 +612,9 @@ def add_bel_ec(ec):
|
|||||||
else:
|
else:
|
||||||
add_bel_input(bel, wire_names[entry[1]], entry[0])
|
add_bel_input(bel, wire_names[entry[1]], entry[0])
|
||||||
elif is_ec_pll_clock_output(ec, entry):
|
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:
|
else:
|
||||||
extra_cell_config[bel].append(entry)
|
extra_cell_config[bel].append(entry)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user