From 8d71a98ce94e07ef8b3f8ad7239ba0618cf76fe7 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 22 Jun 2021 14:40:43 -0700 Subject: [PATCH] Make purposes argument to gdsMill. Create prefixGDS.py script. --- compiler/base/hierarchy_layout.py | 50 ++++++++++++++------------ compiler/gdsMill/gdsMill/gds2reader.py | 6 ++-- compiler/gdsMill/gdsMill/vlsiLayout.py | 22 +++++++----- compiler/prefixGDS.py | 21 +++++++++++ 4 files changed, 65 insertions(+), 34 deletions(-) create mode 100755 compiler/prefixGDS.py diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 22af8373..8bbc72c7 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -20,6 +20,10 @@ from globals import OPTS from vector import vector from pin_layout import pin_layout from utils import round_to_grid +try: + from tech import special_purposes +except ImportError: + special_purposes = {} class layout(): @@ -36,9 +40,9 @@ class layout(): # This gets set in both spice and layout so either can be called first. self.name = name self.cell_name = cell_name - + self.gds_file = OPTS.openram_tech + "gds_lib/" + cell_name + ".gds" - + self.width = None self.height = None self.bounding_box = None # The rectangle shape @@ -58,7 +62,7 @@ class layout(): self.visited = [] # Flag for library cells self.is_library_cell = False - + self.gds_read() try: @@ -66,7 +70,7 @@ class layout(): self.pwr_grid_layer = power_grid[0] except ImportError: self.pwr_grid_layer = "m3" - + ############################################################ # GDS layout ############################################################ @@ -118,7 +122,7 @@ class layout(): if len(self.objs) > 0: lowestx = min(min(obj.lx() for obj in self.objs if obj.name != "label"), lowestx) lowesty = min(min(obj.by() for obj in self.objs if obj.name != "label"), lowesty) - + if len(self.insts) > 0: lowestx = min(min(inst.lx() for inst in self.insts), lowestx) lowesty = min(min(inst.by() for inst in self.insts), lowesty) @@ -129,7 +133,7 @@ class layout(): continue lowestx = min(min(pin.lx() for pin in pin_set), lowestx) lowesty = min(min(pin.by() for pin in pin_set), lowesty) - + return vector(lowestx, lowesty) def find_highest_coords(self): @@ -138,7 +142,7 @@ class layout(): this layout """ highestx = highesty = -sys.maxsize - 1 - + if len(self.objs) > 0: highestx = max(max(obj.rx() for obj in self.objs if obj.name != "label"), highestx) highesty = max(max(obj.uy() for obj in self.objs if obj.name != "label"), highesty) @@ -153,7 +157,7 @@ class layout(): continue highestx = max(max(pin.rx() for pin in pin_set), highestx) highesty = max(max(pin.uy() for pin in pin_set), highesty) - + return vector(highestx, highesty) def find_highest_layer_coords(self, layer): @@ -234,7 +238,7 @@ class layout(): # This is commented out for runtime reasons # debug.info(4, "instance list: " + ",".join(x.name for x in self.insts)) return self.insts[-1] - + def get_inst(self, name): """ Retrieve an instance by name """ for inst in self.insts: @@ -332,7 +336,7 @@ class layout(): Return the pin or list of pins """ name = self.get_pin_name(text) - + try: if len(self.pin_map[name]) > 1: debug.error("Should use a pin iterator since more than one pin {}".format(text), -1) @@ -349,7 +353,7 @@ class layout(): Return a pin list (instead of a single pin) """ name = self.get_pin_name(text) - + if name in self.pin_map.keys(): return self.pin_map[name] else: @@ -360,12 +364,12 @@ class layout(): Create a mapping from internal pin names to external pin names. """ self.pin_names = pin_dict - + self.original_pin_names = {y: x for (x, y) in self.pin_names.items()} def get_pin_name(self, text): """ Return the custom cell pin name """ - + if text in self.pin_names: return self.pin_names[text] else: @@ -373,7 +377,7 @@ class layout(): def get_original_pin_names(self): """ Return the internal cell pin name """ - + # This uses the hierarchy_spice pins (in order) return [self.get_original_pin_name(x) for x in self.pins] @@ -383,7 +387,7 @@ class layout(): return self.original_pin_names[text] else: return text - + def get_pin_names(self): """ Return a pin list of all pins @@ -480,7 +484,7 @@ class layout(): offset=s.ll(), width=s.width(), height=s.height()) - + def replace_layout_pin(self, text, pin): """ Remove the old pin and replace with a new one @@ -495,7 +499,7 @@ class layout(): offset=pin.ll(), width=pin.width(), height=pin.height()) - + def add_layout_pin(self, text, layer, offset, width=None, height=None): """ Create a labeled pin @@ -777,7 +781,7 @@ class layout(): debug.info(3, "opening {}".format(self.gds_file)) self.gds = gdsMill.VlsiLayout(units=GDS["unit"]) reader = gdsMill.Gds2reader(self.gds) - reader.loadFromFile(self.gds_file) + reader.loadFromFile(self.gds_file, special_purposes) else: debug.info(3, "Creating layout structure {}".format(self.name)) self.gds = gdsMill.VlsiLayout(name=self.name, units=GDS["unit"]) @@ -789,7 +793,7 @@ class layout(): debug.info(4, "Printing {}".format(gds_file)) arrayCellLayout = gdsMill.VlsiLayout(units=GDS["unit"]) reader = gdsMill.Gds2reader(arrayCellLayout, debugToTerminal=1) - reader.loadFromFile(gds_file) + reader.loadFromFile(gds_file, special_purposes) def clear_visited(self): """ Recursively clear the visited flag """ @@ -1126,7 +1130,7 @@ class layout(): # self.add_inst(cr.name, cr) # self.connect_inst([]) self.add_flat_inst(cr.name, cr) - + def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None): """ Wrapper to create a horizontal channel route @@ -1138,7 +1142,7 @@ class layout(): # self.add_inst(cr.name, cr) # self.connect_inst([]) self.add_flat_inst(cr.name, cr) - + def add_boundary(self, ll=vector(0, 0), ur=None): """ Add boundary for debugging dimensions """ if OPTS.netlist_only: @@ -1331,7 +1335,7 @@ class layout(): new_name = pin.name if not loc: loc = pin.center() - + # Hack for min area if OPTS.tech_name == "sky130": min_area = drc["minarea_{}".format(self.pwr_grid_layer)] @@ -1340,7 +1344,7 @@ class layout(): else: width = None height = None - + if pin.layer == self.pwr_grid_layer: self.add_layout_pin_rect_center(text=new_name, layer=self.pwr_grid_layer, diff --git a/compiler/gdsMill/gdsMill/gds2reader.py b/compiler/gdsMill/gdsMill/gds2reader.py index 448355a8..94f59d3d 100644 --- a/compiler/gdsMill/gdsMill/gds2reader.py +++ b/compiler/gdsMill/gdsMill/gds2reader.py @@ -79,7 +79,7 @@ class Gds2reader: recordLength = struct.unpack(">h",recordLengthAscii) #gives us a tuple with a short int inside offset_int = int(recordLength[0]) # extract length offset += offset_int # count offset - if(self.debugToTerminal==1): + if(self.debugToTerminal==1): print("Offset: " + str(offset)) #print out the record numbers for de-bugging record = self.fileHandle.read(recordLength[0]-2) #read the rest of it (first 2 bytes were already read) return record @@ -669,11 +669,11 @@ class Gds2reader: else: print("There was an error parsing the GDS header. Aborting...") - def loadFromFile(self, fileName): + def loadFromFile(self, fileName, special_purposes={}): self.fileHandle = open(fileName,"rb") self.readGds2() self.fileHandle.close() - self.layoutObject.initialize() + self.layoutObject.initialize(special_purposes) ############################################## diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py index 056c01c0..5fa8f556 100644 --- a/compiler/gdsMill/gdsMill/vlsiLayout.py +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -3,7 +3,7 @@ from datetime import * import numpy as np import math import debug -from tech import use_purpose + class VlsiLayout: """Class represent a hierarchical layout""" @@ -81,6 +81,12 @@ class VlsiLayout: coordinatesRotate.extend((newX,newY)) return coordinatesRotate + def prefixAll(self, prefix): + for name in self.structures: + if name == self.rootStructureName: + continue + self.structures[prefix + name] = self.structures[name] + def rename(self,newName): # take the root structure and copy it to a new structure with the new name self.structures[newName] = self.structures[self.rootStructureName] @@ -211,17 +217,17 @@ class VlsiLayout: del transformPath[-1] return - def initialize(self): + def initialize(self, special_purposes={}): self.deduceHierarchy() # self.traverseTheHierarchy() self.populateCoordinateMap() - #only ones with text + # only ones with text for layerNumber in self.layerNumbersInUse: - #if layerNumber not in no_pin_shape: - if layerNumber in use_purpose: - self.processLabelPins((layerNumber, use_purpose[layerNumber])) - else: - self.processLabelPins((layerNumber, None)) + # if layerNumber not in no_pin_shape: + if layerNumber in special_purposes: + self.processLabelPins((layerNumber, special_purposes[layerNumber])) + else: + self.processLabelPins((layerNumber, None)) def populateCoordinateMap(self): def addToXyTree(startingStructureName = None,transformPath = None): diff --git a/compiler/prefixGDS.py b/compiler/prefixGDS.py new file mode 100755 index 00000000..5940886e --- /dev/null +++ b/compiler/prefixGDS.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +import sys +from gdsMill import gdsMill + +if len(sys.argv) < 4: + print("Script to prefix every instance and structure to create a unique namespace.") + print("Usage: {0} prefix in.gds out.gds".format(sys.argv[0])) + sys.exit(1) + +prefix = sys.argv[1] + +gds_file = sys.argv[2] +arrayCellLayout = gdsMill.VlsiLayout() +gds = gdsMill.Gds2reader(arrayCellLayout,debugToTerminal = 1) +gds.loadFromFile(gds_file) + +gds.prefixAll(prefix) + +writer = gdsMill.Gds2writer(gds) +writer.writeToFile(sys.argv[3])