mirror of https://github.com/VLSIDA/OpenRAM.git
201 lines
8.9 KiB
Python
201 lines
8.9 KiB
Python
# 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
|
|
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
|
|
instance modules, rotation, and ordering
|
|
|
|
"""
|
|
block = List[List[instance]]
|
|
def __init__(self,
|
|
parent_design: design,
|
|
name:str,
|
|
core_block:block,
|
|
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,
|
|
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
|
|
(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
|
|
xy_block defines a block that is a inserted where the x_block and y_block intercept
|
|
the initial and final booleans determine whether the pattern begins and/or ends with x/y blocks
|
|
"""
|
|
self.parent_design = parent_design
|
|
self.name = name
|
|
self.core_block = core_block
|
|
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
|
|
self.y_block = y_block
|
|
self.xy_block = xy_block
|
|
self.initial_x_block = initial_x_block
|
|
self.initial_y_block = initial_y_block
|
|
self.final_x_block = final_x_block
|
|
self.final_y_block = final_y_block
|
|
if not OPTS.netlist_only:
|
|
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])
|
|
|
|
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 connect_block(self, block: block, col: int, row: int):
|
|
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[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(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 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):
|
|
self.connect_block(self.core_block, col, row)
|
|
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]
|
|
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 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
|
|
|
|
place_x += self.core_block_width
|
|
col += len(self.core_block[0])
|
|
row += len(self.core_block)
|
|
place_y += self.core_block_height
|
|
|