Merge pull request #131 from smunaut/ice40_fixes
iCE40: Bug fixes and general improvement of global network support
This commit is contained in:
commit
343569105d
@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
20
ice40/arch.h
20
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;
|
||||
|
@ -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));
|
||||
|
@ -150,8 +150,13 @@ struct ArchCellInfo
|
||||
struct
|
||||
{
|
||||
bool lvds;
|
||||
bool global;
|
||||
// TODO: clk packing checks...
|
||||
} ioInfo;
|
||||
struct
|
||||
{
|
||||
bool forPadIn;
|
||||
} gbInfo;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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()) {
|
||||
|
@ -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"))
|
||||
|
@ -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);
|
||||
|
107
ice40/chipdb.py
107
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()
|
||||
|
@ -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)
|
||||
|
218
ice40/pack.cc
218
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();
|
||||
|
Loading…
Reference in New Issue
Block a user