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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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() From 4ce6e0538bfab2542aed399e67a1897decb2bbf6 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 6 Mar 2025 02:05:43 -0800 Subject: [PATCH 49/61] fix col_cap array for dummu compatability ...bitcells next --- Makefile | 3 +-- .../modules/capped_replica_bitcell_array.py | 8 +++++-- compiler/modules/col_cap_array.py | 2 +- compiler/modules/pattern.py | 3 --- technology/sky130/custom/sky130_col_cap.py | 21 +++++++++++++------ .../sky130/custom/sky130_col_cap_array.py | 10 +++++---- technology/sky130/tech/tech.py | 4 +++- 7 files changed, 32 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index ff5f5954..eb46bebf 100644 --- a/Makefile +++ b/Makefile @@ -13,8 +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 ?= 8dccd8d8ddb0a9de1b02207b2cd0c0d697807aa9 - +SRAM_LIB_GIT_COMMIT ?= 9bb620d12ae380dfe38f9a68df809f068f7b2a21 SKY130_PDK ?= $(PDK_ROOT)/sky130A GF180_PDK ?= $(PDK_ROOT)/gf180mcuD diff --git a/compiler/modules/capped_replica_bitcell_array.py b/compiler/modules/capped_replica_bitcell_array.py index def6251f..8832bc2f 100644 --- a/compiler/modules/capped_replica_bitcell_array.py +++ b/compiler/modules/capped_replica_bitcell_array.py @@ -87,7 +87,9 @@ class capped_replica_bitcell_array(bitcell_base_array): # dummy column + left replica column(s) column_offset=0, mirror=0, - location="top") + location="top", + left_rbl=self.left_rbl, + right_rbl=self.right_rbl) self.col_cap_bottom = factory.create(module_type=col_cap_module_type, cols=self.column_size + len(self.rbls), @@ -95,7 +97,9 @@ class capped_replica_bitcell_array(bitcell_base_array): # dummy column + left replica column(s) column_offset=0, mirror=0, - location="bottom") + location="bottom", + left_rbl=self.left_rbl, + right_rbl=self.right_rbl) # 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") diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index bc9edc3c..bf0198fc 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -12,7 +12,7 @@ class col_cap_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=""): + def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name="",left_rbl=[],right_rbl=[]): super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.mirror = mirror self.location = location diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index abc8e931..df9ddfb8 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -176,9 +176,6 @@ class pattern(): 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 print_bit_blocck(): - return - def place_inst(self, inst, offset) -> None: x = offset[0] y = offset[1] diff --git a/technology/sky130/custom/sky130_col_cap.py b/technology/sky130/custom/sky130_col_cap.py index 032169ee..c03f35c3 100644 --- a/technology/sky130/custom/sky130_col_cap.py +++ b/technology/sky130/custom/sky130_col_cap.py @@ -8,20 +8,29 @@ from openram import debug from openram.base import design from openram.tech import cell_properties as props - +from openram import OPTS class sky130_col_cap(design): - def __init__(self, version, name=""): + def __init__(self, version, name="",left_rbl=[],right_rbl=[]): + if version == "colend": - cell_name = "sky130_fd_bd_sram__sram_sp_colend" + if OPTS.control_logic == "control_logic_delay" or not right_rbl: + cell_name = "sky130_fd_bd_sram__sram_sp_colend" + else: + cell_name = "sky130_fd_bd_sram__openram_sp_colend_replica" prop = props.col_cap_1port_bitcell + + elif version == "colenda": + if OPTS.control_logic == "control_logic_delay" or not left_rbl: + cell_name = "sky130_fd_bd_sram__sram_sp_colenda" + else: + cell_name = "sky130_fd_bd_sram__openram_sp_colenda_replica" + prop = props.col_cap_1port_bitcell + elif version == "colend_p_cent": cell_name = "sky130_fd_bd_sram__sram_sp_colend_p_cent" prop = props.col_cap_1port_strap_ground - elif version == "colenda": - cell_name = "sky130_fd_bd_sram__sram_sp_colenda" - prop = props.col_cap_1port_bitcell elif version == "colenda_p_cent": cell_name = "sky130_fd_bd_sram__sram_sp_colenda_p_cent" prop = props.col_cap_1port_strap_ground diff --git a/technology/sky130/custom/sky130_col_cap_array.py b/technology/sky130/custom/sky130_col_cap_array.py index 960a87be..5e376f19 100644 --- a/technology/sky130/custom/sky130_col_cap_array.py +++ b/technology/sky130/custom/sky130_col_cap_array.py @@ -18,18 +18,20 @@ 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=column_offset, mirror=mirror, location=location, name=name) + def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name="", left_rbl=[],right_rbl=[]): + self.left_rbl = left_rbl + self.right_rbl = right_rbl + super().__init__(rows, cols, column_offset=column_offset, mirror=mirror, location=location, name=name, left_rbl=left_rbl, right_rbl=right_rbl) self.no_instances = False def add_modules(self): """ Add the modules used in this design """ if self.location == "top": - self.colend1 = factory.create(module_type="col_cap", version="colend") + self.colend1 = factory.create(module_type="col_cap", version="colend",left_rbl=self.left_rbl, right_rbl=self.right_rbl) self.colend2 = factory.create(module_type="col_cap", version="colend_p_cent") self.colend3 = factory.create(module_type="col_cap", version="colend_cent") elif self.location == "bottom": - self.colend1 = factory.create(module_type="col_cap", version="colenda") + self.colend1 = factory.create(module_type="col_cap", version="colenda",left_rbl=self.left_rbl, right_rbl=self.right_rbl) self.colend2 = factory.create(module_type="col_cap", version="colenda_p_cent") self.colend3 = factory.create(module_type="col_cap", version="colenda_cent") diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index 0d23da8a..fe2be437 100755 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -814,7 +814,9 @@ flatglob = ["*_?mos_m*", blackbox_cells = ["sky130_fd_bd_sram__openram_dp_cell", "sky130_fd_bd_sram__openram_dp_cell_dummy", "sky130_fd_bd_sram__openram_dp_cell_replica", - + + "sky130_fd_bd_sram__openram_sp_colend_replica", + "sky130_fd_bd_sram__openram_sp_colenda_replica", "sky130_fd_bd_sram__sram_sp_cell_opt1a", "sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy", "sky130_fd_bd_sram__openram_sp_cell_opt1_dummy", From 5a74605117ab15eadc18376c05a7b3f2498c958f Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Fri, 12 Sep 2025 11:25:03 -0700 Subject: [PATCH 50/61] single port fixes --- Makefile | 2 +- compiler/modules/bitcell_array.py | 2 +- compiler/modules/replica_bitcell_array.py | 4 ++- compiler/tests/golden/sram_2_16_1_sky130.sp | 12 +++---- compiler/tests/sp_files/sram_2_16_1_sky130.sp | 12 +++---- technology/sky130/custom/sky130_bitcell.py | 4 +++ .../sky130/custom/sky130_bitcell_array.py | 34 +++++++++++++++++-- .../sky130/custom/sky130_dummy_array.py | 4 +-- technology/sky130/tech/tech.py | 4 ++- 9 files changed, 57 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index eb46bebf..6974f1e4 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 ?= 9bb620d12ae380dfe38f9a68df809f068f7b2a21 +SRAM_LIB_GIT_COMMIT ?= 32f553d240545574282ac437ff98d2b889bf039f SKY130_PDK ?= $(PDK_ROOT)/sky130A GF180_PDK ?= $(PDK_ROOT)/gf180mcuD diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index db349b8a..6476c71a 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -19,7 +19,7 @@ class bitcell_array(bitcell_base_array): Creates a rows x cols array of memory cells. Assumes bit-lines and word lines are connected by abutment. """ - def __init__(self, rows, cols, column_offset=0, name=""): + def __init__(self, rows, cols, column_offset=0, name="", left_rbl=None, right_rbl=None): super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 79704022..12dad632 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -78,7 +78,9 @@ class replica_bitcell_array(bitcell_base_array): self.bitcell_array = factory.create(module_type="bitcell_array", column_offset=len(self.left_rbl), cols=self.column_size, - rows=self.row_size) + rows=self.row_size, + left_rbl=self.left_rbl, + right_rbl=self.right_rbl) # Replica bitlines self.replica_columns = {} diff --git a/compiler/tests/golden/sram_2_16_1_sky130.sp b/compiler/tests/golden/sram_2_16_1_sky130.sp index d68dde96..0f8dc497 100644 --- a/compiler/tests/golden/sram_2_16_1_sky130.sp +++ b/compiler/tests/golden/sram_2_16_1_sky130.sp @@ -833,8 +833,8 @@ Xrca_top_4 X0 ll WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 X1 ul Q_bar_float VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 X2 BL WL ul VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 -*X3 ur WL ur VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 -*X4 lr WL lr VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +*X3 ur WL ur VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n +*X4 lr WL lr VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n X5 VPWR Q_float lr VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 X6 ur Q_bar_float VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 X7 VGND Q_float ll VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 @@ -977,8 +977,8 @@ Xrca_19 X0 Q_bar WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 X1 Q Q_bar VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 -*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 -*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n +*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n X5 VPWR Q Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 X6 Q Q_bar VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 @@ -989,8 +989,8 @@ X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 X0 Q_bar WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 X1 Q Q_bar VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 -*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 -*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n +*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n X5 VPWR Q Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 X6 Q Q_bar VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 diff --git a/compiler/tests/sp_files/sram_2_16_1_sky130.sp b/compiler/tests/sp_files/sram_2_16_1_sky130.sp index d68dde96..0f8dc497 100644 --- a/compiler/tests/sp_files/sram_2_16_1_sky130.sp +++ b/compiler/tests/sp_files/sram_2_16_1_sky130.sp @@ -833,8 +833,8 @@ Xrca_top_4 X0 ll WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 X1 ul Q_bar_float VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 X2 BL WL ul VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 -*X3 ur WL ur VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 -*X4 lr WL lr VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +*X3 ur WL ur VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n +*X4 lr WL lr VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n X5 VPWR Q_float lr VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 X6 ur Q_bar_float VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 X7 VGND Q_float ll VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 @@ -977,8 +977,8 @@ Xrca_19 X0 Q_bar WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 X1 Q Q_bar VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 -*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 -*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n +*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n X5 VPWR Q Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 X6 Q Q_bar VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 @@ -989,8 +989,8 @@ X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 X0 Q_bar WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 X1 Q Q_bar VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 -*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 -*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n +*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n X5 VPWR Q Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 X6 Q Q_bar VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 diff --git a/technology/sky130/custom/sky130_bitcell.py b/technology/sky130/custom/sky130_bitcell.py index f99c37c8..f861f707 100644 --- a/technology/sky130/custom/sky130_bitcell.py +++ b/technology/sky130/custom/sky130_bitcell.py @@ -23,6 +23,10 @@ class sky130_bitcell(bitcell_base): cell_name = "sky130_fd_bd_sram__sram_sp_cell_opt1" elif version == "opt1a": cell_name = "sky130_fd_bd_sram__sram_sp_cell_opt1a" + elif version == "opt1_noblcon": + cell_name = "sky130_fd_bd_sram__openram_sp_cell_opt1_noblcon" + elif version == "opt1a_noblcon": + cell_name = "sky130_fd_bd_sram__openram_sp_cell_opt1a_noblcon" else: debug.error("Invalid sky130 cell name", -1) diff --git a/technology/sky130/custom/sky130_bitcell_array.py b/technology/sky130/custom/sky130_bitcell_array.py index daf6f899..b6b7a84a 100644 --- a/technology/sky130/custom/sky130_bitcell_array.py +++ b/technology/sky130/custom/sky130_bitcell_array.py @@ -19,14 +19,18 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): Creates a rows x cols array of memory cells. Assumes bit-lines and word lines are connected by abutment. """ - def __init__(self, rows, cols, column_offset=0, name=""): + def __init__(self, rows, cols, column_offset=0, name="",left_rbl=None, right_rbl=None): super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) - + self.left_rbl = left_rbl + self.right_rbl = right_rbl + def add_modules(self): """ Add the modules used in this design """ # Bitcell for port names only self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") self.cella = factory.create(module_type=OPTS.bitcell, version="opt1a") + #self.cell_noblcon = factory.create(module_type=OPTS.bitcell, version="opt1_noblcon") + #self.cella_noblcon = factory.create(module_type=OPTS.bitcell, version="opt1a_noblcon") 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") @@ -36,6 +40,9 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): """ Create the module instances used in this design """ self.all_inst={} self.cell_inst={} + + #self.cell_noblcon_inst = geometry.instance("cell_noblcon_inst", mod=self.cell_noblcon, is_bitcell=True) + #self.cella_noblcon_inst = geometry.instance("cella_noblcon_inst", mod=self.cella_noblcon, is_bitcell=True) 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')]\ @@ -46,11 +53,32 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): + [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) + 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() + + # for i in range(len(self.insts)): + # if self.left_rbl: + # if "r{}".format(self.row_size-1) in self.insts[i].name: + # if self.insts[i].mod == self.cell: + # self.insts[i].mod = self.cell_noblcon_inst.mod + # self.insts[i].gds = self.cell_noblcon_inst.gds + # elif self.insts[i].mod == self.cella: + # self.insts[i].mod = self.cella_noblcon_inst.mod + # self.insts[i].gds = self.cella_noblcon_inst.gds + # if self.right_rbl: + # if "r{}".format("0") in self.insts[i].name: + # if self.insts[i].mod == self.cell: + # self.insts[i].mod = self.cell_noblcon_inst.mod + # self.insts[i].gds = self.cell_noblcon_inst.gds + # elif self.insts[i].mod == self.cella: + # self.insts[i].mod = self.cella_noblcon_inst.mod + # self.insts[i].gds = self.cella_noblcon_inst.gds diff --git a/technology/sky130/custom/sky130_dummy_array.py b/technology/sky130/custom/sky130_dummy_array.py index a95a0b4e..798daaea 100644 --- a/technology/sky130/custom/sky130_dummy_array.py +++ b/technology/sky130/custom/sky130_dummy_array.py @@ -50,9 +50,9 @@ class sky130_dummy_array(dummy_array, sky130_bitcell_base_array): bit_block = [] if(self.row_offset % 2 == 0): - next_row = 0 - else: next_row = 1 + else: + next_row = 0 for i in range(self.row_size): if next_row == 0: diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index fe2be437..40e12903 100755 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -814,7 +814,9 @@ flatglob = ["*_?mos_m*", blackbox_cells = ["sky130_fd_bd_sram__openram_dp_cell", "sky130_fd_bd_sram__openram_dp_cell_dummy", "sky130_fd_bd_sram__openram_dp_cell_replica", - + + "sky130_fd_bd_sram__openram_sp_cell_opt1_noblcon", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_noblcon", "sky130_fd_bd_sram__openram_sp_colend_replica", "sky130_fd_bd_sram__openram_sp_colenda_replica", "sky130_fd_bd_sram__sram_sp_cell_opt1a", From 53d53ec271f287dc049055a3d79e7596c3cabc76 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 14 Jan 2026 12:08:26 -0800 Subject: [PATCH 51/61] checkpoint from tt submission --- compiler/characterizer/simulation.py | 8 +- compiler/globals.py | 2 +- compiler/modules/bitcell_base_array.py | 31 ++++++- .../modules/capped_replica_bitcell_array.py | 45 +++++----- compiler/modules/col_cap_array.py | 3 +- compiler/modules/dummy_array.py | 14 +-- compiler/modules/pattern.py | 2 +- compiler/modules/replica_column.py | 5 +- compiler/sram_config.py | 12 +-- compiler/tests/05_bitcell_array_test.py | 2 +- ...ed_replica_bitcell_array_norbl_1rw_test.py | 2 +- ..._replica_bitcell_array_leftrbl_1rw_test.py | 2 +- compiler/tests/19_single_bank_2mux_test.py | 4 +- install_conda.sh | 17 ++-- macros/sram_configs/sky130_sram_1rw_tiny.py | 12 ++- technology/sky130/custom/sky130_col_cap.py | 14 +-- .../sky130/custom/sky130_row_cap_array.py | 2 +- technology/sky130/tech/tech.py | 90 ++++++++++--------- 18 files changed, 154 insertions(+), 113 deletions(-) diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index 2dec0010..ac91ece2 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -611,10 +611,10 @@ class simulation(): Gets the signal name associated with the bitlines in the bank. """ # FIXME: change to a solution that does not depend on the technology - if OPTS.tech_name == "sky130" and len(self.all_ports) == 1: - cell_mod = factory.create(module_type=OPTS.bitcell, version="opt1") - else: - cell_mod = factory.create(module_type=OPTS.bitcell) + #if OPTS.tech_name == "sky130" and len(self.all_ports) == 1: + # cell_mod = factory.create(module_type=OPTS.bitcell, version="opt1") + #else: + cell_mod = factory.create(module_type=OPTS.bitcell) cell_bl = cell_mod.get_bl_name(port) cell_br = cell_mod.get_br_name(port) diff --git a/compiler/globals.py b/compiler/globals.py index 65bf8567..f64956dc 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -67,7 +67,7 @@ def parse_args(): dest="num_sim_threads"), optparse.make_option("-v", "--verbose", action="count", - dest="verbose_level", + dest="5", help="Increase the verbosity level"), optparse.make_option("-t", "--tech", dest="tech_name", diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index c92857ee..7c42558f 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -158,10 +158,35 @@ class bitcell_base_array(design): height=wl_pin.height()) def route_supplies(self): + + vdd_found = False + gnd_found = False + for inst in self.insts: - for pin_name in ["vdd", "gnd"]: - if pin_name in inst.mod.get_pin_names(): - self.copy_layout_pin(inst, pin_name) + if 'vdd' in inst.mod.get_pin_names(): + vdd_found = True + if 'gnd' in inst.mod.get_pin_names(): + gnd_found = True + + power_name = 'vdd' + ground_name = 'gnd' + if vdd_found is False or gnd_found is False: + from openram.tech import cell_properties + try: + power_name = cell_properties.power_name + except: + pass + try: + ground_name = cell_properties.ground_name + except: + pass + + + for inst in self.insts: + if power_name in inst.mod.get_pin_names(): + self.copy_layout_pin(inst, power_name, new_name='vdd') + if ground_name in inst.mod.get_pin_names(): + self.copy_layout_pin(inst, ground_name , new_name='gnd') def add_layout_pins(self): """ Add the layout pins """ diff --git a/compiler/modules/capped_replica_bitcell_array.py b/compiler/modules/capped_replica_bitcell_array.py index 8832bc2f..f2289663 100644 --- a/compiler/modules/capped_replica_bitcell_array.py +++ b/compiler/modules/capped_replica_bitcell_array.py @@ -107,6 +107,7 @@ class capped_replica_bitcell_array(bitcell_base_array): self.row_cap_left = factory.create(module_type=row_cap_module_type, cols=1, column_offset=0, + row_offset=len(self.left_rbl)+len(self.right_rbl), rows=self.row_size + self.extra_rows, location="left") @@ -117,6 +118,7 @@ class capped_replica_bitcell_array(bitcell_base_array): # + bitcell columns # + right replica column(s) column_offset=len(self.left_rbl) + self.column_size + self.rbl[0], + row_offset=len(self.left_rbl)+len(self.right_rbl), rows=self.row_size + self.extra_rows, location="right") @@ -181,7 +183,7 @@ class capped_replica_bitcell_array(bitcell_base_array): # 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)) + mod=self.col_cap_bottom,)) self.connect_inst(self.bitline_pin_list + ["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)) @@ -217,7 +219,7 @@ class capped_replica_bitcell_array(bitcell_base_array): self.capped_rba_height = self.dummy_col_insts[0].height - #self.route_power_ring(self.supply_stack[2], self.supply_stack[0]) + self.route_power_ring(self.supply_stack[2], self.supply_stack[0]) self.route_supplies() self.route_unused_wordlines() @@ -289,6 +291,7 @@ class capped_replica_bitcell_array(bitcell_base_array): 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: pin = self.replica_bitcell_array_inst.get_pin(pin_name) @@ -308,6 +311,7 @@ class capped_replica_bitcell_array(bitcell_base_array): offset=pin_offset, width=pin_width, height=pin_height) + def route_supplies(self): @@ -322,8 +326,8 @@ class capped_replica_bitcell_array(bitcell_base_array): if top: inst = self.dummy_row_insts[1] - if "vdd" in inst.mod.pins: - array_pins = inst.get_pins("vdd") + 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])]) @@ -331,7 +335,7 @@ class capped_replica_bitcell_array(bitcell_base_array): to_layer = supply_pin.layer, offset = vector(array_pin.center()[0], supply_pin.center()[1])) - array_pins = inst.get_pins("gnd") + 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])]) @@ -340,8 +344,8 @@ class capped_replica_bitcell_array(bitcell_base_array): 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") + 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])]) @@ -349,7 +353,7 @@ class capped_replica_bitcell_array(bitcell_base_array): to_layer = supply_pin.layer, offset = vector(array_pin.center()[0], supply_pin.center()[1])) - array_pins = inst.get_pins("gnd") + 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])]) @@ -358,8 +362,8 @@ class capped_replica_bitcell_array(bitcell_base_array): 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") + if 'vnd' in inst.mod.pins: + array_pins = inst.get_pins('vnd') 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])]) @@ -367,7 +371,7 @@ class capped_replica_bitcell_array(bitcell_base_array): to_layer = supply_pin.layer, offset = vector(supply_pin.center()[0], array_pin.center()[1])) - array_pins = inst.get_pins("gnd") + 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])]) @@ -376,8 +380,8 @@ class capped_replica_bitcell_array(bitcell_base_array): 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") + 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])]) @@ -385,29 +389,30 @@ class capped_replica_bitcell_array(bitcell_base_array): to_layer = supply_pin.layer, offset = vector(supply_pin.center()[0], array_pin.center()[1])) - array_pins = inst.get_pins("gnd") + 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])) + offset = vector(supply_pin.center()[0], array_pin.center()[1])) + 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(): + for wl_name in inst.mod.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) + self.connect_side_pin(pin, "left", self.left_gnd_pin.cx()) + self.connect_side_pin(pin, "right", self.right_gnd_pin.cx()) # Ground the unused replica wordlines for wl_name in self.unused_wordline_names: pin = self.replica_bitcell_array_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) + self.connect_side_pin(pin, "left", self.left_gnd_pin.cx()) + self.connect_side_pin(pin, "right", self.right_gnd_pin.cx()) def route_side_pin(self, name, side, offset_multiple=1): """ diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index bf0198fc..510f7b0c 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_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 col_cap_array(bitcell_base_array): """ diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index e9d652ee..5317c187 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -14,7 +14,7 @@ class dummy_array(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=""): + def __init__(self, rows, cols, column_offset=0, row_offset=0 ,mirror=0, location="", name="", left_rbl=0, right_rbl=0): super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.location = location self.row_offset = row_offset @@ -57,14 +57,14 @@ 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.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') + core_block[(0+self.mirror) %2][(1+self.column_offset+self.row_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + core_block[(1+self.mirror) %2][(1+self.column_offset+self.row_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True) + core_block[(0+self.mirror) %2][(0+self.column_offset+self.row_offset) %2] = geometry.instance("core_0_1", mod=self.dummy_cell, is_bitcell=True, mirror='XY') + core_block[(1+self.mirror) %2][(0+self.column_offset+self.row_offset) %2] = geometry.instance("core_1_1", mod=self.dummy_cell, is_bitcell=True, mirror='MY') 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') + core_block[(0+self.mirror) %2][(1+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + core_block[(1+self.mirror) %2][(1+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True) 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}") diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index df9ddfb8..798ef331 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -187,7 +187,7 @@ class pattern(): inst.place((x, y), inst.mirror, inst.rotate) def place_array(self): - + (self.row_max, self.col_max) = list(self.parent_design.all_inst.keys())[-1] y = 0 for row in range(self.row_max+1): diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 6521fe5b..433ac2ec 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -95,17 +95,18 @@ 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 == 0: + if current_row % 2 == 1: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True) 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 == 1: 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: for row in range(self.total_size): if self.column_offset % 2 == 0: diff --git a/compiler/sram_config.py b/compiler/sram_config.py index 369ae3d0..bdd6b245 100644 --- a/compiler/sram_config.py +++ b/compiler/sram_config.py @@ -141,12 +141,12 @@ class sram_config: + " Bank addr size: {}".format(self.bank_addr_size)) num_ports = OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports - if num_ports == 1: - if ((self.num_cols + num_ports + self.num_spare_cols) % self.array_col_multiple != 0): - debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.num_cols + num_ports + self.num_spare_cols, self.array_col_multiple), -1) - - if ((self.num_rows + num_ports) % self.array_row_multiple != 0): - debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.num_rows + num_ports, self.array_row_multiple), -1) + #if num_ports == 1: + # if ((self.num_cols + num_ports + self.num_spare_cols) % self.array_col_multiple != 0): + # debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.num_cols + num_ports + self.num_spare_cols, self.array_col_multiple), -1)# + # + # if ((self.num_rows + num_ports) % self.array_row_multiple != 0): + # debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.num_rows + num_ports, self.array_row_multiple), -1) def estimate_words_per_row(self, tentative_num_cols, word_size): """ diff --git a/compiler/tests/05_bitcell_array_test.py b/compiler/tests/05_bitcell_array_test.py index bd5d5f01..7a6d3aad 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=2 + num_spare_cols, rows=2 + num_spare_rows) self.local_check(a) openram.end_openram() 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 2a648b89..dad854ed 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/14_replica_bitcell_array_leftrbl_1rw_test.py b/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py index f910c50f..6c1b712c 100755 --- a/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py +++ b/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py @@ -26,7 +26,7 @@ class replica_bitcell_array_leftrbl_1rw_test(openram_test): openram.setup_bitcell() debug.info(2, "Testing 7x5 replica array for 1rw cell with left replica column") - a = factory.create(module_type="replica_bitcell_array", cols=7, rows=5, rbl=[1, 0], left_rbl=[0]) + a = factory.create(module_type="replica_bitcell_array", cols=8, rows=8, rbl=[1, 0], left_rbl=[0]) self.local_check(a) openram.end_openram() diff --git a/compiler/tests/19_single_bank_2mux_test.py b/compiler/tests/19_single_bank_2mux_test.py index 451a253e..6683c9e8 100755 --- a/compiler/tests/19_single_bank_2mux_test.py +++ b/compiler/tests/19_single_bank_2mux_test.py @@ -24,7 +24,9 @@ class single_bank_test(openram_test): from openram import sram_config c = sram_config(word_size=4, - num_words=16) + num_words=16, + num_spare_cols=1, + num_spare_rows=1) c.num_words=32 c.words_per_row=2 diff --git a/install_conda.sh b/install_conda.sh index 6aadd393..d646d624 100755 --- a/install_conda.sh +++ b/install_conda.sh @@ -1,4 +1,7 @@ #!/bin/bash +#CONDA_INSTALLER_URL="https://repo.anaconda.com/miniconda/Miniconda3-py313_25.11.1-1-Linux-x86_64.sh" +#CONDA_INSTALLER_FILE="miniconda_installer_py313.sh" + CONDA_INSTALLER_URL="https://repo.anaconda.com/miniconda/Miniconda3-py38_23.11.0-2-Linux-x86_64.sh" CONDA_INSTALLER_FILE="miniconda_installer_py38.sh" CONDA_HOME="${CONDA_HOME:-miniconda}" @@ -6,12 +9,12 @@ CONDA_HOME="${CONDA_HOME:-miniconda}" # The tool name format is "=". # If you want to use the latest version, just use "". TOOLS="" -TOOLS+="klayout=0.28.3 " -TOOLS+="magic=8.3.497 " +#TOOLS+="klayout=0.28.3 " +TOOLS+="magic=8.3.587 " TOOLS+="netgen=1.5.286 " TOOLS+="ngspice=26 " TOOLS+="trilinos=12.12.1=1 " -TOOLS+="xyce=7.4=3" +TOOLS+="xyce=7.4" # Install miniconda if not already installed if [[ ! -d "${CONDA_HOME}/bin" ]] @@ -24,16 +27,16 @@ then # Prioritize channels to prevent version conflicts conda config --add channels conda-forge conda config --add channels vlsida-eda - - # Install iverilog from conda-eda - conda install -q -y -c litex-hub iverilog - + + #conda install -q -y -c conda-forge trilinos # Install rest of the tools from vlsida-eda for tool in ${TOOLS} do conda install -q -y -c vlsida-eda ${tool} done + # Install iverilog from conda-eda + conda install -q -y -c litex-hub iverilog # Install required Python packages # (This step isn't required but used to prevent possible issues) python3 -m pip install -r requirements.txt --ignore-installed diff --git a/macros/sram_configs/sky130_sram_1rw_tiny.py b/macros/sram_configs/sky130_sram_1rw_tiny.py index 85aa34f4..c4f7ac31 100644 --- a/macros/sram_configs/sky130_sram_1rw_tiny.py +++ b/macros/sram_configs/sky130_sram_1rw_tiny.py @@ -4,12 +4,13 @@ Dual port (1 read/write + 1 read only) 1 kbytes SRAM with byte write. FIXME: What is this useful for? FIXME: Why would you want byte write on this? """ -word_size = 8 # Bits +word_size = 16 # Bits num_words = 16 human_byte_size = "{:.0f}kbytes".format((word_size * num_words)/1024/8) # Allow byte writes -write_size = 2 # Bits +write_size = 8 # Bits +words_per_row = 1 # Dual port num_rw_ports = 1 @@ -17,8 +18,11 @@ num_r_ports = 0 num_w_ports = 0 ports_human = '1rw' -num_spare_cols = 1 -num_spare_rows = 1 +#num_spare_cols = 1 +#num_spare_rows = 1 +netlist_only = True +num_sim_threads = 1 +#analytical_delay = False import os exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) diff --git a/technology/sky130/custom/sky130_col_cap.py b/technology/sky130/custom/sky130_col_cap.py index c03f35c3..3bedfe19 100644 --- a/technology/sky130/custom/sky130_col_cap.py +++ b/technology/sky130/custom/sky130_col_cap.py @@ -15,19 +15,11 @@ class sky130_col_cap(design): def __init__(self, version, name="",left_rbl=[],right_rbl=[]): if version == "colend": - if OPTS.control_logic == "control_logic_delay" or not right_rbl: - cell_name = "sky130_fd_bd_sram__sram_sp_colend" - else: - cell_name = "sky130_fd_bd_sram__openram_sp_colend_replica" + cell_name = "sky130_fd_bd_sram__sram_sp_colend" prop = props.col_cap_1port_bitcell - elif version == "colenda": - if OPTS.control_logic == "control_logic_delay" or not left_rbl: - cell_name = "sky130_fd_bd_sram__sram_sp_colenda" - else: - cell_name = "sky130_fd_bd_sram__openram_sp_colenda_replica" + cell_name = "sky130_fd_bd_sram__sram_sp_colenda" prop = props.col_cap_1port_bitcell - elif version == "colend_p_cent": cell_name = "sky130_fd_bd_sram__sram_sp_colend_p_cent" prop = props.col_cap_1port_strap_ground @@ -41,5 +33,5 @@ class sky130_col_cap(design): cell_name = "sky130_fd_bd_sram__sram_sp_colenda_cent" prop = props.col_cap_1port_strap_power else: - debug.error("Invalid type for col_end", -1) + debug.error("Invalid type for col_end: {}".format(version), -1) super().__init__(name=name, cell_name=cell_name, prop=prop) diff --git a/technology/sky130/custom/sky130_row_cap_array.py b/technology/sky130/custom/sky130_row_cap_array.py index 7031e388..92d97201 100644 --- a/technology/sky130/custom/sky130_row_cap_array.py +++ b/technology/sky130/custom/sky130_row_cap_array.py @@ -17,7 +17,7 @@ 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, location="", name=""): + def __init__(self, rows, cols, column_offset=0, row_offset=0, mirror=0, location="", name=""): super().__init__(rows, cols, column_offset=column_offset, location=location, name=name) self.mirror = mirror self.location = location diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index 40e12903..2a29a88d 100755 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -24,6 +24,7 @@ File containing the process technology parameters for Skywater 130nm. # For example: tech_modules["contact"] = "contact_freepdk45" tech_modules = d.module_type() + # These modules have been hand designed and provided in this repository. tech_modules["nand2_dec"] = "nand2_dec" tech_modules["nand3_dec"] = "nand3_dec" @@ -33,23 +34,24 @@ tech_modules["nand4_dec"] = "nand4_dec" # These are for single port and dual port as a list, # or for both if there is no list, # or only applicable to one if there is no list. -tech_modules["bitcell_1port"] = "sky130_bitcell" -tech_modules["replica_bitcell_1port"] = "sky130_replica_bitcell" -tech_modules["dummy_bitcell_1port"] = "sky130_dummy_bitcell" + +#tech_modules["bitcell_1port"] = "sky130_bitcell" +#tech_modules["replica_bitcell_1port"] = "sky130_replica_bitcell" +#tech_modules["dummy_bitcell_1port"] = "sky130_dummy_bitcell" tech_modules["replica_bitcell_2port"] = "replica_bitcell_2port" tech_modules["dummy_bitcell_2port"] = "dummy_bitcell_2port" tech_modules["bitcell_2port"] = "bitcell_2port" -tech_modules["bitcell_array"] = ["sky130_bitcell_array", "bitcell_array"] -tech_modules["replica_bitcell_array"] = ["sky130_replica_bitcell_array", "replica_bitcell_array"] -tech_modules["capped_replica_bitcell_array"] = ["sky130_capped_replica_bitcell_array", "capped_replica_bitcell_array"] -tech_modules["dummy_array"] = ["sky130_dummy_array", "dummy_array"] +tech_modules["bitcell_array"] = ["bitcell_array", "bitcell_array"] +tech_modules["replica_bitcell_array"] = ["replica_bitcell_array", "replica_bitcell_array"] +tech_modules["capped_replica_bitcell_array"] = ["capped_replica_bitcell_array", "capped_replica_bitcell_array"] +tech_modules["dummy_array"] = ["dummy_array", "dummy_array"] -tech_modules["replica_column"] = ["sky130_replica_column", "replica_column"] +tech_modules["replica_column"] = ["replica_column", "replica_column"] -tech_modules["col_cap_array"] = ["sky130_col_cap_array", "col_cap_array"] -tech_modules["col_cap"] = ["sky130_col_cap", "col_cap_bitcell_2port"] +tech_modules["col_cap_array"] = ["col_cap_array", "col_cap_array"] +tech_modules["col_cap"] = ["col_cap_bitcell_1port", "col_cap_bitcell_2port"] tech_modules["corner"] = ["sky130_corner", None] tech_modules["internal"] = ["sky130_internal", None] tech_modules["row_cap_array"] = ["sky130_row_cap_array", "row_cap_array"] @@ -68,28 +70,32 @@ tech_modules["and4_dec"] = "and4_dec" ################################################### cell_properties = d.cell_properties() -cell_properties.bitcell_power_pin_directions = ("H", "H") +cell_properties.power_name = 'VPWR' +cell_properties.ground_name = 'VGND' + +cell_properties.bitcell_power_pin_directions = ("V", "V") cell_properties.bitcell_1port.mirror.x = True cell_properties.bitcell_1port.mirror.y = True -cell_properties.bitcell_1port.end_caps = True -cell_properties.bitcell_1port.boundary_layer = "mem" -cell_properties.bitcell_1port.port_order = ['bl', 'br', 'gnd', 'vdd', 'vpb', 'vnb', 'wl'] -cell_properties.bitcell_1port.port_types = ["OUTPUT", "OUTPUT", "GROUND", "POWER", "BIAS", "BIAS", "INPUT"] +cell_properties.bitcell_1port.end_caps = False +cell_properties.bitcell_1port.boundary_layer = "boundary" +cell_properties.bitcell_1port.port_order = ['bl', 'br', 'wl', 'vdd', 'gnd'] +cell_properties.bitcell_1port.port_types = ["INPUT", "INPUT", "GROUND", "POWER", "OUTPUT"] cell_properties.bitcell_1port.port_map = {'bl': 'BL', 'br': 'BR', - 'wl': 'WL', + 'gnd': 'VGND', 'vdd': 'VPWR', - 'vnb': 'VNB', - 'vpb': 'VPB', - 'gnd': 'VGND'} + 'wl': 'WL'} + + + cell_properties.bitcell_1port.wl_layer = "m2" cell_properties.bitcell_1port.bl_layer = "m1" cell_properties.bitcell_1port.vdd_layer = "m1" cell_properties.bitcell_1port.vdd_dir = "V" -cell_properties.bitcell_1port.gnd_layer = "m2" -cell_properties.bitcell_1port.gnd_dir = "H" +cell_properties.bitcell_1port.gnd_layer = "m1" +cell_properties.bitcell_1port.gnd_dir = "V" cell_properties.bitcell_2port.mirror.x = True cell_properties.bitcell_2port.mirror.y = True @@ -103,47 +109,45 @@ cell_properties.bitcell_2port.port_map = {'bl0': 'BL0', 'wl1': 'WL1', 'vdd': 'VDD', 'gnd': 'GND'} + cell_properties.bitcell_1port.wl_layer = "m2" -cell_properties.bitcell_1port.vdd_layer = "m2" -cell_properties.bitcell_1port.vdd_dir = "H" -cell_properties.bitcell_1port.gnd_layer = "m2" -cell_properties.bitcell_1port.gnd_dir = "H" +cell_properties.bitcell_1port.vdd_layer = "m1" +cell_properties.bitcell_1port.vdd_dir = "V" +cell_properties.bitcell_1port.gnd_layer = "m1" +cell_properties.bitcell_1port.gnd_dir = "V" cell_properties.bitcell_2port.wl_layer = "m2" cell_properties.bitcell_2port.vdd_layer = "m1" cell_properties.bitcell_2port.vdd_dir = "H" cell_properties.bitcell_2port.gnd_layer = "m2" cell_properties.bitcell_2port.gnd_dir = "H" -cell_properties.col_cap_1port_bitcell = d.cell(['bl', 'br', 'vdd', 'gnd', 'vpb', 'vnb', 'gate'], - ['INPUT', 'INPUT','POWER', 'GROUND', 'BIAS', 'BIAS', 'INPUT'], +cell_properties.col_cap_1port_bitcell = d.cell(['bl', 'br', 'vdd', 'gnd',], + ['INPUT', 'INPUT','POWER', 'GROUND', ], {'bl': 'bl', 'br': 'br', 'vdd': 'vdd', - 'gnd': 'gnd', - 'vnb': 'vnb', - 'vpb': 'vpb', - 'gate': 'gate'}) -cell_properties.col_cap_1port_bitcell.boundary_layer = "mem" + 'gnd': 'gnd',}) +cell_properties.col_cap_1port_bitcell.boundary_layer = "boundary" cell_properties.col_cap_1port_strap_power = d.cell(['vdd', 'vpb', 'vnb'], ['POWER', 'BIAS', 'BIAS'], {'vnb': 'VNB', 'vpb': 'VPB', 'vdd': 'VPWR'}) -cell_properties.col_cap_1port_strap_power.boundary_layer = "mem" +cell_properties.col_cap_1port_strap_power.boundary_layer = "boundary" cell_properties.col_cap_1port_strap_ground = d.cell(['gnd', 'vpb', 'vnb'], ['GROUND', 'BIAS', 'BIAS'], {'vnb': 'VNB', 'vpb': 'VPB', 'gnd': 'VGND'}) -cell_properties.col_cap_1port_strap_ground.boundary_layer = "mem" +cell_properties.col_cap_1port_strap_ground.boundary_layer = "boundary" cell_properties.row_cap_1port_cell = d.cell(['vdd', 'wl'], ['POWER', 'INPUT'], {'wl': 'WL', 'vdd': 'VPWR'}) -cell_properties.row_cap_1port_cell.boundary_layer = "mem" +cell_properties.row_cap_1port_cell.boundary_layer = "boundary" cell_properties.col_cap_2port.port_order = ['bl0', 'br0', 'bl1', 'br1', 'vdd'] cell_properties.col_cap_2port.port_map = {'bl0': 'BL0', @@ -163,7 +167,7 @@ cell_properties.ptx.model_is_subckt = True cell_properties.pgate.add_implants = True -cell_properties.use_strap = True +cell_properties.use_strap = False cell_properties.strap_module = "internal" cell_properties.strap_version = "wlstrap" @@ -219,9 +223,13 @@ cell_properties.write_driver.port_map = {'din': 'DIN', # If it is a list, the first is single port and the second is dual port. # If it is string, it is used for both single and dual port. cell_properties.names["dff"] = "sky130_fd_bd_sram__openram_dff" -cell_properties.names["nand2_dec"] = ["sky130_fd_bd_sram__openram_sp_nand2_dec", "sky130_fd_bd_sram__openram_dp_nand2_dec"] -cell_properties.names["nand3_dec"] = ["sky130_fd_bd_sram__openram_sp_nand3_dec", "sky130_fd_bd_sram__openram_dp_nand3_dec"] -cell_properties.names["nand4_dec"] = ["sky130_fd_bd_sram__openram_sp_nand4_dec", "sky130_fd_bd_sram__openram_dp_nand4_dec"] +cell_properties.names["nand2_dec"] = ["sky130_fd_bd_sram__openram_dp_nand2_dec", "sky130_fd_bd_sram__openram_dp_nand2_dec"] +cell_properties.names["nand3_dec"] = ["sky130_fd_bd_sram__openram_dp_nand3_dec", "sky130_fd_bd_sram__openram_dp_nand3_dec"] +cell_properties.names["nand4_dec"] = ["sky130_fd_bd_sram__openram_dp_nand4_dec", "sky130_fd_bd_sram__openram_dp_nand4_dec"] + +cell_properties.names["bitcell_1port"] = "sky130_custom_cell" +cell_properties.names["replica_bitcell_1port"] = "sky130_custom_replica" +cell_properties.names["dummy_bitcell_1port"] = "sky130_custom_dummy" cell_properties.names["bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell" cell_properties.names["dummy_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_dummy" @@ -253,7 +261,7 @@ layer_properties.hierarchical_predecode.input_layer = "li" layer_properties.hierarchical_predecode.output_layer = "m2" layer_properties.hierarchical_predecode.vertical_supply = True layer_properties.hierarchical_predecode.force_horizontal_input_contact = True - + layer_properties.bank.stack = "m2_stack" layer_properties.bank.pitch = "m3_pitch" @@ -265,7 +273,7 @@ layer_properties.port_address.supply_offset = True layer_properties.port_data.enable_layer = "m1" layer_properties.port_data.channel_route_bitlines = False -layer_properties.replica_column.even_rows = True +layer_properties.replica_column.even_rows = False layer_properties.wordline_driver.vertical_supply = True From ffcbd51019f700b6cb187520f47733da6b2e7a82 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 17 Mar 2026 11:44:20 -0700 Subject: [PATCH 52/61] technology switching working --- compiler/globals.py | 3 + compiler/modules/dummy_array.py | 12 +- compiler/modules/replica_bitcell_array.py | 4 +- ..._replica_bitcell_array_leftrbl_1rw_test.py | 3 +- compiler/tests/Makefile | 4 + install_conda.sh | 2 +- macros/sram_configs/sky130_sram_1rw_tiny.py | 6 +- .../sky130_capped_replica_bitcell_array.py | 6 +- technology/sky130/tech/tech.py | 853 +----------------- 9 files changed, 35 insertions(+), 858 deletions(-) diff --git a/compiler/globals.py b/compiler/globals.py index f64956dc..1c7d86f7 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -72,6 +72,9 @@ def parse_args(): optparse.make_option("-t", "--tech", dest="tech_name", help="Technology name"), + optparse.make_option("-f", "--tech_file", + dest="tech_file", + help="Technology name"), optparse.make_option("-s", "--spice", dest="spice_name", help="Spice simulator executable name"), diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 5317c187..01472e1d 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -57,14 +57,14 @@ 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][(1+self.column_offset+self.row_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - core_block[(1+self.mirror) %2][(1+self.column_offset+self.row_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True) - core_block[(0+self.mirror) %2][(0+self.column_offset+self.row_offset) %2] = geometry.instance("core_0_1", mod=self.dummy_cell, is_bitcell=True, mirror='XY') - core_block[(1+self.mirror) %2][(0+self.column_offset+self.row_offset) %2] = geometry.instance("core_1_1", mod=self.dummy_cell, is_bitcell=True, mirror='MY') + core_block[(0+self.mirror) %2][(0+self.column_offset+self.row_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + core_block[(1+self.mirror) %2][(0+self.column_offset+self.row_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True) + core_block[(0+self.mirror) %2][(1+self.column_offset+self.row_offset) %2] = geometry.instance("core_0_1", mod=self.dummy_cell, is_bitcell=True, mirror='XY') + core_block[(1+self.mirror) %2][(1+self.column_offset+self.row_offset) %2] = geometry.instance("core_1_1", mod=self.dummy_cell, is_bitcell=True, mirror='MY') else: core_block = [[0 for x in range(1)] for y in range(2)] - core_block[(0+self.mirror) %2][(1+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - core_block[(1+self.mirror) %2][(1+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True) + core_block[(1+self.mirror) %2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + core_block[(0+self.mirror) %2][(0+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True) 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}") diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 12dad632..7262e023 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -111,10 +111,10 @@ 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 + mirror = row_offset % 2 + 1 elif port in self.right_rbl: row_offset = 0 - mirror = 0 + mirror = 0 else: continue diff --git a/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py b/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py index 6c1b712c..b8abb4d1 100755 --- a/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py +++ b/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_test.py @@ -23,10 +23,11 @@ class replica_bitcell_array_leftrbl_1rw_test(openram_test): OPTS.num_rw_ports = 1 OPTS.num_r_ports = 0 OPTS.num_w_ports = 0 + openram.setup_bitcell() debug.info(2, "Testing 7x5 replica array for 1rw cell with left replica column") - a = factory.create(module_type="replica_bitcell_array", cols=8, rows=8, rbl=[1, 0], left_rbl=[0]) + a = factory.create(module_type="replica_bitcell_array", cols=7, rows=5, rbl=[1, 0], left_rbl=[0]) self.local_check(a) openram.end_openram() diff --git a/compiler/tests/Makefile b/compiler/tests/Makefile index fc27a521..c9ea9454 100644 --- a/compiler/tests/Makefile +++ b/compiler/tests/Makefile @@ -176,7 +176,11 @@ $(TEST_BASES): @rm -rf results/$* @rm -rf results/$*.* @mkdir -p results/$*/tmp +ifdef TECH_FILE + @sh -c "python3 -u $(OPENRAM_DIR)/$(getfile).py -t $(gettech) -k -f $(TECH_FILE) -v $(ARGS) -p $(OPENRAM_DIR)/results/$* > $(OPENRAM_DIR)/results/$*.out 2>&1 && touch $(OPENRAM_DIR)/results/$*.ok || touch $(OPENRAM_DIR)/results/$*.bad" +else @sh -c "python3 -u $(OPENRAM_DIR)/$(getfile).py -t $(gettech) -k -v $(ARGS) -p $(OPENRAM_DIR)/results/$* > $(OPENRAM_DIR)/results/$*.out 2>&1 && touch $(OPENRAM_DIR)/results/$*.ok || touch $(OPENRAM_DIR)/results/$*.bad" +endif ifdef KEEP @test -f $(TOP_DIR)/compiler/tests/results/$*.ok && echo "$* ... PASS!" || echo "$* ... FAIL!" else diff --git a/install_conda.sh b/install_conda.sh index d646d624..63c5fd75 100755 --- a/install_conda.sh +++ b/install_conda.sh @@ -9,7 +9,7 @@ CONDA_HOME="${CONDA_HOME:-miniconda}" # The tool name format is "=". # If you want to use the latest version, just use "". TOOLS="" -#TOOLS+="klayout=0.28.3 " +TOOLS+="klayout=0.28.3 " TOOLS+="magic=8.3.587 " TOOLS+="netgen=1.5.286 " TOOLS+="ngspice=26 " diff --git a/macros/sram_configs/sky130_sram_1rw_tiny.py b/macros/sram_configs/sky130_sram_1rw_tiny.py index c4f7ac31..99781e1a 100644 --- a/macros/sram_configs/sky130_sram_1rw_tiny.py +++ b/macros/sram_configs/sky130_sram_1rw_tiny.py @@ -18,9 +18,9 @@ num_r_ports = 0 num_w_ports = 0 ports_human = '1rw' -#num_spare_cols = 1 -#num_spare_rows = 1 -netlist_only = True +num_spare_cols = 1 +num_spare_rows = 1 +#netlist_only = True num_sim_threads = 1 #analytical_delay = False diff --git a/technology/sky130/custom/sky130_capped_replica_bitcell_array.py b/technology/sky130/custom/sky130_capped_replica_bitcell_array.py index b19069cb..04d9af8d 100644 --- a/technology/sky130/custom/sky130_capped_replica_bitcell_array.py +++ b/technology/sky130/custom/sky130_capped_replica_bitcell_array.py @@ -25,7 +25,11 @@ 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 route_power_ring(self, v_layer, h_layer): + # ring is manually added and routed in add_layout_pins + pass + def add_layout_pins(self): """ Add the layout pins """ diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index 2a29a88d..3aa4659f 100755 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -5,850 +5,15 @@ # All rights reserved. # +import sys +from openram import OPTS -import os -from openram import drc as d +import os +dir_path = os.path.dirname(os.path.realpath(__file__)) -""" -File containing the process technology parameters for Skywater 130nm. -""" +sys.path.append("{}/{}".format(dir_path,'tech_configs')) -################################################### -# Custom modules -################################################### - -# This uses the default classes to instantiate module from -# '$OPENRAM_HOME/compiler/modules'. -# Using tech_modules['cellname'] you can override each class by providing a custom -# implementation in '$OPENRAM_TECHDIR/modules/' -# For example: tech_modules["contact"] = "contact_freepdk45" -tech_modules = d.module_type() - - -# These modules have been hand designed and provided in this repository. -tech_modules["nand2_dec"] = "nand2_dec" -tech_modules["nand3_dec"] = "nand3_dec" -tech_modules["nand4_dec"] = "nand4_dec" - -# Override default OpenRAM modules to sky130 modules -# These are for single port and dual port as a list, -# or for both if there is no list, -# or only applicable to one if there is no list. - -#tech_modules["bitcell_1port"] = "sky130_bitcell" -#tech_modules["replica_bitcell_1port"] = "sky130_replica_bitcell" -#tech_modules["dummy_bitcell_1port"] = "sky130_dummy_bitcell" - -tech_modules["replica_bitcell_2port"] = "replica_bitcell_2port" -tech_modules["dummy_bitcell_2port"] = "dummy_bitcell_2port" -tech_modules["bitcell_2port"] = "bitcell_2port" - -tech_modules["bitcell_array"] = ["bitcell_array", "bitcell_array"] -tech_modules["replica_bitcell_array"] = ["replica_bitcell_array", "replica_bitcell_array"] -tech_modules["capped_replica_bitcell_array"] = ["capped_replica_bitcell_array", "capped_replica_bitcell_array"] -tech_modules["dummy_array"] = ["dummy_array", "dummy_array"] - -tech_modules["replica_column"] = ["replica_column", "replica_column"] - -tech_modules["col_cap_array"] = ["col_cap_array", "col_cap_array"] -tech_modules["col_cap"] = ["col_cap_bitcell_1port", "col_cap_bitcell_2port"] -tech_modules["corner"] = ["sky130_corner", None] -tech_modules["internal"] = ["sky130_internal", None] -tech_modules["row_cap_array"] = ["sky130_row_cap_array", "row_cap_array"] -tech_modules["row_cap"] = ["sky130_row_cap", "row_cap_bitcell_2port"] - -# These modules are auto-generated from the nand decoders above and are not -# found in this. -tech_modules["buf_dec"] = "pbuf_dec" -tech_modules["inv_dec"] = "pinv_dec" -tech_modules["and2_dec"] = "and2_dec" -tech_modules["and3_dec"] = "and3_dec" -tech_modules["and4_dec"] = "and4_dec" - -################################################### -# Custom cell properties -################################################### -cell_properties = d.cell_properties() - -cell_properties.power_name = 'VPWR' -cell_properties.ground_name = 'VGND' - -cell_properties.bitcell_power_pin_directions = ("V", "V") - -cell_properties.bitcell_1port.mirror.x = True -cell_properties.bitcell_1port.mirror.y = True -cell_properties.bitcell_1port.end_caps = False -cell_properties.bitcell_1port.boundary_layer = "boundary" -cell_properties.bitcell_1port.port_order = ['bl', 'br', 'wl', 'vdd', 'gnd'] -cell_properties.bitcell_1port.port_types = ["INPUT", "INPUT", "GROUND", "POWER", "OUTPUT"] -cell_properties.bitcell_1port.port_map = {'bl': 'BL', - 'br': 'BR', - 'gnd': 'VGND', - 'vdd': 'VPWR', - 'wl': 'WL'} - - - - -cell_properties.bitcell_1port.wl_layer = "m2" -cell_properties.bitcell_1port.bl_layer = "m1" -cell_properties.bitcell_1port.vdd_layer = "m1" -cell_properties.bitcell_1port.vdd_dir = "V" -cell_properties.bitcell_1port.gnd_layer = "m1" -cell_properties.bitcell_1port.gnd_dir = "V" - -cell_properties.bitcell_2port.mirror.x = True -cell_properties.bitcell_2port.mirror.y = True -cell_properties.bitcell_2port.end_caps = True -cell_properties.bitcell_2port.port_order = ['bl0', 'br0', 'bl1', 'br1', 'wl0', 'wl1', 'vdd', 'gnd'] -cell_properties.bitcell_2port.port_map = {'bl0': 'BL0', - 'br0': 'BR0', - 'bl1': 'BL1', - 'br1': 'BR1', - 'wl0': 'WL0', - 'wl1': 'WL1', - 'vdd': 'VDD', - 'gnd': 'GND'} - -cell_properties.bitcell_1port.wl_layer = "m2" -cell_properties.bitcell_1port.vdd_layer = "m1" -cell_properties.bitcell_1port.vdd_dir = "V" -cell_properties.bitcell_1port.gnd_layer = "m1" -cell_properties.bitcell_1port.gnd_dir = "V" -cell_properties.bitcell_2port.wl_layer = "m2" -cell_properties.bitcell_2port.vdd_layer = "m1" -cell_properties.bitcell_2port.vdd_dir = "H" -cell_properties.bitcell_2port.gnd_layer = "m2" -cell_properties.bitcell_2port.gnd_dir = "H" - -cell_properties.col_cap_1port_bitcell = d.cell(['bl', 'br', 'vdd', 'gnd',], - ['INPUT', 'INPUT','POWER', 'GROUND', ], - {'bl': 'bl', - 'br': 'br', - 'vdd': 'vdd', - 'gnd': 'gnd',}) -cell_properties.col_cap_1port_bitcell.boundary_layer = "boundary" - -cell_properties.col_cap_1port_strap_power = d.cell(['vdd', 'vpb', 'vnb'], - ['POWER', 'BIAS', 'BIAS'], - {'vnb': 'VNB', - 'vpb': 'VPB', - 'vdd': 'VPWR'}) -cell_properties.col_cap_1port_strap_power.boundary_layer = "boundary" - -cell_properties.col_cap_1port_strap_ground = d.cell(['gnd', 'vpb', 'vnb'], - ['GROUND', 'BIAS', 'BIAS'], - {'vnb': 'VNB', - 'vpb': 'VPB', - 'gnd': 'VGND'}) -cell_properties.col_cap_1port_strap_ground.boundary_layer = "boundary" - -cell_properties.row_cap_1port_cell = d.cell(['vdd', 'wl'], - ['POWER', 'INPUT'], - {'wl': 'WL', - 'vdd': 'VPWR'}) -cell_properties.row_cap_1port_cell.boundary_layer = "boundary" - -cell_properties.col_cap_2port.port_order = ['bl0', 'br0', 'bl1', 'br1', 'vdd'] -cell_properties.col_cap_2port.port_map = {'bl0': 'BL0', - 'br0': 'BR0', - 'bl1': 'BL1', - 'br1': 'BR1', - 'vdd': 'VDD'} - -cell_properties.row_cap_2port.port_order = ['wl0', 'wl1', 'gnd'] -cell_properties.row_cap_2port.port_map = {'wl0': 'WL0', - 'wl1': 'WL1', - 'gnd': 'GND'} - - -cell_properties.ptx.bin_spice_models = True -cell_properties.ptx.model_is_subckt = True - -cell_properties.pgate.add_implants = True - -cell_properties.use_strap = False -cell_properties.strap_module = "internal" -cell_properties.strap_version = "wlstrap" - -cell_properties.dff.port_order = ['D', 'Q', 'clk', 'vdd', 'gnd'] -cell_properties.dff.port_map = {'D': 'D', - 'Q': 'Q', - 'clk': 'CLK', - 'vdd': 'VDD', - 'gnd': 'GND'} - -cell_properties.nand2_dec.port_order = ['A', 'B', 'Z', 'vdd', 'gnd'] -cell_properties.nand2_dec.port_map = {'A': 'A', - 'B': 'B', - 'Z': 'Z', - 'vdd': 'VDD', - 'gnd': 'GND'} - -cell_properties.nand3_dec.port_order = ['A', 'B', 'C', 'Z', 'vdd', 'gnd'] -cell_properties.nand3_dec.port_map = {'A': 'A', - 'B': 'B', - 'C': 'C', - 'Z': 'Z', - 'vdd': 'VDD', - 'gnd': 'GND'} - -cell_properties.nand4_dec.port_order = ['A', 'B', 'C', 'D', 'Z', 'vdd', 'gnd'] -cell_properties.nand4_dec.port_map = {'A': 'A', - 'B': 'B', - 'C': 'C', - 'D': 'D', - 'Z': 'Z', - 'vdd': 'VDD', - 'gnd': 'GND'} - -cell_properties.sense_amp.port_order = ['bl', 'br', 'dout', 'en', 'vdd', 'gnd'] -cell_properties.sense_amp.port_map = {'bl': 'BL', - 'br': 'BR', - 'dout': 'DOUT', - 'en': 'EN', - 'vdd': 'VDD', - 'gnd': 'GND'} - -cell_properties.write_driver.port_order = ['din', 'bl', 'br', 'en', 'vdd', 'gnd'] -cell_properties.write_driver.port_map = {'din': 'DIN', - 'bl': 'BL', - 'br': 'BR', - 'en': 'EN', - 'vdd': 'VDD', - 'gnd': 'GND'} - - -# You can override the GDS for custom cell using the following: -# If it is a list, the first is single port and the second is dual port. -# If it is string, it is used for both single and dual port. -cell_properties.names["dff"] = "sky130_fd_bd_sram__openram_dff" -cell_properties.names["nand2_dec"] = ["sky130_fd_bd_sram__openram_dp_nand2_dec", "sky130_fd_bd_sram__openram_dp_nand2_dec"] -cell_properties.names["nand3_dec"] = ["sky130_fd_bd_sram__openram_dp_nand3_dec", "sky130_fd_bd_sram__openram_dp_nand3_dec"] -cell_properties.names["nand4_dec"] = ["sky130_fd_bd_sram__openram_dp_nand4_dec", "sky130_fd_bd_sram__openram_dp_nand4_dec"] - -cell_properties.names["bitcell_1port"] = "sky130_custom_cell" -cell_properties.names["replica_bitcell_1port"] = "sky130_custom_replica" -cell_properties.names["dummy_bitcell_1port"] = "sky130_custom_dummy" - -cell_properties.names["bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell" -cell_properties.names["dummy_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_dummy" -cell_properties.names["replica_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_replica" -cell_properties.names["col_cap_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_cap_col" -cell_properties.names["row_cap_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_cap_row" -cell_properties.names["sense_amp"] = "sky130_fd_bd_sram__openram_sense_amp" -cell_properties.names["write_driver"] = "sky130_fd_bd_sram__openram_write_driver" - -array_row_multiple = 2 -array_col_multiple = 2 - -################################################### -# Custom layer properties -################################################### -layer_properties = d.layer_properties() -layer_properties.hierarchical_decoder.bus_layer = "m1" -layer_properties.hierarchical_decoder.bus_directions = "nonpref" -layer_properties.hierarchical_decoder.input_layer = "li" -layer_properties.hierarchical_decoder.output_layer = "m2" -layer_properties.hierarchical_decoder.vertical_supply = True - -layer_properties.hierarchical_predecode.bus_layer = "m1" -layer_properties.hierarchical_predecode.bus_directions = "nonpref" -# This is added to allow the column decoder connections on m2 -layer_properties.hierarchical_predecode.bus_pitch_factor = 1.2 -layer_properties.hierarchical_predecode.bus_space_factor = 1.5 -layer_properties.hierarchical_predecode.input_layer = "li" -layer_properties.hierarchical_predecode.output_layer = "m2" -layer_properties.hierarchical_predecode.vertical_supply = True -layer_properties.hierarchical_predecode.force_horizontal_input_contact = True - -layer_properties.bank.stack = "m2_stack" -layer_properties.bank.pitch = "m3_pitch" - -layer_properties.column_mux_array.select_layer = "m3" -layer_properties.column_mux_array.bitline_layer = "m1" - -layer_properties.port_address.supply_offset = True - -layer_properties.port_data.enable_layer = "m1" -layer_properties.port_data.channel_route_bitlines = False - -layer_properties.replica_column.even_rows = False - -layer_properties.wordline_driver.vertical_supply = True - -layer_properties.global_wordline_layer = "m5" - - -################################################### -# Discrete tx bins -################################################### -# enforce that tx sizes are within 25% of requested size after fingering. -accuracy_requirement = 0.75 -nmos_bins = { - 0.15 : [0.36, 0.39, 0.42, 0.52, 0.54, 0.55, 0.58, 0.6, 0.61, 0.64, 0.65, 0.74, 0.84, 1.0, 1.26, 1.68, 2.0, 3.0, 5.0, 7.0], - 0.18 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], - 0.25 : [0.65, 1.0, 3.0, 5.0, 7.0], - 0.5 : [0.42, 0.55, 0.65, 1.0, 3.0, 5.0, 7.0], - 1.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], - 2.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], - 4.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], - 8.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], - 20.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0] -} - -pmos_bins = { - 0.15 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12, 1.26, 1.65, 1.68, 2.0, 3.0, 5.0, 7.0], - 1.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], - 2.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], - 4.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], - 8.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], - 0.17 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12], - 0.18 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12, 1.26, 1.68, 2.0, 3.0, 5.0, 7.0], - 0.25 : [1.0, 3.0, 5.0, 7.0], - 0.5 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], - 20.0 : [0.42] -} -################################################### -# GDS file info -################################################### -GDS = {} -# gds units -# From http://www.cnf.cornell.edu/cnf_spie9.html: "The first -# is the size of a database unit in user units. The second is the size -# of a database unit in meters. For example, if your library was -# created with the default units (user unit = 1 um and 1000 database -# units per user unit), then the first number would be 0.001 and the -# second number would be 10-9. Typically, the first number is less than -# 1, since you use more than 1 database unit per user unit. To -# calculate the size of a user unit in meters, divide the second number -# by the first." -GDS["unit"] = (0.001, 1e-9) -#GDS["unit"]=(0.001, 1e-6) - -################################################### -# Interconnect stacks -################################################### - -poly_stack = ("poly", "contact", "li") -active_stack = ("active", "contact", "li") -li_stack = ("li", "mcon", "m1") -m1_stack = ("m1", "via1", "m2") -m2_stack = ("m2", "via2", "m3") -m3_stack = ("m3", "via3", "m4") -m4_stack = ("m4", "via4", "m5") - -lef_rom_interconnect = ["m1", "m2", "m3", "m4"] - -layer_indices = {"poly": 0, - "active": 0, - "nwell": 0, - "li": 1, - "m1": 2, - "m2": 3, - "m3": 4, - "m4": 5, - "m5": 6} - -# The FEOL stacks get us up to m1 -feol_stacks = [poly_stack, - active_stack, - li_stack] - -# The BEOL stacks are m1 and up -beol_stacks = [m1_stack, - m2_stack, - m3_stack, - m4_stack] - -layer_stacks = feol_stacks + beol_stacks - -preferred_directions = {"poly": "V", - "active": "V", - "li": "V", - "m1": "H", - "m2": "V", - "m3": "H", - "m4": "V", - "m5": "H"} - -################################################### -# GDS Layer Map -################################################### - - -layer = {} -layer["active"] = (65, 20) # diff -layer["activep"] = (65, 20) # diff -layer["tap"] = (65, 44) # tap -layer["pwellp"] = (122,16) -layer["nwell"] = (64, 20) # nwell -layer["dnwell"] = (64,18) -layer["nimplant"]= (93, 44) # nsdm -layer["pimplant"]= (94, 20) # psdm -layer["vtl"] = (125, 44) # lvtn -layer["vth"] = (78, 44) # hvtp (pmos only) -layer["thkox"] = (8, 0) -layer["poly"] = (66, 20) -layer["contact"] = (66, 44) # licon1 -layer["npc"] = (95, 20) # npc (nitride cut) -layer["li"] = (67, 20) # active li1 -layer["mcon"] = (67, 44) # mcon -layer["m1"] = (68, 20) # met1 -layer["m1p"] = (68, 5) # met1 pin -layer["via1"] = (68, 44) # via1 -layer["m2"] = (69, 20) # met2 -layer["m2p"] = (69, 5) # met2 pin -layer["via2"] = (69, 44) # via2 -layer["m3"] = (70, 20) # met3 -layer["m3p"] = (70, 5) # met3 pin -layer["via3"] = (70, 44) # via3 -layer["m4"] = (71, 20) # met4 -layer["m4p"] = (71, 5) # met4 pin -layer["via4"] = (71, 44) # via4 -layer["m5"] = (72, 20) # met5 -layer["m5p"] = (72, 5) # met5 pin -layer["boundary"]= (235, 4) -# specific boundary type to define standard cell regions for DRC -layer["stdc"] = (81, 4) -layer["mem"] = (81, 2) -# Not an official sky130 layer, but useful for router debug infos -layer["text"]= (234, 5) -# Excpected value according to sky130A tech file -# If calibre is enabled, these will be swapped below -#pin_purpose = 5 -label_purpose = 5 -#label_purpose = 16 -#pin_purpose = 16 -#label_purpose = 5 - -# pin_read purposes -special_purposes = {layer["nwell"][0]: [layer["nwell"][1], 5, 59, 16]} -#layer_override = {"VNB\x00": ["pwell",122]} -layer_override = {"vnb": layer["pwellp"], "VNB": layer["pwellp"]} -layer_override_name = {"vnb": "pwellp", "VNB": "pwellp"} -layer_override_purpose = {122: (64, 59)} -# Layer names for external PDKs -layer_names = {} -layer_names["active"] = "diff" -layer_names["activep"] = "diff" -layer_names["tap"] = "tap" -layer_names["pwellp"] = "pwellp" -layer_names["nwell"] = "nwell" -layer_names["dnwell"] = "dnwell" -layer_names["nimplant"]= "nsdm" -layer_names["pimplant"]= "psdm" -layer_names["vtl"] = "lvtn" -layer_names["vth"] = "hvtp" -layer_names["thkox"] = "thkox" -layer_names["poly"] = "poly" -layer_names["contact"] = "licon1" -layer_names["li"] = "li1" -layer_names["mcon"] = "mcon" -layer_names["m1"] = "met1" -layer_names["m1p"] = "met1" -layer_names["via1"] = "via" -layer_names["m2"] = "met2" -layer_names["m2p"] = "met2" -layer_names["via2"] = "via2" -layer_names["m3"] = "met3" -layer_names["m3p"] = "met3" -layer_names["via3"] = "via3" -layer_names["m4"] = "met4" -layer_names["m4p"] = "met4" -layer_names["via4"] = "via4" -layer_names["m5p"] = "met5" -layer_names["boundary"]= "boundary" -layer_names["stdc"] = "areaid.standardc" -layer_names["mem"] = "areaid.core" -layer_names["text"] = "text" - - -################################################### -# DRC/LVS Rules Setup -################################################### - -# technology parameter -parameter={} -# difftap.2b -parameter["min_tx_size"] = 0.150 -parameter["beta"] = 3 - -parameter["6T_inv_nmos_size"] = 0.205 -parameter["6T_inv_pmos_size"] = 0.09 -parameter["6T_access_size"] = 0.135 - -drc = d.design_rules("sky130") - -# grid size -drc["grid"] = 0.005 - -#DRC/LVS test set_up -# Switching between calibre and magic can be useful for development, -# it eventually should be deleted. -NDA_PDK_ROOT = os.environ.get("NDA_PDK_ROOT", False) -use_calibre = bool(NDA_PDK_ROOT) -use_calibre = False -use_klayout = False -if use_calibre: - # Correct order according to s8 - pin_purpose = 16 - label_purpose = 5 - - drc["drc_rules"] = NDA_PDK_ROOT + "/DRC/Calibre/s8_drcRules" - drc["lvs_rules"] = NDA_PDK_ROOT + "/LVS/Calibre/lvs_s8_opts" - drc["xrc_rules"] = NDA_PDK_ROOT + "/PEX/xRC/extLvsRules_s8_5lm" - drc["layer_map"] = NDA_PDK_ROOT + "/VirtuosoOA/libs/technology_library/s8phirs_10r.layermap" - -# minwidth_tx with contact (no dog bone transistors) -# difftap.2b -drc["minwidth_tx"] = 0.360 -drc["minlength_channel"] = 0.150 - -drc["pwell_to_nwell"] = 0 -# nwell.1 Minimum width of nwell/pwell -drc.add_layer("nwell", - width=0.840, - spacing=1.270) - -# poly.1a Minimum width of poly -# poly.2 Minimum spacing of poly AND active -drc.add_layer("poly", - width=0.150, - spacing=0.210) -# poly.8 -drc["poly_extend_active"] = 0.13 -# Not a rule -drc["poly_to_contact"] = 0 -# poly.7 Minimum enclosure of active around gate -drc["active_enclose_gate"] = 0.075 -# poly.4 Minimum spacing of field poly to active -drc["poly_to_active"] = 0.075 -# poly.2 Minimum spacing of field poly -drc["poly_to_field_poly"] = 0.210 - -# difftap.1 Minimum width of active -# difftap.3 Minimum spacing of active -drc.add_layer("active", - width=0.150, - spacing=0.270) -# difftap.8 -drc.add_enclosure("nwell", - layer="active", - enclosure=0.18, - extension=0.18) - -# nsd/psd.5a -drc.add_enclosure("implant", - layer="active", - enclosure=0.125) - -# Same as active enclosure? -drc["implant_to_contact"] = 0.070 -# nsd/psd.1 nsd/psd.2 -drc.add_layer("implant", - width=0.380, - spacing=0.380, - area=0.265) - -# licon.1, licon.2 -drc.add_layer("contact", - width=0.170, - spacing=0.170) -# licon.5c (0.06 extension), (licon.7 for extension) -drc.add_enclosure("active", - layer="contact", - enclosure=0.040, - extension=0.060) -# licon.7 -drc["tap_extend_contact"] = 0.120 - -# licon.8 Minimum enclosure of poly around contact -drc.add_enclosure("poly", - layer="contact", - enclosure=0.08, - extension=0.08) -# licon.11a -drc["active_contact_to_gate"] = 0.050 -# npc.4 > licon.14 0.19 > licon.11a -drc["poly_contact_to_gate"] = 0.270 -# licon.15 -drc["npc_enclose_poly"] = 0.1 - -# li.1, li.3 -drc.add_layer("li", - width=0.170, - spacing=0.170) - -# licon.5 -drc.add_enclosure("li", - layer="contact", - enclosure=0, - extension=0.080) - -drc.add_enclosure("li", - layer="mcon", - enclosure=0, - extension=0.080) -# mcon.1, mcon.2 -drc.add_layer("mcon", - width=0.170, - spacing=0.210) - -# m1.1 Minimum width of metal1 -# m1.2 Minimum spacing of metal1 -# m1.6 Minimum area of metal1 -drc.add_layer("m1", - width=0.140, - spacing=0.140, - area=0.083) -# m1.4 Minimum enclosure of metal1 -# m1.5 Minimum enclosure around contact on two opposite sides -drc.add_enclosure("m1", - layer="mcon", - enclosure=0.030, - extension=0.060) -# via.4a Minimum enclosure around via1 -# via.5a Minimum enclosure around via1 on two opposite sides -drc.add_enclosure("m1", - layer="via1", - enclosure=0.055, - extension=0.085) - -# via.1a Minimum width of via1 -# via.2 Minimum spacing of via1 -drc.add_layer("via1", - width=0.150, - spacing=0.170) - -# m2.1 Minimum width of intermediate metal -# m2.2 Minimum spacing of intermediate metal -# m2.6 Minimum area of metal2 -drc.add_layer("m2", - width=0.140, - spacing=0.140, - area=0.0676) -# m2.4 Minimum enclosure around via1 -# m2.5 Minimum enclosure around via1 on two opposite sides -drc.add_enclosure("m2", - layer="via1", - enclosure=0.055, - extension=0.085) -# via2.4 Minimum enclosure around via2 -# via2.5 Minimum enclosure around via2 on two opposite sides -drc.add_enclosure("m2", - layer="via2", - enclosure=0.040, - extension=0.085) - -# via2.1a Minimum width of Via2 -# via2.2 Minimum spacing of Via2 -drc.add_layer("via2", - width=0.200, - spacing=0.200) - -# m3.1 Minimum width of metal3 -# m3.2 Minimum spacing of metal3 -# m3.6 Minimum area of metal3 -drc.add_layer("m3", - width=0.300, - spacing=0.300, - area=0.240) -# m3.4 Minimum enclosure around via2 -drc.add_enclosure("m3", - layer="via2", - enclosure=0.065) -# via3.4 Minimum enclosure around via3 -# via3.5 Minimum enclosure around via3 on two opposite sides -drc.add_enclosure("m3", - layer="via3", - enclosure=0.060, - extension=0.090) - -# via3.1 Minimum width of Via3 -# via3.2 Minimum spacing of Via3 -drc.add_layer("via3", - width=0.200, - spacing=0.200) - -# m4.1 Minimum width of metal4 -# m4.2 Minimum spacing of metal4 -# m4.7 Minimum area of metal4 -drc.add_layer("m4", - width=0.300, - spacing=0.300, - area=0.240) -# m4.3 Minimum enclosure around via3 -drc.add_enclosure("m4", - layer="via3", - enclosure=0.065) -# FIXME: Wrong rule m4.3 Minimum enclosure around via3 -drc.add_enclosure("m4", - layer="via4", - enclosure=0.060) - - -# via4.1 Minimum width of Via4 -# via4.2 Minimum spacing of Via4 -drc.add_layer("via4", - width=0.800, - spacing=0.800) - -# FIXME: Wrong rules -# m5.1 Minimum width of metal5 -# m5.2 Minimum spacing of metal5 -# m5.7 Minimum area of metal5 -drc.add_layer("m5", - width=1.600, - spacing=1.600, - area=4.000) -# m5.3 Minimum enclosure around via4 -drc.add_enclosure("m5", - layer="via4", - enclosure=0.310) - - - -# Metal 5-10 are ommitted - -################################################### -# Spice Simulation Parameters -################################################### - -# spice info -spice = {} -spice["nmos"] = "sky130_fd_pr__nfet_01v8" -spice["pmos"] = "sky130_fd_pr__pfet_01v8" -spice["power"]="vccd1" -spice["ground"]="vssd1" - -# whether or not the device model is actually a subckt -spice["device_prefix"] = "X" - -spice["fet_libraries"] = { "TT": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "tt"]], - "SS": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "ss"]], - "FF": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "ff"]], - "SF": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "sf"]], - "FS": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "fs"]] } - -# spice stimulus related variables -spice["feasible_period"] = 10 # estimated feasible period in ns -spice["supply_voltages"] = [1.7, 1.8, 1.9] # Supply voltage corners in [Volts] -spice["nom_supply_voltage"] = 1.8 # Nominal supply voltage in [Volts] -spice["rise_time"] = 0.005 # rise time in [Nano-seconds] -spice["fall_time"] = 0.005 # fall time in [Nano-seconds] -spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius) -spice["nom_temperature"] = 25 # Nominal temperature (celcius) - -# analytical delay parameters -spice["nom_threshold"] = 0.49 # Typical Threshold voltage in Volts -spice["wire_unit_r"] = 0.125 # Unit wire resistance in ohms/square -spice["wire_unit_c"] = 0.134 # Unit wire capacitance ff/um^2 -spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff -spice["min_tx_gate_c"] = 0.2 # Minimum transistor gate capacitance in ff -spice["dff_setup"] = 102.5391 # DFF setup time in ps -spice["dff_hold"] = -56 # DFF hold time in ps -spice["dff_in_cap"] = 6.89 # Input capacitance (D) [Femto-farad] -spice["dff_out_cap"] = 6.89 # Output capacitance (Q) [Femto-farad] - -# analytical power parameters, many values are temporary -spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW -spice["inv_leakage"] = 1 # Leakage power of inverter in nW -spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW -spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW -spice["nand4_leakage"] = 1 # Leakage power of 4-input nand in nW -spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW -spice["dff_leakage"] = 1 # Leakage power of flop in nW - -spice["default_event_frequency"] = 100 # Default event activity of every gate. MHz - -# Parameters related to sense amp enable timing and delay chain/RBL sizing -parameter["le_tau"] = 2.25 # In pico-seconds. -parameter["cap_relative_per_ff"] = 7.5 # Units of Relative Capacitance/ Femto-Farad -parameter["dff_clk_cin"] = 30.6 # relative capacitance -parameter["6tcell_wl_cin"] = 3 # relative capacitance -parameter["min_inv_para_delay"] = 2.4 # Tau delay units -parameter["sa_en_pmos_size"] = 0.72 # micro-meters -parameter["sa_en_nmos_size"] = 0.27 # micro-meters -parameter["sa_inv_pmos_size"] = 0.54 # micro-meters -parameter["sa_inv_nmos_size"] = 0.27 # micro-meters -parameter["bitcell_drain_cap"] = 0.1 # In Femto-Farad, approximation of drain capacitance - -################################################### -# Technology Tool Preferences -################################################### - -if use_calibre: - drc_name = "calibre" - lvs_name = "calibre" - pex_name = "calibre" -elif use_klayout: - drc_name = "klayout" - lvs_name = "klayout" - pex_name = "klayout" -else: - drc_name = "magic" - lvs_name = "netgen" - pex_name = "magic" - - -# This is used by uniqify to not rename the library cells -library_prefix_name = "sky130_fd_bd_sram__" -# List of cells to skip running DRC/LVS on directly -# This will look for a maglef file and copy it over the mag file -# before DRC after extraction - -# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_cell -# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_ce -# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell -# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_ce -# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell -# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_cell -# gds flatglob sky130_fd_bd_sram__sram_sp_cell_fom_serifs - -flatglob = ["*_?mos_m*", - "sky130_fd_bd_sram__sram_sp_cell_fom_serifs", - - "sky130_fd_bd_sram__sram_sp_cell", - "sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell", - "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_cell", - - "sky130_fd_bd_sram__sram_sp_cell_opt1_ce", - "sky130_fd_bd_sram__openram_sp_cell_opt1_replica_ce", - "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_ce", - "sky130_fd_bd_sram__sram_sp_wlstrap_ce", - "sky130_fd_bd_sram__sram_sp_wlstrap_p_ce"] - -blackbox_cells = ["sky130_fd_bd_sram__openram_dp_cell", - "sky130_fd_bd_sram__openram_dp_cell_dummy", - "sky130_fd_bd_sram__openram_dp_cell_replica", - - "sky130_fd_bd_sram__openram_sp_cell_opt1_noblcon", - "sky130_fd_bd_sram__openram_sp_cell_opt1a_noblcon", - "sky130_fd_bd_sram__openram_sp_colend_replica", - "sky130_fd_bd_sram__openram_sp_colenda_replica", - "sky130_fd_bd_sram__sram_sp_cell_opt1a", - "sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy", - "sky130_fd_bd_sram__openram_sp_cell_opt1_dummy", - "sky130_fd_bd_sram__sram_sp_cell_opt1_ce", - "sky130_fd_bd_sram__sram_sp_cell_opt1", - "sky130_fd_bd_sram__openram_sp_cell_opt1_replica", - "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica", - "sky130_fd_bd_sram__sram_sp_colend", - "sky130_fd_bd_sram__sram_sp_colend_cent", - "sky130_fd_bd_sram__sram_sp_colend_p_cent", - "sky130_fd_bd_sram__sram_sp_colenda", - "sky130_fd_bd_sram__sram_sp_colenda_cent", - "sky130_fd_bd_sram__sram_sp_colenda_p_cent", - "sky130_fd_bd_sram__sram_sp_rowend", - "sky130_fd_bd_sram__sram_sp_rowenda", - "sky130_fd_bd_sram__openram_sp_rowend_replica", - "sky130_fd_bd_sram__openram_sp_rowenda_replica", - "sky130_fd_bd_sram__sram_sp_corner", - "sky130_fd_bd_sram__sram_sp_cornera", - "sky130_fd_bd_sram__sram_sp_cornerb", - "sky130_fd_bd_sram__sram_sp_wlstrapa", - "sky130_fd_bd_sram__sram_sp_wlstrap_ce", - "sky130_fd_bd_sram__sram_sp_wlstrap", - "sky130_fd_bd_sram__sram_sp_wlstrap_p_ce", - "sky130_fd_bd_sram__sram_sp_wlstrap_p"] +if not hasattr(OPTS, 'tech_file'): + OPTS.tech_file = 'tech_cypress_cell' +#TODO: FIX THIS TERRIBLE HACK JUST FOR TESTING +exec('from {} import *'.format(OPTS.tech_file)) From b6d98c44d51a5fa60cac95a83a41e483d5eb9bc3 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 17 Mar 2026 14:50:43 -0700 Subject: [PATCH 53/61] singleport cba passing on both tech files --- .../modules/capped_replica_bitcell_array.py | 20 +++++++++---------- .../sky130/custom/sky130_col_cap_array.py | 16 ++++++++------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/compiler/modules/capped_replica_bitcell_array.py b/compiler/modules/capped_replica_bitcell_array.py index f2289663..78d3e927 100644 --- a/compiler/modules/capped_replica_bitcell_array.py +++ b/compiler/modules/capped_replica_bitcell_array.py @@ -85,7 +85,8 @@ 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=0, + column_offset=1, + row_offset=self.row_size+ self.extra_rows, mirror=0, location="top", left_rbl=self.left_rbl, @@ -95,8 +96,9 @@ 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=0, - mirror=0, + column_offset=1, + row_offset=0, + mirror=(1+self.row_size+self.extra_rows) % 2, location="bottom", left_rbl=self.left_rbl, right_rbl=self.right_rbl) @@ -106,20 +108,16 @@ class capped_replica_bitcell_array(bitcell_base_array): self.row_cap_left = factory.create(module_type=row_cap_module_type, cols=1, - column_offset=0, - row_offset=len(self.left_rbl)+len(self.right_rbl), rows=self.row_size + self.extra_rows, + column_offset=0, + row_offset=0, location="left") 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=len(self.left_rbl) + self.column_size + self.rbl[0], - row_offset=len(self.left_rbl)+len(self.right_rbl), rows=self.row_size + self.extra_rows, + column_offset=1 + len(self.left_rbl) + self.column_size + len(self.right_rbl), + row_offset=0, location="right") def add_pins(self): diff --git a/technology/sky130/custom/sky130_col_cap_array.py b/technology/sky130/custom/sky130_col_cap_array.py index 5e376f19..667641fb 100644 --- a/technology/sky130/custom/sky130_col_cap_array.py +++ b/technology/sky130/custom/sky130_col_cap_array.py @@ -18,7 +18,7 @@ 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="", left_rbl=[],right_rbl=[]): + def __init__(self, rows, cols, column_offset=0, row_offset=0, mirror=0, location="", name="", left_rbl=[],right_rbl=[]): self.left_rbl = left_rbl self.right_rbl = right_rbl super().__init__(rows, cols, column_offset=column_offset, mirror=mirror, location=location, name=name, left_rbl=left_rbl, right_rbl=right_rbl) @@ -42,15 +42,17 @@ class sky130_col_cap_array(col_cap_array, sky130_bitcell_base_array): self.cell_inst={} if self.location == "top": - bit_row = [geometry.instance("00_colend", mod=self.colend1, is_bitcell=True)] \ + bit_row = [geometry.instance("02_colend", mod=self.colend1, is_bitcell=True, mirror="MY")] \ + + [geometry.instance("03_strap_p", mod=self.colend3, is_bitcell=False)] \ + + [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")] \ + bit_row = [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")] \ + + [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 = [] From 515591a42287ba8503647d041ea6c62924f12cb9 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 14 Apr 2026 14:48:26 -0700 Subject: [PATCH 54/61] dual port rba lvs clean again with cell library changes --- compiler/modules/bitcell_array.py | 23 +++++----- compiler/modules/bitcell_base_array.py | 16 ++++++- .../modules/capped_replica_bitcell_array.py | 2 +- compiler/modules/col_cap_array.py | 41 ++++++++++++------ compiler/modules/dummy_array.py | 21 +++++---- compiler/modules/pattern.py | 5 +-- compiler/modules/replica_bitcell_array.py | 14 +++--- compiler/modules/replica_column.py | 6 +-- compiler/modules/row_cap_array.py | 43 ++++++++++++++----- macros/sram_configs/sky130_sram_1rw_tiny.py | 1 + .../sky130/custom/sky130_bitcell_array.py | 4 +- technology/sky130/tech/tech.py | 2 +- 12 files changed, 116 insertions(+), 62 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 6476c71a..264f9484 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -19,8 +19,8 @@ class bitcell_array(bitcell_base_array): Creates a rows x cols array of memory cells. Assumes bit-lines and word lines are connected by abutment. """ - def __init__(self, rows, cols, column_offset=0, name="", left_rbl=None, right_rbl=None): - super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) + def __init__(self, rows, cols, column_offset=0, row_offset=0, name="", left_rbl=None, right_rbl=None): + super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) @@ -59,18 +59,21 @@ class bitcell_array(bitcell_base_array): self.cell = factory.create(module_type=OPTS.bitcell) def create_instances(self): + r = self.row_offset + c = self.column_offset 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 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(0+c)%2}", mod=self.cell, is_bitcell=True, mirror='XY') + core_block[(0 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(1+c)%2}", mod=self.cell, is_bitcell=True, mirror='MX') + core_block[(1 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(0+c)%2}", mod=self.cell, is_bitcell=True, mirror='MY') + core_block[(1 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(1+c)%2}", mod=self.cell, is_bitcell=True, mirror='') 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') - + core_block = [[0 for x in range(1)] for y in range(2)] + core_block[(0 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) + core_block[(1 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') + print(r, c) + print(core_block) 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() diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 7c42558f..8c6a813a 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -16,14 +16,14 @@ class bitcell_base_array(design): """ Abstract base class for bitcell-arrays -- bitcell, dummy, replica """ - def __init__(self, name, rows, cols, column_offset): + def __init__(self, name, rows, cols, column_offset, row_offset): super().__init__(name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.column_size = cols self.row_size = rows self.column_offset = column_offset - + self.row_offset = row_offset # Bitcell for port names only self.cell = factory.create(module_type=OPTS.bitcell) @@ -170,6 +170,18 @@ class bitcell_base_array(design): power_name = 'vdd' ground_name = 'gnd' + + if vdd_found == False or gnd_found == False: + for inst in self.insts: + if 'VDD' in inst.mod.get_pin_names(): + vdd_found = True + power_name = 'VDD' + + if 'GND' in inst.mod.get_pin_names(): + gnd_found = True + ground_name = 'GND' + + if vdd_found is False or gnd_found is False: from openram.tech import cell_properties try: diff --git a/compiler/modules/capped_replica_bitcell_array.py b/compiler/modules/capped_replica_bitcell_array.py index 78d3e927..eb0c68e1 100644 --- a/compiler/modules/capped_replica_bitcell_array.py +++ b/compiler/modules/capped_replica_bitcell_array.py @@ -20,7 +20,7 @@ class capped_replica_bitcell_array(bitcell_base_array): 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) + super().__init__(name, rows, cols, column_offset=0, row_offset=0) debug.info(1, "Creating {0} {1} x {2} rbls: {3} left_rbl: {4} right_rbl: {5}".format(self.name, rows, cols, diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index 510f7b0c..99940e98 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -8,12 +8,13 @@ from openram import OPTS from .bitcell_base_array import bitcell_base_array from openram.base import geometry from .pattern import pattern +from math import ceil class col_cap_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="",left_rbl=[],right_rbl=[]): + def __init__(self, rows, cols, column_offset=0, row_offset=0, mirror=0, location="", name="",left_rbl=[],right_rbl=[]): super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.mirror = mirror self.location = location @@ -38,28 +39,44 @@ class col_cap_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() - self.height = self.dummy_cell.height - self.width = self.column_size * self.cell.width + #self.height = self.dummy_cell.height + #self.width = self.column_size * self.cell.width self.add_boundary() self.DRC_LVS() def add_modules(self): """ Add the modules used in this design """ - self.dummy_cell = factory.create(module_type="col_cap_{}".format(OPTS.bitcell)) + self.colend = factory.create(module_type="col_cap_{}".format(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.dummy_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.dummy_cell) - self.connect_inst(self.get_bitcell_pins(row, col)) + self.cell_inst={} + if self.location == "top": + bit_row = [geometry.instance("00_colend", mod=self.colend, is_bitcell=True, mirror="MY")]\ + + [geometry.instance("01_colend", mod=self.colend, is_bitcell=True)] + elif self.location == "bottom": + bit_row = [geometry.instance("00_colend", mod=self.colend, is_bitcell=True, mirror="XY")]\ + + [geometry.instance("01_colend", mod=self.colend, is_bitcell=True, mirror="MX")] + + 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): """ diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 01472e1d..45b04c41 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -15,7 +15,7 @@ class dummy_array(bitcell_base_array): """ def __init__(self, rows, cols, column_offset=0, row_offset=0 ,mirror=0, location="", name="", left_rbl=0, right_rbl=0): - super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) + super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name) self.location = location self.row_offset = row_offset self.mirror = mirror @@ -55,17 +55,20 @@ class dummy_array(bitcell_base_array): def create_instances(self): """ Create the module instances used in this design """ self.cell_inst={} + r = self.row_offset + c = self.column_offset 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+self.column_offset+self.row_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - core_block[(1+self.mirror) %2][(0+self.column_offset+self.row_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True) - core_block[(0+self.mirror) %2][(1+self.column_offset+self.row_offset) %2] = geometry.instance("core_0_1", mod=self.dummy_cell, is_bitcell=True, mirror='XY') - core_block[(1+self.mirror) %2][(1+self.column_offset+self.row_offset) %2] = geometry.instance("core_1_1", mod=self.dummy_cell, is_bitcell=True, mirror='MY') + core_block[(0 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(0+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='XY') + core_block[(0 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(1+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + core_block[(1 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(0+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='MY') + core_block[(1 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(1+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='') else: - core_block = [[0 for x in range(1)] for y in range(2)] - core_block[(1+self.mirror) %2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - core_block[(0+self.mirror) %2][(0+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True) - + core_block = [[0 for x in range(1)] for y in range(2)] + core_block[(0 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) + core_block[(1 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + print(r, c) + print(core_block) 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 798ef331..72ef37de 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -147,7 +147,6 @@ class pattern(): def connect_array(self) -> None: - #debug_array = [[None]*12 for _ in range(6)] row = 0 col = 0 for i in range(self.num_cores_y): @@ -183,7 +182,7 @@ class pattern(): y += inst.height if "Y" in inst.mirror: x += inst.width - #print('placing inst {} at {}'.format(inst, offset)) + print('placing inst {} at {}'.format(inst, offset)) inst.place((x, y), inst.mirror, inst.rotate) def place_array(self): @@ -193,7 +192,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[self.row_max - row, col] + inst = self.parent_design.all_inst[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 7262e023..d27fd2df 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -25,7 +25,7 @@ class replica_bitcell_array(bitcell_base_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) + super().__init__(name=name, rows=rows, cols=cols, column_offset=0, row_offset=0) debug.info(1, "Creating {0} {1} x {2} rbls: {3} left_rbl: {4} right_rbl: {5}".format(self.name, rows, cols, @@ -77,6 +77,7 @@ class replica_bitcell_array(bitcell_base_array): # Bitcell array self.bitcell_array = factory.create(module_type="bitcell_array", column_offset=len(self.left_rbl), + row_offset=len(self.left_rbl), cols=self.column_size, rows=self.row_size, left_rbl=self.left_rbl, @@ -110,20 +111,17 @@ 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 + elif port in self.right_rbl: + row_offset = self.row_size + len(self.left_rbl) else: - continue + row_offset = 0 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=mirror) + column_offset=len(self.left_rbl)) def add_pins(self): diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 433ac2ec..c07b039c 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -30,7 +30,7 @@ class replica_column(bitcell_base_array): 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) + super().__init__(rows=self.row_size, cols=1, column_offset=column_offset, row_offset=0, name=name) self.rows = rows self.left_rbl = rbl[0] @@ -95,12 +95,12 @@ 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 == 1: + if current_row % 2 == 0: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True) else: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MX') else: - if current_row % 2 == 1: + 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') diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index c68034fe..813869e3 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -6,16 +6,19 @@ 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 +from math import ceil 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, location="", 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) self.mirror = mirror self.location = location + self.row_offset = row_offset #self.no_instances = True self.create_netlist() if not OPTS.netlist_only: @@ -33,7 +36,7 @@ class row_cap_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() self.width = max([x.rx() for x in self.insts]) @@ -44,19 +47,37 @@ class row_cap_array(bitcell_base_array): def add_modules(self): """ Add the modules used in this design """ - self.dummy_cell = factory.create(module_type="row_cap_{}".format(OPTS.bitcell)) + self.row_cap = factory.create(module_type="row_cap_{}".format(OPTS.bitcell)) 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(0, 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.all_inst={} + self.cell_inst={} + + bit_block = [] + + 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.row_cap, is_bitcell=True, mirror="") + rowend_m = geometry.instance("row_cap_rowend_m", mod=self.row_cap, is_bitcell=True, mirror="MX") + 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.row_cap, is_bitcell=True, mirror="MY") + rowend_m = geometry.instance("row_cap_rowend_m", mod=self.row_cap, is_bitcell=True, mirror="XY") + #pattern.append_row_to_block(bit_block, [top_corner]) + for row in range(0, self.row_size): + if row % 2 == 1: + pattern.append_row_to_block(bit_block, [rowend]) + else: + pattern.append_row_to_block(bit_block, [rowend_m]) + + #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): """ diff --git a/macros/sram_configs/sky130_sram_1rw_tiny.py b/macros/sram_configs/sky130_sram_1rw_tiny.py index 99781e1a..e04bb3a6 100644 --- a/macros/sram_configs/sky130_sram_1rw_tiny.py +++ b/macros/sram_configs/sky130_sram_1rw_tiny.py @@ -26,3 +26,4 @@ num_sim_threads = 1 #analytical_delay = False import os exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) +#tech_file = "tech_custom_cell" \ No newline at end of file diff --git a/technology/sky130/custom/sky130_bitcell_array.py b/technology/sky130/custom/sky130_bitcell_array.py index b6b7a84a..a6af455d 100644 --- a/technology/sky130/custom/sky130_bitcell_array.py +++ b/technology/sky130/custom/sky130_bitcell_array.py @@ -19,8 +19,8 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): Creates a rows x cols array of memory cells. Assumes bit-lines and word lines are connected by abutment. """ - def __init__(self, rows, cols, column_offset=0, name="",left_rbl=None, right_rbl=None): - super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) + def __init__(self, rows, cols, column_offset=0, row_offset=0, name="",left_rbl=None, right_rbl=None): + super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name) self.left_rbl = left_rbl self.right_rbl = right_rbl diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index 3aa4659f..e65a13db 100755 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -15,5 +15,5 @@ sys.path.append("{}/{}".format(dir_path,'tech_configs')) if not hasattr(OPTS, 'tech_file'): OPTS.tech_file = 'tech_cypress_cell' -#TODO: FIX THIS TERRIBLE HACK JUST FOR TESTING +#TODO: FIX THIS TERRIBLE HACK JUST FOR TESTING EXECUTING exec('from {} import *'.format(OPTS.tech_file)) From 5fd548582f0daed76138085f059d0ce95aa67aed Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 14 Apr 2026 15:10:30 -0700 Subject: [PATCH 55/61] bump cell lib version for dual port fixes --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6974f1e4..eebaaa10 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 ?= 32f553d240545574282ac437ff98d2b889bf039f +SRAM_LIB_GIT_COMMIT ?= 3ad211667d2b7ee0d1092dcc204e6da5a2a3886c SKY130_PDK ?= $(PDK_ROOT)/sky130A GF180_PDK ?= $(PDK_ROOT)/gf180mcuD From cb7f117daa04123f04e4c8cb5f16219a13f01e08 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 22 Apr 2026 01:33:47 -0700 Subject: [PATCH 56/61] squash commits --- compiler/drc/custom_cell_properties.py | 4 +- compiler/modules/bitcell_array.py | 4 +- compiler/modules/bitcell_base.py | 1 + compiler/modules/bitcell_base_array.py | 7 +- .../modules/capped_replica_bitcell_array.py | 25 +- compiler/modules/col_cap_array.py | 2 +- compiler/modules/dummy_array.py | 4 +- compiler/modules/pattern.py | 2 +- compiler/modules/replica_bitcell_array.py | 12 +- compiler/modules/replica_column.py | 8 +- compiler/modules/row_cap_array.py | 8 +- macros/sram_configs/sky130_sram_1rw_tiny.py | 2 +- .../custom/sky130_bitcell_base_array.py | 4 +- .../sky130/custom/sky130_replica_column.py | 4 +- .../sky130/custom/sky130_row_cap_array.py | 13 +- technology/sky130/tech/tech.py | 2 +- .../tech/tech_configs/tech_custom_cell.py | 857 ++++++++++++++++++ .../tech/tech_configs/tech_cypress_cell.py | 848 +++++++++++++++++ 18 files changed, 1768 insertions(+), 39 deletions(-) create mode 100644 technology/sky130/tech/tech_configs/tech_custom_cell.py create mode 100644 technology/sky130/tech/tech_configs/tech_cypress_cell.py diff --git a/compiler/drc/custom_cell_properties.py b/compiler/drc/custom_cell_properties.py index d7972be1..6c5a7240 100644 --- a/compiler/drc/custom_cell_properties.py +++ b/compiler/drc/custom_cell_properties.py @@ -144,11 +144,11 @@ class _pgate: class bitcell(cell): - def __init__(self, port_order, port_types, port_map=None, storage_nets=["Q", "Q_bar"], mirror=None, end_caps=False): + def __init__(self, port_order, port_types, port_map=None, storage_nets=["Q", "Q_bar"], mirror=None, end_caps=False, has_corners=True): super().__init__(port_order, port_types, port_map) self.end_caps = end_caps - + self.has_corners = has_corners if not mirror: self.mirror = _mirror_axis(True, False) else: diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 264f9484..e2292df5 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -72,8 +72,8 @@ class bitcell_array(bitcell_base_array): core_block = [[0 for x in range(1)] for y in range(2)] core_block[(0 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) core_block[(1 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') - print(r, c) - print(core_block) + #print(r, c) + #print(core_block) 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() diff --git a/compiler/modules/bitcell_base.py b/compiler/modules/bitcell_base.py index 3fcdd476..f2f03217 100644 --- a/compiler/modules/bitcell_base.py +++ b/compiler/modules/bitcell_base.py @@ -25,6 +25,7 @@ class bitcell_base(design): self.nets_match = self.do_nets_exist(prop.storage_nets) self.mirror = prop.mirror self.end_caps = prop.end_caps + self.has_corners = prop.has_corners def get_stage_effort(self, load): parasitic_delay = 1 # This accounts for bitline being drained diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 8c6a813a..f470bc46 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -121,13 +121,14 @@ class bitcell_base_array(design): def get_all_wordline_names(self, port=None): """ Return all the wordline names """ temp = [] - temp.extend(self.get_rbl_wordline_names(0)) + if len(self.all_ports) > 1: + temp.extend(self.get_rbl_wordline_names(1)) if port == None: temp.extend(self.all_wordline_names) else: temp.extend(self.wordline_names[port]) - if len(self.all_ports) > 1: - temp.extend(self.get_rbl_wordline_names(1)) + temp.extend(self.get_rbl_wordline_names(0)) + return temp def add_bitline_pins(self): diff --git a/compiler/modules/capped_replica_bitcell_array.py b/compiler/modules/capped_replica_bitcell_array.py index eb0c68e1..c255f0d3 100644 --- a/compiler/modules/capped_replica_bitcell_array.py +++ b/compiler/modules/capped_replica_bitcell_array.py @@ -86,7 +86,7 @@ class capped_replica_bitcell_array(bitcell_base_array): rows=1, # dummy column + left replica column(s) column_offset=1, - row_offset=self.row_size+ self.extra_rows, + row_offset=self.row_size+ self.extra_rows + 1, #add 1 to account for bottom col_cap mirror=0, location="top", left_rbl=self.left_rbl, @@ -162,10 +162,21 @@ class capped_replica_bitcell_array(bitcell_base_array): self.unused_wordline_names = self.replica_bitcell_array.unused_wordline_names self.replica_array_wordline_names_with_grounded_wls = ["gnd" if x in self.unused_wordline_names else x for x in self.replica_bitcell_array.wordline_pin_list] + # Left/right row caps cover the full array height. Pad with gnd so the + # netlist list length matches the row cap (replica in the center); do + # not use col cap wordline heuristics. + + n_rowcap_wl = len(self.row_cap_left.get_wordline_names()) + n_rba_wl = len(self.replica_array_wordline_names_with_grounded_wls) + + self.wordline_pin_list = [] - self.wordline_pin_list.extend(["gnd"] * len(self.col_cap_top.get_wordline_names())) + + if self.rbls: + self.wordline_pin_list.extend(["gnd"] * len(self.rbls)) self.wordline_pin_list.extend(self.replica_array_wordline_names_with_grounded_wls) - self.wordline_pin_list.extend(["gnd"] * len(self.col_cap_bottom.get_wordline_names())) + if self.rbls: + self.wordline_pin_list.extend(["gnd"] * len(self.rbls)) self.add_pin_list(self.used_wordline_names, "INPUT") @@ -192,6 +203,10 @@ class capped_replica_bitcell_array(bitcell_base_array): 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.wordline_pin_list + self.supplies) + + #print(self.dummy_col_insts[0].mod.pins) + #print(["dummy_left_" + bl for bl in self.row_cap_left.all_bitline_names] + self.wordline_pin_list + 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.wordline_pin_list + self.supplies) @@ -282,10 +297,14 @@ class capped_replica_bitcell_array(bitcell_base_array): # Far left dummy col dummy_col_width = vector(self.dummy_col_insts[0].width, 0) offset = self.dummy_row_insts[0].ll() - dummy_col_width + if self.dummy_col_insts[0].mod.cell.has_corners is False: + offset += vector(0, dummy_row_height.y) self.dummy_col_insts[0].place(offset=offset) # Far right dummy col offset = self.dummy_row_insts[0].lr() + if self.dummy_col_insts[0].mod.cell.has_corners is False: + offset += vector(0, dummy_row_height.y) self.dummy_col_insts[1].place(offset=offset) def add_layout_pins(self): diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index 99940e98..dffe9459 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -15,7 +15,7 @@ class col_cap_array(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="",left_rbl=[],right_rbl=[]): - super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) + super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name) self.mirror = mirror self.location = location diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 45b04c41..85970e6f 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -67,8 +67,8 @@ class dummy_array(bitcell_base_array): core_block = [[0 for x in range(1)] for y in range(2)] core_block[(0 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) core_block[(1 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - print(r, c) - print(core_block) + #print(r, c) + #print(core_block) 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 72ef37de..9c52436d 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -182,7 +182,7 @@ class pattern(): y += inst.height if "Y" in inst.mirror: x += inst.width - print('placing inst {} at {}'.format(inst, offset)) + #print('placing inst {} at {}'.format(inst, offset)) inst.place((x, y), inst.mirror, inst.rotate) def place_array(self): diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index d27fd2df..e338a48b 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -76,8 +76,8 @@ 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=len(self.left_rbl), - row_offset=len(self.left_rbl), + column_offset=len(self.left_rbl)+ 1, #add 1 to account for left row_cap + row_offset=len(self.left_rbl)+1, #add 1 to account for bottom col_cap cols=self.column_size, rows=self.row_size, left_rbl=self.left_rbl, @@ -92,11 +92,11 @@ 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 = 0 + column_offset = 1 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 + column_offset = len(self.left_rbl) + self.column_size + 1 else: continue @@ -120,8 +120,8 @@ class replica_bitcell_array(bitcell_base_array): 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)) + row_offset=row_offset+1, #add 1 to account for bottom col_cap + column_offset=len(self.left_rbl)+1) #add 1 to account for left row_cap def add_pins(self): diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index c07b039c..855def17 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -96,14 +96,14 @@ class replica_column(bitcell_base_array): # 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: - core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True) - else: 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 == 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') + else: + core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True) current_row += 1 diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index 813869e3..c4ad50ce 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -15,7 +15,7 @@ class row_cap_array(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) + super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name) self.mirror = mirror self.location = location self.row_offset = row_offset @@ -76,7 +76,11 @@ class row_cap_array(bitcell_base_array): pattern.append_row_to_block(bit_block, [rowend_m]) #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}") + if self.cell.has_corners is False: + num_rows = self.row_size - 2 + else: + num_rows = self.row_size + self.pattern = pattern(self, "row_cap_array_" + self.location, bit_block, num_rows=num_rows, 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): diff --git a/macros/sram_configs/sky130_sram_1rw_tiny.py b/macros/sram_configs/sky130_sram_1rw_tiny.py index e04bb3a6..20aaa465 100644 --- a/macros/sram_configs/sky130_sram_1rw_tiny.py +++ b/macros/sram_configs/sky130_sram_1rw_tiny.py @@ -26,4 +26,4 @@ num_sim_threads = 1 #analytical_delay = False import os exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) -#tech_file = "tech_custom_cell" \ No newline at end of file +tech_file = "tech_custom_cell" \ No newline at end of file diff --git a/technology/sky130/custom/sky130_bitcell_base_array.py b/technology/sky130/custom/sky130_bitcell_base_array.py index 292ff39d..89eb2604 100644 --- a/technology/sky130/custom/sky130_bitcell_base_array.py +++ b/technology/sky130/custom/sky130_bitcell_base_array.py @@ -17,8 +17,8 @@ class sky130_bitcell_base_array(bitcell_base_array): """ Abstract base class for bitcell-arrays -- bitcell, dummy, replica """ - def __init__(self, name, rows, cols, column_offset): - super().__init__(name, rows, cols, column_offset) + def __init__(self, name, rows, cols, column_offset, row_offset): + super().__init__(name, rows, cols, column_offset, row_offset) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") diff --git a/technology/sky130/custom/sky130_replica_column.py b/technology/sky130/custom/sky130_replica_column.py index a40e23fa..f83b504d 100644 --- a/technology/sky130/custom/sky130_replica_column.py +++ b/technology/sky130/custom/sky130_replica_column.py @@ -80,12 +80,12 @@ class sky130_replica_column(replica_column, sky130_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 == 1: + 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 == 1: + if current_row % 2 == 0: 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 92d97201..1130556b 100644 --- a/technology/sky130/custom/sky130_row_cap_array.py +++ b/technology/sky130/custom/sky130_row_cap_array.py @@ -56,13 +56,15 @@ 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, [top_corner]) + pattern.append_row_to_block(bit_block, [bottom_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, [bottom_corner]) + + else: + pattern.append_row_to_block(bit_block, [rowend]) + + 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() @@ -77,8 +79,6 @@ class sky130_row_cap_array(row_cap_array, sky130_bitcell_base_array): 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): @@ -94,7 +94,6 @@ class sky130_row_cap_array(row_cap_array, sky130_bitcell_base_array): 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.wordline_names[port].append("wl_{0}_{1}".format(port, row)) diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index e65a13db..3aa4659f 100755 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -15,5 +15,5 @@ sys.path.append("{}/{}".format(dir_path,'tech_configs')) if not hasattr(OPTS, 'tech_file'): OPTS.tech_file = 'tech_cypress_cell' -#TODO: FIX THIS TERRIBLE HACK JUST FOR TESTING EXECUTING +#TODO: FIX THIS TERRIBLE HACK JUST FOR TESTING exec('from {} import *'.format(OPTS.tech_file)) diff --git a/technology/sky130/tech/tech_configs/tech_custom_cell.py b/technology/sky130/tech/tech_configs/tech_custom_cell.py new file mode 100644 index 00000000..b797cc64 --- /dev/null +++ b/technology/sky130/tech/tech_configs/tech_custom_cell.py @@ -0,0 +1,857 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California +# All rights reserved. +# + + +import os +from openram import drc as d + +""" +File containing the process technology parameters for Skywater 130nm. +""" + +################################################### +# Custom modules +################################################### + +# This uses the default classes to instantiate module from +# '$OPENRAM_HOME/compiler/modules'. +# Using tech_modules['cellname'] you can override each class by providing a custom +# implementation in '$OPENRAM_TECHDIR/modules/' +# For example: tech_modules["contact"] = "contact_freepdk45" +tech_modules = d.module_type() + + +# These modules have been hand designed and provided in this repository. +tech_modules["nand2_dec"] = "nand2_dec" +tech_modules["nand3_dec"] = "nand3_dec" +tech_modules["nand4_dec"] = "nand4_dec" + +# Override default OpenRAM modules to sky130 modules +# These are for single port and dual port as a list, +# or for both if there is no list, +# or only applicable to one if there is no list. + +#tech_modules["bitcell_1port"] = "sky130_bitcell" +#tech_modules["replica_bitcell_1port"] = "sky130_replica_bitcell" +#tech_modules["dummy_bitcell_1port"] = "sky130_dummy_bitcell" + +tech_modules["replica_bitcell_2port"] = "replica_bitcell_2port" +tech_modules["dummy_bitcell_2port"] = "dummy_bitcell_2port" +tech_modules["bitcell_2port"] = "bitcell_2port" + +tech_modules["bitcell_array"] = ["bitcell_array", "bitcell_array"] +tech_modules["replica_bitcell_array"] = ["replica_bitcell_array", "replica_bitcell_array"] +tech_modules["capped_replica_bitcell_array"] = ["capped_replica_bitcell_array", "capped_replica_bitcell_array"] +tech_modules["dummy_array"] = ["dummy_array", "dummy_array"] + +tech_modules["replica_column"] = ["replica_column", "replica_column"] + +tech_modules["col_cap_array"] = ["col_cap_array", "col_cap_array"] +tech_modules["col_cap"] = ["col_cap_bitcell_1port", "col_cap_bitcell_2port"] +tech_modules["corner"] = ["sky130_corner", None] +tech_modules["internal"] = ["sky130_internal", None] +tech_modules["row_cap_array"] = ["sky130_row_cap_array", "row_cap_array"] +tech_modules["row_cap"] = ["sky130_row_cap", "row_cap_bitcell_2port"] + +# These modules are auto-generated from the nand decoders above and are not +# found in this. +tech_modules["buf_dec"] = "pbuf_dec" +tech_modules["inv_dec"] = "pinv_dec" +tech_modules["and2_dec"] = "and2_dec" +tech_modules["and3_dec"] = "and3_dec" +tech_modules["and4_dec"] = "and4_dec" + +################################################### +# Custom cell properties +################################################### +cell_properties = d.cell_properties() + +cell_properties.power_name = 'VPWR' +cell_properties.ground_name = 'VGND' + +cell_properties.bitcell_power_pin_directions = ("V", "V") + +cell_properties.bitcell_1port.mirror.x = True +cell_properties.bitcell_1port.mirror.y = True +cell_properties.bitcell_1port.end_caps = False +cell_properties.bitcell_1port.has_corners = True + +cell_properties.bitcell_1port.boundary_layer = "boundary" +cell_properties.bitcell_1port.port_order = ['bl', 'br', 'wl', 'vdd', 'gnd'] +cell_properties.bitcell_1port.port_types = ["INPUT", "INPUT", "GROUND", "POWER", "OUTPUT"] +cell_properties.bitcell_1port.port_map = {'bl': 'BL', + 'br': 'BR', + 'gnd': 'VGND', + 'vdd': 'VPWR', + 'wl': 'WL'} + + + + +cell_properties.bitcell_1port.wl_layer = "m2" +cell_properties.bitcell_1port.bl_layer = "m1" +cell_properties.bitcell_1port.vdd_layer = "m1" +cell_properties.bitcell_1port.vdd_dir = "V" +cell_properties.bitcell_1port.gnd_layer = "m1" +cell_properties.bitcell_1port.gnd_dir = "V" + +cell_properties.bitcell_2port.mirror.x = True +cell_properties.bitcell_2port.mirror.y = True +cell_properties.bitcell_2port.end_caps = True +cell_properties.bitcell_2port.has_corners = True +cell_properties.bitcell_2port.port_order = ['bl0', 'br0', 'bl1', 'br1', 'wl0', 'wl1', 'vdd', 'gnd'] +cell_properties.bitcell_2port.port_map = {'bl0': 'BL0', + 'br0': 'BR0', + 'bl1': 'BL1', + 'br1': 'BR1', + 'wl0': 'WL0', + 'wl1': 'WL1', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.bitcell_1port.wl_layer = "m2" +cell_properties.bitcell_1port.vdd_layer = "m1" +cell_properties.bitcell_1port.vdd_dir = "V" +cell_properties.bitcell_1port.gnd_layer = "m1" +cell_properties.bitcell_1port.gnd_dir = "V" +cell_properties.bitcell_2port.wl_layer = "m2" +cell_properties.bitcell_2port.vdd_layer = "m1" +cell_properties.bitcell_2port.vdd_dir = "H" +cell_properties.bitcell_2port.gnd_layer = "m2" +cell_properties.bitcell_2port.gnd_dir = "H" + +cell_properties.col_cap_1port_bitcell = d.cell(['bl', 'br', 'vdd', 'gnd',], + ['INPUT', 'INPUT','POWER', 'GROUND', ], + {'bl': 'bl', + 'br': 'br', + 'vdd': 'vdd', + 'gnd': 'gnd',}) +cell_properties.col_cap_1port_bitcell.boundary_layer = "boundary" + +cell_properties.col_cap_1port_strap_power = d.cell(['vdd', 'vpb', 'vnb'], + ['POWER', 'BIAS', 'BIAS'], + {'vnb': 'VNB', + 'vpb': 'VPB', + 'vdd': 'VPWR'}) +cell_properties.col_cap_1port_strap_power.boundary_layer = "boundary" + +cell_properties.col_cap_1port_strap_ground = d.cell(['gnd', 'vpb', 'vnb'], + ['GROUND', 'BIAS', 'BIAS'], + {'vnb': 'VNB', + 'vpb': 'VPB', + 'gnd': 'VGND'}) +cell_properties.col_cap_1port_strap_ground.boundary_layer = "boundary" + +cell_properties.row_cap_1port_cell = d.cell(['vdd', 'wl'], + ['POWER', 'INPUT'], + {'wl': 'WL', + 'vdd': 'VPWR'}) +cell_properties.row_cap_1port_cell.boundary_layer = "boundary" + +cell_properties.col_cap_2port.port_order = ['bl0', 'br0', 'bl1', 'br1', 'vdd'] +cell_properties.col_cap_2port.port_map = {'bl0': 'BL0', + 'br0': 'BR0', + 'bl1': 'BL1', + 'br1': 'BR1', + 'vdd': 'VDD'} + +cell_properties.row_cap_2port.port_order = ['wl0', 'wl1', 'gnd'] +cell_properties.row_cap_2port.port_map = {'wl0': 'WL0', + 'wl1': 'WL1', + 'gnd': 'GND'} + + +cell_properties.ptx.bin_spice_models = True +cell_properties.ptx.model_is_subckt = True + +cell_properties.pgate.add_implants = True + +cell_properties.use_strap = False +cell_properties.strap_module = "internal" +cell_properties.strap_version = "wlstrap" + +cell_properties.dff.port_order = ['D', 'Q', 'clk', 'vdd', 'gnd'] +cell_properties.dff.port_map = {'D': 'D', + 'Q': 'Q', + 'clk': 'CLK', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.nand2_dec.port_order = ['A', 'B', 'Z', 'vdd', 'gnd'] +cell_properties.nand2_dec.port_map = {'A': 'A', + 'B': 'B', + 'Z': 'Z', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.nand3_dec.port_order = ['A', 'B', 'C', 'Z', 'vdd', 'gnd'] +cell_properties.nand3_dec.port_map = {'A': 'A', + 'B': 'B', + 'C': 'C', + 'Z': 'Z', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.nand4_dec.port_order = ['A', 'B', 'C', 'D', 'Z', 'vdd', 'gnd'] +cell_properties.nand4_dec.port_map = {'A': 'A', + 'B': 'B', + 'C': 'C', + 'D': 'D', + 'Z': 'Z', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.sense_amp.port_order = ['bl', 'br', 'dout', 'en', 'vdd', 'gnd'] +cell_properties.sense_amp.port_map = {'bl': 'BL', + 'br': 'BR', + 'dout': 'DOUT', + 'en': 'EN', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.write_driver.port_order = ['din', 'bl', 'br', 'en', 'vdd', 'gnd'] +cell_properties.write_driver.port_map = {'din': 'DIN', + 'bl': 'BL', + 'br': 'BR', + 'en': 'EN', + 'vdd': 'VDD', + 'gnd': 'GND'} + + +# You can override the GDS for custom cell using the following: +# If it is a list, the first is single port and the second is dual port. +# If it is string, it is used for both single and dual port. +cell_properties.names["dff"] = "sky130_fd_bd_sram__openram_dff" +cell_properties.names["nand2_dec"] = ["sky130_fd_bd_sram__openram_dp_nand2_dec", "sky130_fd_bd_sram__openram_dp_nand2_dec"] +cell_properties.names["nand3_dec"] = ["sky130_fd_bd_sram__openram_dp_nand3_dec", "sky130_fd_bd_sram__openram_dp_nand3_dec"] +cell_properties.names["nand4_dec"] = ["sky130_fd_bd_sram__openram_dp_nand4_dec", "sky130_fd_bd_sram__openram_dp_nand4_dec"] + +cell_properties.names["bitcell_1port"] = "sky130_custom_cell" +cell_properties.names["replica_bitcell_1port"] = "sky130_custom_replica" +cell_properties.names["dummy_bitcell_1port"] = "sky130_custom_dummy" + +cell_properties.names["bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell" +cell_properties.names["dummy_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_dummy" +cell_properties.names["replica_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_replica" +cell_properties.names["col_cap_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_cap_col" +cell_properties.names["row_cap_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_cap_row" +cell_properties.names["sense_amp"] = "sky130_fd_bd_sram__openram_sense_amp" +cell_properties.names["write_driver"] = "sky130_fd_bd_sram__openram_write_driver" + +array_row_multiple = 2 +array_col_multiple = 2 + +################################################### +# Custom layer properties +################################################### +layer_properties = d.layer_properties() +layer_properties.hierarchical_decoder.bus_layer = "m1" +layer_properties.hierarchical_decoder.bus_directions = "nonpref" +layer_properties.hierarchical_decoder.input_layer = "li" +layer_properties.hierarchical_decoder.output_layer = "m2" +layer_properties.hierarchical_decoder.vertical_supply = True + +layer_properties.hierarchical_predecode.bus_layer = "m1" +layer_properties.hierarchical_predecode.bus_directions = "nonpref" +# This is added to allow the column decoder connections on m2 +layer_properties.hierarchical_predecode.bus_pitch_factor = 1.2 +layer_properties.hierarchical_predecode.bus_space_factor = 1.5 +layer_properties.hierarchical_predecode.input_layer = "li" +layer_properties.hierarchical_predecode.output_layer = "m2" +layer_properties.hierarchical_predecode.vertical_supply = True +layer_properties.hierarchical_predecode.force_horizontal_input_contact = True + +layer_properties.bank.stack = "m2_stack" +layer_properties.bank.pitch = "m3_pitch" + +layer_properties.column_mux_array.select_layer = "m3" +layer_properties.column_mux_array.bitline_layer = "m1" + +layer_properties.port_address.supply_offset = True + +layer_properties.port_data.enable_layer = "m1" +layer_properties.port_data.channel_route_bitlines = False + +layer_properties.replica_column.even_rows = False + +layer_properties.wordline_driver.vertical_supply = True + +layer_properties.global_wordline_layer = "m5" + + +################################################### +# Discrete tx bins +################################################### +# enforce that tx sizes are within 25% of requested size after fingering. +accuracy_requirement = 0.75 +nmos_bins = { + 0.15 : [0.36, 0.39, 0.42, 0.52, 0.54, 0.55, 0.58, 0.6, 0.61, 0.64, 0.65, 0.74, 0.84, 1.0, 1.26, 1.68, 2.0, 3.0, 5.0, 7.0], + 0.18 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 0.25 : [0.65, 1.0, 3.0, 5.0, 7.0], + 0.5 : [0.42, 0.55, 0.65, 1.0, 3.0, 5.0, 7.0], + 1.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 2.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 4.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 8.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 20.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0] +} + +pmos_bins = { + 0.15 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12, 1.26, 1.65, 1.68, 2.0, 3.0, 5.0, 7.0], + 1.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 2.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 4.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 8.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 0.17 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12], + 0.18 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12, 1.26, 1.68, 2.0, 3.0, 5.0, 7.0], + 0.25 : [1.0, 3.0, 5.0, 7.0], + 0.5 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 20.0 : [0.42] +} +################################################### +# GDS file info +################################################### +GDS = {} +# gds units +# From http://www.cnf.cornell.edu/cnf_spie9.html: "The first +# is the size of a database unit in user units. The second is the size +# of a database unit in meters. For example, if your library was +# created with the default units (user unit = 1 um and 1000 database +# units per user unit), then the first number would be 0.001 and the +# second number would be 10-9. Typically, the first number is less than +# 1, since you use more than 1 database unit per user unit. To +# calculate the size of a user unit in meters, divide the second number +# by the first." +GDS["unit"] = (0.001, 1e-9) +#GDS["unit"]=(0.001, 1e-6) + +################################################### +# Interconnect stacks +################################################### + +poly_stack = ("poly", "contact", "li") +active_stack = ("active", "contact", "li") +li_stack = ("li", "mcon", "m1") +m1_stack = ("m1", "via1", "m2") +m2_stack = ("m2", "via2", "m3") +m3_stack = ("m3", "via3", "m4") +m4_stack = ("m4", "via4", "m5") + +lef_rom_interconnect = ["m1", "m2", "m3", "m4"] + +layer_indices = {"poly": 0, + "active": 0, + "nwell": 0, + "li": 1, + "m1": 2, + "m2": 3, + "m3": 4, + "m4": 5, + "m5": 6} + +# The FEOL stacks get us up to m1 +feol_stacks = [poly_stack, + active_stack, + li_stack] + +# The BEOL stacks are m1 and up +beol_stacks = [m1_stack, + m2_stack, + m3_stack, + m4_stack] + +layer_stacks = feol_stacks + beol_stacks + +preferred_directions = {"poly": "V", + "active": "V", + "li": "V", + "m1": "H", + "m2": "V", + "m3": "H", + "m4": "V", + "m5": "H"} + +################################################### +# GDS Layer Map +################################################### + + +layer = {} +layer["active"] = (65, 20) # diff +layer["activep"] = (65, 20) # diff +layer["tap"] = (65, 44) # tap +layer["pwellp"] = (122,16) +layer["nwell"] = (64, 20) # nwell +layer["dnwell"] = (64,18) +layer["nimplant"]= (93, 44) # nsdm +layer["pimplant"]= (94, 20) # psdm +layer["vtl"] = (125, 44) # lvtn +layer["vth"] = (78, 44) # hvtp (pmos only) +layer["thkox"] = (8, 0) +layer["poly"] = (66, 20) +layer["contact"] = (66, 44) # licon1 +layer["npc"] = (95, 20) # npc (nitride cut) +layer["li"] = (67, 20) # active li1 +layer["mcon"] = (67, 44) # mcon +layer["m1"] = (68, 20) # met1 +layer["m1p"] = (68, 5) # met1 pin +layer["via1"] = (68, 44) # via1 +layer["m2"] = (69, 20) # met2 +layer["m2p"] = (69, 5) # met2 pin +layer["via2"] = (69, 44) # via2 +layer["m3"] = (70, 20) # met3 +layer["m3p"] = (70, 5) # met3 pin +layer["via3"] = (70, 44) # via3 +layer["m4"] = (71, 20) # met4 +layer["m4p"] = (71, 5) # met4 pin +layer["via4"] = (71, 44) # via4 +layer["m5"] = (72, 20) # met5 +layer["m5p"] = (72, 5) # met5 pin +layer["boundary"]= (235, 4) +# specific boundary type to define standard cell regions for DRC +layer["stdc"] = (81, 4) +layer["mem"] = (81, 2) +# Not an official sky130 layer, but useful for router debug infos +layer["text"]= (234, 5) +# Excpected value according to sky130A tech file +# If calibre is enabled, these will be swapped below +#pin_purpose = 5 +label_purpose = 5 +#label_purpose = 16 +#pin_purpose = 16 +#label_purpose = 5 + +# pin_read purposes +special_purposes = {layer["nwell"][0]: [layer["nwell"][1], 5, 59, 16]} +#layer_override = {"VNB\x00": ["pwell",122]} +layer_override = {"vnb": layer["pwellp"], "VNB": layer["pwellp"]} +layer_override_name = {"vnb": "pwellp", "VNB": "pwellp"} +layer_override_purpose = {122: (64, 59)} +# Layer names for external PDKs +layer_names = {} +layer_names["active"] = "diff" +layer_names["activep"] = "diff" +layer_names["tap"] = "tap" +layer_names["pwellp"] = "pwellp" +layer_names["nwell"] = "nwell" +layer_names["dnwell"] = "dnwell" +layer_names["nimplant"]= "nsdm" +layer_names["pimplant"]= "psdm" +layer_names["vtl"] = "lvtn" +layer_names["vth"] = "hvtp" +layer_names["thkox"] = "thkox" +layer_names["poly"] = "poly" +layer_names["contact"] = "licon1" +layer_names["li"] = "li1" +layer_names["mcon"] = "mcon" +layer_names["m1"] = "met1" +layer_names["m1p"] = "met1" +layer_names["via1"] = "via" +layer_names["m2"] = "met2" +layer_names["m2p"] = "met2" +layer_names["via2"] = "via2" +layer_names["m3"] = "met3" +layer_names["m3p"] = "met3" +layer_names["via3"] = "via3" +layer_names["m4"] = "met4" +layer_names["m4p"] = "met4" +layer_names["via4"] = "via4" +layer_names["m5p"] = "met5" +layer_names["boundary"]= "boundary" +layer_names["stdc"] = "areaid.standardc" +layer_names["mem"] = "areaid.core" +layer_names["text"] = "text" + + +################################################### +# DRC/LVS Rules Setup +################################################### + +# technology parameter +parameter={} +# difftap.2b +parameter["min_tx_size"] = 0.150 +parameter["beta"] = 3 + +parameter["6T_inv_nmos_size"] = 0.205 +parameter["6T_inv_pmos_size"] = 0.09 +parameter["6T_access_size"] = 0.135 + +drc = d.design_rules("sky130") + +# grid size +drc["grid"] = 0.005 + +#DRC/LVS test set_up +# Switching between calibre and magic can be useful for development, +# it eventually should be deleted. +NDA_PDK_ROOT = os.environ.get("NDA_PDK_ROOT", False) +use_calibre = bool(NDA_PDK_ROOT) +use_calibre = False +use_klayout = False +if use_calibre: + # Correct order according to s8 + pin_purpose = 16 + label_purpose = 5 + + drc["drc_rules"] = NDA_PDK_ROOT + "/DRC/Calibre/s8_drcRules" + drc["lvs_rules"] = NDA_PDK_ROOT + "/LVS/Calibre/lvs_s8_opts" + drc["xrc_rules"] = NDA_PDK_ROOT + "/PEX/xRC/extLvsRules_s8_5lm" + drc["layer_map"] = NDA_PDK_ROOT + "/VirtuosoOA/libs/technology_library/s8phirs_10r.layermap" + +# minwidth_tx with contact (no dog bone transistors) +# difftap.2b +drc["minwidth_tx"] = 0.360 +drc["minlength_channel"] = 0.150 + +drc["pwell_to_nwell"] = 0 +# nwell.1 Minimum width of nwell/pwell +drc.add_layer("nwell", + width=0.840, + spacing=1.270) + +# poly.1a Minimum width of poly +# poly.2 Minimum spacing of poly AND active +drc.add_layer("poly", + width=0.150, + spacing=0.210) +# poly.8 +drc["poly_extend_active"] = 0.13 +# Not a rule +drc["poly_to_contact"] = 0 +# poly.7 Minimum enclosure of active around gate +drc["active_enclose_gate"] = 0.075 +# poly.4 Minimum spacing of field poly to active +drc["poly_to_active"] = 0.075 +# poly.2 Minimum spacing of field poly +drc["poly_to_field_poly"] = 0.210 + +# difftap.1 Minimum width of active +# difftap.3 Minimum spacing of active +drc.add_layer("active", + width=0.150, + spacing=0.270) +# difftap.8 +drc.add_enclosure("nwell", + layer="active", + enclosure=0.18, + extension=0.18) + +# nsd/psd.5a +drc.add_enclosure("implant", + layer="active", + enclosure=0.125) + +# Same as active enclosure? +drc["implant_to_contact"] = 0.070 +# nsd/psd.1 nsd/psd.2 +drc.add_layer("implant", + width=0.380, + spacing=0.380, + area=0.265) + +# licon.1, licon.2 +drc.add_layer("contact", + width=0.170, + spacing=0.170) +# licon.5c (0.06 extension), (licon.7 for extension) +drc.add_enclosure("active", + layer="contact", + enclosure=0.040, + extension=0.060) +# licon.7 +drc["tap_extend_contact"] = 0.120 + +# licon.8 Minimum enclosure of poly around contact +drc.add_enclosure("poly", + layer="contact", + enclosure=0.08, + extension=0.08) +# licon.11a +drc["active_contact_to_gate"] = 0.050 +# npc.4 > licon.14 0.19 > licon.11a +drc["poly_contact_to_gate"] = 0.270 +# licon.15 +drc["npc_enclose_poly"] = 0.1 + +# li.1, li.3 +drc.add_layer("li", + width=0.170, + spacing=0.170) + +# licon.5 +drc.add_enclosure("li", + layer="contact", + enclosure=0, + extension=0.080) + +drc.add_enclosure("li", + layer="mcon", + enclosure=0, + extension=0.080) +# mcon.1, mcon.2 +drc.add_layer("mcon", + width=0.170, + spacing=0.210) + +# m1.1 Minimum width of metal1 +# m1.2 Minimum spacing of metal1 +# m1.6 Minimum area of metal1 +drc.add_layer("m1", + width=0.140, + spacing=0.140, + area=0.083) +# m1.4 Minimum enclosure of metal1 +# m1.5 Minimum enclosure around contact on two opposite sides +drc.add_enclosure("m1", + layer="mcon", + enclosure=0.030, + extension=0.060) +# via.4a Minimum enclosure around via1 +# via.5a Minimum enclosure around via1 on two opposite sides +drc.add_enclosure("m1", + layer="via1", + enclosure=0.055, + extension=0.085) + +# via.1a Minimum width of via1 +# via.2 Minimum spacing of via1 +drc.add_layer("via1", + width=0.150, + spacing=0.170) + +# m2.1 Minimum width of intermediate metal +# m2.2 Minimum spacing of intermediate metal +# m2.6 Minimum area of metal2 +drc.add_layer("m2", + width=0.140, + spacing=0.140, + area=0.0676) +# m2.4 Minimum enclosure around via1 +# m2.5 Minimum enclosure around via1 on two opposite sides +drc.add_enclosure("m2", + layer="via1", + enclosure=0.055, + extension=0.085) +# via2.4 Minimum enclosure around via2 +# via2.5 Minimum enclosure around via2 on two opposite sides +drc.add_enclosure("m2", + layer="via2", + enclosure=0.040, + extension=0.085) + +# via2.1a Minimum width of Via2 +# via2.2 Minimum spacing of Via2 +drc.add_layer("via2", + width=0.200, + spacing=0.200) + +# m3.1 Minimum width of metal3 +# m3.2 Minimum spacing of metal3 +# m3.6 Minimum area of metal3 +drc.add_layer("m3", + width=0.300, + spacing=0.300, + area=0.240) +# m3.4 Minimum enclosure around via2 +drc.add_enclosure("m3", + layer="via2", + enclosure=0.065) +# via3.4 Minimum enclosure around via3 +# via3.5 Minimum enclosure around via3 on two opposite sides +drc.add_enclosure("m3", + layer="via3", + enclosure=0.060, + extension=0.090) + +# via3.1 Minimum width of Via3 +# via3.2 Minimum spacing of Via3 +drc.add_layer("via3", + width=0.200, + spacing=0.200) + +# m4.1 Minimum width of metal4 +# m4.2 Minimum spacing of metal4 +# m4.7 Minimum area of metal4 +drc.add_layer("m4", + width=0.300, + spacing=0.300, + area=0.240) +# m4.3 Minimum enclosure around via3 +drc.add_enclosure("m4", + layer="via3", + enclosure=0.065) +# FIXME: Wrong rule m4.3 Minimum enclosure around via3 +drc.add_enclosure("m4", + layer="via4", + enclosure=0.060) + + +# via4.1 Minimum width of Via4 +# via4.2 Minimum spacing of Via4 +drc.add_layer("via4", + width=0.800, + spacing=0.800) + +# FIXME: Wrong rules +# m5.1 Minimum width of metal5 +# m5.2 Minimum spacing of metal5 +# m5.7 Minimum area of metal5 +drc.add_layer("m5", + width=1.600, + spacing=1.600, + area=4.000) +# m5.3 Minimum enclosure around via4 +drc.add_enclosure("m5", + layer="via4", + enclosure=0.310) + + + +# Metal 5-10 are ommitted + +################################################### +# Spice Simulation Parameters +################################################### + +# spice info +spice = {} +spice["nmos"] = "sky130_fd_pr__nfet_01v8" +spice["pmos"] = "sky130_fd_pr__pfet_01v8" +spice["power"]="vccd1" +spice["ground"]="vssd1" + +# whether or not the device model is actually a subckt +spice["device_prefix"] = "X" + +spice["fet_libraries"] = { "TT": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "tt"]], + "SS": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "ss"]], + "FF": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "ff"]], + "SF": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "sf"]], + "FS": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "fs"]] } + +# spice stimulus related variables +spice["feasible_period"] = 10 # estimated feasible period in ns +spice["supply_voltages"] = [1.7, 1.8, 1.9] # Supply voltage corners in [Volts] +spice["nom_supply_voltage"] = 1.8 # Nominal supply voltage in [Volts] +spice["rise_time"] = 0.005 # rise time in [Nano-seconds] +spice["fall_time"] = 0.005 # fall time in [Nano-seconds] +spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius) +spice["nom_temperature"] = 25 # Nominal temperature (celcius) + +# analytical delay parameters +spice["nom_threshold"] = 0.49 # Typical Threshold voltage in Volts +spice["wire_unit_r"] = 0.125 # Unit wire resistance in ohms/square +spice["wire_unit_c"] = 0.134 # Unit wire capacitance ff/um^2 +spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff +spice["min_tx_gate_c"] = 0.2 # Minimum transistor gate capacitance in ff +spice["dff_setup"] = 102.5391 # DFF setup time in ps +spice["dff_hold"] = -56 # DFF hold time in ps +spice["dff_in_cap"] = 6.89 # Input capacitance (D) [Femto-farad] +spice["dff_out_cap"] = 6.89 # Output capacitance (Q) [Femto-farad] + +# analytical power parameters, many values are temporary +spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW +spice["inv_leakage"] = 1 # Leakage power of inverter in nW +spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW +spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW +spice["nand4_leakage"] = 1 # Leakage power of 4-input nand in nW +spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW +spice["dff_leakage"] = 1 # Leakage power of flop in nW + +spice["default_event_frequency"] = 100 # Default event activity of every gate. MHz + +# Parameters related to sense amp enable timing and delay chain/RBL sizing +parameter["le_tau"] = 2.25 # In pico-seconds. +parameter["cap_relative_per_ff"] = 7.5 # Units of Relative Capacitance/ Femto-Farad +parameter["dff_clk_cin"] = 30.6 # relative capacitance +parameter["6tcell_wl_cin"] = 3 # relative capacitance +parameter["min_inv_para_delay"] = 2.4 # Tau delay units +parameter["sa_en_pmos_size"] = 0.72 # micro-meters +parameter["sa_en_nmos_size"] = 0.27 # micro-meters +parameter["sa_inv_pmos_size"] = 0.54 # micro-meters +parameter["sa_inv_nmos_size"] = 0.27 # micro-meters +parameter["bitcell_drain_cap"] = 0.1 # In Femto-Farad, approximation of drain capacitance + +################################################### +# Technology Tool Preferences +################################################### + +if use_calibre: + drc_name = "calibre" + lvs_name = "calibre" + pex_name = "calibre" +elif use_klayout: + drc_name = "klayout" + lvs_name = "klayout" + pex_name = "klayout" +else: + drc_name = "magic" + lvs_name = "netgen" + pex_name = "magic" + + +# This is used by uniqify to not rename the library cells +library_prefix_name = "sky130_fd_bd_sram__" +# List of cells to skip running DRC/LVS on directly +# This will look for a maglef file and copy it over the mag file +# before DRC after extraction + +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_cell +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_ce +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_ce +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_cell +# gds flatglob sky130_fd_bd_sram__sram_sp_cell_fom_serifs + +flatglob = ["*_?mos_m*", + "sky130_fd_bd_sram__sram_sp_cell_fom_serifs", + + "sky130_fd_bd_sram__sram_sp_cell", + "sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_cell", + + "sky130_fd_bd_sram__sram_sp_cell_opt1_ce", + "sky130_fd_bd_sram__openram_sp_cell_opt1_replica_ce", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap_p_ce"] + +blackbox_cells = ["sky130_fd_bd_sram__openram_dp_cell", + "sky130_fd_bd_sram__openram_dp_cell_dummy", + "sky130_fd_bd_sram__openram_dp_cell_replica", + + "sky130_fd_bd_sram__openram_sp_cell_opt1_noblcon", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_noblcon", + "sky130_fd_bd_sram__openram_sp_colend_replica", + "sky130_fd_bd_sram__openram_sp_colenda_replica", + "sky130_fd_bd_sram__sram_sp_cell_opt1a", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy", + "sky130_fd_bd_sram__openram_sp_cell_opt1_dummy", + "sky130_fd_bd_sram__sram_sp_cell_opt1_ce", + "sky130_fd_bd_sram__sram_sp_cell_opt1", + "sky130_fd_bd_sram__openram_sp_cell_opt1_replica", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica", + "sky130_fd_bd_sram__sram_sp_colend", + "sky130_fd_bd_sram__sram_sp_colend_cent", + "sky130_fd_bd_sram__sram_sp_colend_p_cent", + "sky130_fd_bd_sram__sram_sp_colenda", + "sky130_fd_bd_sram__sram_sp_colenda_cent", + "sky130_fd_bd_sram__sram_sp_colenda_p_cent", + "sky130_fd_bd_sram__sram_sp_rowend", + "sky130_fd_bd_sram__sram_sp_rowenda", + "sky130_fd_bd_sram__openram_sp_rowend_replica", + "sky130_fd_bd_sram__openram_sp_rowenda_replica", + "sky130_fd_bd_sram__sram_sp_corner", + "sky130_fd_bd_sram__sram_sp_cornera", + "sky130_fd_bd_sram__sram_sp_cornerb", + "sky130_fd_bd_sram__sram_sp_wlstrapa", + "sky130_fd_bd_sram__sram_sp_wlstrap_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap", + "sky130_fd_bd_sram__sram_sp_wlstrap_p_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap_p"] \ No newline at end of file diff --git a/technology/sky130/tech/tech_configs/tech_cypress_cell.py b/technology/sky130/tech/tech_configs/tech_cypress_cell.py new file mode 100644 index 00000000..bfc44ff5 --- /dev/null +++ b/technology/sky130/tech/tech_configs/tech_cypress_cell.py @@ -0,0 +1,848 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California +# All rights reserved. +# + + +import os +from openram import drc as d + +""" +File containing the process technology parameters for Skywater 130nm. +""" + +################################################### +# Custom modules +################################################### + +# This uses the default classes to instantiate module from +# '$OPENRAM_HOME/compiler/modules'. +# Using tech_modules['cellname'] you can override each class by providing a custom +# implementation in '$OPENRAM_TECHDIR/modules/' +# For example: tech_modules["contact"] = "contact_freepdk45" +tech_modules = d.module_type() + +# These modules have been hand designed and provided in this repository. +tech_modules["nand2_dec"] = "nand2_dec" +tech_modules["nand3_dec"] = "nand3_dec" +tech_modules["nand4_dec"] = "nand4_dec" + +# Override default OpenRAM modules to sky130 modules +# These are for single port and dual port as a list, +# or for both if there is no list, +# or only applicable to one if there is no list. +tech_modules["bitcell_1port"] = "sky130_bitcell" +tech_modules["replica_bitcell_1port"] = "sky130_replica_bitcell" +tech_modules["dummy_bitcell_1port"] = "sky130_dummy_bitcell" + +tech_modules["replica_bitcell_2port"] = "replica_bitcell_2port" +tech_modules["dummy_bitcell_2port"] = "dummy_bitcell_2port" +tech_modules["bitcell_2port"] = "bitcell_2port" + +tech_modules["bitcell_array"] = ["sky130_bitcell_array", "bitcell_array"] +tech_modules["replica_bitcell_array"] = ["sky130_replica_bitcell_array", "replica_bitcell_array"] +tech_modules["capped_replica_bitcell_array"] = ["sky130_capped_replica_bitcell_array", "capped_replica_bitcell_array"] +tech_modules["dummy_array"] = ["sky130_dummy_array", "dummy_array"] + +tech_modules["replica_column"] = ["sky130_replica_column", "replica_column"] + +tech_modules["col_cap_array"] = ["sky130_col_cap_array", "col_cap_array"] +tech_modules["col_cap"] = ["sky130_col_cap", "col_cap_bitcell_2port"] +tech_modules["corner"] = ["sky130_corner", None] +tech_modules["internal"] = ["sky130_internal", None] +tech_modules["row_cap_array"] = ["sky130_row_cap_array", "row_cap_array"] +tech_modules["row_cap"] = ["sky130_row_cap", "row_cap_bitcell_2port"] + +# These modules are auto-generated from the nand decoders above and are not +# found in this. +tech_modules["buf_dec"] = "pbuf_dec" +tech_modules["inv_dec"] = "pinv_dec" +tech_modules["and2_dec"] = "and2_dec" +tech_modules["and3_dec"] = "and3_dec" +tech_modules["and4_dec"] = "and4_dec" + +################################################### +# Custom cell properties +################################################### +cell_properties = d.cell_properties() + +cell_properties.bitcell_power_pin_directions = ("H", "H") + +cell_properties.bitcell_1port.mirror.x = True +cell_properties.bitcell_1port.mirror.y = True +cell_properties.bitcell_1port.end_caps = True +cell_properties.bitcell_1port.has_corners = True +cell_properties.bitcell_1port.boundary_layer = "mem" +cell_properties.bitcell_1port.port_order = ['bl', 'br', 'gnd', 'vdd', 'vpb', 'vnb', 'wl'] +cell_properties.bitcell_1port.port_types = ["OUTPUT", "OUTPUT", "GROUND", "POWER", "BIAS", "BIAS", "INPUT"] +cell_properties.bitcell_1port.port_map = {'bl': 'BL', + 'br': 'BR', + 'wl': 'WL', + 'vdd': 'VPWR', + 'vnb': 'VNB', + 'vpb': 'VPB', + 'gnd': 'VGND'} + +cell_properties.bitcell_1port.wl_layer = "m2" +cell_properties.bitcell_1port.bl_layer = "m1" +cell_properties.bitcell_1port.vdd_layer = "m1" +cell_properties.bitcell_1port.vdd_dir = "V" +cell_properties.bitcell_1port.gnd_layer = "m2" +cell_properties.bitcell_1port.gnd_dir = "H" + +cell_properties.bitcell_2port.mirror.x = True +cell_properties.bitcell_2port.mirror.y = True +cell_properties.bitcell_2port.end_caps = True +cell_properties.bitcell_2port.has_corners = True +cell_properties.bitcell_2port.port_order = ['bl0', 'br0', 'bl1', 'br1', 'wl0', 'wl1', 'vdd', 'gnd'] +cell_properties.bitcell_2port.port_map = {'bl0': 'BL0', + 'br0': 'BR0', + 'bl1': 'BL1', + 'br1': 'BR1', + 'wl0': 'WL0', + 'wl1': 'WL1', + 'vdd': 'VDD', + 'gnd': 'GND'} +cell_properties.bitcell_1port.wl_layer = "m2" +cell_properties.bitcell_1port.vdd_layer = "m2" +cell_properties.bitcell_1port.vdd_dir = "H" +cell_properties.bitcell_1port.gnd_layer = "m2" +cell_properties.bitcell_1port.gnd_dir = "H" +cell_properties.bitcell_2port.wl_layer = "m2" +cell_properties.bitcell_2port.vdd_layer = "m1" +cell_properties.bitcell_2port.vdd_dir = "H" +cell_properties.bitcell_2port.gnd_layer = "m2" +cell_properties.bitcell_2port.gnd_dir = "H" + +cell_properties.col_cap_1port_bitcell = 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', + 'gate': 'gate'}) +cell_properties.col_cap_1port_bitcell.boundary_layer = "mem" + +cell_properties.col_cap_1port_strap_power = d.cell(['vdd', 'vpb', 'vnb'], + ['POWER', 'BIAS', 'BIAS'], + {'vnb': 'VNB', + 'vpb': 'VPB', + 'vdd': 'VPWR'}) +cell_properties.col_cap_1port_strap_power.boundary_layer = "mem" + +cell_properties.col_cap_1port_strap_ground = d.cell(['gnd', 'vpb', 'vnb'], + ['GROUND', 'BIAS', 'BIAS'], + {'vnb': 'VNB', + 'vpb': 'VPB', + 'gnd': 'VGND'}) +cell_properties.col_cap_1port_strap_ground.boundary_layer = "mem" + +cell_properties.row_cap_1port_cell = d.cell(['vdd', 'wl'], + ['POWER', 'INPUT'], + {'wl': 'WL', + 'vdd': 'VPWR'}) +cell_properties.row_cap_1port_cell.boundary_layer = "mem" + +cell_properties.col_cap_2port.port_order = ['bl0', 'br0', 'bl1', 'br1', 'vdd'] +cell_properties.col_cap_2port.port_map = {'bl0': 'BL0', + 'br0': 'BR0', + 'bl1': 'BL1', + 'br1': 'BR1', + 'vdd': 'VDD'} + +cell_properties.row_cap_2port.port_order = ['wl0', 'wl1', 'gnd'] +cell_properties.row_cap_2port.port_map = {'wl0': 'WL0', + 'wl1': 'WL1', + 'gnd': 'GND'} + + +cell_properties.ptx.bin_spice_models = True +cell_properties.ptx.model_is_subckt = True + +cell_properties.pgate.add_implants = True + +cell_properties.use_strap = True +cell_properties.strap_module = "internal" +cell_properties.strap_version = "wlstrap" + +cell_properties.dff.port_order = ['D', 'Q', 'clk', 'vdd', 'gnd'] +cell_properties.dff.port_map = {'D': 'D', + 'Q': 'Q', + 'clk': 'CLK', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.nand2_dec.port_order = ['A', 'B', 'Z', 'vdd', 'gnd'] +cell_properties.nand2_dec.port_map = {'A': 'A', + 'B': 'B', + 'Z': 'Z', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.nand3_dec.port_order = ['A', 'B', 'C', 'Z', 'vdd', 'gnd'] +cell_properties.nand3_dec.port_map = {'A': 'A', + 'B': 'B', + 'C': 'C', + 'Z': 'Z', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.nand4_dec.port_order = ['A', 'B', 'C', 'D', 'Z', 'vdd', 'gnd'] +cell_properties.nand4_dec.port_map = {'A': 'A', + 'B': 'B', + 'C': 'C', + 'D': 'D', + 'Z': 'Z', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.sense_amp.port_order = ['bl', 'br', 'dout', 'en', 'vdd', 'gnd'] +cell_properties.sense_amp.port_map = {'bl': 'BL', + 'br': 'BR', + 'dout': 'DOUT', + 'en': 'EN', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.write_driver.port_order = ['din', 'bl', 'br', 'en', 'vdd', 'gnd'] +cell_properties.write_driver.port_map = {'din': 'DIN', + 'bl': 'BL', + 'br': 'BR', + 'en': 'EN', + 'vdd': 'VDD', + 'gnd': 'GND'} + + +# You can override the GDS for custom cell using the following: +# If it is a list, the first is single port and the second is dual port. +# If it is string, it is used for both single and dual port. +cell_properties.names["dff"] = "sky130_fd_bd_sram__openram_dff" +cell_properties.names["nand2_dec"] = ["sky130_fd_bd_sram__openram_sp_nand2_dec", "sky130_fd_bd_sram__openram_dp_nand2_dec"] +cell_properties.names["nand3_dec"] = ["sky130_fd_bd_sram__openram_sp_nand3_dec", "sky130_fd_bd_sram__openram_dp_nand3_dec"] +cell_properties.names["nand4_dec"] = ["sky130_fd_bd_sram__openram_sp_nand4_dec", "sky130_fd_bd_sram__openram_dp_nand4_dec"] + +cell_properties.names["bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell" +cell_properties.names["dummy_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_dummy" +cell_properties.names["replica_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_replica" +cell_properties.names["col_cap_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_cap_col" +cell_properties.names["row_cap_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_cap_row" +cell_properties.names["sense_amp"] = "sky130_fd_bd_sram__openram_sense_amp" +cell_properties.names["write_driver"] = "sky130_fd_bd_sram__openram_write_driver" + +array_row_multiple = 2 +array_col_multiple = 2 + +################################################### +# Custom layer properties +################################################### +layer_properties = d.layer_properties() +layer_properties.hierarchical_decoder.bus_layer = "m1" +layer_properties.hierarchical_decoder.bus_directions = "nonpref" +layer_properties.hierarchical_decoder.input_layer = "li" +layer_properties.hierarchical_decoder.output_layer = "m2" +layer_properties.hierarchical_decoder.vertical_supply = True + +layer_properties.hierarchical_predecode.bus_layer = "m1" +layer_properties.hierarchical_predecode.bus_directions = "nonpref" +# This is added to allow the column decoder connections on m2 +layer_properties.hierarchical_predecode.bus_pitch_factor = 1.2 +layer_properties.hierarchical_predecode.bus_space_factor = 1.5 +layer_properties.hierarchical_predecode.input_layer = "li" +layer_properties.hierarchical_predecode.output_layer = "m2" +layer_properties.hierarchical_predecode.vertical_supply = True +layer_properties.hierarchical_predecode.force_horizontal_input_contact = True + +layer_properties.bank.stack = "m2_stack" +layer_properties.bank.pitch = "m3_pitch" + +layer_properties.column_mux_array.select_layer = "m3" +layer_properties.column_mux_array.bitline_layer = "m1" + +layer_properties.port_address.supply_offset = True + +layer_properties.port_data.enable_layer = "m1" +layer_properties.port_data.channel_route_bitlines = False + +layer_properties.replica_column.even_rows = True + +layer_properties.wordline_driver.vertical_supply = True + +layer_properties.global_wordline_layer = "m5" + + +################################################### +# Discrete tx bins +################################################### +# enforce that tx sizes are within 25% of requested size after fingering. +accuracy_requirement = 0.75 +nmos_bins = { + 0.15 : [0.36, 0.39, 0.42, 0.52, 0.54, 0.55, 0.58, 0.6, 0.61, 0.64, 0.65, 0.74, 0.84, 1.0, 1.26, 1.68, 2.0, 3.0, 5.0, 7.0], + 0.18 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 0.25 : [0.65, 1.0, 3.0, 5.0, 7.0], + 0.5 : [0.42, 0.55, 0.65, 1.0, 3.0, 5.0, 7.0], + 1.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 2.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 4.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 8.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 20.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0] +} + +pmos_bins = { + 0.15 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12, 1.26, 1.65, 1.68, 2.0, 3.0, 5.0, 7.0], + 1.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 2.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 4.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 8.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 0.17 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12], + 0.18 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12, 1.26, 1.68, 2.0, 3.0, 5.0, 7.0], + 0.25 : [1.0, 3.0, 5.0, 7.0], + 0.5 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 20.0 : [0.42] +} +################################################### +# GDS file info +################################################### +GDS = {} +# gds units +# From http://www.cnf.cornell.edu/cnf_spie9.html: "The first +# is the size of a database unit in user units. The second is the size +# of a database unit in meters. For example, if your library was +# created with the default units (user unit = 1 um and 1000 database +# units per user unit), then the first number would be 0.001 and the +# second number would be 10-9. Typically, the first number is less than +# 1, since you use more than 1 database unit per user unit. To +# calculate the size of a user unit in meters, divide the second number +# by the first." +GDS["unit"] = (0.001, 1e-9) +#GDS["unit"]=(0.001, 1e-6) + +################################################### +# Interconnect stacks +################################################### + +poly_stack = ("poly", "contact", "li") +active_stack = ("active", "contact", "li") +li_stack = ("li", "mcon", "m1") +m1_stack = ("m1", "via1", "m2") +m2_stack = ("m2", "via2", "m3") +m3_stack = ("m3", "via3", "m4") +m4_stack = ("m4", "via4", "m5") + +lef_rom_interconnect = ["m1", "m2", "m3", "m4"] + +layer_indices = {"poly": 0, + "active": 0, + "nwell": 0, + "li": 1, + "m1": 2, + "m2": 3, + "m3": 4, + "m4": 5, + "m5": 6} + +# The FEOL stacks get us up to m1 +feol_stacks = [poly_stack, + active_stack, + li_stack] + +# The BEOL stacks are m1 and up +beol_stacks = [m1_stack, + m2_stack, + m3_stack, + m4_stack] + +layer_stacks = feol_stacks + beol_stacks + +preferred_directions = {"poly": "V", + "active": "V", + "li": "V", + "m1": "H", + "m2": "V", + "m3": "H", + "m4": "V", + "m5": "H"} + +################################################### +# GDS Layer Map +################################################### + + +layer = {} +layer["active"] = (65, 20) # diff +layer["activep"] = (65, 20) # diff +layer["tap"] = (65, 44) # tap +layer["pwellp"] = (122,16) +layer["nwell"] = (64, 20) # nwell +layer["dnwell"] = (64,18) +layer["nimplant"]= (93, 44) # nsdm +layer["pimplant"]= (94, 20) # psdm +layer["vtl"] = (125, 44) # lvtn +layer["vth"] = (78, 44) # hvtp (pmos only) +layer["thkox"] = (8, 0) +layer["poly"] = (66, 20) +layer["contact"] = (66, 44) # licon1 +layer["npc"] = (95, 20) # npc (nitride cut) +layer["li"] = (67, 20) # active li1 +layer["mcon"] = (67, 44) # mcon +layer["m1"] = (68, 20) # met1 +layer["m1p"] = (68, 5) # met1 pin +layer["via1"] = (68, 44) # via1 +layer["m2"] = (69, 20) # met2 +layer["m2p"] = (69, 5) # met2 pin +layer["via2"] = (69, 44) # via2 +layer["m3"] = (70, 20) # met3 +layer["m3p"] = (70, 5) # met3 pin +layer["via3"] = (70, 44) # via3 +layer["m4"] = (71, 20) # met4 +layer["m4p"] = (71, 5) # met4 pin +layer["via4"] = (71, 44) # via4 +layer["m5"] = (72, 20) # met5 +layer["m5p"] = (72, 5) # met5 pin +layer["boundary"]= (235, 4) +# specific boundary type to define standard cell regions for DRC +layer["stdc"] = (81, 4) +layer["mem"] = (81, 2) +# Not an official sky130 layer, but useful for router debug infos +layer["text"]= (234, 5) +# Excpected value according to sky130A tech file +# If calibre is enabled, these will be swapped below +#pin_purpose = 5 +label_purpose = 5 +#label_purpose = 16 +#pin_purpose = 16 +#label_purpose = 5 + +# pin_read purposes +special_purposes = {layer["nwell"][0]: [layer["nwell"][1], 5, 59, 16]} +#layer_override = {"VNB\x00": ["pwell",122]} +layer_override = {"vnb": layer["pwellp"], "VNB": layer["pwellp"]} +layer_override_name = {"vnb": "pwellp", "VNB": "pwellp"} +layer_override_purpose = {122: (64, 59)} +# Layer names for external PDKs +layer_names = {} +layer_names["active"] = "diff" +layer_names["activep"] = "diff" +layer_names["tap"] = "tap" +layer_names["pwellp"] = "pwellp" +layer_names["nwell"] = "nwell" +layer_names["dnwell"] = "dnwell" +layer_names["nimplant"]= "nsdm" +layer_names["pimplant"]= "psdm" +layer_names["vtl"] = "lvtn" +layer_names["vth"] = "hvtp" +layer_names["thkox"] = "thkox" +layer_names["poly"] = "poly" +layer_names["contact"] = "licon1" +layer_names["li"] = "li1" +layer_names["mcon"] = "mcon" +layer_names["m1"] = "met1" +layer_names["m1p"] = "met1" +layer_names["via1"] = "via" +layer_names["m2"] = "met2" +layer_names["m2p"] = "met2" +layer_names["via2"] = "via2" +layer_names["m3"] = "met3" +layer_names["m3p"] = "met3" +layer_names["via3"] = "via3" +layer_names["m4"] = "met4" +layer_names["m4p"] = "met4" +layer_names["via4"] = "via4" +layer_names["m5p"] = "met5" +layer_names["boundary"]= "boundary" +layer_names["stdc"] = "areaid.standardc" +layer_names["mem"] = "areaid.core" +layer_names["text"] = "text" + + +################################################### +# DRC/LVS Rules Setup +################################################### + +# technology parameter +parameter={} +# difftap.2b +parameter["min_tx_size"] = 0.150 +parameter["beta"] = 3 + +parameter["6T_inv_nmos_size"] = 0.205 +parameter["6T_inv_pmos_size"] = 0.09 +parameter["6T_access_size"] = 0.135 + +drc = d.design_rules("sky130") + +# grid size +drc["grid"] = 0.005 + +#DRC/LVS test set_up +# Switching between calibre and magic can be useful for development, +# it eventually should be deleted. +NDA_PDK_ROOT = os.environ.get("NDA_PDK_ROOT", False) +use_calibre = bool(NDA_PDK_ROOT) +use_calibre = False +use_klayout = False +if use_calibre: + # Correct order according to s8 + pin_purpose = 16 + label_purpose = 5 + + drc["drc_rules"] = NDA_PDK_ROOT + "/DRC/Calibre/s8_drcRules" + drc["lvs_rules"] = NDA_PDK_ROOT + "/LVS/Calibre/lvs_s8_opts" + drc["xrc_rules"] = NDA_PDK_ROOT + "/PEX/xRC/extLvsRules_s8_5lm" + drc["layer_map"] = NDA_PDK_ROOT + "/VirtuosoOA/libs/technology_library/s8phirs_10r.layermap" + +# minwidth_tx with contact (no dog bone transistors) +# difftap.2b +drc["minwidth_tx"] = 0.360 +drc["minlength_channel"] = 0.150 + +drc["pwell_to_nwell"] = 0 +# nwell.1 Minimum width of nwell/pwell +drc.add_layer("nwell", + width=0.840, + spacing=1.270) + +# poly.1a Minimum width of poly +# poly.2 Minimum spacing of poly AND active +drc.add_layer("poly", + width=0.150, + spacing=0.210) +# poly.8 +drc["poly_extend_active"] = 0.13 +# Not a rule +drc["poly_to_contact"] = 0 +# poly.7 Minimum enclosure of active around gate +drc["active_enclose_gate"] = 0.075 +# poly.4 Minimum spacing of field poly to active +drc["poly_to_active"] = 0.075 +# poly.2 Minimum spacing of field poly +drc["poly_to_field_poly"] = 0.210 + +# difftap.1 Minimum width of active +# difftap.3 Minimum spacing of active +drc.add_layer("active", + width=0.150, + spacing=0.270) +# difftap.8 +drc.add_enclosure("nwell", + layer="active", + enclosure=0.18, + extension=0.18) + +# nsd/psd.5a +drc.add_enclosure("implant", + layer="active", + enclosure=0.125) + +# Same as active enclosure? +drc["implant_to_contact"] = 0.070 +# nsd/psd.1 nsd/psd.2 +drc.add_layer("implant", + width=0.380, + spacing=0.380, + area=0.265) + +# licon.1, licon.2 +drc.add_layer("contact", + width=0.170, + spacing=0.170) +# licon.5c (0.06 extension), (licon.7 for extension) +drc.add_enclosure("active", + layer="contact", + enclosure=0.040, + extension=0.060) +# licon.7 +drc["tap_extend_contact"] = 0.120 + +# licon.8 Minimum enclosure of poly around contact +drc.add_enclosure("poly", + layer="contact", + enclosure=0.08, + extension=0.08) +# licon.11a +drc["active_contact_to_gate"] = 0.050 +# npc.4 > licon.14 0.19 > licon.11a +drc["poly_contact_to_gate"] = 0.270 +# licon.15 +drc["npc_enclose_poly"] = 0.1 + +# li.1, li.3 +drc.add_layer("li", + width=0.170, + spacing=0.170) + +# licon.5 +drc.add_enclosure("li", + layer="contact", + enclosure=0, + extension=0.080) + +drc.add_enclosure("li", + layer="mcon", + enclosure=0, + extension=0.080) +# mcon.1, mcon.2 +drc.add_layer("mcon", + width=0.170, + spacing=0.210) + +# m1.1 Minimum width of metal1 +# m1.2 Minimum spacing of metal1 +# m1.6 Minimum area of metal1 +drc.add_layer("m1", + width=0.140, + spacing=0.140, + area=0.083) +# m1.4 Minimum enclosure of metal1 +# m1.5 Minimum enclosure around contact on two opposite sides +drc.add_enclosure("m1", + layer="mcon", + enclosure=0.030, + extension=0.060) +# via.4a Minimum enclosure around via1 +# via.5a Minimum enclosure around via1 on two opposite sides +drc.add_enclosure("m1", + layer="via1", + enclosure=0.055, + extension=0.085) + +# via.1a Minimum width of via1 +# via.2 Minimum spacing of via1 +drc.add_layer("via1", + width=0.150, + spacing=0.170) + +# m2.1 Minimum width of intermediate metal +# m2.2 Minimum spacing of intermediate metal +# m2.6 Minimum area of metal2 +drc.add_layer("m2", + width=0.140, + spacing=0.140, + area=0.0676) +# m2.4 Minimum enclosure around via1 +# m2.5 Minimum enclosure around via1 on two opposite sides +drc.add_enclosure("m2", + layer="via1", + enclosure=0.055, + extension=0.085) +# via2.4 Minimum enclosure around via2 +# via2.5 Minimum enclosure around via2 on two opposite sides +drc.add_enclosure("m2", + layer="via2", + enclosure=0.040, + extension=0.085) + +# via2.1a Minimum width of Via2 +# via2.2 Minimum spacing of Via2 +drc.add_layer("via2", + width=0.200, + spacing=0.200) + +# m3.1 Minimum width of metal3 +# m3.2 Minimum spacing of metal3 +# m3.6 Minimum area of metal3 +drc.add_layer("m3", + width=0.300, + spacing=0.300, + area=0.240) +# m3.4 Minimum enclosure around via2 +drc.add_enclosure("m3", + layer="via2", + enclosure=0.065) +# via3.4 Minimum enclosure around via3 +# via3.5 Minimum enclosure around via3 on two opposite sides +drc.add_enclosure("m3", + layer="via3", + enclosure=0.060, + extension=0.090) + +# via3.1 Minimum width of Via3 +# via3.2 Minimum spacing of Via3 +drc.add_layer("via3", + width=0.200, + spacing=0.200) + +# m4.1 Minimum width of metal4 +# m4.2 Minimum spacing of metal4 +# m4.7 Minimum area of metal4 +drc.add_layer("m4", + width=0.300, + spacing=0.300, + area=0.240) +# m4.3 Minimum enclosure around via3 +drc.add_enclosure("m4", + layer="via3", + enclosure=0.065) +# FIXME: Wrong rule m4.3 Minimum enclosure around via3 +drc.add_enclosure("m4", + layer="via4", + enclosure=0.060) + + +# via4.1 Minimum width of Via4 +# via4.2 Minimum spacing of Via4 +drc.add_layer("via4", + width=0.800, + spacing=0.800) + +# FIXME: Wrong rules +# m5.1 Minimum width of metal5 +# m5.2 Minimum spacing of metal5 +# m5.7 Minimum area of metal5 +drc.add_layer("m5", + width=1.600, + spacing=1.600, + area=4.000) +# m5.3 Minimum enclosure around via4 +drc.add_enclosure("m5", + layer="via4", + enclosure=0.310) + + + +# Metal 5-10 are ommitted + +################################################### +# Spice Simulation Parameters +################################################### + +# spice info +spice = {} +spice["nmos"] = "sky130_fd_pr__nfet_01v8" +spice["pmos"] = "sky130_fd_pr__pfet_01v8" +spice["power"]="vccd1" +spice["ground"]="vssd1" + +# whether or not the device model is actually a subckt +spice["device_prefix"] = "X" + +spice["fet_libraries"] = { "TT": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "tt"]], + "SS": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "ss"]], + "FF": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "ff"]], + "SF": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "sf"]], + "FS": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "fs"]] } + +# spice stimulus related variables +spice["feasible_period"] = 10 # estimated feasible period in ns +spice["supply_voltages"] = [1.7, 1.8, 1.9] # Supply voltage corners in [Volts] +spice["nom_supply_voltage"] = 1.8 # Nominal supply voltage in [Volts] +spice["rise_time"] = 0.005 # rise time in [Nano-seconds] +spice["fall_time"] = 0.005 # fall time in [Nano-seconds] +spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius) +spice["nom_temperature"] = 25 # Nominal temperature (celcius) + +# analytical delay parameters +spice["nom_threshold"] = 0.49 # Typical Threshold voltage in Volts +spice["wire_unit_r"] = 0.125 # Unit wire resistance in ohms/square +spice["wire_unit_c"] = 0.134 # Unit wire capacitance ff/um^2 +spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff +spice["min_tx_gate_c"] = 0.2 # Minimum transistor gate capacitance in ff +spice["dff_setup"] = 102.5391 # DFF setup time in ps +spice["dff_hold"] = -56 # DFF hold time in ps +spice["dff_in_cap"] = 6.89 # Input capacitance (D) [Femto-farad] +spice["dff_out_cap"] = 6.89 # Output capacitance (Q) [Femto-farad] + +# analytical power parameters, many values are temporary +spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW +spice["inv_leakage"] = 1 # Leakage power of inverter in nW +spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW +spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW +spice["nand4_leakage"] = 1 # Leakage power of 4-input nand in nW +spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW +spice["dff_leakage"] = 1 # Leakage power of flop in nW + +spice["default_event_frequency"] = 100 # Default event activity of every gate. MHz + +# Parameters related to sense amp enable timing and delay chain/RBL sizing +parameter["le_tau"] = 2.25 # In pico-seconds. +parameter["cap_relative_per_ff"] = 7.5 # Units of Relative Capacitance/ Femto-Farad +parameter["dff_clk_cin"] = 30.6 # relative capacitance +parameter["6tcell_wl_cin"] = 3 # relative capacitance +parameter["min_inv_para_delay"] = 2.4 # Tau delay units +parameter["sa_en_pmos_size"] = 0.72 # micro-meters +parameter["sa_en_nmos_size"] = 0.27 # micro-meters +parameter["sa_inv_pmos_size"] = 0.54 # micro-meters +parameter["sa_inv_nmos_size"] = 0.27 # micro-meters +parameter["bitcell_drain_cap"] = 0.1 # In Femto-Farad, approximation of drain capacitance + +################################################### +# Technology Tool Preferences +################################################### + +if use_calibre: + drc_name = "calibre" + lvs_name = "calibre" + pex_name = "calibre" +elif use_klayout: + drc_name = "klayout" + lvs_name = "klayout" + pex_name = "klayout" +else: + drc_name = "magic" + lvs_name = "netgen" + pex_name = "magic" + + +# This is used by uniqify to not rename the library cells +library_prefix_name = "sky130_fd_bd_sram__" +# List of cells to skip running DRC/LVS on directly +# This will look for a maglef file and copy it over the mag file +# before DRC after extraction + +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_cell +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_ce +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_ce +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_cell +# gds flatglob sky130_fd_bd_sram__sram_sp_cell_fom_serifs + +flatglob = ["*_?mos_m*", + "sky130_fd_bd_sram__sram_sp_cell_fom_serifs", + + "sky130_fd_bd_sram__sram_sp_cell", + "sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_cell", + + "sky130_fd_bd_sram__sram_sp_cell_opt1_ce", + "sky130_fd_bd_sram__openram_sp_cell_opt1_replica_ce", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap_p_ce"] + +blackbox_cells = ["sky130_fd_bd_sram__openram_dp_cell", + "sky130_fd_bd_sram__openram_dp_cell_dummy", + "sky130_fd_bd_sram__openram_dp_cell_replica", + + "sky130_fd_bd_sram__openram_sp_cell_opt1_noblcon", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_noblcon", + "sky130_fd_bd_sram__openram_sp_colend_replica", + "sky130_fd_bd_sram__openram_sp_colenda_replica", + "sky130_fd_bd_sram__sram_sp_cell_opt1a", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy", + "sky130_fd_bd_sram__openram_sp_cell_opt1_dummy", + "sky130_fd_bd_sram__sram_sp_cell_opt1_ce", + "sky130_fd_bd_sram__sram_sp_cell_opt1", + "sky130_fd_bd_sram__openram_sp_cell_opt1_replica", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica", + "sky130_fd_bd_sram__sram_sp_colend", + "sky130_fd_bd_sram__sram_sp_colend_cent", + "sky130_fd_bd_sram__sram_sp_colend_p_cent", + "sky130_fd_bd_sram__sram_sp_colenda", + "sky130_fd_bd_sram__sram_sp_colenda_cent", + "sky130_fd_bd_sram__sram_sp_colenda_p_cent", + "sky130_fd_bd_sram__sram_sp_rowend", + "sky130_fd_bd_sram__sram_sp_rowenda", + "sky130_fd_bd_sram__openram_sp_rowend_replica", + "sky130_fd_bd_sram__openram_sp_rowenda_replica", + "sky130_fd_bd_sram__sram_sp_corner", + "sky130_fd_bd_sram__sram_sp_cornera", + "sky130_fd_bd_sram__sram_sp_cornerb", + "sky130_fd_bd_sram__sram_sp_wlstrapa", + "sky130_fd_bd_sram__sram_sp_wlstrap_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap", + "sky130_fd_bd_sram__sram_sp_wlstrap_p_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap_p"] \ No newline at end of file From c7f3ac33cd527f8eff5d7e06dad74f2cc1fd869b Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Mon, 27 Apr 2026 17:24:13 -0700 Subject: [PATCH 57/61] sky130 cypress dp working with offset relative to crba --- Makefile | 2 +- compiler/modules/bitcell_array.py | 8 +++--- .../modules/capped_replica_bitcell_array.py | 2 ++ compiler/modules/col_cap_array.py | 4 +-- compiler/modules/dummy_array.py | 8 +++--- compiler/modules/replica_bitcell_array.py | 27 ++++++++++--------- compiler/modules/replica_column.py | 18 +++++++------ compiler/modules/row_cap_array.py | 15 ++++++----- .../tech/tech_configs/tech_cypress_cell.py | 2 +- 9 files changed, 47 insertions(+), 39 deletions(-) diff --git a/Makefile b/Makefile index 90707053..d64c41b6 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 ?= 3ad211667d2b7ee0d1092dcc204e6da5a2a3886c +SRAM_LIB_GIT_COMMIT ?= fc63b12883b4bf458ee8c756ba64c37063e1ffb9 SKY130_PDK ?= $(PDK_ROOT)/sky130A GF180_PDK ?= $(PDK_ROOT)/gf180mcuD diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index e2292df5..c2948059 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -64,10 +64,10 @@ class bitcell_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 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(0+c)%2}", mod=self.cell, is_bitcell=True, mirror='XY') - core_block[(0 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(1+c)%2}", mod=self.cell, is_bitcell=True, mirror='MX') - core_block[(1 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(0+c)%2}", mod=self.cell, is_bitcell=True, mirror='MY') - core_block[(1 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(1+c)%2}", mod=self.cell, is_bitcell=True, mirror='') + core_block[(0 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(0+c)%2}", mod=self.cell, is_bitcell=True, mirror='') + core_block[(0 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(1+c)%2}", mod=self.cell, is_bitcell=True, mirror='MY') + core_block[(1 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(0+c)%2}", mod=self.cell, is_bitcell=True, mirror='MX') + core_block[(1 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(1+c)%2}", 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.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) diff --git a/compiler/modules/capped_replica_bitcell_array.py b/compiler/modules/capped_replica_bitcell_array.py index c255f0d3..d87ad247 100644 --- a/compiler/modules/capped_replica_bitcell_array.py +++ b/compiler/modules/capped_replica_bitcell_array.py @@ -73,6 +73,8 @@ class capped_replica_bitcell_array(bitcell_base_array): cols=self.column_size, rows=self.row_size, rbl=self.rbl, + column_offset=1, + row_offset=1, left_rbl=self.left_rbl, right_rbl=self.right_rbl) diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index dffe9459..0d60dc57 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -65,10 +65,10 @@ class col_cap_array(bitcell_base_array): def create_instances(self): """ Create the module instances used in this design """ self.cell_inst={} - if self.location == "top": + if self.row_offset % 2 == 0: bit_row = [geometry.instance("00_colend", mod=self.colend, is_bitcell=True, mirror="MY")]\ + [geometry.instance("01_colend", mod=self.colend, is_bitcell=True)] - elif self.location == "bottom": + else: bit_row = [geometry.instance("00_colend", mod=self.colend, is_bitcell=True, mirror="XY")]\ + [geometry.instance("01_colend", mod=self.colend, is_bitcell=True, mirror="MX")] diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 85970e6f..b6b72913 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -59,10 +59,10 @@ class dummy_array(bitcell_base_array): c = self.column_offset if self.cell.mirror.y: core_block = [[0 for x in range(2)] for y in range(2)] - core_block[(0 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(0+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='XY') - core_block[(0 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(1+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - core_block[(1 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(0+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='MY') - core_block[(1 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(1+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='') + core_block[(0 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(0+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='') + core_block[(0 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(1+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='MY') + core_block[(1 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(0+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + core_block[(1 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(1+c)%2}", 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.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index e338a48b..433f03f5 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -24,8 +24,8 @@ class replica_bitcell_array(bitcell_base_array): Requires a regular bitcell array and (if using replica topology) 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, row_offset=0) + def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, column_offset=0, row_offset=0, name=""): + super().__init__(name=name, rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset) debug.info(1, "Creating {0} {1} x {2} rbls: {3} left_rbl: {4} right_rbl: {5}".format(self.name, rows, cols, @@ -35,6 +35,9 @@ class replica_bitcell_array(bitcell_base_array): 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_offset=column_offset + self.row_offset=row_offset + self.column_size = cols self.row_size = rows # This is how many RBLs are in all the arrays @@ -76,8 +79,8 @@ 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=len(self.left_rbl)+ 1, #add 1 to account for left row_cap - row_offset=len(self.left_rbl)+1, #add 1 to account for bottom col_cap + column_offset=len(self.left_rbl)+ self.column_offset, + row_offset=len(self.left_rbl)+ self.row_offset, cols=self.column_size, rows=self.row_size, left_rbl=self.left_rbl, @@ -92,18 +95,18 @@ 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 = 1 + rbc_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 - column_offset = len(self.left_rbl) + self.column_size + 1 + rbc_offset = len(self.left_rbl) + self.column_size else: continue self.replica_columns[port] = factory.create(module_type="replica_column", rows=self.row_size, rbl=self.rbl, - column_offset=column_offset, + column_offset=rbc_offset + self.column_offset, replica_bit=replica_bit) # Dummy row (for replica wordlines) @@ -111,17 +114,17 @@ class replica_bitcell_array(bitcell_base_array): for port in self.all_ports: if port in self.left_rbl: - row_offset = 0 + dummy_offset = 0 elif port in self.right_rbl: - row_offset = self.row_size + len(self.left_rbl) + dummy_offset = self.row_size + len(self.left_rbl) else: - row_offset = 0 + dummy_offset = 0 self.dummy_rows[port] = factory.create(module_type="dummy_array", cols=self.column_size, rows=1, - row_offset=row_offset+1, #add 1 to account for bottom col_cap - column_offset=len(self.left_rbl)+1) #add 1 to account for left row_cap + row_offset=dummy_offset + self.row_offset, + column_offset=len(self.left_rbl) + self.row_offset) def add_pins(self): diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 855def17..d3790050 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -94,26 +94,28 @@ class replica_column(bitcell_base_array): # 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: - core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MX') + core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MY') else: - core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True) + core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='XY') else: if current_row % 2 == 0: - core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='MX') + core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='MY') else: - core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True) + core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='XY') current_row += 1 - + if self.cell.mirror.y: 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' + if core_block[row][0].mirror=='MY': + core_block[row][0].mirror='' else: - core_block[row][0].mirror='MY' + core_block[row][0].mirror='MX' + 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() diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index c4ad50ce..dc2ba951 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -19,6 +19,7 @@ class row_cap_array(bitcell_base_array): self.mirror = mirror self.location = location self.row_offset = row_offset + self.column_offset = column_offset #self.no_instances = True self.create_netlist() if not OPTS.netlist_only: @@ -58,19 +59,19 @@ class row_cap_array(bitcell_base_array): bit_block = [] - if self.location == "left": + if self.column_offset % 2 == 0: #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.row_cap, is_bitcell=True, mirror="") - rowend_m = geometry.instance("row_cap_rowend_m", mod=self.row_cap, is_bitcell=True, mirror="MX") - elif self.location == "right": + rowend = geometry.instance("row_cap_rowend", mod=self.row_cap, is_bitcell=True, mirror="MX") + rowend_m = geometry.instance("row_cap_rowend_m", mod=self.row_cap, is_bitcell=True, mirror="") + else: #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.row_cap, is_bitcell=True, mirror="MY") - rowend_m = geometry.instance("row_cap_rowend_m", mod=self.row_cap, is_bitcell=True, mirror="XY") + rowend = geometry.instance("row_cap_rowend", mod=self.row_cap, is_bitcell=True, mirror="XY") + rowend_m = geometry.instance("row_cap_rowend_m", mod=self.row_cap, is_bitcell=True, mirror="MY") #pattern.append_row_to_block(bit_block, [top_corner]) for row in range(0, self.row_size): - if row % 2 == 1: + if row % 2 == 0: pattern.append_row_to_block(bit_block, [rowend]) else: pattern.append_row_to_block(bit_block, [rowend_m]) diff --git a/technology/sky130/tech/tech_configs/tech_cypress_cell.py b/technology/sky130/tech/tech_configs/tech_cypress_cell.py index bfc44ff5..b62bdacf 100644 --- a/technology/sky130/tech/tech_configs/tech_cypress_cell.py +++ b/technology/sky130/tech/tech_configs/tech_cypress_cell.py @@ -95,7 +95,7 @@ cell_properties.bitcell_1port.gnd_dir = "H" cell_properties.bitcell_2port.mirror.x = True cell_properties.bitcell_2port.mirror.y = True cell_properties.bitcell_2port.end_caps = True -cell_properties.bitcell_2port.has_corners = True +cell_properties.bitcell_2port.has_corners = False cell_properties.bitcell_2port.port_order = ['bl0', 'br0', 'bl1', 'br1', 'wl0', 'wl1', 'vdd', 'gnd'] cell_properties.bitcell_2port.port_map = {'bl0': 'BL0', 'br0': 'BR0', From 50772821808818ca12ff5d4d3a75d51bf420b37d Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 28 Apr 2026 14:04:42 -0700 Subject: [PATCH 58/61] count wordlines from bottom going up --- compiler/modules/bitcell_base_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index f470bc46..b23cce97 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -151,7 +151,7 @@ class bitcell_base_array(design): 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[self.row_size - 1 - row, 0].get_pin(wl_names[port]) + 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), From 88241ca685deb70effdefd09c2e2fbb1d9154ce7 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 28 Apr 2026 17:19:54 -0700 Subject: [PATCH 59/61] add fix for cypress sp wls --- compiler/modules/bitcell_base_array.py | 8 +++-- compiler/modules/row_cap_array.py | 2 +- .../sky130/custom/sky130_bitcell_array.py | 29 +++++++++++-------- .../sky130_capped_replica_bitcell_array.py | 1 + .../sky130/custom/sky130_dummy_array.py | 16 +++++----- .../custom/sky130_replica_bitcell_array.py | 4 +-- .../sky130/custom/sky130_replica_column.py | 2 +- .../sky130/custom/sky130_row_cap_array.py | 8 ----- 8 files changed, 35 insertions(+), 35 deletions(-) diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index b23cce97..8725888a 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -121,13 +121,15 @@ class bitcell_base_array(design): def get_all_wordline_names(self, port=None): """ Return all the wordline names """ temp = [] - if len(self.all_ports) > 1: - temp.extend(self.get_rbl_wordline_names(1)) + if len(self.all_ports) > 0: + temp.extend(self.get_rbl_wordline_names(0)) if port == None: temp.extend(self.all_wordline_names) else: temp.extend(self.wordline_names[port]) - temp.extend(self.get_rbl_wordline_names(0)) + if len(self.all_ports) > 1: + temp.extend(self.get_rbl_wordline_names(1)) + return temp diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index dc2ba951..c421fb42 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -103,7 +103,7 @@ class row_cap_array(bitcell_base_array): max_row = self.row_size - 2 for row in range(0, max_row): for port in self.all_ports: - wl_pin = self.cell_inst[max_row - 1 - row, 0].get_pin(wl_names[port]) + 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), diff --git a/technology/sky130/custom/sky130_bitcell_array.py b/technology/sky130/custom/sky130_bitcell_array.py index a6af455d..89572624 100644 --- a/technology/sky130/custom/sky130_bitcell_array.py +++ b/technology/sky130/custom/sky130_bitcell_array.py @@ -19,11 +19,12 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): Creates a rows x cols array of memory cells. Assumes bit-lines and word lines are connected by abutment. """ - def __init__(self, rows, cols, column_offset=0, row_offset=0, name="",left_rbl=None, right_rbl=None): + def __init__(self, rows, cols, column_offset=0, row_offset=0, name="", left_rbl=None, right_rbl=None): super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name) self.left_rbl = left_rbl self.right_rbl = right_rbl - + self.column_offset = column_offset + self.row_offset = row_offset def add_modules(self): """ Add the modules used in this design """ # Bitcell for port names only @@ -44,19 +45,23 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): #self.cell_noblcon_inst = geometry.instance("cell_noblcon_inst", mod=self.cell_noblcon, is_bitcell=True) #self.cella_noblcon_inst = geometry.instance("cella_noblcon_inst", mod=self.cella_noblcon, is_bitcell=True) - 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_opt1 = [geometry.instance("00_opt1", mod=self.cell, is_bitcell=True, mirror='MX')] \ + + [geometry.instance("01_strap_p", mod=self.strap, is_bitcell=False, mirror='MX')]\ + + [geometry.instance("02_opt1", mod=self.cell, is_bitcell=True, mirror='XY')] \ + + [geometry.instance("03_strap", mod=self.strap_p, is_bitcell=False, mirror='MX')] - 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_row_opt1a = [geometry.instance("10_opt1a", mod=self.cella, is_bitcell=True)] \ + + [geometry.instance("11_strapa", mod=self.strap, 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_block = [] - pattern.append_row_to_block(bit_block, bit_row_opt1) - pattern.append_row_to_block(bit_block, bit_row_opt1a) + if self.row_offset % 2 == 0: + pattern.append_row_to_block(bit_block, bit_row_opt1) + pattern.append_row_to_block(bit_block, bit_row_opt1a) + else: + pattern.append_row_to_block(bit_block, bit_row_opt1a) + pattern.append_row_to_block(bit_block, bit_row_opt1) for row in bit_block: row = pattern.rotate_list(row, self.column_offset * 2) diff --git a/technology/sky130/custom/sky130_capped_replica_bitcell_array.py b/technology/sky130/custom/sky130_capped_replica_bitcell_array.py index 04d9af8d..3ec8286c 100644 --- a/technology/sky130/custom/sky130_capped_replica_bitcell_array.py +++ b/technology/sky130/custom/sky130_capped_replica_bitcell_array.py @@ -35,6 +35,7 @@ class sky130_capped_replica_bitcell_array(capped_replica_bitcell_array, sky130_b for row_end in self.dummy_col_insts: row_end = row_end.mod + print(self.get_all_wordline_names(), row_end.get_wordline_names()) 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, diff --git a/technology/sky130/custom/sky130_dummy_array.py b/technology/sky130/custom/sky130_dummy_array.py index 798daaea..0cf3108a 100644 --- a/technology/sky130/custom/sky130_dummy_array.py +++ b/technology/sky130/custom/sky130_dummy_array.py @@ -37,15 +37,15 @@ class sky130_dummy_array(dummy_array, sky130_bitcell_base_array): self.all_inst={} self.cell_inst={} - bit_row_opt1 = [geometry.instance("00_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='MY')] \ - + [geometry.instance("01_strap_p", mod=self.strap_p, is_bitcell=False, mirror='')]\ - + [geometry.instance("02_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='')] \ - + [geometry.instance("03_strap", mod=self.strap, is_bitcell=False, mirror='')] + bit_row_opt1 = [geometry.instance("00_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='MX')] \ + + [geometry.instance("01_strap", mod=self.strap, is_bitcell=False, mirror='MX')]\ + + [geometry.instance("02_opt1", mod=self.dummy_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.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)] + bit_row_opt1a = [geometry.instance("10_opt1a", mod=self.dummy_cella, is_bitcell=True, mirror='')] \ + + [geometry.instance("11_strapa", mod=self.strapa, is_bitcell=False, mirror='')] \ + + [geometry.instance("12_opt1a", mod=self.dummy_cella, is_bitcell=True, mirror='MY')] \ + + [geometry.instance("13_strapa_p", mod=self.strapa_p, is_bitcell=False, mirror='')] bit_block = [] diff --git a/technology/sky130/custom/sky130_replica_bitcell_array.py b/technology/sky130/custom/sky130_replica_bitcell_array.py index f6e0639d..e86709a2 100644 --- a/technology/sky130/custom/sky130_replica_bitcell_array.py +++ b/technology/sky130/custom/sky130_replica_bitcell_array.py @@ -26,7 +26,7 @@ class sky130_replica_bitcell_array(replica_bitcell_array, sky130_bitcell_base_ar 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=""): + def __init__(self, rows=0, cols=0, rbl=None, left_rbl=None, right_rbl=None, column_offset=0, row_offset=0, 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) + super().__init__(rows, cols, rbl, left_rbl, right_rbl, column_offset, row_offset, name) diff --git a/technology/sky130/custom/sky130_replica_column.py b/technology/sky130/custom/sky130_replica_column.py index f83b504d..45722bef 100644 --- a/technology/sky130/custom/sky130_replica_column.py +++ b/technology/sky130/custom/sky130_replica_column.py @@ -64,7 +64,7 @@ class sky130_replica_column(replica_column, sky130_bitcell_base_array): + [geometry.instance("dummy_13_strapa", mod=self.strapa, is_bitcell=False)] bit_block = [] - if self.column_offset % 2 == 0: + if self.column_offset % 2 == 1: replica_row_opt1 = replica_row_opt1[0:2] replica_row_opt1a = replica_row_opt1a[0:2] dummy_row_opt1 = dummy_row_opt1[0:2] diff --git a/technology/sky130/custom/sky130_row_cap_array.py b/technology/sky130/custom/sky130_row_cap_array.py index 1130556b..ab1064ad 100644 --- a/technology/sky130/custom/sky130_row_cap_array.py +++ b/technology/sky130/custom/sky130_row_cap_array.py @@ -90,15 +90,7 @@ class sky130_row_cap_array(row_cap_array, sky130_bitcell_base_array): 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 - for row in range(start_row, row_size): - for port in self.all_ports: - self.wordline_names[port].append("wl_{0}_{1}".format(port, row)) - self.all_wordline_names = [x for sl in zip(*self.wordline_names) for x in sl] def create_layout(self): From 2780fda35c98b3bae4d2ae16a750d0f44b81a87f Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 28 Apr 2026 23:22:40 -0700 Subject: [PATCH 60/61] all sky130 crba passing --- technology/sky130/custom/sky130_dummy_array.py | 1 + technology/sky130/custom/sky130_dummy_bitcell.py | 4 ++-- technology/sky130/tech/tech_configs/tech_cypress_cell.py | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/technology/sky130/custom/sky130_dummy_array.py b/technology/sky130/custom/sky130_dummy_array.py index 0cf3108a..34ae65a0 100644 --- a/technology/sky130/custom/sky130_dummy_array.py +++ b/technology/sky130/custom/sky130_dummy_array.py @@ -25,6 +25,7 @@ class sky130_dummy_array(dummy_array, sky130_bitcell_base_array): """ Add the modules used in this design """ self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell, version="opt1") self.dummy_cella = factory.create(module_type=OPTS.dummy_bitcell, 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") diff --git a/technology/sky130/custom/sky130_dummy_bitcell.py b/technology/sky130/custom/sky130_dummy_bitcell.py index 6696db8e..78a31ba1 100644 --- a/technology/sky130/custom/sky130_dummy_bitcell.py +++ b/technology/sky130/custom/sky130_dummy_bitcell.py @@ -21,9 +21,9 @@ class sky130_dummy_bitcell(bitcell_base): # Ignore the name argument if version == "opt1": - cell_name = "sky130_fd_bd_sram__openram_sp_cell_opt1_dummy" + cell_name = "sky130_fd_bd_sram__openram_sp_cell_opt1_noblcon" elif version == "opt1a": - cell_name = "sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy" + cell_name = "sky130_fd_bd_sram__openram_sp_cell_opt1a_noblcon" super().__init__(name, cell_name, prop=props.bitcell_1port) debug.info(2, "Create dummy bitcell") diff --git a/technology/sky130/tech/tech_configs/tech_cypress_cell.py b/technology/sky130/tech/tech_configs/tech_cypress_cell.py index b62bdacf..e3e10617 100644 --- a/technology/sky130/tech/tech_configs/tech_cypress_cell.py +++ b/technology/sky130/tech/tech_configs/tech_cypress_cell.py @@ -845,4 +845,5 @@ blackbox_cells = ["sky130_fd_bd_sram__openram_dp_cell", "sky130_fd_bd_sram__sram_sp_wlstrap_ce", "sky130_fd_bd_sram__sram_sp_wlstrap", "sky130_fd_bd_sram__sram_sp_wlstrap_p_ce", - "sky130_fd_bd_sram__sram_sp_wlstrap_p"] \ No newline at end of file + "sky130_fd_bd_sram__sram_sp_wlstrap_p", + "sky130_fd_bd_sram__sram_sp_wlstrapa_p"] \ No newline at end of file From ddac4254ec7722f27662ad78a46f2e55a0fc5c2e Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 30 Apr 2026 12:00:56 -0700 Subject: [PATCH 61/61] switch from conda to nix for tooling --- .github/workflows/regress.yml | 27 +++++++-------- .gitignore | 1 - MANIFEST.in | 1 - Makefile | 18 +++++----- __init__.py | 21 +++--------- compiler/characterizer/stimuli.py | 11 ++++-- compiler/globals.py | 48 +++++++++++++++++--------- compiler/options.py | 6 ++-- compiler/verify/run_script.py | 27 ++++++--------- docs/source/basic_setup.md | 57 ++++++++----------------------- docs/source/debug.md | 7 ++-- docs/source/index.md | 2 +- docs/source/openram_dev_notes.md | 6 ++-- flake.lock | 27 +++++++++++++++ flake.nix | 48 ++++++++++++++++++++++++++ install_conda.sh | 46 ------------------------- setup.py | 2 +- 17 files changed, 178 insertions(+), 177 deletions(-) create mode 100644 flake.lock create mode 100644 flake.nix delete mode 100755 install_conda.sh diff --git a/.github/workflows/regress.yml b/.github/workflows/regress.yml index eb07071d..f9b46bb8 100644 --- a/.github/workflows/regress.yml +++ b/.github/workflows/regress.yml @@ -19,31 +19,30 @@ jobs: run: | rm -rf ~/.local/lib/python3.8/site-packages/openram* make library - - name: Build conda - run: | - ./install_conda.sh - name: PDK Install run: | - export OPENRAM_HOME="${{ github.workspace }}/compiler" - export OPENRAM_TECH="${{ github.workspace }}/technology" - export PDK_ROOT="${{ github.workspace }}/pdk" + nix --extra-experimental-features 'nix-command flakes' develop --command bash -lc ' + export OPENRAM_HOME="${{ github.workspace }}/compiler"; + export OPENRAM_TECH="${{ github.workspace }}/technology"; + export PDK_ROOT="${{ github.workspace }}/pdk"; # Add make targets to install PDKs of all technologies that need it - make sky130-pdk + make sky130-pdk; make sky130-install - name: Regress run: | - export OPENRAM_HOME="${{ github.workspace }}/compiler" - export OPENRAM_TECH="${{ github.workspace }}/technology" - export PDK_ROOT="${{ github.workspace }}/pdk" - export FREEPDK45="~/FreePDK45" + nix --extra-experimental-features 'nix-command flakes' develop --command bash -lc ' + export OPENRAM_HOME="${{ github.workspace }}/compiler"; + export OPENRAM_TECH="${{ github.workspace }}/technology"; + export PDK_ROOT="${{ github.workspace }}/pdk"; + export FREEPDK45="~/FreePDK45"; # KLAYOUT_PATH breaks klayout installation. Unset it for now... - unset KLAYOUT_PATH + unset KLAYOUT_PATH; #cd $OPENRAM_HOME/.. && make pdk && make install #export OPENRAM_TMP="${{ github.workspace }}/scn4me_subm_temp" #python3-coverage run -p $OPENRAM_HOME/tests/regress.py -j 12 -t scn4m_subm #$OPENRAM_HOME/tests/regress.py -j 24 -t scn4m_subm - cd $OPENRAM_HOME/tests - make clean + cd $OPENRAM_HOME/tests; + make clean; make -k -j 48 - name: Archive if: ${{ failure() }} diff --git a/.gitignore b/.gitignore index a46b5394..7b0c7889 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,6 @@ compiler/tests/results/ open_pdks/ dist/ openram.egg-info/ -miniconda/ sky130A sky130B gf180mcuA diff --git a/MANIFEST.in b/MANIFEST.in index cb0967e3..790cc3e3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,7 +2,6 @@ include Makefile include openram.mk include setpaths.sh include requirements.txt -include install_conda.sh include docker/* recursive-include compiler * recursive-include technology * diff --git a/Makefile b/Makefile index d64c41b6..f2c65a15 100644 --- a/Makefile +++ b/Makefile @@ -57,8 +57,8 @@ INSTALL_BASE_DIRS := gds_lib mag_lib sp_lib lvs_lib calibre_lvs_lib klayout_lvs_ INSTALL_BASE := $(OPENRAM_HOME)/../technology/sky130 INSTALL_DIRS := $(addprefix $(INSTALL_BASE)/,$(INSTALL_BASE_DIRS)) -# If conda is installed, we will use ciel from there -CONDA_DIR := $(wildcard $(TOP_DIR)/miniconda) +# If nix is available, run ciel via nix develop +NIX_BIN := $(shell command -v nix 2>/dev/null) check-pdk-root: ifndef PDK_ROOT @@ -102,23 +102,21 @@ sky130-install: $(SRAM_LIB_DIR) sky130-pdk: $(SKY130_PDKS_DIR) @echo "Installing SKY130 via ciel..." -ifeq ($(CONDA_DIR),) +ifeq ($(NIX_BIN),) ciel enable --pdk sky130 $(SKY130_CIEL) else - source $(TOP_DIR)/miniconda/bin/activate && \ - ciel enable --pdk sky130 $(SKY130_CIEL) && \ - conda deactivate + nix --extra-experimental-features 'nix-command flakes' develop --command \ + ciel enable --pdk sky130 $(SKY130_CIEL) endif .PHONY: sky130-pdk gf180mcu-pdk: @echo "Installing GF180 via ciel..." -ifeq ($(CONDA_DIR),) +ifeq ($(NIX_BIN),) ciel enable --pdk gf180mcu $(GF180_CIEL) else - source $(TOP_DIR)/miniconda/bin/activate && \ - ciel enable --pdk gf180mcu $(GF180_CIEL) && \ - conda deactivate + nix --extra-experimental-features 'nix-command flakes' develop --command \ + ciel enable --pdk gf180mcu $(GF180_CIEL) endif .PHONY: gf180mcu-pdk diff --git a/__init__.py b/__init__.py index 758f50c4..48558a8a 100644 --- a/__init__.py +++ b/__init__.py @@ -23,22 +23,11 @@ if "OPENRAM_HOME" not in os.environ.keys(): __path__.insert(0, OPENRAM_HOME) -# Find the conda installer script -if os.path.exists(OPENRAM_HOME + "/install_conda.sh"): - CONDA_INSTALLER = OPENRAM_HOME + "/install_conda.sh" - CONDA_HOME = OPENRAM_HOME + "/miniconda" -elif os.path.exists(OPENRAM_HOME + "/../install_conda.sh"): - CONDA_INSTALLER = OPENRAM_HOME + "/../install_conda.sh" - CONDA_HOME = os.path.abspath(OPENRAM_HOME + "/../miniconda") -# Override CONDA_HOME if it's set as an environment variable -if "CONDA_HOME" in os.environ.keys(): - CONDA_HOME = os.environ["CONDA_HOME"] -# Add CONDA_HOME to environment variables just in case -try: - os.environ["CONDA_HOME"] = CONDA_HOME -except: - from openram import debug - debug.warning("Couldn't find conda setup directory.") +# Nix toolchain root (flake location) +NIX_HOME = os.path.abspath(OPENRAM_HOME + "/..") +if "NIX_HOME" in os.environ.keys(): + NIX_HOME = os.environ["NIX_HOME"] +os.environ["NIX_HOME"] = NIX_HOME # Import everything in globals.py diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index e5f44a2c..c184ed66 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -12,6 +12,7 @@ simulations as well. """ import os +import shlex import subprocess import numpy as np from openram import debug @@ -406,11 +407,15 @@ class stimuli(): spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w') spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w') - # Wrap the command with conda activate & conda deactivate + # Run spice in the Nix devShell when Nix-managed tools are enabled. # FIXME: Should use verify/run_script.py here but run_script doesn't return # the return code of the subprocess. File names might also mismatch. - from openram import CONDA_HOME - cmd = "/bin/bash -c 'source {0}/bin/activate && {1} && conda deactivate'".format(CONDA_HOME, cmd) + if OPTS.use_nix: + cmd = ( + "nix --extra-experimental-features 'nix-command flakes' " + "develop --command /bin/bash -lc {0}" + .format(shlex.quote(cmd)) + ) debug.info(2, cmd) proc = subprocess.run(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True) diff --git a/compiler/globals.py b/compiler/globals.py index 1c7d86f7..ea017aa6 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -188,7 +188,7 @@ def init_openram(config_file, is_unit_test=False): read_config(config_file, is_unit_test) - install_conda() + install_nix() import_tech() @@ -209,17 +209,40 @@ def init_openram(config_file, is_unit_test=False): from openram import verify -def install_conda(): - """ Setup conda for default tools. """ +def install_nix(): + """Initialize Nix-based toolchain dependencies.""" - # Don't setup conda if not used - if not OPTS.use_conda or OPTS.is_unit_test: + # Don't setup tools during unit tests. + if OPTS.is_unit_test: return - debug.info(1, "Creating conda setup..."); + if not OPTS.use_nix or OPTS.is_unit_test: + return - from openram import CONDA_INSTALLER - subprocess.call(CONDA_INSTALLER) + debug.info(1, "Bootstrapping toolchain with Nix...") + + nix_exe = shutil.which("nix") + if nix_exe is None: + debug.error("Nix is required for automatic tool setup, but 'nix' was not found in PATH.", -1) + + repo_root = os.path.abspath(os.path.join(OPENRAM_HOME, "..")) + flake_file = os.path.join(repo_root, "flake.nix") + if not os.path.exists(flake_file): + debug.error("Expected Nix flake at {} for tool setup.".format(flake_file), -1) + + # Trigger materialization/build of the devShell dependencies once. + # Environment activation still happens outside OpenRAM via `nix develop`. + cmd = [ + nix_exe, + "--extra-experimental-features", "nix-command flakes", + "develop", + "--command", "true", + ] + result = subprocess.call(cmd, cwd=repo_root) + if result != 0: + debug.error("Failed to initialize Nix toolchain (nix develop returned {}).".format(result), -1) + + return def setup_bitcell(): @@ -446,14 +469,7 @@ def find_exe(check_exe): Check if the binary exists in any path dir and return the full path. """ - # Search for conda setup if used - if OPTS.use_conda: - from openram import CONDA_HOME - search_path = "{0}/bin{1}{2}".format(CONDA_HOME, - os.pathsep, - os.environ["PATH"]) - else: - search_path = os.environ["PATH"] + search_path = os.environ["PATH"] # Check if the preferred spice option exists in the path for path in search_path.split(os.pathsep): diff --git a/compiler/options.py b/compiler/options.py index d97ee70e..229f7c3a 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -151,9 +151,9 @@ class options(optparse.Values): ################### # Top process that was ran (openram, memchar, memfunc) top_process = None - # Use conda to install the default tools - # (existing tools will be used if disabled) - use_conda = True + # Use Nix to initialize the default open-source toolchain. + # If disabled, OpenRAM uses whatever tools are already in PATH. + use_nix = True # Variable to select the variant of spice spice_name = None # The spice executable being used which is derived from the user PATH. diff --git a/compiler/verify/run_script.py b/compiler/verify/run_script.py index 10c12ad9..9dfef271 100644 --- a/compiler/verify/run_script.py +++ b/compiler/verify/run_script.py @@ -29,26 +29,21 @@ def run_script(cell_name, script="lvs"): scriptpath = '{0}run_{1}.sh'.format(OPTS.openram_temp, script) - # Wrap with conda activate & conda deactivate - if OPTS.use_conda: - from openram import CONDA_HOME - with open(scriptpath, "r") as f: - script_content = f.readlines() - with open(scriptpath, "w") as f: - # First line is shebang - f.write(script_content[0]) - # Activate conda using the activate script - f.write("source {}/bin/activate\n".format(CONDA_HOME)) - for line in script_content[1:]: - f.write(line) - # Deactivate conda at the end - f.write("conda deactivate\n") - debug.info(2, "Starting {}".format(scriptpath)) start = time.time() with open(outfile, 'wb') as fo, open(errfile, 'wb') as fe: + if OPTS.use_nix: + p_cmd = [ + "nix", + "--extra-experimental-features", "nix-command flakes", + "develop", + "--command", + scriptpath, + ] + else: + p_cmd = [scriptpath] p = subprocess.Popen( - [scriptpath], stdout=fo, stderr=fe, cwd=OPTS.openram_temp) + p_cmd, stdout=fo, stderr=fe, cwd=OPTS.openram_temp) if echo_cmd_output: tailo = subprocess.Popen([ diff --git a/docs/source/basic_setup.md b/docs/source/basic_setup.md index 2fc22ae0..daac3dc7 100644 --- a/docs/source/basic_setup.md +++ b/docs/source/basic_setup.md @@ -7,8 +7,8 @@ This page shows the basic setup for using OpenRAM to generate an SRAM. ## Table of Contents 1. [Dependencies](#dependencies) -1. [Anaconda](#anaconda) -1. [Docker](#docker-deprecated-use-anaconda-instead) +1. [Nix](#nix) +1. [Docker](#docker-deprecated-use-nix-instead) 1. [Environment](#environment) 1. [Sky130 Setup](#sky130-setup) @@ -20,51 +20,25 @@ In general, the OpenRAM compiler has very few dependencies: + Make + Python 3.5 or higher + Various Python packages (pip install -r requirements.txt) -+ Anaconda ++ Nix -## Anaconda -We use Anaconda package manager to install the tools used by OpenRAM. This way, -you don't have to worry about updating/installing these tools. OpenRAM installs -Anaconda silently in the background (without affecting any existing Anaconda -setup you have). +## Nix +OpenRAM uses Nix to provide the external toolchain (layout tools, simulators, +etc.) needed for SRAM generation. -You don't have to manually activate/deactivate the Anaconda environment. OpenRAM -automatically manages this before and after running the tools. - -OpenRAM uses Anaconda by default, but you can turn this feature off by setting -`use_conda = False` in your config file. Then, OpenRAM will use the tools you -have installed on your system. - -You can also tell OpenRAM where Anaconda should be installed or which Anaconda -setup it should use. You can set the `$CONDA_HOME` variable like this: +Enter the Nix development environment with: ``` -export CONDA_HOME="/path/to/conda/setup" +nix develop ``` -> **Note**: If you want to install Anaconda without running OpenRAM (for example -> to run unit tests, which do not install Anaconda), you can run: -> ``` -> ./install_conda.sh -> ``` +Within the devShell, required executables are available on `PATH` -> **Note**: You can uninstall OpenRAM's Anaconda installation by simply deleting -> the folder Anaconda is installed to. You can run: -> ``` -> rm -rf miniconda -> ``` +OpenRAM uses the `use_nix` option (enabled by default) to initialize Nix-based +tool dependencies via `nix develop`. -> **Note**: You can change a tool's version with the following commands: -> ``` -> source ./miniconda/bin/activate -> conda uninstall -> conda install -y -c vlsida-eda = -> ``` - - - -## Docker (deprecated, use Anaconda instead) +## Docker (deprecated, use Nix instead) We have a [docker setup](../../docker) to run OpenRAM. To use this, you should run: ``` @@ -124,12 +98,11 @@ make sky130-pdk This will use ciel to get the PDK. -> **Note**: If you don't have Magic installed, you need to install and activate -> the conda environment before running this command. You can run: +> **Note**: If you don't have Magic installed, enter the OpenRAM Nix devShell +> first (it provides Magic and other tools via `PATH`): > > ``` -> ./install_conda.sh -> source miniconda/bin/activate +> nix develop > ``` Then you must also install the [Sky130] SRAM build space with the appropriate diff --git a/docs/source/debug.md b/docs/source/debug.md index 2b6dde0d..e4326066 100644 --- a/docs/source/debug.md +++ b/docs/source/debug.md @@ -61,10 +61,9 @@ make -j 3 ``` The `-j` can run with 3 threads. By default, this will run in all technologies. -> **Note**: If you have not run openram before running unit tests, the conda -> environment will not be installed. You can install it by running -> `OpenRAM/install_conda.sh` (see [Basic Setup](basic_setup.md#anaconda) for -> more details). +> **Note**: The external EDA toolchain is provided by the Nix devShell. +> If you run unit tests without being in a Nix environment, enter it first: +> `nix develop` (see [Basic Setup](basic_setup.md#nix) for more details). To run a specific test in all technologies: ``` diff --git a/docs/source/index.md b/docs/source/index.md index bc7c5f50..4dc0d299 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -40,7 +40,7 @@ In general, the OpenRAM compiler has very few dependencies: + Make + Python 3.5 or higher + Various Python packages (pip install -r requirements.txt) -+ Anaconda ++ Nix Commercial tools (optional): * Spice Simulator diff --git a/docs/source/openram_dev_notes.md b/docs/source/openram_dev_notes.md index f2550cb0..99da300c 100644 --- a/docs/source/openram_dev_notes.md +++ b/docs/source/openram_dev_notes.md @@ -11,7 +11,7 @@ In this section, the detailed usage of using OpenRAM framework will be demonstra > > Before you go through, make sure that environment of Sky130 has been already set up. -1. Activate miniconda +1. Enter the Nix devShell 2. Edit the sram configuration file @@ -19,11 +19,11 @@ In this section, the detailed usage of using OpenRAM framework will be demonstra 4. Check the results -#### Activate miniconda +#### Enter Nix devShell ```bash cd OpenRAM/ -source ./miniconda/bin/activate +nix develop ``` #### Modified the sram configuration diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..e66c0499 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1777270315, + "narHash": "sha256-yKB4G6cKsQsWN7M6rZGk6gkJPDNPIzT05y4qzRyCDlI=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6368eda62c9775c38ef7f714b2555a741c20c72d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..1c0f0038 --- /dev/null +++ b/flake.nix @@ -0,0 +1,48 @@ +{ + description = "OpenRAM development environment (Nix)"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + }; + + outputs = { self, nixpkgs }: + let + systems = [ "x86_64-linux" ]; + forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system); + in + { + devShells = forAllSystems (system: + let + pkgs = import nixpkgs { inherit system; }; + in + { + default = pkgs.mkShell { + packages = [ + # EDA / verification tools + pkgs.klayout + pkgs.magic-vlsi + # Use the LVS-focused netgen package; the generic netgen package + # may require a local build that can fail on some hosts. + pkgs.netgen-vlsi + pkgs.ngspice + pkgs.iverilog + pkgs.xyce + pkgs.xyce-parallel + pkgs.trilinos + pkgs.trilinos-mpi + + # Dev conveniences + pkgs.git + pkgs.gnumake + pkgs.curl + ]; + + shellHook = '' + export OPENRAM_USE_CONDA=0 + echo "OpenRAM: using tools from Nix devShell" + ''; + }; + }); + }; +} + diff --git a/install_conda.sh b/install_conda.sh deleted file mode 100755 index 63c5fd75..00000000 --- a/install_conda.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash -#CONDA_INSTALLER_URL="https://repo.anaconda.com/miniconda/Miniconda3-py313_25.11.1-1-Linux-x86_64.sh" -#CONDA_INSTALLER_FILE="miniconda_installer_py313.sh" - -CONDA_INSTALLER_URL="https://repo.anaconda.com/miniconda/Miniconda3-py38_23.11.0-2-Linux-x86_64.sh" -CONDA_INSTALLER_FILE="miniconda_installer_py38.sh" -CONDA_HOME="${CONDA_HOME:-miniconda}" - -# The tool name format is "=". -# If you want to use the latest version, just use "". -TOOLS="" -TOOLS+="klayout=0.28.3 " -TOOLS+="magic=8.3.587 " -TOOLS+="netgen=1.5.286 " -TOOLS+="ngspice=26 " -TOOLS+="trilinos=12.12.1=1 " -TOOLS+="xyce=7.4" - -# Install miniconda if not already installed -if [[ ! -d "${CONDA_HOME}/bin" ]] -then - curl -s -o ${CONDA_INSTALLER_FILE} ${CONDA_INSTALLER_URL} - /bin/bash ${CONDA_INSTALLER_FILE} -b -p ${CONDA_HOME} - rm ${CONDA_INSTALLER_FILE} - source ${CONDA_HOME}/bin/activate - - # Prioritize channels to prevent version conflicts - conda config --add channels conda-forge - conda config --add channels vlsida-eda - - #conda install -q -y -c conda-forge trilinos - # Install rest of the tools from vlsida-eda - for tool in ${TOOLS} - do - conda install -q -y -c vlsida-eda ${tool} - done - - # Install iverilog from conda-eda - conda install -q -y -c litex-hub iverilog - # Install required Python packages - # (This step isn't required but used to prevent possible issues) - python3 -m pip install -r requirements.txt --ignore-installed - - conda deactivate -fi - diff --git a/setup.py b/setup.py index 34526279..1cae9b3c 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from setuptools import setup, find_namespace_packages # Include these folder from the root of repo as submodules include = ["compiler", "docker", "technology", "macros"] # Exclude files/folders with these words -exclude = ["docs", "images", "miniconda"] +exclude = ["docs", "images"] # Find all modules inside the 'compiler' folder