Merge remote-tracking branch 'origin/master' into facade
diff --git a/.gitmodules b/.gitmodules
index e837083..d01ee12 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
 [submodule "database"]
 	path = database
-	url = https://github.com/SymbiFlow/prjtrellis-db
+	url = https://github.com/cr1901/prjtrellis-db.git 
+	branch = facade
diff --git a/database b/database
index c137076..13da868 160000
--- a/database
+++ b/database
@@ -1 +1 @@
-Subproject commit c137076fdd8bfca3d2bf9cdacda9983dbbec599a
+Subproject commit 13da86838ba39f882a81a9fabe030b4c653854c9
diff --git a/devices.json b/devices.json
index 61eaaa8..68df05f 100644
--- a/devices.json
+++ b/devices.json
@@ -24,7 +24,7 @@
                 "max_row" : 50,
                 "max_col" : 72,
                 "col_bias" : 0,
-                "fuzz": 1
+                "fuzz": 0
             },
             "LFE5U-45F": {
                 "packages": ["csfBGA285", "caBGA256", "caBGA381", "caBGA554", "caBGA756"],
@@ -36,7 +36,7 @@
                 "max_row" : 71,
                 "max_col" : 90,
                 "col_bias" : 0,
-                "fuzz": 1
+                "fuzz": 0
             },
             "LFE5U-85F": {
                 "packages": ["csfBGA285", "caBGA381", "caBGA554", "caBGA756"],
@@ -48,7 +48,7 @@
                 "max_row" : 95,
                 "max_col" : 126,
                 "col_bias" : 0,
-                "fuzz": 1
+                "fuzz": 0
             },
             "LFE5UM-25F": {
                 "packages": ["csfBGA285", "caBGA256", "caBGA381", "caBGA554", "caBGA756"],
@@ -60,7 +60,7 @@
                 "max_row" : 50,
                 "max_col" : 72,
                 "col_bias" : 0,
-                "fuzz": 1
+                "fuzz": 0
             },
             "LFE5UM-45F": {
                 "packages": ["csfBGA285", "caBGA256", "caBGA381", "caBGA554", "caBGA756"],
@@ -72,7 +72,7 @@
                 "max_row" : 71,
                 "max_col" : 90,
                 "col_bias" : 0,
-                "fuzz": 1
+                "fuzz": 0
             },
             "LFE5UM-85F": {
                 "packages": ["csfBGA285", "caBGA381", "caBGA554", "caBGA756"],
@@ -84,7 +84,7 @@
                 "max_row" : 95,
                 "max_col" : 126,
                 "col_bias" : 0,
-                "fuzz": 1
+                "fuzz": 0
             },
             "LFE5UM5G-25F": {
                 "packages": ["csfBGA285", "caBGA256", "caBGA381", "caBGA554", "caBGA756"],
@@ -96,7 +96,7 @@
                 "max_row" : 50,
                 "max_col" : 72,
                 "col_bias" : 0,
-                "fuzz": 1
+                "fuzz": 0
             },
             "LFE5UM5G-45F": {
                 "packages": ["csfBGA285", "caBGA256", "caBGA381", "caBGA554", "caBGA756"],
@@ -108,7 +108,7 @@
                 "max_row" : 71,
                 "max_col" : 90,
                 "col_bias" : 0,
-                "fuzz": 1
+                "fuzz": 0
             },
             "LFE5UM5G-85F": {
                 "packages": ["csfBGA285", "caBGA381", "caBGA554", "caBGA756"],
@@ -120,6 +120,35 @@
                 "max_row" : 95,
                 "max_col" : 126,
                 "col_bias" : 0,
+                "fuzz": 0
+            }
+        }
+    },
+
+    "MachXO2" : {
+        "devices" : {
+            "LCMXO2-256HC": {
+                "packages": ["QFN32"],
+                "idcode": "0x012b8043",
+                "frames": 186,
+                "bits_per_frame": 504,
+                "pad_bits_after_frame": 0,
+                "pad_bits_before_frame": 0,
+                "max_row" : 7,
+                "max_col" : 9,
+                "col_bias" : 1,
+                "fuzz": 1
+            },
+            "LCMXO2-1200HC": {
+                "packages": ["QFN32"],
+                "idcode": "0x012ba043",
+                "frames": 333,
+                "bits_per_frame": 1080,
+                "pad_bits_after_frame": 0,
+                "pad_bits_before_frame": 0,
+                "max_row" : 12,
+                "max_col" : 21,
+                "col_bias" : 1,
                 "fuzz": 1
             }
         }
diff --git a/examples/tinyfpga_ax/.gitignore b/examples/tinyfpga_ax/.gitignore
new file mode 100644
index 0000000..26ec132
--- /dev/null
+++ b/examples/tinyfpga_ax/.gitignore
@@ -0,0 +1,7 @@
+*.dump
+*.twr
+*.txt
+*.ncl
+*.tmp
+*.jed
+*.hex
diff --git a/examples/tinyfpga_ax/Makefile b/examples/tinyfpga_ax/Makefile
new file mode 100644
index 0000000..140e6e4
--- /dev/null
+++ b/examples/tinyfpga_ax/Makefile
@@ -0,0 +1,48 @@
+PROJ ?= blinky
+TRELLIS_ROOT ?= ../..
+export DEV_PACKAGE ?= QFN32
+export JEDEC_BITSTREAM ?= 1
+export COMPRESSED_BITSTREAM ?= 1
+
+all: ${PROJ}.bit ${PROJ}-nextpnr.bit ${PROJ}.txt ${PROJ}-nextpnr.txt
+
+# Get proportion of known bits.
+stats:
+	@python3 -c "import sys; print(\"{:.3f}\".format(int(sys.argv[1]) / int(sys.argv[2])))" \
+		`grep -e arc -e word -e enum ${PROJ}.txt | wc -l` \
+		`grep -e arc -e word -e enum -e unknown  ${PROJ}.txt | wc -l`
+
+# Avoid intermediate files from being deleted.
+.PRECIOUS: %.txt %.bit %-roundtrip.txt %-roundtrip.bit %-comp.txt %-comp-roundtrip.bit
+
+# Test that a bitstream from Diamond survives a round-trip from .bit to .txt
+# back to .bit and .txt again.
+%-roundtrip.bit: %.txt
+	ecppack --db ${TRELLIS_ROOT}/database $< $@
+
+%-comp-roundtrip.bit: %-comp.txt
+	ecppack --db ${TRELLIS_ROOT}/database --compress $< $@
+
+%.txt: %.bit
+	ecpunpack --db ${TRELLIS_ROOT}/database --input $< --textcfg $@
+
+# Diamond rules.
+%.bit: %.v %.lpf
+	${TRELLIS_ROOT}/diamond.sh LCMXO2-1200HC ${PROJ}.v
+
+# FOSS rules.
+# yosys
+%.json: %.v %.lpf
+	@true
+
+# ecppack --db ${TRELLIS_ROOT}/database --input $< $@
+%-nextpnr.bit: %-nextpnr.txt
+	@true
+
+# nextpnr-generic
+${PROJ}-nextpnr.txt: ${PROJ}.json
+	@true
+
+clean:
+	rm -rf ${PROJ}.tmp ${PROJ}_out.ncl ${PROJ}*.bit ${PROJ}.jed ${PROJ}.dump \
+		${PROJ}.twr ${PROJ}*.txt ${PROJ}.json ${PROJ}-nextpnr.* ${PROJ}*.hex
diff --git a/examples/tinyfpga_ax/blinky.lpf b/examples/tinyfpga_ax/blinky.lpf
new file mode 100644
index 0000000..18782c5
--- /dev/null
+++ b/examples/tinyfpga_ax/blinky.lpf
@@ -0,0 +1,3 @@
+BLOCK RESETPATHS ;
+BLOCK ASYNCPATHS ;
+LOCATE COMP "pin1" SITE "13" ;
diff --git a/examples/tinyfpga_ax/blinky.v b/examples/tinyfpga_ax/blinky.v
new file mode 100644
index 0000000..49ed054
--- /dev/null
+++ b/examples/tinyfpga_ax/blinky.v
@@ -0,0 +1,26 @@
+// Modified from:
+// https://github.com/tinyfpga/TinyFPGA-A-Series/tree/master/template_a2
+
+module TinyFPGA_A2 (
+  inout pin1
+);
+
+
+  wire clk;
+
+  OSCH #(
+    .NOM_FREQ("2.08")
+  ) internal_oscillator_inst (
+    .STDBY(1'b0),
+    .OSC(clk)
+  );
+
+  reg [23:0] led_timer;
+
+  always @(posedge clk) begin
+    led_timer <= led_timer + 1;
+  end
+
+  // left side of board
+  assign pin1 = led_timer[23];
+endmodule
diff --git a/examples/tinyfpga_ax/uart.lpf b/examples/tinyfpga_ax/uart.lpf
new file mode 100644
index 0000000..48e84d4
--- /dev/null
+++ b/examples/tinyfpga_ax/uart.lpf
@@ -0,0 +1,10 @@
+BLOCK RESETPATHS ;
+BLOCK ASYNCPATHS ;
+LOCATE COMP "serial_rx" SITE "13" ;
+LOCATE COMP "serial_tx" SITE "14" ;
+LOCATE COMP "user_led" SITE "16" ;
+LOCATE COMP "user_led_1" SITE "17" ;
+LOCATE COMP "user_led_2" SITE "20" ;
+LOCATE COMP "user_led_3" SITE "21" ;
+LOCATE COMP "user_led_4" SITE "23" ;
+LOCATE COMP "clk12" SITE "25" ;
diff --git a/examples/tinyfpga_ax/uart.v b/examples/tinyfpga_ax/uart.v
new file mode 100644
index 0000000..0d76baa
--- /dev/null
+++ b/examples/tinyfpga_ax/uart.v
@@ -0,0 +1,207 @@
+/* Example UART derived from: https://github.com/cr1901/migen_uart.
+   Requires 12MHz clock and runs at 19,200 baud. */
+
+/* Machine-generated using Migen */
+module top(
+	input serial_rx,
+	output serial_tx,
+	output user_led,
+	output user_led_1,
+	output user_led_2,
+	output user_led_3,
+	output user_led_4,
+	input clk12
+);
+
+wire [7:0] out_data;
+wire [7:0] in_data;
+wire tx;
+wire rx;
+reg wr = 1'd0;
+reg rd = 1'd0;
+wire tx_empty;
+wire rx_empty;
+wire tx_ov;
+wire rx_ov;
+wire sout_load;
+wire [7:0] sout_out_data;
+wire sout_shift;
+reg sout_empty = 1'd1;
+reg sout_overrun = 1'd0;
+reg [3:0] sout_count = 4'd0;
+reg [9:0] sout_reg = 10'd0;
+reg sout_tx;
+wire sin_rx;
+wire sin_shift;
+wire sin_take;
+reg [7:0] sin_in_data = 8'd0;
+wire sin_edge;
+reg sin_empty = 1'd1;
+reg sin_busy = 1'd0;
+reg sin_overrun = 1'd0;
+reg sin_sync_rx = 1'd0;
+reg [8:0] sin_reg = 9'd0;
+reg sin_rx_prev = 1'd0;
+reg [3:0] sin_count = 4'd0;
+wire out_active;
+wire in_active;
+reg shift_out_strobe = 1'd0;
+reg shift_in_strobe = 1'd0;
+reg [9:0] in_counter = 10'd0;
+reg [9:0] out_counter = 10'd0;
+wire sys_clk;
+wire sys_rst;
+wire por_clk;
+reg int_rst = 1'd1;
+
+
+// Adding a dummy event (using a dummy signal 'dummy_s') to get the simulator
+// to run the combinatorial process once at the beginning.
+// synthesis translate_off
+reg dummy_s;
+initial dummy_s <= 1'd0;
+// synthesis translate_on
+
+assign user_led_1 = (~serial_tx);
+assign user_led = (~serial_rx);
+assign user_led_2 = sout_load;
+assign user_led_3 = sin_take;
+assign user_led_4 = sin_empty;
+assign serial_tx = tx;
+assign rx = serial_rx;
+assign out_data = in_data;
+assign in_data = sin_in_data;
+assign sout_out_data = out_data;
+assign sin_take = rd;
+assign sout_load = wr;
+assign tx = sout_tx;
+assign sin_rx = rx;
+assign tx_empty = sout_empty;
+assign rx_empty = sin_empty;
+assign tx_ov = sout_overrun;
+assign rx_ov = sin_overrun;
+assign sout_shift = shift_out_strobe;
+assign sin_shift = shift_in_strobe;
+assign out_active = (~sout_empty);
+assign in_active = sin_busy;
+
+// synthesis translate_off
+reg dummy_d;
+// synthesis translate_on
+always @(*) begin
+	sout_tx <= 1'd0;
+	if (sout_empty) begin
+		sout_tx <= 1'd1;
+	end else begin
+		sout_tx <= sout_reg[0];
+	end
+// synthesis translate_off
+	dummy_d <= dummy_s;
+// synthesis translate_on
+end
+assign sin_edge = ((sin_rx_prev == 1'd1) & (sin_sync_rx == 1'd0));
+assign sys_clk = clk12;
+assign por_clk = clk12;
+assign sys_rst = int_rst;
+
+always @(posedge por_clk) begin
+	int_rst <= 1'd0;
+end
+
+always @(posedge sys_clk) begin
+	wr <= 1'd0;
+	rd <= 1'd0;
+	if ((~sin_empty)) begin
+		wr <= 1'd1;
+		rd <= 1'd1;
+	end
+	if (sout_load) begin
+		if (sout_empty) begin
+			sout_reg[0] <= 1'd0;
+			sout_reg[8:1] <= sout_out_data;
+			sout_reg[9] <= 1'd1;
+			sout_empty <= 1'd0;
+			sout_overrun <= 1'd0;
+			sout_count <= 1'd0;
+		end else begin
+			sout_overrun <= 1'd1;
+		end
+	end
+	if (((~sout_empty) & sout_shift)) begin
+		sout_reg[8:0] <= sout_reg[9:1];
+		sout_reg[9] <= 1'd0;
+		if ((sout_count == 4'd9)) begin
+			sout_empty <= 1'd1;
+			sout_count <= 1'd0;
+		end else begin
+			sout_count <= (sout_count + 1'd1);
+		end
+	end
+	sin_sync_rx <= sin_rx;
+	sin_rx_prev <= sin_sync_rx;
+	if (sin_take) begin
+		sin_empty <= 1'd1;
+		sin_overrun <= 1'd0;
+	end
+	if (((~sin_busy) & sin_edge)) begin
+		sin_busy <= 1'd1;
+	end
+	if ((sin_shift & sin_busy)) begin
+		sin_reg[8] <= sin_sync_rx;
+		sin_reg[7:0] <= sin_reg[8:1];
+		if ((sin_count == 4'd9)) begin
+			sin_in_data <= sin_reg[8:1];
+			sin_count <= 1'd0;
+			sin_busy <= 1'd0;
+			if ((~sin_empty)) begin
+				sin_overrun <= 1'd1;
+			end else begin
+				sin_empty <= 1'd0;
+			end
+		end else begin
+			sin_count <= (sin_count + 1'd1);
+		end
+	end
+	out_counter <= 1'd0;
+	in_counter <= 1'd0;
+	if (in_active) begin
+		shift_in_strobe <= 1'd0;
+		in_counter <= (in_counter + 1'd1);
+		if ((in_counter == 9'd311)) begin
+			shift_in_strobe <= 1'd1;
+		end
+		if ((in_counter == 10'd623)) begin
+			in_counter <= 1'd0;
+		end
+	end
+	if (out_active) begin
+		shift_out_strobe <= 1'd0;
+		out_counter <= (out_counter + 1'd1);
+		if ((out_counter == 10'd623)) begin
+			out_counter <= 1'd0;
+			shift_out_strobe <= 1'd1;
+		end
+	end
+	if (sys_rst) begin
+		wr <= 1'd0;
+		rd <= 1'd0;
+		sout_empty <= 1'd1;
+		sout_overrun <= 1'd0;
+		sout_count <= 4'd0;
+		sout_reg <= 10'd0;
+		sin_in_data <= 8'd0;
+		sin_empty <= 1'd1;
+		sin_busy <= 1'd0;
+		sin_overrun <= 1'd0;
+		sin_sync_rx <= 1'd0;
+		sin_reg <= 9'd0;
+		sin_rx_prev <= 1'd0;
+		sin_count <= 4'd0;
+		shift_out_strobe <= 1'd0;
+		shift_in_strobe <= 1'd0;
+		in_counter <= 10'd0;
+		out_counter <= 10'd0;
+	end
+end
+
+endmodule
diff --git a/experiments/machxo2/center_mux/center_mux.py b/experiments/machxo2/center_mux/center_mux.py
new file mode 100644
index 0000000..8f33455
--- /dev/null
+++ b/experiments/machxo2/center_mux/center_mux.py
@@ -0,0 +1,68 @@
+import diamond
+from string import Template
+import pytrellis
+import shutil
+import os
+
+device = "LCMXO2-1200HC"
+
+routes = [
+    ("R1C13_JCLK0", "R6C13_JPCLKCIBVIQT0"),
+    ("R6C13_JPCLKCIBVIQT0", "R6C13_PCLKCIBVIQT0"),
+    ("R6C13_PCLKCIBVIQT0", "R6C13_VPRXCLKI0"),
+    ("R6C13_VPRXCLKI0", "R6C13_CLKI0_DCC"),
+    ("R6C13_CLKI0_DCC", "R6C13_CLKO0_DCC"),
+    ("R6C13_CLKO0_DCC", "R6C13_VPRX0000"),
+    ("R6C13_VPRX0000", "R6C8_HPSX0000"),
+    ("R6C13_VPRX0000", "R6C18_HPSX0000"),
+    ("R6C13_JLPLLCLK1", "R6C13_VPRXCLKI0"),
+    ("R6C8_HPSX0000", "R6C10_CLKI0B_DCC"),
+    ("R6C10_CLKI0B_DCC", "R6C10_CLKO0B_DCC"),
+    ("R6C14_CLKI0B_DCC", "R6C14_CLKO0B_DCC"),
+    ("R6C13_JTCDIVX1", "R6C13_VPRXCLKI5"),
+    ("R6C13_PCLKCIBMID2", "R6C13_VPRXCLK60"),
+    ("R6C13_PCLKCIBMID3", "R6C13_VPRXCLK61"),
+    ("R6C13_PCLKCIBMID2", "R6C13_VPRXCLK71"),
+    ("R6C13_PCLKCIBMID3", "R6C13_VPRXCLK70")
+]
+
+def run_get_tiles(mux_driver, sink):
+    route = ""
+    if mux_driver != "":
+        route = "route\n\t\t\t" + mux_driver + "." + sink + ";"
+
+    with open("center_mux_template.ncl", "r") as inf:
+        with open("work/center_mux.ncl", "w") as ouf:
+            ouf.write(Template(inf.read()).substitute(route=route))
+    diamond.run(device, "work/center_mux.ncl")
+    bs = pytrellis.Bitstream.read_bit("work/center_mux.bit")
+    chip = bs.deserialise_chip()
+    return chip.tiles
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    shutil.rmtree("work", ignore_errors=True)
+    os.mkdir("work")
+    baseline = run_get_tiles("", "")
+
+    with open("center_mux_diff.txt", "w") as f:
+        for r in routes:
+            modified = run_get_tiles(r[0], r[1])
+
+            tile_keys = []
+            for t in modified:
+                tile_keys.append(t.key())
+
+            print("{0} -> {1}".format(r[0], r[1]), file=f)
+            for k in tile_keys:
+                diff = modified[k].cram - baseline[k].cram
+                diff_str = ["{}F{}B{}".format("!" if b.delta < 0 else "", b.frame, b.bit) for b in diff]
+                if not diff_str:
+                    continue
+                print("{0: <30}{1}".format(k, " ".join(diff_str)), file=f)
+                f.flush()
+            print("", file=f)
+
+if __name__ == "__main__":
+    main()
diff --git a/experiments/machxo2/center_mux/center_mux_diff.txt b/experiments/machxo2/center_mux/center_mux_diff.txt
new file mode 100644
index 0000000..8adfd23
--- /dev/null
+++ b/experiments/machxo2/center_mux/center_mux_diff.txt
@@ -0,0 +1,47 @@
+R1C13_JCLK0 -> R6C13_JPCLKCIBVIQT0
+
+R6C13_JPCLKCIBVIQT0 -> R6C13_PCLKCIBVIQT0
+
+R6C13_PCLKCIBVIQT0 -> R6C13_VPRXCLKI0
+CENTER8:CENTER7               F25B0
+CENTER9:CENTER8               F0B0 F0B1 F1B1
+
+R6C13_VPRXCLKI0 -> R6C13_CLKI0_DCC
+
+R6C13_CLKI0_DCC -> R6C13_CLKO0_DCC
+
+R6C13_CLKO0_DCC -> R6C13_VPRX0000
+
+R6C13_VPRX0000 -> R6C8_HPSX0000
+CENTER6:CENTER_EBR_CIB        F23B0
+
+R6C13_VPRX0000 -> R6C18_HPSX0000
+CENTER6:CENTER_EBR_CIB        F23B1
+
+R6C13_JLPLLCLK1 -> R6C13_VPRXCLKI0
+CENTER8:CENTER7               F28B1
+CENTER9:CENTER8               F1B1
+
+R6C8_HPSX0000 -> R6C10_CLKI0B_DCC
+
+R6C10_CLKI0B_DCC -> R6C10_CLKO0B_DCC
+CIB_R6C10:CIB_EBR0            F26B31
+
+R6C14_CLKI0B_DCC -> R6C14_CLKO0B_DCC
+CIB_R6C14:CIB_EBR0            F26B31
+
+R6C13_JTCDIVX1 -> R6C13_VPRXCLKI5
+CENTER5:CENTER5               F25B0 F27B0
+
+R6C13_PCLKCIBMID2 -> R6C13_VPRXCLK60
+CENTER5:CENTER5               F25B0 F27B0
+
+R6C13_PCLKCIBMID3 -> R6C13_VPRXCLK61
+CENTER5:CENTER5               F25B0 F27B0
+
+R6C13_PCLKCIBMID2 -> R6C13_VPRXCLK71
+CENTER5:CENTER5               F25B0 F27B0
+
+R6C13_PCLKCIBMID3 -> R6C13_VPRXCLK70
+CENTER5:CENTER5               F25B0 F27B0
+
diff --git a/experiments/machxo2/center_mux/center_mux_template.ncl b/experiments/machxo2/center_mux/center_mux_template.ncl
new file mode 100644
index 0000000..3f9d75c
--- /dev/null
+++ b/experiments/machxo2/center_mux/center_mux_template.ncl
@@ -0,0 +1,35 @@
+::FROM-WRITER;
+design top
+{
+    device
+    {
+        architecture xo2c00;
+        device LCMXO2-1200HC;
+        package QFN32;
+        performance "6";
+    }
+
+   comp SLICE_0
+      [,,,,A0,B0,D0,C0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,]
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:LOGIC "
+                 "K0::H0=0 "
+                 "F0:F ";
+         primitive K0 i3_4_lut;
+      }
+      site R2C2A;
+   }
+
+    signal q_c
+   {
+      signal-pins
+         // drivers
+         (SLICE_0, F0),
+         // loads
+         (SLICE_0, CLK);
+      ${route}
+   }
+}
diff --git a/experiments/machxo2/findnets/findnets.py b/experiments/machxo2/findnets/findnets.py
new file mode 100644
index 0000000..e8d51ae
--- /dev/null
+++ b/experiments/machxo2/findnets/findnets.py
@@ -0,0 +1,75 @@
+import sys
+from collections import defaultdict
+
+from fuzzconfig import FuzzConfig
+import pytrellis
+import isptcl
+import argparse
+
+
+def net_mode(args):
+    pytrellis.load_database("../../../database")
+
+    cfg = FuzzConfig(job="FINDNETS_NETS_{}".format(args.nets[0]), family="MachXO2", device="LCMXO2-1200HC", ncl="plc2route.ncl", tiles=[])
+    cfg.setup()
+
+    arcs = isptcl.get_arcs_on_wires(cfg.ncd_prf, args.nets, False, defaultdict(lambda : str("mark")))
+
+    with open("{}_out.txt".format(args.nets[0]), "w") as fp:
+        for (k, v) in arcs.items():
+            print("{}:".format(k), file=fp)
+            for c in v:
+                if isinstance(c, isptcl.AmbiguousArc):
+                    print(str(c), file=fp)
+                else:
+                    print("{} --> {}".format(c[0], c[1]), file=fp)
+
+            fp.flush()
+            print("", file=fp)
+
+def pos_mode(args):
+    pytrellis.load_database("../../../database")
+
+    cfg = FuzzConfig(job="FINDNETS_R{}C{}".format(args.row, args.col), family="MachXO2", device="LCMXO2-1200HC", ncl="plc2route.ncl", tiles=[])
+    cfg.setup()
+
+    netdata = isptcl.get_wires_at_position(cfg.ncd_prf, (args.row, args.col))
+    netnames = [x[0] for x in netdata]
+    arcs = isptcl.get_arcs_on_wires(cfg.ncd_prf, netnames, False, defaultdict(lambda : str("mark")))
+
+    with open("r{}c{}_{}out.txt".format(args.row, args.col, "a_" if args.a else ""), "w")  as fp:
+        for (k, v) in arcs.items():
+            print("{}:".format(k), file=fp)
+            for c in v:
+                if isinstance(c, isptcl.AmbiguousArc):
+                    print(str(c), file=fp)
+                else:
+                    if not args.a:
+                        print("{} --> {}".format(c[0], c[1]), file=fp)
+
+            fp.flush()
+            print("", file=fp)
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="Find which nets IspTcl returns in various ways.")
+    subparsers = parser.add_subparsers()
+
+    parser_pos = subparsers.add_parser("pos", help="Return all nets based on position.")
+    parser_net = subparsers.add_parser("net", help="Return connections to one (or more) nets.")
+
+    parser_pos.add_argument("-a", action="store_true", help="Return arcs with ambiguous direction only.")
+    parser_pos.add_argument("row", type=int, help="Tile row.")
+    parser_pos.add_argument("col", type=int, help="Tile column.")
+    parser_pos.set_defaults(func=pos_mode)
+
+    parser_net.add_argument("nets", type=str, nargs="+", help="List of nets to find connections.")
+    parser_net.set_defaults(func=net_mode)
+    args = parser.parse_args()
+
+    if len(args.__dict__) <= 1:
+        # No arguments or subcommands were given.
+        parser.print_help()
+        parser.exit()
+
+    args.func(args)
diff --git a/experiments/machxo2/findnets/plc2route.ncl b/experiments/machxo2/findnets/plc2route.ncl
new file mode 100644
index 0000000..7e7370b
--- /dev/null
+++ b/experiments/machxo2/findnets/plc2route.ncl
@@ -0,0 +1,35 @@
+::FROM-WRITER;
+design top
+{
+    device
+    {
+        architecture xo2c00;
+        device LCMXO2-1200HC;
+        package QFN32;
+        performance "6";
+    }
+
+   comp SLICE_0
+      [,,,,A0,B0,D0,C0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,]
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:LOGIC "
+                 "K0::H0=0 "
+                 "F0:F ";
+         primitive K0 i3_4_lut;
+      }
+      site R2C2A;
+   }
+
+    signal q_c
+   {
+      signal-pins
+         // drivers
+         (SLICE_0, F0),
+         // loads
+         (SLICE_0, A0);
+      ${route}
+   }
+}
diff --git a/experiments/machxo2/interconnect_poc/fuzz_single_mux.py b/experiments/machxo2/interconnect_poc/fuzz_single_mux.py
new file mode 100644
index 0000000..af81853
--- /dev/null
+++ b/experiments/machxo2/interconnect_poc/fuzz_single_mux.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+
+import os
+from os import path
+import shutil
+
+import diamond
+from string import Template
+import pytrellis
+
+device = "LCMXO2-1200HC"
+sink = "R2C2_A0"
+# Drivers found using ispTcl
+drivers = [
+    "R1C2_V02S0501",
+    # "R2C2_H02W0501",
+    # "R2C2_H01E0001",
+    # "R2C3_H02W0701",
+    # "R2C3_H02W0501",
+    # "R2C2_H02W0701",
+    # "R2C2_H02E0501",
+    # "R3C2_V02N0501",
+    # "R1C2_V02S0701",
+    # "R2C2_F5",
+    # "R2C2_H00L0000",
+    # "R2C2_F7",
+    # "R2C2_H02E0701",
+    # "R2C2_V02N0701",
+    # "R2C1_H02E0701",
+    # "R2C3_H01E0001",
+    # "R2C2_V02S0501",
+    # "R3C2_V02N0701",
+    # "R2C2_V02S0701",
+    # "R2C2_V01N0101",
+    # "R2C2_V02N0501",
+    # "R2C1_H02E0501",
+    # "R2C2_H00L0100",
+    # "R2C2_H00R0000"
+]
+
+
+def run_get_bits(mux_driver):
+    route = ""
+    if mux_driver != "":
+        route = "route\n\t\t\t" + mux_driver + "." + sink + ";"
+    with open("mux_template.ncl", "r") as inf:
+        with open("work/mux.ncl", "w") as ouf:
+            ouf.write(Template(inf.read()).substitute(route=route))
+    diamond.run(device, "work/mux.ncl")
+    bs = pytrellis.Bitstream.read_bit("work/mux.bit")
+    chip = bs.deserialise_chip()
+    tile = chip.tiles["R2C2:PLC"]
+    return tile.cram
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    shutil.rmtree("work", ignore_errors=True)
+    os.mkdir("work")
+    baseline = run_get_bits("")
+    with open("a0_mux_out.txt", "w") as f:
+        for d in drivers:
+            bits = run_get_bits(d)
+            diff = bits - baseline
+            diff_str = ["{}F{}B{}".format("!" if b.delta < 0 else "", b.frame, b.bit) for b in diff]
+            print("{0: <18}{1}".format(d, " ".join(diff_str)), file=f)
+            f.flush()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/experiments/machxo2/interconnect_poc/mux_template.ncl b/experiments/machxo2/interconnect_poc/mux_template.ncl
new file mode 100644
index 0000000..7e7370b
--- /dev/null
+++ b/experiments/machxo2/interconnect_poc/mux_template.ncl
@@ -0,0 +1,35 @@
+::FROM-WRITER;
+design top
+{
+    device
+    {
+        architecture xo2c00;
+        device LCMXO2-1200HC;
+        package QFN32;
+        performance "6";
+    }
+
+   comp SLICE_0
+      [,,,,A0,B0,D0,C0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,]
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:LOGIC "
+                 "K0::H0=0 "
+                 "F0:F ";
+         primitive K0 i3_4_lut;
+      }
+      site R2C2A;
+   }
+
+    signal q_c
+   {
+      signal-pins
+         // drivers
+         (SLICE_0, F0),
+         // loads
+         (SLICE_0, A0);
+      ${route}
+   }
+}
diff --git a/experiments/machxo2/io_params/.gitignore b/experiments/machxo2/io_params/.gitignore
new file mode 100644
index 0000000..90d4bad
--- /dev/null
+++ b/experiments/machxo2/io_params/.gitignore
@@ -0,0 +1 @@
+*_diff.txt
diff --git a/experiments/machxo2/io_params/io_params.py b/experiments/machxo2/io_params/io_params.py
new file mode 100644
index 0000000..e865ef5
--- /dev/null
+++ b/experiments/machxo2/io_params/io_params.py
@@ -0,0 +1,65 @@
+import diamond
+from string import Template
+import pytrellis
+import shutil
+import os
+import argparse
+
+device = "LCMXO2-1200HC"
+
+def run_get_tiles(dir, io_type="LVCMOS33", loc="PB11D"):
+    with open("io_params_template.v", "r") as inf:
+        with open("work/io_params.v", "w") as ouf:
+            ouf.write(Template(inf.read()).substitute(dir=dir,
+                io_type="\"" + io_type + "\"", loc= "\"" + loc + "\""))
+    diamond.run(device, "work/io_params.v")
+    bs = pytrellis.Bitstream.read_bit("work/io_params.bit")
+    chip = bs.deserialise_chip()
+    return chip.tiles
+
+
+def main(args):
+    pytrellis.load_database("../../../database")
+    shutil.rmtree("work", ignore_errors=True)
+    os.mkdir("work")
+    os.environ['DEV_PACKAGE'] = args.p
+    baseline = run_get_tiles("NONE", args.io_type, args.loc)
+
+    dirs = []
+
+    if args.b:
+        dirs.append("BIDIR")
+    if args.i:
+        dirs.append("INPUT")
+    if args.o:
+        dirs.append("OUTPUT")
+
+    with open("io_params_diff.txt", "w") as f:
+        for d in dirs:
+            modified = run_get_tiles(d, args.io_type, args.loc)
+
+            tile_keys = []
+            for t in modified:
+                tile_keys.append(t.key())
+
+            print("{0}".format(d), file=f)
+            for k in tile_keys:
+                diff = modified[k].cram - baseline[k].cram
+                diff_str = ["{}F{}B{}".format("!" if b.delta < 0 else "", b.frame, b.bit) for b in diff]
+                if not diff_str:
+                    continue
+                print("{0: <30}{1}".format(k, " ".join(diff_str)), file=f)
+                f.flush()
+            print("", file=f)
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="Test I/O Sites.")
+    parser.add_argument("-b", help="Test bidirectional.", action="store_true")
+    parser.add_argument("-i", help="Test input.", action="store_true")
+    parser.add_argument("-o", help="Test output.", action="store_true")
+    parser.add_argument("-p", type=str, default="QFN32", help="Device package to test.")
+    parser.add_argument(dest="io_type", type=str, default="LVCMOS33", help="I/O standard to test.")
+    parser.add_argument(dest="loc", type=str, default="PB11D", help="Site to test.")
+    args = parser.parse_args()
+
+    main(args)
diff --git a/experiments/machxo2/io_params/io_params_template.v b/experiments/machxo2/io_params/io_params_template.v
new file mode 100644
index 0000000..e71f205
--- /dev/null
+++ b/experiments/machxo2/io_params/io_params_template.v
@@ -0,0 +1,68 @@
+`define ${dir}
+
+`ifdef NONE
+
+module top(input x);
+
+// Minimum legal empty module
+wire dummy;
+
+// Dummy load
+GSR gsr_i(.GSR(dummy));
+
+// Dummy source
+OSCH osc_i(.OSC(dummy));
+
+
+endmodule
+
+`else
+
+module top(inout pad);
+
+`ifdef BIDIR
+
+wire dummyo, dummyi;
+
+(* keep *)
+(* LOC=${loc} *)
+(* IO_TYPE=${io_type} *)
+
+BB b_b(.B(pad), .O(dummyo), .I(1'b1), .T(dummyi));
+
+// Dummy load
+GSR gsr_i(.GSR(dummyo));
+
+// Dummy source
+OSCH osc_i(.OSC(dummyi));
+
+`endif
+
+`ifdef INPUT
+
+wire dummyo;
+
+(* keep *)
+(* LOC=${loc} *)
+(* IO_TYPE=${io_type} *)
+
+IB i_b(.I(pad), .O(dummyo));
+
+// Dummy load
+GSR gsr_i(.GSR(dummyo));
+
+`endif
+
+`ifdef OUTPUT
+
+(* keep *)
+(* LOC=${loc} *)
+(* IO_TYPE=${io_type} *)
+
+OB o_b(.O(pad), .I(1'b1));
+
+`endif
+
+endmodule
+
+`endif
diff --git a/fuzzers/machxo2/001-plc2_routing/fuzzer.py b/fuzzers/machxo2/001-plc2_routing/fuzzer.py
new file mode 100644
index 0000000..b3f86cd
--- /dev/null
+++ b/fuzzers/machxo2/001-plc2_routing/fuzzer.py
@@ -0,0 +1,28 @@
+from fuzzconfig import FuzzConfig
+import interconnect
+import nets
+import pytrellis
+import re
+
+cfg = FuzzConfig(job="PLC2ROUTE", family="MachXO2", device="LCMXO2-1200HC", ncl="plc2route.ncl", tiles=["R5C10:PLC"])
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    cfg.setup()
+
+    span1_re = re.compile(r'R\d+C\d+_[VH]01[NESWTLBR]\d{4}')
+
+    def nn_filter(net, netnames):
+        """ Match nets that are: in the tile according to Tcl, global nets, or span-1 nets that are accidentally
+        left out by Tcl"""
+        return net in netnames or nets.is_global(net) or span1_re.match(net)
+
+    interconnect.fuzz_interconnect(config=cfg, location=(5, 10),
+                                   netname_predicate=nn_filter,
+                                   netname_filter_union=True,
+                                   enable_span1_fix=True,
+                                   bias=1)
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/001-plc2_routing/plc2route.ncl b/fuzzers/machxo2/001-plc2_routing/plc2route.ncl
new file mode 100644
index 0000000..7e7370b
--- /dev/null
+++ b/fuzzers/machxo2/001-plc2_routing/plc2route.ncl
@@ -0,0 +1,35 @@
+::FROM-WRITER;
+design top
+{
+    device
+    {
+        architecture xo2c00;
+        device LCMXO2-1200HC;
+        package QFN32;
+        performance "6";
+    }
+
+   comp SLICE_0
+      [,,,,A0,B0,D0,C0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,]
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:LOGIC "
+                 "K0::H0=0 "
+                 "F0:F ";
+         primitive K0 i3_4_lut;
+      }
+      site R2C2A;
+   }
+
+    signal q_c
+   {
+      signal-pins
+         // drivers
+         (SLICE_0, F0),
+         // loads
+         (SLICE_0, A0);
+      ${route}
+   }
+}
diff --git a/fuzzers/machxo2/003-lut_init/empty.ncl b/fuzzers/machxo2/003-lut_init/empty.ncl
new file mode 100644
index 0000000..11b72ab
--- /dev/null
+++ b/fuzzers/machxo2/003-lut_init/empty.ncl
@@ -0,0 +1,12 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+}
diff --git a/fuzzers/machxo2/003-lut_init/fuzzer.py b/fuzzers/machxo2/003-lut_init/fuzzer.py
new file mode 100644
index 0000000..96d9983
--- /dev/null
+++ b/fuzzers/machxo2/003-lut_init/fuzzer.py
@@ -0,0 +1,46 @@
+from fuzzconfig import FuzzConfig
+import nonrouting
+import fuzzloops
+import nets
+import pytrellis
+import re
+
+cfg = FuzzConfig(job="PLC2INIT", family="MachXO2", device="LCMXO2-1200HC", ncl="empty.ncl", tiles=["R10C11:PLC"])
+
+
+def get_lut_function(init_bits):
+    sop_terms = []
+    lut_inputs = ["A", "B", "C", "D"]
+    for i in range(16):
+        if init_bits[i]:
+            p_terms = []
+            for j in range(4):
+                if i & (1 << j) != 0:
+                    p_terms.append(lut_inputs[j])
+                else:
+                    p_terms.append("~" + lut_inputs[j])
+            sop_terms.append("({})".format("*".join(p_terms)))
+    if len(sop_terms) == 0:
+        lut_func = "0"
+    else:
+        lut_func = "+".join(sop_terms)
+    return lut_func
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    cfg.setup()
+    empty_bitfile = cfg.build_design(cfg.ncl, {})
+    cfg.ncl = "lut.ncl"
+
+    def per_slice(slicen):
+        for k in range(2):
+            def get_substs(bits):
+                return dict(slice=slicen, k=str(k), lut_func=get_lut_function(bits))
+            nonrouting.fuzz_word_setting(cfg, "SLICE{}.K{}.INIT".format(slicen, k), 16, get_substs, empty_bitfile)
+
+    fuzzloops.parallel_foreach(["A", "B", "C", "D"], per_slice)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/003-lut_init/lut.ncl b/fuzzers/machxo2/003-lut_init/lut.ncl
new file mode 100644
index 0000000..3f9fa20
--- /dev/null
+++ b/fuzzers/machxo2/003-lut_init/lut.ncl
@@ -0,0 +1,25 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+   comp SLICE_0
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:LOGIC "
+                 "K${k}::H${k}=${lut_func} "
+                 "F${k}:F ";
+         primitive K${k} i3_4_lut;
+      }
+      site R10C11${slice};
+   }
+
+}
diff --git a/fuzzers/machxo2/005-reg_config/empty.ncl b/fuzzers/machxo2/005-reg_config/empty.ncl
new file mode 100644
index 0000000..11b72ab
--- /dev/null
+++ b/fuzzers/machxo2/005-reg_config/empty.ncl
@@ -0,0 +1,12 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+}
diff --git a/fuzzers/machxo2/005-reg_config/fuzzer.py b/fuzzers/machxo2/005-reg_config/fuzzer.py
new file mode 100644
index 0000000..c332b6f
--- /dev/null
+++ b/fuzzers/machxo2/005-reg_config/fuzzer.py
@@ -0,0 +1,38 @@
+from fuzzconfig import FuzzConfig
+import nonrouting
+import fuzzloops
+import nets
+import pytrellis
+import re
+
+cfg = FuzzConfig(job="PLC2REG", family="MachXO2", device="LCMXO2-1200HC", ncl="empty.ncl", tiles=["R10C11:PLC"])
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    cfg.setup()
+    empty_bitfile = cfg.build_design(cfg.ncl, {})
+    cfg.ncl = "reg.ncl"
+
+    def per_slice(slicen):
+        r = 0
+
+        def get_substs(regset="RESET", sd="0", gsr="DISABLED"):
+            return dict(slice=slicen, r=str(r), regset=regset, sd=sd, gsr=gsr)
+
+        for r in range(2):
+            nonrouting.fuzz_enum_setting(cfg, "SLICE{}.REG{}.REGSET".format(slicen, r), ["RESET", "SET"],
+                                         lambda x: get_substs(regset=x),
+                                         empty_bitfile)
+            nonrouting.fuzz_enum_setting(cfg, "SLICE{}.REG{}.SD".format(slicen, r), ["0", "1"],
+                                         lambda x: get_substs(sd=x),
+                                         empty_bitfile)
+        nonrouting.fuzz_enum_setting(cfg, "SLICE{}.GSR".format(slicen), ["DISABLED", "ENABLED"],
+                                     lambda x: get_substs(gsr=x),
+                                     empty_bitfile)
+
+    fuzzloops.parallel_foreach(["A", "B", "C", "D"], per_slice)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/005-reg_config/reg.ncl b/fuzzers/machxo2/005-reg_config/reg.ncl
new file mode 100644
index 0000000..a59a8b9
--- /dev/null
+++ b/fuzzers/machxo2/005-reg_config/reg.ncl
@@ -0,0 +1,31 @@
+::FROM-WRITER;
+design top
+{
+    device
+    {
+        architecture xo2c00;
+        device LCMXO2-1200HC;
+        package QFN32;
+        performance "6";
+    }
+
+   comp SLICE_0
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:LOGIC "
+                 "REG${r}:::REGSET=${regset}:SD=${sd} "
+                 "Q${r}:Q "
+                 "GSR:${gsr} "
+                 "CLKMUX:CLK "
+                 "CEMUX:1 "
+                 "LSRMUX:LSR "
+                 "SRMODE:LSR_OVER_CE "
+                 "M0MUX:M0 ";
+         primitive REG${r} q_6;
+      }
+      site R10C11${slice};
+   }
+
+}
diff --git a/fuzzers/machxo2/007-plc2_cemux/cemux.ncl b/fuzzers/machxo2/007-plc2_cemux/cemux.ncl
new file mode 100644
index 0000000..5619b2b
--- /dev/null
+++ b/fuzzers/machxo2/007-plc2_cemux/cemux.ncl
@@ -0,0 +1,31 @@
+::FROM-WRITER;
+design top
+{
+    device
+    {
+        architecture xo2c00;
+        device LCMXO2-1200HC;
+        package QFN32;
+        performance "6";
+    }
+
+   comp SLICE_0
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:LOGIC "
+                 "REG0:::REGSET=RESET:SD=0 "
+                 "Q0:Q "
+                 "GSR:DISABLED "
+                 "CLKMUX:CLK "
+                 "CEMUX:${cemux} "
+                 "LSRMUX:LSR "
+                 "SRMODE:LSR_OVER_CE "
+                 "M0MUX:M0 ";
+         primitive REG0 q_6;
+      }
+      site R10C11${slice};
+   }
+
+}
diff --git a/fuzzers/machxo2/007-plc2_cemux/empty.ncl b/fuzzers/machxo2/007-plc2_cemux/empty.ncl
new file mode 100644
index 0000000..11b72ab
--- /dev/null
+++ b/fuzzers/machxo2/007-plc2_cemux/empty.ncl
@@ -0,0 +1,12 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+}
diff --git a/fuzzers/machxo2/007-plc2_cemux/fuzzer.py b/fuzzers/machxo2/007-plc2_cemux/fuzzer.py
new file mode 100644
index 0000000..0916add
--- /dev/null
+++ b/fuzzers/machxo2/007-plc2_cemux/fuzzer.py
@@ -0,0 +1,32 @@
+from fuzzconfig import FuzzConfig
+import nonrouting
+import fuzzloops
+import nets
+import pytrellis
+import re
+
+cfg = FuzzConfig(job="PLC2REG", family="MachXO2", device="LCMXO2-1200HC", ncl="empty.ncl", tiles=["R10C11:PLC"])
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    cfg.setup()
+    empty_bitfile = cfg.build_design(cfg.ncl, {})
+    cfg.ncl = "cemux.ncl"
+
+    def per_slice(slicen):
+        def get_substs(cemux):
+            if cemux == "INV":
+                cemux = "CE:::CE=#INV"
+            if cemux == "0":
+                cemux = "1:::1=0"
+            return dict(slice=slicen, cemux=cemux)
+        nonrouting.fuzz_enum_setting(cfg, "SLICE{}.CEMUX".format(slicen), ["0", "1", "CE", "INV"],
+                                     lambda x: get_substs(cemux=x),
+                                     empty_bitfile, False)
+
+    fuzzloops.parallel_foreach(["A", "B", "C", "D"], per_slice)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/008-plc2_clkmux/clkmux.ncl b/fuzzers/machxo2/008-plc2_clkmux/clkmux.ncl
new file mode 100644
index 0000000..7404fa8
--- /dev/null
+++ b/fuzzers/machxo2/008-plc2_clkmux/clkmux.ncl
@@ -0,0 +1,250 @@
+::FROM-WRITER;
+// designname: top
+// Creation time stamp: 01/31/20  05:21:54
+design top
+{
+   device
+   {
+      architecture xo2c00;
+      device LCMXO2-1200HC;
+      package QFN32;
+      performance "6";
+   }
+
+   // Writing 12 properties.
+   property
+   {
+      LSE_CPS_MAP_FILE string "xxx_lse_sign_file";
+      "PINNAME:0" string "clk";
+      "PINNAME:1" string "d";
+      "PINNAME:2" string "r";
+      "PINNAME:3" string "s";
+      "PINNAME:4" string "q";
+      "PINTYPE:0" string "IN";
+      "PINTYPE:1" string "IN";
+      "PINTYPE:2" string "IN";
+      "PINTYPE:3" string "IN";
+      "PINTYPE:4" string "OUT";
+      "SIGNAME:PUR" string "VCC_net";
+   } // End of property list.
+
+   // The Design macro definitions.
+   // The Design macro instances.
+   // The Design Comps.
+   comp SLICE_0
+   {
+
+      // Writing 2 properties.
+      property
+      {
+         LSE_CPS_ID_1 string "REG0";
+         NGID0 long 2;
+      } // End of property list.
+
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:LOGIC "
+                 "REG0:::REGSET=SET:SD=0 "
+                 "Q0:Q "
+                 "GSR:ENABLED "
+                 "CLKMUX:${clkmux} "
+                 "CEMUX:1:::1=0 "
+                 "LSRMUX:LSR "
+                 "SRMODE:LSR_OVER_CE "
+                 "LSRONMUX:LSRMUX "
+                 "M0MUX:M0 "
+                 "REGMODE:FF ";
+         primitive REG0 ff;
+      }
+      site R10C6${c};
+   }
+   comp d
+   {
+
+      // Writing 3 properties.
+      property
+      {
+         "#%PAD%PINID" long 1;
+         LSE_CPS_ID_2 string "IOBUF";
+         NGID0 long 3;
+      } // End of property list.
+
+      logical
+      {
+         cellmodel-name PIO;
+         program "PADDI:PADDI "
+                 "IOBUF:::PULLMODE=DOWN,CLAMP=ON "
+                 "VREF:OFF "
+                 "PGMUX:INBUF "
+                 "INRDMUX:PGMUX ";
+         primitive IOBUF d_pad;
+         primitive PAD d;
+      }
+      site "10";
+   }
+   comp q
+   {
+
+      // Writing 3 properties.
+      property
+      {
+         "#%PAD%PINID" long 4;
+         LSE_CPS_ID_3 string "IOBUF";
+         NGID0 long 4;
+      } // End of property list.
+
+      logical
+      {
+         cellmodel-name PIO;
+         program "TRIMUX:PADDT:::PADDT=0 "
+                 "IOBUF:::PULLMODE=DOWN,DRIVE=8, \"
+                    "SLEWRATE=SLOW,HYSTERESIS=NA "
+                 "DATAMUX:PADDO "
+                 "VREF:OFF "
+                 "ODMUX:TRIMUX "
+                 "LVDSMUX:DATAMUX ";
+         primitive IOBUF q_pad;
+         primitive PAD q;
+      }
+      site "9";
+   }
+   comp r
+   {
+
+      // Writing 3 properties.
+      property
+      {
+         "#%PAD%PINID" long 2;
+         LSE_CPS_ID_5 string "IOBUF";
+         NGID0 long 6;
+      } // End of property list.
+
+      logical
+      {
+         cellmodel-name PIO;
+         program "PADDI:PADDI "
+                 "IOBUF:::PULLMODE=DOWN,CLAMP=ON "
+                 "VREF:OFF "
+                 "PGMUX:INBUF "
+                 "INRDMUX:PGMUX ";
+         primitive IOBUF r_pad;
+         primitive PAD r;
+      }
+      site "28";
+   }
+   comp s
+   {
+
+      // Writing 3 properties.
+      property
+      {
+         "#%PAD%PINID" long 3;
+         LSE_CPS_ID_6 string "IOBUF";
+         NGID0 long 7;
+      } // End of property list.
+
+      logical
+      {
+         cellmodel-name PIO;
+         program "PADDI:PADDI "
+                 "IOBUF:::PULLMODE=DOWN,CLAMP=ON "
+                 "VREF:OFF "
+                 "PGMUX:INBUF "
+                 "INRDMUX:PGMUX ";
+         primitive IOBUF s_pad;
+         primitive PAD s;
+      }
+      site "8";
+   }
+   comp GSR_INST
+   {
+
+      // Writing 2 properties.
+      property
+      {
+         LSE_CPS_ID_4 string "GSR";
+         NGID0 long 5;
+      } // End of property list.
+
+      logical
+      {
+         cellmodel-name GSR;
+         program "GSRMODE:ACTIVE_LOW "
+                 "SYNCMODE:ASYNC ";
+      }
+      site GSR;
+   }
+   // The Design Signals.
+   signal d_c
+   {
+      signal-pins
+         // drivers
+         (d, PADDI),
+         // loads
+         (SLICE_0, M0);
+      route
+         R10C6_H00L0200.R10C6_M0,
+         R10C6_V02N0301.R10C6_H00L0200,
+         R11C6_JQ3.R10C6_V02N0301,
+         R12C6_JDID.R11C6_JQ3,
+         R10C6_M0.R10C6_M0_SLICE,
+         R12C6_JPADDID_PIO.R12C6_JDID;
+   }
+   signal s_c
+   {
+      signal-pins
+         // drivers
+         (s, PADDI),
+         // loads
+         (SLICE_0, LSR);
+      route
+         R10C6_V00T0100.R10C6_LSR0,
+         R10C5_H02E0401.R10C6_V00T0100,
+         R10C4_V01N0101.R10C5_H02E0401,
+         R11C4_JQ2.R10C4_V01N0101,
+         R12C4_JDIC.R11C4_JQ2,
+         R10C6_LSR0.R10C6_LSR0_SLICE,
+         R12C4_JPADDIC_PIO.R12C4_JDIC;
+   }
+   signal q_c
+   {
+
+      // Writing 1 properties.
+      property
+      {
+         TW_IS_CONST_SIG boolean true;
+      } // End of property list.
+
+      signal-pins
+         // drivers
+         (SLICE_0, Q0),
+         // loads
+         (q, PADDO);
+      route
+         R11C6_V02N0701.R11C6_JA2,
+         R11C6_V02N0701.R11C6_V02S0700,
+         R10C6_V01S0100.R11C6_V02S0700,
+         R10C6_Q0.R10C6_V01S0100,
+         R11C6_JA2.R12C6_JPADDOC,
+         R12C6_JPADDOC.R12C6_PADDOC_PIO,
+         R10C6_Q0_SLICE.R10C6_Q0;
+   }
+   signal r_c
+   {
+      signal-pins
+         // drivers
+         (r, PADDI),
+         // loads
+         (GSR_INST, GSR);
+      route
+         R1C6_H00R0300.R1C6_JC4,
+         R1C6_V02S0101.R1C6_H00R0300,
+         R1C6_V02N0100.R1C6_V02S0101,
+         R1C9_H06W0103.R1C6_V02N0100,
+         R1C12_JQ2.R1C9_H06W0103,
+         R0C12_JDIC.R1C12_JQ2,
+         R1C6_JC4.R1C4_JGSR_GSR,
+         R0C12_JPADDIC_PIO.R0C12_JDIC;
+   }
+}
diff --git a/fuzzers/machxo2/008-plc2_clkmux/empty.ncl b/fuzzers/machxo2/008-plc2_clkmux/empty.ncl
new file mode 100644
index 0000000..11b72ab
--- /dev/null
+++ b/fuzzers/machxo2/008-plc2_clkmux/empty.ncl
@@ -0,0 +1,12 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+}
diff --git a/fuzzers/machxo2/008-plc2_clkmux/fuzzer.py b/fuzzers/machxo2/008-plc2_clkmux/fuzzer.py
new file mode 100644
index 0000000..11b998a
--- /dev/null
+++ b/fuzzers/machxo2/008-plc2_clkmux/fuzzer.py
@@ -0,0 +1,38 @@
+from fuzzconfig import FuzzConfig
+import nonrouting
+import fuzzloops
+import nets
+import pytrellis
+import re
+
+cfg = FuzzConfig(job="PLC2REG", family="MachXO2", device="LCMXO2-1200HC", ncl="empty.ncl", tiles=["R10C6:PLC"])
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    cfg.setup()
+    empty_bitfile = cfg.build_design(cfg.ncl, {})
+    cfg.ncl = "clkmux.ncl"
+
+    def per_clk(clkn):
+        slices = { "0" : "A",
+                   "1" : "B",
+                   "2" : "C",
+                   "3" : "D"
+                 }
+
+        def get_substs(clkmux):
+            if clkmux == "INV":
+                clkmux = "CLK:::CLK=#INV"
+            if clkmux == "1":
+                clkmux = "0:::0=1"
+            return dict(c=slices[clkn], clkmux=clkmux)
+        nonrouting.fuzz_enum_setting(cfg, "CLK{}.CLKMUX".format(clkn), ["CLK", "INV", "0", "1"],
+                                     lambda x: get_substs(clkmux=x),
+                                     empty_bitfile, True)
+
+    fuzzloops.parallel_foreach(["0", "1", "2", "3"], per_clk)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/009-plc2_lsr/empty.ncl b/fuzzers/machxo2/009-plc2_lsr/empty.ncl
new file mode 100644
index 0000000..11b72ab
--- /dev/null
+++ b/fuzzers/machxo2/009-plc2_lsr/empty.ncl
@@ -0,0 +1,12 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+}
diff --git a/fuzzers/machxo2/009-plc2_lsr/fuzzer.py b/fuzzers/machxo2/009-plc2_lsr/fuzzer.py
new file mode 100644
index 0000000..3dd6127
--- /dev/null
+++ b/fuzzers/machxo2/009-plc2_lsr/fuzzer.py
@@ -0,0 +1,41 @@
+from fuzzconfig import FuzzConfig
+import nonrouting
+import fuzzloops
+import nets
+import pytrellis
+import re
+
+cfg = FuzzConfig(job="PLC2REG", family="MachXO2", device="LCMXO2-1200HC", ncl="empty.ncl", tiles=["R10C6:PLC"])
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    cfg.setup()
+    empty_bitfile = cfg.build_design(cfg.ncl, {})
+    cfg.ncl = "lsr.ncl"
+
+    def per_lsr(lsrn):
+        slices = { "0" : "A",
+                   "1" : "B",
+                   "2" : "C",
+                   "3" : "D"
+                 }
+
+        def get_substs(lsrmux="LSR", srmode="LSR_OVER_CE", lsronmux="0"):
+            if lsrmux == "INV":
+                lsrmux = "LSR:::LSR=#INV"
+            return dict(s=slices[lsrn], l=lsrn, lsrmux=lsrmux, srmode=srmode, lsronmux=lsronmux)
+        nonrouting.fuzz_enum_setting(cfg, "LSR{}.LSRMUX".format(lsrn), ["LSR", "INV"],
+                                     lambda x: get_substs(lsrmux=x),
+                                     empty_bitfile, True)
+        nonrouting.fuzz_enum_setting(cfg, "LSR{}.SRMODE".format(lsrn), ["LSR_OVER_CE", "ASYNC"],
+                                     lambda x: get_substs(srmode=x),
+                                     empty_bitfile, True)
+        nonrouting.fuzz_enum_setting(cfg, "LSR{}.LSRONMUX".format(lsrn), ["0", "LSRMUX"],
+                                     lambda x: get_substs(lsronmux=x),
+                                     empty_bitfile, True)
+    fuzzloops.parallel_foreach(["0", "1", "2", "3"], per_lsr)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/009-plc2_lsr/lsr.ncl b/fuzzers/machxo2/009-plc2_lsr/lsr.ncl
new file mode 100644
index 0000000..8bd295a
--- /dev/null
+++ b/fuzzers/machxo2/009-plc2_lsr/lsr.ncl
@@ -0,0 +1,58 @@
+::FROM-WRITER;
+design top
+{
+    device
+    {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+    }
+
+   comp SLICE_0
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:LOGIC "
+                 "REG0:::REGSET=RESET:SD=0 "
+                 "Q0:Q "
+                 "GSR:DISABLED "
+                 "CLKMUX:CLK "
+                 "CEMUX:CE "
+                 "LSRMUX:${lsrmux} "
+                 "SRMODE:${srmode} "
+                 "LSRONMUX:${lsronmux} "
+                 "M0MUX:M0 ";
+         primitive REG0 q_6;
+      }
+      site R10C6${s};
+   }
+
+   comp lsr
+   {
+      logical
+      {
+         cellmodel-name PIO;
+         program "PADDI:PADDI "
+                 "IOBUF:::PULLMODE=DOWN,CLAMP=ON "
+                 "VREF:OFF "
+                 "PGMUX:INBUF "
+                 "INRDMUX:PGMUX ";
+         primitive IOBUF lsr_pad;
+         primitive PAD lsr;
+      }
+      site "13";
+   }
+
+   signal lsrc_c
+   {
+      signal-pins
+         // drivers
+         (lsr, PADDI),
+         // loads
+         (SLICE_0, LSR);
+      route
+         R10C6_LSR${l}.R10C6_LSR${l}_SLICE;
+   }
+}
diff --git a/fuzzers/machxo2/010-plc2_modes/empty.ncl b/fuzzers/machxo2/010-plc2_modes/empty.ncl
new file mode 100644
index 0000000..11b72ab
--- /dev/null
+++ b/fuzzers/machxo2/010-plc2_modes/empty.ncl
@@ -0,0 +1,12 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+}
diff --git a/fuzzers/machxo2/010-plc2_modes/fuzzer.py b/fuzzers/machxo2/010-plc2_modes/fuzzer.py
new file mode 100644
index 0000000..0eabba4
--- /dev/null
+++ b/fuzzers/machxo2/010-plc2_modes/fuzzer.py
@@ -0,0 +1,34 @@
+from fuzzconfig import FuzzConfig
+import nonrouting
+import fuzzloops
+import nets
+import pytrellis
+import re
+
+cfg = FuzzConfig(job="PLC2MODE", family="MachXO2", device="LCMXO2-1200HC", ncl="empty.ncl", tiles=["R10C11:PLC"])
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    cfg.setup()
+    empty_bitfile = cfg.build_design(cfg.ncl, {})
+    cfg.ncl = "modes.ncl"
+
+    def per_slice(slicen):
+        def get_substs(mode):
+            return dict(slice=slicen, mode=mode)
+        if slicen == "A" or slicen == "B":
+            modes = ["LOGIC", "CCU2", "DPRAM"]
+        elif slicen == "C":
+            modes = ["LOGIC", "CCU2", "RAMW"]
+        else:
+            modes = ["LOGIC", "CCU2"]
+        nonrouting.fuzz_enum_setting(cfg, "SLICE{}.MODE".format(slicen), modes,
+                                     lambda x: get_substs(mode=x),
+                                     empty_bitfile, False)
+
+    fuzzloops.parallel_foreach(["A", "B", "C", "D"], per_slice)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/010-plc2_modes/modes.ncl b/fuzzers/machxo2/010-plc2_modes/modes.ncl
new file mode 100644
index 0000000..8780d07
--- /dev/null
+++ b/fuzzers/machxo2/010-plc2_modes/modes.ncl
@@ -0,0 +1,23 @@
+::FROM-WRITER;
+design top
+{
+    device
+    {
+        architecture xo2c00;
+        device LCMXO2-1200HC;
+        package QFN32;
+        performance "6";
+    }
+
+   comp SLICE_0
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:${mode} ";
+         primitive REG0 q_6;
+      }
+      site R10C11${slice};
+   }
+
+}
diff --git a/fuzzers/machxo2/011-ccu2_inject/ccu2.ncl b/fuzzers/machxo2/011-ccu2_inject/ccu2.ncl
new file mode 100644
index 0000000..bbb520f
--- /dev/null
+++ b/fuzzers/machxo2/011-ccu2_inject/ccu2.ncl
@@ -0,0 +1,26 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+   comp SLICE_0
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:CCU2 "
+                 "CCU2::S0=0x9009,S1=0x9009:INJECT1_0=${ij1_0}, \"
+                    "INJECT1_1=${ij1_1} "
+                 "FCO:FCO ";
+         primitive CCU2 "CCU";
+      }
+      site R10C11${slice};
+   }
+
+}
diff --git a/fuzzers/machxo2/011-ccu2_inject/empty.ncl b/fuzzers/machxo2/011-ccu2_inject/empty.ncl
new file mode 100644
index 0000000..11b72ab
--- /dev/null
+++ b/fuzzers/machxo2/011-ccu2_inject/empty.ncl
@@ -0,0 +1,12 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+}
diff --git a/fuzzers/machxo2/011-ccu2_inject/fuzzer.py b/fuzzers/machxo2/011-ccu2_inject/fuzzer.py
new file mode 100644
index 0000000..2b68724
--- /dev/null
+++ b/fuzzers/machxo2/011-ccu2_inject/fuzzer.py
@@ -0,0 +1,30 @@
+from fuzzconfig import FuzzConfig
+import nonrouting
+import fuzzloops
+import nets
+import pytrellis
+import re
+
+cfg = FuzzConfig(job="PLC2MODE", family="MachXO2", device="LCMXO2-1200HC", ncl="empty.ncl", tiles=["R10C11:PLC"])
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    cfg.setup()
+    empty_bitfile = cfg.build_design(cfg.ncl, {})
+    cfg.ncl = "ccu2.ncl"
+
+    def per_slice(slicen):
+        def get_substs(ij1_0="YES", ij1_1="YES"):
+            return dict(slice=slicen, ij1_0=ij1_0, ij1_1=ij1_1)
+        nonrouting.fuzz_enum_setting(cfg, "SLICE{}.CCU2.INJECT1_0".format(slicen), ["YES", "NO"],
+                                     lambda x: get_substs(ij1_0=x),
+                                     empty_bitfile, True)
+        nonrouting.fuzz_enum_setting(cfg, "SLICE{}.CCU2.INJECT1_1".format(slicen), ["YES", "NO"],
+                                     lambda x: get_substs(ij1_1=x),
+                                     empty_bitfile, True)
+    fuzzloops.parallel_foreach(["A", "B", "C", "D"], per_slice)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/012-ccu2_nmux/ccu2.ncl b/fuzzers/machxo2/012-ccu2_nmux/ccu2.ncl
new file mode 100644
index 0000000..12a8af9
--- /dev/null
+++ b/fuzzers/machxo2/012-ccu2_nmux/ccu2.ncl
@@ -0,0 +1,25 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+   comp SLICE_0
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:CCU2 "
+                 "CCU2::S0=0xfaaa,S1=0xfaaa${muxcfg}"
+                 "FCO:FCO ";
+         primitive CCU2 "CCU";
+      }
+      site R10C11${slice};
+   }
+
+}
diff --git a/fuzzers/machxo2/012-ccu2_nmux/empty.ncl b/fuzzers/machxo2/012-ccu2_nmux/empty.ncl
new file mode 100644
index 0000000..11b72ab
--- /dev/null
+++ b/fuzzers/machxo2/012-ccu2_nmux/empty.ncl
@@ -0,0 +1,12 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+}
diff --git a/fuzzers/machxo2/012-ccu2_nmux/fuzzer.py b/fuzzers/machxo2/012-ccu2_nmux/fuzzer.py
new file mode 100644
index 0000000..dedfc85
--- /dev/null
+++ b/fuzzers/machxo2/012-ccu2_nmux/fuzzer.py
@@ -0,0 +1,39 @@
+from fuzzconfig import FuzzConfig
+import nonrouting
+import fuzzloops
+import nets
+import pytrellis
+import re
+
+# At present, I don't believe this affects any bits. Keeping this around
+# just in case I figure out I'm wrong...
+
+cfg = FuzzConfig(job="PLC2NMUX", family="MachXO2", device="LCMXO2-1200HC", ncl="empty.ncl", tiles=["R10C11:PLC"])
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    cfg.setup()
+    empty_bitfile = cfg.build_design(cfg.ncl, {})
+    cfg.ncl = "ccu2.ncl"
+
+    def per_slice(slicen):
+        def get_substs(sig="A0", conn="A0"):
+            subs = {"slice": slicen}
+            if conn == "0":
+                # subs["muxcfg"] = "::{}=0".format(sig)
+                subs["muxcfg"] = "::A0=0,A1=0,B0=0,B1=0,C0=0,C1=0,D0=0,D1=0"
+            else:
+                subs["muxcfg"] = ""
+            return subs
+        # for sig in ["A0", "A1", "B0", "B1", "C0", "C1", "D0", "D1"]:
+        for sig in ["A0"]:
+            nonrouting.fuzz_enum_setting(cfg, "SLICE{}.{}MUX".format(slicen, sig), [sig, "0"],
+                                         lambda x: get_substs(sig=sig, conn=x),
+                                         empty_bitfile, False)
+    # fuzzloops.parallel_foreach(["A", "B", "C", "D"], per_slice)
+    fuzzloops.parallel_foreach(["A"], per_slice)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/013-plc2_mkmux/empty.ncl b/fuzzers/machxo2/013-plc2_mkmux/empty.ncl
new file mode 100644
index 0000000..11b72ab
--- /dev/null
+++ b/fuzzers/machxo2/013-plc2_mkmux/empty.ncl
@@ -0,0 +1,12 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+}
diff --git a/fuzzers/machxo2/013-plc2_mkmux/fuzzer.py b/fuzzers/machxo2/013-plc2_mkmux/fuzzer.py
new file mode 100644
index 0000000..9db7f55
--- /dev/null
+++ b/fuzzers/machxo2/013-plc2_mkmux/fuzzer.py
@@ -0,0 +1,32 @@
+from fuzzconfig import FuzzConfig
+import nonrouting
+import fuzzloops
+import nets
+import pytrellis
+import re
+
+# No evidence this affects any bits.
+
+cfg = FuzzConfig(job="PLC2MKMUX", family="MachXO2", device="LCMXO2-1200HC", ncl="empty.ncl", tiles=["R10C11:PLC"])
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    cfg.setup()
+    empty_bitfile = cfg.build_design(cfg.ncl, {})
+    cfg.ncl = "mkmux.ncl"
+
+    def per_slice(slicen):
+        def get_substs(m0mux="M0", m1mux="M1"):
+            return dict(slice=slicen, m0mux=m0mux, m1mux=m1mux)
+        nonrouting.fuzz_enum_setting(cfg, "SLICE{}.M0MUX".format(slicen), ["M0", "0"],
+                                     lambda x: get_substs(m0mux=x),
+                                     empty_bitfile, False)
+        nonrouting.fuzz_enum_setting(cfg, "SLICE{}.M1MUX".format(slicen), ["M1", "0"],
+                                     lambda x: get_substs(m1mux=x),
+                                     empty_bitfile, False)
+    fuzzloops.parallel_foreach(["A", "B", "C", "D"], per_slice)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/013-plc2_mkmux/mkmux.ncl b/fuzzers/machxo2/013-plc2_mkmux/mkmux.ncl
new file mode 100644
index 0000000..c553eb0
--- /dev/null
+++ b/fuzzers/machxo2/013-plc2_mkmux/mkmux.ncl
@@ -0,0 +1,32 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+   comp SLICE_0
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:LOGIC "
+                 "REG0:::REGSET=RESET:SD=0 "
+                 "Q0:Q "
+                 "GSR:DISABLED "
+                 "CLKMUX:CLK "
+                 "CEMUX:1 "
+                 "LSRMUX:LSR "
+                 "SRMODE:LSR_OVER_CE "
+                 "M0MUX:${m0mux} "
+                 "M1MUX:${m1mux} ";
+         primitive REG0 q_6;
+      }
+      site R10C11${slice};
+   }
+
+}
diff --git a/fuzzers/machxo2/014-plc2_wremux/empty.ncl b/fuzzers/machxo2/014-plc2_wremux/empty.ncl
new file mode 100644
index 0000000..11b72ab
--- /dev/null
+++ b/fuzzers/machxo2/014-plc2_wremux/empty.ncl
@@ -0,0 +1,12 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+}
diff --git a/fuzzers/machxo2/014-plc2_wremux/fuzzer.py b/fuzzers/machxo2/014-plc2_wremux/fuzzer.py
new file mode 100644
index 0000000..cff8b36
--- /dev/null
+++ b/fuzzers/machxo2/014-plc2_wremux/fuzzer.py
@@ -0,0 +1,33 @@
+from fuzzconfig import FuzzConfig
+import nonrouting
+import fuzzloops
+import nets
+import pytrellis
+import re
+
+cfg = FuzzConfig(job="PLC2WRE", family="MachXO2", device="LCMXO2-1200HC", ncl="empty.ncl", tiles=["R10C11:PLC"])
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    cfg.setup()
+    empty_bitfile = cfg.build_design(cfg.ncl, {})
+    cfg.ncl = "wremux.ncl"
+
+    def per_slice(slicen):
+        def get_substs(wremux):
+            if wremux == "INV":
+                wremux = "WRE:::WRE=#INV"
+            if wremux == "1":
+                wremux = "0:::0=1"
+            return dict(slice=slicen, wremux=wremux)
+        nonrouting.fuzz_enum_setting(cfg, "SLICE{}.WREMUX".format(slicen), ["0", "1", "WRE", "INV"],
+                                     lambda x: get_substs(wremux=x),
+                                     empty_bitfile, False)
+
+    # B also has a WREMUX signal, but the same bit as A controls it.
+    fuzzloops.parallel_foreach(["A"], per_slice)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/014-plc2_wremux/wremux.ncl b/fuzzers/machxo2/014-plc2_wremux/wremux.ncl
new file mode 100644
index 0000000..3eedbb6
--- /dev/null
+++ b/fuzzers/machxo2/014-plc2_wremux/wremux.ncl
@@ -0,0 +1,25 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+   comp SLICE_0
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:DPRAM "
+                 "DPRAM::DO0=0x0000,DO1=0x0000 "
+                 "WREMUX:${wremux} ";
+         primitive DPRAM q_6;
+      }
+      site R10C11${slice};
+   }
+
+}
diff --git a/fuzzers/machxo2/020-center-mux/center-mux.ncl b/fuzzers/machxo2/020-center-mux/center-mux.ncl
new file mode 100644
index 0000000..3f9d75c
--- /dev/null
+++ b/fuzzers/machxo2/020-center-mux/center-mux.ncl
@@ -0,0 +1,35 @@
+::FROM-WRITER;
+design top
+{
+    device
+    {
+        architecture xo2c00;
+        device LCMXO2-1200HC;
+        package QFN32;
+        performance "6";
+    }
+
+   comp SLICE_0
+      [,,,,A0,B0,D0,C0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,]
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:LOGIC "
+                 "K0::H0=0 "
+                 "F0:F ";
+         primitive K0 i3_4_lut;
+      }
+      site R2C2A;
+   }
+
+    signal q_c
+   {
+      signal-pins
+         // drivers
+         (SLICE_0, F0),
+         // loads
+         (SLICE_0, CLK);
+      ${route}
+   }
+}
diff --git a/fuzzers/machxo2/020-center-mux/fuzzer.py b/fuzzers/machxo2/020-center-mux/fuzzer.py
new file mode 100644
index 0000000..6b44032
--- /dev/null
+++ b/fuzzers/machxo2/020-center-mux/fuzzer.py
@@ -0,0 +1,54 @@
+from collections import defaultdict
+
+from fuzzconfig import FuzzConfig
+import interconnect
+import nets
+import pytrellis
+import re
+import mk_nets
+
+jobs = [
+        # Global mux connections. The relevant tiles were inferred from the
+        # center_mux experiment.
+        ("fixed", FuzzConfig(job="GLOBAL_MUX", family="MachXO2", device="LCMXO2-1200HC", ncl="center-mux.ncl",
+                  tiles=["CENTER9:CENTER8", "CENTER8:CENTER7", "CENTER7:CENTER6",
+                         "CENTER6:CENTER_EBR_CIB", "CENTER5:CENTER5"])),
+
+        # ("ignored", FuzzConfig(job="GLOBAL_MUX", family="MachXO2", device="LCMXO2-1200HC", ncl="center-mux.ncl",
+        #           tiles=["CENTER6:CENTER_EBR_CIB", "CENTER5:CENTER5", "CENTER4:CENTER4"])),
+
+        # Fixed connections within the global mux (as well as
+        # direction select).
+        ("muxed", FuzzConfig(job="GLOBAL_FIXED", family="MachXO2", device="LCMXO2-1200HC", ncl="center-mux.ncl",
+                  tiles=["CENTER6:CENTER_EBR_CIB"])),
+]
+
+
+def main():
+    pytrellis.load_database("../../../database")
+
+    for job in jobs:
+        net_id, cfg = job
+        cfg.setup()
+
+        netnames = mk_nets.nets[net_id]
+        interconnect.fuzz_interconnect_with_netnames(config=cfg, netnames=netnames,
+                                                     netname_filter_union=False,
+                                                     netdir_override=defaultdict(lambda : str("sink")),
+                                                     bias=1)
+
+    # TODO: R6C13_JA0 --> R6C13_JCE0_DCC. But TCL also claims
+    # R6C13_CLKI0_DCC --> R6C13_CLKO0_DCC (pseudo = 1). Contradiction?
+    # From talking to Dave: No it's not a contradiction. A
+    # config bit controls whether JCE0 has any effect.
+    # interconnect.fuzz_interconnect_with_netnames(config=cfg, netnames=["R6C13_CLKI0_DCC", "R6C13_CLKO0_DCC", "R6C13_JCE0_DCC"],
+    #                                              netname_filter_union=False,
+    #                                              netdir_override = {
+    #                                                 "R6C13_JCE0_DCC" : "sink",
+    #                                              },
+    #                                              full_mux_style=True,
+    #                                              bias=1)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/020-center-mux/mk_nets.py b/fuzzers/machxo2/020-center-mux/mk_nets.py
new file mode 100644
index 0000000..b7d0119
--- /dev/null
+++ b/fuzzers/machxo2/020-center-mux/mk_nets.py
@@ -0,0 +1,114 @@
+from nets import net_product
+
+templates = {
+    "fixed": [
+        # Some inputs to the center MUX are two nets concatenated together; the
+        # "first" net is a sink from locations throughout the FPGA, the
+        # "second" is the actual input to the center MUX. They should all
+        # become fixed connections, but fuzz _just_ in case.
+        (["R6C13_JPCLKCIBLLQ{0}",
+          "R6C13_JPCLKCIBLRQ{0}",
+          "R6C13_JPCLKCIBVIQB{0}",
+          "R6C13_JECLKCIBB{0}",
+          "R6C13_JECLKCIBT{0}",
+          "R6C13_JPCLKCIBVIQT{0}"], range(2)),
+        (["R6C13_JPCLKCIBMID{0}"], range(2, 4)),
+
+        # The "second" net of the concatenated inputs.
+        (["R6C13_PCLKCIBLLQ{0}",
+          "R6C13_PCLKCIBLRQ{0}",
+          "R6C13_PCLKCIBVIQB{0}",
+          "R6C13_ECLKCIBB{0}",
+          "R6C13_ECLKCIBT{0}",
+          "R6C13_PCLKCIBVIQT{0}"], range(2)),
+        (["R6C13_PCLKCIBMID{0}"], range(2, 4)),
+
+        # All other inputs which are _not_ concatenations of two nets.
+        (["R6C13_JSNETCIBMID{0}"], range(8)),
+        (["R6C13_JPCLKT2{0}",
+          "R6C13_JBCDIVX{0}",
+          "R6C13_JBCDIV1{0}",
+          "R6C13_JTCDIVX{0}",
+          "R6C13_JTCDIV1{0}",
+          "R6C13_JPCLKT0{0}",
+          "R6C13_JSNETCIBL{0}",
+          "R6C13_JSNETCIBT{0}",
+          "R6C13_JSNETCIBR{0}",
+          "R6C13_JSNETCIBB{0}"], range(2)),
+        (["R6C13_JLPLLCLK{0}"], range(4)),
+        (["R6C13_JPCLKT3{0}"], range(3)),
+        (["R6C13_JPCLKT10"], range(1)),
+
+        # Mux selects on the outputs.
+        (["R6C13_JCE{0}_DCC"], range(8)),
+        (["R6C13_JSEL{0}_DCM"], range(6, 8)),
+
+        # Unfortunately, same deal w/ concatenated nets also applies to the
+        # output.
+        # "second" is kinda a misnomer here- clock nets go through tristates,
+        # although TCL claims a fixed connection. There are multiple layers of
+        # fixed connections where the net names change on the outputs, so we
+        # just take care of all suspected fixed connections in one fell swoop.
+        #
+        # All the initial output connections are attached to muxes controlled
+        # by config bits, and thus are _not_ fixed.
+
+        # Global nets 6 and 7 are "MUX"ed twice- once to choose between two
+        # connections, another for clock enable (CEN). First net.
+        (["R6C13_CLK{0}_6_DCM",
+          "R6C13_CLK{0}_7_DCM"], range(2)),
+        (["R6C13_CLK{0}_0_ECLKBRIDGECS",
+          "R6C13_CLK{0}_1_ECLKBRIDGECS"], range(2)),
+
+        # Second net. Connects to R6C13_CLKI{6,7}_DCC.
+        (["R6C13_DCMOUT{0}_DCM"], range(6, 8)),
+        (["R6C13_JECSOUT{0}_ECLKBRIDGECS"], range(2)),
+
+        # All CLKO connections are gated by a MUX connected to fabric; defaults
+        # to pass-through in configuration bits.
+        (["R6C13_CLKI{0}_DCC"], range(8)),
+        (["R6C13_CLKO{0}_DCC"], range(8)),
+    ],
+
+    "muxed": [
+        # And also include muxed connections.
+        (["R6C13_VSRX0{0}00"], range(8)),
+        (["R6C13_VPRXCLKI{0}"], range(6)),
+        (["R6C13_VPRXCLKI6{0}",
+          "R6C13_VPRXCLKI7{0}"], range(2)),
+        (["R6C13_EBRG0CLK{0}",
+          "R6C13_EBRG1CLK{0}"], range(2))
+    ],
+
+    "ignore": [
+        # CLK{0,1}_{0,1}_ECLKBRIDGECS do not in fact drive
+        # R6C13_JSEL{0}_ECLKBRIDGECS. R6C13_JSEL{0}_ECLKBRIDGECS is driven from
+        # elsewhere. Ignore for now.
+        (["R6C13_JSEL{0}_ECLKBRIDGECS"], range(2))
+    ],
+}
+
+nets = {k: [] for k in templates.keys()}
+
+for (var, temp) in templates.items():
+    for (n, r) in temp:
+        for p in net_product(n, r):
+            nets[var].append(p)
+
+
+def main():
+    for k, v in nets.items():
+        print("{}: {}".format(k, v))
+        print("")
+
+    sum = 0
+    for k in nets.keys():
+        s = len(nets[k])
+        print("total {}: {}".format(k, s))
+        sum = sum + s
+
+    print("grand total: {}".format(sum))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/030-cib_cfg0/cibroute.ncl b/fuzzers/machxo2/030-cib_cfg0/cibroute.ncl
new file mode 100644
index 0000000..7e7370b
--- /dev/null
+++ b/fuzzers/machxo2/030-cib_cfg0/cibroute.ncl
@@ -0,0 +1,35 @@
+::FROM-WRITER;
+design top
+{
+    device
+    {
+        architecture xo2c00;
+        device LCMXO2-1200HC;
+        package QFN32;
+        performance "6";
+    }
+
+   comp SLICE_0
+      [,,,,A0,B0,D0,C0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,]
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:LOGIC "
+                 "K0::H0=0 "
+                 "F0:F ";
+         primitive K0 i3_4_lut;
+      }
+      site R2C2A;
+   }
+
+    signal q_c
+   {
+      signal-pins
+         // drivers
+         (SLICE_0, F0),
+         // loads
+         (SLICE_0, A0);
+      ${route}
+   }
+}
diff --git a/fuzzers/machxo2/030-cib_cfg0/fuzzer.py b/fuzzers/machxo2/030-cib_cfg0/fuzzer.py
new file mode 100644
index 0000000..0175fa0
--- /dev/null
+++ b/fuzzers/machxo2/030-cib_cfg0/fuzzer.py
@@ -0,0 +1,38 @@
+from collections import defaultdict
+
+from fuzzconfig import FuzzConfig
+import interconnect
+import nets
+import pytrellis
+import re
+
+cfg = FuzzConfig(job="CIBCFG0ROUTE", family="MachXO2", device="LCMXO2-1200HC", ncl="cibroute.ncl", tiles=["CIB_R1C4:CIB_CFG0"])
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    cfg.setup()
+
+    span1_re = re.compile(r'R\d+C\d+_[VH]01[NESWTLBR]\d{4}')
+
+    def nn_filter(net, netnames):
+        """ Match nets that are: in the tile according to Tcl, global nets, or span-1 nets that are accidentally
+        left out by Tcl"""
+        return ((net in netnames or span1_re.match(net)) and nets.is_cib(net)) or nets.is_global(net)
+
+    def fc_filter(arc, netnames):
+        """ Ignore connections between two general routing nets. These are edge buffers which vary based on location
+        and must be excluded from the CIB database.
+        """
+        return not (nets.general_routing_re.match(arc[0]) and nets.general_routing_re.match(arc[1]))
+    interconnect.fuzz_interconnect(config=cfg, location=(1, 4),
+                                   netname_predicate=nn_filter,
+                                   fc_predicate=fc_filter,
+                                   netname_filter_union=True,
+                                   enable_span1_fix=True,
+                                   netdir_override=defaultdict(lambda : str("ignore")),
+                                   bias=1)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/031-cib_cfg1/cibroute.ncl b/fuzzers/machxo2/031-cib_cfg1/cibroute.ncl
new file mode 100644
index 0000000..7e7370b
--- /dev/null
+++ b/fuzzers/machxo2/031-cib_cfg1/cibroute.ncl
@@ -0,0 +1,35 @@
+::FROM-WRITER;
+design top
+{
+    device
+    {
+        architecture xo2c00;
+        device LCMXO2-1200HC;
+        package QFN32;
+        performance "6";
+    }
+
+   comp SLICE_0
+      [,,,,A0,B0,D0,C0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,]
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:LOGIC "
+                 "K0::H0=0 "
+                 "F0:F ";
+         primitive K0 i3_4_lut;
+      }
+      site R2C2A;
+   }
+
+    signal q_c
+   {
+      signal-pins
+         // drivers
+         (SLICE_0, F0),
+         // loads
+         (SLICE_0, A0);
+      ${route}
+   }
+}
diff --git a/fuzzers/machxo2/031-cib_cfg1/fuzzer.py b/fuzzers/machxo2/031-cib_cfg1/fuzzer.py
new file mode 100644
index 0000000..5e70b6e
--- /dev/null
+++ b/fuzzers/machxo2/031-cib_cfg1/fuzzer.py
@@ -0,0 +1,38 @@
+from collections import defaultdict
+
+from fuzzconfig import FuzzConfig
+import interconnect
+import nets
+import pytrellis
+import re
+
+cfg = FuzzConfig(job="CIBCFG1ROUTE", family="MachXO2", device="LCMXO2-1200HC", ncl="cibroute.ncl", tiles=["CIB_R1C5:CIB_CFG1"])
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    cfg.setup()
+
+    span1_re = re.compile(r'R\d+C\d+_[VH]01[NESWTLBR]\d{4}')
+
+    def nn_filter(net, netnames):
+        """ Match nets that are: in the tile according to Tcl, global nets, or span-1 nets that are accidentally
+        left out by Tcl"""
+        return ((net in netnames or span1_re.match(net)) and nets.is_cib(net)) or nets.is_global(net)
+
+    def fc_filter(arc, netnames):
+        """ Ignore connections between two general routing nets. These are edge buffers which vary based on location
+        and must be excluded from the CIB database.
+        """
+        return not (nets.general_routing_re.match(arc[0]) and nets.general_routing_re.match(arc[1]))
+    interconnect.fuzz_interconnect(config=cfg, location=(1, 5),
+                                   netname_predicate=nn_filter,
+                                   fc_predicate=fc_filter,
+                                   netname_filter_union=True,
+                                   enable_span1_fix=True,
+                                   netdir_override=defaultdict(lambda : str("ignore")),
+                                   bias=1)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/032-copy-cib_cfg0/fuzzer.py b/fuzzers/machxo2/032-copy-cib_cfg0/fuzzer.py
new file mode 100644
index 0000000..1c1cdeb
--- /dev/null
+++ b/fuzzers/machxo2/032-copy-cib_cfg0/fuzzer.py
@@ -0,0 +1,12 @@
+import dbcopy
+import pytrellis
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    # dbcopy.dbcopy("MachXO2", "LCMXO2-1200HC", "CIB_CFG0", "CIB_PIC_T0")
+    dbcopy.dbcopy("MachXO2", "LCMXO2-1200HC", "CIB_CFG0", "CIB_PIC_T_DUMMY")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/034-cib_ebrn/cibroute.ncl b/fuzzers/machxo2/034-cib_ebrn/cibroute.ncl
new file mode 100644
index 0000000..7e7370b
--- /dev/null
+++ b/fuzzers/machxo2/034-cib_ebrn/cibroute.ncl
@@ -0,0 +1,35 @@
+::FROM-WRITER;
+design top
+{
+    device
+    {
+        architecture xo2c00;
+        device LCMXO2-1200HC;
+        package QFN32;
+        performance "6";
+    }
+
+   comp SLICE_0
+      [,,,,A0,B0,D0,C0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,]
+   {
+      logical
+      {
+         cellmodel-name SLICE;
+         program "MODE:LOGIC "
+                 "K0::H0=0 "
+                 "F0:F ";
+         primitive K0 i3_4_lut;
+      }
+      site R2C2A;
+   }
+
+    signal q_c
+   {
+      signal-pins
+         // drivers
+         (SLICE_0, F0),
+         // loads
+         (SLICE_0, A0);
+      ${route}
+   }
+}
diff --git a/fuzzers/machxo2/034-cib_ebrn/fuzzer.py b/fuzzers/machxo2/034-cib_ebrn/fuzzer.py
new file mode 100644
index 0000000..d763463
--- /dev/null
+++ b/fuzzers/machxo2/034-cib_ebrn/fuzzer.py
@@ -0,0 +1,68 @@
+from collections import defaultdict
+
+from fuzzconfig import FuzzConfig
+import interconnect
+import nets
+import pytrellis
+import re
+import argparse
+
+jobs = [
+        {
+            "cfg": FuzzConfig(job="CIBEBR0ROUTE", family="MachXO2", device="LCMXO2-1200HC",
+                        ncl="cibroute.ncl", tiles=["CIB_R6C10:CIB_EBR0"]),
+            "location": (6, 10),
+            "nn_filter_extra": []
+        },
+        {
+            "cfg": FuzzConfig(job="CIBEBR0END0ROUTE", family="MachXO2", device="LCMXO2-1200HC",
+                        ncl="cibroute.ncl", tiles=["CIB_R6C1:CIB_EBR0_END0"]),
+            "location": (6, 1),
+            "nn_filter_extra": ["G_HPBX0100", "G_HPBX0500"]
+        },
+        {
+            "cfg": FuzzConfig(job="CIBEBR2END0ROUTE", family="MachXO2", device="LCMXO2-1200HC",
+                        ncl="cibroute.ncl", tiles=["CIB_R6C22:CIB_EBR2_END0"]),
+            "location": (6, 22),
+            "nn_filter_extra": []
+        },
+]
+
+
+def main(args):
+    pytrellis.load_database("../../../database")
+    for job in [jobs[i] for i in args.ids]:
+        cfg = job["cfg"]
+        cfg.setup()
+
+        span1_re = re.compile(r'R\d+C\d+_[VH]01[NESWTLBR]\d{4}')
+
+        def nn_filter(net, netnames):
+            """I want to handle global nets that are associated with this
+            tile manually; any matching nets are filtered out."""
+            if net in job["nn_filter_extra"]:
+                return False
+
+            """ Match nets that are: in the tile according to Tcl, global nets, or span-1 nets that are accidentally
+            left out by Tcl"""
+            return ((net in netnames or span1_re.match(net)) and nets.is_cib(net)) or nets.is_global(net)
+
+        def fc_filter(arc, netnames):
+            """ Ignore connections between two general routing nets. These are edge buffers which vary based on location
+            and must be excluded from the CIB database.
+            """
+            return not (nets.general_routing_re.match(arc[0]) and nets.general_routing_re.match(arc[1]))
+        interconnect.fuzz_interconnect(config=cfg, location=job["location"],
+                                       netname_predicate=nn_filter,
+                                       fc_predicate=fc_filter,
+                                       netname_filter_union=True,
+                                       enable_span1_fix=True,
+                                       netdir_override=defaultdict(lambda : str("ignore")),
+                                       bias=1)
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="CIB_EBRn Fuzzer.")
+    parser.add_argument(dest="ids", metavar="N", type=int, nargs="*",
+                    default=range(0, len(jobs)), help="Job (indices) to run.")
+    args = parser.parse_args()
+    main(args)
diff --git a/fuzzers/machxo2/035-copy-cib_ebr0/fuzzer.py b/fuzzers/machxo2/035-copy-cib_ebr0/fuzzer.py
new file mode 100644
index 0000000..43b9f60
--- /dev/null
+++ b/fuzzers/machxo2/035-copy-cib_ebr0/fuzzer.py
@@ -0,0 +1,15 @@
+import dbcopy
+import pytrellis
+
+# Based on prior fuzzing that wasn't committed, conjecture that CIB_EBR
+# 0,1,2, and DUMMY have the same layout.
+
+def main():
+    pytrellis.load_database("../../../database")
+    dbcopy.dbcopy("MachXO2", "LCMXO2-1200HC", "CIB_EBR0", "CIB_EBR1")
+    dbcopy.dbcopy("MachXO2", "LCMXO2-1200HC", "CIB_EBR0", "CIB_EBR2")
+    dbcopy.dbcopy("MachXO2", "LCMXO2-1200HC", "CIB_EBR0", "CIB_EBR_DUMMY")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/050-pio_routing/fuzzer.py b/fuzzers/machxo2/050-pio_routing/fuzzer.py
new file mode 100644
index 0000000..56e2552
--- /dev/null
+++ b/fuzzers/machxo2/050-pio_routing/fuzzer.py
@@ -0,0 +1,139 @@
+from collections import defaultdict
+from itertools import product
+
+from fuzzconfig import FuzzConfig
+import interconnect
+import nets
+import pytrellis
+import re
+import argparse
+
+import isptcl
+import mk_nets
+
+span1_re = re.compile(r'R\d+C\d+_[VH]01[NESWTLBR]\d{4}')
+jofx_re = re.compile(r'R\d+C\d+_JOFX\d')
+def nn_filter(net, netnames):
+    """ Match nets that are: in the tile according to Tcl, global nets, or span-1 nets that are accidentally
+    left out by Tcl"""
+    return net in netnames or nets.is_global(net) or span1_re.match(net)
+
+# JOFX source connections are conjectured to not go to anything.
+def fc_filter(arc, netnames):
+    return not jofx_re.match(arc[0])
+
+# Bank of None means that the I/O connections are in another tile.
+jobs = [
+        {
+           "pos" : [(12, 11)],
+           "cfg" : FuzzConfig(job="PIOROUTEB", family="MachXO2", device="LCMXO2-1200HC", ncl="pioroute.ncl",
+                                  tiles=["PB11:PIC_B0"]),
+           "missing_nets" : None,
+           "bank" : "B",
+        },
+        {
+           "pos" : [(11, 11)],
+           "cfg" : FuzzConfig(job="PIOROUTEB_CIB", family="MachXO2", device="LCMXO2-1200HC", ncl="pioroute.ncl",
+                                  tiles=["CIB_R11C11:CIB_PIC_B0"]),
+            # A bug in the span1 fix prevents span1 nets from being included.
+            # Just fuzz manually for now.
+           "missing_nets" : ["R10C11_V01N0001", "R10C11_V01N0101"],
+           "bank" : None,
+        },
+        {
+           "pos" : [(10, 1)],
+           "cfg" : FuzzConfig(job="PIOROUTEL", family="MachXO2", device="LCMXO2-1200HC", ncl="pioroute.ncl",
+                                  tiles=["PL10:PIC_L0"]),
+           "missing_nets" : None,
+           "bank" : "L"
+        },
+
+        # Probably the same thing as PIC_L0 plus some additional fixed connections?
+        {
+           "pos" : [(11, 1)],
+           "cfg" : FuzzConfig(job="PIOROUTELLC0", family="MachXO2", device="LCMXO2-1200HC", ncl="pioroute.ncl",
+                                  tiles=["PL11:LLC0"]),
+           "missing_nets" : None,
+           "bank" : "L"
+        },
+        {
+           "pos" : [(10, 22)],
+           "cfg" : FuzzConfig(job="PIOROUTER", family="MachXO2", device="LCMXO2-1200HC", ncl="pioroute.ncl",
+                                   tiles=["PR10:PIC_R0"]),
+           "missing_nets" : None,
+           "bank" : "R"
+        },
+        {
+           "pos" : [(0, 12)],
+           "cfg" : FuzzConfig(job="PIOROUTET", family="MachXO2", device="LCMXO2-1200HC", ncl="pioroute.ncl",
+                                  tiles=["PT12:PIC_T0"]),
+           "missing_nets" : None,
+           "bank" : "T",
+        },
+        {
+           "pos" : [(1, 12)],
+           "cfg" : FuzzConfig(job="PIOROUTET_CIB", family="MachXO2", device="LCMXO2-1200HC", ncl="pioroute.ncl",
+                                  tiles=["CIB_R1C12:CIB_PIC_T0"]),
+           "missing_nets" : None,
+           "bank" : None,
+        },
+]
+
+def main(args):
+    pytrellis.load_database("../../../database")
+    for job in [jobs[i] for i in args.ids]:
+        cfg = job["cfg"]
+        cfg.setup()
+
+        if args.i:
+            # Fuzz basic routing, ignore fixed connections to/from I/O pads.
+            interconnect.fuzz_interconnect(config=cfg, location=pos,
+                                           netname_predicate=nn_filter,
+                                           netdir_override=defaultdict(lambda : str("ignore")),
+                                           fc_predicate=fc_filter,
+                                           netname_filter_union=False,
+                                           enable_span1_fix=True,
+                                           bias=1)
+
+        if args.m and job["missing_nets"]:
+            interconnect.fuzz_interconnect_with_netnames(config=cfg,
+                                                         netnames=job["missing_nets"],
+                                                         fc_predicate=fc_filter,
+                                                         netname_filter_union=False,
+                                                         bidir=True,
+                                                         netdir_override=defaultdict(lambda : str("ignore")),
+                                                         bias=1)
+
+
+        if args.p and job["bank"]:
+            # I/O connections in the left/right tiles exist as-if a column "0"
+            # or one past maximum is physically present.
+            if job["bank"] == "R":
+                io_nets = mk_nets.io_conns((job["pos"][0][0], job["pos"][0][1] + 1), job["bank"])
+            elif job["bank"] == "L":
+                io_nets = mk_nets.io_conns((job["pos"][0][0], job["pos"][0][1] - 1), job["bank"])
+            else:
+                io_nets = mk_nets.io_conns(job["pos"][0], job["bank"])
+
+            io_list = [io[0] for io in io_nets]
+            override_dict = {io[0]: io[1] for io in io_nets}
+            print(override_dict)
+
+            interconnect.fuzz_interconnect_with_netnames(config=cfg,
+                                                         netnames=io_list,
+                                                         fc_predicate=fc_filter,
+                                                         netname_filter_union=False,
+                                                         bidir=True,
+                                                         netdir_override=override_dict,
+                                                         bias=1)
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="PIO Routing Fuzzer.")
+    parser.add_argument("-i", action="store_true", help="Fuzz interconnect.")
+    parser.add_argument("-m", action="store_true", help="Fuzz missing nets.")
+    parser.add_argument("-p", action="store_true", help="Fuzz I/O pad connections.")
+    parser.add_argument(dest="ids", metavar="N", type=int, nargs="*",
+                    default=range(0, len(jobs)), help="Job (indices) to run.")
+    args = parser.parse_args()
+    main(args)
diff --git a/fuzzers/machxo2/050-pio_routing/mk_nets.py b/fuzzers/machxo2/050-pio_routing/mk_nets.py
new file mode 100644
index 0000000..308b667
--- /dev/null
+++ b/fuzzers/machxo2/050-pio_routing/mk_nets.py
@@ -0,0 +1,137 @@
+from nets import net_product, char_range
+from itertools import product, starmap
+from collections import defaultdict
+import re
+
+def io_conns(tile, bank):
+    # All I/O connections on the left bank are contained in the other banks.
+    all_template = [
+        ("JPADDO{}", "sink"),
+        ("JPADDT{}", "sink"),
+        ("PADDO{}_PIO", "sink"),
+        ("PADDT{}_PIO", "sink"),
+        ("IOLDO{}_PIO","sink"),
+        ("IOLDO{}_{}IOLOGIC","sink"),
+        ("IOLTO{}_PIO","sink"),
+        ("IOLTO{}_{}IOLOGIC","sink"),
+        ("JPADDI{}_PIO", "driver"),
+        ("PADDI{}_{}IOLOGIC", "driver"),
+        ("JDI{}", "driver"),
+        ("INDD{}_{}IOLOGIC", "driver"),
+        ("DI{}_{}IOLOGIC", "sink"),
+        ("JONEG{}_{}IOLOGIC", "sink"),
+        ("JOPOS{}_{}IOLOGIC", "sink"),
+        ("JTS{}_{}IOLOGIC", "sink"),
+        ("JCE{}_{}IOLOGIC", "sink"),
+        ("JLSR{}_{}IOLOGIC", "sink"),
+        ("JCLK{}_{}IOLOGIC", "sink"),
+        ("JIN{}_{}IOLOGIC", "driver"),
+        ("JIP{}_{}IOLOGIC", "driver"),
+        ("INRD{}_PIO", "sink"),
+        ("PG{}_PIO", "sink")
+    ]
+
+    right = [
+        ("DQSW90{}_RIOLOGIC", "sink"),
+        ("DQSR90{}_RIOLOGIC", "sink"),
+        ("DDRCLKPOL{}_RIOLOGIC", "sink")
+    ]
+
+    bottom = [
+        ("JRXDA{2}_{1}IOLOGIC", "driver"),
+        ("JRXD{2}{0}_{1}IOLOGIC", "driver"),
+        ("JDEL{2}{0}_{1}IOLOGIC", "sink"),
+        ("JLIP{}_{}IOLOGIC", "sink"),
+        ("ECLK{}_{}IOLOGIC", "sink"),
+    ]
+
+    top = [
+        ("JTXD{2}{0}_{1}IOLOGIC", "sink"),
+        ("ECLK{}_{}IOLOGIC", "sink"),
+        ("LVDS{}_PIO", "sink"),
+    ]
+
+    if bank == "B":
+        bank_template = bottom
+    elif bank == "T":
+        bank_template = top
+    elif bank == "R":
+        bank_template = right
+    else:
+        bank_template = []
+
+    # Nets which come in 0-3/0-7 and 0-4, respectively.
+    rxda_re = re.compile("JRXDA\{2\}*")
+    rxd_re = re.compile("JRXD\{2\}\{0\}*")
+    txda_re = re.compile("JTXD\{2\}A*")
+    txd_re = re.compile("JTXD\{2\}\{0\}*")
+    del_re = re.compile("JDEL*")
+    eclk_re = re.compile("ECLKC*")
+
+    netlist = []
+    for pad in ("A", "B", "C", "D"):
+        # B/BS/T/TSIOLOGIC
+        if bank in ("B", "T"):
+            if pad == "A":
+                io_prefix = bank
+            elif pad == "C":
+                io_prefix = "{}S".format(bank)
+            else:
+                io_prefix = ""
+        # RIOLOGIC
+        elif bank == "R":
+            io_prefix = "R"
+        # Just "LOGIC"
+        else:
+            io_prefix = ""
+
+        for f, d in all_template:
+            suffix = f.format(pad, io_prefix)
+            netlist.append(("R{}C{}_{}".format(tile[0], tile[1], suffix), d))
+
+        if bank == "R":
+            for f, d in bank_template:
+                suffix = f.format(pad, io_prefix)
+                netlist.append(("R{}C{}_{}".format(tile[0], tile[1], suffix), d))
+        elif bank == "B":
+            for f, d in bank_template:
+                if del_re.match(f) and pad in ("A", "C"):
+                    for n in range(5):
+                        suffix = f.format(pad, io_prefix, n)
+                        netlist.append(("R{}C{}_{}".format(tile[0], tile[1], suffix), d))
+                elif rxda_re.match(f) and pad == "A":
+                    for n in range(8):
+                        suffix = f.format(pad, io_prefix, n)
+                        netlist.append(("R{}C{}_{}".format(tile[0], tile[1], suffix), d))
+                elif rxd_re.match(f) and pad in ("A", "C"):
+                    for n in range(4):
+                        suffix = f.format(pad, io_prefix, n)
+                        netlist.append(("R{}C{}_{}".format(tile[0], tile[1], suffix), d))
+                elif pad in ("A", "C") and not rxda_re.match(f):
+                    suffix = f.format(pad, io_prefix)
+                    netlist.append(("R{}C{}_{}".format(tile[0], tile[1], suffix), d))
+        elif bank == "T":
+            for f, d in bank_template:
+                if txd_re.match(f) and pad in ("A", "C"):
+                    netrange = range(8) if pad == "A" else range(4)
+                    for n in netrange:
+                        suffix = f.format(pad, io_prefix, n)
+                        netlist.append(("R{}C{}_{}".format(tile[0], tile[1], suffix), d))
+                elif eclk_re.match(f) and pad in ("A", "C"):
+                    suffix = f.format(pad, io_prefix)
+                    netlist.append(("R{}C{}_{}".format(tile[0], tile[1], suffix), d))
+                elif not txd_re.match(f) and not eclk_re.match(f):
+                    suffix = f.format(pad, io_prefix)
+                    netlist.append(("R{}C{}_{}".format(tile[0], tile[1], suffix), d))
+
+    return netlist
+
+def main():
+    for t, b in zip(((10, 0), (12, 11), (10, 23), (1, 11)), ("L", "B", "R", "T")):
+        print("Bank {}:".format(b))
+        for i, n in enumerate(io_conns(t, b)):
+            print(i, n)
+        print("")
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/050-pio_routing/pioroute.ncl b/fuzzers/machxo2/050-pio_routing/pioroute.ncl
new file mode 100644
index 0000000..a7e908a
--- /dev/null
+++ b/fuzzers/machxo2/050-pio_routing/pioroute.ncl
@@ -0,0 +1,39 @@
+::FROM-WRITER;
+design top
+{
+    device
+    {
+        architecture xo2c00;
+        device LCMXO2-1200HC;
+        package QFN32;
+        performance "6";
+    }
+
+   comp PIO
+   {
+      logical
+      {
+         cellmodel-name PIO;
+         program "TRIMUX:PADDT:::PADDT=0 "
+                 "IOBUF:::PULLMODE=NONE,DRIVE=8, \"
+                    "SLEWRATE=SLOW,HYSTERESIS=NA "
+                 "DATAMUX:PADDO "
+                 "VREF:OFF "
+                 "ODMUX:TRIMUX "
+                 "LVDSMUX:DATAMUX ";
+         primitive IOBUF PIO_pad;
+         primitive PAD PIO;
+      }
+      site "11";
+   }
+
+    signal q_c
+   {
+      signal-pins
+         // drivers
+         (PIO, PADDI),
+         // loads
+         (PIO, PADDO);
+      ${route}
+   }
+}
diff --git a/fuzzers/machxo2/051-pio_attrs/empty.ncl b/fuzzers/machxo2/051-pio_attrs/empty.ncl
new file mode 100644
index 0000000..11b72ab
--- /dev/null
+++ b/fuzzers/machxo2/051-pio_attrs/empty.ncl
@@ -0,0 +1,12 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+}
diff --git a/fuzzers/machxo2/051-pio_attrs/fuzzer.py b/fuzzers/machxo2/051-pio_attrs/fuzzer.py
new file mode 100644
index 0000000..dc5af6f
--- /dev/null
+++ b/fuzzers/machxo2/051-pio_attrs/fuzzer.py
@@ -0,0 +1,245 @@
+from collections import defaultdict
+
+from fuzzconfig import FuzzConfig
+import interconnect
+import nets
+import pytrellis
+import re
+import argparse
+import fuzzloops
+import nonrouting
+import os
+
+jobs = [
+        {
+            "cfg": FuzzConfig(job="PICB0_AB", family="MachXO2", device="LCMXO2-1200HC",
+                        ncl="empty.ncl", tiles=["PB11:PIC_B0"]),
+            "side": "B",
+            "pins": [("13", "A"), ("14", "B")]
+        },
+
+        # Split into multiple jobs, because Diamond chokes if the I/Os don't
+        # actually physically exist (QFN32 is default).
+        {
+            "cfg": FuzzConfig(job="PICB0_CD", family="MachXO2", device="LCMXO2-1200HC",
+                        ncl="empty.ncl", tiles=["PB6:PIC_B0"]),
+            "side": "B",
+            "pins": [("9", "C"), ("10", "D")]
+        },
+
+        {
+            "cfg": FuzzConfig(job="PICL0_IO", family="MachXO2", device="LCMXO2-1200HC",
+                        ncl="empty.ncl", tiles=["PL5:PIC_L0"]),
+            "side": "L",
+            "pins": [("12", "A"), ("13", "B"), ("14", "C"), ("15", "D")],
+            "package": "TQFP100"
+        },
+
+        {
+            "cfg": FuzzConfig(job="PICR0_IO", family="MachXO2", device="LCMXO2-1200HC",
+                        ncl="empty.ncl", tiles=["PR5:PIC_R0"]),
+            "side": "R",
+            "pins": [("65", "A"), ("64", "B"), ("63", "C"), ("62", "D")],
+            "package": "TQFP100"
+        },
+
+        {
+            "cfg": FuzzConfig(job="PICT0_IO", family="MachXO2", device="LCMXO2-1200HC",
+                        ncl="empty.ncl", tiles=["PT10:PIC_T0"]),
+            "side": "T",
+            "pins": [("97", "A"), ("96", "B")],
+            "package": "TQFP100"
+        },
+
+        # FIXME: WARNING - map: In "LOCATE COMP "pad" SITE "PT10{C,D} pin" ;":
+        # Current SYS_CONFIG setting prohibits pin be used as user IO. This
+        # preference has been disabled. Why?
+        {
+            "cfg": FuzzConfig(job="PICT0_IO", family="MachXO2", device="LCMXO2-1200HC",
+                        ncl="empty.ncl", tiles=["PT12:PIC_T0"]),
+            "side": "T",
+            "pins": [("28", "C"), ("27", "D")]
+        },
+]
+
+# Function constructed from reading the MachXO2 sysIO Usage Guide.
+# Diamond is very sensitive to invalid I/O combinations, and will happily
+# change the I/O type out from under you if you give it a bad combination.
+# This can lead to further errors when the Diamond-assigned I/O type is
+# invalid for the pin (for the complementary pair, especially).
+def get_io_types(dir, pio, side):
+    # Singled-ended I/O types.
+    types = [
+        "LVTTL33",
+        "LVCMOS33",
+        "LVCMOS25",
+        "LVCMOS18",
+        "LVCMOS15",
+        "LVCMOS12",
+        "SSTL25_I",
+        "SSTL18_I",
+        "HSTL18_I"
+    ]
+
+    if dir == "INPUT":
+        types += [
+            "SSTL25_II",
+            "SSTL18_II",
+            "HSTL18_II",
+            "LVCMOS25R33",
+            "LVCMOS18R33",
+            "LVCMOS18R25",
+            "LVCMOS15R33",
+            "LVCMOS15R25"
+        ]
+
+    if dir in ("INPUT", "BIDIR"):
+        types += [
+            "LVCMOS12R33",
+            "LVCMOS12R25",
+            "LVCMOS10R33",
+            "LVCMOS10R25"
+        ]
+
+    if side == "B":
+        # Only bottom bank supports PCI33.
+        types += [
+            "PCI33"
+        ]
+
+    # Differential I/O types.
+    if pio in ("A", "C"):
+        types += [
+            "SSTL25D_I",
+            "SSTL18D_I",
+            "HSTL18D_I",
+            "MIPI",
+            "LVCMOS33D",
+            "LVCMOS25D",
+            "LVCMOS18D",
+            "LVCMOS15D",
+            "LVCMOS12D",
+        ]
+
+        if dir == "INPUT":
+            # True differential inputs.
+            # FIXME: Also supported in bidir?
+            # map_impl.mrp suggests no (warning: violates legal combination
+            # and is ignored.)
+            types += [
+                "SSTL25D_II",
+                "SSTL18D_II",
+                "HSTL18D_II",
+                "LVDS25",
+                "LVPECL33",
+                "MLVDS25",
+                "BLVDS25",
+                "RSDS25"
+            ]
+
+        if dir == "OUTPUT":
+            # Emulated differential output.
+            # FIXME: Also supported in bidir?
+            types += [
+                "LVDS25E",
+                "LVPECL33E",
+                "MLVDS25E",
+                "BLVDS25E",
+                "RSDS25E"
+            ]
+
+            # True differential output. Only supported on Top and primary pair.
+            if pio == "A" and side == "T":
+                types += [
+                    "LVDS25"
+                ]
+    return types
+
+
+def get_cfg_vccio(iotype):
+    m = re.match(r".*(\d)(\d)$", iotype)
+    if not m:
+        return "3.3"
+    return "{}.{}".format(m.group(1), m.group(2))
+
+
+def main(args):
+    pytrellis.load_database("../../../database")
+    for job in [jobs[i] for i in args.ids]:
+        cfg = job["cfg"]
+        side = job["side"]
+        pins = job["pins"]
+
+        os.environ['DEV_PACKAGE'] = job.get("package", "QFN32")
+
+        cfg.setup()
+        empty_bitfile = cfg.build_design(cfg.ncl, {})
+        cfg.ncl = "pio.v"
+
+        def per_pin(pin):
+            loc, pio = pin
+
+            def get_substs(iomode, extracfg=None):
+                if iomode == "NONE":
+                    iodir, type = "NONE", ""
+                else:
+                    iodir, type = iomode.split("_", 1)
+                substs = {
+                    "dir": iodir,
+                    "io_type": type,
+                    "loc": loc,
+                    "extra_attrs": "",
+                    "cfg_vio": "3.3"
+                }
+                if extracfg is not None:
+                    substs["extra_attrs"] = '(* {}="{}" *)'.format(extracfg[0], extracfg[1])
+                if side == "B":
+                    substs["cfg_vio"] = get_cfg_vccio(type)
+                return substs
+
+            modes = ["NONE"]
+            for iodir in ("INPUT", "OUTPUT", "BIDIR"):
+                modes += [iodir + "_" + _ for _ in get_io_types(iodir, pio, side)]
+
+            nonrouting.fuzz_enum_setting(cfg, "PIO{}.BASE_TYPE".format(pio), modes,
+                                         lambda x: get_substs(iomode=x),
+                                         empty_bitfile, False)
+
+            nonrouting.fuzz_enum_setting(cfg, "PIO{}.PULLMODE".format(pio), ["UP", "DOWN", "NONE", "KEEPER", "FAILSAFE"],
+                                         lambda x: get_substs(iomode="INPUT_LVCMOS33", extracfg=("PULLMODE", x)),
+                                         empty_bitfile)
+            nonrouting.fuzz_enum_setting(cfg, "PIO{}.SLEWRATE".format(pio), ["FAST", "SLOW"],
+                                         lambda x: get_substs(iomode="OUTPUT_LVCMOS33", extracfg=("SLEWRATE", x)),
+                                         empty_bitfile)
+            # # FIXME: Do LVCMOS12, which is 2/6mA.
+            nonrouting.fuzz_enum_setting(cfg, "PIO{}.DRIVE".format(pio), ["4", "8", "12", "16", "24"],
+                                         lambda x: get_substs(iomode="OUTPUT_LVCMOS33", extracfg=("DRIVE", x)),
+                                         empty_bitfile)
+            nonrouting.fuzz_enum_setting(cfg, "PIO{}.HYSTERESIS".format(pio), ["SMALL", "LARGE"],
+                                         lambda x: get_substs(iomode="INPUT_LVCMOS33", extracfg=("HYSTERESIS", x)),
+                                         empty_bitfile)
+            nonrouting.fuzz_enum_setting(cfg, "PIO{}.OPENDRAIN".format(pio), ["ON", "OFF"],
+                                         lambda x: get_substs(iomode="OUTPUT_LVCMOS33", extracfg=("OPENDRAIN", x)),
+                                         empty_bitfile)
+            if loc in "B":
+                nonrouting.fuzz_enum_setting(cfg, "PIO{}.CLAMP".format(pio), ["PCI", "OFF"],
+                                             lambda x: get_substs(iomode="INPUT_LVCMOS33", extracfg=("CLAMP", x)),
+                                             empty_bitfile)
+            else:
+                nonrouting.fuzz_enum_setting(cfg, "PIO{}.CLAMP".format(pio), ["ON", "OFF"],
+                                             lambda x: get_substs(iomode="INPUT_LVCMOS33", extracfg=("CLAMP", x)),
+                                             empty_bitfile)
+            if loc in "T" and pio in "A":
+                nonrouting.fuzz_enum_setting(cfg, "PIO{}.DIFFDRIVE".format(pio), ["1.25", "2.0", "2.5", "3.5"],
+                                             lambda x: get_substs(iomode="INPUT_LVCMOS33", extracfg=("CLAMP", x)),
+                                             empty_bitfile)
+
+        fuzzloops.parallel_foreach(pins, per_pin)
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="PIO Attributes Fuzzer.")
+    parser.add_argument(dest="ids", metavar="N", type=int, nargs="*",
+                    default=range(0, len(jobs)), help="Job (indices) to run.")
+    args = parser.parse_args()
+    main(args)
diff --git a/fuzzers/machxo2/051-pio_attrs/pio.lpf b/fuzzers/machxo2/051-pio_attrs/pio.lpf
new file mode 100644
index 0000000..0c934b5
--- /dev/null
+++ b/fuzzers/machxo2/051-pio_attrs/pio.lpf
@@ -0,0 +1 @@
+SYSCONFIG CONFIG_IOVOLTAGE = ${cfg_vio};
diff --git a/fuzzers/machxo2/051-pio_attrs/pio.v b/fuzzers/machxo2/051-pio_attrs/pio.v
new file mode 100644
index 0000000..0fa131a
--- /dev/null
+++ b/fuzzers/machxo2/051-pio_attrs/pio.v
@@ -0,0 +1,68 @@
+`define ${dir}
+
+`ifdef NONE
+
+module top(input x);
+
+// Minimum legal empty module
+wire dummy;
+
+// Dummy load
+GSR gsr_i(.GSR(dummy));
+
+// Dummy source
+OSCH osc_i(.OSC(dummy));
+
+
+endmodule
+
+`else
+
+module top(inout pad);
+
+`ifdef BIDIR
+
+wire dummyo, dummyi;
+
+(* keep *)
+(* LOC="${loc}" *)
+(* IO_TYPE="${io_type}" *)
+${extra_attrs}
+BB b_b(.B(pad), .O(dummyo), .I(1'b1), .T(dummyi));
+
+// Dummy load
+GSR gsr_i(.GSR(dummyo));
+
+// Dummy source
+OSCH osc_i(.OSC(dummyi));
+
+`endif
+
+`ifdef INPUT
+
+wire dummyo;
+
+(* keep *)
+(* LOC="${loc}" *)
+(* IO_TYPE="${io_type}" *)
+${extra_attrs}
+IB i_b(.I(pad), .O(dummyo));
+
+// Dummy load
+GSR gsr_i(.GSR(dummyo));
+
+`endif
+
+`ifdef OUTPUT
+
+(* keep *)
+(* LOC="${loc}" *)
+(* IO_TYPE="${io_type}" *)
+${extra_attrs}
+OB o_b(.O(pad), .I(1'b1));
+
+`endif
+
+endmodule
+
+`endif
diff --git a/fuzzers/machxo2/052-pio_fixup/fuzzer.py b/fuzzers/machxo2/052-pio_fixup/fuzzer.py
new file mode 100644
index 0000000..0bdf954
--- /dev/null
+++ b/fuzzers/machxo2/052-pio_fixup/fuzzer.py
@@ -0,0 +1,10 @@
+import dbfixup
+import pytrellis
+
+def main():
+    pytrellis.load_database("../../../database")
+    dbfixup.remove_enum_bits("MachXO2", "LCMXO2-1200HC", "PIC_L0", (29, 11))
+    dbfixup.remove_enum_bits("MachXO2", "LCMXO2-1200HC", "PIC_R0", (29, 59), (0, 48))
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/102-oscg/empty.ncl b/fuzzers/machxo2/102-oscg/empty.ncl
new file mode 100644
index 0000000..7779581
--- /dev/null
+++ b/fuzzers/machxo2/102-oscg/empty.ncl
@@ -0,0 +1,11 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+}
diff --git a/fuzzers/machxo2/102-oscg/fuzzer.py b/fuzzers/machxo2/102-oscg/fuzzer.py
new file mode 100644
index 0000000..98db71f
--- /dev/null
+++ b/fuzzers/machxo2/102-oscg/fuzzer.py
@@ -0,0 +1,78 @@
+import sys
+
+from fuzzconfig import FuzzConfig
+import nonrouting
+import pytrellis
+import fuzzloops
+import interconnect
+
+cfg = FuzzConfig(job="OSCH", family="MachXO2", device="LCMXO2-1200HC", ncl="empty.ncl",
+                                          tiles=["PT8:PIC_T_DUMMY_OSC",
+                                                "PT4:CFG0", "PT5:CFG1",
+                                                "PT6:CFG2", "PT7:CFG3",
+                                                "CIB_R1C4:CIB_CFG0",
+                                                "CIB_R1C5:CIB_CFG1"])
+
+
+def get_substs(mode="OSCH", nom_freq="2.08", stdby="0"):
+    if mode == "NONE":
+        comment = "//"
+    else:
+        comment = ""
+
+    if stdby == "1":
+        stdby = ""
+        stdby_0 = "//"
+    else:
+        stdby = "//"
+        stdby_0 = ""
+
+    if nom_freq == "2.08":
+        using_non_default_freq = ""
+        using_default_freq = "//"
+    else:
+        using_non_default_freq = "//"
+        using_default_freq = ""
+
+    return dict(comment=comment,
+                nom_freq=nom_freq,
+                stdby=stdby,
+                stdby_0=stdby_0,
+                using_non_default_freq=using_non_default_freq,
+                using_default_freq=using_default_freq)
+
+def main():
+    pytrellis.load_database("../../../database")
+    cfg.setup()
+    empty_bitfile = cfg.build_design(cfg.ncl, {})
+    cfg.ncl = "osc.ncl"
+
+    nonrouting.fuzz_enum_setting(cfg, "OSCH.STDBY", ["0", "1"],
+                                 lambda x: get_substs(stdby=x), empty_bitfile)
+    nonrouting.fuzz_enum_setting(cfg, "OSCH.MODE", ["NONE", "OSCH"],
+                                 lambda x: get_substs(mode=x), empty_bitfile)
+
+    # Takes a long time, so permit opt-out.
+    if "-s" not in sys.argv:
+        nonrouting.fuzz_enum_setting(cfg, "OSCH.NOM_FREQ",
+            ["{}".format(i) for i in [
+                2.08, 2.15, 2.22, 2.29, 2.38, 2.46, 2.56, 2.66, 2.77, 2.89,
+                3.02, 3.17, 3.33, 3.50, 3.69, 3.91, 4.16, 4.29, 4.43, 4.59,
+                4.75, 4.93, 5.12, 5.32, 5.54, 5.78, 6.05, 6.33, 6.65, 7.00,
+                7.39, 7.82, 8.31, 8.58, 8.87, 9.17, 9.50, 9.85, 10.23, 10.64,
+                11.08, 11.57, 12.09, 12.67, 13.30, 14.00, 14.78, 15.65, 15.65, 16.63,
+                17.73, 19.00, 20.46, 22.17, 24.18, 26.60, 29.56, 33.25, 38.00, 44.33,
+                53.20, 66.50, 88.67, 133.00
+            ]], lambda x: get_substs(nom_freq=x), empty_bitfile)
+
+    cfg.ncl = "osc_routing.ncl"
+    interconnect.fuzz_interconnect_with_netnames(
+        cfg,
+        ["R1C4_JOSC_OSC"],
+        bidir=True,
+        netdir_override={"R1C4_JOSC_OSC" : "driver"},
+        bias=1
+    )
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/machxo2/102-oscg/osc.ncl b/fuzzers/machxo2/102-oscg/osc.ncl
new file mode 100644
index 0000000..1396f62
--- /dev/null
+++ b/fuzzers/machxo2/102-oscg/osc.ncl
@@ -0,0 +1,23 @@
+::FROM-WRITER;
+design top
+{
+   device
+   {
+       architecture xo2c00;
+       device LCMXO2-1200HC;
+       package QFN32;
+       performance "6";
+   }
+
+   ${comment} comp OSC
+   ${comment} {
+   ${comment}    logical {
+   ${comment}       cellmodel-name OSC;
+   ${comment}       program "${program}"
+   ${using_non_default_freq} ${stdby} ${comment}               "OSCH:#ON ";
+   ${using_non_default_freq} ${stdby_0} ${comment}               "OSCH::::STDBY=0 ";
+   ${using_default_freq} ${comment}               "OSCH:::NOM_FREQ=${nom_freq}:STDBY=0 ";
+   ${comment}    }
+   ${comment}    site OSC;
+   ${comment} }
+}
diff --git a/fuzzers/machxo2/900-db_fixup/fuzzer.py b/fuzzers/machxo2/900-db_fixup/fuzzer.py
new file mode 100644
index 0000000..100a28c
--- /dev/null
+++ b/fuzzers/machxo2/900-db_fixup/fuzzer.py
@@ -0,0 +1,19 @@
+import dbfixup
+import pytrellis
+
+device = "LCMXO2-1200HC"
+
+
+def main():
+    pytrellis.load_database("../../../database")
+    chip = pytrellis.Chip("LCMXO2-1200HC")
+    tiletypes = set()
+    for tile in chip.get_all_tiles():
+        tiletypes.add(tile.info.type)
+
+    for tiletype in sorted(tiletypes):
+        dbfixup.dbfixup("MachXO2", device, tiletype)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/libtrellis/include/Bels.hpp b/libtrellis/include/Bels.hpp
index cb4b5ea..be6ec28 100644
--- a/libtrellis/include/Bels.hpp
+++ b/libtrellis/include/Bels.hpp
@@ -5,7 +5,7 @@
 #include "RoutingGraph.hpp"
 
 namespace Trellis {
-namespace Bels {
+namespace Ecp5Bels {
 
 void add_lc(RoutingGraph &graph, int x, int y, int z);
 void add_pio(RoutingGraph &graph, int x, int y, int z);
@@ -21,6 +21,14 @@
 void add_misc(RoutingGraph &graph, const std::string &name, int x, int y);
 void add_ioclk_bel(RoutingGraph &graph, const std::string &name, int x, int y, int i, int bank = -1);
 }
+
+namespace MachXO2Bels {
+
+void add_lc(RoutingGraph &graph, int x, int y, int z);
+void add_pio(RoutingGraph &graph, int x, int y, int z);
+}
+
+
 }
 
 #endif
diff --git a/libtrellis/include/Chip.hpp b/libtrellis/include/Chip.hpp
index 1c12c3b..5fc1fc5 100644
--- a/libtrellis/include/Chip.hpp
+++ b/libtrellis/include/Chip.hpp
@@ -150,6 +150,11 @@
 
     // Globals data
     GlobalsInfo global_data;
+
+private:
+    // Factory functions
+    shared_ptr<RoutingGraph> get_routing_graph_ecp5();
+    shared_ptr<RoutingGraph> get_routing_graph_machxo2();
 };
 
 ChipDelta operator-(const Chip &a, const Chip &b);
diff --git a/libtrellis/include/RoutingGraph.hpp b/libtrellis/include/RoutingGraph.hpp
index 9f41187..3110e9e 100644
--- a/libtrellis/include/RoutingGraph.hpp
+++ b/libtrellis/include/RoutingGraph.hpp
@@ -135,6 +135,7 @@
 
     // Must be set up beforehand
     std::string chip_name;
+    std::string chip_family;
     std::string chip_prefix;
     int max_row, max_col;
 
@@ -157,6 +158,11 @@
     // Add a Bel input or output pin
     void add_bel_input(RoutingBel &bel, ident_t pin, int wire_x, int wire_y, ident_t wire_name);
     void add_bel_output(RoutingBel &bel, ident_t pin, int wire_x, int wire_y, ident_t wire_name);
+
+private:
+    // Factory functions
+    RoutingId globalise_net_ecp5(int row, int col, const std::string &db_name);
+    RoutingId globalise_net_machxo2(int row, int col, const std::string &db_name);
 };
 }
 
diff --git a/libtrellis/src/Bels.cpp b/libtrellis/src/Bels.cpp
index bbcd7d8..c11ed64 100644
--- a/libtrellis/src/Bels.cpp
+++ b/libtrellis/src/Bels.cpp
@@ -3,7 +3,7 @@
 #include "Database.hpp"
 #include "BitDatabase.hpp"
 namespace Trellis {
-namespace Bels {
+namespace Ecp5Bels {
 
 void add_lc(RoutingGraph &graph, int x, int y, int z) {
     char l = "ABCD"[z];
@@ -634,4 +634,14 @@
 
 
 }
-}
\ No newline at end of file
+
+namespace MachXO2Bels {
+    void add_lc(RoutingGraph &graph, int x, int y, int z) {
+
+    }
+
+    void add_pio(RoutingGraph &graph, int x, int y, int z) {
+      
+    }
+}
+}
diff --git a/libtrellis/src/Bitstream.cpp b/libtrellis/src/Bitstream.cpp
index fb0aa38..1a35e94 100644
--- a/libtrellis/src/Bitstream.cpp
+++ b/libtrellis/src/Bitstream.cpp
@@ -39,8 +39,6 @@
     BitstreamOptions(const Chip &chip) {
       if (chip.info.family == "MachXO2") {
           // Write frames out in order 0 => max or reverse (max => 0).
-          // This apparently does NOT apply to compressed bitstreams, which
-          // uses reversed_frames = true unconditionally.
           reversed_frames = false;
           dummy_bytes_after_preamble = 2;
           crc_meta = 0xE0; // CRC check (0x80), once at end (0x40), dummy bits
@@ -600,9 +598,7 @@
                     bytes_per_frame += (7 - ((bytes_per_frame - 1) % 8));
                 unique_ptr<uint8_t[]> frame_bytes = make_unique<uint8_t[]>(bytes_per_frame);
                 for (size_t i = 0; i < frame_count; i++) {
-                    // Apparently when a bitstream is compressed, even on
-                    // MachXO2, frames are written in reverse order!
-                    const size_t idx = (chip->info.num_frames - 1) - i;
+                    size_t idx = ops.reversed_frames ? (chip->info.num_frames - 1) - i : i;
                     if (cmd == BitstreamCommand::LSC_PROG_INCR_CMP)
                         rd.get_compressed_bytes(frame_bytes.get(), bytes_per_frame, compression_dict.get());
                     else
@@ -798,6 +794,7 @@
         size_t bytes_per_frame = (chip.info.bits_per_frame + chip.info.pad_bits_after_frame +
                                   chip.info.pad_bits_before_frame) / 8U;
         for (size_t i = 0; i < frames; i++) {
+            const size_t idx = ops.reversed_frames? (chip.info.num_frames - 1) - i : i;
             frames_data.emplace_back();
             auto &frame_bytes = frames_data.back();
             frame_bytes.resize(bytes_per_frame);
@@ -805,7 +802,7 @@
                 size_t ofs = j + chip.info.pad_bits_after_frame;
                 assert(((bytes_per_frame - 1) - (ofs / 8)) < bytes_per_frame);
                 frame_bytes[(bytes_per_frame - 1) - (ofs / 8)] |=
-                        (chip.cram.bit((chip.info.num_frames - 1) - i, j) & 0x01) << (ofs % 8);
+                        (chip.cram.bit(idx, j) & 0x01) << (ofs % 8);
             }
         }
         // Then compress and write
diff --git a/libtrellis/src/Chip.cpp b/libtrellis/src/Chip.cpp
index e983fa4..47fdbec 100644
--- a/libtrellis/src/Chip.cpp
+++ b/libtrellis/src/Chip.cpp
@@ -109,6 +109,16 @@
 
 shared_ptr<RoutingGraph> Chip::get_routing_graph()
 {
+    if(info.family == "ECP5") {
+        return get_routing_graph_ecp5();
+    } else if(info.family == "MachXO2") {
+        return get_routing_graph_machxo2();
+    } else
+      throw runtime_error("Unknown chip family: " + info.family);
+}
+
+shared_ptr<RoutingGraph> Chip::get_routing_graph_ecp5()
+{
     shared_ptr<RoutingGraph> rg(new RoutingGraph(*this));
     //cout << "Building routing graph" << endl;
     for (auto tile_entry : tiles) {
@@ -118,145 +128,159 @@
         bitdb->add_routing(tile->info, *rg);
         int x, y;
         tie(y, x) = tile->info.get_row_col();
-        // SLICE Bels
+        // SLICE Ecp5Bels
         if (tile->info.type == "PLC2") {
             for (int z = 0; z < 4; z++)
-                Bels::add_lc(*rg, x, y, z);
+                Ecp5Bels::add_lc(*rg, x, y, z);
         }
-        // PIO Bels
+        // PIO Ecp5Bels
         if (tile->info.type.find("PICL0") != string::npos || tile->info.type.find("PICR0") != string::npos)
             for (int z = 0; z < 4; z++) {
-                Bels::add_pio(*rg, x, y, z);
-                Bels::add_iologic(*rg, x, y, z, false);
+                Ecp5Bels::add_pio(*rg, x, y, z);
+                Ecp5Bels::add_iologic(*rg, x, y, z, false);
             }
         if (tile->info.type.find("PIOT0") != string::npos || (tile->info.type.find("PICB0") != string::npos && tile->info.type != "SPICB0"))
             for (int z = 0; z < 2; z++) {
-                Bels::add_pio(*rg, x, y, z);
-                Bels::add_iologic(*rg, x, y, z, true);
+                Ecp5Bels::add_pio(*rg, x, y, z);
+                Ecp5Bels::add_iologic(*rg, x, y, z, true);
             }
         if (tile->info.type == "SPICB0") {
-            Bels::add_pio(*rg, x, y, 0);
-            Bels::add_iologic(*rg, x, y, 0, true);
+            Ecp5Bels::add_pio(*rg, x, y, 0);
+            Ecp5Bels::add_iologic(*rg, x, y, 0, true);
         }
-        // DCC Bels
+        // DCC Ecp5Bels
         if (tile->info.type == "LMID_0")
             for (int z = 0; z < 14; z++)
-                Bels::add_dcc(*rg, x, y, "L", std::to_string(z));
+                Ecp5Bels::add_dcc(*rg, x, y, "L", std::to_string(z));
         if (tile->info.type == "RMID_0")
             for (int z = 0; z < 14; z++)
-                Bels::add_dcc(*rg, x, y, "R", std::to_string(z));
+                Ecp5Bels::add_dcc(*rg, x, y, "R", std::to_string(z));
         if (tile->info.type == "TMID_0")
             for (int z = 0; z < 12; z++)
-                Bels::add_dcc(*rg, x, y, "T", std::to_string(z));
+                Ecp5Bels::add_dcc(*rg, x, y, "T", std::to_string(z));
         if (tile->info.type == "BMID_0V" || tile->info.type == "BMID_0H")
             for (int z = 0; z < 16; z++)
-                Bels::add_dcc(*rg, x, y, "B", std::to_string(z));
-        // RAM Bels
+                Ecp5Bels::add_dcc(*rg, x, y, "B", std::to_string(z));
+        // RAM Ecp5Bels
         if (tile->info.type == "MIB_EBR0" || tile->info.type == "EBR_CMUX_UR" || tile->info.type == "EBR_CMUX_LR"
             || tile->info.type == "EBR_CMUX_LR_25K")
-            Bels::add_bram(*rg, x, y, 0);
+            Ecp5Bels::add_bram(*rg, x, y, 0);
         if (tile->info.type == "MIB_EBR2")
-            Bels::add_bram(*rg, x, y, 1);
+            Ecp5Bels::add_bram(*rg, x, y, 1);
         if (tile->info.type == "MIB_EBR4")
-            Bels::add_bram(*rg, x, y, 2);
+            Ecp5Bels::add_bram(*rg, x, y, 2);
         if (tile->info.type == "MIB_EBR6")
-            Bels::add_bram(*rg, x, y, 3);
-        // DSP Bels
+            Ecp5Bels::add_bram(*rg, x, y, 3);
+        // DSP Ecp5Bels
         if (tile->info.type == "MIB_DSP0")
-            Bels::add_mult18(*rg, x, y, 0);
+            Ecp5Bels::add_mult18(*rg, x, y, 0);
         if (tile->info.type == "MIB_DSP1")
-            Bels::add_mult18(*rg, x, y, 1);
+            Ecp5Bels::add_mult18(*rg, x, y, 1);
         if (tile->info.type == "MIB_DSP4")
-            Bels::add_mult18(*rg, x, y, 4);
+            Ecp5Bels::add_mult18(*rg, x, y, 4);
         if (tile->info.type == "MIB_DSP5")
-            Bels::add_mult18(*rg, x, y, 5);
+            Ecp5Bels::add_mult18(*rg, x, y, 5);
         if (tile->info.type == "MIB_DSP3")
-            Bels::add_alu54b(*rg, x, y, 3);
+            Ecp5Bels::add_alu54b(*rg, x, y, 3);
         if (tile->info.type == "MIB_DSP7")
-            Bels::add_alu54b(*rg, x, y, 7);
-        // PLL Bels
+            Ecp5Bels::add_alu54b(*rg, x, y, 7);
+        // PLL Ecp5Bels
         if (tile->info.type == "PLL0_UL")
-            Bels::add_pll(*rg, "UL", x+1, y);
+            Ecp5Bels::add_pll(*rg, "UL", x+1, y);
         if (tile->info.type == "PLL0_LL")
-            Bels::add_pll(*rg, "LL", x, y-1);
+            Ecp5Bels::add_pll(*rg, "LL", x, y-1);
         if (tile->info.type == "PLL0_LR")
-            Bels::add_pll(*rg, "LR", x, y-1);
+            Ecp5Bels::add_pll(*rg, "LR", x, y-1);
         if (tile->info.type == "PLL0_UR")
-            Bels::add_pll(*rg, "UR", x-1, y);
-        // DCU and ancillary Bels
+            Ecp5Bels::add_pll(*rg, "UR", x-1, y);
+        // DCU and ancillary Ecp5Bels
         if (tile->info.type == "DCU0") {
-            Bels::add_dcu(*rg, x, y);
-            Bels::add_extref(*rg, x, y);
+            Ecp5Bels::add_dcu(*rg, x, y);
+            Ecp5Bels::add_extref(*rg, x, y);
         }
         if (tile->info.type == "BMID_0H")
             for (int z = 0; z < 2; z++)
-                Bels::add_pcsclkdiv(*rg, x, y-1, z);
-        // Config/system Bels
+                Ecp5Bels::add_pcsclkdiv(*rg, x, y-1, z);
+        // Config/system Ecp5Bels
         if (tile->info.type == "EFB0_PICB0") {
-            Bels::add_misc(*rg, "GSR", x, y-1);
-            Bels::add_misc(*rg, "JTAGG", x, y-1);
-            Bels::add_misc(*rg, "OSCG", x, y-1);
-            Bels::add_misc(*rg, "SEDGA", x, y-1);
+            Ecp5Bels::add_misc(*rg, "GSR", x, y-1);
+            Ecp5Bels::add_misc(*rg, "JTAGG", x, y-1);
+            Ecp5Bels::add_misc(*rg, "OSCG", x, y-1);
+            Ecp5Bels::add_misc(*rg, "SEDGA", x, y-1);
         }
         if (tile->info.type == "DTR")
-            Bels::add_misc(*rg, "DTR", x, y-1);
+            Ecp5Bels::add_misc(*rg, "DTR", x, y-1);
         if (tile->info.type == "EFB1_PICB1")
-            Bels::add_misc(*rg, "USRMCLK", x-5, y);
+            Ecp5Bels::add_misc(*rg, "USRMCLK", x-5, y);
         if (tile->info.type == "ECLK_L") {
-            Bels::add_ioclk_bel(*rg, "CLKDIVF", x-2, y, 0, 7);
-            Bels::add_ioclk_bel(*rg, "CLKDIVF", x-2, y, 1, 6);
-            Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x-2, y, 0, 7);
-            Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x-2, y, 1, 7);
-            Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x-2, y+1, 0, 6);
-            Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x-2, y+1, 1, 6);
-            Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x-2, y, 0, 7);
-            Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x-2, y, 1, 7);
-            Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x-2, y+1, 0, 6);
-            Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x-2, y+1, 1, 6);
-            Bels::add_ioclk_bel(*rg, "DLLDELD", x-2, y-1, 0);
-            Bels::add_ioclk_bel(*rg, "DLLDELD", x-2, y, 0);
-            Bels::add_ioclk_bel(*rg, "DLLDELD", x-2, y+1, 0);
-            Bels::add_ioclk_bel(*rg, "DLLDELD", x-2, y+2, 0);
-            Bels::add_ioclk_bel(*rg, "ECLKBRIDGECS", x-2, y, 1);
-            Bels::add_ioclk_bel(*rg, "BRGECLKSYNC", x-2, y, 1);
+            Ecp5Bels::add_ioclk_bel(*rg, "CLKDIVF", x-2, y, 0, 7);
+            Ecp5Bels::add_ioclk_bel(*rg, "CLKDIVF", x-2, y, 1, 6);
+            Ecp5Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x-2, y, 0, 7);
+            Ecp5Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x-2, y, 1, 7);
+            Ecp5Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x-2, y+1, 0, 6);
+            Ecp5Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x-2, y+1, 1, 6);
+            Ecp5Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x-2, y, 0, 7);
+            Ecp5Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x-2, y, 1, 7);
+            Ecp5Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x-2, y+1, 0, 6);
+            Ecp5Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x-2, y+1, 1, 6);
+            Ecp5Bels::add_ioclk_bel(*rg, "DLLDELD", x-2, y-1, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "DLLDELD", x-2, y, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "DLLDELD", x-2, y+1, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "DLLDELD", x-2, y+2, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "ECLKBRIDGECS", x-2, y, 1);
+            Ecp5Bels::add_ioclk_bel(*rg, "BRGECLKSYNC", x-2, y, 1);
         }
         if (tile->info.type == "ECLK_R") {
-            Bels::add_ioclk_bel(*rg, "CLKDIVF", x+2, y, 0);
-            Bels::add_ioclk_bel(*rg, "CLKDIVF", x+2, y, 1);
-            Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x+2, y, 0, 2);
-            Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x+2, y, 1, 2);
-            Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x+2, y+1, 0, 3);
-            Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x+2, y+1, 1, 3);
-            Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x+2, y, 0, 2);
-            Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x+2, y, 1, 2);
-            Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x+2, y+1, 0, 3);
-            Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x+2, y+1, 1, 3);
-            Bels::add_ioclk_bel(*rg, "DLLDELD", x+2, y-1, 0);
-            Bels::add_ioclk_bel(*rg, "DLLDELD", x+2, y, 0);
-            Bels::add_ioclk_bel(*rg, "DLLDELD", x+2, y+1, 0);
-            Bels::add_ioclk_bel(*rg, "DLLDELD", x+2, y+2, 0);
-            Bels::add_ioclk_bel(*rg, "ECLKBRIDGECS", x+2, y, 0);
-            Bels::add_ioclk_bel(*rg, "BRGECLKSYNC", x+2, y, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "CLKDIVF", x+2, y, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "CLKDIVF", x+2, y, 1);
+            Ecp5Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x+2, y, 0, 2);
+            Ecp5Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x+2, y, 1, 2);
+            Ecp5Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x+2, y+1, 0, 3);
+            Ecp5Bels::add_ioclk_bel(*rg, "ECLKSYNCB", x+2, y+1, 1, 3);
+            Ecp5Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x+2, y, 0, 2);
+            Ecp5Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x+2, y, 1, 2);
+            Ecp5Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x+2, y+1, 0, 3);
+            Ecp5Bels::add_ioclk_bel(*rg, "TRELLIS_ECLKBUF", x+2, y+1, 1, 3);
+            Ecp5Bels::add_ioclk_bel(*rg, "DLLDELD", x+2, y-1, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "DLLDELD", x+2, y, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "DLLDELD", x+2, y+1, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "DLLDELD", x+2, y+2, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "ECLKBRIDGECS", x+2, y, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "BRGECLKSYNC", x+2, y, 0);
         }
         if (tile->info.type == "DDRDLL_UL")
-            Bels::add_ioclk_bel(*rg, "DDRDLL", x-2, y-10, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "DDRDLL", x-2, y-10, 0);
         if (tile->info.type == "DDRDLL_ULA")
-            Bels::add_ioclk_bel(*rg, "DDRDLL", x-2, y-13, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "DDRDLL", x-2, y-13, 0);
         if (tile->info.type == "DDRDLL_UR")
-            Bels::add_ioclk_bel(*rg, "DDRDLL", x+2, y-10, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "DDRDLL", x+2, y-10, 0);
         if (tile->info.type == "DDRDLL_URA")
-            Bels::add_ioclk_bel(*rg, "DDRDLL", x+2, y-13, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "DDRDLL", x+2, y-13, 0);
         if (tile->info.type == "DDRDLL_LL")
-            Bels::add_ioclk_bel(*rg, "DDRDLL", x-2, y+13, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "DDRDLL", x-2, y+13, 0);
         if (tile->info.type == "DDRDLL_LR")
-            Bels::add_ioclk_bel(*rg, "DDRDLL", x+2, y+13, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "DDRDLL", x+2, y+13, 0);
         if (tile->info.type == "PICL0_DQS2" || tile->info.type == "PICR0_DQS2")
-            Bels::add_ioclk_bel(*rg, "DQSBUFM", x, y, 0);
+            Ecp5Bels::add_ioclk_bel(*rg, "DQSBUFM", x, y, 0);
 
     }
     return rg;
 }
 
+shared_ptr<RoutingGraph> Chip::get_routing_graph_machxo2()
+{
+    shared_ptr<RoutingGraph> rg(new RoutingGraph(*this));
+
+    for (auto tile_entry : tiles) {
+        shared_ptr<Tile> tile = tile_entry.second;
+        //cout << "    Tile " << tile->info.name << endl;
+        shared_ptr<TileBitDatabase> bitdb = get_tile_bitdata(TileLocator{info.family, info.name, tile->info.type});
+        bitdb->add_routing(tile->info, *rg);
+    }
+
+    return rg;
+}
+
 // Global network funcs
 
 bool GlobalRegion::matches(int row, int col) const {
diff --git a/libtrellis/src/PyTrellis.cpp b/libtrellis/src/PyTrellis.cpp
index ac07c35..d7ff240 100644
--- a/libtrellis/src/PyTrellis.cpp
+++ b/libtrellis/src/PyTrellis.cpp
@@ -443,6 +443,7 @@
 
     class_<RoutingGraph, shared_ptr<RoutingGraph>>("RoutingGraph", no_init)
             .def_readonly("chip_name", &RoutingGraph::chip_name)
+            .def_readonly("chip_family", &RoutingGraph::chip_family)
             .def_readonly("max_row", &RoutingGraph::max_row)
             .def_readonly("max_col", &RoutingGraph::max_col)
             .def("ident", &RoutingGraph::ident)
diff --git a/libtrellis/src/RoutingGraph.cpp b/libtrellis/src/RoutingGraph.cpp
index 728a9bd..6908187 100644
--- a/libtrellis/src/RoutingGraph.cpp
+++ b/libtrellis/src/RoutingGraph.cpp
@@ -6,9 +6,11 @@
 
 namespace Trellis {
 
+// This is ignored for the MachXO2 family- globals are handled during routing
+// graph creation.
 const Location GlobalLoc(-2, -2);
 
-RoutingGraph::RoutingGraph(const Chip &c) : chip_name(c.info.name), max_row(c.get_max_row()), max_col(c.get_max_col())
+RoutingGraph::RoutingGraph(const Chip &c) : chip_name(c.info.name), chip_family(c.info.family), max_row(c.get_max_row()), max_col(c.get_max_col())
 {
     tiles[GlobalLoc].loc = GlobalLoc;
     for (int y = 0; y <= max_row; y++) {
@@ -23,6 +25,9 @@
         chip_prefix = "45K_";
     else if (chip_name.find("85F") != string::npos)
         chip_prefix = "85K_";
+    else if (chip_name.find("1200HC") != string::npos)
+        // FIXME: Prefix to distinguish XO, XO2, and XO3?
+        chip_prefix = "1200_";
     else
         assert(false);
 }
@@ -53,6 +58,16 @@
 
 RoutingId RoutingGraph::globalise_net(int row, int col, const std::string &db_name)
 {
+    if(chip_family == "ECP5") {
+        return globalise_net_ecp5(row, col, db_name);
+    } else if(chip_family == "MachXO2") {
+        return globalise_net_machxo2(row, col, db_name);
+    } else
+        throw runtime_error("Unknown chip family: " + chip_family);
+}
+
+RoutingId RoutingGraph::globalise_net_ecp5(int row, int col, const std::string &db_name)
+{
     static const std::regex e(R"(^([NS]\d+)?([EW]\d+)?_(.*))", std::regex::optimize);
     std::string stripped_name = db_name;
     if (db_name.find("25K_") == 0 || db_name.find("45K_") == 0 || db_name.find("85K_") == 0) {
@@ -111,6 +126,11 @@
     }
 }
 
+RoutingId RoutingGraph::globalise_net_machxo2(int row, int col, const std::string &db_name)
+{
+    return RoutingId();
+}
+
 void RoutingGraph::add_arc(Location loc, const RoutingArc &arc)
 {
     RoutingId arcId;
diff --git a/metadata/MachXO2/LCMXO2-1200HC/globals.json b/metadata/MachXO2/LCMXO2-1200HC/globals.json
new file mode 100644
index 0000000..a408e0b
--- /dev/null
+++ b/metadata/MachXO2/LCMXO2-1200HC/globals.json
@@ -0,0 +1,8 @@
+{
+  "quadrants": {
+  },
+  "taps": {
+  },
+  "spines": {
+  }
+}
diff --git a/metadata/MachXO2/LCMXO2-256HC/globals.json b/metadata/MachXO2/LCMXO2-256HC/globals.json
new file mode 100644
index 0000000..a408e0b
--- /dev/null
+++ b/metadata/MachXO2/LCMXO2-256HC/globals.json
@@ -0,0 +1,8 @@
+{
+  "quadrants": {
+  },
+  "taps": {
+  },
+  "spines": {
+  }
+}
diff --git a/minitests/reg/clk_1.v b/minitests/reg/clk_1.v
new file mode 100644
index 0000000..08895ef
--- /dev/null
+++ b/minitests/reg/clk_1.v
@@ -0,0 +1,4 @@
+module top(input clk, input d, input r, input s, output q);
+    GSR gsr(.GSR(r));
+    FD1P3JX ff(.D(d), .SP(1'b0), .PD(s), .CK(1'b1), .Q(q));
+endmodule
diff --git a/misc/basecfgs/empty_machxo2-1200hc.config b/misc/basecfgs/empty_machxo2-1200hc.config
new file mode 100644
index 0000000..3d116fc
--- /dev/null
+++ b/misc/basecfgs/empty_machxo2-1200hc.config
@@ -0,0 +1,30 @@
+.device LCMXO2-1200HC
+
+.tile EBR_R6C11:EBR1
+unknown: F0B12
+
+.tile EBR_R6C15:EBR1
+unknown: F0B12
+
+.tile EBR_R6C18:EBR1
+unknown: F0B12
+
+.tile EBR_R6C21:EBR1
+unknown: F0B12
+
+.tile EBR_R6C2:EBR1
+unknown: F0B12
+
+.tile EBR_R6C5:EBR1
+unknown: F0B12
+
+.tile EBR_R6C8:EBR1
+unknown: F0B12
+
+.tile PT4:CFG0
+unknown: F5B30
+unknown: F5B32
+unknown: F5B36
+
+.tile PT7:CFG3
+unknown: F5B18