Merge pull request #127 from SymbiFlow/12k
Add '12k' device
diff --git a/diamond.sh b/diamond.sh
index 4a69df2..b5ab3e5 100755
--- a/diamond.sh
+++ b/diamond.sh
@@ -11,27 +11,53 @@
# - lfe5u-85
# - lfe5u-45
# - lfe5u-25
+# - LCMXO2-1200HC
-# Currently this script supports Linux only.
+# Currently this script supports Linux and Windows using a MINGW64 bash shell.
# You need to set the DIAMONDDIR environment variable to the path where you have
# installed Lattice Diamond, unless it matches this default.
+if [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then
+ WINDOWS=true
+else
+ WINDOWS=false
+fi
+
if [ -z "$DIAMONDVER" ]; then
diamondver="3.10"
else
diamondver="$DIAMONDVER"
fi
-diamonddir="${DIAMONDDIR:-/usr/local/diamond/${diamondver}_x64}"
+if $WINDOWS; then
+ diamonddir="${DIAMONDDIR:-/c/lscc/diamond/${diamondver}_x64}"
+else
+ diamonddir="${DIAMONDDIR:-/usr/local/diamond/${diamondver}_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
@@ -98,6 +124,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"
@@ -140,7 +176,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"
@@ -148,6 +189,7 @@
touch synth_impl.prf
fi
+
else
cat > impl_lse.prj << EOT
#device
@@ -197,6 +239,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
@@ -204,6 +249,11 @@
"$fpgabindir"/bitgen -d par_impl.ncd -jedec output.jed synth_impl.prf
fi
+if [ -n "$COMPRESSED_BITSTREAM" ]; then
+ sed 's/COMPRESS_CONFIG=OFF/COMPRESS_CONFIG=ON/' synth_impl.prf > synth_impl_comp.prf
+ "$fpgabindir"/bitgen -d par_impl.ncd $BITARGS output-comp.bit synth_impl_comp.prf
+fi
+
# dump bitmap
"$fpgabindir"/bstool -d output.bit > output.dump
@@ -212,9 +262,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
@@ -239,6 +294,9 @@
if [ -n "$JEDEC_BITSTREAM" ]; then
cp "$2.tmp"/output.jed "$2.jed"
fi
+if [ -n "$COMPRESSED_BITSTREAM" ]; then
+cp "$2.tmp"/output-comp.bit "$2-comp.bit"
+fi
if [ -n "$BACKANNO" ]; then
cp "$2.tmp"/par_impl.sdf "$2.sdf"
fi
diff --git a/diamond_tcl.sh b/diamond_tcl.sh
index e7172b6..0bbb4f6 100755
--- a/diamond_tcl.sh
+++ b/diamond_tcl.sh
@@ -1,14 +1,50 @@
#!/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 [ -z "$DIAMONDVER" ]; then
+ diamondver="3.10"
+else
+ diamondver="$DIAMONDVER"
+fi
+
+if $WINDOWS; then
+ diamonddir="${DIAMONDDIR:-/c/lscc/diamond/${diamondver}_x64}"
+else
+ diamonddir="${DIAMONDDIR:-/usr/local/diamond/${diamondver}_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/interconnect_poc/fuzz_single_mux.py b/experiments/ECP5/interconnect_poc/fuzz_single_mux.py
similarity index 100%
rename from experiments/interconnect_poc/fuzz_single_mux.py
rename to experiments/ECP5/interconnect_poc/fuzz_single_mux.py
diff --git a/experiments/interconnect_poc/mux_template.ncl b/experiments/ECP5/interconnect_poc/mux_template.ncl
similarity index 100%
rename from experiments/interconnect_poc/mux_template.ncl
rename to experiments/ECP5/interconnect_poc/mux_template.ncl
diff --git a/experiments/lut_init/fuzz_lut_init.py b/experiments/ECP5/lut_init/fuzz_lut_init.py
similarity index 100%
rename from experiments/lut_init/fuzz_lut_init.py
rename to experiments/ECP5/lut_init/fuzz_lut_init.py
diff --git a/experiments/lut_init/lut_init_template.ncl b/experiments/ECP5/lut_init/lut_init_template.ncl
similarity index 100%
rename from experiments/lut_init/lut_init_template.ncl
rename to experiments/ECP5/lut_init/lut_init_template.ncl
diff --git a/libtrellis/.gitignore b/libtrellis/.gitignore
index cab4ee8..a2b28a2 100644
--- a/libtrellis/.gitignore
+++ b/libtrellis/.gitignore
@@ -19,8 +19,22 @@
ecpbram
ecppack
ecpunpack
+ecpmulti
ecppll
libtrellis.dylib
*~
ecpmulti
generated/
+
+# Windows
+ecpbram.exe
+ecppack.exe
+ecpunpack.exe
+ecpmulti.exe
+ecppll.exe
+pytrellis.pyd
+
+# Ninja
+.ninja_*
+build.ninja
+rules.ninja
diff --git a/libtrellis/src/Bitstream.cpp b/libtrellis/src/Bitstream.cpp
index c00799c..fb0aa38 100644
--- a/libtrellis/src/Bitstream.cpp
+++ b/libtrellis/src/Bitstream.cpp
@@ -32,6 +32,45 @@
static const uint32_t multiboot_flag = 1 << 20;
static const uint32_t background_flag = 0x2E000000;
+// Bitstream generation can be tweaked in various ways; this class encapsulates
+// these options and provides defaults that mimic Diamond-generated output.
+class BitstreamOptions {
+public:
+ BitstreamOptions(const Chip &chip) {
+ if (chip.info.family == "MachXO2") {
+ // Write frames out in order 0 => max or reverse (max => 0).
+ // This apparently does NOT apply to compressed bitstreams, which
+ // uses reversed_frames = true unconditionally.
+ reversed_frames = false;
+ dummy_bytes_after_preamble = 2;
+ crc_meta = 0xE0; // CRC check (0x80), once at end (0x40), dummy bits
+ // in bitstream, no dummy bytes after each frame.
+ // FIXME: Diamond seems to set include_dummy_bits for MachXO2
+ // sometimes (0x20). Add this functionality later, as I'm not sure
+ // any MachXO2 members have dummy bits at this time.
+ crc_after_each_frame = false;
+ dummy_bytes_after_frame = 0;
+ security_sed_space = 8;
+ } else if (chip.info.family == "ECP5") {
+ reversed_frames = true;
+ dummy_bytes_after_preamble = 4;
+ crc_meta = 0x91; // CRC check (0x80), per frame (bit 6 cleared),
+ // and there 1 dummy bytes after each frame (0x10).
+ crc_after_each_frame = true;
+ dummy_bytes_after_frame = 1;
+ security_sed_space = 12;
+ } else
+ throw runtime_error("Unknown chip family: " + chip.info.family);
+ };
+
+ bool reversed_frames;
+ size_t dummy_bytes_after_preamble;
+ uint8_t crc_meta;
+ bool crc_after_each_frame;
+ size_t dummy_bytes_after_frame;
+ size_t security_sed_space;
+};
+
// The BitstreamReadWriter class stores state (including CRC16) whilst reading
// the bitstream
class BitstreamReadWriter {
@@ -199,7 +238,7 @@
}
}
- void write_compressed_frames(const std::vector<std::vector<uint8_t>> &frames_in) {
+ void write_compressed_frames(const std::vector<std::vector<uint8_t>> &frames_in, BitstreamOptions &ops) {
// Build a histogram of bytes to aid creating the dictionary
int histogram[256];
for (int i = 0; i < 256; i++)
@@ -223,7 +262,7 @@
write_byte(dict_entries[i]);
// Write data
write_byte(uint8_t(BitstreamCommand::LSC_PROG_INCR_CMP));
- write_byte(0x91); //CRC check, 1 dummy byte
+ write_byte(ops.crc_meta); //CRC check, 1 dummy byte
uint16_t frames = uint16_t(frames_in.size());
write_byte(uint8_t((frames >> 8) & 0xFF));
write_byte(uint8_t(frames & 0xFF));
@@ -286,8 +325,13 @@
// This ensures compressed frame is 8-bit aligned
flush_bits();
// Post-frame CRC and 0xFF byte
- insert_crc16();
- write_byte(0xFF);
+ if(ops.crc_after_each_frame) {
+ insert_crc16();
+ }
+
+ for(size_t j = 0; j < ops.dummy_bytes_after_frame; j++) {
+ write_byte(0xFF);
+ }
}
}
@@ -530,13 +574,8 @@
// This is the main bitstream payload
if (!chip)
throw BitstreamParseError("start of bitstream data before chip was identified", rd.get_offset());
- bool reversed_frames;
- if (chip->info.family == "MachXO2")
- reversed_frames = false;
- else if (chip->info.family == "ECP5")
- reversed_frames = true;
- else
- throw BitstreamParseError("Unknown chip family: " + chip->info.family);
+
+ BitstreamOptions ops(chip.get()); // Only reversed_frames is meaningful here.
uint8_t params[3];
rd.get_bytes(params, 3);
@@ -561,7 +600,9 @@
bytes_per_frame += (7 - ((bytes_per_frame - 1) % 8));
unique_ptr<uint8_t[]> frame_bytes = make_unique<uint8_t[]>(bytes_per_frame);
for (size_t i = 0; i < frame_count; i++) {
- size_t idx = reversed_frames? (chip->info.num_frames - 1) - i : i;
+ // Apparently when a bitstream is compressed, even on
+ // MachXO2, frames are written in reverse order!
+ const size_t idx = (chip->info.num_frames - 1) - i;
if (cmd == BitstreamCommand::LSC_PROG_INCR_CMP)
rd.get_compressed_bytes(frame_bytes.get(), bytes_per_frame, compression_dict.get());
else
@@ -687,10 +728,13 @@
Bitstream Bitstream::serialise_chip(const Chip &chip, const map<string, string> options) {
BitstreamReadWriter wr;
+
+ BitstreamOptions ops(chip);
+
// Preamble
wr.write_bytes(preamble.begin(), preamble.size());
// Padding
- wr.insert_dummy(4);
+ wr.insert_dummy(ops.dummy_bytes_after_preamble);
if (options.count("spimode")) {
auto spimode = find_if(spi_modes.begin(), spi_modes.end(), [&](const pair<string, uint8_t> &fp){
@@ -737,9 +781,16 @@
ctrl0 &= ~background_flag;
}
wr.write_uint32(ctrl0);
+
+ // Seems pretty consistent...
+ if (chip.info.family == "MachXO2") {
+ wr.insert_dummy(4);
+ }
+
// Init address
wr.write_byte(uint8_t(BitstreamCommand::LSC_INIT_ADDRESS));
wr.insert_zeros(3);
+
if (options.count("compress") && options.at("compress") == "yes") {
// First create an uncompressed array of frames
std::vector<std::vector<uint8_t>> frames_data;
@@ -758,11 +809,11 @@
}
}
// Then compress and write
- wr.write_compressed_frames(frames_data);
+ wr.write_compressed_frames(frames_data, ops);
} else {
// Bitstream data
wr.write_byte(uint8_t(BitstreamCommand::LSC_PROG_INCR_RTI));
- wr.write_byte(0x91); //CRC check, 1 dummy byte
+ wr.write_byte(ops.crc_meta);
uint16_t frames = uint16_t(chip.info.num_frames);
wr.write_byte(uint8_t((frames >> 8) & 0xFF));
wr.write_byte(uint8_t(frames & 0xFF));
@@ -770,21 +821,32 @@
chip.info.pad_bits_before_frame) / 8U;
unique_ptr<uint8_t[]> frame_bytes = make_unique<uint8_t[]>(bytes_per_frame);
for (size_t i = 0; i < frames; i++) {
+ size_t idx = ops.reversed_frames? (chip.info.num_frames - 1) - i : i;
fill(frame_bytes.get(), frame_bytes.get() + bytes_per_frame, 0x00);
for (int j = 0; j < chip.info.bits_per_frame; j++) {
size_t ofs = j + chip.info.pad_bits_after_frame;
assert(((bytes_per_frame - 1) - (ofs / 8)) < bytes_per_frame);
frame_bytes[(bytes_per_frame - 1) - (ofs / 8)] |=
- (chip.cram.bit((chip.info.num_frames - 1) - i, j) & 0x01) << (ofs % 8);
+ (chip.cram.bit(idx, j) & 0x01) << (ofs % 8);
}
wr.write_bytes(frame_bytes.get(), bytes_per_frame);
- wr.insert_crc16();
- wr.write_byte(0xFF);
+ if(ops.crc_after_each_frame) {
+ wr.insert_crc16();
+ }
+
+ for(size_t j = 0; j < ops.dummy_bytes_after_frame; j++) {
+ wr.write_byte(0xFF);
+ }
}
}
+ if(!ops.crc_after_each_frame) {
+ wr.insert_crc16();
+ }
+
// Post-bitstream space for SECURITY and SED (not used here)
- wr.insert_dummy(12);
+ wr.insert_dummy(ops.security_sed_space);
+
// Program Usercode
wr.write_byte(uint8_t(BitstreamCommand::ISC_PROGRAM_USERCODE));
wr.write_byte(0x80);
@@ -821,6 +883,14 @@
}
wr.insert_crc16();
}
+
+ // MachXO2 indeed writes this info twice for some reason...
+ if (chip.info.family == "MachXO2") {
+ wr.write_byte(uint8_t(BitstreamCommand::LSC_PROG_CNTRL0));
+ wr.insert_zeros(3);
+ wr.write_uint32(ctrl0);
+ }
+
// Program DONE
wr.write_byte(uint8_t(BitstreamCommand::ISC_PROGRAM_DONE));
wr.insert_zeros(3);
@@ -844,7 +914,7 @@
Bitstream Bitstream::serialise_chip_partial(const Chip &chip, const vector<uint32_t> &frames, const map<string, string> options)
{
BitstreamReadWriter wr;
-
+
// Address encoding for partial frame writes
static const map<uint32_t, uint32_t> msb_weights_45k = {
{{0x0000}, { 0*106}},
diff --git a/libtrellis/src/Tile.cpp b/libtrellis/src/Tile.cpp
index 2b3345a..05b2cc0 100644
--- a/libtrellis/src/Tile.cpp
+++ b/libtrellis/src/Tile.cpp
@@ -6,17 +6,51 @@
#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 = {
+ // 1200HC
+ {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/minitests/dsp/mult.v b/minitests/ECP5/dsp/mult.v
similarity index 100%
rename from minitests/dsp/mult.v
rename to minitests/ECP5/dsp/mult.v
diff --git a/minitests/dsp/mult1.v b/minitests/ECP5/dsp/mult1.v
similarity index 100%
rename from minitests/dsp/mult1.v
rename to minitests/ECP5/dsp/mult1.v
diff --git a/minitests/dsp/mult1n.ncl b/minitests/ECP5/dsp/mult1n.ncl
similarity index 100%
rename from minitests/dsp/mult1n.ncl
rename to minitests/ECP5/dsp/mult1n.ncl
diff --git a/minitests/dsp/preadd.v b/minitests/ECP5/dsp/preadd.v
similarity index 100%
rename from minitests/dsp/preadd.v
rename to minitests/ECP5/dsp/preadd.v
diff --git a/minitests/iologic/iddr.v b/minitests/ECP5/iologic/iddr.v
similarity index 100%
rename from minitests/iologic/iddr.v
rename to minitests/ECP5/iologic/iddr.v
diff --git a/minitests/iologic/iddr2.v b/minitests/ECP5/iologic/iddr2.v
similarity index 100%
rename from minitests/iologic/iddr2.v
rename to minitests/ECP5/iologic/iddr2.v
diff --git a/minitests/iologic/iddr7.v b/minitests/ECP5/iologic/iddr7.v
similarity index 100%
rename from minitests/iologic/iddr7.v
rename to minitests/ECP5/iologic/iddr7.v
diff --git a/minitests/iologic/iddr_LSR.v b/minitests/ECP5/iologic/iddr_LSR.v
similarity index 100%
rename from minitests/iologic/iddr_LSR.v
rename to minitests/ECP5/iologic/iddr_LSR.v
diff --git a/minitests/iologic/iddr_inv.v b/minitests/ECP5/iologic/iddr_inv.v
similarity index 100%
rename from minitests/iologic/iddr_inv.v
rename to minitests/ECP5/iologic/iddr_inv.v
diff --git a/minitests/iologic/idelay.v b/minitests/ECP5/iologic/idelay.v
similarity index 100%
rename from minitests/iologic/idelay.v
rename to minitests/ECP5/iologic/idelay.v
diff --git a/minitests/iologic/ireg.v b/minitests/ECP5/iologic/ireg.v
similarity index 100%
rename from minitests/iologic/ireg.v
rename to minitests/ECP5/iologic/ireg.v
diff --git a/minitests/iologic/ireg2.v b/minitests/ECP5/iologic/ireg2.v
similarity index 100%
rename from minitests/iologic/ireg2.v
rename to minitests/ECP5/iologic/ireg2.v
diff --git a/minitests/iologic/oddr.v b/minitests/ECP5/iologic/oddr.v
similarity index 100%
rename from minitests/iologic/oddr.v
rename to minitests/ECP5/iologic/oddr.v
diff --git a/minitests/iologic/oddr7.v b/minitests/ECP5/iologic/oddr7.v
similarity index 100%
rename from minitests/iologic/oddr7.v
rename to minitests/ECP5/iologic/oddr7.v
diff --git a/minitests/iologic/oreg.v b/minitests/ECP5/iologic/oreg.v
similarity index 100%
rename from minitests/iologic/oreg.v
rename to minitests/ECP5/iologic/oreg.v
diff --git a/minitests/iologic/oshx2a.v b/minitests/ECP5/iologic/oshx2a.v
similarity index 100%
rename from minitests/iologic/oshx2a.v
rename to minitests/ECP5/iologic/oshx2a.v
diff --git a/minitests/iologic/toreg.v b/minitests/ECP5/iologic/toreg.v
similarity index 100%
rename from minitests/iologic/toreg.v
rename to minitests/ECP5/iologic/toreg.v
diff --git a/minitests/ncl/lut.ncl b/minitests/ECP5/ncl/lut.ncl
similarity index 100%
rename from minitests/ncl/lut.ncl
rename to minitests/ECP5/ncl/lut.ncl
diff --git a/minitests/ncl/lut_0.ncl b/minitests/ECP5/ncl/lut_0.ncl
similarity index 100%
rename from minitests/ncl/lut_0.ncl
rename to minitests/ECP5/ncl/lut_0.ncl
diff --git a/minitests/ncl/lut_or.ncl b/minitests/ECP5/ncl/lut_or.ncl
similarity index 100%
rename from minitests/ncl/lut_or.ncl
rename to minitests/ECP5/ncl/lut_or.ncl
diff --git a/minitests/potpourri/dtr.v b/minitests/ECP5/potpourri/dtr.v
similarity index 100%
rename from minitests/potpourri/dtr.v
rename to minitests/ECP5/potpourri/dtr.v
diff --git a/minitests/potpourri/jtagg.v b/minitests/ECP5/potpourri/jtagg.v
similarity index 100%
rename from minitests/potpourri/jtagg.v
rename to minitests/ECP5/potpourri/jtagg.v
diff --git a/minitests/potpourri/osc.v b/minitests/ECP5/potpourri/osc.v
similarity index 100%
rename from minitests/potpourri/osc.v
rename to minitests/ECP5/potpourri/osc.v
diff --git a/minitests/potpourri/osc_div.ncl b/minitests/ECP5/potpourri/osc_div.ncl
similarity index 100%
rename from minitests/potpourri/osc_div.ncl
rename to minitests/ECP5/potpourri/osc_div.ncl
diff --git a/timing/fuzzers/010-basic-cells/fuzzer.py b/timing/fuzzers/ECP5/010-basic-cells/fuzzer.py
similarity index 100%
rename from timing/fuzzers/010-basic-cells/fuzzer.py
rename to timing/fuzzers/ECP5/010-basic-cells/fuzzer.py
diff --git a/timing/fuzzers/012-io/fuzzer.py b/timing/fuzzers/ECP5/012-io/fuzzer.py
similarity index 100%
rename from timing/fuzzers/012-io/fuzzer.py
rename to timing/fuzzers/ECP5/012-io/fuzzer.py
diff --git a/timing/fuzzers/012-io/pio.v b/timing/fuzzers/ECP5/012-io/pio.v
similarity index 100%
rename from timing/fuzzers/012-io/pio.v
rename to timing/fuzzers/ECP5/012-io/pio.v
diff --git a/timing/fuzzers/013-iol/fuzzer.py b/timing/fuzzers/ECP5/013-iol/fuzzer.py
similarity index 100%
rename from timing/fuzzers/013-iol/fuzzer.py
rename to timing/fuzzers/ECP5/013-iol/fuzzer.py
diff --git a/timing/fuzzers/013-iol/pio.v b/timing/fuzzers/ECP5/013-iol/pio.v
similarity index 100%
rename from timing/fuzzers/013-iol/pio.v
rename to timing/fuzzers/ECP5/013-iol/pio.v
diff --git a/timing/fuzzers/014-ebr/ebr_regmode.v b/timing/fuzzers/ECP5/014-ebr/ebr_regmode.v
similarity index 100%
rename from timing/fuzzers/014-ebr/ebr_regmode.v
rename to timing/fuzzers/ECP5/014-ebr/ebr_regmode.v
diff --git a/timing/fuzzers/014-ebr/ebr_writemode.v b/timing/fuzzers/ECP5/014-ebr/ebr_writemode.v
similarity index 100%
rename from timing/fuzzers/014-ebr/ebr_writemode.v
rename to timing/fuzzers/ECP5/014-ebr/ebr_writemode.v
diff --git a/timing/fuzzers/014-ebr/fuzzer.py b/timing/fuzzers/ECP5/014-ebr/fuzzer.py
similarity index 100%
rename from timing/fuzzers/014-ebr/fuzzer.py
rename to timing/fuzzers/ECP5/014-ebr/fuzzer.py
diff --git a/timing/fuzzers/015-mult/fuzzer.py b/timing/fuzzers/ECP5/015-mult/fuzzer.py
similarity index 100%
rename from timing/fuzzers/015-mult/fuzzer.py
rename to timing/fuzzers/ECP5/015-mult/fuzzer.py
diff --git a/timing/fuzzers/015-mult/mult_pipemode.v b/timing/fuzzers/ECP5/015-mult/mult_pipemode.v
similarity index 100%
rename from timing/fuzzers/015-mult/mult_pipemode.v
rename to timing/fuzzers/ECP5/015-mult/mult_pipemode.v
diff --git a/timing/fuzzers/020-basic_routing/fuzzer.py b/timing/fuzzers/ECP5/020-basic_routing/fuzzer.py
similarity index 100%
rename from timing/fuzzers/020-basic_routing/fuzzer.py
rename to timing/fuzzers/ECP5/020-basic_routing/fuzzer.py
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..2bb8aa8 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,13 +106,21 @@
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
desfiles: a tuple (ncdfile, prffile)
wires: list of canonical names of the wire
drivers_only: only include arcs driving the wire in the output
+ dir_override: Dictionary that specificies whether a net queried by ispTcl
+ is a "sink" or "driver" when ispTcl returns "---" (since ISPTcl always puts
+ the queried net on the RHS of an an arc). dir_override is only consulted if
+ ispTcl returns "---" for the direction of a given net, and will
+ additionally override drivers_only=False for any nets specified as
+ "driver". Two additional strings are allowed: "ignore" to ignore "---"
+ connections to/from the queried net, and "mark" to return the connection as
+ an AmbiguousArc for later processing.
Returns a map between wire name and a list of arc tuples (source, sink)
"""
@@ -122,8 +152,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 e7885eb..b05cdff 100644
--- a/util/common/nets.py
+++ b/util/common/nets.py
@@ -312,6 +312,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/dbfixup.py b/util/fuzz/dbfixup.py
index dee2db0..734522f 100644
--- a/util/fuzz/dbfixup.py
+++ b/util/fuzz/dbfixup.py
@@ -3,7 +3,9 @@
"""
Database fix utility
-Run at the end of fuzzing to "finalise" the database and remove problems that may occur during fuzzing
+Run at the end of fuzzing to "finalise" the database and remove problems that may occur during fuzzing.
+
+The remaining functions can be called in other fuzzers as necessary.
"""
@@ -26,3 +28,47 @@
if deleteFc:
db.remove_fixed_sink(mux)
db.save()
+
+
+def remove_enum_bits(family, device, tiletype, lowerright, upperleft=(0, 0)):
+ """
+ Remove bits from enumerations in a given tile that actually belong
+ to routing bits. This can happen when e.g. routing is required for Diamond
+ to set certain bits in the output, as is the case for fuzzing I/O enums
+ in PIC_L0 and PIC_R0.
+
+ Bounds are (0,0)-based. Upperleft is inclusive, lowerright is exclusive.
+ """
+ def in_bounding_box(bit):
+ (x, y) = (bit.frame, bit.bit)
+
+ if upperleft[0] > x or upperleft[1] > y:
+ return False
+
+ if lowerright[0] <= x or lowerright[1] <= y:
+ return False
+
+ return True
+
+ db = pytrellis.get_tile_bitdata(
+ pytrellis.TileLocator(family, device, tiletype))
+
+ for enum in db.get_settings_enums():
+ fixed_enum = pytrellis.EnumSettingBits()
+
+ for option in db.get_data_for_enum(enum).options:
+ key = option.key()
+ fixed_bg = pytrellis.BitGroup()
+
+ for bit in option.data().bits:
+ if in_bounding_box(bit):
+ fixed_bg.bits.add(bit)
+
+ fixed_enum.options[key] = fixed_bg
+
+ fixed_enum.name = db.get_data_for_enum(enum).name
+ fixed_enum.defval = db.get_data_for_enum(enum).defval
+
+ db.remove_setting_enum(enum)
+ db.add_setting_enum(fixed_enum)
+ db.save()
diff --git a/util/fuzz/interconnect.py b/util/fuzz/interconnect.py
index e40045b..5774f78 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
@@ -48,6 +49,10 @@
:param enable_span1_fix: if True, include span1 wires that are excluded due to a Tcl API bug
:param func_cib: if True, we are fuzzing a special function to CIB interconnect, enable optimisations for this
: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. This dictionary overrides
+ func_cib=False for the nets in question.
:param nonlocal_prefix: add a prefix to non-global and non-neighbour wires for device-specific fuzzers
:param bias: Apply offset correction for n-based column numbering, n > 0. Used used by Lattice
on certain families.
@@ -70,7 +75,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 +89,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 +106,14 @@
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. This dictionary overrides
+ bidir=False for the nets in question.
: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()