Merge pull request #345 from YosysHQ/dave/sdf

Improve handling of top level IO and add SDF support
diff --git a/.gitignore b/.gitignore
index 9b3ffd0..9796d24 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,9 @@
 .*.swp
 a.out
 *.json
+*.dot
+*.il
+/generic/examples/blinky.png
 build/
 *.asc
 *.bin
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7bdff61..9af4bb6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -97,7 +97,7 @@
 find_package(Sanitizers)
 
 # List of Boost libraries to include
-set(boost_libs filesystem thread program_options iostreams)
+set(boost_libs filesystem thread program_options iostreams system)
 
 if (BUILD_GUI AND NOT BUILD_PYTHON)
     message(FATAL_ERROR "GUI requires Python to build")
diff --git a/ecp5/arch.cc b/ecp5/arch.cc
index 8ce0653..d931c5b 100644
--- a/ecp5/arch.cc
+++ b/ecp5/arch.cc
@@ -117,6 +117,9 @@
         log_error("Unsupported ECP5 chip type.\n");
     }
 #endif
+    if (chip_info->const_id_count != DB_CONST_ID_COUNT)
+        log_error("Chip database 'bba' and nextpnr code are out of sync; please rebuild (or contact distribution "
+                  "maintainer)!\n");
     package_info = nullptr;
     for (int i = 0; i < chip_info->num_packages; i++) {
         if (args.package == chip_info->package_info[i].name.get()) {
@@ -477,7 +480,13 @@
         }
     };
 
-    auto src_loc = est_location(src), dst_loc = est_location(dst);
+    auto src_loc = est_location(src);
+    std::pair<int, int> dst_loc;
+    if (wire_loc_overrides.count(dst)) {
+        dst_loc = wire_loc_overrides.at(dst);
+    } else {
+        dst_loc = est_location(dst);
+    }
 
     int dx = abs(src_loc.first - dst_loc.first), dy = abs(src_loc.second - dst_loc.second);
 
@@ -562,6 +571,7 @@
 
 bool Arch::route()
 {
+    setupWireLocations();
     route_ecp5_globals(getCtx());
     assignArchInfo();
     assign_budget(getCtx(), true);
diff --git a/ecp5/arch.h b/ecp5/arch.h
index a479abb..3df2d84 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -194,6 +194,7 @@
     int32_t num_tiles;
     int32_t num_location_types;
     int32_t num_packages, num_pios;
+    int32_t const_id_count;
     RelPtr<LocationTypePOD> locations;
     RelPtr<int32_t> location_type;
     RelPtr<GlobalInfoPOD> location_glbinfo;
@@ -1048,6 +1049,11 @@
     // Special case for delay estimates due to its physical location
     // being far from the logical location of its primitive
     WireId gsrclk_wire;
+    // Improves directivity of routing to DSP inputs, avoids issues
+    // with different routes to the same physical reset wire causing
+    // conflicts and slow routing
+    std::unordered_map<WireId, std::pair<int, int>> wire_loc_overrides;
+    void setupWireLocations();
 
     mutable std::unordered_map<DelayKey, std::pair<bool, DelayInfo>> celldelay_cache;
 
diff --git a/ecp5/arch_place.cc b/ecp5/arch_place.cc
index d5c345a..6057605 100644
--- a/ecp5/arch_place.cc
+++ b/ecp5/arch_place.cc
@@ -196,4 +196,28 @@
     }
 }
 
+void Arch::setupWireLocations()
+{
+    wire_loc_overrides.clear();
+    for (auto cell : sorted(cells)) {
+        CellInfo *ci = cell.second;
+        if (ci->bel == BelId())
+            continue;
+        if (ci->type == id_MULT18X18D || ci->type == id_DCUA) {
+            for (auto &port : ci->ports) {
+                if (port.second.type != PORT_IN || port.second.net == nullptr)
+                    continue;
+                WireId pw = getBelPinWire(ci->bel, port.first);
+                if (pw == WireId())
+                    continue;
+                for (auto uh : getPipsUphill(pw)) {
+                    WireId pip_src = getPipSrcWire(uh);
+                    wire_loc_overrides[pw] = std::make_pair(pip_src.location.x, pip_src.location.y);
+                    break;
+                }
+            }
+        }
+    }
+}
+
 NEXTPNR_NAMESPACE_END
diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h
index da12eea..3cd9211 100644
--- a/ecp5/archdefs.h
+++ b/ecp5/archdefs.h
@@ -58,6 +58,8 @@
 #define X(t) , ID_##t
 #include "constids.inc"
 #undef X
+    ,
+    DB_CONST_ID_COUNT
 };
 
 #define X(t) static constexpr auto id_##t = IdString(ID_##t);
diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc
index acab95d..9732cd5 100644
--- a/ecp5/bitstream.cc
+++ b/ecp5/bitstream.cc
@@ -865,6 +865,16 @@
             if (ci->attrs.count(ctx->id("DIFFRESISTOR")))
                 cc.tiles[pio_tile].add_enum(pio + ".DIFFRESISTOR",
                                             str_or_default(ci->attrs, ctx->id("DIFFRESISTOR"), "OFF"));
+            if (ci->attrs.count(ctx->id("DRIVE"))) {
+                static bool drive_3v3_warning_done = false;
+                if (iotype == "LVCMOS33") {
+                    cc.tiles[pio_tile].add_enum(pio + ".DRIVE", str_or_default(ci->attrs, ctx->id("DRIVE"), "8"));
+                } else {
+                    if (!drive_3v3_warning_done)
+                        log_warning("Trellis limitation: DRIVE can only be set on 3V3 IO pins.\n");
+                    drive_3v3_warning_done = true;
+                }
+            }
             if (ci->attrs.count(ctx->id("TERMINATION"))) {
                 auto vccio = get_vccio(ioType_from_str(iotype));
                 switch (vccio) {
diff --git a/ecp5/constids.inc b/ecp5/constids.inc
index 5e8fc7d..4b5e3a3 100644
--- a/ecp5/constids.inc
+++ b/ecp5/constids.inc
@@ -1294,3 +1294,11 @@
 X(ECLKBRIDGECS)
 X(SEL)
 X(ECSOUT)
+
+X(IOLOGIC_MODE_IDDRX1F)
+X(IOLOGIC_MODE_IDDRX2F)
+X(IOLOGIC_MODE_IREG)
+X(IOLOGIC_MODE_ODDRX1F)
+X(IOLOGIC_MODE_ODDRX2F)
+X(IOLOGIC_MODE_OREG)
+X(IOLOGIC_MODE_TSREG)
diff --git a/ecp5/family.cmake b/ecp5/family.cmake
index 5512e7d..2d8dcfc 100644
--- a/ecp5/family.cmake
+++ b/ecp5/family.cmake
@@ -48,7 +48,7 @@
             else()
                 add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
                     COMMAND ${ENV_CMD} ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_CONSTIDS_INC} ${dev} > ${DEV_CC_BBA_DB}
-                    DEPENDS ${DB_PY} ${PREV_DEV_CC_BBA_DB}
+                    DEPENDS ${DB_PY} ${DEV_CONSTIDS_INC} ${PREV_DEV_CC_BBA_DB}
                     )
                 add_custom_command(OUTPUT ${DEV_CC_DB}
                     COMMAND bbasm ${BBASM_ENDIAN_FLAG} ${DEV_CC_BBA_DB} ${DEV_CC_DB}
@@ -80,7 +80,7 @@
                 add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
                     COMMAND ${ENV_CMD} ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_CONSTIDS_INC} ${dev} > ${DEV_CC_BBA_DB}.new
                     COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB}
-                    DEPENDS ${DB_PY} ${PREV_DEV_CC_BBA_DB}
+                    DEPENDS ${DB_PY} ${DEV_CONSTIDS_INC} ${PREV_DEV_CC_BBA_DB}
                     )
                 add_custom_command(OUTPUT ${DEV_CC_DB}
                     COMMAND bbasm --c ${BBASM_ENDIAN_FLAG} ${DEV_CC_BBA_DB} ${DEV_CC_DB}.new
diff --git a/ecp5/pack.cc b/ecp5/pack.cc
index 00ed891..c498d20 100644
--- a/ecp5/pack.cc
+++ b/ecp5/pack.cc
@@ -2345,6 +2345,7 @@
         for (auto cell : sorted(ctx->cells)) {
             CellInfo *ci = cell.second;
             if (ci->type == id_ECLKBRIDGECS) {
+                Loc loc;
                 NetInfo *i0 = get_net_or_empty(ci, id_CLK0), *i1 = get_net_or_empty(ci, id_CLK1),
                         *o = get_net_or_empty(ci, id_ECSOUT);
                 for (NetInfo *input : {i0, i1}) {
@@ -2358,24 +2359,53 @@
                         for (auto bel : ctx->getBels()) {
                             if (ctx->getBelType(bel) != id_ECLKBRIDGECS)
                                 continue;
-                            Loc loc = ctx->getBelLocation(bel);
+                            loc = ctx->getBelLocation(bel);
                             if (loc.x == user_loc.x) {
                                 ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
-                                if (o != nullptr)
-                                    for (auto user2 : o->users) {
-                                        // Set side hint to ensure edge clock choice is routeable
-                                        if (user2.cell->type == id_ECLKSYNCB && user2.port == id_ECLKI) {
-                                            NetInfo *synco = get_net_or_empty(user2.cell, id_ECLKO);
-                                            if (synco != nullptr)
-                                                bridge_side_hint[synco] = (loc.x > 1) ? 0 : 1;
-                                        }
-                                    }
                                 goto eclkbridge_done;
                             }
                         }
                     }
+                    if (input->driver.cell != nullptr) {
+                        CellInfo *drv = input->driver.cell;
+                        if (!drv->attrs.count(ctx->id("BEL")))
+                            continue;
+                        Loc drv_loc = ctx->getBelLocation(
+                                ctx->getBelByName(ctx->id(drv->attrs.at(ctx->id("BEL")).as_string())));
+                        BelId closest;
+                        int closest_x = -1; // aim for same side of chip
+                        for (auto bel : ctx->getBels()) {
+                            if (ctx->getBelType(bel) != id_ECLKBRIDGECS)
+                                continue;
+                            loc = ctx->getBelLocation(bel);
+                            if (closest_x == -1 || std::abs(loc.x - drv_loc.x) < std::abs(closest_x - drv_loc.x)) {
+                                closest_x = loc.x;
+                                closest = bel;
+                            }
+                        }
+                        NPNR_ASSERT(closest != BelId());
+                        loc = ctx->getBelLocation(closest);
+                        ci->attrs[ctx->id("BEL")] = ctx->getBelName(closest).str(ctx);
+                        goto eclkbridge_done;
+                    }
+                }
+                // If all else fails, place randomly
+                for (auto bel : ctx->getBels()) {
+                    if (ctx->getBelType(bel) != id_ECLKBRIDGECS)
+                        continue;
+                    loc = ctx->getBelLocation(bel);
+                    ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
                 }
             eclkbridge_done:
+                if (o != nullptr)
+                    for (auto user2 : o->users) {
+                        // Set side hint to ensure edge clock choice is routeable
+                        if (user2.cell->type == id_ECLKSYNCB && user2.port == id_ECLKI) {
+                            NetInfo *synco = get_net_or_empty(user2.cell, id_ECLKO);
+                            if (synco != nullptr)
+                                bridge_side_hint[synco] = (loc.x > 1) ? 0 : 1;
+                        }
+                    }
                 continue;
             }
         }
@@ -2575,7 +2605,7 @@
             std::unordered_set<IdString> changed_cells;
             for (auto net : changed_nets)
                 for (auto &user : ctx->nets.at(net)->users)
-                    if (user.port == id_CLKI || user.port == id_ECLKI)
+                    if (user.port == id_CLKI || user.port == id_ECLKI || user.port == id_CLK0 || user.port == id_CLK1)
                         changed_cells.insert(user.cell->name);
             changed_nets.clear();
             for (auto cell : sorted(changed_cells)) {
@@ -2592,6 +2622,9 @@
                     copy_constraint(ci, id_CLKI, id_CDIVX, ratio);
                 } else if (ci->type == id_ECLKSYNCB || ci->type == id_TRELLIS_ECLKBUF) {
                     copy_constraint(ci, id_ECLKI, id_ECLKO, 1);
+                } else if (ci->type == id_ECLKBRIDGECS) {
+                    copy_constraint(ci, id_CLK0, id_ECSOUT, 1);
+                    copy_constraint(ci, id_CLK1, id_ECSOUT, 1);
                 } else if (ci->type == id_DCCA) {
                     copy_constraint(ci, id_CLKI, id_CLKO, 1);
                 } else if (ci->type == id_EHXPLLL) {
@@ -2651,12 +2684,12 @@
         prepack_checks();
         pack_io();
         pack_dqsbuf();
+        preplace_plls();
         pack_iologic();
         pack_ebr();
         pack_dsps();
         pack_dcus();
         pack_misc();
-        preplace_plls();
         pack_constants();
         pack_dram();
         pack_carries();
diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py
index 610bd33..ee960fd 100755
--- a/ecp5/trellis_import.py
+++ b/ecp5/trellis_import.py
@@ -186,6 +186,8 @@
                     max_delay = min(entry["rising"][2], entry["falling"][2])
                     delays.append((constids[from_pin], constids[to_pin], min_delay, max_delay))
                 elif entry["type"] == "SetupHold":
+                    if type(entry["pin"]) is list:
+                        continue
                     pin = constids[entry["pin"]]
                     clock = constids[entry["clock"][1]]
                     min_setup = entry["setup"][0]
@@ -441,6 +443,7 @@
     bba.u32(len(location_types), "num_location_types")
     bba.u32(len(packages), "num_packages")
     bba.u32(len(pindata), "num_pios")
+    bba.u32(const_id_count, "const_id_count")
 
     bba.r("locations", "locations")
     bba.r("location_types", "location_type")
@@ -457,11 +460,12 @@
 dev_names = {"25k": "LFE5UM5G-25F", "45k": "LFE5UM5G-45F", "85k": "LFE5UM5G-85F"}
 
 def main():
-    global max_row, max_col
+    global max_row, max_col, const_id_count
     pytrellis.load_database(database.get_db_root())
     args = parser.parse_args()
 
     # Read port pin file
+    const_id_count = 1 # count ID_NONE
     with open(args.constids) as f:
         for line in f:
             line = line.replace("(", " ")
@@ -473,7 +477,7 @@
             assert line[0] == "X"
             idx = len(constids) + 1
             constids[line[1]] = idx
-    
+            const_id_count += 1
 
     constids["SLICE"] = constids["TRELLIS_SLICE"]
     constids["PIO"] = constids["TRELLIS_IO"]
diff --git a/generic/cells.cc b/generic/cells.cc
index 53886e3..2b555f6 100644
--- a/generic/cells.cc
+++ b/generic/cells.cc
@@ -42,7 +42,7 @@
     }
     new_cell->type = type;
     if (type == ctx->id("GENERIC_SLICE")) {
-        new_cell->params[ctx->id("K")] = std::to_string(ctx->args.K);
+        new_cell->params[ctx->id("K")] = ctx->args.K;
         new_cell->params[ctx->id("INIT")] = 0;
         new_cell->params[ctx->id("FF_USED")] = 0;
 
@@ -51,6 +51,7 @@
 
         add_port(ctx, new_cell.get(), "CLK", PORT_IN);
 
+        add_port(ctx, new_cell.get(), "F", PORT_OUT);
         add_port(ctx, new_cell.get(), "Q", PORT_OUT);
     } else if (type == ctx->id("GENERIC_IOB")) {
         new_cell->params[ctx->id("INPUT_USED")] = 0;
@@ -80,8 +81,8 @@
     }
 
     if (no_dff) {
-        replace_port(lut, ctx->id("Q"), lc, ctx->id("Q"));
         lc->params[ctx->id("FF_USED")] = 0;
+        replace_port(lut, ctx->id("Q"), lc, ctx->id("F"));
     }
 }
 
@@ -91,7 +92,14 @@
     replace_port(dff, ctx->id("CLK"), lc, ctx->id("CLK"));
 
     if (pass_thru_lut) {
-        lc->params[ctx->id("INIT")] = 2;
+        // Fill LUT with alternating 10
+        const int init_size = 1 << lc->params[ctx->id("K")].as_int64();
+        std::string init;
+        init.reserve(init_size);
+        for(int i = 0; i < init_size; i+=2)
+            init.append("10");
+        lc->params[ctx->id("INIT")] = Property::from_string(init);
+
         replace_port(dff, ctx->id("D"), lc, ctx->id("I[0]"));
     }
 
diff --git a/generic/examples/bitstream.py b/generic/examples/bitstream.py
index 1ab94f0..7f0b5c0 100644
--- a/generic/examples/bitstream.py
+++ b/generic/examples/bitstream.py
@@ -14,4 +14,4 @@
 }
 
 with open("blinky.fasm", "w") as f:
-	write_fasm(ctx, param_map, f)
\ No newline at end of file
+	write_fasm(ctx, param_map, f)
diff --git a/generic/examples/simple.py b/generic/examples/simple.py
index 9339b68..9379b50 100644
--- a/generic/examples/simple.py
+++ b/generic/examples/simple.py
@@ -9,6 +9,7 @@
 		for z in range(N):
 			ctx.addWire(name="X%dY%dZ%d_CLK" % (x, y, z), type="BEL_CLK", x=x, y=y)
 			ctx.addWire(name="X%dY%dZ%d_Q" % (x, y, z), type="BEL_Q", x=x, y=y)
+			ctx.addWire(name="X%dY%dZ%d_F" % (x, y, z), type="BEL_F", x=x, y=y)
 			for i in range(K):
 				ctx.addWire(name="X%dY%dZ%d_I%d" % (x, y, z, i), type="BEL_I", x=x, y=y)
 		# Local wires
@@ -29,6 +30,7 @@
 				ctx.addBelInput(bel="X%dY%d_SLICE%d" % (x, y, z), name="CLK", wire="X%dY%dZ%d_CLK" % (x, y, z))
 				for k in range(K):
 					ctx.addBelInput(bel="X%dY%d_SLICE%d" % (x, y, z), name="I[%d]" % k, wire="X%dY%dZ%d_I%d" % (x, y, z, k))
+				ctx.addBelOutput(bel="X%dY%d_SLICE%d" % (x, y, z), name="F", wire="X%dY%dZ%d_F" % (x, y, z))
 				ctx.addBelOutput(bel="X%dY%d_SLICE%d" % (x, y, z), name="Q", wire="X%dY%dZ%d_Q" % (x, y, z))
 
 for x in range(X):
@@ -48,6 +50,9 @@
 		# Pips from bel outputs to locals
 		def create_output_pips(dst, offset, skip):
 			for i in range(offset % skip, N, skip):
+				src = "X%dY%dZ%d_F" % (x, y, i)
+				ctx.addPip(name="X%dY%d.%s.%s" % (x, y, src, dst), type="BEL_OUTPUT",
+					srcWire=src, dstWire=dst, delay=ctx.getDelayFromNS(0.05), loc=Loc(x, y, 0))
 				src = "X%dY%dZ%d_Q" % (x, y, i)
 				ctx.addPip(name="X%dY%d.%s.%s" % (x, y, src, dst), type="BEL_OUTPUT",
 					srcWire=src, dstWire=dst, delay=ctx.getDelayFromNS(0.05), loc=Loc(x, y, 0))
@@ -69,4 +74,4 @@
 			create_neighbour_pips(dst, x, y+1, (l + 4) % Sl, Sl)
 			create_neighbour_pips(dst, x+1, y-1, (l + 5) % Sl, Sl)
 			create_neighbour_pips(dst, x+1, y, (l + 6) % Sl, Sl)
-			create_neighbour_pips(dst, x+1, y+1, (l + 7) % Sl, Sl)
\ No newline at end of file
+			create_neighbour_pips(dst, x+1, y+1, (l + 7) % Sl, Sl)
diff --git a/generic/examples/simple.sh b/generic/examples/simple.sh
index 8ae903f..76bc616 100755
--- a/generic/examples/simple.sh
+++ b/generic/examples/simple.sh
@@ -1,4 +1,5 @@
 #!/usr/bin/env bash
 set -ex
 yosys -p "tcl ../synth/synth_generic.tcl 4 blinky.json" blinky.v
-${NEXTPNR:-../../nextpnr-generic} --pre-pack simple.py --pre-place simple_timing.py --json blinky.json --post-route bitstream.py
+${NEXTPNR:-../../nextpnr-generic} --pre-pack simple.py --pre-place simple_timing.py --json blinky.json --post-route bitstream.py --write pnrblinky.json
+yosys -p "read_verilog -lib ../synth/prims.v; read_json pnrblinky.json; dump -o blinky.il; show -format png -prefix blinky"
diff --git a/generic/examples/simple_timing.py b/generic/examples/simple_timing.py
index 2ccb197..1067b55 100644
--- a/generic/examples/simple_timing.py
+++ b/generic/examples/simple_timing.py
@@ -1,15 +1,13 @@
 for cname, cell in ctx.cells:
-	if cell.type != "GENERIC_SLICE":
-		continue
-	if cname in ("$PACKER_GND", "$PACKER_VCC"):
-		continue
-	K = int(cell.params["K"])
-	if int(cell.params["FF_USED"], 2) == 1:
-		ctx.addCellTimingClock(cell=cname, port="CLK")
-		for i in range(K):
-			ctx.addCellTimingSetupHold(cell=cname, port="I[%d]" % i, clock="CLK",
-				setup=ctx.getDelayFromNS(0.2), hold=ctx.getDelayFromNS(0))
-		ctx.addCellTimingClockToOut(cell=cname, port="Q", clock="CLK", clktoq=ctx.getDelayFromNS(0.2))
-	else:
-		for i in range(K):
-			ctx.addCellTimingDelay(cell=cname, fromPort="I[%d]" % i, toPort="Q", delay=ctx.getDelayFromNS(0.2))
\ No newline at end of file
+    if cell.type != "GENERIC_SLICE":
+        continue
+    if cname in ("$PACKER_GND", "$PACKER_VCC"):
+        continue
+    K = int(cell.params["K"])
+    ctx.addCellTimingClock(cell=cname, port="CLK")
+    for i in range(K):
+        ctx.addCellTimingSetupHold(cell=cname, port="I[%d]" % i, clock="CLK",
+            setup=ctx.getDelayFromNS(0.2), hold=ctx.getDelayFromNS(0))
+    ctx.addCellTimingClockToOut(cell=cname, port="Q", clock="CLK", clktoq=ctx.getDelayFromNS(0.2))
+    for i in range(K):
+        ctx.addCellTimingDelay(cell=cname, fromPort="I[%d]" % i, toPort="F", delay=ctx.getDelayFromNS(0.2))
diff --git a/generic/pack.cc b/generic/pack.cc
index 3dc12bc..69f248d 100644
--- a/generic/pack.cc
+++ b/generic/pack.cc
@@ -154,16 +154,16 @@
     std::unique_ptr<NetInfo> gnd_net = std::unique_ptr<NetInfo>(new NetInfo);
     gnd_net->name = ctx->id("$PACKER_GND_NET");
     gnd_net->driver.cell = gnd_cell.get();
-    gnd_net->driver.port = ctx->id("Q");
-    gnd_cell->ports.at(ctx->id("Q")).net = gnd_net.get();
+    gnd_net->driver.port = ctx->id("F");
+    gnd_cell->ports.at(ctx->id("F")).net = gnd_net.get();
 
     std::unique_ptr<CellInfo> vcc_cell = create_generic_cell(ctx, ctx->id("GENERIC_SLICE"), "$PACKER_VCC");
     vcc_cell->params[ctx->id("INIT")] = 1;
     std::unique_ptr<NetInfo> vcc_net = std::unique_ptr<NetInfo>(new NetInfo);
     vcc_net->name = ctx->id("$PACKER_VCC_NET");
     vcc_net->driver.cell = vcc_cell.get();
-    vcc_net->driver.port = ctx->id("Q");
-    vcc_cell->ports.at(ctx->id("Q")).net = vcc_net.get();
+    vcc_net->driver.port = ctx->id("F");
+    vcc_cell->ports.at(ctx->id("F")).net = vcc_net.get();
 
     std::vector<IdString> dead_nets;
 
diff --git a/generic/synth/prims.v b/generic/synth/prims.v
index 95fcfac..1148041 100644
--- a/generic/synth/prims.v
+++ b/generic/synth/prims.v
@@ -25,17 +25,16 @@
 ) (
 	input CLK,
 	input [K-1:0] I,
+	output F,
 	output Q
 );
+	wire f_wire;
 	
-	wire lut_q;
-	LUT #(.K(K), .INIT(INIT)) lut_i(.I(I), .Q(lut_q));
+	LUT #(.K(K), .INIT(INIT)) lut_i(.I(I), .Q(f_wire));
 
-	generate if (FF_USED)
-		DFF dff_i(.CLK(CLK), .D(lut_q), .Q(Q));
-	else
-		assign Q = lut_q; 
-	endgenerate
+	DFF dff_i(.CLK(CLK), .D(f_wire), .Q(Q));
+
+	assign F = f_wire;
 endmodule
 
 module GENERIC_IOB #(
@@ -56,4 +55,4 @@
 	generate if (INPUT_USED)
 		assign O = PAD;
 	endgenerate
-endmodule
\ No newline at end of file
+endmodule
diff --git a/ice40/family.cmake b/ice40/family.cmake
index f22883f..e31fce2 100644
--- a/ice40/family.cmake
+++ b/ice40/family.cmake
@@ -40,14 +40,6 @@
             set(DEV_CC_DB ${CMAKE_CURRENT_BINARY_DIR}/ice40/chipdbs/chipdb-${dev}.bin)
             set(DEV_CONSTIDS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/constids.inc)
             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_CONSTIDS_INC} ${DEV_GFXH} ${DEV_TXT_DB} ${DB_PY} ${PREV_DEV_CC_BBA_DB}
-                )
-            add_custom_command(OUTPUT ${DEV_CC_DB}
-                COMMAND bbasm ${BBASM_ENDIAN_FLAG} ${DEV_CC_BBA_DB} ${DEV_CC_DB}
-                DEPENDS bbasm ${DEV_CC_BBA_DB}
-                )
 
             if(PREGENERATED_BBA_PATH)
                 add_custom_command(OUTPUT ${DEV_CC_DB}
diff --git a/python/interactive.py b/python/interactive.py
new file mode 100644
index 0000000..a2f09ed
--- /dev/null
+++ b/python/interactive.py
@@ -0,0 +1,6 @@
+# Pass this file to one of the Python script arguments (e.g. --pre-place interactive.py)
+# to drop to a command-line interactive Python session in the middle of place and route 
+
+import code
+print("Press Ctrl+D to finish interactive session")
+code.interact(local=locals())