aboutsummaryrefslogtreecommitdiffstats
path: root/common/kernel
diff options
context:
space:
mode:
authorgatecat <gatecat@ds0.me>2022-06-23 18:48:31 +0100
committergatecat <gatecat@ds0.me>2022-07-08 14:30:57 +0200
commit09e388f453d9cf998391495349c88e5478b62e34 (patch)
tree004f2b14ed5a3b0584c4998d9f0a5598cc52ab28 /common/kernel
parent86396c41d64d2583ec1dffca4298e83d927f0762 (diff)
downloadnextpnr-09e388f453d9cf998391495349c88e5478b62e34.tar.gz
nextpnr-09e388f453d9cf998391495349c88e5478b62e34.tar.bz2
nextpnr-09e388f453d9cf998391495349c88e5478b62e34.zip
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.
Diffstat (limited to 'common/kernel')
-rw-r--r--common/kernel/arch_pybindings_shared.h8
-rw-r--r--common/kernel/basectx.cc24
-rw-r--r--common/kernel/basectx.h4
-rw-r--r--common/kernel/context.cc5
-rw-r--r--common/kernel/context.h18
-rw-r--r--common/kernel/nextpnr_types.cc10
-rw-r--r--common/kernel/nextpnr_types.h86
7 files changed, 127 insertions, 28 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<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"); \ No newline at end of file
+ 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");
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<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;
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<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());
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
@@ -65,6 +65,24 @@ struct Context : Arch, DeterministicRNG
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();
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,6 +160,59 @@ struct PortInfo
struct Context;
+enum TimingPortClass
+{
+ TMG_CLOCK_INPUT, // Clock input to a sequential cell
+ TMG_GEN_CLOCK, // Generated clock output (PLL, DCC, etc)
+ TMG_REGISTER_INPUT, // Input to a register, with an associated clock (may also have comb. fanout too)
+ TMG_REGISTER_OUTPUT, // Output from a register
+ TMG_COMB_INPUT, // Combinational input, no paths end here
+ TMG_COMB_OUTPUT, // Combinational output, no paths start here
+ TMG_STARTPOINT, // Unclocked primary startpoint, such as an IO cell output
+ TMG_ENDPOINT, // Unclocked primary endpoint, such as an IO cell input
+ TMG_IGNORE, // Asynchronous to all clocks, "don't care", and should be ignored (false path) for analysis
+};
+
+enum ClockEdge
+{
+ RISING_EDGE,
+ FALLING_EDGE
+};
+
+struct TimingClockingInfo
+{
+ IdString clock_port; // Port name of clock domain
+ ClockEdge edge;
+ DelayPair setup, hold; // Input timing checks
+ 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){};
@@ -179,6 +232,8 @@ struct CellInfo : ArchCellInfo
Region *region = nullptr;
+ std::unique_ptr<PseudoCell> pseudo_cell{};
+
void addInput(IdString name);
void addOutput(IdString name);
void addInout(IdString name);
@@ -190,6 +245,10 @@ struct CellInfo : ArchCellInfo
// 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);
@@ -212,33 +271,6 @@ struct CellInfo : ArchCellInfo
int new_offset, bool new_brackets, int width);
};
-enum TimingPortClass
-{
- TMG_CLOCK_INPUT, // Clock input to a sequential cell
- TMG_GEN_CLOCK, // Generated clock output (PLL, DCC, etc)
- TMG_REGISTER_INPUT, // Input to a register, with an associated clock (may also have comb. fanout too)
- TMG_REGISTER_OUTPUT, // Output from a register
- TMG_COMB_INPUT, // Combinational input, no paths end here
- TMG_COMB_OUTPUT, // Combinational output, no paths start here
- TMG_STARTPOINT, // Unclocked primary startpoint, such as an IO cell output
- TMG_ENDPOINT, // Unclocked primary endpoint, such as an IO cell input
- TMG_IGNORE, // Asynchronous to all clocks, "don't care", and should be ignored (false path) for analysis
-};
-
-enum ClockEdge
-{
- RISING_EDGE,
- FALLING_EDGE
-};
-
-struct TimingClockingInfo
-{
- IdString clock_port; // Port name of clock domain
- ClockEdge edge;
- DelayPair setup, hold; // Input timing checks
- DelayQuad clockToQ; // Output clock-to-Q time
-};
-
struct ClockConstraint
{
DelayPair high;