Basic usage of experiment.generate
qnngds includes the capability to automatically generate an experiment from a device or circuit and pads.
experiment.generate will automatically taper and route between the device/circuit and pads.
Additionally, experiment.generate will automatically handle outlining for positive-tone ebeam resist exposure patterns.
This example will cover basic usage of experiment.generate for a single layer, negative-tone ebeam or photolitho process.
First, define our imports:
1import qnngds as qg
2import phidl.geometry as pg
3from phidl import quickplot as qp
4from functools import partial
Note
If running non-interactively, e.g. with $ python ./my_script.py, add the following lines
from phidl import set_quickplot_options
set_quickplot_options(blocking=True)
Now, we’ll set up our PDK.
1ls = qg.LayerSet()
2ls.add_layer(qg.Layer(name="PHOTO", gds_layer=1))
3cross_sections = dict(
4 photo=partial(qg.geometries.default_cross_section, layer="PHOTO"),
5)
6layer_transitions = qg.layer_auto_transitions(ls)
7PDK = qg.Pdk(
8 "single_layer_pdk",
9 layers=ls,
10 cross_sections=cross_sections,
11 layer_transitions=layer_transitions,
12)
13PDK.activate()
We’ve defined a single layer named "PHOTO" with GDS layer/datatype 1/0 (gds_datatype = 0 is the default argument for the Layer constructor).
We also defined a default routing cross section on the same layer and set up auto layer transitions to be used by the router.
Now, we can set up the inputs to experiment.generate.
We’re setting up a ntron.sharp with default arugments except for the layer, which is PHOTO.
In addition, we’re using utilities.extend_ports to add an optimal step taper between the nTron and routing.
Note
Here, we’re using functools.partial
to specify the end_width, symmetric and layer keyword arguments for the extension optimal step taper.
the use of partial here is important, since utilities.extend_ports will instantiate a
different taper for each port to autosize the taper start width so it matches the ntron.
For more on how to use partial with qnngds, see _best_practices.
1ntron = qg.devices.ntron.sharp(layer="PHOTO")
2ext = partial(pg.optimal_step, end_width=1, symmetric=True, layer=qg.get_layer("PHOTO"))
3dut = qg.utilities.extend_ports(
4 device=ntron, port_names=["g", "s", "d"], extension=ext, auto_width=True
5)
6pad_array = qg.pads.array_single(
7 pad_specs=(qg.pads.stack(size=(200, 200), layers=("PHOTO",)),),
8 columns=1,
9 rows=3,
10 pitch=250,
11)
12route_groups = (
13 qg.experiment.RouteGroup(qg.get_cross_section("photo"), {"g": 2, "s": 1, "d": 3}),
14)
Note
Note that we have to call qg.get_layer() when passing a named layer to a phidl function,
since phidl expects a tuple, int, or phidl Layer and has no concept of the
subclass Layer used by qnngds.
And supply these inputs to experiment.generate and plot the output:
1c = qg.experiment.generate(
2 dut=dut,
3 pad_array=pad_array,
4 label=None,
5 route_groups=route_groups,
6 dut_offset=(350, 250),
7 pad_offset=(0, 0),
8 label_offset=(0, 0),
9 retries=1,
10)
11qp(c)
Zooming in on the nTron:
Reference
1import qnngds as qg
2import phidl.geometry as pg
3from phidl import quickplot as qp
4from functools import partial
5
6ls = qg.LayerSet()
7ls.add_layer(qg.Layer(name="PHOTO", gds_layer=1))
8cross_sections = dict(
9 photo=partial(qg.geometries.default_cross_section, layer="PHOTO"),
10)
11layer_transitions = qg.layer_auto_transitions(ls)
12PDK = qg.Pdk(
13 "single_layer_pdk",
14 layers=ls,
15 cross_sections=cross_sections,
16 layer_transitions=layer_transitions,
17)
18PDK.activate()
19ntron = qg.devices.ntron.sharp(layer="PHOTO")
20ext = partial(pg.optimal_step, end_width=1, symmetric=True, layer=qg.get_layer("PHOTO"))
21dut = qg.utilities.extend_ports(
22 device=ntron, port_names=["g", "s", "d"], extension=ext, auto_width=True
23)
24pad_array = qg.pads.array_single(
25 pad_specs=(qg.pads.stack(size=(200, 200), layers=("PHOTO",)),),
26 columns=1,
27 rows=3,
28 pitch=250,
29)
30route_groups = (
31 qg.experiment.RouteGroup(qg.get_cross_section("photo"), {"g": 2, "s": 1, "d": 3}),
32)
33c = qg.experiment.generate(
34 dut=dut,
35 pad_array=pad_array,
36 label=None,
37 route_groups=route_groups,
38 dut_offset=(350, 250),
39 pad_offset=(0, 0),
40 label_offset=(0, 0),
41 retries=1,
42)
43qp(c)