[util] Update get-lfsr-coeffs script
diff --git a/util/get-lfsr-coeffs.py b/util/get-lfsr-coeffs.py
index 2bfe9e0..70fa01a 100755
--- a/util/get-lfsr-coeffs.py
+++ b/util/get-lfsr-coeffs.py
@@ -7,20 +7,79 @@
 import glob
 import os
 import shutil
+import subprocess
 import sys
 
 import wget
 
-USAGE = """./get_lfsr_coeffs.py [-t <temporary folder>] [-o <outfile>] [-f]
+USAGE = """./get_lfsr_coeffs.py [-t <temporary folder>] [-o <outfile>] [-f] [--fib]
 
-Downloads lfsr constants from https://users.ece.cmu.edu/~koopman/lfsr/
-and dumps them in SystemVerilog format (for use in prim_lfsr.sv).
+Downloads LFSR constants from [1] and dumps them in SystemVerilog format
+(for use in prim_lfsr.sv). These coeffs are for a Galois XOR type LFSR, and cover
+implementations ranging from 4 to 64bits.
+
+Alternatively, the script can also extract the XNOR Fibonacci type LFSR coefficients
+from the XILINX application note 52 [2] by specifying the --fib switch. Note that this
+depends on the pdftotext utility for Linux.
+
+[1] https://users.ece.cmu.edu/~koopman/lfsr/
+
+[2] https://www.xilinx.com/support/documentation/application_notes/xapp052.pdf
 """
 
+# configuration for Galois
 MIN_LFSR_LEN = 4
 MAX_LFSR_LEN = 64
 BASE_URL = 'https://users.ece.cmu.edu/~koopman/lfsr/'
 
+# configuration for Fibonacci
+FIB_URL = 'https://www.xilinx.com/support/documentation/application_notes/xapp052.pdf'
+PDF_NAME = 'xapp052'
+LINE_FILTER = [
+    'Table 3: Taps for Maximum-Length LFSR Counters',
+    'XAPP 052 July 7,1996 (Version 1.1)'
+]
+
+
+# helper function to write out coeffs
+def dump_coeffs(lfsrType, widths, coeffs, outfile):
+    # widths consistency check
+    for k in range(widths[0], widths[-1] + 1):
+        # print("%d -- %d" % (k,widths[k-widths[0]]))
+        if k != widths[k - widths[0]]:
+            print("Error: widths is not consistently increasing")
+            sys.exit(1)
+
+    # select first coefficient in each file and print to SV LUT
+    with outfile:
+        decl_str = "localparam int unsigned %s_LUT_OFF = %d;\n" \
+            % (lfsrType, min(widths))
+        outfile.write(decl_str)
+        decl_str = "localparam logic [%d:0] %s_COEFFS [0:%d] = '{ " \
+            % (max(widths) - 1, lfsrType, max(widths)-min(widths))
+        outfile.write(decl_str)
+        comma = ',\n'
+        spaces = ''
+        for k in widths:
+            if k == max(widths):
+                comma = ""
+            if k == min(widths) + 1:
+                for l in range(len(decl_str)):
+                    spaces += ' '
+            outfile.write("%s%d'h%s%s" % \
+                (spaces, max(widths), coeffs[k-widths[0]], comma))
+        outfile.write(' };\n')
+
+
+# converts list with bit positions to a hex bit mask string
+def to_bit_mask(bitPositions):
+
+    bitMask = 0
+    for b in bitPositions:
+        bitMask += 2**(b - 1)
+
+    return "%X" % bitMask
+
 
 def main():
     parser = argparse.ArgumentParser(
@@ -35,6 +94,9 @@
         help="""temporary folder to download the lfsr constant files
 to (defaults to lfsr_tmp)""",
         default='lfsr_tmp')
+    parser.add_argument('--fib',
+                        help='download fibonacci coefficients',
+                        action='store_true')
     parser.add_argument('-f',
                         '--force',
                         help='overwrites tempfolder',
@@ -47,7 +109,6 @@
                         help='Output file (default stdout)')
 
     args = parser.parse_args()
-    outfile = args.output
 
     if args.force and os.path.exists(args.tempfolder):
         shutil.rmtree(args.tempfolder)
@@ -56,36 +117,68 @@
         # download coefficient files
         os.makedirs(args.tempfolder, exist_ok=args.force)
         os.chdir(args.tempfolder)
-        for k in range(MIN_LFSR_LEN, MAX_LFSR_LEN + 1):
-            url = '%s%d.txt' % (BASE_URL, k)
-            print("\nDownloading %d bit LFSR coeffs from %s..." % (k, url))
-            wget.download(url)
-        print("")
 
-        # select first coefficient in each file and print to SV LUT
-        with outfile:
-            decl_str = "localparam int unsigned LUT_OFF = %d;\n" % MIN_LFSR_LEN
-            outfile.write(decl_str)
-            decl_str = "localparam logic [%d:0] COEFFS [0:%d] = '{ " % (
-                MAX_LFSR_LEN - 1, MAX_LFSR_LEN-MIN_LFSR_LEN)
-            outfile.write(decl_str)
-            comma = ',\n'
-            spaces = ''
+        if args.fib:
+            lfsrType = 'FIB_XNOR'
+
+            wget.download(FIB_URL)
+            cmd = ['pdftotext %s.pdf' % PDF_NAME, '> %s.txt' % PDF_NAME]
+            subprocess.call(cmd, shell=True)
+            print("")
+            cmd = ['grep -A 350 "%s" %s.txt > table.txt' \
+                % (LINE_FILTER[0], PDF_NAME)]
+            subprocess.call(cmd, shell=True)
+
+            # parse the table
+            widths = []
+            coeffs = []
+            columnType = 0
+            with open('table.txt') as infile:
+                for line in infile:
+                    line = line.strip()
+                    if line and line not in LINE_FILTER:
+                        if line == 'n':
+                            columnType = 0
+                        # yes, this is a typo in the PDF :)
+                        elif line == 'XNOR from':
+                            columnType = 1
+                        elif columnType:
+                            tmpCoeffs = [int(c) for c in line.split(',')]
+                            coeffs += [tmpCoeffs]
+                        else:
+                            widths += [int(line)]
+
+            # # printout for checking
+            # for (w,c) in zip(widths,coeffs):
+            #     print("width: %d > coeffs: %s" % (w, str(c)))
+
+            # convert to bitmask
+            for k in range(len(coeffs)):
+                coeffs[k] = to_bit_mask(coeffs[k])
+
+        else:
+            lfsrType = 'GAL_XOR'
+
+            for k in range(MIN_LFSR_LEN, MAX_LFSR_LEN + 1):
+                url = '%s%d.txt' % (BASE_URL, k)
+                print("\nDownloading %d bit LFSR coeffs from %s..." % (k, url))
+                wget.download(url)
+            print("")
+
+            widths = []
+            coeffs = []
             for k in range(MIN_LFSR_LEN, MAX_LFSR_LEN + 1):
                 filename = '%d.txt' % k
                 with open(filename) as infile:
                     # read the first line
-                    poly_coeffs = infile.readline().strip()
-                    if k == MAX_LFSR_LEN:
-                        comma = ""
-                    if k == MIN_LFSR_LEN + 1:
-                        for l in range(len(decl_str)):
-                            spaces += ' '
-                    outfile.write("%s%d'h%s%s" %
-                                  (spaces, MAX_LFSR_LEN, poly_coeffs, comma))
-            outfile.write(' };\n')
+                    widths += [k]
+                    coeffs += [infile.readline().strip()]
+
+        # write to stdout or file
+        dump_coeffs(lfsrType, widths, coeffs, outfile=args.output)
     else:
         print("Temporary directory already exists, abort...")
+        sys.exit(1)
 
 
 if __name__ == '__main__':