2022-10-09 05:23:01 -05:00
#!/usr/bin/env python3
# This script runs PrimeTime STA
2023-04-19 06:28:00 -05:00
2022-10-09 05:23:01 -05:00
import argparse
import os
def run_sta_all (
2022-10-17 11:13:12 -05:00
design : str ,
output_dir : str ,
2023-04-19 06:28:00 -05:00
log_dir : str ,
root_dir : str ,
2022-10-09 05:23:01 -05:00
) :
2022-10-17 11:13:12 -05:00
proc_corners = [ " t " , " s " , " f " ]
rc_corners = [ " nom " , " max " , " min " ]
for proc in proc_corners :
for rc in rc_corners :
2023-04-19 06:28:00 -05:00
run_sta ( design , proc , rc , output_dir , log_dir , root_dir )
2022-10-09 05:23:01 -05:00
def run_sta (
2022-10-17 11:13:12 -05:00
design : str ,
proc_corner : str ,
rc_corner : str ,
output_dir : str ,
2023-04-19 06:28:00 -05:00
log_dir : str ,
root_dir : str ,
2022-10-09 05:23:01 -05:00
) :
2022-10-17 11:13:12 -05:00
print ( f " PrimeTime STA run for design: { design } at process corner { proc_corner } and RC corner { rc_corner } " )
2023-04-19 06:28:00 -05:00
2022-10-17 11:13:12 -05:00
# Output directory structure
sub_dirs = [ ' reports ' , ' sdf ' , ' lib ' ]
for item in sub_dirs :
path = os . path . join ( output_dir , item )
try :
2023-04-19 06:28:00 -05:00
os . makedirs ( os . path . join ( path , f " { proc_corner } { proc_corner } " ) )
2022-10-17 11:13:12 -05:00
except FileExistsError :
# directory already exists
pass
# Enviornment Variables
check_env_vars ( )
os . environ [ " PDK_ROOT " ] = os . getenv ( ' PDK_ROOT ' )
os . environ [ " PDK " ] = os . getenv ( ' PDK ' )
2023-04-19 06:28:00 -05:00
if " sky130 " in os . getenv ( ' PDK ' ) :
os . environ [ " PT_LIB_ROOT " ] = os . getenv ( ' PT_LIB_ROOT ' )
2022-10-17 11:13:12 -05:00
os . environ [ " CARAVEL_ROOT " ] = os . getenv ( ' CARAVEL_ROOT ' )
os . environ [ " UPRJ_ROOT " ] = os . getenv ( ' UPRJ_ROOT ' )
os . environ [ " MCW_ROOT " ] = os . getenv ( ' MCW_ROOT ' )
os . environ [ " OUT_DIR " ] = output_dir
2023-04-19 06:28:00 -05:00
os . environ [ " ROOT " ] = root_dir
2022-10-17 11:13:12 -05:00
SCRIPT_DIR = os . path . dirname ( os . path . abspath ( __file__ ) )
os . environ [ " DESIGN " ] = design
os . environ [ " PROC_CORNER " ] = proc_corner
os . environ [ " RC_CORNER " ] = rc_corner
# PrimeTime command
PT_tcl = f " { SCRIPT_DIR } /pt_sta.tcl "
2023-04-19 06:28:00 -05:00
pt_command = f " pt_shell -f { PT_tcl } -output_log_file { log_dir } / { design } - { proc_corner } - { rc_corner } -sta.log "
2022-10-17 11:13:12 -05:00
os . system ( pt_command )
2023-04-19 06:28:00 -05:00
log = open ( f " { log_dir } / { design } - { proc_corner } - { rc_corner } -sta.log " , " a " )
# Print missing spef
missing_spefs , spefs = find_missing_spefs ( f " { log_dir } / { rc_corner } -parasitics.log " )
if missing_spefs :
print ( " The following spefs are missing: " )
log . write ( " The following spefs are missing: \n " )
for spef in spefs :
print ( spef )
log . write ( f " { spef } \n " )
2022-11-06 08:19:15 -06:00
# Check if there are any violations
2023-04-19 06:28:00 -05:00
sta_pass = search_viol ( f " { output_dir } /reports/ { proc_corner } { proc_corner } / { design } . { rc_corner } -global.rpt " , f " { log_dir } / { design } - { proc_corner } - { rc_corner } -sta.log " )
2022-10-17 11:13:12 -05:00
if sta_pass == " pass " :
print ( f " STA run Passed! " )
log . write ( f " STA run Passed! " )
2023-04-19 06:28:00 -05:00
elif sta_pass == " no link " :
print ( f " STA run Failed! " )
log . write ( f " STA run Failed! \n " )
print ( f " Linking failed. check log: { log_dir } / { design } - { proc_corner } - { rc_corner } -sta.log " )
log . write ( f " Linking failed. check log: { log_dir } / { design } - { proc_corner } - { rc_corner } -sta.log " )
2022-10-09 09:40:32 -05:00
else :
2022-10-17 11:13:12 -05:00
if sta_pass == " max_tran_cap " :
print ( f " STA run Passed! " )
log . write ( f " STA run Passed! \n " )
2023-04-19 06:28:00 -05:00
print ( f " There are max_transition and max_capacitance violations. check report: { output_dir } /reports/ { proc_corner } { proc_corner } / { design } . { rc_corner } -all_viol.rpt " )
log . write ( f " There are max_transition and max_capacitance violations. check report: { output_dir } /reports/ { proc_corner } { proc_corner } / { design } . { rc_corner } -all_viol.rpt " )
2022-10-17 11:13:12 -05:00
elif sta_pass == " max_tran " :
print ( f " STA run Passed! " )
log . write ( f " STA run Passed! \n " )
2023-04-19 06:28:00 -05:00
print ( f " There are max_transition violations. check report: { output_dir } /reports/ { proc_corner } { proc_corner } / { design } . { rc_corner } -all_viol.rpt " )
log . write ( f " There are max_transition violations. check report: { output_dir } /reports/ { proc_corner } { proc_corner } / { design } . { rc_corner } -all_viol.rpt " )
2022-10-17 11:13:12 -05:00
elif sta_pass == " max_cap " :
print ( f " STA run Passed! " )
log . write ( f " STA run Passed! \n " )
2023-04-19 06:28:00 -05:00
print ( f " There are max_capacitance violations. check report: { output_dir } /reports/ { proc_corner } { proc_corner } / { design } . { rc_corner } -all_viol.rpt " )
log . write ( f " There are max_capacitance violations. check report: { output_dir } /reports/ { proc_corner } { proc_corner } / { design } . { rc_corner } -all_viol.rpt " )
elif sta_pass == " viol " :
print ( f " There are other violations. check report: { output_dir } /reports/ { proc_corner } { proc_corner } / { design } . { rc_corner } -all_viol.rpt " )
log . write ( f " There are other violations. check report: { output_dir } /reports/ { proc_corner } { proc_corner } / { design } . { rc_corner } -all_viol.rpt " )
2022-10-17 11:13:12 -05:00
else :
print ( f " STA run Failed! " )
log . write ( f " STA run Failed! \n " )
if sta_pass == " setup " :
2023-04-19 06:28:00 -05:00
print ( f " There are setup violations. check report: { output_dir } /reports/ { proc_corner } { proc_corner } / { design } . { rc_corner } -global.rpt " )
log . write ( f " There are setup violations. check report: { output_dir } /reports/ { proc_corner } { proc_corner } / { design } . { rc_corner } -global.rpt " )
2022-10-17 11:13:12 -05:00
elif sta_pass == " hold " :
2023-04-19 06:28:00 -05:00
print ( f " There are hold violations. check report: { output_dir } /reports/ { proc_corner } { proc_corner } / { design } . { rc_corner } -global.rpt " )
log . write ( f " There are hold violations. check report: { output_dir } /reports/ { proc_corner } { proc_corner } / { design } . { rc_corner } -global.rpt " )
2022-11-11 04:52:32 -06:00
elif sta_pass == " no cons " :
2023-04-19 06:28:00 -05:00
print ( f " Reading constraints SDC failed. check log: { log_dir } / { design } - { proc_corner } - { rc_corner } -sta.log " )
log . write ( f " Reading constraints SDC failed. check log: { log_dir } / { design } - { proc_corner } - { rc_corner } -sta.log " )
2022-10-17 11:13:12 -05:00
log . close ( )
2022-10-09 05:23:01 -05:00
# Check the required env variables
def check_env_vars ( ) :
2022-10-17 11:13:12 -05:00
pdk_root = os . getenv ( ' PDK_ROOT ' )
pdk = os . getenv ( ' PDK ' )
2023-04-19 06:28:00 -05:00
if " sky130 " in os . getenv ( ' PDK ' ) :
pt_lib_root = os . getenv ( ' PT_LIB_ROOT ' )
if pt_lib_root is None :
raise FileNotFoundError (
" Please export PT_LIB_ROOT to the PrimeTime liberties path "
)
2022-10-17 11:13:12 -05:00
caravel_root = os . getenv ( ' CARAVEL_ROOT ' )
uprj_root = os . getenv ( ' UPRJ_ROOT ' )
mcw_root = os . getenv ( ' MCW_ROOT ' )
if pdk_root is None :
raise FileNotFoundError (
" Please export PDK_ROOT to the PDK path "
)
if pdk is None :
raise FileNotFoundError (
" Please export PDK to either sky130A or sky130B "
)
if caravel_root is None :
raise FileNotFoundError (
" Please export CARAVEL_ROOT to the Caravel repo path "
)
if mcw_root is None :
raise FileNotFoundError (
" Please export MCW_ROOT to the Caravel Management SoC Litex repo path "
)
if uprj_root is None :
raise FileNotFoundError (
" Please export UPRJ_ROOT to the Caravel User Project Wrapper repo path "
)
# Analyze the STA all violators output report
def search_viol (
2023-04-19 06:28:00 -05:00
report_path : str ,
log_path : str
2022-10-17 11:13:12 -05:00
) :
2022-11-11 04:52:32 -06:00
with open ( log_path , ' r ' ) as report :
data = report . read ( )
if " Could not auto-link design " in data :
return " no link "
elif " Error: Errors reading SDC file: " in data :
return " no cons "
2022-10-17 11:13:12 -05:00
with open ( report_path , ' r ' ) as report :
data = report . read ( )
2022-11-11 04:52:32 -06:00
if " Hold violations " in data :
2022-10-17 11:13:12 -05:00
return " hold "
2022-11-11 04:52:32 -06:00
elif " Setup violations " in data :
return " setup "
2022-10-17 11:13:12 -05:00
report_path = report_path . replace ( " global " , " all_viol " )
with open ( report_path , ' r ' ) as report :
data = report . read ( )
if " max_transition " in data :
if " max_capacitance " in data :
return " max_tran_cap "
return " max_tran "
elif " max_capacitance " in data :
return " max_cap "
elif " VIOLATED " in data :
return " viol "
else :
return " pass "
2023-04-19 06:28:00 -05:00
# Find missing spefs in parasitics annotation
def find_missing_spefs (
log_path : str
) :
missing_spefs = 0
spefs = [ ]
with open ( log_path , ' r ' ) as log :
data = log . read ( )
if " Error: Cannot open file " in data :
missing_spefs = 1
log . seek ( 0 )
lines = log . readlines ( )
for line in lines :
if " Error: Cannot open file " in line :
spef = line . split ( " . " ) [ 0 ] . split ( " / " ) [ - 1 ]
spefs . append ( spef )
return missing_spefs , spefs
2022-10-17 11:13:12 -05:00
if __name__ == " __main__ " :
parser = argparse . ArgumentParser (
description = " Run STA using PrimeTime "
)
parser . add_argument (
" -d " ,
" --design " ,
help = " design name " ,
required = True
2022-10-10 17:46:41 -05:00
)
2022-10-17 11:13:12 -05:00
parser . add_argument (
" -o " ,
" --output_dir " ,
help = " output directory " ,
required = True
2022-10-10 17:46:41 -05:00
)
2022-10-17 11:13:12 -05:00
parser . add_argument (
" -l " ,
" --logs_dir " ,
2022-11-06 08:19:15 -06:00
help = " log directory " ,
2022-10-17 11:13:12 -05:00
required = True
2022-10-09 05:23:01 -05:00
)
2023-04-19 06:28:00 -05:00
parser . add_argument (
" -r " ,
" --root_dir " ,
help = " design root directory " ,
required = True
)
2022-10-17 11:13:12 -05:00
parser . add_argument (
" -rc " ,
" --rc_corner " ,
help = " Specify the RC corner for the parasitics (Values are nom, max, or min) <default is nom> " ,
nargs = " ? " ,
default = " nom "
2022-10-09 05:23:01 -05:00
)
2022-10-17 11:13:12 -05:00
parser . add_argument (
" -proc " ,
" --proc_corner " ,
help = " Specify the process corner (Values are t, f, or s) <default is t> " ,
nargs = " ? " ,
default = " t "
2022-10-09 05:23:01 -05:00
)
2022-10-17 11:13:12 -05:00
parser . add_argument (
" -a " ,
" --all " ,
help = " Specify to run all the process corners and rc corners combinations for the design " ,
action = ' store_true '
2022-10-12 16:07:36 -05:00
)
2023-04-19 06:28:00 -05:00
parser . add_argument (
" -upw " ,
" --upw " ,
help = " Specify to run with non-empty user project wrapper <default is false " ,
nargs = " ? " ,
default = " 0 "
)
parser . add_argument (
" -rep " ,
" --reports " ,
help = " Specify to generate reports only skipping generating liberties and sdf (for faster runtime) " ,
action = ' store_true '
)
2022-10-09 05:23:01 -05:00
2022-10-17 11:13:12 -05:00
args = parser . parse_args ( )
2022-11-06 08:19:15 -06:00
output = os . path . abspath ( args . output_dir )
2022-10-17 11:13:12 -05:00
log = os . path . abspath ( args . logs_dir )
2023-04-19 06:28:00 -05:00
root = os . path . abspath ( args . root_dir )
os . environ [ " UPW " ] = args . upw
if args . reports :
os . environ [ " REPORTS_ONLY " ] = " 1 "
else :
os . environ [ " REPORTS_ONLY " ] = " 0 "
2022-10-09 05:23:01 -05:00
2022-10-10 17:46:41 -05:00
try :
2022-10-17 11:13:12 -05:00
os . makedirs ( output )
2022-10-10 17:46:41 -05:00
except FileExistsError :
2022-10-17 11:13:12 -05:00
# directory already exists
pass
2022-10-10 17:46:41 -05:00
2022-10-17 11:13:12 -05:00
try :
os . makedirs ( log )
except FileExistsError :
# directory already exists
pass
sub_dirs = [ ' reports ' , ' sdf ' , ' lib ' ]
for item in sub_dirs :
try :
os . makedirs ( os . path . join ( output , item ) )
except FileExistsError :
# directory already exists
pass
if args . all :
2023-04-19 06:28:00 -05:00
run_sta_all ( args . design , output , log , root )
2022-10-17 11:13:12 -05:00
else :
2023-04-19 06:28:00 -05:00
run_sta ( args . design , args . proc_corner , args . rc_corner , output , log , root )