mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' of https://github.com/VLSIDA/PrivateRAM into dev
This commit is contained in:
commit
812cf11e95
|
|
@ -205,7 +205,8 @@ class design(hierarchy_design):
|
|||
print("poly_to_active", self.poly_to_active)
|
||||
print("poly_extend_active", self.poly_extend_active)
|
||||
print("poly_to_contact", self.poly_to_contact)
|
||||
print("contact_to_gate", self.contact_to_gate)
|
||||
print("active_contact_to_gate", self.active_contact_to_gate)
|
||||
print("poly_contact_to_gate", self.poly_contact_to_gate)
|
||||
print("well_enclose_active", self.well_enclose_active)
|
||||
print("implant_enclose_active", self.implant_enclose_active)
|
||||
print("implant_space", self.implant_space)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import verify
|
|||
import debug
|
||||
import os
|
||||
from globals import OPTS
|
||||
|
||||
import tech
|
||||
|
||||
class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||
"""
|
||||
|
|
@ -26,9 +26,12 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
|
||||
# If we have a separate lvs directory, then all the lvs files
|
||||
# should be in there (all or nothing!)
|
||||
lvs_dir = OPTS.openram_tech + "lvs_lib/"
|
||||
# Calibre will do the scaling in s8
|
||||
if os.path.exists(lvs_dir): # and OPTS.lvs_exe[0]!="calibre":
|
||||
try:
|
||||
lvs_subdir = tech.lvs_lib
|
||||
except AttributeError:
|
||||
lvs_subdir = "lvs_lib"
|
||||
lvs_dir = OPTS.openram_tech + lvs_subdir + "/"
|
||||
if os.path.exists(lvs_dir):
|
||||
self.lvs_file = lvs_dir + name + ".sp"
|
||||
else:
|
||||
self.lvs_file = self.sp_file
|
||||
|
|
@ -45,7 +48,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
if i.name == inst.name:
|
||||
break
|
||||
else:
|
||||
debug.error("Couldn't find instance {0}".format(inst_name), -1)
|
||||
debug.error("Couldn't find instance {0}".format(inst.name), -1)
|
||||
inst_map = inst.mod.pin_map
|
||||
return inst_map
|
||||
|
||||
|
|
@ -181,7 +184,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
"""Given a list of nets, will compare the internal alias of a mod to determine
|
||||
if the nets have a connection to this mod's net (but not inst).
|
||||
"""
|
||||
if exclusion_set == None:
|
||||
if not exclusion_set:
|
||||
exclusion_set = set()
|
||||
try:
|
||||
self.name_dict
|
||||
|
|
|
|||
|
|
@ -599,6 +599,10 @@ class layout():
|
|||
"""
|
||||
|
||||
if from_layer == to_layer:
|
||||
# In the case where we have no vias added, make sure that there is at least
|
||||
# a metal enclosure. This helps with center-line path routing.
|
||||
self.add_rect_center(layer=from_layer,
|
||||
offset=offset)
|
||||
return last_via
|
||||
|
||||
from_id = layer_indices[from_layer]
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ import re
|
|||
import os
|
||||
import math
|
||||
import tech
|
||||
from delay_data import *
|
||||
from wire_spice_model import *
|
||||
from power_data import *
|
||||
from delay_data import delay_data
|
||||
from wire_spice_model import wire_spice_model
|
||||
from power_data import power_data
|
||||
import logical_effort
|
||||
|
||||
|
||||
|
|
@ -40,6 +40,8 @@ class spice():
|
|||
# THE CONNECTIONS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the
|
||||
# Spice format)
|
||||
self.conns = []
|
||||
# If this is set, it will out output subckt or isntances of this (for row/col caps etc.)
|
||||
self.no_instances = False
|
||||
# Keep track of any comments to add the the spice
|
||||
try:
|
||||
self.commments
|
||||
|
|
@ -213,7 +215,7 @@ class spice():
|
|||
|
||||
# We don't define self.lvs and will use self.spice if dynamically created
|
||||
# or they are the same file
|
||||
if self.lvs_file!=self.sp_file and os.path.isfile(self.lvs_file):
|
||||
if self.lvs_file != self.sp_file and os.path.isfile(self.lvs_file):
|
||||
debug.info(3, "opening {0}".format(self.lvs_file))
|
||||
f = open(self.lvs_file)
|
||||
self.lvs = f.readlines()
|
||||
|
|
@ -263,7 +265,12 @@ class spice():
|
|||
Recursive spice subcircuit write;
|
||||
Writes the spice subcircuit from the library or the dynamically generated one
|
||||
"""
|
||||
if not self.spice:
|
||||
|
||||
if self.no_instances:
|
||||
return
|
||||
elif not self.spice:
|
||||
# If spice isn't defined, we dynamically generate one.
|
||||
|
||||
# recursively write the modules
|
||||
for i in self.mods:
|
||||
if self.contains(i, usedMODS):
|
||||
|
|
@ -300,6 +307,9 @@ class spice():
|
|||
# these are wires and paths
|
||||
if self.conns[i] == []:
|
||||
continue
|
||||
# Instance with no devices in it needs no subckt/instance
|
||||
if self.insts[i].mod.no_instances:
|
||||
continue
|
||||
if lvs_netlist and hasattr(self.insts[i].mod, "lvs_device"):
|
||||
sp.write(self.insts[i].mod.lvs_device.format(self.insts[i].name,
|
||||
" ".join(self.conns[i])))
|
||||
|
|
@ -316,7 +326,7 @@ class spice():
|
|||
sp.write(".ENDS {0}\n".format(self.name))
|
||||
|
||||
else:
|
||||
# write the subcircuit itself
|
||||
# If spice is a hard module, output the spice file contents.
|
||||
# Including the file path makes the unit test fail for other users.
|
||||
# if os.path.isfile(self.sp_file):
|
||||
# sp.write("\n* {0}\n".format(self.sp_file))
|
||||
|
|
@ -356,7 +366,7 @@ class spice():
|
|||
stage_effort = self.get_stage_effort(relative_cap)
|
||||
|
||||
# If it fails, then keep running with a valid object.
|
||||
if stage_effort == None:
|
||||
if not stage_effort:
|
||||
return delay_data(0.0, 0.0)
|
||||
|
||||
abs_delay = stage_effort.get_absolute_delay()
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import utils
|
|||
from tech import GDS, layer
|
||||
from tech import cell_properties as props
|
||||
import bitcell_base
|
||||
|
||||
from globals import OPTS
|
||||
|
||||
class bitcell(bitcell_base.bitcell_base):
|
||||
"""
|
||||
|
|
@ -50,6 +50,8 @@ class bitcell(bitcell_base.bitcell_base):
|
|||
self.pin_map = bitcell.pin_map
|
||||
self.add_pin_types(self.type_list)
|
||||
self.nets_match = self.do_nets_exist(self.storage_nets)
|
||||
|
||||
debug.check(OPTS.tech_name != "sky130", "sky130 does not yet support single port cells")
|
||||
|
||||
def get_all_wl_names(self):
|
||||
""" Creates a list of all wordline pin names """
|
||||
|
|
|
|||
|
|
@ -41,3 +41,4 @@ class col_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
|||
self.height = col_cap_bitcell_1rw_1r.height
|
||||
self.pin_map = col_cap_bitcell_1rw_1r.pin_map
|
||||
self.add_pin_types(self.type_list)
|
||||
self.no_instances = True
|
||||
|
|
|
|||
|
|
@ -41,3 +41,4 @@ class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
|||
self.height = row_cap_bitcell_1rw_1r.height
|
||||
self.pin_map = row_cap_bitcell_1rw_1r.pin_map
|
||||
self.add_pin_types(self.type_list)
|
||||
self.no_instances = True
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ class col_cap_array(bitcell_base_array):
|
|||
super().__init__(cols, rows, name, column_offset)
|
||||
self.mirror = mirror
|
||||
|
||||
self.no_instances = True
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
|
@ -47,8 +48,8 @@ class col_cap_array(bitcell_base_array):
|
|||
for col in range(self.column_size):
|
||||
for row in range(self.row_size):
|
||||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
self.cell_inst[row,col]=self.add_inst(name=name,
|
||||
mod=self.dummy_cell)
|
||||
self.cell_inst[row, col]=self.add_inst(name=name,
|
||||
mod=self.dummy_cell)
|
||||
self.connect_inst(self.get_bitcell_pins(col, row))
|
||||
|
||||
def get_bitcell_pins(self, col, row):
|
||||
|
|
@ -73,31 +74,20 @@ class col_cap_array(bitcell_base_array):
|
|||
|
||||
for col in range(self.column_size):
|
||||
for cell_column in column_list:
|
||||
bl_pin = self.cell_inst[0,col].get_pin(cell_column)
|
||||
self.add_layout_pin(text=cell_column+"_{0}".format(col),
|
||||
bl_pin = self.cell_inst[0, col].get_pin(cell_column)
|
||||
self.add_layout_pin(text=cell_column + "_{0}".format(col),
|
||||
layer=bl_pin.layer,
|
||||
offset=bl_pin.ll().scale(1,0),
|
||||
offset=bl_pin.ll().scale(1, 0),
|
||||
width=bl_pin.width(),
|
||||
height=self.height)
|
||||
|
||||
# Add vdd/gnd via stacks
|
||||
for row in range(self.row_size):
|
||||
for col in range(self.column_size):
|
||||
inst = self.cell_inst[row,col]
|
||||
inst = self.cell_inst[row, col]
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
for pin in inst.get_pins(pin_name):
|
||||
self.add_power_pin(name=pin.name,
|
||||
loc=pin.center(),
|
||||
start_layer=pin.layer)
|
||||
|
||||
|
||||
# def input_load(self):
|
||||
# wl_wire = self.gen_wl_wire()
|
||||
# return wl_wire.return_input_cap()
|
||||
#
|
||||
# def get_wordline_cin(self):
|
||||
# """Get the relative input capacitance from the wordline connections in all the bitcell"""
|
||||
# #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
||||
# bitcell_wl_cin = self.cell.get_wl_cin()
|
||||
# total_cin = bitcell_wl_cin * self.column_size
|
||||
# return total_cin
|
||||
|
|
|
|||
|
|
@ -178,10 +178,14 @@ class delay_chain(design.design):
|
|||
load_list = self.load_inst_map[inst]
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
pin = load_list[0].get_pin(pin_name)
|
||||
self.add_power_pin(pin_name, pin.rc() - vector(self.m1_pitch, 0))
|
||||
self.add_power_pin(pin_name,
|
||||
pin.rc() - vector(self.m1_pitch, 0),
|
||||
start_layer=pin.layer)
|
||||
|
||||
pin = load_list[-1].get_pin(pin_name)
|
||||
self.add_power_pin(pin_name, pin.rc() - vector(0.5 * self.m1_pitch, 0))
|
||||
pin = load_list[-2].get_pin(pin_name)
|
||||
self.add_power_pin(pin_name,
|
||||
pin.rc() - vector(self.m1_pitch, 0),
|
||||
start_layer=pin.layer)
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ class hierarchical_decoder(design.design):
|
|||
self.predecoder_width = self.pre2_4.width
|
||||
|
||||
# How much space between each predecoder
|
||||
self.predecoder_spacing = self.and2.height
|
||||
self.predecoder_spacing = 2 * self.and2.height
|
||||
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 \
|
||||
+ (self.no_of_pre2x4 + self.no_of_pre3x8 - 1) * self.predecoder_spacing
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from sram_factory import factory
|
|||
from globals import OPTS
|
||||
from tech import cell_properties
|
||||
|
||||
|
||||
class row_cap_array(bitcell_base_array):
|
||||
"""
|
||||
Generate a dummy row/column for the replica array.
|
||||
|
|
@ -15,7 +16,7 @@ class row_cap_array(bitcell_base_array):
|
|||
def __init__(self, cols, rows, column_offset=0, mirror=0, name=""):
|
||||
super().__init__(cols, rows, name, column_offset)
|
||||
self.mirror = mirror
|
||||
|
||||
self.no_instances = True
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
|
@ -46,8 +47,8 @@ class row_cap_array(bitcell_base_array):
|
|||
for col in range(self.column_size):
|
||||
for row in range(1, self.row_size - 1):
|
||||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
self.cell_inst[row,col]=self.add_inst(name=name,
|
||||
mod=self.dummy_cell)
|
||||
self.cell_inst[row, col]=self.add_inst(name=name,
|
||||
mod=self.dummy_cell)
|
||||
self.connect_inst(self.get_bitcell_pins(col, row))
|
||||
|
||||
def get_bitcell_pins(self, col, row):
|
||||
|
|
@ -65,8 +66,8 @@ class row_cap_array(bitcell_base_array):
|
|||
|
||||
def place_array(self, name_template, row_offset=0):
|
||||
# We increase it by a well enclosure so the precharges don't overlap our wells
|
||||
self.height = self.row_size*self.cell.height
|
||||
self.width = self.column_size*self.cell.width
|
||||
self.height = self.row_size * self.cell.height
|
||||
self.width = self.column_size * self.cell.width
|
||||
|
||||
xoffset = 0.0
|
||||
for col in range(self.column_size):
|
||||
|
|
@ -74,7 +75,6 @@ class row_cap_array(bitcell_base_array):
|
|||
tempx, dir_y = self._adjust_x_offset(xoffset, col, self.column_offset)
|
||||
|
||||
for row in range(1, self.row_size - 1):
|
||||
name = name_template.format(row, col)
|
||||
tempy, dir_x = self._adjust_y_offset(yoffset, row, row_offset)
|
||||
|
||||
if dir_x and dir_y:
|
||||
|
|
@ -86,8 +86,8 @@ class row_cap_array(bitcell_base_array):
|
|||
else:
|
||||
dir_key = ""
|
||||
|
||||
self.cell_inst[row,col].place(offset=[tempx, tempy],
|
||||
mirror=dir_key)
|
||||
self.cell_inst[row, col].place(offset=[tempx, tempy],
|
||||
mirror=dir_key)
|
||||
yoffset += self.cell.height
|
||||
xoffset += self.cell.width
|
||||
|
||||
|
|
@ -98,31 +98,20 @@ class row_cap_array(bitcell_base_array):
|
|||
|
||||
for row in range(1, self.row_size - 1):
|
||||
for cell_row in row_list:
|
||||
wl_pin = self.cell_inst[row,0].get_pin(cell_row)
|
||||
self.add_layout_pin(text=cell_row+"_{0}".format(row),
|
||||
wl_pin = self.cell_inst[row, 0].get_pin(cell_row)
|
||||
self.add_layout_pin(text=cell_row + "_{0}".format(row),
|
||||
layer=wl_pin.layer,
|
||||
offset=wl_pin.ll().scale(0,1),
|
||||
offset=wl_pin.ll().scale(0, 1),
|
||||
width=self.width,
|
||||
height=wl_pin.height())
|
||||
|
||||
# Add vdd/gnd via stacks
|
||||
for row in range(1, self.row_size - 1):
|
||||
for col in range(self.column_size):
|
||||
inst = self.cell_inst[row,col]
|
||||
inst = self.cell_inst[row, col]
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
for pin in inst.get_pins(pin_name):
|
||||
self.add_power_pin(name=pin.name,
|
||||
loc=pin.center(),
|
||||
start_layer=pin.layer)
|
||||
|
||||
|
||||
# def input_load(self):
|
||||
# wl_wire = self.gen_wl_wire()
|
||||
# return wl_wire.return_input_cap()
|
||||
#
|
||||
# def get_wordline_cin(self):
|
||||
# """Get the relative input capacitance from the wordline connections in all the bitcell"""
|
||||
# #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
||||
# bitcell_wl_cin = self.cell.get_wl_cin()
|
||||
# total_cin = bitcell_wl_cin * self.column_size
|
||||
# return total_cin
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import debug
|
|||
from sram_factory import factory
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
|
||||
from tech import layer
|
||||
|
||||
class write_mask_and_array(design.design):
|
||||
"""
|
||||
|
|
@ -93,7 +93,7 @@ class write_mask_and_array(design.design):
|
|||
|
||||
self.width = self.bitcell.width * self.columns
|
||||
self.height = self.and2.height
|
||||
|
||||
|
||||
for i in range(self.num_wmasks):
|
||||
base = vector(i * self.wmask_en_len, 0)
|
||||
self.and2_insts[i].place(base)
|
||||
|
|
@ -121,16 +121,17 @@ class write_mask_and_array(design.design):
|
|||
|
||||
for supply in ["gnd", "vdd"]:
|
||||
supply_pin=self.and2_insts[i].get_pin(supply)
|
||||
self.add_power_pin(supply, supply_pin.center())
|
||||
if "li" in layer:
|
||||
self.add_power_pin(supply, supply_pin.center(), start_layer="li", directions = ("H", "H"))
|
||||
else:
|
||||
self.add_power_pin(supply, supply_pin.center())
|
||||
|
||||
for supply in ["gnd", "vdd"]:
|
||||
supply_pin_left = self.and2_insts[0].get_pin(supply)
|
||||
supply_pin_right = self.and2_insts[self.num_wmasks - 1].get_pin(supply)
|
||||
self.add_path(supply_pin_left.layer, [supply_pin_left.lc(), supply_pin_right.rc()])
|
||||
|
||||
|
||||
def get_cin(self):
|
||||
"""Get the relative capacitance of all the input connections in the bank"""
|
||||
# The enable is connected to an and2 for every row.
|
||||
return self.and2.get_cin() * len(self.and2_insts)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ class pgate(design.design):
|
|||
self.route_layer_space = getattr(self, "{}_space".format(self.route_layer))
|
||||
self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer))
|
||||
|
||||
# hack for enclosing input pin with npc
|
||||
self.input_pin_vias = []
|
||||
|
||||
# This is the space from a S/D contact to the supply rail
|
||||
contact_to_vdd_rail_space = 0.5 * self.route_layer_width + self.route_layer_space
|
||||
# This is a poly-to-poly of a flipped cell
|
||||
|
|
@ -132,6 +135,8 @@ class pgate(design.design):
|
|||
offset=contact_offset,
|
||||
directions=directions)
|
||||
|
||||
self.input_pin_vias.append(via)
|
||||
|
||||
self.add_layout_pin_rect_center(text=name,
|
||||
layer=self.route_layer,
|
||||
offset=contact_offset,
|
||||
|
|
@ -146,11 +151,33 @@ class pgate(design.design):
|
|||
height=contact.poly_contact.first_layer_width,
|
||||
width=left_gate_offset.x - contact_offset.x)
|
||||
|
||||
def enclose_npc(self):
|
||||
""" Enclose the poly contacts with npc layer """
|
||||
ll = None
|
||||
ur = None
|
||||
for via in self.input_pin_vias:
|
||||
# Find ll/ur
|
||||
if not ll:
|
||||
ll = via.ll()
|
||||
else:
|
||||
ll = ll.min(via.ll())
|
||||
if not ur:
|
||||
ur = via.ur()
|
||||
else:
|
||||
ur = ur.max(via.ur())
|
||||
|
||||
npc_enclose_poly = drc("npc_enclose_poly")
|
||||
npc_enclose_offset = vector(npc_enclose_poly, npc_enclose_poly)
|
||||
self.add_rect(layer="npc",
|
||||
offset=ll - npc_enclose_offset,
|
||||
width=(ur.x - ll.x) + 2 * npc_enclose_poly,
|
||||
height=(ur.y - ll.y) + 2 * npc_enclose_poly)
|
||||
|
||||
def extend_wells(self):
|
||||
""" Extend the n/p wells to cover whole cell """
|
||||
|
||||
# This should match the cells in the cell library
|
||||
self.nwell_y_offset = 0.48 * self.height
|
||||
self.nwell_yoffset = 0.48 * self.height
|
||||
full_height = self.height + 0.5 * self.m1_width
|
||||
|
||||
# FIXME: float rounding problem
|
||||
|
|
@ -158,8 +185,8 @@ class pgate(design.design):
|
|||
# Add a rail width to extend the well to the top of the rail
|
||||
nwell_max_offset = max(self.find_highest_layer_coords("nwell").y,
|
||||
full_height)
|
||||
nwell_position = vector(0, self.nwell_y_offset) - vector(self.well_extend_active, 0)
|
||||
nwell_height = nwell_max_offset - self.nwell_y_offset
|
||||
nwell_position = vector(0, self.nwell_yoffset) - vector(self.well_extend_active, 0)
|
||||
nwell_height = nwell_max_offset - self.nwell_yoffset
|
||||
self.add_rect(layer="nwell",
|
||||
offset=nwell_position,
|
||||
width=self.width + 2 * self.well_extend_active,
|
||||
|
|
@ -175,7 +202,7 @@ class pgate(design.design):
|
|||
pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y,
|
||||
-0.5 * self.m1_width)
|
||||
pwell_position = vector(-self.well_extend_active, pwell_min_offset)
|
||||
pwell_height = self.nwell_y_offset - pwell_position.y
|
||||
pwell_height = self.nwell_yoffset - pwell_position.y
|
||||
self.add_rect(layer="pwell",
|
||||
offset=pwell_position,
|
||||
width=self.width + 2 * self.well_extend_active,
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ class pnand2(pgate.pgate):
|
|||
# doesn't use nmos uy because that is calculated using offset + poly height
|
||||
active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height
|
||||
active_to_poly_contact = active_top + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height
|
||||
active_to_poly_contact2 = active_top + drc("contact_to_gate") + 0.5 * self.route_layer_width
|
||||
active_to_poly_contact2 = active_top + self.poly_contact_to_gate + 0.5 * self.route_layer_width
|
||||
self.inputA_yoffset = max(active_contact_to_poly_contact,
|
||||
active_to_poly_contact,
|
||||
active_to_poly_contact2)
|
||||
|
|
@ -200,7 +200,7 @@ class pnand2(pgate.pgate):
|
|||
# active_contact_to_poly_contact = self.output_yoffset - self.route_layer_space - 0.5 * contact.poly_contact.second_layer_height
|
||||
# active_bottom = self.pmos1_inst.by()
|
||||
# active_to_poly_contact = active_bottom - self.poly_to_active - 0.5 * contact.poly_contact.first_layer_height
|
||||
# active_to_poly_contact2 = active_bottom - drc("contact_to_gate") - 0.5 * self.route_layer_width
|
||||
# active_to_poly_contact2 = active_bottom - self.poly_contact_to_gate - 0.5 * self.route_layer_width
|
||||
# self.inputB_yoffset = min(active_contact_to_poly_contact,
|
||||
# active_to_poly_contact,
|
||||
# active_to_poly_contact2)
|
||||
|
|
@ -212,6 +212,10 @@ class pnand2(pgate.pgate):
|
|||
"B",
|
||||
position="center")
|
||||
|
||||
if OPTS.tech_name == "sky130":
|
||||
self.enclose_npc()
|
||||
|
||||
|
||||
def route_output(self):
|
||||
""" Route the Z output """
|
||||
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ class pnand3(pgate.pgate):
|
|||
# doesn't use nmos uy because that is calculated using offset + poly height
|
||||
active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height
|
||||
active_to_poly_contact = active_top + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height
|
||||
active_to_poly_contact2 = active_top + drc("contact_to_gate") + 0.5 * self.route_layer_width
|
||||
active_to_poly_contact2 = active_top + self.poly_contact_to_gate + 0.5 * self.route_layer_width
|
||||
self.inputA_yoffset = max(active_contact_to_poly_contact,
|
||||
active_to_poly_contact,
|
||||
active_to_poly_contact2)
|
||||
|
|
@ -233,20 +233,22 @@ class pnand3(pgate.pgate):
|
|||
"A",
|
||||
position="left")
|
||||
|
||||
# Put B right on the well line
|
||||
self.inputB_yoffset = self.inputA_yoffset + non_contact_pitch
|
||||
self.inputB_yoffset = self.inputA_yoffset + self.m3_pitch
|
||||
self.route_input_gate(self.pmos2_inst,
|
||||
self.nmos2_inst,
|
||||
self.inputB_yoffset,
|
||||
"B",
|
||||
position="center")
|
||||
|
||||
self.inputC_yoffset = self.inputB_yoffset + non_contact_pitch
|
||||
self.inputC_yoffset = self.inputB_yoffset + self.m3_pitch
|
||||
self.route_input_gate(self.pmos3_inst,
|
||||
self.nmos3_inst,
|
||||
self.inputC_yoffset,
|
||||
"C",
|
||||
position="right")
|
||||
|
||||
if OPTS.tech_name == "sky130":
|
||||
self.enclose_npc()
|
||||
|
||||
def route_output(self):
|
||||
""" Route the Z output """
|
||||
|
|
|
|||
|
|
@ -211,6 +211,9 @@ class pnor2(pgate.pgate):
|
|||
|
||||
self.output_yoffset = self.inputA_yoffset + self.m1_nonpref_pitch
|
||||
|
||||
if OPTS.tech_name == "sky130":
|
||||
self.enclose_npc()
|
||||
|
||||
def route_output(self):
|
||||
""" Route the Z output """
|
||||
# PMOS2 (right) drain
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ class precharge(design.design):
|
|||
pin_offset = self.lower_pmos_inst.get_pin("G").lr()
|
||||
# This is an extra space down for some techs with contact to active spacing
|
||||
contact_space = max(self.poly_space,
|
||||
self.contact_to_gate) + 0.5 * contact.poly_contact.first_layer_height
|
||||
self.poly_contact_to_gate) + 0.5 * contact.poly_contact.first_layer_height
|
||||
offset = pin_offset - vector(0, contact_space)
|
||||
self.add_via_stack_center(from_layer="poly",
|
||||
to_layer=self.en_layer,
|
||||
|
|
|
|||
|
|
@ -131,8 +131,8 @@ class ptx(design.design):
|
|||
# be decided in the layout later.
|
||||
area_sd = 2.5 * self.poly_width * self.tx_width
|
||||
perimeter_sd = 2 * self.poly_width + 2 * self.tx_width
|
||||
if OPTS.tech_name == "sky130":
|
||||
# sky130 technology is in microns, also needs mult parameter
|
||||
if OPTS.tech_name == "sky130" and OPTS.lvs_exe[0] == "calibre":
|
||||
# sky130 simulation cannot use the mult parameter in simulation
|
||||
(self.tx_width, self.mults) = pgate.bin_width(self.tx_type, self.tx_width)
|
||||
main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type],
|
||||
self.mults,
|
||||
|
|
@ -152,19 +152,17 @@ class ptx(design.design):
|
|||
self.spice_device = main_str + area_str
|
||||
self.spice.append("\n* ptx " + self.spice_device)
|
||||
|
||||
# LVS lib is always in SI units
|
||||
if os.path.exists(OPTS.openram_tech + "lvs_lib"):
|
||||
if OPTS.tech_name == "sky130":
|
||||
# sky130 requires mult parameter too
|
||||
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult={1}".format(spice[self.tx_type],
|
||||
self.mults,
|
||||
self.tx_width,
|
||||
drc("minwidth_poly"))
|
||||
else:
|
||||
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
|
||||
self.mults,
|
||||
self.tx_width,
|
||||
drc("minwidth_poly"))
|
||||
if OPTS.tech_name == "sky130" and OPTS.lvs_exe[0] == "calibre":
|
||||
# sky130 requires mult parameter too
|
||||
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult={1}".format(spice[self.tx_type],
|
||||
self.mults,
|
||||
self.tx_width,
|
||||
drc("minwidth_poly"))
|
||||
else:
|
||||
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
|
||||
self.mults,
|
||||
self.tx_width,
|
||||
drc("minwidth_poly"))
|
||||
|
||||
def setup_layout_constants(self):
|
||||
"""
|
||||
|
|
@ -198,7 +196,7 @@ class ptx(design.design):
|
|||
# This is the spacing between the poly gates
|
||||
self.min_poly_pitch = self.poly_space + self.poly_width
|
||||
self.contacted_poly_pitch = self.poly_space + contact.poly_contact.width
|
||||
self.contact_pitch = 2 * self.contact_to_gate + self.poly_width + self.contact_width
|
||||
self.contact_pitch = 2 * self.active_contact_to_gate + self.poly_width + self.contact_width
|
||||
self.poly_pitch = max(self.min_poly_pitch,
|
||||
self.contacted_poly_pitch,
|
||||
self.contact_pitch)
|
||||
|
|
@ -208,7 +206,7 @@ class ptx(design.design):
|
|||
# Active width is determined by enclosure on both ends and contacted pitch,
|
||||
# at least one poly and n-1 poly pitches
|
||||
self.active_width = 2 * self.end_to_contact + self.active_contact.width \
|
||||
+ 2 * self.contact_to_gate + self.poly_width + (self.mults - 1) * self.poly_pitch
|
||||
+ 2 * self.active_contact_to_gate + self.poly_width + (self.mults - 1) * self.poly_pitch
|
||||
|
||||
# Active height is just the transistor width
|
||||
self.active_height = self.tx_width
|
||||
|
|
@ -323,7 +321,7 @@ class ptx(design.design):
|
|||
"""
|
||||
# poly is one contacted spacing from the end and down an extension
|
||||
poly_offset = self.contact_offset \
|
||||
+ vector(0.5 * self.active_contact.width + 0.5 * self.poly_width + self.contact_to_gate, 0)
|
||||
+ vector(0.5 * self.active_contact.width + 0.5 * self.poly_width + self.active_contact_to_gate, 0)
|
||||
|
||||
# poly_positions are the bottom center of the poly gates
|
||||
self.poly_positions = []
|
||||
|
|
|
|||
|
|
@ -23,10 +23,13 @@ class and2_dec_test(openram_test):
|
|||
global verify
|
||||
import verify
|
||||
|
||||
import and2_dec
|
||||
|
||||
debug.info(2, "Testing and2 gate 4x")
|
||||
a = and2_dec.and2_dec(name="and2x4", size=4)
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
globals.setup_bitcell()
|
||||
|
||||
debug.info(2, "Testing and2_dec gate")
|
||||
a = factory.create(module_type="and2_dec")
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
|
|
|||
|
|
@ -23,10 +23,13 @@ class and3_dec_test(openram_test):
|
|||
global verify
|
||||
import verify
|
||||
|
||||
import and3_dec
|
||||
|
||||
debug.info(2, "Testing and3 gate 4x")
|
||||
a = and3_dec.and3_dec(name="and3x4", size=4)
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
globals.setup_bitcell()
|
||||
|
||||
debug.info(2, "Testing and3_dec gate")
|
||||
a = factory.create(module_type="and3_dec")
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ from globals import OPTS
|
|||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
class and3_dec_test(openram_test):
|
||||
|
||||
@unittest.skip("SKIPPING 04_and4_dec_test")
|
||||
class and4_dec_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
|
|
@ -23,10 +25,13 @@ class and3_dec_test(openram_test):
|
|||
global verify
|
||||
import verify
|
||||
|
||||
import and3_dec
|
||||
|
||||
debug.info(2, "Testing and3 gate 4x")
|
||||
a = and3_dec.and3_dec(name="and3x4", size=4)
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
globals.setup_bitcell()
|
||||
|
||||
debug.info(2, "Testing and4_dec gate")
|
||||
a = factory.create(module_type="and4_dec")
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env python3
|
||||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import unittest
|
||||
from testutils import *
|
||||
import sys,os
|
||||
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
class write_driver_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
globals.setup_bitcell()
|
||||
|
||||
debug.info(2, "Testing write_driver_array for columns=8, word_size=8")
|
||||
a = factory.create(module_type="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")
|
||||
a = factory.create(module_type="write_driver_array", columns=16, word_size=8)
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
# run the test from the command line
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env python3
|
||||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import unittest
|
||||
from testutils import *
|
||||
import sys, os
|
||||
|
||||
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
|
||||
class write_mask_and_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
globals.setup_bitcell()
|
||||
|
||||
debug.info(2, "Testing write_mask_and_array for columns=8, word_size=8, write_size=4")
|
||||
a = factory.create(module_type="write_mask_and_array", columns=8, word_size=8, write_size=4)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(2, "Testing write_mask_and_array for columns=16, word_size=16, write_size=4")
|
||||
a = factory.create(module_type="write_mask_and_array", columns=16, word_size=16, write_size=4)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(2, "Testing write_mask_and_array for columns=16, word_size=8, write_size=2")
|
||||
a = factory.create(module_type="write_mask_and_array", columns=16, word_size=8, write_size=2)
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
||||
# run the test from the command line
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
@ -30,8 +30,8 @@ class port_address_1rw_1r_test(openram_test):
|
|||
a = factory.create("port_address", cols=16, rows=16)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Port address 512 rows")
|
||||
a = factory.create("port_address", cols=256, rows=512)
|
||||
debug.info(1, "Port address 256 rows")
|
||||
a = factory.create("port_address", cols=256, rows=256)
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@ class port_data_wmask_1rw_1r_test(openram_test):
|
|||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
globals.setup_bitcell()
|
||||
|
||||
c = sram_config(word_size=16,
|
||||
write_size=4,
|
||||
num_words=16)
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False):
|
|||
num_drc_runs += 1
|
||||
|
||||
# Filter the layouts through magic as a GDS filter for nsdm/psdm/nwell merging
|
||||
if OPTS.tech_name == "sky130":
|
||||
if OPTS.tech_name == "sky130" and False:
|
||||
shutil.copy(gds_name, OPTS.openram_temp + "temp.gds")
|
||||
from magic import filter_gds
|
||||
filter_gds(cell_name, OPTS.openram_temp + "temp.gds", OPTS.openram_temp + cell_name + ".gds")
|
||||
|
|
|
|||
|
|
@ -234,9 +234,9 @@ drc.add_enclosure("active",
|
|||
enclosure = 0.005)
|
||||
|
||||
# CONTACT.6 Minimum spacing of contact and gate
|
||||
drc["contact_to_gate"] = 0.0375 #changed from 0.035
|
||||
drc["active_contact_to_gate"] = 0.0375 #changed from 0.035
|
||||
# CONTACT.7 Minimum spacing of contact and poly
|
||||
drc["contact_to_poly"] = 0.090
|
||||
drc["poly_contact_to_gate"] = 0.090
|
||||
|
||||
# CONTACT.1 Minimum width of contact
|
||||
# CONTACT.2 Minimum spacing of contact
|
||||
|
|
|
|||
|
|
@ -217,9 +217,9 @@ drc.add_enclosure("active",
|
|||
layer = "contact",
|
||||
enclosure = _lambda_)
|
||||
# Reserved for other technologies
|
||||
drc["contact_to_gate"] = 2*_lambda_
|
||||
drc["active_contact_to_gate"] = 2*_lambda_
|
||||
# 5.4 Minimum spacing to gate of transistor
|
||||
drc["contact_to_poly"] = 2*_lambda_
|
||||
drc["poly_contact_to_gate"] = 2*_lambda_
|
||||
|
||||
# 6.1 Exact contact size
|
||||
# 5.3 Minimum contact spacing
|
||||
|
|
|
|||
Loading…
Reference in New Issue