diff options
-rw-r--r-- | common/timing.cc | 18 | ||||
-rw-r--r-- | ecp5/arch.cc | 175 | ||||
-rw-r--r-- | ecp5/arch.h | 76 | ||||
-rw-r--r-- | ecp5/archdefs.h | 17 | ||||
-rw-r--r-- | ecp5/constids.inc | 29 | ||||
-rw-r--r-- | ecp5/family.cmake | 6 | ||||
-rw-r--r-- | ecp5/main.cc | 28 | ||||
-rw-r--r-- | ecp5/project.cc | 2 | ||||
-rwxr-xr-x | ecp5/trellis_import.py | 155 |
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))) |