1#! /usr/bin/env python3 2 3import argparse 4import os 5import re 6from datetime import date 7from shutil import copy, copytree 8 9import xlsxwriter 10 11 12class VIO(object): 13 def __init__(self, info): 14 self.info = info 15 assert(self.info[0] in ["input", "output"]) 16 self.direction = self.info[0] 17 self.width = 0 if self.info[1] == "" else int(self.info[1].split(":")[0].replace("[", "")) 18 self.width += 1 19 self.name = self.info[2] 20 21 def get_direction(self): 22 return self.direction 23 24 def get_width(self): 25 return self.width 26 27 def get_name(self): 28 return self.name 29 30 def startswith(self, prefix): 31 return self.info[2].startswith(prefix) 32 33 def __str__(self): 34 return " ".join(self.info) 35 36 def __repr__(self): 37 return self.__str__() 38 39 def __lt__(self, other): 40 return str(self) < str(other) 41 42class VModule(object): 43 module_re = re.compile(r'^\s*module\s*(\w+)\s*(#\(?|)\s*(\(.*|)\s*$') 44 io_re = re.compile(r'^\s*(input|output)\s*(\[\s*\d+\s*:\s*\d+\s*\]|)\s*(\w+),?\s*$') 45 submodule_re = re.compile(r'^\s*(\w+)\s*(#\(.*\)|)\s*(\w+)\s*\(\s*(|//.*)\s*$') 46 # when instance submodule is multiline, it will endswith #( or ( , 47 # we can only get the submodule's module name, and set instance name "multiline_instance" 48 submodule_re_multiline = re.compile(r'^\s*(\w+)\s*#*\(\s*$') 49 difftest_module_re = re.compile(r'^ \w*Difftest\w+\s+\w+ \( //.*$') 50 51 def __init__(self, name): 52 self.name = name 53 self.lines = [] 54 self.io = [] 55 self.submodule = dict() 56 self.instance = set() 57 self.in_difftest = False 58 59 def add_line(self, line): 60 debug_dontCare = False 61 if "RegFile" in self.name and "@(posedge clock)" in line: 62 line = line.replace("posedge", "negedge") 63 elif "RenameTable" in self.name: 64 if line.strip().startswith("assign io_debug_rdata_"): 65 debug_dontCare = True 66 elif "SynRegfileSlice" in self.name: 67 if line.strip().startswith("assign io_debug_ports_"): 68 debug_dontCare = True 69 70 # start of difftest module 71 difftest_match = self.difftest_module_re.match(line) 72 if difftest_match: 73 self.in_difftest = True 74 self.lines.append("`ifndef SYNTHESIS\n") 75 76 if debug_dontCare: 77 self.lines.append("`ifndef SYNTHESIS\n") 78 self.lines.append(line) 79 if debug_dontCare: 80 self.lines.append("`else\n") 81 debug_dontCare_name = line.strip().split(" ")[1] 82 self.lines.append(f" assign {debug_dontCare_name} = 0;\n") 83 self.lines.append("`endif\n") 84 85 # end of difftest module 86 if self.in_difftest and line.strip() == ");": 87 self.in_difftest = False 88 self.lines.append("`endif\n") 89 90 if len(self.lines): 91 io_match = self.io_re.match(line) 92 if io_match: 93 this_io = VIO(tuple(map(lambda i: io_match.group(i), range(1, 4)))) 94 self.io.append(this_io) 95 submodule_mutiline_match = self.submodule_re_multiline.match(line) 96 submodule_match = self.submodule_re.match(line) or submodule_mutiline_match 97 if submodule_mutiline_match: 98 print('submodule_re_mutiline:') 99 print(line) 100 if submodule_match: 101 this_submodule = submodule_match.group(1) 102 if this_submodule != "module": 103 print(self.name + " submodule_match:") 104 print(this_submodule) 105 self.add_submodule(this_submodule) 106 if (submodule_mutiline_match): 107 self.add_instance(this_submodule, "multiline_instance") 108 else: 109 self.add_instance(this_submodule, submodule_match.group(3)) 110 111 def add_lines(self, lines): 112 for line in lines: 113 self.add_line(line) 114 115 def get_name(self): 116 return self.name 117 118 def set_name(self, updated_name): 119 for i, line in enumerate(self.lines): 120 module_match = VModule.module_re.match(line) 121 if module_match: 122 print(f"Line Previously: {line.strip()}") 123 updated_line = line.replace(self.name, updated_name) 124 print(f"Line Updated: {updated_line.strip()}") 125 self.lines[i] = updated_line 126 break 127 self.name = updated_name 128 129 def get_lines(self): 130 return self.lines + ["\n"] 131 132 def get_io(self, prefix="", match=""): 133 if match: 134 r = re.compile(match) 135 return list(filter(lambda x: r.match(str(x)), self.io)) 136 else: 137 return list(filter(lambda x: x.startswith(prefix), self.io)) 138 139 def get_submodule(self): 140 return self.submodule 141 142 def get_instance(self): 143 return self.instance 144 145 def add_submodule(self, name): 146 self.submodule[name] = self.submodule.get(name, 0) + 1 147 148 def add_instance(self, name, instance_name): 149 self.instance.add((name, instance_name)) 150 151 def add_submodules(self, names): 152 for name in names: 153 self.add_submodule(name) 154 155 def dump_io(self, prefix="", match=""): 156 print("\n".join(map(lambda x: str(x), self.get_io(prefix, match)))) 157 158 def get_mbist_type(self): 159 r = re.compile(r'input.*mbist_(\w+)_(trim|sleep)_fuse.*') 160 mbist_fuse_io = list(filter(lambda x: r.match(str(x)), self.io)) 161 mbist_types = list(set(map(lambda io: io.get_name().split("_")[1], mbist_fuse_io))) 162 assert(len(mbist_types) == 1) 163 return mbist_types[0] 164 165 def replace(self, s): 166 self.lines = [s] 167 168 def replace_with_macro(self, macro, s): 169 replaced_lines = [] 170 in_io, in_body = False, False 171 for line in self.lines: 172 if self.io_re.match(line): 173 in_io = True 174 replaced_lines.append(line) 175 elif in_io: 176 in_io = False 177 in_body = True 178 replaced_lines.append(line) # This is ");" 179 replaced_lines.append(f"`ifdef {macro}\n") 180 replaced_lines.append(s) 181 replaced_lines.append(f"`else\n") 182 elif in_body: 183 if line.strip() == "endmodule": 184 replaced_lines.append(f"`endif // {macro}\n") 185 replaced_lines.append(line) 186 else: 187 replaced_lines.append(line) 188 self.lines = replaced_lines 189 190 def __str__(self): 191 module_name = "Module {}: \n".format(self.name) 192 module_io = "\n".join(map(lambda x: "\t" + str(x), self.io)) + "\n" 193 return module_name + module_io 194 195 def __repr__(self): 196 return "{}".format(self.name) 197 198 199class VCollection(object): 200 def __init__(self): 201 self.modules = [] 202 self.ancestors = [] 203 204 def load_modules(self, vfile): 205 in_module = False 206 current_module = None 207 skipped_lines = [] 208 with open(vfile) as f: 209 print("Loading modules from {}...".format(vfile)) 210 for i, line in enumerate(f): 211 module_match = VModule.module_re.match(line) 212 if module_match: 213 module_name = module_match.group(1) 214 if in_module or current_module is not None: 215 print("Line {}: does not find endmodule for {}".format(i, current_module)) 216 exit() 217 current_module = VModule(module_name) 218 for skip_line in skipped_lines: 219 print("[WARNING]{}:{} is added to module {}:\n{}".format(vfile, i, module_name, skip_line), end="") 220 current_module.add_line(skip_line) 221 skipped_lines = [] 222 in_module = True 223 if not in_module or current_module is None: 224 if line.strip() != "":# and not line.strip().startswith("//"): 225 skipped_lines.append(line) 226 continue 227 current_module.add_line(line) 228 if line.startswith("endmodule"): 229 self.modules.append(current_module) 230 current_module = None 231 in_module = False 232 233 def get_module_names(self): 234 return list(map(lambda m: m.get_name(), self.modules)) 235 236 def get_all_modules(self, match=""): 237 if match: 238 r = re.compile(match) 239 return list(filter(lambda m: r.match(m.get_name()), self.modules)) 240 else: 241 return self.modules 242 243 def get_module(self, name, negedge_modules=None, negedge_prefix=None, with_submodule=False, try_prefix=None, ignore_modules=None): 244 if negedge_modules is None: 245 negedge_modules = [] 246 target = None 247 for module in self.modules: 248 if module.get_name() == name: 249 target = module 250 if target is None and try_prefix is not None: 251 for module in self.modules: 252 name_no_prefix = name[len(try_prefix):] 253 if module.get_name() == name_no_prefix: 254 target = module 255 print(f"Replace {name_no_prefix} with modulename {name}. Please DOUBLE CHECK the verilog.") 256 target.set_name(name) 257 if target is None or not with_submodule: 258 return target 259 submodules = set() 260 submodules.add(target) 261 for submodule, instance in target.get_instance(): 262 if ignore_modules is not None and submodule in ignore_modules: 263 continue 264 self.ancestors.append(instance) 265 is_negedge_module = False 266 if negedge_prefix is not None: 267 if submodule.startswith(negedge_prefix): 268 is_negedge_module = True 269 elif try_prefix is not None and submodule.startswith(try_prefix + negedge_prefix): 270 is_negedge_module = True 271 if is_negedge_module: 272 negedge_modules.append("/".join(self.ancestors)) 273 result = self.get_module(submodule, negedge_modules, negedge_prefix, with_submodule=True, try_prefix=try_prefix, ignore_modules=ignore_modules) 274 self.ancestors.pop() 275 if result is None: 276 print("Error: cannot find submodules of {} or the module itself".format(submodule)) 277 return None 278 submodules.update(result) 279 return submodules 280 281 def dump_to_file(self, name, output_dir, with_submodule=True, split=True, try_prefix=None, ignore_modules=None): 282 print("Dump module {} to {}...".format(name, output_dir)) 283 modules = self.get_module(name, with_submodule=with_submodule, try_prefix=try_prefix, ignore_modules=ignore_modules) 284 if modules is None: 285 print("does not find module", name) 286 return False 287 # print("All modules:", modules) 288 if not with_submodule: 289 modules = [modules] 290 if not os.path.isdir(output_dir): 291 os.makedirs(output_dir, exist_ok=True) 292 if split: 293 for module in modules: 294 output_file = os.path.join(output_dir, module.get_name() + ".v") 295 # print("write module", module.get_name(), "to", output_file) 296 with open(output_file, "w") as f: 297 f.writelines(module.get_lines()) 298 else: 299 output_file = os.path.join(output_dir, name + ".v") 300 with open(output_file, "w") as f: 301 for module in modules: 302 f.writelines(module.get_lines()) 303 return True 304 305 def dump_negedge_modules_to_file(self, name, output_dir, with_submodule=True, try_prefix=None): 306 print("Dump negedge module {} to {}...".format(name, output_dir)) 307 negedge_modules = [] 308 self.get_module(name, negedge_modules, "NegedgeDataModule_", with_submodule=with_submodule, try_prefix=try_prefix) 309 negedge_modules_sort = [] 310 for negedge in negedge_modules: 311 re_degits = re.compile(r".*[0-9]$") 312 if re_degits.match(negedge): 313 negedge_module, num = negedge.rsplit("_", 1) 314 else: 315 negedge_module, num = negedge, -1 316 negedge_modules_sort.append((negedge_module, int(num))) 317 negedge_modules_sort.sort(key = lambda x : (x[0], x[1])) 318 output_file = os.path.join(output_dir, "negedge_modules.txt") 319 with open(output_file, "w")as f: 320 f.write("set sregfile_list [list\n") 321 for negedge_module, num in negedge_modules_sort: 322 if num == -1: 323 f.write("{}\n".format(negedge_module)) 324 else: 325 f.write("{}_{}\n".format(negedge_module, num)) 326 f.write("]") 327 328 def add_module(self, name, line): 329 module = VModule(name) 330 module.add_line(line) 331 self.modules.append(module) 332 return module 333 334 def count_instances(self, top_name, name): 335 if top_name == name: 336 return 1 337 count = 0 338 top_module = self.get_module(top_name) 339 if top_module is not None: 340 for submodule in top_module.submodule: 341 count += top_module.submodule[submodule] * self.count_instances(submodule, name) 342 return count 343 344def check_data_module_template(collection): 345 error_modules = [] 346 field_re = re.compile(r'io_(w|r)data_(\d*)(_.*|)') 347 modules = collection.get_all_modules(match="(Sync|Async)DataModuleTemplate.*") 348 for module in modules: 349 module_name = module.get_name() 350 print("Checking", module_name, "...") 351 wdata_all = sorted(module.get_io(match="input.*wdata.*")) 352 rdata_all = sorted(module.get_io(match="output.*rdata.*")) 353 wdata_pattern = set(map(lambda x: " ".join((str(x.get_width()), field_re.match(x.get_name()).group(3))), wdata_all)) 354 rdata_pattern = set(map(lambda x: " ".join((str(x.get_width()), field_re.match(x.get_name()).group(3))), rdata_all)) 355 if wdata_pattern != rdata_pattern: 356 print("Errors:") 357 print(" wdata only:", sorted(wdata_pattern - rdata_pattern, key=lambda x: x.split(" ")[1])) 358 print(" rdata only:", sorted(rdata_pattern - wdata_pattern, key=lambda x: x.split(" ")[1])) 359 print("In", str(module)) 360 error_modules.append(module) 361 return error_modules 362 363def create_verilog(files, top_module, config, try_prefix=None, ignore_modules=None): 364 collection = VCollection() 365 for f in files: 366 collection.load_modules(f) 367 today = date.today() 368 directory = f'{top_module}-Release-{config}-{today.strftime("%b-%d-%Y")}' 369 success = collection.dump_to_file(top_module, os.path.join(directory, top_module), try_prefix=try_prefix, ignore_modules=ignore_modules) 370 collection.dump_negedge_modules_to_file(top_module, directory, try_prefix=try_prefix) 371 if not success: 372 return None, None 373 return collection, os.path.realpath(directory) 374 375def get_files(build_path): 376 files = [] 377 for f in os.listdir(build_path): 378 file_path = os.path.join(build_path, f) 379 if f.endswith(".v") or f.endswith(".sv"): 380 files.append(file_path) 381 elif os.path.isdir(file_path): 382 files += get_files(file_path) 383 return files 384 385def create_filelist(filelist_name, out_dir, file_dirs=None, extra_lines=[]): 386 if file_dirs is None: 387 file_dirs = [filelist_name] 388 filelist_entries = [] 389 for file_dir in file_dirs: 390 for filename in os.listdir(os.path.join(out_dir, file_dir)): 391 if filename.endswith(".v") or filename.endswith(".sv"): 392 # check whether it exists in previous directories 393 # this infers an implicit priority between the file_dirs 394 filelist_entry = os.path.join(file_dir, filename) 395 if filelist_entry in filelist_entries: 396 print(f'[warning]: {filelist_entry} is already in filelist_entries') 397 else: 398 filelist_entries.append(filelist_entry) 399 with open(os.path.join(out_dir, f"{filelist_name}.f"), "w") as f: 400 for entry in filelist_entries + extra_lines: 401 f.write(f"{entry}\n") 402 403 404class SRAMConfiguration(object): 405 ARRAY_NAME = "sram_array_(\d)p(\d+)x(\d+)m(\d+)(_multicycle|)(_repair|)" 406 407 SINGLE_PORT = 0 408 SINGLE_PORT_MASK = 1 409 DUAL_PORT = 2 410 DUAL_PORT_MASK = 3 411 412 def __init__(self): 413 self.name = None 414 self.depth = None 415 self.width = None 416 self.ports = None 417 self.mask_gran = None 418 self.has_multi_cycle = False 419 self.has_repair = False 420 421 def size(self): 422 return self.depth * self.width 423 424 def is_single_port(self): 425 return self.ports == self.SINGLE_PORT or self.ports == self.SINGLE_PORT_MASK 426 427 def mask_width(self): 428 return self.width // self.mask_gran 429 430 def match_module_name(self, module_name): 431 sram_array_re = re.compile(self.ARRAY_NAME) 432 module_name_match = sram_array_re.match(self.name) 433 return module_name_match 434 435 def from_module_name(self, module_name): 436 self.name = module_name 437 module_name_match = self.match_module_name(self.name) 438 assert(module_name_match is not None) 439 num_ports = int(module_name_match.group(1)) 440 self.depth = int(module_name_match.group(2)) 441 self.width = int(module_name_match.group(3)) 442 self.mask_gran = int(module_name_match.group(4)) 443 assert(self.width % self.mask_gran == 0) 444 if num_ports == 1: 445 self.ports = self.SINGLE_PORT if self.mask_width() == 1 else self.SINGLE_PORT_MASK 446 else: 447 self.ports = self.DUAL_PORT if self.mask_width() == 1 else self.DUAL_PORT_MASK 448 self.has_multi_cycle = str(module_name_match.group(5)) != "" 449 self.has_repair = str(module_name_match.group(6)) != "" 450 451 def ports_s(self): 452 s = { 453 self.SINGLE_PORT: "rw", 454 self.SINGLE_PORT_MASK: "mrw", 455 self.DUAL_PORT: "write,read", 456 self.DUAL_PORT_MASK: "mwrite,read" 457 } 458 return s[self.ports] 459 460 def to_sram_conf_entry(self): 461 all_info = ["name", self.name, "depth", self.depth, "width", self.width, "ports", self.ports_s()] 462 if self.mask_gran < self.width: 463 all_info += ["mask_gran", self.mask_gran] 464 return " ".join(map(str, all_info)) 465 466 def from_sram_conf_entry(self, line): 467 items = line.strip().split(" ") 468 self.name = items[1] 469 if items[7] == "rw": 470 ports = self.SINGLE_PORT 471 elif items[7] == "mrw": 472 ports = self.SINGLE_PORT_MASK 473 elif items[7] == "write,read": 474 ports = self.DUAL_PORT 475 elif items[7] == "mwrite,read": 476 ports = self.DUAL_PORT_MASK 477 else: 478 assert(0) 479 depth = int(items[3]) 480 width = int(items[5]) 481 mask_gran = int(items[-1]) if len(items) > 8 else width 482 matched_name = self.match_module_name(self.name) is not None 483 if matched_name: 484 self.from_module_name(self.name) 485 assert(self.ports == ports) 486 assert(self.depth == depth) 487 assert(self.width == width) 488 assert(self.mask_gran == mask_gran) 489 else: 490 self.ports = ports 491 self.depth = depth 492 self.width = width 493 self.mask_gran = mask_gran 494 495 def to_sram_xlsx_entry(self, num_instances): 496 if self.is_single_port(): 497 num_read_port = "shared 1" 498 num_write_port = "shared 1" 499 read_clk = "RW0_clk" 500 write_clk = "RW0_clk" 501 else: 502 num_read_port = 1 503 num_write_port = 1 504 read_clk = "R0_clk" 505 write_clk = "W0_clk" 506 all_info = [self.name, num_instances, "SRAM", num_read_port, num_write_port, 0, 507 self.depth, self.width, self.mask_gran, read_clk, write_clk, "N/A"] 508 return all_info 509 510 def get_foundry_sram_wrapper(self, mbist_type): 511 wrapper_type = "RAMSP" if self.is_single_port() else "RF2P" 512 wrapper_mask = "" if self.mask_width() == 1 else f"_M{self.mask_width()}" 513 wrapper_module = f"{wrapper_type}_{self.depth}x{self.width}{wrapper_mask}_WRAP" 514 wrapper_instance = "u_mem" 515 foundry_ports = { 516 "IP_RESET_B" : "mbist_IP_RESET_B", 517 "PWR_MGMT_IN" : "mbist_PWR_MGNT_IN", 518 "TRIM_FUSE_IN" : f"mbist_{mbist_type}_trim_fuse", 519 "SLEEP_FUSE_IN" : f"mbist_{mbist_type}_sleep_fuse", 520 "FSCAN_RAM_BYPSEL" : "mbist_bypsel", 521 "FSCAN_RAM_WDIS_B" : "mbist_wdis_b", 522 "FSCAN_RAM_RDIS_B" : "mbist_rdis_b", 523 "FSCAN_RAM_INIT_EN" : "mbist_init_en", 524 "FSCAN_RAM_INIT_VAL" : "mbist_init_val", 525 "FSCAN_CLKUNGATE" : "mbist_clkungate", 526 "OUTPUT_RESET" : "mbist_OUTPUT_RESET", 527 "PWR_MGMT_OUT" : "mbist_PWR_MGNT_OUT" 528 } 529 if self.is_single_port(): 530 foundry_ports["WRAPPER_CLK_EN"] = "mbist_WRAPPER_CLK_EN" 531 else: 532 foundry_ports["WRAPPER_WR_CLK_EN"] = "mbist_WRAPPER_WR_CLK_EN" 533 foundry_ports["WRAPPER_RD_CLK_EN"] = "mbist_WRAPPER_RD_CLK_EN" 534 if self.has_repair: 535 foundry_ports["ROW_REPAIR_IN"] = "repair_rowRepair" 536 foundry_ports["COL_REPAIR_IN"] = "repair_colRepair" 537 foundry_ports["io_bisr_shift_en"] = "mbist_bisr_shift_en" 538 foundry_ports["io_bisr_clock"] = "mbist_bisr_clock" 539 foundry_ports["io_bisr_reset"] = "mbist_bisr_reset" 540 foundry_ports["u_mem_bisr_inst_SI"] = "mbist_bisr_scan_in" 541 foundry_ports["u_mem_bisr_inst_SO"] = "mbist_bisr_scan_out" 542 if self.is_single_port(): 543 func_ports = { 544 "CK" : "RW0_clk", 545 "A" : "RW0_addr", 546 "WEN" : "RW0_en & RW0_wmode", 547 "D" : "RW0_wdata", 548 "REN" : "RW0_en & ~RW0_wmode", 549 "Q" : "RW0_rdata" 550 } 551 if self.mask_width() > 1: 552 func_ports["WM"] = "RW0_wmask" 553 else: 554 func_ports = { 555 "WCK" : "W0_clk", 556 "WA" : "W0_addr", 557 "WEN" : "W0_en", 558 "D" : "W0_data", 559 "RCK" : "R0_clk", 560 "RA" : "R0_addr", 561 "REN" : "R0_en", 562 "Q" : "R0_data" 563 } 564 if self.mask_width() > 1: 565 func_ports["WM"] = "W0_mask" 566 if self.width > 256: 567 func_ports["MBIST_SELECTEDOH"] = "mbist_selectedOH" 568 verilog_lines = [] 569 verilog_lines.append(f" {wrapper_module} {wrapper_instance} (\n") 570 connected_pins = [] 571 for pin_name in func_ports: 572 connected_pins.append(f".{pin_name}({func_ports[pin_name]})") 573 for pin_name in foundry_ports: 574 connected_pins.append(f".{pin_name}({foundry_ports[pin_name]})") 575 verilog_lines.append(" " + ",\n ".join(connected_pins) + "\n") 576 verilog_lines.append(" );\n") 577 return wrapper_module, "".join(verilog_lines) 578 579def generate_sram_conf(collection, module_prefix, out_dir): 580 if module_prefix is None: 581 module_prefix = "" 582 sram_conf = [] 583 sram_array_name = module_prefix + SRAMConfiguration.ARRAY_NAME 584 modules = collection.get_all_modules(match=sram_array_name) 585 for module in modules: 586 conf = SRAMConfiguration() 587 conf.from_module_name(module.get_name()[len(module_prefix):]) 588 sram_conf.append(conf) 589 conf_path = os.path.join(out_dir, "sram_configuration.txt") 590 with open(conf_path, "w") as f: 591 for conf in sram_conf: 592 f.write(conf.to_sram_conf_entry() + "\n") 593 return conf_path 594 595def create_sram_xlsx(out_dir, collection, sram_conf, top_module, try_prefix=None): 596 workbook = xlsxwriter.Workbook(os.path.join(out_dir, "sram_list.xlsx")) 597 worksheet = workbook.add_worksheet() 598 # Header for the list. Starting from row 5. 599 row = 5 600 columns = ["Array Instance Name", "# Instances", "Memory Type", 601 "# Read Ports", "# Write Ports", "# CAM Ports", 602 "Depth (Entries)", "Width (Bits)", "# Write Segments", 603 "Read Clk Pin Names(s)", "Write Clk Pin Name(s)", "CAM Clk Pin Name" 604 ] 605 for col, column_name in enumerate(columns): 606 worksheet.write(row, col, column_name) 607 row += 1 608 # Entries for the list. 609 total_size = 0 610 with open(sram_conf) as f: 611 for line in f: 612 conf = SRAMConfiguration() 613 conf.from_sram_conf_entry(line) 614 num_instances = collection.count_instances(top_module, conf.name) 615 if num_instances == 0 and try_prefix is not None: 616 try_prefix_name = f"{try_prefix}{conf.name}" 617 num_instances = collection.count_instances(top_module, try_prefix_name) 618 if num_instances != 0: 619 conf.name = try_prefix_name 620 all_info = conf.to_sram_xlsx_entry(num_instances) 621 for col, info in enumerate(all_info): 622 worksheet.write(row, col, info) 623 row += 1 624 total_size += conf.size() * num_instances 625 # Total size of the SRAM in top of the sheet 626 worksheet.write(0, 0, f"Total size: {total_size / (8 * 1024)} KiB") 627 workbook.close() 628 629def create_extra_files(out_dir, build_path): 630 extra_path = os.path.join(out_dir, "extra") 631 copytree("/nfs/home/share/southlake/extra", extra_path) 632 for f in os.listdir(build_path): 633 file_path = os.path.join(build_path, f) 634 if f.endswith(".csv"): 635 copy(file_path, extra_path) 636 637def replace_sram(out_dir, sram_conf, top_module, module_prefix): 638 replace_sram_dir = "memory_array" 639 replace_sram_path = os.path.join(out_dir, replace_sram_dir) 640 if not os.path.exists(replace_sram_path): 641 os.mkdir(replace_sram_path) 642 sram_wrapper_dir = "memory_wrapper" 643 sram_wrapper_path = os.path.join(out_dir, sram_wrapper_dir) 644 if not os.path.exists(sram_wrapper_path): 645 os.mkdir(sram_wrapper_path) 646 replaced_sram = [] 647 with open(sram_conf) as f: 648 for line in f: 649 conf = SRAMConfiguration() 650 conf.from_sram_conf_entry(line) 651 sim_sram_module = VModule(conf.name) 652 sim_sram_path = os.path.join(out_dir, top_module, f"{conf.name}.v") 653 if not os.path.exists(sim_sram_path) and module_prefix is not None: 654 sim_sram_path = os.path.join(out_dir, top_module, f"{module_prefix}{conf.name}.v") 655 sim_sram_module.name = f"{module_prefix}{conf.name}" 656 if not os.path.exists(sim_sram_path): 657 print(f"SRAM Replace: does not find {sim_sram_path}. Skipped.") 658 continue 659 with open(sim_sram_path, "r") as sim_f: 660 sim_sram_module.add_lines(sim_f.readlines()) 661 mbist_type = sim_sram_module.get_mbist_type() 662 wrapper, instantiation_v = conf.get_foundry_sram_wrapper(mbist_type) 663 sim_sram_module.replace_with_macro("FOUNDRY_MEM", instantiation_v) 664 output_file = os.path.join(replace_sram_path, f"{sim_sram_module.name}.v") 665 with open(output_file, "w") as f: 666 f.writelines(sim_sram_module.get_lines()) 667 # uncomment the following lines to copy the provided memory wrapper 668 # wrapper_dir = "/nfs/home/share/southlake/sram_replace/mem_wrap" 669 # wrapper_path = os.path.join(wrapper_dir, f"{wrapper}.v") 670 # copy(wrapper_path, os.path.join(sram_wrapper_path, f"{wrapper}.v")) 671 replaced_sram.append(sim_sram_module.name) 672 with open(os.path.join(out_dir, f"{sram_wrapper_dir}.f"), "w") as wrapper_f: 673 wrapper_f.write("// FIXME: include your SRAM wrappers here\n") 674 return replace_sram_dir, [f"-F {sram_wrapper_dir}.f"] 675 676 677def replace_mbist_scan_controller(out_dir): 678 target_dir = "scan_mbist_ctrl" 679 target_path = os.path.join(out_dir, target_dir) 680 if not os.path.exists(target_path): 681 os.mkdir(target_path) 682 blackbox_src_dir = "/nfs/home/share/southlake/sram_replace/scan_mbist_ctrl_rpl_rtl" 683 for filename in os.listdir(blackbox_src_dir): 684 if filename.startswith("bosc_") and (filename.endswith(".v") or filename.endswith(".sv")): 685 copy(os.path.join(blackbox_src_dir, filename), target_path) 686 with open(os.path.join(out_dir, "dfx_blackbox.f"), "w") as wrapper_f: 687 wrapper_f.write("// FIXME: include your blackbox mbist/scan controllers here\n") 688 return target_dir, [f"-F dfx_blackbox.f"] 689 690 691if __name__ == "__main__": 692 parser = argparse.ArgumentParser(description='Verilog parser for XS') 693 parser.add_argument('top', type=str, help='top-level module') 694 parser.add_argument('--xs-home', type=str, help='path to XS') 695 parser.add_argument('--config', type=str, default="Unknown", help='XSConfig') 696 parser.add_argument('--prefix', type=str, help='module prefix') 697 parser.add_argument('--ignore', type=str, default="", help='ignore modules (and their submodules)') 698 parser.add_argument('--include', type=str, help='include verilog from more directories') 699 parser.add_argument('--no-filelist', action='store_true', help='do not create filelist') 700 parser.add_argument('--no-sram-conf', action='store_true', help='do not create sram configuration file') 701 parser.add_argument('--no-sram-xlsx', action='store_true', help='do not create sram configuration xlsx') 702 parser.add_argument('--with-extra-files', action='store_true', help='copy extra files') # for southlake alone 703 parser.add_argument('--sram-replace', action='store_true', help='replace SRAM libraries') 704 parser.add_argument('--mbist-scan-replace', action='store_true', help='replace mbist and scan controllers') # for southlake alone 705 706 args = parser.parse_args() 707 708 xs_home = args.xs_home 709 if xs_home is None: 710 xs_home = os.path.realpath(os.getenv("NOOP_HOME")) 711 assert(xs_home is not None) 712 build_path = os.path.join(xs_home, "build") 713 files = get_files(build_path) 714 if args.include is not None: 715 for inc_path in args.include.split(","): 716 files += get_files(inc_path) 717 718 top_module = args.top 719 module_prefix = args.prefix 720 config = args.config 721 ignore_modules = list(filter(lambda x: x != "", args.ignore.split(","))) 722 if module_prefix is not None: 723 top_module = f"{module_prefix}{top_module}" 724 ignore_modules += list(map(lambda x: module_prefix + x, ignore_modules)) 725 726 print(f"Top-level Module: {top_module} with prefix {module_prefix}") 727 print(f"Config: {config}") 728 print(f"Ignored modules: {ignore_modules}") 729 collection, out_dir = create_verilog(files, top_module, config, try_prefix=module_prefix, ignore_modules=ignore_modules) 730 assert(collection) 731 732 rtl_dirs = [top_module] 733 extra_filelist_lines = [] 734 if args.mbist_scan_replace: 735 dfx_ctrl, extra_dfx_lines = replace_mbist_scan_controller(out_dir) 736 rtl_dirs = [dfx_ctrl] + rtl_dirs 737 extra_filelist_lines += extra_dfx_lines 738 if not args.no_filelist: 739 create_filelist(top_module, out_dir, rtl_dirs, extra_filelist_lines) 740 if not args.no_sram_conf: 741 sram_conf = generate_sram_conf(collection, module_prefix, out_dir) 742 if not args.no_sram_xlsx: 743 create_sram_xlsx(out_dir, collection, sram_conf, top_module, try_prefix=module_prefix) 744 if args.sram_replace: 745 sram_replace_dir, sram_extra_lines = replace_sram(out_dir, sram_conf, top_module, module_prefix) 746 # We create another filelist for foundry-provided SRAMs 747 if not args.no_filelist: 748 rtl_dirs = [sram_replace_dir] + rtl_dirs 749 extra_filelist_lines += sram_extra_lines 750 create_filelist(f"{top_module}_with_foundry_sram", out_dir, rtl_dirs, extra_filelist_lines) 751 if args.with_extra_files: 752 create_extra_files(out_dir, build_path) 753