diff options
author | David Shah <davey1576@gmail.com> | 2018-08-05 14:31:43 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-05 14:31:43 +0200 |
commit | ba97c233fb6e4502f3465a602f997cc2382f0e06 (patch) | |
tree | 3a62397aebff56e2031f3d5c6c930ead2699d641 /ice40 | |
parent | 8a9b3626d32e8845dc51044e0f281c0ccdb7e53a (diff) | |
parent | 287fe7e89451b952d15c7839aff9cb3db12bf807 (diff) | |
download | nextpnr-ba97c233fb6e4502f3465a602f997cc2382f0e06.tar.gz nextpnr-ba97c233fb6e4502f3465a602f997cc2382f0e06.tar.bz2 nextpnr-ba97c233fb6e4502f3465a602f997cc2382f0e06.zip |
Merge pull request #36 from YosysHQ/lutperm
Add LUT input permutations, improvements in ice40 timing model, improvements in router
Diffstat (limited to 'ice40')
-rw-r--r-- | ice40/arch.cc | 102 | ||||
-rw-r--r-- | ice40/arch.h | 64 | ||||
-rw-r--r-- | ice40/archdefs.h | 15 | ||||
-rw-r--r-- | ice40/bitstream.cc | 87 | ||||
-rw-r--r-- | ice40/chipdb.py | 190 | ||||
-rw-r--r-- | ice40/delay.cc | 238 | ||||
-rw-r--r-- | ice40/gfx.cc | 34 | ||||
-rw-r--r-- | ice40/gfx.h | 51 | ||||
-rw-r--r-- | ice40/main.cc | 78 | ||||
-rw-r--r-- | ice40/tmfuzz.py | 357 |
10 files changed, 998 insertions, 218 deletions
diff --git a/ice40/arch.cc b/ice40/arch.cc index 3934e8f0..5d79a487 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -174,6 +174,7 @@ Arch::Arch(ArchArgs args) : args(args) if (package_info == nullptr) log_error("Unsupported package '%s'.\n", args.package.c_str()); + bel_carry.resize(chip_info->num_bels); bel_to_cell.resize(chip_info->num_bels); wire_to_net.resize(chip_info->num_wires); pip_to_net.resize(chip_info->num_pips); @@ -192,6 +193,7 @@ Arch::Arch(ArchArgs args) : args(args) id_i2 = id("I2"); id_i3 = id("I3"); id_dff_en = id("DFF_ENABLE"); + id_carry_en = id("CARRY_ENABLE"); id_neg_clk = id("NEG_CLK"); id_cin = id("CIN"); id_cout = id("COUT"); @@ -399,6 +401,44 @@ WireId Arch::getWireByName(IdString name) const return ret; } +IdString Arch::getWireType(WireId wire) const +{ + NPNR_ASSERT(wire != WireId()); + switch (chip_info->wire_data[wire.index].type) { + case WireInfoPOD::WIRE_TYPE_NONE: + return IdString(); + case WireInfoPOD::WIRE_TYPE_GLB2LOCAL: + return id("GLB2LOCAL"); + case WireInfoPOD::WIRE_TYPE_GLB_NETWK: + return id("GLB_NETWK"); + case WireInfoPOD::WIRE_TYPE_LOCAL: + return id("LOCAL"); + case WireInfoPOD::WIRE_TYPE_LUTFF_IN: + return id("LUTFF_IN"); + case WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT: + return id("LUTFF_IN_LUT"); + case WireInfoPOD::WIRE_TYPE_LUTFF_LOUT: + return id("LUTFF_LOUT"); + case WireInfoPOD::WIRE_TYPE_LUTFF_OUT: + return id("LUTFF_OUT"); + case WireInfoPOD::WIRE_TYPE_LUTFF_COUT: + return id("LUTFF_COUT"); + case WireInfoPOD::WIRE_TYPE_LUTFF_GLOBAL: + return id("LUTFF_GLOBAL"); + case WireInfoPOD::WIRE_TYPE_CARRY_IN_MUX: + return id("CARRY_IN_MUX"); + case WireInfoPOD::WIRE_TYPE_SP4_V: + return id("SP4_V"); + case WireInfoPOD::WIRE_TYPE_SP4_H: + return id("SP4_H"); + case WireInfoPOD::WIRE_TYPE_SP12_V: + return id("SP12_V"); + case WireInfoPOD::WIRE_TYPE_SP12_H: + return id("SP12_H"); + } + return IdString(); +} + // ----------------------------------------------------------------------- PipId Arch::getPipByName(IdString name) const @@ -541,9 +581,7 @@ std::vector<GroupId> Arch::getGroups() const group.type = GroupId::TYPE_LOCAL_SW; ret.push_back(group); -#if 0 - if (type == TILE_LOGIC) - { + if (type == TILE_LOGIC) { group.type = GroupId::TYPE_LC0_SW; ret.push_back(group); @@ -568,7 +606,6 @@ std::vector<GroupId> Arch::getGroups() const group.type = GroupId::TYPE_LC7_SW; ret.push_back(group); } -#endif } } return ret; @@ -600,50 +637,6 @@ std::vector<GroupId> Arch::getGroupGroups(GroupId group) const // ----------------------------------------------------------------------- -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; - - NPNR_ASSERT(dst != WireId()); - int x2 = chip_info->wire_data[dst.index].x; - int y2 = chip_info->wire_data[dst.index].y; - - int xd = x2 - x1, yd = y2 - y1; - int xscale = 120, yscale = 120, offset = 0; - - return xscale * abs(xd) + yscale * abs(yd) + offset; -} - -delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const -{ - const auto &driver = net_info->driver; - auto driver_loc = getBelLocation(driver.cell->bel); - auto sink_loc = getBelLocation(sink.cell->bel); - - if (driver.port == id_cout) { - if (driver_loc.y == sink_loc.y) - return 0; - return 250; - } - - int xd = sink_loc.x - driver_loc.x, yd = sink_loc.y - driver_loc.y; - int xscale = 120, yscale = 120, offset = 0; - - // if (chip_info->wire_data[src.index].type == WIRE_TYPE_SP4_VERT) { - // yd = yd < -4 ? yd + 4 : (yd < 0 ? 0 : yd); - // offset = 500; - // } - - if (driver.port == id_o) - offset += 330; - if (sink.port == id_i0 || sink.port == id_i1 || sink.port == id_i2 || sink.port == id_i3) - offset += 260; - - return xscale * abs(xd) + yscale * abs(yd) + offset; -} - delay_t Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) const { const auto &driver = net_info->driver; @@ -768,6 +761,18 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const el.y2 = y + local_swbox_y2; ret.push_back(el); } + + if (GroupId::TYPE_LC0_SW <= type && type <= GroupId::TYPE_LC7_SW) { + GraphicElement el; + el.type = GraphicElement::TYPE_BOX; + el.style = GraphicElement::STYLE_FRAME; + + el.x1 = x + lut_swbox_x1; + el.x2 = x + lut_swbox_x2; + el.y1 = y + logic_cell_y1 + logic_cell_pitch * (type - GroupId::TYPE_LC0_SW); + el.y2 = y + logic_cell_y2 + logic_cell_pitch * (type - GroupId::TYPE_LC0_SW); + ret.push_back(el); + } } if (decal.type == DecalId::TYPE_WIRE) { @@ -918,6 +923,7 @@ void Arch::assignCellInfo(CellInfo *cell) cell->belType = belTypeFromId(cell->type); if (cell->type == id_icestorm_lc) { cell->lcInfo.dffEnable = bool_or_default(cell->params, id_dff_en); + cell->lcInfo.carryEnable = bool_or_default(cell->params, id_carry_en); cell->lcInfo.negClk = bool_or_default(cell->params, id_neg_clk); cell->lcInfo.clk = get_net_or_empty(cell, id_clk); cell->lcInfo.cen = get_net_or_empty(cell, id_cen); diff --git a/ice40/arch.h b/ice40/arch.h index cf78088a..d3076416 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -64,6 +64,13 @@ NPNR_PACKED_STRUCT(struct BelPortPOD { }); NPNR_PACKED_STRUCT(struct PipInfoPOD { + enum PipFlags : uint32_t + { + FLAG_NONE = 0, + FLAG_ROUTETHRU = 1, + FLAG_NOCARRY = 2 + }; + // RelPtr<char> name; int32_t src, dst; int32_t fast_delay; @@ -72,6 +79,7 @@ NPNR_PACKED_STRUCT(struct PipInfoPOD { int16_t src_seg, dst_seg; int16_t switch_mask; int32_t switch_index; + PipFlags flags; }); NPNR_PACKED_STRUCT(struct WireSegmentPOD { @@ -80,6 +88,25 @@ NPNR_PACKED_STRUCT(struct WireSegmentPOD { }); NPNR_PACKED_STRUCT(struct WireInfoPOD { + enum WireType : int8_t + { + WIRE_TYPE_NONE = 0, + WIRE_TYPE_GLB2LOCAL = 1, + WIRE_TYPE_GLB_NETWK = 2, + WIRE_TYPE_LOCAL = 3, + WIRE_TYPE_LUTFF_IN = 4, + WIRE_TYPE_LUTFF_IN_LUT = 5, + WIRE_TYPE_LUTFF_LOUT = 6, + WIRE_TYPE_LUTFF_OUT = 7, + WIRE_TYPE_LUTFF_COUT = 8, + WIRE_TYPE_LUTFF_GLOBAL = 9, + WIRE_TYPE_CARRY_IN_MUX = 10, + WIRE_TYPE_SP4_V = 11, + WIRE_TYPE_SP4_H = 12, + WIRE_TYPE_SP12_V = 13, + WIRE_TYPE_SP12_H = 14 + }; + RelPtr<char> name; int32_t num_uphill, num_downhill; RelPtr<int32_t> pips_uphill, pips_downhill; @@ -93,9 +120,8 @@ NPNR_PACKED_STRUCT(struct WireInfoPOD { int32_t fast_delay; int32_t slow_delay; - int8_t x, y; + int8_t x, y, z; WireType type; - int8_t padding_0; }); NPNR_PACKED_STRUCT(struct PackagePinPOD { @@ -373,6 +399,7 @@ struct Arch : BaseCtx mutable std::unordered_map<IdString, int> pip_by_name; mutable std::unordered_map<Loc, int> bel_by_loc; + std::vector<bool> bel_carry; std::vector<IdString> bel_to_cell; std::vector<IdString> wire_to_net; std::vector<IdString> pip_to_net; @@ -414,9 +441,12 @@ struct Arch : BaseCtx { NPNR_ASSERT(bel != BelId()); NPNR_ASSERT(bel_to_cell[bel.index] == IdString()); + auto &c = cells[cell]; + bel_to_cell[bel.index] = cell; - cells[cell]->bel = bel; - cells[cell]->belStrength = strength; + bel_carry[bel.index] = (c->type == id_icestorm_lc && c->lcInfo.carryEnable); + c->bel = bel; + c->belStrength = strength; refreshUiBel(bel); } @@ -427,6 +457,7 @@ struct Arch : BaseCtx cells[bel_to_cell[bel.index]]->bel = BelId(); cells[bel_to_cell[bel.index]]->belStrength = STRENGTH_NONE; bel_to_cell[bel.index] = IdString(); + bel_carry[bel.index] = false; refreshUiBel(bel); } @@ -490,7 +521,7 @@ struct Arch : BaseCtx return id(chip_info->wire_data[wire.index].name.get()); } - IdString getWireType(WireId wire) const { return IdString(); } + IdString getWireType(WireId wire) const; uint32_t getWireChecksum(WireId wire) const { return wire.index; } @@ -614,14 +645,23 @@ struct Arch : BaseCtx bool checkPipAvail(PipId pip) const { NPNR_ASSERT(pip != PipId()); - int switch_idx = chip_info->pip_data[pip.index].switch_index; + auto &pi = chip_info->pip_data[pip.index]; + auto &si = chip_info->bits_info->switches[pi.switch_index]; - if (switches_locked[switch_idx] != IdString()) + if (switches_locked[pi.switch_index] != IdString()) return false; - int bel_idx = chip_info->bits_info->switches[switch_idx].bel; - if (bel_idx >= 0 && bel_to_cell[bel_idx] != IdString()) - return false; + if (pi.flags & PipInfoPOD::FLAG_ROUTETHRU) { + NPNR_ASSERT(si.bel >= 0); + if (bel_to_cell[si.bel] != IdString()) + return false; + } + + if (pi.flags & PipInfoPOD::FLAG_NOCARRY) { + NPNR_ASSERT(si.bel >= 0); + if (bel_carry[si.bel]) + return false; + } return true; } @@ -781,7 +821,7 @@ struct Arch : BaseCtx IdString id_icestorm_lc, id_sb_io, id_sb_gb; IdString id_cen, id_clk, id_sr; IdString id_i0, id_i1, id_i2, id_i3; - IdString id_dff_en, id_neg_clk; + IdString id_dff_en, id_carry_en, id_neg_clk; IdString id_cin, id_cout; IdString id_o, id_lo; IdString id_icestorm_ram, id_rclk, id_wclk; @@ -801,4 +841,6 @@ struct Arch : BaseCtx float placer_constraintWeight = 10; }; +void ice40DelayFuzzerMain(Context *ctx); + NEXTPNR_NAMESPACE_END diff --git a/ice40/archdefs.h b/ice40/archdefs.h index 9329609e..7125ba16 100644 --- a/ice40/archdefs.h +++ b/ice40/archdefs.h @@ -77,17 +77,6 @@ enum PortPin : int32_t PIN_MAXIDX }; -enum WireType : int8_t -{ - WIRE_TYPE_NONE = 0, - WIRE_TYPE_LOCAL = 1, - WIRE_TYPE_GLOBAL = 2, - WIRE_TYPE_SP4_VERT = 3, - WIRE_TYPE_SP4_HORZ = 4, - WIRE_TYPE_SP12_HORZ = 5, - WIRE_TYPE_SP12_VERT = 6 -}; - struct BelId { int32_t index = -1; @@ -167,7 +156,9 @@ struct ArchCellInfo { struct { - bool dffEnable, negClk; + bool dffEnable; + bool carryEnable; + bool negClk; int inputCount; const NetInfo *clk, *cen, *sr; } lcInfo; diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 8b00e878..543d7b35 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -229,6 +229,25 @@ static BelPin get_one_bel_pin(const Context *ctx, WireId wire) return *pins.begin();
}
+// Permute LUT init value given map (LUT input -> ext input)
+unsigned permute_lut(unsigned orig_init, const std::unordered_map<int, int> &input_permute)
+{
+ unsigned new_init = 0;
+
+ for (int i = 0; i < 16; i++) {
+ int permute_address = 0;
+ for (int j = 0; j < 4; j++) {
+ if ((i >> j) & 0x1)
+ permute_address |= (1 << input_permute.at(j));
+ }
+ if ((orig_init >> i) & 0x1) {
+ new_init |= (1 << permute_address);
+ }
+ }
+
+ return new_init;
+}
+
void write_asc(const Context *ctx, std::ostream &out)
{
@@ -282,22 +301,33 @@ void write_asc(const Context *ctx, std::ostream &out) BelId sw_bel;
sw_bel.index = sw_bel_idx;
NPNR_ASSERT(ctx->getBelType(sw_bel) == TYPE_ICESTORM_LC);
- BelPin input = get_one_bel_pin(ctx, ctx->getPipSrcWire(pip));
+
+ if (ci.wire_data[ctx->getPipDstWire(pip).index].type == WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT)
+ continue; // Permutation pips
BelPin output = get_one_bel_pin(ctx, ctx->getPipDstWire(pip));
- NPNR_ASSERT(input.bel == sw_bel);
NPNR_ASSERT(output.bel == sw_bel && output.pin == PIN_O);
unsigned lut_init;
- switch (input.pin) {
- case PIN_I0:
+
+ WireId permWire;
+ for (auto permPip : ctx->getPipsUphill(ctx->getPipSrcWire(pip))) {
+ if (ctx->getBoundPipNet(permPip) != IdString()) {
+ permWire = ctx->getPipSrcWire(permPip);
+ }
+ }
+ NPNR_ASSERT(permWire != WireId());
+ std::string dName = ci.wire_data[permWire.index].name.get();
+
+ switch (dName.back()) {
+ case '0':
lut_init = 2;
break;
- case PIN_I1:
+ case '1':
lut_init = 4;
break;
- case PIN_I2:
+ case '2':
lut_init = 16;
break;
- case PIN_I3:
+ case '3':
lut_init = 256;
break;
default:
@@ -345,8 +375,49 @@ void write_asc(const Context *ctx, std::ostream &out) bool set_noreset = get_param_or_def(cell.second.get(), ctx->id("SET_NORESET"));
bool carry_enable = get_param_or_def(cell.second.get(), ctx->id("CARRY_ENABLE"));
std::vector<bool> lc(20, false);
- // From arachne-pnr
+ // Discover permutation
+ std::unordered_map<int, int> input_perm;
+ std::set<int> unused;
+ for (int i = 0; i < 4; i++)
+ unused.insert(i);
+ for (int i = 0; i < 4; i++) {
+ WireId lut_wire = ctx->getBelPinWire(bel, PortPin(PIN_I0 + i));
+ for (auto pip : ctx->getPipsUphill(lut_wire)) {
+ if (ctx->getBoundPipNet(pip) != IdString()) {
+ std::string name = ci.wire_data[ctx->getPipSrcWire(pip).index].name.get();
+ switch (name.back()) {
+ case '0':
+ input_perm[i] = 0;
+ unused.erase(0);
+ break;
+ case '1':
+ input_perm[i] = 1;
+ unused.erase(1);
+ break;
+ case '2':
+ input_perm[i] = 2;
+ unused.erase(2);
+ break;
+ case '3':
+ input_perm[i] = 3;
+ unused.erase(3);
+ break;
+ default:
+ NPNR_ASSERT_FALSE("failed to determine LUT permutation");
+ }
+ break;
+ }
+ }
+ }
+ for (int i = 0; i < 4; i++) {
+ if (!input_perm.count(i)) {
+ NPNR_ASSERT(!unused.empty());
+ input_perm[i] = *(unused.begin());
+ unused.erase(input_perm[i]);
+ }
+ }
+ lut_init = permute_lut(lut_init, input_perm);
for (int i = 0; i < 16; i++) {
if ((lut_init >> i) & 0x1)
lc.at(lut_perm.at(i)) = true;
diff --git a/ice40/chipdb.py b/ice40/chipdb.py index d782013f..b0d9e567 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -134,12 +134,21 @@ tiletypes["DSP2"] = 7 tiletypes["DSP3"] = 8 tiletypes["IPCON"] = 9 -wiretypes["LOCAL"] = 1 -wiretypes["GLOBAL"] = 2 -wiretypes["SP4_VERT"] = 3 -wiretypes["SP4_HORZ"] = 4 -wiretypes["SP12_HORZ"] = 5 -wiretypes["SP12_VERT"] = 6 +wiretypes["NONE"] = 0 +wiretypes["GLB2LOCAL"] = 1 +wiretypes["GLB_NETWK"] = 2 +wiretypes["LOCAL"] = 3 +wiretypes["LUTFF_IN"] = 4 +wiretypes["LUTFF_IN_LUT"] = 5 +wiretypes["LUTFF_LOUT"] = 6 +wiretypes["LUTFF_OUT"] = 7 +wiretypes["LUTFF_COUT"] = 8 +wiretypes["LUTFF_GLOBAL"] = 9 +wiretypes["CARRY_IN_MUX"] = 10 +wiretypes["SP4_V"] = 11 +wiretypes["SP4_H"] = 12 +wiretypes["SP12_V"] = 13 +wiretypes["SP12_H"] = 14 def maj_wire_name(name): if name[2].startswith("lutff_"): @@ -179,40 +188,84 @@ def cmp_wire_names(newname, oldname): def wire_type(name): longname = name - name = name.split('/')[-1] - wt = None - - if name.startswith("glb_netwk_") or name.startswith("padin_"): - wt = "GLOBAL" - elif name.startswith("D_IN_") or name.startswith("D_OUT_"): - wt = "LOCAL" - elif name in ("OUT_ENB", "cen", "inclk", "latch", "outclk", "clk", "s_r", "carry_in", "carry_in_mux"): - wt = "LOCAL" - elif name in ("in_0", "in_1", "in_2", "in_3", "cout", "lout", "out", "fabout") or name.startswith("slf_op") or name.startswith("O_"): - wt = "LOCAL" - elif name.startswith("local_g") or name.startswith("glb2local_"): - wt = "LOCAL" - elif name.startswith("span4_horz_") or name.startswith("sp4_h_"): - wt = "SP4_HORZ" - elif name.startswith("span4_vert_") or name.startswith("sp4_v_") or name.startswith("sp4_r_v_"): - wt = "SP4_VERT" - elif name.startswith("span12_horz_") or name.startswith("sp12_h_"): - wt = "SP12_HORZ" - elif name.startswith("span12_vert_") or name.startswith("sp12_v_"): - wt = "SP12_VERT" - elif name.startswith("MASK_") or name.startswith("RADDR_") or name.startswith("WADDR_"): - wt = "LOCAL" - elif name.startswith("RDATA_") or name.startswith("WDATA_") or name.startswith("neigh_op_"): - wt = "LOCAL" - elif name in ("WCLK", "WCLKE", "WE", "RCLK", "RCLKE", "RE"): - wt = "LOCAL" - elif name in ("PLLOUT_A", "PLLOUT_B"): - wt = "LOCAL" - - if wt is None: - print("No type for wire: %s (%s)" % (longname, name), file=sys.stderr) - assert 0 - return wt + name = name.split('/') + + if name[0].startswith("X") and name[1].startswith("Y"): + name = name[2:] + + if name[0].startswith("sp4_v_") or name[0].startswith("sp4_r_v_") or name[0].startswith("span4_vert_"): + return "SP4_V" + + if name[0].startswith("sp4_h_") or name[0].startswith("span4_horz_"): + return "SP4_H" + + if name[0].startswith("sp12_v_") or name[0].startswith("span12_vert_"): + return "SP12_V" + + if name[0].startswith("sp12_h_") or name[0].startswith("span12_horz_"): + return "SP12_H" + + if name[0].startswith("glb2local"): + return "GLB2LOCAL" + + if name[0].startswith("glb_netwk_"): + return "GLB_NETWK" + + if name[0].startswith("local_"): + return "LOCAL" + + if name[0].startswith("lutff_"): + if name[1].startswith("in_"): + return "LUTFF_IN_LUT" if name[1].endswith("_lut") else "LUTFF_IN" + + if name[1] == "lout": + return "LUTFF_LOUT" + if name[1] == "out": + return "LUTFF_OUT" + if name[1] == "cout": + return "LUTFF_COUT" + + if name[0] == "ram": + if name[1].startswith("RADDR_"): + return "LUTFF_IN" + if name[1].startswith("WADDR_"): + return "LUTFF_IN" + if name[1].startswith("WDATA_"): + return "LUTFF_IN" + if name[1].startswith("MASK_"): + return "LUTFF_IN" + if name[1].startswith("RDATA_"): + return "LUTFF_OUT" + if name[1] in ("WCLK", "WCLKE", "WE", "RCLK", "RCLKE", "RE"): + return "LUTFF_GLOBAL" + + if name[0].startswith("io_"): + if name[1].startswith("D_IN_") or name[1] == "OUT_ENB": + return "LUTFF_IN" + if name[1].startswith("D_OUT_"): + return "LUTFF_OUT" + if name[0] == "fabout": + return "LUTFF_IN" + + if name[0] == "lutff_global" or name[0] == "io_global": + return "LUTFF_GLOBAL" + + if name[0] == "carry_in_mux": + return "CARRY_IN_MUX" + + if name[0] == "carry_in": + return "LUTFF_COUT" + + if name[0].startswith("neigh_op_"): + return "NONE" + + if name[0].startswith("padin_"): + return "NONE" + + # print("No type for wire: %s (%s)" % (longname, name), file=sys.stderr) + # assert 0 + + return "NONE" def pipdelay(src_idx, dst_idx, db): if db is None: @@ -265,9 +318,12 @@ def pipdelay(src_idx, dst_idx, db): if src[2].startswith("local_") and dst[2] in ("io_0/D_OUT_0", "io_0/D_OUT_1", "io_0/OUT_ENB", "io_1/D_OUT_0", "io_1/D_OUT_1", "io_1/OUT_ENB"): return db["IoInMux.I.O"] - if re.match(r"lutff_\d+/in_\d+", dst[2]): + if re.match(r"lutff_\d+/in_\d+$", dst[2]): return db["InMux.I.O"] + if re.match(r"lutff_\d+/in_\d+_lut", dst[2]): + return 0 + if re.match(r"ram/(MASK|RADDR|WADDR|WDATA)_", dst[2]): return db["InMux.I.O"] @@ -472,7 +528,7 @@ with open(args.filename, "r") as f: wire_uphill[wire_b] = set() wire_downhill[wire_a].add(wire_b) wire_uphill[wire_b].add(wire_a) - pip_xy[(wire_a, wire_b)] = (mode[2], mode[3], int(line[0], 2), len(switches) - 1) + pip_xy[(wire_a, wire_b)] = (mode[2], mode[3], int(line[0], 2), len(switches) - 1, 0) continue if mode[0] == "bits": @@ -508,11 +564,14 @@ def add_wire(x, y, name): wire_names[wname] = wire_idx wire_names_r[wire_idx] = wname wire_segments[wire_idx] = dict() + if ("TILE_WIRE_" + wname[2].upper().replace("/", "_")) in gfx_wire_ids: + wire_segments[wire_idx][(wname[0], wname[1])] = wname[2] + return wire_idx def add_switch(x, y, bel=-1): switches.append((x, y, [], bel)) -def add_pip(src, dst): +def add_pip(src, dst, flags=0): x, y, _, _ = switches[-1] if src not in wire_downhill: @@ -523,7 +582,7 @@ def add_pip(src, dst): wire_uphill[dst] = set() wire_uphill[dst].add(src) - pip_xy[(src, dst)] = (x, y, 0, len(switches) - 1) + pip_xy[(src, dst)] = (x, y, 0, len(switches) - 1, flags) # Add virtual padin wires for i in range(8): @@ -557,10 +616,11 @@ def add_bel_lc(x, y, z): else: wire_cin = wire_names[(x, y, "lutff_%d/cout" % (z-1))] - wire_in_0 = wire_names[(x, y, "lutff_%d/in_0" % z)] - wire_in_1 = wire_names[(x, y, "lutff_%d/in_1" % z)] - wire_in_2 = wire_names[(x, y, "lutff_%d/in_2" % z)] - wire_in_3 = wire_names[(x, y, "lutff_%d/in_3" % z)] + wire_in_0 = add_wire(x, y, "lutff_%d/in_0_lut" % z) + wire_in_1 = add_wire(x, y, "lutff_%d/in_1_lut" % z) + wire_in_2 = add_wire(x, y, "lutff_%d/in_2_lut" % z) + wire_in_3 = add_wire(x, y, "lutff_%d/in_3_lut" % z) + wire_out = wire_names[(x, y, "lutff_%d/out" % z)] wire_cout = wire_names[(x, y, "lutff_%d/cout" % z)] wire_lout = wire_names[(x, y, "lutff_%d/lout" % z)] if z < 7 else None @@ -583,10 +643,21 @@ def add_bel_lc(x, y, z): # route-through LUTs add_switch(x, y, bel) - add_pip(wire_in_0, wire_out) - add_pip(wire_in_1, wire_out) - add_pip(wire_in_2, wire_out) - add_pip(wire_in_3, wire_out) + add_pip(wire_in_0, wire_out, 1) + add_pip(wire_in_1, wire_out, 1) + add_pip(wire_in_2, wire_out, 1) + add_pip(wire_in_3, wire_out, 1) + + # LUT permutation pips + for i in range(4): + add_switch(x, y, bel) + for j in range(4): + if (i == j) or ((i, j) == (1, 2)) or ((i, j) == (2, 1)): + flags = 0 + else: + flags = 2 + add_pip(wire_names[(x, y, "lutff_%d/in_%d" % (z, i))], + wire_names[(x, y, "lutff_%d/in_%d_lut" % (z, j))], flags) def add_bel_io(x, y, z): bel = len(bel_name) @@ -902,6 +973,7 @@ for wire in range(num_wires): pi["y"] = pip_xy[(src, wire)][1] pi["switch_mask"] = pip_xy[(src, wire)][2] pi["switch_index"] = pip_xy[(src, wire)][3] + pi["flags"] = pip_xy[(src, wire)][4] pipinfo.append(pi) pips.append(pipcache[(src, wire)]) num_uphill = len(pips) @@ -927,6 +999,7 @@ for wire in range(num_wires): pi["y"] = pip_xy[(wire, dst)][1] pi["switch_mask"] = pip_xy[(wire, dst)][2] pi["switch_index"] = pip_xy[(wire, dst)][3] + pi["flags"] = pip_xy[(wire, dst)][4] pipinfo.append(pi) pips.append(pipcache[(wire, dst)]) num_downhill = len(pips) @@ -959,16 +1032,20 @@ for wire in range(num_wires): info["num_bel_pins"] = num_bel_pins info["list_bel_pins"] = ("wire%d_bels" % wire) if num_bel_pins > 0 else None - avg_x, avg_y = 0, 0 if wire in wire_xy: + avg_x, avg_y = 0, 0 + for x, y in wire_xy[wire]: avg_x += x avg_y += y avg_x /= len(wire_xy[wire]) avg_y /= len(wire_xy[wire]) - info["x"] = int(round(avg_x)) - info["y"] = int(round(avg_y)) + info["x"] = int(round(avg_x)) + info["y"] = int(round(avg_y)) + else: + info["x"] = wire_names_r[wire][0] + info["y"] = wire_names_r[wire][1] wireinfo.append(info) @@ -1046,8 +1123,8 @@ for wire, info in enumerate(wireinfo): bba.u8(info["x"], "x") bba.u8(info["y"], "y") + bba.u8(0, "z") # FIXME bba.u8(wiretypes[wire_type(info["name"])], "type") - bba.u8(0, "padding") for wire in range(num_wires): if len(wire_segments[wire]): @@ -1084,6 +1161,7 @@ for info in pipinfo: bba.u16(dst_seg, "dst_seg") bba.u16(info["switch_mask"], "switch_mask") bba.u32(info["switch_index"], "switch_index") + bba.u32(info["flags"], "flags") switchinfo = [] for switch in switches: diff --git a/ice40/delay.cc b/ice40/delay.cc new file mode 100644 index 00000000..a9607140 --- /dev/null +++ b/ice40/delay.cc @@ -0,0 +1,238 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com> + * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com> + * + * 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<WireId> srcWires, dstWires; + + for (int i = 0; i < ctx->chip_info->num_wires; 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++]; + std::unordered_map<WireId, PipId> 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->getWireName(cursor).c_str(ctx), 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(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::HX8K) + return model_hx8k; + + if (args.type == ArchArgs::LP384 || args.type == ArchArgs::LP1K || args.type == ArchArgs::LP8K) + return model_lp8k; + + if (args.type == ArchArgs::UP5K) + 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(const NetInfo *net_info, const PortRef &sink) const +{ + const auto &driver = net_info->driver; + auto driver_loc = getBelLocation(driver.cell->bel); + auto sink_loc = getBelLocation(sink.cell->bel); + + if (driver.port == 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 diff --git a/ice40/gfx.cc b/ice40/gfx.cc index d5c6e77f..1ab2fb3c 100644 --- a/ice40/gfx.cc +++ b/ice40/gfx.cc @@ -391,6 +391,17 @@ void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, int z = idx / 4; int input = idx % 4; el.x1 = x + local_swbox_x2; + el.x2 = x + lut_swbox_x1; + el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch; + el.y2 = el.y1; + g.push_back(el); + } + + if (id >= TILE_WIRE_LUTFF_0_IN_0_LUT && id <= TILE_WIRE_LUTFF_7_IN_3_LUT) { + int idx = id - TILE_WIRE_LUTFF_0_IN_0_LUT; + int z = idx / 4; + int input = idx % 4; + el.x1 = x + lut_swbox_x2; el.x2 = x + logic_cell_x1; el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch; el.y2 = el.y1; @@ -706,10 +717,10 @@ void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, return; } - if (TILE_WIRE_LUTFF_0_IN_0 <= src && src <= TILE_WIRE_LUTFF_7_IN_3 && TILE_WIRE_LUTFF_0_OUT <= dst && + if (TILE_WIRE_LUTFF_0_IN_0_LUT <= src && src <= TILE_WIRE_LUTFF_7_IN_3_LUT && TILE_WIRE_LUTFF_0_OUT <= dst && dst <= TILE_WIRE_LUTFF_7_OUT) { - int lut_idx = (src - TILE_WIRE_LUTFF_0_IN_0) / 4; - int in_idx = (src - TILE_WIRE_LUTFF_0_IN_0) % 4; + int lut_idx = (src - TILE_WIRE_LUTFF_0_IN_0_LUT) / 4; + int in_idx = (src - TILE_WIRE_LUTFF_0_IN_0_LUT) % 4; GraphicElement el; el.type = GraphicElement::TYPE_ARROW; @@ -722,6 +733,23 @@ void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, return; } + if (TILE_WIRE_LUTFF_0_IN_0 <= src && src <= TILE_WIRE_LUTFF_7_IN_3 && TILE_WIRE_LUTFF_0_IN_0_LUT <= dst && + dst <= TILE_WIRE_LUTFF_7_IN_3_LUT) { + int lut_idx = (src - TILE_WIRE_LUTFF_0_IN_0) / 4; + int in_idx = (src - TILE_WIRE_LUTFF_0_IN_0) % 4; + int out_idx = (dst - TILE_WIRE_LUTFF_0_IN_0_LUT) % 4; + + GraphicElement el; + el.type = GraphicElement::TYPE_ARROW; + el.style = style; + el.x1 = x + lut_swbox_x1; + el.x2 = x + lut_swbox_x2; + el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * in_idx) + lut_idx * logic_cell_pitch; + el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * out_idx) + lut_idx * logic_cell_pitch; + g.push_back(el); + return; + } + if (src == TILE_WIRE_CARRY_IN && dst == TILE_WIRE_CARRY_IN_MUX) { GraphicElement el; el.type = GraphicElement::TYPE_ARROW; diff --git a/ice40/gfx.h b/ice40/gfx.h index 7eeaccf1..8ee7b0b6 100644 --- a/ice40/gfx.h +++ b/ice40/gfx.h @@ -34,7 +34,10 @@ const float local_swbox_x2 = 0.73; const float local_swbox_y1 = 0.05; const float local_swbox_y2 = 0.55; -const float logic_cell_x1 = 0.76; +const float lut_swbox_x1 = 0.76; +const float lut_swbox_x2 = 0.80; + +const float logic_cell_x1 = 0.83; const float logic_cell_x2 = 0.95; const float logic_cell_y1 = 0.05; const float logic_cell_y2 = 0.10; @@ -92,9 +95,6 @@ enum GfxTileWireId TILE_WIRE_LOCAL_G3_6, TILE_WIRE_LOCAL_G3_7, - TILE_WIRE_CARRY_IN, - TILE_WIRE_CARRY_IN_MUX, - TILE_WIRE_LUTFF_0_IN_0, TILE_WIRE_LUTFF_0_IN_1, TILE_WIRE_LUTFF_0_IN_2, @@ -135,6 +135,46 @@ enum GfxTileWireId TILE_WIRE_LUTFF_7_IN_2, TILE_WIRE_LUTFF_7_IN_3, + TILE_WIRE_LUTFF_0_IN_0_LUT, + TILE_WIRE_LUTFF_0_IN_1_LUT, + TILE_WIRE_LUTFF_0_IN_2_LUT, + TILE_WIRE_LUTFF_0_IN_3_LUT, + + TILE_WIRE_LUTFF_1_IN_0_LUT, + TILE_WIRE_LUTFF_1_IN_1_LUT, + TILE_WIRE_LUTFF_1_IN_2_LUT, + TILE_WIRE_LUTFF_1_IN_3_LUT, + + TILE_WIRE_LUTFF_2_IN_0_LUT, + TILE_WIRE_LUTFF_2_IN_1_LUT, + TILE_WIRE_LUTFF_2_IN_2_LUT, + TILE_WIRE_LUTFF_2_IN_3_LUT, + + TILE_WIRE_LUTFF_3_IN_0_LUT, + TILE_WIRE_LUTFF_3_IN_1_LUT, + TILE_WIRE_LUTFF_3_IN_2_LUT, + TILE_WIRE_LUTFF_3_IN_3_LUT, + + TILE_WIRE_LUTFF_4_IN_0_LUT, + TILE_WIRE_LUTFF_4_IN_1_LUT, + TILE_WIRE_LUTFF_4_IN_2_LUT, + TILE_WIRE_LUTFF_4_IN_3_LUT, + + TILE_WIRE_LUTFF_5_IN_0_LUT, + TILE_WIRE_LUTFF_5_IN_1_LUT, + TILE_WIRE_LUTFF_5_IN_2_LUT, + TILE_WIRE_LUTFF_5_IN_3_LUT, + + TILE_WIRE_LUTFF_6_IN_0_LUT, + TILE_WIRE_LUTFF_6_IN_1_LUT, + TILE_WIRE_LUTFF_6_IN_2_LUT, + TILE_WIRE_LUTFF_6_IN_3_LUT, + + TILE_WIRE_LUTFF_7_IN_0_LUT, + TILE_WIRE_LUTFF_7_IN_1_LUT, + TILE_WIRE_LUTFF_7_IN_2_LUT, + TILE_WIRE_LUTFF_7_IN_3_LUT, + TILE_WIRE_LUTFF_0_LOUT, TILE_WIRE_LUTFF_1_LOUT, TILE_WIRE_LUTFF_2_LOUT, @@ -165,6 +205,9 @@ enum GfxTileWireId TILE_WIRE_LUTFF_GLOBAL_CLK, TILE_WIRE_LUTFF_GLOBAL_S_R, + TILE_WIRE_CARRY_IN, + TILE_WIRE_CARRY_IN_MUX, + TILE_WIRE_NEIGH_OP_BNL_0, TILE_WIRE_NEIGH_OP_BNL_1, TILE_WIRE_NEIGH_OP_BNL_2, diff --git a/ice40/main.cc b/ice40/main.cc index 46cdce71..358bf3c5 100644 --- a/ice40/main.cc +++ b/ice40/main.cc @@ -45,26 +45,6 @@ USING_NEXTPNR_NAMESPACE -void svg_dump_decal(const Context *ctx, const DecalXY &decal) -{ - const float scale = 10.0, offset = 10.0; - const std::string style = "stroke=\"black\" stroke-width=\"0.1\" fill=\"none\""; - - for (auto &el : ctx->getDecalGraphics(decal.decal)) { - if (el.type == GraphicElement::TYPE_BOX) { - std::cout << "<rect x=\"" << (offset + scale * (decal.x + el.x1)) << "\" y=\"" - << (offset + scale * (decal.y + el.y1)) << "\" height=\"" << (scale * (el.y2 - el.y1)) - << "\" width=\"" << (scale * (el.x2 - el.x1)) << "\" " << style << "/>\n"; - } - - if (el.type == GraphicElement::TYPE_LINE) { - std::cout << "<line x1=\"" << (offset + scale * (decal.x + el.x1)) << "\" y1=\"" - << (offset + scale * (decal.y + el.y1)) << "\" x2=\"" << (offset + scale * (decal.x + el.x2)) - << "\" y2=\"" << (offset + scale * (decal.y + el.y2)) << "\" " << style << "/>\n"; - } - } -} - void conflicting_options(const boost::program_options::variables_map &vm, const char *opt1, const char *opt2) { if (vm.count(opt1) && !vm[opt1].defaulted() && vm.count(opt2) && !vm[opt2].defaulted()) { @@ -91,7 +71,6 @@ int main(int argc, char *argv[]) #ifndef NO_GUI options.add_options()("gui", "start gui"); #endif - options.add_options()("svg", "dump SVG file"); options.add_options()("pack-only", "pack design only without placement or routing"); po::positional_options_description pos; @@ -332,64 +311,11 @@ int main(int argc, char *argv[]) ctx->placer_constraintWeight = vm["cstrweight"].as<float>(); } - if (vm.count("svg")) { - std::cout << "<svg xmlns=\"http://www.w3.org/2000/svg\" " - "xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"; - for (auto bel : ctx->getBels()) { - std::cout << "<!-- " << ctx->getBelName(bel).str(ctx.get()) << " -->\n"; - svg_dump_decal(ctx.get(), ctx->getBelDecal(bel)); - } - std::cout << "</svg>\n"; - } - if (vm.count("test")) ctx->archcheck(); - if (vm.count("tmfuzz")) { - std::vector<WireId> src_wires, dst_wires; - - /*for (auto w : ctx->getWires()) - src_wires.push_back(w);*/ - for (auto b : ctx->getBels()) { - if (ctx->getBelType(b) == TYPE_ICESTORM_LC) { - src_wires.push_back(ctx->getBelPinWire(b, PIN_O)); - } - if (ctx->getBelType(b) == TYPE_SB_IO) { - src_wires.push_back(ctx->getBelPinWire(b, PIN_D_IN_0)); - } - } - - for (auto b : ctx->getBels()) { - if (ctx->getBelType(b) == TYPE_ICESTORM_LC) { - dst_wires.push_back(ctx->getBelPinWire(b, PIN_I0)); - dst_wires.push_back(ctx->getBelPinWire(b, PIN_I1)); - dst_wires.push_back(ctx->getBelPinWire(b, PIN_I2)); - dst_wires.push_back(ctx->getBelPinWire(b, PIN_I3)); - dst_wires.push_back(ctx->getBelPinWire(b, PIN_CEN)); - dst_wires.push_back(ctx->getBelPinWire(b, PIN_CIN)); - } - if (ctx->getBelType(b) == TYPE_SB_IO) { - dst_wires.push_back(ctx->getBelPinWire(b, PIN_D_OUT_0)); - dst_wires.push_back(ctx->getBelPinWire(b, PIN_OUTPUT_ENABLE)); - } - } - - ctx->shuffle(src_wires); - ctx->shuffle(dst_wires); - - for (int i = 0; i < int(src_wires.size()) && i < int(dst_wires.size()); i++) { - delay_t actual_delay; - WireId src = src_wires[i], dst = dst_wires[i]; - if (!ctx->getActualRouteDelay(src, dst, actual_delay)) - continue; - printf("%s %s %.3f %.3f %d %d %d %d %d %d\n", ctx->getWireName(src).c_str(ctx.get()), - ctx->getWireName(dst).c_str(ctx.get()), ctx->getDelayNS(actual_delay), - ctx->getDelayNS(ctx->estimateDelay(src, dst)), ctx->chip_info->wire_data[src.index].x, - ctx->chip_info->wire_data[src.index].y, ctx->chip_info->wire_data[src.index].type, - ctx->chip_info->wire_data[dst.index].x, ctx->chip_info->wire_data[dst.index].y, - ctx->chip_info->wire_data[dst.index].type); - } - } + if (vm.count("tmfuzz")) + ice40DelayFuzzerMain(ctx.get()); if (vm.count("freq")) { auto freq = vm["freq"].as<double>(); diff --git a/ice40/tmfuzz.py b/ice40/tmfuzz.py new file mode 100644 index 00000000..4ec2a546 --- /dev/null +++ b/ice40/tmfuzz.py @@ -0,0 +1,357 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# ../nextpnr-ice40 --hx8k --tmfuzz > tmfuzz_hx8k.txt +# ../nextpnr-ice40 --lp8k --tmfuzz > tmfuzz_lp8k.txt +# ../nextpnr-ice40 --up5k --tmfuzz > tmfuzz_up5k.txt + +import numpy as np +import matplotlib.pyplot as plt +from collections import defaultdict + +device = "hx8k" +# device = "lp8k" +# device = "up5k" + +sel_src_type = "LUTFF_OUT" +sel_dst_type = "LUTFF_IN_LUT" + +#%% Read fuzz data + +src_dst_pairs = defaultdict(lambda: 0) + +delay_data = list() +all_delay_data = list() + +delay_map_sum = np.zeros((41, 41)) +delay_map_sum2 = np.zeros((41, 41)) +delay_map_count = np.zeros((41, 41)) + +same_tile_delays = list() +neighbour_tile_delays = list() + +type_delta_data = dict() + +with open("tmfuzz_%s.txt" % device, "r") as f: + for line in f: + line = line.split() + + if line[0] == "dst": + dst_xy = (int(line[1]), int(line[2])) + dst_type = line[3] + dst_wire = line[4] + + src_xy = (int(line[1]), int(line[2])) + src_type = line[3] + src_wire = line[4] + + delay = int(line[5]) + estdelay = int(line[6]) + + all_delay_data.append((delay, estdelay)) + + src_dst_pairs[src_type, dst_type] += 1 + + dx = dst_xy[0] - src_xy[0] + dy = dst_xy[1] - src_xy[1] + + if src_type == sel_src_type and dst_type == sel_dst_type: + if dx == 0 and dy == 0: + same_tile_delays.append(delay) + + elif abs(dx) <= 1 and abs(dy) <= 1: + neighbour_tile_delays.append(delay) + + else: + delay_data.append((delay, estdelay, dx, dy, 0, 0, 0)) + + relx = 20 + dst_xy[0] - src_xy[0] + rely = 20 + dst_xy[1] - src_xy[1] + + if (0 <= relx <= 40) and (0 <= rely <= 40): + delay_map_sum[relx, rely] += delay + delay_map_sum2[relx, rely] += delay*delay + delay_map_count[relx, rely] += 1 + + if dst_type == sel_dst_type: + if src_type not in type_delta_data: + type_delta_data[src_type] = list() + + type_delta_data[src_type].append((dx, dy, delay)) + +delay_data = np.array(delay_data) +all_delay_data = np.array(all_delay_data) +max_delay = np.max(delay_data[:, 0:2]) + +mean_same_tile_delays = np.mean(neighbour_tile_delays) +mean_neighbour_tile_delays = np.mean(neighbour_tile_delays) + +print("Avg same tile delay: %.2f (%.2f std, N=%d)" % \ + (mean_same_tile_delays, np.std(same_tile_delays), len(same_tile_delays))) +print("Avg neighbour tile delay: %.2f (%.2f std, N=%d)" % \ + (mean_neighbour_tile_delays, np.std(neighbour_tile_delays), len(neighbour_tile_delays))) + +#%% Apply simple low-weight bluring to fill gaps + +for i in range(0): + neigh_sum = np.zeros((41, 41)) + neigh_sum2 = np.zeros((41, 41)) + neigh_count = np.zeros((41, 41)) + + for x in range(41): + for y in range(41): + for p in range(-1, 2): + for q in range(-1, 2): + if p == 0 and q == 0: + continue + if 0 <= (x+p) <= 40: + if 0 <= (y+q) <= 40: + neigh_sum[x, y] += delay_map_sum[x+p, y+q] + neigh_sum2[x, y] += delay_map_sum2[x+p, y+q] + neigh_count[x, y] += delay_map_count[x+p, y+q] + + delay_map_sum += 0.1 * neigh_sum + delay_map_sum2 += 0.1 * neigh_sum2 + delay_map_count += 0.1 * neigh_count + +delay_map = delay_map_sum / delay_map_count +delay_map_std = np.sqrt(delay_map_count*delay_map_sum2 - delay_map_sum**2) / delay_map_count + +#%% Print src-dst-pair summary + +print("Src-Dst-Type pair summary:") +for cnt, src, dst in sorted([(v, k[0], k[1]) for k, v in src_dst_pairs.items()]): + print("%20s %20s %5d%s" % (src, dst, cnt, " *" if src == sel_src_type and dst == sel_dst_type else "")) +print() + +#%% Plot estimate vs actual delay + +plt.figure(figsize=(8, 3)) +plt.title("Estimate vs Actual Delay") +plt.plot(all_delay_data[:, 0], all_delay_data[:, 1], ".") +plt.plot(delay_data[:, 0], delay_data[:, 1], ".") +plt.plot([0, max_delay], [0, max_delay], "k") +plt.ylabel("Estimated Delay") +plt.xlabel("Actual Delay") +plt.grid() +plt.show() + +#%% Plot delay heatmap and std dev heatmap + +plt.figure(figsize=(9, 3)) +plt.subplot(121) +plt.title("Actual Delay Map") +plt.imshow(delay_map) +plt.colorbar() +plt.subplot(122) +plt.title("Standard Deviation") +plt.imshow(delay_map_std) +plt.colorbar() +plt.show() + +#%% Generate Model #0 + +def nonlinearPreprocessor0(dx, dy): + dx, dy = abs(dx), abs(dy) + values = [1.0] + values.append(dx + dy) + return np.array(values) + +A = np.zeros((41*41, len(nonlinearPreprocessor0(0, 0)))) +b = np.zeros(41*41) + +index = 0 +for x in range(41): + for y in range(41): + if delay_map_count[x, y] > 0: + A[index, :] = nonlinearPreprocessor0(x-20, y-20) + b[index] = delay_map[x, y] + index += 1 + +model0_params, _, _, _ = np.linalg.lstsq(A, b) +print("Model #0 parameters:", model0_params) + +model0_map = np.zeros((41, 41)) +for x in range(41): + for y in range(41): + v = np.dot(model0_params, nonlinearPreprocessor0(x-20, y-20)) + model0_map[x, y] = v + +plt.figure(figsize=(9, 3)) +plt.subplot(121) +plt.title("Model #0 Delay Map") +plt.imshow(model0_map) +plt.colorbar() +plt.subplot(122) +plt.title("Model #0 Error Map") +plt.imshow(model0_map - delay_map) +plt.colorbar() +plt.show() + +for i in range(delay_data.shape[0]): + dx = delay_data[i, 2] + dy = delay_data[i, 3] + delay_data[i, 4] = np.dot(model0_params, nonlinearPreprocessor0(dx, dy)) + +plt.figure(figsize=(8, 3)) +plt.title("Model #0 vs Actual Delay") +plt.plot(delay_data[:, 0], delay_data[:, 4], ".") +plt.plot(delay_map.flat, model0_map.flat, ".") +plt.plot([0, max_delay], [0, max_delay], "k") +plt.ylabel("Model #0 Delay") +plt.xlabel("Actual Delay") +plt.grid() +plt.show() + +print("In-sample RMS error: %f" % np.sqrt(np.nanmean((delay_map - model0_map)**2))) +print("Out-of-sample RMS error: %f" % np.sqrt(np.nanmean((delay_data[:, 0] - delay_data[:, 4])**2))) +print() + +#%% Generate Model #1 + +def nonlinearPreprocessor1(dx, dy): + dx, dy = abs(dx), abs(dy) + values = [1.0] + values.append(dx + dy) # 1-norm + values.append((dx**2 + dy**2)**(1/2)) # 2-norm + values.append((dx**3 + dy**3)**(1/3)) # 3-norm + return np.array(values) + +A = np.zeros((41*41, len(nonlinearPreprocessor1(0, 0)))) +b = np.zeros(41*41) + +index = 0 +for x in range(41): + for y in range(41): + if delay_map_count[x, y] > 0: + A[index, :] = nonlinearPreprocessor1(x-20, y-20) + b[index] = delay_map[x, y] + index += 1 + +model1_params, _, _, _ = np.linalg.lstsq(A, b) +print("Model #1 parameters:", model1_params) + +model1_map = np.zeros((41, 41)) +for x in range(41): + for y in range(41): + v = np.dot(model1_params, nonlinearPreprocessor1(x-20, y-20)) + model1_map[x, y] = v + +plt.figure(figsize=(9, 3)) +plt.subplot(121) +plt.title("Model #1 Delay Map") +plt.imshow(model1_map) +plt.colorbar() +plt.subplot(122) +plt.title("Model #1 Error Map") +plt.imshow(model1_map - delay_map) +plt.colorbar() +plt.show() + +for i in range(delay_data.shape[0]): + dx = delay_data[i, 2] + dy = delay_data[i, 3] + delay_data[i, 5] = np.dot(model1_params, nonlinearPreprocessor1(dx, dy)) + +plt.figure(figsize=(8, 3)) +plt.title("Model #1 vs Actual Delay") +plt.plot(delay_data[:, 0], delay_data[:, 5], ".") +plt.plot(delay_map.flat, model1_map.flat, ".") +plt.plot([0, max_delay], [0, max_delay], "k") +plt.ylabel("Model #1 Delay") +plt.xlabel("Actual Delay") +plt.grid() +plt.show() + +print("In-sample RMS error: %f" % np.sqrt(np.nanmean((delay_map - model1_map)**2))) +print("Out-of-sample RMS error: %f" % np.sqrt(np.nanmean((delay_data[:, 0] - delay_data[:, 5])**2))) +print() + +#%% Generate Model #2 + +def nonlinearPreprocessor2(v): + return np.array([1, v, np.sqrt(v)]) + +A = np.zeros((41*41, len(nonlinearPreprocessor2(0)))) +b = np.zeros(41*41) + +index = 0 +for x in range(41): + for y in range(41): + if delay_map_count[x, y] > 0: + A[index, :] = nonlinearPreprocessor2(model1_map[x, y]) + b[index] = delay_map[x, y] + index += 1 + +model2_params, _, _, _ = np.linalg.lstsq(A, b) +print("Model #2 parameters:", model2_params) + +model2_map = np.zeros((41, 41)) +for x in range(41): + for y in range(41): + v = np.dot(model1_params, nonlinearPreprocessor1(x-20, y-20)) + v = np.dot(model2_params, nonlinearPreprocessor2(v)) + model2_map[x, y] = v + +plt.figure(figsize=(9, 3)) +plt.subplot(121) +plt.title("Model #2 Delay Map") +plt.imshow(model2_map) +plt.colorbar() +plt.subplot(122) +plt.title("Model #2 Error Map") +plt.imshow(model2_map - delay_map) +plt.colorbar() +plt.show() + +for i in range(delay_data.shape[0]): + dx = delay_data[i, 2] + dy = delay_data[i, 3] + delay_data[i, 6] = np.dot(model2_params, nonlinearPreprocessor2(delay_data[i, 5])) + +plt.figure(figsize=(8, 3)) +plt.title("Model #2 vs Actual Delay") +plt.plot(delay_data[:, 0], delay_data[:, 6], ".") +plt.plot(delay_map.flat, model2_map.flat, ".") +plt.plot([0, max_delay], [0, max_delay], "k") +plt.ylabel("Model #2 Delay") +plt.xlabel("Actual Delay") +plt.grid() +plt.show() + +print("In-sample RMS error: %f" % np.sqrt(np.nanmean((delay_map - model2_map)**2))) +print("Out-of-sample RMS error: %f" % np.sqrt(np.nanmean((delay_data[:, 0] - delay_data[:, 6])**2))) +print() + +#%% Generate deltas for different source net types + +type_deltas = dict() + +print("Delay deltas for different src types:") +for src_type in sorted(type_delta_data.keys()): + deltas = list() + + for dx, dy, delay in type_delta_data[src_type]: + dx = abs(dx) + dy = abs(dy) + + if dx > 1 or dy > 1: + est = model0_params[0] + model0_params[1] * (dx + dy) + else: + est = mean_neighbour_tile_delays + deltas.append(delay - est) + + print("%15s: %8.2f (std %6.2f)" % (\ + src_type, np.mean(deltas), np.std(deltas))) + + type_deltas[src_type] = np.mean(deltas) + +#%% Print C defs of model parameters + +print("--snip--") +print("%d, %d, %d," % (mean_neighbour_tile_delays, 128 * model0_params[0], 128 * model0_params[1])) +print("%d, %d, %d, %d," % (128 * model1_params[0], 128 * model1_params[1], 128 * model1_params[2], 128 * model1_params[3])) +print("%d, %d, %d," % (128 * model2_params[0], 128 * model2_params[1], 128 * model2_params[2])) +print("%d, %d, %d, %d" % (type_deltas["LOCAL"], type_deltas["LUTFF_IN"], \ + (type_deltas["SP4_H"] + type_deltas["SP4_V"]) / 2, + (type_deltas["SP12_H"] + type_deltas["SP12_V"]) / 2)) +print("--snap--") |