From f0cd51e6bc58f3dfd1185fd53ad970ba634359f2 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 4 Apr 2019 16:30:47 +0100 Subject: generic: Cell timing support Signed-off-by: David Shah --- common/nextpnr.h | 6 ++-- docs/generic.md | 20 ++++++++++++ generic/arch.cc | 65 +++++++++++++++++++++++++++++++++++---- generic/arch.h | 34 ++++++++++++++++++++ generic/arch_pybindings.cc | 20 ++++++++++-- generic/examples/README.md | 2 ++ generic/examples/simple.sh | 2 +- generic/examples/simple_timing.py | 15 +++++++++ 8 files changed, 152 insertions(+), 12 deletions(-) create mode 100644 generic/examples/simple_timing.py 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 #include +#include #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 &Arch::getDecalGraphics(DecalId decal) const { +const std::vector &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 +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX CellDelayKey &dk) const noexcept + { + std::size_t seed = std::hash()(dk.from); + seed ^= std::hash()(dk.to) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } +}; +} // namespace std +NEXTPNR_NAMESPACE_BEGIN + +struct CellTiming +{ + std::unordered_map portClasses; + std::unordered_map combDelays; + std::unordered_map> clockingInfo; +}; + struct Arch : BaseCtx { std::string chipName; @@ -106,6 +133,8 @@ struct Arch : BaseCtx std::vector> tileBelDimZ; std::vector> tilePipDimZ; + std::unordered_map 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_, boost::noncopyable>("Arch", init()); auto dxy_cls = class_>("DecalXY_", no_init); - readwrite_wrapper, - conv_from_str>::def_wrap(dxy_cls, "decal"); + readwrite_wrapper, + conv_from_str>::def_wrap(dxy_cls, "decal"); readwrite_wrapper, pass_through>::def_wrap( dxy_cls, "x"); readwrite_wrapper, pass_through>::def_wrap( @@ -213,6 +213,22 @@ void arch_wrap_python() fn_wrapper_2a_v, pass_through>::def_wrap(ctx_cls, "setDelayScaling", (arg("scale"), "offset")); + fn_wrapper_2a_v, conv_from_str>::def_wrap(ctx_cls, "addCellTimingClock", + (arg("cell"), "port")); + fn_wrapper_4a_v, conv_from_str, conv_from_str, + pass_through>::def_wrap(ctx_cls, "addCellTimingDelay", + (arg("cell"), "fromPort", "toPort", "delay")); + fn_wrapper_5a_v, conv_from_str, conv_from_str, pass_through, + pass_through>::def_wrap(ctx_cls, "addCellTimingSetupHold", + (arg("cell"), "port", "clock", "setup", "hold")); + fn_wrapper_4a_v, conv_from_str, conv_from_str, + pass_through>::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, conv_to_str); 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 -- cgit v1.2.3