diff --git a/compiler/base/graph_util.py b/compiler/base/graph_util.py index 7cf8ee6f..5d1ee692 100644 --- a/compiler/base/graph_util.py +++ b/compiler/base/graph_util.py @@ -18,13 +18,16 @@ class timing_graph(): def __init__(self): self.graph = defaultdict(set) self.all_paths = [] + self.edge_mods = {} - def add_edge(self, src_node, dest_node): - """Adds edge to graph. Nodes added as well if they do not exist.""" + def add_edge(self, src_node, dest_node, edge_mod): + """Adds edge to graph. Nodes added as well if they do not exist. + Module which defines the edge must be provided for timing information.""" src_node = src_node.lower() dest_node = dest_node.lower() self.graph[src_node].add(dest_node) + self.edge_mods[(src_node, dest_node)] = edge_mod def add_node(self, node): """Add node to graph with no edges""" @@ -34,8 +37,8 @@ class timing_graph(): self.graph[node] = set() def remove_edges(self, node): - """Helper function to remove edges, useful for removing vdd/gnd""" + node = node.lower() self.graph[node] = set() @@ -93,7 +96,33 @@ class timing_graph(): # Remove current vertex from path[] and mark it as unvisited path.pop() - visited.remove(cur_node) + visited.remove(cur_node) + + def get_timing(self, path, corner, slew, load): + """Returns the analytical delays in the input path""" + + if len(path) == 0: + return [] + + delays = [] + cur_slew = slew + for i in range(len(path)-1): + + path_edge_mod = self.edge_mods[(path[i], path[i+1])] + + # On the output of the current stage, get COUT from all other mods connected + cout = 0 + for node in self.graph[path[i+1]]: + output_edge_mod = self.edge_mods[(path[i+1], node)] + cout+=output_edge_mod.get_cin() + # If at the last output, include the final output load + if i == len(path)-2: + cout+=load + + delays.append(path_edge_mod.analytical_delay(corner, slew, cout)) + cur_slew = delays[-1].slew + + return delays def __str__(self): """ override print function output """ diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 05b2182f..055bf412 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -110,7 +110,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): """Initializes parameters relevant to the graph creation""" #Only initializes a set for checking instances which should not be added self.graph_inst_exclude = set() - + def build_graph(self, graph, inst_name, port_nets): """Recursively create graph from instances in module.""" @@ -219,7 +219,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): for inp in input_pins+inout_pins: for out in output_pins+inout_pins: if inp != out: #do not add self loops - graph.add_edge(pin_dict[inp], pin_dict[out]) + graph.add_edge(pin_dict[inp], pin_dict[out], self) def __str__(self): """ override print function output """ diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 2dcc3d73..5166ac6b 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -13,7 +13,7 @@ import tech from delay_data import * from wire_spice_model import * from power_data import * - +import logical_effort class spice(): """ @@ -304,14 +304,48 @@ class spice(): def analytical_delay(self, corner, slew, load=0.0): """Inform users undefined delay module while building new modules""" - debug.warning("Design Class {0} delay function needs to be defined" + + # FIXME: Slew is not used in the model right now. Can be added heuristically as linear factor + relative_cap = logical_effort.convert_farad_to_relative_c(load) + stage_effort = self.get_stage_effort(relative_cap) + + # If it fails, then keep running with a valid object. + if stage_effort == None: + return delay_data(0.0, 0.0) + + abs_delay = stage_effort.get_absolute_delay() + corner_delay = self.apply_corners_analytically(abs_delay, corner) + SLEW_APPROXIMATION = 0.1 + corner_slew = SLEW_APPROXIMATION*corner_delay + return delay_data(corner_delay, corner_slew) + + def get_stage_effort(self, corner, slew, load=0.0): + """Inform users undefined delay module while building new modules""" + debug.warning("Design Class {0} logical effort function needs to be defined" .format(self.__class__.__name__)) debug.warning("Class {0} name {1}" .format(self.__class__.__name__, self.name)) - # return 0 to keep code running while building - return delay_data(0.0, 0.0) - + return None + + def get_cin(self): + """Returns input load in Femto-Farads. All values generated using + relative capacitance function then converted based on tech file parameter.""" + + # Override this function within a module if a more accurate input capacitance is needed. + # Input/outputs with differing capacitances is not implemented. + relative_cap = self.input_load() + return logical_effort.convert_relative_c_to_farad(relative_cap) + + def input_load(self): + """Inform users undefined relative capacitance functions used for analytical delays.""" + debug.warning("Design Class {0} input capacitance function needs to be defined" + .format(self.__class__.__name__)) + debug.warning("Class {0} name {1}" + .format(self.__class__.__name__, + self.name)) + return 0 + def cal_delay_with_rc(self, corner, r, c ,slew, swing = 0.5): """ Calculate the delay of a mosfet by diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index 96432cea..cad069a5 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -36,7 +36,7 @@ class bitcell(design.design): self.add_pin_types(self.type_list) self.nets_match = self.do_nets_exist(self.storage_nets) - def analytical_delay(self, corner, slew, load=0, swing = 0.5): + def get_stage_effort(self, load): parasitic_delay = 1 size = 0.5 #This accounts for bitline being drained thought the access TX and internal node cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. @@ -94,10 +94,10 @@ class bitcell(design.design): debug.info(1,"Storage nodes={} not found in spice file.".format(self.storage_nets)) return None - def get_wl_cin(self): + def input_load(self): """Return the relative capacitance of the access transistor gates""" - #This is a handmade cell so the value must be entered in the tech.py file or estimated. - #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. + + # FIXME: This applies to bitline capacitances as well. access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] return 2*access_tx_cin diff --git a/compiler/bitcells/bitcell_1rw_1r.py b/compiler/bitcells/bitcell_1rw_1r.py index f627a4ac..0d536b38 100644 --- a/compiler/bitcells/bitcell_1rw_1r.py +++ b/compiler/bitcells/bitcell_1rw_1r.py @@ -36,7 +36,7 @@ class bitcell_1rw_1r(design.design): self.add_pin_types(self.type_list) self.nets_match = self.do_nets_exist(self.storage_nets) - def analytical_delay(self, corner, slew, load=0, swing = 0.5): + def get_stage_effort(self, load): parasitic_delay = 1 size = 0.5 #This accounts for bitline being drained thought the access TX and internal node cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. @@ -118,11 +118,11 @@ class bitcell_1rw_1r(design.design): total_power = self.return_power(dynamic, leakage) return total_power - def get_wl_cin(self): + def input_load(self): """Return the relative capacitance of the access transistor gates""" - #This is a handmade cell so the value must be entered in the tech.py file or estimated. - #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. - #FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. + + # FIXME: This applies to bitline capacitances as well. + # FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] return 2*access_tx_cin @@ -141,8 +141,8 @@ class bitcell_1rw_1r(design.design): pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} #Edges hardcoded here. Essentially wl->bl/br for both ports. # Port 0 edges - graph.add_edge(pin_dict["wl0"], pin_dict["bl0"]) - graph.add_edge(pin_dict["wl0"], pin_dict["br0"]) + graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self) + graph.add_edge(pin_dict["wl0"], pin_dict["br0"], self) # Port 1 edges - graph.add_edge(pin_dict["wl1"], pin_dict["bl1"]) - graph.add_edge(pin_dict["wl1"], pin_dict["br1"]) + graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) + graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) diff --git a/compiler/bitcells/bitcell_1w_1r.py b/compiler/bitcells/bitcell_1w_1r.py index 6063cf86..7e8c9e75 100644 --- a/compiler/bitcells/bitcell_1w_1r.py +++ b/compiler/bitcells/bitcell_1w_1r.py @@ -36,7 +36,7 @@ class bitcell_1w_1r(design.design): self.add_pin_types(self.type_list) self.nets_match = self.do_nets_exist(self.storage_nets) - def analytical_delay(self, corner, slew, load=0, swing = 0.5): + def get_stage_effort(self, load): parasitic_delay = 1 size = 0.5 #This accounts for bitline being drained thought the access TX and internal node cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. @@ -116,11 +116,11 @@ class bitcell_1w_1r(design.design): total_power = self.return_power(dynamic, leakage) return total_power - def get_wl_cin(self): + def input_load(self): """Return the relative capacitance of the access transistor gates""" - #This is a handmade cell so the value must be entered in the tech.py file or estimated. - #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. - #FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. + + # FIXME: This applies to bitline capacitances as well. + # FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] return 2*access_tx_cin @@ -139,6 +139,6 @@ class bitcell_1w_1r(design.design): pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} #Edges hardcoded here. Essentially wl->bl/br for both ports. # Port 0 edges - graph.add_edge(pin_dict["wl1"], pin_dict["bl1"]) - graph.add_edge(pin_dict["wl1"], pin_dict["br1"]) + graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) + graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) # Port 1 is a write port, so its timing is not considered here. diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 3c59a9e3..919a0316 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -934,7 +934,7 @@ class pbitcell(design.design): return "wl{}".format(port) - def analytical_delay(self, corner, slew, load=0, swing = 0.5): + def get_stage_effort(self, load): parasitic_delay = 1 size = 0.5 #This accounts for bitline being drained thought the access TX and internal node cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. @@ -954,9 +954,11 @@ class pbitcell(design.design): total_power = self.return_power(dynamic, leakage) return total_power - def get_wl_cin(self): + def input_load(self): """Return the relative capacitance of the access transistor gates""" - #pbitcell uses the different sizing for the port access tx's. Not accounted for in this model. + + # FIXME: This applies to bitline capacitances as well. + # pbitcell uses the different sizing for the port access tx's. Not accounted for in this model. access_tx_cin = self.readwrite_nmos.get_cin() return 2*access_tx_cin @@ -973,6 +975,6 @@ class pbitcell(design.design): for pin_zip in [rw_pin_names, r_pin_names]: for wl,bl,br in pin_zip: - graph.add_edge(pin_dict[wl],pin_dict[bl]) - graph.add_edge(pin_dict[wl],pin_dict[br]) + graph.add_edge(pin_dict[wl],pin_dict[bl], self) + graph.add_edge(pin_dict[wl],pin_dict[br], self) diff --git a/compiler/bitcells/replica_bitcell.py b/compiler/bitcells/replica_bitcell.py index f8ded969..2f804bf0 100644 --- a/compiler/bitcells/replica_bitcell.py +++ b/compiler/bitcells/replica_bitcell.py @@ -31,6 +31,20 @@ class replica_bitcell(design.design): self.height = replica_bitcell.height self.pin_map = replica_bitcell.pin_map self.add_pin_types(self.type_list) + + def get_stage_effort(self, load): + parasitic_delay = 1 + size = 0.5 #This accounts for bitline being drained thought the access TX and internal node + cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. + read_port_load = 0.5 #min size NMOS gate load + return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) + + def input_load(self): + """Return the relative capacitance of the access transistor gates""" + + # FIXME: This applies to bitline capacitances as well. + access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] + return 2*access_tx_cin def analytical_power(self, corner, load): """Bitcell power in nW. Only characterizes leakage.""" @@ -40,13 +54,6 @@ class replica_bitcell(design.design): total_power = self.return_power(dynamic, leakage) return total_power - def get_wl_cin(self): - """Return the relative capacitance of the access transistor gates""" - #This is a handmade cell so the value must be entered in the tech.py file or estimated. - #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. - access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] - return 2*access_tx_cin - 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) \ No newline at end of file diff --git a/compiler/bitcells/replica_bitcell_1rw_1r.py b/compiler/bitcells/replica_bitcell_1rw_1r.py index e5c33317..0f56319e 100644 --- a/compiler/bitcells/replica_bitcell_1rw_1r.py +++ b/compiler/bitcells/replica_bitcell_1rw_1r.py @@ -32,11 +32,18 @@ class replica_bitcell_1rw_1r(design.design): self.pin_map = replica_bitcell_1rw_1r.pin_map self.add_pin_types(self.type_list) - def get_wl_cin(self): + def get_stage_effort(self, load): + parasitic_delay = 1 + size = 0.5 #This accounts for bitline being drained thought the access TX and internal node + cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. + read_port_load = 0.5 #min size NMOS gate load + return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) + + def input_load(self): """Return the relative capacitance of the access transistor gates""" - #This is a handmade cell so the value must be entered in the tech.py file or estimated. - #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. - #FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. + + # FIXME: This applies to bitline capacitances as well. + # FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] return 2*access_tx_cin @@ -46,8 +53,8 @@ class replica_bitcell_1rw_1r(design.design): pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} #Edges hardcoded here. Essentially wl->bl/br for both ports. # Port 0 edges - graph.add_edge(pin_dict["wl0"], pin_dict["bl0"]) - graph.add_edge(pin_dict["wl0"], pin_dict["br0"]) + graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self) + graph.add_edge(pin_dict["wl0"], pin_dict["br0"], self) # Port 1 edges - graph.add_edge(pin_dict["wl1"], pin_dict["bl1"]) - graph.add_edge(pin_dict["wl1"], pin_dict["br1"]) \ No newline at end of file + graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) + graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) \ No newline at end of file diff --git a/compiler/bitcells/replica_bitcell_1w_1r.py b/compiler/bitcells/replica_bitcell_1w_1r.py index 79171bf5..b903e0ad 100644 --- a/compiler/bitcells/replica_bitcell_1w_1r.py +++ b/compiler/bitcells/replica_bitcell_1w_1r.py @@ -32,11 +32,18 @@ class replica_bitcell_1w_1r(design.design): self.pin_map = replica_bitcell_1w_1r.pin_map self.add_pin_types(self.type_list) - def get_wl_cin(self): + def get_stage_effort(self, load): + parasitic_delay = 1 + size = 0.5 #This accounts for bitline being drained thought the access TX and internal node + cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. + read_port_load = 0.5 #min size NMOS gate load + return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) + + def input_load(self): """Return the relative capacitance of the access transistor gates""" - #This is a handmade cell so the value must be entered in the tech.py file or estimated. - #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. - #FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. + + # FIXME: This applies to bitline capacitances as well. + # FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] return 2*access_tx_cin @@ -47,6 +54,6 @@ class replica_bitcell_1w_1r(design.design): pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} #Edges hardcoded here. Essentially wl->bl/br for the read port. # Port 1 edges - graph.add_edge(pin_dict["wl1"], pin_dict["bl1"]) - graph.add_edge(pin_dict["wl1"], pin_dict["br1"]) + graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) + graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) # Port 0 is a write port, so its timing is not considered here. \ No newline at end of file diff --git a/compiler/bitcells/replica_pbitcell.py b/compiler/bitcells/replica_pbitcell.py index 8453411f..4fcfb4c5 100644 --- a/compiler/bitcells/replica_pbitcell.py +++ b/compiler/bitcells/replica_pbitcell.py @@ -84,8 +84,4 @@ class replica_pbitcell(design.design): self.copy_layout_pin(self.prbc_inst, "wl{}".format(port)) self.copy_layout_pin(self.prbc_inst, "vdd") self.copy_layout_pin(self.prbc_inst, "gnd") - - def get_wl_cin(self): - """Return the relative capacitance of the access transistor gates""" - #This module is made using a pbitcell. Get the cin from that module - return self.prbc.get_wl_cin() + \ No newline at end of file diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index bb48564e..65bc0b78 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -247,7 +247,7 @@ class delay(simulation): def create_graph(self): """Creates timing graph to generate the timing paths for the SRAM output.""" - self.sram.bank.bitcell_array.init_graph_params() # Removes previous bit exclusions + self.sram.bank.bitcell_array.bitcell_array.init_graph_params() # Removes previous bit exclusions self.sram.bank.bitcell_array.graph_exclude_bits(self.wordline_row, self.bitline_column) # Generate new graph every analysis as edges might change depending on test bit @@ -800,6 +800,8 @@ class delay(simulation): debug.info(2,"{}={}".format(meas.name,val)) + dout_success = True + bl_success = False for meas in self.dout_volt_meas: val = meas.retrieve_measure(port=port) debug.info(2,"{}={}".format(meas.name, val)) @@ -813,15 +815,7 @@ class delay(simulation): dout_success = False debug.info(1, "Debug measurement failed. Value {}V was read on read 0 cycle.".format(val)) bl_success = self.check_bitline_meas(br_vals[sram_op.READ_ONE], bl_vals[sram_op.READ_ONE]) - elif meas.meta_str == sram_op.READ_ONE and val > self.vdd_voltage*0.9: - dout_success = True - bl_success = True - elif meas.meta_str == sram_op.READ_ZERO and val < self.vdd_voltage*0.1: - dout_success = True - bl_success = True - else: - dout_success = False - bl_success = False + # If the bitlines have a correct value while the output does not then that is a # sen error. FIXME: there are other checks that can be done to solidfy this conclusion. if not dout_success and bl_success: @@ -1275,39 +1269,71 @@ class delay(simulation): # Add test cycle of read/write port pair. One port could have been used already, but the other has not. self.gen_test_cycles_one_port(cur_read_port, cur_write_port) + def sum_delays(self, delays): + """Adds the delays (delay_data objects) so the correct slew is maintained""" + + delay = delays[0] + for i in range(1, len(delays)): + delay+=delays[i] + return delay + def analytical_delay(self, slews, loads): """ Return the analytical model results for the SRAM. """ if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0: debug.warning("Analytical characterization results are not supported for multiport.") + + # Probe set to 0th bit, does not matter for analytical delay. + self.set_probe('0', 0) + self.create_graph() + self.set_internal_spice_names() self.create_measurement_names() - power = self.analytical_power(slews, loads) + + port = self.read_ports[0] + self.graph.get_all_paths('{}{}'.format(tech.spice["clk"], port), + '{}{}_{}'.format(self.dout_name, port, self.probe_data)) + + # Select the path with the bitline (bl) + bl_name,br_name = self.get_bl_name(self.graph.all_paths, port) + bl_path = [path for path in self.graph.all_paths if bl_name in path][0] + + # Set delay/power for slews and loads port_data = self.get_empty_measure_data_dict() - relative_loads = [logical_effort.convert_farad_to_relative_c(c_farad) for c_farad in loads] + power = self.analytical_power(slews, loads) + debug.info(1,'Slew, Load, Delay(ns), Slew(ns)') + max_delay = 0.0 for slew in slews: - for load in relative_loads: - self.set_load_slew(load,slew) - bank_delay = self.sram.analytical_delay(self.corner, self.slew,self.load) + for load in loads: + # Calculate delay based on slew and load + path_delays = self.graph.get_timing(bl_path, self.corner, slew, load) + + total_delay = self.sum_delays(path_delays) + max_delay = max(max_delay, total_delay.delay) + debug.info(1,'{}, {}, {}, {}'.format(slew,load,total_delay.delay/1e3, total_delay.slew/1e3)) + + # Delay is only calculated on a single port and replicated for now. for port in self.all_ports: for mname in self.delay_meas_names+self.power_meas_names: if "power" in mname: port_data[port][mname].append(power.dynamic) - elif "delay" in mname: - port_data[port][mname].append(bank_delay[port].delay/1e3) - elif "slew" in mname: - port_data[port][mname].append(bank_delay[port].slew/1e3) + elif "delay" in mname and port in self.read_ports: + port_data[port][mname].append(total_delay.delay/1e3) + elif "slew" in mname and port in self.read_ports: + port_data[port][mname].append(total_delay.slew/1e3) else: debug.error("Measurement name not recognized: {}".format(mname),1) + + # Estimate the period as double the delay with margin period_margin = 0.1 - risefall_delay = bank_delay[self.read_ports[0]].delay/1e3 - sram_data = { "min_period":risefall_delay*2*period_margin, + sram_data = { "min_period":(max_delay/1e3)*2*period_margin, "leakage_power": power.leakage} + debug.info(2,"SRAM Data:\n{}".format(sram_data)) debug.info(2,"Port Data:\n{}".format(port_data)) - return (sram_data,port_data) - + return (sram_data,port_data) + def analytical_power(self, slews, loads): """Get the dynamic and leakage power from the SRAM""" diff --git a/compiler/characterizer/logical_effort.py b/compiler/characterizer/logical_effort.py index 024cd4f1..42ac8861 100644 --- a/compiler/characterizer/logical_effort.py +++ b/compiler/characterizer/logical_effort.py @@ -23,23 +23,23 @@ class logical_effort(): self.cin = cin self.cout = cout self.logical_effort = (self.cin/size)/logical_effort.min_inv_cin - self.eletrical_effort = self.cout/self.cin + self.electrical_effort = self.cout/self.cin self.parasitic_scale = parasitic self.is_rise = out_is_rise def __str__(self): return "Name={}, g={}, h={}, p={}*pinv, rise_delay={}".format(self.name, self.logical_effort, - self.eletrical_effort, + self.electrical_effort, self.parasitic_scale, self.is_rise ) def get_stage_effort(self): - return self.logical_effort*self.eletrical_effort + return self.logical_effort*self.electrical_effort def get_parasitic_delay(self): - return logical_effort.pinv * self.parasitic_scale + return logical_effort.pinv*self.parasitic_scale def get_stage_delay(self): return self.get_stage_effort()+self.get_parasitic_delay() @@ -78,4 +78,7 @@ def calculate_relative_rise_fall_delays(stage_effort_list): def convert_farad_to_relative_c(c_farad): """Converts capacitance in Femto-Farads to relative capacitance.""" return c_farad*parameter['cap_relative_per_ff'] - \ No newline at end of file + +def convert_relative_c_to_farad(c_relative): + """Converts capacitance in logical effort relative units to Femto-Farads.""" + return c_relative/parameter['cap_relative_per_ff'] \ No newline at end of file diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 4fa95c44..a5ab4909 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -82,7 +82,7 @@ class bank(design.design): for port in self.read_ports: for bit in range(self.word_size): self.add_pin("dout{0}_{1}".format(port,bit),"OUTPUT") - for port in self.read_ports: + for port in self.all_ports: self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]),"OUTPUT") for port in self.write_ports: for bit in range(self.word_size): @@ -128,14 +128,13 @@ class bank(design.design): def route_rbl(self,port): """ Route the rbl_bl and rbl_wl """ - if self.port_data[port].has_rbl(): - bl_pin_name = self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) - bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name) - self.add_layout_pin(text="rbl_bl{0}".format(port), - layer=bl_pin.layer, - offset=bl_pin.ll(), - height=bl_pin.height(), - width=bl_pin.width()) + bl_pin_name = self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) + bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name) + self.add_layout_pin(text="rbl_bl{0}".format(port), + layer=bl_pin.layer, + offset=bl_pin.ll(), + height=bl_pin.height(), + width=bl_pin.width()) @@ -202,10 +201,7 @@ class bank(design.design): # LOWER RIGHT QUADRANT # Below the bitcell array - if self.port_data[port].has_rbl(): - self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width,0) - else: - self.port_data_offsets[port] = vector(self.main_bitcell_array_left,0) + self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width,0) # UPPER LEFT QUADRANT # To the left of the bitcell array @@ -364,36 +360,15 @@ class bank(design.design): self.add_mod(self.port_address) - # The number of replica lines depends on the port configuration - rbl_counts = [self.read_ports.count(p) for p in self.all_ports] - self.num_rbl = sum(rbl_counts) - - # The replica array indices always start at 0, so this will map them to - # the correct SRAM port - # (e.g. if port 0 is w, then port 1 will use RBL 0 in replica bitcell array - # because write ports don't use an RBL) - self.port_rbl_map = {} - index = 0 - for (i,num) in enumerate(rbl_counts): - if num>0: - self.port_rbl_map[i]=index - index += 1 - if len(rbl_counts)<2: - rbl_counts.append(0) - - - # Which bitcell port should be used in the RBL - # For now (since only 2 ports), if port 0 is not a read port, skip it in the RBLs - bitcell_ports=list(range(len(self.read_ports))) - if 0 not in self.read_ports: - bitcell_ports = [x+1 for x in bitcell_ports] + self.port_rbl_map = self.all_ports + self.num_rbl = len(self.all_ports) self.bitcell_array = factory.create(module_type="replica_bitcell_array", cols=self.num_cols, rows=self.num_rows, - left_rbl=rbl_counts[0], - right_rbl=rbl_counts[1], - bitcell_ports=bitcell_ports) + left_rbl=1, + right_rbl=1 if len(self.all_ports)>1 else 0, + bitcell_ports=self.all_ports) self.add_mod(self.bitcell_array) @@ -421,8 +396,7 @@ class bank(design.design): for wordline in self.wl_names: temp.append("{0}_{1}".format(wordline,row)) for port in self.all_ports: - if self.port_data[port].has_rbl(): - temp.append("wl_en{0}".format(port)) + temp.append("wl_en{0}".format(port)) temp.append("vdd") temp.append("gnd") self.connect_inst(temp) @@ -442,11 +416,10 @@ class bank(design.design): mod=self.port_data[port]) temp = [] - if self.port_data[port].has_rbl(): - rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) - rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port]) - temp.append(rbl_bl_name) - temp.append(rbl_br_name) + rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) + rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port]) + temp.append(rbl_bl_name) + temp.append(rbl_br_name) for col in range(self.num_cols): temp.append("{0}_{1}".format(self.bl_names[port],col)) temp.append("{0}_{1}".format(self.br_names[port],col)) @@ -710,11 +683,10 @@ class bank(design.design): inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) # Connect the replica bitlines - if self.port_data[port].has_rbl(): - rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) - rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port]) - self.connect_bitline(inst1, inst2, rbl_bl_name, "rbl_bl") - self.connect_bitline(inst1, inst2, rbl_br_name, "rbl_br") + rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) + rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port]) + self.connect_bitline(inst1, inst2, rbl_bl_name, "rbl_bl") + self.connect_bitline(inst1, inst2, rbl_br_name, "rbl_br") @@ -943,9 +915,8 @@ class bank(design.design): connection = [] connection.append((self.prefix+"p_en_bar{}".format(port), self.port_data_inst[port].get_pin("p_en_bar").lc())) - if port in self.read_ports: - rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port]) - connection.append((self.prefix+"wl_en{}".format(port), self.bitcell_array_inst.get_pin(rbl_wl_name).lc())) + rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port]) + connection.append((self.prefix+"wl_en{}".format(port), self.bitcell_array_inst.get_pin(rbl_wl_name).lc())) if port in self.write_ports: connection.append((self.prefix+"w_en{}".format(port), self.port_data_inst[port].get_pin("w_en").lc())) @@ -974,40 +945,7 @@ class bank(design.design): self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=control_pos) - - def analytical_delay(self, corner, slew, load, port): - """ return analytical delay of the bank. This will track the clock to output path""" - #FIXME: This delay is determined in the control logic. Should be moved here. - # word_driver_delay = self.wordline_driver.analytical_delay(corner, - # slew, - # self.bitcell_array.input_load()) - - #FIXME: Array delay is the same for every port. - word_driver_slew = 0 - if self.words_per_row > 1: - bitline_ext_load = self.port_data[port].column_mux_array.get_drain_cin() - else: - bitline_ext_load = self.port_data[port].sense_amp_array.get_drain_cin() - - bitcell_array_delay = self.bitcell_array.analytical_delay(corner, word_driver_slew, bitline_ext_load) - - bitcell_array_slew = 0 - #This also essentially creates the same delay for each port. Good structure, no substance - if self.words_per_row > 1: - sa_load = self.port_data[port].sense_amp_array.get_drain_cin() - column_mux_delay = self.port_data[port].column_mux_array.analytical_delay(corner, - bitcell_array_slew, - sa_load) - else: - column_mux_delay = [] - - column_mux_slew = 0 - sense_amp_delay = self.port_data[port].sense_amp_array.analytical_delay(corner, - column_mux_slew, - load) - # output load of bitcell_array is set to be only small part of bl for sense amp. - return bitcell_array_delay + column_mux_delay + sense_amp_delay - + def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): """Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline""" #Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 999382ec..bf4932a3 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -148,17 +148,7 @@ class bitcell_array(design.design): for pin_name in ["vdd", "gnd"]: for pin in inst.get_pins(pin_name): self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer) - - def analytical_delay(self, corner, slew, load): - """Returns relative delay of the bitline in the bitcell array""" - from tech import parameter - #The load being driven/drained is mostly the bitline but could include the sense amp or the column mux. - #The load from the bitlines is due to the drain capacitances from all the other bitlines and wire parasitics. - drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) - wire_unit_load = .05 * drain_load #Wires add 5% to this. - bitline_load = (drain_load+wire_unit_load)*self.row_size - return [self.cell.analytical_delay(corner, slew, load+bitline_load)] - + def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" from tech import drc, parameter @@ -197,15 +187,6 @@ class bitcell_array(design.design): bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell return bl_wire - def output_load(self, bl_pos=0): - bl_wire = self.gen_bl_wire() - return bl_wire.wire_c # sense amp only need to charge small portion of the bl - # set as one segment for now - - def input_load(self): - wl_wire = self.gen_wl_wire() - return wl_wire.return_input_cap() - def get_wordline_cin(self): """Get the relative input capacitance from the wordline connections in all the bitcell""" #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index a5c5dc12..06883125 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -125,33 +125,35 @@ class control_logic(design.design): self.add_mod(self.wl_en_driver) # w_en drives every write driver - self.wen_and2 = factory.create(module_type="pand2", + self.wen_and = factory.create(module_type="pand3", size=self.word_size+8, height=dff_height) - self.add_mod(self.wen_and2) + self.add_mod(self.wen_and) # s_en drives every sense amp - self.sen_and2 = factory.create(module_type="pand2", + self.sen_and3 = factory.create(module_type="pand3", size=self.word_size, height=dff_height) - self.add_mod(self.sen_and2) + self.add_mod(self.sen_and3) # used to generate inverted signals with low fanout self.inv = factory.create(module_type="pinv", size=1, height=dff_height) self.add_mod(self.inv) - + # p_en_bar drives every column in the bitcell array # but it is sized the same as the wl_en driver with # prepended 3 inverter stages to guarantee it is slower and odd polarity self.p_en_bar_driver = factory.create(module_type="pdriver", fanout=self.num_cols, - neg_polarity=True, height=dff_height) self.add_mod(self.p_en_bar_driver) - + + self.nand2 = factory.create(module_type="pnand2", + height=dff_height) + self.add_mod(self.nand2) # if (self.port_type == "rw") or (self.port_type == "r"): # from importlib import reload @@ -330,12 +332,9 @@ class control_logic(design.design): if self.port_type == "rw": self.input_list = ["csb", "web"] self.rbl_list = ["rbl_bl"] - elif self.port_type == "r": - self.input_list = ["csb"] - self.rbl_list = ["rbl_bl"] else: self.input_list = ["csb"] - self.rbl_list = [] + self.rbl_list = ["rbl_bl"] if self.port_type == "rw": self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"] @@ -344,11 +343,11 @@ class control_logic(design.design): # list of output control signals (for making a vertical bus) if self.port_type == "rw": - self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "we", "clk_buf", "we_bar", "cs"] + self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "we", "clk_buf", "we_bar", "cs"] elif self.port_type == "r": - self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs_bar", "cs"] + self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs_bar", "cs"] else: - self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"] + self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"] # leave space for the bus plus one extra space self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch @@ -382,10 +381,11 @@ class control_logic(design.design): self.create_gated_clk_buf_row() self.create_wlen_row() if (self.port_type == "rw") or (self.port_type == "w"): + self.create_rbl_delay_row() self.create_wen_row() if (self.port_type == "rw") or (self.port_type == "r"): self.create_sen_row() - self.create_delay() + self.create_delay() self.create_pen_row() @@ -419,12 +419,15 @@ class control_logic(design.design): row += 1 self.place_pen_row(row) row += 1 + if (self.port_type == "rw") or (self.port_type == "w"): + self.place_rbl_delay_row(row) + row += 1 if (self.port_type == "rw") or (self.port_type == "r"): self.place_sen_row(row) row += 1 - self.place_delay(row) - height = self.delay_inst.uy() - control_center_y = self.delay_inst.by() + self.place_delay(row) + height = self.delay_inst.uy() + control_center_y = self.delay_inst.by() # This offset is used for placement of the control logic in the SRAM level. self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y) @@ -443,9 +446,11 @@ class control_logic(design.design): self.route_dffs() self.route_wlen() if (self.port_type == "rw") or (self.port_type == "w"): + self.route_rbl_delay() self.route_wen() if (self.port_type == "rw") or (self.port_type == "r"): self.route_sen() + self.route_delay() self.route_pen() self.route_clk_buf() self.route_gated_clk_bar() @@ -457,7 +462,7 @@ class control_logic(design.design): """ Create the replica bitline """ self.delay_inst=self.add_inst(name="delay_chain", mod=self.delay_chain) - self.connect_inst(["rbl_bl", "pre_s_en", "vdd", "gnd"]) + self.connect_inst(["rbl_bl", "rbl_bl_delay", "vdd", "gnd"]) def place_delay(self,row): """ Place the replica bitline """ @@ -468,6 +473,22 @@ class control_logic(design.design): offset = vector(self.delay_chain.width, y_off) self.delay_inst.place(offset, mirror="MY") + def route_delay(self): + + out_pos = self.delay_inst.get_pin("out").bc() + # Connect to the rail level with the vdd rail + # Use pen since it is in every type of control logic + vdd_ypos = self.p_en_bar_nand_inst.get_pin("vdd").by() + in_pos = vector(self.rail_offsets["rbl_bl_delay"].x,vdd_ypos) + mid1 = vector(out_pos.x,in_pos.y) + self.add_wire(("metal1","via1","metal2"),[out_pos, mid1, in_pos]) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=in_pos) + + + # Input from RBL goes to the delay line for futher delay + self.copy_layout_pin(self.delay_inst, "in", "rbl_bl") + def create_clk_buf_row(self): """ Create the multistage and gated clock buffer """ @@ -476,12 +497,9 @@ class control_logic(design.design): self.connect_inst(["clk","clk_buf","vdd","gnd"]) def place_clk_buf_row(self,row): - """ Place the multistage clock buffer below the control flops """ - x_off = self.control_x_offset - (y_off,mirror)=self.get_offset(row) + x_offset = self.control_x_offset - offset = vector(x_off,y_off) - self.clk_buf_inst.place(offset, mirror) + x_offset = self.place_util(self.clk_buf_inst, x_offset, row) self.row_end_inst.append(self.clk_buf_inst) @@ -518,17 +536,10 @@ class control_logic(design.design): self.connect_inst(["cs","clk_bar","gated_clk_bar","vdd","gnd"]) def place_gated_clk_bar_row(self,row): - """ Place the gated clk logic below the control flops """ - x_off = self.control_x_offset - (y_off,mirror)=self.get_offset(row) + x_offset = self.control_x_offset - offset = vector(x_off,y_off) - self.clk_bar_inst.place(offset, mirror) - - x_off += self.inv.width - - offset = vector(x_off,y_off) - self.gated_clk_bar_inst.place(offset, mirror) + x_offset = self.place_util(self.clk_bar_inst, x_offset, row) + x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row) self.row_end_inst.append(self.gated_clk_bar_inst) @@ -562,12 +573,9 @@ class control_logic(design.design): self.connect_inst(["clk_buf", "cs","gated_clk_buf","vdd","gnd"]) def place_gated_clk_buf_row(self,row): - """ Place the gated clk logic below the control flops """ - x_off = self.control_x_offset - (y_off,mirror)=self.get_offset(row) - - offset = vector(x_off,y_off) - self.gated_clk_buf_inst.place(offset, mirror) + x_offset = self.control_x_offset + + x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row) self.row_end_inst.append(self.gated_clk_buf_inst) @@ -589,11 +597,9 @@ class control_logic(design.design): self.connect_inst(["gated_clk_bar", "wl_en", "vdd", "gnd"]) def place_wlen_row(self, row): - x_off = self.control_x_offset - (y_off,mirror)=self.get_offset(row) - - offset = vector(x_off, y_off) - self.wl_en_inst.place(offset, mirror) + x_offset = self.control_x_offset + + x_offset = self.place_util(self.wl_en_inst, x_offset, row) self.row_end_inst.append(self.wl_en_inst) @@ -604,25 +610,32 @@ class control_logic(design.design): self.connect_output(self.wl_en_inst, "Z", "wl_en") def create_pen_row(self): - # input: gated_clk_bar, output: p_en_bar - self.p_en_bar_inst=self.add_inst(name="inv_p_en_bar", - mod=self.p_en_bar_driver) - self.connect_inst(["gated_clk_buf", "p_en_bar", "vdd", "gnd"]) - + self.p_en_bar_nand_inst=self.add_inst(name="nand_p_en_bar", + mod=self.nand2) + self.connect_inst(["gated_clk_buf", "rbl_bl_delay", "p_en_bar_unbuf", "vdd", "gnd"]) + + self.p_en_bar_driver_inst=self.add_inst(name="buf_p_en_bar", + mod=self.p_en_bar_driver) + self.connect_inst(["p_en_bar_unbuf", "p_en_bar", "vdd", "gnd"]) def place_pen_row(self,row): - x_off = self.control_x_offset - (y_off,mirror)=self.get_offset(row) - offset = vector(x_off,y_off) - self.p_en_bar_inst.place(offset, mirror) + x_offset = self.control_x_offset - self.row_end_inst.append(self.p_en_bar_inst) + x_offset = self.place_util(self.p_en_bar_nand_inst, x_offset, row) + x_offset = self.place_util(self.p_en_bar_driver_inst, x_offset, row) + + self.row_end_inst.append(self.p_en_bar_driver_inst) def route_pen(self): - in_map = zip(["A"], ["gated_clk_buf"]) - self.connect_vertical_bus(in_map, self.p_en_bar_inst, self.rail_offsets) + in_map = zip(["A", "B"], ["gated_clk_buf", "rbl_bl_delay"]) + self.connect_vertical_bus(in_map, self.p_en_bar_nand_inst, self.rail_offsets) + + out_pos = self.p_en_bar_nand_inst.get_pin("Z").rc() + in_pos = self.p_en_bar_driver_inst.get_pin("A").lc() + mid1 = vector(out_pos.x,in_pos.y) + self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos]) - self.connect_output(self.p_en_bar_inst, "Z", "p_en_bar") + self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar") def create_sen_row(self): """ Create the sense enable buffer. """ @@ -632,20 +645,14 @@ class control_logic(design.design): input_name = "cs_bar" # GATE FOR S_EN self.s_en_gate_inst = self.add_inst(name="buf_s_en_and", - mod=self.sen_and2) - self.connect_inst(["pre_s_en", input_name, "s_en", "vdd", "gnd"]) + mod=self.sen_and3) + self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"]) def place_sen_row(self,row): - """ - The sense enable buffer gets placed to the far right of the - row. - """ - x_off = self.control_x_offset - (y_off,mirror)=self.get_offset(row) + x_offset = self.control_x_offset - offset = vector(x_off, y_off) - self.s_en_gate_inst.place(offset, mirror) + x_offset = self.place_util(self.s_en_gate_inst, x_offset, row) self.row_end_inst.append(self.s_en_gate_inst) @@ -657,22 +664,41 @@ class control_logic(design.design): else: input_name = "cs_bar" - sen_map = zip(["B"], [input_name]) + sen_map = zip(["A", "B", "C"], ["rbl_bl_delay", "gated_clk_bar", input_name]) self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.rail_offsets) - out_pos = self.delay_inst.get_pin("out").bc() - in_pos = self.s_en_gate_inst.get_pin("A").lc() - mid1 = vector(out_pos.x,in_pos.y) - self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos]) - self.connect_output(self.s_en_gate_inst, "Z", "s_en") - # Input from RBL goes to the delay line for futher delay - self.copy_layout_pin(self.delay_inst, "in", "rbl_bl") - + def create_rbl_delay_row(self): + + self.rbl_bl_delay_inv_inst = self.add_inst(name="rbl_bl_delay_inv", + mod=self.inv) + self.connect_inst(["rbl_bl_delay", "rbl_bl_delay_bar", "vdd", "gnd"]) + + def place_rbl_delay_row(self,row): + x_offset = self.control_x_offset + + x_offset = self.place_util(self.rbl_bl_delay_inv_inst, x_offset, row) + + self.row_end_inst.append(self.rbl_bl_delay_inv_inst) + + def route_rbl_delay(self): + # Connect from delay line + # Connect to rail + + rbl_map = zip(["Z"], ["rbl_bl_delay_bar"]) + self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + # The pin is on M1, so we need another via as well + self.add_via_center(layers=("metal1","via1","metal2"), + offset=self.rbl_bl_delay_inv_inst.get_pin("Z").center()) + + + rbl_map = zip(["A"], ["rbl_bl_delay"]) + self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets) def create_wen_row(self): + # input: we (or cs) output: w_en if self.port_type == "rw": input_name = "we" @@ -682,16 +708,14 @@ class control_logic(design.design): # GATE THE W_EN self.w_en_gate_inst = self.add_inst(name="w_en_and", - mod=self.wen_and2) - self.connect_inst([input_name, "gated_clk_bar", "w_en", "vdd", "gnd"]) + mod=self.wen_and) + self.connect_inst([input_name, "rbl_bl_delay_bar", "gated_clk_bar", "w_en", "vdd", "gnd"]) def place_wen_row(self,row): - x_off = self.ctrl_dff_inst.width + self.internal_bus_width - (y_off,mirror)=self.get_offset(row) - - offset = vector(x_off, y_off) - self.w_en_gate_inst.place(offset, mirror) + x_offset = self.control_x_offset + + x_offset = self.place_util(self.w_en_gate_inst, x_offset, row) self.row_end_inst.append(self.w_en_gate_inst) @@ -702,7 +726,7 @@ class control_logic(design.design): # No we for write-only reports, so use cs input_name = "cs" - wen_map = zip(["A", "B"], [input_name, "gated_clk_bar"]) + wen_map = zip(["A", "B", "C"], [input_name, "rbl_bl_delay_bar", "gated_clk_bar"]) self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.rail_offsets) self.connect_output(self.w_en_gate_inst, "Z", "w_en") @@ -781,9 +805,8 @@ class control_logic(design.design): self.add_power_pin("gnd", pin_loc) self.add_path("metal1", [row_loc, pin_loc]) - if (self.port_type == "rw") or (self.port_type == "r"): - self.copy_layout_pin(self.delay_inst,"gnd") - self.copy_layout_pin(self.delay_inst,"vdd") + self.copy_layout_pin(self.delay_inst,"gnd") + self.copy_layout_pin(self.delay_inst,"vdd") self.copy_layout_pin(self.ctrl_dff_inst,"gnd") self.copy_layout_pin(self.ctrl_dff_inst,"vdd") @@ -901,7 +924,8 @@ class control_logic(design.design): last_stage_rise = False #First stage(s), clk -(pdriver)-> clk_buf. - clk_buf_cout = self.replica_bitline.get_en_cin() + #clk_buf_cout = self.replica_bitline.get_en_cin() + clk_buf_cout = 0 stage_effort_list += self.clk_buf_driver.get_stage_efforts(clk_buf_cout, last_stage_rise) last_stage_rise = stage_effort_list[-1].is_rise @@ -948,3 +972,11 @@ class control_logic(design.design): self.graph_inst_exclude.add(self.ctrl_dff_inst) if self.port_type=="rw" or self.port_type=="w": self.graph_inst_exclude.add(self.w_en_gate_inst) + + def place_util(self, inst, x_offset, row): + """ Utility to place a row and compute the next offset """ + + (y_offset,mirror)=self.get_offset(row) + offset = vector(x_offset, y_offset) + inst.place(offset, mirror) + return x_offset+inst.width diff --git a/compiler/modules/dff.py b/compiler/modules/dff.py index c8d7414a..083d92b7 100644 --- a/compiler/modules/dff.py +++ b/compiler/modules/dff.py @@ -48,11 +48,6 @@ class dff(design.design): transition_prob = spice["flop_transition_prob"] return transition_prob*(c_load + c_para) - def analytical_delay(self, corner, slew, load = 0.0): - # dont know how to calculate this now, use constant in tech file - result = self.return_delay(spice["dff_delay"], spice["dff_slew"]) - return result - def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff""" #This is a handmade cell so the value must be entered in the tech.py file or estimated. diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index 32b36765..89b29476 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -160,11 +160,6 @@ class dff_array(design.design): self.add_via_center(layers=("metal2","via2","metal3"), offset=vector(clk_pin.cx(),clk_ypos)) - - - def analytical_delay(self, corner, slew, load=0.0): - return self.dff.analytical_delay(corner, slew=slew, load=load) - def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff array""" dff_clk_cin = self.dff.get_clk_cin() diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 9359329a..f6fc1cf2 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -177,16 +177,7 @@ class dff_buf(design.design): self.add_path("metal1", [self.mid_qb_pos, qb_pos]) self.add_via_center(layers=("metal1","via1","metal2"), offset=qb_pos) - - - - def analytical_delay(self, corner, slew, load=0.0): - """ Calculate the analytical delay of DFF-> INV -> INV """ - dff_delay=self.dff.analytical_delay(corner, slew=slew, load=self.inv1.input_load()) - inv1_delay = self.inv1.analytical_delay(corner, slew=dff_delay.slew, load=self.inv2.input_load()) - inv2_delay = self.inv2.analytical_delay(corner, slew=inv1_delay.slew, load=load) - return dff_delay + inv1_delay + inv2_delay - + def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff""" #This is a handmade cell so the value must be entered in the tech.py file or estimated. diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index df36b2aa..2fafee76 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -193,11 +193,6 @@ class dff_buf_array(design.design): self.add_via_center(layers=("metal2","via2","metal3"), offset=vector(clk_pin.cx(),clk_ypos)) - - - def analytical_delay(self, corner, slew, load=0.0): - return self.dff.analytical_delay(slew=slew, load=load) - def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff array""" dff_clk_cin = self.dff.get_clk_cin() diff --git a/compiler/modules/dff_inv.py b/compiler/modules/dff_inv.py index 226db290..207a5ad0 100644 --- a/compiler/modules/dff_inv.py +++ b/compiler/modules/dff_inv.py @@ -150,15 +150,7 @@ class dff_inv(design.design): offset=dout_pin.center()) self.add_via_center(layers=("metal1","via1","metal2"), offset=dout_pin.center()) - - - - def analytical_delay(self, corner, slew, load=0.0): - """ Calculate the analytical delay of DFF-> INV -> INV """ - dff_delay=self.dff.analytical_delay(corner, slew=slew, load=self.inv1.input_load()) - inv1_delay = self.inv1.analytical_delay(corner, slew=dff_delay.slew, load=load) - return dff_delay + inv1_delay - + def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff""" return self.dff.get_clk_cin() diff --git a/compiler/modules/dff_inv_array.py b/compiler/modules/dff_inv_array.py index 051dd237..760a2337 100644 --- a/compiler/modules/dff_inv_array.py +++ b/compiler/modules/dff_inv_array.py @@ -190,12 +190,6 @@ class dff_inv_array(design.design): self.add_via_center(layers=("metal2","via2","metal3"), offset=vector(clk_pin.cx(),clk_ypos)) - - - - def analytical_delay(self, corner, slew, load=0.0): - return self.dff.analytical_delay(corner, slew=slew, load=load) - def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff array""" dff_clk_cin = self.dff.get_clk_cin() diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 75eb3345..0e70db55 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -596,28 +596,6 @@ class hierarchical_decoder(design.design): self.add_via_center(layers=("metal2", "via2", "metal3"), offset=rail_pos) - - def analytical_delay(self, corner, slew, load = 0.0): - # A -> out - if self.determine_predecodes(self.num_inputs)[1]==0: - pre = self.pre2_4 - nand = self.nand2 - else: - pre = self.pre3_8 - nand = self.nand3 - a_t_out_delay = pre.analytical_delay(corner, slew=slew,load = nand.input_load()) - - # out -> z - out_t_z_delay = nand.analytical_delay(corner, slew= a_t_out_delay.slew, - load = self.inv.input_load()) - result = a_t_out_delay + out_t_z_delay - - # Z -> decode_out - z_t_decodeout_delay = self.inv.analytical_delay(corner, slew = out_t_z_delay.slew , load = load) - result = result + z_t_decodeout_delay - return result - - def input_load(self): if self.determine_predecodes(self.num_inputs)[1]==0: pre = self.pre2_4 diff --git a/compiler/modules/hierarchical_predecode2x4.py b/compiler/modules/hierarchical_predecode2x4.py index 56046c74..f05f54b0 100644 --- a/compiler/modules/hierarchical_predecode2x4.py +++ b/compiler/modules/hierarchical_predecode2x4.py @@ -56,21 +56,4 @@ class hierarchical_predecode2x4(hierarchical_predecode): ["A_0", "Abar_1"], ["Abar_0", "A_1"], ["A_0", "A_1"]] - return combination - - - def analytical_delay(self, corner, slew, load = 0.0 ): - # in -> inbar - a_t_b_delay = self.inv.analytical_delay(corner, slew=slew, load=self.nand.input_load()) - - # inbar -> z - b_t_z_delay = self.nand.analytical_delay(corner, slew=a_t_b_delay.slew, load=self.inv.input_load()) - - # Z -> out - a_t_out_delay = self.inv.analytical_delay(corner, slew=b_t_z_delay.slew, load=load) - - return a_t_b_delay + b_t_z_delay + a_t_out_delay - - - def input_load(self): - return self.nand.input_load() + return combination \ No newline at end of file diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index a7794395..20b629bb 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -65,21 +65,4 @@ class hierarchical_predecode3x8(hierarchical_predecode): ["A_0", "Abar_1", "A_2"], ["Abar_0", "A_1", "A_2"], ["A_0", "A_1", "A_2"]] - return combination - - - def analytical_delay(self, corner, slew, load = 0.0 ): - # A -> Abar - a_t_b_delay = self.inv.analytical_delay(corner, slew=slew, load=self.nand.input_load()) - - # Abar -> z - b_t_z_delay = self.nand.analytical_delay(corner, slew=a_t_b_delay.slew, load=self.inv.input_load()) - - # Z -> out - a_t_out_delay = self.inv.analytical_delay(corner, slew=b_t_z_delay.slew, load=load) - - return a_t_b_delay + b_t_z_delay + a_t_out_delay - - - def input_load(self): - return self.nand.input_load() + return combination \ No newline at end of file diff --git a/compiler/modules/multibank.py b/compiler/modules/multibank.py index fd061273..28f689f7 100644 --- a/compiler/modules/multibank.py +++ b/compiler/modules/multibank.py @@ -827,22 +827,4 @@ class multibank(design.design): rotate=90) self.add_via(layers=("metal2","via2","metal3"), offset=in_pin + self.m2m3_via_offset, - rotate=90) - - def analytical_delay(self, corner, slew, load): - """ return analytical delay of the bank""" - decoder_delay = self.row_decoder.analytical_delay(corner, slew, self.wordline_driver.input_load()) - - word_driver_delay = self.wordline_driver.analytical_delay(corner, decoder_delay.slew, self.bitcell_array.input_load()) - - bitcell_array_delay = self.bitcell_array.analytical_delay(corner, word_driver_delay.slew) - - bl_t_data_out_delay = self.sense_amp_array.analytical_delay(corner, bitcell_array_delay.slew, - self.bitcell_array.output_load()) - # output load of bitcell_array is set to be only small part of bl for sense amp. - - data_t_DATA_delay = self.tri_gate_array.analytical_delay(corner, bl_t_data_out_delay.slew, load) - - result = decoder_delay + word_driver_delay + bitcell_array_delay + bl_t_data_out_delay + data_t_DATA_delay - return result - + rotate=90) \ No newline at end of file diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 26104593..2eed4bb9 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -82,9 +82,8 @@ class port_data(design.design): def add_pins(self): """ Adding pins for port address module""" - if self.has_rbl(): - self.add_pin("rbl_bl","INOUT") - self.add_pin("rbl_br","INOUT") + self.add_pin("rbl_bl","INOUT") + self.add_pin("rbl_br","INOUT") for bit in range(self.num_cols): self.add_pin("{0}_{1}".format(self.bl_names[self.port], bit),"INOUT") self.add_pin("{0}_{1}".format(self.br_names[self.port], bit),"INOUT") @@ -155,29 +154,20 @@ class port_data(design.design): def add_modules(self): - if self.port in self.read_ports: - # Extra column +1 is for RBL - # Precharge will be shifted left if needed - self.precharge_array = factory.create(module_type="precharge_array", - columns=self.num_cols + 1, - bitcell_bl=self.bl_names[self.port], - bitcell_br=self.br_names[self.port]) - self.add_mod(self.precharge_array) + # Extra column +1 is for RBL + # Precharge will be shifted left if needed + self.precharge_array = factory.create(module_type="precharge_array", + columns=self.num_cols + 1, + bitcell_bl=self.bl_names[self.port], + bitcell_br=self.br_names[self.port]) + self.add_mod(self.precharge_array) + if self.port in self.read_ports: self.sense_amp_array = factory.create(module_type="sense_amp_array", word_size=self.word_size, words_per_row=self.words_per_row) self.add_mod(self.sense_amp_array) else: - # Precharge is needed when we have a column mux or for byte writes - # to prevent corruption of half-selected cells, so just always add it - # This is a little power inefficient for write ports without a column mux, - # but it is simpler. - self.precharge_array = factory.create(module_type="precharge_array", - columns=self.num_cols, - bitcell_bl=self.bl_names[self.port], - bitcell_br=self.br_names[self.port]) - self.add_mod(self.precharge_array) self.sense_amp_array = None @@ -205,7 +195,7 @@ class port_data(design.design): write_size=self.write_size) self.add_mod(self.write_mask_and_array) else: - self.write_mask_and_array_inst = None + self.write_mask_and_array = None else: self.write_driver_array = None @@ -243,14 +233,14 @@ class port_data(design.design): temp = [] # Use left BLs for RBL - if self.has_rbl() and self.port==0: + if self.port==0: temp.append("rbl_bl") temp.append("rbl_br") for bit in range(self.num_cols): temp.append(self.bl_names[self.port]+"_{0}".format(bit)) temp.append(self.br_names[self.port]+"_{0}".format(bit)) # Use right BLs for RBL - if self.has_rbl() and self.port==1: + if self.port==1: temp.append("rbl_bl") temp.append("rbl_br") temp.extend(["p_en_bar", "vdd"]) @@ -379,7 +369,7 @@ class port_data(design.design): vertical_port_order.append(self.write_mask_and_array_inst) # Add one column for the the RBL - if self.has_rbl() and self.port == 0: + if self.port==0: x_offset = self.bitcell.width else: x_offset = 0 @@ -478,7 +468,7 @@ class port_data(design.design): inst1 = self.column_mux_array_inst inst2 = self.precharge_array_inst - if self.has_rbl() and self.port==0: + if self.port==0: self.connect_bitlines(inst1, inst2, self.num_cols, inst2_start_bit=1) else: self.connect_bitlines(inst1, inst2, self.num_cols) @@ -499,7 +489,7 @@ class port_data(design.design): inst1 = self.precharge_array_inst inst1_bl_name = "bl_{}" inst1_br_name = "br_{}" - if self.has_rbl() and self.port==0: + if self.port==0: start_bit=1 else: start_bit=0 @@ -524,7 +514,7 @@ class port_data(design.design): inst1 = self.precharge_array_inst inst1_bl_name = "bl_{}" inst1_br_name = "br_{}" - if self.has_rbl() and self.port==0: + if self.port==0: start_bit=1 else: start_bit=0 @@ -560,11 +550,11 @@ class port_data(design.design): """ Add the bitline pins for the given port """ # Connect one bitline to the RBL and offset the indices for the other BLs - if self.has_rbl() and self.port==0: + if self.port==0: self.copy_layout_pin(self.precharge_array_inst, "bl_0", "rbl_bl") self.copy_layout_pin(self.precharge_array_inst, "br_0", "rbl_br") bit_offset=1 - elif self.has_rbl() and self.port==1: + elif self.port==1: self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(self.num_cols), "rbl_bl") self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(self.num_cols), "rbl_br") bit_offset=0 @@ -669,5 +659,3 @@ class port_data(design.design): if self.precharge_array_inst: self.graph_inst_exclude.add(self.precharge_array_inst) - def has_rbl(self): - return self.port in self.read_ports diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 6f4e96c5..b70a7bce 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -35,7 +35,7 @@ class replica_bitcell_array(design.design): self.right_rbl = right_rbl self.bitcell_ports = bitcell_ports - debug.check(left_rbl+right_rbl==len(self.read_ports),"Invalid number of RBLs for port configuration.") + debug.check(left_rbl+right_rbl==len(self.all_ports),"Invalid number of RBLs for port configuration.") debug.check(left_rbl+right_rbl==len(self.bitcell_ports),"Bitcell ports must match total RBLs.") # Two dummy rows/cols plus replica for each port @@ -373,17 +373,7 @@ class replica_bitcell_array(design.design): def get_rbl_br_name(self, port): """ Return the BR for the given RBL port """ return self.rbl_br_names[port] - - def analytical_delay(self, corner, slew, load): - """Returns relative delay of the bitline in the bitcell array""" - from tech import parameter - #The load being driven/drained is mostly the bitline but could include the sense amp or the column mux. - #The load from the bitlines is due to the drain capacitances from all the other bitlines and wire parasitics. - drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) - wire_unit_load = 0.05 * drain_load #Wires add 5% to this. - bitline_load = (drain_load+wire_unit_load)*self.row_size - return [self.cell.analytical_delay(corner, slew, load+bitline_load)] - + def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" from tech import drc, parameter @@ -403,15 +393,6 @@ class replica_bitcell_array(design.design): cell_power.leakage * self.column_size * self.row_size) return total_power - def gen_wl_wire(self): - if OPTS.netlist_only: - width = 0 - else: - width = self.width - wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_metal1")) - wl_wire.wire_c = 2*spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell - return wl_wire - def gen_bl_wire(self): if OPTS.netlist_only: height = 0 @@ -422,15 +403,6 @@ class replica_bitcell_array(design.design): bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell return bl_wire - def output_load(self, bl_pos=0): - bl_wire = self.gen_bl_wire() - return bl_wire.wire_c # sense amp only need to charge small portion of the bl - # set as one segment for now - - def input_load(self): - wl_wire = self.gen_wl_wire() - return wl_wire.return_input_cap() - def get_wordline_cin(self): """Get the relative input capacitance from the wordline connections in all the bitcell""" #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index e104356f..e77d577f 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -33,15 +33,18 @@ class sense_amp(design.design): self.pin_map = sense_amp.pin_map self.add_pin_types(self.type_list) - def input_load(self): + def get_cin(self): + + # FIXME: This input load will be applied to both the s_en timing and bitline timing. + #Input load for the bitlines which are connected to the source/drain of a TX. Not the selects. from tech import spice, parameter # Default is 8x. Per Samira and Hodges-Jackson book: # "Column-mux transistors driven by the decoder must be sized for optimal speed" bitline_pmos_size = 8 #FIXME: This should be set somewhere and referenced. Probably in tech file. - return spice["min_tx_drain_c"]*(bitline_pmos_size/parameter["min_tx_size"])#ff + return spice["min_tx_drain_c"]*(bitline_pmos_size)#ff - def analytical_delay(self, corner, slew, load): + def get_stage_effort(self, load): #Delay of the sense amp will depend on the size of the amp and the output load. parasitic_delay = 1 cin = (parameter["sa_inv_pmos_size"] + parameter["sa_inv_nmos_size"])/drc("minwidth_tx") diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 40c3adeb..598886c0 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -144,10 +144,7 @@ class sense_amp_array(design.design): def input_load(self): return self.amp.input_load() - - def analytical_delay(self, corner, slew, load): - return [self.amp.analytical_delay(corner, slew=slew, load=load)] - + def get_en_cin(self): """Get the relative capacitance of all the sense amp enable connections in the array""" sense_amp_en_cin = self.amp.get_en_cin() diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index e270f2f6..7e3beaad 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -210,15 +210,6 @@ class single_level_column_mux_array(design.design): self.add_via_center(layers=("metal1", "via1", "metal2"), offset=br_out_offset) - - def analytical_delay(self, corner, slew, load): - from tech import parameter - """Returns relative delay that the column mux adds""" - #Single level column mux will add parasitic loads from other mux pass transistors and the sense amp. - drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) - array_load = drain_load*self.words_per_row - return [self.mux.analytical_delay(corner, slew, load+array_load)] - def get_drain_cin(self): """Get the relative capacitance of the drain of the NMOS pass TX""" from tech import parameter diff --git a/compiler/modules/tri_gate.py b/compiler/modules/tri_gate.py index 3907a03b..998562b1 100644 --- a/compiler/modules/tri_gate.py +++ b/compiler/modules/tri_gate.py @@ -12,7 +12,7 @@ from tech import GDS,layer class tri_gate(design.design): """ - This module implements the tri gate cell used in the design for + This module implements the tri gate cell used in the design forS bit-line isolation. It is a hand-made cell, so the layout and netlist should be available in the technology library. """ @@ -36,19 +36,13 @@ class tri_gate(design.design): self.pin_map = tri_gate.pin_map self.add_pin_types(self.type_list) - def analytical_delay(self, corner, slew, load=0.0): - from tech import spice - r = spice["min_tx_r"] - c_para = spice["min_tx_drain_c"] - return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew) - def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" #Power in this module currently not defined. Returns 0 nW (leakage and dynamic). total_power = self.return_power() return total_power - def input_load(self): + def get_cin(self): return 9*spice["min_tx_gate_c"] def build_graph(self, graph, inst_name, port_nets): diff --git a/compiler/modules/tri_gate_array.py b/compiler/modules/tri_gate_array.py index df39ad96..7d1c21d0 100644 --- a/compiler/modules/tri_gate_array.py +++ b/compiler/modules/tri_gate_array.py @@ -120,10 +120,4 @@ class tri_gate_array(design.design): layer="metal1", offset=enbar_pin.ll().scale(0, 1), width=width, - height=drc("minwidth_metal1")) - - - - def analytical_delay(self, corner, slew, load=0.0): - return self.tri.analytical_delay(corner, slew = slew, load = load) - + height=drc("minwidth_metal1")) \ No newline at end of file diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index 39700799..b1292560 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -210,21 +210,6 @@ class wordline_driver(design.design): start=wl_offset, end=wl_offset-vector(self.m1_width,0)) - - def analytical_delay(self, corner, slew, load=0): - # decode -> net - decode_t_net = self.nand2.analytical_delay(corner, slew, self.inv.input_load()) - - # net -> wl - net_t_wl = self.inv.analytical_delay(corner, decode_t_net.slew, load) - - return decode_t_net + net_t_wl - - - def input_load(self): - """Gets the capacitance of the wordline driver in absolute units (fF)""" - return self.nand2.input_load() - def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): """Follows the clk_buf to a wordline signal adding each stages stage effort to a list""" stage_effort_list = [] diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index d299cc56..48f111c7 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -110,14 +110,6 @@ class pand2(pgate.pgate): width=pin.width(), height=pin.height()) - - - def analytical_delay(self, corner, slew, load=0.0): - """ Calculate the analytical delay of DFF-> INV -> INV """ - nand_delay = self.nand.analytical_delay(corner, slew=slew, load=self.inv.input_load()) - inv_delay = self.inv.analytical_delay(corner, slew=nand_delay.slew, load=load) - return nand_delay + inv_delay - def get_stage_efforts(self, external_cout, inp_is_rise=False): """Get the stage efforts of the A or B -> Z path""" stage_effort_list = [] diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py new file mode 100644 index 00000000..b221ad84 --- /dev/null +++ b/compiler/pgates/pand3.py @@ -0,0 +1,138 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +from tech import drc +from math import log +from vector import vector +from globals import OPTS +import pgate +from sram_factory import factory + +class pand3(pgate.pgate): + """ + This is a simple buffer used for driving loads. + """ + def __init__(self, name, size=1, height=None): + debug.info(1, "Creating pand3 {}".format(name)) + self.add_comment("size: {}".format(size)) + + self.size = size + + # Creates the netlist and layout + pgate.pgate.__init__(self, name, height) + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + # Shield the cap, but have at least a stage effort of 4 + self.nand = factory.create(module_type="pnand3",height=self.height) + self.add_mod(self.nand) + + self.inv = factory.create(module_type="pinv", size=self.size, height=self.height) + self.add_mod(self.inv) + + def create_layout(self): + self.width = self.nand.width + self.inv.width + self.place_insts() + self.add_wires() + self.add_layout_pins() + self.DRC_LVS() + + def add_pins(self): + self.add_pin("A", "INPUT") + self.add_pin("B", "INPUT") + self.add_pin("C", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_insts(self): + self.nand_inst=self.add_inst(name="pand3_nand", + mod=self.nand) + self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"]) + + self.inv_inst=self.add_inst(name="pand3_inv", + mod=self.inv) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add NAND to the right + self.nand_inst.place(offset=vector(0,0)) + + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(),0)) + + def add_wires(self): + # nand Z to inv A + z1_pin = self.nand_inst.get_pin("Z") + a2_pin = self.inv_inst.get_pin("A") + mid1_point = vector(0.5*(z1_pin.cx()+a2_pin.cx()), z1_pin.cy()) + mid2_point = vector(mid1_point, a2_pin.cy()) + self.add_path("metal1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) + + + def add_layout_pins(self): + # Continous vdd rail along with label. + vdd_pin=self.inv_inst.get_pin("vdd") + self.add_layout_pin(text="vdd", + layer="metal1", + offset=vdd_pin.ll().scale(0,1), + width=self.width, + height=vdd_pin.height()) + + # Continous gnd rail along with label. + gnd_pin=self.inv_inst.get_pin("gnd") + self.add_layout_pin(text="gnd", + layer="metal1", + offset=gnd_pin.ll().scale(0,1), + width=self.width, + height=vdd_pin.height()) + + pin = self.inv_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + for pin_name in ["A","B", "C"]: + pin = self.nand_inst.get_pin(pin_name) + self.add_layout_pin_rect_center(text=pin_name, + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + + + def analytical_delay(self, corner, slew, load=0.0): + """ Calculate the analytical delay of DFF-> INV -> INV """ + nand_delay = self.nand.analytical_delay(corner, slew=slew, load=self.inv.input_load()) + inv_delay = self.inv.analytical_delay(corner, slew=nand_delay.slew, load=load) + return nand_delay + inv_delay + + def get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.inv.get_cin() + stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/pgates/pbuf.py b/compiler/pgates/pbuf.py index 125a1190..fcfc6586 100644 --- a/compiler/pgates/pbuf.py +++ b/compiler/pgates/pbuf.py @@ -113,14 +113,6 @@ class pbuf(pgate.pgate): width=a_pin.width(), height=a_pin.height()) - - - def analytical_delay(self, corner, slew, load=0.0): - """ Calculate the analytical delay of DFF-> INV -> INV """ - inv1_delay = self.inv1.analytical_delay(corner, slew=slew, load=self.inv2.input_load()) - inv2_delay = self.inv2.analytical_delay(corner, slew=inv1_delay.slew, load=load) - return inv1_delay + inv2_delay - def get_stage_efforts(self, external_cout, inp_is_rise=False): """Get the stage efforts of the A -> Z path""" stage_effort_list = [] diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index a3180006..36811a5e 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -173,30 +173,6 @@ class pdriver(pgate.pgate): offset=a_pin.center(), width = a_pin.width(), height = a_pin.height()) - - def input_load(self): - return self.inv_list[0].input_load() - - def analytical_delay(self, corner, slew, load=0.0): - """ Calculate the analytical delay of INV1 -> ... -> INVn """ - - cout_list = [] - for prev_inv,inv in zip(self.inv_list, self.inv_list[1:]): - cout_list.append(inv.input_load()) - cout_list.append(load) - - input_slew = slew - - delays = [] - for inv,cout in zip(self.inv_list,cout_list): - delays.append(inv.analytical_delay(corner, slew=input_slew, load=cout)) - input_slew = delays[-1].slew - - delay = delays[0] - for i in range(len(delays)-1): - delay += delays[i] - - return delay def get_sizes(self): """ Return the relative sizes of the buffers """ diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 773a0452..0844c762 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -255,15 +255,6 @@ class pinv(pgate.pgate): self.connect_pin_to_rail(self.pmos_inst,"S","vdd") - - def input_load(self): - return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] - - def analytical_delay(self, corner, slew, load=0.0): - r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) - c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff - return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew) - def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" c_eff = self.calculate_effective_capacitance(load) @@ -281,11 +272,11 @@ class pinv(pgate.pgate): transition_prob = spice["inv_transition_prob"] return transition_prob*(c_load + c_para) - def get_cin(self): + def input_load(self): """Return the capacitance of the gate connection in generic capacitive units relative to the minimum width of a transistor""" return self.nmos_size + self.pmos_size - + def get_stage_effort(self, cout, inp_is_rise=True): """Returns an object representing the parameters for delay in tau units. Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. @@ -293,7 +284,7 @@ class pinv(pgate.pgate): parasitic_delay = 1 return logical_effort.logical_effort(self.name, self.size, - self.get_cin(), + self.input_load(), cout, parasitic_delay, not inp_is_rise) diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index 72ab6a8a..a8d2c8b8 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -178,15 +178,7 @@ class pinvbuf(pgate.pgate): offset=a_pin.center()) self.add_via_center(layers=("metal1","via1","metal2"), offset=a_pin.center()) - - - - def analytical_delay(self, corner, slew, load=0.0): - """ Calculate the analytical delay of DFF-> INV -> INV """ - inv1_delay = self.inv1.analytical_delay(corner, slew=slew, load=self.inv2.input_load()) - inv2_delay = self.inv2.analytical_delay(corner, slew=inv1_delay.slew, load=load) - return inv1_delay + inv2_delay - + def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False): """Get the stage efforts of the clk -> clk_buf path""" stage_effort_list = [] diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 956f0a30..21ca4184 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -37,10 +37,7 @@ class pnand2(pgate.pgate): # Creates the netlist and layout pgate.pgate.__init__(self, name, height) - - #For characterization purposes only - #self.exclude_nmos_from_graph() - + def create_netlist(self): self.add_pins() self.add_ptx() @@ -233,17 +230,6 @@ class pnand2(pgate.pgate): width=contact.m1m2.first_layer_height, height=contact.m1m2.first_layer_width) - - - - def input_load(self): - return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] - - def analytical_delay(self, corner, slew, load=0.0): - r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) - c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff - return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew) - def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" c_eff = self.calculate_effective_capacitance(load) @@ -261,23 +247,16 @@ class pnand2(pgate.pgate): transition_prob = spice["nand2_transition_prob"] return transition_prob*(c_load + c_para) - def get_cin(self): + def input_load(self): """Return the relative input capacitance of a single input""" return self.nmos_size+self.pmos_size - + def get_stage_effort(self, cout, inp_is_rise=True): """Returns an object representing the parameters for delay in tau units. Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. """ parasitic_delay = 2 - return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) - - def exclude_nmos_from_graph(self): - """Exclude the nmos TXs from the graph to reduce complexity""" - #The pull-down network has an internal net which causes 2 different in->out paths - #Removing them simplifies generic path searching. - self.graph_inst_exclude.add(self.nmos1_inst) - self.graph_inst_exclude.add(self.nmos2_inst) + return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise) def build_graph(self, graph, inst_name, port_nets): """Adds edges based on inputs/outputs. Overrides base class function.""" diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 30da9165..89628267 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -243,16 +243,6 @@ class pnand3(pgate.pgate): width=contact.m1m2.first_layer_width, height=contact.m1m2.first_layer_height) - - - def input_load(self): - return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] - - def analytical_delay(self, corner, slew, load=0.0): - r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) - c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff - return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew) - def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" c_eff = self.calculate_effective_capacitance(load) @@ -270,7 +260,7 @@ class pnand3(pgate.pgate): transition_prob = spice["nand3_transition_prob"] return transition_prob*(c_load + c_para) - def get_cin(self): + def input_load(self): """Return the relative input capacitance of a single input""" return self.nmos_size+self.pmos_size @@ -279,7 +269,7 @@ class pnand3(pgate.pgate): Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. """ parasitic_delay = 3 - return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) + return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise) def build_graph(self, graph, inst_name, port_nets): """Adds edges based on inputs/outputs. Overrides base class function.""" diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index fa52c528..472c75a8 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -5,12 +5,13 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # +import contact import pgate import debug from tech import drc, parameter, spice from vector import vector from globals import OPTS -import contact +import logical_effort from sram_factory import factory class pnor2(pgate.pgate): @@ -38,28 +39,30 @@ class pnor2(pgate.pgate): pgate.pgate.__init__(self, name, height) - def add_pins(self): - """ Adds pins for spice netlist """ - pin_list = ["A", "B", "Z", "vdd", "gnd"] - dir_list = ["INPUT", "INPUT", "OUTPUT", "INOUT", "INOUT"] - self.add_pin_list(pin_list, dir_list) - def create_netlist(self): self.add_pins() + self.add_ptx() self.create_ptx() - self.setup_layout_constants() def create_layout(self): """ Calls all functions related to the generation of the layout """ - self.add_supply_rails() - self.add_ptx() + + self.setup_layout_constants() + self.route_supply_rails() + self.place_ptx() self.connect_rails() self.add_well_contacts() self.extend_wells(self.well_pos) self.route_inputs() self.route_output() - def create_ptx(self): + def add_pins(self): + """ Adds pins for spice netlist """ + pin_list = ["A", "B", "Z", "vdd", "gnd"] + dir_list = ["INPUT", "INPUT", "OUTPUT", "INOUT", "INOUT"] + self.add_pin_list(pin_list, dir_list) + + def add_ptx(self): """ Create the PMOS and NMOS transistors. """ self.nmos = factory.create(module_type="ptx", width=self.nmos_width, @@ -104,7 +107,7 @@ class pnor2(pgate.pgate): self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, drc("poly_extend_active"), self.poly_space) - def add_supply_rails(self): + def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ self.add_layout_pin_rect_center(text="gnd", layer="metal1", @@ -116,7 +119,31 @@ class pnor2(pgate.pgate): offset=vector(0.5*self.width,self.height), width=self.width) - def add_ptx(self): + def create_ptx(self): + """ + Add PMOS and NMOS to the layout at the upper-most and lowest position + to provide maximum routing in channel + """ + + self.pmos1_inst=self.add_inst(name="pnor2_pmos1", + mod=self.pmos) + self.connect_inst(["vdd", "A", "net1", "vdd"]) + + self.pmos2_inst = self.add_inst(name="pnor2_pmos2", + mod=self.pmos) + self.connect_inst(["net1", "B", "Z", "vdd"]) + + + self.nmos1_inst=self.add_inst(name="pnor2_nmos1", + mod=self.nmos) + self.connect_inst(["Z", "A", "gnd", "gnd"]) + + self.nmos2_inst=self.add_inst(name="pnor2_nmos2", + mod=self.nmos) + self.connect_inst(["Z", "B", "gnd", "gnd"]) + + + def place_ptx(self): """ Add PMOS and NMOS to the layout at the upper-most and lowest position to provide maximum routing in channel @@ -124,29 +151,16 @@ class pnor2(pgate.pgate): pmos1_pos = vector(self.pmos.active_offset.x, self.height - self.pmos.active_height - self.top_bottom_space) - self.pmos1_inst=self.add_inst(name="pnor2_pmos1", - mod=self.pmos, - offset=pmos1_pos) - self.connect_inst(["vdd", "A", "net1", "vdd"]) + self.pmos1_inst.place(pmos1_pos) self.pmos2_pos = pmos1_pos + self.overlap_offset - self.pmos2_inst = self.add_inst(name="pnor2_pmos2", - mod=self.pmos, - offset=self.pmos2_pos) - self.connect_inst(["net1", "B", "Z", "vdd"]) - + self.pmos2_inst.place(self.pmos2_pos) nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space) - self.nmos1_inst=self.add_inst(name="pnor2_nmos1", - mod=self.nmos, - offset=nmos1_pos) - self.connect_inst(["Z", "A", "gnd", "gnd"]) - + self.nmos1_inst.place(nmos1_pos) + self.nmos2_pos = nmos1_pos + self.overlap_offset - self.nmos2_inst=self.add_inst(name="pnor2_nmos2", - mod=self.nmos, - offset=self.nmos2_pos) - self.connect_inst(["Z", "B", "gnd", "gnd"]) + self.nmos2_inst.place(self.nmos2_pos) # Output position will be in between the PMOS and NMOS self.output_pos = vector(0,0.5*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height)) @@ -213,16 +227,6 @@ class pnor2(pgate.pgate): width=contact.m1m2.first_layer_height, height=contact.m1m2.first_layer_width) - - - def input_load(self): - return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] - - def analytical_delay(self, corner, slew, load=0.0): - r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) - c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff - return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew) - def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" c_eff = self.calculate_effective_capacitance(load) diff --git a/compiler/pgates/ptristate_inv.py b/compiler/pgates/ptristate_inv.py index 55a248a7..0564bf86 100644 --- a/compiler/pgates/ptristate_inv.py +++ b/compiler/pgates/ptristate_inv.py @@ -210,18 +210,11 @@ class ptristate_inv(pgate.pgate): self.connect_pin_to_rail(self.nmos1_inst,"S","gnd") self.connect_pin_to_rail(self.pmos1_inst,"S","vdd") - - def analytical_delay(self, corner, slew, load=0.0): - from tech import spice - r = spice["min_tx_r"] - c_para = spice["min_tx_drain_c"] - return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew) - def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" #Power in this module currently not defined. Returns 0 nW (leakage and dynamic). total_power = self.return_power() return total_power - def input_load(self): + def get_cin(self): return 9*spice["min_tx_gate_c"] diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 7c21c0a8..999d3ccd 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -189,7 +189,7 @@ class single_level_column_mux(pgate.pgate): width=self.bitcell.width, height=self.height) - def analytical_delay(self, corner, slew, load): + def get_stage_effort(self, corner, slew, load): """Returns relative delay that the column mux. Difficult to convert to LE model.""" parasitic_delay = 1 cin = 2*self.tx_size #This is not CMOS, so using this may be incorrect. diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 355ca769..51cfeada 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -301,7 +301,7 @@ class sram_1bank(sram_base): dest_pin = self.bank_inst.get_pin(signal+"{}".format(port)) self.connect_vbus_m2m3(src_pin, dest_pin) - for port in self.read_ports: + for port in self.all_ports: # Only input (besides pins) is the replica bitline src_pin = self.control_logic_insts[port].get_pin("rbl_bl") dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port)) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index f34d058e..f06a56f0 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -339,7 +339,7 @@ class sram_base(design, verilog, lef): for port in self.read_ports: for bit in range(self.word_size): temp.append("DOUT{0}[{1}]".format(port,bit)) - for port in self.read_ports: + for port in self.all_ports: temp.append("rbl_bl{0}".format(port)) for port in self.write_ports: for bit in range(self.word_size): @@ -499,9 +499,7 @@ class sram_base(design, verilog, lef): if port in self.readwrite_ports: temp.append("web{}".format(port)) temp.append("clk{}".format(port)) - - if port in self.read_ports: - temp.append("rbl_bl{}".format(port)) + temp.append("rbl_bl{}".format(port)) # Ouputs if port in self.read_ports: @@ -564,31 +562,7 @@ class sram_base(design, verilog, lef): self.sp_write_file(sp, usedMODS) del usedMODS sp.close() - - - def analytical_delay(self, corner, slew,load): - """ Estimates the delay from clk -> DOUT - LH and HL are the same in analytical model. """ - delays = {} - for port in self.all_ports: - if port in self.readonly_ports: - control_logic = self.control_logic_r - elif port in self.readwrite_ports: - control_logic = self.control_logic_rw - else: - delays[port] = self.return_delay(0,0) #Write ports do not have a lib defined delay, marked as 0 - continue - clk_to_wlen_delays = control_logic.analytical_delay(corner, slew, load) - wlen_to_dout_delays = self.bank.analytical_delay(corner,slew,load,port) #port should probably be specified... - all_delays = clk_to_wlen_delays+wlen_to_dout_delays - total_delay = logical_effort.calculate_absolute_delay(all_delays) - total_delay = self.apply_corners_analytically(total_delay, corner) - last_slew = .1*all_delays[-1].get_absolute_delay() #slew approximated as 10% of delay - last_slew = self.apply_corners_analytically(last_slew, corner) - delays[port] = self.return_delay(delay=total_delay, slew=last_slew) - - return delays - + def get_wordline_stage_efforts(self, inp_is_rise=True): """Get the all the stage efforts for each stage in the path from clk_buf to a wordline""" stage_effort_list = [] diff --git a/compiler/tests/04_pand3_test.py b/compiler/tests/04_pand3_test.py new file mode 100755 index 00000000..4408f6e8 --- /dev/null +++ b/compiler/tests/04_pand3_test.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class pand3_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + global verify + import verify + + import pand3 + + debug.info(2, "Testing pand3 gate 4x") + a = pand3.pand3(name="pand3x4", size=4) + self.local_check(a) + + globals.end_openram() + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/21_model_delay_test.py b/compiler/tests/21_model_delay_test.py index 3b43e789..114f0c6e 100755 --- a/compiler/tests/21_model_delay_test.py +++ b/compiler/tests/21_model_delay_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -@unittest.skip("SKIPPING 21_model_delay_test") +# @unittest.skip("SKIPPING 21_model_delay_test") class model_delay_test(openram_test): """ Compare the accuracy of the analytical model with a spice simulation. """ @@ -51,23 +51,28 @@ class model_delay_test(openram_test): import tech loads = [tech.spice["msflop_in_cap"]*4] slews = [tech.spice["rise_time"]*2] + + # Run a spice characterization spice_data, port_data = d.analyze(probe_address, probe_data, slews, loads) spice_data.update(port_data[0]) + # Run analytical characterization model_data, port_data = d.analytical_delay(slews, loads) model_data.update(port_data[0]) - #Only compare the delays + # Only compare the delays spice_delays = {key:value for key, value in spice_data.items() if 'delay' in key} model_delays = {key:value for key, value in model_data.items() if 'delay' in key} debug.info(1,"Spice Delays={}".format(spice_delays)) debug.info(1,"Model Delays={}".format(model_delays)) + if OPTS.tech_name == "freepdk45": error_tolerance = 0.25 elif OPTS.tech_name == "scn4m_subm": error_tolerance = 0.25 else: self.assertTrue(False) # other techs fail + # Check if no too many or too few results self.assertTrue(len(spice_delays.keys())==len(model_delays.keys())) diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 06a61d55..39f0d5e0 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -54,27 +54,27 @@ class timing_sram_test(openram_test): data.update(port_data[0]) if OPTS.tech_name == "freepdk45": - golden_data = {'delay_hl': [0.22609590000000002], - 'delay_lh': [0.22609590000000002], - 'leakage_power': 0.003317743, + golden_data = {'delay_hl': [0.2264205], + 'delay_lh': [0.2264205], + 'leakage_power': 0.0021017429999999997, 'min_period': 0.859, - 'read0_power': [0.3271056], - 'read1_power': [0.3064244], - 'slew_hl': [0.2153979], - 'slew_lh': [0.2153979], - 'write0_power': [0.3532067], - 'write1_power': [0.3381259]} + 'read0_power': [0.3339161], + 'read1_power': [0.31329440000000003], + 'slew_hl': [0.2590786], + 'slew_lh': [0.2590786], + 'write0_power': [0.36360849999999995], + 'write1_power': [0.3486931]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'delay_hl': [1.709791], - 'delay_lh': [1.709791], - 'leakage_power': 0.06803324999999999, + golden_data = {'delay_hl': [1.7083549999999998], + 'delay_lh': [1.7083549999999998], + 'leakage_power': 0.001119657, 'min_period': 7.812, - 'read0_power': [7.9499070000000005], - 'read1_power': [7.619662999999999], - 'slew_hl': [1.390261], - 'slew_lh': [1.390261], - 'write0_power': [8.913003], - 'write1_power': [8.166687000000001]} + 'read0_power': [8.013845], + 'read1_power': [7.6889389999999995], + 'slew_hl': [1.31918], + 'slew_lh': [1.31918], + 'write0_power': [8.791557000000001], + 'write1_power': [8.70443]} else: self.assertTrue(False) # other techs fail diff --git a/compiler/tests/23_lib_sram_model_corners_test.py b/compiler/tests/23_lib_sram_model_corners_test.py index 51fcbca6..84b54e3d 100755 --- a/compiler/tests/23_lib_sram_model_corners_test.py +++ b/compiler/tests/23_lib_sram_model_corners_test.py @@ -14,7 +14,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 23_lib_sram_model_corners_test") +#@unittest.skip("SKIPPING 23_lib_sram_model_corners_test") class lib_model_corners_lib_test(openram_test): def runTest(self): diff --git a/compiler/tests/23_lib_sram_model_test.py b/compiler/tests/23_lib_sram_model_test.py index fd75dc25..9fdda7e7 100755 --- a/compiler/tests/23_lib_sram_model_test.py +++ b/compiler/tests/23_lib_sram_model_test.py @@ -14,7 +14,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 23_lib_sram_model_test") +#@unittest.skip("SKIPPING 23_lib_sram_model_test") class lib_sram_model_test(openram_test): def runTest(self): diff --git a/compiler/tests/30_openram_front_end_test.py b/compiler/tests/30_openram_front_end_test.py index 2be14001..db75fe0a 100755 --- a/compiler/tests/30_openram_front_end_test.py +++ b/compiler/tests/30_openram_front_end_test.py @@ -16,7 +16,7 @@ from sram_factory import factory import debug import getpass -@unittest.skip("SKIPPING 30_openram_front_end_test") +#@unittest.skip("SKIPPING 30_openram_front_end_test") class openram_front_end_test(openram_test): def runTest(self):