mirror of https://github.com/VLSIDA/OpenRAM.git
Revised LEF and Verilog generation. Does not read GDS for speed improvements.
This commit is contained in:
parent
13902538ff
commit
9a4b2b4341
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
word_size = 2
|
||||
num_words = 16
|
||||
num_words = 128
|
||||
num_banks = 1
|
||||
|
||||
tech_name = "freepdk45"
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
322
compiler/lef.py
322
compiler/lef.py
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 """
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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"])
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue