diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 5c300033..1d4beb11 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -15,6 +15,7 @@ class contact(hierarchy_design.hierarchy_design): necessary to import layouts into Magic which requires the select to be in the same GDS hierarchy as the contact. """ + def __init__(self, layer_stack, dimensions=[1,1], implant_type=None, well_type=None): if implant_type or well_type: name = "{0}_{1}_{2}_{3}x{4}_{5}{6}".format(layer_stack[0], @@ -24,13 +25,14 @@ class contact(hierarchy_design.hierarchy_design): dimensions[1], implant_type, well_type) + else: name = "{0}_{1}_{2}_{3}x{4}".format(layer_stack[0], - layer_stack[1], - layer_stack[2], - dimensions[0], - dimensions[1]) - + layer_stack[1], + layer_stack[2], + dimensions[0], + dimensions[1]) + hierarchy_design.hierarchy_design.__init__(self, name) debug.info(4, "create contact object {0}".format(name)) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 01a48f89..3ac5ea63 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -379,12 +379,10 @@ class layout(lef.lef): dimensions=size, implant_type=implant_type, well_type=well_type) - - debug.check(mirror=="R0","Use rotate to rotate vias instead of mirror.") - height = via.height width = via.width - + debug.check(mirror=="R0","Use rotate to rotate vias instead of mirror.") + if rotate==0: corrected_offset = offset + vector(-0.5*width,-0.5*height) elif rotate==90: @@ -849,10 +847,10 @@ class layout(lef.lef): """ self.add_via_center(layers=("metal1", "via1", "metal2"), offset=loc, - rotate=rotate) + rotate=float(rotate)) via=self.add_via_center(layers=("metal2", "via2", "metal3"), offset=loc, - rotate=rotate) + rotate=float(rotate)) self.add_layout_pin_rect_center(text=name, layer="metal3", offset=loc, diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index aa6bacfc..bb65f771 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -1,5 +1,5 @@ import debug -from tech import GDS +from tech import GDS, drc from vector import vector from tech import layer @@ -36,16 +36,29 @@ class pin_layout: def __eq__(self, other): """ Check if these are the same pins for duplicate checks """ if isinstance(other, self.__class__): - return (self.name==other.name and self.layer==other.layer and self.rect == other.rect) + return (self.layer==other.layer and self.rect == other.rect) else: return False + def inflate(self, spacing=None): + """ + Inflate the rectangle by the spacing (or other rule) + and return the new rectangle. + """ + if not spacing: + spacing = drc["{0}_to_{0}".format(self.layer)] + + (ll,ur) = self.rect + spacing = vector(spacing, spacing) + newll = ll - spacing + newur = ur + spacing + + return (newll, newur) + def overlaps(self, other): """ Check if a shape overlaps with a rectangle """ - ll = self.rect[0] - ur = self.rect[1] - oll = other.rect[0] - our = other.rect[1] + (ll,ur) = self.rect + (oll,our) = other.rect # Start assuming no overlaps x_overlaps = False y_overlaps = False diff --git a/compiler/gdsMill/gdsMill/gds2reader.py b/compiler/gdsMill/gdsMill/gds2reader.py index 3d63cf53..85fc0c66 100644 --- a/compiler/gdsMill/gdsMill/gds2reader.py +++ b/compiler/gdsMill/gdsMill/gds2reader.py @@ -294,7 +294,7 @@ class Gds2reader: mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy rotateFlag = bool(transFlags&0x0002) magnifyFlag = bool(transFlags&0x0004) - thisSref.transFlags=[mirrorFlag,rotateFlag,magnifyFlag] + thisSref.transFlags=[mirrorFlag,magnifyFlag,rotateFlag] if(self.debugToTerminal==1): print("\t\t\tMirror X:"+str(mirrorFlag)) print( "\t\t\tRotate:"+str(rotateFlag)) @@ -345,7 +345,7 @@ class Gds2reader: mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy rotateFlag = bool(transFlags&0x0002) magnifyFlag = bool(transFlags&0x0004) - thisAref.transFlags=[mirrorFlag,rotateFlag,magnifyFlag] + thisAref.transFlags=[mirrorFlag,magnifyFlag,rotateFlag] if(self.debugToTerminal==1): print("\t\t\tMirror X:"+str(mirrorFlag)) print("\t\t\tRotate:"+str(rotateFlag)) @@ -408,7 +408,7 @@ class Gds2reader: mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy rotateFlag = bool(transFlags&0x0002) magnifyFlag = bool(transFlags&0x0004) - thisText.transFlags=[mirrorFlag,rotateFlag,magnifyFlag] + thisText.transFlags=[mirrorFlag,magnifyFlag,rotateFlag] if(self.debugToTerminal==1): print("\t\t\tMirror X:"+str(mirrorFlag)) print("\t\t\tRotate:"+str(rotateFlag)) diff --git a/compiler/gdsMill/gdsMill/gds2writer.py b/compiler/gdsMill/gdsMill/gds2writer.py index ce9d27e2..402416cd 100644 --- a/compiler/gdsMill/gdsMill/gds2writer.py +++ b/compiler/gdsMill/gdsMill/gds2writer.py @@ -280,8 +280,13 @@ class Gds2writer: if(thisSref.transFlags!=""): idBits=b'\x1A\x01' mirrorFlag = int(thisSref.transFlags[0])<<15 - rotateFlag = int(thisSref.transFlags[1])<<1 - magnifyFlag = int(thisSref.transFlags[2])<<3 + # The rotate and magnify flags specify "absolute" rotate and magnify. + # It is unclear what that is (ignore all further rotates/mags in the + # hierarchy? But anyway, calibre doesn't support it. + rotateFlag=0 + magnifyFlag = 0 + #rotateFlag = int(thisSref.transFlags[2])<<1 + #magnifyFlag = int(thisSref.transFlags[1])<<2 transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) self.writeRecord(idBits+transFlags) if(thisSref.magFactor!=""): @@ -327,15 +332,20 @@ class Gds2writer: if(thisAref.transFlags): idBits=b'\x1A\x01' mirrorFlag = int(thisAref.transFlags[0])<<15 - rotateFlag = int(thisAref.transFlags[1])<<1 - magnifyFlag = int(thisAref.transFlags[0])<<3 + # The rotate and magnify flags specify "absolute" rotate and magnify. + # It is unclear what that is (ignore all further rotates/mags in the + # hierarchy? But anyway, calibre doesn't support it. + rotateFlag=0 + magnifyFlag = 0 + #rotateFlag = int(thisAref.transFlags[2])<<1 + #magnifyFlag = int(thisAref.transFlags[1])<<2 transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) self.writeRecord(idBits+transFlags) - if(thisAref.magFactor): + if(thisAref.magFactor!=""): idBits=b'\x1B\x05' magFactor=self.ibmDataFromIeeeDouble(thisAref.magFactor) self.writeRecord(idBits+magFactor) - if(thisAref.rotateAngle): + if(thisAref.rotateAngle!=""): idBits=b'\x1C\x05' rotateAngle=self.ibmDataFromIeeeDouble(thisAref.rotateAngle) self.writeRecord(idBits+rotateAngle) @@ -374,15 +384,20 @@ class Gds2writer: if(thisText.transFlags != ""): idBits=b'\x1A\x01' mirrorFlag = int(thisText.transFlags[0])<<15 - rotateFlag = int(thisText.transFlags[1])<<1 - magnifyFlag = int(thisText.transFlags[0])<<3 + # The rotate and magnify flags specify "absolute" rotate and magnify. + # It is unclear what that is (ignore all further rotates/mags in the + # hierarchy? But anyway, calibre doesn't support it. + rotateFlag=0 + magnifyFlag = 0 + #rotateFlag = int(thisText.transFlags[2])<<1 + #magnifyFlag = int(thisText.transFlags[1])<<2 transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) self.writeRecord(idBits+transFlags) - if(thisText.magFactor != ""): + if(thisText.magFactor!=""): idBits=b'\x1B\x05' magFactor=self.ibmDataFromIeeeDouble(thisText.magFactor) self.writeRecord(idBits+magFactor) - if(thisText.rotateAngle != ""): + if(thisText.rotateAngle!=""): idBits=b'\x1C\x05' rotateAngle=self.ibmDataFromIeeeDouble(thisText.rotateAngle) self.writeRecord(idBits+rotateAngle) diff --git a/compiler/gdsMill/gdsMill/gdsPrimitives.py b/compiler/gdsMill/gdsMill/gdsPrimitives.py index 9b148dce..dc43fea6 100644 --- a/compiler/gdsMill/gdsMill/gdsPrimitives.py +++ b/compiler/gdsMill/gdsMill/gdsPrimitives.py @@ -141,7 +141,7 @@ class GdsText: self.plex="" self.drawingLayer="" self.purposeLayer = None - self.transFlags=[0,00] + self.transFlags=[0,0,0] self.magFactor="" self.rotateAngle="" self.pathType="" diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py index b9813149..5e12619c 100644 --- a/compiler/gdsMill/gdsMill/vlsiLayout.py +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -64,10 +64,11 @@ class VlsiLayout: #helper method to rotate a list of coordinates angle=math.radians(float(0)) if(rotateAngle): - angle = math.radians(float(repr(rotateAngle))) + angle = math.radians(float(rotateAngle)) coordinatesRotate = [] #this will hold the rotated values for coordinate in coordinatesToRotate: + # This is the CCW rotation matrix newX = coordinate[0]*math.cos(angle) - coordinate[1]*math.sin(angle) newY = coordinate[0]*math.sin(angle) + coordinate[1]*math.cos(angle) coordinatesRotate.extend((newX,newY)) @@ -274,8 +275,9 @@ class VlsiLayout: Method to insert one layout into another at a particular offset. """ offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1])) - if self.debug==1: - debug.info(0,"DEBUG: GdsMill vlsiLayout: addInstance: type %s, nameOfLayout "%type(layoutToAdd),nameOfLayout) + if self.debug: + debug.info(0,"DEBUG: GdsMill vlsiLayout: addInstance: type {0}, nameOfLayout {1}".format(type(layoutToAdd),nameOfLayout)) + debug.info(0,"DEBUG: name={0} offset={1} mirror={2} rotate={3}".format(layoutToAdd.rootStructureName,offsetInMicrons, mirror, rotate)) @@ -295,7 +297,7 @@ class VlsiLayout: - # If layoutToAdd is a unique object (not this), then copy heirarchy, + # If layoutToAdd is a unique object (not this), then copy hierarchy, # otherwise, if it is a text name of an internal structure, use it. if layoutToAdd != self: @@ -316,9 +318,7 @@ class VlsiLayout: if mirror or rotate: layoutToAddSref.transFlags = [0,0,0] - # This is NOT the same as the order in the GDS spec! - # It gets written out in gds2writer in the right order though. - # transFlags = (mirror around x-axis, rotation, magnification) + # transFlags = (mirror around x-axis, magnification, rotation) # If magnification or rotation is true, it is the flags are then # followed by an amount in the record if mirror=="R90": @@ -328,17 +328,16 @@ class VlsiLayout: if mirror=="R270": rotate = 270.0 if rotate: - #layoutToAddSref.transFlags = [0,1,0] + #layoutToAddSref.transFlags[2] = 1 layoutToAddSref.rotateAngle = rotate if mirror == "x" or mirror == "MX": - layoutToAddSref.transFlags = [1,0,0] + layoutToAddSref.transFlags[0] = 1 if mirror == "y" or mirror == "MY": #NOTE: "MY" option will override specified rotate angle - #layoutToAddSref.transFlags = [1,1,0] - layoutToAddSref.transFlags = [1,0,0] + layoutToAddSref.transFlags[0] = 1 + #layoutToAddSref.transFlags[2] = 1 layoutToAddSref.rotateAngle = 180.0 if mirror == "xy" or mirror == "XY": #NOTE: "XY" option will override specified rotate angle - #layoutToAddSref.transFlags = [0,1,0] - layoutToAddSref.transFlags = [0,0,0] + #layoutToAddSref.transFlags[2] = 1 layoutToAddSref.rotateAngle = 180.0 #add the sref to the root structure @@ -405,10 +404,10 @@ class VlsiLayout: if(len(text)%2 == 1): text = text + '\x00' textToAdd.textString = text - textToAdd.transFlags = [0,0,1] + #textToAdd.transFlags[1] = 1 textToAdd.magFactor = magnification if rotate: - textToAdd.transFlags = [0,1,1] + #textToAdd.transFlags[2] = 1 textToAdd.rotateAngle = rotate #add the sref to the root structure self.structures[self.rootStructureName].texts.append(textToAdd) @@ -816,10 +815,10 @@ class VlsiLayout: """ (structureName,structureOrigin,structureuVector,structurevVector)=structure + #print(structureName,"u",structureuVector.transpose(),"v",structurevVector.transpose(),"o",structureOrigin.transpose()) boundaries = [] for boundary in self.structures[str(structureName)].boundaries: - # Pin enclosures only work on rectangular pins so ignore any non rectangle - # This may report not finding pins, but the user should fix this by adding a rectangle. + # FIXME: Right now, this only supports rectangular shapes! if len(boundary.coordinates)!=5: continue if layer==boundary.drawingLayer: @@ -827,10 +826,11 @@ class VlsiLayout: right_top=boundary.coordinates[2] # Rectangle is [leftx, bottomy, rightx, topy]. boundaryRect=[left_bottom[0],left_bottom[1],right_top[0],right_top[1]] + # perform the rotation boundaryRect=self.transformRectangle(boundaryRect,structureuVector,structurevVector) + # add the offset boundaryRect=[boundaryRect[0]+structureOrigin[0].item(),boundaryRect[1]+structureOrigin[1].item(), boundaryRect[2]+structureOrigin[0].item(),boundaryRect[3]+structureOrigin[1].item()] - boundaries.append(boundaryRect) return boundaries @@ -858,8 +858,12 @@ class VlsiLayout: """ Rotate a coordinate in space. """ - x=coordinate[0]*uVector[0].item()+coordinate[1]*uVector[1].item() - y=coordinate[1]*vVector[1].item()+coordinate[0]*vVector[0].item() + # MRG: 9/3/18 Incorrect matrixi multiplication! + # This is fixed to be: + # |u[0] v[0]| |x| |x'| + # |u[1] v[1]|x|y|=|y'| + x=coordinate[0]*uVector[0].item()+coordinate[1]*vVector[0].item() + y=coordinate[0]*uVector[1].item()+coordinate[1]*vVector[1].item() transformCoordinate=[x,y] return transformCoordinate diff --git a/compiler/globals.py b/compiler/globals.py index 9acfc945..7a7d0098 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -116,6 +116,8 @@ def init_openram(config_file, is_unit_test=True): import_tech() + init_paths() + # Reset the static duplicate name checker for unit tests. import hierarchy_design hierarchy_design.hierarchy_design.name_map=[] @@ -204,36 +206,31 @@ def read_config(config_file, is_unit_test=True): # Note that if we re-read a config file, nothing will get read again! if not k in OPTS.__dict__ or k=="tech_name": OPTS.__dict__[k]=v - + + # Massage the output path to be an absolute one if not OPTS.output_path.endswith('/'): OPTS.output_path += "/" if not OPTS.output_path.startswith('/'): OPTS.output_path = os.getcwd() + "/" + OPTS.output_path debug.info(1, "Output saved in " + OPTS.output_path) + # Remember if we are running unit tests to reduce output OPTS.is_unit_test=is_unit_test # If we are only generating a netlist, we can't do DRC/LVS if OPTS.netlist_only: OPTS.check_lvsdrc=False - + # If config didn't set output name, make a reasonable default. if (OPTS.output_name == ""): - OPTS.output_name = "sram_{0}rw_{1}b_{2}w_{3}bank_{4}".format(OPTS.rw_ports, - OPTS.word_size, - OPTS.num_words, - OPTS.num_banks, - OPTS.tech_name) + OPTS.output_name = "sram_{0}b_{1}w_{2}bank_{3}rw_{4}w_{5}r_{6}".format(OPTS.word_size, + OPTS.num_words, + OPTS.num_banks, + OPTS.num_rw_ports, + OPTS.num_w_ports, + OPTS.num_r_ports, + OPTS.tech_name) - # Don't delete the output dir, it may have other files! - # make the directory if it doesn't exist - try: - os.makedirs(OPTS.output_path, 0o750) - except OSError as e: - if e.errno == 17: # errno.EEXIST - os.chmod(OPTS.output_path, 0o750) - except: - debug.error("Unable to make output directory.",-1) def end_openram(): @@ -257,7 +254,8 @@ def cleanup_paths(): if not OPTS.purge_temp: debug.info(0,"Preserving temp directory: {}".format(OPTS.openram_temp)) return - if os.path.exists(OPTS.openram_temp): + elif os.path.exists(OPTS.openram_temp): + debug.info(1,"Purging temp directory: {}".format(OPTS.openram_temp)) # This annoyingly means you have to re-cd into the directory each debug iteration #shutil.rmtree(OPTS.openram_temp, ignore_errors=True) contents = [os.path.join(OPTS.openram_temp, i) for i in os.listdir(OPTS.openram_temp)] @@ -293,14 +291,6 @@ def setup_paths(): OPTS.openram_temp += "/" debug.info(1, "Temporary files saved in " + OPTS.openram_temp) - cleanup_paths() - - # make the directory if it doesn't exist - try: - os.makedirs(OPTS.openram_temp, 0o750) - except OSError as e: - if e.errno == 17: # errno.EEXIST - os.chmod(OPTS.openram_temp, 0o750) def is_exe(fpath): @@ -316,7 +306,29 @@ def find_exe(check_exe): if is_exe(exe): return exe return None - + +def init_paths(): + """ Create the temp and output directory if it doesn't exist """ + + # make the directory if it doesn't exist + try: + debug.info(1,"Creating temp directory: {}".format(OPTS.openram_temp)) + os.makedirs(OPTS.openram_temp, 0o750) + except OSError as e: + if e.errno == 17: # errno.EEXIST + os.chmod(OPTS.openram_temp, 0o750) + + # Don't delete the output dir, it may have other files! + # make the directory if it doesn't exist + try: + os.makedirs(OPTS.output_path, 0o750) + except OSError as e: + if e.errno == 17: # errno.EEXIST + os.chmod(OPTS.output_path, 0o750) + except: + debug.error("Unable to make output directory.",-1) + + # imports correct technology directories for testing def import_tech(): global OPTS diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 9e711d22..f8e76a8b 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -20,22 +20,20 @@ class bank(design.design): write driver and sense amplifiers. """ - def __init__(self, word_size, num_words, words_per_row, num_banks=1, name=""): + def __init__(self, sram_config, name=""): - if name == "": - name = "bank_{0}_{1}".format(word_size, num_words) - design.design.__init__(self, name) - debug.info(2, "create sram of size {0} with {1} words".format(word_size,num_words)) - - self.word_size = word_size - self.num_words = num_words - self.words_per_row = words_per_row - self.num_banks = num_banks + sram_config.set_local_config(self) - self.total_write = OPTS.rw_ports + OPTS.w_ports - self.total_read = OPTS.rw_ports + OPTS.r_ports - self.total_ports = OPTS.rw_ports + OPTS.w_ports + OPTS.r_ports + if name == "": + name = "bank_{0}_{1}".format(self.word_size, self.num_words) + design.design.__init__(self, name) + debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,self.num_words)) + + self.total_write = OPTS.num_rw_ports + OPTS.num_w_ports + self.total_read = OPTS.num_rw_ports + OPTS.num_r_ports + self.total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports + # The local control signals are gated when we have bank select logic, # so this prefix will be added to all of the input signals to create # the internal gated signals. @@ -70,24 +68,25 @@ class bank(design.design): def add_pins(self): """ Adding pins for Bank module""" - for k in range(self.total_read): - for i in range(self.word_size): - self.add_pin("dout{0}[{1}]".format(k,i),"OUT") - for k in range(self.total_write): - for i in range(self.word_size): - self.add_pin("din{0}[{1}]".format(k,i),"IN") - for k in range(self.total_ports): - for i in range(self.addr_size): - self.add_pin("addr{0}[{1}]".format(k,i),"INPUT") + for port in range(self.total_read): + for bit in range(self.word_size): + self.add_pin("dout{0}[{1}]".format(port,bit),"OUT") + for port in range(self.total_write): + for bit in range(self.word_size): + self.add_pin("din{0}[{1}]".format(port,bit),"IN") + for port in range(self.total_ports): + for bit in range(self.addr_size): + self.add_pin("addr{0}[{1}]".format(port,bit),"INPUT") # For more than one bank, we have a bank select and name # the signals gated_*. if self.num_banks > 1: - self.add_pin("bank_sel","INPUT") - for k in range(self.total_read): - self.add_pin("s_en{0}".format(k), "INPUT") - for k in range(self.total_write): - self.add_pin("w_en{0}".format(k), "INPUT") + for port in range(self.total_ports): + self.add_pin("bank_sel{}".format(port),"INPUT") + for port in range(self.total_read): + self.add_pin("s_en{0}".format(port), "INPUT") + for port in range(self.total_write): + self.add_pin("w_en{0}".format(port), "INPUT") for pin in ["clk_buf_bar","clk_buf"]: self.add_pin(pin,"INPUT") self.add_pin("vdd","POWER") @@ -228,9 +227,9 @@ class bank(design.design): self.total_bitline_list = self.bitcell.list_all_bitline_names() self.precharge_array = [] - for k in range(self.total_read): - self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.read_bl_list[k], bitcell_br=self.read_br_list[k])) - self.add_mod(self.precharge_array[k]) + for port in range(self.total_read): + self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.read_bl_list[port], bitcell_br=self.read_br_list[port])) + self.add_mod(self.precharge_array[port]) if self.col_addr_size > 0: self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols, @@ -287,13 +286,13 @@ class bank(design.design): """ Creating Precharge """ self.precharge_array_inst = [] - for k in range(self.total_read): - self.precharge_array_inst.append(self.add_inst(name="precharge_array{}".format(k), - mod=self.precharge_array[k])) + for port in range(self.total_read): + self.precharge_array_inst.append(self.add_inst(name="precharge_array{}".format(port), + mod=self.precharge_array[port])) temp = [] for i in range(self.num_cols): - temp.append(self.read_bl_list[k]+"[{0}]".format(i)) - temp.append(self.read_br_list[k]+"[{0}]".format(i)) + temp.append(self.read_bl_list[port]+"[{0}]".format(i)) + temp.append(self.read_br_list[port]+"[{0}]".format(i)) temp.extend([self.prefix+"clk_buf_bar", "vdd"]) self.connect_inst(temp) @@ -301,11 +300,11 @@ class bank(design.design): """ Placing Precharge """ # FIXME: place for multiport - for k in range(self.total_read): + for port in range(self.total_read): # The wells must be far enough apart # The enclosure is for the well and the spacing is to the bitcell wells y_offset = self.bitcell_array.height + self.m2_gap - self.precharge_array_inst[k].place(vector(0,y_offset)) + self.precharge_array_inst[port].place(vector(0,y_offset)) def create_column_mux_array(self): """ Creating Column Mux when words_per_row > 1 . """ @@ -313,19 +312,19 @@ class bank(design.design): return self.col_mux_array_inst = [] - for k in range(self.total_ports): - self.col_mux_array_inst.append(self.add_inst(name="column_mux_array{}".format(k), + for port in range(self.total_ports): + self.col_mux_array_inst.append(self.add_inst(name="column_mux_array{}".format(port), mod=self.column_mux_array)) temp = [] - for i in range(self.num_cols): - temp.append(self.total_bl_list[k]+"[{0}]".format(i)) - temp.append(self.total_br_list[k]+"[{0}]".format(i)) - for h in range(self.words_per_row): - temp.append("sel{0}[{1}]".format(k,h)) - for j in range(self.word_size): - temp.append(self.total_bl_list[k]+"_out[{0}]".format(j)) - temp.append(self.total_br_list[k]+"_out[{0}]".format(j)) + for col in range(self.num_cols): + temp.append(self.total_bl_list[port]+"[{0}]".format(col)) + temp.append(self.total_br_list[port]+"[{0}]".format(col)) + for word in range(self.words_per_row): + temp.append("sel{0}[{1}]".format(port,word)) + for bit in range(self.word_size): + temp.append(self.total_bl_list[port]+"_out[{0}]".format(bit)) + temp.append(self.total_br_list[port]+"_out[{0}]".format(bit)) temp.append("gnd") self.connect_inst(temp) @@ -337,68 +336,68 @@ class bank(design.design): self.column_mux_height = 0 return - for k in range(self.total_ports): + for port in range(self.total_ports): y_offset = self.column_mux_height - self.col_mux_array_inst[k].place(vector(0,y_offset).scale(-1,-1)) + self.col_mux_array_inst[port].place(vector(0,y_offset).scale(-1,-1)) def create_sense_amp_array(self): """ Creating Sense amp """ self.sense_amp_array_inst = [] - for k in range(self.total_read): - self.sense_amp_array_inst.append(self.add_inst(name="sense_amp_array{}".format(k), + for port in range(self.total_read): + self.sense_amp_array_inst.append(self.add_inst(name="sense_amp_array{}".format(port), mod=self.sense_amp_array)) temp = [] - for i in range(self.word_size): - temp.append("dout{0}[{1}]".format(k,i)) + for bit in range(self.word_size): + temp.append("dout{0}[{1}]".format(port,bit)) if self.words_per_row == 1: - temp.append(self.read_bl_list[k]+"[{0}]".format(i)) - temp.append(self.read_br_list[k]+"[{0}]".format(i)) + temp.append(self.read_bl_list[port]+"[{0}]".format(bit)) + temp.append(self.read_br_list[port]+"[{0}]".format(bit)) else: - temp.append(self.read_bl_list[k]+"_out[{0}]".format(i)) - temp.append(self.read_br_list[k]+"_out[{0}]".format(i)) + temp.append(self.read_bl_list[port]+"_out[{0}]".format(bit)) + temp.append(self.read_br_list[port]+"_out[{0}]".format(bit)) - temp.extend([self.prefix+"s_en{0}".format(k), "vdd", "gnd"]) + temp.extend([self.prefix+"s_en{0}".format(port), "vdd", "gnd"]) self.connect_inst(temp) def place_sense_amp_array(self): """ Placing Sense amp """ # FIXME: place for multiport - for k in range(self.total_read): + for port in range(self.total_read): y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap - self.sense_amp_array_inst[k].place(vector(0,y_offset).scale(-1,-1)) + self.sense_amp_array_inst[port].place(vector(0,y_offset).scale(-1,-1)) def create_write_driver_array(self): """ Creating Write Driver """ self.write_driver_array_inst = [] - for k in range(self.total_write): - self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(k), + for port in range(self.total_write): + self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(port), mod=self.write_driver_array)) temp = [] - for i in range(self.word_size): - temp.append("din{0}[{1}]".format(k,i)) - for i in range(self.word_size): + for bit in range(self.word_size): + temp.append("din{0}[{1}]".format(port,bit)) + for bit in range(self.word_size): if (self.words_per_row == 1): - temp.append(self.write_bl_list[k]+"[{0}]".format(i)) - temp.append(self.write_br_list[k]+"[{0}]".format(i)) + temp.append(self.write_bl_list[port]+"[{0}]".format(bit)) + temp.append(self.write_br_list[port]+"[{0}]".format(bit)) else: - temp.append(self.write_bl_list[k]+"_out[{0}]".format(i)) - temp.append(self.write_br_list[k]+"_out[{0}]".format(i)) - temp.extend([self.prefix+"w_en{0}".format(k), "vdd", "gnd"]) + temp.append(self.write_bl_list[port]+"_out[{0}]".format(bit)) + temp.append(self.write_br_list[port]+"_out[{0}]".format(bit)) + temp.extend([self.prefix+"w_en{0}".format(port), "vdd", "gnd"]) self.connect_inst(temp) def place_write_driver_array(self): """ Placing Write Driver """ # FIXME: place for multiport - for k in range(self.total_write): + for port in range(self.total_write): y_offset = self.sense_amp_array.height + self.column_mux_height \ + self.m2_gap + self.write_driver_array.height - self.write_driver_array_inst[k].place(vector(0,y_offset).scale(-1,-1)) + self.write_driver_array_inst[port].place(vector(0,y_offset).scale(-1,-1)) @@ -406,15 +405,15 @@ class bank(design.design): """ Create the hierarchical row decoder """ self.row_decoder_inst = [] - for k in range(self.total_ports): - self.row_decoder_inst.append(self.add_inst(name="row_decoder{}".format(k), + for port in range(self.total_ports): + self.row_decoder_inst.append(self.add_inst(name="row_decoder{}".format(port), mod=self.row_decoder)) temp = [] - for i in range(self.row_addr_size): - temp.append("addr{0}[{1}]".format(k,i+self.col_addr_size)) - for j in range(self.num_rows): - temp.append("dec_out{0}[{1}]".format(k,j)) + for bit in range(self.row_addr_size): + temp.append("addr{0}[{1}]".format(port,bit+self.col_addr_size)) + for row in range(self.num_rows): + temp.append("dec_out{0}[{1}]".format(port,row)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -428,24 +427,24 @@ class bank(design.design): # The address flop and decoder are aligned in the x coord. # FIXME: place for multiport - for k in range(self.total_ports): + for port in range(self.total_ports): x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) - self.row_decoder_inst[k].place(vector(x_offset,0)) + self.row_decoder_inst[port].place(vector(x_offset,0)) def create_wordline_driver(self): """ Create the Wordline Driver """ self.wordline_driver_inst = [] - for k in range(self.total_ports): - self.wordline_driver_inst.append(self.add_inst(name="wordline_driver{}".format(k), + for port in range(self.total_ports): + self.wordline_driver_inst.append(self.add_inst(name="wordline_driver{}".format(port), mod=self.wordline_driver)) temp = [] - for i in range(self.num_rows): - temp.append("dec_out{0}[{1}]".format(k,i)) - for i in range(self.num_rows): - temp.append(self.total_wl_list[k]+"[{0}]".format(i)) + for row in range(self.num_rows): + temp.append("dec_out{0}[{1}]".format(port,row)) + for row in range(self.num_rows): + temp.append(self.total_wl_list[port]+"[{0}]".format(row)) temp.append(self.prefix+"clk_buf") temp.append("vdd") temp.append("gnd") @@ -455,10 +454,10 @@ class bank(design.design): """ Place the Wordline Driver """ # FIXME: place for multiport - for k in range(self.total_ports): + for port in range(self.total_ports): # The wordline driver is placed to the right of the main decoder width. x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch - self.wordline_driver_inst[k].place(vector(x_offset,0)) + self.wordline_driver_inst[port].place(vector(x_offset,0)) def create_column_decoder(self): @@ -480,15 +479,15 @@ class bank(design.design): debug.error("Invalid column decoder?",-1) self.col_decoder_inst = [] - for k in range(self.total_ports): - self.col_decoder_inst.append(self.add_inst(name="col_address_decoder{}".format(k), + for port in range(self.total_ports): + self.col_decoder_inst.append(self.add_inst(name="col_address_decoder{}".format(port), mod=self.col_decoder)) temp = [] - for i in range(self.col_addr_size): - temp.append("addr{0}[{1}]".format(k,i)) - for j in range(self.num_col_addr_lines): - temp.append("sel{0}[{1}]".format(k,j)) + for bit in range(self.col_addr_size): + temp.append("addr{0}[{1}]".format(port,bit)) + for bit in range(self.num_col_addr_lines): + temp.append("sel{0}[{1}]".format(port,bit)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -500,11 +499,11 @@ class bank(design.design): return # FIXME: place for multiport - for k in range(self.total_ports): + for port in range(self.total_ports): # Place the col decoder right aligned with row decoder x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) y_off = -(self.col_decoder.height + 2*drc["well_to_well"]) - self.col_decoder_inst[k].place(vector(x_off,y_off)) + self.col_decoder_inst[port].place(vector(x_off,y_off)) def create_bank_select(self): @@ -514,13 +513,13 @@ class bank(design.design): return self.bank_select_inst = [] - for k in range(self.total_ports): - self.bank_select_inst.append(self.add_inst(name="bank_select", + for port in range(self.total_ports): + self.bank_select_inst.append(self.add_inst(name="bank_select{}".format(port), mod=self.bank_select)) temp = [] temp.extend(self.input_control_signals) - temp.append("bank_sel") + temp.append("bank_sel{}".format(port)) temp.extend(self.control_signals) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -532,7 +531,7 @@ class bank(design.design): return # FIXME: place for multiport - for k in range(self.total_ports): + for port in range(self.total_ports): x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) if self.col_addr_size > 0: y_off = min(self.col_decoder_inst[0].by(), self.col_mux_array_inst[0].by()) @@ -540,7 +539,7 @@ class bank(design.design): y_off = self.row_decoder_inst[0].by() y_off -= (self.bank_select.height + drc["well_to_well"]) self.bank_select_pos = vector(x_off,y_off) - self.bank_select_inst[k].place(self.bank_select_pos) + self.bank_select_inst[port].place(self.bank_select_pos) def route_vdd_gnd(self): @@ -549,19 +548,19 @@ class bank(design.design): # These are the instances that every bank has top_instances = [self.bitcell_array_inst] - for k in range(self.total_ports): - top_instances.extend([self.precharge_array_inst[k], - self.sense_amp_array_inst[k], - self.write_driver_array_inst[k], - self.row_decoder_inst[k], - self.wordline_driver_inst[k]]) + for port in range(self.total_ports): + top_instances.extend([self.precharge_array_inst[port], + self.sense_amp_array_inst[port], + self.write_driver_array_inst[port], + self.row_decoder_inst[port], + self.wordline_driver_inst[port]]) # Add these if we use the part... if self.col_addr_size > 0: - top_instances.append(self.col_decoder_inst[k]) - top_instances.append(self.col_mux_array_inst[k]) + top_instances.append(self.col_decoder_inst[port]) + top_instances.append(self.col_mux_array_inst[port]) if self.num_banks > 1: - top_instances.append(self.bank_select_inst[k]) + top_instances.append(self.bank_select_inst[port]) for inst in top_instances: @@ -575,13 +574,13 @@ class bank(design.design): def route_bank_select(self): """ Route the bank select logic. """ - for k in range(self.total_ports): + for port in range(self.total_ports): for input_name in self.input_control_signals+["bank_sel"]: - self.copy_layout_pin(self.bank_select_inst[k], input_name) + self.copy_layout_pin(self.bank_select_inst[port], input_name) for gated_name in self.control_signals: # Connect the inverter output to the central bus - out_pos = self.bank_select_inst[k].get_pin(gated_name).rc() + out_pos = self.bank_select_inst[port].get_pin(gated_name).rc() bus_pos = vector(self.bus_xoffset[gated_name].x, out_pos.y) self.add_path("metal3",[out_pos, bus_pos]) self.add_via_center(layers=("metal2", "via2", "metal3"), @@ -659,12 +658,12 @@ class bank(design.design): """ Routing of BL and BR between pre-charge and bitcell array """ # FIXME: Update for multiport - for k in range(self.total_read): - for i in range(self.num_cols): - precharge_bl = self.precharge_array_inst[k].get_pin("bl[{}]".format(i)).bc() - precharge_br = self.precharge_array_inst[k].get_pin("br[{}]".format(i)).bc() - bitcell_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[k]+"[{}]".format(i)).uc() - bitcell_br = self.bitcell_array_inst.get_pin(self.read_br_list[k]+"[{}]".format(i)).uc() + for port in range(self.total_read): + for col in range(self.num_cols): + precharge_bl = self.precharge_array_inst[port].get_pin("bl[{}]".format(col)).bc() + precharge_br = self.precharge_array_inst[port].get_pin("br[{}]".format(col)).bc() + bitcell_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[port]+"[{}]".format(col)).uc() + bitcell_br = self.bitcell_array_inst.get_pin(self.read_br_list[port]+"[{}]".format(col)).uc() yoffset = 0.5*(precharge_bl.y+bitcell_bl.y) self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset), @@ -681,12 +680,12 @@ class bank(design.design): return # FIXME: Update for multiport - for k in range(self.total_ports): - for i in range(self.num_cols): - col_mux_bl = self.col_mux_array_inst[k].get_pin("bl[{}]".format(i)).uc() - col_mux_br = self.col_mux_array_inst[k].get_pin("br[{}]".format(i)).uc() - bitcell_bl = self.bitcell_array_inst.get_pin(self.total_bl_list[k]+"[{}]".format(i)).bc() - bitcell_br = self.bitcell_array_inst.get_pin(self.total_br_list[k]+"[{}]".format(i)).bc() + for port in range(self.total_ports): + for col in range(self.num_cols): + col_mux_bl = self.col_mux_array_inst[port].get_pin("bl[{}]".format(col)).uc() + col_mux_br = self.col_mux_array_inst[port].get_pin("br[{}]".format(col)).uc() + bitcell_bl = self.bitcell_array_inst.get_pin(self.total_bl_list[port]+"[{}]".format(col)).bc() + bitcell_br = self.bitcell_array_inst.get_pin(self.total_br_list[port]+"[{}]".format(col)).bc() yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y) self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset), @@ -697,19 +696,19 @@ class bank(design.design): def route_sense_amp_to_col_mux_or_bitcell_array(self): """ Routing of BL and BR between sense_amp and column mux or bitcell array """ - for k in range(self.total_read): - for i in range(self.word_size): - sense_amp_bl = self.sense_amp_array_inst[k].get_pin("bl[{}]".format(i)).uc() - sense_amp_br = self.sense_amp_array_inst[k].get_pin("br[{}]".format(i)).uc() + for port in range(self.total_read): + for bit in range(self.word_size): + sense_amp_bl = self.sense_amp_array_inst[port].get_pin("bl[{}]".format(bit)).uc() + sense_amp_br = self.sense_amp_array_inst[port].get_pin("br[{}]".format(bit)).uc() if self.col_addr_size>0: # Sense amp is connected to the col mux - connect_bl = self.col_mux_array_inst[k].get_pin("bl_out[{}]".format(i)).bc() - connect_br = self.col_mux_array_inst[k].get_pin("br_out[{}]".format(i)).bc() + connect_bl = self.col_mux_array_inst[port].get_pin("bl_out[{}]".format(bit)).bc() + connect_br = self.col_mux_array_inst[port].get_pin("br_out[{}]".format(bit)).bc() else: # Sense amp is directly connected to the bitcell array - connect_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[k]+"[{}]".format(i)).bc() - connect_br = self.bitcell_array_inst.get_pin(self.read_br_list[k]+"[{}]".format(i)).bc() + connect_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[port]+"[{}]".format(bit)).bc() + connect_br = self.bitcell_array_inst.get_pin(self.read_br_list[port]+"[{}]".format(bit)).bc() yoffset = 0.5*(sense_amp_bl.y+connect_bl.y) @@ -721,9 +720,11 @@ class bank(design.design): def route_sense_amp_out(self): """ Add pins for the sense amp output """ - for i in range(self.word_size): - data_pin = self.sense_amp_array_inst[0].get_pin("data[{}]".format(i)) - self.add_layout_pin_rect_center(text="dout0[{}]".format(i), + + # FIXME: Update for multiport + for bit in range(self.word_size): + data_pin = self.sense_amp_array_inst[0].get_pin("data[{}]".format(bit)) + self.add_layout_pin_rect_center(text="dout0[{}]".format(bit), layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), @@ -733,10 +734,11 @@ class bank(design.design): def route_row_decoder(self): """ Routes the row decoder inputs and supplies """ + # FIXME: Update for multiport # Create inputs for the row address lines - for i in range(self.row_addr_size): - addr_idx = i + self.col_addr_size - decoder_name = "addr[{}]".format(i) + for row in range(self.row_addr_size): + addr_idx = row + self.col_addr_size + decoder_name = "addr[{}]".format(row) addr_name = "addr0[{}]".format(addr_idx) self.copy_layout_pin(self.row_decoder_inst[0], decoder_name, addr_name) @@ -744,9 +746,9 @@ class bank(design.design): def route_write_driver(self): """ Connecting write driver """ - for i in range(self.word_size): - data_name = "data[{}]".format(i) - din_name = "din0[{}]".format(i) + for row in range(self.word_size): + data_name = "data[{}]".format(row) + din_name = "din0[{}]".format(row) self.copy_layout_pin(self.write_driver_array_inst[0], data_name, din_name) @@ -754,17 +756,17 @@ class bank(design.design): def route_wordline_driver(self): """ Connecting Wordline driver output to Bitcell WL connection """ - for i in range(self.num_rows): + for row in range(self.num_rows): # The pre/post is to access the pin from "outside" the cell to avoid DRCs - decoder_out_pos = self.row_decoder_inst[0].get_pin("decode[{}]".format(i)).rc() - driver_in_pos = self.wordline_driver_inst[0].get_pin("in[{}]".format(i)).lc() + decoder_out_pos = self.row_decoder_inst[0].get_pin("decode[{}]".format(row)).rc() + driver_in_pos = self.wordline_driver_inst[0].get_pin("in[{}]".format(row)).lc() mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos]) # The mid guarantees we exit the input cell to the right. - driver_wl_pos = self.wordline_driver_inst[0].get_pin("wl[{}]".format(i)).rc() - bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_list[0]+"[{}]".format(i)).lc() + driver_wl_pos = self.wordline_driver_inst[0].get_pin("wl[{}]".format(row)).rc() + bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_list[0]+"[{}]".format(row)).lc() mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0) mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1) self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index b8a57f15..90175743 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -53,11 +53,6 @@ class delay_chain(design.design): self.add_pin("gnd") def add_modules(self): - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) - self.bitcell = self.mod_bitcell() - self.inv = pinv(route_output=False) self.add_mod(self.inv) diff --git a/compiler/modules/dff_inv_array.py b/compiler/modules/dff_inv_array.py index c88fabd3..c2455821 100644 --- a/compiler/modules/dff_inv_array.py +++ b/compiler/modules/dff_inv_array.py @@ -115,11 +115,11 @@ class dff_inv_array(design.design): def add_layout_pins(self): for row in range(self.rows): for col in range(self.columns): - # Continous vdd rail along with label. + # Adds power pin on left of row vdd_pin=self.dff_insts[row,col].get_pin("vdd") self.add_power_pin("vdd", vdd_pin.lc()) - # Continous gnd rail along with label. + # Adds gnd pin on left of row gnd_pin=self.dff_insts[row,col].get_pin("gnd") self.add_power_pin("gnd", gnd_pin.lc()) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 38136b62..0b44c8fc 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -24,7 +24,8 @@ class hierarchical_decoder(design.design): from importlib import reload c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) - self.bitcell_height = self.mod_bitcell.height + b = self.mod_bitcell() + self.bitcell_height = b.height self.NAND_FORMAT = "DEC_NAND[{0}]" self.INV_FORMAT = "DEC_INV_[{0}]" @@ -130,7 +131,7 @@ class hierarchical_decoder(design.design): self.total_number_of_predecoder_outputs = 4*self.no_of_pre2x4 + 8*self.no_of_pre3x8 else: self.total_number_of_predecoder_outputs = 0 - debug.error("Not enough rows for a hierarchical decoder. Non-hierarchical not supported yet.",-1) + debug.error("Not enough rows ({}) for a hierarchical decoder. Non-hierarchical not supported yet.".format(self.num_inputs),-1) # Calculates height and width of pre-decoder, if self.no_of_pre3x8 > 0: diff --git a/compiler/modules/replica_bitcell.py b/compiler/modules/replica_bitcell.py index 5ec524f4..7bbdbe06 100644 --- a/compiler/modules/replica_bitcell.py +++ b/compiler/modules/replica_bitcell.py @@ -10,7 +10,7 @@ class replica_bitcell(design.design): is a hand-made cell, so the layout and netlist should be available in the technology library. """ - pin_names = ["BL", "BR", "WL", "vdd", "gnd"] + pin_names = ["bl", "br", "wl", "vdd", "gnd"] (width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"], layer["boundary"]) diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index b15f4515..6ca4a829 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -228,7 +228,7 @@ class replica_bitline(design.design): # 3. Route the contact of previous route to the bitcell WL # route bend of previous net to bitcell WL - wl_offset = self.rbc_inst.get_pin("WL").lc() + wl_offset = self.rbc_inst.get_pin("wl").lc() xmid_point= 0.5*(wl_offset.x+contact_offset.x) wl_mid1 = vector(xmid_point,contact_offset.y) wl_mid2 = vector(xmid_point,wl_offset.y) @@ -247,7 +247,7 @@ class replica_bitline(design.design): # Route the connection of the source route to the RBL bitline (left) # Via will go halfway down from the bitcell - bl_offset = self.rbc_inst.get_pin("BL").bc() + bl_offset = self.rbc_inst.get_pin("bl").bc() # Route down a pitch so we can use M2 routing bl_down_offset = bl_offset - vector(0, self.m2_pitch) self.add_path("metal2",[source_offset, bl_down_offset, bl_offset]) diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 47bcf023..c48d280d 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -30,7 +30,11 @@ class sense_amp_array(design.design): def create_layout(self): self.height = self.amp.height - self.width = self.amp.width * self.word_size * self.words_per_row + + if self.bitcell.width > self.amp.width: + self.width = self.bitcell.width * self.word_size * self.words_per_row + else: + self.width = self.amp.width * self.word_size * self.words_per_row self.place_sense_amp_array() self.add_layout_pins() @@ -53,6 +57,11 @@ class sense_amp_array(design.design): self.amp = self.mod_sense_amp("sense_amp") self.add_mod(self.amp) + # This is just used for measurements, + # so don't add the module + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + self.bitcell = self.mod_bitcell() def create_sense_amp_array(self): self.local_insts = [] @@ -68,7 +77,10 @@ class sense_amp_array(design.design): def place_sense_amp_array(self): - amp_spacing = self.amp.width * self.words_per_row + if self.bitcell.width > self.amp.width: + amp_spacing = self.bitcell.width * self.words_per_row + else: + amp_spacing = self.amp.width * self.words_per_row for i in range(0,self.word_size): amp_position = vector(amp_spacing * i, 0) self.local_insts[i].place(amp_position) diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index 709e3e45..3deac4e1 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -50,6 +50,13 @@ class wordline_driver(design.design): def add_modules(self): + # This is just used for measurements, + # so don't add the module + from importlib import reload + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + self.bitcell = self.mod_bitcell() + self.inv = pinv() self.add_mod(self.inv) @@ -58,6 +65,7 @@ class wordline_driver(design.design): self.nand2 = pnand2() self.add_mod(self.nand2) + def route_vdd_gnd(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ @@ -125,16 +133,20 @@ class wordline_driver(design.design): nand2_xoffset = inv1_xoffset + self.inv.width inv2_xoffset = nand2_xoffset + self.nand2.width - self.width = inv2_xoffset + self.inv.width - self.height = self.inv.height * self.rows - + self.width = inv2_xoffset + self.inv.height + if self.bitcell.height > self.inv.height: + self.height = self.bitcell.height * self.rows + driver_height = self.bitcell.height + else: + self.height = self.inv.height * self.rows + driver_height = self.inv.height for row in range(self.rows): if (row % 2): - y_offset = self.inv.height*(row + 1) + y_offset = driver_height*(row + 1) inst_mirror = "MX" else: - y_offset = self.inv.height*row + y_offset = driver_height*row inst_mirror = "R0" inv1_offset = [inv1_xoffset, y_offset] diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 965f1735..eff0c8d8 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -30,7 +30,12 @@ class write_driver_array(design.design): self.create_write_array() def create_layout(self): - self.width = self.columns * self.driver.width + + if self.bitcell.width > self.driver.width: + self.width = self.columns * self.bitcell.width + else: + self.width = self.columns * self.driver.width + self.height = self.driver.height self.place_write_array() @@ -54,6 +59,12 @@ class write_driver_array(design.design): self.driver = self.mod_write_driver("write_driver") self.add_mod(self.driver) + # This is just used for measurements, + # so don't add the module + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + self.bitcell = self.mod_bitcell() + def create_write_array(self): self.driver_insts = {} for i in range(0,self.columns,self.words_per_row): @@ -69,9 +80,14 @@ class write_driver_array(design.design): def place_write_array(self): + if self.bitcell.width > self.driver.width: + driver_spacing = self.bitcell.width + else: + driver_spacing = self.driver.width + for i in range(0,self.columns,self.words_per_row): index = int(i/self.words_per_row) - base = vector(i * self.driver.width,0) + base = vector(i * driver_spacing,0) self.driver_insts[index].place(base) diff --git a/compiler/openram.py b/compiler/openram.py index 9045e8e7..e4ab593e 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -38,7 +38,8 @@ report_status() # Start importing design modules after we have the config file import verify -import sram +from sram import sram +from sram_config import sram_config output_extensions = ["sp","v","lib"] if not OPTS.netlist_only: @@ -51,11 +52,13 @@ print(*output_files,sep="\n") start_time = datetime.datetime.now() print_time("Start",start_time) +# Configure the SRAM organization +c = sram_config(word_size=OPTS.word_size, + num_words=OPTS.num_words) + # import SRAM test generation -s = sram.sram(word_size=OPTS.word_size, - num_words=OPTS.num_words, - num_banks=OPTS.num_banks, - name=OPTS.output_name) +s = sram(sram_config=c, + name=OPTS.output_name) # Output the files for the resulting SRAM s.save() diff --git a/compiler/options.py b/compiler/options.py index 04d546d4..5298c00f 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -52,14 +52,20 @@ class options(optparse.Values): purge_temp = True # These are the configuration parameters - rw_ports = 1 - r_ports = 0 - w_ports = 0 + num_rw_ports = 1 + num_r_ports = 0 + num_w_ports = 0 + # These will get initialized by the the file supply_voltages = "" temperatures = "" process_corners = "" - + + # These are the main configuration parameters that should be over-ridden + # in a configuration file. + #num_words = 0 + #num_banks = 1 + #word_size = 0 # These are the default modules that can be over-riden decoder = "hierarchical_decoder" diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index ac6ace3b..6f579c06 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -1,5 +1,4 @@ import contact -import pgate import design import debug from tech import drc, parameter, spice @@ -7,47 +6,41 @@ from vector import vector from ptx import ptx from globals import OPTS -class pbitcell(pgate.pgate): +class pbitcell(design.design): """ This module implements a parametrically sized multi-port bitcell, with a variable number of read/write, write, and read ports """ - - width = None - height = None - unique_id = 1 - - def __init__(self, num_readwrite=OPTS.rw_ports, num_write=OPTS.w_ports, num_read=OPTS.r_ports): - name = "pbitcell_{0}RW_{1}W_{2}R_{3}".format(num_readwrite, num_write, num_read, pbitcell.unique_id) - pbitcell.unique_id += 1 - pgate.pgate.__init__(self, name) - debug.info(2, "create a multi-port bitcell with {0} write ports and {1} read ports".format(num_write, num_read)) + def __init__(self): - self.num_readwrite = num_readwrite - self.num_write = num_write - self.num_read = num_read + self.num_rw_ports = OPTS.num_rw_ports + self.num_w_ports = OPTS.num_w_ports + self.num_r_ports = OPTS.num_r_ports + + name = "pbitcell_{0}RW_{1}W_{2}R".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports) + # This is not a pgate because pgates depend on the bitcell height! + design.design.__init__(self, name) + debug.info(2, "create a multi-port bitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, + self.num_w_ports, + self.num_r_ports)) self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() + # We must always create the bitcell layout because + # some transistor sizes in the other netlists depend on it + self.create_layout() - # FIXME: Why is this static set here? - pbitcell.width = self.width - pbitcell.height = self.height - - def create_netlist(self): self.add_pins() self.add_modules() self.create_storage() - if(self.num_readwrite > 0): + if(self.num_rw_ports > 0): self.create_readwrite_ports() - if(self.num_write > 0): + if(self.num_w_ports > 0): self.create_write_ports() - if(self.num_read > 0): + if(self.num_r_ports > 0): self.create_read_ports() def create_layout(self): @@ -58,11 +51,11 @@ class pbitcell(pgate.pgate): self.route_storage() self.route_rails() - if(self.num_readwrite > 0): + if(self.num_rw_ports > 0): self.place_readwrite_ports() - if(self.num_write > 0): + if(self.num_w_ports > 0): self.place_write_ports() - if(self.num_read > 0): + if(self.num_r_ports > 0): self.place_read_ports() self.extend_well() @@ -70,31 +63,30 @@ class pbitcell(pgate.pgate): self.DRC_LVS() def add_pins(self): - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): self.add_pin("rwbl{}".format(k)) self.add_pin("rwbl_bar{}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): self.add_pin("wbl{}".format(k)) self.add_pin("wbl_bar{}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): self.add_pin("rbl{}".format(k)) self.add_pin("rbl_bar{}".format(k)) - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): self.add_pin("rwwl{}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): self.add_pin("wwl{}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): self.add_pin("rwl{}".format(k)) self.add_pin("vdd") self.add_pin("gnd") - def add_modules(self): # if there are any read/write ports, then the inverter nmos is sized based the number of them - if(self.num_readwrite > 0): - inverter_nmos_width = self.num_readwrite*3*parameter["min_tx_size"] + if(self.num_rw_ports > 0): + inverter_nmos_width = self.num_rw_ports*3*parameter["min_tx_size"] inverter_pmos_width = parameter["min_tx_size"] readwrite_nmos_width = 1.5*parameter["min_tx_size"] write_nmos_width = parameter["min_tx_size"] @@ -133,7 +125,6 @@ class pbitcell(pgate.pgate): tx_type="nmos") self.add_mod(self.read_nmos) - def calculate_spacing(self): """ Calculate transistor spacings """ @@ -153,15 +144,15 @@ class pbitcell(pgate.pgate): # readwrite to readwrite transistor spacing (also acts as readwrite to write transistor spacing) if(self.readwrite_nmos_contact_extension > self.gate_contact_thres): - self.readwrite_to_readwrite_spacing = drc["minwidth_metal2"] + self.readwrite_nmos_contact_extension + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] + self.readwrite_to_readwrite_spacing = drc["minwidth_metal2"] + self.readwrite_nmos_contact_extension + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] else: - self.readwrite_to_readwrite_spacing = drc["poly_to_active"] + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] + self.readwrite_to_readwrite_spacing = drc["poly_to_active"] + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] # write to write transistor spacing if(self.write_nmos_contact_extension > self.gate_contact_thres): - self.write_to_write_spacing = drc["minwidth_metal2"] + self.write_nmos_contact_extension + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] + self.write_to_write_spacing = drc["minwidth_metal2"] + self.write_nmos_contact_extension + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] else: - self.write_to_write_spacing = drc["poly_to_active"] + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] + self.write_to_write_spacing = drc["poly_to_active"] + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] # read to read transistor spacing if(self.read_nmos_contact_extension > self.gate_contact_thres): @@ -171,7 +162,7 @@ class pbitcell(pgate.pgate): # write to read transistor spacing (also acts as readwrite to read transistor spacing) # calculation is dependent on whether the read transistor is adjacent to a write transistor or a readwrite transistor - if(self.num_write > 0): + if(self.num_w_ports > 0): if(self.write_nmos_contact_extension > self.gate_contact_thres): write_portion = drc["minwidth_metal2"] + self.write_nmos_contact_extension else: @@ -187,7 +178,7 @@ class pbitcell(pgate.pgate): else: read_portion = drc["poly_to_active"] - self.write_to_read_spacing = write_portion + read_portion + 2*contact.poly.width + drc["poly_to_field_poly"] + self.write_to_read_spacing = write_portion + read_portion + 2*contact.poly.width + drc["poly_to_polycontact"] """ calculations for transistor tiling (transistor + spacing) """ self.inverter_tile_width = self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing @@ -196,13 +187,13 @@ class pbitcell(pgate.pgate): self.read_tile_width = self.read_to_read_spacing + self.read_nmos.active_height """ calculation for row line tiling """ - self.rail_tile_height = drc["active_to_body_active"] + 0.5*(drc["minwidth_tx"] - drc["minwidth_metal1"]) + drc["minwidth_metal1"] + self.rail_tile_height = drc["active_to_body_active"] + contact.well.width #0.5*(drc["minwidth_tx"] - drc["minwidth_metal1"]) + drc["minwidth_metal1"] self.rowline_tile_height = drc["minwidth_metal1"] + contact.m1m2.width """ calculations related to inverter connections """ - self.inverter_gap = drc["poly_to_active"] + drc["poly_to_field_poly"] + 2*contact.poly.width + drc["minwidth_metal1"] + self.inverter_pmos_contact_extension + self.inverter_gap = drc["poly_to_active"] + drc["poly_to_polycontact"] + 2*contact.poly.width + drc["minwidth_metal1"] + self.inverter_pmos_contact_extension self.cross_couple_lower_ypos = self.inverter_nmos.active_height + drc["poly_to_active"] + 0.5*contact.poly.width - self.cross_couple_upper_ypos = self.inverter_nmos.active_height + drc["poly_to_active"] + drc["poly_to_field_poly"] + 1.5*contact.poly.width + self.cross_couple_upper_ypos = self.inverter_nmos.active_height + drc["poly_to_active"] + drc["poly_to_polycontact"] + 1.5*contact.poly.width def calculate_postions(self): @@ -210,23 +201,23 @@ class pbitcell(pgate.pgate): Calculate positions that describe the edges of the cell """ # create flags for excluding readwrite, write, or read port calculations if they are not included in the bitcell - if(self.num_readwrite > 0): + if(self.num_rw_ports > 0): self.readwrite_port_flag = True else: self.readwrite_port_flag = False - if(self.num_write > 0): + if(self.num_w_ports > 0): self.write_port_flag = True else: self.write_port_flag = False - if(self.num_read > 0): + if(self.num_r_ports > 0): self.read_port_flag = True else: self.read_port_flag = False # determine the distance of the leftmost/rightmost transistor gate connection - if (self.num_read > 0): + if (self.num_r_ports > 0): if(self.read_nmos_contact_extension > self.gate_contact_thres): end_connection = drc["minwidth_metal2"] + self.read_nmos_contact_extension + contact.m1m2.height else: @@ -240,22 +231,22 @@ class pbitcell(pgate.pgate): # leftmost position = storage width + read/write ports width + write ports width + read ports width + end transistor gate connections + metal spacing necessary for tiling the bitcell self.leftmost_xpos = -self.inverter_tile_width \ - self.inverter_to_write_spacing \ - - self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_readwrite-1)*self.readwrite_tile_width) \ + - self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_rw_ports-1)*self.readwrite_tile_width) \ - self.write_port_flag*self.readwrite_port_flag*self.write_to_write_spacing \ - - self.write_port_flag*(self.write_nmos.active_height + (self.num_write-1)*self.write_tile_width) \ + - self.write_port_flag*(self.write_nmos.active_height + (self.num_w_ports-1)*self.write_tile_width) \ - self.read_port_flag*self.write_to_read_spacing \ - - self.read_port_flag*(self.read_nmos.active_height + (self.num_read-1)*self.read_tile_width) \ + - self.read_port_flag*(self.read_nmos.active_height + (self.num_r_ports-1)*self.read_tile_width) \ - end_connection \ - - 0.5*drc["minwidth_metal2"] + - 0.5*drc["poly_to_polycontact"] self.rightmost_xpos = -self.leftmost_xpos # bottommost position = gnd height + rwwl height + wwl height + rwl height + space needed between tiled bitcells array_tiling_offset = 0.5*drc["minwidth_metal2"] self.botmost_ypos = -self.rail_tile_height \ - - self.num_readwrite*self.rowline_tile_height \ - - self.num_write*self.rowline_tile_height \ - - self.num_read*self.rowline_tile_height \ + - self.num_rw_ports*self.rowline_tile_height \ + - self.num_w_ports*self.rowline_tile_height \ + - self.num_r_ports*self.rowline_tile_height \ - array_tiling_offset # topmost position = height of the inverter + height of vdd @@ -263,10 +254,35 @@ class pbitcell(pgate.pgate): + self.rail_tile_height # calculations for the cell dimensions - array_vdd_overlap = 0.5*drc["minwidth_metal1"] + array_vdd_overlap = 0.5*contact.well.width self.width = -2*self.leftmost_xpos self.height = self.topmost_ypos - self.botmost_ypos - array_vdd_overlap + + def create_storage(self): + """ + Creates the crossed coupled inverters that act as storage for the bitcell. + The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar". + """ + + # create active for nmos + self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left", + mod=self.inverter_nmos) + self.connect_inst(["Q_bar", "Q", "gnd", "gnd"]) + + self.inverter_nmos_right = self.add_inst(name="inverter_nmos_right", + mod=self.inverter_nmos) + self.connect_inst(["gnd", "Q_bar", "Q", "gnd"]) + + # create active for pmos + self.inverter_pmos_left = self.add_inst(name="inverter_pmos_left", + mod=self.inverter_pmos) + self.connect_inst(["Q_bar", "Q", "vdd", "vdd"]) + + self.inverter_pmos_right = self.add_inst(name="inverter_pmos_right", + mod=self.inverter_pmos) + self.connect_inst(["vdd", "Q_bar", "Q", "vdd"]) + def place_storage(self): """ @@ -318,31 +334,6 @@ class pbitcell(pgate.pgate): # update furthest left and right transistor edges (this will propagate to further transistor offset calculations) self.left_building_edge = -self.inverter_tile_width self.right_building_edge = self.inverter_tile_width - - - def create_storage(self): - """ - Creates the crossed coupled inverters that act as storage for the bitcell. - The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar". - """ - - # create active for nmos - self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left", - mod=self.inverter_nmos) - self.connect_inst(["Q_bar", "Q", "gnd", "gnd"]) - - self.inverter_nmos_right = self.add_inst(name="inverter_nmos_right", - mod=self.inverter_nmos) - self.connect_inst(["gnd", "Q_bar", "Q", "gnd"]) - - # create active for pmos - self.inverter_pmos_left = self.add_inst(name="inverter_pmos_left", - mod=self.inverter_pmos) - self.connect_inst(["Q_bar", "Q", "vdd", "vdd"]) - - self.inverter_pmos_right = self.add_inst(name="inverter_pmos_right", - mod=self.inverter_pmos) - self.connect_inst(["vdd", "Q_bar", "Q", "vdd"]) def route_rails(self): @@ -394,11 +385,11 @@ class pbitcell(pgate.pgate): """ # define write transistor variables as empty arrays based on the number of write ports - self.readwrite_nmos_left = [None] * self.num_readwrite - self.readwrite_nmos_right = [None] * self.num_readwrite + self.readwrite_nmos_left = [None] * self.num_rw_ports + self.readwrite_nmos_right = [None] * self.num_rw_ports # iterate over the number of read/write ports - for k in range(0,self.num_readwrite): + for k in range(0,self.num_rw_ports): # add read/write transistors self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k), mod=self.readwrite_nmos) @@ -415,15 +406,15 @@ class pbitcell(pgate.pgate): """ # Define variables relevant to write transistors - self.rwwl_positions = [None] * self.num_readwrite - self.rwbl_positions = [None] * self.num_readwrite - self.rwbl_bar_positions = [None] * self.num_readwrite + self.rwwl_positions = [None] * self.num_rw_ports + self.rwbl_positions = [None] * self.num_rw_ports + self.rwbl_bar_positions = [None] * self.num_rw_ports # define offset correction due to rotation of the ptx module readwrite_rotation_correct = self.readwrite_nmos.active_height # iterate over the number of read/write ports - for k in range(0,self.num_readwrite): + for k in range(0,self.num_rw_ports): # Add transistors # calculate read/write transistor offsets left_readwrite_transistor_xpos = self.left_building_edge \ @@ -533,14 +524,14 @@ class pbitcell(pgate.pgate): # Drain/Storage connections # this path only needs to be drawn once on the last iteration of the loop - if(k == self.num_readwrite-1): + if(k == self.num_rw_ports-1): # add contacts to connect gate of inverters to drain of read/write transistors - left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_field_poly"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) + left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=left_storage_contact, rotate=90) - right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_field_poly"] + 0.5*contact.poly.width, self.cross_couple_lower_ypos) + right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 0.5*contact.poly.width, self.cross_couple_lower_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=right_storage_contact, rotate=90) @@ -584,11 +575,11 @@ class pbitcell(pgate.pgate): write_rotation_correct = self.write_nmos.active_height # define write transistor variables as empty arrays based on the number of write ports - self.write_nmos_left = [None] * self.num_write - self.write_nmos_right = [None] * self.num_write + self.write_nmos_left = [None] * self.num_w_ports + self.write_nmos_right = [None] * self.num_w_ports # iterate over the number of write ports - for k in range(0,self.num_write): + for k in range(0,self.num_w_ports): # add write transistors self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k), mod=self.write_nmos) @@ -605,15 +596,15 @@ class pbitcell(pgate.pgate): """ # Define variables relevant to write transistors - self.wwl_positions = [None] * self.num_write - self.wbl_positions = [None] * self.num_write - self.wbl_bar_positions = [None] * self.num_write + self.wwl_positions = [None] * self.num_w_ports + self.wbl_positions = [None] * self.num_w_ports + self.wbl_bar_positions = [None] * self.num_w_ports # define offset correction due to rotation of the ptx module write_rotation_correct = self.write_nmos.active_height # iterate over the number of write ports - for k in range(0,self.num_write): + for k in range(0,self.num_w_ports): # Add transistors # calculate write transistor offsets left_write_transistor_xpos = self.left_building_edge \ @@ -638,7 +629,7 @@ class pbitcell(pgate.pgate): # Add WWL lines # calculate WWL position wwl_ypos = self.gnd_position.y \ - - self.num_readwrite*self.rowline_tile_height \ + - self.num_rw_ports*self.rowline_tile_height \ - (k+1)*self.rowline_tile_height self.wwl_positions[k] = vector(self.leftmost_xpos, wwl_ypos) @@ -727,14 +718,14 @@ class pbitcell(pgate.pgate): # Drain/Storage connections # this path only needs to be drawn once on the last iteration of the loop - if(k == self.num_write-1): + if(k == self.num_w_ports-1): # add contacts to connect gate of inverters to drain of write transistors - left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_field_poly"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) + left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=left_storage_contact, rotate=90) - right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_field_poly"] + 0.5*contact.poly.width, self.cross_couple_lower_ypos) + right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 0.5*contact.poly.width, self.cross_couple_lower_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=right_storage_contact, rotate=90) @@ -775,13 +766,13 @@ class pbitcell(pgate.pgate): """ # define read transistor variables as empty arrays based on the number of read ports - self.read_nmos_left = [None] * self.num_read - self.read_nmos_right = [None] * self.num_read - self.read_access_nmos_left = [None] * self.num_read - self.read_access_nmos_right = [None] * self.num_read + self.read_nmos_left = [None] * self.num_r_ports + self.read_nmos_right = [None] * self.num_r_ports + self.read_access_nmos_left = [None] * self.num_r_ports + self.read_access_nmos_right = [None] * self.num_r_ports # iterate over the number of read ports - for k in range(0,self.num_read): + for k in range(0,self.num_r_ports): # add read-access transistors self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k), mod=self.read_nmos) @@ -806,9 +797,9 @@ class pbitcell(pgate.pgate): """ # Define variables relevant to read transistors - self.rwl_positions = [None] * self.num_read - self.rbl_positions = [None] * self.num_read - self.rbl_bar_positions = [None] * self.num_read + self.rwl_positions = [None] * self.num_r_ports + self.rbl_positions = [None] * self.num_r_ports + self.rbl_bar_positions = [None] * self.num_r_ports # define offset correction due to rotation of the ptx module read_rotation_correct = self.read_nmos.active_height @@ -816,17 +807,8 @@ class pbitcell(pgate.pgate): # calculate offset to overlap the drain of the read-access transistor with the source of the read transistor overlap_offset = self.read_nmos.get_pin("D").ll() - self.read_nmos.get_pin("S").ll() - # define read transistor variables as empty arrays based on the number of read ports - self.read_nmos_left = [None] * self.num_read - self.read_nmos_right = [None] * self.num_read - self.read_access_nmos_left = [None] * self.num_read - self.read_access_nmos_right = [None] * self.num_read - self.rwl_positions = [None] * self.num_read - self.rbl_positions = [None] * self.num_read - self.rbl_bar_positions = [None] * self.num_read - # iterate over the number of read ports - for k in range(0,self.num_read): + for k in range(0,self.num_r_ports): # Add transistors # calculate transistor offsets left_read_transistor_xpos = self.left_building_edge \ @@ -840,36 +822,24 @@ class pbitcell(pgate.pgate): + read_rotation_correct # add read-access transistors - self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k), - mod=self.read_nmos, - offset=[left_read_transistor_xpos,0], - rotate=90) - self.connect_inst(["RA_to_R_left{}".format(k), " Q_bar", "gnd", "gnd"]) + self.read_access_nmos_left[k].place(offset=[left_read_transistor_xpos,0], + rotate=90) - self.read_access_nmos_right[k] = self.add_inst(name="read_access_nmos_right{}".format(k), - mod=self.read_nmos, - offset=[right_read_transistor_xpos,0], - rotate=90) - self.connect_inst(["RA_to_R_right{}".format(k), "Q", "gnd", "gnd"]) + self.read_access_nmos_right[k].place(offset=[right_read_transistor_xpos,0], + rotate=90) # add read transistors - self.read_nmos_left[k] = self.add_inst(name="read_nmos_left{}".format(k), - mod=self.read_nmos, - offset=[left_read_transistor_xpos,overlap_offset.x], - rotate=90) - self.connect_inst(["rbl{}".format(k), "rwl{}".format(k), "RA_to_R_left{}".format(k), "gnd"]) + self.read_nmos_left[k].place(offset=[left_read_transistor_xpos,overlap_offset.x], + rotate=90) - self.read_nmos_right[k] = self.add_inst(name="read_nmos_right{}".format(k), - mod=self.read_nmos, - offset=[right_read_transistor_xpos,overlap_offset.x], - rotate=90) - self.connect_inst(["rbl_bar{}".format(k), "rwl{}".format(k), "RA_to_R_right{}".format(k), "gnd"]) + self.read_nmos_right[k].place(offset=[right_read_transistor_xpos,overlap_offset.x], + rotate=90) # Add RWL lines # calculate RWL position rwl_ypos = self.gnd_position.y \ - - self.num_readwrite*self.rowline_tile_height \ - - self.num_write*self.rowline_tile_height \ + - self.num_rw_ports*self.rowline_tile_height \ + - self.num_w_ports*self.rowline_tile_height \ - (k+1)*self.rowline_tile_height self.rwl_positions[k] = vector(self.leftmost_xpos, rwl_ypos) @@ -1028,7 +998,7 @@ class pbitcell(pgate.pgate): # extend pwell over read/write and write transistors to the # height of the write transistor well (read/write and write # transistors are the same height) - if(self.num_write > 0): + if(self.num_w_ports > 0): # calculate the edge of the write transistor well closest to the center left_write_well_xpos = self.write_nmos_left[0].offset.x + drc["well_enclosure_active"] right_write_well_xpos = self.write_nmos_right[0].offset.x - self.write_nmos.active_height - drc["well_enclosure_active"] @@ -1054,7 +1024,7 @@ class pbitcell(pgate.pgate): height=write_well_height) # extend pwell over the read transistors to the height of the bitcell - if(self.num_read > 0): + if(self.num_r_ports > 0): # calculate the edge of the read transistor well clostest to the center left_read_well_xpos = self.read_nmos_left[0].offset.x + drc["well_enclosure_active"] right_read_well_xpos = self.read_nmos_right[0].offset.x - self.read_nmos.active_height - drc["well_enclosure_active"] @@ -1097,42 +1067,36 @@ class pbitcell(pgate.pgate): offset = vector(0, self.gnd_position.y + 0.5*contact.well.second_layer_width) self.add_contact_center(layers=("active", "contact", "metal1"), offset=offset, - rotate=90) - - self.add_rect_center(layer="pimplant", - offset=offset, - width=drc["minwidth_tx"], - height=drc["minwidth_tx"]) + rotate=90, + implant_type="p", + well_type="p") # connect nimplants to vdd offset = vector(0, self.vdd_position.y + 0.5*drc["minwidth_metal1"]) self.add_contact_center(layers=("active", "contact", "metal1"), offset=offset, - rotate=90) - - self.add_rect_center(layer="nimplant", - offset=offset, - width=drc["minwidth_tx"], - height=drc["minwidth_tx"]) + rotate=90, + implant_type="n", + well_type="n") def list_bitcell_pins(self, col, row): """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ bitcell_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): bitcell_pins.append("rwbl{0}[{1}]".format(k,col)) bitcell_pins.append("rwbl_bar{0}[{1}]".format(k,col)) - for k in range(self.num_write): + for k in range(self.num_w_ports): bitcell_pins.append("wbl{0}[{1}]".format(k,col)) bitcell_pins.append("wbl_bar{0}[{1}]".format(k,col)) - for k in range(self.num_read): + for k in range(self.num_r_ports): bitcell_pins.append("rbl{0}[{1}]".format(k,col)) bitcell_pins.append("rbl_bar{0}[{1}]".format(k,col)) - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): bitcell_pins.append("rwwl{0}[{1}]".format(k,row)) - for k in range(self.num_write): + for k in range(self.num_w_ports): bitcell_pins.append("wwl{0}[{1}]".format(k,row)) - for k in range(self.num_read): + for k in range(self.num_r_ports): bitcell_pins.append("rwl{0}[{1}]".format(k,row)) bitcell_pins.append("vdd") bitcell_pins.append("gnd") @@ -1142,11 +1106,11 @@ class pbitcell(pgate.pgate): def list_all_wl_names(self): """ Creates a list of all wordline pin names """ row_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): row_pins.append("rwwl{0}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): row_pins.append("wwl{0}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): row_pins.append("rwl{0}".format(k)) return row_pins @@ -1154,9 +1118,9 @@ class pbitcell(pgate.pgate): def list_read_wl_names(self): """ Creates a list of wordline pin names associated with read ports """ row_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): row_pins.append("rwwl{0}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): row_pins.append("rwl{0}".format(k)) return row_pins @@ -1164,9 +1128,9 @@ class pbitcell(pgate.pgate): def list_write_wl_names(self): """ Creates a list of wordline pin names associated with write ports """ row_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): row_pins.append("rwwl{0}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): row_pins.append("wwl{0}".format(k)) return row_pins @@ -1175,13 +1139,13 @@ class pbitcell(pgate.pgate): def list_all_bitline_names(self): """ Creates a list of all bitline pin names (both bl and br) """ column_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): column_pins.append("rwbl{0}".format(k)) column_pins.append("rwbl_bar{0}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): column_pins.append("wbl{0}".format(k)) column_pins.append("wbl_bar{0}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): column_pins.append("rbl{0}".format(k)) column_pins.append("rbl_bar{0}".format(k)) @@ -1190,11 +1154,11 @@ class pbitcell(pgate.pgate): def list_all_bl_names(self): """ Creates a list of all bl pins names """ column_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): column_pins.append("rwbl{0}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): column_pins.append("wbl{0}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): column_pins.append("rbl{0}".format(k)) return column_pins @@ -1202,11 +1166,11 @@ class pbitcell(pgate.pgate): def list_all_br_names(self): """ Creates a list of all br pins names """ column_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): column_pins.append("rwbl_bar{0}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): column_pins.append("wbl_bar{0}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): column_pins.append("rbl_bar{0}".format(k)) return column_pins @@ -1214,9 +1178,9 @@ class pbitcell(pgate.pgate): def list_read_bl_names(self): """ Creates a list of bl pin names associated with read ports """ column_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): column_pins.append("rwbl{0}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): column_pins.append("rbl{0}".format(k)) return column_pins @@ -1224,9 +1188,9 @@ class pbitcell(pgate.pgate): def list_read_br_names(self): """ Creates a list of br pin names associated with read ports """ column_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): column_pins.append("rwbl_bar{0}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): column_pins.append("rbl_bar{0}".format(k)) return column_pins @@ -1234,9 +1198,9 @@ class pbitcell(pgate.pgate): def list_write_bl_names(self): """ Creates a list of bl pin names associated with write ports """ column_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): column_pins.append("rwbl{0}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): column_pins.append("wbl{0}".format(k)) return column_pins @@ -1244,9 +1208,9 @@ class pbitcell(pgate.pgate): def list_write_br_names(self): """ Creates a list of br pin names asscociated with write ports""" column_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): column_pins.append("rwbl_bar{0}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): column_pins.append("wbl_bar{0}".format(k)) return column_pins diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 4e47934d..ec3bed0c 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -11,10 +11,19 @@ class pgate(design.design): This is a module that implements some shared functions for parameterized gates. """ - def __init__(self, name): + def __init__(self, name, height=None): """ Creates a generic cell """ design.design.__init__(self, name) + if height: + self.height = height + elif not height: + from importlib import reload + c = reload(__import__(OPTS.bitcell)) + bitcell = getattr(c, OPTS.bitcell) + b = bitcell() + self.height = b.height + def connect_pin_to_rail(self,inst,pin,supply): """ Conencts a ptx pin to a supply rail. """ diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index e2e87053..e39f95bc 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -17,26 +17,22 @@ class pinv(pgate.pgate): from center of rail to rail.. The route_output will route the output to the right side of the cell for easier access. """ - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - bitcell = getattr(c, OPTS.bitcell) unique_id = 1 - def __init__(self, size=1, beta=parameter["beta"], height=bitcell.height, route_output=True): + def __init__(self, size=1, beta=parameter["beta"], height=None, route_output=True): # We need to keep unique names because outputting to GDSII # will use the last record with a given name. I.e., you will # over-write a design in GDS if one has and the other doesn't # have poly connected, for example. name = "pinv_{}".format(pinv.unique_id) pinv.unique_id += 1 - pgate.pgate.__init__(self, name) + pgate.pgate.__init__(self, name, height) debug.info(2, "create pinv structure {0} with size of {1}".format(name, size)) self.nmos_size = size self.pmos_size = beta*size self.beta = beta - self.height = height # Maybe minimize height if not defined in future? self.route_output = False @@ -175,6 +171,8 @@ class pinv(pgate.pgate): offset=vector(0.5*self.width,self.height), width=self.width) + + def create_ptx(self): """ Create the PMOS and NMOS netlist. diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index e55fb649..328836dc 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -11,13 +11,9 @@ class pinvbuf(design.design): This is a simple inverter/buffer used for driving loads. It is used in the column decoder for 1:2 decoding and as the clock buffer. """ - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - bitcell = getattr(c, OPTS.bitcell) - unique_id = 1 - def __init__(self, driver_size=4, height=bitcell.height, name=""): + def __init__(self, driver_size=4, height=None, name=""): self.stage_effort = 4 self.row_height = height @@ -32,7 +28,7 @@ class pinvbuf(design.design): name = "pinvbuf_{0}_{1}_{2}".format(self.predriver_size, self.driver_size, pinvbuf.unique_id) pinvbuf.unique_id += 1 - design.design.__init__(self, name) + design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) self.create_netlist() diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 99ae9f02..d38c7de4 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -12,24 +12,19 @@ class pnand2(pgate.pgate): This model use ptx to generate a 2-input nand within a cetrain height. """ - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - bitcell = getattr(c, OPTS.bitcell) - unique_id = 1 - def __init__(self, size=1, height=bitcell.height): + def __init__(self, size=1, height=None): """ Creates a cell for a simple 2 input nand """ name = "pnand2_{0}".format(pnand2.unique_id) pnand2.unique_id += 1 - pgate.pgate.__init__(self, name) + pgate.pgate.__init__(self, name, height) debug.info(2, "create pnand2 structure {0} with size of {1}".format(name, size)) self.nmos_size = 2*size self.pmos_size = parameter["beta"]*size self.nmos_width = self.nmos_size*drc["minwidth_tx"] self.pmos_width = self.pmos_size*drc["minwidth_tx"] - self.height = height # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnand2 is only supported now.") diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 984ee417..b4e11b32 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -12,18 +12,13 @@ class pnand3(pgate.pgate): This model use ptx to generate a 2-input nand within a cetrain height. """ - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - mod_bitcell = getattr(c, OPTS.bitcell) - bitcell = mod_bitcell() - unique_id = 1 - def __init__(self, size=1, height=bitcell.height): + def __init__(self, size=1, height=None): """ Creates a cell for a simple 3 input nand """ name = "pnand3_{0}".format(pnand3.unique_id) pnand3.unique_id += 1 - pgate.pgate.__init__(self, name) + pgate.pgate.__init__(self, name, height) debug.info(2, "create pnand3 structure {0} with size of {1}".format(name, size)) # We have trouble pitch matching a 3x sizes to the bitcell... @@ -32,7 +27,6 @@ class pnand3(pgate.pgate): self.pmos_size = parameter["beta"]*size self.nmos_width = self.nmos_size*drc["minwidth_tx"] self.pmos_width = self.pmos_size*drc["minwidth_tx"] - self.height = height # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnand3 is only supported now.") diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index e19f03cf..8f7dcea4 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -12,17 +12,13 @@ class pnor2(pgate.pgate): This model use ptx to generate a 2-input nor within a cetrain height. """ - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - bitcell = getattr(c, OPTS.bitcell) - unique_id = 1 - def __init__(self, size=1, height=bitcell.height): + def __init__(self, size=1, height=None): """ Creates a cell for a simple 2 input nor """ name = "pnor2_{0}".format(pnor2.unique_id) pnor2.unique_id += 1 - pgate.pgate.__init__(self, name) + pgate.pgate.__init__(self, name, height) debug.info(2, "create pnor2 structure {0} with size of {1}".format(name, size)) self.nmos_size = size @@ -30,7 +26,6 @@ class pnor2(pgate.pgate): self.pmos_size = 1.5*parameter["beta"]*size self.nmos_width = self.nmos_size*drc["minwidth_tx"] self.pmos_width = self.pmos_size*drc["minwidth_tx"] - self.height = height # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnor2 is only supported now.") diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 604c9312..015b434a 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -39,6 +39,8 @@ class single_level_column_mux(design.design): self.add_wells() def add_modules(self): + # This is just used for measurements, + # so don't add the module from importlib import reload c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) diff --git a/compiler/router/grid.py b/compiler/router/grid.py index 5f60da4e..8eb063cf 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -36,9 +36,6 @@ class grid: def add_blockage_shape(self,ll,ur,z): debug.info(3,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z)) - if ll[0]<42 and ll[0]>38 and ll[1]<3 and ll[1]>0: - debug.info(0,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z)) - block_list = [] for x in range(int(ll[0]),int(ur[0])+1): for y in range(int(ll[1]),int(ur[1])+1): diff --git a/compiler/router/router.py b/compiler/router/router.py index bb784b3c..73c0681d 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -49,6 +49,12 @@ class router: """ If we want to route something besides the top-level cell.""" self.top_name = top_name + def get_zindex(self,layer_num): + if layer_num==self.horiz_layer_number: + return 0 + else: + return 1 + def set_layers(self, layers): """Allows us to change the layers that we are routing on. First layer is always horizontal, middle is via, and last is always @@ -84,19 +90,19 @@ class router: - def find_pin(self,pin): + def find_pin(self,pin_name): """ Finds the pin shapes and converts to tracks. Pin can either be a label or a location,layer pair: [[x,y],layer]. """ - label_list=self.layout.getPinShapeByLabel(str(pin)) + shape_list=self.layout.getPinShapeByLabel(str(pin_name)) pin_list = [] - for label in label_list: - (name,layer,boundary)=label + for shape in shape_list: + (name,layer,boundary)=shape rect = [vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])] - # this is a list because other cells/designs may have must-connect pins - pin_list.append(pin_layout(pin, rect, layer)) + pin = pin_layout(pin_name, rect, layer) + pin_list.append(pin) debug.check(len(pin_list)>0,"Did not find any pin shapes for {0}.".format(str(pin))) @@ -202,17 +208,30 @@ class router: def add_blockages(self): - """ Add the blockages except the pin shapes """ + """ Add the blockages except the pin shapes. Also remove the pin shapes from the blockages list. """ + # Join all the pin shapes into one big list + all_pins = [item for sublist in list(self.pins.values()) for item in sublist] + + # Do an n^2 check to see if any shapes are the same, otherwise add them + # FIXME: Make faster, but number of pins won't be *that* large + real_blockages = [] for blockage in self.blockages: - # Skip pin shapes - all_pins = [x[0] for x in list(self.pins.values())] for pin in all_pins: - if blockage.overlaps(pin): + # If the blockage overlaps the pin and is on the same layer, + # it must be connected, so skip it. + if blockage==pin: + debug.info(1,"Removing blockage for pin {}".format(str(pin))) break else: - [ll,ur]=self.convert_blockage_to_tracks(blockage.rect) - zlayer = 0 if blockage.layer_num==self.horiz_layer_number else 1 + debug.info(2,"Adding blockage {}".format(str(blockage))) + # Inflate the blockage by spacing rule + [ll,ur]=self.convert_blockage_to_tracks(blockage.inflate()) + zlayer = self.get_zindex(blockage.layer_num) self.rg.add_blockage_shape(ll,ur,zlayer) + real_blockages.append(blockage) + + # Remember the filtered blockages + self.blockages = real_blockages def get_blockages(self, layer_num): @@ -227,37 +246,6 @@ class router: rect = [ll,ur] new_pin = pin_layout("blockage{}".format(len(self.blockages)),rect,layer_num) self.blockages.append(new_pin) - - # for boundary in self.layout.structures[sref].boundaries: - # coord_trans = self.translate_coordinates(boundary.coordinates, mirr, angle, xyShift) - # shape_coords = self.min_max_coord(coord_trans) - # shape = self.convert_shape_to_units(shape_coords) - - # # only consider the two layers that we are routing on - # if boundary.drawingLayer in [self.vert_layer_number,self.horiz_layer_number]: - # # store the blockages as pin layouts so they are easy to compare etc. - # new_pin = pin_layout("blockage",shape,boundary.drawingLayer) - # # avoid repeated blockage pins - # if new_pin not in self.blockages: - # self.blockages.append(new_pin) - - - # # recurse given the mirror, angle, etc. - # for cur_sref in self.layout.structures[sref].srefs: - # sMirr = 1 - # if cur_sref.transFlags[0] == True: - # sMirr = -1 - # sAngle = math.radians(float(0)) - # if cur_sref.rotateAngle: - # sAngle = math.radians(float(cur_sref.rotateAngle)) - # sAngle += angle - # x = cur_sref.coordinates[0] - # y = cur_sref.coordinates[1] - # newX = (x)*math.cos(angle) - mirr*(y)*math.sin(angle) + xyShift[0] - # newY = (x)*math.sin(angle) + mirr*(y)*math.cos(angle) + xyShift[1] - # sxyShift = (newX, newY) - - # self.get_blockages(cur_sref.sName, sMirr, sAngle, sxyShift) def convert_point_to_units(self,p): @@ -282,11 +270,15 @@ class router: old_ur = ur ll=ll.scale(self.track_factor) ur=ur.scale(self.track_factor) - ll = ll.floor() - ur = ur.ceil() - if ll[0]<45 and ll[0]>35 and ll[1]<10 and ll[1]>0: - debug.info(0,"Converting [ {0} , {1} ]".format(old_ll,old_ur)) - debug.info(0,"Converted [ {0} , {1} ]".format(ll,ur)) + # We can round since we are using inflated shapes + # and the track points are at the center + ll = ll.round() + ur = ur.round() + # if ll[0]<45 and ll[0]>35 and ll[1]<5 and ll[1]>-5: + # debug.info(0,"Converting [ {0} , {1} ]".format(old_ll,old_ur)) + # debug.info(0,"Converted [ {0} , {1} ]".format(ll,ur)) + # pin=self.convert_track_to_shape(ll) + # debug.info(0,"Pin {}".format(pin)) return [ll,ur] def convert_pin_to_tracks(self, pin): @@ -296,9 +288,6 @@ class router: If a pin has insufficent overlap, it returns the blockage list to avoid it. """ (ll,ur) = pin.rect - #ll = snap_to_grid(ll) - #ur = snap_to_grid(ur) - #debug.info(1,"Converting [ {0} , {1} ]".format(ll,ur)) # scale the size bigger to include neaby tracks @@ -306,37 +295,40 @@ class router: ur=ur.scale(self.track_factor).ceil() # width depends on which layer it is - zindex = 0 if pin.layer_num==self.horiz_layer_number else 1 - if zindex==0: - width = self.horiz_layer_width - else: + zindex=self.get_zindex(pin.layer_num) + if zindex: width = self.vert_layer_width + else: + width = self.horiz_layer_width track_list = [] block_list = [] - # include +- 1 so when a shape is less than one grid - for x in range(ll[0]-1,ur[0]+1): - for y in range(ll[1]-1,ur[1]+1): + + track_area = self.track_width*self.track_width + for x in range(ll[0],ur[0]): + for y in range(ll[1],ur[1]): #debug.info(1,"Converting [ {0} , {1} ]".format(x,y)) - # get the rectangular pin at a track location - # if dimension of overlap is greater than min width in any dimension, - # it will be an on-grid pin - rect = self.convert_track_to_pin(vector3d(x,y,zindex)) - max_overlap=max(self.compute_overlap(pin.rect,rect)) # however, if there is not enough overlap, then if there is any overlap at all, # we need to block it to prevent routes coming in on that grid - full_rect = self.convert_full_track_to_shape(vector3d(x,y,zindex)) - full_overlap=max(self.compute_overlap(pin.rect,full_rect)) - + full_rect = self.convert_track_to_shape(vector3d(x,y,zindex)) + overlap_rect=self.compute_overlap(pin.rect,full_rect) + overlap_area = overlap_rect[0]*overlap_rect[1] #debug.info(1,"Check overlap: {0} {1} max={2}".format(shape,rect,max_overlap)) - if max_overlap >= width: + + # Assume if more than half the area, it is occupied + overlap_ratio = overlap_area/track_area + if overlap_ratio > 0.5: track_list.append(vector3d(x,y,zindex)) - elif full_overlap>0: + # otherwise, the pin may not be accessible, so block it + elif overlap_ratio > 0: block_list.append(vector3d(x,y,zindex)) else: debug.info(4,"No overlap: {0} {1} max={2}".format(pin.rect,rect,max_overlap)) - + print("H:",x,y) + if x>38 and x<42 and y>42 and y<45: + print(pin) + print(full_rect, overlap_rect, overlap_ratio) #debug.warning("Off-grid pin for {0}.".format(str(pin))) #debug.info(1,"Converted [ {0} , {1} ]".format(ll,ur)) return (track_list,block_list) @@ -381,7 +373,7 @@ class router: return [ll,ur] - def convert_full_track_to_shape(self,track): + def convert_track_to_shape(self,track): """ Convert a grid point into a rectangle shape that occupies the entire centered track. @@ -447,9 +439,8 @@ class router: grid_keys=self.rg.map.keys() partial_track=vector(0,self.track_width/6.0) for g in grid_keys: - continue # for now... - shape = self.convert_full_track_to_shape(g) - self.cell.add_rect(layer="boundary", + shape = self.convert_track_to_shape(g) + self.cell.add_rect(layer="text", offset=shape[0], width=shape[1].x-shape[0].x, height=shape[1].y-shape[0].y) @@ -481,10 +472,12 @@ class router: zoom=0.05) for blockage in self.blockages: - self.cell.add_rect(layer="boundary", - offset=blockage.ll(), - width=blockage.width(), - height=blockage.height()) + # Display the inflated blockage + (ll,ur) = blockage.inflate() + self.cell.add_rect(layer="blockage", + offset=ll, + width=ur.x-ll.x, + height=ur.y-ll.y) # FIXME: This should be replaced with vector.snap_to_grid at some point diff --git a/compiler/router/tests/10_supply_grid_test.py b/compiler/router/tests/10_supply_grid_test.py index 485d925c..2fa5cb7b 100755 --- a/compiler/router/tests/10_supply_grid_test.py +++ b/compiler/router/tests/10_supply_grid_test.py @@ -32,12 +32,14 @@ class no_blockages_test(openram_test): globals.setup_paths() from control_logic import control_logic cell = control_logic(16) + #from pinv import pinv + #cell = pinv() #gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),"control_logic") #cell = gds_cell(name, gds_file) self.add_inst(name=name, mod=cell, offset=[0,0]) - self.connect_inst(["csb","web","clk","s_en","w_en","clk_buf_bar","clk_buf","vdd","gnd"]) + self.connect_inst(cell.pin_map.keys()) r=router(module=cell) layer_stack =("metal3","via2","metal2") diff --git a/compiler/sram.py b/compiler/sram.py index 653a3a03..0feea1b3 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -3,7 +3,7 @@ import datetime import getpass import debug from globals import OPTS, print_time - +from sram_config import sram_config class sram(): """ @@ -12,31 +12,34 @@ class sram(): results. We can later add visualizer and other high-level functions as needed. """ - def __init__(self, word_size, num_words, num_banks, name): + def __init__(self, sram_config, name): + sram_config.compute_sizes() + sram_config.set_local_config(self) + # reset the static duplicate name checker for unit tests # in case we create more than one SRAM from design import design design.name_map=[] - debug.info(2, "create sram of size {0} with {1} num of words".format(word_size, - num_words)) + debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size, + self.num_words, + self.num_banks)) start_time = datetime.datetime.now() self.name = name + - if num_banks == 1: - from sram_1bank import sram_1bank - self.s=sram_1bank(word_size, num_words, name) - elif num_banks == 2: - from sram_2bank import sram_2bank - self.s=sram_2bank(word_size, num_words, name) - elif num_banks == 4: - from sram_4bank import sram_4bank - self.s=sram_4bank(word_size, num_words, name) + if self.num_banks == 1: + from sram_1bank import sram_1bank as sram + elif self.num_banks == 2: + from sram_2bank import sram_2bank as sram + elif self.num_banks == 4: + from sram_4bank import sram_4bank as sram else: debug.error("Invalid number of banks.",-1) + self.s = sram(name, sram_config) self.s.create_netlist() if not OPTS.netlist_only: self.s.create_layout() diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index 93903551..03791089 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -18,11 +18,11 @@ class sram_1bank(sram_base): """ Procedures specific to a one bank SRAM. """ - def __init__(self, word_size, num_words, name): - sram_base.__init__(self, word_size, num_words, 1, name) + def __init__(self, name, sram_config): + sram_base.__init__(self, name, sram_config) + def create_netlist(self): - self.compute_sizes() sram_base.create_netlist(self) self.create_modules() @@ -99,18 +99,18 @@ class sram_1bank(sram_base): for i in range(self.word_size): dout_name = "dout0[{}]".format(i) - self.copy_layout_pin(self.bank_inst, dout_name, "DOUT[{}]".format(i)) + self.copy_layout_pin(self.bank_inst, dout_name, "DOUT0[{}]".format(i)) # Lower address bits for i in range(self.col_addr_size): - self.copy_layout_pin(self.col_addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i)) + self.copy_layout_pin(self.col_addr_dff_inst, "din[{}]".format(i),"ADDR0[{}]".format(i)) # Upper address bits for i in range(self.row_addr_size): - self.copy_layout_pin(self.row_addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i+self.col_addr_size)) + self.copy_layout_pin(self.row_addr_dff_inst, "din[{}]".format(i),"ADDR0[{}]".format(i+self.col_addr_size)) for i in range(self.word_size): din_name = "din[{}]".format(i) - self.copy_layout_pin(self.data_dff_inst, din_name, din_name.upper()) + self.copy_layout_pin(self.data_dff_inst, din_name, "DIN0[{}]".format(i)) def route(self): """ Route a single bank SRAM """ diff --git a/compiler/sram_2bank.py b/compiler/sram_2bank.py index ba10525c..daf02563 100644 --- a/compiler/sram_2bank.py +++ b/compiler/sram_2bank.py @@ -16,8 +16,8 @@ class sram_2bank(sram_base): """ Procedures specific to a two bank SRAM. """ - def __init__(self, word_size, num_words, name): - sram_base.__init__(self, word_size, num_words, 2, name) + def __init__(self, name, sram_config): + sram_base.__init__(self, name, sram_config) def compute_bank_offsets(self): """ Compute the overall offsets for a two bank SRAM """ diff --git a/compiler/sram_4bank.py b/compiler/sram_4bank.py index 14e597d5..44b8ba87 100644 --- a/compiler/sram_4bank.py +++ b/compiler/sram_4bank.py @@ -16,8 +16,8 @@ class sram_4bank(sram_base): """ Procedures specific to a four bank SRAM. """ - def __init__(self, word_size, num_words, name): - sram_base.__init__(self, word_size, num_words, 4, name) + def __init__(self, name, sram_config): + sram_base.__init__(self, name, sram_config) def compute_bank_offsets(self): """ Compute the overall offsets for a four bank SRAM """ diff --git a/compiler/sram_base.py b/compiler/sram_base.py index da6403db..ca3351b5 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -2,7 +2,7 @@ import sys import datetime import getpass import debug -from math import log,sqrt,ceil +from importlib import reload from vector import vector from globals import OPTS, print_time @@ -13,95 +13,29 @@ class sram_base(design): Dynamically generated SRAM by connecting banks to control logic. The number of banks should be 1 , 2 or 4 """ - def __init__(self, word_size, num_words, num_banks, name): + def __init__(self, name, sram_config): design.__init__(self, name) - from importlib import reload - c = reload(__import__(OPTS.control_logic)) - self.mod_control_logic = getattr(c, OPTS.control_logic) + self.sram_config = sram_config + sram_config.set_local_config(self) - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) - self.bitcell = self.mod_bitcell() - - c = reload(__import__(OPTS.ms_flop)) - self.mod_ms_flop = getattr(c, OPTS.ms_flop) - self.ms_flop = self.mod_ms_flop() - - self.word_size = word_size - self.num_words = num_words - self.num_banks = num_banks + self.total_write = OPTS.num_rw_ports + OPTS.num_w_ports + self.total_read = OPTS.num_rw_ports + OPTS.num_r_ports + self.total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports self.bank_insts = [] - def compute_sizes(self): - """ Computes the organization of the memory using bitcell size by trying to make it square.""" - - debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.") - - self.num_words_per_bank = self.num_words/self.num_banks - self.num_bits_per_bank = self.word_size*self.num_words_per_bank - - # Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry) - self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank - self.bank_side_length = sqrt(self.bank_area) - - # Estimate the words per row given the height of the bitcell and the square side length - self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width) - self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size) - - # Estimate the number of rows given the tentative words per row - self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size) - self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row) - - # Fix the number of columns and rows - self.num_cols = int(self.words_per_row*self.word_size) - self.num_rows = int(self.num_words_per_bank/self.words_per_row) - - # Compute the address and bank sizes - self.row_addr_size = int(log(self.num_rows, 2)) - self.col_addr_size = int(log(self.words_per_row, 2)) - self.bank_addr_size = self.col_addr_size + self.row_addr_size - self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2)) - - debug.info(1,"Words per row: {}".format(self.words_per_row)) - - def estimate_words_per_row(self,tentative_num_cols, word_size): - """ - This provides a heuristic rounded estimate for the number of words - per row. - """ - - if tentative_num_cols < 1.5*word_size: - return 1 - elif tentative_num_cols > 3*word_size: - return 4 - else: - return 2 - - def amend_words_per_row(self,tentative_num_rows, words_per_row): - """ - This picks the number of words per row more accurately by limiting - it to a minimum and maximum. - """ - # Recompute the words per row given a hard max - if(tentative_num_rows > 512): - debug.check(tentative_num_rows*words_per_row <= 2048, "Number of words exceeds 2048") - return int(words_per_row*tentative_num_rows/512) - # Recompute the words per row given a hard min - if(tentative_num_rows < 16): - debug.check(tentative_num_rows*words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows)) - return int(words_per_row*tentative_num_rows/16) - - return words_per_row def add_pins(self): """ Add pins for entire SRAM. """ - - for i in range(self.word_size): - self.add_pin("DIN[{0}]".format(i),"INPUT") - for i in range(self.addr_size): - self.add_pin("ADDR[{0}]".format(i),"INPUT") + + for port in range(self.total_write): + for bit in range(self.word_size): + self.add_pin("DIN{0}[{1}]".format(port,bit),"INPUT") + + for port in range(self.total_ports): + for bit in range(self.addr_size): + self.add_pin("ADDR{0}[{1}]".format(port,bit),"INPUT") # These are used to create the physical pins too self.control_logic_inputs=self.control_logic.get_inputs() @@ -109,8 +43,9 @@ class sram_base(design): self.add_pin_list(self.control_logic_inputs,"INPUT") - for i in range(self.word_size): - self.add_pin("DOUT[{0}]".format(i),"OUTPUT") + for port in range(self.total_read): + for bit in range(self.word_size): + self.add_pin("DOUT{0}[{1}]".format(port,bit),"OUTPUT") self.add_pin("vdd","POWER") self.add_pin("gnd","GROUND") @@ -271,7 +206,18 @@ class sram_base(design): def add_modules(self): """ Create all the modules that will be used """ + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + self.bitcell = self.mod_bitcell() + + c = reload(__import__(OPTS.control_logic)) + self.mod_control_logic = getattr(c, OPTS.control_logic) + c = reload(__import__(OPTS.ms_flop)) + self.mod_ms_flop = getattr(c, OPTS.ms_flop) + self.ms_flop = self.mod_ms_flop() + + from control_logic import control_logic # Create the control logic module self.control_logic = self.mod_control_logic(num_rows=self.num_rows) @@ -279,24 +225,21 @@ class sram_base(design): # Create the address and control flops (but not the clk) from dff_array import dff_array - self.row_addr_dff = dff_array(name="row_addr_dff", rows=self.row_addr_size, columns=1) + self.row_addr_dff = dff_array(name="row_addr_dff", rows=self.row_addr_size*self.total_ports, columns=1) self.add_mod(self.row_addr_dff) if self.col_addr_size > 0: - self.col_addr_dff = dff_array(name="col_addr_dff", rows=1, columns=self.col_addr_size) + self.col_addr_dff = dff_array(name="col_addr_dff", rows=1, columns=self.col_addr_size*self.total_ports) self.add_mod(self.col_addr_dff) else: self.col_addr_dff = None - self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size) + self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size*self.total_ports) self.add_mod(self.data_dff) # Create the bank module (up to four are instantiated) from bank import bank - self.bank = bank(word_size=self.word_size, - num_words=self.num_words_per_bank, - words_per_row=self.words_per_row, - num_banks=self.num_banks, + self.bank = bank(self.sram_config, name="bank") self.add_mod(self.bank) @@ -312,20 +255,28 @@ class sram_base(design): def create_bank(self,bank_num): - """ Create a bank """ + """ Create a bank """ self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num), mod=self.bank)) temp = [] - for i in range(self.word_size): - temp.append("DOUT[{0}]".format(i)) - for i in range(self.word_size): - temp.append("BANK_DIN[{0}]".format(i)) - for i in range(self.bank_addr_size): - temp.append("A[{0}]".format(i)) + for port in range(self.total_read): + for bit in range(self.word_size): + temp.append("DOUT{0}[{1}]".format(port,bit)) + for port in range(self.total_write): + for bit in range(self.word_size): + temp.append("BANK_DIN{0}[{1}]".format(port,bit)) + for port in range(self.total_ports): + for bit in range(self.bank_addr_size): + temp.append("A{0}[{1}]".format(port,bit)) if(self.num_banks > 1): - temp.append("bank_sel[{0}]".format(bank_num)) - temp.extend(["s_en0", "w_en0", "clk_buf_bar","clk_buf" , "vdd", "gnd"]) + for port in range(self.total_ports): + temp.append("bank_sel{0}[{1}]".format(port,bank_num)) + for port in range(self.total_read): + temp.append("s_en{0}".format(port)) + for port in range(self.total_write): + temp.append("w_en{0}".format(port)) + temp.extend(["clk_buf_bar","clk_buf" , "vdd", "gnd"]) self.connect_inst(temp) return self.bank_insts[-1] @@ -370,9 +321,10 @@ class sram_base(design): # inputs, outputs/output/bar inputs = [] outputs = [] - for i in range(self.row_addr_size): - inputs.append("ADDR[{}]".format(i+self.col_addr_size)) - outputs.append("A[{}]".format(i+self.col_addr_size)) + for k in range(self.total_ports): + for i in range(self.row_addr_size): + inputs.append("ADDR{}[{}]".format(k,i+self.col_addr_size)) + outputs.append("A{}[{}]".format(k,i+self.col_addr_size)) self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) return inst @@ -385,9 +337,10 @@ class sram_base(design): # inputs, outputs/output/bar inputs = [] outputs = [] - for i in range(self.col_addr_size): - inputs.append("ADDR[{}]".format(i)) - outputs.append("A[{}]".format(i)) + for k in range(self.total_ports): + for i in range(self.col_addr_size): + inputs.append("ADDR{}[{}]".format(k,i)) + outputs.append("A{}[{}]".format(k,i)) self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) return inst @@ -400,9 +353,10 @@ class sram_base(design): # inputs, outputs/output/bar inputs = [] outputs = [] - for i in range(self.word_size): - inputs.append("DIN[{}]".format(i)) - outputs.append("BANK_DIN[{}]".format(i)) + for k in range(self.total_write): + for i in range(self.word_size): + inputs.append("DIN{}[{}]".format(k,i)) + outputs.append("BANK_DIN{}[{}]".format(k,i)) self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) return inst diff --git a/compiler/sram_config.py b/compiler/sram_config.py new file mode 100644 index 00000000..e7c80fd8 --- /dev/null +++ b/compiler/sram_config.py @@ -0,0 +1,96 @@ +import debug +from math import log,sqrt,ceil +from importlib import reload +from globals import OPTS + +class sram_config: + """ This is a structure that is used to hold the SRAM configuration options. """ + + def __init__(self, word_size, num_words, num_banks=1): + self.word_size = word_size + self.num_words = num_words + self.num_banks = num_banks + + # This will get over-written when we determine the organization + self.words_per_row = None + + # Move the module names to this? + + + def set_local_config(self, module): + """ Copy all of the member variables to the given module for convenience """ + + members = [attr for attr in dir(self) if not callable(getattr(self, attr)) and not attr.startswith("__")] + + # Copy all the variables to the local module + for member in members: + setattr(module,member,getattr(self,member)) + + def compute_sizes(self): + """ Computes the organization of the memory using bitcell size by trying to make it square.""" + + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + # pass a copy of myself for the port numbers + self.bitcell = self.mod_bitcell() + + + debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.") + + self.num_words_per_bank = self.num_words/self.num_banks + self.num_bits_per_bank = self.word_size*self.num_words_per_bank + + # If this was hard coded, don't dynamically compute it! + if not self.words_per_row: + # Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry) + self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank + self.bank_side_length = sqrt(self.bank_area) + + # Estimate the words per row given the height of the bitcell and the square side length + self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width) + self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size) + + # Estimate the number of rows given the tentative words per row + self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size) + self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row) + + # Fix the number of columns and rows + self.num_cols = int(self.words_per_row*self.word_size) + self.num_rows = int(self.num_words_per_bank/self.words_per_row) + + # Compute the address and bank sizes + self.row_addr_size = int(log(self.num_rows, 2)) + self.col_addr_size = int(log(self.words_per_row, 2)) + self.bank_addr_size = self.col_addr_size + self.row_addr_size + self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2)) + + debug.info(1,"Words per row: {}".format(self.words_per_row)) + + def estimate_words_per_row(self,tentative_num_cols, word_size): + """ + This provides a heuristic rounded estimate for the number of words + per row. + """ + + if tentative_num_cols < 1.5*word_size: + return 1 + elif tentative_num_cols > 3*word_size: + return 4 + else: + return 2 + + def amend_words_per_row(self,tentative_num_rows, words_per_row): + """ + This picks the number of words per row more accurately by limiting + it to a minimum and maximum. + """ + # Recompute the words per row given a hard max + if(not OPTS.is_unit_test and tentative_num_rows > 512): + debug.check(tentative_num_rows*words_per_row <= 2048, "Number of words exceeds 2048") + return int(words_per_row*tentative_num_rows/512) + # Recompute the words per row given a hard min + if(not OPTS.is_unit_test and tentative_num_rows < 16): + debug.check(tentative_num_rows*words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows)) + return int(words_per_row*tentative_num_rows/16) + + return words_per_row diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 1db09553..0b6bd8f5 100755 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -13,52 +13,81 @@ import debug OPTS = globals.OPTS -@unittest.skip("SKIPPING 04_pbitcell_test") +#@unittest.skip("SKIPPING 04_pbitcell_test") class pbitcell_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import pbitcell + from pbitcell import pbitcell import tech - + OPTS.num_rw_ports=1 + OPTS.num_w_ports=1 + OPTS.num_r_ports=1 debug.info(2, "Bitcell with 1 of each port: read/write, write, and read") - tx = pbitcell.pbitcell(num_readwrite=1,num_write=1,num_read=1) + tx = pbitcell() self.local_check(tx) + OPTS.num_rw_ports=0 + OPTS.num_w_ports=1 + OPTS.num_r_ports=1 debug.info(2, "Bitcell with 0 read/write ports") - tx = pbitcell.pbitcell(num_readwrite=0,num_write=1,num_read=1) + tx = pbitcell() self.local_check(tx) + OPTS.num_rw_ports=1 + OPTS.num_w_ports=0 + OPTS.num_r_ports=1 debug.info(2, "Bitcell with 0 write ports") - tx = pbitcell.pbitcell(num_readwrite=1,num_write=0,num_read=1) + tx = pbitcell() self.local_check(tx) + OPTS.num_rw_ports=1 + OPTS.num_w_ports=1 + OPTS.num_r_ports=0 debug.info(2, "Bitcell with 0 read ports") - tx = pbitcell.pbitcell(num_readwrite=1,num_write=1,num_read=0) + tx = pbitcell() self.local_check(tx) + OPTS.num_rw_ports=1 + OPTS.num_w_ports=0 + OPTS.num_r_ports=0 debug.info(2, "Bitcell with 0 read ports and 0 write ports") - tx = pbitcell.pbitcell(num_readwrite=1,num_write=0,num_read=0) + tx = pbitcell() self.local_check(tx) + OPTS.num_rw_ports=2 + OPTS.num_w_ports=2 + OPTS.num_r_ports=2 debug.info(2, "Bitcell with 2 of each port: read/write, write, and read") - tx = pbitcell.pbitcell(num_readwrite=2,num_write=2,num_read=2) + tx = pbitcell() self.local_check(tx) + OPTS.num_rw_ports=0 + OPTS.num_w_ports=2 + OPTS.num_r_ports=2 debug.info(2, "Bitcell with 0 read/write ports") - tx = pbitcell.pbitcell(num_readwrite=0,num_write=2,num_read=2) + tx = pbitcell() self.local_check(tx) + OPTS.num_rw_ports=2 + OPTS.num_w_ports=0 + OPTS.num_r_ports=2 debug.info(2, "Bitcell with 0 write ports") - tx = pbitcell.pbitcell(num_readwrite=2,num_write=0,num_read=2) + tx = pbitcell() self.local_check(tx) + OPTS.num_rw_ports=2 + OPTS.num_w_ports=2 + OPTS.num_r_ports=0 debug.info(2, "Bitcell with 0 read ports") - tx = pbitcell.pbitcell(num_readwrite=2,num_write=2,num_read=0) + tx = pbitcell() self.local_check(tx) + OPTS.num_rw_ports=2 + OPTS.num_w_ports=0 + OPTS.num_r_ports=0 debug.info(2, "Bitcell with 0 read ports and 0 write ports") - tx = pbitcell.pbitcell(num_readwrite=2,num_write=0,num_read=0) + tx = pbitcell() self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py index bcf11473..8e44aa52 100755 --- a/compiler/tests/04_precharge_test.py +++ b/compiler/tests/04_precharge_test.py @@ -24,9 +24,9 @@ class precharge_test(openram_test): debug.info(2, "Checking precharge for pbitcell") OPTS.bitcell = "pbitcell" - OPTS.rw_ports = 2 - OPTS.r_ports = 2 - OPTS.w_ports = 2 + OPTS.num_rw_ports = 2 + OPTS.num_r_ports = 2 + OPTS.num_w_ports = 2 tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="rwbl0", bitcell_br="rwbl_bar0") self.local_check(tx) diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index 1517c3b7..4fc75ac5 100755 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 05_pbitcell_array_test") +#@unittest.skip("SKIPPING 05_pbitcell_array_test") class pbitcell_array_test(openram_test): def runTest(self): @@ -20,26 +20,26 @@ class pbitcell_array_test(openram_test): debug.info(2, "Testing 4x4 array for multiport bitcell, with read ports at the edge of the bit cell") OPTS.bitcell = "pbitcell" - OPTS.rw_ports = 2 - OPTS.r_ports = 2 - OPTS.w_ports = 2 - a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) + OPTS.num_rw_ports = 2 + OPTS.num_r_ports = 2 + OPTS.num_w_ports = 2 + a = bitcell_array.bitcell_array(name="pbitcell_array_Rport_edge", cols=4, rows=4) self.local_check(a) debug.info(2, "Testing 4x4 array for multiport bitcell, with write ports at the edge of the bit cell") OPTS.bitcell = "pbitcell" - OPTS.rw_ports = 2 - OPTS.r_ports = 0 - OPTS.w_ports = 2 - a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) + OPTS.num_rw_ports = 2 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 2 + a = bitcell_array.bitcell_array(name="pbitcell_array_Wport_edge", cols=4, rows=4) self.local_check(a) debug.info(2, "Testing 4x4 array for multiport bitcell, with read/write ports at the edge of the bit cell") OPTS.bitcell = "pbitcell" - OPTS.rw_ports = 2 - OPTS.r_ports = 0 - OPTS.w_ports = 0 - a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) + OPTS.num_rw_ports = 2 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + a = bitcell_array.bitcell_array(name="pbitcell_array_RWport_edge", cols=4, rows=4) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index 9592b6c5..49530410 100755 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -24,9 +24,9 @@ class precharge_test(openram_test): debug.info(2, "Checking precharge for pbitcell") OPTS.bitcell = "pbitcell" - OPTS.rw_ports = 2 - OPTS.r_ports = 2 - OPTS.w_ports = 2 + OPTS.num_rw_ports = 2 + OPTS.num_r_ports = 2 + OPTS.num_w_ports = 2 pc = precharge_array.precharge_array(columns=3, bitcell_bl="rwbl0", bitcell_br="rwbl_bar0") self.local_check(pc) diff --git a/compiler/tests/08_wordline_driver_test.py b/compiler/tests/08_wordline_driver_test.py index 9c91c7dd..3a19a2ec 100755 --- a/compiler/tests/08_wordline_driver_test.py +++ b/compiler/tests/08_wordline_driver_test.py @@ -20,10 +20,21 @@ class wordline_driver_test(openram_test): import wordline_driver import tech + # check wordline driver array in single port debug.info(2, "Checking driver") tx = wordline_driver.wordline_driver(rows=8) self.local_check(tx) + # check wordline driver array in multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + debug.info(2, "Checking driver (multi-port case)") + tx = wordline_driver.wordline_driver(rows=8) + self.local_check(tx) + globals.end_openram() # instantiate a copy of the class to actually run the test diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py index 51620495..f6f9e14f 100755 --- a/compiler/tests/09_sense_amp_array_test.py +++ b/compiler/tests/09_sense_amp_array_test.py @@ -17,7 +17,7 @@ class sense_amp_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) import sense_amp_array - + # check sense amp array in single port debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2") a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=2) self.local_check(a) @@ -26,6 +26,20 @@ class sense_amp_test(openram_test): a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=4) self.local_check(a) + # check sense amp array in multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2 (multi-port case)") + a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=2) + self.local_check(a) + + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4 (multi-port case)") + a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=4) + self.local_check(a) + globals.end_openram() # instantiate a copy of the class to actually run the test diff --git a/compiler/tests/10_write_driver_array_test.py b/compiler/tests/10_write_driver_array_test.py index 27538d0b..67978f11 100755 --- a/compiler/tests/10_write_driver_array_test.py +++ b/compiler/tests/10_write_driver_array_test.py @@ -17,6 +17,7 @@ class write_driver_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) import write_driver_array + # check write driver array in single port debug.info(2, "Testing write_driver_array for columns=8, word_size=8") a = write_driver_array.write_driver_array(columns=8, word_size=8) self.local_check(a) @@ -25,6 +26,20 @@ class write_driver_test(openram_test): a = write_driver_array.write_driver_array(columns=16, word_size=8) self.local_check(a) + # check write driver array in multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case)") + a = write_driver_array.write_driver_array(columns=8, word_size=8) + self.local_check(a) + + debug.info(2, "Testing write_driver_array for columns=16, word_size=8 (multi-port case)") + a = write_driver_array.write_driver_array(columns=16, word_size=8) + self.local_check(a) + globals.end_openram() # instantiate a copy of the class to actually run the test diff --git a/compiler/tests/19_multi_bank_test.py b/compiler/tests/19_multi_bank_test.py index 2169dcf1..9bab2a4f 100755 --- a/compiler/tests/19_multi_bank_test.py +++ b/compiler/tests/19_multi_bank_test.py @@ -16,21 +16,34 @@ class multi_bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) from bank import bank + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=16) + c.num_banks=2 + + c.words_per_row=1 debug.info(1, "No column mux") - a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=2, name="bank1_multi") + a = bank(c, name="bank1_multi") self.local_check(a) + c.num_words=32 + c.words_per_row=2 debug.info(1, "Two way column mux") - a = bank(word_size=4, num_words=32, words_per_row=2, num_banks=2, name="bank2_multi") + a = bank(c, name="bank2_multi") self.local_check(a) + c.num_words=64 + c.words_per_row=4 debug.info(1, "Four way column mux") - a = bank(word_size=4, num_words=64, words_per_row=4, num_banks=2, name="bank3_multi") + a = bank(c, name="bank3_multi") self.local_check(a) + c.word_size=2 + c.num_words=128 + c.words_per_row=8 debug.info(1, "Eight way column mux") - a = bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="bank4_multi") + a = bank(c, name="bank4_multi") self.local_check(a) globals.end_openram() diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py index fad2b100..f377a3db 100755 --- a/compiler/tests/19_psingle_bank_test.py +++ b/compiler/tests/19_psingle_bank_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 19_psingle_bank_test") +#@unittest.skip("SKIPPING 19_psingle_bank_test") class psingle_bank_test(openram_test): def runTest(self): @@ -20,65 +20,103 @@ class psingle_bank_test(openram_test): import verify from bank import bank + from sram_config import sram_config OPTS.bitcell = "pbitcell" - # testing all port configurations (with no column mux) to verify layout between bitcell array and peripheral circuitry - OPTS.rw_ports = 2 - OPTS.w_ports = 2 - OPTS.r_ports = 2 - + # testing layout of bank using pbitcell with 1 RW port (a 6T-cell equivalent) + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + c = sram_config(word_size=4, + num_words=16) + c.words_per_row=1 debug.info(1, "No column mux") - a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_2w_2r_single") + a = bank(c, name="bank1_1rw_0w_0r_single") self.local_check(a) """ - OPTS.rw_ports = 0 - OPTS.w_ports = 2 - OPTS.r_ports = 2 - + # multiport can't generate layout yet on the bank level + OPTS.netlist_only = True + + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 1 + debug.info(1, "No column mux") - a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_0rw_2w_2r_single") + name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) - OPTS.rw_ports = 2 - OPTS.w_ports = 0 - OPTS.r_ports = 2 + OPTS.num_rw_ports = c.num_rw_ports = 2 + OPTS.num_w_ports = c.num_w_ports = 2 + OPTS.num_r_ports = c.num_r_ports = 2 debug.info(1, "No column mux") - a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_0w_2r_single") + name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) + self.local_check(a) + + OPTS.num_rw_ports = c.num_rw_ports = 0 + OPTS.num_w_ports = c.num_w_ports = 2 + OPTS.num_r_ports = c.num_r_ports = 2 + + debug.info(1, "No column mux") + name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) - OPTS.rw_ports = 2 - OPTS.w_ports = 2 - OPTS.r_ports = 0 + OPTS.num_rw_ports = c.num_rw_ports = 2 + OPTS.num_w_ports = c.num_w_ports = 0 + OPTS.num_r_ports = c.num_r_ports = 2 debug.info(1, "No column mux") - a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_2w_0r_single") + name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) - OPTS.rw_ports = 2 - OPTS.w_ports = 0 - OPTS.r_ports = 0 + OPTS.num_rw_ports = c.num_rw_ports = 2 + OPTS.num_w_ports = c.num_w_ports = 2 + OPTS.num_r_ports = c.num_r_ports = 0 debug.info(1, "No column mux") - a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_0w_0r_single") + name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) + self.local_check(a) + + OPTS.num_rw_ports = c.num_rw_ports = 2 + OPTS.num_w_ports = c.num_w_ports = 0 + OPTS.num_r_ports = c.num_r_ports = 0 + + debug.info(1, "No column mux") + name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) # testing with various column muxes - OPTS.rw_ports = 2 - OPTS.w_ports = 2 - OPTS.r_ports = 2 + OPTS.num_rw_ports = c.num_rw_ports = 2 + OPTS.num_w_ports = c.num_w_ports = 2 + OPTS.num_r_ports = c.num_r_ports = 2 + c.num_words=32 + c.words_per_row=2 debug.info(1, "Two way column mux") - a = bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="bank2_single") + name = "bank2_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) + c.num_words=64 + c.words_per_row=4 debug.info(1, "Four way column mux") - a = bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="bank3_single") + name = "bank3_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) # Eight way has a short circuit of one column mux select to gnd rail + c.word_size=2 + c.num_words=128 + c.words_per_row=8 debug.info(1, "Eight way column mux") - a = bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4_single") + name = "bank4_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) """ diff --git a/compiler/tests/19_single_bank_test.py b/compiler/tests/19_single_bank_test.py index 25567e82..3c32b30d 100755 --- a/compiler/tests/19_single_bank_test.py +++ b/compiler/tests/19_single_bank_test.py @@ -16,22 +16,34 @@ class single_bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) from bank import bank + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=16) + + c.words_per_row=1 debug.info(1, "No column mux") - a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_single") - self.local_check(a) - - debug.info(1, "Two way column mux") - a = bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="bank2_single") + a = bank(c, name="bank1_single") self.local_check(a) + c.num_words=32 + c.words_per_row=2 + debug.info(1, "Two way column mux") + a = bank(c, name="bank2_single") + self.local_check(a) + + c.num_words=64 + c.words_per_row=4 debug.info(1, "Four way column mux") - a = bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="bank3_single") + a = bank(c, name="bank3_single") self.local_check(a) # Eight way has a short circuit of one column mux select to gnd rail + c.word_size=2 + c.num_words=128 + c.words_per_row=8 debug.info(1, "Eight way column mux") - a = bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4_single") + a = bank(c, name="bank4_single") self.local_check(a) globals.end_openram() diff --git a/compiler/tests/20_sram_1bank_test.py b/compiler/tests/20_sram_1bank_test.py index 7079b715..ce482b6d 100755 --- a/compiler/tests/20_sram_1bank_test.py +++ b/compiler/tests/20_sram_1bank_test.py @@ -16,22 +16,34 @@ class sram_1bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) from sram import sram - + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=16, + num_banks=1) + + c.words_per_row=1 debug.info(1, "Single bank, no column mux with control logic") - a = sram(word_size=4, num_words=16, num_banks=1, name="sram1") + a = sram(c, "sram1") self.local_check(a, final_verification=True) - # debug.info(1, "Single bank two way column mux with control logic") - # a = sram(word_size=4, num_words=32, num_banks=1, name="sram2") - # self.local_check(a, final_verification=True) + c.num_words=32 + c.words_per_row=2 + debug.info(1, "Single bank two way column mux with control logic") + a = sram(c, "sram2") + self.local_check(a, final_verification=True) - # debug.info(1, "Single bank, four way column mux with control logic") - # a = sram(word_size=4, num_words=64, num_banks=1, name="sram3") - # self.local_check(a, final_verification=True) + c.num_words=64 + c.words_per_row=4 + debug.info(1, "Single bank, four way column mux with control logic") + a = sram(c, "sram3") + self.local_check(a, final_verification=True) - # debug.info(1, "Single bank, eight way column mux with control logic") - # a = sram(word_size=2, num_words=128, num_banks=1, name="sram4") - # self.local_check(a, final_verification=True) + c.word_size=2 + c.num_words=128 + c.words_per_row=8 + debug.info(1, "Single bank, eight way column mux with control logic") + a = sram(c, "sram4") + self.local_check(a, final_verification=True) globals.end_openram() diff --git a/compiler/tests/20_sram_2bank_test.py b/compiler/tests/20_sram_2bank_test.py index 00be14df..ff9fbaea 100755 --- a/compiler/tests/20_sram_2bank_test.py +++ b/compiler/tests/20_sram_2bank_test.py @@ -17,21 +17,33 @@ class sram_2bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) from sram import sram + from sram_config import sram_config + c = sram_config(word_size=16, + num_words=32, + num_banks=2) + c.words_per_row=1 debug.info(1, "Two bank, no column mux with control logic") - a = sram(word_size=16, num_words=32, num_banks=2, name="sram1") + a = sram(c, "sram1") self.local_check(a, final_verification=True) + c.num_words=64 + c.words_per_row=2 debug.info(1, "Two bank two way column mux with control logic") - a = sram(word_size=16, num_words=64, num_banks=2, name="sram2") + a = sram(c, "sram2") self.local_check(a, final_verification=True) + c.num_words=128 + c.words_per_row=4 debug.info(1, "Two bank, four way column mux with control logic") - a = sram(word_size=16, num_words=128, num_banks=2, name="sram3") + a = sram(c, "sram3") self.local_check(a, final_verification=True) + c.word_size=2 + c.num_words=256 + c.words_per_row=8 debug.info(1, "Two bank, eight way column mux with control logic") - a = sram(word_size=2, num_words=256, num_banks=2, name="sram4") + a = sram(c, "sram4") self.local_check(a, final_verification=True) globals.end_openram() diff --git a/compiler/tests/20_sram_4bank_test.py b/compiler/tests/20_sram_4bank_test.py index e4a28090..fb34d3b0 100755 --- a/compiler/tests/20_sram_4bank_test.py +++ b/compiler/tests/20_sram_4bank_test.py @@ -17,21 +17,32 @@ class sram_4bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) from sram import sram + from sram_config import sram_config + c = sram_config(word_size=16, + num_words=64, + num_banks=4) debug.info(1, "Four bank, no column mux with control logic") - a = sram(word_size=16, num_words=64, num_banks=4, name="sram1") + a = sram(c, "sram1") self.local_check(a, final_verification=True) + c.num_words=128 + c.words_per_row=2 debug.info(1, "Four bank two way column mux with control logic") - a = sram(word_size=16, num_words=128, num_banks=4, name="sram2") + a = sram(c, "sram2") self.local_check(a, final_verification=True) + c.num_words=256 + c.words_per_row=4 debug.info(1, "Four bank, four way column mux with control logic") - a = sram(word_size=16, num_words=256, num_banks=4, name="sram3") + a = sram(c, "sram3") self.local_check(a, final_verification=True) + c.word_size=2 + c.num_words=256 + c.words_per_row=8 debug.info(1, "Four bank, eight way column mux with control logic") - a = sram.sram(word_size=2, num_words=256, num_banks=4, name="sram4") + a = sram.sram(c, "sram4") self.local_check(a, final_verification=True) globals.end_openram() diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index dd2d14de..93fc8413 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -27,14 +27,14 @@ class timing_sram_test(openram_test): if not OPTS.spice_exe: debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - import sram - import tech + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=1, + num_words=16, + num_banks=1) + c.words_per_row=1 debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") - s = sram.sram(word_size=OPTS.word_size, - num_words=OPTS.num_words, - num_banks=OPTS.num_banks, - name="sram1") - + s = sram(c, name="sram1") tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index cd41c798..d54cdf54 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -27,13 +27,14 @@ class timing_sram_test(openram_test): if not OPTS.spice_exe: debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - import sram - import tech + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=1, + num_words=16, + num_banks=1) + c.words_per_row=1 debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") - s = sram.sram(word_size=OPTS.word_size, - num_words=OPTS.num_words, - num_banks=OPTS.num_banks, - name="sram1") + s = sram(c, name="sram1") tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) diff --git a/compiler/tests/23_lib_sram_model_test.py b/compiler/tests/23_lib_sram_model_test.py index 5d1d641c..8d5064d5 100755 --- a/compiler/tests/23_lib_sram_model_test.py +++ b/compiler/tests/23_lib_sram_model_test.py @@ -16,15 +16,15 @@ class lib_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import sram from characterizer import lib - - debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank") - s = sram.sram(word_size=2, - num_words=16, - num_banks=1, - name="sram_2_16_1_{0}".format(OPTS.tech_name)) - + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=2, + num_words=16, + num_banks=1) + c.words_per_row=1 + debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank") + s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) diff --git a/compiler/tests/23_lib_sram_prune_test.py b/compiler/tests/23_lib_sram_prune_test.py index 35de23aa..952072aa 100755 --- a/compiler/tests/23_lib_sram_prune_test.py +++ b/compiler/tests/23_lib_sram_prune_test.py @@ -26,13 +26,14 @@ class lib_test(openram_test): if not OPTS.spice_exe: debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - import sram - - debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank") - s = sram.sram(word_size=2, - num_words=16, - num_banks=1, - name="sram_2_16_1_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=2, + num_words=16, + num_banks=1) + c.words_per_row=1 + debug.info(1, "Testing pruned timing for sample 2 bit, 16 words SRAM with 1 bank") + s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) diff --git a/compiler/tests/23_lib_sram_test.py b/compiler/tests/23_lib_sram_test.py index 8dc5c7a0..1b2d317c 100755 --- a/compiler/tests/23_lib_sram_test.py +++ b/compiler/tests/23_lib_sram_test.py @@ -26,13 +26,14 @@ class lib_test(openram_test): if not OPTS.spice_exe: debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - import sram - + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=2, + num_words=16, + num_banks=1) + c.words_per_row=1 debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank") - s = sram.sram(word_size=2, - num_words=16, - num_banks=1, - name="sram_2_16_1_{0}".format(OPTS.tech_name)) + s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) diff --git a/compiler/tests/24_lef_sram_test.py b/compiler/tests/24_lef_sram_test.py index 99f13d2d..f66104b6 100755 --- a/compiler/tests/24_lef_sram_test.py +++ b/compiler/tests/24_lef_sram_test.py @@ -17,13 +17,14 @@ class lef_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import sram - + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=2, + num_words=16, + num_banks=1) + c.words_per_row=1 debug.info(1, "Testing LEF for sample 2 bit, 16 words SRAM with 1 bank") - s = sram.sram(word_size=2, - num_words=OPTS.num_words, - num_banks=OPTS.num_banks, - name="sram_2_16_1_{0}".format(OPTS.tech_name)) + s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) gdsfile = s.name + ".gds" leffile = s.name + ".lef" diff --git a/compiler/tests/25_verilog_sram_test.py b/compiler/tests/25_verilog_sram_test.py index fe0be3d9..48ba29e8 100755 --- a/compiler/tests/25_verilog_sram_test.py +++ b/compiler/tests/25_verilog_sram_test.py @@ -16,13 +16,14 @@ class verilog_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import sram - + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=2, + num_words=16, + num_banks=1) + c.words_per_row=1 debug.info(1, "Testing Verilog for sample 2 bit, 16 words SRAM with 1 bank") - s = sram.sram(word_size=2, - num_words=OPTS.num_words, - num_banks=OPTS.num_banks, - name="sram_2_16_1_{0}".format(OPTS.tech_name)) + s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) vfile = s.name + ".v" vname = OPTS.openram_temp + vfile diff --git a/technology/freepdk45/gds_lib/replica_cell_6t.gds b/technology/freepdk45/gds_lib/replica_cell_6t.gds index 9b301c08..2881a883 100644 Binary files a/technology/freepdk45/gds_lib/replica_cell_6t.gds and b/technology/freepdk45/gds_lib/replica_cell_6t.gds differ diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index f232a2a7..3c28fab5 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -56,6 +56,7 @@ layer["via9"] = 28 layer["metal10"] = 29 layer["text"] = 239 layer["boundary"]= 239 +layer["blockage"]= 239 ################################################### ##END GDS Layer Map @@ -98,6 +99,8 @@ drc["minwidth_poly"] = 0.05 drc["poly_to_poly"] = 0.14 # POLY.3 Minimum poly extension beyond active drc["poly_extend_active"] = 0.055 +# Not a rule +drc["poly_to_polycontact"] = 0.075 # POLY.4 Minimum enclosure of active around gate drc["active_enclosure_gate"] = 0.07 # POLY.5 Minimum spacing of field poly to active diff --git a/technology/scn3me_subm/gds_lib/cell_6t.gds b/technology/scn3me_subm/gds_lib/cell_6t.gds index ec926456..20e5367e 100644 Binary files a/technology/scn3me_subm/gds_lib/cell_6t.gds and b/technology/scn3me_subm/gds_lib/cell_6t.gds differ diff --git a/technology/scn3me_subm/gds_lib/dff.gds b/technology/scn3me_subm/gds_lib/dff.gds index b96235c7..07c37298 100644 Binary files a/technology/scn3me_subm/gds_lib/dff.gds and b/technology/scn3me_subm/gds_lib/dff.gds differ diff --git a/technology/scn3me_subm/gds_lib/ms_flop.gds b/technology/scn3me_subm/gds_lib/ms_flop.gds index 0e06e532..e1c071be 100644 Binary files a/technology/scn3me_subm/gds_lib/ms_flop.gds and b/technology/scn3me_subm/gds_lib/ms_flop.gds differ diff --git a/technology/scn3me_subm/gds_lib/replica_cell_6t.gds b/technology/scn3me_subm/gds_lib/replica_cell_6t.gds index af9bd1b8..12d97796 100644 Binary files a/technology/scn3me_subm/gds_lib/replica_cell_6t.gds and b/technology/scn3me_subm/gds_lib/replica_cell_6t.gds differ diff --git a/technology/scn3me_subm/gds_lib/sense_amp.gds b/technology/scn3me_subm/gds_lib/sense_amp.gds index d5ddd7d4..0fc7eb56 100644 Binary files a/technology/scn3me_subm/gds_lib/sense_amp.gds and b/technology/scn3me_subm/gds_lib/sense_amp.gds differ diff --git a/technology/scn3me_subm/gds_lib/tri_gate.gds b/technology/scn3me_subm/gds_lib/tri_gate.gds index b3d696d0..a3f25a39 100644 Binary files a/technology/scn3me_subm/gds_lib/tri_gate.gds and b/technology/scn3me_subm/gds_lib/tri_gate.gds differ diff --git a/technology/scn3me_subm/gds_lib/write_driver.gds b/technology/scn3me_subm/gds_lib/write_driver.gds index bf497a25..fdd3ad88 100644 Binary files a/technology/scn3me_subm/gds_lib/write_driver.gds and b/technology/scn3me_subm/gds_lib/write_driver.gds differ diff --git a/technology/scn3me_subm/mag_lib/cell_6t.gds b/technology/scn3me_subm/mag_lib/cell_6t.gds deleted file mode 100644 index c6fdb0e8..00000000 Binary files a/technology/scn3me_subm/mag_lib/cell_6t.gds and /dev/null differ diff --git a/technology/scn3me_subm/mag_lib/cell_6t.mag b/technology/scn3me_subm/mag_lib/cell_6t.mag index b38f7488..f2e9906a 100644 --- a/technology/scn3me_subm/mag_lib/cell_6t.mag +++ b/technology/scn3me_subm/mag_lib/cell_6t.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1521677056 +timestamp 1536091415 << nwell >> rect -8 29 42 51 << pwell >> @@ -105,13 +105,13 @@ rect 6 2 10 48 rect 24 -2 28 48 rect 32 33 36 48 rect 32 -2 36 29 -<< m3p >> +<< bb >> rect 0 0 34 46 << labels >> rlabel metal2 0 0 0 0 1 gnd rlabel metal2 34 0 34 0 1 gnd rlabel m2contact 17 46 17 46 5 vdd -rlabel metal1 4 7 4 7 1 WL -rlabel metal2 8 43 8 43 1 BL -rlabel metal2 26 43 26 43 1 BR +rlabel metal2 8 43 8 43 1 bl +rlabel metal2 26 43 26 43 1 br +rlabel metal1 4 7 4 7 1 wl << end >> diff --git a/technology/scn3me_subm/mag_lib/dff.mag b/technology/scn3me_subm/mag_lib/dff.mag index 19825153..46d22c84 100644 --- a/technology/scn3me_subm/mag_lib/dff.mag +++ b/technology/scn3me_subm/mag_lib/dff.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1518823399 +timestamp 1536089597 << nwell >> rect 0 48 109 103 << pwell >> @@ -266,7 +266,7 @@ rect 6 30 10 50 rect 22 20 26 57 rect 70 44 74 70 rect 70 20 74 40 -<< m3p >> +<< bb >> rect 0 0 109 100 << labels >> rlabel m2contact 15 34 15 34 4 clk diff --git a/technology/scn3me_subm/mag_lib/ms_flop.mag b/technology/scn3me_subm/mag_lib/ms_flop.mag index 8b3b1d40..713d264f 100644 --- a/technology/scn3me_subm/mag_lib/ms_flop.mag +++ b/technology/scn3me_subm/mag_lib/ms_flop.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1523479368 +timestamp 1536089622 << nwell >> rect -2 0 18 200 << pwell >> @@ -280,7 +280,7 @@ rect 14 8 20 9 rect 14 4 15 8 rect 19 4 20 8 rect 14 3 20 4 -<< m3p >> +<< bb >> rect 0 0 34 200 << labels >> rlabel metal1 0 8 0 8 2 clk diff --git a/technology/scn3me_subm/mag_lib/replica_cell_6t.mag b/technology/scn3me_subm/mag_lib/replica_cell_6t.mag index 52dd6265..d0dc472f 100644 --- a/technology/scn3me_subm/mag_lib/replica_cell_6t.mag +++ b/technology/scn3me_subm/mag_lib/replica_cell_6t.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1521677136 +timestamp 1536091380 << nwell >> rect -8 29 42 51 << pwell >> @@ -106,13 +106,13 @@ rect 6 2 10 48 rect 24 -2 28 48 rect 32 33 36 48 rect 32 -2 36 29 -<< m3p >> +<< bb >> rect 0 0 34 46 << labels >> rlabel metal2 0 0 0 0 1 gnd rlabel metal2 34 0 34 0 1 gnd rlabel m2contact 17 46 17 46 5 vdd -rlabel metal1 4 7 4 7 1 WL -rlabel metal2 8 43 8 43 1 BL -rlabel metal2 26 43 26 43 1 BR +rlabel metal2 8 43 8 43 1 bl +rlabel metal2 26 43 26 43 1 br +rlabel metal1 4 7 4 7 1 wl << end >> diff --git a/technology/scn3me_subm/mag_lib/sense_amp.mag b/technology/scn3me_subm/mag_lib/sense_amp.mag index b29f7da0..e5fa4373 100644 --- a/technology/scn3me_subm/mag_lib/sense_amp.mag +++ b/technology/scn3me_subm/mag_lib/sense_amp.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1524065550 +timestamp 1536089670 << nwell >> rect 0 0 40 102 << pwell >> @@ -122,7 +122,7 @@ rect 20 44 22 48 rect 3 0 7 11 rect 10 0 14 44 rect 20 0 24 44 -<< m3p >> +<< bb >> rect 0 0 34 163 << labels >> flabel metal1 0 149 0 149 4 FreeSans 26 0 0 0 en diff --git a/technology/scn3me_subm/mag_lib/tri_gate.mag b/technology/scn3me_subm/mag_lib/tri_gate.mag index a393074b..bda635c7 100644 --- a/technology/scn3me_subm/mag_lib/tri_gate.mag +++ b/technology/scn3me_subm/mag_lib/tri_gate.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1524499924 +timestamp 1536089695 << nwell >> rect -2 45 38 73 << pwell >> @@ -86,7 +86,7 @@ rect 24 19 28 23 << metal2 >> rect 15 34 25 38 rect 15 0 19 34 -<< m3p >> +<< bb >> rect 0 0 34 73 << labels >> rlabel metal1 0 12 0 12 3 en diff --git a/technology/scn3me_subm/mag_lib/write_driver.mag b/technology/scn3me_subm/mag_lib/write_driver.mag index 80e09d11..ab2014aa 100644 --- a/technology/scn3me_subm/mag_lib/write_driver.mag +++ b/technology/scn3me_subm/mag_lib/write_driver.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1524499497 +timestamp 1536089714 << nwell >> rect -3 101 37 138 rect -3 0 37 51 @@ -209,7 +209,7 @@ rect 10 196 14 202 rect 20 193 24 202 rect 20 177 24 189 rect 15 0 19 6 -<< m3p >> +<< bb >> rect 0 0 34 202 << labels >> rlabel metal2 15 1 15 1 1 din diff --git a/technology/scn3me_subm/tech/SCN3ME_SUBM.30.tech b/technology/scn3me_subm/tech/SCN3ME_SUBM.30.tech index bf6ce64b..be511001 100644 --- a/technology/scn3me_subm/tech/SCN3ME_SUBM.30.tech +++ b/technology/scn3me_subm/tech/SCN3ME_SUBM.30.tech @@ -276,6 +276,11 @@ cifoutput style lambda=0.30(p) scalefactor 30 15 + # This is a custom section to add bounding boxes in OpenRAM + layer BB bb + labels bb + calma 63 0 + layer CWN nwell,rnw bloat-or pdiff,rpd,pdc/a,pfet * 180 bloat-or nsd,nsc/a * 90 @@ -1506,7 +1511,12 @@ cifinput style lambda=0.30(p) scalefactor 30 - layer nwell CWN + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma 63 0 + +layer nwell CWN and-not CWNR and-not CTA labels CWN diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index 18ec5bca..a3f51604 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -39,8 +39,9 @@ layer["via1"] = 50 layer["metal2"] = 51 layer["via2"] = 61 layer["metal3"] = 62 -layer["text"] = 83 -layer["boundary"] = 83 +layer["text"] = 63 +layer["boundary"] = 63 +layer["blockage"] = 83 ################################################### ##END GDS Layer Map @@ -83,14 +84,14 @@ drc["minwidth_poly"] = 0.6 drc["poly_to_poly"] = 0.9 # 3.3 Minimum gate extension of active drc["poly_extend_active"] = 0.6 -# ?? +# 5.5.b Minimum spacing between poly contact and other poly (alternative rules) drc["poly_to_polycontact"] = 1.2 # ?? drc["active_enclosure_gate"] = 0.0 -# 3.2.a Minimum spacing over field poly -drc["poly_to_field_poly"] = 0.9 # 3.5 Minimum field poly to active drc["poly_to_active"] = 0.3 +# 3.2.a Minimum spacing over field poly +drc["poly_to_field_poly"] = 0.9 # Not a rule drc["minarea_poly"] = 0.0 diff --git a/technology/scn3me_subm/tf/display.drf b/technology/scn3me_subm/tf/display.drf index e9a22348..047879b6 100644 --- a/technology/scn3me_subm/tf/display.drf +++ b/technology/scn3me_subm/tf/display.drf @@ -625,7 +625,7 @@ drDefinePacket( ( display deviceAnt stipple0 solid yellow yellow solid ) ( display winBottomShadow solid solid winColor1 winColor1 solid ) ( display PselectNet dots4 solid brown brown outlineStipple) - ( display comment stipple0 lineStyle0 winBack winBack solid ) + ( display comment stipple0 lineStyle0 winBack winBack outline ) ( display Poly1 dots lineStyle0 red red outlineStipple) ( display Unrouted stipple0 lineStyle1 winColor5 winColor5 solid ) ( display stretch stipple0 solid yellow yellow solid ) diff --git a/technology/scn3me_subm/tf/layers.map b/technology/scn3me_subm/tf/layers.map index d10d5f2d..b5440f23 100644 --- a/technology/scn3me_subm/tf/layers.map +++ b/technology/scn3me_subm/tf/layers.map @@ -13,4 +13,4 @@ Metal2 drawing 51 0 Via2 drawing 61 0 Metal3 drawing 62 0 Glass drawing 52 0 -text drawing 83 0 +comment drawing 63 0