From 12a853124887c5d6bf362406dd50f629a3d7aa29 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 21 Oct 2020 03:02:39 -0700 Subject: [PATCH 01/40] 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 02/40] 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 03/40] 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 04/40] 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 05/40] 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 06/40] 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 07/40] 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 From 27a652ac1b65b74dc9c91e1ebdce2b04c3cf0521 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 20 Nov 2020 16:54:53 -0800 Subject: [PATCH 08/40] Fix bounding box of cap arrays --- compiler/modules/col_cap_array.py | 4 ++++ compiler/modules/row_cap_array.py | 14 +++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index 71a59cf8..1b7a1f49 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -39,6 +39,10 @@ class col_cap_array(bitcell_base_array): self.place_array("dummy_r{0}_c{1}", self.mirror) self.add_layout_pins() + + self.height = self.dummy_cell.height + self.width = self.column_size * self.cell.width + self.add_boundary() self.DRC_LVS() diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index 97776eee..583aad65 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -34,6 +34,10 @@ class row_cap_array(bitcell_base_array): self.place_array("dummy_r{0}_c{1}", self.mirror) self.add_layout_pins() + + self.height = self.row_size * self.cell.height + self.width = self.dummy_cell.width + self.add_boundary() self.DRC_LVS() @@ -48,7 +52,7 @@ class row_cap_array(bitcell_base_array): """ Create the module instances used in this design """ self.cell_inst = {} for col in range(self.column_size): - for row in range(1, self.row_size - 1): + for row in range(0, self.row_size): name = "bit_r{0}_c{1}".format(row, col) self.cell_inst[row, col]=self.add_inst(name=name, mod=self.dummy_cell) @@ -67,17 +71,13 @@ class row_cap_array(bitcell_base_array): return bitcell_pins def place_array(self, name_template, row_offset=0): - # We increase it by a well enclosure so the precharges don't overlap our wells - self.height = self.row_size * self.cell.height - self.width = self.column_size * self.cell.width - xoffset = 0.0 for col in range(self.column_size): yoffset = self.cell.height tempx, dir_y = self._adjust_x_offset(xoffset, col, self.column_offset) - for row in range(1, self.row_size - 1): - tempy, dir_x = self._adjust_y_offset(yoffset, row, row_offset) + for row in range(self.row_size): + tempy, dir_x = self._adjust_y_offset(yoffset, row + 1, row_offset) if dir_x and dir_y: dir_key = "XY" From f729e9fca7baab046d196bc8955764a9a917fc72 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 20 Nov 2020 16:56:07 -0800 Subject: [PATCH 09/40] Fix new replica_bitcell_array refactor with end caps. Remove single port end cap exceptions. --- compiler/modules/replica_bitcell_array.py | 50 ++++------- compiler/modules/replica_column.py | 85 ++++++++++--------- .../14_replica_bitcell_array_1rw_1r_test.py | 8 +- .../tests/14_replica_column_1rw_1r_test.py | 18 ++-- compiler/tests/14_replica_column_test.py | 10 +-- 5 files changed, 77 insertions(+), 94 deletions(-) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index ebfd06ba..d0ef36e6 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -318,14 +318,8 @@ class replica_bitcell_array(bitcell_base_array): self.unused_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer)) self.unused_offset = vector(self.unused_pitch, 0) - # Add extra width on the left and right for the unused WLs - self.height = (self.row_size + self.extra_rows) * self.dummy_row.height - self.width = (self.column_size + self.extra_cols) * self.cell.width + 2 * self.unused_pitch - - # This is a bitcell x bitcell offset to scale self.bitcell_offset = vector(self.cell.width, self.cell.height) - self.strap_offset = vector(0, 0) self.col_end_offset = vector(self.cell.width, self.cell.height) self.row_end_offset = vector(self.cell.width, self.cell.height) @@ -336,12 +330,15 @@ class replica_bitcell_array(bitcell_base_array): self.add_end_caps() - # Array was at (0, 0) but move everything so it is at the lower left # We move DOWN the number of left RBL even if we didn't add the column to this bitcell array array_offset = self.bitcell_offset.scale(1 + len(self.left_rbl), 1 + self.rbl[0]) self.translate_all(array_offset.scale(-1, -1)) + # Add extra width on the left and right for the unused WLs + self.height = self.dummy_row_insts[1].uy() + self.width = self.dummy_col_insts[1].rx() + self.add_layout_pins() self.route_unused_wordlines() @@ -375,17 +372,17 @@ class replica_bitcell_array(bitcell_base_array): # Grow from left to right, toward the array for bit, port in enumerate(self.left_rbl): if not self.cell.end_caps: - offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - 1) + self.strap_offset.scale(-len(self.left_rbl) + bit, 0) + self.unused_offset + offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - 1) + self.unused_offset else: - offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) + self.strap_offset.scale(-len(self.left_rbl) + bit, 0) + self.unused_offset + offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) + self.unused_offset self.replica_col_insts[bit].place(offset) # Grow to the right of the bitcell array, array outward for bit, port in enumerate(self.right_rbl): if not self.cell.end_caps: - offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - 1) + self.strap_offset.scale(bit, -self.rbl[0] - 1) + offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - 1) else: - offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) + self.strap_offset.scale(bit, -self.rbl[0] - 1) + offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) self.replica_col_insts[self.rbl[0] + bit].place(offset) @@ -408,37 +405,24 @@ class replica_bitcell_array(bitcell_base_array): # FIXME: These depend on the array size itself # Far top dummy row (first row above array is NOT flipped) flip_dummy = self.rbl[1] % 2 - if not self.cell.end_caps: - dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1] + flip_dummy) + self.bitcell_array_inst.ul() - else: - dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1] + flip_dummy) + self.bitcell_array_inst.ul() - + dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1] + flip_dummy) + self.bitcell_array_inst.ul() self.dummy_row_insts[1].place(offset=dummy_row_offset, mirror="MX" if flip_dummy else "R0") + # FIXME: These depend on the array size itself # Far bottom dummy row (first row below array IS flipped) flip_dummy = (self.rbl[0] + 1) % 2 - if not self.cell.end_caps: - dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - 1 + flip_dummy) + self.unused_offset - else: - dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - (self.col_end_offset.y/self.cell.height) + flip_dummy) + self.unused_offset + dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - 1 + flip_dummy) + self.unused_offset self.dummy_row_insts[0].place(offset=dummy_row_offset, - mirror="MX" if flip_dummy else "R0") + mirror="MX" if flip_dummy else "R0") # Far left dummy col # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array - if not self.cell.end_caps: - dummy_col_offset = self.bitcell_offset.scale(-len(self.left_rbl) - 1, -self.rbl[0] - 1) + self.unused_offset - else: - dummy_col_offset = self.bitcell_offset.scale(-(len(self.left_rbl)*(1+self.strap_offset.x/self.cell.width)) - (self.row_end_offset.x/self.cell.width), -len(self.left_rbl) - (self.col_end_offset.y/self.cell.height)) - + dummy_col_offset = self.bitcell_offset.scale(-len(self.left_rbl) - 1, -self.rbl[0] - 1) + self.unused_offset self.dummy_col_insts[0].place(offset=dummy_col_offset) + # Far right dummy col # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array - if not self.cell.end_caps: - dummy_col_offset = self.bitcell_offset.scale(len(self.right_rbl), -self.rbl[0] - 1) + self.bitcell_array_inst.lr() - else: - dummy_col_offset = self.bitcell_offset.scale(len(self.right_rbl)*(1+self.strap_offset.x/self.cell.width), -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) + self.bitcell_array_inst.lr() - + dummy_col_offset = self.bitcell_offset.scale(len(self.right_rbl), -self.rbl[0] - 1) + self.bitcell_array_inst.lr() self.dummy_col_insts[1].place(offset=dummy_col_offset) def add_layout_pins(self): @@ -455,6 +439,7 @@ class replica_bitcell_array(bitcell_base_array): offset=pin.ll().scale(0, 1), width=self.width, height=pin.height()) + # Replica wordlines (go by the row instead of replica column because we may have to add a pin # even though the column is in another local bitcell array) for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): @@ -549,8 +534,7 @@ class replica_bitcell_array(bitcell_base_array): self.add_power_pin("gnd", right_loc, directions=("H", "H")) # Add a path to connect to the array - self.add_path(pin_layer, [left_loc, left_pin_loc]) - self.add_path(pin_layer, [right_loc, right_pin_loc]) + self.add_path(pin_layer, [left_loc, right_loc], width=pin.height()) def gen_bl_wire(self): if OPTS.netlist_only: diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index f7e0c0fb..b43f2534 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -14,37 +14,38 @@ from tech import layer_properties as layer_props class replica_column(bitcell_base_array): """ Generate a replica bitline column for the replica array. - Rows is the total number of rows i the main array. + Rows is the total number of rows in the main array. rbl is a tuple with the number of left and right replica bitlines. Replica bit specifies which replica column this is (to determine where to put the replica cell relative to the bottom (including the dummy bit at 0). """ def __init__(self, name, rows, rbl, replica_bit, column_offset=0): - super().__init__(rows=sum(rbl) + rows + 2, cols=1, column_offset=column_offset, name=name) + # Used for pin names and properties + self.cell = factory.create(module_type=OPTS.bitcell) + # Row size is the number of rows with word lines + self.row_size = sum(rbl) + rows + # Start of regular word line rows + self.row_start = rbl[0] + 1 + # End of regular word line rows + self.row_end = self.row_start + rows + if not self.cell.end_caps: + self.row_size += 2 + super().__init__(rows=self.row_size, cols=1, column_offset=column_offset, name=name) self.rows = rows self.left_rbl = rbl[0] self.right_rbl = rbl[1] self.replica_bit = replica_bit - # left, right, regular rows plus top/bottom dummy cells - self.total_size = self.left_rbl + rows + self.right_rbl - - # Used for pin names and properties - self.cell = factory.create(module_type=OPTS.bitcell) - # For end caps - try: - if not self.cell.end_caps: - self.total_size += 2 - except AttributeError: - self.total_size += 2 + # Total size includes the replica rows and column cap rows + self.total_size = self.left_rbl + rows + self.right_rbl + 2 self.column_offset = column_offset - debug.check(replica_bit != 0 and replica_bit != rows, - "Replica bit cannot be the dummy row.") - debug.check(replica_bit <= self.left_rbl or replica_bit >= self.total_size - self.right_rbl - 1, + debug.check(replica_bit != 0 and replica_bit != self.total_size - 1, + "Replica bit cannot be the dummy/cap row.") + debug.check(replica_bit < self.row_start or replica_bit >= self.row_end, "Replica bit cannot be in the regular array.") if layer_props.replica_column.even_rows: debug.check(rows % 2 == 0 and (self.left_rbl + 1) % 2 == 0, @@ -61,18 +62,20 @@ class replica_column(bitcell_base_array): self.create_instances() def create_layout(self): - self.height = self.total_size * self.cell.height - self.width = self.cell.width - self.place_instances() + + self.height = self.cell_inst[-1].uy() + self.width = self.cell_inst[0].rx() + self.add_layout_pins() + self.add_boundary() self.DRC_LVS() def add_pins(self): self.create_all_bitline_names() - self.create_all_wordline_names(self.total_size) + self.create_all_wordline_names(self.row_size) self.add_pin_list(self.all_bitline_names, "OUTPUT") self.add_pin_list(self.all_wordline_names, "INPUT") @@ -93,32 +96,32 @@ class replica_column(bitcell_base_array): self.add_mod(self.edge_cell) def create_instances(self): - self.cell_inst = {} + self.cell_inst = [] for row in range(self.total_size): name="rbc_{0}".format(row) - # Top/bottom cell are always dummy cells. - # Regular array cells are replica cells (>left_rbl and self.left_rbl and row < self.total_size - self.right_rbl - 1): - self.cell_inst[row]=self.add_inst(name=name, - mod=self.replica_cell) - self.connect_inst(self.get_bitcell_pins(row, 0)) - elif row==self.replica_bit: - self.cell_inst[row]=self.add_inst(name=name, - mod=self.replica_cell) - self.connect_inst(self.get_bitcell_pins(row, 0)) - elif (row == 0 or row == self.total_size - 1): - self.cell_inst[row]=self.add_inst(name=name, - mod=self.edge_cell) + real_row = row + if self.cell.end_caps: + real_row -= 1 + + # Regular array cells are replica cells + # Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell. + if (row == 0 or row == self.total_size - 1): + self.cell_inst.append(self.add_inst(name=name, + mod=self.edge_cell)) if self.cell.end_caps: - self.connect_inst(self.get_bitcell_pins_col_cap(row, 0)) + self.connect_inst(self.get_bitcell_pins_col_cap(real_row, 0)) else: - self.connect_inst(self.get_bitcell_pins(row, 0)) + self.connect_inst(self.get_bitcell_pins(real_row, 0)) + elif (row==self.replica_bit) or (row >= self.row_start and row < self.row_end): + self.cell_inst.append(self.add_inst(name=name, + mod=self.replica_cell)) + self.connect_inst(self.get_bitcell_pins(real_row, 0)) else: - self.cell_inst[row]=self.add_inst(name=name, - mod=self.dummy_cell) - self.connect_inst(self.get_bitcell_pins(row, 0)) + # Top/bottom cell are always dummy/cap cells. + self.cell_inst.append(self.add_inst(name=name, + mod=self.dummy_cell)) + self.connect_inst(self.get_bitcell_pins(real_row, 0)) def place_instances(self): # Flip the mirrors if we have an odd number of replica+dummy rows at the bottom @@ -184,7 +187,7 @@ class replica_column(bitcell_base_array): height=wl_pin.height()) # Supplies are only connected in the ends - for (index, inst) in self.cell_inst.items(): + for (index, inst) in enumerate(self.cell_inst): for pin_name in ["vdd", "gnd"]: if inst in [self.cell_inst[0], self.cell_inst[self.total_size - 1]]: self.copy_power_pins(inst, pin_name) diff --git a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py b/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py index cf66692e..cbcb40d5 100755 --- a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py +++ b/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py @@ -25,14 +25,14 @@ class replica_bitcell_array_1rw_1r_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() - debug.info(2, "Testing 4x4 non-replica array for cell_1rw_1r") + debug.info(2, "Testing 4x4 non-replica array for dp cell") a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, rbl=[1, 1]) self.local_check(a) - debug.info(2, "Testing 4x4 left replica array for cell_1rw_1r") + debug.info(2, "Testing 4x4 left replica array for dp cell") a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, @@ -40,7 +40,7 @@ class replica_bitcell_array_1rw_1r_test(openram_test): left_rbl=[0]) self.local_check(a) - debug.info(2, "Testing 4x4 array left and right replica for cell_1rw_1r") + debug.info(2, "Testing 4x4 array left and right replica for dp cell") a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, @@ -52,7 +52,7 @@ class replica_bitcell_array_1rw_1r_test(openram_test): # Sky 130 has restrictions on the symmetries if OPTS.tech_name != "sky130": - debug.info(2, "Testing 4x4 array right only replica for cell_1rw_1r") + debug.info(2, "Testing 4x4 array right only replica for dp cell") a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, diff --git a/compiler/tests/14_replica_column_1rw_1r_test.py b/compiler/tests/14_replica_column_1rw_1r_test.py index d197a234..bf0a1108 100755 --- a/compiler/tests/14_replica_column_1rw_1r_test.py +++ b/compiler/tests/14_replica_column_1rw_1r_test.py @@ -25,18 +25,22 @@ class replica_column_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() - debug.info(2, "Testing replica column for 6t_cell") + debug.info(2, "Testing one left replica column for dual port") a = factory.create(module_type="replica_column", rows=4, rbl=[1, 0], replica_bit=1) self.local_check(a) - debug.info(2, "Testing replica column for 6t_cell") + debug.info(2, "Testing one right replica column for dual port") + a = factory.create(module_type="replica_column", rows=4, rbl=[0, 1], replica_bit=5) + self.local_check(a) + + debug.info(2, "Testing two (left, right) replica columns for dual port") + a = factory.create(module_type="replica_column", rows=4, rbl=[1, 1], replica_bit=1) + self.local_check(a) + + debug.info(2, "Testing two (left, right) replica columns for dual port") a = factory.create(module_type="replica_column", rows=4, rbl=[1, 1], replica_bit=6) self.local_check(a) - - debug.info(2, "Testing replica column for 6t_cell") - a = factory.create(module_type="replica_column", rows=4, rbl=[2, 0], replica_bit=2) - self.local_check(a) - + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/14_replica_column_test.py b/compiler/tests/14_replica_column_test.py index 4701cdca..7d73524a 100755 --- a/compiler/tests/14_replica_column_test.py +++ b/compiler/tests/14_replica_column_test.py @@ -20,18 +20,10 @@ class replica_column_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - debug.info(2, "Testing replica column for cell_6t") + debug.info(2, "Testing replica column for single port") a = factory.create(module_type="replica_column", rows=4, rbl=[1, 0], replica_bit=1) self.local_check(a) - debug.info(2, "Testing replica column for cell_1rw_1r") - a = factory.create(module_type="replica_column", rows=4, rbl=[1, 1], replica_bit=6) - self.local_check(a) - - debug.info(2, "Testing replica column for cell_1rw_1r") - a = factory.create(module_type="replica_column", rows=4, rbl=[2, 0], replica_bit=2) - self.local_check(a) - globals.end_openram() # run the test from the command line From 718c32752721042d31f3319a0b89fa06cc404bef Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 20 Nov 2020 17:33:15 -0800 Subject: [PATCH 10/40] Fix iteration bug with new type --- compiler/modules/replica_column.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index b43f2534..2a333c8e 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -234,7 +234,7 @@ class replica_column(bitcell_base_array): Excludes all bits except the replica cell (self.replica_bit). """ - for row, cell in self.cell_inst.items(): + for row, cell in enumerate(self.cell_inst): if row != self.replica_bit: self.graph_inst_exclude.add(cell) From 4c75bc003e78f982966e513c6e00cb4f7e84f49a Mon Sep 17 00:00:00 2001 From: mrg Date: Sat, 21 Nov 2020 07:03:59 -0800 Subject: [PATCH 11/40] Fix bounding box of replica array to include wordline grounds. --- compiler/modules/replica_bitcell_array.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index d0ef36e6..ac9432c2 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -6,7 +6,7 @@ import debug from bitcell_base_array import bitcell_base_array -from tech import drc, spice, cell_properties +from tech import drc, spice from vector import vector from globals import OPTS from sram_factory import factory @@ -313,6 +313,7 @@ class replica_bitcell_array(bitcell_base_array): def create_layout(self): # We will need unused wordlines grounded, so we need to know their layer + # and create a space on the left and right for the vias to connect to ground pin = self.cell.get_pin(self.cell.get_all_wl_names()[0]) pin_layer = pin.layer self.unused_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer)) @@ -332,12 +333,13 @@ class replica_bitcell_array(bitcell_base_array): # Array was at (0, 0) but move everything so it is at the lower left # We move DOWN the number of left RBL even if we didn't add the column to this bitcell array + # Note that this doesn't include the row/col cap array_offset = self.bitcell_offset.scale(1 + len(self.left_rbl), 1 + self.rbl[0]) self.translate_all(array_offset.scale(-1, -1)) # Add extra width on the left and right for the unused WLs self.height = self.dummy_row_insts[1].uy() - self.width = self.dummy_col_insts[1].rx() + self.width = self.dummy_col_insts[1].rx() + self.unused_offset.x self.add_layout_pins() @@ -371,19 +373,11 @@ class replica_bitcell_array(bitcell_base_array): # Grow from left to right, toward the array for bit, port in enumerate(self.left_rbl): - if not self.cell.end_caps: - offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - 1) + self.unused_offset - else: - offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) + self.unused_offset - + offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - 1) + self.unused_offset self.replica_col_insts[bit].place(offset) # Grow to the right of the bitcell array, array outward for bit, port in enumerate(self.right_rbl): - if not self.cell.end_caps: - offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - 1) - else: - offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) - + offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - 1) self.replica_col_insts[self.rbl[0] + bit].place(offset) # Replica dummy rows From aa03eec9432cd995342738ca175d4e1c140d4ec8 Mon Sep 17 00:00:00 2001 From: mrg Date: Sat, 21 Nov 2020 07:16:45 -0800 Subject: [PATCH 12/40] Fix syntax error. --- compiler/modules/replica_bitcell_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index ac9432c2..a2494f1b 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -338,8 +338,8 @@ class replica_bitcell_array(bitcell_base_array): self.translate_all(array_offset.scale(-1, -1)) # Add extra width on the left and right for the unused WLs - self.height = self.dummy_row_insts[1].uy() self.width = self.dummy_col_insts[1].rx() + self.unused_offset.x + self.height = self.dummy_row_insts[1].uy() self.add_layout_pins() From 95573c858c9de8bae9571610f8183d06d5fe53bd Mon Sep 17 00:00:00 2001 From: mrg Date: Sat, 21 Nov 2020 08:05:49 -0800 Subject: [PATCH 13/40] Can redefine number of ports in custom_cell_properties --- compiler/base/custom_cell_properties.py | 56 ++++++++++++++++++++----- compiler/base/design.py | 18 ++++---- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/compiler/base/custom_cell_properties.py b/compiler/base/custom_cell_properties.py index 2407f9c2..c01d2fe7 100644 --- a/compiler/base/custom_cell_properties.py +++ b/compiler/base/custom_cell_properties.py @@ -7,7 +7,10 @@ # class _cell: - def __init__(self, port_order, port_types, port_map=None, hard_cell=True, boundary_layer="boundary"): + def __init__(self, port_order, port_types, port_map=None, body_bias=None, hard_cell=True, boundary_layer="boundary"): + + # Some cells may have body bias (well taps) exposed as ports + self._body_bias = body_bias # Specifies if this is a hard (i.e. GDS) cell self._hard_cell = hard_cell @@ -47,14 +50,24 @@ class _cell: return self._port_order @port_order.setter - def port_order(self, x): - self._port_order = x - # Update ordered name list in the new order - self._port_names = [self._port_map[x] for x in self._port_order] - # Update ordered type list in the new order - self._port_types = [self._port_types_map[x] for x in self._port_order] - # Update the index array - self._port_indices = [self._port_order.index(x) for x in self._original_port_order] + def port_order(self, port_order): + # If we are going to redefine more ports (i.e. well biases) don't init stuff + old_port_len = len(self._port_order) + if old_port_len == len(port_order): + self._port_order = port_order + # Update ordered name list in the new order + self._port_names = [self._port_map[x] for x in self._port_order] + # Update ordered type list in the new order + self._port_types = [self._port_types_map[x] for x in self._port_order] + # Update the index array + self._port_indices = [self._port_order.index(x) for x in self._original_port_order] + else: + # Do the default constructor again except for types stuff which hasn't been set yet + self._port_order = port_order + self._original_port_order = self._port_order + self._port_map = {x: x for x in self._port_order} + self._port_indices = [self._port_order.index(x) for x in self._original_port_order] + self._port_names = [self._port_map[x] for x in self._port_order] @property def port_indices(self): @@ -65,15 +78,36 @@ class _cell: return self._port_map @port_map.setter - def port_map(self, x): - self._port_map = x + def port_map(self, port_map): + self._port_map = port_map # Update ordered name list to use the new names self._port_names = [self.port_map[x] for x in self._port_order] + @property + def body_bias(self): + return self._body_bias + + @body_bias.setter + def body_bias(self, body_bias): + # It is assumed it is [nwell, pwell] + self._body_bias = body_bias + self._port_map['vnb'] = body_bias[0] + self._port_types['vnb'] = "POWER" + self._port_map['vpb'] = body_bias[1] + self._port_types['vpb'] = "GROUND" + @property def port_types(self): return self._port_types + @port_types.setter + def port_types(self, port_types): + self._port_types = port_types + # Specifies the port directions + self._port_types_map = {x: y for (x, y) in zip(self._port_order, self._port_types)} + # Update ordered type list + self._port_types = [self._port_types_map[x] for x in self._port_order] + @property def boundary_layer(self): return self._boundary_layer diff --git a/compiler/base/design.py b/compiler/base/design.py index 71c1ef92..c8bf3070 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -167,8 +167,8 @@ class design(hierarchy_design): from tech import layer_indices import tech - for layer in layer_indices: - key = "{}_stack".format(layer) + for layer_id in layer_indices: + key = "{}_stack".format(layer_id) # Set the stack as a local helper try: @@ -177,24 +177,24 @@ class design(hierarchy_design): except AttributeError: pass - # Skip computing the pitch for active - if layer == "active": + # Skip computing the pitch for non-routing layers + if layer_id in ["active", "nwell"]: continue # Add the pitch setattr(design, - "{}_pitch".format(layer), - design.compute_pitch(layer, True)) + "{}_pitch".format(layer_id), + design.compute_pitch(layer_id, True)) # Add the non-preferrd pitch (which has vias in the "wrong" way) setattr(design, - "{}_nonpref_pitch".format(layer), - design.compute_pitch(layer, False)) + "{}_nonpref_pitch".format(layer_id), + design.compute_pitch(layer_id, False)) if False: from tech import preferred_directions print(preferred_directions) - from tech import layer, layer_indices + from tech import layer_indices for name in layer_indices: if name == "active": continue From 6e51c3cda03d5ea9045ba3aa86cb4ff82bd896cd Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 22 Nov 2020 07:11:08 -0800 Subject: [PATCH 14/40] PEP8 cleanup bitcell_base --- compiler/bitcells/bitcell_base.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/bitcells/bitcell_base.py b/compiler/bitcells/bitcell_base.py index 09f33482..07ede248 100644 --- a/compiler/bitcells/bitcell_base.py +++ b/compiler/bitcells/bitcell_base.py @@ -95,7 +95,7 @@ class bitcell_base(design.design): labels for pex simulation. """ # If we generated the bitcell, we already know where Q and Q_bar are - if OPTS.bitcell is not "pbitcell": + if OPTS.bitcell != "pbitcell": self.storage_net_offsets = [] for i in range(len(self.get_storage_net_names())): for text in self.gds.getTexts(layer["m1"]): @@ -105,7 +105,6 @@ class bitcell_base(design.design): for i in range(len(self.storage_net_offsets)): self.storage_net_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.storage_net_offsets[i]]) - return(self.storage_net_offsets) def get_bitline_offset(self): @@ -150,7 +149,7 @@ class bitcell_base(design.design): of the bitcell. This is useful for making sense of offsets outside of the bitcell. """ - if OPTS.bitcell is not "pbitcell": + if OPTS.bitcell != "pbitcell": normalized_storage_net_offset = self.get_storage_net_offset() else: @@ -160,12 +159,12 @@ class bitcell_base(design.design): Q_bar_x = net_offset[1][0] - self.leftmost_xpos Q_bar_y = net_offset[1][1] - self.botmost_ypos - normalized_storage_net_offset = [[Q_x,Q_y],[Q_bar_x,Q_bar_y]] + normalized_storage_net_offset = [[Q_x, Q_y], [Q_bar_x, Q_bar_y]] return normalized_storage_net_offset def get_normalized_bitline_offset(self): - return self.get_bitline_offset() + return self.get_bitline_offset() def build_graph(self, graph, inst_name, port_nets): """ From 5ee3f4cc66599a47d9b061cc0c8be3de0379d757 Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 22 Nov 2020 08:24:47 -0800 Subject: [PATCH 15/40] Many edits. Use internal vdd/gnd names. Refactor getters in bitcell to base class. Add BIAS signal type. --- compiler/base/custom_cell_properties.py | 16 ++++++++++- compiler/base/hierarchy_spice.py | 2 +- compiler/bitcells/bitcell_1port.py | 34 +---------------------- compiler/bitcells/bitcell_base.py | 37 +++++++++++++++++++++++-- compiler/modules/bitcell_base_array.py | 17 ++++-------- compiler/options.py | 4 +++ 6 files changed, 61 insertions(+), 49 deletions(-) diff --git a/compiler/base/custom_cell_properties.py b/compiler/base/custom_cell_properties.py index c01d2fe7..70fcc761 100644 --- a/compiler/base/custom_cell_properties.py +++ b/compiler/base/custom_cell_properties.py @@ -208,12 +208,18 @@ class cell_properties(): self._bitcell_2port = _bitcell(["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"], ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]) + self._col_cap_1port = _bitcell(["bl", "br", "vdd"], + ["OUTPUT", "OUTPUT", "POWER"]) + + self._row_cap_1port = _bitcell(["wl", "gnd"], + ["INPUT", "POWER", "GROUND"]) + self._col_cap_2port = _bitcell(["bl0", "br0", "bl1", "br1", "vdd"], ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "POWER"]) self._row_cap_2port = _bitcell(["wl0", "wl1", "gnd"], ["INPUT", "INPUT", "POWER", "GROUND"]) - + @property def ptx(self): return self._ptx @@ -258,6 +264,14 @@ class cell_properties(): def bitcell_2port(self): return self._bitcell_2port + @property + def col_cap_1port(self): + return self._col_cap_1port + + @property + def row_cap_1port(self): + return self._row_cap_1port + @property def col_cap_2port(self): return self._col_cap_2port diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 4a279ba7..29f03c7f 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -48,7 +48,7 @@ class spice(): else: self.lvs_file = self.sp_file - self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"] + self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "BIAS", "POWER", "GROUND"] # Holds subckts/mods for this module self.mods = [] # Holds the pins for this module (in order) diff --git a/compiler/bitcells/bitcell_1port.py b/compiler/bitcells/bitcell_1port.py index f2de7c6b..08180607 100644 --- a/compiler/bitcells/bitcell_1port.py +++ b/compiler/bitcells/bitcell_1port.py @@ -22,38 +22,6 @@ class bitcell_1port(bitcell_base.bitcell_base): super().__init__(name, prop=props.bitcell_1port) debug.info(2, "Create bitcell") - def get_all_wl_names(self): - """ Creates a list of all wordline pin names """ - row_pins = ["wl"] - return row_pins - - def get_all_bitline_names(self): - """ Creates a list of all bitline pin names (both bl and br) """ - return ["bl", "br"] - - def get_all_bl_names(self): - """ Creates a list of all bl pins names """ - return ["bl"] - - def get_all_br_names(self): - """ Creates a list of all br pins names """ - return ["br"] - - def get_bl_name(self, port=0): - """Get bl name""" - debug.check(port == 0, "One port for bitcell only.") - return "bl" - - def get_br_name(self, port=0): - """Get bl name""" - debug.check(port == 0, "One port for bitcell only.") - return "br" - - def get_wl_name(self, port=0): - """Get wl name""" - debug.check(port == 0, "One port for bitcell only.") - return "wl" - def build_graph(self, graph, inst_name, port_nets): """ Adds edges based on inputs/outputs. @@ -64,4 +32,4 @@ class bitcell_1port(bitcell_base.bitcell_base): def is_non_inverting(self): """Return input to output polarity for module""" - return False \ No newline at end of file + return False diff --git a/compiler/bitcells/bitcell_base.py b/compiler/bitcells/bitcell_base.py index 07ede248..388d1b7b 100644 --- a/compiler/bitcells/bitcell_base.py +++ b/compiler/bitcells/bitcell_base.py @@ -26,9 +26,7 @@ class bitcell_base(design.design): self.nets_match = self.do_nets_exist(prop.storage_nets) self.mirror = prop.mirror self.end_caps = prop.end_caps - - self.supplies = ["vdd", "gnd"] - + def get_stage_effort(self, load): parasitic_delay = 1 # This accounts for bitline being drained @@ -172,3 +170,36 @@ class bitcell_base(design.design): """ return + + def get_all_wl_names(self): + """ Creates a list of all wordline pin names """ + row_pins = ["wl"] + return row_pins + + def get_all_bitline_names(self): + """ Creates a list of all bitline pin names (both bl and br) """ + return ["bl", "br"] + + def get_all_bl_names(self): + """ Creates a list of all bl pins names """ + return ["bl"] + + def get_all_br_names(self): + """ Creates a list of all br pins names """ + return ["br"] + + def get_bl_name(self, port=0): + """Get bl name""" + debug.check(port == 0, "One port for bitcell only.") + return "bl" + + def get_br_name(self, port=0): + """Get bl name""" + debug.check(port == 0, "One port for bitcell only.") + return "br" + + def get_wl_name(self, port=0): + """Get wl name""" + debug.check(port == 0, "One port for bitcell only.") + return "wl" + diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 088308f9..a9bfefd5 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -35,11 +35,6 @@ class bitcell_base_array(design.design): self.rbl_wordline_names = [[] for port in self.all_ports] self.all_rbl_wordline_names = [] - # The supply pin names - self.bitcell_supplies = self.cell.supplies - # If the technology needs renaming of the supplies - self.supplies = ["vdd", "gnd"] - def create_all_bitline_names(self): for col in range(self.column_size): for port in self.all_ports: @@ -63,8 +58,8 @@ class bitcell_base_array(design.design): self.add_pin(bl_name, "INOUT") for wl_name in self.get_wordline_names(): self.add_pin(wl_name, "INPUT") - self.add_pin(self.supplies[0], "POWER") - self.add_pin(self.supplies[1], "GROUND") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def get_bitcell_pins(self, row, col): """ @@ -75,8 +70,8 @@ class bitcell_base_array(design.design): for port in self.all_ports: bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))]) bitcell_pins.extend([x for x in self.all_wordline_names if x.endswith("_{0}".format(row))]) - bitcell_pins.append(self.bitcell_supplies[0]) - bitcell_pins.append(self.bitcell_supplies[1]) + bitcell_pins.append("vdd") + bitcell_pins.append("gnd") return bitcell_pins @@ -166,8 +161,8 @@ class bitcell_base_array(design.design): for row in range(self.row_size): for col in range(self.column_size): inst = self.cell_inst[row, col] - for (pin_name, new_name) in zip(self.bitcell_supplies, self.supplies): - self.copy_layout_pin(inst, pin_name, new_name) + for pin_name in ["vdd", "gnd"]: + self.copy_layout_pin(inst, pin_name) def _adjust_x_offset(self, xoffset, col, col_offset): tempx = xoffset diff --git a/compiler/options.py b/compiler/options.py index 4995788a..b5652e7e 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -152,6 +152,8 @@ class options(optparse.Values): bitcell = "bitcell" buf_dec = "pbuf" column_mux_array = "column_mux_array" + col_cap = "col_cap" + col_cap_array = "col_cap_array" control_logic = "control_logic" decoder = "hierarchical_decoder" delay_chain = "delay_chain" @@ -164,6 +166,8 @@ class options(optparse.Values): precharge_array = "precharge_array" ptx = "ptx" replica_bitline = "replica_bitline" + row_cap = "row_cap" + row_cap_array = "row_cap_array" sense_amp_array = "sense_amp_array" sense_amp = "sense_amp" tri_gate_array = "tri_gate_array" From f40e5f6dbae4a9b208c70bf77623b01f5063f60c Mon Sep 17 00:00:00 2001 From: jcirimel Date: Mon, 23 Nov 2020 06:55:47 -0800 Subject: [PATCH 16/40] start of adding additional granularity to 1port col caps --- compiler/base/custom_cell_properties.py | 15 +++++++++++++++ compiler/gdsMill/gdsMill/vlsiLayout.py | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/compiler/base/custom_cell_properties.py b/compiler/base/custom_cell_properties.py index 70fcc761..f8987339 100644 --- a/compiler/base/custom_cell_properties.py +++ b/compiler/base/custom_cell_properties.py @@ -211,6 +211,13 @@ class cell_properties(): self._col_cap_1port = _bitcell(["bl", "br", "vdd"], ["OUTPUT", "OUTPUT", "POWER"]) + self._col_cap_1port_bitcell = _cell(["bl", "br", "vdd", "gnd"], + ["OUTPUT", "OUTPUT", "POWER", "GROUND"]) + + self._col_cap_1port_strap = _cell(["vdd", "gnd"], + ["POWER", "GROUND"]) + + self._row_cap_1port = _bitcell(["wl", "gnd"], ["INPUT", "POWER", "GROUND"]) @@ -268,6 +275,14 @@ class cell_properties(): def col_cap_1port(self): return self._col_cap_1port + @property + def col_cap_1port_bitcell(self): + return self._col_cap_1port_bitcell + + @property + def col_cap_1port_strap(self): + return self._col_cap_1port_strap + @property def row_cap_1port(self): return self._row_cap_1port diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py index 19018119..68e32762 100644 --- a/compiler/gdsMill/gdsMill/vlsiLayout.py +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -600,7 +600,7 @@ class VlsiLayout: shapes = self.getAllShapes(lpp) if len(shapes) != 1: - debug.warning("More than one boundary found in cell: {}".format(structure)) + debug.warning("More than one or no boundaries found in cell: {}".format(structure)) debug.check(len(shapes) != 0, "Error: "+str(structure)+".cell_size information not found yet") max_boundary = None From d2bc7340ede56790af166ec3b3002f83f8f43d60 Mon Sep 17 00:00:00 2001 From: jcirimel Date: Tue, 24 Nov 2020 03:02:55 -0800 Subject: [PATCH 17/40] finish col cap start row cap --- compiler/base/custom_cell_properties.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/compiler/base/custom_cell_properties.py b/compiler/base/custom_cell_properties.py index f8987339..5dd0327a 100644 --- a/compiler/base/custom_cell_properties.py +++ b/compiler/base/custom_cell_properties.py @@ -214,9 +214,14 @@ class cell_properties(): self._col_cap_1port_bitcell = _cell(["bl", "br", "vdd", "gnd"], ["OUTPUT", "OUTPUT", "POWER", "GROUND"]) - self._col_cap_1port_strap = _cell(["vdd", "gnd"], - ["POWER", "GROUND"]) + self._col_cap_1port_strap_power = _cell(["vdd"], + ["POWER"]) + self._col_cap_1port_strap_ground = _cell(["gnd"], + ["GROUND"]) + + self._row_cap_1port_cell = _cell(['vdd]'], + ['POWRER']) self._row_cap_1port = _bitcell(["wl", "gnd"], ["INPUT", "POWER", "GROUND"]) @@ -280,8 +285,16 @@ class cell_properties(): return self._col_cap_1port_bitcell @property - def col_cap_1port_strap(self): - return self._col_cap_1port_strap + def col_cap_1port_strap_power(self): + return self._col_cap_1port_strap_power + + @property + def col_cap_1port_strap_ground(self): + return self._col_cap_1port_strap_ground + + @property + def row_cap_1port_cell(self): + return self._row_cap_1port_cell @property def row_cap_1port(self): From cdcd115cec5d5b6d41c7948a47e3daff287a4652 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 24 Nov 2020 10:35:14 -0800 Subject: [PATCH 18/40] Fix typos --- compiler/base/custom_cell_properties.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/base/custom_cell_properties.py b/compiler/base/custom_cell_properties.py index 5dd0327a..b4d110f8 100644 --- a/compiler/base/custom_cell_properties.py +++ b/compiler/base/custom_cell_properties.py @@ -220,8 +220,8 @@ class cell_properties(): self._col_cap_1port_strap_ground = _cell(["gnd"], ["GROUND"]) - self._row_cap_1port_cell = _cell(['vdd]'], - ['POWRER']) + self._row_cap_1port_cell = _cell(['vdd'], + ['POWER']) self._row_cap_1port = _bitcell(["wl", "gnd"], ["INPUT", "POWER", "GROUND"]) From 4e10f6d8a6a8c43e300b91e2f5304d9678707f4b Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 24 Nov 2020 12:01:00 -0800 Subject: [PATCH 19/40] Make cell/bitcell custom cell external accessible. --- compiler/base/custom_cell_properties.py | 58 +++++++------------------ compiler/modules/row_cap_array.py | 4 +- 2 files changed, 17 insertions(+), 45 deletions(-) diff --git a/compiler/base/custom_cell_properties.py b/compiler/base/custom_cell_properties.py index b4d110f8..7b4d21c9 100644 --- a/compiler/base/custom_cell_properties.py +++ b/compiler/base/custom_cell_properties.py @@ -6,7 +6,7 @@ # All rights reserved. # -class _cell: +class cell: def __init__(self, port_order, port_types, port_map=None, body_bias=None, hard_cell=True, boundary_layer="boundary"): # Some cells may have body bias (well taps) exposed as ports @@ -142,7 +142,7 @@ class _pgate: self.add_implants = add_implants -class _bitcell(_cell): +class bitcell(cell): def __init__(self, port_order, port_types, port_map=None, storage_nets=["Q", "Q_bar"], mirror=None, end_caps=False): super().__init__(port_order, port_types, port_map) @@ -181,55 +181,43 @@ class cell_properties(): self._pgate = _pgate(add_implants=False) - self._inv_dec = _cell(["A", "Z", "vdd", "gnd"], + self._inv_dec = cell(["A", "Z", "vdd", "gnd"], ["INPUT", "OUTPUT", "POWER", "GROUND"]) - self._nand2_dec = _cell(["A", "B", "Z", "vdd", "gnd"], + self._nand2_dec = cell(["A", "B", "Z", "vdd", "gnd"], ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]) - self._nand3_dec = _cell(["A", "B", "C", "Z", "vdd", "gnd"], + self._nand3_dec = cell(["A", "B", "C", "Z", "vdd", "gnd"], ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]) - self._nand4_dec = _cell(["A", "B", "C", "D", "Z", "vdd", "gnd"], + self._nand4_dec = cell(["A", "B", "C", "D", "Z", "vdd", "gnd"], ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]) - self._dff = _cell(["D", "Q", "clk", "vdd", "gnd"], + self._dff = cell(["D", "Q", "clk", "vdd", "gnd"], ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]) - self._write_driver = _cell(['din', 'bl', 'br', 'en', 'vdd', 'gnd'], + self._write_driver = cell(['din', 'bl', 'br', 'en', 'vdd', 'gnd'], ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]) - self._sense_amp = _cell(['bl', 'br', 'dout', 'en', 'vdd', 'gnd'], + self._sense_amp = cell(['bl', 'br', 'dout', 'en', 'vdd', 'gnd'], ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]) - self._bitcell_1port = _bitcell(["bl", "br", "wl", "vdd", "gnd"], + self._bitcell_1port = bitcell(["bl", "br", "wl", "vdd", "gnd"], ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]) - self._bitcell_2port = _bitcell(["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"], + self._bitcell_2port = bitcell(["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"], ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]) - self._col_cap_1port = _bitcell(["bl", "br", "vdd"], + self._col_cap_1port = bitcell(["bl", "br", "vdd"], ["OUTPUT", "OUTPUT", "POWER"]) - self._col_cap_1port_bitcell = _cell(["bl", "br", "vdd", "gnd"], - ["OUTPUT", "OUTPUT", "POWER", "GROUND"]) - - self._col_cap_1port_strap_power = _cell(["vdd"], - ["POWER"]) - - self._col_cap_1port_strap_ground = _cell(["gnd"], - ["GROUND"]) - - self._row_cap_1port_cell = _cell(['vdd'], - ['POWER']) - - self._row_cap_1port = _bitcell(["wl", "gnd"], + self._row_cap_1port = bitcell(["wl", "gnd"], ["INPUT", "POWER", "GROUND"]) - self._col_cap_2port = _bitcell(["bl0", "br0", "bl1", "br1", "vdd"], + self._col_cap_2port = bitcell(["bl0", "br0", "bl1", "br1", "vdd"], ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "POWER"]) - self._row_cap_2port = _bitcell(["wl0", "wl1", "gnd"], + self._row_cap_2port = bitcell(["wl0", "wl1", "gnd"], ["INPUT", "INPUT", "POWER", "GROUND"]) @property @@ -280,22 +268,6 @@ class cell_properties(): def col_cap_1port(self): return self._col_cap_1port - @property - def col_cap_1port_bitcell(self): - return self._col_cap_1port_bitcell - - @property - def col_cap_1port_strap_power(self): - return self._col_cap_1port_strap_power - - @property - def col_cap_1port_strap_ground(self): - return self._col_cap_1port_strap_ground - - @property - def row_cap_1port_cell(self): - return self._row_cap_1port_cell - @property def row_cap_1port(self): return self._row_cap_1port diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index 583aad65..5cee866a 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -35,8 +35,8 @@ class row_cap_array(bitcell_base_array): self.place_array("dummy_r{0}_c{1}", self.mirror) self.add_layout_pins() - self.height = self.row_size * self.cell.height - self.width = self.dummy_cell.width + self.width = max([x.rx() for x in self.insts]) + self.height = max([x.uy() for x in self.insts]) self.add_boundary() self.DRC_LVS() From 0ccb3487b6577c9af512dffe9bd6887bd2892ab1 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 24 Nov 2020 13:27:11 -0800 Subject: [PATCH 20/40] Set default port map --- compiler/base/custom_cell_properties.py | 9 +++++---- compiler/options.py | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/base/custom_cell_properties.py b/compiler/base/custom_cell_properties.py index 7b4d21c9..bb211842 100644 --- a/compiler/base/custom_cell_properties.py +++ b/compiler/base/custom_cell_properties.py @@ -15,7 +15,7 @@ class cell: # Specifies if this is a hard (i.e. GDS) cell self._hard_cell = hard_cell self._boundary_layer = boundary_layer - + # Specifies the port directions self._port_types_map = {x: y for (x, y) in zip(port_order, port_types)} @@ -23,7 +23,8 @@ class cell: # by default it is 1:1 if not port_map: self._port_map = {x: x for x in port_order} - + else: + self._port_map = port_map # Update mapping of names self._original_port_order = port_order self._port_order = port_order @@ -81,8 +82,8 @@ class cell: def port_map(self, port_map): self._port_map = port_map # Update ordered name list to use the new names - self._port_names = [self.port_map[x] for x in self._port_order] - + self._port_names = [self._port_map[x] for x in self._port_order] + @property def body_bias(self): return self._body_bias diff --git a/compiler/options.py b/compiler/options.py index b5652e7e..e4a61052 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -162,7 +162,7 @@ class options(optparse.Values): inv_dec = "pinv" nand2_dec = "pnand2" nand3_dec = "pnand3" - nand4_dec = "pnand4" # Not available right now + nand4_dec = "pnand4" precharge_array = "precharge_array" ptx = "ptx" replica_bitline = "replica_bitline" From fa5296e621d02e9cd264de806102bd2bdf499a3e Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Sun, 29 Nov 2020 11:30:12 -0800 Subject: [PATCH 21/40] Improving magic verification shell scripts. * Output header at start of script. * Output footer at end. * Add a bunch more progress report to magic output. * Make script return the same exit code as magic. --- compiler/verify/magic.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index a8345a96..80385dc1 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -81,13 +81,18 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa run_file = output_path + "run_drc.sh" f = open(run_file, "w") f.write("#!/bin/sh\n") + f.write('export OPENRAM_TECH="{}"\n'.format(os.environ['OPENRAM_TECH'])) + f.write('echo "$(date): Starting DRC using Magic {}"\n'.format(OPTS.drc_exe[1])) + f.write('\n') f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1])) f.write("gds polygon subcell true\n") f.write("gds warning default\n") f.write("gds flatten true\n") f.write("gds readonly true\n") f.write("gds read {}\n".format(gds_name)) + f.write('puts "Finished reading gds {}"\n'.format(gds_name)) f.write("load {}\n".format(cell_name)) + f.write('puts "Finished loading cell {}"\n'.format(cell_name)) # Flatten the cell to get rid of DRCs spanning multiple layers # (e.g. with routes) #f.write("flatten {}_new\n".format(cell_name)) @@ -98,8 +103,11 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa f.write("writeall force\n") f.write("select top cell\n") f.write("expand\n") + f.write('puts "Finished expanding"\n') f.write("drc check\n") + f.write('puts "Finished drc check"\n') f.write("drc catchup\n") + f.write('puts "Finished drc catchup"\n') f.write("drc count total\n") f.write("drc count\n") if not sp_name: @@ -116,6 +124,7 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa if OPTS.tech_name=="sky130": f.write(pre + "extract style ngspice(si)\n") f.write(pre + "extract\n") + f.write('puts "Finished extract"\n') # f.write(pre + "ext2spice hierarchy on\n") # f.write(pre + "ext2spice scale off\n") # lvs exists in 8.2.79, but be backword compatible for now @@ -134,8 +143,12 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa # but they all seem compatible enough. f.write(pre + "ext2spice format ngspice\n") f.write(pre + "ext2spice {}\n".format(cell_name)) + f.write('puts "Finished ext2spice"\n') f.write("quit -noprompt\n") f.write("EOF\n") + f.write("magic_retcode=$?\n") + f.write('echo "$(date): Finished ($magic_retcode) DRC using Magic {}"\n'.format(OPTS.drc_exe[1])) + f.write("exit $magic_retcode\n") f.close() os.system("chmod u+x {}".format(run_file)) @@ -210,12 +223,17 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, out run_file = output_path + "/run_lvs.sh" f = open(run_file, "w") f.write("#!/bin/sh\n") + f.write('export OPENRAM_TECH="{}"\n'.format(os.environ['OPENRAM_TECH'])) + f.write('echo "$(date): Starting LVS using Magic {}"\n'.format(OPTS.drc_exe[1])) f.write("{} -noconsole << EOF\n".format(OPTS.lvs_exe[1])) # f.write("readnet spice {0}.spice\n".format(cell_name)) # f.write("readnet spice {0}\n".format(sp_name)) f.write("lvs {{{0}.spice {0}}} {{{1} {0}}} {2} {0}.lvs.report -full -json\n".format(cell_name, sp_name, setup_file)) f.write("quit\n") f.write("EOF\n") + f.write("magic_retcode=$?\n") + f.write('echo "$(date): Finished ($magic_retcode) LVS using Magic {}"\n'.format(OPTS.drc_exe[1])) + f.write("exit $magic_retcode\n") f.close() os.system("chmod u+x {}".format(run_file)) @@ -408,7 +426,9 @@ def write_script_pex_rule(gds_name, cell_name, sp_name, output): run_file = OPTS.openram_temp + "run_pex.sh" f = open(run_file, "w") f.write("#!/bin/sh\n") - f.write("{} -dnull -noconsole << eof\n".format(OPTS.drc_exe[1])) + f.write('export OPENRAM_TECH="{}"\n'.format(os.environ['OPENRAM_TECH'])) + f.write('echo "$(date): Starting PEX using Magic {}"\n'.format(OPTS.drc_exe[1])) + f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1])) f.write("gds polygon subcell true\n") f.write("gds warning default\n") f.write("gds read {}\n".format(gds_name)) @@ -434,8 +454,11 @@ def write_script_pex_rule(gds_name, cell_name, sp_name, output): f.write("ext2spice extresist on\n") f.write("ext2spice {}\n".format(cell_name)) f.write("quit -noprompt\n") - f.write("eof\n") + f.write("EOF\n") + f.write("magic_retcode=$?\n") f.write("mv {0}.spice {1}\n".format(cell_name, output)) + f.write('echo "$(date): Finished PEX using Magic {}"\n'.format(OPTS.drc_exe[1])) + f.write("exit $magic_retcode\n") f.close() os.system("chmod u+x {}".format(run_file)) From 59c6980052fb30b9be6ee6a889cffc1f78af35cc Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Sun, 29 Nov 2020 12:59:17 -0800 Subject: [PATCH 22/40] Rework run_script command. * Use Python subprocess module. * Echo the command output to the console. * Print while things are still running. --- compiler/verify/run_script.py | 55 +++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/compiler/verify/run_script.py b/compiler/verify/run_script.py index f7bb2d9f..8fda376c 100644 --- a/compiler/verify/run_script.py +++ b/compiler/verify/run_script.py @@ -11,24 +11,67 @@ Some baseline functions to run scripts. import os import debug +import subprocess +import time from globals import OPTS def run_script(cell_name, script="lvs"): """ Run script and create output files. """ + # FIXME: Set this value based on options somewhere + echo_cmd_output = True + cwd = os.getcwd() os.chdir(OPTS.openram_temp) errfile = "{0}{1}.{2}.err".format(OPTS.openram_temp, cell_name, script) outfile = "{0}{1}.{2}.out".format(OPTS.openram_temp, cell_name, script) resultsfile = "{0}{1}.{2}.report".format(OPTS.openram_temp, cell_name, script) - cmd = "{0}run_{1}.sh 2> {2} 1> {3}".format(OPTS.openram_temp, - script, - errfile, - outfile) - debug.info(2, cmd) - os.system(cmd) + scriptpath = '{0}run_{1}.sh'.format(OPTS.openram_temp, script) + + debug.info(2, "Starting {}".format(scriptpath)) + start = time.time() + with open(outfile, 'wb') as fo, open(errfile, 'wb') as fe: + p = subprocess.Popen( + [scriptpath], stdout=fo, stderr=fe, cwd=OPTS.openram_temp) + + if echo_cmd_output: + tailo = subprocess.Popen([ + 'tail', + '-f', # Follow the output + '--pid', str(p.pid), # Close when this pid exits + outfile, + ]) + taile = subprocess.Popen([ + 'tail', + '-f', # Follow the output + '--pid', str(p.pid), # Close when this pid exits + errfile, + ]) + + lastoutput = start + while p.poll() == None: + runningfor = time.time() - start + outputdelta = time.time() - lastoutput + if outputdelta > 30: + lastoutput = time.time() + debug.info(1, "Still running {} ({:.0f} seconds)".format(scriptpath, runningfor)) + time.sleep(1) + assert p.poll() != None, (p.poll(), p) + p.wait() + + # Kill the tail commands if they haven't finished. + if cmd_output: + if tailo.poll() != None: + tailo.kill() + tailo.wait() + if taile.poll() != None: + taile.kill() + taile.wait() + + debug.info(2, "Finished {} with {}".format(scriptpath, p.returncode)) + os.chdir(cwd) return (outfile, errfile, resultsfile) From e817b02adedf2bc542bcbff02c78c60f3bcd7cec Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 30 Nov 2020 09:38:42 -0800 Subject: [PATCH 23/40] Fix syntax error. Enable script echo on -v -v. --- compiler/verify/run_script.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/verify/run_script.py b/compiler/verify/run_script.py index 8fda376c..05a9afb9 100644 --- a/compiler/verify/run_script.py +++ b/compiler/verify/run_script.py @@ -19,8 +19,7 @@ from globals import OPTS def run_script(cell_name, script="lvs"): """ Run script and create output files. """ - # FIXME: Set this value based on options somewhere - echo_cmd_output = True + echo_cmd_output = OPTS.verbose_level > 1 cwd = os.getcwd() os.chdir(OPTS.openram_temp) @@ -62,7 +61,7 @@ def run_script(cell_name, script="lvs"): p.wait() # Kill the tail commands if they haven't finished. - if cmd_output: + if echo_cmd_output: if tailo.poll() != None: tailo.kill() tailo.wait() From fb4cf0d4d15af2132104a56fee25a883ada49585 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 1 Dec 2020 09:52:23 -0800 Subject: [PATCH 24/40] Remove env variable from run_lvs script --- compiler/verify/calibre.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index 5e49e6e1..a6376a16 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -127,8 +127,6 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, out run_file = output_path + "run_lvs.sh" f = open(run_file, "w") f.write("#!/bin/sh\n") - PDK_DIR=os.environ.get("PDK_DIR") - f.write("export PDK_DIR={}\n".format(PDK_DIR)) cmd = "{0} -gui -lvs {1}lvs_runset -batch".format(OPTS.lvs_exe[1], output_path) f.write(cmd) From b621c3bdc0414b595222d07ea6368e88319b7ad6 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 1 Dec 2020 11:18:27 -0800 Subject: [PATCH 25/40] Allow verbose output from scripts with one -v and not unit test --- compiler/verify/magic.py | 8 ++++---- compiler/verify/run_script.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 80385dc1..467e9893 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -95,10 +95,10 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa f.write('puts "Finished loading cell {}"\n'.format(cell_name)) # Flatten the cell to get rid of DRCs spanning multiple layers # (e.g. with routes) - #f.write("flatten {}_new\n".format(cell_name)) - #f.write("load {}_new\n".format(cell_name)) - #f.write("cellname rename {0}_new {0}\n".format(cell_name)) - #f.write("load {}\n".format(cell_name)) + # f.write("flatten {}_new\n".format(cell_name)) + # f.write("load {}_new\n".format(cell_name)) + # f.write("cellname rename {0}_new {0}\n".format(cell_name)) + # f.write("load {}\n".format(cell_name)) f.write("cellname delete \\(UNNAMED\\)\n") f.write("writeall force\n") f.write("select top cell\n") diff --git a/compiler/verify/run_script.py b/compiler/verify/run_script.py index 05a9afb9..fff0c875 100644 --- a/compiler/verify/run_script.py +++ b/compiler/verify/run_script.py @@ -19,7 +19,7 @@ from globals import OPTS def run_script(cell_name, script="lvs"): """ Run script and create output files. """ - echo_cmd_output = OPTS.verbose_level > 1 + echo_cmd_output = OPTS.verbose_level > 0 and not OPTS.is_unit_test cwd = os.getcwd() os.chdir(OPTS.openram_temp) From 3829213afebf9e9eda72ea6cc66fe5febc7c4897 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 1 Dec 2020 11:19:12 -0800 Subject: [PATCH 26/40] Use and2_dec instead of buf_dec for better wldriver layout --- compiler/modules/port_address.py | 20 +++++++++++++++++--- compiler/modules/replica_bitcell_array.py | 6 +++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 125f8765..ecab7b84 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -8,7 +8,7 @@ import debug import design from sram_factory import factory from vector import vector -from tech import layer +from tech import layer, drc from globals import OPTS from tech import layer_properties as layer_props @@ -86,6 +86,13 @@ class port_address(design.design): else: self.add_power_pin("vdd", rbl_vdd_pin.lc()) + # Also connect the B input of the RBL and_dec to vdd + if OPTS.local_array_size == 0: + rbl_b_pin = self.rbl_driver_inst.get_pin("B") + rbl_loc = rbl_b_pin.center() - vector(3 * self.m1_pitch, 0) + self.add_path(rbl_b_pin.layer, [rbl_b_pin.center(), rbl_loc]) + self.add_power_pin("vdd", rbl_loc) + def route_pins(self): for row in range(self.addr_size): decoder_name = "addr_{}".format(row) @@ -157,11 +164,13 @@ class port_address(design.design): b = factory.create(module_type=OPTS.bitcell) if local_array_size > 0: + # The local wordline driver will change the polarity self.rbl_driver = factory.create(module_type="inv_dec", size=driver_size, height=b.height) else: - self.rbl_driver = factory.create(module_type="buf_dec", + # There is no local wordline driver + self.rbl_driver = factory.create(module_type="and2_dec", size=driver_size, height=b.height) @@ -189,6 +198,8 @@ class port_address(design.design): temp = [] temp.append("wl_en") + if OPTS.local_array_size == 0: + temp.append("vdd") temp.append("rbl_wl") temp.append("vdd") temp.append("gnd") @@ -221,7 +232,10 @@ class port_address(design.design): wordline_driver_array_offset = vector(self.row_decoder_inst.rx(), 0) self.wordline_driver_array_inst.place(wordline_driver_array_offset) - x_offset = self.wordline_driver_array_inst.rx() - self.rbl_driver.width - self.m1_pitch + # The wordline driver also had an extra gap on the right, so use this offset + well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active") + x_offset = self.wordline_driver_array_inst.rx() - well_gap - self.rbl_driver.width + if self.port == 0: rbl_driver_offset = vector(x_offset, 0) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index a2494f1b..75d47670 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -121,18 +121,18 @@ class replica_bitcell_array(bitcell_base_array): # the array. # These go from the top (where the bitcell array starts ) down replica_bit = self.rbl[0] - port + column_offset = self.rbl[0] + elif port in self.right_rbl: # We will always have self.rbl[0] rows of replica wordlines below # the array. # These go from the bottom up replica_bit = self.rbl[0] + self.row_size + port + column_offset = self.rbl[0] + self.column_size + 1 else: continue - # If we have an odd numer on the bottom - column_offset = self.rbl[0] + 1 - self.replica_columns[port] = factory.create(module_type="replica_column", rows=self.row_size, rbl=self.rbl, From 62bf713913463d6136ff9961430c71aba7b65702 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 1 Dec 2020 11:19:37 -0800 Subject: [PATCH 27/40] Only remove files at end of openram --- compiler/base/hierarchy_design.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index b5d9b8de..e776ac3c 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -8,9 +8,7 @@ import hierarchy_layout import hierarchy_spice import debug -import os from globals import OPTS -import tech class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): @@ -72,10 +70,6 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): "LVS failed for {0} with {1} errors(s)".format(self.cell_name, self.lvs_errors)) - if not OPTS.keep_temp: - os.remove(tempspice) - os.remove(tempgds) - def DRC(self, final_verification=False): """Checks DRC for a module""" import verify @@ -96,9 +90,6 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): "DRC failed for {0} with {1} error(s)".format(self.cell_name, num_errors)) - if not OPTS.keep_temp: - os.remove(tempgds) - def LVS(self, final_verification=False): """Checks LVS for a module""" import verify @@ -118,9 +109,6 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): debug.check(num_errors == 0, "LVS failed for {0} with {1} error(s)".format(self.cell_name, num_errors)) - if not OPTS.keep_temp: - os.remove(tempspice) - os.remove(tempgds) def init_graph_params(self): """ From a5b5f7c22bf058538e769b6e0bed4aac7b1ef514 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 1 Dec 2020 11:33:55 -0800 Subject: [PATCH 28/40] Change layer away from wordlines --- compiler/modules/bank.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index e0668700..fbe60ff0 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -729,7 +729,6 @@ class bank(design.design): inst2_bl_name=inst2_bl_name, inst2_br_name=inst2_br_name) - # Connect the replica bitlines for (array_name, data_name) in zip(["rbl_bl_{0}_{0}".format(port), "rbl_br_{0}_{0}".format(port)], ["rbl_bl", "rbl_br"]): self.connect_bitline(inst1, inst2, array_name, data_name) @@ -876,15 +875,14 @@ class bank(design.design): mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * port_address_pos + 0.5 * bitcell_array_pos, 0) mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1) if driver_wl_pin.layer != bitcell_wl_pin.layer: - self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2]) + self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1]) self.add_via_stack_center(from_layer=driver_wl_pin.layer, to_layer=bitcell_wl_pin.layer, - offset=mid2) - self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos]) + offset=mid1) + self.add_path(bitcell_wl_pin.layer, [mid1, mid2, bitcell_wl_pos]) else: self.add_path(bitcell_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) - def route_port_address_right(self, port): """ Connecting Wordline driver output to Bitcell WL connection """ From a31e0dab028debbfd0902a35a422b42c69957edc Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 1 Dec 2020 13:27:32 -0800 Subject: [PATCH 29/40] Remove via-to-via path width hack --- compiler/modules/hierarchical_predecode.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 9b5cab65..72fb2a6d 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -268,9 +268,11 @@ class hierarchical_predecode(design.design): height=via.mod.second_layer_height, width=via.mod.second_layer_width) - if layer_props.hierarchical_predecode.vertical_supply: - below_rail = vector(self.decode_rails[out_pin].cx(), y_offset - (self.cell_height / 2)) - self.add_path(self.bus_layer, [rail_pos, below_rail], width=self.li_width + self.m1_enclose_mcon * 2) + # This is a hack to fix via-to-via spacing issues, but it is currently + # causing its own DRC problems. + # if layer_props.hierarchical_predecode.vertical_supply: + # below_rail = vector(self.decode_rails[out_pin].cx(), y_offset - (self.cell_height / 2)) + # self.add_path(self.bus_layer, [rail_pos, below_rail], width=self.li_width + self.m1_enclose_mcon * 2) def route_and_to_rails(self): # This 2D array defines the connection mapping From c3472b5bc5ff9aa3e31324a961c7ee9fe32e92f1 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 1 Dec 2020 13:27:50 -0800 Subject: [PATCH 30/40] Remove old commented code --- compiler/verify/magic.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 467e9893..36acaf5a 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -93,12 +93,6 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa f.write('puts "Finished reading gds {}"\n'.format(gds_name)) f.write("load {}\n".format(cell_name)) f.write('puts "Finished loading cell {}"\n'.format(cell_name)) - # Flatten the cell to get rid of DRCs spanning multiple layers - # (e.g. with routes) - # f.write("flatten {}_new\n".format(cell_name)) - # f.write("load {}_new\n".format(cell_name)) - # f.write("cellname rename {0}_new {0}\n".format(cell_name)) - # f.write("load {}\n".format(cell_name)) f.write("cellname delete \\(UNNAMED\\)\n") f.write("writeall force\n") f.write("select top cell\n") From b4cab6ec572b8634168f0613cda08b3f298c8d4f Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 1 Dec 2020 15:20:24 -0800 Subject: [PATCH 31/40] Change mult to 1 always. --- compiler/pgates/ptx.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index b697b9a3..05ec75c0 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -13,7 +13,6 @@ from sram_factory import factory import contact import logical_effort from globals import OPTS -from pgate import pgate from tech import cell_properties as cell_props @@ -151,14 +150,14 @@ class ptx(design.design): self.spice.append("\n* spice ptx " + self.spice_device) if cell_props.ptx.model_is_subckt and OPTS.lvs_exe and OPTS.lvs_exe[0] == "calibre": - # sky130 requires mult parameter too - # self.lvs_device = "X{{0}} {{1}} {0} m={1} w={2} l={3} mult={1}".format(spice[self.tx_type], + # sky130 requires mult parameter too. It is not the same as m, but I don't understand it. + # self.lvs_device = "X{{0}} {{1}} {0} m={1} w={2} l={3} mult=1".format(spice[self.tx_type], # self.mults, # self.tx_width, # drc("minwidth_poly")) # TEMP FIX: Use old device names if using Calibre. - self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult={1}".format("nshort" if self.tx_type == "nmos" else "pshort", + self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult=1".format("nshort" if self.tx_type == "nmos" else "pshort", self.mults, self.tx_width, drc("minwidth_poly")) From 583a70c24e72ccb6c1a36ec468b368229651adb6 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 1 Dec 2020 15:20:44 -0800 Subject: [PATCH 32/40] Fix select layer for column mux array --- compiler/modules/column_mux_array.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/compiler/modules/column_mux_array.py b/compiler/modules/column_mux_array.py index 55d67eee..43a24a20 100644 --- a/compiler/modules/column_mux_array.py +++ b/compiler/modules/column_mux_array.py @@ -34,18 +34,9 @@ class column_mux_array(design.design): self.column_offset = column_offset self.sel_layer = layer_props.column_mux_array.select_layer - self.sel_pitch = getattr(self, layer_props.column_mux_array.select_pitch) + self.sel_pitch = getattr(self, self.sel_layer + "_pitch") self.bitline_layer = layer_props.column_mux_array.bitline_layer - # if OPTS.tech_name == "sky130": - # self.sel_layer = "m3" - # self.sel_pitch = self.m3_pitch - # self.bitline_layer = "m1" - # else: - # self.sel_layer = "m1" - # self.sel_pitch = self.m2_pitch - # self.bitline_layer = "m2" - if preferred_directions[self.sel_layer] == "V": self.via_directions = ("H", "H") else: From f320017b86b5f4858bf978519ba19c7909fc86b9 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 1 Dec 2020 17:12:17 -0800 Subject: [PATCH 33/40] Decrease verbosity of script output --- compiler/verify/run_script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/verify/run_script.py b/compiler/verify/run_script.py index fff0c875..05a9afb9 100644 --- a/compiler/verify/run_script.py +++ b/compiler/verify/run_script.py @@ -19,7 +19,7 @@ from globals import OPTS def run_script(cell_name, script="lvs"): """ Run script and create output files. """ - echo_cmd_output = OPTS.verbose_level > 0 and not OPTS.is_unit_test + echo_cmd_output = OPTS.verbose_level > 1 cwd = os.getcwd() os.chdir(OPTS.openram_temp) From 705d8e31054c3433052da83186935018d398cf9f Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 1 Dec 2020 17:12:35 -0800 Subject: [PATCH 34/40] Fix wrong via starting layer --- compiler/modules/port_address.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index ecab7b84..382aca56 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -91,7 +91,7 @@ class port_address(design.design): rbl_b_pin = self.rbl_driver_inst.get_pin("B") rbl_loc = rbl_b_pin.center() - vector(3 * self.m1_pitch, 0) self.add_path(rbl_b_pin.layer, [rbl_b_pin.center(), rbl_loc]) - self.add_power_pin("vdd", rbl_loc) + self.add_power_pin("vdd", rbl_loc, start_layer=rbl_b_pin.layer) def route_pins(self): for row in range(self.addr_size): From 0250d9add7306c558b10c834e3ef1555cdaebb5a Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 1 Dec 2020 17:15:03 -0800 Subject: [PATCH 35/40] v1.1.7 --- compiler/globals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/globals.py b/compiler/globals.py index 8c6d7fd5..f5c42dc0 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -19,7 +19,7 @@ import re import copy import importlib -VERSION = "1.1.6" +VERSION = "1.1.7" NAME = "OpenRAM v{}".format(VERSION) USAGE = "openram.py [options] \nUse -h for help.\n" From edf3d9557d5b09668fb20f27356378bcf43cde95 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 2 Dec 2020 11:09:40 -0800 Subject: [PATCH 36/40] Purge temp at the start of every run if it exists. --- compiler/globals.py | 51 ++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/compiler/globals.py b/compiler/globals.py index f5c42dc0..1fce6acd 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -353,6 +353,21 @@ def end_openram(): verify.print_lvs_stats() verify.print_pex_stats() + +def purge_temp(): + """ Remove the temp directory. """ + debug.info(1, + "Purging temp directory: {}".format(OPTS.openram_temp)) + # This annoyingly means you have to re-cd into + # the directory each debug iteration + # shutil.rmtree(OPTS.openram_temp, ignore_errors=True) + contents = [os.path.join(OPTS.openram_temp, i) for i in os.listdir(OPTS.openram_temp)] + for i in contents: + if os.path.isfile(i) or os.path.islink(i): + os.remove(i) + else: + shutil.rmtree(i) + def cleanup_paths(): """ @@ -364,19 +379,9 @@ def cleanup_paths(): "Preserving temp directory: {}".format(OPTS.openram_temp)) return elif os.path.exists(OPTS.openram_temp): - debug.info(1, - "Purging temp directory: {}".format(OPTS.openram_temp)) - # This annoyingly means you have to re-cd into - # the directory each debug iteration - # shutil.rmtree(OPTS.openram_temp, ignore_errors=True) - contents = [os.path.join(OPTS.openram_temp, i) for i in os.listdir(OPTS.openram_temp)] - for i in contents: - if os.path.isfile(i) or os.path.islink(i): - os.remove(i) - else: - shutil.rmtree(i) - + purge_temp() + def setup_paths(): """ Set up the non-tech related paths. """ debug.info(2, "Setting up paths...") @@ -405,7 +410,7 @@ def setup_paths(): OPTS.openram_temp += "/" debug.info(1, "Temporary files saved in " + OPTS.openram_temp) - + def is_exe(fpath): """ Return true if the given is an executable file that exists. """ return os.path.exists(fpath) and os.access(fpath, os.X_OK) @@ -427,15 +432,17 @@ def find_exe(check_exe): def init_paths(): """ Create the temp and output directory if it doesn't exist """ - - # make the directory if it doesn't exist - try: - debug.info(1, - "Creating temp directory: {}".format(OPTS.openram_temp)) - os.makedirs(OPTS.openram_temp, 0o750) - except OSError as e: - if e.errno == 17: # errno.EEXIST - os.chmod(OPTS.openram_temp, 0o750) + if os.path.exists(OPTS.openram_temp): + purge_temp() + else: + # make the directory if it doesn't exist + try: + debug.info(1, + "Creating temp directory: {}".format(OPTS.openram_temp)) + os.makedirs(OPTS.openram_temp, 0o750) + except OSError as e: + if e.errno == 17: # errno.EEXIST + os.chmod(OPTS.openram_temp, 0o750) # Don't delete the output dir, it may have other files! # make the directory if it doesn't exist From 3c115f0ecb9c22b65f03a92edfa9f1a97724c7ce Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 2 Dec 2020 11:26:00 -0800 Subject: [PATCH 37/40] LVS using Netgen not Magic --- compiler/verify/magic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 36acaf5a..605303bd 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -218,7 +218,7 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, out f = open(run_file, "w") f.write("#!/bin/sh\n") f.write('export OPENRAM_TECH="{}"\n'.format(os.environ['OPENRAM_TECH'])) - f.write('echo "$(date): Starting LVS using Magic {}"\n'.format(OPTS.drc_exe[1])) + f.write('echo "$(date): Starting LVS using Netgen {}"\n'.format(OPTS.lvs_exe[1])) f.write("{} -noconsole << EOF\n".format(OPTS.lvs_exe[1])) # f.write("readnet spice {0}.spice\n".format(cell_name)) # f.write("readnet spice {0}\n".format(sp_name)) @@ -226,7 +226,7 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, out f.write("quit\n") f.write("EOF\n") f.write("magic_retcode=$?\n") - f.write('echo "$(date): Finished ($magic_retcode) LVS using Magic {}"\n'.format(OPTS.drc_exe[1])) + f.write('echo "$(date): Finished ($magic_retcode) LVS using Netgen {}"\n'.format(OPTS.lvs_exe[1])) f.write("exit $magic_retcode\n") f.close() os.system("chmod u+x {}".format(run_file)) From 4f28351dcd562d01f5b9cf71c88e4d8e4f50fd61 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 2 Dec 2020 11:52:38 -0800 Subject: [PATCH 38/40] Add printGDS script to aid debugging things. --- compiler/printGDS.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100755 compiler/printGDS.py diff --git a/compiler/printGDS.py b/compiler/printGDS.py new file mode 100755 index 00000000..b0683203 --- /dev/null +++ b/compiler/printGDS.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import sys +from gdsMill import gdsMill + +if len(sys.argv) < 2: + print("Usage: {0} file.gds".format(sys.argv[0])) + sys.exit(1) + +gds_file = sys.argv[1] +arrayCellLayout = gdsMill.VlsiLayout() +reader = gdsMill.Gds2reader(arrayCellLayout,debugToTerminal = 1) +reader.loadFromFile(gds_file) + From 28354bffe0ac1d1092c9b92983fc8e81b30426bd Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 2 Dec 2020 12:03:10 -0800 Subject: [PATCH 39/40] Add offset to output when printing verbose GDS --- compiler/gdsMill/gdsMill/gds2reader.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/gdsMill/gdsMill/gds2reader.py b/compiler/gdsMill/gdsMill/gds2reader.py index 2f9976a5..448355a8 100644 --- a/compiler/gdsMill/gdsMill/gds2reader.py +++ b/compiler/gdsMill/gdsMill/gds2reader.py @@ -79,7 +79,8 @@ class Gds2reader: recordLength = struct.unpack(">h",recordLengthAscii) #gives us a tuple with a short int inside offset_int = int(recordLength[0]) # extract length offset += offset_int # count offset - #print(offset) #print out the record numbers for de-bugging + if(self.debugToTerminal==1): + print("Offset: " + str(offset)) #print out the record numbers for de-bugging record = self.fileHandle.read(recordLength[0]-2) #read the rest of it (first 2 bytes were already read) return record @@ -176,7 +177,7 @@ class Gds2reader: def readBoundary(self): ##reads in a boundary type structure = a filled polygon if(self.debugToTerminal==1): - print("\t\t\tBeginBoundary") + print("\t\tBeginBoundary") thisBoundary=GdsBoundary() while 1: record = self.readNextRecord() @@ -214,13 +215,13 @@ class Gds2reader: print("\t\t\tXY Point: "+str(x)+","+str(y)) elif(idBits==b'\x11\x00'): #End Of Element if(self.debugToTerminal==1): - print("\t\t\tEndBoundary") + print("\t\tEndBoundary") break; return thisBoundary def readPath(self): #reads in a path structure if(self.debugToTerminal==1): - print("\t\t\tBeginPath") + print("\t\tBeginPath") thisPath=GdsPath() while 1: @@ -274,7 +275,7 @@ class Gds2reader: print("\t\t\tXY Point: "+str(x)+","+str(y)) elif(idBits==b'\x11\x00'): #End Of Element if(self.debugToTerminal==1): - print("\t\t\tEndPath") + print("\t\tEndPath") break; return thisPath From bad1274bdbe1fcc1fb7feb163f25408d073cb21b Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 3 Dec 2020 10:03:47 -0800 Subject: [PATCH 40/40] Use internal name for col/row caps. gds ordered read enabled. --- compiler/modules/col_cap_array.py | 2 +- compiler/modules/row_cap_array.py | 2 +- compiler/verify/calibre.py | 10 ++++------ compiler/verify/magic.py | 1 + 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index 1b7a1f49..1be29327 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -100,7 +100,7 @@ class col_cap_array(bitcell_base_array): inst = self.cell_inst[row, col] for pin_name in ["vdd", "gnd"]: for pin in inst.get_pins(pin_name): - self.add_power_pin(name=pin.name, + self.add_power_pin(name=pin_name, loc=pin.center(), start_layer=pin.layer) diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index 5cee866a..850dd5f9 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -113,7 +113,7 @@ class row_cap_array(bitcell_base_array): inst = self.cell_inst[row, col] for pin_name in ["vdd", "gnd"]: for pin in inst.get_pins(pin_name): - self.add_power_pin(name=pin.name, + self.add_power_pin(name=pin_name, loc=pin.center(), start_layer=pin.layer) diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index a6376a16..9d079c2a 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -103,10 +103,10 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, out 'cmnTranscriptEchoToFile': 1, 'lvsRecognizeGates': 'NONE', } - # FIXME: Remove when vdd/gnd connected - #'cmnVConnectNamesState' : 'ALL', #connects all nets with the same namee - # FIXME: Remove when vdd/gnd connected - #'lvsAbortOnSupplyError' : 0 + # FIXME: Remove when vdd/gnd connected + # 'cmnVConnectNamesState' : 'ALL', #connects all nets with the same namee + # FIXME: Remove when vdd/gnd connected + # 'lvsAbortOnSupplyError' : 0 if not final_verification or not OPTS.route_supplies: lvs_runset['cmnVConnectReport']=1 @@ -115,8 +115,6 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, out else: lvs_runset['lvsAbortOnSupplyError']=1 - - # write the runset file f = open(output_path + "lvs_runset", "w") for k in sorted(iter(lvs_runset.keys())): diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 605303bd..07a2efab 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -89,6 +89,7 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa f.write("gds warning default\n") f.write("gds flatten true\n") f.write("gds readonly true\n") + f.write("gds ordering true\n") f.write("gds read {}\n".format(gds_name)) f.write('puts "Finished reading gds {}"\n'.format(gds_name)) f.write("load {}\n".format(cell_name))