Skip to content

Commit

Permalink
kind of a mess but this is the version that produced the gds file for…
Browse files Browse the repository at this point in the history
… SPH035
  • Loading branch information
emmakbat committed Nov 5, 2024
1 parent 2320db6 commit a6fce0f
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 32 deletions.
27 changes: 21 additions & 6 deletions src/qnngds/cells.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ def device_cell(
port_map_x = device[0].pads.port_map_x
port_map_y = device[0].pads.port_map_y

max_x_num = max(num_pads_n, num_pads_s)
max_x_num = max(num_pads_n, num_pads_s, num_pads_e)
max_y_num = max(num_pads_e, num_pads_w)

if text is None:
Expand All @@ -368,8 +368,12 @@ def device_cell(
(2*max_x_num*(num_dev+1) * die_parameters.pad_size[0])
/ die_parameters.unit_die_size[0]
)

die_contact_w = USER_DEVICES.xsize + die_parameters.contact_l

default_contact_w = USER_DEVICES.xsize + die_parameters.contact_l
if default_contact_w < die_parameters.pad_size[0]:
die_contact_w = default_contact_w
else:
die_contact_w = die_parameters.pad_size[0]
dev_contact_w = USER_DEVICES.xsize
routes_margin = 4 * die_contact_w
dev_max_size = (
Expand All @@ -390,21 +394,32 @@ def device_cell(
ports=ports,
ports_gnd=ports_gnd,
text=f"{cell_text}",
probe_tip=device[0].pads.probe_tip,
num_devices= len(device)
)

if 'N' in ports:
side = 'N'
elif 'E' in ports:
side = 'E'

for i, ref in enumerate(refs):
x_offset = BORDER.ports[f"N{i+1}"].x
x_offset = BORDER.ports[f"{side}{i*max_x_num+1}"].x
if max_x_num > 1:
x_offset += die_parameters.pad_size[0]*i*cell_scaling_factor_x
x_offset = (x_offset + BORDER.ports[f"{side}{i*max_x_num+max_x_num}"].x)/2
if side == 'E':
x_offset -= device[0].pads.probe_tip.pad_length
ref.movex(x_offset)
for dev_port, pad_port in port_map_x.items():
print(f"{pad_port[0]}{max_x_num*i+pad_port[1]}")
USER_DEVICES.add_port(port=ref.ports[dev_port], name=f"{pad_port[0]}{max_x_num*i+pad_port[1]}")

## Route the nanowires and the die

# hyper tapers
taper_contact = min(dev_contact_w, device[0].pads.contact_w/2)
HT, dev_ports = utility.add_hyptap_to_cell(
BORDER.get_ports(), die_parameters.contact_l, dev_contact_w
BORDER.get_ports(), die_parameters.contact_l, taper_contact
)
FULL_DEVICES.ports = dev_ports.ports
FULL_DEVICES << HT
Expand Down
91 changes: 79 additions & 12 deletions src/qnngds/devices/nanowire.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import phidl.geometry as pg
from typing import Tuple, Optional

from qnngds.utilities import PadPlacement, QnnDevice
from qnngds.utilities import PadPlacement, QnnDevice, WireBond, MultiProbeTip


def spot(
Expand Down Expand Up @@ -51,6 +51,7 @@ def variable_length(
four_point_probe: bool = False,
layer: int = 1,
num_pts: int = 100,
rotation: float = -90
) -> QnnDevice:
"""Creates a single wire, made of two optimal steps from channel_w to
source_w with a constriction of the chosen length in the middle.
Expand Down Expand Up @@ -81,32 +82,98 @@ def variable_length(
constriction = NANOWIRE << line
gnd = NANOWIRE << wire
source.connect(source.ports[1], constriction.ports["top"])
constriction.connect(constriction.ports["bottom"], gnd.ports[1])
gnd.connect( gnd.ports[1], constriction.ports["bottom"])

NANOWIRE.rotate(rotation)
#NANOWIRE = pg.union(NANOWIRE, layer=layer)
NANOWIRE.add_port(name=1, port=source.ports[2])
NANOWIRE.add_port(name=2, port=gnd.ports[2])
NANOWIRE.rotate(-90)
if four_point_probe:
if four_point_probe and rotation == -90:
add_voltage_probe(NANOWIRE, channel_w)

nw_padplace = PadPlacement(
cell_scaling_factor_x= 2.5,
num_pads_n=2,
num_pads_s=2,
port_map_x={
1: ("N", 1),
1: ("N", 2),
2: ("S", 1),
3: ("N", 2),
3: ("N", 1),
4: ("S", 2)
}
)
},
probe_tip=WireBond()

)

'''turn = pg.optimal_90deg(source_w)
source_turn = NANOWIRE << turn
source_turn.connect(source_turn.ports[1], source.ports[2])
source_turn.mirror((0,0),(1,0))
flip_source = NANOWIRE << turn
flip_source.connect(flip_source.ports[2], source_turn.ports[2])
flip_source.rotate(180, center=flip_source.center+(-flip_source.xsize/2+source_w/2, -flip_source.ysize/2))
gnd_turn = NANOWIRE << turn
gnd_turn.connect(gnd_turn.ports[1], gnd.ports[2])
flip_gnd = NANOWIRE << turn
flip_gnd.connect(flip_gnd.ports[2], gnd_turn.ports[2])
flip_gnd.mirror((gnd_turn.xmin, gnd_turn.ymax), (gnd_turn.xmax, gnd_turn.ymax))
nw_padplace = PadPlacement(
cell_scaling_factor_x=1,
num_pads_n=0,
num_pads_s=0,
num_pads_e=4,
port_map_x={
1:("E", 3),
2:("E", 2),
3:("E", 4),
4:("E", 1)
},
probe_tip=MultiProbeTip()
)'''
NANOWIRE.add_port(name=1, port=source.ports[2])
NANOWIRE.add_port(name=2, port=gnd.ports[2])
#NANOWIRE.add_port(name=3, port=flip_source.ports[1])
#NANOWIRE.add_port(name=4, port=flip_gnd.ports[1])

elif four_point_probe and rotation == 0:

turn = pg.optimal_90deg(source_w)
source_turn = NANOWIRE << turn
source_turn.connect(source_turn.ports[1], source.ports[2])
source_turn.mirror((0,0),(1,0))
flip_source = NANOWIRE << turn
flip_source.connect(flip_source.ports[2], source_turn.ports[2])
flip_source.rotate(180, center=flip_source.center+(-flip_source.xsize/2+source_w/2, -flip_source.ysize/2))
gnd_turn = NANOWIRE << turn
gnd_turn.connect(gnd_turn.ports[1], gnd.ports[2])
flip_gnd = NANOWIRE << turn
flip_gnd.connect(flip_gnd.ports[2], gnd_turn.ports[2])
flip_gnd.mirror((gnd_turn.xmin, gnd_turn.ymax), (gnd_turn.xmax, gnd_turn.ymax))

nw_padplace = PadPlacement(
cell_scaling_factor_x=1,
num_pads_n=4,
num_pads_s=0,
port_map_x={
1:("N", 2),
2:("N", 3),
3:("N", 1),
4:("N", 4)
},
probe_tip=MultiProbeTip()
)
NANOWIRE.add_port(name=1, port=source_turn.ports[2])
NANOWIRE.add_port(name=2, port=gnd_turn.ports[2])
NANOWIRE.add_port(name=3, port=flip_source.ports[1])
NANOWIRE.add_port(name=4, port=flip_gnd.ports[1])
else:
nw_padplace = PadPlacement()
NANOWIRE.add_port(name=1, port=source.ports[2])
NANOWIRE.add_port(name=2, port=gnd.ports[2])

NANOWIRE.set_pads(nw_padplace)
NANOWIRE.move(NANOWIRE.center, (0, 0))
NANOWIRE.name = f"NANOWIRE.VAR(w={channel_w} l={constr_length})"
NANOWIRE.simplify(1e-2)
NANOWIRE.name = f"NANOWIRE.VAR(w={channel_w:.2f} l={constr_length:.1f})"
#NANOWIRE.simplify(1e-3)

return NANOWIRE

Expand Down
109 changes: 95 additions & 14 deletions src/qnngds/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,53 @@ def calculate_contact_w(
max_circuit_port_width = max([port.width for port in circuit_ports])
return max(max_circuit_port_width, self.contact_l)

class ConnectionToPCB():
def __init__(self,
pad_pitch,
pad_width,
pad_length,
contact_w = None):
self.pad_pitch = pad_pitch
self.pad_width = pad_width
self.pad_length = pad_length
if contact_w == None:
self.contact_w = pad_pitch/3
else:
self.contact_w = contact_w

class MultiProbeTip(ConnectionToPCB):
def __init__(self,
pad_pitch: Union[int, float] = 100,
pad_width = None,
pad_length = 200,
contact_w = None,
num_tips: int = 6,):
self.num_tips = num_tips
self.pad_pitch = pad_pitch
self.pad_length = pad_length
if contact_w == None:
self.contact_w = pad_pitch/3
else:
self.contact_w = contact_w
if pad_width == None:
self.pad_width = pad_pitch*3/4
else:
self.pad_width = pad_width

class WireBond(ConnectionToPCB):
def __init__(self,
pad_pitch: Union[int, float] = 200,
pad_width: Union[int, float] = 100,
pad_length: Union[int, float] = 200,
contact_w = None):
self.pad_pitch = pad_pitch
self.pad_width = pad_width
self.pad_length = pad_length
if contact_w == None:
self.contact_w = pad_width/3
else:
self.contact_w = contact_w

class PadPlacement:
def __init__(self,
cell_scaling_factor_x: Union[int, float] = 1,
Expand All @@ -206,9 +253,11 @@ def __init__(self,
num_pads_s: int = 1,
num_pads_e: int = 0,
num_pads_w: int = 0,
contact_w: Union[float, int, None] = None,
ports_gnd: List[Union[str, None]] = [None],
port_map_x: Dict[int, Tuple[str, int]] = {1:("N",1), 2:("S",1)},
port_map_y: Dict[int, Tuple[str, int]] = {}):
port_map_y: Dict[int, Tuple[str, int]] = {},
probe_tip: Union[None, MultiProbeTip] = WireBond()):

self.cell_scaling_factor_x = cell_scaling_factor_x
self.cell_scaling_factor_y = cell_scaling_factor_y
Expand All @@ -219,6 +268,12 @@ def __init__(self,
self.ports_gnd = ports_gnd
self.port_map_x = port_map_x
self.port_map_y = port_map_y
self.probe_tip = probe_tip

if contact_w == None:
self.contact_w = probe_tip.contact_w
else:
self.contact_w = contact_w

class QnnDevice(Device):
def set_pads(self, pads: PadPlacement = PadPlacement()):
Expand All @@ -236,6 +291,8 @@ def die_cell(
ports_gnd: List[str] = ["E", "S"],
text: str = "",
text_size: Union[None, int, float] = None,
probe_tip: Union[None, MultiProbeTip] = WireBond(),
num_devices = 1
) -> Device:
"""Creates a die cell with dicing marks, text, and pads to connect to a
device.
Expand All @@ -254,7 +311,7 @@ def die_cell(
Returns:
DIE (Device): The cell, with ports of width contact_w positioned around a device_max_size area.
"""

contact_w = probe_tip.contact_w
def offset(overlap_port):
port_name = overlap_port.name[0]
if port_name == "N":
Expand All @@ -279,30 +336,50 @@ def offset(overlap_port):
padOut = Device()

pad_block_size = (
die_size[0] - 2 * die_parameters.pad_size[1] - 4 * die_parameters.outline - die_parameters.xspace,
die_size[1] - 2 * die_parameters.pad_size[1] - 4 * die_parameters.outline - die_parameters.yspace,
die_size[0] - 2 * probe_tip.pad_length - 4 * die_parameters.outline - die_parameters.xspace,
die_size[1] - 2 * probe_tip.pad_length - 4 * die_parameters.outline - die_parameters.yspace,
)
inner_block = pg.compass_multi(device_max_size, ports)
outer_block = pg.compass_multi(pad_block_size, ports)
# standard pad definition for wirebonding
if type(probe_tip) == WireBond:
inner_block = pg.compass_multi(device_max_size, ports)
outer_block = pg.compass_multi(pad_block_size, ports)

# pad definition for specific probe tip
elif type(probe_tip) == MultiProbeTip:
unit_size = probe_tip.num_tips*probe_tip.pad_pitch
inner_block = pg.compass_multi((unit_size*num_devices, device_max_size[1]), ports)
outer_block = Device()
if 'N' in ports:
side = 'N'
elif 'E' in ports:
side = 'E'
ports_per_dev = int(ports[side]/num_devices)
block_i = pg.compass_multi((unit_size, pad_block_size[1]/2), {side:probe_tip.num_tips})
for i in range(num_devices):
ref = outer_block << block_i
ref.center = [-unit_size*num_devices/2 + (i+1/2)*unit_size, 0]
for j in range(ports_per_dev):
port = list(ref.ports.values())[j]
outer_block.add_port(f"{side}{i*ports_per_dev+j+1}", port=port)

inner_ports = list(inner_block.ports.values())

for i, port in enumerate(list(outer_block.ports.values())):

CONNECT = Device()
port.rotate(180)
# create the pad
pad = pad_with_offset(die_parameters)
pad = pad_with_offset(die_parameters, probe_tip)
pad.add_port(
"1",
midpoint=(die_parameters.pad_size[0] / 2, 0),
width=die_parameters.pad_size[0],
midpoint=(probe_tip.pad_width / 2, 0),
width=probe_tip.pad_width,
orientation=90,
)
pad_ref = CONNECT << pad
pad_ref.connect(pad.ports["1"], port)

# create the route from pad to contact
port.width = die_parameters.pad_size[0]
port.width = probe_tip.pad_width
inner_ports[i].width = contact_w
CONNECT << pr.route_quad(port, inner_ports[i], layer=die_parameters.die_layer)

Expand Down Expand Up @@ -416,14 +493,18 @@ def offset(overlap_port):
return DIE


def pad_with_offset(die_parameters: DieParameters = DieParameters()):
def pad_with_offset(die_parameters: DieParameters = DieParameters(), probe_tip = None):
DEVICE = Device()
if probe_tip == None:
pad_size = die_parameters.pad_size
else:
pad_size = (probe_tip.pad_width, probe_tip.pad_length)
outer_pad = pg.rectangle(
die_parameters.pad_size,
pad_size,
layer=die_parameters.die_layer,
)
inner_pad = pg.rectangle(
[dim - die_parameters.pad_tolerance for dim in die_parameters.pad_size],
[dim - die_parameters.pad_tolerance for dim in pad_size],
layer=die_parameters.pad_layer,
)
inner_pad.center = outer_pad.center
Expand Down

0 comments on commit a6fce0f

Please sign in to comment.