mirror of https://github.com/VLSIDA/OpenRAM.git
229 lines
8.9 KiB
Python
229 lines
8.9 KiB
Python
import debug
|
|
import pgate
|
|
import math
|
|
from tech import drc
|
|
from math import log
|
|
from vector import vector
|
|
from globals import OPTS
|
|
from sram_factory import factory
|
|
|
|
class pdriver(pgate.pgate):
|
|
"""
|
|
This instantiates an even or odd number of inverters sized for driving a load.
|
|
"""
|
|
def __init__(self, name, neg_polarity=False, fanout_size=8, size_list = [], height=None):
|
|
|
|
self.stage_effort = 4
|
|
self.height = height
|
|
self.neg_polarity = neg_polarity
|
|
self.size_list = size_list
|
|
self.fanout_size = fanout_size
|
|
|
|
if len(self.size_list) > 0 and (self.fanout_size != 8 or self.neg_polarity):
|
|
debug.error("Cannot specify both size_list and neg_polarity or fanout_size.", -1)
|
|
|
|
pgate.pgate.__init__(self, name, height)
|
|
debug.info(1, "Creating {}".format(self.name))
|
|
|
|
self.compute_sizes()
|
|
|
|
self.create_netlist()
|
|
if not OPTS.netlist_only:
|
|
self.create_layout()
|
|
|
|
def compute_sizes(self):
|
|
# size_list specified
|
|
if len(self.size_list) > 0:
|
|
if not len(self.size_list) % 2:
|
|
neg_polarity = True
|
|
self.num_inv = len(self.size_list)
|
|
else:
|
|
# find the number of stages
|
|
#fanout_size is a unit inverter fanout, not a capacitance so c_in=1
|
|
num_stages = max(1,int(round(log(self.fanout_size)/log(4))))
|
|
|
|
# find inv_num and compute sizes
|
|
if self.neg_polarity:
|
|
if (num_stages % 2 == 0): # if num_stages is even
|
|
self.diff_polarity(num_stages=num_stages)
|
|
else: # if num_stages is odd
|
|
self.same_polarity(num_stages=num_stages)
|
|
else: # positive polarity
|
|
if (num_stages % 2 == 0):
|
|
self.same_polarity(num_stages=num_stages)
|
|
else:
|
|
self.diff_polarity(num_stages=num_stages)
|
|
|
|
|
|
def same_polarity(self, num_stages):
|
|
self.calc_size_list = []
|
|
self.num_inv = num_stages
|
|
# compute sizes
|
|
fanout_size_prev = self.fanout_size
|
|
for x in range(self.num_inv-1,-1,-1):
|
|
fanout_size_prev = int(round(fanout_size_prev/self.stage_effort))
|
|
self.calc_size_list.append(fanout_size_prev)
|
|
|
|
|
|
def diff_polarity(self, num_stages):
|
|
self.calc_size_list = []
|
|
# find which delay is smaller
|
|
if (num_stages > 1):
|
|
delay_below = ((num_stages-1)*(self.fanout_size**(1/num_stages-1))) + num_stages-1
|
|
delay_above = ((num_stages+1)*(self.fanout_size**(1/num_stages+1))) + num_stages+1
|
|
if (delay_above < delay_below):
|
|
# recompute stage_effort for this delay
|
|
self.num_inv = num_stages+1
|
|
polarity_stage_effort = self.fanout_size**(1/self.num_inv)
|
|
else:
|
|
self.num_inv = num_stages-1
|
|
polarity_stage_effort = self.fanout_size**(1/self.num_inv)
|
|
else: # num_stages is 1, can't go to 0
|
|
self.num_inv = num_stages+1
|
|
polarity_stage_effort = self.fanout_size**(1/self.num_inv)
|
|
|
|
|
|
# compute sizes
|
|
fanout_size_prev = self.fanout_size
|
|
for x in range(self.num_inv-1,-1,-1):
|
|
fanout_size_prev = int(round(fanout_size_prev/polarity_stage_effort))
|
|
self.calc_size_list.append(fanout_size_prev)
|
|
|
|
|
|
def create_netlist(self):
|
|
inv_list = []
|
|
|
|
self.add_pins()
|
|
self.add_modules()
|
|
self.create_insts()
|
|
|
|
def create_layout(self):
|
|
self.width = self.num_inv * self.inv_list[0].width
|
|
self.height = self.inv_list[0].height
|
|
|
|
self.place_modules()
|
|
self.route_wires()
|
|
self.add_layout_pins()
|
|
|
|
self.DRC_LVS()
|
|
|
|
def add_pins(self):
|
|
self.add_pin("A")
|
|
self.add_pin("Z")
|
|
self.add_pin("vdd")
|
|
self.add_pin("gnd")
|
|
|
|
def add_modules(self):
|
|
self.inv_list = []
|
|
if len(self.size_list) > 0: # size list specified
|
|
for x in range(len(self.size_list)):
|
|
temp_inv = factory.create(module_type="pinv", size=self.size_list[x], height=self.height)
|
|
self.inv_list.append(temp_inv)
|
|
self.add_mod(self.inv_list[x])
|
|
else: # find inv sizes
|
|
for x in range(len(self.calc_size_list)):
|
|
temp_inv = factory.create(module_type="pinv", size=self.calc_size_list[x], height=self.height)
|
|
self.inv_list.append(temp_inv)
|
|
self.add_mod(self.inv_list[x])
|
|
|
|
|
|
def create_insts(self):
|
|
self.inv_inst_list = []
|
|
for x in range(1,self.num_inv+1):
|
|
# Create first inverter
|
|
if x == 1:
|
|
zbx_int = "Zb{}_int".format(x);
|
|
self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x),
|
|
mod=self.inv_list[x-1]))
|
|
if self.num_inv == 1:
|
|
self.connect_inst(["A", "Z", "vdd", "gnd"])
|
|
else:
|
|
self.connect_inst(["A", zbx_int, "vdd", "gnd"])
|
|
|
|
# Create last inverter
|
|
elif x == self.num_inv:
|
|
zbn_int = "Zb{}_int".format(x-1);
|
|
self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x),
|
|
mod=self.inv_list[x-1]))
|
|
self.connect_inst([zbn_int, "Z", "vdd", "gnd"])
|
|
|
|
# Create middle inverters
|
|
else:
|
|
zbx_int = "Zb{}_int".format(x-1);
|
|
zbn_int = "Zb{}_int".format(x);
|
|
self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x),
|
|
mod=self.inv_list[x-1]))
|
|
self.connect_inst([zbx_int, zbn_int, "vdd", "gnd"])
|
|
|
|
|
|
def place_modules(self):
|
|
# Add INV1 to the left
|
|
self.inv_inst_list[0].place(vector(0,0))
|
|
|
|
# Add inverters to the right of INV1
|
|
for x in range(1,len(self.inv_inst_list)):
|
|
self.inv_inst_list[x].place(vector(self.inv_inst_list[x-1].rx(),0))
|
|
|
|
|
|
def route_wires(self):
|
|
z_inst_list = []
|
|
a_inst_list = []
|
|
# inv_current Z to inv_next A
|
|
for x in range(0,len(self.inv_inst_list)-1):
|
|
z_inst_list.append(self.inv_inst_list[x].get_pin("Z"))
|
|
a_inst_list.append(self.inv_inst_list[x+1].get_pin("A"))
|
|
mid_point = vector(z_inst_list[x].cx(), a_inst_list[x].cy())
|
|
self.add_path("metal1", [z_inst_list[x].center(), mid_point, a_inst_list[x].center()])
|
|
|
|
|
|
def add_layout_pins(self):
|
|
# Continous vdd rail along with label.
|
|
vdd_pin=self.inv_inst_list[0].get_pin("vdd")
|
|
self.add_layout_pin(text="vdd",
|
|
layer="metal1",
|
|
offset=vdd_pin.ll().scale(0,1),
|
|
width=self.width,
|
|
height=vdd_pin.height())
|
|
|
|
# Continous gnd rail along with label.
|
|
gnd_pin=self.inv_inst_list[0].get_pin("gnd")
|
|
self.add_layout_pin(text="gnd",
|
|
layer="metal1",
|
|
offset=gnd_pin.ll().scale(0,1),
|
|
width=self.width,
|
|
height=vdd_pin.height())
|
|
|
|
z_pin = self.inv_inst_list[len(self.inv_inst_list)-1].get_pin("Z")
|
|
self.add_layout_pin_rect_center(text="Z",
|
|
layer=z_pin.layer,
|
|
offset=z_pin.center(),
|
|
width = z_pin.width(),
|
|
height = z_pin.height())
|
|
|
|
a_pin = self.inv_inst_list[0].get_pin("A")
|
|
self.add_layout_pin_rect_center(text="A",
|
|
layer=a_pin.layer,
|
|
offset=a_pin.center(),
|
|
width = a_pin.width(),
|
|
height = a_pin.height())
|
|
|
|
def analytical_delay(self, slew, load=0.0):
|
|
"""Calculate the analytical delay of INV1 -> ... -> INVn"""
|
|
delay = 0;
|
|
if len(self.inv_inst_list) == 1:
|
|
delay = self.inv_inst_list[x].analytical_delay(slew=slew);
|
|
else:
|
|
for x in range(len(self.inv_inst_list-1)):
|
|
load_next = 0.0
|
|
for n in range(x,len(self.inv_inst_list+1)):
|
|
load_next += self.inv_inst_list[x+1]
|
|
if x == 1:
|
|
delay += self.inv_inst_list[x].analytical_delay(slew=slew,
|
|
load=load_next)
|
|
else:
|
|
delay += self.inv_inst_list[x+1].analytical_delay(slew=delay.slew,
|
|
load=load_next)
|
|
return delay
|
|
|
|
|