From 34e180b901becc520811e3690a676e90aee52a0e Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 30 May 2017 12:50:07 -0700 Subject: [PATCH 1/2] Analytical delay model from Bin Wu. Unit test not passing. --- compiler/TODO | 14 +++ compiler/bank.py | 24 +++++ compiler/bitcell.py | 11 +++ compiler/bitcell_array.py | 43 +++++++++ compiler/characterizer/lib.py | 8 +- compiler/globals.py | 4 +- compiler/hierarchical_decoder.py | 27 ++++++ compiler/hierarchical_predecode2x4.py | 18 ++++ compiler/hierarchical_predecode3x8.py | 14 +++ compiler/hierarchy_spice.py | 106 +++++++++++++++++++++++ compiler/ms_flop.py | 17 ++++ compiler/ms_flop_array.py | 5 ++ compiler/nand_2.py | 11 ++- compiler/nand_3.py | 9 ++ compiler/options.py | 1 + compiler/pinv.py | 11 ++- compiler/sense_amp.py | 7 ++ compiler/sense_amp_array.py | 4 + compiler/sram.py | 10 +++ compiler/tests/23_lib_sram_model_test.py | 70 +++++++++++++++ compiler/tri_gate.py | 11 +++ compiler/tri_gate_array.py | 5 ++ compiler/wordline_driver.py | 13 +++ technology/freepdk45/tech/tech.py | 8 ++ technology/scn3me_subm/tech/tech.py | 11 +++ 25 files changed, 457 insertions(+), 5 deletions(-) create mode 100644 compiler/tests/23_lib_sram_model_test.py diff --git a/compiler/TODO b/compiler/TODO index 5526f91a..3d7d7255 100644 --- a/compiler/TODO +++ b/compiler/TODO @@ -38,3 +38,17 @@ timing code too. Convert characterizer into a Python package +cal_delay_over_path functions in hierarchy_spice +can wire as output(it only take capcitance now). +maybe consider make rc_net a class + +dont use dictionary in analytical model make it like vector class + +add wire delay model for bank connection + +#may 15 +-explain why nand_2 fail lef +-add bank seg for delay +-build better sense amp + + diff --git a/compiler/bank.py b/compiler/bank.py index 8c60c9d9..d464587c 100644 --- a/compiler/bank.py +++ b/compiler/bank.py @@ -228,6 +228,7 @@ class bank(design.design): self.wordline_driver = self.mod_wordline_driver(name="wordline_driver", rows=self.num_rows) + #self.wordline_driver.logic_effort_sizing(self.num_cols) self.add_mod(self.wordline_driver) self.inv = pinv(name="pinv", @@ -1516,3 +1517,26 @@ class bank(design.design): offset=[x_offset + drc["minwidth_metal1"], y_offset], mirror="R90") + + def delay(self, slope): + """ return analytical delay of the bank""" + msf_addr_delay = self.msf_address.delay(slope, + self.decoder.input_load()) + + decoder_delay = self.decoder.delay(msf_addr_delay.slope, + self.wordline_driver.input_load()) + + word_driver_delay = self.wordline_driver.delay(decoder_delay.slope, + self.bitcell_array.input_load()) + + bitcell_array_delay = self.bitcell_array.delay(word_driver_delay.slope) + + bl_t_data_out_delay = self.sens_amp_array.delay(bitcell_array_delay.slope, + self.bitcell_array.output_load()) + # output load of bitcell_array is set to be only small part of bl for sense amp. + + data_t_DATA_delay = self.tri_gate_array.delay(bl_t_data_out_delay.slope) + + result = msf_addr_delay + decoder_delay + word_driver_delay \ + + bitcell_array_delay + bl_t_data_out_delay + data_t_DATA_delay + return result diff --git a/compiler/bitcell.py b/compiler/bitcell.py index 56509875..81127a4f 100644 --- a/compiler/bitcell.py +++ b/compiler/bitcell.py @@ -21,3 +21,14 @@ class bitcell(design.design): self.width = bitcell.chars["width"] self.height = bitcell.chars["height"] + def delay(self, slope, load=0, swing = 0.5): + # delay of bit cell is not like a driver(from WL) + # so the slope used should be 0 + # it should not be slope dependent? + # because the value is there + # the delay is only over half transsmission gate + from tech import spice + r = spice["min_tx_r"]*3 + c_para = spice["min_tx_c_para"]#ff + result = self.cal_delay_with_rc(r = r, c = c_para+load, slope = slope, swing = swing) + return result diff --git a/compiler/bitcell_array.py b/compiler/bitcell_array.py index c6a2265a..9dec605f 100644 --- a/compiler/bitcell_array.py +++ b/compiler/bitcell_array.py @@ -1,9 +1,11 @@ import debug import design +from tech import drc, spice from vector import vector from globals import OPTS + class bitcell_array(design.design): """ Creates a rows x cols array of memory cells. Assumes bit-lines @@ -140,3 +142,44 @@ class bitcell_array(design.design): offset.y += self.cell.height # increments to the next column width offset.x += self.cell.width + + def delay(self, slope, load=0): + from tech import drc + wl_wire = self.gen_wl_wire() + wl_wire.return_delay_over_wire(slope) + + wl_to_cell_delay = wl_wire.return_delay_over_wire(slope) + # hypothetical delay from cell to bl end without sense amp + bl_wire = self.gen_bl_wire() + cell_load = 2 * bl_wire.return_input_cap() # we ingore the wire r + # hence just use the whole c + bl_swing = 0.1 + cell_delay = self.cell.delay(wl_to_cell_delay.slope, cell_load, swing = bl_swing) + + #we do not consider the delay over the wire for now + #bl_wire_delay = bl_wire.return_delay_over_wire(cell_delay.slope, swing = bl_swing) + #return [wl_to_cell_delay, cell_delay, bl_wire_delay] + #return self.return_delay(cell_delay.delay+wl_to_cell_delay.delay+bl_wire_delay.delay, + # bl_wire_delay.slope) + return self.return_delay(cell_delay.delay+wl_to_cell_delay.delay, + wl_to_cell_delay.slope) + + def gen_wl_wire(self): + wl_wire = self.generate_rc_net(int(self.column_size), self.width, drc["minwidth_metal1"]) + 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 + bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), self.height, drc["minwidth_metal1"]) + bl_wire.wire_c =spice["min_tx_c_para"] + 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() + 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() + return wl_wire.return_input_cap() diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index d6c5e21d..25d46ac0 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -15,7 +15,7 @@ OPTS = globals.get_opts() class lib: """ lib file generation.""" - def __init__(self, libname, sram, spfile): + def __init__(self, libname, sram, spfile, use_model=OPTS.analytical_delay): self.name = sram.name self.num_words = sram.num_words self.word_size = sram.word_size @@ -61,7 +61,11 @@ class lib: probe_address = "1" * self.addr_size probe_data = self.word_size - 1 - data = self.d.analyze(probe_address, probe_data) + if use_model: + data = sram.analytical_model(slope=0.001) + else: + data = self.d.analyze(probe_address, probe_data) + for i in data.keys(): if i == "read_power" or i == "write_power": continue diff --git a/compiler/globals.py b/compiler/globals.py index 79b04835..489783dd 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -51,7 +51,9 @@ def parse_args(): help="Spice simulator name"), # TODO: Why is this -f? optparse.make_option("-f", "--trim_noncritical", dest="trim_noncritical", - help="Trim noncritical memory cells during simulation") + help="Trim noncritical memory cells during simulation"), + optparse.make_option("-a", "--analyticaldelay", dest="analytical_delay", + help="Use analytical model to calculate delay or not") } # -h --help is implicit. diff --git a/compiler/hierarchical_decoder.py b/compiler/hierarchical_decoder.py index ace1792f..72199b9f 100644 --- a/compiler/hierarchical_decoder.py +++ b/compiler/hierarchical_decoder.py @@ -601,3 +601,30 @@ class hierarchical_decoder(design.design): offset=[xoffset + self.gap_between_rails, yoffset - self.via_shift], rotate=90) + + def delay(self, slope, load = 0.0): + # A -> out + if self.determine_predecodes(self.num_inputs)[1]==0: + pre = self.pre2_4 + nand = self.nand2 + else: + pre = self.pre3_8 + nand = self.nand3 + a_t_out_delay = pre.delay(slope=slope,load = nand.input_load()) + + # out -> z + out_t_z_delay = nand.delay(slope= a_t_out_delay.slope, + load = self.inv.input_load()) + result = a_t_out_delay + out_t_z_delay + + # Z -> decode_out + z_t_decodeout_delay = self.inv.delay(slope = out_t_z_delay.slope , load = load) + result = result + z_t_decodeout_delay + return result + + def input_load(self): + if self.determine_predecodes(self.num_inputs)[1]==0: + pre = self.pre2_4 + else: + pre = self.pre3_8 + return pre.input_load() diff --git a/compiler/hierarchical_predecode2x4.py b/compiler/hierarchical_predecode2x4.py index 411a5c95..a83ed33c 100644 --- a/compiler/hierarchical_predecode2x4.py +++ b/compiler/hierarchical_predecode2x4.py @@ -108,3 +108,21 @@ class hierarchical_predecode2x4(hierarchical_predecode): def get_via_y(self): return self.rail_height + + def delay(self, slope, load = 0.0 ): + #print "pre2x4 consist:" + # A -> B + a_t_b_delay = self.inv.delay(slope=slope,load = self.nand.input_load()) + + # out -> z + b_t_z_delay = self.nand.delay(slope=a_t_b_delay.slope,load = self.inv.input_load()) + result = a_t_b_delay + b_t_z_delay + + # Z -> out + a_t_out_delay = self.inv.delay(slope=b_t_z_delay.slope,load = load) + result = result + a_t_out_delay + #print "end of pre2x4" + return result + + def input_load(self): + return self.inv.input_load() diff --git a/compiler/hierarchical_predecode3x8.py b/compiler/hierarchical_predecode3x8.py index 622a3268..7b6e6f23 100644 --- a/compiler/hierarchical_predecode3x8.py +++ b/compiler/hierarchical_predecode3x8.py @@ -90,3 +90,17 @@ class hierarchical_predecode3x8(hierarchical_predecode): yoffset = (self.number_of_outputs * self.inv.height - 0.5 * drc["minwidth_metal1"]) return yoffset + + def delay(self, slope, load = 0.0 ): + # A -> z + b_t_z_delay = self.nand.delay(slope=slope, + load = self.input_load()) + + # Z -> out + a_t_out_delay = self.inv.delay(slope=b_t_z_delay.slope, + load = load) + result = b_t_z_delay + a_t_out_delay + return result + + def input_load(self): + return self.nand.input_load() diff --git a/compiler/hierarchy_spice.py b/compiler/hierarchy_spice.py index 78fc56e0..f818626c 100644 --- a/compiler/hierarchy_spice.py +++ b/compiler/hierarchy_spice.py @@ -1,6 +1,7 @@ import debug import re import os +import math class spice: @@ -146,3 +147,108 @@ class spice: self.sp_write_file(spfile, usedMODS) del usedMODS spfile.close() + + def delay(self, slope, load=0.0): + """Inform users undefined delay module while building new modules""" + debug.warning("Design Class {0} delay function needs to be defined" + .format(self.__class__.__name__)) + debug.warning("Class {0} name {1}" + .format(self.__class__.__name__, + self.name)) + # return 0 to keep code running while building + return delay_data(0.0, 0.0) + + def cal_delay_with_rc(self, r, c ,slope, swing = 0.5): + """ + Calculate the delay of a mosfet by + modeling it as a resistance driving a capacitance + """ + swing_factor = abs(math.log(1-swing)) # time constant based on swing + delay = swing_factor * r * c #c is in ff and delay is in fs + delay = delay * 0.001 #make the unit to ps + + # Output slope should be linear to input slope which is described + # as 0.005* slope. + + # The slope will be also influenced by the delay. + # If no input slope(or too small to make impact) + # The mimum slope should be the time to charge RC. + # Delay * 2 is from 0 to 100% swing. 0.6*2*delay is from 20%-80%. + slope = delay * 0.6 * 2 + 0.005 * slope + return delay_data(delay = delay, slope = slope) + + + def return_delay(self, delay, slope): + return delay_data(delay, slope) + + def generate_rc_net(self,lump_num, wire_length, wire_width): + return wire_spice_model(lump_num, wire_length, wire_width) + +class delay_data: + """ + This is the delay class to represent the delay information + Time is 50% of the signal to 50% of reference signal delay. + Slope is the 20% of the signal to 80% of signal + """ + def __init__(self, delay=0.0, slope=0.0): + """ init function support two init method""" + # will take single input as a coordinate + self.delay = delay + self.slope = slope + + def __str__(self): + """ override print function output """ + return "Delay Data: Delay "+str(self.delay)+", Slope "+str(self.slope)+"" + + def __add__(self, other): + """ + Override - function (left), for delay_data: a+b != b+a + """ + assert isinstance(other,delay_data) + return delay_data(other.delay + self.delay, + other.slope) + + def __radd__(self, other): + """ + Override - function (right), for delay_data: a+b != b+a + """ + assert isinstance(other,delay_data) + return delay_data(other.delay + self.delay, + self.slope) + + +class wire_spice_model: + """ + This is the spice class to represent a wire + """ + def __init__(self, lump_num, wire_length, wire_width): + self.lump_num = lump_num # the number of segment the wire delay has + self.wire_c = self.cal_wire_c(wire_length, wire_width) # c in each segment + self.wire_r = self.cal_wire_r(wire_length, wire_width) # r in each segment + + def cal_wire_c(self, wire_length, wire_width): + from tech import spice + total_c = spice["wire_unit_c"] * wire_length * wire_width + wire_c = total_c / self.lump_num + return wire_c + + def cal_wire_r(self, wire_length, wire_width): + from tech import spice + total_r = spice["wire_unit_r"] * wire_length / wire_width + wire_r = total_r / self.lump_num + return wire_r + + def return_input_cap(self): + return 0.5 * self.wire_c * self.lump_num + + def return_delay_over_wire(self, slope, swing = 0.5): + # delay will be sum of arithmetic sequence start from + # rc to self.lump_num*rc with step of rc + + swing_factor = abs(math.log(1-swing)) # time constant based on swing + sum_factor = (1+self.lump_num) * self.lump_num * 0.5 # sum of the arithmetic sequence + delay = sum_factor * swing_factor * self.wire_r * self.wire_c + slope = delay * 2 + slope + result= delay_data(delay, slope) + return result + diff --git a/compiler/ms_flop.py b/compiler/ms_flop.py index 05763bbe..d1342817 100644 --- a/compiler/ms_flop.py +++ b/compiler/ms_flop.py @@ -23,3 +23,20 @@ class ms_flop(design.design): self.din_offset = ms_flop.chars["din"] self.dout_offset = ms_flop.chars["dout"] self.dout_bar_offset = ms_flop.chars["dout_bar"] + + def delay(self, slope, load = 0.0): + #import pinv + # use inv to mimic the delay + # din -> mout + #ref = pinv.pinv("reference_inv") + #mid_load = ref.input_load() + #din_t_mout_delay = ref.delay(slope = slope, load = mid_load) + + # mout -> out + #mout_t_out_delay = ref.delay(slope = slope, load = load) + #result = din_t_mout_delay + mout_t_out_delay + + # dont k how to calculate this now, use constant in tech file + from tech import spice + result = self.return_delay(spice["msflop_delay"], spice["msflop_slope"]) + return result diff --git a/compiler/ms_flop_array.py b/compiler/ms_flop_array.py index 904eebd1..95418227 100644 --- a/compiler/ms_flop_array.py +++ b/compiler/ms_flop_array.py @@ -163,3 +163,8 @@ class ms_flop_array(design.design): layer="text", offset=[self.width / 2.0, self.height / 2.0]) + + def delay(self, slope, load=0.0): + result = self.ms_flop.delay(slope = slope, + load = load) + return result diff --git a/compiler/nand_2.py b/compiler/nand_2.py index 4db2324e..3f593313 100644 --- a/compiler/nand_2.py +++ b/compiler/nand_2.py @@ -1,7 +1,7 @@ import contact import design import debug -from tech import drc +from tech import drc, parameter, spice from ptx import ptx from vector import vector from globals import OPTS @@ -438,3 +438,12 @@ class nand_2(design.design): self.Z_position = self.Z_position = self.output_position self.vdd_position = self.vdd_position self.gnd_position = self.gnd_position + + def input_load(self): + from tech import spice + return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] + + def delay(self, slope, load=0.0): + r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) + c_para = spice["min_tx_c_para"]*(self.nmos_size/parameter["min_tx_size"])#ff + return self.cal_delay_with_rc(r = r, c = c_para+load, slope =slope) diff --git a/compiler/nand_3.py b/compiler/nand_3.py index 2e16d8da..265ab34c 100644 --- a/compiler/nand_3.py +++ b/compiler/nand_3.py @@ -4,6 +4,7 @@ import debug from tech import drc from ptx import ptx from vector import vector +from tech import parameter, spice from globals import OPTS class nand_3(design.design): @@ -525,3 +526,11 @@ class nand_3(design.design): self.Z_position = self.Z_position self.vdd_position = self.vdd_position self.gnd_position = self.gnd_position + + def input_load(self): + return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] + + def delay(self, slope, load=0.0): + r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) + c_para = spice["min_tx_c_para"]*(self.nmos_size/parameter["min_tx_size"])#ff + return self.cal_delay_with_rc(r = r, c = c_para+load, slope =slope) diff --git a/compiler/options.py b/compiler/options.py index ffe7f01d..1155ffbb 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -36,3 +36,4 @@ class options(optparse.Values): out_path = "" # Define the output file base name out_name = "" + analytical_delay = True diff --git a/compiler/pinv.py b/compiler/pinv.py index 03575f4b..50356d7a 100644 --- a/compiler/pinv.py +++ b/compiler/pinv.py @@ -1,7 +1,7 @@ import contact import design import debug -from tech import drc, parameter +from tech import drc, parameter, spice from ptx import ptx from vector import vector from math import ceil @@ -401,3 +401,12 @@ class pinv(design.design): def setup_layout_offsets(self): self.A_position = self.input_position self.Z_position = self.output_position + + def input_load(self): + return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] + + def delay(self, slope, load=0.0): + from tech import spice + r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) + c_para = spice["min_tx_c_para"]*(self.nmos_size/parameter["min_tx_size"])#ff + return self.cal_delay_with_rc(r = r, c = c_para+load, slope =slope) diff --git a/compiler/sense_amp.py b/compiler/sense_amp.py index 1f440e4f..4c2be560 100644 --- a/compiler/sense_amp.py +++ b/compiler/sense_amp.py @@ -21,3 +21,10 @@ class sense_amp(design.design): self.width = sense_amp.chars["width"] self.height = sense_amp.chars["height"] + def delay(self, slope, load=0.0): + from tech import spice + r = spice["min_tx_r"]/(10) + c_para = spice["min_tx_c_para"]#ff + result = self.cal_delay_with_rc(r = r, c = c_para+load, slope =slope) + return self.return_delay(result.delay , result.slope) + diff --git a/compiler/sense_amp_array.py b/compiler/sense_amp_array.py index c843cca1..6f5f4de2 100644 --- a/compiler/sense_amp_array.py +++ b/compiler/sense_amp_array.py @@ -131,3 +131,7 @@ class sense_amp_array(design.design): width=self.width, height=drc["minwidth_metal1"]) self.SCLK_positions.append(sclk_offset) + + def delay(self, slope, load =0.0): + result = self.amp.delay(slope=slope, load =load) + return result diff --git a/compiler/sram.py b/compiler/sram.py index 420ac873..22242fa2 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -1170,3 +1170,13 @@ class sram(design.design): self.sp_write_file(sp, usedMODS) del usedMODS sp.close() + + def analytical_model(self,slope): + #control_delay = self.control.delay(slope=slope) + bank_delay = self.bank.delay(slope = slope) + data ={'delay1': bank_delay.delay, 'delay0': bank_delay.delay, + 'min_period1': 0, + 'min_period0': 0, + 'read_power': 0, + 'write_power': 0} + return data diff --git a/compiler/tests/23_lib_sram_model_test.py b/compiler/tests/23_lib_sram_model_test.py new file mode 100644 index 00000000..ad4f3896 --- /dev/null +++ b/compiler/tests/23_lib_sram_model_test.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python2.7 +""" +Check the .lib file for an SRAM +""" + +import unittest +from testutils import header,isapproxdiff +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + + +class lib_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import sram + import lib + + debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank") + total_size = 1024 + for word_size in [1,2,4,8,16,32,64]: + num_words = total_size/word_size# OPTS.config.num_words + s = sram.sram(word_size=word_size, + num_words=num_words, + num_banks=OPTS.config.num_banks, + name="sram_2_16_1_{0}".format(OPTS.tech_name)) + delay = s.analytical_model(0.1) + + + OPTS.check_lvsdrc = True + + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + filename = s.name + ".lib" + libname = OPTS.openram_temp + filename + use_model = True + lib.lib(libname,s,tempspice,use_model) + + + # let's diff the result with a golden model + golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename) + # Randomly decided 10% difference between spice simulators is ok. + if use_model != True: + self.assertEqual(isapproxdiff(libname,golden,0.10),True) + + os.system("rm {0}".format(libname)) + + 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() + + + + + + diff --git a/compiler/tri_gate.py b/compiler/tri_gate.py index cefc51fe..2a98c2fd 100644 --- a/compiler/tri_gate.py +++ b/compiler/tri_gate.py @@ -20,3 +20,14 @@ class tri_gate(design.design): self.width = tri_gate.chars["width"] self.height = tri_gate.chars["height"] + + def delay(self, slope, load=0.0): + from tech import spice + r = spice["min_tx_r"] + c_para = spice["min_tx_c_para"]#ff + return self.cal_delay_with_rc(r = r, c = c_para+load, slope =slope) + + + def input_load(self): + return 9*spice["min_tx_gate_c"] + diff --git a/compiler/tri_gate_array.py b/compiler/tri_gate_array.py index ffc1b057..e6fc41e3 100644 --- a/compiler/tri_gate_array.py +++ b/compiler/tri_gate_array.py @@ -134,3 +134,8 @@ class tri_gate_array(design.design): layer="text", offset=[self.width / 2.0, self.height / 2.0]) + + def delay(self, slope, load=0.0): + result = self.tri.delay(slope = slope, + load = load) + return result diff --git a/compiler/wordline_driver.py b/compiler/wordline_driver.py index 5a7de1b7..376afd08 100644 --- a/compiler/wordline_driver.py +++ b/compiler/wordline_driver.py @@ -211,3 +211,16 @@ class wordline_driver(design.design): self.WL_positions.append(wl_offset) self.vdd_positions.append(vdd_offset) self.gnd_positions.append(gnd_offset) + + def delay(self, slope, load=0): + # decode_out -> net + decode_t_net = self.NAND2.delay(slope = slope, load = self.inv.input_load()) + + # net -> wl + net_t_wl = self.inv.delay(slope = decode_t_net.slope, load = load) + + result = decode_t_net + net_t_wl + return result + + def input_load(self): + return self.NAND2.input_load() diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index e19c8fa7..ef7f952c 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -291,3 +291,11 @@ spice["clk"] = "clk" # estimated feasible period in ns spice["feasible_period"] = 5 +# analytical delay parameter +spice["wire_unit_r"] = 0.075 #ohm +spice["wire_unit_c"] = 0.64 #ff/um^2 +spice["min_tx_r"] = 9250.0 +spice["min_tx_c_para"] = 0.7 #ff +spice["min_tx_gate_c"] = 0.2 +spice["msflop_delay"] = 20.5171565446#ps +spice["msflop_slope"] = 13.0801872972#ps diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index e473c882..72eda0e8 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -208,3 +208,14 @@ spice["clk"] = "clk" # estimated feasible period in ns spice["feasible_period"] = 5 + +# analytical delay parameter +# these numbers are copied from freepdk +# need to measure them in scn cmos +spice["wire_unit_r"] = 0.075 #ohm +spice["wire_unit_c"] = 0.64 #ff/um^2 +spice["min_tx_r"] = 9250.0 +spice["min_tx_c_para"] = 0.7 #ff +spice["min_tx_gate_c"] = 0.1 +spice["msflop_delay"] = 20.5171565446#ps +spice["msflop_slope"] = 13.0801872972#ps From 46c56863ee6412f60952be3f2159298d4598b474 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 31 May 2017 08:01:42 -0700 Subject: [PATCH 2/2] Bin Wu fixed unit test to pass with analytical delay option --- compiler/tests/23_lib_sram_test.py | 5 +- .../sram_2_16_1_freepdk45_analytical.lib | 315 ++++++++++++++++++ .../sram_2_16_1_scn3me_subm_analytical.lib | 315 ++++++++++++++++++ 3 files changed, 634 insertions(+), 1 deletion(-) create mode 100644 compiler/tests/golden/sram_2_16_1_freepdk45_analytical.lib create mode 100644 compiler/tests/golden/sram_2_16_1_scn3me_subm_analytical.lib diff --git a/compiler/tests/23_lib_sram_test.py b/compiler/tests/23_lib_sram_test.py index d132f906..3c78e2ec 100644 --- a/compiler/tests/23_lib_sram_test.py +++ b/compiler/tests/23_lib_sram_test.py @@ -34,7 +34,10 @@ class lib_test(unittest.TestCase): tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) - filename = s.name + ".lib" + if OPTS.analytical_delay == True: + filename = s.name + "_analytical.lib" + else: + filename = s.name + ".lib" libname = OPTS.openram_temp + filename lib.lib(libname,s,tempspice) diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45_analytical.lib b/compiler/tests/golden/sram_2_16_1_freepdk45_analytical.lib new file mode 100644 index 00000000..1f01dbf8 --- /dev/null +++ b/compiler/tests/golden/sram_2_16_1_freepdk45_analytical.lib @@ -0,0 +1,315 @@ +library (sram_2_16_1_freepdk45_lib){ + delay_model : "table_lookup"; + time_unit : "1ns" ; + voltage_unit : "1v" ; + current_unit : "1mA" ; + resistance_unit : "1kohm" ; + capacitive_load_unit(1 ,fF) ; + leakage_power_unit : "1mW" ; + pulling_resistance_unit :"1kohm" ; + operating_conditions(TT){ + voltage : 1.0 ; + temperature : 25.000 ; + } + + input_threshold_pct_fall : 50.0 ; + output_threshold_pct_fall : 50.0 ; + input_threshold_pct_rise : 50.0 ; + output_threshold_pct_rise : 50.0 ; + slew_lower_threshold_pct_fall : 10.0 ; + slew_upper_threshold_pct_fall : 90.0 ; + slew_lower_threshold_pct_rise : 10.0 ; + slew_upper_threshold_pct_rise : 90.0 ; + + default_cell_leakage_power : 0.0 ; + default_leakage_power_density : 0.0 ; + default_input_pin_cap : 1.0 ; + default_inout_pin_cap : 1.0 ; + default_output_pin_cap : 0.0 ; + default_max_transition : 0.5 ; + default_fanout_load : 1.0 ; + default_max_fanout : 4.0 ; + default_connection_class : universal ; + + lu_table_template(CELL_UP_FOR_CLOCK){ + variable_1 : input_net_transition; + variable_2 : total_output_net_capacitance; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CELL_DN_FOR_CLOCK){ + variable_1 : input_net_transition; + variable_2 : total_output_net_capacitance; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CONSTRAINT_HIGH_POS){ + variable_1 : related_pin_transition; + variable_2 : constrained_pin_transition; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CONSTRAINT_LOW_POS){ + variable_1 : related_pin_transition; + variable_2 : constrained_pin_transition; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CLK_TRAN) { + variable_1 : constrained_pin_transition; + index_1 ("0.5"); + } + + lu_table_template(TRAN) { + variable_1 : total_output_net_capacitance; + index_1 ("0.5"); + } + + power_lut_template(INPUT_BY_TRANS_FOR_CLOCK){ + variable_1 : input_transition_time; + index_1 ("0.5"); + } + + power_lut_template(INPUT_BY_TRANS_FOR_SIGNAL){ + variable_1 : input_transition_time; + index_1 ("0.5"); + } + + default_operating_conditions : TT; + + + type (DATA){ + base_type : array; + data_type : bit; + bit_width : 2; + bit_from : 0; + bit_to : 1; + } + + type (ADDR){ + base_type : array; + data_type : bit; + bit_width : 4; + bit_from : 0; + bit_to : 3; + } + +cell (sram_2_16_1_freepdk45){ + memory(){ + type : ram; + address_width : 4; + word_width : 2; + } + interface_timing : true; + dont_use : true; + map_only : true; + dont_touch : true; + area : 799.659625; + + bus(DATA){ + bus_type : DATA; + direction : inout; + max_capacitance : 0.62166; + pin(DATA[1:0]){ + } + three_state : "!OEb & !clk"; + memory_write(){ + address : ADDR; + clocked_on : clk; + } + internal_power(){ + when : "OEb & !clk"; + rise_power(INPUT_BY_TRANS_FOR_SIGNAL){ + values("0.0"); + } + fall_power(INPUT_BY_TRANS_FOR_SIGNAL){ + values("0.0"); + } + } + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.015"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("-0.005"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.011"); + } + } + memory_read(){ + address : ADDR; + } + internal_power(){ + when : "!OEb & !clk"; + rise_power(INPUT_BY_TRANS_FOR_SIGNAL){ + values("0.0"); + } + fall_power(INPUT_BY_TRANS_FOR_SIGNAL){ + values("0.0"); + } + } + timing(){ + timing_sense : non_unate; + related_pin : "clk"; + timing_type : rising_edge; + cell_rise(CELL_UP_FOR_CLOCK) { + values("120.044"); + } + cell_fall(CELL_DN_FOR_CLOCK) { + values("120.044"); + } + rise_transition(TRAN) { + values("120.044"); + } + fall_transition(TRAN) { + values("120.044"); + } + } + } + + bus(ADDR){ + bus_type : ADDR; + direction : input; + capacitance : 0.2091; + max_transition : 0.5; + fanout_load : 1.000000; + pin(ADDR[3:0]){ + } + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.015"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("-0.005"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.011"); + } + } + } + + pin(CSb){ + direction : input; + capacitance : 0.2091; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.015"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("-0.005"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.011"); + } + } + } + + pin(OEb){ + direction : input; + capacitance : 0.2091; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.015"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("-0.005"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.011"); + } + } + } + + pin(WEb){ + direction : input; + capacitance : 0.2091; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.015"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("-0.005"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.011"); + } + } + } + + pin(clk){ + clock : true; + direction : input; + capacitance : 0.2091; + timing(){ + timing_type :"min_pulse_width"; + related_pin : clk; + rise_constraint(CLK_TRAN) { + values("0.0"); + } + fall_constraint(CLK_TRAN) { + values("0.0"); + } + } + timing(){ + timing_type :"minimum_period"; + related_pin : clk; + rise_constraint(CLK_TRAN) { + values("0.0"); + } + fall_constraint(CLK_TRAN) { + values("0.0"); + } + } + } + } +} diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm_analytical.lib b/compiler/tests/golden/sram_2_16_1_scn3me_subm_analytical.lib new file mode 100644 index 00000000..4e0365d9 --- /dev/null +++ b/compiler/tests/golden/sram_2_16_1_scn3me_subm_analytical.lib @@ -0,0 +1,315 @@ +library (sram_2_16_1_scn3me_subm_lib){ + delay_model : "table_lookup"; + time_unit : "1ns" ; + voltage_unit : "1v" ; + current_unit : "1mA" ; + resistance_unit : "1kohm" ; + capacitive_load_unit(1 ,fF) ; + leakage_power_unit : "1mW" ; + pulling_resistance_unit :"1kohm" ; + operating_conditions(TT){ + voltage : 5.0 ; + temperature : 25.000 ; + } + + input_threshold_pct_fall : 50.0 ; + output_threshold_pct_fall : 50.0 ; + input_threshold_pct_rise : 50.0 ; + output_threshold_pct_rise : 50.0 ; + slew_lower_threshold_pct_fall : 10.0 ; + slew_upper_threshold_pct_fall : 90.0 ; + slew_lower_threshold_pct_rise : 10.0 ; + slew_upper_threshold_pct_rise : 90.0 ; + + default_cell_leakage_power : 0.0 ; + default_leakage_power_density : 0.0 ; + default_input_pin_cap : 1.0 ; + default_inout_pin_cap : 1.0 ; + default_output_pin_cap : 0.0 ; + default_max_transition : 0.5 ; + default_fanout_load : 1.0 ; + default_max_fanout : 4.0 ; + default_connection_class : universal ; + + lu_table_template(CELL_UP_FOR_CLOCK){ + variable_1 : input_net_transition; + variable_2 : total_output_net_capacitance; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CELL_DN_FOR_CLOCK){ + variable_1 : input_net_transition; + variable_2 : total_output_net_capacitance; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CONSTRAINT_HIGH_POS){ + variable_1 : related_pin_transition; + variable_2 : constrained_pin_transition; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CONSTRAINT_LOW_POS){ + variable_1 : related_pin_transition; + variable_2 : constrained_pin_transition; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CLK_TRAN) { + variable_1 : constrained_pin_transition; + index_1 ("0.5"); + } + + lu_table_template(TRAN) { + variable_1 : total_output_net_capacitance; + index_1 ("0.5"); + } + + power_lut_template(INPUT_BY_TRANS_FOR_CLOCK){ + variable_1 : input_transition_time; + index_1 ("0.5"); + } + + power_lut_template(INPUT_BY_TRANS_FOR_SIGNAL){ + variable_1 : input_transition_time; + index_1 ("0.5"); + } + + default_operating_conditions : TT; + + + type (DATA){ + base_type : array; + data_type : bit; + bit_width : 2; + bit_from : 0; + bit_to : 1; + } + + type (ADDR){ + base_type : array; + data_type : bit; + bit_width : 4; + bit_from : 0; + bit_to : 3; + } + +cell (sram_2_16_1_scn3me_subm){ + memory(){ + type : ram; + address_width : 4; + word_width : 2; + } + interface_timing : true; + dont_use : true; + map_only : true; + dont_touch : true; + area : 102102.39; + + bus(DATA){ + bus_type : DATA; + direction : inout; + max_capacitance : 11.3222; + pin(DATA[1:0]){ + } + three_state : "!OEb & !clk"; + memory_write(){ + address : ADDR; + clocked_on : clk; + } + internal_power(){ + when : "OEb & !clk"; + rise_power(INPUT_BY_TRANS_FOR_SIGNAL){ + values("0.0"); + } + fall_power(INPUT_BY_TRANS_FOR_SIGNAL){ + values("0.0"); + } + } + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.093"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.024"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.046"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.083"); + } + } + memory_read(){ + address : ADDR; + } + internal_power(){ + when : "!OEb & !clk"; + rise_power(INPUT_BY_TRANS_FOR_SIGNAL){ + values("0.0"); + } + fall_power(INPUT_BY_TRANS_FOR_SIGNAL){ + values("0.0"); + } + } + timing(){ + timing_sense : non_unate; + related_pin : "clk"; + timing_type : rising_edge; + cell_rise(CELL_UP_FOR_CLOCK) { + values("553.907"); + } + cell_fall(CELL_DN_FOR_CLOCK) { + values("553.907"); + } + rise_transition(TRAN) { + values("553.907"); + } + fall_transition(TRAN) { + values("553.907"); + } + } + } + + bus(ADDR){ + bus_type : ADDR; + direction : input; + capacitance : 9.8242; + max_transition : 0.5; + fanout_load : 1.000000; + pin(ADDR[3:0]){ + } + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.093"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.024"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.046"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.083"); + } + } + } + + pin(CSb){ + direction : input; + capacitance : 9.8242; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.093"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.024"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.046"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.083"); + } + } + } + + pin(OEb){ + direction : input; + capacitance : 9.8242; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.093"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.024"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.046"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.083"); + } + } + } + + pin(WEb){ + direction : input; + capacitance : 9.8242; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.093"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.024"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.046"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.083"); + } + } + } + + pin(clk){ + clock : true; + direction : input; + capacitance : 9.8242; + timing(){ + timing_type :"min_pulse_width"; + related_pin : clk; + rise_constraint(CLK_TRAN) { + values("0.0"); + } + fall_constraint(CLK_TRAN) { + values("0.0"); + } + } + timing(){ + timing_type :"minimum_period"; + related_pin : clk; + rise_constraint(CLK_TRAN) { + values("0.0"); + } + fall_constraint(CLK_TRAN) { + values("0.0"); + } + } + } + } +}