mirror of https://github.com/VLSIDA/OpenRAM.git
Thin-cell decoder changes.
Add hard decoder gates (nand, inv, pnan) Add conditions for routing using LI layer in s8. Generalize bus layers for decoders. Move custom cells to own directory. Fixed via directions, etc. Add 4x16 hierarchical decoder and test case
This commit is contained in:
parent
a305d788d7
commit
4a67f7dc71
|
|
@ -1,7 +1,7 @@
|
|||
before_script:
|
||||
- . /home/gitlab-runner/setup-paths.sh
|
||||
- export OPENRAM_HOME="`pwd`/compiler"
|
||||
- export OPENRAM_TECH="`pwd`/technology"
|
||||
- export OPENRAM_TECH="`pwd`/technology:/home/PDKs/skywater-tech"
|
||||
|
||||
stages:
|
||||
- test
|
||||
|
|
@ -25,6 +25,15 @@ scn4m_subm:
|
|||
- .coverage.*
|
||||
expire_in: 1 week
|
||||
|
||||
# s8:
|
||||
# stage: test
|
||||
# script:
|
||||
# - coverage run -p $OPENRAM_HOME/tests/regress.py -t s8
|
||||
# artifacts:
|
||||
# paths:
|
||||
# - .coverage.*
|
||||
# expire_in: 1 week
|
||||
|
||||
coverage:
|
||||
stage: coverage
|
||||
script:
|
||||
|
|
|
|||
|
|
@ -53,6 +53,10 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
first_dir = "H" if self.get_preferred_direction(layer_stack[0])=="V" else "V"
|
||||
second_dir = "H" if self.get_preferred_direction(layer_stack[2])=="V" else "V"
|
||||
self.directions = (first_dir, second_dir)
|
||||
# Preferred directions
|
||||
elif directions == "pref":
|
||||
self.directions = (tech.preferred_directions[layer_stack[0]],
|
||||
tech.preferred_directions[layer_stack[2]])
|
||||
# User directions
|
||||
elif directions:
|
||||
self.directions = directions
|
||||
|
|
@ -149,7 +153,7 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
self.first_layer_vertical_enclosure = max(self.first_layer_enclosure,
|
||||
(self.first_layer_minwidth - self.contact_array_height) / 2)
|
||||
else:
|
||||
debug.error("Invalid first layer direction.", -1)
|
||||
debug.error("Invalid first layer direction: ".format(self.directions[0]), -1)
|
||||
|
||||
# In some technologies, the minimum width may be larger
|
||||
# than the overlap requirement around the via, so
|
||||
|
|
@ -165,7 +169,7 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
self.second_layer_vertical_enclosure = max(self.second_layer_enclosure,
|
||||
(self.second_layer_minwidth - self.contact_array_width) / 2)
|
||||
else:
|
||||
debug.error("Invalid second layer direction.", -1)
|
||||
debug.error("Invalid secon layer direction: ".format(self.directions[1]), -1)
|
||||
|
||||
def create_contact_array(self):
|
||||
""" Create the contact array at the origin"""
|
||||
|
|
|
|||
|
|
@ -86,8 +86,14 @@ class geometry:
|
|||
elif mirror == "XY":
|
||||
ll = ll.scale(-1, -1)
|
||||
ur = ur.scale(-1, -1)
|
||||
elif mirror == "" or mirror == "R0":
|
||||
pass
|
||||
else:
|
||||
debug.error("Invalid mirroring: {}".format(mirror), -1)
|
||||
|
||||
if rotate == 90:
|
||||
if rotate == 0:
|
||||
pass
|
||||
elif rotate == 90:
|
||||
ll = ll.rotate_scale(-1, 1)
|
||||
ur = ur.rotate_scale(-1, 1)
|
||||
elif rotate == 180:
|
||||
|
|
@ -96,6 +102,8 @@ class geometry:
|
|||
elif rotate == 270:
|
||||
ll = ll.rotate_scale(1, -1)
|
||||
ur = ur.rotate_scale(1, -1)
|
||||
else:
|
||||
debug.error("Invalid rotation: {}".format(rotate), -1)
|
||||
|
||||
self.boundary = [offset + ll, offset + ur]
|
||||
self.normalize()
|
||||
|
|
@ -139,6 +147,10 @@ class geometry:
|
|||
def cy(self):
|
||||
""" Return the center y """
|
||||
return 0.5 * (self.boundary[0].y + self.boundary[1].y)
|
||||
|
||||
def center(self):
|
||||
""" Return the center coordinate """
|
||||
return vector(self.cx(), self.cy())
|
||||
|
||||
|
||||
class instance(geometry):
|
||||
|
|
|
|||
|
|
@ -244,25 +244,27 @@ class layout():
|
|||
height))
|
||||
return self.objs[-1]
|
||||
|
||||
def add_segment_center(self, layer, start, end):
|
||||
def add_segment_center(self, layer, start, end, width=None):
|
||||
"""
|
||||
Add a min-width rectanglular segment using center
|
||||
line on the start to end point
|
||||
"""
|
||||
minwidth_layer = drc["minwidth_{}".format(layer)]
|
||||
if not width:
|
||||
width = drc["minwidth_{}".format(layer)]
|
||||
|
||||
if start.x != end.x and start.y != end.y:
|
||||
debug.error("Nonrectilinear center rect!", -1)
|
||||
elif start.x != end.x:
|
||||
offset = vector(0, 0.5 * minwidth_layer)
|
||||
offset = vector(0, 0.5 * width)
|
||||
return self.add_rect(layer,
|
||||
start - offset,
|
||||
end.x - start.x,
|
||||
minwidth_layer)
|
||||
width)
|
||||
else:
|
||||
offset = vector(0.5 * minwidth_layer, 0)
|
||||
offset = vector(0.5 * width, 0)
|
||||
return self.add_rect(layer,
|
||||
start - offset,
|
||||
minwidth_layer,
|
||||
width,
|
||||
end.y - start.y)
|
||||
|
||||
def get_pin(self, text):
|
||||
|
|
@ -322,7 +324,7 @@ class layout():
|
|||
for pin_name in self.pin_map.keys():
|
||||
self.copy_layout_pin(instance, pin_name, prefix + pin_name)
|
||||
|
||||
def add_layout_pin_segment_center(self, text, layer, start, end):
|
||||
def add_layout_pin_segment_center(self, text, layer, start, end, width=None):
|
||||
"""
|
||||
Creates a path like pin with center-line convention
|
||||
"""
|
||||
|
|
@ -331,27 +333,27 @@ class layout():
|
|||
self.gds_write(file_name)
|
||||
debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1)
|
||||
|
||||
minwidth_layer = drc["minwidth_{}".format(layer)]
|
||||
if not width:
|
||||
layer_width = drc["minwidth_{}".format(layer)]
|
||||
else:
|
||||
layer_width = width
|
||||
|
||||
# one of these will be zero
|
||||
width = max(start.x, end.x) - min(start.x, end.x)
|
||||
height = max(start.y, end.y) - min(start.y, end.y)
|
||||
bbox_width = max(start.x, end.x) - min(start.x, end.x)
|
||||
bbox_height = max(start.y, end.y) - min(start.y, end.y)
|
||||
ll_offset = vector(min(start.x, end.x), min(start.y, end.y))
|
||||
|
||||
# Shift it down 1/2 a width in the 0 dimension
|
||||
if height == 0:
|
||||
ll_offset -= vector(0, 0.5 * minwidth_layer)
|
||||
if width == 0:
|
||||
ll_offset -= vector(0.5 * minwidth_layer, 0)
|
||||
# This makes sure it is long enough, but also it is not 0 width!
|
||||
height = max(minwidth_layer, height)
|
||||
width = max(minwidth_layer, width)
|
||||
if bbox_height == 0:
|
||||
ll_offset -= vector(0, 0.5 * layer_width)
|
||||
if bbox_width == 0:
|
||||
ll_offset -= vector(0.5 * layer_width, 0)
|
||||
|
||||
return self.add_layout_pin(text,
|
||||
layer,
|
||||
ll_offset,
|
||||
width,
|
||||
height)
|
||||
return self.add_layout_pin(text=text,
|
||||
layer=layer,
|
||||
offset=ll_offset,
|
||||
width=bbox_width,
|
||||
height=bbox_height)
|
||||
|
||||
def add_layout_pin_rect_center(self, text, layer, offset, width=None, height=None):
|
||||
""" Creates a path like pin with center-line convention """
|
||||
|
|
@ -692,6 +694,8 @@ class layout():
|
|||
boundary_layer = "stdc"
|
||||
boundary = [self.find_lowest_coords(),
|
||||
self.find_highest_coords()]
|
||||
debug.check(boundary[0] and boundary[1], "No shapes to make a boundary.")
|
||||
|
||||
height = boundary[1][1] - boundary[0][1]
|
||||
width = boundary[1][0] - boundary[0][0]
|
||||
(layer_number, layer_purpose) = techlayer[boundary_layer]
|
||||
|
|
@ -1306,16 +1310,16 @@ class layout():
|
|||
which vias are needed.
|
||||
"""
|
||||
|
||||
via = self.add_via_stack_center(from_layer=start_layer,
|
||||
to_layer=self.pwr_grid_layer,
|
||||
size=size,
|
||||
offset=loc,
|
||||
directions=directions)
|
||||
if start_layer == self.pwr_grid_layer:
|
||||
self.add_layout_pin_rect_center(text=name,
|
||||
layer=self.pwr_grid_layer,
|
||||
offset=loc)
|
||||
else:
|
||||
via = self.add_via_stack_center(from_layer=start_layer,
|
||||
to_layer=self.pwr_grid_layer,
|
||||
size=size,
|
||||
offset=loc,
|
||||
directions=directions)
|
||||
# Hack for min area
|
||||
if OPTS.tech_name == "s8":
|
||||
width = round_to_grid(sqrt(drc["minarea_m3"]))
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@ class spice():
|
|||
# parses line into ports and remove subckt
|
||||
self.pins = subckt_line.split(" ")[2:]
|
||||
else:
|
||||
debug.info(4, "no spfile {0}".format(self.sp_file))
|
||||
self.spice = []
|
||||
|
||||
# We don't define self.lvs and will use self.spice if dynamically created
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#
|
||||
import debug
|
||||
import utils
|
||||
from tech import GDS, layer, parameter
|
||||
from tech import GDS, layer
|
||||
from tech import cell_properties as props
|
||||
import bitcell_base
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class dff(design.design):
|
|||
#Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width.
|
||||
return parameter["dff_clk_cin"]
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
||||
self.add_graph_edges(graph, port_nets)
|
||||
self.add_graph_edges(graph, port_nets)
|
||||
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
# 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 design
|
||||
from tech import GDS, layer, spice, parameter
|
||||
import logical_effort
|
||||
import utils
|
||||
import debug
|
||||
|
||||
|
||||
class inv_dec(design.design):
|
||||
"""
|
||||
INV for address decoders.
|
||||
"""
|
||||
|
||||
pin_names = ["A", "Z", "vdd", "gnd"]
|
||||
type_list = ["INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||
|
||||
(width, height) = utils.get_libcell_size("inv_dec",
|
||||
GDS["unit"],
|
||||
layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "inv_dec", GDS["unit"])
|
||||
|
||||
def __init__(self, name="inv_dec", height=None):
|
||||
design.design.__init__(self, name)
|
||||
|
||||
self.width = inv_dec.width
|
||||
self.height = inv_dec.height
|
||||
self.pin_map = inv_dec.pin_map
|
||||
self.add_pin_types(self.type_list)
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Returns dynamic and leakage power. Results in nW"""
|
||||
c_eff = self.calculate_effective_capacitance(load)
|
||||
freq = spice["default_event_frequency"]
|
||||
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
||||
power_leak = spice["inv_leakage"]
|
||||
|
||||
total_power = self.return_power(power_dyn, power_leak)
|
||||
return total_power
|
||||
|
||||
def calculate_effective_capacitance(self, load):
|
||||
"""Computes effective capacitance. Results in fF"""
|
||||
c_load = load
|
||||
# In fF
|
||||
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
|
||||
|
||||
return transition_prob * (c_load + c_para)
|
||||
|
||||
def input_load(self):
|
||||
"""
|
||||
Return the capacitance of the gate connection in generic capacitive
|
||||
units relative to the minimum width of a transistor
|
||||
"""
|
||||
return self.nmos_size + self.pmos_size
|
||||
|
||||
def get_stage_effort(self, cout, inp_is_rise=True):
|
||||
"""
|
||||
Returns an object representing the parameters for delay in tau units.
|
||||
Optional is_rise refers to the input direction rise/fall.
|
||||
Input inverted by this stage.
|
||||
"""
|
||||
parasitic_delay = 1
|
||||
return logical_effort.logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
Adds edges based on inputs/outputs.
|
||||
Overrides base class function.
|
||||
"""
|
||||
self.add_graph_edges(graph, port_nets)
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
# 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 design
|
||||
from tech import GDS, layer, spice, parameter
|
||||
import logical_effort
|
||||
import utils
|
||||
|
||||
|
||||
class nand2_dec(design.design):
|
||||
"""
|
||||
2-input NAND decoder for address decoders.
|
||||
"""
|
||||
|
||||
pin_names = ["A", "B", "Z", "vdd", "gnd"]
|
||||
type_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||
|
||||
(width, height) = utils.get_libcell_size("nand2_dec",
|
||||
GDS["unit"],
|
||||
layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "nand2_dec", GDS["unit"])
|
||||
|
||||
def __init__(self, name="nand2_dec", height=None):
|
||||
design.design.__init__(self, name)
|
||||
|
||||
self.width = nand2_dec.width
|
||||
self.height = nand2_dec.height
|
||||
self.pin_map = nand2_dec.pin_map
|
||||
self.add_pin_types(self.type_list)
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Returns dynamic and leakage power. Results in nW"""
|
||||
c_eff = self.calculate_effective_capacitance(load)
|
||||
freq = spice["default_event_frequency"]
|
||||
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
||||
power_leak = spice["nand2_leakage"]
|
||||
|
||||
total_power = self.return_power(power_dyn, power_leak)
|
||||
return total_power
|
||||
|
||||
def calculate_effective_capacitance(self, load):
|
||||
"""Computes effective capacitance. Results in fF"""
|
||||
c_load = load
|
||||
# In fF
|
||||
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
|
||||
transition_prob = 0.1875
|
||||
return transition_prob * (c_load + c_para)
|
||||
|
||||
def input_load(self):
|
||||
"""Return the relative input capacitance of a single input"""
|
||||
return self.nmos_size + self.pmos_size
|
||||
|
||||
def get_stage_effort(self, cout, inp_is_rise=True):
|
||||
"""
|
||||
Returns an object representing the parameters for delay in tau units.
|
||||
Optional is_rise refers to the input direction rise/fall.
|
||||
Input inverted by this stage.
|
||||
"""
|
||||
parasitic_delay = 2
|
||||
return logical_effort.logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
Adds edges based on inputs/outputs.
|
||||
Overrides base class function.
|
||||
"""
|
||||
self.add_graph_edges(graph, port_nets)
|
||||
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
# 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 design
|
||||
from tech import GDS, layer, spice, parameter
|
||||
import logical_effort
|
||||
import utils
|
||||
|
||||
|
||||
class nand3_dec(design.design):
|
||||
"""
|
||||
3-input NAND decoder for address decoders.
|
||||
"""
|
||||
|
||||
pin_names = ["A", "B", "C", "Z", "vdd", "gnd"]
|
||||
type_list = ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||
|
||||
(width, height) = utils.get_libcell_size("nand3_dec",
|
||||
GDS["unit"],
|
||||
layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "nand3_dec", GDS["unit"])
|
||||
|
||||
def __init__(self, name="nand3_dec", height=None):
|
||||
design.design.__init__(self, name)
|
||||
|
||||
self.width = nand3_dec.width
|
||||
self.height = nand3_dec.height
|
||||
self.pin_map = nand3_dec.pin_map
|
||||
self.add_pin_types(self.type_list)
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Returns dynamic and leakage power. Results in nW"""
|
||||
c_eff = self.calculate_effective_capacitance(load)
|
||||
freq = spice["default_event_frequency"]
|
||||
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
||||
power_leak = spice["nand3_leakage"]
|
||||
|
||||
total_power = self.return_power(power_dyn, power_leak)
|
||||
return total_power
|
||||
|
||||
def calculate_effective_capacitance(self, load):
|
||||
"""Computes effective capacitance. Results in fF"""
|
||||
c_load = load
|
||||
# In fF
|
||||
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
|
||||
transition_prob = 0.1875
|
||||
return transition_prob * (c_load + c_para)
|
||||
|
||||
def input_load(self):
|
||||
"""Return the relative input capacitance of a single input"""
|
||||
return self.nmos_size + self.pmos_size
|
||||
|
||||
def get_stage_effort(self, cout, inp_is_rise=True):
|
||||
"""
|
||||
Returns an object representing the parameters for delay in tau units.
|
||||
Optional is_rise refers to the input direction rise/fall.
|
||||
Input inverted by this stage.
|
||||
"""
|
||||
parasitic_delay = 2
|
||||
return logical_effort.logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
Adds edges based on inputs/outputs.
|
||||
Overrides base class function.
|
||||
"""
|
||||
self.add_graph_edges(graph, port_nets)
|
||||
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
# 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 design
|
||||
from tech import GDS, layer, spice, parameter
|
||||
import logical_effort
|
||||
import utils
|
||||
|
||||
|
||||
class nand4_dec(design.design):
|
||||
"""
|
||||
2-input NAND decoder for address decoders.
|
||||
"""
|
||||
|
||||
pin_names = ["A", "B", "C", "D", "Z", "vdd", "gnd"]
|
||||
type_list = ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||
|
||||
(width, height) = utils.get_libcell_size("nand4_dec",
|
||||
GDS["unit"],
|
||||
layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "nand4_dec", GDS["unit"])
|
||||
|
||||
def __init__(self, name="nand4_dec", height=None):
|
||||
design.design.__init__(self, name)
|
||||
|
||||
self.width = nand4_dec.width
|
||||
self.height = nand4_dec.height
|
||||
self.pin_map = nand4_dec.pin_map
|
||||
self.add_pin_types(self.type_list)
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Returns dynamic and leakage power. Results in nW"""
|
||||
c_eff = self.calculate_effective_capacitance(load)
|
||||
freq = spice["default_event_frequency"]
|
||||
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
||||
power_leak = spice["nand4_leakage"]
|
||||
|
||||
total_power = self.return_power(power_dyn, power_leak)
|
||||
return total_power
|
||||
|
||||
def calculate_effective_capacitance(self, load):
|
||||
"""Computes effective capacitance. Results in fF"""
|
||||
c_load = load
|
||||
# In fF
|
||||
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
|
||||
transition_prob = 0.1875
|
||||
return transition_prob * (c_load + c_para)
|
||||
|
||||
def input_load(self):
|
||||
"""Return the relative input capacitance of a single input"""
|
||||
return self.nmos_size + self.pmos_size
|
||||
|
||||
def get_stage_effort(self, cout, inp_is_rise=True):
|
||||
"""
|
||||
Returns an object representing the parameters for delay in tau units.
|
||||
Optional is_rise refers to the input direction rise/fall.
|
||||
Input inverted by this stage.
|
||||
"""
|
||||
parasitic_delay = 2
|
||||
return logical_effort.logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
Adds edges based on inputs/outputs.
|
||||
Overrides base class function.
|
||||
"""
|
||||
self.add_graph_edges(graph, port_nets)
|
||||
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
# 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 debug
|
||||
from vector import vector
|
||||
import pgate
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class pand2_dec(pgate.pgate):
|
||||
"""
|
||||
This is an AND with configurable drive strength.
|
||||
"""
|
||||
def __init__(self, name, size=1, height=None, add_wells=True):
|
||||
debug.info(1, "Creating pand2_dec {}".format(name))
|
||||
self.add_comment("size: {}".format(size))
|
||||
|
||||
self.size = size
|
||||
|
||||
pgate.pgate.__init__(self, name, height, add_wells)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.create_insts()
|
||||
|
||||
def create_modules(self):
|
||||
if OPTS.tech_name == "s8":
|
||||
self.nand = factory.create(module_type="nand2_dec")
|
||||
else:
|
||||
self.nand = factory.create(module_type="nand2_dec",
|
||||
height=self.height)
|
||||
|
||||
self.inv = factory.create(module_type="inv_dec",
|
||||
height=self.height,
|
||||
size=self.size)
|
||||
|
||||
self.add_mod(self.nand)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
def create_layout(self):
|
||||
self.width = self.nand.width + self.inv.width
|
||||
|
||||
self.place_insts()
|
||||
self.add_wires()
|
||||
self.add_layout_pins()
|
||||
self.route_supply_rails()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin("A", "INPUT")
|
||||
self.add_pin("B", "INPUT")
|
||||
self.add_pin("Z", "OUTPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def create_insts(self):
|
||||
self.nand_inst = self.add_inst(name="pand2_dec_nand",
|
||||
mod=self.nand)
|
||||
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
|
||||
|
||||
self.inv_inst = self.add_inst(name="pand2_dec_inv",
|
||||
mod=self.inv)
|
||||
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||
|
||||
def place_insts(self):
|
||||
# Add NAND to the right
|
||||
self.nand_inst.place(offset=vector(0, 0))
|
||||
|
||||
# Add INV to the right
|
||||
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
|
||||
|
||||
def route_supply_rails(self):
|
||||
""" Add vdd/gnd rails to the top, (middle), and bottom. """
|
||||
if OPTS.tech_name == "s8":
|
||||
for name in ["vdd", "gnd"]:
|
||||
for inst in [self.nand_inst, self.inv_inst]:
|
||||
self.copy_layout_pin(inst, name)
|
||||
else:
|
||||
self.add_layout_pin_rect_center(text="gnd",
|
||||
layer=self.route_layer,
|
||||
offset=vector(0.5 * self.width, 0),
|
||||
width=self.width)
|
||||
self.add_layout_pin_rect_center(text="vdd",
|
||||
layer=self.route_layer,
|
||||
offset=vector(0.5 * self.width, self.height),
|
||||
width=self.width)
|
||||
|
||||
def add_wires(self):
|
||||
# nand Z to inv A
|
||||
z1_pin = self.nand_inst.get_pin("Z")
|
||||
a2_pin = self.inv_inst.get_pin("A")
|
||||
if OPTS.tech_name == "s8":
|
||||
mid1_point = vector(a2_pin.cx(), z1_pin.cy())
|
||||
else:
|
||||
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
|
||||
self.add_path(self.route_layer,
|
||||
[z1_pin.center(), mid1_point, a2_pin.center()])
|
||||
|
||||
def add_layout_pins(self):
|
||||
pin = self.inv_inst.get_pin("Z")
|
||||
self.add_layout_pin_rect_center(text="Z",
|
||||
layer=pin.layer,
|
||||
offset=pin.center(),
|
||||
width=pin.width(),
|
||||
height=pin.height())
|
||||
|
||||
for pin_name in ["A", "B"]:
|
||||
pin = self.nand_inst.get_pin(pin_name)
|
||||
self.add_layout_pin_rect_center(text=pin_name,
|
||||
layer=pin.layer,
|
||||
offset=pin.center(),
|
||||
width=pin.width(),
|
||||
height=pin.height())
|
||||
|
||||
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||
"""Get the stage efforts of the A or B -> Z path"""
|
||||
stage_effort_list = []
|
||||
stage1_cout = self.inv.get_cin()
|
||||
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
||||
stage_effort_list.append(stage1)
|
||||
last_stage_is_rise = stage1.is_rise
|
||||
|
||||
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
|
||||
stage_effort_list.append(stage2)
|
||||
|
||||
return stage_effort_list
|
||||
|
||||
def get_cin(self):
|
||||
"""Return the relative input capacitance of a single input"""
|
||||
return self.nand.get_cin()
|
||||
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
# 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 debug
|
||||
from vector import vector
|
||||
import pgate
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class pand3_dec(pgate.pgate):
|
||||
"""
|
||||
This is an AND with configurable drive strength.
|
||||
"""
|
||||
def __init__(self, name, size=1, height=None, add_wells=True):
|
||||
debug.info(1, "Creating pand3_dec {}".format(name))
|
||||
self.add_comment("size: {}".format(size))
|
||||
|
||||
self.size = size
|
||||
|
||||
pgate.pgate.__init__(self, name, height, add_wells)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.create_insts()
|
||||
|
||||
def create_modules(self):
|
||||
if OPTS.tech_name == "s8":
|
||||
self.nand = factory.create(module_type="nand3_dec")
|
||||
else:
|
||||
self.nand = factory.create(module_type="nand3_dec",
|
||||
height=self.height)
|
||||
|
||||
self.inv = factory.create(module_type="inv_dec",
|
||||
height=self.height,
|
||||
size=self.size)
|
||||
|
||||
self.add_mod(self.nand)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
def create_layout(self):
|
||||
self.width = self.nand.width + self.inv.width
|
||||
|
||||
self.place_insts()
|
||||
self.add_wires()
|
||||
self.add_layout_pins()
|
||||
self.route_supply_rails()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin("A", "INPUT")
|
||||
self.add_pin("B", "INPUT")
|
||||
self.add_pin("C", "INPUT")
|
||||
self.add_pin("Z", "OUTPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def create_insts(self):
|
||||
self.nand_inst = self.add_inst(name="pand3_dec_nand",
|
||||
mod=self.nand)
|
||||
self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"])
|
||||
|
||||
self.inv_inst = self.add_inst(name="pand3_dec_inv",
|
||||
mod=self.inv)
|
||||
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||
|
||||
def place_insts(self):
|
||||
# Add NAND to the right
|
||||
self.nand_inst.place(offset=vector(0, 0))
|
||||
|
||||
# Add INV to the right
|
||||
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
|
||||
|
||||
def route_supply_rails(self):
|
||||
""" Add vdd/gnd rails to the top, (middle), and bottom. """
|
||||
if OPTS.tech_name == "s8":
|
||||
for name in ["vdd", "gnd"]:
|
||||
for inst in [self.nand_inst, self.inv_inst]:
|
||||
self.copy_layout_pin(inst, name)
|
||||
else:
|
||||
self.add_layout_pin_rect_center(text="gnd",
|
||||
layer=self.route_layer,
|
||||
offset=vector(0.5 * self.width, 0),
|
||||
width=self.width)
|
||||
self.add_layout_pin_rect_center(text="vdd",
|
||||
layer=self.route_layer,
|
||||
offset=vector(0.5 * self.width, self.height),
|
||||
width=self.width)
|
||||
|
||||
def add_wires(self):
|
||||
# nand Z to inv A
|
||||
z1_pin = self.nand_inst.get_pin("Z")
|
||||
a2_pin = self.inv_inst.get_pin("A")
|
||||
if OPTS.tech_name == "s8":
|
||||
mid1_point = vector(a2_pin.cx(), z1_pin.cy())
|
||||
else:
|
||||
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
|
||||
self.add_path(self.route_layer,
|
||||
[z1_pin.center(), mid1_point, a2_pin.center()])
|
||||
|
||||
def add_layout_pins(self):
|
||||
pin = self.inv_inst.get_pin("Z")
|
||||
self.add_layout_pin_rect_center(text="Z",
|
||||
layer=pin.layer,
|
||||
offset=pin.center(),
|
||||
width=pin.width(),
|
||||
height=pin.height())
|
||||
|
||||
for pin_name in ["A", "B", "C"]:
|
||||
pin = self.nand_inst.get_pin(pin_name)
|
||||
self.add_layout_pin_rect_center(text=pin_name,
|
||||
layer=pin.layer,
|
||||
offset=pin.center(),
|
||||
width=pin.width(),
|
||||
height=pin.height())
|
||||
|
||||
def analytical_delay(self, corner, slew, load=0.0):
|
||||
""" Calculate the analytical delay of DFF-> INV -> INV """
|
||||
nand_delay = self.nand.analytical_delay(corner,
|
||||
slew=slew,
|
||||
load=self.inv.input_load())
|
||||
inv_delay = self.inv.analytical_delay(corner,
|
||||
slew=nand_delay.slew,
|
||||
load=load)
|
||||
return nand_delay + inv_delay
|
||||
|
||||
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||
"""Get the stage efforts of the A or B -> Z path"""
|
||||
stage_effort_list = []
|
||||
stage1_cout = self.inv.get_cin()
|
||||
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
||||
stage_effort_list.append(stage1)
|
||||
last_stage_is_rise = stage1.is_rise
|
||||
|
||||
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
|
||||
stage_effort_list.append(stage2)
|
||||
|
||||
return stage_effort_list
|
||||
|
||||
def get_cin(self):
|
||||
"""Return the relative input capacitance of a single input"""
|
||||
return self.nand.get_cin()
|
||||
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
# 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 debug
|
||||
from vector import vector
|
||||
import pgate
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class pand4_dec(pgate.pgate):
|
||||
"""
|
||||
This is an AND with configurable drive strength.
|
||||
"""
|
||||
def __init__(self, name, size=1, height=None, add_wells=True):
|
||||
debug.info(1, "Creating pand4_dec {}".format(name))
|
||||
self.add_comment("size: {}".format(size))
|
||||
|
||||
self.size = size
|
||||
|
||||
pgate.pgate.__init__(self, name, height, add_wells)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.create_insts()
|
||||
|
||||
def create_modules(self):
|
||||
if OPTS.tech_name == "s8":
|
||||
self.nand = factory.create(module_type="nand4_dec")
|
||||
else:
|
||||
self.nand = factory.create(module_type="nand4_dec",
|
||||
height=self.height)
|
||||
|
||||
self.inv = factory.create(module_type="inv_dec",
|
||||
size=self.size)
|
||||
|
||||
self.add_mod(self.nand)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
def create_layout(self):
|
||||
self.width = self.nand.width + self.inv.width
|
||||
|
||||
self.place_insts()
|
||||
self.add_wires()
|
||||
self.add_layout_pins()
|
||||
self.route_supply_rails()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin("A", "INPUT")
|
||||
self.add_pin("B", "INPUT")
|
||||
self.add_pin("C", "INPUT")
|
||||
self.add_pin("D", "INPUT")
|
||||
self.add_pin("Z", "OUTPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def create_insts(self):
|
||||
self.nand_inst = self.add_inst(name="pand4_dec_nand",
|
||||
mod=self.nand)
|
||||
self.connect_inst(["A", "B", "C", "D", "zb_int", "vdd", "gnd"])
|
||||
|
||||
self.inv_inst = self.add_inst(name="pand4_dec_inv",
|
||||
mod=self.inv)
|
||||
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||
|
||||
def place_insts(self):
|
||||
# Add NAND to the right
|
||||
self.nand_inst.place(offset=vector(0, 0))
|
||||
|
||||
# Add INV to the right
|
||||
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
|
||||
|
||||
def route_supply_rails(self):
|
||||
""" Add vdd/gnd rails to the top, (middle), and bottom. """
|
||||
if OPTS.tech_name == "s8":
|
||||
for name in ["vdd", "gnd"]:
|
||||
for inst in [self.nand_inst, self.inv_inst]:
|
||||
self.copy_layout_pin(inst, name)
|
||||
else:
|
||||
self.add_layout_pin_rect_center(text="gnd",
|
||||
layer=self.route_layer,
|
||||
offset=vector(0.5 * self.width, 0),
|
||||
width=self.width)
|
||||
self.add_layout_pin_rect_center(text="vdd",
|
||||
layer=self.route_layer,
|
||||
offset=vector(0.5 * self.width, self.height),
|
||||
width=self.width)
|
||||
|
||||
def add_wires(self):
|
||||
# nand Z to inv A
|
||||
z1_pin = self.nand_inst.get_pin("Z")
|
||||
a2_pin = self.inv_inst.get_pin("A")
|
||||
if OPTS.tech_name == "s8":
|
||||
mid1_point = vector(a2_pin.cx(), z1_pin.cy())
|
||||
else:
|
||||
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
|
||||
self.add_path(self.route_layer,
|
||||
[z1_pin.center(), mid1_point, a2_pin.center()])
|
||||
|
||||
def add_layout_pins(self):
|
||||
pin = self.inv_inst.get_pin("Z")
|
||||
self.add_layout_pin_rect_center(text="Z",
|
||||
layer=pin.layer,
|
||||
offset=pin.center(),
|
||||
width=pin.width(),
|
||||
height=pin.height())
|
||||
|
||||
for pin_name in ["A", "B", "C"]:
|
||||
pin = self.nand_inst.get_pin(pin_name)
|
||||
self.add_layout_pin_rect_center(text=pin_name,
|
||||
layer=pin.layer,
|
||||
offset=pin.center(),
|
||||
width=pin.width(),
|
||||
height=pin.height())
|
||||
|
||||
def analytical_delay(self, corner, slew, load=0.0):
|
||||
""" Calculate the analytical delay of DFF-> INV -> INV """
|
||||
nand_delay = self.nand.analytical_delay(corner,
|
||||
slew=slew,
|
||||
load=self.inv.input_load())
|
||||
inv_delay = self.inv.analytical_delay(corner,
|
||||
slew=nand_delay.slew,
|
||||
load=load)
|
||||
return nand_delay + inv_delay
|
||||
|
||||
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||
"""Get the stage efforts of the A or B -> Z path"""
|
||||
stage_effort_list = []
|
||||
stage1_cout = self.inv.get_cin()
|
||||
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
||||
stage_effort_list.append(stage1)
|
||||
last_stage_is_rise = stage1.is_rise
|
||||
|
||||
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
|
||||
stage_effort_list.append(stage2)
|
||||
|
||||
return stage_effort_list
|
||||
|
||||
def get_cin(self):
|
||||
"""Return the relative input capacitance of a single input"""
|
||||
return self.nand.get_cin()
|
||||
|
||||
|
|
@ -12,7 +12,6 @@ from sram_factory import factory
|
|||
from vector import vector
|
||||
from globals import OPTS
|
||||
from errors import drc_error
|
||||
from tech import cell_properties, layer
|
||||
|
||||
|
||||
class hierarchical_decoder(design.design):
|
||||
|
|
@ -28,12 +27,8 @@ class hierarchical_decoder(design.design):
|
|||
self.pre3x8_inst = []
|
||||
|
||||
b = factory.create(module_type="bitcell")
|
||||
try:
|
||||
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
|
||||
except AttributeError:
|
||||
self.cell_multiple = 1
|
||||
self.cell_height = self.cell_multiple * b.height
|
||||
|
||||
self.cell_height = b.height
|
||||
|
||||
self.num_outputs = num_outputs
|
||||
self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
|
||||
(self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
||||
|
|
@ -41,41 +36,6 @@ class hierarchical_decoder(design.design):
|
|||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def find_decoder_height(self):
|
||||
"""
|
||||
Dead code. This would dynamically determine the bitcell multiple,
|
||||
but I just decided to hard code it in the tech file if it is not 1
|
||||
because a DRC tool would be required even to run in front-end mode.
|
||||
"""
|
||||
b = factory.create(module_type="bitcell")
|
||||
|
||||
# Old behavior
|
||||
if OPTS.netlist_only:
|
||||
return (b.height, 1)
|
||||
|
||||
# Search for the smallest multiple that works
|
||||
cell_multiple = 1
|
||||
while cell_multiple < 5:
|
||||
cell_height = cell_multiple * b.height
|
||||
# debug.info(2,"Trying mult = {0} height={1}".format(cell_multiple, cell_height))
|
||||
try:
|
||||
and3 = factory.create(module_type="pand3",
|
||||
height=cell_height)
|
||||
except drc_error:
|
||||
# debug.info(1, "Incrementing decoder height by 1 bitcell height {}".format(b.height))
|
||||
pass
|
||||
else:
|
||||
(drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True)
|
||||
total_errors = drc_errors + lvs_errors
|
||||
if total_errors == 0:
|
||||
debug.info(1, "Decoder height is multiple of {} bitcells.".format(cell_multiple))
|
||||
return (cell_height, cell_multiple)
|
||||
|
||||
cell_multiple += 1
|
||||
|
||||
else:
|
||||
debug.error("Couldn't find a valid decoder height multiple.", -1)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
|
|
@ -97,15 +57,22 @@ class hierarchical_decoder(design.design):
|
|||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
self.inv = factory.create(module_type="pinv",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.inv)
|
||||
self.and2 = factory.create(module_type="pand2",
|
||||
height=self.cell_height)
|
||||
if OPTS.tech_name == "s8":
|
||||
self.and2 = factory.create(module_type="pand2_dec")
|
||||
else:
|
||||
self.and2 = factory.create(module_type="pand2_dec",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.and2)
|
||||
self.and3 = factory.create(module_type="pand3",
|
||||
height=self.cell_height)
|
||||
if OPTS.tech_name == "s8":
|
||||
self.and3 = factory.create(module_type="pand3_dec")
|
||||
else:
|
||||
self.and3 = factory.create(module_type="pand3_dec",
|
||||
height=self.cell_height)
|
||||
|
||||
self.add_mod(self.and3)
|
||||
# TBD
|
||||
# self.and4 = factory.create(module_type="pand4_dec")
|
||||
# self.add_mod(self.and4)
|
||||
|
||||
self.add_decoders()
|
||||
|
||||
|
|
@ -176,56 +143,63 @@ class hierarchical_decoder(design.design):
|
|||
-1)
|
||||
|
||||
# Calculates height and width of pre-decoder,
|
||||
if self.no_of_pre3x8 > 0:
|
||||
# FIXME: Update with 4x16
|
||||
if self.no_of_pre3x8 > 0 and self.no_of_pre2x4 > 0:
|
||||
self.predecoder_width = max(self.pre3_8.width, self.pre2_4.width)
|
||||
elif self.no_of_pre3x8 > 0:
|
||||
self.predecoder_width = self.pre3_8.width
|
||||
else:
|
||||
self.predecoder_width = self.pre2_4.width
|
||||
|
||||
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8
|
||||
|
||||
# We may have more than one bitcell per decoder row
|
||||
self.num_rows = math.ceil(self.num_outputs / self.cell_multiple)
|
||||
# We will place this many final decoders per row
|
||||
self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows)
|
||||
# We will need to use M2 and M3 in the vertical bus if we have multiple decoders per row
|
||||
if self.decoders_per_row == 1:
|
||||
self.decoder_bus_pitch = self.m2_pitch
|
||||
elif self.decoders_per_row == 2:
|
||||
self.decoder_bus_pitch = self.m3_pitch
|
||||
# How much space between each predecoder
|
||||
self.predecoder_spacing = 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
|
||||
|
||||
# Inputs to cells are on input layer
|
||||
# Outputs from cells are on output layer
|
||||
if OPTS.tech_name == "s8":
|
||||
self.bus_layer = "m1"
|
||||
self.bus_directions = "nonpref"
|
||||
self.bus_pitch = self.m1_pitch
|
||||
self.bus_space = self.m2_space
|
||||
self.input_layer = "m2"
|
||||
self.output_layer = "li"
|
||||
self.output_layer_pitch = self.li_pitch
|
||||
else:
|
||||
debug.error("Insufficient layers for multi-bit height decoder.", -1)
|
||||
self.bus_layer = "m2"
|
||||
self.bus_directions = "pref"
|
||||
self.bus_pitch = self.m2_pitch
|
||||
self.bus_space = self.m2_space
|
||||
# These two layers being the same requires a special jog
|
||||
# to ensure to conflicts with the output layers
|
||||
self.input_layer = "m1"
|
||||
self.output_layer = "m3"
|
||||
self.output_layer_pitch = self.m3_pitch
|
||||
|
||||
# Two extra pitches between modules on left and right
|
||||
self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch
|
||||
self.row_decoder_height = self.and2.height * self.num_outputs
|
||||
# Extra bus space for supply contacts
|
||||
self.input_routing_width = self.num_inputs * self.bus_pitch + self.bus_space
|
||||
|
||||
# Calculates height and width of row-decoder
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
nand_width = self.and2.width
|
||||
nand_inputs = 2
|
||||
else:
|
||||
nand_width = self.and3.width
|
||||
nand_inputs = 3
|
||||
self.internal_routing_width = self.decoder_bus_pitch * (self.total_number_of_predecoder_outputs + 1)
|
||||
self.row_decoder_height = self.inv.height * self.num_rows
|
||||
|
||||
decoder_input_wire_height = self.decoders_per_row * nand_inputs * self.m2_pitch
|
||||
# print(self.decoders_per_row, nand_inputs)
|
||||
# print(decoder_input_wire_height, self.cell_height)
|
||||
if decoder_input_wire_height > self.cell_height:
|
||||
debug.warning("Cannot fit multi-bit decoder routes per row.")
|
||||
# debug.check(decoder_input_wire_height < self.cell_height, "Cannot fit multi-bit decoder routes per row.")
|
||||
|
||||
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
|
||||
|
||||
# Calculates height and width of hierarchical decoder
|
||||
# Add extra pitch for good measure
|
||||
self.height = max(self.predecoder_height, self.row_decoder_height) + self.m2_pitch
|
||||
self.width = self.input_routing_width + self.predecoder_width \
|
||||
self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
self.nand_width = self.and2.width
|
||||
else:
|
||||
self.nand_width = self.and3.width
|
||||
|
||||
self.width = self.input_routing_width \
|
||||
+ self.predecoder_width \
|
||||
+ self.internal_routing_width \
|
||||
+ self.decoders_per_row * nand_width + self.inv.width
|
||||
|
||||
+ self.nand_width \
|
||||
+ self.m1_space
|
||||
|
||||
def route_inputs(self):
|
||||
""" Create input bus for the predecoders """
|
||||
# inputs should be as high as the decoders
|
||||
input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height
|
||||
|
||||
# Find the left-most predecoder
|
||||
min_x = 0
|
||||
if self.no_of_pre2x4 > 0:
|
||||
|
|
@ -235,10 +209,10 @@ class hierarchical_decoder(design.design):
|
|||
input_offset=vector(min_x - self.input_routing_width, 0)
|
||||
|
||||
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
|
||||
self.input_bus = self.create_vertical_pin_bus(layer="m2",
|
||||
self.input_bus = self.create_vertical_pin_bus(layer=self.bus_layer,
|
||||
offset=input_offset,
|
||||
names=input_bus_names,
|
||||
length=input_height)
|
||||
length=self.predecoder_height)
|
||||
|
||||
self.route_input_to_predecodes()
|
||||
|
||||
|
|
@ -253,9 +227,7 @@ class hierarchical_decoder(design.design):
|
|||
in_name = "in_{}".format(i)
|
||||
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
|
||||
|
||||
# To prevent conflicts, we will offset each input connect so
|
||||
# that it aligns with the vdd/gnd rails
|
||||
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * (self.inv.height + self.m1_pitch))
|
||||
decoder_offset = decoder_pin.center()
|
||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||
|
||||
self.route_input_bus(decoder_offset, input_offset)
|
||||
|
|
@ -269,9 +241,7 @@ class hierarchical_decoder(design.design):
|
|||
in_name = "in_{}".format(i)
|
||||
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
|
||||
|
||||
# To prevent conflicts, we will offset each input connect so
|
||||
# that it aligns with the vdd/gnd rails
|
||||
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * (self.inv.height + self.m1_pitch))
|
||||
decoder_offset = decoder_pin.center()
|
||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||
|
||||
self.route_input_bus(decoder_offset, input_offset)
|
||||
|
|
@ -282,13 +252,14 @@ class hierarchical_decoder(design.design):
|
|||
vertical M2 coordinate to the predecode inputs
|
||||
"""
|
||||
|
||||
self.add_via_stack_center(from_layer="m2",
|
||||
to_layer="m3",
|
||||
self.add_via_stack_center(from_layer=self.bus_layer,
|
||||
to_layer=self.input_layer,
|
||||
offset=input_offset)
|
||||
self.add_via_stack_center(from_layer="m2",
|
||||
to_layer="m3",
|
||||
offset=output_offset)
|
||||
self.add_path("m3", [input_offset, output_offset])
|
||||
self.add_via_stack_center(from_layer=self.bus_layer,
|
||||
to_layer=self.input_layer,
|
||||
offset=output_offset,
|
||||
directions=self.bus_directions)
|
||||
self.add_path(self.input_layer, [input_offset, output_offset])
|
||||
|
||||
def add_pins(self):
|
||||
""" Add the module pins """
|
||||
|
|
@ -363,19 +334,19 @@ class hierarchical_decoder(design.design):
|
|||
if (self.num_inputs == 2):
|
||||
base = vector(-self.pre2_4.width, 0)
|
||||
else:
|
||||
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
|
||||
base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing))
|
||||
|
||||
self.pre2x4_inst[num].place(base - vector(2 * self.m2_pitch, 0))
|
||||
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 """
|
||||
if (self.num_inputs == 3):
|
||||
offset = vector(-self.pre_3_8.width, 0)
|
||||
else:
|
||||
height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height
|
||||
height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) + num * (self.pre3_8.height + self.predecoder_spacing)
|
||||
offset = vector(-self.pre3_8.width, height)
|
||||
|
||||
self.pre3x8_inst[num].place(offset - vector(2 * self.m2_pitch, 0))
|
||||
self.pre3x8_inst[num].place(offset)
|
||||
|
||||
def create_row_decoder(self):
|
||||
""" Create the row-decoder by placing AND2/AND3 and Inverters
|
||||
|
|
@ -431,7 +402,6 @@ class hierarchical_decoder(design.design):
|
|||
if (self.num_inputs >= 4):
|
||||
self.place_decoder_and_array()
|
||||
|
||||
|
||||
def place_decoder_and_array(self):
|
||||
"""
|
||||
Add a column of AND gates for final decode.
|
||||
|
|
@ -452,9 +422,7 @@ class hierarchical_decoder(design.design):
|
|||
Add a column of AND gates for the decoder above the predecoders.
|
||||
"""
|
||||
|
||||
for inst_index in range(self.num_outputs):
|
||||
row = math.floor(inst_index / self.decoders_per_row)
|
||||
dec = inst_index % self.decoders_per_row
|
||||
for row in range(self.num_outputs):
|
||||
if ((row % 2) == 0):
|
||||
y_off = and_mod.height * row
|
||||
mirror = "R0"
|
||||
|
|
@ -462,32 +430,16 @@ class hierarchical_decoder(design.design):
|
|||
y_off = and_mod.height * (row + 1)
|
||||
mirror = "MX"
|
||||
|
||||
x_off = self.internal_routing_width + dec * and_mod.width
|
||||
self.and_inst[inst_index].place(offset=vector(x_off, y_off),
|
||||
mirror=mirror)
|
||||
x_off = self.internal_routing_width
|
||||
self.and_inst[row].place(offset=vector(x_off, y_off),
|
||||
mirror=mirror)
|
||||
|
||||
def route_outputs(self):
|
||||
""" Add the pins. """
|
||||
|
||||
max_xoffset = max(x.rx() for x in self.and_inst)
|
||||
|
||||
for output_index in range(self.num_outputs):
|
||||
row_remainder = (output_index % self.decoders_per_row)
|
||||
|
||||
and_inst = self.and_inst[output_index]
|
||||
z_pin = and_inst.get_pin("Z")
|
||||
if row_remainder == 0 and self.decoders_per_row > 1:
|
||||
layer = "m3"
|
||||
self.add_via_stack_center(from_layer=z_pin.layer,
|
||||
to_layer="m3",
|
||||
offset=z_pin.center())
|
||||
else:
|
||||
layer = z_pin.layer
|
||||
|
||||
self.add_layout_pin_segment_center(text="decode_{0}".format(output_index),
|
||||
layer=layer,
|
||||
start=z_pin.center(),
|
||||
end=vector(max_xoffset, z_pin.cy()))
|
||||
for row in range(self.num_outputs):
|
||||
and_inst = self.and_inst[row]
|
||||
self.copy_layout_pin(and_inst, "Z", "decode_{0}".format(row))
|
||||
|
||||
def route_decoder_bus(self):
|
||||
"""
|
||||
|
|
@ -498,9 +450,9 @@ class hierarchical_decoder(design.design):
|
|||
if (self.num_inputs >= 4):
|
||||
# This leaves an offset for the predecoder output jogs
|
||||
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
|
||||
self.predecode_bus = self.create_vertical_pin_bus(layer="m2",
|
||||
pitch=self.decoder_bus_pitch,
|
||||
offset=vector(0, 0),
|
||||
self.predecode_bus = self.create_vertical_pin_bus(layer=self.bus_layer,
|
||||
pitch=self.bus_pitch,
|
||||
offset=vector(self.bus_pitch, 0),
|
||||
names=input_bus_names,
|
||||
length=self.height)
|
||||
|
||||
|
|
@ -518,8 +470,9 @@ class hierarchical_decoder(design.design):
|
|||
predecode_name = "predecode_{}".format(pre_num * 4 + i)
|
||||
out_name = "out_{}".format(i)
|
||||
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
|
||||
x_offset = self.pre2x4_inst[pre_num].rx() + self.m2_pitch
|
||||
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
|
||||
x_offset = self.pre2x4_inst[pre_num].rx() + self.output_layer_pitch
|
||||
y_offset = self.pre2x4_inst[pre_num].by() + i * self.cell_height
|
||||
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
|
||||
|
||||
# FIXME: convert to connect_bus
|
||||
for pre_num in range(self.no_of_pre3x8):
|
||||
|
|
@ -527,8 +480,9 @@ class hierarchical_decoder(design.design):
|
|||
predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
|
||||
out_name = "out_{}".format(i)
|
||||
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
|
||||
x_offset = self.pre3x8_inst[pre_num].rx() + self.m2_pitch
|
||||
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
|
||||
x_offset = self.pre3x8_inst[pre_num].rx() + self.output_layer_pitch
|
||||
y_offset = self.pre3x8_inst[pre_num].by() + i * self.cell_height
|
||||
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
|
||||
|
||||
def route_bus_to_decoder(self):
|
||||
"""
|
||||
|
|
@ -542,12 +496,6 @@ class hierarchical_decoder(design.design):
|
|||
and the 128th AND3 is connected to [3,7,15]
|
||||
"""
|
||||
output_index = 0
|
||||
|
||||
if "li" in layer:
|
||||
self.decoder_layers = [self.m1_stack, self.m2_stack[::-1]]
|
||||
else:
|
||||
self.decoder_layers = [self.m2_stack[::-1]]
|
||||
debug.check(self.decoders_per_row <= len(self.decoder_layers), "Must have more layers for multi-height decoder.")
|
||||
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
for index_B in self.predec_groups[1]:
|
||||
|
|
@ -557,13 +505,11 @@ class hierarchical_decoder(design.design):
|
|||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("A"),
|
||||
output_index,
|
||||
0)
|
||||
output_index)
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("B"),
|
||||
output_index,
|
||||
1)
|
||||
output_index)
|
||||
output_index = output_index + 1
|
||||
|
||||
elif (self.num_inputs > 5):
|
||||
|
|
@ -575,18 +521,15 @@ class hierarchical_decoder(design.design):
|
|||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("A"),
|
||||
output_index,
|
||||
0)
|
||||
output_index)
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("B"),
|
||||
output_index,
|
||||
1)
|
||||
output_index)
|
||||
predecode_name = "predecode_{}".format(index_C)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("C"),
|
||||
output_index,
|
||||
2)
|
||||
output_index)
|
||||
output_index = output_index + 1
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
|
|
@ -594,90 +537,90 @@ class hierarchical_decoder(design.design):
|
|||
Add a pin for each row of vdd/gnd which are
|
||||
must-connects next level up.
|
||||
"""
|
||||
|
||||
if OPTS.tech_name == "s8":
|
||||
for n in ["vdd", "gnd"]:
|
||||
pins = self.and_inst[0].get_pins(n)
|
||||
for pin in pins:
|
||||
self.add_rect(layer=pin.layer,
|
||||
offset=pin.ll() + vector(0, self.bus_space),
|
||||
width=pin.width(),
|
||||
height=self.height - 2 * self.bus_space)
|
||||
|
||||
# The vias will be placed at the right of the cells.
|
||||
xoffset = max(x.rx() for x in self.and_inst)
|
||||
for num in range(0, self.num_outputs):
|
||||
# Only add the power pin for the 1st in each row
|
||||
if num % self.decoders_per_row:
|
||||
continue
|
||||
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
# The nand and inv are the same height rows...
|
||||
supply_pin = self.and_inst[num].get_pin(pin_name)
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_path(supply_pin.layer,
|
||||
[supply_pin.lc(), vector(xoffset, supply_pin.cy())])
|
||||
self.add_power_pin(name=pin_name,
|
||||
loc=pin_pos,
|
||||
start_layer=supply_pin.layer)
|
||||
|
||||
# Copy the pins from the predecoders
|
||||
for pre in self.pre2x4_inst + self.pre3x8_inst:
|
||||
self.copy_layout_pin(pre, "vdd")
|
||||
self.copy_layout_pin(pre, "gnd")
|
||||
# This adds power vias at the top of each cell
|
||||
# (except the last to keep them inside the boundary)
|
||||
for i in self.and_inst[:-1]:
|
||||
pins = i.get_pins(n)
|
||||
for pin in pins:
|
||||
self.add_power_pin(name=n,
|
||||
loc=pin.uc(),
|
||||
start_layer=pin.layer)
|
||||
self.add_power_pin(name=n,
|
||||
loc=pin.uc(),
|
||||
start_layer=pin.layer)
|
||||
|
||||
for i in self.pre2x4_inst + self.pre3x8_inst:
|
||||
self.copy_layout_pin(i, n)
|
||||
else:
|
||||
# The vias will be placed at the right of the cells.
|
||||
xoffset = max(x.rx() for x in self.and_inst) + 0.5 * self.m1_space
|
||||
for row in range(0, self.num_outputs):
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
# The nand and inv are the same height rows...
|
||||
supply_pin = self.and_inst[row].get_pin(pin_name)
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_power_pin(name=pin_name,
|
||||
loc=pin_pos,
|
||||
start_layer=supply_pin.layer)
|
||||
|
||||
# Copy the pins from the predecoders
|
||||
for pre in self.pre2x4_inst + self.pre3x8_inst:
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
self.copy_layout_pin(pre, pin_name)
|
||||
|
||||
def route_predecode_bus_outputs(self, rail_name, pin, output_index, pin_index):
|
||||
def route_predecode_bus_outputs(self, rail_name, pin, row):
|
||||
"""
|
||||
Connect the routing rail to the given metal1 pin
|
||||
using a routing track at the given y_offset
|
||||
"""
|
||||
|
||||
row_index = math.floor(output_index / self.decoders_per_row)
|
||||
row_remainder = (output_index % self.decoders_per_row)
|
||||
row_offset = row_index * self.and_inst[0].height
|
||||
|
||||
pin_pos = pin.center()
|
||||
|
||||
# y_offset is the same for both the M2 and M4 routes so that the rail
|
||||
# contacts align and don't cause problems
|
||||
if pin_index == 0:
|
||||
# Bottom pitch
|
||||
y_offset = row_offset
|
||||
elif pin_index == 1:
|
||||
# One pitch from top
|
||||
y_offset = row_offset + self.and_inst[0].height - self.m3_pitch
|
||||
elif pin_index == 2:
|
||||
# One pitch from bottom
|
||||
y_offset = row_offset + self.m3_pitch
|
||||
else:
|
||||
debug.error("Invalid decoder pitch.")
|
||||
|
||||
rail_pos = vector(self.predecode_bus[rail_name].x, y_offset)
|
||||
mid_pos = vector(pin_pos.x, rail_pos.y)
|
||||
self.add_wire(self.decoder_layers[row_remainder], [rail_pos, mid_pos, pin_pos])
|
||||
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
|
||||
self.add_path(self.input_layer, [rail_pos, pin_pos])
|
||||
|
||||
self.add_via_stack_center(from_layer="m2",
|
||||
to_layer=self.decoder_layers[row_remainder][0],
|
||||
offset=rail_pos)
|
||||
self.add_via_stack_center(from_layer=self.bus_layer,
|
||||
to_layer=self.input_layer,
|
||||
offset=rail_pos,
|
||||
directions=self.bus_directions)
|
||||
|
||||
self.add_via_stack_center(from_layer=pin.layer,
|
||||
to_layer=self.decoder_layers[row_remainder][2],
|
||||
to_layer=self.input_layer,
|
||||
offset=pin_pos,
|
||||
directions=("H", "H"))
|
||||
|
||||
def route_predecode_bus_inputs(self, rail_name, pin, x_offset):
|
||||
def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset):
|
||||
"""
|
||||
Connect the routing rail to the given metal1 pin using a jog
|
||||
to the right of the cell at the given x_offset.
|
||||
"""
|
||||
# This routes the pin up to the rail, basically, to avoid conflicts.
|
||||
# It would be fixed with a channel router.
|
||||
# pin_pos = pin.center()
|
||||
# mid_point1 = vector(x_offset, pin_pos.y)
|
||||
# mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2)
|
||||
# rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y)
|
||||
# self.add_path("m1", [pin_pos, mid_point1, mid_point2, rail_pos])
|
||||
pin_pos = pin.rc()
|
||||
mid_point1 = vector(x_offset, pin_pos.y)
|
||||
mid_point2 = vector(x_offset, y_offset)
|
||||
rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y)
|
||||
self.add_path(self.output_layer, [pin_pos, mid_point1, mid_point2, rail_pos])
|
||||
|
||||
pin_pos = pin.center()
|
||||
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
|
||||
self.add_path("m1", [pin_pos, rail_pos])
|
||||
# pin_pos = pin.center()
|
||||
# rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
|
||||
# self.add_path(self.output_layer, [pin_pos, rail_pos])
|
||||
self.add_via_stack_center(from_layer=pin.layer,
|
||||
to_layer="m1",
|
||||
to_layer=self.output_layer,
|
||||
offset=pin_pos)
|
||||
self.add_via_stack_center(from_layer="m1",
|
||||
to_layer="m2",
|
||||
offset=rail_pos)
|
||||
self.add_via_stack_center(from_layer=self.bus_layer,
|
||||
to_layer=self.output_layer,
|
||||
offset=rail_pos,
|
||||
directions=self.bus_directions)
|
||||
|
||||
def input_load(self):
|
||||
if self.determine_predecodes(self.num_inputs)[1]==0:
|
||||
|
|
|
|||
|
|
@ -8,29 +8,24 @@
|
|||
import debug
|
||||
import design
|
||||
import math
|
||||
import contact
|
||||
from vector import vector
|
||||
from sram_factory import factory
|
||||
from tech import cell_properties
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class hierarchical_predecode(design.design):
|
||||
"""
|
||||
Pre 2x4 and 3x8 decoder shared code.
|
||||
Pre 2x4 and 3x8 and TBD 4x16 decoder shared code.
|
||||
"""
|
||||
def __init__(self, name, input_number, height=None):
|
||||
self.number_of_inputs = input_number
|
||||
|
||||
if not height:
|
||||
b = factory.create(module_type="bitcell")
|
||||
try:
|
||||
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
|
||||
except AttributeError:
|
||||
self.cell_multiple = 1
|
||||
self.cell_height = self.cell_multiple * b.height
|
||||
self.cell_height = b.height
|
||||
else:
|
||||
self.cell_height = height
|
||||
|
||||
|
||||
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
||||
design.design.__init__(self, name)
|
||||
|
||||
|
|
@ -44,34 +39,73 @@ class hierarchical_predecode(design.design):
|
|||
|
||||
def add_modules(self):
|
||||
""" Add the INV and AND gate modules """
|
||||
|
||||
self.inv = factory.create(module_type="pinv",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
self.add_and(self.number_of_inputs)
|
||||
self.add_mod(self.and_mod)
|
||||
|
||||
def add_and(self, inputs):
|
||||
""" Create the NAND for the predecode input stage """
|
||||
if inputs==2:
|
||||
self.and_mod = factory.create(module_type="pand2",
|
||||
# FIXME: Default parms are required for hard cells for now.
|
||||
if self.number_of_inputs == 2:
|
||||
self.and_mod = factory.create(module_type="pand2_dec",
|
||||
height=self.cell_height)
|
||||
elif inputs==3:
|
||||
self.and_mod = factory.create(module_type="pand3",
|
||||
elif self.number_of_inputs == 3:
|
||||
self.and_mod = factory.create(module_type="pand3_dec",
|
||||
height=self.cell_height)
|
||||
elif self.number_of_inputs == 4:
|
||||
self.and_mod = factory.create(module_type="pand4_dec",
|
||||
height=self.cell_height)
|
||||
else:
|
||||
debug.error("Invalid number of predecode inputs: {}".format(inputs), -1)
|
||||
|
||||
debug.error("Invalid number of predecode inputs: {}".format(self.number_of_inputs), -1)
|
||||
self.add_mod(self.and_mod)
|
||||
|
||||
# This uses the pinv_dec parameterized cell
|
||||
self.inv = factory.create(module_type="inv_dec",
|
||||
height=self.cell_height,
|
||||
size=1)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
|
||||
def create_layout(self):
|
||||
""" The general organization is from left to right:
|
||||
1) a set of M2 rails for input signals
|
||||
2) a set of inverters to invert input signals
|
||||
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
|
||||
4) a set of AND gates for inversion
|
||||
"""
|
||||
self.setup_layout_constraints()
|
||||
self.route_rails()
|
||||
self.place_input_inverters()
|
||||
self.place_and_array()
|
||||
self.route()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def setup_layout_constraints(self):
|
||||
|
||||
# Inputs to cells are on input layer
|
||||
# Outputs from cells are on output layer
|
||||
if OPTS.tech_name == "s8":
|
||||
self.bus_layer = "m1"
|
||||
self.bus_directions = None
|
||||
self.bus_pitch = self.m1_pitch
|
||||
self.bus_space = 1.5 * self.m1_space
|
||||
self.input_layer = "li"
|
||||
self.output_layer = "m2"
|
||||
self.output_layer_pitch = self.m2_pitch
|
||||
else:
|
||||
self.bus_layer = "m2"
|
||||
self.bus_directions = None
|
||||
self.bus_pitch = self.m2_pitch
|
||||
self.bus_space = self.m2_space
|
||||
# This requires a special jog to ensure to conflicts with the output layers
|
||||
self.input_layer = "m1"
|
||||
self.output_layer = "m1"
|
||||
self.output_layer_pitch = self.m1_pitch
|
||||
|
||||
self.height = self.number_of_outputs * self.and_mod.height
|
||||
|
||||
# x offset for input inverters
|
||||
self.x_off_inv_1 = self.number_of_inputs * self.m2_pitch + self.m2_space
|
||||
# +1 input for spacing for supply rail contacts
|
||||
self.x_off_inv_1 = (self.number_of_inputs + 1) * self.bus_pitch + self.bus_pitch
|
||||
|
||||
# x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches
|
||||
self.x_off_and = self.x_off_inv_1 + self.inv.width + (2 * self.number_of_inputs + 2) * self.m2_pitch
|
||||
# x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra bus pitches
|
||||
self.x_off_and = self.x_off_inv_1 + self.inv.width + (2 * self.number_of_inputs + 2) * self.bus_pitch
|
||||
|
||||
# x offset to output inverters
|
||||
self.width = self.x_off_and + self.and_mod.width
|
||||
|
|
@ -79,28 +113,30 @@ class hierarchical_predecode(design.design):
|
|||
def route_rails(self):
|
||||
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
|
||||
input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)]
|
||||
offset = vector(0.5 * self.m2_width, self.m3_pitch)
|
||||
self.input_rails = self.create_vertical_pin_bus(layer="m2",
|
||||
offset=offset,
|
||||
names=input_names,
|
||||
length=self.height - 2 * self.m1_pitch)
|
||||
# Offsets for the perimeter spacing to other modules
|
||||
# This uses m3 pitch to leave space for power routes
|
||||
offset = vector(self.bus_pitch, self.bus_pitch)
|
||||
self.input_rails = self.create_vertical_bus(layer=self.bus_layer,
|
||||
offset=offset,
|
||||
names=input_names,
|
||||
length=self.height - 2 * self.bus_pitch)
|
||||
|
||||
invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)]
|
||||
non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)]
|
||||
decode_names = invert_names + non_invert_names
|
||||
offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m2_pitch, self.m3_pitch)
|
||||
self.decode_rails = self.create_vertical_bus(layer="m2",
|
||||
offset = vector(self.x_off_inv_1 + self.inv.width + self.bus_pitch, self.bus_pitch)
|
||||
self.decode_rails = self.create_vertical_bus(layer=self.bus_layer,
|
||||
offset=offset,
|
||||
names=decode_names,
|
||||
length=self.height - 2 * self.m1_pitch)
|
||||
length=self.height - 2 * self.bus_pitch)
|
||||
|
||||
def create_input_inverters(self):
|
||||
""" Create the input inverters to invert input signals for the decode stage. """
|
||||
self.in_inst = []
|
||||
self.inv_inst = []
|
||||
for inv_num in range(self.number_of_inputs):
|
||||
name = "pre_inv_{0}".format(inv_num)
|
||||
self.in_inst.append(self.add_inst(name=name,
|
||||
mod=self.inv))
|
||||
self.inv_inst.append(self.add_inst(name=name,
|
||||
mod=self.inv))
|
||||
self.connect_inst(["in_{0}".format(inv_num),
|
||||
"inbar_{0}".format(inv_num),
|
||||
"vdd", "gnd"])
|
||||
|
|
@ -108,6 +144,7 @@ class hierarchical_predecode(design.design):
|
|||
def place_input_inverters(self):
|
||||
""" Place the input inverters to invert input signals for the decode stage. """
|
||||
for inv_num in range(self.number_of_inputs):
|
||||
|
||||
if (inv_num % 2 == 0):
|
||||
y_off = inv_num * (self.inv.height)
|
||||
mirror = "R0"
|
||||
|
|
@ -115,8 +152,8 @@ class hierarchical_predecode(design.design):
|
|||
y_off = (inv_num + 1) * (self.inv.height)
|
||||
mirror="MX"
|
||||
offset = vector(self.x_off_inv_1, y_off)
|
||||
self.in_inst[inv_num].place(offset=offset,
|
||||
mirror=mirror)
|
||||
self.inv_inst[inv_num].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
def create_and_array(self, connections):
|
||||
""" Create the AND stage for the decodes """
|
||||
|
|
@ -151,21 +188,30 @@ class hierarchical_predecode(design.design):
|
|||
|
||||
def route_inputs_to_rails(self):
|
||||
""" Route the uninverted inputs to the second set of rails """
|
||||
|
||||
top_and_gate = self.and_inst[-1]
|
||||
for num in range(self.number_of_inputs):
|
||||
# route one signal next to each vdd/gnd rail since this is
|
||||
# typically where the p/n devices are and there are no
|
||||
# pins in the and gates.
|
||||
y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m2_via.width + self.m2_space
|
||||
if num == 0:
|
||||
pin = top_and_gate.get_pin("A")
|
||||
elif num == 1:
|
||||
pin = top_and_gate.get_pin("B")
|
||||
elif num == 2:
|
||||
pin = top_and_gate.get_pin("C")
|
||||
elif num == 3:
|
||||
pin = top_and_gate.get_pin("D")
|
||||
else:
|
||||
debug.error("Too many inputs for predecoder.", -1)
|
||||
y_offset = pin.cy()
|
||||
in_pin = "in_{}".format(num)
|
||||
a_pin = "A_{}".format(num)
|
||||
in_pos = vector(self.input_rails[in_pin].x, y_offset)
|
||||
a_pos = vector(self.decode_rails[a_pin].x, y_offset)
|
||||
self.add_path("m1", [in_pos, a_pos])
|
||||
self.add_via_stack_center(from_layer="m1",
|
||||
to_layer="m2",
|
||||
self.add_path(self.input_layer, [in_pos, a_pos])
|
||||
self.add_via_stack_center(from_layer=self.input_layer,
|
||||
to_layer=self.bus_layer,
|
||||
offset=[self.input_rails[in_pin].x, y_offset])
|
||||
self.add_via_stack_center(from_layer="m1",
|
||||
to_layer="m2",
|
||||
self.add_via_stack_center(from_layer=self.input_layer,
|
||||
to_layer=self.bus_layer,
|
||||
offset=[self.decode_rails[a_pin].x, y_offset])
|
||||
|
||||
def route_output_and(self):
|
||||
|
|
@ -188,31 +234,47 @@ class hierarchical_predecode(design.design):
|
|||
for inv_num in range(self.number_of_inputs):
|
||||
out_pin = "Abar_{}".format(inv_num)
|
||||
in_pin = "in_{}".format(inv_num)
|
||||
|
||||
inv_out_pin = self.inv_inst[inv_num].get_pin("Z")
|
||||
inv_out_pos = inv_out_pin.rc()
|
||||
|
||||
# add output so that it is just below the vdd or gnd rail
|
||||
# since this is where the p/n devices are and there are no
|
||||
# pins in the and gates.
|
||||
y_offset = (inv_num + 1) * self.inv.height - 3 * self.m1_space
|
||||
inv_out_pin = self.in_inst[inv_num].get_pin("Z")
|
||||
inv_out_pos = inv_out_pin.rc()
|
||||
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(), 0)
|
||||
rail_pos = vector(self.decode_rails[out_pin].x, y_offset)
|
||||
self.add_path(inv_out_pin.layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
|
||||
if OPTS.tech_name == "s8":
|
||||
rail_pos = vector(self.decode_rails[out_pin].x, inv_out_pos.y)
|
||||
self.add_path(self.output_layer, [inv_out_pos, rail_pos])
|
||||
else:
|
||||
y_offset = (inv_num + 1) * self.inv.height - self.output_layer_pitch
|
||||
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0)
|
||||
rail_pos = vector(self.decode_rails[out_pin].x, y_offset)
|
||||
self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
|
||||
|
||||
self.add_via_stack_center(from_layer=inv_out_pin.layer,
|
||||
to_layer="m2",
|
||||
offset=rail_pos)
|
||||
to_layer=self.output_layer,
|
||||
offset=inv_out_pos)
|
||||
self.add_via_stack_center(from_layer=self.output_layer,
|
||||
to_layer=self.bus_layer,
|
||||
offset=rail_pos,
|
||||
directions=self.bus_directions)
|
||||
|
||||
# route input
|
||||
pin = self.in_inst[inv_num].get_pin("A")
|
||||
inv_in_pos = pin.lc()
|
||||
pin = self.inv_inst[inv_num].get_pin("A")
|
||||
inv_in_pos = pin.center()
|
||||
in_pos = vector(self.input_rails[in_pin].x, inv_in_pos.y)
|
||||
self.add_path("m1", [in_pos, inv_in_pos])
|
||||
self.add_path(self.input_layer, [in_pos, inv_in_pos])
|
||||
self.add_via_stack_center(from_layer=pin.layer,
|
||||
to_layer="m1",
|
||||
to_layer=self.input_layer,
|
||||
offset=inv_in_pos)
|
||||
self.add_via_stack_center(from_layer="m1",
|
||||
to_layer="m2",
|
||||
offset=in_pos)
|
||||
via=self.add_via_stack_center(from_layer=self.input_layer,
|
||||
to_layer=self.bus_layer,
|
||||
offset=in_pos)
|
||||
# Create the input pin at this location on the rail
|
||||
self.add_layout_pin_rect_center(text=in_pin,
|
||||
layer=self.bus_layer,
|
||||
offset=in_pos,
|
||||
height=via.mod.second_layer_height,
|
||||
width=via.mod.second_layer_width)
|
||||
|
||||
def route_and_to_rails(self):
|
||||
# This 2D array defines the connection mapping
|
||||
|
|
@ -231,43 +293,67 @@ class hierarchical_predecode(design.design):
|
|||
pin = self.and_inst[k].get_pin(gate_pin)
|
||||
pin_pos = pin.center()
|
||||
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
|
||||
self.add_path("m1", [rail_pos, pin_pos])
|
||||
self.add_via_stack_center(from_layer="m1",
|
||||
to_layer="m2",
|
||||
offset=rail_pos)
|
||||
self.add_path(self.input_layer, [rail_pos, pin_pos])
|
||||
self.add_via_stack_center(from_layer=self.input_layer,
|
||||
to_layer=self.bus_layer,
|
||||
offset=rail_pos,
|
||||
directions=self.bus_directions)
|
||||
if gate_pin == "A":
|
||||
direction = None
|
||||
else:
|
||||
direction = ("H", "H")
|
||||
|
||||
self.add_via_stack_center(from_layer=pin.layer,
|
||||
to_layer="m1",
|
||||
to_layer=self.input_layer,
|
||||
offset=pin_pos,
|
||||
directions=direction)
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
""" 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() + self.m1_space
|
||||
# out_xoffset = self.and_inst[0].cx() + self.m1_space
|
||||
for num in range(0, self.number_of_outputs):
|
||||
# this will result in duplicate polygons for rails, but who cares
|
||||
|
||||
# Route both supplies
|
||||
# In s8, we use hand-made decoder cells with vertical power
|
||||
if OPTS.tech_name == "s8":
|
||||
for n in ["vdd", "gnd"]:
|
||||
and_pin = self.and_inst[num].get_pin(n)
|
||||
supply_offset = and_pin.ll().scale(0, 1)
|
||||
self.add_rect(layer=and_pin.layer,
|
||||
offset=supply_offset,
|
||||
width=self.and_inst[num].rx())
|
||||
# This makes a wire from top to bottom for both inv and and gates
|
||||
for i in [self.inv_inst, self.and_inst]:
|
||||
bot_pins = i[0].get_pins(n)
|
||||
top_pins = i[-1].get_pins(n)
|
||||
for (bot_pin, top_pin) in zip(bot_pins, top_pins):
|
||||
self.add_rect(layer=bot_pin.layer,
|
||||
offset=vector(bot_pin.lx(), self.bus_pitch),
|
||||
width=bot_pin.width(),
|
||||
height=top_pin.uy() - self.bus_pitch)
|
||||
# This adds power vias at the top of each cell
|
||||
# (except the last to keep them inside the boundary)
|
||||
for i in self.inv_inst[:-1:2] + self.and_inst[:-1:2]:
|
||||
pins = i.get_pins(n)
|
||||
for pin in pins:
|
||||
self.add_power_pin(name=n,
|
||||
loc=pin.uc(),
|
||||
start_layer=pin.layer)
|
||||
self.add_power_pin(name=n,
|
||||
loc=pin.uc(),
|
||||
start_layer=pin.layer)
|
||||
|
||||
# In other techs, we are using standard cell decoder cells with horizontal power
|
||||
else:
|
||||
for num in range(0, self.number_of_outputs):
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in [in_xoffset]:
|
||||
pin_pos = vector(xoffset, and_pin.cy())
|
||||
self.add_power_pin(name=n,
|
||||
loc=pin_pos,
|
||||
start_layer=and_pin.layer)
|
||||
# Route both supplies
|
||||
for n in ["vdd", "gnd"]:
|
||||
and_pins = self.and_inst[num].get_pins(n)
|
||||
for and_pin in and_pins:
|
||||
self.add_segment_center(layer=and_pin.layer,
|
||||
start=vector(0, and_pin.cy()),
|
||||
end=vector(self.width, and_pin.cy()))
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in [self.inv_inst[0].lx() - self.bus_space,
|
||||
self.and_inst[0].lx() - self.bus_space]:
|
||||
pin_pos = vector(xoffset, and_pin.cy())
|
||||
self.add_power_pin(name=n,
|
||||
loc=pin_pos,
|
||||
start_layer=and_pin.layer)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,13 +5,10 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
from tech import drc
|
||||
import debug
|
||||
import design
|
||||
from vector import vector
|
||||
from hierarchical_predecode import hierarchical_predecode
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class hierarchical_predecode2x4(hierarchical_predecode):
|
||||
"""
|
||||
Pre 2x4 decoder used in hierarchical_decoder.
|
||||
|
|
@ -33,21 +30,6 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
|||
["in_0", "in_1", "out_3", "vdd", "gnd"]]
|
||||
self.create_and_array(connections)
|
||||
|
||||
def create_layout(self):
|
||||
""" The general organization is from left to right:
|
||||
1) a set of M2 rails for input signals
|
||||
2) a set of inverters to invert input signals
|
||||
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
|
||||
4) a set of AND gates for inversion
|
||||
"""
|
||||
self.setup_layout_constraints()
|
||||
self.route_rails()
|
||||
self.place_input_inverters()
|
||||
self.place_and_array()
|
||||
self.route()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def get_and_input_line_combination(self):
|
||||
""" These are the decoder connections of the AND gates to the A,B pins """
|
||||
combination = [["Abar_0", "Abar_1"],
|
||||
|
|
|
|||
|
|
@ -5,13 +5,10 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
from tech import drc
|
||||
import debug
|
||||
import design
|
||||
from vector import vector
|
||||
from hierarchical_predecode import hierarchical_predecode
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class hierarchical_predecode3x8(hierarchical_predecode):
|
||||
"""
|
||||
Pre 3x8 decoder used in hierarchical_decoder.
|
||||
|
|
@ -37,22 +34,6 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
|||
["in_0", "in_1", "in_2", "out_7", "vdd", "gnd"]]
|
||||
self.create_and_array(connections)
|
||||
|
||||
def create_layout(self):
|
||||
"""
|
||||
The general organization is from left to right:
|
||||
1) a set of M2 rails for input signals
|
||||
2) a set of inverters to invert input signals
|
||||
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
|
||||
4) a set of NAND gates for inversion
|
||||
"""
|
||||
self.setup_layout_constraints()
|
||||
self.route_rails()
|
||||
self.place_input_inverters()
|
||||
self.place_and_array()
|
||||
self.route()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def get_and_input_line_combination(self):
|
||||
""" These are the decoder connections of the NAND gates to the A,B,C pins """
|
||||
combination = [["Abar_0", "Abar_1", "Abar_2"],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
# 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.
|
||||
#
|
||||
from hierarchical_predecode import hierarchical_predecode
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class hierarchical_predecode4x16(hierarchical_predecode):
|
||||
"""
|
||||
Pre 4x16 decoder used in hierarchical_decoder.
|
||||
"""
|
||||
def __init__(self, name, height=None):
|
||||
hierarchical_predecode.__init__(self, name, 4, height)
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_input_inverters()
|
||||
connections=[["inbar_0", "inbar_1", "inbar_2", "inbar_3", "out_0", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "inbar_2", "inbar_3", "out_1", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "inbar_2", "inbar_3", "out_2", "vdd", "gnd"],
|
||||
["in_0", "in_1", "inbar_2", "inbar_3", "out_3", "vdd", "gnd"],
|
||||
["inbar_0", "inbar_1", "in_2", "inbar_3", "out_4", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "in_2", "inbar_3", "out_5", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "in_2", "inbar_3", "out_6", "vdd", "gnd"],
|
||||
["in_0", "in_1", "in_2", "inbar_3", "out_7", "vdd", "gnd"],
|
||||
["inbar_0", "inbar_1", "inbar_2", "in_3", "out_0", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "inbar_2", "in_3", "out_1", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "inbar_2", "in_3", "out_2", "vdd", "gnd"],
|
||||
["in_0", "in_1", "inbar_2", "in_3", "out_3", "vdd", "gnd"],
|
||||
["inbar_0", "inbar_1", "in_2", "in_3", "out_4", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "in_2", "in_3", "out_5", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "in_2", "in_3", "out_6", "vdd", "gnd"],
|
||||
["in_0", "in_1", "in_2", "in_3", "out_7", "vdd", "gnd"] ]
|
||||
|
||||
self.create_and_array(connections)
|
||||
|
||||
def get_and_input_line_combination(self):
|
||||
""" These are the decoder connections of the AND gates to the A,B pins """
|
||||
combination = [["Abar_0", "Abar_1", "Abar_2", "Abar_3"],
|
||||
["A_0", "Abar_1", "Abar_2", "Abar_3"],
|
||||
["Abar_0", "A_1", "Abar_2", "Abar_3"],
|
||||
["A_0", "A_1", "Abar_2", "Abar_3"],
|
||||
["Abar_0", "Abar_1", "A_2" , "Abar_3"],
|
||||
["A_0", "Abar_1", "A_2" , "Abar_3"],
|
||||
["Abar_0", "A_1", "A_2" , "Abar_3"],
|
||||
["A_0", "A_1", "A_2" , "Abar_3"],
|
||||
["Abar_0", "Abar_1", "Abar_2", "A_3"],
|
||||
["A_0", "Abar_1", "Abar_2", "A_3"],
|
||||
["Abar_0", "A_1", "Abar_2", "A_3"],
|
||||
["A_0", "A_1", "Abar_2", "A_3"],
|
||||
["Abar_0", "Abar_1", "A_2", "A_3"],
|
||||
["A_0", "Abar_1", "A_2", "A_3"],
|
||||
["Abar_0", "A_1", "A_2", "A_3"],
|
||||
["A_0", "A_1", "A_2", "A_3"]]
|
||||
return combination
|
||||
|
|
@ -8,7 +8,7 @@ import debug
|
|||
import design
|
||||
from sram_factory import factory
|
||||
from vector import vector
|
||||
|
||||
from tech import layer
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
|
|
@ -41,6 +41,10 @@ class port_address(design.design):
|
|||
self.create_wordline_driver()
|
||||
|
||||
def create_layout(self):
|
||||
if "li" in layer:
|
||||
self.route_layer = "li"
|
||||
else:
|
||||
self.route_layer = "m1"
|
||||
self.place_instances()
|
||||
self.route_layout()
|
||||
self.DRC_LVS()
|
||||
|
|
@ -85,11 +89,19 @@ class port_address(design.design):
|
|||
def route_internal(self):
|
||||
for row in range(self.num_rows):
|
||||
# The pre/post is to access the pin from "outside" the cell to avoid DRCs
|
||||
decoder_out_pos = self.row_decoder_inst.get_pin("decode_{}".format(row)).rc()
|
||||
driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc()
|
||||
mid1 = decoder_out_pos.scale(0.5, 1) + driver_in_pos.scale(0.5, 0)
|
||||
mid2 = decoder_out_pos.scale(0.5, 0) + driver_in_pos.scale(0.5, 1)
|
||||
self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos])
|
||||
decoder_out_pin = self.row_decoder_inst.get_pin("decode_{}".format(row))
|
||||
decoder_out_pos = decoder_out_pin.rc()
|
||||
driver_in_pin = self.wordline_driver_inst.get_pin("in_{}".format(row))
|
||||
driver_in_pos = driver_in_pin.lc()
|
||||
self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos)
|
||||
|
||||
self.add_via_stack_center(from_layer=decoder_out_pin.layer,
|
||||
to_layer=self.route_layer,
|
||||
offset=decoder_out_pos)
|
||||
|
||||
self.add_via_stack_center(from_layer=driver_in_pin.layer,
|
||||
to_layer=self.route_layer,
|
||||
offset=driver_in_pos)
|
||||
|
||||
def add_modules(self):
|
||||
|
||||
|
|
@ -97,7 +109,7 @@ class port_address(design.design):
|
|||
num_outputs=self.num_rows)
|
||||
self.add_mod(self.row_decoder)
|
||||
|
||||
self.wordline_driver = factory.create(module_type="wordline_driver",
|
||||
self.wordline_driver = factory.create(module_type="wordline_driver_array",
|
||||
rows=self.num_rows,
|
||||
cols=self.num_cols)
|
||||
self.add_mod(self.wordline_driver)
|
||||
|
|
@ -139,7 +151,6 @@ class port_address(design.design):
|
|||
|
||||
row_decoder_offset = vector(0, 0)
|
||||
wordline_driver_offset = vector(self.row_decoder.width, 0)
|
||||
|
||||
self.wordline_driver_inst.place(wordline_driver_offset)
|
||||
self.row_decoder_inst.place(row_decoder_offset)
|
||||
|
||||
|
|
|
|||
|
|
@ -469,45 +469,37 @@ class port_data(design.design):
|
|||
bank_wmask_name = "bank_wmask_{}".format(bit)
|
||||
self.copy_layout_pin(self.write_mask_and_array_inst, wmask_in_name, bank_wmask_name)
|
||||
|
||||
def route_write_mask_and_array_to_write_driver(self,port):
|
||||
""" Routing of wdriver_sel_{} between write mask AND array and write driver array. Adds layout pin for write
|
||||
mask AND array output and via for write driver enable """
|
||||
def route_write_mask_and_array_to_write_driver(self, port):
|
||||
"""
|
||||
Routing of wdriver_sel_{} between write mask AND array and
|
||||
write driver array. Adds layout pin for write
|
||||
mask AND array output and via for write driver enable
|
||||
"""
|
||||
|
||||
inst1 = self.write_mask_and_array_inst
|
||||
inst2 = self.write_driver_array_inst
|
||||
wmask_inst = self.write_mask_and_array_inst
|
||||
wdriver_inst = self.write_driver_array_inst
|
||||
|
||||
loc = 0
|
||||
for bit in range(self.num_wmasks):
|
||||
# Bring write mask AND array output pin to port data level
|
||||
self.copy_layout_pin(inst1, "wmask_out_{0}".format(bit), "wdriver_sel_{0}".format(bit))
|
||||
self.copy_layout_pin(wmask_inst, "wmask_out_{0}".format(bit), "wdriver_sel_{0}".format(bit))
|
||||
|
||||
wmask_out_pin = inst1.get_pin("wmask_out_{0}".format(bit))
|
||||
wdriver_en_pin = inst2.get_pin("en_{0}".format(bit))
|
||||
wmask_out_pin = wmask_inst.get_pin("wmask_out_{0}".format(bit))
|
||||
wdriver_en_pin = wdriver_inst.get_pin("en_{0}".format(bit))
|
||||
|
||||
wmask_pos = wmask_out_pin.center()
|
||||
wdriver_pos = wdriver_en_pin.rc() - vector(self.m2_pitch, 0)
|
||||
mid_pos = vector(wdriver_pos.x, wmask_pos.y)
|
||||
|
||||
# The metal2 wdriver_sel_{} wire must hit the en_{} pin after the closest bitline pin that's right of the
|
||||
# the wdriver_sel_{} pin in the write driver AND array.
|
||||
if bit == 0:
|
||||
# When the write mask output pin is right of the bitline, the target is found
|
||||
while (wmask_out_pin.lx() + self.m2_pitch > inst2.get_pin("data_{0}".format(loc)).rx()):
|
||||
loc += 1
|
||||
length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch
|
||||
debug.check(loc<=self.num_wmasks,
|
||||
"Couldn't route the write mask select.")
|
||||
else:
|
||||
# Stride by the write size rather than finding the next pin to the right
|
||||
loc += self.write_size
|
||||
length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch
|
||||
|
||||
beg_pos = wmask_out_pin.center()
|
||||
middle_pos = vector(length, wmask_out_pin.cy())
|
||||
end_pos = vector(length, wdriver_en_pin.cy())
|
||||
|
||||
# Add driver on mask output
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=wmask_pos)
|
||||
# Add via for the write driver array's enable input
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=end_pos)
|
||||
offset=wdriver_pos)
|
||||
|
||||
# Route between write mask AND array and write driver array
|
||||
self.add_wire(self.m1_stack, [beg_pos, middle_pos, end_pos])
|
||||
self.add_wire(self.m1_stack, [wmask_pos, mid_pos, wdriver_pos])
|
||||
|
||||
def route_column_mux_to_precharge_array(self, port):
|
||||
""" Routing of BL and BR between col mux and precharge array """
|
||||
|
|
@ -516,15 +508,12 @@ class port_data(design.design):
|
|||
if self.col_addr_size==0:
|
||||
return
|
||||
|
||||
inst1 = self.column_mux_array_inst
|
||||
inst2 = self.precharge_array_inst
|
||||
start_bit = 1 if self.port == 0 else 0
|
||||
|
||||
insn2_start_bit = 1 if self.port == 0 else 0
|
||||
|
||||
self.connect_bitlines(inst1=inst1,
|
||||
inst2=inst2,
|
||||
self.connect_bitlines(inst1=self.column_mux_array_inst,
|
||||
inst2=self.precharge_array_inst,
|
||||
num_bits=self.num_cols,
|
||||
inst2_start_bit=insn2_start_bit)
|
||||
inst2_start_bit=start_bit)
|
||||
|
||||
def route_sense_amp_to_column_mux_or_precharge_array(self, port):
|
||||
""" Routing of BL and BR between sense_amp and column mux or precharge array """
|
||||
|
|
|
|||
|
|
@ -7,15 +7,12 @@
|
|||
#
|
||||
import debug
|
||||
import design
|
||||
import math
|
||||
from tech import drc
|
||||
from tech import drc, layer
|
||||
from vector import vector
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
from tech import cell_properties
|
||||
|
||||
|
||||
class wordline_driver(design.design):
|
||||
class wordline_driver_array(design.design):
|
||||
"""
|
||||
Creates a Wordline Driver
|
||||
Generates the wordline-driver to drive the bitcell
|
||||
|
|
@ -26,21 +23,9 @@ class wordline_driver(design.design):
|
|||
debug.info(1, "Creating {0}".format(self.name))
|
||||
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
|
||||
|
||||
self.bitcell_rows = rows
|
||||
self.bitcell_cols = cols
|
||||
self.rows = rows
|
||||
self.cols = cols
|
||||
|
||||
b = factory.create(module_type="bitcell")
|
||||
try:
|
||||
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
|
||||
except AttributeError:
|
||||
self.cell_multiple = 1
|
||||
self.cell_height = self.cell_multiple * b.height
|
||||
|
||||
# We may have more than one bitcell per decoder row
|
||||
self.num_rows = math.ceil(self.bitcell_rows / self.cell_multiple)
|
||||
# We will place this many final decoders per row
|
||||
self.decoders_per_row = math.ceil(self.bitcell_rows / self.num_rows)
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
|
@ -51,7 +36,10 @@ class wordline_driver(design.design):
|
|||
self.create_drivers()
|
||||
|
||||
def create_layout(self):
|
||||
self.setup_layout_constants()
|
||||
if "li" in layer:
|
||||
self.route_layer = "li"
|
||||
else:
|
||||
self.route_layer = "m1"
|
||||
self.place_drivers()
|
||||
self.route_layout()
|
||||
self.route_vdd_gnd()
|
||||
|
|
@ -61,104 +49,99 @@ class wordline_driver(design.design):
|
|||
|
||||
def add_pins(self):
|
||||
# inputs to wordline_driver.
|
||||
for i in range(self.bitcell_rows):
|
||||
for i in range(self.rows):
|
||||
self.add_pin("in_{0}".format(i), "INPUT")
|
||||
# Outputs from wordline_driver.
|
||||
for i in range(self.bitcell_rows):
|
||||
for i in range(self.rows):
|
||||
self.add_pin("wl_{0}".format(i), "OUTPUT")
|
||||
self.add_pin("en", "INPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def add_modules(self):
|
||||
self.and2 = factory.create(module_type="pand2",
|
||||
height=self.cell_height,
|
||||
size=self.bitcell_cols)
|
||||
self.add_mod(self.and2)
|
||||
self.wl_driver = factory.create(module_type="wordline_driver",
|
||||
size=self.cols)
|
||||
self.add_mod(self.wl_driver)
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
"""
|
||||
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
|
||||
xoffset_list = [self.and_inst[0].lx()]
|
||||
for num in range(self.bitcell_rows):
|
||||
# this will result in duplicate polygons for rails, but who cares
|
||||
|
||||
# use the inverter offset even though it will be the and's too
|
||||
(gate_offset, y_dir) = self.get_gate_offset(0,
|
||||
self.and2.height,
|
||||
num)
|
||||
# Route both supplies
|
||||
if OPTS.tech_name == "s8":
|
||||
for name in ["vdd", "gnd"]:
|
||||
supply_pin = self.and_inst[num].get_pin(name)
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in xoffset_list:
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_power_pin(name, pin_pos)
|
||||
supply_pins = self.wld_inst[0].get_pins(name)
|
||||
for pin in supply_pins:
|
||||
self.add_layout_pin_segment_center(text=name,
|
||||
layer=pin.layer,
|
||||
start=pin.bc(),
|
||||
end=vector(pin.cx(), self.height))
|
||||
else:
|
||||
# Find the x offsets for where the vias/pins should be placed
|
||||
xoffset_list = [self.wld_inst[0].rx()]
|
||||
for num in range(self.rows):
|
||||
# this will result in duplicate polygons for rails, but who cares
|
||||
|
||||
# use the inverter offset even though it will be the and's too
|
||||
(gate_offset, y_dir) = self.get_gate_offset(0,
|
||||
self.wl_driver.height,
|
||||
num)
|
||||
# Route both supplies
|
||||
for name in ["vdd", "gnd"]:
|
||||
supply_pin = self.wld_inst[num].get_pin(name)
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in xoffset_list:
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_power_pin(name, pin_pos)
|
||||
|
||||
def create_drivers(self):
|
||||
self.and_inst = []
|
||||
for row in range(self.bitcell_rows):
|
||||
self.wld_inst = []
|
||||
for row in range(self.rows):
|
||||
name_and = "wl_driver_and{}".format(row)
|
||||
|
||||
# add and2
|
||||
self.and_inst.append(self.add_inst(name=name_and,
|
||||
mod=self.and2))
|
||||
self.wld_inst.append(self.add_inst(name=name_and,
|
||||
mod=self.wl_driver))
|
||||
self.connect_inst(["in_{0}".format(row),
|
||||
"en",
|
||||
"wl_{0}".format(row),
|
||||
"vdd", "gnd"])
|
||||
|
||||
def setup_layout_constants(self):
|
||||
# We may have more than one bitcell per decoder row
|
||||
self.driver_rows = math.ceil(self.bitcell_rows / self.cell_multiple)
|
||||
# We will place this many final decoders per row
|
||||
self.decoders_per_row = math.ceil(self.bitcell_rows / self.driver_rows)
|
||||
|
||||
def place_drivers(self):
|
||||
|
||||
for row in range(self.rows):
|
||||
if (row % 2):
|
||||
y_offset = self.wl_driver.height * (row + 1)
|
||||
inst_mirror = "MX"
|
||||
else:
|
||||
y_offset = self.wl_driver.height * row
|
||||
inst_mirror = "R0"
|
||||
|
||||
and2_offset = [self.wl_driver.width, y_offset]
|
||||
|
||||
# add and2
|
||||
self.wld_inst[row].place(offset=and2_offset,
|
||||
mirror=inst_mirror)
|
||||
|
||||
# Leave a well gap to separate the bitcell array well from this well
|
||||
well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active")
|
||||
|
||||
self.width = self.decoders_per_row * self.and2.width + well_gap
|
||||
self.height = self.and2.height * self.driver_rows
|
||||
|
||||
for inst_index in range(self.bitcell_rows):
|
||||
row = math.floor(inst_index / self.decoders_per_row)
|
||||
dec = inst_index % self.decoders_per_row
|
||||
|
||||
if (row % 2):
|
||||
y_offset = self.and2.height * (row + 1)
|
||||
inst_mirror = "MX"
|
||||
else:
|
||||
y_offset = self.and2.height * row
|
||||
inst_mirror = "R0"
|
||||
|
||||
x_offset = dec * self.and2.width
|
||||
and2_offset = [x_offset, y_offset]
|
||||
|
||||
# add and2
|
||||
self.and_inst[inst_index].place(offset=and2_offset,
|
||||
mirror=inst_mirror)
|
||||
self.width = self.wl_driver.width + well_gap
|
||||
self.height = self.wl_driver.height * self.rows
|
||||
|
||||
def route_layout(self):
|
||||
""" Route all of the signals """
|
||||
|
||||
# Wordline enable connection
|
||||
en_pin = self.and_inst[0].get_pin("B")
|
||||
en_pin = self.wld_inst[0].get_pin("B")
|
||||
en_bottom_pos = vector(en_pin.lx(), 0)
|
||||
en_pin = self.add_layout_pin(text="en",
|
||||
layer="m2",
|
||||
offset=en_bottom_pos,
|
||||
height=self.height)
|
||||
|
||||
for inst_index in range(self.bitcell_rows):
|
||||
and_inst = self.and_inst[inst_index]
|
||||
row = math.floor(inst_index / self.decoders_per_row)
|
||||
for row in range(self.rows):
|
||||
and_inst = self.wld_inst[row]
|
||||
|
||||
# Drop a via
|
||||
b_pin = and_inst.get_pin("B")
|
||||
|
|
@ -167,18 +150,12 @@ class wordline_driver(design.design):
|
|||
offset=b_pin.center())
|
||||
|
||||
# connect the decoder input pin to and2 A
|
||||
a_pin = and_inst.get_pin("A")
|
||||
a_pos = a_pin.center()
|
||||
# must under the clk line in M1
|
||||
self.add_layout_pin_segment_center(text="in_{0}".format(row),
|
||||
layer="m1",
|
||||
start=vector(0, a_pos.y),
|
||||
end=a_pos)
|
||||
self.copy_layout_pin(and_inst, "A", "in_{0}".format(row))
|
||||
|
||||
# output each WL on the right
|
||||
wl_offset = and_inst.get_pin("Z").rc()
|
||||
self.add_layout_pin_segment_center(text="wl_{0}".format(row),
|
||||
layer="m1",
|
||||
layer=self.route_layer,
|
||||
start=wl_offset,
|
||||
end=wl_offset - vector(self.m1_width, 0))
|
||||
|
||||
|
|
@ -189,7 +166,7 @@ class wordline_driver(design.design):
|
|||
"""
|
||||
stage_effort_list = []
|
||||
|
||||
stage1 = self.and2.get_stage_effort(external_cout, inp_is_rise)
|
||||
stage1 = self.wl_driver.get_stage_effort(external_cout, inp_is_rise)
|
||||
stage_effort_list.append(stage1)
|
||||
|
||||
return stage_effort_list
|
||||
|
|
@ -200,5 +177,5 @@ class wordline_driver(design.design):
|
|||
the enable connections in the bank
|
||||
"""
|
||||
# The enable is connected to a and2 for every row.
|
||||
total_cin = self.and2.get_cin() * self.rows
|
||||
total_cin = self.wl_driver.get_cin() * self.rows
|
||||
return total_cin
|
||||
|
|
@ -134,6 +134,10 @@ class options(optparse.Values):
|
|||
dff_array = "dff_array"
|
||||
dff = "dff"
|
||||
dummy_bitcell = "dummy_bitcell"
|
||||
inv_dec = "pinv"
|
||||
nand2_dec = "pnand2"
|
||||
nand3_dec = "pnand3"
|
||||
nand4_dec = "pnand4" # Not available right now
|
||||
precharge_array = "precharge_array"
|
||||
ptx = "ptx"
|
||||
replica_bitcell = "replica_bitcell"
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ from sram_factory import factory
|
|||
|
||||
class pand2(pgate.pgate):
|
||||
"""
|
||||
This is a simple buffer used for driving loads.
|
||||
This is an AND (or NAND) with configurable drive strength.
|
||||
"""
|
||||
def __init__(self, name, size=1, height=None, vertical=False, add_wells=True):
|
||||
debug.info(1, "Creating pand2 {}".format(name))
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ class pdriver(pgate.pgate):
|
|||
sized for driving a load.
|
||||
"""
|
||||
|
||||
def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None, add_wells=True):
|
||||
def __init__(self, name, inverting=False, fanout=0, size_list=None, height=None, add_wells=True):
|
||||
|
||||
debug.info(1, "creating pdriver {}".format(name))
|
||||
|
||||
self.stage_effort = 3
|
||||
self.height = height
|
||||
self.neg_polarity = neg_polarity
|
||||
self.inverting = inverting
|
||||
self.size_list = size_list
|
||||
self.fanout = fanout
|
||||
|
||||
|
|
@ -31,8 +31,8 @@ class pdriver(pgate.pgate):
|
|||
debug.error("Either fanout or size list must be specified.", -1)
|
||||
if self.size_list and self.fanout != 0:
|
||||
debug.error("Cannot specify both size_list and fanout.", -1)
|
||||
if self.size_list and self.neg_polarity:
|
||||
debug.error("Cannot specify both size_list and neg_polarity.", -1)
|
||||
if self.size_list and self.inverting:
|
||||
debug.error("Cannot specify both size_list and inverting.", -1)
|
||||
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name, height, add_wells)
|
||||
|
|
@ -47,9 +47,9 @@ class pdriver(pgate.pgate):
|
|||
int(round(self.fanout ** (1 / self.stage_effort))))
|
||||
|
||||
# Increase the number of stages if we need to fix polarity
|
||||
if self.neg_polarity and (self.num_stages % 2 == 0):
|
||||
if self.inverting and (self.num_stages % 2 == 0):
|
||||
self.num_stages += 1
|
||||
elif not self.neg_polarity and (self.num_stages % 2):
|
||||
elif not self.inverting and (self.num_stages % 2):
|
||||
self.num_stages += 1
|
||||
|
||||
self.size_list = []
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ class pgate(design.design):
|
|||
self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer))
|
||||
|
||||
# This is the space from a S/D contact to the supply rail
|
||||
# Assume the contact starts at the active edge
|
||||
contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space
|
||||
# This is a poly-to-poly of a flipped cell
|
||||
poly_to_poly_gate_space = self.poly_extend_active + self.poly_space
|
||||
|
|
|
|||
|
|
@ -0,0 +1,217 @@
|
|||
# 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 contact
|
||||
import pinv
|
||||
import debug
|
||||
from tech import drc, parameter
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
||||
if(OPTS.tech_name == "s8"):
|
||||
from tech import nmos_bins, pmos_bins, accuracy_requirement
|
||||
|
||||
|
||||
class pinv_dec(pinv.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="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
|
||||
if OPTS.tech_name == "s8":
|
||||
self.supply_layer = "m1"
|
||||
else:
|
||||
self.supply_layer = "m2"
|
||||
|
||||
pinv.pinv.__init__(self, 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")
|
||||
if OPTS.tech_name == "s8":
|
||||
(self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width)
|
||||
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
|
||||
return
|
||||
|
||||
# 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.")
|
||||
|
||||
# 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 = contact.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()])
|
||||
|
||||
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. """
|
||||
|
||||
from tech import layer
|
||||
if "pwell" in layer:
|
||||
ll = self.nmos_inst.ll() - self.nmos_inst.mod.active_offset
|
||||
ur = self.nmos_inst.ur() + self.nmos_inst.mod.active_offset
|
||||
self.add_rect(layer="pwell",
|
||||
offset=ll,
|
||||
width=ur.x - ll.x,
|
||||
height=self.height - ll.y)
|
||||
|
||||
if "nwell" in layer:
|
||||
ll = self.pmos_inst.ll() - self.pmos_inst.mod.active_offset
|
||||
ur = self.pmos_inst.ur() + self.pmos_inst.mod.active_offset
|
||||
self.add_rect(layer="nwell",
|
||||
offset=ll - vector(self.nwell_enclose_active, 0),
|
||||
width=ur.x - ll.x + self.nwell_enclose_active,
|
||||
height=self.height - ll.y + 2 * self.nwell_enclose_active)
|
||||
|
||||
def place_ptx(self):
|
||||
"""
|
||||
"""
|
||||
|
||||
# offset so that the input contact is over from the left edge by poly spacing
|
||||
x_offset = self.nmos.active_offset.y + contact.poly_contact.width + self.poly_space
|
||||
# center the transistor in the y-dimension
|
||||
y_offset = self.nmos.width + self.active_space
|
||||
self.nmos_pos = vector(x_offset, y_offset)
|
||||
self.nmos_inst.place(self.nmos_pos)
|
||||
self.nmos_inst.place(self.nmos_pos,
|
||||
rotate=270)
|
||||
# place PMOS so it is half a poly spacing down from the top
|
||||
xoffset = self.nmos_inst.height + 2 * self.poly_extend_active + 2 * self.well_extend_active + drc("pwell_to_nwell")
|
||||
self.pmos_pos = self.nmos_pos + vector(xoffset, 0)
|
||||
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)
|
||||
|
||||
def route_outputs(self):
|
||||
"""
|
||||
Route the output (drains) together.
|
||||
Optionally, routes output to edge.
|
||||
"""
|
||||
|
||||
# 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.nwell_contact = 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.pwell_contact= 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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
|
|
@ -212,7 +212,10 @@ class pnand3(pgate.pgate):
|
|||
pmos_drain_bottom = self.pmos1_inst.get_pin("D").by()
|
||||
self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space
|
||||
|
||||
# This is a more compact offset, but the bottom one works better in the decoders to "center" the pins
|
||||
# in the height of the gates
|
||||
self.inputA_yoffset = self.output_yoffset - 0.5 * self.route_layer_width - self.route_layer_space
|
||||
# self.inputA_yoffset = self.output_yoffset - self.m1_pitch
|
||||
self.route_input_gate(self.pmos1_inst,
|
||||
self.nmos1_inst,
|
||||
self.inputA_yoffset,
|
||||
|
|
|
|||
|
|
@ -386,10 +386,12 @@ class ptx(design.design):
|
|||
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,
|
||||
width=self.well_width,
|
||||
height=self.well_height)
|
||||
well = self.add_rect(layer=well_name,
|
||||
offset=well_ll,
|
||||
width=self.well_width,
|
||||
height=self.well_height)
|
||||
setattr(self, well_name, well)
|
||||
|
||||
if "vtg" in layer:
|
||||
self.add_rect(layer="vtg",
|
||||
offset=well_ll,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,155 @@
|
|||
# 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 debug
|
||||
from vector import vector
|
||||
import design
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
from tech import layer
|
||||
|
||||
|
||||
class wordline_driver(design.design):
|
||||
"""
|
||||
This is an AND (or NAND) with configurable drive strength to drive the wordlines.
|
||||
It is matched to the bitcell height.
|
||||
"""
|
||||
def __init__(self, name, size=1, height=None):
|
||||
debug.info(1, "Creating wordline_driver {}".format(name))
|
||||
self.add_comment("size: {}".format(size))
|
||||
design.design.__init__(self, name)
|
||||
|
||||
if height is None:
|
||||
b = factory.create(module_type="bitcell")
|
||||
self.height = b.height
|
||||
else:
|
||||
self.height = height
|
||||
self.size = size
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.create_insts()
|
||||
|
||||
def create_modules(self):
|
||||
if OPTS.tech_name == "s8":
|
||||
self.nand = factory.create(module_type="nand2_dec")
|
||||
self.height = self.nand.height
|
||||
else:
|
||||
self.nand = factory.create(module_type="nand2_dec",
|
||||
height=self.height)
|
||||
|
||||
self.driver = factory.create(module_type="inv_dec",
|
||||
size=self.size,
|
||||
height=self.nand.height)
|
||||
|
||||
self.add_mod(self.nand)
|
||||
self.add_mod(self.driver)
|
||||
|
||||
def create_layout(self):
|
||||
self.width = self.nand.width + self.driver.width
|
||||
if "li" in layer:
|
||||
self.route_layer = "li"
|
||||
else:
|
||||
self.route_layer = "m1"
|
||||
|
||||
self.place_insts()
|
||||
self.route_wires()
|
||||
self.add_layout_pins()
|
||||
self.route_supply_rails()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin("A", "INPUT")
|
||||
self.add_pin("B", "INPUT")
|
||||
self.add_pin("Z", "OUTPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def create_insts(self):
|
||||
self.nand_inst = self.add_inst(name="wld_nand",
|
||||
mod=self.nand)
|
||||
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
|
||||
|
||||
self.driver_inst = self.add_inst(name="wl_driver",
|
||||
mod=self.driver)
|
||||
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||
|
||||
def place_insts(self):
|
||||
# Add NAND to the right
|
||||
self.nand_inst.place(offset=vector(0, 0))
|
||||
|
||||
# Add INV to the right
|
||||
self.driver_inst.place(offset=vector(self.nand_inst.rx(), 0))
|
||||
|
||||
def route_supply_rails(self):
|
||||
""" Add vdd/gnd rails to the top, (middle), and bottom. """
|
||||
if OPTS.tech_name == "s8":
|
||||
for name in ["vdd", "gnd"]:
|
||||
for inst in [self.nand_inst, self.driver_inst]:
|
||||
self.copy_layout_pin(inst, name)
|
||||
else:
|
||||
self.add_layout_pin_rect_center(text="gnd",
|
||||
layer=self.route_layer,
|
||||
offset=vector(0.5 * self.width, 0),
|
||||
width=self.width)
|
||||
|
||||
y_offset = self.height
|
||||
self.add_layout_pin_rect_center(text="vdd",
|
||||
layer=self.route_layer,
|
||||
offset=vector(0.5 * self.width, y_offset),
|
||||
width=self.width)
|
||||
|
||||
def route_wires(self):
|
||||
|
||||
# nand Z to inv A
|
||||
z1_pin = self.nand_inst.get_pin("Z")
|
||||
a2_pin = self.driver_inst.get_pin("A")
|
||||
if OPTS.tech_name == "s8":
|
||||
mid1_point = vector(a2_pin.cx(), z1_pin.cy())
|
||||
else:
|
||||
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
|
||||
self.add_path(self.route_layer,
|
||||
[z1_pin.center(), mid1_point, a2_pin.center()])
|
||||
|
||||
def add_layout_pins(self):
|
||||
pin = self.driver_inst.get_pin("Z")
|
||||
self.add_layout_pin_rect_center(text="Z",
|
||||
layer=pin.layer,
|
||||
offset=pin.center(),
|
||||
width=pin.width(),
|
||||
height=pin.height())
|
||||
|
||||
for pin_name in ["A", "B"]:
|
||||
pin = self.nand_inst.get_pin(pin_name)
|
||||
self.add_layout_pin_rect_center(text=pin_name,
|
||||
layer=pin.layer,
|
||||
offset=pin.center(),
|
||||
width=pin.width(),
|
||||
height=pin.height())
|
||||
|
||||
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||
"""Get the stage efforts of the A or B -> Z path"""
|
||||
stage_effort_list = []
|
||||
stage1_cout = self.driver.get_cin()
|
||||
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
||||
stage_effort_list.append(stage1)
|
||||
|
||||
stage2 = self.driver.get_stage_effort(external_cout, stage1.is_rise)
|
||||
stage_effort_list.append(stage2)
|
||||
|
||||
return stage_effort_list
|
||||
|
||||
def get_cin(self):
|
||||
"""Return the relative input capacitance of a single input"""
|
||||
return self.nand.get_cin()
|
||||
|
||||
|
|
@ -77,7 +77,9 @@ class sram_factory:
|
|||
"""
|
||||
tech_module_type, tm_overridden = self.get_techmodule_type(module_type)
|
||||
user_module_type, um_overridden = self.get_usermodule_type(module_type)
|
||||
|
||||
#print(module_type, tech_module_type, tm_overridden)
|
||||
#print(module_type, user_module_type, um_overridden)
|
||||
|
||||
# overridden user modules have priority
|
||||
if um_overridden:
|
||||
real_module_type = user_module_type
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class library_lvs_test(openram_test):
|
|||
debug.error("Missing GDS file {}".format(gds_name))
|
||||
if not os.path.isfile(sp_name):
|
||||
lvs_errors += 1
|
||||
debug.error("Missing SPICE file {}".format(gds_name))
|
||||
debug.error("Missing SPICE file {}".format(sp_name))
|
||||
drc_errors += verify.run_drc(name, gds_name)
|
||||
lvs_errors += verify.run_lvs(f, gds_name, sp_name)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
#!/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 pand2_dec_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
global verify
|
||||
import verify
|
||||
|
||||
import pand2_dec
|
||||
|
||||
debug.info(2, "Testing pand2 gate 4x")
|
||||
a = pand2_dec.pand2_dec(name="pand2x4", size=4)
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#!/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 pand3_dec_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
global verify
|
||||
import verify
|
||||
|
||||
import pand3_dec
|
||||
|
||||
debug.info(2, "Testing pand3 gate 4x")
|
||||
a = pand3_dec.pand3_dec(name="pand3x4", size=4)
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#!/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 pand3_dec_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
global verify
|
||||
import verify
|
||||
|
||||
import pand3_dec
|
||||
|
||||
debug.info(2, "Testing pand3 gate 4x")
|
||||
a = pand3_dec.pand3_dec(name="pand3x4", size=4)
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
@ -32,13 +32,13 @@ class pdriver_test(openram_test):
|
|||
c = factory.create(module_type="pdriver", fanout = 50)
|
||||
self.local_check(c)
|
||||
|
||||
d = factory.create(module_type="pdriver", fanout = 50, neg_polarity = True)
|
||||
d = factory.create(module_type="pdriver", fanout = 50, inverting = True)
|
||||
self.local_check(d)
|
||||
|
||||
e = factory.create(module_type="pdriver", fanout = 64)
|
||||
self.local_check(e)
|
||||
|
||||
f = factory.create(module_type="pdriver", fanout = 64, neg_polarity = True)
|
||||
f = factory.create(module_type="pdriver", fanout = 64, inverting = True)
|
||||
self.local_check(f)
|
||||
|
||||
globals.end_openram()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
#!/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 pinv_dec_1x_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Checking 1x size decoder inverter")
|
||||
tx = factory.create(module_type="pinv_dec", size=1)
|
||||
self.local_check(tx)
|
||||
|
||||
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())
|
||||
|
|
@ -25,7 +25,7 @@ class wordline_driver_test(openram_test):
|
|||
|
||||
# check wordline driver for single port
|
||||
debug.info(2, "Checking driver")
|
||||
tx = factory.create(module_type="wordline_driver", rows=8, cols=32)
|
||||
tx = factory.create(module_type="wordline_driver")
|
||||
self.local_check(tx)
|
||||
|
||||
globals.end_openram()
|
||||
|
|
@ -21,11 +21,17 @@ class hierarchical_decoder_test(openram_test):
|
|||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# Use the 2 port cell since it is usually bigger/easier
|
||||
OPTS.bitcell = "bitcell_1rw_1r"
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
|
||||
# Checks 2x4 and 2-input NAND decoder
|
||||
debug.info(1, "Testing 16 row sample for hierarchical_decoder")
|
||||
a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
|
||||
self.local_check(a)
|
||||
|
||||
|
||||
# Checks 2x4 and 2-input NAND decoder with non-power-of-two
|
||||
debug.info(1, "Testing 17 row sample for hierarchical_decoder")
|
||||
a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,12 @@ class hierarchical_predecode2x4_test(openram_test):
|
|||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# checking hierarchical precode 2x4 for single port
|
||||
# Use the 2 port cell since it is usually bigger/easier
|
||||
OPTS.bitcell = "bitcell_1rw_1r"
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
|
||||
debug.info(1, "Testing sample for hierarchy_predecode2x4")
|
||||
a = factory.create(module_type="hierarchical_predecode2x4")
|
||||
self.local_check(a)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,12 @@ class hierarchical_predecode3x8_test(openram_test):
|
|||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# checking hierarchical precode 3x8 for single port
|
||||
# Use the 2 port cell since it is usually bigger/easier
|
||||
OPTS.bitcell = "bitcell_1rw_1r"
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
|
||||
debug.info(1, "Testing sample for hierarchy_predecode3x8")
|
||||
a = factory.create(module_type="hierarchical_predecode3x8")
|
||||
self.local_check(a)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
#!/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
|
||||
|
||||
@unittest.skip("SKIPPING hierarchical_predecode4x16_test")
|
||||
class hierarchical_predecode4x16_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# Use the 2 port cell since it is usually bigger/easier
|
||||
OPTS.bitcell = "bitcell_1rw_1r"
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
|
||||
debug.info(1, "Testing sample for hierarchy_predecode4x16")
|
||||
a = factory.create(module_type="hierarchical_predecode4x16")
|
||||
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())
|
||||
|
|
@ -15,9 +15,8 @@ from globals import OPTS
|
|||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
#@unittest.skip("SKIPPING 04_driver_test")
|
||||
|
||||
class wordline_driver_pbitcell_test(openram_test):
|
||||
class wordline_driver_array_pbitcell_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
|
|
@ -31,7 +30,7 @@ class wordline_driver_pbitcell_test(openram_test):
|
|||
|
||||
factory.reset()
|
||||
debug.info(2, "Checking driver (multi-port case)")
|
||||
tx = factory.create(module_type="wordline_driver", rows=8, cols=64)
|
||||
tx = factory.create(module_type="wordline_driver_array", rows=8, cols=64)
|
||||
self.local_check(tx)
|
||||
|
||||
globals.end_openram()
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#!/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 wordline_driver_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# check wordline driver for single port
|
||||
debug.info(2, "Checking driver")
|
||||
tx = factory.create(module_type="wordline_driver_array", rows=8, cols=32)
|
||||
self.local_check(tx)
|
||||
|
||||
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())
|
||||
|
|
@ -20,10 +20,10 @@ class port_data_1rw_1r_test(openram_test):
|
|||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
OPTS.bitcell = "bitcell_1w_1r"
|
||||
OPTS.num_rw_ports = 0
|
||||
OPTS.bitcell = "bitcell_1rw_1r"
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_w_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
|
||||
c = sram_config(word_size=4,
|
||||
num_words=16)
|
||||
|
|
|
|||
|
|
@ -25,28 +25,28 @@ class single_bank_test(openram_test):
|
|||
c = sram_config(word_size=4,
|
||||
num_words=16)
|
||||
|
||||
# c.words_per_row=1
|
||||
# factory.reset()
|
||||
# c.recompute_sizes()
|
||||
# debug.info(1, "No column mux")
|
||||
# a = factory.create("bank", sram_config=c)
|
||||
# self.local_check(a)
|
||||
c.words_per_row=1
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "No column mux")
|
||||
a = factory.create("bank", sram_config=c)
|
||||
self.local_check(a)
|
||||
|
||||
# c.num_words=32
|
||||
# c.words_per_row=2
|
||||
# factory.reset()
|
||||
# c.recompute_sizes()
|
||||
# debug.info(1, "Two way column mux")
|
||||
# a = factory.create("bank", sram_config=c)
|
||||
# self.local_check(a)
|
||||
c.num_words=32
|
||||
c.words_per_row=2
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "Two way column mux")
|
||||
a = factory.create("bank", sram_config=c)
|
||||
self.local_check(a)
|
||||
|
||||
# c.num_words=64
|
||||
# c.words_per_row=4
|
||||
# factory.reset()
|
||||
# c.recompute_sizes()
|
||||
# debug.info(1, "Four way column mux")
|
||||
# a = factory.create("bank", sram_config=c)
|
||||
# self.local_check(a)
|
||||
c.num_words=64
|
||||
c.words_per_row=4
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "Four way column mux")
|
||||
a = factory.create("bank", sram_config=c)
|
||||
self.local_check(a)
|
||||
|
||||
c.word_size=2
|
||||
c.num_words=128
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ class openram_test(unittest.TestCase):
|
|||
#debug.info(0,"Archiving failed files to {}.zip".format(zip_file))
|
||||
#shutil.make_archive(zip_file, 'zip', OPTS.openram_temp)
|
||||
debug.warning("DRC failed but LVS passed: {}".format(a.name))
|
||||
#self.fail("DRC failed but LVS passed: {}".format(a.name))
|
||||
elif drc_result != 0:
|
||||
#zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid())
|
||||
#debug.info(0,"Archiving failed files to {}.zip".format(zip_file))
|
||||
|
|
|
|||
Loading…
Reference in New Issue