/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Claire Xenia Wolf * Copyright (C) 2018 Serge Bazanski * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include "nextpnr.h" #include "router1.h" NEXTPNR_NAMESPACE_BEGIN #define NUM_FUZZ_ROUTES 100000 void ice40DelayFuzzerMain(Context *ctx) { std::vector srcWires, dstWires; for (int i = 0; i < int(ctx->chip_info->wire_data.size()); i++) { WireId wire; wire.index = i; switch (ctx->chip_info->wire_data[i].type) { case WireInfoPOD::WIRE_TYPE_LUTFF_OUT: srcWires.push_back(wire); break; case WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT: dstWires.push_back(wire); break; default: break; } } ctx->shuffle(srcWires); ctx->shuffle(dstWires); int index = 0; int cnt = 0; while (cnt < NUM_FUZZ_ROUTES) { if (index >= int(srcWires.size()) || index >= int(dstWires.size())) { index = 0; ctx->shuffle(srcWires); ctx->shuffle(dstWires); } WireId src = srcWires[index]; WireId dst = dstWires[index++]; dict route; #if NUM_FUZZ_ROUTES <= 1000 if (!ctx->getActualRouteDelay(src, dst, nullptr, &route, false)) continue; #else if (!ctx->getActualRouteDelay(src, dst, nullptr, &route, true)) continue; #endif WireId cursor = dst; delay_t delay = 0; while (1) { delay += ctx->getWireDelay(cursor).maxDelay(); printf("%s %d %d %s %s %d %d\n", cursor == dst ? "dst" : "src", int(ctx->chip_info->wire_data[cursor.index].x), int(ctx->chip_info->wire_data[cursor.index].y), ctx->getWireType(cursor).c_str(ctx), ctx->nameOfWire(cursor), int(delay), int(ctx->estimateDelay(cursor, dst))); if (cursor == src) break; PipId pip = route.at(cursor); delay += ctx->getPipDelay(pip).maxDelay(); cursor = ctx->getPipSrcWire(pip); } cnt++; if (cnt % 100 == 0) fprintf(stderr, "Fuzzed %d arcs.\n", cnt); } } namespace { struct model_params_t { int neighbourhood; int model0_offset; int model0_norm1; int model1_offset; int model1_norm1; int model1_norm2; int model1_norm3; int model2_offset; int model2_linear; int model2_sqrt; int delta_local; int delta_lutffin; int delta_sp4; int delta_sp12; static const model_params_t &get(const ArchArgs &args) { static const model_params_t model_hx8k = {588, 129253, 8658, 118333, 23915, -73105, 57696, -86797, 89, 3706, -316, -575, -158, -296}; static const model_params_t model_lp8k = {867, 206236, 11043, 191910, 31074, -95972, 75739, -309793, 30, 11056, -474, -856, -363, -536}; static const model_params_t model_up5k = {1761, 305798, 16705, 296830, 24430, -40369, 33038, -162662, 94, 4705, -1099, -1761, -418, -838}; if (args.type == ArchArgs::HX1K || args.type == ArchArgs::HX4K || args.type == ArchArgs::HX8K) return model_hx8k; if (args.type == ArchArgs::LP384 || args.type == ArchArgs::LP1K || args.type == ArchArgs::LP4K || args.type == ArchArgs::LP8K) return model_lp8k; if (args.type == ArchArgs::UP3K || args.type == ArchArgs::UP5K || args.type == ArchArgs::U1K || args.type == ArchArgs::U2K || args.type == ArchArgs::U4K) return model_up5k; NPNR_ASSERT(0); } }; } // namespace delay_t Arch::estimateDelay(WireId src, WireId dst) const { NPNR_ASSERT(src != WireId()); int x1 = chip_info->wire_data[src.index].x; int y1 = chip_info->wire_data[src.index].y; int z1 = chip_info->wire_data[src.index].z; int type = chip_info->wire_data[src.index].type; NPNR_ASSERT(dst != WireId()); int x2 = chip_info->wire_data[dst.index].x; int y2 = chip_info->wire_data[dst.index].y; int z2 = chip_info->wire_data[dst.index].z; int dx = abs(x2 - x1); int dy = abs(y2 - y1); const model_params_t &p = model_params_t::get(args); delay_t v = p.neighbourhood; if (dx > 1 || dy > 1) v = (p.model0_offset + p.model0_norm1 * (dx + dy)) / 128; if (dx == 0 && dy == 0) { if (type == WireInfoPOD::WIRE_TYPE_LOCAL) v += p.delta_local; if (type == WireInfoPOD::WIRE_TYPE_LUTFF_IN || type == WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT) v += (z1 == z2) ? p.delta_lutffin : 0; } if (type == WireInfoPOD::WIRE_TYPE_SP4_V || type == WireInfoPOD::WIRE_TYPE_SP4_H) v += p.delta_sp4; if (type == WireInfoPOD::WIRE_TYPE_SP12_V || type == WireInfoPOD::WIRE_TYPE_SP12_H) v += p.delta_sp12; return v; } delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const { NPNR_UNUSED(dst_pin); auto driver_loc = getBelLocation(src_bel); auto sink_loc = getBelLocation(dst_bel); if (src_pin == id_COUT) { if (driver_loc.y == sink_loc.y) return 0; return 250; } int dx = abs(sink_loc.x - driver_loc.x); int dy = abs(sink_loc.y - driver_loc.y); const model_params_t &p = model_params_t::get(args); if (dx <= 1 && dy <= 1) return p.neighbourhood; #if 1 // Model #0 return (p.model0_offset + p.model0_norm1 * (dx + dy)) / 128; #else float norm1 = dx + dy; float dx2 = dx * dx; float dy2 = dy * dy; float norm2 = sqrtf(dx2 + dy2); float dx3 = dx2 * dx; float dy3 = dy2 * dy; float norm3 = powf(dx3 + dy3, 1.0 / 3.0); // Model #1 float v = p.model1_offset; v += p.model1_norm1 * norm1; v += p.model1_norm2 * norm2; v += p.model1_norm3 * norm3; v /= 128; // Model #2 v = p.model2_offset + p.model2_linear * v + p.model2_sqrt * sqrtf(v); v /= 128; return v; #endif } NEXTPNR_NAMESPACE_END