diff --git a/README.md b/README.md index 82fe75ed..d6b948ef 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ The OpenRAM compiler has very few dependencies: If you want to perform DRC and LVS, you will need either: + Calibre (for [FreePDK45]) -+ [Magic] 8.2.79 or higher (for [SCMOS]) ++ [Magic] 8.3.27 or higher (for [SCMOS]) + [Netgen] 1.5 (for [SCMOS]) You must set two environment variables: @@ -81,6 +81,8 @@ We have included the most recent SCN4M_SUBM design rules from [Qflow]. ## Docker Image +**WARNING! Some OpenRAM dependency tools installed in the Docker image are out-of-date.** + We have a pre-configured Ubuntu [Docker](https://www.docker.com/) image available that has all tools installed for the [SCMOS] process. It is available at [docker hub](https://hub.docker.com/r/vlsida/openram-ubuntu). diff --git a/compiler/base/channel_route.py b/compiler/base/channel_route.py index 00ad6e37..5ec955c1 100644 --- a/compiler/base/channel_route.py +++ b/compiler/base/channel_route.py @@ -97,7 +97,7 @@ class channel_route(design.design): """ name = "cr_{0}".format(channel_route.unique_id) channel_route.unique_id += 1 - design.design.__init__(self, name) + super().__init__(name) self.netlist = netlist self.offset = offset diff --git a/compiler/base/contact.py b/compiler/base/contact.py index cc1ca27a..16c671d4 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -34,7 +34,7 @@ class contact(hierarchy_design.hierarchy_design): # This will ignore the name parameter since # we can guarantee a unique name here - hierarchy_design.hierarchy_design.__init__(self, name) + super().__init__(name) debug.info(4, "create contact object {0}".format(name)) self.add_comment("layers: {0}".format(layer_stack)) diff --git a/compiler/base/design.py b/compiler/base/design.py index 2b2d7711..ee985fec 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -20,7 +20,7 @@ class design(hierarchy_design): """ def __init__(self, name): - hierarchy_design.__init__(self, name) + super().__init__(name) self.setup_drc_constants() self.setup_layer_constants() @@ -230,13 +230,14 @@ class design(hierarchy_design): self.write_ports = [] # These are the write-only port indices. self.writeonly_ports = [] - # These are teh read/write and read-only port indice + # These are the read/write and read-only port indices self.read_ports = [] # These are the read-only port indices. self.readonly_ports = [] # These are all the ports self.all_ports = list(range(total_ports)) - + + # The order is always fixed as RW, W, R port_number = 0 for port in range(OPTS.num_rw_ports): self.readwrite_ports.append(port_number) diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 32af7ee9..0ba8e858 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -12,6 +12,8 @@ import debug from vector import vector import tech import math +import copy +import numpy as np from globals import OPTS from utils import round_to_grid @@ -160,7 +162,7 @@ class instance(geometry): """ def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0): """Initializes an instance to represent a module""" - geometry.__init__(self) + super().__init__() debug.check(mirror not in ["R90", "R180", "R270"], "Please use rotation and not mirroring during instantiation.") @@ -269,6 +271,134 @@ class instance(geometry): p.transform(self.offset, self.mirror, self.rotate) new_pins.append(p) return new_pins + + def calculate_transform(self, node): + #set up the rotation matrix + angle = math.radians(float(node.rotate)) + mRotate = np.array([[math.cos(angle),-math.sin(angle),0.0], + [math.sin(angle),math.cos(angle),0.0], + [0.0,0.0,1.0]]) + + #set up translation matrix + translateX = float(node.offset[0]) + translateY = float(node.offset[1]) + mTranslate = np.array([[1.0,0.0,translateX], + [0.0,1.0,translateY], + [0.0,0.0,1.0]]) + + #set up the scale matrix (handles mirror X) + scaleX = 1.0 + if(node.mirror == 'MX'): + scaleY = -1.0 + else: + scaleY = 1.0 + mScale = np.array([[scaleX,0.0,0.0], + [0.0,scaleY,0.0], + [0.0,0.0,1.0]]) + + return (mRotate, mScale, mTranslate) + + def apply_transform(self, mtransforms, uVector, vVector, origin): + origin = np.dot(mtransforms[0], origin) #rotate + uVector = np.dot(mtransforms[0], uVector) #rotate + vVector = np.dot(mtransforms[0], vVector) #rotate + origin = np.dot(mtransforms[1], origin) #scale + uVector = np.dot(mtransforms[1], uVector) #scale + vVector = np.dot(mtransforms[1], vVector) #scale + origin = np.dot(mtransforms[2], origin) + + return(uVector, vVector, origin) + + def apply_path_transform(self, path): + uVector = np.array([[1.0],[0.0],[0.0]]) + vVector = np.array([[0.0],[1.0],[0.0]]) + origin = np.array([[0.0],[0.0],[1.0]]) + + while(path): + instance = path.pop(-1) + mtransforms = self.calculate_transform(instance) + (uVector, vVector, origin) = self.apply_transform(mtransforms, uVector, vVector, origin) + + return (uVector, vVector, origin) + + def reverse_transformation_bitcell(self, cell_name): + path = [] # path currently follwed in bitcell search + cell_paths = [] # saved paths to bitcells + origin_offsets = [] # cell to bank offset + Q_offsets = [] # Q to cell offet + Q_bar_offsets = [] # Q_bar to cell offset + bl_offsets = [] # bl to cell offset + br_offsets = [] # br to cell offset + bl_meta = [] # bl offset metadata (row,col,name) + br_meta = [] #br offset metadata (row,col,name) + + def walk_subtree(node): + path.append(node) + + if node.mod.name == cell_name: + cell_paths.append(copy.copy(path)) + + inst_name = path[-1].name + + # get the row and col names from the path + row = int(path[-1].name.split('_')[-2][1:]) + col = int(path[-1].name.split('_')[-1][1:]) + + cell_bl_meta = [] + cell_br_meta = [] + + normalized_storage_nets = node.mod.get_normalized_storage_nets_offset() + (normalized_bl_offsets, normalized_br_offsets, bl_names, br_names) = node.mod.get_normalized_bitline_offset() + + for offset in range(len(normalized_bl_offsets)): + for port in range(len(bl_names)): + cell_bl_meta.append([bl_names[offset], row, col, port]) + + for offset in range(len(normalized_br_offsets)): + for port in range(len(br_names)): + cell_br_meta.append([br_names[offset], row, col, port]) + + Q_x = normalized_storage_nets[0][0] + Q_y = normalized_storage_nets[0][1] + + Q_bar_x = normalized_storage_nets[1][0] + Q_bar_y = normalized_storage_nets[1][1] + + if node.mirror == 'MX': + Q_y = -1 * Q_y + Q_bar_y = -1 * Q_bar_y + + for pair in range(len(normalized_bl_offsets)): + normalized_bl_offsets[pair] = (normalized_bl_offsets[pair][0], + -1 * normalized_bl_offsets[pair][1]) + + for pair in range(len(normalized_br_offsets)): + normalized_br_offsets[pair] = (normalized_br_offsets[pair][0], + -1 * normalized_br_offsets[pair][1]) + + + Q_offsets.append([Q_x, Q_y]) + Q_bar_offsets.append([Q_bar_x, Q_bar_y]) + + + bl_offsets.append(normalized_bl_offsets) + br_offsets.append(normalized_br_offsets) + + bl_meta.append(cell_bl_meta) + br_meta.append(cell_br_meta) + + elif node.mod.insts is not []: + for instance in node.mod.insts: + walk_subtree(instance) + path.pop(-1) + + walk_subtree(self) + for path in cell_paths: + vector_spaces = self.apply_path_transform(path) + origin = vector_spaces[2] + origin_offsets.append([origin[0], origin[1]]) + + return(origin_offsets, Q_offsets, Q_bar_offsets, bl_offsets, br_offsets, bl_meta, br_meta) def __str__(self): """ override print function output """ @@ -284,7 +414,7 @@ class path(geometry): def __init__(self, lpp, coordinates, path_width): """Initializes a path for the specified layer""" - geometry.__init__(self) + super().__init__() self.name = "path" self.layerNumber = lpp[0] self.layerPurpose = lpp[1] @@ -322,7 +452,7 @@ class label(geometry): def __init__(self, text, lpp, offset, zoom=-1): """Initializes a text label for specified layer""" - geometry.__init__(self) + super().__init__() self.name = "label" self.text = text self.layerNumber = lpp[0] @@ -366,7 +496,7 @@ class rectangle(geometry): def __init__(self, lpp, offset, width, height): """Initializes a rectangular shape for specified layer""" - geometry.__init__(self) + super().__init__() self.name = "rect" self.layerNumber = lpp[0] self.layerPurpose = lpp[1] diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 22826054..d350c156 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1324,6 +1324,8 @@ class layout(): pdf.drawLayout() pdf.writeToFile(pdf_name) + + def print_attr(self): """Prints a list of attributes for the current layout object""" debug.info(0, diff --git a/compiler/base/route.py b/compiler/base/route.py index c3e446a3..4e3d8a60 100644 --- a/compiler/base/route.py +++ b/compiler/base/route.py @@ -30,7 +30,7 @@ class route(design): def __init__(self, obj, layer_stack, path, layer_widths=[None,1,None]): name = "route_{0}".format(route.unique_route_id) route.unique_route_id += 1 - design.__init__(self, name) + super().__init__(name) debug.info(3, "create route obj {0}".format(name)) self.obj = obj diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index 38cf5ae6..a8e48de6 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -33,7 +33,7 @@ class bitcell(bitcell_base.bitcell_base): props.bitcell.cell_6t.pin.vdd, props.bitcell.cell_6t.pin.gnd] type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] - storage_nets = ['Q', 'Qbar'] + storage_nets = ['Q', 'Q_bar'] (width, height) = utils.get_libcell_size("cell_6t", GDS["unit"], diff --git a/compiler/bitcells/bitcell_base.py b/compiler/bitcells/bitcell_base.py index 5265904b..690e98fa 100644 --- a/compiler/bitcells/bitcell_base.py +++ b/compiler/bitcells/bitcell_base.py @@ -8,8 +8,9 @@ import debug import design +from globals import OPTS import logical_effort -from tech import parameter, drc +from tech import parameter, drc, layer class bitcell_base(design.design): @@ -78,7 +79,85 @@ class bitcell_base(design.design): fmt_str = "Storage nodes={} not found in spice file." debug.info(1, fmt_str.format(self.storage_nets)) return None - + + def get_storage_net_offset(self): + """ + Gets the location of the storage net labels to add top level + labels for pex simulation. + """ + # If we generated the bitcell, we already know where Q and Q_bar are + if OPTS.bitcell is not "pbitcell": + self.storage_net_offsets = [] + for i in range(len(self.get_storage_net_names())): + for text in self.gds.getTexts(layer["m1"]): + if self.storage_nets[i] == text.textString.rstrip('\x00'): + self.storage_net_offsets.append(text.coordinates[0]) + + for i in range(len(self.storage_net_offsets)): + self.storage_net_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.storage_net_offsets[i]]) + + + return(self.storage_net_offsets) + + def get_bitline_offset(self): + + bl_names = self.get_all_bl_names() + br_names = self.get_all_br_names() + + found_bl = [] + found_br = [] + + self.bl_offsets = [] + self.br_offsets = [] + + for i in range(len(bl_names)): + for text in self.gds.getTexts(layer["m2"]): + if not bl_names[i] in found_bl: + if bl_names[i] == text.textString.rstrip('\x00'): + self.bl_offsets.append(text.coordinates[0]) + found_bl.append(bl_names[i]) + + continue + + for i in range(len(br_names)): + for text in self.gds.getTexts(layer["m2"]): + if not br_names[i] in found_br: + if br_names[i] == text.textString.rstrip('\x00'): + self.br_offsets.append(text.coordinates[0]) + found_br.append(br_names[i]) + continue + + for i in range(len(self.bl_offsets)): + self.bl_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.bl_offsets[i]]) + + for i in range(len(self.br_offsets)): + self.br_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.br_offsets[i]]) + + return(self.bl_offsets, self.br_offsets, found_bl, found_br) + + def get_normalized_storage_nets_offset(self): + """ + Convert storage net offset to be relative to the bottom left corner + of the bitcell. This is useful for making sense of offsets outside + of the bitcell. + """ + if OPTS.bitcell is not "pbitcell": + normalized_storage_net_offset = self.get_storage_net_offset() + + else: + net_offset = self.get_storage_net_offset() + Q_x = net_offset[0][0] - self.leftmost_xpos + Q_y = net_offset[0][1] - self.botmost_ypos + Q_bar_x = net_offset[1][0] - self.leftmost_xpos + Q_bar_y = net_offset[1][1] - self.botmost_ypos + + normalized_storage_net_offset = [[Q_x,Q_y],[Q_bar_x,Q_bar_y]] + + return normalized_storage_net_offset + + def get_normalized_bitline_offset(self): + return self.get_bitline_offset() + def build_graph(self, graph, inst_name, port_nets): """ By default, bitcells won't be part of the graph. diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index aea7a6dc..25868df5 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -26,7 +26,7 @@ class pbitcell(bitcell_base.bitcell_base): self.num_w_ports = OPTS.num_w_ports self.num_r_ports = OPTS.num_r_ports self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports - + self.replica_bitcell = replica_bitcell self.dummy_bitcell = dummy_bitcell @@ -152,7 +152,7 @@ class pbitcell(bitcell_base.bitcell_base): self.Q_bar = "Q_bar" self.Q = "Q" self.storage_nets = [self.Q, self.Q_bar] - + def add_modules(self): """ Determine size of transistors and add ptx modules """ # if there are any read/write ports, @@ -352,6 +352,11 @@ class pbitcell(bitcell_base.bitcell_base): self.right_building_edge = right_inverter_xpos \ + self.inverter_nmos.active_width + def add_pex_labels(self, left_inverter_offset, right_inverter_offset): + self.add_label("Q", "metal1", left_inverter_offset) + self.add_label("Q_bar", "metal1", right_inverter_offset) + self.storage_net_offsets = [left_inverter_offset, right_inverter_offset] + def route_storage(self): """ Routes inputs and outputs of inverters to cross couple them """ # connect input (gate) of inverters @@ -396,6 +401,16 @@ class pbitcell(bitcell_base.bitcell_base): gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").rc().x, contact_offset_right.y) self.add_path("poly", [contact_offset_right, gate_offset_left]) + if OPTS.use_pex: + # add labels to cross couple inverter for extracted simulation + contact_offset_left_output = vector(self.inverter_nmos_left.get_pin("D").rc().x \ + + 0.5 * contact.poly.height, + self.cross_couple_upper_ypos) + + contact_offset_right_output = vector(self.inverter_nmos_right.get_pin("S").lc().x \ + - 0.5*contact.poly.height, + self.cross_couple_lower_ypos) + self.add_pex_labels(contact_offset_left_output, contact_offset_right_output) def route_rails(self): """ Adds gnd and vdd rails and connects them to the inverters """ diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index b7faaec3..da1b8147 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -41,7 +41,7 @@ class delay(simulation): """ def __init__(self, sram, spfile, corner): - simulation.__init__(self, sram, spfile, corner) + super().__init__(sram, spfile, corner) self.targ_read_ports = [] self.targ_write_ports = [] @@ -136,19 +136,18 @@ class delay(simulation): """ self.bitline_volt_meas = [] - self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ZERO", - self.bl_name)) + self.bl_name)) self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ZERO", - self.br_name)) + self.br_name)) self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ONE", - self.bl_name)) + self.bl_name)) self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ONE", - self.br_name)) + self.br_name)) self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE return self.bitline_volt_meas @@ -181,13 +180,16 @@ class delay(simulation): self.dout_volt_meas.append(voltage_at_measure("v_{}".format(meas.name), meas.targ_name_no_port)) self.dout_volt_meas[-1].meta_str = meas.meta_str - - self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name+"{}", "FALL", "RISE", measure_scale=1e9) + + if not OPTS.use_pex: + self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name+"{}", "FALL", "RISE", measure_scale=1e9) + else: + self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name, "FALL", "RISE", measure_scale=1e9) + self.sen_meas.meta_str = sram_op.READ_ZERO self.sen_meas.meta_add_delay = True - self.dout_volt_meas.append(self.sen_meas) - - return self.dout_volt_meas + + return self.dout_volt_meas + [self.sen_meas] def create_read_bit_measures(self): """ Adds bit measurements for read0 and read1 cycles """ @@ -229,8 +231,13 @@ class delay(simulation): storage_names = cell_inst.mod.get_storage_net_names() debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes" "supported for characterization. Storage nets={}").format(storage_names)) - q_name = cell_name+'.'+str(storage_names[0]) - qbar_name = cell_name+'.'+str(storage_names[1]) + if not OPTS.use_pex: + q_name = cell_name+'.'+str(storage_names[0]) + qbar_name = cell_name+'.'+str(storage_names[1]) + else: + bank_num = self.sram.get_bank_num(self.sram.name, bit_row, bit_col) + q_name = "bitcell_Q_b{0}_r{1}_c{2}".format(bank_num, bit_row, bit_col) + qbar_name = "bitcell_Q_bar_b{0}_r{1}_c{2}".format(bank_num, bit_row, bit_col) # Bit measures, measurements times to be defined later. The measurement names must be unique # but they is enforced externally. {} added to names to differentiate between ports allow the @@ -272,37 +279,50 @@ class delay(simulation): """Sets important names for characterization such as Sense amp enable and internal bit nets.""" port = self.read_ports[0] - self.graph.get_all_paths('{}{}'.format("clk", port), - '{}{}_{}'.format(self.dout_name, port, self.probe_data)) - - sen_with_port = self.get_sen_name(self.graph.all_paths) - if sen_with_port.endswith(str(port)): - self.sen_name = sen_with_port[:-len(str(port))] - else: - self.sen_name = sen_with_port - debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.") + if not OPTS.use_pex: + self.graph.get_all_paths('{}{}'.format("clk", port), + '{}{}_{}'.format(self.dout_name, port, self.probe_data)) - debug.info(2,"s_en name = {}".format(self.sen_name)) - - bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port) - port_pos = -1-len(str(self.probe_data))-len(str(port)) - - if bl_name_port.endswith(str(port)+"_"+str(self.probe_data)): - self.bl_name = bl_name_port[:port_pos] +"{}"+ bl_name_port[port_pos+len(str(port)):] - elif not bl_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0 - self.bl_name = bl_name_port - else: - self.bl_name = bl_name_port - debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.") + sen_with_port = self.get_sen_name(self.graph.all_paths) + if sen_with_port.endswith(str(port)): + self.sen_name = sen_with_port[:-len(str(port))] + else: + self.sen_name = sen_with_port + debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.") + + debug.info(2,"s_en name = {}".format(self.sen_name)) - if br_name_port.endswith(str(port)+"_"+str(self.probe_data)): - self.br_name = br_name_port[:port_pos] +"{}"+ br_name_port[port_pos+len(str(port)):] - elif not br_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0 - self.br_name = br_name_port + bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port) + port_pos = -1-len(str(self.probe_data))-len(str(port)) + + if bl_name_port.endswith(str(port)+"_"+str(self.probe_data)): + self.bl_name = bl_name_port[:port_pos] +"{}"+ bl_name_port[port_pos+len(str(port)):] + elif not bl_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0 + self.bl_name = bl_name_port + else: + self.bl_name = bl_name_port + debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.") + + if br_name_port.endswith(str(port)+"_"+str(self.probe_data)): + self.br_name = br_name_port[:port_pos] +"{}"+ br_name_port[port_pos+len(str(port)):] + elif not br_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0 + self.br_name = br_name_port + else: + self.br_name = br_name_port + debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.") + debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name)) else: - self.br_name = br_name_port - debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.") - debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name)) + self.graph.get_all_paths('{}{}'.format("clk", port), + '{}{}_{}'.format(self.dout_name, port, self.probe_data)) + + self.sen_name = self.get_sen_name(self.graph.all_paths) + debug.info(2,"s_en name = {}".format(self.sen_name)) + + + self.bl_name = "bl{0}_{1}".format(port, OPTS.word_size-1) + self.br_name = "br{0}_{1}".format(port, OPTS.word_size-1) + debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name)) + def get_sen_name(self, paths, assumed_port=None): """ @@ -316,7 +336,8 @@ class delay(simulation): debug.check(len(sa_mods) == 1, "Only expected one type of Sense Amp. Cannot perform s_en checks.") enable_name = sa_mods[0].get_enable_name() sen_name = self.get_alias_in_path(paths, enable_name, sa_mods[0]) - + if OPTS.use_pex: + sen_name = sen_name.split('.')[-1] return sen_name def get_bl_name(self, paths, port): @@ -332,7 +353,9 @@ class delay(simulation): exclude_set = self.get_bl_name_search_exclusions() for int_net in [cell_bl, cell_br]: bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set)) - + if OPTS.use_pex: + for i in range(len(bl_names)): + bl_names[i] = bl_names[i].split('.')[-1] return bl_names[0], bl_names[1] @@ -423,8 +446,13 @@ class delay(simulation): # instantiate the sram self.sf.write("\n* Instantiation of the SRAM\n") - self.stim.inst_model(pins=self.pins, - model_name=self.sram.name) + if not OPTS.use_pex: + self.stim.inst_model(pins=self.pins, + model_name=self.sram.name) + else: + self.stim.inst_sram_pex(pins=self.pins, + model_name=self.sram.name) + self.sf.write("\n* SRAM output loads\n") for port in self.read_ports: for i in range(self.word_size): @@ -1351,7 +1379,7 @@ class delay(simulation): 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.") + debug.warning("In analytical mode, all ports have the timing of the first read port.") # Probe set to 0th bit, does not matter for analytical delay. self.set_probe('0'*self.addr_size, 0) diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index 2c391e38..8574c4f6 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -24,7 +24,7 @@ class functional(simulation): """ def __init__(self, sram, spfile, corner): - simulation.__init__(self, sram, spfile, corner) + super().__init__(sram, spfile, corner) # Seed the characterizer with a constant seed for unit tests if OPTS.is_unit_test: diff --git a/compiler/characterizer/measurements.py b/compiler/characterizer/measurements.py index 54f9973f..31da8ac5 100644 --- a/compiler/characterizer/measurements.py +++ b/compiler/characterizer/measurements.py @@ -198,4 +198,4 @@ class voltage_at_measure(spice_measurement): meas_name = self.name targ_name = self.targ_name_no_port return (meas_name,targ_name,time_at) - \ No newline at end of file + diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index b35c3c47..52100001 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -26,7 +26,7 @@ class model_check(delay): """ def __init__(self, sram, spfile, corner, custom_delaychain=False): - delay.__init__(self,sram,spfile,corner) + super().__init__(sram, spfile, corner) self.period = tech.spice["feasible_period"] self.create_data_names() self.custom_delaychain=custom_delaychain @@ -446,4 +446,4 @@ class model_check(delay): return name_dict - \ No newline at end of file + diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 11dc449a..90fd6213 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -56,7 +56,30 @@ class stimuli(): for pin in pins: self.sf.write("{0} ".format(pin)) self.sf.write("{0}\n".format(model_name)) + + def inst_sram_pex(self, pins, model_name): + self.sf.write("X{0} ".format(model_name)) + for pin in pins: + self.sf.write("{0} ".format(pin)) + for bank in range(OPTS.num_banks): + row = int(OPTS.num_words / OPTS.words_per_row) - 1 + col = int(OPTS.word_size * OPTS.words_per_row) - 1 + self.sf.write("bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col)) + self.sf.write("bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col)) + # can't add all bitcells to top level due to ngspice max port count of 1005 + # for row in range(int(OPTS.num_words / OPTS.words_per_row)): + # for col in range(int(OPTS.word_size * OPTS.words_per_row)): + # self.sf.write("bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col)) + # self.sf.write("bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col)) + for bank in range(OPTS.num_banks): + for col in range(OPTS.word_size * OPTS.words_per_row): + for port in range(OPTS.num_r_ports + OPTS.num_w_ports + OPTS.num_rw_ports): + self.sf.write("bl{0}_{1} ".format(port, col)) + self.sf.write("br{0}_{1} ".format(port, col)) + + self.sf.write("s_en{0} ".format(bank)) + self.sf.write("{0}\n".format(model_name)) def create_inverter(self, size=1, beta=2.5): """ Generates inverter for the top level signals (only for sim purposes) """ diff --git a/compiler/example_configs/example_config_1w_1r_scn4m_subm.py b/compiler/example_configs/example_config_1w_1r_scn4m_subm.py index 55ac4016..7698f1a3 100644 --- a/compiler/example_configs/example_config_1w_1r_scn4m_subm.py +++ b/compiler/example_configs/example_config_1w_1r_scn4m_subm.py @@ -1,9 +1,9 @@ word_size = 2 num_words = 16 -num_rw_ports = 1 +num_rw_ports = 0 num_r_ports = 1 -num_w_ports = 0 +num_w_ports = 1 tech_name = "scn4m_subm" nominal_corners_only = False diff --git a/compiler/globals.py b/compiler/globals.py index a192ebc9..dd4ac177 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -19,7 +19,7 @@ import re import copy import importlib -VERSION = "1.1.5" +VERSION = "1.1.6" NAME = "OpenRAM v{}".format(VERSION) USAGE = "openram.py [options] \nUse -h for help.\n" diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index b0707edb..d4f40485 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -36,7 +36,7 @@ class bank(design.design): if name == "": name = "bank_{0}_{1}".format(self.word_size, self.num_words) - design.design.__init__(self, name) + super().__init__(name) debug.info(2, "create sram of size {0} with {1} words".format(self.word_size, self.num_words)) @@ -74,7 +74,6 @@ class bank(design.design): self.bank_array_ll = self.offset_all_coordinates().scale(-1, -1) self.bank_array_ur = self.bitcell_array_inst.ur() self.bank_array_ul = self.bitcell_array_inst.ul() - self.DRC_LVS() def add_pins(self): @@ -83,7 +82,7 @@ class bank(design.design): for bit in range(self.word_size + self.num_spare_cols): self.add_pin("dout{0}_{1}".format(port, bit), "OUTPUT") for port in self.all_ports: - self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]), "OUTPUT") + self.add_pin_list(self.bitcell_array.get_rbl_bitline_names(port), "OUTPUT") for port in self.write_ports: for bit in range(self.word_size + self.num_spare_cols): self.add_pin("din{0}_{1}".format(port, bit), "INPUT") @@ -114,6 +113,8 @@ class bank(design.design): def route_layout(self): """ Create routing amoung the modules """ self.route_central_bus() + + self.route_unused_wordlines() for port in self.all_ports: self.route_bitlines(port) @@ -255,7 +256,7 @@ class bank(design.design): self.port_data_offsets[port] = vector(self.main_bitcell_array_left, self.bitcell_array_top) # LOWER RIGHT QUADRANT - # To the left of the bitcell array + # To the right of the bitcell array x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap self.port_address_offsets[port] = vector(x_offset, self.main_bitcell_array_bottom) @@ -375,7 +376,6 @@ class bank(design.design): rows=self.num_rows) self.add_mod(self.port_address) - self.port_rbl_map = self.all_ports self.num_rbl = len(self.all_ports) self.bitcell_array = factory.create(module_type="replica_bitcell_array", @@ -392,24 +392,28 @@ class bank(design.design): def create_bitcell_array(self): """ Creating Bitcell Array """ - self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array", mod=self.bitcell_array) - temp = [] - for col in range(self.num_cols + self.num_spare_cols): - for bitline in self.bitline_names: - temp.append("{0}_{1}".format(bitline, col)) - for rbl in range(self.num_rbl): - rbl_bl_name=self.bitcell_array.get_rbl_bl_name(rbl) - temp.append(rbl_bl_name) - rbl_br_name=self.bitcell_array.get_rbl_br_name(rbl) - temp.append(rbl_br_name) - for row in range(self.num_rows): - for wordline in self.wl_names: - temp.append("{0}_{1}".format(wordline, row)) + rbl_names = self.bitcell_array.get_rbl_bitline_names() + temp.extend(rbl_names) + bitline_names = self.bitcell_array.get_bitline_names() + temp.extend(bitline_names) + # Replace RBL wordline with wl_en# + wordline_names = self.bitcell_array.get_wordline_names() + + rbl_wl_names = [] for port in self.all_ports: - temp.append("wl_en{0}".format(port)) + rbl_wl_names.append(self.bitcell_array.get_rbl_wordline_names(port)) + + # Rename the RBL WL to the enable name + for port in self.all_ports: + wordline_names = [x.replace(rbl_wl_names[port], "wl_en{0}".format(port)) for x in wordline_names] + # Connect the other RBL WL to gnd + wordline_names = ["gnd" if x.startswith("rbl_wl") else x for x in wordline_names] + # Connect the dummy WL to gnd + wordline_names = ["gnd" if x.startswith("dummy") else x for x in wordline_names] + temp.extend(wordline_names) temp.append("vdd") temp.append("gnd") self.connect_inst(temp) @@ -420,17 +424,14 @@ class bank(design.design): def create_port_data(self): """ Creating Port Data """ - self.port_data_inst = [None] * len(self.all_ports) for port in self.all_ports: self.port_data_inst[port]=self.add_inst(name="port_data{}".format(port), mod=self.port_data[port]) temp = [] - 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_names = self.bitcell_array.get_rbl_bitline_names(port) + temp.extend(rbl_bl_names) for col in range(self.num_cols + self.num_spare_cols): temp.append("{0}_{1}".format(self.bl_names[port], col)) temp.append("{0}_{1}".format(self.br_names[port], col)) @@ -453,7 +454,6 @@ class bank(design.design): for bit in range(self.num_spare_cols): temp.append("bank_spare_wen{0}_{1}".format(port, bit)) temp.extend(["vdd", "gnd"]) - self.connect_inst(temp) def place_port_data(self, offsets): @@ -478,7 +478,7 @@ class bank(design.design): temp = [] for bit in range(self.row_addr_size): temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size)) - temp.append("wl_en{0}".format(port)) + temp.append("wl_en{}".format(port)) for row in range(self.num_rows): temp.append("{0}_{1}".format(self.wl_names[port], row)) temp.extend(["vdd", "gnd"]) @@ -666,7 +666,7 @@ class bank(design.design): # Port 0 # The bank is at (0,0), so this is to the left of the y-axis. # 2 pitches on the right for vias/jogs to access the inputs - control_bus_offset = vector(-self.m3_pitch * self.num_control_lines[0] - self.m3_pitch, self.min_y_offset) + control_bus_offset = vector(-self.m3_pitch * self.num_control_lines[0] - 2 * self.m3_pitch, self.min_y_offset) # The control bus is routed up to two pitches below the bitcell array control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2 * self.m1_pitch self.bus_pins[0] = self.create_bus(layer="m2", @@ -681,7 +681,7 @@ class bank(design.design): if len(self.all_ports)==2: # The other control bus is routed up to two pitches above the bitcell array control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2 * self.m1_pitch - control_bus_offset = vector(self.bitcell_array_right + self.m3_pitch, + control_bus_offset = vector(self.bitcell_array_right + 2.5 * self.m3_pitch, self.max_y_offset - control_bus_length) # The bus for the right port is reversed so that the rbl_wl is closest to the array self.bus_pins[1] = self.create_bus(layer="m2", @@ -714,14 +714,13 @@ class bank(design.design): # connect spare bitlines for i in range(self.num_spare_cols): - self.connect_bitline(inst1, inst2, inst1_bl_name.format(self.num_cols+i), "spare" + inst2_bl_name.format(i)) - self.connect_bitline(inst1, inst2, inst1_br_name.format(self.num_cols+i), "spare" + inst2_br_name.format(i)) + self.connect_bitline(inst1, inst2, inst1_bl_name.format(self.num_cols + i), "spare" + inst2_bl_name.format(i)) + self.connect_bitline(inst1, inst2, inst1_br_name.format(self.num_cols + i), "spare" + inst2_br_name.format(i)) # Connect the replica bitlines - 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_names = self.bitcell_array.get_rbl_bitline_names(port) + for (array_name, data_name) in zip(rbl_bl_names, ["rbl_bl", "rbl_br"]): + self.connect_bitline(inst1, inst2, array_name, data_name) def route_port_data_out(self, port): """ Add pins for the port data out """ @@ -962,7 +961,35 @@ class bank(design.design): self.add_label(text=data_name, layer="m1", offset=data_pin.center()) - + + def route_unused_wordlines(self): + """ Connect the unused RBL and dummy wordlines to gnd """ + gnd_wl_names = [] + + # Connect unused RBL WL to gnd + array_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("rbl")]) + dummy_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("dummy")]) + rbl_wl_names = set([self.bitcell_array.get_rbl_wordline_names(x) for x in self.all_ports]) + + gnd_wl_names = list((array_rbl_names - rbl_wl_names) | dummy_rbl_names) + + for wl_name in gnd_wl_names: + pin = self.bitcell_array_inst.get_pin(wl_name) + pin_layer = pin.layer + layer_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer)) + left_pin_loc = pin.lc() + right_pin_loc = pin.rc() + + # Place the pins a track outside of the array + left_loc = left_pin_loc - vector(layer_pitch, 0) + right_loc = right_pin_loc + vector(layer_pitch, 0) + self.add_power_pin("gnd", left_loc, directions=("H", "H")) + self.add_power_pin("gnd", right_loc, directions=("H", "H")) + + # Add a path to connect to the array + self.add_path(pin_layer, [left_loc, left_pin_loc]) + self.add_path(pin_layer, [right_loc, right_pin_loc]) + def route_control_lines(self, port): """ Route the control lines of the entire bank """ @@ -974,10 +1001,10 @@ class bank(design.design): connection.append((self.prefix + "p_en_bar{}".format(port), self.port_data_inst[port].get_pin("p_en_bar"))) - rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port]) + rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port) connection.append((self.prefix + "wl_en{}".format(port), self.bitcell_array_inst.get_pin(rbl_wl_name))) - + if port in self.write_ports: connection.append((self.prefix + "w_en{}".format(port), self.port_data_inst[port].get_pin("w_en"))) @@ -1012,7 +1039,7 @@ class bank(design.design): self.add_wire(self.m1_stack, [pin_pos, mid_pos, control_pos]) self.add_via_center(layers=self.m1_stack, offset=control_pos) - + 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. @@ -1023,7 +1050,7 @@ class bank(design.design): inp_is_rise) return stage_effort_list - + def get_wl_en_cin(self): """Get the relative capacitance of all the clk connections in the bank""" # wl_en only used in the wordline driver. diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index b6246268..8a776d30 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -24,7 +24,7 @@ class bank_select(design.design): """ def __init__(self, name="bank_select", port="rw"): - design.design.__init__(self, name) + super().__init__(name) self.port = port diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 97b681e9..81f1062f 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -13,12 +13,11 @@ from sram_factory import factory class bitcell_array(bitcell_base_array): """ - Creates a rows x cols array of memory cells. Assumes bit-lines - and word line is connected by abutment. - Connects the word lines and bit lines. + Creates a rows x cols array of memory cells. + Assumes bit-lines and word lines are connected by abutment. """ - def __init__(self, cols, rows, name, column_offset=0): - super().__init__(cols, rows, name, column_offset) + def __init__(self, rows, cols, column_offset=0, name=""): + super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.create_netlist() if not OPTS.netlist_only: @@ -27,7 +26,7 @@ class bitcell_array(bitcell_base_array): # We don't offset this because we need to align # the replica bitcell in the control logic # self.offset_all_coordinates() - + def create_netlist(self): """ Create and connect the netlist """ self.add_modules() @@ -41,7 +40,7 @@ class bitcell_array(bitcell_base_array): self.add_layout_pins() self.add_boundary() - + self.DRC_LVS() def add_modules(self): @@ -57,21 +56,21 @@ class bitcell_array(bitcell_base_array): name = "bit_r{0}_c{1}".format(row, col) self.cell_inst[row, col]=self.add_inst(name=name, mod=self.cell) - self.connect_inst(self.get_bitcell_pins(col, row)) - + self.connect_inst(self.get_bitcell_pins(row, col)) + def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" - + # Dynamic Power from Bitline bl_wire = self.gen_bl_wire() cell_load = 2 * bl_wire.return_input_cap() bl_swing = OPTS.rbl_delay_percentage freq = spice["default_event_frequency"] bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) - + # Calculate the bitcell power which currently only includes leakage cell_power = self.cell.analytical_power(corner, load) - + # Leakage power grows with entire array and bitlines. total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size, cell_power.leakage * self.column_size * self.row_size) @@ -83,7 +82,8 @@ class bitcell_array(bitcell_base_array): else: width = self.width wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_m1")) - wl_wire.wire_c = 2 * spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell + # 2 access tx gate per cell + wl_wire.wire_c = 2 * spice["min_tx_gate_c"] + wl_wire.wire_c return wl_wire def gen_bl_wire(self): @@ -93,7 +93,8 @@ class bitcell_array(bitcell_base_array): height = self.height bl_pos = 0 bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1")) - bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell + # 1 access tx d/s per cell + bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c return bl_wire def get_wordline_cin(self): @@ -102,7 +103,7 @@ class bitcell_array(bitcell_base_array): bitcell_wl_cin = self.cell.get_wl_cin() total_cin = bitcell_wl_cin * self.column_size return total_cin - + def graph_exclude_bits(self, targ_row, targ_col): """Excludes bits in column from being added to graph except target""" # Function is not robust with column mux configurations @@ -111,7 +112,7 @@ class bitcell_array(bitcell_base_array): if row == targ_row and col == targ_col: continue self.graph_inst_exclude.add(self.cell_inst[row, col]) - + def get_cell_name(self, inst_name, row, col): """Gets the spice name of the target bitcell.""" return inst_name + '.x' + self.cell_inst[row, col].name, self.cell_inst[row, col] diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 7d241b4d..292831b5 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -8,14 +8,15 @@ import debug import design from tech import cell_properties +from sram_factory import factory class bitcell_base_array(design.design): """ - Abstract base class for bitcell-arrays -- bitcell, dummy + Abstract base class for bitcell-arrays -- bitcell, dummy, replica """ - def __init__(self, cols, rows, name, column_offset): - design.design.__init__(self, name) + def __init__(self, name, rows, cols, column_offset): + super().__init__(name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) @@ -23,56 +24,52 @@ class bitcell_base_array(design.design): self.row_size = rows self.column_offset = column_offset - def get_all_bitline_names(self): + # Bitcell for port names only + self.cell = factory.create(module_type="bitcell") + + self.create_all_bitline_names() + self.create_all_wordline_names() - res = list() + def get_all_bitline_names(self, prefix=""): + return [prefix + x for x in self.bitline_names] + + def create_all_bitline_names(self): + self.bitline_names = list() bitline_names = self.cell.get_all_bitline_names() - # We have to keep the order of self.pins, otherwise we connect - # it wrong in the spice netlist - for pin in self.pins: - for bl_name in bitline_names: - if bl_name in pin: - res.append(pin) - return res + for col in range(self.column_size): + for cell_column in bitline_names: + self.bitline_names.append("{0}_{1}".format(cell_column, col)) - def get_all_wordline_names(self): + def get_all_wordline_names(self, prefix=""): + return [prefix + x for x in self.wordline_names] - res = list() + def create_all_wordline_names(self): + + self.wordline_names = list() wordline_names = self.cell.get_all_wl_names() - # We have to keep the order of self.pins, otherwise we connect - # it wrong in the spice netlist - for pin in self.pins: - for wl_name in wordline_names: - if wl_name in pin: - res.append(pin) - return res + for row in range(self.row_size): + for cell_row in wordline_names: + self.wordline_names.append("{0}_{1}".format(cell_row, row)) def add_pins(self): - row_list = self.cell.get_all_wl_names() - column_list = self.cell.get_all_bitline_names() - for col in range(self.column_size): - for cell_column in column_list: - self.add_pin(cell_column+"_{0}".format(col), "INOUT") - for row in range(self.row_size): - for cell_row in row_list: - self.add_pin(cell_row+"_{0}".format(row), "INPUT") + for bl_name in self.bitline_names: + self.add_pin(bl_name, "INOUT") + for wl_name in self.wordline_names: + self.add_pin(wl_name, "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - def get_bitcell_pins(self, col, row): + def get_bitcell_pins(self, row, col): """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ bitcell_pins = [] - - pin_names = self.cell.get_all_bitline_names() - for pin in pin_names: - bitcell_pins.append(pin + "_{0}".format(col)) - pin_names = self.cell.get_all_wl_names() - for pin in pin_names: - bitcell_pins.append(pin + "_{0}".format(row)) + # bitlines + bitcell_pins.extend([x for x in self.bitline_names if x.endswith("_{0}".format(col))]) + # wordlines + bitcell_pins.extend([x for x in self.wordline_names if x.endswith("_{0}".format(row))]) bitcell_pins.append("vdd") bitcell_pins.append("gnd") @@ -81,22 +78,21 @@ class bitcell_base_array(design.design): def add_layout_pins(self): """ Add the layout pins """ - row_list = self.cell.get_all_wl_names() - column_list = self.cell.get_all_bitline_names() - + bitline_names = self.cell.get_all_bitline_names() for col in range(self.column_size): - for cell_column in column_list: - bl_pin = self.cell_inst[0, col].get_pin(cell_column) - self.add_layout_pin(text=cell_column + "_{0}".format(col), + for bl_name in bitline_names: + bl_pin = self.cell_inst[0, col].get_pin(bl_name) + self.add_layout_pin(text="{0}_{1}".format(bl_name, col), layer=bl_pin.layer, offset=bl_pin.ll().scale(1, 0), width=bl_pin.width(), height=self.height) + wl_names = self.cell.get_all_wl_names() for row in range(self.row_size): - for cell_row in row_list: - wl_pin = self.cell_inst[row, 0].get_pin(cell_row) - self.add_layout_pin(text=cell_row + "_{0}".format(row), + for wl_name in wl_names: + wl_pin = self.cell_inst[row, 0].get_pin(wl_name) + self.add_layout_pin(text="{0}_{1}".format(wl_name, row), layer=wl_pin.layer, offset=wl_pin.ll().scale(0, 1), width=self.width, diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index ee9302d8..a2bf8379 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -13,8 +13,8 @@ class col_cap_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. """ - def __init__(self, cols, rows, column_offset=0, mirror=0, name=""): - super().__init__(cols, rows, name, column_offset) + def __init__(self, rows, cols, column_offset=0, mirror=0, name=""): + super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.mirror = mirror self.no_instances = True @@ -50,9 +50,9 @@ class col_cap_array(bitcell_base_array): name = "bit_r{0}_c{1}".format(row, col) self.cell_inst[row, col]=self.add_inst(name=name, mod=self.dummy_cell) - self.connect_inst(self.get_bitcell_pins(col, row)) + self.connect_inst(self.get_bitcell_pins(row, col)) - def get_bitcell_pins(self, col, row): + def get_bitcell_pins(self, row, col): """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index a4e26a6f..706888de 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -23,7 +23,7 @@ class control_logic(design.design): def __init__(self, num_rows, words_per_row, word_size, spare_columns=None, sram=None, port_type="rw", name=""): """ Constructor """ name = "control_logic_" + port_type - design.design.__init__(self, name) + super().__init__(name) debug.info(1, "Creating {}".format(name)) self.add_comment("num_rows: {0}".format(num_rows)) self.add_comment("words_per_row: {0}".format(words_per_row)) @@ -869,7 +869,6 @@ class control_logic(design.design): offset=pin.ll(), height=pin.height(), width=pin.width()) - def get_delays_to_wl(self): """Get the delay (in delay units) of the clk to a wordline in the bitcell array""" debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index c261138a..246299c1 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -21,7 +21,7 @@ class delay_chain(design.design): def __init__(self, name, fanout_list): """init function""" - design.design.__init__(self, name) + super().__init__(name) debug.info(1, "creating delay chain {0}".format(str(fanout_list))) self.add_comment("fanouts: {0}".format(str(fanout_list))) diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index d3f9b68e..c4f85a6d 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -24,7 +24,7 @@ class dff_array(design.design): if name=="": name = "dff_array_{0}x{1}".format(rows, columns) - design.design.__init__(self, name) + super().__init__(name) debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns)) self.add_comment("rows: {0} cols: {1}".format(rows, columns)) diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index a1e54a4d..1657d7a8 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -27,7 +27,7 @@ class dff_buf(design.design): if name=="": name = "dff_buf_{0}".format(dff_buf.unique_id) dff_buf.unique_id += 1 - design.design.__init__(self, name) + super().__init__(name) debug.info(1, "Creating {}".format(self.name)) self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size)) diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 1cbd9284..88852d45 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -27,7 +27,7 @@ class dff_buf_array(design.design): if name=="": name = "dff_buf_array_{0}x{1}_{2}".format(rows, columns, dff_buf_array.unique_id) dff_buf_array.unique_id += 1 - design.design.__init__(self, name) + super().__init__(name) debug.info(1, "Creating {}".format(self.name)) self.add_comment("rows: {0} cols: {1}".format(rows, columns)) self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size)) diff --git a/compiler/modules/dff_inv.py b/compiler/modules/dff_inv.py index 9dcb84c5..033312ef 100644 --- a/compiler/modules/dff_inv.py +++ b/compiler/modules/dff_inv.py @@ -25,7 +25,7 @@ class dff_inv(design.design): if name=="": name = "dff_inv_{0}".format(dff_inv.unique_id) dff_inv.unique_id += 1 - design.design.__init__(self, name) + super().__init__(name) debug.info(1, "Creating {}".format(self.name)) self.add_comment("inv: {0}".format(inv_size)) diff --git a/compiler/modules/dff_inv_array.py b/compiler/modules/dff_inv_array.py index aadb4257..6b08bcce 100644 --- a/compiler/modules/dff_inv_array.py +++ b/compiler/modules/dff_inv_array.py @@ -27,7 +27,7 @@ class dff_inv_array(design.design): if name=="": name = "dff_inv_array_{0}x{1}_{2}".format(rows, columns, dff_inv_array.unique_id) dff_inv_array.unique_id += 1 - design.design.__init__(self, name) + super().__init__(name) debug.info(1, "Creating {}".format(self.name)) self.add_comment("rows: {0} cols: {1}".format(rows, columns)) self.add_comment("inv1: {0}".format(inv1_size)) diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index f4b240da..9004994b 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -12,8 +12,8 @@ class dummy_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. """ - def __init__(self, cols, rows, column_offset=0, mirror=0, name=""): - super().__init__(cols, rows, name, column_offset) + def __init__(self, rows, cols, column_offset=0, mirror=0, name=""): + super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.mirror = mirror self.create_netlist() @@ -51,7 +51,7 @@ class dummy_array(bitcell_base_array): name = "bit_r{0}_c{1}".format(row, col) self.cell_inst[row, col]=self.add_inst(name=name, mod=self.dummy_cell) - self.connect_inst(self.get_bitcell_pins(col, row)) + self.connect_inst(self.get_bitcell_pins(row, col)) def input_load(self): wl_wire = self.gen_wl_wire() diff --git a/compiler/modules/global_bitcell_array.py b/compiler/modules/global_bitcell_array.py new file mode 100644 index 00000000..728f170f --- /dev/null +++ b/compiler/modules/global_bitcell_array.py @@ -0,0 +1,66 @@ +# 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. +# +from bitcell_base_array import bitcell_base_array +from tech import drc, spice +from globals import OPTS +from sram_factory import factory +import debug + +class global_bitcell_array(bitcell_base_array): + """ + Creates a global bitcell array with a number + of local arrays of a sizes given by a tuple in the list. + """ + def __init__(self, sizes, name=""): + # Each bank will have the same number of rows + self.rows = sizes[0][0] + for (r, c) in sizes: + debug.check(r[0] == self.rows, "Cannot have non-uniform number of rows in global array.") + # The total of all columns will be the number of columns + self.cols = sum(x[1] for x in sizes) + self.sizes = sizes + super().__init__(rows=self.rows, cols=self.cols, name=name) + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + """ Create and connect the netlist """ + self.add_modules() + self.add_pins() + self.create_instances() + + def create_layout(self): + + self.place() + + self.add_layout_pins() + + self.add_boundary() + + self.DRC_LVS() + + def add_modules(self): + """ Add the modules used in this design """ + self.local_mods = [] + for (row, col) in self.sizes: + la = factory.create(module_type="local_bitcell_array", rows=row, cols=col) + self.add_mod(la) + self.local_mods.append(la) + + def create_instances(self): + """ Create the module instances used in this design """ + self.local_inst = {} + for i in range(self.sizes): + name = "local_array_{0}".format(i) + self.local_inst.append(self.add_inst(name=name, + mod=self.local_mods[i]) + self.connect_inst(self.get_bitcell_pins(row, col)) + + diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 80cdfb1c..d9c715f7 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -18,7 +18,7 @@ class hierarchical_decoder(design.design): Dynamically generated hierarchical decoder. """ def __init__(self, name, num_outputs): - design.design.__init__(self, name) + super().__init__(name) self.AND_FORMAT = "DEC_AND_{0}" diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 1645d0d1..d72f30a1 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -31,7 +31,7 @@ class hierarchical_predecode(design.design): self.column_decoder = (height != b.height) self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) - design.design.__init__(self, name) + super().__init__(name) def add_pins(self): for k in range(self.number_of_inputs): diff --git a/compiler/modules/hierarchical_predecode2x4.py b/compiler/modules/hierarchical_predecode2x4.py index 9c7ddfa3..3c1daa6a 100644 --- a/compiler/modules/hierarchical_predecode2x4.py +++ b/compiler/modules/hierarchical_predecode2x4.py @@ -14,7 +14,7 @@ class hierarchical_predecode2x4(hierarchical_predecode): Pre 2x4 decoder used in hierarchical_decoder. """ def __init__(self, name, height=None): - hierarchical_predecode.__init__(self, name, 2, height) + super().__init__( name, 2, height) self.create_netlist() if not OPTS.netlist_only: diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index e8c44e48..7513ed7c 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -14,7 +14,7 @@ class hierarchical_predecode3x8(hierarchical_predecode): Pre 3x8 decoder used in hierarchical_decoder. """ def __init__(self, name, height=None): - hierarchical_predecode.__init__(self, name, 3, height) + super().__init__(name, 3, height) self.create_netlist() if not OPTS.netlist_only: diff --git a/compiler/modules/hierarchical_predecode4x16.py b/compiler/modules/hierarchical_predecode4x16.py index 4a258bfb..3b423fde 100644 --- a/compiler/modules/hierarchical_predecode4x16.py +++ b/compiler/modules/hierarchical_predecode4x16.py @@ -14,7 +14,7 @@ class hierarchical_predecode4x16(hierarchical_predecode): Pre 4x16 decoder used in hierarchical_decoder. """ def __init__(self, name, height=None): - hierarchical_predecode.__init__(self, name, 4, height) + super().__init__(name, 4, height) self.create_netlist() if not OPTS.netlist_only: diff --git a/compiler/modules/local_bitcell_array.py b/compiler/modules/local_bitcell_array.py new file mode 100644 index 00000000..cf5fbdee --- /dev/null +++ b/compiler/modules/local_bitcell_array.py @@ -0,0 +1,168 @@ +# 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 bitcell_base_array +from globals import OPTS +from sram_factory import factory +from vector import vector +from tech import drc +import debug + +class local_bitcell_array(bitcell_base_array.bitcell_base_array): + """ + A local bitcell array is a bitcell array with a wordline driver. + This can either be a single aray on its own if there is no hierarchical WL + or it can be combined into a larger array with hierarchical WL. + """ + def __init__(self, rows, cols, ports, left_rbl=0, right_rbl=0, add_replica=True, name=""): + super().__init__(name, rows, cols, 0) + debug.info(2, "create local array of size {} rows x {} cols words".format(rows, + cols + left_rbl + right_rbl)) + + self.rows = rows + self.cols = cols + self.add_replica=add_replica + self.all_ports = ports + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + # We don't offset this because we need to align + # the replica bitcell in the control logic + # self.offset_all_coordinates() + + def create_netlist(self): + """ Create and connect the netlist """ + self.add_modules() + self.add_pins() + self.create_instances() + + def create_layout(self): + + self.place() + + self.add_layout_pins() + + self.route() + + self.add_boundary() + + self.DRC_LVS() + + def add_modules(self): + """ Add the modules used in this design """ + # This is just used for names + self.cell = factory.create(module_type="bitcell") + + self.bitcell_array = factory.create(module_type="replica_bitcell_array", + cols=self.cols, + rows=self.rows, + left_rbl=1, + right_rbl=1 if len(self.all_ports)>1 else 0, + bitcell_ports=self.all_ports, + add_replica=self.add_replica) + self.add_mod(self.bitcell_array) + + self.wl_array = factory.create(module_type="wordline_buffer_array", + rows=self.rows + len(self.all_ports), + cols=self.cols) + self.add_mod(self.wl_array) + + def add_pins(self): + + self.bitline_names = self.bitcell_array.get_all_bitline_names() + self.add_pin_list(self.bitline_names, "INOUT") + self.driver_wordline_inputs = [x for x in self.bitcell_array.get_all_wordline_names() if not x.startswith("dummy")] + self.driver_wordline_outputs = [x + "i" for x in self.driver_wordline_inputs] + self.array_wordline_inputs = [x + "i" if not x.startswith("dummy") else "gnd" for x in self.bitcell_array.get_all_wordline_names()] + self.add_pin_list(self.wordline_names, "INPUT") + self.replica_names = self.bitcell_array.get_rbl_wordline_names() + self.add_pin_list(self.replica_names, "INPUT") + self.bitline_names = self.bitcell_array.get_inouts() + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_instances(self): + """ Create the module instances used in this design """ + + self.wl_inst = self.add_inst(name="wl_driver", + mod=self.wl_array) + self.connect_inst(self.driver_wordline_inputs + self.driver_wordline_outputs + ["vdd", "gnd"]) + + self.bitcell_array_inst = self.add_inst(name="array", + mod=self.bitcell_array, + offset=self.wl_inst.lr()) + self.connect_inst(self.bitline_names + self.array_wordline_inputs + ["vdd", "gnd"]) + + def place(self): + """ Place the bitcelll array to the right of the wl driver. """ + + self.wl_inst.place(vector(0, self.cell.height)) + # FIXME: Replace this with a tech specific paramter + driver_to_array_spacing = 3 * self.m3_pitch + self.bitcell_array_inst.place(vector(self.wl_inst.rx() + driver_to_array_spacing, + 0)) + + self.height = self.bitcell_array.height + self.width = self.bitcell_array_inst.rx() + + def route_unused_wordlines(self): + """ Connect the unused RBL and dummy wordlines to gnd """ + gnd_wl_names = [] + + # Connect unused RBL WL to gnd + array_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("rbl")]) + dummy_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("dummy")]) + rbl_wl_names = set([self.bitcell_array.get_rbl_wordline_names(x) for x in self.all_ports]) + + gnd_wl_names = list((array_rbl_names - rbl_wl_names) | dummy_rbl_names) + + for wl_name in gnd_wl_names: + pin = self.bitcell_array_inst.get_pin(wl_name) + pin_layer = pin.layer + layer_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer)) + left_pin_loc = pin.lc() + right_pin_loc = pin.rc() + + # Place the pins a track outside of the array + left_loc = left_pin_loc - vector(layer_pitch, 0) + right_loc = right_pin_loc + vector(layer_pitch, 0) + self.add_power_pin("gnd", left_loc, directions=("H", "H")) + self.add_power_pin("gnd", right_loc, directions=("H", "H")) + + # Add a path to connect to the array + self.add_path(pin_layer, [left_loc, left_pin_loc]) + self.add_path(pin_layer, [right_loc, right_pin_loc]) + + def add_layout_pins(self): + + for (x, y) in zip(self.bitline_names, self.bitcell_array.get_inouts()): + self.copy_layout_pin(self.bitcell_array_inst, y, x) + + for (x, y) in zip(self.driver_wordline_inputs, self.wl_array.get_inputs()): + self.copy_layout_pin(self.wl_inst, y, x) + + supply_insts = [self.wl_inst, self.bitcell_array_inst] + for pin_name in ["vdd", "gnd"]: + for inst in supply_insts: + pin_list = inst.get_pins(pin_name) + for pin in pin_list: + self.add_power_pin(name=pin_name, + loc=pin.center(), + start_layer=pin.layer) + + def route(self): + array_names = [x for x in self.bitcell_array.get_all_wordline_names() if not x.startswith("dummy")] + for (driver_name, array_name) in zip(self.wl_array.get_outputs(), array_names): + out_pin = self.wl_inst.get_pin(driver_name) + in_pin = self.bitcell_array_inst.get_pin(array_name) + mid_loc = self.wl_inst.rx() + 1.5 * self.m3_pitch + self.add_path(out_pin.layer, [out_pin.rc(), vector(mid_loc, out_pin.cy()), in_pin.lc()]) + + self.route_unused_wordlines() + diff --git a/compiler/modules/multibank.py b/compiler/modules/multibank.py index bf19954b..48bf5f1f 100644 --- a/compiler/modules/multibank.py +++ b/compiler/modules/multibank.py @@ -26,7 +26,7 @@ class multibank(design.design): def __init__(self, name, word_size, num_words, words_per_row, num_banks=1): - design.design.__init__(self, name) + super().__init__(name) debug.info(2, "create sram of size {0} with {1} words".format(word_size,num_words)) self.word_size = word_size diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 980c9d96..532463a7 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -25,7 +25,7 @@ class port_address(design.design): if name == "": name = "port_address_{0}_{1}".format(cols, rows) - design.design.__init__(self, name) + super().__init__(name) debug.info(2, "create data port of cols {0} rows {1}".format(cols, rows)) self.create_netlist() diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index bd6b39e2..eb827f2f 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -32,7 +32,7 @@ class port_data(design.design): if name == "": name = "port_data_{0}".format(self.port) - design.design.__init__(self, name) + super().__init__(name) debug.info(2, "create data port of size {0} with {1} words per row".format(self.word_size, self.words_per_row)) diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index d37de64f..c2d3d986 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -20,7 +20,7 @@ class precharge_array(design.design): """ def __init__(self, name, columns, size=1, bitcell_bl="bl", bitcell_br="br", column_offset=0): - design.design.__init__(self, name) + super().__init__(name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("cols: {0} size: {1} bl: {2} br: {3}".format(columns, size, bitcell_bl, bitcell_br)) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 0c7e412e..1abd5553 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -5,14 +5,14 @@ # import debug -import design +import bitcell_base_array from tech import drc, spice, cell_properties from vector import vector from globals import OPTS from sram_factory import factory -class replica_bitcell_array(design.design): +class replica_bitcell_array(bitcell_base_array.bitcell_base_array): """ Creates a bitcell arrow of cols x rows and then adds the replica and dummy columns and rows. Replica columns are on the left and @@ -21,8 +21,8 @@ class replica_bitcell_array(design.design): Requires a regular bitcell array, replica bitcell, and dummy bitcell (Bl/BR disconnected). """ - def __init__(self, cols, rows, left_rbl, right_rbl, bitcell_ports, name): - design.design.__init__(self, name) + def __init__(self, rows, cols, left_rbl, right_rbl, bitcell_ports, name, add_replica=True): + super().__init__(name, rows, cols, column_offset=0) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) @@ -31,15 +31,26 @@ class replica_bitcell_array(design.design): self.left_rbl = left_rbl self.right_rbl = right_rbl self.bitcell_ports = bitcell_ports + # If set to false, we increase the height for the replica wordline row, but don't + # actually add the column to this array. This is so the height matches other + # banks that have the replica columns. + # Number of replica columns to actually add + if add_replica: + self.add_left_rbl = self.left_rbl + self.add_right_rbl = self.right_rbl + else: + self.add_left_rbl = 0 + self.add_right_rbl = 0 - debug.check(left_rbl + right_rbl == len(self.all_ports), + 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), + 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 - self.extra_rows = 2 + left_rbl + right_rbl - self.extra_cols = 2 + left_rbl + right_rbl + # Two dummy rows plus replica even if we don't add the column + self.extra_rows = 2 + self.left_rbl + self.right_rbl + # Two dummy cols plus replica if we add the column + self.extra_cols = 2 + self.add_left_rbl + self.add_right_rbl self.create_netlist() if not OPTS.netlist_only: @@ -79,33 +90,30 @@ class replica_bitcell_array(design.design): 1 x (rows + 4) """ - # Bitcell for port names only - self.cell = factory.create(module_type="bitcell") - # Bitcell array self.bitcell_array = factory.create(module_type="bitcell_array", - column_offset=1 + self.left_rbl, + column_offset=1 + self.add_left_rbl, cols=self.column_size, rows=self.row_size) self.add_mod(self.bitcell_array) # Replica bitlines self.replica_columns = {} - for bit in range(self.left_rbl + self.right_rbl): + for bit in range(self.add_left_rbl + self.add_right_rbl): # Creating left_rbl - if bit1 port) - for port in range(self.left_rbl): - # Make names for all RBLs - wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x), port) for x in range(len(self.cell.get_all_wl_names()))] - # Keep track of the pin that is the RBL - self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]] - self.replica_col_wl_names.extend(wl_names) - # Regular WLs - self.replica_col_wl_names.extend(self.bitcell_array_wl_names) - # Right port WLs (one dummy for each port when we allow >1 port) - for port in range(self.left_rbl, self.left_rbl + self.right_rbl): - # Make names for all RBLs - wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x), port) for x in range(len(self.cell.get_all_wl_names()))] - # Keep track of the pin that is the RBL - self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]] - self.replica_col_wl_names.extend(wl_names) - self.replica_col_wl_names.extend(["{0}_top".format(x) for x in self.dummy_cell_wl_names]) - - # Left/right dummy columns are connected identically to the replica column - self.dummy_col_wl_names = self.replica_col_wl_names - - # Per port bitline names - self.replica_bl_names = {} - self.replica_wl_names = {} - # Array of all port bitline names - for port in range(self.left_rbl + self.right_rbl): - left_names=["rbl_{0}_{1}".format(self.cell.get_bl_name(x), port) for x in range(len(self.all_ports))] - right_names=["rbl_{0}_{1}".format(self.cell.get_br_name(x), port) for x in range(len(self.all_ports))] - # Keep track of the left pins that are the RBL - self.rbl_bl_names[port]=left_names[self.bitcell_ports[port]] - self.rbl_br_names[port]=right_names[self.bitcell_ports[port]] - # Interleave the left and right lists - bl_names = [x for t in zip(left_names, right_names) for x in t] - self.replica_bl_names[port] = bl_names - - wl_names = ["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()] - self.replica_wl_names[port] = wl_names - - # External pins - self.add_pin_list(self.bitcell_array_bl_names, "INOUT") - # Need to sort by port order since dictionary values may not be in order - bl_names = [self.rbl_bl_names[x] for x in sorted(self.rbl_bl_names.keys())] - br_names = [self.rbl_br_names[x] for x in sorted(self.rbl_br_names.keys())] - for (bl_name, br_name) in zip(bl_names, br_names): - self.add_pin(bl_name, "OUTPUT") - self.add_pin(br_name, "OUTPUT") - self.add_pin_list(self.bitcell_array_wl_names, "INPUT") - # Need to sort by port order since dictionary values may not be in order - wl_names = [self.rbl_wl_names[x] for x in sorted(self.rbl_wl_names.keys())] - for pin_name in wl_names: - self.add_pin(pin_name, "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") + def add_bitline_pins(self): + + # Regular bitline names for all ports + self.bitline_names = [] + # Bitline names for each port + self.bitline_names_by_port = [[] for x in self.all_ports] + # Replica wordlines by port + self.replica_bitline_names = [[] for x in self.all_ports] + # Replica wordlines by port (bl only) + self.replica_bl_names = [[] for x in self.all_ports] + # Dummy wordlines by port + self.dummy_bitline_names = [] + + # Regular array bitline names + self.bitcell_array_bitline_names = self.bitcell_array.get_all_bitline_names() + + # These are the non-indexed names + dummy_bitline_names = ["dummy_" + x for x in self.cell.get_all_bitline_names()] + self.dummy_bitline_names.append([x + "_left" for x in dummy_bitline_names]) + self.dummy_bitline_names.append([x + "_right" for x in dummy_bitline_names]) + + # Array of all port bitline names + for port in range(self.add_left_rbl + self.add_right_rbl): + left_names=["rbl_{0}_{1}".format(self.cell.get_bl_name(x), port) for x in range(len(self.all_ports))] + right_names=["rbl_{0}_{1}".format(self.cell.get_br_name(x), port) for x in range(len(self.all_ports))] + # Keep track of the left pins that are the RBL + self.replica_bl_names[port]=left_names[self.bitcell_ports[port]] + # Interleave the left and right lists + bitline_names = [x for t in zip(left_names, right_names) for x in t] + self.replica_bitline_names[port] = bitline_names + + # Dummy bitlines are not connected to anything + self.bitline_names.extend(self.bitcell_array_bitline_names) + + for port in self.all_ports: + self.add_pin_list(self.replica_bitline_names[port], "INOUT") + self.add_pin_list(self.bitline_names, "INOUT") + + def add_wordline_pins(self): + + # All wordline names for all ports + self.wordline_names = [] + # Wordline names for each port + self.wordline_names_by_port = [[] for x in self.all_ports] + # Replica wordlines by port + self.replica_wordline_names = [[] for x in self.all_ports] + # Dummy wordlines + self.dummy_wordline_names = {} + + # Regular array wordline names + self.bitcell_array_wordline_names = self.bitcell_array.get_all_wordline_names() + + # These are the non-indexed names + dummy_cell_wl_names = ["dummy_" + x for x in self.cell.get_all_wl_names()] + + # Create the full WL names include dummy, replica, and regular bit cells + self.wordline_names = [] + + self.dummy_wordline_names["bot"] = ["{0}_bot".format(x) for x in dummy_cell_wl_names] + self.wordline_names.extend(self.dummy_wordline_names["bot"]) + + # Left port WLs + for port in range(self.left_rbl): + # Make names for all RBLs + wl_names=["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()] + # Keep track of the pin that is the RBL + self.replica_wordline_names[port] = wl_names + self.wordline_names.extend(wl_names) + + # Regular WLs + self.wordline_names.extend(self.bitcell_array_wordline_names) + + # Right port WLs + for port in range(self.left_rbl, self.left_rbl + self.right_rbl): + # Make names for all RBLs + wl_names=["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()] + # Keep track of the pin that is the RBL + self.replica_wordline_names[port] = wl_names + self.wordline_names.extend(wl_names) + + self.dummy_wordline_names["top"] = ["{0}_top".format(x) for x in dummy_cell_wl_names] + self.wordline_names.extend(self.dummy_wordline_names["top"]) + + # Array of all port wl names + for port in range(self.left_rbl + self.right_rbl): + wl_names = ["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()] + self.replica_wordline_names[port] = wl_names + + self.add_pin_list(self.wordline_names, "INPUT") + def create_instances(self): """ Create the module instances used in this design """ @@ -239,37 +273,42 @@ class replica_bitcell_array(design.design): # Main array self.bitcell_array_inst=self.add_inst(name="bitcell_array", mod=self.bitcell_array) - self.connect_inst(self.bitcell_array_bl_names + self.bitcell_array_wl_names + supplies) + self.connect_inst(self.bitcell_array_bitline_names + self.bitcell_array_wordline_names + supplies) # Replica columns self.replica_col_inst = {} - for port in range(self.left_rbl + self.right_rbl): + for port in range(self.add_left_rbl + self.add_right_rbl): self.replica_col_inst[port]=self.add_inst(name="replica_col_{}".format(port), mod=self.replica_columns[port]) - self.connect_inst(self.replica_bl_names[port] + self.replica_col_wl_names + supplies) - + self.connect_inst(self.replica_bitline_names[port] + self.wordline_names + supplies) + # Dummy rows under the bitcell array (connected with with the replica cell wl) self.dummy_row_replica_inst = {} + # Note, this is the number of left and right even if we aren't adding the columns to this bitcell array! for port in range(self.left_rbl + self.right_rbl): self.dummy_row_replica_inst[port]=self.add_inst(name="dummy_row_{}".format(port), mod=self.dummy_row) - self.connect_inst(self.dummy_row_bl_names + self.replica_wl_names[port] + supplies) + self.connect_inst(self.bitcell_array_bitline_names + self.replica_wordline_names[port] + supplies) # Top/bottom dummy rows or col caps self.dummy_row_bot_inst=self.add_inst(name="dummy_row_bot", - mod=self.edge_row) - self.connect_inst(self.dummy_row_bl_names + [x + "_bot" for x in self.dummy_cell_wl_names] + supplies) + mod=self.col_cap) + self.connect_inst(self.bitcell_array_bitline_names + + self.dummy_wordline_names["bot"] + + supplies) self.dummy_row_top_inst=self.add_inst(name="dummy_row_top", - mod=self.edge_row) - self.connect_inst(self.dummy_row_bl_names + [x + "_top" for x in self.dummy_cell_wl_names] + supplies) + mod=self.col_cap) + self.connect_inst(self.bitcell_array_bitline_names + + self.dummy_wordline_names["top"] + + supplies) # Left/right Dummy columns self.dummy_col_left_inst=self.add_inst(name="dummy_col_left", - mod=self.edge_col_left) - self.connect_inst([x + "_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) + mod=self.row_cap_left) + self.connect_inst(self.dummy_bitline_names[0] + self.wordline_names + supplies) self.dummy_col_right_inst=self.add_inst(name="dummy_col_right", - mod=self.edge_col_right) - self.connect_inst([x + "_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) + mod=self.row_cap_right) + self.connect_inst(self.dummy_bitline_names[-1] + self.wordline_names + supplies) def create_layout(self): @@ -277,41 +316,18 @@ class replica_bitcell_array(design.design): self.width = (self.column_size + self.extra_cols) * self.cell.width # This is a bitcell x bitcell offset to scale - offset = vector(self.cell.width, self.cell.height) + self.bitcell_offset = vector(self.cell.width, self.cell.height) + # Everything is computed with the main array at (0, 0) to start self.bitcell_array_inst.place(offset=[0, 0]) - # To the left of the bitcell array - for bit in range(self.left_rbl): - self.replica_col_inst[bit].place(offset=offset.scale(-bit - 1, -self.left_rbl - 1)) - # To the right of the bitcell array - for bit in range(self.right_rbl): - self.replica_col_inst[self.left_rbl + bit].place(offset=offset.scale(bit, -self.left_rbl - 1) + self.bitcell_array_inst.lr()) + self.add_replica_columns() + + self.add_end_caps() - # FIXME: These depend on the array size itself - # Far top dummy row (first row above array is NOT flipped) - flip_dummy = self.right_rbl % 2 - self.dummy_row_top_inst.place(offset=offset.scale(0, self.right_rbl + flip_dummy) + self.bitcell_array_inst.ul(), - mirror="MX" if flip_dummy else "R0") - # FIXME: These depend on the array size itself - # Far bottom dummy row (first row below array IS flipped) - flip_dummy = (self.left_rbl + 1) % 2 - self.dummy_row_bot_inst.place(offset=offset.scale(0, -self.left_rbl - 1 + flip_dummy), - mirror="MX" if flip_dummy else "R0") - # Far left dummy col - self.dummy_col_left_inst.place(offset=offset.scale(-self.left_rbl - 1, -self.left_rbl - 1)) - # Far right dummy col - self.dummy_col_right_inst.place(offset=offset.scale(self.right_rbl, -self.left_rbl - 1) + self.bitcell_array_inst.lr()) - - # Replica dummy rows - for bit in range(self.left_rbl): - self.dummy_row_replica_inst[bit].place(offset=offset.scale(0, -bit - bit % 2), - mirror="R0" if bit % 2 else "MX") - for bit in range(self.right_rbl): - self.dummy_row_replica_inst[self.left_rbl + bit].place(offset=offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul(), - mirror="MX" if bit % 2 else "R0") - - self.translate_all(offset.scale(-1 - self.left_rbl, -1 - self.left_rbl)) + # Array was at (0, 0) but move everything so it is at the lower left + # We move DOWN the number of left RBL even if we didn't add the column to this bitcell array + self.translate_all(self.bitcell_offset.scale(-1 - self.add_left_rbl, -1 - self.left_rbl)) self.add_layout_pins() @@ -319,22 +335,70 @@ class replica_bitcell_array(design.design): self.DRC_LVS() + def add_replica_columns(self): + """ Add replica columns on left and right of array """ + + # Grow from left to right, toward the array + for bit in range(self.add_left_rbl): + offset = self.bitcell_offset.scale(-self.add_left_rbl + bit, -self.add_left_rbl - 1) + self.replica_col_inst[bit].place(offset) + # Grow to the right of the bitcell array, array outward + for bit in range(self.add_right_rbl): + offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.add_left_rbl - 1) + self.replica_col_inst[self.add_left_rbl + bit].place(offset) + + # Replica dummy rows + # Add the dummy rows even if we aren't adding the replica column to this bitcell array + # These grow up, toward the array + for bit in range(self.left_rbl): + self.dummy_row_replica_inst[bit].place(offset=self.bitcell_offset.scale(0, -self.left_rbl + bit + (-self.left_rbl + bit) % 2), + mirror="MX" if (-self.left_rbl + bit) % 2 else "R0") + # These grow up, away from the array + for bit in range(self.right_rbl): + self.dummy_row_replica_inst[self.left_rbl + bit].place(offset=self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul(), + mirror="MX" if bit % 2 else "R0") + + def add_end_caps(self): + """ Add dummy cells or end caps around the array """ + + # FIXME: These depend on the array size itself + # Far top dummy row (first row above array is NOT flipped) + flip_dummy = self.right_rbl % 2 + dummy_row_offset = self.bitcell_offset.scale(0, self.right_rbl + flip_dummy) + self.bitcell_array_inst.ul() + self.dummy_row_top_inst.place(offset=dummy_row_offset, + mirror="MX" if flip_dummy else "R0") + # FIXME: These depend on the array size itself + # Far bottom dummy row (first row below array IS flipped) + flip_dummy = (self.left_rbl + 1) % 2 + dummy_row_offset = self.bitcell_offset.scale(0, -self.left_rbl - 1 + flip_dummy) + self.dummy_row_bot_inst.place(offset=dummy_row_offset, + mirror="MX" if flip_dummy else "R0") + # Far left dummy col + # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array + dummy_col_offset = self.bitcell_offset.scale(-self.add_left_rbl - 1, -self.left_rbl - 1) + self.dummy_col_left_inst.place(offset=dummy_col_offset) + # Far right dummy col + # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array + dummy_col_offset = self.bitcell_offset.scale(self.add_right_rbl, -self.left_rbl - 1) + self.bitcell_array_inst.lr() + self.dummy_col_right_inst.place(offset=dummy_col_offset) + def add_layout_pins(self): """ Add the layout pins """ + # All wordlines # Main array wl and bl/br pin_names = self.bitcell_array.get_pin_names() for pin_name in pin_names: - for wl in self.bitcell_array_wl_names: + for wl in self.bitcell_array_wordline_names: if wl in pin_name: pin_list = self.bitcell_array_inst.get_pins(pin_name) for pin in pin_list: self.add_layout_pin(text=pin_name, - layer=pin.layer, + layer=pin.layer, offset=pin.ll().scale(0, 1), width=self.width, height=pin.height()) - for bitline in self.bitcell_array_bl_names: + for bitline in self.bitcell_array_bitline_names: if bitline in pin_name: pin_list = self.bitcell_array_inst.get_pins(pin_name) for pin in pin_list: @@ -344,31 +408,35 @@ class replica_bitcell_array(design.design): width=pin.width(), height=self.height) - # Replica wordlines - for port in range(self.left_rbl + self.right_rbl): - inst = self.replica_col_inst[port] - for (pin_name, wl_name) in zip(self.cell.get_all_wl_names(), self.replica_wl_names[port]): - # +1 for dummy row - pin_bit = port + 1 - # +row_size if above the array - if port>=self.left_rbl: - pin_bit += self.row_size + # Dummy wordlines + for (name, inst) in [("bot", self.dummy_row_bot_inst), ("top", self.dummy_row_top_inst)]: + for (pin_name, wl_name) in zip(self.cell.get_all_wl_names(), self.dummy_wordline_names[name]): + # It's always a single row + pin = inst.get_pin(pin_name + "_0") + self.add_layout_pin(text=wl_name, + layer=pin.layer, + offset=pin.ll().scale(0, 1), + width=self.width, + height=pin.height()) - pin_name += "_{}".format(pin_bit) - pin = inst.get_pin(pin_name) - if wl_name in self.rbl_wl_names.values(): - self.add_layout_pin(text=wl_name, - layer=pin.layer, - offset=pin.ll().scale(0, 1), - width=self.width, - height=pin.height()) + # Replica wordlines (go by the row instead of replica column because we may have to add a pin + # even though the column is in another local bitcell array) + for (port, inst) in list(self.dummy_row_replica_inst.items()): + for (pin_name, wl_name) in zip(self.cell.get_all_wl_names(), self.replica_wordline_names[port]): + pin = inst.get_pin(pin_name + "_0") + self.add_layout_pin(text=wl_name, + layer=pin.layer, + offset=pin.ll().scale(0, 1), + width=self.width, + height=pin.height()) # Replica bitlines - for port in range(self.left_rbl + self.right_rbl): + for port in range(self.add_left_rbl + self.add_right_rbl): inst = self.replica_col_inst[port] - for (pin_name, bl_name) in zip(self.cell.get_all_bitline_names(), self.replica_bl_names[port]): + for (pin_name, bl_name) in zip(self.cell.get_all_bitline_names(), self.replica_bitline_names[port]): pin = inst.get_pin(pin_name) - if bl_name in self.rbl_bl_names or bl_name in self.rbl_br_names: + + if bl_name in self.replica_bl_names: name = bl_name else: name = "rbl_{0}_{1}".format(pin_name, port) @@ -390,22 +458,42 @@ class replica_bitcell_array(design.design): loc=pin.center(), directions=("V", "V"), start_layer=pin.layer) - + for inst in list(self.replica_col_inst.values()): self.copy_layout_pin(inst, pin_name) - self.copy_layout_pin(inst, pin_name) - def get_rbl_wl_name(self, port): - """ Return the WL for the given RBL port """ - return self.rbl_wl_names[port] + def get_rbl_wordline_names(self, port=None): + """ + Return the ACTIVE WL for the given RBL port. + Inactive will be set to gnd. + """ + if port == None: + temp = [] + for port in self.all_ports: + temp.extend(self.replica_wordline_names[port]) + return temp + else: + wl_names = self.replica_wordline_names[port] + return wl_names[port] - def get_rbl_bl_name(self, port): + def get_rbl_bitline_names(self, port=None): """ Return the BL for the given RBL port """ - return self.rbl_bl_names[port] + if port == None: + temp = [] + for port in self.all_ports: + temp.extend(self.replica_bitline_names[port]) + return temp + else: + bl_names = self.replica_bitline_names[port] + return bl_names[2 * port:2 * port + 2] - def get_rbl_br_name(self, port): - """ Return the BR for the given RBL port """ - return self.rbl_br_names[port] + def get_wordline_names(self): + """ Return the wordline names """ + return self.wordline_names + + def get_bitline_names(self): + """ Return the bitline names """ + return self.bitline_names def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 9613e6fa..a4311394 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -22,7 +22,7 @@ class replica_column(design.design): def __init__(self, name, rows, left_rbl, right_rbl, replica_bit, column_offset=0): - design.design.__init__(self, name) + super().__init__(name) self.rows = rows self.left_rbl = left_rbl @@ -102,22 +102,22 @@ class replica_column(design.design): if (row > self.left_rbl and row < self.total_size - self.right_rbl - 1): self.cell_inst[row]=self.add_inst(name=name, mod=self.replica_cell) - self.connect_inst(self.get_bitcell_pins(0, row)) + self.connect_inst(self.get_bitcell_pins(row, 0)) elif row==self.replica_bit: self.cell_inst[row]=self.add_inst(name=name, mod=self.replica_cell) - self.connect_inst(self.get_bitcell_pins(0, row)) + self.connect_inst(self.get_bitcell_pins(row, 0)) elif (row == 0 or row == self.total_size - 1): self.cell_inst[row]=self.add_inst(name=name, mod=self.edge_cell) if end_caps_enabled: - self.connect_inst(self.get_bitcell_pins_col_cap(0, row)) + self.connect_inst(self.get_bitcell_pins_col_cap(row, 0)) else: - self.connect_inst(self.get_bitcell_pins(0, row)) + self.connect_inst(self.get_bitcell_pins(row, 0)) else: self.cell_inst[row]=self.add_inst(name=name, mod=self.dummy_cell) - self.connect_inst(self.get_bitcell_pins(0, row)) + self.connect_inst(self.get_bitcell_pins(row, 0)) def place_instances(self): from tech import cell_properties @@ -191,7 +191,7 @@ class replica_column(design.design): else: self.copy_layout_pin(inst, pin_name) - def get_bitcell_pins(self, col, row): + def get_bitcell_pins(self, row, col): """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ @@ -208,7 +208,7 @@ class replica_column(design.design): return bitcell_pins - def get_bitcell_pins_col_cap(self, col, row): + def get_bitcell_pins_col_cap(self, row, col): """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index f108d86e..58b33c89 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -13,8 +13,8 @@ class row_cap_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. """ - def __init__(self, cols, rows, column_offset=0, mirror=0, name=""): - super().__init__(cols, rows, name, column_offset) + def __init__(self, rows, cols, column_offset=0, mirror=0, name=""): + super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.mirror = mirror self.no_instances = True self.create_netlist() @@ -49,9 +49,9 @@ class row_cap_array(bitcell_base_array): name = "bit_r{0}_c{1}".format(row, col) self.cell_inst[row, col]=self.add_inst(name=name, mod=self.dummy_cell) - self.connect_inst(self.get_bitcell_pins(col, row)) + self.connect_inst(self.get_bitcell_pins(row, col)) - def get_bitcell_pins(self, col, row): + def get_bitcell_pins(self, row, col): """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index 35fbdf42..67703903 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -50,7 +50,7 @@ class sense_amp(design.design): return props.sense_amp.pin.en def __init__(self, name): - design.design.__init__(self, name) + super().__init__(name) debug.info(2, "Create sense_amp") self.width = sense_amp.width diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 98cbee66..20f6e06f 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -22,7 +22,7 @@ class sense_amp_array(design.design): def __init__(self, name, word_size, words_per_row, num_spare_cols=None, column_offset=0): - design.design.__init__(self, name) + super().__init__(name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("word_size {0}".format(word_size)) self.add_comment("words_per_row: {0}".format(words_per_row)) diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 8b01d111..f57bbb20 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -21,7 +21,7 @@ class single_level_column_mux_array(design.design): """ def __init__(self, name, columns, word_size, bitcell_bl="bl", bitcell_br="br", column_offset=0): - design.design.__init__(self, name) + super().__init__(name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br)) diff --git a/compiler/modules/tri_gate_array.py b/compiler/modules/tri_gate_array.py index e7ebd802..c329a04b 100644 --- a/compiler/modules/tri_gate_array.py +++ b/compiler/modules/tri_gate_array.py @@ -19,7 +19,7 @@ class tri_gate_array(design.design): def __init__(self, columns, word_size, name): """Intial function of tri gate array """ - design.design.__init__(self, name) + super().__init__(name) debug.info(1, "Creating {0}".format(self.name)) self.columns = columns diff --git a/compiler/modules/wordline_buffer_array.py b/compiler/modules/wordline_buffer_array.py new file mode 100644 index 00000000..7a1bf8d1 --- /dev/null +++ b/compiler/modules/wordline_buffer_array.py @@ -0,0 +1,140 @@ +# 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 +import design +from tech import drc, layer +from vector import vector +from sram_factory import factory +from globals import OPTS + + +class wordline_buffer_array(design.design): + """ + Creates a Wordline Buffer/Inverter array + """ + + def __init__(self, name, rows, cols): + design.design.__init__(self, name) + debug.info(1, "Creating {0}".format(self.name)) + self.add_comment("rows: {0} cols: {1}".format(rows, cols)) + + self.rows = rows + self.cols = cols + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_drivers() + + def create_layout(self): + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + self.place_drivers() + self.route_layout() + self.route_vdd_gnd() + self.offset_all_coordinates() + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + # inputs to wordline_driver. + for i in range(self.rows): + self.add_pin("in_{0}".format(i), "INPUT") + # Outputs from wordline_driver. + for i in range(self.rows): + self.add_pin("out_{0}".format(i), "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def add_modules(self): + b = factory.create(module_type="bitcell") + + self.wl_driver = factory.create(module_type="inv_dec", + size=self.cols, + height=b.height) + self.add_mod(self.wl_driver) + + def route_vdd_gnd(self): + """ + Add a pin for each row of vdd/gnd which + are must-connects next level up. + """ + if OPTS.tech_name == "sky130": + for name in ["vdd", "gnd"]: + supply_pins = self.wld_inst[0].get_pins(name) + for pin in supply_pins: + self.add_layout_pin_segment_center(text=name, + layer=pin.layer, + start=pin.bc(), + end=vector(pin.cx(), self.height)) + else: + # Find the x offsets for where the vias/pins should be placed + xoffset_list = [self.wld_inst[0].rx()] + for num in range(self.rows): + # this will result in duplicate polygons for rails, but who cares + + # use the inverter offset even though it will be the and's too + (gate_offset, y_dir) = self.get_gate_offset(0, + self.wl_driver.height, + num) + # Route both supplies + for name in ["vdd", "gnd"]: + supply_pin = self.wld_inst[num].get_pin(name) + + # Add pins in two locations + for xoffset in xoffset_list: + pin_pos = vector(xoffset, supply_pin.cy()) + self.add_power_pin(name, pin_pos) + + def create_drivers(self): + self.wld_inst = [] + for row in range(self.rows): + self.wld_inst.append(self.add_inst(name="wld{0}".format(row), + mod=self.wl_driver)) + self.connect_inst(["in_{0}".format(row), + "out_{0}".format(row), + "vdd", "gnd"]) + + def place_drivers(self): + + for row in range(self.rows): + if (row % 2): + y_offset = self.wl_driver.height * (row + 1) + inst_mirror = "MX" + else: + y_offset = self.wl_driver.height * row + inst_mirror = "R0" + + offset = [0, y_offset] + + self.wld_inst[row].place(offset=offset, + mirror=inst_mirror) + + self.width = self.wl_driver.width + self.height = self.wl_driver.height * self.rows + + def route_layout(self): + """ Route all of the signals """ + + for row in range(self.rows): + inst = self.wld_inst[row] + + self.copy_layout_pin(inst, "A", "in_{0}".format(row)) + + # output each WL on the right + wl_offset = inst.get_pin("Z").rc() + self.add_layout_pin_segment_center(text="out_{0}".format(row), + layer=self.route_layer, + start=wl_offset, + end=wl_offset - vector(self.m1_width, 0)) diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index e8a3c110..f82938de 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -12,6 +12,7 @@ from vector import vector from sram_factory import factory from globals import OPTS + class wordline_driver_array(design.design): """ Creates a Wordline Driver @@ -19,7 +20,7 @@ class wordline_driver_array(design.design): """ def __init__(self, name, rows, cols): - design.design.__init__(self, name) + super().__init__(name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index a6eb1384..665142ec 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -21,7 +21,7 @@ class write_driver_array(design.design): def __init__(self, name, columns, word_size, num_spare_cols=None, write_size=None, column_offset=0): - design.design.__init__(self, name) + super().__init__(name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("columns: {0}".format(columns)) self.add_comment("word_size {0}".format(word_size)) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index d48aefef..9b083512 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -19,7 +19,7 @@ class write_mask_and_array(design.design): """ def __init__(self, name, columns, word_size, write_size, column_offset=0): - design.design.__init__(self, name) + super().__init__(name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("columns: {0}".format(columns)) self.add_comment("word_size {0}".format(word_size)) diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 435ace1f..a46485d0 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -22,7 +22,7 @@ class pand2(pgate.pgate): self.vertical = vertical self.size = size - pgate.pgate.__init__(self, name, height, add_wells) + super().__init__(name, height, add_wells) def create_netlist(self): self.add_pins() diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index 92429921..72a57f74 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -23,7 +23,7 @@ class pand3(pgate.pgate): self.size = size # Creates the netlist and layout - pgate.pgate.__init__(self, name, height, add_wells) + super().__init__(name, height, add_wells) def create_netlist(self): self.add_pins() diff --git a/compiler/pgates/pbuf.py b/compiler/pgates/pbuf.py index 8b9c4eab..d82e2091 100644 --- a/compiler/pgates/pbuf.py +++ b/compiler/pgates/pbuf.py @@ -25,7 +25,7 @@ class pbuf(pgate.pgate): self.height = height # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + super().__init__(name, height) def create_netlist(self): self.add_pins() diff --git a/compiler/pgates/pbuf_dec.py b/compiler/pgates/pbuf_dec.py new file mode 100644 index 00000000..e5018b62 --- /dev/null +++ b/compiler/pgates/pbuf_dec.py @@ -0,0 +1,125 @@ +# 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 vector import vector +import pgate +from sram_factory import factory + + +class pbuf_dec(pgate.pgate): + """ + This is a simple buffer used for driving wordlines. + """ + def __init__(self, name, size=4, height=None): + + debug.info(1, "creating {0} with size of {1}".format(name, size)) + self.add_comment("size: {}".format(size)) + + self.stage_effort = 4 + self.size = size + self.height = height + + # 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_layout(self): + self.width = self.inv1.width + self.inv2.width + self.place_insts() + self.add_wires() + self.add_layout_pins() + self.route_supply_rails() + self.add_boundary() + + def add_pins(self): + self.add_pin("A", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_modules(self): + # Shield the cap, but have at least a stage effort of 4 + input_size = max(1, int(self.size / self.stage_effort)) + self.inv1 = factory.create(module_type="pinv_dec", + size=input_size, + height=self.height) + self.add_mod(self.inv1) + + self.inv2 = factory.create(module_type="pinv_dec", + size=self.size, + height=self.height) + self.add_mod(self.inv2) + + def create_insts(self): + self.inv1_inst = self.add_inst(name="buf_inv1", + mod=self.inv1) + self.connect_inst(["A", "zb_int", "vdd", "gnd"]) + + self.inv2_inst = self.add_inst(name="buf_inv2", + mod=self.inv2) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add INV1 to the right + self.inv1_inst.place(vector(0, 0)) + + # Add INV2 to the right + self.inv2_inst.place(vector(self.inv1_inst.rx(), 0)) + + def add_wires(self): + # inv1 Z to inv2 A + z1_pin = self.inv1_inst.get_pin("Z") + a2_pin = self.inv2_inst.get_pin("A") + mid_loc = vector(a2_pin.cx(), z1_pin.cy()) + self.add_path(self.route_layer, + [z1_pin.rc(), mid_loc, a2_pin.lc()], + width=a2_pin.width()) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + self.copy_layout_pin(self.inv1_inst, "vdd") + self.copy_layout_pin(self.inv1_inst, "gnd") + self.copy_layout_pin(self.inv2_inst, "vdd") + self.copy_layout_pin(self.inv2_inst, "gnd") + + def add_layout_pins(self): + z_pin = self.inv2_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=z_pin.layer, + offset=z_pin.center(), + width=z_pin.width(), + height=z_pin.height()) + + a_pin = self.inv1_inst.get_pin("A") + self.add_layout_pin_rect_center(text="A", + layer=a_pin.layer, + offset=a_pin.center(), + width=a_pin.width(), + height=a_pin.height()) + + def get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A -> Z path""" + stage_effort_list = [] + stage1_cout = self.inv2.get_cin() + stage1 = self.inv1.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv2.get_stage_effort(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Returns the relative capacitance of the input""" + input_cin = self.inv1.get_cin() + return input_cin diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index 578a11c4..8916f0fa 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -35,7 +35,7 @@ class pdriver(pgate.pgate): debug.error("Cannot specify both size_list and inverting.", -1) # Creates the netlist and layout - pgate.pgate.__init__(self, name, height, add_wells) + super().__init__(name, height, add_wells) def compute_sizes(self): # size_list specified diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 1e55d5fb..082ef7b8 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -26,7 +26,7 @@ class pgate(design.design): def __init__(self, name, height=None, add_wells=True): """ Creates a generic cell """ - design.design.__init__(self, name) + super().__init__(name) if height: self.height = height diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 4caf2a18..db46b6b1 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -44,7 +44,7 @@ class pinv(pgate.pgate): self.pmos_size = beta * size self.beta = beta - pgate.pgate.__init__(self, name, height, add_wells) + super().__init__(name, height, add_wells) def create_netlist(self): """ Calls all functions related to the generation of the netlist """ diff --git a/compiler/pgates/pinv_dec.py b/compiler/pgates/pinv_dec.py index e832f452..028fe954 100644 --- a/compiler/pgates/pinv_dec.py +++ b/compiler/pgates/pinv_dec.py @@ -38,7 +38,7 @@ class pinv_dec(pinv.pinv): else: self.supply_layer = "m2" - pinv.pinv.__init__(self, name, size, beta, self.cell_height, add_wells) + super().__init__(name, size, beta, self.cell_height, add_wells) def determine_tx_mults(self): """ diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index 5b286e9b..f746736c 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -32,7 +32,7 @@ class pinvbuf(pgate.pgate): self.predriver_size = max(int(self.size / (self.stage_effort / 2)), 1) # Creates the netlist and layout - pgate.pgate.__init__(self, name) + super().__init__(name) def create_netlist(self): self.add_pins() diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index fb6bb210..c1295a1b 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -43,7 +43,7 @@ class pnand2(pgate.pgate): self.pmos_width = self.nearest_bin("pmos", self.pmos_width) # Creates the netlist and layout - pgate.pgate.__init__(self, name, height, add_wells) + super().__init__(name, height, add_wells) def create_netlist(self): self.add_pins() diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index e4e71e61..efcbe369 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -46,7 +46,7 @@ class pnand3(pgate.pgate): self.pmos_width = self.nearest_bin("pmos", self.pmos_width) # Creates the netlist and layout - pgate.pgate.__init__(self, name, height, add_wells) + super().__init__(name, height, add_wells) def add_pins(self): """ Adds pins for spice netlist """ diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index aad405e8..331a0745 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -42,7 +42,7 @@ class pnor2(pgate.pgate): self.pmos_width = self.nearest_bin("pmos", self.pmos_width) # Creates the netlist and layout - pgate.pgate.__init__(self, name, height, add_wells) + super().__init__(name, height, add_wells) def create_netlist(self): self.add_pins() diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 52d24390..aefdbb86 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -23,7 +23,7 @@ class precharge(design.design): def __init__(self, name, size=1, bitcell_bl="bl", bitcell_br="br"): debug.info(2, "creating precharge cell {0}".format(name)) - design.design.__init__(self, name) + super().__init__(name) self.bitcell = factory.create(module_type="bitcell") self.beta = parameter["beta"] diff --git a/compiler/pgates/ptristate_inv.py b/compiler/pgates/ptristate_inv.py index 9fd5f8b6..affc157e 100644 --- a/compiler/pgates/ptristate_inv.py +++ b/compiler/pgates/ptristate_inv.py @@ -38,7 +38,7 @@ class ptristate_inv(pgate.pgate): self.pmos_width = self.pmos_size * drc("minwidth_tx") # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + super().__init__(name, height) def create_netlist(self): """ Calls all functions related to the generation of the netlist """ diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index b6f839f3..c7f69422 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -75,7 +75,7 @@ class ptx(design.design): # replace periods with underscore for newer spice compatibility name = name.replace('.', '_') debug.info(3, "creating ptx {0}".format(name)) - design.design.__init__(self, name) + super().__init__(name) self.tx_type = tx_type self.mults = mults @@ -129,7 +129,7 @@ class ptx(design.design): # be decided in the layout later. area_sd = 2.5 * self.poly_width * self.tx_width perimeter_sd = 2 * self.poly_width + 2 * self.tx_width - if OPTS.tech_name == "sky130" and OPTS.lvs_exe and OPTS.lvs_exe[0] == "calibre": + if OPTS.tech_name == "sky130": # sky130 simulation cannot use the mult parameter in simulation (self.tx_width, self.mults) = pgate.best_bin(self.tx_type, self.tx_width) main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], @@ -148,20 +148,19 @@ class ptx(design.design): area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd, area_sd) self.spice_device = main_str + area_str - self.spice.append("\n* ptx " + self.spice_device) + self.spice.append("\n* spice ptx " + self.spice_device) if OPTS.tech_name == "sky130" and OPTS.lvs_exe and OPTS.lvs_exe[0] == "calibre": # sky130 requires mult parameter too self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult={1}".format(spice[self.tx_type], - self.mults, - self.tx_width, - drc("minwidth_poly")) + self.mults, + self.tx_width, + drc("minwidth_poly")) else: self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], - self.mults, - self.tx_width, - drc("minwidth_poly")) - + self.mults, + self.tx_width, + drc("minwidth_poly")) def setup_layout_constants(self): """ Pre-compute some handy layout parameters. diff --git a/compiler/pgates/pwrite_driver.py b/compiler/pgates/pwrite_driver.py index 87db7b20..6ae448f9 100644 --- a/compiler/pgates/pwrite_driver.py +++ b/compiler/pgates/pwrite_driver.py @@ -22,7 +22,7 @@ class pwrite_driver(design.design): def __init__(self, name, size=0): debug.error("pwrite_driver not implemented yet.", -1) debug.info(1, "creating pwrite_driver {}".format(name)) - design.design.__init__(self, name) + super().__init__(name) self.size = size self.beta = parameter["beta"] self.pmos_width = self.beta*self.size*parameter["min_tx_size"] diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index d2dbacc1..cd9be887 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -30,7 +30,7 @@ class single_level_column_mux(pgate.pgate): self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br - pgate.pgate.__init__(self, name) + super().__init__(name) def get_bl_names(self): return "bl" diff --git a/compiler/pgates/wordline_driver.py b/compiler/pgates/wordline_driver.py index c8cf1326..a8ca76f8 100644 --- a/compiler/pgates/wordline_driver.py +++ b/compiler/pgates/wordline_driver.py @@ -21,7 +21,7 @@ class wordline_driver(design.design): def __init__(self, name, size=1, height=None): debug.info(1, "Creating wordline_driver {}".format(name)) self.add_comment("size: {}".format(size)) - design.design.__init__(self, name) + super().__init__(name) if height is None: b = factory.create(module_type="bitcell") diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 1ec7d636..8cf926c6 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -90,9 +90,9 @@ class sram(): # Save the LVS file start_time = datetime.datetime.now() - spname = OPTS.output_path + self.s.name + ".lvs" - debug.print_raw("LVS: Writing to {0}".format(spname)) - self.lvs_write(spname) + lvsname = OPTS.output_path + self.s.name + ".lvs.sp" + debug.print_raw("LVS: Writing to {0}".format(lvsname)) + self.lvs_write(lvsname) print_time("LVS writing", datetime.datetime.now(), start_time) # Save the extracted spice file @@ -100,8 +100,10 @@ class sram(): import verify start_time = datetime.datetime.now() # Output the extracted design if requested - sp_file = OPTS.output_path + "temp_pex.sp" - verify.run_pex(self.s.name, gdsname, spname, output=sp_file) + pexname = OPTS.output_path + self.s.name + ".pex.sp" + spname = OPTS.output_path + self.s.name + ".sp" + verify.run_pex(self.s.name, gdsname, spname, output=pexname) + sp_file = pexname print_time("Extraction", datetime.datetime.now(), start_time) else: # Use generated spice file for characterization diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index a5dfb0e5..905a531e 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -160,7 +160,10 @@ class sram_1bank(sram_base): port = 0 # Add the col address flops below the bank to the right of the control logic x_offset = self.control_logic_insts[port].rx() + self.dff.width - y_offset = - self.data_bus_size[port] - self.dff.height + # Place it a data bus below the x-axis, but at least as low as the control logic to not block + # the control logic signals + y_offset = min(-self.data_bus_size[port] - self.dff.height, + self.control_logic_insts[port].by()) if self.col_addr_dff: self.col_addr_pos[port] = vector(x_offset, y_offset) @@ -201,7 +204,10 @@ class sram_1bank(sram_base): # Add the col address flops below the bank to the right of the control logic x_offset = self.control_logic_insts[port].lx() - 2 * self.dff.width - y_offset = self.bank.height + self.data_bus_size[port] + self.dff.height + # Place it a data bus below the x-axis, but at least as high as the control logic to not block + # the control logic signals + y_offset = max(self.bank.height + self.data_bus_size[port] + self.dff.height, + self.control_logic_insts[port].uy() - self.dff.height) if self.col_addr_dff: self.col_addr_pos[port] = vector(x_offset, y_offset) @@ -637,5 +643,8 @@ class sram_1bank(sram_base): """Gets the spice name of the target bitcell.""" # Sanity check in case it was forgotten if inst_name.find('x') != 0: - inst_name = 'x' + inst_name - return self.bank_inst.mod.get_cell_name(inst_name + '.x' + self.bank_inst.name, row, col) + inst_name = 'x'+inst_name + return self.bank_inst.mod.get_cell_name(inst_name+'.x'+self.bank_inst.name, row, col) + + def get_bank_num(self, inst_name, row, col): + return 0; diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 12af7cf6..f3598e35 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -15,6 +15,9 @@ from design import design from verilog import verilog from lef import lef from sram_factory import factory +from tech import drc +import numpy as np +import logical_effort class sram_base(design, verilog, lef): @@ -84,8 +87,71 @@ class sram_base(design, verilog, lef): for bit in range(self.word_size + self.num_spare_cols): self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT") - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") + self.add_pin("vdd","POWER") + self.add_pin("gnd","GROUND") + + def add_global_pex_labels(self): + """ + Add pex labels at the sram level for spice analysis + """ + + # add pex labels for bitcells + for bank_num in range(len(self.bank_insts)): + bank = self.bank_insts[bank_num] + pex_data = bank.reverse_transformation_bitcell(bank.mod.bitcell.name) + + bank_offset = pex_data[0] # offset bank relative to sram + Q_offset = pex_data[1] # offset of storage relative to bank + Q_bar_offset = pex_data[2] # offset of storage relative to bank + bl_offsets = pex_data[3] + br_offsets = pex_data[4] + bl_meta = pex_data[5] + br_meta = pex_data[6] + + bl = [] + br = [] + + storage_layer_name = "m1" + bitline_layer_name = "m2" + + for cell in range(len(bank_offset)): + Q = [bank_offset[cell][0] + Q_offset[cell][0], bank_offset[cell][1] + Q_offset[cell][1]] + Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0], bank_offset[cell][1] + Q_bar_offset[cell][1]] + OPTS.words_per_row = self.words_per_row + self.add_layout_pin_rect_center("bitcell_Q_b{}_r{}_c{}".format(bank_num, int(cell % (OPTS.num_words / self.words_per_row)), int(cell / (OPTS.num_words))) , storage_layer_name, Q) + self.add_layout_pin_rect_center("bitcell_Q_bar_b{}_r{}_c{}".format(bank_num, int(cell % (OPTS.num_words / self.words_per_row)), int(cell / (OPTS.num_words))), storage_layer_name, Q_bar) + + for cell in range(len(bl_offsets)): + col = bl_meta[cell][0][2] + for bitline in range(len(bl_offsets[cell])): + bitline_location = [float(bank_offset[cell][0]) + bl_offsets[cell][bitline][0], float(bank_offset[cell][1]) + bl_offsets[cell][bitline][1]] + bl.append([bitline_location, bl_meta[cell][bitline][3], col]) + + for cell in range(len(br_offsets)): + col = br_meta[cell][0][2] + for bitline in range(len(br_offsets[cell])): + bitline_location = [float(bank_offset[cell][0]) + br_offsets[cell][bitline][0], float(bank_offset[cell][1]) + br_offsets[cell][bitline][1]] + br.append([bitline_location, br_meta[cell][bitline][3], col]) + + for i in range(len(bl)): + self.add_layout_pin_rect_center("bl{0}_{1}".format(bl[i][1], bl[i][2]), bitline_layer_name, bl[i][0]) + + for i in range(len(br)): + self.add_layout_pin_rect_center("br{0}_{1}".format(br[i][1], br[i][2]), bitline_layer_name, br[i][0]) + + # add pex labels for control logic + for i in range (len(self.control_logic_insts)): + instance = self.control_logic_insts[i] + control_logic_offset = instance.offset + for output in instance.mod.output_list: + pin = instance.mod.get_pin(output) + pin.transform([0,0], instance.mirror, instance.rotate) + offset = [control_logic_offset[0] + pin.center()[0], control_logic_offset[1] + pin.center()[1]] + self.add_layout_pin_rect_center("{0}{1}".format(pin.name,i), storage_layer_name, offset) + + + + def create_netlist(self): """ Netlist creation """ @@ -124,6 +190,8 @@ class sram_base(design, verilog, lef): highest_coord = self.find_highest_coords() self.width = highest_coord[0] self.height = highest_coord[1] + if OPTS.use_pex: + self.add_global_pex_labels() self.add_boundary(ll=vector(0, 0), ur=vector(self.width, self.height)) @@ -346,6 +414,7 @@ class sram_base(design, verilog, lef): temp.append("dout{0}[{1}]".format(port, bit)) for port in self.all_ports: temp.append("rbl_bl{0}".format(port)) + temp.append("rbl_br{0}".format(port)) for port in self.write_ports: for bit in range(self.word_size + self.num_spare_cols): temp.append("bank_din{0}[{1}]".format(port, bit)) diff --git a/compiler/tests/04_pbuf_dec_8x_test.py b/compiler/tests/04_pbuf_dec_8x_test.py new file mode 100755 index 00000000..ad1665fe --- /dev/null +++ b/compiler/tests/04_pbuf_dec_8x_test.py @@ -0,0 +1,40 @@ +#!/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 pbuf_dec_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Checking 8x size decoder buffer") + a = factory.create(module_type="pbuf_dec", size=8) + 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/04_pbuf_test.py b/compiler/tests/04_pbuf_test.py index ffd06962..7ed92ab8 100755 --- a/compiler/tests/04_pbuf_test.py +++ b/compiler/tests/04_pbuf_test.py @@ -21,7 +21,7 @@ class pbuf_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - debug.info(2, "Testing inverter/buffer 4x 8x") + debug.info(2, "Testing buffer 8x") a = factory.create(module_type="pbuf", size=8) self.local_check(a) diff --git a/compiler/tests/05_local_bitcell_array_test.py b/compiler/tests/05_local_bitcell_array_test.py new file mode 100755 index 00000000..664d9ef8 --- /dev/null +++ b/compiler/tests/05_local_bitcell_array_test.py @@ -0,0 +1,41 @@ +#!/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 sram_factory import factory +import debug + + +#@unittest.skip("SKIPPING 05_local_bitcell_array_test") +class local_bitcell_array_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + debug.info(2, "Testing 4x4 local bitcell array for 6t_cell without replica") + a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, ports=[0], add_replica=False) + self.local_check(a) + + debug.info(2, "Testing 4x4 local bitcell array for 6t_cell with replica column") + a = factory.create(module_type="local_bitcell_array", cols=4, left_rbl=1, rows=4, ports=[0]) + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +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/08_wordline_buffer_array_test.py b/compiler/tests/08_wordline_buffer_array_test.py new file mode 100755 index 00000000..a753a21a --- /dev/null +++ b/compiler/tests/08_wordline_buffer_array_test.py @@ -0,0 +1,37 @@ +#!/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 wordline_buffer_array_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check wordline driver for single port + debug.info(2, "Checking driver") + tx = factory.create(module_type="wordline_buffer_array", rows=8, cols=32) + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +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/14_replica_bitcell_array_1rw_1r_test.py b/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py index 626725c4..bfb35cd7 100755 --- a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py +++ b/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py @@ -24,14 +24,35 @@ class replica_bitcell_array_1rw_1r_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() - debug.info(2, "Testing 4x4 array for cell_1rw_1r") - a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=1, bitcell_ports=[0, 1]) + debug.info(2, "Testing 4x4 non-replica array for cell_1rw_1r") + a = factory.create(module_type="replica_bitcell_array", + cols=4, + rows=4, + left_rbl=1, + right_rbl=1, + bitcell_ports=[0, 1], + add_replica=False) self.local_check(a) + debug.info(2, "Testing 4x4 array for cell_1rw_1r") + a = factory.create(module_type="replica_bitcell_array", + cols=4, + rows=4, + left_rbl=1, + right_rbl=1, + bitcell_ports=[0, 1]) + self.local_check(a) + + # Sky 130 has restrictions on the symmetries if OPTS.tech_name != "sky130": debug.info(2, "Testing 4x4 array for cell_1rw_1r") - a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=2, right_rbl=0, bitcell_ports=[0, 1]) + a = factory.create(module_type="replica_bitcell_array", + cols=4, + rows=4, + left_rbl=2, + right_rbl=0, + bitcell_ports=[0, 1]) self.local_check(a) globals.end_openram() diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index 443c91ca..965e3557 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -30,7 +30,7 @@ num_lvs_runs = 0 num_pex_runs = 0 -def write_calibre_drc_script(cell_name, extract, final_verification): +def write_calibre_drc_script(cell_name, extract, final_verification, gds_name): """ Write a Calibre runset file and script to run DRC """ # the runset file contains all the options to run calibre from tech import drc @@ -39,7 +39,7 @@ def write_calibre_drc_script(cell_name, extract, final_verification): drc_runset = { 'drcRulesFile': drc_rules, 'drcRunDir': OPTS.openram_temp, - 'drcLayoutPaths': cell_name + ".gds", + 'drcLayoutPaths': gds_name, 'drcLayoutPrimary': cell_name, 'drcLayoutSystem': 'GDSII', 'drcResultsformat': 'ASCII', @@ -68,7 +68,7 @@ def write_calibre_drc_script(cell_name, extract, final_verification): return drc_runset -def write_calibre_lvs_script(cell_name, final_verification): +def write_calibre_lvs_script(cell_name, final_verification, gds_name, sp_name): """ Write a Calibre runset file and script to run LVS """ from tech import drc @@ -76,9 +76,9 @@ def write_calibre_lvs_script(cell_name, final_verification): lvs_runset = { 'lvsRulesFile': lvs_rules, 'lvsRunDir': OPTS.openram_temp, - 'lvsLayoutPaths': cell_name + ".gds", + 'lvsLayoutPaths': gds_name, 'lvsLayoutPrimary': cell_name, - 'lvsSourcePath': cell_name + ".sp", + 'lvsSourcePath': sp_name, 'lvsSourcePrimary': cell_name, 'lvsSourceSystem': 'SPICE', 'lvsSpiceFile': "{}.spice".format(cell_name), @@ -135,11 +135,11 @@ def write_calibre_lvs_script(cell_name, final_verification): def write_calibre_pex_script(cell_name, extract, output, final_verification): """ Write a pex script that can either just extract the netlist or the netlist+parasitics """ if output == None: - output = name + ".pex.netlist" + output = cell_name + ".pex.sp" # check if lvs report has been done # if not run drc and lvs - if not os.path.isfile(cell_name + ".lvs.report"): + if not os.path.isfile(OPTS.openram_temp + cell_name + ".lvs.report"): gds_name = OPTS.openram_temp +"/"+ cell_name + ".gds" sp_name = OPTS.openram_temp +"/"+ cell_name + ".sp" run_drc(cell_name, gds_name) @@ -155,7 +155,7 @@ def write_calibre_pex_script(cell_name, extract, output, final_verification): 'pexSourcePath': cell_name + ".sp", 'pexSourcePrimary': cell_name, 'pexReportFile': cell_name + ".pex.report", - 'pexPexNetlistFile': cell_name + ".pex.netlist", + 'pexPexNetlistFile': output, 'pexPexReportFile': cell_name + ".pex.report", 'pexMaskDBFile': cell_name + ".maskdb", 'cmnFDIDEFLayoutPath': cell_name + ".def", @@ -195,11 +195,14 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False): filter_gds(cell_name, OPTS.openram_temp + "temp.gds", OPTS.openram_temp + cell_name + ".gds") else: # Copy file to local dir if it isn't already - if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'): + if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)): shutil.copy(gds_name, OPTS.openram_temp) - drc_runset = write_calibre_drc_script(cell_name, extract, final_verification) + drc_runset = write_calibre_drc_script(cell_name, extract, final_verification, gds_name) + if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)): + shutil.copy(gds_name, OPTS.openram_temp + os.path.basename(gds_name)) + (outfile, errfile, resultsfile) = run_script(cell_name, "drc") # check the result for these lines in the summary: @@ -238,12 +241,12 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): global num_lvs_runs num_lvs_runs += 1 - lvs_runset = write_calibre_lvs_script(cell_name, final_verification) + lvs_runset = write_calibre_lvs_script(cell_name, final_verification, gds_name, sp_name) # Copy file to local dir if it isn't already - if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'): + if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)): shutil.copy(gds_name, OPTS.openram_temp) - if os.path.dirname(sp_name)!=OPTS.openram_temp.rstrip('/'): + if not os.path.isfile(OPTS.openram_temp + os.path.basename(sp_name)): shutil.copy(sp_name, OPTS.openram_temp) (outfile, errfile, resultsfile) = run_script(cell_name, "lvs") @@ -325,8 +328,14 @@ def run_pex(cell_name, gds_name, sp_name, output=None, final_verification=False) global num_pex_runs num_pex_runs += 1 - write_calibre_pex_script(cell_name,True,output,final_verification) + write_calibre_pex_script(cell_name, True, output, final_verification) + # Copy file to local dir if it isn't already + if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)): + shutil.copy(gds_name, OPTS.openram_temp) + if not os.path.isfile(OPTS.openram_temp + os.path.basename(sp_name)): + shutil.copy(sp_name, OPTS.openram_temp) + (outfile, errfile, resultsfile) = run_script(cell_name, "pex") diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index ef98a09b..a9ea544e 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -77,6 +77,8 @@ def write_magic_script(cell_name, extract=False, final_verification=False): f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1])) f.write("gds polygon subcell true\n") f.write("gds warning default\n") + # This causes substrate contacts to not be extracted + f.write("# gds readonly true\n") f.write("gds read {}.gds\n".format(cell_name)) f.write("load {}\n".format(cell_name)) # Flatten the cell to get rid of DRCs spanning multiple layers @@ -107,21 +109,21 @@ def write_magic_script(cell_name, extract=False, final_verification=False): # f.write(pre + "ext2spice hierarchy on\n") # f.write(pre + "ext2spice scale off\n") # lvs exists in 8.2.79, but be backword compatible for now - # f.write(pre + "ext2spice lvs\n") - f.write(pre + "ext2spice hierarchy on\n") - f.write(pre + "ext2spice format ngspice\n") - f.write(pre + "ext2spice cthresh infinite\n") - f.write(pre + "ext2spice rthresh infinite\n") - f.write(pre + "ext2spice renumber off\n") - f.write(pre + "ext2spice scale off\n") - f.write(pre + "ext2spice blackbox on\n") - f.write(pre + "ext2spice subcircuit top auto\n") - f.write(pre + "ext2spice global off\n") + #f.write(pre+"ext2spice lvs\n") + f.write(pre+"ext2spice hierarchy on\n") + f.write(pre+"ext2spice format ngspice\n") + f.write(pre+"ext2spice cthresh infinite\n") + f.write(pre+"ext2spice rthresh infinite\n") + f.write(pre+"ext2spice renumber off\n") + f.write(pre+"ext2spice scale off\n") + f.write(pre+"ext2spice blackbox on\n") + f.write(pre+"ext2spice subcircuit top on\n") + f.write(pre+"ext2spice global off\n") # Can choose hspice, ngspice, or spice3, # but they all seem compatible enough. - #f.write(pre + "ext2spice format ngspice\n") - f.write(pre + "ext2spice {}\n".format(cell_name)) + f.write(pre+"ext2spice format ngspice\n") + f.write(pre+"ext2spice {}\n".format(cell_name)) f.write("quit -noprompt\n") f.write("EOF\n") @@ -411,13 +413,13 @@ def write_script_pex_rule(gds_name,cell_name,output): else: pre = "" f.write(pre+"extract\n".format(cell_name)) - #f.write(pre+"ext2spice hierarchy on\n") - #f.write(pre+"ext2spice format ngspice\n") - #f.write(pre+"ext2spice renumber off\n") - #f.write(pre+"ext2spice scale off\n") - #f.write(pre+"ext2spice blackbox on\n") + f.write(pre+"ext2spice hierarchy off\n") + f.write(pre+"ext2spice format ngspice\n") + f.write(pre+"ext2spice renumber off\n") + f.write(pre+"ext2spice scale off\n") + f.write(pre+"ext2spice blackbox on\n") f.write(pre+"ext2spice subcircuit top on\n") - #f.write(pre+"ext2spice global off\n") + f.write(pre+"ext2spice global off\n") f.write(pre+"ext2spice {}\n".format(cell_name)) f.write("quit -noprompt\n") f.write("eof\n") @@ -440,31 +442,49 @@ def correct_port(name, output_file_name, ref_file_name): pex_file = open(output_file_name, "r") contents = pex_file.read() # locate the start of circuit definition line - match = re.search(".subckt " + str(name) + ".*", contents) + match = re.search(r'^\.subckt+[^M]*', contents, re.MULTILINE) match_index_start = match.start() - pex_file.seek(match_index_start) - rest_text = pex_file.read() - # locate the end of circuit definition line - match = re.search(r'\n', rest_text) - match_index_end = match.start() + match_index_end = match.end() # store the unchanged part of pex file in memory pex_file.seek(0) part1 = pex_file.read(match_index_start) - pex_file.seek(match_index_start + match_index_end) + pex_file.seek(match_index_end) part2 = pex_file.read() + + bitcell_list = "+ " + if OPTS.words_per_row: + for bank in range(OPTS.num_banks): + for bank in range(OPTS.num_banks): + row = int(OPTS.num_words / OPTS.words_per_row) - 1 + col = int(OPTS.word_size * OPTS.words_per_row) - 1 + bitcell_list += "bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col) + bitcell_list += "bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col) + for col in range(OPTS.word_size * OPTS.words_per_row): + for port in range(OPTS.num_r_ports + OPTS.num_w_ports + OPTS.num_rw_ports): + bitcell_list += "bl{0}_{1} ".format(bank, col) + bitcell_list += "br{0}_{1} ".format(bank, col) + bitcell_list += "\n" + control_list = "+ " + if OPTS.words_per_row: + for bank in range(OPTS.num_banks): + control_list += "bank_{}/s_en0".format(bank) + control_list += '\n' + + part2 = bitcell_list + control_list + part2 + pex_file.close() # obtain the correct definition line from the original spice file sp_file = open(ref_file_name, "r") contents = sp_file.read() - circuit_title = re.search(".SUBCKT " + str(name) + ".*\n", contents) + circuit_title = re.search(".SUBCKT " + str(name) + ".*", contents) circuit_title = circuit_title.group() sp_file.close() # write the new pex file with info in the memory output_file = open(output_file_name, "w") output_file.write(part1) - output_file.write(circuit_title) + output_file.write(circuit_title+'\n') output_file.write(part2) output_file.close() diff --git a/technology/freepdk45/sp_lib/cell_6t.sp b/technology/freepdk45/sp_lib/cell_6t.sp index e1e4936d..b39427d7 100644 --- a/technology/freepdk45/sp_lib/cell_6t.sp +++ b/technology/freepdk45/sp_lib/cell_6t.sp @@ -1,15 +1,15 @@ .SUBCKT cell_6t bl br wl vdd gnd * Inverter 1 -MM0 Qbar Q gnd gnd NMOS_VTG W=205.00n L=50n -MM4 Qbar Q vdd vdd PMOS_VTG W=90n L=50n +MM0 Q_bar Q gnd gnd NMOS_VTG W=205.00n L=50n +MM4 Q_bar Q vdd vdd PMOS_VTG W=90n L=50n * Inverer 2 -MM1 Q Qbar gnd gnd NMOS_VTG W=205.00n L=50n -MM5 Q Qbar vdd vdd PMOS_VTG W=90n L=50n +MM1 Q Q_bar gnd gnd NMOS_VTG W=205.00n L=50n +MM5 Q Q_bar vdd vdd PMOS_VTG W=90n L=50n * Access transistors MM3 bl wl Q gnd NMOS_VTG W=135.00n L=50n -MM2 br wl Qbar gnd NMOS_VTG W=135.00n L=50n +MM2 br wl Q_bar gnd NMOS_VTG W=135.00n L=50n .ENDS cell_6t diff --git a/technology/freepdk45/sp_lib/dummy_cell_6t.sp b/technology/freepdk45/sp_lib/dummy_cell_6t.sp index ab862ec5..6e192049 100644 --- a/technology/freepdk45/sp_lib/dummy_cell_6t.sp +++ b/technology/freepdk45/sp_lib/dummy_cell_6t.sp @@ -1,15 +1,15 @@ .SUBCKT dummy_cell_6t bl br wl vdd gnd * Inverter 1 -MM0 Qbar Q gnd gnd NMOS_VTG W=205.00n L=50n -MM4 Qbar Q vdd vdd PMOS_VTG W=90n L=50n +MM0 Q_bar Q gnd gnd NMOS_VTG W=205.00n L=50n +MM4 Q_bar Q vdd vdd PMOS_VTG W=90n L=50n * Inverer 2 -MM1 Q Qbar gnd gnd NMOS_VTG W=205.00n L=50n -MM5 Q Qbar vdd vdd PMOS_VTG W=90n L=50n +MM1 Q Q_bar gnd gnd NMOS_VTG W=205.00n L=50n +MM5 Q Q_bar vdd vdd PMOS_VTG W=90n L=50n * Access transistors MM3 bl_noconn wl Q gnd NMOS_VTG W=135.00n L=50n -MM2 br_noconn wl Qbar gnd NMOS_VTG W=135.00n L=50n +MM2 br_noconn wl Q_bar gnd NMOS_VTG W=135.00n L=50n .ENDS cell_6t diff --git a/technology/scn4m_subm/gds_lib/cell_1w_1r.gds b/technology/scn4m_subm/gds_lib/cell_1w_1r.gds index 782c43dc..06f79ba5 100644 Binary files a/technology/scn4m_subm/gds_lib/cell_1w_1r.gds and b/technology/scn4m_subm/gds_lib/cell_1w_1r.gds differ diff --git a/technology/scn4m_subm/gds_lib/cell_6t.gds b/technology/scn4m_subm/gds_lib/cell_6t.gds index 14d6ab7e..6bfc4431 100644 Binary files a/technology/scn4m_subm/gds_lib/cell_6t.gds and b/technology/scn4m_subm/gds_lib/cell_6t.gds differ diff --git a/technology/scn4m_subm/gds_lib/dff.gds b/technology/scn4m_subm/gds_lib/dff.gds index 7825c3bc..ca6d2b6d 100644 Binary files a/technology/scn4m_subm/gds_lib/dff.gds and b/technology/scn4m_subm/gds_lib/dff.gds differ diff --git a/technology/scn4m_subm/gds_lib/replica_cell_6t.gds b/technology/scn4m_subm/gds_lib/replica_cell_6t.gds index 191f1206..30f5a37e 100644 Binary files a/technology/scn4m_subm/gds_lib/replica_cell_6t.gds and b/technology/scn4m_subm/gds_lib/replica_cell_6t.gds differ diff --git a/technology/scn4m_subm/gds_lib/sense_amp.gds b/technology/scn4m_subm/gds_lib/sense_amp.gds index cf5fa587..5ffbb0d5 100644 Binary files a/technology/scn4m_subm/gds_lib/sense_amp.gds and b/technology/scn4m_subm/gds_lib/sense_amp.gds differ diff --git a/technology/scn4m_subm/gds_lib/tri_gate.gds b/technology/scn4m_subm/gds_lib/tri_gate.gds index ad83f4c6..088e8870 100644 Binary files a/technology/scn4m_subm/gds_lib/tri_gate.gds and b/technology/scn4m_subm/gds_lib/tri_gate.gds differ diff --git a/technology/scn4m_subm/gds_lib/write_driver.gds b/technology/scn4m_subm/gds_lib/write_driver.gds index 8223c795..9e201f24 100644 Binary files a/technology/scn4m_subm/gds_lib/write_driver.gds and b/technology/scn4m_subm/gds_lib/write_driver.gds differ diff --git a/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag b/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag index 9aec1c5d..0484df70 100644 --- a/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag +++ b/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1542220294 +timestamp 1577066542 << nwell >> rect 0 46 54 75 << pwell >> @@ -47,12 +47,17 @@ rect 17 33 21 37 rect 25 29 29 37 rect 33 33 37 37 rect 41 33 45 37 +rect 9 17 13 21 rect 25 17 29 23 +rect 41 17 45 21 << pdcontact >> rect 17 54 21 58 +rect 25 54 29 58 rect 33 54 37 58 << psubstratepcontact >> rect 25 9 29 13 +<< nsubstratencontact >> +rect 25 68 29 72 << polysilicon >> rect 22 57 24 60 rect 30 57 32 60 @@ -103,19 +108,16 @@ rect 0 2 16 6 rect 20 2 34 6 rect 38 2 54 6 << m2contact >> +rect 25 68 29 72 +rect 25 54 29 58 rect 2 33 6 37 rect 48 33 52 37 rect 16 24 20 28 rect 34 24 38 28 -rect 16 2 20 6 -rect 34 2 38 6 -<< pdm12contact >> -rect 25 54 29 58 -<< ndm12contact >> rect 9 17 13 21 rect 41 17 45 21 -<< nsm12contact >> -rect 25 68 29 72 +rect 16 2 20 6 +rect 34 2 38 6 << metal2 >> rect 2 37 6 72 rect 2 0 6 33 @@ -139,4 +141,6 @@ rlabel metal2 4 7 4 7 2 bl0 rlabel metal2 11 7 11 7 1 bl1 rlabel metal2 43 7 43 7 1 br1 rlabel metal2 50 7 50 7 8 br0 +rlabel polycontact 29 49 29 49 1 Q +rlabel polycontact 25 42 25 42 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/cell_1w_1r.mag b/technology/scn4m_subm/mag_lib/cell_1w_1r.mag index 9aec1c5d..71a3b1bc 100644 --- a/technology/scn4m_subm/mag_lib/cell_1w_1r.mag +++ b/technology/scn4m_subm/mag_lib/cell_1w_1r.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1542220294 +timestamp 1577067400 << nwell >> rect 0 46 54 75 << pwell >> @@ -47,12 +47,17 @@ rect 17 33 21 37 rect 25 29 29 37 rect 33 33 37 37 rect 41 33 45 37 +rect 9 17 13 21 rect 25 17 29 23 +rect 41 17 45 21 << pdcontact >> rect 17 54 21 58 +rect 25 54 29 58 rect 33 54 37 58 << psubstratepcontact >> rect 25 9 29 13 +<< nsubstratencontact >> +rect 25 68 29 72 << polysilicon >> rect 22 57 24 60 rect 30 57 32 60 @@ -103,19 +108,16 @@ rect 0 2 16 6 rect 20 2 34 6 rect 38 2 54 6 << m2contact >> +rect 25 68 29 72 +rect 25 54 29 58 rect 2 33 6 37 rect 48 33 52 37 rect 16 24 20 28 rect 34 24 38 28 -rect 16 2 20 6 -rect 34 2 38 6 -<< pdm12contact >> -rect 25 54 29 58 -<< ndm12contact >> rect 9 17 13 21 rect 41 17 45 21 -<< nsm12contact >> -rect 25 68 29 72 +rect 16 2 20 6 +rect 34 2 38 6 << metal2 >> rect 2 37 6 72 rect 2 0 6 33 @@ -139,4 +141,6 @@ rlabel metal2 4 7 4 7 2 bl0 rlabel metal2 11 7 11 7 1 bl1 rlabel metal2 43 7 43 7 1 br1 rlabel metal2 50 7 50 7 8 br0 +rlabel polycontact 29 49 29 49 1 Q +rlabel polycontact 25 42 25 42 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/cell_6t.mag b/technology/scn4m_subm/mag_lib/cell_6t.mag index bb9d943d..e8c16eff 100644 --- a/technology/scn4m_subm/mag_lib/cell_6t.mag +++ b/technology/scn4m_subm/mag_lib/cell_6t.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1560809302 +timestamp 1577163318 << nwell >> rect -8 35 42 57 << pwell >> @@ -115,4 +115,6 @@ rlabel m2contact 17 52 17 52 5 vdd rlabel metal2 8 49 8 49 1 bl rlabel metal2 26 49 26 49 1 br rlabel metal1 4 13 4 13 1 wl +rlabel polycontact 17 37 17 37 1 Q +rlabel polycontact 23 28 23 28 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/dummy_cell_1rw_1r.mag b/technology/scn4m_subm/mag_lib/dummy_cell_1rw_1r.mag index 60e24aca..1931485f 100644 --- a/technology/scn4m_subm/mag_lib/dummy_cell_1rw_1r.mag +++ b/technology/scn4m_subm/mag_lib/dummy_cell_1rw_1r.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1562188987 +timestamp 1577067400 << nwell >> rect 0 46 54 75 << pwell >> @@ -133,4 +133,6 @@ rlabel metal2 4 7 4 7 2 bl0 rlabel metal2 11 7 11 7 1 bl1 rlabel metal2 43 7 43 7 1 br1 rlabel metal2 50 7 50 7 8 br0 +rlabel polycontact 29 49 29 49 1 Q +rlabel polycontact 25 42 25 42 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/dummy_cell_1w_1r.mag b/technology/scn4m_subm/mag_lib/dummy_cell_1w_1r.mag index 03e49f03..1931485f 100644 --- a/technology/scn4m_subm/mag_lib/dummy_cell_1w_1r.mag +++ b/technology/scn4m_subm/mag_lib/dummy_cell_1w_1r.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1562189027 +timestamp 1577067400 << nwell >> rect 0 46 54 75 << pwell >> @@ -133,4 +133,6 @@ rlabel metal2 4 7 4 7 2 bl0 rlabel metal2 11 7 11 7 1 bl1 rlabel metal2 43 7 43 7 1 br1 rlabel metal2 50 7 50 7 8 br0 +rlabel polycontact 29 49 29 49 1 Q +rlabel polycontact 25 42 25 42 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/dummy_cell_6t.mag b/technology/scn4m_subm/mag_lib/dummy_cell_6t.mag index 74562f15..515ef682 100644 --- a/technology/scn4m_subm/mag_lib/dummy_cell_6t.mag +++ b/technology/scn4m_subm/mag_lib/dummy_cell_6t.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1560809362 +timestamp 1577067400 << nwell >> rect -8 35 42 57 << pwell >> @@ -112,4 +112,6 @@ rlabel m2contact 17 52 17 52 5 vdd rlabel metal2 8 49 8 49 1 bl rlabel metal2 26 49 26 49 1 br rlabel metal1 4 13 4 13 1 wl +rlabel polycontact 17 37 17 37 1 Q +rlabel polycontact 23 28 23 28 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/replica_cell_1rw_1r.mag b/technology/scn4m_subm/mag_lib/replica_cell_1rw_1r.mag index f215ff04..982e1630 100644 --- a/technology/scn4m_subm/mag_lib/replica_cell_1rw_1r.mag +++ b/technology/scn4m_subm/mag_lib/replica_cell_1rw_1r.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1542221056 +timestamp 1577067400 << nwell >> rect 0 46 54 75 << pwell >> @@ -142,4 +142,6 @@ rlabel metal2 4 7 4 7 2 bl0 rlabel metal2 11 7 11 7 1 bl1 rlabel metal2 43 7 43 7 1 br1 rlabel metal2 50 7 50 7 8 br0 +rlabel polycontact 29 49 29 49 1 Q +rlabel polycontact 25 42 25 42 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/replica_cell_1w_1r.mag b/technology/scn4m_subm/mag_lib/replica_cell_1w_1r.mag index f215ff04..61add325 100644 --- a/technology/scn4m_subm/mag_lib/replica_cell_1w_1r.mag +++ b/technology/scn4m_subm/mag_lib/replica_cell_1w_1r.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1542221056 +timestamp 1577067446 << nwell >> rect 0 46 54 75 << pwell >> @@ -142,4 +142,6 @@ rlabel metal2 4 7 4 7 2 bl0 rlabel metal2 11 7 11 7 1 bl1 rlabel metal2 43 7 43 7 1 br1 rlabel metal2 50 7 50 7 8 br0 +rlabel polycontact 29 49 29 49 1 Q +rlabel polycontact 25 42 25 42 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/replica_cell_6t.mag b/technology/scn4m_subm/mag_lib/replica_cell_6t.mag index b5a5f7b8..61a7646e 100644 --- a/technology/scn4m_subm/mag_lib/replica_cell_6t.mag +++ b/technology/scn4m_subm/mag_lib/replica_cell_6t.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1560809329 +timestamp 1577067503 << nwell >> rect -8 35 42 57 << pwell >> @@ -116,4 +116,6 @@ rlabel m2contact 17 52 17 52 5 vdd rlabel metal2 8 49 8 49 1 bl rlabel metal2 26 49 26 49 1 br rlabel metal1 4 13 4 13 1 wl +rlabel polycontact 17 37 17 37 1 Q +rlabel polycontact 23 28 23 28 1 Q_bar << end >> diff --git a/technology/scn4m_subm/sp_lib/cell_6t.sp b/technology/scn4m_subm/sp_lib/cell_6t.sp index bb430893..dc724007 100644 --- a/technology/scn4m_subm/sp_lib/cell_6t.sp +++ b/technology/scn4m_subm/sp_lib/cell_6t.sp @@ -4,15 +4,15 @@ * SPICE3 file created from cell_6t.ext - technology: scmos * Inverter 1 -M1000 Q Qbar vdd vdd p w=0.6u l=0.8u -M1002 Q Qbar gnd gnd n w=1.6u l=0.4u +M1000 Q Q_bar vdd vdd p w=0.6u l=0.8u +M1002 Q Q_bar gnd gnd n w=1.6u l=0.4u * Inverter 2 -M1001 vdd Q Qbar vdd p w=0.6u l=0.8u -M1003 gnd Q Qbar gnd n w=1.6u l=0.4u +M1001 vdd Q Q_bar vdd p w=0.6u l=0.8u +M1003 gnd Q Q_bar gnd n w=1.6u l=0.4u * Access transistors M1004 Q wl bl gnd n w=0.8u l=0.4u -M1005 Qbar wl br gnd n w=0.8u l=0.4u +M1005 Q_bar wl br gnd n w=0.8u l=0.4u .ENDS diff --git a/technology/scn4m_subm/sp_lib/dummy_cell_6t.sp b/technology/scn4m_subm/sp_lib/dummy_cell_6t.sp index 3b0584df..c5b6ff9d 100644 --- a/technology/scn4m_subm/sp_lib/dummy_cell_6t.sp +++ b/technology/scn4m_subm/sp_lib/dummy_cell_6t.sp @@ -3,15 +3,15 @@ .SUBCKT dummy_cell_6t bl br wl vdd gnd * Inverter 1 -M1000 Q Qbar vdd vdd p w=0.6u l=0.8u -M1002 Q Qbar gnd gnd n w=1.6u l=0.4u +M1000 Q Q_bar vdd vdd p w=0.6u l=0.8u +M1002 Q Q_bar gnd gnd n w=1.6u l=0.4u * Inverter 2 -M1001 vdd Q Qbar vdd p w=0.6u l=0.8u -M1003 gnd Q Qbar gnd n w=1.6u l=0.4u +M1001 vdd Q Q_bar vdd p w=0.6u l=0.8u +M1003 gnd Q Q_bar gnd n w=1.6u l=0.4u * Access transistors M1004 Q wl bl_noconn gnd n w=0.8u l=0.4u -M1005 Qbar wl br_noconn gnd n w=0.8u l=0.4u +M1005 Q_bar wl br_noconn gnd n w=0.8u l=0.4u .ENDS diff --git a/technology/scn4m_subm/tf/mosis.lyt b/technology/scn4m_subm/tf/mosis.lyt new file mode 100644 index 00000000..8673627e --- /dev/null +++ b/technology/scn4m_subm/tf/mosis.lyt @@ -0,0 +1,27 @@ + + + SCMOS + + 0.001 + + mosis.lyp + true + + Active,ActX,Metal1 + Poly1,P1Con,Metal1 + Metal1,Via,Metal2 + Metal2,Via2,Metal3 + Metal3,Via3,Metal4 + Active='43/0' + Poly1='46/0' + P1Con='47/0' + ActX='48/0' + Metal1='49/0' + Via='50/0' + Metal2='51/0' + Via2='61/0' + Metal3.='62/0' + Via3='30/0' + Metal4='31/0' + +