aboutsummaryrefslogtreecommitdiffstats
path: root/ice40
diff options
context:
space:
mode:
authorEddie Hung <e.hung@imperial.ac.uk>2018-07-24 22:20:10 -0700
committerEddie Hung <e.hung@imperial.ac.uk>2018-07-24 22:20:10 -0700
commit9382938661613b84e3ad3155e414aaae2fa87da2 (patch)
tree107c14811cf2e91c82ee4f9ea983260ee7acea8c /ice40
parent4920cf18fa1128758dac2ffd12bf88d194863f17 (diff)
parent32c7247785f48b2307e559a0af50d9387bda8b49 (diff)
downloadnextpnr-9382938661613b84e3ad3155e414aaae2fa87da2.tar.gz
nextpnr-9382938661613b84e3ad3155e414aaae2fa87da2.tar.bz2
nextpnr-9382938661613b84e3ad3155e414aaae2fa87da2.zip
Merge branch 'master' into redist_slack
Diffstat (limited to 'ice40')
-rw-r--r--ice40/arch.cc44
-rw-r--r--ice40/arch.h7
-rw-r--r--ice40/arch_place.cc34
-rw-r--r--ice40/arch_pybindings.cc2
-rw-r--r--ice40/bitstream.cc108
-rw-r--r--ice40/cells.cc50
-rw-r--r--ice40/cells.h15
-rw-r--r--ice40/chipdb.py41
-rw-r--r--ice40/family.cmake21
-rw-r--r--ice40/gfx.cc2
-rw-r--r--ice40/gfx.h6
-rw-r--r--ice40/pack.cc172
-rw-r--r--ice40/place_legaliser.cc8
-rw-r--r--ice40/portpins.inc2
14 files changed, 419 insertions, 93 deletions
diff --git a/ice40/arch.cc b/ice40/arch.cc
index 51fa6472..5ec0a42d 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -2,6 +2,7 @@
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -291,23 +292,6 @@ BelRange Arch::getBelsByTile(int x, int y) const
return br;
}
-BelRange Arch::getBelsAtSameTile(BelId bel) const
-{
- BelRange br;
- NPNR_ASSERT(bel != BelId());
- int x = chip_info->bel_data[bel.index].x;
- int y = chip_info->bel_data[bel.index].y;
- int start = bel.index, end = bel.index;
- while (start >= 0 && chip_info->bel_data[start].x == x && chip_info->bel_data[start].y == y)
- start--;
- start++;
- br.b.cursor = start;
- while (end < chip_info->num_bels && chip_info->bel_data[end].x == x && chip_info->bel_data[end].y == y)
- end++;
- br.e.cursor = end;
- return br;
-}
-
PortType Arch::getBelPinType(BelId bel, PortPin pin) const
{
NPNR_ASSERT(bel != BelId());
@@ -331,11 +315,12 @@ WireId Arch::getBelPinWire(BelId bel, PortPin pin) const
int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires;
const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
- for (int i = 0; i < num_bel_wires; i++)
+ for (int i = 0; i < num_bel_wires; i++) {
if (bel_wires[i].port == pin) {
ret.index = bel_wires[i].wire_index;
break;
}
+ }
return ret;
}
@@ -482,14 +467,6 @@ std::vector<GroupId> Arch::getGroupGroups(GroupId group) const
// -----------------------------------------------------------------------
-void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const
-{
- NPNR_ASSERT(bel != BelId());
- x = chip_info->bel_data[bel.index].x;
- y = chip_info->bel_data[bel.index].y;
- gb = chip_info->bel_data[bel.index].type == TYPE_SB_GB;
-}
-
delay_t Arch::estimateDelay(WireId src, WireId dst) const
{
NPNR_ASSERT(src != WireId());
@@ -739,6 +716,14 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
} else if (fromPort == id("I2") && toPort == id("COUT")) {
delay = 230;
return true;
+ } else if (fromPort == id("CLK") && toPort == id("O")) {
+ delay = 540;
+ return true;
+ }
+ } else if (cell->type == id("ICESTORM_RAM")) {
+ if (fromPort == id("RCLK")) {
+ delay = 2140;
+ return true;
}
}
return false;
@@ -749,6 +734,11 @@ IdString Arch::getPortClock(const CellInfo *cell, IdString port) const
if (cell->type == id("ICESTORM_LC") && bool_or_default(cell->params, id("DFF_ENABLE"))) {
if (port != id("LO") && port != id("CIN") && port != id("COUT"))
return id("CLK");
+ } else if (cell->type == id("ICESTORM_RAM")) {
+ if (port.str(this)[0] == 'R')
+ return id("RCLK");
+ else
+ return id("WCLK");
}
return IdString();
}
@@ -757,6 +747,8 @@ bool Arch::isClockPort(const CellInfo *cell, IdString port) const
{
if (cell->type == id("ICESTORM_LC") && port == id("CLK"))
return true;
+ if (cell->type == id("ICESTORM_RAM") && (port == id("RCLK") || (port == id("WCLK"))))
+ return true;
return false;
}
diff --git a/ice40/arch.h b/ice40/arch.h
index 697d4142..a4d43cd4 100644
--- a/ice40/arch.h
+++ b/ice40/arch.h
@@ -83,10 +83,6 @@ NPNR_PACKED_STRUCT(struct WireInfoPOD {
int32_t num_uphill, num_downhill;
RelPtr<int32_t> pips_uphill, pips_downhill;
- int32_t num_bels_downhill;
- BelPortPOD bel_uphill;
- RelPtr<BelPortPOD> bels_downhill;
-
int32_t num_bel_pins;
RelPtr<BelPortPOD> bel_pins;
@@ -453,8 +449,6 @@ struct Arch : BaseCtx
bool getBelGlobalBuf(BelId bel) const { return chip_info->bel_data[bel.index].type == TYPE_SB_GB; }
- BelRange getBelsAtSameTile(BelId bel) const NPNR_DEPRECATED;
-
BelType getBelType(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
@@ -685,7 +679,6 @@ struct Arch : BaseCtx
// -------------------------------------------------
- void estimatePosition(BelId bel, int &x, int &y, bool &gb) const NPNR_DEPRECATED;
delay_t estimateDelay(WireId src, WireId dst) const;
delay_t getDelayEpsilon() const { return 20; }
delay_t getRipupDelayPenalty() const { return 200; }
diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc
index cf1276a7..59e1807d 100644
--- a/ice40/arch_place.cc
+++ b/ice40/arch_place.cc
@@ -3,6 +3,7 @@
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -71,7 +72,8 @@ bool Arch::isBelLocationValid(BelId bel) const
{
if (getBelType(bel) == TYPE_ICESTORM_LC) {
std::vector<const CellInfo *> bel_cells;
- for (auto bel_other : getBelsAtSameTile(bel)) {
+ Loc bel_loc = getBelLocation(bel);
+ for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
IdString cell_other = getBoundBelCell(bel_other);
if (cell_other != IdString()) {
const CellInfo *ci_other = cells.at(cell_other).get();
@@ -94,8 +96,8 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
NPNR_ASSERT(getBelType(bel) == TYPE_ICESTORM_LC);
std::vector<const CellInfo *> bel_cells;
-
- for (auto bel_other : getBelsAtSameTile(bel)) {
+ Loc bel_loc = getBelLocation(bel);
+ for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
IdString cell_other = getBoundBelCell(bel_other);
if (cell_other != IdString() && bel_other != bel) {
const CellInfo *ci_other = cells.at(cell_other).get();
@@ -106,6 +108,32 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
bel_cells.push_back(cell);
return logicCellsCompatible(bel_cells);
} else if (cell->type == id_sb_io) {
+ // Do not allow placement of input SB_IOs on blocks where there a PLL is outputting to.
+
+ // Find shared PLL by looking for driving bel siblings from D_IN_0
+ // that are a PLL clock output.
+ auto wire = getBelPinWire(bel, PIN_D_IN_0);
+ PortPin pll_bel_pin;
+ BelId pll_bel;
+ for (auto pin : getWireBelPins(wire)) {
+ if (pin.pin == PIN_PLLOUT_A || pin.pin == PIN_PLLOUT_B) {
+ pll_bel = pin.bel;
+ pll_bel_pin = pin.pin;
+ break;
+ }
+ }
+ // Is there a PLL that shares this IO buffer?
+ if (pll_bel.index != -1) {
+ // Is a PLL placed in this PLL bel?
+ if (!checkBelAvail(pll_bel)) {
+ // Is the shared port driving a net?
+ auto pll_cell = getBoundBelCell(pll_bel);
+ auto pi = cells.at(pll_cell)->ports[portPinToId(pll_bel_pin)];
+ if (pi.net != nullptr) {
+ return false;
+ }
+ }
+ }
return getBelPackagePin(bel) != "";
} else if (cell->type == id_sb_gb) {
NPNR_ASSERT(cell->ports.at(id_glb_buf_out).net != nullptr);
diff --git a/ice40/arch_pybindings.cc b/ice40/arch_pybindings.cc
index 246d0f57..98164e87 100644
--- a/ice40/arch_pybindings.cc
+++ b/ice40/arch_pybindings.cc
@@ -79,8 +79,6 @@ void arch_wrap_python()
conv_to_str<IdString>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell");
fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls,
"getBels");
- fn_wrapper_1a<Context, decltype(&Context::getBelsAtSameTile), &Context::getBelsAtSameTile, wrap_context<BelRange>,
- conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelsAtSameTile");
fn_wrapper_2a<Context, decltype(&Context::getBelPinWire), &Context::getBelPinWire, conv_to_str<WireId>,
conv_from_str<BelId>, conv_from_str<PortPin>>::def_wrap(ctx_cls, "getBelPinWire");
diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc
index 9ac8e857..e9851a83 100644
--- a/ice40/bitstream.cc
+++ b/ice40/bitstream.cc
@@ -3,6 +3,7 @@
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -74,14 +75,26 @@ void set_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cf
for (int i = 0; i < cfg.num_bits; i++) {
int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
if (cbit && !value)
- log_error("clearing already set config bit %s", name.c_str());
+ log_error("clearing already set config bit %s\n", name.c_str());
cbit = value;
}
} else {
int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
cbit = value;
if (cbit && !value)
- log_error("clearing already set config bit %s[%d]", name.c_str(), index);
+ log_error("clearing already set config bit %s[%d]\n", name.c_str(), index);
+ }
+}
+
+// Set an IE_{EN,REN} logical bit in a tile config. Logical means enabled.
+// On {HX,LP}1K devices these bits are active low, so we need to invert them.
+void set_ie_bit_logical(const Context *ctx, const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg,
+ const std::string &name, bool value)
+{
+ if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
+ set_config(ti, tile_cfg, name, !value);
+ } else {
+ set_config(ti, tile_cfg, name, value);
}
}
@@ -117,7 +130,7 @@ static const BelConfigPOD &get_ec_config(const ChipInfoPOD *chip, BelId bel)
typedef std::vector<std::vector<std::vector<std::vector<int8_t>>>> chipconfig_t;
static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfigPOD &cell_cbits, std::string name,
- bool value)
+ bool value, std::string prefix)
{
const ChipInfoPOD *chip = ctx->chip_info;
@@ -125,7 +138,7 @@ static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfi
const auto &cbit = cell_cbits.entries[i];
if (cbit.entry_name.get() == name) {
const auto &ti = chip->bits_info->tiles_nonrouting[tile_at(ctx, cbit.x, cbit.y)];
- set_config(ti, config.at(cbit.y).at(cbit.x), std::string("IpConfig.") + cbit.cbit_name.get(), value);
+ set_config(ti, config.at(cbit.y).at(cbit.x), prefix + cbit.cbit_name.get(), value);
return;
}
}
@@ -133,7 +146,7 @@ static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfi
}
void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell,
- const std::vector<std::pair<std::string, int>> &params, bool string_style)
+ const std::vector<std::pair<std::string, int>> &params, bool string_style, std::string prefix)
{
const ChipInfoPOD *chip = ctx->chip_info;
const auto &bc = get_ec_config(chip, cell->bel);
@@ -163,10 +176,10 @@ void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *ce
value.resize(p.second);
if (p.second == 1) {
- set_ec_cbit(config, ctx, bc, p.first, value.at(0));
+ set_ec_cbit(config, ctx, bc, p.first, value.at(0), prefix);
} else {
for (int i = 0; i < p.second; i++) {
- set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i));
+ set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i), prefix);
}
}
}
@@ -258,8 +271,13 @@ 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;
+
// Set logic cell config
for (auto &cell : ctx->cells) {
+
BelId bel = cell.second.get()->bel;
if (bel == BelId()) {
std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl;
@@ -304,6 +322,7 @@ 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"));
@@ -405,7 +424,70 @@ void write_asc(const Context *ctx, std::ostream &out)
{"MODE_8x8", 1},
{"A_SIGNED", 1},
{"B_SIGNED", 1}};
- configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false);
+ configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false, std::string("IpConfig."));
+ } else if (cell.second->type == ctx->id("ICESTORM_PLL")) {
+ const std::vector<std::pair<std::string, int>> pll_params = {{"DELAY_ADJMODE_FB", 1},
+ {"DELAY_ADJMODE_REL", 1},
+ {"DIVF", 7},
+ {"DIVQ", 3},
+ {"DIVR", 4},
+ {"FDA_FEEDBACK", 4},
+ {"FDA_RELATIVE", 4},
+ {"FEEDBACK_PATH", 3},
+ {"FILTER_RANGE", 3},
+ {"PLLOUT_SELECT_A", 2},
+ {"PLLOUT_SELECT_B", 2},
+ {"PLLTYPE", 3},
+ {"SHIFTREG_DIV_MODE", 1},
+ {"TEST_MODE", 1}};
+ 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, ctx->portPinFromId(port.second.name));
+ BelId io_bel;
+ for (auto pin : ctx->getWireBelPins(wire)) {
+ if (pin.pin == PIN_D_IN_0) {
+ io_bel = pin.bel;
+ break;
+ }
+ }
+ NPNR_ASSERT(io_bel.index != -1);
+ auto io_bel_loc = ctx->getBelLocation(io_bel);
+
+ // Check that this SB_IO is either unused or just used as an output.
+ 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.
+ 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.
+ set_config(ti, config.at(io_bel_loc.y).at(io_bel_loc.x),
+ "IOB_" + std::to_string(io_bel_loc.z) + ".PINTYPE_0", true);
+ }
+
} else {
NPNR_ASSERT(false);
}
@@ -416,14 +498,16 @@ 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))) {
+ continue;
+ }
+
auto ieren = get_ieren(bi, x, y, z);
int iex, iey, iez;
std::tie(iex, iey, iez) = ieren;
if (iez != -1) {
- 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);
- }
+ 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);
}
} else if (ctx->bel_to_cell[bel.index] == IdString() && ctx->getBelType(bel) == TYPE_ICESTORM_RAM) {
const BelInfoPOD &beli = ci.bel_data[bel.index];
diff --git a/ice40/cells.cc b/ice40/cells.cc
index 71a65d44..610bf85e 100644
--- a/ice40/cells.cc
+++ b/ice40/cells.cc
@@ -3,6 +3,7 @@
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -207,6 +208,40 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
add_port(ctx, new_cell.get(), "ACCUMCO", PORT_OUT);
add_port(ctx, new_cell.get(), "SIGNEXTOUT", PORT_OUT);
+ } else if (type == ctx->id("ICESTORM_PLL")) {
+ new_cell->params[ctx->id("DELAY_ADJMODE_FB")] = "0";
+ new_cell->params[ctx->id("DELAY_ADJMODE_REL")] = "0";
+
+ new_cell->params[ctx->id("DIVF")] = "0";
+ new_cell->params[ctx->id("DIVQ")] = "0";
+ new_cell->params[ctx->id("DIVR")] = "0";
+
+ 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("FILTER_RANGE")] = "0";
+
+ new_cell->params[ctx->id("PLLOUT_SELECT_A")] = "0";
+ new_cell->params[ctx->id("PLLOUT_SELECT_B")] = "0";
+
+ new_cell->params[ctx->id("PLLTYPE")] = "0";
+ new_cell->params[ctx->id("SHIFTREG_DIVMODE")] = "0";
+ new_cell->params[ctx->id("TEST_MODE")] = "0";
+
+ add_port(ctx, new_cell.get(), "BYPASS", PORT_IN);
+ add_port(ctx, new_cell.get(), "DYNAMICDELAY", PORT_IN);
+ add_port(ctx, new_cell.get(), "EXTFEEDBACK", PORT_IN);
+ add_port(ctx, new_cell.get(), "LATCHINPUTVALUE", PORT_IN);
+ add_port(ctx, new_cell.get(), "REFERENCECLK", PORT_IN);
+ add_port(ctx, new_cell.get(), "RESETB", PORT_IN);
+
+ add_port(ctx, new_cell.get(), "SCLK", PORT_IN);
+ add_port(ctx, new_cell.get(), "SDI", PORT_IN);
+ add_port(ctx, new_cell.get(), "SDI", PORT_OUT);
+
+ 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);
} else {
log_error("unable to create iCE40 cell of type %s", type.c_str(ctx));
}
@@ -312,6 +347,21 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio)
}
}
+uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell)
+{
+ if (cell->type == ctx->id("SB_PLL40_PAD"))
+ return 2;
+ if (cell->type == ctx->id("SB_PLL40_2_PAD"))
+ return 4;
+ if (cell->type == ctx->id("SB_PLL40_2F_PAD"))
+ return 5;
+ if (cell->type == ctx->id("SB_PLL40_CORE"))
+ return 3;
+ if (cell->type == ctx->id("SB_PLL40_2F_CORE"))
+ return 7;
+ NPNR_ASSERT(0);
+}
+
bool is_clock_port(const BaseCtx *ctx, const PortRef &port)
{
if (port.cell == nullptr)
diff --git a/ice40/cells.h b/ice40/cells.h
index 2f9c77e8..16135448 100644
--- a/ice40/cells.h
+++ b/ice40/cells.h
@@ -71,6 +71,21 @@ 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_pll40(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_CORE") ||
+ cell->type == ctx->id("SB_PLL40_2F_CORE");
+}
+
+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");
+}
+
+uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell);
+
// Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports
// as needed. Set no_dff if a DFF is not being used, so that the output
// can be reconnected
diff --git a/ice40/chipdb.py b/ice40/chipdb.py
index 0e8e3ba7..108197c1 100644
--- a/ice40/chipdb.py
+++ b/ice40/chipdb.py
@@ -41,8 +41,6 @@ extra_cells = dict()
extra_cell_config = dict()
packages = list()
-wire_uphill_belport = dict()
-wire_downhill_belports = dict()
wire_belports = dict()
wire_names = dict()
@@ -184,6 +182,8 @@ def wire_type(name):
wt = "LOCAL"
elif name in ("WCLK", "WCLKE", "WE", "RCLK", "RCLKE", "RE"):
wt = "LOCAL"
+ elif name in ("PLLOUT_A", "PLLOUT_B"):
+ wt = "LOCAL"
if wt is None:
print("No type for wire: %s (%s)" % (longname, name), file=sys.stderr)
@@ -451,17 +451,12 @@ for i in range(8):
add_wire(0, 0, "padin_%d" % i)
def add_bel_input(bel, wire, port):
- if wire not in wire_downhill_belports:
- wire_downhill_belports[wire] = set()
- wire_downhill_belports[wire].add((bel, port))
if wire not in wire_belports:
wire_belports[wire] = set()
wire_belports[wire].add((bel, port))
bel_wires[bel].append((wire, port, 0))
def add_bel_output(bel, wire, port):
- assert wire not in wire_uphill_belport
- wire_uphill_belport[wire] = (bel, port)
if wire not in wire_belports:
wire_belports[wire] = set()
wire_belports[wire].add((bel, port))
@@ -591,6 +586,9 @@ def is_ec_output(ec_entry):
if "glb_netwk_" in wirename: return True
return False
+def is_ec_pll_clock_output(ec, ec_entry):
+ return ec[0] == 'PLL' and ec_entry[0] in ('PLLOUT_A', 'PLLOUT_B')
+
def add_bel_ec(ec):
ectype, x, y, z = ec
bel = len(bel_name)
@@ -605,6 +603,10 @@ def add_bel_ec(ec):
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])
else:
extra_cell_config[bel].append(entry)
@@ -662,7 +664,7 @@ for tile_xy, tile_type in sorted(tiles.items()):
add_bel_ec(ec)
for ec in sorted(extra_cells.keys()):
- if ec[1] == 0 and ec[2] == 0:
+ if ec[1] in (0, dev_width - 1) and ec[2] in (0, dev_height - 1):
add_bel_ec(ec)
class BinaryBlobAssembler:
@@ -1001,15 +1003,6 @@ for wire in range(num_wires):
num_downhill = 0
list_downhill = None
- if wire in wire_downhill_belports:
- num_bels_downhill = len(wire_downhill_belports[wire])
- bba.l("wire%d_downbels" % wire, "BelPortPOD")
- for belport in sorted(wire_downhill_belports[wire]):
- bba.u32(belport[0], "bel_index")
- bba.u32(portpins[belport[1]], "port")
- else:
- num_bels_downhill = 0
-
if wire in wire_belports:
num_bel_pins = len(wire_belports[wire])
bba.l("wire%d_bels" % wire, "BelPortPOD")
@@ -1028,19 +1021,9 @@ for wire in range(num_wires):
info["num_downhill"] = num_downhill
info["list_downhill"] = list_downhill
- info["num_bels_downhill"] = num_bels_downhill
- info["list_bels_downhill"] = ("wire%d_downbels" % wire) if num_bels_downhill > 0 else None
-
info["num_bel_pins"] = num_bel_pins
info["list_bel_pins"] = ("wire%d_bels" % wire) if num_bel_pins > 0 else None
- if wire in wire_uphill_belport:
- info["uphill_bel"] = wire_uphill_belport[wire][0]
- info["uphill_pin"] = portpins[wire_uphill_belport[wire][1]]
- else:
- info["uphill_bel"] = -1
- info["uphill_pin"] = 0
-
avg_x, avg_y = 0, 0
if wire in wire_xy:
for x, y in wire_xy[wire]:
@@ -1115,10 +1098,6 @@ for wire, info in enumerate(wireinfo):
bba.u32(info["num_downhill"], "num_downhill")
bba.r(info["list_uphill"], "pips_uphill")
bba.r(info["list_downhill"], "pips_downhill")
- bba.u32(info["num_bels_downhill"], "num_bels_downhill")
- bba.u32(info["uphill_bel"], "bel_uphill.bel_index")
- bba.u32(info["uphill_pin"], "bel_uphill.port")
- bba.r(info["list_bels_downhill"], "bels_downhill")
bba.u32(info["num_bel_pins"], "num_bel_pins")
bba.r(info["list_bel_pins"], "bel_pins")
bba.u32(len(wire_segments[wire]), "num_segments")
diff --git a/ice40/family.cmake b/ice40/family.cmake
index 9af06f82..95cdf331 100644
--- a/ice40/family.cmake
+++ b/ice40/family.cmake
@@ -19,13 +19,18 @@ if (MSVC)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ice40/resources/chipdb.rc PROPERTIES LANGUAGE RC)
foreach (dev ${devices})
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
+ set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bba)
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bin)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
- add_custom_command(OUTPUT ${DEV_CC_DB}
- COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -b -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_DB}
+ add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
+ COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -b -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}
DEPENDS ${DEV_TXT_DB} ${DB_PY}
)
+ add_custom_command(OUTPUT ${DEV_CC_DB}
+ COMMAND bbasm < ${DEV_CC_BBA_DB} > ${DEV_CC_DB}
+ DEPENDS bbasm ${DEV_CC_BBA_DB}
+ )
target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB})
set_source_files_properties(${DEV_CC_DB} PROPERTIES HEADER_FILE_ONLY TRUE)
foreach (target ${family_targets})
@@ -36,14 +41,20 @@ else()
target_compile_options(ice40_chipdb PRIVATE -g0 -O0 -w)
foreach (dev ${devices})
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
+ set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bba)
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.cc)
set(DEV_PORTS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/portpins.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
+ add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
+ COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -c -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}.new
+ COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB}
+ DEPENDS ${DEV_TXT_DB} ${DB_PY}
+ )
add_custom_command(OUTPUT ${DEV_CC_DB}
- COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -c -p ${DEV_PORTS_INC} -g ${DEV_GFXH} ${DEV_TXT_DB} > ${DEV_CC_DB}.new
+ COMMAND bbasm < ${DEV_CC_BBA_DB} > ${DEV_CC_DB}.new
COMMAND mv ${DEV_CC_DB}.new ${DEV_CC_DB}
- DEPENDS ${DEV_TXT_DB} ${DB_PY}
- )
+ DEPENDS bbasm ${DEV_CC_BBA_DB}
+ )
target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB})
foreach (target ${family_targets})
target_sources(${target} PRIVATE $<TARGET_OBJECTS:ice40_chipdb>)
diff --git a/ice40/gfx.cc b/ice40/gfx.cc
index f6ed789f..1b01cbd8 100644
--- a/ice40/gfx.cc
+++ b/ice40/gfx.cc
@@ -647,7 +647,7 @@ void pipGfx(std::vector<GraphicElement> &g, int x, int y, float x1, float y1, fl
float ty = 0.5 * (y1 + y2);
GraphicElement el;
- el.type = GraphicElement::G_LINE;
+ el.type = GraphicElement::G_ARROW;
el.style = style;
if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) {
diff --git a/ice40/gfx.h b/ice40/gfx.h
index 8a55407d..7eeaccf1 100644
--- a/ice40/gfx.h
+++ b/ice40/gfx.h
@@ -464,7 +464,11 @@ enum GfxTileWireId
TILE_WIRE_SP12_H_R_23,
TILE_WIRE_SP12_H_L_22,
- TILE_WIRE_SP12_H_L_23
+ TILE_WIRE_SP12_H_L_23,
+
+ TILE_WIRE_PLLIN,
+ TILE_WIRE_PLLOUT_A,
+ TILE_WIRE_PLLOUT_B
};
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style);
diff --git a/ice40/pack.cc b/ice40/pack.cc
index 7e2e389c..b4f711f3 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -3,6 +3,7 @@
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -311,6 +312,10 @@ static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constne
(user.port != ctx->id("CLK") &&
((constval && user.port == ctx->id("CE")) || (!constval && user.port != ctx->id("CE"))))) {
uc->ports[user.port].net = nullptr;
+ } else if (is_ram(ctx, uc) && !constval && user.port != ctx->id("RCLK") && user.port != ctx->id("RCLKN") &&
+ user.port != ctx->id("WCLK") && user.port != ctx->id("WCLKN") && user.port != ctx->id("RCLKE") &&
+ user.port != ctx->id("WCLKE")) {
+ uc->ports[user.port].net = nullptr;
} else {
uc->ports[user.port].net = constnet;
constnet->users.push_back(user);
@@ -536,6 +541,56 @@ static void promote_globals(Context *ctx)
}
}
+// spliceLUT adds a pass-through LUT LC between the given cell's output port
+// and either all users or only non_LUT users.
+static std::unique_ptr<CellInfo> spliceLUT(Context *ctx, CellInfo *ci, IdString portId, bool onlyNonLUTs)
+{
+ auto port = ci->ports[portId];
+
+ NPNR_ASSERT(port.net != nullptr);
+
+ // Create pass-through LUT.
+ std::unique_ptr<CellInfo> pt =
+ create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "$nextpnr_ice40_pack_pll_lc");
+ pt->params[ctx->id("LUT_INIT")] = "255"; // output is always I3
+
+ // Create LUT output net.
+ std::unique_ptr<NetInfo> out_net = std::unique_ptr<NetInfo>(new NetInfo);
+ out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_ice40_pack_pll_net");
+ out_net->driver.cell = pt.get();
+ out_net->driver.port = ctx->id("O");
+ pt->ports.at(ctx->id("O")).net = out_net.get();
+
+ // New users of the original cell's port
+ std::vector<PortRef> new_users;
+ for (const auto &user : port.net->users) {
+ if (onlyNonLUTs && user.cell->type == ctx->id("ICESTORM_LC")) {
+ new_users.push_back(user);
+ continue;
+ }
+ // Rewrite pointer into net in user.
+ user.cell->ports[user.port].net = out_net.get();
+ // Add user to net.
+ PortRef pr;
+ pr.cell = user.cell;
+ pr.port = user.port;
+ out_net->users.push_back(pr);
+ }
+
+ // Add LUT to new users.
+ PortRef pr;
+ pr.cell = pt.get();
+ pr.port = ctx->id("I3");
+ new_users.push_back(pr);
+ pt->ports.at(ctx->id("I3")).net = port.net;
+
+ // Replace users of the original net.
+ port.net->users = new_users;
+
+ ctx->nets[out_net->name] = std::move(out_net);
+ return pt;
+}
+
// Pack special functions
static void pack_special(Context *ctx)
{
@@ -591,6 +646,123 @@ 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_pll40(ctx, ci)) {
+ std::unique_ptr<CellInfo> packed =
+ create_ice_cell(ctx, ctx->id("ICESTORM_PLL"), ci->name.str(ctx) + "_PLL");
+ packed_cells.insert(ci->name);
+
+ if (is_sb_pll40_pad(ctx, ci)) {
+ // TODO(q3k): Implement these after checking their behaviour on
+ // a board with exposed 'clock pads'.
+ log_error("SB_PLL40_*_PAD cells are not supported yet.\n");
+ }
+
+ for (auto attr : ci->attrs)
+ packed->attrs[attr.first] = attr.second;
+ for (auto param : ci->params)
+ packed->params[param.first] = param.second;
+
+ auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")];
+ packed->params[ctx->id("FEEDBACK_PATH")] =
+ feedback_path == "DELAY"
+ ? "0"
+ : feedback_path == "SIMPLE" ? "1"
+ : feedback_path == "PHASE_AND_DELAY"
+ ? "2"
+ : feedback_path == "EXTERNAL" ? "6" : feedback_path;
+ packed->params[ctx->id("PLLTYPE")] = std::to_string(sb_pll40_type(ctx, ci));
+
+ for (auto port : ci->ports) {
+ PortInfo &pi = port.second;
+ std::string newname = pi.name.str(ctx);
+ size_t bpos = newname.find('[');
+ if (bpos != std::string::npos) {
+ newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
+ }
+ if (pi.name == ctx->id("PLLOUTCOREA"))
+ newname = "PLLOUT_A";
+ if (pi.name == ctx->id("PLLOUTCOREB"))
+ newname = "PLLOUT_B";
+ if (pi.name == ctx->id("PLLOUTCORE"))
+ newname = "PLLOUT_A";
+ replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
+ }
+
+ // If PLL is not constrained already, do that - we need this
+ // information to then constrain the LOCK LUT.
+ BelId pll_bel;
+ bool constrained = false;
+ if (packed->attrs.find(ctx->id("BEL")) == packed->attrs.end()) {
+ for (auto bel : ctx->getBels()) {
+ if (ctx->getBelType(bel) != TYPE_ICESTORM_PLL)
+ continue;
+ log_info(" constrained '%s' to %s\n", packed->name.c_str(ctx), ctx->getBelName(bel).c_str(ctx));
+ packed->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
+ pll_bel = bel;
+ constrained = true;
+ }
+ if (!constrained) {
+ log_error(" could not constrain '%s' to any PLL Bel\n", packed->name.c_str(ctx));
+ }
+ }
+
+ // The LOCK signal on iCE40 PLLs goes through the neigh_op_bnl_1 wire.
+ // In practice, this means the LOCK signal can only directly reach LUT
+ // inputs.
+ // If we have a net connected to LOCK, make sure it only drives LUTs.
+ auto port = packed->ports[ctx->id("LOCK")];
+ if (port.net != nullptr) {
+ bool found_lut = false;
+ bool all_luts = true;
+ unsigned int lut_count = 0;
+ for (const auto &user : port.net->users) {
+ NPNR_ASSERT(user.cell != nullptr);
+ if (user.cell->type == ctx->id("ICESTORM_LC")) {
+ found_lut = true;
+ lut_count++;
+ } else {
+ all_luts = false;
+ }
+ }
+
+ if (found_lut && all_luts) {
+ // Every user is a LUT, carry on now.
+ } else if (found_lut && !all_luts && lut_count < 8) {
+ // Strategy: create a pass-through LUT, move all non-LUT users behind it.
+ log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx));
+ auto pt = spliceLUT(ctx, packed.get(), port.name, true);
+ new_cells.push_back(std::move(pt));
+ } else {
+ // Strategy: create a pass-through LUT, move every user behind it.
+ log_info(" LUT strategy for %s: move all users to new LUT\n", port.name.c_str(ctx));
+ auto pt = spliceLUT(ctx, packed.get(), port.name, false);
+ new_cells.push_back(std::move(pt));
+ }
+
+ // Find wire that will be driven by this port.
+ const auto pll_out_wire = ctx->getBelPinWire(pll_bel, ctx->portPinFromId(port.name));
+ NPNR_ASSERT(pll_out_wire.index != -1);
+
+ // Now, constrain all LUTs on the output of the signal to be at
+ // the correct Bel relative to the PLL Bel.
+ int x = ctx->chip_info->wire_data[pll_out_wire.index].x;
+ int y = ctx->chip_info->wire_data[pll_out_wire.index].y;
+ int z = 0;
+ for (const auto &user : port.net->users) {
+ NPNR_ASSERT(user.cell != nullptr);
+ NPNR_ASSERT(user.cell->type == ctx->id("ICESTORM_LC"));
+
+ // TODO(q3k): handle when the Bel might be already the
+ // target of another constraint.
+ NPNR_ASSERT(z < 8);
+ auto target_bel = ctx->getBelByLocation(Loc(x, y, z++));
+ auto target_bel_name = ctx->getBelName(target_bel).str(ctx);
+ user.cell->attrs[ctx->id("BEL")] = target_bel_name;
+ log_info(" constrained '%s' to %s\n", user.cell->name.c_str(ctx), target_bel_name.c_str());
+ }
+ }
+
+ new_cells.push_back(std::move(packed));
}
}
diff --git a/ice40/place_legaliser.cc b/ice40/place_legaliser.cc
index 2aefb839..ebc2b865 100644
--- a/ice40/place_legaliser.cc
+++ b/ice40/place_legaliser.cc
@@ -75,11 +75,9 @@ static void get_chain_midpoint(const Context *ctx, const CellChain &chain, float
for (auto cell : chain.cells) {
if (cell->bel == BelId())
continue;
- int bel_x, bel_y;
- bool bel_gb;
- ctx->estimatePosition(cell->bel, bel_x, bel_y, bel_gb);
- total_x += bel_x;
- total_y += bel_y;
+ Loc bel_loc = ctx->getBelLocation(cell->bel);
+ total_x += bel_loc.x;
+ total_y += bel_loc.y;
N++;
}
NPNR_ASSERT(N > 0);
diff --git a/ice40/portpins.inc b/ice40/portpins.inc
index d78625d1..f9dac887 100644
--- a/ice40/portpins.inc
+++ b/ice40/portpins.inc
@@ -118,6 +118,8 @@ X(DYNAMICDELAY_5)
X(DYNAMICDELAY_6)
X(DYNAMICDELAY_7)
X(LOCK)
+X(PLLOUT_A)
+X(PLLOUT_B)
X(BYPASS)
X(RESETB)
X(LATCHINPUTVALUE)