From 12a853124887c5d6bf362406dd50f629a3d7aa29 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 21 Oct 2020 03:02:39 -0700 Subject: [PATCH 1/7] Allowed for OPTS writeback of words_per_row if automatically generated during generation. --- compiler/options.py | 10 +++------- compiler/sram/sram.py | 6 ++++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/compiler/options.py b/compiler/options.py index 0f94be2b..37d2dc5e 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -9,7 +9,6 @@ import optparse import getpass import os - class options(optparse.Values): """ Class for holding all of the OpenRAM options. All @@ -100,7 +99,7 @@ class options(optparse.Values): # Run with extracted parasitics use_pex = False # Output config with all options - output_extended_config = False + output_extended_config = True ################### @@ -122,9 +121,6 @@ class options(optparse.Values): # For sky130, we need magic for filtering. magic_exe = None - # Number of threads to use - num_threads = 2 - # Should we print out the banner at startup print_banner = True @@ -149,7 +145,7 @@ class options(optparse.Values): bitcell_array = "bitcell_array" bitcell = "bitcell" buf_dec = "pbuf" - column_mux_array = "column_mux_array" + column_mux_array = "single_level_column_mux_array" control_logic = "control_logic" decoder = "hierarchical_decoder" delay_chain = "delay_chain" @@ -158,7 +154,7 @@ class options(optparse.Values): inv_dec = "pinv" nand2_dec = "pnand2" nand3_dec = "pnand3" - nand4_dec = "pnand4" + nand4_dec = "pnand4" # Not available right now precharge_array = "precharge_array" ptx = "ptx" replica_bitline = "replica_bitline" diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 63d971f3..5da8d8aa 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -20,6 +20,12 @@ class sram(): def __init__(self, sram_config, name): sram_config.set_local_config(self) + + # FIXME: adjust this to not directly change OPTS. + # Word-around to have values relevant to OPTS be displayed if not directly set. + OPTS.words_per_row = self.words_per_row + debug.info(1, "Changed OPTS wpr={}".format(self.words_per_row)) + debug.info(1, "OPTS wpr={}".format(OPTS.words_per_row)) # reset the static duplicate name checker for unit tests # in case we create more than one SRAM From eaf285639a6b358adbc5a2c0e96de38563a7b7bf Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Tue, 17 Nov 2020 12:43:17 -0800 Subject: [PATCH 2/7] Added debug measurements along main delay paths in SRAM. WIP. --- compiler/base/graph_util.py | 8 +++ compiler/characterizer/delay.py | 90 +++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) 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 """ From df4c2bad1fdb87350669e3db031dad13ba7bab84 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Tue, 17 Nov 2020 13:30:18 -0800 Subject: [PATCH 3/7] Disabled debug measures that are WIP. --- compiler/characterizer/delay.py | 2 +- compiler/options.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 4a4f80eb..70dbaa89 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -128,7 +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()) + #read_measures.append(self.create_sen_and_bitline_path_measures()) return read_measures diff --git a/compiler/options.py b/compiler/options.py index d21e0062..ae9bc89d 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -123,6 +123,9 @@ class options(optparse.Values): pex_exe = None # For sky130, we need magic for filtering. magic_exe = None + + # Number of threads to use + num_threads = 2 # Should we print out the banner at startup print_banner = True From 35e1a523cceee5d6bc50d70065e30b424a384869 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Tue, 17 Nov 2020 14:29:01 -0800 Subject: [PATCH 4/7] Changed named on delay chain sizing variable. Automatic sizing default is False. --- compiler/characterizer/model_check.py | 4 ++-- compiler/options.py | 2 +- compiler/tests/21_hspice_delay_test.py | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index 794b3988..4ff67b39 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -412,7 +412,7 @@ class model_check(delay): data_dict[self.bl_meas_name] = bl_delays[read_port] data_dict[self.power_name] = powers[read_port] - if not OPTS.use_tech_delay_chain_size: #Model is not used in this case + if OPTS.auto_delay_chain_sizing: #Model is not used in this case wl_model_delays, sae_model_delays = self.get_model_delays(read_port) debug.info(1,"Wordline model delays:\n\t {}".format(wl_model_delays)) debug.info(1,"SAE model delays:\n\t {}".format(sae_model_delays)) @@ -439,7 +439,7 @@ class model_check(delay): name_dict[self.power_name] = self.power_meas_names #name_dict[self.wl_slew_name] = self.wl_slew_meas_names - if not OPTS.use_tech_delay_chain_size: + if OPTS.auto_delay_chain_sizing: name_dict[self.wl_model_name] = name_dict["wl_measures"] #model uses same names as measured. name_dict[self.sae_model_name] = name_dict["sae_measures"] diff --git a/compiler/options.py b/compiler/options.py index ae9bc89d..75cd7a0a 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -60,7 +60,7 @@ class options(optparse.Values): rbl_delay_percentage = 0.5 # Allow manual adjustment of the delay chain over automatic - use_tech_delay_chain_size = False + auto_delay_chain_sizing = False delay_chain_stages = 9 delay_chain_fanout_per_stage = 4 diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index f7af5152..552308ed 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -38,7 +38,6 @@ class timing_sram_test(openram_test): # num_words=256, # num_banks=1) # c.words_per_row=2 - # OPTS.use_tech_delay_chain_size = True c.recompute_sizes() debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") s = factory.create(module_type="sram", sram_config=c) From 7a0f5e15db4630fb3c95eed1633cc89e373c54f4 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Tue, 17 Nov 2020 15:05:07 -0800 Subject: [PATCH 5/7] Added polarity checks in modules to allow to make it easier to get spice rise/fall. Path measures not failing now but should be changed later. --- compiler/bitcells/bitcell_1port.py | 5 +++++ compiler/bitcells/replica_bitcell_1port.py | 5 +++++ compiler/characterizer/delay.py | 19 ++++++++++++++++++- compiler/custom/sense_amp.py | 6 ++++++ compiler/pgates/pand2.py | 5 +++++ compiler/pgates/pinv.py | 5 +++++ compiler/pgates/pnand2.py | 5 +++++ compiler/pgates/pnand3.py | 5 +++++ 8 files changed, 54 insertions(+), 1 deletion(-) diff --git a/compiler/bitcells/bitcell_1port.py b/compiler/bitcells/bitcell_1port.py index f3ff4d10..f2de7c6b 100644 --- a/compiler/bitcells/bitcell_1port.py +++ b/compiler/bitcells/bitcell_1port.py @@ -60,3 +60,8 @@ class bitcell_1port(bitcell_base.bitcell_base): Overrides base class function. """ self.add_graph_edges(graph, port_nets) + + def is_non_inverting(self): + """Return input to output polarity for module""" + + return False \ No newline at end of file diff --git a/compiler/bitcells/replica_bitcell_1port.py b/compiler/bitcells/replica_bitcell_1port.py index 58087b97..efdb5020 100644 --- a/compiler/bitcells/replica_bitcell_1port.py +++ b/compiler/bitcells/replica_bitcell_1port.py @@ -48,3 +48,8 @@ class replica_bitcell_1port(bitcell_base.bitcell_base): def build_graph(self, graph, inst_name, port_nets): """Adds edges based on inputs/outputs. Overrides base class function.""" self.add_graph_edges(graph, port_nets) + + def is_non_inverting(self): + """Return input to output polarity for module""" + + return False \ No newline at end of file diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 70dbaa89..6ab7903c 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -128,7 +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()) + read_measures.append(self.create_sen_and_bitline_path_measures()) return read_measures @@ -756,6 +756,8 @@ class delay(simulation): debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict), 1) result[port].update(read_port_dict) + + self.check_path_measures() return (True, result) @@ -857,6 +859,21 @@ class delay(simulation): debug.info(1, "min_dicharge={}, min_diff={}".format(min_dicharge, min_diff)) return (min_dicharge and min_diff) + def check_path_measures(self): + """Get and check all the delays along the sen and bitline paths""" + + # Get and set measurement, no error checking done other than prints. + debug.info(2, "Checking measures in Delay Path") + value_dict = {} + for meas in self.sen_path_meas+self.bl_path_meas: + val = meas.retrieve_measure() + debug.info(2, '{}={}'.format(meas.name, val)) + if type(val) != float or val > self.period/2: + debug.info(1,'Failed measurement:{}={}'.format(meas.name, val)) + value_dict[meas.name] = val + + return value_dict + def run_power_simulation(self): """ This simulates a disabled SRAM to get the leakage power when it is off. diff --git a/compiler/custom/sense_amp.py b/compiler/custom/sense_amp.py index d57bdaac..3de664fc 100644 --- a/compiler/custom/sense_amp.py +++ b/compiler/custom/sense_amp.py @@ -73,3 +73,9 @@ class sense_amp(design.design): def build_graph(self, graph, inst_name, port_nets): """Adds edges based on inputs/outputs. Overrides base class function.""" self.add_graph_edges(graph, port_nets) + + def is_non_inverting(self): + """Return input to output polarity for module""" + + #FIXME: This only applied to bl/br -> dout and not s_en->dout + return True diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index a6efc93b..56aeffe3 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -146,3 +146,8 @@ class pand2(pgate.pgate): offset=pin.center(), width=pin.width(), height=pin.height()) + + def is_non_inverting(self): + """Return input to output polarity for module""" + + return True \ No newline at end of file diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index eff1337b..6a39ccd4 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -337,3 +337,8 @@ class pinv(pgate.pgate): Overrides base class function. """ self.add_graph_edges(graph, port_nets) + + def is_non_inverting(self): + """Return input to output polarity for module""" + + return False diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index b2fe7bae..56563b31 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -314,3 +314,8 @@ class pnand2(pgate.pgate): Overrides base class function. """ self.add_graph_edges(graph, port_nets) + + def is_non_inverting(self): + """Return input to output polarity for module""" + + return False \ No newline at end of file diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 24396cd4..9df03926 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -347,3 +347,8 @@ class pnand3(pgate.pgate): Overrides base class function. """ self.add_graph_edges(graph, port_nets) + + def is_non_inverting(self): + """Return input to output polarity for module""" + + return False \ No newline at end of file From b201fa4bca2a1a229d49ddba2ceea654c527a8db Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 19 Nov 2020 22:53:38 -0800 Subject: [PATCH 6/7] Fixed path measurement in delay --- compiler/characterizer/delay.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 6ab7903c..5dffcaf8 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -305,7 +305,10 @@ class delay(simulation): 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)) + if i+1 != len(path)-1: + path_meas.append(delay_measure(meas_name, cur_net, next_net, cur_dir, next_dir, measure_scale=1e9, has_port=False)) + else: # Make the last measurement always measure on FALL because is a read 0 + path_meas.append(delay_measure(meas_name, cur_net, next_net, cur_dir, "FALL", 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 From 9fd473ce7031bbab45126a1b5d7da4434993f721 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Fri, 20 Nov 2020 01:11:08 -0800 Subject: [PATCH 7/7] Fixed issue with selection of column address when checking bitline names. --- compiler/characterizer/delay.py | 2 +- compiler/characterizer/simulation.py | 13 +++++++++---- compiler/options.py | 2 +- compiler/pgates/ptx.py | 4 ++++ 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 5dffcaf8..867b3cdb 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -1163,7 +1163,7 @@ class delay(simulation): # The inverse address needs to share the same bitlines as the probe address as the trimming will remove all other bitlines # This is only an issue when there is a column mux and the address maps to different bitlines. - column_addr = self.probe_address[:self.sram.col_addr_size] # do not invert this part + column_addr = self.get_column_addr() # do not invert this part inverse_address = "" for c in self.probe_address[self.sram.col_addr_size:]: # invert everything else if c=="0": diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index af30df9b..f6ee260d 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -403,6 +403,10 @@ class simulation(): pin_names.append("{0}".format("gnd")) return pin_names + def get_column_addr(self): + """Returns column address of probe bit""" + return self.probe_address[:self.sram.col_addr_size] + def add_graph_exclusions(self): """ Exclude portions of SRAM from timing graph which are not relevant @@ -434,11 +438,12 @@ class simulation(): debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.") debug.info(2, "s_en name = {}".format(self.sen_name)) - + + column_addr = self.get_column_addr() bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port) - port_pos = -1 - len(str(self.probe_data)) - len(str(port)) + port_pos = -1 - len(str(column_addr)) - len(str(port)) - if bl_name_port.endswith(str(port) + "_" + str(self.probe_data)): + if bl_name_port.endswith(str(port) + "_" + str(column_addr)): self.bl_name = bl_name_port[:port_pos] + "{}" + bl_name_port[port_pos + len(str(port)):] elif not bl_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0 self.bl_name = bl_name_port @@ -446,7 +451,7 @@ class simulation(): self.bl_name = bl_name_port debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.") - if br_name_port.endswith(str(port) + "_" + str(self.probe_data)): + if br_name_port.endswith(str(port) + "_" + str(column_addr)): self.br_name = br_name_port[:port_pos] + "{}" + br_name_port[port_pos + len(str(port)):] elif not br_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0 self.br_name = br_name_port diff --git a/compiler/options.py b/compiler/options.py index 75cd7a0a..db78a70e 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -151,7 +151,7 @@ class options(optparse.Values): bitcell_array = "bitcell_array" bitcell = "bitcell" buf_dec = "pbuf" - column_mux_array = "single_level_column_mux_array" + column_mux_array = "column_mux_array" control_logic = "control_logic" decoder = "hierarchical_decoder" delay_chain = "delay_chain" diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index ae32e557..b697b9a3 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -549,3 +549,7 @@ class ptx(design.design): """ self.add_graph_edges(graph, port_nets) + def is_non_inverting(self): + """Return input to output polarity for module""" + + return True