aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/nextpnr.h3
-rw-r--r--common/router1.cc40
-rw-r--r--common/router1.h1
-rw-r--r--ice40/arch.cc102
-rw-r--r--ice40/arch.h64
-rw-r--r--ice40/archdefs.h15
-rw-r--r--ice40/bitstream.cc87
-rw-r--r--ice40/chipdb.py190
-rw-r--r--ice40/delay.cc238
-rw-r--r--ice40/gfx.cc34
-rw-r--r--ice40/gfx.h51
-rw-r--r--ice40/main.cc78
-rw-r--r--ice40/tmfuzz.py357
13 files changed, 1033 insertions, 227 deletions
diff --git a/common/nextpnr.h b/common/nextpnr.h
index c87a98d9..bb55d4ff 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -484,7 +484,8 @@ struct Context : Arch, DeterministicRNG
delay_t getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &sink) const;
// provided by router1.cc
- bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t &delay);
+ bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay = nullptr,
+ std::unordered_map<WireId, PipId> *route = nullptr, bool useEstimate = true);
// --------------------------------------------------------------
diff --git a/common/router1.cc b/common/router1.cc
index dd338b35..77e84696 100644
--- a/common/router1.cc
+++ b/common/router1.cc
@@ -130,7 +130,8 @@ struct Router
qw.wire = it.first;
qw.pip = PipId();
qw.delay = it.second - (it.second / 16);
- qw.togo = ctx->estimateDelay(qw.wire, dst_wire);
+ if (cfg.useEstimate)
+ qw.togo = ctx->estimateDelay(qw.wire, dst_wire);
qw.randtag = ctx->rng();
queue.push(qw);
@@ -216,7 +217,8 @@ struct Router
next_qw.wire = next_wire;
next_qw.pip = pip;
next_qw.delay = next_delay;
- next_qw.togo = ctx->estimateDelay(next_wire, dst_wire);
+ if (cfg.useEstimate)
+ next_qw.togo = ctx->estimateDelay(next_wire, dst_wire);
next_qw.randtag = ctx->rng();
visited[next_qw.wire] = next_qw;
@@ -420,7 +422,9 @@ struct Router
NPNR_ASSERT(ripup);
NPNR_ASSERT(conflicting_pip_net != net_name);
- ctx->unbindPip(pip);
+ if (ctx->getBoundPipNet(pip) == conflicting_pip_net)
+ ctx->unbindPip(pip);
+
if (!ctx->checkPipAvail(pip))
ripup_net(ctx, conflicting_pip_net);
@@ -945,13 +949,33 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
}
}
-bool Context::getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t &delay)
+bool Context::getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay,
+ std::unordered_map<WireId, PipId> *route, bool useEstimate)
{
RipupScoreboard scores;
- Router router(this, Router1Cfg(), scores, src_wire, dst_wire);
- if (router.routedOkay)
- delay = router.visited.at(dst_wire).delay;
- return router.routedOkay;
+ Router1Cfg cfg;
+ cfg.useEstimate = useEstimate;
+
+ Router router(this, cfg, scores, src_wire, dst_wire);
+
+ if (!router.routedOkay)
+ return false;
+
+ if (delay != nullptr)
+ *delay = router.visited.at(dst_wire).delay;
+
+ if (route != nullptr) {
+ WireId cursor = dst_wire;
+ while (1) {
+ PipId pip = router.visited.at(cursor).pip;
+ (*route)[cursor] = pip;
+ if (pip == PipId())
+ break;
+ cursor = getPipSrcWire(pip);
+ }
+ }
+
+ return true;
}
NEXTPNR_NAMESPACE_END
diff --git a/common/router1.h b/common/router1.h
index a9e84b6b..0380adc2 100644
--- a/common/router1.h
+++ b/common/router1.h
@@ -29,6 +29,7 @@ struct Router1Cfg
int maxIterCnt = 200;
bool cleanupReroute = true;
bool fullCleanupReroute = true;
+ bool useEstimate = true;
};
extern bool router1(Context *ctx, const Router1Cfg &cfg);
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--")