Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
donn committed Sep 23, 2024
2 parents ada1299 + 92c2a55 commit 13cd748
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 49 deletions.
46 changes: 45 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,50 @@
## Documentation
-->

# 2.1.8

## Steps

* `OpenROAD.STA*PnR`

* Fixed a bug in STA metrics where paths with exactly zero slack are counted
as violations

# 2.1.7

## Steps

* `Odb.Remove*Obstructions`

* Rework obstruction matching code to not use IEEE 754 in any capacity
* Fixed bug where non-integral obstructions would not be matched correctly
(thanks @urish!)

# 2.1.6

## Steps

* `Yosys.Synthesis`

* Fixed bug where `hilomap` command was invoked incorrectly (thanks @htfab!)

# 2.1.5

## Steps

* `Odb.SetPowerConnections`

* Fixed an issue introduced in `2.1.1` where modules that are defined as part
of hierarchical netlists would be considered macros and then cause a crash
when they are inevitably not found in the design database.
* Explicitly mention that macros that are not on the top level will not be
connected, and emit warnings if a hierarchical netlist is detected.

## Documentation

* Updated macro documentation to further clarify how instances should be named
and how names should be added to the configuration.

# 2.1.4

## Steps
Expand All @@ -27,7 +71,7 @@
regardless the information in the SDC file.
* For backwards compatibility, `STAPrePNR` unsets all propagated clocks and
the rest set all propagated clocks IF the SDC file lacks the strings
`set_propagated_clock` or `unset_propagated_clock`.
`set_propagated_clock` or `unset_propagated_clock`.

# 2.1.3

Expand Down
48 changes: 47 additions & 1 deletion docs/source/usage/using_macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ would be declared as:
}
```

```{admonition} On Instance Names
````{admonition} On Instance Names
:class: tip
Instance names should match the name as instantiated in Verilog, i.e.,
Expand All @@ -163,7 +163,53 @@ without escaping any characters for layout formats.
For example, if you instantiate an array of macros `spm spm_inst[1:0]`,
the first instance's name should be `spm_inst[0]`, not
`spm_inst\[0\]` or similar.
---
If your macros are within other instances, the names get a bit more complicated
depending on whether you flatten the hierarchy during synthesis or not. It is
the designer's responsibility to use either notation in the Instances dictionary
depending on which hierarchy mode is utilized.
**Hierarchy flattened during Synthesis (default, recommended)**
When flattening the hierarchy, Yosys will rename instances to use the dot
notation, i.e, the name of an instance inside another instance will be
`instance_a.instance_b`. Note that they are the names of instances and not
the names of modules.
**Hierarchy maintainted during Synthesis**
Using macros with hierarchy is maintained during Synthesis is
**currently unsupported** in OpenLane, as the netlist is flattened upon moving
into OpenROAD as its hierarchical support is rather experimental. This will be
remedied in a future update to OpenLane.
In the meantime, if you do choose to keep the hierarchy after Synthesis
regardless by using the attribute `(* keep_hierarchy = "TRUE" *)`, you're on
your own, but here are a couple tips.
* Naming will fall upon OpenROAD, which use the `/` notation regardless of your
technology LEF's divider characters property, i.e., `instance_a/instance_b`.
Note once again that are the names of instances and not the names of modules.
* The power and ground pins of the macro must match that of the standard cell
library as `Odb.SetPowerConnections` won't work for these macros.
**Please do not do this:**
Please do not use any of these characters in instance names by using the `\`
prefix, as the example below shows:
* `/`
* `[]`
* `:`
Using these is considered **undefined behavior** and the flow will likely crash
because of assumptions about the hierarchy involving these characters.
```verilog
CellType \instance/name (…);
```
````

## STA

Expand Down
31 changes: 14 additions & 17 deletions openlane/scripts/odbpy/defutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import os
import re
import sys
from decimal import Decimal

from reader import click_odb, click
from typing import Tuple, List
Expand Down Expand Up @@ -456,7 +457,7 @@ def replace_instance_prefixes(original_prefix, new_prefix, reader):
cli.add_command(replace_instance_prefixes)


def parse_obstructions(obstructions):
def parse_obstructions(obstructions) -> List[Tuple[str, List[int]]]:
RE_NUMBER = r"[\-]?[0-9]+(\.[0-9]+)?"
RE_OBS = (
r"(?P<layer>\S+)\s+"
Expand All @@ -483,7 +484,7 @@ def parse_obstructions(obstructions):
sys.exit(FORMAT_ERROR)
else:
layer = m.group("layer")
bbox = [float(x) for x in m.group("bbox").split()]
bbox = [Decimal(x) for x in m.group("bbox").split()]
obs_list.append((layer, bbox))

return obs_list
Expand Down Expand Up @@ -526,41 +527,37 @@ def add_obstructions(reader, input_lefs, obstructions):
)
@click_odb
def remove_obstructions(reader, input_lefs, obstructions):
obs_list = parse_obstructions(obstructions)
dbu: int = reader.tech.getDbUnitsPerMicron()
existing_obstructions: List[Tuple[str, List[int], odb.dbObstruction]] = []
dbu = reader.tech.getDbUnitsPerMicron()

def to_microns(x):
return int(x / dbu)

for odb_obstruction in reader.block.getObstructions():
bbox = odb_obstruction.getBBox()
existing_obstructions.append(
(
bbox.getTechLayer().getName(),
[
to_microns(bbox.xMin()),
to_microns(bbox.yMin()),
to_microns(bbox.xMax()),
to_microns(bbox.yMax()),
bbox.xMin(),
bbox.yMin(),
bbox.xMax(),
bbox.yMax(),
],
odb_obstruction,
)
)

for obs in obs_list:
layer = obs[0]
bbox = obs[1]
bbox = [int(x * dbu) for x in bbox]
for obs in parse_obstructions(obstructions):
layer, bbox = obs
bbox = [int(x * dbu) for x in bbox] # To dbus
found = False
if reader.tech.findLayer(layer) is None:
print(f"[ERROR] layer {layer} doesn't exist.", file=sys.stderr)
sys.exit(METAL_LAYER_ERROR)
for odb_obstruction in existing_obstructions:
if odb_obstruction[0:2] == obs:
odb_layer, odb_bbox, odb_obj = odb_obstruction
if (odb_layer, odb_bbox) == (layer, bbox):
print(f"Removing obstruction on {layer} at {bbox} (DBU)…")
found = True
odb.dbObstruction_destroy(odb_obstruction[2])
odb.dbObstruction_destroy(odb_obj)
if found:
break
if not found:
Expand Down
75 changes: 54 additions & 21 deletions openlane/scripts/odbpy/power_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,29 @@ class Design(object):
@dataclass
class Instance:
name: str
module: str
module_name: str
power_connections: Dict[str, str]
ground_connections: Dict[str, str]

def __init__(self, reader: OdbReader, yosys_dict: dict) -> None:
self.reader = reader
self.design_name = reader.block.getName()
self.yosys_dict = yosys_dict
self.yosys_design_object = yosys_dict["modules"][self.design_name]

self.pins_by_module_name: Dict[str, Dict[str, odb.dbMTerm]] = {}
self.verilog_net_name_by_bit: Dict[int, str] = functools.reduce(
lambda a, b: {**a, **{bit: b[0] for bit in b[1]["bits"]}},
self.yosys_design_object["netnames"].items(),
{},
)
self.verilog_net_names_by_bit_by_module: Dict[str, Dict[int, str]] = {}
self.nets_by_net_name = {net.getName(): net for net in reader.block.getNets()}

def get_verilog_net_name_by_bit(self, top_module: str, target_bit: int):
yosys_design_object = self.yosys_dict["modules"][top_module]
if top_module not in self.verilog_net_names_by_bit_by_module:
self.verilog_net_names_by_bit_by_module[top_module] = functools.reduce(
lambda a, b: {**a, **{bit: b[0] for bit in b[1]["bits"]}},
yosys_design_object["netnames"].items(),
{},
)
return self.verilog_net_names_by_bit_by_module[top_module][target_bit]

def get_pins(self, module_name: str) -> Dict[str, odb.dbMTerm]:
if module_name not in self.pins_by_module_name:
master = self.reader.db.findMaster(module_name)
Expand Down Expand Up @@ -83,10 +88,17 @@ def is_ground(self, module_name: str, pin_name: str) -> bool:

return module_pins[pin_name].getSigType() == "GROUND"

def extract_pg_pins(self, cell_name: str) -> dict:
cells = self.yosys_design_object["cells"]
module = cells[cell_name]["type"]
master = self.reader.db.findMaster(module)
def extract_pg_pins(self, top_module: str, cell_name: str) -> dict:
yosys_design_object = self.yosys_dict["modules"][top_module]
cells = yosys_design_object["cells"]
module_name = cells[cell_name]["type"]
master = self.reader.db.findMaster(module_name)
if master is None:
print(
f"[ERROR] Could not find master for cell type '{module_name}' in the database."
)
exit(-1)

lef_pg_pins = []
for pin in master.getMTerms():
if pin.getSigType() in ["POWER", "GROUND"]:
Expand All @@ -102,32 +114,53 @@ def extract_pg_pins(self, cell_name: str) -> dict:
connection_bits = connections[pin_name]
if len(connection_bits) != 1:
print(
f"[ERROR] Unexpectedly found more than one bit connected to {sigtype} pin {module}/{pin_name}."
f"[ERROR] Unexpectedly found more than one bit connected to {sigtype} pin {module_name}/{pin_name}."
)
exit(-1)
connection_bit = connection_bits[0]
connected_to_v = self.verilog_net_name_by_bit[connection_bit]
connected_to_v = self.get_verilog_net_name_by_bit(
top_module,
connection_bit,
)
(power_pins if sigtype == "POWER" else ground_pins)[
pin_name
] = connected_to_v

return power_pins, ground_pins

def extract_instances(self) -> List["Design.Instance"]:
def extract_instances(
self,
top_module: str,
prefix: str = "",
) -> List["Design.Instance"]:
yosys_design_object = self.yosys_dict["modules"][top_module]
instances = []
cells = self.yosys_design_object["cells"]
cells = yosys_design_object["cells"]
for cell_name in cells.keys():
module = cells[cell_name]["type"]
if module.startswith("$"):
module_name = cells[cell_name]["type"]
if module_name.startswith("$"):
# yosys primitive
continue
power_pins, ground_pins = self.extract_pg_pins(cell_name)
if module_name in self.yosys_dict["modules"]:
print(
f"[WARNING] Macros inside hierarchical netlists are not currently supported in OpenLane: skipping submodule '{cell_name}' of type '{module_name}'."
)
continue
# sub_instances = self.extract_instances(
# self.yosys_dict["modules"][module_name],
# prefix=prefix + f"{cell_name}/",
# )
# instances += sub_instances
power_pins, ground_pins = self.extract_pg_pins(
top_module,
cell_name,
)
instances.append(
Design.Instance(
name=cell_name,
name=prefix + cell_name,
ground_connections=ground_pins,
power_connections=power_pins,
module=module,
module_name=module_name,
)
)

Expand Down Expand Up @@ -244,7 +277,7 @@ def set_power_connections(input_json, reader: OdbReader):
yosys_dict = json.loads(design_str)

design = Design(reader, yosys_dict)
macro_instances = design.extract_instances()
macro_instances = design.extract_instances(design.design_name)
for instance in macro_instances:
for pin in instance.power_connections.keys():
net_name = instance.power_connections[pin]
Expand Down
4 changes: 2 additions & 2 deletions openlane/scripts/odbpy/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ def __init__(self, *args, **kwargs):
self.instances = self.block.getInsts()

busbitchars = re.escape("[]") # TODO: Get alternatives from LEF parser
dividerchar = re.escape("/") # TODO: Get alternatives from LEF parser
self.escape_verilog_rx = re.compile(rf"([{dividerchar + busbitchars}])")
# dividerchar = re.escape("/") # TODO: Get alternatives from LEF parser
self.escape_verilog_rx = re.compile(rf"([{busbitchars}])")

def add_lef(self, new_lef):
self.ord_tech.readLef(new_lef)
Expand Down
20 changes: 20 additions & 0 deletions openlane/scripts/openroad/sta/corner.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,11 @@ foreach path $hold_violating_paths {
set start_pin [get_property $path startpoint]
set end_pin [get_property $path endpoint]
set kind "[get_path_kind $start_pin $end_pin]"
set slack [get_property $path slack]

if { $slack >= 0 } {
continue
}

incr total_hold_vios
if { "$kind" == "reg-reg" } {
Expand All @@ -279,6 +284,11 @@ foreach path $hold_paths {
set start_pin [get_property $path startpoint]
set end_pin [get_property $path endpoint]
set kind "[get_path_kind $start_pin $end_pin]"
set slack [get_property $path slack]

if { $slack >= 0 } {
continue
}
if { "$kind" == "reg-reg" } {
set slack [get_property $path slack]

Expand All @@ -293,6 +303,11 @@ foreach path $setup_violating_paths {
set start_pin [get_property $path startpoint]
set end_pin [get_property $path endpoint]
set kind "[get_path_kind $start_pin $end_pin]"
set slack [get_property $path slack]

if { $slack >= 0 } {
continue
}

incr total_setup_vios
if { "$kind" == "reg-reg" } {
Expand All @@ -307,6 +322,11 @@ foreach path $setup_paths {
set start_pin [get_property $path startpoint]
set end_pin [get_property $path endpoint]
set kind "[get_path_kind $start_pin $end_pin]"
set slack [get_property $path slack]

if { $slack >= 0 } {
continue
}

if { "$kind" == "reg-reg" } {
set slack [get_property $path slack]
Expand Down
Loading

0 comments on commit 13cd748

Please sign in to comment.