blob: 9bb84da99cb0e1a0b59fa061b8640b3324da055a [file] [log] [blame]
lowRISC Contributors802543a2019-08-31 12:12:56 +01001#!/usr/bin/env python3
2# Copyright lowRISC contributors.
3# Licensed under the Apache License, Version 2.0, see LICENSE for details.
4# SPDX-License-Identifier: Apache-2.0
5
6import argparse
lowRISC Contributors802543a2019-08-31 12:12:56 +01007import os
8import shutil
Michael Schaffner49c82072019-09-10 14:38:06 -07009import subprocess
lowRISC Contributors802543a2019-08-31 12:12:56 +010010import sys
11
12import wget
13
Michael Schaffner49c82072019-09-10 14:38:06 -070014USAGE = """./get_lfsr_coeffs.py [-t <temporary folder>] [-o <outfile>] [-f] [--fib]
lowRISC Contributors802543a2019-08-31 12:12:56 +010015
Michael Schaffner49c82072019-09-10 14:38:06 -070016Downloads LFSR constants from [1] and dumps them in SystemVerilog format
17(for use in prim_lfsr.sv). These coeffs are for a Galois XOR type LFSR, and cover
18implementations ranging from 4 to 64bits.
19
20Alternatively, the script can also extract the XNOR Fibonacci type LFSR coefficients
21from the XILINX application note 52 [2] by specifying the --fib switch. Note that this
22depends on the pdftotext utility for Linux.
23
24[1] https://users.ece.cmu.edu/~koopman/lfsr/
25
26[2] https://www.xilinx.com/support/documentation/application_notes/xapp052.pdf
lowRISC Contributors802543a2019-08-31 12:12:56 +010027"""
28
Michael Schaffner49c82072019-09-10 14:38:06 -070029# configuration for Galois
lowRISC Contributors802543a2019-08-31 12:12:56 +010030MIN_LFSR_LEN = 4
31MAX_LFSR_LEN = 64
32BASE_URL = 'https://users.ece.cmu.edu/~koopman/lfsr/'
33
Michael Schaffner49c82072019-09-10 14:38:06 -070034# configuration for Fibonacci
35FIB_URL = 'https://www.xilinx.com/support/documentation/application_notes/xapp052.pdf'
36PDF_NAME = 'xapp052'
37LINE_FILTER = [
38 'Table 3: Taps for Maximum-Length LFSR Counters',
39 'XAPP 052 July 7,1996 (Version 1.1)'
40]
41
42
43# helper function to write out coeffs
44def dump_coeffs(lfsrType, widths, coeffs, outfile):
45 # widths consistency check
46 for k in range(widths[0], widths[-1] + 1):
47 # print("%d -- %d" % (k,widths[k-widths[0]]))
48 if k != widths[k - widths[0]]:
49 print("Error: widths is not consistently increasing")
50 sys.exit(1)
51
52 # select first coefficient in each file and print to SV LUT
53 with outfile:
54 decl_str = "localparam int unsigned %s_LUT_OFF = %d;\n" \
55 % (lfsrType, min(widths))
56 outfile.write(decl_str)
Michael Schaffner1b5fa9f2020-01-17 17:43:42 -080057 decl_str = "localparam logic [%d:0] %s_COEFFS [%d] = '{ " \
Michael Schaffner1fff9852021-01-08 14:06:35 -080058 % (max(widths) - 1, lfsrType, max(widths) - min(widths) + 1)
Michael Schaffner49c82072019-09-10 14:38:06 -070059 outfile.write(decl_str)
60 comma = ',\n'
61 spaces = ''
62 for k in widths:
63 if k == max(widths):
64 comma = ""
65 if k == min(widths) + 1:
Michael Schaffner1fff9852021-01-08 14:06:35 -080066 spaces += ' ' * len(decl_str)
67 outfile.write("%s%d'h%s%s" %
68 (spaces, max(widths), coeffs[k - widths[0]], comma))
Michael Schaffner49c82072019-09-10 14:38:06 -070069 outfile.write(' };\n')
70
71
72# converts list with bit positions to a hex bit mask string
73def to_bit_mask(bitPositions):
74
75 bitMask = 0
76 for b in bitPositions:
77 bitMask += 2**(b - 1)
78
79 return "%X" % bitMask
80
lowRISC Contributors802543a2019-08-31 12:12:56 +010081
82def main():
83 parser = argparse.ArgumentParser(
84 prog="get-lfsr-coeffs",
85 formatter_class=argparse.RawDescriptionHelpFormatter,
86 usage=USAGE,
87 description=__doc__,
88 epilog='defaults or the filename - can be used for stdin/stdout')
89 parser.add_argument(
90 '-t',
91 '--tempfolder',
92 help="""temporary folder to download the lfsr constant files
93to (defaults to lfsr_tmp)""",
94 default='lfsr_tmp')
Michael Schaffner49c82072019-09-10 14:38:06 -070095 parser.add_argument('--fib',
96 help='download fibonacci coefficients',
97 action='store_true')
lowRISC Contributors802543a2019-08-31 12:12:56 +010098 parser.add_argument('-f',
99 '--force',
100 help='overwrites tempfolder',
101 action='store_true')
102 parser.add_argument('-o',
103 '--output',
104 type=argparse.FileType('w'),
105 default=sys.stdout,
106 metavar='file',
107 help='Output file (default stdout)')
108
109 args = parser.parse_args()
lowRISC Contributors802543a2019-08-31 12:12:56 +0100110
111 if args.force and os.path.exists(args.tempfolder):
112 shutil.rmtree(args.tempfolder)
113
114 if not os.path.exists(args.tempfolder):
115 # download coefficient files
116 os.makedirs(args.tempfolder, exist_ok=args.force)
117 os.chdir(args.tempfolder)
lowRISC Contributors802543a2019-08-31 12:12:56 +0100118
Michael Schaffner49c82072019-09-10 14:38:06 -0700119 if args.fib:
120 lfsrType = 'FIB_XNOR'
121
122 wget.download(FIB_URL)
123 cmd = ['pdftotext %s.pdf' % PDF_NAME, '> %s.txt' % PDF_NAME]
124 subprocess.call(cmd, shell=True)
125 print("")
Michael Schaffner1fff9852021-01-08 14:06:35 -0800126 cmd = [
127 'grep -A 350 "%s" %s.txt > table.txt' %
128 (LINE_FILTER[0], PDF_NAME)
129 ]
Michael Schaffner49c82072019-09-10 14:38:06 -0700130 subprocess.call(cmd, shell=True)
131
132 # parse the table
133 widths = []
134 coeffs = []
135 columnType = 0
136 with open('table.txt') as infile:
137 for line in infile:
138 line = line.strip()
139 if line and line not in LINE_FILTER:
140 if line == 'n':
141 columnType = 0
142 # yes, this is a typo in the PDF :)
143 elif line == 'XNOR from':
144 columnType = 1
145 elif columnType:
146 tmpCoeffs = [int(c) for c in line.split(',')]
147 coeffs += [tmpCoeffs]
148 else:
149 widths += [int(line)]
150
151 # # printout for checking
152 # for (w,c) in zip(widths,coeffs):
153 # print("width: %d > coeffs: %s" % (w, str(c)))
154
155 # convert to bitmask
156 for k in range(len(coeffs)):
157 coeffs[k] = to_bit_mask(coeffs[k])
158
159 else:
160 lfsrType = 'GAL_XOR'
161
162 for k in range(MIN_LFSR_LEN, MAX_LFSR_LEN + 1):
163 url = '%s%d.txt' % (BASE_URL, k)
164 print("\nDownloading %d bit LFSR coeffs from %s..." % (k, url))
165 wget.download(url)
166 print("")
167
168 widths = []
169 coeffs = []
lowRISC Contributors802543a2019-08-31 12:12:56 +0100170 for k in range(MIN_LFSR_LEN, MAX_LFSR_LEN + 1):
171 filename = '%d.txt' % k
172 with open(filename) as infile:
173 # read the first line
Michael Schaffner49c82072019-09-10 14:38:06 -0700174 widths += [k]
175 coeffs += [infile.readline().strip()]
176
177 # write to stdout or file
178 dump_coeffs(lfsrType, widths, coeffs, outfile=args.output)
lowRISC Contributors802543a2019-08-31 12:12:56 +0100179 else:
180 print("Temporary directory already exists, abort...")
Michael Schaffner49c82072019-09-10 14:38:06 -0700181 sys.exit(1)
lowRISC Contributors802543a2019-08-31 12:12:56 +0100182
183
184if __name__ == '__main__':
185 main()