Merge branch 'dev' into multiport_characterization

This commit is contained in:
Hunter Nichols 2018-09-06 00:25:11 -07:00
commit f824d039c6
77 changed files with 1133 additions and 858 deletions

View File

@ -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))

View File

@ -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,

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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=""

View File

@ -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

View File

@ -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

View File

@ -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])

View File

@ -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)

View File

@ -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())

View File

@ -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:

View File

@ -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"])

View File

@ -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])

View File

@ -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)

View File

@ -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]

View File

@ -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)

View File

@ -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()

View File

@ -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"

View File

@ -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

View File

@ -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. """

View File

@ -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.

View File

@ -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()

View File

@ -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.")

View File

@ -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.")

View File

@ -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.")

View File

@ -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)

View File

@ -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):

View File

@ -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

View File

@ -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")

View File

@ -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()

View File

@ -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 """

View File

@ -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 """

View File

@ -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 """

View File

@ -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

96
compiler/sram_config.py Normal file
View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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)
"""

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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 >>

View File

@ -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

View File

@ -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

View File

@ -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 >>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 )

View File

@ -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