Skip to content

Commit f657291

Browse files
authored
P&R timing flow for Innovus/Tempus (#858)
* Fix par timing flow for innovus/tempus * default to output netlist if needed * add default value * run generate properties * add update constraint mode to tempus flow * pass timing inputs to timing tool * use full tree ilms * use full tree ilms * make physical netlist optional * remove extraneous call to get * remove use of my constraint mode * typo * sdcs read in by read_mmmc in mmmc flow
1 parent ad01485 commit f657291

File tree

7 files changed

+96
-21
lines changed

7 files changed

+96
-21
lines changed

hammer/common/cadence/__init__.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
class CadenceTool(HasSDCSupport, HasCPFSupport, HasUPFSupport, TCLTool, HammerTool):
1515
"""Mix-in trait with functions useful for Cadence-based tools."""
1616

17+
constraint_mode = "my_constraint_mode"
18+
1719
@property
1820
def env_vars(self) -> Dict[str, str]:
1921
"""
@@ -127,9 +129,6 @@ def generate_mmmc_script(self) -> str:
127129
def append_mmmc(cmd: str) -> None:
128130
self.verbose_tcl_append(cmd, mmmc_output)
129131

130-
# Create an Innovus constraint mode.
131-
constraint_mode = "my_constraint_mode"
132-
133132
sdc_files = self.generate_sdc_files()
134133

135134
# Append any custom SDC files.
@@ -150,13 +149,13 @@ def append_mmmc(cmd: str) -> None:
150149
self.run_executable(["touch", blank_sdc])
151150
sdc_files_arg = "-sdc_files {{ {} }}".format(blank_sdc)
152151
if self.hierarchical_mode.is_nonleaf_hierarchical():
153-
ilm_sdcs = reduce_list_str(add_lists, list(map(lambda ilm: ilm.sdcs, self.get_input_ilms()))) # type: List[str]
152+
ilm_sdcs = reduce_list_str(add_lists, list(map(lambda ilm: ilm.sdcs, self.get_input_ilms())), []) # type: List[str]
154153
ilm_sdc_files_arg = "-ilm_sdc_files [list {sdc_files}]".format(
155154
sdc_files=" ".join(ilm_sdcs))
156155
else:
157156
ilm_sdc_files_arg = ""
158157
append_mmmc("create_constraint_mode -name {name} {sdc_files_arg} {ilm_sdc_files_arg}".format(
159-
name=constraint_mode,
158+
name=self.constraint_mode,
160159
sdc_files_arg=sdc_files_arg,
161160
ilm_sdc_files_arg=ilm_sdc_files_arg
162161
))
@@ -205,7 +204,7 @@ def append_mmmc(cmd: str) -> None:
205204
# Next, create the analysis views
206205
append_mmmc("create_analysis_view -name {name}_view -delay_corner {name}_delay -constraint_mode {constraint}".format(
207206
name=corner_name,
208-
constraint=constraint_mode
207+
constraint=self.constraint_mode
209208
))
210209

211210
# Finally, apply the analysis view.
@@ -251,7 +250,7 @@ def append_mmmc(cmd: str) -> None:
251250
# Next, create an Innovus analysis view.
252251
analysis_view_name = "my_view"
253252
append_mmmc("create_analysis_view -name {name} -delay_corner {corner} -constraint_mode {constraint}".format(
254-
name=analysis_view_name, corner=delay_corner_name, constraint=constraint_mode))
253+
name=analysis_view_name, corner=delay_corner_name, constraint=self.constraint_mode))
255254
# Finally, apply the analysis view.
256255
# TODO: introduce different views of setup/hold and true multi-corner
257256
append_mmmc("set_analysis_view -setup {{ {setup_view} }} -hold {{ {hold_view} }}".format(

hammer/generate_properties.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ def main(args) -> int:
145145
"(optional) output ILM information for hierarchical mode"),
146146
InterfaceVar("output_gds", "str", "path to the output GDS file"),
147147
InterfaceVar("output_netlist", "str", "path to the output netlist file"),
148+
InterfaceVar("output_physical_netlist", "Optional[str]", "(optional) path to the output physical netlist file"),
148149
InterfaceVar("output_sim_netlist", "str", "path to the output simulation netlist file"),
149150
InterfaceVar("hcells_list", "List[str]",
150151
"list of cells to explicitly map hierarchically in LVS"),
@@ -249,7 +250,9 @@ def main(args) -> int:
249250
InterfaceVar("spefs", "Optional[List]",
250251
"(optional) list of SPEF files"),
251252
InterfaceVar("sdf_file", "Optional[str]",
252-
"(optional) input SDF file")
253+
"(optional) input SDF file"),
254+
InterfaceVar("def_file", "Optional[str]",
255+
"(optional) input DEF file")
253256
],
254257
outputs=[]
255258
)

hammer/par/innovus/__init__.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def export_config_outputs(self) -> Dict[str, Any]:
3232
outputs["par.outputs.seq_cells"] = self.output_seq_cells
3333
outputs["par.outputs.all_regs"] = self.output_all_regs
3434
outputs["par.outputs.sdf_file"] = self.output_sdf_path
35+
outputs["par.outputs.def_file"] = self.output_def_path
3536
outputs["par.outputs.spefs"] = self.output_spef_paths
3637
return outputs
3738

@@ -114,6 +115,10 @@ def output_gds_filename(self) -> str:
114115
def output_netlist_filename(self) -> str:
115116
return os.path.join(self.run_dir, "{top}.lvs.v".format(top=self.top_module))
116117

118+
@property
119+
def output_physical_netlist_filename(self) -> str:
120+
return os.path.join(self.run_dir, "{top}.physical.v".format(top=self.top_module))
121+
117122
@property
118123
def output_sim_netlist_filename(self) -> str:
119124
return os.path.join(self.run_dir, "{top}.sim.v".format(top=self.top_module))
@@ -130,6 +135,10 @@ def all_cells_path(self) -> str:
130135
def output_sdf_path(self) -> str:
131136
return os.path.join(self.run_dir, "{top}.par.sdf".format(top=self.top_module))
132137

138+
@property
139+
def output_def_path(self) -> str:
140+
return os.path.join(self.run_dir, "{top}.def".format(top=self.top_module))
141+
133142
@property
134143
def output_spef_paths(self) -> List[str]:
135144
corners = self.get_mmmc_corners()
@@ -554,8 +563,8 @@ def place_opt_design(self) -> bool:
554563
if self.hierarchical_mode.is_nonleaf_hierarchical():
555564
self.verbose_append('''
556565
flatten_ilm
557-
update_constraint_mode -name my_constraint_mode -ilm_sdc_files {sdc}
558-
'''.format(sdc=self.post_synth_sdc), clean=True)
566+
update_constraint_mode -name {name} -ilm_sdc_files {sdc}
567+
'''.format(name=self.constraint_mode, sdc=self.post_synth_sdc), clean=True)
559568

560569
# Use place_opt_design V2 (POD-Turbo). Option must be explicitly set only in 22.1.
561570
if self.version() >= self.version_number("221") and self.version() < self.version_number("231"):
@@ -771,13 +780,20 @@ def write_netlist(self) -> bool:
771780
if pwr_gnd_net.tie is not None:
772781
self.verbose_append("connect_global_net {tie} -type net -net_base_name {net}".format(tie=pwr_gnd_net.tie, net=pwr_gnd_net.name))
773782

774-
# Output the Verilog netlist for the design and include physical cells (-phys) like decaps and fill
783+
# Output a flattened Verilog netlist for the design and include physical cells (-phys) like decaps and fill
775784
self.verbose_append("write_netlist {netlist} -top_module_first -top_module {top} -exclude_leaf_cells -phys -flat -exclude_insts_of_cells {{ {pcells} }} ".format(
776785
netlist=self.output_netlist_filename,
777786
top=self.top_module,
778787
pcells=" ".join(self.get_physical_only_cells())
779788
))
780789

790+
# Output a non-flattened Verilog netlist with physical instances
791+
self.verbose_append("write_netlist {netlist} -top_module_first -top_module {top} -exclude_leaf_cells -phys -exclude_insts_of_cells {{ {pcells} }} ".format(
792+
netlist=self.output_physical_netlist_filename,
793+
top=self.top_module,
794+
pcells=" ".join(self.get_physical_only_cells())
795+
))
796+
781797
self.verbose_append("write_netlist {netlist} -top_module_first -top_module {top} -exclude_leaf_cells -exclude_insts_of_cells {{ {pcells} }} ".format(
782798
netlist=self.output_sim_netlist_filename,
783799
top=self.top_module,
@@ -848,6 +864,11 @@ def write_gds(self) -> bool:
848864
))
849865
return True
850866

867+
def write_def(self) -> bool:
868+
self.verbose_append("write_def {def_file} -floorplan -netlist -routing".format(def_file=self.output_def_path))
869+
870+
return True
871+
851872
def write_sdf(self) -> bool:
852873
if self.hierarchical_mode.is_nonleaf_hierarchical():
853874
self.verbose_append("flatten_ilm")
@@ -936,6 +957,9 @@ def write_design(self) -> bool:
936957
# Write netlist
937958
self.write_netlist()
938959

960+
# Write DEF
961+
self.write_def()
962+
939963
# GDS streamout.
940964
self.write_gds()
941965

hammer/timing/tempus/__init__.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def init_design(self) -> bool:
107107
hammer_tech.filters.lef_filter
108108
], hammer_tech.HammerTechnologyUtils.to_plain_item)
109109
if self.hierarchical_mode.is_nonleaf_hierarchical():
110-
ilm_lefs = list(map(lambda ilm: ilm.lef, self.get_input_ilms()))
110+
ilm_lefs = list(map(lambda ilm: ilm.lef, self.get_input_ilms(full_tree=True)))
111111
lef_files.extend(ilm_lefs)
112112
verbose_append("read_physical -lef {{ {files} }}".format(
113113
files=" ".join(lef_files)
@@ -127,17 +127,22 @@ def init_design(self) -> bool:
127127

128128
if self.hierarchical_mode.is_nonleaf_hierarchical():
129129
# Read ILMs.
130-
for ilm in self.get_input_ilms():
130+
for ilm in self.get_input_ilms(full_tree=True):
131131
# Assumes that the ILM was created by Innovus (or at least the file/folder structure).
132132
# TODO: support non-Innovus hierarchical (read netlists, etc.)
133-
verbose_append("read_ilm -cell {module} -directory {dir}".format(dir=ilm.dir, module=ilm.module))
133+
verbose_append("set_ilm -cell {module} -in_dir {dir}".format(dir=ilm.dir, module=ilm.module))
134134

135135
# Read power intent
136136
if self.get_setting("vlsi.inputs.power_spec_mode") != "empty":
137137
# Setup power settings from cpf/upf
138138
for l in self.generate_power_spec_commands():
139139
verbose_append(l)
140140

141+
verbose_append("init_design")
142+
143+
if self.def_file is not None:
144+
verbose_append("read_def " + os.path.join(os.getcwd(), self.def_file))
145+
141146
# Read parasitics
142147
if self.spefs is not None: # post-P&R
143148
corners = self.get_mmmc_corners()
@@ -166,10 +171,9 @@ def init_design(self) -> bool:
166171
if self.sdf_file is not None:
167172
verbose_append("read_sdf " + os.path.join(os.getcwd(), self.sdf_file))
168173

169-
verbose_append("init_design")
170-
171-
# TODO: Optionally read additional DEF or OA physical data
172-
174+
if self.hierarchical_mode.is_nonleaf_hierarchical() and len(self.get_input_ilms(full_tree=True)):
175+
verbose_append("read_ilm")
176+
verbose_append("flatten_ilm")
173177

174178
# Set some default analysis settings for max accuracy
175179
# Clock path pessimism removal

hammer/timing/tempus/defaults.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ timing.tempus:
88

99
# Tempus version to use
1010
# Used to locate the binary - e.g. the '211_ISR3' in ${cadence.cadence_home}/SSV/SSV211_ISR3/bin/tempus
11-
version: "211_ISR3"
11+
version: "231"
1212

1313
# Enable signal integrity delay and glitch analysis
1414
# Note: your tech libs should have noise models!

hammer/vlsi/driver.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,9 @@ def set_up_timing_tool(self, timing_tool: HammerTimingTool,
757757
missing_inputs = True
758758
if missing_inputs:
759759
return False
760+
timing_tool.post_synth_sdc = self.database.get_setting("timing.inputs.post_synth_sdc")
761+
timing_tool.spefs = self.database.get_setting("timing.inputs.spefs")
762+
timing_tool.def_file = self.database.get_setting("timing.inputs.def_file")
760763

761764
self.timing_tool = timing_tool
762765

@@ -1165,13 +1168,14 @@ def par_output_to_timing_input(output_dict: dict) -> Optional[dict]:
11651168
or None if output_dict was invalid
11661169
"""
11671170
try:
1168-
input_files = deeplist([output_dict["par.outputs.output_netlist"]])
1171+
input_files = deeplist([output_dict.get("par.outputs.output_physical_netlist", output_dict["par.outputs.output_netlist"])])
11691172
result = {
11701173
"timing.inputs.input_files": input_files,
11711174
"timing.inputs.input_files_meta": "append",
11721175
"timing.inputs.top_module": output_dict["par.inputs.top_module"],
1176+
"timing.inputs.post_synth_sdc": output_dict["par.inputs.post_synth_sdc"],
11731177
"timing.inputs.spefs": output_dict["par.outputs.spefs"],
1174-
"timing.inputs.sdf_file": output_dict["par.outputs.sdf_file"],
1178+
"timing.inputs.def_file": output_dict.get("par.outputs.def_file", None),
11751179
"vlsi.builtins.is_complete": False
11761180
} # type: Dict[str, Any]
11771181
return result

hammer/vlsi/hammer_vlsi_impl.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ def export_config_outputs(self) -> Dict[str, Any]:
397397
outputs["vlsi.inputs.ilms_meta"] = "append" # to coalesce ILMs for entire hierarchical tree
398398
outputs["par.outputs.output_gds"] = str(self.output_gds)
399399
outputs["par.outputs.output_netlist"] = str(self.output_netlist)
400+
outputs["par.outputs.output_physical_netlist"] = str(self.output_physical_netlist)
400401
outputs["par.outputs.output_sim_netlist"] = str(self.output_sim_netlist)
401402
outputs["par.outputs.hcells_list"] = list(self.hcells_list)
402403
outputs["par.outputs.seq_cells"] = self.output_seq_cells
@@ -511,6 +512,26 @@ def output_netlist(self, value: str) -> None:
511512
self.attr_setter("_output_netlist", value)
512513

513514

515+
@property
516+
def output_physical_netlist(self) -> Optional[str]:
517+
"""
518+
Get the (optional) path to the output physical netlist file.
519+
520+
:return: The (optional) path to the output physical netlist file.
521+
"""
522+
try:
523+
return self.attr_getter("_output_physical_netlist", None)
524+
except AttributeError:
525+
return None
526+
527+
@output_physical_netlist.setter
528+
def output_physical_netlist(self, value: Optional[str]) -> None:
529+
"""Set the (optional) path to the output physical netlist file."""
530+
if not (isinstance(value, str) or (value is None)):
531+
raise TypeError("output_physical_netlist must be a Optional[str]")
532+
self.attr_setter("_output_physical_netlist", value)
533+
534+
514535
@property
515536
def output_sim_netlist(self) -> str:
516537
"""
@@ -2085,6 +2106,26 @@ def sdf_file(self, value: Optional[str]) -> None:
20852106
self.attr_setter("_sdf_file", value)
20862107

20872108

2109+
@property
2110+
def def_file(self) -> Optional[str]:
2111+
"""
2112+
Get the (optional) input DEF file.
2113+
2114+
:return: The (optional) input DEF file.
2115+
"""
2116+
try:
2117+
return self.attr_getter("_def_file", None)
2118+
except AttributeError:
2119+
return None
2120+
2121+
@def_file.setter
2122+
def def_file(self, value: Optional[str]) -> None:
2123+
"""Set the (optional) input DEF file."""
2124+
if not (isinstance(value, str) or (value is None)):
2125+
raise TypeError("def_file must be a Optional[str]")
2126+
self.attr_setter("_def_file", value)
2127+
2128+
20882129
### Outputs ###
20892130
### END Generated interface HammerTimingTool ###
20902131

0 commit comments

Comments
 (0)