diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..6a7cd73b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +name: ci +on: [push] +jobs: + scn4me_subm: + runs-on: self-hosted + steps: + - name: Check out repository + uses: actions/checkout@v1 + - name: SCMOS test + run: | + . /home/github-runner/setup-paths.sh + export OPENRAM_HOME="`pwd`/compiler" + export OPENRAM_TECH="`pwd`/technology:/software/PDKs/skywater-tech" + export OPENRAM_TMP="`pwd`/scn4me_subm" + python3-coverage run -p $OPENRAM_HOME/tests/regress.py -j 48 -t scn4m_subm + - name: Archive + if: ${{ failure() }} + uses: actions/upload-artifact@v2 + with: + name: scn4me_subm Archives + path: $OPENRAM_TMP/ + freepdk45: + runs-on: self-hosted + steps: + - name: Check out repository + uses: actions/checkout@v1 + - name: FreePDK45 test + run: | + . /home/github-runner/setup-paths.sh + export OPENRAM_HOME="`pwd`/compiler" + export OPENRAM_TECH="`pwd`/technology:/software/PDKs/skywater-tech" + export OPENRAM_TMP="`pwd`/freepdk45" + python3-coverage run -p $OPENRAM_HOME/tests/regress.py -j 48 -t freepdk45 + - name: Archive + if: ${{ failure() }} + uses: actions/upload-artifact@v2 + with: + name: FreePDK45 Archives + path: $OPENRAM_TMP/ + coverage: + if: ${{ always() }} + needs: [scn4me_subm, freepdk45] + runs-on: self-hosted + steps: + - name: Coverage stats + run: | + python3-coverage combine + python3-coverage report + python3-coverage html -d coverage_html + - name: Archive coverage + uses: actions/upload-artifact@v2 + with: + name: code-coverage-report + path: coverage_html/ + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 27431cb2..00000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,48 +0,0 @@ -before_script: - - . /home/gitlab-runner/setup-paths.sh - - export OPENRAM_HOME="`pwd`/compiler" - - export OPENRAM_TECH="`pwd`/technology:/home/PDKs/skywater-tech" - -stages: - - test - - coverage - -freepdk45: - stage: test - script: - - coverage run -p $OPENRAM_HOME/tests/regress.py -t freepdk45 - artifacts: - paths: - - .coverage.* - expire_in: 1 week - -scn4m_subm: - stage: test - script: - - coverage run -p $OPENRAM_HOME/tests/regress.py -t scn4m_subm - artifacts: - paths: - - .coverage.* - expire_in: 1 week - -# s8: -# stage: test -# script: -# - coverage run -p $OPENRAM_HOME/tests/regress.py -t s8 -# artifacts: -# paths: -# - .coverage.* -# expire_in: 1 week - -coverage: - stage: coverage - script: - - coverage combine - - coverage report - - coverage html -d coverage_html - artifacts: - paths: - - coverage_html - expire_in: 1 week - coverage: '/TOTAL.+ ([0-9]{1,3}%)/' - diff --git a/README.md b/README.md index 8810f26a..da68361b 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,9 @@ [![License: BSD 3-clause](./images/license_badge.svg)](./LICENSE) Master: -[![Pipeline Status](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/master/pipeline.svg)](https://github.com/VLSIDA/OpenRAM/commits/master) -![Coverage](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/master/coverage.svg) [![Download](./images/download-stable-blue.svg)](https://github.com/VLSIDA/OpenRAM/archive/master.zip) Dev: -[![Pipeline Status](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/dev/pipeline.svg)](https://github.com/VLSIDA/OpenRAM/commits/dev) -![Coverage](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/dev/coverage.svg) [![Download](./images/download-unstable-blue.svg)](https://github.com/VLSIDA/OpenRAM/archive/dev.zip) An open-source static random access memory (SRAM) compiler. @@ -43,12 +39,12 @@ The OpenRAM compiler has very few dependencies: If you want to perform DRC and LVS, you will need either: + Calibre (for [FreePDK45]) -+ [Magic] 8.3.27 or higher (for [SCMOS]) -+ [Netgen] 1.5 (for [SCMOS]) ++ [Magic] 8.3.130 or newer ++ [Netgen] 1.5.164 or newer You must set two environment variables: + OPENRAM\_HOME should point to the compiler source directory. -+ OPENERAM\_TECH should point to a root technology directory. ++ OPENERAM\_TECH should point to one or more root technology directories (colon separated). ## Environment @@ -65,11 +61,11 @@ You may also wish to add OPENRAM\_HOME to your PYTHONPATH: export PYTHONPATH="$PYTHONPATH:$OPENRAM_HOME" ``` -We include the tech files necessary for [SCMOS] -SCN4M_SUBM. The [SCMOS] spice models, however, are generic and should -be replaced with foundry models. If you are using [FreePDK45], you -should also have that set up and have the environment variable point -to the PDK. For example add this to your .bashrc: +We include the tech files necessary for [SCMOS] SCN4M_SUBM. The +[SCMOS] spice models, however, are generic and should be replaced with +foundry models. If you are using [FreePDK45], you should also have +that set up and have the environment variable point to the PDK. For +example add this to your .bashrc: ``` export FREEPDK45="/bsoe/software/design-kits/FreePDK45" @@ -160,9 +156,9 @@ specific technology (e.g., [FreePDK45]) should be a subdirectory * dff.gds * sense_amp.gds * write_driver.gds - * cell_6t.gds - * replica\_cell\_6t.gds - * dummy\_cell\_6t.gds + * cell_1rw.gds + * replica\_cell\_1rw.gds + * dummy\_cell\_1rw.gds * sp_lib folder with all the .sp (premade) library netlists for the above cells. * layers.map * A valid tech Python module (tech directory with \_\_init\_\_.py and tech.py) with: diff --git a/compiler/base/channel_route.py b/compiler/base/channel_route.py index 5e86692e..abbb267b 100644 --- a/compiler/base/channel_route.py +++ b/compiler/base/channel_route.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 1f38fc38..bb6eb391 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/delay_data.py b/compiler/base/delay_data.py index 5157de76..9e963265 100644 --- a/compiler/base/delay_data.py +++ b/compiler/base/delay_data.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/design.py b/compiler/base/design.py index c8bf3070..8c621742 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. @@ -57,6 +57,12 @@ class design(hierarchy_design): self.cell_name, GDS["unit"]) + # Convert names back to the original names + # so that copying will use the new names + for pin_name in self.pin_map: + for index1, pin in enumerate(self.pin_map[pin_name]): + self.pin_map[pin_name][index1].name = self.get_original_pin_name(pin.name) + self.width = width self.height = height diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index debeceee..1b42f51e 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index ee5e2653..fe1f4c55 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 4dc464e1..fad5c8ae 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. @@ -1235,7 +1235,6 @@ class layout(): self.add_power_pin(new_name, pin.center(), start_layer=start_layer) def add_power_pin(self, name, loc, directions=None, start_layer="m1"): - # Hack for min area if OPTS.tech_name == "sky130": min_area = drc["minarea_{}".format(self.pwr_grid_layer)] diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 29f03c7f..51d2c3b7 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/lef.py b/compiler/base/lef.py index 97d32d0a..942a8ac2 100644 --- a/compiler/base/lef.py +++ b/compiler/base/lef.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index 73f38d3e..c476c19b 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/power_data.py b/compiler/base/power_data.py index 77d50d34..7ab35d14 100644 --- a/compiler/base/power_data.py +++ b/compiler/base/power_data.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/route.py b/compiler/base/route.py index d1caf612..812318da 100644 --- a/compiler/base/route.py +++ b/compiler/base/route.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/utils.py b/compiler/base/utils.py index ed016964..2b6ef888 100644 --- a/compiler/base/utils.py +++ b/compiler/base/utils.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/vector.py b/compiler/base/vector.py index 356ef42b..d217ec90 100644 --- a/compiler/base/vector.py +++ b/compiler/base/vector.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index 989d9d9c..ef0550c6 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/wire.py b/compiler/base/wire.py index 67026a5b..5c78755a 100644 --- a/compiler/base/wire.py +++ b/compiler/base/wire.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/wire_path.py b/compiler/base/wire_path.py index b4f81363..411b9ede 100644 --- a/compiler/base/wire_path.py +++ b/compiler/base/wire_path.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/base/wire_spice_model.py b/compiler/base/wire_spice_model.py index 9a73134b..37efedb5 100644 --- a/compiler/base/wire_spice_model.py +++ b/compiler/base/wire_spice_model.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/bitcells/bitcell_1port.py b/compiler/bitcells/bitcell_1port.py index 08180607..12c3c3ce 100644 --- a/compiler/bitcells/bitcell_1port.py +++ b/compiler/bitcells/bitcell_1port.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/bitcells/bitcell_2port.py b/compiler/bitcells/bitcell_2port.py index fa790ef6..fcba7a38 100644 --- a/compiler/bitcells/bitcell_2port.py +++ b/compiler/bitcells/bitcell_2port.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/bitcells/bitcell_base.py b/compiler/bitcells/bitcell_base.py index 388d1b7b..5112642e 100644 --- a/compiler/bitcells/bitcell_base.py +++ b/compiler/bitcells/bitcell_base.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/bitcells/col_cap_bitcell_1port.py b/compiler/bitcells/col_cap_bitcell_1port.py index 5d34e2f5..d2771e6d 100644 --- a/compiler/bitcells/col_cap_bitcell_1port.py +++ b/compiler/bitcells/col_cap_bitcell_1port.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/bitcells/col_cap_bitcell_2port.py b/compiler/bitcells/col_cap_bitcell_2port.py index d18d0762..71b27619 100644 --- a/compiler/bitcells/col_cap_bitcell_2port.py +++ b/compiler/bitcells/col_cap_bitcell_2port.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/bitcells/dummy_bitcell_1port.py b/compiler/bitcells/dummy_bitcell_1port.py index 2ff836c1..6573394e 100644 --- a/compiler/bitcells/dummy_bitcell_1port.py +++ b/compiler/bitcells/dummy_bitcell_1port.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/bitcells/dummy_bitcell_2port.py b/compiler/bitcells/dummy_bitcell_2port.py index 5ed56f09..a1a96810 100644 --- a/compiler/bitcells/dummy_bitcell_2port.py +++ b/compiler/bitcells/dummy_bitcell_2port.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/bitcells/dummy_pbitcell.py b/compiler/bitcells/dummy_pbitcell.py index 323f2bc3..c2082033 100644 --- a/compiler/bitcells/dummy_pbitcell.py +++ b/compiler/bitcells/dummy_pbitcell.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index a10af0b3..71679bd1 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/bitcells/replica_bitcell_1port.py b/compiler/bitcells/replica_bitcell_1port.py index efdb5020..191ba4a7 100644 --- a/compiler/bitcells/replica_bitcell_1port.py +++ b/compiler/bitcells/replica_bitcell_1port.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/bitcells/replica_bitcell_2port.py b/compiler/bitcells/replica_bitcell_2port.py index 3a1c7cbf..f75d514f 100644 --- a/compiler/bitcells/replica_bitcell_2port.py +++ b/compiler/bitcells/replica_bitcell_2port.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/bitcells/replica_pbitcell.py b/compiler/bitcells/replica_pbitcell.py index 710cf81d..e3212d6d 100644 --- a/compiler/bitcells/replica_pbitcell.py +++ b/compiler/bitcells/replica_pbitcell.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/bitcells/row_cap_bitcell_1port.py b/compiler/bitcells/row_cap_bitcell_1port.py index c82b3782..9fb1f813 100644 --- a/compiler/bitcells/row_cap_bitcell_1port.py +++ b/compiler/bitcells/row_cap_bitcell_1port.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/bitcells/row_cap_bitcell_2port.py b/compiler/bitcells/row_cap_bitcell_2port.py index 2edee57c..0c2989b0 100644 --- a/compiler/bitcells/row_cap_bitcell_2port.py +++ b/compiler/bitcells/row_cap_bitcell_2port.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/characterizer/__init__.py b/compiler/characterizer/__init__.py index d1f45575..0d9fbe83 100644 --- a/compiler/characterizer/__init__.py +++ b/compiler/characterizer/__init__.py @@ -1,23 +1,26 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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 debug -import globals -from globals import OPTS,find_exe,get_tool +from globals import OPTS, find_exe, get_tool from .lib import * from .delay import * +from .elmore import * +from .linear_regression import * from .setup_hold import * from .functional import * from .simulation import * from .measurements import * from .model_check import * +from .analytical_util import * +from .regression_model import * -debug.info(1,"Initializing characterizer...") +debug.info(1, "Initializing characterizer...") OPTS.spice_exe = "" if not OPTS.analytical_delay: @@ -26,17 +29,17 @@ if not OPTS.analytical_delay: if OPTS.spice_name != "": OPTS.spice_exe=find_exe(OPTS.spice_name) if OPTS.spice_exe=="" or OPTS.spice_exe==None: - debug.error("{0} not found. Unable to perform characterization.".format(OPTS.spice_name),1) + debug.error("{0} not found. Unable to perform characterization.".format(OPTS.spice_name), 1) else: - (OPTS.spice_name,OPTS.spice_exe) = get_tool("spice",["hspice", "ngspice", "ngspice.exe", "xa"]) + (OPTS.spice_name, OPTS.spice_exe) = get_tool("spice", ["ngspice", "ngspice.exe", "hspice", "xa"]) # set the input dir for spice files if using ngspice if OPTS.spice_name == "ngspice": os.environ["NGSPICE_INPUT_DIR"] = "{0}".format(OPTS.openram_temp) if OPTS.spice_exe == "": - debug.error("No recognizable spice version found. Unable to perform characterization.",1) + debug.error("No recognizable spice version found. Unable to perform characterization.", 1) else: - debug.info(1,"Analytical model enabled.") + debug.info(1, "Analytical model enabled.") diff --git a/compiler/characterizer/analytical_util.py b/compiler/characterizer/analytical_util.py new file mode 100644 index 00000000..43435667 --- /dev/null +++ b/compiler/characterizer/analytical_util.py @@ -0,0 +1,310 @@ +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +import debug + +import csv +import math +import numpy as np +import os + +process_transform = {'SS':0.0, 'TT': 0.5, 'FF':1.0} + +def get_data_names(file_name): + """ + Returns just the data names in the first row of the CSV + """ + + with open(file_name, newline='') as csvfile: + csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|') + row_iter = 0 + # reader is iterable not a list, probably a better way to do this + for row in csv_reader: + # Return names from first row + return row[0].split(',') + +def get_data(file_name): + """ + Returns data in CSV as lists of features + """ + + with open(file_name, newline='') as csvfile: + csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|') + row_iter = 0 + for row in csv_reader: + row_iter += 1 + if row_iter == 1: + feature_names = row[0].split(',') + input_list = [[] for _ in feature_names] + scaled_list = [[] for _ in feature_names] + + try: + process_ind = feature_names.index('process') + except: + debug.error('Process not included as a feature.') + continue + + data = [] + split_str = row[0].split(',') + for i in range(len(split_str)): + if i == process_ind: + data.append(process_transform[split_str[i]]) + else: + data.append(float(split_str[i])) + + data[0] = math.log(data[0], 2) + + for i in range(len(data)): + input_list[i].append(data[i]) + + return input_list + +def apply_samples_to_data(all_data, algo_samples): + # Take samples from algorithm and match them to samples in data + data_samples, unused_data = [], [] + sample_positions = set() + for sample in algo_samples: + sample_positions.add(find_sample_position_with_min_error(all_data, sample)) + + for i in range(len(all_data)): + if i in sample_positions: + data_samples.append(all_data[i]) + else: + unused_data.append(all_data[i]) + + return data_samples, unused_data + +def find_sample_position_with_min_error(data, sampled_vals): + min_error = 0 + sample_pos = 0 + count = 0 + for data_slice in data: + error = squared_error(data_slice, sampled_vals) + if min_error == 0 or error < min_error: + min_error = error + sample_pos = count + count += 1 + return sample_pos + +def squared_error(list_a, list_b): + error_sum = 0; + for a,b in zip(list_a, list_b): + error_sum+=(a-b)**2 + return error_sum + + +def get_max_min_from_datasets(dir): + if not os.path.isdir(dir): + debug.warning("Input Directory not found:{}".format(dir)) + return [], [], [] + + # Assuming all files are CSV + data_files = [f for f in os.listdir(dir) if os.path.isfile(os.path.join(dir, f))] + maxs,mins,sums,total_count = [],[],[],0 + for file in data_files: + data = get_data(os.path.join(dir, file)) + # Get max, min, sum, and count from every file + data_max, data_min, data_sum, count = [],[],[], 0 + for feature_list in data: + data_max.append(max(feature_list)) + data_min.append(min(feature_list)) + data_sum.append(sum(feature_list)) + count = len(feature_list) + + # Aggregate the data + if not maxs or not mins or not sums: + maxs,mins,sums,total_count = data_max,data_min,data_sum,count + else: + for i in range(len(maxs)): + maxs[i] = max(data_max[i], maxs[i]) + mins[i] = min(data_min[i], mins[i]) + sums[i] = data_sum[i]+sums[i] + total_count+=count + + avgs = [s/total_count for s in sums] + return maxs,mins,avgs + +def get_max_min_from_file(path): + if not os.path.isfile(path): + debug.warning("Input file not found: {}".format(path)) + return [], [], [] + + + data = get_data(path) + # Get max, min, sum, and count from every file + data_max, data_min, data_sum, count = [],[],[], 0 + for feature_list in data: + data_max.append(max(feature_list)) + data_min.append(min(feature_list)) + data_sum.append(sum(feature_list)) + count = len(feature_list) + + avgs = [s/count for s in data_sum] + return data_max, data_min, avgs + +def get_data_and_scale(file_name, sample_dir): + maxs,mins,avgs = get_max_min_from_datasets(sample_dir) + + # Get data + all_data = get_data(file_name) + + # Scale data from file + self_scaled_data = [[] for _ in range(len(all_data[0]))] + self_maxs,self_mins = [],[] + for feature_list, cur_max, cur_min in zip(all_data,maxs, mins): + for i in range(len(feature_list)): + self_scaled_data[i].append((feature_list[i]-cur_min)/(cur_max-cur_min)) + + return np.asarray(self_scaled_data) + +def rescale_data(data, old_maxs, old_mins, new_maxs, new_mins): + # unscale from old values, rescale by new values + data_new_scaling = [] + for data_row in data: + scaled_row = [] + for val, old_max,old_min, cur_max, cur_min in zip(data_row, old_maxs,old_mins, new_maxs, new_mins): + unscaled_data = val*(old_max-old_min) + old_min + scaled_row.append((unscaled_data-cur_min)/(cur_max-cur_min)) + + data_new_scaling.append(scaled_row) + + return data_new_scaling + +def sample_from_file(num_samples, file_name, sample_dir=None): + """ + Get a portion of the data from CSV file and scale it based on max/min of dataset. + Duplicate samples are trimmed. + """ + + if sample_dir: + maxs,mins,avgs = get_max_min_from_datasets(sample_dir) + else: + maxs,mins,avgs = [], [], [] + + # Get data + all_data = get_data(file_name) + + # Get algorithms sample points, assuming hypercube for now + num_labels = 1 + inp_dims = len(all_data) - num_labels + samples = np.random.rand(num_samples, inp_dims) + + + # Scale data from file + self_scaled_data = [[] for _ in range(len(all_data[0]))] + self_maxs,self_mins = [],[] + for feature_list in all_data: + max_val = max(feature_list) + self_maxs.append(max_val) + min_val = min(feature_list) + self_mins.append(min_val) + for i in range(len(feature_list)): + self_scaled_data[i].append((feature_list[i]-min_val)/(max_val-min_val)) + # Apply algorithm sampling points to available data + sampled_data, unused_data = apply_samples_to_data(self_scaled_data,samples) + + #unscale values and rescale using all available data (both sampled and unused points rescaled) + if len(maxs)!=0 and len(mins)!=0: + sampled_data = rescale_data(sampled_data, self_maxs,self_mins, maxs, mins) + unused_new_scaling = rescale_data(unused_data, self_maxs,self_mins, maxs, mins) + + return np.asarray(sampled_data), np.asarray(unused_new_scaling) + +def get_scaled_data(file_name): + """Get data from CSV file and scale it based on max/min of dataset""" + + if file_name: + maxs,mins,avgs = get_max_min_from_file(file_name) + else: + maxs,mins,avgs = [], [], [] + + # Get data + all_data = get_data(file_name) + + # Data is scaled by max/min and data format is changed to points vs feature lists + self_scaled_data = scale_data_and_transform(all_data) + samples = np.asarray(self_scaled_data) + features, labels = samples[:, :-1], samples[:,-1:] + return features, labels + +def scale_data_and_transform(data): + """ + Assume data is a list of features, change to a list of points and max/min scale + """ + + scaled_data = [[] for _ in range(len(data[0]))] + for feature_list in data: + max_val = max(feature_list) + min_val = min(feature_list) + + for i in range(len(feature_list)): + if max_val == min_val: + scaled_data[i].append(0.0) + else: + scaled_data[i].append((feature_list[i]-min_val)/(max_val-min_val)) + return scaled_data + +def scale_input_datapoint(point, file_path): + """ + Input data has no output and needs to be scaled like the model inputs during + training. + """ + maxs, mins, avgs = get_max_min_from_file(file_path) + debug.info(3, "maxs={}".format(maxs)) + debug.info(3, "mins={}".format(mins)) + debug.info(3, "point={}".format(point)) + + scaled_point = [] + for feature, mx, mn in zip(point, maxs, mins): + if mx == mn: + scaled_point.append(0.0) + else: + scaled_point.append((feature-mn)/(mx-mn)) + return scaled_point + +def unscale_data(data, file_path, pos=None): + if file_path: + maxs,mins,avgs = get_max_min_from_file(file_path) + else: + debug.error("Must provide reference data to unscale") + return None + + # Hard coded to only convert the last max/min (i.e. the label of the data) + if pos == None: + maxs,mins,avgs = [maxs[-1]],[mins[-1]],[avgs[-1]] + else: + maxs,mins,avgs = [maxs[pos]],[mins[pos]],[avgs[pos]] + unscaled_data = [] + for data_row in data: + unscaled_row = [] + for val, cur_max, cur_min in zip(data_row, maxs, mins): + unscaled_val = val*(cur_max-cur_min) + cur_min + unscaled_row.append(unscaled_val) + unscaled_data.append(unscaled_row) + + return unscaled_data + +def abs_error(labels, preds): + total_error = 0 + for label_i, pred_i in zip(labels, preds): + cur_error = abs(label_i[0]-pred_i[0])/label_i[0] + total_error += cur_error + return total_error/len(labels) + +def max_error(labels, preds): + mx_error = 0 + for label_i, pred_i in zip(labels, preds): + cur_error = abs(label_i[0]-pred_i[0])/label_i[0] + mx_error = max(cur_error, mx_error) + return mx_error + +def min_error(labels, preds): + mn_error = 1 + for label_i, pred_i in zip(labels, preds): + cur_error = abs(label_i[0]-pred_i[0])/label_i[0] + mn_error = min(cur_error, mn_error) + return mn_error diff --git a/compiler/characterizer/bit_polarity.py b/compiler/characterizer/bit_polarity.py index 2cd8186a..5b8e5994 100644 --- a/compiler/characterizer/bit_polarity.py +++ b/compiler/characterizer/bit_polarity.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/characterizer/charutils.py b/compiler/characterizer/charutils.py index e3813b36..4ceafbcf 100644 --- a/compiler/characterizer/charutils.py +++ b/compiler/characterizer/charutils.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 5d2dd09a..e2d7e1d2 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. @@ -52,21 +52,6 @@ class delay(simulation): self.create_signal_names() self.add_graph_exclusions() - def create_measurement_names(self): - """ Create measurement names. The names themselves currently define the type of measurement """ - - self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] - self.power_meas_names = ["read0_power", - "read1_power", - "write0_power", - "write1_power", - "disabled_read0_power", - "disabled_read1_power", - "disabled_write0_power", - "disabled_write1_power"] - # self.voltage_when_names = ["volt_bl", "volt_br"] - # self.bitline_delay_names = ["delay_bl", "delay_br"] - def create_measurement_objects(self): """ Create the measurements used for read and write ports """ @@ -525,7 +510,7 @@ class delay(simulation): elif delay_obj.meta_str == sram_op.READ_ONE: meas_cycle_delay = self.cycle_times[self.measure_cycles[port][delay_obj.meta_str]] else: - debug.error("Unrecognised delay Index={}".format(delay_obj.meta_str),1) + debug.error("Unrecognized delay Index={}".format(delay_obj.meta_str),1) # These measurements have there time further delayed to the neg. edge of the clock. if delay_obj.meta_add_delay: @@ -1037,29 +1022,11 @@ class delay(simulation): """ Probe address and data can be set separately to utilize other functions in this characterizer besides analyze. + Netlist reduced for simulation. """ - - self.probe_address = probe_address - self.probe_data = probe_data - self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data) - self.wordline_row = self.get_address_row_number(probe_address) + super().set_probe(probe_address, probe_data) self.prepare_netlist() - def get_data_bit_column_number(self, probe_address, probe_data): - """Calculates bitline column number of data bit under test using bit position and mux size""" - - if self.sram.col_addr_size>0: - col_address = int(probe_address[0:self.sram.col_addr_size], 2) - else: - col_address = 0 - bl_column = int(self.sram.words_per_row * probe_data + col_address) - return bl_column - - def get_address_row_number(self, probe_address): - """Calculates wordline row number of data bit under test using address and column mux size""" - - return int(probe_address[self.sram.col_addr_size:], 2) - def prepare_netlist(self): """ Prepare a trimmed netlist and regular netlist. """ @@ -1303,89 +1270,6 @@ class delay(simulation): # Add test cycle of read/write port pair. One port could have been used already, but the other has not. self.gen_test_cycles_one_port(cur_read_port, cur_write_port) - def sum_delays(self, delays): - """Adds the delays (delay_data objects) so the correct slew is maintained""" - - delay = delays[0] - for i in range(1, len(delays)): - delay+=delays[i] - return delay - - def analytical_delay(self, slews, loads): - """ - Return the analytical model results for the SRAM. - """ - if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0: - debug.warning("In analytical mode, all ports have the timing of the first read port.") - - # Probe set to 0th bit, does not matter for analytical delay. - self.set_probe('0' * self.addr_size, 0) - self.create_graph() - self.set_internal_spice_names() - self.create_measurement_names() - - port = self.read_ports[0] - self.graph.get_all_paths('{}{}'.format("clk", port), - '{}{}_{}'.format(self.dout_name, port, self.probe_data)) - - # Select the path with the bitline (bl) - bl_name, br_name = self.get_bl_name(self.graph.all_paths, port) - bl_path = [path for path in self.graph.all_paths if bl_name in path][0] - - # Set delay/power for slews and loads - port_data = self.get_empty_measure_data_dict() - power = self.analytical_power(slews, loads) - debug.info(1, 'Slew, Load, Delay(ns), Slew(ns)') - max_delay = 0.0 - for slew in slews: - for load in loads: - # Calculate delay based on slew and load - path_delays = self.graph.get_timing(bl_path, self.corner, slew, load) - - total_delay = self.sum_delays(path_delays) - max_delay = max(max_delay, total_delay.delay) - debug.info(1, - '{}, {}, {}, {}'.format(slew, - load, - total_delay.delay / 1e3, - total_delay.slew / 1e3)) - - # Delay is only calculated on a single port and replicated for now. - for port in self.all_ports: - for mname in self.delay_meas_names + self.power_meas_names: - if "power" in mname: - port_data[port][mname].append(power.dynamic) - elif "delay" in mname and port in self.read_ports: - port_data[port][mname].append(total_delay.delay / 1e3) - elif "slew" in mname and port in self.read_ports: - port_data[port][mname].append(total_delay.slew / 1e3) - else: - debug.error("Measurement name not recognized: {}".format(mname), 1) - - # Margin for error in period. Calculated by averaging required margin for a small and large - # memory. FIXME: margin is quite large, should be looked into. - period_margin = 1.85 - sram_data = {"min_period": (max_delay / 1e3) * 2 * period_margin, - "leakage_power": power.leakage} - - debug.info(2, "SRAM Data:\n{}".format(sram_data)) - debug.info(2, "Port Data:\n{}".format(port_data)) - - return (sram_data, port_data) - - def analytical_power(self, slews, loads): - """Get the dynamic and leakage power from the SRAM""" - - # slews unused, only last load is used - load = loads[-1] - power = self.sram.analytical_power(self.corner, load) - # convert from nW to mW - power.dynamic /= 1e6 - power.leakage /= 1e6 - debug.info(1, "Dynamic Power: {0} mW".format(power.dynamic)) - debug.info(1, "Leakage Power: {0} mW".format(power.leakage)) - return power - def gen_data(self): """ Generates the PWL data inputs for a simulation timing test. """ @@ -1412,11 +1296,3 @@ class delay(simulation): self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) if port in self.readwrite_ports: self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) - - def get_empty_measure_data_dict(self): - """Make a dict of lists for each type of delay and power measurement to append results to""" - - measure_names = self.delay_meas_names + self.power_meas_names - # Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists. - measure_data = [{mname: [] for mname in measure_names} for i in self.all_ports] - return measure_data diff --git a/compiler/characterizer/elmore.py b/compiler/characterizer/elmore.py new file mode 100644 index 00000000..b9f99f02 --- /dev/null +++ b/compiler/characterizer/elmore.py @@ -0,0 +1,106 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +from .simulation import simulation +from globals import OPTS +import debug + +class elmore(simulation): + """ + Delay model for the SRAM which calculates Elmore delays along the SRAM critical path. + """ + + def __init__(self, sram, spfile, corner): + super().__init__(sram, spfile, corner) + + # self.targ_read_ports = [] + # self.targ_write_ports = [] + # self.period = 0 + # if self.write_size: + # self.num_wmasks = int(math.ceil(self.word_size / self.write_size)) + # else: + # self.num_wmasks = 0 + #self.set_load_slew(0, 0) + self.set_corner(corner) + self.create_signal_names() + self.add_graph_exclusions() + + def get_lib_values(self, slews, loads): + """ + Return the analytical model results for the SRAM. + """ + if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0: + debug.warning("In analytical mode, all ports have the timing of the first read port.") + + # Probe set to 0th bit, does not matter for analytical delay. + self.set_probe('0' * self.addr_size, 0) + self.create_graph() + self.set_internal_spice_names() + self.create_measurement_names() + + port = self.read_ports[0] + self.graph.get_all_paths('{}{}'.format("clk", port), + '{}{}_{}'.format(self.dout_name, port, self.probe_data)) + + # Select the path with the bitline (bl) + bl_name, br_name = self.get_bl_name(self.graph.all_paths, port) + bl_path = [path for path in self.graph.all_paths if bl_name in path][0] + + # Set delay/power for slews and loads + port_data = self.get_empty_measure_data_dict() + power = self.analytical_power(slews, loads) + debug.info(1, 'Slew, Load, Delay(ns), Slew(ns)') + max_delay = 0.0 + for slew in slews: + for load in loads: + # Calculate delay based on slew and load + path_delays = self.graph.get_timing(bl_path, self.corner, slew, load) + + total_delay = self.sum_delays(path_delays) + max_delay = max(max_delay, total_delay.delay) + debug.info(1, + '{}, {}, {}, {}'.format(slew, + load, + total_delay.delay / 1e3, + total_delay.slew / 1e3)) + + # Delay is only calculated on a single port and replicated for now. + for port in self.all_ports: + for mname in self.delay_meas_names + self.power_meas_names: + if "power" in mname: + port_data[port][mname].append(power.dynamic) + elif "delay" in mname and port in self.read_ports: + port_data[port][mname].append(total_delay.delay / 1e3) + elif "slew" in mname and port in self.read_ports: + port_data[port][mname].append(total_delay.slew / 1e3) + else: + debug.error("Measurement name not recognized: {}".format(mname), 1) + + # Margin for error in period. Calculated by averaging required margin for a small and large + # memory. FIXME: margin is quite large, should be looked into. + period_margin = 1.85 + sram_data = {"min_period": (max_delay / 1e3) * 2 * period_margin, + "leakage_power": power.leakage} + + debug.info(2, "SRAM Data:\n{}".format(sram_data)) + debug.info(2, "Port Data:\n{}".format(port_data)) + + return (sram_data, port_data) + + def analytical_power(self, slews, loads): + """Get the dynamic and leakage power from the SRAM""" + + # slews unused, only last load is used + load = loads[-1] + power = self.sram.analytical_power(self.corner, load) + # convert from nW to mW + power.dynamic /= 1e6 + power.leakage /= 1e6 + debug.info(1, "Dynamic Power: {0} mW".format(power.dynamic)) + debug.info(1, "Leakage Power: {0} mW".format(power.leakage)) + return power \ No newline at end of file diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index 8a511c42..fb0a176f 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 05db4147..d37119a9 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. @@ -74,26 +74,38 @@ class lib: self.corners = [] self.lib_files = [] - - # Nominal corner - corner_set = set() - nom_corner = (nom_process, nom_supply, nom_temperature) - corner_set.add(nom_corner) - if not OPTS.nominal_corner_only: - # Temperature corners - corner_set.add((nom_process, nom_supply, min_temperature)) - corner_set.add((nom_process, nom_supply, max_temperature)) - # Supply corners - corner_set.add((nom_process, min_supply, nom_temperature)) - corner_set.add((nom_process, max_supply, nom_temperature)) - # Process corners - corner_set.add((min_process, nom_supply, nom_temperature)) - corner_set.add((max_process, nom_supply, nom_temperature)) - - # Enforce that nominal corner is the first to be characterized - self.add_corner(*nom_corner) - corner_set.remove(nom_corner) - for corner_tuple in corner_set: + + if OPTS.use_specified_corners == None: + # Nominal corner + corner_tuples = set() + if OPTS.only_use_config_corners: + if OPTS.nominal_corner_only: + debug.warning("Nominal corner only option ignored if use only config corners is set.") + # Generate a powerset of input PVT lists + for p in self.process_corners: + for v in self.supply_voltages: + for t in self.temperatures: + corner_tuples.add((p, v, t)) + else: + nom_corner = (nom_process, nom_supply, nom_temperature) + corner_tuples.add(nom_corner) + if not OPTS.nominal_corner_only: + # Temperature corners + corner_tuples.add((nom_process, nom_supply, min_temperature)) + corner_tuples.add((nom_process, nom_supply, max_temperature)) + # Supply corners + corner_tuples.add((nom_process, min_supply, nom_temperature)) + corner_tuples.add((nom_process, max_supply, nom_temperature)) + # Process corners + corner_tuples.add((min_process, nom_supply, nom_temperature)) + corner_tuples.add((max_process, nom_supply, nom_temperature)) + # Enforce that nominal corner is the first to be characterized + self.add_corner(*nom_corner) + corner_tuples.remove(nom_corner) + else: + corner_tuples = OPTS.use_specified_corners + + for corner_tuple in corner_tuples: self.add_corner(*corner_tuple) def add_corner(self, proc, volt, temp): @@ -581,18 +593,28 @@ class lib: def compute_delay(self): """Compute SRAM delays for current corner""" - self.d = delay(self.sram, self.sp_file, self.corner) if self.use_model: - char_results = self.d.analytical_delay(self.slews,self.loads) - self.char_sram_results, self.char_port_results = char_results + model_name_lc = OPTS.model_name.lower() + if model_name_lc == "linear_regression": + from .linear_regression import linear_regression as model + elif model_name_lc == "elmore": + from .elmore import elmore as model + else: + debug.error("{} model not recognized. See options.py for available models.".format(OPTS.model_name)) + import math + + m = model(self.sram, self.sp_file, self.corner) + char_results = m.get_lib_values(self.slews,self.loads) + else: + self.d = delay(self.sram, self.sp_file, self.corner) if (self.sram.num_spare_rows == 0): probe_address = "1" * self.sram.addr_size else: probe_address = "0" + "1" * (self.sram.addr_size - 1) probe_data = self.sram.word_size - 1 char_results = self.d.analyze(probe_address, probe_data, self.slews, self.loads) - self.char_sram_results, self.char_port_results = char_results + self.char_sram_results, self.char_port_results = char_results def compute_setup_hold(self): """ Do the analysis if we haven't characterized a FF yet """ @@ -607,6 +629,79 @@ class lib: def parse_info(self,corner,lib_name): """ Copies important characterization data to datasheet.info to be added to datasheet """ + if OPTS.output_datasheet_info: + datasheet_path = OPTS.output_path + else: + datasheet_path = OPTS.openram_temp + datasheet = open(datasheet_path +'/datasheet.info', 'a+') + + self.write_inp_params_datasheet(datasheet, corner, lib_name) + self.write_signal_from_ports(datasheet, + "din{1}[{0}:0]".format(self.sram.word_size - 1, '{}'), + self.write_ports, + "setup_times_LH", + "setup_times_HL", + "hold_times_LH", + "hold_times_HL") + + # self.write_signal_from_ports(datasheet, + # "dout{1}[{0}:0]".format(self.sram.word_size - 1, '{}'), + # self.read_ports, + # "delay_lh", + # "delay_hl", + # "slew_lh", + # "slew_hl") + for port in self.all_ports: + #dout timing + if port in self.read_ports: + datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( + "dout{1}[{0}:0]".format(self.sram.word_size - 1, port), + min(list(map(round_time,self.char_port_results[port]["delay_lh"]))), + max(list(map(round_time,self.char_port_results[port]["delay_lh"]))), + + min(list(map(round_time,self.char_port_results[port]["delay_hl"]))), + max(list(map(round_time,self.char_port_results[port]["delay_hl"]))), + + min(list(map(round_time,self.char_port_results[port]["slew_lh"]))), + max(list(map(round_time,self.char_port_results[port]["slew_lh"]))), + + min(list(map(round_time,self.char_port_results[port]["slew_hl"]))), + max(list(map(round_time,self.char_port_results[port]["slew_hl"]))) + )) + + self.write_signal_from_ports(datasheet, + "csb{}", + self.all_ports, + "setup_times_LH", + "setup_times_HL", + "hold_times_LH", + "hold_times_HL") + + self.write_signal_from_ports(datasheet, + "addr{1}[{0}:0]".format(self.sram.addr_size - 1, '{}'), + self.all_ports, + "setup_times_LH", + "setup_times_HL", + "hold_times_LH", + "hold_times_HL") + + self.write_signal_from_ports(datasheet, + "web{}", + self.readwrite_ports, + "setup_times_LH", + "setup_times_HL", + "hold_times_LH", + "hold_times_HL") + + self.write_power_datasheet(datasheet) + + self.write_model_params(datasheet) + + datasheet.write("END\n") + datasheet.close() + + def write_inp_params_datasheet(self, datasheet, corner, lib_name): + if OPTS.is_unit_test: git_id = 'FFFFFFFFFFFFFFFFFFFF' @@ -624,11 +719,9 @@ class lib: # check if git id is valid if len(git_id) != 40: debug.warning("Failed to retrieve git id") - git_id = 'Failed to retruieve' - - datasheet = open(OPTS.openram_temp +'/datasheet.info', 'a+') - + git_id = 'Failed to retrieve' current_time = datetime.date.today() + # write static information to be parser later datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},".format( OPTS.output_name, @@ -656,104 +749,26 @@ class lib: # write area datasheet.write(str(self.sram.width * self.sram.height) + ',') - - # write timing information for all ports - for port in self.all_ports: - #din timings - if port in self.write_ports: - datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( - "din{1}[{0}:0]".format(self.sram.word_size - 1, port), - min(list(map(round_time,self.times["setup_times_LH"]))), - max(list(map(round_time,self.times["setup_times_LH"]))), - - min(list(map(round_time,self.times["setup_times_HL"]))), - max(list(map(round_time,self.times["setup_times_HL"]))), - - min(list(map(round_time,self.times["hold_times_LH"]))), - max(list(map(round_time,self.times["hold_times_LH"]))), - - min(list(map(round_time,self.times["hold_times_HL"]))), - max(list(map(round_time,self.times["hold_times_HL"]))) - - )) - - for port in self.all_ports: - #dout timing - if port in self.read_ports: - datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( - "dout{1}[{0}:0]".format(self.sram.word_size - 1, port), - min(list(map(round_time,self.char_port_results[port]["delay_lh"]))), - max(list(map(round_time,self.char_port_results[port]["delay_lh"]))), - - min(list(map(round_time,self.char_port_results[port]["delay_hl"]))), - max(list(map(round_time,self.char_port_results[port]["delay_hl"]))), - - min(list(map(round_time,self.char_port_results[port]["slew_lh"]))), - max(list(map(round_time,self.char_port_results[port]["slew_lh"]))), - - min(list(map(round_time,self.char_port_results[port]["slew_hl"]))), - max(list(map(round_time,self.char_port_results[port]["slew_hl"]))) - - - )) - - for port in self.all_ports: - #csb timings + + def write_signal_from_ports(self, datasheet, signal, ports, time_pos_1, time_pos_2, time_pos_3, time_pos_4): + for port in ports: datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( - "csb{0}".format(port), - min(list(map(round_time,self.times["setup_times_LH"]))), - max(list(map(round_time,self.times["setup_times_LH"]))), + signal.format(port), + min(list(map(round_time,self.times[time_pos_1]))), + max(list(map(round_time,self.times[time_pos_1]))), - min(list(map(round_time,self.times["setup_times_HL"]))), - max(list(map(round_time,self.times["setup_times_HL"]))), + min(list(map(round_time,self.times[time_pos_2]))), + max(list(map(round_time,self.times[time_pos_2]))), - min(list(map(round_time,self.times["hold_times_LH"]))), - max(list(map(round_time,self.times["hold_times_LH"]))), + min(list(map(round_time,self.times[time_pos_3]))), + max(list(map(round_time,self.times[time_pos_3]))), - min(list(map(round_time,self.times["hold_times_HL"]))), - max(list(map(round_time,self.times["hold_times_HL"]))) + min(list(map(round_time,self.times[time_pos_4]))), + max(list(map(round_time,self.times[time_pos_4]))) )) - - for port in self.all_ports: - #addr timings - datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( - "addr{1}[{0}:0]".format(self.sram.addr_size - 1, port), - min(list(map(round_time,self.times["setup_times_LH"]))), - max(list(map(round_time,self.times["setup_times_LH"]))), - - min(list(map(round_time,self.times["setup_times_HL"]))), - max(list(map(round_time,self.times["setup_times_HL"]))), - - min(list(map(round_time,self.times["hold_times_LH"]))), - max(list(map(round_time,self.times["hold_times_LH"]))), - - min(list(map(round_time,self.times["hold_times_HL"]))), - max(list(map(round_time,self.times["hold_times_HL"]))) - - )) - - - for port in self.all_ports: - if port in self.readwrite_ports: - - #web timings - datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( - "web{0}".format(port), - min(list(map(round_time,self.times["setup_times_LH"]))), - max(list(map(round_time,self.times["setup_times_LH"]))), - - min(list(map(round_time,self.times["setup_times_HL"]))), - max(list(map(round_time,self.times["setup_times_HL"]))), - - min(list(map(round_time,self.times["hold_times_LH"]))), - max(list(map(round_time,self.times["hold_times_LH"]))), - - min(list(map(round_time,self.times["hold_times_HL"]))), - max(list(map(round_time,self.times["hold_times_HL"]))) - - )) - + + def write_power_datasheet(self, datasheet): # write power information for port in self.all_ports: name = '' @@ -792,7 +807,31 @@ class lib: control_str += ' & csb{0}'.format(i) datasheet.write("{0},{1},{2},".format('leak', control_str, self.char_sram_results["leakage_power"])) - - - datasheet.write("END\n") - datasheet.close() + + def write_model_params(self, datasheet): + """Write values which will be used in the analytical model as inputs""" + datasheet.write("{0},{1},".format('words_per_row', OPTS.words_per_row)) + datasheet.write("{0},{1},".format('slews', list(self.slews))) + datasheet.write("{0},{1},".format('loads', list(self.loads))) + + for port in self.read_ports: + datasheet.write("{0},{1},".format('cell_rise_{}'.format(port), self.char_port_results[port]["delay_lh"])) + datasheet.write("{0},{1},".format('cell_fall_{}'.format(port), self.char_port_results[port]["delay_hl"])) + datasheet.write("{0},{1},".format('rise_transition_{}'.format(port), self.char_port_results[port]["slew_lh"])) + datasheet.write("{0},{1},".format('fall_transition_{}'.format(port), self.char_port_results[port]["slew_hl"])) + + for port in self.write_ports: + write1_power = np.mean(self.char_port_results[port]["write1_power"]) + write0_power = np.mean(self.char_port_results[port]["write0_power"]) + datasheet.write("{0},{1},".format('write_rise_power_{}'.format(port), write1_power)) + #FIXME: should be write_fall_power + datasheet.write("{0},{1},".format('read_fall_power_{}'.format(port), write0_power)) + + for port in self.read_ports: + read1_power = np.mean(self.char_port_results[port]["read1_power"]) + read0_power = np.mean(self.char_port_results[port]["read0_power"]) + datasheet.write("{0},{1},".format('read_rise_power_{}'.format(port), read1_power)) + #FIXME: should be read_fall_power + datasheet.write("{0},{1},".format('write_fall_power_{}'.format(port), read0_power)) + + \ No newline at end of file diff --git a/compiler/characterizer/linear_regression.py b/compiler/characterizer/linear_regression.py new file mode 100644 index 00000000..fac7a170 --- /dev/null +++ b/compiler/characterizer/linear_regression.py @@ -0,0 +1,38 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +from .regression_model import regression_model +from globals import OPTS +import debug + +from sklearn.linear_model import LinearRegression + + +class linear_regression(regression_model): + + def __init__(self, sram, spfile, corner): + super().__init__(sram, spfile, corner) + + def generate_model(self, features, labels): + """ + Supervised training of model. + """ + + model = LinearRegression() + model.fit(features, labels) + return model + + def model_prediction(self, model, features): + """ + Have the model perform a prediction and unscale the prediction + as the model is trained with scaled values. + """ + + pred = model.predict(features) + return pred + \ No newline at end of file diff --git a/compiler/characterizer/logical_effort.py b/compiler/characterizer/logical_effort.py index 6656e5fd..20225ebe 100644 --- a/compiler/characterizer/logical_effort.py +++ b/compiler/characterizer/logical_effort.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/characterizer/measurements.py b/compiler/characterizer/measurements.py index 7900dd2b..b1896880 100644 --- a/compiler/characterizer/measurements.py +++ b/compiler/characterizer/measurements.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index 4ff67b39..f72c3211 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/characterizer/neural_network.py b/compiler/characterizer/neural_network.py new file mode 100644 index 00000000..57db4580 --- /dev/null +++ b/compiler/characterizer/neural_network.py @@ -0,0 +1,48 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +from .regression_model import regression_model +from globals import OPTS +import debug + +from tensorflow import keras +from tensorflow.keras import layers +import tensorflow as tf + + +class neural_network(regression_model): + + def __init__(self, sram, spfile, corner): + super().__init__(sram, spfile, corner) + + def generate_model(self, features, labels): + """ + Supervised training of model. + """ + + model = keras.Sequential([ + layers.Dense(32, activation=tf.nn.relu, input_shape=[features.shape[1]]), + layers.Dense(32, activation=tf.nn.relu), + layers.Dense(32, activation=tf.nn.relu), + layers.Dense(1) + ]) + + optimizer = keras.optimizers.RMSprop(0.0099) + model.compile(loss='mean_squared_error', optimizer=optimizer) + model.fit(features, labels, epochs=100, verbose=0) + return model + + def model_prediction(self, model, features): + """ + Have the model perform a prediction and unscale the prediction + as the model is trained with scaled values. + """ + + pred = model.predict(features) + return pred + \ No newline at end of file diff --git a/compiler/characterizer/regression_model.py b/compiler/characterizer/regression_model.py new file mode 100644 index 00000000..d9c2359d --- /dev/null +++ b/compiler/characterizer/regression_model.py @@ -0,0 +1,140 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +from .analytical_util import * +from .simulation import simulation +from globals import OPTS +import debug + +import math + +relative_data_path = "/sim_data" +data_fnames = ["rise_delay.csv", + "fall_delay.csv", + "rise_slew.csv", + "fall_slew.csv", + "write1_power.csv", + "write0_power.csv", + "read1_power.csv", + "read0_power.csv", + "leakage_data.csv"] +# Positions must correspond to data_fname list +lib_dnames = ["delay_lh", + "delay_hl", + "slew_lh", + "slew_hl", + "write1_power", + "write0_power", + "read1_power", + "read0_power", + "leakage_power"] +# Check if another data dir was specified +if OPTS.sim_data_path == None: + data_dir = OPTS.openram_tech+relative_data_path +else: + data_dir = OPTS.sim_data_path + +data_paths = {dname:data_dir +'/'+fname for dname, fname in zip(lib_dnames, data_fnames)} + +class regression_model(simulation): + + def __init__(self, sram, spfile, corner): + super().__init__(sram, spfile, corner) + self.set_corner(corner) + + def get_lib_values(self, slews, loads): + """ + A model and prediction is created for each output needed for the LIB + """ + + debug.info(1, "Characterizing SRAM using linear regression models.") + log_num_words = math.log(OPTS.num_words, 2) + model_inputs = [log_num_words, + OPTS.word_size, + OPTS.words_per_row, + self.sram.width * self.sram.height, + process_transform[self.process], + self.vdd_voltage, + self.temperature] + + self.create_measurement_names() + models = self.train_models() + + # Set delay/power for slews and loads + port_data = self.get_empty_measure_data_dict() + debug.info(1, 'Slew, Load, Port, Delay(ns), Slew(ns)') + max_delay = 0.0 + for slew in slews: + for load in loads: + # List returned with value order being delay, power, leakage, slew + sram_vals = self.get_predictions(model_inputs+[slew, load], models) + # Delay is only calculated on a single port and replicated for now. + for port in self.all_ports: + port_data[port]['delay_lh'].append(sram_vals['delay_lh']) + port_data[port]['delay_hl'].append(sram_vals['delay_hl']) + port_data[port]['slew_lh'].append(sram_vals['slew_lh']) + port_data[port]['slew_hl'].append(sram_vals['slew_hl']) + + port_data[port]['write1_power'].append(sram_vals['write1_power']) + port_data[port]['write0_power'].append(sram_vals['write0_power']) + port_data[port]['read1_power'].append(sram_vals['read1_power']) + port_data[port]['read0_power'].append(sram_vals['read0_power']) + + # Disabled power not modeled. Copied from other power predictions + port_data[port]['disabled_write1_power'].append(sram_vals['write1_power']) + port_data[port]['disabled_write0_power'].append(sram_vals['write0_power']) + port_data[port]['disabled_read1_power'].append(sram_vals['read1_power']) + port_data[port]['disabled_read0_power'].append(sram_vals['read0_power']) + + debug.info(1, '{}, {}, {}, {}, {}'.format(slew, + load, + port, + sram_vals['delay_lh'], + sram_vals['slew_lh'])) + # Estimate the period as double the delay with margin + period_margin = 0.1 + sram_data = {"min_period": sram_vals['delay_lh'] * 2, + "leakage_power": sram_vals["leakage_power"]} + + debug.info(2, "SRAM Data:\n{}".format(sram_data)) + debug.info(2, "Port Data:\n{}".format(port_data)) + + return (sram_data, port_data) + + def get_predictions(self, model_inputs, models): + """ + Generate a model and prediction for LIB output + """ + + #Scaled the inputs using first data file as a reference + data_name = lib_dnames[0] + scaled_inputs = np.asarray([scale_input_datapoint(model_inputs, data_paths[data_name])]) + + predictions = {} + for dname in data_paths.keys(): + path = data_paths[dname] + m = models[dname] + + features, labels = get_scaled_data(path) + scaled_pred = self.model_prediction(m, scaled_inputs) + pred = unscale_data(scaled_pred.tolist(), path) + debug.info(2,"Unscaled Prediction = {}".format(pred)) + predictions[dname] = pred[0][0] + return predictions + + def train_models(self): + """ + Generate and return models + """ + models = {} + for dname, dpath in data_paths.items(): + features, labels = get_scaled_data(dpath) + model = self.generate_model(features, labels) + models[dname] = model + return models + \ No newline at end of file diff --git a/compiler/characterizer/setup_hold.py b/compiler/characterizer/setup_hold.py index 688e6535..83ec835b 100644 --- a/compiler/characterizer/setup_hold.py +++ b/compiler/characterizer/setup_hold.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index f6ee260d..0617bfcd 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. @@ -39,6 +39,21 @@ class simulation(): else: self.num_wmasks = 0 + def create_measurement_names(self): + """ Create measurement names. The names themselves currently define the type of measurement """ + + self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] + self.power_meas_names = ["read0_power", + "read1_power", + "write0_power", + "write1_power", + "disabled_read0_power", + "disabled_read1_power", + "disabled_write0_power", + "disabled_write1_power"] + # self.voltage_when_names = ["volt_bl", "volt_br"] + # self.bitline_delay_names = ["delay_bl", "delay_br"] + def set_corner(self, corner): """ Set the corner values """ self.corner = corner @@ -92,6 +107,32 @@ class simulation(): self.cycle_comments = [] self.fn_cycle_comments = [] + def set_probe(self, probe_address, probe_data): + """ + Probe address and data can be set separately to utilize other + functions in this characterizer besides analyze. + """ + + self.probe_address = probe_address + self.probe_data = probe_data + self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data) + self.wordline_row = self.get_address_row_number(probe_address) + + def get_data_bit_column_number(self, probe_address, probe_data): + """Calculates bitline column number of data bit under test using bit position and mux size""" + + if self.sram.col_addr_size>0: + col_address = int(probe_address[0:self.sram.col_addr_size], 2) + else: + col_address = 0 + bl_column = int(self.sram.words_per_row * probe_data + col_address) + return bl_column + + def get_address_row_number(self, probe_address): + """Calculates wordline row number of data bit under test using address and column mux size""" + + return int(probe_address[self.sram.col_addr_size:], 2) + def add_control_one_port(self, port, op): """Appends control signals for operation to a given port""" # Determine values to write to port @@ -544,6 +585,21 @@ class simulation(): for i in range(len(bl_names)): bl_names[i] = bl_names[i].split('.')[-1] return bl_names[0], bl_names[1] - + + def get_empty_measure_data_dict(self): + """Make a dict of lists for each type of delay and power measurement to append results to""" + + measure_names = self.delay_meas_names + self.power_meas_names + # Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists. + measure_data = [{mname: [] for mname in measure_names} for i in self.all_ports] + return measure_data + + def sum_delays(self, delays): + """Adds the delays (delay_data objects) so the correct slew is maintained""" + + delay = delays[0] + for i in range(1, len(delays)): + delay+=delays[i] + return delay diff --git a/compiler/characterizer/sram_op.py b/compiler/characterizer/sram_op.py index 58999ca0..e8696347 100644 --- a/compiler/characterizer/sram_op.py +++ b/compiler/characterizer/sram_op.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index b7a84cb6..035e9e58 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. @@ -312,12 +312,12 @@ class stimuli(): cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt {3}".format(OPTS.spice_exe, temp_stim, OPTS.openram_temp, - OPTS.num_threads) + OPTS.num_sim_threads) valid_retcode=0 elif OPTS.spice_name == "hspice": # TODO: Should make multithreading parameter a configuration option cmd = "{0} -mt {1} -i {2} -o {3}timing".format(OPTS.spice_exe, - OPTS.num_threads, + OPTS.num_sim_threads, temp_stim, OPTS.openram_temp) valid_retcode=0 @@ -326,7 +326,7 @@ class stimuli(): # Measurements can't be made with a raw file set in ngspice # -r {2}timing.raw ng_cfg = open("{}.spiceinit".format(OPTS.openram_temp), "w") - ng_cfg.write("set num_threads={}\n".format(OPTS.num_threads)) + ng_cfg.write("set num_threads={}\n".format(OPTS.num_sim_threads)) ng_cfg.close() cmd = "{0} -b -o {2}timing.lis {1}".format(OPTS.spice_exe, diff --git a/compiler/characterizer/trim_spice.py b/compiler/characterizer/trim_spice.py index 11c1216f..d659212c 100644 --- a/compiler/characterizer/trim_spice.py +++ b/compiler/characterizer/trim_spice.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/custom/dff.py b/compiler/custom/dff.py index b0c2b5a8..33bd2b3a 100644 --- a/compiler/custom/dff.py +++ b/compiler/custom/dff.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/custom/inv_dec.py b/compiler/custom/inv_dec.py index a1bb0c83..d963783f 100644 --- a/compiler/custom/inv_dec.py +++ b/compiler/custom/inv_dec.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/custom/nand2_dec.py b/compiler/custom/nand2_dec.py index b5c48a15..98992f42 100644 --- a/compiler/custom/nand2_dec.py +++ b/compiler/custom/nand2_dec.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/custom/nand3_dec.py b/compiler/custom/nand3_dec.py index 2713fde9..34890a9c 100644 --- a/compiler/custom/nand3_dec.py +++ b/compiler/custom/nand3_dec.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/custom/nand4_dec.py b/compiler/custom/nand4_dec.py index 5fab2d36..d89dc926 100644 --- a/compiler/custom/nand4_dec.py +++ b/compiler/custom/nand4_dec.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/custom/sense_amp.py b/compiler/custom/sense_amp.py index 3de664fc..d3268713 100644 --- a/compiler/custom/sense_amp.py +++ b/compiler/custom/sense_amp.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/custom/tri_gate.py b/compiler/custom/tri_gate.py index 6be58104..f0c0afeb 100644 --- a/compiler/custom/tri_gate.py +++ b/compiler/custom/tri_gate.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/custom/write_driver.py b/compiler/custom/write_driver.py index 755fdb03..6d5c4018 100644 --- a/compiler/custom/write_driver.py +++ b/compiler/custom/write_driver.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/datasheet/add_db.py b/compiler/datasheet/add_db.py index 2ad546a9..7d689f4c 100644 --- a/compiler/datasheet/add_db.py +++ b/compiler/datasheet/add_db.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. diff --git a/compiler/datasheet/datasheet.py b/compiler/datasheet/datasheet.py index 35091d36..612a91df 100644 --- a/compiler/datasheet/datasheet.py +++ b/compiler/datasheet/datasheet.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California and The Board +# Copyright (c) 2016-2021 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. @@ -28,7 +28,11 @@ class datasheet(): # css styling is kept in a seperate file self.html += datasheet_css.read() - with open(OPTS.openram_temp + "/datasheet.info") as info: + if OPTS.output_datasheet_info: + datasheet_path = OPTS.output_path + else: + datasheet_path = OPTS.openram_temp + with open(datasheet_path + "/datasheet.info") as info: self.html += '