diff options
-rw-r--r-- | common/command.cc | 6 | ||||
-rw-r--r-- | common/timing.cc | 5 | ||||
-rw-r--r-- | ecp5/family.cmake | 6 | ||||
-rw-r--r-- | gui/designwidget.cc | 8 | ||||
-rw-r--r-- | gui/fpgaviewwidget.cc | 2 | ||||
-rw-r--r-- | ice40/arch.cc | 49 | ||||
-rw-r--r-- | ice40/arch.h | 5 |
7 files changed, 60 insertions, 21 deletions
diff --git a/common/command.cc b/common/command.cc index 6ba3442f..8f18f54d 100644 --- a/common/command.cc +++ b/common/command.cc @@ -124,6 +124,8 @@ po::options_description CommandHandler::getGeneralOptions() general.add_options()("cstrweight", po::value<float>(), "placer weighting for relative constraint satisfaction"); general.add_options()("pack-only", "pack design only without placement or routing"); + general.add_options()("ignore-loops", "ignore combinational loops in timing analysis"); + general.add_options()("version,V", "show version"); general.add_options()("test", "check architecture database integrity"); general.add_options()("freq", po::value<double>(), "set target frequency for design in MHz"); @@ -172,6 +174,10 @@ void CommandHandler::setupContext(Context *ctx) } } + if (vm.count("ignore-loops")) { + settings->set("timing/ignoreLoops", true); + } + if (vm.count("cstrweight")) { settings->set("placer1/constraintWeight", vm["cstrweight"].as<float>()); } diff --git a/common/timing.cc b/common/timing.cc index b27dd56e..13f0e07b 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -225,7 +225,7 @@ struct Timing } // Sanity check to ensure that all ports where fanins were recorded were indeed visited - if (!port_fanin.empty()) { + if (!port_fanin.empty() && !bool_or_default(ctx->settings, ctx->id("timing/ignoreLoops"), false)) { for (auto fanin : port_fanin) { NetInfo *net = fanin.first->net; if (net != nullptr) { @@ -267,8 +267,7 @@ struct Timing auto net_delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t(); auto usr_arrival = net_arrival + net_delay; - if (portClass == TMG_REGISTER_INPUT || portClass == TMG_ENDPOINT || portClass == TMG_IGNORE || - portClass == TMG_CLOCK_INPUT) { + if (portClass == TMG_ENDPOINT || portClass == TMG_IGNORE || portClass == TMG_CLOCK_INPUT) { // Skip } else { auto budget_override = ctx->getBudgetOverride(net, usr, net_delay); diff --git a/ecp5/family.cmake b/ecp5/family.cmake index 461fed4f..679325b2 100644 --- a/ecp5/family.cmake +++ b/ecp5/family.cmake @@ -2,11 +2,11 @@ set(devices 25k 45k 85k) if (NOT DEFINED TRELLIS_ROOT) - message(FATAL_ERROR "you must define TRELLIS_ROOT using -DTRELLIS_ROOT=/path/to/prjtrellis for ECP5 support") + message(STATUS "TRELLIS_ROOT not defined using -DTRELLIS_ROOT=/path/to/prjtrellis. Default to /usr/local/share/trellis") + set(TRELLIS_ROOT "/usr/local/share/trellis") endif() - -file( GLOB found_pytrellis ${TRELLIS_ROOT}/libtrellis/pytrellis.*) +file(GLOB found_pytrellis ${TRELLIS_ROOT}/libtrellis/pytrellis.*) if ("${found_pytrellis}" STREQUAL "") message(FATAL_ERROR "failed to find pytrellis library in ${TRELLIS_ROOT}/libtrellis/") diff --git a/gui/designwidget.cc b/gui/designwidget.cc index 235dd2cb..fc99cd14 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -706,8 +706,12 @@ void DesignWidget::onSelectionChanged(int num, const QItemSelection &, const QIt addProperty(topItem, QVariant::String, "Type", ctx->getPipType(pip).c_str(ctx));
addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip));
addProperty(topItem, QVariant::String, "Bound Net", ctx->nameOf(ctx->getBoundPipNet(pip)), ElementType::NET);
- addProperty(topItem, QVariant::String, "Conflicting Wire",
- ctx->getWireName(ctx->getConflictingPipWire(pip)).c_str(ctx), ElementType::WIRE);
+ if (ctx->getConflictingPipWire(pip) != WireId()) {
+ addProperty(topItem, QVariant::String, "Conflicting Wire",
+ ctx->getWireName(ctx->getConflictingPipWire(pip)).c_str(ctx), ElementType::WIRE);
+ } else {
+ addProperty(topItem, QVariant::String, "Conflicting Wire", "", ElementType::NONE);
+ }
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->nameOf(ctx->getConflictingPipNet(pip)),
ElementType::NET);
addProperty(topItem, QVariant::String, "Src Wire", ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx),
diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index 3fba6bff..0ad90527 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -102,7 +102,7 @@ void FPGAViewWidget::newContext(Context *ctx) pokeRenderer(); } -QSize FPGAViewWidget::minimumSizeHint() const { return QSize(640, 480); } +QSize FPGAViewWidget::minimumSizeHint() const { return QSize(320, 200); } QSize FPGAViewWidget::sizeHint() const { return QSize(640, 480); } diff --git a/ice40/arch.cc b/ice40/arch.cc index 04c70e94..f6084e72 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -594,26 +594,29 @@ std::vector<GroupId> Arch::getGroupGroups(GroupId group) const bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { const auto &driver = net_info->driver; - if (driver.port == id_COUT && sink.port == id_CIN) { - if (driver.cell->constr_abs_z && driver.cell->constr_z < 7) + if (driver.port == id_COUT) { + NPNR_ASSERT(sink.port == id_CIN || sink.port == id_I3); + NPNR_ASSERT(driver.cell->constr_abs_z); + bool cin = sink.port == id_CIN; + bool same_y = driver.cell->constr_z < 7; + if (cin && same_y) budget = 0; else { - NPNR_ASSERT(driver.cell->constr_z == 7); switch (args.type) { #ifndef ICE40_HX1K_ONLY case ArchArgs::HX8K: #endif case ArchArgs::HX1K: - budget = 190; + budget = cin ? 190 : (same_y ? 260 : 560); break; #ifndef ICE40_HX1K_ONLY case ArchArgs::LP384: case ArchArgs::LP1K: case ArchArgs::LP8K: - budget = 290; + budget = cin ? 290 : (same_y ? 380 : 670); break; case ArchArgs::UP5K: - budget = 560; + budget = cin ? 560 : (same_y ? 660 : 1220); break; #endif default: @@ -867,6 +870,17 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const { + if (cell->type == id_ICESTORM_LC && cell->lcInfo.dffEnable) { + if (toPort == id_O) + return false; + } else if (cell->type == id_ICESTORM_RAM || cell->type == id_ICESTORM_SPRAM) { + return false; + } + return getCellDelayInternal(cell, fromPort, toPort, delay); +} + +bool Arch::getCellDelayInternal(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const +{ for (int i = 0; i < chip_info->num_timing_cells; i++) { const auto &tc = chip_info->cell_timing[i]; if (tc.type == cell->type.index) { @@ -965,7 +979,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_IGNORE; } else if (cell->type == id_ICESTORM_PLL) { - if (port == id_PLLOUT_A || port == id_PLLOUT_B) + if (port == id_PLLOUT_A || port == id_PLLOUT_B || port == id_PLLOUT_A_GLOBAL || port == id_PLLOUT_B_GLOBAL) return TMG_GEN_CLOCK; return TMG_IGNORE; } else if (cell->type == id_ICESTORM_LFOSC) { @@ -1001,10 +1015,23 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port info.clock_port = id_CLK; info.edge = cell->lcInfo.negClk ? FALLING_EDGE : RISING_EDGE; if (port == id_O) { - bool has_clktoq = getCellDelay(cell, id_CLK, id_O, info.clockToQ); + bool has_clktoq = getCellDelayInternal(cell, id_CLK, id_O, info.clockToQ); NPNR_ASSERT(has_clktoq); } else { - info.setup.delay = 100; + if (port == id_I0 || port == id_I1 || port == id_I2 || port == id_I3) { + DelayInfo dlut; + bool has_ld = getCellDelayInternal(cell, port, id_O, dlut); + NPNR_ASSERT(has_ld); + if (args.type == ArchArgs::LP1K || args.type == ArchArgs::LP8K || args.type == ArchArgs::LP384) { + info.setup.delay = 30 + dlut.delay; + } else if (args.type == ArchArgs::UP5K) { + info.setup.delay = dlut.delay - 50; + } else { + info.setup.delay = 20 + dlut.delay; + } + } else { + info.setup.delay = 100; + } info.hold.delay = 0; } } else if (cell->type == id_ICESTORM_RAM) { @@ -1016,7 +1043,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port info.edge = bool_or_default(cell->params, id("NEG_CLK_W")) ? FALLING_EDGE : RISING_EDGE; } if (cell->ports.at(port).type == PORT_OUT) { - bool has_clktoq = getCellDelay(cell, info.clock_port, port, info.clockToQ); + bool has_clktoq = getCellDelayInternal(cell, info.clock_port, port, info.clockToQ); NPNR_ASSERT(has_clktoq); } else { info.setup.delay = 100; @@ -1061,7 +1088,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port info.clock_port = cell->type == id_ICESTORM_SPRAM ? id_CLOCK : id_CLK; info.edge = RISING_EDGE; if (cell->ports.at(port).type == PORT_OUT) { - bool has_clktoq = getCellDelay(cell, info.clock_port, port, info.clockToQ); + bool has_clktoq = getCellDelayInternal(cell, info.clock_port, port, info.clockToQ); if (!has_clktoq) info.clockToQ.delay = 100; } else { diff --git a/ice40/arch.h b/ice40/arch.h index 10255dbe..2dd6b06c 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -841,8 +841,11 @@ struct Arch : BaseCtx // ------------------------------------------------- // Get the delay through a cell from one port to another, returning false - // if no path exists + // if no path exists. This only considers combinational delays, as required by the Arch API bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; + // getCellDelayInternal is similar to the above, but without false path checks and including clock to out delays + // for internal arch use only + bool getCellDelayInternal(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; // Get the port class, also setting clockInfoCount to the number of TimingClockingInfos associated with a port TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const; // Get the TimingClockingInfo of a port |