blob: 9bb84da99cb0e1a0b59fa061b8640b3324da055a [file] [log] [blame]
#!/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()