Merge pull request #36 from YosysHQ/lutperm

Add LUT input permutations, improvements in ice40 timing model, improvements in router
This commit is contained in:
David Shah 2018-08-05 14:31:43 +02:00 committed by GitHub
commit ba97c233fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1031 additions and 225 deletions

View File

@ -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);
// --------------------------------------------------------------

View File

@ -130,6 +130,7 @@ struct Router
qw.wire = it.first;
qw.pip = PipId();
qw.delay = it.second - (it.second / 16);
if (cfg.useEstimate)
qw.togo = ctx->estimateDelay(qw.wire, dst_wire);
qw.randtag = ctx->rng();
@ -216,6 +217,7 @@ struct Router
next_qw.wire = next_wire;
next_qw.pip = pip;
next_qw.delay = next_delay;
if (cfg.useEstimate)
next_qw.togo = ctx->estimateDelay(next_wire, dst_wire);
next_qw.randtag = ctx->rng();
@ -420,7 +422,9 @@ struct Router
NPNR_ASSERT(ripup);
NPNR_ASSERT(conflicting_pip_net != net_name);
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

View File

@ -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);

View File

@ -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);

View File

@ -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())
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

View File

@ -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;

View File

@ -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;

View File

@ -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
name = name.split('/')
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 name[0].startswith("X") and name[1].startswith("Y"):
name = name[2:]
if wt is None:
print("No type for wire: %s (%s)" % (longname, name), file=sys.stderr)
assert 0
return wt
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,8 +1032,9 @@ 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
@ -969,6 +1043,9 @@ for wire in range(num_wires):
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:

238
ice40/delay.cc Normal file
View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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>();

357
ice40/tmfuzz.py Normal file
View File

@ -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--")