aboutsummaryrefslogtreecommitdiffstats
path: root/ice40
diff options
context:
space:
mode:
Diffstat (limited to 'ice40')
-rw-r--r--ice40/arch.cc26
-rw-r--r--ice40/arch.h20
-rw-r--r--ice40/arch_place.cc43
-rw-r--r--ice40/archdefs.h5
-rw-r--r--ice40/bitstream.cc127
-rw-r--r--ice40/cells.cc22
-rw-r--r--ice40/cells.h19
-rw-r--r--ice40/chipdb.py107
-rw-r--r--ice40/constids.inc2
-rw-r--r--ice40/pack.cc218
10 files changed, 422 insertions, 167 deletions
diff --git a/ice40/arch.cc b/ice40/arch.cc
index 2a9e167b..02e5515b 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -284,6 +284,25 @@ std::vector<IdString> Arch::getBelPins(BelId bel) const
return ret;
}
+bool Arch::isBelLocked(BelId bel) const
+{
+ const BelConfigPOD *bel_config = nullptr;
+ for (int i = 0; i < chip_info->num_belcfgs; i++) {
+ if (chip_info->bel_config[i].bel_index == bel.index) {
+ bel_config = &chip_info->bel_config[i];
+ break;
+ }
+ }
+ NPNR_ASSERT(bel_config != nullptr);
+ for (int i = 0; i < bel_config->num_entries; i++) {
+ if (strcmp("LOCKED", bel_config->entries[i].cbit_name.get()))
+ continue;
+ if ("LOCKED_" + archArgs().package == bel_config->entries[i].entry_name.get())
+ return true;
+ }
+ return false;
+}
+
// -----------------------------------------------------------------------
WireId Arch::getWireByName(IdString name) const
@@ -927,6 +946,10 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
return TMG_COMB_INPUT;
} else if (cell->type == id_SB_WARMBOOT) {
return TMG_ENDPOINT;
+ } else if (cell->type == id_SB_RGBA_DRV) {
+ if (port == id_RGB0 || port == id_RGB1 || port == id_RGB2)
+ return TMG_IGNORE;
+ return TMG_ENDPOINT;
}
log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this));
}
@@ -1025,6 +1048,9 @@ void Arch::assignCellInfo(CellInfo *cell)
cell->lcInfo.inputCount++;
} else if (cell->type == id_SB_IO) {
cell->ioInfo.lvds = str_or_default(cell->params, id_IO_STANDARD, "SB_LVCMOS") == "SB_LVDS_INPUT";
+ cell->ioInfo.global = bool_or_default(cell->attrs, this->id("GLOBAL"));
+ } else if (cell->type == id_SB_GB) {
+ cell->gbInfo.forPadIn = bool_or_default(cell->attrs, this->id("FOR_PAD_IN"));
}
}
diff --git a/ice40/arch.h b/ice40/arch.h
index 836dc46e..e8c597c9 100644
--- a/ice40/arch.h
+++ b/ice40/arch.h
@@ -212,11 +212,26 @@ NPNR_PACKED_STRUCT(struct CellTimingPOD {
RelPtr<CellPathDelayPOD> path_delays;
});
+NPNR_PACKED_STRUCT(struct GlobalNetworkInfoPOD {
+ uint8_t gb_x;
+ uint8_t gb_y;
+
+ uint8_t pi_gb_x;
+ uint8_t pi_gb_y;
+ uint8_t pi_gb_pio;
+
+ uint8_t pi_eb_bank;
+ uint16_t pi_eb_x;
+ uint16_t pi_eb_y;
+
+ uint16_t pad;
+});
+
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
int32_t width, height;
int32_t num_bels, num_wires, num_pips;
int32_t num_switches, num_belcfgs, num_packages;
- int32_t num_timing_cells;
+ int32_t num_timing_cells, num_global_networks;
RelPtr<BelInfoPOD> bel_data;
RelPtr<WireInfoPOD> wire_data;
RelPtr<PipInfoPOD> pip_data;
@@ -225,6 +240,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD {
RelPtr<BelConfigPOD> bel_config;
RelPtr<PackageInfoPOD> packages_data;
RelPtr<CellTimingPOD> cell_timing;
+ RelPtr<GlobalNetworkInfoPOD> global_network_info;
RelPtr<RelPtr<char>> tile_wire_names;
});
@@ -510,6 +526,8 @@ struct Arch : BaseCtx
PortType getBelPinType(BelId bel, IdString pin) const;
std::vector<IdString> getBelPins(BelId bel) const;
+ bool isBelLocked(BelId bel) const;
+
// -------------------------------------------------
WireId getWireByName(IdString name) const;
diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc
index c97b9c26..41f9b640 100644
--- a/ice40/arch_place.cc
+++ b/ice40/arch_place.cc
@@ -114,31 +114,30 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
// Find shared PLL by looking for driving bel siblings from D_IN_0
// that are a PLL clock output.
auto wire = getBelPinWire(bel, id_D_IN_0);
- IdString pll_bel_pin;
- BelId pll_bel;
for (auto pin : getWireBelPins(wire)) {
if (pin.pin == id_PLLOUT_A || pin.pin == id_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) {
- auto pll_cell = getBoundBelCell(pll_bel);
- // Is a PLL placed in this PLL bel?
- if (pll_cell != nullptr) {
- // Is the shared port driving a net?
- auto pi = pll_cell->ports[pll_bel_pin];
- if (pi.net != nullptr) {
- // Are we perhaps a PAD INPUT Bel that can be placed here?
- if (pll_cell->attrs[id("BEL_PAD_INPUT")] == getBelName(bel).str(this)) {
- return true;
- }
- return false;
- }
+ // Is there a PLL there ?
+ auto pll_cell = getBoundBelCell(pin.bel);
+ if (pll_cell == nullptr)
+ break;
+
+ // Is that port actually used ?
+ if ((pin.pin == id_PLLOUT_B) && !is_sb_pll40_dual(this, pll_cell))
+ break;
+
+ // Is that SB_IO used at an input ?
+ if ((cell->ports[id_D_IN_0].net == nullptr) && (cell->ports[id_D_IN_1].net == nullptr))
+ break;
+
+ // Are we perhaps a PAD INPUT Bel that can be placed here?
+ if (pll_cell->attrs[id("BEL_PAD_INPUT")] == getBelName(bel).str(this))
+ return true;
+
+ // Conflict
+ return false;
}
}
+
Loc ioLoc = getBelLocation(bel);
Loc compLoc = ioLoc;
compLoc.z = 1 - compLoc.z;
@@ -162,6 +161,8 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
return getBelPackagePin(bel) != "";
} else if (cell->type == id_SB_GB) {
+ if (cell->gbInfo.forPadIn)
+ return true;
NPNR_ASSERT(cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net != nullptr);
const NetInfo *net = cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net;
IdString glb_net = getWireName(getBelPinWire(bel, id_GLOBAL_BUFFER_OUTPUT));
diff --git a/ice40/archdefs.h b/ice40/archdefs.h
index b9614c07..2bffe667 100644
--- a/ice40/archdefs.h
+++ b/ice40/archdefs.h
@@ -150,8 +150,13 @@ struct ArchCellInfo
struct
{
bool lvds;
+ bool global;
// TODO: clk packing checks...
} ioInfo;
+ struct
+ {
+ bool forPadIn;
+ } gbInfo;
};
};
diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc
index 4e54df1d..ecb26753 100644
--- a/ice40/bitstream.cc
+++ b/ice40/bitstream.cc
@@ -269,6 +269,9 @@ void write_asc(const Context *ctx, std::ostream &out)
config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
}
}
+
+ std::vector<std::tuple<int, int, int>> extra_bits;
+
out << ".comment from next-pnr" << std::endl;
switch (ctx->args.type) {
@@ -353,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) {
@@ -442,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);
}
@@ -472,6 +513,9 @@ void write_asc(const Context *ctx, std::ostream &out)
}
}
+ input_en = (input_en & !used_by_pll_out) | used_by_pll_pad;
+ input_en |= cell.second->ioInfo.global;
+
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);
@@ -513,7 +557,16 @@ void write_asc(const Context *ctx, std::ostream &out)
}
}
} else if (cell.second->type == ctx->id("SB_GB")) {
- // no cell config bits
+ if (cell.second->gbInfo.forPadIn) {
+ Loc gb_loc = ctx->getBelLocation(bel);
+ for (int i = 0; i < ci.num_global_networks; i++) {
+ if ((gb_loc.x == ci.global_network_info[i].gb_x) && (gb_loc.y == ci.global_network_info[i].gb_y)) {
+ extra_bits.push_back(std::make_tuple(ci.global_network_info[i].pi_eb_bank,
+ ci.global_network_info[i].pi_eb_x,
+ ci.global_network_info[i].pi_eb_y));
+ }
+ }
+ }
} else if (cell.second->type == ctx->id("ICESTORM_RAM")) {
const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y;
@@ -533,6 +586,11 @@ void write_asc(const Context *ctx, std::ostream &out)
set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_1", write_mode & 0x2);
set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_2", read_mode & 0x1);
set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_3", read_mode & 0x2);
+ } else if (cell.second->type == ctx->id("SB_RGBA_DRV")) {
+ const std::vector<std::pair<std::string, int>> rgba_params = {
+ {"CURRENT_MODE", 1}, {"RGB0_CURRENT", 6}, {"RGB1_CURRENT", 6}, {"RGB2_CURRENT", 6}};
+ configure_extra_cell(config, ctx, cell.second.get(), rgba_params, true, std::string("IpConfig."));
+ set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "RGBA_DRV_EN", true, "IpConfig.");
} else if (cell.second->type == ctx->id("SB_WARMBOOT") || cell.second->type == ctx->id("ICESTORM_LFOSC")) {
// No config needed
} else if (cell.second->type == ctx->id("ICESTORM_SPRAM")) {
@@ -601,47 +659,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);
}
@@ -656,7 +680,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;
}
@@ -671,8 +695,13 @@ void write_asc(const Context *ctx, std::ostream &out)
if (lvds0cell != nullptr && lvds0cell->ioInfo.lvds)
continue;
}
- 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);
+ 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);
+ } else {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), false);
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
+ }
}
} else if (ctx->bel_to_cell[bel.index] == nullptr && ctx->getBelType(bel) == id_ICESTORM_RAM) {
const BelInfoPOD &beli = ci.bel_data[bel.index];
@@ -790,6 +819,10 @@ void write_asc(const Context *ctx, std::ostream &out)
}
}
+ // Write extra-bits
+ for (auto eb : extra_bits)
+ out << ".extra_bit " << std::get<0>(eb) << " " << std::get<1>(eb) << " " << std::get<2>(eb) << std::endl;
+
// Write symbols
// const bool write_symbols = 1;
for (auto wire : ctx->getWires()) {
diff --git a/ice40/cells.cc b/ice40/cells.cc
index fbb77b0c..dbb75c2c 100644
--- a/ice40/cells.cc
+++ b/ice40/cells.cc
@@ -219,7 +219,7 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
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("FEEDBACK_PATH")] = "1";
new_cell->params[ctx->id("FILTER_RANGE")] = "0";
new_cell->params[ctx->id("PLLOUT_SELECT_A")] = "0";
@@ -244,8 +244,22 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
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);
- add_port(ctx, new_cell.get(), "PLLOUTGLOBALA", PORT_OUT);
- add_port(ctx, new_cell.get(), "PLLOUTGLOBALB", PORT_OUT);
+ add_port(ctx, new_cell.get(), "PLLOUT_A_GLOBAL", PORT_OUT);
+ add_port(ctx, new_cell.get(), "PLLOUT_B_GLOBAL", PORT_OUT);
+ } else if (type == ctx->id("SB_RGBA_DRV")) {
+ new_cell->params[ctx->id("CURRENT_MODE")] = "0b0";
+ new_cell->params[ctx->id("RGB0_CURRENT")] = "0b000000";
+ new_cell->params[ctx->id("RGB1_CURRENT")] = "0b000000";
+ new_cell->params[ctx->id("RGB2_CURRENT")] = "0b000000";
+
+ add_port(ctx, new_cell.get(), "CURREN", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGBLEDEN", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGB0PWM", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGB1PWM", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGB2PWM", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGB0", PORT_OUT);
+ add_port(ctx, new_cell.get(), "RGB1", PORT_OUT);
+ add_port(ctx, new_cell.get(), "RGB2", PORT_OUT);
} else {
log_error("unable to create iCE40 cell of type %s", type.c_str(ctx));
}
@@ -362,7 +376,7 @@ uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell)
if (cell->type == ctx->id("SB_PLL40_2_PAD"))
return 4;
if (cell->type == ctx->id("SB_PLL40_2F_PAD"))
- return 5;
+ return 6;
if (cell->type == ctx->id("SB_PLL40_CORE"))
return 3;
if (cell->type == ctx->id("SB_PLL40_2F_CORE"))
diff --git a/ice40/cells.h b/ice40/cells.h
index 054388ac..1fbd9073 100644
--- a/ice40/cells.h
+++ b/ice40/cells.h
@@ -53,6 +53,9 @@ inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type
// Return true if a cell is a SB_IO
inline bool is_sb_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_IO"); }
+// Return true if a cell is a SB_GB_IO
+inline bool is_sb_gb_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_GB_IO"); }
+
// Return true if a cell is a global buffer
inline bool is_gbuf(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_GB"); }
@@ -71,6 +74,8 @@ 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_rgba_drv(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_RGBA_DRV"); }
+
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") ||
@@ -81,7 +86,19 @@ inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell)
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");
+ cell->type == ctx->id("SB_PLL40_2F_PAD") ||
+ (cell->type == ctx->id("ICESTORM_PLL") &&
+ (cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_PAD" || cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2_PAD" ||
+ cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2F_PAD"));
+}
+
+inline bool is_sb_pll40_dual(const BaseCtx *ctx, const CellInfo *cell)
+{
+ return cell->type == ctx->id("SB_PLL40_2_PAD") || cell->type == ctx->id("SB_PLL40_2F_PAD") ||
+ cell->type == ctx->id("SB_PLL40_2F_CORE") ||
+ (cell->type == ctx->id("ICESTORM_PLL") && (cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2_PAD" ||
+ cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2F_PAD" ||
+ cell->attrs.at(ctx->id("TYPE")) == "SB_PLL40_2F_CORE"));
}
uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell);
diff --git a/ice40/chipdb.py b/ice40/chipdb.py
index 5b2f3e57..96231b26 100644
--- a/ice40/chipdb.py
+++ b/ice40/chipdb.py
@@ -36,6 +36,7 @@ ierens = list()
extra_cells = dict()
extra_cell_config = dict()
packages = list()
+glbinfo = dict([(i, {}) for i in range(8)])
wire_belports = dict()
@@ -640,6 +641,18 @@ with open(args.filename, "r") as f:
extra_cells[mode[1]] = []
continue
+ if line[0] == ".gbufin":
+ mode = ("gbufin",)
+ continue
+
+ if line[0] == ".gbufpin":
+ mode = ("gbufpin",)
+ continue
+
+ if line[0] == ".extra_bits":
+ mode = ("extra_bits",)
+ continue
+
if (line[0][0] == ".") or (mode is None):
mode = None
continue
@@ -692,11 +705,33 @@ with open(args.filename, "r") as f:
if mode[0] == "extra_cell":
if line[0] == "LOCKED":
- extra_cells[mode[1]].append((("LOCKED_" + line[1]), (0, 0, "LOCKED")))
+ for pkg in line[1:]:
+ extra_cells[mode[1]].append((("LOCKED_" + pkg), (0, 0, "LOCKED")))
else:
extra_cells[mode[1]].append((line[0], (int(line[1]), int(line[2]), line[3])))
continue
+ if mode[0] == "gbufin":
+ idx = int(line[2])
+ glbinfo[idx]['gb_x'] = int(line[0])
+ glbinfo[idx]['gb_y'] = int(line[1])
+ continue
+
+ if mode[0] == "gbufpin":
+ idx = int(line[3])
+ glbinfo[idx]['pi_gb_x'] = int(line[0])
+ glbinfo[idx]['pi_gb_y'] = int(line[1])
+ glbinfo[idx]['pi_gb_pio'] = int(line[2])
+ continue
+
+ if mode[0] == "extra_bits":
+ if line[0].startswith('padin_glb_netwk.'):
+ idx = int(line[0].split('.')[1])
+ glbinfo[idx]['pi_eb_bank'] = int(line[1])
+ glbinfo[idx]['pi_eb_x'] = int(line[2])
+ glbinfo[idx]['pi_eb_y'] = int(line[3])
+ continue
+
def add_wire(x, y, name):
global num_wires
wire_idx = num_wires
@@ -828,6 +863,10 @@ def add_bel_io(x, y, z):
add_bel_input(bel, wire_dout_1, "D_OUT_1")
add_bel_input(bel, wire_out_en, "OUTPUT_ENABLE")
+ for gidx, ginfo in glbinfo.items():
+ if (ginfo['pi_gb_x'], ginfo['pi_gb_y'], ginfo['pi_gb_pio']) == (x,y,z):
+ add_bel_output(bel, wire_names[(x, y, "glb_netwk_%d" % gidx)], "GLOBAL_BUFFER_OUTPUT")
+
def add_bel_ram(x, y):
bel = len(bel_name)
bel_name.append("X%d/Y%d/ram" % (x, y))
@@ -885,6 +924,18 @@ 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, entry):
+ # Fabric output
+ io_x, io_y, io_z = entry[1]
+ io_zs = 'io_{}/D_IN_0'.format(io_z)
+ io_z = int(io_z)
+ add_bel_output(bel, wire_names[(io_x, io_y, io_zs)], entry[0])
+
+ # Global output
+ for gidx, ginfo in glbinfo.items():
+ if (ginfo['pi_gb_x'], ginfo['pi_gb_y'], ginfo['pi_gb_pio']) == (io_x, io_y, io_z):
+ add_bel_output(bel, wire_names[(io_x, io_y, "glb_netwk_%d" % gidx)], entry[0] + '_GLOBAL')
+
def add_bel_ec(ec):
ectype, x, y, z = ec
bel = len(bel_name)
@@ -894,15 +945,13 @@ def add_bel_ec(ec):
bel_pos.append((x, y, z))
bel_wires.append(list())
for entry in extra_cells[ec]:
- if is_ec_wire(entry) and "glb_netwk_" not in entry[1][2]: # TODO: osc glb output conflicts with GB
+ if is_ec_wire(entry):
if is_ec_output(entry):
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):
- x, y, z = entry[1]
- z = 'io_{}/D_IN_0'.format(z)
- add_bel_output(bel, wire_names[(x, y, z)], entry[0])
+ add_pll_clock_output(bel, ec, entry)
else:
extra_cell_config[bel].append(entry)
@@ -973,42 +1022,8 @@ for tile_xy, tile_type in sorted(tiles.items()):
for i in range(2):
add_bel_io(tile_xy[0], tile_xy[1], i)
- if dev_name == "1k":
- add_bel_gb(tile_xy, 7, 0, 0)
- add_bel_gb(tile_xy, 7, 17, 1)
- add_bel_gb(tile_xy, 13, 9, 2)
- add_bel_gb(tile_xy, 0, 9, 3)
- add_bel_gb(tile_xy, 6, 17, 4)
- add_bel_gb(tile_xy, 6, 0, 5)
- add_bel_gb(tile_xy, 0, 8, 6)
- add_bel_gb(tile_xy, 13, 8, 7)
- elif dev_name == "5k":
- add_bel_gb(tile_xy, 13, 0, 0)
- add_bel_gb(tile_xy, 13, 31, 1)
- add_bel_gb(tile_xy, 19, 31, 2)
- add_bel_gb(tile_xy, 6, 31, 3)
- add_bel_gb(tile_xy, 12, 31, 4)
- add_bel_gb(tile_xy, 12, 0, 5)
- add_bel_gb(tile_xy, 6, 0, 6)
- add_bel_gb(tile_xy, 19, 0, 7)
- elif dev_name == "8k":
- add_bel_gb(tile_xy, 33, 16, 7)
- add_bel_gb(tile_xy, 0, 16, 6)
- add_bel_gb(tile_xy, 17, 33, 1)
- add_bel_gb(tile_xy, 17, 0, 0)
- add_bel_gb(tile_xy, 0, 17, 3)
- add_bel_gb(tile_xy, 33, 17, 2)
- add_bel_gb(tile_xy, 16, 0, 5)
- add_bel_gb(tile_xy, 16, 33, 4)
- elif dev_name == "384":
- add_bel_gb(tile_xy, 7, 4, 7)
- add_bel_gb(tile_xy, 0, 4, 6)
- add_bel_gb(tile_xy, 4, 9, 1)
- add_bel_gb(tile_xy, 4, 0, 0)
- add_bel_gb(tile_xy, 0, 5, 3)
- add_bel_gb(tile_xy, 7, 5, 2)
- add_bel_gb(tile_xy, 3, 0, 5)
- add_bel_gb(tile_xy, 3, 9, 4)
+ for gidx, ginfo in glbinfo.items():
+ add_bel_gb(tile_xy, ginfo['gb_x'], ginfo['gb_y'], gidx)
if tile_type == "ramb":
add_bel_ram(tile_xy[0], tile_xy[1])
@@ -1423,6 +1438,14 @@ for cell, timings in sorted(cell_timings.items()):
bba.u32(len(timings), "num_paths")
bba.r("cell_paths_%d" % beltype, "path_delays")
+bba.l("global_network_info_%s" % dev_name, "GlobalNetworkInfoPOD")
+for i in range(len(glbinfo)):
+ for k in ['gb_x', 'gb_y', 'pi_gb_x', 'pi_gb_y', 'pi_gb_pio', 'pi_eb_bank']:
+ bba.u8(glbinfo[i][k], k)
+ for k in ['pi_eb_x', 'pi_eb_y']:
+ bba.u16(glbinfo[i][k], k)
+ bba.u16(0, "padding")
+
bba.l("chip_info_%s" % dev_name)
bba.u32(dev_width, "dev_width")
bba.u32(dev_height, "dev_height")
@@ -1433,6 +1456,7 @@ bba.u32(len(switchinfo), "num_switches")
bba.u32(len(extra_cell_config), "num_belcfgs")
bba.u32(len(packageinfo), "num_packages")
bba.u32(len(cell_timings), "num_timing_cells")
+bba.u32(len(glbinfo), "num_global_networks")
bba.r("bel_data_%s" % dev_name, "bel_data")
bba.r("wire_data_%s" % dev_name, "wire_data")
bba.r("pip_data_%s" % dev_name, "pip_data")
@@ -1441,6 +1465,7 @@ bba.r("bits_info_%s" % dev_name, "bits_info")
bba.r("bel_config_%s" % dev_name if len(extra_cell_config) > 0 else None, "bel_config")
bba.r("package_info_%s" % dev_name, "packages_data")
bba.r("cell_timings_%s" % dev_name, "cell_timing")
+bba.r("global_network_info_%s" % dev_name, "global_network_info")
bba.r("tile_wire_names", "tile_wire_names")
bba.pop()
diff --git a/ice40/constids.inc b/ice40/constids.inc
index dad08e59..e1c4992e 100644
--- a/ice40/constids.inc
+++ b/ice40/constids.inc
@@ -121,6 +121,8 @@ X(DYNAMICDELAY_7)
X(LOCK)
X(PLLOUT_A)
X(PLLOUT_B)
+X(PLLOUT_A_GLOBAL)
+X(PLLOUT_B_GLOBAL)
X(BYPASS)
X(RESETB)
X(LATCHINPUTVALUE)
diff --git a/ice40/pack.cc b/ice40/pack.cc
index e0a9f6ad..dae19b2d 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -381,12 +381,44 @@ static void pack_constants(Context *ctx)
}
}
+static std::unique_ptr<CellInfo> create_padin_gbuf(Context *ctx, CellInfo *cell, IdString port_name,
+ std::string gbuf_name)
+{
+ // Find the matching SB_GB BEL connected to the same global network
+ BelId gb_bel;
+ BelId bel = ctx->getBelByName(ctx->id(cell->attrs[ctx->id("BEL")]));
+ auto wire = ctx->getBelPinWire(bel, port_name);
+ for (auto src_bel : ctx->getWireBelPins(wire)) {
+ if (ctx->getBelType(src_bel.bel) == id_SB_GB && src_bel.pin == id_GLOBAL_BUFFER_OUTPUT) {
+ gb_bel = src_bel.bel;
+ break;
+ }
+ }
+
+ NPNR_ASSERT(gb_bel != BelId());
+
+ // Create a SB_GB Cell and lock it there
+ std::unique_ptr<CellInfo> gb = create_ice_cell(ctx, ctx->id("SB_GB"), gbuf_name);
+ gb->attrs[ctx->id("FOR_PAD_IN")] = "1";
+ gb->attrs[ctx->id("BEL")] = ctx->getBelName(gb_bel).str(ctx);
+
+ // Reconnect the net to that port for easier identification it's a global net
+ replace_port(cell, port_name, gb.get(), id_GLOBAL_BUFFER_OUTPUT);
+
+ return gb;
+}
+
static bool is_nextpnr_iob(Context *ctx, CellInfo *cell)
{
return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") ||
cell->type == ctx->id("$nextpnr_iobuf");
}
+static bool is_ice_iob(const Context *ctx, const CellInfo *cell)
+{
+ return is_sb_io(ctx, cell) || is_sb_gb_io(ctx, cell);
+}
+
// Pack IO buffers
static void pack_io(Context *ctx)
{
@@ -399,12 +431,15 @@ static void pack_io(Context *ctx)
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_nextpnr_iob(ctx, ci)) {
- CellInfo *sb = nullptr;
+ CellInfo *sb = nullptr, *rgb = nullptr;
if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) {
- sb = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_sb_io, ctx->id("PACKAGE_PIN"), true, ci);
+ sb = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_ice_iob, ctx->id("PACKAGE_PIN"), true, ci);
} else if (ci->type == ctx->id("$nextpnr_obuf")) {
- sb = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_sb_io, ctx->id("PACKAGE_PIN"), true, ci);
+ NetInfo *net = ci->ports.at(ctx->id("I")).net;
+ sb = net_only_drives(ctx, net, is_ice_iob, ctx->id("PACKAGE_PIN"), true, ci);
+ if (net && net->driver.cell && is_sb_rgba_drv(ctx, net->driver.cell))
+ rgb = net->driver.cell;
}
if (sb != nullptr) {
// Trivial case, SB_IO used. Just destroy the net and the
@@ -415,8 +450,8 @@ static void pack_io(Context *ctx)
if (((ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) &&
net->users.size() > 1) ||
(ci->type == ctx->id("$nextpnr_obuf") && (net->users.size() > 2 || net->driver.cell != nullptr)))
- log_error("PACKAGE_PIN of SB_IO '%s' connected to more than a single top level IO.\n",
- sb->name.c_str(ctx));
+ log_error("PACKAGE_PIN of %s '%s' connected to more than a single top level IO.\n",
+ sb->type.c_str(ctx), sb->name.c_str(ctx));
if (net != nullptr) {
delete_nets.insert(net->name);
@@ -428,6 +463,11 @@ static void pack_io(Context *ctx)
delete_nets.insert(net2->name);
}
}
+ } else if (rgb != nullptr) {
+ log_info("%s use by SB_RGBA_DRV %s, not creating SB_IO\n", ci->name.c_str(ctx), rgb->name.c_str(ctx));
+ disconnect_port(ctx, ci, ctx->id("I"));
+ packed_cells.insert(ci->name);
+ continue;
} else {
// Create a SB_IO buffer
std::unique_ptr<CellInfo> ice_cell =
@@ -438,13 +478,26 @@ static void pack_io(Context *ctx)
}
packed_cells.insert(ci->name);
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(sb->attrs, sb->attrs.begin()));
- } else if (is_sb_io(ctx, ci)) {
+ } else if (is_sb_io(ctx, ci) || is_sb_gb_io(ctx, ci)) {
NetInfo *net = ci->ports.at(ctx->id("PACKAGE_PIN")).net;
if ((net != nullptr) && (net->users.size() > 1))
- log_error("PACKAGE_PIN of SB_IO '%s' connected to more than a single top level IO.\n",
+ log_error("PACKAGE_PIN of %s '%s' connected to more than a single top level IO.\n", ci->type.c_str(ctx),
ci->name.c_str(ctx));
}
}
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (is_sb_gb_io(ctx, ci)) {
+ // If something is connecto the GLOBAL OUTPUT, create the fake 'matching' SB_GB
+ std::unique_ptr<CellInfo> gb =
+ create_padin_gbuf(ctx, ci, id_GLOBAL_BUFFER_OUTPUT, "$gbuf_" + ci->name.str(ctx) + "_io");
+ new_cells.push_back(std::move(gb));
+
+ // Make it a normal SB_IO with global marker
+ ci->type = ctx->id("SB_IO");
+ ci->attrs[ctx->id("GLOBAL")] = "1";
+ }
+ }
for (auto pcell : packed_cells) {
ctx->cells.erase(pcell);
}
@@ -461,8 +514,8 @@ static bool is_logic_port(BaseCtx *ctx, const PortRef &port)
{
if (is_clock_port(ctx, port) || is_reset_port(ctx, port) || is_enable_port(ctx, port))
return false;
- return !is_sb_io(ctx, port.cell) && !is_sb_pll40(ctx, port.cell) && !is_sb_pll40_pad(ctx, port.cell) &&
- port.cell->type != ctx->id("SB_GB");
+ return !is_sb_io(ctx, port.cell) && !is_sb_gb_io(ctx, port.cell) && !is_gbuf(ctx, port.cell) &&
+ !is_sb_pll40(ctx, port.cell);
}
static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen, bool is_logic)
@@ -650,6 +703,22 @@ static std::unique_ptr<CellInfo> spliceLUT(Context *ctx, CellInfo *ci, IdString
return pt;
}
+// Force placement for cells that are unique anyway
+static BelId cell_place_unique(Context *ctx, CellInfo *ci)
+{
+ for (auto bel : ctx->getBels()) {
+ if (ctx->getBelType(bel) != ci->type)
+ continue;
+ if (ctx->isBelLocked(bel))
+ continue;
+ IdString bel_name = ctx->getBelName(bel);
+ ci->attrs[ctx->id("BEL")] = bel_name.str(ctx);
+ log_info(" constrained %s '%s' to %s\n", ci->type.c_str(ctx), ci->name.c_str(ctx), bel_name.c_str(ctx));
+ return bel;
+ }
+ log_error("Unable to place cell '%s' of type '%s'\n", ci->name.c_str(ctx), ci->type.c_str(ctx));
+}
+
// Pack special functions
static void pack_special(Context *ctx)
{
@@ -664,25 +733,33 @@ static void pack_special(Context *ctx)
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_LFOSC"), ci->name.str(ctx) + "_OSC");
packed_cells.insert(ci->name);
+ cell_place_unique(ctx, packed.get());
replace_port(ci, ctx->id("CLKLFEN"), packed.get(), ctx->id("CLKLFEN"));
replace_port(ci, ctx->id("CLKLFPU"), packed.get(), ctx->id("CLKLFPU"));
- if (/*bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))*/ true) { // FIXME
+ if (bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))) {
replace_port(ci, ctx->id("CLKLF"), packed.get(), ctx->id("CLKLF_FABRIC"));
} else {
replace_port(ci, ctx->id("CLKLF"), packed.get(), ctx->id("CLKLF"));
+ std::unique_ptr<CellInfo> gb =
+ create_padin_gbuf(ctx, packed.get(), ctx->id("CLKLF"), "$gbuf_" + ci->name.str(ctx) + "_lfosc");
+ new_cells.push_back(std::move(gb));
}
new_cells.push_back(std::move(packed));
} else if (is_sb_hfosc(ctx, ci)) {
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_HFOSC"), ci->name.str(ctx) + "_OSC");
packed_cells.insert(ci->name);
+ cell_place_unique(ctx, packed.get());
packed->params[ctx->id("CLKHF_DIV")] = str_or_default(ci->params, ctx->id("CLKHF_DIV"), "0b00");
replace_port(ci, ctx->id("CLKHFEN"), packed.get(), ctx->id("CLKHFEN"));
replace_port(ci, ctx->id("CLKHFPU"), packed.get(), ctx->id("CLKHFPU"));
- if (/*bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))*/ true) { // FIXME
+ if (bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))) {
replace_port(ci, ctx->id("CLKHF"), packed.get(), ctx->id("CLKHF_FABRIC"));
} else {
replace_port(ci, ctx->id("CLKHF"), packed.get(), ctx->id("CLKHF"));
+ std::unique_ptr<CellInfo> gb =
+ create_padin_gbuf(ctx, packed.get(), ctx->id("CLKHF"), "$gbuf_" + ci->name.str(ctx) + "_hfosc");
+ new_cells.push_back(std::move(gb));
}
new_cells.push_back(std::move(packed));
} else if (is_sb_spram(ctx, ci)) {
@@ -718,6 +795,29 @@ 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_rgba_drv(ctx, ci)) {
+ /* Force placement (no choices anyway) */
+ cell_place_unique(ctx, ci);
+
+ /* Disconnect all external ports and check there is no users (they should have been
+ * dealth with during IO packing */
+ for (auto port : ci->ports) {
+ PortInfo &pi = port.second;
+ NetInfo *net = pi.net;
+
+ if (net == nullptr)
+ continue;
+ if ((pi.name != ctx->id("RGB0")) && (pi.name != ctx->id("RGB1")) && (pi.name != ctx->id("RGB2")))
+ continue;
+
+ if (net->users.size() > 0)
+ log_error("SB_RGBA_DRV port connected to more than just package pin !\n");
+
+ ctx->nets.erase(net->name);
+ }
+ ci->ports.erase(ctx->id("RGB0"));
+ ci->ports.erase(ctx->id("RGB1"));
+ ci->ports.erase(ctx->id("RGB2"));
} else if (is_sb_pll40(ctx, ci)) {
bool is_pad = is_sb_pll40_pad(ctx, ci);
bool is_core = !is_pad;
@@ -732,6 +832,24 @@ static void pack_special(Context *ctx)
for (auto param : ci->params)
packed->params[param.first] = param.second;
+ const std::map<IdString, IdString> pos_map_name = {
+ {ctx->id("PLLOUT_SELECT"), ctx->id("PLLOUT_SELECT_A")},
+ {ctx->id("PLLOUT_SELECT_PORTA"), ctx->id("PLLOUT_SELECT_A")},
+ {ctx->id("PLLOUT_SELECT_PORTB"), ctx->id("PLLOUT_SELECT_B")},
+ };
+ const std::map<std::string, int> pos_map_val = {
+ {"GENCLK", 0},
+ {"GENCLK_HALF", 1},
+ {"SHIFTREG_90deg", 2},
+ {"SHIFTREG_0deg", 3},
+ };
+ for (auto param : ci->params)
+ if (pos_map_name.find(param.first) != pos_map_name.end()) {
+ if (pos_map_val.find(param.second) == pos_map_val.end())
+ log_error("Invalid PLL output selection '%s'\n", param.second.c_str());
+ packed->params[pos_map_name.at(param.first)] = std::to_string(pos_map_val.at(param.second));
+ }
+
auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")];
packed->params[ctx->id("FEEDBACK_PATH")] =
feedback_path == "DELAY"
@@ -744,32 +862,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);
@@ -777,24 +869,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) {
@@ -838,6 +921,8 @@ static void pack_special(Context *ctx)
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) != id_ICESTORM_PLL)
continue;
+ if (ctx->isBelLocked(bel))
+ continue;
// A PAD PLL must have its' PACKAGEPIN on the SB_IO that's shared
// with PLLOUT_A.
@@ -879,11 +964,22 @@ static void pack_special(Context *ctx)
packed->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
pll_bel = bel;
constrained = true;
+ break;
}
if (!constrained) {
log_error("Could not constrain PLL '%s' to any PLL Bel (too many PLLs?)\n",
packed->name.c_str(ctx));
}
+ } else {
+ pll_bel = ctx->getBelByName(ctx->id(packed->attrs[ctx->id("BEL")]));
+ if (ctx->getBelType(pll_bel) != id_ICESTORM_PLL)
+ log_error("PLL '%s' is constrained to BEL %s which isn't a ICESTORM_PLL BEL\n",
+ packed->name.c_str(ctx), ctx->getBelName(pll_bel).c_str(ctx));
+ if (ctx->isBelLocked(pll_bel))
+ log_error("PLL '%s' is constrained to locked BEL %s\n", packed->name.c_str(ctx),
+ ctx->getBelName(pll_bel).c_str(ctx));
+ log_info(" constrained PLL '%s' to %s\n", packed->name.c_str(ctx),
+ ctx->getBelName(pll_bel).c_str(ctx));
}
// Delete the original PACKAGEPIN net if needed.
@@ -952,6 +1048,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));
}
}
@@ -971,13 +1085,13 @@ bool Arch::pack()
try {
log_break();
pack_constants(ctx);
- promote_globals(ctx);
pack_io(ctx);
pack_lut_lutffs(ctx);
pack_nonlut_ffs(ctx);
pack_carries(ctx);
pack_ram(ctx);
pack_special(ctx);
+ promote_globals(ctx);
ctx->assignArchInfo();
constrain_chains(ctx);
ctx->assignArchInfo();