Merge remote-tracking branch 'origin' 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 d0b219a..2d3b111 160000
--- a/database
+++ b/database
@@ -1 +1 @@
-Subproject commit d0b219af41ae3da6150645fbc5cc5613b530603f
+Subproject commit 2d3b111146f68cc788f0806fca6888f58beabd54
diff --git a/devices.json b/devices.json
index a2095be..4d79d6e 100644
--- a/devices.json
+++ b/devices.json
@@ -12,7 +12,7 @@
"max_row" : 50,
"max_col" : 72,
"col_bias" : 0,
- "fuzz": 1
+ "fuzz": 0
},
"LFE5U-45F": {
"packages": ["csfBGA285", "caBGA256", "caBGA381", "caBGA554", "caBGA756"],
@@ -24,7 +24,7 @@
"max_row" : 71,
"max_col" : 90,
"col_bias" : 0,
- "fuzz": 1
+ "fuzz": 0
},
"LFE5U-85F": {
"packages": ["csfBGA285", "caBGA381", "caBGA554", "caBGA756"],
@@ -36,7 +36,7 @@
"max_row" : 95,
"max_col" : 126,
"col_bias" : 0,
- "fuzz": 1
+ "fuzz": 0
},
"LFE5UM-25F": {
"packages": ["csfBGA285", "caBGA256", "caBGA381", "caBGA554", "caBGA756"],
@@ -48,7 +48,7 @@
"max_row" : 50,
"max_col" : 72,
"col_bias" : 0,
- "fuzz": 1
+ "fuzz": 0
},
"LFE5UM-45F": {
"packages": ["csfBGA285", "caBGA256", "caBGA381", "caBGA554", "caBGA756"],
@@ -60,7 +60,7 @@
"max_row" : 71,
"max_col" : 90,
"col_bias" : 0,
- "fuzz": 1
+ "fuzz": 0
},
"LFE5UM-85F": {
"packages": ["csfBGA285", "caBGA381", "caBGA554", "caBGA756"],
@@ -72,7 +72,7 @@
"max_row" : 95,
"max_col" : 126,
"col_bias" : 0,
- "fuzz": 1
+ "fuzz": 0
},
"LFE5UM5G-25F": {
"packages": ["csfBGA285", "caBGA256", "caBGA381", "caBGA554", "caBGA756"],
@@ -84,7 +84,7 @@
"max_row" : 50,
"max_col" : 72,
"col_bias" : 0,
- "fuzz": 1
+ "fuzz": 0
},
"LFE5UM5G-45F": {
"packages": ["csfBGA285", "caBGA256", "caBGA381", "caBGA554", "caBGA756"],
@@ -96,7 +96,7 @@
"max_row" : 71,
"max_col" : 90,
"col_bias" : 0,
- "fuzz": 1
+ "fuzz": 0
},
"LFE5UM5G-85F": {
"packages": ["csfBGA285", "caBGA381", "caBGA554", "caBGA756"],
@@ -108,6 +108,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/diamond.sh b/diamond.sh
index b23aff5..97928e4 100755
--- a/diamond.sh
+++ b/diamond.sh
@@ -17,15 +17,40 @@
# You need to set the DIAMONDDIR environment variable to the path where you have
# installed Lattice Diamond, unless it matches this default.
-diamonddir="${DIAMONDDIR:-/usr/local/diamond/3.10_x64}"
+if [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then
+ WINDOWS=true
+else
+ WINDOWS=false
+fi
+
+if $WINDOWS; then
+ diamonddir="${DIAMONDDIR:-/c/lscc/diamond/3.10_x64}"
+else
+ diamonddir="${DIAMONDDIR:-/usr/local/diamond/3.10_x64}"
+fi
export FOUNDRY="${diamonddir}/ispfpga"
-bindir="${diamonddir}/bin/lin64"
+
+if $WINDOWS; then
+ bindir="${diamonddir}/bin/nt64"
+else
+ bindir="${diamonddir}/bin/lin64"
+fi
LSC_DIAMOND=true
export LSC_DIAMOND
export NEOCAD_MAXLINEWIDTH=32767
export TCL_LIBRARY="${diamonddir}/tcltk/lib/tcl8.5"
-export fpgabindir=${FOUNDRY}/bin/lin64
-export LD_LIBRARY_PATH="${bindir}:${fpgabindir}"
+
+if $WINDOWS; then
+ export fpgabindir=${FOUNDRY}/bin/nt64
+else
+ export fpgabindir=${FOUNDRY}/bin/lin64
+fi
+
+if $WINDOWS; then
+ export PATH="${bindir}:${fpgabindir}:$PATH"
+else
+ export LD_LIBRARY_PATH="${bindir}:${fpgabindir}"
+fi
export LM_LICENSE_FILE="${diamonddir}/license/license.dat"
set -ex
@@ -92,6 +117,16 @@
LSE_ARCH="ECP5UM5G"
;;
+ LCMXO2-256HC)
+ PACKAGE="${DEV_PACKAGE:-QFN32}"
+ DEVICE="LCMXO2-256HC"
+ LSE_ARCH="MachXO2"
+ ;;
+ LCMXO2-1200HC)
+ PACKAGE="${DEV_PACKAGE:-QFN32}"
+ DEVICE="LCMXO2-1200HC"
+ LSE_ARCH="MachXO2"
+ ;;
LCMXO2-2000HC)
PACKAGE="${DEV_PACKAGE:-TQFP100}"
DEVICE="LCMXO2-2000HC"
@@ -134,7 +169,12 @@
touch input.lpf
if [ -n "$USE_NCL" ]; then
-"$FOUNDRY"/userware/unix/bin/lin64/ncl2ncd input.ncl -drc -o par_impl.ncd
+
+if $WINDOWS; then
+ "$FOUNDRY"/userware/NT/bin/nt64/ncl2ncd input.ncl -drc -o par_impl.ncd
+else
+ "$FOUNDRY"/userware/unix/bin/lin64/ncl2ncd input.ncl -drc -o par_impl.ncd
+fi
if test -f "input.prf"; then
cp "input.prf" "synth_impl.prf"
@@ -142,6 +182,7 @@
touch synth_impl.prf
fi
+
else
cat > impl_lse.prj << EOT
#device
@@ -191,6 +232,9 @@
fi
+# Forcefully disable compression
+echo "SYSCONFIG COMPRESS_CONFIG=OFF ;" >> synth_impl.prf
+
# make bitmap
"$fpgabindir"/bitgen -d par_impl.ncd $BITARGS output.bit synth_impl.prf
@@ -202,9 +246,14 @@
"$fpgabindir"/bstool -t output.bit > output.test
# convert ngd to ncl
-"$FOUNDRY"/userware/unix/bin/lin64/ncd2ncl par_impl.ncd output.ncl
+if $WINDOWS; then
+ "$FOUNDRY"/userware/NT/bin/nt64/ncd2ncl par_impl.ncd output.ncl
+else
+ "$FOUNDRY"/userware/unix/bin/lin64/ncd2ncl par_impl.ncd output.ncl
+fi
fi
+
if [ -z "$NO_TRCE" ]; then
# run trce
"$fpgabindir"/trce -v -u -c par_impl.ncd
@@ -228,4 +277,4 @@
fi
if [ -n "$BACKANNO" ]; then
cp "$2.tmp"/par_impl.sdf "$2.sdf"
-fi
\ No newline at end of file
+fi
diff --git a/diamond_tcl.sh b/diamond_tcl.sh
index e7172b6..5dac379 100755
--- a/diamond_tcl.sh
+++ b/diamond_tcl.sh
@@ -1,14 +1,44 @@
#!/bin/bash
-# Script to start a Diamond ispTcl consoke
-diamonddir="${DIAMONDDIR:-/usr/local/diamond/3.10_x64}"
+# Script to start a Diamond ispTcl console
+if [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then
+ WINDOWS=true
+else
+ WINDOWS=false
+fi
+
+if $WINDOWS; then
+ diamonddir="${DIAMONDDIR:-/c/lscc/diamond/3.10_x64}"
+else
+ diamonddir="${DIAMONDDIR:-/usr/local/diamond/3.10_x64}"
+fi
export FOUNDRY="${diamonddir}/ispfpga"
-bindir="${diamonddir}/bin/lin64"
+
+if $WINDOWS; then
+ bindir="${diamonddir}/bin/nt64"
+else
+ bindir="${diamonddir}/bin/lin64"
+fi
LSC_DIAMOND=true
export LSC_DIAMOND
export NEOCAD_MAXLINEWIDTH=32767
export TCL_LIBRARY="${diamonddir}/tcltk/lib/tcl8.5"
-export fpgabindir=${FOUNDRY}/bin/lin64
-export LD_LIBRARY_PATH="${bindir}:${fpgabindir}"
+
+if $WINDOWS; then
+ export fpgabindir=${FOUNDRY}/bin/nt64
+else
+ export fpgabindir=${FOUNDRY}/bin/lin64
+fi
+
+if $WINDOWS; then
+ export PATH="${bindir}:${fpgabindir}:$PATH"
+else
+ export LD_LIBRARY_PATH="${bindir}:${fpgabindir}"
+fi
export LM_LICENSE_FILE="${diamonddir}/license/license.dat"
-$FOUNDRY/userware/unix/bin/lin64/ispTcl $1
+
+if $WINDOWS; then
+ $FOUNDRY/userware/NT/bin/nt64/ispTcl $1
+else
+ $FOUNDRY/userware/unix/bin/lin64/ispTcl $1
+fi
diff --git a/experiments/machxo2/findnets/findnets.py b/experiments/machxo2/findnets/findnets.py
new file mode 100644
index 0000000..603ec37
--- /dev/null
+++ b/experiments/machxo2/findnets/findnets.py
@@ -0,0 +1,35 @@
+import sys
+from collections import defaultdict
+
+from fuzzconfig import FuzzConfig
+import pytrellis
+import isptcl
+
+
+def main(row, col):
+ pytrellis.load_database("../../../database")
+
+ cfg = FuzzConfig(job="FINDNETS_R{}C{}".format(row, col), family="MachXO2", device="LCMXO2-1200HC", ncl="plc2route.ncl", tiles=[])
+ cfg.setup()
+
+ netdata = isptcl.get_wires_at_position(cfg.ncd_prf, (row, col))
+ netnames = [x[0] for x in netdata]
+ arcs = isptcl.get_arcs_on_wires(cfg.ncd_prf, netnames, False, defaultdict(lambda : str("mark")))
+
+ ambiguous_arcs = list()
+ for (k, v) in arcs.items():
+ for c in v:
+ if isinstance(c, isptcl.AmbiguousArc):
+ # ISPTcl always puts queried net on RHS
+ ambiguous_arcs.append(c)
+
+ with open("r{}c{}.txt".format(row, col), "w") as fp:
+ for a in ambiguous_arcs:
+ fp.write(str(a) + "\n")
+
+
+if __name__ == "__main__":
+ if len(sys.argv) < 3:
+ print("Usage: {} [row] [col]".format(sys.argv[0]))
+ else:
+ main(sys.argv[1], sys.argv[2])
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/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/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/040-center-ebr-cib/center-ebr-cib_1200.ncl b/fuzzers/machxo2/040-center-ebr-cib/center-ebr-cib_1200.ncl
new file mode 100644
index 0000000..3f9d75c
--- /dev/null
+++ b/fuzzers/machxo2/040-center-ebr-cib/center-ebr-cib_1200.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/040-center-ebr-cib/fuzzer.py b/fuzzers/machxo2/040-center-ebr-cib/fuzzer.py
new file mode 100644
index 0000000..4f60e93
--- /dev/null
+++ b/fuzzers/machxo2/040-center-ebr-cib/fuzzer.py
@@ -0,0 +1,125 @@
+from collections import defaultdict
+
+from fuzzconfig import FuzzConfig
+import interconnect
+import nets
+import pytrellis
+import re
+
+
+def mk_numbered_nets(net_list, range_iter):
+ return [n.format(i) for i in range_iter for n in net_list]
+
+# 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.
+def get_first_concat_nets_input():
+ nets_01 = mk_numbered_nets(["R6C13_JPCLKCIBLLQ{0}",
+ "R6C13_JPCLKCIBLRQ{0}",
+ "R6C13_JPCLKCIBVIQB{0}",
+ "R6C13_JECLKCIBB{0}",
+ "R6C13_JECLKCIBT{0}",
+ "R6C13_JPCLKCIBVIQT{0}"], range(2))
+ nets_23 = mk_numbered_nets(["R6C13_JPCLKCIBMID{0}"], range(2,4))
+ return nets_01 + nets_23
+
+# Required to indicate fixed connections in the database.
+def get_input_nets():
+ # The "second" net of the concatenated inputs.
+ def get_second_concat_nets_input():
+ nets_01 = mk_numbered_nets(["R6C13_PCLKCIBLLQ{0}",
+ "R6C13_PCLKCIBLRQ{0}",
+ "R6C13_PCLKCIBVIQB{0}",
+ "R6C13_ECLKCIBB{0}",
+ "R6C13_ECLKCIBT{0}",
+ "R6C13_PCLKCIBVIQT{0}"], range(2))
+ nets_23 = mk_numbered_nets(["R6C13_PCLKCIBMID{0}"], range(2,4))
+ return nets_01 + nets_23
+
+ # All other inputs
+ nets_07 = mk_numbered_nets(["R6C13_JSNETCIBMID{0}"], range(8))
+ nets_01 = mk_numbered_nets(["R6C13_JSNETCIBL{0}",
+ "R6C13_JPCLKT2{0}",
+ "R6C13_JBCDIVX{0}",
+ "R6C13_JBCDIV1{0}",
+ "R6C13_JTCDIVX{0}",
+ "R6C13_JTCDIV1{0}",
+ "R6C13_JPCLKT0{0}",
+ "R6C13_JSNETCIBT{0}",
+ "R6C13_JSNETCIBR{0}",
+ "R6C13_JSNETCIBB{0}"], range(2))
+ nets_03 = mk_numbered_nets(["R6C13_JLPLLCLK{0}"], range(4))
+ nets_02 = mk_numbered_nets(["R6C13_JPCLKT3{0}"], range(3))
+ nets_0 = ["R6C13_JPCLKT10"]
+
+ return get_second_concat_nets_input() + nets_07 + nets_01 + nets_03 + nets_02 + nets_0
+
+# 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.
+def get_fixed_nets_output():
+ nets_01 = mk_numbered_nets(["R6C13_CLK{0}_6_DCM",
+ "R6C13_CLK{0}_7_DCM",
+ "R6C13_CLK{0}_1_ECLKBRIDGECS",
+ "R6C13_CLK{0}_0_ECLKBRIDGECS",
+ "R6C13_JSEL{0}_ECLKBRIDGECS"], range(2))
+ nets_07 = mk_numbered_nets(["R6C13_CLKI{0}_DCC",
+ # "R6C13_CLKO{0}_DCC", # XXX: I suspect these
+ # are gated by a tristate or a mux.
+ "R6C13_JCE{0}_DCC"], range(8))
+ nets_67 = mk_numbered_nets(["R6C13_JSEL{0}_DCM"], range(6,8))
+ return nets_01 + nets_07 + nets_67
+
+
+cfg = FuzzConfig(job="CIB_1200", family="MachXO2", device="LCMXO2-1200HC", ncl="center-ebr-cib_1200.ncl",
+ tiles=["CENTER6:CENTER_EBR_CIB", "CENTER8:CENTER7", "CENTER9:CENTER8"])
+
+def main():
+ pytrellis.load_database("../../../database")
+ cfg.setup()
+
+ # TODO: Add fc_prefix=job["prefix"] argument.
+
+ interconnect.fuzz_interconnect_with_netnames(config=cfg, netnames=get_first_concat_nets_input(),
+ netname_filter_union=False,
+ netdir_override=defaultdict(lambda : str("sink")),
+ full_mux_style=True,
+ bias=1)
+
+ interconnect.fuzz_interconnect_with_netnames(config=cfg, netnames=get_input_nets(),
+ netname_filter_union=False,
+ netdir_override=defaultdict(lambda : str("sink")),
+ full_mux_style=True,
+ bias=1)
+
+ interconnect.fuzz_interconnect_with_netnames(config=cfg, netnames=get_fixed_nets_output(),
+ netname_filter_union=False,
+ netdir_override=defaultdict(lambda : str("sink")),
+ full_mux_style=True,
+ bias=1)
+
+ # Test fuzzers follow below.
+ # interconnect.fuzz_interconnect_with_netnames(config=cfg, netnames=["R6C13_PCLKCIBVIQT0", "R6C13_JPCLKCIBVIQT0", "R6C13_VPRXCLKI0", "R6C13_CLKI0_DCC"],
+ # netname_filter_union=False,
+ # netdir_override = {
+ # "R6C13_JPCLKCIBVIQT0" : "sink",
+ # },
+ # full_mux_style=True,
+ # bias=1)
+
+ # TODO: R6C13_JA0 --> R6C13_JCE0_DCC. But TCL also claims
+ # R6C13_CLKI0_DCC --> R6C13_CLKO0_DCC (pseudo = 1). Contradiction?
+ # 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/050-pio_routing/fuzzer.py b/fuzzers/machxo2/050-pio_routing/fuzzer.py
new file mode 100644
index 0000000..0d5f27a
--- /dev/null
+++ b/fuzzers/machxo2/050-pio_routing/fuzzer.py
@@ -0,0 +1,123 @@
+from collections import defaultdict
+from itertools import product
+
+from fuzzconfig import FuzzConfig
+import interconnect
+import nets
+import pytrellis
+import re
+
+import isptcl
+
+# Hint: F/Q are sinks for "driver"s, A-D are sources for "sinks".
+# Bottom Fuzzing
+b_overrides = dict()
+for (n, r, d) in [(["R12C9_JRXDA{}_BIOLOGIC"], range(0,8), "driver"),
+ (["R12C9_JDI{}",
+ "R12C9_JIN{}_IOLOGIC"], nets.char_range("A","E"), "driver"),
+ (["R12C9_JPADDO{}",
+ "R12C9_JPADDT{}"], nets.char_range("A","E"), "driver"),
+ (["R12C9_JRXD{}A_BIOLOGIC",
+ "R12C9_JRXD{}C_BSIOLOGIC"], range(0,4), "driver"),
+ (["R12C9_JDEL{}A_BIOLOGIC",
+ "R12C9_JDEL{}C_BSIOLOGIC"], range(0,5), "sink"),
+ (["R12C9_JI{}A_BIOLOGIC",
+ "R12C9_JI{}B_IOLOGIC",
+ "R12C9_JI{}C_BSIOLOGIC",
+ "R12C9_JI{}D_IOLOGIC"], ["N", "P"], "sink"),
+ (["R12C9_JOPOS{}",
+ "R12C9_JONEG{}",
+ "R12C9_JTS{}",
+ "R12C9_JCLK{}",
+ "R12C9_JLSR{}",
+ "R12C9_JCE{}"], ["A_BIOLOGIC",
+ "B_IOLOGIC",
+ "C_BSIOLOGIC",
+ "D_IOLOGIC"], "sink"),
+ (["R12C9_JSLIP{}"], ["A_BIOLOGIC",
+ "C_BSIOLOGIC"], "sink")
+ ]:
+ for p in nets.net_product(n, r):
+ b_overrides[p] = d
+
+def nn_filterb(net, netnames):
+ return not nets.is_cib(net)
+
+
+# Left and Right are done from CIB's POV because
+# there are no tiles dedicated strictly to I/O connections.
+# Ignore loopback/CIBTEST nets.
+l_overrides = defaultdict(lambda : str("ignore"))
+
+# grep "R10C1_J.*" r10c1.txt | grep -v "CIBTEST" | sort | less
+for (n, r, d) in [(["R10C1_JA{}",
+ "R10C1_JB{}",
+ "R10C1_JC{}",
+ "R10C1_JCLK{}",
+ "R10C1_LSR{}",
+ "R10C1_JCE{}"], range(0,4), "driver"),
+ (["R10C1_JQ{}"], range(0,4), "sink"),
+ (["R10C1_JF{}"], range(0,8), "sink")
+ ]:
+ for p in nets.net_product(n, r):
+ l_overrides[p] = d
+
+span1_re = re.compile(r'R\d+C\d+_[VH]01[NESWTLBR]\d{4}')
+def nn_filterl(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)
+
+
+jobs = [
+ {
+ "pos" : [(12, 9)],
+ "cfg" : FuzzConfig(job="PIOROUTEB", family="MachXO2", device="LCMXO2-1200HC", ncl="pioroute.ncl",
+ tiles=["PB9:PIC_B0"]),
+ "nn_filter" : nn_filterb,
+ "netnames_override" : b_overrides,
+ },
+
+ {
+ "pos" : [(10, 1)],
+ "cfg" : FuzzConfig(job="PIOROUTEL", family="MachXO2", device="LCMXO2-1200HC", ncl="pioroute.ncl",
+ tiles=["PL10:PIC_L0"]),
+ "nn_filter" : nn_filterl,
+ "netnames_override" : l_overrides,
+ },
+
+ # Probably the same thing as PIC_L0 plus some additional fixed connections?
+ {
+ "pos" : [(11, 1)],
+ "cfg" : FuzzConfig(job="PIOROUTEL", family="MachXO2", device="LCMXO2-1200HC", ncl="pioroute.ncl",
+ tiles=["PL11:LLC0"]),
+ "nn_filter" : nn_filterl,
+ "netnames_override" : l_overrides,
+ },
+
+ {
+ "pos" : [(10, 22)],
+ "cfg" : FuzzConfig(job="PIOROUTER", family="MachXO2", device="LCMXO2-1200HC", ncl="pioroute.ncl",
+ tiles=["PR10:PIC_R0"]),
+ "nn_filter" : nn_filterl,
+ "netnames_override" : l_overrides,
+ },
+]
+
+def main():
+ pytrellis.load_database("../../../database")
+ for job in jobs:
+ cfg = job["cfg"]
+ cfg.setup()
+
+ for pos in job["pos"]:
+ interconnect.fuzz_interconnect(config=cfg, location=pos,
+ netname_predicate=job["nn_filter"],
+ netdir_override=job["netnames_override"],
+ netname_filter_union=False,
+ enable_span1_fix=True,
+ bias=1)
+
+
+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/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/libtrellis/.gitignore b/libtrellis/.gitignore
index 245392f..ad1dcd0 100644
--- a/libtrellis/.gitignore
+++ b/libtrellis/.gitignore
@@ -18,6 +18,7 @@
install_manifest.txt
ecppack
ecpunpack
+ecpmulti
ecppll
libtrellis.dylib
*~
diff --git a/libtrellis/include/Tile.hpp b/libtrellis/include/Tile.hpp
index 4c5059f..7d87bf0 100644
--- a/libtrellis/include/Tile.hpp
+++ b/libtrellis/include/Tile.hpp
@@ -1,6 +1,7 @@
#ifndef LIBTRELLIS_TILE_HPP
#define LIBTRELLIS_TILE_HPP
+#include <iostream>
#include <string>
#include <iostream>
#include <cstdint>
diff --git a/libtrellis/src/Tile.cpp b/libtrellis/src/Tile.cpp
index 2b3345a..ba0ef69 100644
--- a/libtrellis/src/Tile.cpp
+++ b/libtrellis/src/Tile.cpp
@@ -6,17 +6,50 @@
#include "Util.hpp"
namespace Trellis {
-// Regex to extract row/column from a tile name
+// Regexes to extract row/column from a tile name.
static const regex tile_rxcx_re(R"(R(\d+)C(\d+))");
+// MachXO2-specific, in order of precedence (otherwise, e.g.
+// CENTER_EBR matches r_regex)
+static const regex tile_center_re(R"(CENTER(\d+))");
+static const regex tile_centerb_re(R"(CENTER_B)");
+static const regex tile_centert_re(R"(CENTER_T)");
+static const regex tile_centerebr_re(R"(CENTER_EBR(\d+))");
+static const regex tile_t_re(R"([A-Za-z0-9_]*T(\d+))");
+static const regex tile_b_re(R"([A-Za-z0-9_]*B(\d+))");
+static const regex tile_l_re(R"([A-Za-z0-9_]*L(\d+))");
+static const regex tile_r_re(R"([A-Za-z0-9_]*R(\d+))");
+
+// Given the zero-indexed max chip_size, return the zero-indexed
+// center. Mainly for MachXO2.
+// TODO: Make const.
+map<pair<int, int>, pair<int, int>> center_map = {
+ {make_pair(12, 21), make_pair(6, 12)}
+};
+
// Universal function to get a zero-indexed row/column pair.
pair<int, int> get_row_col_pair_from_chipsize(string name, pair<int, int> chip_size, int bias) {
smatch m;
- bool match;
- match = regex_search(name, m, tile_rxcx_re);
- if(match) {
- return make_pair(stoi(m.str(1)), stoi(m.str(2)));
+ if(regex_search(name, m, tile_rxcx_re)) {
+ return make_pair(stoi(m.str(1)), stoi(m.str(2)) - bias);
+ } else if(regex_search(name, m, tile_centert_re)) {
+ return make_pair(0, center_map[chip_size].second);
+ } else if(regex_search(name, m, tile_centerb_re)) {
+ return make_pair(chip_size.first, center_map[chip_size].second);
+ } else if(regex_search(name, m, tile_centerebr_re)) {
+ // TODO: This may not apply to devices larger than 1200.
+ return make_pair(center_map[chip_size].first, stoi(m.str(1)) - bias);
+ } else if(regex_search(name, m, tile_center_re)) {
+ return make_pair(stoi(m.str(1)), center_map[chip_size].second);
+ } else if(regex_search(name, m, tile_t_re)) {
+ return make_pair(0, stoi(m.str(1)) - bias);
+ } else if(regex_search(name, m, tile_b_re)) {
+ return make_pair(chip_size.first, stoi(m.str(1)) - bias);
+ } else if(regex_search(name, m, tile_l_re)) {
+ return make_pair(stoi(m.str(1)), 0);
+ } else if(regex_search(name, m, tile_r_re)) {
+ return make_pair(stoi(m.str(1)), chip_size.second);
} else {
throw runtime_error(fmt("Could not extract position from " << name));
}
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/hello/hello-machxo2.v b/minitests/hello/hello-machxo2.v
new file mode 100644
index 0000000..49ed054
--- /dev/null
+++ b/minitests/hello/hello-machxo2.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/tools/connectivity.py b/tools/connectivity.py
index 66fe964..04b53b7 100755
--- a/tools/connectivity.py
+++ b/tools/connectivity.py
@@ -1,8 +1,11 @@
#!/usr/bin/env python3
+import sys
import pytrellis
import nets
import tiles
import database
+if sys.platform in ("win32"):
+ import pyreadline.rlmain
import readline
import re
@@ -20,56 +23,58 @@
pytrellis.load_database(database.get_db_root())
c = pytrellis.Chip("LFE5U-45F")
chip_size = (c.get_max_row(), c.get_max_col())
+ bias = c.info.col_bias
+
# Get fan-in to a net
# Returns (source, configurable, loc)
def get_fanin(net):
drivers = []
- npos = tiles.pos_from_name(net, chip_size, 0)
+ npos = tiles.pos_from_name(net, chip_size, bias)
for tile in c.get_all_tiles():
tinf = tile.info
tname = tinf.name
- pos = tiles.pos_from_name(tname, chip_size, 0)
+ pos = tiles.pos_from_name(tname, chip_size, bias)
if abs(pos[0] - npos[0]) >= 10 or abs(pos[1] - npos[1]) >= 10:
continue
if net.startswith("G_"):
tnet = net
else:
- tnet = nets.normalise_name(chip_size, tname, net, 0)
+ tnet = nets.normalise_name(chip_size, tname, net, bias)
tdb = pytrellis.get_tile_bitdata(pytrellis.TileLocator(c.info.family, c.info.name, tinf.type))
try:
mux = tdb.get_mux_data_for_sink(tnet)
for src in mux.get_sources():
- drivers.append((nets.canonicalise_name(chip_size, tname, src, 0), True, tname))
+ drivers.append((nets.canonicalise_name(chip_size, tname, src, bias), True, tname))
except IndexError:
pass
for fc in tdb.get_fixed_conns():
if fc.sink == tnet:
- drivers.append((nets.canonicalise_name(chip_size, tname, fc.source, 0), False, tname))
+ drivers.append((nets.canonicalise_name(chip_size, tname, fc.source, bias), False, tname))
return drivers
# Get fan-out of a net
# Returns (dest, configurable, loc)
def get_fanout(net):
drivers = []
- npos = tiles.pos_from_name(net, chip_size, 0)
+ npos = tiles.pos_from_name(net, chip_size, bias)
for tile in c.get_all_tiles():
tinf = tile.info
tname = tinf.name
- pos = tiles.pos_from_name(tname, chip_size, 0)
+ pos = tiles.pos_from_name(tname, chip_size, bias)
if abs(pos[0] - npos[0]) >= 12 or abs(pos[1] - npos[1]) >= 12:
continue
if net.startswith("G_"):
tnet = net
else:
- tnet = nets.normalise_name(chip_size, tname, net, 0)
+ tnet = nets.normalise_name(chip_size, tname, net, bias)
tdb = pytrellis.get_tile_bitdata(pytrellis.TileLocator(c.info.family, c.info.name, tinf.type))
for sink in tdb.get_sinks():
mux = tdb.get_mux_data_for_sink(sink)
if tnet in mux.arcs:
- drivers.append((nets.canonicalise_name(chip_size, tname, sink, 0), True, tname))
+ drivers.append((nets.canonicalise_name(chip_size, tname, sink, bias), True, tname))
for fc in tdb.get_fixed_conns():
if fc.source == tnet:
- drivers.append((nets.canonicalise_name(chip_size, tname, fc.sink, 0), False, tname))
+ drivers.append((nets.canonicalise_name(chip_size, tname, fc.sink, bias), False, tname))
return drivers
@@ -105,7 +110,7 @@
def completer(str, idx):
if not tile_net_re.match(str):
return None
- loc = tiles.pos_from_name(str, chip_size, 0)
+ loc = tiles.pos_from_name(str, chip_size, bias)
nets = get_nets_at(loc)
for n in nets:
if n.startswith(str):
diff --git a/tools/demobuilder/design.py b/tools/demobuilder/design.py
index d79f53f..a57c781 100644
--- a/tools/demobuilder/design.py
+++ b/tools/demobuilder/design.py
@@ -6,6 +6,7 @@
class Design:
def __init__(self, family):
self.chip = pytrellis.Chip("LFE5U-45F")
+ self.bias = self.chip.info.col_bias
self.router = route.Autorouter(self.chip)
self.config = {_.info.name: pytrellis.TileConfig() for _ in self.chip.get_all_tiles()}
# TODO: load skeleton config
@@ -33,7 +34,7 @@
tinf = tile.info
tname = tinf.name
chip_size = (self.chip.get_max_row(), self.chip.get_max_col())
- pos = tiles.pos_from_name(tname, chip_size, 0)
+ pos = tiles.pos_from_name(tname, chip_size, self.bias)
if tinf.type == "PLC2":
for loc in ("A", "B", "C", "D"):
bel = "R{}C{}{}".format(pos[0], pos[1], loc)
@@ -62,7 +63,7 @@
beltype, belloc = self.bels[bel]
tile, loc = belloc
chip_size = (self.chip.get_max_row(), self.chip.get_max_col())
- pos = tiles.pos_from_name(tile, chip_size, 0)
+ pos = tiles.pos_from_name(tile, chip_size, self.bias)
net_prefix = "R{}C{}".format(pos[0], pos[1])
slice_index = "ABCD".index(loc)
lc0 = 2 * slice_index
diff --git a/tools/demobuilder/route.py b/tools/demobuilder/route.py
index 788ad17..36975f5 100755
--- a/tools/demobuilder/route.py
+++ b/tools/demobuilder/route.py
@@ -11,6 +11,7 @@
def __init__(self, chip):
self.chip = chip
self.chip_size = (self.chip.get_max_row(), self.chip.get_max_col())
+ self.bias = self.chip.info.col_bias
self.dh_arc_cache = {}
self.net_to_wire = {}
self.wire_to_net = {}
@@ -24,7 +25,7 @@
drivers = []
chip_size = (self.chip.get_max_row(), self.chip.get_max_col())
try:
- npos = tiles.pos_from_name(wire, chip_size, 0)
+ npos = tiles.pos_from_name(wire, chip_size, self.bias)
except AssertionError:
return []
wname = wire.split("_", 1)[1]
@@ -42,20 +43,20 @@
tname = tinf.name
if tname.startswith("TAP"):
continue
- pos = tiles.pos_from_name(tname, chip_size, 0)
+ pos = tiles.pos_from_name(tname, chip_size, self.bias)
if abs(pos[0] - npos[0]) not in (vspan, 0) or abs(pos[1] - npos[1]) not in (hspan, 0):
continue
if wire.startswith("G_"):
twire = wire
else:
- twire = nets.normalise_name(self.chip_size, tname, wire, 0)
+ twire = nets.normalise_name(self.chip_size, tname, wire, self.bias)
tdb = pytrellis.get_tile_bitdata(
pytrellis.TileLocator(self.chip.info.family, self.chip.info.name, tinf.type))
downhill = tdb.get_downhill_wires(twire)
for sink in downhill:
- nn = nets.canonicalise_name(self.chip_size, tname, sink.first, 0)
+ nn = nets.canonicalise_name(self.chip_size, tname, sink.first, self.bias)
if nn is not None:
drivers.append((nn, sink.second, tname))
self.dh_arc_cache[wire] = drivers
@@ -73,8 +74,8 @@
else:
self.net_to_wire[net] = {dest_wire}
if configurable and not exists:
- src_wirename = nets.normalise_name(self.chip_size, tile, uphill_wire, 0)
- sink_wirename = nets.normalise_name(self.chip_size, tile, dest_wire, 0)
+ src_wirename = nets.normalise_name(self.chip_size, tile, uphill_wire, self.bias)
+ sink_wirename = nets.normalise_name(self.chip_size, tile, dest_wire, self.bias)
config[tile].add_arc(sink_wirename, src_wirename)
# Bind a net to a wire (used for port connections)
@@ -90,9 +91,9 @@
def route_net_to_wire(self, net, wire, config):
print(" Routing net '{}' to wire/pin '{}'...".format(net, wire))
chip_size = (self.chip.get_max_row(), self.chip.get_max_col())
- dest_pos = tiles.pos_from_name(wire, chip_size, 0)
+ dest_pos = tiles.pos_from_name(wire, chip_size, self.bias)
def get_score(x_wire):
- pos = tiles.pos_from_name(x_wire, chip_size, 0)
+ pos = tiles.pos_from_name(x_wire, chip_size, self.bias)
score = abs(pos[0] - dest_pos[0]) + abs(pos[1] - dest_pos[1])
x_wname = x_wire.split("_", 1)[1]
if x_wname[1:3].isdigit() and score > 3:
diff --git a/tools/extract_tilegrid.py b/tools/extract_tilegrid.py
index 1411d66..08686b8 100755
--- a/tools/extract_tilegrid.py
+++ b/tools/extract_tilegrid.py
@@ -25,6 +25,8 @@
r'^\s+([A-Z0-9_]+) \((-?\d+), (-?\d+)\)')
parser = argparse.ArgumentParser(description=__doc__)
+parser.add_argument('-m', action="store_true",
+ help="Use MachXO2 family layout")
parser.add_argument('infile', type=argparse.FileType('r'),
help="input file from bstool")
parser.add_argument('outfile', type=argparse.FileType('w'),
@@ -38,14 +40,24 @@
tile_m = tile_re.match(line)
if tile_m:
name = tile_m.group(6)
- current_tile = {
- "type": tile_m.group(1),
- "start_bit": int(tile_m.group(4)),
- "start_frame": int(tile_m.group(5)),
- "rows": int(tile_m.group(2)),
- "cols": int(tile_m.group(3)),
- "sites": []
- }
+ if args.m:
+ current_tile = {
+ "type": tile_m.group(1),
+ "start_bit": int(tile_m.group(5)),
+ "start_frame": int(tile_m.group(4)),
+ "rows": int(tile_m.group(3)),
+ "cols": int(tile_m.group(2)),
+ "sites": []
+ }
+ else:
+ current_tile = {
+ "type": tile_m.group(1),
+ "start_bit": int(tile_m.group(4)),
+ "start_frame": int(tile_m.group(5)),
+ "rows": int(tile_m.group(2)),
+ "cols": int(tile_m.group(3)),
+ "sites": []
+ }
identifier = name + ":" + tile_m.group(1)
assert identifier not in tiles
tiles[identifier] = current_tile
diff --git a/tools/get_tilegrid_all.py b/tools/get_tilegrid_all.py
index 5e687b9..53bc1f2 100755
--- a/tools/get_tilegrid_all.py
+++ b/tools/get_tilegrid_all.py
@@ -21,10 +21,14 @@
devices = database.get_devices()
for family in sorted(devices["families"].keys()):
for device in sorted(devices["families"][family]["devices"].keys()):
- diamond.run(device, "work_tilegrid/wire.v")
- output_file = path.join(database.get_db_subdir(family, device), "tilegrid.json")
- extract_tilegrid.main(["extract_tilegrid", "work_tilegrid/wire.tmp/output.test", output_file])
+ if devices["families"][family]["devices"][device]["fuzz"]:
+ diamond.run(device, "work_tilegrid/wire.v")
+ output_file = path.join(database.get_db_subdir(family, device), "tilegrid.json")
+ if family in ["MachXO2"]:
+ extract_tilegrid.main(["extract_tilegrid", "-m", "work_tilegrid/wire.tmp/output.test", output_file])
+ else:
+ extract_tilegrid.main(["extract_tilegrid", "work_tilegrid/wire.tmp/output.test", output_file])
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()
diff --git a/tools/html_all.py b/tools/html_all.py
index 8ada8eb..42edfcd 100755
--- a/tools/html_all.py
+++ b/tools/html_all.py
@@ -84,8 +84,6 @@
docs_toc = ""
pytrellis.load_database(database.get_db_root())
for fam, fam_data in sorted(database.get_devices()["families"].items()):
- if fam == "MachXO2":
- continue
fdir = path.join(args.fld, fam)
if not path.exists(fdir):
os.mkdir(fdir)
@@ -112,33 +110,35 @@
dev
)
- docs_toc += "</ul>"
- docs_toc += "<h4>Cell Timing Documentation</h4>"
- docs_toc += "<ul>"
- for spgrade in ["6", "7", "8", "8_5G"]:
- tdir = path.join(fdir, "timing")
- if not path.exists(tdir):
- os.mkdir(tdir)
- docs_toc += '<li><a href="{}">Speed Grade -{}</a></li>'.format(
- '{}/timing/cell_timing_{}.html'.format(fam, spgrade),
- spgrade
- )
- cell_html.make_cell_timing_html(timing_dbs.cells_db_path(fam, spgrade), fam, spgrade,
- path.join(tdir, 'cell_timing_{}.html'.format(spgrade)))
- docs_toc += "</ul>"
- docs_toc += "<h4>Interconnect Timing Documentation</h4>"
- docs_toc += "<ul>"
- for spgrade in ["6", "7", "8", "8_5G"]:
- tdir = path.join(fdir, "timing")
- if not path.exists(tdir):
- os.mkdir(tdir)
- docs_toc += '<li><a href="{}">Speed Grade -{}</a></li>'.format(
- '{}/timing/interconn_timing_{}.html'.format(fam, spgrade),
- spgrade
- )
- interconnect_html.make_interconn_timing_html(timing_dbs.interconnect_db_path(fam, spgrade), fam, spgrade,
- path.join(tdir, 'interconn_timing_{}.html'.format(spgrade)))
- docs_toc += "</ul>"
+ # No timing stuff for MachXO2 yet.
+ if fam in ["ECP5"]:
+ docs_toc += "</ul>"
+ docs_toc += "<h4>Cell Timing Documentation</h4>"
+ docs_toc += "<ul>"
+ for spgrade in ["6", "7", "8", "8_5G"]:
+ tdir = path.join(fdir, "timing")
+ if not path.exists(tdir):
+ os.mkdir(tdir)
+ docs_toc += '<li><a href="{}">Speed Grade -{}</a></li>'.format(
+ '{}/timing/cell_timing_{}.html'.format(fam, spgrade),
+ spgrade
+ )
+ cell_html.make_cell_timing_html(timing_dbs.cells_db_path(fam, spgrade), fam, spgrade,
+ path.join(tdir, 'cell_timing_{}.html'.format(spgrade)))
+ docs_toc += "</ul>"
+ docs_toc += "<h4>Interconnect Timing Documentation</h4>"
+ docs_toc += "<ul>"
+ for spgrade in ["6", "7", "8", "8_5G"]:
+ tdir = path.join(fdir, "timing")
+ if not path.exists(tdir):
+ os.mkdir(tdir)
+ docs_toc += '<li><a href="{}">Speed Grade -{}</a></li>'.format(
+ '{}/timing/interconn_timing_{}.html'.format(fam, spgrade),
+ spgrade
+ )
+ interconnect_html.make_interconn_timing_html(timing_dbs.interconnect_db_path(fam, spgrade), fam, spgrade,
+ path.join(tdir, 'interconn_timing_{}.html'.format(spgrade)))
+ docs_toc += "</ul>"
index_html = Template(trellis_docs_index).substitute(
datetime=build_dt,
diff --git a/tools/html_tilegrid.py b/tools/html_tilegrid.py
index cc7e683..6b471ff 100755
--- a/tools/html_tilegrid.py
+++ b/tools/html_tilegrid.py
@@ -46,6 +46,7 @@
max_row = device_info["max_row"]
max_col = device_info["max_col"]
+ bias = device_info["col_bias"]
tiles = []
for i in range(max_row + 1):
@@ -56,7 +57,7 @@
for identifier, data in sorted(tilegrid.items()):
name = identifier.split(":")[0]
- row, col = tilelib.pos_from_name(name, (max_row, max_col), 0)
+ row, col = tilelib.pos_from_name(name, (max_row, max_col), bias)
colour = get_colour(data["type"])
tiles[row][col].append((name, data["type"], colour))
diff --git a/util/common/isptcl.py b/util/common/isptcl.py
index 6b0742e..70512c7 100644
--- a/util/common/isptcl.py
+++ b/util/common/isptcl.py
@@ -2,12 +2,34 @@
Interface between Python fuzzer scripts and Lattice Diamond ispTcl
"""
+from collections import defaultdict
+
import database
import subprocess
import tempfile
from os import path
import re
+
+# Arc whose direction is ambiguous "---"
+class AmbiguousArc:
+ # I
+ def __init__(self, lhs, rhs):
+ self.lhs = lhs
+ self.rhs = rhs
+
+ def __getitem__(self, idx):
+ if idx == 0:
+ return self.lhs
+ elif idx == 1:
+ return self.rhs
+ else:
+ raise IndexError("AmbiguousArc only connects two nets")
+
+ def __repr__(self):
+ return "{} --- {}".format(self.lhs, self.rhs)
+
+
def run(commands):
"""Run a list of Tcl commands, returning the output as a string"""
dtcl_path = path.join(database.get_trellis_root(), "diamond_tcl.sh")
@@ -84,7 +106,7 @@
return wires
-def get_arcs_on_wires(desfiles, wires, drivers_only=False):
+def get_arcs_on_wires(desfiles, wires, drivers_only=False, dir_override=dict()):
"""
Use ispTcl to get a list of arcs sinking or sourcing a list of wires
@@ -122,8 +144,29 @@
if not drivers_only:
arcs.append((splitline[2].strip(), splitline[0].strip()))
elif splitline[1].strip() == "---":
- # Edge wires, currently ignored
- pass
+ if isinstance(dir_override, defaultdict):
+ # get() overrides defaultdict behavior, and a user may
+ # have a valid reason to provide a default such as
+ # ignore.
+ override = dir_override[wires[wire_idx]]
+ else:
+ override = dir_override.get(wires[wire_idx], "")
+ if override:
+ if override == "sink":
+ arcs.append((splitline[0].strip(), splitline[2].strip()))
+ elif override == "driver":
+ arcs.append((splitline[2].strip(), splitline[0].strip()))
+ elif override == "mark":
+ arcs.append(AmbiguousArc(splitline[0].strip(), splitline[2].strip()))
+ elif override == "ignore":
+ pass
+ else:
+ assert False, ("invalid override for wire {}".
+ format(wires[wire_idx]))
+ else:
+ assert False, ("'---' found in ispTcl output, and no netdir_override"
+ " was given for {wire}. Full line:\n{line}".
+ format(wire=wires[wire_idx], line=line))
else:
print (splitline)
assert False, "invalid output from Tcl command `dev_list_arcs`"
diff --git a/util/common/nets.py b/util/common/nets.py
index ea9ae18..3a69425 100644
--- a/util/common/nets.py
+++ b/util/common/nets.py
@@ -302,6 +302,16 @@
return "R{}C{}_{}".format(wire_pos[0], wire_pos[1], wire)
+# Useful functions for constructing nets.
+def char_range(c1, c2):
+ """Generates the characters from `c1` to `c2`, exclusive."""
+ for c in range(ord(c1), ord(c2)):
+ yield chr(c)
+
+def net_product(net_list, range_iter):
+ return [n.format(i) for i in range_iter for n in net_list]
+
+
def main():
assert is_global("R2C7_HPBX0100")
assert is_global("R24C12_VPTX0700")
diff --git a/util/fuzz/interconnect.py b/util/fuzz/interconnect.py
index e40045b..9d16da0 100644
--- a/util/fuzz/interconnect.py
+++ b/util/fuzz/interconnect.py
@@ -28,6 +28,7 @@
func_cib=False,
fc_prefix="",
nonlocal_prefix="",
+ netdir_override=dict(),
bias=0):
"""
The fully-automatic interconnect fuzzer function. This performs the fuzzing and updates the database with the
@@ -70,7 +71,7 @@
if func_cib and not netname_filter_union:
netnames = list(filter(lambda x: netname_predicate(x, netnames), netnames))
fuzz_interconnect_with_netnames(config, netnames, netname_predicate, arc_predicate, fc_predicate, func_cib,
- netname_filter_union, False, fc_prefix, nonlocal_prefix, bias)
+ netname_filter_union, False, fc_prefix, nonlocal_prefix, netdir_override, bias)
def fuzz_interconnect_with_netnames(
@@ -84,6 +85,7 @@
full_mux_style=False,
fc_prefix="",
nonlocal_prefix="",
+ netdir_override=dict(),
bias=0):
"""
Fuzz interconnect given a list of netnames to analyse. Arcs associated these netnames will be found using the Tcl
@@ -100,10 +102,13 @@
nets much pass the predicate.
:param full_mux_style: if True, is a full mux, and all 0s is considered a valid config bit possibility
:param fc_prefix: add a prefix to non-global fixed connections for device-specific fuzzers
+ :param netdir_override: Manually specify whether the nets in the dictionary are driven by other nets (`"sink"`,
+ specified as "-->" in ispTcl), or drive other nets (`"driver"`, specified as "<--" in ispTcl). The dictionary is
+ only consulted if ispTcl returns "---" for the direction of a given net.
:param bias: Apply offset correction for n-based column numbering, n > 0. Used used by Lattice
on certain families.
"""
- net_arcs = isptcl.get_arcs_on_wires(config.ncd_prf, netnames, not bidir)
+ net_arcs = isptcl.get_arcs_on_wires(config.ncd_prf, netnames, not bidir, netdir_override)
baseline_bitf = config.build_design(config.ncl, {}, "base_")
baseline_chip = pytrellis.Bitstream.read_bit(baseline_bitf).deserialise_chip()