From cb21443e2d30ef0518a8ec04566faa9499c393b9 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Mon, 24 Jul 2023 23:25:35 -0700 Subject: [PATCH 01/48] start of pattern refactor --- compiler/base/hierarchy_layout.py | 7 ++ compiler/modules/bitcell_array.py | 39 +++++--- compiler/modules/bitcell_base_array.py | 56 ++++++----- compiler/modules/pattern.py | 129 +++++++++++++++++++++++++ 4 files changed, 191 insertions(+), 40 deletions(-) create mode 100644 compiler/modules/pattern.py diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index d33d552d..899e4806 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -476,6 +476,13 @@ class layout(): # debug.info(4, "instance list: " + ",".join(x.name for x in self.insts)) return self.insts[-1] + def add_existing_inst(self, inst): + self.mods.add(inst.mod) + self.inst_names.add(self.name) + self.insts.append(inst) + debug.info(3, "adding existing instance{}".format(self.insts[-1])) + return self.insts[-1] + def get_inst(self, name): """ Retrieve an instance by name """ for inst in self.insts: diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index f8d0f031..10e57747 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -10,7 +10,8 @@ from openram.tech import drc, spice from openram.sram_factory import factory from openram import OPTS from .bitcell_base_array import bitcell_base_array - +from .pattern import pattern +from openram.base import geometry, instance class bitcell_array(bitcell_base_array): """ @@ -42,7 +43,7 @@ class bitcell_array(bitcell_base_array): def create_layout(self): - self.place_array("bit_r{0}_c{1}") + self.place_array() self.add_layout_pins() @@ -56,19 +57,29 @@ class bitcell_array(bitcell_base_array): """ Add the modules used in this design """ self.cell = factory.create(module_type=OPTS.bitcell) - def create_instances(self): - """ Create the module instances used in this design """ - self.cell_inst = {} - for col in range(self.column_size): - for row in range(self.row_size): - name = "bit_r{0}_c{1}".format(row, col) - self.cell_inst[row, col]=self.add_inst(name=name, - mod=self.cell) - self.connect_inst(self.get_bitcell_pins(row, col)) + # def create_instances(self): + # """ Create the module instances used in this design """ + # self.cell_inst = {} + # for col in range(self.column_size): + # for row in range(self.row_size): + # name = "bit_r{0}_c{1}".format(row, col) + # self.cell_inst[row, col]=self.add_inst(name=name, + # mod=self.cell) + # self.connect_inst(self.get_bitcell_pins(row, col)) + # + # # If it is a "core" cell, it could be trimmed for sim time + # if col>0 and col0 and row0 and col0 and row List[int]: + for row in block: + row_height = row[0].height + for inst in row: + debug.check(row_height == inst.height, "intrablock instances within the same row are different heights") + + for y in range(len(block[0])): + debug.check(all([row[y].width for row in block]), "intrablock instances within the same column are different widths") + + block_width = sum([instance.width for instance in block[0]]) + block_height = sum([row[0].height for row in block]) + + return [block_width, block_height] + + def verify_interblock_dimensions(self) -> None: + """ + Ensure the individual blocks are valid and interblock dimensions are valid + """ + debug.check(len(self.core_block) >= 1, "invalid core_block dimension: core_block rows must be >=1") + debug.check(len(self.core_block[0]) >= 1, "invalid core_block dimension: core_block cols must be >=1") + if self.x_block and self.y_block: + debug.check(self.xy_block is not None, "must have xy_block if both x_block and y_block are provided") + + + (self.core_block_width, self.core_block_height) = self.compute_and_verify_intrablock_dimensions(self.core_block) + if self.x_block: + (self.x_block_width, self.x_block_height) = self.compute_and_verify_intrablock_dimensions(self.x_block) + if self.y_block: + (self.y_block_width, self.y_block_height) = self.compute_and_verify_intrablock_dimensions(self.y_block) + if self.xy_block: + (self.xy_block_width, self.xy_block_height) = self.compute_and_verify_intrablock_dimensions(self.xy_block) + if(self.x_block): + debug.check(self.core_block_width * self.cores_per_x_block == self.x_block_width, "core_block does not align with x_block") + if(self.y_block): + debug.check(self.core_block_height * self.cores_per_y_block == self.y_block_height, "core_block does not aligns with y_block") + if(self.xy_block): + debug.check(self.xy_block_height == self.x_block_height, "xy_block does not align with x_block") + debug.check(self.xy_block_width == self.y_block_width, "xy_block does not align with y_block") + + def place_block(self, block: block, x: float, y: float, x_count: int, y_count: int) -> None: + return + + + def connect_block(self, block: block, x: int, y: int) -> None: + for dy in range(len(block)): + for dx in range(len(block[0])): + inst = block[dy][dx] + print(inst.name) + self.parent_design.cell_inst[x + dx, y + dy] = self.parent_design.add_existing_inst(inst) + self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(x+dx, y+dy)) + + + def connect_array(self) -> None: + x = 0 + y = 0 + self.connect_block(self.core_block, x,y) + + + def place_array(self) -> None: + + array_x = 0 + array_y = 0 + # if(self.initial_x_block and self.initial_y_block): + # self.place_block(array, self.xy_block, array_x, array_y) + # array_x += self.xy_block_width + # if(self.initial_x_block): + # self.place_block(array, self.xy_block, array_x, array_y) + # array_x += self.x_block_width + self.connect_array() + self.place_block(self.core_block, array_x, array_y, 0, 0) + + + def connect_pins(self, array: design) -> None: + array.connect_isnt() + From 4cf3ea91ff2e96c6a92446c2c40c9a6b3c7a7cd4 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 25 Jul 2023 15:02:06 -0700 Subject: [PATCH 02/48] scmos array connecting --- compiler/modules/bitcell_array.py | 10 +++++++++- compiler/modules/pattern.py | 18 ++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 10e57747..624bd4ac 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -79,7 +79,15 @@ class bitcell_array(bitcell_base_array): core_block[0][1] = geometry.instance("core_1_0", mod=self.cell, mirror="MX") core_block[1][0] = geometry.instance("core_0_1", mod=self.cell, mirror="MY") core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, mirror="XY") - self.pattern = pattern(self, "bitcell_array", core_block, self.row_size/2, self.column_size/2) + num_core_x = self.row_size/len(core_block[0]) + num_core_y = self.column_size/len(core_block) + debug.check(num_core_x.is_integer(), "number of core blocks must be an integer") + debug.check(num_core_y.is_integer(), "number of core blocks must be an integer") + num_core_x = int(num_core_x) + num_core_y = int(num_core_y) + + self.pattern = pattern(self, "bitcell_array", core_block, num_core_x, num_core_y) + self.pattern.connect_array() def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index d0320f13..c45c4e49 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -40,11 +40,13 @@ class pattern(): self.parent_design = parent_design self.name = name self.core_block = core_block + self.num_core_x = num_core_x + self.num_core_y = num_core_y + self.cores_per_x_block = cores_per_x_block + self.cores_per_y_block = cores_per_y_block self.x_block = x_block self.y_block = y_block self.xy_block = xy_block - self.cores_per_x_block = cores_per_x_block - self.cores_per_y_block = cores_per_y_block self.initial_x_block = initial_x_block self.initial_y_block = initial_y_block self.final_x_block = final_x_block @@ -99,7 +101,6 @@ class pattern(): for dy in range(len(block)): for dx in range(len(block[0])): inst = block[dy][dx] - print(inst.name) self.parent_design.cell_inst[x + dx, y + dy] = self.parent_design.add_existing_inst(inst) self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(x+dx, y+dy)) @@ -107,9 +108,14 @@ class pattern(): def connect_array(self) -> None: x = 0 y = 0 - self.connect_block(self.core_block, x,y) - - + for i in range(self.num_core_y): + for j in range (self.num_core_x): + print("connecting {} {}".format(x,y)) + self.connect_block(self.core_block, x,y) + x += len(self.core_block[0]) + x = 0 + y += len(self.core_block) + def place_array(self) -> None: array_x = 0 From 3fe44a375189bd343c5da9ee6b2b22ec3dcf0f37 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 26 Jul 2023 01:28:50 -0700 Subject: [PATCH 03/48] scmos array placing --- compiler/modules/pattern.py | 41 ++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index c45c4e49..7624af02 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -92,10 +92,6 @@ class pattern(): if(self.xy_block): debug.check(self.xy_block_height == self.x_block_height, "xy_block does not align with x_block") debug.check(self.xy_block_width == self.y_block_width, "xy_block does not align with y_block") - - def place_block(self, block: block, x: float, y: float, x_count: int, y_count: int) -> None: - return - def connect_block(self, block: block, x: int, y: int) -> None: for dy in range(len(block)): @@ -104,6 +100,16 @@ class pattern(): self.parent_design.cell_inst[x + dx, y + dy] = self.parent_design.add_existing_inst(inst) self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(x+dx, y+dy)) + def place_block(self, block: block, x: int, y: int, place_x: float, place_y: float) -> None: + x_offset = 0 + y_offset = 0 + for dy in range(len(block)): + for dx in range(len(block[0])): + inst = self.parent_design.cell_inst[x + dx, y +dy] + inst.place((place_x + x_offset, place_y + y_offset), inst.mirror, inst.rotate) + x_offset += inst.width + x_offset = 0 + y_offset += inst.height def connect_array(self) -> None: x = 0 @@ -118,16 +124,23 @@ class pattern(): def place_array(self) -> None: - array_x = 0 - array_y = 0 - # if(self.initial_x_block and self.initial_y_block): - # self.place_block(array, self.xy_block, array_x, array_y) - # array_x += self.xy_block_width - # if(self.initial_x_block): - # self.place_block(array, self.xy_block, array_x, array_y) - # array_x += self.x_block_width - self.connect_array() - self.place_block(self.core_block, array_x, array_y, 0, 0) + x = 0 + y = 0 + place_x = 0 + place_y = 0 + for i in range(self.num_core_y): + for j in range (self.num_core_x): + print("placing {} {}".format(x,y)) + self.place_block(self.core_block, x, y, place_x, place_y) + place_x += self.core_block_width + x += len(self.core_block[0]) + x = 0 + place_x = 0 + y += len(self.core_block) + place_y += self.core_block_height + + + def connect_pins(self, array: design) -> None: From 4baec81f829f02f98146777c3915a9cbd8cc7c89 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 26 Jul 2023 02:51:31 -0700 Subject: [PATCH 04/48] lvs failures --- compiler/modules/bitcell_base_array.py | 27 -------------------------- compiler/modules/pattern.py | 9 ++++++--- 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index e7a3f314..87f9f149 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -185,33 +185,6 @@ class bitcell_base_array(design): dir_x = True return (tempy, dir_x) - # def place_array(self, name_template: str, row_offset=0): - # # We increase it by a well enclosure so the precharges don't overlap our wells - # self.height = self.row_size * self.cell.height - # self.width = self.column_size * self.cell.width - # - # xoffset = 0.0 - # for col in range(self.column_size): - # yoffset = 0.0 - # tempx, dir_y = self._adjust_x_offset(xoffset, col, self.column_offset) - # - # for row in range(self.row_size): - # tempy, dir_x = self._adjust_y_offset(yoffset, row, row_offset) - # - # if dir_x and dir_y: - # dir_key = "XY" - # elif dir_x: - # dir_key = "MX" - # elif dir_y: - # dir_key = "MY" - # else: - # dir_key = "" - # - # self.cell_inst[row, col].place(offset=[tempx, tempy], - # mirror=dir_key) - # yoffset += self.cell.height - # xoffset += self.cell.width - def place_array(self): self.pattern.place_array() diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index 7624af02..47f3f20a 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -129,16 +129,19 @@ class pattern(): place_x = 0 place_y = 0 for i in range(self.num_core_y): + x = 0 + place_x = 0 for j in range (self.num_core_x): print("placing {} {}".format(x,y)) self.place_block(self.core_block, x, y, place_x, place_y) place_x += self.core_block_width x += len(self.core_block[0]) - x = 0 - place_x = 0 y += len(self.core_block) place_y += self.core_block_height - + self.parent_design.width = x + self.parent_design.height = y + + From 8a4b34dee1a6fe397ebfcec6ba8361ad536e81f5 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 26 Jul 2023 18:05:36 -0700 Subject: [PATCH 05/48] proper tiling --- compiler/base/hierarchy_layout.py | 8 ++++++-- compiler/modules/bitcell_array.py | 4 ++-- compiler/modules/pattern.py | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 899e4806..b91af980 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -9,6 +9,7 @@ import sys import os import re from math import sqrt +from copy import deepcopy from openram import debug from openram.gdsMill import gdsMill from openram import tech @@ -476,9 +477,12 @@ class layout(): # debug.info(4, "instance list: " + ",".join(x.name for x in self.insts)) return self.insts[-1] - def add_existing_inst(self, inst): + def add_existing_inst(self, inst, name): + inst = deepcopy(inst) self.mods.add(inst.mod) - self.inst_names.add(self.name) + if name: + inst.name = name + self.inst_names.add(inst.name) self.insts.append(inst) debug.info(3, "adding existing instance{}".format(self.insts[-1])) return self.insts[-1] diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 624bd4ac..b5b95d4c 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -76,8 +76,8 @@ class bitcell_array(bitcell_base_array): core_block = [[0 for x in range(2)] for y in range(2)] core_block[0][0] = geometry.instance("core_0_0", mod=self.cell) - core_block[0][1] = geometry.instance("core_1_0", mod=self.cell, mirror="MX") - core_block[1][0] = geometry.instance("core_0_1", mod=self.cell, mirror="MY") + core_block[0][1] = geometry.instance("core_1_0", mod=self.cell, mirror="MY") + core_block[1][0] = geometry.instance("core_0_1", mod=self.cell, mirror="MX") core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, mirror="XY") num_core_x = self.row_size/len(core_block[0]) num_core_y = self.column_size/len(core_block) diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index 47f3f20a..83d1b046 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -97,7 +97,7 @@ class pattern(): for dy in range(len(block)): for dx in range(len(block[0])): inst = block[dy][dx] - self.parent_design.cell_inst[x + dx, y + dy] = self.parent_design.add_existing_inst(inst) + self.parent_design.cell_inst[x + dx, y + dy] = self.parent_design.add_existing_inst(inst,"bit_r{}_c{}".format(y +dy, x+dx)) self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(x+dx, y+dy)) def place_block(self, block: block, x: int, y: int, place_x: float, place_y: float) -> None: @@ -106,11 +106,21 @@ class pattern(): for dy in range(len(block)): for dx in range(len(block[0])): inst = self.parent_design.cell_inst[x + dx, y +dy] - inst.place((place_x + x_offset, place_y + y_offset), inst.mirror, inst.rotate) + self.place_inst(inst, (place_x + x_offset, place_y + y_offset)) + #inst.place((place_x + x_offset, place_y + y_offset), inst.mirror, inst.rotate) x_offset += inst.width x_offset = 0 y_offset += inst.height + def place_inst(self, inst, offset) -> None: + x = offset[0] + y = offset[1] + if "X" in inst.mirror: + y += inst.height + if "Y" in inst.mirror: + x += inst.width + inst.place((x, y), inst.mirror, inst.rotate) + def connect_array(self) -> None: x = 0 y = 0 From dde4103d49eb05a3378089c22aa6a94d87000e0d Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 26 Jul 2023 18:40:37 -0700 Subject: [PATCH 06/48] scmos pass --- compiler/modules/bitcell_array.py | 9 +++--- compiler/modules/pattern.py | 53 +++++++++++++++---------------- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index b5b95d4c..b4865f48 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -73,12 +73,11 @@ class bitcell_array(bitcell_base_array): def create_instances(self): self.cell_inst={} - - core_block = [[0 for x in range(2)] for y in range(2)] + core_block = [[0 for x in range(2)] for y in range(2)] core_block[0][0] = geometry.instance("core_0_0", mod=self.cell) - core_block[0][1] = geometry.instance("core_1_0", mod=self.cell, mirror="MY") - core_block[1][0] = geometry.instance("core_0_1", mod=self.cell, mirror="MX") - core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, mirror="XY") + core_block[0][1] = geometry.instance("core_1_0", mod=self.cell) + core_block[1][0] = geometry.instance("core_0_1", mod=self.cell) + core_block[1][1] = geometry.instance("core_1_1", mod=self.cell) num_core_x = self.row_size/len(core_block[0]) num_core_y = self.column_size/len(core_block) debug.check(num_core_x.is_integer(), "number of core blocks must be an integer") diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index 83d1b046..fec8bc51 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -93,21 +93,20 @@ class pattern(): debug.check(self.xy_block_height == self.x_block_height, "xy_block does not align with x_block") debug.check(self.xy_block_width == self.y_block_width, "xy_block does not align with y_block") - def connect_block(self, block: block, x: int, y: int) -> None: - for dy in range(len(block)): - for dx in range(len(block[0])): - inst = block[dy][dx] - self.parent_design.cell_inst[x + dx, y + dy] = self.parent_design.add_existing_inst(inst,"bit_r{}_c{}".format(y +dy, x+dx)) - self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(x+dx, y+dy)) + def connect_block(self, block: block, col: int, row: int) -> None: + for dr in range(len(block)): + for dc in range(len(block[0])): + inst = block[dc][dr] + self.parent_design.cell_inst[row + dr, col + dc] = self.parent_design.add_existing_inst(inst,"bit_r{}_c{}".format(row +dr, col+dc)) + self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(row+dr, col+dc)) - def place_block(self, block: block, x: int, y: int, place_x: float, place_y: float) -> None: + def place_block(self, block: block, row: int, col: int, place_x: float, place_y: float) -> None: x_offset = 0 y_offset = 0 - for dy in range(len(block)): - for dx in range(len(block[0])): - inst = self.parent_design.cell_inst[x + dx, y +dy] + for dr in range(len(block)): + for dc in range(len(block[0])): + inst = self.parent_design.cell_inst[row + dr, col +dc] self.place_inst(inst, (place_x + x_offset, place_y + y_offset)) - #inst.place((place_x + x_offset, place_y + y_offset), inst.mirror, inst.rotate) x_offset += inst.width x_offset = 0 y_offset += inst.height @@ -122,34 +121,34 @@ class pattern(): inst.place((x, y), inst.mirror, inst.rotate) def connect_array(self) -> None: - x = 0 - y = 0 + row = 0 + col = 0 for i in range(self.num_core_y): for j in range (self.num_core_x): - print("connecting {} {}".format(x,y)) - self.connect_block(self.core_block, x,y) - x += len(self.core_block[0]) - x = 0 - y += len(self.core_block) + print("connecting {} {}".format(row,col)) + self.connect_block(self.core_block, col, row) + col += len(self.core_block[0]) + col = 0 + row += len(self.core_block) def place_array(self) -> None: - x = 0 - y = 0 + row = 0 + col = 0 place_x = 0 place_y = 0 for i in range(self.num_core_y): - x = 0 + col = 0 place_x = 0 for j in range (self.num_core_x): - print("placing {} {}".format(x,y)) - self.place_block(self.core_block, x, y, place_x, place_y) + print("placing {} {}".format(row,col)) + self.place_block(self.core_block, row, col, place_x, place_y) place_x += self.core_block_width - x += len(self.core_block[0]) - y += len(self.core_block) + col += len(self.core_block[0]) + row += len(self.core_block) place_y += self.core_block_height - self.parent_design.width = x - self.parent_design.height = y + self.parent_design.width = place_x + self.parent_design.height = place_y From a6e07aa364ad08be457e6ddb6084062775c1f52f Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 26 Jul 2023 18:53:39 -0700 Subject: [PATCH 07/48] cleanup --- compiler/modules/bitcell_array.py | 14 -------------- compiler/modules/pattern.py | 8 -------- 2 files changed, 22 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index b4865f48..f0874a84 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -57,20 +57,6 @@ class bitcell_array(bitcell_base_array): """ Add the modules used in this design """ self.cell = factory.create(module_type=OPTS.bitcell) - # def create_instances(self): - # """ Create the module instances used in this design """ - # self.cell_inst = {} - # for col in range(self.column_size): - # for row in range(self.row_size): - # name = "bit_r{0}_c{1}".format(row, col) - # self.cell_inst[row, col]=self.add_inst(name=name, - # mod=self.cell) - # self.connect_inst(self.get_bitcell_pins(row, col)) - # - # # If it is a "core" cell, it could be trimmed for sim time - # if col>0 and col0 and row None: - array.connect_isnt() - From 8d8f243f996ef497399b6691685e3418a575ba78 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 27 Jul 2023 18:39:18 -0700 Subject: [PATCH 08/48] scmos passing with odd sizses again --- compiler/base/geometry.py | 3 +- compiler/modules/bitcell_array.py | 18 ++--- compiler/modules/pattern.py | 106 +++++++++++++++++++++--------- 3 files changed, 83 insertions(+), 44 deletions(-) diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 2e33885c..9e7172bf 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -164,7 +164,7 @@ class instance(geometry): An instance of an instance/module with a specified location and rotation """ - def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0): + def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0, is_bitcell=False): """Initializes an instance to represent a module""" super().__init__() debug.check(mirror not in ["R90", "R180", "R270"], @@ -176,6 +176,7 @@ class instance(geometry): self.rotate = rotate self.offset = vector(offset).snap_to_grid() self.mirror = mirror + self.is_bitcell = is_bitcell if OPTS.netlist_only: self.width = 0 self.height = 0 diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index f0874a84..f95cde51 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -12,6 +12,7 @@ from openram import OPTS from .bitcell_base_array import bitcell_base_array from .pattern import pattern from openram.base import geometry, instance +from math import ceil class bitcell_array(bitcell_base_array): """ @@ -60,18 +61,11 @@ class bitcell_array(bitcell_base_array): def create_instances(self): self.cell_inst={} core_block = [[0 for x in range(2)] for y in range(2)] - core_block[0][0] = geometry.instance("core_0_0", mod=self.cell) - core_block[0][1] = geometry.instance("core_1_0", mod=self.cell) - core_block[1][0] = geometry.instance("core_0_1", mod=self.cell) - core_block[1][1] = geometry.instance("core_1_1", mod=self.cell) - num_core_x = self.row_size/len(core_block[0]) - num_core_y = self.column_size/len(core_block) - debug.check(num_core_x.is_integer(), "number of core blocks must be an integer") - debug.check(num_core_y.is_integer(), "number of core blocks must be an integer") - num_core_x = int(num_core_x) - num_core_y = int(num_core_y) - - self.pattern = pattern(self, "bitcell_array", core_block, num_core_x, num_core_y) + core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) + core_block[0][1] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True) + core_block[1][0] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True) + core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True) + self.pattern = pattern(self, "bitcell_array", core_block, self.row_size, self.column_size) self.pattern.connect_array() def analytical_power(self, corner, load): diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index 9e1691c4..bce6763d 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -4,6 +4,7 @@ from typing import List from typing import Optional from openram.base import design from openram.globals import OPTS +from math import ceil class pattern(): """ This class is used to desribe the internals of a bitcell array. It describes @@ -15,8 +16,10 @@ class pattern(): parent_design: design, name:str, core_block:block, - num_core_x:int, - num_core_y:int, + num_rows:int, + num_cols:int, + num_cores_x: Optional[int] = 0, + num_cores_y: Optional[int] = 0, cores_per_x_block: int = 1, cores_per_y_block: int = 1, x_block: Optional[block] = None, @@ -40,8 +43,15 @@ class pattern(): self.parent_design = parent_design self.name = name self.core_block = core_block - self.num_core_x = num_core_x - self.num_core_y = num_core_y + self.num_rows = num_rows + self.num_cols = num_cols + self.num_cores_x = num_cores_x + self.num_cores_y = num_cores_y + if num_cores_x == 0: + self.num_cores_x = ceil(num_cols/len(core_block[0])) + if num_cores_y == 0: + self.num_cores_y = ceil(num_rows/len(core_block)) + self.cores_per_x_block = cores_per_x_block self.cores_per_y_block = cores_per_y_block self.x_block = x_block @@ -92,24 +102,39 @@ class pattern(): if(self.xy_block): debug.check(self.xy_block_height == self.x_block_height, "xy_block does not align with x_block") debug.check(self.xy_block_width == self.y_block_width, "xy_block does not align with y_block") + - def connect_block(self, block: block, col: int, row: int) -> None: + def connect_block(self, block: block, col: int, row: int): for dr in range(len(block)): for dc in range(len(block[0])): - inst = block[dc][dr] - self.parent_design.cell_inst[row + dr, col + dc] = self.parent_design.add_existing_inst(inst,"bit_r{}_c{}".format(row +dr, col+dc)) - self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(row+dr, col+dc)) + if(self.bit_rows.count(self.num_rows) != self.num_cols and self.bit_cols.count(self.bit_cols) != self.num_rows): + inst = block[dc][dr] + if(len(self.bit_rows) <= col + dc): + self.bit_rows.append(0) + if(len(self.bit_cols) <= row + dr): + self.bit_cols.append(0) + if(self.bit_rows[col+dc] < self.num_cols and self.bit_cols[row+dr] < self.num_rows): + if(inst.is_bitcell): + self.bit_rows[col+dc] += 1 + self.bit_cols[row+dr] += 1 + self.parent_design.cell_inst[row + dr, col + dc] = self.parent_design.add_existing_inst(inst,"bit_r{}_c{}".format(row +dr, col+dc)) + self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(row+dr, col+dc)) - def place_block(self, block: block, row: int, col: int, place_x: float, place_y: float) -> None: - x_offset = 0 - y_offset = 0 - for dr in range(len(block)): - for dc in range(len(block[0])): - inst = self.parent_design.cell_inst[row + dr, col +dc] - self.place_inst(inst, (place_x + x_offset, place_y + y_offset)) - x_offset += inst.width - x_offset = 0 - y_offset += inst.height + def connect_array(self) -> None: + self.bit_rows = [] + self.bit_cols = [] + row = 0 + col = 0 + for i in range(self.num_cores_y): + for j in range (self.num_cores_x): + print("connecting {} {}".format(row,col)) + self.connect_block(self.core_block, col, row) + col += len(self.core_block[0]) + col = 0 + row += len(self.core_block) + print(self.bit_rows) + print(self.bit_cols) + print(self.parent_design.cell_inst) def place_inst(self, inst, offset) -> None: x = offset[0] @@ -120,31 +145,50 @@ class pattern(): x += inst.width inst.place((x, y), inst.mirror, inst.rotate) - def connect_array(self) -> None: - row = 0 - col = 0 - for i in range(self.num_core_y): - for j in range (self.num_core_x): - print("connecting {} {}".format(row,col)) - self.connect_block(self.core_block, col, row) - col += len(self.core_block[0]) - col = 0 - row += len(self.core_block) - + + + def place_block(self, block: block, row: int, col: int, place_x: float, place_y: float) -> None: + x_offset = 0 + y_offset = 0 + for dr in range(len(block)): + for dc in range(len(block[0])): + if(self.bit_rows.count(self.num_rows) != self.num_cols and self.bit_cols.count(self.bit_cols) != self.num_rows): + if(len(self.bit_rows) <= col + dc): + self.bit_rows.append(0) + if(len(self.bit_cols) <= row + dr): + self.bit_cols.append(0) + if(self.bit_rows[col+dc] < self.num_cols and self.bit_cols[row+dr] < self.num_rows): + inst = self.parent_design.cell_inst[row + dr, col +dc] + if(inst.is_bitcell): + self.bit_rows[col+dc] += 1 + self.bit_cols[row+dr] += 1 + self.place_inst(inst, (place_x + x_offset, place_y + y_offset)) + x_offset += inst.width + x_offset = 0 + y_offset += inst.height + + def place_array(self) -> None: + self.bit_rows = [] + self.bit_cols = [] row = 0 col = 0 place_x = 0 place_y = 0 - for i in range(self.num_core_y): + for i in range(self.num_cores_y): col = 0 place_x = 0 - for j in range (self.num_core_x): + for j in range (self.num_cores_x): print("placing {} {}".format(row,col)) self.place_block(self.core_block, row, col, place_x, place_y) place_x += self.core_block_width col += len(self.core_block[0]) + if(self.bit_rows.count(self.num_rows) == self.num_cols and self.bit_cols.count(self.bit_cols) == self.num_rows): + print(self.bit_rows) + print(self.bit_cols) + return + row += len(self.core_block) place_y += self.core_block_height self.parent_design.width = place_x From 6f9618f28aa62d0157f3f2dff70029b8dc42dd43 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Fri, 28 Jul 2023 21:46:07 -0700 Subject: [PATCH 09/48] fix --- compiler/modules/bitcell_array.py | 4 ++-- compiler/modules/pattern.py | 18 +++++++++++++----- compiler/tests/05_bitcell_array_test.py | 2 +- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index f95cde51..4400a508 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -64,8 +64,8 @@ class bitcell_array(bitcell_base_array): core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) core_block[0][1] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True) core_block[1][0] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True) - core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True) - self.pattern = pattern(self, "bitcell_array", core_block, self.row_size, self.column_size) + core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True) + self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.row_size, num_cols=self.column_size) self.pattern.connect_array() def analytical_power(self, corner, load): diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index bce6763d..369f8fa4 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -49,8 +49,10 @@ class pattern(): self.num_cores_y = num_cores_y if num_cores_x == 0: self.num_cores_x = ceil(num_cols/len(core_block[0])) + print('num_cores_x:', self.num_cores_x) if num_cores_y == 0: - self.num_cores_y = ceil(num_rows/len(core_block)) + self.num_cores_y = ceil(num_rows/len(core_block)) + print('num_cores_y:', self.num_cores_y) self.cores_per_x_block = cores_per_x_block self.cores_per_y_block = cores_per_y_block @@ -61,6 +63,7 @@ class pattern(): self.initial_y_block = initial_y_block self.final_x_block = final_x_block self.final_y_block = final_y_block + print(self.num_cols) if not OPTS.netlist_only: self.verify_interblock_dimensions() @@ -113,11 +116,16 @@ class pattern(): self.bit_rows.append(0) if(len(self.bit_cols) <= row + dr): self.bit_cols.append(0) - if(self.bit_rows[col+dc] < self.num_cols and self.bit_cols[row+dr] < self.num_rows): + # print(self.bit_rows[col+dc], self.num_rows, self.bit_cols[row+dr], self.num_cols) + if(self.bit_rows[col+dc] < self.num_rows and self.bit_cols[row+dr] < self.num_cols): if(inst.is_bitcell): self.bit_rows[col+dc] += 1 self.bit_cols[row+dr] += 1 + print(self.bit_rows) + print(self.bit_cols) + print('-----------------------------------') self.parent_design.cell_inst[row + dr, col + dc] = self.parent_design.add_existing_inst(inst,"bit_r{}_c{}".format(row +dr, col+dc)) + print('inst:', row+dr, col+dc) self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(row+dr, col+dc)) def connect_array(self) -> None: @@ -132,8 +140,8 @@ class pattern(): col += len(self.core_block[0]) col = 0 row += len(self.core_block) - print(self.bit_rows) - print(self.bit_cols) + # print(self.bit_rows) + # print(self.bit_cols) print(self.parent_design.cell_inst) def place_inst(self, inst, offset) -> None: @@ -157,7 +165,7 @@ class pattern(): self.bit_rows.append(0) if(len(self.bit_cols) <= row + dr): self.bit_cols.append(0) - if(self.bit_rows[col+dc] < self.num_cols and self.bit_cols[row+dr] < self.num_rows): + if(self.bit_rows[col+dc] < self.num_rows and self.bit_cols[row+dr] < self.num_cols): inst = self.parent_design.cell_inst[row + dr, col +dc] if(inst.is_bitcell): self.bit_rows[col+dc] += 1 diff --git a/compiler/tests/05_bitcell_array_test.py b/compiler/tests/05_bitcell_array_test.py index a92d3653..872986a7 100755 --- a/compiler/tests/05_bitcell_array_test.py +++ b/compiler/tests/05_bitcell_array_test.py @@ -31,7 +31,7 @@ class array_test(openram_test): num_spare_rows = 0 num_spare_cols = 0 - a = factory.create(module_type="bitcell_array", cols=8 + num_spare_cols, rows=8 + num_spare_rows) + a = factory.create(module_type="bitcell_array", cols=4 + num_spare_cols, rows=2 + num_spare_rows) self.local_check(a) openram.end_openram() From c1acdadd81bab0d4131c2fe80eabbef9644e12e8 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Sat, 29 Jul 2023 17:39:27 -0700 Subject: [PATCH 10/48] remove print statements --- compiler/modules/pattern.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index 369f8fa4..7bc75a1e 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -49,10 +49,8 @@ class pattern(): self.num_cores_y = num_cores_y if num_cores_x == 0: self.num_cores_x = ceil(num_cols/len(core_block[0])) - print('num_cores_x:', self.num_cores_x) if num_cores_y == 0: self.num_cores_y = ceil(num_rows/len(core_block)) - print('num_cores_y:', self.num_cores_y) self.cores_per_x_block = cores_per_x_block self.cores_per_y_block = cores_per_y_block @@ -63,7 +61,6 @@ class pattern(): self.initial_y_block = initial_y_block self.final_x_block = final_x_block self.final_y_block = final_y_block - print(self.num_cols) if not OPTS.netlist_only: self.verify_interblock_dimensions() @@ -116,16 +113,11 @@ class pattern(): self.bit_rows.append(0) if(len(self.bit_cols) <= row + dr): self.bit_cols.append(0) - # print(self.bit_rows[col+dc], self.num_rows, self.bit_cols[row+dr], self.num_cols) if(self.bit_rows[col+dc] < self.num_rows and self.bit_cols[row+dr] < self.num_cols): if(inst.is_bitcell): self.bit_rows[col+dc] += 1 self.bit_cols[row+dr] += 1 - print(self.bit_rows) - print(self.bit_cols) - print('-----------------------------------') self.parent_design.cell_inst[row + dr, col + dc] = self.parent_design.add_existing_inst(inst,"bit_r{}_c{}".format(row +dr, col+dc)) - print('inst:', row+dr, col+dc) self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(row+dr, col+dc)) def connect_array(self) -> None: @@ -135,14 +127,10 @@ class pattern(): col = 0 for i in range(self.num_cores_y): for j in range (self.num_cores_x): - print("connecting {} {}".format(row,col)) self.connect_block(self.core_block, col, row) col += len(self.core_block[0]) col = 0 row += len(self.core_block) - # print(self.bit_rows) - # print(self.bit_cols) - print(self.parent_design.cell_inst) def place_inst(self, inst, offset) -> None: x = offset[0] @@ -188,13 +176,10 @@ class pattern(): col = 0 place_x = 0 for j in range (self.num_cores_x): - print("placing {} {}".format(row,col)) self.place_block(self.core_block, row, col, place_x, place_y) place_x += self.core_block_width col += len(self.core_block[0]) if(self.bit_rows.count(self.num_rows) == self.num_cols and self.bit_cols.count(self.bit_cols) == self.num_rows): - print(self.bit_rows) - print(self.bit_cols) return row += len(self.core_block) From 4d6e836b202abf8af03e8b8c23f6c90d3e927d53 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Sat, 29 Jul 2023 17:43:09 -0700 Subject: [PATCH 11/48] revert bitcell test numbers --- compiler/tests/05_bitcell_array_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/tests/05_bitcell_array_test.py b/compiler/tests/05_bitcell_array_test.py index 872986a7..a92d3653 100755 --- a/compiler/tests/05_bitcell_array_test.py +++ b/compiler/tests/05_bitcell_array_test.py @@ -31,7 +31,7 @@ class array_test(openram_test): num_spare_rows = 0 num_spare_cols = 0 - a = factory.create(module_type="bitcell_array", cols=4 + num_spare_cols, rows=2 + num_spare_rows) + a = factory.create(module_type="bitcell_array", cols=8 + num_spare_cols, rows=8 + num_spare_rows) self.local_check(a) openram.end_openram() From 811eb434599d442e9fa8f71bc72c6a9fdb8bfdf4 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Sun, 30 Jul 2023 20:06:40 -0700 Subject: [PATCH 12/48] working on updated placemet code --- compiler/modules/bitcell_array.py | 7 ++++--- compiler/modules/dummy_array.py | 28 +++++++++++++++++++--------- compiler/modules/pattern.py | 16 +++++++++++----- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 4400a508..f0a951bf 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -61,10 +61,11 @@ class bitcell_array(bitcell_base_array): def create_instances(self): self.cell_inst={} core_block = [[0 for x in range(2)] for y in range(2)] + # block[col][row core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) - core_block[0][1] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True) - core_block[1][0] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True) - core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True) + core_block[0][1] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') + core_block[1][0] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True, mirror='MY') + core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True, mirror='XY') self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.row_size, num_cols=self.column_size) self.pattern.connect_array() diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 60679bd3..387c00e5 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -6,7 +6,8 @@ from openram.sram_factory import factory from openram import OPTS from .bitcell_base_array import bitcell_base_array - +from openram.base import geometry +from .pattern import pattern class dummy_array(bitcell_base_array): """ @@ -32,7 +33,7 @@ class dummy_array(bitcell_base_array): def create_layout(self): - self.place_array("dummy_r{0}_c{1}", self.mirror) + self.place_array() self.add_layout_pins() @@ -50,13 +51,22 @@ class dummy_array(bitcell_base_array): def create_instances(self): """ Create the module instances used in this design """ - self.cell_inst = {} - for col in range(self.column_size): - for row in range(self.row_size): - name = "bit_r{0}_c{1}".format(row, col) - self.cell_inst[row, col]=self.add_inst(name=name, - mod=self.dummy_cell) - self.connect_inst(self.get_bitcell_pins(row, col)) + self.cell_inst={} + core_block = [[0 for x in range(2)] for y in range(2)] + if not self.mirror: + core_block[0][0] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) + core_block[0][1] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + core_block[1][0] = geometry.instance("core_0_1", mod=self.dummy_cell, is_bitcell=True, mirror='MY') + core_block[1][1] = geometry.instance("core_1_1", mod=self.dummy_cell, is_bitcell=True, mirror='XY') + else: + core_block[0][0] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True, mirror='MY') + core_block[0][1] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='XY') + core_block[1][0] = geometry.instance("core_0_1", mod=self.dummy_cell, is_bitcell=True) + core_block[1][1] = geometry.instance("core_1_1", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + + self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size) + self.pattern.connect_array() + def add_pins(self): # bitline pins are not added because they are floating diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index 7bc75a1e..c9f90f8a 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -177,13 +177,19 @@ class pattern(): place_x = 0 for j in range (self.num_cores_x): self.place_block(self.core_block, row, col, place_x, place_y) - place_x += self.core_block_width - col += len(self.core_block[0]) if(self.bit_rows.count(self.num_rows) == self.num_cols and self.bit_cols.count(self.bit_cols) == self.num_rows): + self.parent_design.width = place_x + self.parent_design.height= place_y + print("early") return + self.parent_design.width = place_x + place_x += self.core_block_width + col += len(self.core_block[0]) + row += len(self.core_block) + self.parent_design.height = place_y place_y += self.core_block_height - self.parent_design.width = place_x - self.parent_design.height = place_y - + print(self.parent_design.width, self.parent_design.height) + print("late") + From 6b12d442fabb9451b327f6b6496ce54aaf5c1dca Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Sun, 30 Jul 2023 22:01:30 -0700 Subject: [PATCH 13/48] placement fixed --- compiler/modules/pattern.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index c9f90f8a..3cbd5a08 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -143,9 +143,11 @@ class pattern(): - def place_block(self, block: block, row: int, col: int, place_x: float, place_y: float) -> None: + def place_block(self, block: block, row: int, col: int, place_x: float, place_y: float, bx, by): x_offset = 0 y_offset = 0 + bounding_x = bx + bounding_y = by for dr in range(len(block)): for dc in range(len(block[0])): if(self.bit_rows.count(self.num_rows) != self.num_cols and self.bit_cols.count(self.bit_cols) != self.num_rows): @@ -159,12 +161,16 @@ class pattern(): self.bit_rows[col+dc] += 1 self.bit_cols[row+dr] += 1 self.place_inst(inst, (place_x + x_offset, place_y + y_offset)) + if(place_x + x_offset + inst.width > bounding_x): + bounding_x = place_x + x_offset + inst.width + if(place_y + y_offset + inst.height > bounding_y): + bounding_y = place_y + y_offset + inst.height x_offset += inst.width x_offset = 0 y_offset += inst.height + return bounding_x, bounding_y - - def place_array(self) -> None: + def place_array(self): self.bit_rows = [] self.bit_cols = [] @@ -172,24 +178,18 @@ class pattern(): col = 0 place_x = 0 place_y = 0 + bounding_x = 0 + bounding_y = 0 for i in range(self.num_cores_y): col = 0 place_x = 0 - for j in range (self.num_cores_x): - self.place_block(self.core_block, row, col, place_x, place_y) + for j in range(self.num_cores_x): + self.parent_design.width, self.parent_design.height= self.place_block(self.core_block, row, col, place_x, place_y, bounding_x, bounding_y) if(self.bit_rows.count(self.num_rows) == self.num_cols and self.bit_cols.count(self.bit_cols) == self.num_rows): - self.parent_design.width = place_x - self.parent_design.height= place_y - print("early") return - self.parent_design.width = place_x place_x += self.core_block_width col += len(self.core_block[0]) - row += len(self.core_block) - self.parent_design.height = place_y place_y += self.core_block_height - print(self.parent_design.width, self.parent_design.height) - print("late") From d9d8cb298336099d0fe659a56f7405628389bff2 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Sun, 30 Jul 2023 22:39:23 -0700 Subject: [PATCH 14/48] capped norbl scmos passing --- compiler/modules/bitcell_array.py | 13 +++++++++---- compiler/modules/dummy_array.py | 15 +++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index f0a951bf..c4aa8c5d 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -62,12 +62,17 @@ class bitcell_array(bitcell_base_array): self.cell_inst={} core_block = [[0 for x in range(2)] for y in range(2)] # block[col][row - core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) - core_block[0][1] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') - core_block[1][0] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True, mirror='MY') - core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True, mirror='XY') + core_block[(0 + self.column_offset) %2][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) + core_block[(0 + self.column_offset) %2][1] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') + core_block[(1 + self.column_offset) %2][0] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True, mirror='MY') + core_block[(1 + self.column_offset) %2][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True, mirror='XY') + self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.row_size, num_cols=self.column_size) self.pattern.connect_array() + + for key in self.cell_inst.keys(): + if key != (0,0): + self.trim_insts.add(self.cell_inst[key].name) def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 387c00e5..f890d98e 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -53,16 +53,11 @@ class dummy_array(bitcell_base_array): """ Create the module instances used in this design """ self.cell_inst={} core_block = [[0 for x in range(2)] for y in range(2)] - if not self.mirror: - core_block[0][0] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) - core_block[0][1] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - core_block[1][0] = geometry.instance("core_0_1", mod=self.dummy_cell, is_bitcell=True, mirror='MY') - core_block[1][1] = geometry.instance("core_1_1", mod=self.dummy_cell, is_bitcell=True, mirror='XY') - else: - core_block[0][0] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True, mirror='MY') - core_block[0][1] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='XY') - core_block[1][0] = geometry.instance("core_0_1", mod=self.dummy_cell, is_bitcell=True) - core_block[1][1] = geometry.instance("core_1_1", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + core_block[(0 + self.column_offset) %2][(0+self.mirror) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) + core_block[(0 + self.column_offset) %2][(1+self.mirror) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + core_block[(1 + self.column_offset) %2][(0+self.mirror) %2] = geometry.instance("core_0_1", mod=self.dummy_cell, is_bitcell=True, mirror='MY') + core_block[(1 + self.column_offset) %2][(1+self.mirror) %2] = geometry.instance("core_1_1", mod=self.dummy_cell, is_bitcell=True, mirror='XY') + self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size) self.pattern.connect_array() From 5a764c9d43ba7e7c42b693c8b60ef41dde23b608 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 1 Aug 2023 19:05:55 -0700 Subject: [PATCH 15/48] remove MY mirroring in scmos --- compiler/modules/bitcell_array.py | 10 ++++------ compiler/modules/dummy_array.py | 8 +++----- compiler/modules/pattern.py | 2 +- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index c4aa8c5d..6d546baa 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -60,12 +60,10 @@ class bitcell_array(bitcell_base_array): def create_instances(self): self.cell_inst={} - core_block = [[0 for x in range(2)] for y in range(2)] - # block[col][row - core_block[(0 + self.column_offset) %2][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) - core_block[(0 + self.column_offset) %2][1] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') - core_block[(1 + self.column_offset) %2][0] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True, mirror='MY') - core_block[(1 + self.column_offset) %2][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True, mirror='XY') + core_block = [[0 for x in range(1)] for y in range(2)] + # block[row][col] + core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) + core_block[1][0] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.row_size, num_cols=self.column_size) self.pattern.connect_array() diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index f890d98e..73d6dc0e 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -52,11 +52,9 @@ class dummy_array(bitcell_base_array): def create_instances(self): """ Create the module instances used in this design """ self.cell_inst={} - core_block = [[0 for x in range(2)] for y in range(2)] - core_block[(0 + self.column_offset) %2][(0+self.mirror) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) - core_block[(0 + self.column_offset) %2][(1+self.mirror) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - core_block[(1 + self.column_offset) %2][(0+self.mirror) %2] = geometry.instance("core_0_1", mod=self.dummy_cell, is_bitcell=True, mirror='MY') - core_block[(1 + self.column_offset) %2][(1+self.mirror) %2] = geometry.instance("core_1_1", mod=self.dummy_cell, is_bitcell=True, mirror='XY') + core_block = [[0 for x in range(1)] for y in range(2)] + core_block[(0+self.mirror) %2][0] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) + core_block[(1+self.mirror) %2][0] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size) diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index 3cbd5a08..01f106bc 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -108,7 +108,7 @@ class pattern(): for dr in range(len(block)): for dc in range(len(block[0])): if(self.bit_rows.count(self.num_rows) != self.num_cols and self.bit_cols.count(self.bit_cols) != self.num_rows): - inst = block[dc][dr] + inst = block[dr][dc] if(len(self.bit_rows) <= col + dc): self.bit_rows.append(0) if(len(self.bit_cols) <= row + dr): From 61cfa55d758c9aaf680aa30a0435549df3816339 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 2 Aug 2023 15:19:48 -0700 Subject: [PATCH 16/48] fix replica col --- compiler/modules/bitcell_array.py | 1 - compiler/modules/replica_column.py | 72 +++++++++--------------------- 2 files changed, 22 insertions(+), 51 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 6d546baa..d847ac4a 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -61,7 +61,6 @@ class bitcell_array(bitcell_base_array): def create_instances(self): self.cell_inst={} core_block = [[0 for x in range(1)] for y in range(2)] - # block[row][col] core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) core_block[1][0] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 79a6e276..54631ccf 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -9,7 +9,8 @@ from openram.sram_factory import factory from openram.tech import layer_properties as layer_props from openram import OPTS from .bitcell_base_array import bitcell_base_array - +from openram.base import geometry +from openram.modules import pattern class replica_column(bitcell_base_array): """ @@ -59,10 +60,7 @@ class replica_column(bitcell_base_array): self.create_instances() def create_layout(self): - self.place_instances() - - self.height = self.cell_inst[-1].uy() - self.width = self.cell_inst[0].rx() + self.place_array() self.add_layout_pins() @@ -88,63 +86,37 @@ class replica_column(bitcell_base_array): self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell) def create_instances(self): - self.cell_inst = [] + self.cell_inst = {} + core_block = [[0 for x in range(1)] for y in range(self.total_size)] + current_row = self.row_start for row in range(self.total_size): - name = "rbc_{0}".format(row) - # Regular array cells are replica cells # Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell. # All other cells are dummies if (row == self.replica_bit) or (row >= self.row_start and row < self.row_end): - self.cell_inst.append(self.add_inst(name=name, - mod=self.replica_cell)) - self.connect_inst(self.get_bitcell_pins(row, 0)) - else: - self.cell_inst.append(self.add_inst(name=name, - mod=self.dummy_cell)) - self.connect_inst(self.get_bitcell_pins(row, 0)) - - def place_instances(self): - # Flip the mirrors if we have an odd number of replica+dummy rows at the bottom - # so that we will start with mirroring rather than not mirroring - rbl_offset = (self.left_rbl) % 2 - - # if our bitcells are mirrored on the y axis, check if we are in global - # column that needs to be flipped. - dir_y = False - xoffset = 0 - if self.cell.mirror.y and self.column_offset % 2: - dir_y = True - xoffset = self.replica_cell.width - - for row in range(self.total_size): - # name = "bit_r{0}_{1}".format(row, "rbl") - dir_x = self.cell.mirror.x and (row + rbl_offset) % 2 - - offset = vector(xoffset, self.cell.height * (row + (row + rbl_offset) % 2)) - - if dir_x and dir_y: - dir_key = "XY" - elif dir_x: - dir_key = "MX" - elif dir_y: - dir_key = "MY" - else: - dir_key = "" - - self.cell_inst[row].place(offset=offset, - mirror=dir_key) + if current_row % 2: + core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MX') + else: + core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True) + else: + if current_row %2: + core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='MX') + else: + core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True) + current_row += 1 + self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.total_size, num_cols=self.column_size) + self.pattern.connect_array() def add_layout_pins(self): for port in self.all_ports: - bl_pin = self.cell_inst[0].get_pin(self.cell.get_bl_name(port)) + bl_pin = self.cell_inst[0,0].get_pin(self.cell.get_bl_name(port)) self.add_layout_pin(text="bl_{0}_{1}".format(port, 0), layer=bl_pin.layer, offset=bl_pin.ll().scale(1, 0), width=bl_pin.width(), height=self.height) - bl_pin = self.cell_inst[0].get_pin(self.cell.get_br_name(port)) + bl_pin = self.cell_inst[0,0].get_pin(self.cell.get_br_name(port)) self.add_layout_pin(text="br_{0}_{1}".format(port, 0), layer=bl_pin.layer, offset=bl_pin.ll().scale(1, 0), @@ -153,7 +125,7 @@ class replica_column(bitcell_base_array): for port in self.all_ports: for row in range(self.total_size): - wl_pin = self.cell_inst[row].get_pin(self.cell.get_wl_name(port)) + wl_pin = self.cell_inst[row,0].get_pin(self.cell.get_wl_name(port)) self.add_layout_pin(text="wl_{0}_{1}".format(port, row), layer=wl_pin.layer, offset=wl_pin.ll().scale(0, 1), @@ -162,7 +134,7 @@ class replica_column(bitcell_base_array): def route_supplies(self): - for inst in self.cell_inst: + for inst in self.cell_inst.values(): for pin_name in ["vdd", "gnd"]: self.copy_layout_pin(inst, pin_name) From 5e01bad2ee44253af2a09daf6a9af4e8a1faec0f Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 3 Aug 2023 00:42:42 -0700 Subject: [PATCH 17/48] remove whitespace --- compiler/modules/bitcell_array.py | 2 +- compiler/modules/dummy_array.py | 6 ++--- compiler/modules/pattern.py | 35 +++++++++++++++++------------- compiler/modules/replica_column.py | 4 ++-- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index d847ac4a..b05cfd23 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -66,7 +66,7 @@ class bitcell_array(bitcell_base_array): self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.row_size, num_cols=self.column_size) self.pattern.connect_array() - + for key in self.cell_inst.keys(): if key != (0,0): self.trim_insts.add(self.cell_inst[key].name) diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 73d6dc0e..a8ec957b 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -6,7 +6,7 @@ from openram.sram_factory import factory from openram import OPTS from .bitcell_base_array import bitcell_base_array -from openram.base import geometry +from openram.base import geometry from .pattern import pattern class dummy_array(bitcell_base_array): @@ -53,8 +53,8 @@ class dummy_array(bitcell_base_array): """ Create the module instances used in this design """ self.cell_inst={} core_block = [[0 for x in range(1)] for y in range(2)] - core_block[(0+self.mirror) %2][0] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) - core_block[(1+self.mirror) %2][0] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + core_block[0][(0+self.mirror) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) + core_block[0][(1+self.mirror) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size) diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index 01f106bc..00c21783 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -1,3 +1,8 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz +# All rights reserved. +# from openram import debug from openram.base.geometry import instance,geometry from typing import List @@ -7,7 +12,7 @@ from openram.globals import OPTS from math import ceil class pattern(): """ - This class is used to desribe the internals of a bitcell array. It describes + This class is used to desribe the internals of a bitcell array. It describes instance modules, rotation, and ordering """ @@ -22,18 +27,18 @@ class pattern(): num_cores_y: Optional[int] = 0, cores_per_x_block: int = 1, cores_per_y_block: int = 1, - x_block: Optional[block] = None, - y_block: Optional[block] = None, + x_block: Optional[block] = None, + y_block: Optional[block] = None, xy_block: Optional[block] = None, initial_x_block:bool = False, initial_y_block:bool = False, final_x_block:bool = False, final_y_block:bool = False - ): + ): """ a "block" is a 2d list of instances core_block defines the main block that is tiled - num_core defines the number of times the core block is to be tiled + num_core defines the number of times the core block is to be tiled (i.e. bitcells in dimension / bitcells in core_block) x_block defines a block that is inserted to the right of the core_block y_block defines a block that is inserted below the core block @@ -43,7 +48,7 @@ class pattern(): self.parent_design = parent_design self.name = name self.core_block = core_block - self.num_rows = num_rows + self.num_rows = num_rows self.num_cols = num_cols self.num_cores_x = num_cores_x self.num_cores_y = num_cores_y @@ -62,22 +67,22 @@ class pattern(): self.final_x_block = final_x_block self.final_y_block = final_y_block if not OPTS.netlist_only: - self.verify_interblock_dimensions() - + self.verify_interblock_dimensions() + def compute_and_verify_intrablock_dimensions(self, block: block) -> List[int]: for row in block: row_height = row[0].height for inst in row: debug.check(row_height == inst.height, "intrablock instances within the same row are different heights") - + for y in range(len(block[0])): debug.check(all([row[y].width for row in block]), "intrablock instances within the same column are different widths") - + block_width = sum([instance.width for instance in block[0]]) - block_height = sum([row[0].height for row in block]) + block_height = sum([row[0].height for row in block]) return [block_width, block_height] - + def verify_interblock_dimensions(self) -> None: """ Ensure the individual blocks are valid and interblock dimensions are valid @@ -95,14 +100,14 @@ class pattern(): (self.y_block_width, self.y_block_height) = self.compute_and_verify_intrablock_dimensions(self.y_block) if self.xy_block: (self.xy_block_width, self.xy_block_height) = self.compute_and_verify_intrablock_dimensions(self.xy_block) - if(self.x_block): + if(self.x_block): debug.check(self.core_block_width * self.cores_per_x_block == self.x_block_width, "core_block does not align with x_block") if(self.y_block): debug.check(self.core_block_height * self.cores_per_y_block == self.y_block_height, "core_block does not aligns with y_block") if(self.xy_block): debug.check(self.xy_block_height == self.x_block_height, "xy_block does not align with x_block") debug.check(self.xy_block_width == self.y_block_width, "xy_block does not align with y_block") - + def connect_block(self, block: block, col: int, row: int): for dr in range(len(block)): @@ -141,7 +146,7 @@ class pattern(): x += inst.width inst.place((x, y), inst.mirror, inst.rotate) - + def place_block(self, block: block, row: int, col: int, place_x: float, place_y: float, bx, by): x_offset = 0 diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 54631ccf..944b9b94 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -87,7 +87,7 @@ class replica_column(bitcell_base_array): def create_instances(self): self.cell_inst = {} - core_block = [[0 for x in range(1)] for y in range(self.total_size)] + core_block = [[0 for x in range(1)] for y in range(self.total_size)] current_row = self.row_start for row in range(self.total_size): @@ -99,7 +99,7 @@ class replica_column(bitcell_base_array): core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MX') else: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True) - else: + else: if current_row %2: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='MX') else: From 1aa04db2b601fc714e5b0ba9a87af2741e5f1509 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 3 Aug 2023 16:24:24 -0700 Subject: [PATCH 18/48] add isntance naming templates --- compiler/characterizer/simulation.py | 4 ++++ compiler/modules/bitcell_array.py | 8 ++++---- compiler/modules/dummy_array.py | 6 +++--- compiler/modules/pattern.py | 4 +++- compiler/modules/replica_column.py | 2 +- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index c49f0e63..3ef33d10 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -619,6 +619,10 @@ class simulation(): bl_names = [] exclude_set = self.get_bl_name_search_exclusions() + print(paths) + print(cell_bl) + print(cell_mod) + print(exclude_set) for int_net in [cell_bl, cell_br]: bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set)) if OPTS.use_pex and OPTS.pex_exe[0] != "calibre": diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index b05cfd23..319691ba 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -64,12 +64,12 @@ class bitcell_array(bitcell_base_array): core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) core_block[1][0] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') - self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.row_size, num_cols=self.column_size) + self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.row_size, num_cols=self.column_size,name_template="bit_r{0}_c{1}") self.pattern.connect_array() - for key in self.cell_inst.keys(): - if key != (0,0): - self.trim_insts.add(self.cell_inst[key].name) + #for key in self.cell_inst.keys(): + # if key != (0,0): + # self.trim_insts.add(self.cell_inst[key].name) def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index a8ec957b..ced3de20 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -53,11 +53,11 @@ class dummy_array(bitcell_base_array): """ Create the module instances used in this design """ self.cell_inst={} core_block = [[0 for x in range(1)] for y in range(2)] - core_block[0][(0+self.mirror) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) - core_block[0][(1+self.mirror) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + core_block[(0+self.mirror) %2][0] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) + core_block[(1+self.mirror) %2][0] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size) + self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size, name_template="bit_r{0}_c{1}") self.pattern.connect_array() diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index 00c21783..feb04927 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -23,6 +23,7 @@ class pattern(): core_block:block, num_rows:int, num_cols:int, + name_template, num_cores_x: Optional[int] = 0, num_cores_y: Optional[int] = 0, cores_per_x_block: int = 1, @@ -50,6 +51,7 @@ class pattern(): self.core_block = core_block self.num_rows = num_rows self.num_cols = num_cols + self.name_template = name_template self.num_cores_x = num_cores_x self.num_cores_y = num_cores_y if num_cores_x == 0: @@ -122,7 +124,7 @@ class pattern(): if(inst.is_bitcell): self.bit_rows[col+dc] += 1 self.bit_cols[row+dr] += 1 - self.parent_design.cell_inst[row + dr, col + dc] = self.parent_design.add_existing_inst(inst,"bit_r{}_c{}".format(row +dr, col+dc)) + self.parent_design.cell_inst[row + dr, col + dc] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc)) self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(row+dr, col+dc)) def connect_array(self) -> None: diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 944b9b94..84f8f1c6 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -105,7 +105,7 @@ class replica_column(bitcell_base_array): else: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True) current_row += 1 - self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.total_size, num_cols=self.column_size) + self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.total_size, num_cols=self.column_size, name_template="rbc_r{0}_c{1}") self.pattern.connect_array() def add_layout_pins(self): From 6f4ee4ad2dd2bd30cc1cebc5ba9385240ed5de12 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 9 Aug 2023 14:06:35 -0700 Subject: [PATCH 19/48] pass modules by pointer not value --- compiler/base/hierarchy_layout.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index b91af980..8c595254 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -478,12 +478,13 @@ class layout(): return self.insts[-1] def add_existing_inst(self, inst, name): - inst = deepcopy(inst) - self.mods.add(inst.mod) + new_inst = deepcopy(inst) + new_inst.mod = inst.mod + self.mods.add(new_inst.mod) if name: - inst.name = name - self.inst_names.add(inst.name) - self.insts.append(inst) + new_inst.name = name + self.inst_names.add(new_inst.name) + self.insts.append(new_inst) debug.info(3, "adding existing instance{}".format(self.insts[-1])) return self.insts[-1] From 3fc2a1e229406e0e54e58ad69f8e28d77ee0f858 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 9 Aug 2023 22:59:43 -0700 Subject: [PATCH 20/48] remove print statements --- compiler/characterizer/simulation.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index 3ef33d10..c49f0e63 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -619,10 +619,6 @@ class simulation(): bl_names = [] exclude_set = self.get_bl_name_search_exclusions() - print(paths) - print(cell_bl) - print(cell_mod) - print(exclude_set) for int_net in [cell_bl, cell_br]: bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set)) if OPTS.use_pex and OPTS.pex_exe[0] != "calibre": From be72bcfa011c66a1473d1bec6741c6acd505aa45 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 10 Aug 2023 00:34:16 -0700 Subject: [PATCH 21/48] trim bitcells and fix replica column excluding --- compiler/modules/bitcell_array.py | 6 +++--- compiler/modules/replica_column.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 319691ba..582829cf 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -67,9 +67,9 @@ class bitcell_array(bitcell_base_array): self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.row_size, num_cols=self.column_size,name_template="bit_r{0}_c{1}") self.pattern.connect_array() - #for key in self.cell_inst.keys(): - # if key != (0,0): - # self.trim_insts.add(self.cell_inst[key].name) + for key in self.cell_inst.keys(): + if key != (0,0): + self.trim_insts.add(self.cell_inst[key].name) def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 84f8f1c6..c6cdd492 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -180,4 +180,4 @@ class replica_column(bitcell_base_array): for row, cell in enumerate(self.cell_inst): if row != self.replica_bit: - self.graph_inst_exclude.add(cell) + self.graph_inst_exclude.add(self.cell_inst[cell]) From 0111620c918f7d6227a36988d285b1036e497c76 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Fri, 11 Aug 2023 13:45:24 -0700 Subject: [PATCH 22/48] deepcopy overide for instance --- compiler/base/geometry.py | 10 +++++++++- compiler/base/hierarchy_layout.py | 1 - 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 0f26f02e..e8589266 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -200,9 +200,17 @@ class instance(geometry): self.width = round_to_grid(mod.width) self.height = round_to_grid(mod.height) self.compute_boundary(offset, mirror, rotate) - debug.info(4, "creating instance: " + self.name) + def __deepcopy__(original, memo): + new_inst = instance(original.name+"_copy", original.mod) + new_inst.rotate = original.rotate + new_inst.offset = original.offset + new_inst.mirror = original.mirror + new_inst.is_bitcell = original.is_bitcell + return new_inst + + def get_blockages(self, lpp, top=False): """ Retrieve blockages of all modules in this instance. Apply the transform of the instance placement to give absolute blockages.""" diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 7965f87c..2b636d47 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -479,7 +479,6 @@ class layout(): def add_existing_inst(self, inst, name): new_inst = deepcopy(inst) - new_inst.mod = inst.mod self.mods.add(new_inst.mod) if name: new_inst.name = name From 74c12f944ff47567a85c7e881252b5844cc6bf8b Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Mon, 14 Aug 2023 13:59:31 -0700 Subject: [PATCH 23/48] mirror skywater dp --- compiler/modules/bitcell_array.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 582829cf..08dff652 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -60,9 +60,17 @@ class bitcell_array(bitcell_base_array): def create_instances(self): self.cell_inst={} - core_block = [[0 for x in range(1)] for y in range(2)] - core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) - core_block[1][0] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') + if self.cell.mirror.y: + core_block = [[0 for x in range(2)] for y in range(2)] + core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) + core_block[1][0] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') + core_block[0][1] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True, mirror='MY') + core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True, mirror='XY') + else: + core_block = [[0 for x in range(1)] for y in range(2)] + core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) + core_block[1][0] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') + self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.row_size, num_cols=self.column_size,name_template="bit_r{0}_c{1}") self.pattern.connect_array() From 30ee5a0a2e1a1e7caeb05e2c29a3f66f44de5cf3 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Mon, 14 Aug 2023 14:19:58 -0700 Subject: [PATCH 24/48] add dummy cell mirroring for sky130 --- compiler/modules/dummy_array.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index ced3de20..0bc6c065 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -52,9 +52,16 @@ class dummy_array(bitcell_base_array): def create_instances(self): """ Create the module instances used in this design """ self.cell_inst={} - core_block = [[0 for x in range(1)] for y in range(2)] - core_block[(0+self.mirror) %2][0] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) - core_block[(1+self.mirror) %2][0] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + if self.cell.mirror.y: + core_block = [[0 for x in range(2)] for y in range(2)] + core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) + core_block[1][0] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') + core_block[0][1] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True, mirror='MY') + core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True, mirror='XY') + else: + core_block = [[0 for x in range(1)] for y in range(2)] + core_block[(0+self.mirror) %2][0] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) + core_block[(1+self.mirror) %2][0] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size, name_template="bit_r{0}_c{1}") From 0391bf6593cac847f3dc728cc3a2cadd9949b183 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Mon, 14 Aug 2023 14:25:57 -0700 Subject: [PATCH 25/48] add dummy mirroring for sky130 dp --- compiler/modules/dummy_array.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 0bc6c065..b31f8c00 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -54,10 +54,10 @@ class dummy_array(bitcell_base_array): self.cell_inst={} if self.cell.mirror.y: core_block = [[0 for x in range(2)] for y in range(2)] - core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) - core_block[1][0] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') - core_block[0][1] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True, mirror='MY') - core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True, mirror='XY') + core_block[(0+self.mirror) %2][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) + core_block[(1+self.mirror) %2][0] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') + core_block[(0+self.mirror) %2][1] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True, mirror='MY') + core_block[(1+self.mirror) %2][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True, mirror='XY') else: core_block = [[0 for x in range(1)] for y in range(2)] core_block[(0+self.mirror) %2][0] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) From cd1b0f973d6c60452734a2df1610498fa51f1b88 Mon Sep 17 00:00:00 2001 From: Sam Crow Date: Mon, 14 Aug 2023 18:30:40 -0700 Subject: [PATCH 26/48] Revert pin/net spice object work This reverts commits 01116 6e3e9 2ced8 c67fd 2b9e7 bfabe 09aa3 5907c aa717 478c7 45b88 d0339 e15fe 7581d c8c43 146ef --- compiler/base/contact.py | 2 + compiler/base/design.py | 8 +- compiler/base/geometry.py | 54 +--- compiler/base/hierarchy_design.py | 18 +- compiler/base/hierarchy_layout.py | 4 +- compiler/base/hierarchy_spice.py | 407 ++++++++++------------------- compiler/base/lef.py | 2 +- compiler/modules/column_decoder.py | 2 +- compiler/modules/pbitcell.py | 2 +- 9 files changed, 166 insertions(+), 333 deletions(-) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 84f60d0e..5fe59bcb 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -66,6 +66,8 @@ class contact(hierarchy_design): self.offset = vector(0, 0) self.implant_type = implant_type self.well_type = well_type + # Module does not have pins, but has empty pin list. + self.pins = [] self.create_layout() def create_layout(self): diff --git a/compiler/base/design.py b/compiler/base/design.py index 69d213d0..87030d4c 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -41,17 +41,17 @@ class design(hierarchy_design): if prop and prop.hard_cell: # The pins get added from the spice file, so just check # that they matched here - debug.check(prop.port_names == list(self.pins), - "Custom cell pin names do not match spice file:\n{0} vs {1}".format(prop.port_names, list(self.pins))) + debug.check(prop.port_names == self.pins, + "Custom cell pin names do not match spice file:\n{0} vs {1}".format(prop.port_names, self.pins)) self.add_pin_indices(prop.port_indices) self.add_pin_names(prop.port_map) - self.update_pin_types(prop.port_types) + self.add_pin_types(prop.port_types) (width, height) = utils.get_libcell_size(self.cell_name, GDS["unit"], layer[prop.boundary_layer]) - self.pin_map = utils.get_libcell_pins(list(self.pins), + self.pin_map = utils.get_libcell_pins(self.pins, self.cell_name, GDS["unit"]) diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index e8589266..9e7172bf 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -161,8 +161,8 @@ class geometry: class instance(geometry): """ - An instance of a module with a specified location, rotation, - spice pins, and spice nets + An instance of an instance/module with a specified location and + rotation """ def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0, is_bitcell=False): """Initializes an instance to represent a module""" @@ -177,18 +177,6 @@ class instance(geometry): self.offset = vector(offset).snap_to_grid() self.mirror = mirror self.is_bitcell = is_bitcell - # track if the instance's spice pin connections have been made - self.connected = False - - # deepcopy because this instance needs to - # change attributes in these spice objects - self.spice_pins = copy.deepcopy(self.mod.pins) - self.spice_nets = copy.deepcopy(self.mod.nets) - for pin in self.spice_pins.values(): - pin.set_inst(self) - for net in self.spice_nets.values(): - net.set_inst(self) - if OPTS.netlist_only: self.width = 0 self.height = 0 @@ -200,17 +188,9 @@ class instance(geometry): self.width = round_to_grid(mod.width) self.height = round_to_grid(mod.height) self.compute_boundary(offset, mirror, rotate) + debug.info(4, "creating instance: " + self.name) - def __deepcopy__(original, memo): - new_inst = instance(original.name+"_copy", original.mod) - new_inst.rotate = original.rotate - new_inst.offset = original.offset - new_inst.mirror = original.mirror - new_inst.is_bitcell = original.is_bitcell - return new_inst - - def get_blockages(self, lpp, top=False): """ Retrieve blockages of all modules in this instance. Apply the transform of the instance placement to give absolute blockages.""" @@ -295,34 +275,6 @@ class instance(geometry): new_pins.append(p) return new_pins - def connect_spice_pins(self, nets_list): - """ - add the connection between instance pins and module nets - to both of their respective objects - nets_list must be the same length as self.spice_pins - """ - if len(nets_list) == 0 and len(self.spice_pins) == 0: - # this is the only valid case to skip the following debug check - # because this with no pins are often connected arbitrarily - self.connected = True - return - debug.check(not self.connected, - "instance {} has already been connected".format(self.name)) - debug.check(len(self.spice_pins) == len(nets_list), - "must provide list of nets the same length as pin list\ - when connecting an instance") - for pin in self.spice_pins.values(): - net = nets_list.pop(0) - pin.set_inst_net(net) - net.connect_pin(pin) - self.connected = True - - def get_connections(self): - conns = [] - for pin in self.spice_pins.values(): - conns.append(pin.inst_net.name) - return conns - def calculate_transform(self, node): #set up the rotation matrix angle = math.radians(float(node.rotate)) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 31a1e191..42c0543d 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -135,11 +135,11 @@ class hierarchy_design(spice, layout): # Translate port names to external nets if len(port_nets) != len(self.pins): debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets, - list(self.pins)), + self.pins), 1) - port_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)} + port_dict = {pin: port for pin, port in zip(self.pins, port_nets)} debug.info(3, "Instance name={}".format(inst_name)) - for subinst, conns in zip(self.insts, self.get_instance_connections()): + for subinst, conns in zip(self.insts, self.conns): if subinst in self.graph_inst_exclude: continue subinst_name = inst_name + "{}x".format(OPTS.hier_seperator) + subinst.name @@ -153,11 +153,11 @@ class hierarchy_design(spice, layout): # Translate port names to external nets if len(port_nets) != len(self.pins): debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets, - list(self.pins)), + self.pins), 1) - port_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)} + port_dict = {pin: port for pin, port in zip(self.pins, port_nets)} debug.info(3, "Instance name={}".format(inst_name)) - for subinst, conns in zip(self.insts, self.get_instance_connections()): + for subinst, conns in zip(self.insts, self.conns): subinst_name = inst_name + "{}x".format(OPTS.hier_seperator) + subinst.name subinst_ports = self.translate_nets(conns, port_dict, inst_name) for si_port, conn in zip(subinst_ports, conns): @@ -186,7 +186,7 @@ class hierarchy_design(spice, layout): """ # The final pin names will depend on the spice hierarchy, so # they are passed as an input. - pin_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)} + pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} input_pins = self.get_inputs() output_pins = self.get_outputs() inout_pins = self.get_inouts() @@ -197,7 +197,7 @@ class hierarchy_design(spice, layout): def __str__(self): """ override print function output """ - pins = ",".join(list(self.pins)) + pins = ",".join(self.pins) insts = [" {}".format(x) for x in self.insts] objs = [" {}".format(x) for x in self.objs] s = "********** design {0} **********".format(self.cell_name) @@ -208,7 +208,7 @@ class hierarchy_design(spice, layout): def __repr__(self): """ override print function output """ - text="( design: " + self.name + " pins=" + str(list(self.pins)) + " " + str(self.width) + "x" + str(self.height) + " )\n" + text="( design: " + self.name + " pins=" + str(self.pins) + " " + str(self.width) + "x" + str(self.height) + " )\n" for i in self.objs: text+=str(i) + ",\n" for i in self.insts: diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 2b636d47..8c595254 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -479,6 +479,7 @@ class layout(): def add_existing_inst(self, inst, name): new_inst = deepcopy(inst) + new_inst.mod = inst.mod self.mods.add(new_inst.mod) if name: new_inst.name = name @@ -640,7 +641,7 @@ class layout(): """ Return a pin list of all pins """ - return list(self.pins) + return self.pins def copy_layout_pin(self, instance, pin_name, new_name="", relative_offset=vector(0, 0)): """ @@ -1534,7 +1535,6 @@ class layout(): """ Return the pin shapes as blockages for non-top-level blocks. """ # FIXME: We don't have a body contact in ptx, so just ignore it for now import copy - # FIXME: this may not work now that self.pins is a dict as defined in hierarchy_spice pin_names = copy.deepcopy(self.pins) if self.name.startswith("pmos") or self.name.startswith("nmos"): pin_names.remove("B") diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 2294208e..64dc0b2b 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -13,7 +13,6 @@ from pprint import pformat from openram import debug from openram import tech from openram import OPTS -from collections import OrderedDict from .delay_data import delay_data from .wire_spice_model import wire_spice_model from .power_data import power_data @@ -50,18 +49,20 @@ class spice(): if not os.path.exists(self.lvs_file): self.lvs_file = self.sp_file + self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "BIAS", "POWER", "GROUND"] # Holds subckts/mods for this module self.mods = set() # Holds the pins for this module (in order) - # on Python3.7+ regular dictionaries guarantee order too, but we allow use of v3.5+ - self.pins = OrderedDict() + self.pins = [] + # The type map of each pin: INPUT, OUTPUT, INOUT, POWER, GROUND + # for each instance, this is the set of nets/nodes that map to the pins for this instance + self.pin_type = {} # An (optional) list of indices to reorder the pins to match the spice. self.pin_indices = [] # THE CONNECTIONS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the # Spice format) - # internal nets, which may or may not be connected to pins of the same name - self.nets = {} - # If this is set, it will not output subckt or instances of this (for row/col caps etc.) + self.conns = [] + # If this is set, it will out output subckt or isntances of this (for row/col caps etc.) self.no_instances = False # If we are doing a trimmed netlist, these are the instance that will be filtered self.trim_insts = set() @@ -89,114 +90,128 @@ class spice(): def add_pin(self, name, pin_type="INOUT"): """ Adds a pin to the pins list. Default type is INOUT signal. """ - debug.check(name not in self.pins, "cannot add duplicate spice pin {}".format(name)) - self.pins[name] = pin_spice(name, pin_type, self) + self.pins.append(name) + self.pin_type[name]=pin_type + debug.check(pin_type in self.valid_signal_types, + "Invalid signaltype for {0}: {1}".format(name, + pin_type)) def add_pin_list(self, pin_list, pin_type="INOUT"): """ Adds a pin_list to the pins list """ - # The pin type list can be a single type for all pins + # The type list can be a single type for all pins # or a list that is the same length as the pin list. - if isinstance(pin_type, str): + if type(pin_type)==str: for pin in pin_list: + debug.check(pin_type in self.valid_signal_types, + "Invalid signaltype for {0}: {1}".format(pin, + pin_type)) self.add_pin(pin, pin_type) elif len(pin_type)==len(pin_list): - for (pin, type) in zip(pin_list, pin_type): - self.add_pin(pin, type) + for (pin, ptype) in zip(pin_list, pin_type): + debug.check(ptype in self.valid_signal_types, + "Invalid signaltype for {0}: {1}".format(pin, + ptype)) + self.add_pin(pin, ptype) else: - debug.error("Pin type must be a string or list of strings the same length as pin_list", -1) + debug.error("Mismatch in type and pin list lengths.", -1) def add_pin_indices(self, index_list): - """ Add pin indices for all the cell's pins. """ + """ + Add pin indices for all the cell's pins. + """ self.pin_indices = index_list def get_ordered_inputs(self, input_list): - """ Return the inputs reordered to match the pins. """ + """ + Return the inputs reordered to match the pins. + """ if not self.pin_indices: return input_list new_list = [input_list[x] for x in self.pin_indices] return new_list - def update_pin_types(self, type_list): - """ Change pin types for all the cell's pins. """ - debug.check(len(type_list) == len(self.pins), - "{} spice subcircuit number of port types does not match number of pins\ - \n pin names={}\n port types={}".format(self.name, list(self.pins), type_list)) - for pin, type in zip(self.pins.values(), type_list): - pin.set_type(type) + def add_pin_types(self, type_list): + """ + Add pin types for all the cell's pins. + """ + # This only works if self.pins == bitcell.pin_names + if len(type_list) != len(self.pins): + debug.error("{} spice subcircuit number of port types does not match number of pins\ + \n SPICE names={}\ + \n Module names={}\ + ".format(self.name, self.pins, type_list), 1) + self.pin_type = {pin: type for pin, type in zip(self.pins, type_list)} def get_pin_type(self, name): """ Returns the type of the signal pin. """ - pin = self.pins.get(name) - debug.check(pin is not None, "Spice pin {} not found".format(name)) - return pin.type + pin_type = self.pin_type[name] + debug.check(pin_type in self.valid_signal_types, + "Invalid signaltype for {0}: {1}".format(name, pin_type)) + return pin_type def get_pin_dir(self, name): """ Returns the direction of the pin. (Supply/ground are INOUT). """ - pin_type = self.get_pin_type(name) - if pin_type in ["POWER", "GROUND"]: + if self.pin_type[name] in ["POWER", "GROUND"]: return "INOUT" else: - return pin_type + return self.pin_type[name] def get_inputs(self): - """ - These use pin types to determine pin lists. - Returns names only, to maintain historical interface. - """ + """ These use pin types to determine pin lists. These + may be over-ridden by submodules that didn't use pin directions yet.""" input_list = [] - for pin in self.pins.values(): - if pin.type == "INPUT": - input_list.append(pin.name) + for pin in self.pins: + if self.pin_type[pin]=="INPUT": + input_list.append(pin) return input_list def get_outputs(self): - """ - These use pin types to determine pin lists. - Returns names only, to maintain historical interface. - """ + """ These use pin types to determine pin lists. These + may be over-ridden by submodules that didn't use pin directions yet.""" output_list = [] - for pin in self.pins.values(): - if pin.type == "OUTPUT": - output_list.append(pin.name) + for pin in self.pins: + if self.pin_type[pin]=="OUTPUT": + output_list.append(pin) return output_list - def get_inouts(self): - """ - These use pin types to determine pin lists. - Returns names only, to maintain historical interface. - """ - inout_list = [] - for pin in self.pins.values(): - if pin.type == "INOUT": - inout_list.append(pin.name) - return inout_list - def copy_pins(self, other_module, suffix=""): """ This will copy all of the pins from the other module and add an optional suffix.""" - for pin in other_module.pins.values(): - self.add_pin(pin.name + suffix, pin.type) + for pin in other_module.pins: + self.add_pin(pin + suffix, other_module.get_pin_type(pin)) - def connect_inst(self, args): + def get_inouts(self): + """ These use pin types to determine pin lists. These + may be over-ridden by submodules that didn't use pin directions yet.""" + inout_list = [] + for pin in self.pins: + if self.pin_type[pin]=="INOUT": + inout_list.append(pin) + return inout_list + + def connect_inst(self, args, check=True): """ Connects the pins of the last instance added + It is preferred to use the function with the check to find if + there is a problem. The check option can be set to false + where we dynamically generate groups of connections after a + group of modules are generated. """ - spice_pins = list(self.insts[-1].spice_pins) - num_pins = len(spice_pins) + num_pins = len(self.insts[-1].mod.pins) num_args = len(args) # Order the arguments if the hard cell has a custom port order ordered_args = self.get_ordered_inputs(args) - if (num_pins != num_args): + if (check and num_pins != num_args): if num_pins < num_args: - mod_pins = spice_pins + [""] * (num_args - num_pins) + mod_pins = self.insts[-1].mod.pins + [""] * (num_args - num_pins) arg_pins = ordered_args else: arg_pins = ordered_args + [""] * (num_pins - num_args) - mod_pins = spice_pins + mod_pins = self.insts[-1].mod.pins modpins_string = "\n".join(["{0} -> {1}".format(arg, mod) for (arg, mod) in zip(arg_pins, mod_pins)]) debug.error("Connection mismatch:\nInst ({0}) -> Mod ({1})\n{2}".format(num_args, @@ -204,17 +219,27 @@ class spice(): modpins_string), 1) - ordered_nets = self.create_nets(ordered_args) - self.insts[-1].connect_spice_pins(ordered_nets) + self.conns.append(ordered_args) - def create_nets(self, names_list): - nets = [] - for name in names_list: - # setdefault adds to the dict if it doesn't find the net in it already - # then it returns the net it found or created, a net_spice object - net = self.nets.setdefault(name, net_spice(name, self)) - nets.append(net) - return nets + # This checks if we don't have enough instance port connections for the number of insts + if check and (len(self.insts)!=len(self.conns)): + insts_string=pformat(self.insts) + conns_string=pformat(self.conns) + + debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name, + len(self.insts), + len(self.conns))) + debug.error("Instances: \n" + str(insts_string)) + debug.error("-----") + debug.error("Connections: \n" + str(conns_string), 1) + + def get_conns(self, inst): + """Returns the connections of a given instance.""" + for i in range(len(self.insts)): + if inst is self.insts[i]: + return self.conns[i] + # If not found, returns None + return None def sp_read(self): """ @@ -233,7 +258,7 @@ class spice(): subckt = re.compile("^.subckt {}".format(self.cell_name), re.IGNORECASE) subckt_line = list(filter(subckt.search, self.spice))[0] # parses line into ports and remove subckt - self.add_pin_list(subckt_line.split(" ")[2:]) + self.pins = subckt_line.split(" ")[2:] else: debug.info(4, "no spfile {0}".format(self.sp_file)) self.spice = [] @@ -254,10 +279,10 @@ class spice(): subckt_line = list(filter(subckt.search, self.lvs))[0] # parses line into ports and remove subckt lvs_pins = subckt_line.split(" ")[2:] - debug.check(lvs_pins == list(self.pins), + debug.check(lvs_pins == self.pins, "Spice netlists for LVS and simulation have port mismatches:\n{0} (LVS {1})\nvs\n{2} (sim {3})".format(lvs_pins, self.lvs_file, - list(self.pins), + self.pins, self.sp_file)) def check_net_in_spice(self, net_name): @@ -302,72 +327,78 @@ class spice(): # If spice isn't defined, we dynamically generate one. # recursively write the modules - for mod in self.mods: - if self.contains(mod, usedMODS): + for i in self.mods: + if self.contains(i, usedMODS): continue - usedMODS.append(mod) - mod.sp_write_file(sp, usedMODS, lvs, trim) + usedMODS.append(i) + i.sp_write_file(sp, usedMODS, lvs, trim) if len(self.insts) == 0: return - if len(self.pins) == 0: + if self.pins == []: return # write out the first spice line (the subcircuit) - wrapped_pins = "\n+ ".join(tr.wrap(" ".join(list(self.pins)))) + wrapped_pins = "\n+ ".join(tr.wrap(" ".join(self.pins))) sp.write("\n.SUBCKT {0}\n+ {1}\n".format(self.cell_name, wrapped_pins)) + # write a PININFO line + if False: + pin_info = "*.PININFO" + for pin in self.pins: + if self.pin_type[pin] == "INPUT": + pin_info += " {0}:I".format(pin) + elif self.pin_type[pin] == "OUTPUT": + pin_info += " {0}:O".format(pin) + else: + pin_info += " {0}:B".format(pin) + sp.write(pin_info + "\n") + # Also write pins as comments - for pin in self.pins.values(): - sp.write("* {1:6}: {0} \n".format(pin.name, pin.type)) + for pin in self.pins: + sp.write("* {1:6}: {0} \n".format(pin, self.pin_type[pin])) for line in self.comments: sp.write("* {}\n".format(line)) - # every instance must be connected with the connect_inst function - # TODO: may run into empty pin lists edge case, not sure yet - connected = True - for inst in self.insts: - if inst.connected: - continue - debug.error("Instance {} spice pins not connected".format(str(inst))) - connected = False - debug.check(connected, "{0} : Not all instance spice pins are connected.".format(self.cell_name)) + # every instance must have a set of connections, even if it is empty. + if len(self.insts) != len(self.conns): + debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.cell_name, + len(self.insts), + len(self.conns))) + debug.error("Instances: \n" + str(self.insts)) + debug.error("-----") + debug.error("Connections: \n" + str(self.conns), 1) - for inst in self.insts: + for i in range(len(self.insts)): # we don't need to output connections of empty instances. # these are wires and paths - if len(inst.spice_pins) == 0: + if self.conns[i] == []: continue # Instance with no devices in it needs no subckt/instance - if inst.mod.no_instances: + if self.insts[i].mod.no_instances: continue # If this is a trimmed netlist, skip it by adding comment char - if trim and inst.name in self.trim_insts: + if trim and self.insts[i].name in self.trim_insts: sp.write("* ") - if lvs and hasattr(inst.mod, "lvs_device"): - sp.write(inst.mod.lvs_device.format(inst.name, - " ".join(inst.get_connections()))) + if lvs and hasattr(self.insts[i].mod, "lvs_device"): + sp.write(self.insts[i].mod.lvs_device.format(self.insts[i].name, + " ".join(self.conns[i]))) sp.write("\n") - elif hasattr(inst.mod, "spice_device"): - sp.write(inst.mod.spice_device.format(inst.name, - " ".join(inst.get_connections()))) + elif hasattr(self.insts[i].mod, "spice_device"): + sp.write(self.insts[i].mod.spice_device.format(self.insts[i].name, + " ".join(self.conns[i]))) sp.write("\n") else: - if trim and inst.name in self.trim_insts: - wrapped_connections = "\n*+ ".join(tr.wrap(" ".join(inst.get_connections()))) - sp.write("X{0}\n*+ {1}\n*+ {2}\n".format(inst.name, - wrapped_connections, - inst.mod.cell_name)) - else: - wrapped_connections = "\n+ ".join(tr.wrap(" ".join(inst.get_connections()))) - sp.write("X{0}\n+ {1}\n+ {2}\n".format(inst.name, - wrapped_connections, - inst.mod.cell_name)) + wrapped_connections = "\n+ ".join(tr.wrap(" ".join(self.conns[i]))) + + sp.write("X{0}\n+ {1}\n+ {2}\n".format(self.insts[i].name, + wrapped_connections, + self.insts[i].mod.cell_name)) sp.write(".ENDS {0}\n".format(self.cell_name)) @@ -696,12 +727,6 @@ class spice(): aliases.append(net) return aliases - def get_instance_connections(self): - conns = [] - for inst in self.insts: - conns.append(inst.get_connections()) - return conns - def is_net_alias(self, known_net, net_alias, mod, exclusion_set): """ Checks if the alias_net in input mod is the same as the input net for this mod (self). @@ -714,7 +739,7 @@ class spice(): return True # Check connections of all other subinsts mod_set = set() - for subinst, inst_conns in zip(self.insts, self.get_instance_connections()): + for subinst, inst_conns in zip(self.insts, self.conns): for inst_conn, mod_pin in zip(inst_conns, subinst.mod.pins): if self.is_net_alias_name_check(known_net, inst_conn, net_alias, mod): return True @@ -731,149 +756,3 @@ class spice(): return self == mod and \ child_net.lower() == alias_net.lower() and \ parent_net.lower() == alias_net.lower() - - -class pin_spice(): - """ - A class to represent a spice netlist pin. - mod is the parent module that created this pin. - mod_net is the net object of this pin's parent module. It must have the same name as the pin. - inst is the instance this pin is a part of, if any. - inst_net is the net object from mod's nets which connects to this pin. - """ - - valid_pin_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND", "BIAS"] - - def __init__(self, name, type, mod): - self.name = name - self.set_type(type) - self.mod = mod - self.mod_net = None - self.inst = None - self.inst_net = None - - # TODO: evaluate if this makes sense... and works - self._hash = hash(self.name) - - def set_type(self, type): - debug.check(type in pin_spice.valid_pin_types, - "Invalid pin type for {0}: {1}".format(self.name, type)) - self.type = type - - def set_mod_net(self, net): - debug.check(isinstance(net, net_spice), "net must be a net_spice object") - debug.check(net.name == self.name, "module spice net must have same name as spice pin") - self.mod_net = net - - def set_inst(self, inst): - self.inst = inst - - def set_inst_net(self, net): - if self.inst_net is not None: - debug.error("pin {} is already connected to net {}\ - so it cannot also be connected to net {}\ - ".format(self.name, self.inst_net.name, net.name), 1) - debug.check(isinstance(net, net_spice), "net must be a net_spice object") - self.inst_net = net - - def __str__(self): - """ override print function output """ - return "(pin_name={} type={})".format(self.name, self.type) - - def __repr__(self): - """ override repr function output """ - return self.name - - def __eq__(self, name): - return (name == self.name) if isinstance(name, str) else super().__eq__(name) - - def __hash__(self): - """ - Implement the hash function for sets etc. - Only hash name since spice does not allow two pins to share a name. - Provides a speedup if pin_spice is used as a key for dicts. - """ - return self._hash - - def __deepcopy__(original, memo): - """ - This function is defined so that instances of modules can make deep - copies of their parent module's pins dictionary. It is only expected - to be called by the instance class __init__ function. Mod and mod_net - should not be deep copies but references to the existing mod and net - objects they refer to in the original. If inst is already defined this - function will throw an error because that means it was called on a pin - from an instance, which is not defined behavior. - """ - debug.check(original.inst is None, - "cannot make a deepcopy of a spice pin from an inst") - pin = pin_spice(original.name, original.type, original.mod) - if original.mod_net is not None: - pin.set_mod_net(original.mod_net) - return pin - - -class net_spice(): - """ - A class to represent a spice net. - mod is the parent module that created this net. - pins are all the pins connected to this net. - inst is the instance this net is a part of, if any. - """ - - def __init__(self, name, mod): - self.name = name - self.pins = [] - self.mod = mod - self.inst = None - - # TODO: evaluate if this makes sense... and works - self._hash = hash(self.name) - - def connect_pin(self, pin): - debug.check(isinstance(pin, pin_spice), "pin must be a pin_spice object") - if pin in self.pins: - debug.warning("pin {} was already connected to net {} ... why was it connected again?".format(pin.name, self.name)) - else: - self.pins.append(pin) - - def set_inst(self, inst): - self.inst = inst - - def __str__(self): - """ override print function output """ - return "(net_name={} type={})".format(self.name, self.type) - - def __repr__(self): - """ override repr function output """ - return self.name - - def __eq__(self, name): - return (name == self.name) if isinstance(name, str) else super().__eq__(name) - - def __hash__(self): - """ - Implement the hash function for sets etc. - Only hash name since spice does not allow two nets to share a name - (on the same level of hierarchy, or rather they will be the same net). - Provides a speedup if net_spice is used as a key for dicts. - """ - return self._hash - - def __deepcopy__(original, memo): - """ - This function is defined so that instances of modules can make deep - copies of their parent module's nets dictionary. It is only expected - to be called by the instance class __init__ function. Mod - should not be a deep copy but a reference to the existing mod - object it refers to in the original. If inst is already defined this - function will throw an error because that means it was called on a net - from an instance, which is not defined behavior. - """ - debug.check(original.inst is None, - "cannot make a deepcopy of a spice net from an inst") - net = net_spice(original.name, original.mod) - if original.pins != []: - # TODO: honestly I'm not sure if this is right but we'll see... - net.pins = original.pins - return net diff --git a/compiler/base/lef.py b/compiler/base/lef.py index e08f0efc..b138799b 100644 --- a/compiler/base/lef.py +++ b/compiler/base/lef.py @@ -18,7 +18,7 @@ class lef: """ SRAM LEF Class open GDS file, read pins information, obstruction and write them to LEF file. - This is inherited by the sram_1bank class. + This is inherited by the sram_base class. """ def __init__(self, layers): # LEF db units per micron diff --git a/compiler/modules/column_decoder.py b/compiler/modules/column_decoder.py index ab80b1c9..92149a71 100644 --- a/compiler/modules/column_decoder.py +++ b/compiler/modules/column_decoder.py @@ -42,7 +42,7 @@ class column_decoder(design): def create_instances(self): self.column_decoder_inst = self.add_inst(name="column_decoder", mod=self.column_decoder) - self.connect_inst(list(self.pins)) + self.connect_inst(self.pins) def create_layout(self): self.column_decoder_inst.place(vector(0,0)) diff --git a/compiler/modules/pbitcell.py b/compiler/modules/pbitcell.py index 9c1800bd..2eab6e28 100644 --- a/compiler/modules/pbitcell.py +++ b/compiler/modules/pbitcell.py @@ -1212,7 +1212,7 @@ class pbitcell(bitcell_base): if self.dummy_bitcell: return - pin_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)} + pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} # Edges added wl->bl, wl->br for every port except write ports rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names) From bb47452baf9f86ee52a3014df9862fca546e8981 Mon Sep 17 00:00:00 2001 From: Sam Crow Date: Tue, 15 Aug 2023 11:07:04 -0700 Subject: [PATCH 27/48] reapply commit c8a06a1 patch that was incorrectly reverted --- compiler/base/hierarchy_spice.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 64dc0b2b..fe5d3a8a 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -394,11 +394,17 @@ class spice(): " ".join(self.conns[i]))) sp.write("\n") else: - wrapped_connections = "\n+ ".join(tr.wrap(" ".join(self.conns[i]))) + if trim and self.insts[i].name in self.trim_insts: + wrapped_connections = "\n*+ ".join(tr.wrap(" ".join(self.conns[i]))) + sp.write("X{0}\n*+ {1}\n*+ {2}\n".format(self.insts[i].name, + wrapped_connections, + self.insts[i].mod.cell_name)) + else: + wrapped_connections = "\n+ ".join(tr.wrap(" ".join(self.conns[i]))) + sp.write("X{0}\n+ {1}\n+ {2}\n".format(self.insts[i].name, + wrapped_connections, + self.insts[i].mod.cell_name)) - sp.write("X{0}\n+ {1}\n+ {2}\n".format(self.insts[i].name, - wrapped_connections, - self.insts[i].mod.cell_name)) sp.write(".ENDS {0}\n".format(self.cell_name)) From 9ac894e2effe79eb1da431dc673e833049468071 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 15 Aug 2023 11:30:16 -0700 Subject: [PATCH 28/48] update bitcell array trimming --- compiler/modules/bitcell_array.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 08dff652..3a5ddd47 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -76,7 +76,8 @@ class bitcell_array(bitcell_base_array): self.pattern.connect_array() for key in self.cell_inst.keys(): - if key != (0,0): + (row, col) = key + if col>0 and col0 and row Date: Mon, 21 Aug 2023 19:24:06 -0700 Subject: [PATCH 29/48] singleport bitcell array laying out --- compiler/modules/__init__.py | 1 - compiler/modules/orig_bitcell_array.py | 116 ------------------ compiler/modules/pattern.py | 100 +++++++-------- compiler/tests/05_bitcell_array_test.py | 2 +- .../sky130/custom/sky130_bitcell_array.py | 72 ++++------- .../custom/sky130_bitcell_base_array.py | 107 ++++------------ 6 files changed, 94 insertions(+), 304 deletions(-) delete mode 100644 compiler/modules/orig_bitcell_array.py diff --git a/compiler/modules/__init__.py b/compiler/modules/__init__.py index 2f7d257a..df7e2a81 100755 --- a/compiler/modules/__init__.py +++ b/compiler/modules/__init__.py @@ -41,7 +41,6 @@ from .local_bitcell_array import * from .nand2_dec import * from .nand3_dec import * from .nand4_dec import * -from .orig_bitcell_array import * from .pand2 import * from .pand3 import * from .pand4 import * diff --git a/compiler/modules/orig_bitcell_array.py b/compiler/modules/orig_bitcell_array.py deleted file mode 100644 index 55ed5d3b..00000000 --- a/compiler/modules/orig_bitcell_array.py +++ /dev/null @@ -1,116 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2023 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -from openram.sram_factory import factory -from openram.tech import drc, spice -from openram import OPTS -from .bitcell_base_array import bitcell_base_array - - -class bitcell_array(bitcell_base_array): - """ - Creates a rows x cols array of memory cells. Assumes bit-lines - and word line is connected by abutment. - Connects the word lines and bit lines. - """ - def __init__(self, cols, rows, name, column_offset=0): - super().__init__(cols, rows, name, column_offset) - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - # We don't offset this because we need to align - # the replica bitcell in the control logic - # self.offset_all_coordinates() - - def create_netlist(self): - """ Create and connect the netlist """ - self.add_modules() - self.add_pins() - self.create_instances() - - def create_layout(self): - - self.place_array("bit_r{0}_c{1}") - - self.add_layout_pins() - - self.add_boundary() - - self.DRC_LVS() - - def add_modules(self): - """ Add the modules used in this design """ - self.cell = factory.create(module_type=OPTS.bitcell) - - def create_instances(self): - """ Create the module instances used in this design """ - self.cell_inst = {} - for col in range(self.column_size): - for row in range(self.row_size): - name = "bit_r{0}_c{1}".format(row, col) - self.cell_inst[row, col]=self.add_inst(name=name, - mod=self.cell) - self.connect_inst(self.get_bitcell_pins(col, row)) - - def analytical_power(self, corner, load): - """Power of Bitcell array and bitline in nW.""" - - # Dynamic Power from Bitline - bl_wire = self.gen_bl_wire() - cell_load = 2 * bl_wire.return_input_cap() - bl_swing = OPTS.rbl_delay_percentage - freq = spice["default_event_frequency"] - bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) - - # Calculate the bitcell power which currently only includes leakage - cell_power = self.cell.analytical_power(corner, load) - - # Leakage power grows with entire array and bitlines. - total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size, - cell_power.leakage * self.column_size * self.row_size) - return total_power - - def gen_wl_wire(self): - if OPTS.netlist_only: - width = 0 - else: - width = self.width - wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_m1")) - wl_wire.wire_c = 2 * spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell - return wl_wire - - def gen_bl_wire(self): - if OPTS.netlist_only: - height = 0 - else: - height = self.height - bl_pos = 0 - bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1")) - bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell - return bl_wire - - def get_wordline_cin(self): - """Get the relative input capacitance from the wordline connections in all the bitcell""" - # A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns - bitcell_wl_cin = self.cell.get_wl_cin() - total_cin = bitcell_wl_cin * self.column_size - return total_cin - - def graph_exclude_bits(self, targ_row, targ_col): - """Excludes bits in column from being added to graph except target""" - # Function is not robust with column mux configurations - for row in range(self.row_size): - for col in range(self.column_size): - if row == targ_row and col == targ_col: - continue - self.graph_inst_exclude.add(self.cell_inst[row, col]) - - def get_cell_name(self, inst_name, row, col): - """Gets the spice name of the target bitcell.""" - return inst_name + "{}x".format(OPTS.hier_seperator) + self.cell_inst[row, col].name, self.cell_inst[row, col] diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index feb04927..af9d2e52 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -9,7 +9,9 @@ from typing import List from typing import Optional from openram.base import design from openram.globals import OPTS -from math import ceil +from math import ceil, floor +from copy import deepcopy + class pattern(): """ This class is used to desribe the internals of a bitcell array. It describes @@ -68,6 +70,8 @@ class pattern(): self.initial_y_block = initial_y_block self.final_x_block = final_x_block self.final_y_block = final_y_block + self.bits_per_row = ceil(self.num_rows/self.num_cores_x) + self.bits_per_col = ceil(self.num_cols/self.num_cores_y) if not OPTS.netlist_only: self.verify_interblock_dimensions() @@ -113,23 +117,39 @@ class pattern(): def connect_block(self, block: block, col: int, row: int): for dr in range(len(block)): + row_done = False for dc in range(len(block[0])): - if(self.bit_rows.count(self.num_rows) != self.num_cols and self.bit_cols.count(self.bit_cols) != self.num_rows): + if(self.bit_rows.count(self.num_rows) <= self.num_cols and self.bit_cols.count(self.bit_cols) <= self.num_rows): inst = block[dr][dc] if(len(self.bit_rows) <= col + dc): self.bit_rows.append(0) if(len(self.bit_cols) <= row + dr): self.bit_cols.append(0) - if(self.bit_rows[col+dc] < self.num_rows and self.bit_cols[row+dr] < self.num_cols): + if(row_done or self.bit_cols[row+dr] >= self.num_cols): + row_done = True + continue + if((self.bit_rows[col+dc] < self.num_rows) and (self.bit_cols[row+dr] < self.num_cols)): + print(row+dr, col+dc) if(inst.is_bitcell): + #x_bit = sum(bit > 0 for bit in self.bit_rows) + #y_bit = sum(bit > 0 for bit in self.bit_cols) + #print(x_bit, y_bit) + self.parent_design.cell_inst[self.bit_rows[col+dc], self.bit_cols[row+dr]] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc)) + self.parent_design.all_inst[row + dr, col + dc] = self.parent_design.cell_inst[self.bit_rows[col+dc], self.bit_cols[row+dr]] + self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(self.bit_rows[col+dc], self.bit_cols[row+dr])) self.bit_rows[col+dc] += 1 self.bit_cols[row+dr] += 1 - self.parent_design.cell_inst[row + dr, col + dc] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc)) - self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(row+dr, col+dc)) + + else: + self.parent_design.all_inst[row + dr, col + dc] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc)) + self.parent_design.connect_inst(self.parent_design.get_strap_pins(self.bit_rows[col+dc], self.bit_cols[row+dr])) + else: + row_done = True def connect_array(self) -> None: self.bit_rows = [] self.bit_cols = [] + #debug_array = [[None]*12 for _ in range(6)] row = 0 col = 0 for i in range(self.num_cores_y): @@ -138,7 +158,7 @@ class pattern(): col += len(self.core_block[0]) col = 0 row += len(self.core_block) - + def place_inst(self, inst, offset) -> None: x = offset[0] y = offset[1] @@ -148,55 +168,27 @@ class pattern(): x += inst.width inst.place((x, y), inst.mirror, inst.rotate) - - - def place_block(self, block: block, row: int, col: int, place_x: float, place_y: float, bx, by): - x_offset = 0 - y_offset = 0 - bounding_x = bx - bounding_y = by - for dr in range(len(block)): - for dc in range(len(block[0])): - if(self.bit_rows.count(self.num_rows) != self.num_cols and self.bit_cols.count(self.bit_cols) != self.num_rows): - if(len(self.bit_rows) <= col + dc): - self.bit_rows.append(0) - if(len(self.bit_cols) <= row + dr): - self.bit_cols.append(0) - if(self.bit_rows[col+dc] < self.num_rows and self.bit_cols[row+dr] < self.num_cols): - inst = self.parent_design.cell_inst[row + dr, col +dc] - if(inst.is_bitcell): - self.bit_rows[col+dc] += 1 - self.bit_cols[row+dr] += 1 - self.place_inst(inst, (place_x + x_offset, place_y + y_offset)) - if(place_x + x_offset + inst.width > bounding_x): - bounding_x = place_x + x_offset + inst.width - if(place_y + y_offset + inst.height > bounding_y): - bounding_y = place_y + y_offset + inst.height - x_offset += inst.width - x_offset = 0 - y_offset += inst.height - return bounding_x, bounding_y - def place_array(self): - self.bit_rows = [] - self.bit_cols = [] - row = 0 - col = 0 - place_x = 0 - place_y = 0 - bounding_x = 0 - bounding_y = 0 - for i in range(self.num_cores_y): - col = 0 - place_x = 0 - for j in range(self.num_cores_x): - self.parent_design.width, self.parent_design.height= self.place_block(self.core_block, row, col, place_x, place_y, bounding_x, bounding_y) - if(self.bit_rows.count(self.num_rows) == self.num_cols and self.bit_cols.count(self.bit_cols) == self.num_rows): - return + (row_max, col_max) = list(self.parent_design.all_inst.keys())[-1] + y = 0 + for row in range(row_max+1): + x = 0 + for col in range(col_max+1): + inst = self.parent_design.all_inst[row, col] + self.place_inst(inst, (x, y)) + x += inst.width + y += inst.height - place_x += self.core_block_width - col += len(self.core_block[0]) - row += len(self.core_block) - place_y += self.core_block_height + self.parent_design.width = max([x.rx() for x in self.parent_design.insts]) + self.parent_design.height = max([x.uy() for x in self.parent_design.insts]) + def append_row_to_block(block, row): + block.append(row) + + def append_block_under_block(base_block, under_block): + base_block = base_block + under_block + + def append_block_right_block(base_block, right_block): + for row in base_block: + row = row + right_block diff --git a/compiler/tests/05_bitcell_array_test.py b/compiler/tests/05_bitcell_array_test.py index a92d3653..425803db 100755 --- a/compiler/tests/05_bitcell_array_test.py +++ b/compiler/tests/05_bitcell_array_test.py @@ -31,7 +31,7 @@ class array_test(openram_test): num_spare_rows = 0 num_spare_cols = 0 - a = factory.create(module_type="bitcell_array", cols=8 + num_spare_cols, rows=8 + num_spare_rows) + a = factory.create(module_type="bitcell_array", cols=4 + num_spare_cols, rows=4 + num_spare_rows) self.local_check(a) openram.end_openram() diff --git a/technology/sky130/custom/sky130_bitcell_array.py b/technology/sky130/custom/sky130_bitcell_array.py index e5a1704b..4526374e 100644 --- a/technology/sky130/custom/sky130_bitcell_array.py +++ b/technology/sky130/custom/sky130_bitcell_array.py @@ -6,11 +6,12 @@ # from openram import debug -from openram.modules import bitcell_array +from openram.modules import bitcell_array, pattern from openram.sram_factory import factory +from openram.base import geometry from openram import OPTS from .sky130_bitcell_base_array import sky130_bitcell_base_array - +from math import ceil class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): """ @@ -38,56 +39,29 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): """ Add the modules used in this design """ # Bitcell for port names only self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") - self.cell2 = factory.create(module_type=OPTS.bitcell, version="opt1a") + self.cella = factory.create(module_type=OPTS.bitcell, version="opt1a") self.strap = factory.create(module_type="internal", version="wlstrap") - self.strap2 = factory.create(module_type="internal", version="wlstrap_p") - self.strap3 = factory.create(module_type="internal", version="wlstrapa") - self.strap4 = factory.create(module_type="internal", version="wlstrapa_p") + self.strap_p = factory.create(module_type="internal", version="wlstrap_p") + self.strapa = factory.create(module_type="internal", version="wlstrapa") + self.strapa_p = factory.create(module_type="internal", version="wlstrapa_p") def create_instances(self): """ Create the module instances used in this design """ - self.cell_inst = {} - self.array_layout = [] - alternate_bitcell = (self.row_size) % 2 - for row in range(0, self.row_size): + self.all_inst={} + self.cell_inst={} + bit_row_opt1 = [geometry.instance("00_opt1", mod=self.cell, is_bitcell=True)] \ + + [geometry.instance("01_strap", mod=self.strap, is_bitcell=False)]\ + + [geometry.instance("02_opt1", mod=self.cell, is_bitcell=True)] \ + + [geometry.instance("03_strap_p", mod=self.strap_p, is_bitcell=False)] + + bit_row_opt1a = [geometry.instance("10_opt1a", mod=self.cella, is_bitcell=True)] \ + + [geometry.instance("11_strapa", mod=self.strapa, is_bitcell=False)] \ + + [geometry.instance("12_opt1a", mod=self.cella, is_bitcell=True)] \ + + [geometry.instance("13_strapa_p", mod=self.strapa_p, is_bitcell=False)] - row_layout = [] + bit_block = [] + pattern.append_row_to_block(bit_block, bit_row_opt1) + pattern.append_row_to_block(bit_block, bit_row_opt1a) + self.pattern = pattern(self, "bitcell_array", bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.row_size/2), name_template="bit_r{0}_c{1}") + self.pattern.connect_array() - alternate_strap = (self.row_size+1) % 2 - for col in range(0, self.column_size): - if alternate_bitcell == 1: - row_layout.append(self.cell) - self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col), - mod=self.cell) - else: - row_layout.append(self.cell2) - self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col), - mod=self.cell2) - self.connect_inst(self.get_bitcell_pins(row, col)) - if col != self.column_size - 1: - if alternate_strap: - if row % 2: - name="row_{}_col_{}_wlstrapa_p".format(row, col) - row_layout.append(self.strap4) - self.add_inst(name=name, mod=self.strap4) - else: - name="row_{}_col_{}_wlstrap_p".format(row, col) - row_layout.append(self.strap2) - self.add_inst(name=name, mod=self.strap2) - alternate_strap = 0 - else: - if row % 2: - name="row_{}_col_{}_wlstrapa".format(row, col) - row_layout.append(self.strap3) - self.add_inst(name=name.format(row, col), mod=self.strap3) - else: - name="row_{}_col_{}_wlstrap".format(row, col) - row_layout.append(self.strap) - self.add_inst(name=name.format(row, col), mod=self.strap) - alternate_strap = 1 - self.connect_inst(self.get_strap_pins(row, col, name)) - if alternate_bitcell == 0: - alternate_bitcell = 1 - else: - alternate_bitcell = 0 - self.array_layout.append(row_layout) diff --git a/technology/sky130/custom/sky130_bitcell_base_array.py b/technology/sky130/custom/sky130_bitcell_base_array.py index 9263f8dd..b1310a6a 100644 --- a/technology/sky130/custom/sky130_bitcell_base_array.py +++ b/technology/sky130/custom/sky130_bitcell_base_array.py @@ -23,44 +23,6 @@ class sky130_bitcell_base_array(bitcell_base_array): self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") - def place_array(self, name_template, row_offset=0, col_offset=0): - yoffset = 0.0 - - for row in range(0, len(self.array_layout)): - xoffset = 0.0 - for col in range(0, len(self.array_layout[row])): - self.place_inst = self.insts[(col) + (row) * len(self.array_layout[row])] - - if row % 2 == 0: - if col == 0: - self.place_inst.place(offset=[xoffset, yoffset + self.cell.height], mirror="MX") - elif col % 4 == 0: - self.place_inst.place(offset=[xoffset, yoffset + self.cell.height], mirror="MX") - elif col % 4 == 3 : - self.place_inst.place(offset=[xoffset, yoffset + self.cell.height], mirror="MX") - elif col % 4 == 2: - self.place_inst.place(offset=[xoffset + self.cell.width, yoffset + self.cell.height], mirror="XY") - else: - self.place_inst.place(offset=[xoffset, yoffset + self.cell.height], mirror="MX") - else: - if col == 0: - self.place_inst.place(offset=[xoffset, yoffset]) - elif col % 4 == 0: - self.place_inst.place(offset=[xoffset, yoffset]) - elif col % 4 == 3 : - self.place_inst.place(offset=[xoffset, yoffset]) - elif col % 4 == 2: - self.place_inst.place(offset=[xoffset + self.cell.width, yoffset], mirror="MY") - # self.place_inst.place(offset=[xoffset, yoffset]) - else: - self.place_inst.place(offset=[xoffset, yoffset]) - - xoffset += self.place_inst.width - yoffset += self.place_inst.height - - self.width = max([x.rx() for x in self.insts]) - self.height = max([x.uy() for x in self.insts]) - def get_bitcell_pins(self, row, col, swap = False): """ Creates a list of connections in the bitcell, @@ -139,49 +101,28 @@ class sky130_bitcell_base_array(bitcell_base_array): for pin_name in ["vdd", "gnd"]: self.copy_layout_pin(inst, pin_name) - if row == 2: #add only 1 label per col + # if row == 2: #add only 1 label per col + # + # if 'VPB' or 'vpb' in self.cell_inst[row, col].mod.pins: + # pin = inst.get_pin("vpb") + # self.objs.append(geometry.rectangle(layer["nwell"], + # pin.ll(), + # pin.width(), + # pin.height())) + # self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) + # + # if 'VNB' or 'vnb'in self.cell_inst[row, col].mod.pins: + # try: + # from openram.tech import layer_override + # if layer_override['VNB']: + # pin = inst.get_pin("vnb") + # self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) + # self.objs.append(geometry.rectangle(layer["pwellp"], + # pin.ll(), + # pin.width(), + # pin.height())) + # except: + # pin = inst.get_pin("vnb") + # self.add_label("vdd", pin.layer, pin.center()) - if 'VPB' or 'vpb' in self.cell_inst[row, col].mod.pins: - pin = inst.get_pin("vpb") - self.objs.append(geometry.rectangle(layer["nwell"], - pin.ll(), - pin.width(), - pin.height())) - self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) - - if 'VNB' or 'vnb'in self.cell_inst[row, col].mod.pins: - try: - from openram.tech import layer_override - if layer_override['VNB']: - pin = inst.get_pin("vnb") - self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) - self.objs.append(geometry.rectangle(layer["pwellp"], - pin.ll(), - pin.width(), - pin.height())) - except: - pin = inst.get_pin("vnb") - self.add_label("vdd", pin.layer, pin.center()) - - def add_bitline_pins(self): - bitline_names = self.cell.get_all_bitline_names() - for col in range(self.column_size): - for port in self.all_ports: - bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port]) - text = "bl_{0}_{1}".format(port, col) - #if "Y" in self.cell_inst[0, col].mirror: - # text = text.replace("bl", "br") - self.add_layout_pin(text=text, - layer=bl_pin.layer, - offset=bl_pin.ll().scale(1, 0), - width=bl_pin.width(), - height=self.height) - br_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port + 1]) - text = "br_{0}_{1}".format(port, col) - #if "Y" in self.cell_inst[0, col].mirror: - # text = text.replace("br", "bl") - self.add_layout_pin(text=text, - layer=br_pin.layer, - offset=br_pin.ll().scale(1, 0), - width=br_pin.width(), - height=self.height) + From f890160601f440b9a0311d544877a1645b5325de Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Mon, 21 Aug 2023 20:12:36 -0700 Subject: [PATCH 30/48] add nwell routing in bca --- compiler/modules/pattern.py | 7 +- .../sky130/custom/sky130_bitcell_array.py | 12 ++-- .../custom/sky130_bitcell_base_array.py | 66 ++++++++----------- 3 files changed, 37 insertions(+), 48 deletions(-) diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index af9d2e52..8b6070db 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -129,7 +129,6 @@ class pattern(): row_done = True continue if((self.bit_rows[col+dc] < self.num_rows) and (self.bit_cols[row+dr] < self.num_cols)): - print(row+dr, col+dc) if(inst.is_bitcell): #x_bit = sum(bit > 0 for bit in self.bit_rows) #y_bit = sum(bit > 0 for bit in self.bit_cols) @@ -170,11 +169,11 @@ class pattern(): def place_array(self): - (row_max, col_max) = list(self.parent_design.all_inst.keys())[-1] + (self.row_max, self.col_max) = list(self.parent_design.all_inst.keys())[-1] y = 0 - for row in range(row_max+1): + for row in range(self.row_max+1): x = 0 - for col in range(col_max+1): + for col in range(self.col_max+1): inst = self.parent_design.all_inst[row, col] self.place_inst(inst, (x, y)) x += inst.width diff --git a/technology/sky130/custom/sky130_bitcell_array.py b/technology/sky130/custom/sky130_bitcell_array.py index 4526374e..4c37fb11 100644 --- a/technology/sky130/custom/sky130_bitcell_array.py +++ b/technology/sky130/custom/sky130_bitcell_array.py @@ -33,7 +33,7 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): self.create_netlist() if not OPTS.netlist_only: self.create_layout() - self.add_supply_pins() + #self.add_supply_pins() def add_modules(self): """ Add the modules used in this design """ @@ -49,14 +49,14 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): """ Create the module instances used in this design """ self.all_inst={} self.cell_inst={} - bit_row_opt1 = [geometry.instance("00_opt1", mod=self.cell, is_bitcell=True)] \ - + [geometry.instance("01_strap", mod=self.strap, is_bitcell=False)]\ - + [geometry.instance("02_opt1", mod=self.cell, is_bitcell=True)] \ - + [geometry.instance("03_strap_p", mod=self.strap_p, is_bitcell=False)] + bit_row_opt1 = [geometry.instance("00_opt1", mod=self.cell, is_bitcell=True, mirror='MX')] \ + + [geometry.instance("01_strap", mod=self.strap, is_bitcell=False, mirror='MX')]\ + + [geometry.instance("02_opt1", mod=self.cell, is_bitcell=True, mirror='XY')] \ + + [geometry.instance("03_strap_p", mod=self.strap_p, is_bitcell=False, mirror='MX')] bit_row_opt1a = [geometry.instance("10_opt1a", mod=self.cella, is_bitcell=True)] \ + [geometry.instance("11_strapa", mod=self.strapa, is_bitcell=False)] \ - + [geometry.instance("12_opt1a", mod=self.cella, is_bitcell=True)] \ + + [geometry.instance("12_opt1a", mod=self.cella, is_bitcell=True, mirror='MY')] \ + [geometry.instance("13_strapa_p", mod=self.strapa_p, is_bitcell=False)] bit_block = [] diff --git a/technology/sky130/custom/sky130_bitcell_base_array.py b/technology/sky130/custom/sky130_bitcell_base_array.py index b1310a6a..07d187ed 100644 --- a/technology/sky130/custom/sky130_bitcell_base_array.py +++ b/technology/sky130/custom/sky130_bitcell_base_array.py @@ -84,45 +84,35 @@ class sky130_bitcell_base_array(bitcell_base_array): strap_pins = ["vdd", "gnd", "vdd"] return strap_pins - def add_supply_pins(self): - """ Add the layout pins """ + def route_supplies(self): # Copy a vdd/gnd layout pin from every cell - + print("routing power") for inst in self.insts: - if "wlstrap" in inst.name: - if "VPWR" in inst.mod.pins: - self.copy_layout_pin(inst, "VPWR", "vdd") - if "VGND" in inst.mod.pins: - self.copy_layout_pin(inst, "VGND", "gnd") + if "VPWR" in inst.mod.pins: + self.copy_layout_pin(inst, "VPWR", "vdd") + if "VGND" in inst.mod.pins: + self.copy_layout_pin(inst, "VGND", "gnd") + + for row in range(self.pattern.row_max+1): + inst = self.all_inst[row,0] + pin = inst.get_pin("vpb") + self.objs.append(geometry.rectangle(layer["nwell"], + pin.ll(), + pin.width(), + pin.height())) + self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) + + try: + from openram.tech import layer_override + if layer_override['VNB']: + pin = inst.get_pin("vnb") + self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) + self.objs.append(geometry.rectangle(layer["pwellp"], + pin.ll(), + pin.width(), + pin.height())) + except: + pin = inst.get_pin("vnb") + self.add_label("vdd", pin.layer, pin.center()) - 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) - # if row == 2: #add only 1 label per col - # - # if 'VPB' or 'vpb' in self.cell_inst[row, col].mod.pins: - # pin = inst.get_pin("vpb") - # self.objs.append(geometry.rectangle(layer["nwell"], - # pin.ll(), - # pin.width(), - # pin.height())) - # self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) - # - # if 'VNB' or 'vnb'in self.cell_inst[row, col].mod.pins: - # try: - # from openram.tech import layer_override - # if layer_override['VNB']: - # pin = inst.get_pin("vnb") - # self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) - # self.objs.append(geometry.rectangle(layer["pwellp"], - # pin.ll(), - # pin.width(), - # pin.height())) - # except: - # pin = inst.get_pin("vnb") - # self.add_label("vdd", pin.layer, pin.center()) - - From 5cf50b333a802e819d47b057f7c68b7effcd4f5b Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Mon, 21 Aug 2023 20:25:51 -0700 Subject: [PATCH 31/48] bitcell array passing --- technology/sky130/custom/sky130_bitcell_base_array.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/technology/sky130/custom/sky130_bitcell_base_array.py b/technology/sky130/custom/sky130_bitcell_base_array.py index 07d187ed..68814d65 100644 --- a/technology/sky130/custom/sky130_bitcell_base_array.py +++ b/technology/sky130/custom/sky130_bitcell_base_array.py @@ -93,8 +93,8 @@ class sky130_bitcell_base_array(bitcell_base_array): if "VGND" in inst.mod.pins: self.copy_layout_pin(inst, "VGND", "gnd") - for row in range(self.pattern.row_max+1): - inst = self.all_inst[row,0] + for col in range(self.column_size): + inst = self.cell_inst[0,col] pin = inst.get_pin("vpb") self.objs.append(geometry.rectangle(layer["nwell"], pin.ll(), From 450f8ab0c360d8d04a7060d5128d6b31578d4361 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 22 Aug 2023 00:45:57 -0700 Subject: [PATCH 32/48] replica col generating, funny dummy cell placement --- compiler/modules/pattern.py | 30 ++- compiler/modules/replica_column.py | 6 - .../sky130/custom/sky130_replica_column.py | 226 ++++-------------- 3 files changed, 72 insertions(+), 190 deletions(-) diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index 8b6070db..a7cc062f 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -36,7 +36,7 @@ class pattern(): initial_x_block:bool = False, initial_y_block:bool = False, final_x_block:bool = False, - final_y_block:bool = False + final_y_block:bool = False, ): """ a "block" is a 2d list of instances @@ -72,6 +72,9 @@ class pattern(): self.final_y_block = final_y_block self.bits_per_row = ceil(self.num_rows/self.num_cores_x) self.bits_per_col = ceil(self.num_cols/self.num_cores_y) + self.bit_rows = [] + self.bit_cols = [] + self.parent_design.all_inst = {} if not OPTS.netlist_only: self.verify_interblock_dimensions() @@ -130,8 +133,6 @@ class pattern(): continue if((self.bit_rows[col+dc] < self.num_rows) and (self.bit_cols[row+dr] < self.num_cols)): if(inst.is_bitcell): - #x_bit = sum(bit > 0 for bit in self.bit_rows) - #y_bit = sum(bit > 0 for bit in self.bit_cols) #print(x_bit, y_bit) self.parent_design.cell_inst[self.bit_rows[col+dc], self.bit_cols[row+dr]] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc)) self.parent_design.all_inst[row + dr, col + dc] = self.parent_design.cell_inst[self.bit_rows[col+dc], self.bit_cols[row+dr]] @@ -146,8 +147,7 @@ class pattern(): row_done = True def connect_array(self) -> None: - self.bit_rows = [] - self.bit_cols = [] + #debug_array = [[None]*12 for _ in range(6)] row = 0 col = 0 @@ -157,6 +157,26 @@ class pattern(): col += len(self.core_block[0]) col = 0 row += len(self.core_block) + + def connect_array_raw(self) -> None: + for row in range(self.num_rows): + for col in range(self.num_cols): + inst = self.core_block[row][col] + if(len(self.bit_rows) <= col): + self.bit_rows.append(0) + if(len(self.bit_cols) <= row): + self.bit_cols.append(0) + if(inst.is_bitcell): + self.parent_design.cell_inst[self.bit_rows[col], self.bit_cols[row]] = self.parent_design.add_existing_inst(inst,self.name_template.format(row, col)) + self.parent_design.all_inst[row, col] = self.parent_design.cell_inst[self.bit_rows[col], self.bit_cols[row]] + self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(self.bit_rows[col], self.bit_cols[row])) + self.bit_rows[col] += 1 + self.bit_cols[row] += 1 + + else: + self.parent_design.all_inst[row, col] = self.parent_design.add_existing_inst(inst,self.name_template.format(row, col)) + self.parent_design.connect_inst(self.parent_design.get_strap_pins(self.bit_rows[col], self.bit_cols[row])) + def place_inst(self, inst, offset) -> None: x = offset[0] diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index c6cdd492..dc1b6f12 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -132,12 +132,6 @@ class replica_column(bitcell_base_array): width=self.width, height=wl_pin.height()) - 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 get_bitline_names(self, port=None): if port == None: return self.all_bitline_names diff --git a/technology/sky130/custom/sky130_replica_column.py b/technology/sky130/custom/sky130_replica_column.py index 4d95a3a4..9eca5dfd 100644 --- a/technology/sky130/custom/sky130_replica_column.py +++ b/technology/sky130/custom/sky130_replica_column.py @@ -11,7 +11,7 @@ from openram.sram_factory import factory from openram.tech import layer from openram import OPTS from .sky130_bitcell_base_array import sky130_bitcell_base_array - +from openram.modules import pattern class sky130_replica_column(sky130_bitcell_base_array): """ @@ -31,8 +31,6 @@ class sky130_replica_column(sky130_bitcell_base_array): self.row_start = rbl[0] + 1 # End of regular word line rows self.row_end = self.row_start + rows - if not self.cell.end_caps: - self.row_size += 2 super().__init__(rows=self.row_size, cols=1, column_offset=column_offset, name=name) self.rows = rows @@ -41,23 +39,17 @@ class sky130_replica_column(sky130_bitcell_base_array): self.replica_bit = replica_bit # left, right, regular rows plus top/bottom dummy cells - self.total_size = self.left_rbl + rows + self.right_rbl + 2 + self.total_size = self.left_rbl + rows + self.right_rbl self.column_offset = column_offset - if self.rows % 2 == 0: - debug.error("Invalid number of rows {}. Number of rows must be even to connect to col ends".format(self.rows), -1) - if self.column_offset % 2 == 0: - debug.error("Invalid column_offset {}. Column offset must be odd to connect to col ends".format(self.rows), -1) - debug.check(replica_bit != 0 and replica_bit != rows, - "Replica bit cannot be the dummy row.") - debug.check(replica_bit <= self.left_rbl or replica_bit >= self.total_size - self.right_rbl - 1, - "Replica bit cannot be in the regular array.") - # if OPTS.tech_name == "sky130": - # debug.check(rows % 2 == 0 and (self.left_rbl + 1) % 2 == 0, - # "sky130 currently requires rows to be even and to start with X mirroring" - # + " (left_rbl must be even) for LVS.") - # commented out to support odd row counts while testing opc - + # if self.rows % 2 == 0: + # debug.error("Invalid number of rows {}. Number of rows must be even to connect to col ends".format(self.rows), -1) + # if self.column_offset % 2 == 0: + # debug.error("Invalid column_offset {}. Column offset must be odd to connect to col ends".format(self.rows), -1) + # debug.check(replica_bit != 0 and replica_bit != rows, + # "Replica bit cannot be the dummy row.") + # debug.check(replica_bit <= self.left_rbl or replica_bit >= self.total_size - self.right_rbl - 1, + # "Replica bit cannot be in the regular array.") self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -68,10 +60,7 @@ class sky130_replica_column(sky130_bitcell_base_array): self.create_instances() def create_layout(self): - self.place_instances() - - self.width = max([x.rx() for x in self.insts]) - self.height = max([x.uy() for x in self.insts]) + self.place_array() self.add_layout_pins() @@ -83,15 +72,15 @@ class sky130_replica_column(sky130_bitcell_base_array): self.create_all_bitline_names() #self.create_all_wordline_names(self.row_size+2) # +2 to add fake wl pins for colends - self.create_all_wordline_names(self.row_size+1, 1) + self.create_all_wordline_names(self.row_size) self.add_pin_list(self.all_bitline_names, "OUTPUT") self.add_pin_list(self.all_wordline_names, "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - self.add_pin("top_gate", "INPUT") - self.add_pin("bot_gate", "INPUT") + #self.add_pin("top_gate", "INPUT") + #self.add_pin("bot_gate", "INPUT") def add_modules(self): self.replica_cell = factory.create(module_type="replica_bitcell_1port", version="opt1") @@ -101,165 +90,44 @@ class sky130_replica_column(sky130_bitcell_base_array): self.dummy_cell = factory.create(module_type="dummy_bitcell_1port", version="opt1") self.dummy_cell2 = factory.create(module_type="dummy_bitcell_1port", version="opt1") - self.strap1 = factory.create(module_type="internal", version="wlstrap") - self.strap2 = factory.create(module_type="internal", version="wlstrap_p") - self.strap3 = factory.create(module_type="internal", version="wlstrapa_p") - - self.colend = factory.create(module_type="col_cap", version="colend") - self.edge_cell = self.colend - self.colenda = factory.create(module_type="col_cap", version="colenda") - self.colend_p_cent = factory.create(module_type="col_cap", version="colend_p_cent") - self.colenda_p_cent = factory.create(module_type="col_cap", version="colenda_p_cent") + self.strap = factory.create(module_type="internal", version="wlstrap_p") + self.strap2 = factory.create(module_type="internal", version="wlstrapa_p") def create_instances(self): - self.cell_inst = {} - self.array_layout = [] - alternate_bitcell = (self.rows + 1) % 2 + """ Create the module instances used in this design """ + self.all_inst={} + self.cell_inst={} + replica_row_opt1 = [geometry.instance("00_rep_opt1", mod=self.replica_cell, is_bitcell=True, mirror='MX')] \ + + [geometry.instance("01_strap1", mod=self.strap, is_bitcell=False, mirror='MX')] + + replica_row_opt1a = [geometry.instance("10_opt1a", mod=self.replica_cell2, is_bitcell=True)] \ + + [geometry.instance("11_strapa", mod=self.strap2, is_bitcell=False)] + + replica_dummy_row_opt1 = [geometry.instance("00_rep_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='MX')] \ + + [geometry.instance("01_rep_strap", mod=self.strap, is_bitcell=False, mirror='MX')] + + replica_dummy_row_opt1a = [geometry.instance("10_opt1a", mod=self.dummy_cell2, is_bitcell=True)] \ + + [geometry.instance("11_strapa", mod=self.strap2, is_bitcell=False)] + + bit_block = [] + current_row = self.row_start for row in range(self.total_size): - row_layout = [] - name="rbc_{0}".format(row) - # Top/bottom cell are always dummy cells. - # Regular array cells are replica cells (>left_rbl and self.left_rbl and row < self.total_size - 1 or row == self.replica_bit): - - if alternate_bitcell == 0: - row_layout.append(self.replica_cell) - self.cell_inst[row]=self.add_inst(name=name, mod=self.replica_cell) - self.connect_inst(self.get_bitcell_pins(row, 0)) - row_layout.append(self.strap2) - self.add_inst(name=name + "_strap_p", mod=self.strap2) - self.connect_inst(self.get_strap_pins(row, 0, name + "_strap_p")) - alternate_bitcell = 1 - + # Regular array cells are replica cells + # Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell. + # All other cells are dummies + if (row == self.replica_bit) or (row >= self.row_start and row < self.row_end): + if current_row % 2: + pattern.append_row_to_block(bit_block, replica_row_opt1) else: - row_layout.append(self.replica_cell2) - self.cell_inst[row]=self.add_inst(name=name, mod=self.replica_cell2) - self.connect_inst(self.get_bitcell_pins(row, 0)) - row_layout.append(self.strap3) - self.add_inst(name=name + "_strap", mod=self.strap3) - self.connect_inst(self.get_strap_pins(row, 0)) - alternate_bitcell = 0 - - elif (row == 0): - row_layout.append(self.colend) - self.cell_inst[row]=self.add_inst(name=name, mod=self.colend) - self.connect_inst(self.get_col_cap_pins(row, 0)) - row_layout.append(self.colend_p_cent) - self.add_inst(name=name + "_cap", mod=self.colend_p_cent) - self.connect_inst(self.get_col_cap_p_pins(row, 0)) - elif (row == self.total_size - 1): - row_layout.append(self.colenda) - self.cell_inst[row]=self.add_inst(name=name, mod=self.colenda) - self.connect_inst(self.get_col_cap_pins(row, 0)) - row_layout.append(self.colenda_p_cent) - self.add_inst(name=name + "_cap", mod=self.colenda_p_cent) - self.connect_inst(self.get_col_cap_p_pins(row, 0)) - - self.array_layout.append(row_layout) - - def place_instances(self, name_template="", row_offset=0): - col_offset = self.column_offset - yoffset = 0.0 - - for row in range(row_offset, len(self.array_layout) + row_offset): - xoffset = 0.0 - for col in range(col_offset, len(self.array_layout[row]) + col_offset): - self.place_inst = self.insts[(col - col_offset) + (row - row_offset) * len(self.array_layout[row - row_offset])] - if row == row_offset or row == (len(self.array_layout) + row_offset -1): - if row == row_offset: - self.place_inst.place(offset=[xoffset, yoffset + self.colend.height], mirror="MX") - else: - self.place_inst.place(offset=[xoffset, yoffset]) - - elif col % 2 == 0: - if row % 2 == 0: - self.place_inst.place(offset=[xoffset, yoffset + self.place_inst.height], mirror="MX") - else: - self.place_inst.place(offset=[xoffset, yoffset]) - else: - if row % 2 == 0: - self.place_inst.place(offset=[xoffset + self.place_inst.width, yoffset + self.place_inst.height], mirror="XY") - else: - self.place_inst.place(offset=[xoffset + self.place_inst.width, yoffset], mirror="MY") - - xoffset += self.place_inst.width - if row == row_offset: - yoffset += self.colend.height + pattern.append_row_to_block(bit_block, replica_row_opt1a) else: - yoffset += self.place_inst.height - - self.width = max([x.rx() for x in self.insts]) - self.height = max([x.uy() for x in self.insts]) - - def add_layout_pins(self): - """ Add the layout pins """ - for port in self.all_ports: - bl_pin = self.cell_inst[2].get_pin(self.cell.get_bl_name(port)) - self.add_layout_pin(text="bl_{0}_{1}".format(port, 0), - layer=bl_pin.layer, - offset=bl_pin.ll().scale(1, 0), - width=bl_pin.width(), - height=self.height) - bl_pin = self.cell_inst[2].get_pin(self.cell.get_br_name(port)) - self.add_layout_pin(text="br_{0}_{1}".format(port, 0), - layer=bl_pin.layer, - offset=bl_pin.ll().scale(1, 0), - width=bl_pin.width(), - height=self.height) - - row_range_max = self.total_size - 1 - row_range_min = 1 - - for port in self.all_ports: - for row in range(row_range_min, row_range_max): - wl_pin = self.cell_inst[row].get_pin(self.cell.get_wl_name(port)) - self.add_layout_pin(text="wl_{0}_{1}".format(port, row_range_max-row), - layer=wl_pin.layer, - offset=wl_pin.ll().scale(0, 1), - width=self.width, - height=wl_pin.height()) - - # for colend in [self.cell_inst[0], self.cell_inst[self.row_size]]: - # inst = self.cell_inst[row] - # for pin_name in ["top_gate", "bot_gate"]: - # pin = inst.get_pin("gate") - # self.add_layout_pin(text=pin_name, - # layer=pin.layer, - # offset=pin.ll(), - # width=pin.width(), - # height=pin.height()) - - for row in range(self.row_size + 2): - inst = self.cell_inst[row] - # add only 1 label per col - for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(inst, pin_name) - #if row == 2: - if 'VPB' or 'vpb' in self.cell_inst[row].mod.pins: - pin = inst.get_pin("vpb") - self.objs.append(geometry.rectangle(layer["nwell"], - pin.ll(), - pin.width(), - pin.height())) - self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) - - if 'VNB' or 'vnb' in self.cell_inst[row].mod.pins: - try: - from openram.tech import layer_override - if layer_override['VNB']: - pin = inst.get_pin("vnb") - self.add_label("gnd", pin.layer, pin.center()) - self.objs.append(geometry.rectangle(layer["pwellp"], - pin.ll(), - pin.width(), - pin.height())) - self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) - - - except: - pin = inst.get_pin("vnb") - self.add_label("gnd", pin.layer, pin.center()) + if current_row %2: + pattern.append_row_to_block(bit_block, replica_dummy_row_opt1) + else: + pattern.append_row_to_block(bit_block, replica_dummy_row_opt1a) + current_row += 1 + self.pattern = pattern(self, "replica_column", bit_block, num_rows=self.total_size, num_cols=len(replica_row_opt1a), name_template="rbc_r{0}_c{1}", ) + self.pattern.connect_array_raw() def exclude_all_but_replica(self): """ From a05ab6e908e1acd820bf67877bd127e543293bae Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 22 Aug 2023 01:09:19 -0700 Subject: [PATCH 33/48] route supplies + fix replica col dummy --- technology/sky130/custom/sky130_replica_column.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/technology/sky130/custom/sky130_replica_column.py b/technology/sky130/custom/sky130_replica_column.py index 9eca5dfd..6d6333fc 100644 --- a/technology/sky130/custom/sky130_replica_column.py +++ b/technology/sky130/custom/sky130_replica_column.py @@ -28,7 +28,7 @@ class sky130_replica_column(sky130_bitcell_base_array): # Row size is the number of rows with word lines self.row_size = sum(rbl) + rows # Start of regular word line rows - self.row_start = rbl[0] + 1 + self.row_start = rbl[0] # End of regular word line rows self.row_end = self.row_start + rows super().__init__(rows=self.row_size, cols=1, column_offset=column_offset, name=name) @@ -63,7 +63,7 @@ class sky130_replica_column(sky130_bitcell_base_array): self.place_array() self.add_layout_pins() - + self.route_supplies() self.add_boundary() self.DRC_LVS() From 64b0cd25d7bfb405bb0b23ab0a8ffbaae61fd4e8 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 22 Aug 2023 01:16:35 -0700 Subject: [PATCH 34/48] replica col passing --- technology/sky130/custom/sky130_replica_column.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/technology/sky130/custom/sky130_replica_column.py b/technology/sky130/custom/sky130_replica_column.py index 6d6333fc..64486e14 100644 --- a/technology/sky130/custom/sky130_replica_column.py +++ b/technology/sky130/custom/sky130_replica_column.py @@ -97,16 +97,16 @@ class sky130_replica_column(sky130_bitcell_base_array): """ Create the module instances used in this design """ self.all_inst={} self.cell_inst={} - replica_row_opt1 = [geometry.instance("00_rep_opt1", mod=self.replica_cell, is_bitcell=True, mirror='MX')] \ + replica_row_opt1 = [geometry.instance("00_rep_opt1", mod=self.replica_cell, is_bitcell=True, mirror='XY')] \ + [geometry.instance("01_strap1", mod=self.strap, is_bitcell=False, mirror='MX')] - replica_row_opt1a = [geometry.instance("10_opt1a", mod=self.replica_cell2, is_bitcell=True)] \ + replica_row_opt1a = [geometry.instance("10_opt1a", mod=self.replica_cell2, is_bitcell=True, mirror='MY')] \ + [geometry.instance("11_strapa", mod=self.strap2, is_bitcell=False)] replica_dummy_row_opt1 = [geometry.instance("00_rep_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='MX')] \ + [geometry.instance("01_rep_strap", mod=self.strap, is_bitcell=False, mirror='MX')] - replica_dummy_row_opt1a = [geometry.instance("10_opt1a", mod=self.dummy_cell2, is_bitcell=True)] \ + replica_dummy_row_opt1a = [geometry.instance("10_opt1a", mod=self.dummy_cell2, is_bitcell=True, mirror='MY')] \ + [geometry.instance("11_strapa", mod=self.strap2, is_bitcell=False)] bit_block = [] From 036cc54b996cc264d101cc97e54d009f04df81a7 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 24 Aug 2023 02:55:45 -0700 Subject: [PATCH 35/48] rba done w/o wordline --- compiler/modules/bitcell_base_array.py | 5 +- compiler/modules/dummy_array.py | 44 +- compiler/modules/pattern.py | 10 +- compiler/modules/replica_bitcell_array.py | 56 +- compiler/modules/replica_column.py | 64 +- .../sky130/custom/replica_bitcell_array.py | 731 ------------------ .../sky130/custom/sky130_bitcell_array.py | 39 +- .../custom/sky130_bitcell_base_array.py | 47 +- .../sky130/custom/sky130_dummy_array.py | 205 +---- .../custom/sky130_replica_bitcell_array.py | 419 +--------- .../sky130/custom/sky130_replica_column.py | 127 +-- 11 files changed, 208 insertions(+), 1539 deletions(-) delete mode 100644 technology/sky130/custom/replica_bitcell_array.py diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 87f9f149..16e70bb0 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -158,9 +158,10 @@ class bitcell_base_array(design): height=wl_pin.height()) def route_supplies(self): - for inst in self.cell_inst.values(): + for inst in self.insts: for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(inst, pin_name) + if pin_name in inst.mod.get_pin_names(): + self.copy_layout_pin(inst, pin_name) def add_layout_pins(self): """ Add the layout pins """ diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index b31f8c00..fbf79cd9 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -64,10 +64,9 @@ class dummy_array(bitcell_base_array): core_block[(1+self.mirror) %2][0] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size, name_template="bit_r{0}_c{1}") + self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size * 2, name_template="bit_r{0}_c{1}") self.pattern.connect_array() - def add_pins(self): # bitline pins are not added because they are floating for bl_name in self.get_bitline_names(): @@ -78,47 +77,6 @@ class dummy_array(bitcell_base_array): self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - def add_layout_pins(self): - """ Add the layout pins """ - - # Add the bitline metal, but not as pins since they are going to just be floating - # For some reason, LVS has an issue if we don't add this metal - bitline_names = self.cell.get_all_bitline_names() - for col in range(self.column_size): - for port in self.all_ports: - bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port]) - self.add_layout_pin(text="bl_{0}_{1}".format(port, col), - layer=bl_pin.layer, - offset=bl_pin.ll().scale(1, 0), - width=bl_pin.width(), - height=self.height) - br_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port + 1]) - self.add_layout_pin(text="br_{0}_{1}".format(port, col), - layer=br_pin.layer, - offset=br_pin.ll().scale(1, 0), - width=br_pin.width(), - height=self.height) - - wl_names = self.cell.get_all_wl_names() - for row in range(self.row_size): - for port in self.all_ports: - wl_pins = self.cell_inst[row, 0].get_pins(wl_names[port]) - for wl_pin in wl_pins: - self.add_layout_pin(text="wl_{0}_{1}".format(port, row), - layer=wl_pin.layer, - offset=wl_pin.ll().scale(0, 1), - 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): - inst = self.cell_inst[row, col] - for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(inst, pin_name) - def input_load(self): # FIXME: This appears to be old code from previous characterization. Needs to be updated. wl_wire = self.gen_wl_wire() diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index a7cc062f..c52d6480 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -122,7 +122,7 @@ class pattern(): for dr in range(len(block)): row_done = False for dc in range(len(block[0])): - if(self.bit_rows.count(self.num_rows) <= self.num_cols and self.bit_cols.count(self.bit_cols) <= self.num_rows): + if(self.bit_rows.count(self.num_rows) <= self.num_cols and self.bit_cols.count(self.num_cols) <= self.num_rows): inst = block[dr][dc] if(len(self.bit_rows) <= col + dc): self.bit_rows.append(0) @@ -211,3 +211,11 @@ class pattern(): def append_block_right_block(base_block, right_block): for row in base_block: row = row + right_block + + def rotate_list(lst, n): + # Loop through the range of n positions to rotate + for i in range(n): + # Remove the last element of the list and insert it at the beginning + lst.insert(0, lst.pop()) + # Return the rotated list + return lst diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index b3661aff..805b39f0 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -74,7 +74,7 @@ class replica_bitcell_array(bitcell_base_array): """ Array and dummy/replica columns """ # Bitcell array self.bitcell_array = factory.create(module_type="bitcell_array", - column_offset=1 + len(self.left_rbl), + column_offset=len(self.left_rbl), cols=self.column_size, rows=self.row_size) @@ -91,7 +91,7 @@ class replica_bitcell_array(bitcell_base_array): elif port in self.right_rbl: # These go bottom up starting from the top of the bitcell array. replica_bit = self.rbl[0] + self.row_size + port - 1 - column_offset = len(self.left_rbl) + self.column_size + 1 + column_offset = len(self.left_rbl) + self.column_size else: continue @@ -102,14 +102,21 @@ class replica_bitcell_array(bitcell_base_array): replica_bit=replica_bit) # Dummy row (for replica wordlines) - self.dummy_row = factory.create(module_type="dummy_array", + self.dummy_rows = {} + + for port in self.all_ports: + if port in self.left_rbl: + row_offset = self.row_size + elif port in self.right_rbl: + row_offset = 0 + else: + continue + self.dummy_rows[port] = factory.create(module_type="dummy_array", cols=self.column_size, rows=1, - # cap column + left replica column - # FIXME: these col offsets should really start at 0 because - # this is the left edge of the array... but changing them all is work - column_offset=1 + len(self.left_rbl), - mirror=0) + row_offset=row_offset, + column_offset=len(self.left_rbl), + mirror='R0') def add_pins(self): @@ -213,7 +220,7 @@ class replica_bitcell_array(bitcell_base_array): for port in self.all_ports: # TODO: tie to self.rbl or whatever if self.rbl[port] != 0: self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port), - mod=self.dummy_row)) + mod=self.dummy_rows[port])) self.connect_inst(self.all_bitline_names + self.rbl_wordline_names[port] + self.supplies) else: self.dummy_row_replica_insts.append(None) @@ -244,11 +251,13 @@ class replica_bitcell_array(bitcell_base_array): self.add_layout_pins() self.route_supplies() + + ll=vector(min([x.lx() for x in self.insts]),min([y.by() for y in self.insts])) - self.height = (sum(self.rbl) + self.row_size) * self.cell.height - self.width = (len(self.rbls) + self.column_size) * self.cell.width + self.width = max([x.rx() for x in self.insts]) - min([x.lx() for x in self.insts]) + self.height = max([x.uy() for x in self.insts]) - min([y.by() for y in self.insts]) - self.add_boundary() + self.add_boundary(ll) self.DRC_LVS() @@ -280,25 +289,24 @@ class replica_bitcell_array(bitcell_base_array): # Grow from left to right, toward the array for bit, port in enumerate(self.left_rbl): - offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0]) + rbc_dimension = vector(self.replica_col_insts[port].width, self.cell.height) + offset = rbc_dimension.scale(-len(self.left_rbl) + bit, -self.rbl[0]) self.replica_col_insts[bit].place(offset) # Grow to the right of the bitcell array, array outward for bit, port in enumerate(self.right_rbl): - offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0]) + offset = self.bitcell_array_inst.lr() + rbc_dimension.scale(bit, -self.rbl[0]) self.replica_col_insts[self.rbl[0] + bit].place(offset) # Replica dummy rows # Add the dummy rows even if we aren't adding the replica column to this bitcell array # These grow up, toward the array for bit in range(self.rbl[0]): - dummy_offset = self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2) - self.dummy_row_replica_insts[bit].place(offset=dummy_offset, - mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0") + dummy_offset = self.bitcell_array_inst.ll() - vector(0, self.dummy_row_replica_insts[bit].height) + self.dummy_row_replica_insts[bit].place(offset=dummy_offset) # These grow up, away from the array for bit in range(self.rbl[1]): - dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul() - self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset, - mirror="MX" if (self.row_size + bit) % 2 else "R0") + dummy_offset = self.bitcell_array_inst.ul() + self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset) def add_layout_pins(self): """ Add the layout pins """ @@ -317,7 +325,7 @@ class replica_bitcell_array(bitcell_base_array): # Replica wordlines (go by the row instead of replica column because we may have to add a pin # even though the column is in another local bitcell array) 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()): + for (wl_name, pin_name) in zip(names, self.dummy_rows[0].get_wordline_names()): pin = inst.get_pin(pin_name) self.add_layout_pin(text=wl_name, layer=pin.layer, @@ -347,12 +355,6 @@ class replica_bitcell_array(bitcell_base_array): width=pin.width(), height=self.height) - def route_supplies(self): - """ just copy supply pins from all instances """ - for inst in self.insts: - for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(inst, pin_name) - def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" # Dynamic Power from Bitline diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index dc1b6f12..1645442e 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -45,10 +45,10 @@ class replica_column(bitcell_base_array): debug.check(replica_bit < self.row_start or replica_bit >= self.row_end, "Replica bit cannot be in the regular array.") - if layer_props.replica_column.even_rows: - debug.check(rows % 2 == 0 and (self.left_rbl + 1) % 2 == 0, - "sky130 currently requires rows to be even and to start with X mirroring" - + " (left_rbl must be odd) for LVS.") + #if layer_props.replica_column.even_rows: + # debug.check(rows % 2 == 0 and (self.left_rbl + 1) % 2 == 0, + # "sky130 currently requires rows to be even and to start with X mirroring" + # + " (left_rbl must be odd) for LVS.") self.create_netlist() if not OPTS.netlist_only: @@ -95,63 +95,19 @@ class replica_column(bitcell_base_array): # Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell. # All other cells are dummies if (row == self.replica_bit) or (row >= self.row_start and row < self.row_end): - if current_row % 2: - core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MX') - else: + if current_row % 2 == 0: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True) - else: - if current_row %2: - core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='MX') else: + core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MX') + else: + if current_row %2 == 0: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True) + else: + core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='MX') current_row += 1 self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.total_size, num_cols=self.column_size, name_template="rbc_r{0}_c{1}") self.pattern.connect_array() - def add_layout_pins(self): - for port in self.all_ports: - bl_pin = self.cell_inst[0,0].get_pin(self.cell.get_bl_name(port)) - self.add_layout_pin(text="bl_{0}_{1}".format(port, 0), - layer=bl_pin.layer, - offset=bl_pin.ll().scale(1, 0), - width=bl_pin.width(), - height=self.height) - bl_pin = self.cell_inst[0,0].get_pin(self.cell.get_br_name(port)) - self.add_layout_pin(text="br_{0}_{1}".format(port, 0), - layer=bl_pin.layer, - offset=bl_pin.ll().scale(1, 0), - width=bl_pin.width(), - height=self.height) - - for port in self.all_ports: - for row in range(self.total_size): - wl_pin = self.cell_inst[row,0].get_pin(self.cell.get_wl_name(port)) - self.add_layout_pin(text="wl_{0}_{1}".format(port, row), - layer=wl_pin.layer, - offset=wl_pin.ll().scale(0, 1), - width=self.width, - height=wl_pin.height()) - - def get_bitline_names(self, port=None): - if port == None: - return self.all_bitline_names - else: - return self.bitline_names[port] - - def get_bitcell_pins(self, row, col): - """ - Creates a list of connections in the bitcell, - indexed by column and row, for instance use in bitcell_array - """ - bitcell_pins = [] - for port in self.all_ports: - bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))]) - bitcell_pins.extend([x for x in self.all_wordline_names if x.endswith("_{0}".format(row))]) - bitcell_pins.append("vdd") - bitcell_pins.append("gnd") - - return bitcell_pins - def get_bitcell_pins_col_cap(self, row, col): """ Creates a list of connections in the bitcell, diff --git a/technology/sky130/custom/replica_bitcell_array.py b/technology/sky130/custom/replica_bitcell_array.py deleted file mode 100644 index 2b1ab54a..00000000 --- a/technology/sky130/custom/replica_bitcell_array.py +++ /dev/null @@ -1,731 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz -# All rights reserved. -# -from openram import debug -from openram.base import vector -from openram.base import contact -from openram.sram_factory import factory -from openram.tech import drc, spice -from openram.tech import cell_properties as props -from openram import OPTS -from openram.modules import bitcell_base_array - - -class replica_bitcell_array(bitcell_base_array): - """ - Creates a bitcell array of cols x rows and then adds the replica - and dummy columns and rows. Replica columns are on the left and - right, respectively and connected to the given bitcell ports. - Dummy are the outside columns/rows with WL and BL tied to gnd. - Requires a regular bitcell array, replica bitcell, and dummy - bitcell (BL/BR disconnected). - """ - def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""): - super().__init__(name=name, rows=rows, cols=cols, column_offset=0) - debug.info(1, "Creating {0} {1} x {2} rbls: {3} left_rbl: {4} right_rbl: {5}".format(self.name, - rows, - cols, - rbl, - left_rbl, - right_rbl)) - self.add_comment("rows: {0} cols: {1}".format(rows, cols)) - self.add_comment("rbl: {0} left_rbl: {1} right_rbl: {2}".format(rbl, left_rbl, right_rbl)) - - self.column_size = cols - self.row_size = rows - # This is how many RBLs are in all the arrays - if rbl: - self.rbl = rbl - else: - self.rbl=[1, 1 if len(self.all_ports)>1 else 0] - # This specifies which RBL to put on the left or right - # by port number - # This could be an empty list - if left_rbl != None: - self.left_rbl = left_rbl - else: - self.left_rbl = [0] - # This could be an empty list - if right_rbl != None: - self.right_rbl = right_rbl - else: - self.right_rbl=[1] if len(self.all_ports) > 1 else [] - self.rbls = self.left_rbl + self.right_rbl - - debug.check(sum(self.rbl) == len(self.all_ports), - "Invalid number of RBLs for port configuration.") - debug.check(sum(self.rbl) >= len(self.left_rbl) + len(self.right_rbl), - "Invalid number of RBLs for port configuration.") - - # Two dummy rows plus replica even if we don't add the column - self.extra_rows = sum(self.rbl) - # Two dummy cols plus replica if we add the column - self.extra_cols = len(self.left_rbl) + len(self.right_rbl) - - # If we aren't using row/col caps, then we need to use the bitcell - if not self.cell.end_caps: - self.extra_rows += 2 - self.extra_cols += 2 - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - # We don't offset this because we need to align - # the replica bitcell in the control logic - # self.offset_all_coordinates() - - def create_netlist(self): - """ Create and connect the netlist """ - self.add_modules() - self.add_pins() - self.create_instances() - - def add_modules(self): - """ Array and dummy/replica columns - - d or D = dummy cell (caps to distinguish grouping) - r or R = replica cell (caps to distinguish grouping) - b or B = bitcell - replica columns 1 - v v - bdDDDDDDDDDDDDDDdb <- Dummy row - bdDDDDDDDDDDDDDDrb <- Dummy row - br--------------rb - br| Array |rb - br| row x col |rb - br--------------rb - brDDDDDDDDDDDDDDdb <- Dummy row - bdDDDDDDDDDDDDDDdb <- Dummy row - - ^^^^^^^^^^^^^^^ - dummy rows cols x 1 - - ^ dummy columns ^ - 1 x (rows + 4) - """ - # Bitcell array - self.bitcell_array = factory.create(module_type="bitcell_array", - column_offset=1 + len(self.left_rbl), - cols=self.column_size, - rows=self.row_size) - - # Replica bitlines - self.replica_columns = {} - - for port in self.all_ports: - if port in self.left_rbl: - # We will always have self.rbl[0] rows of replica wordlines below - # the array. - # These go from the top (where the bitcell array starts ) down - replica_bit = self.rbl[0] - port - column_offset = self.rbl[0] - - elif port in self.right_rbl: - - # We will always have self.rbl[0] rows of replica wordlines below - # the array. - # These go from the bottom up - replica_bit = self.rbl[0] + self.row_size + port - column_offset = self.rbl[0] + self.column_size + 1 - else: - continue - - self.replica_columns[port] = factory.create(module_type="replica_column", - rows=self.row_size, - rbl=self.rbl, - column_offset=column_offset, - replica_bit=replica_bit) - - # Dummy row - self.dummy_row = factory.create(module_type="dummy_array", - cols=self.column_size, - rows=1, - # dummy column + left replica column - column_offset=1 + len(self.left_rbl), - mirror=0) - - # Dummy Row or Col Cap, depending on bitcell array properties - col_cap_module_type = ("col_cap_array" if self.cell.end_caps else "dummy_array") - self.col_cap_top = factory.create(module_type=col_cap_module_type, - cols=self.column_size, - rows=1, - # dummy column + left replica column(s) - column_offset=1 + len(self.left_rbl), - mirror=0, - location="top") - - self.col_cap_bottom = factory.create(module_type=col_cap_module_type, - cols=self.column_size, - rows=1, - # dummy column + left replica column(s) - column_offset=1 + len(self.left_rbl), - mirror=0, - location="bottom") - - # Dummy Col or Row Cap, depending on bitcell array properties - row_cap_module_type = ("row_cap_array" if self.cell.end_caps else "dummy_array") - - self.row_cap_left = factory.create(module_type=row_cap_module_type, - cols=1, - column_offset=0, - rows=self.row_size + self.extra_rows, - mirror=(self.rbl[0] + 1) % 2) - - self.row_cap_right = factory.create(module_type=row_cap_module_type, - cols=1, - # dummy column - # + left replica column(s) - # + bitcell columns - # + right replica column(s) - column_offset=1 + len(self.left_rbl) + self.column_size + self.rbl[0], - rows=self.row_size + self.extra_rows, - mirror=(self.rbl[0] + 1) %2) - - def add_pins(self): - - # Arrays are always: - # bitlines (column first then port order) - # word lines (row first then port order) - # dummy wordlines - # replica wordlines - # regular wordlines (bottom to top) - # # dummy bitlines - # replica bitlines (port order) - # regular bitlines (left to right port order) - # - # vdd - # gnd - - self.add_bitline_pins() - self.add_wordline_pins() - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") - - def add_bitline_pins(self): - # The bit is which port the RBL is for - for bit in self.rbls: - for port in self.all_ports: - self.rbl_bitline_names[bit].append("rbl_bl_{0}_{1}".format(port, bit)) - for port in self.all_ports: - self.rbl_bitline_names[bit].append("rbl_br_{0}_{1}".format(port, bit)) - # Make a flat list too - self.all_rbl_bitline_names = [x for sl in self.rbl_bitline_names for x in sl] - - self.bitline_names = self.bitcell_array.bitline_names - # Make a flat list too - self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl] - - for port in self.left_rbl: - self.add_pin_list(self.rbl_bitline_names[port], "INOUT") - self.add_pin_list(self.all_bitline_names, "INOUT") - for port in self.right_rbl: - self.add_pin_list(self.rbl_bitline_names[port], "INOUT") - - def add_wordline_pins(self): - - # Wordlines to ground - self.gnd_wordline_names = [] - - for port in self.all_ports: - for bit in self.all_ports: - self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit)) - if bit != port: - self.gnd_wordline_names.append("rbl_wl_{0}_{1}".format(port, bit)) - - self.all_rbl_wordline_names = [x for sl in self.rbl_wordline_names for x in sl] - - self.wordline_names = self.bitcell_array.wordline_names - self.all_wordline_names = self.bitcell_array.all_wordline_names - - # All wordlines including dummy and RBL - self.replica_array_wordline_names = [] - self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names())) - for bit in range(self.rbl[0]): - self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[bit]]) - self.replica_array_wordline_names.extend(self.all_wordline_names) - for bit in range(self.rbl[1]): - self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[self.rbl[0] + bit]]) - self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names())) - - for port in range(self.rbl[0]): - self.add_pin(self.rbl_wordline_names[port][port], "INPUT") - self.add_pin_list(self.all_wordline_names, "INPUT") - for port in range(self.rbl[0], self.rbl[0] + self.rbl[1]): - self.add_pin(self.rbl_wordline_names[port][port], "INPUT") - - def create_instances(self): - """ Create the module instances used in this design """ - self.supplies = ["vdd", "gnd"] - - # Used for names/dimensions only - self.cell = factory.create(module_type=OPTS.bitcell) - - # Main array - self.bitcell_array_inst=self.add_inst(name="bitcell_array", - mod=self.bitcell_array) - self.connect_inst(self.all_bitline_names + self.all_wordline_names + self.supplies) - - # Replica columns - self.replica_col_insts = [] - for port in self.all_ports: - if port in self.rbls: - self.replica_col_insts.append(self.add_inst(name="replica_col_{}".format(port), - mod=self.replica_columns[port])) - self.connect_inst(self.rbl_bitline_names[port] + self.replica_array_wordline_names + self.supplies) - else: - self.replica_col_insts.append(None) - - # Dummy rows under the bitcell array (connected with with the replica cell wl) - self.dummy_row_replica_insts = [] - # Note, this is the number of left and right even if we aren't adding the columns to this bitcell array! - for port in self.all_ports: - self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port), - mod=self.dummy_row)) - self.connect_inst(self.all_bitline_names + [x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[port]] + self.supplies) - - # Top/bottom dummy rows or col caps - self.dummy_row_insts = [] - self.dummy_row_insts.append(self.add_inst(name="dummy_row_bot", - mod=self.col_cap_bottom)) - self.connect_inst(self.all_bitline_names + ["gnd"] * len(self.col_cap_bottom.get_wordline_names()) + self.supplies) - self.dummy_row_insts.append(self.add_inst(name="dummy_row_top", - mod=self.col_cap_top)) - self.connect_inst(self.all_bitline_names + ["gnd"] * len(self.col_cap_top.get_wordline_names()) + self.supplies) - - # Left/right Dummy columns - self.dummy_col_insts = [] - self.dummy_col_insts.append(self.add_inst(name="dummy_col_left", - mod=self.row_cap_left)) - self.connect_inst(["dummy_left_" + bl for bl in self.row_cap_left.all_bitline_names] + self.replica_array_wordline_names + self.supplies) - self.dummy_col_insts.append(self.add_inst(name="dummy_col_right", - mod=self.row_cap_right)) - self.connect_inst(["dummy_right_" + bl for bl in self.row_cap_right.all_bitline_names] + self.replica_array_wordline_names + self.supplies) - - def create_layout(self): - - # This creates space for the unused wordline connections as well as the - # row-based or column based power and ground lines. - self.vertical_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[0])) - self.horizontal_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[2])) - 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) - 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 - self.bitcell_array_inst.place(offset=self.unused_offset) - - self.add_replica_columns() - - self.add_end_caps() - - # Array was at (0, 0) but move everything so it is at the lower left - # We move DOWN the number of left RBL even if we didn't add the column to this bitcell array - # Note that this doesn't include the row/col cap - array_offset = self.bitcell_offset.scale(1 + len(self.left_rbl), 1 + self.rbl[0]) - 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() - - self.add_layout_pins() - - self.route_supplies() - - 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() - - 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 the top of all replica columns. """ - return self.dummy_row_insts[0].by() - - def get_replica_bottom(self): - """ Return the bottom of all replica columns. """ - return self.dummy_row_insts[0].uy() - - def get_replica_left(self): - """ Return the left of all replica columns. """ - return self.dummy_col_insts[0].rx() - - def get_replica_right(self): - """ Return the right of all replica columns. """ - return self.dummy_col_insts[1].rx() - - def get_column_offsets(self): - """ - Return an array of the x offsets of all the regular bits - """ - offsets = [x + self.bitcell_array_inst.lx() for x in self.bitcell_array.get_column_offsets()] - return offsets - - def add_replica_columns(self): - """ Add replica columns on left and right of array """ - - # Grow from left to right, toward the array - for bit, port in enumerate(self.left_rbl): - offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - 1) + self.unused_offset - self.replica_col_insts[bit].place(offset) - # Grow to the right of the bitcell array, array outward - for bit, port in enumerate(self.right_rbl): - offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - 1) - self.replica_col_insts[self.rbl[0] + bit].place(offset) - - # Replica dummy rows - # Add the dummy rows even if we aren't adding the replica column to this bitcell array - # These grow up, toward the array - for bit in range(self.rbl[0]): - dummy_offset = self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2) + self.unused_offset - self.dummy_row_replica_insts[bit].place(offset=dummy_offset, - mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0") - # These grow up, away from the array - for bit in range(self.rbl[1]): - dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul() - self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset, - mirror="MX" if (self.row_size + bit) % 2 else "R0") - - def add_end_caps(self): - """ Add dummy cells or end caps around the array """ - - # Far top dummy row (first row above array is NOT flipped if even number of rows) - flip_dummy = (self.row_size + self.rbl[1]) % 2 - dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1] + flip_dummy) + self.bitcell_array_inst.ul() - self.dummy_row_insts[1].place(offset=dummy_row_offset, - mirror="MX" if flip_dummy else "R0") - - # Far bottom dummy row (first row below array IS flipped) - flip_dummy = (self.rbl[0] + 1) % 2 - dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - 1 + flip_dummy) + self.unused_offset - self.dummy_row_insts[0].place(offset=dummy_row_offset, - mirror="MX" if flip_dummy else "R0") - # Far left dummy col - # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array - dummy_col_offset = self.bitcell_offset.scale(-len(self.left_rbl) - 1, -self.rbl[0] - 1) + self.unused_offset - self.dummy_col_insts[0].place(offset=dummy_col_offset) - - # Far right dummy col - # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array - dummy_col_offset = self.bitcell_offset.scale(len(self.right_rbl), -self.rbl[0] - 1) + self.bitcell_array_inst.lr() - self.dummy_col_insts[1].place(offset=dummy_col_offset) - - def add_layout_pins(self): - """ Add the layout pins """ - - # All wordlines - # Main array wl and bl/br - for pin_name in self.all_wordline_names: - pin_list = self.bitcell_array_inst.get_pins(pin_name) - for pin in pin_list: - self.add_layout_pin(text=pin_name, - layer=pin.layer, - offset=pin.ll().scale(0, 1), - width=self.width, - height=pin.height()) - - # Replica wordlines (go by the row instead of replica column because we may have to add a pin - # even though the column is in another local bitcell array) - 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: - continue - pin = inst.get_pin(pin_name) - self.add_layout_pin(text=wl_name, - layer=pin.layer, - offset=pin.ll().scale(0, 1), - width=self.width, - height=pin.height()) - - for pin_name in self.all_bitline_names: - pin_list = self.bitcell_array_inst.get_pins(pin_name) - for pin in pin_list: - self.add_layout_pin(text=pin_name, - layer=pin.layer, - offset=pin.ll().scale(1, 0), - width=pin.width(), - height=self.height) - - # Replica bitlines - if len(self.rbls) > 0: - for (names, inst) in zip(self.rbl_bitline_names, self.replica_col_insts): - pin_names = self.replica_columns[self.rbls[0]].all_bitline_names - for (bl_name, pin_name) in zip(names, pin_names): - pin = inst.get_pin(pin_name) - self.add_layout_pin(text=bl_name, - layer=pin.layer, - offset=pin.ll().scale(1, 0), - width=pin.width(), - height=self.height) - - def route_supplies(self): - - if OPTS.bitcell == "pbitcell": - bitcell = factory.create(module_type="pbitcell") - else: - bitcell = getattr(props, "bitcell_{}port".format(OPTS.num_ports)) - - vdd_dir = bitcell.vdd_dir - gnd_dir = bitcell.gnd_dir - - # vdd/gnd are only connected in the perimeter cells - supply_insts = self.dummy_col_insts + self.dummy_row_insts - - # For the wordlines - top_bot_mult = 1 - left_right_mult = 1 - - # 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) - # 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) - # 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) - 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"): - 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"): - 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): - """ 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_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_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 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) - - 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, -# start=bot_loc, -# end=top_loc) - self.add_layout_pin_segment_center(text=name, - layer=layer, - start=bot_loc, - end=top_loc, - width=top_via.second_layer_width) - - 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] - side_via = contact(layer_stack=self.supply_stack, - directions=("V", "V")) - -# 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, - width=side_via.first_layer_height) - - 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 - 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, [cell_loc, pin_loc]) - - def analytical_power(self, corner, load): - """Power of Bitcell array and bitline in nW.""" - # Dynamic Power from Bitline - bl_wire = self.gen_bl_wire() - cell_load = 2 * bl_wire.return_input_cap() - bl_swing = OPTS.rbl_delay_percentage - freq = spice["default_event_frequency"] - bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) - - # Calculate the bitcell power which currently only includes leakage - cell_power = self.cell.analytical_power(corner, load) - - # Leakage power grows with entire array and bitlines. - total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size, - cell_power.leakage * self.column_size * self.row_size) - return total_power - - - def gen_bl_wire(self): - if OPTS.netlist_only: - height = 0 - else: - height = self.height - bl_pos = 0 - bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1")) - bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell - return bl_wire - - def graph_exclude_bits(self, targ_row=None, targ_col=None): - """ - Excludes bits in column from being added to graph except target - """ - self.bitcell_array.graph_exclude_bits(targ_row, targ_col) - - def graph_exclude_replica_col_bits(self): - """ - Exclude all replica/dummy cells in the replica columns except the replica bit. - """ - - for port in self.left_rbl + self.right_rbl: - self.replica_columns[port].exclude_all_but_replica() - - def get_cell_name(self, inst_name, row, col): - """ - Gets the spice name of the target bitcell. - """ - return self.bitcell_array.get_cell_name(inst_name + "{}x".format(OPTS.hier_seperator) + self.bitcell_array_inst.name, row, col) - - def clear_exclude_bits(self): - """ - Clears the bit exclusions - """ - self.bitcell_array.init_graph_params() diff --git a/technology/sky130/custom/sky130_bitcell_array.py b/technology/sky130/custom/sky130_bitcell_array.py index 4c37fb11..90c169c7 100644 --- a/technology/sky130/custom/sky130_bitcell_array.py +++ b/technology/sky130/custom/sky130_bitcell_array.py @@ -19,21 +19,9 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): Assumes bit-lines and word lines are connected by abutment. """ def __init__(self, rows, cols, column_offset=0, name=""): - # Don't call the regular bitcell_array constructor since we don't want its constructor, just - # some of it's useful member functions - sky130_bitcell_base_array.__init__(self, rows=rows, cols=cols, column_offset=column_offset, name=name) - if self.row_size % 2 == 0: - debug.error("Invalid number of rows {}. number of rows (excluding dummy rows) must be odd to connect to col ends".format(self.row_size), -1) - debug.info(1, "Creating {0} {1} x {2}".format(self.name, self.row_size, self.column_size)) - self.add_comment("rows: {0} cols: {1}".format(self.row_size, self.column_size)) - - # This will create a default set of bitline/wordline names - self.create_all_bitline_names() - self.create_all_wordline_names() - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - #self.add_supply_pins() + if rows % 2 == 0: + debug.error("Invalid number of rows {}. number of rows (excluding dummy rows) must be odd to connect to col ends".format(rows), -1) + super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) def add_modules(self): """ Add the modules used in this design """ @@ -49,19 +37,22 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): """ Create the module instances used in this design """ self.all_inst={} self.cell_inst={} - bit_row_opt1 = [geometry.instance("00_opt1", mod=self.cell, is_bitcell=True, mirror='MX')] \ - + [geometry.instance("01_strap", mod=self.strap, is_bitcell=False, mirror='MX')]\ - + [geometry.instance("02_opt1", mod=self.cell, is_bitcell=True, mirror='XY')] \ - + [geometry.instance("03_strap_p", mod=self.strap_p, is_bitcell=False, mirror='MX')] + + bit_row_opt1 = [geometry.instance("00_opt1", mod=self.cell, is_bitcell=True, mirror='XY')] \ + + [geometry.instance("01_strap_p", mod=self.strap_p, is_bitcell=False, mirror='MX')]\ + + [geometry.instance("02_opt1", mod=self.cell, is_bitcell=True, mirror='MX')] \ + + [geometry.instance("03_strap", mod=self.strap, is_bitcell=False, mirror='MX')] - bit_row_opt1a = [geometry.instance("10_opt1a", mod=self.cella, is_bitcell=True)] \ - + [geometry.instance("11_strapa", mod=self.strapa, is_bitcell=False)] \ - + [geometry.instance("12_opt1a", mod=self.cella, is_bitcell=True, mirror='MY')] \ - + [geometry.instance("13_strapa_p", mod=self.strapa_p, is_bitcell=False)] + bit_row_opt1a = [geometry.instance("10_opt1a", mod=self.cella, is_bitcell=True, mirror='MY')] \ + + [geometry.instance("11_strap_p", mod=self.strap_p, is_bitcell=False)] \ + + [geometry.instance("12_opt1a", mod=self.cella, is_bitcell=True)] \ + + [geometry.instance("13_strapa", mod=self.strapa, is_bitcell=False)] bit_block = [] pattern.append_row_to_block(bit_block, bit_row_opt1) pattern.append_row_to_block(bit_block, bit_row_opt1a) - self.pattern = pattern(self, "bitcell_array", bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.row_size/2), name_template="bit_r{0}_c{1}") + for row in bit_block: + row = pattern.rotate_list(row, self.column_offset * 2) + self.pattern = pattern(self, "bitcell_array", bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="bit_r{0}_c{1}") self.pattern.connect_array() diff --git a/technology/sky130/custom/sky130_bitcell_base_array.py b/technology/sky130/custom/sky130_bitcell_base_array.py index 68814d65..7ad024ab 100644 --- a/technology/sky130/custom/sky130_bitcell_base_array.py +++ b/technology/sky130/custom/sky130_bitcell_base_array.py @@ -86,33 +86,34 @@ class sky130_bitcell_base_array(bitcell_base_array): def route_supplies(self): # Copy a vdd/gnd layout pin from every cell - print("routing power") + super().route_supplies() for inst in self.insts: if "VPWR" in inst.mod.pins: self.copy_layout_pin(inst, "VPWR", "vdd") if "VGND" in inst.mod.pins: self.copy_layout_pin(inst, "VGND", "gnd") - - for col in range(self.column_size): - inst = self.cell_inst[0,col] - pin = inst.get_pin("vpb") - self.objs.append(geometry.rectangle(layer["nwell"], - pin.ll(), - pin.width(), - pin.height())) - self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) - - try: - from openram.tech import layer_override - if layer_override['VNB']: - pin = inst.get_pin("vnb") - self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) - self.objs.append(geometry.rectangle(layer["pwellp"], - pin.ll(), - pin.width(), - pin.height())) - except: - pin = inst.get_pin("vnb") - self.add_label("vdd", pin.layer, pin.center()) + + if hasattr(self, 'cell_inst'): + for col in range(self.column_size): + inst = self.cell_inst[0,col] + pin = inst.get_pin("vpb") + self.objs.append(geometry.rectangle(layer["nwell"], + pin.ll(), + pin.width(), + pin.height())) + self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) + + try: + from openram.tech import layer_override + if layer_override['VNB']: + pin = inst.get_pin("vnb") + self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) + self.objs.append(geometry.rectangle(layer["pwellp"], + pin.ll(), + pin.width(), + pin.height())) + except: + pin = inst.get_pin("vnb") + self.add_label("vdd", pin.layer, pin.center()) diff --git a/technology/sky130/custom/sky130_dummy_array.py b/technology/sky130/custom/sky130_dummy_array.py index 3e1c5805..8fd3e00f 100644 --- a/technology/sky130/custom/sky130_dummy_array.py +++ b/technology/sky130/custom/sky130_dummy_array.py @@ -10,186 +10,63 @@ from openram.sram_factory import factory from openram.tech import layer from openram import OPTS from .sky130_bitcell_base_array import sky130_bitcell_base_array +from openram.modules import dummy_array +from openram.modules import pattern +from math import ceil -class sky130_dummy_array(sky130_bitcell_base_array): +class sky130_dummy_array(dummy_array, sky130_bitcell_base_array): """ Generate a dummy row/column for the replica array. """ def __init__(self, rows, cols, column_offset=0, row_offset=0 ,mirror=0, location="", name=""): - - super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) + self.location = location + self.row_offset = row_offset self.mirror = mirror - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - def create_netlist(self): - """ Create and connect the netlist """ - # This will create a default set of bitline/wordline names - self.create_all_bitline_names() - self.create_all_wordline_names() - - self.add_modules() - self.add_pins() - self.create_instances() - - def create_layout(self): - self.place_array("dummy_r{0}_c{1}", self.mirror) - - self.add_layout_pins() - self.add_supply_pins() - self.add_boundary() - - self.DRC_LVS() + super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) def add_modules(self): """ Add the modules used in this design """ self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell, version="opt1") - self.dummy_cell2 = factory.create(module_type=OPTS.dummy_bitcell, version="opt1a") + self.dummy_cella = factory.create(module_type=OPTS.dummy_bitcell, version="opt1a") self.strap = factory.create(module_type="internal", version="wlstrap") - self.strap2 = factory.create(module_type="internal", version="wlstrap_p") - self.strap3 = factory.create(module_type="internal", version="wlstrapa") - self.strap4 = factory.create(module_type="internal", version="wlstrapa_p") + self.strap_p = factory.create(module_type="internal", version="wlstrap_p") + self.strapa = factory.create(module_type="internal", version="wlstrapa") + self.strapa_p = factory.create(module_type="internal", version="wlstrapa_p") self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") def create_instances(self): """ Create the module instances used in this design """ - self.cell_inst = {} - self.array_layout = [] - alternate_bitcell = (self.row_size + 1) % 2 - for row in range(0, self.row_size): + # this code needs to use connect_array_raw() to make dummy columns correctly, but single port shouldn't need these since there are dedicated cap cells + self.all_inst={} + self.cell_inst={} + + bit_row_opt1 = [geometry.instance("00_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='XY')] \ + + [geometry.instance("01_strap_p", mod=self.strap_p, is_bitcell=False, mirror='MX')]\ + + [geometry.instance("02_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='MX')] \ + + [geometry.instance("03_strap", mod=self.strap, is_bitcell=False, mirror='MX')] + + bit_row_opt1a = [geometry.instance("10_opt1a", mod=self.dummy_cella, is_bitcell=True, mirror='MY')] \ + + [geometry.instance("11_strap_p", mod=self.strap_p, is_bitcell=False)] \ + + [geometry.instance("12_opt1a", mod=self.dummy_cella, is_bitcell=True)] \ + + [geometry.instance("13_strapaa", mod=self.strapa, is_bitcell=False)] - row_layout = [] - - alternate_strap = (self.row_size + 1) % 2 - for col in range(0, self.column_size): - if alternate_bitcell == 1: - row_layout.append(self.dummy_cell) - self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col), - mod=self.dummy_cell) - else: - row_layout.append(self.dummy_cell2) - self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col), - mod=self.dummy_cell2) - self.connect_inst(self.get_bitcell_pins(row, col)) - if col != self.column_size - 1: - if alternate_strap: - if col % 2: - name="row_{}_col_{}_wlstrap_p".format(row, col) - row_layout.append(self.strap4) - self.add_inst(name=name, - mod=self.strap4) - else: - name="row_{}_col_{}_wlstrapa_p".format(row, col) - row_layout.append(self.strap2) - self.add_inst(name=name, - mod=self.strap2) - alternate_strap = 0 - else: - if col % 2: - name="row_{}_col_{}_wlstrap".format(row, col) - row_layout.append(self.strap) - self.add_inst(name=name, - mod=self.strap) - else: - name="row_{}_col_{}_wlstrapa".format(row, col) - row_layout.append(self.strap3) - self.add_inst(name=name, - mod=self.strap3) - alternate_strap = 1 - - self.connect_inst(self.get_strap_pins(row, col, name)) - if alternate_bitcell == 0: - alternate_bitcell = 1 + + bit_block = [] + if(self.row_offset % 2 == 0): + next_row = 0 + else: + next_row = 1 + + for i in range(self.row_size): + if next_row == 0: + pattern.append_row_to_block(bit_block, bit_row_opt1) + next_row = 1 else: - alternate_bitcell = 0 - self.array_layout.append(row_layout) + pattern.append_row_to_block(bit_block, bit_row_opt1a) + next_row = 0 - def add_pins(self): - # bitline pins are not added because they are floating - for bl in range(self.column_size): - self.add_pin("bl_0_{}".format(bl)) - self.add_pin("br_0_{}".format(bl)) - for wl_name in self.get_wordline_names(): - self.add_pin(wl_name, "INPUT") - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") - #self.add_pin("vpb", "BIAS") - #Sself.add_pin("vnb", "BIAS") + for row in bit_block: + row = pattern.rotate_list(row, self.column_offset * 2) - def add_layout_pins(self): - """ Add the layout pins """ - bitline_names = self.cell.get_all_bitline_names() - for col in range(self.column_size): - for port in self.all_ports: - bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port]) - text = "bl_{0}_{1}".format(port, col) - self.add_layout_pin(text=text, - layer=bl_pin.layer, - offset=bl_pin.ll().scale(1, 0), - width=bl_pin.width(), - height=self.height) - br_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port + 1]) - text = "br_{0}_{1}".format(port, col) - self.add_layout_pin(text=text, - layer=br_pin.layer, - offset=br_pin.ll().scale(1, 0), - width=br_pin.width(), - height=self.height) - # self.add_rect(layer=bl_pin.layer, - # offset=bl_pin.ll().scale(1, 0), - # width=bl_pin.width(), - # height=self.height) - # self.add_rect(layer=br_pin.layer, - # offset=br_pin.ll().scale(1, 0), - # width=br_pin.width(), - # height=self.height) - - wl_names = self.cell.get_all_wl_names() - for row in range(self.row_size): - for port in self.all_ports: - wl_pin = self.cell_inst[row, 0].get_pin(wl_names[port]) - self.add_layout_pin(text="wl_{0}_{1}".format(port, row), - layer=wl_pin.layer, - offset=wl_pin.ll().scale(0, 1), - width=self.width, - height=wl_pin.height()) - - # Copy a vdd/gnd layout pin from every cell - 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 add_supply_pins(self): - for row in range(self.row_size): - for col in range(self.column_size): - inst = self.cell_inst[row, col] - if 'VPB' or 'vpb' in self.cell_inst[row, col].mod.pins: - pin = inst.get_pin("vpb") - self.objs.append(geometry.rectangle(layer["nwell"], - pin.ll(), - pin.width(), - pin.height())) - self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) - - if 'VNB' or 'vnb' in self.cell_inst[row, col].mod.pins: - try: - from openram.tech import layer_override - if layer_override['VNB']: - pin = inst.get_pin("vnb") - self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) - self.objs.append(geometry.rectangle(layer["pwellp"], - pin.ll(), - pin.width(), - pin.height())) - except: - pin = inst.get_pin("vnb") - self.add_label("vdd", pin.layer, pin.center()) - - def input_load(self): - # FIXME: This appears to be old code from previous characterization. Needs to be updated. - wl_wire = self.gen_wl_wire() - return wl_wire.return_input_cap() + self.pattern = pattern(self, "bitcell_array", bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="dummy_bit_r{0}_c{1}") + self.pattern.connect_array() diff --git a/technology/sky130/custom/sky130_replica_bitcell_array.py b/technology/sky130/custom/sky130_replica_bitcell_array.py index 33a52492..f6d9b7aa 100644 --- a/technology/sky130/custom/sky130_replica_bitcell_array.py +++ b/technology/sky130/custom/sky130_replica_bitcell_array.py @@ -13,7 +13,7 @@ from openram.tech import drc from openram.tech import array_row_multiple from openram.tech import array_col_multiple from openram import OPTS -from .replica_bitcell_array import replica_bitcell_array +from openram.modules import replica_bitcell_array from .sky130_bitcell_base_array import sky130_bitcell_base_array @@ -27,389 +27,38 @@ class sky130_replica_bitcell_array(replica_bitcell_array, sky130_bitcell_base_ar bitcell (Bl/BR disconnected). """ def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""): - total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports - self.all_ports = list(range(total_ports)) - - self.column_size = cols - self.row_size = rows - - # This is how many RBLs are in all the arrays - if rbl: - self.rbl = rbl - else: - self.rbl=[1, 1 if len(self.all_ports)>1 else 0] - # This specifies which RBL to put on the left or right - # by port number - # This could be an empty list - if left_rbl != None: - self.left_rbl = left_rbl - else: - self.left_rbl = [0] - # This could be an empty list - if right_rbl != None: - self.right_rbl = right_rbl - else: - self.right_rbl=[1] if len(self.all_ports) > 1 else [] - self.rbls = self.left_rbl + self.right_rbl - - if ((self.column_size + self.rbl[0] + self.rbl[1]) % array_col_multiple != 0): - debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.column_size + self.rbl[0] + self.rbl[1], array_col_multiple), -1) - - if ((self.row_size + self.rbl[0] + self.rbl[1]) % array_row_multiple != 0): - debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.row_size + self.rbl[0] + self.rbl[1], array_row_multiple), -15) - - super().__init__(self.row_size, self.column_size, rbl, left_rbl, right_rbl, name) - - 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 is a bitcell x bitcell offset to scale - self.bitcell_offset = vector(self.cell.width, self.cell.height) - self.strap_offset = vector(self.replica_col_insts[0].mod.strap1.width, self.replica_col_insts[0].mod.strap1.height) - self.col_end_offset = vector(self.dummy_row_insts[0].mod.colend1.width, self.dummy_row_insts[0].mod.colend1.height) - self.row_end_offset = vector(self.dummy_col_insts[0].mod.rowend1.width, self.dummy_col_insts[0].mod.rowend1.height) - - # Everything is computed with the main array at (self.unused_pitch, 0) to start - self.bitcell_array_inst.place(offset=self.unused_offset) - - self.add_replica_columns() - - self.add_end_caps() - - # Array was at (0, 0) but move everything so it is at the lower left - self.offset_all_coordinates() - - # Add extra width on the left and right for the unused WLs - #self.width = self.dummy_col_insts[0].rx() + self.unused_offset[0] - self.width = self.dummy_col_insts[1].rx() - self.height = self.dummy_col_insts[0].uy() - - self.add_layout_pins() - - self.route_unused_wordlines() - - self.add_boundary() - - self.DRC_LVS() - - def add_pins(self): - super().add_pins() - - def add_replica_columns(self): - """ Add replica columns on left and right of array """ - - # Grow from left to right, toward the array - for bit, port in enumerate(self.left_rbl): - offset = self.bitcell_array_inst.ll() \ - - vector(0, self.col_cap_bottom.height) \ - - vector(0, self.dummy_row.height) \ - - vector(self.replica_columns[0].width, 0) - self.replica_col_insts[bit].place(offset + vector(0, self.replica_col_insts[bit].height), mirror="MX") - - # Grow to the right of the bitcell array, array outward - for bit, port in enumerate(self.right_rbl): - offset = self.bitcell_array_inst.lr() \ - + self.bitcell_offset.scale(bit, -self.rbl[0] - (self.col_end_offset.y / self.cell.height)) \ - + self.strap_offset.scale(bit, -self.rbl[0] - 1) - self.replica_col_insts[self.rbl[0] + bit].place(offset) - - # Replica dummy rows - # Add the dummy rows even if we aren't adding the replica column to this bitcell array - # These grow up, toward the array - for bit in range(self.rbl[0]): - dummy_offset = self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2) + self.unused_offset - self.dummy_row_replica_insts[bit].place(offset=dummy_offset, - mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0") - # These grow up, away from the array - for bit in range(self.rbl[1]): - dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul() - self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset, - mirror="MX" if bit % 2 else "R0") - - def add_end_caps(self): - """ Add dummy cells or end caps around the array """ - - dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1]) + self.bitcell_array_inst.ul() - self.dummy_row_insts[1].place(offset=dummy_row_offset) - - dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - (self.col_end_offset.y / self.cell.height)) + self.unused_offset - self.dummy_row_insts[0].place(offset=dummy_row_offset + vector(0, self.dummy_row_insts[0].height), mirror="MX") - - # Far left dummy col - # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array - dummy_col_offset = self.bitcell_offset.scale(len(self.right_rbl) * (1 + self.strap_offset.x / self.cell.width), -self.rbl[0] - (self.col_end_offset.y / self.cell.height)) - vector(self.replica_col_insts[0].width, 0) + self.unused_offset - self.dummy_col_insts[0].place(offset=dummy_col_offset, mirror="MY") - - # Far right dummy col - # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array - dummy_col_offset = self.bitcell_offset.scale(len(self.right_rbl) * (1 + self.strap_offset.x / self.cell.width), -self.rbl[0] - (self.col_end_offset.y / self.cell.height)) + self.bitcell_array_inst.lr() - self.dummy_col_insts[1].place(offset=dummy_col_offset) - - def route_unused_wordlines(self): - """ Connect the unused RBL and dummy wordlines to gnd """ - return - # This grounds all the dummy row word lines - for inst in self.dummy_row_insts: - for wl_name in self.col_cap.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 add_layout_pins(self): - """ Add the layout pins """ - - for row_end in self.dummy_col_insts: - row_end = row_end.mod - for (rba_wl_name, wl_name) in zip(self.get_all_wordline_names(), row_end.get_wordline_names()): - pin = row_end.get_pin(wl_name) - self.add_layout_pin(text=rba_wl_name, - layer=pin.layer, - offset=vector(0,pin.ll().scale(0, 1)[1]), - #width=self.width, - width=pin.width(), - height=pin.height()) - - pin_height = (round_to_grid(drc["minarea_m3"] / round_to_grid(sqrt(drc["minarea_m3"]))) + drc["{0}_to_{0}".format('m3')]) - drc_width = drc["{0}_to_{0}".format('m3')] - - # 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_row_insts + self.replica_col_insts - - for pin_name in self.supplies: - for supply_inst in supply_insts: - vdd_alternate = 0 - gnd_alternate = 0 - for cell_inst in supply_inst.mod.insts: - inst = cell_inst.mod - for pin in inst.get_pins(pin_name): - if pin.name == 'vdd': - if vdd_alternate: - connection_offset = 0.035 - vdd_alternate = 0 - else: - connection_offset = -0.035 - vdd_alternate = 1 - connection_width = drc["minwidth_{}".format('m1')] - track_offset = 1 - elif pin.name == 'gnd': - if gnd_alternate: - connection_offset = 0.035 - gnd_alternate = 0 - else: - connection_offset = -0.035 - gnd_alternate = 1 - connection_width = drc["minwidth_{}".format('m1')] - track_offset = 4 - pin_width = round_to_grid(sqrt(drc["minarea_m3"])) - pin_height = round_to_grid(drc["minarea_m3"] / pin_width) - if inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_cent' or 'corner' in inst.cell_name: - if 'dummy_row' in supply_inst.name and supply_inst.mirror == 'MX': - pin_center = vector(pin.center()[0], -1 * track_offset * (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width) - elif 'dummy_row' in supply_inst.name: - pin_center = vector(pin.center()[0],inst.height + 1 * track_offset* (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, self.height), connection_width) - elif 'replica_col' in supply_inst.name and cell_inst.mirror == 'MX': - pin_center = vector(pin.center()[0], -1 * track_offset* (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width) - elif 'replica_col' in supply_inst.name: - pin_center = vector(pin.center()[0],inst.height + 1 * track_offset * (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset,self.height), connection_width) - self.add_via_stack_center(from_layer=pin.layer, - to_layer='m2', - offset=pin_center+supply_inst.ll()+cell_inst.ll() + vector(connection_offset,0)) + + super().__init__(rows, cols, rbl, left_rbl, right_rbl, name) + # total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports + # self.all_ports = list(range(total_ports)) + # + # self.column_size = cols + # self.row_size = rows + # + # # This is how many RBLs are in all the arrays + # if rbl: + # self.rbl = rbl + # else: + # self.rbl=[1, 1 if len(self.all_ports)>1 else 0] + # # This specifies which RBL to put on the left or right + # # by port number + # # This could be an empty list + # if left_rbl != None: + # self.left_rbl = left_rbl + # else: + # self.left_rbl = [0] + # # This could be an empty list + # if right_rbl != None: + # self.right_rbl = right_rbl + # else: + # self.right_rbl=[1] if len(self.all_ports) > 1 else [] + # self.rbls = self.left_rbl + self.right_rbl + # + # if ((self.column_size + self.rbl[0] + self.rbl[1]) % array_col_multiple != 0): + # debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.column_size + self.rbl[0] + self.rbl[1], array_col_multiple), -1) + # + # if ((self.row_size + self.rbl[0] + self.rbl[1]) % array_row_multiple != 0): + # debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.row_size + self.rbl[0] + self.rbl[1], array_row_multiple), -15) + # - # add well contacts to perimeter cells - for pin_name in ['vpb', 'vnb']: - for supply_inst in supply_insts: - vnb_alternate = 0 - vpb_alternate = 0 - for cell_inst in supply_inst.mod.insts: - - inst = cell_inst.mod - for pin in inst.get_pins(pin_name): - if pin.name == 'vpb': - if vpb_alternate: - connection_offset = 0.01 - vpb_alternate = 0 - else: - connection_offset = 0.02 - vpb_alternate = 1 - connection_width = drc["minwidth_{}".format('m1')] - track_offset = 2 - elif pin.name == 'vnb': - if vnb_alternate: - connection_offset = -0.01 - vnb_alternate = 0 - else: - connection_offset = -0.02 - vnb_alternate = 1 - connection_width = drc["minwidth_{}".format('m1')] - track_offset = 3 - if inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_cent': - if 'dummy_row' in supply_inst.name and supply_inst.mirror == 'MX': - pin_center = vector(pin.center()[0], -1 * track_offset * (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width) - elif 'dummy_row' in supply_inst.name: - pin_center = vector(pin.center()[0],inst.height + 1 * track_offset* (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, self.height), connection_width) - elif 'replica_col' in supply_inst.name and cell_inst.mirror == 'MX': - pin_center = vector(pin.center()[0], -1 * track_offset* (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width) - elif 'replica_col' in supply_inst.name: - pin_center = vector(pin.center()[0],inst.height + 1 * track_offset * (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset,self.height), connection_width) - self.add_via_stack_center(from_layer=pin.layer, - to_layer='m2', - offset=pin_center+supply_inst.ll()+cell_inst.ll() + vector(connection_offset,0)) - - min_area = drc["minarea_{}".format('m3')] - for track,supply, offset in zip(range(1,5),['vdd','vdd','gnd','gnd'],[min_area * 6,min_area * 6, 0, 0]): - y_offset = track * (pin_height + drc_width*2) - self.add_segment_center('m2', vector(0,-y_offset), vector(self.width, -y_offset), drc["minwidth_{}".format('m2')]) - self.add_segment_center('m2', vector(0,self.height + y_offset), vector(self.width, self.height + y_offset), drc["minwidth_{}".format('m2')]) - self.add_power_pin(name=supply, - loc=vector(round_to_grid(sqrt(min_area))/2 + offset, -y_offset), - start_layer='m2') - self.add_power_pin(name=supply, - loc=vector(round_to_grid(sqrt(min_area))/2 + offset, self.height + y_offset), - start_layer='m2') - self.add_power_pin(name=supply, - loc=vector(self.width - round_to_grid(sqrt(min_area))/2 - offset, -y_offset), - start_layer='m2') - self.add_power_pin(name=supply, - loc=vector(self.width - round_to_grid(sqrt(min_area))/2 - offset, self.height + y_offset), - start_layer='m2') - - self.offset_all_coordinates() - self.height = self.height + self.dummy_col_insts[0].lr().y * 2 - - for pin_name in self.all_bitline_names: - pin_list = self.bitcell_array_inst.get_pins(pin_name) - for pin in pin_list: - if 'bl' in pin.name: - self.add_layout_pin(text=pin_name, - layer=pin.layer, - offset=pin.ll().scale(1, 0), - width=pin.width(), - height=self.height) - elif 'br' in pin_name: - self.add_layout_pin(text=pin_name, - layer=pin.layer, - offset=pin.ll().scale(1, 0) + vector(0,pin_height + drc_width*2), - width=pin.width(), - height=self.height - 2 *(pin_height + drc_width*2)) - # Replica bitlines - if len(self.rbls) > 0: - for (names, inst) in zip(self.rbl_bitline_names, self.replica_col_insts): - pin_names = self.replica_columns[self.rbls[0]].all_bitline_names - mirror = self.replica_col_insts[0].mirror - for (bl_name, pin_name) in zip(names, pin_names): - pin = inst.get_pin(pin_name) - if 'rbl_bl' in bl_name: - # if mirror != "MY": - # bl_name = bl_name.replace("rbl_bl","rbl_br") - self.add_layout_pin(text=bl_name, - layer=pin.layer, - offset=pin.ll().scale(1, 0), - width=pin.width(), - height=self.height) - elif 'rbl_br' in bl_name: - # if mirror != "MY": - # bl_name = bl_name.replace("rbl_br","rbl_bl") - self.add_layout_pin(text=bl_name, - layer=pin.layer, - offset=pin.ll().scale(1, 0) + vector(0,(pin_height + drc_width*2)), - width=pin.width(), - height=self.height - 2 *(pin_height + drc_width*2)) - return - - def add_wordline_pins(self): - - # Wordlines to ground - self.gnd_wordline_names = [] - - for port in self.all_ports: - for bit in self.all_ports: - self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit)) - if bit != port: - self.gnd_wordline_names.append("rbl_wl_{0}_{1}".format(port, bit)) - - self.all_rbl_wordline_names = [x for sl in self.rbl_wordline_names for x in sl] - - self.wordline_names = self.bitcell_array.wordline_names - self.all_wordline_names = self.bitcell_array.all_wordline_names - - # All wordlines including dummy and RBL - self.replica_array_wordline_names = [] - #self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names())) - for bit in range(self.rbl[0]): - self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[bit]]) - self.replica_array_wordline_names.extend(self.all_wordline_names) - for bit in range(self.rbl[1]): - self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[self.rbl[0] + bit]]) - #self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names())) - - for port in range(self.rbl[0]): - self.add_pin(self.rbl_wordline_names[port][port], "INPUT") - self.add_pin_list(self.all_wordline_names, "INPUT") - for port in range(self.rbl[0], self.rbl[0] + self.rbl[1]): - self.add_pin(self.rbl_wordline_names[port][port], "INPUT") - - def create_instances(self): - """ Create the module instances used in this design """ - self.supplies = ["vdd", "gnd"] - - # Used for names/dimensions only - # self.cell = factory.create(module_type=OPTS.bitcell) - - # Main array - self.bitcell_array_inst=self.add_inst(name="bitcell_array", - mod=self.bitcell_array) - self.connect_inst(self.all_bitline_names + self.all_wordline_names + self.supplies) - # Replica columns - self.replica_col_insts = [] - for port in self.all_ports: - if port in self.rbls: - self.replica_col_insts.append(self.add_inst(name="replica_col_{}".format(port), - mod=self.replica_columns[port])) - self.connect_inst(self.rbl_bitline_names[port] + self.replica_array_wordline_names + self.supplies + ["gnd"] + ["gnd"]) - else: - self.replica_col_insts.append(None) - - # Dummy rows under the bitcell array (connected with with the replica cell wl) - self.dummy_row_replica_insts = [] - # Note, this is the number of left and right even if we aren't adding the columns to this bitcell array! - for port in self.all_ports: - self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port), - mod=self.dummy_row)) - self.connect_inst(self.all_bitline_names + [x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[port]] + self.supplies) - - # Top/bottom dummy rows or col caps - self.dummy_row_insts = [] - self.dummy_row_insts.append(self.add_inst(name="dummy_row_bot", - mod=self.col_cap_bottom)) - self.connect_inst(self.all_bitline_names + self.supplies + ["gnd"]) - self.dummy_row_insts.append(self.add_inst(name="dummy_row_top", - mod=self.col_cap_top)) - self.connect_inst(self.all_bitline_names + self.supplies + ["gnd"]) - - # Left/right Dummy columns - self.dummy_col_insts = [] - self.dummy_col_insts.append(self.add_inst(name="dummy_col_left", - mod=self.row_cap_left)) - self.connect_inst(["dummy_left_" + bl for bl in self.row_cap_left.all_bitline_names] + ["gnd"] + self.replica_array_wordline_names + ["gnd"] + self.supplies) - self.dummy_col_insts.append(self.add_inst(name="dummy_col_right", - mod=self.row_cap_right)) - self.connect_inst(["dummy_right_" + bl for bl in self.row_cap_right.all_bitline_names] + ["gnd"] + self.replica_array_wordline_names + ["gnd"] + self.supplies) diff --git a/technology/sky130/custom/sky130_replica_column.py b/technology/sky130/custom/sky130_replica_column.py index 64486e14..6a749b2d 100644 --- a/technology/sky130/custom/sky130_replica_column.py +++ b/technology/sky130/custom/sky130_replica_column.py @@ -12,8 +12,8 @@ from openram.tech import layer from openram import OPTS from .sky130_bitcell_base_array import sky130_bitcell_base_array from openram.modules import pattern - -class sky130_replica_column(sky130_bitcell_base_array): +from openram.modules import replica_column +class sky130_replica_column(replica_column, sky130_bitcell_base_array): """ Generate a replica bitline column for the replica array. Rows is the total number of rows i the main array. @@ -23,64 +23,7 @@ class sky130_replica_column(sky130_bitcell_base_array): """ def __init__(self, name, rows, rbl, replica_bit, column_offset=0): - # Used for pin names and properties - self.cell = factory.create(module_type=OPTS.bitcell) - # Row size is the number of rows with word lines - self.row_size = sum(rbl) + rows - # Start of regular word line rows - self.row_start = rbl[0] - # End of regular word line rows - self.row_end = self.row_start + rows - super().__init__(rows=self.row_size, cols=1, column_offset=column_offset, name=name) - - self.rows = rows - self.left_rbl = rbl[0] - self.right_rbl = rbl[1] - self.replica_bit = replica_bit - # left, right, regular rows plus top/bottom dummy cells - - self.total_size = self.left_rbl + rows + self.right_rbl - self.column_offset = column_offset - - # if self.rows % 2 == 0: - # debug.error("Invalid number of rows {}. Number of rows must be even to connect to col ends".format(self.rows), -1) - # if self.column_offset % 2 == 0: - # debug.error("Invalid column_offset {}. Column offset must be odd to connect to col ends".format(self.rows), -1) - # debug.check(replica_bit != 0 and replica_bit != rows, - # "Replica bit cannot be the dummy row.") - # debug.check(replica_bit <= self.left_rbl or replica_bit >= self.total_size - self.right_rbl - 1, - # "Replica bit cannot be in the regular array.") - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - def create_netlist(self): - self.add_modules() - self.add_pins() - self.create_instances() - - def create_layout(self): - self.place_array() - - self.add_layout_pins() - self.route_supplies() - self.add_boundary() - self.DRC_LVS() - - def add_pins(self): - - self.create_all_bitline_names() - #self.create_all_wordline_names(self.row_size+2) - # +2 to add fake wl pins for colends - self.create_all_wordline_names(self.row_size) - self.add_pin_list(self.all_bitline_names, "OUTPUT") - self.add_pin_list(self.all_wordline_names, "INPUT") - - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") - - #self.add_pin("top_gate", "INPUT") - #self.add_pin("bot_gate", "INPUT") + super().__init__(name=name, rows=rows, rbl=rbl, replica_bit=replica_bit, column_offset=column_offset) def add_modules(self): self.replica_cell = factory.create(module_type="replica_bitcell_1port", version="opt1") @@ -88,51 +31,65 @@ class sky130_replica_column(sky130_bitcell_base_array): self.replica_cell2 = factory.create(module_type="replica_bitcell_1port", version="opt1a") self.dummy_cell = factory.create(module_type="dummy_bitcell_1port", version="opt1") - self.dummy_cell2 = factory.create(module_type="dummy_bitcell_1port", version="opt1") - - self.strap = factory.create(module_type="internal", version="wlstrap_p") - self.strap2 = factory.create(module_type="internal", version="wlstrapa_p") + self.dummy_cell2 = factory.create(module_type="dummy_bitcell_1port", version="opt1a") + self.strap = factory.create(module_type="internal", version="wlstrap") + self.strap_p = factory.create(module_type="internal", version="wlstrap_p") + self.strapa = factory.create(module_type="internal", version="wlstrapa") + self.strapa_p = factory.create(module_type="internal", version="wlstrapa_p") def create_instances(self): """ Create the module instances used in this design """ self.all_inst={} self.cell_inst={} - replica_row_opt1 = [geometry.instance("00_rep_opt1", mod=self.replica_cell, is_bitcell=True, mirror='XY')] \ - + [geometry.instance("01_strap1", mod=self.strap, is_bitcell=False, mirror='MX')] - - replica_row_opt1a = [geometry.instance("10_opt1a", mod=self.replica_cell2, is_bitcell=True, mirror='MY')] \ - + [geometry.instance("11_strapa", mod=self.strap2, is_bitcell=False)] - replica_dummy_row_opt1 = [geometry.instance("00_rep_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='MX')] \ - + [geometry.instance("01_rep_strap", mod=self.strap, is_bitcell=False, mirror='MX')] + replica_row_opt1 = [geometry.instance("rep_00_opt1", mod=self.replica_cell, is_bitcell=True, mirror='XY')] \ + + [geometry.instance("rep_01_strap", mod=self.strap_p, is_bitcell=False, mirror='MX')]\ + + [geometry.instance("rep_02_opt1", mod=self.replica_cell, is_bitcell=True, mirror='MX')] \ + + [geometry.instance("rep_03_strap_p", mod=self.strap, is_bitcell=False, mirror='MX')] - replica_dummy_row_opt1a = [geometry.instance("10_opt1a", mod=self.dummy_cell2, is_bitcell=True, mirror='MY')] \ - + [geometry.instance("11_strapa", mod=self.strap2, is_bitcell=False)] + replica_row_opt1a = [geometry.instance("rep_10_opt1a", mod=self.replica_cell2, is_bitcell=True, mirror='MY')] \ + + [geometry.instance("rep_11_strapa", mod=self.strap_p, is_bitcell=False)] \ + + [geometry.instance("rep_12_opt1a", mod=self.replica_cell2, is_bitcell=True)] \ + + [geometry.instance("rep_13_strapa_p", mod=self.strapa, is_bitcell=False)] + + dummy_row_opt1 = [geometry.instance("dummy_00_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='XY')] \ + + [geometry.instance("dummy_01_strap", mod=self.strap_p, is_bitcell=False, mirror='MX')]\ + + [geometry.instance("dummy_02_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='MX')] \ + + [geometry.instance("dummy_03_strap_p", mod=self.strap, is_bitcell=False, mirror='MX')] + + dummy_row_opt1a = [geometry.instance("dummy_10_opt1a", mod=self.dummy_cell2, is_bitcell=True, mirror='MY')] \ + + [geometry.instance("dummy_11_strapa", mod=self.strap_p, is_bitcell=False)] \ + + [geometry.instance("dummy_12_opt1a", mod=self.dummy_cell2, is_bitcell=True)] \ + + [geometry.instance("dummy_13_strapa_p", mod=self.strapa, is_bitcell=False)] bit_block = [] + if self.column_offset % 2: + replica_row_opt1 = replica_row_opt1[0:2] + replica_row_opt1a = replica_row_opt1a[0:2] + dummy_row_opt1 = dummy_row_opt1[0:2] + dummy_row_opt1a = dummy_row_opt1a[0:2] + else: + replica_row_opt1 = replica_row_opt1[2:4] + replica_row_opt1a = replica_row_opt1a[2:4] + dummy_row_opt1 = dummy_row_opt1[2:4] + dummy_row_opt1a = dummy_row_opt1a[2:4] + current_row = self.row_start for row in range(self.total_size): # Regular array cells are replica cells # Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell. # All other cells are dummies if (row == self.replica_bit) or (row >= self.row_start and row < self.row_end): - if current_row % 2: + if current_row % 2 == 0: pattern.append_row_to_block(bit_block, replica_row_opt1) else: pattern.append_row_to_block(bit_block, replica_row_opt1a) else: - if current_row %2: - pattern.append_row_to_block(bit_block, replica_dummy_row_opt1) + if current_row % 2 == 0: + pattern.append_row_to_block(bit_block, dummy_row_opt1) else: - pattern.append_row_to_block(bit_block, replica_dummy_row_opt1a) + pattern.append_row_to_block(bit_block, dummy_row_opt1a) current_row += 1 - self.pattern = pattern(self, "replica_column", bit_block, num_rows=self.total_size, num_cols=len(replica_row_opt1a), name_template="rbc_r{0}_c{1}", ) + self.pattern = pattern(self, "replica_column", bit_block, num_rows=self.total_size, num_cols=len(replica_row_opt1), name_template="rbc_r{0}_c{1}") self.pattern.connect_array_raw() - def exclude_all_but_replica(self): - """ - Excludes all bits except the replica cell (self.replica_bit). - """ - for row, cell in self.cell_inst.items(): - if row != self.replica_bit: - self.graph_inst_exclude.add(cell) From 72a7b0342bc5c30d4aeb0b450527d00f8ae8bb67 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Fri, 25 Aug 2023 16:39:32 -0700 Subject: [PATCH 36/48] work on capped rba --- technology/sky130/custom/sky130_bitcell.py | 2 +- .../sky130/custom/sky130_bitcell_array.py | 3 +- .../sky130_capped_replica_bitcell_array.py | 202 +-------------- .../sky130/custom/sky130_col_cap_array.py | 231 +----------------- .../custom/sky130_replica_bitcell_array.py | 35 +-- 5 files changed, 14 insertions(+), 459 deletions(-) diff --git a/technology/sky130/custom/sky130_bitcell.py b/technology/sky130/custom/sky130_bitcell.py index 21fc7264..f99c37c8 100644 --- a/technology/sky130/custom/sky130_bitcell.py +++ b/technology/sky130/custom/sky130_bitcell.py @@ -6,7 +6,7 @@ # from openram import debug -from openram.modules import bitcell_base +from openram.modules.bitcell_base import bitcell_base from openram.tech import cell_properties as props diff --git a/technology/sky130/custom/sky130_bitcell_array.py b/technology/sky130/custom/sky130_bitcell_array.py index 90c169c7..09287d68 100644 --- a/technology/sky130/custom/sky130_bitcell_array.py +++ b/technology/sky130/custom/sky130_bitcell_array.py @@ -6,7 +6,8 @@ # from openram import debug -from openram.modules import bitcell_array, pattern +from openram.modules.bitcell_array import bitcell_array +from openram.modules import pattern from openram.sram_factory import factory from openram.base import geometry from openram import OPTS diff --git a/technology/sky130/custom/sky130_capped_replica_bitcell_array.py b/technology/sky130/custom/sky130_capped_replica_bitcell_array.py index ab79fce3..ca3a9648 100644 --- a/technology/sky130/custom/sky130_capped_replica_bitcell_array.py +++ b/technology/sky130/custom/sky130_capped_replica_bitcell_array.py @@ -11,209 +11,15 @@ from openram.sram_factory import factory from openram.tech import drc, spice from openram.tech import cell_properties as props from openram import OPTS +from openram.modules.capped_replica_bitcell_array import capped_replica_bitcell_array from .sky130_bitcell_base_array import sky130_bitcell_base_array -class sky130_capped_replica_bitcell_array(sky130_bitcell_base_array): +class sky130_capped_replica_bitcell_array(capped_replica_bitcell_array, sky130_bitcell_base_array): """ Creates a replica bitcell array then adds the row and column caps to all sides of a bitcell array. """ def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""): - super().__init__(name, rows, cols, column_offset=0) - debug.info(1, "Creating {0} {1} x {2} rbls: {3} left_rbl: {4} right_rbl: {5}".format(self.name, - rows, - cols, - rbl, - left_rbl, - right_rbl)) - self.add_comment("rows: {0} cols: {1}".format(rows, cols)) - self.add_comment("rbl: {0} left_rbl: {1} right_rbl: {2}".format(rbl, left_rbl, right_rbl)) - - # This is how many RBLs are in all the arrays - self.rbl = rbl - # This specifies which RBL to put on the left or right by port number - # This could be an empty list - if left_rbl is not None: - self.left_rbl = left_rbl - else: - self.left_rbl = [] - # This could be an empty list - if right_rbl is not None: - self.right_rbl = right_rbl - else: - self.right_rbl = [] - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - def create_netlist(self): - """ Create and connect the netlist """ - self.add_modules() - self.add_pins() - self.create_instances() - - def add_modules(self): - self.replica_bitcell_array = factory.create(module_type="replica_bitcell_array", - cols=self.column_size, - rows=self.row_size, - rbl=self.rbl, - left_rbl=self.left_rbl, - right_rbl=self.right_rbl) - - def add_pins(self): - - # Arrays are always: - # bitlines (column first then port order) - # word lines (row first then port order) - # dummy wordlines - # replica wordlines - # regular wordlines (bottom to top) - # # dummy bitlines - # replica bitlines (port order) - # regular bitlines (left to right port order) - # - # vdd - # gnd - - self.add_bitline_pins() - self.add_wordline_pins() - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") - - def add_bitline_pins(self): - self.bitline_names = self.replica_bitcell_array.bitline_names - self.all_bitline_names = self.replica_bitcell_array.all_bitline_names - self.rbl_bitline_names = self.replica_bitcell_array.rbl_bitline_names - self.all_rbl_bitline_names = self.replica_bitcell_array.all_rbl_bitline_names - - self.bitline_pins = [] - - for port in self.left_rbl: - self.bitline_pins.extend(self.rbl_bitline_names[port]) - self.bitline_pins.extend(self.all_bitline_names) - for port in self.right_rbl: - self.bitline_pins.extend(self.rbl_bitline_names[port]) - - self.add_pin_list(self.bitline_pins, "INOUT") - - def add_wordline_pins(self): - # some of these are just included for compatibility with modules instantiating this module - self.rbl_wordline_names = self.replica_bitcell_array.rbl_wordline_names - self.all_rbl_wordline_names = self.replica_bitcell_array.all_rbl_wordline_names - self.wordline_names = self.replica_bitcell_array.wordline_names - self.all_wordline_names = self.replica_bitcell_array.all_wordline_names - - self.wordline_pins = [] - - for port in range(self.rbl[0]): - self.wordline_pins.append(self.rbl_wordline_names[port][port]) - self.wordline_pins.extend(self.all_wordline_names) - for port in range(self.rbl[0], self.rbl[0] + self.rbl[1]): - self.wordline_pins.append(self.rbl_wordline_names[port][port]) - - self.add_pin_list(self.wordline_pins, "INPUT") - - def create_instances(self): - """ Create the module instances used in this design """ - self.supplies = ["vdd", "gnd"] - - # Main array - self.replica_bitcell_array_inst=self.add_inst(name="replica_bitcell_array", - mod=self.replica_bitcell_array) - self.connect_inst(self.bitline_pins + self.wordline_pins + self.supplies) - - def create_layout(self): - - self.replica_bitcell_array_inst.place(offset=0) - - self.width = self.replica_bitcell_array.width - self.height = self.replica_bitcell_array.height - - for pin_name in self.bitline_pins + self.wordline_pins + self.supplies: - self.copy_layout_pin(self.replica_bitcell_array_inst, pin_name) - - self.add_boundary() - - self.DRC_LVS() - - def get_main_array_top(self): - return self.replica_bitcell_array.get_main_array_top() - - def get_main_array_bottom(self): - return self.replica_bitcell_array.get_main_array_bottom() - - def get_main_array_left(self): - return self.replica_bitcell_array.get_main_array_left() - - def get_main_array_right(self): - return self.replica_bitcell_array.get_main_array_right() - - def get_replica_top(self): - return self.replica_bitcell_array.get_replica_top() - - def get_replica_bottom(self): - return self.replica_bitcell_array.get_replica_bottom() - - def get_replica_left(self): - return self.replica_bitcell_array.get_replica_left() - - def get_replica_right(self): - return self.replica_bitcell_array.get_replica_right() - - - def get_column_offsets(self): - return self.replica_bitcell_array.get_column_offsets() - - def analytical_power(self, corner, load): - """Power of Bitcell array and bitline in nW.""" - # Dynamic Power from Bitline - bl_wire = self.gen_bl_wire() - cell_load = 2 * bl_wire.return_input_cap() - bl_swing = OPTS.rbl_delay_percentage - freq = spice["default_event_frequency"] - bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) - - # Calculate the bitcell power which currently only includes leakage - cell_power = self.cell.analytical_power(corner, load) - - # Leakage power grows with entire array and bitlines. - total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size, - cell_power.leakage * self.column_size * self.row_size) - return total_power - - - def gen_bl_wire(self): - if OPTS.netlist_only: - height = 0 - else: - height = self.height - bl_pos = 0 - bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1")) - bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell - return bl_wire - - def graph_exclude_bits(self, targ_row=None, targ_col=None): - """ - Excludes bits in column from being added to graph except target - """ - self.replica_bitcell_array.graph_exclude_bits(targ_row, targ_col) - - def graph_exclude_replica_col_bits(self): - """ - Exclude all replica/dummy cells in the replica columns except the replica bit. - """ - self.replica_bitcell_array.graph_exclude_replica_col_bits() - - def get_cell_name(self, inst_name, row, col): - """ - Gets the spice name of the target bitcell. - """ - return self.replica_bitcell_array.get_cell_name(inst_name + "{}x".format(OPTS.hier_seperator) + self.replica_bitcell_array_inst.name, row, col) - - def clear_exclude_bits(self): - """ - Clears the bit exclusions - """ - self.replica_bitcell_array.clear_exclude_bits() + super().__init__(rows, cols, rbl, left_rbl, right_rbl, name) + diff --git a/technology/sky130/custom/sky130_col_cap_array.py b/technology/sky130/custom/sky130_col_cap_array.py index 445c918c..03ea574c 100644 --- a/technology/sky130/custom/sky130_col_cap_array.py +++ b/technology/sky130/custom/sky130_col_cap_array.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 + #!/usr/bin/env python3 # See LICENSE for licensing information. # # Copyright (c) 2016-2023 Regents of the University of California @@ -9,42 +9,15 @@ from openram.base import geometry from openram.sram_factory import factory from openram.tech import layer from openram import OPTS +from openram.modules.col_cap_array import col_cap_array from .sky130_bitcell_base_array import sky130_bitcell_base_array -class sky130_col_cap_array(sky130_bitcell_base_array): +class sky130_col_cap_array(col_cap_array, sky130_bitcell_base_array): """ Generate a dummy row/column for the replica array. """ - def __init__(self, rows, cols, location, column_offset=0, mirror=0, name=""): - # Don't call the regular col-cap_array constructor since we don't want its constructor, just - # some of it's useful member functions - sky130_bitcell_base_array.__init__(self, rows=rows, cols=cols, column_offset=column_offset, name=name) - self.mirror = mirror - self.location = location - self.rows = rows - self.cols = cols - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - def create_netlist(self): - """ Create and connect the netlist """ - # This module has no wordlines - # self.create_all_wordline_names() - # This module has no bitlines - # self.create_all_bitline_names() - self.add_modules() - self.create_all_wordline_names() - self.add_pins() - self.create_instances() - - def create_layout(self): - - self.place_array("dummy_r{0}_c{1}", self.mirror) - self.add_layout_pins() - self.add_supply_pins() - self.add_boundary() - self.DRC_LVS() + def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name=""): + super().__init__(rows, cols, column_offset=0, mirror=0, location="", name="") def add_modules(self): """ Add the modules used in this design """ @@ -60,196 +33,4 @@ class sky130_col_cap_array(sky130_bitcell_base_array): self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") def create_instances(self): - """ Create the module instances used in this design """ - self.cell_inst = {} - self.array_layout = [] - bitline = 0 - for col in range((self.column_size * 2) - 1): - row_layout = [] - name="rca_{0}_{1}".format(self.location, col) - # Top/bottom cell are always dummy cells. - # Regular array cells are replica cells (>left_rbl and 1 else 0] - # # This specifies which RBL to put on the left or right - # # by port number - # # This could be an empty list - # if left_rbl != None: - # self.left_rbl = left_rbl - # else: - # self.left_rbl = [0] - # # This could be an empty list - # if right_rbl != None: - # self.right_rbl = right_rbl - # else: - # self.right_rbl=[1] if len(self.all_ports) > 1 else [] - # self.rbls = self.left_rbl + self.right_rbl - # - # if ((self.column_size + self.rbl[0] + self.rbl[1]) % array_col_multiple != 0): - # debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.column_size + self.rbl[0] + self.rbl[1], array_col_multiple), -1) - # - # if ((self.row_size + self.rbl[0] + self.rbl[1]) % array_row_multiple != 0): - # debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.row_size + self.rbl[0] + self.rbl[1], array_row_multiple), -15) - # - From ba51149dcef483aff06b5d014a9bb3c286a1929e Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Sat, 26 Aug 2023 18:54:07 -0700 Subject: [PATCH 37/48] placement working for sp capped rba, need fix rowcap patterns --- Makefile | 2 +- .../modules/capped_replica_bitcell_array.py | 76 +++----- compiler/modules/replica_bitcell_array.py | 20 +- compiler/modules/row_cap_array.py | 30 +-- .../sky130_capped_replica_bitcell_array.py | 2 +- .../sky130/custom/sky130_col_cap_array.py | 55 +++++- technology/sky130/custom/sky130_corner.py | 18 +- .../sky130/custom/sky130_row_cap_array.py | 182 +++++++----------- technology/sky130/tech/tech.py | 5 +- 9 files changed, 177 insertions(+), 213 deletions(-) diff --git a/Makefile b/Makefile index 18768dd4..dc07a5b1 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ SRAM_LIB_GIT_REPO ?= https://github.com/vlsida/sky130_fd_bd_sram.git # Use this for development #SRAM_LIB_GIT_REPO ?= git@github.com:VLSIDA/sky130_fd_bd_sram.git #SRAM_LIB_GIT_REPO ?= https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git -SRAM_LIB_GIT_COMMIT ?= 060f3638be6269dd9aa82cfbbdfd9525943c1582 +SRAM_LIB_GIT_COMMIT ?= 9fcf3a78398037583b6d6c1ebac71957343c4bd8 # Open PDKs OPEN_PDKS_DIR ?= $(PDK_ROOT)/open_pdks diff --git a/compiler/modules/capped_replica_bitcell_array.py b/compiler/modules/capped_replica_bitcell_array.py index 8e1395d1..a2976235 100644 --- a/compiler/modules/capped_replica_bitcell_array.py +++ b/compiler/modules/capped_replica_bitcell_array.py @@ -51,10 +51,7 @@ class capped_replica_bitcell_array(bitcell_base_array): self.rbls = self.left_rbl + self.right_rbl # Two dummy rows plus replica even if we don't add the column - self.extra_rows = sum(self.rbl) - # If we aren't using row/col caps, then we need to use the bitcell - if not self.cell.end_caps: - self.extra_rows += 2 + self.extra_rows = sum(self.rbl) + 2 self.create_netlist() if not OPTS.netlist_only: @@ -85,7 +82,7 @@ class capped_replica_bitcell_array(bitcell_base_array): cols=self.column_size + len(self.rbls), rows=1, # dummy column + left replica column(s) - column_offset=1, + column_offset=0, mirror=0, location="top") @@ -93,7 +90,7 @@ class capped_replica_bitcell_array(bitcell_base_array): cols=self.column_size + len(self.rbls), rows=1, # dummy column + left replica column(s) - column_offset=1, + column_offset=0, mirror=0, location="bottom") @@ -104,7 +101,7 @@ class capped_replica_bitcell_array(bitcell_base_array): cols=1, column_offset=0, rows=self.row_size + self.extra_rows, - mirror=(self.rbl[0] + 1) % 2) + location="left") self.row_cap_right = factory.create(module_type=row_cap_module_type, cols=1, @@ -112,9 +109,9 @@ class capped_replica_bitcell_array(bitcell_base_array): # + left replica column(s) # + bitcell columns # + right replica column(s) - column_offset=1 + len(self.left_rbl) + self.column_size + self.rbl[0], + column_offset=len(self.left_rbl) + self.column_size + self.rbl[0], rows=self.row_size + self.extra_rows, - mirror=(self.rbl[0] + 1) % 2) + location="right") def add_pins(self): @@ -201,36 +198,26 @@ class capped_replica_bitcell_array(bitcell_base_array): # row-based or column based power and ground lines. self.vertical_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[0])) self.horizontal_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[2])) - # FIXME: custom sky130 replica module has a better version of this offset - 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) - 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 replica array - self.replica_bitcell_array_inst.place(offset=self.unused_offset) + self.replica_bitcell_array_inst.place(offset=0) self.add_end_caps() - - # shift everything up and right to account for cap cells - self.translate_all(self.bitcell_offset.scale(-1, -1)) - - self.width = self.dummy_col_insts[1].rx() + self.unused_offset.x - self.height = self.dummy_row_insts[1].uy() - + + self.width = max([x.rx() for x in self.insts]) - min([x.lx() for x in self.insts]) + self.height = max([x.uy() for x in self.insts]) - min([y.by() for y in self.insts]) + self.add_layout_pins() self.route_supplies() 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.width = max([x.rx() for x in self.insts]) - min([x.lx() for x in self.insts]) + self.height = max([x.uy() for x in self.insts]) - min([y.by() for y in self.insts]) + ll=vector(min([x.lx() for x in self.insts]),min([y.by() for y in self.insts])) + + self.translate_all(ll) self.add_boundary() @@ -248,7 +235,7 @@ class capped_replica_bitcell_array(bitcell_base_array): def get_main_array_right(self): return self.replica_bitcell_array_inst.lx() + self.replica_bitcell_array.get_main_array_right() - # FIXME: these names need to be changed to reflect what they're actually returning + #FIXME: these names need to be changed to reflect what they're actually returning def get_replica_top(self): return self.dummy_row_insts[1].by() @@ -273,26 +260,23 @@ class capped_replica_bitcell_array(bitcell_base_array): def add_end_caps(self): """ Add dummy cells or end caps around the array """ - # Far top dummy row (first row above array is NOT flipped if even number of rows) - flip_dummy = (self.row_size + self.rbl[1]) % 2 - dummy_row_offset = self.bitcell_offset.scale(0, flip_dummy) + self.replica_bitcell_array_inst.ul() - self.dummy_row_insts[1].place(offset=dummy_row_offset, - mirror="MX" if flip_dummy else "R0") + # Far top dummy row + offset = self.replica_bitcell_array_inst.ul() + self.dummy_row_insts[1].place(offset=offset) - # Far bottom dummy row (first row below array IS flipped) - flip_dummy = (self.rbl[0] + 1) % 2 - dummy_row_offset = self.bitcell_offset.scale(0, flip_dummy - 1) + self.unused_offset - self.dummy_row_insts[0].place(offset=dummy_row_offset, - mirror="MX" if flip_dummy else "R0") + # Far bottom dummy row + dummy_row_height = vector(0, self.dummy_row_insts[0].height) + offset = self.replica_bitcell_array_inst.ll() - dummy_row_height + self.dummy_row_insts[0].place(offset=offset) + # Far left dummy col - # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array - dummy_col_offset = self.bitcell_offset.scale(-1, -1) + self.unused_offset - self.dummy_col_insts[0].place(offset=dummy_col_offset) + dummy_col_width = vector(self.dummy_col_insts[0].width, 0) + offset = self.dummy_row_insts[0].ll() - dummy_col_width + self.dummy_col_insts[0].place(offset=offset) # Far right dummy col - # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array - dummy_col_offset = self.bitcell_offset.scale(0, -1) + self.replica_bitcell_array_inst.lr() - self.dummy_col_insts[1].place(offset=dummy_col_offset) + offset = self.dummy_row_insts[0].lr() + self.dummy_col_insts[1].place(offset=offset) def add_layout_pins(self): for pin_name in self.used_wordline_names + self.bitline_pin_list: diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 805b39f0..cbfdfbb1 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -232,32 +232,30 @@ class replica_bitcell_array(bitcell_base_array): self.vertical_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[0])) self.horizontal_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[2])) - # 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 - self.bitcell_array_inst.place(offset=0) + self.bitcell_array_inst.place(offset=(0,0)) self.add_replica_columns() # Array was at (0, 0) but move everything so it is at the lower left # We move DOWN the number of left RBL even if we didn't add the column to this bitcell array # Note that this doesn't include the row/col cap - array_offset = self.bitcell_offset.scale(-len(self.left_rbl), -self.rbl[0]) - self.translate_all(array_offset) + + #rbc_width = (self.replica_col_insts[0].width, 0) + #dummy_height = max(x for x in map(lambda x: x if x != None else 0, self.)) + #array_offset = self.bitcell_offset.scale(-len(self.left_rbl), -self.rbl[0]) + ll=vector(min([x.lx() for x in self.insts]),min([y.by() for y in self.insts])) + + self.translate_all(ll) self.add_layout_pins() self.route_supplies() - ll=vector(min([x.lx() for x in self.insts]),min([y.by() for y in self.insts])) - self.width = max([x.rx() for x in self.insts]) - min([x.lx() for x in self.insts]) self.height = max([x.uy() for x in self.insts]) - min([y.by() for y in self.insts]) - self.add_boundary(ll) + self.add_boundary() self.DRC_LVS() diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index 34760177..3c91932b 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -12,9 +12,10 @@ class row_cap_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. """ - def __init__(self, rows, cols, column_offset=0, mirror=0, name=""): + def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name=""): super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.mirror = mirror + self.location = location self.no_instances = True self.create_netlist() if not OPTS.netlist_only: @@ -69,35 +70,12 @@ class row_cap_array(bitcell_base_array): return bitcell_pins - def place_array(self, name_template, row_offset=0): - xoffset = 0.0 - for col in range(self.column_size): - yoffset = self.cell.height - tempx, dir_y = self._adjust_x_offset(xoffset, col, self.column_offset) - - for row in range(self.row_size): - tempy, dir_x = self._adjust_y_offset(yoffset, row + 1, row_offset) - - if dir_x and dir_y: - dir_key = "XY" - elif dir_x: - dir_key = "MX" - elif dir_y: - dir_key = "MY" - else: - dir_key = "" - - self.cell_inst[row, col].place(offset=[tempx, tempy], - mirror=dir_key) - yoffset += self.cell.height - xoffset += self.cell.width - def add_layout_pins(self): """ Add the layout pins """ row_list = self.cell.get_all_wl_names() - for row in range(1, self.row_size - 1): + for row in range(0, self.row_size - 2): for cell_row in row_list: wl_pin = self.cell_inst[row, 0].get_pin(cell_row) self.add_layout_pin(text=cell_row + "_{0}".format(row), @@ -106,7 +84,7 @@ class row_cap_array(bitcell_base_array): width=self.width, height=wl_pin.height()) - for row in range(1, self.row_size - 1): + for row in range(0, self.row_size - 2): for col in range(self.column_size): inst = self.cell_inst[row, col] for pin_name in ["vdd", "gnd"]: diff --git a/technology/sky130/custom/sky130_capped_replica_bitcell_array.py b/technology/sky130/custom/sky130_capped_replica_bitcell_array.py index ca3a9648..49868bbe 100644 --- a/technology/sky130/custom/sky130_capped_replica_bitcell_array.py +++ b/technology/sky130/custom/sky130_capped_replica_bitcell_array.py @@ -22,4 +22,4 @@ class sky130_capped_replica_bitcell_array(capped_replica_bitcell_array, sky130_b """ def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""): super().__init__(rows, cols, rbl, left_rbl, right_rbl, name) - + diff --git a/technology/sky130/custom/sky130_col_cap_array.py b/technology/sky130/custom/sky130_col_cap_array.py index 03ea574c..eb44bc81 100644 --- a/technology/sky130/custom/sky130_col_cap_array.py +++ b/technology/sky130/custom/sky130_col_cap_array.py @@ -11,13 +11,15 @@ from openram.tech import layer from openram import OPTS from openram.modules.col_cap_array import col_cap_array from .sky130_bitcell_base_array import sky130_bitcell_base_array +from openram.modules import pattern +from math import ceil class sky130_col_cap_array(col_cap_array, sky130_bitcell_base_array): """ Generate a dummy row/column for the replica array. """ def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name=""): - super().__init__(rows, cols, column_offset=0, mirror=0, location="", name="") + super().__init__(rows, cols, column_offset=column_offset, mirror=mirror, location=location, name=name) def add_modules(self): """ Add the modules used in this design """ @@ -33,4 +35,53 @@ class sky130_col_cap_array(col_cap_array, sky130_bitcell_base_array): self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") def create_instances(self): - \ No newline at end of file + self.all_inst={} + self.cell_inst={} + + bit_row = [geometry.instance("00_colend", mod=self.colend1, is_bitcell=True)] \ + + [geometry.instance("01_strap_p_cent", mod=self.colend2, is_bitcell=False)]\ + + [geometry.instance("02_colend", mod=self.colend1, is_bitcell=True, mirror="MY")] \ + + [geometry.instance("03_strap_p", mod=self.colend3, is_bitcell=False)] + + bit_row = pattern.rotate_list(bit_row, self.column_offset * 2) + bit_block = [] + pattern.append_row_to_block(bit_block, bit_row) + self.pattern = pattern(self, "col_cap_array_" + self.location , bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="col_cap_array" + self.location + "_r{0}_c{1}") + self.pattern.connect_array() + + + def get_bitcell_pins(self, row, col): + """ + Creates a list of connections in the bitcell, + indexed by column and row, for instance use in bitcell_array + """ + bitcell_pins = [] + for port in self.all_ports: + bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))]) + bitcell_pins.append("vdd") # vdd + bitcell_pins.append("gnd") # gnd + bitcell_pins.append("vdd") # vpb + bitcell_pins.append("gnd") # vnb + #bitcell_pins.extend([x for x in self.all_wordline_names if x.endswith("_{0}".format(row))]) + + return bitcell_pins + + def get_strap_pins(self, row, col): + + strap_pins = [] + if col % 2 == 0 and col % 4 != 0: + strap_pins.append("vdd") # vdd + else: + strap_pins.append("gnd") # gnd + strap_pins.append("vdd") # vpb + strap_pins.append("gnd") # vnb + + return strap_pins + + def create_layout(self): + + self.place_array() + self.add_layout_pins() + + self.add_boundary() + self.DRC_LVS() \ No newline at end of file diff --git a/technology/sky130/custom/sky130_corner.py b/technology/sky130/custom/sky130_corner.py index 164e07a0..1abf832a 100644 --- a/technology/sky130/custom/sky130_corner.py +++ b/technology/sky130/custom/sky130_corner.py @@ -7,27 +7,21 @@ from openram import debug from openram.base import design -from openram.base import get_libcell_size -from openram.tech import layer, GDS +from openram.tech import cell_properties as props class sky130_corner(design): def __init__(self, location, name=""): - super().__init__(name) if location == "ul": - self.name = "sky130_fd_bd_sram__sram_sp_corner" + cell_name = "sky130_fd_bd_sram__sram_sp_corner" elif location == "ur": - self.name = "sky130_fd_bd_sram__sram_sp_cornerb" + cell_name = "sky130_fd_bd_sram__sram_sp_cornerb" elif location == "ll": - self.name = "sky130_fd_bd_sram__sram_sp_cornera" + cell_name = "sky130_fd_bd_sram__sram_sp_cornera" elif location == "lr": - self.name = "sky130_fd_bd_sram__sram_sp_cornera" + cell_name = "sky130_fd_bd_sram__sram_sp_cornera" else: debug.error("Invalid sky130_corner location", -1) - design.__init__(self, name=self.name) - (self.width, self.height) = get_libcell_size(self.name, - GDS["unit"], - layer["mem"]) - # pin_map = get_libcell_pins(pin_names, self.name, GDS["unit"]) + super().__init__(name=name, cell_name=cell_name, prop=props.col_cap_1port_strap_power) diff --git a/technology/sky130/custom/sky130_row_cap_array.py b/technology/sky130/custom/sky130_row_cap_array.py index f8d9d91c..b0754896 100644 --- a/technology/sky130/custom/sky130_row_cap_array.py +++ b/technology/sky130/custom/sky130_row_cap_array.py @@ -5,140 +5,100 @@ # All rights reserved. # +from openram.base import geometry from openram.sram_factory import factory from openram import OPTS from .sky130_bitcell_base_array import sky130_bitcell_base_array +from openram.modules.row_cap_array import row_cap_array +from openram.modules.pattern import pattern +from math import ceil - -class sky130_row_cap_array(sky130_bitcell_base_array): +class sky130_row_cap_array(row_cap_array, sky130_bitcell_base_array): """ Generate a dummy row/column for the replica array. """ - def __init__(self, rows, cols, column_offset=0, mirror=0, name=""): - # Don't call the regular col-cap_array constructor since we don't want its constructor, just - # some of it's useful member functions - sky130_bitcell_base_array.__init__(self, rows=rows, cols=cols, column_offset=column_offset, name=name) - self.rows = rows - self.cols = cols - self.column_offset = column_offset + def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name=""): + super().__init__(rows, cols, column_offset=column_offset, location=location, name=name) self.mirror = mirror - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - def create_netlist(self): - """ Create and connect the netlist """ - self.create_all_wordline_names() - # This module has no bitlines - # self.create_all_bitline_names() - - self.add_modules() - self.add_pins() - self.create_instances() - - def create_layout(self): - - self.place_array("dummy_r{0}_c{1}", self.mirror) - self.add_layout_pins() - - self.width = max([x.rx() for x in self.insts]) - self.height = max([x.uy() for x in self.insts]) - - self.add_boundary() - self.DRC_LVS() - + self.location = location def add_modules(self): """ Add the modules used in this design """ if self.column_offset == 0: self.top_corner = factory.create(module_type="corner", location="ul") self.bottom_corner =factory.create(module_type="corner", location="ll") - self.rowend1 = factory.create(module_type="row_cap", version="rowend_replica") - self.rowend2 = factory.create(module_type="row_cap", version="rowenda_replica") + #self.rowend1 = factory.create(module_type="row_cap", version="rowend_replica") + #self.rowend2 = factory.create(module_type="row_cap", version="rowenda_replica") else: self.top_corner = factory.create(module_type="corner", location="ur") self.bottom_corner = factory.create(module_type="corner", location="lr") - self.rowend1 = factory.create(module_type="row_cap", version="rowend") - self.rowend2 = factory.create(module_type="row_cap", version="rowenda") - + #self.rowend1 = factory.create(module_type="row_cap", version="rowend") + #self.rowend2 = factory.create(module_type="row_cap", version="rowenda") + self.rowend = factory.create(module_type="row_cap", version="rowend") + self.rowenda = factory.create(module_type="row_cap", version="rowenda") self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") def create_instances(self): - """ Create the module instances used in this design """ - self.cell_inst = {} - self.array_layout = [] - alternate_bitcell = (self.rows + 1) % 2 - for row in range(self.rows + 2): - row_layout = [] - name="rca_{0}".format(row) - # Top/bottom cell are always dummy cells. - # Regular array cells are replica cells (>left_rbl and 0): - - if alternate_bitcell == 0: - row_layout.append(self.rowend1) - self.cell_inst[row]=self.add_inst(name=name, mod=self.rowend1) - self.connect_inst(["wl_0_{}".format(row - 1), "vdd"]) - alternate_bitcell = 1 - - else: - row_layout.append(self.rowend2) - self.cell_inst[row] = self.add_inst(name=name, mod=self.rowend2) - self.connect_inst(["wl_0_{}".format(row - 1), "vdd"]) - alternate_bitcell = 0 - - elif (row == 0): - row_layout.append(self.bottom_corner) - self.cell_inst[row]=self.add_inst(name=name, mod=self.bottom_corner) - self.connect_inst(self.get_corner_pins()) - - elif (row == self.rows + 1): - row_layout.append(self.top_corner) - self.cell_inst[row]=self.add_inst(name=name, mod=self.top_corner) - self.connect_inst(self.get_corner_pins()) - - self.array_layout.append(row_layout) - - def place_array(self, name_template, row_offset=0): - xoffset = 0.0 - yoffset = 0.0 - for row in range(len(self.insts)): - inst = self.insts[row] - if row == 0: - inst.place(offset=[xoffset, yoffset + inst.height], mirror="MX") - elif row == len(self.insts)-1: - inst.place(offset=[xoffset, yoffset]) + self.all_inst={} + self.cell_inst={} + + bit_block = [] + top_corner = geometry.instance("row_cap_top_corner", mod=self.top_corner, is_bitcell=False, mirror="XY") + bottom_corner = geometry.instance("row_cap_bottom_corner", mod=self.bottom_corner, is_bitcell=False) + rowend = geometry.instance("row_cap_rowend", mod=self.rowend, is_bitcell=True) + rowenda = geometry.instance("row_cap_rowenda", mod=self.rowenda, is_bitcell=True, mirror="XY") + + pattern.append_row_to_block(bit_block, [top_corner]) + for row in range(1,self.row_size-1): + if row % 2 == 0: + pattern.append_row_to_block(bit_block, [rowend]) else: - if row % 2 ==0: - inst.place(offset=[xoffset, yoffset + inst.height], mirror="MX") - else: - inst.place(offset=[xoffset, yoffset]) - yoffset += inst.height + pattern.append_row_to_block(bit_block, [rowenda]) + pattern.append_row_to_block(bit_block, [bottom_corner]) + self.pattern = pattern(self, "row_cap_array_" + self.location, bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="row_cap_array" + self.location + "_r{0}_c{1}") + self.pattern.connect_array_raw() + + + def get_bitcell_pins(self, row, col): + """ + Creates a list of connections in the bitcell, + indexed by column and row, for instance use in bitcell_array + """ - def add_pins(self): - for row in range(self.rows + 2): + bitcell_pins = [] + bitcell_pins.append("vdd") # vdd + bitcell_pins.extend([x for x in self.all_wordline_names if x.endswith("_{0}".format(row))]) + + #bitcell_pins.extend([x for x in self.all_wordline_names if x.endswith("_{0}".format(row))]) + + return bitcell_pins + + def get_strap_pins(self, row, col): + + strap_pins = [] + + strap_pins.append("vdd") # vdd + strap_pins.append("vdd") # vpb + strap_pins.append("gnd") # vnb + + return strap_pins + + def create_all_wordline_names(self, row_size=None, start_row=0): + if row_size == None: + row_size = self.row_size + row_size = row_size - 2 + for row in range(start_row, row_size): for port in self.all_ports: - self.add_pin("wl_{}_{}".format(port, row), "OUTPUT") - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") + self.wordline_names[port].append("wl_{0}_{1}".format(port, row)) - def add_layout_pins(self): - """ Add the layout pins """ - for row in range(0, self.rows + 1): - if row > 0 and row < self.rows + 1: - wl_pin = self.cell_inst[row].get_pin("wl") - self.add_layout_pin(text="wl_0_{0}".format(row -1), - layer=wl_pin.layer, - offset=wl_pin.ll().scale(0, 1), - width=self.width, - height=wl_pin.height()) + self.all_wordline_names = [x for sl in zip(*self.wordline_names) for x in sl] + + def create_layout(self): - # Add vdd/gnd via stacks - for row in range(1, self.rows): - inst = self.cell_inst[row] - for pin_name in ["vdd", "gnd"]: - for pin in inst.get_pins(pin_name): - self.copy_layout_pin(inst, pin_name) + self.place_array() + self.add_layout_pins() + + self.add_boundary() + self.DRC_LVS() + diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index 9c491013..51a4b8d7 100755 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -114,13 +114,12 @@ 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 = d.cell(['bl', 'vdd', 'gnd', 'br', 'gate', 'vpb', 'vnb'], - ['INPUT', 'POWER', 'GROUND', 'INPUT', 'INPUT', 'BIAS', 'BIAS'], +cell_properties.col_cap_1port_bitcell = d.cell(['bl', 'br', 'vdd', 'gnd', 'vpb', 'vnb'], + ['INPUT', 'INPUT','POWER', 'GROUND', 'BIAS', 'BIAS'], {'bl': 'bl', 'br': 'br', 'vdd': 'vdd', 'gnd': 'gnd', - 'gate': 'gate', 'vnb': 'vnb', 'vpb': 'vpb'}) cell_properties.col_cap_1port_bitcell.boundary_layer = "mem" From 8794070ebcfb830b1ae2cd5345e6fc99fd346974 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Mon, 28 Aug 2023 12:31:55 -0700 Subject: [PATCH 38/48] various refactor changes --- .../modules/capped_replica_bitcell_array.py | 5 +++- compiler/modules/col_cap_array.py | 16 +++++++----- compiler/modules/row_cap_array.py | 19 +++++++++++--- .../sky130_capped_replica_bitcell_array.py | 26 +++++++++++++++++++ .../sky130/custom/sky130_col_cap_array.py | 18 ++++++++----- .../sky130/custom/sky130_row_cap_array.py | 21 ++++++++++----- 6 files changed, 81 insertions(+), 24 deletions(-) diff --git a/compiler/modules/capped_replica_bitcell_array.py b/compiler/modules/capped_replica_bitcell_array.py index a2976235..df74b8b9 100644 --- a/compiler/modules/capped_replica_bitcell_array.py +++ b/compiler/modules/capped_replica_bitcell_array.py @@ -204,10 +204,12 @@ class capped_replica_bitcell_array(bitcell_base_array): self.add_end_caps() + ll=vector(min([x.lx() for x in self.insts]),min([y.by() for y in self.insts])) + + self.translate_all(ll) self.width = max([x.rx() for x in self.insts]) - min([x.lx() for x in self.insts]) self.height = max([x.uy() for x in self.insts]) - min([y.by() for y in self.insts]) - self.add_layout_pins() self.route_supplies() @@ -219,6 +221,7 @@ class capped_replica_bitcell_array(bitcell_base_array): self.translate_all(ll) + self.add_layout_pins() self.add_boundary() self.DRC_LVS() diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index 26554ca6..9f9a64da 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -82,17 +82,21 @@ class col_cap_array(bitcell_base_array): def add_layout_pins(self): """ Add the layout pins """ - column_list = self.cell.get_all_bitline_names() - + bitline_names = self.cell.get_all_bitline_names() for col in range(self.column_size): - for cell_column in column_list: - bl_pin = self.cell_inst[0, col].get_pin(cell_column) - self.add_layout_pin(text=cell_column + "_{0}".format(col), + for port in self.all_ports: + bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port]) + self.add_layout_pin(text="bl_{0}_{1}".format(port, col), layer=bl_pin.layer, offset=bl_pin.ll().scale(1, 0), width=bl_pin.width(), height=self.height) - + br_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port + 1]) + self.add_layout_pin(text="br_{0}_{1}".format(port, col), + layer=br_pin.layer, + offset=br_pin.ll().scale(1, 0), + width=br_pin.width(), + height=self.height) # Add vdd/gnd via stacks for row in range(self.row_size): for col in range(self.column_size): diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index 3c91932b..4febdde3 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -73,12 +73,23 @@ class row_cap_array(bitcell_base_array): def add_layout_pins(self): """ Add the layout pins """ - row_list = self.cell.get_all_wl_names() + #row_list = self.cell.get_all_wl_names() + #for row in range(0, self.row_size - 2): + # for cell_row in row_list: + # wl_pin = self.cell_inst[row, 0].get_pin(cell_row) + # self.add_layout_pin(text=cell_row + "_{0}".format(row), + # layer=wl_pin.layer, + # offset=wl_pin.ll().scale(0, 1), + # width=self.width, + # height=wl_pin.height()) + + wl_names = self.cell.get_all_wl_names() + for row in range(0, self.row_size - 2): - for cell_row in row_list: - wl_pin = self.cell_inst[row, 0].get_pin(cell_row) - self.add_layout_pin(text=cell_row + "_{0}".format(row), + for port in self.all_ports: + wl_pin = self.cell_inst[row, 0].get_pin(wl_names[port]) + self.add_layout_pin(text="wl_{0}_{1}".format(port, row), layer=wl_pin.layer, offset=wl_pin.ll().scale(0, 1), width=self.width, diff --git a/technology/sky130/custom/sky130_capped_replica_bitcell_array.py b/technology/sky130/custom/sky130_capped_replica_bitcell_array.py index 49868bbe..dfe9a727 100644 --- a/technology/sky130/custom/sky130_capped_replica_bitcell_array.py +++ b/technology/sky130/custom/sky130_capped_replica_bitcell_array.py @@ -23,3 +23,29 @@ class sky130_capped_replica_bitcell_array(capped_replica_bitcell_array, sky130_b def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""): super().__init__(rows, cols, rbl, left_rbl, right_rbl, name) + def add_layout_pins(self): + for used_name, base_name in zip(self.used_wordline_names, self.dummy_col_insts[0].mod.all_wordline_names): + pin = self.dummy_col_insts[0].get_pin(base_name) + + pin_offset = pin.ll().scale(0, 1) + pin_width = self.width + pin_height = pin.height() + + self.add_layout_pin(text=used_name, + layer=pin.layer, + offset=pin_offset, + width=pin_width, + height=pin_height) + + for used_name, pin_name in zip(self.bitline_pin_list, self.dummy_row_insts[0].mod.all_bitline_names): + pin = self.dummy_row_insts[0].get_pin(pin_name) + pin_offset = pin.ll().scale(1, 0) + pin_width = pin.width() + pin_height = self.height + self.add_layout_pin(text=used_name, + layer=pin.layer, + offset=pin_offset, + width=pin_width, + height=pin_height) + + diff --git a/technology/sky130/custom/sky130_col_cap_array.py b/technology/sky130/custom/sky130_col_cap_array.py index eb44bc81..70ca69a3 100644 --- a/technology/sky130/custom/sky130_col_cap_array.py +++ b/technology/sky130/custom/sky130_col_cap_array.py @@ -37,12 +37,18 @@ class sky130_col_cap_array(col_cap_array, sky130_bitcell_base_array): def create_instances(self): self.all_inst={} self.cell_inst={} - - bit_row = [geometry.instance("00_colend", mod=self.colend1, is_bitcell=True)] \ - + [geometry.instance("01_strap_p_cent", mod=self.colend2, is_bitcell=False)]\ - + [geometry.instance("02_colend", mod=self.colend1, is_bitcell=True, mirror="MY")] \ - + [geometry.instance("03_strap_p", mod=self.colend3, is_bitcell=False)] + if self.location == "top": + bit_row = [geometry.instance("00_colend", mod=self.colend1, is_bitcell=True)] \ + + [geometry.instance("01_strap_p_cent", mod=self.colend2, is_bitcell=False)]\ + + [geometry.instance("02_colend", mod=self.colend1, is_bitcell=True, mirror="MY")] \ + + [geometry.instance("03_strap_p", mod=self.colend3, is_bitcell=False)] + elif self.location == "bottom": + bit_row = [geometry.instance("00_colend", mod=self.colend1, is_bitcell=True, mirror="MX")] \ + + [geometry.instance("01_strap_p_cent", mod=self.colend2, is_bitcell=False, mirror="MX")]\ + + [geometry.instance("02_colend", mod=self.colend1, is_bitcell=True, mirror="XY")] \ + + [geometry.instance("03_strap_p", mod=self.colend3, is_bitcell=False, mirror="MX")] + bit_row = pattern.rotate_list(bit_row, self.column_offset * 2) bit_block = [] pattern.append_row_to_block(bit_block, bit_row) @@ -84,4 +90,4 @@ class sky130_col_cap_array(col_cap_array, sky130_bitcell_base_array): self.add_layout_pins() self.add_boundary() - self.DRC_LVS() \ No newline at end of file + self.DRC_LVS() diff --git a/technology/sky130/custom/sky130_row_cap_array.py b/technology/sky130/custom/sky130_row_cap_array.py index b0754896..24e85585 100644 --- a/technology/sky130/custom/sky130_row_cap_array.py +++ b/technology/sky130/custom/sky130_row_cap_array.py @@ -23,7 +23,7 @@ class sky130_row_cap_array(row_cap_array, sky130_bitcell_base_array): self.location = location def add_modules(self): """ Add the modules used in this design """ - if self.column_offset == 0: + if self.location == "left": self.top_corner = factory.create(module_type="corner", location="ul") self.bottom_corner =factory.create(module_type="corner", location="ll") #self.rowend1 = factory.create(module_type="row_cap", version="rowend_replica") @@ -44,18 +44,25 @@ class sky130_row_cap_array(row_cap_array, sky130_bitcell_base_array): self.cell_inst={} bit_block = [] - top_corner = geometry.instance("row_cap_top_corner", mod=self.top_corner, is_bitcell=False, mirror="XY") - bottom_corner = geometry.instance("row_cap_bottom_corner", mod=self.bottom_corner, is_bitcell=False) - rowend = geometry.instance("row_cap_rowend", mod=self.rowend, is_bitcell=True) - rowenda = geometry.instance("row_cap_rowenda", mod=self.rowenda, is_bitcell=True, mirror="XY") - pattern.append_row_to_block(bit_block, [top_corner]) + if self.location == "left": + top_corner = geometry.instance("row_cap_top_corner", mod=self.top_corner, is_bitcell=False, mirror="MY") + bottom_corner = geometry.instance("row_cap_bottom_corner", mod=self.bottom_corner, is_bitcell=False, mirror="XY") + rowend = geometry.instance("row_cap_rowend", mod=self.rowend, is_bitcell=True, mirror="XY") + rowenda = geometry.instance("row_cap_rowenda", mod=self.rowenda, is_bitcell=True, mirror="MY") + elif self.location == "right": + top_corner = geometry.instance("row_cap_top_corner", mod=self.top_corner, is_bitcell=False) + bottom_corner = geometry.instance("row_cap_bottom_corner", mod=self.bottom_corner, is_bitcell=False, mirror="MX") + rowend = geometry.instance("row_cap_rowend", mod=self.rowend, is_bitcell=True, mirror="MX") + rowenda = geometry.instance("row_cap_rowenda", mod=self.rowenda, is_bitcell=True) + + pattern.append_row_to_block(bit_block, [bottom_corner]) for row in range(1,self.row_size-1): if row % 2 == 0: pattern.append_row_to_block(bit_block, [rowend]) else: pattern.append_row_to_block(bit_block, [rowenda]) - pattern.append_row_to_block(bit_block, [bottom_corner]) + pattern.append_row_to_block(bit_block, [top_corner]) self.pattern = pattern(self, "row_cap_array_" + self.location, bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="row_cap_array" + self.location + "_r{0}_c{1}") self.pattern.connect_array_raw() From 8f2e4c691462760f7634891a1d3fb51a9419b9de Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Mon, 28 Aug 2023 22:15:05 -0700 Subject: [PATCH 39/48] power ring working --- compiler/base/hierarchy_layout.py | 85 ++++++----- .../modules/capped_replica_bitcell_array.py | 139 +++++++++++------- 2 files changed, 128 insertions(+), 96 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 8c595254..31ec17d7 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -2157,6 +2157,13 @@ class layout(): # Add the gnd ring self.add_ring([ll, ur]) + def reset_coordinates(self): + ll=vector(min([x.lx() for x in self.insts]),min([y.by() for y in self.insts])) + + self.translate_all(ll) + self.width = max([x.rx() for x in self.insts]) - min([x.lx() for x in self.insts]) + self.height = max([x.uy() for x in self.insts]) - min([y.by() for y in self.insts]) + def add_ring(self, bbox=None, width_mult=8, offset=0): """ Add a ring around the bbox @@ -2209,7 +2216,7 @@ class layout(): size=(supply_vias, supply_vias)) - def add_power_ring(self): + def add_power_ring(self, h_layer="m2", v_layer="m1"): """ Create vdd and gnd power rings around an area of the bounding box argument. Must have a supply_rail_width and supply_rail_pitch @@ -2217,42 +2224,42 @@ class layout(): left/right/top/bottom vdd/gnd center offsets for use in other modules.. """ - [ll, ur] = self.bbox - supply_rail_spacing = self.supply_rail_pitch - self.supply_rail_width + supply_rail_spacing = self.supply_rail_pitch height = (ur.y - ll.y) + 3 * self.supply_rail_pitch - supply_rail_spacing width = (ur.x - ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing # LEFT vertical rails - offset = ll + vector(-2 * self.supply_rail_pitch, - -2 * self.supply_rail_pitch) - left_gnd_pin = self.add_layout_pin(text="gnd", - layer="m2", + offset = ll + vector(-2*self.supply_rail_pitch, + -2*self.supply_rail_pitch) + self.left_gnd_pin = self.add_layout_pin(text="gnd", + layer=v_layer, offset=offset, width=self.supply_rail_width, - height=height) + height=height + 2 * supply_rail_spacing) offset = ll + vector(-1 * self.supply_rail_pitch, -1 * self.supply_rail_pitch) - left_vdd_pin = self.add_layout_pin(text="vdd", - layer="m2", + self.left_vdd_pin = self.add_layout_pin(text="vdd", + layer=v_layer, offset=offset, width=self.supply_rail_width, height=height) - # RIGHT vertical rails - offset = vector(ur.x, ll.y) + vector(0, -2 * self.supply_rail_pitch) - right_gnd_pin = self.add_layout_pin(text="gnd", - layer="m2", + # RIGHT vertical railsteac a 460 + offset = vector(ur.x, ll.y) + vector(2 * self.supply_rail_pitch - self.supply_rail_width, + -2 * self.supply_rail_pitch) + self.right_gnd_pin = self.add_layout_pin(text="gnd", + layer=v_layer, offset=offset, width=self.supply_rail_width, - height=height) + height=height + 2* supply_rail_spacing) - offset = vector(ur.x, ll.y) + vector(self.supply_rail_pitch, + offset = vector(ur.x, ll.y) + vector(1 * self.supply_rail_pitch - self.supply_rail_width, -1 * self.supply_rail_pitch) - right_vdd_pin = self.add_layout_pin(text="vdd", - layer="m2", + self.right_vdd_pin = self.add_layout_pin(text="vdd", + layer=v_layer, offset=offset, width=self.supply_rail_width, height=height) @@ -2260,47 +2267,47 @@ class layout(): # BOTTOM horizontal rails offset = ll + vector(-2 * self.supply_rail_pitch, -2 * self.supply_rail_pitch) - bottom_gnd_pin = self.add_layout_pin(text="gnd", - layer="m1", + self.bottom_gnd_pin = self.add_layout_pin(text="gnd", + layer=h_layer, offset=offset, - width=width, + width=width + 2 * supply_rail_spacing, height=self.supply_rail_width) offset = ll + vector(-1 * self.supply_rail_pitch, -1 * self.supply_rail_pitch) - bottom_vdd_pin = self.add_layout_pin(text="vdd", - layer="m1", + self.bottom_vdd_pin = self.add_layout_pin(text="vdd", + layer=h_layer, offset=offset, width=width, height=self.supply_rail_width) # TOP horizontal rails offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch, - 0) - top_gnd_pin = self.add_layout_pin(text="gnd", - layer="m1", + 2 * self.supply_rail_pitch - self.supply_rail_width) + self.top_gnd_pin = self.add_layout_pin(text="gnd", + layer=h_layer, offset=offset, - width=width, + width=width + 2 * supply_rail_spacing, height=self.supply_rail_width) - offset = vector(ll.x, ur.y) + vector(-1 * self.supply_rail_pitch, - self.supply_rail_pitch) - top_vdd_pin = self.add_layout_pin(text="vdd", - layer="m1", + offset = vector(ll.x, ur.y) + vector(-1 * self.supply_rail_pitch, + 1 * self.supply_rail_pitch - self.supply_rail_width) + self.top_vdd_pin = self.add_layout_pin(text="vdd", + layer=h_layer, offset=offset, width=width, height=self.supply_rail_width) # Remember these for connecting things in the design - self.left_gnd_x_center = left_gnd_pin.cx() - self.left_vdd_x_center = left_vdd_pin.cx() - self.right_gnd_x_center = right_gnd_pin.cx() - self.right_vdd_x_center = right_vdd_pin.cx() + self.left_gnd_x_center = self.left_gnd_pin.cx() + self.left_vdd_x_center = self.left_vdd_pin.cx() + self.right_gnd_x_center = self.right_gnd_pin.cx() + self.right_vdd_x_center = self.right_vdd_pin.cx() - self.bottom_gnd_y_center = bottom_gnd_pin.cy() - self.bottom_vdd_y_center = bottom_vdd_pin.cy() - self.top_gnd_y_center = top_gnd_pin.cy() - self.top_vdd_y_center = top_vdd_pin.cy() + self.bottom_gnd_y_center = self.bottom_gnd_pin.cy() + self.bottom_vdd_y_center = self.bottom_vdd_pin.cy() + self.top_gnd_y_center = self.top_gnd_pin.cy() + self.top_vdd_y_center = self.top_vdd_pin.cy() # Find the number of vias for this pitch self.supply_vias = 1 diff --git a/compiler/modules/capped_replica_bitcell_array.py b/compiler/modules/capped_replica_bitcell_array.py index df74b8b9..30d6e2c5 100644 --- a/compiler/modules/capped_replica_bitcell_array.py +++ b/compiler/modules/capped_replica_bitcell_array.py @@ -203,28 +203,29 @@ class capped_replica_bitcell_array(bitcell_base_array): self.replica_bitcell_array_inst.place(offset=0) self.add_end_caps() - - ll=vector(min([x.lx() for x in self.insts]),min([y.by() for y in self.insts])) + ll = vector(-1 * self.dummy_col_insts[0].width, -1 * self.dummy_row_insts[0].height) self.translate_all(ll) - self.width = max([x.rx() for x in self.insts]) - min([x.lx() for x in self.insts]) - self.height = max([x.uy() for x in self.insts]) - min([y.by() for y in self.insts]) + self.capped_rba_width = self.dummy_col_insts[0].width + self.dummy_row_insts[0].width + self.dummy_col_insts[1].width + self.capped_rba_height = self.dummy_col_insts[0].height + self.route_power_ring(self.supply_stack[2], self.supply_stack[0]) self.route_supplies() self.route_unused_wordlines() - self.width = max([x.rx() for x in self.insts]) - min([x.lx() for x in self.insts]) - self.height = max([x.uy() for x in self.insts]) - min([y.by() for y in self.insts]) - ll=vector(min([x.lx() for x in self.insts]),min([y.by() for y in self.insts])) - - self.translate_all(ll) - + self.reset_coordinates() self.add_layout_pins() self.add_boundary() - self.DRC_LVS() + + + def route_power_ring(self, v_layer, h_layer): + self.bbox = (vector(0,0), vector(self.capped_rba_width, self.capped_rba_height)) + self.supply_rail_width = drc["minwidth_m1"] + self.supply_rail_pitch = 3 * self.supply_rail_width + self.add_power_ring(v_layer, h_layer) def get_main_array_top(self): return self.replica_bitcell_array_inst.by() + self.replica_bitcell_array.get_main_array_top() @@ -288,13 +289,13 @@ class capped_replica_bitcell_array(bitcell_base_array): if "wl" in pin_name: # wordlines pin_offset = pin.ll().scale(0, 1) - pin_width = self.width + pin_width = self.capped_rba_width pin_height = pin.height() else: # bitlines pin_offset = pin.ll().scale(1, 0) pin_width = pin.width() - pin_height = self.height + pin_height = self.capped_rba_height self.add_layout_pin(text=pin_name, layer=pin.layer, @@ -309,55 +310,79 @@ class capped_replica_bitcell_array(bitcell_base_array): else: bitcell = getattr(props, "bitcell_{}port".format(OPTS.num_ports)) - vdd_dir = bitcell.vdd_dir - gnd_dir = bitcell.gnd_dir - + #vdd_dir = bitcell.vdd_dir + #gnd_dir = bitcell.gnd_dir + # vdd/gnd are only connected in the perimeter cells - supply_insts = self.dummy_col_insts + self.dummy_row_insts + #supply_insts = self.dummy_col_insts + self.dummy_row_insts + inst = self.dummy_row_insts[1] + if "vdd" in inst.mod.pins: + array_pins = inst.get_pins("vdd") + for array_pin in array_pins: + supply_pin = self.top_vdd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(array_pin.center()[0], supply_pin.center()[1])) - # For the wordlines - top_bot_mult = 1 - left_right_mult = 1 + array_pins = inst.get_pins("gnd") + for array_pin in array_pins: + supply_pin = self.top_gnd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(array_pin.center()[0], supply_pin.center()[1])) + inst = self.dummy_row_insts[0] + if "vdd" in inst.mod.pins: + array_pins = inst.get_pins("vdd") + for array_pin in array_pins: + supply_pin = self.bottom_vdd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(array_pin.center()[0], supply_pin.center()[1])) - # 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) - # This needs to be big enough so that they aren't in the same supply routing grid - left_right_mult = 4 + array_pins = inst.get_pins("gnd") + for array_pin in array_pins: + supply_pin = self.bottom_gnd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(array_pin.center()[0], supply_pin.center()[1])) + inst = self.dummy_col_insts[0] + if "vdd" in inst.mod.pins: + array_pins = inst.get_pins("vdd") + for array_pin in array_pins: + supply_pin = self.left_vdd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(supply_pin.center()[0], array_pin.center()[1])) - 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) - # 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) - 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"): - 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"): - 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) + array_pins = inst.get_pins("gnd") + for array_pin in array_pins: + supply_pin = self.left_gnd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(supply_pin.center()[0], array_pin.center()[1])) + inst = self.dummy_col_insts[1] + if "vdd" in inst.mod.pins: + array_pins = inst.get_pins("vdd") + for array_pin in array_pins: + supply_pin = self.right_vdd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(supply_pin.center()[0], array_pin.center()[1])) + array_pins = inst.get_pins("gnd") + for array_pin in array_pins: + supply_pin = self.right_gnd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(supply_pin.center()[0], array_pin.center()[1])) def route_unused_wordlines(self): """ Connect the unused RBL and dummy wordlines to gnd From 0cba6a605043c50be769239a8135b1e4f5483492 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 30 Aug 2023 20:59:02 -0700 Subject: [PATCH 40/48] single port sky130 crba passing lvs --- compiler/base/hierarchy_layout.py | 194 +++++++++--------- .../modules/capped_replica_bitcell_array.py | 147 ++++++------- compiler/modules/col_cap_array.py | 2 +- compiler/modules/row_cap_array.py | 2 +- .../sky130/custom/sky130_col_cap_array.py | 1 + technology/sky130/tech/tech.py | 7 +- 6 files changed, 180 insertions(+), 173 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 31ec17d7..9d9bb131 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -2216,7 +2216,7 @@ class layout(): size=(supply_vias, supply_vias)) - def add_power_ring(self, h_layer="m2", v_layer="m1"): + def add_power_ring(self, h_layer="m2", v_layer="m1", top=True, bottom=True, left=True, right=True): """ Create vdd and gnd power rings around an area of the bounding box argument. Must have a supply_rail_width and supply_rail_pitch @@ -2231,111 +2231,113 @@ class layout(): width = (ur.x - ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing # LEFT vertical rails - offset = ll + vector(-2*self.supply_rail_pitch, - -2*self.supply_rail_pitch) - self.left_gnd_pin = self.add_layout_pin(text="gnd", - layer=v_layer, - offset=offset, - width=self.supply_rail_width, - height=height + 2 * supply_rail_spacing) + if left: + offset = ll + vector(-2*self.supply_rail_pitch, + -2*self.supply_rail_pitch) + self.left_gnd_pin = self.add_layout_pin(text="gnd", + layer=v_layer, + offset=offset, + width=self.supply_rail_width, + height=height + 2 * supply_rail_spacing) - offset = ll + vector(-1 * self.supply_rail_pitch, - -1 * self.supply_rail_pitch) - self.left_vdd_pin = self.add_layout_pin(text="vdd", - layer=v_layer, - offset=offset, - width=self.supply_rail_width, - height=height) + offset = ll + vector(-1 * self.supply_rail_pitch, + -1 * self.supply_rail_pitch) + self.left_vdd_pin = self.add_layout_pin(text="vdd", + layer=v_layer, + offset=offset, + width=self.supply_rail_width, + height=height) - # RIGHT vertical railsteac a 460 - offset = vector(ur.x, ll.y) + vector(2 * self.supply_rail_pitch - self.supply_rail_width, - -2 * self.supply_rail_pitch) - self.right_gnd_pin = self.add_layout_pin(text="gnd", - layer=v_layer, - offset=offset, - width=self.supply_rail_width, - height=height + 2* supply_rail_spacing) + if right: + # RIGHT vertical railsteac a 460 + offset = vector(ur.x, ll.y) + vector(2 * self.supply_rail_pitch - self.supply_rail_width, + -2 * self.supply_rail_pitch) + self.right_gnd_pin = self.add_layout_pin(text="gnd", + layer=v_layer, + offset=offset, + width=self.supply_rail_width, + height=height + 2* supply_rail_spacing) - offset = vector(ur.x, ll.y) + vector(1 * self.supply_rail_pitch - self.supply_rail_width, - -1 * self.supply_rail_pitch) - self.right_vdd_pin = self.add_layout_pin(text="vdd", - layer=v_layer, - offset=offset, - width=self.supply_rail_width, - height=height) + offset = vector(ur.x, ll.y) + vector(1 * self.supply_rail_pitch - self.supply_rail_width, + -1 * self.supply_rail_pitch) + self.right_vdd_pin = self.add_layout_pin(text="vdd", + layer=v_layer, + offset=offset, + width=self.supply_rail_width, + height=height) - # BOTTOM horizontal rails - offset = ll + vector(-2 * self.supply_rail_pitch, - -2 * self.supply_rail_pitch) - self.bottom_gnd_pin = self.add_layout_pin(text="gnd", - layer=h_layer, - offset=offset, - width=width + 2 * supply_rail_spacing, - height=self.supply_rail_width) + if bottom: + # BOTTOM horizontal rails + offset = ll + vector(-2 * self.supply_rail_pitch, + -2 * self.supply_rail_pitch) + self.bottom_gnd_pin = self.add_layout_pin(text="gnd", + layer=h_layer, + offset=offset, + width=width + 2 * supply_rail_spacing, + height=self.supply_rail_width) - offset = ll + vector(-1 * self.supply_rail_pitch, - -1 * self.supply_rail_pitch) - self.bottom_vdd_pin = self.add_layout_pin(text="vdd", - layer=h_layer, - offset=offset, - width=width, - height=self.supply_rail_width) + offset = ll + vector(-1 * self.supply_rail_pitch, + -1 * self.supply_rail_pitch) + self.bottom_vdd_pin = self.add_layout_pin(text="vdd", + layer=h_layer, + offset=offset, + width=width, + height=self.supply_rail_width) + if top: + # TOP horizontal rails + offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch, + 2 * self.supply_rail_pitch - self.supply_rail_width) + self.top_gnd_pin = self.add_layout_pin(text="gnd", + layer=h_layer, + offset=offset, + width=width + 2 * supply_rail_spacing, + height=self.supply_rail_width) - # TOP horizontal rails - offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch, - 2 * self.supply_rail_pitch - self.supply_rail_width) - self.top_gnd_pin = self.add_layout_pin(text="gnd", - layer=h_layer, - offset=offset, - width=width + 2 * supply_rail_spacing, - height=self.supply_rail_width) - - offset = vector(ll.x, ur.y) + vector(-1 * self.supply_rail_pitch, - 1 * self.supply_rail_pitch - self.supply_rail_width) - self.top_vdd_pin = self.add_layout_pin(text="vdd", - layer=h_layer, - offset=offset, - width=width, - height=self.supply_rail_width) + offset = vector(ll.x, ur.y) + vector(-1 * self.supply_rail_pitch, + 1 * self.supply_rail_pitch - self.supply_rail_width) + self.top_vdd_pin = self.add_layout_pin(text="vdd", + layer=h_layer, + offset=offset, + width=width, + height=self.supply_rail_width) # Remember these for connecting things in the design - self.left_gnd_x_center = self.left_gnd_pin.cx() - self.left_vdd_x_center = self.left_vdd_pin.cx() - self.right_gnd_x_center = self.right_gnd_pin.cx() - self.right_vdd_x_center = self.right_vdd_pin.cx() - - self.bottom_gnd_y_center = self.bottom_gnd_pin.cy() - self.bottom_vdd_y_center = self.bottom_vdd_pin.cy() - self.top_gnd_y_center = self.top_gnd_pin.cy() - self.top_vdd_y_center = self.top_vdd_pin.cy() - - # Find the number of vias for this pitch - self.supply_vias = 1 - while True: - c = factory.create(module_type="contact", - layer_stack=self.m1_stack, - dimensions=(self.supply_vias, self.supply_vias)) - if c.second_layer_width < self.supply_rail_width and c.second_layer_height < self.supply_rail_width: - self.supply_vias += 1 - else: - self.supply_vias -= 1 - break - - via_points = [vector(self.left_gnd_x_center, self.bottom_gnd_y_center), - vector(self.left_gnd_x_center, self.top_gnd_y_center), - vector(self.right_gnd_x_center, self.bottom_gnd_y_center), - vector(self.right_gnd_x_center, self.top_gnd_y_center), - vector(self.left_vdd_x_center, self.bottom_vdd_y_center), - vector(self.left_vdd_x_center, self.top_vdd_y_center), - vector(self.right_vdd_x_center, self.bottom_vdd_y_center), - vector(self.right_vdd_x_center, self.top_vdd_y_center)] + if left: + self.left_gnd_x_center = self.left_gnd_pin.cx() + self.left_vdd_x_center = self.left_vdd_pin.cx() + if right: + self.right_gnd_x_center = self.right_gnd_pin.cx() + self.right_vdd_x_center = self.right_vdd_pin.cx() + if bottom: + self.bottom_gnd_y_center = self.bottom_gnd_pin.cy() + self.bottom_vdd_y_center = self.bottom_vdd_pin.cy() + if top: + self.top_gnd_y_center = self.top_gnd_pin.cy() + self.top_vdd_y_center = self.top_vdd_pin.cy() + via_points = [] + if left and bottom: + via_points.append((self.left_gnd_x_center, self.bottom_gnd_y_center)) + if left and top: + via_points.append(vector(self.left_gnd_x_center, self.top_gnd_y_center)) + if right and bottom: + via_points.append(vector(self.right_gnd_x_center, self.bottom_gnd_y_center)) + if right and top: + via_points.append(vector(self.right_gnd_x_center, self.top_gnd_y_center)) + if left and bottom: + via_points.append(vector(self.left_vdd_x_center, self.bottom_vdd_y_center)) + if left and top: + via_points.append(vector(self.left_vdd_x_center, self.top_vdd_y_center)) + if right and bottom: + via_points.append(vector(self.right_vdd_x_center, self.bottom_vdd_y_center)) + if right and top: + via_points.append((self.right_vdd_x_center, self.top_vdd_y_center)) + for pt in via_points: - self.add_via_center(layers=self.m1_stack, - offset=pt, - size=(self.supply_vias, - self.supply_vias)) - + self.add_via_stack_center(offset=pt, + from_layer=h_layer, + to_layer=v_layer, + min_area=True) def pdf_write(self, pdf_name): """ Display the layout to a PDF file. diff --git a/compiler/modules/capped_replica_bitcell_array.py b/compiler/modules/capped_replica_bitcell_array.py index 30d6e2c5..db7a1547 100644 --- a/compiler/modules/capped_replica_bitcell_array.py +++ b/compiler/modules/capped_replica_bitcell_array.py @@ -224,8 +224,8 @@ class capped_replica_bitcell_array(bitcell_base_array): def route_power_ring(self, v_layer, h_layer): self.bbox = (vector(0,0), vector(self.capped_rba_width, self.capped_rba_height)) self.supply_rail_width = drc["minwidth_m1"] - self.supply_rail_pitch = 3 * self.supply_rail_width - self.add_power_ring(v_layer, h_layer) + self.supply_rail_pitch = 6 * self.supply_rail_width + self.add_power_ring(v_layer=v_layer, h_layer=h_layer) def get_main_array_top(self): return self.replica_bitcell_array_inst.by() + self.replica_bitcell_array.get_main_array_top() @@ -309,80 +309,83 @@ class capped_replica_bitcell_array(bitcell_base_array): bitcell = factory.create(module_type="pbitcell") else: bitcell = getattr(props, "bitcell_{}port".format(OPTS.num_ports)) + top = True + bottom = True + left = False + right = False + + if top: + inst = self.dummy_row_insts[1] + if "vdd" in inst.mod.pins: + array_pins = inst.get_pins("vdd") + for array_pin in array_pins: + supply_pin = self.top_vdd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(array_pin.center()[0], supply_pin.center()[1])) - #vdd_dir = bitcell.vdd_dir - #gnd_dir = bitcell.gnd_dir - - # vdd/gnd are only connected in the perimeter cells - #supply_insts = self.dummy_col_insts + self.dummy_row_insts - inst = self.dummy_row_insts[1] - if "vdd" in inst.mod.pins: - array_pins = inst.get_pins("vdd") - for array_pin in array_pins: - supply_pin = self.top_vdd_pin - self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])]) - self.add_via_stack_center(from_layer = array_pin.layer, - to_layer = supply_pin.layer, - offset = vector(array_pin.center()[0], supply_pin.center()[1])) + array_pins = inst.get_pins("gnd") + for array_pin in array_pins: + supply_pin = self.top_gnd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(array_pin.center()[0], supply_pin.center()[1])) + if bottom: + inst = self.dummy_row_insts[0] + if "vdd" in inst.mod.pins: + array_pins = inst.get_pins("vdd") + for array_pin in array_pins: + supply_pin = self.bottom_vdd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(array_pin.center()[0], supply_pin.center()[1])) - array_pins = inst.get_pins("gnd") - for array_pin in array_pins: - supply_pin = self.top_gnd_pin - self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])]) - self.add_via_stack_center(from_layer = array_pin.layer, - to_layer = supply_pin.layer, - offset = vector(array_pin.center()[0], supply_pin.center()[1])) - inst = self.dummy_row_insts[0] - if "vdd" in inst.mod.pins: - array_pins = inst.get_pins("vdd") - for array_pin in array_pins: - supply_pin = self.bottom_vdd_pin - self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])]) - self.add_via_stack_center(from_layer = array_pin.layer, - to_layer = supply_pin.layer, - offset = vector(array_pin.center()[0], supply_pin.center()[1])) + array_pins = inst.get_pins("gnd") + for array_pin in array_pins: + supply_pin = self.bottom_gnd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(array_pin.center()[0], supply_pin.center()[1])) + if left: + inst = self.dummy_col_insts[0] + if "vdd" in inst.mod.pins: + array_pins = inst.get_pins("vdd") + for array_pin in array_pins: + supply_pin = self.left_vdd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(supply_pin.center()[0], array_pin.center()[1])) - array_pins = inst.get_pins("gnd") - for array_pin in array_pins: - supply_pin = self.bottom_gnd_pin - self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])]) - self.add_via_stack_center(from_layer = array_pin.layer, - to_layer = supply_pin.layer, - offset = vector(array_pin.center()[0], supply_pin.center()[1])) - inst = self.dummy_col_insts[0] - if "vdd" in inst.mod.pins: - array_pins = inst.get_pins("vdd") - for array_pin in array_pins: - supply_pin = self.left_vdd_pin - self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])]) - self.add_via_stack_center(from_layer = array_pin.layer, - to_layer = supply_pin.layer, - offset = vector(supply_pin.center()[0], array_pin.center()[1])) + array_pins = inst.get_pins("gnd") + for array_pin in array_pins: + supply_pin = self.left_gnd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(supply_pin.center()[0], array_pin.center()[1])) + if right: + inst = self.dummy_col_insts[1] + if "vdd" in inst.mod.pins: + array_pins = inst.get_pins("vdd") + for array_pin in array_pins: + supply_pin = self.right_vdd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(supply_pin.center()[0], array_pin.center()[1])) - array_pins = inst.get_pins("gnd") - for array_pin in array_pins: - supply_pin = self.left_gnd_pin - self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])]) - self.add_via_stack_center(from_layer = array_pin.layer, - to_layer = supply_pin.layer, - offset = vector(supply_pin.center()[0], array_pin.center()[1])) - inst = self.dummy_col_insts[1] - if "vdd" in inst.mod.pins: - array_pins = inst.get_pins("vdd") - for array_pin in array_pins: - supply_pin = self.right_vdd_pin - self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])]) - self.add_via_stack_center(from_layer = array_pin.layer, - to_layer = supply_pin.layer, - offset = vector(supply_pin.center()[0], array_pin.center()[1])) - - array_pins = inst.get_pins("gnd") - for array_pin in array_pins: - supply_pin = self.right_gnd_pin - self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])]) - self.add_via_stack_center(from_layer = array_pin.layer, - to_layer = supply_pin.layer, - offset = vector(supply_pin.center()[0], array_pin.center()[1])) + array_pins = inst.get_pins("gnd") + for array_pin in array_pins: + supply_pin = self.right_gnd_pin + self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])]) + self.add_via_stack_center(from_layer = array_pin.layer, + to_layer = supply_pin.layer, + offset = vector(supply_pin.center()[0], array_pin.center()[1])) def route_unused_wordlines(self): """ Connect the unused RBL and dummy wordlines to gnd diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index 9f9a64da..5291340e 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -17,7 +17,7 @@ class col_cap_array(bitcell_base_array): self.mirror = mirror self.location = location - self.no_instances = True + #self.no_instances = True self.create_netlist() if not OPTS.netlist_only: self.create_layout() diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index 4febdde3..e35e260a 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -16,7 +16,7 @@ class row_cap_array(bitcell_base_array): super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.mirror = mirror self.location = location - self.no_instances = True + #self.no_instances = True self.create_netlist() if not OPTS.netlist_only: self.create_layout() diff --git a/technology/sky130/custom/sky130_col_cap_array.py b/technology/sky130/custom/sky130_col_cap_array.py index 70ca69a3..017e50ce 100644 --- a/technology/sky130/custom/sky130_col_cap_array.py +++ b/technology/sky130/custom/sky130_col_cap_array.py @@ -68,6 +68,7 @@ class sky130_col_cap_array(col_cap_array, sky130_bitcell_base_array): bitcell_pins.append("gnd") # gnd bitcell_pins.append("vdd") # vpb bitcell_pins.append("gnd") # vnb + bitcell_pins.append("gnd")# poly gate for parasitic tx #bitcell_pins.extend([x for x in self.all_wordline_names if x.endswith("_{0}".format(row))]) return bitcell_pins diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index 51a4b8d7..07ffa69e 100755 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -114,14 +114,15 @@ 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 = d.cell(['bl', 'br', 'vdd', 'gnd', 'vpb', 'vnb'], - ['INPUT', 'INPUT','POWER', 'GROUND', 'BIAS', 'BIAS'], +cell_properties.col_cap_1port_bitcell = d.cell(['bl', 'br', 'vdd', 'gnd', 'vpb', 'vnb', 'gate'], + ['INPUT', 'INPUT','POWER', 'GROUND', 'BIAS', 'BIAS', 'INPUT'], {'bl': 'bl', 'br': 'br', 'vdd': 'vdd', 'gnd': 'gnd', 'vnb': 'vnb', - 'vpb': 'vpb'}) + 'vpb': 'vpb', + 'gate': 'gate'}) cell_properties.col_cap_1port_bitcell.boundary_layer = "mem" cell_properties.col_cap_1port_strap_power = d.cell(['vdd', 'vpb', 'vnb'], From 29e80e8f25d0823fac73f1b1bed3f8c7a86dad94 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 30 Aug 2023 21:18:50 -0700 Subject: [PATCH 41/48] bump sky130 sram library --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dc07a5b1..12c9eaa7 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ SRAM_LIB_GIT_REPO ?= https://github.com/vlsida/sky130_fd_bd_sram.git # Use this for development #SRAM_LIB_GIT_REPO ?= git@github.com:VLSIDA/sky130_fd_bd_sram.git #SRAM_LIB_GIT_REPO ?= https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git -SRAM_LIB_GIT_COMMIT ?= 9fcf3a78398037583b6d6c1ebac71957343c4bd8 +SRAM_LIB_GIT_COMMIT ?= baa2b14282ee6c8498a9e480c88a5096fdce2b06 # Open PDKs OPEN_PDKS_DIR ?= $(PDK_ROOT)/open_pdks From c9a848550c1028822fda99e2423e7da28b5d06d4 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 30 Aug 2023 22:40:35 -0700 Subject: [PATCH 42/48] Revert "merge dev" This reverts commit daec8408883f2842cbebb2a8ce5df10fce87e7b7, reversing changes made to 29e80e8f25d0823fac73f1b1bed3f8c7a86dad94. --- Makefile | 1 - VERSION | 2 +- compiler/router/graph.py | 7 +- compiler/router/graph_shape.py | 11 ++ compiler/router/router.py | 62 ++++------- compiler/router/signal_escape_router.py | 142 +++++++----------------- compiler/router/supply_router.py | 43 ++++--- 7 files changed, 107 insertions(+), 161 deletions(-) diff --git a/Makefile b/Makefile index 42e84f41..12c9eaa7 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,6 @@ SRAM_LIB_GIT_REPO ?= https://github.com/vlsida/sky130_fd_bd_sram.git # Use this for development #SRAM_LIB_GIT_REPO ?= git@github.com:VLSIDA/sky130_fd_bd_sram.git #SRAM_LIB_GIT_REPO ?= https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git - SRAM_LIB_GIT_COMMIT ?= baa2b14282ee6c8498a9e480c88a5096fdce2b06 # Open PDKs diff --git a/VERSION b/VERSION index 37258517..550c9e96 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.32 +1.2.29 diff --git a/compiler/router/graph.py b/compiler/router/graph.py index dc9f5c40..132681c9 100644 --- a/compiler/router/graph.py +++ b/compiler/router/graph.py @@ -184,7 +184,7 @@ class graph: return False - def create_graph(self, source, target): + def create_graph(self, source, target, scale=1): """ Create the graph to run routing on later. """ debug.info(2, "Creating the graph for source '{}' and target'{}'.".format(source, target)) @@ -195,6 +195,7 @@ class graph: # Find the region to be routed and only include objects inside that region region = deepcopy(source) region.bbox([target]) + region.multiply(scale) region = region.inflated_pin(spacing=self.router.track_space) debug.info(3, "Routing region is {}".format(region.rect)) @@ -220,6 +221,9 @@ class graph: debug.info(3, "Number of vias detected in the routing region: {}".format(len(self.graph_vias))) debug.info(3, "Number of nodes in the routing graph: {}".format(len(self.nodes))) + # Return the region to scale later if no path is found + return region.rect + def find_graph_blockages(self, region): """ Find blockages that overlap the routing region. """ @@ -418,7 +422,6 @@ class graph: path.append(current) current = came_from[current.id] path.append(current) - path.reverse() return path # Get the previous node to better calculate the next costs diff --git a/compiler/router/graph_shape.py b/compiler/router/graph_shape.py index 0f950ae4..1221dabe 100644 --- a/compiler/router/graph_shape.py +++ b/compiler/router/graph_shape.py @@ -72,6 +72,17 @@ class graph_shape(pin_layout): return graph_shape(self.name, inflated_area, self.layer, self) + def multiply(self, scale): + """ Multiply the width and height with the scale value. """ + + width = (self.width() * (scale - 1)) / 2 + height = (self.height() * (scale - 1)) / 2 + ll, ur = self.rect + newll = vector(ll.x - width, ll.y - height) + newur = vector(ur.x + width, ur.y + height) + self.rect = [snap(newll), snap(newur)] + + def core_contained_by_any(self, shape_list): """ Return if the core of this shape is contained by any shape's core in the diff --git a/compiler/router/router.py b/compiler/router/router.py index c498a815..fe445ae1 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -59,8 +59,6 @@ class router(router_tech): def prepare_gds_reader(self): """ Write the current layout to a temporary file to read the layout. """ - # NOTE: Avoid using this function if possible since it is too slow to - # write/read these files self.design.gds_write(self.gds_filename) self.layout = gdsMill.VlsiLayout(units=GDS["unit"]) self.reader = gdsMill.Gds2reader(self.layout) @@ -111,26 +109,16 @@ class router(router_tech): self.all_pins.update(pin_set) - def find_blockages(self, name="blockage", shape_list=None): + def find_blockages(self, name="blockage"): """ Find all blockages in the routing layers. """ debug.info(2, "Finding blockages...") for lpp in [self.vert_lpp, self.horiz_lpp]: - # If the list of shapes is given, don't get them from gdsMill - if shape_list is None: - shapes = self.layout.getAllShapes(lpp) - else: - shapes = shape_list + shapes = self.layout.getAllShapes(lpp) for boundary in shapes: - if shape_list is not None: - if boundary.lpp != lpp: - continue - ll = boundary.ll() - ur = boundary.ur() - else: - # gdsMill boundaries are in (left, bottom, right, top) order - ll = vector(boundary[0], boundary[1]) - ur = vector(boundary[2], boundary[3]) + # gdsMill boundaries are in (left, bottom, right, top) order + ll = vector(boundary[0], boundary[1]) + ur = vector(boundary[2], boundary[3]) rect = [ll, ur] new_shape = graph_shape(name, rect, lpp) new_shape = self.inflate_shape(new_shape) @@ -144,28 +132,20 @@ class router(router_tech): self.blockages.append(new_shape) - def find_vias(self, shape_list=None): + def find_vias(self): """ Find all vias in the routing layers. """ debug.info(2, "Finding vias...") # Prepare lpp values here from openram.tech import layer via_lpp = layer[self.via_layer_name] - valid_lpp = self.horiz_lpp # Just a temporary lpp to prevent errors + valid_lpp = self.horiz_lpp - # If the list of shapes is given, don't get them from gdsMill - if shape_list is None: - shapes = self.layout.getAllShapes(via_lpp) - else: - shapes = shape_list + shapes = self.layout.getAllShapes(via_lpp) for boundary in shapes: - if shape_list is not None: - ll = boundary.ll() - ur = boundary.ur() - else: - # gdsMill boundaries are in (left, bottom, right, top) order - ll = vector(boundary[0], boundary[1]) - ur = vector(boundary[2], boundary[3]) + # gdsMill boundaries are in (left, bottom, right, top) order + ll = vector(boundary[0], boundary[1]) + ur = vector(boundary[2], boundary[3]) rect = [ll, ur] new_shape = graph_shape("via", rect, valid_lpp) # Skip this via if it's contained by an existing via blockage @@ -284,8 +264,7 @@ class router(router_tech): working for this router. """ - new_wires = [] - new_vias = [] + new_shapes = [] for i in range(0, len(nodes) - 1): start = nodes[i].center end = nodes[i + 1].center @@ -296,16 +275,15 @@ class router(router_tech): offset.y - self.half_wire) if direction == (1, 1): # Via offset = vector(start.x, start.y) - via = self.design.add_via_center(layers=self.layers, - offset=offset) - new_vias.append(via) + self.design.add_via_center(layers=self.layers, + offset=offset) else: # Wire - wire = self.design.add_rect(layer=self.get_layer(start.z), - offset=offset, - width=abs(diff.x) + self.track_wire, - height=abs(diff.y) + self.track_wire) - new_wires.append(wire) - return new_wires, new_vias + shape = self.design.add_rect(layer=self.get_layer(start.z), + offset=offset, + width=abs(diff.x) + self.track_wire, + height=abs(diff.y) + self.track_wire) + new_shapes.append(shape) + return new_shapes def write_debug_gds(self, gds_name, g=None, source=None, target=None): diff --git a/compiler/router/signal_escape_router.py b/compiler/router/signal_escape_router.py index 21cae924..5e4ae66e 100644 --- a/compiler/router/signal_escape_router.py +++ b/compiler/router/signal_escape_router.py @@ -5,7 +5,6 @@ # from openram import debug from openram.base.vector import vector -from openram.base.vector3d import vector3d from openram import OPTS from .graph import graph from .graph_shape import graph_shape @@ -57,82 +56,42 @@ class signal_escape_router(router): for source, target, _ in self.get_route_pairs(pin_names): # Change fake pin's name so the graph will treat it as routable target.name = source.name - # Create the graph - g = graph(self) - g.create_graph(source, target) - # Find the shortest path from source to target - path = g.find_shortest_path() - # If no path is found, throw an error - if path is None: - self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target) - debug.error("Couldn't route from {} to {}.".format(source, target), -1) - # Create the path shapes on layout - new_wires, new_vias = self.add_path(path) - self.new_pins[source.name] = new_wires[-1] - # Find the recently added shapes - self.find_blockages(name, new_wires) - self.find_vias(new_vias) + # This is the routing region scale + scale = 1 + while True: + # Create the graph + g = graph(self) + region = g.create_graph(source, target, scale) + # Find the shortest path from source to target + path = g.find_shortest_path() + # If there is no path found, exponentially try again with a + # larger routing region + if path is None: + rll, rur = region + bll, bur = self.bbox + # Stop scaling the region and throw an error + if rll.x < bll.x and rll.y < bll.y and \ + rur.x > bur.x and rur.y > bur.y: + self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target) + debug.error("Couldn't route from {} to {}.".format(source, target), -1) + # Exponentially scale the region + scale *= 2 + debug.info(0, "Retry routing in larger routing region with scale {}".format(scale)) + continue + # Create the path shapes on layout + new_shapes = self.add_path(path) + self.new_pins[source.name] = new_shapes[0] + # Find the recently added shapes + self.prepare_gds_reader() + self.find_blockages(name) + self.find_vias() + break self.replace_layout_pins() - def get_closest_edge(self, point): - """ Return a point's the closest edge and the edge's axis direction. """ - - ll, ur = self.bbox - - # Snap the pin to the perimeter and break the iteration - ll_diff_x = abs(point.x - ll.x) - ll_diff_y = abs(point.y - ll.y) - ur_diff_x = abs(point.x - ur.x) - ur_diff_y = abs(point.y - ur.y) - min_diff = min(ll_diff_x, ll_diff_y, ur_diff_x, ur_diff_y) - - if min_diff == ll_diff_x: - return "left", True - if min_diff == ll_diff_y: - return "bottom", False - if min_diff == ur_diff_x: - return "right", True - return "top", False - - - def prepare_path(self, path): - """ - Override the `prepare_path` method from the `router` class to prevent - overflows from the SRAM layout area. - """ - - ll, ur = self.bbox - nodes = super().prepare_path(path) - new_nodes = [] - for i in range(len(nodes)): - node = nodes[i] - c = node.center - # Haven't overflown yet - if ll.x < c.x and c.x < ur.x and ll.y < c.y and c.y < ur.y: - new_nodes.append(node) - continue - # Snap the pin to the perimeter and break the iteration - edge, _ = self.get_closest_edge(c) - if edge == "left": - fake_center = vector3d(ll.x + self.half_wire, c.y, c.z) - if edge == "bottom": - fake_center = vector3d(c.x, ll.y + self.half_wire, c.z) - if edge == "right": - fake_center = vector3d(ur.x - self.half_wire, c.y, c.z) - if edge == "top": - fake_center = vector3d(c.x, ur.y - self.half_wire, c.z) - node.center = fake_center - new_nodes.append(node) - break - return new_nodes - - def add_perimeter_fake_pins(self): """ Add the fake pins on the perimeter to where the signals will be routed. - These perimeter fake pins are only used to replace layout pins at the - end of routing. """ ll, ur = self.bbox @@ -173,36 +132,17 @@ class signal_escape_router(router): self.fake_pins.append(pin) - def create_fake_pin(self, pin): - """ Create a fake pin on the perimeter orthogonal to the given pin. """ + def get_closest_perimeter_fake_pin(self, pin): + """ Return the closest fake pin for the given pin. """ - ll, ur = self.bbox - c = pin.center() - - # Find the closest edge - edge, vertical = self.get_closest_edge(c) - - # Keep the fake pin out of the SRAM layout are so that they won't be - # blocked by previous signals if they're on the same orthogonal line - if edge == "left": - fake_center = vector(ll.x - self.track_wire * 2, c.y) - if edge == "bottom": - fake_center = vector(c.x, ll.y - self.track_wire * 2) - if edge == "right": - fake_center = vector(ur.x + self.track_wire * 2, c.y) - if edge == "top": - fake_center = vector(c.x, ur.y + self.track_wire * 2) - - # Create the fake pin shape - layer = self.get_layer(int(not vertical)) - half_wire_vector = vector([self.half_wire] * 2) - nll = fake_center - half_wire_vector - nur = fake_center + half_wire_vector - rect = [nll, nur] - pin = graph_shape(name="fake", - rect=rect, - layer_name_pp=layer) - return pin + min_dist = float("inf") + close_fake = None + for fake in self.fake_pins: + dist = pin.distance(fake) + if dist < min_dist: + min_dist = dist + close_fake = fake + return close_fake def get_route_pairs(self, pin_names): @@ -211,7 +151,7 @@ class signal_escape_router(router): to_route = [] for name in pin_names: pin = next(iter(self.pins[name])) - fake = self.create_fake_pin(pin) + fake = self.get_closest_perimeter_fake_pin(pin) to_route.append((pin, fake, pin.distance(fake))) return sorted(to_route, key=lambda x: x[2]) diff --git a/compiler/router/supply_router.py b/compiler/router/supply_router.py index 3cdcc4e6..f6acd24c 100644 --- a/compiler/router/supply_router.py +++ b/compiler/router/supply_router.py @@ -71,20 +71,35 @@ class supply_router(router): pins = self.pins[pin_name] # Route closest pins according to the minimum spanning tree for source, target in self.get_mst_pairs(list(pins)): - # Create the graph - g = graph(self) - g.create_graph(source, target) - # Find the shortest path from source to target - path = g.find_shortest_path() - # If no path is found, throw an error - if path is None: - self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target) - debug.error("Couldn't route from {} to {}.".format(source, target), -1) - # Create the path shapes on layout - new_wires, new_vias = self.add_path(path) - # Find the recently added shapes - self.find_blockages(pin_name, new_wires) - self.find_vias(new_vias) + # This is the routing region scale + scale = 1 + while True: + # Create the graph + g = graph(self) + region = g.create_graph(source, target, scale) + # Find the shortest path from source to target + path = g.find_shortest_path() + # If there is no path found, exponentially try again with a + # larger routing region + if path is None: + rll, rur = region + bll, bur = self.bbox + # Stop scaling the region and throw an error + if rll.x < bll.x and rll.y < bll.y and \ + rur.x > bur.x and rur.y > bur.y: + self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target) + debug.error("Couldn't route from {} to {}.".format(source, target), -1) + # Exponentially scale the region + scale *= 2 + debug.info(0, "Retry routing in larger routing region with scale {}".format(scale)) + continue + # Create the path shapes on layout + self.add_path(path) + # Find the recently added shapes + self.prepare_gds_reader() + self.find_blockages(pin_name) + self.find_vias() + break def add_side_pin(self, pin_name, side, num_vias=3, num_fake_pins=4): From 066d00f44b13e91b9d169ec1952059c691e75dd9 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Fri, 1 Sep 2023 02:02:41 -0700 Subject: [PATCH 43/48] increase power ring crba width for drc --- compiler/modules/capped_replica_bitcell_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/capped_replica_bitcell_array.py b/compiler/modules/capped_replica_bitcell_array.py index db7a1547..83c4e109 100644 --- a/compiler/modules/capped_replica_bitcell_array.py +++ b/compiler/modules/capped_replica_bitcell_array.py @@ -223,7 +223,7 @@ class capped_replica_bitcell_array(bitcell_base_array): def route_power_ring(self, v_layer, h_layer): self.bbox = (vector(0,0), vector(self.capped_rba_width, self.capped_rba_height)) - self.supply_rail_width = drc["minwidth_m1"] + self.supply_rail_width = drc["minwidth_m3"] self.supply_rail_pitch = 6 * self.supply_rail_width self.add_power_ring(v_layer=v_layer, h_layer=h_layer) From eb82053ab87ba94452fe3559ee92884f0529e2c3 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Sun, 3 Sep 2023 13:16:18 -0700 Subject: [PATCH 44/48] copy router from dev --- compiler/router/graph.py | 6 ++-- compiler/router/graph_shape.py | 11 ------ compiler/router/router.py | 62 +++++++++++++++++++++++----------- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/compiler/router/graph.py b/compiler/router/graph.py index 37bec676..d8017999 100644 --- a/compiler/router/graph.py +++ b/compiler/router/graph.py @@ -185,7 +185,7 @@ class graph: return False - def create_graph(self, source, target, scale=1): + def create_graph(self, source, target): """ Create the graph to run routing on later. """ debug.info(3, "Creating the graph for source '{}' and target'{}'.".format(source, target)) @@ -223,9 +223,6 @@ class graph: debug.info(4, "Number of vias detected in the routing region: {}".format(len(self.graph_vias))) debug.info(4, "Number of nodes in the routing graph: {}".format(len(self.nodes))) - # Return the region to scale later if no path is found - return region.rect - def find_graph_blockages(self, region): """ Find blockages that overlap the routing region. """ @@ -439,6 +436,7 @@ class graph: path.append(current) current = came_from[current.id] path.append(current) + path.reverse() return path # Get the previous node to better calculate the next costs diff --git a/compiler/router/graph_shape.py b/compiler/router/graph_shape.py index 1221dabe..0f950ae4 100644 --- a/compiler/router/graph_shape.py +++ b/compiler/router/graph_shape.py @@ -72,17 +72,6 @@ class graph_shape(pin_layout): return graph_shape(self.name, inflated_area, self.layer, self) - def multiply(self, scale): - """ Multiply the width and height with the scale value. """ - - width = (self.width() * (scale - 1)) / 2 - height = (self.height() * (scale - 1)) / 2 - ll, ur = self.rect - newll = vector(ll.x - width, ll.y - height) - newur = vector(ur.x + width, ur.y + height) - self.rect = [snap(newll), snap(newur)] - - def core_contained_by_any(self, shape_list): """ Return if the core of this shape is contained by any shape's core in the diff --git a/compiler/router/router.py b/compiler/router/router.py index f1d30941..61395f1f 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -59,6 +59,8 @@ class router(router_tech): def prepare_gds_reader(self): """ Write the current layout to a temporary file to read the layout. """ + # NOTE: Avoid using this function if possible since it is too slow to + # write/read these files self.design.gds_write(self.gds_filename) self.layout = gdsMill.VlsiLayout(units=GDS["unit"]) self.reader = gdsMill.Gds2reader(self.layout) @@ -109,16 +111,26 @@ class router(router_tech): self.all_pins.update(pin_set) - def find_blockages(self, name="blockage"): + def find_blockages(self, name="blockage", shape_list=None): """ Find all blockages in the routing layers. """ debug.info(4, "Finding blockages...") for lpp in [self.vert_lpp, self.horiz_lpp]: - shapes = self.layout.getAllShapes(lpp) + # If the list of shapes is given, don't get them from gdsMill + if shape_list is None: + shapes = self.layout.getAllShapes(lpp) + else: + shapes = shape_list for boundary in shapes: - # gdsMill boundaries are in (left, bottom, right, top) order - ll = vector(boundary[0], boundary[1]) - ur = vector(boundary[2], boundary[3]) + if shape_list is not None: + if boundary.lpp != lpp: + continue + ll = boundary.ll() + ur = boundary.ur() + else: + # gdsMill boundaries are in (left, bottom, right, top) order + ll = vector(boundary[0], boundary[1]) + ur = vector(boundary[2], boundary[3]) rect = [ll, ur] new_shape = graph_shape(name, rect, lpp) new_shape = self.inflate_shape(new_shape) @@ -132,20 +144,28 @@ class router(router_tech): self.blockages.append(new_shape) - def find_vias(self): + def find_vias(self, shape_list=None): """ Find all vias in the routing layers. """ debug.info(4, "Finding vias...") # Prepare lpp values here from openram.tech import layer via_lpp = layer[self.via_layer_name] - valid_lpp = self.horiz_lpp + valid_lpp = self.horiz_lpp # Just a temporary lpp to prevent errors - shapes = self.layout.getAllShapes(via_lpp) + # If the list of shapes is given, don't get them from gdsMill + if shape_list is None: + shapes = self.layout.getAllShapes(via_lpp) + else: + shapes = shape_list for boundary in shapes: - # gdsMill boundaries are in (left, bottom, right, top) order - ll = vector(boundary[0], boundary[1]) - ur = vector(boundary[2], boundary[3]) + if shape_list is not None: + ll = boundary.ll() + ur = boundary.ur() + else: + # gdsMill boundaries are in (left, bottom, right, top) order + ll = vector(boundary[0], boundary[1]) + ur = vector(boundary[2], boundary[3]) rect = [ll, ur] new_shape = graph_shape("via", rect, valid_lpp) # Skip this via if it's contained by an existing via blockage @@ -264,7 +284,8 @@ class router(router_tech): working for this router. """ - new_shapes = [] + new_wires = [] + new_vias = [] for i in range(0, len(nodes) - 1): start = nodes[i].center end = nodes[i + 1].center @@ -275,15 +296,16 @@ class router(router_tech): offset.y - self.half_wire) if direction == (1, 1): # Via offset = vector(start.x, start.y) - self.design.add_via_center(layers=self.layers, - offset=offset) + via = self.design.add_via_center(layers=self.layers, + offset=offset) + new_vias.append(via) else: # Wire - shape = self.design.add_rect(layer=self.get_layer(start.z), - offset=offset, - width=abs(diff.x) + self.track_wire, - height=abs(diff.y) + self.track_wire) - new_shapes.append(shape) - return new_shapes + wire = self.design.add_rect(layer=self.get_layer(start.z), + offset=offset, + width=abs(diff.x) + self.track_wire, + height=abs(diff.y) + self.track_wire) + new_wires.append(wire) + return new_wires, new_vias def write_debug_gds(self, gds_name, g=None, source=None, target=None): From e553f3db417620c003a9273880e112b25cb1c2be Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 7 Sep 2023 12:24:39 -0700 Subject: [PATCH 45/48] fix sram.sp spare_wen --- compiler/modules/sram_1bank.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/modules/sram_1bank.py b/compiler/modules/sram_1bank.py index 48e6cf89..97494ea5 100644 --- a/compiler/modules/sram_1bank.py +++ b/compiler/modules/sram_1bank.py @@ -662,7 +662,10 @@ class sram_1bank(design, verilog, lef): inputs = [] outputs = [] for bit in range(self.num_spare_cols): - inputs.append("spare_wen{}[{}]".format(port, bit)) + if self.num_spare_cols == 1: + inputs.append("spare_wen{}".format(port)) + else: + inputs.append("spare_wen{}[{}]".format(port, bit)) outputs.append("bank_spare_wen{}_{}".format(port, bit)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) From 00347987871174c7d4f1231263b6c7e9d522bfa0 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Mon, 11 Sep 2023 11:23:39 -0700 Subject: [PATCH 46/48] both rbl replica array working --- compiler/modules/dummy_array.py | 19 +++++++++++-------- compiler/modules/pattern.py | 1 - compiler/modules/replica_bitcell_array.py | 5 ++++- .../sky130/custom/sky130_dummy_array.py | 7 ++----- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index fbf79cd9..ec9ac24f 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -13,10 +13,13 @@ class dummy_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. """ - def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name=""): - super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) - self.mirror = mirror + def __init__(self, rows, cols, column_offset=0, row_offset=0 ,mirror=0, location="", name=""): + super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) + self.location = location + self.row_offset = row_offset + self.mirror = mirror + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -54,17 +57,17 @@ class dummy_array(bitcell_base_array): self.cell_inst={} if self.cell.mirror.y: core_block = [[0 for x in range(2)] for y in range(2)] - core_block[(0+self.mirror) %2][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) - core_block[(1+self.mirror) %2][0] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') - core_block[(0+self.mirror) %2][1] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True, mirror='MY') - core_block[(1+self.mirror) %2][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True, mirror='XY') + core_block[(0+self.mirror) %2][0] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) + core_block[(1+self.mirror) %2][0] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + core_block[(0+self.mirror) %2][1] = geometry.instance("core_0_1", mod=self.dummy_cell, is_bitcell=True, mirror='MY') + core_block[(1+self.mirror) %2][1] = geometry.instance("core_1_1", mod=self.dummy_cell, is_bitcell=True, mirror='XY') else: core_block = [[0 for x in range(1)] for y in range(2)] core_block[(0+self.mirror) %2][0] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) core_block[(1+self.mirror) %2][0] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size * 2, name_template="bit_r{0}_c{1}") + self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size, name_template="bit_r{0}_c{1}") self.pattern.connect_array() def add_pins(self): diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index c52d6480..5d8ac143 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -133,7 +133,6 @@ class pattern(): continue if((self.bit_rows[col+dc] < self.num_rows) and (self.bit_cols[row+dr] < self.num_cols)): if(inst.is_bitcell): - #print(x_bit, y_bit) self.parent_design.cell_inst[self.bit_rows[col+dc], self.bit_cols[row+dr]] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc)) self.parent_design.all_inst[row + dr, col + dc] = self.parent_design.cell_inst[self.bit_rows[col+dc], self.bit_cols[row+dr]] self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(self.bit_rows[col+dc], self.bit_cols[row+dr])) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index cbfdfbb1..99171875 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -107,16 +107,19 @@ class replica_bitcell_array(bitcell_base_array): for port in self.all_ports: if port in self.left_rbl: row_offset = self.row_size + mirror = row_offset % 2 + 1 elif port in self.right_rbl: row_offset = 0 + mirror = 0 else: continue + self.dummy_rows[port] = factory.create(module_type="dummy_array", cols=self.column_size, rows=1, row_offset=row_offset, column_offset=len(self.left_rbl), - mirror='R0') + mirror=mirror) def add_pins(self): diff --git a/technology/sky130/custom/sky130_dummy_array.py b/technology/sky130/custom/sky130_dummy_array.py index 8fd3e00f..196a4451 100644 --- a/technology/sky130/custom/sky130_dummy_array.py +++ b/technology/sky130/custom/sky130_dummy_array.py @@ -18,11 +18,8 @@ class sky130_dummy_array(dummy_array, sky130_bitcell_base_array): """ Generate a dummy row/column for the replica array. """ - def __init__(self, rows, cols, column_offset=0, row_offset=0 ,mirror=0, location="", name=""): - self.location = location - self.row_offset = row_offset - self.mirror = mirror - super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) + #def __init__(self, rows, cols, column_offset=0, row_offset=0 ,mirror=0, location="", name=""): + # super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) def add_modules(self): """ Add the modules used in this design """ From 2faa067ea6c64a722f78a3548eb164b9d1685d0c Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 12 Sep 2023 12:12:21 -0700 Subject: [PATCH 47/48] add support for col offset to rbc; fix rba mirroring --- compiler/modules/replica_bitcell_array.py | 2 +- compiler/modules/replica_column.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 99171875..d3dcffac 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -87,7 +87,7 @@ class replica_bitcell_array(bitcell_base_array): if port in self.left_rbl: # These go top down starting from the bottom of the bitcell array. replica_bit = self.rbl[0] - port - 1 - column_offset = len(self.left_rbl) + column_offset = 0 elif port in self.right_rbl: # These go bottom up starting from the top of the bitcell array. replica_bit = self.rbl[0] + self.row_size + port - 1 diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 1645442e..37eebc2d 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -104,7 +104,16 @@ class replica_column(bitcell_base_array): core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True) else: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='MX') + current_row += 1 + if self.cell.mirror.y: + print(self.column_offset) + for row in range(self.total_size): + if self.column_offset % 2 == 0: + if core_block[row][0].mirror=='MX': + core_block[row][0].mirror='XY' + else: + core_block[row][0].mirror='MY' self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.total_size, num_cols=self.column_size, name_template="rbc_r{0}_c{1}") self.pattern.connect_array() From d7c3bbea3e84e1ff5b847b4662321a38d50b49cc Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Sat, 28 Oct 2023 18:05:07 -0700 Subject: [PATCH 48/48] crba passing again norbl/leftrbl --- Makefile | 2 +- compiler/modules/pattern.py | 2 +- compiler/modules/replica_bitcell_array.py | 17 ++++++------- compiler/modules/replica_column.py | 3 +-- ...ed_replica_bitcell_array_norbl_1rw_test.py | 2 +- compiler/tests/Makefile | 1 + macros/Makefile | 3 +++ .../sky130/custom/sky130_bitcell_array.py | 5 ++-- .../custom/sky130_bitcell_base_array.py | 2 +- .../custom/sky130_replica_bitcell_array.py | 1 + .../sky130/custom/sky130_replica_column.py | 24 +++++++++---------- .../sky130/custom/sky130_row_cap_array.py | 8 +++---- 12 files changed, 37 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index 12c9eaa7..329dc824 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ SRAM_LIB_GIT_REPO ?= https://github.com/vlsida/sky130_fd_bd_sram.git # Use this for development #SRAM_LIB_GIT_REPO ?= git@github.com:VLSIDA/sky130_fd_bd_sram.git #SRAM_LIB_GIT_REPO ?= https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git -SRAM_LIB_GIT_COMMIT ?= baa2b14282ee6c8498a9e480c88a5096fdce2b06 +SRAM_LIB_GIT_COMMIT ?= 118c1a628c5f8ffb58a8e1a20de2b67c5058345f # Open PDKs OPEN_PDKS_DIR ?= $(PDK_ROOT)/open_pdks diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index 5d8ac143..84a3f232 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -193,7 +193,7 @@ class pattern(): for row in range(self.row_max+1): x = 0 for col in range(self.col_max+1): - inst = self.parent_design.all_inst[row, col] + inst = self.parent_design.all_inst[self.row_max - row, col] self.place_inst(inst, (x, y)) x += inst.width y += inst.height diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index d3dcffac..2004a84c 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -325,14 +325,15 @@ class replica_bitcell_array(bitcell_base_array): # Replica wordlines (go by the row instead of replica column because we may have to add a pin # even though the column is in another local bitcell array) - for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): - for (wl_name, pin_name) in zip(names, self.dummy_rows[0].get_wordline_names()): - pin = inst.get_pin(pin_name) - self.add_layout_pin(text=wl_name, - layer=pin.layer, - offset=pin.ll().scale(0, 1), - width=self.width, - height=pin.height()) + if self.rbl != [0,0]: + for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): + for (wl_name, pin_name) in zip(names, self.dummy_rows[0].get_wordline_names()): + pin = inst.get_pin(pin_name) + self.add_layout_pin(text=wl_name, + layer=pin.layer, + offset=pin.ll().scale(0, 1), + width=self.width, + height=pin.height()) # Main array bl/br for pin_name in self.all_bitline_names: diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 37eebc2d..13d01237 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -100,14 +100,13 @@ class replica_column(bitcell_base_array): else: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MX') else: - if current_row %2 == 0: + if current_row % 2 == 0: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True) else: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='MX') current_row += 1 if self.cell.mirror.y: - print(self.column_offset) for row in range(self.total_size): if self.column_offset % 2 == 0: if core_block[row][0].mirror=='MX': diff --git a/compiler/tests/14_capped_replica_bitcell_array_norbl_1rw_test.py b/compiler/tests/14_capped_replica_bitcell_array_norbl_1rw_test.py index 56080536..2179bc7e 100755 --- a/compiler/tests/14_capped_replica_bitcell_array_norbl_1rw_test.py +++ b/compiler/tests/14_capped_replica_bitcell_array_norbl_1rw_test.py @@ -26,7 +26,7 @@ class capped_replica_bitcell_array_norbl_1rw_test(openram_test): openram.setup_bitcell() debug.info(2, "Testing 7x5 capped replica array for 1rw cell without replica column or dummy row") - a = factory.create(module_type="capped_replica_bitcell_array", cols=7, rows=5, rbl=[0, 0]) + a = factory.create(module_type="capped_replica_bitcell_array", cols=8, rows=6, rbl=[0, 0]) self.local_check(a) openram.end_openram() diff --git a/compiler/tests/Makefile b/compiler/tests/Makefile index 7f1bef57..6453ec81 100644 --- a/compiler/tests/Makefile +++ b/compiler/tests/Makefile @@ -1,3 +1,4 @@ +SHELL := /bin/bash TOP_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))../..) include $(TOP_DIR)/openram.mk diff --git a/macros/Makefile b/macros/Makefile index b50db045..d7000a62 100644 --- a/macros/Makefile +++ b/macros/Makefile @@ -64,6 +64,9 @@ all: | configs example: $(EXAMPLE_STAMPS) .PHONY: example +sp: sky130_sram_1kbyte_1rw_32x256_8 sky130_sram_2kbyte_1rw_32x512_8 sky130_sram_4kbyte_1rw_32x1024_8 sky130_sram_4kbyte_1rw_64x512_8 +.PHONY: sp + sky130: $(SKY130_STAMPS) .PHONY: sky130 diff --git a/technology/sky130/custom/sky130_bitcell_array.py b/technology/sky130/custom/sky130_bitcell_array.py index 09287d68..f39c95e2 100644 --- a/technology/sky130/custom/sky130_bitcell_array.py +++ b/technology/sky130/custom/sky130_bitcell_array.py @@ -20,8 +20,6 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): Assumes bit-lines and word lines are connected by abutment. """ def __init__(self, rows, cols, column_offset=0, name=""): - if rows % 2 == 0: - debug.error("Invalid number of rows {}. number of rows (excluding dummy rows) must be odd to connect to col ends".format(rows), -1) super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) def add_modules(self): @@ -53,7 +51,8 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): pattern.append_row_to_block(bit_block, bit_row_opt1) pattern.append_row_to_block(bit_block, bit_row_opt1a) for row in bit_block: - row = pattern.rotate_list(row, self.column_offset * 2) + row = pattern.rotate_list(row, self.column_offset * 2) + print(bit_block) self.pattern = pattern(self, "bitcell_array", bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="bit_r{0}_c{1}") self.pattern.connect_array() diff --git a/technology/sky130/custom/sky130_bitcell_base_array.py b/technology/sky130/custom/sky130_bitcell_base_array.py index 7ad024ab..292ff39d 100644 --- a/technology/sky130/custom/sky130_bitcell_base_array.py +++ b/technology/sky130/custom/sky130_bitcell_base_array.py @@ -11,7 +11,7 @@ from openram.modules import bitcell_base_array from openram.sram_factory import factory from openram.tech import layer from openram import OPTS - +from openram.modules import pattern class sky130_bitcell_base_array(bitcell_base_array): """ diff --git a/technology/sky130/custom/sky130_replica_bitcell_array.py b/technology/sky130/custom/sky130_replica_bitcell_array.py index 74c9cf76..f6e0639d 100644 --- a/technology/sky130/custom/sky130_replica_bitcell_array.py +++ b/technology/sky130/custom/sky130_replica_bitcell_array.py @@ -27,5 +27,6 @@ class sky130_replica_bitcell_array(replica_bitcell_array, sky130_bitcell_base_ar bitcell (Bl/BR disconnected). """ def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""): + debug.check((cols+ sum(rbl)) % 2==0, "must have an even number of cols including replica cols; you can add a spare col to fix this") super().__init__(rows, cols, rbl, left_rbl, right_rbl, name) diff --git a/technology/sky130/custom/sky130_replica_column.py b/technology/sky130/custom/sky130_replica_column.py index 6a749b2d..cedfaf5f 100644 --- a/technology/sky130/custom/sky130_replica_column.py +++ b/technology/sky130/custom/sky130_replica_column.py @@ -43,27 +43,27 @@ class sky130_replica_column(replica_column, sky130_bitcell_base_array): self.cell_inst={} replica_row_opt1 = [geometry.instance("rep_00_opt1", mod=self.replica_cell, is_bitcell=True, mirror='XY')] \ - + [geometry.instance("rep_01_strap", mod=self.strap_p, is_bitcell=False, mirror='MX')]\ + + [geometry.instance("rep_01_strap_p", mod=self.strap_p, is_bitcell=False, mirror='MX')]\ + [geometry.instance("rep_02_opt1", mod=self.replica_cell, is_bitcell=True, mirror='MX')] \ - + [geometry.instance("rep_03_strap_p", mod=self.strap, is_bitcell=False, mirror='MX')] + + [geometry.instance("rep_03_strap", mod=self.strap, is_bitcell=False, mirror='MX')] replica_row_opt1a = [geometry.instance("rep_10_opt1a", mod=self.replica_cell2, is_bitcell=True, mirror='MY')] \ - + [geometry.instance("rep_11_strapa", mod=self.strap_p, is_bitcell=False)] \ + + [geometry.instance("rep_11_strap_p", mod=self.strap_p, is_bitcell=False)] \ + [geometry.instance("rep_12_opt1a", mod=self.replica_cell2, is_bitcell=True)] \ - + [geometry.instance("rep_13_strapa_p", mod=self.strapa, is_bitcell=False)] + + [geometry.instance("rep_13_strapaa", mod=self.strapa, is_bitcell=False)] dummy_row_opt1 = [geometry.instance("dummy_00_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='XY')] \ - + [geometry.instance("dummy_01_strap", mod=self.strap_p, is_bitcell=False, mirror='MX')]\ + + [geometry.instance("dummy_01_strap_p", mod=self.strap_p, is_bitcell=False, mirror='MX')]\ + [geometry.instance("dummy_02_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='MX')] \ - + [geometry.instance("dummy_03_strap_p", mod=self.strap, is_bitcell=False, mirror='MX')] + + [geometry.instance("dummy_03_strap", mod=self.strap, is_bitcell=False, mirror='MX')] dummy_row_opt1a = [geometry.instance("dummy_10_opt1a", mod=self.dummy_cell2, is_bitcell=True, mirror='MY')] \ - + [geometry.instance("dummy_11_strapa", mod=self.strap_p, is_bitcell=False)] \ + + [geometry.instance("dummy_11_strap_p", mod=self.strap_p, is_bitcell=False)] \ + [geometry.instance("dummy_12_opt1a", mod=self.dummy_cell2, is_bitcell=True)] \ - + [geometry.instance("dummy_13_strapa_p", mod=self.strapa, is_bitcell=False)] + + [geometry.instance("dummy_13_strapa", mod=self.strapa, is_bitcell=False)] bit_block = [] - if self.column_offset % 2: + if self.column_offset % 2 == 0: replica_row_opt1 = replica_row_opt1[0:2] replica_row_opt1a = replica_row_opt1a[0:2] dummy_row_opt1 = dummy_row_opt1[0:2] @@ -73,19 +73,19 @@ class sky130_replica_column(replica_column, sky130_bitcell_base_array): replica_row_opt1a = replica_row_opt1a[2:4] dummy_row_opt1 = dummy_row_opt1[2:4] dummy_row_opt1a = dummy_row_opt1a[2:4] - + print(self.row_start) current_row = self.row_start for row in range(self.total_size): # Regular array cells are replica cells # Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell. # All other cells are dummies if (row == self.replica_bit) or (row >= self.row_start and row < self.row_end): - if current_row % 2 == 0: + if current_row % 2 == 1: pattern.append_row_to_block(bit_block, replica_row_opt1) else: pattern.append_row_to_block(bit_block, replica_row_opt1a) else: - if current_row % 2 == 0: + if current_row % 2 == 1: pattern.append_row_to_block(bit_block, dummy_row_opt1) else: pattern.append_row_to_block(bit_block, dummy_row_opt1a) diff --git a/technology/sky130/custom/sky130_row_cap_array.py b/technology/sky130/custom/sky130_row_cap_array.py index 24e85585..7031e388 100644 --- a/technology/sky130/custom/sky130_row_cap_array.py +++ b/technology/sky130/custom/sky130_row_cap_array.py @@ -56,13 +56,13 @@ class sky130_row_cap_array(row_cap_array, sky130_bitcell_base_array): rowend = geometry.instance("row_cap_rowend", mod=self.rowend, is_bitcell=True, mirror="MX") rowenda = geometry.instance("row_cap_rowenda", mod=self.rowenda, is_bitcell=True) - pattern.append_row_to_block(bit_block, [bottom_corner]) - for row in range(1,self.row_size-1): - if row % 2 == 0: + pattern.append_row_to_block(bit_block, [top_corner]) + for row in range(1, self.row_size-1): + if row % 2 == 1: pattern.append_row_to_block(bit_block, [rowend]) else: pattern.append_row_to_block(bit_block, [rowenda]) - pattern.append_row_to_block(bit_block, [top_corner]) + pattern.append_row_to_block(bit_block, [bottom_corner]) self.pattern = pattern(self, "row_cap_array_" + self.location, bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="row_cap_array" + self.location + "_r{0}_c{1}") self.pattern.connect_array_raw()