mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into char
This commit is contained in:
commit
dae275c508
|
|
@ -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
|
||||
|
|
|
|||
12
Makefile
12
Makefile
|
|
@ -69,12 +69,12 @@ $(OPEN_PDKS_DIR): $(SKY130_PDKS_DIR)
|
|||
|
||||
$(SKY130_PDK): $(OPEN_PDKS_DIR) $(SKY130_PDKS_DIR)
|
||||
@echo "Installing open_pdks..."
|
||||
$(DOCKER_CMD) sh -c ". /home/cad-user/.bashrc && cd /pdk/open_pdks && \
|
||||
./configure --enable-sky130-pdk=/pdk/skywater-pdk/libraries --with-sky130-local-path=/pdk && \
|
||||
cd sky130 && \
|
||||
make veryclean && \
|
||||
make && \
|
||||
make SHARED_PDKS_PATH=/pdk install"
|
||||
@cd $(PDK_ROOT)/open_pdks && \
|
||||
./configure --enable-sky130-pdk=$(PDK_ROOT)/skywater-pdk/libraries --with-sky130-local-path=$(PDK_ROOT) && \
|
||||
cd sky130 && \
|
||||
make veryclean && \
|
||||
make && \
|
||||
make SHARED_PDKS_PATH=$(PDK_ROOT) install
|
||||
|
||||
$(SRAM_LIB_DIR): check-pdk-root
|
||||
@echo "Cloning SRAM library..."
|
||||
|
|
|
|||
32
README.md
32
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?
|
||||
<img align="right" width="25%" src="images/SCMOS_16kb_sram.jpg">
|
||||
<img align="right" width="25%" src="https://raw.githubusercontent.com/VLSIDA/OpenRAM/stable/images/SCMOS_16kb_sram.jpg">
|
||||
|
||||
OpenRAM is an award winning open-source Python framework to create the layout,
|
||||
netlists, timing and power models, placement and routing models, and
|
||||
|
|
@ -17,20 +18,26 @@ other views necessary to use SRAMs in ASIC design. OpenRAM supports
|
|||
integration in both commercial and open-source flows with both
|
||||
predictive and fabricable technologies.
|
||||
|
||||
|
||||
|
||||
# Documentation
|
||||
|
||||
Please see our [documentation][documentation] and let us know if anything needs
|
||||
updating.
|
||||
|
||||
|
||||
|
||||
# Get Involved
|
||||
|
||||
+ [Port it](./PORTING.md) to a new technology.
|
||||
+ Report bugs by submitting [Github issues].
|
||||
+ [Port it](./PORTING.md) to a new technology
|
||||
+ Report bugs by submitting [Github issues]
|
||||
+ Develop new features (see [how to contribute](./CONTRIBUTING.md))
|
||||
+ Submit code/fixes using a [Github pull request]
|
||||
+ Follow our [project][Github project].
|
||||
+ Follow our [project][Github project]
|
||||
+ Read and cite our [ICCAD paper][OpenRAMpaper]
|
||||
|
||||
|
||||
|
||||
# Further Help
|
||||
|
||||
+ [Documentation][documentation]
|
||||
|
|
@ -38,9 +45,13 @@ updating.
|
|||
+ [OpenRAM Users Group][user-group] ([subscribe here][user-group-subscribe])
|
||||
+ [OpenRAM Developers Group][dev-group] ([subscribe here][dev-group-subscribe])
|
||||
|
||||
|
||||
|
||||
# License
|
||||
|
||||
OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
|
||||
OpenRAM is licensed under the [BSD 3-Clause License](./LICENSE).
|
||||
|
||||
|
||||
|
||||
# Publications
|
||||
|
||||
|
|
@ -53,6 +64,7 @@ OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
|
|||
+ [H. Nichols, "Statistical Modeling of SRAMs", M.S. Thesis, UCSC, 2022.](https://escholarship.org/content/qt7vx9n089/qt7vx9n089_noSplash_cfc4ba479d8eb1b6ec25d7c92357bc18.pdf?t=ra9wzr)
|
||||
+ [M. Guthaus, H. Nichols, J. Cirimelli-Low, J. Kunzler, B. Wu, "Enabling Design Technology Co-Optimization of SRAMs though Open-Source Software", IEEE International Electron Devices Meeting (IEDM), 2020.](https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=9372047)
|
||||
|
||||
|
||||
|
||||
# Contributors & Acknowledgment
|
||||
|
||||
|
|
@ -62,7 +74,7 @@ OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
|
|||
|
||||
If I forgot to add you, please let me know!
|
||||
|
||||
* * *
|
||||
|
||||
|
||||
[Matthew Guthaus]: https://users.soe.ucsc.edu/~mrg
|
||||
[James Stine]: https://ece.okstate.edu/content/stine-james-e-jr-phd
|
||||
|
|
|
|||
|
|
@ -44,3 +44,6 @@ from .globals import *
|
|||
# sram_config should be imported before sram
|
||||
from .sram_config import *
|
||||
from .sram import *
|
||||
|
||||
from .rom_config import *
|
||||
from .rom import *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -20,3 +20,10 @@ class col_cap_bitcell_1port(bitcell_base):
|
|||
debug.info(2, "Create col_cap bitcell 1 port object")
|
||||
|
||||
self.no_instances = True
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
Adds edges based on inputs/outputs.
|
||||
Overrides base class function.
|
||||
"""
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -20,3 +20,10 @@ class col_cap_bitcell_2port(bitcell_base):
|
|||
debug.info(2, "Create col_cap bitcell 2 port object")
|
||||
|
||||
self.no_instances = True
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
Adds edges based on inputs/outputs.
|
||||
Overrides base class function.
|
||||
"""
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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())
|
||||
|
|
@ -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)
|
||||
|
|
@ -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])
|
||||
|
|
@ -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())
|
||||
|
|
@ -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")
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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))
|
||||
|
|
@ -20,3 +20,10 @@ class row_cap_bitcell_1port(bitcell_base):
|
|||
debug.info(2, "Create row_cap bitcell 1 port object")
|
||||
|
||||
self.no_instances = True
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
Adds edges based on inputs/outputs.
|
||||
Overrides base class function.
|
||||
"""
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -20,3 +20,10 @@ class row_cap_bitcell_2port(bitcell_base):
|
|||
debug.info(2, "Create row_cap bitcell 2 port object")
|
||||
|
||||
self.no_instances = True
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
Adds edges based on inputs/outputs.
|
||||
Overrides base class function.
|
||||
"""
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -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
|
||||
###################
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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())
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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())
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,28 +15,14 @@ TEST_STAMPS= $(addsuffix .ok,$(TEST_BASES))
|
|||
OPENRAM_DIR = $(OPENRAM_HOME)/tests
|
||||
RESULTS_DIR = $(OPENRAM_DIR)/results
|
||||
|
||||
# Use % for all techs:
|
||||
|
||||
|
||||
# Use % for all techs:
|
||||
# %/test.py
|
||||
# or a specific tech:
|
||||
# freepdk45/test.py
|
||||
BROKEN_STAMPS = \
|
||||
sky130/01_library_test.ok \
|
||||
sky130/04_column_mux_pbitcell_test.ok \
|
||||
sky130/04_dummy_pbitcell_test.ok \
|
||||
sky130/04_pbitcell_test.ok \
|
||||
sky130/04_pnand4_test.ok \
|
||||
sky130/04_pand4_test.ok \
|
||||
sky130/04_precharge_pbitcell_test.ok \
|
||||
sky130/04_replica_pbitcell_test.ok \
|
||||
sky130/05_pbitcell_array_test.ok \
|
||||
sky130/05_bitcell_array_test.ok \
|
||||
sky130/05_bitcell_array_1rw_1r_test.ok \
|
||||
sky130/05_dummy_array_test.ok \
|
||||
%/06_hierarchical_decoder_4096row_test.ok \
|
||||
sky130/07_column_mux_array_pbitcell_test.ok \
|
||||
sky130/19_pmulti_bank_test.ok \
|
||||
sky130/19_psingle_bank_test.ok \
|
||||
sky130/19_bank_select_pbitcell_test.ok \
|
||||
%/19_single_bank_16mux_1rw_1r_test.ok \
|
||||
%/19_single_bank_16mux_test.ok \
|
||||
%/20_sram_1bank_16mux_1rw_1r_test.ok \
|
||||
|
|
@ -47,11 +33,6 @@ BROKEN_STAMPS = \
|
|||
%/20_psram_1bank_2mux_test.ok \
|
||||
%/21_hspice_delay_test.ok \
|
||||
%/21_hspice_setuphold_test.ok \
|
||||
sky130/20_psram_1bank_4mux_1rw_1r_test.ok \
|
||||
sky130/22_psram_1bank_2mux_func_test.ok \
|
||||
sky130/22_psram_1bank_4mux_func_test.ok \
|
||||
sky130/22_psram_1bank_8mux_func_test.ok \
|
||||
sky130/22_psram_1bank_nomux_func_test.ok \
|
||||
%/22_psram_1bank_2mux_func_test.ok \
|
||||
%/22_psram_1bank_4mux_func_test.ok \
|
||||
%/22_psram_1bank_8mux_func_test.ok \
|
||||
|
|
@ -66,11 +47,6 @@ BROKEN_STAMPS = \
|
|||
%/22_sram_1bank_nomux_sparecols_func_test.ok \
|
||||
%/22_sram_1bank_wmask_1rw_1r_func_test.ok \
|
||||
%/22_sram_wmask_func_test.ok \
|
||||
sky130/23_lib_sram_linear_regression_test.ok \
|
||||
sky130/23_lib_sram_model_corners_test.ok \
|
||||
sky130/23_lib_sram_model_test.ok \
|
||||
sky130/23_lib_sram_prune_test.ok \
|
||||
sky131/23_lib_sram_test.ok \
|
||||
%/26_hspice_pex_pinv_test.ok \
|
||||
%/27_verilog_multibank_test.ok \
|
||||
%/50_riscv_1k_1rw1r_func_test.ok \
|
||||
|
|
@ -87,6 +63,51 @@ BROKEN_STAMPS = \
|
|||
%/50_riscv_512b_1rw_func_test.ok \
|
||||
%/50_riscv_8k_1rw1r_func_test.ok \
|
||||
%/50_riscv_8k_1rw_func_test.ok \
|
||||
freepdk45/06_rom_decoder_test.ok \
|
||||
freepdk45/07_rom_column_mux_array_test.ok \
|
||||
freepdk45/08_rom_decoder_buffer_array_test.ok \
|
||||
freepdk45/08_rom_precharge_array_test.ok \
|
||||
freepdk45/10_rom_wordline_driver_array_test.ok \
|
||||
freepdk45/14_rom_array_test.ok \
|
||||
freepdk45/16_rom_control_logic_test.ok \
|
||||
freepdk45/19_rom_bank_test.ok \
|
||||
freepdk45/21_xyce_delay_test.ok \
|
||||
scn4m_subm/06_rom_decoder_test.ok \
|
||||
scn4m_subm/07_rom_column_mux_array_test.ok \
|
||||
scn4m_subm/08_rom_decoder_buffer_array_test.ok \
|
||||
scn4m_subm/08_rom_precharge_array_test.ok \
|
||||
scn4m_subm/10_rom_wordline_driver_array_test.ok \
|
||||
scn4m_subm/14_rom_array_test.ok \
|
||||
scn4m_subm/16_rom_control_logic_test.ok \
|
||||
scn4m_subm/19_rom_bank_test.ok \
|
||||
scn4m_subm/19_single_bank_global_bitline_test.ok \
|
||||
scn4m_subm/21_xyce_delay_test.ok \
|
||||
sky130/01_library_test.ok \
|
||||
sky130/04_column_mux_pbitcell_test.ok \
|
||||
sky130/04_dummy_pbitcell_test.ok \
|
||||
sky130/04_pbitcell_test.ok \
|
||||
sky130/04_pnand4_test.ok \
|
||||
sky130/04_pand4_test.ok \
|
||||
sky130/04_precharge_pbitcell_test.ok \
|
||||
sky130/04_replica_pbitcell_test.ok \
|
||||
sky130/05_pbitcell_array_test.ok \
|
||||
sky130/05_bitcell_array_test.ok \
|
||||
sky130/05_bitcell_array_1rw_1r_test.ok \
|
||||
sky130/05_dummy_array_test.ok \
|
||||
sky130/07_column_mux_array_pbitcell_test.ok \
|
||||
sky130/19_pmulti_bank_test.ok \
|
||||
sky130/19_psingle_bank_test.ok \
|
||||
sky130/19_bank_select_pbitcell_test.ok \
|
||||
sky130/20_psram_1bank_4mux_1rw_1r_test.ok \
|
||||
sky130/22_psram_1bank_2mux_func_test.ok \
|
||||
sky130/22_psram_1bank_4mux_func_test.ok \
|
||||
sky130/22_psram_1bank_8mux_func_test.ok \
|
||||
sky130/22_psram_1bank_nomux_func_test.ok \
|
||||
sky130/23_lib_sram_linear_regression_test.ok \
|
||||
sky130/23_lib_sram_model_corners_test.ok \
|
||||
sky130/23_lib_sram_model_test.ok \
|
||||
sky130/23_lib_sram_prune_test.ok \
|
||||
sky131/23_lib_sram_test.ok
|
||||
|
||||
gettech = $(word 1,$(subst /, ,$*))
|
||||
getfile = $(word 2,$(subst /, ,$*))
|
||||
|
|
@ -103,7 +124,7 @@ all: clean $(WORKING_TECH_TEST_STAMPS)
|
|||
|
||||
# Run a given technology
|
||||
# e.g. make freepdk45
|
||||
$(TECHS):
|
||||
$(TECHS):
|
||||
@$(MAKE) --no-print-directory $(filter-out $(BROKEN_STAMPS), $(addprefix $@/, $(TEST_STAMPS)))
|
||||
.PHONY: $(TECHS)
|
||||
|
||||
|
|
@ -114,7 +135,7 @@ $(TEST_BASES):
|
|||
.PHONY: $(TEST_BASES)
|
||||
|
||||
# To run a test in a given technology
|
||||
%.ok:
|
||||
%.ok:
|
||||
# @echo "Running $(gettech) $(getfile) ... "
|
||||
@rm -rf results/$*
|
||||
@rm -rf results/$*.*
|
||||
|
|
@ -127,10 +148,6 @@ else
|
|||
endif
|
||||
.DELETE_ON_ERROR: $(TEST_STAMPS)
|
||||
|
||||
.PHONY: docker-pull
|
||||
docker-pull:
|
||||
docker pull vlsida/openram-ubuntu:latest
|
||||
|
||||
clean:
|
||||
@rm -rf $(TOP_DIR)/compiler/tests/results
|
||||
.PHONE: clean
|
||||
.PHONY: clean
|
||||
|
|
|
|||
|
|
@ -17,3 +17,4 @@ check_lvsdrc = True
|
|||
#route_supplies = False
|
||||
|
||||
output_name = "sram"
|
||||
rom_data_dir = "tests/configs"
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFF
|
||||
|
|
@ -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())
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
04_dummy_pbitcell_test.py
|
||||
04_pbitcell_test.py
|
||||
04_precharge_pbitcell_test.py
|
||||
04_replica_pbitcell_test.py
|
||||
04_column_mux_pbitcell_test.py
|
||||
05_bitcell_array_test.py
|
||||
05_dummy_array_test.py
|
||||
05_pbitcell_array_test.py
|
||||
06_hierarchical_decoder_pbitcell_test.py
|
||||
06_hierarchical_decoder_test.py
|
||||
06_hierarchical_predecode2x4_pbitcell_test.py
|
||||
06_hierarchical_predecode2x4_test.py
|
||||
06_hierarchical_predecode3x8_pbitcell_test.py
|
||||
06_hierarchical_predecode3x8_test.py
|
||||
06_hierarchical_predecode4x16_test.py
|
||||
07_column_mux_array_pbitcell_test.py
|
||||
08_wordline_driver_array_pbitcell_test.py
|
||||
08_wordline_driver_array_test.py
|
||||
09_sense_amp_array_test_pbitcell.py
|
||||
09_sense_amp_array_test.py
|
||||
10_write_driver_array_pbitcell_test.py
|
||||
10_write_driver_array_test.py
|
||||
10_write_driver_array_wmask_pbitcell_test.py
|
||||
10_write_driver_array_wmask_test.py
|
||||
10_write_mask_and_array_pbitcell_test.py
|
||||
10_write_mask_and_array_test.py
|
||||
12_tri_gate_array_test.py
|
||||
14_replica_pbitcell_array_test.py
|
||||
14_replica_bitcell_array_test.py
|
||||
14_replica_column_test.py
|
||||
14_replica_column_1rw_1r_test.py
|
||||
18_port_address_test.py
|
||||
18_port_data_test.py
|
||||
18_port_data_wmask_test.py
|
||||
19_bank_select_pbitcell_test.py
|
||||
19_bank_select_test.py
|
||||
19_psingle_bank_test.py
|
||||
19_bank_select_pbitcell_test.py
|
||||
19_pmulti_bank_test.py
|
||||
19_multi_bank_test.py
|
||||
19_psingle_bank_test.py
|
||||
19_single_bank_1w_1r_test.py
|
||||
19_single_bank_wmask_1rw_1r_test.py
|
||||
19_single_bank_1rw_1r_test.py
|
||||
19_single_bank_test.py
|
||||
19_single_bank_wmask_test.py
|
||||
20_psram_1bank_2mux_1rw_1w_test.py
|
||||
20_psram_1bank_2mux_1rw_1w_wmask_test.py
|
||||
20_psram_1bank_2mux_1w_1r_test.py
|
||||
20_psram_1bank_2mux_test.py
|
||||
20_psram_1bank_4mux_1rw_1r_test.py
|
||||
20_sram_1bank_2mux_1w_1r_test.py
|
||||
20_sram_1bank_2mux_test.py
|
||||
20_sram_1bank_2mux_wmask_test.py
|
||||
20_sram_1bank_32b_1024_wmask_test.py
|
||||
20_sram_1bank_4mux_test.py
|
||||
20_sram_1bank_8mux_test.py
|
||||
20_sram_1bank_nomux_test.py
|
||||
20_sram_1bank_nomux_wmask_test.py
|
||||
20_sram_2bank_test.py
|
||||
21_hspice_delay_test.py
|
||||
21_hspice_setuphold_test.py
|
||||
21_model_delay_test.py
|
||||
21_ngspice_delay_test.py
|
||||
21_ngspice_setuphold_test.py
|
||||
22_psram_1bank_2mux_func_test.py
|
||||
22_psram_1bank_4mux_func_test.py
|
||||
22_psram_1bank_8mux_func_test.py
|
||||
22_psram_1bank_nomux_func_test.py
|
||||
22_sram_1bank_2mux_func_test.py
|
||||
22_sram_1bank_4mux_func_test.py
|
||||
22_sram_1bank_8mux_func_test.py
|
||||
22_sram_1bank_nomux_func_test.py
|
||||
22_sram_1rw_1r_1bank_nomux_func_test.py
|
||||
22_sram_wmask_func_test.py
|
||||
23_lib_sram_model_corners_test.py
|
||||
23_lib_sram_model_test.py
|
||||
23_lib_sram_prune_test.py
|
||||
23_lib_sram_test.py
|
||||
24_lef_sram_test.py
|
||||
25_verilog_sram_test.py
|
||||
26_hspice_pex_pinv_test.py
|
||||
26_ngspice_pex_pinv_test.py
|
||||
26_pex_test.py
|
||||
30_openram_back_end_test.py
|
||||
30_openram_front_end_test.py
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue