aboutsummaryrefslogtreecommitdiffstats
path: root/ice40
diff options
context:
space:
mode:
Diffstat (limited to 'ice40')
-rw-r--r--ice40/arch.cc108
-rw-r--r--ice40/arch.h9
-rw-r--r--ice40/bitstream.cc10
-rw-r--r--ice40/cells.cc3
-rw-r--r--ice40/chipdb.py216
-rw-r--r--ice40/family.cmake4
-rw-r--r--ice40/gfx.cc319
-rw-r--r--ice40/gfx.h202
-rw-r--r--ice40/pack.cc37
-rw-r--r--ice40/pcf.cc2
-rw-r--r--ice40/project.cc4
11 files changed, 839 insertions, 75 deletions
diff --git a/ice40/arch.cc b/ice40/arch.cc
index 7e2dd7f7..3983a24e 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -226,6 +226,15 @@ PortType Arch::getBelPinType(BelId bel, IdString pin) const
return PORT_INOUT;
}
+std::vector<std::pair<IdString, std::string>> Arch::getBelAttrs(BelId bel) const
+{
+ std::vector<std::pair<IdString, std::string>> ret;
+
+ ret.push_back(std::make_pair(id("INDEX"), stringf("%d", bel.index)));
+
+ return ret;
+}
+
WireId Arch::getBelPinWire(BelId bel, IdString pin) const
{
WireId ret;
@@ -331,6 +340,28 @@ IdString Arch::getWireType(WireId wire) const
return IdString();
}
+std::vector<std::pair<IdString, std::string>> Arch::getWireAttrs(WireId wire) const
+{
+ std::vector<std::pair<IdString, std::string>> ret;
+ auto &wi = chip_info->wire_data[wire.index];
+
+ ret.push_back(std::make_pair(id("INDEX"), stringf("%d", wire.index)));
+
+ ret.push_back(std::make_pair(id("GRID_X"), stringf("%d", wi.x)));
+ ret.push_back(std::make_pair(id("GRID_Y"), stringf("%d", wi.y)));
+ ret.push_back(std::make_pair(id("GRID_Z"), stringf("%d", wi.z)));
+
+#if 0
+ for (int i = 0; i < wi.num_segments; i++) {
+ auto &si = wi.segments[i];
+ ret.push_back(std::make_pair(id(stringf("segment[%d]", i)),
+ stringf("X%d/Y%d/%s", si.x, si.y, chip_info->tile_wire_names[si.index].get())));
+ }
+#endif
+
+ return ret;
+}
+
// -----------------------------------------------------------------------
PipId Arch::getPipByName(IdString name) const
@@ -372,6 +403,18 @@ IdString Arch::getPipName(PipId pip) const
#endif
}
+IdString Arch::getPipType(PipId pip) const { return IdString(); }
+
+std::vector<std::pair<IdString, std::string>> Arch::getPipAttrs(PipId pip) const
+{
+ std::vector<std::pair<IdString, std::string>> ret;
+
+ ret.push_back(std::make_pair(id("INDEX"), stringf("%d", pip.index)));
+
+ return ret;
+}
+
+
// -----------------------------------------------------------------------
BelId Arch::getPackagePinBel(const std::string &pin) const
@@ -565,18 +608,9 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay
// -----------------------------------------------------------------------
-bool Arch::place()
-{
- Placer1Cfg cfg;
- cfg.constraintWeight = placer_constraintWeight;
- return placer1(getCtx(), cfg);
-}
+bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); }
-bool Arch::route()
-{
- Router1Cfg cfg;
- return router1(getCtx(), cfg);
-}
+bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); }
// -----------------------------------------------------------------------
@@ -695,13 +729,29 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
for (int i = 0; i < n; i++)
- gfxTileWire(ret, p[i].x, p[i].y, GfxTileWireId(p[i].index), style);
+ gfxTileWire(ret, p[i].x, p[i].y, chip_info->width, chip_info->height, GfxTileWireId(p[i].index), style);
+
+#if 0
+ if (ret.empty()) {
+ WireId wire;
+ wire.index = decal.index;
+ log_warning("No gfx decal for wire %s (%d).\n", getWireName(wire).c_str(getCtx()), decal.index);
+ }
+#endif
}
if (decal.type == DecalId::TYPE_PIP) {
const PipInfoPOD &p = chip_info->pip_data[decal.index];
GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_HIDDEN;
gfxTilePip(ret, p.x, p.y, GfxTileWireId(p.src_seg), GfxTileWireId(p.dst_seg), style);
+
+#if 0
+ if (ret.empty()) {
+ PipId pip;
+ pip.index = decal.index;
+ log_warning("No gfx decal for pip %s (%d).\n", getPipName(pip).c_str(getCtx()), decal.index);
+ }
+#endif
}
if (decal.type == DecalId::TYPE_BEL) {
@@ -727,7 +777,7 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
GraphicElement el;
el.type = GraphicElement::TYPE_BOX;
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
- el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
+ el.x1 = chip_info->bel_data[bel.index].x + lut_swbox_x1;
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 +
(4 * chip_info->bel_data[bel.index].z) * logic_cell_pitch;
@@ -741,13 +791,43 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
GraphicElement el;
el.type = GraphicElement::TYPE_BOX;
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
- el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
+ el.x1 = chip_info->bel_data[bel.index].x + lut_swbox_x1;
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 + i;
el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + i + 7 * logic_cell_pitch;
ret.push_back(el);
}
}
+
+ if (bel_type == id_SB_GB) {
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_BOX;
+ el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
+ el.x1 = chip_info->bel_data[bel.index].x + local_swbox_x1 + 0.05;
+ el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2 - 0.05;
+ el.y1 = chip_info->bel_data[bel.index].y + main_swbox_y2 - 0.05;
+ el.y2 = chip_info->bel_data[bel.index].y + main_swbox_y2 - 0.10;
+ ret.push_back(el);
+ }
+
+ if (bel_type == id_ICESTORM_PLL || bel_type == id_SB_WARMBOOT) {
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_BOX;
+ el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
+ el.x1 = chip_info->bel_data[bel.index].x + local_swbox_x1 + 0.05;
+ el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2 - 0.05;
+ el.y1 = chip_info->bel_data[bel.index].y + main_swbox_y2;
+ el.y2 = chip_info->bel_data[bel.index].y + main_swbox_y2 + 0.05;
+ ret.push_back(el);
+ }
+
+#if 0
+ if (ret.empty()) {
+ BelId bel;
+ bel.index = decal.index;
+ log_warning("No gfx decal for bel %s (%d).\n", getBelName(bel).c_str(getCtx()), decal.index);
+ }
+#endif
}
return ret;
diff --git a/ice40/arch.h b/ice40/arch.h
index 8fd1af23..37f663d9 100644
--- a/ice40/arch.h
+++ b/ice40/arch.h
@@ -225,6 +225,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD {
RelPtr<BelConfigPOD> bel_config;
RelPtr<PackageInfoPOD> packages_data;
RelPtr<CellTimingPOD> cell_timing;
+ RelPtr<RelPtr<char>> tile_wire_names;
});
#if defined(_MSC_VER)
@@ -502,6 +503,8 @@ struct Arch : BaseCtx
return IdString(chip_info->bel_data[bel.index].type);
}
+ std::vector<std::pair<IdString, std::string>> getBelAttrs(BelId bel) const;
+
WireId getBelPinWire(BelId bel, IdString pin) const;
PortType getBelPinType(BelId bel, IdString pin) const;
std::vector<IdString> getBelPins(BelId bel) const;
@@ -517,6 +520,7 @@ struct Arch : BaseCtx
}
IdString getWireType(WireId wire) const;
+ std::vector<std::pair<IdString, std::string>> getWireAttrs(WireId wire) const;
uint32_t getWireChecksum(WireId wire) const { return wire.index; }
@@ -692,7 +696,8 @@ struct Arch : BaseCtx
IdString getPipName(PipId pip) const;
- IdString getPipType(PipId pip) const { return IdString(); }
+ IdString getPipType(PipId pip) const;
+ std::vector<std::pair<IdString, std::string>> getPipAttrs(PipId pip) const;
uint32_t getPipChecksum(PipId pip) const { return pip.index; }
@@ -832,8 +837,6 @@ struct Arch : BaseCtx
}
NPNR_ASSERT_FALSE("Expected PLL pin to share an output with an SB_IO D_IN_{0,1}");
}
-
- float placer_constraintWeight = 10;
};
void ice40DelayFuzzerMain(Context *ctx);
diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc
index ee276e49..4ea91011 100644
--- a/ice40/bitstream.cc
+++ b/ice40/bitstream.cc
@@ -870,10 +870,12 @@ bool read_asc(Context *ctx, std::istream &in)
}
if (isUsed) {
NetInfo *net = ctx->wire_to_net[pi.dst];
- WireId wire;
- wire.index = pi.dst;
- ctx->unbindWire(wire);
- ctx->bindPip(pip, net, STRENGTH_WEAK);
+ if (net!=nullptr) {
+ WireId wire;
+ wire.index = pi.dst;
+ ctx->unbindWire(wire);
+ ctx->bindPip(pip, net, STRENGTH_WEAK);
+ }
}
}
for (auto bel : ctx->getBels()) {
diff --git a/ice40/cells.cc b/ice40/cells.cc
index 5bdc7990..e79a1fda 100644
--- a/ice40/cells.cc
+++ b/ice40/cells.cc
@@ -28,6 +28,7 @@ NEXTPNR_NAMESPACE_BEGIN
void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir)
{
IdString id = ctx->id(name);
+ NPNR_ASSERT(cell->ports.count(id) == 0);
cell->ports[id] = PortInfo{id, nullptr, dir};
}
@@ -237,7 +238,7 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
add_port(ctx, new_cell.get(), "SCLK", PORT_IN);
add_port(ctx, new_cell.get(), "SDI", PORT_IN);
- add_port(ctx, new_cell.get(), "SDI", PORT_OUT);
+ add_port(ctx, new_cell.get(), "SDO", PORT_OUT);
add_port(ctx, new_cell.get(), "LOCK", PORT_OUT);
add_port(ctx, new_cell.get(), "PLLOUT_A", PORT_OUT);
diff --git a/ice40/chipdb.py b/ice40/chipdb.py
index 7bdf82f0..5b2f3e57 100644
--- a/ice40/chipdb.py
+++ b/ice40/chipdb.py
@@ -50,6 +50,7 @@ tiletypes = dict()
wiretypes = dict()
gfx_wire_ids = dict()
+gfx_wire_names = list()
wire_segments = dict()
fast_timings = None
@@ -93,6 +94,136 @@ with open(args.gfxh) as f:
idx = len(gfx_wire_ids)
name = line.strip().rstrip(",")
gfx_wire_ids[name] = idx
+ gfx_wire_names.append(name)
+
+def gfx_wire_alias(old, new):
+ assert old in gfx_wire_ids
+ assert new not in gfx_wire_ids
+ gfx_wire_ids[new] = gfx_wire_ids[old]
+
+# GFX aliases for RAM tiles
+
+gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_0", "TILE_WIRE_RAM_RADDR_0")
+gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_1", "TILE_WIRE_RAM_RADDR_1")
+gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_2", "TILE_WIRE_RAM_RADDR_2")
+gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_3", "TILE_WIRE_RAM_RADDR_3")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_0", "TILE_WIRE_RAM_RADDR_4")
+gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_1", "TILE_WIRE_RAM_RADDR_5")
+gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_2", "TILE_WIRE_RAM_RADDR_6")
+gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_3", "TILE_WIRE_RAM_RADDR_7")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_2_IN_0", "TILE_WIRE_RAM_RADDR_8")
+gfx_wire_alias("TILE_WIRE_LUTFF_2_IN_1", "TILE_WIRE_RAM_RADDR_9")
+gfx_wire_alias("TILE_WIRE_LUTFF_2_IN_2", "TILE_WIRE_RAM_RADDR_10")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_0", "TILE_WIRE_RAM_WADDR_0")
+gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_1", "TILE_WIRE_RAM_WADDR_1")
+gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_2", "TILE_WIRE_RAM_WADDR_2")
+gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_3", "TILE_WIRE_RAM_WADDR_3")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_0", "TILE_WIRE_RAM_WADDR_4")
+gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_1", "TILE_WIRE_RAM_WADDR_5")
+gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_2", "TILE_WIRE_RAM_WADDR_6")
+gfx_wire_alias("TILE_WIRE_LUTFF_1_IN_3", "TILE_WIRE_RAM_WADDR_7")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_2_IN_0", "TILE_WIRE_RAM_WADDR_8")
+gfx_wire_alias("TILE_WIRE_LUTFF_2_IN_1", "TILE_WIRE_RAM_WADDR_9")
+gfx_wire_alias("TILE_WIRE_LUTFF_2_IN_2", "TILE_WIRE_RAM_WADDR_10")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_0", "TILE_WIRE_RAM_MASK_0")
+gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_1", "TILE_WIRE_RAM_MASK_1")
+gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_2", "TILE_WIRE_RAM_MASK_2")
+gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_3", "TILE_WIRE_RAM_MASK_3")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_0", "TILE_WIRE_RAM_MASK_4")
+gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_1", "TILE_WIRE_RAM_MASK_5")
+gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_2", "TILE_WIRE_RAM_MASK_6")
+gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_3", "TILE_WIRE_RAM_MASK_7")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_0", "TILE_WIRE_RAM_MASK_8")
+gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_1", "TILE_WIRE_RAM_MASK_9")
+gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_2", "TILE_WIRE_RAM_MASK_10")
+gfx_wire_alias("TILE_WIRE_LUTFF_3_IN_3", "TILE_WIRE_RAM_MASK_11")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_0", "TILE_WIRE_RAM_MASK_12")
+gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_1", "TILE_WIRE_RAM_MASK_13")
+gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_2", "TILE_WIRE_RAM_MASK_14")
+gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_3", "TILE_WIRE_RAM_MASK_15")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_0", "TILE_WIRE_RAM_WDATA_0")
+gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_1", "TILE_WIRE_RAM_WDATA_1")
+gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_2", "TILE_WIRE_RAM_WDATA_2")
+gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_3", "TILE_WIRE_RAM_WDATA_3")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_0", "TILE_WIRE_RAM_WDATA_4")
+gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_1", "TILE_WIRE_RAM_WDATA_5")
+gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_2", "TILE_WIRE_RAM_WDATA_6")
+gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_3", "TILE_WIRE_RAM_WDATA_7")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_0", "TILE_WIRE_RAM_WDATA_8")
+gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_1", "TILE_WIRE_RAM_WDATA_9")
+gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_2", "TILE_WIRE_RAM_WDATA_10")
+gfx_wire_alias("TILE_WIRE_LUTFF_5_IN_3", "TILE_WIRE_RAM_WDATA_11")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_0", "TILE_WIRE_RAM_WDATA_12")
+gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_1", "TILE_WIRE_RAM_WDATA_13")
+gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_2", "TILE_WIRE_RAM_WDATA_14")
+gfx_wire_alias("TILE_WIRE_LUTFF_6_IN_3", "TILE_WIRE_RAM_WDATA_15")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_0_OUT", "TILE_WIRE_RAM_RDATA_0")
+gfx_wire_alias("TILE_WIRE_LUTFF_1_OUT", "TILE_WIRE_RAM_RDATA_1")
+gfx_wire_alias("TILE_WIRE_LUTFF_2_OUT", "TILE_WIRE_RAM_RDATA_2")
+gfx_wire_alias("TILE_WIRE_LUTFF_3_OUT", "TILE_WIRE_RAM_RDATA_3")
+gfx_wire_alias("TILE_WIRE_LUTFF_4_OUT", "TILE_WIRE_RAM_RDATA_4")
+gfx_wire_alias("TILE_WIRE_LUTFF_5_OUT", "TILE_WIRE_RAM_RDATA_5")
+gfx_wire_alias("TILE_WIRE_LUTFF_6_OUT", "TILE_WIRE_RAM_RDATA_6")
+gfx_wire_alias("TILE_WIRE_LUTFF_7_OUT", "TILE_WIRE_RAM_RDATA_7")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_0_OUT", "TILE_WIRE_RAM_RDATA_8")
+gfx_wire_alias("TILE_WIRE_LUTFF_1_OUT", "TILE_WIRE_RAM_RDATA_9")
+gfx_wire_alias("TILE_WIRE_LUTFF_2_OUT", "TILE_WIRE_RAM_RDATA_10")
+gfx_wire_alias("TILE_WIRE_LUTFF_3_OUT", "TILE_WIRE_RAM_RDATA_11")
+gfx_wire_alias("TILE_WIRE_LUTFF_4_OUT", "TILE_WIRE_RAM_RDATA_12")
+gfx_wire_alias("TILE_WIRE_LUTFF_5_OUT", "TILE_WIRE_RAM_RDATA_13")
+gfx_wire_alias("TILE_WIRE_LUTFF_6_OUT", "TILE_WIRE_RAM_RDATA_14")
+gfx_wire_alias("TILE_WIRE_LUTFF_7_OUT", "TILE_WIRE_RAM_RDATA_15")
+
+gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_CEN", "TILE_WIRE_RAM_RCLKE")
+gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_CEN", "TILE_WIRE_RAM_WCLKE")
+gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_CLK", "TILE_WIRE_RAM_RCLK")
+gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_CLK", "TILE_WIRE_RAM_WCLK")
+gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_S_R", "TILE_WIRE_RAM_RE")
+gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_S_R", "TILE_WIRE_RAM_WE")
+
+# GFX aliases for IO tiles
+
+gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_0", "TILE_WIRE_IO_0_D_OUT_0")
+gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_1", "TILE_WIRE_IO_0_D_OUT_1")
+gfx_wire_alias("TILE_WIRE_LUTFF_0_IN_3", "TILE_WIRE_IO_0_OUT_ENB")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_0_OUT", "TILE_WIRE_IO_0_D_IN_0")
+gfx_wire_alias("TILE_WIRE_LUTFF_1_OUT", "TILE_WIRE_IO_0_D_IN_1")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_0", "TILE_WIRE_IO_1_D_OUT_0")
+gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_1", "TILE_WIRE_IO_1_D_OUT_1")
+gfx_wire_alias("TILE_WIRE_LUTFF_4_IN_3", "TILE_WIRE_IO_1_OUT_ENB")
+
+gfx_wire_alias("TILE_WIRE_LUTFF_4_OUT", "TILE_WIRE_IO_1_D_IN_0")
+gfx_wire_alias("TILE_WIRE_LUTFF_5_OUT", "TILE_WIRE_IO_1_D_IN_1")
+
+gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_CEN", "TILE_WIRE_IO_GLOBAL_CEN")
+gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_CLK", "TILE_WIRE_IO_GLOBAL_INCLK")
+gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_S_R", "TILE_WIRE_IO_GLOBAL_OUTCLK")
+
+gfx_wire_alias("TILE_WIRE_FUNC_GLOBAL_G0", "TILE_WIRE_IO_GLOBAL_LATCH")
+
+for neigh in "BNL BNR BOT LFT RGT TNL TNR TOP".split():
+ for i in range(8):
+ gfx_wire_alias("TILE_WIRE_NEIGH_OP_%s_%d" % (neigh, i), "TILE_WIRE_LOGIC_OP_%s_%d" % (neigh, i))
+
+# End of GFX aliases
+
def read_timings(filename):
db = dict()
@@ -165,6 +296,18 @@ def maj_wire_name(name):
return name[2] in ("sp12_v_b_0", "sp12_v_b_1")
return False
+def norm_wire_xy(x, y, name):
+ if name.startswith("glb_netwk_"):
+ return None
+ if name.startswith("neigh_op_"):
+ return None
+ if name.startswith("logic_op_"):
+ return None
+ if name.startswith("io_global/latch"):
+ return None
+ return None # FIXME
+ return (x, y)
+
def cmp_wire_names(newname, oldname):
if maj_wire_name(newname):
return True
@@ -508,11 +651,13 @@ with open(args.filename, "r") as f:
wire_names_r[mode[1]] = wname
if mode[1] not in wire_xy:
wire_xy[mode[1]] = list()
- wire_xy[mode[1]].append((int(line[0]), int(line[1])))
+ wire_xy[mode[1]].append(wname)
if mode[1] not in wire_segments:
wire_segments[mode[1]] = dict()
if ("TILE_WIRE_" + wname[2].upper().replace("/", "_")) in gfx_wire_ids:
- wire_segments[mode[1]][(wname[0], wname[1])] = wname[2]
+ if (wname[0], wname[1]) not in wire_segments[mode[1]]:
+ wire_segments[mode[1]][(wname[0], wname[1])] = list()
+ wire_segments[mode[1]][(wname[0], wname[1])].append(wname[2])
continue
if mode[0] in ("buffer", "routing"):
@@ -561,7 +706,9 @@ def add_wire(x, y, name):
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]
+ if (wname[0], wname[1]) not in wire_segments[wire_idx]:
+ wire_segments[wire_idx][(wname[0], wname[1])] = list()
+ wire_segments[wire_idx][(wname[0], wname[1])].append(wname[2])
return wire_idx
def add_switch(x, y, bel=-1):
@@ -580,10 +727,6 @@ def add_pip(src, dst, flags=0):
pip_xy[(src, dst)] = (x, y, 0, len(switches) - 1, flags)
-# Add virtual padin wires
-for i in range(8):
- add_wire(0, 0, "padin_%d" % i)
-
def add_bel_input(bel, wire, port):
if wire not in wire_belports:
wire_belports[wire] = set()
@@ -932,6 +1075,10 @@ bba.post('NEXTPNR_NAMESPACE_END')
bba.push("chipdb_blob_%s" % dev_name)
bba.r("chip_info_%s" % dev_name, "chip_info")
+bba.l("tile_wire_names")
+for name in gfx_wire_names:
+ bba.s(name, name)
+
for bel in range(len(bel_name)):
bba.l("bel_wires_%d" % bel, "BelWirePOD")
for data in sorted(bel_wires[bel]):
@@ -1028,20 +1175,32 @@ 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
- if wire in wire_xy:
- avg_x, avg_y = 0, 0
+ pos_xy = None
+ first = None
- 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])
+ if wire in wire_xy:
+ for x, y, n in wire_xy[wire]:
+ norm_xy = norm_wire_xy(x, y, n)
+ if norm_xy is None:
+ continue
+ if pos_xy is None:
+ pos_xy = norm_xy
+ first = (x, y, n)
+ elif pos_xy != norm_xy:
+ print("Conflicting positions for wire %s: (%d, %d, %s) -> (%d, %d), (%d, %d, %s) -> (%d, %d)" % \
+ ((info["name"],) + first + pos_xy + (x, y, n) + norm_xy), file=sys.stderr)
+ assert 0
+ if (pos_xy is None) and (len(wire_xy[wire]) > 1):
+ # print("Only 'None' positions for wire %s." % info["name"], file=sys.stderr)
+ # assert 0
+ pass
- info["x"] = int(round(avg_x))
- info["y"] = int(round(avg_y))
- else:
+ if pos_xy is None:
info["x"] = wire_names_r[wire][0]
info["y"] = wire_names_r[wire][1]
+ else:
+ info["x"] = pos_xy[0]
+ info["y"] = pos_xy[1]
wireinfo.append(info)
@@ -1108,8 +1267,13 @@ for wire, info in enumerate(wireinfo):
bba.r(info["list_downhill"], "pips_downhill")
bba.u32(info["num_bel_pins"], "num_bel_pins")
bba.r(info["list_bel_pins"], "bel_pins")
- bba.u32(len(wire_segments[wire]), "num_segments")
- if len(wire_segments[wire]):
+
+ num_segments = 0
+ for segs in wire_segments[wire].values():
+ num_segments += len(segs)
+ bba.u32(num_segments, "num_segments")
+
+ if num_segments:
bba.r("wire_segments_%d" % wire, "segments")
else:
bba.u32(0, "segments")
@@ -1125,24 +1289,25 @@ for wire, info in enumerate(wireinfo):
for wire in range(num_wires):
if len(wire_segments[wire]):
bba.l("wire_segments_%d" % wire, "WireSegmentPOD")
- for xy, seg in sorted(wire_segments[wire].items()):
- bba.u8(xy[0], "x")
- bba.u8(xy[1], "y")
- bba.u16(gfx_wire_ids["TILE_WIRE_" + seg.upper().replace("/", "_")], "index")
+ for xy, segs in sorted(wire_segments[wire].items()):
+ for seg in segs:
+ bba.u8(xy[0], "x")
+ bba.u8(xy[1], "y")
+ bba.u16(gfx_wire_ids["TILE_WIRE_" + seg.upper().replace("/", "_")], "index")
bba.l("pip_data_%s" % dev_name, "PipInfoPOD")
for info in pipinfo:
src_seg = -1
src_segname = wire_names_r[info["src"]]
if (info["x"], info["y"]) in wire_segments[info["src"]]:
- src_segname = wire_segments[info["src"]][(info["x"], info["y"])]
+ src_segname = wire_segments[info["src"]][(info["x"], info["y"])][0]
src_seg = gfx_wire_ids["TILE_WIRE_" + src_segname.upper().replace("/", "_")]
src_segname = src_segname.replace("/", ".")
dst_seg = -1
dst_segname = wire_names_r[info["dst"]]
if (info["x"], info["y"]) in wire_segments[info["dst"]]:
- dst_segname = wire_segments[info["dst"]][(info["x"], info["y"])]
+ dst_segname = wire_segments[info["dst"]][(info["x"], info["y"])][0]
dst_seg = gfx_wire_ids["TILE_WIRE_" + dst_segname.upper().replace("/", "_")]
dst_segname = dst_segname.replace("/", ".")
@@ -1276,5 +1441,6 @@ bba.r("bits_info_%s" % dev_name, "bits_info")
bba.r("bel_config_%s" % dev_name if len(extra_cell_config) > 0 else None, "bel_config")
bba.r("package_info_%s" % dev_name, "packages_data")
bba.r("cell_timings_%s" % dev_name, "cell_timing")
+bba.r("tile_wire_names", "tile_wire_names")
bba.pop()
diff --git a/ice40/family.cmake b/ice40/family.cmake
index f558a14d..0e1d36e6 100644
--- a/ice40/family.cmake
+++ b/ice40/family.cmake
@@ -36,7 +36,7 @@ if (MSVC)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_CONSTIDS_INC} -g ${DEV_GFXH} ${OPT_FAST} ${OPT_SLOW} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}
- DEPENDS ${DEV_TXT_DB} ${DB_PY}
+ DEPENDS ${DEV_CONSTIDS_INC} ${DEV_GFXH} ${DEV_TXT_DB} ${DB_PY}
)
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND bbasm ${DEV_CC_BBA_DB} ${DEV_CC_DB}
@@ -69,7 +69,7 @@ else()
add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_CONSTIDS_INC} -g ${DEV_GFXH} ${OPT_FAST} ${OPT_SLOW} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}.new
COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB}
- DEPENDS ${DEV_TXT_DB} ${DB_PY}
+ DEPENDS ${DEV_CONSTIDS_INC} ${DEV_GFXH} ${DEV_TXT_DB} ${DB_PY}
)
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND bbasm --c ${DEV_CC_BBA_DB} ${DEV_CC_DB}.new
diff --git a/ice40/gfx.cc b/ice40/gfx.cc
index 1ab2fb3c..74338b8d 100644
--- a/ice40/gfx.cc
+++ b/ice40/gfx.cc
@@ -21,7 +21,7 @@
NEXTPNR_NAMESPACE_BEGIN
-void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style)
+void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, int w, int h, GfxTileWireId id, GraphicElement::style_t style)
{
GraphicElement el;
el.type = GraphicElement::TYPE_LINE;
@@ -339,6 +339,168 @@ void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id,
g.push_back(el);
}
+ // IO Span-4 Wires connecting to fabric
+
+ if (id >= TILE_WIRE_SPAN4_HORZ_0 && id <= TILE_WIRE_SPAN4_HORZ_47) {
+ int idx = id - TILE_WIRE_SPAN4_HORZ_0;
+ float y1 = y + 1.0 - (0.03 + 0.0025 * (48 - (idx ^ 1)));
+
+ el.x1 = x;
+ el.x2 = x + 1.0;
+ el.y1 = y1;
+ el.y2 = y1;
+ g.push_back(el);
+
+ el.x1 = x + main_swbox_x1 + 0.0025 * ((idx ^ 1) + 35);
+ el.x2 = el.x1;
+ el.y1 = y1;
+ el.y2 = y + main_swbox_y2;
+ g.push_back(el);
+ }
+
+ if (id >= TILE_WIRE_SPAN4_VERT_0 && id <= TILE_WIRE_SPAN4_VERT_47) {
+ int idx = id - TILE_WIRE_SPAN4_VERT_0;
+ float x1 = x + 0.03 + 0.0025 * (48 - (idx ^ 1));
+
+ el.x1 = x1;
+ el.x2 = x1;
+ el.y1 = y;
+ el.y2 = y + 1.0;
+ g.push_back(el);
+
+ el.y1 = y + 1.0 - (0.03 + 0.0025 * (270 - (idx ^ 1)));
+ el.y2 = el.y1;
+ el.x1 = x1;
+ el.x2 = x + main_swbox_x1;
+ g.push_back(el);
+ }
+
+ // IO Span-12 Wires connecting to fabric
+
+ if (id >= TILE_WIRE_SPAN12_HORZ_0 && id <= TILE_WIRE_SPAN12_HORZ_23) {
+ int idx = id - TILE_WIRE_SPAN12_HORZ_0;
+ float y1 = y + 1.0 - (0.03 + 0.0025 * (88 - (idx ^ 1)));
+
+ el.x1 = x;
+ el.x2 = x + 1.0;
+ el.y1 = y1;
+ el.y2 = y1;
+ g.push_back(el);
+
+ el.x1 = x + main_swbox_x1 + 0.0025 * ((idx ^ 1) + 5);
+ el.x2 = el.x1;
+ el.y1 = y1;
+ el.y2 = y + main_swbox_y2;
+ g.push_back(el);
+ }
+
+ if (id >= TILE_WIRE_SPAN12_VERT_0 && id <= TILE_WIRE_SPAN12_VERT_23) {
+ int idx = id - TILE_WIRE_SPAN12_VERT_0;
+ float x1 = x + 0.03 + 0.0025 * (88 - (idx ^ 1));
+
+ el.x1 = x1;
+ el.x2 = x1;
+ el.y1 = y;
+ el.y2 = y + 1.0;
+ g.push_back(el);
+
+ el.y1 = y + 1.0 - (0.03 + 0.0025 * (300 - (idx ^ 1)));
+ el.y2 = el.y1;
+ el.x1 = x1;
+ el.x2 = x + main_swbox_x1;
+ g.push_back(el);
+ }
+
+ // Horizontal IO Span-4 Wires
+
+ if (id >= TILE_WIRE_SPAN4_HORZ_R_0 && id <= TILE_WIRE_SPAN4_HORZ_L_15) {
+ int idx = id - TILE_WIRE_SPAN4_HORZ_R_0;
+
+ float y1 = y + 1.0 - (0.03 + 0.0025 * (60 - idx));
+ float y2 = y + 1.0 - (0.03 + 0.0025 * (60 - idx - 4));
+
+ el.x1 = x;
+ el.x2 = x + 0.9;
+ el.y1 = y1;
+ el.y2 = y1;
+ g.push_back(el);
+
+ if (idx <= 15) {
+ el.x1 = x + 0.9;
+ el.x2 = x + 1.0;
+ el.y1 = y1;
+ el.y2 = y2;
+ g.push_back(el);
+ }
+
+ el.x1 = x + main_swbox_x1 + 0.0025 * (idx + 35);
+ el.x2 = el.x1;
+ el.y1 = y1;
+ el.y2 = y + main_swbox_y2;
+ g.push_back(el);
+ }
+
+ // Vertical IO Span-4 Wires
+
+ if (id >= TILE_WIRE_SPAN4_VERT_B_0 && id <= TILE_WIRE_SPAN4_VERT_T_15) {
+ int idx = id - TILE_WIRE_SPAN4_VERT_B_0;
+
+ float x1 = x + 0.03 + 0.0025 * (60 - idx);
+ float x2 = x + 0.03 + 0.0025 * (60 - idx - 4);
+
+ el.y1 = y + 1.00;
+ el.y2 = y + 0.10;
+ el.x1 = x1;
+ el.x2 = x1;
+ g.push_back(el);
+
+ if (idx <= 15) {
+ el.y1 = y + 0.10;
+ el.y2 = y;
+ el.x1 = x1;
+ el.x2 = x2;
+ g.push_back(el);
+ }
+
+ if (idx <= 15 && (x == 0 || x == w-1) && y == 1) {
+ float y1 = y - (0.03 + 0.0025 * (60 - idx - 4));
+
+ el.x1 = x2;
+ el.y1 = y;
+ el.x2 = x2;
+ el.y2 = y1;
+ g.push_back(el);
+
+ el.x1 = x2;
+ el.y1 = y1;
+ el.x2 = x + (x == 0);
+ el.y2 = y1;
+ g.push_back(el);
+ }
+
+ if (idx >= 4 && (x == 0 || x == w-1) && y == h-2) {
+ float y1 = y + 2.0 - (0.03 + 0.0025 * (60 - idx));
+
+ el.x1 = x1;
+ el.y1 = y + 1.0;
+ el.x2 = x1;
+ el.y2 = y1;
+ g.push_back(el);
+
+ el.x1 = x1;
+ el.y1 = y1;
+ el.x2 = x + (x == 0);
+ el.y2 = y1;
+ g.push_back(el);
+ }
+
+ el.y1 = y + 1.0 - (0.03 + 0.0025 * (270 - idx));
+ el.y2 = el.y1;
+ el.x1 = x1;
+ el.x2 = x + main_swbox_x1;
+ g.push_back(el);
+ }
+
// Global2Local
if (id >= TILE_WIRE_GLB2LOCAL_0 && id <= TILE_WIRE_GLB2LOCAL_3) {
@@ -392,7 +554,7 @@ void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id,
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.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);
}
@@ -403,7 +565,7 @@ void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id,
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.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);
}
@@ -457,6 +619,42 @@ void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id,
}
}
+ // LC Control for IO and BRAM
+
+ if (id >= TILE_WIRE_FUNC_GLOBAL_CEN && id <= TILE_WIRE_FUNC_GLOBAL_S_R) {
+ int idx = id - TILE_WIRE_FUNC_GLOBAL_CEN;
+
+ el.x1 = x + main_swbox_x2 - 0.005 * (idx + 5);
+ el.x2 = el.x1;
+ el.y1 = y + main_swbox_y1;
+ el.y2 = el.y1 - 0.005 * (idx + 2);
+ g.push_back(el);
+
+ el.y1 = el.y2;
+ el.x2 = x + logic_cell_x2 - 0.005 * (2 - idx + 5);
+ g.push_back(el);
+
+ el.y2 = y + logic_cell_y1;
+ el.x1 = el.x2;
+ g.push_back(el);
+ }
+
+ if (id == TILE_WIRE_FABOUT) {
+ el.y1 = y + main_swbox_y1;
+ el.y2 = el.y1 - 0.005 * 4;
+ el.x1 = x + main_swbox_x2 - 0.005 * 9;
+ el.x2 = el.x1;
+ g.push_back(el);
+ }
+
+ if (id == TILE_WIRE_FUNC_GLOBAL_G0) {
+ el.y1 = y + logic_cell_y1;
+ el.y2 = el.y1 - 0.005 * 4;
+ el.x1 = x + logic_cell_x2 - 0.005 * 3;
+ el.x2 = el.x1;
+ g.push_back(el);
+ }
+
// LC Cascade
if (id >= TILE_WIRE_LUTFF_0_LOUT && id <= TILE_WIRE_LUTFF_6_LOUT) {
@@ -571,6 +769,54 @@ static bool getWireXY_main(GfxTileWireId id, float &x, float &y)
return true;
}
+ // IO Span-4 Wires connecting to fabric
+
+ if (id >= TILE_WIRE_SPAN4_HORZ_0 && id <= TILE_WIRE_SPAN4_HORZ_47) {
+ int idx = id - TILE_WIRE_SPAN4_HORZ_0;
+ x = main_swbox_x1 + 0.0025 * ((idx ^ 1) + 35);
+ y = main_swbox_y2;
+ return true;
+ }
+
+ if (id >= TILE_WIRE_SPAN4_VERT_0 && id <= TILE_WIRE_SPAN4_VERT_47) {
+ int idx = id - TILE_WIRE_SPAN4_VERT_0;
+ y = 1.0 - (0.03 + 0.0025 * (270 - (idx ^ 1)));
+ x = main_swbox_x1;
+ return true;
+ }
+
+ // IO Span-12 Wires connecting to fabric
+
+ if (id >= TILE_WIRE_SPAN12_HORZ_0 && id <= TILE_WIRE_SPAN12_HORZ_23) {
+ int idx = id - TILE_WIRE_SPAN12_HORZ_0;
+ x = main_swbox_x1 + 0.0025 * ((idx ^ 1) + 5);
+ y = main_swbox_y2;
+ return true;
+ }
+
+ if (id >= TILE_WIRE_SPAN12_VERT_0 && id <= TILE_WIRE_SPAN12_VERT_23) {
+ int idx = id - TILE_WIRE_SPAN12_VERT_0;
+ y = 1.0 - (0.03 + 0.0025 * (300 - (idx ^ 1)));
+ x = main_swbox_x1;
+ return true;
+ }
+
+ // IO Span-4 Wires
+
+ if (id >= TILE_WIRE_SPAN4_HORZ_R_0 && id <= TILE_WIRE_SPAN4_HORZ_L_15) {
+ int idx = id - TILE_WIRE_SPAN4_HORZ_R_0;
+ y = main_swbox_y2;
+ x = main_swbox_x1 + 0.0025 * (idx + 35);
+ return true;
+ }
+
+ if (id >= TILE_WIRE_SPAN4_VERT_B_0 && id <= TILE_WIRE_SPAN4_VERT_T_15) {
+ int idx = id - TILE_WIRE_SPAN4_VERT_B_0;
+ y = 1.0 - (0.03 + 0.0025 * (270 - idx));
+ x = main_swbox_x1;
+ return true;
+ }
+
// Global2Local
if (id >= TILE_WIRE_GLB2LOCAL_0 && id <= TILE_WIRE_GLB2LOCAL_3) {
@@ -626,6 +872,19 @@ static bool getWireXY_main(GfxTileWireId id, float &x, float &y)
return true;
}
+ if (id >= TILE_WIRE_FUNC_GLOBAL_CEN && id <= TILE_WIRE_FUNC_GLOBAL_S_R) {
+ int idx = id - TILE_WIRE_FUNC_GLOBAL_CEN;
+ x = main_swbox_x2 - 0.005 * (idx + 5);
+ y = main_swbox_y1;
+ return true;
+ }
+
+ if (id == TILE_WIRE_FABOUT) {
+ x = main_swbox_x2 - 0.005 * 9;
+ y = main_swbox_y1;
+ return true;
+ }
+
return false;
}
@@ -662,22 +921,22 @@ void pipGfx(std::vector<GraphicElement> &g, int x, int y, float x1, float y1, fl
el.style = style;
if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) {
- tx = x1 + 0.25 * fabsf(y1 - y2);
+ tx = swx1 + 0.25 * fabsf(y1 - y2);
goto edge_pip;
}
if (fabsf(x1 - swx2) < 0.001 && fabsf(x2 - swx2) < 0.001) {
- tx = x1 - 0.25 * fabsf(y1 - y2);
+ tx = swx2 - 0.25 * fabsf(y1 - y2);
goto edge_pip;
}
if (fabsf(y1 - swy1) < 0.001 && fabsf(y2 - swy1) < 0.001) {
- ty = y1 + 0.25 * fabsf(x1 - x2);
+ ty = swy1 + 0.25 * fabsf(x1 - x2);
goto edge_pip;
}
- if (fabsf(y1 - swy1) < 0.001 && fabsf(y2 - swy1) < 0.001) {
- ty = y1 + 0.25 * fabsf(x1 - x2);
+ if (fabsf(y1 - swy2) < 0.001 && fabsf(y2 - swy2) < 0.001) {
+ ty = swy2 - 0.25 * fabsf(x1 - x2);
goto edge_pip;
}
@@ -727,7 +986,7 @@ void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src,
el.style = style;
el.x1 = x + logic_cell_x1;
el.x2 = x + logic_cell_x2;
- el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * in_idx) + lut_idx * logic_cell_pitch;
+ 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 + lut_idx * logic_cell_pitch;
g.push_back(el);
return;
@@ -744,8 +1003,46 @@ void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src,
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;
+ 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_MUX || (src >= TILE_WIRE_LUTFF_0_COUT && src <= TILE_WIRE_LUTFF_6_COUT)) &&
+ (dst >= TILE_WIRE_LUTFF_0_IN_0 && dst <= TILE_WIRE_LUTFF_7_IN_3 && (dst - TILE_WIRE_LUTFF_0_IN_0) % 4 == 3)) {
+ int lut_idx = (dst - TILE_WIRE_LUTFF_0_IN_0) / 4;
+
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_ARROW;
+ el.style = style;
+ el.x1 = x + (local_swbox_x2 + lut_swbox_x1) / 2;
+ el.x2 = el.x1;
+ el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 + 0.0075 - (0.005 * 3) + lut_idx * logic_cell_pitch;
+ el.y2 = y + (logic_cell_y1 + logic_cell_y2 - logic_cell_pitch) / 2 + lut_idx * logic_cell_pitch;
+ g.push_back(el);
+
+ el.x1 = x + logic_cell_x1 + 0.005 * 3;
+ el.y1 = el.y2;
+ g.push_back(el);
+ return;
+ }
+
+ if ((src >= TILE_WIRE_LUTFF_0_LOUT && src <= TILE_WIRE_LUTFF_6_LOUT) &&
+ (dst >= TILE_WIRE_LUTFF_0_IN_0 && dst <= TILE_WIRE_LUTFF_7_IN_3 && (dst - TILE_WIRE_LUTFF_0_IN_0) % 4 == 2)) {
+ int lut_idx = (dst - TILE_WIRE_LUTFF_0_IN_0) / 4;
+
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_ARROW;
+ el.style = style;
+ el.x1 = x + (local_swbox_x2 + lut_swbox_x1) / 2 + 0.005;
+ el.x2 = el.x1;
+ el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 + 0.0075 - (0.005 * 2) + lut_idx * logic_cell_pitch;
+ el.y2 = y + (logic_cell_y1 + logic_cell_y2 - logic_cell_pitch) / 2 + lut_idx * logic_cell_pitch + 0.003;
+ g.push_back(el);
+
+ el.x1 = x + logic_cell_x1 + 0.005 * 5;
+ el.y1 = el.y2;
g.push_back(el);
return;
}
diff --git a/ice40/gfx.h b/ice40/gfx.h
index 8ee7b0b6..4fb6e147 100644
--- a/ice40/gfx.h
+++ b/ice40/gfx.h
@@ -205,6 +205,13 @@ enum GfxTileWireId
TILE_WIRE_LUTFF_GLOBAL_CLK,
TILE_WIRE_LUTFF_GLOBAL_S_R,
+ TILE_WIRE_FUNC_GLOBAL_CEN,
+ TILE_WIRE_FUNC_GLOBAL_CLK,
+ TILE_WIRE_FUNC_GLOBAL_S_R,
+
+ TILE_WIRE_FUNC_GLOBAL_G0,
+ TILE_WIRE_FABOUT,
+
TILE_WIRE_CARRY_IN,
TILE_WIRE_CARRY_IN_MUX,
@@ -509,12 +516,205 @@ enum GfxTileWireId
TILE_WIRE_SP12_H_L_22,
TILE_WIRE_SP12_H_L_23,
+ TILE_WIRE_SPAN4_VERT_0,
+ TILE_WIRE_SPAN4_VERT_1,
+ TILE_WIRE_SPAN4_VERT_2,
+ TILE_WIRE_SPAN4_VERT_3,
+ TILE_WIRE_SPAN4_VERT_4,
+ TILE_WIRE_SPAN4_VERT_5,
+ TILE_WIRE_SPAN4_VERT_6,
+ TILE_WIRE_SPAN4_VERT_7,
+ TILE_WIRE_SPAN4_VERT_8,
+ TILE_WIRE_SPAN4_VERT_9,
+ TILE_WIRE_SPAN4_VERT_10,
+ TILE_WIRE_SPAN4_VERT_11,
+ TILE_WIRE_SPAN4_VERT_12,
+ TILE_WIRE_SPAN4_VERT_13,
+ TILE_WIRE_SPAN4_VERT_14,
+ TILE_WIRE_SPAN4_VERT_15,
+ TILE_WIRE_SPAN4_VERT_16,
+ TILE_WIRE_SPAN4_VERT_17,
+ TILE_WIRE_SPAN4_VERT_18,
+ TILE_WIRE_SPAN4_VERT_19,
+ TILE_WIRE_SPAN4_VERT_20,
+ TILE_WIRE_SPAN4_VERT_21,
+ TILE_WIRE_SPAN4_VERT_22,
+ TILE_WIRE_SPAN4_VERT_23,
+ TILE_WIRE_SPAN4_VERT_24,
+ TILE_WIRE_SPAN4_VERT_25,
+ TILE_WIRE_SPAN4_VERT_26,
+ TILE_WIRE_SPAN4_VERT_27,
+ TILE_WIRE_SPAN4_VERT_28,
+ TILE_WIRE_SPAN4_VERT_29,
+ TILE_WIRE_SPAN4_VERT_30,
+ TILE_WIRE_SPAN4_VERT_31,
+ TILE_WIRE_SPAN4_VERT_32,
+ TILE_WIRE_SPAN4_VERT_33,
+ TILE_WIRE_SPAN4_VERT_34,
+ TILE_WIRE_SPAN4_VERT_35,
+ TILE_WIRE_SPAN4_VERT_36,
+ TILE_WIRE_SPAN4_VERT_37,
+ TILE_WIRE_SPAN4_VERT_38,
+ TILE_WIRE_SPAN4_VERT_39,
+ TILE_WIRE_SPAN4_VERT_40,
+ TILE_WIRE_SPAN4_VERT_41,
+ TILE_WIRE_SPAN4_VERT_42,
+ TILE_WIRE_SPAN4_VERT_43,
+ TILE_WIRE_SPAN4_VERT_44,
+ TILE_WIRE_SPAN4_VERT_45,
+ TILE_WIRE_SPAN4_VERT_46,
+ TILE_WIRE_SPAN4_VERT_47,
+
+ TILE_WIRE_SPAN4_HORZ_0,
+ TILE_WIRE_SPAN4_HORZ_1,
+ TILE_WIRE_SPAN4_HORZ_2,
+ TILE_WIRE_SPAN4_HORZ_3,
+ TILE_WIRE_SPAN4_HORZ_4,
+ TILE_WIRE_SPAN4_HORZ_5,
+ TILE_WIRE_SPAN4_HORZ_6,
+ TILE_WIRE_SPAN4_HORZ_7,
+ TILE_WIRE_SPAN4_HORZ_8,
+ TILE_WIRE_SPAN4_HORZ_9,
+ TILE_WIRE_SPAN4_HORZ_10,
+ TILE_WIRE_SPAN4_HORZ_11,
+ TILE_WIRE_SPAN4_HORZ_12,
+ TILE_WIRE_SPAN4_HORZ_13,
+ TILE_WIRE_SPAN4_HORZ_14,
+ TILE_WIRE_SPAN4_HORZ_15,
+ TILE_WIRE_SPAN4_HORZ_16,
+ TILE_WIRE_SPAN4_HORZ_17,
+ TILE_WIRE_SPAN4_HORZ_18,
+ TILE_WIRE_SPAN4_HORZ_19,
+ TILE_WIRE_SPAN4_HORZ_20,
+ TILE_WIRE_SPAN4_HORZ_21,
+ TILE_WIRE_SPAN4_HORZ_22,
+ TILE_WIRE_SPAN4_HORZ_23,
+ TILE_WIRE_SPAN4_HORZ_24,
+ TILE_WIRE_SPAN4_HORZ_25,
+ TILE_WIRE_SPAN4_HORZ_26,
+ TILE_WIRE_SPAN4_HORZ_27,
+ TILE_WIRE_SPAN4_HORZ_28,
+ TILE_WIRE_SPAN4_HORZ_29,
+ TILE_WIRE_SPAN4_HORZ_30,
+ TILE_WIRE_SPAN4_HORZ_31,
+ TILE_WIRE_SPAN4_HORZ_32,
+ TILE_WIRE_SPAN4_HORZ_33,
+ TILE_WIRE_SPAN4_HORZ_34,
+ TILE_WIRE_SPAN4_HORZ_35,
+ TILE_WIRE_SPAN4_HORZ_36,
+ TILE_WIRE_SPAN4_HORZ_37,
+ TILE_WIRE_SPAN4_HORZ_38,
+ TILE_WIRE_SPAN4_HORZ_39,
+ TILE_WIRE_SPAN4_HORZ_40,
+ TILE_WIRE_SPAN4_HORZ_41,
+ TILE_WIRE_SPAN4_HORZ_42,
+ TILE_WIRE_SPAN4_HORZ_43,
+ TILE_WIRE_SPAN4_HORZ_44,
+ TILE_WIRE_SPAN4_HORZ_45,
+ TILE_WIRE_SPAN4_HORZ_46,
+ TILE_WIRE_SPAN4_HORZ_47,
+
+ TILE_WIRE_SPAN12_VERT_0,
+ TILE_WIRE_SPAN12_VERT_1,
+ TILE_WIRE_SPAN12_VERT_2,
+ TILE_WIRE_SPAN12_VERT_3,
+ TILE_WIRE_SPAN12_VERT_4,
+ TILE_WIRE_SPAN12_VERT_5,
+ TILE_WIRE_SPAN12_VERT_6,
+ TILE_WIRE_SPAN12_VERT_7,
+ TILE_WIRE_SPAN12_VERT_8,
+ TILE_WIRE_SPAN12_VERT_9,
+ TILE_WIRE_SPAN12_VERT_10,
+ TILE_WIRE_SPAN12_VERT_11,
+ TILE_WIRE_SPAN12_VERT_12,
+ TILE_WIRE_SPAN12_VERT_13,
+ TILE_WIRE_SPAN12_VERT_14,
+ TILE_WIRE_SPAN12_VERT_15,
+ TILE_WIRE_SPAN12_VERT_16,
+ TILE_WIRE_SPAN12_VERT_17,
+ TILE_WIRE_SPAN12_VERT_18,
+ TILE_WIRE_SPAN12_VERT_19,
+ TILE_WIRE_SPAN12_VERT_20,
+ TILE_WIRE_SPAN12_VERT_21,
+ TILE_WIRE_SPAN12_VERT_22,
+ TILE_WIRE_SPAN12_VERT_23,
+
+ TILE_WIRE_SPAN12_HORZ_0,
+ TILE_WIRE_SPAN12_HORZ_1,
+ TILE_WIRE_SPAN12_HORZ_2,
+ TILE_WIRE_SPAN12_HORZ_3,
+ TILE_WIRE_SPAN12_HORZ_4,
+ TILE_WIRE_SPAN12_HORZ_5,
+ TILE_WIRE_SPAN12_HORZ_6,
+ TILE_WIRE_SPAN12_HORZ_7,
+ TILE_WIRE_SPAN12_HORZ_8,
+ TILE_WIRE_SPAN12_HORZ_9,
+ TILE_WIRE_SPAN12_HORZ_10,
+ TILE_WIRE_SPAN12_HORZ_11,
+ TILE_WIRE_SPAN12_HORZ_12,
+ TILE_WIRE_SPAN12_HORZ_13,
+ TILE_WIRE_SPAN12_HORZ_14,
+ TILE_WIRE_SPAN12_HORZ_15,
+ TILE_WIRE_SPAN12_HORZ_16,
+ TILE_WIRE_SPAN12_HORZ_17,
+ TILE_WIRE_SPAN12_HORZ_18,
+ TILE_WIRE_SPAN12_HORZ_19,
+ TILE_WIRE_SPAN12_HORZ_20,
+ TILE_WIRE_SPAN12_HORZ_21,
+ TILE_WIRE_SPAN12_HORZ_22,
+ TILE_WIRE_SPAN12_HORZ_23,
+
+ TILE_WIRE_SPAN4_VERT_B_0,
+ TILE_WIRE_SPAN4_VERT_B_1,
+ TILE_WIRE_SPAN4_VERT_B_2,
+ TILE_WIRE_SPAN4_VERT_B_3,
+ TILE_WIRE_SPAN4_VERT_B_4,
+ TILE_WIRE_SPAN4_VERT_B_5,
+ TILE_WIRE_SPAN4_VERT_B_6,
+ TILE_WIRE_SPAN4_VERT_B_7,
+ TILE_WIRE_SPAN4_VERT_B_8,
+ TILE_WIRE_SPAN4_VERT_B_9,
+ TILE_WIRE_SPAN4_VERT_B_10,
+ TILE_WIRE_SPAN4_VERT_B_11,
+ TILE_WIRE_SPAN4_VERT_B_12,
+ TILE_WIRE_SPAN4_VERT_B_13,
+ TILE_WIRE_SPAN4_VERT_B_14,
+ TILE_WIRE_SPAN4_VERT_B_15,
+
+ TILE_WIRE_SPAN4_VERT_T_12,
+ TILE_WIRE_SPAN4_VERT_T_13,
+ TILE_WIRE_SPAN4_VERT_T_14,
+ TILE_WIRE_SPAN4_VERT_T_15,
+
+ TILE_WIRE_SPAN4_HORZ_R_0,
+ TILE_WIRE_SPAN4_HORZ_R_1,
+ TILE_WIRE_SPAN4_HORZ_R_2,
+ TILE_WIRE_SPAN4_HORZ_R_3,
+ TILE_WIRE_SPAN4_HORZ_R_4,
+ TILE_WIRE_SPAN4_HORZ_R_5,
+ TILE_WIRE_SPAN4_HORZ_R_6,
+ TILE_WIRE_SPAN4_HORZ_R_7,
+ TILE_WIRE_SPAN4_HORZ_R_8,
+ TILE_WIRE_SPAN4_HORZ_R_9,
+ TILE_WIRE_SPAN4_HORZ_R_10,
+ TILE_WIRE_SPAN4_HORZ_R_11,
+ TILE_WIRE_SPAN4_HORZ_R_12,
+ TILE_WIRE_SPAN4_HORZ_R_13,
+ TILE_WIRE_SPAN4_HORZ_R_14,
+ TILE_WIRE_SPAN4_HORZ_R_15,
+
+ TILE_WIRE_SPAN4_HORZ_L_12,
+ TILE_WIRE_SPAN4_HORZ_L_13,
+ TILE_WIRE_SPAN4_HORZ_L_14,
+ TILE_WIRE_SPAN4_HORZ_L_15,
+
TILE_WIRE_PLLIN,
TILE_WIRE_PLLOUT_A,
TILE_WIRE_PLLOUT_B
};
-void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style);
+void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, int w, int h, GfxTileWireId id,
+ GraphicElement::style_t style);
void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst,
GraphicElement::style_t style);
diff --git a/ice40/pack.cc b/ice40/pack.cc
index 05338713..7c853e0e 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -733,7 +733,7 @@ static void pack_special(Context *ctx)
if (pi.name == ctx->id("PACKAGEPIN")) {
if (!is_pad) {
- log_error(" PLL '%s' has a PACKAGEPIN but is not a PAD PLL", ci->name.c_str(ctx));
+ log_error("PLL '%s' has a PACKAGEPIN but is not a PAD PLL", ci->name.c_str(ctx));
} else {
// We drop this port and instead place the PLL adequately below.
pad_packagepin_net = port.second.net;
@@ -743,9 +743,22 @@ static void pack_special(Context *ctx)
}
if (pi.name == ctx->id("REFERENCECLK")) {
if (!is_core)
- log_error(" PLL '%s' has a REFERENCECLK but is not a CORE PLL", ci->name.c_str(ctx));
+ log_error("PLL '%s' has a REFERENCECLK but is not a CORE PLL", ci->name.c_str(ctx));
}
+ if (packed->ports.count(ctx->id(newname)) == 0) {
+ if (ci->ports[pi.name].net == nullptr) {
+ log_warning("PLL '%s' has unknown unconnected port '%s' - ignoring\n", ci->name.c_str(ctx), pi.name.c_str(ctx));
+ continue;
+ } else {
+ if (ctx->force) {
+ log_error("PLL '%s' has unknown connected port '%s'\n", ci->name.c_str(ctx), pi.name.c_str(ctx));
+ } else {
+ log_warning("PLL '%s' has unknown connected port '%s' - ignoring\n", ci->name.c_str(ctx), pi.name.c_str(ctx));
+ continue;
+ }
+ }
+ }
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
@@ -766,7 +779,7 @@ static void pack_special(Context *ctx)
auto pll_packagepin_driver = pad_packagepin_net->driver;
NPNR_ASSERT(pll_packagepin_driver.cell != nullptr);
if (pll_packagepin_driver.cell->type != ctx->id("SB_IO")) {
- log_error(" PLL '%s' has a PACKAGEPIN driven by "
+ log_error("PLL '%s' has a PACKAGEPIN driven by "
"an %s, should be directly connected to an input SB_IO\n",
ci->name.c_str(ctx), pll_packagepin_driver.cell->type.c_str(ctx));
}
@@ -774,17 +787,17 @@ static void pack_special(Context *ctx)
auto packagepin_cell = pll_packagepin_driver.cell;
auto packagepin_bel_name = packagepin_cell->attrs.find(ctx->id("BEL"));
if (packagepin_bel_name == packagepin_cell->attrs.end()) {
- log_error(" PLL '%s' PACKAGEPIN SB_IO '%s' is unconstrained\n", ci->name.c_str(ctx),
+ log_error("PLL '%s' PACKAGEPIN SB_IO '%s' is unconstrained\n", ci->name.c_str(ctx),
packagepin_cell->name.c_str(ctx));
}
auto packagepin_bel = ctx->getBelByName(ctx->id(packagepin_bel_name->second));
if (pll_sb_io_belpin.bel != packagepin_bel) {
- log_error(" PLL '%s' PACKAGEPIN is connected to pin %s, can only be pin %s\n",
+ log_error("PLL '%s' PACKAGEPIN is connected to pin %s, can only be pin %s\n",
ci->name.c_str(ctx), ctx->getBelPackagePin(packagepin_bel).c_str(),
ctx->getBelPackagePin(pll_sb_io_belpin.bel).c_str());
}
if (pad_packagepin_net->users.size() != 1) {
- log_error(" PLL '%s' clock input '%s' can only drive PLL\n", ci->name.c_str(ctx),
+ log_error("PLL '%s' clock input '%s' can only drive PLL\n", ci->name.c_str(ctx),
pad_packagepin_net->name.c_str(ctx));
}
// Set an attribute about this PLL's PAD SB_IO.
@@ -793,13 +806,13 @@ static void pack_special(Context *ctx)
packagepin_cell->ports.erase(pll_packagepin_driver.port);
}
- log_info(" constrained '%s' to %s\n", packed->name.c_str(ctx), ctx->getBelName(bel).c_str(ctx));
+ log_info(" constrained PLL '%s' to %s\n", packed->name.c_str(ctx), ctx->getBelName(bel).c_str(ctx));
packed->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
pll_bel = bel;
constrained = true;
}
if (!constrained) {
- log_error(" could not constrain '%s' to any PLL Bel\n", packed->name.c_str(ctx));
+ log_error("Could not constrain PLL '%s' to any PLL Bel (too many PLLs?)\n", packed->name.c_str(ctx));
}
}
@@ -818,6 +831,8 @@ static void pack_special(Context *ctx)
// If we have a net connected to LOCK, make sure it only drives LUTs.
auto port = packed->ports[ctx->id("LOCK")];
if (port.net != nullptr) {
+ log_info(" PLL '%s' has LOCK output, need to pass all outputs via LUT\n",
+ ci->name.c_str(ctx));
bool found_lut = false;
bool all_luts = true;
unsigned int lut_count = 0;
@@ -835,12 +850,12 @@ static void pack_special(Context *ctx)
// Every user is a LUT, carry on now.
} else if (found_lut && !all_luts && lut_count < 8) {
// Strategy: create a pass-through LUT, move all non-LUT users behind it.
- log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx));
+ log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx));
auto pt = spliceLUT(ctx, packed.get(), port.name, true);
new_cells.push_back(std::move(pt));
} else {
// Strategy: create a pass-through LUT, move every user behind it.
- log_info(" LUT strategy for %s: move all users to new LUT\n", port.name.c_str(ctx));
+ log_info(" LUT strategy for %s: move all users to new LUT\n", port.name.c_str(ctx));
auto pt = spliceLUT(ctx, packed.get(), port.name, false);
new_cells.push_back(std::move(pt));
}
@@ -864,7 +879,7 @@ static void pack_special(Context *ctx)
auto target_bel = ctx->getBelByLocation(Loc(x, y, z++));
auto target_bel_name = ctx->getBelName(target_bel).str(ctx);
user.cell->attrs[ctx->id("BEL")] = target_bel_name;
- log_info(" constrained '%s' to %s\n", user.cell->name.c_str(ctx), target_bel_name.c_str());
+ log_info(" constrained '%s' to %s\n", user.cell->name.c_str(ctx), target_bel_name.c_str());
}
}
diff --git a/ice40/pcf.cc b/ice40/pcf.cc
index d9fc4e68..af5b3e17 100644
--- a/ice40/pcf.cc
+++ b/ice40/pcf.cc
@@ -66,7 +66,7 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in)
log_error("unsupported pcf command '%s'\n", cmd.c_str());
}
}
- ctx->settings.emplace(ctx->id("project/input/pcf"), filename);
+ ctx->settings.emplace(ctx->id("input/pcf"), filename);
return true;
} catch (log_execution_error_exception) {
return false;
diff --git a/ice40/project.cc b/ice40/project.cc
index 8ca10e36..47c0903d 100644
--- a/ice40/project.cc
+++ b/ice40/project.cc
@@ -28,8 +28,8 @@ NEXTPNR_NAMESPACE_BEGIN
void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path)
{
root.put("project.arch.package", ctx->archArgs().package);
- if (ctx->settings.find(ctx->id("project/input/pcf")) != ctx->settings.end()) {
- std::string fn = ctx->settings[ctx->id("project/input/pcf")];
+ if (ctx->settings.find(ctx->id("input/pcf")) != ctx->settings.end()) {
+ std::string fn = ctx->settings[ctx->id("input/pcf")];
root.put("project.input.pcf", make_relative(fn, path).string());
}
}