ice40: Add support for PLL global outputs via PADIN
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
This commit is contained in:
parent
bc9f2da470
commit
d8e4c21d96
@ -356,8 +356,45 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_set<Loc> sb_io_used_by_pll;
|
// Scan for PLL and collects the affected SB_IOs
|
||||||
std::unordered_set<Loc> sb_io_used_by_io;
|
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
|
// Set logic cell config
|
||||||
for (auto &cell : ctx->cells) {
|
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")) {
|
} else if (cell.second->type == ctx->id("SB_IO")) {
|
||||||
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
||||||
int x = beli.x, y = beli.y, z = beli.z;
|
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];
|
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
|
||||||
unsigned pin_type = get_param_or_def(cell.second.get(), ctx->id("PIN_TYPE"));
|
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 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 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 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;
|
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);
|
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) {
|
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.IE_" + std::to_string(iez), !input_en);
|
||||||
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
|
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_extra_cell(config, ctx, cell.second.get(), pll_params, false, std::string("PLL."));
|
||||||
|
|
||||||
// Configure the SB_IOs that the clock outputs are going through.
|
// Configure the SB_IOs that the clock outputs are going through.
|
||||||
for (auto &port : cell.second->ports) {
|
for (auto &io_bel_loc : sb_io_used_by_pll_out) {
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Write config.
|
// Write config.
|
||||||
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
|
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
|
||||||
// Enable input buffer and disable pull-up resistor in block
|
|
||||||
// (this is used by the PLL).
|
// PINTYPE[1:0] == "01" passes the PLL through to the fabric.
|
||||||
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
|
set_config(ti, config.at(io_bel_loc.y).at(io_bel_loc.x),
|
||||||
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
|
"IOB_" + std::to_string(io_bel_loc.z) + ".PINTYPE_1", false);
|
||||||
// PINTYPE[0] passes the PLL through to the fabric.
|
|
||||||
set_config(ti, config.at(io_bel_loc.y).at(io_bel_loc.x),
|
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);
|
"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 TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
|
||||||
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
||||||
int x = beli.x, y = beli.y, z = beli.z;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -790,32 +790,6 @@ static void pack_special(Context *ctx)
|
|||||||
|
|
||||||
NetInfo *pad_packagepin_net = nullptr;
|
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) {
|
for (auto port : ci->ports) {
|
||||||
PortInfo &pi = port.second;
|
PortInfo &pi = port.second;
|
||||||
std::string newname = pi.name.str(ctx);
|
std::string newname = pi.name.str(ctx);
|
||||||
@ -823,24 +797,15 @@ static void pack_special(Context *ctx)
|
|||||||
if (bpos != std::string::npos) {
|
if (bpos != std::string::npos) {
|
||||||
newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
|
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";
|
newname = "PLLOUT_A";
|
||||||
if (pi.name == ctx->id("PLLOUTCOREB"))
|
if (pi.name == ctx->id("PLLOUTCOREB"))
|
||||||
newname = "PLLOUT_B";
|
newname = "PLLOUT_B";
|
||||||
if (pi.name == ctx->id("PLLOUTCORE"))
|
if (pi.name == ctx->id("PLLOUTGLOBALA") || pi.name == ctx->id("PLLOUTGLOBALA"))
|
||||||
newname = "PLLOUT_A";
|
newname = "PLLOUT_A_GLOBAL";
|
||||||
if (pi.name == ctx->id("PLLOUTGLOBALA"))
|
|
||||||
newname = "PLLOUT_A";
|
|
||||||
if (pi.name == ctx->id("PLLOUTGLOBALB"))
|
if (pi.name == ctx->id("PLLOUTGLOBALB"))
|
||||||
newname = "PLLOUT_B";
|
newname = "PLLOUT_B_GLOBAL";
|
||||||
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());
|
|
||||||
|
|
||||||
if (pi.name == ctx->id("PACKAGEPIN")) {
|
if (pi.name == ctx->id("PACKAGEPIN")) {
|
||||||
if (!is_pad) {
|
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));
|
new_cells.push_back(std::move(packed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user