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
a23f72d5a3
|
|
@ -42,6 +42,8 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
if implant_type or well_type:
|
||||
self.add_comment("implant type: {}\n".format(implant_type))
|
||||
self.add_comment("well_type: {}\n".format(well_type))
|
||||
|
||||
self.is_well_contact = implant_type == well_type
|
||||
|
||||
self.layer_stack = layer_stack
|
||||
self.dimensions = dimensions
|
||||
|
|
@ -114,7 +116,12 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
|
||||
self.first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name))
|
||||
self.first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name))
|
||||
self.first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name))
|
||||
# If there's a different rule for active
|
||||
# FIXME: Make this more elegant
|
||||
if self.is_well_contact and self.first_layer_name == "active" and "tap_extend_contact" in drc.keys():
|
||||
self.first_layer_extend = drc("tap_extend_contact")
|
||||
else:
|
||||
self.first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name))
|
||||
|
||||
self.second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name))
|
||||
self.second_layer_enclosure = drc("{0}_enclose_{1}".format(self.second_layer_name, self.via_layer_name))
|
||||
|
|
@ -231,14 +238,18 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
# Optionally implant well if layer exists
|
||||
well_layer = "{}well".format(self.well_type)
|
||||
if well_layer in tech.layer:
|
||||
well_enclose_active = drc(well_layer + "_enclose_active")
|
||||
well_position = self.first_layer_position - [well_enclose_active] * 2
|
||||
well_width = self.first_layer_width + 2 * well_enclose_active
|
||||
well_height = self.first_layer_height + 2 * well_enclose_active
|
||||
well_width_rule = drc("minwidth_" + well_layer)
|
||||
self.well_enclose_active = drc(well_layer + "_enclose_active")
|
||||
self.well_width = max(self.first_layer_width + 2 * self.well_enclose_active,
|
||||
well_width_rule)
|
||||
self.well_height = max(self.first_layer_height + 2 * self.well_enclose_active,
|
||||
well_width_rule)
|
||||
center_pos = vector(0.5*self.width, 0.5*self.height)
|
||||
well_position = center_pos - vector(0.5*self.well_width, 0.5*self.well_height)
|
||||
self.add_rect(layer=well_layer,
|
||||
offset=well_position,
|
||||
width=well_width,
|
||||
height=well_height)
|
||||
width=self.well_width,
|
||||
height=self.well_height)
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
""" Get total power of a module """
|
||||
|
|
@ -257,6 +268,21 @@ for layer_stack in tech.layer_stacks:
|
|||
else:
|
||||
setattr(module, layer1 + "_via", cont)
|
||||
|
||||
|
||||
# Set up a static for each well contact for measurements
|
||||
if "nwell" in tech.layer:
|
||||
cont = factory.create(module_type="contact",
|
||||
layer_stack=tech.active_stack,
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
module = sys.modules[__name__]
|
||||
setattr(module, "nwell_contact", cont)
|
||||
|
||||
if "pwell" in tech.layer:
|
||||
cont = factory.create(module_type="contact",
|
||||
layer_stack=tech.active_stack,
|
||||
implant_type="p",
|
||||
well_type="p")
|
||||
module = sys.modules[__name__]
|
||||
setattr(module, "pwell_contact", cont)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -113,6 +113,15 @@ class design(hierarchy_design):
|
|||
if match:
|
||||
setattr(self, match.group(0), drc(match.group(0)))
|
||||
|
||||
# Create the maximum well extend active that gets used
|
||||
# by cells to extend the wells for interaction with other cells
|
||||
from tech import layer
|
||||
self.well_extend_active = 0
|
||||
if "nwell" in layer:
|
||||
self.well_extend_active = max(self.well_extend_active, self.nwell_extend_active)
|
||||
if "pwell" in layer:
|
||||
self.well_extend_active = max(self.well_extend_active, self.pwell_extend_active)
|
||||
|
||||
# These are for debugging previous manual rules
|
||||
if False:
|
||||
print("poly_width", self.poly_width)
|
||||
|
|
|
|||
|
|
@ -5,19 +5,15 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import sys
|
||||
from tech import drc, parameter
|
||||
import debug
|
||||
import design
|
||||
import math
|
||||
from math import log,sqrt,ceil
|
||||
import contact
|
||||
import pgates
|
||||
from sram_factory import factory
|
||||
from math import log
|
||||
from tech import drc
|
||||
from vector import vector
|
||||
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class bank(design.design):
|
||||
"""
|
||||
Dynamically generated a single bank including bitcell array,
|
||||
|
|
@ -500,6 +496,8 @@ class bank(design.design):
|
|||
Create a 2:4 or 3:8 column address decoder.
|
||||
"""
|
||||
|
||||
# Height is a multiple of DFF so that it can be staggered
|
||||
# and rows do not align with the control logic module
|
||||
self.dff = factory.create(module_type="dff")
|
||||
|
||||
if self.col_addr_size == 0:
|
||||
|
|
@ -868,7 +866,6 @@ class bank(design.design):
|
|||
route_map = list(zip(decode_pins, column_mux_pins))
|
||||
self.create_vertical_channel_route(route_map, offset, self.m1_stack)
|
||||
|
||||
|
||||
def add_lvs_correspondence_points(self):
|
||||
""" This adds some points for easier debugging if LVS goes wrong.
|
||||
These should probably be turned off by default though, since extraction
|
||||
|
|
@ -913,7 +910,6 @@ class bank(design.design):
|
|||
layer="m1",
|
||||
offset=data_pin.center())
|
||||
|
||||
|
||||
def route_control_lines(self, port):
|
||||
""" Route the control lines of the entire bank """
|
||||
|
||||
|
|
@ -921,23 +917,25 @@ class bank(design.design):
|
|||
# From control signal to the module pin
|
||||
# Connection from the central bus to the main control block crosses
|
||||
# pre-decoder and this connection is in metal3
|
||||
write_inst = 0
|
||||
read_inst = 0
|
||||
|
||||
connection = []
|
||||
connection.append((self.prefix+"p_en_bar{}".format(port), self.port_data_inst[port].get_pin("p_en_bar").lc()))
|
||||
connection.append((self.prefix + "p_en_bar{}".format(port),
|
||||
self.port_data_inst[port].get_pin("p_en_bar").lc()))
|
||||
|
||||
rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port])
|
||||
connection.append((self.prefix+"wl_en{}".format(port), self.bitcell_array_inst.get_pin(rbl_wl_name).lc()))
|
||||
connection.append((self.prefix + "wl_en{}".format(port),
|
||||
self.bitcell_array_inst.get_pin(rbl_wl_name).lc()))
|
||||
|
||||
if port in self.write_ports:
|
||||
if port % 2:
|
||||
connection.append((self.prefix+"w_en{}".format(port), self.port_data_inst[port].get_pin("w_en").rc()))
|
||||
connection.append((self.prefix + "w_en{}".format(port),
|
||||
self.port_data_inst[port].get_pin("w_en").rc()))
|
||||
else:
|
||||
connection.append((self.prefix+"w_en{}".format(port), self.port_data_inst[port].get_pin("w_en").lc()))
|
||||
connection.append((self.prefix + "w_en{}".format(port),
|
||||
self.port_data_inst[port].get_pin("w_en").lc()))
|
||||
|
||||
if port in self.read_ports:
|
||||
connection.append((self.prefix+"s_en{}".format(port), self.port_data_inst[port].get_pin("s_en").lc()))
|
||||
connection.append((self.prefix + "s_en{}".format(port),
|
||||
self.port_data_inst[port].get_pin("s_en").lc()))
|
||||
|
||||
for (control_signal, pin_pos) in connection:
|
||||
control_mid_pos = self.bus_xoffset[port][control_signal]
|
||||
|
|
@ -961,7 +959,7 @@ class bank(design.design):
|
|||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=control_pos)
|
||||
|
||||
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
||||
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
||||
"""Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline"""
|
||||
#Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption
|
||||
stage_effort_list = []
|
||||
|
|
|
|||
|
|
@ -50,10 +50,11 @@ class dff_buf(design.design):
|
|||
self.create_instances()
|
||||
|
||||
def create_layout(self):
|
||||
self.width = self.dff.width + self.inv1.width + self.inv2.width
|
||||
self.place_instances()
|
||||
self.width = self.inv2_inst.rx()
|
||||
self.height = self.dff.height
|
||||
|
||||
self.place_instances()
|
||||
|
||||
self.route_wires()
|
||||
self.add_layout_pins()
|
||||
self.add_boundary()
|
||||
|
|
@ -106,7 +107,10 @@ class dff_buf(design.design):
|
|||
self.dff_inst.place(vector(0,0))
|
||||
|
||||
# Add INV1 to the right
|
||||
self.inv1_inst.place(vector(self.dff_inst.rx(),0))
|
||||
well_spacing = max(self.nwell_space,
|
||||
self.pwell_space,
|
||||
self.pwell_to_nwell)
|
||||
self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active,0))
|
||||
|
||||
# Add INV2 to the right
|
||||
self.inv2_inst.place(vector(self.inv1_inst.rx(),0))
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ class dff_buf_array(design.design):
|
|||
inv2_size=self.inv2_size)
|
||||
self.add_mod(self.dff)
|
||||
|
||||
|
||||
def create_dff_array(self):
|
||||
self.dff_insts={}
|
||||
for row in range(self.rows):
|
||||
|
|
@ -94,14 +95,21 @@ class dff_buf_array(design.design):
|
|||
self.connect_inst(inst_ports)
|
||||
|
||||
def place_dff_array(self):
|
||||
|
||||
well_spacing = max(self.nwell_space,
|
||||
self.pwell_space,
|
||||
self.pwell_to_nwell)
|
||||
|
||||
dff_pitch = self.dff.width + well_spacing + self.well_extend_active
|
||||
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
||||
if (row % 2 == 0):
|
||||
base = vector(col*self.dff.width,row*self.dff.height)
|
||||
base = vector(col*dff_pitch,row*self.dff.height)
|
||||
mirror = "R0"
|
||||
else:
|
||||
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
||||
base = vector(col*dff_pitch,(row+1)*self.dff.height)
|
||||
mirror = "MX"
|
||||
self.dff_insts[row,col].place(offset=base,
|
||||
mirror=mirror)
|
||||
|
|
|
|||
|
|
@ -5,18 +5,14 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
from tech import drc
|
||||
import debug
|
||||
import design
|
||||
from math import log
|
||||
from math import sqrt
|
||||
from math import ceil
|
||||
import math
|
||||
import contact
|
||||
from sram_factory import factory
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class hierarchical_decoder(design.design):
|
||||
"""
|
||||
Dynamically generated hierarchical decoder.
|
||||
|
|
@ -34,13 +30,12 @@ class hierarchical_decoder(design.design):
|
|||
self.cell_height = b.height
|
||||
self.rows = rows
|
||||
self.num_inputs = math.ceil(math.log(self.rows, 2))
|
||||
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
||||
(self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.setup_netlist_constants()
|
||||
|
|
@ -308,7 +303,6 @@ class hierarchical_decoder(design.design):
|
|||
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
|
||||
|
||||
self.pre2x4_inst[num].place(base)
|
||||
|
||||
|
||||
def place_pre3x8(self,num):
|
||||
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
||||
|
|
@ -321,7 +315,6 @@ class hierarchical_decoder(design.design):
|
|||
|
||||
self.pre3x8_inst[num].place(offset)
|
||||
|
||||
|
||||
def create_row_decoder(self):
|
||||
""" Create the row-decoder by placing NAND2/NAND3 and Inverters
|
||||
and add the primary decoder output pins. """
|
||||
|
|
@ -329,7 +322,6 @@ class hierarchical_decoder(design.design):
|
|||
self.create_decoder_nand_array()
|
||||
self.create_decoder_inv_array()
|
||||
|
||||
|
||||
def create_decoder_nand_array(self):
|
||||
""" Add a column of NAND gates for final decode """
|
||||
|
||||
|
|
@ -556,7 +548,7 @@ class hierarchical_decoder(design.design):
|
|||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
||||
|
||||
# The vias will be placed in the center and right of the cells, respectively.
|
||||
xoffset = self.nand_inst[0].cx()
|
||||
xoffset = self.nand_inst[0].rx()
|
||||
for num in range(0,self.rows):
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
# The nand and inv are the same height rows...
|
||||
|
|
|
|||
|
|
@ -8,12 +8,11 @@
|
|||
import debug
|
||||
import design
|
||||
import math
|
||||
from tech import drc
|
||||
import contact
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class hierarchical_predecode(design.design):
|
||||
"""
|
||||
Pre 2x4 and 3x8 decoder shared code.
|
||||
|
|
@ -42,7 +41,7 @@ class hierarchical_predecode(design.design):
|
|||
self.add_nand(self.number_of_inputs)
|
||||
self.add_mod(self.nand)
|
||||
|
||||
def add_nand(self,inputs):
|
||||
def add_nand(self, inputs):
|
||||
""" Create the NAND for the predecode input stage """
|
||||
if inputs==2:
|
||||
self.nand = factory.create(module_type="pnand2",
|
||||
|
|
@ -51,7 +50,7 @@ class hierarchical_predecode(design.design):
|
|||
self.nand = factory.create(module_type="pnand3",
|
||||
height=self.cell_height)
|
||||
else:
|
||||
debug.error("Invalid number of predecode inputs: {}".format(inputs),-1)
|
||||
debug.error("Invalid number of predecode inputs: {}".format(inputs), -1)
|
||||
|
||||
def setup_layout_constraints(self):
|
||||
|
||||
|
|
@ -89,7 +88,6 @@ class hierarchical_predecode(design.design):
|
|||
names=decode_names,
|
||||
length=self.height - 2*self.m1_width)
|
||||
|
||||
|
||||
def create_input_inverters(self):
|
||||
""" Create the input inverters to invert input signals for the decode stage. """
|
||||
self.in_inst = []
|
||||
|
|
@ -266,7 +264,7 @@ class hierarchical_predecode(design.design):
|
|||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
||||
|
||||
# Find the x offsets for where the vias/pins should be placed
|
||||
in_xoffset = self.in_inst[0].rx()
|
||||
in_xoffset = self.in_inst[0].rx() + self.m1_space
|
||||
out_xoffset = self.inv_inst[0].lx() - self.m1_space
|
||||
for num in range(0,self.number_of_outputs):
|
||||
# this will result in duplicate polygons for rails, but who cares
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import debug
|
|||
from tech import layer
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class pgate(design.design):
|
||||
|
|
@ -29,7 +28,7 @@ class pgate(design.design):
|
|||
elif not height:
|
||||
# By default, we make it 8 M1 pitch tall
|
||||
self.height = 8*self.m1_pitch
|
||||
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
|
@ -75,11 +74,13 @@ class pgate(design.design):
|
|||
pmos_gate_pin = pmos_inst.get_pin("G")
|
||||
|
||||
# Check if the gates are aligned and give an error if they aren't!
|
||||
if nmos_gate_pin.ll().x != pmos_gate_pin.ll().x:
|
||||
self.gds_write("unaliged_gates.gds")
|
||||
debug.check(nmos_gate_pin.ll().x == pmos_gate_pin.ll().x,
|
||||
"Connecting unaligned gates not supported.")
|
||||
"Connecting unaligned gates not supported. See unaligned_gates.gds.")
|
||||
|
||||
# Pick point on the left of NMOS and connect down to PMOS
|
||||
nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5 * self.poly_width, 0)
|
||||
# Pick point on the left of NMOS and up to PMOS
|
||||
nmos_gate_pos = nmos_gate_pin.ul() + vector(0.5 * self.poly_width, 0)
|
||||
pmos_gate_pos = vector(nmos_gate_pos.x, pmos_gate_pin.bc().y)
|
||||
self.add_path("poly", [nmos_gate_pos, pmos_gate_pos])
|
||||
|
||||
|
|
@ -123,19 +124,22 @@ class pgate(design.design):
|
|||
height=contact.poly_contact.first_layer_width,
|
||||
width=left_gate_offset.x - contact_offset.x)
|
||||
|
||||
def extend_wells(self, middle_position):
|
||||
def extend_wells(self):
|
||||
""" Extend the n/p wells to cover whole cell """
|
||||
|
||||
# This should match the cells in the cell library
|
||||
nwell_y_offset = 0.48 * self.height
|
||||
full_height = self.height + 0.5*self.m1_width
|
||||
|
||||
# FIXME: float rounding problem
|
||||
middle_position = middle_position.snap_to_grid()
|
||||
if "nwell" in layer:
|
||||
# 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,
|
||||
self.height + 0.5 * self.m1_width)
|
||||
nwell_position = middle_position
|
||||
nwell_height = nwell_max_offset - middle_position.y
|
||||
full_height)
|
||||
nwell_position = vector(0, nwell_y_offset) - vector(self.well_extend_active, 0)
|
||||
nwell_height = nwell_max_offset - nwell_y_offset
|
||||
self.add_rect(layer="nwell",
|
||||
offset=middle_position,
|
||||
offset=nwell_position,
|
||||
width=self.well_width,
|
||||
height=nwell_height)
|
||||
if "vtg" in layer:
|
||||
|
|
@ -148,8 +152,8 @@ class pgate(design.design):
|
|||
if "pwell" in layer:
|
||||
pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y,
|
||||
-0.5 * self.m1_width)
|
||||
pwell_position = vector(0, pwell_min_offset)
|
||||
pwell_height = middle_position.y - pwell_position.y
|
||||
pwell_position = vector(-self.well_extend_active, pwell_min_offset)
|
||||
pwell_height = nwell_y_offset - pwell_position.y
|
||||
self.add_rect(layer="pwell",
|
||||
offset=pwell_position,
|
||||
width=self.well_width,
|
||||
|
|
@ -181,7 +185,6 @@ class pgate(design.design):
|
|||
0.5 * pmos.active_contact.first_layer_height)
|
||||
self.nwell_contact = self.add_via_center(layers=layer_stack,
|
||||
offset=contact_offset,
|
||||
directions=("H", "V"),
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
self.add_rect_center(layer="m1",
|
||||
|
|
@ -235,7 +238,6 @@ class pgate(design.design):
|
|||
0.5 * nmos.active_contact.first_layer_height)
|
||||
self.pwell_contact= self.add_via_center(layers=layer_stack,
|
||||
offset=contact_offset,
|
||||
directions=("H", "V"),
|
||||
implant_type="p",
|
||||
well_type="p")
|
||||
self.add_rect_center(layer="m1",
|
||||
|
|
|
|||
|
|
@ -52,10 +52,10 @@ class pinv(pgate.pgate):
|
|||
def create_layout(self):
|
||||
""" Calls all functions related to the generation of the layout """
|
||||
self.setup_layout_constants()
|
||||
self.route_supply_rails()
|
||||
self.place_ptx()
|
||||
self.add_well_contacts()
|
||||
self.extend_wells(self.well_pos)
|
||||
self.extend_wells()
|
||||
self.route_supply_rails()
|
||||
self.connect_rails()
|
||||
self.route_input_gate(self.pmos_inst,
|
||||
self.nmos_inst,
|
||||
|
|
@ -104,8 +104,9 @@ class pinv(pgate.pgate):
|
|||
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
|
||||
# This is a poly-to-poly of a flipped cell
|
||||
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
|
||||
self.poly_extend_active, self.poly_space)
|
||||
self.poly_extend_active + self.poly_space)
|
||||
total_height = tx_height + min_channel + 2 * self.top_bottom_space
|
||||
|
||||
debug.check(self.height > total_height,
|
||||
"Cell height {0} too small for simple min height {1}.".format(self.height,
|
||||
total_height))
|
||||
|
|
@ -148,14 +149,18 @@ class pinv(pgate.pgate):
|
|||
|
||||
def setup_layout_constants(self):
|
||||
"""
|
||||
Pre-compute some handy layout parameters.
|
||||
Compute the width and height
|
||||
"""
|
||||
|
||||
# the well width is determined the multi-finger PMOS device width plus
|
||||
# the well contact width and half well enclosure on both sides
|
||||
self.well_width = self.pmos.active_width + self.pmos.active_contact.width \
|
||||
+ self.active_space + 2*self.nwell_enclose_active
|
||||
self.width = self.well_width
|
||||
# the width is determined the multi-finger PMOS device width plus
|
||||
# the well contact width, spacing between them
|
||||
# space is for power supply contact to nwell m1 spacing
|
||||
self.width = self.pmos.active_offset.x + self.pmos.active_width \
|
||||
+ self.active_space + contact.nwell_contact.width \
|
||||
+ 0.5 * self.nwell_enclose_active \
|
||||
+ self.m1_space
|
||||
# This includes full enclosures on each end
|
||||
self.well_width = self.width + 2*self.nwell_enclose_active
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
def add_ptx(self):
|
||||
|
|
@ -223,9 +228,6 @@ class pinv(pgate.pgate):
|
|||
nmos_drain_pos = self.nmos_inst.get_pin("D").ul()
|
||||
self.output_pos = vector(0, 0.5 * (pmos_drain_pos.y + nmos_drain_pos.y))
|
||||
|
||||
# This will help with the wells
|
||||
self.well_pos = self.output_pos
|
||||
|
||||
def route_outputs(self):
|
||||
"""
|
||||
Route the output (drains) together.
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class pnand2(pgate.pgate):
|
|||
self.place_ptx()
|
||||
self.connect_rails()
|
||||
self.add_well_contacts()
|
||||
self.extend_wells(self.well_pos)
|
||||
self.extend_wells()
|
||||
self.route_inputs()
|
||||
self.route_output()
|
||||
|
||||
|
|
@ -98,11 +98,11 @@ class pnand2(pgate.pgate):
|
|||
|
||||
# Two PMOS devices and a well contact. Separation between each.
|
||||
# Enclosure space on the sides.
|
||||
self.well_width = 2 * self.pmos.active_width + contact.active_contact.width \
|
||||
self.width = 2 * self.pmos.active_width + contact.active_contact.width \
|
||||
+ 2 * self.active_space \
|
||||
+ 2 * self.nwell_enclose_active
|
||||
+ 0.5 * self.nwell_enclose_active
|
||||
|
||||
self.width = self.well_width
|
||||
self.well_width = self.width + 2 * self.nwell_enclose_active
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
# This is the extra space needed to ensure DRC rules
|
||||
|
|
@ -110,7 +110,7 @@ class pnand2(pgate.pgate):
|
|||
extra_contact_space = max(-self.nmos.get_pin("D").by(), 0)
|
||||
# This is a poly-to-poly of a flipped cell
|
||||
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
|
||||
self.poly_extend_active, self.poly_space)
|
||||
self.poly_extend_active + self.poly_space)
|
||||
|
||||
def route_supply_rails(self):
|
||||
""" Add vdd/gnd rails to the top and bottom. """
|
||||
|
|
@ -170,9 +170,6 @@ class pnand2(pgate.pgate):
|
|||
self.output_pos = vector(0,
|
||||
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
|
||||
|
||||
# This will help with the wells
|
||||
self.well_pos = self.output_pos
|
||||
|
||||
def add_well_contacts(self):
|
||||
"""
|
||||
Add n/p well taps to the layout and connect to supplies
|
||||
|
|
@ -227,11 +224,11 @@ class pnand2(pgate.pgate):
|
|||
|
||||
# Non-preferred active contacts
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
directions=("V","H"),
|
||||
directions=("V", "H"),
|
||||
offset=pmos_pin.center())
|
||||
# Non-preferred active contacts
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
directions=("V","H"),
|
||||
directions=("V", "H"),
|
||||
offset=nmos_pin.center())
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=out_offset)
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class pnand3(pgate.pgate):
|
|||
self.place_ptx()
|
||||
self.connect_rails()
|
||||
self.add_well_contacts()
|
||||
self.extend_wells(self.well_pos)
|
||||
self.extend_wells()
|
||||
self.route_inputs()
|
||||
self.route_output()
|
||||
|
||||
|
|
@ -91,10 +91,10 @@ class pnand3(pgate.pgate):
|
|||
|
||||
# Two PMOS devices and a well contact. Separation between each.
|
||||
# Enclosure space on the sides.
|
||||
self.well_width = 3 * self.pmos.active_width + self.pmos.active_contact.width \
|
||||
+ 2 * self.active_space + 2 * self.nwell_enclose_active \
|
||||
self.width = 3 * self.pmos.active_width + self.pmos.active_contact.width \
|
||||
+ 2 * self.active_space + 0.5 * self.nwell_enclose_active \
|
||||
- self.overlap_offset.x
|
||||
self.width = self.well_width
|
||||
self.well_width = self.width + 2 * self.nwell_enclose_active
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
# This is the extra space needed to ensure DRC rules
|
||||
|
|
@ -102,10 +102,8 @@ class pnand3(pgate.pgate):
|
|||
nmos = factory.create(module_type="ptx", tx_type="nmos")
|
||||
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
|
||||
# This is a poly-to-poly of a flipped cell
|
||||
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space \
|
||||
+ extra_contact_space,
|
||||
self.poly_extend_active,
|
||||
self.poly_space)
|
||||
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
|
||||
self.poly_extend_active + self.poly_space)
|
||||
|
||||
def route_supply_rails(self):
|
||||
""" Add vdd/gnd rails to the top and bottom. """
|
||||
|
|
@ -179,9 +177,6 @@ class pnand3(pgate.pgate):
|
|||
# This will help with the wells and the input/output placement
|
||||
self.output_pos = vector(0, 0.5*self.height)
|
||||
|
||||
# This should be placed at the top of the NMOS well
|
||||
self.well_pos = self.output_pos
|
||||
|
||||
def add_well_contacts(self):
|
||||
""" Add n/p well taps to the layout and connect to supplies """
|
||||
|
||||
|
|
@ -238,17 +233,20 @@ class pnand3(pgate.pgate):
|
|||
|
||||
# Go up to metal2 for ease on all output pins
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=pmos1_pin.center())
|
||||
offset=pmos1_pin.center(),
|
||||
directions=("V", "V"))
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=pmos3_pin.center())
|
||||
offset=pmos3_pin.center(),
|
||||
directions=("V", "V"))
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=nmos3_pin.center())
|
||||
offset=nmos3_pin.center(),
|
||||
directions=("V", "V"))
|
||||
|
||||
# PMOS3 and NMOS3 are drain aligned
|
||||
self.add_path("m2", [pmos3_pin.bc(), nmos3_pin.uc()])
|
||||
self.add_path("m2", [pmos3_pin.center(), nmos3_pin.uc()])
|
||||
# Route in the A input track (top track)
|
||||
mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
|
||||
self.add_path("m2", [pmos1_pin.bc(), mid_offset, nmos3_pin.uc()])
|
||||
self.add_path("m2", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()])
|
||||
|
||||
# This extends the output to the edge of the cell
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class pnor2(pgate.pgate):
|
|||
self.place_ptx()
|
||||
self.connect_rails()
|
||||
self.add_well_contacts()
|
||||
self.extend_wells(self.well_pos)
|
||||
self.extend_wells()
|
||||
self.route_inputs()
|
||||
self.route_output()
|
||||
|
||||
|
|
@ -95,11 +95,11 @@ class pnor2(pgate.pgate):
|
|||
|
||||
# Two PMOS devices and a well contact. Separation between each.
|
||||
# Enclosure space on the sides.
|
||||
self.well_width = 2 * self.pmos.active_width \
|
||||
self.width = 2 * self.pmos.active_width \
|
||||
+ self.pmos.active_contact.width \
|
||||
+ 2 * self.active_space \
|
||||
+ 2 * self.nwell_enclose_active
|
||||
self.width = self.well_width
|
||||
+ 0.5 * self.nwell_enclose_active
|
||||
self.well_width = self.width + 2 * self.nwell_enclose_active
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
# This is the extra space needed to ensure DRC rules
|
||||
|
|
@ -168,9 +168,6 @@ class pnor2(pgate.pgate):
|
|||
self.output_pos = vector(0,
|
||||
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
|
||||
|
||||
# This will help with the wells
|
||||
self.well_pos = self.output_pos
|
||||
|
||||
def add_well_contacts(self):
|
||||
""" Add n/p well taps to the layout and connect to supplies """
|
||||
|
||||
|
|
@ -225,7 +222,7 @@ class pnor2(pgate.pgate):
|
|||
|
||||
# PMOS1 to mid-drain to NMOS2 drain
|
||||
self.add_path("m2",
|
||||
[pmos_pin.bc(), mid2_offset, mid3_offset])
|
||||
[pmos_pin.center(), mid2_offset, mid3_offset])
|
||||
self.add_path("m2",
|
||||
[nmos_pin.rc(), mid1_offset, mid2_offset])
|
||||
# This extends the output to the edge of the cell
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
import contact
|
||||
import design
|
||||
import debug
|
||||
from tech import drc, parameter
|
||||
from tech import parameter
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
|
|
|||
|
|
@ -154,17 +154,23 @@ class ptx(design.design):
|
|||
|
||||
# Poly height must include poly extension over active
|
||||
self.poly_height = self.tx_width + 2 * self.poly_extend_active
|
||||
|
||||
well_name = "{}well".format(self.well_type)
|
||||
|
||||
# The active offset is due to the well extension
|
||||
if well_name in layer:
|
||||
well_enclose_active = drc(well_name + "_enclose_active")
|
||||
self.active_offset = vector([well_enclose_active] * 2)
|
||||
if "pwell" in layer:
|
||||
pwell_enclose_active = drc("pwell_enclose_active")
|
||||
else:
|
||||
self.active_offset = vector(0, 0)
|
||||
pwell_enclose_active = 0
|
||||
if "nwell" in layer:
|
||||
nwell_enclose_active = drc("nwell_enclose_active")
|
||||
else:
|
||||
nwell_enclose_active = 0
|
||||
# Use the max of either so that the poly gates will align properly
|
||||
well_enclose_active = max(pwell_enclose_active,
|
||||
nwell_enclose_active)
|
||||
self.active_offset = vector([well_enclose_active] * 2)
|
||||
|
||||
# Well enclosure of active, ensure minwidth as well
|
||||
well_name = "{}well".format(self.well_type)
|
||||
if well_name in layer:
|
||||
well_width_rule = drc("minwidth_" + well_name)
|
||||
well_enclose_active = drc(well_name + "_enclose_active")
|
||||
|
|
@ -179,7 +185,7 @@ class ptx(design.design):
|
|||
# The well is not included in the height and width
|
||||
self.height = self.poly_height
|
||||
self.width = self.active_width
|
||||
|
||||
|
||||
# This is the center of the first active contact offset (centered vertically)
|
||||
self.contact_offset = self.active_offset + vector(0.5 * self.active_contact.width,
|
||||
0.5 * self.active_height)
|
||||
|
|
@ -334,13 +340,10 @@ class ptx(design.design):
|
|||
if not (well_name in layer or "vtg" in layer):
|
||||
return
|
||||
|
||||
center_pos = self.active_offset + vector(self.width / 2.0,
|
||||
self.height / 2.0)
|
||||
well_ll = center_pos - vector(self.well_width / 2.0,
|
||||
self.well_height / 2.0)
|
||||
well_ll = well_ll - vector(0,
|
||||
self.poly_extend_active)
|
||||
|
||||
center_pos = self.active_offset + vector(0.5*self.active_width,
|
||||
0.5*self.active_height)
|
||||
well_ll = center_pos - vector(0.5*self.well_width,
|
||||
0.5*self.well_height)
|
||||
if well_name in layer:
|
||||
self.add_rect(layer=well_name,
|
||||
offset=well_ll,
|
||||
|
|
|
|||
|
|
@ -6,14 +6,13 @@
|
|||
#All rights reserved.
|
||||
#
|
||||
import design
|
||||
from tech import drc, parameter, spice
|
||||
from tech import parameter
|
||||
import debug
|
||||
import math
|
||||
from tech import drc
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class pwrite_driver(design.design):
|
||||
"""
|
||||
The pwrite_driver is two tristate inverters that drive the bitlines.
|
||||
|
|
|
|||
|
|
@ -5,21 +5,10 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import sys
|
||||
from tech import drc, spice
|
||||
import debug
|
||||
from math import log,sqrt,ceil
|
||||
import datetime
|
||||
import getpass
|
||||
import numpy as np
|
||||
from vector import vector
|
||||
from globals import OPTS, print_time
|
||||
|
||||
from sram_base import sram_base
|
||||
from bank import bank
|
||||
from contact import m2_via
|
||||
from dff_buf_array import dff_buf_array
|
||||
from dff_array import dff_array
|
||||
|
||||
|
||||
class sram_1bank(sram_base):
|
||||
|
|
@ -328,10 +317,10 @@ class sram_1bank(sram_base):
|
|||
offset=flop_pos)
|
||||
|
||||
def route_col_addr_dff(self):
|
||||
""" Connect the output of the row flops to the bank pins """
|
||||
""" Connect the output of the col flops to the bank pins """
|
||||
for port in self.all_ports:
|
||||
if port%2:
|
||||
offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch)
|
||||
offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.col_addr_size+2)*self.m1_pitch)
|
||||
else:
|
||||
offset = self.col_addr_dff_insts[port].ul() + vector(0, 2*self.m1_pitch)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class contact_test(openram_test):
|
|||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
from tech import poly_stack, beol_stacks
|
||||
from tech import active_stack, poly_stack, beol_stacks
|
||||
|
||||
# Don't do active because of nwell contact rules
|
||||
# Don't do metal3 because of min area rules
|
||||
|
|
@ -67,6 +67,25 @@ class contact_test(openram_test):
|
|||
c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(3, 3))
|
||||
self.local_drc_check(c)
|
||||
|
||||
# Test the well taps
|
||||
# check vertical array with one in the middle and two ends
|
||||
layer_stack = active_stack
|
||||
stack_name = ":".join(map(str, layer_stack))
|
||||
|
||||
debug.info(2, "1 x 1 {} nwell".format(stack_name))
|
||||
c = factory.create(module_type="contact",
|
||||
layer_stack=layer_stack,
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
self.local_drc_check(c)
|
||||
|
||||
debug.info(2, "1 x 1 {} pwell".format(stack_name))
|
||||
c = factory.create(module_type="contact",
|
||||
layer_stack=layer_stack,
|
||||
implant_type="p",
|
||||
well_type="p")
|
||||
self.local_drc_check(c)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue