From 048be5d2dd9fbeb7e4efa3d468211f4c2b61bf6a Mon Sep 17 00:00:00 2001 From: kenhoberkeley <89747404+kenhoberkeley@users.noreply.github.com> Date: Mon, 12 Feb 2024 10:12:21 -0800 Subject: [PATCH] Relocate CPF Read + Power Strap Issues (#841) * Move the power intent due to flattening difficulties * Add PG Net blockages that are equivalent in shape to place halos * pull back slightly from edge * clean up naming * typo * A slightly better fix * clean up comment * fixes * give the ratio a key * fix minor typo --------- Co-authored-by: ken_ho --- hammer/config/defaults.yml | 2 ++ hammer/config/defaults_types.yml | 3 +++ hammer/par/innovus/__init__.py | 34 ++++++++++++++++++++++---------- hammer/tech/stackup.py | 13 ++++++++++++ 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/hammer/config/defaults.yml b/hammer/config/defaults.yml index a845fc103..2578ee6df 100644 --- a/hammer/config/defaults.yml +++ b/hammer/config/defaults.yml @@ -565,6 +565,8 @@ par: # Used by power straps and floorplanning # type: Decimal + power_to_route_blockage_ratio: 1.1 # By default, set the power halo + blockage spacing to 1.1x in every dimension relative to route blockage. + blockage_spacing_top_layer: null # Top metal layer that is pulled back around blocks and hardmacros # Used by floorplanning. # Overridden by individual placement constraints on top_layer. diff --git a/hammer/config/defaults_types.yml b/hammer/config/defaults_types.yml index fa9f41d6a..264784761 100644 --- a/hammer/config/defaults_types.yml +++ b/hammer/config/defaults_types.yml @@ -280,6 +280,9 @@ par: # Spacing around blocks and hardmacros in microns blockage_spacing: float + # Ratio between the power blockage + place halo relative to route blockage. Route blockage should be smaller. + power_to_route_blockage_ratio: float + # Top metal layer that is pulled back around blocks and hardmacros # type: Optional[str] blockage_spacing_top_layer: Optional[str] diff --git a/hammer/par/innovus/__init__.py b/hammer/par/innovus/__init__.py index dc4e61892..ad442a7da 100644 --- a/hammer/par/innovus/__init__.py +++ b/hammer/par/innovus/__init__.py @@ -309,10 +309,6 @@ def init_design(self) -> bool: # Run init_design to validate data and start the Cadence place-and-route workflow. verbose_append("init_design") - # Setup power settings from cpf/upf - for l in self.generate_power_spec_commands(): - verbose_append(l) - # Set the top and bottom global/detail routing layers. # This must happen after the tech LEF is loaded layers = self.get_setting("vlsi.technology.routing_layers") @@ -342,6 +338,11 @@ def floorplan_design(self) -> bool: # (after ILMs are placed) if self.hierarchical_mode.is_nonleaf_hierarchical(): self.verbose_append("flatten_ilm") + + # Setup power settings from cpf/upf + for l in self.generate_power_spec_commands(): + self.verbose_append(l) + for l in self.generate_dont_use_commands(): self.append(l) if self.hierarchical_mode.is_nonleaf_hierarchical(): @@ -421,6 +422,8 @@ def place_pins(self) -> bool: self.logger.fatal("Cannot find top-level constraints to place pins") 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 @@ -483,6 +486,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)) @@ -1066,13 +1073,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_place_halo -insts {inst} -halo_deltas {{{s} {s} {s} {s}}} -snap_to_site".format( - inst=new_path, s=spacing)) - 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)) - output.append("create_route_blockage -pg_nets -inst {inst} -layers {{{layers}}} -cover".format( - inst=new_path, layers=" ".join(cover_layers))) - + + 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( + 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" diff --git a/hammer/tech/stackup.py b/hammer/tech/stackup.py index a3eafb09a..e8b81a596 100644 --- a/hammer/tech/stackup.py +++ b/hammer/tech/stackup.py @@ -406,6 +406,19 @@ def get_metals_below_layer(self, name: str) -> List[Metal]: except StopIteration: raise ValueError("Metal named %s is not defined in stackup %s" % (name, self.name)) + def get_metals_incl_layer(self, name: str) -> List[Metal]: + """ + Get all the metals including the specified metal layer. + + :param index: Index of the metal layer + :return: A list of metal layer objects + """ + try: + index = next(m.index for m in self.metals if m.name == name) + return list(filter(lambda m: m.index in range(1, index+1), self.metals)) + except StopIteration: + raise ValueError("Metal named %s is not defined in stackup %s" % (name, self.name)) + def get_metal_by_index(self, index: int) -> Metal: """ Get a given metal layer by index.