From 3c44ce2df65bd8be4719af18550d968debce6704 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 8 Aug 2019 02:33:51 -0700 Subject: [PATCH] Replaced analytical characterization with graph implementation. Removed most analytical delay functions used by old chacterizer. --- compiler/characterizer/delay.py | 54 ++++++++++--------- compiler/modules/bank.py | 35 +----------- compiler/modules/bitcell_array.py | 12 +---- compiler/modules/replica_bitcell_array.py | 12 +---- compiler/modules/sense_amp_array.py | 5 +- .../modules/single_level_column_mux_array.py | 9 ---- compiler/sram/sram_base.py | 26 +-------- 7 files changed, 33 insertions(+), 120 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 90e1561e..81b31ef8 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -1277,6 +1277,7 @@ class delay(simulation): def sum_delays(self, delays): """Adds the delays (delay_data objects) so the correct slew is maintained""" + delay = delays[0] for i in range(1, len(delays)): delay+=delays[i] @@ -1303,40 +1304,41 @@ class delay(simulation): bl_name,br_name = self.get_bl_name(self.graph.all_paths, port) bl_path = [path for path in self.graph.all_paths if bl_name in path][0] + # Set delay/power for slews and loads + port_data = self.get_empty_measure_data_dict() + power = self.analytical_power(slews, loads) debug.info(1,'Slew, Load, Delay(ns), Slew(ns)') + max_delay = 0.0 for slew in slews: for load in loads: + # Calculate delay based on slew and load path_delays = self.graph.get_timing(bl_path, self.corner, slew, load) total_delay = self.sum_delays(path_delays) + max_delay = max(max_delay, total_delay.delay) debug.info(1,'{}, {}, {}, {}'.format(slew,load,total_delay.delay/1e3, total_delay.slew/1e3)) - debug.error('Not finished with graph delay characterization.', 1) - # power = self.analytical_power(slews, loads) - # port_data = self.get_empty_measure_data_dict() - # relative_loads = [logical_effort.convert_farad_to_relative_c(c_farad) for c_farad in loads] - # for slew in slews: - # for load in relative_loads: - # self.set_load_slew(load,slew) - # bank_delay = self.sram.analytical_delay(self.corner, self.slew,self.load) - # for port in self.all_ports: - # for mname in self.delay_meas_names+self.power_meas_names: - # if "power" in mname: - # port_data[port][mname].append(power.dynamic) - # elif "delay" in mname: - # port_data[port][mname].append(bank_delay[port].delay/1e3) - # elif "slew" in mname: - # port_data[port][mname].append(bank_delay[port].slew/1e3) - # else: - # debug.error("Measurement name not recognized: {}".format(mname),1) - # period_margin = 0.1 - # risefall_delay = bank_delay[self.read_ports[0]].delay/1e3 - # sram_data = { "min_period":risefall_delay*2*period_margin, - # "leakage_power": power.leakage} - # debug.info(2,"SRAM Data:\n{}".format(sram_data)) - # debug.info(2,"Port Data:\n{}".format(port_data)) - return (sram_data,port_data) + # Delay is only calculated on a single port and replicated for now. + for port in self.all_ports: + for mname in self.delay_meas_names+self.power_meas_names: + if "power" in mname: + port_data[port][mname].append(power.dynamic) + elif "delay" in mname and port in self.read_ports: + port_data[port][mname].append(total_delay.delay/1e3) + elif "slew" in mname and port in self.read_ports: + port_data[port][mname].append(total_delay.slew/1e3) + else: + debug.error("Measurement name not recognized: {}".format(mname),1) - + # Estimate the period as double the delay with margin + period_margin = 0.1 + sram_data = { "min_period":(max_delay/1e3)*2*period_margin, + "leakage_power": power.leakage} + + debug.info(2,"SRAM Data:\n{}".format(sram_data)) + debug.info(2,"Port Data:\n{}".format(port_data)) + + return (sram_data,port_data) + def analytical_power(self, slews, loads): """Get the dynamic and leakage power from the SRAM""" diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 613a3381..7774e15f 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -976,40 +976,7 @@ class bank(design.design): self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=control_pos) - - def analytical_delay(self, corner, slew, load, port): - """ return analytical delay of the bank. This will track the clock to output path""" - #FIXME: This delay is determined in the control logic. Should be moved here. - # word_driver_delay = self.wordline_driver.analytical_delay(corner, - # slew, - # self.bitcell_array.input_load()) - - #FIXME: Array delay is the same for every port. - word_driver_slew = 0 - if self.words_per_row > 1: - bitline_ext_load = self.port_data[port].column_mux_array.get_drain_cin() - else: - bitline_ext_load = self.port_data[port].sense_amp_array.get_drain_cin() - - bitcell_array_delay = self.bitcell_array.analytical_delay(corner, word_driver_slew, bitline_ext_load) - - bitcell_array_slew = 0 - #This also essentially creates the same delay for each port. Good structure, no substance - if self.words_per_row > 1: - sa_load = self.port_data[port].sense_amp_array.get_drain_cin() - column_mux_delay = self.port_data[port].column_mux_array.analytical_delay(corner, - bitcell_array_slew, - sa_load) - else: - column_mux_delay = [] - - column_mux_slew = 0 - sense_amp_delay = self.port_data[port].sense_amp_array.analytical_delay(corner, - column_mux_slew, - load) - # output load of bitcell_array is set to be only small part of bl for sense amp. - return bitcell_array_delay + column_mux_delay + sense_amp_delay - + def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): """Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline""" #Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 90405d75..bf4932a3 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -148,17 +148,7 @@ class bitcell_array(design.design): for pin_name in ["vdd", "gnd"]: for pin in inst.get_pins(pin_name): self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer) - - def analytical_delay(self, corner, slew, load): - """Returns relative delay of the bitline in the bitcell array""" - from tech import parameter - #The load being driven/drained is mostly the bitline but could include the sense amp or the column mux. - #The load from the bitlines is due to the drain capacitances from all the other bitlines and wire parasitics. - drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) - wire_unit_load = .05 * drain_load #Wires add 5% to this. - bitline_load = (drain_load+wire_unit_load)*self.row_size - return [self.cell.analytical_delay(corner, slew, load+bitline_load)] - + def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" from tech import drc, parameter diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 89a8ff04..d6d7d19e 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -373,17 +373,7 @@ class replica_bitcell_array(design.design): def get_rbl_br_name(self, port): """ Return the BR for the given RBL port """ return self.rbl_br_names[port] - - def analytical_delay(self, corner, slew, load): - """Returns relative delay of the bitline in the bitcell array""" - from tech import parameter - #The load being driven/drained is mostly the bitline but could include the sense amp or the column mux. - #The load from the bitlines is due to the drain capacitances from all the other bitlines and wire parasitics. - drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) - wire_unit_load = 0.05 * drain_load #Wires add 5% to this. - bitline_load = (drain_load+wire_unit_load)*self.row_size - return [self.cell.analytical_delay(corner, slew, load+bitline_load)] - + def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" from tech import drc, parameter diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 40c3adeb..598886c0 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -144,10 +144,7 @@ class sense_amp_array(design.design): def input_load(self): return self.amp.input_load() - - def analytical_delay(self, corner, slew, load): - return [self.amp.analytical_delay(corner, slew=slew, load=load)] - + def get_en_cin(self): """Get the relative capacitance of all the sense amp enable connections in the array""" sense_amp_en_cin = self.amp.get_en_cin() diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index e270f2f6..7e3beaad 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -210,15 +210,6 @@ class single_level_column_mux_array(design.design): self.add_via_center(layers=("metal1", "via1", "metal2"), offset=br_out_offset) - - def analytical_delay(self, corner, slew, load): - from tech import parameter - """Returns relative delay that the column mux adds""" - #Single level column mux will add parasitic loads from other mux pass transistors and the sense amp. - drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) - array_load = drain_load*self.words_per_row - return [self.mux.analytical_delay(corner, slew, load+array_load)] - def get_drain_cin(self): """Get the relative capacitance of the drain of the NMOS pass TX""" from tech import parameter diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 45befe16..da780317 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -566,31 +566,7 @@ class sram_base(design, verilog, lef): self.sp_write_file(sp, usedMODS) del usedMODS sp.close() - - - def analytical_delay(self, corner, slew,load): - """ Estimates the delay from clk -> DOUT - LH and HL are the same in analytical model. """ - delays = {} - for port in self.all_ports: - if port in self.readonly_ports: - control_logic = self.control_logic_r - elif port in self.readwrite_ports: - control_logic = self.control_logic_rw - else: - delays[port] = self.return_delay(0,0) #Write ports do not have a lib defined delay, marked as 0 - continue - clk_to_wlen_delays = control_logic.analytical_delay(corner, slew, load) - wlen_to_dout_delays = self.bank.analytical_delay(corner,slew,load,port) #port should probably be specified... - all_delays = clk_to_wlen_delays+wlen_to_dout_delays - total_delay = logical_effort.calculate_absolute_delay(all_delays) - total_delay = self.apply_corners_analytically(total_delay, corner) - last_slew = .1*all_delays[-1].get_absolute_delay() #slew approximated as 10% of delay - last_slew = self.apply_corners_analytically(last_slew, corner) - delays[port] = self.return_delay(delay=total_delay, slew=last_slew) - - return delays - + def get_wordline_stage_efforts(self, inp_is_rise=True): """Get the all the stage efforts for each stage in the path from clk_buf to a wordline""" stage_effort_list = []