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