diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index 00ff5da3..02248181 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -12,8 +12,10 @@ class pdriver(design.design): This instantiates an even or odd number of inverters sized for driving a load. """ unique_id = 1 + inv_list = [] + inv_inst_list = [] - def __init__(self, driver_size=4, height=None, name="", neg_polarity=False, c_load=8, electrical_effort=1, size_list): + def __init__(self, driver_size=4, height=None, name="", neg_polarity=False, c_load=8, size_list = []): self.stage_effort = 4 self.row_height = height @@ -24,26 +26,26 @@ class pdriver(design.design): self.neg_polarity = neg_polarity self.size_list = size_list self.c_load = c_load - self.electrical_effort = electrical_effort - if length(self.size_list) > 0 and (self.c_load != 8 or self.neg_polarity): - raise Exception("Cannot specify both neg_polarity or c_load and size_list.") + if len(self.size_list) > 0 and (self.c_load != 8 or self.neg_polarity): + raise Exception("Cannot specify both size_list and neg_polarity or c_load.") # size_list specified - if length(self.size_list) > 0: - if not length(self.size_list) % 2: + if len(self.size_list) > 0: + if not len(self.size_list) % 2: neg_polarity = True - self.inv_num = length(self.size_list) + self.inv_num = len(self.size_list) else: - c_in = c_load/electrical_effort - N = max(1, math.loglp(electrical_effort) / math.loglp(3.6)) + # with pinv = i + rho = 3.59 + N = max(1, int(math.log1p(self.stage_effort)/math.log1p(rho))) if self.neg_polarity: - if (int(N) % 2 == 0): # if N is even + if (N % 2 == 0): # if N is even self.inv_num = int(N)+1 else: # if N is odd self.inv_num = int(N) - else: # positive polarity - if (int(N) % 2 == 0): # if N is even + else: # positive polarity + if (N % 2 == 0): self.inv_num = int(N) else: self.inv_num = int(N)+1 @@ -84,62 +86,64 @@ class pdriver(design.design): self.add_pin("gnd") def add_modules(self): - inv_list = [] - - if length(self.size_list) > 0: # size list specified - for x in length(self.size_list): - inv_list.append.=(pinv(size=size_list[x], height=self.row_height)) - self.add_mod(inv_list[x]) - else: - # Shield the cap, but have at least a stage effort of 4 + if len(self.size_list) > 0: # size list specified + for x in range(len(self.size_list)): + self.inv_list.append(pinv(size=self.size_list[x], height=self.row_height)) + self.add_mod(self.inv_list[x]) + else: # find inv sizes + # shield the cap, but have at least a stage effort of 4 input_size = max(1,int(self.driver_size/self.stage_effort)) - for x in inv_num: - inv_list.append(pinv(size=input_size, height=self.row_height)) - self.add_mod(inv_list[x]) + self.inv_list.append(pinv(size=input_size, height=self.row_height)) + self.add_mod(self.inv_list[0]) + + # work backwards + for x in range(self.inv_num-1, 0, -1): + c_in = max(input_size, int(round(self.c_load/self.stage_effort ,0))) + self.c_load = c_in + self.inv_list.append(pinv(size=c_in, height=self.row_height)) + self.add_mod(self.inv_list[x]) def create_insts(self): - inv_inst_list = [] - for x in range(1,self.inv_num+1): # Create first inverter if x == 1: zbx_int = "Zb{}_int".format(x); - inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x), - mod=self.inv_list[x])) + self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x), + mod=self.inv_list[x-1])) if self.inv_num == 1: self.connect_inst(["A", "Z", "vdd", "gnd"]) else: self.connect_inst(["A", zbx_int, "vdd", "gnd"]) # Create last inverter - else if x == inv_num: + elif x == self.inv_num: zbn_int = "Zb{}_int".format(x-1); - inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x), - mod=self.inv_list[x])) + 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); - inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x), - mod=self.inv_list[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 - inv_inst_list[0].place(vector(0,0)) + self.inv_inst_list[0].place(vector(0,0)) # Add inverters to the right of INV1 - for x in range(1,len(inv_inst_list)): - inv_inst_list[x].place(vector(inv_inst_list[x-1].rx(),0)) + 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(inv_inst_list)-1): + 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()) @@ -148,7 +152,7 @@ class pdriver(design.design): def add_layout_pins(self): # Continous vdd rail along with label. - vdd_pin=inv_inst_list[0].get_pin("vdd") + 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), @@ -156,25 +160,43 @@ class pdriver(design.design): height=vdd_pin.height()) # Continous gnd rail along with label. - gnd_pin=inv_inst_list[0].get_pin("gnd") + 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 = inv_inst_list[len(inv_inst_list)-1].get_pin("Z") + z_pin = self.inv_inst_list[len(self.inv_inst_list)-1].get_pin("Z") self.add_layout_pin_rect_center(text="Z", - layer="metal2", - offset=z_pin.center()) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=z_pin.center()) + layer=z_pin.layer, + offset=z_pin.center(), + width = z_pin.width(), + height = z_pin.height()) - a_pin = inv_inst_list[0].get_pin("A") + a_pin = self.inv_inst_list[0].get_pin("A") self.add_layout_pin_rect_center(text="A", - layer="metal2", - offset=a_pin.center()) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=a_pin.center()) + layer=a_pin.layer, + offset=a_pin.center(), + width = a_pin.width(), + height = a_pin.height()) - \ No newline at end of file + def analytical_delay(self, slew, load=0.0): + """Calculate the analytical delay of DFF -> INV -> ... -> INV""" + 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 + + diff --git a/compiler/tests/04_pdriver_test.py b/compiler/tests/04_pdriver_test.py new file mode 100644 index 00000000..df1bd15c --- /dev/null +++ b/compiler/tests/04_pdriver_test.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 2-row buffer cell +""" + +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 pdriver_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + global verify + import verify + + import pdriver + + debug.info(2, "Testing inverter/buffer 4x 8x") + #a = pdriver.pdriver(c_load = 4,size_list = [1,2,4,8]) + #a = pdriver.pdriver(size_list = [1,2,4,8]) + a = pdriver.pdriver(c_load = 4) + #a = pdriver.pdriver(c_load = 4, neg_polarity = True) + self.local_check(a) + + 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()