From ea1a1c7705a00285c0c8cd764146071e894480a2 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Fri, 9 Nov 2018 17:14:52 -0800 Subject: [PATCH] Added delay chain resizing based on analytical delay. --- compiler/characterizer/logical_effort.py | 5 +- compiler/modules/control_logic.py | 90 ++++++++++++++++--- compiler/sram_base.py | 109 ++++++++++++----------- 3 files changed, 137 insertions(+), 67 deletions(-) diff --git a/compiler/characterizer/logical_effort.py b/compiler/characterizer/logical_effort.py index d0f32eb7..7d3a2eb9 100644 --- a/compiler/characterizer/logical_effort.py +++ b/compiler/characterizer/logical_effort.py @@ -8,6 +8,8 @@ class logical_effort(): """ beta = parameter["beta"] min_inv_cin = 1+beta + pinv=parameter["min_inv_para_delay"] + def __init__(self, size, cin, cout, parasitic): self.cin = cin self.cout = cout @@ -32,4 +34,5 @@ def calculate_relative_delay(stage_effort_list, pinv=parameter["min_inv_para_del total_delay = 0 for stage in stage_effort_list: total_delay += stage.get_stage_delay(pinv) - return total_delay \ No newline at end of file + return total_delay + \ No newline at end of file diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 49e051dd..21595698 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -12,13 +12,14 @@ from dff_inv_array import dff_inv_array import math from vector import vector from globals import OPTS +import logical_effort class control_logic(design.design): """ Dynamically generated Control logic for the total SRAM circuit. """ - def __init__(self, num_rows, words_per_row, port_type="rw"): + def __init__(self, num_rows, words_per_row, sram=None, port_type="rw"): """ Constructor """ name = "control_logic_" + port_type design.design.__init__(self, name) @@ -28,6 +29,11 @@ class control_logic(design.design): self.words_per_row = words_per_row self.port_type = port_type + #This is needed to resize the delay chain. Likely to be changed at some point. + self.sram=sram + self.wl_timing_tolerance = 1 #Determines how much larger the sen delay should be. Accounts for possible error in model. + self.parasitic_inv_delay = 0 #Keeping 0 for now until further testing. + if self.port_type == "rw": self.num_control_signals = 2 else: @@ -94,13 +100,19 @@ class control_logic(design.design): c = reload(__import__(OPTS.replica_bitline)) replica_bitline = getattr(c, OPTS.replica_bitline) - delay_stages, delay_fanout = self.get_delay_chain_size() + delay_stages_heuristic, delay_fanout_heuristic = self.get_heuristic_delay_chain_size() bitcell_loads = int(math.ceil(self.num_rows / 2.0)) - self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads, name="replica_bitline_"+self.port_type) + self.replica_bitline = replica_bitline(delay_stages_heuristic, delay_fanout_heuristic, bitcell_loads, name="replica_bitline_"+self.port_type) + + if self.sram != None and not self.is_sen_timing_okay(): + #Resize the delay chain (by instantiating a new rbl) if the analytical timing failed. + delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic) + self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads, name="replica_bitline_resized_"+self.port_type) + self.add_mod(self.replica_bitline) - def get_delay_chain_size(self): - """Determine the size of the delay chain used for the Sense Amp Enable """ + def get_heuristic_delay_chain_size(self): + """Use a basic heuristic to determine the size of the delay chain used for the Sense Amp Enable """ # FIXME: These should be tuned according to the additional size parameters delay_fanout = 3 # This can be anything >=2 # Delay stages Must be non-inverting @@ -112,6 +124,35 @@ class control_logic(design.design): delay_stages = 4 return (delay_stages, delay_fanout) + def is_sen_timing_okay(self): + self.wl_delay = self.get_delay_to_wl() + self.sen_delay = self.get_delay_to_sen() + + #The sen delay must always be bigger than than the wl delay. This decides how much larger the sen delay must be before + #a re-size is warranted. + + if self.wl_delay*self.wl_timing_tolerance >= self.sen_delay: + return False + else: + return True + + def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout): + """Determine the size of the delay chain used for the Sense Amp Enable using path delays""" + previous_delay_chain_delay = (previous_fanout+1+self.parasitic_inv_delay)*previous_stages + debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay)) + + delay_fanout = 3 # This can be anything >=2 + #The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each + #inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value + required_delay = self.wl_delay*self.wl_timing_tolerance - (self.sen_delay-previous_delay_chain_delay) + debug.check(required_delay > 0, "Cannot size delay chain to have negative delay") + delay_stages = int(required_delay/(delay_fanout+1+self.parasitic_inv_delay)) + if delay_stages%2 == 1: #force an even number of stages. + delay_stages+=1 + #Fanout can be varied as well but is a little more complicated but potentially optimal. + debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay)) + return (delay_stages, delay_fanout) + def setup_signal_busses(self): """ Setup bus names, determine the size of the busses etc """ @@ -595,26 +636,48 @@ class control_logic(design.design): offset=pin.ll(), height=pin.height(), width=pin.width()) + + def get_delay_to_wl(self): + """Get the delay (in delay units) of the clk to a wordline in the bitcell array""" + debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") + stage_efforts = self.determine_wordline_stage_efforts() + clk_to_wl_delay = logical_effort.calculate_relative_delay(stage_efforts, self.parasitic_inv_delay) + debug.info(1, "Clock to wordline delay is {} delay units".format(clk_to_wl_delay)) + return clk_to_wl_delay - def determine_wordline_stage_efforts(self, external_cout): - """Follows the clock signal to the clk_buf signal adding each stages stage effort to a list""" + def determine_wordline_stage_efforts(self): + """Follows the clock signal to the clk_buf signal to the wordline signal for the total path efforts""" stage_effort_list = [] #Calculate the load on clk_buf within the module and add it to external load internal_cout = self.ctrl_dff_array.get_clk_cin() - clk_buf_cap = internal_cout+external_cout + external_cout = self.sram.get_clk_cin() #First stage is the clock buffer - stage_effort_list += self.clkbuf.determine_clk_buf_stage_efforts(clk_buf_cap) + stage_effort_list += self.clkbuf.determine_clk_buf_stage_efforts(internal_cout+external_cout) + + #Then ask the sram for the other path delays (from the bank) + stage_effort_list += self.sram.determine_wordline_stage_efforts() return stage_effort_list - def determine_sa_enable_stage_efforts(self, ext_clk_buf_cout, ext_sen_cout): + def get_delay_to_sen(self): + """Get the delay (in delay units) of the clk to a sense amp enable. + This does not incorporate the delay of the replica bitline. + """ + debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") + stage_efforts = self.determine_sa_enable_stage_efforts() + clk_to_sen_delay = logical_effort.calculate_relative_delay(stage_efforts, self.parasitic_inv_delay) + debug.info(1, "Clock to s_en delay is {} delay units".format(clk_to_sen_delay)) + return clk_to_sen_delay + + def determine_sa_enable_stage_efforts(self): """Follows the clock signal to the sense amp enable signal adding each stages stage effort to a list""" stage_effort_list = [] #Calculate the load on clk_buf_bar int_clk_buf_cout = self.get_clk_buf_bar_cin() - clk_buf_bar_cout = int_clk_buf_cout+ext_clk_buf_cout + ext_clk_buf_cout = self.sram.get_clk_bar_cin() + #First stage is the clock buffer - stage1 = self.clkbuf.determine_clk_buf_bar_stage_efforts(clk_buf_bar_cout) + stage1 = self.clkbuf.determine_clk_buf_bar_stage_efforts(int_clk_buf_cout+ext_clk_buf_cout) stage_effort_list += stage1 #nand2 stage @@ -638,7 +701,8 @@ class control_logic(design.design): stage_effort_list.append(stage5) #inverter (inv8) stage, s_en output - stage6 = self.inv8.get_effort_stage(ext_sen_cout) + clk_sen_cout = self.sram.get_sen_cin() + stage6 = self.inv8.get_effort_stage(clk_sen_cout) stage_effort_list.append(stage6) return stage_effort_list diff --git a/compiler/sram_base.py b/compiler/sram_base.py index bcedb6ad..7d52e093 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -20,7 +20,9 @@ class sram_base(design): sram_config.set_local_config(self) self.bank_insts = [] - + + #For logical effort delay calculations. + self.all_mods_except_control_done = False def add_pins(self): """ Add pins for entire SRAM. """ @@ -67,8 +69,6 @@ class sram_base(design): # Must create the control logic before pins to get the pins self.add_modules() self.add_pins() - self.calculate_delay_to_wl() - self.calculate_delay_to_sen() # This is for the lib file if we don't create layout self.width=0 self.height=0 @@ -216,22 +216,6 @@ class sram_base(design): c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) self.bitcell = self.mod_bitcell() - - #c = reload(__import__(OPTS.control_logic)) - #self.mod_control_logic = getattr(c, OPTS.control_logic) - - - from control_logic import control_logic - # Create the control logic module for each port type - if OPTS.num_rw_ports>0: - self.control_logic = self.control_logic_rw = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="rw") - self.add_mod(self.control_logic_rw) - if OPTS.num_w_ports>0: - self.control_logic_w = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="w") - self.add_mod(self.control_logic_w) - if OPTS.num_r_ports>0: - self.control_logic_r = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="r") - self.add_mod(self.control_logic_r) # Create the address and control flops (but not the clk) from dff_array import dff_array @@ -261,7 +245,23 @@ class sram_base(design): self.supply_rail_width = self.bank.supply_rail_width self.supply_rail_pitch = self.bank.supply_rail_pitch + + #The control logic can resize itself based on the other modules. Requires all other modules added before control logic. + self.all_mods_except_control_done = True + #c = reload(__import__(OPTS.control_logic)) + #self.mod_control_logic = getattr(c, OPTS.control_logic) + from control_logic import control_logic + # Create the control logic module for each port type + if OPTS.num_rw_ports>0: + self.control_logic = self.control_logic_rw = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row,sram=self, port_type="rw") + self.add_mod(self.control_logic_rw) + if OPTS.num_w_ports>0: + self.control_logic_w = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row,sram=self, port_type="w") + self.add_mod(self.control_logic_w) + if OPTS.num_r_ports>0: + self.control_logic_r = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row,sram=self, port_type="r") + self.add_mod(self.control_logic_r) def create_bank(self,bank_num): """ Create a bank """ @@ -456,24 +456,26 @@ class sram_base(design): """ LH and HL are the same in analytical model. """ return self.bank.analytical_delay(vdd,slew,load) - def calculate_delay_to_wl(self): - """Get the delay (in delay units) of the clk to a wordline in the bitcell array""" - stage_efforts = self.determine_wordline_stage_efforts() - clk_to_wl_delay = logical_effort.calculate_relative_delay(stage_efforts, 0) - debug.info(1, "Clock to wordline delay is {} delay units".format(clk_to_wl_delay)) - return clk_to_wl_delay + # def get_delay_to_wl(self): + # """Get the delay (in delay units) of the clk to a wordline in the bitcell array""" + # debug.check(self.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") + # stage_efforts = self.determine_wordline_stage_efforts() + # clk_to_wl_delay = logical_effort.calculate_relative_delay(stage_efforts, self.pinv) + # debug.info(1, "Clock to wordline delay is {} delay units".format(clk_to_wl_delay)) + # return clk_to_wl_delay def determine_wordline_stage_efforts(self): - """Get the all the stage efforts for each stage in the path from clk to a wordline""" + """Get the all the stage efforts for each stage in the path from clk_buf to a wordline""" + #clk stage_effort_list = [] - clk_buf_cout = self.get_clk_cin() - #Assume rw only. There are important differences with multiport that will need to be accounted for. - if self.control_logic_rw != None: - stage_effort_list += self.control_logic_rw.determine_wordline_stage_efforts(clk_buf_cout) - else: - stage_effort_list += self.control_logic_r.determine_wordline_stage_efforts(clk_buf_cout) + # clk_buf_cout = self.get_clk_cin() + # #Assume rw only. There are important differences with multiport that will need to be accounted for. + # if self.control_logic_rw != None: + # stage_effort_list += self.control_logic_rw.determine_wordline_stage_efforts(clk_buf_cout) + # else: + # stage_effort_list += self.control_logic_r.determine_wordline_stage_efforts(clk_buf_cout) - #Clk_buf then move to the bank/wordline driver. Get the delay stages there. + #Clk_buf originates from the control logic so only the bank is related to the wordline path external_wordline_cout = 0 #No loading on the wordline other than in the bank. stage_effort_list += self.bank.determine_wordline_stage_efforts(external_wordline_cout) @@ -491,27 +493,28 @@ class sram_base(design): return row_addr_clk_cin + data_clk_cin + col_addr_clk_cin + bank_clk_cin - def calculate_delay_to_sen(self): - """Get the delay (in delay units) of the clk to a sense amp enable. - This does not incorporate the delay of the replica bitline. - """ - stage_efforts = self.determine_sa_enable_stage_efforts() - clk_to_sen_delay = logical_effort.calculate_relative_delay(stage_efforts, 0) - debug.info(1, "Clock to s_en delay is {} delay units".format(clk_to_sen_delay)) - return clk_to_sen_delay + # def get_delay_to_sen(self): + # """Get the delay (in delay units) of the clk to a sense amp enable. + # This does not incorporate the delay of the replica bitline. + # """ + # debug.check(self.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") + # stage_efforts = self.determine_sa_enable_stage_efforts() + # clk_to_sen_delay = logical_effort.calculate_relative_delay(stage_efforts, self.pinv) + # debug.info(1, "Clock to s_en delay is {} delay units".format(clk_to_sen_delay)) + # return clk_to_sen_delay - def determine_sa_enable_stage_efforts(self): - """Get the all the stage efforts for each stage in the path from clk to a sense amp enable""" - stage_effort_list = [] - clk_buf_bar_cout = self.get_clk_bar_cin() - clk_sen_cout = self.get_sen_cin() - #Assume rw only. There are important differences with multiport that will need to be accounted for. - if self.control_logic_rw != None: - stage_effort_list += self.control_logic_rw.determine_sa_enable_stage_efforts(clk_buf_bar_cout, clk_sen_cout) - else: - stage_effort_list += self.control_logic_r.determine_sa_enable_stage_efforts(clk_buf_bar_cout, clk_sen_cout) + # def determine_sa_enable_stage_efforts(self): + # """Get the all the stage efforts for each stage in the path from clk to a sense amp enable""" + # stage_effort_list = [] + # clk_buf_bar_cout = self.get_clk_bar_cin() + # clk_sen_cout = self.get_sen_cin() + # #Assume rw only. There are important differences with multiport that will need to be accounted for. + # if self.control_logic_rw != None: + # stage_effort_list += self.control_logic_rw.determine_sa_enable_stage_efforts(clk_buf_bar_cout, clk_sen_cout) + # else: + # stage_effort_list += self.control_logic_r.determine_sa_enable_stage_efforts(clk_buf_bar_cout, clk_sen_cout) - return stage_effort_list + # return stage_effort_list def get_clk_bar_cin(self): """Gets the capacitive load the of clock (clk_buf_bar) for the sram""" @@ -527,5 +530,5 @@ class sram_base(design): bank_sen_cin = self.bank.get_sen_cin() return bank_sen_cin - + \ No newline at end of file