blob: 664d39b1d8395c33a1db86a32f1927d1096d34eb [file] [log] [blame]
# Copyright 2022 GlobalFoundries PDK Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Run GlobalFoundries 180nm MCU DRC SC Regression.
Usage:
run_sc_regression.py (--help| -h)
run_sc_regression.py (--path=<file_path>)... [--thr=<thr>]
Options:
--help -h Print this help message
--path=<file_path> The input GDS file path.
--thr=<thr> The number of threads used in run.
"""
from docopt import docopt
import os
import xml.etree.ElementTree as ET
import csv
import time
import concurrent.futures
def get_results(rule_deck_path, iname, file, x):
"""
The function takes the path of the rule deck and the path of the gds file as inputs, then it runs
the DRC using the rule deck on the gds file and returns the name of the gds file, the name of the
rule deck, the names of the violated rules and the status of the gds file
:param rule_deck_path: The path to the rule deck file
:param path: The path to the GDS file you want to check
:return: the file name, the rule deck name, the violated rules and the status of the file.
"""
mytree = ET.parse(f"{iname[0]}_{x}.lyrdb")
myroot = mytree.getroot()
violated = []
for lrule in rules:
# Loop on database to get the violations of required rule
for z in myroot[7]:
if f"'{lrule}'" == f"{z[1].text}":
violated.append(lrule)
break
if len(violated) > 0:
status = "Not_clean"
else:
status = "Clean"
rule_deck = rule_deck_path.split("../")
print(f"\n The file {file[-1]} has violated rule deck {rule_deck[-1]} in: {len(violated)} rule/s which are: {violated} \n")
print(f" The file {file[-1]} with rule deck {rule_deck[-1]} is {status} \n")
return file[-1], rule_deck[-1], ' '.join(violated), status
def call_simulator(arg):
"""
It runs the simulator with the given rule deck and input file, and saves the output to a database
file
:param rule_deck_path: The path to the rule deck file
:param path: The path to the GDS file you want to simulate
:param thrCount: number of threads to use
"""
os.system(arg)
if __name__ == "__main__":
t0 = time.time()
args = docopt(__doc__)
if os.path.exists("sc"):
os.system("rm -rf sc")
report = [["File_Name", "Rule Deck", "Rules", "Status"]]
# Get threads count
if args["--thr"]:
thrCount = args["--thr"][0]
else:
thrCount = os.cpu_count() * 2
os.system("klayout -v")
rule_deck_path = []
rules = []
runs = []
files = os.listdir('..')
for file in files:
if ".drc" in file:
rule_deck_path.append(f"../{file}")
# Get rules names
for runset in rule_deck_path:
with open(runset, 'r') as f:
for line in f:
if 'GEOMETRY RULES' in line:
break
if ".output" in line:
line_list = line.split('"')
rules.append(line_list[1])
# Create GDS splitter script
with open('split_gds.rb', 'w') as f:
f.write(''' layout = RBA::Layout::new
layout.read($input)
Dir.mkdir("sc") unless File.exists?("sc")
layout.each_cell do |cell|
ly2 = RBA::Layout.new
ly2.dbu = layout.dbu
cell_index = layout.cell_by_name("#{cell.name}")
new_top = ly2.add_cell("#{cell.name}")
ly2.cell(new_top).copy_tree(layout.cell("#{cell.name}"))
ly2.write("sc/#{cell.name}.gds")
end''')
# Split standard cells top-cells into multiple gds files
for path in args["--path"]:
iname = path.split('.gds')
file = iname[0].split('/')
if "sc" in file[-1]:
os.system(f"klayout -b -r split_gds.rb -rd input={path}")
print(f"File {path} was splitted into multiple gds files")
other_files = os.listdir('sc')
args["--path"] = args["--path"] + other_files
# Get input data for simulator
for path in args["--path"]:
x = 0
iname = path.split('.gds')
file = iname[0].split('/')
if "sc" in file[-1]:
continue
if "/" not in path:
path = f"sc/{path}"
iname = path.split('.gds')
file = iname[0].split('/')
for runset in rule_deck_path:
arg = f"klayout -b -r {runset} -rd input={path} -rd report={file[-1]}_{x}.lyrdb -rd thr={thrCount} -rd conn_drc=true"
runs.append(arg)
x += 1
# Run DRC
with concurrent.futures.ProcessPoolExecutor(max_workers=thrCount) as executor:
for run in runs:
executor.submit(call_simulator, run)
# Get results
for path in args["--path"]:
x = 0
iname = path.split('.gds')
file = iname[0].split('/')
if "sc" in file[-1]:
continue
if "/" not in path:
path = f"sc/{path}"
iname = path.split('.gds')
file = iname[0].split('/')
for runset in rule_deck_path:
if os.path.exists(f"{iname[0]}_{x}.lyrdb"):
file, rule_deck, violations, status = get_results(runset, iname, file, x)
report.append([file, rule_deck, violations, status])
x += 1
with open(f'sc_drc_report.csv', 'w') as f:
writer = csv.writer(f, delimiter=',')
writer.writerows(report)
if os.path.exists("split_gds.rb"):
os.remove("split_gds.rb")
if os.path.exists("sc"):
os.system("rm -rf sc")
t1 = time.time()
print(f'Total execution time {t1 - t0} s')