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
@ -151,3 +151,9 @@ fn_wrapper_1a<Context, decltype(&Context::getDelayFromNS), &Context::getDelayFro
|
|||||||
|
|
||||||
fn_wrapper_1a<Context, decltype(&Context::getDelayNS), &Context::getDelayNS, pass_through<double>,
|
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)
|
if (!matched)
|
||||||
log_warning("No cell matched '%s' when constraining to region '%s'\n", nameOf(cell), nameOf(region_name));
|
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 BaseCtx::constructDecalXY(DecalId decal, float x, float y)
|
||||||
{
|
{
|
||||||
DecalXY dxy;
|
DecalXY dxy;
|
||||||
|
@ -220,6 +220,10 @@ struct BaseCtx
|
|||||||
void addBelToRegion(IdString name, BelId bel);
|
void addBelToRegion(IdString name, BelId bel);
|
||||||
void constrainCellToRegion(IdString cell, IdString region_name);
|
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
|
// Helper functions for Python bindings
|
||||||
NetInfo *createNet(IdString name);
|
NetInfo *createNet(IdString name);
|
||||||
void connectPort(IdString net, IdString cell, IdString port);
|
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)
|
if (net_info->driver.cell == nullptr)
|
||||||
return WireId();
|
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;
|
auto src_bel = net_info->driver.cell->bel;
|
||||||
|
|
||||||
if (src_bel == BelId())
|
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
|
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;
|
auto dst_bel = user_info.cell->bel;
|
||||||
if (dst_bel == BelId())
|
if (dst_bel == BelId())
|
||||||
return SSOArray<WireId, 2>(0, WireId());
|
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,
|
bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay = nullptr,
|
||||||
dict<WireId, PipId> *route = nullptr, bool useEstimate = true);
|
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
|
// call after changing hierpath or adding/removing nets and cells
|
||||||
void fixupHierarchy();
|
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
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -160,58 +160,6 @@ struct PortInfo
|
|||||||
|
|
||||||
struct Context;
|
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
|
enum TimingPortClass
|
||||||
{
|
{
|
||||||
TMG_CLOCK_INPUT, // Clock input to a sequential cell
|
TMG_CLOCK_INPUT, // Clock input to a sequential cell
|
||||||
@ -239,6 +187,90 @@ struct TimingClockingInfo
|
|||||||
DelayQuad clockToQ; // Output clock-to-Q time
|
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
|
struct ClockConstraint
|
||||||
{
|
{
|
||||||
DelayPair high;
|
DelayPair high;
|
||||||
|
@ -37,6 +37,8 @@ PlacePartition::PlacePartition(Context *ctx)
|
|||||||
x1 = 0;
|
x1 = 0;
|
||||||
y1 = 0;
|
y1 = 0;
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
|
if (cell.second->isPseudo())
|
||||||
|
continue;
|
||||||
Loc l = ctx->getBelLocation(cell.second->bel);
|
Loc l = ctx->getBelLocation(cell.second->bel);
|
||||||
x0 = std::min(x0, l.x);
|
x0 = std::min(x0, l.x);
|
||||||
x1 = std::max(x1, 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)
|
if (!net->driver.cell)
|
||||||
return result;
|
return result;
|
||||||
auto bel_loc = [&](const CellInfo *cell) {
|
auto bel_loc = [&](const CellInfo *cell) {
|
||||||
|
if (cell->isPseudo())
|
||||||
|
return cell->getLocation();
|
||||||
BelId bel = cell2bel ? cell2bel->at(cell->name) : cell->bel;
|
BelId bel = cell2bel ? cell2bel->at(cell->name) : cell->bel;
|
||||||
return ctx->getBelLocation(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
|
// Set up the original cell-bel map for all nets inside the thread
|
||||||
local_cell2bel.clear();
|
local_cell2bel.clear();
|
||||||
for (NetInfo *net : thread_nets) {
|
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;
|
local_cell2bel[net->driver.cell->name] = net->driver.cell->bel;
|
||||||
for (auto &usr : net->users)
|
for (auto &usr : net->users) {
|
||||||
local_cell2bel[usr.cell->name] = usr.cell->bel;
|
if (!usr.cell->isPseudo())
|
||||||
|
local_cell2bel[usr.cell->name] = usr.cell->bel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,6 +390,8 @@ struct ParallelRefine
|
|||||||
// Setup fast bels map
|
// Setup fast bels map
|
||||||
pool<IdString> cell_types_in_use;
|
pool<IdString> cell_types_in_use;
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
|
if (cell.second->isPseudo())
|
||||||
|
continue;
|
||||||
IdString cell_type = cell.second->type;
|
IdString cell_type = cell.second->type;
|
||||||
cell_types_in_use.insert(cell_type);
|
cell_types_in_use.insert(cell_type);
|
||||||
if (cell.second->cluster != ClusterId())
|
if (cell.second->cluster != ClusterId())
|
||||||
|
@ -293,6 +293,8 @@ class ConstraintLegaliseWorker
|
|||||||
{
|
{
|
||||||
if (cell->cluster != ClusterId() && ctx->getClusterRootCell(cell->cluster) != cell)
|
if (cell->cluster != ClusterId() && ctx->getClusterRootCell(cell->cluster) != cell)
|
||||||
return true; // Only process chain roots
|
return true; // Only process chain roots
|
||||||
|
if (cell->isPseudo())
|
||||||
|
return true;
|
||||||
if (constraints_satisfied(cell)) {
|
if (constraints_satisfied(cell)) {
|
||||||
if (cell->cluster != ClusterId())
|
if (cell->cluster != ClusterId())
|
||||||
lockdown_chain(cell);
|
lockdown_chain(cell);
|
||||||
@ -415,7 +417,7 @@ class ConstraintLegaliseWorker
|
|||||||
{
|
{
|
||||||
log_info("Legalising relative constraints...\n");
|
log_info("Legalising relative constraints...\n");
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
oldLocations[cell.first] = ctx->getBelLocation(cell.second->bel);
|
oldLocations[cell.first] = cell.second->getLocation();
|
||||||
}
|
}
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
bool res = legalise_cell(cell.second.get());
|
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 get_constraints_distance(const Context *ctx, const CellInfo *cell)
|
||||||
{
|
{
|
||||||
int dist = 0;
|
int dist = 0;
|
||||||
|
if (cell->isPseudo())
|
||||||
|
return 0;
|
||||||
if (cell->bel == BelId())
|
if (cell->bel == BelId())
|
||||||
return 100000;
|
return 100000;
|
||||||
Loc loc = ctx->getBelLocation(cell->bel);
|
Loc loc = ctx->getBelLocation(cell->bel);
|
||||||
|
@ -76,6 +76,8 @@ class SAPlacer
|
|||||||
|
|
||||||
pool<IdString> cell_types_in_use;
|
pool<IdString> cell_types_in_use;
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
|
if (cell.second->isPseudo())
|
||||||
|
continue;
|
||||||
IdString cell_type = cell.second->type;
|
IdString cell_type = cell.second->type;
|
||||||
cell_types_in_use.insert(cell_type);
|
cell_types_in_use.insert(cell_type);
|
||||||
}
|
}
|
||||||
@ -120,7 +122,7 @@ class SAPlacer
|
|||||||
}
|
}
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
CellInfo *ci = cell.second.get();
|
CellInfo *ci = cell.second.get();
|
||||||
if (ci->cluster == ClusterId())
|
if (ci->isPseudo() || ci->cluster == ClusterId())
|
||||||
continue;
|
continue;
|
||||||
cluster2cell[ci->cluster].push_back(ci);
|
cluster2cell[ci->cluster].push_back(ci);
|
||||||
}
|
}
|
||||||
@ -145,6 +147,8 @@ class SAPlacer
|
|||||||
// Initial constraints placer
|
// Initial constraints placer
|
||||||
for (auto &cell_entry : ctx->cells) {
|
for (auto &cell_entry : ctx->cells) {
|
||||||
CellInfo *cell = cell_entry.second.get();
|
CellInfo *cell = cell_entry.second.get();
|
||||||
|
if (cell->isPseudo())
|
||||||
|
continue;
|
||||||
auto loc = cell->attrs.find(ctx->id("BEL"));
|
auto loc = cell->attrs.find(ctx->id("BEL"));
|
||||||
if (loc != cell->attrs.end()) {
|
if (loc != cell->attrs.end()) {
|
||||||
std::string loc_name = loc->second.as_string();
|
std::string loc_name = loc->second.as_string();
|
||||||
@ -187,7 +191,7 @@ class SAPlacer
|
|||||||
|
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
CellInfo *ci = cell.second.get();
|
CellInfo *ci = cell.second.get();
|
||||||
if (ci->bel == BelId()) {
|
if (!ci->isPseudo() && (ci->bel == BelId())) {
|
||||||
autoplaced.push_back(cell.second.get());
|
autoplaced.push_back(cell.second.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,7 +221,7 @@ class SAPlacer
|
|||||||
} else {
|
} else {
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
CellInfo *ci = cell.second.get();
|
CellInfo *ci = cell.second.get();
|
||||||
if (ci->belStrength > STRENGTH_STRONG) {
|
if (ci->isPseudo() || ci->belStrength > STRENGTH_STRONG) {
|
||||||
continue;
|
continue;
|
||||||
} else if (ci->cluster != ClusterId()) {
|
} else if (ci->cluster != ClusterId()) {
|
||||||
if (ctx->getClusterRootCell(ci->cluster) == ci)
|
if (ctx->getClusterRootCell(ci->cluster) == ci)
|
||||||
@ -353,6 +357,8 @@ class SAPlacer
|
|||||||
autoplaced.clear();
|
autoplaced.clear();
|
||||||
chain_basis.clear();
|
chain_basis.clear();
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
|
if (cell.second->isPseudo())
|
||||||
|
continue;
|
||||||
if (cell.second->belStrength <= STRENGTH_STRONG && cell.second->cluster != ClusterId() &&
|
if (cell.second->belStrength <= STRENGTH_STRONG && cell.second->cluster != ClusterId() &&
|
||||||
ctx->getClusterRootCell(cell.second->cluster) == cell.second.get())
|
ctx->getClusterRootCell(cell.second->cluster) == cell.second.get())
|
||||||
chain_basis.push_back(cell.second.get());
|
chain_basis.push_back(cell.second.get());
|
||||||
@ -814,7 +820,7 @@ class SAPlacer
|
|||||||
{
|
{
|
||||||
BoundingBox bb;
|
BoundingBox bb;
|
||||||
NPNR_ASSERT(net->driver.cell != nullptr);
|
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.x0 = dloc.x;
|
||||||
bb.x1 = dloc.x;
|
bb.x1 = dloc.x;
|
||||||
bb.y0 = dloc.y;
|
bb.y0 = dloc.y;
|
||||||
@ -824,9 +830,9 @@ class SAPlacer
|
|||||||
bb.ny0 = 1;
|
bb.ny0 = 1;
|
||||||
bb.ny1 = 1;
|
bb.ny1 = 1;
|
||||||
for (auto user : net->users) {
|
for (auto user : net->users) {
|
||||||
if (user.cell->bel == BelId())
|
if (!user.cell->isPseudo() && user.cell->bel == BelId())
|
||||||
continue;
|
continue;
|
||||||
Loc uloc = ctx->getBelLocation(user.cell->bel);
|
Loc uloc = user.cell->getLocation();
|
||||||
if (bb.x0 == uloc.x)
|
if (bb.x0 == uloc.x)
|
||||||
++bb.nx0;
|
++bb.nx0;
|
||||||
else if (uloc.x < bb.x0) {
|
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));
|
nets_by_tile.resize(max_x + 1, std::vector<dict<IdString, int>>(max_y + 1));
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
CellInfo *ci = cell.second.get();
|
CellInfo *ci = cell.second.get();
|
||||||
if (int(ci->ports.size()) > large_cell_thresh)
|
if (ci->isPseudo() || (int(ci->ports.size()) > large_cell_thresh))
|
||||||
continue;
|
continue;
|
||||||
Loc loc = ctx->getBelLocation(ci->bel);
|
Loc loc = ctx->getBelLocation(ci->bel);
|
||||||
auto &nbt = nets_by_tile.at(loc.x).at(loc.y);
|
auto &nbt = nets_by_tile.at(loc.x).at(loc.y);
|
||||||
|
@ -147,7 +147,7 @@ class HeAPPlacer
|
|||||||
tmg.setup();
|
tmg.setup();
|
||||||
|
|
||||||
for (auto &cell : ctx->cells)
|
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());
|
cluster2cells[cell.second->cluster].push_back(cell.second.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,6 +284,8 @@ class HeAPPlacer
|
|||||||
// Save solution
|
// Save solution
|
||||||
solution.clear();
|
solution.clear();
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
|
if (cell.second->isPseudo())
|
||||||
|
continue;
|
||||||
solution.emplace_back(cell.second.get(), cell.second->bel, cell.second->belStrength);
|
solution.emplace_back(cell.second.get(), cell.second->bel, cell.second->belStrength);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -312,6 +314,8 @@ class HeAPPlacer
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
|
if (cell.second->isPseudo())
|
||||||
|
continue;
|
||||||
if (cell.second->bel == BelId())
|
if (cell.second->bel == BelId())
|
||||||
log_error("Found unbound cell %s\n", cell.first.c_str(ctx));
|
log_error("Found unbound cell %s\n", cell.first.c_str(ctx));
|
||||||
if (ctx->getBoundBelCell(cell.second->bel) != cell.second.get())
|
if (ctx->getBoundBelCell(cell.second->bel) != cell.second.get())
|
||||||
@ -411,7 +415,8 @@ class HeAPPlacer
|
|||||||
// Initial constraints placer
|
// Initial constraints placer
|
||||||
for (auto &cell_entry : ctx->cells) {
|
for (auto &cell_entry : ctx->cells) {
|
||||||
CellInfo *cell = cell_entry.second.get();
|
CellInfo *cell = cell_entry.second.get();
|
||||||
|
if (cell->isPseudo())
|
||||||
|
continue;
|
||||||
auto loc = cell->attrs.find(ctx->id("BEL"));
|
auto loc = cell->attrs.find(ctx->id("BEL"));
|
||||||
if (loc != cell->attrs.end()) {
|
if (loc != cell->attrs.end()) {
|
||||||
std::string loc_name = loc->second.as_string();
|
std::string loc_name = loc->second.as_string();
|
||||||
@ -461,6 +466,8 @@ class HeAPPlacer
|
|||||||
pool<IdString> cell_types_in_use;
|
pool<IdString> cell_types_in_use;
|
||||||
pool<BelBucketId> buckets_in_use;
|
pool<BelBucketId> buckets_in_use;
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
|
if (cell.second->isPseudo())
|
||||||
|
continue;
|
||||||
IdString cell_type = cell.second->type;
|
IdString cell_type = cell.second->type;
|
||||||
cell_types_in_use.insert(cell_type);
|
cell_types_in_use.insert(cell_type);
|
||||||
BelBucketId bucket = ctx->getBelBucketForCellType(cell_type);
|
BelBucketId bucket = ctx->getBelBucketForCellType(cell_type);
|
||||||
@ -527,6 +534,8 @@ class HeAPPlacer
|
|||||||
{
|
{
|
||||||
pool<IdString> cell_types;
|
pool<IdString> cell_types;
|
||||||
for (const auto &cell : ctx->cells) {
|
for (const auto &cell : ctx->cells) {
|
||||||
|
if (cell.second->isPseudo())
|
||||||
|
continue;
|
||||||
cell_types.insert(cell.second->type);
|
cell_types.insert(cell.second->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,6 +560,14 @@ class HeAPPlacer
|
|||||||
|
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
CellInfo *ci = cell.second.get();
|
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()) {
|
if (ci->bel != BelId()) {
|
||||||
Loc loc = ctx->getBelLocation(ci->bel);
|
Loc loc = ctx->getBelLocation(ci->bel);
|
||||||
cell_locs[cell.first].x = loc.x;
|
cell_locs[cell.first].x = loc.x;
|
||||||
@ -627,8 +644,9 @@ class HeAPPlacer
|
|||||||
int row = 0;
|
int row = 0;
|
||||||
solve_cells.clear();
|
solve_cells.clear();
|
||||||
// First clear the udata of all cells
|
// First clear the udata of all cells
|
||||||
for (auto &cell : ctx->cells)
|
for (auto &cell : ctx->cells) {
|
||||||
cell.second->udata = dont_solve;
|
cell.second->udata = dont_solve;
|
||||||
|
}
|
||||||
// Then update cells to be placed, which excludes cell children
|
// Then update cells to be placed, which excludes cell children
|
||||||
for (auto cell : place_cells) {
|
for (auto cell : place_cells) {
|
||||||
if (buckets && !buckets->count(ctx->getBelBucketForCellType(cell->type)))
|
if (buckets && !buckets->count(ctx->getBelBucketForCellType(cell->type)))
|
||||||
|
@ -128,7 +128,7 @@ struct Router2
|
|||||||
nets.at(i).cy = 0;
|
nets.at(i).cy = 0;
|
||||||
|
|
||||||
if (ni->driver.cell != nullptr) {
|
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).cx += drv_loc.x;
|
||||||
nets.at(i).cy += drv_loc.y;
|
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);
|
nets.at(i).bb.y1 = std::max(nets.at(i).bb.y1, ad.bb.y1);
|
||||||
}
|
}
|
||||||
// Add location to centroid sum
|
// 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).cx += usr_loc.x;
|
||||||
nets.at(i).cy += usr_loc.y;
|
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`.
|
- `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.
|
- `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.
|
- `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
|
||||||
|
|
||||||
`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
|
- `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
|
- `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
|
// 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
|
// 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
|
// 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
|
// 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;
|
bool have_iobuf_or_constr = false;
|
||||||
for (auto &cell : cells) {
|
for (auto &cell : cells) {
|
||||||
CellInfo *ci = cell.second.get();
|
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;
|
have_iobuf_or_constr = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user