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