From 09e388f453d9cf998391495349c88e5478b62e34 Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 23 Jun 2022 18:48:31 +0100 Subject: [PATCH] netlist: Add PseudoCell API When implementing concepts such as partition pins or deliberately split nets, there's a need for something that looks like a cell (starts/ends routing with pins on nets, has timing data) but isn't mapped to a fixed bel in the architecture, but instead can have pin mappings defined at runtime. The PseudoCell allows this by providing an alternate, virtual-function based API for such cells. When a cell has `pseudo_cell` used, instead of calling functions such as getBelPinWire, getBelLocation or getCellDelay in the Arch API; such data is provided by the cell itself, fully flexible at runtime regardless of arch, via methods on the PseudoCell implementation. --- common/kernel/arch_pybindings_shared.h | 8 +- common/kernel/basectx.cc | 24 +++++ common/kernel/basectx.h | 4 + common/kernel/context.cc | 5 + common/kernel/context.h | 18 ++++ common/kernel/nextpnr_types.cc | 10 ++ common/kernel/nextpnr_types.h | 136 +++++++++++++++---------- common/place/detail_place_core.cc | 12 ++- common/place/parallel_refine.cc | 2 + common/place/place_common.cc | 6 +- common/place/placer1.cc | 20 ++-- common/place/placer_heap.cc | 24 ++++- common/route/router2.cc | 4 +- docs/netlist.md | 16 ++- fpga_interchange/arch.h | 6 +- generic/arch.cc | 2 +- 16 files changed, 223 insertions(+), 74 deletions(-) diff --git a/common/kernel/arch_pybindings_shared.h b/common/kernel/arch_pybindings_shared.h index bfb58f11..d78d240c 100644 --- a/common/kernel/arch_pybindings_shared.h +++ b/common/kernel/arch_pybindings_shared.h @@ -150,4 +150,10 @@ fn_wrapper_1a>::def_wrap(ctx_cls, "getDelayFromNS"); fn_wrapper_1a, - pass_through>::def_wrap(ctx_cls, "getDelayNS"); \ No newline at end of file + pass_through>::def_wrap(ctx_cls, "getDelayNS"); + +fn_wrapper_3a_v, + conv_from_str, pass_through>::def_wrap(ctx_cls, "createRegionPlug"); +fn_wrapper_4a_v, + conv_from_str, pass_through, conv_from_str>::def_wrap(ctx_cls, + "addPlugPin"); diff --git a/common/kernel/basectx.cc b/common/kernel/basectx.cc index 83a2deea..82cdd835 100644 --- a/common/kernel/basectx.cc +++ b/common/kernel/basectx.cc @@ -131,6 +131,30 @@ void BaseCtx::constrainCellToRegion(IdString cell, IdString region_name) if (!matched) log_warning("No cell matched '%s' when constraining to region '%s'\n", nameOf(cell), nameOf(region_name)); } + +void BaseCtx::createRegionPlug(IdString name, IdString type, Loc approx_loc) +{ + CellInfo *cell = nullptr; + if (cells.count(name)) + cell = cells.at(name).get(); + else + cell = createCell(name, type); + cell->pseudo_cell = std::make_unique(approx_loc); +} + +void BaseCtx::addPlugPin(IdString plug, IdString pin, PortType dir, WireId wire) +{ + if (!cells.count(plug)) + log_error("no cell named '%s' found\n", plug.c_str(this)); + CellInfo *ci = cells.at(plug).get(); + RegionPlug *rplug = dynamic_cast(ci->pseudo_cell.get()); + if (!rplug) + log_error("cell '%s' is not a RegionPlug\n", plug.c_str(this)); + rplug->port_wires[pin] = wire; + ci->ports[pin].name = pin; + ci->ports[pin].type = dir; +} + DecalXY BaseCtx::constructDecalXY(DecalId decal, float x, float y) { DecalXY dxy; diff --git a/common/kernel/basectx.h b/common/kernel/basectx.h index 21d6d63a..5775e47f 100644 --- a/common/kernel/basectx.h +++ b/common/kernel/basectx.h @@ -220,6 +220,10 @@ struct BaseCtx void addBelToRegion(IdString name, BelId bel); void constrainCellToRegion(IdString cell, IdString region_name); + // Helper functions for the partial reconfiguration plug API using PseudoCells + void createRegionPlug(IdString name, IdString type, Loc approx_loc); + void addPlugPin(IdString plug, IdString pin, PortType dir, WireId wire); + // Helper functions for Python bindings NetInfo *createNet(IdString name); void connectPort(IdString net, IdString cell, IdString port); diff --git a/common/kernel/context.cc b/common/kernel/context.cc index e35d3e49..014394a6 100644 --- a/common/kernel/context.cc +++ b/common/kernel/context.cc @@ -30,6 +30,9 @@ WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const if (net_info->driver.cell == nullptr) return WireId(); + if (net_info->driver.cell->isPseudo()) + return net_info->driver.cell->pseudo_cell->getPortWire(net_info->driver.port); + auto src_bel = net_info->driver.cell->bel; if (src_bel == BelId()) @@ -47,6 +50,8 @@ WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const SSOArray Context::getNetinfoSinkWires(const NetInfo *net_info, const PortRef &user_info) const { + if (user_info.cell->isPseudo()) + return SSOArray(1, user_info.cell->pseudo_cell->getPortWire(user_info.port)); auto dst_bel = user_info.cell->bel; if (dst_bel == BelId()) return SSOArray(0, WireId()); diff --git a/common/kernel/context.h b/common/kernel/context.h index cb8fd257..4a5667d0 100644 --- a/common/kernel/context.h +++ b/common/kernel/context.h @@ -64,6 +64,24 @@ struct Context : Arch, DeterministicRNG bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay = nullptr, dict *route = nullptr, bool useEstimate = true); + // -------------------------------------------------------------- + // Dispatch to the Arch API or pseudo-cell API accordingly + bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const override + { + return cell->pseudo_cell ? cell->pseudo_cell->getDelay(fromPort, toPort, delay) + : Arch::getCellDelay(cell, fromPort, toPort, delay); + } + TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const override + { + return cell->pseudo_cell ? cell->pseudo_cell->getPortTimingClass(port, clockInfoCount) + : Arch::getPortTimingClass(cell, port, clockInfoCount); + } + TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const override + { + return cell->pseudo_cell ? cell->pseudo_cell->getPortClockingInfo(port, index) + : Arch::getPortClockingInfo(cell, port, index); + } + // -------------------------------------------------------------- // call after changing hierpath or adding/removing nets and cells void fixupHierarchy(); diff --git a/common/kernel/nextpnr_types.cc b/common/kernel/nextpnr_types.cc index 82725d6f..8563eb27 100644 --- a/common/kernel/nextpnr_types.cc +++ b/common/kernel/nextpnr_types.cc @@ -177,4 +177,14 @@ void CellInfo::copyPortBusTo(IdString old_name, int old_offset, bool old_bracket } } +Loc CellInfo::getLocation() const +{ + if (pseudo_cell) { + return pseudo_cell->getLocation(); + } else { + NPNR_ASSERT(bel != BelId()); + return ctx->getBelLocation(bel); + } +} + NEXTPNR_NAMESPACE_END diff --git a/common/kernel/nextpnr_types.h b/common/kernel/nextpnr_types.h index c21182cc..e4042aec 100644 --- a/common/kernel/nextpnr_types.h +++ b/common/kernel/nextpnr_types.h @@ -160,58 +160,6 @@ struct PortInfo struct Context; -struct CellInfo : ArchCellInfo -{ - CellInfo(Context *ctx, IdString name, IdString type) : ctx(ctx), name(name), type(type){}; - Context *ctx = nullptr; - - IdString name, type, hierpath; - int32_t udata; - - dict ports; - dict attrs, params; - - BelId bel; - PlaceStrength belStrength = STRENGTH_NONE; - - // cell is part of a cluster if != ClusterId - ClusterId cluster; - - Region *region = nullptr; - - void addInput(IdString name); - void addOutput(IdString name); - void addInout(IdString name); - - void setParam(IdString name, Property value); - void unsetParam(IdString name); - void setAttr(IdString name, Property value); - void unsetAttr(IdString name); - // check whether a bel complies with the cell's region constraint - bool testRegion(BelId bel) const; - - NetInfo *getPort(IdString name) - { - auto found = ports.find(name); - return (found == ports.end()) ? nullptr : found->second.net; - } - const NetInfo *getPort(IdString name) const - { - auto found = ports.find(name); - return (found == ports.end()) ? nullptr : found->second.net; - } - void connectPort(IdString port, NetInfo *net); - void disconnectPort(IdString port); - void connectPorts(IdString port, CellInfo *other, IdString other_port); - void movePortTo(IdString port, CellInfo *other, IdString other_port); - void renamePort(IdString old_name, IdString new_name); - void movePortBusTo(IdString old_name, int old_offset, bool old_brackets, CellInfo *new_cell, IdString new_name, - int new_offset, bool new_brackets, int width); - void copyPortTo(IdString port, CellInfo *other, IdString other_port); - void copyPortBusTo(IdString old_name, int old_offset, bool old_brackets, CellInfo *new_cell, IdString new_name, - int new_offset, bool new_brackets, int width); -}; - enum TimingPortClass { TMG_CLOCK_INPUT, // Clock input to a sequential cell @@ -239,6 +187,90 @@ struct TimingClockingInfo DelayQuad clockToQ; // Output clock-to-Q time }; +struct PseudoCell +{ + virtual Loc getLocation() const = 0; + virtual WireId getPortWire(IdString port) const = 0; + + virtual bool getDelay(IdString fromPort, IdString toPort, DelayQuad &delay) const = 0; + virtual TimingPortClass getPortTimingClass(IdString port, int &clockInfoCount) const = 0; + virtual TimingClockingInfo getPortClockingInfo(IdString port, int index) const = 0; + virtual ~PseudoCell(){}; +}; + +struct RegionPlug : PseudoCell +{ + RegionPlug(Loc loc) : loc(loc){}; // 'loc' is a notional location for the placer only + Loc getLocation() const override { return loc; } + WireId getPortWire(IdString port) const override { return port_wires.at(port); } + + // TODO: partial reconfiguration region timing + bool getDelay(IdString fromPort, IdString toPort, DelayQuad &delay) const { return false; } + TimingPortClass getPortTimingClass(IdString port, int &clockInfoCount) const { return TMG_IGNORE; } + virtual TimingClockingInfo getPortClockingInfo(IdString port, int index) const { return TimingClockingInfo{}; } + + dict port_wires; + Loc loc; +}; + +struct CellInfo : ArchCellInfo +{ + CellInfo(Context *ctx, IdString name, IdString type) : ctx(ctx), name(name), type(type){}; + Context *ctx = nullptr; + + IdString name, type, hierpath; + int32_t udata; + + dict ports; + dict attrs, params; + + BelId bel; + PlaceStrength belStrength = STRENGTH_NONE; + + // cell is part of a cluster if != ClusterId + ClusterId cluster; + + Region *region = nullptr; + + std::unique_ptr pseudo_cell{}; + + void addInput(IdString name); + void addOutput(IdString name); + void addInout(IdString name); + + void setParam(IdString name, Property value); + void unsetParam(IdString name); + void setAttr(IdString name, Property value); + void unsetAttr(IdString name); + // check whether a bel complies with the cell's region constraint + bool testRegion(BelId bel) const; + + bool isPseudo() const { return bool(pseudo_cell); } + + Loc getLocation() const; + + NetInfo *getPort(IdString name) + { + auto found = ports.find(name); + return (found == ports.end()) ? nullptr : found->second.net; + } + const NetInfo *getPort(IdString name) const + { + auto found = ports.find(name); + return (found == ports.end()) ? nullptr : found->second.net; + } + void connectPort(IdString port, NetInfo *net); + void disconnectPort(IdString port); + void connectPorts(IdString port, CellInfo *other, IdString other_port); + void movePortTo(IdString port, CellInfo *other, IdString other_port); + void renamePort(IdString old_name, IdString new_name); + void movePortBusTo(IdString old_name, int old_offset, bool old_brackets, CellInfo *new_cell, IdString new_name, + int new_offset, bool new_brackets, int width); + void copyPortTo(IdString port, CellInfo *other, IdString other_port); + void copyPortBusTo(IdString old_name, int old_offset, bool old_brackets, CellInfo *new_cell, IdString new_name, + int new_offset, bool new_brackets, int width); +}; + struct ClockConstraint { DelayPair high; diff --git a/common/place/detail_place_core.cc b/common/place/detail_place_core.cc index 7e629f24..18118fc8 100644 --- a/common/place/detail_place_core.cc +++ b/common/place/detail_place_core.cc @@ -37,6 +37,8 @@ PlacePartition::PlacePartition(Context *ctx) x1 = 0; y1 = 0; for (auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; Loc l = ctx->getBelLocation(cell.second->bel); x0 = std::min(x0, l.x); x1 = std::max(x1, l.x); @@ -110,6 +112,8 @@ NetBB NetBB::compute(const Context *ctx, const NetInfo *net, const dictdriver.cell) return result; auto bel_loc = [&](const CellInfo *cell) { + if (cell->isPseudo()) + return cell->getLocation(); BelId bel = cell2bel ? cell2bel->at(cell->name) : cell->bel; return ctx->getBelLocation(bel); }; @@ -176,10 +180,12 @@ void DetailPlacerThreadState::set_partition(const PlacePartition &part) // Set up the original cell-bel map for all nets inside the thread local_cell2bel.clear(); for (NetInfo *net : thread_nets) { - if (net->driver.cell) + if (net->driver.cell && !net->driver.cell->isPseudo()) local_cell2bel[net->driver.cell->name] = net->driver.cell->bel; - for (auto &usr : net->users) - local_cell2bel[usr.cell->name] = usr.cell->bel; + for (auto &usr : net->users) { + if (!usr.cell->isPseudo()) + local_cell2bel[usr.cell->name] = usr.cell->bel; + } } } diff --git a/common/place/parallel_refine.cc b/common/place/parallel_refine.cc index de71b8e0..0fb99be5 100644 --- a/common/place/parallel_refine.cc +++ b/common/place/parallel_refine.cc @@ -390,6 +390,8 @@ struct ParallelRefine // Setup fast bels map pool cell_types_in_use; for (auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; IdString cell_type = cell.second->type; cell_types_in_use.insert(cell_type); if (cell.second->cluster != ClusterId()) diff --git a/common/place/place_common.cc b/common/place/place_common.cc index e03fca55..c2fc3b7d 100644 --- a/common/place/place_common.cc +++ b/common/place/place_common.cc @@ -293,6 +293,8 @@ class ConstraintLegaliseWorker { if (cell->cluster != ClusterId() && ctx->getClusterRootCell(cell->cluster) != cell) return true; // Only process chain roots + if (cell->isPseudo()) + return true; if (constraints_satisfied(cell)) { if (cell->cluster != ClusterId()) lockdown_chain(cell); @@ -415,7 +417,7 @@ class ConstraintLegaliseWorker { log_info("Legalising relative constraints...\n"); for (auto &cell : ctx->cells) { - oldLocations[cell.first] = ctx->getBelLocation(cell.second->bel); + oldLocations[cell.first] = cell.second->getLocation(); } for (auto &cell : ctx->cells) { bool res = legalise_cell(cell.second.get()); @@ -448,6 +450,8 @@ bool legalise_relative_constraints(Context *ctx) { return ConstraintLegaliseWork int get_constraints_distance(const Context *ctx, const CellInfo *cell) { int dist = 0; + if (cell->isPseudo()) + return 0; if (cell->bel == BelId()) return 100000; Loc loc = ctx->getBelLocation(cell->bel); diff --git a/common/place/placer1.cc b/common/place/placer1.cc index a6ba3895..23264ce2 100644 --- a/common/place/placer1.cc +++ b/common/place/placer1.cc @@ -76,6 +76,8 @@ class SAPlacer pool cell_types_in_use; for (auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; IdString cell_type = cell.second->type; cell_types_in_use.insert(cell_type); } @@ -120,7 +122,7 @@ class SAPlacer } for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->cluster == ClusterId()) + if (ci->isPseudo() || ci->cluster == ClusterId()) continue; cluster2cell[ci->cluster].push_back(ci); } @@ -145,6 +147,8 @@ class SAPlacer // Initial constraints placer for (auto &cell_entry : ctx->cells) { CellInfo *cell = cell_entry.second.get(); + if (cell->isPseudo()) + continue; auto loc = cell->attrs.find(ctx->id("BEL")); if (loc != cell->attrs.end()) { std::string loc_name = loc->second.as_string(); @@ -187,7 +191,7 @@ class SAPlacer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->bel == BelId()) { + if (!ci->isPseudo() && (ci->bel == BelId())) { autoplaced.push_back(cell.second.get()); } } @@ -217,7 +221,7 @@ class SAPlacer } else { for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->belStrength > STRENGTH_STRONG) { + if (ci->isPseudo() || ci->belStrength > STRENGTH_STRONG) { continue; } else if (ci->cluster != ClusterId()) { if (ctx->getClusterRootCell(ci->cluster) == ci) @@ -353,6 +357,8 @@ class SAPlacer autoplaced.clear(); chain_basis.clear(); for (auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; if (cell.second->belStrength <= STRENGTH_STRONG && cell.second->cluster != ClusterId() && ctx->getClusterRootCell(cell.second->cluster) == cell.second.get()) chain_basis.push_back(cell.second.get()); @@ -814,7 +820,7 @@ class SAPlacer { BoundingBox bb; NPNR_ASSERT(net->driver.cell != nullptr); - Loc dloc = ctx->getBelLocation(net->driver.cell->bel); + Loc dloc = net->driver.cell->getLocation(); bb.x0 = dloc.x; bb.x1 = dloc.x; bb.y0 = dloc.y; @@ -824,9 +830,9 @@ class SAPlacer bb.ny0 = 1; bb.ny1 = 1; for (auto user : net->users) { - if (user.cell->bel == BelId()) + if (!user.cell->isPseudo() && user.cell->bel == BelId()) continue; - Loc uloc = ctx->getBelLocation(user.cell->bel); + Loc uloc = user.cell->getLocation(); if (bb.x0 == uloc.x) ++bb.nx0; else if (uloc.x < bb.x0) { @@ -1173,7 +1179,7 @@ class SAPlacer nets_by_tile.resize(max_x + 1, std::vector>(max_y + 1)); for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (int(ci->ports.size()) > large_cell_thresh) + if (ci->isPseudo() || (int(ci->ports.size()) > large_cell_thresh)) continue; Loc loc = ctx->getBelLocation(ci->bel); auto &nbt = nets_by_tile.at(loc.x).at(loc.y); diff --git a/common/place/placer_heap.cc b/common/place/placer_heap.cc index 4c9ffb23..bd8cd37d 100644 --- a/common/place/placer_heap.cc +++ b/common/place/placer_heap.cc @@ -147,7 +147,7 @@ class HeAPPlacer tmg.setup(); for (auto &cell : ctx->cells) - if (cell.second->cluster != ClusterId()) + if (!cell.second->isPseudo() && cell.second->cluster != ClusterId()) cluster2cells[cell.second->cluster].push_back(cell.second.get()); } @@ -284,6 +284,8 @@ class HeAPPlacer // Save solution solution.clear(); for (auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; solution.emplace_back(cell.second.get(), cell.second->bel, cell.second->belStrength); } } else { @@ -312,6 +314,8 @@ class HeAPPlacer } for (auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; if (cell.second->bel == BelId()) log_error("Found unbound cell %s\n", cell.first.c_str(ctx)); if (ctx->getBoundBelCell(cell.second->bel) != cell.second.get()) @@ -411,7 +415,8 @@ class HeAPPlacer // Initial constraints placer for (auto &cell_entry : ctx->cells) { CellInfo *cell = cell_entry.second.get(); - + if (cell->isPseudo()) + continue; auto loc = cell->attrs.find(ctx->id("BEL")); if (loc != cell->attrs.end()) { std::string loc_name = loc->second.as_string(); @@ -461,6 +466,8 @@ class HeAPPlacer pool cell_types_in_use; pool buckets_in_use; for (auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; IdString cell_type = cell.second->type; cell_types_in_use.insert(cell_type); BelBucketId bucket = ctx->getBelBucketForCellType(cell_type); @@ -527,6 +534,8 @@ class HeAPPlacer { pool cell_types; for (const auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; cell_types.insert(cell.second->type); } @@ -551,6 +560,14 @@ class HeAPPlacer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); + if (ci->isPseudo()) { + Loc loc = ci->pseudo_cell->getLocation(); + cell_locs[cell.first].x = loc.x; + cell_locs[cell.first].y = loc.y; + cell_locs[cell.first].locked = true; + cell_locs[cell.first].global = false; + continue; + } if (ci->bel != BelId()) { Loc loc = ctx->getBelLocation(ci->bel); cell_locs[cell.first].x = loc.x; @@ -627,8 +644,9 @@ class HeAPPlacer int row = 0; solve_cells.clear(); // First clear the udata of all cells - for (auto &cell : ctx->cells) + for (auto &cell : ctx->cells) { cell.second->udata = dont_solve; + } // Then update cells to be placed, which excludes cell children for (auto cell : place_cells) { if (buckets && !buckets->count(ctx->getBelBucketForCellType(cell->type))) diff --git a/common/route/router2.cc b/common/route/router2.cc index e943e493..1153f054 100644 --- a/common/route/router2.cc +++ b/common/route/router2.cc @@ -128,7 +128,7 @@ struct Router2 nets.at(i).cy = 0; if (ni->driver.cell != nullptr) { - Loc drv_loc = ctx->getBelLocation(ni->driver.cell->bel); + Loc drv_loc = ni->driver.cell->getLocation(); nets.at(i).cx += drv_loc.x; nets.at(i).cy += drv_loc.y; } @@ -159,7 +159,7 @@ struct Router2 nets.at(i).bb.y1 = std::max(nets.at(i).bb.y1, ad.bb.y1); } // Add location to centroid sum - Loc usr_loc = ctx->getBelLocation(usr.value.cell->bel); + Loc usr_loc = usr.value.cell->getLocation(); nets.at(i).cx += usr_loc.x; nets.at(i).cy += usr_loc.y; } diff --git a/docs/netlist.md b/docs/netlist.md index 5d8ca572..43a96dde 100644 --- a/docs/netlist.md +++ b/docs/netlist.md @@ -25,10 +25,24 @@ Other structures used by these basic structures include: - `params` and `attrs` store parameters and attributes - from the input JSON or assigned in flows to add metadata - by mapping from parameter name `IdString` to `Property`. - `cluster` is used to specify that the cell is inside a placement cluster, with the details of the placement within the cluster provided by the architecture. - `region` is a reference to a `Region` if the cell is constrained to a placement region (e.g. for partial reconfiguration or out-of-context flows) or `nullptr` otherwise. + - `pseudo_cell` is an optional pointer to an implementation of the pseudo-cell API, used for cells implementing virtual functions such as partition pins without a mapped bel. `bel` will always be `BelId()` for pseudo-cells. + +## PseudoCellAPI + +Pseudo-cells can be used to implement cells with runtime-defined cell pin to wire mappings. This means they don't have to be a fixed part of the architecture, example use cases could be for implementing partition pins for partial reconfiguration regions; or forcing splits between SLRs. Pseudo-cells implement a series of virtual functions to provide data that for an ordinary cell would be obtained by calling 'bel' ArchAPI functions + +The pseudo-cell API is as follows: + - `Loc getLocation() const` : get an approximate location of the pseudocell + - `WireId getPortWire(IdString port) const`: gets the wire corresponding to a port (or WireId if it has no wire) + +It also implements functions for getting timing data, mirroring that of the Arch API: + - `bool getDelay(IdString fromPort, IdString toPort, DelayQuad &delay) const` + - `TimingPortClass getPortTimingClass(IdString port, int &clockInfoCount) const` + - `TimingClockingInfo getPortClockingInfo(IdString port, int index) const` ## NetInfo -`NetInfo` instances have the following fields: +`NetInfo` instances have the following fields:\ - `name` is the IdString name of the net - for nets with multiple names, one name is chosen according to a set of rules by the JSON frontend - `hierpath` is name of the hierarchical cell containing the instance, for designs with hierarchy diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index 789b188e..aeb5578f 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -748,11 +748,11 @@ struct Arch : ArchAPI // Get the delay through a cell from one port to another, returning false // if no path exists. This only considers combinational delays, as required by the Arch API - bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const final; + bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const; // Get the port class, also setting clockInfoCount to the number of TimingClockingInfos associated with a port - TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const final; + TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const; // Get the TimingClockingInfo of a port - TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const final; + TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const; // ------------------------------------------------- diff --git a/generic/arch.cc b/generic/arch.cc index 11b5868b..3df58c9b 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -605,7 +605,7 @@ bool Arch::place() bool have_iobuf_or_constr = false; for (auto &cell : cells) { CellInfo *ci = cell.second.get(); - if (ci->type == id("GENERIC_IOB") || ci->bel != BelId() || ci->attrs.count(id("BEL"))) { + if (ci->isPseudo() || ci->type == id("GENERIC_IOB") || ci->bel != BelId() || ci->attrs.count(id("BEL"))) { have_iobuf_or_constr = true; break; }