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.
This commit is contained in:
parent
86396c41d6
commit
09e388f453
@ -150,4 +150,10 @@ fn_wrapper_1a<Context, decltype(&Context::getDelayFromNS), &Context::getDelayFro
|
||||
pass_through<double>>::def_wrap(ctx_cls, "getDelayFromNS");
|
||||
|
||||
fn_wrapper_1a<Context, decltype(&Context::getDelayNS), &Context::getDelayNS, pass_through<double>,
|
||||
pass_through<delay_t>>::def_wrap(ctx_cls, "getDelayNS");
|
||||
pass_through<delay_t>>::def_wrap(ctx_cls, "getDelayNS");
|
||||
|
||||
fn_wrapper_3a_v<Context, decltype(&Context::createRegionPlug), &Context::createRegionPlug, conv_from_str<IdString>,
|
||||
conv_from_str<IdString>, pass_through<Loc>>::def_wrap(ctx_cls, "createRegionPlug");
|
||||
fn_wrapper_4a_v<Context, decltype(&Context::addPlugPin), &Context::addPlugPin, conv_from_str<IdString>,
|
||||
conv_from_str<IdString>, pass_through<PortType>, conv_from_str<WireId>>::def_wrap(ctx_cls,
|
||||
"addPlugPin");
|
||||
|
@ -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<RegionPlug>(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<RegionPlug *>(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;
|
||||
|
@ -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);
|
||||
|
@ -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<WireId, 2> Context::getNetinfoSinkWires(const NetInfo *net_info, const PortRef &user_info) const
|
||||
{
|
||||
if (user_info.cell->isPseudo())
|
||||
return SSOArray<WireId, 2>(1, user_info.cell->pseudo_cell->getPortWire(user_info.port));
|
||||
auto dst_bel = user_info.cell->bel;
|
||||
if (dst_bel == BelId())
|
||||
return SSOArray<WireId, 2>(0, WireId());
|
||||
|
@ -64,6 +64,24 @@ struct Context : Arch, DeterministicRNG
|
||||
bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay = nullptr,
|
||||
dict<WireId, PipId> *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();
|
||||
|
@ -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
|
||||
|
@ -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<IdString, PortInfo> ports;
|
||||
dict<IdString, Property> 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<IdString, WireId> 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<IdString, PortInfo> ports;
|
||||
dict<IdString, Property> attrs, params;
|
||||
|
||||
BelId bel;
|
||||
PlaceStrength belStrength = STRENGTH_NONE;
|
||||
|
||||
// cell is part of a cluster if != ClusterId
|
||||
ClusterId cluster;
|
||||
|
||||
Region *region = nullptr;
|
||||
|
||||
std::unique_ptr<PseudoCell> 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;
|
||||
|
@ -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 dict<IdString
|
||||
if (!net->driver.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -390,6 +390,8 @@ struct ParallelRefine
|
||||
// Setup fast bels map
|
||||
pool<IdString> 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())
|
||||
|
@ -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);
|
||||
|
@ -76,6 +76,8 @@ class SAPlacer
|
||||
|
||||
pool<IdString> 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<dict<IdString, int>>(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);
|
||||
|
@ -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<IdString> cell_types_in_use;
|
||||
pool<BelBucketId> 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<IdString> 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)))
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -748,11 +748,11 @@ struct Arch : ArchAPI<ArchRanges>
|
||||
|
||||
// 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;
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user