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