Merge branch 'dev' into char

This commit is contained in:
Bugra Onal 2023-04-12 12:00:31 -07:00
commit dae275c508
146 changed files with 3949 additions and 722 deletions

View File

@ -8,7 +8,6 @@ commit" means.
1. If `regress` workflow fails on 'private/dev', `sync` workflow gets triggered
and it pushes the latest changes to the public repo's 'dev' branch (public/dev).
After this push, `regress` workflow will also run on 'public/dev'.
1. If `regress` workflow successfully passes on 'private/dev', `version`
workflow gets triggered. It creates a new version commit and tag, and pushes to
@ -18,16 +17,6 @@ workflow gets triggered. It creates a new version commit and tag, and pushes to
workflow runs. It deploys the PyPI package of OpenRAM and creates a new GitHub
release on that repo.
1. If there is a pull request on either repo, `regress` workflow runs on that
pull request.
1. If there is a push to 'public/dev', `regress` workflow runs (it also happens
when pull requests are merged).
1. If `regress` workflow successfully passes on 'public/dev', `version`
workflow gets triggered. It creates a new version commit and tag, and pushes to
'private/dev', 'public/dev', and 'public/stable'.
## Important Notes
@ -54,7 +43,7 @@ automatically. That means, you don't have to tag that commit manually.
this commit was automatically generated after a previous commit passed `regress`
workflow or was manually generated with caution.
1. `regress` workflow doesn't run on branches named 'stable'.
1. `regress` workflow doesn't run on the public repo.
1. `deploy` workflow only runs on branches named 'stable'.
@ -63,17 +52,14 @@ workflow or was manually generated with caution.
1. `sync` workflow only runs on the private repo.
1. Pull requests merged on to 'public/dev' will also trigger `regress` and it
can create a new version.
1. Merging pull requests that don't pass `regress` workflow on the public repo
should be avoided since it won't update the private repo automatically. To
prevent merging by mistake, the dev branch can be protected in the GitHub
settings.
1. `sync_tag` workflow only runs on the private repo.
1. Merging pull requests on the private repo should be safe in any case. They
are treated the same as commit pushes.
> **Warning**: `regress` workflow is currently disabled on the public repo
> manually. This was done because of a security risk on our private server.
> Enabling it on GitHub will run `regress` workflow on the public repo.
## Flowchart

View File

@ -69,12 +69,12 @@ $(OPEN_PDKS_DIR): $(SKY130_PDKS_DIR)
$(SKY130_PDK): $(OPEN_PDKS_DIR) $(SKY130_PDKS_DIR)
@echo "Installing open_pdks..."
$(DOCKER_CMD) sh -c ". /home/cad-user/.bashrc && cd /pdk/open_pdks && \
./configure --enable-sky130-pdk=/pdk/skywater-pdk/libraries --with-sky130-local-path=/pdk && \
cd sky130 && \
make veryclean && \
make && \
make SHARED_PDKS_PATH=/pdk install"
@cd $(PDK_ROOT)/open_pdks && \
./configure --enable-sky130-pdk=$(PDK_ROOT)/skywater-pdk/libraries --with-sky130-local-path=$(PDK_ROOT) && \
cd sky130 && \
make veryclean && \
make && \
make SHARED_PDKS_PATH=$(PDK_ROOT) install
$(SRAM_LIB_DIR): check-pdk-root
@echo "Cloning SRAM library..."

View File

@ -1,15 +1,16 @@
![](./images/OpenRAM_logo_yellow_transparent.svg)
![](https://raw.githubusercontent.com/VLSIDA/OpenRAM/stable/images/OpenRAM_logo_yellow_transparent.svg)
# OpenRAM
[![Python 3.5](https://img.shields.io/badge/Python-3.5-green.svg)](https://www.python.org/)
[![License: BSD 3-clause](./images/license_badge.svg)](./LICENSE)
[![Download](./images/download-stable-blue.svg)](https://github.com/VLSIDA/OpenRAM/archive/stable.zip)
[![Download](./images/download-unstable-blue.svg)](https://github.com/VLSIDA/OpenRAM/archive/dev.zip)
[![License: BSD 3-clause](https://raw.githubusercontent.com/VLSIDA/OpenRAM/stable/images/license_badge.svg)](./LICENSE)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/openram?color=brightgreen&label=PyPI)](https://pypi.org/project/openram/)
An open-source static random access memory (SRAM) compiler.
# What is OpenRAM?
<img align="right" width="25%" src="images/SCMOS_16kb_sram.jpg">
<img align="right" width="25%" src="https://raw.githubusercontent.com/VLSIDA/OpenRAM/stable/images/SCMOS_16kb_sram.jpg">
OpenRAM is an award winning open-source Python framework to create the layout,
netlists, timing and power models, placement and routing models, and
@ -17,20 +18,26 @@ other views necessary to use SRAMs in ASIC design. OpenRAM supports
integration in both commercial and open-source flows with both
predictive and fabricable technologies.
# Documentation
Please see our [documentation][documentation] and let us know if anything needs
updating.
# Get Involved
+ [Port it](./PORTING.md) to a new technology.
+ Report bugs by submitting [Github issues].
+ [Port it](./PORTING.md) to a new technology
+ Report bugs by submitting [Github issues]
+ Develop new features (see [how to contribute](./CONTRIBUTING.md))
+ Submit code/fixes using a [Github pull request]
+ Follow our [project][Github project].
+ Follow our [project][Github project]
+ Read and cite our [ICCAD paper][OpenRAMpaper]
# Further Help
+ [Documentation][documentation]
@ -38,9 +45,13 @@ updating.
+ [OpenRAM Users Group][user-group] ([subscribe here][user-group-subscribe])
+ [OpenRAM Developers Group][dev-group] ([subscribe here][dev-group-subscribe])
# License
OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
OpenRAM is licensed under the [BSD 3-Clause License](./LICENSE).
# Publications
@ -53,6 +64,7 @@ OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
+ [H. Nichols, "Statistical Modeling of SRAMs", M.S. Thesis, UCSC, 2022.](https://escholarship.org/content/qt7vx9n089/qt7vx9n089_noSplash_cfc4ba479d8eb1b6ec25d7c92357bc18.pdf?t=ra9wzr)
+ [M. Guthaus, H. Nichols, J. Cirimelli-Low, J. Kunzler, B. Wu, "Enabling Design Technology Co-Optimization of SRAMs though Open-Source Software", IEEE International Electron Devices Meeting (IEDM), 2020.](https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=9372047)
# Contributors & Acknowledgment
@ -62,7 +74,7 @@ OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
If I forgot to add you, please let me know!
* * *
[Matthew Guthaus]: https://users.soe.ucsc.edu/~mrg
[James Stine]: https://ece.okstate.edu/content/stine-james-e-jr-phd

View File

@ -1 +1 @@
1.2.1
1.2.8

View File

@ -44,3 +44,6 @@ from .globals import *
# sram_config should be imported before sram
from .sram_config import *
from .sram import *
from .rom_config import *
from .rom import *

View File

@ -696,13 +696,15 @@ class layout():
start=left_pos,
end=right_pos)
def connect_row_pins(self, layer, pins, name=None, full=False):
def connect_row_pins(self, layer, pins, name=None, full=False, round=False):
"""
Connects left/right rows that are aligned.
"""
bins = {}
for pin in pins:
y = pin.cy()
if round:
y = round_to_grid(y)
try:
bins[y].append(pin)
except KeyError:
@ -785,13 +787,15 @@ class layout():
end=bot_pos)
def connect_col_pins(self, layer, pins, name=None, full=False):
def connect_col_pins(self, layer, pins, name=None, full=False, round=False, directions="pref"):
"""
Connects top/bot columns that are aligned.
"""
bins = {}
for pin in pins:
x = pin.cx()
if round:
x = round_to_grid(x)
try:
bins[x].append(pin)
except KeyError:
@ -817,7 +821,8 @@ class layout():
self.add_via_stack_center(from_layer=pin.layer,
to_layer=layer,
offset=pin.center(),
min_area=True)
min_area=True,
directions=directions)
if name:
self.add_layout_pin_segment_center(text=name,
@ -1892,7 +1897,7 @@ class layout():
elif add_vias:
self.copy_power_pin(pin, new_name=new_name)
def add_io_pin(self, instance, pin_name, new_name, start_layer=None):
def add_io_pin(self, instance, pin_name, new_name, start_layer=None, directions=None):
"""
Add a signle input or output pin up to metal 3.
"""
@ -1902,7 +1907,7 @@ class layout():
start_layer = pin.layer
# Just use the power pin function for now to save code
self.add_power_pin(new_name, pin.center(), start_layer=start_layer)
self.add_power_pin(new_name, pin.center(), start_layer=start_layer, directions=directions)
def add_power_pin(self, name, loc, directions=None, start_layer="m1"):
# Hack for min area

View File

@ -72,6 +72,7 @@ from .replica_pbitcell import *
from .row_cap_array import *
from .row_cap_bitcell_1port import *
from .row_cap_bitcell_2port import *
from .rom_bank import *
from .sense_amp_array import *
from .sense_amp import *
from .tri_gate_array import *

View File

@ -20,3 +20,10 @@ class col_cap_bitcell_1port(bitcell_base):
debug.info(2, "Create col_cap bitcell 1 port object")
self.no_instances = True
def build_graph(self, graph, inst_name, port_nets):
"""
Adds edges based on inputs/outputs.
Overrides base class function.
"""
pass

View File

@ -20,3 +20,10 @@ class col_cap_bitcell_2port(bitcell_base):
debug.info(2, "Create col_cap bitcell 2 port object")
self.no_instances = True
def build_graph(self, graph, inst_name, port_nets):
"""
Adds edges based on inputs/outputs.
Overrides base class function.
"""
pass

View File

@ -15,7 +15,7 @@ class pbuf_dec(pgate):
"""
This is a simple buffer used for driving wordlines.
"""
def __init__(self, name, size=4, height=None):
def __init__(self, name, size=4, height=None, add_wells=True):
debug.info(1, "creating {0} with size of {1}".format(name, size))
self.add_comment("size: {}".format(size))
@ -25,7 +25,7 @@ class pbuf_dec(pgate):
self.height = height
# Creates the netlist and layout
pgate.__init__(self, name, height)
pgate.__init__(self, name, height, add_wells)
def create_netlist(self):
self.add_pins()
@ -51,11 +51,13 @@ class pbuf_dec(pgate):
input_size = max(1, int(self.size / self.stage_effort))
self.inv1 = factory.create(module_type="pinv_dec",
size=input_size,
height=self.height)
height=self.height,
add_wells=self.add_wells)
self.inv2 = factory.create(module_type="pinv_dec",
size=self.size,
height=self.height)
height=self.height,
add_wells=self.add_wells)
def create_insts(self):
self.inv1_inst = self.add_inst(name="buf_inv1",

View File

@ -20,7 +20,7 @@ class pinv_dec(pinv):
Other stuff is the same (netlist, sizes, etc.)
"""
def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True):
def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True, flip_io=False):
debug.info(2,
"creating pinv_dec structure {0} with size of {1}".format(name,
@ -37,7 +37,7 @@ class pinv_dec(pinv):
self.supply_layer = "m1"
else:
self.supply_layer = "m2"
self.flip_io=flip_io
super().__init__(name, size, beta, self.cell_height, add_wells)
def determine_tx_mults(self):
@ -76,9 +76,14 @@ class pinv_dec(pinv):
pmos_gate_pos = pmos_gate_pin.lc()
self.add_path("poly", [nmos_gate_pos, pmos_gate_pos])
# Center is completely symmetric.
contact_width = self.poly_contact.width
contact_offset = nmos_gate_pin.lc() \
if self.flip_io:
contact_offset = pmos_gate_pin.rc() \
+ vector(self.poly_extend_active + 0.6 * contact_width, 0)
else:
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,
@ -107,8 +112,11 @@ class pinv_dec(pinv):
height=self.height - ll.y + 0.5 * self.pwell_contact.height + self.well_enclose_active)
if "nwell" in layer:
ll = (self.pmos_inst.ll() - vector(2 * [self.well_enclose_active])).scale(1, 0)
ur = self.pmos_inst.ur() + vector(2 * [self.well_enclose_active])
poly_offset = 0
if self.flip_io:
poly_offset = 1.2 * self.poly_contact.width
ll = (self.pmos_inst.ll() - vector(2 * [self.well_enclose_active])).scale(1, 0) - vector(0, poly_offset)
ur = self.pmos_inst.ur() + vector(2 * [self.well_enclose_active + poly_offset])
self.add_rect(layer="nwell",
offset=ll,
width=ur.x - ll.x,

View File

@ -132,15 +132,16 @@ class pinvbuf(pgate):
to_layer=a3_pin.layer,
offset=a3_pin.center())
# inv1 Z to inv4 A (up and over)
z1_pin = self.inv1_inst.get_pin("Z")
a4_pin = self.inv4_inst.get_pin("A")
# inv1 Z to inv4 A (up and over)
mid_point = vector(z1_pin.cx(), a4_pin.cy())
self.add_wire(route_stack,
[z1_pin.center(), mid_point, a4_pin.center()])
[z1_pin.center(), mid_point, a4_pin.center()])
self.add_via_stack_center(from_layer=z1_pin.layer,
to_layer=route_stack[2],
offset=z1_pin.center())
to_layer=route_stack[2],
offset=z1_pin.center())
def add_layout_pins(self):

View File

@ -39,7 +39,8 @@ class ptx(design):
connect_drain_active=False,
connect_source_active=False,
connect_poly=False,
num_contacts=None):
num_contacts=None,
):
if "li" in layer:
self.route_layer = "li"
@ -47,11 +48,11 @@ class ptx(design):
self.route_layer = "m1"
# Default contacts are the lowest layer
if not add_source_contact:
if add_source_contact == None:
add_source_contact = self.route_layer
# Default contacts are the lowest layer
if not add_drain_contact:
if add_drain_contact == None:
add_drain_contact = self.route_layer
# We need to keep unique names because outputting to GDSII

View File

@ -0,0 +1,121 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from openram.base import design
from openram.sram_factory import factory
from openram.base import vector
from openram.tech import layer, drc
class rom_address_control_array(design):
"""
Takes the input address lines and creates the address and address bar lines for the decoder.
Adds control logic for the precharge cycle so that all address lines are high before the read cycle
"""
def __init__(self, cols, inv_height=None, inv_size=1, name="", route_layer="m1"):
self.size=inv_size
self.cols = cols
self.route_layer = route_layer
dff = factory.create(module_type="dff")
if name=="":
name = "rom_inv_array_{0}".format(cols)
if inv_height == None:
self.inv_height = dff.height * 0.5
else:
self.inv_height = inv_height
if "li" in layer:
self.inv_layer = "li"
else:
self.inv_layer = "m1"
super().__init__(name)
self.create_netlist()
self.create_layout()
def create_netlist(self):
self.create_modules()
self.add_pins()
self.create_instances()
def create_layout(self):
self.width = self.cols * self.addr_control.width
self.height = self.addr_control.height
self.setup_layout_constants()
self.place_instances()
self.route_clk()
self.route_sources()
self.copy_pins()
self.add_boundary()
def create_modules(self):
self.addr_control = factory.create(module_type="rom_address_control_buf", size=self.inv_height)
def add_pins(self):
for col in range(self.cols):
self.add_pin("A{0}_in".format(col), "INPUT")
for col in range(self.cols):
self.add_pin("A{0}_out".format(col), "OUTPUT")
for col in range(self.cols):
self.add_pin("Abar{0}_out".format(col), "OUTPUT")
self.add_pin("clk", "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_instances(self):
self.buf_insts = []
for col in range(self.cols):
name = "Xaddr_buf_{0}".format(col)
addr_buf = self.add_inst(name=name, mod=self.addr_control)
A_in = "A{0}_in".format(col)
Aout = "A{0}_out".format(col)
Abar_out = "Abar{0}_out".format(col)
self.connect_inst([A_in, Aout, Abar_out, "clk", "vdd", "gnd"])
self.buf_insts.append(addr_buf)
def setup_layout_constants(self):
self.route_width = drc["minwidth_{}".format(self.route_layer)]
def place_instances(self):
for col in range(self.cols):
base = vector((col+1)*(self.addr_control.width), 0)
self.buf_insts[col].place(offset=base, mirror="MY")
def copy_pins(self):
for i in range(self.cols):
self.copy_layout_pin(self.buf_insts[i], "A_out", "A{0}_out".format(i))
self.copy_layout_pin(self.buf_insts[i], "Abar_out", "Abar{0}_out".format(i))
self.copy_layout_pin(self.buf_insts[i], "A_in", "A{0}_in".format(i))
def route_clk(self):
self.route_horizontal_pins("clk", insts=self.buf_insts, layer=self.route_layer)
def route_sources(self):
self.route_horizontal_pins("vdd", insts=self.buf_insts, layer=self.route_layer)
self.route_horizontal_pins("gnd", insts=self.buf_insts, layer=self.route_layer)
tmp_pins = []
for pin in self.get_pins("vdd"):
edge = vector(pin.lx() + 0.5 * self.route_width, pin.cy())
tmp_pins.append(self.add_layout_pin_rect_center("vdd_edge", layer=self.route_layer, offset=edge))
self.copy_layout_pin_shapes("vdd")
self.remove_layout_pin("vdd")
for pin in tmp_pins:
self.copy_layout_pin(self, "vdd_edge", "vdd")
self.remove_layout_pin("vdd_edge")

View File

@ -0,0 +1,185 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from openram.base import design
from openram.sram_factory import factory
from openram.base import vector
from openram.tech import layer, drc
class rom_address_control_buf(design):
"""
Takes the input address lines and creates the address and address bar lines for the decoder.
Adds control logic for the precharge cycle so that all address lines are high before the read cycle
"""
def __init__(self, size, name="", route_layer="m1", add_wells=False):
self.route_layer = route_layer
self.add_wells = add_wells
self.size = size
if "li" in layer:
self.inv_layer = "li"
else:
self.inv_layer = "m1"
super().__init__(name)
self.create_netlist()
self.create_layout()
def create_netlist(self):
self.create_modules()
self.add_pins()
self.create_instances()
def create_layout(self):
self.width = self.cell.height * 2
self.height = self.inv.width + 2 * self.nand.width
self.setup_layout_constants()
self.place_instances()
self.route_gates()
self.route_sources()
self.add_boundary()
def create_modules(self):
self.inv = factory.create(module_type="pinv_dec", module_name="inv_array_mod", add_wells=False, size=self.size)
self.nand = factory.create(module_type="nand2_dec", height=self.inv.height)
# For layout constants
self.cell = factory.create(module_type="rom_base_cell")
def add_pins(self):
self.add_pin("A_in", "INPUT")
self.add_pin("A_out", "INOUT")
self.add_pin("Abar_out", "OUTPUT")
self.add_pin("clk", "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_instances(self):
name = "XinvAbar"
self.inv_inst = self.add_inst(name=name, mod=self.inv)
inst_A = "A_in"
inst_Z = "Abar_internal"
self.connect_inst([inst_A, inst_Z, "vdd", "gnd"])
name = "Xnand_addr"
self.addr_nand = self.add_inst(name=name, mod=self.nand)
inst_A = "clk"
inst_B = "Abar_internal"
inst_Z = "A_out"
self.connect_inst([inst_A, inst_B, inst_Z, "vdd", "gnd"])
name = "Xnand_addr_bar"
self.addr_bar_nand = self.add_inst(name=name, mod=self.nand)
inst_A = "clk"
inst_B = "A_out"
inst_Z = "Abar_out"
self.connect_inst([inst_A, inst_B, inst_Z, "vdd", "gnd"])
def setup_layout_constants(self):
self.route_width = drc["minwidth_{}".format(self.route_layer)]
self.interconnect_width = drc["minwidth_{}".format(self.inv_layer)]
def place_instances(self):
self.inv_inst.place(offset=vector(self.inv_inst.height,0), rotate=90)
self.addr_nand.place(offset=vector(self.addr_nand.height , self.inv_inst.width + self.route_width ), rotate=90)
self.addr_bar_nand.place(offset=vector( self.addr_bar_nand.height, self.addr_nand.width + self.inv_inst.width + self.route_width), rotate=90)
def route_gates(self):
clk1_pin = self.addr_nand.get_pin("A")
clk2_pin = self.addr_bar_nand.get_pin("A")
Abar_out = self.addr_bar_nand.get_pin("Z")
A_out = self.addr_nand.get_pin("Z")
Abar_in = self.addr_nand.get_pin("B")
Abar_int_out = self.inv_inst.get_pin("Z")
Aint_in = self.addr_bar_nand.get_pin("B")
A_in = self.inv_inst.get_pin("A")
# Find the center of the pmos poly/gate
poly_right = clk1_pin.cx() + self.poly_enclose_contact + 0.5 * self.contact_width
ppoly_center = poly_right - 0.7 * self.poly_width
contact_offset = vector(ppoly_center, clk2_pin.cy())
# Route the two shared clk inputs together by connecting poly
self.add_segment_center("poly", contact_offset, vector(ppoly_center, A_out.cy()))
clk_offset = vector(clk2_pin.cx(), self.addr_nand.uy())
self.add_layout_pin_rect_center("clk", offset=clk_offset, layer=self.route_layer)
self.add_via_stack_center(from_layer=self.inv_layer, to_layer=self.route_layer, offset=self.addr_bar_nand.get_pin("A").center())
self.add_segment_center(self.route_layer, clk_offset, vector(clk_offset.x, clk2_pin.cy()))
# Route first NAND output to second NAND input
start = A_out.center()
end = Aint_in.center()
self.add_path("m2", [start, end])
self.add_via_stack_center(Aint_in.center(), self.inv_layer, "m2")
self.add_via_stack_center(A_out.center(), self.inv_layer, "m2")
# Route first NAND to output pin
self.add_segment_center("m2", end, vector(end.x, self.addr_bar_nand.uy()))
self.add_layout_pin_rect_center("A_out", offset=vector(end.x, self.addr_bar_nand.uy() - 0.5 * self.m2_width), layer="m2")
# Route second NAND to output pin
self.add_via_stack_center(Abar_out.center(), self.inv_layer, "m2")
self.add_segment_center("m2", Abar_out.center(), vector(Abar_out.cx(), self.addr_bar_nand.uy()))
self.add_layout_pin_rect_center("Abar_out", offset=vector(Abar_out.cx(), self.addr_bar_nand.uy() - 0.5 * self.m2_width), layer="m2")
# Route inverter output to NAND
end = vector(Abar_int_out.cx(), Abar_in.cy() + 0.5 * self.interconnect_width)
self.add_segment_center(self.inv_layer, Abar_int_out.center(), end)
self.copy_layout_pin(self.inv_inst, "A", "A_in")
def route_sources(self):
self.copy_layout_pin(self.addr_nand, "vdd")
self.copy_layout_pin(self.addr_bar_nand, "vdd")
self.copy_layout_pin(self.inv_inst, "vdd")
self.copy_layout_pin(self.addr_bar_nand, "gnd")
self.copy_layout_pin(self.addr_nand, "gnd")
self.copy_layout_pin(self.inv_inst, "gnd")
""" Add n/p well taps to the layout and connect to supplies """
source_pin = self.inv_inst.get_pin("vdd")
gnd_pin = self.inv_inst.get_pin("gnd")
left_edge = self.inv_inst.get_pin("Z").cx() - 2 * self.contact_width - 2 * self.active_contact_to_gate - 4 * self.active_enclose_contact - self.poly_width - self.active_space
contact_pos = vector(left_edge, source_pin.cy())
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.route_layer)
contact_pos = vector(left_edge, gnd_pin.cy())
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.route_layer)

View File

@ -0,0 +1,512 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import datetime
from math import ceil, log
from openram.base import vector
from openram.base import design
from openram import OPTS, print_time
from openram.sram_factory import factory
from openram.tech import drc, layer, parameter
from openram.router import router_tech
class rom_bank(design):
"""
Rom data bank with row and column decoder + control logic
word size is in bytes
"""
def __init__(self, name, rom_config):
super().__init__(name=name)
self.rom_config = rom_config
rom_config.set_local_config(self)
self.word_size = self.word_bits
self.num_outputs = self.rows
self.num_inputs = ceil(log(self.rows, 2))
self.col_bits = ceil(log(self.words_per_row, 2))
self.row_bits = self.num_inputs
self.tap_spacing = self.strap_spacing
try:
from openram.tech import power_grid
self.supply_stack = power_grid
except ImportError:
# if no power_grid is specified by tech we use sensible defaults
# Route a M3/M4 grid
self.supply_stack = self.m3_stack
self.interconnect_layer = "m1"
self.bitline_layer = "m1"
self.wordline_layer = "m2"
if "li" in layer:
self.route_stack = self.m1_stack
else:
self.route_stack = self.m2_stack
self.route_layer = self.route_stack[0]
if OPTS.is_unit_test:
self.create_netlist()
self.create_layout()
def create_netlist(self):
start_time = datetime.datetime.now()
self.add_modules()
self.add_pins()
self.create_instances()
if not OPTS.is_unit_test:
print_time("Submodules", datetime.datetime.now(), start_time)
def create_layout(self):
start_time = datetime.datetime.now()
self.setup_layout_constants()
self.place_instances()
if not OPTS.is_unit_test:
print_time("Placement", datetime.datetime.now(), start_time)
self.add_boundary()
start_time = datetime.datetime.now()
self.route_layout()
if not OPTS.is_unit_test:
print_time("Routing", datetime.datetime.now(), start_time)
start_time = datetime.datetime.now()
if not OPTS.is_unit_test:
# We only enable final verification if we have routed the design
# Only run this if not a unit test, because unit test will also verify it.
self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=OPTS.check_lvsdrc)
print_time("Verification", datetime.datetime.now(), start_time)
def add_boundary(self):
ll = self.find_lowest_coords()
m1_offset = self.m1_width
self.translate_all(vector(0, ll.y))
ur = self.find_highest_coords()
ur = vector(ur.x, ur.y)
super().add_boundary(vector(0, 0), ur)
self.width = ur.x
self.height = ur.y
def route_layout(self):
self.route_decode_outputs()
self.route_precharge()
self.route_clock()
self.route_array_outputs()
self.place_top_level_pins()
self.route_output_buffers()
rt = router_tech(self.supply_stack, 1)
init_bbox = self.get_bbox(side="ring",
margin=rt.track_width)
self.route_supplies(init_bbox)
# We need the initial bbox for the supply rings later
# because the perimeter pins will change the bbox
# Route the pins to the perimeter
if OPTS.perimeter_pins:
# We now route the escape routes far enough out so that they will
# reach past the power ring or stripes on the sides
bbox = self.get_bbox(side="ring",
margin=11*rt.track_width)
self.route_escape_pins(bbox)
def setup_layout_constants(self):
self.route_layer_width = drc["minwidth_{}".format(self.route_stack[0])]
self.route_layer_pitch = drc["{0}_to_{0}".format(self.route_stack[0])]
self.interconnect_layer_width = drc["minwidth_{}".format(self.interconnect_layer)]
self.interconnect_layer_pitch = drc["{0}_to_{0}".format(self.interconnect_layer)]
def add_pins(self):
self.add_pin("clk", "INPUT")
self.add_pin("cs", "INPUT")
for i in range(self.row_bits + self.col_bits):
self.add_pin("addr[{}]".format(i), "INPUT")
out_pins = []
for j in range(self.word_size):
out_pins.append("dout[{}]".format(j))
self.add_pin_list(out_pins, "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
# TODO: provide technology-specific calculation of these parameters
# in sky130 the address control buffer is composed of 2 size 2 NAND gates,
# with a beta of 3, each of these gates has gate capacitance of 2 min sized inverters, therefor a load of 4
addr_control_buffer_effort = parameter['beta'] + 1
# a single min sized nmos makes up 1/4 of the input capacitance of a min sized inverter
bitcell_effort = 0.25
# Takes into account inverter sizing
wordline_effort = bitcell_effort * 0.5
# a single min sized pmos plus a single min sized nmos have approximately half the gate capacitance of a min inverter
# an additional 0.2 accounts for the long wire capacitance and add delay to gaurentee the read timing
precharge_cell_effort = 0.5 + 0.2
self.array = factory.create(module_type="rom_base_array",
cols=self.cols,
rows=self.rows,
strap_spacing=self.strap_spacing,
bitmap=self.data,
bitline_layer=self.bitline_layer,
wordline_layer=self.wordline_layer,
pitch_match=True,
tap_spacing=self.tap_spacing)
self.decode_array = factory.create(module_name="rom_row_decode",
module_type="rom_decoder",
num_outputs=self.rows,
strap_spacing=self.strap_spacing,
route_layer=self.route_layer,
fanout=(self.cols)*wordline_effort )
self.column_mux = factory.create(module_type="rom_column_mux_array",
columns=self.cols,
word_size=self.word_size,
tap_spacing=self.strap_spacing,
bitline_layer=self.interconnect_layer,
input_layer=self.bitline_layer)
self.column_decode = factory.create(module_name="rom_column_decode",
module_type="rom_decoder",
num_outputs=self.words_per_row,
strap_spacing=self.strap_spacing,
route_layer=self.route_layer,
fanout=2,
invert_outputs=True )
self.control_logic = factory.create(module_type="rom_control_logic",
num_outputs=(self.cols + self.words_per_row * precharge_cell_effort) \
+ (addr_control_buffer_effort * self.col_bits),
clk_fanout=(self.row_bits * addr_control_buffer_effort) + (precharge_cell_effort * self.rows),
height=self.column_decode.height )
self.bitline_inv = factory.create(module_type="rom_wordline_driver_array",
module_name="rom_bitline_inverter",
rows=self.cols,
fanout=4,
invert_outputs=True,
tap_spacing=0,
flip_io=True)
self.output_inv = factory.create(module_type="rom_wordline_driver_array",
module_name="rom_output_buffer",
rows=self.word_size,
fanout=4,
tap_spacing=1,
invert_outputs=True)
def create_instances(self):
gnd = ["gnd"]
vdd = ["vdd"]
prechrg = ["precharge"]
clk = ["clk_int"]
bitlines = ["bl_{}".format(bl) for bl in range(self.cols)]
wordlines = ["wl_{}".format(wl) for wl in range(self.rows)]
addr_msb = ["addr[{}]".format(addr + self.col_bits) for addr in range(self.row_bits)]
addr_lsb = ["addr[{}]".format(addr) for addr in range(self.col_bits)]
select_lines = ["word_sel_{}".format(word) for word in range(self.words_per_row)]
bitline_bar = ["bl_b_{}".format(bl) for bl in range(self.cols)]
pre_buf_outputs = ["rom_out_prebuf_{}".format(bit) for bit in range(self.word_size)]
outputs = ["dout[{}]".format(bl) for bl in range(self.word_size)]
array_pins = bitlines + wordlines + prechrg + vdd + gnd
row_decode_pins = addr_msb + wordlines + clk + clk + vdd + gnd
col_decode_pins = addr_lsb + select_lines + prechrg + prechrg + vdd + gnd
col_mux_pins = bitline_bar + select_lines + pre_buf_outputs + gnd
bitline_inv_pins = bitlines + bitline_bar + vdd + gnd
output_buf_pins = pre_buf_outputs + outputs + vdd + gnd
self.array_inst = self.add_inst(name="rom_bit_array", mod=self.array)
self.connect_inst(array_pins)
self.decode_inst = self.add_inst(name="rom_row_decoder", mod=self.decode_array)
self.connect_inst(row_decode_pins)
self.control_inst = self.add_inst(name="rom_control", mod=self.control_logic)
self.connect_inst(["clk", "cs", "precharge", "clk_int", "vdd", "gnd"])
self.mux_inst = self.add_inst(name="rom_column_mux", mod=self.column_mux)
self.connect_inst(col_mux_pins)
self.col_decode_inst = self.add_inst(name="rom_column_decoder", mod=self.column_decode)
self.connect_inst(col_decode_pins)
self.bitline_inv_inst = self.add_inst(name="rom_bitline_inverter", mod=self.bitline_inv)
self.connect_inst(bitline_inv_pins)
self.output_inv_inst = self.add_inst(name="rom_output_inverter", mod=self.output_inv)
self.connect_inst(output_buf_pins)
def place_instances(self):
self.place_row_decoder()
self.place_data_array()
self.place_bitline_inverter()
self.place_col_mux()
self.place_col_decoder()
self.place_control_logic()
self.place_output_buffer()
def place_row_decoder(self):
self.decode_offset = vector(0, self.control_inst.height )
self.decode_inst.place(offset=self.decode_offset)
def place_data_array(self):
# We approximate the correct position for the array
array_x = self.decode_inst.width + (2) * ( self.route_layer_width + self.route_layer_pitch )
array_y = self.decode_array.buf_inst.height - self.array.precharge_inst.cy() - self.array.zero_cell.height * 0.5
self.array_offset = vector(array_x ,array_y)
self.array_inst.place(offset=self.array_offset)
# now move array to correct alignment with decoder
array_align = self.decode_inst.get_pin("wl_0").cy() - self.array_inst.get_pin("wl_0_0").cy()
self.array_inst.place(offset=(self.array_offset + vector(0, array_align)))
def place_bitline_inverter(self):
self.bitline_inv_inst.place(offset=[0,0], rotate=90)
inv_y_offset = self.array_inst.by() - self.bitline_inv_inst.width - 2 * self.m1_pitch
inv_x_offset = self.array_inst.get_pin("bl_0_0").cx() - self.bitline_inv_inst.get_pin("out_0").cx()
self.inv_offset = vector(inv_x_offset, inv_y_offset)
self.bitline_inv_inst.place(offset=self.inv_offset, rotate=90)
def place_control_logic(self):
self.control_offset = vector(self.col_decode_inst.lx() - self.control_inst.width - 3 * self.m1_pitch, self.decode_inst.by() - self.control_logic.height - self.m1_pitch)
self.control_inst.place(offset=self.control_offset)
def place_col_decoder(self):
col_decode_y = self.mux_inst.get_pin("sel_0").cy() - self.col_decode_inst.get_pin("wl_0").cy()
self.col_decode_offset = vector(self.decode_inst.width - self.col_decode_inst.width, col_decode_y)
self.col_decode_inst.place(offset=self.col_decode_offset)
def place_col_mux(self):
mux_y_offset = self.bitline_inv_inst.by() - self.mux_inst.height - 5 * self.route_layer_pitch
mux_x_offset = self.bitline_inv_inst.get_pin("out_0").cx() - self.mux_inst.get_pin("bl_0").cx()
self.mux_offset = vector(mux_x_offset, mux_y_offset)
self.mux_inst.place(offset=self.mux_offset)
def place_output_buffer(self):
output_x = self.col_decode_inst.rx() + self.output_inv_inst.height
output_y = self.mux_inst.by() - self.word_size * self.m1_pitch
self.output_inv_offset = vector(output_x, output_y)
self.output_inv_inst.place(offset=self.output_inv_offset, rotate=270)
def route_decode_outputs(self):
# for the row decoder
route_pins = [self.array_inst.get_pin("wl_0_{}".format(wl)) for wl in range(self.rows)]
decode_pins = [self.decode_inst.get_pin("wl_{}".format(wl)) for wl in range(self.rows)]
route_pins.extend(decode_pins)
self.connect_row_pins(self.interconnect_layer, route_pins, round=True)
# then for the column decoder
col_decode_pins = [self.col_decode_inst.get_pin("wl_{}".format(wl)) for wl in range(self.words_per_row)]
sel_pins = [self.mux_inst.get_pin("sel_{}".format(wl)) for wl in range(self.words_per_row)]
sel_pins.extend(col_decode_pins)
self.connect_row_pins(self.wordline_layer, sel_pins, round=True)
def route_array_inputs(self):
for wl in range(self.rows):
array_wl = self.array.wordline_names[0][wl]
array_wl_pin = self.array_inst.get_pin(array_wl)
wl_bus_wire = self.wl_bus[self.wl_interconnects[wl]]
end = array_wl_pin.center()
start = vector(wl_bus_wire.cx(), end.y)
self.add_segment_center(self.interconnect_layer, start, end)
def route_precharge(self):
prechrg_control = self.control_inst.get_pin("prechrg")
col_decode_prechrg = self.col_decode_inst.get_pin("precharge_r")
col_decode_clk = self.col_decode_inst.get_pin("clk")
array_prechrg = self.array_inst.get_pin("precharge")
self.add_via_stack_center(from_layer=self.route_stack[0],
to_layer=prechrg_control.layer,
offset=prechrg_control.center())
# Route precharge to col decoder
start = prechrg_control.center()
mid1 = vector(self.control_inst.rx() + self.interconnect_layer_pitch, prechrg_control.cy())
mid2 = vector(self.control_inst.rx() + self.interconnect_layer_pitch, col_decode_prechrg.cy())
end = col_decode_prechrg.center()
self.add_path(self.route_stack[0], [start, mid1, mid2, end])
self.add_via_stack_center(from_layer=self.route_stack[0],
to_layer=col_decode_prechrg.layer,
offset=end)
start = mid1
mid1 = vector(self.control_inst.rx() + self.interconnect_layer_pitch, start.y)
mid2 = vector(mid1.x, col_decode_clk.cy())
end = col_decode_clk.center()
self.add_path(self.route_stack[0], [start, mid1, mid2, end])
# Route precharge to main array
mid = vector(col_decode_prechrg.cx(), array_prechrg.cy() )
self.add_path(self.route_stack[0], [array_prechrg.center(), mid, col_decode_prechrg.center()])
def route_clock(self):
clk_out = self.control_inst.get_pin("clk_out")
row_decode_clk = self.decode_inst.get_pin("clk")
self.add_via_stack_center(from_layer=self.route_stack[2],
to_layer=clk_out.layer,
offset=clk_out.center())
# Route clock to row decoder
mid = vector(self.control_inst.rx() + self.m1_pitch, clk_out.cy())
addr_control_clk = row_decode_clk.rc() + vector( 2 * self.route_layer_pitch + self.route_layer_width, 0)
row_decode_prechrg = self.decode_inst.get_pin("precharge")
self.add_path(self.route_stack[2], [clk_out.center(), mid, addr_control_clk, row_decode_prechrg.center()])
self.add_via_stack_center(from_layer=self.route_stack[2],
to_layer=row_decode_clk.layer,
offset=addr_control_clk)
self.add_segment_center(row_decode_clk.layer, addr_control_clk, row_decode_clk.rc())
def route_array_outputs(self):
array_out_pins = [self.array_inst.get_pin("bl_0_{}".format(bl)) for bl in range(self.cols)]
inv_in_pins = [self.bitline_inv_inst.get_pin("in_{}".format(bl)) for bl in range(self.cols)]
inv_out_pins = [self.bitline_inv_inst.get_pin("out_{}".format(bl)) for bl in range(self.cols)]
mux_pins = [self.mux_inst.get_pin("bl_{}".format(bl)) for bl in range(self.cols)]
self.connect_col_pins(self.interconnect_layer, array_out_pins + inv_in_pins, round=True, directions="nonpref")
self.connect_col_pins(self.interconnect_layer, inv_out_pins + mux_pins, round=True, directions="nonpref")
def route_output_buffers(self):
mux = self.mux_inst
buf = self.output_inv_inst
route_nets = [ [mux.get_pin("bl_out_{}".format(bit)), buf.get_pin("in_{}".format(bit))] for bit in range(self.word_size)]
channel_ll = vector( route_nets[0][0].cx(), route_nets[0][1].cy() + self.m1_pitch * 3)
self.create_horizontal_channel_route(netlist=route_nets, offset=channel_ll, layer_stack=self.m1_stack)
def place_top_level_pins(self):
self.add_io_pin(self.control_inst, "CS", "cs")
self.add_io_pin(self.control_inst, "clk_in", "clk")
for i in range(self.word_size):
self.add_io_pin(self.output_inv_inst, "out_{}".format(i), "dout[{}]".format(i), directions="nonpref")
for lsb in range(self.col_bits):
name = "addr[{}]".format(lsb)
self.add_io_pin(self.col_decode_inst, "A{}".format(lsb), name)
for msb in range(self.col_bits, self.row_bits + self.col_bits):
name = "addr[{}]".format(msb)
pin_num = msb - self.col_bits
self.add_io_pin(self.decode_inst, "A{}".format(pin_num), name)
def route_supplies(self, bbox=None):
for pin_name in ["vdd", "gnd"]:
for inst in self.insts:
self.copy_power_pins(inst, pin_name)
if not OPTS.route_supplies:
# Do not route the power supply (leave as must-connect pins)
return
elif OPTS.route_supplies == "grid":
from openram.router import supply_grid_router as router
else:
from openram.router import supply_tree_router as router
rtr=router(layers=self.supply_stack,
design=self,
bbox=bbox,
pin_type=OPTS.supply_pin_type)
rtr.route()
if OPTS.supply_pin_type in ["left", "right", "top", "bottom", "ring"]:
# Find the lowest leftest pin for vdd and gnd
for pin_name in ["vdd", "gnd"]:
# Copy the pin shape(s) to rectangles
for pin in self.get_pins(pin_name):
self.add_rect(layer=pin.layer,
offset=pin.ll(),
width=pin.width(),
height=pin.height())
# Remove the pin shape(s)
self.remove_layout_pin(pin_name)
# Get new pins
pins = rtr.get_new_pins(pin_name)
for pin in pins:
self.add_layout_pin(pin_name,
pin.layer,
pin.ll(),
pin.width(),
pin.height())
def route_escape_pins(self, bbox):
pins_to_route = []
for bit in range(self.col_bits):
pins_to_route.append("addr[{0}]".format(bit))
for bit in range(self.row_bits):
pins_to_route.append("addr[{0}]".format(bit + self.col_bits))
for bit in range(self.word_size):
pins_to_route.append("dout[{0}]".format(bit))
pins_to_route.append("clk")
pins_to_route.append("cs")
from openram.router import signal_escape_router as router
rtr=router(layers=self.m3_stack,
design=self,
bbox=bbox)
rtr.escape_route(pins_to_route)

View File

@ -0,0 +1,377 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import math
from .bitcell_base_array import bitcell_base_array
from openram.base import vector
from openram import OPTS, debug
from openram.sram_factory import factory
from openram.tech import drc, layer
class rom_base_array(bitcell_base_array):
def __init__(self, rows, cols, strap_spacing, bitmap, tap_spacing = 4, name="", bitline_layer="m1", wordline_layer="m2", tap_direction="row", pitch_match=False):
super().__init__(name=name, rows=rows, cols=cols, column_offset=0)
self.data = bitmap
self.tap_direction = tap_direction
self.pitch_match = pitch_match
self.bitline_layer = bitline_layer
self.strap_spacing = strap_spacing
self.wordline_layer = wordline_layer
self.data_col_size = self.column_size
self.tap_spacing = tap_spacing
if strap_spacing != 0:
self.array_col_size = self.column_size + math.ceil(self.column_size / strap_spacing)
else:
self.array_col_size = self.column_size
self.create_all_bitline_names()
self.create_all_wordline_names()
# debug.info(1, "ROM array with rows: {0}, cols: {1}".format(self.row_size, self.column_size))
self.create_netlist()
self.create_layout()
def create_netlist(self):
self.add_modules()
self.add_pins()
self.create_cell_instances()
self.create_precharge_inst()
def create_layout(self):
self.create_layout_constants()
self.place_array()
if self.tap_direction == "row":
self.route_pitch_offsets()
self.place_precharge()
self.place_wordline_contacts()
self.place_bitline_contacts()
self.route_precharge()
self.add_boundary()
self.route_supplies()
self.connect_taps()
def add_boundary(self):
ll = self.find_lowest_coords()
m1_offset = self.m1_width
self.translate_all(vector(0, ll.y + 0.5 * m1_offset))
ur = self.find_highest_coords()
ur = vector(ur.x, ur.y - self.m1_width)
super().add_boundary(vector(0, 0), ur)
self.width = ur.x
self.height = ur.y
def add_modules(self):
self.zero_cell = factory.create(module_name="rom_base_zero_cell",
module_type="rom_base_cell",
bitline_layer=self.bitline_layer,
bit_value=0)
self.one_cell = factory.create(module_name="rom_base_one_cell",
module_type="rom_base_cell",
bitline_layer=self.bitline_layer,
bit_value=1)
if self.tap_direction == "row":
self.poly_tap = factory.create(module_type="rom_poly_tap")
else:
self.poly_tap = factory.create(module_type="rom_poly_tap", add_active_tap=True)
self.end_poly_tap = factory.create(module_type="rom_poly_tap", place_poly=True)
self.precharge_array = factory.create(module_type="rom_precharge_array",
cols=self.column_size,
strap_spacing=self.strap_spacing,
bitline_layer=self.bitline_layer,
strap_layer=self.wordline_layer,
tap_direction=self.tap_direction)
def create_layout_constants(self):
self.route_width = drc("minwidth_" + self.bitline_layer)
self.route_pitch = drc("{0}_to_{0}".format(self.bitline_layer))
def add_pins(self):
for bl_name in self.get_bitline_names():
self.add_pin(bl_name, "OUTPUT")
for wl_name in self.get_wordline_names():
self.add_pin(wl_name, "INPUT")
self.add_pin("precharge", "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_cell_instances(self):
self.tap_inst = {}
self.active_tap_list = []
self.poly_tap_list = []
self.cell_inst = {}
self.cell_list = []
self.current_row = 0
# list of current bitline interconnect nets,
# starts as the same as the bitline list and is updated when new insts of cells are added
self.int_bl_list = self.bitline_names[0].copy()
for row in range(self.row_size + 1):
row_list = []
for col in range(self.column_size):
if col % self.strap_spacing == 0:
self.create_poly_tap(row, col)
new_inst = self.create_cell(row, col)
self.cell_inst[row, col] = new_inst
row_list.append(new_inst)
self.create_poly_tap(row, self.column_size)
# name = "tap_r{0}_c{1}".format(row, self.array_col_size)
# new_tap = self.add_inst(name=name, mod=self.poly_tap)
# self.tap_inst[row, self.column_size] = new_tap
# self.tap_list.append(new_tap)
# self.connect_inst([])
self.cell_list.append(row_list)
def create_poly_tap(self, row, col):
name = "tap_r{0}_c{1}".format(row, col)
if row == self.row_size and self.tap_direction == "col":
new_tap = self.add_inst(name=name, mod=self.end_poly_tap)
else:
new_tap = self.add_inst(name=name, mod=self.poly_tap)
self.active_tap_list.append(new_tap)
self.tap_inst[row, col]=new_tap
self.poly_tap_list.append(new_tap)
self.connect_inst([])
def create_cell(self, row, col):
name = "bit_r{0}_c{1}".format(row, col)
# when col = 0, bl_h is connected to precharge, otherwise connect to previous bl connection
# when col = col_size - 1 connected column_sizeto gnd otherwise create new bl connection
# debug.info(1, "Create cell: r{0}, c{1}".format(row, col))
if row == self.row_size:
bl_l = self.int_bl_list[col]
bl_h = "gnd"
else:
bl_l = self.int_bl_list[col]
if self.data[row][col] == 1:
self.int_bl_list[col] = "bl_int_{0}_{1}".format(row, col)
bl_h = self.int_bl_list[col]
# Final row of dummy nmos that contains only 1s, acts to prevent shorting bl to ground when precharging
if row == self.row_size:
new_inst = self.add_inst(name=name, mod=self.one_cell)
self.connect_inst([bl_h, bl_l, "precharge", "gnd"])
elif self.data[row][col] == 1:
new_inst = self.add_inst(name=name, mod=self.one_cell)
self.connect_inst([bl_h, bl_l, self.wordline_names[0][row], "gnd"])
else:
new_inst = self.add_inst(name=name, mod=self.zero_cell)
self.connect_inst([bl_h, self.wordline_names[0][row], "gnd"])
return new_inst
def create_precharge_inst(self):
prechrg_pins = self.bitline_names[0].copy()
prechrg_pins.append("precharge")
prechrg_pins.append("vdd")
self.precharge_inst = self.add_inst(name="bitcell_array_precharge", mod=self.precharge_array)
self.connect_inst(prechrg_pins)
def create_all_bitline_names(self):
for col in range(self.column_size):
for port in self.all_ports:
self.bitline_names[port].extend(["bl_{0}_{1}".format(port, col)])
# Make a flat list too
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
def route_supplies(self):
via_width = drc("m2_enclose_via1") * 0.5 + drc("minwidth_via1")
pitch = drc["{0}_to_{0}".format(self.wordline_layer)]
drain_l = self.cell_list[self.row_size][0].get_pin("D")
drain_r = self.cell_list[self.row_size][self.column_size - 1].get_pin("D")
gnd_l = drain_l.center() + vector(-0.5 * self.route_width, pitch + via_width + self.route_pitch)
gnd_r = drain_r.center() + vector(0.5 * self.route_width, pitch + via_width + self.route_pitch)
self.add_layout_pin_rect_ends(name="gnd", layer=self.bitline_layer, start=gnd_l, end=gnd_r)
if self.tap_direction == "row":
self.connect_row_pins(layer=self.wordline_layer, pins=self.gnd_taps, name="gnd")
self.remove_layout_pin("gnd_tap")
if self.tap_direction == "col":
self.remove_layout_pin("gnd")
active_tap_pins = [self.active_tap_list[i].get_pin("active_tap") for i in range(len(self.active_tap_list))]
self.connect_col_pins(layer=self.supply_stack[0], pins=active_tap_pins, name="gnd_tmp")
gnd_y = gnd_l.y
min_x = float('inf')
max_x = 0
for pin in self.get_pins("gnd_tmp"):
# find the pins on the edges
if pin.cx() < min_x:
min_x = pin.cx()
if pin.cx() > max_x:
max_x = pin.cx()
bottom = vector(pin.cx(), pin.by())
top = vector(pin.cx(), gnd_y)
self.add_via_stack_center(offset=top, from_layer=self.bitline_layer, to_layer=self.supply_stack[0])
self.add_via_center(offset=bottom, layers=self.supply_stack)
self.add_layout_pin_rect_ends(name="gnd", layer=self.supply_stack[0], start=bottom, end=top)
self.remove_layout_pin("gnd_tmp")
self.add_segment_center(layer=self.supply_stack[2], start=vector(min_x, bottom.y), end=vector(max_x, bottom.y))
self.add_segment_center(layer=self.bitline_layer, start=gnd_l, end=vector(min_x, gnd_l.y))
self.add_segment_center(layer=self.bitline_layer, start=gnd_r, end=vector(max_x, gnd_r.y))
self.copy_layout_pin(self.precharge_inst, "vdd")
def place_array(self):
self.cell_pos = {}
self.strap_pos = {}
pitch_offset = 0
for row in range(self.row_size + 1):
if row % self.tap_spacing == 0 and self.pitch_match and row != self.row_size:
pitch_offset += self.active_contact.width + self.active_space
cell_y = row * (self.zero_cell.height) + pitch_offset
cell_x = 0
for col in range(self.column_size):
if col % self.strap_spacing == 0:
self.strap_pos[row, col] = vector(cell_x, cell_y)
self.tap_inst[row, col].place(self.strap_pos[row, col])
if self.tap_direction == "col":
cell_x += self.poly_tap.pitch_offset
self.cell_pos[row, col] = vector(cell_x, cell_y)
self.cell_inst[row, col].place(self.cell_pos[row, col])
cell_x += self.zero_cell.width
self.strap_pos[row, self.column_size] = vector(cell_x, cell_y)
self.tap_inst[row, self.column_size].place(self.strap_pos[row, self.column_size])
def route_pitch_offsets(self):
self.gnd_taps = []
for row in range(0 , self.row_size, self.tap_spacing):
for col in range(self.column_size):
cell = self.cell_inst[row, col]
source = cell.get_pin("S")
if row != 0:
drain = self.cell_inst[row - 1, col].get_pin("D")
start = vector(drain.cx(), source.cy())
end = drain.center()
self.add_segment_center(self.bitline_layer, start, end)
self.place_well_tap(row, col)
def place_well_tap(self, row, col):
cell = self.cell_inst[row, col]
source = cell.get_pin("S")
if col != self.column_size - 1:
tap_x = (self.cell_inst[row , col + 1].get_pin("S").cx() + source.cx()) * 0.5
else:
tap_x = cell.rx() + self.active_space
if row != 0:
drain = self.cell_inst[row - 1, col].get_pin("D")
tap_y = (source.cy() + drain.cy()) * 0.5
else:
tap_y = source.cy() - self.contact_width - 2 * self.active_enclose_contact - self.active_space
tap_pos = vector(tap_x, tap_y)
self.add_via_center(layers=self.active_stack,
offset=tap_pos,
implant_type="p",
well_type="p",
directions="nonpref")
self.add_via_stack_center(offset=tap_pos,
from_layer=self.active_stack[2],
to_layer=self.wordline_layer)
self.gnd_taps.append(self.add_layout_pin_rect_center("gnd_tap", self.wordline_layer, tap_pos))
def place_precharge(self):
self.precharge_offset = vector(0, - self.precharge_inst.height - self.zero_cell.nmos.end_to_contact - 2 * drc["nwell_enclose_active"] - 3 * self.m1_pitch)
self.precharge_inst.place(offset=self.precharge_offset)
self.copy_layout_pin(self.precharge_inst, "gate", "precharge")
def place_wordline_contacts(self):
for wl in range(self.row_size):
self.copy_layout_pin(self.tap_inst[wl, 0], "poly_tap", self.wordline_names[0][wl])
def place_bitline_contacts(self):
rail_y = self.precharge_inst.get_pins("vdd")[0].cy()
for bl in range(self.column_size):
src_pin = self.cell_list[0][bl].get_pin("S")
prechg_pin_name = "pre_bl{0}_out".format(bl)
pre_pin = self.precharge_inst.get_pin(prechg_pin_name)
middle_offset = (src_pin.cy() - pre_pin.cy() ) * 0.5
corrected = vector(src_pin.cx(), src_pin.cy() - middle_offset)
output_pos = vector(corrected.x, rail_y)
self.add_segment_center(self.bitline_layer, corrected, output_pos)
self.add_layout_pin_rect_center(self.bitline_names[0][bl], self.bitline_layer, output_pos )
def route_precharge(self):
for bl in range(self.column_size):
bl_pin = self.cell_list[0][bl].get_pin("S")
prechg_pin = "pre_bl{0}_out".format(bl)
pre_out_pin = self.precharge_inst.get_pin(prechg_pin)
bl_start = bl_pin.center()
bl_end = vector(bl_start.x, pre_out_pin.cy())
self.add_segment_center(self.bitline_layer, bl_start, bl_end)
upper_precharge = self.precharge_inst.get_pin("precharge_r")
lower_precharge = self.tap_inst[self.row_size, self.column_size ].get_pin("poly_tap")
if self.pitch_match:
wire_offset = 2 * self.m1_pitch
else:
wire_offset = 3 * self.m1_pitch
start = upper_precharge.center()
end = lower_precharge.center()
mid1 = start + vector(wire_offset, 0)
mid2 = end + vector(wire_offset, 0)
self.add_path(layer="m1", coordinates=[start, mid1, mid2, end])
self.add_layout_pin_rect_center(text="precharge_r", layer="m1", offset=mid1)
def connect_taps(self):
poly_tap_pins = [self.poly_tap_list[i].get_pin("poly_tap") for i in range(len(self.poly_tap_list))]
self.connect_row_pins(layer=self.wordline_layer, pins=poly_tap_pins)

View File

@ -0,0 +1,125 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from openram.base import design
from openram.base import vector
from openram import OPTS
from openram.sram_factory import factory
from openram.tech import drc
class rom_base_cell(design):
def __init__(self, name="", bitline_layer="li", bit_value=1, add_well=False):
super().__init__(name)
self.bit_value = bit_value
self.bitline_layer = bitline_layer
self.add_well=add_well
self.create_netlist()
self.create_layout()
def create_netlist(self):
self.add_pins()
self.add_modules()
def create_layout(self):
self.create_tx()
self.setup_drc_offsets()
self.add_boundary()
self.place_tx()
self.place_bitline()
self.place_poly()
if self.bit_value == 0:
self.short_gate()
# Calculates offsets of cell width and height so that tiling of cells does not violate any drc rules
def setup_drc_offsets(self):
self.poly_size = (self.cell_inst.width + self.active_space) - (self.cell_inst.height + 2 * self.poly_extend_active)
def add_boundary(self):
height = self.cell_inst.width + self.active_space
#cell width with offsets applied, height becomes width when the cells are rotated
width = self.cell_inst.height + 2 * self.poly_extend_active
# make the cells square so the pitch of wordlines will match bitlines
if width > height:
self.width = width
self.height = width
else:
self.width = height
self.height = height
super().add_boundary()
def add_modules(self):
self.nmos = factory.create(module_type="ptx",
module_name="nmos_rom_mod",
tx_type="nmos",
add_source_contact=self.bitline_layer,
add_drain_contact=self.bitline_layer
)
def create_tx(self):
self.cell_inst = self.add_inst( name=self.name + "_nmos",
mod=self.nmos,
)
if self.bit_value == 0:
self.connect_inst(["bl", "wl", "bl", "gnd"])
else:
self.connect_inst(["bl_h", "wl", "bl_l", "gnd"])
def add_pins(self):
if self.bit_value == 0 :
pin_list = ["bl", "wl", "gnd"]
dir_list = ["INOUT", "INPUT", "GROUND"]
else:
pin_list = ["bl_h", "bl_l", "wl", "gnd"]
dir_list = ["INOUT", "INOUT", "INPUT", "GROUND"]
self.add_pin_list(pin_list, dir_list)
def place_tx(self):
# sizing_offset = self.cell_inst.height - drc["minwidth_tx"]
tx_offset = vector(self.poly_extend_active + self.cell_inst.height + self.poly_size,- 0.5 * self.contact_width - self.active_enclose_contact)
# add rect of poly to account for offset from drc spacing
# self.add_rect_center("poly", poly_offset, self.poly_extend_active_spacing, self.poly_width)
self.cell_inst.place(tx_offset, rotate=90)
self.copy_layout_pin(self.cell_inst, "S", "S")
self.copy_layout_pin(self.cell_inst, "D", "D")
self.source_pos = self.cell_inst.get_pin("S").center()
def place_poly(self):
poly_offset = vector(0, self.cell_inst.width * 0.5 - 0.5 * self.contact_width - self.active_enclose_contact)
start = poly_offset
end = poly_offset + vector(self.poly_size, 0)
self.add_segment_center("poly", start, end)
def place_bitline(self):
start = self.get_pin("D").center()
end = start + vector(0, 2 * self.active_enclose_contact + 0.5 * self.contact_width + self.active_space)
self.add_segment_center(self.bitline_layer, start, end)
def short_gate(self):
self.add_segment_center(self.bitline_layer, self.get_pin("D").center(), self.get_pin("S").center())

View File

@ -0,0 +1,173 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from openram import debug
from openram.base import vector
from openram.sram_factory import factory
from openram.tech import drc, layer
from openram.tech import cell_properties as cell_props
from openram import OPTS
from .pgate import *
class rom_column_mux(pgate):
"""
This module implements the columnmux bitline cell used in the design.
Creates a single column mux cell with the given integer size relative
to minimum size. Default is 8x. Per Samira and Hodges-Jackson book:
Column-mux transistors driven by the decoder must be sized
for optimal speed
"""
def __init__(self, name, tx_size=8, input_layer="m2", output_layer="m1"):
debug.info(2, "creating single ROM column mux cell: {0}".format(name))
self.tx_size = int(tx_size)
self.input_layer = input_layer
self.output_layer= output_layer
super().__init__(name)
def create_netlist(self):
self.add_pins()
self.add_ptx()
def create_layout(self):
self.pin_layer = self.input_layer
self.pin_pitch = getattr(self, "{}_pitch".format(self.pin_layer))
self.pin_width = getattr(self, "{}_width".format(self.pin_layer))
self.pin_height = 2 * self.pin_width
# If li exists, use li and m1 for the mux, otherwise use m1 and m2
if self.output_layer == "li" :
self.col_mux_stack = self.li_stack
else:
self.col_mux_stack = self.m1_stack
self.place_ptx()
self.width = self.bitcell.width
self.height = self.nmos_lower.uy() + self.pin_height
self.connect_poly()
self.add_bitline_pins()
self.connect_bitlines()
self.add_pn_wells()
def add_ptx(self):
self.bitcell = factory.create(module_type="rom_base_cell")
# Adds nmos_lower,nmos_upper to the module
self.ptx_width = self.tx_size * drc("minwidth_tx")
self.nmos = factory.create(module_type="ptx",
width=self.ptx_width)
# Space it in the center
self.nmos_lower = self.add_inst(name="mux_tx1",
mod=self.nmos)
self.connect_inst(["bl", "sel", "bl_out", "gnd"])
def add_pins(self):
self.add_pin_list(["bl", "bl_out", "sel", "gnd"])
def add_bitline_pins(self):
""" Add the top and bottom pins to this cell """
bl_pos = vector(self.pin_pitch, 0)
# bl and br
self.add_layout_pin(text="bl",
layer=self.pin_layer,
offset=bl_pos + vector(0, self.height - self.pin_height),
height=self.pin_height)
# bl_out and br_out
self.add_layout_pin(text="bl_out",
layer=self.col_mux_stack[0],
offset=bl_pos,
height=self.pin_height)
def place_ptx(self):
""" Create the pass gate NMOS transistor to switch the bitline """
# Space it in the center
nmos_lower_position = self.nmos.active_offset.scale(0, 1) \
+ vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0)
self.nmos_lower.place(nmos_lower_position)
def connect_poly(self):
""" Connect the poly gate of the two pass transistors """
# offset is the top of the lower nmos' diffusion
# height is the distance between the nmos' diffusions, which depends on max(self.active_space,self.poly_space)
offset = self.nmos_lower.get_pin("G").ul() - vector(0, self.poly_extend_active)
height = self.poly_extend_active - offset.y
self.add_rect(layer="poly",
offset=offset,
height=height)
# Add the sel pin to the bottom of the mux
self.add_layout_pin(text="sel",
layer="poly",
offset=self.nmos_lower.get_pin("G").ll(),
height=self.poly_extend_active)
def connect_bitlines(self):
""" Connect the bitlines to the mux transistors """
bl_pin = self.get_pin("bl")
bl_out_pin = self.get_pin("bl_out")
nmos_lower_s_pin = self.nmos_lower.get_pin("S")
nmos_lower_d_pin = self.nmos_lower.get_pin("D")
self.add_via_stack_center(from_layer=nmos_lower_s_pin.layer,
to_layer=self.input_layer,
offset=nmos_lower_s_pin.center())
self.add_via_stack_center(from_layer=nmos_lower_d_pin.layer,
to_layer=self.output_layer,
offset=nmos_lower_d_pin.center())
# bl -> nmos_upper/D on metal1
# bl_out -> nmos_upper/S on metal2
mid1 = bl_pin.bc().scale(1, 0.4) \
+ nmos_lower_s_pin.uc().scale(0, 0.5)
mid2 = bl_pin.bc().scale(0, 0.4) \
+ nmos_lower_s_pin.uc().scale(1, 0.5)
self.add_path(self.input_layer,
[bl_pin.bc(), mid1, mid2, nmos_lower_s_pin.center()])
# halfway up, move over
mid1 = bl_out_pin.uc().scale(1, 0.4) \
+ nmos_lower_d_pin.bc().scale(0, 0.4)
mid2 = bl_out_pin.uc().scale(0, 0.4) \
+ nmos_lower_d_pin.bc().scale(1, 0.4)
self.add_path(self.output_layer,
[bl_out_pin.uc(), mid1, mid2, nmos_lower_d_pin.center()])
def add_pn_wells(self):
"""
Add a well and implant over the whole cell. Also, add the
pwell contact (if it exists)
"""
# Add it to the right, aligned in between the two tx
active_pos = vector(self.bitcell.width,
self.nmos_lower.uy() + self.active_contact.height + self.active_space)
self.add_via_center(layers=self.active_stack,
offset=active_pos,
implant_type="p",
well_type="p")
# If there is a li layer, include it in the power stack
self.add_via_stack_center(from_layer=self.active_stack[2],
to_layer=self.pin_layer,
offset=active_pos)
self.add_layout_pin_rect_center(text="gnd",
layer=self.pin_layer,
offset=active_pos)

View File

@ -0,0 +1,209 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from openram import debug
from openram.base import design
from openram.base import vector
from openram.sram_factory import factory
from openram.tech import layer, preferred_directions, drc
from openram.tech import layer_properties as layer_props
from openram import OPTS
class rom_column_mux_array(design):
"""
Dynamically generated column mux array.
Array of column mux to read the bitlines from ROM, based on the RAM column mux
"""
def __init__(self, name, columns, word_size, tap_spacing=4, input_layer="m1", bitline_layer="m1", sel_layer="m2"):
super().__init__(name)
debug.info(1, "Creating {0}".format(self.name))
self.add_comment("cols: {0} word_size: {1} ".format(columns, word_size))
self.columns = columns
self.word_size = word_size
self.words_per_row = int(self.columns / self.word_size)
self.input_layer = input_layer
self.tap_spacing = tap_spacing
self.sel_layer = sel_layer
self.supply_layer = "m2"
self.sel_pitch = getattr(self, self.sel_layer + "_pitch")
self.bitline_layer = bitline_layer
if preferred_directions[self.sel_layer] == "V":
self.via_directions = ("H", "H")
else:
self.via_directions = "pref"
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_modules()
self.add_pins()
self.create_array()
def create_layout(self):
self.setup_layout_constants()
self.place_array()
self.add_routing()
# Find the highest shapes to determine height before adding well
highest = self.find_highest_coords()
self.height = highest.y
self.add_layout_pins()
if "pwell" in layer:
self.add_enclosure(self.mux_inst, "pwell")
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
for i in range(self.columns):
self.add_pin("bl_{}".format(i))
for i in range(self.words_per_row):
self.add_pin("sel_{}".format(i))
for i in range(self.word_size):
self.add_pin("bl_out_{}".format(i))
self.add_pin("gnd")
def add_modules(self):
self.mux = factory.create(module_type="rom_column_mux", input_layer=self.input_layer, output_layer=self.bitline_layer)
self.tap = factory.create(module_type="rom_poly_tap", add_active_tap=True)
self.cell = factory.create(module_type="rom_base_cell")
def setup_layout_constants(self):
self.column_addr_size = int(self.words_per_row / 2)
self.width = self.columns * self.mux.width
# one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br
# one extra route pitch is to space from the sense amp
self.route_height = (self.words_per_row + 3) * self.cell.width
self.route_layer_width = drc["minwidth_{}".format(self.bitline_layer)]
self.route_layer_pitch = drc["{0}_to_{0}".format(self.bitline_layer)]
def create_array(self):
self.mux_inst = []
# For every column, add a pass gate
for col_num in range(self.columns):
name = "XMUX{0}".format(col_num)
self.mux_inst.append(self.add_inst(name=name,
mod=self.mux))
self.connect_inst(["bl_{}".format(col_num),
"bl_out_{}".format(int(col_num / self.words_per_row)),
"sel_{}".format(col_num % self.words_per_row),
"gnd"])
def place_array(self):
# Default to single spaced columns
self.offsets = [n * self.mux.width for n in range(self.columns)]
# For every column, add a pass gate
for col_num, xoffset in enumerate(self.offsets[0:self.columns]):
offset = vector(xoffset, self.route_height)
self.mux_inst[col_num].place(offset=offset)
def add_layout_pins(self):
""" Add the pins after we determine the height. """
# For every column, add a pass gate
for col_num in range(self.columns):
mux_inst = self.mux_inst[col_num]
bl_pin = mux_inst.get_pin("bl")
offset = bl_pin.ll()
self.add_layout_pin(text="bl_{}".format(col_num),
layer=bl_pin.layer,
offset=offset,
height=self.height - offset.y)
def route_supplies(self):
self.route_horizontal_pins("gnd", self.insts, layer=self.supply_layer)
def add_routing(self):
self.add_horizontal_input_rail()
self.add_vertical_poly_rail()
self.route_bitlines()
self.route_supplies()
def add_horizontal_input_rail(self):
""" Create address input rails below the mux transistors """
tap_offset = 0
for j in range(self.words_per_row):
if j % self.tap_spacing == 0 and j != 0:
tap_offset += self.tap.pitch_offset
offset = vector(0, self.route_height + tap_offset + (j - self.words_per_row) * self.cell.width)
self.add_layout_pin(text="sel_{}".format(j),
layer=self.sel_layer,
offset=offset,
width=self.mux_inst[-1].rx())
def add_vertical_poly_rail(self):
""" Connect the poly to the address rails """
# Offset to the first transistor gate in the pass gate
for col in range(self.columns):
# which select bit should this column connect to depends on the position in the word
sel_index = col % self.words_per_row
# Add the column x offset to find the right select bit
gate_offset = self.mux_inst[col].get_pin("sel").bc()
# use the y offset from the sel pin and the x offset from the gate
offset = vector(gate_offset.x,
self.get_pin("sel_{}".format(sel_index)).cy())
bl_x_offset = self.mux_inst[col].get_pin("bl_out").cx() + 2 * self.route_layer_width + self.route_layer_pitch + 0.5 * self.poly_contact.width
bl_offset = offset.scale(0, 1) + vector(bl_x_offset, 0)
self.add_via_stack_center(from_layer="poly",
to_layer=self.sel_layer,
offset=bl_offset,
directions=self.via_directions)
self.add_path("poly", [offset, gate_offset, bl_offset])
def route_bitlines(self):
""" Connect the output bit-lines to form the appropriate width mux """
for j in range(self.columns):
bl_offset_begin = self.mux_inst[j].get_pin("bl_out").bc()
bl_out_offset_begin = bl_offset_begin - vector(0, (self.words_per_row + 1) * self.cell.width)
# Add the horizontal wires for the first bit
if j % self.words_per_row == 0:
bl_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("bl_out").bc()
bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.cell.width)
self.add_path(self.sel_layer, [bl_out_offset_begin, bl_out_offset_end])
# Extend the bitline output rails and gnd downward on the first bit of each n-way mux
self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j / self.words_per_row)),
layer=self.bitline_layer,
start=bl_offset_begin,
end=bl_out_offset_begin)
else:
self.add_path(self.bitline_layer, [bl_out_offset_begin, bl_offset_begin])
# This via is on the right of the wire
self.add_via_stack_center(from_layer=self.bitline_layer,
to_layer=self.sel_layer,
offset=bl_out_offset_begin,
directions=self.via_directions)
def graph_exclude_columns(self, column_include_num):
"""
Excludes all columns muxes unrelated to the target bit being simulated.
Each mux in mux_inst corresponds to respective column in bitcell array.
"""
for i in range(len(self.mux_inst)):
if i != column_include_num:
self.graph_inst_exclude.add(self.mux_inst[i])

View File

@ -0,0 +1,141 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from openram.sram_factory import factory
from openram.base import vector, design
from openram.tech import layer, drc
class rom_control_logic(design):
def __init__(self, num_outputs, clk_fanout, name="", height=None):
self.output_size = num_outputs
super().__init__(name, prop=False)
self.height = height
if self.height is not None:
self.driver_height = 0.5 * self.height
self.gate_height = 0.5 * self.height
else:
self.gate_height = 20 * self.m1_pitch
self.driver_height = self.gate_height
self.clk_fanout = clk_fanout
if "li" in layer:
self.route_stack = self.li_stack
else:
self.route_stack = self.m1_stack
self.create_netlist()
self.create_layout()
self.add_boundary()
def create_netlist(self):
self.add_modules()
self.add_pins()
def create_layout(self):
self.create_instances()
self.height=self.driver_inst.height + self.buf_inst.height
self.width= max(self.nand_inst.width + self.buf_inst.width, self.driver_inst.width)
self.place_instances()
self.route_insts()
def add_modules(self):
self.buf_mod = factory.create(module_type="pdriver",
module_name="rom_clock_driver",
height=self.gate_height,
fanout=self.clk_fanout + 2,
add_wells=True,
)
self.nand_mod = factory.create(module_type="pnand2",
module_name="rom_control_nand",
height=self.gate_height,
add_wells=False)
self.driver_mod = factory.create(module_type="pdriver",
module_name="rom_precharge_driver",
inverting=True,
fanout=self.output_size,
height=self.driver_height,
add_wells=True)
def add_pins(self):
self.add_pin("clk_in", "INPUT")
self.add_pin("CS", "INPUT")
self.add_pin("prechrg", "OUTPUT")
self.add_pin("clk_out", "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_instances(self):
self.buf_inst = self.add_inst(name="clk_driver", mod=self.buf_mod)
self.connect_inst(["clk_in", "clk_out", "vdd", "gnd"])
self.nand_inst = self.add_inst(name="control_nand", mod=self.nand_mod)
self.connect_inst(["CS", "clk_out", "pre_drive", "vdd", "gnd"])
self.driver_inst = self.add_inst(name="precharge_driver", mod=self.driver_mod)
self.connect_inst(["pre_drive", "prechrg", "vdd", "gnd"])
def place_instances(self):
self.nand_inst.place(offset=[self.buf_inst.width, 0])
self.driver_inst.place(offset=[0, self.buf_inst.height + self.driver_inst.height], mirror="MX")
# hack to get around the fact these modules dont tile properly
offset = self.driver_inst.get_pin("vdd").cy() - self.nand_inst.get_pin("vdd").cy()
self.driver_inst.place(offset=[0, self.buf_inst.height + self.driver_inst.height - offset], mirror="MX")
def route_insts(self):
route_width = drc["minwidth_{}".format(self.route_stack[2])]
self.copy_layout_pin(self.buf_inst, "A", "clk_in")
self.copy_layout_pin(self.buf_inst, "Z", "clk_out")
self.copy_layout_pin(self.driver_inst, "Z", "prechrg")
self.copy_layout_pin(self.nand_inst, "A", "CS")
self.copy_power_pin(self.buf_inst.get_pin("gnd"), directions="nonpref")
self.copy_power_pin(self.driver_inst.get_pin("gnd"), directions="nonpref")
self.copy_power_pin(self.buf_inst.get_pin("vdd"), directions="nonpref")
clk = self.buf_inst.get_pin("Z")
nand_B = self.nand_inst.get_pin("B")
# Connect buffered clock bar to nand input
mid = vector(clk.lx() - route_width - 2 * self.m1_space)
self.add_path(self.route_stack[2], [clk.center(), nand_B.center()])
self.add_via_stack_center(from_layer=clk.layer,
to_layer=self.route_stack[2],
offset=clk.center())
self.add_via_stack_center(from_layer=nand_B.layer,
to_layer=self.route_stack[2],
offset=nand_B.center())
# Connect nand output to precharge driver
nand_Z = self.nand_inst.get_pin("Z")
driver_A = self.driver_inst.get_pin("A")
mid = vector(driver_A.cx(), driver_A.cy() - 4 * route_width)
self.add_path(self.route_stack[2], [nand_Z.center(), mid, driver_A.center()])
self.add_via_stack_center(from_layer=nand_Z.layer,
to_layer=self.route_stack[2],
offset=nand_Z.center())
self.add_via_stack_center(from_layer=driver_A.layer,
to_layer=self.route_stack[2],
offset=driver_A.center())

View File

@ -0,0 +1,243 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from math import ceil, log
from openram.sram_factory import factory
from openram.base import vector, design
from openram import OPTS
from openram.tech import drc
class rom_decoder(design):
def __init__(self, num_outputs, fanout, strap_spacing, name="", route_layer="m1", output_layer="m1", invert_outputs=False):
# word lines in the base array become the address lines/cols in the decoder
# bit lines in the base array become the word lines/rows in the decoder
# array gets rotated 90deg so rows/cols switch
self.strap_spacing=strap_spacing
self.num_outputs = num_outputs
self.num_inputs = ceil(log(num_outputs, 2))
self.create_decode_map()
super().__init__(name)
b = factory.create(module_type=OPTS.bitcell)
self.cell_height = b.height
self.route_layer = route_layer
self.output_layer = output_layer
self.inv_route_layer = "m2"
self.fanout=fanout
self.invert_outputs=invert_outputs
self.create_netlist()
self.width = self.array_mod.height + self.wordline_buf.width
self.height = self.array_mod.width + self.control_array.height
self.create_layout()
def create_netlist(self):
self.add_modules()
self.add_pins()
self.create_instances()
def create_layout(self):
self.setup_layout_constants()
self.place_array()
self.place_input_buffer()
self.place_driver()
self.route_outputs()
self.connect_inputs()
self.route_supplies()
self.add_boundary()
def add_boundary(self):
ll = self.find_lowest_coords()
m1_offset = self.m1_width
self.translate_all(vector(0, ll.y))
ur = self.find_highest_coords()
ur = vector(ur.x, ur.y)
super().add_boundary(ll, ur)
self.width = ur.x
self.height = ur.y
def setup_layout_constants(self):
self.inv_route_width = drc["minwidth_{}".format(self.inv_route_layer)]
def create_decode_map(self):
self.decode_map = []
# create decoding map that will be the bitmap for the rom decoder
# row/col order in the map will be switched in the placed decoder/
for col in range(self.num_inputs):
# odd cols are address
# even cols are address bar
col_array = []
inv_col_array = []
for row in range(self.num_outputs):
addr_idx = -col - 1
addr = format(row, 'b')
if col >= len(addr) :
bin_digit = 0
else:
bin_digit = int(addr[addr_idx])
col_array.append(bin_digit)
if bin_digit == 0 : inv_col_array.append(1)
else : inv_col_array.append(0)
self.decode_map.append(col_array)
self.decode_map.append(inv_col_array)
self.decode_map.reverse()
def add_pins(self):
for i in range(self.num_inputs):
self.add_pin("A{0}".format(i), "INPUT")
for j in range(self.num_outputs):
self.add_pin("wl_{0}".format(j), "OUTPUT")
self.add_pin("precharge", "INPUT")
self.add_pin("clk", "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
self.control_array = factory.create(module_type="rom_address_control_array",
cols=self.num_inputs)
self.wordline_buf = factory.create(module_type="rom_wordline_driver_array", module_name="{}_wordline_buffer".format(self.name),
rows=self.num_outputs,
fanout=ceil(self.fanout),
invert_outputs=self.invert_outputs,
tap_spacing=self.strap_spacing)
self.array_mod = factory.create(module_type="rom_base_array",
module_name="{}_array".format(self.name),
cols=self.num_outputs,
rows=2 * self.num_inputs,
bitmap=self.decode_map,
strap_spacing = self.strap_spacing,
bitline_layer=self.output_layer,
tap_direction="col")
def create_instances(self):
self.create_array_inst()
self.create_input_buffer()
self.create_wordline_buffer()
def create_input_buffer(self):
name = "pre_control_array"
self.buf_inst = self.add_inst(name=name, mod=self.control_array)
control_pins = []
for i in range(self.num_inputs):
control_pins.append("A{0}".format(i))
for i in range(self.num_inputs):
control_pins.append("A_int_{0}".format(i))
for i in range(self.num_inputs):
control_pins.append("Ab_int_{0}".format(i))
control_pins.append("clk")
control_pins.append("vdd")
control_pins.append("gnd")
self.connect_inst(control_pins)
def create_array_inst(self):
self.array_inst = self.add_inst(name="decode_array_inst", mod=self.array_mod)
array_pins = []
for j in range(self.num_outputs):
name = "wl_int{0}".format(j)
array_pins.append(name)
for i in reversed(range(self.num_inputs)):
array_pins.append("Ab_int_{0}".format(i))
array_pins.append("A_int_{0}".format(i))
array_pins.append("precharge")
array_pins.append("vdd")
array_pins.append("gnd")
self.connect_inst(array_pins)
def create_wordline_buffer(self):
self.wordline_buf_inst = self.add_inst("rom_wordline_driver", mod=self.wordline_buf)
in_pins = ["wl_int{}".format(wl) for wl in range(self.num_outputs)]
out_pins = ["wl_{}".format(wl) for wl in range(self.num_outputs)]
pwr_pins = ["vdd", "gnd"]
self.connect_inst(in_pins + out_pins + pwr_pins)
def place_input_buffer(self):
wl = self.array_mod.row_size - 1
align = self.array_inst.get_pin(self.array_mod.wordline_names[0][wl]).cx() - self.buf_inst.get_pin("A0_out").cx()
self.buf_inst.place(vector(align, 0))
self.copy_layout_pin(self.buf_inst, "clk")
def place_array(self):
offset = vector(self.array_mod.height, self.control_array.height + self.m1_width + self.poly_contact.width)
self.array_inst.place(offset, rotate=90)
def place_driver(self):
offset = vector(self.array_inst.height + self.m1_width, self.array_inst.by())
self.wordline_buf_inst.place(offset)
# calculate the offset between the decode array and the buffer inputs now that their zeros are aligned
pin_offset = self.array_inst.get_pin("bl_0_0").cy() - self.wordline_buf_inst.get_pin("in_0").cy()
self.wordline_buf_inst.place(offset + vector(0, pin_offset))
def route_outputs(self):
for j in range(self.num_outputs):
self.copy_layout_pin(self.wordline_buf_inst, "out_{}".format(j), "wl_{}".format(j))
offset = self.wordline_buf_inst.get_pin("out_{}".format(j)).center()
array_pins = [self.array_inst.get_pin("bl_0_{}".format(bl)) for bl in range(self.num_outputs)]
driver_pins = [self.wordline_buf_inst.get_pin("in_{}".format(bl)) for bl in range(self.num_outputs)]
route_pins = array_pins + driver_pins
self.connect_row_pins(self.output_layer, route_pins, round=True)
def connect_inputs(self):
self.copy_layout_pin(self.array_inst, "precharge")
self.copy_layout_pin(self.array_inst, "precharge_r")
for i in range(self.num_inputs):
wl = (self.num_inputs - i) * 2 - 1
wl_bar = wl - 1
addr_pin = self.array_inst.get_pin(self.array_mod.wordline_names[0][wl])
addr_bar_pin = self.array_inst.get_pin(self.array_mod.wordline_names[0][wl_bar])
addr_out_pin = self.buf_inst.get_pin("A{}_out".format(i))
addr_bar_out_pin = self.buf_inst.get_pin("Abar{}_out".format(i))
addr_middle = vector(addr_pin.cx(), addr_out_pin.cy())
addr_bar_middle = vector(addr_bar_pin.cx(), addr_bar_out_pin.cy())
self.add_path(self.inv_route_layer, [addr_out_pin.center(), addr_middle, addr_pin.center()])
self.add_path(self.inv_route_layer, [addr_bar_out_pin.center(), addr_bar_middle, addr_bar_pin.center()])
self.copy_layout_pin(self.buf_inst, "A{}_in".format(i), "A{}".format(i))
def route_supplies(self):
self.copy_layout_pin(self.array_inst, "vdd")
self.copy_layout_pin(self.wordline_buf_inst, "vdd")
self.copy_layout_pin(self.buf_inst, "vdd")
self.copy_layout_pin(self.array_inst, "gnd")
self.copy_layout_pin(self.wordline_buf_inst, "gnd")
self.copy_layout_pin(self.buf_inst, "gnd")

View File

@ -0,0 +1,96 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from openram.base import design
from openram.base import vector
from openram import OPTS
from openram.sram_factory import factory
from openram.tech import drc
class rom_poly_tap(design):
def __init__(self, name="", cell_name=None, tx_type="nmos", strap_layer="m2", add_active_tap=False, place_poly=None):
super().__init__(name, cell_name)
self.strap_layer=strap_layer
self.tx_type = tx_type
self.add_tap = add_active_tap
if place_poly is None:
self.place_poly = add_active_tap
else:
self.place_poly = place_poly
self.pitch_offset = 0
self.create_netlist()
self.create_layout()
def create_netlist(self):
#for layout constants
self.dummy = factory.create(module_type="rom_base_cell")
self.pmos = factory.create(module_type="ptx", tx_type="pmos")
def create_layout(self):
self.place_via()
self.add_boundary()
if self.add_tap or self.place_poly:
self.place_active_tap()
self.extend_poly()
def add_boundary(self):
contact_width = self.poly_contact.width
self.height = self.dummy.height
self.width = contact_width + self.pitch_offset
super().add_boundary()
def place_via(self):
contact_width = self.poly_contact.width
contact_y = self.dummy.cell_inst.width * 0.5 - 0.5 * self.contact_width - self.active_enclose_contact
self.contact_x_offset = 0
contact_x = contact_width * 0.5 + self.contact_x_offset
self.contact_offset = vector(contact_x, contact_y)
self.via = self.add_via_stack_center(from_layer="poly",
to_layer=self.strap_layer,
offset=self.contact_offset)
self.add_layout_pin_rect_center("poly_tap", self.strap_layer, self.contact_offset)
def extend_poly(self):
y_offset = 0
if self.tx_type == "pmos":
y_offset = -self.height
start = self.via.center() + vector(0, y_offset)
self.add_segment_center("poly", start, vector(self.via.cx() + self.pitch_offset, self.via.cy() + y_offset))
self.add_segment_center("poly", start, vector(0, self.via.cy() + y_offset))
def place_active_tap(self):
gap = self.poly_extend_active - 0.5 * ( self.active_contact.height - self.poly_contact.width )
offset = self.active_space - gap
tap_x = self.via.cx() + offset
tap_y = self.via.cy() + self.dummy.width * 0.5
contact_pos = vector(tap_x, tap_y)
# edge of the next nmos
active_edge = self.dummy.width - self.dummy.cell_inst.height - self.poly_extend_active
# edge of the active contact
tap_edge = tap_x + 0.5 * self.active_contact.height
self.pitch_offset += (self.active_space * 2) - (tap_edge - active_edge) + self.contact_x_offset
if self.tx_type == "nmos" and self.add_tap:
self.add_via_center(layers=self.active_stack,
offset=contact_pos,
implant_type="p",
well_type="p",
directions="nonpref")
self.add_layout_pin_rect_center("active_tap", self.active_stack[2], contact_pos)

View File

@ -0,0 +1,177 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from math import ceil
from openram.base import geometry
from openram.base import design
from openram.sram_factory import factory
from openram.base import vector
from openram.tech import layer, drc
class rom_precharge_array(design):
"""
An array of inverters to create the inverted address lines for the rom decoder
"""
def __init__(self, cols, name="", bitline_layer=None, strap_spacing=None, strap_layer="m2", tap_direction="row"):
self.cols = cols
self.strap_layer = strap_layer
self.tap_direction = tap_direction
if "li" in layer:
self.supply_layer = "li"
else:
self.supply_layer = "m1"
if bitline_layer is not None:
self.bitline_layer = bitline_layer
else:
self.bitline_layer = self.supply_layer
if name=="":
name = "rom_inv_array_{0}".format(cols)
if strap_spacing != None:
self.strap_spacing = strap_spacing
else:
self.strap_spacing = 0
if strap_spacing != 0:
self.num_straps = ceil(self.cols / self.strap_spacing)
self.array_col_size = self.cols + self.num_straps
else:
self.num_straps = 0
self.array_col_size = self.cols
super().__init__(name)
self.create_netlist()
self.create_layout()
def create_netlist(self):
self.create_modules()
self.add_pins()
self.create_instances()
def create_layout(self):
self.width = self.cols * self.pmos.width
self.height = self.pmos.width
self.place_instances()
self.create_layout_pins()
self.route_supply()
self.connect_taps()
self.add_boundary()
self.extend_well()
def add_boundary(self):
ur = self.find_highest_coords()
self.add_label(layer="nwell", text="upper right",offset=ur)
super().add_boundary(vector(0, 0), ur)
self.height = ur.y
self.width = ur.x
def create_modules(self):
self.pmos = factory.create(module_type="rom_precharge_cell", module_name="precharge_cell", bitline_layer=self.bitline_layer, supply_layer=self.supply_layer)
# For layout constants
self.dummy = factory.create(module_type="rom_base_cell")
self.poly_tap = factory.create(module_type="rom_poly_tap", tx_type="pmos", add_active_tap=(self.tap_direction == "col"))
def add_pins(self):
for col in range(self.cols):
self.add_pin("pre_bl{0}_out".format(col), "OUTPUT")
self.add_pin("gate", "INPUT")
self.add_pin("vdd", "POWER")
def create_instances(self):
self.array_insts = []
self.pmos_insts = []
self.tap_insts = []
self.create_poly_tap(-1)
for col in range(self.cols):
if col % self.strap_spacing == 0:
self.create_poly_tap(col)
self.create_precharge_tx(col)
def create_precharge_tx(self, col):
name = "pmos_c{0}".format(col)
pmos = self.add_inst(name=name, mod=self.pmos)
self.array_insts.append(pmos)
self.pmos_insts.append(pmos)
bl = "pre_bl{0}_out".format(col)
self.connect_inst(["vdd", "gate", bl])
def create_poly_tap(self, col):
name = "tap_c{}".format( col)
new_tap = self.add_inst(name=name, mod=self.poly_tap)
self.tap_insts.append(new_tap)
self.connect_inst([])
def place_instances(self):
self.array_pos = []
strap_num = 0
cell_y = 0
# columns are bit lines
cell_x = 0
for col in range(self.cols):
if col % self.strap_spacing == 0:
self.tap_insts[strap_num].place(vector(cell_x, cell_y + self.poly_tap.height))
strap_num += 1
if self.tap_direction == "col":
cell_x += self.poly_tap.pitch_offset
self.pmos_insts[col].place(vector(cell_x, cell_y))
self.add_label("debug", "li", vector(cell_x, cell_y))
cell_x += self.pmos.width
self.tap_insts[strap_num].place(vector(cell_x, cell_y + self.poly_tap.height))
def create_layout_pins(self):
self.copy_layout_pin(self.tap_insts[0], "poly_tap", "gate")
self.copy_layout_pin(self.tap_insts[-1], "poly_tap", "precharge_r")
for col in range(self.cols):
source_pin = self.pmos_insts[col].get_pin("D")
bl = "pre_bl{0}_out".format(col)
self.add_layout_pin_rect_center(bl, self.bitline_layer, source_pin.center())
def route_supply(self):
self.route_horizontal_pins("vdd", insts=self.pmos_insts, layer=self.strap_layer)
def connect_taps(self):
array_pins = [self.tap_insts[i].get_pin("poly_tap") for i in range(len(self.tap_insts))]
self.connect_row_pins(layer=self.strap_layer, pins=array_pins, name=None, round=False)
for tap in self.tap_insts:
tap_pin = tap.get_pin("poly_tap")
start = vector(tap_pin.cx(), tap_pin.by())
end = vector(start.x, tap.mod.get_pin("poly_tap").cy())
self.add_segment_center(layer="poly", start=start, end=end)
offset_start = vector(end.x - self.poly_tap.width + self.poly_extend_active, end.y)
offset_end = end + vector(0.5*self.poly_width, 0)
self.add_segment_center(layer="poly", start=offset_start, end=offset_end)
def extend_well(self):
self.well_offset = self.pmos.tap_offset
well_y = self.pmos_insts[0].get_pin("vdd").cy() - 0.5 * self.nwell_width
well_y = self.get_pin("vdd").cy() - 0.5 * self.nwell_width
well_ll = vector(0, well_y)
self.add_rect("nwell", well_ll, self.width , self.height - well_y)

View File

@ -0,0 +1,86 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from .rom_base_cell import rom_base_cell
from .pgate import pgate
from openram.base import vector
from openram import OPTS
from openram.sram_factory import factory
from openram.tech import drc
class rom_precharge_cell(rom_base_cell):
def __init__(self, name="", bitline_layer="m1", supply_layer="li"):
self.supply_layer = supply_layer
super().__init__(name=name, bitline_layer=bitline_layer)
def create_layout(self):
super().create_layout()
self.place_tap()
self.extend_well()
def add_modules(self):
width = pgate.nearest_bin("pmos", drc["minwidth_tx"])
self.pmos = factory.create(module_type="ptx",
module_name="pre_pmos_mod",
tx_type="pmos",
width=width,
add_source_contact=self.supply_layer,
add_drain_contact=self.bitline_layer
)
def create_tx(self):
self.cell_inst = self.add_inst( name="precharge_pmos",
mod=self.pmos,
)
self.connect_inst(["bitline", "gate", "vdd", "vdd"])
def add_pins(self):
pin_list = ["vdd", "gate", "bitline"]
dir_list = ["POWER", "INPUT", "OUTPUT"]
self.add_pin_list(pin_list, dir_list)
def setup_drc_offsets(self):
self.poly_size = (self.cell_inst.width + self.active_space) - (self.cell_inst.height + 2 * self.poly_extend_active)
def extend_well(self):
well_y = self.get_pin("vdd").cy() - 0.5 * self.nwell_width
well_ll = vector(0, well_y)
height = self.get_pin("D").cy() + 0.5 * self.nwell_width - well_y
self.add_rect("nwell", well_ll, self.width , height)
def place_tap(self):
source = self.cell_inst.get_pin("S")
tap_y = source.cy() - self.contact_width - 2 * self.active_enclose_contact - self.active_space
self.tap_offset = abs(tap_y)
pos = vector(source.cx(), tap_y )
self.add_via_center(layers=self.active_stack,
offset=pos,
implant_type="n",
well_type="n",
directions="nonpref")
self.add_via_stack_center(offset=pos,
from_layer=self.active_stack[2],
to_layer=self.supply_layer)
bitline_offset = vector( 1.5 * (drc("minwidth_{}".format(self.bitline_layer)) + drc("{0}_to_{0}".format(self.bitline_layer))) ,0)
self.add_layout_pin_rect_center("vdd", self.supply_layer, pos - bitline_offset)
self.add_path(self.supply_layer, [self.get_pin("vdd").center(), pos, self.get_pin("S").center()])
self.remove_layout_pin("S")
def place_bitline(self):
pass

View File

@ -0,0 +1,223 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from openram import debug
from openram.base import design, drc
from openram.base import vector
from openram.sram_factory import factory
from openram.tech import layer
from openram.tech import layer_properties as layer_props
from openram import OPTS
class rom_wordline_driver_array(design):
"""
Creates a Wordline Buffer/Inverter array
"""
def __init__(self, name, rows, fanout, invert_outputs=False, tap_spacing=4, flip_io=False):
design.__init__(self, name)
debug.info(1, "Creating {0}".format(self.name))
self.add_comment("rows: {0} Buffer size of: {1}".format(rows, fanout))
self.rows = rows
self.fanout = fanout
self.invert_outputs=invert_outputs
self.tap_spacing = tap_spacing
self.flip_io = flip_io
if OPTS.tech_name == "sky130":
self.supply_layer = "m1"
else:
self.supply_layer = "m2"
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_modules()
self.add_pins()
self.create_drivers()
def create_layout(self):
if "li" in layer:
self.route_layer = "li"
else:
self.route_layer = "m1"
self.place_drivers()
self.route_layout()
if self.tap_spacing != 0:
self.place_taps()
self.route_supplies()
self.add_boundary()
def add_pins(self):
# inputs to wordline_driver.
for i in range(self.rows):
self.add_pin("in_{0}".format(i), "INPUT")
# Outputs from wordline_driver.
for i in range(self.rows):
self.add_pin("out_{0}".format(i), "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
b = factory.create(module_type="rom_base_cell")
self.tap = factory.create(module_type="rom_poly_tap", add_active_tap = True)
if self.invert_outputs:
self.wl_driver = factory.create(module_type="pinv_dec",
size=self.fanout,
height=b.height,
add_wells=False,
flip_io=self.flip_io)
else:
self.wl_driver = factory.create(module_type="pbuf_dec",
size=self.fanout,
height=b.height,
add_wells=False)
def route_supplies(self):
"""
Add a pin for each row of vdd/gnd which
are must-connects next level up.
"""
# self.route_vertical_pins("vdd", self.wld_inst, xside="cx", layer=self.supply_layer)
# self.route_vertical_pins("gnd", self.wld_inst, xside="cx", layer=self.supply_layer)
if not self.invert_outputs:
vdd_pins = [pin for inst in self.wld_inst for pin in inst.get_pins("vdd")]
gnd_pins = [pin for inst in self.wld_inst for pin in inst.get_pins("gnd")]
else:
vdd_pins = [inst.get_pin("vdd") for inst in self.wld_inst]
gnd_pins = [inst.get_pin("gnd") for inst in self.wld_inst]
if self.tap_spacing != 0:
vdd_pins = vdd_pins + self.vdd_taps
gnd_pins = gnd_pins + self.gnd_taps
supply_width = drc["minwidth_{}".format(self.supply_layer)]
# Route together all internal supply pins
self.connect_col_pins(layer=self.supply_layer, pins=vdd_pins, name="vdd_tmp")
self.connect_col_pins(layer=self.supply_layer, pins=gnd_pins, name="gnd_tmp")
self.remove_layout_pin("gnd_tap")
self.remove_layout_pin("vdd_tap")
# Place the top level supply pins on the edge of the module
for pin in self.get_pins("gnd_tmp"):
bottom = vector(pin.cx(), pin.by() - 0.5 * supply_width)
top = vector(pin.cx(), pin.uy() + 0.5 * supply_width)
self.add_layout_pin_rect_ends(layer=self.supply_layer, start=bottom, end=top, name="gnd")
for pin in self.get_pins("vdd_tmp"):
bottom = vector(pin.cx(), pin.by() - 0.5 * supply_width)
top = vector(pin.cx(), pin.uy() + 0.5 * supply_width)
self.add_layout_pin_rect_ends(layer=self.supply_layer, start=bottom, end=top, name="vdd")
self.remove_layout_pin("gnd_tmp")
self.remove_layout_pin("vdd_tmp")
def create_drivers(self):
self.wld_inst = []
for row in range(self.rows):
self.wld_inst.append(self.add_inst(name="wld{0}".format(row),
mod=self.wl_driver))
self.connect_inst(["in_{0}".format(row),
"out_{0}".format(row),
"vdd", "gnd"])
def place_drivers(self):
y_offset = 0
for row in range(self.rows):
if self.tap_spacing != 0 and row % self.tap_spacing == 0:
y_offset += self.tap.pitch_offset
offset = [0, y_offset]
self.wld_inst[row].place(offset=offset)
y_offset += self.wld_inst[row].height
self.width = self.wl_driver.width
self.height = self.wl_driver.height * self.rows
def route_layout(self):
""" Route all of the signals """
route_width = drc["minwidth_{}".format(self.route_layer)]
for row in range(self.rows):
if self.flip_io:
row_num = self.rows - row - 1
else:
row_num = row
inst = self.wld_inst[row_num]
self.copy_layout_pin(inst, "A", "in_{0}".format(row))
out_pin = inst.get_pin("Z")
# output each WL on the right
if self.flip_io:
wl_offset = out_pin.lc() - vector(1.6 * route_width, 0)
else:
wl_offset = out_pin.rc() - vector( 0.5 * route_width, 0)
end = vector(wl_offset.x, \
self.get_pin("in_{}".format(row)).cy() + 0.5 * route_width)
self.add_segment_center(layer=self.route_layer,
start=wl_offset,
end=end)
if self.flip_io:
self.add_segment_center(layer=self.route_layer,
start=out_pin.lc(),
end=vector(wl_offset.x - 0.5 * route_width, out_pin.cy()))
self.add_layout_pin_rect_center(text="out_{}".format(row), layer=self.route_layer, offset=end - vector(0, 0.5 * route_width))
def place_taps(self):
self.vdd_taps = []
self.gnd_taps = []
for wl in range(0 , self.rows, self.tap_spacing):
driver = self.wld_inst[wl]
source_pin1 = driver.get_pins("vdd")[0]
gnd_pin1 = driver.get_pins("gnd")[0]
left_edge = driver.get_pin("Z").cy() - 0.5 * self.contact_width - self.active_enclose_contact - self.active_space - 0.5 * self.active_contact.width
contact_pos = vector(source_pin1.cx(), left_edge)
self.place_tap(contact_pos, "n")
contact_pos = vector( gnd_pin1.cx(), left_edge)
self.place_tap(contact_pos, "p")
if not self.invert_outputs:
source_pin2 = driver.get_pins("vdd")[1]
gnd_pin2 = driver.get_pins("gnd")[1]
contact_pos = vector(source_pin2.cx(), left_edge)
self.place_tap(contact_pos, "n")
contact_pos = vector( gnd_pin2.cx(), left_edge)
self.place_tap(contact_pos, "p")
def place_tap(self, offset, well_type):
self.add_via_center(layers=self.active_stack,
offset=offset,
implant_type=well_type,
well_type=well_type,
directions="nonpref")
self.add_via_stack_center(offset=offset,
from_layer=self.active_stack[2],
to_layer=self.supply_layer)
if well_type == "p":
pin = "gnd_tap"
self.gnd_taps.append(self.add_layout_pin_rect_center(text=pin, layer=self.supply_layer, offset=offset))
else:
pin = "vdd_tap"
self.vdd_taps.append(self.add_layout_pin_rect_center(text=pin, layer=self.supply_layer, offset=offset))

View File

@ -20,3 +20,10 @@ class row_cap_bitcell_1port(bitcell_base):
debug.info(2, "Create row_cap bitcell 1 port object")
self.no_instances = True
def build_graph(self, graph, inst_name, port_nets):
"""
Adds edges based on inputs/outputs.
Overrides base class function.
"""
pass

View File

@ -20,3 +20,10 @@ class row_cap_bitcell_2port(bitcell_base):
debug.info(2, "Create row_cap bitcell 2 port object")
self.no_instances = True
def build_graph(self, graph, inst_name, port_nets):
"""
Adds edges based on inputs/outputs.
Overrides base class function.
"""
pass

View File

@ -53,6 +53,14 @@ class options(optparse.Values):
num_spare_rows = 0
num_spare_cols = 0
###################
# ROM configuration options
###################
rom_endian = "little"
rom_data = None
strap_spacing = 8
scramble_bits = True
###################
# Optimization options
###################

161
compiler/rom.py Normal file
View File

@ -0,0 +1,161 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import os
import shutil
import datetime
from openram import debug
from openram import rom_config as config
from openram import OPTS, print_time
class rom():
"""
This is not a design module, but contains an ROM design instance.
"""
def __init__(self, rom_config=None, name=None):
# Create default configs if custom config isn't provided
if rom_config is None:
rom_config = config(rom_data=OPTS.rom_data,
word_size=OPTS.word_size,
words_per_row=OPTS.words_per_row,
rom_endian=OPTS.rom_endian,
scramble_bits=OPTS.scramble_bits,
strap_spacing=OPTS.strap_spacing)
if name is None:
name = OPTS.output_name
rom_config.set_local_config(self)
# reset the static duplicate name checker for unit tests
# in case we create more than one ROM
from openram.base import design
design.name_map=[]
debug.info(2, "create rom of size {0} with {1} num of words".format(self.word_size,
self.num_words))
start_time = datetime.datetime.now()
self.name = name
import openram.modules.rom_bank as rom
self.r = rom(name, rom_config)
self.r.create_netlist()
if not OPTS.netlist_only:
self.r.create_layout()
if not OPTS.is_unit_test:
print_time("ROM creation", datetime.datetime.now(), start_time)
def sp_write(self, name, lvs=False, trim=False):
self.r.sp_write(name, lvs, trim)
def gds_write(self, name):
self.r.gds_write(name)
def verilog_write(self, name):
self.r.verilog_write(name)
def extended_config_write(self, name):
"""Dump config file with all options.
Include defaults and anything changed by input config."""
f = open(name, "w")
var_dict = dict((name, getattr(OPTS, name)) for name in dir(OPTS) if not name.startswith('__') and not callable(getattr(OPTS, name)))
for var_name, var_value in var_dict.items():
if isinstance(var_value, str):
f.write(str(var_name) + " = " + "\"" + str(var_value) + "\"\n")
else:
f.write(str(var_name) + " = " + str(var_value)+ "\n")
f.close()
def save(self):
""" Save all the output files while reporting time to do it as well. """
# Import this at the last minute so that the proper tech file
# is loaded and the right tools are selected
from openram import verify
# Save the spice file
start_time = datetime.datetime.now()
spname = OPTS.output_path + self.r.name + ".sp"
debug.print_raw("SP: Writing to {0}".format(spname))
self.sp_write(spname)
print_time("Spice writing", datetime.datetime.now(), start_time)
if not OPTS.netlist_only:
# Write the layout
start_time = datetime.datetime.now()
gdsname = OPTS.output_path + self.r.name + ".gds"
debug.print_raw("GDS: Writing to {0}".format(gdsname))
self.gds_write(gdsname)
if OPTS.check_lvsdrc:
verify.write_drc_script(cell_name=self.r.name,
gds_name=os.path.basename(gdsname),
extract=True,
final_verification=True,
output_path=OPTS.output_path)
print_time("GDS", datetime.datetime.now(), start_time)
# Save the LVS file
start_time = datetime.datetime.now()
lvsname = OPTS.output_path + self.r.name + ".lvs.sp"
debug.print_raw("LVS: Writing to {0}".format(lvsname))
self.sp_write(lvsname, lvs=True)
if not OPTS.netlist_only and OPTS.check_lvsdrc:
verify.write_lvs_script(cell_name=self.r.name,
gds_name=os.path.basename(gdsname),
sp_name=os.path.basename(lvsname),
final_verification=True,
output_path=OPTS.output_path)
print_time("LVS writing", datetime.datetime.now(), start_time)
# Save the extracted spice file
if OPTS.use_pex:
start_time = datetime.datetime.now()
# Output the extracted design if requested
pexname = OPTS.output_path + self.r.name + ".pex.sp"
spname = OPTS.output_path + self.r.name + ".sp"
verify.run_pex(self.r.name, gdsname, spname, output=pexname)
sp_file = pexname
print_time("Extraction", datetime.datetime.now(), start_time)
else:
# Use generated spice file for characterization
sp_file = spname
# Save a functional simulation file
# TODO: Characterize the design
# Write the config file
start_time = datetime.datetime.now()
from shutil import copyfile
copyfile(OPTS.config_file, OPTS.output_path + OPTS.output_name + '.py')
debug.print_raw("Config: Writing to {0}".format(OPTS.output_path + OPTS.output_name + '.py'))
print_time("Config", datetime.datetime.now(), start_time)
# TODO: Write the datasheet
# TODO: Write a verilog model
# start_time = datetime.datetime.now()
# vname = OPTS.output_path + self.r.name + '.v'
# debug.print_raw("Verilog: Writing to {0}".format(vname))
# self.verilog_write(vname)
# print_time("Verilog", datetime.datetime.now(), start_time)
# Write out options if specified
if OPTS.output_extended_config:
start_time = datetime.datetime.now()
oname = OPTS.output_path + OPTS.output_name + "_extended.py"
debug.print_raw("Extended Config: Writing to {0}".format(oname))
self.extended_config_write(oname)
print_time("Extended Config", datetime.datetime.now(), start_time)

123
compiler/rom_config.py Normal file
View File

@ -0,0 +1,123 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from typing import List
from math import log, sqrt, ceil
from openram import debug
from openram.sram_factory import factory
from openram import OPTS
class rom_config:
""" This is a structure that is used to hold the ROM configuration options. """
def __init__(self, word_size, rom_data, words_per_row=None, rom_endian="little", scramble_bits=True, strap_spacing=8):
self.word_size = word_size
self.word_bits = self.word_size * 8
self.rom_data = rom_data
self.strap_spacing = strap_spacing
# TODO: This currently does nothing. It should change the behavior of the chunk funciton.
self.endian = rom_endian
# This should pretty much always be true. If you want to make silicon art you might set to false
self.scramble_bits = scramble_bits
# This will get over-written when we determine the organization
self.words_per_row = words_per_row
self.compute_sizes()
def __str__(self):
""" override print function output """
config_items = ["word_size",
"num_words",
"words_per_row",
"endian",
"strap_spacing",
"rom_data"]
str = ""
for item in config_items:
val = getattr(self, item)
str += "{} : {}\n".format(item, val)
return str
def set_local_config(self, module):
""" Copy all of the member variables to the given module for convenience """
members = [attr for attr in dir(self) if not callable(getattr(self, attr)) and not attr.startswith("__")]
# Copy all the variables to the local module
for member in members:
setattr(module, member, getattr(self, member))
def compute_sizes(self):
""" Computes the organization of the memory using data size by trying to make it a rectangle."""
# Read data as hexidecimal text file
hex_file = open(self.rom_data, 'r')
hex_data = hex_file.read()
# Convert from hex into an int
data_int = int(hex_data, 16)
# Then from int into a right aligned, zero padded string
bin_string = bin(data_int)[2:].zfill(len(hex_data) * 4)
# Then turn the string into a list of ints
bin_data = list(bin_string)
raw_data = [int(x) for x in bin_data]
# data size in bytes
data_size = len(raw_data) / 8
self.num_words = int(data_size / self.word_size)
# If this was hard coded, don't dynamically compute it!
if not self.words_per_row:
# Row size if the array was square
bytes_per_row = sqrt(self.num_words)
# Heuristic to value longer wordlines over long bitlines.
# The extra factor of 2 in the denominator should make the array less square
self.words_per_row = int(ceil(bytes_per_row /(2*self.word_size)))
self.cols = self.words_per_row * self.word_size * 8
self.rows = int(self.num_words / self.words_per_row)
self.chunk_data(raw_data)
# Set word_per_row in OPTS
OPTS.words_per_row = self.words_per_row
debug.info(1, "Read rom data file: length {0} bytes, {1} words, set number of cols to {2}, rows to {3}, with {4} words per row".format(data_size, self.num_words, self.cols, self.rows, self.words_per_row))
def chunk_data(self, raw_data: List[int]):
"""
Chunks a flat list of bits into rows based on the calculated ROM sizes. Handles scrambling of data
"""
bits_per_row = self.cols
chunked_data = []
for i in range(0, len(raw_data), bits_per_row):
row_data = raw_data[i:i + bits_per_row]
if len(row_data) < bits_per_row:
row_data = [0] * (bits_per_row - len(row_data)) + row_data
chunked_data.append(row_data)
self.data = chunked_data
if self.scramble_bits:
scrambled_chunked = []
for row_data in chunked_data:
scambled_data = []
for bit in range(self.word_bits):
for word in range(self.words_per_row):
scambled_data.append(row_data[bit + word * self.word_bits])
scrambled_chunked.append(scambled_data)
self.data = scrambled_chunked

View File

@ -28,12 +28,10 @@ class column_mux_pbitcell_test(openram_test):
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
factory.reset()
debug.info(2, "Checking column mux for pbitcell (innermost connections)")
tx = factory.create(module_type="column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(tx)
factory.reset()
debug.info(2, "Checking column mux for pbitcell (outermost connections)")
tx = factory.create(module_type="column_mux",tx_size=8, bitcell_bl="bl2", bitcell_br="br2")
self.local_check(tx)

View File

@ -24,20 +24,10 @@ class replica_pbitcell_test(openram_test):
from openram.modules import dummy_pbitcell
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 0
OPTS.num_w_ports = 0
factory.reset()
debug.info(2, "Checking dummy bitcell using pbitcell (small cell)")
tx = dummy_pbitcell(name="rpbc")
self.local_check(tx)
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
factory.reset()
debug.info(2, "Checking dummy bitcell using pbitcell (large cell)")
tx = dummy_pbitcell(name="rpbc")
self.local_check(tx)

View File

@ -0,0 +1,43 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import sys, os
import unittest
from testutils import *
import openram
from openram import debug
from openram.sram_factory import factory
from openram import OPTS
class dummy_pbitcell_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
openram.init_openram(config_file, is_unit_test=True)
from openram.modules import dummy_pbitcell
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 0
OPTS.num_w_ports = 0
debug.info(2, "Checking dummy bitcell using pbitcell (small cell)")
tx = dummy_pbitcell(name="rpbc")
self.local_check(tx)
openram.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = openram.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -26,7 +26,6 @@ class pbitcell_test(openram_test):
OPTS.num_rw_ports=1
OPTS.num_w_ports=1
OPTS.num_r_ports=1
factory.reset()
debug.info(2, "Bitcell with 1 of each port: read/write, write, and read")
tx = factory.create(module_type="pbitcell")
self.local_check(tx)
@ -34,7 +33,6 @@ class pbitcell_test(openram_test):
OPTS.num_rw_ports=0
OPTS.num_w_ports=1
OPTS.num_r_ports=1
factory.reset()
debug.info(2, "Bitcell with 0 read/write ports")
tx = factory.create(module_type="pbitcell")
self.local_check(tx)
@ -42,7 +40,6 @@ class pbitcell_test(openram_test):
OPTS.num_rw_ports=1
OPTS.num_w_ports=0
OPTS.num_r_ports=1
factory.reset()
debug.info(2, "Bitcell with 0 write ports")
tx = factory.create(module_type="pbitcell")
self.local_check(tx)
@ -50,7 +47,6 @@ class pbitcell_test(openram_test):
OPTS.num_rw_ports=1
OPTS.num_w_ports=1
OPTS.num_r_ports=0
factory.reset()
debug.info(2, "Bitcell with 0 read ports")
tx = factory.create(module_type="pbitcell")
self.local_check(tx)
@ -58,7 +54,6 @@ class pbitcell_test(openram_test):
OPTS.num_rw_ports=1
OPTS.num_w_ports=0
OPTS.num_r_ports=0
factory.reset()
debug.info(2, "Bitcell with 0 read ports and 0 write ports")
tx = factory.create(module_type="pbitcell")
self.local_check(tx)
@ -66,7 +61,6 @@ class pbitcell_test(openram_test):
OPTS.num_rw_ports=2
OPTS.num_w_ports=2
OPTS.num_r_ports=2
factory.reset()
debug.info(2, "Bitcell with 2 of each port: read/write, write, and read")
tx = factory.create(module_type="pbitcell")
self.local_check(tx)
@ -74,7 +68,6 @@ class pbitcell_test(openram_test):
OPTS.num_rw_ports=0
OPTS.num_w_ports=2
OPTS.num_r_ports=2
factory.reset()
debug.info(2, "Bitcell with 0 read/write ports")
tx = factory.create(module_type="pbitcell")
self.local_check(tx)
@ -82,7 +75,6 @@ class pbitcell_test(openram_test):
OPTS.num_rw_ports=2
OPTS.num_w_ports=0
OPTS.num_r_ports=2
factory.reset()
debug.info(2, "Bitcell with 0 write ports")
tx = factory.create(module_type="pbitcell")
self.local_check(tx)
@ -90,7 +82,6 @@ class pbitcell_test(openram_test):
OPTS.num_rw_ports=2
OPTS.num_w_ports=2
OPTS.num_r_ports=0
factory.reset()
debug.info(2, "Bitcell with 0 read ports")
tx = factory.create(module_type="pbitcell")
self.local_check(tx)
@ -98,7 +89,6 @@ class pbitcell_test(openram_test):
OPTS.num_rw_ports=2
OPTS.num_w_ports=0
OPTS.num_r_ports=0
factory.reset()
debug.info(2, "Bitcell with 0 read ports and 0 write ports")
tx = factory.create(module_type="pbitcell")
self.local_check(tx)

View File

@ -32,7 +32,6 @@ class precharge_test(openram_test):
tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(tx)
factory.reset()
debug.info(2, "Checking precharge for 1rw1r port 1")
tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl1", bitcell_br="br1")
self.local_check(tx)

View File

@ -28,17 +28,14 @@ class precharge_pbitcell_test(openram_test):
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
factory.reset()
debug.info(2, "Checking precharge for pbitcell (innermost connections)")
tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(tx)
factory.reset()
debug.info(2, "Checking precharge for pbitcell (innermost connections)")
tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl1", bitcell_br="br1")
self.local_check(tx)
factory.reset()
debug.info(2, "Checking precharge for pbitcell (outermost connections)")
tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl2", bitcell_br="br2")
self.local_check(tx)

View File

@ -24,20 +24,10 @@ class replica_pbitcell_test(openram_test):
from openram.modules import replica_pbitcell
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 0
OPTS.num_w_ports = 0
factory.reset()
debug.info(2, "Checking replica bitcell using pbitcell (small cell)")
tx = replica_pbitcell(name="rpbc")
self.local_check(tx)
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
factory.reset()
debug.info(2, "Checking replica bitcell using pbitcell (large cell)")
tx = replica_pbitcell(name="rpbc")
self.local_check(tx)

View File

@ -0,0 +1,43 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import sys, os
import unittest
from testutils import *
import openram
from openram import debug
from openram.sram_factory import factory
from openram import OPTS
class replica_pbitcell_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
openram.init_openram(config_file, is_unit_test=True)
from openram.modules import replica_pbitcell
OPTS.bitcell = "pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 0
OPTS.num_w_ports = 0
debug.info(2, "Checking replica bitcell using pbitcell (small cell)")
tx = replica_pbitcell(name="rpbc")
self.local_check(tx)
openram.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = openram.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -27,17 +27,14 @@ class hierarchical_decoder_pbitcell_test(openram_test):
OPTS.num_r_ports = 0
openram.setup_bitcell()
factory.reset()
debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
self.local_check(a)
factory.reset()
debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
self.local_check(a)
factory.reset()
debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", num_outputs=23)
self.local_check(a)
@ -46,7 +43,6 @@ class hierarchical_decoder_pbitcell_test(openram_test):
a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
self.local_check(a)
factory.reset()
debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", num_outputs=65)
self.local_check(a)
@ -55,7 +51,6 @@ class hierarchical_decoder_pbitcell_test(openram_test):
a = factory.create(module_type="hierarchical_decoder", num_outputs=128)
self.local_check(a)
factory.reset()
debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", num_outputs=341)
self.local_check(a)

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
import openram
from openram import OPTS
from openram.sram_factory import factory
from openram import debug
class rom_decoder_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
openram.init_openram(config_file, is_unit_test=True)
debug.info(2, "Testing 2x4 decoder for rom cell")
a = factory.create(module_type="rom_decoder", num_outputs=16, strap_spacing=4, fanout=16)
self.local_check(a)
openram.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = openram.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -27,7 +27,6 @@ class column_mux_pbitcell_test(openram_test):
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
factory.reset()
debug.info(1, "Testing sample for 2-way column_mux_array in multi-port")
a = factory.create(module_type="column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(a)

View File

@ -0,0 +1,36 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import sys, os
from testutils import *
import openram
from openram import debug
from openram.sram_factory import factory
from openram import OPTS
class rom_column_mux_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
openram.init_openram(config_file, is_unit_test=True)
debug.info(1, "Testing sample for 2-way rom column_mux_array")
a = factory.create(module_type="rom_column_mux_array", columns=16, word_size=8)
self.local_check(a)
openram.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = openram.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -28,7 +28,6 @@ class precharge_1rw_1r_test(openram_test):
OPTS.num_w_ports = 0
openram.setup_bitcell()
factory.reset()
debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell (port 0)")
pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(pc)

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
import openram
from openram import OPTS
from openram.sram_factory import factory
from openram import debug
class rom_decoder_buffer_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
openram.init_openram(config_file, is_unit_test=True)
debug.info(2, "Testing 4 col decoder buffer for rom decoder")
a = factory.create(module_type="rom_address_control_array", cols=4)
self.local_check(a)
openram.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = openram.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
import openram
from openram import OPTS
from openram.sram_factory import factory
from openram import debug
class rom_precharge_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
openram.init_openram(config_file, is_unit_test=True)
debug.info(2, "Testing precharge array for rom cell")
a = factory.create(module_type="rom_precharge_array", cols=4, strap_spacing=2)
self.local_check(a)
openram.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = openram.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -28,7 +28,6 @@ class wordline_driver_array_pbitcell_test(openram_test):
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Checking driver (multi-port case)")
tx = factory.create(module_type="wordline_driver_array", rows=8, cols=64)
self.local_check(tx)

View File

@ -28,7 +28,6 @@ class sense_amp_pbitcell_test(openram_test):
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2 (multi-port case)")
a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=2)
self.local_check(a)

View File

@ -37,7 +37,6 @@ class sense_amp_array_spare_cols_test(openram_test):
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2, num_spare_cols=2 (multi-port case)")
a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=2, num_spare_cols=2)
self.local_check(a)

View File

@ -0,0 +1,38 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import sys, os
import unittest
from testutils import *
import openram
from openram import debug
from openram.sram_factory import factory
from openram import OPTS
class wordline_driver_array_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
openram.init_openram(config_file, is_unit_test=True)
# check wordline driver for single port
debug.info(2, "Checking driver")
tx = factory.create(module_type="rom_wordline_driver_array", rows=8, fanout=32)
self.local_check(tx)
openram.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = openram.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -28,7 +28,6 @@ class write_driver_pbitcell_test(openram_test):
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case)")
a = factory.create(module_type="write_driver_array", columns=8, word_size=8)
self.local_check(a)

View File

@ -37,7 +37,6 @@ class write_driver_array_spare_cols_test(openram_test):
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case and num_spare_cols=3")
a = factory.create(module_type="write_driver_array", columns=8, word_size=8, num_spare_cols=3)
self.local_check(a)

View File

@ -28,7 +28,6 @@ class write_driver_pbitcell_test(openram_test):
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing write_driver_array for columns=8, word_size=8, write_size=4 (multi-port case)")
a = factory.create(module_type="write_driver_array", columns=8, word_size=8, write_size=4)
self.local_check(a)

View File

@ -28,7 +28,6 @@ class write_mask_and_array_pbitcell_test(openram_test):
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
factory.reset()
debug.info(2, "Testing write_mask_and_array for columns=8, word_size=8, write_size=4 (multi-port case)")
a = factory.create(module_type="write_mask_and_array", columns=8, word_size=8, write_size=4)
self.local_check(a)

View File

@ -24,7 +24,6 @@ class capped_replica_bitcell_array_test(openram_test):
OPTS.num_r_ports = 0
OPTS.num_w_ports = 0
factory.reset()
debug.info(2, "Testing 4x4 array for bitcell")
a = factory.create(module_type="capped_replica_bitcell_array", cols=7, rows=5, rbl=[1, 0], left_rbl=[0])
self.local_check(a)

View File

@ -24,7 +24,6 @@ class capped_replica_bitcell_array_test(openram_test):
OPTS.num_r_ports = 0
OPTS.num_w_ports = 0
factory.reset()
debug.info(2, "Testing 4x4 array for bitcell")
a = factory.create(module_type="capped_replica_bitcell_array", cols=7, rows=5, rbl=[1, 0])
self.local_check(a)

View File

@ -24,7 +24,6 @@ class replica_bitcell_array_test(openram_test):
OPTS.num_r_ports = 0
OPTS.num_w_ports = 0
factory.reset()
debug.info(2, "Testing 4x4 array for bitcell")
a = factory.create(module_type="replica_bitcell_array", cols=7, rows=5, rbl=[1, 0], left_rbl=[0])
self.local_check(a)

View File

@ -24,7 +24,6 @@ class replica_bitcell_array_test(openram_test):
OPTS.num_r_ports = 0
OPTS.num_w_ports = 0
factory.reset()
debug.info(2, "Testing 4x4 array for bitcell")
a = factory.create(module_type="replica_bitcell_array", cols=7, rows=5, rbl=[1, 0])
self.local_check(a)

View File

@ -31,18 +31,6 @@ class replica_pbitcell_array_test(openram_test):
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, rbl=[1, 1], left_rbl=[0], right_rbl=[1])
self.local_check(a)
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.dummy_bitcell = "dummy_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 0
OPTS.num_w_ports = 0
factory.reset()
debug.info(2, "Testing 4x4 array for pbitcell")
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, rbl=[1, 0], left_rbl=[0])
self.local_check(a)
openram.end_openram()

View File

@ -0,0 +1,42 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
# All rights reserved.
#
import sys, os
import unittest
from testutils import *
import openram
from openram import debug
from openram.sram_factory import factory
from openram import OPTS
class replica_pbitcell_array_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
openram.init_openram(config_file, is_unit_test=True)
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.dummy_bitcell = "dummy_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 0
OPTS.num_w_ports = 0
debug.info(2, "Testing 4x4 array for pbitcell")
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, rbl=[1, 0], left_rbl=[0])
self.local_check(a)
openram.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = openram.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -0,0 +1,41 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import sys, os
import unittest
from testutils import *
import openram
from openram import debug
from openram.sram_factory import factory
from openram import OPTS
class rom_array_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
openram.init_openram(config_file, is_unit_test=True)
debug.info(2, "Testing 4x4 array for rom cell")
data = [[1, 0, 0, 1, 0, 0, 1, 1, 0], [0, 1, 0, 0, 1, 0, 1, 0, 0], [0, 0, 1, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 1], [0, 1, 0, 0, 1, 1, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 0, 0, 1, 0, 0], [1, 0, 0, 1, 0, 0, 0, 1, 0]]
a = factory.create(module_type="rom_base_array", cols=9, rows=8, bitmap=data, strap_spacing=4, pitch_match=True)
self.local_check(a)
a.sp_write(OPTS.openram_temp + 'simulation_file.sp')
openram.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = openram.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
import openram
from openram import OPTS
from openram.sram_factory import factory
from openram import debug
class rom_decoder_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
openram.init_openram(config_file, is_unit_test=True)
debug.info(2, "Testing control logic for rom cell")
a = factory.create(module_type="rom_control_logic", num_outputs=4, clk_fanout=4, height=40)
self.local_check(a)
openram.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = openram.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -32,7 +32,6 @@ class port_data_1rw_1r_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=16
factory.reset()
c.recompute_sizes()
debug.info(1, "Sixteen way column mux")
a = factory.create("port_data", sram_config=c, port=0)

View File

@ -36,7 +36,6 @@ class port_data_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=16
factory.reset()
c.recompute_sizes()
debug.info(1, "Sixteen way column mux")
a = factory.create("port_data", sram_config=c, port=0)

View File

@ -31,7 +31,6 @@ class port_data_1rw_1r_test(openram_test):
c.num_words=32
c.words_per_row=2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create("port_data", sram_config=c, port=0)

View File

@ -35,7 +35,6 @@ class port_data_test(openram_test):
c.num_words=32
c.words_per_row=2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create("port_data", sram_config=c, port=0)

View File

@ -31,7 +31,6 @@ class port_data_1rw_1r_test(openram_test):
c.num_words=64
c.words_per_row=4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create("port_data", sram_config=c, port=0)

View File

@ -35,7 +35,6 @@ class port_data_test(openram_test):
c.num_words=64
c.words_per_row=4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create("port_data", sram_config=c, port=0)

View File

@ -32,7 +32,6 @@ class port_data_1rw_1r_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("port_data", sram_config=c, port=0)

View File

@ -36,7 +36,6 @@ class port_data_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("port_data", sram_config=c, port=0)

View File

@ -30,7 +30,6 @@ class port_data_1rw_1r_test(openram_test):
num_words=16)
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("port_data", sram_config=c, port=0)

View File

@ -34,7 +34,6 @@ class port_data_test(openram_test):
num_spare_rows=num_spare_rows)
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("port_data", sram_config=c, port=0)

View File

@ -26,7 +26,6 @@ class port_data_spare_cols_test(openram_test):
num_spare_cols=3)
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -34,7 +33,6 @@ class port_data_spare_cols_test(openram_test):
c.num_words=32
c.words_per_row=2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -43,7 +41,6 @@ class port_data_spare_cols_test(openram_test):
c.num_words=64
c.words_per_row=4
c.num_spare_cols=3
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -53,7 +50,6 @@ class port_data_spare_cols_test(openram_test):
c.num_words=128
c.words_per_row=8
c.num_spare_cols=4
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -66,7 +62,6 @@ class port_data_spare_cols_test(openram_test):
c.num_words=16
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -76,7 +71,6 @@ class port_data_spare_cols_test(openram_test):
c.num_words=32
c.words_per_row=2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -86,7 +80,6 @@ class port_data_spare_cols_test(openram_test):
c.num_words=64
c.words_per_row=4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -97,7 +90,6 @@ class port_data_spare_cols_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("port_data", sram_config=c, port=0)

View File

@ -31,7 +31,6 @@ class port_data_wmask_1rw_1r_test(openram_test):
num_words=16)
c.words_per_row = 1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -39,7 +38,6 @@ class port_data_wmask_1rw_1r_test(openram_test):
c.num_words = 32
c.words_per_row = 2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -47,7 +45,6 @@ class port_data_wmask_1rw_1r_test(openram_test):
c.num_words = 64
c.words_per_row = 4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -55,7 +52,6 @@ class port_data_wmask_1rw_1r_test(openram_test):
c.num_words = 128
c.words_per_row = 8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -68,7 +64,6 @@ class port_data_wmask_1rw_1r_test(openram_test):
c.num_words = 16
c.words_per_row = 1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -78,7 +73,6 @@ class port_data_wmask_1rw_1r_test(openram_test):
#
c.num_words = 32
c.words_per_row = 2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -88,7 +82,6 @@ class port_data_wmask_1rw_1r_test(openram_test):
c.num_words = 64
c.words_per_row = 4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -99,7 +92,6 @@ class port_data_wmask_1rw_1r_test(openram_test):
c.word_size = 8
c.num_words = 128
c.words_per_row = 8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("port_data", sram_config=c, port=0)

View File

@ -35,7 +35,6 @@ class port_data_wmask_test(openram_test):
num_spare_rows=num_spare_rows)
c.words_per_row = 1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -43,7 +42,6 @@ class port_data_wmask_test(openram_test):
c.num_words = 32
c.words_per_row = 2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -51,7 +49,6 @@ class port_data_wmask_test(openram_test):
c.num_words = 64
c.words_per_row = 4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -59,7 +56,6 @@ class port_data_wmask_test(openram_test):
c.num_words = 128
c.words_per_row = 8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -72,7 +68,6 @@ class port_data_wmask_test(openram_test):
c.num_words = 16
c.words_per_row = 1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -82,7 +77,6 @@ class port_data_wmask_test(openram_test):
#
c.num_words = 32
c.words_per_row = 2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -92,7 +86,6 @@ class port_data_wmask_test(openram_test):
c.num_words = 64
c.words_per_row = 4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create("port_data", sram_config=c, port=0)
@ -103,7 +96,6 @@ class port_data_wmask_test(openram_test):
c.word_size = 8
c.num_words = 128
c.words_per_row = 8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("port_data", sram_config=c, port=0)

View File

@ -29,7 +29,6 @@ class multi_bank_test(openram_test):
c.num_banks=2
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("bank", sram_config=c)
@ -37,7 +36,6 @@ class multi_bank_test(openram_test):
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)
@ -45,7 +43,6 @@ class multi_bank_test(openram_test):
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)
@ -54,7 +51,6 @@ class multi_bank_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("bank", sram_config=c)

View File

@ -34,7 +34,6 @@ class multi_bank_test(openram_test):
c.num_banks=2
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("bank", sram_config=c)
@ -42,7 +41,6 @@ class multi_bank_test(openram_test):
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)
@ -50,7 +48,6 @@ class multi_bank_test(openram_test):
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)
@ -59,7 +56,6 @@ class multi_bank_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("bank", sram_config=c)

View File

@ -36,7 +36,6 @@ class psingle_bank_test(openram_test):
num_words=16)
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create(module_type="bank", sram_config=c)
@ -44,7 +43,6 @@ class psingle_bank_test(openram_test):
c.num_words=32
c.words_per_row=2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create(module_type="bank", sram_config=c)
@ -52,7 +50,6 @@ class psingle_bank_test(openram_test):
c.num_words=64
c.words_per_row=4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create(module_type="bank", sram_config=c)
@ -61,7 +58,6 @@ class psingle_bank_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create(module_type="bank", sram_config=c)

View File

@ -0,0 +1,44 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
import openram
from openram import OPTS
from openram.sram_factory import factory
from openram import debug
class rom_bank_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
openram.init_openram(config_file, is_unit_test=True)
debug.info(1, "Testing rom cell")
test_data = "{0}/{1}/rom_data_64B".format(os.getenv("OPENRAM_HOME"), OPTS.rom_data_dir)
from openram import rom_config
conf = rom_config(strap_spacing = 8,
rom_data = test_data,
word_size = 1)
a = factory.create(module_type="rom_bank", rom_config=conf)
a.sp_write(OPTS.openram_temp + 'simulation_file.sp')
self.local_check(a)
openram.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = openram.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -32,7 +32,6 @@ class single_bank_1rw_1r_test(openram_test):
num_words=128)
c.words_per_row=16
factory.reset()
c.recompute_sizes()
debug.info(1, "Sixteen way column mux")
a = factory.create(module_type="bank", sram_config=c)

View File

@ -38,7 +38,6 @@ class single_bank_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=16
factory.reset()
c.recompute_sizes()
debug.info(1, "Sixteen way column mux")
a = factory.create("bank", sram_config=c)

View File

@ -32,7 +32,6 @@ class single_bank_1w_1r_test(openram_test):
num_words=16)
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create(module_type="bank", sram_config=c)
@ -40,7 +39,6 @@ class single_bank_1w_1r_test(openram_test):
c.num_words=32
c.words_per_row=2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create(module_type="bank", sram_config=c)
@ -48,7 +46,6 @@ class single_bank_1w_1r_test(openram_test):
c.num_words=64
c.words_per_row=4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create(module_type="bank", sram_config=c)
@ -57,7 +54,6 @@ class single_bank_1w_1r_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create(module_type="bank", sram_config=c)

View File

@ -33,7 +33,6 @@ class single_bank_1rw_1r_test(openram_test):
c.num_words=32
c.words_per_row=2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create(module_type="bank", sram_config=c)

View File

@ -28,7 +28,6 @@ class single_bank_test(openram_test):
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)

View File

@ -33,7 +33,6 @@ class single_bank_1rw_1r_test(openram_test):
c.num_words=64
c.words_per_row=4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create(module_type="bank", sram_config=c)

View File

@ -28,7 +28,6 @@ class single_bank_test(openram_test):
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)

View File

@ -34,7 +34,6 @@ class single_bank_1rw_1r_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create(module_type="bank", sram_config=c)

View File

@ -38,7 +38,6 @@ class single_bank_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("bank", sram_config=c)

View File

@ -33,7 +33,6 @@ class single_bank_1rw_1r_test(openram_test):
num_words=16)
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create(module_type="bank", sram_config=c)
@ -41,7 +40,6 @@ class single_bank_1rw_1r_test(openram_test):
c.num_words=32
c.words_per_row=2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create(module_type="bank", sram_config=c)
@ -49,7 +47,6 @@ class single_bank_1rw_1r_test(openram_test):
c.num_words=64
c.words_per_row=4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create(module_type="bank", sram_config=c)
@ -58,7 +55,6 @@ class single_bank_1rw_1r_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create(module_type="bank", sram_config=c)

View File

@ -32,7 +32,6 @@ class single_bank_1rw_1r_test(openram_test):
num_words=16)
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create(module_type="bank", sram_config=c)

View File

@ -36,7 +36,6 @@ class single_bank_test(openram_test):
num_spare_rows=num_spare_rows)
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("bank", sram_config=c)

View File

@ -28,7 +28,6 @@ class single_bank_spare_cols_test(openram_test):
num_spare_cols=3)
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("bank", sram_config=c)
@ -36,7 +35,6 @@ class single_bank_spare_cols_test(openram_test):
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)
@ -44,7 +42,6 @@ class single_bank_spare_cols_test(openram_test):
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)
@ -53,7 +50,6 @@ class single_bank_spare_cols_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("bank", sram_config=c)

View File

@ -34,7 +34,6 @@ class single_bank_wmask_1rw_1r_test(openram_test):
num_banks=1)
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("bank", sram_config=c)
@ -42,7 +41,6 @@ class single_bank_wmask_1rw_1r_test(openram_test):
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)
@ -50,7 +48,6 @@ class single_bank_wmask_1rw_1r_test(openram_test):
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)
@ -58,7 +55,6 @@ class single_bank_wmask_1rw_1r_test(openram_test):
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("bank", sram_config=c)

View File

@ -29,7 +29,6 @@ class single_bank_wmask_test(openram_test):
num_banks=1)
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("bank", sram_config=c)
@ -37,7 +36,6 @@ class single_bank_wmask_test(openram_test):
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)
@ -45,7 +43,6 @@ class single_bank_wmask_test(openram_test):
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)
@ -53,7 +50,6 @@ class single_bank_wmask_test(openram_test):
c.num_words=128
c.words_per_row=8
factory.reset()
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = factory.create("bank", sram_config=c)

View File

@ -38,7 +38,6 @@ class sram_2bank_test(openram_test):
c.num_words,
c.words_per_row,
c.num_banks))
factory.reset()
a = factory.create(module_type="sram", sram_config=c)
self.local_check(a, final_verification=True)
@ -54,7 +53,6 @@ class sram_2bank_test(openram_test):
c.num_words,
c.words_per_row,
c.num_banks))
factory.reset()
a = factory.create(module_type="sram", sram_config=c)
self.local_check(a, final_verification=True)
@ -70,7 +68,6 @@ class sram_2bank_test(openram_test):
c.num_words,
c.words_per_row,
c.num_banks))
factory.reset()
a = factory.create(module_type="sram", sram_config=c)
self.local_check(a, final_verification=True)
@ -87,7 +84,6 @@ class sram_2bank_test(openram_test):
c.num_words,
c.words_per_row,
c.num_banks))
factory.reset()
a = factory.create(module_type="sram", sram_config=c)
self.local_check(a, final_verification=True)

View File

@ -15,28 +15,14 @@ TEST_STAMPS= $(addsuffix .ok,$(TEST_BASES))
OPENRAM_DIR = $(OPENRAM_HOME)/tests
RESULTS_DIR = $(OPENRAM_DIR)/results
# Use % for all techs:
# Use % for all techs:
# %/test.py
# or a specific tech:
# freepdk45/test.py
BROKEN_STAMPS = \
sky130/01_library_test.ok \
sky130/04_column_mux_pbitcell_test.ok \
sky130/04_dummy_pbitcell_test.ok \
sky130/04_pbitcell_test.ok \
sky130/04_pnand4_test.ok \
sky130/04_pand4_test.ok \
sky130/04_precharge_pbitcell_test.ok \
sky130/04_replica_pbitcell_test.ok \
sky130/05_pbitcell_array_test.ok \
sky130/05_bitcell_array_test.ok \
sky130/05_bitcell_array_1rw_1r_test.ok \
sky130/05_dummy_array_test.ok \
%/06_hierarchical_decoder_4096row_test.ok \
sky130/07_column_mux_array_pbitcell_test.ok \
sky130/19_pmulti_bank_test.ok \
sky130/19_psingle_bank_test.ok \
sky130/19_bank_select_pbitcell_test.ok \
%/19_single_bank_16mux_1rw_1r_test.ok \
%/19_single_bank_16mux_test.ok \
%/20_sram_1bank_16mux_1rw_1r_test.ok \
@ -47,11 +33,6 @@ BROKEN_STAMPS = \
%/20_psram_1bank_2mux_test.ok \
%/21_hspice_delay_test.ok \
%/21_hspice_setuphold_test.ok \
sky130/20_psram_1bank_4mux_1rw_1r_test.ok \
sky130/22_psram_1bank_2mux_func_test.ok \
sky130/22_psram_1bank_4mux_func_test.ok \
sky130/22_psram_1bank_8mux_func_test.ok \
sky130/22_psram_1bank_nomux_func_test.ok \
%/22_psram_1bank_2mux_func_test.ok \
%/22_psram_1bank_4mux_func_test.ok \
%/22_psram_1bank_8mux_func_test.ok \
@ -66,11 +47,6 @@ BROKEN_STAMPS = \
%/22_sram_1bank_nomux_sparecols_func_test.ok \
%/22_sram_1bank_wmask_1rw_1r_func_test.ok \
%/22_sram_wmask_func_test.ok \
sky130/23_lib_sram_linear_regression_test.ok \
sky130/23_lib_sram_model_corners_test.ok \
sky130/23_lib_sram_model_test.ok \
sky130/23_lib_sram_prune_test.ok \
sky131/23_lib_sram_test.ok \
%/26_hspice_pex_pinv_test.ok \
%/27_verilog_multibank_test.ok \
%/50_riscv_1k_1rw1r_func_test.ok \
@ -87,6 +63,51 @@ BROKEN_STAMPS = \
%/50_riscv_512b_1rw_func_test.ok \
%/50_riscv_8k_1rw1r_func_test.ok \
%/50_riscv_8k_1rw_func_test.ok \
freepdk45/06_rom_decoder_test.ok \
freepdk45/07_rom_column_mux_array_test.ok \
freepdk45/08_rom_decoder_buffer_array_test.ok \
freepdk45/08_rom_precharge_array_test.ok \
freepdk45/10_rom_wordline_driver_array_test.ok \
freepdk45/14_rom_array_test.ok \
freepdk45/16_rom_control_logic_test.ok \
freepdk45/19_rom_bank_test.ok \
freepdk45/21_xyce_delay_test.ok \
scn4m_subm/06_rom_decoder_test.ok \
scn4m_subm/07_rom_column_mux_array_test.ok \
scn4m_subm/08_rom_decoder_buffer_array_test.ok \
scn4m_subm/08_rom_precharge_array_test.ok \
scn4m_subm/10_rom_wordline_driver_array_test.ok \
scn4m_subm/14_rom_array_test.ok \
scn4m_subm/16_rom_control_logic_test.ok \
scn4m_subm/19_rom_bank_test.ok \
scn4m_subm/19_single_bank_global_bitline_test.ok \
scn4m_subm/21_xyce_delay_test.ok \
sky130/01_library_test.ok \
sky130/04_column_mux_pbitcell_test.ok \
sky130/04_dummy_pbitcell_test.ok \
sky130/04_pbitcell_test.ok \
sky130/04_pnand4_test.ok \
sky130/04_pand4_test.ok \
sky130/04_precharge_pbitcell_test.ok \
sky130/04_replica_pbitcell_test.ok \
sky130/05_pbitcell_array_test.ok \
sky130/05_bitcell_array_test.ok \
sky130/05_bitcell_array_1rw_1r_test.ok \
sky130/05_dummy_array_test.ok \
sky130/07_column_mux_array_pbitcell_test.ok \
sky130/19_pmulti_bank_test.ok \
sky130/19_psingle_bank_test.ok \
sky130/19_bank_select_pbitcell_test.ok \
sky130/20_psram_1bank_4mux_1rw_1r_test.ok \
sky130/22_psram_1bank_2mux_func_test.ok \
sky130/22_psram_1bank_4mux_func_test.ok \
sky130/22_psram_1bank_8mux_func_test.ok \
sky130/22_psram_1bank_nomux_func_test.ok \
sky130/23_lib_sram_linear_regression_test.ok \
sky130/23_lib_sram_model_corners_test.ok \
sky130/23_lib_sram_model_test.ok \
sky130/23_lib_sram_prune_test.ok \
sky131/23_lib_sram_test.ok
gettech = $(word 1,$(subst /, ,$*))
getfile = $(word 2,$(subst /, ,$*))
@ -103,7 +124,7 @@ all: clean $(WORKING_TECH_TEST_STAMPS)
# Run a given technology
# e.g. make freepdk45
$(TECHS):
$(TECHS):
@$(MAKE) --no-print-directory $(filter-out $(BROKEN_STAMPS), $(addprefix $@/, $(TEST_STAMPS)))
.PHONY: $(TECHS)
@ -114,7 +135,7 @@ $(TEST_BASES):
.PHONY: $(TEST_BASES)
# To run a test in a given technology
%.ok:
%.ok:
# @echo "Running $(gettech) $(getfile) ... "
@rm -rf results/$*
@rm -rf results/$*.*
@ -127,10 +148,6 @@ else
endif
.DELETE_ON_ERROR: $(TEST_STAMPS)
.PHONY: docker-pull
docker-pull:
docker pull vlsida/openram-ubuntu:latest
clean:
@rm -rf $(TOP_DIR)/compiler/tests/results
.PHONE: clean
.PHONY: clean

View File

@ -17,3 +17,4 @@ check_lvsdrc = True
#route_supplies = False
output_name = "sram"
rom_data_dir = "tests/configs"

View File

@ -0,0 +1 @@
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFF

View File

@ -1,113 +0,0 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import re
import unittest
import sys, os
from openram import globals
from subunit import ProtocolTestCase, TestProtocolClient
from testtools import ConcurrentTestSuite
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
from testutils import *
header(__file__, OPTS.tech_name)
# get a list of all files in the tests directory
files = os.listdir(sys.path[0])
# load a file with all tests to skip in a given technology
# since tech_name is dynamically loaded, we can't use @skip directives
try:
skip_file_name = "{0}/tests/skip_tests_{1}.txt".format(os.getenv("OPENRAM_HOME"), OPTS.tech_name)
skip_file = open(skip_file_name, "r")
skip_tests = skip_file.read().splitlines()
for st in skip_tests:
debug.warning("Skipping: " + st)
except FileNotFoundError:
skip_tests = []
# assume any file that ends in "test.py" in it is a regression test
nametest = re.compile("test\.py$", re.IGNORECASE)
all_tests = list(filter(nametest.search, files))
filtered_tests = list(filter(lambda i: i not in skip_tests, all_tests))
filtered_tests.sort()
num_threads = OPTS.num_threads
def partition_unit_tests(suite, num_threads):
partitions = [list() for x in range(num_threads)]
for index, test in enumerate(suite):
partitions[index % num_threads].append(test)
return partitions
def fork_tests(num_threads):
results = []
test_partitions = partition_unit_tests(suite, num_threads)
suite._tests[:] = []
def do_fork(suite):
for test_partition in test_partitions:
test_suite = unittest.TestSuite(test_partition)
test_partition[:] = []
c2pread, c2pwrite = os.pipe()
pid = os.fork()
if pid == 0:
# PID of 0 is a child
try:
# Open a stream to write to the parent
stream = os.fdopen(c2pwrite, 'wb', 0)
os.close(c2pread)
sys.stdin.close()
test_suite_result = TestProtocolClient(stream)
test_suite.run(test_suite_result)
except EBADF:
try:
stream.write(traceback.format_exc())
finally:
os._exit(1)
os._exit(0)
else:
# PID >0 is the parent
# Collect all of the child streams and append to the results
os.close(c2pwrite)
stream = os.fdopen(c2pread, 'rb', 0)
test = ProtocolTestCase(stream)
results.append(test)
return results
return do_fork
# import all of the modules
filenameToModuleName = lambda f: os.path.splitext(f)[0]
moduleNames = map(filenameToModuleName, filtered_tests)
modules = map(__import__, moduleNames)
suite = unittest.TestSuite()
load = unittest.defaultTestLoader.loadTestsFromModule
suite.addTests(map(load, modules))
test_runner = unittest.TextTestRunner(verbosity=2, stream=sys.stderr)
if num_threads == 1:
final_suite = suite
else:
final_suite = ConcurrentTestSuite(suite, fork_tests(num_threads))
test_result = test_runner.run(final_suite)
# import verify
# verify.print_drc_stats()
# verify.print_lvs_stats()
# verify.print_pex_stats()
sys.exit(not test_result.wasSuccessful())

View File

@ -1,86 +0,0 @@
04_dummy_pbitcell_test.py
04_pbitcell_test.py
04_precharge_pbitcell_test.py
04_replica_pbitcell_test.py
04_column_mux_pbitcell_test.py
05_bitcell_array_test.py
05_dummy_array_test.py
05_pbitcell_array_test.py
06_hierarchical_decoder_pbitcell_test.py
06_hierarchical_decoder_test.py
06_hierarchical_predecode2x4_pbitcell_test.py
06_hierarchical_predecode2x4_test.py
06_hierarchical_predecode3x8_pbitcell_test.py
06_hierarchical_predecode3x8_test.py
06_hierarchical_predecode4x16_test.py
07_column_mux_array_pbitcell_test.py
08_wordline_driver_array_pbitcell_test.py
08_wordline_driver_array_test.py
09_sense_amp_array_test_pbitcell.py
09_sense_amp_array_test.py
10_write_driver_array_pbitcell_test.py
10_write_driver_array_test.py
10_write_driver_array_wmask_pbitcell_test.py
10_write_driver_array_wmask_test.py
10_write_mask_and_array_pbitcell_test.py
10_write_mask_and_array_test.py
12_tri_gate_array_test.py
14_replica_pbitcell_array_test.py
14_replica_bitcell_array_test.py
14_replica_column_test.py
14_replica_column_1rw_1r_test.py
18_port_address_test.py
18_port_data_test.py
18_port_data_wmask_test.py
19_bank_select_pbitcell_test.py
19_bank_select_test.py
19_psingle_bank_test.py
19_bank_select_pbitcell_test.py
19_pmulti_bank_test.py
19_multi_bank_test.py
19_psingle_bank_test.py
19_single_bank_1w_1r_test.py
19_single_bank_wmask_1rw_1r_test.py
19_single_bank_1rw_1r_test.py
19_single_bank_test.py
19_single_bank_wmask_test.py
20_psram_1bank_2mux_1rw_1w_test.py
20_psram_1bank_2mux_1rw_1w_wmask_test.py
20_psram_1bank_2mux_1w_1r_test.py
20_psram_1bank_2mux_test.py
20_psram_1bank_4mux_1rw_1r_test.py
20_sram_1bank_2mux_1w_1r_test.py
20_sram_1bank_2mux_test.py
20_sram_1bank_2mux_wmask_test.py
20_sram_1bank_32b_1024_wmask_test.py
20_sram_1bank_4mux_test.py
20_sram_1bank_8mux_test.py
20_sram_1bank_nomux_test.py
20_sram_1bank_nomux_wmask_test.py
20_sram_2bank_test.py
21_hspice_delay_test.py
21_hspice_setuphold_test.py
21_model_delay_test.py
21_ngspice_delay_test.py
21_ngspice_setuphold_test.py
22_psram_1bank_2mux_func_test.py
22_psram_1bank_4mux_func_test.py
22_psram_1bank_8mux_func_test.py
22_psram_1bank_nomux_func_test.py
22_sram_1bank_2mux_func_test.py
22_sram_1bank_4mux_func_test.py
22_sram_1bank_8mux_func_test.py
22_sram_1bank_nomux_func_test.py
22_sram_1rw_1r_1bank_nomux_func_test.py
22_sram_wmask_func_test.py
23_lib_sram_model_corners_test.py
23_lib_sram_model_test.py
23_lib_sram_prune_test.py
23_lib_sram_test.py
24_lef_sram_test.py
25_verilog_sram_test.py
26_hspice_pex_pinv_test.py
26_ngspice_pex_pinv_test.py
26_pex_test.py
30_openram_back_end_test.py
30_openram_front_end_test.py

Some files were not shown because too many files have changed in this diff Show More