generic: Cell timing support
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
3f98084021
commit
f0cd51e6bc
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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>);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
../../nextpnr-generic --pre-pack simple.py --pre-place simple_timing.py --json blinky.json --post-route report.py
|
15
generic/examples/simple_timing.py
Normal file
15
generic/examples/simple_timing.py
Normal 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))
|
Loading…
Reference in New Issue
Block a user