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.

149 lines
4.9 KiB

<?xml version="1.0" encoding="utf-8"?>
def create_dummy_pins(netlist: db.Netlist, circuit: db.Circuit, core_area: db.DSimplePolygon) -&gt; Tuple[
db.Circuit, Dict[int, Tuple[int, int]]]:
Create dummy pin instances and locations that are distributed evenly around the `core_area`.
:param netlist:
:param circuit:
:param core_area:
:return: Tuple[new top level circuit, positions of dummy pins Dict[sub circuit id, (x,y)]]
# Create dummy circuit for pads.
dummy_pad = db.Circuit() = 'dummy_pad'
new_top = db.Circuit() = 'DUMMY_TOP'
inst = new_top.create_subcircuit(circuit, 'i_{}'.format(
port_nodes = []
# Create virtual modules
for p in circuit.each_pin():
assert isinstance(p, db.Pin)
pad_inst: db.SubCircuit = new_top.create_subcircuit(dummy_pad, 'i_dummy_pad_{}'.format(
# net = circuit.net_for_pin(p)
net = new_top.create_net(
pad_inst.connect_pin(dummy_pad.pin_by_name('IO'), net)
inst.connect_pin(p, net)
top_pin = new_top.create_pin(
new_top.connect_pin(top_pin, net)
pad_ring_box = core_area.bbox()
# Create dummy node positions for ports.
node_positions = dict()
for i, n in enumerate(port_nodes):
# Distribute them on the edge of a unit square.
p = (pad_ring_box.width() + pad_ring_box.height()) * 2 * i / len(port_nodes)
w = pad_ring_box.width()
h = pad_ring_box.height()
if p &lt; w:
x, y = p, pad_ring_box.p1.y
elif p &lt; w + h:
x, y = pad_ring_box.p2.x, p - h
elif p &lt; w + h + w:
x, y = pad_ring_box.p2.x - (p - w - h), pad_ring_box.p2.y
x, y = pad_ring_box.p1.x, pad_ring_box.p2.y - (p - w - h - w)
node_positions[n] = (x, y)
return new_top, node_positions
# Fetch the correct circuit. TODO: Fetch it by name!
top_circuit_without_pads: db.Circuit = next(netlist.each_circuit_top_down())"Top circuit: {}".format(
# Define area to place the cells within.
design.core_area = db.SimplePolygon(db.Box(db.Point(0, 0), db.Point(20000, 20000))).to_dtype(1)
# Create a new top_circuit with dummy pad instances at dummy positions.
design.top_circuit, fixed_positions = create_dummy_pins(netlist, top_circuit_without_pads, design.core_area)
# Print number of subcircuit instances in the flat netlist. This includes standar-cells, pads, macros, ...
num_cells = len(list(top_circuit_without_pads.each_subcircuit()))'Number of cell instances: {}'.format(num_cells))
# Create top level cell.
top_cell: db.Cell = layout.create_cell('TOP')
design.top_cell = top_cell
# === Insert unplaced cells ==='Insert unplaced cells.')
# Store cell instances by the IDs of their sub-circuits.
# This creates the mapping between layout cells and sub-circuits in the netlist.
core_center = design.core_area.bbox().center()
trans = db.DTrans(core_center.x, core_center.y)
for sub_circuit in design.top_circuit.each_subcircuit():
cell_name = sub_circuit.circuit_ref().name
cell = layout.cell_by_name(cell_name)
cell_inst = db.CellInstArray(cell, trans)
cell_inst = top_cell.insert(cell_inst)
# Store subcircuit ID with the cell instance.
cell_inst.set_property('fixed', False)
design.cell_instances_by_subcircuit_id[] = cell_inst
except RuntimeError as err:
logger.warning('Cell not found: {}'.format(cell_name))
# (debug) Sketch core area.
core_area_layer = db.LayerInfo(1000, 0, 'core_area')
core_area_layer = layout.layer(db.LayerInfo(1000, 0, 'core_area'))
# Compute total used area.
area_used = 0
for inst in design.top_circuit.each_subcircuit():
shape = cell_shapes.get(inst.circuit_ref().name, (0, 0))
area = shape[0] * shape[1]
area_used += area
area_available = design.core_area.area()
area_ratio = area_used / area_available
if area_ratio &gt;= 1:
logger.warning(f"Core area is more than 100% full: {area_ratio*100}%")"Core area usage: {area_ratio*100}%")
# Show the new cell in KLayout.
cell_view.cell = top_cell
# Make sure all layers are displayed.