[util] Move design-related helper scripts to util/design
This also updates the paths and consolidates some subfunctions
Signed-off-by: Michael Schaffner <msf@opentitan.org>
diff --git a/util/design/get-lfsr-coeffs.py b/util/design/get-lfsr-coeffs.py
new file mode 100755
index 0000000..9bb84da
--- /dev/null
+++ b/util/design/get-lfsr-coeffs.py
@@ -0,0 +1,185 @@
+#!/usr/bin/env python3
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+import argparse
+import os
+import shutil
+import subprocess
+import sys
+
+import wget
+
+USAGE = """./get_lfsr_coeffs.py [-t <temporary folder>] [-o <outfile>] [-f] [--fib]
+
+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 [%d] = '{ " \
+ % (max(widths) - 1, lfsrType, max(widths) - min(widths) + 1)
+ outfile.write(decl_str)
+ comma = ',\n'
+ spaces = ''
+ for k in widths:
+ if k == max(widths):
+ comma = ""
+ if k == min(widths) + 1:
+ spaces += ' ' * len(decl_str)
+ 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(
+ prog="get-lfsr-coeffs",
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ usage=USAGE,
+ description=__doc__,
+ epilog='defaults or the filename - can be used for stdin/stdout')
+ parser.add_argument(
+ '-t',
+ '--tempfolder',
+ 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',
+ action='store_true')
+ parser.add_argument('-o',
+ '--output',
+ type=argparse.FileType('w'),
+ default=sys.stdout,
+ metavar='file',
+ help='Output file (default stdout)')
+
+ args = parser.parse_args()
+
+ if args.force and os.path.exists(args.tempfolder):
+ shutil.rmtree(args.tempfolder)
+
+ if not os.path.exists(args.tempfolder):
+ # download coefficient files
+ os.makedirs(args.tempfolder, exist_ok=args.force)
+ os.chdir(args.tempfolder)
+
+ 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
+ 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__':
+ main()