Add bank_sel to bank_select module as input.

Remove reference to control in sram.
Add dff_buf_array to options.
Added inverted DFF
Add variable height pinvbuf
This commit is contained in:
Matt Guthaus 2018-03-19 16:23:13 -07:00
parent 5bf915a232
commit 696433b1ec
10 changed files with 443 additions and 57 deletions

View File

@ -56,8 +56,6 @@ class bank(design.design):
self.add_modules()
self.setup_layout_constraints()
self.add_power_ring(self.core_bbox)
# FIXME: Move this to the add modules function
self.add_bank_select()
@ -451,6 +449,7 @@ class bank(design.design):
temp = []
temp.extend(self.input_control_signals)
temp.append("bank_sel")
temp.extend(self.control_signals)
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
@ -488,39 +487,41 @@ class bank(design.design):
#the column decoder (if there is one) or the tristate output
#driver.
# Leave room for the output below the tri gate.
tri_gate_min_point = self.tri_gate_array_inst.by() - 3*self.m2_pitch
row_decoder_min_point = self.row_decoder_inst.by()
tri_gate_min_y_offset = self.tri_gate_array_inst.by() - 3*self.m2_pitch
row_decoder_min_y_offset = self.row_decoder_inst.by()
if self.col_addr_size > 0:
col_decoder_min_point = self.col_decoder_inst.by()
col_decoder_min_y_offset = self.col_decoder_inst.by()
else:
col_decoder_min_point = row_decoder_min_point
col_decoder_min_y_offset = row_decoder_min_y_offset
if self.num_banks>1:
# The control gating logic is below the decoder
# Min of the control gating logic and tri gate.
self.min_point = min(col_decoder_min_point - self.bank_select.height, tri_gate_min_point)
self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, tri_gate_min_y_offset)
else:
# Just the min of the decoder logic logic and tri gate.
self.min_point = min(col_decoder_min_point, tri_gate_min_point)
self.min_y_offset = min(col_decoder_min_y_offset, tri_gate_min_y_offset)
# The max point is always the top of the precharge bitlines
# Add a vdd and gnd power rail above the array
self.max_point = self.precharge_array_inst.uy() + 3*self.m1_width
self.max_y_offset = self.precharge_array_inst.uy() + 3*self.m1_width
self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width
self.min_x_offset = self.row_decoder_inst.lx()
# Create the core bbox for the power rings
ur = vector(self.bitcell_array_inst.ur().x + 3*self.m1_width, self.max_point)
ll = vector(self.row_decoder_inst.lx(), self.min_point)
ur = vector(self.max_x_offset, self.max_y_offset)
ll = vector(self.min_x_offset, self.min_y_offset)
self.core_bbox = [ll, ur]
self.add_power_ring(self.core_bbox)
# Compute the vertical rail positions for later use
self.right_gnd_x_offset = ur.x
self.right_gnd_x_offset = self.right_gnd_x_center - 0.5*self.supply_rail_pitch
self.right_vdd_x_offset = self.right_gnd_x_offset + self.supply_rail_pitch
self.left_vdd_x_offset = ll.x - self.supply_rail_pitch
self.left_vdd_x_offset = self.left_gnd_x_center - 0.5*self.supply_rail_pitch
self.left_gnd_x_offset = self.left_vdd_x_offset - self.supply_rail_pitch
# Add a vdd and gnd power rail below the array
self.min_point -= 2*self.supply_rail_pitch
# Add a vdd and gnd power rail above the array
self.max_point += self.supply_rail_pitch + self.supply_rail_width
# Have the pins go below the vdd and gnd power rail at the bottom
self.min_y_offset -= 2*self.supply_rail_pitch
self.height = ur.y - ll.y + 4*self.supply_rail_pitch
self.width = ur.x - ll.x + 4*self.supply_rail_pitch
@ -547,9 +548,9 @@ class bank(design.design):
self.bus_xoffset[self.control_signals[i]]=x_offset + 0.5*self.m2_width
# Pins are added later if this is a single bank, so just add rectangle now
self.add_rect(layer="metal2",
offset=vector(x_offset, self.min_point),
offset=vector(x_offset, self.min_y_offset),
width=self.m2_width,
height=self.height)
height=self.max_y_offset-self.min_y_offset)
@ -606,12 +607,12 @@ class bank(design.design):
""" Metal 3 routing of tri_gate output data """
for i in range(self.word_size):
tri_gate_out_position = self.tri_gate_array_inst.get_pin("out[{}]".format(i)).ul()
data_line_position = vector(tri_gate_out_position.x, self.min_point)
data_line_position = vector(tri_gate_out_position.x, self.min_y_offset)
self.add_via(("metal2", "via2", "metal3"), data_line_position)
self.add_rect(layer="metal3",
offset=data_line_position,
width=drc["minwidth_metal3"],
height=tri_gate_out_position.y - self.min_point)
height=tri_gate_out_position.y - self.min_y_offset)
self.add_layout_pin(text="DATA[{}]".format(i),
layer="metal2",
offset=data_line_position,
@ -620,12 +621,12 @@ class bank(design.design):
def route_row_decoder(self):
""" Routes the row decoder inputs and supplies """
# # Create inputs for the row address lines
# for i in range(self.row_addr_size):
# addr_idx = i + self.col_addr_size
# decoder_name = "A[{}]".format(i)
# addr_name = "A[{}]".format(addr_idx)
# self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name)
# Create inputs for the row address lines
for i in range(self.row_addr_size):
addr_idx = i + self.col_addr_size
decoder_name = "A[{}]".format(i)
addr_name = "A[{}]".format(addr_idx)
self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name)
# Route the power and ground, but only BELOW the y=0 since the
@ -909,15 +910,15 @@ class bank(design.design):
# it's not an input pin if we have multiple banks
self.add_label_pin(text=ctrl,
layer="metal2",
offset=vector(x_offset, self.min_point),
offset=vector(x_offset, self.min_y_offset),
width=self.m2_width,
height=self.height)
height=self.max_y_offset-self.min_y_offset)
else:
self.add_layout_pin(text=ctrl,
layer="metal2",
offset=vector(x_offset, self.min_point),
offset=vector(x_offset, self.min_y_offset),
width=self.m2_width,
height=self.height)
height=self.max_y_offset-self.min_y_offset)
def connect_rail_from_right(self,inst, pin, rail):

View File

@ -26,6 +26,7 @@ class bank_select(design.design):
self.control_signals = ["gated_"+str for str in self.input_control_signals]
self.add_pin_list(self.input_control_signals, "INPUT")
self.add_pin("bank_sel")
self.add_pin_list(self.control_signals, "OUTPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")

128
compiler/modules/dff_inv.py Normal file
View File

@ -0,0 +1,128 @@
import debug
import design
from tech import drc
from math import log
from vector import vector
from globals import OPTS
from pinv import pinv
class dff_inv(design.design):
"""
This is a simple DFF with an inverted output. Some DFFs
do not have Qbar, so this will create it.
"""
def __init__(self, inv_size=1, name=""):
if name=="":
name = "dff_inv_{0}".format(inv_size)
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
c = reload(__import__(OPTS.dff))
self.mod_dff = getattr(c, OPTS.dff)
self.dff = self.mod_dff("dff")
self.add_mod(self.dff)
self.inv1 = pinv(size=inv_size,height=self.dff.height)
self.add_mod(self.inv1)
self.width = self.dff.width + self.inv1.width
self.height = self.dff.height
self.create_layout()
def create_layout(self):
self.add_pins()
self.add_insts()
self.add_wires()
self.add_layout_pins()
self.DRC_LVS()
def add_pins(self):
self.add_pin("D")
self.add_pin("Q")
self.add_pin("Qb")
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
def add_insts(self):
# Add the DFF
self.dff_inst=self.add_inst(name="dff_inv_dff",
mod=self.dff,
offset=vector(0,0))
self.connect_inst(["D", "Q", "clk", "vdd", "gnd"])
# Add INV1 to the right
self.inv1_inst=self.add_inst(name="dff_inv_inv1",
mod=self.inv1,
offset=vector(self.dff_inst.rx(),0))
self.connect_inst(["Q", "Qb", "vdd", "gnd"])
def add_wires(self):
# Route dff q to inv1 a
q_pin = self.dff_inst.get_pin("Q")
a1_pin = self.inv1_inst.get_pin("A")
mid_point = vector(a1_pin.cx(), q_pin.cy())
self.add_wire(("metal3","via2","metal2"),
[q_pin.center(), mid_point, a1_pin.center()])
self.add_via_center(("metal2","via2","metal3"),
q_pin.center())
self.add_via_center(("metal1","via1","metal2"),
a1_pin.center())
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin=self.dff_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll(),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin=self.dff_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll(),
width=self.width,
height=vdd_pin.height())
clk_pin = self.dff_inst.get_pin("clk")
self.add_layout_pin(text="clk",
layer=clk_pin.layer,
offset=clk_pin.ll(),
width=clk_pin.width(),
height=clk_pin.height())
din_pin = self.dff_inst.get_pin("D")
self.add_layout_pin(text="D",
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
dout_pin = self.dff_inst.get_pin("Q")
self.add_layout_pin_center_rect(text="Q",
layer=dout_pin.layer,
offset=dout_pin.center())
dout_pin = self.inv1_inst.get_pin("Z")
self.add_layout_pin_center_rect(text="Qb",
layer="metal2",
offset=dout_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=dout_pin.center())
def analytical_delay(self, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
dff_delay=self.dff.analytical_delay(slew=slew, load=self.inv1.input_load())
inv1_delay = self.inv1.analytical_delay(slew=dff_delay.slew, load=load)
return dff_delay + inv1_delay

View File

@ -0,0 +1,180 @@
import debug
import design
from tech import drc
from math import log
from vector import vector
from globals import OPTS
import dff_inv
class dff_inv_array(design.design):
"""
This is a simple row (or multiple rows) of flops.
Unlike the data flops, these are never spaced out.
"""
def __init__(self, rows, columns, inv_size=2, name=""):
self.rows = rows
self.columns = columns
if name=="":
name = "dff_array_{0}x{1}".format(rows, columns)
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
self.dff = dff_inv.dff_inv(inv_size)
self.add_mod(self.dff)
self.width = self.columns * self.dff.width
self.height = self.rows * self.dff.height
self.create_layout()
def create_layout(self):
self.add_pins()
self.create_dff_array()
self.add_layout_pins()
self.DRC_LVS()
def add_pins(self):
for y in range(self.rows):
for x in range(self.columns):
self.add_pin(self.get_din_name(y,x))
for y in range(self.rows):
for x in range(self.columns):
self.add_pin(self.get_dout_name(y,x))
self.add_pin(self.get_dout_bar_name(y,x))
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
def create_dff_array(self):
self.dff_insts={}
for y in range(self.rows):
for x in range(self.columns):
name = "Xdff_r{0}_c{1}".format(y,x)
if (y % 2 == 0):
base = vector(x*self.dff.width,y*self.dff.height)
mirror = "R0"
else:
base = vector(x*self.dff.width,(y+1)*self.dff.height)
mirror = "MX"
self.dff_insts[x,y]=self.add_inst(name=name,
mod=self.dff,
offset=base,
mirror=mirror)
self.connect_inst([self.get_din_name(y,x),
self.get_dout_name(y,x),
self.get_dout_bar_name(y,x),
"clk",
"vdd",
"gnd"])
def get_din_name(self, row, col):
if self.columns == 1:
din_name = "din[{0}]".format(row)
elif self.rows == 1:
din_name = "din[{0}]".format(col)
else:
din_name = "din[{0}][{1}]".format(row,col)
return din_name
def get_dout_name(self, row, col):
if self.columns == 1:
dout_name = "dout[{0}]".format(row)
elif self.rows == 1:
dout_name = "dout[{0}]".format(col)
else:
dout_name = "dout[{0}][{1}]".format(row,col)
return dout_name
def get_dout_bar_name(self, row, col):
if self.columns == 1:
dout_bar_name = "dout_bar[{0}]".format(row)
elif self.rows == 1:
dout_bar_name = "dout_bar[{0}]".format(col)
else:
dout_bar_name = "dout_bar[{0}][{1}]".format(row,col)
return dout_bar_name
def add_layout_pins(self):
for y in range(self.rows):
# Continous vdd rail along with label.
vdd_pin=self.dff_insts[0,y].get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll(),
width=self.width,
height=self.m1_width)
# Continous gnd rail along with label.
gnd_pin=self.dff_insts[0,y].get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll(),
width=self.width,
height=self.m1_width)
for y in range(self.rows):
for x in range(self.columns):
din_pin = self.dff_insts[x,y].get_pin("D")
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
self.add_layout_pin(text=self.get_din_name(y,x),
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
dout_pin = self.dff_insts[x,y].get_pin("Q")
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
self.add_layout_pin(text=self.get_dout_name(y,x),
layer=dout_pin.layer,
offset=dout_pin.ll(),
width=dout_pin.width(),
height=dout_pin.height())
dout_bar_pin = self.dff_insts[x,y].get_pin("Qb")
debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2")
self.add_layout_pin(text=self.get_dout_bar_name(y,x),
layer=dout_bar_pin.layer,
offset=dout_bar_pin.ll(),
width=dout_bar_pin.width(),
height=dout_bar_pin.height())
# Create vertical spines to a single horizontal rail
clk_pin = self.dff_insts[0,0].get_pin("clk")
debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2")
if self.columns==1:
self.add_layout_pin(text="clk",
layer="metal2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
else:
self.add_layout_pin(text="clk",
layer="metal3",
offset=vector(0,0),
width=self.width,
height=self.m3_width)
for x in range(self.columns):
clk_pin = self.dff_insts[x,0].get_pin("clk")
# Make a vertical strip for each column
self.add_layout_pin(text="clk",
layer="metal2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
# Drop a via to the M3 pin
self.add_via_center(layers=("metal2","via2","metal3"),
offset=clk_pin.center().scale(1,0))
def analytical_delay(self, slew, load=0.0):
return self.dff.analytical_delay(slew=slew, load=load)

View File

@ -59,7 +59,8 @@ class options(optparse.Values):
ms_flop = "ms_flop"
ms_flop_array = "ms_flop_array"
dff = "dff"
dff_array = "dff_buf_array"
dff_array = "dff_array"
dff_buf_array = "dff_buf_array"
control_logic = "control_logic"
bitcell_array = "bitcell_array"
sense_amp = "sense_amp"

View File

@ -11,21 +11,23 @@ class pinvbuf(design.design):
This is a simple inverter/buffer used for driving loads. It is
used in the column decoder for 1:2 decoding and as the clock buffer.
"""
c = reload(__import__(OPTS.bitcell))
bitcell = getattr(c, OPTS.bitcell)
def __init__(self, inv1_size=2, inv2_size=4, name=""):
def __init__(self, inv1_size=2, inv2_size=4, height=bitcell.height, name=""):
if name=="":
name = "pinvbuf_{0}_{1}".format(inv1_size, inv2_size)
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
self.inv = pinv(size=1)
self.inv = pinv(size=1, height=height)
self.add_mod(self.inv)
self.inv1 = pinv(size=inv1_size)
self.inv1 = pinv(size=inv1_size, height=height)
self.add_mod(self.inv1)
self.inv2 = pinv(size=inv2_size)
self.inv2 = pinv(size=inv2_size, height=height)
self.add_mod(self.inv2)
self.width = 2*self.inv1.width + self.inv2.width

View File

@ -219,18 +219,17 @@ class sram(design.design):
# 3/5/18 MRG: Cannot reference positions inside submodules because boundaries
# are not recomputed using instance placement. So, place the control logic such that it aligns
# with the top of the SRAM.
control_gap = 2*self.m3_width
control_pos = vector(-self.control_logic.width-control_gap,
self.bank.height-self.control_logic.height-3*self.supply_rail_width)
control_pos = vector(-self.control_logic.width - self.m3_pitch,
self.bank.height - self.control_logic.height - 3*self.supply_rail_width)
self.add_control_logic(position=control_pos)
# Leave room for the control routes to the left of the flops
addr_pos = vector(self.control_logic_inst.lx() + 4*self.m2_pitch,
3*self.supply_rail_pitch)
self.add_control_addr_dff(addr_pos)
self.add_addr_dff(addr_pos)
self.width = self.bank.width + self.control_logic.height + control_gap
self.height = self.bank.height
self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
self.height = self.bank.height + 2*self.supply_rail_pitch
def route_shared_banks(self):
@ -933,7 +932,7 @@ class sram(design.design):
return line_positions
def add_control_addr_dff(self, position):
def add_addr_dff(self, position):
""" Add and place address and control flops """
self.addr_dff_inst = self.add_inst(name="address",
mod=self.addr_dff,
@ -945,7 +944,7 @@ class sram(design.design):
inputs.append("ADDR[{}]".format(i))
outputs.append("A[{}]".format(i))
self.connect_inst(inputs + outputs + ["clk", "vdd", "gnd"])
self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"])
def add_control_logic(self, position):
""" Add and place control logic """
@ -989,9 +988,6 @@ class sram(design.design):
for i in range(self.addr_size):
self.copy_layout_pin(self.addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i))
self.copy_layout_pin(self.addr_dff_inst, "clk")
# Power ring contains the power pins
def add_two_banks(self):
# Placement of bank 0 (left)
@ -1083,10 +1079,8 @@ class sram(design.design):
self.copy_layout_pin(self.control_logic_inst, n.lower(), n)
# Connect the clock between the flops and control module
# FIXME: Buffered clock should drive the flops, but then
# it would change the setup time...
flop_pin = self.addr_dff_inst.get_pin("clk")
ctrl_pin = self.control_logic_inst.get_pin("clk")
ctrl_pin = self.control_logic_inst.get_pin("clk_buf")
flop_pos = flop_pin.uc()
ctrl_pos = ctrl_pin.bc()
mid_ypos = 0.5*(ctrl_pos.y+flop_pos.y)
@ -1109,7 +1103,7 @@ class sram(design.design):
self.add_path("metal1", [left_rail_pos, vdd_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left_rail_pos,
size = (1,3),
size = (1,self.supply_vias),
rotate=90)
# Route the vdd rails to the TOP
@ -1121,7 +1115,7 @@ class sram(design.design):
self.add_path("metal2", [top_rail_pos, vdd_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=top_rail_pos,
size = (1,3))
size = (1,self.supply_vias))
def route_single_bank_gnd(self):
@ -1138,7 +1132,7 @@ class sram(design.design):
self.add_path("metal1", [left_rail_pos, gnd_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left_rail_pos,
size = (1,3),
size = (1,self.supply_vias),
rotate=90)
# Route the vdd rails to the TOP
@ -1150,7 +1144,7 @@ class sram(design.design):
self.add_path("metal2", [top_rail_pos, gnd_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=top_rail_pos,
size = (1,3))
size = (1,self.supply_vias))
def sp_write(self, sp_name):

View File

@ -0,0 +1,44 @@
#!/usr/bin/env python2.7
"""
Run a regresion test on a dff_array.
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
class dff_inv_array_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify
import verify
OPTS.check_lvsdrc = False
import dff_inv_array
debug.info(2, "Testing dff_inv_array for 3x3")
a = dff_inv_array.dff_inv_array(rows=3, columns=3)
self.local_check(a)
debug.info(2, "Testing dff_inv_array for 1x3")
a = dff_inv_array.dff_inv_array(rows=1, columns=3)
self.local_check(a)
debug.info(2, "Testing dff_inv_array for 3x1")
a = dff_inv_array.dff_inv_array(rows=3, columns=1)
self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -0,0 +1,36 @@
#!/usr/bin/env python2.7
"""
Run a regresion test on a dff_inv.
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
class dff_inv_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify
import verify
OPTS.check_lvsdrc = False
import dff_inv
debug.info(2, "Testing dff_inv 4x")
a = dff_inv.dff_inv(4)
self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -135,7 +135,6 @@ def write_netgen_script(cell_name, sp_name):
# This circuit has symmetries and needs to be flattened to resolve them or the banks won't pass
# Is there a more elegant way to add this when needed?
f.write("flatten class {{{0}.spice precharge_array}}\n".format(cell_name))
f.write("flatten class {{{0}.spice dff_array}}\n".format(cell_name))
f.write("property {{nfet {0}.spice}} remove as ad ps pd\n".format(cell_name))
f.write("property {{pfet {0}.spice}} remove as ad ps pd\n".format(cell_name))
f.write("property {{n {0}}} remove as ad ps pd\n".format(sp_name))