Forum Replies Created
-
AuthorPosts
-
Ronald
KeymasterDear gpsar3,
The nd.strt() and nd.bend() functions are a bit of shortcut functions to get started quickly.
If you need interconnect functionality using strt, bend and other waveguides you get a lot of extra milage when using them in as part of the Interconnect class:Interconnect remember defaults like xs, width and radius and can be fine tuned.
Structures GS or GSG lines you can define as a single interconnect as demonstrated here
Ronald
Ronald
KeymasterDear Iomuc,
A list object can not be used in Python as a dictionary key, as a list is mutable (it can change) after creation, like append, insert, pop etc. Only immutable objects can be hashed and serve as a key.
Ronald
21 May 2023 at 16:36 in reply to: Minimum Bending Radius and Use Cases for Point-to-Point Interconnects #6977Ronald
KeymasterDear Iomuc,
The Interconnects take their minimum bend radius from the xsection attribute minimum_radius.
If you create an Interconnect you provide a xsection by its name with the “xs” keyword.To set the minimum radius to the xsection and create an Interconnect:
xsname = “xsname”
xs_object = nd.get_xsection(xsname) # or create a new xs: nd.add_xsection(xsname)
xs.minimum_radius = 100
ic – nd.Interconnect(xs=xsname)If you violate the minimum_radius, then you will get a log message.
For more convenient log message handling activating the log files, see https://nazca-design.org/logging-2/Using Interconnects has a bit more memory overhead than not using them, but the overhead is to keep track of default widths, radius, etc., as well as the more convenient point2point connections. Hence, best practice is to always use Interconnects for mask design instead of directly using mask_elements. Using mask_elements directly can be useful, however, for quick tests or tutorials.
Ronald
Ronald
KeymasterDear Peter,
A PDK can be seen as a module under Nazca Design under its original license. Demofab PDK is part of the Nazca installer on the website as this make the demofab distribution a single install.
To create your separate PDK file that installs as a Python package, you need to make a Python install file as for any Python module/package.
As for the building blocks, you indeed need to add them to pdk_30_BB_library.py for best practice.
Ronald
Ronald
KeymasterDear bhchoi98,
I think this was solved in the answer to Pervaiz:
If you follow the installation on https://nazca-design.org/installation/ you should be fine.
“pip install nazca” is an unrelated package to Nazca Design.
Ronald
Ronald
KeymasterRonald
KeymasterDear Tommaso,
Thank you for the wishes.
Interconnects are based on (curved) lines. A line can be assigned a (varying) thickness and thus becomes a waveguide (or metal line). Lines can be described in countless ways. Compare for example a curved Nazca cobra interconnect with a straight line. Finding positions along a line at equal distance requires in generic terms a parameterized description of the line [x(t), y(t)] that traces the line at a constant speed w.r.t. to parameter t. However, it is unlikely that the length of an arbitrary line is linear in t by default. For that to happen we would need condition “A” to be true:
A: (dx(t)/dt)^2 + (dy(t)/dt)^2) = C^2,
where C is a constant and C * dt describes the (local) distance between points on the line.
Fortunately, we can enforce condition “A” across the whole line having the same C by defining a new parameter s and function t = f(s) such that we obtain condition “B” for all s:
B: (dx(f)/df * df(s)/ds)^2 + (dy(f)/df * df(s)/ds)^2 = C^2.
If we consider an interconnect to be a series of N connected (and possibly different) line definitions (e.g. bend, straight, straight, bend etc), indexed as i = 0, … N-1, we need to create a function fi(s) for each line such that all N lines now have the same C value under condition “B”. Here we assume f(s) covers the full t domain monotonously. We also need to know the length of each line in the series to get to the same C along the whole interconnect. In short, we need to reparameterize all interconnect elements from t to s following “B”.
Obtaining exact positions along the interconnect may have varying difficulty levels depending on the line type. A straight line of length L described as x(t) = t * L, where t in [0, 1] will have C = L under condition “A”. If C is no longer free to choose we apply condition “B” and use t = f(s) = a * s and obtain L * a = C and find a = C / L.
Hence, the linearized and normalized “constant speed” parametric straight line in s can be expressed as
x(s) = s * C, where s in [0, L / C].Similarly, for an arc bend defined by [R*cos(phi*t), R*sin(phi*t)], with t in [0, 1] and reparametrization with t = f(s) = a * s we obtain a = C /(phi * R) and the linearized and normalized “constant speed” parametric bend in s becomes
[x(s), y(s)] = [R*cos(s*C/R), R*sin(s*C/R)], for s in [0, phi*R/C]For curves without an analytical solution we would need to resort to a (ideally generic) numerical solution to reparametrize them.
Still, when placing elements along the interconnect, as you describe in your question, one needs to be able access all the interconnect elements (line definitions) in the first place, and calculate [x(s), y(s)] for a certain list of s values. The good news is that Nazca already has been testing since last summer an interconnect upgrade which is a dict-based description of all the elements in an interconnect (versions > 0.5.13). Hence, you can simply scan through the those line elements, apply the reparametrization as described above, and generate equidistant positions along the interconnect. More elegantly, one would create an output terminal that generates these positions when provided with the interconnect dict as input. The default terminal generates gds layout. In addition, a final solution would store reparametrization options inside the interconnect line definitions from the start, or as a generic filter function. Reparameterization is also already an ongoing development in Nazca for other purposes. The website release is a bit overdue, not because little has happened, but rather the opposite.
Ronald
Ronald
KeymasterIn the case of merging polygons in a cell, the cell_iter() can help out, like described in the example below. The merge_cell_polygons function returns a new cell with the merged result, although the pyclipper may not handle all cases as desired for GDS.
import nazca as nd from collections import defaultdict def merge_cell_polygons(cell): """Flatten a cell and merge polygons per layer. Args: cell (Cell): cell to flatten and merge polygon of. Returns: Cell: flattened cell with merged polygons per layer. """ layerpgons = defaultdict(list) for P in nd.cell_iter(cell, flat=True): if P.cell_start: for pgon, xy, bbox in P.iters['polygon']: layerpgons[pgon.layer].append(xy) with nd.Cell(name=f"{cell.cell_name}_merged") as C: for layer, pgons in layerpgons.items(): merged = nd.clipper.merge_polygons(pgons) for pgon in merged: nd.Polygon(points=pgon, layer=layer).put(0) return C with nd.Cell('test') as my_cell: # add content merged_cell = merge_cell_polygons(my_cell) merged_cell.put()
Ronald
Ronald
KeymasterDear Neetesh,
The picture helps, thanks. This can be approached analogous to the thread remove holes.
Note that if you intend to draw just polygon shapes rather than circuit elements, it is better to use nd.Polygon() objects in Nazca and the nd.geometries module for geometry helper functions, instead of nd.strt() and nd.bend() structures. In the former case, one can manipulate polygons directly with the nd.pyclipper module. Also check out the merge_polygons function in the inverted MMI tutorial.
In the “bend and strt” case you first have to pry the polygons out of the bend or strt cells. Note also that bends and strt are circuit elements, which have a much larger overhead associated with them. This becomes more noticeable when you use (very) large numbers of them.
Ronald
Ronald
KeymasterDear Milan,
You can try
print(nd.get_xsection('myXS').mask_layers)
to see layer info for ‘myXS’.
It returns a Pandas DataFrame like this:xsection layer datatype tech ... growy1 growy2 accuracy polyline layer_name ... layer1 myXS 1 0 None ... 0.0 0.0 0.001 0.0 NoFill myXS 2 0 None ... 0.0 0.0 0.100 0.0
Ronald
Ronald
KeymasterDear Dominik,
You are correct. Thanks for adding that. The cell_iter() by default will not drill down identical branch signatures more than once. In case you would like to visit all branch copies, e.g. to find all instances, the revisit=True will indeed visit the whole cell_tree. Hence, revisit identical branch signatures, mounted on different parts of the celltree. Another way to visit all instances is to use cell_iter(…, flat=True) or cell_iter(…, hierarchy=’flat’).
Ronald
Ronald
KeymasterDear Dominik,
The cell_iter() can be of help. It can locate all elements in a celltree with respect to a topcell.
Place this before nd.export_gds():for params in nd.cell_iter(cell_top): if params.cell_start: # check if we are on a cell_open pass (or check for a cell_close). if params.cell.cell_name == "Device": # check if we are in the right cell pointer, flip = params.transflip_glob # get cell's translation (pointer) and flip w.r.t. cell_top for name, pin in params.cell.pin.items(): # print all pin names and locations print(name, pointer.copy().move_ptr(pin.pointer))) # add pin position in the cell to the cell translation. # output: org Pointer: (x = 0.00, y = 20.00, a = 0.00°) gc1 Pointer: (x = 0.00, y = 20.00, a = 180.00°) gc2 Pointer: (x = 100.00, y = 20.00, a = 180.00°) a0 Pointer: (x = 0.00, y = 20.00, a = 360.00°) b0 Pointer: (x = 0.00, y = 20.00, a = 360.00°)
The .copy() is needed to not change the original pointer with the .move_ptr().
The ‘org’ pin is always created as cell origin.
The ‘a0’ and ‘b0’ pins are default Nazca pins. You can get rid of those using cell_device.default_pins(‘gc1’, ‘gc2’) inside the cell_device cell definition.Ronald
Ronald
KeymasterDear Milan,
A “NoFill” layer to protect from tiling is a specific layer. You can add that to your xsection definition and use interconnects. Whenever you use that interconnect your get the NoFill layer.
In another case, when working directly with Polygon objects, you can grow the Polygon (with pyclipper installed) and redirect the result to the NoFill layer.
The above solutions look like this when applied to the MMI polygon tutorial:
import nazca as nd grow = 5.0 # NoFill clearance # create a layers and xsections: nd.add_layer(name='layer1', layer=1, accuracy=0.001) nd.add_layer(name='NoFill', layer=2, accuracy=0.1) # NoFill can be course, here 0.1 um resolution nd.add_xsection(name='myXS') nd.add_layer2xsection(xsection='myXS', layer='layer1') nd.add_layer2xsection(xsection='myXS', layer='NoFill', growx=grow) # add a NoFill layer ic = nd.interconnects.Interconnect(xs="myXS", width=2.0, radius=10.0) # create interconnect # create a building block (cell) from Polygon points and add the NoFill layer with nd.Cell('building_block') as bb: bb_body = [ (5.0, -5.0), (5.0, -1.0), (0.0, -1.0), (0.0, 1.0), (5.0, 1.0), (5.0, 5.0), (35.0, 5.0), (35.0, 3.5), (40.0, 3.5), (40.0, 1.5), (35.0, 1.5), (35.0, -1.5), (40.0, -1.5), (40.0, -3.5), (35.0, -3.5), (35.0, -5.0) ] poly = nd.Polygon(points=bb_body, layer='layer1') poly.put(0) poly.grow(layer='NoFill', grow=grow).put(0) # add NoFill layer based on original Polygon. nd.Pin('a0').put(0, 0, 180) nd.Pin('b0').put(40, 2.5, 0) nd.Pin('b1').put(40, -2.5, 0) nd.put_stub() # draw MMIs and interconnects and automatically get NoFill layers: bb.put(0) mmi = bb.put('b1') ic.strt(length=10).put(mmi.pin['a0']) ic.bend(angle=90).put() nd.export_gds(filename="nofill")
A less surgical alternative is to use the cell’s bounding box and fill it with the NoFill layer (not shown in the example above).
Ronald
Ronald
KeymasterDear sinobadm,
Xsections are global objects. As such you do not have to add them to cells as a xsection before you can use them. You can just use them when they are defined, see xsections and layers.
Note that Polygon objects, as used in the example you refer to, do not work with xsections. They work with layers.
Conceptually a xsection is mostly applied to represent a waveguide or metal guide, consisting of one or more layers, captured in a xsection. Polygons are more free form geometries for drawing, like when used in a BB as in the example.
Ronald
20 October 2021 at 16:43 in reply to: nm-scale offset using Tp_viper or curve2polyline pin to pin at an angle #6602Ronald
KeymasterDear Cecil,
When using gds hierarchy under (no multiple of 90 degree) angles or in between grid-point translations, the snapping errors (better: delta’s) you see are fundamentally caused by looking at two non-aligned discrete coordinate systems on top of each other.
Interconnects within a single cell are by default not instantiated and snap to the same parent grid. In contrast, the Vipers are now child cells in your example, each in their own grid system. If you flatten topcell “nazca” in your example you will enforce a single parent grid to snap to for all structures in cell “nazca”.
Ronald
6 October 2021 at 00:32 in reply to: Avoiding/fixing waveguide mismatch warnings on GDS layer 1500 #6595Ronald
KeymasterDear Jon,
The interconnect error flags in layer 1500 can be fine tuned to have zero false positives. They are essential for design validation in my experience. That said, there is no full description I can point to yet.
Basically, the interconnection DRC can check in pin2pin connections for xs, width, angle, radius and symmetry violations. Each of those can be fine tuned, e.g. for metal xsections it is safe to switch of width DRC, or to allow metal of xsection type-1 to connect to metal of xsection type-2.
In case you connect two different xsections or widths on purpose, like the tapers you mention, you can use the move() method on a pin to indicate this is desired, e.g. for a Node (pin) variable p you can do p.move(width=7.0) and set up a new pin width of 7.0 for the DRC, or, similarly p.move(xs=’newxs’). This “moves” pin properties in a similar (syntax) way as moving x, y and a coordinates. A quick and dirty way is to silence all pin2pin DRC in a specific placement is to use .put(…, drc=False), but you may now hide true positives as well.
Ronald
Ronald
KeymasterDear ale35,
It would be a bad habit to change a cell after creation, nor should it be needed in any normal design situation. It’s a bit like the Terminator movies, as you are changing history.
Technically, it is possible to update a cell after closure (and there are some nifty use cases), but it is advised to not do this as you may create inconsistencies and, again, most likely it is not needed.
Could you indicate why cell1 needs an update with cell2 after closure, in order to find an alternative solution?
Ronald
Ronald
KeymasterAs a second option you can flatten the celltree during iteration. The “xy” in the polygon iterator will always return the coordinates with respect to the first instantiated parent, which is the top cell in the iteration, here “trench”, because flat=True. In the example below I now use params.cell_start instead of params.cell_open, as the former will be True on any cell processed (instantiated or not), whereas the latter is only True if the cell will be instantiated. Depending on your needs cell_open or cell_start could be more useful, but here cell_open will not do the job due to the flat=True, which results in an instantiate=False state. As explained in my previous post you could even omit the check for cell_start completely here.
import nazca as nd nd.clear_layers() nd.add_layer(name='tr', layer=2) with nd.Cell(instantiate=True, name='trench') as trench: nd.strt(length=5, width=5, layer='tr').put(0, 0) nd.strt(length=3, width=7, layer='tr').put() trenchInstance = trench.put(0, -10, 10) # search the trench cell for polygons polygons = [] for params in nd.cell_iter(trench, flat=True): if params.cell_start: for pgon, xy, bbox in params.iters['polygon']: if pgon.layer == 'tr': polygons.append(xy) for polygon in polygons: nd.Polygon(points=polygon, layer='tr').put(0) nd.export_gds(filename='forum_problem.gds')
Ronald
Ronald
KeymasterDear garbagebag,
The example below should do what you described (extracting polygons and their position w.r.t. a top cell in a celltree).
The params is a namedtuple that contains both the translation and flip state of a cell instantiation with respect to its direct parent, as well as with respect to the top cell that is iterated over, i.e. “trench” in your example. The namedtuple keys to look for are transflip_loc and transflip_glob. Both are themselves a tuple of the form (Pointer, flipstate). Here “trans” stands for translation (in x, y, and a).
Because the iterator will initiate a loop on cell opening and again on cell closure, I added a check for some actions on cell_open is True only, in the example below, and save the xya position in the same manner as you did for the polygon points. Note that on cell closure the polygon iterator is an empty list, so in your initial example everything works just fine without checking for a cell_open condition.
import nazca as nd nd.clear_layers() nd.add_layer(name='tr', layer=2) with nd.Cell(instantiate=True, name='trench') as trench: nd.strt(length=5, width=5, layer='tr').put(0, 0) nd.strt(length=3, width=7, layer='tr').put() trenchInstance = trench.put(0, -10, 10) # search the trench cell for polygons polygons = [] xya = [] for params in nd.cell_iter(trench, flat=False): if params.cell_open: trans, flip = params.transflip_glob for pgon, xy, bbox in params.iters['polygon']: if pgon.layer == 'tr': polygons.append(xy) xya.append(trans.xya()) for polygon, xya in zip(polygons, xya): nd.Polygon(points=polygon, layer='tr').put(xya) nd.export_gds(filename='forum_problem.gds')
Ronald
Ronald
KeymasterDear Jon,
A correct observation. This has been fixed a while ago in the development version and will make it in the new public Nazca release. That release will also have more advanced Euler bends that by default fix euler-bend scaling as well as provide automated transitions to arc-bends if a specified mininum bend radius is reached. The updated Euler will also use a better discretisation setting for the specified mask resolution.
Ronald
-
AuthorPosts