From 3edbde3fe2706fdff8dea3cb92416b1f50664e59 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Thu, 18 Oct 2018 18:26:34 -0700 Subject: [PATCH 01/15] Add FASM library to prjxray path. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- .gitmodules | 3 +++ third_party/fasm | 1 + utils/environment.sh | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) create mode 160000 third_party/fasm diff --git a/.gitmodules b/.gitmodules index 96944e6e..62e557c1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "third_party/yaml-cpp"] path = third_party/yaml-cpp url = https://github.com/jbeder/yaml-cpp.git +[submodule "third_party/fasm"] + path = third_party/fasm + url = https://github.com/SymbiFlow/fasm.git diff --git a/third_party/fasm b/third_party/fasm new file mode 160000 index 00000000..faed2f5e --- /dev/null +++ b/third_party/fasm @@ -0,0 +1 @@ +Subproject commit faed2f5e9915497c3774ec201ced8dc017cc03ae diff --git a/utils/environment.sh b/utils/environment.sh index 375c5f15..5545f727 100644 --- a/utils/environment.sh +++ b/utils/environment.sh @@ -4,7 +4,7 @@ while [ -h "$XRAY_ENV_PATH" ]; do # resolve $XRAY_ENV_PATH until the file is no XRAY_ENV_PATH="$(readlink "$XRAY_ENV_PATH")" [[ $XRAY_ENV_PATH != /* ]] && XRAY_ENV_PATH="$XRAY_UTILS_DIR/$XRAY_ENV_PATH" # if $XRAY_ENV_PATH was a relative symlink, we need to resolve it relative to the path where the symlink file was located done -export PYTHONPATH="${XRAY_DIR}:$PYTHONPATH" +export PYTHONPATH="${XRAY_DIR}:${XRAY_DIR}/third_party/fasm:$PYTHONPATH" export XRAY_UTILS_DIR="$( cd -P "$( dirname "$XRAY_ENV_PATH" )" && pwd )" export XRAY_DIR="$( dirname "$XRAY_UTILS_DIR" )" From 6dd9626cfc27351bbca955386ba60160d9ead821 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Thu, 18 Oct 2018 21:13:14 -0700 Subject: [PATCH 02/15] Move all use of getenv into prjxray.util and behind a function. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- experiments/clbpips/generate.py | 5 +-- experiments/gndvcc/generate.py | 5 ++- experiments/pipsroute/generate.py | 3 +- fuzzers/010-lutinit/generate.py | 5 ++- fuzzers/011-ffconfig/generate.py | 5 +-- fuzzers/011-ffconfig/top.py | 8 ++-- fuzzers/012-clbn5ffmux/generate.py | 5 +-- fuzzers/012-clbn5ffmux/top.py | 8 ++-- fuzzers/013-clbncy0/generate.py | 5 +-- fuzzers/013-clbncy0/top.py | 8 ++-- fuzzers/014-ffsrcemux/generate.py | 5 +-- fuzzers/015-clbnffmux/generate.py | 4 +- fuzzers/015-clbnffmux/top.py | 8 ++-- fuzzers/016-clbnoutmux/generate.py | 4 +- fuzzers/016-clbnoutmux/top.py | 8 ++-- fuzzers/017-clbprecyinit/generate.py | 5 +-- fuzzers/018-clbram/generate.py | 5 +-- fuzzers/018-clbram/top.py | 4 +- fuzzers/019-ndi1mux/generate.py | 5 +-- fuzzers/019-ndi1mux/top.py | 4 +- fuzzers/050-intpips/generate.py | 5 ++- fuzzers/051-imuxlout/generate.py | 5 ++- fuzzers/052-clkin/generate.py | 5 ++- fuzzers/053-ctrlin/generate.py | 5 ++- fuzzers/054-gfan/generate.py | 5 ++- fuzzers/055-gnd/generate.py | 5 ++- fuzzers/056-rempips/generate.py | 5 +-- fuzzers/057-bipips/generate.py | 5 ++- fuzzers/058-hclkpips/generate.py | 5 ++- fuzzers/100-dsp-mskpat/generate.py | 5 ++- prjxray/segmaker.py | 10 ++--- prjxray/util.py | 64 ++++++---------------------- utils/fasm2frames.py | 7 +-- 33 files changed, 93 insertions(+), 147 deletions(-) diff --git a/experiments/clbpips/generate.py b/experiments/clbpips/generate.py index 1a625365..58824df8 100644 --- a/experiments/clbpips/generate.py +++ b/experiments/clbpips/generate.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 -import sys, re - from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") print("Loading tags from design.txt.") with open("design.txt", "r") as f: diff --git a/experiments/gndvcc/generate.py b/experiments/gndvcc/generate.py index 3f814358..f0200544 100644 --- a/experiments/gndvcc/generate.py +++ b/experiments/gndvcc/generate.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 -import sys, os, re +import re from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") tiledata = dict() pipdata = dict() diff --git a/experiments/pipsroute/generate.py b/experiments/pipsroute/generate.py index aa131cae..c58c8298 100644 --- a/experiments/pipsroute/generate.py +++ b/experiments/pipsroute/generate.py @@ -3,13 +3,14 @@ import sys, re from prjxray.segmaker import Segmaker +from prjxray import util pipdata = dict() ignpip = set() def handle_design(prefix, second_pass): - segmk = Segmaker(prefix + ".bits") + segmk = Segmaker(util.get_db_root(), prefix + ".bits") tiledata = dict() nlines = 0 diff --git a/fuzzers/010-lutinit/generate.py b/fuzzers/010-lutinit/generate.py index bcf8c3ac..4e53630c 100644 --- a/fuzzers/010-lutinit/generate.py +++ b/fuzzers/010-lutinit/generate.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 -import sys, re +import sys from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design_%s.bits" % sys.argv[1]) +segmk = Segmaker(util.get_db_root(), "design_%s.bits" % sys.argv[1]) print("Loading tags from design_%s.txt." % sys.argv[1]) with open("design_%s.txt" % sys.argv[1], "r") as f: diff --git a/fuzzers/011-ffconfig/generate.py b/fuzzers/011-ffconfig/generate.py index b16edfca..169a6c1b 100644 --- a/fuzzers/011-ffconfig/generate.py +++ b/fuzzers/011-ffconfig/generate.py @@ -10,11 +10,10 @@ LDPE Primitive: Transparent Data Latch with Asynchronous Preset and Gate Enable from prims import * -import sys, re - from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") def ones(l): diff --git a/fuzzers/011-ffconfig/top.py b/fuzzers/011-ffconfig/top.py index 717f2bbf..3baec636 100644 --- a/fuzzers/011-ffconfig/top.py +++ b/fuzzers/011-ffconfig/top.py @@ -1,7 +1,5 @@ import random random.seed(0) -import os -import re from prjxray import util from prims import * @@ -14,8 +12,10 @@ f.write("i,prim,loc,bel,init\n") def gen_slices(): - for _tile_name, site_name, _site_type in util.gen_sites(['SLICEL', - 'SLICEM']): + for _tile_name, site_name, _site_type in util.get_roi().gen_sites([ + 'SLICEL', + 'SLICEM', + ]): yield site_name diff --git a/fuzzers/012-clbn5ffmux/generate.py b/fuzzers/012-clbn5ffmux/generate.py index b16cc19a..521e4d03 100644 --- a/fuzzers/012-clbn5ffmux/generate.py +++ b/fuzzers/012-clbn5ffmux/generate.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 -import sys, re - from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") print("Loading tags") ''' diff --git a/fuzzers/012-clbn5ffmux/top.py b/fuzzers/012-clbn5ffmux/top.py index 68013175..efb8079d 100644 --- a/fuzzers/012-clbn5ffmux/top.py +++ b/fuzzers/012-clbn5ffmux/top.py @@ -1,7 +1,5 @@ import random random.seed(0) -import os -import re from prjxray import util CLBN = 40 @@ -9,8 +7,10 @@ print('//Requested CLBs: %s' % str(CLBN)) def gen_slices(): - for _tile_name, site_name, _site_type in util.gen_sites(['SLICEL', - 'SLICEM']): + for _tile_name, site_name, _site_type in util.get_roi().gen_sites([ + 'SLICEL', + 'SLICEM' + ]): yield site_name diff --git a/fuzzers/013-clbncy0/generate.py b/fuzzers/013-clbncy0/generate.py index 8bb35c9a..333c4aa0 100644 --- a/fuzzers/013-clbncy0/generate.py +++ b/fuzzers/013-clbncy0/generate.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 -import sys, re - from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") print("Loading tags") ''' diff --git a/fuzzers/013-clbncy0/top.py b/fuzzers/013-clbncy0/top.py index 531372c1..af32d952 100644 --- a/fuzzers/013-clbncy0/top.py +++ b/fuzzers/013-clbncy0/top.py @@ -1,7 +1,5 @@ import random random.seed(0) -import os -import re from prjxray import util CLBN = 400 @@ -9,8 +7,10 @@ print('//Requested CLBs: %s' % str(CLBN)) def gen_slices(): - for _tile_name, site_name, _site_type in util.gen_sites(['SLICEL', - 'SLICEM']): + for _tile_name, site_name, _site_type in util.get_roi().gen_sites([ + 'SLICEL', + 'SLICEM' + ]): yield site_name diff --git a/fuzzers/014-ffsrcemux/generate.py b/fuzzers/014-ffsrcemux/generate.py index 2701b854..d3618ff8 100644 --- a/fuzzers/014-ffsrcemux/generate.py +++ b/fuzzers/014-ffsrcemux/generate.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 -import sys, re - from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") print("Loading tags") ''' diff --git a/fuzzers/015-clbnffmux/generate.py b/fuzzers/015-clbnffmux/generate.py index 2e69102d..f2d60d1d 100644 --- a/fuzzers/015-clbnffmux/generate.py +++ b/fuzzers/015-clbnffmux/generate.py @@ -1,11 +1,9 @@ #!/usr/bin/env python3 -import sys, os, re - from prjxray.segmaker import Segmaker from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") cache = dict() print("Loading tags") diff --git a/fuzzers/015-clbnffmux/top.py b/fuzzers/015-clbnffmux/top.py index c3a21fda..052e6e24 100644 --- a/fuzzers/015-clbnffmux/top.py +++ b/fuzzers/015-clbnffmux/top.py @@ -1,7 +1,5 @@ import random random.seed(0) -import os -import re from prjxray import util CLBN = 400 @@ -9,8 +7,10 @@ print('//Requested CLBs: %s' % str(CLBN)) def gen_slices(): - for _tile_name, site_name, _site_type in util.gen_sites(['SLICEL', - 'SLICEM']): + for _tile_name, site_name, _site_type in util.get_roi().gen_sites([ + 'SLICEL', + 'SLICEM', + ]): yield site_name diff --git a/fuzzers/016-clbnoutmux/generate.py b/fuzzers/016-clbnoutmux/generate.py index 71af6b2b..d463f64b 100644 --- a/fuzzers/016-clbnoutmux/generate.py +++ b/fuzzers/016-clbnoutmux/generate.py @@ -1,11 +1,9 @@ #!/usr/bin/env python3 -import sys, os, re - from prjxray.segmaker import Segmaker from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") cache = dict() print("Loading tags") diff --git a/fuzzers/016-clbnoutmux/top.py b/fuzzers/016-clbnoutmux/top.py index bb5bbbe4..72785a1c 100644 --- a/fuzzers/016-clbnoutmux/top.py +++ b/fuzzers/016-clbnoutmux/top.py @@ -1,7 +1,5 @@ import random random.seed(0) -import os -import re from prjxray import util CLBN = 400 @@ -9,8 +7,10 @@ print('//Requested CLBs: %s' % str(CLBN)) def gen_slices(): - for _tile_name, site_name, _site_type in util.gen_sites(['SLICEL', - 'SLICEM']): + for _tile_name, site_name, _site_type in util.get_roi().gen_sites([ + 'SLICEL', + 'SLICEM' + ]): yield site_name diff --git a/fuzzers/017-clbprecyinit/generate.py b/fuzzers/017-clbprecyinit/generate.py index 6e3f46ba..6035d675 100644 --- a/fuzzers/017-clbprecyinit/generate.py +++ b/fuzzers/017-clbprecyinit/generate.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 -import sys, os, re - from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") print("Loading tags") f = open('params.csv', 'r') diff --git a/fuzzers/018-clbram/generate.py b/fuzzers/018-clbram/generate.py index ec46faa1..74e4154a 100644 --- a/fuzzers/018-clbram/generate.py +++ b/fuzzers/018-clbram/generate.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 -import sys, re, os - from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") # Can fit 4 per CLB # BELable diff --git a/fuzzers/018-clbram/top.py b/fuzzers/018-clbram/top.py index 1de15ff2..b3c4fe30 100644 --- a/fuzzers/018-clbram/top.py +++ b/fuzzers/018-clbram/top.py @@ -17,8 +17,6 @@ Note: LUT6 was added to try to simplify reduction, although it might not be need import random random.seed(0) -import os -import re from prjxray import util CLBN = 50 @@ -26,7 +24,7 @@ print('//Requested CLBs: %s' % str(CLBN)) def gen_slicems(): - for _tile_name, site_name, _site_type in util.gen_sites(['SLICEM']): + for _tile_name, site_name, _site_type in util.get_roi().gen_sites(['SLICEM']): yield site_name diff --git a/fuzzers/019-ndi1mux/generate.py b/fuzzers/019-ndi1mux/generate.py index ece577a4..03317dd4 100644 --- a/fuzzers/019-ndi1mux/generate.py +++ b/fuzzers/019-ndi1mux/generate.py @@ -5,11 +5,10 @@ # Can we find instance where they are not aliased? WA7USED = 0 -import sys, re, os - from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") print("Loading tags") ''' diff --git a/fuzzers/019-ndi1mux/top.py b/fuzzers/019-ndi1mux/top.py index fbb3a312..47c65f34 100644 --- a/fuzzers/019-ndi1mux/top.py +++ b/fuzzers/019-ndi1mux/top.py @@ -1,7 +1,5 @@ import random random.seed(0) -import os -import re from prjxray import util CLBN = 50 @@ -9,7 +7,7 @@ print('//Requested CLBs: %s' % str(CLBN)) def gen_slicems(): - for _tile_name, site_name, _site_type in util.gen_sites(['SLICEM']): + for _tile_name, site_name, _site_type in util.get_roi().gen_sites(['SLICEM']): yield site_name diff --git a/fuzzers/050-intpips/generate.py b/fuzzers/050-intpips/generate.py index 84353ca0..13573664 100644 --- a/fuzzers/050-intpips/generate.py +++ b/fuzzers/050-intpips/generate.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 -import sys, re +import re from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/051-imuxlout/generate.py b/fuzzers/051-imuxlout/generate.py index 9627960b..66c23414 100644 --- a/fuzzers/051-imuxlout/generate.py +++ b/fuzzers/051-imuxlout/generate.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 -import sys, re, os +import re, os from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/052-clkin/generate.py b/fuzzers/052-clkin/generate.py index 2aca7188..fa5f9463 100644 --- a/fuzzers/052-clkin/generate.py +++ b/fuzzers/052-clkin/generate.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 -import sys, os, re +import os, re from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/053-ctrlin/generate.py b/fuzzers/053-ctrlin/generate.py index bbae2ecc..70d82ad2 100644 --- a/fuzzers/053-ctrlin/generate.py +++ b/fuzzers/053-ctrlin/generate.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 -import sys, os, re +import os, re from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/054-gfan/generate.py b/fuzzers/054-gfan/generate.py index 2f09618e..a05df06b 100644 --- a/fuzzers/054-gfan/generate.py +++ b/fuzzers/054-gfan/generate.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 -import sys, os, re +import re from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/055-gnd/generate.py b/fuzzers/055-gnd/generate.py index fd1d8d9f..04f38579 100644 --- a/fuzzers/055-gnd/generate.py +++ b/fuzzers/055-gnd/generate.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 -import sys, os, re +import re from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/056-rempips/generate.py b/fuzzers/056-rempips/generate.py index 16e2de41..56e761f8 100644 --- a/fuzzers/056-rempips/generate.py +++ b/fuzzers/056-rempips/generate.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 -import sys, os, re - from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/057-bipips/generate.py b/fuzzers/057-bipips/generate.py index 11379236..903f2162 100644 --- a/fuzzers/057-bipips/generate.py +++ b/fuzzers/057-bipips/generate.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 -import sys, os, re +import os from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design.bits") +segmk = Segmaker(util.get_db_root(), "design.bits") tiledata = dict() pipdata = set() diff --git a/fuzzers/058-hclkpips/generate.py b/fuzzers/058-hclkpips/generate.py index fc6f7b6d..72a4dab7 100644 --- a/fuzzers/058-hclkpips/generate.py +++ b/fuzzers/058-hclkpips/generate.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 -import sys, os, re +import sys from prjxray.segmaker import Segmaker +from prjxray import util tags = dict() en_tags = dict() @@ -23,7 +24,7 @@ for arg in sys.argv[1:]: for arg in sys.argv[1:]: print("Processing %s." % arg) - segmk = Segmaker(arg + ".bits") + segmk = Segmaker(util.get_db_root(), arg + ".bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/100-dsp-mskpat/generate.py b/fuzzers/100-dsp-mskpat/generate.py index 13606f7b..870daf03 100644 --- a/fuzzers/100-dsp-mskpat/generate.py +++ b/fuzzers/100-dsp-mskpat/generate.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 -import sys, os, re +import sys from prjxray.segmaker import Segmaker +from prjxray import util -segmk = Segmaker("design_%s.bits" % sys.argv[1]) +segmk = Segmaker(util.get_db_root(), "design_%s.bits" % sys.argv[1]) pipdata = dict() ignpip = set() diff --git a/prjxray/segmaker.py b/prjxray/segmaker.py index 8cd8ee41..887e2951 100644 --- a/prjxray/segmaker.py +++ b/prjxray/segmaker.py @@ -15,9 +15,6 @@ tilegrid.json provides tile addresses import os, json, re -XRAY_DATABASE = os.getenv("XRAY_DATABASE") -XRAY_DIR = os.getenv("XRAY_DIR") - BLOCK_TYPES = set(('CLB_IO_CLK', 'BLOCK_RAM', 'CFG_CLB')) @@ -41,7 +38,8 @@ def json_hex2i(s): class Segmaker: - def __init__(self, bitsfile, verbose=None): + def __init__(self, db_root, bitsfile, verbose=None): + self.db_root = db_root self.verbose = verbose if verbose is not None else os.getenv( 'VERBOSE', 'N') == 'Y' self.load_grid() @@ -60,9 +58,7 @@ class Segmaker: def load_grid(self): '''Load self.grid holding tile addresses''' - print("Loading %s grid." % XRAY_DATABASE) - with open("%s/database/%s/tilegrid.json" % (XRAY_DIR, XRAY_DATABASE), - "r") as f: + with open(os.path.join(db_root, "tilegrid.json"), "r") as f: self.grid = json.load(f) assert "segments" not in self.grid, "Old format tilegrid.json" diff --git a/prjxray/util.py b/prjxray/util.py index c61654db..e08285ce 100644 --- a/prjxray/util.py +++ b/prjxray/util.py @@ -1,11 +1,10 @@ import os import re -import os -import json - -DB_PATH = "%s/%s" % ( - os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE")) +from .roi import Roi +def get_db_root(): + return "%s/%s" % ( + os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE")) def roi_xy(): x1 = int(os.getenv('XRAY_ROI_GRID_X1')) @@ -15,10 +14,6 @@ def roi_xy(): return (x1, x2), (y1, y2) - -(ROI_X1, ROI_X2), (ROI_Y1, ROI_Y2) = roi_xy() - - def slice_xy(): '''Return (X1, X2), (Y1, Y2) from XRAY_ROI, exclusive end (for xrange)''' # SLICE_X12Y100:SLICE_X27Y149 @@ -29,49 +24,14 @@ def slice_xy(): ms = [int(m.group(i + 1)) for i in range(4)] return ((ms[0], ms[2] + 1), (ms[1], ms[3] + 1)) - -def tile_in_roi(tilej): - x = int(tilej['grid_x']) - y = int(tilej['grid_y']) - return ROI_X1 <= x <= ROI_X2 and ROI_Y1 <= y <= ROI_Y2 - - -def load_tilegrid(): - return json.load(open('%s/tilegrid.json' % DB_PATH)) - - -def gen_tiles(tile_types=None, tilegrid=None): - ''' - tile_types: list of tile types to keep, or None for all - tilegrid: cache the tilegrid database - ''' - tilegrid = tilegrid or load_tilegrid() - - for tile_name, tilej in tilegrid.items(): - if tile_in_roi(tilej) and (tile_types is None - or tilej['type'] in tile_types): - yield (tile_name, tilej) - - -def gen_sites(site_types=None, tilegrid=None): - ''' - site_types: list of site types to keep, or None for all - tilegrid: cache the tilegrid database - ''' - tilegrid = tilegrid or load_tilegrid() - - for tile_name, tilej in tilegrid.items(): - if not tile_in_roi(tilej): - continue - for site_name, site_type in tilej['sites'].items(): - if site_types is None or site_type in site_types: - yield (tile_name, site_name, site_type) - - -#print(list(gen_tiles(['CLBLL_L', 'CLBLL_R', 'CLBLM_L', 'CLBLM_R']))) -#print(list(gen_sites(['SLICEL', 'SLICEM']))) -#print(list(gen_sites(['SLICEM']))) - +def get_roi(): + (x1, x2), (y1, y2) = roi_xy() + return Roi( + tilegrid_file=os.path.join(get_db_root(), 'tilegrid.json'), + x1=x1, + x2=x2, + y1=y1, + y2=y2) # we know that all bits for CLB MUXes are in frames 30 and 31, so filter all other bits def bitfilter_clb_mux(frame_idx, bit_idx): diff --git a/utils/fasm2frames.py b/utils/fasm2frames.py index 361baa8e..db64fe7f 100755 --- a/utils/fasm2frames.py +++ b/utils/fasm2frames.py @@ -5,11 +5,7 @@ import re import sys import json import collections - - -class FASMSyntaxError(Exception): - pass - +import fasm def parsebit(val): '''Return "!012_23" => (12, 23, False)''' @@ -22,7 +18,6 @@ def parsebit(val): seg_word_column, word_bit_n = val.split('_') return int(seg_word_column), int(word_bit_n), isset - # TODO: migrate to library def process_db(tile_type, process): fns = [ From 41d9ede26a312eeb258e1f6ffbb345e514a95960 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Fri, 19 Oct 2018 08:33:31 -0700 Subject: [PATCH 03/15] Working bits2fasm using prjxray and fasm library. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- minitests/roi_harness/bit2fasm.sh | 7 + minitests/roi_harness/fasm2bit.sh | 2 +- prjxray/bitstream.py | 27 +++ prjxray/db.py | 16 ++ prjxray/fasm_assembler.py | 140 +++++++++++++ prjxray/fasm_disassembler.py | 130 ++++++++++++ prjxray/grid.py | 54 ++++- prjxray/roi.py | 49 +++++ prjxray/segment_map.py | 18 ++ prjxray/tile.py | 3 +- prjxray/tile_segbits.py | 136 +++++++++++++ third_party/fasm | 2 +- utils/bits2fasm.py | 319 +++--------------------------- utils/fasm2frames.py | 315 +++-------------------------- utils/fasm2pips.py | 53 +++++ 15 files changed, 689 insertions(+), 582 deletions(-) create mode 100755 minitests/roi_harness/bit2fasm.sh create mode 100644 prjxray/bitstream.py create mode 100644 prjxray/fasm_assembler.py create mode 100644 prjxray/fasm_disassembler.py create mode 100644 prjxray/roi.py create mode 100644 prjxray/segment_map.py create mode 100644 prjxray/tile_segbits.py create mode 100644 utils/fasm2pips.py diff --git a/minitests/roi_harness/bit2fasm.sh b/minitests/roi_harness/bit2fasm.sh new file mode 100755 index 00000000..13a6c4c6 --- /dev/null +++ b/minitests/roi_harness/bit2fasm.sh @@ -0,0 +1,7 @@ +set -ex + +BIT_IN=$1 +BITS=$(tempfile --suffix .bits) + +${XRAY_BITREAD} -F $XRAY_ROI_FRAMES -o ${BITS} -z -y ${BIT_IN} +${XRAY_DIR}/utils/bits2fasm.py ${BITS} diff --git a/minitests/roi_harness/fasm2bit.sh b/minitests/roi_harness/fasm2bit.sh index 5beb90f5..88bff430 100755 --- a/minitests/roi_harness/fasm2bit.sh +++ b/minitests/roi_harness/fasm2bit.sh @@ -27,7 +27,7 @@ echo "Design .fasm: $fasm_in" echo "Harness .bit: $bit_in" echo "Out .bit: $bit_out" -${XRAY_DIR}/tools/fasm2frame.py $fasm_in roi_partial.frm +${XRAY_DIR}/utils/fasm2frames.py --sparse $fasm_in roi_partial.frm ${XRAY_TOOLS_DIR}/xc7patch \ --part_name ${XRAY_PART} \ diff --git a/prjxray/bitstream.py b/prjxray/bitstream.py new file mode 100644 index 00000000..4f43a383 --- /dev/null +++ b/prjxray/bitstream.py @@ -0,0 +1,27 @@ +# Break frames into WORD_SIZE bit words. +WORD_SIZE_BITS = 32 + +def load_bitdata(f): + """ Read bit file and return bitdata map. + + bitdata is a map of of two sets. + The map key is the frame address. + The first sets are the word columns that have any bits set. + Word columsn are WORD_SIZE_BITS wide. + The second sets are bit index within the frame and word if it is set. + """ + bitdata = dict() + + for line in f: + line = line.split("_") + frame = int(line[1], 16) + wordidx = int(line[2], 10) + bitidx = int(line[3], 10) + + if frame not in bitdata: + bitdata[frame] = set(), set() + + bitdata[frame][0].add(wordidx) + bitdata[frame][1].add(wordidx*WORD_SIZE_BITS + bitidx) + + return bitdata diff --git a/prjxray/db.py b/prjxray/db.py index c75ce52c..21b6706d 100644 --- a/prjxray/db.py +++ b/prjxray/db.py @@ -2,6 +2,7 @@ import os.path import json from prjxray import grid from prjxray import tile +from prjxray import tile_segbits from prjxray import site_type from prjxray import connections @@ -37,7 +38,9 @@ class Database(object): self.tile_types = None self.tile_types = {} + self.tile_segbits = {} self.site_types = {} + for f in os.listdir(self.db_root): if f.endswith('.json') and f.startswith('tile_type_'): tile_type = f[len('tile_type_'):-len('.json')].lower() @@ -47,6 +50,11 @@ class Database(object): if not os.path.isfile(segbits): segbits = None + ppips = os.path.join( + self.db_root, 'ppips_{}.db'.format(tile_type)) + if not os.path.isfile(ppips): + ppips = None + mask = os.path.join( self.db_root, 'mask_{}.db'.format(tile_type)) if not os.path.isfile(mask): @@ -60,6 +68,7 @@ class Database(object): self.tile_types[tile_type.upper()] = tile.TileDbs( segbits=segbits, + ppips=ppips, mask=mask, tile_type=tile_type_file, ) @@ -124,3 +133,10 @@ class Database(object): site_type_data = json.load(f) return site_type.SiteType(site_type_data) + + def get_tile_segbits(self, tile_type): + if tile_type not in self.tile_segbits: + self.tile_segbits[tile_type] = tile_segbits.TileSegbits( + self.tile_types[tile_type.upper()]) + + return self.tile_segbits[tile_type] diff --git a/prjxray/fasm_assembler.py b/prjxray/fasm_assembler.py new file mode 100644 index 00000000..f53e91fa --- /dev/null +++ b/prjxray/fasm_assembler.py @@ -0,0 +1,140 @@ +import fasm +from prjxray import bitstream +from prjxray import grid + +class FasmLookupError(Exception): + pass + +class FasmInconsistentBits(Exception): + pass + +# How many 32-bit words for frame in a 7-series bitstream? +FRAME_WORD_COUNT = 101 + +def init_frame_at_address(frames, addr): + '''Set given frame to 0 if not initialized ''' + if not addr in frames: + frames[addr] = [0 for _i in range(FRAME_WORD_COUNT)] + +class FasmAssembler(object): + def __init__(self, db): + self.db = db + self.grid = db.grid() + + self.frames = {} + self.frames_line = {} + + def get_frames(self, sparse=False): + if not sparse: + frames = self.frames_init() + else: + frames = {} + + for (frame_addr, word_addr, bit_index), is_set in self.frames.items(): + init_frame_at_address(frames, frame_addr) + + if is_set: + frames[frame_addr][word_addr] |= 1 << bit_index + + return frames + + def frames_init(self): + '''Set all frames to 0''' + frames = {} + + for bits_info in self.grid.iter_all_frames(): + for coli in range(bits_info.bits.frames): + init_frame_at_address(frames, bits_info.bits.base_address + coli) + + return frames + + def frame_set(self, frame_addr, word_addr, bit_index, line): + '''Set given bit in given frame address and word''' + assert bit_index is not None + + key = (frame_addr, word_addr, bit_index) + if key in self.frames: + if self.frames[key] != 1: + raise FasmInconsistentBits( + 'FASM line "{}" wanted to set bit {} but was cleared by FASM line "{}"'.format( + line, key, self.frames_line[key], + )) + return + + self.frames[key] = 1 + self.frames_line[key] = line + + def frame_clear(self, frame_addr, word_addr, bit_index, line): + '''Set given bit in given frame address and word''' + assert bit_index is not None + + key = (frame_addr, word_addr, bit_index) + if key in self.frames: + if self.frames[key] != 0: + raise FasmInconsistentBits( + 'FASM line "{}" wanted to clear bit {} but was set by FASM line "{}"'.format( + line, key, self.frames_line[key], + )) + return + + self.frames[key] = 0 + self.frames_line[key] = line + + def enable_feature(self, tile, feature, address, line): + gridinfo = self.grid.gridinfo_at_tilename(tile) + + # TODO: How to determine if the feature targets BLOCK_RAM segment type? + bits = gridinfo.bits[grid.SegmentType.CLB_IO_CLK] + + seg_baseaddr = bits.base_address + seg_word_base = bits.offset + + def update_segbit(bit): + '''Set or clear a single bit in a segment at the given word column and word bit position''' + # Now we have the word column and word bit index + # Combine with the segments relative frame position to fully get the position + frame_addr = seg_baseaddr + bit.word_column + # 2 words per segment + word_addr = seg_word_base + bit.word_bit // bitstream.WORD_SIZE_BITS + bit_index = bit.word_bit % bitstream.WORD_SIZE_BITS + if bit.isset: + self.frame_set(frame_addr, word_addr, bit_index, line) + else: + self.frame_clear(frame_addr, word_addr, bit_index, line) + + segbits = self.db.get_tile_segbits(gridinfo.tile_type) + + db_k = '%s.%s' % (gridinfo.tile_type, feature) + + try: + for bit in segbits.feature_to_bits(db_k, address): + update_segbit(bit) + except KeyError: + raise FasmLookupError( + "Segment DB %s, key %s not found from line '%s'" % + (gridinfo.tile_type, db_k, line)) + + def parse_fasm_filename(self, filename): + for line in fasm.parse_fasm_filename(filename): + if not line.set_feature: + continue + + line_strs = tuple(fasm.fasm_line_to_string(line)) + assert len(line_strs) == 1 + line_str = line_strs[0] + + parts = line.set_feature.feature.split('.') + tile = parts[0] + feature = '.'.join(parts[1:]) + + # canonical_features flattens multibit feature enables to only + # single bit features, which is what enable_feature expects. + # + # canonical_features also filters out features that are not enabled, + # which are no-ops. + for flat_set_feature in fasm.canonical_features(line.set_feature): + address = 0 + if flat_set_feature.start is not None: + address = flat_set_feature.start + + self.enable_feature(tile, feature, address, line_str) diff --git a/prjxray/fasm_disassembler.py b/prjxray/fasm_disassembler.py new file mode 100644 index 00000000..34c4aebc --- /dev/null +++ b/prjxray/fasm_disassembler.py @@ -0,0 +1,130 @@ +import re +import fasm +from prjxray import bitstream + +def mk_fasm(tile_name, feature): + """ Convert matches tile and feature to FasmLine tuple. """ + # Seperate addressing of multi-bit features: + # TILE.ALUT[0] -> ('TILE', 'ALUT', '0') + # TILE.ALUT.SMALL -> ('TILE', 'ALUT.SMALL', None) + m = re.match(r'([A-Za-z0-9_]+).([^\[]+)(\[[0-9]+\])?', feature) + tag_post = m.group(2) + address = None + if m.group(3) is not None: + address = int(m.group(3)[1:-1]) + + feature = '{}.{}'.format(tile_name, tag_post) + + return fasm.FasmLine( + set_feature=fasm.SetFasmFeature( + feature=feature, + start=address, + end=None, + value=1, + value_format=None, + ), + annotations=None, + comment=None) + +class FasmDisassembler(object): + """ Given a Project X-ray data, outputs FasmLine tuples for bits set. """ + def __init__(self, db): + self.db = db + self.grid = self.db.grid() + self.segment_map = self.grid.get_segment_map() + self.decode_warnings = set() + + def find_features_in_tile(self, tile_name, bits, bitdata, verbose=False): + gridinfo = self.grid.gridinfo_at_tilename(tile_name) + + try: + tile_segbits = self.db.get_tile_segbits(gridinfo.tile_type) + except KeyError as e: + if not verbose: + return + + if gridinfo.tile_type in self.decode_warnings: + return + + comment = " WARNING: failed to load DB for tile type {}".format( + gridinfo.tile_type) + yield fasm.FasmLine( + set_feature=None, + annotations=None, + comment=comment, + ) + yield fasm.FasmLine( + set_feature=None, + annotations=[ + fasm.Annotation('missing_segbits', gridinfo.tile_type), + fasm.Annotation('exception', str(e)), + ], + comment=None, + ) + + self.decode_warnings.add(gridinfo.tile_type) + return + + for ones_matched, feature in tile_segbits.match_bitdata( + bits, bitdata): + for frame, bit in ones_matched: + bitdata[frame][1].remove(bit) + + yield mk_fasm(tile_name=tile_name, feature=feature) + + def find_features_in_bitstream(self, bitdata, verbose=False): + frames = set(bitdata.keys()) + tiles_checked = set() + + while len(frames) > 0: + frame = frames.pop() + + # Skip frames that were emptied in a previous iteration. + if not bitdata[frame]: + continue + + # Iterate over all tiles that use this frame. + for bits_info in self.segment_map.segment_info_for_frame(frame): + # Don't examine a tile twice + if bits_info.tile in tiles_checked: + continue + + # Check if this frame has any data for the relevant tile. + any_column = False + for word_idx in range(bits_info.bits.words): + if word_idx + bits_info.bits.offset in bitdata[frame][0]: + any_column = True + break + + if not any_column: + continue + + tiles_checked.add(bits_info.tile) + + for fasm_line in self.find_features_in_tile( + bits_info.tile, + bits_info.bits, + bitdata, + verbose=verbose): + yield fasm_line + + if len(bitdata[frame]) > 0 and verbose: + # Some bits were not decoded, add warning and annotations to + # FASM. + yield fasm.FasmLine( + set_feature=None, + annotations=None, + comment=" In frame 0x{:08x} {} bits were not converted.".format( + frame, len(bitdata[frame]), + )) + + for bit in bitdata[frame][1]: + wordidx = bit // bitstream.WORD_SIZE_BITS + bitidx = bit % bitstream.WORD_SIZE_BITS + annotation = fasm.Annotation('unknown_bit', + '{:08x}_{}_{}'.format(frame, wordidx, bitidx)) + yield fasm.FasmLine( + set_feature=None, + annotations=[annotation], + comment=None, + ) diff --git a/prjxray/grid.py b/prjxray/grid.py index df1a6a1b..a1a043f5 100644 --- a/prjxray/grid.py +++ b/prjxray/grid.py @@ -1,8 +1,18 @@ from collections import namedtuple +import enum +from prjxray import segment_map + +class SegmentType(enum.Enum): + # Segments describing CLB features, interconnect, clocks and IOs. + CLB_IO_CLK = 'CLB_IO_CLK' + + # Segments describing block RAM initialization. + BLOCK_RAM = 'BLOCK_RAM' GridLoc = namedtuple('GridLoc', 'grid_x grid_y') -GridInfo = namedtuple('GridInfo', 'segment sites tile_type in_roi') - +GridInfo = namedtuple('GridInfo', 'segment bits sites tile_type in_roi') +Bits = namedtuple('Bits', 'base_address frames offset words') +BitsInfo = namedtuple('BitsInfo', 'segment_type tile bits') class Grid(object): """ Object that represents grid for a given database. @@ -15,6 +25,14 @@ class Grid(object): self.tilegrid = tilegrid self.loc = {} self.tileinfo = {} + # Map of segment name to tiles in that segment + self.segments = {} + + # Map of (base_address, segment type) -> segment name + self.base_addresses = {} + + # Map of base_address -> (segment type, segment name) + self.base_addresses = {} for tile in self.tilegrid: tileinfo = self.tilegrid[tile] @@ -27,8 +45,28 @@ class Grid(object): else: in_roi = True + bits = {} + + if 'segment' in tileinfo: + if tileinfo['segment'] not in self.segments: + self.segments[tileinfo['segment']] = [] + + self.segments[tileinfo['segment']].append(tile) + + if 'bits' in tileinfo: + for k in tileinfo['bits']: + segment_type = SegmentType(k) + base_address = int(tileinfo['bits'][k]['baseaddr'], 0) + bits[segment_type] = Bits( + base_address=base_address, + frames=tileinfo['bits'][k]['frames'], + offset=tileinfo['bits'][k]['offset'], + words=tileinfo['bits'][k]['words'], + ) + self.tileinfo[tile] = GridInfo( segment=tileinfo['segment'] if 'segment' in tileinfo else None, + bits=bits, sites=tileinfo['sites'], tile_type=tileinfo['type'], in_roi=in_roi, @@ -64,3 +102,15 @@ class Grid(object): def gridinfo_at_tilename(self, tilename): return self.tileinfo[tilename] + + def iter_all_frames(self): + for tile, tileinfo in self.tileinfo.items(): + for segment_type, bits in tileinfo.bits.items(): + yield BitsInfo( + segment_type=segment_type, + tile=tile, + bits=bits, + ) + + def get_segment_map(self): + return segment_map.SegmentMap(self) diff --git a/prjxray/roi.py b/prjxray/roi.py new file mode 100644 index 00000000..15934446 --- /dev/null +++ b/prjxray/roi.py @@ -0,0 +1,49 @@ +import json + +class Roi(object): + def __init__(self, tilegrid_file, x1, x2, y1, y2): + self.tilegrid_file = tilegrid_file + self.tilegrid = None + self.x1 = x1 + self.x2 = x2 + self.y1 = y1 + self.y2 = y2 + + def tile_in_roi(self, tilej): + x = int(tilej['grid_x']) + y = int(tilej['grid_y']) + return self.x1 <= x and x <= self.x2 and self.y1 <= y and y <= self.y2 + + def read_tilegrid(self): + if not self.tilegrid: + with open(self.tilegrid_file) as f: + self.tilegrid = json.load(f) + + + def gen_tiles(self, tile_types=None): + ''' + tile_types: list of tile types to keep, or None for all + ''' + + self.read_tilegrid() + + for tile_name, tilej in self.tilegrid.items(): + if self.tile_in_roi(tilej) and (tile_types is None + or tilej['type'] in tile_types): + yield (tile_name, tilej) + + + def gen_sites(self, site_types=None): + ''' + site_types: list of site types to keep, or None for all + ''' + + self.read_tilegrid() + + for tile_name, tilej in self.tilegrid.items(): + if not self.tile_in_roi(tilej): + continue + + for site_name, site_type in tilej['sites'].items(): + if site_types is None or site_type in site_types: + yield (tile_name, site_name, site_type) diff --git a/prjxray/segment_map.py b/prjxray/segment_map.py new file mode 100644 index 00000000..3a98fa37 --- /dev/null +++ b/prjxray/segment_map.py @@ -0,0 +1,18 @@ +from intervaltree import IntervalTree, Interval +from prjxray import bitstream + +class SegmentMap(object): + def __init__(self, grid): + self.segment_tree = IntervalTree() + + for bits_info in grid.iter_all_frames(): + self.segment_tree.add(Interval( + begin=bits_info.bits.base_address, + end=bits_info.bits.base_address+bits_info.bits.frames, + data=bits_info, + )) + + def segment_info_for_frame(self, frame): + """ Return all bits info that match frame address. """ + for frame in self.segment_tree[frame]: + yield frame.data diff --git a/prjxray/tile.py b/prjxray/tile.py index 85f2ca2a..958f757b 100644 --- a/prjxray/tile.py +++ b/prjxray/tile.py @@ -1,8 +1,9 @@ from collections import namedtuple import json from prjxray import lib + """ Database files available for a tile """ -TileDbs = namedtuple('TileDbs', 'segbits mask tile_type') +TileDbs = namedtuple('TileDbs', 'segbits ppips mask tile_type') Pip = namedtuple( 'Pip', 'name net_to net_from can_invert is_directional is_pseudo') diff --git a/prjxray/tile_segbits.py b/prjxray/tile_segbits.py new file mode 100644 index 00000000..cbf5e1c3 --- /dev/null +++ b/prjxray/tile_segbits.py @@ -0,0 +1,136 @@ +from collections import namedtuple +from prjxray import bitstream +import enum + +class PsuedoPipType(enum.Enum): + ALWAYS = 'always' + DEFAULT = 'default' + HINT = 'hint' + +def read_ppips(f): + ppips = {} + + for l in f: + l = l.strip() + if not l: + continue + + feature, ppip_type = l.split(' ') + + ppips[feature] = PsuedoPipType(ppip_type) + + return ppips + +Bit = namedtuple('Bit', 'word_column word_bit isset') + +def parsebit(val): + '''Return "!012_23" => (12, 23, False)''' + isset = True + # Default is 0. Skip explicit call outs + if val[0] == '!': + isset = False + val = val[1:] + # 28_05 => 28, 05 + seg_word_column, word_bit_n = val.split('_') + + return Bit( + word_column=int(seg_word_column), + word_bit=int(word_bit_n), + isset=isset, + ) + +def read_segbits(f): + segbits = {} + + for l in f: + # CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14 + l = l.strip() + + if not l: + continue + + parts = l.split(' ') + + assert len(parts) > 1 + + segbits[parts[0]] = [parsebit(val) for val in parts[1:]] + + return segbits + +class TileSegbits(object): + def __init__(self, tile_db): + self.segbits = {} + self.ppips = {} + self.feature_addresses = {} + + if tile_db.ppips is not None: + with open(tile_db.ppips) as f: + self.ppips = read_ppips(f) + + if tile_db.segbits is not None: + with open(tile_db.segbits) as f: + self.segbits = read_segbits(f) + + for feature in self.segbits: + sidx = feature.rfind('[') + eidx = feature.rfind(']') + + if sidx != -1: + assert eidx != -1 + + base_feature = feature[:sidx] + + if base_feature not in self.feature_addresses: + self.feature_addresses[base_feature] = {} + + self.feature_addresses[base_feature][int(feature[sidx+1:eidx])] = feature + + + def match_bitdata(self, bits, bitdata): + """ Return matching features for tile bits data (grid.Bits) and bitdata. + + See bitstream.load_bitdata for details on bitdata structure. + + """ + + for feature, segbit in self.segbits.items(): + match = True + for query_bit in segbit: + frame = bits.base_address + query_bit.word_column + bitidx = bits.offset*bitstream.WORD_SIZE_BITS + query_bit.word_bit + + if frame not in bitdata: + match = not query_bit.isset + if match: + continue + else: + break + + found_bit = bitidx in bitdata[frame][1] + match = found_bit == query_bit.isset + + if not match: + break + + if not match: + continue + + def inner(): + for query_bit in segbit: + if query_bit.isset: + frame = bits.base_address + query_bit.word_column + bitidx = bits.offset*bitstream.WORD_SIZE_BITS + query_bit.word_bit + yield (frame, bitidx) + + yield (tuple(inner()), feature) + + def feature_to_bits(self, feature, address=0): + if feature in self.ppips: + return + + if address == 0 and feature in self.segbits: + for bit in self.segbits[feature]: + yield bit + else: + for bit in self.segbits[self.feature_addresses[feature][address]]: + yield bit diff --git a/third_party/fasm b/third_party/fasm index faed2f5e..46eadd95 160000 --- a/third_party/fasm +++ b/third_party/fasm @@ -1 +1 @@ -Subproject commit faed2f5e9915497c3774ec201ced8dc017cc03ae +Subproject commit 46eadd95407381752160a62972ec256a97e03f3d diff --git a/utils/bits2fasm.py b/utils/bits2fasm.py index fa3e6ade..7b132036 100755 --- a/utils/bits2fasm.py +++ b/utils/bits2fasm.py @@ -1,310 +1,47 @@ #!/usr/bin/env python3 ''' -Take raw .bits files and decode them to higher level functionality -This output is intended for debugging and not directly related to FASM -However, as of 2018-10-16, the output is being parsed to create FASM, -so be mindful when changing output format - -TODO: +Take raw .bits files and decode them to FASM. ''' -import sys, os, json, re +import os +import fasm +from prjxray import db +from prjxray import fasm_disassembler +from prjxray import bitstream +def run(db_root, bits_file): + disassembler = fasm_disassembler.FasmDisassembler(db.Database(db_root)) -class NoDB(Exception): - pass - - -def line(s=''): - print(s) - - -def comment(s): - print('# %s' % s) - - -enumdb = dict() - - -# TODO: migrate to library -def process_db(tile_type, process): - if tile_type in ('INT_L', 'INT_R'): - # interconnect - fn = "%s/%s/segbits_int_%s.db" % ( - os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"), - tile_type[-1].lower()) - else: - # sites - fn = "%s/%s/segbits_%s.db" % ( - os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"), - tile_type.lower()) - - if not os.path.exists(fn): - raise NoDB(tile_type) - - with open(fn, "r") as f: - for line in f: - process(line) - - -def get_enums(tile_type): - if tile_type in enumdb: - return enumdb[tile_type] - - enumdb[tile_type] = {} - - def process(l): - # CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14 - parts = l.strip().split() - name = parts[0] - bit_vals = parts[1:] - - # Assumption - # only 1 bit => non-enumerated value - enumdb[tile_type][name] = len(bit_vals) != 1 - - process_db(tile_type, process) - - return enumdb[tile_type] - - -def isenum(tilename, tag): - return get_enums(tilename)[tag] - - -# cache -segbitsdb = dict() - - -def get_database(tile_type): - if tile_type in segbitsdb: - return segbitsdb[tile_type] - - ret = list() - - def process(l): - ret.append(l.split()) - - process_db(tile_type, process) - - assert len(ret) - segbitsdb[tile_type] = ret - return ret - - -def mk_fasm(segj, entry): - tile_name = segj['tile_name'] - - # ex: CLBLL_L.SLICEL_X0.AFF.DMUX.O6 - tag = entry[0] - m = re.match(r'([A-Za-z0-9_]+)[.](.*)', tag) - # tile_type = m.group(1) - # the postfix, O6 in the above example - tag_post = m.group(2) - - if not isenum(segj['type'], tag): - return '%s.%s 1' % (tile_name, tag_post) - else: - # Make the selection an argument of the configruation - m = re.match(r'(.*)[.]([A-Za-z0-9_]+)', tag_post) - which = m.group(1) - value = m.group(2) - return '%s.%s %s' % (tile_name, which, value) - - -def mk_segbits(seginfo, bitdata): - baseframe = int(seginfo["baseaddr"][0], 16) - basewordidx = int(seginfo["baseaddr"][1]) - numframes = int(seginfo["frames"]) - numwords = int(seginfo["words"]) - - segbits = set() - for frame in range(baseframe, baseframe + numframes): - if frame not in bitdata: - continue - for wordidx in range(basewordidx, basewordidx + numwords): - if wordidx not in bitdata[frame]: - continue - for bitidx in bitdata[frame][wordidx]: - segbits.add( - "%02d_%02d" % - (frame - baseframe, 32 * (wordidx - basewordidx) + bitidx)) - return segbits - - -def tagmatch(entry, segbits): - for bit in entry[1:]: - if bit[0] != "!" and bit not in segbits: - return False - if bit[0] == "!" and bit[1:] in segbits: - return False - return True - - -def tag_matched(entry, segbits): - for bit in entry[1:]: - if bit[0] != "!": - segbits.remove(bit) - - -decode_warnings = set() - - -def seg_decode(seginfo, segbits, verbose=False): - fasms = set() - - # already failed? - if seginfo["type"] in decode_warnings: - return fasms - - try: - db = get_database(seginfo["type"]) - except NoDB: - verbose and comment( - "WARNING: failed to load DB for %s" % seginfo["type"]) - decode_warnings.add(seginfo["type"]) - return fasms - - for entry in db: - if not tagmatch(entry, segbits): - continue - tag_matched(entry, segbits) - #fasms.add('%s.%s 1' % (seginfo['tile_name'], entry[0])) - fasm = mk_fasm(seginfo, entry) - fasms.add(fasm) - return fasms - - -def handle_segment(segname, grid, bitdata, verbose=False): - - assert segname - - # only print bitstream tiles - if segname not in grid["segments"]: - return - seginfo = grid["segments"][segname] - - segbits = mk_segbits(seginfo, bitdata) - - fasms = seg_decode(seginfo, segbits, verbose=verbose) - - # Found something to print? - if len(segbits) == 0 and len(fasms) == 0: - return - - line('') - comment("seg %s" % (segname, )) - - for fasm in sorted(fasms): - line(fasm) - - if verbose and len(segbits) > 0: - comment('%u unknown bits' % len(segbits)) - for bit in sorted(segbits): - comment("bit %s" % bit) - - -def load_bitdata(bits_file): - bitdata = dict() - - with open(bits_file, "r") as f: - for line in f: - line = line.split("_") - frame = int(line[1], 16) - wordidx = int(line[2], 10) - bitidx = int(line[3], 10) - - if frame not in bitdata: - bitdata[frame] = dict() - - if wordidx not in bitdata[frame]: - bitdata[frame][wordidx] = set() - - bitdata[frame][wordidx].add(bitidx) - return bitdata - - -def mk_grid(): - '''Load tilegrid, flattening all blocks into one dictionary''' - - with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"), - os.getenv("XRAY_DATABASE")), "r") as f: - new_grid = json.load(f) - - # TODO: Migrate to new tilegrid format via library. - grid = {'tiles': new_grid, 'segments': {}} - - for tile_name, tile in grid['tiles'].items(): - bits = tile.get('bits', None) - if not bits: - continue - for block_name, block in bits.items(): - segname = mksegment(tile_name, block_name) - grid['segments'][segname] = { - 'baseaddr': [ - block['baseaddr'], - block['offset'], - ], - 'type': tile['type'], - 'frames': block['frames'], - 'words': block['words'], - 'tile_name': tile_name, - 'block_name': block_name, - } - return grid - - -def mksegment(tile_name, block_name): - '''Create a segment name''' - return '%s:%s' % (tile_name, block_name) - - -def tile_segnames(grid): - ret = [] - for tile_name, tile in grid['tiles'].items(): - for block_name in tile['bits'].keys(): - ret.append(mksegment(tile_name, block_name)) - return ret - - -def run(bits_file, segnames, verbose=False): - grid = mk_grid() - - bitdata = load_bitdata(bits_file) - - # Default: print all - if segnames: - for i, segname in enumerate(segnames): - # Default to common tile config area if tile given without explicit block - if ':' not in segname: - segnames[i] = mksegment(segname, 'CLB_IO_CLK') - else: - segnames = sorted(tile_segnames(grid)) - - comment('Segments: %u' % len(segnames)) - - # XXX: previously this was sorted by address, not name - # revisit? - for segname in segnames: - handle_segment(segname, grid, bitdata, verbose=verbose) + with open(bits_file) as f: + bitdata = bitstream.load_bitdata(f) + for fasm_line in disassembler.find_features_in_bitstream(bitdata): + for line in fasm.fasm_line_to_string(fasm_line): + print(line) def main(): import argparse - # XXX: tool still works, but not well - # need to eliminate segments entirely parser = argparse.ArgumentParser( - description='XXX: does not print all data?') + description='Convert 7-series bits file to FASM.') + + database_dir = os.getenv("XRAY_DATABASE_DIR") + database = os.getenv("XRAY_DATABASE") + db_root_kwargs = {} + if database_dir is None or database is None: + db_root_kwargs['required'] = True + else: + db_root_kwargs['required'] = False + db_root_kwargs['default'] = os.path.join(database_dir, database) - parser.add_argument('--verbose', action='store_true', help='') - parser.add_argument('bits_file', help='') parser.add_argument( - 'segnames', nargs='*', help='List of tile or tile:block to print') + '--db_root', help="Database root.", **db_root_kwargs) + parser.add_argument('bits_file', help='') + parser.add_argument('verbose', help='Print lines for unknown tiles and bits', + action='store_true') args = parser.parse_args() - run(args.bits_file, args.segnames, verbose=args.verbose) - + run(args.db_root, args.bits_file) if __name__ == '__main__': main() diff --git a/utils/fasm2frames.py b/utils/fasm2frames.py index db64fe7f..84ceb7ca 100755 --- a/utils/fasm2frames.py +++ b/utils/fasm2frames.py @@ -1,97 +1,11 @@ #!/usr/bin/env python3 +from __future__ import print_function +from prjxray import fasm_assembler +from prjxray import db +import argparse import os -import re -import sys -import json -import collections -import fasm - -def parsebit(val): - '''Return "!012_23" => (12, 23, False)''' - isset = True - # Default is 0. Skip explicit call outs - if val[0] == '!': - isset = False - val = val[1:] - # 28_05 => 28, 05 - seg_word_column, word_bit_n = val.split('_') - return int(seg_word_column), int(word_bit_n), isset - -# TODO: migrate to library -def process_db(tile_type, process): - fns = [ - # sites - "%s/%s/segbits_%s.db" % ( - os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"), - tile_type.lower()), - # interconnect - "%s/%s/segbits_int_%s.db" % ( - os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"), - tile_type[-1].lower()), - ] - - for fn in fns: - if os.path.exists(fn): - with open(fn, "r") as f: - for line in f: - process(line) - - -''' -Loosely based on segprint function -Maybe better to return as two distinct dictionaries? - -{ - 'tile.meh': { - 'O5': [(11, 2, False), (12, 2, True)], - 'O6': [(11, 2, True), (12, 2, False)], - }, -} -''' -segbitsdb = dict() - - -def get_database(tile_type): - if tile_type in segbitsdb: - return segbitsdb[tile_type] - - segbitsdb[tile_type] = {} - - def process(l): - # CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14 - parts = l.strip().split() - name = parts[0] - bit_vals = parts[1:] - - # Assumption - # only 1 bit => non-enumerated value - if len(bit_vals) == 1: - seg_word_column, word_bit_n, isset = parsebit(bit_vals[0]) - if not isset: - raise Exception( - "Expect single bit DB entries to be set, got %s" % l) - # Treat like an enumerated value with keys 0 or 1 - assert name not in segbitsdb[tile_type] - segbitsdb[tile_type][name] = { - '0': [(seg_word_column, word_bit_n, 0)], - '1': [(seg_word_column, word_bit_n, 1)], - } - else: - # An enumerated value - # Split the base name and selected key - m = re.match(r'(.+)[.](.+)', name) - namepart = m.group(1) - key = m.group(2) - - # May or may not be the first key encountered - bits_map = segbitsdb[tile_type].setdefault(namepart, {}) - bits_map[key] = [parsebit(x) for x in bit_vals] - - process_db(tile_type, process) - - return segbitsdb[tile_type] - +import os.path def dump_frames_verbose(frames): print() @@ -102,7 +16,6 @@ def dump_frames_verbose(frames): '0x%08X ' % addr + ', '.join(['0x%08X' % w for w in words]) + '...') - def dump_frames_sparse(frames): print() print("Frames: %d" % len(frames)) @@ -121,7 +34,6 @@ def dump_frames_sparse(frames): if w: print(' % 3d: 0x%08X' % (i, w)) - def dump_frm(f, frames): '''Write a .frm file given a list of frames, each containing a list of 101 32 bit words''' for addr in sorted(frames.keys()): @@ -129,214 +41,40 @@ def dump_frm(f, frames): f.write( '0x%08X ' % addr + ','.join(['0x%08X' % w for w in words]) + '\n') - -def mksegment(tile_name, block_name): - '''Create a segment name''' - return '%s:%s' % (tile_name, block_name) - - -def mk_grid(): - '''Load tilegrid, flattening all blocks into one dictionary''' - - with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"), - os.getenv("XRAY_DATABASE")), "r") as f: - new_grid = json.load(f) - - # TODO: Migrate to new tilegrid format via library. - grid = {'tiles': new_grid, 'segments': {}} - - for tile_name, tile in grid['tiles'].items(): - for block_name, block in tile['bits'].items(): - segname = mksegment(tile_name, block_name) - grid['segments'][segname] = { - 'baseaddr': [ - block['baseaddr'], - block['offset'], - ], - 'type': tile['type'], - 'frames': block['frames'], - 'words': block['words'], - 'tile_name': tile_name, - 'block_name': block_name, - } - return grid - - -def frame_init(frames, addr): - '''Set given frame to 0''' - if not addr in frames: - frames[addr] = [0 for _i in range(101)] - - -def frames_init(frames, grid): - '''Set all frames to 0''' - for segj in grid['segments'].values(): - seg_baseaddr, seg_word_base = segj['baseaddr'] - seg_baseaddr = int(seg_baseaddr, 0) - for coli in range(segj['frames']): - frame_init(frames, seg_baseaddr + coli) - - -def frame_set(frames, frame_addr, word_addr, bit_index): - '''Set given bit in given frame address and word''' - frames[frame_addr][word_addr] |= 1 << bit_index - - -def frame_clear(frames, frame_addr, word_addr, bit_index): - '''Set given bit in given frame address and word''' - frames[frame_addr][word_addr] &= 0xFFFFFFFF ^ (1 << bit_index) - - -def parse_line(l): - # Comment - # Remove all text including and after # - i = l.rfind('#') - if i >= 0: - l = l[0:i] - l = l.strip() - - # Ignore blank lines - if not l: - return - - # tile.site.stuff value - # INT_L_X10Y102.CENTER_INTER_L.IMUX_L1 EE2END0 - # Optional value - m = re.match(r'([a-zA-Z0-9_]+)[.]([a-zA-Z0-9_.\[\]]+)([ ](.+))?', l) - if not m: - raise FASMSyntaxError("Bad line: %s" % l) - tile = m.group(1) - name = m.group(2) - value = m.group(4) - - return tile, name, value - - -def check_duplicate(used_names, tile, name, l, line_number): - '''Throw an exception if a conflicting FASM directive was given''' - used_name = (tile, name) - old_line_number = used_names.get(used_name, None) - if old_line_number: - raise FASMSyntaxError( - "Duplicate name lines %d and %d, second line: %s" % - (old_line_number, line_number, l)) - used_names[used_name] = line_number - - -def update_segbit( - frames, seg_word_column, word_bit_n, isset, seg_baseaddr, - seg_word_base): - '''Set or clear a single bit in a segment at the given word column and word bit position''' - # Now we have the word column and word bit index - # Combine with the segments relative frame position to fully get the position - frame_addr = seg_baseaddr + seg_word_column - # 2 words per segment - word_addr = seg_word_base + word_bit_n // 32 - bit_index = word_bit_n % 32 - if isset: - frame_set(frames, frame_addr, word_addr, bit_index) - else: - frame_clear(frames, frame_addr, word_addr, bit_index) - - -def default_value(db_vals, name): - # If its binary, allow omitted value default to 1 - if tuple(sorted(db_vals.keys())) == ('0', '1'): - return '1' - else: - raise FASMSyntaxError( - "Enumerable entry %s must have explicit value" % name) - - -def process_line(line_number, l, grid, frames, used_names): - parsed = parse_line(l) - # empty line - if not parsed: - return - tile_name, name, value = parsed - check_duplicate(used_names, tile_name, name, l, line_number) - - tilej = grid['tiles'][tile_name] - for block_name, block in tilej['bits'].items(): - segname = mksegment(tile_name, block_name) - - segj = grid['segments'][segname] - seg_baseaddr, seg_word_base = segj['baseaddr'] - seg_baseaddr = int(seg_baseaddr, 0) - - # Ensure that all frames exist for this segment - # FIXME: type dependent - for coli in range(segj['frames']): - frame_init(frames, seg_baseaddr + coli) - - # Now lets look up the bits we need frames for - segdb = get_database(segj['type']) - - db_k = '%s.%s' % (tilej['type'], name) - try: - db_vals = segdb[db_k] - except KeyError: - raise FASMSyntaxError( - "Segment DB %s, key %s not found from line '%s'" % - (segj['type'], db_k, l)) from None - - if not value: - value = default_value(db_vals, name) - - # Get the specific entry we need - try: - db_vals = db_vals[value] - except KeyError: - raise FASMSyntaxError( - "Invalid entry %s. Valid entries are %s" % - (value, db_vals.keys())) - - for seg_word_column, word_bit_n, isset in db_vals: - update_segbit( - frames, seg_word_column, word_bit_n, isset, seg_baseaddr, - seg_word_base) - - -def run(f_in, f_out, sparse=False, debug=False): - # address to array of 101 32 bit words - frames = {} - # Directives we've seen so far - # Complain if there is a duplicate - # Contains line number of last entry - used_names = {} - - grid = mk_grid() - - if not sparse: - # Initiaize bitstream to 0 - frames_init(frames, grid) - - for line_number, l in enumerate(f_in, 1): - process_line(line_number, l, grid, frames, used_names) +def run(db_root, filename_in, f_out, sparse=False, debug=False): + assembler = fasm_assembler.FasmAssembler(db.Database(db_root)) + assembler.parse_fasm_filename(filename_in) + frames = assembler.get_frames(sparse=sparse) if debug: - #dump_frames_verbose(frames) dump_frames_sparse(frames) dump_frm(f_out, frames) -if __name__ == '__main__': - import argparse - +def main(): parser = argparse.ArgumentParser( description= 'Convert FPGA configuration description ("FPGA assembly") into binary frame equivalent' ) + database_dir = os.getenv("XRAY_DATABASE_DIR") + database = os.getenv("XRAY_DATABASE") + db_root_kwargs = {} + if database_dir is None or database is None: + db_root_kwargs['required'] = True + else: + db_root_kwargs['required'] = False + db_root_kwargs['default'] = os.path.join(database_dir, database) + + parser.add_argument( + '--db_root', help="Database root.", **db_root_kwargs) parser.add_argument( '--sparse', action='store_true', help="Don't zero fill all frames") parser.add_argument( '--debug', action='store_true', help="Print debug dump") parser.add_argument( 'fn_in', - default='/dev/stdin', - nargs='?', help='Input FPGA assembly (.fasm) file') parser.add_argument( 'fn_out', @@ -346,7 +84,12 @@ if __name__ == '__main__': args = parser.parse_args() run( - open(args.fn_in, 'r'), - open(args.fn_out, 'w'), + db_root=args.db_root, + filename_in=args.fn_in, + f_out=open(args.fn_out, 'w'), sparse=args.sparse, - debug=args.debug) + debug=args.debug + ) + +if __name__ == '__main__': + main() diff --git a/utils/fasm2pips.py b/utils/fasm2pips.py new file mode 100644 index 00000000..8b2da11c --- /dev/null +++ b/utils/fasm2pips.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +from __future__ import print_function +import os.path +import fasm +from prjxray import db + +def main(): + import argparse + + parser = argparse.ArgumentParser( + description= + 'Convert FASM to pip list' + ) + + database_dir = os.getenv("XRAY_DATABASE_DIR") + database = os.getenv("XRAY_DATABASE") + db_root_kwargs = {} + if database_dir is None or database is None: + db_root_kwargs['required'] = True + else: + db_root_kwargs['required'] = False + db_root_kwargs['default'] = os.path.join(database_dir, database) + + parser.add_argument( + '--db_root', help="Database root.", **db_root_kwargs) + parser.add_argument( + 'fn_in', + help='Input FPGA assembly (.fasm) file') + + args = parser.parse_args() + database = db.Database(args.db_root) + grid = database.grid() + + def inner(): + for line in fasm.parse_fasm_filename(args.fn_in): + if not line.set_feature: + continue + + parts = line.set_feature.feature.split('.') + tile = parts[0] + gridinfo = grid.gridinfo_at_tilename(tile) + + tile_type = database.get_tile_type(gridinfo.tile_type) + + for pip in tile_type.pips: + if pip.net_from == parts[2] and pip.net_to == parts[1]: + yield '{}/{}.{}'.format(tile, gridinfo.tile_type, pip.name) + + print('highlight_objects [concat {}]'.format(' '.join('[get_pips {}]'.format(pip) for pip in inner()))) + +if __name__ == '__main__': + main() From b22001a645e0621e5a025f402c14d5c52e95ea76 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Fri, 19 Oct 2018 14:24:02 -0700 Subject: [PATCH 04/15] Add tool for outputing canonical prjxray FASM. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- third_party/fasm | 2 +- utils/bits2fasm.py | 12 ++++--- utils/fasm2fasm.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 utils/fasm2fasm.py diff --git a/third_party/fasm b/third_party/fasm index 46eadd95..f78c27ec 160000 --- a/third_party/fasm +++ b/third_party/fasm @@ -1 +1 @@ -Subproject commit 46eadd95407381752160a62972ec256a97e03f3d +Subproject commit f78c27ecc34236df3cd4c845c13bdf279d30608c diff --git a/utils/bits2fasm.py b/utils/bits2fasm.py index 7b132036..c86169d1 100755 --- a/utils/bits2fasm.py +++ b/utils/bits2fasm.py @@ -9,15 +9,15 @@ from prjxray import db from prjxray import fasm_disassembler from prjxray import bitstream -def run(db_root, bits_file): +def run(db_root, bits_file, verbose, canonical): disassembler = fasm_disassembler.FasmDisassembler(db.Database(db_root)) with open(bits_file) as f: bitdata = bitstream.load_bitdata(f) - for fasm_line in disassembler.find_features_in_bitstream(bitdata): - for line in fasm.fasm_line_to_string(fasm_line): - print(line) + print(fasm.fasm_tuple_to_string( + disassembler.find_features_in_bitstream(bitdata, verbose=verbose), + canonical=canonical)) def main(): import argparse @@ -39,9 +39,11 @@ def main(): parser.add_argument('bits_file', help='') parser.add_argument('verbose', help='Print lines for unknown tiles and bits', action='store_true') + parser.add_argument('--canonical', help='Output canonical bitstream.', + action='store_true') args = parser.parse_args() - run(args.db_root, args.bits_file) + run(args.db_root, args.bits_file, args.verbose, args.canonical) if __name__ == '__main__': main() diff --git a/utils/fasm2fasm.py b/utils/fasm2fasm.py new file mode 100644 index 00000000..4b9f3139 --- /dev/null +++ b/utils/fasm2fasm.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +''' +Takes input FASM and outputs optionally canonical FASM. +''' + +import os +import fasm +from prjxray import db + +def process_fasm(db_root, fasm_file, canonical): + database = db.Database(db_root) + grid = database.grid() + + for fasm_line in fasm.parse_fasm_filename(fasm_file): + if not fasm_line.set_feature: + if not canonical: + yield fasm_line + + for feature in fasm.canonical_features(fasm_line.set_feature): + parts = feature.feature.split('.') + tile = parts[0] + + + gridinfo = grid.gridinfo_at_tilename(tile) + tile_segbits = database.get_tile_segbits(gridinfo.tile_type) + + address = 0 + if feature.start is not None: + address = feature.start + + feature_name = '{}.{}'.format(gridinfo.tile_type, '.'.join(parts[1:])) + + # Convert feature to bits. If no bits are set, feature is + # psuedo pip, and should not be output from canonical FASM. + bits = tuple(tile_segbits.feature_to_bits(feature_name, address=address)) + if len(bits) == 0 and canonical: + continue + + # In canonical output, only output the canonical features. + if canonical: + yield fasm.FasmLine( + set_feature=feature, + annotations=None, + comment=None, + ) + + # If not in canonical mode, output original FASM line + if not canonical: + yield fasm_line + + +def run(db_root, fasm_file, canonical): + print(fasm.fasm_tuple_to_string( + process_fasm(db_root, fasm_file, canonical), + canonical=canonical)) + +def main(): + import argparse + + parser = argparse.ArgumentParser( + description='Read FASM file, check against database, and output optionally canonical FASM.') + + database_dir = os.getenv("XRAY_DATABASE_DIR") + database = os.getenv("XRAY_DATABASE") + db_root_kwargs = {} + if database_dir is None or database is None: + db_root_kwargs['required'] = True + else: + db_root_kwargs['required'] = False + db_root_kwargs['default'] = os.path.join(database_dir, database) + + parser.add_argument( + '--db_root', help="Database root.", **db_root_kwargs) + parser.add_argument('fasm_file', help='Input FASM file') + parser.add_argument('--canonical', help='Output canonical bitstream.', + action='store_true') + args = parser.parse_args() + + run(args.db_root, args.fasm_file, args.canonical) + +if __name__ == '__main__': + main() + From e7370d6fdc0b5f7484fd4b7ee82669f08454bc39 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Fri, 19 Oct 2018 16:19:22 -0700 Subject: [PATCH 05/15] Run make format. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fuzzers/011-ffconfig/top.py | 6 +-- fuzzers/012-clbn5ffmux/top.py | 6 +-- fuzzers/013-clbncy0/top.py | 6 +-- fuzzers/015-clbnffmux/top.py | 6 +-- fuzzers/016-clbnoutmux/top.py | 6 +-- fuzzers/018-clbram/top.py | 3 +- fuzzers/019-ndi1mux/top.py | 3 +- prjxray/bitstream.py | 3 +- prjxray/db.py | 2 +- prjxray/fasm_assembler.py | 22 +++++++--- prjxray/fasm_disassembler.py | 77 ++++++++++++++++++----------------- prjxray/grid.py | 13 +++--- prjxray/roi.py | 7 ++-- prjxray/segment_map.py | 10 +++-- prjxray/tile.py | 1 - prjxray/tile_segbits.py | 28 ++++++++----- prjxray/util.py | 15 ++++--- utils/bits2fasm.py | 23 +++++++---- utils/fasm2fasm.py | 36 ++++++++-------- utils/fasm2frames.py | 15 +++---- utils/fasm2pips.py | 18 ++++---- 21 files changed, 170 insertions(+), 136 deletions(-) diff --git a/fuzzers/011-ffconfig/top.py b/fuzzers/011-ffconfig/top.py index 3baec636..e7bc479f 100644 --- a/fuzzers/011-ffconfig/top.py +++ b/fuzzers/011-ffconfig/top.py @@ -13,9 +13,9 @@ f.write("i,prim,loc,bel,init\n") def gen_slices(): for _tile_name, site_name, _site_type in util.get_roi().gen_sites([ - 'SLICEL', - 'SLICEM', - ]): + 'SLICEL', + 'SLICEM', + ]): yield site_name diff --git a/fuzzers/012-clbn5ffmux/top.py b/fuzzers/012-clbn5ffmux/top.py index efb8079d..e7d25219 100644 --- a/fuzzers/012-clbn5ffmux/top.py +++ b/fuzzers/012-clbn5ffmux/top.py @@ -7,10 +7,8 @@ print('//Requested CLBs: %s' % str(CLBN)) def gen_slices(): - for _tile_name, site_name, _site_type in util.get_roi().gen_sites([ - 'SLICEL', - 'SLICEM' - ]): + for _tile_name, site_name, _site_type in util.get_roi().gen_sites( + ['SLICEL', 'SLICEM']): yield site_name diff --git a/fuzzers/013-clbncy0/top.py b/fuzzers/013-clbncy0/top.py index af32d952..4069a8f4 100644 --- a/fuzzers/013-clbncy0/top.py +++ b/fuzzers/013-clbncy0/top.py @@ -7,10 +7,8 @@ print('//Requested CLBs: %s' % str(CLBN)) def gen_slices(): - for _tile_name, site_name, _site_type in util.get_roi().gen_sites([ - 'SLICEL', - 'SLICEM' - ]): + for _tile_name, site_name, _site_type in util.get_roi().gen_sites( + ['SLICEL', 'SLICEM']): yield site_name diff --git a/fuzzers/015-clbnffmux/top.py b/fuzzers/015-clbnffmux/top.py index 052e6e24..352f1fd8 100644 --- a/fuzzers/015-clbnffmux/top.py +++ b/fuzzers/015-clbnffmux/top.py @@ -8,9 +8,9 @@ print('//Requested CLBs: %s' % str(CLBN)) def gen_slices(): for _tile_name, site_name, _site_type in util.get_roi().gen_sites([ - 'SLICEL', - 'SLICEM', - ]): + 'SLICEL', + 'SLICEM', + ]): yield site_name diff --git a/fuzzers/016-clbnoutmux/top.py b/fuzzers/016-clbnoutmux/top.py index 72785a1c..068a38eb 100644 --- a/fuzzers/016-clbnoutmux/top.py +++ b/fuzzers/016-clbnoutmux/top.py @@ -7,10 +7,8 @@ print('//Requested CLBs: %s' % str(CLBN)) def gen_slices(): - for _tile_name, site_name, _site_type in util.get_roi().gen_sites([ - 'SLICEL', - 'SLICEM' - ]): + for _tile_name, site_name, _site_type in util.get_roi().gen_sites( + ['SLICEL', 'SLICEM']): yield site_name diff --git a/fuzzers/018-clbram/top.py b/fuzzers/018-clbram/top.py index b3c4fe30..97247283 100644 --- a/fuzzers/018-clbram/top.py +++ b/fuzzers/018-clbram/top.py @@ -24,7 +24,8 @@ print('//Requested CLBs: %s' % str(CLBN)) def gen_slicems(): - for _tile_name, site_name, _site_type in util.get_roi().gen_sites(['SLICEM']): + for _tile_name, site_name, _site_type in util.get_roi().gen_sites( + ['SLICEM']): yield site_name diff --git a/fuzzers/019-ndi1mux/top.py b/fuzzers/019-ndi1mux/top.py index 47c65f34..10c6a75a 100644 --- a/fuzzers/019-ndi1mux/top.py +++ b/fuzzers/019-ndi1mux/top.py @@ -7,7 +7,8 @@ print('//Requested CLBs: %s' % str(CLBN)) def gen_slicems(): - for _tile_name, site_name, _site_type in util.get_roi().gen_sites(['SLICEM']): + for _tile_name, site_name, _site_type in util.get_roi().gen_sites( + ['SLICEM']): yield site_name diff --git a/prjxray/bitstream.py b/prjxray/bitstream.py index 4f43a383..ede19df2 100644 --- a/prjxray/bitstream.py +++ b/prjxray/bitstream.py @@ -1,6 +1,7 @@ # Break frames into WORD_SIZE bit words. WORD_SIZE_BITS = 32 + def load_bitdata(f): """ Read bit file and return bitdata map. @@ -22,6 +23,6 @@ def load_bitdata(f): bitdata[frame] = set(), set() bitdata[frame][0].add(wordidx) - bitdata[frame][1].add(wordidx*WORD_SIZE_BITS + bitidx) + bitdata[frame][1].add(wordidx * WORD_SIZE_BITS + bitidx) return bitdata diff --git a/prjxray/db.py b/prjxray/db.py index 21b6706d..200bf0a0 100644 --- a/prjxray/db.py +++ b/prjxray/db.py @@ -137,6 +137,6 @@ class Database(object): def get_tile_segbits(self, tile_type): if tile_type not in self.tile_segbits: self.tile_segbits[tile_type] = tile_segbits.TileSegbits( - self.tile_types[tile_type.upper()]) + self.tile_types[tile_type.upper()]) return self.tile_segbits[tile_type] diff --git a/prjxray/fasm_assembler.py b/prjxray/fasm_assembler.py index f53e91fa..75290a87 100644 --- a/prjxray/fasm_assembler.py +++ b/prjxray/fasm_assembler.py @@ -2,20 +2,25 @@ import fasm from prjxray import bitstream from prjxray import grid + class FasmLookupError(Exception): pass + class FasmInconsistentBits(Exception): pass + # How many 32-bit words for frame in a 7-series bitstream? FRAME_WORD_COUNT = 101 + def init_frame_at_address(frames, addr): '''Set given frame to 0 if not initialized ''' if not addr in frames: frames[addr] = [0 for _i in range(FRAME_WORD_COUNT)] + class FasmAssembler(object): def __init__(self, db): self.db = db @@ -44,7 +49,8 @@ class FasmAssembler(object): for bits_info in self.grid.iter_all_frames(): for coli in range(bits_info.bits.frames): - init_frame_at_address(frames, bits_info.bits.base_address + coli) + init_frame_at_address( + frames, bits_info.bits.base_address + coli) return frames @@ -56,8 +62,11 @@ class FasmAssembler(object): if key in self.frames: if self.frames[key] != 1: raise FasmInconsistentBits( - 'FASM line "{}" wanted to set bit {} but was cleared by FASM line "{}"'.format( - line, key, self.frames_line[key], + 'FASM line "{}" wanted to set bit {} but was cleared by FASM line "{}"' + .format( + line, + key, + self.frames_line[key], )) return @@ -72,8 +81,11 @@ class FasmAssembler(object): if key in self.frames: if self.frames[key] != 0: raise FasmInconsistentBits( - 'FASM line "{}" wanted to clear bit {} but was set by FASM line "{}"'.format( - line, key, self.frames_line[key], + 'FASM line "{}" wanted to clear bit {} but was set by FASM line "{}"' + .format( + line, + key, + self.frames_line[key], )) return diff --git a/prjxray/fasm_disassembler.py b/prjxray/fasm_disassembler.py index 34c4aebc..7b03c438 100644 --- a/prjxray/fasm_disassembler.py +++ b/prjxray/fasm_disassembler.py @@ -2,6 +2,7 @@ import re import fasm from prjxray import bitstream + def mk_fasm(tile_name, feature): """ Convert matches tile and feature to FasmLine tuple. """ # Seperate addressing of multi-bit features: @@ -16,18 +17,20 @@ def mk_fasm(tile_name, feature): feature = '{}.{}'.format(tile_name, tag_post) return fasm.FasmLine( - set_feature=fasm.SetFasmFeature( - feature=feature, - start=address, - end=None, - value=1, - value_format=None, - ), - annotations=None, - comment=None) + set_feature=fasm.SetFasmFeature( + feature=feature, + start=address, + end=None, + value=1, + value_format=None, + ), + annotations=None, + comment=None) + class FasmDisassembler(object): """ Given a Project X-ray data, outputs FasmLine tuples for bits set. """ + def __init__(self, db): self.db = db self.grid = self.db.grid() @@ -47,26 +50,25 @@ class FasmDisassembler(object): return comment = " WARNING: failed to load DB for tile type {}".format( - gridinfo.tile_type) + gridinfo.tile_type) yield fasm.FasmLine( - set_feature=None, - annotations=None, - comment=comment, - ) + set_feature=None, + annotations=None, + comment=comment, + ) yield fasm.FasmLine( - set_feature=None, - annotations=[ - fasm.Annotation('missing_segbits', gridinfo.tile_type), - fasm.Annotation('exception', str(e)), - ], - comment=None, - ) + set_feature=None, + annotations=[ + fasm.Annotation('missing_segbits', gridinfo.tile_type), + fasm.Annotation('exception', str(e)), + ], + comment=None, + ) self.decode_warnings.add(gridinfo.tile_type) return - for ones_matched, feature in tile_segbits.match_bitdata( - bits, bitdata): + for ones_matched, feature in tile_segbits.match_bitdata(bits, bitdata): for frame, bit in ones_matched: bitdata[frame][1].remove(bit) @@ -102,9 +104,7 @@ class FasmDisassembler(object): tiles_checked.add(bits_info.tile) for fasm_line in self.find_features_in_tile( - bits_info.tile, - bits_info.bits, - bitdata, + bits_info.tile, bits_info.bits, bitdata, verbose=verbose): yield fasm_line @@ -112,19 +112,22 @@ class FasmDisassembler(object): # Some bits were not decoded, add warning and annotations to # FASM. yield fasm.FasmLine( - set_feature=None, - annotations=None, - comment=" In frame 0x{:08x} {} bits were not converted.".format( - frame, len(bitdata[frame]), - )) + set_feature=None, + annotations=None, + comment=" In frame 0x{:08x} {} bits were not converted.". + format( + frame, + len(bitdata[frame]), + )) for bit in bitdata[frame][1]: wordidx = bit // bitstream.WORD_SIZE_BITS bitidx = bit % bitstream.WORD_SIZE_BITS - annotation = fasm.Annotation('unknown_bit', - '{:08x}_{}_{}'.format(frame, wordidx, bitidx)) + annotation = fasm.Annotation( + 'unknown_bit', '{:08x}_{}_{}'.format( + frame, wordidx, bitidx)) yield fasm.FasmLine( - set_feature=None, - annotations=[annotation], - comment=None, - ) + set_feature=None, + annotations=[annotation], + comment=None, + ) diff --git a/prjxray/grid.py b/prjxray/grid.py index a1a043f5..a31318ee 100644 --- a/prjxray/grid.py +++ b/prjxray/grid.py @@ -2,6 +2,7 @@ from collections import namedtuple import enum from prjxray import segment_map + class SegmentType(enum.Enum): # Segments describing CLB features, interconnect, clocks and IOs. CLB_IO_CLK = 'CLB_IO_CLK' @@ -9,11 +10,13 @@ class SegmentType(enum.Enum): # Segments describing block RAM initialization. BLOCK_RAM = 'BLOCK_RAM' + GridLoc = namedtuple('GridLoc', 'grid_x grid_y') GridInfo = namedtuple('GridInfo', 'segment bits sites tile_type in_roi') Bits = namedtuple('Bits', 'base_address frames offset words') BitsInfo = namedtuple('BitsInfo', 'segment_type tile bits') + class Grid(object): """ Object that represents grid for a given database. @@ -62,7 +65,7 @@ class Grid(object): frames=tileinfo['bits'][k]['frames'], offset=tileinfo['bits'][k]['offset'], words=tileinfo['bits'][k]['words'], - ) + ) self.tileinfo[tile] = GridInfo( segment=tileinfo['segment'] if 'segment' in tileinfo else None, @@ -107,10 +110,10 @@ class Grid(object): for tile, tileinfo in self.tileinfo.items(): for segment_type, bits in tileinfo.bits.items(): yield BitsInfo( - segment_type=segment_type, - tile=tile, - bits=bits, - ) + segment_type=segment_type, + tile=tile, + bits=bits, + ) def get_segment_map(self): return segment_map.SegmentMap(self) diff --git a/prjxray/roi.py b/prjxray/roi.py index 15934446..e195c60a 100644 --- a/prjxray/roi.py +++ b/prjxray/roi.py @@ -1,5 +1,6 @@ import json + class Roi(object): def __init__(self, tilegrid_file, x1, x2, y1, y2): self.tilegrid_file = tilegrid_file @@ -12,14 +13,13 @@ class Roi(object): def tile_in_roi(self, tilej): x = int(tilej['grid_x']) y = int(tilej['grid_y']) - return self.x1 <= x and x <= self.x2 and self.y1 <= y and y <= self.y2 + return self.x1 <= x and x <= self.x2 and self.y1 <= y and y <= self.y2 def read_tilegrid(self): if not self.tilegrid: with open(self.tilegrid_file) as f: self.tilegrid = json.load(f) - def gen_tiles(self, tile_types=None): ''' tile_types: list of tile types to keep, or None for all @@ -29,10 +29,9 @@ class Roi(object): for tile_name, tilej in self.tilegrid.items(): if self.tile_in_roi(tilej) and (tile_types is None - or tilej['type'] in tile_types): + or tilej['type'] in tile_types): yield (tile_name, tilej) - def gen_sites(self, site_types=None): ''' site_types: list of site types to keep, or None for all diff --git a/prjxray/segment_map.py b/prjxray/segment_map.py index 3a98fa37..56ee8f68 100644 --- a/prjxray/segment_map.py +++ b/prjxray/segment_map.py @@ -1,15 +1,17 @@ from intervaltree import IntervalTree, Interval from prjxray import bitstream + class SegmentMap(object): def __init__(self, grid): self.segment_tree = IntervalTree() for bits_info in grid.iter_all_frames(): - self.segment_tree.add(Interval( - begin=bits_info.bits.base_address, - end=bits_info.bits.base_address+bits_info.bits.frames, - data=bits_info, + self.segment_tree.add( + Interval( + begin=bits_info.bits.base_address, + end=bits_info.bits.base_address + bits_info.bits.frames, + data=bits_info, )) def segment_info_for_frame(self, frame): diff --git a/prjxray/tile.py b/prjxray/tile.py index 958f757b..f8b4770b 100644 --- a/prjxray/tile.py +++ b/prjxray/tile.py @@ -1,7 +1,6 @@ from collections import namedtuple import json from prjxray import lib - """ Database files available for a tile """ TileDbs = namedtuple('TileDbs', 'segbits ppips mask tile_type') diff --git a/prjxray/tile_segbits.py b/prjxray/tile_segbits.py index cbf5e1c3..b8737b7f 100644 --- a/prjxray/tile_segbits.py +++ b/prjxray/tile_segbits.py @@ -2,11 +2,13 @@ from collections import namedtuple from prjxray import bitstream import enum + class PsuedoPipType(enum.Enum): ALWAYS = 'always' DEFAULT = 'default' HINT = 'hint' + def read_ppips(f): ppips = {} @@ -21,8 +23,10 @@ def read_ppips(f): return ppips + Bit = namedtuple('Bit', 'word_column word_bit isset') + def parsebit(val): '''Return "!012_23" => (12, 23, False)''' isset = True @@ -34,10 +38,11 @@ def parsebit(val): seg_word_column, word_bit_n = val.split('_') return Bit( - word_column=int(seg_word_column), - word_bit=int(word_bit_n), - isset=isset, - ) + word_column=int(seg_word_column), + word_bit=int(word_bit_n), + isset=isset, + ) + def read_segbits(f): segbits = {} @@ -57,6 +62,7 @@ def read_segbits(f): return segbits + class TileSegbits(object): def __init__(self, tile_db): self.segbits = {} @@ -65,7 +71,7 @@ class TileSegbits(object): if tile_db.ppips is not None: with open(tile_db.ppips) as f: - self.ppips = read_ppips(f) + self.ppips = read_ppips(f) if tile_db.segbits is not None: with open(tile_db.segbits) as f: @@ -83,8 +89,8 @@ class TileSegbits(object): if base_feature not in self.feature_addresses: self.feature_addresses[base_feature] = {} - self.feature_addresses[base_feature][int(feature[sidx+1:eidx])] = feature - + self.feature_addresses[base_feature][int( + feature[sidx + 1:eidx])] = feature def match_bitdata(self, bits, bitdata): """ Return matching features for tile bits data (grid.Bits) and bitdata. @@ -96,8 +102,8 @@ class TileSegbits(object): for feature, segbit in self.segbits.items(): match = True for query_bit in segbit: - frame = bits.base_address + query_bit.word_column - bitidx = bits.offset*bitstream.WORD_SIZE_BITS + query_bit.word_bit + frame = bits.base_address + query_bit.word_column + bitidx = bits.offset * bitstream.WORD_SIZE_BITS + query_bit.word_bit if frame not in bitdata: match = not query_bit.isset @@ -118,8 +124,8 @@ class TileSegbits(object): def inner(): for query_bit in segbit: if query_bit.isset: - frame = bits.base_address + query_bit.word_column - bitidx = bits.offset*bitstream.WORD_SIZE_BITS + query_bit.word_bit + frame = bits.base_address + query_bit.word_column + bitidx = bits.offset * bitstream.WORD_SIZE_BITS + query_bit.word_bit yield (frame, bitidx) yield (tuple(inner()), feature) diff --git a/prjxray/util.py b/prjxray/util.py index e08285ce..ae600121 100644 --- a/prjxray/util.py +++ b/prjxray/util.py @@ -2,10 +2,12 @@ import os import re from .roi import Roi + def get_db_root(): return "%s/%s" % ( os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE")) + def roi_xy(): x1 = int(os.getenv('XRAY_ROI_GRID_X1')) x2 = int(os.getenv('XRAY_ROI_GRID_X2')) @@ -14,6 +16,7 @@ def roi_xy(): return (x1, x2), (y1, y2) + def slice_xy(): '''Return (X1, X2), (Y1, Y2) from XRAY_ROI, exclusive end (for xrange)''' # SLICE_X12Y100:SLICE_X27Y149 @@ -24,14 +27,16 @@ def slice_xy(): ms = [int(m.group(i + 1)) for i in range(4)] return ((ms[0], ms[2] + 1), (ms[1], ms[3] + 1)) + def get_roi(): (x1, x2), (y1, y2) = roi_xy() return Roi( - tilegrid_file=os.path.join(get_db_root(), 'tilegrid.json'), - x1=x1, - x2=x2, - y1=y1, - y2=y2) + tilegrid_file=os.path.join(get_db_root(), 'tilegrid.json'), + x1=x1, + x2=x2, + y1=y1, + y2=y2) + # we know that all bits for CLB MUXes are in frames 30 and 31, so filter all other bits def bitfilter_clb_mux(frame_idx, bit_idx): diff --git a/utils/bits2fasm.py b/utils/bits2fasm.py index c86169d1..9116b6bd 100755 --- a/utils/bits2fasm.py +++ b/utils/bits2fasm.py @@ -9,15 +9,18 @@ from prjxray import db from prjxray import fasm_disassembler from prjxray import bitstream + def run(db_root, bits_file, verbose, canonical): disassembler = fasm_disassembler.FasmDisassembler(db.Database(db_root)) with open(bits_file) as f: bitdata = bitstream.load_bitdata(f) - print(fasm.fasm_tuple_to_string( - disassembler.find_features_in_bitstream(bitdata, verbose=verbose), - canonical=canonical)) + print( + fasm.fasm_tuple_to_string( + disassembler.find_features_in_bitstream(bitdata, verbose=verbose), + canonical=canonical)) + def main(): import argparse @@ -34,16 +37,18 @@ def main(): db_root_kwargs['required'] = False db_root_kwargs['default'] = os.path.join(database_dir, database) - parser.add_argument( - '--db_root', help="Database root.", **db_root_kwargs) + parser.add_argument('--db_root', help="Database root.", **db_root_kwargs) parser.add_argument('bits_file', help='') - parser.add_argument('verbose', help='Print lines for unknown tiles and bits', - action='store_true') - parser.add_argument('--canonical', help='Output canonical bitstream.', - action='store_true') + parser.add_argument( + 'verbose', + help='Print lines for unknown tiles and bits', + action='store_true') + parser.add_argument( + '--canonical', help='Output canonical bitstream.', action='store_true') args = parser.parse_args() run(args.db_root, args.bits_file, args.verbose, args.canonical) + if __name__ == '__main__': main() diff --git a/utils/fasm2fasm.py b/utils/fasm2fasm.py index 4b9f3139..b927ffcd 100644 --- a/utils/fasm2fasm.py +++ b/utils/fasm2fasm.py @@ -7,6 +7,7 @@ import os import fasm from prjxray import db + def process_fasm(db_root, fasm_file, canonical): database = db.Database(db_root) grid = database.grid() @@ -20,7 +21,6 @@ def process_fasm(db_root, fasm_file, canonical): parts = feature.feature.split('.') tile = parts[0] - gridinfo = grid.gridinfo_at_tilename(tile) tile_segbits = database.get_tile_segbits(gridinfo.tile_type) @@ -28,21 +28,23 @@ def process_fasm(db_root, fasm_file, canonical): if feature.start is not None: address = feature.start - feature_name = '{}.{}'.format(gridinfo.tile_type, '.'.join(parts[1:])) + feature_name = '{}.{}'.format( + gridinfo.tile_type, '.'.join(parts[1:])) # Convert feature to bits. If no bits are set, feature is # psuedo pip, and should not be output from canonical FASM. - bits = tuple(tile_segbits.feature_to_bits(feature_name, address=address)) + bits = tuple( + tile_segbits.feature_to_bits(feature_name, address=address)) if len(bits) == 0 and canonical: continue # In canonical output, only output the canonical features. if canonical: yield fasm.FasmLine( - set_feature=feature, - annotations=None, - comment=None, - ) + set_feature=feature, + annotations=None, + comment=None, + ) # If not in canonical mode, output original FASM line if not canonical: @@ -50,15 +52,18 @@ def process_fasm(db_root, fasm_file, canonical): def run(db_root, fasm_file, canonical): - print(fasm.fasm_tuple_to_string( - process_fasm(db_root, fasm_file, canonical), - canonical=canonical)) + print( + fasm.fasm_tuple_to_string( + process_fasm(db_root, fasm_file, canonical), canonical=canonical)) + def main(): import argparse parser = argparse.ArgumentParser( - description='Read FASM file, check against database, and output optionally canonical FASM.') + description= + 'Read FASM file, check against database, and output optionally canonical FASM.' + ) database_dir = os.getenv("XRAY_DATABASE_DIR") database = os.getenv("XRAY_DATABASE") @@ -69,15 +74,14 @@ def main(): db_root_kwargs['required'] = False db_root_kwargs['default'] = os.path.join(database_dir, database) - parser.add_argument( - '--db_root', help="Database root.", **db_root_kwargs) + parser.add_argument('--db_root', help="Database root.", **db_root_kwargs) parser.add_argument('fasm_file', help='Input FASM file') - parser.add_argument('--canonical', help='Output canonical bitstream.', - action='store_true') + parser.add_argument( + '--canonical', help='Output canonical bitstream.', action='store_true') args = parser.parse_args() run(args.db_root, args.fasm_file, args.canonical) + if __name__ == '__main__': main() - diff --git a/utils/fasm2frames.py b/utils/fasm2frames.py index 84ceb7ca..dcd1fb23 100755 --- a/utils/fasm2frames.py +++ b/utils/fasm2frames.py @@ -7,6 +7,7 @@ import argparse import os import os.path + def dump_frames_verbose(frames): print() print("Frames: %d" % len(frames)) @@ -16,6 +17,7 @@ def dump_frames_verbose(frames): '0x%08X ' % addr + ', '.join(['0x%08X' % w for w in words]) + '...') + def dump_frames_sparse(frames): print() print("Frames: %d" % len(frames)) @@ -34,6 +36,7 @@ def dump_frames_sparse(frames): if w: print(' % 3d: 0x%08X' % (i, w)) + def dump_frm(f, frames): '''Write a .frm file given a list of frames, each containing a list of 101 32 bit words''' for addr in sorted(frames.keys()): @@ -41,6 +44,7 @@ def dump_frm(f, frames): f.write( '0x%08X ' % addr + ','.join(['0x%08X' % w for w in words]) + '\n') + def run(db_root, filename_in, f_out, sparse=False, debug=False): assembler = fasm_assembler.FasmAssembler(db.Database(db_root)) assembler.parse_fasm_filename(filename_in) @@ -67,15 +71,12 @@ def main(): db_root_kwargs['required'] = False db_root_kwargs['default'] = os.path.join(database_dir, database) - parser.add_argument( - '--db_root', help="Database root.", **db_root_kwargs) + parser.add_argument('--db_root', help="Database root.", **db_root_kwargs) parser.add_argument( '--sparse', action='store_true', help="Don't zero fill all frames") parser.add_argument( '--debug', action='store_true', help="Print debug dump") - parser.add_argument( - 'fn_in', - help='Input FPGA assembly (.fasm) file') + parser.add_argument('fn_in', help='Input FPGA assembly (.fasm) file') parser.add_argument( 'fn_out', default='/dev/stdout', @@ -88,8 +89,8 @@ def main(): filename_in=args.fn_in, f_out=open(args.fn_out, 'w'), sparse=args.sparse, - debug=args.debug - ) + debug=args.debug) + if __name__ == '__main__': main() diff --git a/utils/fasm2pips.py b/utils/fasm2pips.py index 8b2da11c..9044c640 100644 --- a/utils/fasm2pips.py +++ b/utils/fasm2pips.py @@ -5,13 +5,11 @@ import os.path import fasm from prjxray import db + def main(): import argparse - parser = argparse.ArgumentParser( - description= - 'Convert FASM to pip list' - ) + parser = argparse.ArgumentParser(description='Convert FASM to pip list') database_dir = os.getenv("XRAY_DATABASE_DIR") database = os.getenv("XRAY_DATABASE") @@ -22,11 +20,8 @@ def main(): db_root_kwargs['required'] = False db_root_kwargs['default'] = os.path.join(database_dir, database) - parser.add_argument( - '--db_root', help="Database root.", **db_root_kwargs) - parser.add_argument( - 'fn_in', - help='Input FPGA assembly (.fasm) file') + parser.add_argument('--db_root', help="Database root.", **db_root_kwargs) + parser.add_argument('fn_in', help='Input FPGA assembly (.fasm) file') args = parser.parse_args() database = db.Database(args.db_root) @@ -47,7 +42,10 @@ def main(): if pip.net_from == parts[2] and pip.net_to == parts[1]: yield '{}/{}.{}'.format(tile, gridinfo.tile_type, pip.name) - print('highlight_objects [concat {}]'.format(' '.join('[get_pips {}]'.format(pip) for pip in inner()))) + print( + 'highlight_objects [concat {}]'.format( + ' '.join('[get_pips {}]'.format(pip) for pip in inner()))) + if __name__ == '__main__': main() From 7eadae4deddb6d04f92befdaad276aa31c6c306d Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Mon, 22 Oct 2018 09:08:34 -0700 Subject: [PATCH 06/15] Assorted fixes. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- minitests/roi_harness/runme.sh | 4 ++-- prjxray/fasm_disassembler.py | 4 ++-- utils/bits2fasm.py | 2 +- utils/segprint.py | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/minitests/roi_harness/runme.sh b/minitests/roi_harness/runme.sh index 49fb7555..c5da2a4e 100755 --- a/minitests/roi_harness/runme.sh +++ b/minitests/roi_harness/runme.sh @@ -57,8 +57,8 @@ test -z "$(fgrep CRITICAL vivado.log)" ${XRAY_BITREAD} -F $XRAY_ROI_FRAMES -o design.bits -z -y design.bit ${XRAY_SEGPRINT} -zd design.bits >design.segp -${XRAY_DIR}/tools/segprint2fasm.py design.segp design.fasm -${XRAY_DIR}/tools/fasm2frame.py design.fasm design.frm +${XRAY_DIR}/utils/bits2fasm.py --verbose design.bits > design.fasm +${XRAY_DIR}/utils/fasm2frames.py design.fasm design.frm # Hack to get around weird clock error related to clk net not found # Remove following lines: #set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF] diff --git a/prjxray/fasm_disassembler.py b/prjxray/fasm_disassembler.py index 7b03c438..7784594b 100644 --- a/prjxray/fasm_disassembler.py +++ b/prjxray/fasm_disassembler.py @@ -108,7 +108,7 @@ class FasmDisassembler(object): verbose=verbose): yield fasm_line - if len(bitdata[frame]) > 0 and verbose: + if len(bitdata[frame][1]) > 0 and verbose: # Some bits were not decoded, add warning and annotations to # FASM. yield fasm.FasmLine( @@ -117,7 +117,7 @@ class FasmDisassembler(object): comment=" In frame 0x{:08x} {} bits were not converted.". format( frame, - len(bitdata[frame]), + len(bitdata[frame][1]), )) for bit in bitdata[frame][1]: diff --git a/utils/bits2fasm.py b/utils/bits2fasm.py index 9116b6bd..92795306 100755 --- a/utils/bits2fasm.py +++ b/utils/bits2fasm.py @@ -40,7 +40,7 @@ def main(): parser.add_argument('--db_root', help="Database root.", **db_root_kwargs) parser.add_argument('bits_file', help='') parser.add_argument( - 'verbose', + '--verbose', help='Print lines for unknown tiles and bits', action='store_true') parser.add_argument( diff --git a/utils/segprint.py b/utils/segprint.py index db515a73..564dcd93 100755 --- a/utils/segprint.py +++ b/utils/segprint.py @@ -236,6 +236,9 @@ def mksegment(tile_name, block_name): def tile_segnames(grid): ret = [] for tile_name, tile in grid['tiles'].items(): + if 'bits' not in tile: + continue + for block_name in tile['bits'].keys(): ret.append(mksegment(tile_name, block_name)) return ret From 204c6246adcc3e5add8072bb388500d812f11512 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Mon, 22 Oct 2018 11:20:03 -0700 Subject: [PATCH 07/15] Fix comments. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- minitests/roi_harness/bit2fasm.sh | 2 +- minitests/roi_harness/fasm2bit.sh | 2 +- prjxray/bitstream.py | 2 ++ prjxray/fasm_assembler.py | 6 +----- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/minitests/roi_harness/bit2fasm.sh b/minitests/roi_harness/bit2fasm.sh index 13a6c4c6..235d6888 100755 --- a/minitests/roi_harness/bit2fasm.sh +++ b/minitests/roi_harness/bit2fasm.sh @@ -4,4 +4,4 @@ BIT_IN=$1 BITS=$(tempfile --suffix .bits) ${XRAY_BITREAD} -F $XRAY_ROI_FRAMES -o ${BITS} -z -y ${BIT_IN} -${XRAY_DIR}/utils/bits2fasm.py ${BITS} +${XRAY_BITS2FASM} ${BITS} diff --git a/minitests/roi_harness/fasm2bit.sh b/minitests/roi_harness/fasm2bit.sh index 88bff430..ee1d389a 100755 --- a/minitests/roi_harness/fasm2bit.sh +++ b/minitests/roi_harness/fasm2bit.sh @@ -27,7 +27,7 @@ echo "Design .fasm: $fasm_in" echo "Harness .bit: $bit_in" echo "Out .bit: $bit_out" -${XRAY_DIR}/utils/fasm2frames.py --sparse $fasm_in roi_partial.frm +${XRAY_FASM2FRAMES} --sparse $fasm_in roi_partial.frm ${XRAY_TOOLS_DIR}/xc7patch \ --part_name ${XRAY_PART} \ diff --git a/prjxray/bitstream.py b/prjxray/bitstream.py index ede19df2..ce73157d 100644 --- a/prjxray/bitstream.py +++ b/prjxray/bitstream.py @@ -1,6 +1,8 @@ # Break frames into WORD_SIZE bit words. WORD_SIZE_BITS = 32 +# How many 32-bit words for frame in a 7-series bitstream? +FRAME_WORD_COUNT = 101 def load_bitdata(f): """ Read bit file and return bitdata map. diff --git a/prjxray/fasm_assembler.py b/prjxray/fasm_assembler.py index 75290a87..7a69f882 100644 --- a/prjxray/fasm_assembler.py +++ b/prjxray/fasm_assembler.py @@ -11,14 +11,10 @@ class FasmInconsistentBits(Exception): pass -# How many 32-bit words for frame in a 7-series bitstream? -FRAME_WORD_COUNT = 101 - - def init_frame_at_address(frames, addr): '''Set given frame to 0 if not initialized ''' if not addr in frames: - frames[addr] = [0 for _i in range(FRAME_WORD_COUNT)] + frames[addr] = [0 for _i in range(bitstream.FRAME_WORD_COUNT)] class FasmAssembler(object): From db5c8d00634bea4f9d25fb1420609f66ef2702e2 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Mon, 22 Oct 2018 11:51:07 -0700 Subject: [PATCH 08/15] Rename SegmentType to BlockType. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- prjxray/fasm_assembler.py | 2 +- prjxray/grid.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/prjxray/fasm_assembler.py b/prjxray/fasm_assembler.py index 7a69f882..c625ac6c 100644 --- a/prjxray/fasm_assembler.py +++ b/prjxray/fasm_assembler.py @@ -92,7 +92,7 @@ class FasmAssembler(object): gridinfo = self.grid.gridinfo_at_tilename(tile) # TODO: How to determine if the feature targets BLOCK_RAM segment type? - bits = gridinfo.bits[grid.SegmentType.CLB_IO_CLK] + bits = gridinfo.bits[grid.BlockType.CLB_IO_CLK] seg_baseaddr = bits.base_address seg_word_base = bits.offset diff --git a/prjxray/grid.py b/prjxray/grid.py index a31318ee..15b7c94d 100644 --- a/prjxray/grid.py +++ b/prjxray/grid.py @@ -3,11 +3,11 @@ import enum from prjxray import segment_map -class SegmentType(enum.Enum): - # Segments describing CLB features, interconnect, clocks and IOs. +class BlockType(enum.Enum): + # Frames describing CLB features, interconnect, clocks and IOs. CLB_IO_CLK = 'CLB_IO_CLK' - # Segments describing block RAM initialization. + # Frames describing block RAM initialization. BLOCK_RAM = 'BLOCK_RAM' @@ -58,7 +58,7 @@ class Grid(object): if 'bits' in tileinfo: for k in tileinfo['bits']: - segment_type = SegmentType(k) + segment_type = BlockType(k) base_address = int(tileinfo['bits'][k]['baseaddr'], 0) bits[segment_type] = Bits( base_address=base_address, From 99704740a3fc329d67dc70b692787074e9ee6d4f Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Mon, 22 Oct 2018 12:04:55 -0700 Subject: [PATCH 09/15] Make Segmaker db_root be implicit. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- experiments/clbpips/generate.py | 3 +-- experiments/gndvcc/generate.py | 3 +-- experiments/pipsroute/generate.py | 3 +-- fuzzers/010-lutinit/generate.py | 3 +-- fuzzers/011-ffconfig/generate.py | 3 +-- fuzzers/012-clbn5ffmux/generate.py | 3 +-- fuzzers/013-clbncy0/generate.py | 3 +-- fuzzers/014-ffsrcemux/generate.py | 3 +-- fuzzers/015-clbnffmux/generate.py | 2 +- fuzzers/016-clbnoutmux/generate.py | 2 +- fuzzers/017-clbprecyinit/generate.py | 3 +-- fuzzers/018-clbram/generate.py | 3 +-- fuzzers/019-ndi1mux/generate.py | 3 +-- fuzzers/050-intpips/generate.py | 3 +-- fuzzers/051-imuxlout/generate.py | 3 +-- fuzzers/052-clkin/generate.py | 3 +-- fuzzers/053-ctrlin/generate.py | 3 +-- fuzzers/054-gfan/generate.py | 3 +-- fuzzers/055-gnd/generate.py | 3 +-- fuzzers/056-rempips/generate.py | 3 +-- fuzzers/057-bipips/generate.py | 3 +-- fuzzers/058-hclkpips/generate.py | 3 +-- fuzzers/100-dsp-mskpat/generate.py | 3 +-- prjxray/segmaker.py | 6 +++++- 24 files changed, 28 insertions(+), 45 deletions(-) diff --git a/experiments/clbpips/generate.py b/experiments/clbpips/generate.py index 58824df8..b2fe2a2c 100644 --- a/experiments/clbpips/generate.py +++ b/experiments/clbpips/generate.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") print("Loading tags from design.txt.") with open("design.txt", "r") as f: diff --git a/experiments/gndvcc/generate.py b/experiments/gndvcc/generate.py index f0200544..366db065 100644 --- a/experiments/gndvcc/generate.py +++ b/experiments/gndvcc/generate.py @@ -3,9 +3,8 @@ import re from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") tiledata = dict() pipdata = dict() diff --git a/experiments/pipsroute/generate.py b/experiments/pipsroute/generate.py index c58c8298..aa131cae 100644 --- a/experiments/pipsroute/generate.py +++ b/experiments/pipsroute/generate.py @@ -3,14 +3,13 @@ import sys, re from prjxray.segmaker import Segmaker -from prjxray import util pipdata = dict() ignpip = set() def handle_design(prefix, second_pass): - segmk = Segmaker(util.get_db_root(), prefix + ".bits") + segmk = Segmaker(prefix + ".bits") tiledata = dict() nlines = 0 diff --git a/fuzzers/010-lutinit/generate.py b/fuzzers/010-lutinit/generate.py index 4e53630c..ea6ceae3 100644 --- a/fuzzers/010-lutinit/generate.py +++ b/fuzzers/010-lutinit/generate.py @@ -3,9 +3,8 @@ import sys from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design_%s.bits" % sys.argv[1]) +segmk = Segmaker("design_%s.bits" % sys.argv[1]) print("Loading tags from design_%s.txt." % sys.argv[1]) with open("design_%s.txt" % sys.argv[1], "r") as f: diff --git a/fuzzers/011-ffconfig/generate.py b/fuzzers/011-ffconfig/generate.py index 169a6c1b..d27a3497 100644 --- a/fuzzers/011-ffconfig/generate.py +++ b/fuzzers/011-ffconfig/generate.py @@ -11,9 +11,8 @@ LDPE Primitive: Transparent Data Latch with Asynchronous Preset and Gate Enable from prims import * from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") def ones(l): diff --git a/fuzzers/012-clbn5ffmux/generate.py b/fuzzers/012-clbn5ffmux/generate.py index 521e4d03..aae15bcf 100644 --- a/fuzzers/012-clbn5ffmux/generate.py +++ b/fuzzers/012-clbn5ffmux/generate.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") print("Loading tags") ''' diff --git a/fuzzers/013-clbncy0/generate.py b/fuzzers/013-clbncy0/generate.py index 333c4aa0..d1124ba5 100644 --- a/fuzzers/013-clbncy0/generate.py +++ b/fuzzers/013-clbncy0/generate.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") print("Loading tags") ''' diff --git a/fuzzers/014-ffsrcemux/generate.py b/fuzzers/014-ffsrcemux/generate.py index d3618ff8..a936f8a8 100644 --- a/fuzzers/014-ffsrcemux/generate.py +++ b/fuzzers/014-ffsrcemux/generate.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") print("Loading tags") ''' diff --git a/fuzzers/015-clbnffmux/generate.py b/fuzzers/015-clbnffmux/generate.py index f2d60d1d..3fa520ad 100644 --- a/fuzzers/015-clbnffmux/generate.py +++ b/fuzzers/015-clbnffmux/generate.py @@ -3,7 +3,7 @@ from prjxray.segmaker import Segmaker from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") cache = dict() print("Loading tags") diff --git a/fuzzers/016-clbnoutmux/generate.py b/fuzzers/016-clbnoutmux/generate.py index d463f64b..ada2425b 100644 --- a/fuzzers/016-clbnoutmux/generate.py +++ b/fuzzers/016-clbnoutmux/generate.py @@ -3,7 +3,7 @@ from prjxray.segmaker import Segmaker from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") cache = dict() print("Loading tags") diff --git a/fuzzers/017-clbprecyinit/generate.py b/fuzzers/017-clbprecyinit/generate.py index 6035d675..6fa1265b 100644 --- a/fuzzers/017-clbprecyinit/generate.py +++ b/fuzzers/017-clbprecyinit/generate.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") print("Loading tags") f = open('params.csv', 'r') diff --git a/fuzzers/018-clbram/generate.py b/fuzzers/018-clbram/generate.py index 74e4154a..2a62bdde 100644 --- a/fuzzers/018-clbram/generate.py +++ b/fuzzers/018-clbram/generate.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") # Can fit 4 per CLB # BELable diff --git a/fuzzers/019-ndi1mux/generate.py b/fuzzers/019-ndi1mux/generate.py index 03317dd4..71a8fe61 100644 --- a/fuzzers/019-ndi1mux/generate.py +++ b/fuzzers/019-ndi1mux/generate.py @@ -6,9 +6,8 @@ WA7USED = 0 from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") print("Loading tags") ''' diff --git a/fuzzers/050-intpips/generate.py b/fuzzers/050-intpips/generate.py index 13573664..ad9dc1a3 100644 --- a/fuzzers/050-intpips/generate.py +++ b/fuzzers/050-intpips/generate.py @@ -3,9 +3,8 @@ import re from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/051-imuxlout/generate.py b/fuzzers/051-imuxlout/generate.py index 66c23414..a8638ee2 100644 --- a/fuzzers/051-imuxlout/generate.py +++ b/fuzzers/051-imuxlout/generate.py @@ -3,9 +3,8 @@ import re, os from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/052-clkin/generate.py b/fuzzers/052-clkin/generate.py index fa5f9463..4d39578c 100644 --- a/fuzzers/052-clkin/generate.py +++ b/fuzzers/052-clkin/generate.py @@ -3,9 +3,8 @@ import os, re from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/053-ctrlin/generate.py b/fuzzers/053-ctrlin/generate.py index 70d82ad2..d58c6f86 100644 --- a/fuzzers/053-ctrlin/generate.py +++ b/fuzzers/053-ctrlin/generate.py @@ -3,9 +3,8 @@ import os, re from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/054-gfan/generate.py b/fuzzers/054-gfan/generate.py index a05df06b..6a882d91 100644 --- a/fuzzers/054-gfan/generate.py +++ b/fuzzers/054-gfan/generate.py @@ -3,9 +3,8 @@ import re from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/055-gnd/generate.py b/fuzzers/055-gnd/generate.py index 04f38579..5bc37073 100644 --- a/fuzzers/055-gnd/generate.py +++ b/fuzzers/055-gnd/generate.py @@ -3,9 +3,8 @@ import re from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/056-rempips/generate.py b/fuzzers/056-rempips/generate.py index 56e761f8..ad7f4584 100644 --- a/fuzzers/056-rempips/generate.py +++ b/fuzzers/056-rempips/generate.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/057-bipips/generate.py b/fuzzers/057-bipips/generate.py index 903f2162..d22412fe 100644 --- a/fuzzers/057-bipips/generate.py +++ b/fuzzers/057-bipips/generate.py @@ -3,9 +3,8 @@ import os from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design.bits") +segmk = Segmaker("design.bits") tiledata = dict() pipdata = set() diff --git a/fuzzers/058-hclkpips/generate.py b/fuzzers/058-hclkpips/generate.py index 72a4dab7..f32fb121 100644 --- a/fuzzers/058-hclkpips/generate.py +++ b/fuzzers/058-hclkpips/generate.py @@ -3,7 +3,6 @@ import sys from prjxray.segmaker import Segmaker -from prjxray import util tags = dict() en_tags = dict() @@ -24,7 +23,7 @@ for arg in sys.argv[1:]: for arg in sys.argv[1:]: print("Processing %s." % arg) - segmk = Segmaker(util.get_db_root(), arg + ".bits") + segmk = Segmaker(arg + ".bits") tiledata = dict() pipdata = dict() diff --git a/fuzzers/100-dsp-mskpat/generate.py b/fuzzers/100-dsp-mskpat/generate.py index 870daf03..c1b2accc 100644 --- a/fuzzers/100-dsp-mskpat/generate.py +++ b/fuzzers/100-dsp-mskpat/generate.py @@ -3,9 +3,8 @@ import sys from prjxray.segmaker import Segmaker -from prjxray import util -segmk = Segmaker(util.get_db_root(), "design_%s.bits" % sys.argv[1]) +segmk = Segmaker("design_%s.bits" % sys.argv[1]) pipdata = dict() ignpip = set() diff --git a/prjxray/segmaker.py b/prjxray/segmaker.py index 887e2951..1be40cb4 100644 --- a/prjxray/segmaker.py +++ b/prjxray/segmaker.py @@ -14,6 +14,7 @@ tilegrid.json provides tile addresses ''' import os, json, re +from prjxray import util BLOCK_TYPES = set(('CLB_IO_CLK', 'BLOCK_RAM', 'CFG_CLB')) @@ -38,8 +39,11 @@ def json_hex2i(s): class Segmaker: - def __init__(self, db_root, bitsfile, verbose=None): + def __init__(self, bitsfile, verbose=None, db_root=None): self.db_root = db_root + if self.db_root is None: + self.db_root = util.get_db_root() + self.verbose = verbose if verbose is not None else os.getenv( 'VERBOSE', 'N') == 'Y' self.load_grid() From 1d7738271a25a2bc504dd524b9208e63a842fc22 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Mon, 22 Oct 2018 12:31:15 -0700 Subject: [PATCH 10/15] Address comments. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- prjxray/roi.py | 77 ++++++++++++++++++++++++++-------------------- prjxray/util.py | 4 ++- utils/fasm2pips.py | 6 +++- 3 files changed, 52 insertions(+), 35 deletions(-) diff --git a/prjxray/roi.py b/prjxray/roi.py index e195c60a..c99c9816 100644 --- a/prjxray/roi.py +++ b/prjxray/roi.py @@ -1,48 +1,59 @@ -import json - - class Roi(object): - def __init__(self, tilegrid_file, x1, x2, y1, y2): - self.tilegrid_file = tilegrid_file - self.tilegrid = None + """ Object that represents a Project X-ray ROI. + + Can be used to iterate over tiles and sites within an ROI. + + """ + def __init__(self, db, x1, x2, y1, y2): + self.grid = db.grid() self.x1 = x1 self.x2 = x2 self.y1 = y1 self.y2 = y2 - def tile_in_roi(self, tilej): - x = int(tilej['grid_x']) - y = int(tilej['grid_y']) + def tile_in_roi(self, grid_loc): + """ Returns true if grid_loc (GridLoc tuple) is within the ROI. """ + x = grid_loc.grid_x + y = grid_loc.grid_y return self.x1 <= x and x <= self.x2 and self.y1 <= y and y <= self.y2 - def read_tilegrid(self): - if not self.tilegrid: - with open(self.tilegrid_file) as f: - self.tilegrid = json.load(f) - def gen_tiles(self, tile_types=None): - ''' + ''' Yield tile names within ROI. + tile_types: list of tile types to keep, or None for all ''' - self.read_tilegrid() + for tile_name in self.grid.tiles(): + loc = self.grid.loc_of_tilename(tile_name) - for tile_name, tilej in self.tilegrid.items(): - if self.tile_in_roi(tilej) and (tile_types is None - or tilej['type'] in tile_types): - yield (tile_name, tilej) - - def gen_sites(self, site_types=None): - ''' - site_types: list of site types to keep, or None for all - ''' - - self.read_tilegrid() - - for tile_name, tilej in self.tilegrid.items(): - if not self.tile_in_roi(tilej): + if not self.tile_in_roi(loc): continue - for site_name, site_type in tilej['sites'].items(): - if site_types is None or site_type in site_types: - yield (tile_name, site_name, site_type) + gridinfo = self.grid.gridinfo_at_loc(loc) + + if tile_types is not None and gridinfo.tile_type not in tile_types: + continue + + yield tile_name + + def gen_sites(self, site_types=None): + ''' Yield (tile_name, site_name, site_type) within ROI. + + site_types: list of site types to keep, or None for all + + ''' + + for tile_name in self.grid.tiles(): + loc = self.grid.loc_of_tilename(tile_name) + + if not self.tile_in_roi(loc): + continue + + gridinfo = self.grid.gridinfo_at_loc(loc) + + + for site_name, site_type in gridinfo.sites.items(): + if site_types is not None and site_type not in site_types: + continue + + yield (tile_name, site_name, site_type) diff --git a/prjxray/util.py b/prjxray/util.py index ae600121..9e7518ef 100644 --- a/prjxray/util.py +++ b/prjxray/util.py @@ -1,6 +1,7 @@ import os import re from .roi import Roi +from .db import Database def get_db_root(): @@ -30,8 +31,9 @@ def slice_xy(): def get_roi(): (x1, x2), (y1, y2) = roi_xy() + db = Database(get_db_root()) return Roi( - tilegrid_file=os.path.join(get_db_root(), 'tilegrid.json'), + db=db, x1=x1, x2=x2, y1=y1, diff --git a/utils/fasm2pips.py b/utils/fasm2pips.py index 9044c640..4c4ddc0d 100644 --- a/utils/fasm2pips.py +++ b/utils/fasm2pips.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +""" Tool for generating Vivavo commands to highlight objects from a FASM file. + +Currently this tool only highlights pips directly referenced in the FASM file. +""" from __future__ import print_function import os.path @@ -9,7 +13,7 @@ from prjxray import db def main(): import argparse - parser = argparse.ArgumentParser(description='Convert FASM to pip list') + parser = argparse.ArgumentParser(description='Outputs a Vivavo highlight_objects command from a FASM file.') database_dir = os.getenv("XRAY_DATABASE_DIR") database = os.getenv("XRAY_DATABASE") From 42addd98a22deb35b98448c078e55598d91832cc Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Mon, 22 Oct 2018 12:32:42 -0700 Subject: [PATCH 11/15] Run make format. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- prjxray/bitstream.py | 1 + prjxray/roi.py | 2 +- prjxray/util.py | 7 +------ utils/fasm2pips.py | 4 +++- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/prjxray/bitstream.py b/prjxray/bitstream.py index ce73157d..450046ed 100644 --- a/prjxray/bitstream.py +++ b/prjxray/bitstream.py @@ -4,6 +4,7 @@ WORD_SIZE_BITS = 32 # How many 32-bit words for frame in a 7-series bitstream? FRAME_WORD_COUNT = 101 + def load_bitdata(f): """ Read bit file and return bitdata map. diff --git a/prjxray/roi.py b/prjxray/roi.py index c99c9816..c8d02318 100644 --- a/prjxray/roi.py +++ b/prjxray/roi.py @@ -4,6 +4,7 @@ class Roi(object): Can be used to iterate over tiles and sites within an ROI. """ + def __init__(self, db, x1, x2, y1, y2): self.grid = db.grid() self.x1 = x1 @@ -51,7 +52,6 @@ class Roi(object): gridinfo = self.grid.gridinfo_at_loc(loc) - for site_name, site_type in gridinfo.sites.items(): if site_types is not None and site_type not in site_types: continue diff --git a/prjxray/util.py b/prjxray/util.py index 9e7518ef..70ed3427 100644 --- a/prjxray/util.py +++ b/prjxray/util.py @@ -32,12 +32,7 @@ def slice_xy(): def get_roi(): (x1, x2), (y1, y2) = roi_xy() db = Database(get_db_root()) - return Roi( - db=db, - x1=x1, - x2=x2, - y1=y1, - y2=y2) + return Roi(db=db, x1=x1, x2=x2, y1=y1, y2=y2) # we know that all bits for CLB MUXes are in frames 30 and 31, so filter all other bits diff --git a/utils/fasm2pips.py b/utils/fasm2pips.py index 4c4ddc0d..9c92887b 100644 --- a/utils/fasm2pips.py +++ b/utils/fasm2pips.py @@ -13,7 +13,9 @@ from prjxray import db def main(): import argparse - parser = argparse.ArgumentParser(description='Outputs a Vivavo highlight_objects command from a FASM file.') + parser = argparse.ArgumentParser( + description= + 'Outputs a Vivavo highlight_objects command from a FASM file.') database_dir = os.getenv("XRAY_DATABASE_DIR") database = os.getenv("XRAY_DATABASE") From d9fc6875248d194feb80881b246a3a616b56bfbf Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Mon, 22 Oct 2018 13:09:38 -0700 Subject: [PATCH 12/15] Address review comments. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- utils/{fasm2fasm.py => fasm_pprint.py} | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) rename utils/{fasm2fasm.py => fasm_pprint.py} (93%) diff --git a/utils/fasm2fasm.py b/utils/fasm_pprint.py similarity index 93% rename from utils/fasm2fasm.py rename to utils/fasm_pprint.py index b927ffcd..6645a1a3 100644 --- a/utils/fasm2fasm.py +++ b/utils/fasm_pprint.py @@ -1,6 +1,11 @@ #!/usr/bin/env python3 ''' -Takes input FASM and outputs optionally canonical FASM. +Pretty print FASM. + +Sanity checks FASM against prjxray database. +Can output canonical FASM. +In the future may support other formatting options. + ''' import os @@ -62,7 +67,7 @@ def main(): parser = argparse.ArgumentParser( description= - 'Read FASM file, check against database, and output optionally canonical FASM.' + 'Pretty print a FASM file.' ) database_dir = os.getenv("XRAY_DATABASE_DIR") From c7ff5238f0485f3b7d0347b14ace8a453e412507 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Mon, 22 Oct 2018 13:19:22 -0700 Subject: [PATCH 13/15] Run make format. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- utils/fasm_pprint.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/utils/fasm_pprint.py b/utils/fasm_pprint.py index 6645a1a3..31a06b0e 100644 --- a/utils/fasm_pprint.py +++ b/utils/fasm_pprint.py @@ -65,10 +65,7 @@ def run(db_root, fasm_file, canonical): def main(): import argparse - parser = argparse.ArgumentParser( - description= - 'Pretty print a FASM file.' - ) + parser = argparse.ArgumentParser(description='Pretty print a FASM file.') database_dir = os.getenv("XRAY_DATABASE_DIR") database = os.getenv("XRAY_DATABASE") From 522a684a7b5e2d9296c3527d3273365c26276c47 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Mon, 22 Oct 2018 14:00:00 -0700 Subject: [PATCH 14/15] Add missing intervaltree requirement. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 213940cb..10f644b2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ futures +intervaltree numpy progressbar2 pyjson5 From d4f9d10c8c114a0f8bd13152f2ca1df80f01dc2d Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Mon, 22 Oct 2018 14:08:18 -0700 Subject: [PATCH 15/15] Fix missing self. on db_root. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- prjxray/segmaker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prjxray/segmaker.py b/prjxray/segmaker.py index 1be40cb4..9ce7e832 100644 --- a/prjxray/segmaker.py +++ b/prjxray/segmaker.py @@ -62,7 +62,7 @@ class Segmaker: def load_grid(self): '''Load self.grid holding tile addresses''' - with open(os.path.join(db_root, "tilegrid.json"), "r") as f: + with open(os.path.join(self.db_root, "tilegrid.json"), "r") as f: self.grid = json.load(f) assert "segments" not in self.grid, "Old format tilegrid.json"