blob: 55e1c767c48bc6eeee37938b8c33a8957653fa6e [file] [log] [blame]
Tim Edwards51f81422020-07-26 12:49:48 -04001#!/usr/bin/env python3
2#
3# create_verilog_library.py
4#
5#----------------------------------------------------------------------------
6# Given a destination directory holding individual verilog files of a number
7# of modules, create a single verilog library file named <alllibname> and place
8# it in the same directory. This is done for the option "compile" if specified
9# for the "-verilog" install.
10#----------------------------------------------------------------------------
11
12import sys
13import os
14import re
15import glob
16import fnmatch
17
18#----------------------------------------------------------------------------
19
20def usage():
21 print('')
22 print('Usage:')
23 print(' create_verilog_library <destlibdir> <destlib> [-compile-only]')
24 print(' [-stub] [-excludelist="file1,file2,..."]')
25 print('')
26 print('Create a single verilog library from a set of individual verilog files.')
27 print('')
28 print('where:')
29 print(' <destlibdir> is the directory containing the individual files')
30 print(' <destlib> is the root name of the library file')
31 print(' -compile-only remove the indidual files if specified')
32 print(' -stub generate only the module headers for each cell')
33 print(' -excludelist= is a comma-separated list of files to ignore')
34 print('')
35
36#----------------------------------------------------------------------------
37
Tim Edwards9be4ac22020-07-26 12:59:30 -040038def create_verilog_library(destlibdir, destlib, do_compile_only=False, do_stub=False, excludelist=[]):
Tim Edwards51f81422020-07-26 12:49:48 -040039
Tim Edwards05e66eb2020-09-24 13:11:59 -040040 # 'destlib' should not have an extension, because one will be generated.
41 destlibroot = os.path.splitext(destlib)[0]
42
43 alllibname = destlibdir + '/' + destlibroot + '.v'
Tim Edwards51f81422020-07-26 12:49:48 -040044 if os.path.isfile(alllibname):
45 os.remove(alllibname)
46
Tim Edwards05e66eb2020-09-24 13:11:59 -040047 print('Diagnostic: Creating consolidated verilog library ' + destlibroot + '.v')
Tim Edwards995c1332020-09-25 15:33:58 -040048
49 # If file "filelist.txt" exists in the directory, get the list of files from it
50 if os.path.exists(destlibdir + '/filelist.txt'):
51 print('Diagnostic: Reading sorted verilog file list.')
52 with open(destlibdir + '/filelist.txt', 'r') as ifile:
53 rlist = ifile.read().splitlines()
54 vlist = []
55 for rfile in rlist:
56 vlist.append(destlibdir + '/' + rfile)
57 else:
58 vlist = glob.glob(destlibdir + '/*.v')
59
Tim Edwards51f81422020-07-26 12:49:48 -040060 if alllibname in vlist:
61 vlist.remove(alllibname)
62
63 # Create exclude list with glob-style matching using fnmatch
64 if len(vlist) > 0:
65 vlistnames = list(os.path.split(item)[1] for item in vlist)
66 notvlist = []
67 for exclude in excludelist:
68 notvlist.extend(fnmatch.filter(vlistnames, exclude))
69
70 # Apply exclude list
71 if len(notvlist) > 0:
72 for file in vlist[:]:
73 if os.path.split(file)[1] in notvlist:
74 vlist.remove(file)
75
76 if len(vlist) > 1:
77 print('New file is: ' + alllibname)
78 with open(alllibname, 'w') as ofile:
79 allmodules = []
80 for vfile in vlist:
81 with open(vfile, 'r') as ifile:
82 # print('Adding ' + vfile + ' to library.')
83 vtext = ifile.read()
84 modules = re.findall(r'[ \t\n]module[ \t]+([^ \t\n\(]+)', vtext)
85 mseen = list(item for item in modules if item in allmodules)
86 allmodules.extend(list(item for item in modules if item not in allmodules))
87 vfilter = remove_redundant_modules(vtext, allmodules, mseen)
88 # NOTE: The following workaround resolves an issue with iverilog,
89 # which does not properly parse specify timing paths that are not in
90 # parentheses. Easy to work around
91 vlines = re.sub(r'\)[ \t]*=[ \t]*([01]:[01]:[01])[ \t]*;', r') = ( \1 ) ;', vfilter)
92 print(vlines, file=ofile)
93 print('\n//--------EOF---------\n', file=ofile)
94
95 if do_compile_only == True:
96 print('Compile-only: Removing individual verilog files')
97 for vfile in vlist:
98 if os.path.isfile(vfile):
99 os.remove(vfile)
100 elif os.path.islink(vfile):
101 os.unlink(vfile)
102 else:
103 print('Only one file (' + str(vlist) + '); ignoring "compile" option.')
104
105#----------------------------------------------------------------------------
106# Remove redundant module entries from a verilog file. "m2list" is a list of
107# module names gleaned from all previously read files using re.findall().
108# "mlist" is a list of all module names including those in "ntext".
109# The reason for doing this is that some verilog files may includes modules used
110# by all the files, and if included more than once, then iverilog complains.
111#----------------------------------------------------------------------------
112
113def remove_redundant_modules(ntext, mlist, m2list):
114 updated = ntext
115 for module in mlist:
116 # Determine the number of times the module appears in the text
117 if module in m2list:
118 # This module seen before outside of ntext, so remove all occurrances in ntext
119 new = re.sub(r'[ \t\n]+module[ \t]+' + module + '[ \t\n\(]+.*[ \t\n]endmodule', '\n', updated, flags=re.DOTALL)
120 updated = new
121
122 else:
123 n = len(re.findall(r'[ \t\n]module[ \t]+' + module + '[ \t\n\(]+.*[ \t\n]endmodule', updated, flags=re.DOTALL))
124 # This module defined more than once inside ntext, so remove all but one
125 # Optimization: Just keep original text if n < 2
126 if n < 2:
127 continue
128
129 # Remove all but one
130 updated = re.sub(r'[ \t\n]+module[ \t]+' + module + '[ \t\n]+.*[ \t\n]endmodule', '\n', n - 1, updated, flags=re.IGNORECASE | re.DOTALL)
131 return updated
132
133#----------------------------------------------------------------------------
134
135if __name__ == '__main__':
136
137 if len(sys.argv) == 1:
138 usage()
139 sys.exit(0)
140
141 argumentlist = []
142
143 # Defaults
144 do_compile_only = False
145 do_stub = False
146 excludelist = []
147
148 # Break arguments into groups where the first word begins with "-".
149 # All following words not beginning with "-" are appended to the
150 # same list (optionlist). Then each optionlist is processed.
151 # Note that the first entry in optionlist has the '-' removed.
152
153 for option in sys.argv[1:]:
154 if option.find('-', 0) == 0:
155 keyval = option[1:].split('=')
156 if keyval[0] == 'compile-only':
157 if len(keyval) > 0:
158 if keyval[1].tolower() == 'true' or keyval[1].tolower() == 'yes' or keyval[1] == '1':
159 do_compile_only = True
160 else:
161 do_compile_only = True
162 elif keyval[1] == 'exclude' or key == 'excludelist':
163 if len(keyval) > 0:
164 excludelist = keyval[1].trim('"').split(',')
165 else:
166 print("No items in exclude list (ignoring).")
167 elif keyval[0] == 'stub':
168 if len(keyval) > 0:
169 if keyval[1].tolower() == 'true' or keyval[1].tolower() == 'yes' or keyval[1] == '1':
170 do_stub = True
171 else:
172 do_stub = True
173 else:
174 print("Unknown option '" + keyval[0] + "' (ignoring).")
175 else:
176 argumentlist.append(option)
177
178 if len(argumentlist) < 3:
179 print("Not enough arguments given to create_verilog_library.py.")
180 usage()
181 sys.exit(1)
182
183 destlibdir = argumentlist[0]
184 destlib = argumentlist[1]
185 startup_script = argumentlist[2]
186
187 print('')
188 print('Create verilog library from files:')
189 print('')
190 print('Path to files: ' + destlibdir)
191 print('Name of compiled library: ' + destlib + '.v')
192 print('Path to magic startup script: ' + startup_script)
193 print('Remove individual files: ' + 'Yes' if do_compile_only else 'No')
194 if len(excludelist) > 0:
195 print('List of files to exclude: ')
196 for file in excludelist:
197 print(file)
198 print('')
199
200 create_verilog_library(destlibdir, destlib, startup_script, do_compile_only, do_stub, excludelist)
201 print('Done.')
202 sys.exit(0)
203
204#----------------------------------------------------------------------------