aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--common/command.cc4
-rw-r--r--common/placer1.cc272
-rw-r--r--common/timing_opt.cc2
-rw-r--r--ice40/arch.cc4
-rw-r--r--ice40/arch_place.cc36
-rw-r--r--ice40/pack.cc112
7 files changed, 377 insertions, 57 deletions
diff --git a/README.md b/README.md
index a9eaf393..b2144ce5 100644
--- a/README.md
+++ b/README.md
@@ -69,11 +69,11 @@ cmake -DARCH=ice40 -DICEBOX_ROOT=C:/ProgramData/icestorm/share/icebox -DCMAKE_TO
cmake --build . --config Release
```
-A simple example that runs on the iCEstick dev board can be found in `ice40/blinky.*`.
+A simple example that runs on the iCEstick dev board can be found in `ice40/examples/blinky/blinky.*`.
Usage example:
```
-cd ice40
+cd ice40/examples/blinky
yosys -p 'synth_ice40 -top blinky -json blinky.json' blinky.v # synthesize into blinky.json
nextpnr-ice40 --hx1k --json blinky.json --pcf blinky.pcf --asc blinky.asc # run place and route
icepack blinky.asc blinky.bin # generate binary bitstream file
diff --git a/common/command.cc b/common/command.cc
index 6f4137fe..49081e72 100644
--- a/common/command.cc
+++ b/common/command.cc
@@ -87,8 +87,8 @@ bool CommandHandler::executeBeforeContext()
if (vm.count("log")) {
std::string logfilename = vm["log"].as<std::string>();
- logfile = std::ofstream(logfilename);
- if (!logfile)
+ logfile.open(logfilename);
+ if (!logfile.is_open())
log_error("Failed to open log file '%s' for writing.\n", logfilename.c_str());
log_streams.push_back(std::make_pair(&logfile, LogLevel::LOG_MSG));
}
diff --git a/common/placer1.cc b/common/placer1.cc
index 98251627..a8ddd8a6 100644
--- a/common/placer1.cc
+++ b/common/placer1.cc
@@ -64,9 +64,10 @@ class SAPlacer
private:
struct BoundingBox
{
+ // Actual bounding box
int x0 = 0, x1 = 0, y0 = 0, y1 = 0;
- bool is_inside_inc(int x, int y) const { return x >= x0 && x <= x1 && y >= y0 && y <= y1; }
- bool touches_bounds(int x, int y) const { return x == x0 || x == x1 || y == y0 || y == y1; }
+ // Number of cells at each extremity
+ int nx0 = 0, nx1 = 0, ny0 = 0, ny1 = 0;
wirelen_t hpwl() const { return wirelen_t((x1 - x0) + (y1 - y0)); }
};
@@ -103,15 +104,12 @@ class SAPlacer
net_bounds.resize(ctx->nets.size());
net_arc_tcost.resize(ctx->nets.size());
- moveChange.already_bounds_changed.resize(ctx->nets.size());
- moveChange.already_changed_arcs.resize(ctx->nets.size());
old_udata.reserve(ctx->nets.size());
net_by_udata.reserve(ctx->nets.size());
decltype(NetInfo::udata) n = 0;
for (auto &net : ctx->nets) {
old_udata.emplace_back(net.second->udata);
net_arc_tcost.at(n).resize(net.second->users.size());
- moveChange.already_changed_arcs.at(n).resize(net.second->users.size());
net.second->udata = n++;
net_by_udata.push_back(net.second.get());
}
@@ -252,6 +250,7 @@ class SAPlacer
// Calculate costs after initial placement
setup_costs();
+ moveChange.init(this);
curr_wirelen_cost = total_wirelen_cost();
curr_timing_cost = total_timing_cost();
last_wirelen_cost = curr_wirelen_cost;
@@ -292,6 +291,24 @@ class SAPlacer
}
}
+ if (ctx->debug) {
+ // Verify correctness of incremental wirelen updates
+ for (size_t i = 0; i < net_bounds.size(); i++) {
+ auto net = net_by_udata[i];
+ if (ignore_net(net))
+ continue;
+ auto &incr = net_bounds.at(i), gold = get_net_bounds(net);
+ NPNR_ASSERT(incr.x0 == gold.x0);
+ NPNR_ASSERT(incr.x1 == gold.x1);
+ NPNR_ASSERT(incr.y0 == gold.y0);
+ NPNR_ASSERT(incr.y1 == gold.y1);
+ NPNR_ASSERT(incr.nx0 == gold.nx0);
+ NPNR_ASSERT(incr.nx1 == gold.nx1);
+ NPNR_ASSERT(incr.ny0 == gold.ny0);
+ NPNR_ASSERT(incr.ny1 == gold.ny1);
+ }
+ }
+
if (curr_wirelen_cost < min_wirelen) {
min_wirelen = curr_wirelen_cost;
improved = true;
@@ -366,6 +383,10 @@ class SAPlacer
get_criticalities(ctx, &net_crit);
// Need to rebuild costs after criticalities change
setup_costs();
+ // Reset incremental bounds
+ moveChange.reset(this);
+ moveChange.new_net_bounds = net_bounds;
+
// Recalculate total metric entirely to avoid rounding errors
// accumulating over time
curr_wirelen_cost = total_wirelen_cost();
@@ -475,7 +496,7 @@ class SAPlacer
bool try_swap_position(CellInfo *cell, BelId newBel)
{
static const double epsilon = 1e-20;
- moveChange.reset();
+ moveChange.reset(this);
if (!require_legal && is_constrained(cell))
return false;
BelId oldBel = cell->bel;
@@ -587,7 +608,7 @@ class SAPlacer
std::vector<std::pair<CellInfo *, BelId>> moves_made;
std::vector<std::pair<CellInfo *, BelId>> dest_bels;
double delta = 0;
- moveChange.reset();
+ moveChange.reset(this);
if (ctx->debug)
log_info("finding cells for chain swap %s\n", cell->name.c_str(ctx));
@@ -619,6 +640,10 @@ class SAPlacer
for (const auto &db : dest_bels) {
BelId oldBel = swap_cell_bels(db.first, db.second);
moves_made.emplace_back(std::make_pair(db.first, oldBel));
+ CellInfo *bound = ctx->getBoundBelCell(oldBel);
+ add_move_cell(moveChange, db.first, oldBel);
+ if (bound != nullptr)
+ add_move_cell(moveChange, bound, db.second);
}
for (const auto &mm : moves_made) {
if (!ctx->isBelLocationValid(mm.first->bel) || !check_cell_bel_region(mm.first, mm.first->bel))
@@ -628,9 +653,6 @@ class SAPlacer
CellInfo *bound = ctx->getBoundBelCell(mm.second);
if (bound && !check_cell_bel_region(bound, bound->bel))
goto swap_fail;
- add_move_cell(moveChange, mm.first, mm.second);
- if (bound != nullptr)
- add_move_cell(moveChange, bound, mm.first->bel);
}
compute_cost_changes(moveChange);
delta = lambda * (moveChange.timing_delta / last_timing_cost) +
@@ -717,15 +739,38 @@ class SAPlacer
bb.x1 = dloc.x;
bb.y0 = dloc.y;
bb.y1 = dloc.y;
-
+ bb.nx0 = 1;
+ bb.nx1 = 1;
+ bb.ny0 = 1;
+ bb.ny1 = 1;
for (auto user : net->users) {
if (user.cell->bel == BelId())
continue;
Loc uloc = ctx->getBelLocation(user.cell->bel);
- bb.x0 = std::min(bb.x0, uloc.x);
- bb.x1 = std::max(bb.x1, uloc.x);
- bb.y0 = std::min(bb.y0, uloc.y);
- bb.y1 = std::max(bb.y1, uloc.y);
+ if (bb.x0 == uloc.x)
+ ++bb.nx0;
+ else if (uloc.x < bb.x0) {
+ bb.x0 = uloc.x;
+ bb.nx0 = 1;
+ }
+ if (bb.x1 == uloc.x)
+ ++bb.nx1;
+ else if (uloc.x > bb.x1) {
+ bb.x1 = uloc.x;
+ bb.nx1 = 1;
+ }
+ if (bb.y0 == uloc.y)
+ ++bb.ny0;
+ else if (uloc.y < bb.y0) {
+ bb.y0 = uloc.y;
+ bb.ny0 = 1;
+ }
+ if (bb.y1 == uloc.y)
+ ++bb.ny1;
+ else if (uloc.y > bb.y1) {
+ bb.y1 = uloc.y;
+ bb.ny1 = 1;
+ }
}
return bb;
@@ -789,27 +834,53 @@ class SAPlacer
// Cost-change-related data for a move
struct MoveChangeData
{
- std::vector<decltype(NetInfo::udata)> bounds_changed_nets;
+
+ enum BoundChangeType
+ {
+ NO_CHANGE,
+ CELL_MOVED_INWARDS,
+ CELL_MOVED_OUTWARDS,
+ FULL_RECOMPUTE
+ };
+
+ std::vector<decltype(NetInfo::udata)> bounds_changed_nets_x, bounds_changed_nets_y;
std::vector<std::pair<decltype(NetInfo::udata), size_t>> changed_arcs;
- std::vector<bool> already_bounds_changed;
+ std::vector<BoundChangeType> already_bounds_changed_x, already_bounds_changed_y;
std::vector<std::vector<bool>> already_changed_arcs;
- std::vector<std::pair<decltype(NetInfo::udata), BoundingBox>> new_net_bounds;
+ std::vector<BoundingBox> new_net_bounds;
std::vector<std::pair<std::pair<decltype(NetInfo::udata), size_t>, double>> new_arc_costs;
wirelen_t wirelen_delta = 0;
double timing_delta = 0;
- void reset()
+ void init(SAPlacer *p)
+ {
+ already_bounds_changed_x.resize(p->ctx->nets.size());
+ already_bounds_changed_y.resize(p->ctx->nets.size());
+ already_changed_arcs.resize(p->ctx->nets.size());
+ for (auto &net : p->ctx->nets) {
+ already_changed_arcs.at(net.second->udata).resize(net.second->users.size());
+ }
+ new_net_bounds = p->net_bounds;
+ }
+
+ void reset(SAPlacer *p)
{
- for (auto bc : bounds_changed_nets)
- already_bounds_changed[bc] = false;
+ for (auto bc : bounds_changed_nets_x) {
+ new_net_bounds[bc] = p->net_bounds[bc];
+ already_bounds_changed_x[bc] = NO_CHANGE;
+ }
+ for (auto bc : bounds_changed_nets_y) {
+ new_net_bounds[bc] = p->net_bounds[bc];
+ already_bounds_changed_y[bc] = NO_CHANGE;
+ }
for (const auto &tc : changed_arcs)
already_changed_arcs[tc.first][tc.second] = false;
- bounds_changed_nets.clear();
+ bounds_changed_nets_x.clear();
+ bounds_changed_nets_y.clear();
changed_arcs.clear();
- new_net_bounds.clear();
new_arc_costs.clear();
wirelen_delta = 0;
timing_delta = 0;
@@ -828,14 +899,128 @@ class SAPlacer
continue;
if (ignore_net(pn))
continue;
- const BoundingBox &curr_bounds = net_bounds[pn->udata];
- // If the old location was at the edge of the bounds, or the new location exceeds the bounds,
- // an update is needed
- if (curr_bounds.touches_bounds(old_loc.x, old_loc.y) || !curr_bounds.is_inside_inc(curr_loc.x, curr_loc.y))
- if (!mc.already_bounds_changed[pn->udata]) {
- mc.bounds_changed_nets.push_back(pn->udata);
- mc.already_bounds_changed[pn->udata] = true;
+ BoundingBox &curr_bounds = mc.new_net_bounds[pn->udata];
+ // Incremental bounding box updates
+ // Note that everything other than full updates are applied immediately rather than being queued,
+ // so further updates to the same net in the same move are dealt with correctly.
+ // If a full update is already queued, this can be considered a no-op
+ if (mc.already_bounds_changed_x[pn->udata] != MoveChangeData::FULL_RECOMPUTE) {
+ // Bounds x0
+ if (curr_loc.x < curr_bounds.x0) {
+ // Further out than current bounds x0
+ curr_bounds.x0 = curr_loc.x;
+ curr_bounds.nx0 = 1;
+ if (mc.already_bounds_changed_x[pn->udata] == MoveChangeData::NO_CHANGE) {
+ // Checking already_bounds_changed_x ensures that each net is only added once
+ // to bounds_changed_nets, lest we add its HPWL change multiple times skewing the
+ // overall cost change
+ mc.already_bounds_changed_x[pn->udata] = MoveChangeData::CELL_MOVED_OUTWARDS;
+ mc.bounds_changed_nets_x.push_back(pn->udata);
+ }
+ } else if (curr_loc.x == curr_bounds.x0 && old_loc.x > curr_bounds.x0) {
+ curr_bounds.nx0++;
+ if (mc.already_bounds_changed_x[pn->udata] == MoveChangeData::NO_CHANGE) {
+ mc.already_bounds_changed_x[pn->udata] = MoveChangeData::CELL_MOVED_OUTWARDS;
+ mc.bounds_changed_nets_x.push_back(pn->udata);
+ }
+ } else if (old_loc.x == curr_bounds.x0 && curr_loc.x > curr_bounds.x0) {
+ if (mc.already_bounds_changed_x[pn->udata] == MoveChangeData::NO_CHANGE)
+ mc.bounds_changed_nets_x.push_back(pn->udata);
+ if (curr_bounds.nx0 == 1) {
+ mc.already_bounds_changed_x[pn->udata] = MoveChangeData::FULL_RECOMPUTE;
+ } else {
+ curr_bounds.nx0--;
+ if (mc.already_bounds_changed_x[pn->udata] == MoveChangeData::NO_CHANGE)
+ mc.already_bounds_changed_x[pn->udata] = MoveChangeData::CELL_MOVED_INWARDS;
+ }
}
+
+ // Bounds x1
+ if (curr_loc.x > curr_bounds.x1) {
+ // Further out than current bounds x1
+ curr_bounds.x1 = curr_loc.x;
+ curr_bounds.nx1 = 1;
+ if (mc.already_bounds_changed_x[pn->udata] == MoveChangeData::NO_CHANGE) {
+ // Checking already_bounds_changed_x ensures that each net is only added once
+ // to bounds_changed_nets, lest we add its HPWL change multiple times skewing the
+ // overall cost change
+ mc.already_bounds_changed_x[pn->udata] = MoveChangeData::CELL_MOVED_OUTWARDS;
+ mc.bounds_changed_nets_x.push_back(pn->udata);
+ }
+ } else if (curr_loc.x == curr_bounds.x1 && old_loc.x < curr_bounds.x1) {
+ curr_bounds.nx1++;
+ if (mc.already_bounds_changed_x[pn->udata] == MoveChangeData::NO_CHANGE) {
+ mc.already_bounds_changed_x[pn->udata] = MoveChangeData::CELL_MOVED_OUTWARDS;
+ mc.bounds_changed_nets_x.push_back(pn->udata);
+ }
+ } else if (old_loc.x == curr_bounds.x1 && curr_loc.x < curr_bounds.x1) {
+ if (mc.already_bounds_changed_x[pn->udata] == MoveChangeData::NO_CHANGE)
+ mc.bounds_changed_nets_x.push_back(pn->udata);
+ if (curr_bounds.nx1 == 1) {
+ mc.already_bounds_changed_x[pn->udata] = MoveChangeData::FULL_RECOMPUTE;
+ } else {
+ curr_bounds.nx1--;
+ if (mc.already_bounds_changed_x[pn->udata] == MoveChangeData::NO_CHANGE)
+ mc.already_bounds_changed_x[pn->udata] = MoveChangeData::CELL_MOVED_INWARDS;
+ }
+ }
+ }
+ if (mc.already_bounds_changed_y[pn->udata] != MoveChangeData::FULL_RECOMPUTE) {
+ // Bounds y0
+ if (curr_loc.y < curr_bounds.y0) {
+ // Further out than current bounds y0
+ curr_bounds.y0 = curr_loc.y;
+ curr_bounds.ny0 = 1;
+ if (mc.already_bounds_changed_y[pn->udata] == MoveChangeData::NO_CHANGE) {
+ mc.already_bounds_changed_y[pn->udata] = MoveChangeData::CELL_MOVED_OUTWARDS;
+ mc.bounds_changed_nets_y.push_back(pn->udata);
+ }
+ } else if (curr_loc.y == curr_bounds.y0 && old_loc.y > curr_bounds.y0) {
+ curr_bounds.ny0++;
+ if (mc.already_bounds_changed_y[pn->udata] == MoveChangeData::NO_CHANGE) {
+ mc.already_bounds_changed_y[pn->udata] = MoveChangeData::CELL_MOVED_OUTWARDS;
+ mc.bounds_changed_nets_y.push_back(pn->udata);
+ }
+ } else if (old_loc.y == curr_bounds.y0 && curr_loc.y > curr_bounds.y0) {
+ if (mc.already_bounds_changed_y[pn->udata] == MoveChangeData::NO_CHANGE)
+ mc.bounds_changed_nets_y.push_back(pn->udata);
+ if (curr_bounds.ny0 == 1) {
+ mc.already_bounds_changed_y[pn->udata] = MoveChangeData::FULL_RECOMPUTE;
+ } else {
+ curr_bounds.ny0--;
+ if (mc.already_bounds_changed_y[pn->udata] == MoveChangeData::NO_CHANGE)
+ mc.already_bounds_changed_y[pn->udata] = MoveChangeData::CELL_MOVED_INWARDS;
+ }
+ }
+
+ // Bounds y1
+ if (curr_loc.y > curr_bounds.y1) {
+ // Further out than current bounds y1
+ curr_bounds.y1 = curr_loc.y;
+ curr_bounds.ny1 = 1;
+ if (mc.already_bounds_changed_y[pn->udata] == MoveChangeData::NO_CHANGE) {
+ mc.already_bounds_changed_y[pn->udata] = MoveChangeData::CELL_MOVED_OUTWARDS;
+ mc.bounds_changed_nets_y.push_back(pn->udata);
+ }
+ } else if (curr_loc.y == curr_bounds.y1 && old_loc.y < curr_bounds.y1) {
+ curr_bounds.ny1++;
+ if (mc.already_bounds_changed_y[pn->udata] == MoveChangeData::NO_CHANGE) {
+ mc.already_bounds_changed_y[pn->udata] = MoveChangeData::CELL_MOVED_OUTWARDS;
+ mc.bounds_changed_nets_y.push_back(pn->udata);
+ }
+ } else if (old_loc.y == curr_bounds.y1 && curr_loc.y < curr_bounds.y1) {
+ if (mc.already_bounds_changed_y[pn->udata] == MoveChangeData::NO_CHANGE)
+ mc.bounds_changed_nets_y.push_back(pn->udata);
+ if (curr_bounds.ny1 == 1) {
+ mc.already_bounds_changed_y[pn->udata] = MoveChangeData::FULL_RECOMPUTE;
+ } else {
+ curr_bounds.ny1--;
+ if (mc.already_bounds_changed_y[pn->udata] == MoveChangeData::NO_CHANGE)
+ mc.already_bounds_changed_y[pn->udata] = MoveChangeData::CELL_MOVED_INWARDS;
+ }
+ }
+ }
+
if (ctx->timing_driven && int(pn->users.size()) < cfg.timingFanoutThresh) {
// Output ports - all arcs change timing
if (port.second.type == PORT_OUT) {
@@ -860,13 +1045,22 @@ class SAPlacer
void compute_cost_changes(MoveChangeData &md)
{
- for (const auto &bc : md.bounds_changed_nets) {
- wirelen_t old_hpwl = net_bounds.at(bc).hpwl();
- auto bounds = get_net_bounds(net_by_udata.at(bc));
- md.new_net_bounds.emplace_back(std::make_pair(bc, bounds));
- md.wirelen_delta += (bounds.hpwl() - old_hpwl);
- md.already_bounds_changed[bc] = false;
+ for (const auto &bc : md.bounds_changed_nets_x) {
+ if (md.already_bounds_changed_x[bc] == MoveChangeData::FULL_RECOMPUTE)
+ md.new_net_bounds[bc] = get_net_bounds(net_by_udata[bc]);
+ }
+ for (const auto &bc : md.bounds_changed_nets_y) {
+ if (md.already_bounds_changed_x[bc] != MoveChangeData::FULL_RECOMPUTE &&
+ md.already_bounds_changed_y[bc] == MoveChangeData::FULL_RECOMPUTE)
+ md.new_net_bounds[bc] = get_net_bounds(net_by_udata[bc]);
}
+
+ for (const auto &bc : md.bounds_changed_nets_x)
+ md.wirelen_delta += md.new_net_bounds[bc].hpwl() - net_bounds[bc].hpwl();
+ for (const auto &bc : md.bounds_changed_nets_y)
+ if (md.already_bounds_changed_x[bc] == MoveChangeData::NO_CHANGE)
+ md.wirelen_delta += md.new_net_bounds[bc].hpwl() - net_bounds[bc].hpwl();
+
if (ctx->timing_driven) {
for (const auto &tc : md.changed_arcs) {
double old_cost = net_arc_tcost.at(tc.first).at(tc.second);
@@ -880,8 +1074,10 @@ class SAPlacer
void commit_cost_changes(MoveChangeData &md)
{
- for (const auto &bc : md.new_net_bounds)
- net_bounds[bc.first] = bc.second;
+ for (const auto &bc : md.bounds_changed_nets_x)
+ net_bounds[bc] = md.new_net_bounds[bc];
+ for (const auto &bc : md.bounds_changed_nets_y)
+ net_bounds[bc] = md.new_net_bounds[bc];
for (const auto &tc : md.new_arc_costs)
net_arc_tcost[tc.first.first].at(tc.first.second) = tc.second;
curr_wirelen_cost += md.wirelen_delta;
diff --git a/common/timing_opt.cc b/common/timing_opt.cc
index c0968db7..898222ab 100644
--- a/common/timing_opt.cc
+++ b/common/timing_opt.cc
@@ -84,6 +84,7 @@ class TimingOptimiser
bool optimise()
{
log_info("Running timing-driven placement optimisation...\n");
+ ctx->lock();
if (ctx->verbose)
timing_analysis(ctx, false, true, false, false);
for (int i = 0; i < 30; i++) {
@@ -96,6 +97,7 @@ class TimingOptimiser
if (ctx->verbose)
timing_analysis(ctx, false, true, false, false);
}
+ ctx->unlock();
return true;
}
diff --git a/ice40/arch.cc b/ice40/arch.cc
index bfcadc0b..d536ad35 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -1221,8 +1221,8 @@ void Arch::assignCellInfo(CellInfo *cell)
} 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"));
- cell->ioInfo.pintype = int_or_default(cell->attrs, this->id("PIN_TYPE"));
- cell->ioInfo.negtrig = bool_or_default(cell->attrs, this->id("NEG_TRIGGER"));
+ cell->ioInfo.pintype = int_or_default(cell->params, this->id("PIN_TYPE"));
+ cell->ioInfo.negtrig = bool_or_default(cell->params, this->id("NEG_TRIGGER"));
} 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_place.cc b/ice40/arch_place.cc
index 90bb62b9..ede8d47f 100644
--- a/ice40/arch_place.cc
+++ b/ice40/arch_place.cc
@@ -91,6 +91,18 @@ bool Arch::isBelLocationValid(BelId bel) const
}
}
+static inline bool _io_pintype_need_clk_in(unsigned pin_type) { return (pin_type & 0x01) == 0x00; }
+
+static inline bool _io_pintype_need_clk_out(unsigned pin_type)
+{
+ return ((pin_type & 0x30) == 0x30) || ((pin_type & 0x3c) && ((pin_type & 0x0c) != 0x08));
+}
+
+static inline bool _io_pintype_need_clk_en(unsigned pin_type)
+{
+ return _io_pintype_need_clk_in(pin_type) || _io_pintype_need_clk_out(pin_type);
+}
+
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
{
if (cell->type == id_ICESTORM_LC) {
@@ -157,6 +169,30 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
CellInfo *compCell = getBoundBelCell(compBel);
if (compCell && compCell->ioInfo.lvds)
return false;
+
+ // Check for conflicts on shared nets
+ // - CLOCK_ENABLE
+ // - OUTPUT_CLK
+ // - INPUT_CLK
+ if (compCell) {
+ bool use[6] = {
+ _io_pintype_need_clk_in(cell->ioInfo.pintype),
+ _io_pintype_need_clk_in(compCell->ioInfo.pintype),
+ _io_pintype_need_clk_out(cell->ioInfo.pintype),
+ _io_pintype_need_clk_out(compCell->ioInfo.pintype),
+ _io_pintype_need_clk_en(cell->ioInfo.pintype),
+ _io_pintype_need_clk_en(compCell->ioInfo.pintype),
+ };
+ NetInfo *nets[] = {
+ cell->ports[id_INPUT_CLK].net, compCell->ports[id_INPUT_CLK].net,
+ cell->ports[id_OUTPUT_CLK].net, compCell->ports[id_OUTPUT_CLK].net,
+ cell->ports[id_CLOCK_ENABLE].net, compCell->ports[id_CLOCK_ENABLE].net,
+ };
+
+ for (int i = 0; i < 6; i++)
+ if (use[i] && (nets[i] != nets[i ^ 1]) && (use[i ^ 1] || (nets[i ^ 1] != nullptr)))
+ return false;
+ }
}
return getBelPackagePin(bel) != "";
diff --git a/ice40/pack.cc b/ice40/pack.cc
index b1efe5b2..2ba0bb5a 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -383,12 +383,9 @@ 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)
+static BelId find_padin_gbuf(Context *ctx, BelId bel, IdString port_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);
if (wire == WireId())
@@ -401,6 +398,15 @@ static std::unique_ptr<CellInfo> create_padin_gbuf(Context *ctx, CellInfo *cell,
}
}
+ return gb_bel;
+}
+
+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 bel = ctx->getBelByName(ctx->id(cell->attrs[ctx->id("BEL")]));
+ BelId gb_bel = find_padin_gbuf(ctx, bel, port_name);
NPNR_ASSERT(gb_bel != BelId());
// Create a SB_GB Cell and lock it there
@@ -502,6 +508,19 @@ static void pack_io(Context *ctx)
// Make it a normal SB_IO with global marker
ci->type = ctx->id("SB_IO");
ci->attrs[ctx->id("GLOBAL")] = "1";
+ } else if (is_sb_io(ctx, ci)) {
+ // Disconnect unused inputs
+ NetInfo *net_in0 = ci->ports.count(id_D_IN_0) ? ci->ports[id_D_IN_0].net : nullptr;
+ NetInfo *net_in1 = ci->ports.count(id_D_IN_1) ? ci->ports[id_D_IN_1].net : nullptr;
+
+ if (net_in0 != nullptr && net_in0->users.size() == 0) {
+ delete_nets.insert(net_in0->name);
+ ci->ports[id_D_IN_0].net = nullptr;
+ }
+ if (net_in1 != nullptr && net_in1->users.size() == 0) {
+ delete_nets.insert(net_in1->name);
+ ci->ports[id_D_IN_1].net = nullptr;
+ }
}
}
for (auto pcell : packed_cells) {
@@ -691,14 +710,15 @@ static void promote_globals(Context *ctx)
// Figure out where to place PLLs
static void place_plls(Context *ctx)
{
- std::map<BelId, std::pair<BelPin, BelPin>> pll_all_bels;
+ std::map<BelId, std::tuple<BelPin, BelId, BelPin, BelId>> pll_all_bels;
std::map<BelId, CellInfo *> pll_used_bels;
std::vector<CellInfo *> pll_cells;
std::map<BelId, CellInfo *> bel2io;
+ std::map<BelId, CellInfo *> bel2gb;
log_info("Placing PLLs..\n");
- // Find all the PLLs BELs and matching IO sites
+ // Find all the PLLs BELs and matching IO sites and global networks
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) != id_ICESTORM_PLL)
continue;
@@ -707,8 +727,10 @@ static void place_plls(Context *ctx)
auto io_a_pin = ctx->getIOBSharingPLLPin(bel, id_PLLOUT_A);
auto io_b_pin = ctx->getIOBSharingPLLPin(bel, id_PLLOUT_B);
+ auto gb_a = find_padin_gbuf(ctx, bel, id_PLLOUT_A_GLOBAL);
+ auto gb_b = find_padin_gbuf(ctx, bel, id_PLLOUT_B_GLOBAL);
- pll_all_bels[bel] = std::make_pair(io_a_pin, io_b_pin);
+ pll_all_bels[bel] = std::make_tuple(io_a_pin, gb_a, io_b_pin, gb_b);
}
// Find all the PLLs cells we need to place and do pre-checks
@@ -813,7 +835,8 @@ static void place_plls(Context *ctx)
for (auto placed_pll : pll_used_bels) {
BelPin pll_io_a, pll_io_b;
- std::tie(pll_io_a, pll_io_b) = pll_all_bels[placed_pll.first];
+ BelId gb_a, gb_b;
+ std::tie(pll_io_a, gb_a, pll_io_b, gb_b) = pll_all_bels[placed_pll.first];
if (io_bel == pll_io_a.bel) {
// All the PAD type PLL stuff already checked above,so only
// check for conflict with a user placed CORE PLL
@@ -831,6 +854,47 @@ static void place_plls(Context *ctx)
bel2io[io_bel] = io_ci;
}
+ // Scan all SB_GBs to check for conflicts with PLL BELs
+ for (auto gb_cell : sorted(ctx->cells)) {
+ CellInfo *gb_ci = gb_cell.second;
+ if (!is_gbuf(ctx, gb_ci))
+ continue;
+
+ // Only consider the bound ones
+ if (!gb_ci->attrs.count(ctx->id("BEL")))
+ continue;
+
+ // Check all placed PLL (either forced by user, or forced by PACKAGEPIN)
+ BelId gb_bel = ctx->getBelByName(ctx->id(gb_ci->attrs[ctx->id("BEL")]));
+
+ for (auto placed_pll : pll_used_bels) {
+ CellInfo *ci = placed_pll.second;
+
+ // Used global connections
+ bool gb_a_used = ci->ports.count(id_PLLOUT_A_GLOBAL) && (ci->ports[id_PLLOUT_A_GLOBAL].net != nullptr) &&
+ (ci->ports[id_PLLOUT_A_GLOBAL].net->users.size() > 0);
+ bool gb_b_used = is_sb_pll40_dual(ctx, ci) && ci->ports.count(id_PLLOUT_B_GLOBAL) &&
+ (ci->ports[id_PLLOUT_B_GLOBAL].net != nullptr) &&
+ (ci->ports[id_PLLOUT_B_GLOBAL].net->users.size() > 0);
+
+ // Check for conflict
+ BelPin pll_io_a, pll_io_b;
+ BelId gb_a, gb_b;
+ std::tie(pll_io_a, gb_a, pll_io_b, gb_b) = pll_all_bels[placed_pll.first];
+ if (gb_a_used && (gb_bel == gb_a)) {
+ log_error("PLL '%s' A output conflict with SB_GB '%s'\n", placed_pll.second->name.c_str(ctx),
+ gb_cell.second->name.c_str(ctx));
+ }
+ if (gb_b_used && (gb_bel == gb_b)) {
+ log_error("PLL '%s' B output conflicts with SB_GB '%s'\n", placed_pll.second->name.c_str(ctx),
+ gb_cell.second->name.c_str(ctx));
+ }
+ }
+
+ // Save for later checks
+ bel2gb[gb_bel] = gb_ci;
+ }
+
// Scan all the CORE PLLs and place them in remaining available PLL BELs
// (in two pass ... first do the dual ones, harder to place, then single port)
for (int i = 0; i < 2; i++) {
@@ -849,6 +913,13 @@ static void place_plls(Context *ctx)
log_error("PLL '%s' is of CORE type but doesn't have a valid REFERENCECLK connection\n",
ci->name.c_str(ctx));
+ // Used global connections
+ bool gb_a_used = ci->ports.count(id_PLLOUT_A_GLOBAL) && (ci->ports[id_PLLOUT_A_GLOBAL].net != nullptr) &&
+ (ci->ports[id_PLLOUT_A_GLOBAL].net->users.size() > 0);
+ bool gb_b_used = is_sb_pll40_dual(ctx, ci) && ci->ports.count(id_PLLOUT_B_GLOBAL) &&
+ (ci->ports[id_PLLOUT_B_GLOBAL].net != nullptr) &&
+ (ci->ports[id_PLLOUT_B_GLOBAL].net->users.size() > 0);
+
// Could this be a PAD PLL ?
bool could_be_pad = false;
BelId pad_bel;
@@ -861,7 +932,8 @@ static void place_plls(Context *ctx)
if (pll_used_bels.count(bel_pll.first))
continue;
BelPin pll_io_a, pll_io_b;
- std::tie(pll_io_a, pll_io_b) = bel_pll.second;
+ BelId gb_a, gb_b;
+ std::tie(pll_io_a, gb_a, pll_io_b, gb_b) = bel_pll.second;
if (bel2io.count(pll_io_a.bel)) {
if (pll_io_a.bel == pad_bel)
could_be_pad = !bel2io.count(pll_io_b.bel) || !is_sb_pll40_dual(ctx, ci);
@@ -869,6 +941,10 @@ static void place_plls(Context *ctx)
}
if (bel2io.count(pll_io_b.bel) && is_sb_pll40_dual(ctx, ci))
continue;
+ if (gb_a_used && bel2gb.count(gb_a))
+ continue;
+ if (gb_b_used && bel2gb.count(gb_b))
+ continue;
found_bel = bel_pll.first;
break;
}
@@ -1278,10 +1354,20 @@ static void pack_special(Context *ctx)
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));
+ // Only if there is actually a net ...
+ if (pi.net != nullptr) {
+ // ... and it's used
+ if (pi.net->users.size() > 0) {
+ 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));
+ } else {
+ // If not, remove it to avoid routing issues
+ ctx->nets.erase(pi.net->name);
+ packed->ports[pi.name].net = nullptr;
+ }
+ }
}
new_cells.push_back(std::move(packed));