diff options
author | gatecat <gatecat@ds0.me> | 2022-06-23 18:48:31 +0100 |
---|---|---|
committer | gatecat <gatecat@ds0.me> | 2022-07-08 14:30:57 +0200 |
commit | 09e388f453d9cf998391495349c88e5478b62e34 (patch) | |
tree | 004f2b14ed5a3b0584c4998d9f0a5598cc52ab28 /common/kernel | |
parent | 86396c41d64d2583ec1dffca4298e83d927f0762 (diff) | |
download | nextpnr-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.h | 8 | ||||
-rw-r--r-- | common/kernel/basectx.cc | 24 | ||||
-rw-r--r-- | common/kernel/basectx.h | 4 | ||||
-rw-r--r-- | common/kernel/context.cc | 5 | ||||
-rw-r--r-- | common/kernel/context.h | 18 | ||||
-rw-r--r-- | common/kernel/nextpnr_types.cc | 10 | ||||
-rw-r--r-- | common/kernel/nextpnr_types.h | 86 |
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; |