From 4567c2ebcddfea945bb1b89f649d2891bca2374e Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 10 Mar 2022 08:37:48 -0800 Subject: [PATCH 01/44] Add space after docker command. Regress to klayout v0.27.4 --- compiler/tests/Makefile | 4 ++-- docker/Dockerfile | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/tests/Makefile b/compiler/tests/Makefile index c5d508c4..2cd5c17d 100644 --- a/compiler/tests/Makefile +++ b/compiler/tests/Makefile @@ -120,8 +120,8 @@ $(TEST_BASES): @mkdir -p results/$*/tmp @docker run \ -v $(TOP_DIR):/openram \ - -v $(FREEPDK45):/freepdk45\ - -e FREEPDK45=/freepdk45\ + -v $(FREEPDK45):/freepdk45 \ + -e FREEPDK45=/freepdk45 \ -v $(PDK_ROOT):/pdk \ -e PDK_ROOT=/pdk \ -e PDKPATH=/pdk/sky130A \ diff --git a/docker/Dockerfile b/docker/Dockerfile index e1045107..0913cfba 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -24,7 +24,8 @@ RUN apt-get install --no-install-recommends -y libx11-dev libcairo2-dev RUN apt-get install --no-install-recommends -y qt5-default qtcreator ruby-full ruby-dev python3-dev qtmultimedia5-dev libqt5multimediawidgets5 libqt5multimedia5-plugins libqt5multimedia5 libqt5svg5-dev libqt5designer5 libqt5designercomponents5 libqt5xmlpatterns5-dev qttools5-dev ### Klayout ### -ARG KLAYOUT_COMMIT=v0.27.8 +#ARG KLAYOUT_COMMIT=v0.27.8 +ARG KLAYOUT_COMMIT=ea1bf40a1ee1c1c934e47a0020417503ab3d7e7e WORKDIR /root RUN git clone https://github.com/KLayout/klayout WORKDIR /root/klayout From c2589fbb398b07f0ef8c73a87f9bbaad58e32c39 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 10 Mar 2022 11:48:53 -0800 Subject: [PATCH 02/44] Change docker to use debug klayout --- docker/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 0913cfba..3922477c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -30,8 +30,8 @@ WORKDIR /root RUN git clone https://github.com/KLayout/klayout WORKDIR /root/klayout RUN git checkout ${KLAYOUT_COMMIT} -RUN ./build.sh -qt5 -j 8 \ - && cp -r bin-release /usr/local/klayout +RUN ./build.sh -qt5 -debug -j 8 \ + && cp -r bin-debug /usr/local/klayout RUN rm -rf /root/klayout ### Trilinos ### From b981ad58149cb03e8c06c5eba1ffce30e5cf4fe0 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 11 Mar 2022 10:17:24 -0800 Subject: [PATCH 03/44] Erase duplicate macro configs --- macros/configs/riscv_sky130_1kbyte_1rw.py | 25 --------------------- macros/configs/riscv_sky130_1kbyte_1rw1r.py | 25 --------------------- macros/configs/riscv_sky130_2kbyte_1rw.py | 25 --------------------- macros/configs/riscv_sky130_2kbyte_1rw1r.py | 25 --------------------- macros/configs/riscv_sky130_4kbyte_1rw.py | 25 --------------------- macros/configs/riscv_sky130_4kbyte_1rw1r.py | 25 --------------------- 6 files changed, 150 deletions(-) delete mode 100644 macros/configs/riscv_sky130_1kbyte_1rw.py delete mode 100644 macros/configs/riscv_sky130_1kbyte_1rw1r.py delete mode 100644 macros/configs/riscv_sky130_2kbyte_1rw.py delete mode 100644 macros/configs/riscv_sky130_2kbyte_1rw1r.py delete mode 100644 macros/configs/riscv_sky130_4kbyte_1rw.py delete mode 100644 macros/configs/riscv_sky130_4kbyte_1rw1r.py diff --git a/macros/configs/riscv_sky130_1kbyte_1rw.py b/macros/configs/riscv_sky130_1kbyte_1rw.py deleted file mode 100644 index 9bdf47ed..00000000 --- a/macros/configs/riscv_sky130_1kbyte_1rw.py +++ /dev/null @@ -1,25 +0,0 @@ -word_size = 32 -num_words = 256 -write_size = 8 - -local_array_size = 16 - -num_rw_ports = 1 -num_r_ports = 0 -num_w_ports = 0 - -tech_name = "sky130" -nominal_corner_only = True - -route_supplies = False -check_lvsdrc = True -perimeter_pins = False -#netlist_only = True -#analytical_delay = False -output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports, - num_r_ports, - num_w_ports, - word_size, - num_words, - tech_name) -output_path = "macro/{}".format(output_name) diff --git a/macros/configs/riscv_sky130_1kbyte_1rw1r.py b/macros/configs/riscv_sky130_1kbyte_1rw1r.py deleted file mode 100644 index d0b47857..00000000 --- a/macros/configs/riscv_sky130_1kbyte_1rw1r.py +++ /dev/null @@ -1,25 +0,0 @@ -word_size = 32 -num_words = 256 -write_size = 8 - -#local_array_size = 16 - -num_rw_ports = 1 -num_r_ports = 1 -num_w_ports = 0 - -tech_name = "sky130" -nominal_corner_only = True - -#route_supplies = False -check_lvsdrc = True -#perimeter_pins = False -#netlist_only = True -#analytical_delay = False -output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports, - num_r_ports, - num_w_ports, - word_size, - num_words, - tech_name) -output_path = "macro/{}".format(output_name) diff --git a/macros/configs/riscv_sky130_2kbyte_1rw.py b/macros/configs/riscv_sky130_2kbyte_1rw.py deleted file mode 100644 index b85df3f9..00000000 --- a/macros/configs/riscv_sky130_2kbyte_1rw.py +++ /dev/null @@ -1,25 +0,0 @@ -word_size = 32 -num_words = 512 -write_size = 8 - -local_array_size = 16 - -num_rw_ports = 1 -num_r_ports = 0 -num_w_ports = 0 - -tech_name = "sky130" -nominal_corner_only = True - -route_supplies = False -check_lvsdrc = True -perimeter_pins = False -#netlist_only = True -#analytical_delay = False -output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports, - num_r_ports, - num_w_ports, - word_size, - num_words, - tech_name) -output_path = "macro/{}".format(output_name) diff --git a/macros/configs/riscv_sky130_2kbyte_1rw1r.py b/macros/configs/riscv_sky130_2kbyte_1rw1r.py deleted file mode 100644 index e94882e9..00000000 --- a/macros/configs/riscv_sky130_2kbyte_1rw1r.py +++ /dev/null @@ -1,25 +0,0 @@ -word_size = 32 -num_words = 512 -write_size = 8 - -local_array_size = 16 - -num_rw_ports = 1 -num_r_ports = 1 -num_w_ports = 0 - -tech_name = "sky130" -nominal_corner_only = True - -route_supplies = False -check_lvsdrc = True -perimeter_pins = False -#netlist_only = True -#analytical_delay = False -output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports, - num_r_ports, - num_w_ports, - word_size, - num_words, - tech_name) -output_path = "macro/{}".format(output_name) diff --git a/macros/configs/riscv_sky130_4kbyte_1rw.py b/macros/configs/riscv_sky130_4kbyte_1rw.py deleted file mode 100644 index 1b6cdc07..00000000 --- a/macros/configs/riscv_sky130_4kbyte_1rw.py +++ /dev/null @@ -1,25 +0,0 @@ -word_size = 32 -num_words = 1024 -write_size = 8 - -local_array_size = 16 - -num_rw_ports = 1 -num_r_ports = 0 -num_w_ports = 0 - -tech_name = "sky130" -nominal_corner_only = True - -route_supplies = False -check_lvsdrc = True -perimeter_pins = False -#netlist_only = True -#analytical_delay = False -output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports, - num_r_ports, - num_w_ports, - word_size, - num_words, - tech_name) -output_path = "macro/{}".format(output_name) diff --git a/macros/configs/riscv_sky130_4kbyte_1rw1r.py b/macros/configs/riscv_sky130_4kbyte_1rw1r.py deleted file mode 100644 index 2d53df31..00000000 --- a/macros/configs/riscv_sky130_4kbyte_1rw1r.py +++ /dev/null @@ -1,25 +0,0 @@ -word_size = 32 -num_words = 1024 -write_size = 8 - -local_array_size = 16 - -num_rw_ports = 1 -num_r_ports = 1 -num_w_ports = 0 - -tech_name = "sky130" -nominal_corner_only = True - -route_supplies = False -check_lvsdrc = True -perimeter_pins = False -#netlist_only = True -#analytical_delay = False -output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports, - num_r_ports, - num_w_ports, - word_size, - num_words, - tech_name) -output_path = "macro/{}".format(output_name) From 229a3b5b3d0375f075a9aaf7b48fa4bf1206d0e7 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 11 Mar 2022 18:01:45 -0800 Subject: [PATCH 04/44] By default uniquify instances based on macro name. --- compiler/base/hierarchy_design.py | 10 ++++++++++ compiler/base/hierarchy_layout.py | 7 +------ compiler/globals.py | 1 - compiler/openram.py | 5 +++-- compiler/options.py | 4 ---- compiler/sram/sram.py | 20 -------------------- compiler/tests/configs/config.py | 1 + compiler/tests/configs/config_back_end.py | 1 + compiler/tests/configs/config_front_end.py | 2 ++ technology/freepdk45/tech/freepdk45.lylvs | 2 +- 10 files changed, 19 insertions(+), 34 deletions(-) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index fe295273..00edc558 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -8,6 +8,7 @@ import hierarchy_layout import hierarchy_spice import debug +import os from globals import OPTS @@ -22,6 +23,15 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): self.drc_errors = "skipped" self.lvs_errors = "skipped" + # Flag for library cells which is recomputed in hierachy_layout + gds_file = OPTS.openram_tech + "gds_lib/" + cell_name + ".gds" + is_library_cell = os.path.isfile(gds_file) + # Uniquify names to address the flat GDS namespace + # except for the top/output name + if not is_library_cell and name != OPTS.output_name: + name = OPTS.output_name + "_" + name + cell_name = name + hierarchy_spice.spice.__init__(self, name, cell_name) hierarchy_layout.layout.__init__(self, name, cell_name) self.init_graph_params() diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index b3eff27d..bb17f81d 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -42,6 +42,7 @@ class layout(): self.cell_name = cell_name self.gds_file = OPTS.openram_tech + "gds_lib/" + cell_name + ".gds" + self.is_library_cell = os.path.isfile(self.gds_file) self.width = None self.height = None @@ -60,8 +61,6 @@ class layout(): self.pin_map = {} # List of modules we have already visited self.visited = [] - # Flag for library cells - self.is_library_cell = False self.gds_read() @@ -903,10 +902,6 @@ class layout(): """Reads a GDSII file in the library and checks if it exists Otherwise, start a new layout for dynamic generation.""" - # This must be done for netlist only mode too - if os.path.isfile(self.gds_file): - self.is_library_cell = True - if OPTS.netlist_only: self.gds = None return diff --git a/compiler/globals.py b/compiler/globals.py index 15bb4fe9..d0c0d673 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -377,7 +377,6 @@ def read_config(config_file, is_unit_test=True): ports, OPTS.tech_name) - def end_openram(): """ Clean up openram for a proper exit """ cleanup_paths() diff --git a/compiler/openram.py b/compiler/openram.py index 089c0f3a..3a509f3b 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -74,8 +74,9 @@ for path in output_files: from sram import sram -s = sram(sram_config=c, - name=OPTS.output_name) +s = sram(name=OPTS.output_name, + sram_config=c) + # Output the files for the resulting SRAM s.save() diff --git a/compiler/options.py b/compiler/options.py index d81a8c7e..ebf716b0 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -166,10 +166,6 @@ class options(optparse.Values): keep_temp = False - # Add a prefix of the root cell before every structure in the GDS - # after outputting the GDS2 - uniquify = False - # These are the default modules that can be over-riden bank_select = "bank_select" bitcell_array = "bitcell_array" diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 90855d8e..b656f547 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -61,26 +61,6 @@ class sram(): def gds_write(self, name): self.s.gds_write(name) - # This addresses problems with flat GDS namespaces when we - # want to merge this SRAM with other SRAMs. - if OPTS.uniquify: - import gdsMill - gds = gdsMill.VlsiLayout() - reader = gdsMill.Gds2reader(gds) - reader.loadFromFile(name) - - # Uniquify but skip the library cells since they are hard coded - try: - from tech import library_prefix_name - except ImportError: - library_prefix_name = None - gds.uniquify(library_prefix_name) - - writer = gdsMill.Gds2writer(gds) - unique_name = name.replace(".gds", "_unique.gds") - writer.writeToFile(unique_name) - shutil.move(unique_name, name) - def verilog_write(self, name): self.s.verilog_write(name) diff --git a/compiler/tests/configs/config.py b/compiler/tests/configs/config.py index dc3739ab..9e35f558 100644 --- a/compiler/tests/configs/config.py +++ b/compiler/tests/configs/config.py @@ -14,3 +14,4 @@ tech_name = OPTS.tech_name nominal_corner_only = True check_lvsdrc = True +output_name = "sram" diff --git a/compiler/tests/configs/config_back_end.py b/compiler/tests/configs/config_back_end.py index 3294e979..4bf0aa8b 100644 --- a/compiler/tests/configs/config_back_end.py +++ b/compiler/tests/configs/config_back_end.py @@ -15,4 +15,5 @@ nominal_corner_only = True check_lvsdrc = True spice_name = "ngspice" +output_name = "sram" diff --git a/compiler/tests/configs/config_front_end.py b/compiler/tests/configs/config_front_end.py index 4486b077..2b42a914 100644 --- a/compiler/tests/configs/config_front_end.py +++ b/compiler/tests/configs/config_front_end.py @@ -11,3 +11,5 @@ num_words = 16 tech_name = OPTS.tech_name +output_name = "sram" + diff --git a/technology/freepdk45/tech/freepdk45.lylvs b/technology/freepdk45/tech/freepdk45.lylvs index 934a981c..5500d9e3 100644 --- a/technology/freepdk45/tech/freepdk45.lylvs +++ b/technology/freepdk45/tech/freepdk45.lylvs @@ -214,7 +214,7 @@ connect_global(pwell, "PWELL") connect_global(nwell, "NWELL") connect_global(bulk, "BULK") -for pat in %w(pinv* pnor* pnand* and?_dec* write_driver* port_address* replica_bitcell_array*) +for pat in %w(*pinv* *pnor* *pnand* *and?_dec* *write_driver* *port_address* *replica_bitcell_array*) connect_explicit(pat, [ "NWELL", "vdd" ]) connect_explicit(pat, [ "BULK", "PWELL", "gnd" ]) end From 8979612ccaaca2ecd1d666761119c738be14dc46 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 14 Mar 2022 10:14:31 -0700 Subject: [PATCH 05/44] Rework macro Makefile to take technology target --- macros/Makefile | 24 ++++++++++++++----- ...bm.py => example_config_big_scn4m_subm.py} | 0 ....py => example_config_giant_scn4m_subm.py} | 0 ...py => example_config_medium_scn4m_subm.py} | 0 ...e.py => freepdk45_sram_1rw1r_32x2048_8.py} | 0 ...cn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py} | 0 ... scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py} | 0 ... scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py} | 0 ...4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py} | 1 + ...scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py} | 0 ...scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py} | 0 11 files changed, 19 insertions(+), 6 deletions(-) rename macros/configs/{big_config_scn4m_subm.py => example_config_big_scn4m_subm.py} (100%) rename macros/configs/{giant_config_scn4m_subm.py => example_config_giant_scn4m_subm.py} (100%) rename macros/configs/{medium_config_scn4m_subm.py => example_config_medium_scn4m_subm.py} (100%) rename macros/configs/{riscv_freepdk45_8kbyte.py => freepdk45_sram_1rw1r_32x2048_8.py} (100%) rename macros/configs/{riscv_scn4m_subm_16kbyte_1rw1r.py => scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py} (100%) rename macros/configs/{riscv_scn4m_subm_1kbyte_1rw1r.py => scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py} (100%) rename macros/configs/{riscv_scn4m_subm_2kbyte_1rw1r.py => scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py} (100%) rename macros/configs/{riscv_scn4m_subm_32kbyte_1rw1r.py => scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py} (98%) rename macros/configs/{riscv_scn4m_subm_4kbyte_1rw1r.py => scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py} (100%) rename macros/configs/{riscv_scn4m_subm_8kbyte_1rw1r.py => scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py} (100%) diff --git a/macros/Makefile b/macros/Makefile index 31943ad9..ca7bcdc7 100644 --- a/macros/Makefile +++ b/macros/Makefile @@ -33,15 +33,15 @@ configs: .PHONY: configs -BROKEN := \ - sky130_sram_1kbyte_1r1w_8x1024_8 \ - sky130_sram_1kbyte_1rw_32x256_8 \ - sky130_sram_2kbyte_1rw_32x512_8 \ - sky130_sram_4kbyte_1rw_32x1024_8 \ +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)) -all: | configs +all: | configs @echo @echo "Building following working configs" @for S in $(WORKING_STAMPS); do echo " - $$S"; done @@ -49,6 +49,18 @@ all: | configs $(MAKE) $(WORKING_STAMPS) @echo "Built all macros." +example: $(EXAMPLE_STAMPS) +.PHONY: example + +sky130: $(SKY130_STAMPS) +.PHONY: sky130 + +freepdk45: $(FREEPDK45_STAMPS) +.PHONY: freepdk45 + +scn4m_subm: $(SCN4M_SUBM_STAMPS) +.PHONY: scn4m_subm + %.ok: configs/%.py @echo "Building $*" @mkdir -p $* diff --git a/macros/configs/big_config_scn4m_subm.py b/macros/configs/example_config_big_scn4m_subm.py similarity index 100% rename from macros/configs/big_config_scn4m_subm.py rename to macros/configs/example_config_big_scn4m_subm.py diff --git a/macros/configs/giant_config_scn4m_subm.py b/macros/configs/example_config_giant_scn4m_subm.py similarity index 100% rename from macros/configs/giant_config_scn4m_subm.py rename to macros/configs/example_config_giant_scn4m_subm.py diff --git a/macros/configs/medium_config_scn4m_subm.py b/macros/configs/example_config_medium_scn4m_subm.py similarity index 100% rename from macros/configs/medium_config_scn4m_subm.py rename to macros/configs/example_config_medium_scn4m_subm.py diff --git a/macros/configs/riscv_freepdk45_8kbyte.py b/macros/configs/freepdk45_sram_1rw1r_32x2048_8.py similarity index 100% rename from macros/configs/riscv_freepdk45_8kbyte.py rename to macros/configs/freepdk45_sram_1rw1r_32x2048_8.py diff --git a/macros/configs/riscv_scn4m_subm_16kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py similarity index 100% rename from macros/configs/riscv_scn4m_subm_16kbyte_1rw1r.py rename to macros/configs/scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py diff --git a/macros/configs/riscv_scn4m_subm_1kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py similarity index 100% rename from macros/configs/riscv_scn4m_subm_1kbyte_1rw1r.py rename to macros/configs/scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py diff --git a/macros/configs/riscv_scn4m_subm_2kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py similarity index 100% rename from macros/configs/riscv_scn4m_subm_2kbyte_1rw1r.py rename to macros/configs/scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py diff --git a/macros/configs/riscv_scn4m_subm_32kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py similarity index 98% rename from macros/configs/riscv_scn4m_subm_32kbyte_1rw1r.py rename to macros/configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py index 87ddb5eb..285b1dbf 100644 --- a/macros/configs/riscv_scn4m_subm_32kbyte_1rw1r.py +++ b/macros/configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py @@ -1,3 +1,4 @@ +num_banks=2 word_size = 32 num_words = 8192 write_size = 8 diff --git a/macros/configs/riscv_scn4m_subm_4kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py similarity index 100% rename from macros/configs/riscv_scn4m_subm_4kbyte_1rw1r.py rename to macros/configs/scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py diff --git a/macros/configs/riscv_scn4m_subm_8kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py similarity index 100% rename from macros/configs/riscv_scn4m_subm_8kbyte_1rw1r.py rename to macros/configs/scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py From 7e7670581c11fb4f579acc81e9516de5610facdf Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 16 Mar 2022 07:58:29 -0700 Subject: [PATCH 06/44] Add some vertical/horizontal pins for sky130 only --- compiler/base/hierarchy_layout.py | 76 +++++++++++++++-------- compiler/modules/hierarchical_decoder.py | 2 +- compiler/modules/precharge_array.py | 2 +- compiler/modules/replica_bitcell_array.py | 27 +++----- 4 files changed, 59 insertions(+), 48 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index bb17f81d..3ebc4ac3 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -449,12 +449,19 @@ class layout(): bins[x] = [(inst,pin)] for x, v in bins.items(): - # Not enough to route a pin + # Not enough to route a pin, so just copy them if len(v) < 2: + debug.warning("Copying pins instead of connecting with pin.") + for inst,pin in v: + self.add_layout_pin(pin.name, + pin.layer, + pin.ll(), + pin.width(), + pin.height()) continue - bot_y = min([inst.by() for (inst,pin) in v]) - top_y = max([inst.uy() for (inst,pin) in v]) + bot_y = min([pin.by() for (inst,pin) in v]) + top_y = max([pin.uy() for (inst,pin) in v]) last_via = None for inst,pin in v: @@ -477,16 +484,21 @@ class layout(): top_pos = vector(x, top_y) bot_pos = vector(x, bot_y) - rect = self.add_layout_pin_rect_center(text=name, - layer=pin_layer, - offset=top_pos) -# self.add_layout_pin_rect_center(text=name, -# layer=pin_layer, -# offset=bot_pos) - self.add_segment_center(layer=pin_layer, - start=vector(rect.cx(), bot_pos.y), - end=rect.bc(), - width=via_width) +# top_rect = self.add_layout_pin_rect_center(text=name, +# layer=pin_layer, +# offset=top_pos) + #bot_rect = self.add_layout_pin_rect_center(text=name, + # layer=pin_layer, + # offset=bot_pos) +# self.add_segment_center(layer=pin_layer, +# start=vector(top_rect.cx(), bot_pos.y), +# end=top_rect.bc(), +# width=via_width) + self.add_layout_pin_segment_center(text=name, + layer=pin_layer, + start=top_pos, + end=bot_pos, + width=via_width) @@ -514,12 +526,18 @@ class layout(): # Filter the small bins for y, v in bins.items(): - # Not enough to route a pin if len(v) < 2: + debug.warning("Copying pins instead of connecting with pin.") + for inst,pin in v: + self.add_layout_pin(pin.name, + pin.layer, + pin.ll(), + pin.width(), + pin.height()) continue - left_x = min([inst.lx() for (inst,pin) in v]) - right_x = max([inst.rx() for (inst,pin) in v]) + left_x = min([pin.lx() for (inst,pin) in v]) + right_x = max([pin.rx() for (inst,pin) in v]) last_via = None for inst,pin in v: @@ -543,19 +561,25 @@ class layout(): left_pos = vector(left_x, y) right_pos = vector(right_x, y) - rect = self.add_layout_pin_rect_center(text=name, - layer=pin_layer, - offset=left_pos) -# self.add_layout_pin_rect_center(text=name, -# layer=pin_layer, -# offset=right_pos) +# left_rect = self.add_layout_pin_rect_center(text=name, +# layer=pin_layer, +# offset=left_pos) + #right_rect = self.add_layout_pin_rect_center(text=name, + # layer=pin_layer, + # offset=right_pos) # This is made to not overlap with the pin above # so that the power router will only select a small pin. # Otherwise it adds big blockages over the rails. - self.add_segment_center(layer=pin_layer, - start=rect.rc(), - end=vector(right_pos.x, rect.cy()), - width=via_height) +# self.add_segment_center(layer=pin_layer, +# start=left_rect.rc(), +# end=vector(right_pos.x, left_rect.cy()), +# width=via_height) + + self.add_layout_pin_segment_center(text=name, + layer=pin_layer, + start=left_pos, + end=right_pos, + width=via_height) def add_layout_pin_segment_center(self, text, layer, start, end, width=None): diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 0db5acbb..f9987bb8 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -596,7 +596,7 @@ class hierarchical_decoder(design.design): """ # This is an experiment with power rails - if OPTS.experimental_power: + if OPTS.tech_name=="sky130" or OPTS.experimental_power: if layer_props.hierarchical_decoder.vertical_supply: pre_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst self.route_vertical_pins("vdd", insts=pre_insts, yside="by") diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 0215d76a..cfd2015b 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -95,7 +95,7 @@ class precharge_array(design.design): self.copy_layout_pin(inst, "br", "br_{0}".format(i)) def route_supplies(self): - if OPTS.experimental_power: + if OPTS.tech_name=="sky130" or OPTS.experimental_power: self.route_horizontal_pins("vdd") else: for inst in self.local_insts: diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 57a58d79..be6d1ef5 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -458,27 +458,14 @@ class replica_bitcell_array(bitcell_base_array): width=pin.width(), height=self.height) - # vdd/gnd are only connected in the perimeter cells - # replica column should only have a vdd/gnd in the dummy cell on top/bottom - supply_insts = self.dummy_col_insts + self.dummy_row_insts - - - if OPTS.experimental_power: - for pin_name in self.supplies: - #self.route_vertical_pins(name=pin_name, insts=supply_insts) - self.route_horizontal_pins(name=pin_name, insts=supply_insts) - - #self.route_vertical_pins(name=pin_name, insts=self.replica_col_insts) - #self.route_horizontal_pins(name=pin_name, insts=self.replica_col_insts) - for inst in supply_insts: - pin_list = inst.get_pins(pin_name) - for pin in pin_list: - self.copy_power_pin(pin) - - for inst in self.replica_col_insts: - if inst: - self.copy_layout_pin(inst, pin_name) + if OPTS.tech_name=="sky130" or OPTS.experimental_power: + self.route_vertical_pins(name="gnd", insts=self.replica_col_insts) + self.route_horizontal_pins(name="vdd", insts=self.dummy_row_insts) else: + # vdd/gnd are only connected in the perimeter cells + # replica column should only have a vdd/gnd in the dummy cell on top/bottom + supply_insts = self.dummy_col_insts + self.dummy_row_insts + for pin_name in self.supplies: for inst in supply_insts: pin_list = inst.get_pins(pin_name) From 01a73b31e18702357b7ee6c7da7d6f1e0533f576 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 18 Mar 2022 10:32:25 -0700 Subject: [PATCH 07/44] Fix power ring routing boundary bug. --- compiler/base/hierarchy_layout.py | 23 ++++-------- compiler/modules/delay_chain.py | 19 ++++++---- compiler/modules/wordline_driver_array.py | 2 +- compiler/router/grid.py | 12 +++++- compiler/router/router.py | 46 ++++++++++++++++------- compiler/router/signal_escape_router.py | 2 +- compiler/router/signal_grid.py | 5 ++- compiler/router/supply_tree_router.py | 22 ++++++----- compiler/sram/sram_1bank.py | 34 ++++++++--------- compiler/sram/sram_base.py | 4 ++ 10 files changed, 98 insertions(+), 71 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 3ebc4ac3..403920c6 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1322,7 +1322,7 @@ class layout(): self.bbox = [self.bounding_box.ll(), self.bounding_box.ur()] - def get_bbox(self, side="all", big_margin=0, little_margin=0): + def get_bbox(self, side="all", margin=0): """ Get the bounding box from the GDS """ @@ -1349,27 +1349,18 @@ class layout(): ll_offset = vector(0, 0) ur_offset = vector(0, 0) if side in ["ring", "top", "all"]: - ur_offset += vector(0, big_margin) - else: - ur_offset += vector(0, little_margin) + ur_offset += vector(0, margin) if side in ["ring", "bottom", "all"]: - ll_offset += vector(0, big_margin) - else: - ll_offset += vector(0, little_margin) + ll_offset += vector(0, margin) if side in ["ring", "left", "all"]: - ll_offset += vector(big_margin, 0) - else: - ll_offset += vector(little_margin, 0) + ll_offset += vector(margin, 0) if side in ["ring", "right", "all"]: - ur_offset += vector(big_margin, 0) - else: - ur_offset += vector(little_margin, 0) + ur_offset += vector(margin, 0) bbox = (ll - ll_offset, ur + ur_offset) size = ur - ll - debug.info(1, "Size: {0} x {1} with perimeter big margin {2} little margin {3}".format(size.x, + debug.info(1, "Size: {0} x {1} with perimeter margin {2}".format(size.x, size.y, - big_margin, - little_margin)) + margin)) return bbox diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index e044aaa3..a5bcea27 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -177,15 +177,18 @@ class delay_chain(design.design): # The routing to connect the loads is over the first and last cells # We have an even number of drivers and must only do every other # supply rail + if OPTS.experimental_power: + self.route_horizontal_pins("vdd") + self.route_horizontal_pins("gnd") + else: + for inst in self.driver_inst_list: + load_list = self.load_inst_map[inst] + for pin_name in ["vdd", "gnd"]: + pin = load_list[0].get_pin(pin_name) + self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0)) - for inst in self.driver_inst_list: - load_list = self.load_inst_map[inst] - for pin_name in ["vdd", "gnd"]: - pin = load_list[0].get_pin(pin_name) - self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0)) - - pin = load_list[-2].get_pin(pin_name) - self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0)) + pin = load_list[-2].get_pin(pin_name) + self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0)) def add_layout_pins(self): diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index a13f4bcd..b4c2c54f 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -78,7 +78,7 @@ class wordline_driver_array(design.design): """ # Experiment with power straps - if OPTS.experimental_power: + if OPTS.tech_name=="sky130" or OPTS.experimental_power: if layer_props.wordline_driver.vertical_supply: self.route_vertical_pins("vdd", insts=self.wld_inst) self.route_vertical_pins("gnd", insts=self.wld_inst) diff --git a/compiler/router/grid.py b/compiler/router/grid.py index 843fb3ed..a366ebe0 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -68,6 +68,16 @@ class grid: self.add_map(n) return self.map[n].blocked + def is_inside(self, n): + if not isinstance(n, vector3d): + for item in n: + if self.is_inside(item): + return True + else: + return False + else: + return n.x >= self.ll.x and n.x <= self.ur.x and n.y >= self.ll.y and n.y <= self.ur.y + def set_path(self, n, value=True): if isinstance(n, (list, tuple, set, frozenset)): for item in n: @@ -128,7 +138,7 @@ class grid: """ Side specifies which side. Layer specifies horizontal (0) or vertical (1) - Width specifies how wide the perimter "stripe" should be. + Width specifies how wide the perimeter "stripe" should be. Works from the inside out from the bbox (ll, ur) """ if "ring" in side: diff --git a/compiler/router/router.py b/compiler/router/router.py index f82a6128..2502d322 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -92,12 +92,19 @@ class router(router_tech): def get_bbox(self): return self.bbox - def create_routing_grid(self, router_type): + def create_routing_grid(self, router_type=None): """ - Create a sprase routing grid with A* expansion functions. + Create (or recreate) a sprase routing grid with A* expansion functions. """ + debug.check(router_type or hasattr(self, "router_type"), "Must specify a routing grid type.") + self.init_bbox(self.bbox, self.margin) - self.rg = router_type(self.ll, self.ur, self.track_width) + + if router_type: + self.router_type = router_type + self.rg = router_type(self.ll, self.ur, self.track_width) + else: + self.rg = self.router_type(self.ll, self.ur, self.track_width) def clear_pins(self): """ @@ -927,40 +934,34 @@ class router(router_tech): def add_ring_supply_pin(self, name, width=3, space=2): """ - Adds a ring supply pin that goes inside the given bbox. + Adds a ring supply pin that goes outside the given bbox. """ pg = pin_group(name, [], self) - # Offset two spaces inside and one between the rings - # Units are in routing grids - if name == "gnd": - offset = width + 2 * space - else: - offset = space # LEFT left_grids = set(self.rg.get_perimeter_list(side="left_ring", width=width, margin=self.margin, - offset=offset, + offset=space, layers=[1])) # RIGHT right_grids = set(self.rg.get_perimeter_list(side="right_ring", width=width, margin=self.margin, - offset=offset, + offset=space, layers=[1])) # TOP top_grids = set(self.rg.get_perimeter_list(side="top_ring", width=width, margin=self.margin, - offset=offset, + offset=space, layers=[0])) # BOTTOM bottom_grids = set(self.rg.get_perimeter_list(side="bottom_ring", width=width, margin=self.margin, - offset=offset, + offset=space, layers=[0])) horizontal_layer_grids = left_grids | right_grids @@ -972,6 +973,7 @@ class router(router_tech): # Add vias in the overlap points horizontal_corner_grids = vertical_layer_grids & horizontal_layer_grids + corners = [] for g in horizontal_corner_grids: self.add_via(g) @@ -984,6 +986,15 @@ class router(router_tech): self.pin_groups[name].append(pg) self.new_pins[name] = pg.pins + # Update the bbox so that it now includes the new pins + for p in pg.pins: + if p.lx() < self.ll.x or p.by() < self.ll.y: + self.ll = p.ll() + if p.rx() > self.ur.x or p.uy() > self.ur.y: + self.ur = p.ur() + self.bbox = (self.ll, self.ur) + self.create_routing_grid() + def get_new_pins(self, name): return self.new_pins[name] @@ -1274,11 +1285,18 @@ class router(router_tech): """ debug.info(2, "Adding router info") + show_bbox = False show_blockages = False show_blockage_grids = False show_enclosures = False show_all_grids = True + if show_bbox: + self.cell.add_rect(layer="text", + offset=vector(self.ll.x, self.ll.y), + width=self.ur.x - self.ll.x, + height=self.ur.y - self.ll.y) + if show_all_grids: for g in self.rg.map: self.annotate_grid(g) diff --git a/compiler/router/signal_escape_router.py b/compiler/router/signal_escape_router.py index 7cc41d97..d727e900 100644 --- a/compiler/router/signal_escape_router.py +++ b/compiler/router/signal_escape_router.py @@ -63,7 +63,7 @@ class signal_escape_router(router): print_time("Maze routing pins",datetime.now(), start_time, 3) - # self.write_debug_gds("final_escape_router.gds",False) + #self.write_debug_gds("final_escape_router.gds",False) return True diff --git a/compiler/router/signal_grid.py b/compiler/router/signal_grid.py index 340db093..3d8c69eb 100644 --- a/compiler/router/signal_grid.py +++ b/compiler/router/signal_grid.py @@ -119,10 +119,11 @@ class signal_grid(grid): # Expand all directions. neighbors = curpath.expand_dirs() + # Filter the out of region ones # Filter the blocked ones - unblocked_neighbors = [x for x in neighbors if not self.is_blocked(x)] + valid_neighbors = [x for x in neighbors if self.is_inside(x) and not self.is_blocked(x)] - return unblocked_neighbors + return valid_neighbors def hpwl(self, src, dest): """ diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index 88d02f2e..bb21813f 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -43,6 +43,7 @@ class supply_tree_router(router): bbox=bbox, route_track_width=self.route_track_width) + def route(self, vdd_name="vdd", gnd_name="gnd"): """ Route the two nets in a single layer. @@ -75,6 +76,9 @@ class supply_tree_router(router): self.add_ring_supply_pin(self.vdd_name) self.add_ring_supply_pin(self.gnd_name) + #self.write_debug_gds("initial_tree_router.gds",False) + #breakpoint() + # Route the supply pins to the supply rails # Route vdd first since we want it to be shorter start_time = datetime.now() @@ -82,8 +86,6 @@ class supply_tree_router(router): self.route_pins(gnd_name) print_time("Maze routing supplies", datetime.now(), start_time, 3) - # self.write_debug_gds("final_tree_router.gds",False) - # Did we route everything?? if not self.check_all_routed(vdd_name): return False @@ -144,15 +146,15 @@ class supply_tree_router(router): # Route MST components for index, (src, dest) in enumerate(connections): - if not (index % 100): + if not (index % 25): debug.info(1, "{0} supply segments routed, {1} remaining.".format(index, len(connections) - index)) self.route_signal(pin_name, src, dest) - # if pin_name == "gnd": - # print("\nSRC {}: ".format(src) + str(self.pin_groups[pin_name][src].grids) + str(self.pin_groups[pin_name][src].blockages)) - # print("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages)) - # self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False) + if False and pin_name == "gnd": + print("\nSRC {}: ".format(src) + str(self.pin_groups[pin_name][src].grids) + str(self.pin_groups[pin_name][src].blockages)) + print("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages)) + self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False) - #self.write_debug_gds("final.gds", True) + #self.write_debug_gds("final_tree_router_{}.gds".format(pin_name), False) #return def route_signal(self, pin_name, src_idx, dest_idx): @@ -161,7 +163,7 @@ class supply_tree_router(router): # Second pass, clear prior pin blockages so that you can route over other metal # of the same supply. Otherwise, this can create a lot of circular routes due to accidental overlaps. for unblock_routes in [False, True]: - for detour_scale in [5 * pow(2, x) for x in range(5)]: + for detour_scale in [2 * pow(2, x) for x in range(5)]: debug.info(2, "Routing {0} to {1} with scale {2}".format(src_idx, dest_idx, detour_scale)) # Clear everything in the routing grid. @@ -187,6 +189,8 @@ class supply_tree_router(router): # Actually run the A* router if self.run_router(detour_scale=detour_scale): return + if detour_scale > 2: + self.write_debug_gds("route_{0}_{1}_d{2}.gds".format(src_idx, dest_idx, detour_scale), False) self.write_debug_gds("debug_route.gds", True) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index ce10eb4f..110b8330 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -336,31 +336,27 @@ class sram_1bank(sram_base): # Some technologies have an isolation self.add_dnwell(inflate=2.5) + # Route the supplies together and/or to the ring/stripes. + # This is done with the original bbox since the escape routes need to + # be outside of the ring for OpenLane + rt = router_tech(self.supply_stack, 1) + init_bbox = self.get_bbox(side="ring", + margin=rt.track_width) + # We need the initial bbox for the supply rings later # because the perimeter pins will change the bbox # Route the pins to the perimeter - pre_bbox = None if OPTS.perimeter_pins: - rt = router_tech(self.supply_stack, 1) - - if OPTS.supply_pin_type in ["ring", "left", "right", "top", "bottom"]: - big_margin = 12 * rt.track_width - little_margin = 2 * rt.track_width - else: - big_margin = 6 * rt.track_width - little_margin = 0 - - pre_bbox = self.get_bbox(side="ring", - big_margin=rt.track_width) - - bbox = self.get_bbox(side=OPTS.supply_pin_type, - big_margin=big_margin, - little_margin=little_margin) + # We now route the escape routes far enough out so that they will + # reach past the power ring or stripes on the sides + # The power rings are 4 tracks wide with 2 tracks spacing, so space it + # 11 tracks out + bbox = self.get_bbox(side="ring", + margin=11*rt.track_width) self.route_escape_pins(bbox) - # Route the supplies first since the MST is not blockage aware - # and signals can route to anywhere on sides (it is flexible) - self.route_supplies(pre_bbox) + self.route_supplies(init_bbox) + def route_dffs(self, add_routes=True): diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index ec690610..f6782597 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -243,6 +243,7 @@ class sram_base(design, verilog, lef): for inst in self.insts: self.copy_power_pins(inst, pin_name, self.ext_supply[pin_name]) + # Pick the router type if not OPTS.route_supplies: # Do not route the power supply (leave as must-connect pins) return @@ -250,6 +251,7 @@ class sram_base(design, verilog, lef): from supply_grid_router import supply_grid_router as router else: from supply_tree_router import supply_tree_router as router + rtr=router(layers=self.supply_stack, design=self, bbox=bbox, @@ -257,6 +259,8 @@ class sram_base(design, verilog, lef): rtr.route() + # This removes the original pre-supply routing pins and replaces them + # with the ring or peripheral power pins 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"]: From 2bfc94fcdd6f78a97348add91369829b51b40ca3 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 18 Mar 2022 14:44:13 -0700 Subject: [PATCH 08/44] Add unblocking of source and destination pins to router. --- compiler/router/grid_cell.py | 3 +-- compiler/router/router.py | 13 ++++++++++--- compiler/router/signal_router.py | 2 +- compiler/router/supply_tree_router.py | 6 +++--- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/compiler/router/grid_cell.py b/compiler/router/grid_cell.py index 42be2761..f201a094 100644 --- a/compiler/router/grid_cell.py +++ b/compiler/router/grid_cell.py @@ -20,8 +20,7 @@ class grid_cell: def reset(self): """ - Reset the dynamic info about routing. The pins/blockages are not reset so - that they can be reused. + Reset the dynamic info about routing. """ self.min_cost=-1 self.min_path=None diff --git a/compiler/router/router.py b/compiler/router/router.py index 2502d322..39a9ea0c 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -375,9 +375,10 @@ class router(router_tech): # This is just a virtual function pass - def prepare_blockages(self): + def prepare_blockages(self, src=None, dest=None): """ Reset and add all of the blockages in the design. + Skip adding blockages from src and dest component if specified as a tuple of name,component. """ debug.info(3, "Preparing blockages.") @@ -400,8 +401,14 @@ class router(router_tech): # Now go and block all of the blockages due to pin shapes. # Some of these will get unblocked later if they are the source/target. for name in self.pin_groups: - # This should be a superset of the grids... - blockage_grids = {y for x in self.pin_groups[name] for y in x.blockages} + blockage_grids = [] + for component_idx, component in enumerate(self.pin_groups[name]): + # Skip adding source or dest blockages + if src and src[0] == name and src[1] == component_idx: + continue + if dest and dest[0] == name and dest[1] == component_idx: + continue + blockage_grids.extend(component.blockages) self.set_blockages(blockage_grids, True) # If we have paths that were recently routed, add them as blockages as well. diff --git a/compiler/router/signal_router.py b/compiler/router/signal_router.py index 9fbf871f..11e40a91 100644 --- a/compiler/router/signal_router.py +++ b/compiler/router/signal_router.py @@ -59,7 +59,7 @@ class signal_router(router): self.write_debug_gds(stop_program=False) return False - self.write_debug_gds(stop_program=False) + #self.write_debug_gds(stop_program=False) return True diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index bb21813f..9450243d 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -171,7 +171,7 @@ class supply_tree_router(router): # This is inefficient since it is non-incremental, but it was # easier to debug. - self.prepare_blockages() + self.prepare_blockages(src=(pin_name, src_idx), dest=(pin_name, dest_idx)) if unblock_routes: msg = "Unblocking supply self blockages to improve access (may cause DRC errors):\n{0}\n{1})" debug.warning(msg.format(pin_name, @@ -189,8 +189,8 @@ class supply_tree_router(router): # Actually run the A* router if self.run_router(detour_scale=detour_scale): return - if detour_scale > 2: - self.write_debug_gds("route_{0}_{1}_d{2}.gds".format(src_idx, dest_idx, detour_scale), False) + #if detour_scale > 2: + # self.write_debug_gds("route_{0}_{1}_d{2}.gds".format(src_idx, dest_idx, detour_scale), False) self.write_debug_gds("debug_route.gds", True) From a8f50f212e2c760e81d1e54aaad0eff8048d6f32 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 18 Mar 2022 16:01:57 -0700 Subject: [PATCH 09/44] Change track spacing for freepdk45 --- compiler/router/router.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/router/router.py b/compiler/router/router.py index 39a9ea0c..fd00d98d 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -939,7 +939,7 @@ class router(router_tech): self.new_pins[name] = pg.pins - def add_ring_supply_pin(self, name, width=3, space=2): + def add_ring_supply_pin(self, name, width=3, space=3): """ Adds a ring supply pin that goes outside the given bbox. """ From e31bec131c3f510f7b440b9f4a5b2fa6b377699d Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 22 Mar 2022 11:59:04 -0700 Subject: [PATCH 10/44] Remove 1rw1r combined test and add separate tests. --- ...hierarchical_decoder_132row_1rw_1r_test.py | 42 ++++++++++++ ..._hierarchical_decoder_16row_1rw_1r_test.py | 42 ++++++++++++ ..._hierarchical_decoder_17row_1rw_1r_test.py | 42 ++++++++++++ .../06_hierarchical_decoder_1rw_1r_test.py | 68 ------------------- ..._hierarchical_decoder_32row_1rw_1r_test.py | 42 ++++++++++++ ...ierarchical_decoder_4096row_1rw_1r_test.py | 42 ++++++++++++ ...hierarchical_decoder_512row_1rw_1r_test.py | 42 ++++++++++++ ..._hierarchical_decoder_64row_1rw_1r_test.py | 42 ++++++++++++ 8 files changed, 294 insertions(+), 68 deletions(-) create mode 100755 compiler/tests/06_hierarchical_decoder_132row_1rw_1r_test.py create mode 100755 compiler/tests/06_hierarchical_decoder_16row_1rw_1r_test.py create mode 100755 compiler/tests/06_hierarchical_decoder_17row_1rw_1r_test.py delete mode 100755 compiler/tests/06_hierarchical_decoder_1rw_1r_test.py create mode 100755 compiler/tests/06_hierarchical_decoder_32row_1rw_1r_test.py create mode 100755 compiler/tests/06_hierarchical_decoder_4096row_1rw_1r_test.py create mode 100755 compiler/tests/06_hierarchical_decoder_512row_1rw_1r_test.py create mode 100755 compiler/tests/06_hierarchical_decoder_64row_1rw_1r_test.py diff --git a/compiler/tests/06_hierarchical_decoder_132row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_132row_1rw_1r_test.py new file mode 100755 index 00000000..3c4ee7ee --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_132row_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# 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 unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two + debug.info(1, "Testing 132 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=132) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_16row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_16row_1rw_1r_test.py new file mode 100755 index 00000000..ea50b36c --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_16row_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# 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 unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 and 2-input NAND decoder + debug.info(1, "Testing 16 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=16) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_17row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_17row_1rw_1r_test.py new file mode 100755 index 00000000..549ce54f --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_17row_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# 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 unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 and 2-input NAND decoder with non-power-of-two + debug.info(1, "Testing 17 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=17) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py deleted file mode 100755 index 867cdaff..00000000 --- a/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python3 -# See LICENSE for licensing information. -# -# 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 unittest -from testutils import * -import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) -import globals -from globals import OPTS -from sram_factory import factory -import debug - - -class hierarchical_decoder_1rw_1r_test(openram_test): - - def runTest(self): - config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) - globals.init_openram(config_file) - - # Use the 2 port cell since it is usually bigger/easier - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 0 - globals.setup_bitcell() - - # Checks 2x4 and 2-input NAND decoder - debug.info(1, "Testing 16 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=16) - self.local_check(a) - - # Checks 2x4 and 2-input NAND decoder with non-power-of-two - debug.info(1, "Testing 17 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=17) - self.local_check(a) - - # Checks 2x4 with 3x8 and 2-input NAND decoder - debug.info(1, "Testing 32 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=32) - self.local_check(a) - - # Checks 3 x 2x4 and 3-input NAND decoder - debug.info(1, "Testing 64 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=64) - self.local_check(a) - - # Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two - debug.info(1, "Testing 132 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=132) - self.local_check(a) - - # Checks 3 x 3x8 and 3-input NAND decoder - debug.info(1, "Testing 512 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=512) - self.local_check(a) - - globals.end_openram() - -# run the test from the command line -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_32row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_32row_1rw_1r_test.py new file mode 100755 index 00000000..6c2f6bd6 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_32row_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# 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 unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 with 3x8 and 2-input NAND decoder + debug.info(1, "Testing 32 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=32) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_4096row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_4096row_1rw_1r_test.py new file mode 100755 index 00000000..201fc399 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_4096row_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# 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 unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 and 2-input NAND decoder + debug.info(1, "Testing 4096 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=4096) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_512row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_512row_1rw_1r_test.py new file mode 100755 index 00000000..f53f4838 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_512row_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# 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 unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 3 x 3x8 and 3-input NAND decoder + debug.info(1, "Testing 512 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=512) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_64row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_64row_1rw_1r_test.py new file mode 100755 index 00000000..af9fb4f4 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_64row_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# 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 unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 3 x 2x4 and 3-input NAND decoder + debug.info(1, "Testing 64 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=64) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From 9f7426052de24cef34ace79ddf8887f44cd8a4b4 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 23 Mar 2022 14:46:41 -0700 Subject: [PATCH 11/44] Split port_address tests --- .../18_port_address_16rows_1rw_1r_test.py | 40 +++++++++++++++++++ compiler/tests/18_port_address_16rows_test.py | 34 ++++++++++++++++ ...=> 18_port_address_256rows_1rw_1r_test.py} | 4 -- ...est.py => 18_port_address_512rows_test.py} | 4 -- 4 files changed, 74 insertions(+), 8 deletions(-) create mode 100755 compiler/tests/18_port_address_16rows_1rw_1r_test.py create mode 100755 compiler/tests/18_port_address_16rows_test.py rename compiler/tests/{18_port_address_1rw_1r_test.py => 18_port_address_256rows_1rw_1r_test.py} (88%) rename compiler/tests/{18_port_address_test.py => 18_port_address_512rows_test.py} (86%) diff --git a/compiler/tests/18_port_address_16rows_1rw_1r_test.py b/compiler/tests/18_port_address_16rows_1rw_1r_test.py new file mode 100755 index 00000000..ff39eeed --- /dev/null +++ b/compiler/tests/18_port_address_16rows_1rw_1r_test.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_address_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # Use the 2 port cell since it is usually bigger/easier + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(1, "Port address 16 rows") + a = factory.create("port_address", cols=16, rows=16, port=0) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_address_16rows_test.py b/compiler/tests/18_port_address_16rows_test.py new file mode 100755 index 00000000..a60508de --- /dev/null +++ b/compiler/tests/18_port_address_16rows_test.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_address_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + debug.info(1, "Port address 16 rows") + a = factory.create("port_address", cols=16, rows=16, port=0) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_address_1rw_1r_test.py b/compiler/tests/18_port_address_256rows_1rw_1r_test.py similarity index 88% rename from compiler/tests/18_port_address_1rw_1r_test.py rename to compiler/tests/18_port_address_256rows_1rw_1r_test.py index f81196e8..e9c11bd7 100755 --- a/compiler/tests/18_port_address_1rw_1r_test.py +++ b/compiler/tests/18_port_address_256rows_1rw_1r_test.py @@ -26,10 +26,6 @@ class port_address_1rw_1r_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() - debug.info(1, "Port address 16 rows") - a = factory.create("port_address", cols=16, rows=16, port=0) - self.local_check(a) - debug.info(1, "Port address 256 rows") a = factory.create("port_address", cols=256, rows=256, port=1) self.local_check(a) diff --git a/compiler/tests/18_port_address_test.py b/compiler/tests/18_port_address_512rows_test.py similarity index 86% rename from compiler/tests/18_port_address_test.py rename to compiler/tests/18_port_address_512rows_test.py index 7ecf3288..120ec9be 100755 --- a/compiler/tests/18_port_address_test.py +++ b/compiler/tests/18_port_address_512rows_test.py @@ -20,10 +20,6 @@ class port_address_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - debug.info(1, "Port address 16 rows") - a = factory.create("port_address", cols=16, rows=16, port=0) - self.local_check(a) - debug.info(1, "Port address 512 rows") a = factory.create("port_address", cols=256, rows=512, port=0) self.local_check(a) From 23b5655cabadc15fcfbcb730209f80a0434dba63 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 23 Mar 2022 15:59:29 -0700 Subject: [PATCH 12/44] Split replica_bitcell_array test --- ...lica_bitcell_array_bothrbl_1rw_1r_test.py} | 26 ----------- ...plica_bitcell_array_leftrbl_1rw_1r_test.py | 43 +++++++++++++++++++ ...replica_bitcell_array_norbl_1rw_1r_test.py | 42 ++++++++++++++++++ 3 files changed, 85 insertions(+), 26 deletions(-) rename compiler/tests/{14_replica_bitcell_array_1rw_1r_test.py => 14_replica_bitcell_array_bothrbl_1rw_1r_test.py} (55%) create mode 100755 compiler/tests/14_replica_bitcell_array_leftrbl_1rw_1r_test.py create mode 100755 compiler/tests/14_replica_bitcell_array_norbl_1rw_1r_test.py diff --git a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py b/compiler/tests/14_replica_bitcell_array_bothrbl_1rw_1r_test.py similarity index 55% rename from compiler/tests/14_replica_bitcell_array_1rw_1r_test.py rename to compiler/tests/14_replica_bitcell_array_bothrbl_1rw_1r_test.py index 0523b471..8dad9f9b 100755 --- a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py +++ b/compiler/tests/14_replica_bitcell_array_bothrbl_1rw_1r_test.py @@ -25,21 +25,6 @@ class replica_bitcell_array_1rw_1r_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() - debug.info(2, "Testing 4x4 non-replica array for dp cell") - a = factory.create(module_type="replica_bitcell_array", - cols=4, - rows=4, - rbl=[1, 1]) - self.local_check(a) - - debug.info(2, "Testing 4x4 left replica array for dp cell") - a = factory.create(module_type="replica_bitcell_array", - cols=4, - rows=4, - rbl=[1, 1], - left_rbl=[0]) - self.local_check(a) - debug.info(2, "Testing 4x4 array left and right replica for dp cell") a = factory.create(module_type="replica_bitcell_array", cols=4, @@ -49,17 +34,6 @@ class replica_bitcell_array_1rw_1r_test(openram_test): right_rbl=[1]) self.local_check(a) - - # Sky 130 has restrictions on the symmetries - if OPTS.tech_name != "sky130": - debug.info(2, "Testing 4x4 array right only replica for dp cell") - a = factory.create(module_type="replica_bitcell_array", - cols=4, - rows=4, - rbl=[1, 1], - right_rbl=[1]) - self.local_check(a) - globals.end_openram() # run the test from the command line diff --git a/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_1r_test.py b/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_1r_test.py new file mode 100755 index 00000000..3137802b --- /dev/null +++ b/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_1r_test.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class replica_bitcell_array_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Testing 4x4 left replica array for dp cell") + a = factory.create(module_type="replica_bitcell_array", + cols=4, + rows=4, + rbl=[1, 1], + left_rbl=[0]) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/14_replica_bitcell_array_norbl_1rw_1r_test.py b/compiler/tests/14_replica_bitcell_array_norbl_1rw_1r_test.py new file mode 100755 index 00000000..7f7ee74c --- /dev/null +++ b/compiler/tests/14_replica_bitcell_array_norbl_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class replica_bitcell_array_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Testing 4x4 non-replica array for dp cell") + a = factory.create(module_type="replica_bitcell_array", + cols=4, + rows=4, + rbl=[1, 1]) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From 83e5848728fa95fc231aadb80d5f3888154773d7 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 30 Mar 2022 13:48:53 -0700 Subject: [PATCH 13/44] Change FreePDK and SCMOS 2rw cell to share gnd power rail. --- technology/freepdk45/gds_lib/cell_2rw.gds | Bin 15930 -> 15226 bytes .../freepdk45/gds_lib/dummy_cell_2rw.gds | Bin 15672 -> 14976 bytes .../freepdk45/gds_lib/replica_cell_2rw.gds | Bin 16002 -> 15234 bytes technology/scn4m_subm/gds_lib/cell_2rw.gds | Bin 6326 -> 6262 bytes .../scn4m_subm/gds_lib/dummy_cell_2rw.gds | Bin 6082 -> 6012 bytes .../scn4m_subm/gds_lib/replica_cell_2rw.gds | Bin 6278 -> 6270 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/technology/freepdk45/gds_lib/cell_2rw.gds b/technology/freepdk45/gds_lib/cell_2rw.gds index 3ceea6170128341e277a067442310aada3fe3664..ca7468cf7a094b00bd96065d213ecce73753e642 100644 GIT binary patch literal 15226 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 literal 15930 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 YYkm!C-`S`?o^=Ddw+wII=9k%j0f1|D4FCWD diff --git a/technology/freepdk45/gds_lib/replica_cell_2rw.gds b/technology/freepdk45/gds_lib/replica_cell_2rw.gds index 1b2564a4edde04c4198b1d51cfc6371b9bee9972..24b5543ad3d5a4a7c42ca3c550ed281fd1b5ae16 100644 GIT binary patch literal 15234 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 J8}y@c{{|qX3zq-@ literal 16002 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 diff --git a/technology/scn4m_subm/gds_lib/cell_2rw.gds b/technology/scn4m_subm/gds_lib/cell_2rw.gds index 0a0e0f8dd79ae21d52569758fc389a38f91532f5..cc0c08738cc802cdb9dd225f758eacb0e136f6aa 100644 GIT binary patch literal 6262 zcmbW5J!~9R6os$<)=t=jH8I8plNb_X$1(BRNl0iQNEDIMq@b{9h#&|T7P4I6lv|nt z$z>!Ocer%vk_x#@nUX3>=`s?C<$m+#FmKnsBjJ_oZ*|^1d++<1_h!Z#HaNIuGXsO8 zFKx&!ThrP$o`1L2VB@>*H#WXm+1mC$vx%XR&%XHi?!!NRJO1>`!{xnSzy8W*Mq+=n zk^HM$+crFA*0?Zei`KX}Xl6^TA^-26+2@IOpPDVU%|z+W;c@%Vf4$e=?f&&L`H9D$ zD7|3r|2X%Lzcy=cn!!&F%%<*DlCW80l=q*!bDfm`#_uV=DBT(Q$6t55fB%`rZ)i4+U!wGax&KC< z|I&`xs+qw%?w{PRDBT(QFFY{2zHSDYd-^U)cZPoQ5(n{-UzA?wpFT={h;QaqMf_!Z zn4h}mTd98H@r%-(p?`EN)eOmXb)h1CDceIo`RO|(p8TS8XXxkm&o87r7hk0OqI75I zzqHfs{CVtU`rPcDO|RcM?d$N| zPn2FTum5I#{fxKGuH5(i+sVHF zmj8*;ouQw#a_um!8GNikQMxnqbLM^U%#tc$#vr|#JGxW1Y$PJ0d zFG_cYe#VV>jU%2b_tS4mcZPoU8`d0T?8z@mcZPoI=A4Jb;}@m(o&P81pE&h{+|&)F z*ZG-$;vn?5LdXKjA^ zsoxH^iywZJ=M)UID5V*-5L7HMI6LOK2ds~e>ER}`m6hd+iPhaMCpC^ z4=4QdFaFp5-{be^uj1N)TsuRrDsoMg?Ro!u{2x6?eu$r1MEqrY=%@cWXLat1()-Te zp8RJx|9kvrIKO-RXE^^>^7;26E1o~#o!NBnF0LZ_$C;1c3IEbgid)`Keo?wJ^ylXn z@$BOw`&ii?`k5;|C+N9Hl-~FJ&{IG6_{2dy_lVN#{5*3IciQuh^g2KFbKa{T?49_a zbZ2e<*>m(eJip_E(w((_YR6AJc}3~Y(9eAvdwp>q=XJB^Q9qRK4E^j?%rhijJ}BK8 z`U{WyA?t=SL&RUUhkoj&pOARvh0^Q#pFgt^&wegW?wghEVSdJ)IEaruTa@k${pY;D z%o*gY;=3r_8T#|*S8AYsk?&=D=;yj24pKKYi_)E;U$KxsjXTVKEA(^T5eM-vc>hJ| z&d|@iGft3k>%adg+e1Hj=_h2|c>f?uuj{}0hck~4W6j51}scX>a1D?c9xc2hs- z6X)XafhfJ|-)a6)?;r1X7)$)DiA3r3{``Ex$C?(|XUq0{etPQ9-!IW;{D~=l*&h05 z%lcX0D!HV?|k;easRKl uzV*=)$JtiKZPmpwwu)0;9|G3L=lS|DJ|o3{)>Co)iqf4$|3O2mlwXwY4E@xN+>pK)yC~h6``^p+GuF+eH0G^4X7dMTqI75I zKYP~~lJ^0xe`+>-R8zV$^pl@Bh@bqT^pbz^S@J`Cqm!EWtM)KI<7TaD9{BM=>CVtU z-A_3no=aD2lDldT{p4lNka+qKr8`4E{o{qK0rHE|ouQw)*u`J zeDlN~OwL=6%`W&F7Nt8w|MaohYfpR(&XKRfnFlqcJ465MF!>?=bNe;%SM8yn7@hd@ zQ@S(sFC8a8#QXMxn)s{s&`;eKE!B^B>L*HfhJNbC93gY3exh_|=qLZ|?}6GZ=%+sTR7b|kIH2@Wey$ZY*EJ(A z*APl~hWQyE`z<71J}BK8`WZKIng@L_ZYaIv=eiRIxn5j9QF_V$#$n0_sat+Bj;cKz zKkZy6^@B&JbZ61eT=7$Po+F}kXVK3*@e{AwLFujeujcn3xi0UeoL7daj-qsDp1Areqwau%TMVgKjYQj&pkuUMCs0O{Jbv{2l0_p zl-}}h8CU2`HA8;l$ty~ChJNmG_DsmQ@rcr$p`ULxt}`Sa zzbM@q`iarWy1^?-FZmfaIU(`nBQghWYu%B{w8D;}@knLqGS8_E5$>e~`uxr8`4EbJU4HKc$!E&wfiB#LI6+ zqV$rV>rS1Z-n&HUrTUSVIzj5j?+>E%QvJ*iYR^HvxOYV9&ai&?SbH3khsiHWcaHso z^!ukj|K|DqbHMMP!@vC9?OW?F_P+VD!pQ3bU=)b&2RQg81$NeQrcaHtl zy7Btg>&ve(zt8%5__Doj8gYC-C{F9)Mg8%9U&ZZ|;`oM9+*)&fALZ+dIq2|pJHG!! M>CW-|rk#`a51+?*QUCw| diff --git a/technology/scn4m_subm/gds_lib/dummy_cell_2rw.gds b/technology/scn4m_subm/gds_lib/dummy_cell_2rw.gds index 00bba7f7276252e9e35d1d689b1b40c36b8389bf..f8fd67606fa2e1b71f0ed72c5f981459974f2711 100644 GIT binary patch literal 6012 zcmb7|J#1V>6os$<)=qH3Ix!{&lNb`qPGaJpBqTHtBnl*&6ciRsA~3>*gRDr9(77}P zlFLXm>2R6SB^7d+G9^`%(q$wN%l+Pchn-#LKEYb+Z*}ILy)$>_&6|DJvcbVMn;94! zeQrZ`)=v1})$+5o2U}l%v$=U^<^BVIn@tXleDujrH$MOU*TWA#-CuhA%a>o+%t-D( z)oQJOfA8LpcdBn$z5l?5$IMzM2JMoyP7a#cVtdH{`)l@T@Ww;4OUq`Wbm#Dd{p-Kj z>+f{_d>(${@du?>EdB47{)v}n%UfpfgC}NZZgwf%nfgb(&u^SDgKw{yUD)qZx-<2^ z=JD+fGf4c|hh0i{rhf8{J&OFqlV6nXEd7h+_{lrB9r-W+7WqZ#&eT8gveWtNkEq|! zY}79(y<+LVT;@N$W43B$@Vfh__PUhrO#LT5GrPE92AO-tE=qT%e)19r@sVGY-sGP? z2tUL(^P)@qb$gnhe$Tg~|HR`Lr8`sq=ved_lI#3JmyD%uPyOU)?2vf!i_)E`pYK1u z5P42Mi~OQ=XX-z_)9L)Z6@3`kjod-$6-)nexqc>Z`}#TX?a6s<&95C_i=uRA>K}b- z_U4xN-#P8;@Yvlhr8`sq$#(c5{?{LOiN9`7{cr7tAL5^x?Gk_8p8DDAh=cgwyW6F7 zXX>YJ)C>~OXHmK{^;5Uwhf%-r?Wmt9-I@By%N_(-gX9#YJ5xX7CJ!Vp;}@kjHEo5cz}BE0+DgQeHn3 z%Vy_p`Tp%>-#_JVQMxnrvsNzb$C|;%8Wg2FQ$P2-cOQAboy6l8r8`qU>yMg2`a8be zrF3WNXN`~>5|3Y$?o9pEjd;}&&$(Oiozk7DpZ$h42dO>zMd{AePrtd(L*ns^();fJ zN9Lb6jf33u8%l5TGylXv=8O3gr8oKCc@p^`b>sRK=||n3j-PgFuJ6H%4@z&EKYY{+ zGG_J>QM$7+KjYN*kQX16-Z%cG^7>=HV9fCRVboES?kw~7_{qUN8*+B!o-0asrv7Ta z-4F4xkBRu}_S8Q+7WpAQ=3c~Kx2Jyk+un$Na|UP67o|H>Ke>p5_{b+p@AI#g^=G^~ zCtO>Lc@U-doga?)=b!zr{r`wx_c`77MCpC^zdpbAd9(kx|F4wecUN`(hSz7~>?*Dz z`pa3?|A>EaC*qd&!Y@j9rv7sOCZ2svCVRSbIs{{`2C6xN_RH;={tVn$ty~Crhd*X>~+;S z#{11RPyeBGXX@u#Wu77N@>YCen7ovFY4{YD??zsTphJ@s?l5C`cueHNuV zQ@>&%Z`Bx>7kvDpbZ6>k-l-F$ZvFTFx;^!imvKVs#_w-YdeivLUqdhW zAGK!hq2^G!GtF-nzmxH^*6;!T-*ds`iMzbTH|Do%O51gwvb%k^QrMymg;r|S9> Nr8}$fje7rw?H`G^uDbvL literal 6082 zcmaKwJ!~CC6os$(4rS&)k{2vpe%PIx#$aKNg0D$N!Fz z7>cRr#CZLG%no-pe|Y@(rw3nuv$geLb$dr=#D$TuyC45@`?EiOJHGSD;quPaCFn zG4<0vUPwIoP3vOqzg~}@m}`5bjhla!{HAp=^;0);L)xP6rggFQzhCF4uj`AY&l~q5 zX7?jZ>tgCZ`>7_9+JHAckLW(?)4G`Y$xj@_Pkz&Sn}7a!@k4wIFZ#saTu<}UZ|17U zfgc~VE~ftRiIM~2xpb{hayQpgKY1B5B%U@*>tgDseY}u4Kz`G@nEI(ZUU)`ca+=n~ z+P_lIp9`w_`U}0D#M@6IE@}>&*2UC6eiZTgQ}sby&^(-c*r#>}C;s}hE~fs)qvD5n-+kC8{^ok>r*896>PI~FGp&oMpSm$d$k?f$XSui9f#hZUruDY`#4KsvLY_OkrgbsRPydO7_^6+0z0FUpiG%pKho<#5 zKY6J=oYdIKZ(0}Y{@<*hpP7|XGst`Y{O^5I%jSCOf9?B-tGa&>KXb;kE~fsAb7jqf z#N#(FA8I`1dg^D4Z{I65pubbQWjxTjnEKBgmo*C#Zy#hWYp$n$>Vwa9q`&k7T5rqG zy`tv6XXND`LhE9hpZ>AlLgMX%*2UCMzlrlWXoG%3>urASJ8_Wv#r-p_xB1^ZDET0D zt55pTTu=MYb?%ef!Q<1q*y?Aj_^CVZ5!1TZ>SvtziFfUw^?~s(*UulhuI!eaSG%Q- zrggE-f5JcUOnsh+fbZyJ?KG{6sh>U*2l4S9F|D`xiSdcAKCQR;>95y*o*8OpS{Kv) zvtK3-;v=VNeZap~*ProvpWuCiX?uvpi z<9_i&eC*#${LS^cey8&5_h2voh`i)Ct&6Fj_KAb|$ZuL7@UPbO4;|}^{4M)__Pp%> zp>?s&f6BkS-?PWs^WrnDi>bd}KZxf&YVr&>*Hb_D&HMDh-yhb+)XzO44*GY8X}!(Q zUY$5N`1`|po1gx(-g+G8_DXxux|sGKAMbm&Gq+KE(7M>_XU@1C^5TQm#njKfi8Z6Q zk5V1*Fm7mFO#S>;;XXs+?St0E)K830<_&F_*4zB_n_Q51=Y`hAH2;u%+Az`RO#M7JUPI{j?0)G#v@WK8 z#^@7&eOhlDKkE%~5HH_MOzUla?mKmY-glVR+v-PN>IA79zrRiEZS@Ns)SiQS@$8t^ z#k79-n0p+f2gPq%7kmDx^8HWm?^(Y8P4WG&`;Yc6cVl9{^X+$=YJ%^4FNjdjr-t$xF0v$+GC9zSuE|XHtnele4o5kuTRw&dg~HnV|{`g&GouIC;U|ndp>H5 zA96I;Q-8fa_`8_Ny=tzf{;A#4N9gC$r0>o3)X%&m&hyjzWN2MX{me@aNWYn%rgbs( zU)d$9?2+;De3{n8p1+wls(*ie=^pcaQuE=f!Fh8cj{URaJRe@x9{bxSZo4gxJ-p*q aPLA)RdVVnmADXwl=g+h*_Qp3Zj>dm))MWAi diff --git a/technology/scn4m_subm/gds_lib/replica_cell_2rw.gds b/technology/scn4m_subm/gds_lib/replica_cell_2rw.gds index c91147fdfd1589ddd5f8706e1fced468c73e866c..df0f9e2802dea06430f41223efae3fff9867b4ef 100644 GIT binary patch literal 6270 zcmbW5KX4UQ6vhwv^Aa%Ni$nyB5CtUs5kdgLMsdc185;_Vj14F-#+i(hNnt2c#=@DD z%-GPGl9JNOp`@g=Vkjw@(eZhHd-ikhzIb+o$AsUT@1F0Sd(XXl@9tX9Mn;xwcGt+n zGaI!dw%7k1F27rUq<8!7%F4|PYwP}JHZwZ@>E}OP`SSN)Uwrb#qw^1b`T8rH9nbwU zz24g0)s^pWe{-|?oyE0v8=Exi?H#ex*4sB?W@r1O{=a|bo&>MlGdsOtCQ5gXP20cz zQ*HlX@aMDe6OTVAy<+MAsPs?2G+S6TgC9RMJ9xFBbZ6=x_ddUU&fL+Q@c z|Axo+m(3vYNA5M0?o9pUoxC6Ui6_4(-C6q2mg6VyiH*pA=C{Z%N_VFI>6e4SUw=gX zM(3h_LFpAs|CuuX{!Oz*GlQ4ie_*?zbZ6?{d)@5RvKeIV8M`Rmnfl2~9K=U{QF@pE z(Btq!e6!CR;;-A&{Pg=|Kl)EReo?wJ^-oMjpCP%9%{OE$b$jY3KVyf)lV6nXO#S@+ zp83eL?`h;0r8`sq{>{PQ@73tTuC2%&lwPs)FO=(N=7z7I$G$;1FD?1C<7-ir?o9m? zPt4v~_5M2#`8wQvtD$sf>fhH7Kg9p$gNFF)_SAoPEBp}u>|8_qb$jY(uOklP|KL_b z>CV(o-KZHPp6{Y`XX>YJdtOBSrZ%E}qI75KCog*tWDSy2l|Jxyi^6@iF%z{<=N&)8GDb^qYHd_Iy#gGxd{;IEasYqVysEVp)I2 ztNVmYOEC|k^r8EQ9sZL~Uupku`TaSpx^^Jf&gk=oTvK&>IsTUa!|UOP`00y?zivh{#nTRDyu6g|M|M=-Y z=e@?k-iZ%NcXp1SJx9O8^E*B$-P!5qxd}h<pS_BC zhQ!MUr8`r9<#9h`-Ed}z`0Mu6Prn%_Bwl%;^se!j&uqlApNj{!&Fc0vKXoS#;$zPi zr8`sqZXYjm205$vE=qT%{_^>iKG1)W?{$0X=ei*d(r@}KN_VDy#X|m6cUb&Z>gT*8 z4&tBp@r%-(sh@eLPLR3{-~ZI@sh_-z6H+(cKZw%1#&7=N%;Q6?**iq(&NRPSyeGiV zTEicdzT^EtYyV?>!5ul!T-{?qdx;Qi+m??0y}{rT)? zn>^I}?uR?|IrCp}{bhg0H$&X|%7uTG%s-%w}|`kj`DhUc|UuxJRf&$ zMIEUr=i->xB`AGIeOvwsA0O{`s0BXOL{NISzdWDtv8Ki3d}-w`-!Cy{{K3dyx0mzV z^3T=%XMK~G^&Jd<-JbeSY((6Njqq`O;-7yYy<#~}p1)Ziyi4_aK%b{?SBB=Z703O* v;)d2oD~_|Rj$7-Bqqd4uUM~UbAs`-nezNy8rW6 zU)W$GZfCaBY-eL<*T%bMowF@lvCeeM%r5ua{@0JC)5MRSnysvviPD|pGxo3ly-|O1 zr*`n;gV~Y$ujKxjzs#;ZF@rab&E~giN_U3-iO0zU-}H0&s3vVy*F*nH9{+)_+)2x-;~XpE!u0{G#-df8km3Lwtjin)s{hVSf6} zTGcr4{4&hJM<|3t0o?7o|HxKXu0o&$ySI zqI75OU(46etk-<=#2--3>yORO`x+LdJ465EvDs@+ybsQSufwUkHKjX4|8zh3A^vmw zHSt&1Lq9P(@#m*>XXsx%PJW2@ox3&hSJy*7bz87hKjNvMDBT(QsT*U2jGg+4(w(87 z{L{au{QZY1zbM@q`WYX2AbAdYwHt08$Uh*^V#6jkZ`4gp={BInl ze2}{3C;h0dhyCX|^Q3n0=#=g(`WY*J>dtdSlUuu@hX38$$q(`K>=f}=*F!(=Xw(T(H}+gndZ~V|d%uZ;_;`O5rI-9Xzu9}? zx&8n2{?Hiz|JeTCVuf@1Ml8kBB_Os_UVjy6HP*^!rQt$oEU5|Ev+>;OO_0^pcm#azf(yUzF|)^N+cY?@dTN`++Fk8TzR^aS$Kh4x;puf6B+p z-Ur!>nLkmwGtAF7D!C!K>Axu58TwiG+5_qL+(GI;l$d5HG*^ zh|)`b=AAl0y*G){OZ6i!b%NB5-w#CTrTUp4)SiQSaqo!Ionig(vGzE|50hV%?i~7i z>Gw;2e$DawWsl!4`+xhp*9X>J=zRPAeQ$!_DPI)FzOueW-0rq_u^9K^A#p$4FSW-W z+g?oVtyk@N8TftmTE4$zV;Jr$jE(&Ta#Yvz`ZWAm4TnBziyv}S*F%5)e8r<5O4E-1Oh)VDH__)7B>CU0Q zS~p(*dVTpE^ZTl=hi^vLO(Tx?f5mA%yr@0i&#SndQXJnPid$=r?~{CeF$NvJZin}u NDBU?6-=uTg{sGWqc#;4B From 111533f0b0c0f3bb95cc8961a4bcbe99ace4eae8 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 31 Mar 2022 16:36:19 -0700 Subject: [PATCH 14/44] Move power pins to horizontal or vertical layer in all cells. --- technology/freepdk45/gds_lib/cell_1rw.gds | Bin 19192 -> 18132 bytes technology/freepdk45/gds_lib/cell_2rw.gds | Bin 15226 -> 14614 bytes .../freepdk45/gds_lib/dummy_cell_1rw.gds | Bin 18930 -> 18510 bytes .../freepdk45/gds_lib/dummy_cell_2rw.gds | Bin 14976 -> 14364 bytes .../freepdk45/gds_lib/replica_cell_1rw.gds | Bin 19060 -> 18512 bytes .../freepdk45/gds_lib/replica_cell_2rw.gds | Bin 15234 -> 14622 bytes technology/scn4m_subm/gds_lib/cell_1rw.gds | Bin 5868 -> 5746 bytes .../scn4m_subm/gds_lib/dummy_cell_1rw.gds | Bin 5544 -> 5422 bytes .../scn4m_subm/gds_lib/replica_cell_1rw.gds | Bin 5906 -> 5784 bytes 9 files changed, 0 insertions(+), 0 deletions(-) diff --git a/technology/freepdk45/gds_lib/cell_1rw.gds b/technology/freepdk45/gds_lib/cell_1rw.gds index 3a67e40b4ceeba52e0663ae9a61c1e918a35b8f5..905175076a286367b1dcee649395e8746ad5a589 100644 GIT binary patch delta 169 zcmew{mGMe1qY?uf0~1pOgA5}R`!fb+26+Yn1`!5jWcEZQRdF0r8{;0xZ?02lV%+SY z?7+53tDZ%KgMmkYnSp_669WivNV28prA+>)CxtA*)dZ3#O94r!P43p)zqw1_j%hQ8 bQ4`~0FB8_yrz~GEZZ@*H#Io7NaSJB^lQbqA delta 913 zcmcc8%lKm|qY?uf0~1pOgA5}R`%?x^1_1_s5JYBAR8kekA+<5?fxO25|Nkq2ct40{ zU|?_r;&vDf69>_o)f8qi*0cNuu^AXxenZr-al&YrIGAP;1S;WRU|{2AV&G4T|*>4VWQalHELDK&tB$qHf-lNH2bW)Bz*6UXNO4F)0&*t|?JijBh10y>1K zm|?&b+MEBYH?T~;t#ew9sgQwz6BuP6nj08pOn(tHOq}!g<{sS$CRl>l1+pF(Hf}%+ zN)TWgC(VNfc&tg;qN9ZnSf&i)kCkQYNQUi_fU6XrsrKqoTvyY(^E3w9&(Un#O z1t(#D`Is>=O}=EVB>+^t5a@F`1`bKK^t=>?$!E-s_?R9rFmO!+36-U!Fl_d)xWTgd Ifz20A03t>v5dZ)H diff --git a/technology/freepdk45/gds_lib/cell_2rw.gds b/technology/freepdk45/gds_lib/cell_2rw.gds index ca7468cf7a094b00bd96065d213ecce73753e642..cd57876817e87c14a13e9c9f7cf0dfa11d556cbe 100644 GIT binary patch delta 141 zcmexWHmyjBfsKKQDS|g8+jp13NN%qLQI77U7L)N_>+~@HuYwR@32 E0EQ_eh4wPYy6*cjRYa<6{Dvts}q;w5Fby_~2WD25h>8r$LZHNN w1`bKK^t=?HK9FUo3MK(vH5H_wECr?j>KeYuH;nBW`6kaWwqRoi>SJL507no|SO5S3 diff --git a/technology/freepdk45/gds_lib/dummy_cell_1rw.gds b/technology/freepdk45/gds_lib/dummy_cell_1rw.gds index cbfcb06421b33ab8ed54140940c31b6e25d1928a..031ded2ba10f04b29c143651edf438f2d99b0470 100644 GIT binary patch delta 154 zcmew~nep5NMkNL|1}3Hm1{p>s_Gb*t4Dt*D3?dAi$n1$qs^U1LHpVTJ+w3Cmz`6N| z#z9t54h9|pW(EePO$;EwA<34Wm%=bPM^6%2hN}rAQd;`6h4nv%f$`I1MkNL|1}3Hm1{p>s_NNS-3<3=N3}Ord$n1$qs^U1LHpVTJ)A;}YeAbRr~xd=|8RZq^5k>2dCv5$3gqTXIcn6_O|OWdHAfN7u@SR6!e zW;2+pt(8%%nS@ng$xXwzaca?Cy@TjzyPCR;+(%H zAG0)KV|u{Az%^~MgP!Q*%a+OlKvygTnj*)*A<34Wm%_05n58G*uBf!kSz%qvc1UMww((_V)TtOh_VPIh6Wn$oFU=w2k%CU(vGQd<%VqoB& W08(9+lESe0i%6R?8#_=33j+XVP!>%9 delta 433 zcmbPJ&`_$xz{bGD6u}_F$i)7Pftf*$ftNv(K^2)jQOQski}1!Y4L&6nZUzQ!e+C9H zeUX8IRT9XD(O~h(8+hd=_whL@ivIuqe?L&AC<6n7BT%I>jE0F%-oP)v`8;1eGm)ll z_7nQgxOt^WuCfOQ0|Of`69Yd38y^$USRDaophX=(r*uQ<`9OLOkPo9_;@u1^fgHp_Kf_K=ZV=nqMFqUbmViO!d{^1B0zck diff --git a/technology/freepdk45/gds_lib/replica_cell_1rw.gds b/technology/freepdk45/gds_lib/replica_cell_1rw.gds index cef0722d49c6456cabf4a9c5df43f82b86c40383..706f0220c82b4785b5e1382a90b47a858b280e58 100644 GIT binary patch delta 160 zcmew|h4I1!MkNL|1}3Hm1{p>s_Gb*t4Dt*D3?dAc$n1$qs^U1LHpab>-`u9)#I)H% zxsGi!n^q$WF9!pU05byv(s_NNS-3<3=N3_=XX$n1$qs^U1LHpab>*ZBYceAbPW&LKKr4lNE@~z`$e$QN!#3qhaD8dUBqIv?4*3o3oVWu#sZ_ zEXK_pTK}0QKi0KZV=81|;QS4A2$1IH1k!&IG)$cH_ht^g2qu^nyMRsudC?7sL0$yY zKryg5h~DgBFoRtMqzWbqQw5`8;+v0{voTHfv(yp*iYx@m$}wuBf!kSz%qvc1UMww((_V)TtOh_VPIh6Wn$oFU=w2k%CU(vGQd<%VqoB& W08(9+lESe0n@F1q8#_=33j+XZdKOp! delta 433 zcmbPN)Ksp-z{bGD6u}_F$i)7Pftf*$ftNv%K^>VrQOQski}1!YEj~3CZUzQ!e+C9H zeUX8IRT9XD(J*oU$z^=es-pk@|KAT(DaydW;0RQy45MM~^vv{CxnY=(mMTTV|P~tZOha_8iUJ6hj n$TCy~lYp+83Q|y(0#g8W4d3Jk#`cVSlNT6Uu(1R6u`mDt@6b%V diff --git a/technology/scn4m_subm/gds_lib/cell_1rw.gds b/technology/scn4m_subm/gds_lib/cell_1rw.gds index 92523dc1bf8eb78f321678c9b2b1d2bce034067a..6f4d7b268a63b17752ed5d9a96f7dbf7f0e3d8c3 100644 GIT binary patch delta 117 zcmaE(`$g8+jlgCR0|qLQjO4ylcC3A~&C@$O}nU=wF# r;9%epU}j+8O=DotSiry`$ySzg8+jV11BYm>C#&c^DWp7BFx~vX!NzFibWQJ}}uxa!^G7ECLa@i#l!2t Wz@V{!fkTq5EM;?6(FJj diff --git a/technology/scn4m_subm/gds_lib/replica_cell_1rw.gds b/technology/scn4m_subm/gds_lib/replica_cell_1rw.gds index 53525453da5d3e71b9bc506b7936044fb6beeffb..a5b5e3d26b206d8059c634ecd31cbd13232cd8f4 100644 GIT binary patch delta 108 zcmbQFH$zv6fsKKQDS|g8+jVgDf(8qLRKSR*8*i8N8eM`2Mo;vWYV? ja4_%)Ff%alR!z delta 183 zcmbQCJ4sK8fsKKQDS| Date: Mon, 4 Apr 2022 16:02:47 -0700 Subject: [PATCH 15/44] Fix WL to gnd spacing for grounded wordlines. --- technology/freepdk45/gds_lib/cell_2rw.gds | Bin 14614 -> 14422 bytes .../freepdk45/gds_lib/dummy_cell_2rw.gds | Bin 14364 -> 14172 bytes .../freepdk45/gds_lib/replica_cell_2rw.gds | Bin 14622 -> 14430 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/technology/freepdk45/gds_lib/cell_2rw.gds b/technology/freepdk45/gds_lib/cell_2rw.gds index cd57876817e87c14a13e9c9f7cf0dfa11d556cbe..728b20a795535b32a4885490014edacd3cc39a39 100644 GIT binary patch literal 14422 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 F{{=a|r)vNJ literal 14614 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 OG=FEZephf+xPJpow3254 diff --git a/technology/freepdk45/gds_lib/replica_cell_2rw.gds b/technology/freepdk45/gds_lib/replica_cell_2rw.gds index ef71ca9d4f1d02a07c9c858b3ebab6784de539ba..f7bec774581137999370dbc670b05c9c77bbaec5 100644 GIT binary patch literal 14430 zcmcheU94S270379XMdb~drK%#`Km$fgTbn?CPW((E2LsdC5Vcmt=yEhDUC&F(Kdz$ zWBh1LG#DNz za`=@s`@h$kHEU+i*=NY=tSD~GHsnR~Y*yl*bF)0VsQ>ruf}(!M;R8o@e|XLCGJtzhxgrn@W7tki+lDRJh=G! z!$-4noMrXeBAd(VbBZj>UVZK;%Zl^xw_iBsw}SU)nJ7D0uFEFt>od41bYz43HJ|Dz zI~do0+WD_R`fWCJq<`8|{zw0W=46(U z{G#k&QeQnkSuZ=7)XPtKz0aSg>?!}uTX~zyALDM2KT&ot>c6iqf9>=w{e73yv!6DO z1OKw8`fqiQ+9&5RcUb@8*xSKq{C)l0dZ!ns)z3XP*}v>y)PKc!gE&dX&H5K*PsQKY zGj8g|Y4wbAl7HD#@pFyJ_h9@$=8|(rlpT!rzpu|vJAF%k-{th9EtCDX_APP$#U>X& z$+(NVIx>FW9*w`RXWZ1coKnv?C;4yfiTD{a<0ScE{fn}v^4HfhZt7e5`!1(voRj>w z_GRK9Ejc~c9r_n#2c!9?U#=6RbfWCbtsnBk^^^WZ*;D@ceBe51&k4j|9CC4xvZvO+ zzMku*=|q3u<@D_5B>%Ff`d@8y^+Q@Nk9B1I`1WZ2`+C+5*PoVC>RBg~{I~Y7e%AE& zpY=7^-#v|-gN(Piv7_u@KmI^(dp_*F$m$#5Qb+E|e0x-1zX0Ex@c|p|{qF22I~dhB z+Y#@-p#et=;K`1%gHe6C6Lt1>e8B4cAZkaH9gOPppS%2#%wO?LN9NDBNA=kb_(Yya z<}dq6N7=!so<7-6lKszr-%<8d|MQ1j{z>M)z#dv;{(XDYKO)Z7+{%B0XYlNNZI7~t z-#>!-S#A8Q9{=l6qrbu!fOF4*Pjr+WjP`%@J>=^t2`Ln17<|U{qgx8*yKZdw?82u6d&DU|j!Om;VA}{`vh?Wd3PS<)1m`IFlUr`e;Yl zQ{!L#-NjEbZt6wG@7tsCGxPHYT>j^uhZgg%rR)dd{kQcO>jydiGxX)ojJd_BX}NA@PJuCn@)9QR$gwA%W({W;~l<>w&Amr{T9;O4luxc(y4_!pf1bzbk^ z?XB(at;+RS`@V|{)+36mk>8D??8bg>2YQU_Hm5I6Iekmnw*|Ew=;@Plz1z;DdVz()Lz}L6m*4w+Qs*d*E{(zCi28?>fp3M*E-N^rxdr~ozoGA#CoN^aCGMa5KbwOf=VbZIjKcrnqwHyYcD1{X^+O%&$5HmQKEKIb$8n-A$M`wQ zp4Kz}a~M04drRhDlpT!rfBZ4zYzZH5?s5?8v?x0m)i;Nr`3v#}a$Re_*->^ds?R@; z>udCzd?WaHkFwWseYS|}(SPGr;J5JKqU^r^3vO*i{k3arbp7IcE!-FD-_3_HEEfMt z^vzw+uXp;v`Z<;V{7cYZZu;T+JC#2BY^PWJW7q$pzy6M%M13LeqbHHOA7Sq86=gT} z$1l)l+}m;u!ympAVGTISZj9359gMZ|-#CuIkt{&Ng2uOq1&eWs)ADSh#v)06gYr0Ph$Z;$#f_T%~`TnDn> z74)L)sr>P~H1kAKH~L^l*;DzW55`F{{u=XGlzq_uQNR9JH=Jwyvv9=Kk0?9X?|+~l z{TPqWXWz3D%)U$p?*$4W%-~SovKhV!m|ABsn z{s;Qi_J4-?7x2<4wU$__^TbA0T# z3=qEoxMz>2>|j)1!f2ocpp;slz+|}=80tgsS{;StCSfy2JdD%-8e*^st^Ec4j{sCi8 zvTp3z<;j}x?fv@spX!I}N43$#LDIi(kNO|#hw(3`w|?I+x&M{yHw5cX(Eoa~n}4L` z@8OP-^()XfA8>k-`r-QL+oSzot$rB)(7$h=&i@@geoyv)x%h|s2Y&zH^WV7Mt&7-S zME$Q;U%t)nKS=FcMELjZQU9ye594Rf+BjC){|9{D#3}Sa{Lq+3+dg;wKE%!}op#UT zJly|<>&M7Ff9y;Yo3R>4*M@dfy)H|8W0gXN=(A_T=zCVgD4w-#^R5 zxV4YczRI^x?SH6W&cFP$y8B%IMj-R&+oS&Z-kjHwjGy%)%AU&q(Em_BjemPi!atuM z)Qhr%(fGOk@H&z@uDhb_|4;oeeji^lemnPbNO+t^_(p0Ixo>UXvJ z{#h*gEnal~Nv_4dJ=*`F|Dk?G@#DE|{r9B*<@Br(%N1yK|6E7e!D#%e)mO)S%pm=@ z>*)ENeI@>fdLLghexAqYbylnBe@of72K`^?e}A&G9owPb3EfFQ=A!J|<9a(g2A_7e z>;Slg`tdBi9qh*+_|JC0C$1x@v$Iv6vZwUTcEpX}&Wt|<5qlxZ9{&Cs_~$z+>W$Fx zY!>xK*%Nx^eeN>!fjkaA(WC5_#rsbk&t@6VIKBOThWoVk@N8BPf4LLi4+tcv-^};*#RgpUG;#)74!_z__QBc2KyPPH zjzaSg=FSVa&lNq&j{h$w=D+^D^H0)$b6!XK_w8{#eDXYxow32+dA5dsH_D#yKSTY6 z{a<z@ta}(1pe)8(;|H0_os^Wa$S$IFUI}b*(R)Cb~X>|mYr?#x|G(vHk>l_M85V(A(K2QTBv?#$7GC_?bUD(?96-yY51N}gX0(o<&t0jcw<@c;k- literal 14622 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 CRjNJ! From 5e546ee97445f3707168c8b34dd24f781b5d8bae Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 5 Apr 2022 13:51:55 -0700 Subject: [PATCH 16/44] New power strapping mostly working. Each module uses M3/M4 power straps with pins on the ends. Works in all technologies for a single no mux, dual port SRAM. --- compiler/base/hierarchy_layout.py | 347 +++++++++++++++++---- compiler/modules/bitcell_array.py | 2 + compiler/modules/bitcell_base_array.py | 11 +- compiler/modules/control_logic.py | 60 +++- compiler/modules/delay_chain.py | 8 +- compiler/modules/dff_array.py | 29 +- compiler/modules/dff_buf_array.py | 31 +- compiler/modules/dummy_array.py | 4 + compiler/modules/hierarchical_decoder.py | 66 +--- compiler/modules/hierarchical_predecode.py | 6 +- compiler/modules/port_address.py | 24 +- compiler/modules/precharge_array.py | 8 +- compiler/modules/replica_bitcell_array.py | 132 +++++--- compiler/modules/replica_column.py | 14 +- compiler/modules/wordline_driver_array.py | 52 +-- compiler/options.py | 2 +- compiler/sram/sram_1bank.py | 2 - 17 files changed, 512 insertions(+), 286 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 403920c6..6fca63c7 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -407,8 +407,8 @@ class layout(): """ pins = instance.get_pins(pin_name) - debug.check(len(pins) > 0, - "Could not find pin {}".format(pin_name)) + if len(pins) == 0: + debug.warning("Could not find pin {0} on {1}".format(pin_name, instance.mod.name)) for pin in pins: if new_name == "": @@ -427,11 +427,194 @@ class layout(): for pin_name in self.pin_map.keys(): self.copy_layout_pin(instance, pin_name, prefix + pin_name) - def route_vertical_pins(self, name, insts=None, layer=None, xside="cx", yside="cy"): + def connect_row_locs(self, from_layer, to_layer, locs, name=None, full=False): + """ + Connects left/right rows that are aligned on the given layer. + """ + bins = {} + for loc in locs: + y = pin.y + try: + bins[y].append(loc) + except KeyError: + bins[y] = [loc] + + for y, v in bins.items(): + # Not enough to route a pin, so just copy them + if len(v) < 2: + continue + + if full: + left_x = 0 + right_x = self.width + else: + left_x = min([loc.x for loc in v]) + right_x = max([loc.x for loc in v]) + + left_pos = vector(left_x, y) + right_pos = vector(right_x, y) + + # Make sure to add vias to the new route + for loc in v: + self.add_via_stack_center(from_layer=from_layer, + to_layer=to_layer, + offset=loc, + min_area=True) + + if name: + self.add_layout_pin_segment_center(text=name, + layer=to_layer, + start=left_pos, + end=right_pos) + else: + self.add_segment_center(layer=to_layer, + start=left_pos, + end=right_pos) + + def connect_row_pins(self, layer, pins, name=None, full=False): + """ + Connects left/right rows that are aligned. + """ + bins = {} + for pin in pins: + y = pin.cy() + try: + bins[y].append(pin) + except KeyError: + bins[y] = [pin] + + for y, v in bins.items(): + # Not enough to route a pin, so just copy them + if len(v) < 2: + continue + + if full: + left_x = 0 + right_x = self.width + else: + left_x = min([pin.lx() for pin in v]) + right_x = max([pin.rx() for pin in v]) + + left_pos = vector(left_x, y) + right_pos = vector(right_x, y) + + # Make sure to add vias to the new route + for pin in v: + self.add_via_stack_center(from_layer=pin.layer, + to_layer=layer, + offset=pin.center(), + min_area=True) + + if name: + self.add_layout_pin_segment_center(text=name, + layer=layer, + start=left_pos, + end=right_pos) + else: + self.add_segment_center(layer=layer, + start=left_pos, + end=right_pos) + + def connect_col_locs(self, from_layer, to_layer, locs, name=None, full=False): + """ + Connects top/bot columns that are aligned. + """ + bins = {} + for loc in locs: + x = loc.x + try: + bins[x].append(loc) + except KeyError: + bins[x] = [loc] + + for x, v in bins.items(): + # Not enough to route a pin, so just copy them + if len(v) < 2: + continue + + if full: + bot_y = 0 + top_y = self.height + else: + bot_y = min([loc.y for loc in v]) + top_y = max([loc.y for loc in v]) + + top_pos = vector(x, top_y) + bot_pos = vector(x, bot_y) + + # Make sure to add vias to the new route + for loc in v: + self.add_via_stack_center(from_layer=from_layer, + to_layer=to_layer, + offset=loc, + min_area=True) + + if name: + self.add_layout_pin_segment_center(text=name, + layer=to_layer, + start=top_pos, + end=bot_pos) + else: + self.add_segment_center(layer=to_layer, + start=top_pos, + end=bot_pos) + + + def connect_col_pins(self, layer, pins, name=None, full=False): + """ + Connects top/bot columns that are aligned. + """ + bins = {} + for pin in pins: + x = pin.cx() + try: + bins[x].append(pin) + except KeyError: + bins[x] = [pin] + + for x, v in bins.items(): + # Not enough to route a pin, so just copy them + if len(v) < 2: + continue + + if full: + bot_y = 0 + top_y = self.height + else: + bot_y = min([pin.by() for pin in v]) + top_y = max([pin.uy() for pin in v]) + + top_pos = vector(x, top_y) + bot_pos = vector(x, bot_y) + + # Make sure to add vias to the new route + for pin in v: + self.add_via_stack_center(from_layer=pin.layer, + to_layer=layer, + offset=pin.center(), + min_area=True) + + if name: + self.add_layout_pin_segment_center(text=name, + layer=layer, + start=top_pos, + end=bot_pos) + else: + self.add_segment_center(layer=layer, + start=top_pos, + end=bot_pos) + + + + + + def route_vertical_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", num_pins=2, full_width=True): """ Route together all of the pins of a given name that vertically align. Uses local_insts if insts not specified. Uses center of pin by default, or right or left if specified. + num_pins specifies whether to add a single pin or multiple pins (equally spaced) + TODO: Add equally spaced option for IR drop min, right now just 2 """ @@ -451,7 +634,7 @@ class layout(): for x, v in bins.items(): # Not enough to route a pin, so just copy them if len(v) < 2: - debug.warning("Copying pins instead of connecting with pin.") + debug.warning("Pins don't align well so copying pins instead of connecting with pin.") for inst,pin in v: self.add_layout_pin(pin.name, pin.layer, @@ -460,10 +643,8 @@ class layout(): pin.height()) continue - bot_y = min([pin.by() for (inst,pin) in v]) - top_y = max([pin.uy() for (inst,pin) in v]) - last_via = None + pin_layer = None for inst,pin in v: if layer: pin_layer = layer @@ -482,31 +663,57 @@ class layout(): else: via_width=None + if full_width: + bot_y = 0 + top_y = self.height + else: + bot_y = min([pin.by() for (inst,pin) in v]) + top_y = max([pin.uy() for (inst,pin) in v]) top_pos = vector(x, top_y) bot_pos = vector(x, bot_y) -# top_rect = self.add_layout_pin_rect_center(text=name, -# layer=pin_layer, -# offset=top_pos) - #bot_rect = self.add_layout_pin_rect_center(text=name, - # layer=pin_layer, - # offset=bot_pos) -# self.add_segment_center(layer=pin_layer, -# start=vector(top_rect.cx(), bot_pos.y), -# end=top_rect.bc(), -# width=via_width) - self.add_layout_pin_segment_center(text=name, - layer=pin_layer, - start=top_pos, - end=bot_pos, - width=via_width) + + if num_pins==2: + self.add_layout_pin_rect_ends(name=name, + layer=pin_layer, + start=top_pos, + end=bot_pos, + width=via_width) + else: + self.add_layout_pin_segment_center(text=name, + layer=pin_layer, + start=top_pos, + end=bot_pos, + width=via_width) + def add_layout_pin_rect_ends(self, name, layer, start, end, width=None): - def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy"): + # This adds pins on the end connected by a segment + top_rect = self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=start) + bot_rect = self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=end) + # This is made to not overlap with the pin above + # so that the power router will only select a small pin. + # Otherwise it adds big blockages over the rails. + if start.y != end.y: + self.add_segment_center(layer=layer, + start=bot_rect.uc(), + end=top_rect.bc()) + else: + self.add_segment_center(layer=layer, + start=bot_rect.rc(), + end=top_rect.lc()) + + def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", num_pins=2, full_width=True): """ Route together all of the pins of a given name that horizontally align. Uses local_insts if insts not specified. Uses center of pin by default, or top or botom if specified. + num_pins specifies whether to add a single pin or multiple pins (equally spaced) + TODO: Add equally spaced option for IR drop min, right now just 2 """ @@ -527,7 +734,7 @@ class layout(): for y, v in bins.items(): if len(v) < 2: - debug.warning("Copying pins instead of connecting with pin.") + debug.warning("Pins don't align well so copying pins instead of connecting with pin.") for inst,pin in v: self.add_layout_pin(pin.name, pin.layer, @@ -536,10 +743,8 @@ class layout(): pin.height()) continue - left_x = min([pin.lx() for (inst,pin) in v]) - right_x = max([pin.rx() for (inst,pin) in v]) - last_via = None + pin_layer = None for inst,pin in v: if layer: pin_layer = layer @@ -550,7 +755,7 @@ class layout(): last_via = self.add_via_stack_center(from_layer=pin.layer, to_layer=pin_layer, - offset=vector(pin.cx(), y), + offset=vector(x, y), min_area=True) if last_via: @@ -558,29 +763,56 @@ class layout(): else: via_height=None + if full_width: + left_x = 0 + right_x = self.width + else: + left_x = min([pin.lx() for (inst,pin) in v]) + right_x = max([pin.rx() for (inst,pin) in v]) left_pos = vector(left_x, y) right_pos = vector(right_x, y) -# left_rect = self.add_layout_pin_rect_center(text=name, -# layer=pin_layer, -# offset=left_pos) - #right_rect = self.add_layout_pin_rect_center(text=name, - # layer=pin_layer, - # offset=right_pos) - # This is made to not overlap with the pin above - # so that the power router will only select a small pin. - # Otherwise it adds big blockages over the rails. -# self.add_segment_center(layer=pin_layer, -# start=left_rect.rc(), -# end=vector(right_pos.x, left_rect.cy()), -# width=via_height) + if num_pins==2: + self.add_layout_pin_rect_ends(name=name, + layer=pin_layer, + start=left_pos, + end=right_pos, + width=via_height) + else: + # This adds a single big pin + self.add_layout_pin_segment_center(text=name, + layer=pin_layer, + start=left_pos, + end=right_pos, + width=via_height) - self.add_layout_pin_segment_center(text=name, - layer=pin_layer, - start=left_pos, - end=right_pos, - width=via_height) + def add_layout_end_pin_segment_center(self, text, layer, start, end): + """ + Creates a path with two pins on the end that don't overlap. + """ + start_pin = self.add_layout_pin_rect_center(text=text, + layer=layer, + offset=start) + end_pin = self.add_layout_pin_rect_center(text=text, + layer=layer, + offset=end) + + if start.x != end.x and start.y != end.y: + file_name = "non_rectilinear.gds" + self.gds_write(file_name) + debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1) + elif start.x != end.x: + self.add_segment_center(layer=layer, + start=start_pin.rc(), + end=end_pin.lc()) + elif start.y != end.y: + self.add_segment_center(layer=layer, + start=start_pin.uc(), + end=end_pin.bc()) + else: + debug.error("Cannot have a point pin.", -1) + def add_layout_pin_segment_center(self, text, layer, start, end, width=None): """ @@ -1447,12 +1679,13 @@ class layout(): width = None height = None + pin = None if start_layer in self.pwr_grid_layers: - self.add_layout_pin_rect_center(text=name, - layer=start_layer, - offset=loc, - width=width, - height=height) + pin = self.add_layout_pin_rect_center(text=name, + layer=start_layer, + offset=loc, + width=width, + height=height) else: via = self.add_via_stack_center(from_layer=start_layer, to_layer=self.pwr_grid_layers[0], @@ -1463,11 +1696,13 @@ class layout(): width = via.width if not height: height = via.height - self.add_layout_pin_rect_center(text=name, - layer=self.pwr_grid_layers[0], - offset=loc, - width=width, - height=height) + pin = self.add_layout_pin_rect_center(text=name, + layer=self.pwr_grid_layers[0], + offset=loc, + width=width, + height=height) + + return pin def copy_power_pin(self, pin, loc=None, directions=None, new_name=""): """ diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index d08e3f48..0432dec2 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -46,6 +46,8 @@ class bitcell_array(bitcell_base_array): self.add_layout_pins() + self.route_supplies() + self.add_boundary() self.DRC_LVS() diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 5bff2448..45285267 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -156,18 +156,15 @@ class bitcell_base_array(design.design): width=self.width, height=wl_pin.height()) - def add_supply_pins(self): - for row in range(self.row_size): - for col in range(self.column_size): - inst = self.cell_inst[row, col] - for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(inst, pin_name) + def route_supplies(self): + for inst in self.cell_inst.values(): + for pin_name in ["vdd", "gnd"]: + self.copy_layout_pin(inst, pin_name) def add_layout_pins(self): """ Add the layout pins """ self.add_bitline_pins() self.add_wl_pins() - self.add_supply_pins() def _adjust_x_offset(self, xoffset, col, col_offset): tempx = xoffset diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 01810323..df7a8241 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -280,7 +280,8 @@ class control_logic(design.design): def route_rails(self): """ Add the input signal inverted tracks """ height = self.control_logic_center.y - self.m2_pitch - offset = vector(self.ctrl_dff_array.width, 0) + # DFF spacing plus the power routing + offset = vector(self.ctrl_dff_array.width + self.m4_pitch, 0) self.input_bus = self.create_vertical_bus("m2", offset, @@ -312,7 +313,8 @@ class control_logic(design.design): self.place_dffs() # All of the control logic is placed to the right of the DFFs and bus - self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width + # as well as the power supply stripe + self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width + self.m4_pitch row = 0 # Add the logic on the right of the bus @@ -368,7 +370,7 @@ class control_logic(design.design): self.route_clk_buf() self.route_gated_clk_bar() self.route_gated_clk_buf() - self.route_supply() + self.route_supplies() def create_delay(self): """ Create the replica bitline """ @@ -707,28 +709,60 @@ class control_logic(design.design): start=out_pos, end=right_pos) - def route_supply(self): + def route_supplies(self): """ Add vdd and gnd to the instance cells """ - supply_layer = self.dff.get_pin("vdd").layer + pin_layer = self.dff.get_pin("vdd").layer + supply_layer = self.supply_stack[2] max_row_x_loc = max([inst.rx() for inst in self.row_end_inst]) + min_row_x_loc = self.control_x_offset + + vdd_pin_locs = [] + gnd_pin_locs = [] + for inst in self.row_end_inst: pins = inst.get_pins("vdd") for pin in pins: - if pin.layer == supply_layer: + if pin.layer == pin_layer: row_loc = pin.rc() pin_loc = vector(max_row_x_loc, pin.rc().y) - self.add_power_pin("vdd", pin_loc, start_layer=pin.layer) - self.add_path(supply_layer, [row_loc, pin_loc]) + vdd_pin_locs.append(pin_loc) + self.add_via_stack_center(from_layer=pin_layer, + to_layer=supply_layer, + offset=pin_loc, + min_area=True) + self.add_path(pin_layer, [row_loc, pin_loc]) pins = inst.get_pins("gnd") for pin in pins: - if pin.layer == supply_layer: + if pin.layer == pin_layer: row_loc = pin.rc() - pin_loc = vector(max_row_x_loc, pin.rc().y) - self.add_power_pin("gnd", pin_loc, start_layer=pin.layer) - self.add_path(supply_layer, [row_loc, pin_loc]) + pin_loc = vector(min_row_x_loc, pin.rc().y) + gnd_pin_locs.append(pin_loc) + self.add_via_stack_center(from_layer=pin_layer, + to_layer=supply_layer, + offset=pin_loc, + min_area=True) + self.add_path(pin_layer, [row_loc, pin_loc]) + + min_y = min([x.y for x in vdd_pin_locs]) + max_y = max([x.y for x in vdd_pin_locs]) + bot_pos = vector(max_row_x_loc, min_y) + top_pos = vector(max_row_x_loc, max_y) + self.add_layout_pin_segment_center(text="vdd", + layer=supply_layer, + start=bot_pos, + end=top_pos) + + min_y = min([x.y for x in gnd_pin_locs]) + max_y = max([x.y for x in gnd_pin_locs]) + bot_pos = vector(min_row_x_loc, min_y) + top_pos = vector(min_row_x_loc, max_y) + self.add_layout_pin_segment_center(text="gnd", + layer=supply_layer, + start=bot_pos, + end=top_pos) self.copy_layout_pin(self.delay_inst, "gnd") self.copy_layout_pin(self.delay_inst, "vdd") @@ -781,7 +815,7 @@ class control_logic(design.design): # Connect this at the bottom of the buffer out_pin = inst.get_pin("Z") out_pos = out_pin.center() - mid1 = vector(out_pos.x, out_pos.y - 0.4 * inst.mod.height) + mid1 = vector(out_pos.x, out_pos.y - 0.3 * inst.mod.height) mid2 = vector(self.input_bus[name].cx(), mid1.y) bus_pos = self.input_bus[name].center() self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos]) diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index a5bcea27..af446bbd 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -177,9 +177,11 @@ class delay_chain(design.design): # The routing to connect the loads is over the first and last cells # We have an even number of drivers and must only do every other # supply rail - if OPTS.experimental_power: - self.route_horizontal_pins("vdd") - self.route_horizontal_pins("gnd") + if True or OPTS.experimental_power: + left_load_insts = [self.load_inst_map[x][0] for x in self.driver_inst_list] + right_load_insts = [self.load_inst_map[x][-1] for x in self.driver_inst_list] + self.route_vertical_pins("vdd", left_load_insts, xside="lx") + self.route_vertical_pins("gnd", right_load_insts, xside="rx") else: for inst in self.driver_inst_list: load_list = self.load_inst_map[inst] diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index 27f6f5bf..99b59064 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -42,6 +42,7 @@ class dff_array(design.design): self.height = self.rows * self.dff.height self.place_dff_array() + self.route_supplies() self.add_layout_pins() self.add_boundary() self.DRC_LVS() @@ -106,17 +107,25 @@ class dff_array(design.design): return dout_name + def route_supplies(self): + if OPTS.experimental_power and self.rows > 1: + # Vertical straps on ends if multiple rows + left_dff_insts = [self.dff_insts[x, 0] for x in range(self.rows)] + right_dff_insts = [self.dff_insts[x, self.columns-1] for x in range(self.rows)] + self.route_vertical_pins("vdd", left_dff_insts, xside="lx", yside="cy") + self.route_vertical_pins("gnd", right_dff_insts, xside="rx", yside="cy") + else: + for row in range(self.rows): + for col in range(self.columns): + # Continous vdd rail along with label. + vdd_pin=self.dff_insts[row, col].get_pin("vdd") + self.copy_power_pin(vdd_pin) + + # Continous gnd rail along with label. + gnd_pin=self.dff_insts[row, col].get_pin("gnd") + self.copy_power_pin(gnd_pin) + def add_layout_pins(self): - for row in range(self.rows): - for col in range(self.columns): - # Continous vdd rail along with label. - vdd_pin=self.dff_insts[row, col].get_pin("vdd") - self.copy_power_pin(vdd_pin) - - # Continous gnd rail along with label. - gnd_pin=self.dff_insts[row, col].get_pin("gnd") - self.copy_power_pin(gnd_pin) - for row in range(self.rows): for col in range(self.columns): din_pin = self.dff_insts[row, col].get_pin("D") diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 6f8b54aa..62a7cfb6 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -145,24 +145,23 @@ class dff_buf_array(design.design): return dout_bar_name def route_supplies(self): - for row in range(self.rows): - vdd0_pin=self.dff_insts[row, 0].get_pin("vdd") - vddn_pin=self.dff_insts[row, self.columns - 1].get_pin("vdd") - self.add_path(vdd0_pin.layer, [vdd0_pin.lc(), vddn_pin.rc()], width=vdd0_pin.height()) + if OPTS.experimental_power and self.rows > 1: + # Vertical straps on ends if multiple rows + left_dff_insts = [self.dff_insts[x, 0] for x in range(self.rows)] + right_dff_insts = [self.dff_insts[x, self.columns-1] for x in range(self.rows)] + self.route_vertical_pins("vdd", left_dff_insts, xside="lx", yside="cy") + self.route_vertical_pins("gnd", right_dff_insts, xside="rx", yside="cy") + else: + for row in range(self.rows): + for col in range(self.columns): + # Continous vdd rail along with label. + vdd_pin=self.dff_insts[row, col].get_pin("vdd") + self.copy_power_pin(vdd_pin) - gnd0_pin=self.dff_insts[row, 0].get_pin("gnd") - gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd") - self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height()) + # Continous gnd rail along with label. + gnd_pin=self.dff_insts[row, col].get_pin("gnd") + self.copy_power_pin(gnd_pin) - for row in range(self.rows): - for col in range(self.columns): - # Continous vdd rail along with label. - vdd_pin=self.dff_insts[row, col].get_pin("vdd") - self.copy_power_pin(vdd_pin, loc=vdd_pin.lc()) - - # Continous gnd rail along with label. - gnd_pin=self.dff_insts[row, col].get_pin("gnd") - self.copy_power_pin(gnd_pin, loc=gnd_pin.lc()) def add_layout_pins(self): diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 0786142f..83449a70 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -36,6 +36,8 @@ class dummy_array(bitcell_base_array): self.add_layout_pins() + self.route_supplies() + self.add_boundary() self.DRC_LVS() @@ -98,6 +100,8 @@ class dummy_array(bitcell_base_array): width=self.width, height=wl_pin.height()) + def route_supplies(self): + # Copy a vdd/gnd layout pin from every cell for row in range(self.row_size): for col in range(self.column_size): diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index f9987bb8..2b173969 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -28,7 +28,6 @@ class hierarchical_decoder(design.design): self.pre2x4_inst = [] self.pre3x8_inst = [] self.pre4x16_inst = [] - self.local_insts = [] b = factory.create(module_type=OPTS.bitcell) self.cell_height = b.height @@ -591,65 +590,20 @@ class hierarchical_decoder(design.design): def route_supplies(self): """ - Add a pin for each row of vdd/gnd which are - must-connects next level up. """ - # This is an experiment with power rails - if OPTS.tech_name=="sky130" or OPTS.experimental_power: - if layer_props.hierarchical_decoder.vertical_supply: - pre_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst - self.route_vertical_pins("vdd", insts=pre_insts, yside="by") - self.route_vertical_pins("gnd", insts=pre_insts, yside="by") - self.route_vertical_pins("vdd", insts=self.and_inst, yside="by") - self.route_vertical_pins("gnd", insts=self.and_inst, yside="by") - else: - pre_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst - self.route_vertical_pins("vdd", insts=pre_insts) - self.route_vertical_pins("gnd", insts=pre_insts) - self.route_vertical_pins("vdd", insts=self.and_inst, xside="rx") - self.route_vertical_pins("gnd", insts=self.and_inst, xside="lx") + # Leave these to route in the port_address + all_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst + for inst in all_insts: + self.copy_layout_pin(inst, "vdd") + self.copy_layout_pin(inst, "gnd") - # Widen the rails to cover any gap - for inst in self.and_inst: - for name in ["vdd", "gnd"]: - supply_pin = inst.get_pin(name) - self.add_segment_center(layer=supply_pin.layer, - start=vector(0, supply_pin.cy()), - end=vector(self.width, supply_pin.cy())) - else: - if layer_props.hierarchical_decoder.vertical_supply: - for n in ["vdd", "gnd"]: - pins = self.and_inst[0].get_pins(n) - for pin in pins: - self.add_rect(layer=pin.layer, - offset=pin.ll() + vector(0, self.bus_space), - width=pin.width(), - height=self.height - 2 * self.bus_space) + for inst in self.and_inst: + for pin in inst.get_pins("vdd"): + self.add_power_pin("vdd", pin.rc()) + for pin in inst.get_pins("gnd"): + self.add_power_pin("gnd", pin.lc()) - # This adds power vias at the top of each cell - # (except the last to keep them inside the boundary) - for i in self.and_inst[:-1]: - pins = i.get_pins(n) - for pin in pins: - self.copy_power_pin(pin, loc=pin.uc()) - - for i in self.pre2x4_inst + self.pre3x8_inst: - self.copy_layout_pin(i, n) - else: - # The vias will be placed at the right of the cells. - xoffset = max(x.rx() for x in self.and_inst) + 0.5 * self.m1_space - for row in range(0, self.num_outputs): - for pin_name in ["vdd", "gnd"]: - # The nand and inv are the same height rows... - supply_pin = self.and_inst[row].get_pin(pin_name) - pin_pos = vector(xoffset, supply_pin.cy()) - self.copy_power_pin(supply_pin, loc=pin_pos) - - # Copy the pins from the predecoders - for pre in self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst: - for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(pre, pin_name) def route_predecode_bus_outputs(self, rail_name, pin, row): """ diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 706fc58e..0d7f31a3 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -381,7 +381,7 @@ class hierarchical_predecode(design.design): def route_supplies(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ - # We may ahve vertical power supply rails + # We may have vertical power supply rails if layer_props.hierarchical_predecode.vertical_supply and not self.column_decoder: for n in ["vdd", "gnd"]: # This makes a wire from top to bottom for both inv and and gates @@ -403,8 +403,6 @@ class hierarchical_predecode(design.design): # In other techs, we are using standard cell decoder cells with horizontal power else: for num in range(0, self.number_of_outputs): - - # Route both supplies for n in ["vdd", "gnd"]: and_pins = self.and_inst[num].get_pins(n) for and_pin in and_pins: @@ -418,4 +416,4 @@ class hierarchical_predecode(design.design): else: xoffset = self.inv_inst[0].lx() - self.bus_space pin_pos = vector(xoffset, and_pin.cy()) - self.copy_power_pin(and_pin, loc=pin_pos) + self.add_power_pin(n, pin_pos) diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 7c3af51c..2757a86f 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -76,15 +76,21 @@ class port_address(design.design): def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ - for inst in [self.wordline_driver_array_inst, self.row_decoder_inst]: - self.copy_layout_pin(inst, "vdd") - self.copy_layout_pin(inst, "gnd") - - for rbl_vdd_pin in self.rbl_driver_inst.get_pins("vdd"): - if layer_props.port_address.supply_offset: - self.copy_power_pin(rbl_vdd_pin) - else: - self.copy_power_pin(rbl_vdd_pin, loc=rbl_vdd_pin.lc()) + self.route_vertical_pins("vdd", [self.row_decoder_inst]) + self.route_vertical_pins("gnd", [self.row_decoder_inst]) + self.route_vertical_pins("vdd", [self.wordline_driver_array_inst]) + if layer_props.wordline_driver.vertical_supply: + self.route_vertical_pins("gnd", [self.wordline_driver_array_inst]) + self.copy_layout_pin(self.rbl_driver_inst, "vdd") + else: + rbl_pos = self.rbl_driver_inst.get_pin("vdd").rc() + self.add_power_pin("vdd", rbl_pos) + self.add_path("m4", [rbl_pos, self.wordline_driver_array_inst.get_pins("vdd")[0].rc()]) + + vdd_pins = self.row_decoder_inst.get_pins("vdd") + self.wordline_driver_array_inst.get_pins("vdd") + self.connect_row_pins(self.route_layer, vdd_pins) + gnd_pins = self.row_decoder_inst.get_pins("gnd") + self.wordline_driver_array_inst.get_pins("gnd") + self.connect_row_pins(self.route_layer, gnd_pins) # Also connect the B input of the RBL and_dec to vdd if OPTS.local_array_size == 0: diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index cfd2015b..c820dfc1 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -83,7 +83,7 @@ class precharge_array(design.design): def add_layout_pins(self): en_pin = self.pc_cell.get_pin("en_bar") - self.route_horizontal_pins("en_bar", layer=self.en_bar_layer) + self.route_horizontal_pins("en_bar", layer=self.en_bar_layer, num_pins=1) for inst in self.local_insts: self.add_via_stack_center(from_layer=en_pin.layer, to_layer=self.en_bar_layer, @@ -95,11 +95,7 @@ class precharge_array(design.design): self.copy_layout_pin(inst, "br", "br_{0}".format(i)) def route_supplies(self): - if OPTS.tech_name=="sky130" or OPTS.experimental_power: - self.route_horizontal_pins("vdd") - else: - for inst in self.local_insts: - self.copy_layout_pin(inst, "vdd") + self.route_horizontal_pins("vdd") def create_insts(self): """Creates a precharge array by horizontally tiling the precharge cell""" diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index be6d1ef5..5a7a046f 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -305,19 +305,18 @@ class replica_bitcell_array(bitcell_base_array): def create_layout(self): - # We will need unused wordlines grounded, so we need to know their layer - # and create a space on the left and right for the vias to connect to ground - pin = self.cell.get_pin(self.cell.get_all_wl_names()[0]) - pin_layer = pin.layer - self.unused_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer)) - self.unused_offset = vector(self.unused_pitch, 0) + # This creates space for the unused wordline connections as well as the + # row-based or column based power and ground lines. + self.vertical_pitch = getattr(self, "{}_pitch".format(self.supply_stack[0])) + self.horizontal_pitch = getattr(self, "{}_pitch".format(self.supply_stack[2])) + self.unused_offset = vector(2 * self.horizontal_pitch, 2 * self.vertical_pitch) # This is a bitcell x bitcell offset to scale self.bitcell_offset = vector(self.cell.width, self.cell.height) self.col_end_offset = vector(self.cell.width, self.cell.height) self.row_end_offset = vector(self.cell.width, self.cell.height) - # Everything is computed with the main array at (self.unused_pitch, 0) to start + # Everything is computed with the main array self.bitcell_array_inst.place(offset=self.unused_offset) self.add_replica_columns() @@ -336,6 +335,8 @@ class replica_bitcell_array(bitcell_base_array): self.add_layout_pins() + self.route_supplies() + self.route_unused_wordlines() self.add_boundary() @@ -458,23 +459,82 @@ class replica_bitcell_array(bitcell_base_array): width=pin.width(), height=self.height) - if OPTS.tech_name=="sky130" or OPTS.experimental_power: - self.route_vertical_pins(name="gnd", insts=self.replica_col_insts) - self.route_horizontal_pins(name="vdd", insts=self.dummy_row_insts) - else: - # vdd/gnd are only connected in the perimeter cells - # replica column should only have a vdd/gnd in the dummy cell on top/bottom - supply_insts = self.dummy_col_insts + self.dummy_row_insts + def route_supplies(self): - for pin_name in self.supplies: - for inst in supply_insts: - pin_list = inst.get_pins(pin_name) - for pin in pin_list: - self.copy_power_pin(pin) + # vdd/gnd are only connected in the perimeter cells + # replica column should only have a vdd/gnd in the dummy cell on top/bottom + supply_insts = self.dummy_col_insts + self.dummy_row_insts + + vdd_leftx = 0 + vdd_rightx = 0 + gnd_leftx = 0 + gnd_rightx = 0 + + for inst in supply_insts: + for pin in inst.get_pins("vdd"): + (vdd_leftx, vdd_rightx) = self.connect_pin(pin, 2.5) + for pin in inst.get_pins("gnd"): + (gnd_leftx, gnd_rightx) = self.connect_pin(pin) + + + self.add_layout_end_pin_segment_center(text="vdd", + layer=self.supply_stack[2], + start=vector(vdd_leftx, 0), + end=vector(vdd_leftx, self.height)) + self.add_layout_end_pin_segment_center(text="vdd", + layer=self.supply_stack[2], + start=vector(vdd_rightx, 0), + end=vector(vdd_rightx, self.height)) + self.add_layout_end_pin_segment_center(text="gnd", + layer=self.supply_stack[2], + start=vector(gnd_leftx, 0), + end=vector(gnd_leftx, self.height)) + self.add_layout_end_pin_segment_center(text="gnd", + layer=self.supply_stack[2], + start=vector(gnd_rightx, 0), + end=vector(gnd_rightx, self.height)) + + + def route_unused_wordlines(self): + """ Connect the unused RBL and dummy wordlines to gnd """ + # This grounds all the dummy row word lines + for inst in self.dummy_row_insts: + for wl_name in self.col_cap_top.get_wordline_names(): + pin = inst.get_pin(wl_name) + self.connect_pin(pin) + + # Ground the unused replica wordlines + for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): + for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): + if wl_name in self.gnd_wordline_names: + pin = inst.get_pin(pin_name) + self.connect_pin(pin) + + + def connect_pin(self, pin, offset_multiple=1): + + pin_layer = pin.layer + + left_pin_loc = vector(self.dummy_col_insts[0].lx(), pin.cy()) + right_pin_loc = vector(self.dummy_col_insts[1].rx(), pin.cy()) + + # Place the pins a track outside of the array + left_loc = left_pin_loc - vector(offset_multiple * self.horizontal_pitch, 0) + right_loc = right_pin_loc + vector(offset_multiple * self.horizontal_pitch, 0) + self.add_via_stack_center(offset=left_loc, + from_layer=pin_layer, + to_layer=self.supply_stack[2], + directions=("H", "H")) + self.add_via_stack_center(offset=right_loc, + from_layer=pin_layer, + to_layer=self.supply_stack[2], + directions=("H", "H")) + + # Add a path to connect to the array + self.add_path(pin_layer, [left_loc, right_loc]) + + return (left_loc.x, right_loc.x) - for inst in self.replica_col_insts: - if inst: - self.copy_layout_pin(inst, pin_name) def analytical_power(self, corner, load): @@ -494,34 +554,6 @@ class replica_bitcell_array(bitcell_base_array): cell_power.leakage * self.column_size * self.row_size) return total_power - def route_unused_wordlines(self): - """ Connect the unused RBL and dummy wordlines to gnd """ - # This grounds all the dummy row word lines - for inst in self.dummy_row_insts: - for wl_name in self.col_cap_top.get_wordline_names(): - self.ground_pin(inst, wl_name) - - # Ground the unused replica wordlines - for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): - for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): - if wl_name in self.gnd_wordline_names: - self.ground_pin(inst, pin_name) - - def ground_pin(self, inst, name): - pin = inst.get_pin(name) - pin_layer = pin.layer - - left_pin_loc = vector(self.dummy_col_insts[0].lx(), pin.cy()) - right_pin_loc = vector(self.dummy_col_insts[1].rx(), pin.cy()) - - # Place the pins a track outside of the array - left_loc = left_pin_loc - vector(self.unused_pitch, 0) - right_loc = right_pin_loc + vector(self.unused_pitch, 0) - self.add_power_pin("gnd", left_loc, directions=("H", "H")) - self.add_power_pin("gnd", right_loc, directions=("H", "H")) - - # Add a path to connect to the array - self.add_path(pin_layer, [left_loc, right_loc], width=pin.height()) def gen_bl_wire(self): if OPTS.netlist_only: diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index a3de3a38..43a3cda3 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -69,6 +69,8 @@ class replica_column(bitcell_base_array): self.add_layout_pins() + self.route_supplies() + self.add_boundary() self.DRC_LVS() @@ -185,15 +187,11 @@ class replica_column(bitcell_base_array): width=self.width, height=wl_pin.height()) - # Supplies are only connected in the ends - for (index, inst) in enumerate(self.cell_inst): + def route_supplies(self): + + for inst in self.cell_inst: for pin_name in ["vdd", "gnd"]: - if inst in [self.cell_inst[0], self.cell_inst[self.total_size - 1]]: - #for pin in inst.get_pins(pin_name): - # self.copy_power_pin(pin) - self.copy_power_pins(inst, pin_name) - else: - self.copy_layout_pin(inst, pin_name) + self.copy_layout_pin(inst, pin_name) def get_bitline_names(self, port=None): if port == None: diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index b4c2c54f..276194ee 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -53,7 +53,7 @@ class wordline_driver_array(design.design): self.height = self.wld_inst[-1].uy() self.add_boundary() - self.route_vdd_gnd() + self.route_supplies() self.DRC_LVS() def add_pins(self): @@ -72,54 +72,16 @@ class wordline_driver_array(design.design): self.wl_driver = factory.create(module_type="wordline_driver", cols=self.cols) - def route_vdd_gnd(self): + def route_supplies(self): """ Add vertical power rails. """ - # Experiment with power straps - if OPTS.tech_name=="sky130" or OPTS.experimental_power: - if layer_props.wordline_driver.vertical_supply: - self.route_vertical_pins("vdd", insts=self.wld_inst) - self.route_vertical_pins("gnd", insts=self.wld_inst) - else: - self.route_vertical_pins("vdd", insts=self.wld_inst, xside="lx") - self.route_vertical_pins("gnd", insts=self.wld_inst, xside="rx") - - # Widen the rails to cover any gap - for num in range(self.rows): - for name in ["vdd", "gnd"]: - supply_pin = self.wld_inst[num].get_pin(name) - self.add_segment_center(layer=supply_pin.layer, - start=vector(0, supply_pin.cy()), - end=vector(self.width, supply_pin.cy())) - else: - if layer_props.wordline_driver.vertical_supply: - for name in ["vdd", "gnd"]: - supply_pins = self.wld_inst[0].get_pins(name) - for pin in supply_pins: - self.add_layout_pin_segment_center(text=name, - layer=pin.layer, - start=pin.bc(), - end=vector(pin.cx(), self.height)) - else: - # Find the x offsets for where the vias/pins should be placed - xoffset_list = [self.wld_inst[0].rx()] - for num in range(self.rows): - # this will result in duplicate polygons for rails, but who cares - - # use the inverter offset even though it will be the and's too - (gate_offset, y_dir) = self.get_gate_offset(0, - self.wl_driver.height, - num) - # Route both supplies - for name in ["vdd", "gnd"]: - supply_pin = self.wld_inst[num].get_pin(name) - - # Add pins in two locations - for xoffset in xoffset_list: - pin_pos = vector(xoffset, supply_pin.cy()) - self.copy_power_pin(supply_pin, loc=pin_pos) + for inst in self.wld_inst: + for pin in inst.get_pins("vdd"): + self.add_power_pin("vdd", pin.rc()) + #self.copy_layout_pin(inst, "vdd") + self.copy_layout_pin(inst, "gnd") def create_drivers(self): diff --git a/compiler/options.py b/compiler/options.py index ebf716b0..c7c062ba 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -195,4 +195,4 @@ class options(optparse.Values): write_mask_and_array = "write_mask_and_array" # Non-public options - experimental_power = False + experimental_power = True diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 110b8330..a410e04a 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -349,8 +349,6 @@ class sram_1bank(sram_base): 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 - # The power rings are 4 tracks wide with 2 tracks spacing, so space it - # 11 tracks out bbox = self.get_bbox(side="ring", margin=11*rt.track_width) self.route_escape_pins(bbox) From 64f2f9066417f281cbad33d2f522c86bfab11b52 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 19 Apr 2022 08:50:11 -0700 Subject: [PATCH 17/44] Rework replica_bitcell_array supplies Uses layer and direction preferences in tech file. Places straps on left/right or top/bottom. --- compiler/base/custom_cell_properties.py | 10 ++ compiler/base/hierarchy_layout.py | 17 +- compiler/modules/col_cap_array.py | 2 +- compiler/modules/dff_buf_array.py | 9 + compiler/modules/replica_bitcell_array.py | 201 +++++++++++++++++----- compiler/modules/row_cap_array.py | 7 +- compiler/openram.py | 3 +- compiler/tests/configs/config.py | 2 + technology/freepdk45/tech/tech.py | 1 - technology/scn4m_subm/tech/tech.py | 2 + technology/sky130/tech/tech.py | 17 ++ 11 files changed, 210 insertions(+), 61 deletions(-) diff --git a/compiler/base/custom_cell_properties.py b/compiler/base/custom_cell_properties.py index b2697472..0102bf24 100644 --- a/compiler/base/custom_cell_properties.py +++ b/compiler/base/custom_cell_properties.py @@ -156,6 +156,16 @@ class bitcell(cell): self.storage_nets = storage_nets + self.wl_layer = "m1" + self.wl_dir = "H" + self.bl_layer = "m2" + self.bl_dir = "V" + + self.vdd_layer = "m1" + self.vdd_dir = "H" + self.gnd_layer = "m1" + self.gnd_dir = "H" + class cell_properties(): """ diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 6fca63c7..a37ccb2b 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -673,9 +673,9 @@ class layout(): bot_pos = vector(x, bot_y) if num_pins==2: - self.add_layout_pin_rect_ends(name=name, - layer=pin_layer, - start=top_pos, + self.add_layout_pin_rect_ends(name=name, + layer=pin_layer, + start=top_pos, end=bot_pos, width=via_width) else: @@ -689,10 +689,10 @@ class layout(): def add_layout_pin_rect_ends(self, name, layer, start, end, width=None): # This adds pins on the end connected by a segment - top_rect = self.add_layout_pin_rect_center(text=name, + top_rect = self.add_layout_pin_rect_center(text=name, layer=layer, offset=start) - bot_rect = self.add_layout_pin_rect_center(text=name, + bot_rect = self.add_layout_pin_rect_center(text=name, layer=layer, offset=end) # This is made to not overlap with the pin above @@ -773,9 +773,9 @@ class layout(): right_pos = vector(right_x, y) if num_pins==2: - self.add_layout_pin_rect_ends(name=name, - layer=pin_layer, - start=left_pos, + self.add_layout_pin_rect_ends(name=name, + layer=pin_layer, + start=left_pos, end=right_pos, width=via_height) else: @@ -812,7 +812,6 @@ class layout(): end=end_pin.bc()) else: debug.error("Cannot have a point pin.", -1) - def add_layout_pin_segment_center(self, text, layer, start, end, width=None): """ diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index 438fd34a..27194a89 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -99,4 +99,4 @@ class col_cap_array(bitcell_base_array): inst = self.cell_inst[row, col] for pin_name in ["vdd", "gnd"]: for pin in inst.get_pins(pin_name): - self.copy_power_pin(pin) + self.copy_layout_pin(inst, pin_name) diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 62a7cfb6..70209767 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -145,6 +145,15 @@ class dff_buf_array(design.design): return dout_bar_name def route_supplies(self): + for row in range(self.rows): + vdd0_pin=self.dff_insts[row, 0].get_pin("vdd") + vddn_pin=self.dff_insts[row, self.columns - 1].get_pin("vdd") + self.add_path(vdd0_pin.layer, [vdd0_pin.lc(), vddn_pin.rc()], width=vdd0_pin.height()) + + gnd0_pin=self.dff_insts[row, 0].get_pin("gnd") + gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd") + self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height()) + if OPTS.experimental_power and self.rows > 1: # Vertical straps on ends if multiple rows left_dff_insts = [self.dff_insts[x, 0] for x in range(self.rows)] diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 5a7a046f..e1a3cbb8 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -6,7 +6,8 @@ import debug from bitcell_base_array import bitcell_base_array -from tech import drc, spice, cell_properties +from tech import drc, spice, preferred_directions +from tech import cell_properties as props from vector import vector from globals import OPTS from sram_factory import factory @@ -309,7 +310,7 @@ class replica_bitcell_array(bitcell_base_array): # row-based or column based power and ground lines. self.vertical_pitch = getattr(self, "{}_pitch".format(self.supply_stack[0])) self.horizontal_pitch = getattr(self, "{}_pitch".format(self.supply_stack[2])) - self.unused_offset = vector(2 * self.horizontal_pitch, 2 * self.vertical_pitch) + self.unused_offset = vector(0.25, 0.25) # This is a bitcell x bitcell offset to scale self.bitcell_offset = vector(self.cell.width, self.cell.height) @@ -461,38 +462,67 @@ class replica_bitcell_array(bitcell_base_array): def route_supplies(self): + bitcell = getattr(props, "bitcell_{}port".format(OPTS.num_ports)) + + wl_layer = bitcell.wl_layer + wl_dir = bitcell.wl_dir + + bl_layer = bitcell.bl_layer + bl_dir = bitcell.bl_dir + + vdd_layer = bitcell.vdd_layer + vdd_dir = bitcell.vdd_dir + + gnd_layer = bitcell.gnd_layer + gnd_dir = bitcell.gnd_dir + # vdd/gnd are only connected in the perimeter cells - # replica column should only have a vdd/gnd in the dummy cell on top/bottom supply_insts = self.dummy_col_insts + self.dummy_row_insts - vdd_leftx = 0 - vdd_rightx = 0 - gnd_leftx = 0 - gnd_rightx = 0 + # For the wordlines + top_bot_mult = 1 + left_right_mult = 1 + + vdd_locs = [] + gnd_locs = [] + # There are always vertical pins for the WLs on the left/right if we have unused wordlines + self.left_gnd_locs = self.route_side_pin("gnd", "left", left_right_mult) + self.right_gnd_locs = self.route_side_pin("gnd","right", left_right_mult) + left_right_mult = 3 + + if gnd_dir == "V": + self.top_gnd_locs = self.route_side_pin("gnd", "top", top_bot_mult) + self.bot_gnd_locs = self.route_side_pin("gnd", "bot", top_bot_mult) + top_bot_mult = 3 + + if vdd_dir == "V": + self.top_vdd_locs = self.route_side_pin("vdd", "top", top_bot_mult) + self.bot_vdd_locs = self.route_side_pin("vdd", "bot", top_bot_mult) + elif vdd_dir == "H": + self.left_vdd_locs = self.route_side_pin("vdd", "left", left_right_mult) + self.right_vdd_locs = self.route_side_pin("vdd", "right", left_right_mult) + else: + debug.error("Invalid vdd direction {}".format(vdd_dir), -1) + for inst in supply_insts: for pin in inst.get_pins("vdd"): - (vdd_leftx, vdd_rightx) = self.connect_pin(pin, 2.5) + if vdd_dir == "V": + self.connect_side_pin(pin, "top", self.top_vdd_locs[0].y) + self.connect_side_pin(pin, "bot", self.bot_vdd_locs[0].y) + elif vdd_dir == "H": + self.connect_side_pin(pin, "left", self.left_vdd_locs[0].x) + self.connect_side_pin(pin, "right", self.right_vdd_locs[0].x) + + + for inst in supply_insts: for pin in inst.get_pins("gnd"): - (gnd_leftx, gnd_rightx) = self.connect_pin(pin) - - - self.add_layout_end_pin_segment_center(text="vdd", - layer=self.supply_stack[2], - start=vector(vdd_leftx, 0), - end=vector(vdd_leftx, self.height)) - self.add_layout_end_pin_segment_center(text="vdd", - layer=self.supply_stack[2], - start=vector(vdd_rightx, 0), - end=vector(vdd_rightx, self.height)) - self.add_layout_end_pin_segment_center(text="gnd", - layer=self.supply_stack[2], - start=vector(gnd_leftx, 0), - end=vector(gnd_leftx, self.height)) - self.add_layout_end_pin_segment_center(text="gnd", - layer=self.supply_stack[2], - start=vector(gnd_rightx, 0), - end=vector(gnd_rightx, self.height)) + if gnd_dir == "V": + self.connect_side_pin(pin, "top", self.top_gnd_locs[0].y) + self.connect_side_pin(pin, "bot", self.bot_gnd_locs[0].y) + elif gnd_dir == "H": + self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x) + self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x) def route_unused_wordlines(self): @@ -501,41 +531,120 @@ class replica_bitcell_array(bitcell_base_array): for inst in self.dummy_row_insts: for wl_name in self.col_cap_top.get_wordline_names(): pin = inst.get_pin(wl_name) - self.connect_pin(pin) + self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x) + self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x) # Ground the unused replica wordlines for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): if wl_name in self.gnd_wordline_names: pin = inst.get_pin(pin_name) - self.connect_pin(pin) + self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x) + self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x) + def route_side_pin(self, name, side, offset_multiple=1): + """ + Routes a vertical or horizontal pin on the side of the bbox. + The multiple specifies how many track offsets to be away from the side assuming + (0,0) (self.width, self.height) + """ + if side in ["left", "right"]: + return self.route_vertical_side_pin(name, side, offset_multiple) + elif side in ["top", "bottom", "bot"]: + return self.route_horizontal_side_pin(name, side, offset_multiple) + else: + debug.error("Invalid side {}".format(side), -1) - def connect_pin(self, pin, offset_multiple=1): + def route_vertical_side_pin(self, name, side, offset_multiple=1): + """ + Routes a vertical pin on the side of the bbox. + """ + if side == "left": + bot_loc = vector(-offset_multiple * self.vertical_pitch, 0) + top_loc = vector(-offset_multiple * self.vertical_pitch, self.height) + elif side == "right": + bot_loc = vector(self.width + offset_multiple * self.vertical_pitch, 0) + top_loc = vector(self.width + offset_multiple * self.vertical_pitch, self.height) - pin_layer = pin.layer + layer = self.supply_stack[2] + self.add_path(layer, [bot_loc, top_loc]) - left_pin_loc = vector(self.dummy_col_insts[0].lx(), pin.cy()) - right_pin_loc = vector(self.dummy_col_insts[1].rx(), pin.cy()) + self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=top_loc) + self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=bot_loc) + + return (bot_loc, top_loc) + + def route_horizontal_side_pin(self, name, side, offset_multiple=1): + """ + Routes a horizontal pin on the side of the bbox. + """ + if side in ["bottom", "bot"]: + left_loc = vector(0, -offset_multiple * self.horizontal_pitch) + right_loc = vector(self.width, -offset_multiple * self.horizontal_pitch) + elif side == "top": + left_loc = vector(0, self.height + offset_multiple * self.horizontal_pitch) + right_loc = vector(self.width, self.height + offset_multiple * self.horizontal_pitch) + + layer = self.supply_stack[0] + self.add_path(layer, [left_loc, right_loc]) + + self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=left_loc) + self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=right_loc) + + return (left_loc, right_loc) + + def connect_side_pin(self, pin, side, offset): + """ + Used to connect horizontal layers of pins to the left/right straps + locs provides the offsets of the pin strip end points. + """ + if side in ["left", "right"]: + self.connect_vertical_side_pin(pin, side, offset) + elif side in ["top", "bottom", "bot"]: + self.connect_horizontal_side_pin(pin, side, offset) + else: + debug.error("Invalid side {}".format(side), -1) + + def connect_horizontal_side_pin(self, pin, side, yoffset): + """ + Used to connect vertical layers of pins to the top/bottom horizontal straps + """ + cell_loc = pin.center() + pin_loc = vector(cell_loc.x, yoffset) # Place the pins a track outside of the array - left_loc = left_pin_loc - vector(offset_multiple * self.horizontal_pitch, 0) - right_loc = right_pin_loc + vector(offset_multiple * self.horizontal_pitch, 0) - self.add_via_stack_center(offset=left_loc, - from_layer=pin_layer, - to_layer=self.supply_stack[2], - directions=("H", "H")) - self.add_via_stack_center(offset=right_loc, - from_layer=pin_layer, + self.add_via_stack_center(offset=pin_loc, + from_layer=pin.layer, + to_layer=self.supply_stack[0], + directions=("V", "V")) + + # Add a path to connect to the array + self.add_path(pin.layer, [cell_loc, pin_loc]) + + + def connect_vertical_side_pin(self, pin, side, xoffset): + """ + Used to connect vertical layers of pins to the top/bottom vertical straps + """ + cell_loc = pin.center() + pin_loc = vector(xoffset, cell_loc.y) + + # Place the pins a track outside of the array + self.add_via_stack_center(offset=pin_loc, + from_layer=pin.layer, to_layer=self.supply_stack[2], directions=("H", "H")) # Add a path to connect to the array - self.add_path(pin_layer, [left_loc, right_loc]) - - return (left_loc.x, right_loc.x) - - + self.add_path(pin.layer, [cell_loc, pin_loc]) def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index e7dc9816..8f092dc4 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -106,10 +106,13 @@ class row_cap_array(bitcell_base_array): width=self.width, height=wl_pin.height()) - # Add vdd/gnd via stacks for row in range(1, self.row_size - 1): for col in range(self.column_size): inst = self.cell_inst[row, col] for pin_name in ["vdd", "gnd"]: for pin in inst.get_pins(pin_name): - self.copy_power_pin(pin) + self.add_layout_pin(text=pin_name, + layer=pin.layer, + offset=pin.ll(), + width=pin.width(), + height=pin.height()) diff --git a/compiler/openram.py b/compiler/openram.py index 3a509f3b..8ce6ae1e 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -74,9 +74,8 @@ for path in output_files: from sram import sram -s = sram(name=OPTS.output_name, +s = sram(name=OPTS.output_name, sram_config=c) - # Output the files for the resulting SRAM s.save() diff --git a/compiler/tests/configs/config.py b/compiler/tests/configs/config.py index 9e35f558..b56c63db 100644 --- a/compiler/tests/configs/config.py +++ b/compiler/tests/configs/config.py @@ -14,4 +14,6 @@ tech_name = OPTS.tech_name nominal_corner_only = True check_lvsdrc = True +route_supplies = False + output_name = "sram" diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index d351ae36..7c1ac84b 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -31,7 +31,6 @@ tech_modules = module_type() # Custom cell properties ################################################### cell_properties = cell_properties() -cell_properties.bitcell_power_pin_directions = ("V", "V") ################################################### # Custom cell properties diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index a8c44996..525736a8 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -30,6 +30,8 @@ tech_modules = module_type() # Custom cell properties ################################################### cell_properties = cell_properties() +cell_properties.bitcell_1port.gnd_layer = "m2" +cell_properties.bitcell_1port.gnd_dir = "V" ################################################### # Custom cell properties diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index e81fe476..72250afc 100644 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -86,6 +86,13 @@ cell_properties.bitcell_1port.port_map = {'bl': 'BL', 'vpb': 'VPB', 'gnd': 'VGND'} +cell_properties.bitcell_1port.wl_layer = "m2" +cell_properties.bitcell_1port.bl_layer = "m1" +cell_properties.bitcell_1port.vdd_layer = "m1" +cell_properties.bitcell_1port.vdd_dir = "V" +cell_properties.bitcell_1port.gnd_layer = "m2" +cell_properties.bitcell_1port.gnd_dir = "H" + cell_properties.bitcell_2port.mirror.x = True cell_properties.bitcell_2port.mirror.y = True cell_properties.bitcell_2port.end_caps = True @@ -98,6 +105,16 @@ cell_properties.bitcell_2port.port_map = {'bl0': 'BL0', 'wl1': 'WL1', 'vdd': 'VDD', 'gnd': 'GND'} +cell_properties.bitcell_1port.wl_layer = "m2" +cell_properties.bitcell_1port.vdd_layer = "m2" +cell_properties.bitcell_1port.vdd_dir = "H" +cell_properties.bitcell_1port.gnd_layer = "m2" +cell_properties.bitcell_1port.gnd_dir = "H" +cell_properties.bitcell_2port.wl_layer = "m2" +cell_properties.bitcell_2port.vdd_layer = "m1" +cell_properties.bitcell_2port.vdd_dir = "H" +cell_properties.bitcell_2port.gnd_layer = "m2" +cell_properties.bitcell_2port.gnd_dir = "H" cell_properties.col_cap_1port_bitcell = cell(['br', 'vdd', 'gnd', 'bl', 'gate'], ['INPUT', 'POWER', 'GROUND', 'INPUT', 'INPUT'], From 7195d81736f37e55e5332f261788d69f028aa72c Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 19 Apr 2022 10:32:37 -0700 Subject: [PATCH 18/44] Adjust WL and GND for contacted via2 spacing. --- technology/freepdk45/gds_lib/cell_1rw.gds | Bin 18132 -> 18132 bytes .../freepdk45/gds_lib/dummy_cell_1rw.gds | Bin 18510 -> 18510 bytes .../freepdk45/gds_lib/replica_cell_1rw.gds | Bin 18512 -> 18512 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/technology/freepdk45/gds_lib/cell_1rw.gds b/technology/freepdk45/gds_lib/cell_1rw.gds index 905175076a286367b1dcee649395e8746ad5a589..e8116b9e86ed1433d85e8715f7c62abf9905b873 100644 GIT binary patch literal 18132 zcmeI4U#MkO9mm(+`|N%1xpR%Nnuf_iEf7rSA$v$g(Z)(>n4(EglR{@28HOe&uQc=0 z5C%QfLj*<8K=fcz8H7-XU`kF{GQ`kBG*5wo5l$FI;Y-}U-`{V2&;E1ITK8PformxU zhmZc&`tILazx7*x_S!RsCY0sqV5cch? z*4NkX!tWLmg{SfRh$uT5MpuSu`VfjYQ2r2!C;lUZ>T9Cx#=QQPsQ+Em8_lmjKj|NJ zE*PR6hNpvRAteN?3VSD#@$ z_yjHfA8sl8KwtI8FNEbM;UD9-V+~r2pD6qClRf_(y~ln0ijz8ypC~)n9lveVd)%y@ zh~vu59zRj`T>BX}lw16$YY;zC_JO|X#qS{6!2E~*e*9%8d;K5iWBj4FxWm7gXn$XS zY0LTB(R}1^0XhnHm?Wy+tIs!#Yy8X%AOuS_pfo*xQnt6 z^hG=Vh~MXtFQ0bq7~y$X=tSAUZvGGS=);d)A0BYMQ+DHJ)X&#`w$;{du>7yD-J<5&HvCF2*%FPnb!9rsVte|>#R`j6#F|MTjH{U7?DrT>icwD?WznenrJLqDzm z)W`Bf|FJeai#jKr%@}!vwMdkGbTXAc^Xf5n`{>_Ezr}CdKPh|K|Gav%{}-|U;70!{ z&-cH6YPaWgqOV?X|0H#z zds|W;%ai`MTVMY%_8*+ZKjXtVPm7=Ix%jjFtT(Oyvgh>7|M4y#|MBlZ^dHZ+@)`U8vlu^d)A;H6kfgfoujilV2A#8}oqu9^cm3_?k>7*;zrX*gyf^r_x5$yum1X}^0fb6{%PKb)AMiKZGZR{_8Z*0kbK|c9h4|L z*zNyD{d2Bgx(D(6dkA6^?O;-0d>8fqh8zHwad$HMPfOXsq@L$Pwv%MLO&=-yrtP1j z|J&g|{M+MKeAC-6$_^&`U)_iPBhIAaF3O&Z|GfI)_?0*M_>r@W-@N(~ImbAWj9bZE zij+MUKb~KB|3mWrr}%bD*$4NRz5J(+>((^A>|k>Iy8i8b9sVbb?aKMD{)zgiN3#9g ze?-|g)3cq^^s?vL-}P_rtMETzY*)@d*B#z}P1Yf-Kb*H*hh!hzfA!*Ty76>9&-1Z7 z8UL<-p0~}f7~7Te&-u@GPSydeU$*XmvTvrh_YOQqx(U&Kd+s=$KVAQ9Ki9trW4m(x z*?(K_LEihZ|Dx<*Hh!wd^Ah!cv_0 zZ09upvJcw7qqqMa#oWW5891=Xc+?wNG>2F7|KabBu%Hl3Ty#%|E{PaGfK${)N|C%AQ;Qia)uYwDt4;mej}c za?)x+K_0_H?sgLCeJ@(IY(D3~cSeFTETfLM>`vM9`kL+h1Nx12^MuYUbINW^^dIBH8#Tz~K$J-&hB{a%z%;+)QMy|Kr4f!L= zKG@|t`Qtj(lTMUW;X}g0{_dME#o+gP{kZ4zYOas3iy zPv_r4KVSTOy|>dO)_vw}`LUL=gNyu~n!k(nllzwT3GExA?CJTth~HM?7thUDKPRW` z?%B0-`oz5cD%Mi`eV9GXk$C#tih8tjqF(FG{gvL?73a0_H5fzkN8pPc%6?5+Z*L~3 zXN2C~P(<02daV7=(|;s%JU6l(M%j~k#0YO_Y`+mY`@$v4p3$@2^%-w}z3%OwQ1&zF z{xfdW8=+_Xs5i=<(X-vu8=+(SsW-}=)O+0OGm+5Qxm%PysYid$V!wDa5;ui*Ga$FU`o5O34{lhuRbL&(67dq^4)TV%dbOqOgB#|a|9SQ0 zDbKy~6ymra@q1I0-Pk>Q^!!uDFQP`gGf~fXD5LECFYK%B`o(AK=YH7+|H)4F^0%W$ zzN3C~Ao4tj`P+!HA6V$G#1awD{@WZs8y_17Te}c{r|g@JpXx~`%6@tE_5~N~j1lVq zzwp}nN6Ow`f3`V(|IhjBW7Hgo#JuC!bN(4+PtL!E-oEfEPKui-doq65x1RGnH6nlc zg_rqjME=I|?)tsR-^u?WW3hkK`+W{_AKm);O|5>e=QI2#JGt0@FW>&V<^AK0_WXsh a;y>3r@YN^OU&r|^Qg+bRk0qQTM&W-I1dW0K literal 18132 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 diff --git a/technology/freepdk45/gds_lib/dummy_cell_1rw.gds b/technology/freepdk45/gds_lib/dummy_cell_1rw.gds index 031ded2ba10f04b29c143651edf438f2d99b0470..92d2b130fb69c0fc2845042ee9df2bbf23a9fee7 100644 GIT binary patch literal 18510 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 literal 18510 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 Q#I^z3GQKBT!Dkl!3l(?w1poj5 diff --git a/technology/freepdk45/gds_lib/replica_cell_1rw.gds b/technology/freepdk45/gds_lib/replica_cell_1rw.gds index 706f0220c82b4785b5e1382a90b47a858b280e58..076a80496d5bd062c30cf7b077ef4f833e5535ab 100644 GIT binary patch literal 18512 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 From b1bb9151c41d5f2704e328d212dfe95537f3f906 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 2 May 2022 15:43:14 -0700 Subject: [PATCH 19/44] Reimplement off grid pins. Long pins aren't accessed on end pins anymore. Fix problem with multiple non-enclosed space causing blockages. Add partial pin offgrid enclosure algorithm. --- compiler/base/hierarchy_layout.py | 73 +++++------ compiler/base/pin_layout.py | 7 ++ compiler/modules/bank.py | 34 ++--- compiler/modules/control_logic.py | 42 ++++--- compiler/modules/delay_chain.py | 22 +--- compiler/modules/hierarchical_decoder.py | 12 +- compiler/modules/hierarchical_predecode.py | 2 +- compiler/modules/local_bitcell_array.py | 5 +- compiler/modules/port_address.py | 15 +-- compiler/modules/precharge_array.py | 2 +- compiler/modules/replica_bitcell_array.py | 49 +++++--- compiler/modules/wordline_driver_array.py | 11 +- compiler/router/pin_group.py | 6 +- compiler/router/router.py | 139 ++++++++++++++++++--- compiler/router/supply_tree_router.py | 6 +- compiler/sram/sram_base.py | 2 +- compiler/tests/configs/config.py | 2 +- 17 files changed, 274 insertions(+), 155 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index a37ccb2b..5c986d75 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -419,13 +419,6 @@ class layout(): pin.width(), pin.height()) - def copy_layout_pins(self, instance, prefix=""): - """ - Create a copied version of the layout pin at the current level. - You can optionally rename the pin to a new name. - """ - for pin_name in self.pin_map.keys(): - self.copy_layout_pin(instance, pin_name, prefix + pin_name) def connect_row_locs(self, from_layer, to_layer, locs, name=None, full=False): """ @@ -608,12 +601,11 @@ class layout(): - def route_vertical_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", num_pins=2, full_width=True): + def route_vertical_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", full_width=True): """ Route together all of the pins of a given name that vertically align. Uses local_insts if insts not specified. Uses center of pin by default, or right or left if specified. - num_pins specifies whether to add a single pin or multiple pins (equally spaced) TODO: Add equally spaced option for IR drop min, right now just 2 """ @@ -660,8 +652,10 @@ class layout(): if last_via: via_width=last_via.mod.second_layer_width + via_height=last_via.mod.second_layer_height else: via_width=None + via_height=0 if full_width: bot_y = 0 @@ -669,21 +663,19 @@ class layout(): else: bot_y = min([pin.by() for (inst,pin) in v]) top_y = max([pin.uy() for (inst,pin) in v]) - top_pos = vector(x, top_y) - bot_pos = vector(x, bot_y) + top_pos = vector(x, top_y + 0.5 * via_height) + bot_pos = vector(x, bot_y - 0.5 * via_height) - if num_pins==2: - self.add_layout_pin_rect_ends(name=name, - layer=pin_layer, - start=top_pos, - end=bot_pos, - width=via_width) - else: - self.add_layout_pin_segment_center(text=name, - layer=pin_layer, - start=top_pos, - end=bot_pos, - width=via_width) +# self.add_layout_pin_rect_ends(name=name, +# layer=pin_layer, +# start=top_pos, +# end=bot_pos, +# width=via_width) + self.add_layout_pin_segment_center(text=name, + layer=pin_layer, + start=top_pos, + end=bot_pos, + width=via_width) def add_layout_pin_rect_ends(self, name, layer, start, end, width=None): @@ -707,12 +699,13 @@ class layout(): start=bot_rect.rc(), end=top_rect.lc()) - def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", num_pins=2, full_width=True): + return (bot_rect, top_rect) + + def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", full_width=True): """ Route together all of the pins of a given name that horizontally align. Uses local_insts if insts not specified. Uses center of pin by default, or top or botom if specified. - num_pins specifies whether to add a single pin or multiple pins (equally spaced) TODO: Add equally spaced option for IR drop min, right now just 2 """ @@ -760,8 +753,10 @@ class layout(): if last_via: via_height=last_via.mod.second_layer_height + via_width=last_via.mod.second_layer_width else: via_height=None + via_width=0 if full_width: left_x = 0 @@ -769,22 +764,19 @@ class layout(): else: left_x = min([pin.lx() for (inst,pin) in v]) right_x = max([pin.rx() for (inst,pin) in v]) - left_pos = vector(left_x, y) - right_pos = vector(right_x, y) + left_pos = vector(left_x + 0.5 * via_width, y) + right_pos = vector(right_x + 0.5 * via_width, y) - if num_pins==2: - self.add_layout_pin_rect_ends(name=name, - layer=pin_layer, - start=left_pos, - end=right_pos, - width=via_height) - else: - # This adds a single big pin - self.add_layout_pin_segment_center(text=name, - layer=pin_layer, - start=left_pos, - end=right_pos, - width=via_height) +# self.add_layout_pin_rect_ends(name=name, +# layer=pin_layer, +# start=left_pos, +# end=right_pos, +# width=via_height) + self.add_layout_pin_segment_center(text=name, + layer=pin_layer, + start=left_pos, + end=right_pos, + width=via_height) def add_layout_end_pin_segment_center(self, text, layer, start, end): """ @@ -1123,7 +1115,6 @@ class layout(): Add a minimum area retcangle at the given point. Either width or height should be fixed. """ - min_area = drc("minarea_{}".format(layer)) if min_area == 0: return diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index cc13e049..7ba6cdca 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -174,6 +174,10 @@ class pin_layout: def intersection(self, other): """ Check if a shape overlaps with a rectangle """ + + if not self.overlaps(other): + return None + (ll, ur) = self.rect (oll, our) = other.rect @@ -182,6 +186,9 @@ class pin_layout: min_y = max(ll.y, oll.y) max_y = min(ur.y, our.y) + if max_x - min_x == 0 or max_y - min_y == 0: + return None + return [vector(min_x, min_y), vector(max_x, max_y)] def xoverlaps(self, other): diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index bd3fc0a4..718f8f62 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -229,7 +229,7 @@ class bank(design.design): # UPPER LEFT QUADRANT # To the left of the bitcell array above the predecoders and control logic - x_offset = self.m2_gap + self.port_address[port].width + x_offset = self.decoder_gap + self.port_address[port].width self.port_address_offsets[port] = vector(-x_offset, self.main_bitcell_array_bottom) @@ -272,7 +272,7 @@ class bank(design.design): # LOWER RIGHT QUADRANT # To the right of the bitcell array - x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap + x_offset = self.bitcell_array_right + self.port_address[port].width + self.decoder_gap self.port_address_offsets[port] = vector(x_offset, self.main_bitcell_array_bottom) @@ -364,9 +364,9 @@ class bank(design.design): self.num_col_addr_lines = 0 self.col_addr_bus_width = self.m2_pitch * self.num_col_addr_lines - # A space for wells or jogging m2 - self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"), - 3 * self.m2_pitch, + # Gap between decoder and array + self.decoder_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"), + 2 * self.m2_pitch, drc("nwell_to_nwell")) def add_modules(self): @@ -400,11 +400,10 @@ class bank(design.design): self.port_data = [] self.bit_offsets = self.get_column_offsets() for port in self.all_ports: - temp_pre = factory.create(module_type="port_data", - sram_config=self.sram_config, - port=port, - bit_offsets=self.bit_offsets) - self.port_data.append(temp_pre) + self.port_data.append(factory.create(module_type="port_data", + sram_config=self.sram_config, + port=port, + bit_offsets=self.bit_offsets)) if(self.num_banks > 1): self.bank_select = factory.create(module_type="bank_select") @@ -606,15 +605,22 @@ class bank(design.design): def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ + # Copy only the power pins already on the power layer # (this won't add vias to internal bitcell pins, for example) - for inst in self.insts: - self.copy_power_pins(inst, "vdd", add_vias=False) - self.copy_power_pins(inst, "gnd", add_vias=False) + + # This avoids getting copy errors on vias and other instances + all_insts = [self.bitcell_array_inst] + self.port_address_inst + self.port_data_inst + if hasattr(self, "column_decoder_inst"): + all_insts += self.column_decoder_inst + + for inst in all_insts: + self.copy_layout_pin(inst, "vdd") + self.copy_layout_pin(inst, "gnd") if 'vpb' in self.bitcell_array_inst.mod.pins and 'vnb' in self.bitcell_array_inst.mod.pins: for pin_name, supply_name in zip(['vnb','vpb'],['gnd','vdd']): - self.copy_power_pins(self.bitcell_array_inst, pin_name, new_name=supply_name) + self.copy_layout_pin(self.bitcell_array_inst, pin_name, new_name=supply_name) # If we use the pinvbuf as the decoder, we need to add power pins. # Other decoders already have them. diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index df7a8241..79ce3e30 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -715,12 +715,17 @@ class control_logic(design.design): pin_layer = self.dff.get_pin("vdd").layer supply_layer = self.supply_stack[2] + + # FIXME: We should be able to replace this with route_vertical_pins instead + # but we may have to make the logic gates a separate module so that they + # have row pins of the same width max_row_x_loc = max([inst.rx() for inst in self.row_end_inst]) min_row_x_loc = self.control_x_offset vdd_pin_locs = [] gnd_pin_locs = [] + last_via = None for inst in self.row_end_inst: pins = inst.get_pins("vdd") for pin in pins: @@ -728,10 +733,10 @@ class control_logic(design.design): row_loc = pin.rc() pin_loc = vector(max_row_x_loc, pin.rc().y) vdd_pin_locs.append(pin_loc) - self.add_via_stack_center(from_layer=pin_layer, - to_layer=supply_layer, - offset=pin_loc, - min_area=True) + last_via = self.add_via_stack_center(from_layer=pin_layer, + to_layer=supply_layer, + offset=pin_loc, + min_area=True) self.add_path(pin_layer, [row_loc, pin_loc]) pins = inst.get_pins("gnd") @@ -740,29 +745,38 @@ class control_logic(design.design): row_loc = pin.rc() pin_loc = vector(min_row_x_loc, pin.rc().y) gnd_pin_locs.append(pin_loc) - self.add_via_stack_center(from_layer=pin_layer, - to_layer=supply_layer, - offset=pin_loc, - min_area=True) + last_via = self.add_via_stack_center(from_layer=pin_layer, + to_layer=supply_layer, + offset=pin_loc, + min_area=True) self.add_path(pin_layer, [row_loc, pin_loc]) + + if last_via: + via_height=last_via.mod.second_layer_height + via_width=last_via.mod.second_layer_width + else: + via_height=None + via_width=0 min_y = min([x.y for x in vdd_pin_locs]) max_y = max([x.y for x in vdd_pin_locs]) - bot_pos = vector(max_row_x_loc, min_y) - top_pos = vector(max_row_x_loc, max_y) + bot_pos = vector(max_row_x_loc, min_y - 0.5 * via_height) + top_pos = vector(max_row_x_loc, max_y + 0.5 * via_height) self.add_layout_pin_segment_center(text="vdd", layer=supply_layer, start=bot_pos, - end=top_pos) + end=top_pos, + width=via_width) min_y = min([x.y for x in gnd_pin_locs]) max_y = max([x.y for x in gnd_pin_locs]) - bot_pos = vector(min_row_x_loc, min_y) - top_pos = vector(min_row_x_loc, max_y) + bot_pos = vector(min_row_x_loc, min_y - 0.5 * via_height) + top_pos = vector(min_row_x_loc, max_y + 0.5 * via_height) self.add_layout_pin_segment_center(text="gnd", layer=supply_layer, start=bot_pos, - end=top_pos) + end=top_pos, + width=via_width) self.copy_layout_pin(self.delay_inst, "gnd") self.copy_layout_pin(self.delay_inst, "vdd") diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index af446bbd..fe039411 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -172,25 +172,9 @@ class delay_chain(design.design): self.add_path("m2", [z_pin.center(), mid1_point, mid2_point, next_a_pin.center()]) def route_supplies(self): - # Add power and ground to all the cells except: - # the fanout driver, the right-most load - # The routing to connect the loads is over the first and last cells - # We have an even number of drivers and must only do every other - # supply rail - if True or OPTS.experimental_power: - left_load_insts = [self.load_inst_map[x][0] for x in self.driver_inst_list] - right_load_insts = [self.load_inst_map[x][-1] for x in self.driver_inst_list] - self.route_vertical_pins("vdd", left_load_insts, xside="lx") - self.route_vertical_pins("gnd", right_load_insts, xside="rx") - else: - for inst in self.driver_inst_list: - load_list = self.load_inst_map[inst] - for pin_name in ["vdd", "gnd"]: - pin = load_list[0].get_pin(pin_name) - self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0)) - - pin = load_list[-2].get_pin(pin_name) - self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0)) + self.route_vertical_pins("vdd", self.driver_inst_list, xside="lx") + right_load_insts = [self.load_inst_map[x][-1] for x in self.driver_inst_list] + self.route_vertical_pins("gnd", right_load_insts, xside="rx") def add_layout_pins(self): diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 2b173969..40c2a93a 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -595,14 +595,12 @@ class hierarchical_decoder(design.design): # Leave these to route in the port_address all_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst for inst in all_insts: - self.copy_layout_pin(inst, "vdd") - self.copy_layout_pin(inst, "gnd") + self.copy_layout_pin(inst, "vdd") + self.copy_layout_pin(inst, "gnd") + + self.route_vertical_pins("vdd", self.and_inst, xside="rx",) + self.route_vertical_pins("gnd", self.and_inst, xside="lx",) - for inst in self.and_inst: - for pin in inst.get_pins("vdd"): - self.add_power_pin("vdd", pin.rc()) - for pin in inst.get_pins("gnd"): - self.add_power_pin("gnd", pin.lc()) def route_predecode_bus_outputs(self, rail_name, pin, row): diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 0d7f31a3..8ae3a08d 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -395,7 +395,7 @@ class hierarchical_predecode(design.design): height=top_pin.uy() - self.bus_pitch) # This adds power vias at the top of each cell # (except the last to keep them inside the boundary) - for i in self.inv_inst[:-1:2] + self.and_inst[:-1:2]: + for i in [self.inv_inst[0], self.inv_inst[-2], self.and_inst[0], self.and_inst[-2]]: pins = i.get_pins(n) for pin in pins: self.copy_power_pin(pin, loc=pin.uc()) diff --git a/compiler/modules/local_bitcell_array.py b/compiler/modules/local_bitcell_array.py index 49e8ed76..0d3cfd12 100644 --- a/compiler/modules/local_bitcell_array.py +++ b/compiler/modules/local_bitcell_array.py @@ -162,14 +162,15 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): # FIXME: Replace this with a tech specific paramter driver_to_array_spacing = 3 * self.m3_pitch - self.wl_insts[0].place(vector(0, self.cell.height)) + self.wl_insts[0].place(vector(0, + self.bitcell_array.get_replica_bottom() + self.cell.height)) self.bitcell_array_inst.place(vector(self.wl_insts[0].rx() + driver_to_array_spacing, 0)) if len(self.all_ports) > 1: self.wl_insts[1].place(vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing, - 2 * self.cell.height + self.wl_array.height), + self.bitcell_array.get_replica_top() + self.cell.height), mirror="XY") self.height = self.bitcell_array.height diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 2757a86f..a96a556e 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -76,22 +76,19 @@ class port_address(design.design): def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ - self.route_vertical_pins("vdd", [self.row_decoder_inst]) - self.route_vertical_pins("gnd", [self.row_decoder_inst]) - self.route_vertical_pins("vdd", [self.wordline_driver_array_inst]) if layer_props.wordline_driver.vertical_supply: - self.route_vertical_pins("gnd", [self.wordline_driver_array_inst]) self.copy_layout_pin(self.rbl_driver_inst, "vdd") else: rbl_pos = self.rbl_driver_inst.get_pin("vdd").rc() self.add_power_pin("vdd", rbl_pos) self.add_path("m4", [rbl_pos, self.wordline_driver_array_inst.get_pins("vdd")[0].rc()]) - - vdd_pins = self.row_decoder_inst.get_pins("vdd") + self.wordline_driver_array_inst.get_pins("vdd") - self.connect_row_pins(self.route_layer, vdd_pins) - gnd_pins = self.row_decoder_inst.get_pins("gnd") + self.wordline_driver_array_inst.get_pins("gnd") - self.connect_row_pins(self.route_layer, gnd_pins) + self.copy_layout_pin(self.wordline_driver_array_inst, "vdd") + self.copy_layout_pin(self.wordline_driver_array_inst, "gnd") + + self.copy_layout_pin(self.row_decoder_inst, "vdd") + self.copy_layout_pin(self.row_decoder_inst, "gnd") + # Also connect the B input of the RBL and_dec to vdd if OPTS.local_array_size == 0: rbl_b_pin = self.rbl_driver_inst.get_pin("B") diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index c820dfc1..372a5629 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -83,7 +83,7 @@ class precharge_array(design.design): def add_layout_pins(self): en_pin = self.pc_cell.get_pin("en_bar") - self.route_horizontal_pins("en_bar", layer=self.en_bar_layer, num_pins=1) + self.route_horizontal_pins("en_bar", layer=self.en_bar_layer) for inst in self.local_insts: self.add_via_stack_center(from_layer=en_pin.layer, to_layer=self.en_bar_layer, diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index e1a3cbb8..785bb62b 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -331,6 +331,7 @@ class replica_bitcell_array(bitcell_base_array): self.translate_all(array_offset.scale(-1, -1)) # Add extra width on the left and right for the unused WLs + self.width = self.dummy_col_insts[1].rx() + self.unused_offset.x self.height = self.dummy_row_insts[1].uy() @@ -340,6 +341,12 @@ class replica_bitcell_array(bitcell_base_array): self.route_unused_wordlines() + lower_left = self.find_lowest_coords() + upper_right = self.find_highest_coords() + self.width = upper_right.x - lower_left.x + self.height = upper_right.y - lower_left.y + self.translate_all(lower_left) + self.add_boundary() self.DRC_LVS() @@ -356,6 +363,18 @@ class replica_bitcell_array(bitcell_base_array): def get_main_array_right(self): return self.bitcell_array_inst.rx() + def get_replica_top(self): + return max([x.uy() for x in self.replica_col_insts if x] + [self.get_main_array_top()]) + + def get_replica_bottom(self): + return min([x.by() for x in self.replica_col_insts if x] + [self.get_main_array_bottom()]) + + def get_replica_left(self): + return min([x.lx() for x in self.replica_col_insts if x] + [self.get_main_array_left()]) + + def get_replica_right(self): + return min([x.rx() for x in self.replica_col_insts if x] + [self.get_main_array_right()]) + def get_column_offsets(self): """ Return an array of the x offsets of all the regular bits @@ -567,14 +586,15 @@ class replica_bitcell_array(bitcell_base_array): top_loc = vector(self.width + offset_multiple * self.vertical_pitch, self.height) layer = self.supply_stack[2] - self.add_path(layer, [bot_loc, top_loc]) - self.add_layout_pin_rect_center(text=name, - layer=layer, - offset=top_loc) - self.add_layout_pin_rect_center(text=name, - layer=layer, - offset=bot_loc) +# self.add_layout_pin_rect_ends(text=name, +# layer=layer, +# start=bot_loc, +# end=top_loc) + self.add_layout_pin_segment_center(text=name, + layer=layer, + start=bot_loc, + end=top_loc) return (bot_loc, top_loc) @@ -590,14 +610,15 @@ class replica_bitcell_array(bitcell_base_array): right_loc = vector(self.width, self.height + offset_multiple * self.horizontal_pitch) layer = self.supply_stack[0] - self.add_path(layer, [left_loc, right_loc]) - self.add_layout_pin_rect_center(text=name, - layer=layer, - offset=left_loc) - self.add_layout_pin_rect_center(text=name, - layer=layer, - offset=right_loc) +# self.add_layout_pin_rect_ends(text=name, +# layer=layer, +# start=left_loc, +# end=right_loc) + self.add_layout_pin_segment_center(text=name, + layer=layer, + start=left_loc, + end=right_loc) return (left_loc, right_loc) diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index 276194ee..63c39a7c 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -77,11 +77,12 @@ class wordline_driver_array(design.design): Add vertical power rails. """ - for inst in self.wld_inst: - for pin in inst.get_pins("vdd"): - self.add_power_pin("vdd", pin.rc()) - #self.copy_layout_pin(inst, "vdd") - self.copy_layout_pin(inst, "gnd") + if layer_props.wordline_driver.vertical_supply: + self.route_vertical_pins("vdd", self.wld_inst) + self.route_vertical_pins("gnd", self.wld_inst) + else: + self.route_vertical_pins("vdd", self.wld_inst, xside="rx",) + self.route_vertical_pins("gnd", self.wld_inst, xside="lx",) def create_drivers(self): diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index 5e6d6f89..d67200d5 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -158,7 +158,7 @@ class pin_group: # Now add the right name for pin in new_pin_list: pin.name = self.name - + debug.check(len(new_pin_list) > 0, "Did not find any enclosures.") @@ -424,7 +424,7 @@ class pin_group: debug.check(len(self.grids) > 0, "Cannot seed an grid empty set.") common_blockages = self.router.get_blocked_grids() & self.grids - + # Start with the ll and make the widest row row = [ll] # Move in dir1 while we can @@ -641,7 +641,7 @@ class pin_group: # way than blockages. blockages = sufficient | insufficient | blockage_in_tracks self.blockages.update(blockages) - + # If we have a blockage, we must remove the grids # Remember, this excludes the pin blockages already blocked_grids = self.router.get_blocked_grids() diff --git a/compiler/router/router.py b/compiler/router/router.py index fd00d98d..94a4c432 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -229,9 +229,9 @@ class router(router_tech): # Enclose the continguous grid units in a metal # rectangle to fix some DRCs - start_time = datetime.now() - self.enclose_pins() - print_time("Enclosing pins", datetime.now(), start_time, 4) + #start_time = datetime.now() + #self.enclose_pins() + #print_time("Enclosing pins", datetime.now(), start_time, 4) # MRG: Removing this code for now. The later compute enclosure code # assumes that all pins are touching and this may produce sets of pins @@ -670,6 +670,25 @@ class router(router_tech): return set([best_coord]) + def divide_pin_to_tracks(self, pin, tracks): + """ + Return a list of pin shape parts that are in the tracks. + """ + overlap_pins = [] + for track in tracks: + track_pin = self.convert_track_to_shape_pin(track) + overlap_rect = track_pin.intersection(pin) + if not overlap_rect: + continue + else: + overlap_pin = pin_layout(pin.name, + overlap_rect, + pin.layer) + overlap_pins.append(overlap_pin) + + return overlap_pins + + def convert_pin_coord_to_tracks(self, pin, coord): """ Return all tracks that an inflated pin overlaps @@ -893,6 +912,8 @@ class router(router_tech): This will mark the grids for all pin components as a source. Marking as source or target also clears blockage status. """ + self.source_name = pin_name + self.source_components = [] for i in range(self.num_pin_components(pin_name)): self.add_pin_component_source(pin_name, i) @@ -904,6 +925,8 @@ class router(router_tech): This will mark the grids for all pin components as a target. Marking as source or target also clears blockage status. """ + self.target_name = pin_name + self.target_components = [] for i in range(self.num_pin_components(pin_name)): self.add_pin_component_target(pin_name, i) @@ -1009,6 +1032,8 @@ class router(router_tech): """ This will mark all the cells on the perimeter of the original layout as a target. """ + self.target_name = "" + self.target_components = [] self.rg.add_perimeter_target(side=side) def num_pin_components(self, pin_name): @@ -1017,6 +1042,15 @@ class router(router_tech): """ return len(self.pin_groups[pin_name]) + def set_pin_component_source(self, pin_name, index): + """ + Add the pin component but also set it as the exclusive one. + Used by supply routing with multiple components. + """ + self.source_name = pin_name + self.source_components = [] + self.add_pin_component_source(pin_name, index) + def add_pin_component_source(self, pin_name, index): """ This will mark only the pin tracks @@ -1026,6 +1060,7 @@ class router(router_tech): debug.check(index 1: + self.cell.add_route(layers=self.layers, + coordinates=abs_path, + layer_widths=self.layer_widths) else: - # convert the path back to absolute units from tracks - # This assumes 1-track wide again - abs_path = [self.convert_point_to_units(x[0]) for x in path] - # Otherwise, add the route which includes enclosures - if len(self.layers) > 1: - self.cell.add_route(layers=self.layers, - coordinates=abs_path, - layer_widths=self.layer_widths) - else: - self.cell.add_path(layer=self.layers[0], - coordinates=abs_path, - width=self.layer_widths[0]) + self.cell.add_path(layer=self.layers[0], + coordinates=abs_path, + width=self.layer_widths[0]) + + def create_route_connector(self, path_tracks, pin_name, components): + """ + Find a rectangle to connect the track and the off-grid pin of a component. + """ + + if len(path_tracks) == 0 or len(components) == 0: + return + + # Convert the off-grid pin into parts in each routing grid + offgrid_pin_parts = [] + for component in components: + pg = self.pin_groups[pin_name][component] + for pin in pg.pins: + partial_pin_parts = self.divide_pin_to_tracks(pin, pg.grids) + offgrid_pin_parts.extend(partial_pin_parts) + + # Find the track pin + track_pins = [self.convert_tracks_to_pin(x) for x in path_tracks] + + # Find closest part + closest_track_pin, closest_part_pin = self.find_closest_pin(track_pins, offgrid_pin_parts) + + # Find the bbox of the on-grid track and the off-grid pin part + closest_track_pin.bbox([closest_part_pin]) + + # Connect to off grid pin to track pin with closest shape + self.cell.add_rect(layer=closest_track_pin.layer, + offset=closest_track_pin.ll(), + width=closest_track_pin.width(), + height=closest_track_pin.height()) + + def find_closest_pin(self, first_list, second_list): + """ + Find the closest pin in the lists. Does a stupid O(n^2). + """ + min_dist = None + min_item = None + for pin1 in first_list: + for pin2 in second_list: + if pin1.layer != pin2.layer: + continue + new_dist = pin1.distance(pin2) + if not min_item or new_dist < min_dist: + min_item = (pin1, pin2) + min_dist = new_dist + + return min_item + def add_single_enclosure(self, track): """ @@ -1192,7 +1283,15 @@ class router(router_tech): self.paths.append(grid_utils.flatten_set(path)) self.add_route(path) + self.create_route_connector(path, + self.source_name, + self.source_components) + self.create_route_connector(path, + self.target_name, + self.target_components) self.path_blockages.append(self.paths[-1]) + #self.write_debug_gds("debug_route.gds", False) + #breakpoint() return True else: return False diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index 9450243d..6873999a 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -154,7 +154,7 @@ class supply_tree_router(router): print("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages)) self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False) - #self.write_debug_gds("final_tree_router_{}.gds".format(pin_name), False) + self.write_debug_gds("final_tree_router_{}.gds".format(pin_name), False) #return def route_signal(self, pin_name, src_idx, dest_idx): @@ -180,11 +180,11 @@ class supply_tree_router(router): # Add the single component of the pin as the source # which unmarks it as a blockage too - self.add_pin_component_source(pin_name, src_idx) + self.set_pin_component_source(pin_name, src_idx) # Marks all pin components except index as target # which unmarks it as a blockage too - self.add_pin_component_target(pin_name, dest_idx) + self.set_pin_component_target(pin_name, dest_idx) # Actually run the A* router if self.run_router(detour_scale=detour_scale): diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index f6782597..4bcc1cb3 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -213,7 +213,7 @@ class sram_base(design, verilog, lef): self.add_lvs_correspondence_points() - self.offset_all_coordinates() + #self.offset_all_coordinates() highest_coord = self.find_highest_coords() self.width = highest_coord[0] diff --git a/compiler/tests/configs/config.py b/compiler/tests/configs/config.py index b56c63db..44c3e774 100644 --- a/compiler/tests/configs/config.py +++ b/compiler/tests/configs/config.py @@ -14,6 +14,6 @@ tech_name = OPTS.tech_name nominal_corner_only = True check_lvsdrc = True -route_supplies = False +#route_supplies = False output_name = "sram" From 3e48991acb9e0e75c5e7143b6bbbb8a7890badc9 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 2 May 2022 16:07:05 -0700 Subject: [PATCH 20/44] Skip partial pins if they are too small to prevent DRC overlap errors. --- compiler/router/router.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/compiler/router/router.py b/compiler/router/router.py index 94a4c432..6e5d5b4e 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -674,16 +674,26 @@ class router(router_tech): """ Return a list of pin shape parts that are in the tracks. """ + # If pin is smaller than a track, just return it. + track_pin = self.convert_track_to_shape_pin(list(tracks)[0]) + if pin.width() < track_pin.width() and pin.height() < track_pin.height(): + return [pin] + overlap_pins = [] for track in tracks: track_pin = self.convert_track_to_shape_pin(track) overlap_rect = track_pin.intersection(pin) if not overlap_rect: continue + overlap_pin = pin_layout(pin.name, + overlap_rect, + pin.layer) + + # If pin is smaller than minwidth, in one dimension, skip it. + min_pin_width = drc("minwidth_{0}". format(pin.layer)) + if overlap_pin.width() < min_pin_width and overlap_pin.height() < min_pin_width: + continue else: - overlap_pin = pin_layout(pin.name, - overlap_rect, - pin.layer) overlap_pins.append(overlap_pin) return overlap_pins From 942ab8975440e290ae44b35731d0196c8ddf20dc Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 2 May 2022 16:42:04 -0700 Subject: [PATCH 21/44] Remove debug output. --- compiler/router/supply_tree_router.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index 6873999a..a018a129 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -154,7 +154,7 @@ class supply_tree_router(router): print("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages)) self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False) - self.write_debug_gds("final_tree_router_{}.gds".format(pin_name), False) + #self.write_debug_gds("final_tree_router_{}.gds".format(pin_name), False) #return def route_signal(self, pin_name, src_idx, dest_idx): From f8f3f16b1f975c5a5d254b2e5a2c40d071fd7573 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 2 May 2022 16:42:14 -0700 Subject: [PATCH 22/44] Move delay line supply strap for pin access. --- compiler/modules/delay_chain.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index fe039411..f90191ef 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -172,9 +172,11 @@ class delay_chain(design.design): self.add_path("m2", [z_pin.center(), mid1_point, mid2_point, next_a_pin.center()]) def route_supplies(self): - self.route_vertical_pins("vdd", self.driver_inst_list, xside="lx") + # These pins get routed in one cell from the left/right + # because the input signal gets routed on M3 and can interfere with the delay input. + self.route_vertical_pins("vdd", self.driver_inst_list, xside="rx") right_load_insts = [self.load_inst_map[x][-1] for x in self.driver_inst_list] - self.route_vertical_pins("gnd", right_load_insts, xside="rx") + self.route_vertical_pins("gnd", right_load_insts, xside="lx") def add_layout_pins(self): From 50045e54e8801d61c8feaa497f3fd73bc3604d17 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 3 May 2022 11:45:51 -0700 Subject: [PATCH 23/44] Fix a couple supply routing issues. --- compiler/base/hierarchy_layout.py | 22 +++++----- compiler/modules/column_mux_array.py | 5 ++- compiler/modules/replica_bitcell_array.py | 6 ++- compiler/pgates/column_mux.py | 7 ++-- compiler/pgates/precharge.py | 49 ++++++----------------- 5 files changed, 34 insertions(+), 55 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 5c986d75..ee97a7ac 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -657,12 +657,13 @@ class layout(): via_width=None via_height=0 + bot_y = min([pin.by() for (inst,pin) in v]) + top_y = max([pin.uy() for (inst,pin) in v]) + if full_width: - bot_y = 0 - top_y = self.height - else: - bot_y = min([pin.by() for (inst,pin) in v]) - top_y = max([pin.uy() for (inst,pin) in v]) + bot_y = min(0, bot_y) + top_y = max(self.height, top_y) + top_pos = vector(x, top_y + 0.5 * via_height) bot_pos = vector(x, bot_y - 0.5 * via_height) @@ -758,12 +759,13 @@ class layout(): via_height=None via_width=0 + left_x = min([pin.lx() for (inst,pin) in v]) + right_x = max([pin.rx() for (inst,pin) in v]) + if full_width: - left_x = 0 - right_x = self.width - else: - left_x = min([pin.lx() for (inst,pin) in v]) - right_x = max([pin.rx() for (inst,pin) in v]) + left_x = min(0, left_x) + right_x = max(self.width, right_x) + left_pos = vector(left_x + 0.5 * via_width, y) right_pos = vector(right_x + 0.5 * via_width, y) diff --git a/compiler/modules/column_mux_array.py b/compiler/modules/column_mux_array.py index 0fa35af0..36d4080e 100644 --- a/compiler/modules/column_mux_array.py +++ b/compiler/modules/column_mux_array.py @@ -147,13 +147,14 @@ class column_mux_array(design.design): offset=offset, height=self.height - offset.y) - for inst in self.mux_inst: - self.copy_layout_pin(inst, "gnd") + def route_supplies(self): + self.route_horizontal_pins("gnd", self.insts) 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 """ diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 785bb62b..cbfc6ec2 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -507,12 +507,14 @@ class replica_bitcell_array(bitcell_base_array): # There are always vertical pins for the WLs on the left/right if we have unused wordlines self.left_gnd_locs = self.route_side_pin("gnd", "left", left_right_mult) self.right_gnd_locs = self.route_side_pin("gnd","right", left_right_mult) - left_right_mult = 3 + # This needs to be big enough so that they aren't in the same supply routing grid + left_right_mult = 4 if gnd_dir == "V": self.top_gnd_locs = self.route_side_pin("gnd", "top", top_bot_mult) self.bot_gnd_locs = self.route_side_pin("gnd", "bot", top_bot_mult) - top_bot_mult = 3 + # This needs to be big enough so that they aren't in the same supply routing grid + top_bot_mult = 4 if vdd_dir == "V": self.top_vdd_locs = self.route_side_pin("vdd", "top", top_bot_mult) diff --git a/compiler/pgates/column_mux.py b/compiler/pgates/column_mux.py index a4a83dcc..bf489e87 100644 --- a/compiler/pgates/column_mux.py +++ b/compiler/pgates/column_mux.py @@ -240,10 +240,9 @@ class column_mux(pgate.pgate): self.add_via_center(layers=self.col_mux_stack, offset=active_pos) - # Add the M1->..->power_grid_layer stack - self.add_power_pin(name="gnd", - loc=active_pos, - start_layer="m1") + self.add_layout_pin_rect_center(text="gnd", + layer="m1", + offset=active_pos) # Add well enclosure over all the tx and contact if "pwell" in layer: diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 8eff8c8f..22cef930 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -96,46 +96,21 @@ class precharge(design.design): Adds a vdd rail at the top of the cell """ - if OPTS.experimental_power: - pmos_pin = self.upper_pmos2_inst.get_pin("S") - pmos_pos = pmos_pin.center() - self.add_path(pmos_pin.layer, [pmos_pos, self.well_contact_pos]) + pmos_pin = self.upper_pmos2_inst.get_pin("S") + pmos_pos = pmos_pin.center() + self.add_path(pmos_pin.layer, [pmos_pos, self.well_contact_pos]) - self.add_via_stack_center(from_layer=pmos_pin.layer, - to_layer=self.supply_stack[0], + self.add_via_stack_center(from_layer=pmos_pin.layer, + to_layer=self.supply_stack[0], + offset=self.well_contact_pos) + + self.add_min_area_rect_center(layer=self.en_layer, offset=self.well_contact_pos, - directions=("V", "V")) + width=self.well_contact.mod.second_layer_width) - self.add_min_area_rect_center(layer=self.en_layer, - offset=self.well_contact_pos, - width=self.well_contact.mod.second_layer_width) - - self.add_layout_pin_rect_center(text="vdd", - layer=self.supply_stack[0], - offset=self.well_contact_pos) - else: - # Adds the rail across the width of the cell - vdd_position = vector(0.5 * self.width, self.height) - layer_width = drc("minwidth_" + self.en_layer) - self.add_rect_center(layer=self.en_layer, - offset=vdd_position, - width=self.width, - height=layer_width) - - pmos_pin = self.upper_pmos2_inst.get_pin("S") - - # center of vdd rail - pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) - self.add_path(self.en_layer, [pmos_pin.center(), pmos_vdd_pos]) - - self.add_power_pin("vdd", - self.well_contact_pos, - directions=("V", "V")) - - self.add_via_stack_center(from_layer=pmos_pin.layer, - to_layer=self.en_layer, - offset=pmos_pin.center(), - directions=("V", "V")) + self.add_layout_pin_rect_center(text="vdd", + layer=self.supply_stack[0], + offset=self.well_contact_pos) def create_ptx(self): """ From 7e89bbb9f5dcba9db9e0a8823bb74a874e070e58 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 4 May 2022 10:15:40 -0700 Subject: [PATCH 24/44] Update Slack invitation and Mastodon verification link. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 688f22da..3dc775d6 100644 --- a/README.md +++ b/README.md @@ -215,4 +215,6 @@ If I forgot to add you, please let me know! [SCMOS]: https://www.mosis.com/files/scmos/scmos.pdf [Sky130]: https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git -[Slack]: https://join.slack.com/t/openram/shared_invite/enQtNDgxMjc3NzU5NTI1LWZiYWMwNjNkZThmYTdkODc3NDE1NDhjNzUxNDhmMDQ4ZTM3NDgwNWFlNjM5NWFiZDkyMzBlNzc1NTg3ZjllNTY +[Slack]: https://join.slack.com/t/openram/shared_invite/zt-onim74ue-zlttW5XI30xvdBlJGJF6JA +[Mastodon]: @mrg@fostodon.org + From af4f94afdef8d4482eebcd0b2ed0d7808f126f7a Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 4 May 2022 10:21:12 -0700 Subject: [PATCH 25/44] Fix command formatting. Move mastodon link to getting help. --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3dc775d6..3499ca87 100644 --- a/README.md +++ b/README.md @@ -146,16 +146,18 @@ To run a specific test: ``` ce openram/compiler/tests make 05_bitcell_array_test - +``` To run a specific technology: - +``` 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: ``` ARGS="-v" make 05_bitcell_array_test +``` # Get Involved @@ -173,6 +175,7 @@ ARGS="-v" make 05_bitcell_array_test + [OpenRAM Slack Workspace][Slack] + [OpenRAM Users Group][user-group] ([subscribe here][user-group-subscribe]) + [OpenRAM Developers Group][dev-group] ([subscribe here][dev-group-subscribe]) ++ [Mastodon]: @mrg@fostodon.org # License @@ -216,5 +219,5 @@ If I forgot to add you, please let me know! [Sky130]: https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git [Slack]: https://join.slack.com/t/openram/shared_invite/zt-onim74ue-zlttW5XI30xvdBlJGJF6JA -[Mastodon]: @mrg@fostodon.org + From b3615f943de585c6901cdca2887c867e9f3fc432 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 4 May 2022 10:24:48 -0700 Subject: [PATCH 26/44] Remove word mastodon. Stupid markdown. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3499ca87..4a3ec69a 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ ARGS="-v" make 05_bitcell_array_test + [OpenRAM Slack Workspace][Slack] + [OpenRAM Users Group][user-group] ([subscribe here][user-group-subscribe]) + [OpenRAM Developers Group][dev-group] ([subscribe here][dev-group-subscribe]) -+ [Mastodon]: @mrg@fostodon.org ++ @mrg@fostodon.org # License From b6c3580e249db400077cf982d92cd52ff55b602a Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 9 May 2022 11:44:46 -0700 Subject: [PATCH 27/44] Fix width of replica routes. Don't enclose pins if they overlap sufficiently. --- compiler/base/pin_layout.py | 2 +- compiler/modules/replica_bitcell_array.py | 12 ++++++-- compiler/router/router.py | 36 ++++++++++++++++------- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index 7ba6cdca..2058d137 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -189,7 +189,7 @@ class pin_layout: if max_x - min_x == 0 or max_y - min_y == 0: return None - return [vector(min_x, min_y), vector(max_x, max_y)] + return pin_layout("", [vector(min_x, min_y), vector(max_x, max_y)], self.layer) def xoverlaps(self, other): """ Check if shape has x overlap """ diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index cbfc6ec2..0c86f3a6 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -6,6 +6,7 @@ import debug from bitcell_base_array import bitcell_base_array +from contact import contact from tech import drc, spice, preferred_directions from tech import cell_properties as props from vector import vector @@ -588,6 +589,9 @@ class replica_bitcell_array(bitcell_base_array): top_loc = vector(self.width + offset_multiple * self.vertical_pitch, self.height) layer = self.supply_stack[2] + top_via = contact(layer_stack=self.supply_stack, + directions=("H", "H")) + # self.add_layout_pin_rect_ends(text=name, # layer=layer, @@ -596,7 +600,8 @@ class replica_bitcell_array(bitcell_base_array): self.add_layout_pin_segment_center(text=name, layer=layer, start=bot_loc, - end=top_loc) + end=top_loc, + width=top_via.second_layer_width) return (bot_loc, top_loc) @@ -612,6 +617,8 @@ class replica_bitcell_array(bitcell_base_array): right_loc = vector(self.width, self.height + offset_multiple * self.horizontal_pitch) layer = self.supply_stack[0] + side_via = contact(layer_stack=self.supply_stack, + directions=("V", "V")) # self.add_layout_pin_rect_ends(text=name, # layer=layer, @@ -620,7 +627,8 @@ class replica_bitcell_array(bitcell_base_array): self.add_layout_pin_segment_center(text=name, layer=layer, start=left_loc, - end=right_loc) + end=right_loc, + width=side_via.first_layer_height) return (left_loc, right_loc) diff --git a/compiler/router/router.py b/compiler/router/router.py index 6e5d5b4e..37db7af1 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -670,6 +670,17 @@ class router(router_tech): return set([best_coord]) + def break_on_grids(self, tracks, xvals, yvals): + track_list = [] + for x in xvals: + for y in yvals: + track_list.append(vector3d(x, y, 0)) + track_list.append(vector3d(x, y, 1)) + + for current in tracks: + if current in track_list: + breakpoint() + def divide_pin_to_tracks(self, pin, tracks): """ Return a list of pin shape parts that are in the tracks. @@ -682,16 +693,11 @@ class router(router_tech): overlap_pins = [] for track in tracks: track_pin = self.convert_track_to_shape_pin(track) - overlap_rect = track_pin.intersection(pin) - if not overlap_rect: - continue - overlap_pin = pin_layout(pin.name, - overlap_rect, - pin.layer) + overlap_pin = track_pin.intersection(pin) # If pin is smaller than minwidth, in one dimension, skip it. min_pin_width = drc("minwidth_{0}". format(pin.layer)) - if overlap_pin.width() < min_pin_width and overlap_pin.height() < min_pin_width: + if not overlap_pin or (overlap_pin.width() < min_pin_width and overlap_pin.height() < min_pin_width): continue else: overlap_pins.append(overlap_pin) @@ -1173,17 +1179,27 @@ class router(router_tech): if len(path_tracks) == 0 or len(components) == 0: return + # Find the track pin + track_pins = [self.convert_tracks_to_pin(x) for x in path_tracks] + # Convert the off-grid pin into parts in each routing grid offgrid_pin_parts = [] for component in components: pg = self.pin_groups[pin_name][component] for pin in pg.pins: + # Layer min with + min_width = drc("minwidth_{}".format(pin.layer)) + + # If we intersect, by a min_width, we are done! + for track_pin in track_pins: + intersection = pin.intersection(track_pin) + if intersection and intersection.width() > min_width and intersection.height() > min_width: + return + + #self.break_on_grids(pg.grids, xvals=[68], yvals=range(93,100)) partial_pin_parts = self.divide_pin_to_tracks(pin, pg.grids) offgrid_pin_parts.extend(partial_pin_parts) - # Find the track pin - track_pins = [self.convert_tracks_to_pin(x) for x in path_tracks] - # Find closest part closest_track_pin, closest_part_pin = self.find_closest_pin(track_pins, offgrid_pin_parts) From 8f2d787d53e329b49bb71488d1aad62133dacce9 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 11 May 2022 10:50:32 -0700 Subject: [PATCH 28/44] Add min area metal in preferred direction --- compiler/base/hierarchy_layout.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index ee97a7ac..68476a00 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -597,8 +597,14 @@ class layout(): start=top_pos, end=bot_pos) + def get_metal_layers(self, from_layer, to_layer): + from_id = layer_indices[from_layer] + to_id = layer_indices[to_layer] + layer_list = [x for x in layer_indices.keys() if layer_indices[x] >= from_id and layer_indices[x] < to_id] + + return layer_list def route_vertical_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", full_width=True): @@ -647,8 +653,7 @@ class layout(): last_via = self.add_via_stack_center(from_layer=pin.layer, to_layer=pin_layer, - offset=vector(x, y), - min_area=True) + offset=vector(x, y)) if last_via: via_width=last_via.mod.second_layer_width @@ -1076,6 +1081,8 @@ class layout(): offset=offset) return None + intermediate_layers = self.get_metal_layers(from_layer, to_layer) + via = None cur_layer = from_layer while cur_layer != to_layer: @@ -1098,7 +1105,9 @@ class layout(): implant_type=implant_type, well_type=well_type) - if cur_layer != from_layer or min_area: + # Only add the enclosure if we are in an intermediate layer + # or we are forced to + if min_area or cur_layer in intermediate_layers: self.add_min_area_rect_center(cur_layer, offset, via.mod.first_layer_width, @@ -1124,14 +1133,18 @@ class layout(): min_width = drc("minwidth_{}".format(layer)) if preferred_directions[layer] == "V": - height = max(min_area / width, min_width) + new_height = max(min_area / width, min_width) + new_width = width else: - width = max(min_area / height, min_width) + new_width = max(min_area / height, min_width) + new_height = height + + debug.check(min_area <= round_to_grid(new_height*new_width), "Min area violated.") self.add_rect_center(layer=layer, offset=offset, - width=width, - height=height) + width=new_width, + height=new_height) def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"): """Adds a ptx module to the design.""" From 357f967a93e46c373eb1aa7b876c80e5d389ef50 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 11 May 2022 11:01:14 -0700 Subject: [PATCH 29/44] Leave supply routing to new helper functions. --- compiler/modules/sense_amp_array.py | 14 ++------------ compiler/pgates/precharge.py | 10 +--------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index a481679f..daabf8df 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -174,18 +174,8 @@ class sense_amp_array(design.design): height=dout_pin.height()) def route_supplies(self): - if OPTS.experimental_power: - self.route_horizontal_pins("vdd") - self.route_horizontal_pins("gnd") - else: - for i in range(len(self.local_insts)): - inst = self.local_insts[i] - - for gnd_pin in inst.get_pins("gnd"): - self.copy_power_pin(gnd_pin) - - for vdd_pin in inst.get_pins("vdd"): - self.copy_power_pin(vdd_pin) + self.route_horizontal_pins("vdd") + self.route_horizontal_pins("gnd") def route_rails(self): # Add enable across the array diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 22cef930..1538e0a5 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -100,16 +100,8 @@ class precharge(design.design): pmos_pos = pmos_pin.center() self.add_path(pmos_pin.layer, [pmos_pos, self.well_contact_pos]) - self.add_via_stack_center(from_layer=pmos_pin.layer, - to_layer=self.supply_stack[0], - offset=self.well_contact_pos) - - self.add_min_area_rect_center(layer=self.en_layer, - offset=self.well_contact_pos, - width=self.well_contact.mod.second_layer_width) - self.add_layout_pin_rect_center(text="vdd", - layer=self.supply_stack[0], + layer=pmos_pin.layer, offset=self.well_contact_pos) def create_ptx(self): From 4345136d1a3b4ada6ab1afa14bf9db97c5299bf8 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 13 May 2022 10:46:00 -0700 Subject: [PATCH 30/44] Fix offsets for local bitcell arrays. --- compiler/modules/local_bitcell_array.py | 16 ++++++----- compiler/modules/replica_bitcell_array.py | 16 ++++++++--- compiler/modules/wordline_buffer_array.py | 33 ++++++----------------- 3 files changed, 29 insertions(+), 36 deletions(-) diff --git a/compiler/modules/local_bitcell_array.py b/compiler/modules/local_bitcell_array.py index 0d3cfd12..18b33c0f 100644 --- a/compiler/modules/local_bitcell_array.py +++ b/compiler/modules/local_bitcell_array.py @@ -159,18 +159,20 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): def place(self): """ Place the bitcelll array to the right of the wl driver. """ - # FIXME: Replace this with a tech specific paramter + + # FIXME: Replace this with a tech specific parameter driver_to_array_spacing = 3 * self.m3_pitch - self.wl_insts[0].place(vector(0, - self.bitcell_array.get_replica_bottom() + self.cell.height)) + wl_offset = vector(0, self.bitcell_array.get_replica_bottom()) + self.wl_insts[0].place(wl_offset) - self.bitcell_array_inst.place(vector(self.wl_insts[0].rx() + driver_to_array_spacing, - 0)) + bitcell_array_offset = vector(self.wl_insts[0].rx() + driver_to_array_spacing, 0) + self.bitcell_array_inst.place(bitcell_array_offset) if len(self.all_ports) > 1: - self.wl_insts[1].place(vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing, - self.bitcell_array.get_replica_top() + self.cell.height), + wl_offset = vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing, + self.bitcell_array.get_replica_bottom() + self.wl_array.height + self.cell.height) + self.wl_insts[1].place(wl_offset, mirror="XY") self.height = self.bitcell_array.height diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 0c86f3a6..f81926d4 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -353,28 +353,36 @@ class replica_bitcell_array(bitcell_base_array): self.DRC_LVS() def get_main_array_top(self): + """ Return the top of the main bitcell array. """ return self.bitcell_array_inst.uy() def get_main_array_bottom(self): + """ Return the bottom of the main bitcell array. """ return self.bitcell_array_inst.by() def get_main_array_left(self): + """ Return the left of the main bitcell array. """ return self.bitcell_array_inst.lx() def get_main_array_right(self): + """ Return the right of the main bitcell array. """ return self.bitcell_array_inst.rx() def get_replica_top(self): - return max([x.uy() for x in self.replica_col_insts if x] + [self.get_main_array_top()]) + """ Return the top of all replica columns. """ + return self.dummy_row_insts[0].by() def get_replica_bottom(self): - return min([x.by() for x in self.replica_col_insts if x] + [self.get_main_array_bottom()]) + """ Return the bottom of all replica columns. """ + return self.dummy_row_insts[0].uy() def get_replica_left(self): - return min([x.lx() for x in self.replica_col_insts if x] + [self.get_main_array_left()]) + """ Return the left of all replica columns. """ + return self.dummy_col_insts[0].rx() def get_replica_right(self): - return min([x.rx() for x in self.replica_col_insts if x] + [self.get_main_array_right()]) + """ Return the right of all replica columns. """ + return self.dummy_col_insts[1].rx() def get_column_offsets(self): """ diff --git a/compiler/modules/wordline_buffer_array.py b/compiler/modules/wordline_buffer_array.py index 51fe4030..9c74b79d 100644 --- a/compiler/modules/wordline_buffer_array.py +++ b/compiler/modules/wordline_buffer_array.py @@ -44,7 +44,10 @@ class wordline_buffer_array(design.design): self.place_drivers() self.route_layout() self.route_supplies() - self.offset_all_coordinates() + + # Don't offset these because some cells use standard cell style drivers + #self.offset_all_coordinates() + self.add_boundary() self.DRC_LVS() @@ -71,31 +74,11 @@ class wordline_buffer_array(design.design): are must-connects next level up. """ if layer_props.wordline_driver.vertical_supply: - for name in ["vdd", "gnd"]: - supply_pins = self.wld_inst[0].get_pins(name) - for pin in supply_pins: - self.add_layout_pin_segment_center(text=name, - layer=pin.layer, - start=pin.bc(), - end=vector(pin.cx(), self.height)) + self.route_vertical_pins("vdd", self.wld_inst) + self.route_vertical_pins("gnd", self.wld_inst) else: - # Find the x offsets for where the vias/pins should be placed - xoffset_list = [self.wld_inst[0].rx()] - for num in range(self.rows): - # this will result in duplicate polygons for rails, but who cares - - # use the inverter offset even though it will be the and's too - (gate_offset, y_dir) = self.get_gate_offset(0, - self.wl_driver.height, - num) - # Route both supplies - for name in ["vdd", "gnd"]: - supply_pin = self.wld_inst[num].get_pin(name) - - # Add pins in two locations - for xoffset in xoffset_list: - pin_pos = vector(xoffset, supply_pin.cy()) - self.copy_power_pin(supply_pin, loc=pin_pos) + self.route_vertical_pins("vdd", self.wld_inst, xside="rx",) + self.route_vertical_pins("gnd", self.wld_inst, xside="lx",) def create_drivers(self): self.wld_inst = [] From fbb2ea5fb697c1ef9fd604c2a1c9be3b871e37a8 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 13 May 2022 13:56:16 -0700 Subject: [PATCH 31/44] Intersection now returns a pin_layout fixed during LEF computation. --- compiler/base/lef.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/base/lef.py b/compiler/base/lef.py index 1d86a63e..8a54253f 100644 --- a/compiler/base/lef.py +++ b/compiler/base/lef.py @@ -119,14 +119,13 @@ class lef: old_blockages = list(self.blockages[pin.layer]) for blockage in old_blockages: if blockage.overlaps(inflated_pin): - intersection_shape = blockage.intersection(inflated_pin) + intersection_pin = blockage.intersection(inflated_pin) # If it is zero area, don't split the blockage - if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]: + if not intersection_pin or intersection_pin.area() == 0: continue # Remove the old blockage and add the new ones self.blockages[pin.layer].remove(blockage) - intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer) new_blockages = blockage.cut(intersection_pin) self.blockages[pin.layer].extend(new_blockages) # We split something so make another pass From 74c2c5ae0eaf2ecfe1aa6e999d2d2bff769620da Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 13 May 2022 14:32:52 -0700 Subject: [PATCH 32/44] Don't double prefix a name --- compiler/base/hierarchy_design.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 00edc558..5e9bec3f 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -28,7 +28,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): is_library_cell = os.path.isfile(gds_file) # Uniquify names to address the flat GDS namespace # except for the top/output name - if not is_library_cell and name != OPTS.output_name: + if not is_library_cell and name != OPTS.output_name and not name.startswith(OPTS.output_name): name = OPTS.output_name + "_" + name cell_name = name From 3101643964a7b4b92fa689b33ac73bcb7c0e40d0 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 13 May 2022 14:34:26 -0700 Subject: [PATCH 33/44] Check for no pins and fix closest pin return type --- compiler/router/router.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/router/router.py b/compiler/router/router.py index 37db7af1..6b2e3ab4 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -702,6 +702,8 @@ class router(router_tech): else: overlap_pins.append(overlap_pin) + debug.check(len(overlap_pins) > 0, "No pins overlapped the tracks.") + return overlap_pins @@ -1200,8 +1202,12 @@ class router(router_tech): partial_pin_parts = self.divide_pin_to_tracks(pin, pg.grids) offgrid_pin_parts.extend(partial_pin_parts) + debug.check(len(offgrid_pin_parts) > 0, "No offgrid pin parts found.") + # Find closest part closest_track_pin, closest_part_pin = self.find_closest_pin(track_pins, offgrid_pin_parts) + + debug.check(closest_track_pin and closest_part_pin, "Found no closest pins.") # Find the bbox of the on-grid track and the off-grid pin part closest_track_pin.bbox([closest_part_pin]) @@ -1217,13 +1223,13 @@ class router(router_tech): Find the closest pin in the lists. Does a stupid O(n^2). """ min_dist = None - min_item = None + min_item = (None, None) for pin1 in first_list: for pin2 in second_list: if pin1.layer != pin2.layer: continue new_dist = pin1.distance(pin2) - if not min_item or new_dist < min_dist: + if not min_dist or new_dist < min_dist: min_item = (pin1, pin2) min_dist = new_dist From 4be075e586f2f4d757f84cfb9b1b29713f4b8e1c Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 16 May 2022 14:57:32 -0700 Subject: [PATCH 34/44] Overlap length can include a rectangle overlap. --- compiler/base/pin_layout.py | 7 +++++++ compiler/router/router.py | 14 ++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index 2058d137..e6a35a0a 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -517,6 +517,13 @@ class pin_layout: if len(intersections) == 2: (p1, p2) = intersections return math.sqrt(pow(p1[0]-p2[0], 2) + pow(p1[1]-p2[1], 2)) + # If we have a rectangular overlap region + elif len(intersections) == 4: + points = intersections + ll = vector(min(p.x for p in points), min(p.y for p in points)) + ur = vector(max(p.x for p in points), max(p.y for p in points)) + new_shape = pin_layout("", [ll, ur], self.lpp) + return max(new_shape.height(), new_shape.width()) else: # This is where we had a corner intersection or none return 0 diff --git a/compiler/router/router.py b/compiler/router/router.py index 6b2e3ab4..d5f2dada 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -574,20 +574,18 @@ class router(router_tech): debug.info(3, "Converting pin [ {0} , {1} ]".format(ll, ur)) # scale the size bigger to include neaby tracks - ll = ll.scale(self.track_factor).floor() - ur = ur.scale(self.track_factor).ceil() + ll_scaled = ll.scale(self.track_factor).floor() + ur_scaled = ur.scale(self.track_factor).ceil() # Keep tabs on tracks with sufficient and insufficient overlap sufficient_list = set() insufficient_list = set() zindex = self.get_zindex(pin.lpp) - for x in range(int(ll[0]) - expansion, int(ur[0]) + 1 + expansion): - for y in range(int(ll[1] - expansion), int(ur[1]) + 1 + expansion): - (full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin, - vector3d(x, - y, - zindex)) + for x in range(int(ll_scaled[0]) - expansion, int(ur_scaled[0]) + 1 + expansion): + for y in range(int(ll_scaled[1] - expansion), int(ur_scaled[1]) + 1 + expansion): + cur_grid = vector3d(x, y, zindex) + (full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin, cur_grid) if full_overlap: sufficient_list.update([full_overlap]) if partial_overlap: From bdd334bce9bdbe4cb1594cba1ab81a89743716cf Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 16 May 2022 16:11:13 -0700 Subject: [PATCH 35/44] Add layer and directions to pbitcell --- compiler/bitcells/pbitcell.py | 12 ++++++++++++ compiler/modules/replica_bitcell_array.py | 6 +++++- technology/sky130/tech/tech.py | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index fc131d0f..29355e83 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -33,6 +33,18 @@ class pbitcell(bitcell_base.bitcell_base): self.mirror = props.bitcell_1port.mirror self.end_caps = props.bitcell_1port.end_caps + self.wl_layer = "m1" + self.wl_dir = "H" + + self.bl_layer = "m2" + self.bl_dir = "V" + + self.vdd_layer = "m1" + self.vdd_dir = "H" + + self.gnd_layer = "m1" + self.gnd_dir = "H" + bitcell_base.bitcell_base.__init__(self, name) fmt_str = "{0} rw ports, {1} w ports and {2} r ports" info_string = fmt_str.format(self.num_rw_ports, diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index f81926d4..92a0358b 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -6,6 +6,7 @@ import debug from bitcell_base_array import bitcell_base_array +from pbitcell import pbitcell from contact import contact from tech import drc, spice, preferred_directions from tech import cell_properties as props @@ -490,7 +491,10 @@ class replica_bitcell_array(bitcell_base_array): def route_supplies(self): - bitcell = getattr(props, "bitcell_{}port".format(OPTS.num_ports)) + if OPTS.bitcell == "pbitcell": + bitcell = factory.create(module_type="pbitcell") + else: + bitcell = getattr(props, "bitcell_{}port".format(OPTS.num_ports)) wl_layer = bitcell.wl_layer wl_dir = bitcell.wl_dir diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index 72250afc..3cf60d70 100644 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -9,7 +9,7 @@ import os from design_rules import * from module_type import * -from custom_cell_properties import cell_properties, cell +from custom_cell_properties import cell_properties from custom_layer_properties import layer_properties """ From bed12d2a9e60f2768c215cdaab4e84adebb1fd79 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 16 May 2022 17:02:53 -0700 Subject: [PATCH 36/44] pbitcell vdd/gnd are on layer m1 only. --- compiler/bitcells/pbitcell.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 29355e83..d1e7927b 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -99,7 +99,7 @@ class pbitcell(bitcell_base.bitcell_base): self.route_wordlines() self.route_bitlines() - self.route_supply() + self.route_supplies() if self.replica_bitcell: self.route_rbc_short() @@ -424,13 +424,14 @@ class pbitcell(bitcell_base.bitcell_base): def route_rails(self): """ Adds gnd and vdd rails and connects them to the inverters """ + # Add rails for vdd and gnd gnd_ypos = self.m1_offset - self.total_ports * self.m1_nonpref_pitch self.gnd_position = vector(0, gnd_ypos) - self.add_rect_center(layer="m1", - offset=self.gnd_position, - width=self.width) - self.add_power_pin("gnd", vector(0, gnd_ypos), directions=("H", "H")) + self.add_layout_pin_rect_center(text="gnd", + layer="m1", + offset=self.gnd_position, + width=self.width) vdd_ypos = self.inverter_nmos_ypos \ + self.inverter_nmos.active_height \ @@ -438,10 +439,10 @@ class pbitcell(bitcell_base.bitcell_base): + self.inverter_pmos.active_height \ + self.vdd_offset self.vdd_position = vector(0, vdd_ypos) - self.add_rect_center(layer="m1", - offset=self.vdd_position, - width=self.width) - self.add_power_pin("vdd", vector(0, vdd_ypos), directions=("H", "H")) + self.add_layout_pin_rect_center(text="vdd", + layer="m1", + offset=self.vdd_position, + width=self.width) def create_readwrite_ports(self): """ @@ -910,7 +911,7 @@ class pbitcell(bitcell_base.bitcell_base): self.add_path("m2", [port_contact_offest, br_offset], width=contact.m1_via.height) - def route_supply(self): + def route_supplies(self): """ Route inverter nmos and read-access nmos to gnd. Route inverter pmos to vdd. """ # route inverter nmos and read-access nmos to gnd nmos_contact_positions = [] From 9b592ab432cc9feecf94244c43bf29b8d8d40259 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 17 May 2022 13:30:41 -0700 Subject: [PATCH 37/44] Fix missing hash recompute in vector class. --- compiler/base/vector.py | 2 + compiler/modules/column_decoder.py | 111 +++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 compiler/modules/column_decoder.py diff --git a/compiler/base/vector.py b/compiler/base/vector.py index 6c4fabe2..5d011a09 100644 --- a/compiler/base/vector.py +++ b/compiler/base/vector.py @@ -51,6 +51,7 @@ class vector(): else: self.x=float(value[0]) self.y=float(value[1]) + self._hash = hash((self.x,self.y)) def __getitem__(self, index): """ @@ -104,6 +105,7 @@ class vector(): def snap_to_grid(self): self.x = self.snap_offset_to_grid(self.x) self.y = self.snap_offset_to_grid(self.y) + self._hash = hash((self.x,self.y)) return self def snap_offset_to_grid(self, offset): diff --git a/compiler/modules/column_decoder.py b/compiler/modules/column_decoder.py new file mode 100644 index 00000000..09de25fb --- /dev/null +++ b/compiler/modules/column_decoder.py @@ -0,0 +1,111 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2022 Regents of the University of California +# All rights reserved. +# +from tech import drc +import debug +import design +import math +from sram_factory import factory +from vector import vector +from globals import OPTS +from tech import cell_properties +from tech import layer_properties as layer_props + + +class column_decoder(design.design): + """ + Create the column mux decoder. + """ + + def __init__(self, name, col_addr_size): + super().__init__(name) + + self.col_addr_size = col_addr_size + self.num_inputs = col_addr_size + self.num_outputs = pow(col_addr_size, 2) + debug.info(2, + "create column decoder of {0} inputs and {1} outputs".format(self.num_inputs, + self.num_outputs)) + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + self.DRC_LVS() + + def create_netlist(self): + self.add_pins() + self.add_modules() + self.create_instances() + + def create_instances(self): + self.column_decoder_inst = self.add_inst(name="column_decoder", + mod=self.column_decoder) + self.connect_inst(self.pins) + + def create_layout(self): + self.column_decoder_inst.place(vector(0,0)) + + self.width = self.column_decoder_inst.width + self.height = self.column_decoder_inst.height + + self.route_layout() + + + def add_pins(self): + """ Add the module pins """ + + for i in range(self.num_inputs): + self.add_pin("in_{0}".format(i), "INPUT") + + for j in range(self.num_outputs): + self.add_pin("out_{0}".format(j), "OUTPUT") + + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def route_layout_pins(self): + """ Add the pins. """ + + for i in range(self.num_inputs): + self.copy_layout_pin(self.column_decoder_inst, "in_{0}".format(i)) + + for i in range(self.num_outputs): + self.copy_layout_pin(self.column_decoder_inst, "out_{0}".format(i)) + + def route_layout(self): + """ Create routing among the modules """ + self.route_layout_pins() + self.route_supplies() + + def route_supplies(self): + """ Propagate all vdd/gnd pins up to this level for all modules """ + + self.route_vertical_pins("vdd", self.insts, xside="rx",) + self.route_vertical_pins("gnd", self.insts, xside="lx",) + + def add_modules(self): + + self.dff =factory.create(module_type="dff") + + if self.col_addr_size == 1: + self.column_decoder = factory.create(module_type="pinvbuf", + height=self.dff.height) + elif self.col_addr_size == 2: + self.column_decoder = factory.create(module_type="hierarchical_predecode2x4", + column_decoder=True, + height=self.dff.height) + + elif self.col_addr_size == 3: + self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", + column_decoder=True, + height=self.dff.height) + elif self.col_addr_size == 4: + self.column_decoder = factory.create(module_type="hierarchical_predecode4x16", + column_decoder=True, + height=self.dff.height) + else: + # No error checking before? + debug.error("Invalid column decoder?", -1) + From 8217a8416507ef0d05421897f36141388c3fcafd Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 17 May 2022 13:31:23 -0700 Subject: [PATCH 38/44] Uniquify overlap points during segment overlap computation. --- compiler/base/pin_layout.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index e6a35a0a..82590ee6 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -511,7 +511,7 @@ class pin_layout: elif other.contains(self): return math.inf else: - intersections = self.compute_overlap_segment(other) + intersections = set(self.compute_overlap_segment(other)) # This is the common case where two pairs of edges overlap # at two points, so just find the distance between those two points if len(intersections) == 2: From f1f4453d14abd3da05f7aad5111ba6c097de71be Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 17 May 2022 13:32:19 -0700 Subject: [PATCH 39/44] Add column decoder module with power supply straps. --- compiler/modules/bank.py | 43 ++++--------------- compiler/modules/column_decoder.py | 24 +++++++---- compiler/options.py | 1 + .../tests/06_column_decoder_16row_test.py | 42 ++++++++++++++++++ 4 files changed, 68 insertions(+), 42 deletions(-) create mode 100755 compiler/tests/06_column_decoder_16row_test.py diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 718f8f62..1f807d63 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -523,25 +523,9 @@ class bank(design.design): if self.col_addr_size == 0: return - elif self.col_addr_size == 1: - self.column_decoder = factory.create(module_type="pinvbuf", - height=self.dff.height) - elif self.col_addr_size == 2: - self.column_decoder = factory.create(module_type="hierarchical_predecode2x4", - column_decoder=True, - height=self.dff.height) - - elif self.col_addr_size == 3: - self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", - column_decoder=True, - height=self.dff.height) - elif self.col_addr_size == 4: - self.column_decoder = factory.create(module_type="hierarchical_predecode4x16", - column_decoder=True, - height=self.dff.height) else: - # No error checking before? - debug.error("Invalid column decoder?", -1) + self.column_decoder = factory.create(module_type="column_decoder", + col_addr_size=self.col_addr_size) self.column_decoder_inst = [None] * len(self.all_ports) for port in self.all_ports: @@ -927,23 +911,14 @@ class bank(design.design): stack = getattr(self, layer_props.bank.stack) pitch = getattr(self, layer_props.bank.pitch) - if self.col_addr_size == 1: + decode_names = [] + for i in range(self.num_col_addr_lines): + decode_names.append("out_{}".format(i)) - # Connect to sel[0] and sel[1] - decode_names = ["Zb", "Z"] - - # The Address LSB - self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port)) - - elif self.col_addr_size > 1: - decode_names = [] - for i in range(self.num_col_addr_lines): - decode_names.append("out_{}".format(i)) - - for i in range(self.col_addr_size): - decoder_name = "in_{}".format(i) - addr_name = "addr{0}_{1}".format(port, i) - self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name) + for i in range(self.col_addr_size): + decoder_name = "in_{}".format(i) + addr_name = "addr{0}_{1}".format(port, i) + self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name) if port % 2: offset = self.column_decoder_inst[port].ll() - vector((self.num_col_addr_lines + 1) * pitch, 0) diff --git a/compiler/modules/column_decoder.py b/compiler/modules/column_decoder.py index 09de25fb..9cb08284 100644 --- a/compiler/modules/column_decoder.py +++ b/compiler/modules/column_decoder.py @@ -24,7 +24,7 @@ class column_decoder(design.design): self.col_addr_size = col_addr_size self.num_inputs = col_addr_size - self.num_outputs = pow(col_addr_size, 2) + self.num_outputs = pow(2, col_addr_size) debug.info(2, "create column decoder of {0} inputs and {1} outputs".format(self.num_inputs, self.num_outputs)) @@ -68,11 +68,16 @@ class column_decoder(design.design): def route_layout_pins(self): """ Add the pins. """ - for i in range(self.num_inputs): - self.copy_layout_pin(self.column_decoder_inst, "in_{0}".format(i)) + if self.col_addr_size == 1: + self.copy_layout_pin(self.column_decoder_inst, "A", "in_0") + self.copy_layout_pin(self.column_decoder_inst, "Zb", "out_0") + self.copy_layout_pin(self.column_decoder_inst, "Z", "out_1") + elif self.col_addr_size > 1: + for i in range(self.num_inputs): + self.copy_layout_pin(self.column_decoder_inst, "in_{0}".format(i)) - for i in range(self.num_outputs): - self.copy_layout_pin(self.column_decoder_inst, "out_{0}".format(i)) + for i in range(self.num_outputs): + self.copy_layout_pin(self.column_decoder_inst, "out_{0}".format(i)) def route_layout(self): """ Create routing among the modules """ @@ -81,9 +86,12 @@ class column_decoder(design.design): def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ - - self.route_vertical_pins("vdd", self.insts, xside="rx",) - self.route_vertical_pins("gnd", self.insts, xside="lx",) + if self.col_addr_size == 1: + self.copy_power_pins(self.column_decoder_inst, "vdd") + self.copy_power_pins(self.column_decoder_inst, "gnd") + else: + self.route_vertical_pins("vdd", self.insts, xside="rx",) + self.route_vertical_pins("gnd", self.insts, xside="lx",) def add_modules(self): diff --git a/compiler/options.py b/compiler/options.py index c7c062ba..26bcb099 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -171,6 +171,7 @@ class options(optparse.Values): bitcell_array = "bitcell_array" bitcell = "bitcell" buf_dec = "pbuf" + column_decoder = "column_decoder" column_mux_array = "column_mux_array" control_logic = "control_logic" decoder = "hierarchical_decoder" diff --git a/compiler/tests/06_column_decoder_16row_test.py b/compiler/tests/06_column_decoder_16row_test.py new file mode 100755 index 00000000..2823b647 --- /dev/null +++ b/compiler/tests/06_column_decoder_16row_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# 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 unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class column_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 and 2-input NAND decoder + debug.info(1, "Testing 16 row sample for column_decoder") + a = factory.create(module_type="column_decoder", col_addr_size=4) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From c8905c410a83183499222ca9346d2f062f66477c Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 17 May 2022 15:49:06 -0700 Subject: [PATCH 40/44] Fix case where distance is zero length comparison --- compiler/router/router.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/router/router.py b/compiler/router/router.py index d5f2dada..2e5236ea 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -1227,7 +1227,7 @@ class router(router_tech): if pin1.layer != pin2.layer: continue new_dist = pin1.distance(pin2) - if not min_dist or new_dist < min_dist: + if min_dist == None or new_dist < min_dist: min_item = (pin1, pin2) min_dist = new_dist From 3e02a0e7dff2f2b06fc0703e48b7299d31ec3796 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 17 May 2022 15:49:50 -0700 Subject: [PATCH 41/44] Update column decoder and dff array supplies --- compiler/modules/column_decoder.py | 4 ++-- compiler/modules/dff_array.py | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/compiler/modules/column_decoder.py b/compiler/modules/column_decoder.py index 9cb08284..f94786a2 100644 --- a/compiler/modules/column_decoder.py +++ b/compiler/modules/column_decoder.py @@ -87,8 +87,8 @@ class column_decoder(design.design): def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ if self.col_addr_size == 1: - self.copy_power_pins(self.column_decoder_inst, "vdd") - self.copy_power_pins(self.column_decoder_inst, "gnd") + self.copy_layout_pin(self.column_decoder_inst, "vdd") + self.copy_layout_pin(self.column_decoder_inst, "gnd") else: self.route_vertical_pins("vdd", self.insts, xside="rx",) self.route_vertical_pins("gnd", self.insts, xside="lx",) diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index 99b59064..226db997 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -108,22 +108,23 @@ class dff_array(design.design): return dout_name def route_supplies(self): - if OPTS.experimental_power and self.rows > 1: + if self.rows > 1: # Vertical straps on ends if multiple rows left_dff_insts = [self.dff_insts[x, 0] for x in range(self.rows)] right_dff_insts = [self.dff_insts[x, self.columns-1] for x in range(self.rows)] self.route_vertical_pins("vdd", left_dff_insts, xside="lx", yside="cy") self.route_vertical_pins("gnd", right_dff_insts, xside="rx", yside="cy") else: - for row in range(self.rows): - for col in range(self.columns): - # Continous vdd rail along with label. - vdd_pin=self.dff_insts[row, col].get_pin("vdd") - self.copy_power_pin(vdd_pin) - # Continous gnd rail along with label. - gnd_pin=self.dff_insts[row, col].get_pin("gnd") - self.copy_power_pin(gnd_pin) + # Add connections every 4 cells + for col in range(1, self.columns, 4): + vdd_pin=self.dff_insts[0, col].get_pin("vdd") + self.add_power_pin("vdd", vdd_pin.lc()) + + # Add connections every 4 cells + for col in range(1, self.columns, 4): + gnd_pin=self.dff_insts[0, col].get_pin("gnd") + self.add_power_pin("gnd", gnd_pin.rc()) def add_layout_pins(self): for row in range(self.rows): From 735d66c9f176cbd530fdad07b80c77ec7ae9662e Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 17 May 2022 15:54:54 -0700 Subject: [PATCH 42/44] Start dff array supplies on first rather than second bit. --- compiler/modules/dff_array.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index 226db997..5cca38b6 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -117,12 +117,12 @@ class dff_array(design.design): else: # Add connections every 4 cells - for col in range(1, self.columns, 4): + for col in range(0, self.columns, 4): vdd_pin=self.dff_insts[0, col].get_pin("vdd") self.add_power_pin("vdd", vdd_pin.lc()) # Add connections every 4 cells - for col in range(1, self.columns, 4): + for col in range(0, self.columns, 4): gnd_pin=self.dff_insts[0, col].get_pin("gnd") self.add_power_pin("gnd", gnd_pin.rc()) From 18c04892d1ee528ba38f9241c827caa50ad9f37d Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 18 May 2022 16:59:38 -0700 Subject: [PATCH 43/44] Set path for FREEPDK45 in ci.yml --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57facdb8..0d4852d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,7 @@ jobs: run: | export OPENRAM_HOME="${{ github.workspace }}/compiler" export OPENRAM_TECH="${{ github.workspace }}/technology" + export FREEPDK45="~/FreePDK45" #cd $OPENRAM_HOME/.. && make pdk && make install #export OPENRAM_TMP="${{ github.workspace }}/scn4me_subm_temp" #python3-coverage run -p $OPENRAM_HOME/tests/regress.py -j 12 -t scn4m_subm From 25fa0a8de3651ac2bb095b6c599781990976afb1 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 19 May 2022 14:53:17 -0700 Subject: [PATCH 44/44] Fix missing cell syntax error. --- technology/sky130/tech/tech.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index 3cf60d70..72250afc 100644 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -9,7 +9,7 @@ import os from design_rules import * from module_type import * -from custom_cell_properties import cell_properties +from custom_cell_properties import cell_properties, cell from custom_layer_properties import layer_properties """