mirror of https://github.com/VLSIDA/OpenRAM.git
202 lines
6.8 KiB
Python
202 lines
6.8 KiB
Python
|
|
# See LICENSE for licensing information.
|
||
|
|
#
|
||
|
|
# Copyright (c) 2016-2021 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 math
|
||
|
|
from base import geometry
|
||
|
|
from base import design
|
||
|
|
from sram_factory import factory
|
||
|
|
from base import vector
|
||
|
|
from tech import layer, drc
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
class rom_precharge_array(design):
|
||
|
|
"""
|
||
|
|
An array of inverters to create the inverted address lines for the rom decoder
|
||
|
|
"""
|
||
|
|
def __init__(self, cols, pmos_size=None, name="", route_layer="li", strap_spacing=None):
|
||
|
|
self.cols = cols
|
||
|
|
self.route_layer = route_layer
|
||
|
|
if name=="":
|
||
|
|
name = "rom_inv_array_{0}".format(cols)
|
||
|
|
# if pmos_size == None:
|
||
|
|
# self.pmos_size = dff.height * 0.5
|
||
|
|
# else:
|
||
|
|
# self.pmos_size = inv_size
|
||
|
|
if strap_spacing != None:
|
||
|
|
self.strap_spacing = strap_spacing
|
||
|
|
else:
|
||
|
|
self.strap_spacing = 0
|
||
|
|
|
||
|
|
if "li" in layer:
|
||
|
|
self.inv_layer = "li"
|
||
|
|
else:
|
||
|
|
self.inv_layer = "m1"
|
||
|
|
|
||
|
|
|
||
|
|
if strap_spacing != 0:
|
||
|
|
self.num_straps = math.ceil(self.cols / self.strap_spacing)
|
||
|
|
self.array_col_size = self.cols + self.num_straps
|
||
|
|
else:
|
||
|
|
self.num_straps = 0
|
||
|
|
self.array_col_size = self.cols
|
||
|
|
|
||
|
|
super().__init__(name)
|
||
|
|
self.create_netlist()
|
||
|
|
self.create_layout()
|
||
|
|
|
||
|
|
def create_netlist(self):
|
||
|
|
self.create_modules()
|
||
|
|
self.add_pins()
|
||
|
|
self.create_instances()
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
def create_layout(self):
|
||
|
|
self.width = self.cols * self.pmos.width + self.num_straps * self.poly_tap.width
|
||
|
|
self.height = self.pmos.width
|
||
|
|
self.place_instances()
|
||
|
|
self.create_layout_pins()
|
||
|
|
self.add_well_tap()
|
||
|
|
self.route_supply()
|
||
|
|
self.extend_implant()
|
||
|
|
|
||
|
|
self.add_boundary()
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
def add_boundary(self):
|
||
|
|
self.translate_all(self.well_ll)
|
||
|
|
ur = self.find_highest_coords()
|
||
|
|
ur = vector(ur.x, ur.y - self.well_ll.y)
|
||
|
|
super().add_boundary(vector(0, 0), ur)
|
||
|
|
self.width = self.cols * self.pmos.width + self.num_straps * self.poly_tap.width
|
||
|
|
self.height = ur.y
|
||
|
|
|
||
|
|
def create_modules(self):
|
||
|
|
|
||
|
|
self.pmos = factory.create(module_type="rom_precharge_cell", module_name="precharge_cell", route_layer=self.route_layer)
|
||
|
|
|
||
|
|
# For layout constants
|
||
|
|
self.dummy = factory.create(module_type="rom_base_cell")
|
||
|
|
self.poly_tap = factory.create(module_type="rom_poly_tap", strap_length=self.strap_spacing, tx_type="pmos")
|
||
|
|
|
||
|
|
def add_pins(self):
|
||
|
|
for col in range(self.cols):
|
||
|
|
self.add_pin("pre_bl{0}_out".format(col), "OUTPUT")
|
||
|
|
self.add_pin("vdd", "POWER")
|
||
|
|
self.add_pin("gate", "INPUT")
|
||
|
|
|
||
|
|
def create_instances(self):
|
||
|
|
self.array_insts = []
|
||
|
|
self.pmos_insts = []
|
||
|
|
self.tap_insts = []
|
||
|
|
for col in range(self.cols):
|
||
|
|
|
||
|
|
|
||
|
|
if col % self.strap_spacing == 0:
|
||
|
|
name = "tap_c{}".format(col)
|
||
|
|
tap = self.add_inst(name=name, mod=self.poly_tap)
|
||
|
|
self.array_insts.append(tap)
|
||
|
|
self.tap_insts.append(tap)
|
||
|
|
self.connect_inst([])
|
||
|
|
name = "Xpmos_c{0}".format(col)
|
||
|
|
pmos = self.add_inst(name=name, mod=self.pmos)
|
||
|
|
self.array_insts.append(pmos)
|
||
|
|
self.pmos_insts.append(pmos)
|
||
|
|
bl = "pre_bl{0}_out".format(col)
|
||
|
|
self.connect_inst(["vdd", "gate", bl, "vdd"])
|
||
|
|
print(self.array_insts)
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
def place_instances(self):
|
||
|
|
self.add_label("ZERO", self.route_layer)
|
||
|
|
|
||
|
|
self.array_pos = []
|
||
|
|
strap_num = 0
|
||
|
|
cell_y = 0
|
||
|
|
# columns are bit lines4
|
||
|
|
cell_x = 0
|
||
|
|
print("starting array place")
|
||
|
|
|
||
|
|
for col in range(self.cols):
|
||
|
|
|
||
|
|
if col % self.strap_spacing == 0 :
|
||
|
|
self.tap_insts[strap_num].place(vector(cell_x, cell_y))
|
||
|
|
self.add_label("debug", "li", vector(cell_x, cell_y))
|
||
|
|
cell_x += self.poly_tap.width
|
||
|
|
strap_num += 1
|
||
|
|
|
||
|
|
self.pmos_insts[col].place(vector(cell_x, cell_y))
|
||
|
|
self.add_label("debug", "li", vector(cell_x, cell_y))
|
||
|
|
cell_x += self.pmos.width
|
||
|
|
|
||
|
|
|
||
|
|
def create_layout_pins(self):
|
||
|
|
for col in range(self.cols):
|
||
|
|
source_pin = self.pmos_insts[col].get_pin("D")
|
||
|
|
bl = "pre_bl{0}_out".format(col)
|
||
|
|
self.add_layout_pin_rect_center(bl, self.route_layer, source_pin.center())
|
||
|
|
|
||
|
|
|
||
|
|
def extend_implant(self):
|
||
|
|
layer = "nwell"
|
||
|
|
# center of source contact minus radius of the well generated by the contact gives the lowermost well position
|
||
|
|
contact_well_by = self.pmos.pmos.source_contacts[0].cx() - self.pmos.pmos.source_contacts[0].mod.well_width * 0.5
|
||
|
|
|
||
|
|
ptx_well_by = 0.5 * self.pmos.pmos.active_width - 0.5 * self.pmos.pmos.well_width
|
||
|
|
|
||
|
|
well_extend = ptx_well_by - contact_well_by
|
||
|
|
|
||
|
|
|
||
|
|
self.well_ll = vector(0, min(contact_well_by, ptx_well_by))
|
||
|
|
|
||
|
|
height = self.pmos.pmos.active_width + 2 * self.well_enclose_active
|
||
|
|
self.add_rect(layer, self.well_ll, self.width + well_extend, height + 2 * well_extend)
|
||
|
|
|
||
|
|
def add_well_tap(self):
|
||
|
|
|
||
|
|
layer_stack = self.active_stack
|
||
|
|
contact_x = self.pmos_insts[self.cols - 1].rx() + self.active_space
|
||
|
|
contact_offset = vector(contact_x, self.pmos.height * 0.5)
|
||
|
|
|
||
|
|
self.nwell_contact = self.add_via_center(layers=layer_stack,
|
||
|
|
offset=contact_offset,
|
||
|
|
implant_type="n",
|
||
|
|
well_type="n",
|
||
|
|
directions=("V", "V"))
|
||
|
|
|
||
|
|
def route_supply(self):
|
||
|
|
start_pin = self.pmos_insts[0].get_pin("S").lx()
|
||
|
|
end_pin = self.pmos_insts[-1].get_pin("S").rx()
|
||
|
|
spacing = drc["{0}_to_{0}".format(self.route_layer)]
|
||
|
|
start = vector(start_pin, -1.5*spacing)
|
||
|
|
end = vector(end_pin, -1.5*spacing)
|
||
|
|
|
||
|
|
self.vdd = self.add_layout_pin_segment_center("vdd", "m1", start, end)
|
||
|
|
|
||
|
|
for i in range(self.cols):
|
||
|
|
start = self.pmos_insts[i].get_pin("S").center()
|
||
|
|
end = vector(start.x, self.vdd.cy())
|
||
|
|
|
||
|
|
self.add_segment_center(self.route_layer, start, end)
|
||
|
|
self.add_via_stack_center(end, self.route_layer, "m1")
|
||
|
|
|
||
|
|
|
||
|
|
# connect nwell tap to vdd
|
||
|
|
|
||
|
|
start = end
|
||
|
|
end = vector(self.nwell_contact.cx(), start.y)
|
||
|
|
self.add_segment_center(self.route_layer, start, end)
|
||
|
|
|
||
|
|
start = end - vector(0, 0.5 * self.mcon_width)
|
||
|
|
end = self.nwell_contact.center()
|
||
|
|
self.add_segment_center(self.route_layer, start, end)
|
||
|
|
|