generic: Cell timing support

Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
David Shah 2019-04-04 16:30:47 +01:00
parent 3f98084021
commit f0cd51e6bc
8 changed files with 152 additions and 12 deletions

View File

@ -181,9 +181,9 @@ struct GraphicElement
float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0; float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0;
std::string text; std::string text;
GraphicElement() {}; GraphicElement(){};
GraphicElement(type_t type, style_t style, float x1, float y1, float x2, float y2, float z) : type(type), GraphicElement(type_t type, style_t style, float x1, float y1, float x2, float y2, float z)
style(style), x1(x1), y1(y1), x2(x2), y2(y2), z(z) {}; : type(type), style(style), x1(x1), y1(y1), x2(x2), y2(y2), z(z){};
}; };
struct Loc struct Loc

View File

@ -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. 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 ## 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. 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.

View File

@ -17,8 +17,8 @@
* *
*/ */
#include <math.h>
#include <iostream> #include <iostream>
#include <math.h>
#include "nextpnr.h" #include "nextpnr.h"
#include "placer1.h" #include "placer1.h"
#include "router1.h" #include "router1.h"
@ -200,9 +200,42 @@ void Arch::setDelayScaling(double scale, double offset)
args.delayOffset = 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 // Dummy for empty decals
decal_graphics[IdString()]; 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)) { if (!decal_graphics.count(decal)) {
std::cerr << "No decal named " << decal.str(this) << std::endl; std::cerr << "No decal named " << decal.str(this) << std::endl;
log_error("No decal named %s!\n", decal.c_str(this)); 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 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 // Get the port class, also setting clockPort if applicable
TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const 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 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 bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const

View File

@ -86,6 +86,33 @@ struct GroupInfo
DecalXY decalxy; 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 struct Arch : BaseCtx
{ {
std::string chipName; std::string chipName;
@ -106,6 +133,8 @@ struct Arch : BaseCtx
std::vector<std::vector<int>> tileBelDimZ; std::vector<std::vector<int>> tileBelDimZ;
std::vector<std::vector<int>> tilePipDimZ; std::vector<std::vector<int>> tilePipDimZ;
std::unordered_map<IdString, CellTiming> cellTiming;
void addWire(IdString name, IdString type, int x, int y); 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 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); void addAlias(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay);
@ -133,6 +162,11 @@ struct Arch : BaseCtx
void setLutK(int K); void setLutK(int K);
void setDelayScaling(double scale, double offset); 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. // Common Arch API. Every arch must provide the following methods.

View File

@ -42,8 +42,8 @@ void arch_wrap_python()
auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>()); auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>());
auto dxy_cls = class_<ContextualWrapper<DecalXY>>("DecalXY_", no_init); auto dxy_cls = class_<ContextualWrapper<DecalXY>>("DecalXY_", no_init);
readwrite_wrapper<DecalXY, decltype(&DecalXY::decal), &DecalXY::decal, conv_to_str<DecalId>, readwrite_wrapper<DecalXY, decltype(&DecalXY::decal), &DecalXY::decal, conv_to_str<DecalId>,
conv_from_str<DecalId>>::def_wrap(dxy_cls, "decal"); 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( readwrite_wrapper<DecalXY, decltype(&DecalXY::x), &DecalXY::x, pass_through<float>, pass_through<float>>::def_wrap(
dxy_cls, "x"); dxy_cls, "x");
readwrite_wrapper<DecalXY, decltype(&DecalXY::y), &DecalXY::y, pass_through<float>, pass_through<float>>::def_wrap( 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>, fn_wrapper_2a_v<Context, decltype(&Context::setDelayScaling), &Context::setDelayScaling, pass_through<double>,
pass_through<double>>::def_wrap(ctx_cls, "setDelayScaling", (arg("scale"), "offset")); 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(CellMap, "IdCellMap");
WRAP_MAP_UPTR(NetMap, "IdNetMap"); WRAP_MAP_UPTR(NetMap, "IdNetMap");
WRAP_VECTOR(const std::vector<IdString>, conv_to_str<IdString>); WRAP_VECTOR(const std::vector<IdString>, conv_to_str<IdString>);

View File

@ -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, - 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 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 - report.py stores design information after place-and-route to blinky.txt in place
of real bitstream generation of real bitstream generation

View File

@ -1,4 +1,4 @@
#!/usr/bin/bash #!/usr/bin/bash
set -ex set -ex
yosys -p "tcl ../synth/synth_generic.tcl 4 blinky.json" blinky.v 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 ../../nextpnr-generic --pre-pack simple.py --pre-place simple_timing.py --json blinky.json --post-route report.py

View File

@ -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))