From 17d836fcaeec87dbc9aeb65762ef184bf1834b45 Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Sun, 18 Feb 2024 10:01:40 -0800 Subject: [PATCH 1/8] Many enhancements --- hammer/par/innovus/__init__.py | 154 ++++++++++++++++++++++++++++++-- hammer/par/innovus/defaults.yml | 10 +++ 2 files changed, 155 insertions(+), 9 deletions(-) diff --git a/hammer/par/innovus/__init__.py b/hammer/par/innovus/__init__.py index ad442a7da..e62baa118 100644 --- a/hammer/par/innovus/__init__.py +++ b/hammer/par/innovus/__init__.py @@ -155,6 +155,8 @@ def env_vars(self) -> Dict[str, str]: v["INNOVUS_BIN"] = self.get_setting("par.innovus.innovus_bin") if self.version() >= self.version_number("221"): # support critical region resynthesis with DDI v["PATH"] = f'{os.environ.copy()["PATH"]}:{os.path.dirname(self.get_setting("par.innovus.innovus_bin").replace("INNOVUS", "GENUS"))}' + if self.get_setting("par.innovus.signoff"): # add path to Tempus + v["PATH"] = v["PATH"] + f':{self.get_setting("cadence.cadence_home")}/SSV/SSV{self.get_setting("par.innovus.version")}/bin' return v @property @@ -223,10 +225,18 @@ def steps(self) -> List[HammerToolStep]: self.place_pins, self.place_opt_design, self.clock_tree, - self.add_fillers, - self.route_design, - self.opt_design + self.add_fillers ] + if self.version() >= self.version_number("231"): + # Route & post-route opt are now combined + steps += [self.route_opt_design] + else: + steps += [ + self.route_design, + self.opt_design + ] + if self.get_setting("par.innovus.signoff"): + steps += [self.opt_signoff] write_design_step = [ self.write_regs, self.write_design @@ -392,6 +402,10 @@ def place_bumps(self) -> bool: lly = "[get_db bump:Bump_{x}.{y} .bbox.ll.y]".format(x=bump.x, y=bump.y), urx = "[get_db bump:Bump_{x}.{y} .bbox.ur.x]".format(x=bump.x, y=bump.y), ury = "[get_db bump:Bump_{x}.{y} .bbox.ur.y]".format(x=bump.x, y=bump.y))) + + # Use early global bump router + if self.version() >= self.version_number("231") and self.get_setting("par.innovus.early_route_bumps"): + self.append("set_db route_early_global_route_bump_nets true") return True def place_tap_cells(self) -> bool: @@ -528,11 +542,24 @@ def power_straps(self) -> bool: def place_opt_design(self) -> bool: """Place the design and do pre-routing optimization.""" + # First, check that no ports are left unplaced. Exit with error code for user to fix. + self.append(''' + set unplaced_pins [get_db ports -if {.place_status == unplaced}] + if {$unplaced_pins ne ""} { + print_message -error "Some pins remain unplaced, which will cause invalid placement and routing. These are the unplaced pins: $unplaced_pins" + exit 2 + } + ''', clean=True) if self.hierarchical_mode.is_nonleaf_hierarchical(): self.verbose_append(''' flatten_ilm update_constraint_mode -name my_constraint_mode -ilm_sdc_files {sdc} '''.format(sdc=self.post_synth_sdc), clean=True) + + # Use place_opt_design V2 (POD-Turbo). Option must be explicitly set only in 22.1. + if self.version() >= self.version_number("221") and self.version() < self.version_number("231"): + self.verbose_append("set_db opt_enable_podv2_clock_opt_flow true") + self.verbose_append("place_opt_design") return True @@ -554,11 +581,18 @@ def clock_tree(self) -> bool: if len(logic_cells) > 0: self.append(f"set_db cts_logic_cells {{{' '.join(logic_cells[0].name)}}}") self.verbose_append("create_clock_tree_spec") - if bool(self.get_setting("par.innovus.use_cco")): - # -hold is a secret flag for ccopt_design (undocumented anywhere) - self.verbose_append("ccopt_design -hold -report_dir hammer_cts_debug -report_prefix hammer_cts") + if self.version() >= self.version_number("221"): + # Required for place_opt_design v2 (POD-Turbo) + if bool(self.get_setting("par.innovus.use_cco")): + self.verbose_append("clock_opt_design -hold -timing_debug_report") + else: + self.verbose_append("clock_opt_design -cts -timing_debug_report") else: - self.verbose_append("clock_design") + if bool(self.get_setting("par.innovus.use_cco")): + # -hold is a secret flag for ccopt_design (undocumented anywhere) + self.verbose_append("ccopt_design -hold -timing_debug_report") + else: + self.verbose_append("clock_design") if self.hierarchical_mode.is_nonleaf_hierarchical(): self.verbose_append("unflatten_ilm") return True @@ -641,12 +675,93 @@ def route_design(self) -> bool: self.verbose_append("route_design") return True + def opt_settings(self) -> str: + cmds = [] + # Enable auto hold recovery if slack degrades + cmds.append("set_db opt_post_route_hold_recovery auto") + # Fix fanout load violations + cmds.append("set_db opt_fix_fanout_load true") + # Fix SI-induced slew violations (glitch fixing enabled by default) + cmds.append("set_db opt_post_route_fix_si_transitions true") + # Report reasons for not fixing hold + cmds.append("set_db opt_verbose true") + # Report reasons for not fixing DRVs + cmds.append("set_db opt_detail_drv_failure_reason true") + if self.version() >= self.version_number("221"): # critical region resynthesis + # Report failure reasons + cmds.append("set_db opt_sequential_genus_restructure_report_failure_reason true") + return "\n".join(cmds) + def opt_design(self) -> bool: """ Post-route optimization and fix setup & hold time violations. -expanded_views creates timing reports for each MMMC view. """ - self.verbose_append("opt_design -post_route -setup -hold -expanded_views") + self.append(self.opt_settings()) + self.verbose_append("opt_design -post_route -setup -hold -expanded_views -timing_debug_report") + if self.hierarchical_mode.is_nonleaf_hierarchical(): + self.verbose_append("unflatten_ilm") + return True + + def route_opt_design(self) -> bool: + """ + Routing and post-route optimization. New flow as default in 23.1. + Performs more optimization during the routing stage. + Fixes setup, hold, and DRV violations. + """ + if self.hierarchical_mode.is_nonleaf_hierarchical(): + self.verbose_append("flatten_ilm") + + # Allow express design effort to complete running. + # By default, route_design will abort in express mode with + # "WARNING (NRIG-142) Express flow by default will not run routing". + self.verbose_append("set_db design_express_route true") + + self.append(self.opt_settings()) + + self.verbose_append("route_opt_design -timing_debug_report") + + if self.hierarchical_mode.is_nonleaf_hierarchical(): + self.verbose_append("unflatten_ilm") + return True + + def opt_signoff(self) -> bool: + """ + Signoff timing and optimization. + Should only be run after all implementation flows to obtain better timing, + setup/hold fixing, DRV fixing, and power optimization. + Note: runs Tempus in batch mode. + """ + if self.hierarchical_mode.is_nonleaf_hierarchical(): + self.verbose_append("flatten_ilm") + + # Options + # Set clock fixing cell list + regexp = "" + buffer_cells = self.technology.get_special_cell_by_type(CellType.CTSBuffer) + if len(buffer_cells) > 0: + regexp += "|".join(buffer_cells[0].name) + "|" + inverter_cells = self.technology.get_special_cell_by_type(CellType.CTSInverter) + if len(inverter_cells) > 0: + regexp += "|".join(inverter_cells[0].name) + "|" + gate_cells = self.technology.get_special_cell_by_type(CellType.CTSGate) + if len(gate_cells) > 0: + regexp += "|".join(gate_cells[0].name) + "|" + logic_cells = self.technology.get_special_cell_by_type(CellType.CTSLogic) + if len(logic_cells) > 0: + regexp += "|".join(logic_cells[0].name) + self.verbose_append(f"set_db opt_signoff_clock_cell_list [lsearch -regexp -all -inline [get_db lib_cells .base_name] {regexp}]") + # Fix DRVs + self.verbose_append("set_db opt_signoff_fix_data_drv true") + self.verbose_append("set_db opt_signoff_fix_clock_drv true") + + # Signoff timing requires remote host setting + self.verbose_append(f"set_multi_cpu_usage -remote_host 1 -cpu_per_remote_host {self.get_setting('vlsi.core.max_threads')}") + self.verbose_append("time_design_signoff") + + # Run + self.verbose_append("opt_signoff -all") + if self.hierarchical_mode.is_nonleaf_hierarchical(): self.verbose_append("unflatten_ilm") return True @@ -746,7 +861,24 @@ def write_sdf(self) -> bool: self.verbose_append("flatten_ilm") # Output the Standard Delay Format File for use in timing annotated gate level simulations - self.verbose_append("write_sdf {run_dir}/{top}.par.sdf".format(run_dir=self.run_dir, top=self.top_module)) + corners = self.get_mmmc_corners() + if corners: + # Derive flags for min::type::max + max_view = next((c for c in corners if c.type is MMMCCornerType.Setup), None) + max_view_flag = "" + if max_view is not None: + max_view_flag = f"-max_view {max_view.name}.setup_view" + min_view = next((c for c in corners if c.type is MMMCCornerType.Hold), None) + min_view_flag = "" + if min_view is not None: + min_view_flag = f"-min_view {min_view.name}.hold_view" + typ_view = next((c for c in corners if c.type is MMMCCornerType.Extra), None) + typ_view_flag = "" + if typ_view is not None: + typ_view_flag = f"-typical_view {typ_view.name}.extra_view" + self.verbose_append(f"write_sdf {max_view_flag} {min_view_flag} {typ_view_flag} {self.run_dir}/{self.top_module}.par.sdf") + else: + self.verbose_append(f"write_sdf {self.run_dir}/{self.top_module}.par.sdf".format(run_dir=self.run_dir, top=self.top_module)) return True @@ -804,6 +936,10 @@ def write_regs(self) -> bool: return True def write_design(self) -> bool: + # Because implementation is done, enable report_timing -early/late and SDF writing + # without recalculating timing graph for each analysis view + self.append("set_db timing_enable_simultaneous_setup_hold_mode true") + # Save the Innovus design. self.verbose_append("write_db {lib_name} -def -verilog".format( lib_name=self.output_innovus_lib_name diff --git a/hammer/par/innovus/defaults.yml b/hammer/par/innovus/defaults.yml index 22c59461e..c85f931a2 100644 --- a/hammer/par/innovus/defaults.yml +++ b/hammer/par/innovus/defaults.yml @@ -35,3 +35,13 @@ par.innovus: # Note that this requires an optional licence (enccco). # type: bool use_cco: true + + # Route bumps with early global router (version >= 23.1 only). + # By default, bumps are ignored for routing. If true, turn on early bump routing. + # This is useful for RC delay estimation if you don't have IO cells routed to bumps via the flip-chip router. + early_route_bumps: false + + # Perform signoff timing, then timing/DRV/power optimization with opt_signoff. + # Should only be run at the very end, as runtime impact is significant. + # Requires the same version of Tempus to be installed at ${cadence.cadence_home}/SSV/SSV${par.innovus.bin}/bin + signoff: false From 60234dc2b793676ac9a6791d2be6da55610d96f5 Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Sun, 18 Feb 2024 19:17:37 -0800 Subject: [PATCH 2/8] regexp may match anything if any cell types were empty (trailing |) --- hammer/par/innovus/__init__.py | 36 +++++++++++++--------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/hammer/par/innovus/__init__.py b/hammer/par/innovus/__init__.py index e62baa118..5d61949bc 100644 --- a/hammer/par/innovus/__init__.py +++ b/hammer/par/innovus/__init__.py @@ -4,6 +4,7 @@ import shutil from typing import List, Dict, Optional, Callable, Tuple, Any, cast +from itertools import chain import os import errno @@ -437,7 +438,7 @@ def place_pins(self) -> bool: return False power_pin_layers = self.get_setting("par.generate_power_straps_options.by_tracks.pin_layers") - + const = cast(PlacementConstraint, topconst) assert isinstance(const.margins, Margins), "Margins must be defined for the top level" fp_llx = const.margins.left @@ -500,10 +501,10 @@ def place_pins(self) -> bool: assign_arg = "-assign {{ {x} {y} }}".format(x=pin.location[0], y=pin.location[1]) layers_arg = "" - + if set(pin.layers or []).intersection(set(power_pin_layers)): self.logger.error("Signal pins will be generated on the same layer(s) as power pins. Double-check to see if intended.") - + if pin.layers is not None and len(pin.layers) > 0: layers_arg = "-layer {{ {} }}".format(" ".join(pin.layers)) @@ -737,19 +738,10 @@ def opt_signoff(self) -> bool: # Options # Set clock fixing cell list - regexp = "" - buffer_cells = self.technology.get_special_cell_by_type(CellType.CTSBuffer) - if len(buffer_cells) > 0: - regexp += "|".join(buffer_cells[0].name) + "|" - inverter_cells = self.technology.get_special_cell_by_type(CellType.CTSInverter) - if len(inverter_cells) > 0: - regexp += "|".join(inverter_cells[0].name) + "|" - gate_cells = self.technology.get_special_cell_by_type(CellType.CTSGate) - if len(gate_cells) > 0: - regexp += "|".join(gate_cells[0].name) + "|" - logic_cells = self.technology.get_special_cell_by_type(CellType.CTSLogic) - if len(logic_cells) > 0: - regexp += "|".join(logic_cells[0].name) + cell_types = [CellType.CTSBuffer, CellType.CTSInverter, CellType.CTSGate, CellType.CTSLogic] + cells_by_type = chain(*map(lambda c: self.technology.get_special_cell_by_type(c), cell_types)) + flat_cells = chain(*map(lambda cells: cells.name, cells_by_type)) + regexp = "|".join(flat_cells) self.verbose_append(f"set_db opt_signoff_clock_cell_list [lsearch -regexp -all -inline [get_db lib_cells .base_name] {regexp}]") # Fix DRVs self.verbose_append("set_db opt_signoff_fix_data_drv true") @@ -1209,20 +1201,20 @@ def generate_floorplan_tcl(self) -> List[str]: if current_top_layer is not None: bot_layer = self.get_stackup().get_metal_by_index(1).name cover_layers = list(map(lambda m: m.name, self.get_stackup().get_metals_below_layer(current_top_layer))) - output.append("create_route_halo -bottom_layer {b} -space {s} -top_layer {t} -inst {inst}".format( + output.append("create_route_halo -bottom_layer {b} -space {s} -top_layer {t} -inst {inst}".format( inst=new_path, b=bot_layer, t=current_top_layer, s=spacing)) - + if(self.get_setting("par.power_to_route_blockage_ratio") < 1): self.logger.warning("The power strap blockage region is smaller than the routing halo region for hard macros. Double-check if this is intended.") - - place_push_out = round(spacing*self.get_setting("par.power_to_route_blockage_ratio") , 1) # Push the place halo, and therefore PG blockage, further out from route halo so router is aware of straps before entering final routing. - output.append("create_place_halo -insts {inst} -halo_deltas {{{s} {s} {s} {s}}} -snap_to_site".format( + place_push_out = round(spacing*self.get_setting("par.power_to_route_blockage_ratio") , 1) # Push the place halo, and therefore PG blockage, further out from route halo so router is aware of straps before entering final routing. + + output.append("create_place_halo -insts {inst} -halo_deltas {{{s} {s} {s} {s}}} -snap_to_site".format( inst=new_path, s=place_push_out)) output.append("set pg_blockage_shape [get_db [get_db hinsts {inst}][get_db insts {inst}] .place_halo_polygon]".format( inst=new_path)) output.append("create_route_blockage -pg_nets -layers {{{layers}}} -polygon $pg_blockage_shape".format(layers=" ".join(cover_layers))) - + elif constraint.type == PlacementConstraintType.Obstruction: obs_types = get_or_else(constraint.obs_types, []) # type: List[ObstructionType] assert '/' not in new_path, "'obstruction' placement constraints must be provided a path directly under the top level" From 334c315e971cb60bedc09f2e9a3c2740903004ed Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Thu, 29 Feb 2024 14:47:32 -0800 Subject: [PATCH 3/8] update versions, set power analysis views, fix add_tieoffs --- hammer/common/cadence/__init__.py | 9 +++++++-- hammer/par/innovus/defaults.yml | 2 +- hammer/power/joules/defaults.yml | 2 +- hammer/synthesis/genus/__init__.py | 13 +++++++------ hammer/synthesis/genus/defaults.yml | 2 +- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/hammer/common/cadence/__init__.py b/hammer/common/cadence/__init__.py index 4d0702f7a..1f16069f4 100644 --- a/hammer/common/cadence/__init__.py +++ b/hammer/common/cadence/__init__.py @@ -207,10 +207,15 @@ def append_mmmc(cmd: str) -> None: # Finally, apply the analysis view. # TODO: should not need to analyze extra views as well. Defaulting to hold for now (min. runtime impact). - append_mmmc("set_analysis_view -setup {{ {setup_views} }} -hold {{ {hold_views} {extra_views} }}".format( + # First extra view is assumed to be for dynamic and leakage power calculation. + power_opts = "" + if len(extra_view_names) > 0: + power_opts = f"-dynamic {extra_view_names[0]} -leakage {extra_view_names[0]}" + append_mmmc("set_analysis_view -setup {{ {setup_views} }} -hold {{ {hold_views} {extra_views} }} {power}".format( setup_views=" ".join(setup_view_names), hold_views=" ".join(hold_view_names), - extra_views=" ".join(extra_view_names) + extra_views=" ".join(extra_view_names), + power=power_opts )) else: # First, create an Innovus library set. diff --git a/hammer/par/innovus/defaults.yml b/hammer/par/innovus/defaults.yml index c85f931a2..3e753d321 100644 --- a/hammer/par/innovus/defaults.yml +++ b/hammer/par/innovus/defaults.yml @@ -8,7 +8,7 @@ par.innovus: # Innovus version to use. # Used to locate the binary - e.g. the '221' in ${cadence.cadence_home}/DDI/DDI221/INNOVUS221/bin/innovus - version: "221" + version: "231" # Design flow effort. # Valid options: express (fastest), standard, and extreme (slowest). diff --git a/hammer/power/joules/defaults.yml b/hammer/power/joules/defaults.yml index 1feb6fef5..d9eda2326 100644 --- a/hammer/power/joules/defaults.yml +++ b/hammer/power/joules/defaults.yml @@ -8,4 +8,4 @@ power.joules: # Joules version to use. # Used to locate the binary - e.g. the '221' in ${cadence.cadence_home}/DDI/DDI221/JLS221/bin/joules - version: "221" + version: "231" diff --git a/hammer/synthesis/genus/__init__.py b/hammer/synthesis/genus/__init__.py index 5a658e690..64d745626 100644 --- a/hammer/synthesis/genus/__init__.py +++ b/hammer/synthesis/genus/__init__.py @@ -357,12 +357,13 @@ def add_tieoffs(self) -> bool: self.verbose_append("set_db message:WSDF-201 .max_print 20") self.verbose_append("set_db use_tiehilo_for_const duplicate") - # If there is more than 1 corner or a certain type, use lib cells for only the active analysis view - corner_counts = Counter(list(map(lambda c: c.type, self.get_mmmc_corners()))) - if any(cnt>1 for cnt in corner_counts.values()): - self.verbose_append("set ACTIVE_VIEW [string map { .setup_view {} .hold_view {} .extra_view {} } [get_db analysis_view:[get_analysis_views] .name]]") - self.verbose_append("set HI_TIEOFF [get_db base_cell:{TIE_HI_CELL} .lib_cells -if {{ .library.default_opcond == $ACTIVE_VIEW }}]".format(TIE_HI_CELL=tie_hi_cell)) - self.verbose_append("set LO_TIEOFF [get_db base_cell:{TIE_LO_CELL} .lib_cells -if {{ .library.default_opcond == $ACTIVE_VIEW }}]".format(TIE_LO_CELL=tie_lo_cell)) + # If MMMC corners specified, get the single lib cell for the active analysis view + # Else, Genus will complain that multiple objects match for the cell name + corners = self.get_mmmc_corners() + if corners: + self.verbose_append("set ACTIVE_SET [string map { .setup_view .setup_set .hold_view .hold_set .extra_view .extra_set } [get_db [get_analysis_views] .name]]") + self.verbose_append("set HI_TIEOFF [get_db base_cell:{TIE_HI_CELL} .lib_cells -if {{ .library.library_set.name == $ACTIVE_SET }}]".format(TIE_HI_CELL=tie_hi_cell)) + self.verbose_append("set LO_TIEOFF [get_db base_cell:{TIE_LO_CELL} .lib_cells -if {{ .library.library_set.name == $ACTIVE_SET }}]".format(TIE_LO_CELL=tie_lo_cell)) self.verbose_append("add_tieoffs -high $HI_TIEOFF -low $LO_TIEOFF -max_fanout 1 -verbose") else: self.verbose_append("add_tieoffs -high {HI_TIEOFF} -low {LO_TIEOFF} -max_fanout 1 -verbose".format(HI_TIEOFF=tie_hi_cell, LO_TIEOFF=tie_lo_cell)) diff --git a/hammer/synthesis/genus/defaults.yml b/hammer/synthesis/genus/defaults.yml index a8c79b846..fee8294b9 100644 --- a/hammer/synthesis/genus/defaults.yml +++ b/hammer/synthesis/genus/defaults.yml @@ -6,7 +6,7 @@ synthesis.genus: # Genus version to use. # Used to locate the binary - e.g. the '221' in ${cadence.cadence_home}/DDI/DDI221/GENUS221/bin/genus - version: "221" + version: "231" # Generate the TCL file but do not run it yet. generate_only: false From 58ae5190265867611486af09567f256e219fd74c Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Thu, 29 Feb 2024 15:51:23 -0800 Subject: [PATCH 4/8] report timing constraint issues after init-ing design --- hammer/synthesis/genus/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hammer/synthesis/genus/__init__.py b/hammer/synthesis/genus/__init__.py index 64d745626..a167f08b7 100644 --- a/hammer/synthesis/genus/__init__.py +++ b/hammer/synthesis/genus/__init__.py @@ -250,6 +250,9 @@ def init_environment(self) -> bool: verbose_append("set_db module:{top}/{mod} .preserve true".format(top=self.top_module, mod=ilm.module)) verbose_append("init_design -top {}".format(self.top_module)) + # Report timing constraint issues + verbose_append("report_timing -lint -verbose") + # Setup power settings from cpf/upf # Difference from other tools: apply_power_intent after read power_cmds = self.generate_power_spec_commands() From d0cb266b583910ab0313d5a77eb4daa3c185f842 Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Fri, 1 Mar 2024 18:27:04 -0800 Subject: [PATCH 5/8] add soft & hard placement constraint types - maps to guide & region, respectively --- hammer/config/defaults.yml | 3 ++- hammer/par/innovus/__init__.py | 15 ++++++++++----- hammer/par/openroad/__init__.py | 2 +- hammer/vlsi/constraints.py | 19 +++++++++++-------- tests/test_constraints.py | 8 +++++++- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/hammer/config/defaults.yml b/hammer/config/defaults.yml index 2578ee6df..1adabf4c0 100644 --- a/hammer/config/defaults.yml +++ b/hammer/config/defaults.yml @@ -221,7 +221,8 @@ vlsi.inputs: # - Required for all types # - One of the following: # - "dummy" (does nothing with this constraint) - # - "placement" (creates a placement constraint for an instance) + # - "placement" or "soft_placement" (creates a soft placement constraint for a hierarchical instance) + # - "hard_placement" (creates a hard placement constraint for a hierarchical instance) # - "toplevel" (top-level chip dimensions; may only occur once, for the top-level module) # - "hardmacro" (places this hard macro at a particular spot) # - "hierarchical" (marks this instance as part of hierarchical place and route) diff --git a/hammer/par/innovus/__init__.py b/hammer/par/innovus/__init__.py index 5d61949bc..d63dacd80 100644 --- a/hammer/par/innovus/__init__.py +++ b/hammer/par/innovus/__init__.py @@ -1118,9 +1118,6 @@ def generate_floorplan_tcl(self) -> List[str]: """ output = [] # type: List[str] - # TODO(edwardw): proper source locators/SourceInfo - output.append("# Floorplan automatically generated from HAMMER") - # Top-level chip size constraint. # Default/fallback constraints if no other constraints are provided. # TODO snap this to a core site @@ -1167,8 +1164,16 @@ def generate_floorplan_tcl(self) -> List[str]: )) if constraint.type == PlacementConstraintType.Dummy: pass - elif constraint.type == PlacementConstraintType.Placement: - output.append("create_guide -name {name} -area {x1} {y1} {x2} {y2}".format( + elif constraint.type == PlacementConstraintType.SoftPlacement: + output.append("create_boundary_constraint -type guide -hinst {name} -rects {{ {x1} {y1} {x2} {y2} }}".format( + name=new_path, + x1=constraint.x, + x2=constraint.x + constraint.width, + y1=constraint.y, + y2=constraint.y + constraint.height + )) + elif constraint.type == PlacementConstraintType.HardPlacement: + output.append("create_boundary_constraint -type region -hinst {name} -rects {{ {x1} {y1} {x2} {y2} }}".format( name=new_path, x1=constraint.x, x2=constraint.x + constraint.width, diff --git a/hammer/par/openroad/__init__.py b/hammer/par/openroad/__init__.py index afd90b89c..dfe3085c4 100644 --- a/hammer/par/openroad/__init__.py +++ b/hammer/par/openroad/__init__.py @@ -1814,7 +1814,7 @@ def generate_floorplan_tcl(self) -> List[str]: pass if constraint.type == PlacementConstraintType.Dummy: pass - elif constraint.type == PlacementConstraintType.Placement: + elif constraint.type in [PlacementConstraintType.SoftPlacement, PlacementConstraintType.HardPlacement]: pass # for OpenROAD elif constraint.type in [PlacementConstraintType.HardMacro, PlacementConstraintType.Hierarchical]: diff --git a/hammer/vlsi/constraints.py b/hammer/vlsi/constraints.py index 87830178d..e35cdd524 100644 --- a/hammer/vlsi/constraints.py +++ b/hammer/vlsi/constraints.py @@ -530,18 +530,21 @@ def __str__(self) -> str: class PlacementConstraintType(Enum): Dummy = 1 - Placement = 2 - TopLevel = 3 - HardMacro = 4 - Hierarchical = 5 - Obstruction = 6 - Overlap = 7 + SoftPlacement = 2 + HardPlacement = 3 + TopLevel = 4 + HardMacro = 5 + Hierarchical = 6 + Obstruction = 7 + Overlap = 8 @classmethod def __mapping(cls) -> Dict[str, "PlacementConstraintType"]: return { "dummy": PlacementConstraintType.Dummy, - "placement": PlacementConstraintType.Placement, + "placement": PlacementConstraintType.SoftPlacement, + "soft_placement": PlacementConstraintType.SoftPlacement, + "hard_placement": PlacementConstraingType.HardPlacement, "toplevel": PlacementConstraintType.TopLevel, "hardmacro": PlacementConstraintType.HardMacro, "hierarchical": PlacementConstraintType.Hierarchical, @@ -781,7 +784,7 @@ def from_dict(constraint: dict) -> "PlacementConstraint": assert isinstance(create_physical, bool) ### Width & height ### - # These fields are mandatory for Hierarchical, Dummy, Placement, TopLevel, and Obstruction constraints + # These fields are mandatory for Hierarchical, Dummy, Soft/HardPlacement, TopLevel, and Obstruction constraints # These fields are optional for HardMacro and Overlap constraints # TODO(ucb-bar/hammer#414) make them mandatory for HardMacro and Overlap once there's a more robust way of automatically getting that data into hammer # This is not None because we don't want to make width optional for the reason above diff --git a/tests/test_constraints.py b/tests/test_constraints.py index f538f8c7a..3e82268ea 100644 --- a/tests/test_constraints.py +++ b/tests/test_constraints.py @@ -621,7 +621,13 @@ def test_placement(self) -> None: "height": Decimal(20), "orientation": "r0"} tc = PlacementConstraint.from_dict(d) - assert tc.type == PlacementConstraintType.Placement + assert tc.type == PlacementConstraintType.SoftPlacement + d["type"] = "soft_placement" + tc = PlacementConstraint.from_dict(d) + assert tc.type == PlacementConstraintType.SoftPlacement + d["type"] = "hard_placement" + tc = PlacementConstraint.from_dict(d) + assert tc.type == PlacementConstraintType.HardPlacement assert tc.path == "path/to/placement" assert tc.x == Decimal(4) assert tc.y == Decimal(6) From 22739fb4b388ed3988e1173ef47a4af976f891a4 Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Fri, 1 Mar 2024 22:44:08 -0800 Subject: [PATCH 6/8] typo --- hammer/vlsi/constraints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hammer/vlsi/constraints.py b/hammer/vlsi/constraints.py index e35cdd524..e0c075e9a 100644 --- a/hammer/vlsi/constraints.py +++ b/hammer/vlsi/constraints.py @@ -544,7 +544,7 @@ def __mapping(cls) -> Dict[str, "PlacementConstraintType"]: "dummy": PlacementConstraintType.Dummy, "placement": PlacementConstraintType.SoftPlacement, "soft_placement": PlacementConstraintType.SoftPlacement, - "hard_placement": PlacementConstraingType.HardPlacement, + "hard_placement": PlacementConstraintType.HardPlacement, "toplevel": PlacementConstraintType.TopLevel, "hardmacro": PlacementConstraintType.HardMacro, "hierarchical": PlacementConstraintType.Hierarchical, From a43fe67494bbbf23a1b4b7989b173f876ef5ec0b Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Fri, 1 Mar 2024 22:46:20 -0800 Subject: [PATCH 7/8] fix viz --- hammer/vlsi/hammer_tool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hammer/vlsi/hammer_tool.py b/hammer/vlsi/hammer_tool.py index b3f808802..46136f105 100644 --- a/hammer/vlsi/hammer_tool.py +++ b/hammer/vlsi/hammer_tool.py @@ -1268,7 +1268,7 @@ def get_macro_wh(macro: Optional[str]) -> Tuple[Decimal, Decimal]: if viz_mode in ["all", "floorplan"]: macro_rects = macro_text = obs_rects = obs_text = orient_lines = '\n'.format(translate) for c in fp_consts: - if c.type in [PlacementConstraintType.Placement, PlacementConstraintType.HardMacro, PlacementConstraintType.Hierarchical]: + if c.type in [PlacementConstraintType.SoftPlacement, PlacementConstraintType.HardPlacement, PlacementConstraintType.HardMacro, PlacementConstraintType.Hierarchical]: # macros & hierarchical not required to have width/height, will resolve to 0 (width, height) = (c.width, c.height) if width == 0 or height == 0: From 7b681c994bb32db6b6c93ccea62ea0437f0726fe Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Wed, 6 Mar 2024 09:11:56 -0800 Subject: [PATCH 8/8] move fanout fixing earlier --- hammer/par/innovus/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hammer/par/innovus/__init__.py b/hammer/par/innovus/__init__.py index d63dacd80..3f95f8663 100644 --- a/hammer/par/innovus/__init__.py +++ b/hammer/par/innovus/__init__.py @@ -567,6 +567,8 @@ def place_opt_design(self) -> bool: def clock_tree(self) -> bool: """Setup and route a clock tree for clock nets.""" if len(self.get_clock_ports()) > 0: + # Fix fanout load violations + self.verbose_append("set_db opt_fix_fanout_load true") # Ignore clock tree when there are no clocks # If special cells are specified, explicitly set them instead of letting tool infer from libs buffer_cells = self.technology.get_special_cell_by_type(CellType.CTSBuffer) @@ -680,8 +682,6 @@ def opt_settings(self) -> str: cmds = [] # Enable auto hold recovery if slack degrades cmds.append("set_db opt_post_route_hold_recovery auto") - # Fix fanout load violations - cmds.append("set_db opt_fix_fanout_load true") # Fix SI-induced slew violations (glitch fixing enabled by default) cmds.append("set_db opt_post_route_fix_si_transitions true") # Report reasons for not fixing hold