Merge pull request #585 from YosysHQ/gatecat/remove-ivbfc
Remove isValidBelForCell
This commit is contained in:
commit
a77ceec5cf
@ -1169,7 +1169,6 @@ template <typename R> struct ArchAPI : BaseCtx
|
||||
virtual BelBucketId getBelBucketByName(IdString name) const = 0;
|
||||
virtual BelBucketId getBelBucketForBel(BelId bel) const = 0;
|
||||
virtual BelBucketId getBelBucketForCellType(IdString cell_type) const = 0;
|
||||
virtual bool isValidBelForCell(CellInfo *cell, BelId bel) const = 0;
|
||||
virtual bool isBelLocationValid(BelId bel) const = 0;
|
||||
virtual typename R::CellTypeRangeT getCellTypes() const = 0;
|
||||
virtual typename R::BelBucketRangeT getBelBuckets() const = 0;
|
||||
@ -1420,7 +1419,6 @@ template <typename R> struct BaseArch : ArchAPI<R>
|
||||
{
|
||||
return getBelBucketByName(cell_type);
|
||||
};
|
||||
virtual bool isValidBelForCell(CellInfo *cell, BelId bel) const override { return true; }
|
||||
virtual bool isBelLocationValid(BelId bel) const override { return true; }
|
||||
virtual typename R::CellTypeRangeT getCellTypes() const override
|
||||
{
|
||||
|
@ -118,8 +118,7 @@ bool place_single_cell(Context *ctx, CellInfo *cell, bool require_legality)
|
||||
}
|
||||
IdString targetType = cell->type;
|
||||
for (auto bel : ctx->getBels()) {
|
||||
if (ctx->isValidBelForCellType(targetType, bel) &&
|
||||
(!require_legality || ctx->isValidBelForCell(cell, bel))) {
|
||||
if (ctx->isValidBelForCellType(targetType, bel)) {
|
||||
if (ctx->checkBelAvail(bel)) {
|
||||
wirelen_t wirelen = get_cell_metric_at_bel(ctx, cell, bel, MetricType::COST);
|
||||
if (iters >= 4)
|
||||
@ -155,12 +154,20 @@ bool place_single_cell(Context *ctx, CellInfo *cell, bool require_legality)
|
||||
ctx->unbindBel(ripup_target->bel);
|
||||
best_bel = ripup_bel;
|
||||
} else {
|
||||
ripup_target = nullptr;
|
||||
all_placed = true;
|
||||
}
|
||||
ctx->bindBel(best_bel, cell, STRENGTH_WEAK);
|
||||
if (require_legality && !ctx->isBelLocationValid(best_bel)) {
|
||||
ctx->unbindBel(best_bel);
|
||||
if (ripup_target != nullptr) {
|
||||
ctx->bindBel(best_bel, ripup_target, STRENGTH_WEAK);
|
||||
}
|
||||
all_placed = false;
|
||||
continue;
|
||||
}
|
||||
if (ctx->verbose)
|
||||
log_info(" placed single cell '%s' at '%s'\n", cell->name.c_str(ctx), ctx->nameOfBel(best_bel));
|
||||
ctx->bindBel(best_bel, cell, STRENGTH_WEAK);
|
||||
|
||||
cell = ripup_target;
|
||||
}
|
||||
return true;
|
||||
@ -387,7 +394,7 @@ class ConstraintLegaliseWorker
|
||||
for (auto bel : ctx->getBelsByTile(cp.second.x, cp.second.y)) {
|
||||
CellInfo *belCell = ctx->getBoundBelCell(bel);
|
||||
if (belCell != nullptr && !solution.count(belCell->name)) {
|
||||
if (!ctx->isValidBelForCell(belCell, bel)) {
|
||||
if (!ctx->isBelLocationValid(bel)) {
|
||||
NPNR_ASSERT(belCell->belStrength < STRENGTH_STRONG);
|
||||
ctx->unbindBel(bel);
|
||||
rippedCells.insert(belCell->name);
|
||||
|
@ -167,13 +167,6 @@ class SAPlacer
|
||||
"\'%s\' of type \'%s\'\n",
|
||||
loc_name.c_str(), bel_type.c_str(ctx), cell->name.c_str(ctx), cell->type.c_str(ctx));
|
||||
}
|
||||
if (!ctx->isValidBelForCell(cell, bel)) {
|
||||
IdString bel_type = ctx->getBelType(bel);
|
||||
log_error("Bel \'%s\' of type \'%s\' is not valid for cell "
|
||||
"\'%s\' of type \'%s\'\n",
|
||||
loc_name.c_str(), bel_type.c_str(ctx), cell->name.c_str(ctx), cell->type.c_str(ctx));
|
||||
}
|
||||
|
||||
auto bound_cell = ctx->getBoundBelCell(bel);
|
||||
if (bound_cell) {
|
||||
log_error(
|
||||
@ -182,6 +175,12 @@ class SAPlacer
|
||||
}
|
||||
|
||||
ctx->bindBel(bel, cell, STRENGTH_USER);
|
||||
if (!ctx->isBelLocationValid(bel)) {
|
||||
IdString bel_type = ctx->getBelType(bel);
|
||||
log_error("Bel \'%s\' of type \'%s\' is not valid for cell "
|
||||
"\'%s\' of type \'%s\'\n",
|
||||
loc_name.c_str(), bel_type.c_str(ctx), cell->name.c_str(ctx), cell->type.c_str(ctx));
|
||||
}
|
||||
locked_bels.insert(bel);
|
||||
placed_cells++;
|
||||
}
|
||||
@ -444,7 +443,7 @@ class SAPlacer
|
||||
IdString targetType = cell->type;
|
||||
|
||||
auto proc_bel = [&](BelId bel) {
|
||||
if (ctx->isValidBelForCellType(targetType, bel) && ctx->isValidBelForCell(cell, bel)) {
|
||||
if (ctx->isValidBelForCellType(targetType, bel)) {
|
||||
if (ctx->checkBelAvail(bel)) {
|
||||
uint64_t score = ctx->rng64();
|
||||
if (score <= best_score) {
|
||||
@ -480,10 +479,20 @@ class SAPlacer
|
||||
ctx->unbindBel(ripup_target->bel);
|
||||
best_bel = ripup_bel;
|
||||
} else {
|
||||
ripup_target = nullptr;
|
||||
all_placed = true;
|
||||
}
|
||||
ctx->bindBel(best_bel, cell, STRENGTH_WEAK);
|
||||
|
||||
if (!ctx->isBelLocationValid(best_bel)) {
|
||||
ctx->unbindBel(best_bel);
|
||||
if (ripup_target != nullptr) {
|
||||
ctx->bindBel(best_bel, ripup_target, STRENGTH_WEAK);
|
||||
}
|
||||
all_placed = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Back annotate location
|
||||
cell->attrs[ctx->id("BEL")] = ctx->getBelName(cell->bel).str(ctx);
|
||||
cell = ripup_target;
|
||||
|
@ -392,13 +392,6 @@ class HeAPPlacer
|
||||
"\'%s\' of type \'%s\'\n",
|
||||
loc_name.c_str(), bel_type.c_str(ctx), cell->name.c_str(ctx), cell->type.c_str(ctx));
|
||||
}
|
||||
if (!ctx->isValidBelForCell(cell, bel)) {
|
||||
IdString bel_type = ctx->getBelType(bel);
|
||||
log_error("Bel \'%s\' of type \'%s\' is not valid for cell "
|
||||
"\'%s\' of type \'%s\'\n",
|
||||
loc_name.c_str(), bel_type.c_str(ctx), cell->name.c_str(ctx), cell->type.c_str(ctx));
|
||||
}
|
||||
|
||||
auto bound_cell = ctx->getBoundBelCell(bel);
|
||||
if (bound_cell) {
|
||||
log_error("Cell \'%s\' cannot be bound to bel \'%s\' since it is already bound to cell \'%s\'\n",
|
||||
@ -406,6 +399,12 @@ class HeAPPlacer
|
||||
}
|
||||
|
||||
ctx->bindBel(bel, cell, STRENGTH_USER);
|
||||
if (!ctx->isBelLocationValid(bel)) {
|
||||
IdString bel_type = ctx->getBelType(bel);
|
||||
log_error("Bel \'%s\' of type \'%s\' is not valid for cell "
|
||||
"\'%s\' of type \'%s\'\n",
|
||||
loc_name.c_str(), bel_type.c_str(ctx), cell->name.c_str(ctx), cell->type.c_str(ctx));
|
||||
}
|
||||
placed_cells++;
|
||||
}
|
||||
}
|
||||
@ -571,12 +570,13 @@ class HeAPPlacer
|
||||
place_cells.push_back(ci);
|
||||
placed = true;
|
||||
} else {
|
||||
if (ctx->isValidBelForCell(ci, bel)) {
|
||||
ctx->bindBel(bel, ci, STRENGTH_STRONG);
|
||||
ctx->bindBel(bel, ci, STRENGTH_STRONG);
|
||||
if (ctx->isBelLocationValid(bel)) {
|
||||
cell_locs[cell.first].locked = true;
|
||||
placed = true;
|
||||
bels_used.insert(bel);
|
||||
} else {
|
||||
ctx->unbindBel(bel);
|
||||
available_bels.at(ci->type).push_front(bel);
|
||||
}
|
||||
}
|
||||
|
@ -652,8 +652,8 @@ strict legality enforcement. It is not required that all bels within a bucket
|
||||
are strictly equivelant.
|
||||
|
||||
Strict legality step will enforce those differences, along with additional
|
||||
local constraints. `isValidBelForCell`, `isValidBelForCellType`, and
|
||||
`isBelLocationValid` are used to enforce strict legality checks.
|
||||
local constraints. `isValidBelForCellType`, and `isBelLocationValid` are used
|
||||
to enforce strict legality checks.
|
||||
|
||||
### BelBucketRangeT getBelBuckets() const
|
||||
|
||||
@ -702,18 +702,10 @@ return the same value regardless if other cells are placed within the fabric.
|
||||
|
||||
*BaseArch default: returns `cell_type == getBelType(bel)`*
|
||||
|
||||
### bool isValidBelForCell(CellInfo \*cell, BelId bel) const
|
||||
|
||||
Returns true if the given cell can be bound to the given bel, considering
|
||||
other bound resources. For example, this can be used if there is only
|
||||
a certain number of different clock signals allowed for a group of bels.
|
||||
|
||||
*BaseArch default: returns true*
|
||||
|
||||
### bool isBelLocationValid(BelId bel) const
|
||||
|
||||
Returns true if a bell in the current configuration is valid, i.e. if
|
||||
`isValidBelForCell()` would return true for the current mapping.
|
||||
Returns true if a bel in the current configuration is legal (for example,
|
||||
a flipflop's clock signal is correctly shared with all bels in a slice.)
|
||||
|
||||
*BaseArch default: returns true*
|
||||
|
||||
|
@ -75,7 +75,7 @@ The job of the placer in nextpnr is to find a suitable bel for each cell in the
|
||||
|
||||
Placers might want to create their own indices of bels (for example, bels by type and location) to speed up the search.
|
||||
|
||||
As nextpnr allows arbitrary constraints on bels for more advanced packer-free flows and complex real-world architectures; placements must be checked for legality using `isValidBelForCell` (before placement) or `isBelLocationValid` (after placement) and the placement rejected if invalid. For analytical placement algorithms; after creating a spread-out AP solution the legality of placing each cell needs to be checked. In practice, the cost of this is fairly low as the architecture should ensure these functions are as fast as possible.
|
||||
As nextpnr allows arbitrary constraints on bels for more advanced packer-free flows and complex real-world architectures; placements must be checked for legality using `isBelLocationValid` (after placement) and the placement rejected if invalid. For analytical placement algorithms; after creating a spread-out AP solution the legality of placing each cell needs to be checked. In practice, the cost of this is fairly low as the architecture should ensure these functions are as fast as possible.
|
||||
|
||||
There are several routes for timing information in the placer:
|
||||
- sink `PortRef`s have a `budget` value annotated by calling `assign_budget` which is an estimate of the maximum delay that an arc may have
|
||||
|
@ -864,7 +864,6 @@ struct Arch : BaseArch<ArchRanges>
|
||||
|
||||
// -------------------------------------------------
|
||||
// Placement validity checks
|
||||
bool isValidBelForCell(CellInfo *cell, BelId bel) const override;
|
||||
bool isBelLocationValid(BelId bel) const override;
|
||||
|
||||
// Helper function for above
|
||||
|
@ -82,38 +82,14 @@ bool Arch::isBelLocationValid(BelId bel) const
|
||||
return slices_compatible(bel_cells);
|
||||
} else {
|
||||
CellInfo *cell = getBoundBelCell(bel);
|
||||
if (cell == nullptr)
|
||||
if (cell == nullptr) {
|
||||
return true;
|
||||
} else if (cell->type == id_DCUA || cell->type == id_EXTREFB || cell->type == id_PCSCLKDIV) {
|
||||
return args.type != ArchArgs::LFE5U_25F && args.type != ArchArgs::LFE5U_45F &&
|
||||
args.type != ArchArgs::LFE5U_85F;
|
||||
} else {
|
||||
return true;
|
||||
else
|
||||
return isValidBelForCell(cell, bel);
|
||||
}
|
||||
}
|
||||
|
||||
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
||||
{
|
||||
if (cell->type == id_TRELLIS_SLICE) {
|
||||
NPNR_ASSERT(getBelType(bel) == id_TRELLIS_SLICE);
|
||||
|
||||
std::vector<const CellInfo *> bel_cells;
|
||||
Loc bel_loc = getBelLocation(bel);
|
||||
|
||||
if (cell->sliceInfo.has_l6mux && ((bel_loc.z % 2) == 1))
|
||||
return false;
|
||||
|
||||
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
|
||||
CellInfo *cell_other = getBoundBelCell(bel_other);
|
||||
if (cell_other != nullptr && bel_other != bel) {
|
||||
bel_cells.push_back(cell_other);
|
||||
}
|
||||
}
|
||||
|
||||
bel_cells.push_back(cell);
|
||||
return slices_compatible(bel_cells);
|
||||
} else if (cell->type == id_DCUA || cell->type == id_EXTREFB || cell->type == id_PCSCLKDIV) {
|
||||
return args.type != ArchArgs::LFE5U_25F && args.type != ArchArgs::LFE5U_45F && args.type != ArchArgs::LFE5U_85F;
|
||||
} else {
|
||||
// other checks
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,9 +44,6 @@ void arch_wrap_python(py::module &m)
|
||||
.def("place", &Context::place)
|
||||
.def("route", &Context::route);
|
||||
|
||||
fn_wrapper_2a<Context, decltype(&Context::isValidBelForCell), &Context::isValidBelForCell, pass_through<bool>,
|
||||
addr_and_unwrap<CellInfo>, conv_from_str<BelId>>::def_wrap(ctx_cls, "isValidBelForCell");
|
||||
|
||||
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
|
||||
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
|
||||
typedef std::unordered_map<IdString, IdString> AliasMap;
|
||||
|
@ -403,33 +403,35 @@ class Ecp5GlobalRouter
|
||||
bool dedicated_routing = false;
|
||||
for (auto bel : ctx->getBels()) {
|
||||
if (ctx->getBelType(bel) == id_DCCA && ctx->checkBelAvail(bel)) {
|
||||
if (ctx->isValidBelForCell(dcc, bel)) {
|
||||
std::string belname = ctx->loc_info(bel)->bel_data[bel.index].name.get();
|
||||
if (belname.at(0) == 'D' && using_ce)
|
||||
continue; // don't allow DCCs with CE at center
|
||||
ctx->bindBel(bel, dcc, STRENGTH_LOCKED);
|
||||
wirelen_t wirelen = get_dcc_wirelen(dcc, dedicated_routing);
|
||||
if (wirelen < best_wirelen) {
|
||||
if (dedicated_routing) {
|
||||
best_bel_pclkcib = WireId();
|
||||
} else {
|
||||
bool found_pclkcib = false;
|
||||
for (WireId pclkcib : get_candidate_pclkcibs(bel)) {
|
||||
if (used_pclkcib.count(pclkcib))
|
||||
continue;
|
||||
found_pclkcib = true;
|
||||
best_bel_pclkcib = pclkcib;
|
||||
break;
|
||||
}
|
||||
if (!found_pclkcib)
|
||||
goto pclkcib_fail;
|
||||
}
|
||||
best_bel = bel;
|
||||
best_wirelen = wirelen;
|
||||
}
|
||||
pclkcib_fail:
|
||||
std::string belname = ctx->loc_info(bel)->bel_data[bel.index].name.get();
|
||||
if (belname.at(0) == 'D' && using_ce)
|
||||
continue; // don't allow DCCs with CE at center
|
||||
ctx->bindBel(bel, dcc, STRENGTH_LOCKED);
|
||||
if (!ctx->isBelLocationValid(bel)) {
|
||||
ctx->unbindBel(bel);
|
||||
continue;
|
||||
}
|
||||
wirelen_t wirelen = get_dcc_wirelen(dcc, dedicated_routing);
|
||||
if (wirelen < best_wirelen) {
|
||||
if (dedicated_routing) {
|
||||
best_bel_pclkcib = WireId();
|
||||
} else {
|
||||
bool found_pclkcib = false;
|
||||
for (WireId pclkcib : get_candidate_pclkcibs(bel)) {
|
||||
if (used_pclkcib.count(pclkcib))
|
||||
continue;
|
||||
found_pclkcib = true;
|
||||
best_bel_pclkcib = pclkcib;
|
||||
break;
|
||||
}
|
||||
if (!found_pclkcib)
|
||||
goto pclkcib_fail;
|
||||
}
|
||||
best_bel = bel;
|
||||
best_wirelen = wirelen;
|
||||
}
|
||||
pclkcib_fail:
|
||||
ctx->unbindBel(bel);
|
||||
}
|
||||
}
|
||||
NPNR_ASSERT(best_bel != BelId());
|
||||
|
@ -1280,17 +1280,6 @@ struct Arch : ArchAPI<ArchRanges>
|
||||
return bel_info(chip_info, bel).valid_cells[get_cell_type_index(cell_type)];
|
||||
}
|
||||
|
||||
// Whether or not a given cell can be placed at a given Bel
|
||||
// This is not intended for Bel type checks, but finer-grained constraints
|
||||
// such as conflicting set/reset signals, etc
|
||||
bool isValidBelForCell(CellInfo *cell, BelId bel) const override
|
||||
{
|
||||
NPNR_ASSERT(isValidBelForCellType(cell->type, bel));
|
||||
|
||||
// FIXME: Implement this
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return true whether all Bels at a given location are valid
|
||||
bool isBelLocationValid(BelId bel) const override
|
||||
{
|
||||
|
@ -44,9 +44,6 @@ void arch_wrap_python(py::module &m)
|
||||
.def("place", &Context::place)
|
||||
.def("route", &Context::route);
|
||||
|
||||
fn_wrapper_2a<Context, decltype(&Context::isValidBelForCell), &Context::isValidBelForCell, pass_through<bool>,
|
||||
addr_and_unwrap<CellInfo>, conv_from_str<BelId>>::def_wrap(ctx_cls, "isValidBelForCell");
|
||||
|
||||
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
|
||||
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
|
||||
typedef std::unordered_map<IdString, IdString> AliasMap;
|
||||
|
@ -650,21 +650,6 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
|
||||
return tmg.clockingInfo.at(port).at(index);
|
||||
}
|
||||
|
||||
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
||||
{
|
||||
std::vector<const CellInfo *> cells;
|
||||
cells.push_back(cell);
|
||||
Loc loc = getBelLocation(bel);
|
||||
for (auto tbel : getBelsByTile(loc.x, loc.y)) {
|
||||
if (tbel == bel)
|
||||
continue;
|
||||
CellInfo *bound = getBoundBelCell(tbel);
|
||||
if (bound != nullptr)
|
||||
cells.push_back(bound);
|
||||
}
|
||||
return cellsCompatible(cells.data(), int(cells.size()));
|
||||
}
|
||||
|
||||
bool Arch::isBelLocationValid(BelId bel) const
|
||||
{
|
||||
std::vector<const CellInfo *> cells;
|
||||
|
@ -357,7 +357,6 @@ struct Arch : ArchAPI<ArchRanges>
|
||||
TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const override;
|
||||
|
||||
bool isValidBelForCellType(IdString cell_type, BelId bel) const override { return cell_type == getBelType(bel); }
|
||||
bool isValidBelForCell(CellInfo *cell, BelId bel) const override;
|
||||
bool isBelLocationValid(BelId bel) const override;
|
||||
|
||||
static const std::string defaultPlacer;
|
||||
|
@ -1092,21 +1092,6 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
|
||||
return tmg.clockingInfo.at(port).at(index);
|
||||
}
|
||||
|
||||
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
||||
{
|
||||
std::vector<const CellInfo *> cells;
|
||||
cells.push_back(cell);
|
||||
Loc loc = getBelLocation(bel);
|
||||
for (auto tbel : getBelsByTile(loc.x, loc.y)) {
|
||||
if (tbel == bel)
|
||||
continue;
|
||||
CellInfo *bound = getBoundBelCell(tbel);
|
||||
if (bound != nullptr)
|
||||
cells.push_back(bound);
|
||||
}
|
||||
return cellsCompatible(cells.data(), int(cells.size()));
|
||||
}
|
||||
|
||||
bool Arch::isBelLocationValid(BelId bel) const
|
||||
{
|
||||
std::vector<const CellInfo *> cells;
|
||||
|
@ -449,7 +449,6 @@ struct Arch : BaseArch<ArchRanges>
|
||||
// Get the TimingClockingInfo of a port
|
||||
TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
|
||||
|
||||
bool isValidBelForCell(CellInfo *cell, BelId bel) const;
|
||||
bool isBelLocationValid(BelId bel) const;
|
||||
|
||||
static const std::string defaultPlacer;
|
||||
|
@ -834,11 +834,6 @@ struct Arch : BaseArch<ArchRanges>
|
||||
// Perform placement validity checks, returning false on failure (all
|
||||
// implemented in arch_place.cc)
|
||||
|
||||
// Whether or not a given cell can be placed at a given Bel
|
||||
// This is not intended for Bel type checks, but finer-grained constraints
|
||||
// such as conflicting set/reset signals, etc
|
||||
bool isValidBelForCell(CellInfo *cell, BelId bel) const override;
|
||||
|
||||
// Return true whether all Bels at a given location are valid
|
||||
bool isBelLocationValid(BelId bel) const override;
|
||||
|
||||
|
@ -70,6 +70,18 @@ bool Arch::logic_cells_compatible(const CellInfo **it, const size_t size) const
|
||||
return locals_count <= 32;
|
||||
}
|
||||
|
||||
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::isBelLocationValid(BelId bel) const
|
||||
{
|
||||
if (getBelType(bel) == id_ICESTORM_LC) {
|
||||
@ -83,136 +95,103 @@ bool Arch::isBelLocationValid(BelId bel) const
|
||||
}
|
||||
return logic_cells_compatible(bel_cells.data(), num_cells);
|
||||
} else {
|
||||
CellInfo *ci = getBoundBelCell(bel);
|
||||
if (ci == nullptr)
|
||||
CellInfo *cell = getBoundBelCell(bel);
|
||||
if (cell == nullptr)
|
||||
return true;
|
||||
else
|
||||
return isValidBelForCell(ci, bel);
|
||||
}
|
||||
}
|
||||
else if (cell->type == id_SB_IO) {
|
||||
// Do not allow placement of input SB_IOs on blocks where there a PLL is outputting to.
|
||||
|
||||
static inline bool _io_pintype_need_clk_in(unsigned pin_type) { return (pin_type & 0x01) == 0x00; }
|
||||
// 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);
|
||||
for (auto pin : getWireBelPins(wire)) {
|
||||
if (pin.pin == id_PLLOUT_A || pin.pin == id_PLLOUT_B) {
|
||||
// Is there a PLL there ?
|
||||
auto pll_cell = getBoundBelCell(pin.bel);
|
||||
if (pll_cell == nullptr)
|
||||
break;
|
||||
|
||||
static inline bool _io_pintype_need_clk_out(unsigned pin_type)
|
||||
{
|
||||
return ((pin_type & 0x30) == 0x30) || ((pin_type & 0x3c) && ((pin_type & 0x0c) != 0x08));
|
||||
}
|
||||
// Is that port actually used ?
|
||||
if ((pin.pin == id_PLLOUT_B) && !is_sb_pll40_dual(this, pll_cell))
|
||||
break;
|
||||
|
||||
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);
|
||||
}
|
||||
// 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;
|
||||
|
||||
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
||||
{
|
||||
if (cell->type == id_ICESTORM_LC) {
|
||||
NPNR_ASSERT(getBelType(bel) == id_ICESTORM_LC);
|
||||
// Are we perhaps a PAD INPUT Bel that can be placed here?
|
||||
if (pll_cell->attrs[id("BEL_PAD_INPUT")] == getBelName(bel).str(getCtx()))
|
||||
return true;
|
||||
|
||||
std::array<const CellInfo *, 8> bel_cells;
|
||||
size_t num_cells = 0;
|
||||
|
||||
Loc bel_loc = getBelLocation(bel);
|
||||
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
|
||||
CellInfo *ci_other = getBoundBelCell(bel_other);
|
||||
if (ci_other != nullptr && bel_other != bel)
|
||||
bel_cells[num_cells++] = ci_other;
|
||||
}
|
||||
|
||||
bel_cells[num_cells++] = cell;
|
||||
return logic_cells_compatible(bel_cells.data(), num_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, id_D_IN_0);
|
||||
for (auto pin : getWireBelPins(wire)) {
|
||||
if (pin.pin == id_PLLOUT_A || pin.pin == id_PLLOUT_B) {
|
||||
// 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(getCtx()))
|
||||
return true;
|
||||
|
||||
// Conflict
|
||||
return false;
|
||||
// Conflict
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loc ioLoc = getBelLocation(bel);
|
||||
Loc compLoc = ioLoc;
|
||||
compLoc.z = 1 - compLoc.z;
|
||||
Loc ioLoc = getBelLocation(bel);
|
||||
Loc compLoc = ioLoc;
|
||||
compLoc.z = 1 - compLoc.z;
|
||||
|
||||
// Check LVDS pairing
|
||||
if (cell->ioInfo.lvds) {
|
||||
// Check correct z and complement location is free
|
||||
if (ioLoc.z != 0)
|
||||
return false;
|
||||
BelId compBel = getBelByLocation(compLoc);
|
||||
CellInfo *compCell = getBoundBelCell(compBel);
|
||||
if (compCell)
|
||||
// Check LVDS pairing
|
||||
if (cell->ioInfo.lvds) {
|
||||
// Check correct z and complement location is free
|
||||
if (ioLoc.z != 0)
|
||||
return false;
|
||||
BelId compBel = getBelByLocation(compLoc);
|
||||
CellInfo *compCell = getBoundBelCell(compBel);
|
||||
if (compCell)
|
||||
return false;
|
||||
} else {
|
||||
// Check LVDS IO is not placed at complement location
|
||||
BelId compBel = getBelByLocation(compLoc);
|
||||
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 get_bel_package_pin(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;
|
||||
int glb_id = get_driven_glb_netwk(bel);
|
||||
if (net->is_reset && net->is_enable)
|
||||
return false;
|
||||
else if (net->is_reset)
|
||||
return (glb_id % 2) == 0;
|
||||
else if (net->is_enable)
|
||||
return (glb_id % 2) == 1;
|
||||
else
|
||||
return true;
|
||||
} else {
|
||||
// Check LVDS IO is not placed at complement location
|
||||
BelId compBel = getBelByLocation(compLoc);
|
||||
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;
|
||||
}
|
||||
// TODO: IO cell clock checks
|
||||
return true;
|
||||
}
|
||||
|
||||
return get_bel_package_pin(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;
|
||||
int glb_id = get_driven_glb_netwk(bel);
|
||||
if (net->is_reset && net->is_enable)
|
||||
return false;
|
||||
else if (net->is_reset)
|
||||
return (glb_id % 2) == 0;
|
||||
else if (net->is_enable)
|
||||
return (glb_id % 2) == 1;
|
||||
else
|
||||
return true;
|
||||
} else {
|
||||
// TODO: IO cell clock checks
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,9 +60,6 @@ void arch_wrap_python(py::module &m)
|
||||
.def("place", &Context::place)
|
||||
.def("route", &Context::route);
|
||||
|
||||
fn_wrapper_2a<Context, decltype(&Context::isValidBelForCell), &Context::isValidBelForCell, pass_through<bool>,
|
||||
addr_and_unwrap<CellInfo>, conv_from_str<BelId>>::def_wrap(ctx_cls, "isValidBelForCell");
|
||||
|
||||
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
|
||||
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
|
||||
typedef std::unordered_map<IdString, IdString> AliasMap;
|
||||
|
@ -448,14 +448,6 @@ bool Arch::route()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
||||
{
|
||||
// FIXME: Unlike ECP5, SLICEs in a given tile do not share a clock, so
|
||||
// any SLICE Cell is valid for any BEL, even if some cells are already
|
||||
// bound to BELs in the tile. However, this may need to be filled in once
|
||||
// more than one LUT4 and DFF type is supported.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arch::isBelLocationValid(BelId bel) const
|
||||
{
|
||||
|
@ -650,7 +650,6 @@ struct Arch : BaseArch<ArchRanges>
|
||||
bool route() override;
|
||||
|
||||
// Placer
|
||||
bool isValidBelForCell(CellInfo *cell, BelId bel) const override;
|
||||
bool isBelLocationValid(BelId bel) const override;
|
||||
|
||||
static const std::string defaultPlacer;
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
@ -44,9 +45,6 @@ void arch_wrap_python(py::module &m)
|
||||
.def("place", &Context::place)
|
||||
.def("route", &Context::route);
|
||||
|
||||
fn_wrapper_2a<Context, decltype(&Context::isValidBelForCell), &Context::isValidBelForCell, pass_through<bool>,
|
||||
addr_and_unwrap<CellInfo>, conv_from_str<BelId>>::def_wrap(ctx_cls, "isValidBelForCell");
|
||||
|
||||
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
|
||||
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
|
||||
typedef std::unordered_map<IdString, IdString> AliasMap;
|
||||
|
@ -1209,11 +1209,6 @@ struct Arch : BaseArch<ArchRanges>
|
||||
// Perform placement validity checks, returning false on failure (all
|
||||
// implemented in arch_place.cc)
|
||||
|
||||
// Whether or not a given cell can be placed at a given Bel
|
||||
// This is not intended for Bel type checks, but finer-grained constraints
|
||||
// such as conflicting set/reset signals, etc
|
||||
bool isValidBelForCell(CellInfo *cell, BelId bel) const override;
|
||||
|
||||
// Return true whether all Bels at a given location are valid
|
||||
bool isBelLocationValid(BelId bel) const override;
|
||||
|
||||
|
@ -96,12 +96,6 @@ bool Arch::nexus_logic_tile_valid(LogicTileStatus <s) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
||||
{
|
||||
// FIXME
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arch::isBelLocationValid(BelId bel) const
|
||||
{
|
||||
if (bel_tile_is(bel, LOC_LOGIC)) {
|
||||
|
Loading…
Reference in New Issue
Block a user