resolved conflicts with bitcell_array after PrivateRAM merge

This commit is contained in:
Michael Timothy Grimes 2018-05-22 14:12:14 -07:00
parent b5df0cc30a
commit 5e4d4bf6cd
1 changed files with 202 additions and 201 deletions

View File

@ -1,201 +1,202 @@
import debug import debug
import design import design
from tech import drc, spice from tech import drc, spice
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
class bitcell_array(design.design): class bitcell_array(design.design):
""" """
Creates a rows x cols array of memory cells. Assumes bit-lines Creates a rows x cols array of memory cells. Assumes bit-lines
and word line is connected by abutment. and word line is connected by abutment.
Connects the word lines and bit lines. Connects the word lines and bit lines.
""" """
def __init__(self, cols, rows, name="bitcell_array"): def __init__(self, cols, rows, name="bitcell_array"):
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
self.column_size = cols self.column_size = cols
self.row_size = rows self.row_size = rows
c = reload(__import__(OPTS.bitcell)) #from importlib import reload
self.mod_bitcell = getattr(c, OPTS.bitcell) c = reload(__import__(OPTS.bitcell))
self.cell = self.mod_bitcell() self.mod_bitcell = getattr(c, OPTS.bitcell)
self.add_mod(self.cell) self.cell = self.mod_bitcell()
self.add_mod(self.cell)
# We increase it by a well enclosure so the precharges don't overlap our wells
self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] # We increase it by a well enclosure so the precharges don't overlap our wells
self.width = self.column_size*self.cell.width self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] + self.m1_width
self.width = self.column_size*self.cell.width + self.m1_width
self.add_pins()
self.create_layout() self.add_pins()
self.add_layout_pins() self.create_layout()
self.add_layout_pins()
self.DRC_LVS()
# We don't offset this because we need to align
# the replica bitcell in the control logic
def add_pins(self): #self.offset_all_coordinates()
row_list = self.cell.list_row_pins()
column_list = self.cell.list_column_pins() self.DRC_LVS()
for col in range(self.column_size):
for cell_column in column_list: def add_pins(self):
self.add_pin(cell_column+"[{0}]".format(col)) row_list = self.cell.list_row_pins()
for row in range(self.row_size): column_list = self.cell.list_column_pins()
for cell_row in row_list: for col in range(self.column_size):
self.add_pin(cell_row+"[{0}]".format(row)) for cell_column in column_list:
self.add_pin("vdd") self.add_pin(cell_column+"[{0}]".format(col))
self.add_pin("gnd") for row in range(self.row_size):
for cell_row in row_list:
self.add_pin(cell_row+"[{0}]".format(row))
def create_layout(self): self.add_pin("vdd")
xoffset = 0.0 self.add_pin("gnd")
self.cell_inst = {}
for col in range(self.column_size): def create_layout(self):
yoffset = 0.0 xoffset = 0.0
for row in range(self.row_size): self.cell_inst = {}
name = "bit_r{0}_c{1}".format(row, col) for col in range(self.column_size):
yoffset = 0.0
if row % 2: for row in range(self.row_size):
tempy = yoffset + self.cell.height name = "bit_r{0}_c{1}".format(row, col)
dir_key = "MX"
else: if row % 2:
tempy = yoffset tempy = yoffset + self.cell.height
dir_key = "" dir_key = "MX"
else:
self.cell_inst[row,col]=self.add_inst(name=name, tempy = yoffset
mod=self.cell, dir_key = ""
offset=[xoffset, tempy],
mirror=dir_key) self.cell_inst[row,col]=self.add_inst(name=name,
self.connect_inst(self.cell.list_bitcell_pins(col, row)) mod=self.cell,
offset=[xoffset, tempy],
yoffset += self.cell.height mirror=dir_key)
xoffset += self.cell.width self.connect_inst(self.cell.list_bitcell_pins(col, row))
yoffset += self.cell.height
def add_layout_pins(self): xoffset += self.cell.width
# Our cells have multiple gnd pins for now.
# FIXME: fix for multiple vdd too def add_layout_pins(self):
vdd_pin = self.cell.get_pin("vdd") """ Add the layout pins """
# shift it up by the overlap amount (gnd_pin) too row_list = self.cell.list_row_pins()
# must find the lower gnd pin to determine this overlap column_list = self.cell.list_column_pins()
lower_y = self.cell.height
gnd_pins = self.cell.get_pins("gnd") offset = vector(0.0, 0.0)
for gnd_pin in gnd_pins: for col in range(self.column_size):
if gnd_pin.layer=="metal2" and gnd_pin.by()<lower_y: for cell_column in column_list:
lower_y=gnd_pin.by() bl_pin = self.cell_inst[0,col].get_pin(cell_column)
self.add_layout_pin(text=cell_column+"[{0}]".format(col),
# lower_y is negative, so subtract off double this amount for each pair of layer="metal2",
# overlapping cells offset=bl_pin.ll(),
full_height = self.height - 2*lower_y width=bl_pin.width(),
height=self.height)
vdd_pin = self.cell.get_pin("vdd")
lower_x = vdd_pin.lx() # increments to the next column width
# lower_x is negative, so subtract off double this amount for each pair of offset.x += self.cell.width
# overlapping cells
full_width = self.width - 2*lower_x offset.x = 0.0
for row in range(self.row_size):
row_list = self.cell.list_row_pins() for cell_row in row_list:
column_list = self.cell.list_column_pins() wl_pin = self.cell_inst[row,0].get_pin(cell_row)
self.add_layout_pin(text=cell_row+"[{0}]".format(row),
offset = vector(0.0, 0.0) layer="metal1",
for col in range(self.column_size): offset=wl_pin.ll(),
# get the pins of the lower row cell and make it the full width width=self.width,
for cell_column in column_list: height=wl_pin.height())
bl_pin = self.cell_inst[0,col].get_pin(cell_column)
self.add_layout_pin(text=cell_column+"[{0}]".format(col), # increments to the next row height
layer="metal2", offset.y += self.cell.height
offset=bl_pin.ll(),
width=bl_pin.width(), # For every second row and column, add a via for vdd
height=full_height) for row in range(self.row_size):
for col in range(self.column_size):
# gnd offset is 0 in our cell, but it be non-zero inst = self.cell_inst[row,col]
gnd_pins = self.cell_inst[0,col].get_pins("gnd") for vdd_pin in inst.get_pins("vdd"):
for gnd_pin in gnd_pins: # Drop to M1 if needed
# avoid duplicates by only doing even rows if vdd_pin.layer == "metal1":
# also skip if it isn't the pin that spans the entire cell down to the bottom self.add_via_center(layers=("metal1", "via1", "metal2"),
if gnd_pin.layer=="metal2" and gnd_pin.by()==lower_y: offset=vdd_pin.center(),
self.add_layout_pin(text="gnd", rotate=90)
layer="metal2", # Always drop to M2
offset=gnd_pin.ll(), self.add_via_center(layers=("metal2", "via2", "metal3"),
width=gnd_pin.width(), offset=vdd_pin.center())
height=full_height) self.add_layout_pin_rect_center(text="vdd",
layer="metal3",
# increments to the next column width offset=vdd_pin.center())
offset.x += self.cell.width
offset.x = 0.0 # For every second row and column (+1), add a via for gnd
for row in range(self.row_size): for row in range(self.row_size):
vdd_pins = self.cell_inst[row,0].get_pins("vdd") for col in range(self.column_size):
gnd_pins = self.cell_inst[row,0].get_pins("gnd") inst = self.cell_inst[row,col]
for gnd_pin in inst.get_pins("gnd"):
# add wl label and offset # Drop to M1 if needed
for cell_row in row_list: if gnd_pin.layer == "metal1":
wl_pin = self.cell_inst[row,0].get_pin(cell_row) self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_layout_pin(text=cell_row+"[{0}]".format(row), offset=gnd_pin.center(),
layer="metal1", rotate=90)
offset=wl_pin.ll(), # Always drop to M2
width=full_width, self.add_via_center(layers=("metal2", "via2", "metal3"),
height=wl_pin.height()) offset=gnd_pin.center())
self.add_layout_pin_rect_center(text="gnd",
for gnd_pin in gnd_pins: layer="metal3",
if gnd_pin.layer=="metal1": offset=gnd_pin.center())
self.add_layout_pin(text="gnd",
layer="metal1", def analytical_delay(self, slew, load=0):
offset=gnd_pin.ll(), from tech import drc
width=full_width, wl_wire = self.gen_wl_wire()
height=drc["minwidth_metal1"]) wl_wire.return_delay_over_wire(slew)
# add vdd label and offset wl_to_cell_delay = wl_wire.return_delay_over_wire(slew)
# only add to even rows to avoid duplicates # hypothetical delay from cell to bl end without sense amp
for vdd_pin in vdd_pins: bl_wire = self.gen_bl_wire()
if row % 2 == 0 and vdd_pin.layer=="metal1": cell_load = 2 * bl_wire.return_input_cap() # we ingore the wire r
self.add_layout_pin(text="vdd", # hence just use the whole c
layer="metal1", bl_swing = 0.1
offset=vdd_pin.ll(), cell_delay = self.cell.analytical_delay(wl_to_cell_delay.slew, cell_load, swing = bl_swing)
width=full_width,
height=drc["minwidth_metal1"]) #we do not consider the delay over the wire for now
return self.return_delay(cell_delay.delay+wl_to_cell_delay.delay,
# increments to the next row height wl_to_cell_delay.slew)
offset.y += self.cell.height
def analytical_power(self, proc, vdd, temp, load):
def analytical_delay(self, slew, load=0): """Power of Bitcell array and bitline in nW."""
from tech import drc from tech import drc
wl_wire = self.gen_wl_wire()
wl_wire.return_delay_over_wire(slew) # Dynamic Power from Bitline
bl_wire = self.gen_bl_wire()
wl_to_cell_delay = wl_wire.return_delay_over_wire(slew) cell_load = 2 * bl_wire.return_input_cap()
# hypothetical delay from cell to bl end without sense amp bl_swing = 0.1 #This should probably be defined in the tech file or input
bl_wire = self.gen_bl_wire() freq = spice["default_event_rate"]
cell_load = 2 * bl_wire.return_input_cap() # we ingore the wire r bitline_dynamic = bl_swing*cell_load*vdd*vdd*freq #not sure if calculation is correct
# hence just use the whole c
bl_swing = 0.1 #Calculate the bitcell power which currently only includes leakage
cell_delay = self.cell.analytical_delay(wl_to_cell_delay.slew, cell_load, swing = bl_swing) cell_power = self.cell.analytical_power(proc, vdd, temp, load)
#we do not consider the delay over the wire for now #Leakage power grows with entire array and bitlines.
return self.return_delay(cell_delay.delay+wl_to_cell_delay.delay, total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
wl_to_cell_delay.slew) cell_power.leakage * self.column_size * self.row_size)
return total_power
def gen_wl_wire(self):
wl_wire = self.generate_rc_net(int(self.column_size), self.width, drc["minwidth_metal1"]) def gen_wl_wire(self):
wl_wire.wire_c = 2*spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell wl_wire = self.generate_rc_net(int(self.column_size), self.width, drc["minwidth_metal1"])
return wl_wire 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):
bl_pos = 0 def gen_bl_wire(self):
bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), self.height, drc["minwidth_metal1"]) bl_pos = 0
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), self.height, drc["minwidth_metal1"])
return bl_wire bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
return bl_wire
def output_load(self, bl_pos=0):
bl_wire = self.gen_bl_wire() def output_load(self, bl_pos=0):
return bl_wire.wire_c # sense amp only need to charge small portion of the bl bl_wire = self.gen_bl_wire()
# set as one segment for now return bl_wire.wire_c # sense amp only need to charge small portion of the bl
# set as one segment for now
def input_load(self):
wl_wire = self.gen_wl_wire() def input_load(self):
return wl_wire.return_input_cap() wl_wire = self.gen_wl_wire()
return wl_wire.return_input_cap()