aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Shah <davey1576@gmail.com>2018-11-22 11:55:25 +0000
committerGitHub <noreply@github.com>2018-11-22 11:55:25 +0000
commit17315901605ed46a5c3b5c743453e054c50013ce (patch)
tree005a6c37bb6f59fbb7ef9cae52b79fbce5e0c9cc
parent48c793bd4d26f28094c714b57cfa1491904ca4e3 (diff)
parent72b53016c01d7fa2028dd84b356efeed80e78330 (diff)
downloadnextpnr-17315901605ed46a5c3b5c743453e054c50013ce.tar.gz
nextpnr-17315901605ed46a5c3b5c743453e054c50013ce.tar.bz2
nextpnr-17315901605ed46a5c3b5c743453e054c50013ce.zip
Merge pull request #122 from YosysHQ/ecp5_timing
ecp5: Use cell and pip timings from the Trellis database
-rw-r--r--common/timing.cc18
-rw-r--r--ecp5/arch.cc175
-rw-r--r--ecp5/arch.h76
-rw-r--r--ecp5/archdefs.h17
-rw-r--r--ecp5/constids.inc29
-rw-r--r--ecp5/family.cmake6
-rw-r--r--ecp5/main.cc28
-rw-r--r--ecp5/project.cc2
-rwxr-xr-xecp5/trellis_import.py155
9 files changed, 372 insertions, 134 deletions
diff --git a/common/timing.cc b/common/timing.cc
index 80be554c..002ccda9 100644
--- a/common/timing.cc
+++ b/common/timing.cc
@@ -593,7 +593,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
if (print_path) {
auto print_path_report = [ctx](ClockPair &clocks, PortRefVector &crit_path) {
- delay_t total = 0;
+ delay_t total = 0, logic_total = 0, route_total = 0;
auto &front = crit_path.front();
auto &front_port = front->cell->ports.at(front->port);
auto &front_driver = front_port.net->driver;
@@ -608,6 +608,9 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
if (clknet != nullptr && clknet->name == clocks.start.clock &&
clockInfo.edge == clocks.start.edge) {
last_port = clockInfo.clock_port;
+ total += clockInfo.clockToQ.maxDelay();
+ logic_total += clockInfo.clockToQ.maxDelay();
+ break;
}
}
}
@@ -627,10 +630,12 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
ctx->getCellDelay(sink_cell, last_port, driver.port, comb_delay);
}
total += comb_delay.maxDelay();
+ logic_total += comb_delay.maxDelay();
log_info("%4.1f %4.1f Source %s.%s\n", ctx->getDelayNS(comb_delay.maxDelay()), ctx->getDelayNS(total),
driver_cell->name.c_str(ctx), driver.port.c_str(ctx));
auto net_delay = ctx->getNetinfoRouteDelay(net, *sink);
total += net_delay;
+ route_total += net_delay;
auto driver_loc = ctx->getBelLocation(driver_cell->bel);
auto sink_loc = ctx->getBelLocation(sink_cell->bel);
log_info("%4.1f %4.1f Net %s budget %f ns (%d,%d) -> (%d,%d)\n", ctx->getDelayNS(net_delay),
@@ -658,6 +663,17 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
}
last_port = sink->port;
}
+ int clockCount = 0;
+ auto sinkClass = ctx->getPortTimingClass(crit_path.back()->cell, crit_path.back()->port, clockCount);
+ if (sinkClass == TMG_REGISTER_INPUT && clockCount > 0) {
+ auto sinkClockInfo = ctx->getPortClockingInfo(crit_path.back()->cell, crit_path.back()->port, 0);
+ delay_t setup = sinkClockInfo.setup.maxDelay();
+ total += setup;
+ logic_total += setup;
+ log_info("%4.1f %4.1f Setup %s.%s\n", ctx->getDelayNS(setup), ctx->getDelayNS(total),
+ crit_path.back()->cell->name.c_str(ctx), crit_path.back()->port.c_str(ctx));
+ }
+ log_info("%.1f ns logic, %.1f ns routing\n", ctx->getDelayNS(logic_total), ctx->getDelayNS(route_total));
};
for (auto &clock : clock_reports) {
diff --git a/ecp5/arch.cc b/ecp5/arch.cc
index afea8d4a..fc3c97bf 100644
--- a/ecp5/arch.cc
+++ b/ecp5/arch.cc
@@ -96,7 +96,7 @@ Arch::Arch(ArchArgs args) : args(args)
break;
}
}
-
+ speed_grade = &(chip_info->speed_grades[args.speed]);
if (!package_info)
log_error("Unsupported package '%s' for '%s'.\n", args.package.c_str(), getChipName().c_str());
@@ -400,7 +400,7 @@ BelId Arch::getBelByLocation(Loc loc) const
delay_t Arch::estimateDelay(WireId src, WireId dst) const
{
- return 170 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y));
+ return (240 - 20 * args.speed) * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y));
}
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
@@ -409,7 +409,7 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
auto driver_loc = getBelLocation(driver.cell->bel);
auto sink_loc = getBelLocation(sink.cell->bel);
- return 170 * (abs(driver_loc.x - sink_loc.x) + abs(driver_loc.y - sink_loc.y));
+ return (240 - 20 * args.speed) * (abs(driver_loc.x - sink_loc.x) + abs(driver_loc.y - sink_loc.y));
}
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }
@@ -422,7 +422,34 @@ bool Arch::route()
{
route_ecp5_globals(getCtx());
assign_budget(getCtx(), true);
- return router1(getCtx(), Router1Cfg(getCtx()));
+
+ bool result = router1(getCtx(), Router1Cfg(getCtx()));
+#if 0
+ std::vector<std::pair<WireId, int>> fanout_vector;
+ std::copy(wire_fanout.begin(), wire_fanout.end(), std::back_inserter(fanout_vector));
+ std::sort(fanout_vector.begin(), fanout_vector.end(), [](const std::pair<WireId, int> &a, const std::pair<WireId, int> &b) {
+ return a.second > b.second;
+ });
+ for (size_t i = 0; i < std::min(size_t(20), fanout_vector.size()); i++)
+ log_info(" fanout %s = %d\n", getWireName(fanout_vector[i].first).c_str(this), fanout_vector[i].second);
+ log_break();
+ PipId slowest_pip;
+ delay_t slowest_pipdelay = 0;
+ for (auto pip : pip_to_net) {
+ if (pip.second) {
+ delay_t dly = getPipDelay(pip.first).maxDelay();
+ if (dly > slowest_pipdelay) {
+ slowest_pip = pip.first;
+ slowest_pipdelay = dly;
+ }
+ }
+ }
+ log_info(" slowest pip %s = %.02f ns\n", getPipName(slowest_pip).c_str(this), getDelayNS(slowest_pipdelay));
+ log_info(" fanout %d\n", wire_fanout[getPipSrcWire(slowest_pip)]);
+ log_info(" base %d adder %d\n", speed_grade->pip_classes[locInfo(slowest_pip)->pip_data[slowest_pip.index].timing_class].max_base_delay,
+ speed_grade->pip_classes[locInfo(slowest_pip)->pip_data[slowest_pip.index].timing_class].max_fanout_adder);
+#endif
+ return result;
}
// -----------------------------------------------------------------------
@@ -482,94 +509,74 @@ DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; };
// -----------------------------------------------------------------------
-bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
+bool Arch::getDelayFromTimingDatabase(IdString tctype, IdString from, IdString to, DelayInfo &delay) const
{
- // Data for -8 grade
- if (cell->type == id_TRELLIS_SLICE) {
- bool has_carry = str_or_default(cell->params, id("MODE"), "LOGIC") == "CCU2";
- if (fromPort == id_A0 || fromPort == id_B0 || fromPort == id_C0 || fromPort == id_D0) {
- if (toPort == id_F0) {
- delay.delay = 180;
- return true;
- } else if (has_carry && toPort == id_F1) {
- delay.delay = 500;
- return true;
- } else if (has_carry && toPort == id_FCO) {
- delay.delay = 355;
- return true;
- } else if (toPort == id_OFX0) {
- delay.delay = 306;
- return true;
+ for (int i = 0; i < speed_grade->num_cell_timings; i++) {
+ const auto &tc = speed_grade->cell_timings[i];
+ if (tc.cell_type == tctype.index) {
+ for (int j = 0; j < tc.num_prop_delays; j++) {
+ const auto &dly = tc.prop_delays[j];
+ if (dly.from_port == from.index && dly.to_port == to.index) {
+ delay.max_delay = dly.max_delay;
+ delay.min_delay = dly.min_delay;
+ return true;
+ }
}
+ return false;
}
+ }
+ NPNR_ASSERT_FALSE("failed to find timing cell in db");
+}
- if (fromPort == id_A1 || fromPort == id_B1 || fromPort == id_C1 || fromPort == id_D1) {
- if (toPort == id_F1) {
- delay.delay = 180;
- return true;
- } else if (has_carry && toPort == id_FCO) {
- delay.delay = 355;
- return true;
- } else if (toPort == id_OFX0) {
- delay.delay = 306;
- return true;
+void Arch::getSetupHoldFromTimingDatabase(IdString tctype, IdString clock, IdString port, DelayInfo &setup,
+ DelayInfo &hold) const
+{
+ for (int i = 0; i < speed_grade->num_cell_timings; i++) {
+ const auto &tc = speed_grade->cell_timings[i];
+ if (tc.cell_type == tctype.index) {
+ for (int j = 0; j < tc.num_setup_holds; j++) {
+ const auto &sh = tc.setup_holds[j];
+ if (sh.clock_port == clock.index && sh.sig_port == port.index) {
+ setup.max_delay = sh.max_setup;
+ setup.min_delay = sh.min_setup;
+ hold.max_delay = sh.max_hold;
+ hold.min_delay = sh.min_hold;
+ return;
+ }
}
}
+ }
+ NPNR_ASSERT_FALSE("failed to find timing cell in db");
+}
- if (has_carry && fromPort == id_FCI) {
- if (toPort == id_F0) {
- delay.delay = 328;
- return true;
- } else if (toPort == id_F1) {
- delay.delay = 349;
- return true;
- } else if (toPort == id_FCO) {
- delay.delay = 56;
- return true;
- }
- }
+bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
+{
- if (fromPort == id_CLK && (toPort == id_Q0 || toPort == id_Q1)) {
- delay.delay = 395;
- return true;
+ // Data for -8 grade
+ if (cell->type == id_TRELLIS_SLICE) {
+ bool has_carry = str_or_default(cell->params, id("MODE"), "LOGIC") == "CCU2";
+ if (fromPort == id_A0 || fromPort == id_B0 || fromPort == id_C0 || fromPort == id_D0 || fromPort == id_A1 ||
+ fromPort == id_B1 || fromPort == id_C1 || fromPort == id_D1 || fromPort == id_M0 || fromPort == id_FCI) {
+ return getDelayFromTimingDatabase(has_carry ? id_SCCU2C : id_SLOGICB, fromPort, toPort, delay);
}
- if (fromPort == id_M0 && toPort == id_OFX0) {
- delay.delay = 193;
- return true;
- }
-#if 0 // FIXME
- if (fromPort == id_WCK && (toPort == id_F0 || toPort == id_F1)) {
- delay.delay = 717;
- return true;
- }
-#endif
if ((fromPort == id_A0 && toPort == id_WADO3) || (fromPort == id_A1 && toPort == id_WDO1) ||
(fromPort == id_B0 && toPort == id_WADO1) || (fromPort == id_B1 && toPort == id_WDO3) ||
(fromPort == id_C0 && toPort == id_WADO2) || (fromPort == id_C1 && toPort == id_WDO0) ||
(fromPort == id_D0 && toPort == id_WADO0) || (fromPort == id_D1 && toPort == id_WDO2)) {
- delay.delay = 0;
+ delay.min_delay = 0;
+ delay.max_delay = 0;
return true;
}
return false;
} else if (cell->type == id_DCCA) {
if (fromPort == id_CLKI && toPort == id_CLKO) {
- delay.delay = 0;
+ delay.min_delay = 0;
+ delay.max_delay = 0;
return true;
}
return false;
} else if (cell->type == id_DP16KD) {
- if (fromPort == id_CLKA) {
- if (toPort.str(this).substr(0, 3) == "DOA") {
- delay.delay = 4260;
- return true;
- }
- } else if (fromPort == id_CLKB) {
- if (toPort.str(this).substr(0, 3) == "DOB") {
- delay.delay = 4280;
- return true;
- }
- }
return false;
} else {
return false;
@@ -669,9 +676,9 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
{
TimingClockingInfo info;
- info.setup.delay = 0;
- info.hold.delay = 0;
- info.clockToQ.delay = 0;
+ info.setup = getDelayFromNS(0);
+ info.hold = getDelayFromNS(0);
+ info.clockToQ = getDelayFromNS(0);
if (cell->type == id_TRELLIS_SLICE) {
int sd0 = int_or_default(cell->params, id("REG0_SD"), 0), sd1 = int_or_default(cell->params, id("REG1_SD"), 0);
@@ -679,18 +686,18 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
port == id_WAD3 || port == id_WRE) {
info.edge = RISING_EDGE;
info.clock_port = id_WCK;
- info.setup.delay = 100;
- info.hold.delay = 0;
+ getSetupHoldFromTimingDatabase(id_SDPRAME, id_WCK, port, info.setup, info.hold);
} else if (port == id_DI0 || port == id_DI1 || port == id_CE || port == id_LSR || (sd0 == 1 && port == id_M0) ||
(sd1 == 1 && port == id_M1)) {
info.edge = cell->sliceInfo.clkmux == id("INV") ? FALLING_EDGE : RISING_EDGE;
info.clock_port = id_CLK;
- info.setup.delay = 100;
- info.hold.delay = 0;
+ getSetupHoldFromTimingDatabase(id_SLOGICB, id_CLK, port, info.setup, info.hold);
+
} else {
info.edge = cell->sliceInfo.clkmux == id("INV") ? FALLING_EDGE : RISING_EDGE;
info.clock_port = id_CLK;
- info.clockToQ.delay = 395;
+ bool is_path = getDelayFromTimingDatabase(id_SLOGICB, id_CLK, port, info.clockToQ);
+ NPNR_ASSERT(is_path);
}
} else if (cell->type == id_DP16KD) {
std::string port_name = port.str(this);
@@ -711,10 +718,12 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
? FALLING_EDGE
: RISING_EDGE;
if (cell->ports.at(port).type == PORT_OUT) {
- info.clockToQ.delay = 4280;
+ bool is_path = getDelayFromTimingDatabase(id_DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG, info.clock_port, port,
+ info.clockToQ);
+ NPNR_ASSERT(is_path);
} else {
- info.setup.delay = 100;
- info.hold.delay = 0;
+ getSetupHoldFromTimingDatabase(id_DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG, info.clock_port, port, info.setup,
+ info.hold);
}
} else if (cell->type == id_DCUA) {
std::string prefix = port.str(this).substr(0, 9);
@@ -728,10 +737,10 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
else if (prefix == "CH1_FF_RX")
info.clock_port = id_CH1_FF_RXI_CLK;
if (cell->ports.at(port).type == PORT_OUT) {
- info.clockToQ.delay = 660;
+ info.clockToQ = getDelayFromNS(0.7);
} else {
- info.setup.delay = 1000;
- info.hold.delay = 0;
+ info.setup = getDelayFromNS(1);
+ info.hold = getDelayFromNS(0);
}
}
return info;
diff --git a/ecp5/arch.h b/ecp5/arch.h
index aa3c5348..a68673f4 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -71,7 +71,7 @@ NPNR_PACKED_STRUCT(struct BelPortPOD {
NPNR_PACKED_STRUCT(struct PipInfoPOD {
LocationPOD rel_src_loc, rel_dst_loc;
int32_t src_idx, dst_idx;
- int32_t delay;
+ int32_t timing_class;
int16_t tile_type;
int8_t pip_type;
int8_t padding_0;
@@ -151,6 +151,44 @@ NPNR_PACKED_STRUCT(struct GlobalInfoPOD {
int16_t spine_col;
});
+NPNR_PACKED_STRUCT(struct CellPropDelayPOD {
+ int32_t from_port;
+ int32_t to_port;
+ int32_t min_delay;
+ int32_t max_delay;
+});
+
+NPNR_PACKED_STRUCT(struct CellSetupHoldPOD {
+ int32_t sig_port;
+ int32_t clock_port;
+ int32_t min_setup;
+ int32_t max_setup;
+ int32_t min_hold;
+ int32_t max_hold;
+});
+
+NPNR_PACKED_STRUCT(struct CellTimingPOD {
+ int32_t cell_type;
+ int32_t num_prop_delays;
+ int32_t num_setup_holds;
+ RelPtr<CellPropDelayPOD> prop_delays;
+ RelPtr<CellSetupHoldPOD> setup_holds;
+});
+
+NPNR_PACKED_STRUCT(struct PipDelayPOD {
+ int32_t min_base_delay;
+ int32_t max_base_delay;
+ int32_t min_fanout_adder;
+ int32_t max_fanout_adder;
+});
+
+NPNR_PACKED_STRUCT(struct SpeedGradePOD {
+ int32_t num_cell_timings;
+ int32_t num_pip_classes;
+ RelPtr<CellTimingPOD> cell_timings;
+ RelPtr<PipDelayPOD> pip_classes;
+});
+
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
int32_t width, height;
int32_t num_tiles;
@@ -163,6 +201,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD {
RelPtr<PackageInfoPOD> package_info;
RelPtr<PIOInfoPOD> pio_info;
RelPtr<TileInfoPOD> tile_info;
+ RelPtr<SpeedGradePOD> speed_grades;
});
#if defined(_MSC_VER)
@@ -400,13 +439,20 @@ struct ArchArgs
LFE5UM5G_85F,
} type = NONE;
std::string package;
- int speed = 6;
+ enum SpeedGrade
+ {
+ SPEED_6 = 0,
+ SPEED_7,
+ SPEED_8,
+ SPEED_8_5G,
+ } speed = SPEED_6;
};
struct Arch : BaseCtx
{
const ChipInfoPOD *chip_info;
const PackageInfoPOD *package_info;
+ const SpeedGradePOD *speed_grade;
mutable std::unordered_map<IdString, BelId> bel_by_name;
mutable std::unordered_map<IdString, WireId> wire_by_name;
@@ -415,6 +461,7 @@ struct Arch : BaseCtx
std::vector<CellInfo *> bel_to_cell;
std::unordered_map<WireId, NetInfo *> wire_to_net;
std::unordered_map<PipId, NetInfo *> pip_to_net;
+ std::unordered_map<WireId, int> wire_fanout;
ArchArgs args;
Arch(ArchArgs args);
@@ -597,6 +644,7 @@ struct Arch : BaseCtx
auto pip = it->second.pip;
if (pip != PipId()) {
+ wire_fanout[getPipSrcWire(pip)]--;
pip_to_net[pip] = nullptr;
}
@@ -633,7 +681,8 @@ struct Arch : BaseCtx
DelayInfo getWireDelay(WireId wire) const
{
DelayInfo delay;
- delay.delay = 0;
+ delay.min_delay = 0;
+ delay.max_delay = 0;
return delay;
}
@@ -686,6 +735,7 @@ struct Arch : BaseCtx
NPNR_ASSERT(pip_to_net[pip] == nullptr);
pip_to_net[pip] = net;
+ wire_fanout[getPipSrcWire(pip)]++;
WireId dst;
dst.index = locInfo(pip)->pip_data[pip.index].dst_idx;
@@ -700,6 +750,7 @@ struct Arch : BaseCtx
{
NPNR_ASSERT(pip != PipId());
NPNR_ASSERT(pip_to_net[pip] != nullptr);
+ wire_fanout[getPipSrcWire(pip)]--;
WireId dst;
dst.index = locInfo(pip)->pip_data[pip.index].dst_idx;
@@ -772,7 +823,17 @@ struct Arch : BaseCtx
{
DelayInfo delay;
NPNR_ASSERT(pip != PipId());
- delay.delay = locInfo(pip)->pip_data[pip.index].delay;
+ int fanout = 0;
+ auto fnd_fanout = wire_fanout.find(getPipSrcWire(pip));
+ if (fnd_fanout != wire_fanout.end())
+ fanout = fnd_fanout->second;
+ NPNR_ASSERT(locInfo(pip)->pip_data[pip.index].timing_class < speed_grade->num_pip_classes);
+ delay.min_delay =
+ speed_grade->pip_classes[locInfo(pip)->pip_data[pip.index].timing_class].min_base_delay +
+ fanout * speed_grade->pip_classes[locInfo(pip)->pip_data[pip.index].timing_class].min_fanout_adder;
+ delay.max_delay =
+ speed_grade->pip_classes[locInfo(pip)->pip_data[pip.index].timing_class].max_base_delay +
+ fanout * speed_grade->pip_classes[locInfo(pip)->pip_data[pip.index].timing_class].max_fanout_adder;
return delay;
}
@@ -862,7 +923,8 @@ struct Arch : BaseCtx
DelayInfo getDelayFromNS(float ns) const
{
DelayInfo del;
- del.delay = delay_t(ns * 1000);
+ del.min_delay = delay_t(ns * 1000);
+ del.max_delay = delay_t(ns * 1000);
return del;
}
uint32_t getDelayChecksum(delay_t v) const { return v; }
@@ -895,6 +957,10 @@ struct Arch : BaseCtx
// Return true if a port is a net
bool isGlobalNet(const NetInfo *net) const;
+ bool getDelayFromTimingDatabase(IdString tctype, IdString from, IdString to, DelayInfo &delay) const;
+ void getSetupHoldFromTimingDatabase(IdString tctype, IdString clock, IdString port, DelayInfo &setup,
+ DelayInfo &hold) const;
+
// -------------------------------------------------
// Placement validity checks
bool isValidBelForCell(CellInfo *cell, BelId bel) const;
diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h
index b2c23134..9428960c 100644
--- a/ecp5/archdefs.h
+++ b/ecp5/archdefs.h
@@ -30,21 +30,22 @@ typedef int delay_t;
struct DelayInfo
{
- delay_t delay = 0;
+ delay_t min_delay = 0, max_delay = 0;
- delay_t minRaiseDelay() const { return delay; }
- delay_t maxRaiseDelay() const { return delay; }
+ delay_t minRaiseDelay() const { return min_delay; }
+ delay_t maxRaiseDelay() const { return max_delay; }
- delay_t minFallDelay() const { return delay; }
- delay_t maxFallDelay() const { return delay; }
+ delay_t minFallDelay() const { return min_delay; }
+ delay_t maxFallDelay() const { return max_delay; }
- delay_t minDelay() const { return delay; }
- delay_t maxDelay() const { return delay; }
+ delay_t minDelay() const { return min_delay; }
+ delay_t maxDelay() const { return max_delay; }
DelayInfo operator+(const DelayInfo &other) const
{
DelayInfo ret;
- ret.delay = this->delay + other.delay;
+ ret.min_delay = this->min_delay + other.min_delay;
+ ret.max_delay = this->max_delay + other.max_delay;
return ret;
}
};
diff --git a/ecp5/constids.inc b/ecp5/constids.inc
index 11ecc240..250bc3fc 100644
--- a/ecp5/constids.inc
+++ b/ecp5/constids.inc
@@ -811,7 +811,6 @@ X(INTLOCK)
X(REFCLK)
X(CLKINTFB)
-
X(EXTREFB)
X(REFCLKP)
X(REFCLKN)
@@ -1116,4 +1115,30 @@ X(SEL2)
X(SEL1)
X(SEL0)
X(CDIV1)
-X(CDIVX) \ No newline at end of file
+X(CDIVX)
+
+X(DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG)
+X(DP16KD_REGMODE_A_NOREG_REGMODE_B_OUTREG)
+X(DP16KD_REGMODE_A_OUTREG_REGMODE_B_NOREG)
+X(DP16KD_REGMODE_A_OUTREG_REGMODE_B_OUTREG)
+X(DP16KD_WRITEMODE_A_NORMAL_WRITEMODE_B_NORMAL)
+X(DP16KD_WRITEMODE_A_NORMAL_WRITEMODE_B_READBEFOREWRITE)
+X(DP16KD_WRITEMODE_A_NORMAL_WRITEMODE_B_WRITETHROUGH)
+X(PIO_IOTYPE_LVCMOS12)
+X(PIO_IOTYPE_LVCMOS15)
+X(PIO_IOTYPE_LVCMOS18)
+X(PIO_IOTYPE_LVCMOS25)
+X(PIO_IOTYPE_LVCMOS33)
+X(PIO_IOTYPE_LVDS)
+X(PIO_IOTYPE_SSTL15_I)
+X(PIO_IOTYPE_SSTL15_II)
+X(PIO_IOTYPE_SSTL18_I)
+X(PIO_IOTYPE_SSTL18_II)
+X(SCCU2C)
+X(SDPRAME)
+X(SLOGICB)
+X(SRAMWB)
+X(PAD)
+X(PADDI)
+X(PADDO)
+X(PADDT)
diff --git a/ecp5/family.cmake b/ecp5/family.cmake
index ebe654af..1aae2bea 100644
--- a/ecp5/family.cmake
+++ b/ecp5/family.cmake
@@ -18,11 +18,13 @@ file(MAKE_DIRECTORY ecp5/chipdbs/)
add_library(ecp5_chipdb OBJECT ecp5/chipdbs/)
target_compile_definitions(ecp5_chipdb PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family})
target_include_directories(ecp5_chipdb PRIVATE ${family}/)
+
if (WIN32)
-set(ENV_CMD ${CMAKE_COMMAND} -E env "PYTHONPATH=\"${TRELLIS_ROOT}/libtrellis\;${TRELLIS_ROOT}/util/common\"")
+set(ENV_CMD ${CMAKE_COMMAND} -E env "PYTHONPATH=\"${TRELLIS_ROOT}/libtrellis\;${TRELLIS_ROOT}/util/common\;${TRELLIS_ROOT}/timing/util\"")
else()
-set(ENV_CMD ${CMAKE_COMMAND} -E env "PYTHONPATH=${TRELLIS_ROOT}/libtrellis:${TRELLIS_ROOT}/util/common")
+set(ENV_CMD ${CMAKE_COMMAND} -E env "PYTHONPATH=${TRELLIS_ROOT}/libtrellis:${TRELLIS_ROOT}/util/common:${TRELLIS_ROOT}/timing/util")
endif()
+
if (MSVC)
target_sources(ecp5_chipdb PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ecp5/resource/embed.cc)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ecp5/resources/chipdb.rc PROPERTIES LANGUAGE RC)
diff --git a/ecp5/main.cc b/ecp5/main.cc
index cc004df3..12afb09d 100644
--- a/ecp5/main.cc
+++ b/ecp5/main.cc
@@ -59,6 +59,8 @@ po::options_description ECP5CommandHandler::getArchOptions()
specific.add_options()("um5g-85k", "set device type to LFE5UM5G-85F");
specific.add_options()("package", po::value<std::string>(), "select device package (defaults to CABGA381)");
+ specific.add_options()("speed", po::value<int>(), "select device speedgrade (6, 7 or 8)");
+
specific.add_options()("basecfg", po::value<std::string>(), "base chip configuration in Trellis text format");
specific.add_options()("textcfg", po::value<std::string>(), "textual configuration in Trellis format to write");
@@ -111,7 +113,31 @@ std::unique_ptr<Context> ECP5CommandHandler::createContext()
chipArgs.package = vm["package"].as<std::string>();
else
chipArgs.package = "CABGA381";
- chipArgs.speed = 6;
+ if (chipArgs.type == ArchArgs::LFE5UM5G_25F || chipArgs.type == ArchArgs::LFE5UM5G_45F ||
+ chipArgs.type == ArchArgs::LFE5UM5G_85F) {
+ if (vm.count("speed") && vm["speed"].as<int>() != 8)
+ log_error("Only speed grade 8 is available for 5G parts\n");
+ chipArgs.speed = ArchArgs::SPEED_8_5G;
+ } else {
+ if (vm.count("speed")) {
+ int speed = vm["speed"].as<int>();
+ switch (speed) {
+ case 6:
+ chipArgs.speed = ArchArgs::SPEED_6;
+ break;
+ case 7:
+ chipArgs.speed = ArchArgs::SPEED_7;
+ break;
+ case 8:
+ chipArgs.speed = ArchArgs::SPEED_8;
+ break;
+ default:
+ log_error("Unsupported speed grade '%d'\n", speed);
+ }
+ } else {
+ chipArgs.speed = ArchArgs::SPEED_6;
+ }
+ }
return std::unique_ptr<Context>(new Context(chipArgs));
}
diff --git a/ecp5/project.cc b/ecp5/project.cc
index bca21643..43318b1c 100644
--- a/ecp5/project.cc
+++ b/ecp5/project.cc
@@ -45,7 +45,7 @@ std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root)
chipArgs.type = ArchArgs::LFE5U_85F;
}
chipArgs.package = root.get<std::string>("project.arch.package");
- chipArgs.speed = root.get<int>("project.arch.speed");
+ chipArgs.speed = ArchArgs::SpeedGrade(root.get<int>("project.arch.speed"));
return std::unique_ptr<Context>(new Context(chipArgs));
}
diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py
index 99fe7ba9..cdd3bd06 100755
--- a/ecp5/trellis_import.py
+++ b/ecp5/trellis_import.py
@@ -3,6 +3,8 @@ import pytrellis
import database
import argparse
import json
+import pip_classes
+import timing_dbs
from os import path
location_types = dict()
@@ -135,34 +137,81 @@ def process_loc_globals(chip):
spine = (-1, -1)
global_data[x, y] = (quadrants.index(quad), int(tapdrv.dir), tapdrv.col, spine)
-def get_wire_type(name):
- if "H00" in name or "V00" in name:
- return "X0"
- if "H01" in name or "V01" in name:
- return "X1"
- if "H02" in name or "V02" in name:
- return "X2"
- if "H06" in name or "V06" in name:
- return "X6"
- if "_SLICE" in name or "_EBR" in name:
- return "SLICE"
- return "LOCAL"
-
-def get_pip_delay(wire_from, wire_to):
- # ECP5 timings WIP!!!
- type_from = get_wire_type(wire_from)
- type_to = get_wire_type(wire_to)
- if type_from == "X2" and type_to == "X2":
- return 170
- if type_from == "SLICE" or type_to == "SLICE":
- return 205
- if type_from in ("LOCAL", "X0") and type_to in ("X1", "X2", "X6"):
- return 90
- if type_from == "X6" or type_to == "X6":
- return 200
- if type_from in ("X1", "X2", "X6") and type_to in ("LOCAL", "X0"):
- return 90
- return 100
+
+speed_grade_names = ["6", "7", "8", "8_5G"]
+speed_grade_cells = {}
+speed_grade_pips = {}
+
+pip_class_to_idx = {"default": 0}
+
+timing_port_xform = {
+ "RAD0": "D0",
+ "RAD1": "B0",
+ "RAD2": "C0",
+ "RAD3": "A0",
+}
+
+
+def process_timing_data():
+ for grade in speed_grade_names:
+ with open(timing_dbs.cells_db_path("ECP5", grade)) as f:
+ cell_data = json.load(f)
+ cells = []
+ for cell, cdata in sorted(cell_data.items()):
+ celltype = constids[cell.replace(":", "_").replace("=", "_").replace(",", "_")]
+ delays = []
+ setupholds = []
+ for entry in cdata:
+ if entry["type"] == "Width":
+ continue
+ elif entry["type"] == "IOPath":
+ from_pin = entry["from_pin"][1] if type(entry["from_pin"]) is list else entry["from_pin"]
+ if from_pin in timing_port_xform:
+ from_pin = timing_port_xform[from_pin]
+ to_pin = entry["to_pin"]
+ if to_pin in timing_port_xform:
+ to_pin = timing_port_xform[to_pin]
+ min_delay = min(entry["rising"][0], entry["falling"][0])
+ max_delay = min(entry["rising"][2], entry["falling"][2])
+ delays.append((constids[from_pin], constids[to_pin], min_delay, max_delay))
+ elif entry["type"] == "SetupHold":
+ pin = constids[entry["pin"]]
+ clock = constids[entry["clock"][1]]
+ min_setup = entry["setup"][0]
+ max_setup = entry["setup"][2]
+ min_hold = entry["hold"][0]
+ max_hold = entry["hold"][2]
+ setupholds.append((pin, clock, min_setup, max_setup, min_hold, max_hold))
+ else:
+ assert False, entry["type"]
+ cells.append((celltype, delays, setupholds))
+ pip_class_delays = []
+ for i in range(len(pip_class_to_idx)):
+ pip_class_delays.append((50, 50, 0, 0))
+
+ with open(timing_dbs.interconnect_db_path("ECP5", grade)) as f:
+ interconn_data = json.load(f)
+ for pipclass, pipdata in sorted(interconn_data.items()):
+
+ min_delay = pipdata["delay"][0] * 1.1
+ max_delay = pipdata["delay"][2] * 1.1
+ min_fanout = pipdata["fanout"][0]
+ max_fanout = pipdata["fanout"][2]
+ if grade == "6":
+ pip_class_to_idx[pipclass] = len(pip_class_delays)
+ pip_class_delays.append((min_delay, max_delay, min_fanout, max_fanout))
+ else:
+ if pipclass in pip_class_to_idx:
+ pip_class_delays[pip_class_to_idx[pipclass]] = (min_delay, max_delay, min_fanout, max_fanout)
+ speed_grade_cells[grade] = cells
+ speed_grade_pips[grade] = pip_class_delays
+
+
+def get_pip_class(wire_from, wire_to):
+ class_name = pip_classes.get_pip_class(wire_from, wire_to)
+ if class_name is None or class_name not in pip_class_to_idx:
+ class_name = "default"
+ return pip_class_to_idx[class_name]
@@ -181,7 +230,7 @@ def write_database(dev_name, chip, ddrg, endianness):
loc = loc_with_type[arc_loctype]
lt = ddrg.typeAtLocation[pytrellis.Location(loc[0] + rel.x, loc[1] + rel.y)]
wire = ddrg.locationTypes[lt].wires[idx]
- return ddrg.to_str(wire.name)
+ return "R{}C{}_{}".format(loc[1] + rel.y, loc[0] + rel.x, ddrg.to_str(wire.name))
bba = BinaryBlobAssembler()
bba.pre('#include "nextpnr.h"')
@@ -202,7 +251,7 @@ def write_database(dev_name, chip, ddrg, endianness):
bba.u32(arc.sinkWire.id, "dst_idx")
src_name = get_wire_name(idx, arc.srcWire.rel, arc.srcWire.id)
snk_name = get_wire_name(idx, arc.sinkWire.rel, arc.sinkWire.id)
- bba.u32(get_pip_delay(src_name, snk_name), "delay") # TODO:delay
+ bba.u32(get_pip_class(src_name, snk_name), "timing_class")
bba.u16(get_tiletype_index(ddrg.to_str(arc.tiletype)), "tile_type")
cls = arc.cls
if cls == 1 and "PCS" in snk_name or "DCU" in snk_name or "DCU" in src_name:
@@ -321,11 +370,53 @@ def write_database(dev_name, chip, ddrg, endianness):
bba.u16(bank, "bank")
bba.u16(0, "padding")
-
bba.l("tiletype_names", "RelPtr<char>")
for tt, idx in sorted(tiletype_names.items(), key=lambda x: x[1]):
bba.s(tt, "name")
+ for grade in speed_grade_names:
+ for cell in speed_grade_cells[grade]:
+ celltype, delays, setupholds = cell
+ if len(delays) > 0:
+ bba.l("cell_%d_delays_%s" % (celltype, grade))
+ for delay in delays:
+ from_pin, to_pin, min_delay, max_delay = delay
+ bba.u32(from_pin, "from_pin")
+ bba.u32(to_pin, "to_pin")
+ bba.u32(min_delay, "min_delay")
+ bba.u32(max_delay, "max_delay")
+ if len(setupholds) > 0:
+ bba.l("cell_%d_setupholds_%s" % (celltype, grade))
+ for sh in setupholds:
+ pin, clock, min_setup, max_setup, min_hold, max_hold = sh
+ bba.u32(pin, "sig_port")
+ bba.u32(clock, "clock_port")
+ bba.u32(min_setup, "min_setup")
+ bba.u32(max_setup, "max_setup")
+ bba.u32(min_hold, "min_hold")
+ bba.u32(max_hold, "max_hold")
+ bba.l("cell_timing_data_%s" % grade)
+ for cell in speed_grade_cells[grade]:
+ celltype, delays, setupholds = cell
+ bba.u32(celltype, "cell_type")
+ bba.u32(len(delays), "num_delays")
+ bba.u32(len(setupholds), "num_setup_hold")
+ bba.r("cell_%d_delays_%s" % (celltype, grade) if len(delays) > 0 else None, "delays")
+ bba.r("cell_%d_setupholds_%s" % (celltype, grade) if len(delays) > 0 else None, "setupholds")
+ bba.l("pip_timing_data_%s" % grade)
+ for pipclass in speed_grade_pips[grade]:
+ min_delay, max_delay, min_fanout, max_fanout = pipclass
+ bba.u32(min_delay, "min_delay")
+ bba.u32(max_delay, "max_delay")
+ bba.u32(min_fanout, "min_fanout")
+ bba.u32(max_fanout, "max_fanout")
+ bba.l("speed_grade_data")
+ for grade in speed_grade_names:
+ bba.u32(len(speed_grade_cells[grade]), "num_cell_timings")
+ bba.u32(len(speed_grade_pips[grade]), "num_pip_classes")
+ bba.r("cell_timing_data_%s" % grade, "cell_timings")
+ bba.r("pip_timing_data_%s" % grade, "pip_classes")
+
bba.l("chip_info")
bba.u32(max_col + 1, "width")
bba.u32(max_row + 1, "height")
@@ -341,6 +432,7 @@ def write_database(dev_name, chip, ddrg, endianness):
bba.r("package_data", "package_info")
bba.r("pio_info", "pio_info")
bba.r("tiles_info", "tile_info")
+ bba.r("speed_grade_data", "speed_grades")
bba.pop()
return bba
@@ -375,6 +467,7 @@ def main():
ddrg = pytrellis.make_dedup_chipdb(chip)
max_row = chip.get_max_row()
max_col = chip.get_max_col()
+ process_timing_data()
process_pio_db(ddrg, args.device)
process_loc_globals(chip)
# print("{} unique location types".format(len(ddrg.locationTypes)))