OpenRAM/compiler/modules/pinv_dec.py

244 lines
10 KiB
Python
Raw Normal View History

# See LICENSE for licensing information.
#
2023-01-29 07:56:27 +01:00
# Copyright (c) 2016-2023 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.
#
2022-11-27 22:01:20 +01:00
from openram import debug
from openram.base import vector
from openram.sram_factory import factory
from openram.tech import drc, parameter, layer
from openram.tech import cell_properties as cell_props
from openram import OPTS
from .pinv import pinv
class pinv_dec(pinv):
"""
This is another version of pinv but with layout for the decoder.
Other stuff is the same (netlist, sizes, etc.)
"""
def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True):
debug.info(2,
"creating pinv_dec structure {0} with size of {1}".format(name,
size))
if not height:
b = factory.create(module_type=OPTS.bitcell)
self.cell_height = b.height
else:
self.cell_height = height
# Inputs to cells are on input layer
# Outputs from cells are on output layer
2020-06-12 23:23:26 +02:00
if OPTS.tech_name == "sky130":
self.supply_layer = "m1"
else:
self.supply_layer = "m2"
2020-11-03 15:29:17 +01:00
2020-08-12 22:22:28 +02:00
super().__init__(name, size, beta, self.cell_height, add_wells)
def determine_tx_mults(self):
"""
Determines the number of fingers needed to achieve the size within
the height constraint. This may fail if the user has a tight height.
"""
# This is always 1 tx, because we have horizontal transistors.
self.tx_mults = 1
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
2020-10-28 18:39:54 +01:00
if cell_props.ptx.bin_spice_models:
self.nmos_width = self.nearest_bin("nmos", self.nmos_width)
self.pmos_width = self.nearest_bin("pmos", self.pmos_width)
2020-11-03 15:29:17 +01:00
# Over-ride the route input gate to call the horizontal version.
# Other top-level netlist and layout functions are not changed.
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", directions=None):
"""
Route the input gate to the left side of the cell for access.
Position is actually ignored and is left to be compatible with the pinv.
"""
nmos_gate_pin = nmos_inst.get_pin("G")
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().y != pmos_gate_pin.ll().y:
self.gds_write("unaliged_gates.gds")
debug.check(nmos_gate_pin.ll().y == pmos_gate_pin.ll().y,
"Connecting unaligned gates not supported. See unaligned_gates.gds.")
2020-11-03 15:29:17 +01:00
# Pick point on the left of NMOS and up to PMOS
nmos_gate_pos = nmos_gate_pin.rc()
pmos_gate_pos = pmos_gate_pin.lc()
self.add_path("poly", [nmos_gate_pos, pmos_gate_pos])
# Center is completely symmetric.
contact_width = self.poly_contact.width
contact_offset = nmos_gate_pin.lc() \
- vector(self.poly_extend_active + 0.5 * contact_width, 0)
via = self.add_via_stack_center(from_layer="poly",
to_layer=self.route_layer,
offset=contact_offset,
directions=directions)
self.add_path("poly", [contact_offset, nmos_gate_pin.lc()])
2020-11-03 15:29:17 +01:00
self.add_layout_pin_rect_center(text=name,
layer=self.route_layer,
offset=contact_offset,
width=via.mod.second_layer_width,
height=via.mod.second_layer_height)
def determine_width(self):
self.width = self.pmos_inst.rx() + self.well_extend_active
def extend_wells(self):
""" Extend bottom to top for each well. """
if "pwell" in layer:
2020-06-14 23:09:45 +02:00
ll = (self.nmos_inst.ll() - vector(2 * [self.well_enclose_active])).scale(1, 0)
ur = self.nmos_inst.ur() + vector(2 * [self.well_enclose_active])
self.add_rect(layer="pwell",
offset=ll,
width=ur.x - ll.x,
2020-06-13 00:23:51 +02:00
height=self.height - ll.y + 0.5 * self.pwell_contact.height + self.well_enclose_active)
if "nwell" in layer:
2020-06-14 23:09:45 +02:00
ll = (self.pmos_inst.ll() - vector(2 * [self.well_enclose_active])).scale(1, 0)
ur = self.pmos_inst.ur() + vector(2 * [self.well_enclose_active])
self.add_rect(layer="nwell",
2020-06-13 00:23:51 +02:00
offset=ll,
width=ur.x - ll.x,
height=self.height - ll.y + 0.5 * self.nwell_contact.height + self.well_enclose_active)
2020-06-14 23:09:45 +02:00
def place_ptx(self):
"""
"""
2020-06-14 23:09:45 +02:00
# center the transistors in the y-dimension (it is rotated, so use the width)
y_offset = 0.5 * (self.height - self.nmos.width) + self.nmos.width
2020-08-14 01:20:39 +02:00
if OPTS.tech_name == "sky130":
# make room for well contacts between cells
y_offset = (0.5 * (self.height - self.nmos.width) + self.nmos.width) * 0.9
# offset so that the input contact is over from the left edge by poly spacing
x_offset = self.nmos.active_offset.y + self.poly_contact.width + self.poly_space
self.nmos_pos = vector(x_offset, y_offset)
self.nmos_inst.place(self.nmos_pos,
rotate=270)
# place PMOS so it is half a poly spacing down from the top
well_offsets = 2 * self.poly_extend_active + 2 * self.well_extend_active + drc("pwell_to_nwell")
# This is to provide spacing for the vdd rails
metal_offsets = 2 * self.m3_pitch
xoffset = self.nmos_inst.rx() + max(well_offsets, metal_offsets)
2020-06-13 00:23:51 +02:00
self.pmos_pos = vector(xoffset, y_offset)
self.pmos_inst.place(self.pmos_pos,
rotate=270)
# Output position will be in between the PMOS and NMOS drains
pmos_drain_pos = self.pmos_inst.get_pin("D").center()
nmos_drain_pos = self.nmos_inst.get_pin("D").center()
self.output_pos = vector(0.5 * (pmos_drain_pos.x + nmos_drain_pos.x), nmos_drain_pos.y)
2020-10-28 17:54:15 +01:00
if cell_props.pgate.add_implants:
self.extend_implants()
2020-11-03 15:29:17 +01:00
2020-10-28 17:54:15 +01:00
def extend_implants(self):
2020-06-13 00:23:51 +02:00
"""
Add top-to-bottom implants for adjacency issues in s8.
"""
# Route to the bottom
ll = (self.nmos_inst.ll() - vector(2 * [self.implant_enclose_active])).scale(1, 0)
# Don't route to the top
ur = self.nmos_inst.ur() + vector(self.implant_enclose_active, 0)
self.add_rect("nimplant",
ll,
ur.x - ll.x,
ur.y - ll.y)
# Route to the bottom
ll = (self.pmos_inst.ll() - vector(2 * [self.implant_enclose_active])).scale(1, 0)
# Don't route to the top
ur = self.pmos_inst.ur() + vector(self.implant_enclose_active, 0)
self.add_rect("pimplant",
ll,
ur.x - ll.x,
ur.y - ll.y)
2020-11-03 15:29:17 +01:00
def route_outputs(self):
"""
Route the output (drains) together.
Optionally, routes output to edge.
"""
2020-11-03 15:29:17 +01:00
# Get the drain pin
nmos_drain_pin = self.nmos_inst.get_pin("D")
# Pick point at right most of NMOS and connect over to PMOS
nmos_drain_pos = nmos_drain_pin.lc()
right_side = vector(self.width, nmos_drain_pos.y)
self.add_layout_pin_segment_center("Z",
self.route_layer,
nmos_drain_pos,
right_side)
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
source_pos = self.pmos_inst.get_pin("S").center()
contact_pos = vector(source_pos.x, self.height)
self.add_via_center(layers=self.active_stack,
offset=contact_pos,
implant_type="n",
well_type="n")
self.add_via_stack_center(offset=contact_pos,
from_layer=self.active_stack[2],
to_layer=self.supply_layer)
source_pos = self.nmos_inst.get_pin("S").center()
contact_pos = vector(source_pos.x, self.height)
self.add_via_center(layers=self.active_stack,
offset=contact_pos,
implant_type="p",
well_type="p")
self.add_via_stack_center(offset=contact_pos,
from_layer=self.active_stack[2],
to_layer=self.supply_layer)
2020-06-14 23:09:45 +02:00
def route_supply_rails(self):
pin = self.nmos_inst.get_pin("S")
source_pos = pin.center()
bottom_pos = source_pos.scale(1, 0)
top_pos = bottom_pos + vector(0, self.height)
self.add_layout_pin_segment_center("gnd",
self.supply_layer,
start=bottom_pos,
end=top_pos)
pin = self.pmos_inst.get_pin("S")
source_pos = pin.center()
bottom_pos = source_pos.scale(1, 0)
top_pos = bottom_pos + vector(0, self.height)
self.add_layout_pin_segment_center("vdd",
self.supply_layer,
start=bottom_pos,
end=top_pos)
2020-11-03 15:29:17 +01:00
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
source_pos = self.nmos_inst.get_pin("S").center()
self.add_via_stack_center(offset=source_pos,
from_layer=self.route_layer,
to_layer=self.supply_layer)
source_pos = self.pmos_inst.get_pin("S").center()
self.add_via_stack_center(offset=source_pos,
from_layer=self.route_layer,
to_layer=self.supply_layer)
2020-11-03 15:29:17 +01:00