Revised LEF and Verilog generation. Does not read GDS for speed improvements.

This commit is contained in:
Matt Guthaus 2017-12-19 09:01:24 -08:00
parent 13902538ff
commit 9a4b2b4341
21 changed files with 5759 additions and 14393 deletions

View File

@ -181,8 +181,8 @@ class control_logic(design.design):
pin=self.clk_inv1.get_pin("A")
self.add_layout_pin(text="clk",
layer="metal1",
offset=pin.ll(),
width=pin.width(),
offset=pin.ll().scale(0,1),
width=pin.rx(),
height=pin.height())
pin=self.clk_inv1.get_pin("gnd")

View File

@ -1,5 +1,5 @@
word_size = 2
num_words = 16
num_words = 128
num_banks = 1
tech_name = "freepdk45"

View File

@ -4,6 +4,8 @@ This provides a set of useful generic types for the gdsMill interface.
import debug
from vector import vector
from tech import GDS
import math
from globals import OPTS
class geometry:
"""
@ -23,40 +25,24 @@ class geometry:
""" override print function output """
debug.error("__repr__ must be overridden by all geometry types.",1)
# def translate_coords(self, coords, mirr, angle, xyShift):
# """Calculate coordinates after flip, rotate, and shift"""
# coordinate = []
# for item in coords:
# x = (item[0]*math.cos(angle)-item[1]*mirr*math.sin(angle)+xyShift[0])
# y = (item[0]*math.sin(angle)+item[1]*mirr*math.cos(angle)+xyShift[1])
# coordinate += [(x, y)]
# return coordinate
class instance(geometry):
"""
An instance of an instance/module with a specified location and
rotation
"""
def __init__(self, name, mod, offset, mirror, rotate):
"""Initializes an instance to represent a module"""
geometry.__init__(self)
debug.check(mirror not in ["R90","R180","R270"], "Please use rotation and not mirroring during instantiation.")
self.name = name
self.mod = mod
self.gds = mod.gds
self.rotate = rotate
self.offset = vector(offset).snap_to_grid()
self.mirror = mirror
self.compute_boundary(offset,mirror,rotate)
debug.info(4, "creating instance: " + self.name)
def gds_write_file(self, newLayout):
"""Recursively writes all the sub-modules in this instance"""
debug.info(4, "writing instance: " + self.name)
# make sure to write out my module/structure
# (it will only be written the first time though)
self.mod.gds_write_file(self.gds)
# now write an instance of my module/structure
newLayout.addInstance(self.gds,
offsetInMicrons=self.offset,
mirror=self.mirror,
rotate=self.rotate)
def transform_coords(self, coords, offset, mirr, angle):
"""Calculate coordinates after flip, rotate, and shift"""
coordinate = []
for item in coords:
x = item[0]*math.cos(angle) - item[1]*mirr*math.sin(angle) + offset[0]
y = item[0]*math.sin(angle) + item[1]*mirr*math.cos(angle) + offset[1]
coordinate += [[x, y]]
return coordinate
def normalize(self):
""" Re-find the LL and UR points after a transform """
(first,second)=self.boundary
@ -67,7 +53,7 @@ class instance(geometry):
def compute_boundary(self,offset=vector(0,0),mirror="",rotate=0):
""" Transform with offset, mirror and rotation to get the absolute pin location.
We must then re-find the ll and ur. The master is the cell instance. """
(ll,ur) = [vector(0,0),vector(self.mod.width,self.mod.height)]
(ll,ur) = [vector(0,0),vector(self.width,self.height)]
if mirror=="MX":
ll=ll.scale(1,-1)
ur=ur.scale(1,-1)
@ -123,6 +109,77 @@ class instance(geometry):
def rx(self):
""" Return the right edge """
return self.boundary[1].x
class instance(geometry):
"""
An instance of an instance/module with a specified location and
rotation
"""
def __init__(self, name, mod, offset, mirror, rotate):
"""Initializes an instance to represent a module"""
geometry.__init__(self)
debug.check(mirror not in ["R90","R180","R270"], "Please use rotation and not mirroring during instantiation.")
self.name = name
self.mod = mod
self.gds = mod.gds
self.rotate = rotate
self.offset = vector(offset).snap_to_grid()
self.mirror = mirror
self.width = mod.width
self.height = mod.height
self.compute_boundary(offset,mirror,rotate)
debug.info(4, "creating instance: " + self.name)
def get_blockages(self, layer, top=False):
""" Retrieve rectangular blockages of all modules in this instance.
Apply the transform of the instance placement to give absolute blockages."""
angle = math.radians(float(self.rotate))
mirr = 1
if self.mirror=="R90":
angle += math.radians(90.0)
elif self.mirror=="R180":
angle += math.radians(180.0)
elif self.mirror=="R270":
angle += math.radians(270.0)
elif self.mirror=="MX":
mirr = -1
elif self.mirror=="MY":
mirr = -1
angle += math.radians(180.0)
elif self.mirror=="XY":
mirr = 1
angle += math.radians(180.0)
if self.mod.is_library_cell:
# For lib cells, block the whole thing
b = self.mod.get_boundary()
newb = self.transform_coords(b, self.offset, mirr, angle)
return [newb]
else:
blockages = self.mod.get_blockages(layer)
new_blockages = []
for b in blockages:
newb = self.transform_coords(b,self.offset, mirr, angle)
new_blockages.append(newb)
return new_blockages
def gds_write_file(self, new_layout):
"""Recursively writes all the sub-modules in this instance"""
debug.info(4, "writing instance: " + self.name)
# make sure to write out my module/structure
# (it will only be written the first time though)
self.mod.gds_write_file(self.gds)
# now write an instance of my module/structure
new_layout.addInstance(self.gds,
offsetInMicrons=self.offset,
mirror=self.mirror,
rotate=self.rotate)
def get_pin(self,name,index=-1):
""" Return an absolute pin that is offset and transformed based on
@ -187,6 +244,10 @@ class path(geometry):
coordinates=self.coordinates,
width=self.path_width)
def get_blockages(self, layer):
""" Fail since we don't support paths yet. """
assert(0)
def __str__(self):
""" override print function output """
return "path: layer=" + self.layerNumber + " w=" + self.width
@ -206,12 +267,12 @@ class label(geometry):
self.text = text
self.layerNumber = layerNumber
self.offset = vector(offset).snap_to_grid()
if zoom<0:
self.zoom = GDS["zoom"]
else:
self.zoom = zoom
self.size = 0
debug.info(4,"creating label " + self.text + " " + str(self.layerNumber) + " " + str(self.offset))
@ -226,6 +287,10 @@ class label(geometry):
magnification=self.zoom,
rotate=None)
def get_blockages(self, layer):
""" Returns an empty list since text cannot be blockages. """
return []
def __str__(self):
""" override print function output """
return "label: " + self.text + " layer=" + str(self.layerNumber)
@ -246,10 +311,18 @@ class rectangle(geometry):
self.size = vector(width, height).snap_to_grid()
self.width = self.size.x
self.height = self.size.y
self.compute_boundary(offset,"",0)
debug.info(4, "creating rectangle (" + str(self.layerNumber) + "): "
+ str(self.width) + "x" + str(self.height) + " @ " + str(self.offset))
def get_blockages(self, layer):
""" Returns a list of one rectangle if it is on this layer"""
if self.layerNumber == layer:
return [[self.offset, vector(self.offset.x+self.width,self.offset.y+self.height)]]
else:
return []
def gds_write_file(self, newLayout):
"""Writes the rectangular shape to GDS"""

View File

@ -43,10 +43,6 @@ class hierarchical_decoder(design.design):
self.create_row_decoder()
self.create_vertical_rail()
self.route_vdd_gnd()
# We only need to call the offset_all_coordinate function when there
# are vertical metal rails.
#if (self.num_inputs >= 4):
# self.offset_all_coordinates()
def add_modules(self):
self.inv = pinv()

View File

@ -7,8 +7,9 @@ from tech import layer as techlayer
import os
from vector import vector
from pin_layout import pin_layout
import lef
class layout:
class layout(lef.lef):
"""
Class consisting of a set of objs and instances for a module
This provides a set of useful generic types for hierarchy
@ -18,7 +19,8 @@ class layout:
layout/netlist and perform LVS/DRC.
"""
def __init__(self, name):
def __init__(self, name):
lef.lef.__init__(self, ["metal1", "metal2", "metal3"])
self.name = name
self.width = None
self.height = None
@ -26,7 +28,7 @@ class layout:
self.objs = [] # Holds all other objects (labels, geometries, etc)
self.pin_map = {} # Holds name->pin_layout map for all pins
self.visited = False # Flag for traversing the hierarchy
self.is_library_cell = False # Flag for library cells
self.gds_read()
############################################################
@ -36,7 +38,6 @@ class layout:
""" This function is called after everything is placed to
shift the origin in the lowest left corner """
offset = self.find_lowest_coords()
#self.offset_attributes(offset)
self.translate_all(offset)
def get_gate_offset(self, x_offset, height, inv_num):
@ -59,19 +60,22 @@ class layout:
def find_lowest_coords(self):
"""Finds the lowest set of 2d cartesian coordinates within
this layout"""
# FIXME: don't depend on 1e9
try:
lowestx1 = min(rect.offset.x for rect in self.objs)
lowesty1 = min(rect.offset.y for rect in self.objs)
except:
[lowestx1, lowesty1] = [1000000.0, 1000000.0]
try:
lowestx2 = min(inst.offset.x for inst in self.insts)
lowesty2 = min(inst.offset.y for inst in self.insts)
except:
[lowestx2, lowesty2] = [1000000.0, 1000000.0]
lowestx1 = min(obj.lx() for obj in self.objs if obj.name!="label")
lowesty1 = min(obj.by() for obj in self.objs if obj.name!="label")
lowestx2 = min(inst.lx() for inst in self.insts)
lowesty2 = min(inst.by() for inst in self.insts)
return vector(min(lowestx1, lowestx2), min(lowesty1, lowesty2))
def find_highest_coords(self):
"""Finds the highest set of 2d cartesian coordinates within
this layout"""
highestx1 = min(obj.rx() for obj in self.objs if obj.name!="label")
highesty1 = min(obj.uy() for obj in self.objs if obj.name!="label")
highestx2 = min(inst.rx() for inst in self.insts)
highesty2 = min(inst.uy() for inst in self.insts)
return vector(min(highestx1, highestx2), min(highesty1, highesty2))
def translate_all(self, offset):
"""
@ -111,9 +115,9 @@ class layout:
if height==0:
height=drc["minwidth_{}".format(layer)]
# negative layers indicate "unused" layers in a given technology
layerNumber = techlayer[layer]
if layerNumber >= 0:
self.objs.append(geometry.rectangle(layerNumber, offset, width, height))
layer_num = techlayer[layer]
if layer_num >= 0:
self.objs.append(geometry.rectangle(layer_num, offset, width, height))
return self.objs[-1]
return None
@ -124,10 +128,10 @@ class layout:
if height==0:
height=drc["minwidth_{}".format(layer)]
# negative layers indicate "unused" layers in a given technology
layerNumber = techlayer[layer]
layer_num = techlayer[layer]
corrected_offset = offset - vector(0.5*width,0.5*height)
if layerNumber >= 0:
self.objs.append(geometry.rectangle(layerNumber, corrected_offset, width, height))
if layer_num >= 0:
self.objs.append(geometry.rectangle(layer_num, corrected_offset, width, height))
return self.objs[-1]
return None
@ -259,9 +263,9 @@ class layout:
"""Adds a text label on the given layer,offset, and zoom level"""
# negative layers indicate "unused" layers in a given technology
debug.info(5,"add label " + str(text) + " " + layer + " " + str(offset))
layerNumber = techlayer[layer]
if layerNumber >= 0:
self.objs.append(geometry.label(text, layerNumber, offset, zoom))
layer_num = techlayer[layer]
if layer_num >= 0:
self.objs.append(geometry.label(text, layer_num, offset, zoom))
return self.objs[-1]
return None
@ -272,9 +276,9 @@ class layout:
import path
# NOTE: (UNTESTED) add_path(...) is currently not used
# negative layers indicate "unused" layers in a given technology
#layerNumber = techlayer[layer]
#if layerNumber >= 0:
# self.objs.append(geometry.path(layerNumber, coordinates, width))
#layer_num = techlayer[layer]
#if layer_num >= 0:
# self.objs.append(geometry.path(layer_num, coordinates, width))
path.path(obj=self,
layer=layer,
@ -390,6 +394,7 @@ class layout:
# open the gds file if it exists or else create a blank layout
if os.path.isfile(self.gds_file):
debug.info(3, "opening %s" % self.gds_file)
self.is_library_cell=True
self.gds = gdsMill.VlsiLayout(units=GDS["unit"])
reader = gdsMill.Gds2reader(self.gds)
reader.loadFromFile(self.gds_file)
@ -440,6 +445,49 @@ class layout:
# self.gds.prepareForWrite()
writer.writeToFile(gds_name)
def get_boundary(self):
""" Return the lower-left and upper-right coordinates of boundary """
# This assumes nothing spans outside of the width and height!
return [vector(0,0), vector(self.width, self.height)]
#return [self.find_lowest_coords(), self.find_highest_coords()]
def get_blockages(self, layer, top_level=False):
"""
Write all of the obstacles in the current (and children) modules to the lef file
Do not write the pins since they aren't obstructions.
"""
if type(layer)==str:
layer_num = techlayer[layer]
else:
layer_num = layer
blockages = []
for i in self.objs:
blockages += i.get_blockages(layer_num)
for i in self.insts:
blockages += i.get_blockages(layer_num)
# Must add pin blockages to non-top cells
if not top_level:
blockages += self.get_pin_blockages(layer_num)
return blockages
def get_pin_blockages(self, layer_num):
""" Return the pin shapes as blockages for non-top-level blocks. """
# FIXME: We don't have a body contact in ptx, so just ignore it for now
import copy
pin_names = copy.deepcopy(self.pins)
if self.name.startswith("pmos") or self.name.startswith("nmos"):
pin_names.remove("B")
blockages = []
for pin_name in pin_names:
pin_list = self.get_pins(pin_name)
for pin in pin_list:
if pin.layer_num==layer_num:
blockages += [pin.rect]
return blockages
def pdf_write(self, pdf_name):
# NOTE: Currently does not work (Needs further research)
#self.pdf_name = self.name + ".pdf"
@ -465,20 +513,22 @@ class layout:
debug.info(0,
"|==============================================================================|")
debug.info(0,
"|========= LIST OF OBJECTS (Rects) FOR: " + self.attr["name"])
"|========= LIST OF OBJECTS (Rects) FOR: " + self.name)
debug.info(0,
"|==============================================================================|")
for obj in self.objs:
debug.info(0, "layer={0} : offset={1} : size={2}".format(
obj.layerNumber, obj.offset, obj.size))
debug.info(0, "layer={0} : offset={1} : size={2}".format(obj.layerNumber,
obj.offset,
obj.size))
debug.info(0,
"|==============================================================================|")
debug.info(0,
"|========= LIST OF INSTANCES FOR: " +
self.attr["name"])
"|========= LIST OF INSTANCES FOR: " + self.name)
debug.info(0,
"|==============================================================================|")
for inst in self.insts:
debug.info(0, "name={0} : mod={1} : offset={2}".format(
inst.name, inst.mod.name, inst.offset))
debug.info(0, "name={0} : mod={1} : offset={2}".format(inst.name,
inst.mod.name,
inst.offset))

View File

@ -2,9 +2,9 @@ import debug
import re
import os
import math
import verilog
class spice:
class spice(verilog.verilog):
"""
This provides a set of useful generic types for hierarchy
management. If a module is a custom designed cell, it will read from
@ -19,7 +19,7 @@ class spice:
self.mods = [] # Holds subckts/mods for this module
self.pins = [] # Holds the pins for this module
self.pin_type = {} # The type map of each pin: INPUT, OUTPUT, INOUT, POWER, GROUND
# for each instance, this is the set of nets/nodes that map to the pins for this instance
# THIS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the
# Spice format)
@ -31,13 +31,35 @@ class spice:
# Spice circuit
############################################################
def add_pin(self, name):
"""Adds a pin to the pins list"""
def add_pin(self, name, pin_type="INOUT"):
""" Adds a pin to the pins list. Default type is INOUT signal. """
self.pins.append(name)
self.pin_type[name]=pin_type
def add_pin_list(self, pin_list):
"""Adds a pin_list to the pins list"""
self.pins = self.pins + pin_list
def add_pin_list(self, pin_list, pin_type_list="INOUT"):
""" Adds a pin_list to the pins list """
# The type list can be a single type for all pins
# or a list that is the same length as the pin list.
if type(pin_type_list)==str:
for pin in pin_list:
self.add_pin(pin,pin_type_list)
elif len(pin_type_list)==len(pin_list):
for (pin,ptype) in zip(pin_list, pin_type_list):
self.add_pin(pin,ptype)
else:
debug.error("Mismatch in type and pin list lengths.", -1)
def get_pin_type(self, name):
""" Returns the type of the signal pin. """
return self.pin_type[name]
def get_pin_dir(self, name):
""" Returns the direction of the pin. (Supply/ground are INOUT). """
if self.pin_type[name] in ["POWER","GROUND"]:
return "INOUT"
else:
return self.pin_type[name]
def add_mod(self, mod):
"""Adds a subckt/submodule to the subckt hierarchy"""

View File

@ -3,6 +3,7 @@ import tech
import globals
import math
import debug
import datetime
from collections import defaultdict
class lef:
@ -10,246 +11,107 @@ class lef:
SRAM LEF Class open GDS file, read pins information, obstruction
and write them to LEF file
"""
def __init__(self, gdsName, lefName, sr):
self.gdsName = gdsName
self.lef = open(lefName,"w")
self.sr = sr
self.myLayout = gdsMill.VlsiLayout(units=tech.GDS["unit"])
self.reader = gdsMill.Gds2reader(self.myLayout)
self.reader.loadFromFile(gdsName)
self.unit = float(self.myLayout.info['units'][0])
self.layer = ["metal1", "via1", "metal2", "via2", "metal3"]
self.create()
def __init__(self,layers):
# LEF db units per micron
self.lef_units = 1000
# These are the layers of the obstructions
self.layer = layers
def lef_write(self, lef_name):
"""Write the entire lef of the object to the file."""
debug.info(3, "Writing to {0}".format(lef_name))
self.indent = "" # To maintain the indent level easily
self.lef_units = 1000
self.lef_layer = ["metal1", "metal2", "metal3"]
self.lef = open(lef_name,"w")
self.lef_write_header()
for pin in self.pins:
self.lef_write_pin(pin)
self.lef_write_obstructions()
self.lef_write_footer()
self.lef.close()
def create(self):
"""Write to LEF file"""
power_pin_name = self.powerPinName()
ground_pin_name = self.groundPinName()
input_pin_name = self.inputPinName()
inout_pin_name = self.inoutPinName()
self.writeLefHeader()
def lef_write_header(self):
""" Header of LEF file """
self.lef.write("VERSION 5.4 ;\n")
self.lef.write("NAMESCASESENSITIVE ON ;\n")
self.lef.write("BUSBITCHARS \"[]\" ;\n")
self.lef.write("DIVIDERCHAR \"/\" ;\n")
self.lef.write("UNITS\n")
self.lef.write(" DATABASE MICRONS {0} ;\n".format(self.lef_units))
self.lef.write("END UNITS\n")
self.lef.write("SITE MacroSite\n")
self.indent += " "
self.lef.write("{0}CLASS Core ;\n".format(self.indent))
self.lef.write("{0}SIZE {1} by {2} ;\n".format(self.indent,
self.lef_units*self.width,
self.lef_units*self.height))
self.indent = self.indent[:-3]
self.lef.write("END MacroSite\n")
for pin in power_pin_name:
self.writePin(pin, 1)
self.lef.write("{0}MACRO {1}\n".format(self.indent,self.name))
self.indent += " "
self.lef.write("{0}CLASS BLOCK ;\n".format(self.indent))
self.lef.write("{0}SIZE {1} BY {2} ;\n" .format(self.indent,
self.lef_units*self.width,
self.lef_units*self.height))
self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent))
self.lef.write("{0}SITE MacroSite ;\n".format(self.indent))
for pin in ground_pin_name:
self.writePin(pin, 2)
for pin in inout_pin_name:
self.writePin(pin, 3)
for pin in input_pin_name:
self.writePin(pin,4)
self.lef.write(" OBS\n")
for lay in self.layer:
self.lef.write(" LAYER {0} ;\n".format(lay))
self.writeObstruct(self.sr.name, lay, mirr = 1, angle = math.radians(float(0)), xyShift = (0, 0))
self.lef.write(" END\n")
self.writeLefFooter()
def coordinatesTranslate(self, coord, mirr, angle, xyShift):
"""Calculate coordinates after flip, rotate, and shift"""
coordinate = []
for item in coord:
x = (item[0]*math.cos(angle)-item[1]*mirr*math.sin(angle)+xyShift[0])
y = (item[0]*math.sin(angle)+item[1]*mirr*math.cos(angle)+xyShift[1])
coordinate += [(x, y)]
return coordinate
def writeObstruct(self, sr, lay, mirr = 1, angle = math.radians(float(0)), xyShift = (0, 0)):
"""Recursive write boundaries on each Structure in GDS file to LEF"""
for boundary in self.myLayout.structures[sr].boundaries:
coordTrans = self.coordinatesTranslate(boundary.coordinates, mirr, angle, xyShift)
rect = self.minMaxCoord(coordTrans)
lay_convert = tech.layer[lay]
if boundary.drawingLayer == lay_convert:
self.lef.write(" RECT ")
for item in rect:
self.lef.write(" {0} {1}".format(item[0]*self.unit, item[1]*self.unit))
self.lef.write(" ;\n")
for sref in self.myLayout.structures[sr].srefs:
sMirr = 1
if sref.transFlags[0] == True:
sMirr = -1
sAngle = math.radians(float(0))
if sref.rotateAngle:
sAngle = math.radians(float(sref.rotateAngle))
sAngle += angle
x = sref.coordinates[0]
y = 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.writeObstruct(sref.sName, lay,sMirr, sAngle, sxyShift)
def writePinCoord(self, sr, pinName, pinLayer, pinCoord, mirr = 1,
angle = math.radians(float(0)), xyShift = (0, 0)):
"""Write PIN information to LEF"""
for boundary in self.myLayout.structures[sr].boundaries:
if (pinLayer == boundary.drawingLayer):
coordTrans = self.coordinatesTranslate(boundary.coordinates, mirr, angle, xyShift)
rect = self.minMaxCoord(coordTrans)
if self.pointInsideRect(pinCoord, rect):
self.lef.write(" RECT ")
for item in rect:
self.lef.write(" {0} {1}".format(item[0]*self.unit, item[1]*self.unit))
self.lef.write(" ;\n")
for sref in self.myLayout.structures[sr].srefs:
sMirr = 1
if sref.transFlags[0] == True:
sMirr = -1
sAngle = math.radians(float(0))
if sref.rotateAngle:
sAngle = math.radians(float(sref.rotateAngle))
sAngle += angle
x = sref.coordinates[0]
y = 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.writePinCoord(sref.sName, pinName, pinLayer, pinCoord, sMirr, sAngle, sxyShift)
def pinLayerCoord(self, sr, pinName):
"""Get Pin Layer and Coordinates {layer:[coord1, coord2, ...]}"""
layCoord = defaultdict(list)
for text in self.myLayout.structures[self.sr.name].texts:
if text.textString.strip('\x00') == pinName:
k = text.drawingLayer
v = text.coordinates
d = {k: v}
layCoord.setdefault(k, []).append(v)
return layCoord
def minMaxCoord(self, coordTrans):
"""Find the lowest and highest conner of a Rectangle"""
coordinate = []
minx = min(coordTrans[0][0], coordTrans[1][0], coordTrans[2][0], coordTrans[3][0])
maxx = max(coordTrans[0][0], coordTrans[1][0], coordTrans[2][0], coordTrans[3][0])
miny = min(coordTrans[0][1], coordTrans[1][1], coordTrans[2][1], coordTrans[3][1])
maxy = max(coordTrans[0][1], coordTrans[1][1], coordTrans[2][1], coordTrans[3][1])
coordinate += [(minx, miny)]
coordinate += [(maxx, maxy)]
return coordinate
def pointInsideRect(self, p, rect):
"""Check if a point is inside a rectangle"""
inside = False
if ((p[0][0] >= rect[0][0])& (p[0][0] <= rect[1][0])
& (p[0][1] >= rect[0][1]) &(p[0][1] <= rect[1][1])):
inside = not inside
return inside
def writeLefHeader(self):
"""Heafer of LEF file"""
coord = self.lowestLeftCorner(self.sr.name, 1, 0.0, (0, 0), [], [], [], [])
self.lef.write("MACRO {0}\n".format(self.sr.name))
self.lef.write(" CLASS RING ;\n")
self.lef.write(" ORIGIN {0} {1} ;\n".format(-coord[0][0]*self.unit, coord[0][1]*self.unit))
self.lef.write(" FOREIGN sram {0} {1} ;\n"
.format(0.0, 0.0))
self.lef.write(" SIZE {0} BY {1} ;\n"
.format(self.sr.width, self.sr.height))
self.lef.write(" SYMMETRY X Y R90 ;\n")
def writeLefFooter(self):
self.lef.write("END {0}\n".format(self.sr.name))
def lef_write_footer(self):
self.lef.write("{0}END {1}\n".format(self.indent,self.name))
self.indent = self.indent[:-3]
self.lef.write("END LIBRARY\n")
def powerPinName(self):
return ["vdd"]
def groundPinName(self):
return ["gnd"]
def inputPinName(self):
input_pin_name = []
for i in range(self.sr.addr_size + int(math.log(self.sr.num_banks, 2))):
input_pin_name.append("ADDR[{0}]".format(i))
input_pin_name.append("CSb")
input_pin_name.append("OEb")
input_pin_name.append("WEb")
input_pin_name.append("clk")
return input_pin_name
def lef_write_pin(self, name):
pin_dir = self.get_pin_dir(name)
pin_type = self.get_pin_type(name)
self.lef.write("{0}PIN {1}\n".format(self.indent,name))
self.indent += " "
def inoutPinName(self):
inout_pin_name = []
for i in range(self.sr.word_size):
inout_pin_name.append("DATA[{0}]".format(i))
self.lef.write("{0}DIRECTION {1} ;\n".format(self.indent,pin_dir))
if pin_type in ["POWER","GROUND"]:
self.lef.write("{0}USE {1} ; \n".format(self.indent,pin_type))
self.lef.write("{0}SHAPE ABUTMENT ; \n".format(self.indent))
return inout_pin_name
def writePin(self, pinName, typ):
self.lef.write(" PIN {0}\n".format(pinName))
if typ == 1:
self.lef.write(" DIRECTION INOUT ;\n")
self.lef.write(" USE POWER ;\n")
self.lef.write(" SHAPE ABUTMENT ;\n")
self.lef.write(" PORT\n")
elif typ == 2:
self.lef.write(" DIRECTION INOUT ;\n")
self.lef.write(" USE GROUND ;\n")
self.lef.write(" SHAPE ABUTMENT ;\n")
self.lef.write(" PORT\n")
elif typ == 3:
self.lef.write(" DIRECTION INOUT ;\n")
self.lef.write(" PORT\n")
elif typ == 4:
self.lef.write(" DIRECTION INPUT ;\n")
self.lef.write(" PORT\n")
else:
debug.error("Invalid pin type on pin {0}".format(pinName))
self.lef.write("{0}PORT\n".format(self.indent))
self.indent += " "
pin_layer_coord = self.pinLayerCoord(self.sr.name, pinName)
for pinLayer in pin_layer_coord:
lay = [key for key, value in tech.layer.iteritems() if value == pinLayer][0]
self.lef.write(" LAYER {0} ;\n".format(lay))
for pinCoord in pin_layer_coord[pinLayer]:
self.writePinCoord(self.sr.name, pinName, pinLayer, pinCoord,
mirr = 1,angle = math.radians(float(0)), xyShift = (0, 0))
self.lef.write(" END\n")
self.lef.write(" END {0}\n".format(pinName))
# We could sort these together to minimize different layer sections, but meh.
pin_list = self.get_pins(name)
for pin in pin_list:
self.lef.write("{0}LAYER {1} ;\n".format(self.indent,pin.layer))
self.lef_write_rect(pin.rect)
# End the PORT
self.indent = self.indent[:-3]
self.lef.write("{0}END\n".format(self.indent))
def lowestLeftCorner(self, sr, mirr = 1, angle = math.radians(float(0)), xyShift = (0, 0), listMinX = [], listMinY = [], listMaxX = [], listMaxY =[]):
"""Recursive find a lowest left conner on each Structure in GDS file"""
for boundary in self.myLayout.structures[sr].boundaries:
coordTrans = self.coordinatesTranslate(boundary.coordinates, mirr, angle, xyShift)
minX = min(coordTrans[0][0], coordTrans[1][0], coordTrans[2][0], coordTrans[3][0])
minY = min(coordTrans[0][1], coordTrans[1][1], coordTrans[2][1], coordTrans[3][1])
maxX = max(coordTrans[0][0], coordTrans[1][0], coordTrans[2][0], coordTrans[3][0])
maxY = max(coordTrans[0][1], coordTrans[1][1], coordTrans[2][1], coordTrans[3][1])
listMinX.append(minX)
listMinY.append(minY)
listMaxX.append(maxX)
listMaxY.append(maxY)
for sref in self.myLayout.structures[sr].srefs:
sMirr = 1
if sref.transFlags[0] == True:
sMirr = -1
sAngle = math.radians(float(0))
if sref.rotateAngle:
sAngle = math.radians(float(sref.rotateAngle))
sAngle += angle
x = sref.coordinates[0]
y = 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.lowestLeftCorner(sref.sName, sMirr, sAngle, sxyShift, listMinX, listMinY, listMaxX, listMaxY)
coordinate = []
lowestX = min(listMinX)
lowestY = min(float(item) for item in listMinY)
highestX = max(float(item) for item in listMaxX)
highestY = max(float(item) for item in listMaxY)
coordinate.append((lowestX, lowestY))
coordinate.append((highestX, highestY))
return coordinate
# End the PIN
self.indent = self.indent[:-3]
self.lef.write("{0}END {1}\n".format(self.indent,name))
def lef_write_obstructions(self):
""" Write all the obstructions on each layer """
self.lef.write("{0}OBS\n".format(self.indent))
for layer in self.lef_layer:
self.lef.write("{0}LAYER {1} ;\n".format(self.indent,layer))
self.indent += " "
blockages = self.get_blockages(layer,True)
for b in blockages:
self.lef_write_rect(b)
self.indent = self.indent[:-3]
self.lef.write("{0}END\n".format(self.indent))
def lef_write_rect(self, rect):
""" Write a LEF rectangle """
self.lef.write("{0}RECT ".format(self.indent))
for item in rect:
self.lef.write(" {0} {1}".format(self.lef_units*item[0], self.lef_units*item[1]))
self.lef.write(" ;\n")

View File

@ -120,17 +120,15 @@ s.gds_write(gdsname)
last_time=print_time("GDS", datetime.datetime.now(), last_time)
# Create a LEF physical model
import lef
lefname = OPTS.output_path + s.name + ".lef"
print("LEF: Writing to {0}".format(lefname))
lef.lef(gdsname,lefname,s)
s.lef_write(lefname)
last_time=print_time("LEF writing", datetime.datetime.now(), last_time)
# Write a verilog model
import verilog
vname = OPTS.output_path + s.name + ".v"
print("Verilog: Writing to {0}".format(vname))
verilog.verilog(vname,s)
s.verilog_write(vname)
last_time=print_time("Verilog writing", datetime.datetime.now(), last_time)
globals.end_openram()

View File

@ -32,6 +32,8 @@ class options(optparse.Values):
use_pex = False
# Remove noncritical memory cells for characterization speed-up
trim_netlist = True
# Use detailed LEF blockages
detailed_blockages = True
# Define the output file paths
output_path = ""
# Define the output file base name

View File

@ -23,6 +23,7 @@ class pin_layout:
self.layer = layer.keys()[layer.values().index(layer_name_num)]
else:
self.layer=layer_name_num
self.layer_num = layer[self.layer]
def __str__(self):
""" override print function output """

View File

@ -74,7 +74,7 @@ class precharge(pgate.pgate):
self.lower_pmos_inst=self.add_inst(name="lower_pmos",
mod=self.pmos,
offset=self.lower_pmos_position)
self.connect_inst(["bl", "clk", "br", "vdd"])
self.connect_inst(["bl", "clk", "BR", "vdd"])
# adds the upper pmos(s) to layout
ydiff = self.pmos.height + 2*self.m1_space + contact.poly.width
@ -150,7 +150,7 @@ class precharge(pgate.pgate):
"""Adds both bit-line and bit-line-bar to the module"""
# adds the BL on metal 2
offset = vector(self.bitcell.get_pin("BL").cx(),0) - vector(0.5 * self.m2_width,0)
self.add_layout_pin(text="BL",
self.add_layout_pin(text="bl",
layer="metal2",
offset=offset,
width=drc['minwidth_metal2'],
@ -158,7 +158,7 @@ class precharge(pgate.pgate):
# adds the BR on metal 2
offset = vector(self.bitcell.get_pin("BR").cx(),0) - vector(0.5 * self.m2_width,0)
self.add_layout_pin(text="BR",
self.add_layout_pin(text="br",
layer="metal2",
offset=offset,
width=drc['minwidth_metal2'],
@ -166,10 +166,10 @@ class precharge(pgate.pgate):
def connect_to_bitlines(self):
self.add_bitline_contacts()
self.connect_pmos(self.lower_pmos_inst.get_pin("S"),self.get_pin("BL"))
self.connect_pmos(self.lower_pmos_inst.get_pin("D"),self.get_pin("BR"))
self.connect_pmos(self.upper_pmos1_inst.get_pin("S"),self.get_pin("BL"))
self.connect_pmos(self.upper_pmos2_inst.get_pin("D"),self.get_pin("BR"))
self.connect_pmos(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl"))
self.connect_pmos(self.lower_pmos_inst.get_pin("D"),self.get_pin("br"))
self.connect_pmos(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl"))
self.connect_pmos(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br"))
def add_bitline_contacts(self):

View File

@ -50,7 +50,6 @@ class precharge_array(design.design):
width=self.width,
height=drc["minwidth_metal1"])
#self.offset_all_coordinates()
def add_insts(self):
"""Creates a precharge array by horizontally tiling the precharge cell"""
@ -60,13 +59,13 @@ class precharge_array(design.design):
inst=self.add_inst(name=name,
mod=self.pc_cell,
offset=offset)
bl_pin = inst.get_pin("BL")
bl_pin = inst.get_pin("bl")
self.add_layout_pin(text="bl[{0}]".format(i),
layer="metal2",
offset=bl_pin.ll(),
width=drc["minwidth_metal2"],
height=bl_pin.height())
br_pin = inst.get_pin("BR")
br_pin = inst.get_pin("br")
self.add_layout_pin(text="br[{0}]".format(i),
layer="metal2",
offset=br_pin.ll(),

View File

@ -45,8 +45,6 @@ class sense_amp_array(design.design):
self.add_sense_amp()
self.connect_rails()
#self.offset_all_coordinates()
def add_sense_amp(self):

View File

@ -90,7 +90,7 @@ class single_level_column_mux(design.design):
""" Connect the poly gate of the two pass transistors """
height=self.nmos2.get_pin("G").uy() - self.nmos1.get_pin("G").by()
self.add_layout_pin(text="col_addr",
self.add_layout_pin(text="sel",
layer="poly",
offset=self.nmos1.get_pin("G").ll(),
height=height)

View File

@ -125,7 +125,7 @@ class single_level_column_mux_array(design.design):
# which select bit should this column connect to depends on the position in the word
sel_index = col % self.words_per_row
# Add the column x offset to find the right select bit
gate_offset = self.mux_inst[col].get_pin("col_addr").bc()
gate_offset = self.mux_inst[col].get_pin("sel").bc()
# height to connect the gate to the correct horizontal row
sel_height = self.get_pin("sel[{}]".format(sel_index)).by()
# use the y offset from the sel pin and the x offset from the gate

View File

@ -67,6 +67,11 @@ class sram(design.design):
# Can remove the following, but it helps for debug!
self.add_lvs_correspondence_points()
self.offset_all_coordinates()
sizes = self.find_highest_coords()
self.width = sizes[0]
self.height = sizes[1]
self.DRC_LVS()
def compute_sizes(self):
@ -131,15 +136,17 @@ class sram(design.design):
""" Add pins for entire SRAM. """
for i in range(self.word_size):
self.add_pin("DATA[{0}]".format(i))
self.add_pin("DATA[{0}]".format(i),"INOUT")
for i in range(self.addr_size):
self.add_pin("ADDR[{0}]".format(i))
self.add_pin("ADDR[{0}]".format(i),"INPUT")
# These are used to create the physical pins too
self.control_logic_inputs=["CSb", "WEb", "OEb", "clk"]
self.control_logic_outputs=["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar", "clk_buf"]
for pin in self.control_logic_inputs+["vdd","gnd"]:
self.add_pin(pin)
self.add_pin_list(self.control_logic_inputs,"INPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
def create_layout(self):
""" Layout creation """
@ -221,7 +228,6 @@ class sram(design.design):
self.add_path("metal3",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# connect the horizontal control bus to the vertical bus
def route_four_banks(self):

View File

@ -21,7 +21,6 @@ class lef_test(unittest.TestCase):
OPTS.check_lvsdrc = False
import sram
import lef
debug.info(1, "Testing LEF for sample 2 bit, 16 words SRAM with 1 bank")
s = sram.sram(word_size=2,
@ -36,7 +35,7 @@ class lef_test(unittest.TestCase):
gdsname = OPTS.openram_temp + gdsfile
lefname = OPTS.openram_temp + leffile
s.gds_write(gdsname)
lef.lef(gdsname,lefname,s)
s.lef_write(lefname)
# let's diff the result with a golden model
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),leffile)

View File

@ -21,7 +21,6 @@ class verilog_test(unittest.TestCase):
OPTS.check_lvsdrc = False
import sram
import verilog
debug.info(1, "Testing Verilog for sample 2 bit, 16 words SRAM with 1 bank")
s = sram.sram(word_size=2,
@ -33,7 +32,7 @@ class verilog_test(unittest.TestCase):
vfile = s.name + ".v"
vname = OPTS.openram_temp + vfile
verilog.verilog(vname,s)
s.verilog_write(vname)
# let's diff the result with a golden model

File diff suppressed because it is too large Load Diff

View File

@ -3,25 +3,16 @@ import debug
class verilog:
""" Create a behavioral Verilog file for simulation."""
def __init__(self,verilog_name,sram):
self.sram_name = sram.name
self.num_words = sram.num_words
self.word_size = sram.word_size
self.addr_size = sram.addr_size
def verilog_write(self,verilog_name):
""" Write a behavioral Verilog model. """
self.vf = open(verilog_name, "w")
self.create()
self.vf.close()
def create(self):
self.vf.write("// OpenRAM SRAM model\n")
self.vf.write("// Words: {0}\n".format(self.num_words))
self.vf.write("// Word size: {0}\n\n".format(self.word_size))
self.vf.write("module {0}(DATA,ADDR,CSb,WEb,OEb,clk);\n".format(self.sram_name))
self.vf.write("module {0}(DATA,ADDR,CSb,WEb,OEb,clk);\n".format(self.name))
self.vf.write("\n")
self.vf.write(" parameter DATA_WIDTH = {0} ;\n".format(self.word_size))
self.vf.write(" parameter ADDR_WIDTH = {0} ;\n".format(self.addr_size))
@ -65,47 +56,5 @@ class verilog:
self.vf.write("endmodule\n")
# //SRAM Model
# module sram(CSB,WRB,ABUS,DATABUS);
# input CSB; // active low chip select
# input WRB; // active low write control
# input [11:0] ABUS; // 12-bit address bus
# inout [7:0] DATABUS; // 8-bit data bus
# //** internal signals
# reg [7:0] DATABUS_driver;
# wire [7:0] DATABUS = DATABUS_driver;
# reg [7:0] ram[0:4095]; // memory cells
# integer i;
# initial //initialize all RAM cells to 0 at startup
# begin
# DATABUS_driver = 8'bzzzzzzzz;
# for (i=0; i < 4095; i = i + 1)
# ram[i] = 0;
# end
# always @(CSB or WRB or ABUS)
# begin
# if (CSB == 1'b0)
# begin
# if (WRB == 1'b0) //Start: latch Data on rising edge of CSB or WRB
# begin
# DATABUS_driver <= #10 8'bzzzzzzzz;
# @(posedge CSB or posedge WRB);
# $display($time," Writing %m ABUS=%b DATA=%b",ABUS,DATABUS);
# ram[ABUS] = DATABUS;
# end
# if (WRB == 1'b1) //Reading from sram (data becomes valid after 10ns)
# begin
# #10 DATABUS_driver = ram[ABUS];
# $display($time," Reading %m ABUS=%b DATA=%b",ABUS,DATABUS_driver);
# end
# end
# else //sram unselected, stop driving bus after 10ns
# begin
# DATABUS_driver <= #10 8'bzzzzzzzz;
# end
# end
# endmodule
self.vf.close()

View File

@ -35,8 +35,8 @@ class write_driver_array(design.design):
for i in range(self.word_size):
self.add_pin("data[{0}]".format(i))
for i in range(self.word_size):
self.add_pin("bl_out[{0}]".format(i))
self.add_pin("br_out[{0}]".format(i))
self.add_pin("bl[{0}]".format(i))
self.add_pin("br[{0}]".format(i))
self.add_pin("en")
self.add_pin("vdd")
self.add_pin("gnd")
@ -44,7 +44,6 @@ class write_driver_array(design.design):
def create_layout(self):
self.create_write_array()
self.add_layout_pins()
#self.offset_all_coordinates()
def create_write_array(self):
self.driver_insts = {}
@ -57,8 +56,8 @@ class write_driver_array(design.design):
offset=base)
self.connect_inst(["data[{0}]".format(i/self.words_per_row),
"bl_out[{0}]".format(i/self.words_per_row),
"br_out[{0}]".format(i/self.words_per_row),
"bl[{0}]".format(i/self.words_per_row),
"br[{0}]".format(i/self.words_per_row),
"en", "vdd", "gnd"])