Simple framework for physical chip design (place & route) based on KLayout.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

125 lines
4.2 KiB

<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description/>
<version/>
<category>pymacros</category>
<prolog/>
<epilog/>
<doc/>
<autorun>false</autorun>
<autorun-early>false</autorun-early>
<shortcut/>
<show-in-menu>false</show-in-menu>
<group-name/>
<menu-path/>
<interpreter>python</interpreter>
<dsl-interpreter-name/>
<text># Load technology information and libraries.
# * Load cell layouts (GDS) into the current layout.
# * Extract cell dimensions from LEF.
# Object that represents the macro block which is currently under construction.
design = SimpleDesign()
# Fetch the layout from the current view.
main_window = pya.Application.instance().main_window()
current_view = main_window.current_view()
if current_view is None:
pya.MessageBox.warning("Error", "No view open! Create a new Layout!", pya.MessageBox.Ok)
raise Exception("No view open!")
cell_view = pya.CellView.active()
design.layout = cell_view.layout()
# Create a new layout when running without the KLayout GUI.
#design.layout = db.Layout()
# Create shortcut for accessing the layout.
layout = design.layout
# TODO: Load layer definitions from LEF or KLayout technology manager.
# Paths to library files.
liberty_data = get_example_liberty_library()
lef_file = get_path('gscl45nm.lef')
# Paths to cell layouts.
kpnr_base_dir = os.path.dirname(os.path.dirname(kpnr.__file__))
gds_paths = [f'{kpnr_base_dir}/test_data/lib/gds']
# Read liberty library.
library = parse_liberty(liberty_data)
liberty_reader = LibertyNetlistReader()
leaf_cells = liberty_reader.read_liberty(library)
logger.info(f"Number of leaf cells: {len(list(leaf_cells.each_circuit()))}")
# Read LEF library.
lef_library = db.Layout()
logger.info('Read LEF: {}'.format(lef_file))
lef_library.read(lef_file)
# Define pin layers of the standard cells.
# This is used to recognize the pin shapes from the GDS layouts.
# TODO: Take pin shapes from LEF.
l_metal1_pin = (21, 0)
l_metal1_label = (21, 1)
# Process library
# Find layer number of the 'OUTLINE' layer which is used as the abutment box of the cells.
l_outline = lef_library.find_layer('OUTLINE')
# Get abutment boxes for all cells.
cell_outlines = {
cell.name: cell.bbox_per_layer(l_outline) for cell in lef_library.each_cell()
}
# Get cell shapes with dummy height.
cell_shapes = {
name: (bbox.width(), bbox.height()) for name, bbox in cell_outlines.items()
}
# Populate cell layout library.
# Find all GDS file in the search path and add their content cells to the layout.
logger.info('Read GDS files from : {}'.format(gds_paths))
load_options = db.LoadLayoutOptions()
load_options.create_other_layers = True
for path in gds_paths:
if not os.path.isabs(path):
path = os.path.join(os.path.dirname(__file__), path)
dir_content = os.listdir(path)
for f in dir_content:
f = os.path.join(path, f)
if os.path.isfile(f) and f.lower().endswith('.gds'):
# logger.debug('Read GDS: {}'.format(f))
layout.read(f, load_options)
logger.info('Number of cells in layout: {}'.format(layout.cells()))
assert layout.cells() &gt; 1, "No cells loaded."
# Find pin shapes of the cells.
# Here this is based on the text labels in the GDS layouts of the cells.
l_metal1_pin = layout.find_layer(*l_metal1_pin)
l_metal1_label = layout.find_layer(*l_metal1_label)
cell_pin_shapes = dict()
for lef_cell in lef_library.each_cell():
cell = layout.cell(lef_cell.name)
if cell is not None:
cell_pin_shapes[cell.name] = pin_detection.find_pins_from_text_labels(cell, l_metal1_pin, l_metal1_label)
# Check that cell heights are unique.
cell_heights = {cell_shapes[cell.name][1] for cell in layout.each_cell() if cell.name in cell_shapes}
assert len(cell_heights) == 1, "Cell heights are not all the same: {}".format(cell_heights)
row_height = cell_heights.pop()
cell_widths = {cell_shapes[cell.name][0] for cell in layout.each_cell() if cell.name in cell_shapes}
x_grid_pitch = cell_widths.pop()
for w in cell_widths:
x_grid_pitch = math.gcd(x_grid_pitch, w)
logger.info("Detected x-grid pitch for placement: {}".format(x_grid_pitch))
assert x_grid_pitch &gt; 1, "Cells don't have a width that is a integer multiple of a number larger than 1."</text>
</klayout-macro>