aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/nextpnr.h6
-rw-r--r--docs/generic.md20
-rw-r--r--generic/arch.cc65
-rw-r--r--generic/arch.h34
-rw-r--r--generic/arch_pybindings.cc20
-rw-r--r--generic/examples/README.md2
-rwxr-xr-xgeneric/examples/simple.sh2
-rw-r--r--generic/examples/simple_timing.py15
8 files changed, 152 insertions, 12 deletions
diff --git a/common/nextpnr.h b/common/nextpnr.h
index 79dbbaca..fc49300e 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -181,9 +181,9 @@ struct GraphicElement
float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0;
std::string text;
- GraphicElement() {};
- GraphicElement(type_t type, style_t style, float x1, float y1, float x2, float y2, float z) : type(type),
- style(style), x1(x1), y1(y1), x2(x2), y2(y2), z(z) {};
+ GraphicElement(){};
+ GraphicElement(type_t type, style_t style, float x1, float y1, float x2, float y2, float z)
+ : type(type), style(style), x1(x1), y1(y1), x2(x2), y2(y2), z(z){};
};
struct Loc
diff --git a/docs/generic.md b/docs/generic.md
index 2f884274..d6ddbfb6 100644
--- a/docs/generic.md
+++ b/docs/generic.md
@@ -74,6 +74,26 @@ Sets the number of input pins a LUT in the architecture has. Only affects the ge
Set the linear scaling vs distance and fixed offset (both values in nanoseconds) for routing delay estimates.
+### void addCellTimingClock(IdString cell, IdString port);
+
+Set the timing class of a port on a particular cell to a clock input.
+
+_NOTE: All cell timing functions apply to an individual named cell and not a cell type. This is because
+cell-specific configuration might affect timing, e.g. whether or not the register is used for a slice._
+
+### void addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayInfo delay);
+
+Specify the combinational delay between two ports of a cell, and set the timing class of
+ those ports as combinational input/output.
+
+### void addCellTimingSetupHold(IdString cell, IdString port, IdString clock, DelayInfo setup, DelayInfo hold);
+
+Specify setup and hold timings for a port of a cell, and set the timing class of that port as register input.
+
+### void addCellTimingClockToOut(IdString cell, IdString port, IdString clock, DelayInfo clktoq);
+
+Specify clock-to-out time for a port of a cell, and set the timing class of that port as register output.
+
## Generic Packer
The generic packer combines K-input LUTs (`LUT` cells) and simple D-type flip flops (`DFF` cells) (posedge clock only, no set/reset or enable) into a `GENERIC_SLICE` cell. It also inserts `GENERIC_IOB`s onto any top level IO pins without an IO buffer.
diff --git a/generic/arch.cc b/generic/arch.cc
index 08146b65..772d3534 100644
--- a/generic/arch.cc
+++ b/generic/arch.cc
@@ -17,8 +17,8 @@
*
*/
-#include <math.h>
#include <iostream>
+#include <math.h>
#include "nextpnr.h"
#include "placer1.h"
#include "router1.h"
@@ -200,9 +200,42 @@ void Arch::setDelayScaling(double scale, double offset)
args.delayOffset = offset;
}
+void Arch::addCellTimingClock(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_CLOCK_INPUT; }
+
+void Arch::addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayInfo delay)
+{
+ if (get_or_default(cellTiming[cell].portClasses, fromPort, TMG_IGNORE) == TMG_IGNORE)
+ cellTiming[cell].portClasses[fromPort] = TMG_COMB_INPUT;
+ if (get_or_default(cellTiming[cell].portClasses, toPort, TMG_IGNORE) == TMG_IGNORE)
+ cellTiming[cell].portClasses[toPort] = TMG_COMB_OUTPUT;
+ cellTiming[cell].combDelays[CellDelayKey{fromPort, toPort}] = delay;
+}
+
+void Arch::addCellTimingSetupHold(IdString cell, IdString port, IdString clock, DelayInfo setup, DelayInfo hold)
+{
+ TimingClockingInfo ci;
+ ci.clock_port = clock;
+ ci.edge = RISING_EDGE;
+ ci.setup = setup;
+ ci.hold = hold;
+ cellTiming[cell].clockingInfo[port].push_back(ci);
+ cellTiming[cell].portClasses[port] = TMG_REGISTER_INPUT;
+}
+
+void Arch::addCellTimingClockToOut(IdString cell, IdString port, IdString clock, DelayInfo clktoq)
+{
+ TimingClockingInfo ci;
+ ci.clock_port = clock;
+ ci.edge = RISING_EDGE;
+ ci.clockToQ = clktoq;
+ cellTiming[cell].clockingInfo[port].push_back(ci);
+ cellTiming[cell].portClasses[port] = TMG_REGISTER_OUTPUT;
+}
+
// ---------------------------------------------------------------
-Arch::Arch(ArchArgs args) : chipName("generic"), args(args) {
+Arch::Arch(ArchArgs args) : chipName("generic"), args(args)
+{
// Dummy for empty decals
decal_graphics[IdString()];
}
@@ -473,7 +506,8 @@ bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); }
// ---------------------------------------------------------------
-const std::vector<GraphicElement> &Arch::getDecalGraphics(DecalId decal) const {
+const std::vector<GraphicElement> &Arch::getDecalGraphics(DecalId decal) const
+{
if (!decal_graphics.count(decal)) {
std::cerr << "No decal named " << decal.str(this) << std::endl;
log_error("No decal named %s!\n", decal.c_str(this));
@@ -493,18 +527,37 @@ DecalXY Arch::getGroupDecal(GroupId group) const { return groups.at(group).decal
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
{
- return false;
+ if (!cellTiming.count(cell->name))
+ return false;
+ const auto &tmg = cellTiming.at(cell->name);
+ auto fnd = tmg.combDelays.find(CellDelayKey{fromPort, toPort});
+ if (fnd != tmg.combDelays.end()) {
+ delay = fnd->second;
+ return true;
+ } else {
+ return false;
+ }
}
// Get the port class, also setting clockPort if applicable
TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
{
- return TMG_IGNORE;
+ if (!cellTiming.count(cell->name))
+ return TMG_IGNORE;
+ const auto &tmg = cellTiming.at(cell->name);
+ if (tmg.clockingInfo.count(port))
+ clockInfoCount = int(tmg.clockingInfo.at(port).size());
+ else
+ clockInfoCount = 0;
+ return get_or_default(tmg.portClasses, port, TMG_IGNORE);
}
TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
{
- NPNR_ASSERT_FALSE("no clocking info for generic");
+ NPNR_ASSERT(cellTiming.count(cell->name));
+ const auto &tmg = cellTiming.at(cell->name);
+ NPNR_ASSERT(tmg.clockingInfo.count(port));
+ return tmg.clockingInfo.at(port).at(index);
}
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
diff --git a/generic/arch.h b/generic/arch.h
index 02017c8d..92a2c331 100644
--- a/generic/arch.h
+++ b/generic/arch.h
@@ -86,6 +86,33 @@ struct GroupInfo
DecalXY decalxy;
};
+struct CellDelayKey
+{
+ IdString from, to;
+ inline bool operator==(const CellDelayKey &other) const { return from == other.from && to == other.to; }
+};
+
+NEXTPNR_NAMESPACE_END
+namespace std {
+template <> struct hash<NEXTPNR_NAMESPACE_PREFIX CellDelayKey>
+{
+ std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX CellDelayKey &dk) const noexcept
+ {
+ std::size_t seed = std::hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(dk.from);
+ seed ^= std::hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(dk.to) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+ return seed;
+ }
+};
+} // namespace std
+NEXTPNR_NAMESPACE_BEGIN
+
+struct CellTiming
+{
+ std::unordered_map<IdString, TimingPortClass> portClasses;
+ std::unordered_map<CellDelayKey, DelayInfo> combDelays;
+ std::unordered_map<IdString, std::vector<TimingClockingInfo>> clockingInfo;
+};
+
struct Arch : BaseCtx
{
std::string chipName;
@@ -106,6 +133,8 @@ struct Arch : BaseCtx
std::vector<std::vector<int>> tileBelDimZ;
std::vector<std::vector<int>> tilePipDimZ;
+ std::unordered_map<IdString, CellTiming> cellTiming;
+
void addWire(IdString name, IdString type, int x, int y);
void addPip(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay, Loc loc);
void addAlias(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay);
@@ -133,6 +162,11 @@ struct Arch : BaseCtx
void setLutK(int K);
void setDelayScaling(double scale, double offset);
+ void addCellTimingClock(IdString cell, IdString port);
+ void addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayInfo delay);
+ void addCellTimingSetupHold(IdString cell, IdString port, IdString clock, DelayInfo setup, DelayInfo hold);
+ void addCellTimingClockToOut(IdString cell, IdString port, IdString clock, DelayInfo clktoq);
+
// ---------------------------------------------------------------
// Common Arch API. Every arch must provide the following methods.
diff --git a/generic/arch_pybindings.cc b/generic/arch_pybindings.cc
index 5eb2f2c8..8526e409 100644
--- a/generic/arch_pybindings.cc
+++ b/generic/arch_pybindings.cc
@@ -42,8 +42,8 @@ void arch_wrap_python()
auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>());
auto dxy_cls = class_<ContextualWrapper<DecalXY>>("DecalXY_", no_init);
- readwrite_wrapper<DecalXY, decltype(&DecalXY::decal), &DecalXY::decal, conv_to_str<DecalId>,
- conv_from_str<DecalId>>::def_wrap(dxy_cls, "decal");
+ readwrite_wrapper<DecalXY, decltype(&DecalXY::decal), &DecalXY::decal, conv_to_str<DecalId>,
+ conv_from_str<DecalId>>::def_wrap(dxy_cls, "decal");
readwrite_wrapper<DecalXY, decltype(&DecalXY::x), &DecalXY::x, pass_through<float>, pass_through<float>>::def_wrap(
dxy_cls, "x");
readwrite_wrapper<DecalXY, decltype(&DecalXY::y), &DecalXY::y, pass_through<float>, pass_through<float>>::def_wrap(
@@ -213,6 +213,22 @@ void arch_wrap_python()
fn_wrapper_2a_v<Context, decltype(&Context::setDelayScaling), &Context::setDelayScaling, pass_through<double>,
pass_through<double>>::def_wrap(ctx_cls, "setDelayScaling", (arg("scale"), "offset"));
+ fn_wrapper_2a_v<Context, decltype(&Context::addCellTimingClock), &Context::addCellTimingClock,
+ conv_from_str<IdString>, conv_from_str<IdString>>::def_wrap(ctx_cls, "addCellTimingClock",
+ (arg("cell"), "port"));
+ fn_wrapper_4a_v<Context, decltype(&Context::addCellTimingDelay), &Context::addCellTimingDelay,
+ conv_from_str<IdString>, conv_from_str<IdString>, conv_from_str<IdString>,
+ pass_through<DelayInfo>>::def_wrap(ctx_cls, "addCellTimingDelay",
+ (arg("cell"), "fromPort", "toPort", "delay"));
+ fn_wrapper_5a_v<Context, decltype(&Context::addCellTimingSetupHold), &Context::addCellTimingSetupHold,
+ conv_from_str<IdString>, conv_from_str<IdString>, conv_from_str<IdString>, pass_through<DelayInfo>,
+ pass_through<DelayInfo>>::def_wrap(ctx_cls, "addCellTimingSetupHold",
+ (arg("cell"), "port", "clock", "setup", "hold"));
+ fn_wrapper_4a_v<Context, decltype(&Context::addCellTimingClockToOut), &Context::addCellTimingClockToOut,
+ conv_from_str<IdString>, conv_from_str<IdString>, conv_from_str<IdString>,
+ pass_through<DelayInfo>>::def_wrap(ctx_cls, "addCellTimingClockToOut",
+ (arg("cell"), "port", "clock", "clktoq"));
+
WRAP_MAP_UPTR(CellMap, "IdCellMap");
WRAP_MAP_UPTR(NetMap, "IdNetMap");
WRAP_VECTOR(const std::vector<IdString>, conv_to_str<IdString>);
diff --git a/generic/examples/README.md b/generic/examples/README.md
index 4641f542..dd154a51 100644
--- a/generic/examples/README.md
+++ b/generic/examples/README.md
@@ -4,6 +4,8 @@ This contains a simple, artificial, example of the nextpnr generic API.
- simple.py procedurally generates a simple FPGA architecture with IO at the edges,
logic slices in all other tiles, and interconnect only between adjacent tiles
+
+ - simple_timing.py annotates cells with timing data (this is a separate script that must be run after packing)
- report.py stores design information after place-and-route to blinky.txt in place
of real bitstream generation
diff --git a/generic/examples/simple.sh b/generic/examples/simple.sh
index ed800639..2e8d6180 100755
--- a/generic/examples/simple.sh
+++ b/generic/examples/simple.sh
@@ -1,4 +1,4 @@
#!/usr/bin/bash
set -ex
yosys -p "tcl ../synth/synth_generic.tcl 4 blinky.json" blinky.v
-../../nextpnr-generic --pre-pack simple.py --json blinky.json --post-route report.py \ No newline at end of file
+../../nextpnr-generic --pre-pack simple.py --pre-place simple_timing.py --json blinky.json --post-route report.py \ No newline at end of file
diff --git a/generic/examples/simple_timing.py b/generic/examples/simple_timing.py
new file mode 100644
index 00000000..a955c8d7
--- /dev/null
+++ b/generic/examples/simple_timing.py
@@ -0,0 +1,15 @@
+for cname, cell in ctx.cells:
+ if cell.type != "GENERIC_SLICE":
+ continue
+ if cname in ("$PACKER_GND", "$PACKER_VCC"):
+ continue
+ K = int(cell.params["K"])
+ if cell.params["FF_USED"] == "1":
+ ctx.addCellTimingClock(cell=cname, port="CLK")
+ for i in range(K):
+ ctx.addCellTimingSetupHold(cell=cname, port="I[%d]" % i, clock="CLK",
+ setup=ctx.getDelayFromNS(0.2), hold=ctx.getDelayFromNS(0))
+ ctx.addCellTimingClockToOut(cell=cname, port="Q", clock="CLK", clktoq=ctx.getDelayFromNS(0.2))
+ else:
+ for i in range(K):
+ ctx.addCellTimingDelay(cell=cname, fromPort="I[%d]" % i, toPort="Q", delay=ctx.getDelayFromNS(0.2)) \ No newline at end of file