Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add floorplanning checks #411

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions src/hammer-vlsi/hammer_vlsi/hammer_checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# hammer_checks.py
# Automated rule checks for constraints
#
# See LICENSE for licence details.

from decimal import Decimal
from hammer_utils import check_on_grid, lcm_grid
from hammer_tech import RoutingDirection
from .constraints import *
from .hammer_tool import HammerTool

class HammerChecks(HammerTool):

def run_all(self) -> bool:
return self.floorplan_checks()

def floorplan_checks(self) -> bool:

floorplan_constraints = self.get_placement_constraints()

############## Rule-check the constraints before generation ################
fp_checks_pass = True
site = self.technology.get_placement_site()
for constraint in floorplan_constraints:
# Enumerate all possible constraint types here
if constraint.type == PlacementConstraintType.TopLevel:
# Rule FP.TOP.X: Width of top-level must be an integer multiple of the site x dimension
if not check_on_grid(constraint.width, site.x):
suggest_l = Decimal(int(Decimal(constraint.height) / site.x)) * site.x
suggest_h = suggest_l + site.y
self.logger.error("Floorplanning rule FP.TOP.X: Width of top-level must be an integer multiple of the site x dimension: {x}. Try {suggest_l} or {suggest_h}.".format(
mod=constraint.path, x=site.x, suggest_l=suggest_l, suggest_h = suggest_h))
fp_checks_pass = False
# Rule FP.TOP.Y: Height of top-level must be an integer multiple of the site y dimension
if not check_on_grid(constraint.height, site.y):
suggest_l = Decimal(int(Decimal(constraint.width) / site.y)) * site.y
suggest_h = suggest_l + site.y
self.logger.error("Floorplanning rule FP.TOP.Y: Height of top-level must be an integer multiple of the site y dimension: {y}. Try {suggest_l} or {suggest_h}.".format(
mod=constraint.path, y=site.y, suggest_l=suggest_l, suggest_h = suggest_h))
fp_checks_pass = False
elif constraint.type == PlacementConstraintType.Dummy:
pass
elif constraint.type == PlacementConstraintType.Placement:
pass
elif constraint.type == PlacementConstraintType.HardMacro:
pass
elif constraint.type == PlacementConstraintType.Hierarchical:
# TODO some of these might need to be checking HierarchicalMode instead of constraint type
# Rule FP.HIER.WIDTH: Width of hierarchical module must be an integer multiple of the site x dimension
if not check_on_grid(constraint.width, site.x):
suggest_l = Decimal(int(Decimal(constraint.height) / site.x)) * site.x
suggest_h = suggest_l + site.y
self.logger.error("Floorplanning rule FP.HIER.WIDTH: Width of hierarchical module {mod} must be an integer multiple of the site x dimension: {x}. Try {suggest_l} or {suggest_h}.".format(
mod=constraint.path, y=site.y, suggest_l=suggest_l, suggest_h = suggest_h))
fp_checks_pass = False
# Rule FP.HIER.HEIGHT: Height of hierarchical module must be an integer multiple of the site y dimension
if not check_on_grid(constraint.height, site.y):
suggest_l = Decimal(int(Decimal(constraint.width) / site.y)) * site.y
suggest_h = suggest_l + site.y
self.logger.error("Floorplanning rule FP.HIER.HEIGHT: Height of hierarchical module {mod} must be an integer multiple of the site y dimension: {y}. Try {sugggest_l} or {suggest_h}.".format(
mod=constraint.path, y=site.y, suggest_l=suggest_l, suggest_h = suggest_h))
fp_checks_pass = False
# Rule FP.HIER.PLACEMENT.X: Origin X of hierarchical module must be on the site grid
if not check_on_grid(constraint.x, site.x):
suggest_l = Decimal(int(Decimal(constraint.x) / site.x)) * site.x
suggest_h = suggest_l + site.x
self.logger.error("Floorplanning rule FP.HIER.PLACEMENT.X: Origin X of hierarchical module {mod} must be on the site grid: {x}. Try {suggest_l} or {suggest_h}.".format(
mod=constraint.path, x=site.x, suggest_l=suggest_l, suggest_h=suggest_h))
fp_checks_pass = False
# Rule FP.HIER.PLACEMENT.Y: Origin Y of hierarchical module must be on the site grid
if not check_on_grid(constraint.y, site.y):
suggest_l = Decimal(int(Decimal(constraint.y) / site.y)) * site.y
suggest_h = suggest_l + site.y
self.logger.error("Floorplanning rule FP.HIER.PLACEMENT.Y: Origin Y of hierarchical module {mod} must be on the site grid: {y}. Try {suggest_l} or {suggest_h}.".format(
mod=constraint.path, y=site.y, suggest_l=suggest_l, suggest_h=suggest_h))
fp_checks_pass = False
# Rule FP.HIER.PLACEMENT.???: Routing track offsets of pin layers must line up with parent
# TODO(johnwright) (in a future PR)
# Rule FP.HIER.POWER.PINS: Hierarchical cells must have power strap pins enabled
# TODO(johnwright) (in a future PR)
# Determine if this is a singly- or multiply-instantiated module
# TODO(johnwright) This rule can be relaxed for multiply-instantiated modules that are not tiled, but we need other checks for those (e.g. all offsets must be the same)
num_occurrences = 2 # TODO how to get this?
if num_occurrences > 1:
# Rule FP.TILE.WIDTH: Width of tiled hierarchical module must be an integer multiple of the LCM of:
# - site x dimension
# - all vertical routing pitches of pin layers
# - all vertical power strap group pitches of layers intended to abut at the top level (for now assume pin layers) (TODO when this is more useful)
# - the bump pitch (only if bumps are directly connected to the tiled cell) TODO add a property for this, not currently implemented
# TODO(johnwright) when hier power straps exist, use the hier power straps pins (throw a warning if hier power straps pins is different than regular pins)
grid_unit_x = lcm_grid(self.technology.get_grid_unit(),
site.x,
*map(lambda x: x.pitch, filter(lambda x: x.direction == RoutingDirection.Vertical, self.get_pin_metals())))
#*map(self.get_by_tracks_drawn_pitch, filter(lambda x: x.direction == RoutingDirection.Vertical, self.get_pin_metals())))
if not check_on_grid(constraint.width, grid_unit_x):
suggest_l = Decimal(int(Decimal(constraint.width) / grid_unit_x)) * grid_unit_x
suggest_h = suggest_l + grid_unit_x
self.logger.error("Floorplanning rule FP.TILE.WIDTH: Width of tiled hierarchical module {mod} must be an integer multiple of {x} (computed). Try {suggest_l} or {suggest_h}.".format(
mod=constraint.path, x=grid_unit_x, suggest_l=suggest_l, suggest_h=suggest_h))
fp_checks_pass = False
# Rule FP.TILE.HEIGHT: Height of tiled hierarchical module must be an integer multiple of the LCM of:
# - site y dimension
# - all horizontal routing pitches of pin layers
# - all horizontal power strap group pitches of layers intended to abut at the top level (for now assume pin layers) (TODO when this is more useful)
# - the bump pitch (only if bumps are directly connected to the tiled cell) TODO add a property for this, not currently implemented
# TODO(johnwright) when hier power straps exist, use the hier power straps pins (throw a warning if hier power straps pins is different than regular pins)
grid_unit_y = lcm_grid(self.technology.get_grid_unit(),
site.y,
*map(lambda x: x.pitch, filter(lambda x: x.direction == RoutingDirection.Horizontal, self.get_pin_metals())))
#*map(self.get_by_tracks_drawn_pitch, filter(lambda x: x.direction == RoutingDirection.Horizontal, self.get_pin_metals())))
if not check_on_grid(constraint.height, grid_unit_y):
suggest_l = Decimal(int(Decimal(constraint.height) / grid_unit_y)) * grid_unit_y
suggest_h = suggest_l + grid_unit_y
self.logger.error("Floorplanning rule FP.TILE.HEIGHT: Height of tiled hierarchical module {mod} must be an integer multiple of {y} (computed). Try {suggest_l} or {suggest_h}.".format(
mod=constraint.path, y=grid_unit_y, suggest_l=suggest_l, suggest_h=suggest_h))
fp_checks_pass = False
# Rule FP.TILE.PLACEMENT.X: Width of tiled hierarchical module must be an integer multiple of the LCM of:
# - site x dimension
# - all vertical routing pitches of pin layers
# - all vertical power strap group pitches of layers intended to abut at the top level (for now assume pin layers) (TODO when this is more useful)
# - the bump pitch (only if bumps are directly connected to the tiled cell) TODO add a property for this, not currently implemented
# TODO(johnwright) when hier power straps exist, use the hier power straps pins (throw a warning if hier power straps pins is different than regular pins)
grid_unit_x = lcm_grid(self.technology.get_grid_unit(),
site.x,
*map(lambda x: x.pitch, filter(lambda x: x.direction == RoutingDirection.Vertical, self.get_pin_metals())))
#*map(self.get_by_tracks_drawn_pitch, filter(lambda x: x.direction == RoutingDirection.Vertical, self.get_pin_metals())))
if not check_on_grid(constraint.x, grid_unit_x):
suggest_l = Decimal(int(Decimal(constraint.x) / grid_unit_x)) * grid_unit_x
suggest_h = suggest_l + grid_unit_x
self.logger.error("Floorplanning rule FP.TILE.PLACEMENT.X: Origin X of tiled hierarchical module {mod} must be an integer multiple of {x} (computed). Try {suggest_l} or {suggest_h}.".format(
mod=constraint.path, x=grid_unit_x, suggest_l=suggest_l, suggest_h=suggest_h))
fp_checks_pass = False
# Rule FP.TILE.PLACEMENT.Y: Height of tiled hierarchical module must be an integer multiple of the LCM of:
# - site y dimension
# - all horizontal routing pitches of pin layers
# - all horizontal power strap group pitches of layers intended to abut at the top level (for now assume pin layers) (TODO when this is more useful)
# - the bump pitch (only if bumps are directly connected to the tiled cell) TODO add a property for this, not currently implemented
# TODO(johnwright) when hier power straps exist, use the hier power straps pins (throw a warning if hier power straps pins is different than regular pins)
grid_unit_y = lcm_grid(self.technology.get_grid_unit(),
site.y,
*map(lambda x: x.pitch, filter(lambda x: x.direction == RoutingDirection.Horizontal, self.get_pin_metals())))
#*map(self.get_by_tracks_drawn_pitch, filter(lambda x: x.direction == RoutingDirection.Horizontal, self.get_pin_metals())))
if not check_on_grid(constraint.y, grid_unit_y):
suggest_l = Decimal(int(Decimal(constraint.y) / grid_unit_y)) * grid_unit_y
suggest_h = suggest_l + grid_unit_y
self.logger.error("Floorplanning rule FP.TILE.PLACEMENT.Y: Origin Y of tiled hierarchical module {mod} must be an integer multiple of {y} (computed). Try {suggest_l} or {suggest_h}.".format(
mod=constraint.path, y=grid_unit_y, suggest_l=suggest_l, suggest_h=suggest_h))
fp_checks_pass = False
elif constraint.type == PlacementConstraintType.Obstruction:
pass
else:
assert False, "Invalid constraint type: {}. Perhaps you added a new one and forgot to update the rules above?".format(constraint.type)

return fp_checks_pass

16 changes: 16 additions & 0 deletions src/hammer-vlsi/hammer_vlsi/hammer_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import shlex
from abc import ABCMeta, abstractmethod
from functools import reduce
import itertools
from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, cast

import hammer_config
Expand Down Expand Up @@ -917,6 +918,21 @@ def get_bumps(self) -> Optional[BumpsDefinition]:
pitch=self.get_setting("vlsi.inputs.bumps.pitch"),
cell=self.get_setting("vlsi.inputs.bumps.cell"), assignments=assignments)

def get_pin_metals(self) -> List[Metal]:
"""
Get a list of the metal layers on which there are pins.
:return: A potentially empty list of Metals
"""
# This function works around mypy not liking filtering Nones out of a List[Optional[...]]
def get_pin_layer(pa: PinAssignment) -> List[str]:
l = [] # type: List[str]
if pa.layers is not None:
l = pa.layers
return l
layers = list(map(get_pin_layer, self.get_pin_assignments())) # type: List[List[str]]
flat_layers = list(set(itertools.chain.from_iterable(layers)))
return list(map(self.get_stackup().get_metal, flat_layers))

def get_pin_assignments(self) -> List[PinAssignment]:
"""
Get a list of pin assignments in accordance with settings in the Hammer IR.
Expand Down