diff --git a/.github/workflows/README.md b/.github/workflows/README.md
index b0e9ddf7..a4d19df1 100644
--- a/.github/workflows/README.md
+++ b/.github/workflows/README.md
@@ -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
diff --git a/.github/workflows/regress.yml b/.github/workflows/regress.yml
index b98153ab..9a8291aa 100644
--- a/.github/workflows/regress.yml
+++ b/.github/workflows/regress.yml
@@ -23,14 +23,14 @@ jobs:
run: |
export OPENRAM_HOME="${{ github.workspace }}/compiler"
export OPENRAM_TECH="${{ github.workspace }}/technology"
- #cd $OPENRAM_HOME/tests
- #export PDK_ROOT="${{ github.workspace }}/pdk"
- #make pdk
- #make install
+ export PDK_ROOT="${{ github.workspace }}/pdk"
+ make pdk
+ make install
- name: Regress
run: |
export OPENRAM_HOME="${{ github.workspace }}/compiler"
export OPENRAM_TECH="${{ github.workspace }}/technology"
+ export PDK_ROOT="${{ github.workspace }}/pdk"
export FREEPDK45="~/FreePDK45"
# KLAYOUT_PATH breaks klayout installation. Unset it for now...
unset KLAYOUT_PATH
diff --git a/.gitignore b/.gitignore
index f79d6d85..a045c64a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
.DS_Store
+.coverage*
*~
*.orig
*.rej
diff --git a/Makefile b/Makefile
index 467277cf..4a09922c 100644
--- a/Makefile
+++ b/Makefile
@@ -49,6 +49,9 @@ INSTALL_BASE_DIRS := gds_lib mag_lib sp_lib lvs_lib calibre_lvs_lib klayout_lvs_
INSTALL_BASE := $(OPENRAM_HOME)/../technology/sky130
INSTALL_DIRS := $(addprefix $(INSTALL_BASE)/,$(INSTALL_BASE_DIRS))
+# Remove this if you don't want to use conda
+USE_CONDA ?= 1
+
check-pdk-root:
ifndef PDK_ROOT
$(error PDK_ROOT is undefined, please export it before running make)
@@ -69,12 +72,23 @@ $(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"
+ifdef USE_CONDA
+ @source $(TOP_DIR)/miniconda/bin/activate && \
+ 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 && \
+ conda deactivate
+else
+ @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
+endif
$(SRAM_LIB_DIR): check-pdk-root
@echo "Cloning SRAM library..."
diff --git a/README.md b/README.md
index 46d43290..4c00b2dc 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,16 @@
-
+
# OpenRAM
[](https://www.python.org/)
-[](./LICENSE)
-[](https://github.com/VLSIDA/OpenRAM/archive/stable.zip)
-[](https://github.com/VLSIDA/OpenRAM/archive/dev.zip)
+[](./LICENSE)
+[](https://pypi.org/project/openram/)
An open-source static random access memory (SRAM) compiler.
+
+
# What is OpenRAM?
-
+
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,21 +45,26 @@ 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
+ [M. R. Guthaus, J. E. Stine, S. Ataei, B. Chen, B. Wu, M. Sarwar, "OpenRAM: An Open-Source Memory Compiler," Proceedings of the 35th International Conference on Computer-Aided Design (ICCAD), 2016.](https://escholarship.org/content/qt8x19c778/qt8x19c778_noSplash_b2b3fbbb57f1269f86d0de77865b0691.pdf)
+ [S. Ataei, J. Stine, M. Guthaus, “A 64 kb differential single-port 12T SRAM design with a bit-interleaving scheme for low-voltage operation in 32 nm SOI CMOS,” International Conference on Computer Design (ICCD), 2016, pp. 499-506.](https://escholarship.org/uc/item/99f6q9c9)
-+ [E. Ebrahimi, M. Guthaus, J. Renau, “Timing Speculative SRAM”, IEEE In- ternational Symposium on Circuits and Systems (ISCAS), 2017.](https://escholarship.org/content/qt7nn0j5x3/qt7nn0j5x3_noSplash_172457455e1aceba20694c3d7aa489b4.pdf)
++ [E. Ebrahimi, M. Guthaus, J. Renau, “Timing Speculative SRAM”, IEEE International Symposium on Circuits and Systems (ISCAS), 2017.](https://escholarship.org/content/qt7nn0j5x3/qt7nn0j5x3_noSplash_172457455e1aceba20694c3d7aa489b4.pdf)
+ [B. Wu, J.E. Stine, M.R. Guthaus, "Fast and Area-Efficient Word-Line Optimization", IEEE International Symposium on Circuits and Systems (ISCAS), 2019.](https://escholarship.org/content/qt98s4c1hp/qt98s4c1hp_noSplash_753dcc3e218f60aafff98ef77fb56384.pdf)
+ [B. Wu, M. Guthaus, "Bottom Up Approach for High Speed SRAM Word-line Buffer Insertion Optimization", IFIP/IEEE International Conference on Very Large Scale Integration (VLSI-SoC), 2019.](https://ieeexplore.ieee.org/document/8920325)
+ [H. Nichols, M. Grimes, J. Sowash, J. Cirimelli-Low, M. Guthaus "Automated Synthesis of Multi-Port Memories and Control", IFIP/IEEE International Conference on Very Large Scale Integration (VLSI-SoC), 2019.](https://escholarship.org/content/qt7047n3k0/qt7047n3k0.pdf?t=q4gcij)
+ [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
diff --git a/VERSION b/VERSION
index 6085e946..963ed7cf 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.2.1
+1.2.10
diff --git a/__init__.py b/__init__.py
index 746ce73b..ccb1c2b0 100644
--- a/__init__.py
+++ b/__init__.py
@@ -7,19 +7,17 @@
#
import os
-# Attempt to add the source code to the PYTHONPATH here before running globals.init_openram().
+
+# Attempt to add the source code to the PYTHONPATH here before running globals.init_openram()
try:
OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME"))
except:
OPENRAM_HOME = os.path.dirname(os.path.abspath(__file__)) + "/compiler"
-
if not os.path.isdir(OPENRAM_HOME):
assert False
-
# Make sure that OPENRAM_HOME is an environment variable just in case
if "OPENRAM_HOME" not in os.environ.keys():
os.environ["OPENRAM_HOME"] = OPENRAM_HOME
-
# Prepend $OPENRAM_HOME to __path__ so that openram will use those modules
__path__.insert(0, OPENRAM_HOME)
@@ -29,7 +27,6 @@ if os.path.exists(OPENRAM_HOME + "/install_conda.sh"):
CONDA_HOME = OPENRAM_HOME + "/miniconda"
elif os.path.exists(OPENRAM_HOME + "/../install_conda.sh"):
CONDA_HOME = os.path.abspath(OPENRAM_HOME + "/../miniconda")
-
# Add CONDA_HOME to environment variables
try:
os.environ["CONDA_HOME"] = CONDA_HOME
@@ -41,6 +38,49 @@ except:
# Import everything in globals.py
from .globals import *
# Import classes in the "openram" namespace
-# sram_config should be imported before sram
from .sram_config import *
from .sram import *
+from .rom_config import *
+from .rom import *
+
+
+# Add a meta path finder for custom modules
+from importlib.abc import MetaPathFinder
+class custom_module_finder(MetaPathFinder):
+ """
+ This class is a 'hook' in Python's import system. If it encounters a module
+ that can be customized, it checks if there is a custom module specified in
+ the configuration file. If there is a custom module, it is imported instead
+ of the default one.
+ """
+ def find_spec(self, fullname, path, target=None):
+ # Get package and module names
+ package_name = fullname.split(".")[0]
+ module_name = fullname.split(".")[-1]
+ # Skip if the package is not openram
+ if package_name != "openram":
+ return None
+ customizable = False
+ # Search for the module name in customizable modules
+ from openram import OPTS
+ for k, v in OPTS.__dict__.items():
+ if module_name == v:
+ customizable = True
+ # Search for the custom module
+ if customizable:
+ import sys
+ # Try to find the module in sys.path
+ for path in sys.path:
+ # Skip this path if not directory
+ if not os.path.isdir(path):
+ continue
+ for file in os.listdir(path):
+ # If there is a script matching the custom module name,
+ # import it with the default module name
+ if file == (module_name + ".py"):
+ from importlib.util import spec_from_file_location
+ return spec_from_file_location(module_name, "{0}/{1}.py".format(path, module_name))
+ return None
+# Python calls meta path finders and asks them to handle the module import if
+# they can
+sys.meta_path.insert(0, custom_module_finder())
diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py
index 9a11b68a..d33d552d 100644
--- a/compiler/base/hierarchy_layout.py
+++ b/compiler/base/hierarchy_layout.py
@@ -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
diff --git a/compiler/globals.py b/compiler/globals.py
index 626b5d22..60442e99 100644
--- a/compiler/globals.py
+++ b/compiler/globals.py
@@ -28,7 +28,6 @@ NAME = "OpenRAM v{}".format(VERSION)
USAGE = "sram_compiler.py [options] \nUse -h for help.\n"
OPTS = options.options()
-CHECKPOINT_OPTS = None
def parse_args():
@@ -209,17 +208,6 @@ def init_openram(config_file, is_unit_test=False):
factory.reset()
global OPTS
- global CHECKPOINT_OPTS
-
- # This is a hack. If we are running a unit test and have checkpointed
- # the options, load them rather than reading the config file.
- # This way, the configuration is reloaded at the start of every unit test.
- # If a unit test fails,
- # we don't have to worry about restoring the old config values
- # that may have been tested.
- if is_unit_test and CHECKPOINT_OPTS:
- OPTS.__dict__ = CHECKPOINT_OPTS.__dict__.copy()
- return
# Setup correct bitcell names
setup_bitcell()
@@ -227,10 +215,6 @@ def init_openram(config_file, is_unit_test=False):
# Import these to find the executables for checkpointing
from openram import characterizer
from openram import verify
- # Make a checkpoint of the options so we can restore
- # after each unit test
- if not CHECKPOINT_OPTS:
- CHECKPOINT_OPTS = copy.copy(OPTS)
def install_conda():
@@ -581,8 +565,9 @@ def import_tech():
debug.info(1, "Tech directory found in {}".format(OPENRAM_TECH))
- # Add this environment variable to os.environ
+ # Add this environment variable to os.environ and openram namespace
os.environ["OPENRAM_TECH"] = OPENRAM_TECH
+ openram.OPENRAM_TECH = OPENRAM_TECH
# Add all of the paths
for tech_path in OPENRAM_TECH.split(":"):
diff --git a/compiler/modules/__init__.py b/compiler/modules/__init__.py
index ae748da9..2f7d257a 100755
--- a/compiler/modules/__init__.py
+++ b/compiler/modules/__init__.py
@@ -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 *
diff --git a/compiler/modules/pbuf_dec.py b/compiler/modules/pbuf_dec.py
index e42c159e..5e2877f3 100644
--- a/compiler/modules/pbuf_dec.py
+++ b/compiler/modules/pbuf_dec.py
@@ -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",
diff --git a/compiler/modules/pinv_dec.py b/compiler/modules/pinv_dec.py
index eda634f5..1ee75266 100644
--- a/compiler/modules/pinv_dec.py
+++ b/compiler/modules/pinv_dec.py
@@ -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,
diff --git a/compiler/modules/pinvbuf.py b/compiler/modules/pinvbuf.py
index 2ac793c6..f05a2e5b 100644
--- a/compiler/modules/pinvbuf.py
+++ b/compiler/modules/pinvbuf.py
@@ -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):
diff --git a/compiler/modules/ptx.py b/compiler/modules/ptx.py
index bc36f064..250cd3f6 100644
--- a/compiler/modules/ptx.py
+++ b/compiler/modules/ptx.py
@@ -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
diff --git a/compiler/modules/rom_address_control_array.py b/compiler/modules/rom_address_control_array.py
new file mode 100644
index 00000000..9f59ca71
--- /dev/null
+++ b/compiler/modules/rom_address_control_array.py
@@ -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")
\ No newline at end of file
diff --git a/compiler/modules/rom_address_control_buf.py b/compiler/modules/rom_address_control_buf.py
new file mode 100644
index 00000000..9e092ff0
--- /dev/null
+++ b/compiler/modules/rom_address_control_buf.py
@@ -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)
\ No newline at end of file
diff --git a/compiler/modules/rom_bank.py b/compiler/modules/rom_bank.py
new file mode 100644
index 00000000..f4940338
--- /dev/null
+++ b/compiler/modules/rom_bank.py
@@ -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)
\ No newline at end of file
diff --git a/compiler/modules/rom_base_array.py b/compiler/modules/rom_base_array.py
new file mode 100644
index 00000000..9c380624
--- /dev/null
+++ b/compiler/modules/rom_base_array.py
@@ -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)
diff --git a/compiler/modules/rom_base_cell.py b/compiler/modules/rom_base_cell.py
new file mode 100644
index 00000000..28fc07f0
--- /dev/null
+++ b/compiler/modules/rom_base_cell.py
@@ -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())
\ No newline at end of file
diff --git a/compiler/modules/rom_column_mux.py b/compiler/modules/rom_column_mux.py
new file mode 100644
index 00000000..e3d8a1d3
--- /dev/null
+++ b/compiler/modules/rom_column_mux.py
@@ -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)
\ No newline at end of file
diff --git a/compiler/modules/rom_column_mux_array.py b/compiler/modules/rom_column_mux_array.py
new file mode 100644
index 00000000..e314596e
--- /dev/null
+++ b/compiler/modules/rom_column_mux_array.py
@@ -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])
\ No newline at end of file
diff --git a/compiler/modules/rom_control_logic.py b/compiler/modules/rom_control_logic.py
new file mode 100644
index 00000000..494c328d
--- /dev/null
+++ b/compiler/modules/rom_control_logic.py
@@ -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())
\ No newline at end of file
diff --git a/compiler/modules/rom_decoder.py b/compiler/modules/rom_decoder.py
new file mode 100644
index 00000000..760af8c4
--- /dev/null
+++ b/compiler/modules/rom_decoder.py
@@ -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")
\ No newline at end of file
diff --git a/compiler/modules/rom_poly_tap.py b/compiler/modules/rom_poly_tap.py
new file mode 100644
index 00000000..c16323b6
--- /dev/null
+++ b/compiler/modules/rom_poly_tap.py
@@ -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)
\ No newline at end of file
diff --git a/compiler/modules/rom_precharge_array.py b/compiler/modules/rom_precharge_array.py
new file mode 100644
index 00000000..a450c968
--- /dev/null
+++ b/compiler/modules/rom_precharge_array.py
@@ -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)
\ No newline at end of file
diff --git a/compiler/modules/rom_precharge_cell.py b/compiler/modules/rom_precharge_cell.py
new file mode 100644
index 00000000..3d4730aa
--- /dev/null
+++ b/compiler/modules/rom_precharge_cell.py
@@ -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
\ No newline at end of file
diff --git a/compiler/modules/rom_wordline_driver_array.py b/compiler/modules/rom_wordline_driver_array.py
new file mode 100644
index 00000000..44deac1c
--- /dev/null
+++ b/compiler/modules/rom_wordline_driver_array.py
@@ -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))
diff --git a/compiler/options.py b/compiler/options.py
index 14dc7b57..92427ac0 100644
--- a/compiler/options.py
+++ b/compiler/options.py
@@ -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
###################
diff --git a/compiler/rom.py b/compiler/rom.py
new file mode 100644
index 00000000..b4f41889
--- /dev/null
+++ b/compiler/rom.py
@@ -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)
diff --git a/compiler/rom_config.py b/compiler/rom_config.py
new file mode 100644
index 00000000..9e67e37a
--- /dev/null
+++ b/compiler/rom_config.py
@@ -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
+
diff --git a/compiler/tests/04_column_mux_pbitcell_test.py b/compiler/tests/04_column_mux_pbitcell_test.py
index e8d5038b..177f1eba 100755
--- a/compiler/tests/04_column_mux_pbitcell_test.py
+++ b/compiler/tests/04_column_mux_pbitcell_test.py
@@ -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)
diff --git a/compiler/tests/04_dummy_pbitcell_test.py b/compiler/tests/04_dummy_pbitcell_1rw1r1w_test.py
similarity index 81%
rename from compiler/tests/04_dummy_pbitcell_test.py
rename to compiler/tests/04_dummy_pbitcell_1rw1r1w_test.py
index 9043dbc6..a5d038ca 100755
--- a/compiler/tests/04_dummy_pbitcell_test.py
+++ b/compiler/tests/04_dummy_pbitcell_1rw1r1w_test.py
@@ -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)
diff --git a/compiler/tests/04_dummy_pbitcell_1rw_test.py b/compiler/tests/04_dummy_pbitcell_1rw_test.py
new file mode 100755
index 00000000..000856a5
--- /dev/null
+++ b/compiler/tests/04_dummy_pbitcell_1rw_test.py
@@ -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())
diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py
index d1ab4168..c0338015 100755
--- a/compiler/tests/04_pbitcell_test.py
+++ b/compiler/tests/04_pbitcell_test.py
@@ -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)
diff --git a/compiler/tests/04_precharge_1rw_1r_test.py b/compiler/tests/04_precharge_1rw_1r_test.py
index 59f01a73..9b422ca5 100755
--- a/compiler/tests/04_precharge_1rw_1r_test.py
+++ b/compiler/tests/04_precharge_1rw_1r_test.py
@@ -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)
diff --git a/compiler/tests/04_precharge_pbitcell_test.py b/compiler/tests/04_precharge_pbitcell_test.py
index 4a228b6e..71024200 100755
--- a/compiler/tests/04_precharge_pbitcell_test.py
+++ b/compiler/tests/04_precharge_pbitcell_test.py
@@ -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)
diff --git a/compiler/tests/04_replica_pbitcell_test.py b/compiler/tests/04_replica_pbitcell_1rw1r1w_test.py
similarity index 81%
rename from compiler/tests/04_replica_pbitcell_test.py
rename to compiler/tests/04_replica_pbitcell_1rw1r1w_test.py
index 00252a84..5af89b43 100755
--- a/compiler/tests/04_replica_pbitcell_test.py
+++ b/compiler/tests/04_replica_pbitcell_1rw1r1w_test.py
@@ -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)
diff --git a/compiler/tests/04_replica_pbitcell_1rw_test.py b/compiler/tests/04_replica_pbitcell_1rw_test.py
new file mode 100755
index 00000000..ef6a1cd9
--- /dev/null
+++ b/compiler/tests/04_replica_pbitcell_1rw_test.py
@@ -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())
diff --git a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py
index 3b731b31..e77922f6 100755
--- a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py
+++ b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py
@@ -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)
diff --git a/compiler/tests/06_rom_decoder_test.py b/compiler/tests/06_rom_decoder_test.py
new file mode 100755
index 00000000..d6e5d27a
--- /dev/null
+++ b/compiler/tests/06_rom_decoder_test.py
@@ -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())
diff --git a/compiler/tests/07_column_mux_array_pbitcell_test.py b/compiler/tests/07_column_mux_array_pbitcell_test.py
index 020696fa..9f082218 100755
--- a/compiler/tests/07_column_mux_array_pbitcell_test.py
+++ b/compiler/tests/07_column_mux_array_pbitcell_test.py
@@ -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)
diff --git a/compiler/tests/07_rom_column_mux_array_test.py b/compiler/tests/07_rom_column_mux_array_test.py
new file mode 100755
index 00000000..c937e834
--- /dev/null
+++ b/compiler/tests/07_rom_column_mux_array_test.py
@@ -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())
diff --git a/compiler/tests/08_precharge_array_1rw_1r_test.py b/compiler/tests/08_precharge_array_1rw_1r_test.py
index e9b2bce4..739d8006 100755
--- a/compiler/tests/08_precharge_array_1rw_1r_test.py
+++ b/compiler/tests/08_precharge_array_1rw_1r_test.py
@@ -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)
diff --git a/compiler/tests/08_rom_decoder_buffer_array_test.py b/compiler/tests/08_rom_decoder_buffer_array_test.py
new file mode 100755
index 00000000..7231863d
--- /dev/null
+++ b/compiler/tests/08_rom_decoder_buffer_array_test.py
@@ -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())
diff --git a/compiler/tests/08_rom_precharge_array_test.py b/compiler/tests/08_rom_precharge_array_test.py
new file mode 100755
index 00000000..7d585e66
--- /dev/null
+++ b/compiler/tests/08_rom_precharge_array_test.py
@@ -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())
diff --git a/compiler/tests/08_wordline_driver_array_pbitcell_test.py b/compiler/tests/08_wordline_driver_array_pbitcell_test.py
index 5162bf1f..9eb3bdfd 100755
--- a/compiler/tests/08_wordline_driver_array_pbitcell_test.py
+++ b/compiler/tests/08_wordline_driver_array_pbitcell_test.py
@@ -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)
diff --git a/compiler/tests/09_sense_amp_array_pbitcell_test.py b/compiler/tests/09_sense_amp_array_pbitcell_test.py
index ae1186b7..2040b4b2 100755
--- a/compiler/tests/09_sense_amp_array_pbitcell_test.py
+++ b/compiler/tests/09_sense_amp_array_pbitcell_test.py
@@ -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)
diff --git a/compiler/tests/09_sense_amp_array_spare_cols_test.py b/compiler/tests/09_sense_amp_array_spare_cols_test.py
index c39fd094..981cc6ae 100755
--- a/compiler/tests/09_sense_amp_array_spare_cols_test.py
+++ b/compiler/tests/09_sense_amp_array_spare_cols_test.py
@@ -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)
diff --git a/compiler/tests/10_rom_wordline_driver_array_test.py b/compiler/tests/10_rom_wordline_driver_array_test.py
new file mode 100755
index 00000000..f3da0e75
--- /dev/null
+++ b/compiler/tests/10_rom_wordline_driver_array_test.py
@@ -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())
diff --git a/compiler/tests/10_write_driver_array_pbitcell_test.py b/compiler/tests/10_write_driver_array_pbitcell_test.py
index 1a72989b..e5914935 100755
--- a/compiler/tests/10_write_driver_array_pbitcell_test.py
+++ b/compiler/tests/10_write_driver_array_pbitcell_test.py
@@ -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)
diff --git a/compiler/tests/10_write_driver_array_spare_cols_test.py b/compiler/tests/10_write_driver_array_spare_cols_test.py
index ef0c3596..a1926a90 100755
--- a/compiler/tests/10_write_driver_array_spare_cols_test.py
+++ b/compiler/tests/10_write_driver_array_spare_cols_test.py
@@ -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)
diff --git a/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py b/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py
index 69054586..7bc6704d 100755
--- a/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py
+++ b/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py
@@ -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)
diff --git a/compiler/tests/10_write_mask_and_array_pbitcell_test.py b/compiler/tests/10_write_mask_and_array_pbitcell_test.py
index 3d79d4dd..e19bfd27 100755
--- a/compiler/tests/10_write_mask_and_array_pbitcell_test.py
+++ b/compiler/tests/10_write_mask_and_array_pbitcell_test.py
@@ -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)
diff --git a/compiler/tests/14_capped_replica_bitcell_array_leftrbl_1rw_test.py b/compiler/tests/14_capped_replica_bitcell_array_leftrbl_1rw_test.py
index b4ba91d1..61464b9b 100755
--- a/compiler/tests/14_capped_replica_bitcell_array_leftrbl_1rw_test.py
+++ b/compiler/tests/14_capped_replica_bitcell_array_leftrbl_1rw_test.py
@@ -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)
diff --git a/compiler/tests/14_capped_replica_bitcell_array_norbl_1rw_test.py b/compiler/tests/14_capped_replica_bitcell_array_norbl_1rw_test.py
index 175689c3..070404ac 100755
--- a/compiler/tests/14_capped_replica_bitcell_array_norbl_1rw_test.py
+++ b/compiler/tests/14_capped_replica_bitcell_array_norbl_1rw_test.py
@@ -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)
diff --git a/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py b/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py
index f77a571d..b8d5e5ed 100755
--- a/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py
+++ b/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py
@@ -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)
diff --git a/compiler/tests/14_replica_bitcell_array_norbl_1rw_test.py b/compiler/tests/14_replica_bitcell_array_norbl_1rw_test.py
index e893c3ca..219bd680 100755
--- a/compiler/tests/14_replica_bitcell_array_norbl_1rw_test.py
+++ b/compiler/tests/14_replica_bitcell_array_norbl_1rw_test.py
@@ -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)
diff --git a/compiler/tests/14_replica_pbitcell_array_test.py b/compiler/tests/14_replica_pbitcell_1rw1r_array_test.py
similarity index 73%
rename from compiler/tests/14_replica_pbitcell_array_test.py
rename to compiler/tests/14_replica_pbitcell_1rw1r_array_test.py
index cb143c40..6ef53ed3 100755
--- a/compiler/tests/14_replica_pbitcell_array_test.py
+++ b/compiler/tests/14_replica_pbitcell_1rw1r_array_test.py
@@ -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()
diff --git a/compiler/tests/14_replica_pbitcell_1rw_array_test.py b/compiler/tests/14_replica_pbitcell_1rw_array_test.py
new file mode 100755
index 00000000..154fc914
--- /dev/null
+++ b/compiler/tests/14_replica_pbitcell_1rw_array_test.py
@@ -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())
diff --git a/compiler/tests/14_rom_array_test.py b/compiler/tests/14_rom_array_test.py
new file mode 100755
index 00000000..c7a03ea1
--- /dev/null
+++ b/compiler/tests/14_rom_array_test.py
@@ -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())
diff --git a/compiler/tests/16_rom_control_logic_test.py b/compiler/tests/16_rom_control_logic_test.py
new file mode 100755
index 00000000..12b1fbdb
--- /dev/null
+++ b/compiler/tests/16_rom_control_logic_test.py
@@ -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())
diff --git a/compiler/tests/18_port_data_16mux_1rw_1r_test.py b/compiler/tests/18_port_data_16mux_1rw_1r_test.py
index 57947481..b4b50323 100755
--- a/compiler/tests/18_port_data_16mux_1rw_1r_test.py
+++ b/compiler/tests/18_port_data_16mux_1rw_1r_test.py
@@ -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)
diff --git a/compiler/tests/18_port_data_16mux_test.py b/compiler/tests/18_port_data_16mux_test.py
index 0b1859e4..66d4c4bf 100755
--- a/compiler/tests/18_port_data_16mux_test.py
+++ b/compiler/tests/18_port_data_16mux_test.py
@@ -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)
diff --git a/compiler/tests/18_port_data_2mux_1rw_1r_test.py b/compiler/tests/18_port_data_2mux_1rw_1r_test.py
index d9ca31ea..fc0d9243 100755
--- a/compiler/tests/18_port_data_2mux_1rw_1r_test.py
+++ b/compiler/tests/18_port_data_2mux_1rw_1r_test.py
@@ -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)
diff --git a/compiler/tests/18_port_data_2mux_test.py b/compiler/tests/18_port_data_2mux_test.py
index 469d3e34..8eb29615 100755
--- a/compiler/tests/18_port_data_2mux_test.py
+++ b/compiler/tests/18_port_data_2mux_test.py
@@ -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)
diff --git a/compiler/tests/18_port_data_4mux_1rw_1r_test.py b/compiler/tests/18_port_data_4mux_1rw_1r_test.py
index edd2336b..ac81e561 100755
--- a/compiler/tests/18_port_data_4mux_1rw_1r_test.py
+++ b/compiler/tests/18_port_data_4mux_1rw_1r_test.py
@@ -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)
diff --git a/compiler/tests/18_port_data_4mux_test.py b/compiler/tests/18_port_data_4mux_test.py
index 12ef31d2..c3eae434 100755
--- a/compiler/tests/18_port_data_4mux_test.py
+++ b/compiler/tests/18_port_data_4mux_test.py
@@ -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)
diff --git a/compiler/tests/18_port_data_8mux_1rw_1r_test.py b/compiler/tests/18_port_data_8mux_1rw_1r_test.py
index 683a086f..e57d12ef 100755
--- a/compiler/tests/18_port_data_8mux_1rw_1r_test.py
+++ b/compiler/tests/18_port_data_8mux_1rw_1r_test.py
@@ -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)
diff --git a/compiler/tests/18_port_data_8mux_test.py b/compiler/tests/18_port_data_8mux_test.py
index ae067163..a8f82489 100755
--- a/compiler/tests/18_port_data_8mux_test.py
+++ b/compiler/tests/18_port_data_8mux_test.py
@@ -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)
diff --git a/compiler/tests/18_port_data_nomux_1rw_1r_test.py b/compiler/tests/18_port_data_nomux_1rw_1r_test.py
index 04cbddd8..8b887d9b 100755
--- a/compiler/tests/18_port_data_nomux_1rw_1r_test.py
+++ b/compiler/tests/18_port_data_nomux_1rw_1r_test.py
@@ -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)
diff --git a/compiler/tests/18_port_data_nomux_test.py b/compiler/tests/18_port_data_nomux_test.py
index 07836e3d..e479de1f 100755
--- a/compiler/tests/18_port_data_nomux_test.py
+++ b/compiler/tests/18_port_data_nomux_test.py
@@ -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)
diff --git a/compiler/tests/18_port_data_spare_cols_test.py b/compiler/tests/18_port_data_spare_cols_test.py
index c511dc5c..0db34da0 100755
--- a/compiler/tests/18_port_data_spare_cols_test.py
+++ b/compiler/tests/18_port_data_spare_cols_test.py
@@ -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)
diff --git a/compiler/tests/18_port_data_wmask_1rw_1r_test.py b/compiler/tests/18_port_data_wmask_1rw_1r_test.py
index ee3278d2..b731a1d0 100755
--- a/compiler/tests/18_port_data_wmask_1rw_1r_test.py
+++ b/compiler/tests/18_port_data_wmask_1rw_1r_test.py
@@ -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)
diff --git a/compiler/tests/18_port_data_wmask_test.py b/compiler/tests/18_port_data_wmask_test.py
index e1860e6e..3db910c1 100755
--- a/compiler/tests/18_port_data_wmask_test.py
+++ b/compiler/tests/18_port_data_wmask_test.py
@@ -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)
diff --git a/compiler/tests/19_multi_bank_test.py b/compiler/tests/19_multi_bank_test.py
index a1fb9b57..c1f408e0 100755
--- a/compiler/tests/19_multi_bank_test.py
+++ b/compiler/tests/19_multi_bank_test.py
@@ -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)
diff --git a/compiler/tests/19_pmulti_bank_test.py b/compiler/tests/19_pmulti_bank_test.py
index 4e39a32c..378ff837 100755
--- a/compiler/tests/19_pmulti_bank_test.py
+++ b/compiler/tests/19_pmulti_bank_test.py
@@ -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)
diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py
index 205b6bff..cf38b68b 100755
--- a/compiler/tests/19_psingle_bank_test.py
+++ b/compiler/tests/19_psingle_bank_test.py
@@ -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)
diff --git a/compiler/tests/19_rom_bank_test.py b/compiler/tests/19_rom_bank_test.py
new file mode 100755
index 00000000..4a3233e8
--- /dev/null
+++ b/compiler/tests/19_rom_bank_test.py
@@ -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())
diff --git a/compiler/tests/19_single_bank_16mux_1rw_1r_test.py b/compiler/tests/19_single_bank_16mux_1rw_1r_test.py
index 9ce9ee22..4d31ea38 100755
--- a/compiler/tests/19_single_bank_16mux_1rw_1r_test.py
+++ b/compiler/tests/19_single_bank_16mux_1rw_1r_test.py
@@ -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)
diff --git a/compiler/tests/19_single_bank_16mux_test.py b/compiler/tests/19_single_bank_16mux_test.py
index 889d6e95..2bc53ace 100755
--- a/compiler/tests/19_single_bank_16mux_test.py
+++ b/compiler/tests/19_single_bank_16mux_test.py
@@ -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)
diff --git a/compiler/tests/19_single_bank_1w_1r_test.py b/compiler/tests/19_single_bank_1w_1r_test.py
index ed3594e6..428f6dc6 100755
--- a/compiler/tests/19_single_bank_1w_1r_test.py
+++ b/compiler/tests/19_single_bank_1w_1r_test.py
@@ -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)
diff --git a/compiler/tests/19_single_bank_2mux_1rw_1r_test.py b/compiler/tests/19_single_bank_2mux_1rw_1r_test.py
index 14400ece..e81d1bbc 100755
--- a/compiler/tests/19_single_bank_2mux_1rw_1r_test.py
+++ b/compiler/tests/19_single_bank_2mux_1rw_1r_test.py
@@ -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)
diff --git a/compiler/tests/19_single_bank_2mux_test.py b/compiler/tests/19_single_bank_2mux_test.py
index 897ad100..a1679f5c 100755
--- a/compiler/tests/19_single_bank_2mux_test.py
+++ b/compiler/tests/19_single_bank_2mux_test.py
@@ -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)
diff --git a/compiler/tests/19_single_bank_4mux_1rw_1r_test.py b/compiler/tests/19_single_bank_4mux_1rw_1r_test.py
index 588055ec..0c1f3439 100755
--- a/compiler/tests/19_single_bank_4mux_1rw_1r_test.py
+++ b/compiler/tests/19_single_bank_4mux_1rw_1r_test.py
@@ -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)
diff --git a/compiler/tests/19_single_bank_4mux_test.py b/compiler/tests/19_single_bank_4mux_test.py
index def5a5bc..94219f97 100755
--- a/compiler/tests/19_single_bank_4mux_test.py
+++ b/compiler/tests/19_single_bank_4mux_test.py
@@ -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)
diff --git a/compiler/tests/19_single_bank_8mux_1rw_1r_test.py b/compiler/tests/19_single_bank_8mux_1rw_1r_test.py
index 7e1c9bb4..2a12397c 100755
--- a/compiler/tests/19_single_bank_8mux_1rw_1r_test.py
+++ b/compiler/tests/19_single_bank_8mux_1rw_1r_test.py
@@ -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)
diff --git a/compiler/tests/19_single_bank_8mux_test.py b/compiler/tests/19_single_bank_8mux_test.py
index 626746b2..bee204a5 100755
--- a/compiler/tests/19_single_bank_8mux_test.py
+++ b/compiler/tests/19_single_bank_8mux_test.py
@@ -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)
diff --git a/compiler/tests/19_single_bank_global_bitline_test.py b/compiler/tests/19_single_bank_global_bitline_test.py
index 5501e485..3649dea4 100755
--- a/compiler/tests/19_single_bank_global_bitline_test.py
+++ b/compiler/tests/19_single_bank_global_bitline_test.py
@@ -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)
diff --git a/compiler/tests/19_single_bank_nomux_1rw_1r_test.py b/compiler/tests/19_single_bank_nomux_1rw_1r_test.py
index 3926df30..14c998ae 100755
--- a/compiler/tests/19_single_bank_nomux_1rw_1r_test.py
+++ b/compiler/tests/19_single_bank_nomux_1rw_1r_test.py
@@ -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)
diff --git a/compiler/tests/19_single_bank_nomux_test.py b/compiler/tests/19_single_bank_nomux_test.py
index 3c49f975..f7c9b72d 100755
--- a/compiler/tests/19_single_bank_nomux_test.py
+++ b/compiler/tests/19_single_bank_nomux_test.py
@@ -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)
diff --git a/compiler/tests/19_single_bank_spare_cols_test.py b/compiler/tests/19_single_bank_spare_cols_test.py
index 68b71b51..1e3280de 100755
--- a/compiler/tests/19_single_bank_spare_cols_test.py
+++ b/compiler/tests/19_single_bank_spare_cols_test.py
@@ -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)
diff --git a/compiler/tests/19_single_bank_wmask_1rw_1r_test.py b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py
index 8d810d91..20410bc8 100755
--- a/compiler/tests/19_single_bank_wmask_1rw_1r_test.py
+++ b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py
@@ -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)
diff --git a/compiler/tests/19_single_bank_wmask_test.py b/compiler/tests/19_single_bank_wmask_test.py
index 19b01e61..16a14c6f 100755
--- a/compiler/tests/19_single_bank_wmask_test.py
+++ b/compiler/tests/19_single_bank_wmask_test.py
@@ -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)
diff --git a/compiler/tests/20_sram_2bank_test.py b/compiler/tests/20_sram_2bank_test.py
index c2d2c412..52031166 100755
--- a/compiler/tests/20_sram_2bank_test.py
+++ b/compiler/tests/20_sram_2bank_test.py
@@ -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)
diff --git a/compiler/tests/Makefile b/compiler/tests/Makefile
index 7351a424..3ea5d299 100644
--- a/compiler/tests/Makefile
+++ b/compiler/tests/Makefile
@@ -4,7 +4,6 @@ include $(TOP_DIR)/openram.mk
.DEFAULT_GOAL := all
ARGS ?=
-TEST_TECHS ?= scn4m_subm freepdk45
TECHS ?= scn4m_subm freepdk45 sky130
TEST_DIR = $(TOP_DIR)/compiler/tests
@@ -15,28 +14,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 +32,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 +46,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,10 +62,109 @@ 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 \
+ sky130/23_lib_sram_test.ok \
+ sky130/03_wire_test.ok \
+ sky130/04_dummy_pbitcell_1rw1r1w_test.ok \
+ sky130/04_dummy_pbitcell_1rw_test.ok \
+ sky130/04_replica_pbitcell_1rw1r1w_test.ok \
+ sky130/04_replica_pbitcell_1rw_test.ok \
+ sky130/06_hierarchical_decoder_132row_test.ok \
+ sky130/06_hierarchical_decoder_512row_test.ok \
+ sky130/06_hierarchical_decoder_64row_test.ok \
+ sky130/06_hierarchical_decoder_pbitcell_test.ok \
+ sky130/10_write_driver_array_spare_cols_test.ok \
+ sky130/10_write_driver_array_wmask_spare_cols_test.ok \
+ sky130/14_capped_replica_bitcell_array_leftrbl_1rw_test.ok \
+ sky130/14_capped_replica_bitcell_array_norbl_1rw_test.ok \
+ sky130/14_replica_bitcell_array_leftrbl_1rw_test.ok \
+ sky130/14_replica_bitcell_array_norbl_1rw_test.ok \
+ sky130/14_replica_column_1rw_1r_test.ok \
+ sky130/14_replica_column_1rw_test.ok \
+ sky130/14_replica_pbitcell_1rw1r_array_test.ok \
+ sky130/14_replica_pbitcell_1rw_array_test.ok \
+ sky130/15_global_bitcell_array_1rw_1r_test.ok \
+ sky130/15_global_bitcell_array_test.ok \
+ sky130/15_local_bitcell_array_test.ok \
+ sky130/18_port_address_512rows_test.ok \
+ sky130/18_port_data_spare_cols_test.ok \
+ sky130/19_single_bank_2mux_test.ok \
+ sky130/19_single_bank_4mux_test.ok \
+ sky130/19_single_bank_8mux_test.ok \
+ sky130/19_single_bank_global_bitline_test.ok \
+ sky130/19_single_bank_nomux_test.ok \
+ sky130/19_single_bank_spare_cols_test.ok \
+ sky130/19_single_bank_wmask_test.ok \
+ sky130/20_sram_1bank_2mux_1rw_1r_spare_cols_test.ok \
+ sky130/20_sram_1bank_2mux_1w_1r_spare_cols_test.ok \
+ sky130/20_sram_1bank_2mux_global_test.ok \
+ sky130/20_sram_1bank_2mux_test.ok \
+ sky130/20_sram_1bank_2mux_wmask_spare_cols_test.ok \
+ sky130/20_sram_1bank_2mux_wmask_test.ok \
+ sky130/20_sram_1bank_4mux_test.ok \
+ sky130/20_sram_1bank_8mux_test.ok \
+ sky130/20_sram_1bank_nomux_spare_cols_test.ok \
+ sky130/20_sram_1bank_nomux_test.ok \
+ sky130/20_sram_1bank_nomux_wmask_test.ok \
+ sky130/20_sram_1bank_ring_test.ok \
+ sky130/21_model_delay_test.ok \
+ sky130/21_ngspice_delay_extra_rows_test.ok \
+ sky130/21_ngspice_delay_test.ok \
+ sky130/21_regression_delay_test.ok \
+ sky130/21_xyce_delay_test.ok \
+ sky130/25_verilog_multibank_test.ok \
+ sky130/25_verilog_sram_test.ok \
+ sky130/30_openram_back_end_library_test.ok \
+ sky130/30_openram_back_end_test.ok \
+ sky130/30_openram_front_end_library_test.ok \
+ sky130/30_openram_front_end_test.ok \
gettech = $(word 1,$(subst /, ,$*))
getfile = $(word 2,$(subst /, ,$*))
-TECH_TEST_STAMPS=$(foreach T, $(TEST_TECHS), $(addprefix $T/, $(TEST_STAMPS)))
+TECH_TEST_STAMPS=$(foreach T, $(TECHS), $(addprefix $T/, $(TEST_STAMPS)))
# Filter out the tests after creating the tech stamps
WORKING_TECH_TEST_STAMPS=$(shell shuf -e -- $(filter-out $(BROKEN_STAMPS), $(TECH_TEST_STAMPS)))
@@ -103,7 +177,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 +188,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 +201,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
diff --git a/compiler/tests/configs/config.py b/compiler/tests/configs/config.py
index e95a1f7c..bb0c8894 100644
--- a/compiler/tests/configs/config.py
+++ b/compiler/tests/configs/config.py
@@ -17,3 +17,4 @@ check_lvsdrc = True
#route_supplies = False
output_name = "sram"
+rom_data_dir = "tests/configs"
diff --git a/compiler/tests/configs/rom_data_64B b/compiler/tests/configs/rom_data_64B
new file mode 100644
index 00000000..f92d10d5
--- /dev/null
+++ b/compiler/tests/configs/rom_data_64B
@@ -0,0 +1 @@
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFF
\ No newline at end of file
diff --git a/compiler/tests/regress.py b/compiler/tests/regress.py
deleted file mode 100755
index 4df495df..00000000
--- a/compiler/tests/regress.py
+++ /dev/null
@@ -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())
diff --git a/compiler/tests/skip_tests_freepdk45.txt b/compiler/tests/skip_tests_freepdk45.txt
deleted file mode 100644
index 145cf857..00000000
--- a/compiler/tests/skip_tests_freepdk45.txt
+++ /dev/null
@@ -1 +0,0 @@
-21_xyce_delay_test.py
diff --git a/compiler/tests/skip_tests_scn4m_subm.txt b/compiler/tests/skip_tests_scn4m_subm.txt
deleted file mode 100644
index c3fb3138..00000000
--- a/compiler/tests/skip_tests_scn4m_subm.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-19_single_bank_global_bitline_test.py
-21_xyce_delay_test.py
diff --git a/compiler/tests/skip_tests_sky130.txt b/compiler/tests/skip_tests_sky130.txt
deleted file mode 100644
index b0cba8aa..00000000
--- a/compiler/tests/skip_tests_sky130.txt
+++ /dev/null
@@ -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
diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py
index fcb9a776..a41620c3 100644
--- a/compiler/tests/testutils.py
+++ b/compiler/tests/testutils.py
@@ -10,21 +10,6 @@ import sys, os, glob
import pdb
import traceback
import time
-# FIXME: This is a hack for unit tests running on docker.
-try:
- import openram
-except:
- # If openram library isn't found as a python package,
- # import it from the $OPENRAM_HOME path.
- import importlib.util
- OPENRAM_HOME = os.getenv("OPENRAM_HOME")
- # Import using spec since the directory can be named something
- # other than "openram".
- spec = importlib.util.spec_from_file_location("openram", "{}/../__init__.py".format(OPENRAM_HOME))
- module = importlib.util.module_from_spec(spec)
- sys.modules["openram"] = module
- spec.loader.exec_module(module)
- import openram
from openram import debug
from openram import OPTS
diff --git a/docs/source/basic_setup.md b/docs/source/basic_setup.md
index 83f2c544..91905861 100644
--- a/docs/source/basic_setup.md
+++ b/docs/source/basic_setup.md
@@ -7,7 +7,6 @@ This page shows the basic setup for using OpenRAM.
## Table of Contents
1. [Dependencies](#dependencies)
-1. [OpenRAM Library](#openram-library)
1. [Anaconda](#anaconda)
1. [Docker](#docker-deprecated-use-anaconda-instead)
1. [Environment](#environment)
@@ -16,116 +15,127 @@ This page shows the basic setup for using OpenRAM.
## Dependencies
-Please see the Dockerfile for the required versions of tools.
-
In general, the OpenRAM compiler has very few dependencies:
+ Git
+ Make
-+ Python 3.6 or higher
++ Python 3.5 or higher
+ Various Python packages (pip install -r requirements.txt)
+ Anaconda
-## OpenRAM Library
-OpenRAM is available as a Python library. There are a few ways to install it:
-
-+ Install using Makefile (you need to clone the repo):
-```
-git clone git@github.com:VLSIDA/OpenRAM.git
-cd OpenRAM
-make library
-```
-
-+ Install the latest _dev_ version:
-```
-pip3 install git+https://git@github.com/VLSIDA/OpenRAM.git@dev
-```
-
-
-
## Anaconda
-We use Anaconda package manager to install the tools used by OpenRAM. This way, you don't have to
-worry about updating/installing these tools. OpenRAM installs Anaconda silently in the background
-(without affecting any existing anaconda setup you have).
+We use Anaconda package manager to install the tools used by OpenRAM. This way,
+you don't have to worry about updating/installing these tools. OpenRAM installs
+Anaconda silently in the background (without affecting any existing Anaconda
+setup you have).
-OpenRAM uses Anaconda by default, but you can turn this feature off by setting `use_conda = False`
-in your config file. Then, OpenRAM will use the tools you have installed on your system.
+You don't have to manually activate/deactivate the Anaconda environment. OpenRAM
+automatically manages this before and after running the tools.
-If you want to install Anaconda without running OpenRAM (for example to run unit tests, which do not install Anaconda), you can run:
-```
-cd OpenRAM
-./install_conda.sh
-```
+OpenRAM uses Anaconda by default, but you can turn this feature off by setting
+`use_conda = False` in your config file. Then, OpenRAM will use the tools you
+have installed on your system.
+
+> **Note**: If you want to install Anaconda without running OpenRAM (for example
+> to run unit tests, which do not install Anaconda), you can run:
+> ```
+> ./install_conda.sh
+> ```
+
+> **Note**: You can uninstall OpenRAM's Anaconda installation by simply deleting
+> the folder Anaconda is installed to. You can run:
+> ```
+> rm -rf miniconda
+> ```
+
+> **Note**: You can change a tool's version with the following commands:
+> ```
+> source ./miniconda/bin/activate
+> conda uninstall
+> conda install -y -c vlsida-eda =
+> ```
## Docker (deprecated, use Anaconda instead)
-We have a [docker setup](../../docker) to run OpenRAM. To use this, you should run:
+We have a [docker setup](../../docker) to run OpenRAM. To use this, you should
+run:
```
cd OpenRAM/docker
make build
```
-This must be run once and will take a while to build all the tools. If you have the
-OpenRAM library installed, you can also run the docker setup from the package
-installation directory.
+This must be run once and will take a while to build all the tools. If you have
+the OpenRAM library installed, you can also run the docker setup from the
+package installation directory.
## Environment
-If you haven't installed the OpenRAM library or you want to use a different OpenRAM installation,
-you can set two environment variables:
-+ OPENRAM\_HOME should point to the compiler source directory.
-+ OPENRAM\_TECH should point to one or more root technology directories (colon separated).
+If you haven't installed the OpenRAM library or you want to use a different
+OpenRAM installation, you can set two environment variables:
++ `OPENRAM_HOME` should point to the compiler source directory.
++ `OPENRAM_TECH` should point to one or more root technology directories (colon
+ separated).
-If you have the library installed and OPENRAM\_HOME set, the library will use the installation on
-the OPENRAM\_HOME path.
+If you have the library installed and `OPENRAM_HOME` set, the library will use
+the installation on the `OPENRAM_HOME` path.
-If you don't have the library, you should also add OPENRAM\_HOME to your PYTHONPATH. This is not
-needed if you have the library.
+If you don't have the library, you should also add `OPENRAM_HOME` to your
+`PYTHONPATH`. This is not needed if you have the library.
You can add these environment variables to your `.bashrc`:
```
- export OPENRAM_HOME="$HOME/OpenRAM/compiler"
- export OPENRAM_TECH="$HOME/OpenRAM/technology"
- export PYTHONPATH=$OPENRAM_HOME
+export OPENRAM_HOME="$HOME/OpenRAM/compiler"
+export OPENRAM_TECH="$HOME/OpenRAM/technology"
+export PYTHONPATH=$OPENRAM_HOME
```
-Note that if you want symbols to resolve in your editor, you may also want to add the specific technology
-directory that you use and any custom technology modules as well. For example:
+Note that if you want symbols to resolve in your editor, you may also want to
+add the specific technology directory that you use and any custom technology
+modules as well. For example:
```
- export PYTHONPATH="$OPENRAM_HOME:$OPENRAM_TECH/sky130:$OPENRAM_TECH/sky130/custom"
+export PYTHONPATH="$OPENRAM_HOME:$OPENRAM_TECH/sky130:$OPENRAM_TECH/sky130/custom"
```
-We include the tech files necessary for [SCMOS] SCN4M\_SUBM,
-[FreePDK45]. The [SCMOS] spice models, however, are
-generic and should be replaced with foundry models. You may get the
-entire [FreePDK45 PDK here][FreePDK45].
+We include the tech files necessary for [SCMOS] SCN4M\_SUBM, [FreePDK45]. The
+[SCMOS] spice models, however, are generic and should be replaced with foundry
+models. You may get the entire [FreePDK45 PDK here][FreePDK45].
## Sky130 Setup
-To install [Sky130], you must have open\_pdks installed in $PDK\_ROOT. We highly recommend that you
-use the version tagged in the Makefile as others have not been verified.
-To install this automatically, you can run:
+To install [Sky130], you must have open\_pdks installed in $PDK\_ROOT. We highly
+recommend that you use the version tagged in the Makefile as others have not
+been verified. To install this automatically, you can run:
```
cd $HOME/OpenRAM
make pdk
```
+> **Note**: If you don't have Magic installed, you need to install and activate
+> the conda environment before running this command. You can run:
+>
+> ```
+> ./install_conda.sh
+> source miniconda/bin/activate
+> ```
-Then you must also install the [Sky130] SRAM build space with the appropriate cell views into the OpenRAM technology directory
-by running:
+
+Then you must also install the [Sky130] SRAM build space with the appropriate
+cell views into the OpenRAM technology directory by running:
```
cd $HOME/OpenRAM
make install
```
-You can also run these from the package installation directory if you have the OpenRAM library.
+You can also run these from the package installation directory if you have the
+OpenRAM library.
[SCMOS]: https://www.mosis.com/files/scmos/scmos.pdf
[FreePDK45]: https://www.eda.ncsu.edu/wiki/FreePDK45:Contents
[Sky130]: https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git
+
diff --git a/docs/source/basic_usage.md b/docs/source/basic_usage.md
index b9f5f468..7550137f 100644
--- a/docs/source/basic_usage.md
+++ b/docs/source/basic_usage.md
@@ -7,9 +7,8 @@ This page of the documentation explains the basic usage of OpenRAM.
## Table of Contents
1. [Environment Variable Setup](#environment-variable-setup-assuming-bash)
-1. [Script Usage (with library)](#script-usage-with-library)
-1. [Command Line Usage (with library)](#command-line-usage-with-library)
-1. [Command Line Usage (without library)](#command-line-usage-without-library)
+1. [Command Line Usage](#command-line-usage)
+1. [Script Usage](#script-usage)
1. [Configuration Files](#configuration-files)
1. [Common Configuration File Options](#common-configuration-file-options)
1. [Output Files](#output-files)
@@ -18,10 +17,11 @@ This page of the documentation explains the basic usage of OpenRAM.
## Environment Variable Setup (assuming bash)
-> **Note**: This is optional if you have the OpenRAM library. See [basic setup](./basic_setup.md#go-back) for details.
-* OPENRAM\_HOME defines where the compiler directory is
- * ```export OPENRAM_HOME="$HOME/openram/compiler"```
-* OPENRAM_TECH defines list of paths where the technologies exist
+> **Note**: This is optional if you have the OpenRAM library. See
+> [Python library](./python_library.md#go-back) for details.
+* `OPENRAM_HOME` defines where the compiler directory is
+ * `export OPENRAM_HOME="$HOME/openram/compiler"`
+* `OPENRAM_TECH` defines list of paths where the technologies exist
* `export OPENRAM_TECH="$HOME/openram/technology"`
* Colon separated list so you can have private technology directories
* Must also have any PDK related variables set up
@@ -30,102 +30,25 @@ This page of the documentation explains the basic usage of OpenRAM.
-## Script Usage (with library)
-If you have the library installed, you can use OpenRAM in any Python script. You can import "openram" as follows:
-```python
-import openram
-openram.init_openram("myconfig.py") # Config files are explained on this page
-# Now you can use modules from openram
-from openram import tech
-...
-```
-
-Note that you need to initalize OpenRAM so that the modules are imported properly. You can also look
-at [sram_compiler.py](../../sram_compiler.py) as an example on how to use "openram."
-
-If you want to pass custom configuration when generating an SRAM, you can use the `sram_config` class.
-```python
-import openram
-openram.init_openram("myconfig.py")
-
-from openram import sram_config
-c = sram_config(...)
-
-from openram import sram
-s = sram(sram_config=c,
- name="custom_name")
-
-s.save()
-
-openram.end_openram()
-```
-
-
-## Command Line Usage (with library)
-You can run OpenRAM from the command line using the [sram_compiler.py](../../sram_compiler.py) script that is
-included in the library's installation. You can find the package directory on a path like:
-```
-/home/mrg/.local/lib/python3.8/site-packages/openram
-```
-
-Alternatively, you can run the following command to find that path:
-```
-echo -e "import os\nimport openram\nprint(os.path.dirname(openram.__file__))" | python3 -
-```
-
-You can continue with following section for more details.
-
-
-## Command Line Usage (without library)
+## Command Line Usage
Once you have defined the environment, you can run OpenRAM from the command line
-using a single configuration file written in Python.
-
-For example, create a file called *myconfig.py* specifying the following
-parameters for your memory:
-```python
-# Data word size
-word_size = 2
-# Number of words in the memory
-num_words = 16
-
-# Technology to use in $OPENRAM_TECH
-tech_name = "scn4m_subm"
-
-# You can use the technology nominal corner only
-nominal_corner_only = True
-# Or you can specify particular corners
-# Process corners to characterize
-# process_corners = ["SS", "TT", "FF"]
-# Voltage corners to characterize
-# supply_voltages = [ 3.0, 3.3, 3.5 ]
-# Temperature corners to characterize
-# temperatures = [ 0, 25 100]
-
-# Output directory for the results
-output_path = "temp"
-# Output file base name
-output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
-
-# Disable analytical models for full characterization (WARNING: slow!)
-# analytical_delay = False
-
-```
-
-You can then run OpenRAM by executing:
+using a single configuration file written in Python. You can then run OpenRAM by
+executing:
```
python3 $OPENRAM_HOME/../sram_compiler.py myconfig
```
You can see all of the options for the configuration file in
$OPENRAM\_HOME/options.py
-To run designs in Docker, it is suggested to use, for example:
+To run macros, it is suggested to use, for example:
```
cd OpenRAM/macros
make example_config_scn4m_subm
```
* Common arguments:
- * `-t` specify technology (scn4m_subm or scmos or freepdk45)
+ * `-h` print all arguments
+ * `-t` specify technology (scn4m\_subm or scmos or freepdk45)
* `-v` increase verbosity of output
* `-n` don't run DRC/LVS
* `-c` perform simulation-based characterization
@@ -133,6 +56,12 @@ make example_config_scn4m_subm
+## Script Usage
+OpenRAM is also available as a Python library. See
+[Python library](./python_library.md#go-back) for details.
+
+
+
## Configuration Files
* Memories are created using a Python configuration file to replicate results
* No YAML, JSON, etc.
@@ -167,7 +96,7 @@ make example_config_scn4m_subm
# Could be calibre for FreePDK45
drc_name = "magic"
lvs_name = "netgen"
- pex_name = "magic"
+ pex_name = "magic"
```
@@ -202,7 +131,8 @@ make example_config_scn4m_subm
## Output Files
-The output files are placed in the `output_dir` defined in the configuration file.
+The output files are placed in the `output_dir` defined in the configuration
+file.
The base name is specified by `output_name` and suffixes are added.
diff --git a/docs/source/debug.md b/docs/source/debug.md
index e8ae091c..32752704 100644
--- a/docs/source/debug.md
+++ b/docs/source/debug.md
@@ -1,7 +1,8 @@
### [Go Back](./index.md#table-of-contents)
# Debugging and Unit Testing
-This page of the documentation explains the debugging and unit testing of OpenRAM.
+This page of the documentation explains the debugging and unit testing of
+OpenRAM.
@@ -16,11 +17,14 @@ This page of the documentation explains the debugging and unit testing of OpenRA
## Unit Tests
-OpenRAM has the set of thorough regression tests implemented with the Python unit test framework:
-* Unit tests allow users to add features without worrying about breaking functionality.
+OpenRAM has the set of thorough regression tests implemented with the Python
+unit test framework:
+* Unit tests allow users to add features without worrying about breaking
+ functionality.
* Unit tests guide users when porting to new technologies.
* Every sub-module has its own regression test.
-* There are regression tests for memory functionality, library cell verification, timing verification, and technology verification.
+* There are regression tests for memory functionality, library cell
+ verification, timing verification, and technology verification.
@@ -41,16 +45,20 @@ OpenRAM has the set of thorough regression tests implemented with the Python uni
## Running Unit Tests
-
-Regression testing performs a number of tests for all modules in OpenRAM.
-From the unit test directory ($OPENRAM\_HOME/tests),
-use the following command to run all regression tests:
+Regression testing performs a number of tests for all modules in OpenRAM. From
+the unit test directory (`$OPENRAM_HOME/tests`), use the following command to run
+all regression tests:
```
cd OpenRAM/compiler/tests
make -j 3
```
-The -j can run with 3 threads. By default, this will run in all technologies. Note that if you have not run openram, the conda environment will not be installed. You can install it by running OpenRAM/install_conda.sh (see [Basic Setup](basic_setup.md#anaconda) for more details).
+
+The `-j` can run with 3 threads. By default, this will run in all technologies.
+> **Note**: If you have not run openram before running unit tests, the conda
+> environment will not be installed. You can install it by running
+> `OpenRAM/install_conda.sh` (see [Basic Setup](basic_setup.md#anaconda) for
+> more details).
To run a specific test in all technologies:
```
@@ -63,8 +71,8 @@ cd OpenRAM/compiler/tests
TECHS=scn4m_subm make 05_bitcell_array_test
```
-To increase the verbosity of the test, add one (or more) -v options and
-pass it as an argument to OpenRAM:
+To increase the verbosity of the test, add one (or more) `-v` options and pass
+it as an argument to OpenRAM:
```
ARGS="-v" make 05_bitcell_array_test
```
@@ -73,95 +81,52 @@ Unit test results are put in a directory:
```
OpenRAM/compiler/tests/results//
```
-If the test fails, there will be a tmp directory with intermediate results.
-If the test passes, this directory will be deleted to save space.
-You can view the .out file to see what the output of a test is in either case.
-* Tests can be run in the `$OPENRAM_HOME/tests` directory
-* Command line arguments
- * `-v` for verbose
- * `-t` freepdk45 for tech
- * `-d` to preserve /tmp results (done automatically if test fails)
-* Individual tests
- * `01_library_drc_test.py`
-* All tests
- * `regress.py`
+If the test fails, there will be a `tmp` directory with intermediate results. If
+the test passes, this directory will be deleted to save space. You can view the
+`.out` file to see what the output of a test is in either case.
+
+To preserve results on successful tests (done automatically if test fails):
+```
+KEEP=1 make 05_bitcell_array_test
+```
## Successful Unit Tests
```console
-user@host:/openram/compiler/tests$ ./regress.py
- ______________________________________________________________________________
-|==============================================================================|
-|========= Running Test for: =========|
-|========= scn4m_subm =========|
-|========= ./regress.py =========|
-|========= /tmp/openram_mrg_13245_temp/ =========|
-|==============================================================================|
-runTest (00_code_format_check_test.code_format_test) ... ok
-runTest (01_library_drc_test.library_drc_test) ... ok
-runTest (02_library_lvs_test.library_lvs_test) ... ok
-runTest (03_contact_test.contact_test) ... ok
-runTest (03_path_test.path_test) ... ok
+user@host:/openram/compiler/tests$ make
+scn4m_subm/12_tri_gate_array_test ... PASS!
+scn4m_subm/19_pmulti_bank_test ... PASS!
+freepdk45/21_ngspice_delay_global_test ... PASS!
+scn4m_subm/23_lib_sram_linear_regression_test ... PASS!
.
.
.
```
```console
-user@host:/openram/compiler/tests$ ./03_ptx_1finger_nmos_test.py
- ______________________________________________________________________________
-|==============================================================================|
-|========= Running Test for: =========|
-|========= scn4m_subm =========|
-|========= ./03_ptx_1finger_nmos_test.py =========|
-|========= /tmp/openram_mrg_13750_temp/ =========|
-|==============================================================================|
-.
-----------------------------------------------------------------------
-Ran 1 test in 0.596s
-
-OK
+user@host:/openram/compiler/tests$ make 01_library_test
+scn4m_subm/01_library_test ... PASS!
+freepdk45/01_library_test ... PASS!
```
## Debugging Unsuccessful Unit Tests (or sram\_compiler.py)
-* You will get an ERROR during unit test and see a stack trace
-* Examine the temporary output files in the temp directory (/tmp/mydir)
+* You will get a FAIL during unit test
+* You can see the output and stack trace in
+ `$OPENRAM_HOME/tests/results//.out`
+* Examine the temporary output files in the temp directory
+ (`$OPENRAM_HOME/tests/results///`)
```console
- _____________________________________________________________________________
-|==============================================================================|
-|========= Running Test for: =========|
-|========= scn4m_subm =========|
-|========= ./04_pinv_10x_test.py =========|
-|========= /tmp/mydir =========|
-|==============================================================================|
-ERROR: file magic.py: line 174: DRC Errors pinv_0 2
-F
-======================================================================
-FAIL: runTest (__main__.pinv_test)
-----------------------------------------------------------------------
-Traceback (most recent call last):
- File "./04_pinv_10x_test.py", line 22, in runTest
- self.local_check(tx)
- File "/Users/mrg/openram/compiler/tests/testutils.py", line 45, in local_check
- self.fail("DRC failed: {}".format(a.name))
-AssertionError: DRC failed: pinv_0
-
-----------------------------------------------------------------------
-Ran 1 test in 0.609s
-
-FAILED (failures=1)
+user@host:/openram/compiler/tests$ make 01_library_test
+scn4m_subm/01_library_test ... FAIL!
```
### It didn't finish... where are my files?
* OpenRAM puts all temporary files in a temporary directory named:
- * `/tmp/openram___temp`
- * This allows multiple processes/users to simultaneously run
- * This allows /tmp to be mapped to a RAM disk for faster performance
+ * `$OPENRAM_HOME/tests/results///`
+ * This allows multiple unit tests to simultaneously run
* After a successful run, the directory and contents are deleted
- * To preserve the contents, you can run with the `-d` option for debugging
-* `OPENRAM_TMP` will override the temporary directory location for debug
- * `export OPENRAM_TMP="/home/myname/debugdir"`
+ * To preserve the contents, you can run with the `KEEP` option for debugging
@@ -176,4 +141,3 @@ FAILED (failures=1)
-
diff --git a/docs/source/index.md b/docs/source/index.md
index a86f893e..2bb59fbe 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -1,7 +1,8 @@
# OpenRAM Documentation

-These pages provide the documentation of OpenRAM. You can use the links below to navigate through the documentation.
+These pages provide the documentation of OpenRAM. You can use the links below to
+navigate through the documentation.
@@ -10,6 +11,7 @@ These pages provide the documentation of OpenRAM. You can use the links below to
1. [Supported Technologies](#supported-technologies)
1. [Basic Setup](./basic_setup.md#go-back)
1. [Basic Usage](./basic_usage.md#go-back)
+1. [Python Library](./python_library.md#go-back)
1. [Bitcells](./bitcells.md#go-back)
1. [Architecture](./architecture.md#go-back)
1. [Implementation](#implementation)
@@ -29,14 +31,11 @@ These pages provide the documentation of OpenRAM. You can use the links below to
-
## OpenRAM Dependencies
-Please see the Dockerfile for the required versions of tools.
-
In general, the OpenRAM compiler has very few dependencies:
+ Git
+ Make
-+ Python 3.6 or higher
++ Python 3.5 or higher
+ Various Python packages (pip install -r requirements.txt)
+ Anaconda
@@ -80,15 +79,23 @@ Commercial tools (optional):
## Technology and Tool Portability
-* OpenRAM is technology independent by using a technology directory that includes:
+* OpenRAM is technology independent by using a technology directory that
+ includes:
* Technology's specific information
* Technology's rules such as DRC rules and the GDS layer map
- * Custom designed library cells (6T, sense amp, DFF) to improve the SRAM density.
-* For technologies that have specific design requirements, such as specialized well contacts, the user can include helper functions in the technology directory.
+ * Custom designed library cells (6T, sense amp, DFF) to improve the SRAM
+ density.
+* For technologies that have specific design requirements, such as specialized
+ well contacts, the user can include helper functions in the technology
+ directory.
* Verification wrapper scripts
* Uses a wrapper interface with DRC and LVS tools that allow flexibility
- * DRC and LVS can be performed at all levels of the design hierarchy to enhance bug tracking.
- * DRC and LVS can be disabled completely for improved run-time or if licenses are not available.
+ * DRC and LVS can be performed at all levels of the design hierarchy to
+ enhance bug tracking.
+ * DRC and LVS can be disabled completely for improved run-time or if
+ licenses are not available.
+
+
## Contributors/Collaborators
@@ -108,4 +115,3 @@ Commercial tools (optional):
* Marcelo Sero
* Seokjoong Kim
-
diff --git a/docs/source/python_library.md b/docs/source/python_library.md
new file mode 100644
index 00000000..d61a6a82
--- /dev/null
+++ b/docs/source/python_library.md
@@ -0,0 +1,91 @@
+### [Go Back](./index.md#table-of-contents)
+
+# Python Library
+This page explains the Python library of OpenRAM.
+
+
+
+## Table of Contents
+1. [Installation](#installation)
+1. [Environment Variables](#environment-variables)
+1. [Usage](#usage)
+
+
+
+## Installation
+OpenRAM is available as a Python library. There are a few ways to install it:
+
++ Install the latest _stable_ version:
+```
+pip3 install openram
+```
+
++ Install the latest _dev_ version:
+```
+pip3 install git+https://git@github.com/VLSIDA/OpenRAM.git@dev
+```
+
++ Install using Makefile (you need to clone the repo):
+```
+git clone git@github.com:VLSIDA/OpenRAM.git
+cd OpenRAM
+make library
+```
+
+
+
+## Environment Variables
+OpenRAM library doesn't need any environment variables by default. However, if
+you have set the environment variables explained on
+[basic usage](.basic_usage.md#go-back), the library will use the OpenRAM source
+code located at `OPENRAM_HOME`.
+
+If you want the convenience of being able to run OpenRAM from any Python script
+and have a custom OpenRAM setup, you can set these environment variables to
+point to that OpenRAM installation directory.
+
+If you don't want to use this feature, you can simply unset these environment
+variables.
+
+> **Note**: If you are a developer working on the source code on local clone of
+> the repository and want to use the Python library at the same time, you should
+> set both `OPENRAM_HOME` and `OPENRAM_TECH` to point to the local clone (follow
+> [Basic Setup](./basic_setup.md#go-back)). This way, the library will use the
+> source code located at these paths and you won't have to rebuild the library
+> after every change.
+
+
+
+## Usage
+With the OpenRAM library, you can use OpenRAM in any Python script. You can
+import "openram" as follows:
+```python
+import openram
+openram.init_openram("myconfig.py") # Config files are explained on "Basic Usage" page
+# Now you can use modules from openram
+from openram import tech
+...
+```
+
+Note that you need to initialize OpenRAM so that the modules are imported
+properly. You can also look at [sram\_compiler.py](../../sram_compiler.py) as an
+example on how to use "openram."
+
+If you want to pass custom configuration when generating an SRAM, you can use
+the `sram_config` class.
+```python
+import openram
+openram.init_openram("myconfig.py")
+
+from openram import sram_config
+c = sram_config(...)
+
+from openram import sram
+s = sram(sram_config=c,
+ name="custom_name")
+
+s.save()
+
+openram.end_openram()
+```
+
diff --git a/images/download-stable-blue.svg b/images/download-stable-blue.svg
deleted file mode 100644
index 2fbc3649..00000000
--- a/images/download-stable-blue.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/images/download-unstable-blue.svg b/images/download-unstable-blue.svg
deleted file mode 100644
index a233df6b..00000000
--- a/images/download-unstable-blue.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/images/download.svg b/images/download.svg
deleted file mode 100644
index 95d978ed..00000000
--- a/images/download.svg
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/macros/Makefile b/macros/Makefile
index 94e0bf43..e555f3a0 100644
--- a/macros/Makefile
+++ b/macros/Makefile
@@ -17,29 +17,38 @@ endif
# Verbosity
OPENRAM_OPTS += -v --keeptemp
-CONFIG_DIR = configs
-SRCS=$(filter-out disabled-% %_common.py,$(sort $(notdir $(wildcard $(CONFIG_DIR)/*.py))))
-DIRS=$(basename $(SRCS))
-STAMPS=$(addsuffix .ok,$(DIRS))
+SRAM_CONFIG_DIR = sram_configs
+SRAM_SRCS=$(filter-out disabled-% %_common.py,$(sort $(notdir $(wildcard $(SRAM_CONFIG_DIR)/*.py))))
+SRAM_DIRS=$(basename $(SRAM_SRCS))
+SRAM_STAMPS=$(addsuffix .ok,$(SRAM_DIRS))
+
+ROM_CONFIG_DIR = rom_configs
+ROM_SRCS=$(filter-out disabled-% %_common.py,$(sort $(notdir $(wildcard $(ROM_CONFIG_DIR)/*.py))))
+ROM_DIRS=$(basename $(ROM_SRCS))
+ROM_STAMPS=$(addsuffix .ok,$(ROM_DIRS))
configs:
@echo
@echo "Using OpenRAM at $(OPENRAM_HOME)"
@echo " (which is version $$(cd $(OPENRAM_HOME); git describe --tags))"
@echo
- @echo "Configurations:"
- @for D in $(DIRS); do echo " - $$D"; done
+ @echo "SRAM Configurations:"
+ @for D in $(SRAM_DIRS); do echo " - $$D"; done
+ @echo "ROM Configurations:"
+ @for D in $(ROM_DIRS); do echo " - $$D"; done
@echo
.PHONY: configs
BROKEN :=
-WORKING_STAMPS=$(filter-out $(addsuffix .ok, $(BROKEN)), $(STAMPS))
-EXAMPLE_STAMPS=$(filter example%, $(WORKING_STAMPS))
-SKY130_STAMPS=$(filter sky130%, $(WORKING_STAMPS))
-FREEPDK45_STAMPS=$(filter freepdk45%, $(WORKING_STAMPS))
-SCN4M_SUBM_STAMPS=$(filter scn4m_subm%, $(WORKING_STAMPS))
+WORKING_SRAM_STAMPS=$(filter-out $(addsuffix .ok, $(BROKEN)), $(SRAM_STAMPS))
+WORKING_ROM_STAMPS=$(filter-out $(addsuffix .ok, $(BROKEN)), $(ROM_STAMPS))
+
+EXAMPLE_STAMPS=$(filter example%, $(WORKING_SRAM_STAMPS)) $(filter example%, $(WORKING_ROM_STAMPS))
+SKY130_STAMPS=$(filter sky130%, $(WORKING_SRAM_STAMPS)) $(filter sky130%, $(WORKING_ROM_STAMPS))
+FREEPDK45_STAMPS=$(filter freepdk45%, $(WORKING_STAMPS)) $(filter freepdk45%, $(WORKING_ROM_STAMPS))
+SCN4M_SUBM_STAMPS=$(filter scn4m_subm%, $(WORKING_STAMPS)) $(filter scn4m_subm%, $(WORKING_ROM_STAMPS))
all: | configs
@echo
@@ -60,14 +69,24 @@ freepdk45: $(FREEPDK45_STAMPS)
scn4m_subm: $(SCN4M_SUBM_STAMPS)
.PHONY: scn4m_subm
+
+rom: $(WORKING_ROM_STAMPS)
+.PHONY: rom
+
+sram: $(WORKING_SRAM_STAMPS)
+.PHONY: sram
-OPENRAM_TMP=$(MACRO_DIR)/$*/tmp
-%.ok: configs/%.py
+%.ok: sram_configs/%.py
@echo "Building $*"
@mkdir -p $*
- @python3 -u $(OPENRAM_COMPILER) $(OPENRAM_OPTS) -o $* -p $(MACRO_DIR)/$* $(MACRO_DIR)/$< && touch $@
+ @python3 -u $(SRAM_COMPILER) $(OPENRAM_OPTS) -o $* -p $(MACRO_DIR)/$* $(MACRO_DIR)/$< && touch $@
-.DELETE_ON_ERROR: $(STAMPS)
+%.ok: rom_configs/%.py
+ @echo "Building $*"
+ @mkdir -p $*
+ @python3 -u $(ROM_COMPILER) $(OPENRAM_OPTS) -o $* -p $(MACRO_DIR)/$* $(MACRO_DIR)/$< && touch $@
+
+.DELETE_ON_ERROR: $(WORKING_SRAM_STAMPS) $(WORKING_ROM_STAMPS)
$(DIRS):
@$(MAKE) --no-print-directory $@.ok
@@ -75,6 +94,8 @@ $(DIRS):
.PHONY: $(DIRS)
clean:
- rm -rf $(STAMPS)
- rm -rf $(DIRS)
+ rm -rf $(WORKING_SRAM_STAMPS)
+ rm -rf $(WORKING_ROM_STAMPS)
+ rm -rf $(SRAM_DIRS)
+ rm -rf $(ROM_DIRS)
.PHONY: clean
diff --git a/macros/rom_configs/example_1kbyte.dat b/macros/rom_configs/example_1kbyte.dat
new file mode 100644
index 00000000..11006ecd
--- /dev/null
+++ b/macros/rom_configs/example_1kbyte.dat
@@ -0,0 +1 @@
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
\ No newline at end of file
diff --git a/macros/rom_configs/sky130_rom_1kbyte.py b/macros/rom_configs/sky130_rom_1kbyte.py
new file mode 100644
index 00000000..1f598070
--- /dev/null
+++ b/macros/rom_configs/sky130_rom_1kbyte.py
@@ -0,0 +1,19 @@
+# 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.
+#
+
+word_size = 1
+
+check_lvsdrc = True
+
+rom_data = "macros/rom_configs/example_1kbyte.dat"
+
+output_name = "rom_1kbyte"
+output_path = "macro/{output_name}".format(**locals())
+
+import os
+exec(open(os.path.join(os.path.dirname(__file__), 'sky130_rom_common.py')).read())
diff --git a/macros/rom_configs/sky130_rom_common.py b/macros/rom_configs/sky130_rom_common.py
new file mode 100644
index 00000000..d36887bf
--- /dev/null
+++ b/macros/rom_configs/sky130_rom_common.py
@@ -0,0 +1,8 @@
+
+tech_name = "sky130"
+nominal_corner_only = True
+
+route_supplies = "ring"
+check_lvsdrc = True
+# check_lvsdrc = False
+
diff --git a/macros/configs/example_config_1rw_1r_scn4m_subm.py b/macros/sram_configs/example_config_1rw_1r_scn4m_subm.py
similarity index 100%
rename from macros/configs/example_config_1rw_1r_scn4m_subm.py
rename to macros/sram_configs/example_config_1rw_1r_scn4m_subm.py
diff --git a/macros/configs/example_config_1rw_1w_scn4m_subm.py b/macros/sram_configs/example_config_1rw_1w_scn4m_subm.py
similarity index 100%
rename from macros/configs/example_config_1rw_1w_scn4m_subm.py
rename to macros/sram_configs/example_config_1rw_1w_scn4m_subm.py
diff --git a/macros/configs/example_config_1rw_2mux_scn4m_subm.py b/macros/sram_configs/example_config_1rw_2mux_scn4m_subm.py
similarity index 100%
rename from macros/configs/example_config_1rw_2mux_scn4m_subm.py
rename to macros/sram_configs/example_config_1rw_2mux_scn4m_subm.py
diff --git a/macros/configs/example_config_1w_1r_scn4m_subm.py b/macros/sram_configs/example_config_1w_1r_scn4m_subm.py
similarity index 100%
rename from macros/configs/example_config_1w_1r_scn4m_subm.py
rename to macros/sram_configs/example_config_1w_1r_scn4m_subm.py
diff --git a/macros/configs/example_config_2rw_scn4m_subm.py b/macros/sram_configs/example_config_2rw_scn4m_subm.py
similarity index 100%
rename from macros/configs/example_config_2rw_scn4m_subm.py
rename to macros/sram_configs/example_config_2rw_scn4m_subm.py
diff --git a/macros/configs/example_config_big_scn4m_subm.py b/macros/sram_configs/example_config_big_scn4m_subm.py
similarity index 100%
rename from macros/configs/example_config_big_scn4m_subm.py
rename to macros/sram_configs/example_config_big_scn4m_subm.py
diff --git a/macros/configs/example_config_freepdk45.py b/macros/sram_configs/example_config_freepdk45.py
similarity index 100%
rename from macros/configs/example_config_freepdk45.py
rename to macros/sram_configs/example_config_freepdk45.py
diff --git a/macros/configs/example_config_giant_scn4m_subm.py b/macros/sram_configs/example_config_giant_scn4m_subm.py
similarity index 100%
rename from macros/configs/example_config_giant_scn4m_subm.py
rename to macros/sram_configs/example_config_giant_scn4m_subm.py
diff --git a/macros/configs/example_config_medium_scn4m_subm.py b/macros/sram_configs/example_config_medium_scn4m_subm.py
similarity index 100%
rename from macros/configs/example_config_medium_scn4m_subm.py
rename to macros/sram_configs/example_config_medium_scn4m_subm.py
diff --git a/macros/configs/example_config_scn4m_subm.py b/macros/sram_configs/example_config_scn4m_subm.py
similarity index 100%
rename from macros/configs/example_config_scn4m_subm.py
rename to macros/sram_configs/example_config_scn4m_subm.py
diff --git a/macros/configs/freepdk45_sram_1rw1r_32x2048_8.py b/macros/sram_configs/freepdk45_sram_1rw1r_32x2048_8.py
similarity index 100%
rename from macros/configs/freepdk45_sram_1rw1r_32x2048_8.py
rename to macros/sram_configs/freepdk45_sram_1rw1r_32x2048_8.py
diff --git a/macros/configs/scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py b/macros/sram_configs/scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py
similarity index 100%
rename from macros/configs/scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py
rename to macros/sram_configs/scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py
diff --git a/macros/configs/scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py b/macros/sram_configs/scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py
similarity index 100%
rename from macros/configs/scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py
rename to macros/sram_configs/scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py
diff --git a/macros/configs/scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py b/macros/sram_configs/scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py
similarity index 100%
rename from macros/configs/scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py
rename to macros/sram_configs/scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py
diff --git a/macros/configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py b/macros/sram_configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py
similarity index 100%
rename from macros/configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py
rename to macros/sram_configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py
diff --git a/macros/configs/scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py b/macros/sram_configs/scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py
similarity index 100%
rename from macros/configs/scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py
rename to macros/sram_configs/scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py
diff --git a/macros/configs/scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py b/macros/sram_configs/scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py
similarity index 100%
rename from macros/configs/scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py
rename to macros/sram_configs/scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py
diff --git a/macros/configs/sky130_sram_1kbyte_1r1w_8x1024_8.py b/macros/sram_configs/sky130_sram_1kbyte_1r1w_8x1024_8.py
similarity index 100%
rename from macros/configs/sky130_sram_1kbyte_1r1w_8x1024_8.py
rename to macros/sram_configs/sky130_sram_1kbyte_1r1w_8x1024_8.py
diff --git a/macros/configs/sky130_sram_1kbyte_1rw1r_32x256_8.py b/macros/sram_configs/sky130_sram_1kbyte_1rw1r_32x256_8.py
similarity index 100%
rename from macros/configs/sky130_sram_1kbyte_1rw1r_32x256_8.py
rename to macros/sram_configs/sky130_sram_1kbyte_1rw1r_32x256_8.py
diff --git a/macros/configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py b/macros/sram_configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py
similarity index 100%
rename from macros/configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py
rename to macros/sram_configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py
diff --git a/macros/configs/sky130_sram_1kbyte_1rw_32x256_8.py b/macros/sram_configs/sky130_sram_1kbyte_1rw_32x256_8.py
similarity index 100%
rename from macros/configs/sky130_sram_1kbyte_1rw_32x256_8.py
rename to macros/sram_configs/sky130_sram_1kbyte_1rw_32x256_8.py
diff --git a/macros/configs/sky130_sram_1rw1r_tiny.py b/macros/sram_configs/sky130_sram_1rw1r_tiny.py
similarity index 100%
rename from macros/configs/sky130_sram_1rw1r_tiny.py
rename to macros/sram_configs/sky130_sram_1rw1r_tiny.py
diff --git a/macros/configs/sky130_sram_1rw_tiny.py b/macros/sram_configs/sky130_sram_1rw_tiny.py
similarity index 100%
rename from macros/configs/sky130_sram_1rw_tiny.py
rename to macros/sram_configs/sky130_sram_1rw_tiny.py
diff --git a/macros/configs/sky130_sram_2kbyte_1rw1r_32x512_8.py b/macros/sram_configs/sky130_sram_2kbyte_1rw1r_32x512_8.py
similarity index 100%
rename from macros/configs/sky130_sram_2kbyte_1rw1r_32x512_8.py
rename to macros/sram_configs/sky130_sram_2kbyte_1rw1r_32x512_8.py
diff --git a/macros/configs/sky130_sram_2kbyte_1rw_32x512_8.py b/macros/sram_configs/sky130_sram_2kbyte_1rw_32x512_8.py
similarity index 100%
rename from macros/configs/sky130_sram_2kbyte_1rw_32x512_8.py
rename to macros/sram_configs/sky130_sram_2kbyte_1rw_32x512_8.py
diff --git a/macros/configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py b/macros/sram_configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py
similarity index 100%
rename from macros/configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py
rename to macros/sram_configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py
diff --git a/macros/configs/sky130_sram_4kbyte_1rw_32x1024_8.py b/macros/sram_configs/sky130_sram_4kbyte_1rw_32x1024_8.py
similarity index 100%
rename from macros/configs/sky130_sram_4kbyte_1rw_32x1024_8.py
rename to macros/sram_configs/sky130_sram_4kbyte_1rw_32x1024_8.py
diff --git a/macros/configs/sky130_sram_4kbyte_1rw_64x512_8.py b/macros/sram_configs/sky130_sram_4kbyte_1rw_64x512_8.py
similarity index 100%
rename from macros/configs/sky130_sram_4kbyte_1rw_64x512_8.py
rename to macros/sram_configs/sky130_sram_4kbyte_1rw_64x512_8.py
diff --git a/macros/configs/sky130_sram_common.py b/macros/sram_configs/sky130_sram_common.py
similarity index 100%
rename from macros/configs/sky130_sram_common.py
rename to macros/sram_configs/sky130_sram_common.py
diff --git a/openram.mk b/openram.mk
index 60b59f17..b86e4a94 100644
--- a/openram.mk
+++ b/openram.mk
@@ -1,11 +1,12 @@
OPENRAM_HOME := $(abspath $(TOP_DIR)/compiler)
OPENRAM_TECH := $(abspath $(TOP_DIR)/technology)
-OPENRAM_COMPILER := $(abspath $(TOP_DIR)/sram_compiler.py)
+SRAM_COMPILER := $(abspath $(TOP_DIR)/sram_compiler.py)
+ROM_COMPILER := $(abspath $(TOP_DIR)/rom_compiler.py)
PDK_ROOT ?= $(TOP_DIR)
-ifeq (,$(wildcard $(OPENRAM_COMPILER)))
-$(error Did not find '$(OPENRAM_COMPILER)' in '$(OPENRAM_HOME)' (from $$OPENRAM_HOME))
+ifeq (,$(wildcard $(SRAM_COMPILER)))
+$(error Did not find '$(SRAM_COMPILER)' in '$(OPENRAM_HOME)' (from $$OPENRAM_HOME))
endif
export OPENRAM_HOME
export OPENRAM_TECH
diff --git a/rom_compiler.py b/rom_compiler.py
new file mode 100755
index 00000000..f09c1c0a
--- /dev/null
+++ b/rom_compiler.py
@@ -0,0 +1,72 @@
+#!/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.
+#
+
+"""
+ROM Compiler
+
+The output files append the given suffixes to the output name:
+a spice (.sp) file for circuit simulation
+a GDS2 (.gds) file containing the layout
+"""
+
+
+import sys
+import os
+import datetime
+
+# You don't need the next two lines if you're sure that openram package is installed
+from common import *
+make_openram_package()
+import openram
+
+
+(OPTS, args) = openram.parse_args()
+
+
+# Check that we are left with a single configuration file as argument.
+if len(args) != 1:
+ print(openram.USAGE)
+ sys.exit(2)
+
+# These depend on arguments, so don't load them until now.
+from openram import debug
+
+# Parse config file and set up all the options
+openram.init_openram(config_file=args[0])
+
+# Only print banner here so it's not in unit tests
+openram.print_banner()
+
+# Keep track of running stats
+start_time = datetime.datetime.now()
+openram.print_time("Start", start_time)
+
+
+output_extensions = [ "sp", "v"]
+# Only output lef/gds if back-end
+if not OPTS.netlist_only:
+ output_extensions.extend(["gds"])
+
+output_files = ["{0}{1}.{2}".format(OPTS.output_path,
+ OPTS.output_name, x)
+ for x in output_extensions]
+debug.print_raw("Output files are: ")
+for path in output_files:
+ debug.print_raw(path)
+
+from openram import rom
+
+r = rom()
+
+# Output the files for the resulting ROM
+r.save()
+
+# Delete temp files etc.
+openram.end_openram()
+openram.print_time("End", datetime.datetime.now(), start_time)
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 8f3fd9f2..80883771 100644
--- a/setup.py
+++ b/setup.py
@@ -52,17 +52,43 @@ with open("requirements.txt") as f:
version = open("VERSION", "r").read().rstrip()
+with open("README.md") as f:
+ long_description = f.read()
+
+
# Call the setup to create the package
setup(
name="openram",
version=version,
description="An open-source static random access memory (SRAM) compiler",
+ long_description=long_description,
+ long_description_content_type="text/markdown",
url="https://openram.org/",
+ download_url="https://github.com/VLSIDA/OpenRAM/releases",
+ project_urls={
+ "Bug Tracker": "https://github.com/VLSIDA/OpenRAM/issues",
+ "Documentation": "https://github.com/VLSIDA/OpenRAM/blob/stable/docs/source/index.md",
+ "Source Code": "https://github.com/VLSIDA/OpenRAM",
+ },
author="Matthew Guthaus",
- author_email="mrg@ucsc.edu",
+ author_email="mrg+vlsida@ucsc.edu",
keywords=[ "sram", "magic", "gds", "netgen", "ngspice", "netlist" ],
- readme="README.md",
- license="BSD-3",
+ license="BSD 3-Clause",
+ python_requires=">=3.5",
+ classifiers=[
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: Developers",
+ "Intended Audience :: Science/Research",
+ "License :: OSI Approved :: BSD License",
+ "Natural Language :: English",
+ "Operating System :: Unix",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3 :: Only",
+ "Topic :: Scientific/Engineering",
+ "Topic :: Software Development",
+ "Topic :: System :: Hardware",
+ ],
packages=packages,
package_dir=package_dir,
include_package_data=True,