diff --git a/compiler/base/graph_util.py b/compiler/base/graph_util.py index d7afbf26..909e94c8 100644 --- a/compiler/base/graph_util.py +++ b/compiler/base/graph_util.py @@ -117,6 +117,14 @@ class timing_graph(): cur_slew = delays[-1].slew return delays + + def get_edge_mods(self, path): + """Return all edge mods associated with path""" + + if len(path) == 0: + return [] + + return [self.edge_mods[(path[i], path[i+1])] for i in range(len(path)-1)] def __str__(self): """ override print function output """ diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index dfb05db3..4a4f80eb 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -128,6 +128,7 @@ class delay(simulation): read_measures.append(self.create_bitline_measurement_objects()) read_measures.append(self.create_debug_measurement_objects()) read_measures.append(self.create_read_bit_measures()) + read_measures.append(self.create_sen_and_bitline_path_measures()) return read_measures @@ -249,6 +250,95 @@ class delay(simulation): qbar_meas = voltage_at_measure("v_qbar_{}".format(meas_tag), qbar_name) return {bit_polarity.NONINVERTING: q_meas, bit_polarity.INVERTING: qbar_meas} + + def create_sen_and_bitline_path_measures(self): + """Create measurements for the s_en and bitline paths for individual delays per stage.""" + + # FIXME: There should be a default_read_port variable in this case, pathing is done with this + # but is never mentioned otherwise + port = self.read_ports[0] + sen_and_port = self.sen_name+str(port) + bl_and_port = self.bl_name.format(port) # bl_name contains a '{}' for the port + # Isolate the s_en and bitline paths + debug.info(1, "self.bl_name = {}".format(self.bl_name)) + debug.info(1, "self.graph.all_paths = {}".format(self.graph.all_paths)) + sen_paths = [path for path in self.graph.all_paths if sen_and_port in path] + bl_paths = [path for path in self.graph.all_paths if bl_and_port in path] + debug.check(len(sen_paths)==1, 'Found {} paths which contain the s_en net.'.format(len(sen_paths))) + debug.check(len(bl_paths)==1, 'Found {} paths which contain the bitline net.'.format(len(bl_paths))) + sen_path = sen_paths[0] + bitline_path = bl_paths[0] + + # Get the measures + self.sen_path_meas = self.create_delay_path_measures(sen_path) + self.bl_path_meas = self.create_delay_path_measures(bitline_path) + all_meas = self.sen_path_meas + self.bl_path_meas + + # Paths could have duplicate measurements, remove them before they go to the stim file + all_meas = self.remove_duplicate_meas_names(all_meas) + # FIXME: duplicate measurements still exist in the member variables, since they have the same + # name it will still work, but this could cause an issue in the future. + + return all_meas + + def remove_duplicate_meas_names(self, measures): + """Returns new list of measurements without duplicate names""" + + name_set = set() + unique_measures = [] + for meas in measures: + if meas.name not in name_set: + name_set.add(meas.name) + unique_measures.append(meas) + + return unique_measures + + def create_delay_path_measures(self, path): + """Creates measurements for each net along given path.""" + + # Determine the directions (RISE/FALL) of signals + path_dirs = self.get_meas_directions(path) + + # Create the measurements + path_meas = [] + for i in range(len(path)-1): + cur_net, next_net = path[i], path[i+1] + cur_dir, next_dir = path_dirs[i], path_dirs[i+1] + meas_name = "delay_{}_to_{}".format(cur_net, next_net) + path_meas.append(delay_measure(meas_name, cur_net, next_net, cur_dir, next_dir, measure_scale=1e9, has_port=False)) + # Some bitcell logic is hardcoded for only read zeroes, force that here as well. + path_meas[-1].meta_str = sram_op.READ_ZERO + path_meas[-1].meta_add_delay = True + + return path_meas + + def get_meas_directions(self, path): + """Returns SPICE measurements directions based on path.""" + + # Get the edges modules which define the path + edge_mods = self.graph.get_edge_mods(path) + + # Convert to booleans based on function of modules (inverting/non-inverting) + mod_type_bools = [mod.is_non_inverting() for mod in edge_mods] + + #FIXME: obtuse hack to differentiate s_en input from bitline in sense amps + if self.sen_name in path: + # Force the sense amp to be inverting for s_en->DOUT. + # bitline->DOUT is non-inverting, but the module cannot differentiate inputs. + s_en_index = path.index(self.sen_name) + mod_type_bools[s_en_index] = False + debug.info(2,'Forcing sen->dout to be inverting.') + + # Use these to determine direction list assuming delay start on neg. edge of clock (FALL) + # Also, use shorthand that 'FALL' == False, 'RISE' == True to simplify logic + bool_dirs = [False] + cur_dir = False # All Paths start on FALL edge of clock + for mod_bool in mod_type_bools: + cur_dir = (cur_dir == mod_bool) + bool_dirs.append(cur_dir) + + # Convert from boolean to string + return ['RISE' if dbool else 'FALL' for dbool in bool_dirs] def set_load_slew(self, load, slew): """ Set the load and slew """