From 5166a65e5dfb938e6017192ecc34e66e5ac6570c Mon Sep 17 00:00:00 2001 From: Jerry Zhao Date: Sun, 5 Nov 2023 14:13:38 -0800 Subject: [PATCH] Improvements to supplies API (#816) * Change supply.pin: str to supply.pins: List[str] This enables generation of CPFs where a single supply net can be global_connect'd to many pins * Add power_straps.by_tracks.power_nets API This setting, if set, allows precise control over which supplies in a design the auto-strap generation should generate stripes for. Previously, it would generate straps for all supplies * Add supplies.voltage to override the vlsi.inputs.supplies.VDD Allow per-supply voltage specification. If unset, defaults to vlsi.inputs.supplies.VDD (old behavior) * Change supply.pins to a Option[List[str]] | fix upf gen for supplies --- hammer/config/defaults.yml | 9 +++-- hammer/config/defaults_types.yml | 8 +++- hammer/technology/sky130/defaults.yml | 16 ++++---- hammer/vlsi/constraints.py | 5 ++- hammer/vlsi/hammer_tool.py | 9 +++-- hammer/vlsi/hammer_vlsi_impl.py | 56 +++++++++++++++++---------- tests/test_power_straps.py | 8 ++-- 7 files changed, 69 insertions(+), 42 deletions(-) diff --git a/hammer/config/defaults.yml b/hammer/config/defaults.yml index 7ad50b127..b5776ac54 100644 --- a/hammer/config/defaults.yml +++ b/hammer/config/defaults.yml @@ -113,13 +113,13 @@ vlsi.inputs: # Supply voltages and names # Supply Struct: # name (str) - The name of your supply's net - # pin (Optional[str]) - The cell pin that this net should drive, no pins are connected when omitted + # pins (Optional[List[str]]) - The cell pin/s that this net should drive. If None, pin name is assumed to be net name. If empty, no pins are connected # tie (Optional[str]) - Another supply this supply is tied to. Nets with this set will not end up in your final layout. # weight (Optional[int]) - The weight of this supply when building power straps (larger number = more straps). # Not used for grounds. Mandatory, but defaults to 1 if omitted. supplies: - power: [{name: "VDD", pin: "VDD"}] # power (List(Supply)): A list of of all power net(s) in the design - ground: [{name: "VSS", pin: "VSS"}] # ground (List(Supply)): A list of all ground net(s) in the design + power: [{name: "VDD", pins: ["VDD"]}] # power (List(Supply)): A list of of all power net(s) in the design + ground: [{name: "VSS", pins: ["VSS"]}] # ground (List(Supply)): A list of all ground net(s) in the design VDD: "0.85 V" # VDD (str): The default voltage of the primary power net GND: "0 V" # GND (str): The default voltage of the primary ground net @@ -621,6 +621,9 @@ par: # Otherwise should be set to some layer name # type: str + power_nets: [] # List of power nets to generate straps for. If empty, generates straps for all nets in vlsi.inputs.supplies + # type: List[str] + # DRC settings drc.inputs: # DRC settings diff --git a/hammer/config/defaults_types.yml b/hammer/config/defaults_types.yml index d7227e78d..fa9f41d6a 100644 --- a/hammer/config/defaults_types.yml +++ b/hammer/config/defaults_types.yml @@ -82,8 +82,8 @@ vlsi.technology: vlsi.inputs: # Supply voltages and names supplies: - power: list[dict[str, str]] - ground: list[dict[str, str]] + power: list[dict[str, Any]] + ground: list[dict[str, Any]] VDD: str GND: str @@ -334,6 +334,10 @@ par: # type: str bottom_via_layer: str + # Indicates which power nets straps should be generated for + # type: list[str] + power_nets: list[str] + # DRC settings drc.inputs: # Top RTL module. diff --git a/hammer/technology/sky130/defaults.yml b/hammer/technology/sky130/defaults.yml index 48068af66..59bff2f12 100644 --- a/hammer/technology/sky130/defaults.yml +++ b/hammer/technology/sky130/defaults.yml @@ -53,14 +53,14 @@ vlsi: inputs: supplies: # Supply voltages. # TODO: add ability to tie pin to net in Hammer Innovus plugin - power: [ {name: "VDD", pin: "VDD"}, - {name: "VPWR", pin: "VPWR", tie: "VDD"}, - {name: "VPB", pin: "VPB" , tie: "VDD"}, - {name: "vdd", pin: "vdd", tie: "VDD"}] - ground: [ {name: "VSS", pin: "VSS"}, - {name: "VGND", pin: "VGND", tie: "VSS"}, - {name: "VNB", pin: "VNB" , tie: "VSS"}, - {name: "vss", pin: "vss", tie: "VSS"}] + power: [ {name: "VDD", pins: ["VDD" ]}, + {name: "VPWR", pins: ["VPWR"], tie: "VDD"}, + {name: "VPB", pins: ["VPB" ], tie: "VDD"}, + {name: "vdd", pins: ["vdd" ], tie: "VDD"}] + ground: [ {name: "VSS", pins: ["VSS" ]}, + {name: "VGND", pins: ["VGND"], tie: "VSS"}, + {name: "VNB", pins: ["VNB" ], tie: "VSS"}, + {name: "vss", pina: ["vss" ], tie: "VSS"}] VDD: "1.8 V" GND: "0 V" diff --git a/hammer/vlsi/constraints.py b/hammer/vlsi/constraints.py index 562fbc016..2e8650a6e 100644 --- a/hammer/vlsi/constraints.py +++ b/hammer/vlsi/constraints.py @@ -87,9 +87,10 @@ def from_setting(d: dict) -> "SRAMParameters": Supply = NamedTuple('Supply', [ ('name', str), - ('pin', Optional[str]), + ('pins', Optional[List[str]]), ('tie', Optional[str]), - ('weight', Optional[int]) + ('weight', Optional[int]), + ('voltage', Optional[str]) ]) diff --git a/hammer/vlsi/hammer_tool.py b/hammer/vlsi/hammer_tool.py index 2949a68a7..b6c60a25a 100644 --- a/hammer/vlsi/hammer_tool.py +++ b/hammer/vlsi/hammer_tool.py @@ -1090,13 +1090,16 @@ def get_all_supplies(self, key: str) -> List[Supply]: supplies = self.get_setting(key) output = [] # type: List[Supply] for raw_supply in supplies: - supply = Supply(name=raw_supply['name'], pin=None, tie=None, weight=1) - if 'pin' in raw_supply: - supply = supply._replace(pin=raw_supply['pin']) + supply = Supply(name=raw_supply['name'], pins=[], tie=None, weight=1, voltage=None) + assert 'pin' not in raw_supply, "supply.pin: str has been replaced with supply.pins: List[str]" + if 'pins' in raw_supply: + supply = supply._replace(pins=raw_supply['pins']) if 'tie' in raw_supply: supply = supply._replace(tie=raw_supply['tie']) if 'weight' in raw_supply: supply = supply._replace(weight=raw_supply['weight']) + if 'voltage' in raw_supply: + supply = supply._replace(voltage=raw_supply['voltage']) output.append(supply) return output diff --git a/hammer/vlsi/hammer_vlsi_impl.py b/hammer/vlsi/hammer_vlsi_impl.py index 763682dcd..63c87223a 100644 --- a/hammer/vlsi/hammer_vlsi_impl.py +++ b/hammer/vlsi/hammer_vlsi_impl.py @@ -649,17 +649,24 @@ def generate_power_straps_tcl(self) -> List[str]: generate_rail_layer = self.get_setting("{}.generate_rail_layer".format(namespace)) ground_net_names = list(map(lambda x: x.name, self.get_independent_ground_nets())) # type: List[str] power_net_names = list(map(lambda x: x.name, self.get_independent_power_nets())) # type: List[str] + specified_power_net_names = self.get_setting("{}.power_nets".format(namespace)) + if len(specified_power_net_names) != 0: # filter by user specified settings + assert all(map(lambda n: n in power_net_names, specified_power_net_names)) + power_net_names = specified_power_net_names bottom_via_option = self.get_setting("{}.bottom_via_layer".format(namespace)) if bottom_via_option == "rail": bottom_via_layer = self.get_setting("technology.core.std_cell_rail_layer") else: bottom_via_layer = bottom_via_option - def get_weight(s: Supply) -> int: + def get_weight(supply_name: str) -> int: + supply = list(filter(lambda s: s.name == supply_name, self.get_independent_power_nets())) + # Check that single supply with name exists + assert len(supply) == 1 # Check that it's not None - assert isinstance(s.weight, int) - return s.weight - weights = list(map(get_weight, self.get_independent_power_nets())) # type: List[int] + assert isinstance(supply[0].weight, int) + return supply[0].weight + weights = list(map(get_weight, power_net_names)) # type: List[int] assert len(ground_net_names) == 1, "FIXME, I am assuming there's only 1 ground net" return self.specify_all_power_straps_by_tracks(layers, bottom_via_layer, ground_net_names[0], power_net_names, weights, bbox, pin_layers, generate_rail_layer) else: @@ -2084,25 +2091,28 @@ def upf_power_specification(self) -> str: ground_nets = self.get_all_ground_nets() #Create Supply Ports for pg_net in (power_nets+ground_nets): - if(pg_net.pin != None): - #Create Supply Nets - output.append(f'create_supply_net {pg_net.name} -domain {domain}') - output.append(f'create_supply_port {pg_net.name} -domain {domain} \\') - output.append(f'\t-direction in') + pins = pg_net.pins if pg_net.pins is not None else [pg_net.name] + #Create Supply Nets + output.append(f'create_supply_net {pg_net.name} -domain {domain}') + output.append(f'create_supply_port {pg_net.name} -domain {domain} \\') + output.append(f'\t-direction in') + for pin in pins: #Connect Supply Net - output.append(f'connect_supply_net {pg_net.name} -ports {pg_net.name}') - #Set Domain Supply Net + output.append(f'connect_supply_net {pg_net.name} -ports {pin}') + #Set Domain Supply Net output.append(f'set_domain_supply_net {domain} \\') output.append(f'\t-primary_power_net {power_nets[0].name} \\') output.append(f'\t-primary_ground_net {ground_nets[0].name}') #Add Port States for p_net in power_nets: - if(p_net.pin != None): - output.append(f'add_port_state {p_net.name} \\') + pins = p_net.pins if p_net.pins is not None else [p_net.name] + for pin in pins: + output.append(f'add_port_state {pin} \\') output.append(f'\t-state {{default {vdd.value}}}') for g_net in ground_nets: - if(g_net.pin != None): - output.append(f'add_port_state {g_net.name} \\') + pins = g_net.pins if g_net.pins is not None else [g_net.name] + for pin in pins: + output.append(f'add_port_state {pin} \\') output.append(f'\t-state {{default 0.0}}') #Create Power State Table output.append('create_pst pwr_state_table \\') @@ -2131,8 +2141,11 @@ def cpf_power_specification(self) -> str: # Define power and ground nets (HARD CODE) power_nets = self.get_all_power_nets() # type: List[Supply] ground_nets = self.get_all_ground_nets()# type: List[Supply] - vdd = VoltageValue(self.get_setting("vlsi.inputs.supplies.VDD")) # type: VoltageValue - output.append(f'create_power_nets -nets {{ {" ".join(map(lambda x: x.name, power_nets))} }} -voltage {vdd.value}') + for power_net in power_nets: + vdd = VoltageValue(self.get_setting("vlsi.inputs.supplies.VDD")) # type: VoltageValue + if power_net.voltage is not None: + vdd = VoltageValue(power_net.voltage) + output.append(f'create_power_nets -nets {power_net.name} -voltage {vdd.value}') output.append(f'create_ground_nets -nets {{ {" ".join(map(lambda x: x.name, ground_nets))} }}') # Define power domain and connections output.append(f'create_power_domain -name {domain} -default') @@ -2140,10 +2153,13 @@ def cpf_power_specification(self) -> str: output.append(f'update_power_domain -name {domain} -primary_power_net {power_nets[0].name} -primary_ground_net {ground_nets[0].name}') # Assuming that all power/ground nets correspond to pins for pg_net in (power_nets+ground_nets): - if(pg_net.pin != None): - output.append(f'create_global_connection -domain {domain} -net {pg_net.name} -pins {pg_net.pin}') + pins = pg_net.pins if pg_net.pins is not None else [pg_net.name] + if len(pins): + pins_str = ' '.join(pins) + output.append(f'create_global_connection -domain {domain} -net {pg_net.name} -pins [list {pins_str}]') # Create nominal operation condtion and power mode - output.append(f'create_nominal_condition -name {condition} -voltage {vdd.value}') + nominal_vdd = VoltageValue(self.get_setting("vlsi.inputs.supplies.VDD")) # type: VoltageValue + output.append(f'create_nominal_condition -name {condition} -voltage {nominal_vdd.value}') output.append(f'create_power_mode -name {mode} -default -domain_conditions {{{domain}@{condition}}}') # Footer output.append("end_design") diff --git a/tests/test_power_straps.py b/tests/test_power_straps.py index af9e46751..165af1c28 100644 --- a/tests/test_power_straps.py +++ b/tests/test_power_straps.py @@ -93,8 +93,8 @@ def simple_straps_options() -> Dict[str, Any]: straps_options = { "vlsi.inputs.supplies": { - "power": [{"name": "VDD", "pin": "VDD"}], - "ground": [{"name": "VSS", "pin": "VSS"}], + "power": [{"name": "VDD", "pins": ["VDD"]}], + "ground": [{"name": "VSS", "pins": ["VSS"]}], "VDD": "1.00 V", "GND": "0 V" }, @@ -129,8 +129,8 @@ def multiple_domains_straps_options() -> Dict[str, Any]: straps_options = { "vlsi.inputs.supplies": { - "power": [{"name": "VDD", "pin": "VDD"}, {"name": "VDD2", "pin": "VDD2"}], - "ground": [{"name": "VSS", "pin": "VSS"}], + "power": [{"name": "VDD", "pins": ["VDD"]}, {"name": "VDD2", "pins": ["VDD2"]}], + "ground": [{"name": "VSS", "pins": ["VSS"]}], "VDD": "1.00 V", "GND": "0 V" },