diff --git a/.github/workflows/README.md b/.github/workflows/README.md index b0e9ddf7..a4d19df1 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -8,7 +8,6 @@ commit" means. 1. If `regress` workflow fails on 'private/dev', `sync` workflow gets triggered and it pushes the latest changes to the public repo's 'dev' branch (public/dev). -After this push, `regress` workflow will also run on 'public/dev'. 1. If `regress` workflow successfully passes on 'private/dev', `version` workflow gets triggered. It creates a new version commit and tag, and pushes to @@ -18,16 +17,6 @@ workflow gets triggered. It creates a new version commit and tag, and pushes to workflow runs. It deploys the PyPI package of OpenRAM and creates a new GitHub release on that repo. -1. If there is a pull request on either repo, `regress` workflow runs on that -pull request. - -1. If there is a push to 'public/dev', `regress` workflow runs (it also happens -when pull requests are merged). - -1. If `regress` workflow successfully passes on 'public/dev', `version` -workflow gets triggered. It creates a new version commit and tag, and pushes to -'private/dev', 'public/dev', and 'public/stable'. - ## Important Notes @@ -54,7 +43,7 @@ automatically. That means, you don't have to tag that commit manually. this commit was automatically generated after a previous commit passed `regress` workflow or was manually generated with caution. -1. `regress` workflow doesn't run on branches named 'stable'. +1. `regress` workflow doesn't run on the public repo. 1. `deploy` workflow only runs on branches named 'stable'. @@ -63,17 +52,14 @@ workflow or was manually generated with caution. 1. `sync` workflow only runs on the private repo. -1. Pull requests merged on to 'public/dev' will also trigger `regress` and it -can create a new version. - -1. Merging pull requests that don't pass `regress` workflow on the public repo -should be avoided since it won't update the private repo automatically. To -prevent merging by mistake, the dev branch can be protected in the GitHub -settings. +1. `sync_tag` workflow only runs on the private repo. 1. Merging pull requests on the private repo should be safe in any case. They are treated the same as commit pushes. +> **Warning**: `regress` workflow is currently disabled on the public repo +> manually. This was done because of a security risk on our private server. +> Enabling it on GitHub will run `regress` workflow on the public repo. ## Flowchart diff --git a/Makefile b/Makefile index 467277cf..fb0e8e6c 100644 --- a/Makefile +++ b/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..." diff --git a/README.md b/README.md index 46d43290..0bee4049 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ -![](./images/OpenRAM_logo_yellow_transparent.svg) +![](https://raw.githubusercontent.com/VLSIDA/OpenRAM/stable/images/OpenRAM_logo_yellow_transparent.svg) # OpenRAM [![Python 3.5](https://img.shields.io/badge/Python-3.5-green.svg)](https://www.python.org/) -[![License: BSD 3-clause](./images/license_badge.svg)](./LICENSE) -[![Download](./images/download-stable-blue.svg)](https://github.com/VLSIDA/OpenRAM/archive/stable.zip) -[![Download](./images/download-unstable-blue.svg)](https://github.com/VLSIDA/OpenRAM/archive/dev.zip) +[![License: BSD 3-clause](https://raw.githubusercontent.com/VLSIDA/OpenRAM/stable/images/license_badge.svg)](./LICENSE) +[![PyPI - Downloads](https://img.shields.io/pypi/dm/openram?color=brightgreen&label=PyPI)](https://pypi.org/project/openram/) An open-source static random access memory (SRAM) compiler. + + # What is OpenRAM? - + 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 diff --git a/VERSION b/VERSION index 6085e946..db6fb4a9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.1 +1.2.8 diff --git a/__init__.py b/__init__.py index 746ce73b..dd5751d5 100644 --- a/__init__.py +++ b/__init__.py @@ -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 * diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 9a11b68a..d33d552d 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -696,13 +696,15 @@ class layout(): start=left_pos, end=right_pos) - def connect_row_pins(self, layer, pins, name=None, full=False): + def connect_row_pins(self, layer, pins, name=None, full=False, round=False): """ Connects left/right rows that are aligned. """ bins = {} for pin in pins: y = pin.cy() + if round: + y = round_to_grid(y) try: bins[y].append(pin) except KeyError: @@ -785,13 +787,15 @@ class layout(): end=bot_pos) - def connect_col_pins(self, layer, pins, name=None, full=False): + def connect_col_pins(self, layer, pins, name=None, full=False, round=False, directions="pref"): """ Connects top/bot columns that are aligned. """ bins = {} for pin in pins: x = pin.cx() + if round: + x = round_to_grid(x) try: bins[x].append(pin) except KeyError: @@ -817,7 +821,8 @@ class layout(): self.add_via_stack_center(from_layer=pin.layer, to_layer=layer, offset=pin.center(), - min_area=True) + min_area=True, + directions=directions) if name: self.add_layout_pin_segment_center(text=name, @@ -1892,7 +1897,7 @@ class layout(): elif add_vias: self.copy_power_pin(pin, new_name=new_name) - def add_io_pin(self, instance, pin_name, new_name, start_layer=None): + def add_io_pin(self, instance, pin_name, new_name, start_layer=None, directions=None): """ Add a signle input or output pin up to metal 3. """ @@ -1902,7 +1907,7 @@ class layout(): start_layer = pin.layer # Just use the power pin function for now to save code - self.add_power_pin(new_name, pin.center(), start_layer=start_layer) + self.add_power_pin(new_name, pin.center(), start_layer=start_layer, directions=directions) def add_power_pin(self, name, loc, directions=None, start_layer="m1"): # Hack for min area diff --git a/compiler/modules/__init__.py b/compiler/modules/__init__.py index ae748da9..2f7d257a 100755 --- a/compiler/modules/__init__.py +++ b/compiler/modules/__init__.py @@ -72,6 +72,7 @@ from .replica_pbitcell import * from .row_cap_array import * from .row_cap_bitcell_1port import * from .row_cap_bitcell_2port import * +from .rom_bank import * from .sense_amp_array import * from .sense_amp import * from .tri_gate_array import * diff --git a/compiler/modules/col_cap_bitcell_1port.py b/compiler/modules/col_cap_bitcell_1port.py index 71261a0f..733a6a86 100644 --- a/compiler/modules/col_cap_bitcell_1port.py +++ b/compiler/modules/col_cap_bitcell_1port.py @@ -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 diff --git a/compiler/modules/col_cap_bitcell_2port.py b/compiler/modules/col_cap_bitcell_2port.py index 4fd1941c..c7ece7d8 100644 --- a/compiler/modules/col_cap_bitcell_2port.py +++ b/compiler/modules/col_cap_bitcell_2port.py @@ -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 diff --git a/compiler/modules/pbuf_dec.py b/compiler/modules/pbuf_dec.py index e42c159e..5e2877f3 100644 --- a/compiler/modules/pbuf_dec.py +++ b/compiler/modules/pbuf_dec.py @@ -15,7 +15,7 @@ class pbuf_dec(pgate): """ This is a simple buffer used for driving wordlines. """ - def __init__(self, name, size=4, height=None): + def __init__(self, name, size=4, height=None, add_wells=True): debug.info(1, "creating {0} with size of {1}".format(name, size)) self.add_comment("size: {}".format(size)) @@ -25,7 +25,7 @@ class pbuf_dec(pgate): self.height = height # Creates the netlist and layout - pgate.__init__(self, name, height) + pgate.__init__(self, name, height, add_wells) def create_netlist(self): self.add_pins() @@ -51,11 +51,13 @@ class pbuf_dec(pgate): input_size = max(1, int(self.size / self.stage_effort)) self.inv1 = factory.create(module_type="pinv_dec", size=input_size, - height=self.height) + height=self.height, + add_wells=self.add_wells) self.inv2 = factory.create(module_type="pinv_dec", size=self.size, - height=self.height) + height=self.height, + add_wells=self.add_wells) def create_insts(self): self.inv1_inst = self.add_inst(name="buf_inv1", diff --git a/compiler/modules/pinv_dec.py b/compiler/modules/pinv_dec.py index eda634f5..1ee75266 100644 --- a/compiler/modules/pinv_dec.py +++ b/compiler/modules/pinv_dec.py @@ -20,7 +20,7 @@ class pinv_dec(pinv): Other stuff is the same (netlist, sizes, etc.) """ - def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True): + def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True, flip_io=False): debug.info(2, "creating pinv_dec structure {0} with size of {1}".format(name, @@ -37,7 +37,7 @@ class pinv_dec(pinv): self.supply_layer = "m1" else: self.supply_layer = "m2" - + self.flip_io=flip_io super().__init__(name, size, beta, self.cell_height, add_wells) def determine_tx_mults(self): @@ -76,9 +76,14 @@ class pinv_dec(pinv): pmos_gate_pos = pmos_gate_pin.lc() self.add_path("poly", [nmos_gate_pos, pmos_gate_pos]) + # Center is completely symmetric. contact_width = self.poly_contact.width - contact_offset = nmos_gate_pin.lc() \ + if self.flip_io: + contact_offset = pmos_gate_pin.rc() \ + + vector(self.poly_extend_active + 0.6 * contact_width, 0) + else: + contact_offset = nmos_gate_pin.lc() \ - vector(self.poly_extend_active + 0.5 * contact_width, 0) via = self.add_via_stack_center(from_layer="poly", to_layer=self.route_layer, @@ -107,8 +112,11 @@ class pinv_dec(pinv): height=self.height - ll.y + 0.5 * self.pwell_contact.height + self.well_enclose_active) if "nwell" in layer: - ll = (self.pmos_inst.ll() - vector(2 * [self.well_enclose_active])).scale(1, 0) - ur = self.pmos_inst.ur() + vector(2 * [self.well_enclose_active]) + poly_offset = 0 + if self.flip_io: + poly_offset = 1.2 * self.poly_contact.width + ll = (self.pmos_inst.ll() - vector(2 * [self.well_enclose_active])).scale(1, 0) - vector(0, poly_offset) + ur = self.pmos_inst.ur() + vector(2 * [self.well_enclose_active + poly_offset]) self.add_rect(layer="nwell", offset=ll, width=ur.x - ll.x, diff --git a/compiler/modules/pinvbuf.py b/compiler/modules/pinvbuf.py index 2ac793c6..f05a2e5b 100644 --- a/compiler/modules/pinvbuf.py +++ b/compiler/modules/pinvbuf.py @@ -132,15 +132,16 @@ class pinvbuf(pgate): to_layer=a3_pin.layer, offset=a3_pin.center()) - # inv1 Z to inv4 A (up and over) z1_pin = self.inv1_inst.get_pin("Z") a4_pin = self.inv4_inst.get_pin("A") + + # inv1 Z to inv4 A (up and over) mid_point = vector(z1_pin.cx(), a4_pin.cy()) self.add_wire(route_stack, - [z1_pin.center(), mid_point, a4_pin.center()]) + [z1_pin.center(), mid_point, a4_pin.center()]) self.add_via_stack_center(from_layer=z1_pin.layer, - to_layer=route_stack[2], - offset=z1_pin.center()) + to_layer=route_stack[2], + offset=z1_pin.center()) def add_layout_pins(self): diff --git a/compiler/modules/ptx.py b/compiler/modules/ptx.py index bc36f064..250cd3f6 100644 --- a/compiler/modules/ptx.py +++ b/compiler/modules/ptx.py @@ -39,7 +39,8 @@ class ptx(design): connect_drain_active=False, connect_source_active=False, connect_poly=False, - num_contacts=None): + num_contacts=None, + ): if "li" in layer: self.route_layer = "li" @@ -47,11 +48,11 @@ class ptx(design): self.route_layer = "m1" # Default contacts are the lowest layer - if not add_source_contact: + if add_source_contact == None: add_source_contact = self.route_layer # Default contacts are the lowest layer - if not add_drain_contact: + if add_drain_contact == None: add_drain_contact = self.route_layer # We need to keep unique names because outputting to GDSII diff --git a/compiler/modules/rom_address_control_array.py b/compiler/modules/rom_address_control_array.py new file mode 100644 index 00000000..9f59ca71 --- /dev/null +++ b/compiler/modules/rom_address_control_array.py @@ -0,0 +1,121 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from openram.base import design +from openram.sram_factory import factory +from openram.base import vector +from openram.tech import layer, drc + + + +class rom_address_control_array(design): + """ + Takes the input address lines and creates the address and address bar lines for the decoder. + Adds control logic for the precharge cycle so that all address lines are high before the read cycle + """ + def __init__(self, cols, inv_height=None, inv_size=1, name="", route_layer="m1"): + self.size=inv_size + self.cols = cols + self.route_layer = route_layer + dff = factory.create(module_type="dff") + if name=="": + name = "rom_inv_array_{0}".format(cols) + if inv_height == None: + self.inv_height = dff.height * 0.5 + else: + self.inv_height = inv_height + + + if "li" in layer: + self.inv_layer = "li" + else: + self.inv_layer = "m1" + super().__init__(name) + self.create_netlist() + self.create_layout() + + def create_netlist(self): + self.create_modules() + self.add_pins() + self.create_instances() + + + def create_layout(self): + self.width = self.cols * self.addr_control.width + self.height = self.addr_control.height + self.setup_layout_constants() + self.place_instances() + self.route_clk() + self.route_sources() + self.copy_pins() + self.add_boundary() + + + def create_modules(self): + self.addr_control = factory.create(module_type="rom_address_control_buf", size=self.inv_height) + + + def add_pins(self): + for col in range(self.cols): + self.add_pin("A{0}_in".format(col), "INPUT") + for col in range(self.cols): + self.add_pin("A{0}_out".format(col), "OUTPUT") + for col in range(self.cols): + self.add_pin("Abar{0}_out".format(col), "OUTPUT") + self.add_pin("clk", "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_instances(self): + + self.buf_insts = [] + + for col in range(self.cols): + + name = "Xaddr_buf_{0}".format(col) + addr_buf = self.add_inst(name=name, mod=self.addr_control) + + A_in = "A{0}_in".format(col) + Aout = "A{0}_out".format(col) + Abar_out = "Abar{0}_out".format(col) + self.connect_inst([A_in, Aout, Abar_out, "clk", "vdd", "gnd"]) + + self.buf_insts.append(addr_buf) + + def setup_layout_constants(self): + self.route_width = drc["minwidth_{}".format(self.route_layer)] + + def place_instances(self): + for col in range(self.cols): + base = vector((col+1)*(self.addr_control.width), 0) + + self.buf_insts[col].place(offset=base, mirror="MY") + + def copy_pins(self): + for i in range(self.cols): + self.copy_layout_pin(self.buf_insts[i], "A_out", "A{0}_out".format(i)) + self.copy_layout_pin(self.buf_insts[i], "Abar_out", "Abar{0}_out".format(i)) + self.copy_layout_pin(self.buf_insts[i], "A_in", "A{0}_in".format(i)) + + def route_clk(self): + self.route_horizontal_pins("clk", insts=self.buf_insts, layer=self.route_layer) + + def route_sources(self): + + self.route_horizontal_pins("vdd", insts=self.buf_insts, layer=self.route_layer) + self.route_horizontal_pins("gnd", insts=self.buf_insts, layer=self.route_layer) + + tmp_pins = [] + for pin in self.get_pins("vdd"): + edge = vector(pin.lx() + 0.5 * self.route_width, pin.cy()) + tmp_pins.append(self.add_layout_pin_rect_center("vdd_edge", layer=self.route_layer, offset=edge)) + self.copy_layout_pin_shapes("vdd") + self.remove_layout_pin("vdd") + + for pin in tmp_pins: + self.copy_layout_pin(self, "vdd_edge", "vdd") + self.remove_layout_pin("vdd_edge") \ No newline at end of file diff --git a/compiler/modules/rom_address_control_buf.py b/compiler/modules/rom_address_control_buf.py new file mode 100644 index 00000000..9e092ff0 --- /dev/null +++ b/compiler/modules/rom_address_control_buf.py @@ -0,0 +1,185 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +from openram.base import design +from openram.sram_factory import factory +from openram.base import vector +from openram.tech import layer, drc + + + +class rom_address_control_buf(design): + """ + Takes the input address lines and creates the address and address bar lines for the decoder. + Adds control logic for the precharge cycle so that all address lines are high before the read cycle + """ + def __init__(self, size, name="", route_layer="m1", add_wells=False): + + self.route_layer = route_layer + self.add_wells = add_wells + self.size = size + if "li" in layer: + self.inv_layer = "li" + else: + self.inv_layer = "m1" + super().__init__(name) + self.create_netlist() + self.create_layout() + + def create_netlist(self): + self.create_modules() + self.add_pins() + self.create_instances() + + def create_layout(self): + self.width = self.cell.height * 2 + self.height = self.inv.width + 2 * self.nand.width + self.setup_layout_constants() + self.place_instances() + self.route_gates() + self.route_sources() + self.add_boundary() + + def create_modules(self): + + self.inv = factory.create(module_type="pinv_dec", module_name="inv_array_mod", add_wells=False, size=self.size) + self.nand = factory.create(module_type="nand2_dec", height=self.inv.height) + # For layout constants + self.cell = factory.create(module_type="rom_base_cell") + + def add_pins(self): + + self.add_pin("A_in", "INPUT") + self.add_pin("A_out", "INOUT") + self.add_pin("Abar_out", "OUTPUT") + self.add_pin("clk", "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_instances(self): + + name = "XinvAbar" + + self.inv_inst = self.add_inst(name=name, mod=self.inv) + inst_A = "A_in" + inst_Z = "Abar_internal" + self.connect_inst([inst_A, inst_Z, "vdd", "gnd"]) + + name = "Xnand_addr" + + self.addr_nand = self.add_inst(name=name, mod=self.nand) + inst_A = "clk" + inst_B = "Abar_internal" + inst_Z = "A_out" + self.connect_inst([inst_A, inst_B, inst_Z, "vdd", "gnd"]) + + name = "Xnand_addr_bar" + + self.addr_bar_nand = self.add_inst(name=name, mod=self.nand) + inst_A = "clk" + inst_B = "A_out" + inst_Z = "Abar_out" + self.connect_inst([inst_A, inst_B, inst_Z, "vdd", "gnd"]) + + def setup_layout_constants(self): + self.route_width = drc["minwidth_{}".format(self.route_layer)] + self.interconnect_width = drc["minwidth_{}".format(self.inv_layer)] + + def place_instances(self): + self.inv_inst.place(offset=vector(self.inv_inst.height,0), rotate=90) + self.addr_nand.place(offset=vector(self.addr_nand.height , self.inv_inst.width + self.route_width ), rotate=90) + self.addr_bar_nand.place(offset=vector( self.addr_bar_nand.height, self.addr_nand.width + self.inv_inst.width + self.route_width), rotate=90) + + def route_gates(self): + clk1_pin = self.addr_nand.get_pin("A") + clk2_pin = self.addr_bar_nand.get_pin("A") + + Abar_out = self.addr_bar_nand.get_pin("Z") + A_out = self.addr_nand.get_pin("Z") + + Abar_in = self.addr_nand.get_pin("B") + Abar_int_out = self.inv_inst.get_pin("Z") + + Aint_in = self.addr_bar_nand.get_pin("B") + A_in = self.inv_inst.get_pin("A") + + + # Find the center of the pmos poly/gate + poly_right = clk1_pin.cx() + self.poly_enclose_contact + 0.5 * self.contact_width + + ppoly_center = poly_right - 0.7 * self.poly_width + + contact_offset = vector(ppoly_center, clk2_pin.cy()) + + # Route the two shared clk inputs together by connecting poly + self.add_segment_center("poly", contact_offset, vector(ppoly_center, A_out.cy())) + + + clk_offset = vector(clk2_pin.cx(), self.addr_nand.uy()) + self.add_layout_pin_rect_center("clk", offset=clk_offset, layer=self.route_layer) + + self.add_via_stack_center(from_layer=self.inv_layer, to_layer=self.route_layer, offset=self.addr_bar_nand.get_pin("A").center()) + self.add_segment_center(self.route_layer, clk_offset, vector(clk_offset.x, clk2_pin.cy())) + + # Route first NAND output to second NAND input + start = A_out.center() + end = Aint_in.center() + self.add_path("m2", [start, end]) + self.add_via_stack_center(Aint_in.center(), self.inv_layer, "m2") + self.add_via_stack_center(A_out.center(), self.inv_layer, "m2") + + # Route first NAND to output pin + self.add_segment_center("m2", end, vector(end.x, self.addr_bar_nand.uy())) + self.add_layout_pin_rect_center("A_out", offset=vector(end.x, self.addr_bar_nand.uy() - 0.5 * self.m2_width), layer="m2") + + # Route second NAND to output pin + self.add_via_stack_center(Abar_out.center(), self.inv_layer, "m2") + self.add_segment_center("m2", Abar_out.center(), vector(Abar_out.cx(), self.addr_bar_nand.uy())) + self.add_layout_pin_rect_center("Abar_out", offset=vector(Abar_out.cx(), self.addr_bar_nand.uy() - 0.5 * self.m2_width), layer="m2") + + # Route inverter output to NAND + end = vector(Abar_int_out.cx(), Abar_in.cy() + 0.5 * self.interconnect_width) + self.add_segment_center(self.inv_layer, Abar_int_out.center(), end) + self.copy_layout_pin(self.inv_inst, "A", "A_in") + + def route_sources(self): + + self.copy_layout_pin(self.addr_nand, "vdd") + self.copy_layout_pin(self.addr_bar_nand, "vdd") + self.copy_layout_pin(self.inv_inst, "vdd") + + self.copy_layout_pin(self.addr_bar_nand, "gnd") + self.copy_layout_pin(self.addr_nand, "gnd") + self.copy_layout_pin(self.inv_inst, "gnd") + + + """ Add n/p well taps to the layout and connect to supplies """ + + source_pin = self.inv_inst.get_pin("vdd") + gnd_pin = self.inv_inst.get_pin("gnd") + + left_edge = self.inv_inst.get_pin("Z").cx() - 2 * self.contact_width - 2 * self.active_contact_to_gate - 4 * self.active_enclose_contact - self.poly_width - self.active_space + + contact_pos = vector(left_edge, source_pin.cy()) + + self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="n", + well_type="n") + self.add_via_stack_center(offset=contact_pos, + from_layer=self.active_stack[2], + to_layer=self.route_layer) + + contact_pos = vector(left_edge, gnd_pin.cy()) + self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="p", + well_type="p") + self.add_via_stack_center(offset=contact_pos, + from_layer=self.active_stack[2], + to_layer=self.route_layer) \ No newline at end of file diff --git a/compiler/modules/rom_bank.py b/compiler/modules/rom_bank.py new file mode 100644 index 00000000..f4940338 --- /dev/null +++ b/compiler/modules/rom_bank.py @@ -0,0 +1,512 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +import datetime +from math import ceil, log +from openram.base import vector +from openram.base import design +from openram import OPTS, print_time +from openram.sram_factory import factory +from openram.tech import drc, layer, parameter +from openram.router import router_tech + + +class rom_bank(design): + + """ + Rom data bank with row and column decoder + control logic + + word size is in bytes + """ + + def __init__(self, name, rom_config): + super().__init__(name=name) + self.rom_config = rom_config + rom_config.set_local_config(self) + + self.word_size = self.word_bits + self.num_outputs = self.rows + self.num_inputs = ceil(log(self.rows, 2)) + self.col_bits = ceil(log(self.words_per_row, 2)) + self.row_bits = self.num_inputs + + self.tap_spacing = self.strap_spacing + + try: + from openram.tech import power_grid + self.supply_stack = power_grid + except ImportError: + # if no power_grid is specified by tech we use sensible defaults + # Route a M3/M4 grid + self.supply_stack = self.m3_stack + + self.interconnect_layer = "m1" + self.bitline_layer = "m1" + self.wordline_layer = "m2" + + if "li" in layer: + self.route_stack = self.m1_stack + else: + self.route_stack = self.m2_stack + self.route_layer = self.route_stack[0] + + if OPTS.is_unit_test: + self.create_netlist() + self.create_layout() + + + def create_netlist(self): + start_time = datetime.datetime.now() + self.add_modules() + self.add_pins() + self.create_instances() + if not OPTS.is_unit_test: + print_time("Submodules", datetime.datetime.now(), start_time) + + def create_layout(self): + + start_time = datetime.datetime.now() + + self.setup_layout_constants() + self.place_instances() + if not OPTS.is_unit_test: + print_time("Placement", datetime.datetime.now(), start_time) + + self.add_boundary() + + start_time = datetime.datetime.now() + self.route_layout() + if not OPTS.is_unit_test: + print_time("Routing", datetime.datetime.now(), start_time) + + start_time = datetime.datetime.now() + if not OPTS.is_unit_test: + # We only enable final verification if we have routed the design + # Only run this if not a unit test, because unit test will also verify it. + self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=OPTS.check_lvsdrc) + print_time("Verification", datetime.datetime.now(), start_time) + + def add_boundary(self): + + ll = self.find_lowest_coords() + m1_offset = self.m1_width + self.translate_all(vector(0, ll.y)) + ur = self.find_highest_coords() + ur = vector(ur.x, ur.y) + super().add_boundary(vector(0, 0), ur) + self.width = ur.x + self.height = ur.y + + def route_layout(self): + self.route_decode_outputs() + self.route_precharge() + self.route_clock() + self.route_array_outputs() + self.place_top_level_pins() + self.route_output_buffers() + + rt = router_tech(self.supply_stack, 1) + init_bbox = self.get_bbox(side="ring", + margin=rt.track_width) + self.route_supplies(init_bbox) + # We need the initial bbox for the supply rings later + # because the perimeter pins will change the bbox + # Route the pins to the perimeter + if OPTS.perimeter_pins: + # We now route the escape routes far enough out so that they will + # reach past the power ring or stripes on the sides + bbox = self.get_bbox(side="ring", + margin=11*rt.track_width) + self.route_escape_pins(bbox) + + + + + def setup_layout_constants(self): + self.route_layer_width = drc["minwidth_{}".format(self.route_stack[0])] + self.route_layer_pitch = drc["{0}_to_{0}".format(self.route_stack[0])] + + self.interconnect_layer_width = drc["minwidth_{}".format(self.interconnect_layer)] + self.interconnect_layer_pitch = drc["{0}_to_{0}".format(self.interconnect_layer)] + + def add_pins(self): + + self.add_pin("clk", "INPUT") + self.add_pin("cs", "INPUT") + + for i in range(self.row_bits + self.col_bits): + self.add_pin("addr[{}]".format(i), "INPUT") + + out_pins = [] + for j in range(self.word_size): + out_pins.append("dout[{}]".format(j)) + self.add_pin_list(out_pins, "OUTPUT") + + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + + def add_modules(self): + + # TODO: provide technology-specific calculation of these parameters + # in sky130 the address control buffer is composed of 2 size 2 NAND gates, + # with a beta of 3, each of these gates has gate capacitance of 2 min sized inverters, therefor a load of 4 + + + addr_control_buffer_effort = parameter['beta'] + 1 + # a single min sized nmos makes up 1/4 of the input capacitance of a min sized inverter + bitcell_effort = 0.25 + + # Takes into account inverter sizing + wordline_effort = bitcell_effort * 0.5 + + # a single min sized pmos plus a single min sized nmos have approximately half the gate capacitance of a min inverter + # an additional 0.2 accounts for the long wire capacitance and add delay to gaurentee the read timing + precharge_cell_effort = 0.5 + 0.2 + + self.array = factory.create(module_type="rom_base_array", + cols=self.cols, + rows=self.rows, + strap_spacing=self.strap_spacing, + bitmap=self.data, + bitline_layer=self.bitline_layer, + wordline_layer=self.wordline_layer, + pitch_match=True, + tap_spacing=self.tap_spacing) + + + self.decode_array = factory.create(module_name="rom_row_decode", + module_type="rom_decoder", + num_outputs=self.rows, + strap_spacing=self.strap_spacing, + route_layer=self.route_layer, + fanout=(self.cols)*wordline_effort ) + + + self.column_mux = factory.create(module_type="rom_column_mux_array", + columns=self.cols, + word_size=self.word_size, + tap_spacing=self.strap_spacing, + bitline_layer=self.interconnect_layer, + input_layer=self.bitline_layer) + + self.column_decode = factory.create(module_name="rom_column_decode", + module_type="rom_decoder", + num_outputs=self.words_per_row, + strap_spacing=self.strap_spacing, + route_layer=self.route_layer, + fanout=2, + invert_outputs=True ) + + self.control_logic = factory.create(module_type="rom_control_logic", + num_outputs=(self.cols + self.words_per_row * precharge_cell_effort) \ + + (addr_control_buffer_effort * self.col_bits), + clk_fanout=(self.row_bits * addr_control_buffer_effort) + (precharge_cell_effort * self.rows), + height=self.column_decode.height ) + + self.bitline_inv = factory.create(module_type="rom_wordline_driver_array", + module_name="rom_bitline_inverter", + rows=self.cols, + fanout=4, + invert_outputs=True, + tap_spacing=0, + flip_io=True) + self.output_inv = factory.create(module_type="rom_wordline_driver_array", + module_name="rom_output_buffer", + rows=self.word_size, + fanout=4, + tap_spacing=1, + invert_outputs=True) + + + def create_instances(self): + gnd = ["gnd"] + vdd = ["vdd"] + prechrg = ["precharge"] + clk = ["clk_int"] + + bitlines = ["bl_{}".format(bl) for bl in range(self.cols)] + wordlines = ["wl_{}".format(wl) for wl in range(self.rows)] + + addr_msb = ["addr[{}]".format(addr + self.col_bits) for addr in range(self.row_bits)] + addr_lsb = ["addr[{}]".format(addr) for addr in range(self.col_bits)] + + select_lines = ["word_sel_{}".format(word) for word in range(self.words_per_row)] + + bitline_bar = ["bl_b_{}".format(bl) for bl in range(self.cols)] + pre_buf_outputs = ["rom_out_prebuf_{}".format(bit) for bit in range(self.word_size)] + outputs = ["dout[{}]".format(bl) for bl in range(self.word_size)] + + + array_pins = bitlines + wordlines + prechrg + vdd + gnd + + row_decode_pins = addr_msb + wordlines + clk + clk + vdd + gnd + col_decode_pins = addr_lsb + select_lines + prechrg + prechrg + vdd + gnd + + col_mux_pins = bitline_bar + select_lines + pre_buf_outputs + gnd + + bitline_inv_pins = bitlines + bitline_bar + vdd + gnd + + output_buf_pins = pre_buf_outputs + outputs + vdd + gnd + + self.array_inst = self.add_inst(name="rom_bit_array", mod=self.array) + self.connect_inst(array_pins) + + self.decode_inst = self.add_inst(name="rom_row_decoder", mod=self.decode_array) + self.connect_inst(row_decode_pins) + + self.control_inst = self.add_inst(name="rom_control", mod=self.control_logic) + self.connect_inst(["clk", "cs", "precharge", "clk_int", "vdd", "gnd"]) + + self.mux_inst = self.add_inst(name="rom_column_mux", mod=self.column_mux) + self.connect_inst(col_mux_pins) + + self.col_decode_inst = self.add_inst(name="rom_column_decoder", mod=self.column_decode) + self.connect_inst(col_decode_pins) + + self.bitline_inv_inst = self.add_inst(name="rom_bitline_inverter", mod=self.bitline_inv) + self.connect_inst(bitline_inv_pins) + + self.output_inv_inst = self.add_inst(name="rom_output_inverter", mod=self.output_inv) + self.connect_inst(output_buf_pins) + + def place_instances(self): + self.place_row_decoder() + self.place_data_array() + self.place_bitline_inverter() + self.place_col_mux() + self.place_col_decoder() + self.place_control_logic() + self.place_output_buffer() + + + def place_row_decoder(self): + self.decode_offset = vector(0, self.control_inst.height ) + self.decode_inst.place(offset=self.decode_offset) + + def place_data_array(self): + # We approximate the correct position for the array + array_x = self.decode_inst.width + (2) * ( self.route_layer_width + self.route_layer_pitch ) + array_y = self.decode_array.buf_inst.height - self.array.precharge_inst.cy() - self.array.zero_cell.height * 0.5 + self.array_offset = vector(array_x ,array_y) + self.array_inst.place(offset=self.array_offset) + + # now move array to correct alignment with decoder + array_align = self.decode_inst.get_pin("wl_0").cy() - self.array_inst.get_pin("wl_0_0").cy() + self.array_inst.place(offset=(self.array_offset + vector(0, array_align))) + + def place_bitline_inverter(self): + self.bitline_inv_inst.place(offset=[0,0], rotate=90) + inv_y_offset = self.array_inst.by() - self.bitline_inv_inst.width - 2 * self.m1_pitch + + inv_x_offset = self.array_inst.get_pin("bl_0_0").cx() - self.bitline_inv_inst.get_pin("out_0").cx() + self.inv_offset = vector(inv_x_offset, inv_y_offset) + self.bitline_inv_inst.place(offset=self.inv_offset, rotate=90) + + def place_control_logic(self): + + self.control_offset = vector(self.col_decode_inst.lx() - self.control_inst.width - 3 * self.m1_pitch, self.decode_inst.by() - self.control_logic.height - self.m1_pitch) + self.control_inst.place(offset=self.control_offset) + + def place_col_decoder(self): + col_decode_y = self.mux_inst.get_pin("sel_0").cy() - self.col_decode_inst.get_pin("wl_0").cy() + self.col_decode_offset = vector(self.decode_inst.width - self.col_decode_inst.width, col_decode_y) + self.col_decode_inst.place(offset=self.col_decode_offset) + + def place_col_mux(self): + mux_y_offset = self.bitline_inv_inst.by() - self.mux_inst.height - 5 * self.route_layer_pitch + + mux_x_offset = self.bitline_inv_inst.get_pin("out_0").cx() - self.mux_inst.get_pin("bl_0").cx() + self.mux_offset = vector(mux_x_offset, mux_y_offset) + self.mux_inst.place(offset=self.mux_offset) + + def place_output_buffer(self): + output_x = self.col_decode_inst.rx() + self.output_inv_inst.height + output_y = self.mux_inst.by() - self.word_size * self.m1_pitch + self.output_inv_offset = vector(output_x, output_y) + self.output_inv_inst.place(offset=self.output_inv_offset, rotate=270) + + def route_decode_outputs(self): + # for the row decoder + route_pins = [self.array_inst.get_pin("wl_0_{}".format(wl)) for wl in range(self.rows)] + decode_pins = [self.decode_inst.get_pin("wl_{}".format(wl)) for wl in range(self.rows)] + route_pins.extend(decode_pins) + self.connect_row_pins(self.interconnect_layer, route_pins, round=True) + + + # then for the column decoder + col_decode_pins = [self.col_decode_inst.get_pin("wl_{}".format(wl)) for wl in range(self.words_per_row)] + sel_pins = [self.mux_inst.get_pin("sel_{}".format(wl)) for wl in range(self.words_per_row)] + sel_pins.extend(col_decode_pins) + self.connect_row_pins(self.wordline_layer, sel_pins, round=True) + + def route_array_inputs(self): + + for wl in range(self.rows): + array_wl = self.array.wordline_names[0][wl] + array_wl_pin = self.array_inst.get_pin(array_wl) + + wl_bus_wire = self.wl_bus[self.wl_interconnects[wl]] + + end = array_wl_pin.center() + start = vector(wl_bus_wire.cx(), end.y) + + self.add_segment_center(self.interconnect_layer, start, end) + + + def route_precharge(self): + + prechrg_control = self.control_inst.get_pin("prechrg") + + col_decode_prechrg = self.col_decode_inst.get_pin("precharge_r") + col_decode_clk = self.col_decode_inst.get_pin("clk") + array_prechrg = self.array_inst.get_pin("precharge") + + self.add_via_stack_center(from_layer=self.route_stack[0], + to_layer=prechrg_control.layer, + offset=prechrg_control.center()) + + # Route precharge to col decoder + start = prechrg_control.center() + mid1 = vector(self.control_inst.rx() + self.interconnect_layer_pitch, prechrg_control.cy()) + mid2 = vector(self.control_inst.rx() + self.interconnect_layer_pitch, col_decode_prechrg.cy()) + end = col_decode_prechrg.center() + self.add_path(self.route_stack[0], [start, mid1, mid2, end]) + + self.add_via_stack_center(from_layer=self.route_stack[0], + to_layer=col_decode_prechrg.layer, + offset=end) + + start = mid1 + mid1 = vector(self.control_inst.rx() + self.interconnect_layer_pitch, start.y) + mid2 = vector(mid1.x, col_decode_clk.cy()) + end = col_decode_clk.center() + self.add_path(self.route_stack[0], [start, mid1, mid2, end]) + + + # Route precharge to main array + mid = vector(col_decode_prechrg.cx(), array_prechrg.cy() ) + self.add_path(self.route_stack[0], [array_prechrg.center(), mid, col_decode_prechrg.center()]) + + + def route_clock(self): + clk_out = self.control_inst.get_pin("clk_out") + row_decode_clk = self.decode_inst.get_pin("clk") + + self.add_via_stack_center(from_layer=self.route_stack[2], + to_layer=clk_out.layer, + offset=clk_out.center()) + + # Route clock to row decoder + mid = vector(self.control_inst.rx() + self.m1_pitch, clk_out.cy()) + + addr_control_clk = row_decode_clk.rc() + vector( 2 * self.route_layer_pitch + self.route_layer_width, 0) + row_decode_prechrg = self.decode_inst.get_pin("precharge") + + self.add_path(self.route_stack[2], [clk_out.center(), mid, addr_control_clk, row_decode_prechrg.center()]) + + self.add_via_stack_center(from_layer=self.route_stack[2], + to_layer=row_decode_clk.layer, + offset=addr_control_clk) + + self.add_segment_center(row_decode_clk.layer, addr_control_clk, row_decode_clk.rc()) + + def route_array_outputs(self): + array_out_pins = [self.array_inst.get_pin("bl_0_{}".format(bl)) for bl in range(self.cols)] + inv_in_pins = [self.bitline_inv_inst.get_pin("in_{}".format(bl)) for bl in range(self.cols)] + inv_out_pins = [self.bitline_inv_inst.get_pin("out_{}".format(bl)) for bl in range(self.cols)] + mux_pins = [self.mux_inst.get_pin("bl_{}".format(bl)) for bl in range(self.cols)] + + self.connect_col_pins(self.interconnect_layer, array_out_pins + inv_in_pins, round=True, directions="nonpref") + self.connect_col_pins(self.interconnect_layer, inv_out_pins + mux_pins, round=True, directions="nonpref") + + def route_output_buffers(self): + mux = self.mux_inst + buf = self.output_inv_inst + route_nets = [ [mux.get_pin("bl_out_{}".format(bit)), buf.get_pin("in_{}".format(bit))] for bit in range(self.word_size)] + + channel_ll = vector( route_nets[0][0].cx(), route_nets[0][1].cy() + self.m1_pitch * 3) + self.create_horizontal_channel_route(netlist=route_nets, offset=channel_ll, layer_stack=self.m1_stack) + + def place_top_level_pins(self): + self.add_io_pin(self.control_inst, "CS", "cs") + self.add_io_pin(self.control_inst, "clk_in", "clk") + + for i in range(self.word_size): + self.add_io_pin(self.output_inv_inst, "out_{}".format(i), "dout[{}]".format(i), directions="nonpref") + + for lsb in range(self.col_bits): + name = "addr[{}]".format(lsb) + self.add_io_pin(self.col_decode_inst, "A{}".format(lsb), name) + + for msb in range(self.col_bits, self.row_bits + self.col_bits): + name = "addr[{}]".format(msb) + pin_num = msb - self.col_bits + self.add_io_pin(self.decode_inst, "A{}".format(pin_num), name) + + def route_supplies(self, bbox=None): + + for pin_name in ["vdd", "gnd"]: + for inst in self.insts: + self.copy_power_pins(inst, pin_name) + + if not OPTS.route_supplies: + # Do not route the power supply (leave as must-connect pins) + return + elif OPTS.route_supplies == "grid": + from openram.router import supply_grid_router as router + else: + from openram.router import supply_tree_router as router + rtr=router(layers=self.supply_stack, + design=self, + bbox=bbox, + pin_type=OPTS.supply_pin_type) + + rtr.route() + + if OPTS.supply_pin_type in ["left", "right", "top", "bottom", "ring"]: + # Find the lowest leftest pin for vdd and gnd + for pin_name in ["vdd", "gnd"]: + # Copy the pin shape(s) to rectangles + for pin in self.get_pins(pin_name): + self.add_rect(layer=pin.layer, + offset=pin.ll(), + width=pin.width(), + height=pin.height()) + + # Remove the pin shape(s) + self.remove_layout_pin(pin_name) + + # Get new pins + pins = rtr.get_new_pins(pin_name) + for pin in pins: + self.add_layout_pin(pin_name, + pin.layer, + pin.ll(), + pin.width(), + pin.height()) + + def route_escape_pins(self, bbox): + pins_to_route = [] + + for bit in range(self.col_bits): + pins_to_route.append("addr[{0}]".format(bit)) + + for bit in range(self.row_bits): + pins_to_route.append("addr[{0}]".format(bit + self.col_bits)) + + for bit in range(self.word_size): + pins_to_route.append("dout[{0}]".format(bit)) + + pins_to_route.append("clk") + pins_to_route.append("cs") + from openram.router import signal_escape_router as router + rtr=router(layers=self.m3_stack, + design=self, + bbox=bbox) + rtr.escape_route(pins_to_route) \ No newline at end of file diff --git a/compiler/modules/rom_base_array.py b/compiler/modules/rom_base_array.py new file mode 100644 index 00000000..9c380624 --- /dev/null +++ b/compiler/modules/rom_base_array.py @@ -0,0 +1,377 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + + +import math +from .bitcell_base_array import bitcell_base_array +from openram.base import vector +from openram import OPTS, debug +from openram.sram_factory import factory +from openram.tech import drc, layer + +class rom_base_array(bitcell_base_array): + + def __init__(self, rows, cols, strap_spacing, bitmap, tap_spacing = 4, name="", bitline_layer="m1", wordline_layer="m2", tap_direction="row", pitch_match=False): + + super().__init__(name=name, rows=rows, cols=cols, column_offset=0) + + self.data = bitmap + self.tap_direction = tap_direction + self.pitch_match = pitch_match + self.bitline_layer = bitline_layer + self.strap_spacing = strap_spacing + self.wordline_layer = wordline_layer + self.data_col_size = self.column_size + self.tap_spacing = tap_spacing + + if strap_spacing != 0: + self.array_col_size = self.column_size + math.ceil(self.column_size / strap_spacing) + else: + self.array_col_size = self.column_size + self.create_all_bitline_names() + self.create_all_wordline_names() + # debug.info(1, "ROM array with rows: {0}, cols: {1}".format(self.row_size, self.column_size)) + self.create_netlist() + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + + self.create_cell_instances() + self.create_precharge_inst() + + def create_layout(self): + self.create_layout_constants() + self.place_array() + if self.tap_direction == "row": + self.route_pitch_offsets() + + self.place_precharge() + self.place_wordline_contacts() + self.place_bitline_contacts() + self.route_precharge() + self.add_boundary() + + self.route_supplies() + self.connect_taps() + + def add_boundary(self): + ll = self.find_lowest_coords() + m1_offset = self.m1_width + self.translate_all(vector(0, ll.y + 0.5 * m1_offset)) + ur = self.find_highest_coords() + ur = vector(ur.x, ur.y - self.m1_width) + super().add_boundary(vector(0, 0), ur) + self.width = ur.x + self.height = ur.y + + def add_modules(self): + + self.zero_cell = factory.create(module_name="rom_base_zero_cell", + module_type="rom_base_cell", + bitline_layer=self.bitline_layer, + bit_value=0) + + self.one_cell = factory.create(module_name="rom_base_one_cell", + module_type="rom_base_cell", + bitline_layer=self.bitline_layer, + bit_value=1) + + if self.tap_direction == "row": + self.poly_tap = factory.create(module_type="rom_poly_tap") + else: + self.poly_tap = factory.create(module_type="rom_poly_tap", add_active_tap=True) + self.end_poly_tap = factory.create(module_type="rom_poly_tap", place_poly=True) + self.precharge_array = factory.create(module_type="rom_precharge_array", + cols=self.column_size, + strap_spacing=self.strap_spacing, + bitline_layer=self.bitline_layer, + strap_layer=self.wordline_layer, + tap_direction=self.tap_direction) + + def create_layout_constants(self): + self.route_width = drc("minwidth_" + self.bitline_layer) + self.route_pitch = drc("{0}_to_{0}".format(self.bitline_layer)) + + def add_pins(self): + for bl_name in self.get_bitline_names(): + self.add_pin(bl_name, "OUTPUT") + for wl_name in self.get_wordline_names(): + self.add_pin(wl_name, "INPUT") + self.add_pin("precharge", "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_cell_instances(self): + self.tap_inst = {} + self.active_tap_list = [] + self.poly_tap_list = [] + self.cell_inst = {} + self.cell_list = [] + self.current_row = 0 + # list of current bitline interconnect nets, + # starts as the same as the bitline list and is updated when new insts of cells are added + self.int_bl_list = self.bitline_names[0].copy() + + for row in range(self.row_size + 1): + row_list = [] + + for col in range(self.column_size): + + if col % self.strap_spacing == 0: + self.create_poly_tap(row, col) + + new_inst = self.create_cell(row, col) + self.cell_inst[row, col] = new_inst + row_list.append(new_inst) + + self.create_poly_tap(row, self.column_size) + # name = "tap_r{0}_c{1}".format(row, self.array_col_size) + # new_tap = self.add_inst(name=name, mod=self.poly_tap) + # self.tap_inst[row, self.column_size] = new_tap + # self.tap_list.append(new_tap) + # self.connect_inst([]) + + self.cell_list.append(row_list) + + def create_poly_tap(self, row, col): + name = "tap_r{0}_c{1}".format(row, col) + if row == self.row_size and self.tap_direction == "col": + new_tap = self.add_inst(name=name, mod=self.end_poly_tap) + else: + new_tap = self.add_inst(name=name, mod=self.poly_tap) + self.active_tap_list.append(new_tap) + + self.tap_inst[row, col]=new_tap + self.poly_tap_list.append(new_tap) + self.connect_inst([]) + + def create_cell(self, row, col): + name = "bit_r{0}_c{1}".format(row, col) + + # when col = 0, bl_h is connected to precharge, otherwise connect to previous bl connection + # when col = col_size - 1 connected column_sizeto gnd otherwise create new bl connection + # debug.info(1, "Create cell: r{0}, c{1}".format(row, col)) + if row == self.row_size: + + bl_l = self.int_bl_list[col] + bl_h = "gnd" + else: + bl_l = self.int_bl_list[col] + + if self.data[row][col] == 1: + self.int_bl_list[col] = "bl_int_{0}_{1}".format(row, col) + bl_h = self.int_bl_list[col] + # Final row of dummy nmos that contains only 1s, acts to prevent shorting bl to ground when precharging + + if row == self.row_size: + new_inst = self.add_inst(name=name, mod=self.one_cell) + self.connect_inst([bl_h, bl_l, "precharge", "gnd"]) + elif self.data[row][col] == 1: + new_inst = self.add_inst(name=name, mod=self.one_cell) + self.connect_inst([bl_h, bl_l, self.wordline_names[0][row], "gnd"]) + else: + new_inst = self.add_inst(name=name, mod=self.zero_cell) + self.connect_inst([bl_h, self.wordline_names[0][row], "gnd"]) + + return new_inst + + def create_precharge_inst(self): + prechrg_pins = self.bitline_names[0].copy() + + prechrg_pins.append("precharge") + prechrg_pins.append("vdd") + self.precharge_inst = self.add_inst(name="bitcell_array_precharge", mod=self.precharge_array) + self.connect_inst(prechrg_pins) + + def create_all_bitline_names(self): + for col in range(self.column_size): + for port in self.all_ports: + self.bitline_names[port].extend(["bl_{0}_{1}".format(port, col)]) + # Make a flat list too + self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl] + + def route_supplies(self): + via_width = drc("m2_enclose_via1") * 0.5 + drc("minwidth_via1") + pitch = drc["{0}_to_{0}".format(self.wordline_layer)] + drain_l = self.cell_list[self.row_size][0].get_pin("D") + drain_r = self.cell_list[self.row_size][self.column_size - 1].get_pin("D") + gnd_l = drain_l.center() + vector(-0.5 * self.route_width, pitch + via_width + self.route_pitch) + gnd_r = drain_r.center() + vector(0.5 * self.route_width, pitch + via_width + self.route_pitch) + self.add_layout_pin_rect_ends(name="gnd", layer=self.bitline_layer, start=gnd_l, end=gnd_r) + + + if self.tap_direction == "row": + self.connect_row_pins(layer=self.wordline_layer, pins=self.gnd_taps, name="gnd") + self.remove_layout_pin("gnd_tap") + + if self.tap_direction == "col": + self.remove_layout_pin("gnd") + + active_tap_pins = [self.active_tap_list[i].get_pin("active_tap") for i in range(len(self.active_tap_list))] + self.connect_col_pins(layer=self.supply_stack[0], pins=active_tap_pins, name="gnd_tmp") + + gnd_y = gnd_l.y + min_x = float('inf') + max_x = 0 + for pin in self.get_pins("gnd_tmp"): + + # find the pins on the edges + if pin.cx() < min_x: + min_x = pin.cx() + if pin.cx() > max_x: + max_x = pin.cx() + + bottom = vector(pin.cx(), pin.by()) + top = vector(pin.cx(), gnd_y) + self.add_via_stack_center(offset=top, from_layer=self.bitline_layer, to_layer=self.supply_stack[0]) + self.add_via_center(offset=bottom, layers=self.supply_stack) + + self.add_layout_pin_rect_ends(name="gnd", layer=self.supply_stack[0], start=bottom, end=top) + + self.remove_layout_pin("gnd_tmp") + self.add_segment_center(layer=self.supply_stack[2], start=vector(min_x, bottom.y), end=vector(max_x, bottom.y)) + self.add_segment_center(layer=self.bitline_layer, start=gnd_l, end=vector(min_x, gnd_l.y)) + self.add_segment_center(layer=self.bitline_layer, start=gnd_r, end=vector(max_x, gnd_r.y)) + + self.copy_layout_pin(self.precharge_inst, "vdd") + + def place_array(self): + self.cell_pos = {} + self.strap_pos = {} + pitch_offset = 0 + + for row in range(self.row_size + 1): + + if row % self.tap_spacing == 0 and self.pitch_match and row != self.row_size: + pitch_offset += self.active_contact.width + self.active_space + + cell_y = row * (self.zero_cell.height) + pitch_offset + + cell_x = 0 + for col in range(self.column_size): + + if col % self.strap_spacing == 0: + self.strap_pos[row, col] = vector(cell_x, cell_y) + self.tap_inst[row, col].place(self.strap_pos[row, col]) + + if self.tap_direction == "col": + cell_x += self.poly_tap.pitch_offset + + self.cell_pos[row, col] = vector(cell_x, cell_y) + self.cell_inst[row, col].place(self.cell_pos[row, col]) + cell_x += self.zero_cell.width + + self.strap_pos[row, self.column_size] = vector(cell_x, cell_y) + self.tap_inst[row, self.column_size].place(self.strap_pos[row, self.column_size]) + + def route_pitch_offsets(self): + self.gnd_taps = [] + for row in range(0 , self.row_size, self.tap_spacing): + + for col in range(self.column_size): + cell = self.cell_inst[row, col] + + source = cell.get_pin("S") + + if row != 0: + drain = self.cell_inst[row - 1, col].get_pin("D") + start = vector(drain.cx(), source.cy()) + end = drain.center() + self.add_segment_center(self.bitline_layer, start, end) + self.place_well_tap(row, col) + + def place_well_tap(self, row, col): + cell = self.cell_inst[row, col] + source = cell.get_pin("S") + + if col != self.column_size - 1: + tap_x = (self.cell_inst[row , col + 1].get_pin("S").cx() + source.cx()) * 0.5 + else: + tap_x = cell.rx() + self.active_space + + if row != 0: + drain = self.cell_inst[row - 1, col].get_pin("D") + tap_y = (source.cy() + drain.cy()) * 0.5 + else: + tap_y = source.cy() - self.contact_width - 2 * self.active_enclose_contact - self.active_space + + tap_pos = vector(tap_x, tap_y) + self.add_via_center(layers=self.active_stack, + offset=tap_pos, + implant_type="p", + well_type="p", + directions="nonpref") + self.add_via_stack_center(offset=tap_pos, + from_layer=self.active_stack[2], + to_layer=self.wordline_layer) + self.gnd_taps.append(self.add_layout_pin_rect_center("gnd_tap", self.wordline_layer, tap_pos)) + + def place_precharge(self): + self.precharge_offset = vector(0, - self.precharge_inst.height - self.zero_cell.nmos.end_to_contact - 2 * drc["nwell_enclose_active"] - 3 * self.m1_pitch) + self.precharge_inst.place(offset=self.precharge_offset) + self.copy_layout_pin(self.precharge_inst, "gate", "precharge") + + def place_wordline_contacts(self): + + for wl in range(self.row_size): + self.copy_layout_pin(self.tap_inst[wl, 0], "poly_tap", self.wordline_names[0][wl]) + + + def place_bitline_contacts(self): + + rail_y = self.precharge_inst.get_pins("vdd")[0].cy() + + for bl in range(self.column_size): + + src_pin = self.cell_list[0][bl].get_pin("S") + prechg_pin_name = "pre_bl{0}_out".format(bl) + pre_pin = self.precharge_inst.get_pin(prechg_pin_name) + + middle_offset = (src_pin.cy() - pre_pin.cy() ) * 0.5 + + corrected = vector(src_pin.cx(), src_pin.cy() - middle_offset) + + output_pos = vector(corrected.x, rail_y) + + self.add_segment_center(self.bitline_layer, corrected, output_pos) + + self.add_layout_pin_rect_center(self.bitline_names[0][bl], self.bitline_layer, output_pos ) + + def route_precharge(self): + for bl in range(self.column_size): + bl_pin = self.cell_list[0][bl].get_pin("S") + prechg_pin = "pre_bl{0}_out".format(bl) + pre_out_pin = self.precharge_inst.get_pin(prechg_pin) + + bl_start = bl_pin.center() + bl_end = vector(bl_start.x, pre_out_pin.cy()) + + self.add_segment_center(self.bitline_layer, bl_start, bl_end) + + upper_precharge = self.precharge_inst.get_pin("precharge_r") + lower_precharge = self.tap_inst[self.row_size, self.column_size ].get_pin("poly_tap") + + if self.pitch_match: + wire_offset = 2 * self.m1_pitch + else: + wire_offset = 3 * self.m1_pitch + start = upper_precharge.center() + end = lower_precharge.center() + mid1 = start + vector(wire_offset, 0) + mid2 = end + vector(wire_offset, 0) + + self.add_path(layer="m1", coordinates=[start, mid1, mid2, end]) + + self.add_layout_pin_rect_center(text="precharge_r", layer="m1", offset=mid1) + + def connect_taps(self): + poly_tap_pins = [self.poly_tap_list[i].get_pin("poly_tap") for i in range(len(self.poly_tap_list))] + + self.connect_row_pins(layer=self.wordline_layer, pins=poly_tap_pins) diff --git a/compiler/modules/rom_base_cell.py b/compiler/modules/rom_base_cell.py new file mode 100644 index 00000000..28fc07f0 --- /dev/null +++ b/compiler/modules/rom_base_cell.py @@ -0,0 +1,125 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +from openram.base import design +from openram.base import vector +from openram import OPTS +from openram.sram_factory import factory +from openram.tech import drc + + +class rom_base_cell(design): + + def __init__(self, name="", bitline_layer="li", bit_value=1, add_well=False): + super().__init__(name) + self.bit_value = bit_value + self.bitline_layer = bitline_layer + self.add_well=add_well + self.create_netlist() + self.create_layout() + + def create_netlist(self): + self.add_pins() + self.add_modules() + + def create_layout(self): + + self.create_tx() + self.setup_drc_offsets() + self.add_boundary() + self.place_tx() + self.place_bitline() + self.place_poly() + if self.bit_value == 0: + self.short_gate() + + # Calculates offsets of cell width and height so that tiling of cells does not violate any drc rules + def setup_drc_offsets(self): + + self.poly_size = (self.cell_inst.width + self.active_space) - (self.cell_inst.height + 2 * self.poly_extend_active) + + def add_boundary(self): + + height = self.cell_inst.width + self.active_space + + #cell width with offsets applied, height becomes width when the cells are rotated + width = self.cell_inst.height + 2 * self.poly_extend_active + + + # make the cells square so the pitch of wordlines will match bitlines + + if width > height: + self.width = width + self.height = width + else: + self.width = height + self.height = height + + super().add_boundary() + + + def add_modules(self): + + self.nmos = factory.create(module_type="ptx", + module_name="nmos_rom_mod", + tx_type="nmos", + add_source_contact=self.bitline_layer, + add_drain_contact=self.bitline_layer + ) + + + def create_tx(self): + self.cell_inst = self.add_inst( name=self.name + "_nmos", + mod=self.nmos, + ) + if self.bit_value == 0: + self.connect_inst(["bl", "wl", "bl", "gnd"]) + else: + self.connect_inst(["bl_h", "wl", "bl_l", "gnd"]) + + + def add_pins(self): + if self.bit_value == 0 : + pin_list = ["bl", "wl", "gnd"] + dir_list = ["INOUT", "INPUT", "GROUND"] + else: + pin_list = ["bl_h", "bl_l", "wl", "gnd"] + dir_list = ["INOUT", "INOUT", "INPUT", "GROUND"] + + self.add_pin_list(pin_list, dir_list) + + def place_tx(self): + + # sizing_offset = self.cell_inst.height - drc["minwidth_tx"] + tx_offset = vector(self.poly_extend_active + self.cell_inst.height + self.poly_size,- 0.5 * self.contact_width - self.active_enclose_contact) + # add rect of poly to account for offset from drc spacing + # self.add_rect_center("poly", poly_offset, self.poly_extend_active_spacing, self.poly_width) + + self.cell_inst.place(tx_offset, rotate=90) + + self.copy_layout_pin(self.cell_inst, "S", "S") + self.copy_layout_pin(self.cell_inst, "D", "D") + self.source_pos = self.cell_inst.get_pin("S").center() + + def place_poly(self): + poly_offset = vector(0, self.cell_inst.width * 0.5 - 0.5 * self.contact_width - self.active_enclose_contact) + + start = poly_offset + end = poly_offset + vector(self.poly_size, 0) + self.add_segment_center("poly", start, end) + + + def place_bitline(self): + + start = self.get_pin("D").center() + end = start + vector(0, 2 * self.active_enclose_contact + 0.5 * self.contact_width + self.active_space) + self.add_segment_center(self.bitline_layer, start, end) + + def short_gate(self): + + self.add_segment_center(self.bitline_layer, self.get_pin("D").center(), self.get_pin("S").center()) \ No newline at end of file diff --git a/compiler/modules/rom_column_mux.py b/compiler/modules/rom_column_mux.py new file mode 100644 index 00000000..e3d8a1d3 --- /dev/null +++ b/compiler/modules/rom_column_mux.py @@ -0,0 +1,173 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from openram import debug +from openram.base import vector +from openram.sram_factory import factory +from openram.tech import drc, layer +from openram.tech import cell_properties as cell_props +from openram import OPTS +from .pgate import * + + +class rom_column_mux(pgate): + """ + This module implements the columnmux bitline cell used in the design. + Creates a single column mux cell with the given integer size relative + to minimum size. Default is 8x. Per Samira and Hodges-Jackson book: + Column-mux transistors driven by the decoder must be sized + for optimal speed + """ + def __init__(self, name, tx_size=8, input_layer="m2", output_layer="m1"): + + debug.info(2, "creating single ROM column mux cell: {0}".format(name)) + self.tx_size = int(tx_size) + self.input_layer = input_layer + self.output_layer= output_layer + super().__init__(name) + + def create_netlist(self): + self.add_pins() + self.add_ptx() + + def create_layout(self): + + self.pin_layer = self.input_layer + self.pin_pitch = getattr(self, "{}_pitch".format(self.pin_layer)) + self.pin_width = getattr(self, "{}_width".format(self.pin_layer)) + self.pin_height = 2 * self.pin_width + + # If li exists, use li and m1 for the mux, otherwise use m1 and m2 + if self.output_layer == "li" : + self.col_mux_stack = self.li_stack + else: + self.col_mux_stack = self.m1_stack + + self.place_ptx() + + self.width = self.bitcell.width + self.height = self.nmos_lower.uy() + self.pin_height + + self.connect_poly() + self.add_bitline_pins() + self.connect_bitlines() + self.add_pn_wells() + + def add_ptx(self): + self.bitcell = factory.create(module_type="rom_base_cell") + + # Adds nmos_lower,nmos_upper to the module + self.ptx_width = self.tx_size * drc("minwidth_tx") + self.nmos = factory.create(module_type="ptx", + width=self.ptx_width) + + # Space it in the center + self.nmos_lower = self.add_inst(name="mux_tx1", + mod=self.nmos) + self.connect_inst(["bl", "sel", "bl_out", "gnd"]) + + + def add_pins(self): + self.add_pin_list(["bl", "bl_out", "sel", "gnd"]) + + def add_bitline_pins(self): + """ Add the top and bottom pins to this cell """ + + bl_pos = vector(self.pin_pitch, 0) + + # bl and br + self.add_layout_pin(text="bl", + layer=self.pin_layer, + offset=bl_pos + vector(0, self.height - self.pin_height), + height=self.pin_height) + + # bl_out and br_out + self.add_layout_pin(text="bl_out", + layer=self.col_mux_stack[0], + offset=bl_pos, + height=self.pin_height) + + + def place_ptx(self): + """ Create the pass gate NMOS transistor to switch the bitline """ + + # Space it in the center + nmos_lower_position = self.nmos.active_offset.scale(0, 1) \ + + vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0) + self.nmos_lower.place(nmos_lower_position) + + def connect_poly(self): + """ Connect the poly gate of the two pass transistors """ + + # offset is the top of the lower nmos' diffusion + # height is the distance between the nmos' diffusions, which depends on max(self.active_space,self.poly_space) + offset = self.nmos_lower.get_pin("G").ul() - vector(0, self.poly_extend_active) + height = self.poly_extend_active - offset.y + self.add_rect(layer="poly", + offset=offset, + height=height) + + # Add the sel pin to the bottom of the mux + self.add_layout_pin(text="sel", + layer="poly", + offset=self.nmos_lower.get_pin("G").ll(), + height=self.poly_extend_active) + + def connect_bitlines(self): + """ Connect the bitlines to the mux transistors """ + + bl_pin = self.get_pin("bl") + bl_out_pin = self.get_pin("bl_out") + + nmos_lower_s_pin = self.nmos_lower.get_pin("S") + nmos_lower_d_pin = self.nmos_lower.get_pin("D") + self.add_via_stack_center(from_layer=nmos_lower_s_pin.layer, + to_layer=self.input_layer, + offset=nmos_lower_s_pin.center()) + + self.add_via_stack_center(from_layer=nmos_lower_d_pin.layer, + to_layer=self.output_layer, + offset=nmos_lower_d_pin.center()) + + # bl -> nmos_upper/D on metal1 + # bl_out -> nmos_upper/S on metal2 + mid1 = bl_pin.bc().scale(1, 0.4) \ + + nmos_lower_s_pin.uc().scale(0, 0.5) + mid2 = bl_pin.bc().scale(0, 0.4) \ + + nmos_lower_s_pin.uc().scale(1, 0.5) + self.add_path(self.input_layer, + [bl_pin.bc(), mid1, mid2, nmos_lower_s_pin.center()]) + # halfway up, move over + mid1 = bl_out_pin.uc().scale(1, 0.4) \ + + nmos_lower_d_pin.bc().scale(0, 0.4) + mid2 = bl_out_pin.uc().scale(0, 0.4) \ + + nmos_lower_d_pin.bc().scale(1, 0.4) + self.add_path(self.output_layer, + [bl_out_pin.uc(), mid1, mid2, nmos_lower_d_pin.center()]) + + def add_pn_wells(self): + """ + Add a well and implant over the whole cell. Also, add the + pwell contact (if it exists) + """ + # Add it to the right, aligned in between the two tx + active_pos = vector(self.bitcell.width, + self.nmos_lower.uy() + self.active_contact.height + self.active_space) + + self.add_via_center(layers=self.active_stack, + offset=active_pos, + implant_type="p", + well_type="p") + + # If there is a li layer, include it in the power stack + self.add_via_stack_center(from_layer=self.active_stack[2], + to_layer=self.pin_layer, + offset=active_pos) + + self.add_layout_pin_rect_center(text="gnd", + layer=self.pin_layer, + offset=active_pos) \ No newline at end of file diff --git a/compiler/modules/rom_column_mux_array.py b/compiler/modules/rom_column_mux_array.py new file mode 100644 index 00000000..e314596e --- /dev/null +++ b/compiler/modules/rom_column_mux_array.py @@ -0,0 +1,209 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from openram import debug +from openram.base import design +from openram.base import vector +from openram.sram_factory import factory +from openram.tech import layer, preferred_directions, drc +from openram.tech import layer_properties as layer_props +from openram import OPTS + + +class rom_column_mux_array(design): + """ + Dynamically generated column mux array. + Array of column mux to read the bitlines from ROM, based on the RAM column mux + """ + + def __init__(self, name, columns, word_size, tap_spacing=4, input_layer="m1", bitline_layer="m1", sel_layer="m2"): + super().__init__(name) + debug.info(1, "Creating {0}".format(self.name)) + self.add_comment("cols: {0} word_size: {1} ".format(columns, word_size)) + + self.columns = columns + self.word_size = word_size + self.words_per_row = int(self.columns / self.word_size) + self.input_layer = input_layer + self.tap_spacing = tap_spacing + self.sel_layer = sel_layer + self.supply_layer = "m2" + self.sel_pitch = getattr(self, self.sel_layer + "_pitch") + self.bitline_layer = bitline_layer + + if preferred_directions[self.sel_layer] == "V": + self.via_directions = ("H", "H") + else: + self.via_directions = "pref" + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_array() + + def create_layout(self): + self.setup_layout_constants() + self.place_array() + self.add_routing() + + # Find the highest shapes to determine height before adding well + highest = self.find_highest_coords() + self.height = highest.y + self.add_layout_pins() + if "pwell" in layer: + self.add_enclosure(self.mux_inst, "pwell") + + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + for i in range(self.columns): + self.add_pin("bl_{}".format(i)) + for i in range(self.words_per_row): + self.add_pin("sel_{}".format(i)) + for i in range(self.word_size): + self.add_pin("bl_out_{}".format(i)) + self.add_pin("gnd") + + def add_modules(self): + self.mux = factory.create(module_type="rom_column_mux", input_layer=self.input_layer, output_layer=self.bitline_layer) + self.tap = factory.create(module_type="rom_poly_tap", add_active_tap=True) + self.cell = factory.create(module_type="rom_base_cell") + + def setup_layout_constants(self): + self.column_addr_size = int(self.words_per_row / 2) + self.width = self.columns * self.mux.width + # one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br + # one extra route pitch is to space from the sense amp + self.route_height = (self.words_per_row + 3) * self.cell.width + self.route_layer_width = drc["minwidth_{}".format(self.bitline_layer)] + self.route_layer_pitch = drc["{0}_to_{0}".format(self.bitline_layer)] + + def create_array(self): + self.mux_inst = [] + # For every column, add a pass gate + for col_num in range(self.columns): + name = "XMUX{0}".format(col_num) + self.mux_inst.append(self.add_inst(name=name, + mod=self.mux)) + + self.connect_inst(["bl_{}".format(col_num), + "bl_out_{}".format(int(col_num / self.words_per_row)), + "sel_{}".format(col_num % self.words_per_row), + "gnd"]) + + def place_array(self): + # Default to single spaced columns + self.offsets = [n * self.mux.width for n in range(self.columns)] + + # For every column, add a pass gate + for col_num, xoffset in enumerate(self.offsets[0:self.columns]): + offset = vector(xoffset, self.route_height) + self.mux_inst[col_num].place(offset=offset) + + def add_layout_pins(self): + """ Add the pins after we determine the height. """ + # For every column, add a pass gate + for col_num in range(self.columns): + mux_inst = self.mux_inst[col_num] + bl_pin = mux_inst.get_pin("bl") + offset = bl_pin.ll() + self.add_layout_pin(text="bl_{}".format(col_num), + layer=bl_pin.layer, + offset=offset, + height=self.height - offset.y) + + + def route_supplies(self): + self.route_horizontal_pins("gnd", self.insts, layer=self.supply_layer) + + def add_routing(self): + self.add_horizontal_input_rail() + self.add_vertical_poly_rail() + self.route_bitlines() + self.route_supplies() + + def add_horizontal_input_rail(self): + """ Create address input rails below the mux transistors """ + tap_offset = 0 + for j in range(self.words_per_row): + if j % self.tap_spacing == 0 and j != 0: + tap_offset += self.tap.pitch_offset + offset = vector(0, self.route_height + tap_offset + (j - self.words_per_row) * self.cell.width) + + self.add_layout_pin(text="sel_{}".format(j), + layer=self.sel_layer, + offset=offset, + width=self.mux_inst[-1].rx()) + + def add_vertical_poly_rail(self): + """ Connect the poly to the address rails """ + + + # Offset to the first transistor gate in the pass gate + for col in range(self.columns): + # which select bit should this column connect to depends on the position in the word + sel_index = col % self.words_per_row + # Add the column x offset to find the right select bit + gate_offset = self.mux_inst[col].get_pin("sel").bc() + # use the y offset from the sel pin and the x offset from the gate + + offset = vector(gate_offset.x, + self.get_pin("sel_{}".format(sel_index)).cy()) + + bl_x_offset = self.mux_inst[col].get_pin("bl_out").cx() + 2 * self.route_layer_width + self.route_layer_pitch + 0.5 * self.poly_contact.width + + bl_offset = offset.scale(0, 1) + vector(bl_x_offset, 0) + self.add_via_stack_center(from_layer="poly", + to_layer=self.sel_layer, + offset=bl_offset, + directions=self.via_directions) + self.add_path("poly", [offset, gate_offset, bl_offset]) + + def route_bitlines(self): + """ Connect the output bit-lines to form the appropriate width mux """ + for j in range(self.columns): + + bl_offset_begin = self.mux_inst[j].get_pin("bl_out").bc() + + bl_out_offset_begin = bl_offset_begin - vector(0, (self.words_per_row + 1) * self.cell.width) + + # Add the horizontal wires for the first bit + if j % self.words_per_row == 0: + bl_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("bl_out").bc() + bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.cell.width) + + self.add_path(self.sel_layer, [bl_out_offset_begin, bl_out_offset_end]) + + # Extend the bitline output rails and gnd downward on the first bit of each n-way mux + self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j / self.words_per_row)), + layer=self.bitline_layer, + start=bl_offset_begin, + end=bl_out_offset_begin) + + + else: + self.add_path(self.bitline_layer, [bl_out_offset_begin, bl_offset_begin]) + + # This via is on the right of the wire + self.add_via_stack_center(from_layer=self.bitline_layer, + to_layer=self.sel_layer, + offset=bl_out_offset_begin, + directions=self.via_directions) + + def graph_exclude_columns(self, column_include_num): + """ + Excludes all columns muxes unrelated to the target bit being simulated. + Each mux in mux_inst corresponds to respective column in bitcell array. + """ + for i in range(len(self.mux_inst)): + if i != column_include_num: + self.graph_inst_exclude.add(self.mux_inst[i]) \ No newline at end of file diff --git a/compiler/modules/rom_control_logic.py b/compiler/modules/rom_control_logic.py new file mode 100644 index 00000000..494c328d --- /dev/null +++ b/compiler/modules/rom_control_logic.py @@ -0,0 +1,141 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +from openram.sram_factory import factory +from openram.base import vector, design +from openram.tech import layer, drc + + +class rom_control_logic(design): + + def __init__(self, num_outputs, clk_fanout, name="", height=None): + self.output_size = num_outputs + super().__init__(name, prop=False) + self.height = height + if self.height is not None: + + self.driver_height = 0.5 * self.height + self.gate_height = 0.5 * self.height + else: + self.gate_height = 20 * self.m1_pitch + self.driver_height = self.gate_height + + + self.clk_fanout = clk_fanout + + if "li" in layer: + self.route_stack = self.li_stack + else: + self.route_stack = self.m1_stack + + self.create_netlist() + self.create_layout() + self.add_boundary() + + def create_netlist(self): + self.add_modules() + self.add_pins() + + def create_layout(self): + self.create_instances() + self.height=self.driver_inst.height + self.buf_inst.height + self.width= max(self.nand_inst.width + self.buf_inst.width, self.driver_inst.width) + self.place_instances() + self.route_insts() + + def add_modules(self): + self.buf_mod = factory.create(module_type="pdriver", + module_name="rom_clock_driver", + height=self.gate_height, + fanout=self.clk_fanout + 2, + add_wells=True, + ) + self.nand_mod = factory.create(module_type="pnand2", + module_name="rom_control_nand", + height=self.gate_height, + add_wells=False) + self.driver_mod = factory.create(module_type="pdriver", + module_name="rom_precharge_driver", + inverting=True, + fanout=self.output_size, + height=self.driver_height, + add_wells=True) + + def add_pins(self): + self.add_pin("clk_in", "INPUT") + self.add_pin("CS", "INPUT") + self.add_pin("prechrg", "OUTPUT") + self.add_pin("clk_out", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_instances(self): + + self.buf_inst = self.add_inst(name="clk_driver", mod=self.buf_mod) + self.connect_inst(["clk_in", "clk_out", "vdd", "gnd"]) + + self.nand_inst = self.add_inst(name="control_nand", mod=self.nand_mod) + self.connect_inst(["CS", "clk_out", "pre_drive", "vdd", "gnd"]) + + self.driver_inst = self.add_inst(name="precharge_driver", mod=self.driver_mod) + self.connect_inst(["pre_drive", "prechrg", "vdd", "gnd"]) + + + def place_instances(self): + self.nand_inst.place(offset=[self.buf_inst.width, 0]) + self.driver_inst.place(offset=[0, self.buf_inst.height + self.driver_inst.height], mirror="MX") + + # hack to get around the fact these modules dont tile properly + offset = self.driver_inst.get_pin("vdd").cy() - self.nand_inst.get_pin("vdd").cy() + self.driver_inst.place(offset=[0, self.buf_inst.height + self.driver_inst.height - offset], mirror="MX") + + def route_insts(self): + + route_width = drc["minwidth_{}".format(self.route_stack[2])] + self.copy_layout_pin(self.buf_inst, "A", "clk_in") + self.copy_layout_pin(self.buf_inst, "Z", "clk_out") + self.copy_layout_pin(self.driver_inst, "Z", "prechrg") + self.copy_layout_pin(self.nand_inst, "A", "CS") + + self.copy_power_pin(self.buf_inst.get_pin("gnd"), directions="nonpref") + self.copy_power_pin(self.driver_inst.get_pin("gnd"), directions="nonpref") + self.copy_power_pin(self.buf_inst.get_pin("vdd"), directions="nonpref") + clk = self.buf_inst.get_pin("Z") + + nand_B = self.nand_inst.get_pin("B") + + + # Connect buffered clock bar to nand input + + mid = vector(clk.lx() - route_width - 2 * self.m1_space) + self.add_path(self.route_stack[2], [clk.center(), nand_B.center()]) + + self.add_via_stack_center(from_layer=clk.layer, + to_layer=self.route_stack[2], + offset=clk.center()) + self.add_via_stack_center(from_layer=nand_B.layer, + to_layer=self.route_stack[2], + offset=nand_B.center()) + + + # Connect nand output to precharge driver + nand_Z = self.nand_inst.get_pin("Z") + + driver_A = self.driver_inst.get_pin("A") + + mid = vector(driver_A.cx(), driver_A.cy() - 4 * route_width) + + self.add_path(self.route_stack[2], [nand_Z.center(), mid, driver_A.center()]) + + self.add_via_stack_center(from_layer=nand_Z.layer, + to_layer=self.route_stack[2], + offset=nand_Z.center()) + + self.add_via_stack_center(from_layer=driver_A.layer, + to_layer=self.route_stack[2], + offset=driver_A.center()) \ No newline at end of file diff --git a/compiler/modules/rom_decoder.py b/compiler/modules/rom_decoder.py new file mode 100644 index 00000000..760af8c4 --- /dev/null +++ b/compiler/modules/rom_decoder.py @@ -0,0 +1,243 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from math import ceil, log +from openram.sram_factory import factory +from openram.base import vector, design +from openram import OPTS +from openram.tech import drc + +class rom_decoder(design): + def __init__(self, num_outputs, fanout, strap_spacing, name="", route_layer="m1", output_layer="m1", invert_outputs=False): + + # word lines in the base array become the address lines/cols in the decoder + # bit lines in the base array become the word lines/rows in the decoder + # array gets rotated 90deg so rows/cols switch + + self.strap_spacing=strap_spacing + self.num_outputs = num_outputs + self.num_inputs = ceil(log(num_outputs, 2)) + self.create_decode_map() + + super().__init__(name) + + b = factory.create(module_type=OPTS.bitcell) + self.cell_height = b.height + self.route_layer = route_layer + self.output_layer = output_layer + self.inv_route_layer = "m2" + self.fanout=fanout + self.invert_outputs=invert_outputs + self.create_netlist() + + self.width = self.array_mod.height + self.wordline_buf.width + self.height = self.array_mod.width + self.control_array.height + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_instances() + + def create_layout(self): + self.setup_layout_constants() + self.place_array() + self.place_input_buffer() + self.place_driver() + self.route_outputs() + + self.connect_inputs() + self.route_supplies() + self.add_boundary() + + def add_boundary(self): + ll = self.find_lowest_coords() + m1_offset = self.m1_width + self.translate_all(vector(0, ll.y)) + ur = self.find_highest_coords() + + ur = vector(ur.x, ur.y) + super().add_boundary(ll, ur) + self.width = ur.x + self.height = ur.y + + def setup_layout_constants(self): + self.inv_route_width = drc["minwidth_{}".format(self.inv_route_layer)] + + def create_decode_map(self): + self.decode_map = [] + # create decoding map that will be the bitmap for the rom decoder + # row/col order in the map will be switched in the placed decoder/ + for col in range(self.num_inputs): + + # odd cols are address + # even cols are address bar + col_array = [] + inv_col_array = [] + for row in range(self.num_outputs): + + addr_idx = -col - 1 + + addr = format(row, 'b') + if col >= len(addr) : + bin_digit = 0 + else: + bin_digit = int(addr[addr_idx]) + + col_array.append(bin_digit) + + if bin_digit == 0 : inv_col_array.append(1) + else : inv_col_array.append(0) + + self.decode_map.append(col_array) + self.decode_map.append(inv_col_array) + self.decode_map.reverse() + + def add_pins(self): + for i in range(self.num_inputs): + self.add_pin("A{0}".format(i), "INPUT") + + for j in range(self.num_outputs): + self.add_pin("wl_{0}".format(j), "OUTPUT") + self.add_pin("precharge", "INPUT") + self.add_pin("clk", "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def add_modules(self): + + self.control_array = factory.create(module_type="rom_address_control_array", + cols=self.num_inputs) + + self.wordline_buf = factory.create(module_type="rom_wordline_driver_array", module_name="{}_wordline_buffer".format(self.name), + rows=self.num_outputs, + fanout=ceil(self.fanout), + invert_outputs=self.invert_outputs, + tap_spacing=self.strap_spacing) + + self.array_mod = factory.create(module_type="rom_base_array", + module_name="{}_array".format(self.name), + cols=self.num_outputs, + rows=2 * self.num_inputs, + bitmap=self.decode_map, + strap_spacing = self.strap_spacing, + bitline_layer=self.output_layer, + tap_direction="col") + + def create_instances(self): + + self.create_array_inst() + self.create_input_buffer() + self.create_wordline_buffer() + + def create_input_buffer(self): + name = "pre_control_array" + self.buf_inst = self.add_inst(name=name, mod=self.control_array) + + control_pins = [] + + for i in range(self.num_inputs): + control_pins.append("A{0}".format(i)) + for i in range(self.num_inputs): + control_pins.append("A_int_{0}".format(i)) + for i in range(self.num_inputs): + control_pins.append("Ab_int_{0}".format(i)) + + + control_pins.append("clk") + control_pins.append("vdd") + control_pins.append("gnd") + self.connect_inst(control_pins) + + def create_array_inst(self): + self.array_inst = self.add_inst(name="decode_array_inst", mod=self.array_mod) + + array_pins = [] + + for j in range(self.num_outputs): + name = "wl_int{0}".format(j) + array_pins.append(name) + + + for i in reversed(range(self.num_inputs)): + array_pins.append("Ab_int_{0}".format(i)) + array_pins.append("A_int_{0}".format(i)) + array_pins.append("precharge") + array_pins.append("vdd") + array_pins.append("gnd") + self.connect_inst(array_pins) + + def create_wordline_buffer(self): + self.wordline_buf_inst = self.add_inst("rom_wordline_driver", mod=self.wordline_buf) + in_pins = ["wl_int{}".format(wl) for wl in range(self.num_outputs)] + out_pins = ["wl_{}".format(wl) for wl in range(self.num_outputs)] + pwr_pins = ["vdd", "gnd"] + self.connect_inst(in_pins + out_pins + pwr_pins) + + def place_input_buffer(self): + wl = self.array_mod.row_size - 1 + align = self.array_inst.get_pin(self.array_mod.wordline_names[0][wl]).cx() - self.buf_inst.get_pin("A0_out").cx() + + self.buf_inst.place(vector(align, 0)) + + self.copy_layout_pin(self.buf_inst, "clk") + + def place_array(self): + offset = vector(self.array_mod.height, self.control_array.height + self.m1_width + self.poly_contact.width) + self.array_inst.place(offset, rotate=90) + + def place_driver(self): + + offset = vector(self.array_inst.height + self.m1_width, self.array_inst.by()) + self.wordline_buf_inst.place(offset) + + # calculate the offset between the decode array and the buffer inputs now that their zeros are aligned + pin_offset = self.array_inst.get_pin("bl_0_0").cy() - self.wordline_buf_inst.get_pin("in_0").cy() + self.wordline_buf_inst.place(offset + vector(0, pin_offset)) + + def route_outputs(self): + for j in range(self.num_outputs): + + self.copy_layout_pin(self.wordline_buf_inst, "out_{}".format(j), "wl_{}".format(j)) + offset = self.wordline_buf_inst.get_pin("out_{}".format(j)).center() + + array_pins = [self.array_inst.get_pin("bl_0_{}".format(bl)) for bl in range(self.num_outputs)] + driver_pins = [self.wordline_buf_inst.get_pin("in_{}".format(bl)) for bl in range(self.num_outputs)] + + route_pins = array_pins + driver_pins + self.connect_row_pins(self.output_layer, route_pins, round=True) + + def connect_inputs(self): + + self.copy_layout_pin(self.array_inst, "precharge") + self.copy_layout_pin(self.array_inst, "precharge_r") + for i in range(self.num_inputs): + wl = (self.num_inputs - i) * 2 - 1 + wl_bar = wl - 1 + addr_pin = self.array_inst.get_pin(self.array_mod.wordline_names[0][wl]) + addr_bar_pin = self.array_inst.get_pin(self.array_mod.wordline_names[0][wl_bar]) + + addr_out_pin = self.buf_inst.get_pin("A{}_out".format(i)) + addr_bar_out_pin = self.buf_inst.get_pin("Abar{}_out".format(i)) + + addr_middle = vector(addr_pin.cx(), addr_out_pin.cy()) + + addr_bar_middle = vector(addr_bar_pin.cx(), addr_bar_out_pin.cy()) + + self.add_path(self.inv_route_layer, [addr_out_pin.center(), addr_middle, addr_pin.center()]) + self.add_path(self.inv_route_layer, [addr_bar_out_pin.center(), addr_bar_middle, addr_bar_pin.center()]) + self.copy_layout_pin(self.buf_inst, "A{}_in".format(i), "A{}".format(i)) + + def route_supplies(self): + + self.copy_layout_pin(self.array_inst, "vdd") + self.copy_layout_pin(self.wordline_buf_inst, "vdd") + self.copy_layout_pin(self.buf_inst, "vdd") + + self.copy_layout_pin(self.array_inst, "gnd") + self.copy_layout_pin(self.wordline_buf_inst, "gnd") + self.copy_layout_pin(self.buf_inst, "gnd") \ No newline at end of file diff --git a/compiler/modules/rom_poly_tap.py b/compiler/modules/rom_poly_tap.py new file mode 100644 index 00000000..c16323b6 --- /dev/null +++ b/compiler/modules/rom_poly_tap.py @@ -0,0 +1,96 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from openram.base import design +from openram.base import vector +from openram import OPTS +from openram.sram_factory import factory +from openram.tech import drc + +class rom_poly_tap(design): + + def __init__(self, name="", cell_name=None, tx_type="nmos", strap_layer="m2", add_active_tap=False, place_poly=None): + super().__init__(name, cell_name) + self.strap_layer=strap_layer + self.tx_type = tx_type + self.add_tap = add_active_tap + if place_poly is None: + self.place_poly = add_active_tap + else: + self.place_poly = place_poly + self.pitch_offset = 0 + self.create_netlist() + self.create_layout() + + def create_netlist(self): + #for layout constants + self.dummy = factory.create(module_type="rom_base_cell") + + self.pmos = factory.create(module_type="ptx", tx_type="pmos") + + def create_layout(self): + + self.place_via() + self.add_boundary() + if self.add_tap or self.place_poly: + self.place_active_tap() + self.extend_poly() + + def add_boundary(self): + contact_width = self.poly_contact.width + self.height = self.dummy.height + self.width = contact_width + self.pitch_offset + + super().add_boundary() + + def place_via(self): + + contact_width = self.poly_contact.width + + contact_y = self.dummy.cell_inst.width * 0.5 - 0.5 * self.contact_width - self.active_enclose_contact + + self.contact_x_offset = 0 + + + contact_x = contact_width * 0.5 + self.contact_x_offset + self.contact_offset = vector(contact_x, contact_y) + + self.via = self.add_via_stack_center(from_layer="poly", + to_layer=self.strap_layer, + offset=self.contact_offset) + self.add_layout_pin_rect_center("poly_tap", self.strap_layer, self.contact_offset) + + def extend_poly(self): + y_offset = 0 + if self.tx_type == "pmos": + y_offset = -self.height + start = self.via.center() + vector(0, y_offset) + + self.add_segment_center("poly", start, vector(self.via.cx() + self.pitch_offset, self.via.cy() + y_offset)) + self.add_segment_center("poly", start, vector(0, self.via.cy() + y_offset)) + + def place_active_tap(self): + gap = self.poly_extend_active - 0.5 * ( self.active_contact.height - self.poly_contact.width ) + offset = self.active_space - gap + tap_x = self.via.cx() + offset + tap_y = self.via.cy() + self.dummy.width * 0.5 + contact_pos = vector(tap_x, tap_y) + + # edge of the next nmos + active_edge = self.dummy.width - self.dummy.cell_inst.height - self.poly_extend_active + + # edge of the active contact + tap_edge = tap_x + 0.5 * self.active_contact.height + self.pitch_offset += (self.active_space * 2) - (tap_edge - active_edge) + self.contact_x_offset + + if self.tx_type == "nmos" and self.add_tap: + self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="p", + well_type="p", + directions="nonpref") + self.add_layout_pin_rect_center("active_tap", self.active_stack[2], contact_pos) \ No newline at end of file diff --git a/compiler/modules/rom_precharge_array.py b/compiler/modules/rom_precharge_array.py new file mode 100644 index 00000000..a450c968 --- /dev/null +++ b/compiler/modules/rom_precharge_array.py @@ -0,0 +1,177 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +from math import ceil +from openram.base import geometry +from openram.base import design +from openram.sram_factory import factory +from openram.base import vector +from openram.tech import layer, drc + +class rom_precharge_array(design): + """ + An array of inverters to create the inverted address lines for the rom decoder + """ + def __init__(self, cols, name="", bitline_layer=None, strap_spacing=None, strap_layer="m2", tap_direction="row"): + self.cols = cols + self.strap_layer = strap_layer + self.tap_direction = tap_direction + + if "li" in layer: + self.supply_layer = "li" + else: + self.supply_layer = "m1" + + if bitline_layer is not None: + self.bitline_layer = bitline_layer + else: + self.bitline_layer = self.supply_layer + + + if name=="": + name = "rom_inv_array_{0}".format(cols) + + if strap_spacing != None: + self.strap_spacing = strap_spacing + else: + self.strap_spacing = 0 + + + if strap_spacing != 0: + self.num_straps = ceil(self.cols / self.strap_spacing) + self.array_col_size = self.cols + self.num_straps + else: + self.num_straps = 0 + self.array_col_size = self.cols + + super().__init__(name) + self.create_netlist() + self.create_layout() + + def create_netlist(self): + self.create_modules() + self.add_pins() + self.create_instances() + + def create_layout(self): + self.width = self.cols * self.pmos.width + self.height = self.pmos.width + self.place_instances() + self.create_layout_pins() + self.route_supply() + self.connect_taps() + + self.add_boundary() + self.extend_well() + + def add_boundary(self): + ur = self.find_highest_coords() + self.add_label(layer="nwell", text="upper right",offset=ur) + super().add_boundary(vector(0, 0), ur) + self.height = ur.y + self.width = ur.x + + def create_modules(self): + + self.pmos = factory.create(module_type="rom_precharge_cell", module_name="precharge_cell", bitline_layer=self.bitline_layer, supply_layer=self.supply_layer) + + # For layout constants + self.dummy = factory.create(module_type="rom_base_cell") + + self.poly_tap = factory.create(module_type="rom_poly_tap", tx_type="pmos", add_active_tap=(self.tap_direction == "col")) + + def add_pins(self): + for col in range(self.cols): + self.add_pin("pre_bl{0}_out".format(col), "OUTPUT") + self.add_pin("gate", "INPUT") + self.add_pin("vdd", "POWER") + + def create_instances(self): + self.array_insts = [] + self.pmos_insts = [] + self.tap_insts = [] + + self.create_poly_tap(-1) + for col in range(self.cols): + + if col % self.strap_spacing == 0: + self.create_poly_tap(col) + self.create_precharge_tx(col) + + def create_precharge_tx(self, col): + name = "pmos_c{0}".format(col) + pmos = self.add_inst(name=name, mod=self.pmos) + self.array_insts.append(pmos) + self.pmos_insts.append(pmos) + bl = "pre_bl{0}_out".format(col) + self.connect_inst(["vdd", "gate", bl]) + + def create_poly_tap(self, col): + name = "tap_c{}".format( col) + new_tap = self.add_inst(name=name, mod=self.poly_tap) + self.tap_insts.append(new_tap) + self.connect_inst([]) + + def place_instances(self): + + self.array_pos = [] + strap_num = 0 + cell_y = 0 + # columns are bit lines + cell_x = 0 + + + for col in range(self.cols): + + if col % self.strap_spacing == 0: + self.tap_insts[strap_num].place(vector(cell_x, cell_y + self.poly_tap.height)) + strap_num += 1 + + if self.tap_direction == "col": + cell_x += self.poly_tap.pitch_offset + + self.pmos_insts[col].place(vector(cell_x, cell_y)) + self.add_label("debug", "li", vector(cell_x, cell_y)) + cell_x += self.pmos.width + + self.tap_insts[strap_num].place(vector(cell_x, cell_y + self.poly_tap.height)) + + def create_layout_pins(self): + self.copy_layout_pin(self.tap_insts[0], "poly_tap", "gate") + self.copy_layout_pin(self.tap_insts[-1], "poly_tap", "precharge_r") + for col in range(self.cols): + source_pin = self.pmos_insts[col].get_pin("D") + bl = "pre_bl{0}_out".format(col) + self.add_layout_pin_rect_center(bl, self.bitline_layer, source_pin.center()) + + def route_supply(self): + + self.route_horizontal_pins("vdd", insts=self.pmos_insts, layer=self.strap_layer) + + def connect_taps(self): + array_pins = [self.tap_insts[i].get_pin("poly_tap") for i in range(len(self.tap_insts))] + + self.connect_row_pins(layer=self.strap_layer, pins=array_pins, name=None, round=False) + + for tap in self.tap_insts: + tap_pin = tap.get_pin("poly_tap") + start = vector(tap_pin.cx(), tap_pin.by()) + end = vector(start.x, tap.mod.get_pin("poly_tap").cy()) + self.add_segment_center(layer="poly", start=start, end=end) + offset_start = vector(end.x - self.poly_tap.width + self.poly_extend_active, end.y) + offset_end = end + vector(0.5*self.poly_width, 0) + self.add_segment_center(layer="poly", start=offset_start, end=offset_end) + + def extend_well(self): + self.well_offset = self.pmos.tap_offset + well_y = self.pmos_insts[0].get_pin("vdd").cy() - 0.5 * self.nwell_width + + well_y = self.get_pin("vdd").cy() - 0.5 * self.nwell_width + well_ll = vector(0, well_y) + + self.add_rect("nwell", well_ll, self.width , self.height - well_y) \ No newline at end of file diff --git a/compiler/modules/rom_precharge_cell.py b/compiler/modules/rom_precharge_cell.py new file mode 100644 index 00000000..3d4730aa --- /dev/null +++ b/compiler/modules/rom_precharge_cell.py @@ -0,0 +1,86 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +from .rom_base_cell import rom_base_cell +from .pgate import pgate +from openram.base import vector +from openram import OPTS +from openram.sram_factory import factory +from openram.tech import drc + +class rom_precharge_cell(rom_base_cell): + + def __init__(self, name="", bitline_layer="m1", supply_layer="li"): + self.supply_layer = supply_layer + super().__init__(name=name, bitline_layer=bitline_layer) + + def create_layout(self): + super().create_layout() + + self.place_tap() + self.extend_well() + + def add_modules(self): + width = pgate.nearest_bin("pmos", drc["minwidth_tx"]) + self.pmos = factory.create(module_type="ptx", + module_name="pre_pmos_mod", + tx_type="pmos", + width=width, + add_source_contact=self.supply_layer, + add_drain_contact=self.bitline_layer + ) + + def create_tx(self): + self.cell_inst = self.add_inst( name="precharge_pmos", + mod=self.pmos, + ) + self.connect_inst(["bitline", "gate", "vdd", "vdd"]) + + def add_pins(self): + pin_list = ["vdd", "gate", "bitline"] + dir_list = ["POWER", "INPUT", "OUTPUT"] + + self.add_pin_list(pin_list, dir_list) + + def setup_drc_offsets(self): + + self.poly_size = (self.cell_inst.width + self.active_space) - (self.cell_inst.height + 2 * self.poly_extend_active) + + def extend_well(self): + + well_y = self.get_pin("vdd").cy() - 0.5 * self.nwell_width + well_ll = vector(0, well_y) + height = self.get_pin("D").cy() + 0.5 * self.nwell_width - well_y + self.add_rect("nwell", well_ll, self.width , height) + + def place_tap(self): + source = self.cell_inst.get_pin("S") + + tap_y = source.cy() - self.contact_width - 2 * self.active_enclose_contact - self.active_space + self.tap_offset = abs(tap_y) + pos = vector(source.cx(), tap_y ) + + self.add_via_center(layers=self.active_stack, + offset=pos, + implant_type="n", + well_type="n", + directions="nonpref") + self.add_via_stack_center(offset=pos, + from_layer=self.active_stack[2], + to_layer=self.supply_layer) + + bitline_offset = vector( 1.5 * (drc("minwidth_{}".format(self.bitline_layer)) + drc("{0}_to_{0}".format(self.bitline_layer))) ,0) + + self.add_layout_pin_rect_center("vdd", self.supply_layer, pos - bitline_offset) + + self.add_path(self.supply_layer, [self.get_pin("vdd").center(), pos, self.get_pin("S").center()]) + + self.remove_layout_pin("S") + + def place_bitline(self): + pass \ No newline at end of file diff --git a/compiler/modules/rom_wordline_driver_array.py b/compiler/modules/rom_wordline_driver_array.py new file mode 100644 index 00000000..44deac1c --- /dev/null +++ b/compiler/modules/rom_wordline_driver_array.py @@ -0,0 +1,223 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from openram import debug +from openram.base import design, drc +from openram.base import vector +from openram.sram_factory import factory +from openram.tech import layer +from openram.tech import layer_properties as layer_props +from openram import OPTS + +class rom_wordline_driver_array(design): + """ + Creates a Wordline Buffer/Inverter array + """ + + def __init__(self, name, rows, fanout, invert_outputs=False, tap_spacing=4, flip_io=False): + design.__init__(self, name) + debug.info(1, "Creating {0}".format(self.name)) + self.add_comment("rows: {0} Buffer size of: {1}".format(rows, fanout)) + + self.rows = rows + self.fanout = fanout + self.invert_outputs=invert_outputs + self.tap_spacing = tap_spacing + self.flip_io = flip_io + if OPTS.tech_name == "sky130": + self.supply_layer = "m1" + else: + self.supply_layer = "m2" + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_drivers() + + def create_layout(self): + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + self.place_drivers() + self.route_layout() + + if self.tap_spacing != 0: + self.place_taps() + self.route_supplies() + self.add_boundary() + + + def add_pins(self): + # inputs to wordline_driver. + for i in range(self.rows): + self.add_pin("in_{0}".format(i), "INPUT") + # Outputs from wordline_driver. + for i in range(self.rows): + self.add_pin("out_{0}".format(i), "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def add_modules(self): + b = factory.create(module_type="rom_base_cell") + self.tap = factory.create(module_type="rom_poly_tap", add_active_tap = True) + + if self.invert_outputs: + self.wl_driver = factory.create(module_type="pinv_dec", + size=self.fanout, + height=b.height, + add_wells=False, + flip_io=self.flip_io) + + else: + self.wl_driver = factory.create(module_type="pbuf_dec", + size=self.fanout, + height=b.height, + add_wells=False) + + def route_supplies(self): + """ + Add a pin for each row of vdd/gnd which + are must-connects next level up. + """ + # self.route_vertical_pins("vdd", self.wld_inst, xside="cx", layer=self.supply_layer) + # self.route_vertical_pins("gnd", self.wld_inst, xside="cx", layer=self.supply_layer) + if not self.invert_outputs: + vdd_pins = [pin for inst in self.wld_inst for pin in inst.get_pins("vdd")] + gnd_pins = [pin for inst in self.wld_inst for pin in inst.get_pins("gnd")] + else: + vdd_pins = [inst.get_pin("vdd") for inst in self.wld_inst] + gnd_pins = [inst.get_pin("gnd") for inst in self.wld_inst] + if self.tap_spacing != 0: + vdd_pins = vdd_pins + self.vdd_taps + gnd_pins = gnd_pins + self.gnd_taps + + supply_width = drc["minwidth_{}".format(self.supply_layer)] + + # Route together all internal supply pins + self.connect_col_pins(layer=self.supply_layer, pins=vdd_pins, name="vdd_tmp") + self.connect_col_pins(layer=self.supply_layer, pins=gnd_pins, name="gnd_tmp") + self.remove_layout_pin("gnd_tap") + self.remove_layout_pin("vdd_tap") + + # Place the top level supply pins on the edge of the module + for pin in self.get_pins("gnd_tmp"): + bottom = vector(pin.cx(), pin.by() - 0.5 * supply_width) + top = vector(pin.cx(), pin.uy() + 0.5 * supply_width) + self.add_layout_pin_rect_ends(layer=self.supply_layer, start=bottom, end=top, name="gnd") + + for pin in self.get_pins("vdd_tmp"): + bottom = vector(pin.cx(), pin.by() - 0.5 * supply_width) + top = vector(pin.cx(), pin.uy() + 0.5 * supply_width) + self.add_layout_pin_rect_ends(layer=self.supply_layer, start=bottom, end=top, name="vdd") + + + self.remove_layout_pin("gnd_tmp") + self.remove_layout_pin("vdd_tmp") + + + def create_drivers(self): + self.wld_inst = [] + for row in range(self.rows): + self.wld_inst.append(self.add_inst(name="wld{0}".format(row), + mod=self.wl_driver)) + self.connect_inst(["in_{0}".format(row), + "out_{0}".format(row), + "vdd", "gnd"]) + + def place_drivers(self): + y_offset = 0 + for row in range(self.rows): + if self.tap_spacing != 0 and row % self.tap_spacing == 0: + y_offset += self.tap.pitch_offset + offset = [0, y_offset] + + self.wld_inst[row].place(offset=offset) + y_offset += self.wld_inst[row].height + + self.width = self.wl_driver.width + self.height = self.wl_driver.height * self.rows + + def route_layout(self): + """ Route all of the signals """ + route_width = drc["minwidth_{}".format(self.route_layer)] + for row in range(self.rows): + if self.flip_io: + row_num = self.rows - row - 1 + else: + row_num = row + inst = self.wld_inst[row_num] + + + self.copy_layout_pin(inst, "A", "in_{0}".format(row)) + + out_pin = inst.get_pin("Z") + # output each WL on the right + if self.flip_io: + wl_offset = out_pin.lc() - vector(1.6 * route_width, 0) + + else: + wl_offset = out_pin.rc() - vector( 0.5 * route_width, 0) + + end = vector(wl_offset.x, \ + self.get_pin("in_{}".format(row)).cy() + 0.5 * route_width) + self.add_segment_center(layer=self.route_layer, + start=wl_offset, + end=end) + if self.flip_io: + self.add_segment_center(layer=self.route_layer, + start=out_pin.lc(), + end=vector(wl_offset.x - 0.5 * route_width, out_pin.cy())) + + self.add_layout_pin_rect_center(text="out_{}".format(row), layer=self.route_layer, offset=end - vector(0, 0.5 * route_width)) + + def place_taps(self): + self.vdd_taps = [] + self.gnd_taps = [] + for wl in range(0 , self.rows, self.tap_spacing): + driver = self.wld_inst[wl] + + source_pin1 = driver.get_pins("vdd")[0] + gnd_pin1 = driver.get_pins("gnd")[0] + + left_edge = driver.get_pin("Z").cy() - 0.5 * self.contact_width - self.active_enclose_contact - self.active_space - 0.5 * self.active_contact.width + + contact_pos = vector(source_pin1.cx(), left_edge) + self.place_tap(contact_pos, "n") + + contact_pos = vector( gnd_pin1.cx(), left_edge) + self.place_tap(contact_pos, "p") + + if not self.invert_outputs: + source_pin2 = driver.get_pins("vdd")[1] + gnd_pin2 = driver.get_pins("gnd")[1] + contact_pos = vector(source_pin2.cx(), left_edge) + self.place_tap(contact_pos, "n") + + contact_pos = vector( gnd_pin2.cx(), left_edge) + self.place_tap(contact_pos, "p") + + def place_tap(self, offset, well_type): + + self.add_via_center(layers=self.active_stack, + offset=offset, + implant_type=well_type, + well_type=well_type, + directions="nonpref") + self.add_via_stack_center(offset=offset, + from_layer=self.active_stack[2], + to_layer=self.supply_layer) + if well_type == "p": + pin = "gnd_tap" + self.gnd_taps.append(self.add_layout_pin_rect_center(text=pin, layer=self.supply_layer, offset=offset)) + else: + pin = "vdd_tap" + self.vdd_taps.append(self.add_layout_pin_rect_center(text=pin, layer=self.supply_layer, offset=offset)) diff --git a/compiler/modules/row_cap_bitcell_1port.py b/compiler/modules/row_cap_bitcell_1port.py index 9a60a414..09b89737 100644 --- a/compiler/modules/row_cap_bitcell_1port.py +++ b/compiler/modules/row_cap_bitcell_1port.py @@ -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 diff --git a/compiler/modules/row_cap_bitcell_2port.py b/compiler/modules/row_cap_bitcell_2port.py index 88134292..63f9af84 100644 --- a/compiler/modules/row_cap_bitcell_2port.py +++ b/compiler/modules/row_cap_bitcell_2port.py @@ -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 diff --git a/compiler/options.py b/compiler/options.py index 9fcc9105..1448a321 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -53,6 +53,14 @@ class options(optparse.Values): num_spare_rows = 0 num_spare_cols = 0 + ################### + # ROM configuration options + ################### + rom_endian = "little" + rom_data = None + strap_spacing = 8 + scramble_bits = True + ################### # Optimization options ################### diff --git a/compiler/rom.py b/compiler/rom.py new file mode 100644 index 00000000..b4f41889 --- /dev/null +++ b/compiler/rom.py @@ -0,0 +1,161 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import os +import shutil +import datetime +from openram import debug +from openram import rom_config as config +from openram import OPTS, print_time + + +class rom(): + """ + This is not a design module, but contains an ROM design instance. + """ + def __init__(self, rom_config=None, name=None): + + # Create default configs if custom config isn't provided + if rom_config is None: + rom_config = config(rom_data=OPTS.rom_data, + word_size=OPTS.word_size, + words_per_row=OPTS.words_per_row, + rom_endian=OPTS.rom_endian, + scramble_bits=OPTS.scramble_bits, + strap_spacing=OPTS.strap_spacing) + + if name is None: + name = OPTS.output_name + + rom_config.set_local_config(self) + + # reset the static duplicate name checker for unit tests + # in case we create more than one ROM + from openram.base import design + design.name_map=[] + + debug.info(2, "create rom of size {0} with {1} num of words".format(self.word_size, + self.num_words)) + start_time = datetime.datetime.now() + + self.name = name + + import openram.modules.rom_bank as rom + + self.r = rom(name, rom_config) + + self.r.create_netlist() + if not OPTS.netlist_only: + self.r.create_layout() + + if not OPTS.is_unit_test: + print_time("ROM creation", datetime.datetime.now(), start_time) + + def sp_write(self, name, lvs=False, trim=False): + self.r.sp_write(name, lvs, trim) + + def gds_write(self, name): + self.r.gds_write(name) + + def verilog_write(self, name): + self.r.verilog_write(name) + + def extended_config_write(self, name): + """Dump config file with all options. + Include defaults and anything changed by input config.""" + f = open(name, "w") + var_dict = dict((name, getattr(OPTS, name)) for name in dir(OPTS) if not name.startswith('__') and not callable(getattr(OPTS, name))) + for var_name, var_value in var_dict.items(): + if isinstance(var_value, str): + f.write(str(var_name) + " = " + "\"" + str(var_value) + "\"\n") + else: + f.write(str(var_name) + " = " + str(var_value)+ "\n") + f.close() + + def save(self): + """ Save all the output files while reporting time to do it as well. """ + + # Import this at the last minute so that the proper tech file + # is loaded and the right tools are selected + from openram import verify + + # Save the spice file + start_time = datetime.datetime.now() + spname = OPTS.output_path + self.r.name + ".sp" + debug.print_raw("SP: Writing to {0}".format(spname)) + self.sp_write(spname) + + print_time("Spice writing", datetime.datetime.now(), start_time) + + if not OPTS.netlist_only: + # Write the layout + start_time = datetime.datetime.now() + gdsname = OPTS.output_path + self.r.name + ".gds" + debug.print_raw("GDS: Writing to {0}".format(gdsname)) + self.gds_write(gdsname) + if OPTS.check_lvsdrc: + verify.write_drc_script(cell_name=self.r.name, + gds_name=os.path.basename(gdsname), + extract=True, + final_verification=True, + output_path=OPTS.output_path) + print_time("GDS", datetime.datetime.now(), start_time) + + # Save the LVS file + start_time = datetime.datetime.now() + lvsname = OPTS.output_path + self.r.name + ".lvs.sp" + debug.print_raw("LVS: Writing to {0}".format(lvsname)) + self.sp_write(lvsname, lvs=True) + if not OPTS.netlist_only and OPTS.check_lvsdrc: + verify.write_lvs_script(cell_name=self.r.name, + gds_name=os.path.basename(gdsname), + sp_name=os.path.basename(lvsname), + final_verification=True, + output_path=OPTS.output_path) + print_time("LVS writing", datetime.datetime.now(), start_time) + + # Save the extracted spice file + if OPTS.use_pex: + start_time = datetime.datetime.now() + # Output the extracted design if requested + pexname = OPTS.output_path + self.r.name + ".pex.sp" + spname = OPTS.output_path + self.r.name + ".sp" + verify.run_pex(self.r.name, gdsname, spname, output=pexname) + sp_file = pexname + print_time("Extraction", datetime.datetime.now(), start_time) + else: + # Use generated spice file for characterization + sp_file = spname + + # Save a functional simulation file + + # TODO: Characterize the design + + + # Write the config file + start_time = datetime.datetime.now() + from shutil import copyfile + copyfile(OPTS.config_file, OPTS.output_path + OPTS.output_name + '.py') + debug.print_raw("Config: Writing to {0}".format(OPTS.output_path + OPTS.output_name + '.py')) + print_time("Config", datetime.datetime.now(), start_time) + + # TODO: Write the datasheet + + # TODO: Write a verilog model + # start_time = datetime.datetime.now() + # vname = OPTS.output_path + self.r.name + '.v' + # debug.print_raw("Verilog: Writing to {0}".format(vname)) + # self.verilog_write(vname) + # print_time("Verilog", datetime.datetime.now(), start_time) + + # Write out options if specified + if OPTS.output_extended_config: + start_time = datetime.datetime.now() + oname = OPTS.output_path + OPTS.output_name + "_extended.py" + debug.print_raw("Extended Config: Writing to {0}".format(oname)) + self.extended_config_write(oname) + print_time("Extended Config", datetime.datetime.now(), start_time) diff --git a/compiler/rom_config.py b/compiler/rom_config.py new file mode 100644 index 00000000..9e67e37a --- /dev/null +++ b/compiler/rom_config.py @@ -0,0 +1,123 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from typing import List +from math import log, sqrt, ceil +from openram import debug +from openram.sram_factory import factory +from openram import OPTS + + + +class rom_config: + """ This is a structure that is used to hold the ROM configuration options. """ + + def __init__(self, word_size, rom_data, words_per_row=None, rom_endian="little", scramble_bits=True, strap_spacing=8): + self.word_size = word_size + self.word_bits = self.word_size * 8 + self.rom_data = rom_data + self.strap_spacing = strap_spacing + # TODO: This currently does nothing. It should change the behavior of the chunk funciton. + self.endian = rom_endian + + # This should pretty much always be true. If you want to make silicon art you might set to false + self.scramble_bits = scramble_bits + # This will get over-written when we determine the organization + self.words_per_row = words_per_row + + self.compute_sizes() + + def __str__(self): + """ override print function output """ + config_items = ["word_size", + "num_words", + "words_per_row", + "endian", + "strap_spacing", + "rom_data"] + str = "" + for item in config_items: + val = getattr(self, item) + str += "{} : {}\n".format(item, val) + return str + + def set_local_config(self, module): + """ Copy all of the member variables to the given module for convenience """ + + members = [attr for attr in dir(self) if not callable(getattr(self, attr)) and not attr.startswith("__")] + + # Copy all the variables to the local module + for member in members: + setattr(module, member, getattr(self, member)) + + def compute_sizes(self): + """ Computes the organization of the memory using data size by trying to make it a rectangle.""" + + # Read data as hexidecimal text file + hex_file = open(self.rom_data, 'r') + hex_data = hex_file.read() + + # Convert from hex into an int + data_int = int(hex_data, 16) + # Then from int into a right aligned, zero padded string + bin_string = bin(data_int)[2:].zfill(len(hex_data) * 4) + + # Then turn the string into a list of ints + bin_data = list(bin_string) + raw_data = [int(x) for x in bin_data] + + # data size in bytes + data_size = len(raw_data) / 8 + self.num_words = int(data_size / self.word_size) + + # If this was hard coded, don't dynamically compute it! + if not self.words_per_row: + + # Row size if the array was square + bytes_per_row = sqrt(self.num_words) + + # Heuristic to value longer wordlines over long bitlines. + # The extra factor of 2 in the denominator should make the array less square + self.words_per_row = int(ceil(bytes_per_row /(2*self.word_size))) + + self.cols = self.words_per_row * self.word_size * 8 + self.rows = int(self.num_words / self.words_per_row) + + self.chunk_data(raw_data) + + # Set word_per_row in OPTS + OPTS.words_per_row = self.words_per_row + debug.info(1, "Read rom data file: length {0} bytes, {1} words, set number of cols to {2}, rows to {3}, with {4} words per row".format(data_size, self.num_words, self.cols, self.rows, self.words_per_row)) + + + def chunk_data(self, raw_data: List[int]): + """ + Chunks a flat list of bits into rows based on the calculated ROM sizes. Handles scrambling of data + """ + bits_per_row = self.cols + + chunked_data = [] + + for i in range(0, len(raw_data), bits_per_row): + row_data = raw_data[i:i + bits_per_row] + if len(row_data) < bits_per_row: + row_data = [0] * (bits_per_row - len(row_data)) + row_data + chunked_data.append(row_data) + + self.data = chunked_data + + if self.scramble_bits: + scrambled_chunked = [] + + for row_data in chunked_data: + scambled_data = [] + for bit in range(self.word_bits): + for word in range(self.words_per_row): + scambled_data.append(row_data[bit + word * self.word_bits]) + scrambled_chunked.append(scambled_data) + self.data = scrambled_chunked + diff --git a/compiler/tests/04_column_mux_pbitcell_test.py b/compiler/tests/04_column_mux_pbitcell_test.py index e8d5038b..177f1eba 100755 --- a/compiler/tests/04_column_mux_pbitcell_test.py +++ b/compiler/tests/04_column_mux_pbitcell_test.py @@ -28,12 +28,10 @@ class column_mux_pbitcell_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 1 - factory.reset() debug.info(2, "Checking column mux for pbitcell (innermost connections)") tx = factory.create(module_type="column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") self.local_check(tx) - factory.reset() debug.info(2, "Checking column mux for pbitcell (outermost connections)") tx = factory.create(module_type="column_mux",tx_size=8, bitcell_bl="bl2", bitcell_br="br2") self.local_check(tx) diff --git a/compiler/tests/04_dummy_pbitcell_test.py b/compiler/tests/04_dummy_pbitcell_1rw1r1w_test.py similarity index 81% rename from compiler/tests/04_dummy_pbitcell_test.py rename to compiler/tests/04_dummy_pbitcell_1rw1r1w_test.py index 9043dbc6..a5d038ca 100755 --- a/compiler/tests/04_dummy_pbitcell_test.py +++ b/compiler/tests/04_dummy_pbitcell_1rw1r1w_test.py @@ -24,20 +24,10 @@ class replica_pbitcell_test(openram_test): from openram.modules import dummy_pbitcell OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 0 - OPTS.num_w_ports = 0 - - factory.reset() - debug.info(2, "Checking dummy bitcell using pbitcell (small cell)") - tx = dummy_pbitcell(name="rpbc") - self.local_check(tx) - OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 1 - factory.reset() debug.info(2, "Checking dummy bitcell using pbitcell (large cell)") tx = dummy_pbitcell(name="rpbc") self.local_check(tx) diff --git a/compiler/tests/04_dummy_pbitcell_1rw_test.py b/compiler/tests/04_dummy_pbitcell_1rw_test.py new file mode 100755 index 00000000..000856a5 --- /dev/null +++ b/compiler/tests/04_dummy_pbitcell_1rw_test.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import sys, os +import unittest +from testutils import * + +import openram +from openram import debug +from openram.sram_factory import factory +from openram import OPTS + + +class dummy_pbitcell_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + openram.init_openram(config_file, is_unit_test=True) + from openram.modules import dummy_pbitcell + + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + + debug.info(2, "Checking dummy bitcell using pbitcell (small cell)") + tx = dummy_pbitcell(name="rpbc") + self.local_check(tx) + + openram.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index d1ab4168..c0338015 100755 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -26,7 +26,6 @@ class pbitcell_test(openram_test): OPTS.num_rw_ports=1 OPTS.num_w_ports=1 OPTS.num_r_ports=1 - factory.reset() debug.info(2, "Bitcell with 1 of each port: read/write, write, and read") tx = factory.create(module_type="pbitcell") self.local_check(tx) @@ -34,7 +33,6 @@ class pbitcell_test(openram_test): OPTS.num_rw_ports=0 OPTS.num_w_ports=1 OPTS.num_r_ports=1 - factory.reset() debug.info(2, "Bitcell with 0 read/write ports") tx = factory.create(module_type="pbitcell") self.local_check(tx) @@ -42,7 +40,6 @@ class pbitcell_test(openram_test): OPTS.num_rw_ports=1 OPTS.num_w_ports=0 OPTS.num_r_ports=1 - factory.reset() debug.info(2, "Bitcell with 0 write ports") tx = factory.create(module_type="pbitcell") self.local_check(tx) @@ -50,7 +47,6 @@ class pbitcell_test(openram_test): OPTS.num_rw_ports=1 OPTS.num_w_ports=1 OPTS.num_r_ports=0 - factory.reset() debug.info(2, "Bitcell with 0 read ports") tx = factory.create(module_type="pbitcell") self.local_check(tx) @@ -58,7 +54,6 @@ class pbitcell_test(openram_test): OPTS.num_rw_ports=1 OPTS.num_w_ports=0 OPTS.num_r_ports=0 - factory.reset() debug.info(2, "Bitcell with 0 read ports and 0 write ports") tx = factory.create(module_type="pbitcell") self.local_check(tx) @@ -66,7 +61,6 @@ class pbitcell_test(openram_test): OPTS.num_rw_ports=2 OPTS.num_w_ports=2 OPTS.num_r_ports=2 - factory.reset() debug.info(2, "Bitcell with 2 of each port: read/write, write, and read") tx = factory.create(module_type="pbitcell") self.local_check(tx) @@ -74,7 +68,6 @@ class pbitcell_test(openram_test): OPTS.num_rw_ports=0 OPTS.num_w_ports=2 OPTS.num_r_ports=2 - factory.reset() debug.info(2, "Bitcell with 0 read/write ports") tx = factory.create(module_type="pbitcell") self.local_check(tx) @@ -82,7 +75,6 @@ class pbitcell_test(openram_test): OPTS.num_rw_ports=2 OPTS.num_w_ports=0 OPTS.num_r_ports=2 - factory.reset() debug.info(2, "Bitcell with 0 write ports") tx = factory.create(module_type="pbitcell") self.local_check(tx) @@ -90,7 +82,6 @@ class pbitcell_test(openram_test): OPTS.num_rw_ports=2 OPTS.num_w_ports=2 OPTS.num_r_ports=0 - factory.reset() debug.info(2, "Bitcell with 0 read ports") tx = factory.create(module_type="pbitcell") self.local_check(tx) @@ -98,7 +89,6 @@ class pbitcell_test(openram_test): OPTS.num_rw_ports=2 OPTS.num_w_ports=0 OPTS.num_r_ports=0 - factory.reset() debug.info(2, "Bitcell with 0 read ports and 0 write ports") tx = factory.create(module_type="pbitcell") self.local_check(tx) diff --git a/compiler/tests/04_precharge_1rw_1r_test.py b/compiler/tests/04_precharge_1rw_1r_test.py index 59f01a73..9b422ca5 100755 --- a/compiler/tests/04_precharge_1rw_1r_test.py +++ b/compiler/tests/04_precharge_1rw_1r_test.py @@ -32,7 +32,6 @@ class precharge_test(openram_test): tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl0", bitcell_br="br0") self.local_check(tx) - factory.reset() debug.info(2, "Checking precharge for 1rw1r port 1") tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl1", bitcell_br="br1") self.local_check(tx) diff --git a/compiler/tests/04_precharge_pbitcell_test.py b/compiler/tests/04_precharge_pbitcell_test.py index 4a228b6e..71024200 100755 --- a/compiler/tests/04_precharge_pbitcell_test.py +++ b/compiler/tests/04_precharge_pbitcell_test.py @@ -28,17 +28,14 @@ class precharge_pbitcell_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 1 - factory.reset() debug.info(2, "Checking precharge for pbitcell (innermost connections)") tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl0", bitcell_br="br0") self.local_check(tx) - factory.reset() debug.info(2, "Checking precharge for pbitcell (innermost connections)") tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl1", bitcell_br="br1") self.local_check(tx) - factory.reset() debug.info(2, "Checking precharge for pbitcell (outermost connections)") tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl2", bitcell_br="br2") self.local_check(tx) diff --git a/compiler/tests/04_replica_pbitcell_test.py b/compiler/tests/04_replica_pbitcell_1rw1r1w_test.py similarity index 81% rename from compiler/tests/04_replica_pbitcell_test.py rename to compiler/tests/04_replica_pbitcell_1rw1r1w_test.py index 00252a84..5af89b43 100755 --- a/compiler/tests/04_replica_pbitcell_test.py +++ b/compiler/tests/04_replica_pbitcell_1rw1r1w_test.py @@ -24,20 +24,10 @@ class replica_pbitcell_test(openram_test): from openram.modules import replica_pbitcell OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 0 - OPTS.num_w_ports = 0 - - factory.reset() - debug.info(2, "Checking replica bitcell using pbitcell (small cell)") - tx = replica_pbitcell(name="rpbc") - self.local_check(tx) - OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 1 - factory.reset() debug.info(2, "Checking replica bitcell using pbitcell (large cell)") tx = replica_pbitcell(name="rpbc") self.local_check(tx) diff --git a/compiler/tests/04_replica_pbitcell_1rw_test.py b/compiler/tests/04_replica_pbitcell_1rw_test.py new file mode 100755 index 00000000..ef6a1cd9 --- /dev/null +++ b/compiler/tests/04_replica_pbitcell_1rw_test.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import sys, os +import unittest +from testutils import * + +import openram +from openram import debug +from openram.sram_factory import factory +from openram import OPTS + + +class replica_pbitcell_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + openram.init_openram(config_file, is_unit_test=True) + from openram.modules import replica_pbitcell + + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + + debug.info(2, "Checking replica bitcell using pbitcell (small cell)") + tx = replica_pbitcell(name="rpbc") + self.local_check(tx) + + openram.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py index 3b731b31..e77922f6 100755 --- a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py @@ -27,17 +27,14 @@ class hierarchical_decoder_pbitcell_test(openram_test): OPTS.num_r_ports = 0 openram.setup_bitcell() - factory.reset() debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)") a = factory.create(module_type="hierarchical_decoder", num_outputs=16) self.local_check(a) - factory.reset() debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)") a = factory.create(module_type="hierarchical_decoder", num_outputs=17) self.local_check(a) - factory.reset() debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)") a = factory.create(module_type="hierarchical_decoder", num_outputs=23) self.local_check(a) @@ -46,7 +43,6 @@ class hierarchical_decoder_pbitcell_test(openram_test): a = factory.create(module_type="hierarchical_decoder", num_outputs=32) self.local_check(a) - factory.reset() debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)") a = factory.create(module_type="hierarchical_decoder", num_outputs=65) self.local_check(a) @@ -55,7 +51,6 @@ class hierarchical_decoder_pbitcell_test(openram_test): a = factory.create(module_type="hierarchical_decoder", num_outputs=128) self.local_check(a) - factory.reset() debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)") a = factory.create(module_type="hierarchical_decoder", num_outputs=341) self.local_check(a) diff --git a/compiler/tests/06_rom_decoder_test.py b/compiler/tests/06_rom_decoder_test.py new file mode 100755 index 00000000..d6e5d27a --- /dev/null +++ b/compiler/tests/06_rom_decoder_test.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import openram +from openram import OPTS +from openram.sram_factory import factory +from openram import debug + + +class rom_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + openram.init_openram(config_file, is_unit_test=True) + + debug.info(2, "Testing 2x4 decoder for rom cell") + + + a = factory.create(module_type="rom_decoder", num_outputs=16, strap_spacing=4, fanout=16) + self.local_check(a) + openram.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/07_column_mux_array_pbitcell_test.py b/compiler/tests/07_column_mux_array_pbitcell_test.py index 020696fa..9f082218 100755 --- a/compiler/tests/07_column_mux_array_pbitcell_test.py +++ b/compiler/tests/07_column_mux_array_pbitcell_test.py @@ -27,7 +27,6 @@ class column_mux_pbitcell_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 1 - factory.reset() debug.info(1, "Testing sample for 2-way column_mux_array in multi-port") a = factory.create(module_type="column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) diff --git a/compiler/tests/07_rom_column_mux_array_test.py b/compiler/tests/07_rom_column_mux_array_test.py new file mode 100755 index 00000000..c937e834 --- /dev/null +++ b/compiler/tests/07_rom_column_mux_array_test.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import sys, os +from testutils import * + +import openram +from openram import debug +from openram.sram_factory import factory +from openram import OPTS + + +class rom_column_mux_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + openram.init_openram(config_file, is_unit_test=True) + + debug.info(1, "Testing sample for 2-way rom column_mux_array") + a = factory.create(module_type="rom_column_mux_array", columns=16, word_size=8) + self.local_check(a) + + openram.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/08_precharge_array_1rw_1r_test.py b/compiler/tests/08_precharge_array_1rw_1r_test.py index e9b2bce4..739d8006 100755 --- a/compiler/tests/08_precharge_array_1rw_1r_test.py +++ b/compiler/tests/08_precharge_array_1rw_1r_test.py @@ -28,7 +28,6 @@ class precharge_1rw_1r_test(openram_test): OPTS.num_w_ports = 0 openram.setup_bitcell() - factory.reset() debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell (port 0)") pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0") self.local_check(pc) diff --git a/compiler/tests/08_rom_decoder_buffer_array_test.py b/compiler/tests/08_rom_decoder_buffer_array_test.py new file mode 100755 index 00000000..7231863d --- /dev/null +++ b/compiler/tests/08_rom_decoder_buffer_array_test.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import openram +from openram import OPTS +from openram.sram_factory import factory +from openram import debug + + +class rom_decoder_buffer_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + openram.init_openram(config_file, is_unit_test=True) + + debug.info(2, "Testing 4 col decoder buffer for rom decoder") + + + a = factory.create(module_type="rom_address_control_array", cols=4) + self.local_check(a) + openram.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/08_rom_precharge_array_test.py b/compiler/tests/08_rom_precharge_array_test.py new file mode 100755 index 00000000..7d585e66 --- /dev/null +++ b/compiler/tests/08_rom_precharge_array_test.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import openram +from openram import OPTS +from openram.sram_factory import factory +from openram import debug + + +class rom_precharge_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + openram.init_openram(config_file, is_unit_test=True) + + debug.info(2, "Testing precharge array for rom cell") + + + a = factory.create(module_type="rom_precharge_array", cols=4, strap_spacing=2) + self.local_check(a) + openram.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/08_wordline_driver_array_pbitcell_test.py b/compiler/tests/08_wordline_driver_array_pbitcell_test.py index 5162bf1f..9eb3bdfd 100755 --- a/compiler/tests/08_wordline_driver_array_pbitcell_test.py +++ b/compiler/tests/08_wordline_driver_array_pbitcell_test.py @@ -28,7 +28,6 @@ class wordline_driver_array_pbitcell_test(openram_test): OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - factory.reset() debug.info(2, "Checking driver (multi-port case)") tx = factory.create(module_type="wordline_driver_array", rows=8, cols=64) self.local_check(tx) diff --git a/compiler/tests/09_sense_amp_array_pbitcell_test.py b/compiler/tests/09_sense_amp_array_pbitcell_test.py index ae1186b7..2040b4b2 100755 --- a/compiler/tests/09_sense_amp_array_pbitcell_test.py +++ b/compiler/tests/09_sense_amp_array_pbitcell_test.py @@ -28,7 +28,6 @@ class sense_amp_pbitcell_test(openram_test): OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - factory.reset() debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2 (multi-port case)") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=2) self.local_check(a) diff --git a/compiler/tests/09_sense_amp_array_spare_cols_test.py b/compiler/tests/09_sense_amp_array_spare_cols_test.py index c39fd094..981cc6ae 100755 --- a/compiler/tests/09_sense_amp_array_spare_cols_test.py +++ b/compiler/tests/09_sense_amp_array_spare_cols_test.py @@ -37,7 +37,6 @@ class sense_amp_array_spare_cols_test(openram_test): OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - factory.reset() debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2, num_spare_cols=2 (multi-port case)") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=2, num_spare_cols=2) self.local_check(a) diff --git a/compiler/tests/10_rom_wordline_driver_array_test.py b/compiler/tests/10_rom_wordline_driver_array_test.py new file mode 100755 index 00000000..f3da0e75 --- /dev/null +++ b/compiler/tests/10_rom_wordline_driver_array_test.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import sys, os +import unittest +from testutils import * + +import openram +from openram import debug +from openram.sram_factory import factory +from openram import OPTS + + +class wordline_driver_array_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + openram.init_openram(config_file, is_unit_test=True) + + # check wordline driver for single port + debug.info(2, "Checking driver") + tx = factory.create(module_type="rom_wordline_driver_array", rows=8, fanout=32) + self.local_check(tx) + + openram.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/10_write_driver_array_pbitcell_test.py b/compiler/tests/10_write_driver_array_pbitcell_test.py index 1a72989b..e5914935 100755 --- a/compiler/tests/10_write_driver_array_pbitcell_test.py +++ b/compiler/tests/10_write_driver_array_pbitcell_test.py @@ -28,7 +28,6 @@ class write_driver_pbitcell_test(openram_test): OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - factory.reset() debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case)") a = factory.create(module_type="write_driver_array", columns=8, word_size=8) self.local_check(a) diff --git a/compiler/tests/10_write_driver_array_spare_cols_test.py b/compiler/tests/10_write_driver_array_spare_cols_test.py index ef0c3596..a1926a90 100755 --- a/compiler/tests/10_write_driver_array_spare_cols_test.py +++ b/compiler/tests/10_write_driver_array_spare_cols_test.py @@ -37,7 +37,6 @@ class write_driver_array_spare_cols_test(openram_test): OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - factory.reset() debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case and num_spare_cols=3") a = factory.create(module_type="write_driver_array", columns=8, word_size=8, num_spare_cols=3) self.local_check(a) diff --git a/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py b/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py index 69054586..7bc6704d 100755 --- a/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py +++ b/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py @@ -28,7 +28,6 @@ class write_driver_pbitcell_test(openram_test): OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - factory.reset() debug.info(2, "Testing write_driver_array for columns=8, word_size=8, write_size=4 (multi-port case)") a = factory.create(module_type="write_driver_array", columns=8, word_size=8, write_size=4) self.local_check(a) diff --git a/compiler/tests/10_write_mask_and_array_pbitcell_test.py b/compiler/tests/10_write_mask_and_array_pbitcell_test.py index 3d79d4dd..e19bfd27 100755 --- a/compiler/tests/10_write_mask_and_array_pbitcell_test.py +++ b/compiler/tests/10_write_mask_and_array_pbitcell_test.py @@ -28,7 +28,6 @@ class write_mask_and_array_pbitcell_test(openram_test): OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - factory.reset() debug.info(2, "Testing write_mask_and_array for columns=8, word_size=8, write_size=4 (multi-port case)") a = factory.create(module_type="write_mask_and_array", columns=8, word_size=8, write_size=4) self.local_check(a) diff --git a/compiler/tests/14_capped_replica_bitcell_array_leftrbl_1rw_test.py b/compiler/tests/14_capped_replica_bitcell_array_leftrbl_1rw_test.py index b4ba91d1..61464b9b 100755 --- a/compiler/tests/14_capped_replica_bitcell_array_leftrbl_1rw_test.py +++ b/compiler/tests/14_capped_replica_bitcell_array_leftrbl_1rw_test.py @@ -24,7 +24,6 @@ class capped_replica_bitcell_array_test(openram_test): OPTS.num_r_ports = 0 OPTS.num_w_ports = 0 - factory.reset() debug.info(2, "Testing 4x4 array for bitcell") a = factory.create(module_type="capped_replica_bitcell_array", cols=7, rows=5, rbl=[1, 0], left_rbl=[0]) self.local_check(a) diff --git a/compiler/tests/14_capped_replica_bitcell_array_norbl_1rw_test.py b/compiler/tests/14_capped_replica_bitcell_array_norbl_1rw_test.py index 175689c3..070404ac 100755 --- a/compiler/tests/14_capped_replica_bitcell_array_norbl_1rw_test.py +++ b/compiler/tests/14_capped_replica_bitcell_array_norbl_1rw_test.py @@ -24,7 +24,6 @@ class capped_replica_bitcell_array_test(openram_test): OPTS.num_r_ports = 0 OPTS.num_w_ports = 0 - factory.reset() debug.info(2, "Testing 4x4 array for bitcell") a = factory.create(module_type="capped_replica_bitcell_array", cols=7, rows=5, rbl=[1, 0]) self.local_check(a) diff --git a/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py b/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py index f77a571d..b8d5e5ed 100755 --- a/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py +++ b/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py @@ -24,7 +24,6 @@ class replica_bitcell_array_test(openram_test): OPTS.num_r_ports = 0 OPTS.num_w_ports = 0 - factory.reset() debug.info(2, "Testing 4x4 array for bitcell") a = factory.create(module_type="replica_bitcell_array", cols=7, rows=5, rbl=[1, 0], left_rbl=[0]) self.local_check(a) diff --git a/compiler/tests/14_replica_bitcell_array_norbl_1rw_test.py b/compiler/tests/14_replica_bitcell_array_norbl_1rw_test.py index e893c3ca..219bd680 100755 --- a/compiler/tests/14_replica_bitcell_array_norbl_1rw_test.py +++ b/compiler/tests/14_replica_bitcell_array_norbl_1rw_test.py @@ -24,7 +24,6 @@ class replica_bitcell_array_test(openram_test): OPTS.num_r_ports = 0 OPTS.num_w_ports = 0 - factory.reset() debug.info(2, "Testing 4x4 array for bitcell") a = factory.create(module_type="replica_bitcell_array", cols=7, rows=5, rbl=[1, 0]) self.local_check(a) diff --git a/compiler/tests/14_replica_pbitcell_array_test.py b/compiler/tests/14_replica_pbitcell_1rw1r_array_test.py similarity index 73% rename from compiler/tests/14_replica_pbitcell_array_test.py rename to compiler/tests/14_replica_pbitcell_1rw1r_array_test.py index cb143c40..6ef53ed3 100755 --- a/compiler/tests/14_replica_pbitcell_array_test.py +++ b/compiler/tests/14_replica_pbitcell_1rw1r_array_test.py @@ -31,18 +31,6 @@ class replica_pbitcell_array_test(openram_test): a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, rbl=[1, 1], left_rbl=[0], right_rbl=[1]) self.local_check(a) - OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell = "replica_pbitcell" - OPTS.dummy_bitcell = "dummy_pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 0 - OPTS.num_w_ports = 0 - - factory.reset() - debug.info(2, "Testing 4x4 array for pbitcell") - a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, rbl=[1, 0], left_rbl=[0]) - self.local_check(a) - openram.end_openram() diff --git a/compiler/tests/14_replica_pbitcell_1rw_array_test.py b/compiler/tests/14_replica_pbitcell_1rw_array_test.py new file mode 100755 index 00000000..154fc914 --- /dev/null +++ b/compiler/tests/14_replica_pbitcell_1rw_array_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz +# All rights reserved. +# +import sys, os +import unittest +from testutils import * + +import openram +from openram import debug +from openram.sram_factory import factory +from openram import OPTS + + +class replica_pbitcell_array_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + openram.init_openram(config_file, is_unit_test=True) + + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell = "replica_pbitcell" + OPTS.dummy_bitcell = "dummy_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + + debug.info(2, "Testing 4x4 array for pbitcell") + a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, rbl=[1, 0], left_rbl=[0]) + self.local_check(a) + + openram.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/14_rom_array_test.py b/compiler/tests/14_rom_array_test.py new file mode 100755 index 00000000..c7a03ea1 --- /dev/null +++ b/compiler/tests/14_rom_array_test.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import sys, os +import unittest +from testutils import * + +import openram +from openram import debug +from openram.sram_factory import factory +from openram import OPTS + + +class rom_array_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + openram.init_openram(config_file, is_unit_test=True) + + debug.info(2, "Testing 4x4 array for rom cell") + + + data = [[1, 0, 0, 1, 0, 0, 1, 1, 0], [0, 1, 0, 0, 1, 0, 1, 0, 0], [0, 0, 1, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 1], [0, 1, 0, 0, 1, 1, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 0, 0, 1, 0, 0], [1, 0, 0, 1, 0, 0, 0, 1, 0]] + + a = factory.create(module_type="rom_base_array", cols=9, rows=8, bitmap=data, strap_spacing=4, pitch_match=True) + self.local_check(a) + a.sp_write(OPTS.openram_temp + 'simulation_file.sp') + + openram.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/16_rom_control_logic_test.py b/compiler/tests/16_rom_control_logic_test.py new file mode 100755 index 00000000..12b1fbdb --- /dev/null +++ b/compiler/tests/16_rom_control_logic_test.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import openram +from openram import OPTS +from openram.sram_factory import factory +from openram import debug + + +class rom_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + openram.init_openram(config_file, is_unit_test=True) + + debug.info(2, "Testing control logic for rom cell") + + + a = factory.create(module_type="rom_control_logic", num_outputs=4, clk_fanout=4, height=40) + self.local_check(a) + openram.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_16mux_1rw_1r_test.py b/compiler/tests/18_port_data_16mux_1rw_1r_test.py index 57947481..b4b50323 100755 --- a/compiler/tests/18_port_data_16mux_1rw_1r_test.py +++ b/compiler/tests/18_port_data_16mux_1rw_1r_test.py @@ -32,7 +32,6 @@ class port_data_1rw_1r_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=16 - factory.reset() c.recompute_sizes() debug.info(1, "Sixteen way column mux") a = factory.create("port_data", sram_config=c, port=0) diff --git a/compiler/tests/18_port_data_16mux_test.py b/compiler/tests/18_port_data_16mux_test.py index 0b1859e4..66d4c4bf 100755 --- a/compiler/tests/18_port_data_16mux_test.py +++ b/compiler/tests/18_port_data_16mux_test.py @@ -36,7 +36,6 @@ class port_data_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=16 - factory.reset() c.recompute_sizes() debug.info(1, "Sixteen way column mux") a = factory.create("port_data", sram_config=c, port=0) diff --git a/compiler/tests/18_port_data_2mux_1rw_1r_test.py b/compiler/tests/18_port_data_2mux_1rw_1r_test.py index d9ca31ea..fc0d9243 100755 --- a/compiler/tests/18_port_data_2mux_1rw_1r_test.py +++ b/compiler/tests/18_port_data_2mux_1rw_1r_test.py @@ -31,7 +31,6 @@ class port_data_1rw_1r_test(openram_test): c.num_words=32 c.words_per_row=2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create("port_data", sram_config=c, port=0) diff --git a/compiler/tests/18_port_data_2mux_test.py b/compiler/tests/18_port_data_2mux_test.py index 469d3e34..8eb29615 100755 --- a/compiler/tests/18_port_data_2mux_test.py +++ b/compiler/tests/18_port_data_2mux_test.py @@ -35,7 +35,6 @@ class port_data_test(openram_test): c.num_words=32 c.words_per_row=2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create("port_data", sram_config=c, port=0) diff --git a/compiler/tests/18_port_data_4mux_1rw_1r_test.py b/compiler/tests/18_port_data_4mux_1rw_1r_test.py index edd2336b..ac81e561 100755 --- a/compiler/tests/18_port_data_4mux_1rw_1r_test.py +++ b/compiler/tests/18_port_data_4mux_1rw_1r_test.py @@ -31,7 +31,6 @@ class port_data_1rw_1r_test(openram_test): c.num_words=64 c.words_per_row=4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create("port_data", sram_config=c, port=0) diff --git a/compiler/tests/18_port_data_4mux_test.py b/compiler/tests/18_port_data_4mux_test.py index 12ef31d2..c3eae434 100755 --- a/compiler/tests/18_port_data_4mux_test.py +++ b/compiler/tests/18_port_data_4mux_test.py @@ -35,7 +35,6 @@ class port_data_test(openram_test): c.num_words=64 c.words_per_row=4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create("port_data", sram_config=c, port=0) diff --git a/compiler/tests/18_port_data_8mux_1rw_1r_test.py b/compiler/tests/18_port_data_8mux_1rw_1r_test.py index 683a086f..e57d12ef 100755 --- a/compiler/tests/18_port_data_8mux_1rw_1r_test.py +++ b/compiler/tests/18_port_data_8mux_1rw_1r_test.py @@ -32,7 +32,6 @@ class port_data_1rw_1r_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create("port_data", sram_config=c, port=0) diff --git a/compiler/tests/18_port_data_8mux_test.py b/compiler/tests/18_port_data_8mux_test.py index ae067163..a8f82489 100755 --- a/compiler/tests/18_port_data_8mux_test.py +++ b/compiler/tests/18_port_data_8mux_test.py @@ -36,7 +36,6 @@ class port_data_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create("port_data", sram_config=c, port=0) diff --git a/compiler/tests/18_port_data_nomux_1rw_1r_test.py b/compiler/tests/18_port_data_nomux_1rw_1r_test.py index 04cbddd8..8b887d9b 100755 --- a/compiler/tests/18_port_data_nomux_1rw_1r_test.py +++ b/compiler/tests/18_port_data_nomux_1rw_1r_test.py @@ -30,7 +30,6 @@ class port_data_1rw_1r_test(openram_test): num_words=16) c.words_per_row=1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create("port_data", sram_config=c, port=0) diff --git a/compiler/tests/18_port_data_nomux_test.py b/compiler/tests/18_port_data_nomux_test.py index 07836e3d..e479de1f 100755 --- a/compiler/tests/18_port_data_nomux_test.py +++ b/compiler/tests/18_port_data_nomux_test.py @@ -34,7 +34,6 @@ class port_data_test(openram_test): num_spare_rows=num_spare_rows) c.words_per_row=1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create("port_data", sram_config=c, port=0) diff --git a/compiler/tests/18_port_data_spare_cols_test.py b/compiler/tests/18_port_data_spare_cols_test.py index c511dc5c..0db34da0 100755 --- a/compiler/tests/18_port_data_spare_cols_test.py +++ b/compiler/tests/18_port_data_spare_cols_test.py @@ -26,7 +26,6 @@ class port_data_spare_cols_test(openram_test): num_spare_cols=3) c.words_per_row=1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -34,7 +33,6 @@ class port_data_spare_cols_test(openram_test): c.num_words=32 c.words_per_row=2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -43,7 +41,6 @@ class port_data_spare_cols_test(openram_test): c.num_words=64 c.words_per_row=4 c.num_spare_cols=3 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -53,7 +50,6 @@ class port_data_spare_cols_test(openram_test): c.num_words=128 c.words_per_row=8 c.num_spare_cols=4 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -66,7 +62,6 @@ class port_data_spare_cols_test(openram_test): c.num_words=16 c.words_per_row=1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -76,7 +71,6 @@ class port_data_spare_cols_test(openram_test): c.num_words=32 c.words_per_row=2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -86,7 +80,6 @@ class port_data_spare_cols_test(openram_test): c.num_words=64 c.words_per_row=4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -97,7 +90,6 @@ class port_data_spare_cols_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create("port_data", sram_config=c, port=0) diff --git a/compiler/tests/18_port_data_wmask_1rw_1r_test.py b/compiler/tests/18_port_data_wmask_1rw_1r_test.py index ee3278d2..b731a1d0 100755 --- a/compiler/tests/18_port_data_wmask_1rw_1r_test.py +++ b/compiler/tests/18_port_data_wmask_1rw_1r_test.py @@ -31,7 +31,6 @@ class port_data_wmask_1rw_1r_test(openram_test): num_words=16) c.words_per_row = 1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -39,7 +38,6 @@ class port_data_wmask_1rw_1r_test(openram_test): c.num_words = 32 c.words_per_row = 2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -47,7 +45,6 @@ class port_data_wmask_1rw_1r_test(openram_test): c.num_words = 64 c.words_per_row = 4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -55,7 +52,6 @@ class port_data_wmask_1rw_1r_test(openram_test): c.num_words = 128 c.words_per_row = 8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -68,7 +64,6 @@ class port_data_wmask_1rw_1r_test(openram_test): c.num_words = 16 c.words_per_row = 1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -78,7 +73,6 @@ class port_data_wmask_1rw_1r_test(openram_test): # c.num_words = 32 c.words_per_row = 2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -88,7 +82,6 @@ class port_data_wmask_1rw_1r_test(openram_test): c.num_words = 64 c.words_per_row = 4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -99,7 +92,6 @@ class port_data_wmask_1rw_1r_test(openram_test): c.word_size = 8 c.num_words = 128 c.words_per_row = 8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create("port_data", sram_config=c, port=0) diff --git a/compiler/tests/18_port_data_wmask_test.py b/compiler/tests/18_port_data_wmask_test.py index e1860e6e..3db910c1 100755 --- a/compiler/tests/18_port_data_wmask_test.py +++ b/compiler/tests/18_port_data_wmask_test.py @@ -35,7 +35,6 @@ class port_data_wmask_test(openram_test): num_spare_rows=num_spare_rows) c.words_per_row = 1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -43,7 +42,6 @@ class port_data_wmask_test(openram_test): c.num_words = 32 c.words_per_row = 2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -51,7 +49,6 @@ class port_data_wmask_test(openram_test): c.num_words = 64 c.words_per_row = 4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -59,7 +56,6 @@ class port_data_wmask_test(openram_test): c.num_words = 128 c.words_per_row = 8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -72,7 +68,6 @@ class port_data_wmask_test(openram_test): c.num_words = 16 c.words_per_row = 1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -82,7 +77,6 @@ class port_data_wmask_test(openram_test): # c.num_words = 32 c.words_per_row = 2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -92,7 +86,6 @@ class port_data_wmask_test(openram_test): c.num_words = 64 c.words_per_row = 4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create("port_data", sram_config=c, port=0) @@ -103,7 +96,6 @@ class port_data_wmask_test(openram_test): c.word_size = 8 c.num_words = 128 c.words_per_row = 8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create("port_data", sram_config=c, port=0) diff --git a/compiler/tests/19_multi_bank_test.py b/compiler/tests/19_multi_bank_test.py index a1fb9b57..c1f408e0 100755 --- a/compiler/tests/19_multi_bank_test.py +++ b/compiler/tests/19_multi_bank_test.py @@ -29,7 +29,6 @@ class multi_bank_test(openram_test): c.num_banks=2 c.words_per_row=1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create("bank", sram_config=c) @@ -37,7 +36,6 @@ class multi_bank_test(openram_test): c.num_words=32 c.words_per_row=2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create("bank", sram_config=c) @@ -45,7 +43,6 @@ class multi_bank_test(openram_test): c.num_words=64 c.words_per_row=4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create("bank", sram_config=c) @@ -54,7 +51,6 @@ class multi_bank_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create("bank", sram_config=c) diff --git a/compiler/tests/19_pmulti_bank_test.py b/compiler/tests/19_pmulti_bank_test.py index 4e39a32c..378ff837 100755 --- a/compiler/tests/19_pmulti_bank_test.py +++ b/compiler/tests/19_pmulti_bank_test.py @@ -34,7 +34,6 @@ class multi_bank_test(openram_test): c.num_banks=2 c.words_per_row=1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create("bank", sram_config=c) @@ -42,7 +41,6 @@ class multi_bank_test(openram_test): c.num_words=32 c.words_per_row=2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create("bank", sram_config=c) @@ -50,7 +48,6 @@ class multi_bank_test(openram_test): c.num_words=64 c.words_per_row=4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create("bank", sram_config=c) @@ -59,7 +56,6 @@ class multi_bank_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create("bank", sram_config=c) diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py index 205b6bff..cf38b68b 100755 --- a/compiler/tests/19_psingle_bank_test.py +++ b/compiler/tests/19_psingle_bank_test.py @@ -36,7 +36,6 @@ class psingle_bank_test(openram_test): num_words=16) c.words_per_row=1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create(module_type="bank", sram_config=c) @@ -44,7 +43,6 @@ class psingle_bank_test(openram_test): c.num_words=32 c.words_per_row=2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create(module_type="bank", sram_config=c) @@ -52,7 +50,6 @@ class psingle_bank_test(openram_test): c.num_words=64 c.words_per_row=4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create(module_type="bank", sram_config=c) @@ -61,7 +58,6 @@ class psingle_bank_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=8 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create(module_type="bank", sram_config=c) diff --git a/compiler/tests/19_rom_bank_test.py b/compiler/tests/19_rom_bank_test.py new file mode 100755 index 00000000..4a3233e8 --- /dev/null +++ b/compiler/tests/19_rom_bank_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import openram +from openram import OPTS +from openram.sram_factory import factory +from openram import debug + + +class rom_bank_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + openram.init_openram(config_file, is_unit_test=True) + debug.info(1, "Testing rom cell") + test_data = "{0}/{1}/rom_data_64B".format(os.getenv("OPENRAM_HOME"), OPTS.rom_data_dir) + + from openram import rom_config + + conf = rom_config(strap_spacing = 8, + rom_data = test_data, + word_size = 1) + + a = factory.create(module_type="rom_bank", rom_config=conf) + a.sp_write(OPTS.openram_temp + 'simulation_file.sp') + self.local_check(a) + + openram.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_single_bank_16mux_1rw_1r_test.py b/compiler/tests/19_single_bank_16mux_1rw_1r_test.py index 9ce9ee22..4d31ea38 100755 --- a/compiler/tests/19_single_bank_16mux_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_16mux_1rw_1r_test.py @@ -32,7 +32,6 @@ class single_bank_1rw_1r_test(openram_test): num_words=128) c.words_per_row=16 - factory.reset() c.recompute_sizes() debug.info(1, "Sixteen way column mux") a = factory.create(module_type="bank", sram_config=c) diff --git a/compiler/tests/19_single_bank_16mux_test.py b/compiler/tests/19_single_bank_16mux_test.py index 889d6e95..2bc53ace 100755 --- a/compiler/tests/19_single_bank_16mux_test.py +++ b/compiler/tests/19_single_bank_16mux_test.py @@ -38,7 +38,6 @@ class single_bank_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=16 - factory.reset() c.recompute_sizes() debug.info(1, "Sixteen way column mux") a = factory.create("bank", sram_config=c) diff --git a/compiler/tests/19_single_bank_1w_1r_test.py b/compiler/tests/19_single_bank_1w_1r_test.py index ed3594e6..428f6dc6 100755 --- a/compiler/tests/19_single_bank_1w_1r_test.py +++ b/compiler/tests/19_single_bank_1w_1r_test.py @@ -32,7 +32,6 @@ class single_bank_1w_1r_test(openram_test): num_words=16) c.words_per_row=1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create(module_type="bank", sram_config=c) @@ -40,7 +39,6 @@ class single_bank_1w_1r_test(openram_test): c.num_words=32 c.words_per_row=2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create(module_type="bank", sram_config=c) @@ -48,7 +46,6 @@ class single_bank_1w_1r_test(openram_test): c.num_words=64 c.words_per_row=4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create(module_type="bank", sram_config=c) @@ -57,7 +54,6 @@ class single_bank_1w_1r_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create(module_type="bank", sram_config=c) diff --git a/compiler/tests/19_single_bank_2mux_1rw_1r_test.py b/compiler/tests/19_single_bank_2mux_1rw_1r_test.py index 14400ece..e81d1bbc 100755 --- a/compiler/tests/19_single_bank_2mux_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_2mux_1rw_1r_test.py @@ -33,7 +33,6 @@ class single_bank_1rw_1r_test(openram_test): c.num_words=32 c.words_per_row=2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create(module_type="bank", sram_config=c) diff --git a/compiler/tests/19_single_bank_2mux_test.py b/compiler/tests/19_single_bank_2mux_test.py index 897ad100..a1679f5c 100755 --- a/compiler/tests/19_single_bank_2mux_test.py +++ b/compiler/tests/19_single_bank_2mux_test.py @@ -28,7 +28,6 @@ class single_bank_test(openram_test): c.num_words=32 c.words_per_row=2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create("bank", sram_config=c) diff --git a/compiler/tests/19_single_bank_4mux_1rw_1r_test.py b/compiler/tests/19_single_bank_4mux_1rw_1r_test.py index 588055ec..0c1f3439 100755 --- a/compiler/tests/19_single_bank_4mux_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_4mux_1rw_1r_test.py @@ -33,7 +33,6 @@ class single_bank_1rw_1r_test(openram_test): c.num_words=64 c.words_per_row=4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create(module_type="bank", sram_config=c) diff --git a/compiler/tests/19_single_bank_4mux_test.py b/compiler/tests/19_single_bank_4mux_test.py index def5a5bc..94219f97 100755 --- a/compiler/tests/19_single_bank_4mux_test.py +++ b/compiler/tests/19_single_bank_4mux_test.py @@ -28,7 +28,6 @@ class single_bank_test(openram_test): c.num_words=64 c.words_per_row=4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create("bank", sram_config=c) diff --git a/compiler/tests/19_single_bank_8mux_1rw_1r_test.py b/compiler/tests/19_single_bank_8mux_1rw_1r_test.py index 7e1c9bb4..2a12397c 100755 --- a/compiler/tests/19_single_bank_8mux_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_8mux_1rw_1r_test.py @@ -34,7 +34,6 @@ class single_bank_1rw_1r_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create(module_type="bank", sram_config=c) diff --git a/compiler/tests/19_single_bank_8mux_test.py b/compiler/tests/19_single_bank_8mux_test.py index 626746b2..bee204a5 100755 --- a/compiler/tests/19_single_bank_8mux_test.py +++ b/compiler/tests/19_single_bank_8mux_test.py @@ -38,7 +38,6 @@ class single_bank_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create("bank", sram_config=c) diff --git a/compiler/tests/19_single_bank_global_bitline_test.py b/compiler/tests/19_single_bank_global_bitline_test.py index 5501e485..3649dea4 100755 --- a/compiler/tests/19_single_bank_global_bitline_test.py +++ b/compiler/tests/19_single_bank_global_bitline_test.py @@ -33,7 +33,6 @@ class single_bank_1rw_1r_test(openram_test): num_words=16) c.words_per_row=1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create(module_type="bank", sram_config=c) @@ -41,7 +40,6 @@ class single_bank_1rw_1r_test(openram_test): c.num_words=32 c.words_per_row=2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create(module_type="bank", sram_config=c) @@ -49,7 +47,6 @@ class single_bank_1rw_1r_test(openram_test): c.num_words=64 c.words_per_row=4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create(module_type="bank", sram_config=c) @@ -58,7 +55,6 @@ class single_bank_1rw_1r_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create(module_type="bank", sram_config=c) diff --git a/compiler/tests/19_single_bank_nomux_1rw_1r_test.py b/compiler/tests/19_single_bank_nomux_1rw_1r_test.py index 3926df30..14c998ae 100755 --- a/compiler/tests/19_single_bank_nomux_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_nomux_1rw_1r_test.py @@ -32,7 +32,6 @@ class single_bank_1rw_1r_test(openram_test): num_words=16) c.words_per_row=1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create(module_type="bank", sram_config=c) diff --git a/compiler/tests/19_single_bank_nomux_test.py b/compiler/tests/19_single_bank_nomux_test.py index 3c49f975..f7c9b72d 100755 --- a/compiler/tests/19_single_bank_nomux_test.py +++ b/compiler/tests/19_single_bank_nomux_test.py @@ -36,7 +36,6 @@ class single_bank_test(openram_test): num_spare_rows=num_spare_rows) c.words_per_row=1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create("bank", sram_config=c) diff --git a/compiler/tests/19_single_bank_spare_cols_test.py b/compiler/tests/19_single_bank_spare_cols_test.py index 68b71b51..1e3280de 100755 --- a/compiler/tests/19_single_bank_spare_cols_test.py +++ b/compiler/tests/19_single_bank_spare_cols_test.py @@ -28,7 +28,6 @@ class single_bank_spare_cols_test(openram_test): num_spare_cols=3) c.words_per_row=1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create("bank", sram_config=c) @@ -36,7 +35,6 @@ class single_bank_spare_cols_test(openram_test): c.num_words=32 c.words_per_row=2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create("bank", sram_config=c) @@ -44,7 +42,6 @@ class single_bank_spare_cols_test(openram_test): c.num_words=64 c.words_per_row=4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create("bank", sram_config=c) @@ -53,7 +50,6 @@ class single_bank_spare_cols_test(openram_test): c.word_size=2 c.num_words=128 c.words_per_row=8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create("bank", sram_config=c) diff --git a/compiler/tests/19_single_bank_wmask_1rw_1r_test.py b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py index 8d810d91..20410bc8 100755 --- a/compiler/tests/19_single_bank_wmask_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py @@ -34,7 +34,6 @@ class single_bank_wmask_1rw_1r_test(openram_test): num_banks=1) c.words_per_row=1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create("bank", sram_config=c) @@ -42,7 +41,6 @@ class single_bank_wmask_1rw_1r_test(openram_test): c.num_words=32 c.words_per_row=2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create("bank", sram_config=c) @@ -50,7 +48,6 @@ class single_bank_wmask_1rw_1r_test(openram_test): c.num_words=64 c.words_per_row=4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create("bank", sram_config=c) @@ -58,7 +55,6 @@ class single_bank_wmask_1rw_1r_test(openram_test): c.num_words=128 c.words_per_row=8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create("bank", sram_config=c) diff --git a/compiler/tests/19_single_bank_wmask_test.py b/compiler/tests/19_single_bank_wmask_test.py index 19b01e61..16a14c6f 100755 --- a/compiler/tests/19_single_bank_wmask_test.py +++ b/compiler/tests/19_single_bank_wmask_test.py @@ -29,7 +29,6 @@ class single_bank_wmask_test(openram_test): num_banks=1) c.words_per_row=1 - factory.reset() c.recompute_sizes() debug.info(1, "No column mux") a = factory.create("bank", sram_config=c) @@ -37,7 +36,6 @@ class single_bank_wmask_test(openram_test): c.num_words=32 c.words_per_row=2 - factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") a = factory.create("bank", sram_config=c) @@ -45,7 +43,6 @@ class single_bank_wmask_test(openram_test): c.num_words=64 c.words_per_row=4 - factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") a = factory.create("bank", sram_config=c) @@ -53,7 +50,6 @@ class single_bank_wmask_test(openram_test): c.num_words=128 c.words_per_row=8 - factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") a = factory.create("bank", sram_config=c) diff --git a/compiler/tests/20_sram_2bank_test.py b/compiler/tests/20_sram_2bank_test.py index c2d2c412..52031166 100755 --- a/compiler/tests/20_sram_2bank_test.py +++ b/compiler/tests/20_sram_2bank_test.py @@ -38,7 +38,6 @@ class sram_2bank_test(openram_test): c.num_words, c.words_per_row, c.num_banks)) - factory.reset() a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) @@ -54,7 +53,6 @@ class sram_2bank_test(openram_test): c.num_words, c.words_per_row, c.num_banks)) - factory.reset() a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) @@ -70,7 +68,6 @@ class sram_2bank_test(openram_test): c.num_words, c.words_per_row, c.num_banks)) - factory.reset() a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) @@ -87,7 +84,6 @@ class sram_2bank_test(openram_test): c.num_words, c.words_per_row, c.num_banks)) - factory.reset() a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) diff --git a/compiler/tests/Makefile b/compiler/tests/Makefile index 7351a424..8b992341 100644 --- a/compiler/tests/Makefile +++ b/compiler/tests/Makefile @@ -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 diff --git a/compiler/tests/configs/config.py b/compiler/tests/configs/config.py index e95a1f7c..bb0c8894 100644 --- a/compiler/tests/configs/config.py +++ b/compiler/tests/configs/config.py @@ -17,3 +17,4 @@ check_lvsdrc = True #route_supplies = False output_name = "sram" +rom_data_dir = "tests/configs" diff --git a/compiler/tests/configs/rom_data_64B b/compiler/tests/configs/rom_data_64B new file mode 100644 index 00000000..f92d10d5 --- /dev/null +++ b/compiler/tests/configs/rom_data_64B @@ -0,0 +1 @@ +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFF \ No newline at end of file diff --git a/compiler/tests/regress.py b/compiler/tests/regress.py deleted file mode 100755 index 4df495df..00000000 --- a/compiler/tests/regress.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env python3 -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2023 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# - -import re -import unittest -import sys, os -from openram import globals -from subunit import ProtocolTestCase, TestProtocolClient -from testtools import ConcurrentTestSuite - -(OPTS, args) = globals.parse_args() -del sys.argv[1:] - -from testutils import * -header(__file__, OPTS.tech_name) - -# get a list of all files in the tests directory -files = os.listdir(sys.path[0]) - -# load a file with all tests to skip in a given technology -# since tech_name is dynamically loaded, we can't use @skip directives -try: - skip_file_name = "{0}/tests/skip_tests_{1}.txt".format(os.getenv("OPENRAM_HOME"), OPTS.tech_name) - skip_file = open(skip_file_name, "r") - skip_tests = skip_file.read().splitlines() - for st in skip_tests: - debug.warning("Skipping: " + st) -except FileNotFoundError: - skip_tests = [] - -# assume any file that ends in "test.py" in it is a regression test -nametest = re.compile("test\.py$", re.IGNORECASE) -all_tests = list(filter(nametest.search, files)) -filtered_tests = list(filter(lambda i: i not in skip_tests, all_tests)) -filtered_tests.sort() - -num_threads = OPTS.num_threads - - -def partition_unit_tests(suite, num_threads): - partitions = [list() for x in range(num_threads)] - for index, test in enumerate(suite): - partitions[index % num_threads].append(test) - return partitions - - -def fork_tests(num_threads): - results = [] - test_partitions = partition_unit_tests(suite, num_threads) - suite._tests[:] = [] - - def do_fork(suite): - - for test_partition in test_partitions: - test_suite = unittest.TestSuite(test_partition) - test_partition[:] = [] - c2pread, c2pwrite = os.pipe() - pid = os.fork() - if pid == 0: - # PID of 0 is a child - try: - # Open a stream to write to the parent - stream = os.fdopen(c2pwrite, 'wb', 0) - os.close(c2pread) - sys.stdin.close() - test_suite_result = TestProtocolClient(stream) - test_suite.run(test_suite_result) - except EBADF: - try: - stream.write(traceback.format_exc()) - finally: - os._exit(1) - os._exit(0) - else: - # PID >0 is the parent - # Collect all of the child streams and append to the results - os.close(c2pwrite) - stream = os.fdopen(c2pread, 'rb', 0) - test = ProtocolTestCase(stream) - results.append(test) - return results - return do_fork - - -# import all of the modules -filenameToModuleName = lambda f: os.path.splitext(f)[0] -moduleNames = map(filenameToModuleName, filtered_tests) -modules = map(__import__, moduleNames) - -suite = unittest.TestSuite() -load = unittest.defaultTestLoader.loadTestsFromModule -suite.addTests(map(load, modules)) - -test_runner = unittest.TextTestRunner(verbosity=2, stream=sys.stderr) -if num_threads == 1: - final_suite = suite -else: - final_suite = ConcurrentTestSuite(suite, fork_tests(num_threads)) - -test_result = test_runner.run(final_suite) - -# import verify -# verify.print_drc_stats() -# verify.print_lvs_stats() -# verify.print_pex_stats() - -sys.exit(not test_result.wasSuccessful()) diff --git a/compiler/tests/skip_tests_sky130.txt b/compiler/tests/skip_tests_sky130.txt deleted file mode 100644 index b0cba8aa..00000000 --- a/compiler/tests/skip_tests_sky130.txt +++ /dev/null @@ -1,86 +0,0 @@ -04_dummy_pbitcell_test.py -04_pbitcell_test.py -04_precharge_pbitcell_test.py -04_replica_pbitcell_test.py -04_column_mux_pbitcell_test.py -05_bitcell_array_test.py -05_dummy_array_test.py -05_pbitcell_array_test.py -06_hierarchical_decoder_pbitcell_test.py -06_hierarchical_decoder_test.py -06_hierarchical_predecode2x4_pbitcell_test.py -06_hierarchical_predecode2x4_test.py -06_hierarchical_predecode3x8_pbitcell_test.py -06_hierarchical_predecode3x8_test.py -06_hierarchical_predecode4x16_test.py -07_column_mux_array_pbitcell_test.py -08_wordline_driver_array_pbitcell_test.py -08_wordline_driver_array_test.py -09_sense_amp_array_test_pbitcell.py -09_sense_amp_array_test.py -10_write_driver_array_pbitcell_test.py -10_write_driver_array_test.py -10_write_driver_array_wmask_pbitcell_test.py -10_write_driver_array_wmask_test.py -10_write_mask_and_array_pbitcell_test.py -10_write_mask_and_array_test.py -12_tri_gate_array_test.py -14_replica_pbitcell_array_test.py -14_replica_bitcell_array_test.py -14_replica_column_test.py -14_replica_column_1rw_1r_test.py -18_port_address_test.py -18_port_data_test.py -18_port_data_wmask_test.py -19_bank_select_pbitcell_test.py -19_bank_select_test.py -19_psingle_bank_test.py -19_bank_select_pbitcell_test.py -19_pmulti_bank_test.py -19_multi_bank_test.py -19_psingle_bank_test.py -19_single_bank_1w_1r_test.py -19_single_bank_wmask_1rw_1r_test.py -19_single_bank_1rw_1r_test.py -19_single_bank_test.py -19_single_bank_wmask_test.py -20_psram_1bank_2mux_1rw_1w_test.py -20_psram_1bank_2mux_1rw_1w_wmask_test.py -20_psram_1bank_2mux_1w_1r_test.py -20_psram_1bank_2mux_test.py -20_psram_1bank_4mux_1rw_1r_test.py -20_sram_1bank_2mux_1w_1r_test.py -20_sram_1bank_2mux_test.py -20_sram_1bank_2mux_wmask_test.py -20_sram_1bank_32b_1024_wmask_test.py -20_sram_1bank_4mux_test.py -20_sram_1bank_8mux_test.py -20_sram_1bank_nomux_test.py -20_sram_1bank_nomux_wmask_test.py -20_sram_2bank_test.py -21_hspice_delay_test.py -21_hspice_setuphold_test.py -21_model_delay_test.py -21_ngspice_delay_test.py -21_ngspice_setuphold_test.py -22_psram_1bank_2mux_func_test.py -22_psram_1bank_4mux_func_test.py -22_psram_1bank_8mux_func_test.py -22_psram_1bank_nomux_func_test.py -22_sram_1bank_2mux_func_test.py -22_sram_1bank_4mux_func_test.py -22_sram_1bank_8mux_func_test.py -22_sram_1bank_nomux_func_test.py -22_sram_1rw_1r_1bank_nomux_func_test.py -22_sram_wmask_func_test.py -23_lib_sram_model_corners_test.py -23_lib_sram_model_test.py -23_lib_sram_prune_test.py -23_lib_sram_test.py -24_lef_sram_test.py -25_verilog_sram_test.py -26_hspice_pex_pinv_test.py -26_ngspice_pex_pinv_test.py -26_pex_test.py -30_openram_back_end_test.py -30_openram_front_end_test.py diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index fcb9a776..a41620c3 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -10,21 +10,6 @@ import sys, os, glob import pdb import traceback import time -# FIXME: This is a hack for unit tests running on docker. -try: - import openram -except: - # If openram library isn't found as a python package, - # import it from the $OPENRAM_HOME path. - import importlib.util - OPENRAM_HOME = os.getenv("OPENRAM_HOME") - # Import using spec since the directory can be named something - # other than "openram". - spec = importlib.util.spec_from_file_location("openram", "{}/../__init__.py".format(OPENRAM_HOME)) - module = importlib.util.module_from_spec(spec) - sys.modules["openram"] = module - spec.loader.exec_module(module) - import openram from openram import debug from openram import OPTS diff --git a/docs/source/basic_setup.md b/docs/source/basic_setup.md index 83f2c544..9b0d0517 100644 --- a/docs/source/basic_setup.md +++ b/docs/source/basic_setup.md @@ -7,7 +7,6 @@ This page shows the basic setup for using OpenRAM. ## Table of Contents 1. [Dependencies](#dependencies) -1. [OpenRAM Library](#openram-library) 1. [Anaconda](#anaconda) 1. [Docker](#docker-deprecated-use-anaconda-instead) 1. [Environment](#environment) @@ -16,116 +15,111 @@ This page shows the basic setup for using OpenRAM. ## Dependencies -Please see the Dockerfile for the required versions of tools. - In general, the OpenRAM compiler has very few dependencies: + Git + Make -+ Python 3.6 or higher ++ Python 3.5 or higher + Various Python packages (pip install -r requirements.txt) + Anaconda -## OpenRAM Library -OpenRAM is available as a Python library. There are a few ways to install it: - -+ Install using Makefile (you need to clone the repo): -``` -git clone git@github.com:VLSIDA/OpenRAM.git -cd OpenRAM -make library -``` - -+ Install the latest _dev_ version: -``` -pip3 install git+https://git@github.com/VLSIDA/OpenRAM.git@dev -``` - - - ## Anaconda -We use Anaconda package manager to install the tools used by OpenRAM. This way, you don't have to -worry about updating/installing these tools. OpenRAM installs Anaconda silently in the background -(without affecting any existing anaconda setup you have). +We use Anaconda package manager to install the tools used by OpenRAM. This way, +you don't have to worry about updating/installing these tools. OpenRAM installs +Anaconda silently in the background (without affecting any existing Anaconda +setup you have). -OpenRAM uses Anaconda by default, but you can turn this feature off by setting `use_conda = False` -in your config file. Then, OpenRAM will use the tools you have installed on your system. +OpenRAM uses Anaconda by default, but you can turn this feature off by setting +`use_conda = False` in your config file. Then, OpenRAM will use the tools you +have installed on your system. -If you want to install Anaconda without running OpenRAM (for example to run unit tests, which do not install Anaconda), you can run: -``` -cd OpenRAM -./install_conda.sh -``` +> **Note**: If you want to install Anaconda without running OpenRAM (for example +> to run unit tests, which do not install Anaconda), you can run: +> ``` +> ./install_conda.sh +> ``` ## Docker (deprecated, use Anaconda instead) -We have a [docker setup](../../docker) to run OpenRAM. To use this, you should run: +We have a [docker setup](../../docker) to run OpenRAM. To use this, you should +run: ``` cd OpenRAM/docker make build ``` -This must be run once and will take a while to build all the tools. If you have the -OpenRAM library installed, you can also run the docker setup from the package -installation directory. +This must be run once and will take a while to build all the tools. If you have +the OpenRAM library installed, you can also run the docker setup from the +package installation directory. ## Environment -If you haven't installed the OpenRAM library or you want to use a different OpenRAM installation, -you can set two environment variables: -+ OPENRAM\_HOME should point to the compiler source directory. -+ OPENRAM\_TECH should point to one or more root technology directories (colon separated). +If you haven't installed the OpenRAM library or you want to use a different +OpenRAM installation, you can set two environment variables: ++ `OPENRAM_HOME` should point to the compiler source directory. ++ `OPENRAM_TECH` should point to one or more root technology directories (colon + separated). -If you have the library installed and OPENRAM\_HOME set, the library will use the installation on -the OPENRAM\_HOME path. +If you have the library installed and `OPENRAM_HOME` set, the library will use +the installation on the `OPENRAM_HOME` path. -If you don't have the library, you should also add OPENRAM\_HOME to your PYTHONPATH. This is not -needed if you have the library. +If you don't have the library, you should also add `OPENRAM_HOME` to your +`PYTHONPATH`. This is not needed if you have the library. You can add these environment variables to your `.bashrc`: ``` - export OPENRAM_HOME="$HOME/OpenRAM/compiler" - export OPENRAM_TECH="$HOME/OpenRAM/technology" - export PYTHONPATH=$OPENRAM_HOME +export OPENRAM_HOME="$HOME/OpenRAM/compiler" +export OPENRAM_TECH="$HOME/OpenRAM/technology" +export PYTHONPATH=$OPENRAM_HOME ``` -Note that if you want symbols to resolve in your editor, you may also want to add the specific technology -directory that you use and any custom technology modules as well. For example: +Note that if you want symbols to resolve in your editor, you may also want to +add the specific technology directory that you use and any custom technology +modules as well. For example: ``` - export PYTHONPATH="$OPENRAM_HOME:$OPENRAM_TECH/sky130:$OPENRAM_TECH/sky130/custom" +export PYTHONPATH="$OPENRAM_HOME:$OPENRAM_TECH/sky130:$OPENRAM_TECH/sky130/custom" ``` -We include the tech files necessary for [SCMOS] SCN4M\_SUBM, -[FreePDK45]. The [SCMOS] spice models, however, are -generic and should be replaced with foundry models. You may get the -entire [FreePDK45 PDK here][FreePDK45]. +We include the tech files necessary for [SCMOS] SCN4M\_SUBM, [FreePDK45]. The +[SCMOS] spice models, however, are generic and should be replaced with foundry +models. You may get the entire [FreePDK45 PDK here][FreePDK45]. ## Sky130 Setup -To install [Sky130], you must have open\_pdks installed in $PDK\_ROOT. We highly recommend that you -use the version tagged in the Makefile as others have not been verified. -To install this automatically, you can run: +To install [Sky130], you must have open\_pdks installed in $PDK\_ROOT. We highly +recommend that you use the version tagged in the Makefile as others have not +been verified. To install this automatically, you can run: ``` cd $HOME/OpenRAM make pdk ``` +> **Note**: If you don't have Magic installed, you need to install and activate +> the conda environment before running this command. You can run: +> +> ``` +> ./install_conda.sh +> source miniconda/bin/activate +> ``` -Then you must also install the [Sky130] SRAM build space with the appropriate cell views into the OpenRAM technology directory -by running: + +Then you must also install the [Sky130] SRAM build space with the appropriate +cell views into the OpenRAM technology directory by running: ``` cd $HOME/OpenRAM make install ``` -You can also run these from the package installation directory if you have the OpenRAM library. +You can also run these from the package installation directory if you have the +OpenRAM library. [SCMOS]: https://www.mosis.com/files/scmos/scmos.pdf [FreePDK45]: https://www.eda.ncsu.edu/wiki/FreePDK45:Contents [Sky130]: https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git + diff --git a/docs/source/basic_usage.md b/docs/source/basic_usage.md index b9f5f468..7550137f 100644 --- a/docs/source/basic_usage.md +++ b/docs/source/basic_usage.md @@ -7,9 +7,8 @@ This page of the documentation explains the basic usage of OpenRAM. ## Table of Contents 1. [Environment Variable Setup](#environment-variable-setup-assuming-bash) -1. [Script Usage (with library)](#script-usage-with-library) -1. [Command Line Usage (with library)](#command-line-usage-with-library) -1. [Command Line Usage (without library)](#command-line-usage-without-library) +1. [Command Line Usage](#command-line-usage) +1. [Script Usage](#script-usage) 1. [Configuration Files](#configuration-files) 1. [Common Configuration File Options](#common-configuration-file-options) 1. [Output Files](#output-files) @@ -18,10 +17,11 @@ This page of the documentation explains the basic usage of OpenRAM. ## Environment Variable Setup (assuming bash) -> **Note**: This is optional if you have the OpenRAM library. See [basic setup](./basic_setup.md#go-back) for details. -* OPENRAM\_HOME defines where the compiler directory is - * ```export OPENRAM_HOME="$HOME/openram/compiler"``` -* OPENRAM_TECH defines list of paths where the technologies exist +> **Note**: This is optional if you have the OpenRAM library. See +> [Python library](./python_library.md#go-back) for details. +* `OPENRAM_HOME` defines where the compiler directory is + * `export OPENRAM_HOME="$HOME/openram/compiler"` +* `OPENRAM_TECH` defines list of paths where the technologies exist * `export OPENRAM_TECH="$HOME/openram/technology"` * Colon separated list so you can have private technology directories * Must also have any PDK related variables set up @@ -30,102 +30,25 @@ This page of the documentation explains the basic usage of OpenRAM. -## Script Usage (with library) -If you have the library installed, you can use OpenRAM in any Python script. You can import "openram" as follows: -```python -import openram -openram.init_openram("myconfig.py") # Config files are explained on this page -# Now you can use modules from openram -from openram import tech -... -``` - -Note that you need to initalize OpenRAM so that the modules are imported properly. You can also look -at [sram_compiler.py](../../sram_compiler.py) as an example on how to use "openram." - -If you want to pass custom configuration when generating an SRAM, you can use the `sram_config` class. -```python -import openram -openram.init_openram("myconfig.py") - -from openram import sram_config -c = sram_config(...) - -from openram import sram -s = sram(sram_config=c, - name="custom_name") - -s.save() - -openram.end_openram() -``` - - -## Command Line Usage (with library) -You can run OpenRAM from the command line using the [sram_compiler.py](../../sram_compiler.py) script that is -included in the library's installation. You can find the package directory on a path like: -``` -/home/mrg/.local/lib/python3.8/site-packages/openram -``` - -Alternatively, you can run the following command to find that path: -``` -echo -e "import os\nimport openram\nprint(os.path.dirname(openram.__file__))" | python3 - -``` - -You can continue with following section for more details. - - -## Command Line Usage (without library) +## Command Line Usage Once you have defined the environment, you can run OpenRAM from the command line -using a single configuration file written in Python. - -For example, create a file called *myconfig.py* specifying the following -parameters for your memory: -```python -# Data word size -word_size = 2 -# Number of words in the memory -num_words = 16 - -# Technology to use in $OPENRAM_TECH -tech_name = "scn4m_subm" - -# You can use the technology nominal corner only -nominal_corner_only = True -# Or you can specify particular corners -# Process corners to characterize -# process_corners = ["SS", "TT", "FF"] -# Voltage corners to characterize -# supply_voltages = [ 3.0, 3.3, 3.5 ] -# Temperature corners to characterize -# temperatures = [ 0, 25 100] - -# Output directory for the results -output_path = "temp" -# Output file base name -output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) - -# Disable analytical models for full characterization (WARNING: slow!) -# analytical_delay = False - -``` - -You can then run OpenRAM by executing: +using a single configuration file written in Python. You can then run OpenRAM by +executing: ``` python3 $OPENRAM_HOME/../sram_compiler.py myconfig ``` You can see all of the options for the configuration file in $OPENRAM\_HOME/options.py -To run designs in Docker, it is suggested to use, for example: +To run macros, it is suggested to use, for example: ``` cd OpenRAM/macros make example_config_scn4m_subm ``` * Common arguments: - * `-t` specify technology (scn4m_subm or scmos or freepdk45) + * `-h` print all arguments + * `-t` specify technology (scn4m\_subm or scmos or freepdk45) * `-v` increase verbosity of output * `-n` don't run DRC/LVS * `-c` perform simulation-based characterization @@ -133,6 +56,12 @@ make example_config_scn4m_subm +## Script Usage +OpenRAM is also available as a Python library. See +[Python library](./python_library.md#go-back) for details. + + + ## Configuration Files * Memories are created using a Python configuration file to replicate results * No YAML, JSON, etc. @@ -167,7 +96,7 @@ make example_config_scn4m_subm # Could be calibre for FreePDK45 drc_name = "magic" lvs_name = "netgen" - pex_name = "magic" + pex_name = "magic" ``` @@ -202,7 +131,8 @@ make example_config_scn4m_subm ## Output Files -The output files are placed in the `output_dir` defined in the configuration file. +The output files are placed in the `output_dir` defined in the configuration +file. The base name is specified by `output_name` and suffixes are added. diff --git a/docs/source/debug.md b/docs/source/debug.md index e8ae091c..32752704 100644 --- a/docs/source/debug.md +++ b/docs/source/debug.md @@ -1,7 +1,8 @@ ### [Go Back](./index.md#table-of-contents) # Debugging and Unit Testing -This page of the documentation explains the debugging and unit testing of OpenRAM. +This page of the documentation explains the debugging and unit testing of +OpenRAM. @@ -16,11 +17,14 @@ This page of the documentation explains the debugging and unit testing of OpenRA ## Unit Tests -OpenRAM has the set of thorough regression tests implemented with the Python unit test framework: -* Unit tests allow users to add features without worrying about breaking functionality. +OpenRAM has the set of thorough regression tests implemented with the Python +unit test framework: +* Unit tests allow users to add features without worrying about breaking + functionality. * Unit tests guide users when porting to new technologies. * Every sub-module has its own regression test. -* There are regression tests for memory functionality, library cell verification, timing verification, and technology verification. +* There are regression tests for memory functionality, library cell + verification, timing verification, and technology verification. @@ -41,16 +45,20 @@ OpenRAM has the set of thorough regression tests implemented with the Python uni ## Running Unit Tests - -Regression testing performs a number of tests for all modules in OpenRAM. -From the unit test directory ($OPENRAM\_HOME/tests), -use the following command to run all regression tests: +Regression testing performs a number of tests for all modules in OpenRAM. From +the unit test directory (`$OPENRAM_HOME/tests`), use the following command to run +all regression tests: ``` cd OpenRAM/compiler/tests make -j 3 ``` -The -j can run with 3 threads. By default, this will run in all technologies. Note that if you have not run openram, the conda environment will not be installed. You can install it by running OpenRAM/install_conda.sh (see [Basic Setup](basic_setup.md#anaconda) for more details). + +The `-j` can run with 3 threads. By default, this will run in all technologies. +> **Note**: If you have not run openram before running unit tests, the conda +> environment will not be installed. You can install it by running +> `OpenRAM/install_conda.sh` (see [Basic Setup](basic_setup.md#anaconda) for +> more details). To run a specific test in all technologies: ``` @@ -63,8 +71,8 @@ cd OpenRAM/compiler/tests TECHS=scn4m_subm make 05_bitcell_array_test ``` -To increase the verbosity of the test, add one (or more) -v options and -pass it as an argument to OpenRAM: +To increase the verbosity of the test, add one (or more) `-v` options and pass +it as an argument to OpenRAM: ``` ARGS="-v" make 05_bitcell_array_test ``` @@ -73,95 +81,52 @@ Unit test results are put in a directory: ``` OpenRAM/compiler/tests/results// ``` -If the test fails, there will be a tmp directory with intermediate results. -If the test passes, this directory will be deleted to save space. -You can view the .out file to see what the output of a test is in either case. -* Tests can be run in the `$OPENRAM_HOME/tests` directory -* Command line arguments - * `-v` for verbose - * `-t` freepdk45 for tech - * `-d` to preserve /tmp results (done automatically if test fails) -* Individual tests - * `01_library_drc_test.py` -* All tests - * `regress.py` +If the test fails, there will be a `tmp` directory with intermediate results. If +the test passes, this directory will be deleted to save space. You can view the +`.out` file to see what the output of a test is in either case. + +To preserve results on successful tests (done automatically if test fails): +``` +KEEP=1 make 05_bitcell_array_test +``` ## Successful Unit Tests ```console -user@host:/openram/compiler/tests$ ./regress.py - ______________________________________________________________________________ -|==============================================================================| -|========= Running Test for: =========| -|========= scn4m_subm =========| -|========= ./regress.py =========| -|========= /tmp/openram_mrg_13245_temp/ =========| -|==============================================================================| -runTest (00_code_format_check_test.code_format_test) ... ok -runTest (01_library_drc_test.library_drc_test) ... ok -runTest (02_library_lvs_test.library_lvs_test) ... ok -runTest (03_contact_test.contact_test) ... ok -runTest (03_path_test.path_test) ... ok +user@host:/openram/compiler/tests$ make +scn4m_subm/12_tri_gate_array_test ... PASS! +scn4m_subm/19_pmulti_bank_test ... PASS! +freepdk45/21_ngspice_delay_global_test ... PASS! +scn4m_subm/23_lib_sram_linear_regression_test ... PASS! . . . ``` ```console -user@host:/openram/compiler/tests$ ./03_ptx_1finger_nmos_test.py - ______________________________________________________________________________ -|==============================================================================| -|========= Running Test for: =========| -|========= scn4m_subm =========| -|========= ./03_ptx_1finger_nmos_test.py =========| -|========= /tmp/openram_mrg_13750_temp/ =========| -|==============================================================================| -. ----------------------------------------------------------------------- -Ran 1 test in 0.596s - -OK +user@host:/openram/compiler/tests$ make 01_library_test +scn4m_subm/01_library_test ... PASS! +freepdk45/01_library_test ... PASS! ``` ## Debugging Unsuccessful Unit Tests (or sram\_compiler.py) -* You will get an ERROR during unit test and see a stack trace -* Examine the temporary output files in the temp directory (/tmp/mydir) +* You will get a FAIL during unit test +* You can see the output and stack trace in + `$OPENRAM_HOME/tests/results//.out` +* Examine the temporary output files in the temp directory + (`$OPENRAM_HOME/tests/results///`) ```console - _____________________________________________________________________________ -|==============================================================================| -|========= Running Test for: =========| -|========= scn4m_subm =========| -|========= ./04_pinv_10x_test.py =========| -|========= /tmp/mydir =========| -|==============================================================================| -ERROR: file magic.py: line 174: DRC Errors pinv_0 2 -F -====================================================================== -FAIL: runTest (__main__.pinv_test) ----------------------------------------------------------------------- -Traceback (most recent call last): - File "./04_pinv_10x_test.py", line 22, in runTest - self.local_check(tx) - File "/Users/mrg/openram/compiler/tests/testutils.py", line 45, in local_check - self.fail("DRC failed: {}".format(a.name)) -AssertionError: DRC failed: pinv_0 - ----------------------------------------------------------------------- -Ran 1 test in 0.609s - -FAILED (failures=1) +user@host:/openram/compiler/tests$ make 01_library_test +scn4m_subm/01_library_test ... FAIL! ``` ### It didn't finish... where are my files? * OpenRAM puts all temporary files in a temporary directory named: - * `/tmp/openram___temp` - * This allows multiple processes/users to simultaneously run - * This allows /tmp to be mapped to a RAM disk for faster performance + * `$OPENRAM_HOME/tests/results///` + * This allows multiple unit tests to simultaneously run * After a successful run, the directory and contents are deleted - * To preserve the contents, you can run with the `-d` option for debugging -* `OPENRAM_TMP` will override the temporary directory location for debug - * `export OPENRAM_TMP="/home/myname/debugdir"` + * To preserve the contents, you can run with the `KEEP` option for debugging @@ -176,4 +141,3 @@ FAILED (failures=1) - diff --git a/docs/source/index.md b/docs/source/index.md index a86f893e..2bb59fbe 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -1,7 +1,8 @@ # OpenRAM Documentation ![OpenRAM Logo](../../images/OpenRAM_logo_yellow_transparent.svg) -These pages provide the documentation of OpenRAM. You can use the links below to navigate through the documentation. +These pages provide the documentation of OpenRAM. You can use the links below to +navigate through the documentation. @@ -10,6 +11,7 @@ These pages provide the documentation of OpenRAM. You can use the links below to 1. [Supported Technologies](#supported-technologies) 1. [Basic Setup](./basic_setup.md#go-back) 1. [Basic Usage](./basic_usage.md#go-back) +1. [Python Library](./python_library.md#go-back) 1. [Bitcells](./bitcells.md#go-back) 1. [Architecture](./architecture.md#go-back) 1. [Implementation](#implementation) @@ -29,14 +31,11 @@ These pages provide the documentation of OpenRAM. You can use the links below to - ## OpenRAM Dependencies -Please see the Dockerfile for the required versions of tools. - In general, the OpenRAM compiler has very few dependencies: + Git + Make -+ Python 3.6 or higher ++ Python 3.5 or higher + Various Python packages (pip install -r requirements.txt) + Anaconda @@ -80,15 +79,23 @@ Commercial tools (optional): ## Technology and Tool Portability -* OpenRAM is technology independent by using a technology directory that includes: +* OpenRAM is technology independent by using a technology directory that + includes: * Technology's specific information * Technology's rules such as DRC rules and the GDS layer map - * Custom designed library cells (6T, sense amp, DFF) to improve the SRAM density. -* For technologies that have specific design requirements, such as specialized well contacts, the user can include helper functions in the technology directory. + * Custom designed library cells (6T, sense amp, DFF) to improve the SRAM + density. +* For technologies that have specific design requirements, such as specialized + well contacts, the user can include helper functions in the technology + directory. * Verification wrapper scripts * Uses a wrapper interface with DRC and LVS tools that allow flexibility - * DRC and LVS can be performed at all levels of the design hierarchy to enhance bug tracking. - * DRC and LVS can be disabled completely for improved run-time or if licenses are not available. + * DRC and LVS can be performed at all levels of the design hierarchy to + enhance bug tracking. + * DRC and LVS can be disabled completely for improved run-time or if + licenses are not available. + + ## Contributors/Collaborators @@ -108,4 +115,3 @@ Commercial tools (optional): * Marcelo Sero * Seokjoong Kim - diff --git a/docs/source/python_library.md b/docs/source/python_library.md new file mode 100644 index 00000000..a4b92c5c --- /dev/null +++ b/docs/source/python_library.md @@ -0,0 +1,84 @@ +### [Go Back](./index.md#table-of-contents) + +# Python Library +This page explains the Python library of OpenRAM. + + + +## Table of Contents +1. [Installation](#installation) +1. [Environment Variables](#environment-variables) +1. [Usage](#usage) + + + +## Installation +OpenRAM is available as a Python library. There are a few ways to install it: + ++ Install the latest _stable_ version: +``` +pip3 install openram +``` + ++ Install the latest _dev_ version: +``` +pip3 install git+https://git@github.com/VLSIDA/OpenRAM.git@dev +``` + ++ Install using Makefile (you need to clone the repo): +``` +git clone git@github.com:VLSIDA/OpenRAM.git +cd OpenRAM +make library +``` + + + +## Environment Variables +OpenRAM library doesn't need any environment variables by default. However, if +you have set the environment variables explained on +[basic usage](.basic_usage.md#go-back), the library will use the OpenRAM source +code located at `OPENRAM_HOME`. + +If you want the convenience of being able to run OpenRAM from any Python script +and have a custom OpenRAM setup, you can set these environment variables to +point to that OpenRAM installation directory. + +If you don't want to use this feature, you can simply unset these environment +variables. + + + +## Usage +With the OpenRAM library, you can use OpenRAM in any Python script. You can +import "openram" as follows: +```python +import openram +openram.init_openram("myconfig.py") # Config files are explained on "Basic Usage" page +# Now you can use modules from openram +from openram import tech +... +``` + +Note that you need to initialize OpenRAM so that the modules are imported +properly. You can also look at [sram\_compiler.py](../../sram_compiler.py) as an +example on how to use "openram." + +If you want to pass custom configuration when generating an SRAM, you can use +the `sram_config` class. +```python +import openram +openram.init_openram("myconfig.py") + +from openram import sram_config +c = sram_config(...) + +from openram import sram +s = sram(sram_config=c, + name="custom_name") + +s.save() + +openram.end_openram() +``` + diff --git a/images/download-stable-blue.svg b/images/download-stable-blue.svg deleted file mode 100644 index 2fbc3649..00000000 --- a/images/download-stable-blue.svg +++ /dev/null @@ -1 +0,0 @@ - downloaddownloadstablestable \ No newline at end of file diff --git a/images/download-unstable-blue.svg b/images/download-unstable-blue.svg deleted file mode 100644 index a233df6b..00000000 --- a/images/download-unstable-blue.svg +++ /dev/null @@ -1 +0,0 @@ - downloaddownloadunstableunstable \ No newline at end of file diff --git a/images/download.svg b/images/download.svg deleted file mode 100644 index 95d978ed..00000000 --- a/images/download.svg +++ /dev/null @@ -1,2 +0,0 @@ - -download download latestlatest diff --git a/macros/Makefile b/macros/Makefile index 94e0bf43..e555f3a0 100644 --- a/macros/Makefile +++ b/macros/Makefile @@ -17,29 +17,38 @@ endif # Verbosity OPENRAM_OPTS += -v --keeptemp -CONFIG_DIR = configs -SRCS=$(filter-out disabled-% %_common.py,$(sort $(notdir $(wildcard $(CONFIG_DIR)/*.py)))) -DIRS=$(basename $(SRCS)) -STAMPS=$(addsuffix .ok,$(DIRS)) +SRAM_CONFIG_DIR = sram_configs +SRAM_SRCS=$(filter-out disabled-% %_common.py,$(sort $(notdir $(wildcard $(SRAM_CONFIG_DIR)/*.py)))) +SRAM_DIRS=$(basename $(SRAM_SRCS)) +SRAM_STAMPS=$(addsuffix .ok,$(SRAM_DIRS)) + +ROM_CONFIG_DIR = rom_configs +ROM_SRCS=$(filter-out disabled-% %_common.py,$(sort $(notdir $(wildcard $(ROM_CONFIG_DIR)/*.py)))) +ROM_DIRS=$(basename $(ROM_SRCS)) +ROM_STAMPS=$(addsuffix .ok,$(ROM_DIRS)) configs: @echo @echo "Using OpenRAM at $(OPENRAM_HOME)" @echo " (which is version $$(cd $(OPENRAM_HOME); git describe --tags))" @echo - @echo "Configurations:" - @for D in $(DIRS); do echo " - $$D"; done + @echo "SRAM Configurations:" + @for D in $(SRAM_DIRS); do echo " - $$D"; done + @echo "ROM Configurations:" + @for D in $(ROM_DIRS); do echo " - $$D"; done @echo .PHONY: configs BROKEN := -WORKING_STAMPS=$(filter-out $(addsuffix .ok, $(BROKEN)), $(STAMPS)) -EXAMPLE_STAMPS=$(filter example%, $(WORKING_STAMPS)) -SKY130_STAMPS=$(filter sky130%, $(WORKING_STAMPS)) -FREEPDK45_STAMPS=$(filter freepdk45%, $(WORKING_STAMPS)) -SCN4M_SUBM_STAMPS=$(filter scn4m_subm%, $(WORKING_STAMPS)) +WORKING_SRAM_STAMPS=$(filter-out $(addsuffix .ok, $(BROKEN)), $(SRAM_STAMPS)) +WORKING_ROM_STAMPS=$(filter-out $(addsuffix .ok, $(BROKEN)), $(ROM_STAMPS)) + +EXAMPLE_STAMPS=$(filter example%, $(WORKING_SRAM_STAMPS)) $(filter example%, $(WORKING_ROM_STAMPS)) +SKY130_STAMPS=$(filter sky130%, $(WORKING_SRAM_STAMPS)) $(filter sky130%, $(WORKING_ROM_STAMPS)) +FREEPDK45_STAMPS=$(filter freepdk45%, $(WORKING_STAMPS)) $(filter freepdk45%, $(WORKING_ROM_STAMPS)) +SCN4M_SUBM_STAMPS=$(filter scn4m_subm%, $(WORKING_STAMPS)) $(filter scn4m_subm%, $(WORKING_ROM_STAMPS)) all: | configs @echo @@ -60,14 +69,24 @@ freepdk45: $(FREEPDK45_STAMPS) scn4m_subm: $(SCN4M_SUBM_STAMPS) .PHONY: scn4m_subm + +rom: $(WORKING_ROM_STAMPS) +.PHONY: rom + +sram: $(WORKING_SRAM_STAMPS) +.PHONY: sram -OPENRAM_TMP=$(MACRO_DIR)/$*/tmp -%.ok: configs/%.py +%.ok: sram_configs/%.py @echo "Building $*" @mkdir -p $* - @python3 -u $(OPENRAM_COMPILER) $(OPENRAM_OPTS) -o $* -p $(MACRO_DIR)/$* $(MACRO_DIR)/$< && touch $@ + @python3 -u $(SRAM_COMPILER) $(OPENRAM_OPTS) -o $* -p $(MACRO_DIR)/$* $(MACRO_DIR)/$< && touch $@ -.DELETE_ON_ERROR: $(STAMPS) +%.ok: rom_configs/%.py + @echo "Building $*" + @mkdir -p $* + @python3 -u $(ROM_COMPILER) $(OPENRAM_OPTS) -o $* -p $(MACRO_DIR)/$* $(MACRO_DIR)/$< && touch $@ + +.DELETE_ON_ERROR: $(WORKING_SRAM_STAMPS) $(WORKING_ROM_STAMPS) $(DIRS): @$(MAKE) --no-print-directory $@.ok @@ -75,6 +94,8 @@ $(DIRS): .PHONY: $(DIRS) clean: - rm -rf $(STAMPS) - rm -rf $(DIRS) + rm -rf $(WORKING_SRAM_STAMPS) + rm -rf $(WORKING_ROM_STAMPS) + rm -rf $(SRAM_DIRS) + rm -rf $(ROM_DIRS) .PHONY: clean diff --git a/macros/rom_configs/example_1kbyte.dat b/macros/rom_configs/example_1kbyte.dat new file mode 100644 index 00000000..11006ecd --- /dev/null +++ b/macros/rom_configs/example_1kbyte.dat @@ -0,0 +1 @@ +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF \ No newline at end of file diff --git a/macros/rom_configs/sky130_rom_1kbyte.py b/macros/rom_configs/sky130_rom_1kbyte.py new file mode 100644 index 00000000..1f598070 --- /dev/null +++ b/macros/rom_configs/sky130_rom_1kbyte.py @@ -0,0 +1,19 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +word_size = 1 + +check_lvsdrc = True + +rom_data = "macros/rom_configs/example_1kbyte.dat" + +output_name = "rom_1kbyte" +output_path = "macro/{output_name}".format(**locals()) + +import os +exec(open(os.path.join(os.path.dirname(__file__), 'sky130_rom_common.py')).read()) diff --git a/macros/rom_configs/sky130_rom_common.py b/macros/rom_configs/sky130_rom_common.py new file mode 100644 index 00000000..d36887bf --- /dev/null +++ b/macros/rom_configs/sky130_rom_common.py @@ -0,0 +1,8 @@ + +tech_name = "sky130" +nominal_corner_only = True + +route_supplies = "ring" +check_lvsdrc = True +# check_lvsdrc = False + diff --git a/macros/configs/example_config_1rw_1r_scn4m_subm.py b/macros/sram_configs/example_config_1rw_1r_scn4m_subm.py similarity index 100% rename from macros/configs/example_config_1rw_1r_scn4m_subm.py rename to macros/sram_configs/example_config_1rw_1r_scn4m_subm.py diff --git a/macros/configs/example_config_1rw_1w_scn4m_subm.py b/macros/sram_configs/example_config_1rw_1w_scn4m_subm.py similarity index 100% rename from macros/configs/example_config_1rw_1w_scn4m_subm.py rename to macros/sram_configs/example_config_1rw_1w_scn4m_subm.py diff --git a/macros/configs/example_config_1rw_2mux_scn4m_subm.py b/macros/sram_configs/example_config_1rw_2mux_scn4m_subm.py similarity index 100% rename from macros/configs/example_config_1rw_2mux_scn4m_subm.py rename to macros/sram_configs/example_config_1rw_2mux_scn4m_subm.py diff --git a/macros/configs/example_config_1w_1r_scn4m_subm.py b/macros/sram_configs/example_config_1w_1r_scn4m_subm.py similarity index 100% rename from macros/configs/example_config_1w_1r_scn4m_subm.py rename to macros/sram_configs/example_config_1w_1r_scn4m_subm.py diff --git a/macros/configs/example_config_2rw_scn4m_subm.py b/macros/sram_configs/example_config_2rw_scn4m_subm.py similarity index 100% rename from macros/configs/example_config_2rw_scn4m_subm.py rename to macros/sram_configs/example_config_2rw_scn4m_subm.py diff --git a/macros/configs/example_config_big_scn4m_subm.py b/macros/sram_configs/example_config_big_scn4m_subm.py similarity index 100% rename from macros/configs/example_config_big_scn4m_subm.py rename to macros/sram_configs/example_config_big_scn4m_subm.py diff --git a/macros/configs/example_config_freepdk45.py b/macros/sram_configs/example_config_freepdk45.py similarity index 100% rename from macros/configs/example_config_freepdk45.py rename to macros/sram_configs/example_config_freepdk45.py diff --git a/macros/configs/example_config_giant_scn4m_subm.py b/macros/sram_configs/example_config_giant_scn4m_subm.py similarity index 100% rename from macros/configs/example_config_giant_scn4m_subm.py rename to macros/sram_configs/example_config_giant_scn4m_subm.py diff --git a/macros/configs/example_config_medium_scn4m_subm.py b/macros/sram_configs/example_config_medium_scn4m_subm.py similarity index 100% rename from macros/configs/example_config_medium_scn4m_subm.py rename to macros/sram_configs/example_config_medium_scn4m_subm.py diff --git a/macros/configs/example_config_scn4m_subm.py b/macros/sram_configs/example_config_scn4m_subm.py similarity index 100% rename from macros/configs/example_config_scn4m_subm.py rename to macros/sram_configs/example_config_scn4m_subm.py diff --git a/macros/configs/freepdk45_sram_1rw1r_32x2048_8.py b/macros/sram_configs/freepdk45_sram_1rw1r_32x2048_8.py similarity index 100% rename from macros/configs/freepdk45_sram_1rw1r_32x2048_8.py rename to macros/sram_configs/freepdk45_sram_1rw1r_32x2048_8.py diff --git a/macros/configs/scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py b/macros/sram_configs/scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py similarity index 100% rename from macros/configs/scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py rename to macros/sram_configs/scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py diff --git a/macros/configs/scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py b/macros/sram_configs/scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py similarity index 100% rename from macros/configs/scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py rename to macros/sram_configs/scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py diff --git a/macros/configs/scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py b/macros/sram_configs/scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py similarity index 100% rename from macros/configs/scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py rename to macros/sram_configs/scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py diff --git a/macros/configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py b/macros/sram_configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py similarity index 100% rename from macros/configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py rename to macros/sram_configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py diff --git a/macros/configs/scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py b/macros/sram_configs/scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py similarity index 100% rename from macros/configs/scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py rename to macros/sram_configs/scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py diff --git a/macros/configs/scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py b/macros/sram_configs/scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py similarity index 100% rename from macros/configs/scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py rename to macros/sram_configs/scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py diff --git a/macros/configs/sky130_sram_1kbyte_1r1w_8x1024_8.py b/macros/sram_configs/sky130_sram_1kbyte_1r1w_8x1024_8.py similarity index 100% rename from macros/configs/sky130_sram_1kbyte_1r1w_8x1024_8.py rename to macros/sram_configs/sky130_sram_1kbyte_1r1w_8x1024_8.py diff --git a/macros/configs/sky130_sram_1kbyte_1rw1r_32x256_8.py b/macros/sram_configs/sky130_sram_1kbyte_1rw1r_32x256_8.py similarity index 100% rename from macros/configs/sky130_sram_1kbyte_1rw1r_32x256_8.py rename to macros/sram_configs/sky130_sram_1kbyte_1rw1r_32x256_8.py diff --git a/macros/configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py b/macros/sram_configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py similarity index 100% rename from macros/configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py rename to macros/sram_configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py diff --git a/macros/configs/sky130_sram_1kbyte_1rw_32x256_8.py b/macros/sram_configs/sky130_sram_1kbyte_1rw_32x256_8.py similarity index 100% rename from macros/configs/sky130_sram_1kbyte_1rw_32x256_8.py rename to macros/sram_configs/sky130_sram_1kbyte_1rw_32x256_8.py diff --git a/macros/configs/sky130_sram_1rw1r_tiny.py b/macros/sram_configs/sky130_sram_1rw1r_tiny.py similarity index 100% rename from macros/configs/sky130_sram_1rw1r_tiny.py rename to macros/sram_configs/sky130_sram_1rw1r_tiny.py diff --git a/macros/configs/sky130_sram_1rw_tiny.py b/macros/sram_configs/sky130_sram_1rw_tiny.py similarity index 100% rename from macros/configs/sky130_sram_1rw_tiny.py rename to macros/sram_configs/sky130_sram_1rw_tiny.py diff --git a/macros/configs/sky130_sram_2kbyte_1rw1r_32x512_8.py b/macros/sram_configs/sky130_sram_2kbyte_1rw1r_32x512_8.py similarity index 100% rename from macros/configs/sky130_sram_2kbyte_1rw1r_32x512_8.py rename to macros/sram_configs/sky130_sram_2kbyte_1rw1r_32x512_8.py diff --git a/macros/configs/sky130_sram_2kbyte_1rw_32x512_8.py b/macros/sram_configs/sky130_sram_2kbyte_1rw_32x512_8.py similarity index 100% rename from macros/configs/sky130_sram_2kbyte_1rw_32x512_8.py rename to macros/sram_configs/sky130_sram_2kbyte_1rw_32x512_8.py diff --git a/macros/configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py b/macros/sram_configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py similarity index 100% rename from macros/configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py rename to macros/sram_configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py diff --git a/macros/configs/sky130_sram_4kbyte_1rw_32x1024_8.py b/macros/sram_configs/sky130_sram_4kbyte_1rw_32x1024_8.py similarity index 100% rename from macros/configs/sky130_sram_4kbyte_1rw_32x1024_8.py rename to macros/sram_configs/sky130_sram_4kbyte_1rw_32x1024_8.py diff --git a/macros/configs/sky130_sram_4kbyte_1rw_64x512_8.py b/macros/sram_configs/sky130_sram_4kbyte_1rw_64x512_8.py similarity index 100% rename from macros/configs/sky130_sram_4kbyte_1rw_64x512_8.py rename to macros/sram_configs/sky130_sram_4kbyte_1rw_64x512_8.py diff --git a/macros/configs/sky130_sram_common.py b/macros/sram_configs/sky130_sram_common.py similarity index 100% rename from macros/configs/sky130_sram_common.py rename to macros/sram_configs/sky130_sram_common.py diff --git a/openram.mk b/openram.mk index 60b59f17..b86e4a94 100644 --- a/openram.mk +++ b/openram.mk @@ -1,11 +1,12 @@ OPENRAM_HOME := $(abspath $(TOP_DIR)/compiler) OPENRAM_TECH := $(abspath $(TOP_DIR)/technology) -OPENRAM_COMPILER := $(abspath $(TOP_DIR)/sram_compiler.py) +SRAM_COMPILER := $(abspath $(TOP_DIR)/sram_compiler.py) +ROM_COMPILER := $(abspath $(TOP_DIR)/rom_compiler.py) PDK_ROOT ?= $(TOP_DIR) -ifeq (,$(wildcard $(OPENRAM_COMPILER))) -$(error Did not find '$(OPENRAM_COMPILER)' in '$(OPENRAM_HOME)' (from $$OPENRAM_HOME)) +ifeq (,$(wildcard $(SRAM_COMPILER))) +$(error Did not find '$(SRAM_COMPILER)' in '$(OPENRAM_HOME)' (from $$OPENRAM_HOME)) endif export OPENRAM_HOME export OPENRAM_TECH diff --git a/rom_compiler.py b/rom_compiler.py new file mode 100755 index 00000000..f09c1c0a --- /dev/null +++ b/rom_compiler.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +""" +ROM Compiler + +The output files append the given suffixes to the output name: +a spice (.sp) file for circuit simulation +a GDS2 (.gds) file containing the layout +""" + + +import sys +import os +import datetime + +# You don't need the next two lines if you're sure that openram package is installed +from common import * +make_openram_package() +import openram + + +(OPTS, args) = openram.parse_args() + + +# Check that we are left with a single configuration file as argument. +if len(args) != 1: + print(openram.USAGE) + sys.exit(2) + +# These depend on arguments, so don't load them until now. +from openram import debug + +# Parse config file and set up all the options +openram.init_openram(config_file=args[0]) + +# Only print banner here so it's not in unit tests +openram.print_banner() + +# Keep track of running stats +start_time = datetime.datetime.now() +openram.print_time("Start", start_time) + + +output_extensions = [ "sp", "v"] +# Only output lef/gds if back-end +if not OPTS.netlist_only: + output_extensions.extend(["gds"]) + +output_files = ["{0}{1}.{2}".format(OPTS.output_path, + OPTS.output_name, x) + for x in output_extensions] +debug.print_raw("Output files are: ") +for path in output_files: + debug.print_raw(path) + +from openram import rom + +r = rom() + +# Output the files for the resulting ROM +r.save() + +# Delete temp files etc. +openram.end_openram() +openram.print_time("End", datetime.datetime.now(), start_time) \ No newline at end of file diff --git a/setup.py b/setup.py index 8f3fd9f2..80883771 100644 --- a/setup.py +++ b/setup.py @@ -52,17 +52,43 @@ with open("requirements.txt") as f: version = open("VERSION", "r").read().rstrip() +with open("README.md") as f: + long_description = f.read() + + # Call the setup to create the package setup( name="openram", version=version, description="An open-source static random access memory (SRAM) compiler", + long_description=long_description, + long_description_content_type="text/markdown", url="https://openram.org/", + download_url="https://github.com/VLSIDA/OpenRAM/releases", + project_urls={ + "Bug Tracker": "https://github.com/VLSIDA/OpenRAM/issues", + "Documentation": "https://github.com/VLSIDA/OpenRAM/blob/stable/docs/source/index.md", + "Source Code": "https://github.com/VLSIDA/OpenRAM", + }, author="Matthew Guthaus", - author_email="mrg@ucsc.edu", + author_email="mrg+vlsida@ucsc.edu", keywords=[ "sram", "magic", "gds", "netgen", "ngspice", "netlist" ], - readme="README.md", - license="BSD-3", + license="BSD 3-Clause", + python_requires=">=3.5", + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Operating System :: Unix", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering", + "Topic :: Software Development", + "Topic :: System :: Hardware", + ], packages=packages, package_dir=package_dir, include_package_data=True, diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index 945cbd5c..b3b39997 100755 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -714,7 +714,11 @@ spice["ground"]="vssd1" # whether or not the device model is actually a subckt spice["device_prefix"] = "X" -spice["fet_libraries"] = {"TT": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "tt"]]} +spice["fet_libraries"] = { "TT": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "tt"]], + "SS": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "ss"]], + "FF": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "ff"]], + "SF": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "sf"]], + "FS": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "fs"]] } # spice stimulus related variables spice["feasible_period"] = 10 # estimated feasible period in ns