Home Forums Nazca Clipping check, distance and length for interconnects

Viewing 6 posts - 1 through 6 (of 6 total)
• Author
Posts
• #6061
iv_tern
Member

Hello all,

Had a bunch of questions about interconnects and sanity checks, that I have lumped together, apologies if this poses difficulty.

I was wondering if there is any way of checking whether an interconnect clips onto a pre-existing feature? I tried playing around with clipper, but it seemed to always return an empty list (have to confirm this, as I can’t use my nazca machine for a day or two). Similarly, is there an implemented way of checking the minimum distance between two features? Finally, is there a way to check the overall length of an interconnect?

#6063
Ronald
Keymaster

Dear iv_tern,

When you place elements (cells) Nazca checks on all of the pins of the element if they are closer than a small value epsilon (typically < 1 nm) from an existing pin in the parent cell. In that case Nazca internally creates a connection between all matching pins for a complete circuit netlist. The connections are validated against a number of conditions, like the pin width, xsection and symmetry. Note this is on pins, not an arbitrary feature of the polygons or polylines; It is about circuit connectivity in this context, not geometrical drawings.

The same feature can be used for snapping (useful in a drag & drop environment) by using a larger epsilon and let the placement follow the existing pin if close enough and if all the other required pin properties (apart from spatial proximity) match according to a customizable set of rules. It needs some small adaptations in the code to be able to switch from proximity based in-place connections to snapping connections depending on the situation (script vs mouse).

The distance between two pins can be obtained as follows

`x, y, a = nazca.diff(pin1, pin2)`

Some other features in this context may be useful, e.g. getting the position in between two pins:

``````x, y, a = nazca.midpoint(pin1, pin2)
pin = nazca.midpointer(pin1, pin2)``````

The geometrical length of interconnects can be obtained by starting a “trace”:

``````import nazca as nd

nd.trace.trace_start()
ic.strt(length=200).put(0)
ic.sbend(offset=100).put()
ic.bend(angle=45).put()
nd.trace.trace_stop()
length = nd.trace.trace_length()
print(length)``````

The trace does not check if elements are connected.

Instead of the trace method you can use the pathfinder, which returns all connected paths and their geometrical lengths, starting in the pin provided to the “start” keyword.

``````import nazca as nd

e1 = ic.strt(length=200).put(0)
ic.sbend(offset=100).put()
ic.bend(angle=45).put()
nd.pathfinder(start=e1.pin['a0'])``````

Note that some parametric interconnect elements may not have a path length yet.

Ronald

#6075
iv_tern
Member

One final question, if that’s ok – is there any way to get the coordinates of all the points in an interconnect (i.e. where in the object properties as those stores?) I.e. doing the reverse of the tutorial example of placing a polygon, where we give a list of points and then use nd.Polygon(points=pts, layer=’layer’).put(coords).

Let me know if this isn’t clear, and I can try to elaborate.

#6084
Ronald
Keymaster

Dear iv_tern,

The properties are accessible through the Cell objects, which are themselves in a tree structure.

If you want to see the polygons (if I interpret your question well) you can iterate over all cells under a specific cell and in each cell iterate over all polygons. The method cell_iter() is available for that as demonstrated in the example below.

``````import nazca as nd
import nazca.demofab as demo

cell_1 = demo.deep.strt(length=10, width=5)

print("points in original polygon:")
for NT in nd.cell_iter(cell_1):  # NT is a named tuple object
if NT.cell_start:
print(f"\ncell: '{NT.cell.cell_name}'")
for i, (pgon, points, bbox) in enumerate(NT.iters['polygon']):
print(f"{i}: points: {pgon.points}, layer: '{pgon.layer}'")``````

In the line (pgon, points, bbox), the “pgon” refers to the original polygon object, the “points” to the polygon translated w.r.t. the top cell provided to cell_iter, and “bbox” the translated bounding box.

Ronald

#6090
iv_tern
Member

Dear Ronald, thank you very much for your prompt response – this is almost what I’m looking for.

If I run the following code:

``````import nazca as nd

nd.clear_layers()
nd.clear_layout()

toy_points1=[(0,0), (0,7), (10, 14), (0,25)]
toy_points2=[(0,0), (0,15), (10, 14), (0,20)]

with nd.Cell('toy_poly1') as tp1:
toy_polygon1=nd.Polygon(points=toy_points1, layer=1).put(0)
nd.Pin('a0').put(0, 0, 180)
nd.Pin('b0').put(10, 2.5, 0)

with nd.Cell('toy_poly2') as tp2:
toy_polygon2=nd.Polygon(points=toy_points2, layer=1).put(0)
nd.Pin('a0').put(0, 0, 180)
nd.Pin('b0').put(10, 2.5, 0)

p1 = tp1.put(0,0,0)
p2 = tp2.put(40,40,90)

print("points in original polygon:")
for NT in nd.cell_iter(p1):  # NT is a named tuple object
if NT.cell_start:
print(f"\ncell: '{NT.cell.cell_name}'")
for i, (pgon, points, bbox) in enumerate(NT.iters['polygon']):
print(f"{i}: points: {pgon.points}, layer: '{pgon.layer}'")

print("points in original polygon:")
for NT in nd.cell_iter(p2):  # NT is a named tuple objec
if NT.cell_start:
print(f"\ncell: '{NT.cell.cell_name}'")
for i, (pgon, points, bbox) in enumerate(NT.iters['polygon']):
print(f"{i}: points: {pgon.points}, layer: '{pgon.layer}'")``````

I get this output:

``````points in original polygon:

cell: 'toy_poly1'
0: points: [(0, 0), (0, 7), (10, 14), (0, 25)], layer: '1/0/None'
points in original polygon:

cell: 'toy_poly2'
0: points: [(0, 0), (0, 15), (10, 14), (0, 20)], layer: '1/0/None'``````

Whereas I’m looking for the output, that takes into account the fact that p2 has been .put at 40,40 and rotated 90. I.e. I’m interested in the points with the origin of the entire mask, as opposed to the origin of the top cell, if this makes sense.

Regards,
iv_tern

#6091
Ronald
Keymaster

Dear iv_tern,

print `points` rather than `pgon.points` for the polygon coordinates with respect to the first instantiated parent cell, which maximaly goes up to the cell provided to cell_iter(), the “topcell” in this context.

To get the polygon with respect to the topcell, regardless of any cell instantiation, flatten the hierarchy with `flat=True` or `hierarchy='flat'`.

In your case you are looking for the position with respect to the “nazca” top cell, which is always created as a blank canvas when importing nazca.

– nazca
.. – tp1
…. – polygon1
.. – tp2
…. – polygon2

That “nazca” topcell can be accessed as nd.cfg.defaultcell and you get:

``````for NT in nd.cell_iter(nd.cfg.defaultcell, flat=True):
if NT.cell_start:
print(f"\ncell: '{NT.cell.cell_name}'")
for i, (pgon, points, bbox) in enumerate(NT.iters['polygon']):
print(f"{i}: points: {points}, layer: '{pgon.layer}'")``````

output:

``````cell: 'nazca'

cell: 'toy_poly2'
0: points: [(40.0, 40.0), (25.0, 40.0), (26.0, 50.0), (20.0, 40.0)], layer: '1/0/None'

cell: 'toy_poly1'
0: points: [(0, 0), (0, 7), (10, 14), (0, 25)], layer: '1/0/None'``````

``````nd.with(name='topcell') as top:
p1 = tp1.put(0, 0, 0)
p2 = tp2.put(40, 40, 90)``````

and use `nd.cell_iter(cell=top, flat=True)` to get the coordinates as before and `nd.export_gds(top)` in case you export cell “top” to gds.

Ronald

Viewing 6 posts - 1 through 6 (of 6 total)
• You must be logged in to reply to this topic.